rubyx/lib/arm/translator.rb
Torsten Ruger a8453c126d use arm shift at runtime
arm indexes are in bytes (x4) at compile time
but at runtime we only have the array indexes, iw word indexes
arm has the nice barrel shifter to save us an extra instruction
2015-11-19 12:48:13 +02:00

167 lines
5.0 KiB
Ruby

module Arm
class Translator
# translator should translate from register instructio set to it's own (arm eg)
# for each instruction we call the translator with translate_XXX
# with XXX being the class name.
# the result is replaced in the stream
def translate instruction
class_name = instruction.class.name.split("::").last
self.send( "translate_#{class_name}".to_sym , instruction)
end
# don't replace labels
def translate_Label code
nil
end
# arm indexes are
# in bytes, so *4
# if an instruction is passed in we get the index with index function
def arm_index index
index = index.index if index.is_a?(Register::Instruction)
raise "index error 0" if index == 0
index * 4
end
# 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 vm defines a register for the location, so we store it there.
def translate_SaveReturn code
ArmMachine.str( :lr , code.register , arm_index(code) )
end
def translate_RegisterTransfer code
# Register machine convention is from => to
# But arm has the receiver/result as the first
ArmMachine.mov( code.to , code.from)
end
def translate_GetSlot code
if(code.index.is_a? Numeric)
ArmMachine.ldr( code.register , code.array , arm_index(code) )
else
ArmMachine.ldr( code.register , code.array , code.index , :shift_lsl => 2 )
end
end
def translate_SetSlot code
if(code.index.is_a? Numeric)
ArmMachine.str( code.register , code.array , arm_index(code) )
else
ArmMachine.str( code.register , code.array , code.index , :shift_lsl => 2 )
end
end
def translate_GetByte code
if(code.index.is_a? Numeric)
ArmMachine.ldrb( code.register , code.array , arm_index(code) )
else
ArmMachine.ldrb( code.register , code.array , code.index )
end
end
def translate_SetByte code
if(code.index.is_a? Numeric)
ArmMachine.strb( code.register , code.array , arm_index(code) )
else
ArmMachine.strb( code.register , code.array , code.index )
end
end
def translate_FunctionCall code
ArmMachine.b( code.method.instructions )
end
def translate_FunctionReturn code
ArmMachine.ldr( :pc , code.register , arm_index(code) )
end
def translate_LoadConstant code
constant = code.constant
if constant.is_a?(Parfait::Object) or constant.is_a?(Symbol) or constant.is_a?(Register::Label)
return ArmMachine.add( code.register , constant )
else
return ArmMachine.mov( code.register , constant )
end
end
def translate_OperatorInstruction code
left = code.left
right = code.right
case code.operator.to_s
when "+"
c = ArmMachine.add(left , left , right)
when "-"
c = ArmMachine.sub(left , left , right)
when "&"
c = ArmMachine.and(left , left , right)
when "|"
c = ArmMachine.orr(left , left , right)
when "*"
c = ArmMachine.mul(left , right , left) #arm rule about left not being result, lukily commutative
when ">>"
c = ArmMachine.mov(left , left , :shift_asr => right) #arm rule about left not being result, lukily commutative
when "<<"
c = ArmMachine.mov(left , left , :shift_lsl => right) #arm rule about left not being result, lukily commutative
else
raise "unimplemented '#{code.operator}' #{code}"
end
c
end
# This implements branch logic, which is simply assembler branch
#
# The only target for a call is a Block, so we just need to get the address for the code
# and branch to it.
def translate_Branch code
ArmMachine.b( code.label )
end
def translate_IsPlus code
ArmMachine.bpl( code.label)
end
def translate_IsMinus code
ArmMachine.bmi( code.label)
end
def translate_IsZero code
ArmMachine.beq( code.label)
end
def translate_IsOverflow code
ArmMachine.bvs( code.label)
end
def translate_Syscall code
call_codes = { :putstring => 4 , :exit => 1 }
int_code = call_codes[code.name]
raise "Not implemented syscall, #{code.name}" unless int_code
send( code.name , int_code )
end
def putstring int_code
codes = ArmMachine.add( :r1 , :r1 , 12 ) # adjust for object header
codes.append ArmMachine.mov( :r0 , 1 ) # write to stdout == 1
syscall(int_code , codes )
end
def exit int_code
codes = ArmMachine.ldr( :r0 , :r0 , arm_index(Register.resolve_index(:Message , :return_value)) )
syscall int_code , codes
end
private
# syscall is always triggered by swi(0)
# The actual code (ie the index of the kernel function) is in r7
def syscall int_code , codes
codes.append ArmMachine.mov( :r7 , int_code )
codes.append ArmMachine.swi( 0 )
codes
end
end
end