homing in on line length 100

This commit is contained in:
Torsten Ruger 2015-05-30 12:20:39 +03:00
parent 33d464f032
commit e651b57d08
29 changed files with 159 additions and 108 deletions

View File

@ -88,7 +88,7 @@ inlining, but i have at least an idea.
### Sof ### Sof
**S**alama **O**bject **F**ile format is a yaml ike format to look at code dumps and help testing. **S**alama **O**bject **F**ile format is a yaml like format to look at code dumps and help testing.
The dumper is ok and does produce (as intended) considerably denser dumps than yaml The dumper is ok and does produce (as intended) considerably denser dumps than yaml
When a reader is done (not started) the idea is to use sof as pre-compiled, language independent When a reader is done (not started) the idea is to use sof as pre-compiled, language independent

View File

@ -9,15 +9,15 @@ module Arm
# Also, shortcuts are created to easily instantiate Instruction objects. # Also, shortcuts are created to easily instantiate Instruction objects.
# Example: pop -> StackInstruction.new( {:opcode => :pop}.merge(options) ) # Example: pop -> StackInstruction.new( {:opcode => :pop}.merge(options) )
# Instructions work with options, so you can pass anything in, and the only thing the functions does # Instructions work with options, so you can pass anything in, and the only thing the functions
# is save you typing the clazz.new. It passes the function name as the :opcode # does is save you typing the clazz.new. It passes the function name as the :opcode
class ArmMachine class ArmMachine
# conditions specify all the possibilities for branches. Branches are b + condition # conditions specify all the possibilities for branches. Branches are b + condition
# Example: beq means brach if equal. # Example: beq means brach if equal.
# :al means always, so bal is an unconditional branch (but b() also works) # :al means always, so bal is an unconditional branch (but b() also works)
CONDITIONS = [ :al , :eq , :ne , :lt , :le, :ge, :gt , :cs , :mi , :hi , :cc , :pl, :ls , :vc , :vs ] CONDITIONS = [:al ,:eq ,:ne ,:lt ,:le ,:ge,:gt ,:cs ,:mi ,:hi ,:cc ,:pl,:ls ,:vc ,:vs]
# here we create the shortcuts for the "standard" instructions, see above # here we create the shortcuts for the "standard" instructions, see above
# Derived machines may use own instructions and define functions for them if so desired # Derived machines may use own instructions and define functions for them if so desired
@ -67,8 +67,8 @@ module Arm
# an actual machine must create derived classes (from this base class) # an actual machine must create derived classes (from this base class)
# These instruction classes must follow a naming pattern and take a hash in the contructor # These instruction classes must follow a naming pattern and take a hash in the contructor
# Example, a mov() opcode instantiates a Register::MoveInstruction # Example, a mov() opcode instantiates a Register::MoveInstruction
# for an Arm machine, a class Arm::MoveInstruction < Register::MoveInstruction exists, and it will # for an Arm machine, a class Arm::MoveInstruction < Register::MoveInstruction exists, and it
# be used to define the mov on an arm machine. # will be used to define the mov on an arm machine.
# This methods picks up that derived class and calls a define_instruction methods that can # This methods picks up that derived class and calls a define_instruction methods that can
# be overriden in subclasses # be overriden in subclasses
def self.define_instruction_one(inst , clazz , defaults = {} ) def self.define_instruction_one(inst , clazz , defaults = {} )

View File

@ -28,7 +28,8 @@ module Arm
#codition codes can be applied to many instructions and thus save branches #codition codes can be applied to many instructions and thus save branches
# :al => always , :eq => equal and so on # :al => always , :eq => equal and so on
# eq mov if equal :moveq r1 r2 (also exists as function) will only execute if the last operation was 0 # eq mov if equal :moveq r1 r2 (also exists as function) will only execute
# if the last operation was 0
COND_CODES = { COND_CODES = {
:al => 0b1110, :eq => 0b0000, :al => 0b1110, :eq => 0b0000,
:ne => 0b0001, :cs => 0b0010, :ne => 0b0001, :cs => 0b0010,
@ -39,7 +40,8 @@ module Arm
:ge => 0b1010, :gt => 0b1100, :ge => 0b1010, :gt => 0b1100,
:vs => 0b0110 :vs => 0b0110
} }
#return the bit pattern for the @attributes[:condition_code] variable, which signals the conditional code # return the bit pattern for the @attributes[:condition_code] variable,
# which signals the conditional code
def cond_bit_code def cond_bit_code
COND_CODES[@attributes[:condition_code]] or throw "no code found for #{@attributes[:condition_code]}" COND_CODES[@attributes[:condition_code]] or throw "no code found for #{@attributes[:condition_code]}"
end end
@ -96,7 +98,8 @@ module Arm
op = 0 op = 0
#codes that one can shift, first two probably most common. #codes that one can shift, first two probably most common.
# l (in lsr) means logical, ie unsigned, a (in asr) is arithmetic, ie signed # l (in lsr) means logical, ie unsigned, a (in asr) is arithmetic, ie signed
{'lsl' => 0b000, 'lsr' => 0b010, 'asr' => 0b100, 'ror' => 0b110, 'rrx' => 0b110}.each do |short, bin| shift_codes = {'lsl' => 0b000, 'lsr' => 0b010, 'asr' => 0b100, 'ror' => 0b110, 'rrx' => 0b110}
shift_codes.each do |short, bin|
long = "shift_#{short}".to_sym long = "shift_#{short}".to_sym
if shif = @attributes[long] if shif = @attributes[long]
# TODO delete this code, AFTER you understand it # TODO delete this code, AFTER you understand it

View File

@ -36,8 +36,10 @@ module Arm
if arg.is_a?(Virtual::Block) or arg.is_a?(Parfait::Method) if arg.is_a?(Virtual::Block) or arg.is_a?(Parfait::Method)
#relative addressing for jumps/calls #relative addressing for jumps/calls
diff = arg.position - self.position diff = arg.position - self.position
# but because of the arm "theoretical" 3- stage pipeline, we have to subtract 2 words (fetch/decode) # but because of the arm "theoretical" 3- stage pipeline,
# But, for methods, this happens to be the size of the object header, so there it balances out, but not blocks # we have to subtract 2 words (fetch/decode)
# But, for methods, this happens to be the size of the object header,
# so there it balances out, but not blocks
diff -= 8 if arg.is_a?(Virtual::Block) diff -= 8 if arg.is_a?(Virtual::Block)
arg = diff arg = diff
end end

View File

@ -19,12 +19,12 @@ module Arm
attr_accessor :to , :from attr_accessor :to , :from
# arm intructions are pretty sensible, and always 4 bytes (thumb not supported) # arm intructions are pretty sensible, and always 4 bytes (thumb not supported)
# but not all constants fit into the part of the instruction that is left after the instruction code, # but not all constants fit into the part of the instruction that is left after the instruction
# so large moves have to be split into two instructions. # code, so large moves have to be split into two instructions.
# we handle this "transparently", just this instruction looks longer # we handle this "transparently", just this instruction looks longer
# alas, full transparency is not achieved as we only know when to use 2 instruction once we know where the # alas, full transparency is not achieved as we only know when to use 2 instruction once we
# other object is, and that position is only set after code positions have been determined (in link) and so # know where the other object is, and that position is only set after code positions have been
# see below in assemble # determined (in link) and so see below in assemble
def mem_length def mem_length
@extra ? 8 : 4 @extra ? 8 : 4
end end
@ -52,13 +52,14 @@ module Arm
operand = op_with_rot operand = op_with_rot
immediate = 1 immediate = 1
else else
# unfortunately i was wrong in thinking the pi is armv7. The good news is the code below implements # unfortunately i was wrong in thinking the pi is armv7. The good news is the code
# the movw instruction (armv7 for moving a word) and works # below implements the movw instruction (armv7 for moving a word) and works
#armv7 raise "Too big #{right} " if (right >> 16) > 0 #armv7 raise "Too big #{right} " if (right >> 16) > 0
#armv7 operand = (right & 0xFFF) #armv7 operand = (right & 0xFFF)
#armv7 immediate = 1 #armv7 immediate = 1
#armv7 rn = (right >> 12) #armv7 rn = (right >> 12)
# a little STRANGE, that the armv7 movw (move a 2 byte word) is an old test opcode, but there it is # a little STRANGE, that the armv7 movw (move a 2 byte word) is an old test opcode,
# but there it is
#armv7 @attributes[:opcode] = :tst #armv7 @attributes[:opcode] = :tst
raise "No negatives implemented #{right} " if right < 0 raise "No negatives implemented #{right} " if right < 0
# and so it continues: when we notice that the const doesn't fit, first time we raise an # and so it continues: when we notice that the const doesn't fit, first time we raise an
@ -74,7 +75,8 @@ module Arm
raise "no fit for #{right}" unless operand raise "no fit for #{right}" unless operand
immediate = 1 immediate = 1
@extra = ArmMachine.add( to , to , (right & 0xFF) ) @extra = ArmMachine.add( to , to , (right & 0xFF) )
#TODO: this is still a hack, as it does not encode all possible values. The way it _should_ be done #TODO: this is still a hack, as it does not encode all possible values.
# The way it _should_ be done
# is to check that the first part is doabe with u8_with_rr AND leaves a u8 remainder # is to check that the first part is doabe with u8_with_rr AND leaves a u8 remainder
end end
elsif (right.is_a?(Symbol) or right.is_a?(::Register::RegisterReference)) elsif (right.is_a?(Symbol) or right.is_a?(::Register::RegisterReference))

View File

@ -67,7 +67,8 @@ module Arm
end end
def syscall block , num def syscall block , num
# This is very arm specific, syscall number is passed in r7, other arguments like a c call ie 0 and up # This is very arm specific, syscall number is passed in r7,
# other arguments like a c call ie 0 and up
sys = Virtual::Integer.new( Virtual::RegisterReference.new(SYSCALL_REG) ) sys = Virtual::Integer.new( Virtual::RegisterReference.new(SYSCALL_REG) )
ret = Virtual::Integer.new( Virtual::RegisterReference.new(RETURN_REG) ) ret = Virtual::Integer.new( Virtual::RegisterReference.new(RETURN_REG) )
block.add_code mov( sys , num ) block.add_code mov( sys , num )
@ -78,4 +79,3 @@ module Arm
end end
end end

View File

@ -1,7 +1,8 @@
module Arm module Arm
# Arm stores the return address in a register (not on the stack) # Arm stores the return address in a register (not on the stack)
# The register is called link , or lr for short . Maybe because it provides the "link" back to the caller (?) # The register is called link , or lr for short .
# Maybe because it provides the "link" back to the caller (?)
# the vm defines a register for the location, so we store it there. # the vm defines a register for the location, so we store it there.

View File

@ -44,7 +44,8 @@ module Virtual
# get the function and if not found, try superclasses. raise error if not found # get the function and if not found, try superclasses. raise error if not found
def resolve_method name def resolve_method name
fun = get_function name fun = get_function name
# TODO THE BOOK says is class A derives from B , then the metaclass of A derives from the metaclass of B # TODO THE BOOK says is class A derives from B , then the metaclass of
# A derives from the metaclass of B
# just get to it ! (and stop whimpering) # just get to it ! (and stop whimpering)
raise "Method not found #{name} , for #{inspect}" unless fun raise "Method not found #{name} , for #{inspect}" unless fun
fun fun

View File

@ -14,7 +14,8 @@ module Parfait
# The Space contains all objects for a program. In functional terms it is a program, but in oo # The Space contains all objects for a program. In functional terms it is a program, but in oo
# it is a collection of objects, some of which are data, some classes, some functions # it is a collection of objects, some of which are data, some classes, some functions
# The main entry is a function called (of all things) "main", This _must be supplied by the compling # The main entry is a function called (of all things) "main".
# This _must be supplied by the compled code (similar to c)
# There is a start and exit block that call main, which receives an List of strings # There is a start and exit block that call main, which receives an List of strings
# While data ususally would live in a .data section, we may also "inline" it into the code # While data ususally would live in a .data section, we may also "inline" it into the code

View File

@ -1,12 +1,13 @@
module Register module Register
class LinkException < Exception class LinkException < Exception
end end
# Assmble the object space into a binary. # Assemble the object space into a binary.
# Link first to get positions, then assemble # Link first to get positions, then assemble
# link and assemble functions for each class are close to each other, so to get them the same.
# meaning: as the link function determines the length of an object and the assemble actually writes the bytes # The link function determines the length of an object and the assemble actually
# they are pretty much dependant. In an earlier version they were functions on the objects, but now it # writes the bytes they are pretty much dependant. In an earlier version they were
# has gone to a visitor pattern. # functions on the objects, but now it has gone to a visitor pattern.
class Assembler class Assembler
TYPE_REF = 0 TYPE_REF = 0
TYPE_INT = 1 TYPE_INT = 1
@ -119,7 +120,9 @@ module Register
# write type and layout of the instance, and the variables that are passed # write type and layout of the instance, and the variables that are passed
# variables ar values, ie int or refs. For refs the object needs to save the object first # variables ar values, ie int or refs. For refs the object needs to save the object first
def assemble_self( object , variables ) def assemble_self( object , variables )
raise "Object(#{object.object_id}) not linked #{object.inspect}" unless @objects[object.object_id] unless @objects[object.object_id]
raise "Object(#{object.object_id}) not linked #{object.inspect}"
end
type = type_word(variables) type = type_word(variables)
@stream.write_uint32( type ) @stream.write_uint32( type )
write_ref_for(object.layout[:names] ) write_ref_for(object.layout[:names] )
@ -207,9 +210,8 @@ module Register
private private
# write means we write the resulting address straight into the assembler stream (ie don't return it) # write means we write the resulting address straight into the assembler stream
# object means the object of which we write the address # object means the object of which we write the address
# and we write the address into the self, given as second parameter
def write_ref_for object def write_ref_for object
@stream.write_sint32 object.position @stream.write_sint32 object.position
end end

View File

@ -2,7 +2,8 @@
module Builtin module Builtin
module Integer module Integer
module ClassMethods module ClassMethods
# The conversion to base10 is quite a bit more complicated than i thought. The bulk of it is in div10 # The conversion to base10 is quite a bit more complicated than i thought.
# The bulk of it is in div10
# We set up variables, do the devision and write the result to the string # We set up variables, do the devision and write the result to the string
# then check if were done and recurse if neccessary # then check if were done and recurse if neccessary
# As we write before we recurse (save a push) we write the number backwards # As we write before we recurse (save a push) we write the number backwards

View File

@ -4,7 +4,8 @@ module Builtin
# return the index of the variable. Now "normal" code can't really do anything with that, but # return the index of the variable. Now "normal" code can't really do anything with that, but
# set/get instance variable use it. # set/get instance variable use it.
# This is just a placeholder, as we code this in ruby, but the instance methods need the definition before. # This is just a placeholder, as we code this in ruby,
# but the instance methods need the definition before.
def index_of context , name = Virtual::Integer def index_of context , name = Virtual::Integer
index_function = Virtual::CompiledMethodInfo.create_method("Object" , "index_of" , [Virtual::Reference] ) index_function = Virtual::CompiledMethodInfo.create_method("Object" , "index_of" , [Virtual::Reference] )
index_function.info.return_type = Virtual::Integer index_function.info.return_type = Virtual::Integer
@ -22,7 +23,8 @@ module Builtin
# i = self.index_of(var) # i = self.index_of(var)
# return at_index(i) # return at_index(i)
# end # end
# 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 # 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 def _get_instance_variable context , name = Virtual::Integer
get_function = Virtual::CompiledMethodInfo.create_method("Object","_get_instance_variable" , [ Virtual::Reference ] ) get_function = Virtual::CompiledMethodInfo.create_method("Object","_get_instance_variable" , [ Virtual::Reference ] )
return get_function return get_function

View File

@ -2,7 +2,8 @@ module Register
# RegisterReference is not the name for a register, "only" for a certain use of it. # RegisterReference is not the name for a register, "only" for a certain use of it.
# In a way it is like a variable name, a storage location. The location is a register off course, # In a way it is like a variable name, a storage location. The location is a register off course,
# but which register can be changed, and _all_ instructions sharing the RegisterReference then use that register # but which register can be changed, and _all_ instructions sharing the RegisterReference then
# use that register
# In other words a simple level of indirection, or change from value to reference sematics. # In other words a simple level of indirection, or change from value to reference sematics.
class RegisterReference class RegisterReference

View File

@ -15,8 +15,8 @@ module Virtual
# They also have local variables. # They also have local variables.
# Code-wise Methods are made up from a list of Blocks, in a similar way blocks are made up of Instructions # Code-wise Methods are made up from a list of Blocks, in a similar way blocks are made up of
# The function starts with one block, and that has a start and end (return) # Instructions. The function starts with one block, and that has a start and end (return)
# Blocks can be linked in two ways: # Blocks can be linked in two ways:
# -linear: flow continues from one to the next as they are sequential both logically and # -linear: flow continues from one to the next as they are sequential both logically and
@ -59,7 +59,9 @@ module Virtual
# add an instruction after the current (insertion point) # add an instruction after the current (insertion point)
# the added instruction will become the new insertion point # the added instruction will become the new insertion point
def add_code instruction def add_code instruction
raise instruction.inspect unless (instruction.is_a?(Instruction) or instruction.is_a?(Register::Instruction)) unless (instruction.is_a?(Instruction) or instruction.is_a?(Register::Instruction))
raise instruction.inspect
end
@current.add_code(instruction) #insert after current @current.add_code(instruction) #insert after current
self self
end end
@ -79,21 +81,24 @@ module Virtual
used.uniq used.uniq
end end
# control structures need to see blocks as a graph, but they are stored as a list with implict branches # control structures need to see blocks as a graph, but they are stored as a list with implict
# branches
# So when creating a new block (with new_block), it is only added to the list, but instructions # So when creating a new block (with new_block), it is only added to the list, but instructions
# still go to the current one # still go to the current one
# With this function one can change the current block, to actually code it. # With this function one can change the current block, to actually code it.
# This juggling is (unfortunately) neccessary, as all compile functions just keep puring their code into the # This juggling is (unfortunately) neccessary, as all compile functions just keep puring their
# method and don't care what other compiles (like if's) do. # code into the method and don't care what other compiles (like if's) do.
# Example: while, needs 2 extra blocks # Example: while, needs 2 extra blocks
# 1 condition code, must be its own blockas we jump back to it # 1 condition code, must be its own blockas we jump back to it
# - the body, can actually be after the condition as we don't need to jump there # - the body, can actually be after the condition as we don't need to jump there
# 2 after while block. Condition jumps here # 2 after while block. Condition jumps here
# After block 2, the function is linear again and the calling code does not need to know what happened # After block 2, the function is linear again and the calling code does not need to know what
# happened
# But subsequent statements are still using the original block (self) to add code to # But subsequent statements are still using the original block (self) to add code to
# So the while expression creates the extra blocks, adds them and the code and then "moves" the insertion point along # So the while expression creates the extra blocks, adds them and the code and then "moves"
# the insertion point along
def current block def current block
@current = block @current = block
self self

View File

@ -79,7 +79,9 @@ module Virtual
#attr_reader :left, :right #attr_reader :left, :right
def self.compile_assignment expression , method def self.compile_assignment expression , method
raise "must assign to NameExpression , not #{expression.left}" unless expression.left.instance_of? Ast::NameExpression unless expression.left.instance_of? Ast::NameExpression
raise "must assign to NameExpression , not #{expression.left}"
end
r = Compiler.compile(expression.right , method ) r = Compiler.compile(expression.right , method )
raise "oh noo, nil from where #{expression.right.inspect}" unless r raise "oh noo, nil from where #{expression.right.inspect}" unless r
index = method.has_arg(Virtual.new_word name) index = method.has_arg(Virtual.new_word name)

View File

@ -4,7 +4,8 @@ module Virtual
def self.compile_if expression , method def self.compile_if expression , method
# to execute the logic as the if states it, the blocks are the other way around # 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 # 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 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 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 false_block = method.info.new_block "if_false" # directly next in order, ie if we don't jump we land here

View File

@ -10,7 +10,8 @@ module Virtual
puts "Created class #{clazz.name.inspect}" puts "Created class #{clazz.name.inspect}"
expression.expressions.each do |expr| expression.expressions.each do |expr|
# check if it's a function definition and add # check if it's a function definition and add
# if not, execute it, but that does means we should be in salama (executable), not ruby. ie throw an error for now # if not, execute it, but that does means we should be in salama (executable), not ruby.
# ie throw an error for now
raise "only functions for now #{expr.inspect}" unless expr.is_a? Ast::FunctionExpression raise "only functions for now #{expr.inspect}" unless expr.is_a? Ast::FunctionExpression
#puts "compiling expression #{expression}" #puts "compiling expression #{expression}"
expression_value = Compiler.compile(expr,method ) expression_value = Compiler.compile(expr,method )

View File

@ -15,7 +15,9 @@ module Virtual
if expression_value.is_a?(IntegerConstant) or expression_value.is_a?(ObjectConstant) if expression_value.is_a?(IntegerConstant) or expression_value.is_a?(ObjectConstant)
return_reg.load into , expression_value return_reg.load into , expression_value
else else
return_reg.move( into, expression_value ) if expression_value.register_symbol != return_reg.register_symbol if expression_value.register_symbol != return_reg.register_symbol
return_reg.move( into, expression_value )
end
end end
#function.set_return return_reg #function.set_return return_reg
return return_reg return return_reg

View File

@ -1,10 +1,11 @@
module Virtual module Virtual
# the first instruction we need is to stop. Off course in a real machine this would be a syscall, but that is just # the first instruction we need is to stop. Off course in a real machine this would be a syscall,
# an implementation (in a programm it would be a function). # but that is just an implementation (in a programm it would be a function).
# But in a virtual machine, not only do we need this instruction, # But in a virtual machine, not only do we need this instruction,
# it is indeed the first instruction as just this instruction is the smallest possible programm for the machine. # it is indeed the first instruction as just this instruction is the smallest possible programm
# for the machine.
# As such it is the next instruction for any first instruction that we generate. # As such it is the next instruction for any first instruction that we generate.
class Halt < Instruction class Halt < Instruction
end end

View File

@ -1,7 +1,9 @@
module Virtual module Virtual
# Get a instance variable by _name_ . So we have to resolve the name to an index to trnsform into a Slot # Get a instance variable by _name_ . So we have to resolve the name to an index to
# The slot may the be used in a set on left or right hand. The transformation is done by GetImplementation # transform into a Slot
# The slot may the be used in a set on left or right hand.
# The transformation is done by GetImplementation
class InstanceGet < Instruction class InstanceGet < Instruction
def initialize name def initialize name
@name = name.to_sym @name = name.to_sym

View File

@ -1,8 +1,8 @@
module Virtual module Virtual
# class for Set instructions, A set is basically a mem move. # class for Set instructions, A set is basically a mem move.
# to and from are indexes into the known objects(frame,message,self and new_message), these are represented as slots # to and from are indexes into the known objects(frame,message,self and new_message),
# (see there) # these are represented as slots (see there)
# from may be a Constant (Object,Integer,String,Class) # from may be a Constant (Object,Integer,String,Class)
class Set < Instruction class Set < Instruction
def initialize to , from def initialize to , from

View File

@ -1,34 +1,37 @@
module Virtual module Virtual
# The Virtual Machine is a value based virtual machine in which ruby is implemented. While it is value based, # The Virtual Machine is a value based virtual machine in which ruby is implemented.
# it resembles oo in basic ways of object encapsulation and method invokation, it is a "closed" / static sytem # While it is value based, it resembles oo in basic ways of object encapsulation and method
# in that all types are know and there is no dynamic dispatch (so we don't bite our tail here). # invocation, it is a "closed" / static sytem in that all types are know and there is no
# dynamic dispatch (so we don't bite our tail here).
# #
# It is minimal and realistic and low level # It is minimal and realistic and low level
# - minimal means that if one thing can be implemented by another, it is left out. This is quite the opposite from # - minimal means that if one thing can be implemented by another, it is left out. This is quite
# ruby, which has several loops, many redundant if forms and the like. # the opposite from ruby, which has several loops, many redundant if forms and the like.
# - realistic means it is easy to implement on a 32 bit machine (arm) and possibly 64 bit. Memory access, a stack, # - realistic means it is easy to implement on a 32 bit machine (arm) and possibly 64 bit.
# some registers of same size are the underlying hardware. (not ie byte machine) # Memory access,some registers of same size are the underlying hardware. (not ie byte machine)
# - low level means it's basic instructions are realively easily implemented in a register machine. ie send is not # - low level means it's basic instructions are realively easily implemented in a register machine.
# a an instruction but a function. # ie send is not a an instruction but a function.
# #
# So the memory model of the machine allows for indexed access into an "object" . A fixed number of objects exist # So the memory model of the machine allows for indexed access into an "object" .
# (ie garbage collection is reclaming, not destroying and recreating) although there may be a way to increase that number. # A fixed number of objects exist (ie garbage collection is reclaming, not destroying and
# recreating) although there may be a way to increase that number.
# #
# The ast is transformed to virtaul-machine objects, some of which represent code, some data. # The ast is transformed to virtaul-machine objects, some of which represent code, some data.
# #
# The next step transforms to the register machine layer, which is what actually executes. # The next step transforms to the register machine layer, which is what actually executes.
# #
# More concretely, a virtual machine is a sort of oo turing machine, it has a current instruction, executes the # More concretely, a virtual machine is a sort of oo turing machine, it has a current instruction,
# instructions, fetches the next one and so on. # executes the instructions, fetches the next one and so on.
# Off course the instructions are not soo simple, but in oo terms quite so. # Off course the instructions are not soo simple, but in oo terms quite so.
# #
# The machine is virtual in the sense that it is completely modeled in software, # The machine is virtual in the sense that it is completely modeled in software,
# it's complete state explicitly available (not implicitly by walking stacks or something) # it's complete state explicitly available (not implicitly by walking stacks or something)
# The machine has a no register, but local variables, a scope at each point in time. # The machine has a no register, but local variables, a scope at each point in time.
# Scope changes with calls and blocks, but is saved at each level. In terms of lower level implementation this means # Scope changes with calls and blocks, but is saved at each level. In terms of lower level
# that the the model is such that what is a variable in ruby, never ends up being just on the pysical stack. # implementation this means that the the model is such that what is a variable in ruby,
# never ends up being just on the pysical stack.
# #
class Machine class Machine

View File

@ -1,21 +1,23 @@
module Virtual module Virtual
# So when an object calls a method, or sends a message, this is what it sends: a Message # So when an object calls a method, or sends a message, this is what it sends: a Message
# A message contains the sender, return and exceptional return addresses,the arguments, and a slot for the frame. # A message contains the sender, return and exceptional return addresses,the arguments,
# and a slot for the frame.
# As such it is a very run-time object, deep in the machinery as it were, and does not have meaningful # As such it is a very run-time object, deep in the machinery as it were, and does not have
# methods you could call at compile time. # meaningful methods you could call at compile time.
# The methods that are there, are nevertheless meant to be called at compile time and generate code, rather than # The methods that are there, are nevertheless meant to be called at compile time and generate
# executing it. # code, rather than executing it.
# The caller creates the Message and passes control to the receiver's method # The caller creates the Message and passes control to the receiver's method
# The receiver create a new Frame to hold local and temporary variables and (later) creates default values for # The receiver create a new Frame to hold local and temporary variables and (later) creates
# arguments that were not passed # default values for arguments that were not passed
# How the actual finding of the method takes place (acording to the ruby rules) is not simple, but as there is a # How the actual finding of the method takes place (acording to the ruby rules) is not simple,
# guaranteed result (be it method_missing) it does not matter to the passing mechanism described # but as there is a guaranteed result (be it method_missing) it does not matter to the passing
# mechanism described
# During compilation Message and frame objects are created to do type analysis # During compilation Message and frame objects are created to do type analysis

View File

@ -25,7 +25,9 @@ module FakeMem
end end
#TODO, this is copied from module Positioned, maybe avoid duplication ? #TODO, this is copied from module Positioned, maybe avoid duplication ?
def position def position
raise "position accessed but not set at #{mem_length} for #{self.inspect[0...1000]}" if @position == nil if @position == nil
raise "position accessed but not set at #{mem_length} for #{self.inspect[0...1000]}"
end
@position @position
end end
def set_position pos def set_position pos

View File

@ -3,14 +3,18 @@ module Virtual
# Frames and Message are very similar apart from the class name # Frames and Message are very similar apart from the class name
# - All existing instances are stored in the space for both # - All existing instances are stored in the space for both
# - Size is currently 2, ie 16 words (TODO a little flexibility here would not hurt, but the mountain is big) # - Size is currently 2, ie 16 words
# - Unused instances for a linked list with their first instance variable. This is HARD coded to avoid any lookup # (TODO a little flexibility here would not hurt, but the mountain is big)
# - Unused instances for a linked list with their first instance variable.
# This is HARD coded to avoid any lookup
# Just as a reminder: a message object is created before a send and holds return address/message and arguemnts + self # Just as a reminder: a message object is created before a send and holds return address/message
# frames are created upon entering a method and hold local and temporary variables # and arguemnts + self frames are created upon entering a method and hold local and temporary
# as a result one of each is created for every single method call. A LOT, so make it fast luke # variables as a result one of each is created for every single method call.
# Note: this is off course the reason for stack based implementations that just increment a known pointer/register or # A LOT, so make it fast luke
# something. But i think most programs are memory bound and a few extra instructions don't hurt. # Note: this is off course the reason for stack based implementations that just increment
# a known pointer/register or something. But i think most programs are memory bound
# and a few extra instructions don't hurt.
# After all, we are buying a big prize:oo, otherwise known as sanity. # After all, we are buying a big prize:oo, otherwise known as sanity.
class FrameImplementation class FrameImplementation

View File

@ -1,7 +1,9 @@
module Virtual module Virtual
# This implements instance variable get (not the opposite of Set, such a thing does not exists, their slots) # This implements instance variable get (not the opposite of Set,
# such a thing does not exists, their slots)
# Ivar get needs to acces the layout, find the index of the name, and shuffle the data to return register # Ivar get needs to acces the layout, find the index of the name,
# and shuffle the data to return register
# In short it's so complicated we implement it in ruby and stick the implementation here # In short it's so complicated we implement it in ruby and stick the implementation here
class GetImplementation class GetImplementation
def run block def run block

View File

@ -4,9 +4,11 @@ module Virtual
# That off course opens up an endless loop possibility that we stop by # That off course opens up an endless loop possibility that we stop by
# implementing Class and Module methods # implementing Class and Module methods
# Note: I find it slightly unsemetrical that the NewMessage object needs to be created before this instruction # Note: I find it slightly unsymmetrical that the NewMessage object needs to be created
# This is because all expressions create a (return) value and that return value is overwritten by the next # before this instruction.
# expression unless saved. And since the message is the place to save it it needs to exist. qed # This is because all expressions create a (return) value and that return value is
# overwritten by the next expression unless saved.
# And since the message is the place to save it it needs to exist. qed
class SendImplementation class SendImplementation
def run block def run block
block.codes.dup.each do |code| block.codes.dup.each do |code|

View File

@ -4,14 +4,18 @@ module Virtual
# Data in a Block is usefull in the same way data in objects is. Plocks being otherwise just code. # Data in a Block is usefull in the same way data in objects is. Plocks being otherwise just code.
# #
# But the concept is not quite straigtforwrd: If one thinks of a Plock embedded in a normal method, # But the concept is not quite straigtforwrd: If one thinks of a Plock embedded in a normal method,
# the a data in the Plock would be static data. In OO terms this comes quite close to a Proc, if the data is the local # the a data in the Plock would be static data. In OO terms this comes quite close to a Proc,
# variables. Quite possibly they shall be used to implement procs, but that is not the direction now. # if the data is the local variables.
# Quite possibly they shall be used to implement procs, but that is not the direction now.
# #
# For now we use Plocks behaind the scenes as it were. In the code that you never see, method invocation mainly. # For now we use Plocks behaind the scenes as it were. In the code that you never see,
# method invocation mainly.
# #
# In terms of implementation the Plock is a Block with data (Not too much data, mainly a couple of references). # In terms of implementation the Plock is a Block with data
# The block writes it's instructions as normal, but a jump is inserted as the last instruction. The jump is to the # (Not too much data, mainly a couple of references).
# next block, over the data that is inserted after the block code (and so before the next) # The block writes it's instructions as normal, but a jump is inserted as the last instruction.
# The jump is to the next block, over the data that is inserted after the block code
# (and so before the next)
# #
# It follows that Plocks should be linear blocks. # It follows that Plocks should be linear blocks.
class Plock < Block class Plock < Block

View File

@ -2,7 +2,9 @@ require_relative "type"
module Positioned module Positioned
def position def position
raise "position accessed but not set at #{mem_length} for #{self.inspect[0...500]}" if @position == nil if @position == nil
raise "position accessed but not set at #{mem_length} for #{self.inspect[0...500]}"
end
@position @position
end end
def set_position pos def set_position pos