2018-07-10 21:02:11 +02:00
|
|
|
module Risc
|
|
|
|
|
|
|
|
# CallableCompiler is used to generate risc instructions. It is an abstact base
|
|
|
|
# class shared by BlockCompiler and MethodCompiler
|
|
|
|
|
2019-10-03 20:07:55 +02:00
|
|
|
# - risc_instructions: The sequence of risc level instructions that slot machine was
|
|
|
|
# compiled to
|
2018-07-10 21:02:11 +02:00
|
|
|
# Instructions derive from class Instruction and form a linked list
|
2019-08-08 11:19:27 +02:00
|
|
|
# - constants is an array of Parfait objects that need to be available
|
|
|
|
# - callable is a Method of Block
|
|
|
|
# - current instruction is where addidion happens
|
|
|
|
#
|
2018-07-10 21:02:11 +02:00
|
|
|
class CallableCompiler
|
2019-09-28 16:24:10 +02:00
|
|
|
include Util::CompilerList
|
2018-07-10 21:02:11 +02:00
|
|
|
|
2019-08-10 11:42:47 +02:00
|
|
|
# Must pass the callable (method/block)
|
2019-08-08 11:19:27 +02:00
|
|
|
# Also start instuction, usually a label is mandatory
|
2019-10-03 19:55:41 +02:00
|
|
|
def initialize( callable , slot_label)
|
2019-09-15 11:58:43 +02:00
|
|
|
raise "No method" unless callable
|
2018-07-30 09:26:11 +02:00
|
|
|
@callable = callable
|
2020-02-27 10:57:18 +01:00
|
|
|
@allocator = Allocator.new
|
2019-08-10 11:42:47 +02:00
|
|
|
@constants = []
|
2019-10-03 19:55:41 +02:00
|
|
|
@current = @risc_instructions = slot_label.risc_label(self)
|
2020-02-27 10:57:18 +01:00
|
|
|
@allocator.reset_regs
|
2018-08-02 16:36:39 +02:00
|
|
|
end
|
2019-09-28 16:24:10 +02:00
|
|
|
attr_reader :risc_instructions , :constants , :callable , :current
|
2018-08-02 16:36:39 +02:00
|
|
|
|
2020-02-26 18:01:01 +01:00
|
|
|
# find the return label. Every methd should have exactly one
|
2018-08-02 16:36:39 +02:00
|
|
|
def return_label
|
|
|
|
@risc_instructions.each do |ins|
|
|
|
|
next unless ins.is_a?(Label)
|
|
|
|
return ins if ins.name == "return_label"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-07-10 21:02:11 +02:00
|
|
|
# add a constant (which get created during compilation and need to be linked)
|
2020-02-26 18:01:01 +01:00
|
|
|
# constants must be Parfait instances
|
2018-07-10 21:02:11 +02:00
|
|
|
def add_constant(const)
|
|
|
|
raise "Must be Parfait #{const}" unless const.is_a?(Parfait::Object)
|
|
|
|
@constants << const
|
|
|
|
end
|
|
|
|
|
|
|
|
# add a risc instruction after the current (insertion point)
|
|
|
|
# the added instruction will become the new insertion point
|
|
|
|
def add_code( instruction )
|
2018-11-21 10:12:39 +01:00
|
|
|
raise "Not an instruction:#{instruction.to_s}:#{instruction.class.name}" unless instruction.is_a?(Risc::Instruction)
|
2018-07-10 21:02:11 +02:00
|
|
|
raise instruction.to_s if( instruction.class.name.split("::").first == "Arm")
|
|
|
|
new_current = instruction.last #after insertion this point is lost
|
|
|
|
@current.insert(instruction) #insert after current
|
|
|
|
@current = new_current
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2018-07-16 10:46:18 +02:00
|
|
|
# resolve the type of the slot, by inferring from it's name, using the type
|
2018-07-17 09:37:33 +02:00
|
|
|
# scope related slots are resolved by the compiler by method/block
|
2018-07-16 10:46:18 +02:00
|
|
|
def slot_type( slot , type)
|
|
|
|
case slot
|
2018-07-16 11:03:40 +02:00
|
|
|
when :frame
|
|
|
|
new_type = self.frame_type
|
|
|
|
when :arguments
|
|
|
|
new_type = self.arg_type
|
|
|
|
when :receiver
|
|
|
|
new_type = self.receiver_type
|
2018-07-16 10:46:18 +02:00
|
|
|
when Symbol
|
|
|
|
new_type = type.type_for(slot)
|
|
|
|
raise "Not found object #{slot}: in #{type}" unless new_type
|
|
|
|
else
|
|
|
|
raise "Not implemented object #{slot}:#{slot.class}"
|
|
|
|
end
|
|
|
|
#puts "RESOLVE in #{@type.class_name} #{slot}->#{type}"
|
|
|
|
return new_type
|
|
|
|
end
|
|
|
|
|
2018-08-16 09:43:41 +02:00
|
|
|
# return the frame type, ie the blocks frame type
|
|
|
|
def frame_type
|
|
|
|
@callable.frame_type
|
|
|
|
end
|
|
|
|
# return the frame type, ie the blocks arguments type
|
|
|
|
def arg_type
|
|
|
|
@callable.arguments_type
|
|
|
|
end
|
|
|
|
# return the frame type, ie the blocks self_type
|
|
|
|
def receiver_type
|
|
|
|
@callable.self_type
|
|
|
|
end
|
|
|
|
|
2018-07-10 21:02:11 +02:00
|
|
|
def copy( reg , source )
|
|
|
|
copied = use_reg reg.type
|
|
|
|
add_code Register.transfer( source , reg , copied )
|
|
|
|
copied
|
|
|
|
end
|
|
|
|
|
2020-03-08 11:05:25 +01:00
|
|
|
# Load a constant, meaning create a LoadConstant or LoadData instruction for the
|
|
|
|
# given constant. Integers create LoadData (meaning the integer is encoded into
|
|
|
|
# the actual instruction), Parfait::Objects create LoadConstant, where a pointer
|
|
|
|
# to the object is loaded.
|
2020-03-01 09:52:21 +01:00
|
|
|
# add the instruction to the code and return the register_value that was created
|
|
|
|
# for further use
|
2020-03-04 11:39:52 +01:00
|
|
|
# register may be passed in (epecially in mcro building) as second arg
|
|
|
|
def load_object( object , into = nil)
|
2020-03-08 11:05:25 +01:00
|
|
|
if(object.is_a? Integer)
|
|
|
|
ins = Risc.load_data("load data #{object}" , object , into)
|
|
|
|
else
|
|
|
|
ins = Risc.load_constant("load to #{object}" , object , into)
|
|
|
|
end
|
2020-03-01 17:07:42 +01:00
|
|
|
ins.register.set_compiler(self)
|
2020-03-01 09:52:21 +01:00
|
|
|
add_code ins
|
|
|
|
# todo for constants (not objects)
|
2020-03-13 19:18:34 +01:00
|
|
|
add_constant( object) if object.is_a?(Parfait::Object)
|
2020-03-01 09:52:21 +01:00
|
|
|
# add_constant(right) if compiler
|
|
|
|
ins.register
|
2018-07-10 21:02:11 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
# Build with builder (see there), adding the created instructions
|
2018-08-16 09:43:41 +02:00
|
|
|
def build(source , &block)
|
2018-08-19 12:16:07 +02:00
|
|
|
builder(source).build(&block)
|
2018-07-10 21:02:11 +02:00
|
|
|
end
|
|
|
|
|
2018-08-19 12:16:07 +02:00
|
|
|
# return a Builder, that adds the generated code to this compiler
|
|
|
|
def builder( source)
|
|
|
|
Builder.new(self , source)
|
2018-07-10 21:02:11 +02:00
|
|
|
end
|
2019-09-28 14:34:09 +02:00
|
|
|
|
|
|
|
# compile the callable (method or block) to cpu
|
|
|
|
# return an Assembler that will then translate to binary
|
|
|
|
def translate_cpu(translator)
|
|
|
|
risc = @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(@callable , cpu_instructions )
|
|
|
|
end
|
|
|
|
|
2019-09-28 16:24:10 +02:00
|
|
|
# translate this method, which means the method itself and all blocks inside it
|
|
|
|
# returns the array (of assemblers) that you pass in as collection
|
|
|
|
def translate_method( translator , collection)
|
|
|
|
collection << translate_cpu( translator )
|
|
|
|
collection
|
|
|
|
end
|
|
|
|
|
2018-07-10 21:02:11 +02:00
|
|
|
end
|
|
|
|
end
|
2020-02-27 10:57:18 +01:00
|
|
|
require_relative "allocator"
|