1 require 'voodoo/generators/common_code_generator'
4 # = ARM GNU Assembler Code Generator
6 # The ARM code generator generates assembly code for use with
9 # == Calling Convention
11 # The first four arguments are passed in the registers r0 through r3.
12 # Any additional arguments are passed on the stack, starting at
13 # r13. r13 will always be a multiple of 8.
15 # The return address for the called function is passed in r14.
17 # The called function will store its return value in r0.
19 # The called function is required to preserve the values of registers
20 # r4 through r11 and register r13.
22 # This calling convention is compatible with the Procedure Call
23 # Standard for the ARM Architecture (AAPCS).
27 # Call frames have the following layout:
29 # When a function is called, it receives a stack frame that looks like
37 # arg4 <-- r13 points here
39 # The function prologue of functions generated by this code generator
40 # creates activiation frames that look as follows:
51 # saved r4 <-- r13 points here
55 # Inside a function, registers r4..r8, r10, and r11 are used for
56 # local variables and function arguments.
58 # r12 is used as a temporary, and r3 is used when another temporary
61 class ARMGasGenerator < CommonCodeGenerator
65 @DATA_ALIGNMENT = @WORDSIZE
66 @FUNCTION_ALIGNMENT = @WORDSIZE
75 @function_end_label = nil
80 @output_file_suffix = '.s'
83 # Create an entry in the constants table,
84 # returning the label that will refer to the constant.
85 # The value may be an integer or a label.
86 def add_constant value
88 @constants << [label, value]
92 def align alignment = nil
94 # Get default alignment
97 alignment = @CODE_ALIGNMENT
99 alignment = @DATA_ALIGNMENT
101 alignment = @FUNCTION_ALIGNMENT
103 # Use data alignment as default
104 alignment = @DATA_ALIGNMENT
107 emit ".align #{alignment}\n" unless alignment == 0
110 # Returns an sp-relative reference for the nth (0-based) argument.
112 "[sp, \##{@frame_size + (n - @NREGISTER_ARGS) * @WORDSIZE}]"
115 # Return the register in which the nth (0-based) argument is stored, or
116 # nil if not stored in a register
118 # The first @NREGISTER_ARGS arguments are in the v registers,
119 # the rest are on the stack.
127 # Test if op is a binary operation
128 def assymetric_binop? op
129 [:asr, :bsr, :div, :mod, :rol, :ror, :shl, :shr, :sub].member?(op)
132 # Test if a value is an at-expression
134 value.respond_to?(:[]) && value[0] == :'@'
137 # Emit function prologue and declare _formals_ as function arguments
138 def begin_function formals, nlocals
139 if @environment != @top_level
140 raise "Can only begin a function at top level"
143 @function_end_label = gensym
144 emit "# function #{formals.join ' '}\n"
145 environment = Environment.new @environment
146 environment.add_args formals
147 @environment = environment
148 emit_function_prologue formals, nlocals
151 # Test if op is a binary operation
153 assymetric_binop?(op) || symmetric_binop?(op)
156 # Define a byte with the given value
158 emit ".byte #{value}\n"
163 emit "# call #{func} #{args.join ' '}\n"
165 # Calculate how many arguments need to be pushed on
166 # the stack, and allocate space for them.
167 nstack_args = number_of_stack_arguments args.length
168 old_frame_offset = @frame_offset
169 old_frame_size = @frame_size
170 grow_frame nstack_args if nstack_args > 0
171 $stderr.puts "@frame_size was #{old_frame_size}, is now #{@frame_size}"
173 # Put stack arguments on the stack
174 (@NREGISTER_ARGS...args.length).each do |n|
175 load_value_into_register args[n], @TEMPORARY
176 emit "str #{@TEMPORARY}, " +
177 "[sp , \##{(n - @NREGISTER_ARGS) * @WORDSIZE}]\n"
180 # Put register arguments in the right registers
181 nregister_args = number_of_register_arguments args.length
182 nregister_args.times do |n|
183 load_value_into_register args[n], :"a#{n + 1}"
189 # Restore original stack frame
190 if old_frame_size != @frame_size
191 emit "add sp, sp, \##{@frame_size - old_frame_size}\n"
192 @frame_offset = old_frame_offset
193 @frame_size = old_frame_size
197 # Start a conditional using the specified branch instruction
198 # after the comparison.
199 def common_if comp, x, y = nil
200 emit "# #{comp} #{x} #{y}\n"
202 xreg = load_value x, @TEMPORARY
203 yreg = load_value y, :a4
205 falselabel = @environment.gensym
206 @if_labels.push falselabel
208 emit "cmp #{xreg}, #{yreg}\n"
210 lut = { :ifeq => "bne", :ifge => "blt", :ifgt => "ble",
211 :ifle => "bgt", :iflt => "bge", :ifne => "beq" }
212 emit "#{lut[comp]} #{falselabel}\n"
215 # Counts the number of local variables created in
216 # a sequence of statements.
217 def count_locals statements
219 each_statement(statements) do |statement|
220 if statement[0] == :let
221 # let introduces a single local
228 # Emit function prologue.
229 def emit_function_prologue formals = [], nlocals = 0
230 # For the calculations here, treat the first @NREGISTER_ARGS
231 # function arguments as local variables.
232 nregister_args = [formals.length, @NREGISTER_ARGS].min
233 nlocals = nlocals + nregister_args
235 nregister_locals = [nlocals, @NREGISTER_LOCALS].min
236 nstack_locals = (nlocals > nregister_locals ?
237 nlocals - nregister_locals : 0)
239 # Save the registers we will clobber to the stack.
241 nregister_locals.times do |i|
242 clobbered << :"v#{i < 5 ? i + 1 : i + 2}"
244 @saved_registers = clobbered
245 clobbered = clobbered + [:lr]
246 emit "stmfd sp!, {#{clobbered.join ', '}}\n"
248 # Move arguments that were passed in registers into
249 # callee-save registers.
250 nregister_args.times do |i|
251 emit "cpy v#{i + 1}, a#{i + 1}\n"
254 # Calculate frame size so that the stack pointer will
255 # be properly aligned at the end of emit_function_prologue.
256 @frame_size = (clobbered.length + nstack_locals) * @WORDSIZE
257 if @frame_size % 8 != 0
258 @frame_size = (@frame_size + 7) / 8 * 8
260 extra_space = @frame_size - clobbered.length * @WORDSIZE
262 emit "sub sp, sp, \##{extra_space}\n"
267 # End a function body
269 if @environment == @top_level
270 raise "Cannot end function when not in a function"
273 emit "# function epilogue\n"
274 label @function_end_label
276 # Set sp back to where saved registers were stored
277 saved = @saved_registers + [:pc]
278 offset = @frame_size - saved.length * @WORDSIZE
280 emit "add sp, sp, \##{offset}\n"
283 # Restore saved registers and return
284 emit "ldmfd sp!, {#{saved.join ', '}}\n"
289 @constants.each do |x|
295 emit "# end function\n\n"
297 @environment = @top_level
298 @saved_registers = []
301 # Ends a conditional.
306 # Evaluate the binary operation expr and store the result in register
307 def eval_binop expr, register
308 x = load_value expr[1], :a4
309 y = load_value expr[2], @TEMPORARY
313 emit "lsr #{register}, #{x}, #{y}\n"
315 raise "TODO: Not implemented yet"
317 raise "TODO: Not implemented yet"
319 emit "orr #{register}, #{x}, #{y}\n"
321 emit "lsl #{register}, #{x}, #{y}\n"
323 emit "lsr #{register}, #{x}, #{y}\n"
325 emit "eor #{register}, #{x}, #{y}\n"
327 emit "#{expr[0]} #{register}, #{x}, #{y}\n"
331 # Evaluates the expression +expr+ and stores the result in +register+.
332 def eval_expr expr, register
335 load_value_into_register expr[0], register
337 # Evaluate expression
342 emit "cpy #{register}, #{@RETURN}\n" if register != @RETURN
344 get_byte expr[1], expr[2], register
346 get_word expr[1], expr[2], register
348 load_value_into_register expr[1], register
349 emit "mvn #{@TEMPORARY}, #0\n"
350 emit "eor #{register}, #{register}, #{@TEMPORARY}\n"
353 eval_binop expr, register
355 raise "Not a magic word: #{op}"
361 # Export symbols from the current section
363 symbols.each { |sym| emit ".globl #{sym}\n" }
366 # Add a function to the current section
367 def function formals, *code
368 nlocals = count_locals code
369 begin_function formals, nlocals
370 code.each { |action| add section, action }
374 # Load byte from _base_ + _offset_ into _register_
375 def get_byte base, offset, register
376 # If base is an integer, but offset isn't, swap them
377 if !integer?(offset) && integer?(base)
378 base, offset = [offset, base]
382 base_reg = load_value base
384 emit "ldrb #{register}, [#{base_reg}]\n"
386 emit "ldrb #{register}, [#{base_reg}, \##{offset}]\n"
389 base_reg = load_value base
390 offset_reg = load_value offset, :a4
391 emit "ldrb #{register}, [#{base_reg}, #{offset_reg}]\n"
395 # Load word from _base_ + _offset_ * _@WORDSIZE_ into _register_
396 def get_word base, offset, register
398 base_reg = load_value base
400 emit "ldr #{register}, [#{base_reg}]\n"
402 emit "ldr #{register}, [#{base_reg}, \##{offset * @WORDSIZE}]\n"
405 base_reg = load_value base
406 offset_reg = load_value offset, :a4
407 emit "ldr #{register}, [#{base_reg}, #{offset_reg}, LSL #2]\n"
411 # Test if a symbol refers to a global
413 symbol?(symbol) && @environment[symbol] == nil
421 # Grows the current frame by n words, plus padding to
422 # respect alignment rules.
423 def grow_frame nwords
424 increment = (nwords * @WORDSIZE + 7) / 8 * 8
425 emit "sub sp, sp, \##{increment}\n"
426 @frame_size = @frame_size + increment
427 @frame_offset = @frame_offset + increment
430 # Start the false path of a conditional.
433 newlabel = @environment.gensym
437 @if_labels.push newlabel
440 # Test if x is equal to y
442 common_if :ifeq, x, y
445 # Test if x is greater than or equal to y
447 common_if :ifge, x, y
450 # Test if x is strictly greater than y
452 common_if :ifgt, x, y
455 # Test if x is less than or equal to y
457 common_if :ifle, x, y
460 # Test if x is strictly less than y
462 common_if :iflt, x, y
465 # Test if x different from y
467 common_if :ifne, x, y
470 # Import labels into the current section
472 # Record imported labels in @imports
473 symbols.each { |sym| @imports[sym] = sym }
476 # Test if a value is an integer
478 value.kind_of? Integer
486 # Introduce a new local variable
487 def let symbol, *expr
488 emit "# let #{symbol} #{expr.join ' '}\n"
489 n = @environment.locals
490 @environment.add_local symbol
492 register = local_register n
494 # We will use a register to store the value
495 eval_expr expr, register
497 # We will use the stack to store the value
498 ref = local_reference n
499 eval_expr expr, @TEMPORARY
500 emit "str #{@TEMPORARY}, #{ref}\n"
504 # Load the value at the given address.
505 def load_at address, register = @TEMPORARY
506 load_value_into_register address, register
507 emit "ldr #{register}, [#{register}]\n"
511 # Load a value into a register.
512 # Returns the name of the register.
513 # If the value was already in a register, the name of that
514 # register is returned.
515 # Else, the value is loaded into a register and the name of
516 # that register is returned. The register to use in that case
517 # may be specified using the optional second argument.
518 def load_value x, register = @TEMPORARY
520 if x >= 0 && x <= 255
521 emit "mov #{register}, \##{x}\n"
523 elsif x >= -255 && x < 0
524 emit "mvn #{register}, \##{-(x + 1)}\n"
528 emit "ldr #{register}, #{lbl}\n"
532 binding = @environment[x]
538 return arg_register(n)
540 emit "ldr #{register}, #{arg_reference binding[1]}\n"
546 return local_register(n)
548 emit "ldr #{register}, #{local_reference n}\n"
552 raise "Don't know how to load #{x.inspect}"
556 label = add_constant x
557 emit "ldr #{register}, #{label}\n"
561 load_at x[1], register
563 raise "Don't know how to load #{x.inspect}"
567 # Load a value into a specific register
568 def load_value_into_register x, register
569 reg = load_value x, register
571 emit "cpy #{register}, #{reg}\n"
575 # Returns an sp-relative reference for the nth (0-based) local.
576 def local_reference n
577 "[sp, \##{@frame_offset + (number_of_register_arguments + n) * @WORDSIZE}]"
580 # Return the register in which the nth local (0-based) is stored, or
581 # nil if not stored in a register
584 n = n + number_of_register_arguments
595 # Calculate the number of register arguments,
596 # given the total number of arguments.
597 def number_of_register_arguments n = @environment.args
598 [n, @NREGISTER_ARGS].min
601 # Calculate the number of stack arguments,
602 # given the total number of arguments.
603 def number_of_stack_arguments n = @environment.args
604 [0, n - @NREGISTER_ARGS].max
607 # Returns true if the nth (0-based) argument is stored in a register
612 # Returns true if the nth (0-based) local is stored in a register
613 def register_local? n
614 (n + number_of_register_arguments) < @NREGISTER_LOCALS
617 # Returns from a function.
619 # _words_ may contain an expression to be evaluated. The result
620 # of the evaluation is returned from the function.
622 emit "# return #{words.join ' '}\n"
623 # Compute return value and store it in @RETURN
624 eval_expr(words, @RETURN) unless words.empty?
626 goto @function_end_label
629 # Set a variable to the result of evaluating an expression
630 def set symbol, *expr
631 emit "# set #{symbol} #{expr.join ' '}\n"
633 x = @environment[symbol]
635 raise "Cannot change value of constant #{symbol}"
641 register = arg_register x[1]
643 register = local_register x[1]
648 eval_expr expr, register
652 ref = local_reference x[1]
654 ref = arg_reference x[1]
656 raise "??? #{sym} is neither a local nor an argument"
658 eval_expr expr, @TEMPORARY
659 emit "str #{@TEMPORARY}, #{ref}\n"
663 # Set the byte at _base_ + _offset_ to _value_
664 def set_byte base, offset, value
665 emit "# set-byte #{base} #{offset} #{value}\n"
666 # If base is an integer, but offset isn't, swap them
667 if !integer?(offset) && integer?(base)
668 base, offset = [offset, base]
672 base_reg = load_value base, :a4
673 load_value_into_register value, @TEMPORARY
675 emit "strb #{@TEMPORARY}, [#{base_reg}]\n"
677 emit "strb #{@TEMPORARY}, [#{base_reg}, \##{offset}]\n"
680 eval_binop [:add, base, offset], :a4
681 load_value_into_register value, @TEMPORARY
682 emit "strb #{@TEMPORARY}, [a4]\n"
686 # Set the word at _base_ + _offset_ * +@WORDSIZE+ to _value_
687 def set_word base, offset, value
688 emit "# set-word #{base} #{offset} #{value}\n"
689 # If base is an integer, but offset isn't, swap them
690 if !integer?(offset) && integer?(base)
691 base, offset = [offset, base]
695 base_reg = load_value base, :a4
696 load_value_into_register value, @TEMPORARY
698 emit "str #{@TEMPORARY}, [#{base_reg}]\n"
700 emit "str #{@TEMPORARY}, [#{base_reg}, \##{offset * @WORDSIZE}]\n"
703 load_value_into_register base, :a4
704 load_value_into_register offset, @TEMPORARY
705 emit "add a4, a4, #{@TEMPORARY}, LSL #2\n"
706 load_value_into_register value, @TEMPORARY
707 emit "str #{@TEMPORARY}, [a4]\n"
711 # Define a string with the given value
714 value.each_byte do |b|
717 elsif b >= 32 && b < 127 && b != 34
720 code << sprintf("\\%03o", b)
723 emit ".ascii \"#{code}\"\n"
726 # Test if a value is a symbol
728 value.kind_of? Symbol
731 # Test if op is a symmetric binary operation (i.e. it will yield the
732 # same result if the order of its source operands is changed).
733 def symmetric_binop? op
734 [:add, :and, :mul, :or, :xor].member? op
737 # Call a function, re-using the current call frame if possible.
738 def tail_call func, *args
739 emit "# tail-call #{func} #{args.join ' '}\n"
741 # TODO: Implement proper tail calls
742 ret :call, func, *args
745 # Define a word with the given value
747 emit ".int #{value}\n"
750 # Write generated code to the given IO object.
752 @sections.each do |section,code|
754 io.puts ".section #{section.to_s}"
763 # Register class for little endian ARM
764 Voodoo::CodeGenerator.register_generator ARMGasGenerator,
765 :architecture => :arm,