starting on mom to risc

some docs too
This commit is contained in:
Torsten Rüger 2019-08-08 12:19:27 +03:00
parent 82c9f1d97f
commit 5994cd3276
9 changed files with 102 additions and 77 deletions

View File

@ -34,6 +34,12 @@ module Mom
@constants << const
end
# translate to Risc, ie a Risc level CallableCompiler
# abstract functon that needs to be implemented by Method/BlockCompiler
def to_risc
raise "abstract in #{self.class}"
end
# add a risc instruction after the current (insertion point)
# the added instruction will become the new insertion point
def add_code( instruction )

View File

@ -37,49 +37,26 @@ module Mom
self
end
# Translate code to whatever cpu is specified.
# Currently only :arm and :interpret
#
# Translating means translating the initial jump
# and then translating all methods
def translate( platform_sym )
platform_sym = platform_sym.to_s.capitalize
platform = Risc::Platform.for(platform_sym)
assemblers = translate_methods( platform.translator )
Risc::Linker.new(platform , assemblers , constants)
def to_risc( )
riscs = []
# to_risc all compilers
# for each suffling constnts and fist label, then all instructions (see below)
# then create risc collection
Risc::RiscCollection.new(riscs)
end
# go through all methods and translate them to cpu, given the translator
def translate_methods(translator)
compilers.collect do |compiler|
#log.debug "Translate method #{compiler.method.name}"
translate_method(compiler , translator)
end.flatten
end
# translate one method, which means the method itself and all blocks inside it
# returns an array of assemblers
def translate_method( method_compiler , translator)
all = []
all << translate_cpu( method_compiler , translator )
method_compiler.block_compilers.each do |block_compiler|
all << translate_cpu(block_compiler , translator)
# convert the given mom instruction to_risc and then add it (see add_code)
# continue down the instruction chain unti depleted
# (adding moves the insertion point so the whole mom chain is added as a risc chain)
def add_mom( instruction )
while( instruction )
raise "whats this a #{instruction}" unless instruction.is_a?(Mom::Instruction)
#puts "adding mom #{instruction.to_s}:#{instruction.next.to_s}"
instruction.to_risc( self )
reset_regs
#puts "adding risc #{risc.to_s}:#{risc.next.to_s}"
instruction = instruction.next
end
all
end
# compile the callable (method or block) to cpu
# return an Assembler that will then translate to binary
def translate_cpu(compiler , translator)
risc = compiler.risc_instructions
cpu_instructions = risc.to_cpu(translator)
nekst = risc.next
while(nekst)
cpu = nekst.to_cpu(translator) # returning nil means no replace
cpu_instructions << cpu if cpu
nekst = nekst.next
end
Risc::Assembler.new(compiler.callable , cpu_instructions )
end
end

View File

@ -35,6 +35,7 @@ require_relative "risc/callable_compiler"
require_relative "risc/method_compiler"
require_relative "risc/block_compiler"
require_relative "risc/assembler"
require_relative "risc/risc_collection"
class Integer
def fits_u8?

View File

@ -4,23 +4,21 @@ module Risc
# class shared by BlockCompiler and MethodCompiler
# - risc_instructions: The sequence of risc level instructions that mom was compiled to
# - cpu_instructions: The sequence of cpu specific instructions that the
# risc_instructions was compiled to
# Instructions derive from class Instruction and form a linked list
# - constants is an array of Parfait objects that need to be available
# - callable is a Method of Block
# - current instruction is where addidion happens
#
class CallableCompiler
def initialize( callable )
# Must pass the callable (method/block) and the constants that were parsed
# Also start instuction, usually a label is mandatory
def initialize( callable , constants , start)
@callable = callable
@regs = []
@constants = []
@constants = constants
@block_compilers = []
@risc_instructions = Risc.label(source_name, source_name)
@current = start = @risc_instructions
add_code Risc.label( source_name, "return_label")
Mom::ReturnSequence.new.to_risc(self)
add_code Risc.label( source_name, "unreachable")
@current = start
@current = @risc_instructions = start
reset_regs
end
attr_reader :risc_instructions , :constants , :block_compilers , :callable , :current
@ -32,19 +30,6 @@ module Risc
end
end
# convert the given mom instruction to_risc and then add it (see add_code)
# continue down the instruction chain unti depleted
# (adding moves the insertion point so the whole mom chain is added as a risc chain)
def add_mom( instruction )
while( instruction )
raise "whats this a #{instruction}" unless instruction.is_a?(Mom::Instruction)
#puts "adding mom #{instruction.to_s}:#{instruction.next.to_s}"
instruction.to_risc( self )
reset_regs
#puts "adding risc #{risc.to_s}:#{risc.next.to_s}"
instruction = instruction.next
end
end
# add a constant (which get created during compilation and need to be linked)
def add_constant(const)

View File

@ -1,16 +1,8 @@
module Mom
# The Compiler for the Mom level is a collection of Risc level Method compilers,
module Risc
# The Collection for the Risc level is a collection of Risc level Method compilers,
# plus functions to translate from the risc to cpu specific code.
#
# Builtin functions are created here, lazily, when translate is called.
# Instantiating builtin functions results in a MethodCompiler for that function, and
# to avoid confusion, these should be instantiated only once.
#
# As RubyCompiler pools source at the vool level, when several classes are compiled
# from vool to mom, several MomCompilers get instantiated. They must be merged before
# proceeding with translate. Thus we have a append method.
#
class MomCompiler
class RiscCollection
attr_reader :method_compilers
# Initialize with an array of risc MethodCompilers

View File

@ -63,7 +63,7 @@ module RubyX
# to generate binaries
def to_risc(platform)
mom = to_mom
mom.translate(platform)
mom.to_risc(platform)
end
# ruby_to_mom does exactly that, it transform the incoming ruby source (string)

View File

@ -1,4 +1,13 @@
module Vool
# This represents a class at the vool level. Vool is a syntax tree,
# so here the only child (or children) is a body.
# Body may either be a MethodStatement, or Statements (either empty or
# containing MethodStatement)
#
# We store the class name and the parfait class
#
# The Parfait class gets created lazily on the way down to mom, ie the clazz
# attribute will only be set after to_mom, or a direct call to create_class
class ClassStatement < Statement
attr_reader :name, :super_class_name , :body
attr_reader :clazz
@ -17,6 +26,10 @@ module Vool
end
end
# This create the Parfait class, and then transforms every method
#
# As there is no class equivalnet in code, a MomCollection is returned,
# which is just a list of Mom::MethodCompilers
def to_mom( _ )
create_class_object
method_compilers = body.statements.collect do |node|
@ -37,6 +50,10 @@ module Vool
@body.each(&block) if @body
end
# This creates the Parfait class. But doesn not hadle reopening yet, so only new classes
# Creating the class involves creating the instance_type (or an initial version)
# which means knowing all used names. So we go through the code looking for
# InstanceVariables or InstanceVariable Assignments, to do that.
def create_class_object
@clazz = Parfait.object_space.get_class_by_name(@name )
if(@clazz)
@ -53,6 +70,7 @@ module Vool
ivar_hash[node.name] = :Object
end
@clazz.set_instance_type( Parfait::Type.for_hash( @clazz , ivar_hash ) )
@clazz
end
end

View File

@ -9,7 +9,7 @@ module Vool
def to_mom(clazz)
raise( "no class in #{self}") unless clazz
method = clazz.add_method_for(name , make_arg_type , make_frame , body )
method = make_method(clazz)
compiler = method.compiler_for(clazz.instance_type)
each do |node| ## TODO: must account for nested blocks (someday)
next unless node.is_a?(BlockStatement)
@ -18,6 +18,15 @@ module Vool
compiler
end
# Class to be passed in is a Parfait class
# return VoolMethod
#
# extracted call to create the VoolMethod as this is the place
# where we have all the info. Used in testing.
def make_method(clazz)
clazz.add_method_for(name , make_arg_type , make_frame , body )
end
def each(&block)
block.call(self)
@body.each(&block)

View File

@ -0,0 +1,37 @@
require_relative "helper"
module Risc
class FakeCallable
end
class FakeCallableCompiler < CallableCompiler
def initialize(a,b,c)
super(a,b,c)
end
def source_name
"luke"
end
end
class TestCallableCompiler < MiniTest::Test
def setup
Parfait.boot!({})
label = Risc.label("hi","ho")
@compiler = FakeCallableCompiler.new(FakeCallable.new , [] , label)
end
def test_ok
assert @compiler
end
def test_current
assert @compiler.current
end
def test_current_label
assert_equal Label , @compiler.current.class
assert_equal "ho" , @compiler.current.name
end
def test_mom
assert @compiler.risc_instructions
end
def test_const
assert_equal Array , @compiler.constants.class
end
end
end