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 activation frames that look as follows:
47 # arg4 <-- r11 points here
51 # saved r4 <-- r13 points here
55 # Inside a function, registers r4..r8 and r10 are used for local variables
56 # and function arguments, whereas r11 is used as a frame pointer.
58 # r12 is used as a temporary, and r3 is used when another temporary
61 class ARMGasGenerator < CommonCodeGenerator
64 @WORDSIZE = 1 << @WORDSIZE_BITS
66 @DATA_ALIGNMENT = @WORDSIZE
67 @FUNCTION_ALIGNMENT = @WORDSIZE
68 @STACK_ALIGNMENT_BITS = 3
69 @STACK_ALIGNMENT = 1 << @STACK_ALIGNMENT_BITS
71 @INITIAL_FRAME_SIZE = 2 * @WORDSIZE
80 @function_end_label = nil
85 @output_file_suffix = '.s'
87 :'bits-per-word' => '32',
88 :'byte-order' => 'little-endian',
89 :'bytes-per-word' => 4
92 # Create an entry in the constants table,
93 # returning the label that will refer to the constant.
94 # The value may be an integer or a label.
95 def add_constant value
97 @constants << [label, value]
101 def align alignment = nil
103 # Get default alignment
106 alignment = @CODE_ALIGNMENT
108 alignment = @DATA_ALIGNMENT
110 alignment = @FUNCTION_ALIGNMENT
112 # Use data alignment as default
113 alignment = @DATA_ALIGNMENT
116 emit ".align #{alignment}\n" unless alignment == 0
119 # Returns the fp-relative offset for the nth (0-based) argument.
121 (n - @NREGISTER_ARGS) * @WORDSIZE
124 # Returns an fp-relative reference for the nth (0-based) argument.
126 offset_reference arg_offset(n)
129 # Return the register in which the nth (0-based) argument is stored, or
130 # nil if not stored in a register
132 # The first @NREGISTER_ARGS arguments are in the v registers,
133 # the rest are on the stack.
141 # Test if op is a binary operation
142 def assymetric_binop? op
143 [:asr, :bsr, :div, :mod, :rol, :ror, :shl, :shr, :sub].member?(op)
146 # Test if a value is an at-expression
148 value.respond_to?(:[]) && value[0] == :'@'
151 def auto_bytes value, register
152 if value.kind_of? Integer
155 temporary = register == :sp ? @TEMPORARY : register
156 load_value_into_register value, temporary
157 auto_bytes_register temporary
159 emit "cpy #{register}, sp\n" unless register == :sp
162 # auto-bytes where the value is supplied in a register and the return
163 # value will be in sp. register must not be sp.
164 def auto_bytes_register register
165 temporary = register == @TEMPORARY ? :r3 : @TEMPORARY
166 emit "add #{register}, #{register}, \##{@STACK_ALIGNMENT - 1}\n"
167 emit "mvn #{temporary}, \##{@STACK_ALIGNMENT - 1}\n"
168 emit "and #{register}, #{register}, #{temporary}\n"
169 emit "sub sp, #{register}\n"
172 def auto_words value, register
173 if value.kind_of? Integer
174 auto_bytes(value * @WORDSIZE, register)
176 temporary = register == :sp ? @TEMPORARY : register
177 load_value_into_register value, temporary
178 emit "lsl #{temporary}, #{temporary}, \##{@WORDSIZE_BITS}\n"
179 auto_bytes_register temporary
180 emit "cpy #{register}, sp\n" unless register == :sp
184 # Begins a new block.
185 def begin_block *code
186 emit "# begin block\n"
187 # If we are starting a block at top level, create a frame
188 if @environment == @top_level
189 nlocals = count_locals code
190 create_frame nlocals, false
192 @environment = Environment.new @environment
195 # Emit function prologue and declare _formals_ as function arguments
196 def begin_function formals, nlocals
197 if @environment != @top_level
198 raise "Can only begin a function at top level"
201 @function_end_label = gensym
202 emit "# function #{formals.join ' '}\n"
203 environment = Environment.new @environment
204 formals.each_with_index do |formal, i|
205 if i < @NREGISTER_ARGS
206 environment.add_arg formal, arg_register(i)
208 environment.add_arg formal, arg_offset(i)
211 @environment = environment
212 emit_function_prologue formals, nlocals
215 # Test if op is a binary operation
217 assymetric_binop?(op) || symmetric_binop?(op)
220 # Define a byte with the given value
222 emit ".byte #{value}\n"
227 emit "# call #{func} #{args.join ' '}\n"
229 # Calculate how many arguments need to be pushed on
230 # the stack, and allocate space for them.
231 nstack_args = number_of_stack_arguments args.length
232 old_frame_offset = @frame_offset
233 old_frame_size = @frame_size
234 grow_frame nstack_args if nstack_args > 0
236 # Put stack arguments on the stack
237 (@NREGISTER_ARGS...args.length).each do |n|
238 load_value_into_register args[n], @TEMPORARY
239 emit "str #{@TEMPORARY}, " +
240 "[sp , \##{(n - @NREGISTER_ARGS) * @WORDSIZE}]\n"
243 # Put register arguments in the right registers
244 nregister_args = number_of_register_arguments args.length
245 nregister_args.times do |n|
246 load_value_into_register args[n], :"a#{n + 1}"
253 func_reg = load_value func
254 emit "blx #{func_reg}\n"
257 # Restore original stack frame
258 if old_frame_size != @frame_size
259 emit "add sp, sp, \##{@frame_size - old_frame_size}\n"
260 @frame_offset = old_frame_offset
261 @frame_size = old_frame_size
265 # Creates a stack frame for the given number of arguments
266 # and local variables.
267 def create_frame nvars, save_lr = true
268 # Calculate how many variables we will store in registers,
269 # and how many on the stack.
270 nregister_vars = [nvars, @NREGISTER_LOCALS].min
271 nstack_vars = nvars - nregister_vars
273 # Save the registers we will clobber to the stack.
275 nregister_vars.times do |i|
276 clobbered << :"v#{i < 5 ? i + 1 : i + 2}"
279 clobbered << :lr if save_lr
280 @saved_registers = clobbered
281 emit "stmfd sp!, {#{clobbered.join ', '}}\n"
282 emit "add #{@FP}, sp, \##{clobbered.length * @WORDSIZE}\n"
284 # Calculate frame size so that the stack pointer will
285 # be properly aligned at the end of emit_function_prologue.
286 @frame_size = stack_align((clobbered.length + nstack_vars) * @WORDSIZE)
287 extra_space = @frame_size - clobbered.length * @WORDSIZE
289 emit "sub sp, sp, \##{extra_space}\n"
294 # Start a conditional using the specified branch instruction
295 # after the comparison.
296 def common_if comp, x, y = nil
297 emit "# #{comp} #{x} #{y}\n"
299 xreg = load_value x, @TEMPORARY
300 yreg = load_value y, :a4
302 falselabel = @environment.gensym
303 @if_labels.push falselabel
305 emit "cmp #{xreg}, #{yreg}\n"
307 lut = { :ifeq => "bne", :ifge => "blt", :ifgt => "ble",
308 :ifle => "bgt", :iflt => "bge", :ifne => "beq" }
309 emit "#{lut[comp]} #{falselabel}\n"
312 # Counts the number of local variables created in
313 # a sequence of statements.
314 def count_locals statements
316 each_statement(statements) do |statement|
317 if statement[0] == :let
318 # let introduces a single local
325 # Destroys the current stack frame.
326 # If ret is true, loads the saved value of lr into pc.
327 def destroy_frame ret = false
328 # Set sp back to where saved registers were stored
329 saved = @saved_registers
330 emit "sub sp, #{@FP}, \##{saved.length * @WORDSIZE}\n"
333 index = saved.index :lr
337 raise "Request to load saved lr into pc, but lr has not been saved"
340 emit "ldmfd sp!, {#{saved.join ', '}}\n"
342 emit_constants if ret
345 # Writes any constants that need to be written to the instruction
346 # stream, and clears the list of constants that need to be written.
348 @constants.each do |x|
355 # Emit function prologue.
356 def emit_function_prologue formals = [], nlocals = 0
357 # Calculate the number of arguments we were passed in
358 # registers, the total number of values we need to save
359 # on the stack, then create a stack frame and save
360 # the v registers we will be using.
361 nregister_args = [formals.length, @NREGISTER_ARGS].min
362 nvars = nregister_args + nlocals
363 create_frame nvars, true
365 # Move arguments that were passed in registers into
366 # callee-save registers.
367 nregister_args.times do |i|
368 emit "cpy v#{i + 1}, a#{i + 1}\n"
372 # Ends the current block.
376 # If we are returning to top level, restore stack pointer
377 # and saved registers.
378 if @environment.parent == @top_level
379 offset = @frame_size - @saved_registers.length * @WORDSIZE
381 emit "add sp, sp, \##{offset}\n"
383 emit "ldmfd sp!, {#{@saved_registers.join ', '}}\n"
386 @saved_registers = []
388 # If we need to emit constants, do so now
389 unless @constants.empty?
396 # Restore old value of @environment
397 @environment = @environment.parent
400 # Ends a function body.
402 if @environment == @top_level
403 raise "Cannot end function when not in a function"
406 emit "# function epilogue\n"
407 label @function_end_label
412 @saved_registers = []
413 emit "# end function\n\n"
414 @environment = @top_level
417 # Ends a conditional.
422 # Evaluate the binary operation expr and store the result in register
423 def eval_binop expr, register
426 # Emulation for div and mod, which ARM does not have instructions for
429 func = :"__aeabi_idiv"
430 import func unless @imports.has_key? func
431 call func, expr[1], expr[2]
432 emit "cpy #{register}, r0\n" if register != :r0
435 func = :"__aeabi_idivmod"
436 import func unless @imports.has_key? func
437 call func, expr[1], expr[2]
438 emit "cpy #{register}, r1\n" if register != :r1
442 x = load_value expr[1], :a4
443 y = load_value expr[2], @TEMPORARY
447 emit "lsr #{register}, #{x}, #{y}\n"
449 emit "orr #{register}, #{x}, #{y}\n"
451 emit "rsb #{y}, #{y}, #32\n"
452 emit "ror #{register}, #{x}, #{y}\n"
454 emit "lsl #{register}, #{x}, #{y}\n"
456 emit "lsr #{register}, #{x}, #{y}\n"
458 emit "eor #{register}, #{x}, #{y}\n"
460 emit "#{expr[0]} #{register}, #{x}, #{y}\n"
464 # Evaluates the expression +expr+ and stores the result in +register+.
465 def eval_expr expr, register
468 load_value_into_register expr[0], register
470 # Evaluate expression
474 auto_bytes expr[1], register
476 auto_words expr[1], register
479 emit "cpy #{register}, #{@RETURN}\n" if register != @RETURN
481 get_byte expr[1], expr[2], register
483 get_word expr[1], expr[2], register
485 load_value_into_register expr[1], register
486 emit "mvn #{@TEMPORARY}, #0\n"
487 emit "eor #{register}, #{register}, #{@TEMPORARY}\n"
490 eval_binop expr, register
492 raise "Not a magic word: #{op}"
498 # Export symbols from the current section
500 symbols.each { |sym| emit ".globl #{sym}\n" }
503 # Add a function to the current section
504 def function formals, *code
505 nlocals = count_locals code
506 begin_function formals, nlocals
507 code.each { |action| add section, action }
511 # Load byte from _base_ + _offset_ into _register_
512 def get_byte base, offset, register
513 # If base is an integer, but offset isn't, swap them
514 if !integer?(offset) && integer?(base)
515 base, offset = [offset, base]
519 base_reg = load_value base
521 emit "ldrb #{register}, [#{base_reg}]\n"
523 emit "ldrb #{register}, [#{base_reg}, \##{offset}]\n"
526 base_reg = load_value base
527 offset_reg = load_value offset, :a4
528 emit "ldrb #{register}, [#{base_reg}, #{offset_reg}]\n"
532 # Load word from _base_ + _offset_ * _@WORDSIZE_ into _register_
533 def get_word base, offset, register
535 base_reg = load_value base
537 emit "ldr #{register}, [#{base_reg}]\n"
539 emit "ldr #{register}, [#{base_reg}, \##{offset * @WORDSIZE}]\n"
542 base_reg = load_value base
543 offset_reg = load_value offset, :a4
544 emit "ldr #{register}, [#{base_reg}, #{offset_reg}, LSL #2]\n"
548 # Test if a symbol refers to a global
550 symbol?(symbol) && @environment[symbol] == nil
557 # If we have constants that need to be emitted, do so now
561 # Grows the current frame by n words, plus padding to
562 # respect alignment rules.
563 def grow_frame nwords
564 increment = stack_align(nwords * @WORDSIZE)
565 emit "sub sp, sp, \##{increment}\n"
566 @frame_size = @frame_size + increment
567 @frame_offset = @frame_offset + increment
570 # Start the false path of a conditional.
573 newlabel = @environment.gensym
577 @if_labels.push newlabel
580 # Test if x is equal to y
582 common_if :ifeq, x, y
585 # Test if x is greater than or equal to y
587 common_if :ifge, x, y
590 # Test if x is strictly greater than y
592 common_if :ifgt, x, y
595 # Test if x is less than or equal to y
597 common_if :ifle, x, y
600 # Test if x is strictly less than y
602 common_if :iflt, x, y
605 # Test if x different from y
607 common_if :ifne, x, y
610 # Import labels into the current section
612 # Record imported labels in @imports
613 symbols.each { |sym| @imports[sym] = sym }
616 # Test if a value is an integer
618 value.kind_of? Integer
626 # Introduce a new local variable
627 def let symbol, *expr
628 n = @environment.locals
629 register = local_register n
632 # We will use a register to store the value
633 @environment.add_local symbol, register
634 eval_expr expr, register
636 # We will use the stack to store the value
637 offset = local_offset n
638 @environment.add_local symbol, offset
639 eval_expr expr, @TEMPORARY
640 emit "str #{@TEMPORARY}, #{offset_reference offset}\n"
644 # Load the value at the given address.
645 def load_at address, register = @TEMPORARY
646 load_value_into_register address, register
647 emit "ldr #{register}, [#{register}]\n"
651 # Load a value into a register.
652 # Returns the name of the register.
653 # If the value was already in a register, the name of that
654 # register is returned.
655 # Else, the value is loaded into a register and the name of
656 # that register is returned. The register to use in that case
657 # may be specified using the optional second argument.
658 def load_value x, register = @TEMPORARY
660 if x >= 0 && x <= 255
661 emit "mov #{register}, \##{x}\n"
663 elsif x >= -255 && x < 0
664 emit "mvn #{register}, \##{-(x + 1)}\n"
668 emit "ldr #{register}, #{lbl}\n"
672 binding = @environment[x]
673 if binding.kind_of? String
674 # Value is already in a register. Return register name.
676 elsif binding.kind_of? Integer
677 # Value is on the stack. Load from the stack.
678 emit "ldr #{register}, #{offset_reference binding}\n"
683 emit "ldr #{register}, #{lbl}\n"
687 load_at x[1], register
689 raise "Don't know how to load #{x.inspect}"
693 # Load a value into a specific register
694 def load_value_into_register x, register
695 reg = load_value x, register
697 emit "cpy #{register}, #{reg}\n"
701 # Returns the fp-relative reference for the nth (0-based) local.
703 -@INITIAL_FRAME_SIZE - (n * @WORDSIZE)
706 # Return the register in which the nth local (0-based) is stored, or
707 # nil if not stored in a register
710 n = n + number_of_register_arguments
721 # Calculate the number of register arguments,
722 # given the total number of arguments.
723 def number_of_register_arguments n = @environment.args
724 [n, @NREGISTER_ARGS].min
727 # Calculate the number of stack arguments,
728 # given the total number of arguments.
729 def number_of_stack_arguments n = @environment.args
730 [0, n - @NREGISTER_ARGS].max
733 # Given an offset, returns an fp-relative reference.
734 def offset_reference offset
735 "[#{@FP}, \##{offset}]"
738 # Returns true if the nth (0-based) argument is stored in a register
743 # Returns true if the nth (0-based) local is stored in a register
744 def register_local? n
745 (n + number_of_register_arguments) < @NREGISTER_LOCALS
748 # Returns from a function.
750 # _words_ may contain an expression to be evaluated. The result
751 # of the evaluation is returned from the function.
753 emit "# return #{words.join ' '}\n"
754 # Compute return value and store it in @RETURN
755 eval_expr(words, @RETURN) unless words.empty?
757 goto @function_end_label
760 # Set a variable to the result of evaluating an expression
761 def set symbol, *expr
764 register = load_value symbol[1]
765 emit "str r3, [#{register}]\n"
767 x = @environment[symbol]
769 raise "Cannot change value of constant #{symbol}"
770 elsif x.kind_of? String
773 eval_expr expr, @TEMPORARY
774 emit "str #{@TEMPORARY}, #{offset_reference x}\n"
779 # Set the byte at _base_ + _offset_ to _value_
780 def set_byte base, offset, value
781 emit "# set-byte #{base} #{offset} #{value}\n"
782 # If base is an integer, but offset isn't, swap them
783 if !integer?(offset) && integer?(base)
784 base, offset = [offset, base]
788 base_reg = load_value base, :a4
789 load_value_into_register value, @TEMPORARY
791 emit "strb #{@TEMPORARY}, [#{base_reg}]\n"
793 emit "strb #{@TEMPORARY}, [#{base_reg}, \##{offset}]\n"
796 eval_binop [:add, base, offset], :a4
797 load_value_into_register value, @TEMPORARY
798 emit "strb #{@TEMPORARY}, [a4]\n"
802 # Set the word at _base_ + _offset_ * +@WORDSIZE+ to _value_
803 def set_word base, offset, value
804 emit "# set-word #{base} #{offset} #{value}\n"
805 # If base is an integer, but offset isn't, swap them
806 if !integer?(offset) && integer?(base)
807 base, offset = [offset, base]
811 base_reg = load_value base, :a4
812 load_value_into_register value, @TEMPORARY
814 emit "str #{@TEMPORARY}, [#{base_reg}]\n"
816 emit "str #{@TEMPORARY}, [#{base_reg}, \##{offset * @WORDSIZE}]\n"
819 load_value_into_register base, :a4
820 load_value_into_register offset, @TEMPORARY
821 emit "add a4, a4, #{@TEMPORARY}, LSL #2\n"
822 load_value_into_register value, @TEMPORARY
823 emit "str #{@TEMPORARY}, [a4]\n"
827 # Given n, returns the nearest multiple of @STACK_ALIGNMENT
830 (n + @STACK_ALIGNMENT - 1) / @STACK_ALIGNMENT * @STACK_ALIGNMENT
833 # Define a string with the given value
836 value.each_byte do |b|
839 elsif b >= 32 && b < 127 && b != 34
842 code << sprintf("\\%03o", b)
845 emit ".ascii \"#{code}\"\n"
848 # Test if a value is a symbol
850 value.kind_of? Symbol
853 # Test if op is a symmetric binary operation (i.e. it will yield the
854 # same result if the order of its source operands is changed).
855 def symmetric_binop? op
856 [:add, :and, :mul, :or, :xor].member? op
859 # Call a function, re-using the current call frame if possible.
860 def tail_call func, *args
861 emit "# tail-call #{func} #{args.join ' '}\n"
863 # Compute number of stack arguments
864 nstackargs = number_of_stack_arguments args.length
865 # If we need more stack arguments than we have now,
866 # perform a normal call and return
867 if nstackargs > number_of_stack_arguments(@environment.args)
868 emit "# Not enough space for proper tail call; using regular call\n"
869 ret :call, func, *args
872 # We will assign arguments from left to right.
873 # Find places that we will overwrite before we read them,
874 # and store their values in some newly allocated stack space.
875 old_frame_offset = @frame_offset
876 old_frame_size = @frame_size
878 (@NREGISTER_ARGS...args.length).each do |i|
880 arg = arg[1] if at_expr? arg
882 binding = @environment[arg]
883 if binding[0] == :arg && binding[1] >= @NREGISTER_ARGS &&
885 # Argument i is a stack argument, but the value we will assign
886 # it is a stack argument that comes before it, so we will
887 # have overwritten it by the time we get to it.
888 overwritten[arg] = nil
893 unless overwritten.empty?
894 # Allocate space for arguments to be saved
895 grow_frame overwritten.length
898 overwritten.each_key do |key|
900 emit "str #{reg}, [sp, \##{offset}]\n"
901 overwritten[key] = offset
902 offset = offset + @WORDSIZE
907 args.each_index do |i|
910 load_value_into_register arg, "a#{i + 1}"
912 # Test if this is a value we saved
913 sym = at_expr?(arg) ? arg[1] : arg
914 saved = overwritten[sym]
916 # Saved value, load from stack
918 emit "ldr #{reg}, [sp, \##{saved}]\n"
920 # Regular value, use load_value
923 emit "str #{reg}, #{arg_reference i}\n"
927 # Load address of function to be called into @TEMPORARY
928 load_value_into_register func, @TEMPORARY
930 # Destroy current activation frame and enter func
932 emit "bx #{@TEMPORARY}\n"
936 # Define a word with the given value
938 emit ".int #{value}\n"
941 # Write generated code to the given IO object.
943 @sections.each do |section,code|
945 io.puts ".section #{section.to_s}"
953 # Register class for little endian ARM
954 Voodoo::CodeGenerator.register_generator ARMGasGenerator,
955 :architecture => :arm,