1 require 'voodoo/generators/common_code_generator'
4 # = MIPS GNU Assembler Code Generator
6 # The MIPS code generator generates assembly code for use with
9 # == Calling Convention
11 # The first four arguments are passed in the registers $4 through $7.
12 # Any additional arguments are passed on the stack, starting at
13 # $sp + 16. Words $sp through $sp + 12 will be available for the called
14 # function to use. $sp will always be a multiple of 8.
16 # The return address for the called function is passed in $31.
18 # When performing a position-independent call, the address of the called
19 # function is passed in $25.
21 # The called function will store its return value in $2.
23 # The called function is required to preserve the values of registers
24 # $16 through $23 and register $30.
26 # This calling convention is compatible with the System V ELF ABI.
30 # Call frames have the following layout:
32 # When a function is called, it receives a stack frame that looks like
43 # empty0 <-- $sp points here
45 # The function prologue of functions generated by this code generator
46 # creates activation frames that look as follows:
56 # arg0 <-- $fp points here
58 # pointer to Global Offset Table
66 # padding <-- $sp points here
70 # The four empty slots provided by the caller are used to store
71 # arguments 0 through 3 (originally passed in $4 through $7),
74 # The stack frame created below the four slots provided by the caller
75 # contains the following data, in order:
77 # - Saved return address (originally passed in $31), if necessary.
79 # - Saved pointer to global offset table (computed from $25), if necessary.
81 # - Saved value of $fp, if necessary.
83 # - Saved values of caller's locals 0 through 7
84 # (originally in $16 through $23), if necessary.
86 # - Values of our locals > 8, if necessary.
88 # - Padding to align $sp on a multiple of 8, if necessary.
91 # In accordance with the System V ELF ABI for MIPS, the pointer to
92 # the global offset table is calculated as follows:
94 # $gp = _gp_disp + $25
96 # where $gp is the register to store the pointer, $25 is the register
97 # holding the address of the called function, and _gp_disp is the
98 # offset between the beginning of the function and the global offset table.
100 class MIPSGasGenerator < CommonCodeGenerator
101 def initialize params
103 @WORDSIZE = 1 << @WORDSIZE_BITS
105 @DATA_ALIGNMENT = @WORDSIZE
106 @STACK_ALIGNMENT_BITS = 3
107 @STACK_ALIGNMENT = 1 << @STACK_ALIGNMENT_BITS
108 @FP_OFFSET = -3 * @WORDSIZE
109 @FUNCTION_ALIGNMENT = @WORDSIZE
110 @GP_OFFSET = -2 * @WORDSIZE
111 @INITIAL_FRAME_SIZE = 4 * @WORDSIZE
112 @REGISTER_ARG_BASE = 4
114 @REGISTER_LOCAL_BASE = 16
115 @NREGISTER_LOCALS = 8
116 @RA_OFFSET = -@WORDSIZE
121 @TEMPORARIES = [:'$8', :'$9', :'$10', :'$11',
122 :'$12', :'$13', :'$14', :'$15']
123 @function_end_label = nil
127 @output_file_suffix = '.s'
129 :'bits-per-word' => '32',
130 :'bytes-per-word' => '4'
133 @features[:'byte-order'] = 'big-endian'
135 @features[:'byte-order'] = 'little-endian'
137 raise ArgumentError.new("#{self.class} does not support " +
138 "architecture #{@architecture}")
142 # Adds immediate to source and stores the result in target.
143 def addiu target, source, immediate, temporary = @TEMPORARY
144 if immediate >= -32768 && immediate < 32767
145 emit "addiu #{target}, #{source}, #{immediate}\n"
147 emit "lui #{target}, #{(immediate >> 16) & 0xffff}\n"
148 emit "ori #{target}, #{target}, #{immediate & 0xffff}\n"
150 addiu temporary, "$0", immediate
151 emit "addu #{target}, #{source}, #{temporary}\n"
155 def align alignment = nil
157 # Get default alignment
160 alignment = @CODE_ALIGNMENT
162 alignment = @DATA_ALIGNMENT
164 alignment = @FUNCTION_ALIGNMENT
166 # Use data alignment as default
167 alignment = @DATA_ALIGNMENT
170 emit ".align #{alignment}\n" unless alignment == 0
173 # Return the offset from the frame base at which the nth argument is
179 # Return an $fp-relative reference for the nth (0-based) argument
181 offset_reference(arg_offset(n))
184 # Return the register in which the nth (0-based) argument is stored, or
185 # nil if not stored in a register
188 "$#{@REGISTER_ARG_BASE + n}"
194 # Test if op is a binary operation
195 def assymetric_binop? op
196 [:asr, :bsr, :div, :mod, :rol, :ror, :shl, :shr, :sub].member?(op)
199 # Test if a value is an at-expression
201 value.respond_to?(:[]) && value[0] == :'@'
204 def auto_bytes n, register
205 if n.kind_of? Integer
206 # Maintain stack alignment
207 n = (n + @STACK_ALIGNMENT - 1) / @STACK_ALIGNMENT * @STACK_ALIGNMENT
210 temporary = @TEMPORARIES.pop
212 load_value_into_register n, register
213 emit "addi #{register}, #{@STACK_ALIGNMENT - 1}\n"
214 emit "li #{temporary}, -#{@STACK_ALIGNMENT}\n"
215 emit "and #{register}, #{register}, #{temporary}\n"
216 emit "sub $sp, #{register}\n"
218 @TEMPORARIES.push temporary
221 emit "move #{register}, $sp\n" unless register == "$sp"
224 def auto_words n, register
225 if n.kind_of? Integer
226 auto_bytes n * @WORDSIZE, register
228 load_value_into_register n, register
229 if @STACK_ALIGNMENT_BITS > @WORDSIZE_BITS
230 bits = @STACK_ALIGNMENT_BITS - @WORDSIZE_BITS
231 emit "addi #{register}, #{(1 << bits) - 1}\n"
232 emit "srl #{register}, #{register}, #{bits}\n"
233 emit "sll #{register}, #{register}, #{@STACK_ALIGNMENT_BITS}\n"
235 emit "sll #{register}, #{register}, #{@WORDSIZE_BITS}\n"
237 emit "sub $sp, $sp, #{register}\n"
238 emit "move #{register}, $sp\n" unless register == "$sp"
242 # Begins a new block.
243 def begin_block *code
244 emit "# begin block\n"
245 # If we are at top-level, create a frame
246 if @environment == @top_level
247 create_frame count_locals(code)
249 environment = Environment.new @environment
250 @environment = environment
253 # Emit function prologue and declare _formals_ as function arguments
254 def begin_function formals, nlocals
255 if @environment != @top_level
256 raise "Cannot begin function when already in a function"
259 environment = Environment.new @environment
261 formals.each {|x| environment.add_arg x, arg_offset(environment.args)}
262 @environment = environment
263 @function_end_label = gensym
264 emit_function_prologue formals, nlocals
267 # Test if op is a binary operation
269 assymetric_binop?(op) || symmetric_binop?(op)
272 # Define a byte with the given value
274 emit ".byte #{value}\n"
279 # Grow the stack frame, subject to 3 conditions:
280 # 1. There must be enough space to hold all arguments
281 # 2. $sp must remain a multiple of 8
282 # 3. We must provide space for at least 4 words
283 increment = ([args.length, 4].max * @WORDSIZE + 7) / 8 * 8
286 # Put arguments in the right places
288 while n < args.length
289 if n < @NREGISTER_ARGS
290 # Put arguments up to @NREGISTER_ARGS in registers
291 load_value_into_register args[n], arg_register(n)
293 # Put other arguments on the stack
294 load_value_into_register args[n], @TEMPORARY
295 emit "sw #{@TEMPORARY}, #{n * @WORDSIZE}($sp)\n"
300 # Load function address
302 if @imports.has_key? func
303 # Load address from global offset table
304 emit "lw #{@FUNCTION}, %call16(#{func})(#{@GOT})\n"
306 # Assume label defined in this module
307 emit "lui #{@FUNCTION}, %hi(#{func})\n"
308 emit "addiu #{@FUNCTION}, %lo(#{func})\n"
312 load_value_into_register func, "#{@FUNCTION}"
316 emit "jalr #{@FUNCTION}\n"
320 # Restore original stack frame
321 grow_stack -increment
324 emit "lw #{@GOT}, #{@GP_OFFSET}($fp)\n"
332 # Start a conditional using the specified branch instruction
333 # after the comparison.
334 def common_if comp, x, y = nil
335 emit "# #{comp} #{x} #{y}\n"
337 temporaries = @TEMPORARIES.dup
338 xreg = load_value x, temporaries[0]
339 temporaries.delete xreg
340 yreg = load_value y, temporaries[0]
341 temporaries.delete yreg
343 falselabel = @environment.gensym
344 @if_labels.push falselabel
348 emit "bne #{xreg}, #{yreg}, #{falselabel}\n"
350 emit "slt #{@TEMPORARY}, #{xreg}, #{yreg}\n"
351 emit "bne #{@TEMPORARY}, $0, #{falselabel}\n"
353 emit "slt #{@TEMPORARY}, #{yreg}, #{xreg}\n"
354 emit "beq #{@TEMPORARY}, $0, #{falselabel}\n"
356 emit "slt #{@TEMPORARY}, #{yreg}, #{xreg}\n"
357 emit "bne #{@TEMPORARY}, $0, #{falselabel}\n"
359 emit "slt #{@TEMPORARY}, #{xreg}, #{yreg}\n"
360 emit "beq #{@TEMPORARY}, $0, #{falselabel}\n"
362 emit "beq #{xreg}, #{yreg}, #{falselabel}\n"
364 raise "Unknown conditional: #{comp}"
369 # Creates an activation frame which can store nlocals local variables
370 def create_frame nlocals
371 @frame_size = @INITIAL_FRAME_SIZE
373 @frame_size = @frame_size + (nlocals * @WORDSIZE + 7) / 8 * 8
375 grow_stack @frame_size
378 # Emit function prologue.
379 def emit_function_prologue formals = [], nlocals = 0
380 # Calculate new value for $gp
381 emit "lui #{@GOT}, %hi(_gp_disp)\n"
382 emit "addiu #{@GOT}, %lo(_gp_disp)\n"
383 emit "addu #{@GOT}, #{@GOT}, #{@FUNCTION}\n"
387 # Save return address
388 emit "sw $31, #{@frame_size - @WORDSIZE}($sp)\n"
391 emit "sw #{@GOT}, #{@frame_size - 2 * @WORDSIZE}($sp)\n"
394 emit "sw $fp, #{@frame_size - 3 * @WORDSIZE}($sp)\n"
396 # Point fp at where sp was before we created the frame
397 addiu "$fp", "$sp", @frame_size
399 # Save parameters 0 .. 3 in the stack space that the caller
400 # should have allocated for them.
401 [@NREGISTER_ARGS, formals.length].min.times do |n|
402 ref = offset_reference(@environment[formals[n]])
403 emit "sw $#{n + @REGISTER_ARG_BASE}, #{ref}\n"
407 # End a function body
409 if @environment == @top_level
410 raise "Cannot end function when not in a function"
413 label @function_end_label
414 # Restore saved locals
415 [@NREGISTER_LOCALS, @environment.locals].min.times do |n|
416 emit "lw #{local_register n}, #{local_reference n}\n"
418 # Load return address
419 emit "lw $31, #{@RA_OFFSET}($fp)\n"
420 # Restore stack pointer
421 emit "move $sp, $fp\n"
422 # Restore frame pointer
423 emit "lw $fp, #{@FP_OFFSET}($fp)\n"
428 emit "# end function\n\n"
430 @function_end_label = nil
431 @environment = @top_level
434 # Ends the current block.
438 # If we are returning to top level, restore stack pointer
439 if @environment.parent == @top_level
440 grow_stack -@frame_size
443 # Restore old value of @environment
444 @environment = @environment.parent
449 label = @if_labels.pop
453 # Evaluate the binary operation expr and store the result in register
454 def eval_binop expr, register
455 temporaries = @TEMPORARIES.dup
456 x = load_value expr[1], temporaries[0]
458 y = load_value expr[2], temporaries[0]
463 emit "srav #{register}, #{x}, #{y}\n"
465 emit "srlv #{register}, #{x}, #{y}\n"
467 emit "div $0, #{x}, #{y}\n"
468 emit "mflo #{register}\n"
470 emit "div $0, #{x}, #{y}\n"
471 emit "mfhi #{register}\n"
473 emit "mult #{x}, #{y}\n"
474 emit "mflo #{register}\n"
476 emit "subu #{@TEMPORARY}, $0, #{y}\n"
477 emit "srlv #{@TEMPORARY}, #{x}, #{@TEMPORARY}\n"
478 emit "sllv #{register}, #{x}, #{y}\n"
479 emit "or #{register}, #{register}, #{@TEMPORARY}\n"
481 emit "subu #{@TEMPORARY}, $0, #{y}\n"
482 emit "sllv #{@TEMPORARY}, #{x}, #{@TEMPORARY}\n"
483 emit "srlv #{register}, #{x}, #{y}\n"
484 emit "or #{register}, #{register}, #{@TEMPORARY}\n"
486 emit "sllv #{register}, #{x}, #{y}\n"
488 emit "srlv #{register}, #{x}, #{y}\n"
490 emit "#{expr[0]} #{register}, #{x}, #{y}\n"
494 # Evaluate the expression expr and store the result in register
495 def eval_expr expr, register
498 load_value_into_register expr[0], register
500 # Evaluate expression
504 auto_bytes expr[1], register
506 auto_words expr[1], register
509 emit "move #{register}, #{@RETURN}\n" if register != @RETURN
511 get_byte expr[1], expr[2], register
513 get_word expr[1], expr[2], register
515 eval_binop [:nor, 0, expr[1]], register
518 eval_binop expr, register
520 raise "Not a magic word: #{op}"
526 # Export symbols from the current section
528 symbols.each { |sym| emit ".globl #{sym}\n" }
531 # Load byte from _base_ + _offset_ into _register_
532 def get_byte base, offset, register
533 # If base is an integer, but offset isn't, swap them
534 if !integer?(offset) && integer?(base)
535 base, offset = [offset, base]
539 base_reg = load_value base
540 emit "lb #{register}, #{offset}(#{base_reg})\n"
542 eval_binop [:add, base, offset], @TEMPORARY
543 emit "lb #{register}, 0(#{@TEMPORARY})\n"
547 # Load word from _base_ + _offset_ * _@WORDSIZE_ into _register_
548 def get_word base, offset, register
550 base_reg = load_value base
551 emit "lw #{register}, #{offset * @WORDSIZE}(#{base_reg})\n"
553 offset_reg = @TEMPORARIES.pop
555 load_value_into_register offset, offset_reg
556 base_reg = load_value base
557 emit "sll #{offset_reg}, #{offset_reg}, 2\n" # multiply by @WORDSIZE
558 emit "add #{@TEMPORARY}, #{base_reg}, #{offset_reg}\n"
559 emit "lw #{register}, 0(#{@TEMPORARY})\n"
561 @TEMPORARIES.push offset_reg
566 # Test if a symbol refers to a global
568 symbol?(symbol) && @environment[symbol] == nil
577 # Grows the stack by n bytes.
579 addiu "$sp", "$sp", -n
582 # Start the false path of a conditional.
585 newlabel = @environment.gensym
586 emit "j #{newlabel}\n"
588 label = @if_labels.pop
590 @if_labels.push newlabel
593 # Test if x is equal to y
595 common_if :ifeq, x, y
598 # Test if x is greater than or equal to y
600 common_if :ifge, x, y
603 # Test if x is strictly greater than y
605 common_if :ifgt, x, y
608 # Test if x is less than or equal to y
610 common_if :ifle, x, y
613 # Test if x is strictly less than y
615 common_if :iflt, x, y
618 # Test if x different from y
620 common_if :ifne, x, y
623 # Import labels into the current section
625 # Record imported labels in @imports
626 symbols.each { |sym| @imports[sym] = sym }
629 # Test if a value is an integer
631 value.kind_of? Integer
639 # Introduces a new local variable.
640 def let symbol, *expr
641 emit "# let #{symbol} #{expr.join ' '}\n"
642 n = @environment.locals
643 register = local_register n
644 ref = local_reference n
646 # We will use a register to store the value
647 @environment.add_local symbol, register
648 # Save current value of register
649 emit "sw #{register}, #{ref}\n"
651 eval_expr expr, register
653 # We will use the stack to store the value
654 @environment.add_local symbol, local_offset(n)
655 eval_expr expr, @TEMPORARY
656 emit "sw #{@TEMPORARY}, #{ref}\n"
660 # Loads the value at the given address.
661 def load_at address, register = @TEMPORARY
662 load_value_into_register address, register
663 emit "lw #{register}, 0(#{register})\n"
667 # Loads a value into a register.
668 # Returns the name of the register.
669 # If the value was already in a register, the name of that
670 # register is returned.
671 # Else, the value is loaded into a register and the name of
672 # that register is returned. The register to use in that case
673 # may be specified using the optional second argument.
674 def load_value x, register = @TEMPORARY
678 addiu register, "$0", x
681 binding = @environment[x]
682 if binding.kind_of? String
683 # Value is already in a register. Return register name.
685 elsif binding.kind_of? Integer
686 # Load value from memory.
687 emit "lw #{register}, #{offset_reference binding}\n"
691 emit "lui #{register}, %hi(#{x})\n"
692 emit "addiu #{register}, %lo(#{x})\n"
696 load_at x[1], register
698 raise "Don't know how to load #{x.inspect}"
702 # Load a value into a specific register
703 def load_value_into_register x, register
704 reg = load_value x, register
706 emit "move #{register}, #{reg}\n"
710 # Return the offset from the frame base at which the nth local is stored.
711 # For register locals this is the offset at which the saved
712 # value (from the calling function) is stored.
717 # Return an $sp-relative reference for the nth (0-based) local
718 def local_reference n
719 offset_reference(local_offset(n))
722 # Return the register in which the nth local (0-based) is stored, or
723 # nil if not stored in a register
726 "$#{@REGISTER_LOCAL_BASE + n}"
732 # Compute the maximum number of locals that would fit in the current
735 # + 1, because the initial frame size has room for 1 local
736 (@frame_size - @INITIAL_FRAME_SIZE) / @WORDSIZE + 1
739 # Calculate the number of stack arguments,
740 # given the total number of arguments.
741 def number_of_stack_arguments n
742 [0, n - @NREGISTER_ARGS].max
745 # Given an offset relative to the base of the frame, returns
746 # an reference to that memory location.
747 def offset_reference offset
751 # Returns true if the nth (0-based) argument is stored in a register
756 # Returns true if the nth (0-based) local is stored in a register
757 def register_local? n
758 n < @NREGISTER_LOCALS
761 # Return a from a function.
763 # _words_ may contain an expression to be evaluated. The result
764 # of the evaluation is returned from the function.
766 emit "# return #{words.join ' '}\n"
767 # Compute return value and store it in @RETURN
768 eval_expr(words, @RETURN) unless words.empty?
770 goto @function_end_label
773 # Set a variable to the result of evaluating an expression
774 def set symbol, *expr
776 eval_expr expr, @RETURN
777 load_value_into_register symbol.to_s[1..-1].to_sym, @TEMPORARY
778 emit "sw #{@RETURN}, 0(#{@TEMPORARY})\n"
780 x = @environment[symbol]
782 raise "Cannot change value of constant #{symbol}"
783 elsif x.kind_of? String
787 # Should be an integer.
788 eval_expr expr, @TEMPORARY
789 emit "sw #{@TEMPORARY}, #{offset_reference x}\n"
794 # Set the byte at _base_ + _offset_ to _value_
795 def set_byte base, offset, value
796 emit "# set-byte #{base} #{offset} #{value}\n"
797 # If base is an integer, but offset isn't, swap them
798 if !integer?(offset) && integer?(base)
799 base, offset = [offset, base]
802 value_reg = @TEMPORARIES.pop
803 load_value_into_register value, value_reg
806 base_reg = load_value base
807 emit "sb #{value_reg}, #{offset}(#{base_reg})\n"
809 eval_binop [:add, base, offset], @TEMPORARY
810 emit "sb #{value_reg}, 0(#{@TEMPORARY})\n"
813 @TEMPORARIES.push value_reg
817 # Set the word at _base_ + _offset_ * +@WORDSIZE+ to _value_
818 def set_word base, offset, value
819 emit "# set-word #{base} #{offset} #{value}\n"
821 value_reg = @TEMPORARIES.pop
822 load_value_into_register value, value_reg
825 base_reg = load_value base
826 emit "sw #{value_reg}, #{offset * @WORDSIZE}(#{base_reg})\n"
828 offset_reg = @TEMPORARIES.pop
830 load_value_into_register offset, offset_reg
831 base_reg = load_value base
832 emit "sll #{offset_reg}, #{offset_reg}, 2\n"
833 emit "add #{@TEMPORARY}, #{base_reg}, #{offset_reg}\n"
834 emit "sw #{value_reg}, 0(#{@TEMPORARY})\n"
836 @TEMPORARIES.push offset_reg
840 @TEMPORARIES.push value_reg
844 # Define a string with the given value
847 value.each_byte do |b|
850 elsif b >= 32 && b < 127 && b != 34
853 code << sprintf("\\%03o", b)
856 emit ".ascii \"#{code}\"\n"
859 # Test if a value is a symbol
861 value.kind_of? Symbol
864 # Test if op is a symmetric binary operation (i.e. it will yield the
865 # same result if the order of its source operands is changed).
866 def symmetric_binop? op
867 [:add, :and, :mul, :or, :xor].member? op
870 # Call a function, re-using the current call frame if possible.
871 def tail_call func, *args
872 emit "# tail-call #{func} #{args.join ' '}\n"
874 # Compute number of stack arguments
875 nstackargs = number_of_stack_arguments args.length
876 # If we need more stack arguments than we have now,
877 # perform a normal call and return
878 if nstackargs > number_of_stack_arguments(@environment.args)
879 emit "# Not enough space for proper tail call; using regular call\n"
880 ret :call, func, *args
883 # Back up any stack arguments we will need after we overwrite them
884 temporaries = @TEMPORARIES.dup
885 nlocals = @environment.locals
886 (@NREGISTER_ARGS...args.length).each do |i|
887 x = @environment[args[i]]
888 if x && x[0] == :arg && x[1] >= @NREGISTER_ARGS && x[1] < i
889 # args[i] is a stack arg, but has an index < i
890 # That means that, by the time we get to pass arg[i] to the called
891 # function, it will have been overwritten. So we make a backup.
892 if temporaries.empty?
893 # Oh dear, we're out of temporaries.
894 # Store the value in the current stack frame, extending it
896 if nlocals >= max_locals
899 reg = load_value args[i]
900 emit "sw #{reg}, #{local_reference nlocals}\n"
901 args[i] = [:local, nlocals]
902 nlocals = nlocals + 1
904 # Load the argument into a temporary
905 reg = temporaries.shift
906 load_value_into_register args[i], reg
907 # Update args[i] so we know how to get to it later
908 args[i] = [:reg, reg]
913 # Set stack arguments
914 (@NREGISTER_ARGS...args.length).each do |i|
919 if arg.kind_of? Array
920 # Special cases, created above
925 emit "lw #{reg}, #{local_reference arg[1]}\n"
928 load_value_into_register arg, reg
931 # Store value in designated place
932 emit "sw #{reg}, #{arg_reference i}\n"
935 # Set register arguments
936 [@NREGISTER_ARGS,args.length].min.times do |i|
938 load_value_into_register args[i], reg
941 # Load function address
943 if @imports.has_key? func
944 # Load address from global offset table
945 emit "lw #{@FUNCTION}, %call16(#{func})(#{@GOT})\n"
947 # Assume label defined in this module
948 emit "lui #{@FUNCTION}, %hi(#{func})\n"
949 emit "addiu #{@FUNCTION}, %lo(#{func})\n"
953 load_value_into_register func, "#{@FUNCTION}"
956 # Restore saved registers
957 [@NREGISTER_LOCALS, @environment.locals].min.times do |n|
958 emit "lw #{local_register n}, #{local_reference n}\n"
961 # Restore return address
962 emit "lw $31, #{@RA_OFFSET}($fp)\n"
964 # Restore stack pointer
965 emit "move $sp, $fp\n"
967 # Restore frame pointer
968 emit "lw $fp, #{@FP_OFFSET}($fp)\n"
971 emit "jr #{@FUNCTION}\n"
976 # Define a word with the given value
978 emit ".int #{value}\n"
981 # Write generated code to the given IO object.
984 @sections.each do |section,code|
986 io.puts ".section #{section.to_s}"
995 # Register class for big endian MIPS
996 Voodoo::CodeGenerator.register_generator MIPSGasGenerator,
997 :architecture => :mips,
1001 # Register class for little endian MIPS
1002 Voodoo::CodeGenerator.register_generator MIPSGasGenerator,
1003 :architecture => :mipsel,