renamed info to MethodSource

This commit is contained in:
Torsten Ruger 2015-07-03 20:13:03 +03:00
parent e959c5b0f5
commit b61c73acdd
24 changed files with 103 additions and 103 deletions

View File

@ -1,7 +1,7 @@
module Arm
# This implements call logic, which is simply like a c call (not send, that involves lookup and all sorts)
#
# The only target for a call is a CompiledMethod, so we just need to get the address for the code
# The only target for a call is a Method, so we just need to get the address for the code
# and call it.
#
# The only slight snag is that we would need to assemble before getting the address, but to assemble

View File

@ -159,7 +159,7 @@ module Parfait
#many basic List functions can not be defined in ruby, such as
# get/set/length/add/delete
# so they must be defined as CompiledMethods in Builtin::Kernel
# so they must be defined as Methods in Builtin::Kernel
#ruby 2.1 list (just for reference, keep at bottom)
# :at, :fetch, :first, :last, :concat, :<<, :push, :pop, :shift, :unshift, :insert, :each, :each_index, :reverse_each,

View File

@ -43,7 +43,7 @@ module Parfait
found = get_instance_method( method.name )
if found
@instance_methods.delete(found)
raise "existed in #{self.name} #{Sof.write found.info.blocks}"
raise "existed in #{self.name} #{Sof.write found.source.blocks}"
end
@instance_methods.push method
#puts "#{self.name} add #{method.name}"

View File

@ -1,17 +1,17 @@
Register Machine
Register Machine
===============
This is the logic that uses the compiled virtual object space to produce code and an executable binary.
There is a mechanism for an actual machine (derived class) to generate harware specific instructions (as the
There is a mechanism for an actual machine (derived class) to generate harware specific instructions (as the
plain ones in this directory don't assemble to binary). Currently there is only the Arm module to actually do
that.
The elf module is used to generate the actual binary from the final Space. Space is a virtual class representing
all objects that will be in the executable. Other than CompiledMethods, objects get transformed to data.
all objects that will be in the executable. Other than MethodSource, objects get transformed to data.
But CompiledMethods, which are made up of Blocks, are compiled into a stream of bytes, which are the binary code for the
function.
But MethodSource, which are made up of Blocks, are compiled into a stream of bytes,
which are the binary code for the function.
Virtual Objects
----------------
@ -32,13 +32,13 @@ and then into the destination. And then we need some fiddling of bits to shift t
Another simple example is a Call. A simple case of a Class function call resolves the class object,
and with the method name the function to be found at compile-time.
And so this results in a Register::Call, which is an Arm instruction.
And so this results in a Register::Call, which is an Arm instruction.
A C call
A C call
---------
Ok, there are no c calls. But syscalls are very similar.
This is not at all as simple as the nice Class call described above.
This is not at all as simple as the nice Class call described above.
For syscall in Arm (linux) you have to load registers 0-x (depending on call), load R7 with the
syscall number and then issue the software interupt instruction.

View File

@ -28,7 +28,7 @@ module Register
@machine.objects.each do |objekt|
next unless objekt.is_a? Parfait::Method
# should be fill_to_length (with zeros)
objekt.code.set_length(objekt.info.byte_length , 0)
objekt.code.set_length(objekt.source.byte_length , 0)
end
#need the initial jump at 0 and then functions
@machine.init.set_position(at)
@ -47,7 +47,7 @@ module Register
# have to tell the code that will be assembled where it is to
# get the jumps/calls right
if objekt.is_a? Parfait::Method
objekt.info.set_position( objekt.code.position )
objekt.source.set_position( objekt.code.position )
end
next if objekt.is_a? Parfait::BinaryCode
objekt.set_position at
@ -102,16 +102,16 @@ module Register
return @stream.string
end
# assemble the CompiledMethodInfo into a stringio
# assemble the MethodSource into a stringio
# and then plonk that binary data into the method.code array
def assemble_binary_method method
stream = StringIO.new
method.info.blocks.each do |block|
method.source.blocks.each do |block|
block.codes.each do |code|
begin
code.assemble( stream )
rescue => e
puts "Method error #{method.name}\n#{Sof.write(method.info.blocks).to_s[0...2000]}"
puts "Method error #{method.name}\n#{Sof.write(method.source.blocks).to_s[0...2000]}"
puts Sof.write(code)
raise e
end
@ -121,8 +121,8 @@ module Register
index = 1
stream.rewind
#puts "Assembled #{method.name} with length #{stream.length}"
raise "length error #{method.code.length} != #{method.info.byte_length}" if method.code.length != method.info.byte_length
raise "length error #{stream.length} != #{method.info.byte_length}" if method.info.byte_length != stream.length
raise "length error #{method.code.length} != #{method.source.byte_length}" if method.code.length != method.source.byte_length
raise "length error #{stream.length} != #{method.source.byte_length}" if method.source.byte_length != stream.length
stream.each_byte do |b|
method.code.set_char(index , b )
index = index + 1

View File

@ -6,8 +6,8 @@ It is the other side of the parfait coin, part of the runtime.
The functions are organized by their respective class and get loaded in boot_classes! ,
right at the start. (see virtual/boot.rb)
These functions return their code, ie a Virtual::CompiledMethod object, which can then be called by
ruby code as if it were a "normal" function.
These functions return their code, ie a Parfait::Method with a Virtual::MethodSource object,
which can then be called by ruby code as if it were a "normal" function.
A normal ruby function is one that is parsed and transformed to code. But not all functionality can
be written in ruby, one of those chicken and egg things.

View File

@ -10,9 +10,9 @@ module Register
# As we write before we recurse (save a push) we write the number backwards
# arguments: string address , integer
def utoa context
utoa_function = Virtual::CompiledMethodInfo.create_method(:Integer ,:utoa , [ Virtual::Integer ] )
function.info.return_type = Virtual::Integer
function.info.receiver = Virtual::Integer
utoa_function = Virtual::MethodSource.create_method(:Integer ,:utoa , [ Virtual::Integer ] )
function.source.return_type = Virtual::Integer
function.source.receiver = Virtual::Integer
return utoa_function
str_addr = utoa_function.receiver
number = utoa_function.args.first
@ -30,9 +30,9 @@ module Register
end
def putint context
putint_function = Virtual::CompiledMethodInfo.create_method(:Integer,:putint , [] )
putint_function.info.return_type = Virtual::Integer
putint_function.info.receiver = Virtual::Integer
putint_function = Virtual::MethodSource.create_method(:Integer,:putint , [] )
putint_function.source.return_type = Virtual::Integer
putint_function.source.receiver = Virtual::Integer
return putint_function
buffer = Parfait::Word.new(" ") # create a buffer
context.object_space.add_object buffer # and save it (function local variable: a no no)
@ -59,9 +59,9 @@ module Register
# a hand coded version of the fibonachi numbers
# not my hand off course, found in the net http://www.peter-cockerell.net/aalp/html/ch-5.html
def fibo context
fibo_function = Virtual::CompiledMethodInfo.create_method(:Integer,:fibo , [] )
fibo_function.info.return_type = Virtual::Integer
fibo_function.info.receiver = Virtual::Integer
fibo_function = Virtual::MethodSource.create_method(:Integer,:fibo , [] )
fibo_function.source.return_type = Virtual::Integer
fibo_function.source.receiver = Virtual::Integer
return fibo_function
result = fibo_function.return_type
int = fibo_function.receiver

View File

@ -6,41 +6,41 @@ module Register
# it isn't really a function, ie it is jumped to (not called), exits and may not return
# so it is responsible for initial setup
def __init__ context
function = Virtual::CompiledMethodInfo.create_method(:Kernel,:__init__ , [])
function.info.return_type = Virtual::Integer
function = Virtual::MethodSource.create_method(:Kernel,:__init__ , [])
function.source.return_type = Virtual::Integer
# no method enter or return (automatically added), remove
function.info.blocks.first.codes.pop # no Method enter
function.info.blocks.last.codes.pop # no Method return
function.source.blocks.first.codes.pop # no Method enter
function.source.blocks.last.codes.pop # no Method return
#Set up the Space as self upon init
space = Parfait::Space.object_space
function.info.add_code LoadConstant.new( space , Register.self_reg)
function.source.add_code LoadConstant.new( space , Register.self_reg)
message_ind = Register.resolve_index( :space , :first_message )
# Load the message to new message register (r3)
function.info.add_code Register.get_slot( :self , message_ind , :new_message)
function.source.add_code Register.get_slot( :self , message_ind , :new_message)
# And store the space as the new self (so the call can move it back as self)
function.info.add_code Register.set_slot( :self , :new_message , :receiver)
function.source.add_code Register.set_slot( :self , :new_message , :receiver)
# now we are set up to issue a call to the main
function.info.add_code Virtual::MethodCall.new(Virtual.machine.space.get_main)
function.source.add_code Virtual::MethodCall.new(Virtual.machine.space.get_main)
emit_syscall( function , :exit )
return function
end
def exit context
function = Virtual::CompiledMethodInfo.create_method(:Kernel,:exit , [])
function.info.return_type = Virtual::Integer
function = Virtual::MethodSource.create_method(:Kernel,:exit , [])
function.source.return_type = Virtual::Integer
return function
ret = Virtual::RegisterMachine.instance.exit(function)
function.set_return ret
function
end
def __send context
function = Virtual::CompiledMethodInfo.create_method(:Kernel ,:__send , [] )
function.info.return_type = Virtual::Integer
function = Virtual::MethodSource.create_method(:Kernel ,:__send , [] )
function.source.return_type = Virtual::Integer
return function
end
def emit_syscall function , name
save_message( function )
function.info.add_code Syscall.new( name )
function.source.add_code Syscall.new( name )
restore_message(function)
end
@ -49,20 +49,20 @@ module Register
# This relies on linux to save and restore all registers
#
def save_message(function)
function.info.add_code RegisterTransfer.new( Register.message_reg , :r8 )
function.source.add_code RegisterTransfer.new( Register.message_reg , :r8 )
end
def restore_message(function)
return_tmp = Register.tmp_reg
# get the sys return out of the way
function.info.add_code RegisterTransfer.new( Register.message_reg , return_tmp )
function.source.add_code RegisterTransfer.new( Register.message_reg , return_tmp )
# load the stored message into the base RegisterMachine
function.info.add_code RegisterTransfer.new( :r8 , Register.message_reg )
function.source.add_code RegisterTransfer.new( :r8 , Register.message_reg )
# save the return value into the message
function.info.add_code Register.set_slot( return_tmp , :message , :return_value )
function.source.add_code Register.set_slot( return_tmp , :message , :return_value )
# and "unroll" self and frame
function.info.add_code Register.get_slot(:message , :receiver, :self )
function.info.add_code Register.get_slot(:message , :frame , :frame)
function.source.add_code Register.get_slot(:message , :receiver, :self )
function.source.add_code Register.get_slot(:message , :frame , :frame)
end
end
extend ClassMethods

View File

@ -5,15 +5,15 @@ module Register
class List
module ClassMethods
def get context , index = Virtual::Integer
get_function = Virtual::CompiledMethodInfo.create_method(:get , [ Virtual::Integer] , Virtual::Integer , Virtual::Integer )
get_function = Virtual::MethodSource.create_method(:get , [ Virtual::Integer] , Virtual::Integer , Virtual::Integer )
return get_function
end
def set context , index = Virtual::Integer , object = Virtual::Reference
set_function = Virtual::CompiledMethodInfo.create_method(:set , [Virtual::Integer, Virtual::Reference] )
set_function = Virtual::MethodSource.create_method(:set , [Virtual::Integer, Virtual::Reference] )
return set_function
end
def push context , object = Virtual::Reference
push_function = Virtual::CompiledMethodInfo.create_method(:push , [Virtual::Reference] )
push_function = Virtual::MethodSource.create_method(:push , [Virtual::Reference] )
return push_function
end
end

View File

@ -6,7 +6,7 @@ module Register
# main entry point, ie __init__ calls this
# defined here as empty, to be redefined
def main context
function = Virtual::CompiledMethodInfo.create_method(:Object,:main , [])
function = Virtual::MethodSource.create_method(:Object,:main , [])
return function
end
@ -18,7 +18,7 @@ module Register
# The at_index is just "below" the api, something we need but don't want to expose,
# so we can't code the above in ruby
def _get_instance_variable context , name = Virtual::Integer
get_function = Virtual::CompiledMethodInfo.create_method(:Object,:_get_instance_variable , [ Virtual::Reference ] )
get_function = Virtual::MethodSource.create_method(:Object,:_get_instance_variable , [ Virtual::Reference ] )
return get_function
me = get_function.receiver
var_name = get_function.args.first
@ -39,7 +39,7 @@ module Register
end
def _set_instance_variable(context , name = Virtual::Integer , value = Virtual::Integer )
set_function = Virtual::CompiledMethodInfo.create_method(:Object,:_set_instance_variable ,[Virtual::Reference ,Virtual::Reference] )
set_function = Virtual::MethodSource.create_method(:Object,:_set_instance_variable ,[Virtual::Reference ,Virtual::Reference] )
return set_function
receiver set_function
me = set_function.receiver

View File

@ -3,7 +3,7 @@ module Register
module Word
module ClassMethods
def putstring context
function = Virtual::CompiledMethodInfo.create_method(:Word , :putstring , [] )
function = Virtual::MethodSource.create_method(:Word , :putstring , [] )
Kernel.emit_syscall( function , :putstring )
function
end

View File

@ -7,7 +7,7 @@ require "virtual/parfait_adapter"
require "virtual/compiler"
require "virtual/instruction"
require "virtual/compiled_method_info"
require "virtual/method_source"
require "virtual/slots/slot"
require "virtual/type"
# the passes _are_ order dependant
@ -27,7 +27,7 @@ Sof::Volotile.add(Parfait::Frame , [:memory])
Sof::Volotile.add(Parfait::Message , [:memory])
Sof::Volotile.add(Parfait::BinaryCode , [:memory])
Sof::Volotile.add(Virtual::Block , [:method])
Sof::Volotile.add(Virtual::CompiledMethodInfo , [:current])
Sof::Volotile.add(Virtual::MethodSource , [:current])
class Fixnum
def fits_u8?

View File

@ -21,8 +21,8 @@ also in an similar way that objects have their classes at runtime.
source. Here we add compile functions to ast classes and compile the AST layer into
Virtual::Objects and Parfait::Values
The main objects are Space (lots of objects), BootClass (represents a class),
CompiledMethod (with Blocks and Instruction).
The main objects are Space (lots of objects), Parfait::Class ,
Method and MethodSource (with Blocks and Instruction).
**Virtual** Instructions get further transformed into **register** instructions.
This is done by an abstractly defined Register Machine with basic Intructions.
@ -59,4 +59,4 @@ and also implement very machine dependent functionality which nevertheless is fu
**Parfait** is that part of the runtime that can be coded in ruby.
It is parsed, like any other code and always included in the resulting binary.
**Builtin** is the part of the runtime that can not be coded in ruby (but is still needed).
This is coded by construction CompiledMethods in code and neccesarily machine dependant.
This is coded by construction MethodSource in code and necessarily machine dependant.

View File

@ -2,7 +2,7 @@ module Virtual
module Compiler
# Compiling is the conversion of the AST into 2 things:
# - code (ie sequences of Instructions inside Blocks) carried by CompiledMethods
# - code (ie sequences of Instructions inside Blocks) carried by MethodSource
# - an object graph containing all the Methods, their classes and Constants
#
# Some compile methods just add code, some may add structure (ie Blocks) while

View File

@ -14,24 +14,24 @@ All the headache comes from mixing those two up.*
Similarly, the result of compiling is two-fold: a static and a dynamic part.
- the static part are objects like the constants, but also defined classes and their methods
- the dynamic part is the code, which is stored as streams of instructions in the CompiledMethod
- the dynamic part is the code, which is stored as streams of instructions in the MethodSource
Too make things a little simpler, we create a very high level instruction stream at first and then
run transformation and optimization passes on the stream to improve it.
Each ast class gets a compile method that does the compilation.
#### Compiled Method and Instructions
#### MethodSource and Instructions
The first argument to the compile method is the CompiledMethod.
All code is encoded as a stream of Instructions in the CompiledMethod.
The first argument to the compile method is the MethodSource.
All code is encoded as a stream of Instructions in the MethodSource.
Instructions are stored as a list of Blocks, and Blocks are the smallest unit of code,
which is always linear.
Code is added to the method (using add_code), rather than working with the actual instructions.
This is so each compiling method can just do it's bit and be unaware of the larger structure
that is being created.
The genearal structure of the instructions is a graph
The general structure of the instructions is a graph
(with if's and whiles and breaks and what), but we build it to have one start and *one* end (return).

View File

@ -15,25 +15,25 @@ module Virtual
def self.compile_integer expression , method
int = expression.value
to = Return.new(Integer , int)
method.info.add_code Set.new( int , to )
method.source.add_code Set.new( int , to )
to
end
def self.compile_true expression , method
to = Return.new(Reference , true )
method.info.add_code Set.new( true , to )
method.source.add_code Set.new( true , to )
to
end
def self.compile_false expression , method
to = Return.new(Reference , false)
method.info.add_code Set.new( false , to )
method.source.add_code Set.new( false , to )
to
end
def self.compile_nil expression , method
to = Return.new(Reference , nil)
method.info.add_code Set.new( nil , to )
method.source.add_code Set.new( nil , to )
to
end
@ -47,9 +47,9 @@ module Virtual
if method.has_var(name)
# either an argument, so it's stored in message
if( index = method.has_arg(name))
method.info.add_code MessageGet.new(expression.name , index)
method.source.add_code MessageGet.new(expression.name , index)
else # or a local so it is in the frame
method.info.add_code FrameGet.new(expression.name , index)
method.source.add_code FrameGet.new(expression.name , index)
end
else
call = Ast::CallSiteExpression.new(expression.name , [] ) #receiver self is implicit
@ -62,7 +62,7 @@ module Virtual
clazz = Space.space.get_class_by_name name
raise "uups #{clazz}.#{name}" unless clazz
to = Return.new(Reference , clazz )
method.info.add_code Set.new( clazz , to )
method.source.add_code Set.new( clazz , to )
to
end
@ -71,8 +71,8 @@ module Virtual
# Clearly a TODO here to implement strings rather than reusing symbols
value = expression.string.to_sym
to = Return.new(Reference , value)
method.info.constants << value
method.info.add_code Set.new( value , to )
method.source.constants << value
method.source.add_code Set.new( value , to )
to
end
@ -85,16 +85,16 @@ module Virtual
raise "oh noo, nil from where #{expression.right.inspect}" unless r
index = method.has_arg(expression.left.name.to_sym)
if index
method.info.add_code Set.new(MessageSlot.new(index , r,type , r ) , Return.new)
method.source.add_code Set.new(MessageSlot.new(index , r,type , r ) , Return.new)
else
index = method.ensure_local(expression.left.name.to_sym)
method.info.add_code Set.new(FrameSlot.new(index , r.type , r ) , Return.new)
method.source.add_code Set.new(FrameSlot.new(index , r.type , r ) , Return.new)
end
r
end
def self.compile_variable expression, method
method.info.add_code InstanceGet.new(expression.name)
method.source.add_code InstanceGet.new(expression.name)
Return.new( Unknown )
end
end

View File

@ -6,9 +6,9 @@ module Virtual
def self.compile_callsite expession , method
me = Compiler.compile( expession.receiver , method )
method.info.add_code NewMessage.new
method.info.add_code Set.new( me , NewSelf.new(me.type))
method.info.add_code Set.new( expession.name.to_sym , NewMessageName.new())
method.source.add_code NewMessage.new
method.source.add_code Set.new( me , NewSelf.new(me.type))
method.source.add_code Set.new( expession.name.to_sym , NewMessageName.new())
compiled_args = []
expession.args.each_with_index do |arg , i|
#compile in the running method, ie before passing control
@ -18,13 +18,13 @@ module Virtual
# so the next free is +1
to = NewArgSlot.new(i + 1 ,val.type , val)
# (doing this immediately, not after the loop, so if it's a return it won't get overwritten)
method.info.add_code Set.new( val , to )
method.source.add_code Set.new( val , to )
compiled_args << to
end
method.info.add_code MessageSend.new(expession.name , me , compiled_args) #and pass control
method.source.add_code MessageSend.new(expession.name , me , compiled_args) #and pass control
# the effect of the method is that the NewMessage Return slot will be filled, return it
# (this is what is moved _inside_ above loop for such expressions that are calls (or constants))
Return.new( method.info.return_type )
Return.new( method.source.return_type )
end
end
end

View File

@ -18,8 +18,8 @@ module Virtual
r = Self.new()
class_name = method.for_class.name
end
new_method = CompiledMethodInfo.create_method(class_name, expression.name , args )
new_method.info.receiver = r
new_method = MethodSource.create_method(class_name, expression.name , args )
new_method.source.receiver = r
new_method.for_class.add_instance_method new_method
#frame = frame.new_frame
@ -28,7 +28,7 @@ module Virtual
return_type = Compiler.compile(ex,new_method )
raise return_type.inspect if return_type.is_a? Instruction
end
new_method.info.return_type = return_type
new_method.source.return_type = return_type
new_method
end
def scratch

View File

@ -6,18 +6,18 @@ module Virtual
# to execute the logic as the if states it, the blocks are the other way around
# so we can the jump over the else if true ,
# and the else joins unconditionally after the true_block
merge_block = method.info.new_block "if_merge" # last one, created first
true_block = method.info.new_block "if_true" # second, linked in after current, before merge
false_block = method.info.new_block "if_false" # directly next in order, ie if we don't jump we land here
merge_block = method.source.new_block "if_merge" # last one, created first
true_block = method.source.new_block "if_true" # second, linked in after current, before merge
false_block = method.source.new_block "if_false" # directly next in order, ie if we don't jump we land here
is = Compiler.compile(expression.cond, method )
# TODO should/will use different branches for different conditions.
# just a scetch : cond_val = cond_val.is_true?(method) unless cond_val.is_a? BranchCondition
method.info.add_code IsTrueBranch.new( true_block )
method.source.add_code IsTrueBranch.new( true_block )
# compile the true block (as we think of it first, even it is second in sequential order)
method.info.current true_block
method.source.current true_block
last = is
expression.if_true.each do |part|
last = Compiler.compile(part,method )
@ -25,16 +25,16 @@ module Virtual
end
# compile the false block
method.info.current false_block
method.source.current false_block
expression.if_false.each do |part|
#puts "compiling in if false #{part}"
last = Compiler.compile(part,method )
raise part.inspect if last.nil?
end
method.info.add_code UnconditionalBranch.new( merge_block )
method.source.add_code UnconditionalBranch.new( merge_block )
#puts "compiled if: end"
method.info.current merge_block
method.source.current merge_block
#TODO should return the union of the true and false types
last

View File

@ -53,7 +53,7 @@ module Virtual
@blocks = [@init]
@space.classes.values.each do |c|
c.instance_methods.each do |f|
nb = f.info.blocks
nb = f.source.blocks
@blocks += nb
end
end

View File

@ -4,7 +4,7 @@ module Virtual
# the static info of a method (with its compiled code, argument names etc ) is part of the
# runtime, ie found in Parfait::Method
# the info we create here is injected int the method and used only at compile-time
# the source we create here is injected into the method and used only at compile-time
# receiver
# return arg (usually mystery, but for coded ones can be more specific)
@ -27,11 +27,11 @@ module Virtual
# These (eg if/while) blocks may themselves have linear blocks ,but the last of these
# MUST have an uncoditional branch. And remember, all roads lead to return.
class CompiledMethodInfo
class MethodSource
# create method does two things
# first it creates the parfait method, for the given class, with given argument names
# second, it creates CompiledMethodInfo and attaches it to the method
# second, it creates MethodSource and attaches it to the method
#
# compile code then works with the method, but adds code tot the info
def self.create_method( class_name , method_name , args)
@ -40,7 +40,7 @@ module Virtual
clazz = Virtual.machine.space.get_class_by_name class_name
raise "No such class #{class_name}" unless clazz
method = clazz.create_instance_method( method_name , Virtual.new_list(args))
method.info = CompiledMethodInfo.new(method)
method.source = MethodSource.new(method)
method
end
# just passing the method object in for Instructions to make decisions (later)

View File

@ -157,7 +157,7 @@ module Parfait
end
class Method
attr_accessor :info
attr_accessor :source
end
class Word

View File

@ -15,7 +15,7 @@ module Virtual
object.init_layout
end
if( object.is_a? Parfait::Method)
object.info.constants.each{|c| keep(c) }
object.source.constants.each{|c| keep(c) }
end
layout = object.get_layout
keep layout

View File

@ -24,7 +24,7 @@ module Virtual
end
#puts "stayer #{function.name}"
@gonners.delete function
function.info.blocks.each do |block|
function.source.blocks.each do |block|
block.codes.each do |code|
if code.is_a? Virtual::MessageSend
@gonners.each do |stay|