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:
58 # pointer to Global Offset Table
65 # padding <-- $sp points here
69 # The four empty slots provided by the caller are used to store
70 # arguments 0 through 3 (originally passed in $4 through $7),
73 # The stack frame created below the four slots provided by the caller
74 # contains the following data, in order:
76 # - Saved return address (originally passed in $31), if necessary.
78 # - Saved pointer to global offset table (computed from $25), if necessary.
80 # - Saved values of caller's locals 0 through 7
81 # (originally in $16 through $23), if necessary.
83 # - Values of our locals > 8, if necessary.
85 # - Padding to align $sp on a multiple of 8, if necessary.
88 # In accordance with the System V ELF ABI for MIPS, the pointer to
89 # the global offset table is calculated as follows:
91 # $gp = _gp_disp + $25
93 # where $gp is the register to store the pointer, $25 is the register
94 # holding the address of the called function, and _gp_disp is the
95 # offset between the beginning of the function and the global offset table.
97 class MIPSGasGenerator < CommonCodeGenerator
101 @DATA_ALIGNMENT = @WORDSIZE
102 @FUNCTION_ALIGNMENT = @WORDSIZE
104 @INITIAL_FRAME_SIZE = 2 * @WORDSIZE
105 @REGISTER_ARG_BASE = 4
107 @REGISTER_LOCAL_BASE = 16
108 @NREGISTER_LOCALS = 8
113 @TEMPORARIES = [:'$8', :'$9', :'$10', :'$11',
114 :'$12', :'$13', :'$14', :'$15']
115 @function_end_label = nil
119 @output_file_suffix = '.s'
121 :'bits-per-word' => '32',
122 :'bytes-per-word' => '4'
125 @features[:'byte-order'] = 'big-endian'
127 @features[:'byte-order'] = 'little-endian'
129 raise ArgumentError.new("#{self.class} does not support " +
130 "architecture #{@architecture}")
134 def align alignment = nil
136 # Get default alignment
139 alignment = @CODE_ALIGNMENT
141 alignment = @DATA_ALIGNMENT
143 alignment = @FUNCTION_ALIGNMENT
145 # Use data alignment as default
146 alignment = @DATA_ALIGNMENT
149 emit ".align #{alignment}\n" unless alignment == 0
152 # Return the offset from the frame base at which the nth argument is
158 # Return an $sp-relative reference for the nth (0-based) argument
160 offset_reference(arg_offset(n))
163 # Return the register in which the nth (0-based) argument is stored, or
164 # nil if not stored in a register
167 "$#{@REGISTER_ARG_BASE + n}"
173 # Test if op is a binary operation
174 def assymetric_binop? op
175 [:asr, :bsr, :div, :mod, :rol, :ror, :shl, :shr, :sub].member?(op)
178 # Test if a value is an at-expression
180 value.respond_to?(:[]) && value[0] == :'@'
183 # Begins a new block.
184 def begin_block *code
185 emit "# begin block\n"
186 # If we are at top-level, create a frame
187 if @environment == @top_level
188 create_frame count_locals(code)
190 environment = Environment.new @environment
191 @environment = environment
194 # Emit function prologue and declare _formals_ as function arguments
195 def begin_function formals, nlocals
196 if @environment != @top_level
197 raise "Cannot begin function when already in a function"
200 environment = Environment.new @environment
202 formals.each {|x| environment.add_arg x, arg_offset(environment.args)}
203 @environment = environment
204 @function_end_label = gensym
205 emit_function_prologue formals, nlocals
208 # Test if op is a binary operation
210 assymetric_binop?(op) || symmetric_binop?(op)
213 # Define a byte with the given value
215 emit ".byte #{value}\n"
220 emit "# call #{func} #{args.join ' '}\n"
221 # Grow the stack frame, subject to 3 conditions:
222 # 1. There must be enough space to hold all arguments
223 # 2. $sp must remain a multiple of 8
224 # 3. We must provide space for at least 4 words
225 increment = ([args.length, 4].max * @WORDSIZE + 7) / 8 * 8
226 emit "addiu $sp, $sp, -#{increment}\n"
227 @frame_size = @frame_size + increment
229 # Put arguments in the right places
231 while n < args.length
232 if n < @NREGISTER_ARGS
233 # Put arguments up to @NREGISTER_ARGS in registers
234 load_value_into_register args[n], arg_register(n)
236 # Put other arguments on the stack
237 load_value_into_register args[n], @TEMPORARY
238 emit "sw #{@TEMPORARY}, #{n * @WORDSIZE}($sp)\n"
243 # Load function address
245 if @imports.has_key? func
246 # Load address from global offset table
247 emit "lw #{@FUNCTION}, %call16(#{func})(#{@GOT})\n"
249 # Assume label defined in this module
250 emit "lui #{@FUNCTION}, %hi(#{func})\n"
251 emit "addiu #{@FUNCTION}, %lo(#{func})\n"
255 load_value_into_register func, "#{@FUNCTION}"
259 emit "jalr #{@FUNCTION}\n"
263 # Restore original stack frame
264 emit "addiu $sp, $sp, #{increment}\n"
265 @frame_size = @frame_size - increment
268 emit "lw #{@GOT}, #{@frame_size - 2 * @WORDSIZE}($sp)\n"
271 # Start a conditional using the specified branch instruction
272 # after the comparison.
273 def common_if comp, x, y = nil
274 emit "# #{comp} #{x} #{y}\n"
276 temporaries = @TEMPORARIES.dup
277 xreg = load_value x, temporaries[0]
278 temporaries.delete xreg
279 yreg = load_value y, temporaries[0]
280 temporaries.delete yreg
282 falselabel = @environment.gensym
283 @if_labels.push falselabel
287 emit "bne #{xreg}, #{yreg}, #{falselabel}\n"
289 emit "slt #{@TEMPORARY}, #{xreg}, #{yreg}\n"
290 emit "bne #{@TEMPORARY}, $0, #{falselabel}\n"
292 emit "slt #{@TEMPORARY}, #{yreg}, #{xreg}\n"
293 emit "beq #{@TEMPORARY}, $0, #{falselabel}\n"
295 emit "slt #{@TEMPORARY}, #{yreg}, #{xreg}\n"
296 emit "bne #{@TEMPORARY}, $0, #{falselabel}\n"
298 emit "slt #{@TEMPORARY}, #{xreg}, #{yreg}\n"
299 emit "beq #{@TEMPORARY}, $0, #{falselabel}\n"
301 emit "beq #{xreg}, #{yreg}, #{falselabel}\n"
303 raise "Unknown conditional: #{comp}"
308 # Counts the number of local variables created in
309 # a sequence of statements.
310 def count_locals statements
312 each_statement(statements) do |statement|
313 if statement[0] == :let
314 # let introduces a single local
321 # Creates an activation frame which can store nlocals local variables
322 def create_frame nlocals
323 @frame_size = (@INITIAL_FRAME_SIZE + nlocals * @WORDSIZE + 7) / 8 * 8
324 emit "addiu $sp, $sp, -#{@frame_size}\n"
327 # Emit function prologue.
328 def emit_function_prologue formals = [], nlocals = 0
329 # Calculate new value for $gp
330 emit "lui #{@GOT}, %hi(_gp_disp)\n"
331 emit "addiu #{@GOT}, %lo(_gp_disp)\n"
332 emit "addu #{@GOT}, #{@GOT}, #{@FUNCTION}\n"
334 # Save parameters 0 .. 3 in the stack space that the caller
335 # should have allocated for them.
336 [@NREGISTER_ARGS, formals.length].min.times do |n|
337 ref = offset_reference(@environment[formals[n]])
338 emit "sw $#{n + @REGISTER_ARG_BASE}, #{ref}\n"
343 # Save return address
344 emit "sw $31, #{@frame_size - @WORDSIZE}($sp)\n"
347 emit "sw #{@GOT}, #{@frame_size - 2 * @WORDSIZE}($sp)\n"
350 # End a function body
352 if @environment == @top_level
353 raise "Cannot end function when not in a function"
356 emit "# function epilogue\n"
358 label @function_end_label
359 # Restore saved locals
360 [@NREGISTER_LOCALS, @environment.locals].min.times do |n|
361 emit "lw #{local_register n}, #{local_reference n}\n"
363 # Load return address
364 emit "lw $31, #{@frame_size - @WORDSIZE}($sp)\n"
365 # Restore stack pointer
366 emit "addiu $sp, $sp, #{@frame_size}\n"
371 emit "# end function\n\n"
373 @function_end_label = nil
374 @environment = @top_level
377 # Ends the current block.
381 # If we are returning to top level, restore stack pointer
382 if @environment.parent == @top_level
383 emit "addiu $sp, $sp, #{@frame_size}\n"
386 # Restore old value of @environment
387 @environment = @environment.parent
392 label = @if_labels.pop
396 # Evaluate the binary operation expr and store the result in register
397 def eval_binop expr, register
398 temporaries = @TEMPORARIES.dup
399 x = load_value expr[1], temporaries[0]
401 y = load_value expr[2], temporaries[0]
406 emit "srav #{register}, #{x}, #{y}\n"
408 emit "srlv #{register}, #{x}, #{y}\n"
410 emit "div $0, #{x}, #{y}\n"
411 emit "mflo #{register}\n"
413 emit "div $0, #{x}, #{y}\n"
414 emit "mfhi #{register}\n"
416 emit "mult #{x}, #{y}\n"
417 emit "mflo #{register}\n"
419 emit "subu #{@TEMPORARY}, $0, #{y}\n"
420 emit "srlv #{@TEMPORARY}, #{x}, #{@TEMPORARY}\n"
421 emit "sllv #{register}, #{x}, #{y}\n"
422 emit "or #{register}, #{register}, #{@TEMPORARY}\n"
424 emit "subu #{@TEMPORARY}, $0, #{y}\n"
425 emit "sllv #{@TEMPORARY}, #{x}, #{@TEMPORARY}\n"
426 emit "srlv #{register}, #{x}, #{y}\n"
427 emit "or #{register}, #{register}, #{@TEMPORARY}\n"
429 emit "sllv #{register}, #{x}, #{y}\n"
431 emit "srlv #{register}, #{x}, #{y}\n"
433 emit "#{expr[0]} #{register}, #{x}, #{y}\n"
437 # Evaluate the expression expr and store the result in register
438 def eval_expr expr, register
441 load_value_into_register expr[0], register
443 # Evaluate expression
448 emit "move #{register}, #{@RETURN}\n" if register != @RETURN
450 get_byte expr[1], expr[2], register
452 get_word expr[1], expr[2], register
454 eval_binop [:nor, 0, expr[1]], register
457 eval_binop expr, register
459 raise "Not a magic word: #{op}"
465 # Export symbols from the current section
467 symbols.each { |sym| emit ".globl #{sym}\n" }
470 # Add a function to the current section
471 def function formals, *code
472 nlocals = count_locals code
473 begin_function formals, nlocals
474 code.each { |action| add section, action }
478 # Load byte from _base_ + _offset_ into _register_
479 def get_byte base, offset, register
480 # If base is an integer, but offset isn't, swap them
481 if !integer?(offset) && integer?(base)
482 base, offset = [offset, base]
486 base_reg = load_value base
487 emit "lb #{register}, #{offset}(#{base_reg})\n"
489 eval_binop [:add, base, offset], @TEMPORARY
490 emit "lb #{register}, 0(#{@TEMPORARY})\n"
494 # Load word from _base_ + _offset_ * _@WORDSIZE_ into _register_
495 def get_word base, offset, register
497 base_reg = load_value base
498 emit "lw #{register}, #{offset * @WORDSIZE}(#{base_reg})\n"
500 offset_reg = @TEMPORARIES.pop
502 load_value_into_register offset, offset_reg
503 base_reg = load_value base
504 emit "sll #{offset_reg}, #{offset_reg}, 2\n" # multiply by @WORDSIZE
505 emit "add #{@TEMPORARY}, #{base_reg}, #{offset_reg}\n"
506 emit "lw #{register}, 0(#{@TEMPORARY})\n"
508 @TEMPORARIES.push offset_reg
513 # Test if a symbol refers to a global
515 symbol?(symbol) && @environment[symbol] == nil
524 # Start the false path of a conditional.
527 newlabel = @environment.gensym
528 emit "j #{newlabel}\n"
530 label = @if_labels.pop
532 @if_labels.push newlabel
535 # Test if x is equal to y
537 common_if :ifeq, x, y
540 # Test if x is greater than or equal to y
542 common_if :ifge, x, y
545 # Test if x is strictly greater than y
547 common_if :ifgt, x, y
550 # Test if x is less than or equal to y
552 common_if :ifle, x, y
555 # Test if x is strictly less than y
557 common_if :iflt, x, y
560 # Test if x different from y
562 common_if :ifne, x, y
565 # Import labels into the current section
567 # Record imported labels in @imports
568 symbols.each { |sym| @imports[sym] = sym }
571 # Test if a value is an integer
573 value.kind_of? Integer
581 # Introduces a new local variable.
582 def let symbol, *expr
583 emit "# let #{symbol} #{expr.join ' '}\n"
584 n = @environment.locals
585 register = local_register n
586 ref = local_reference n
588 # We will use a register to store the value
589 @environment.add_local symbol, register
590 # Save current value of register
591 emit "sw #{register}, #{ref}\n"
593 eval_expr expr, register
595 # We will use the stack to store the value
596 @environment.add_local symbol, local_offset(n)
597 eval_expr expr, @TEMPORARY
598 emit "sw #{@TEMPORARY}, #{ref}\n"
602 # Loads the value at the given address.
603 def load_at address, register = @TEMPORARY
604 load_value_into_register address, register
605 emit "lw #{register}, 0(#{register})\n"
609 # Loads a value into a register.
610 # Returns the name of the register.
611 # If the value was already in a register, the name of that
612 # register is returned.
613 # Else, the value is loaded into a register and the name of
614 # that register is returned. The register to use in that case
615 # may be specified using the optional second argument.
616 def load_value x, register = @TEMPORARY
620 if x >= -32768 && x <= 32767
621 emit "addiu #{register}, $0, #{x}\n"
624 emit "lui #{register}, #{x >> 16}\n"
625 emit "ori #{register}, #{register}, #{x & 0xffff}\n"
629 binding = @environment[x]
630 if binding.kind_of? String
631 # Value is already in a register. Return register name.
633 elsif binding.kind_of? Integer
634 # Load value from memory.
635 emit "lw #{register}, #{offset_reference binding}\n"
639 emit "lui #{register}, %hi(#{x})\n"
640 emit "addiu #{register}, %lo(#{x})\n"
644 load_at x[1], register
646 raise "Don't know how to load #{x.inspect}"
650 # Load a value into a specific register
651 def load_value_into_register x, register
652 reg = load_value x, register
654 emit "move #{register}, #{reg}\n"
658 # Return the offset from the frame base at which the nth local is stored.
659 # For register locals this is the offset at which the saved
660 # value (from the calling function) is stored.
665 # Return an $sp-relative reference for the nth (0-based) local
666 def local_reference n
667 offset_reference(local_offset(n))
670 # Return the register in which the nth local (0-based) is stored, or
671 # nil if not stored in a register
674 "$#{@REGISTER_LOCAL_BASE + n}"
680 # Compute the maximum number of locals that would fit in the current
683 (@frame_size - @INITIAL_FRAME_SIZE) / @WORDSIZE
686 # Calculate the number of stack arguments,
687 # given the total number of arguments.
688 def number_of_stack_arguments n
689 [0, n - @NREGISTER_ARGS].max
692 # Given an offset relative to the base of the frame, returns
693 # an reference to that memory location.
694 def offset_reference offset
695 "#{offset + @frame_size}($sp)"
698 # Returns true if the nth (0-based) argument is stored in a register
703 # Returns true if the nth (0-based) local is stored in a register
704 def register_local? n
705 n < @NREGISTER_LOCALS
708 # Return a from a function.
710 # _words_ may contain an expression to be evaluated. The result
711 # of the evaluation is returned from the function.
713 emit "# return #{words.join ' '}\n"
714 # Compute return value and store it in @RETURN
715 eval_expr(words, @RETURN) unless words.empty?
717 goto @function_end_label
720 # Set a variable to the result of evaluating an expression
721 def set symbol, *expr
723 eval_expr expr, @RETURN
724 load_value_into_register symbol.to_s[1..-1].to_sym, @TEMPORARY
725 emit "sw #{@RETURN}, 0(#{@TEMPORARY})\n"
727 x = @environment[symbol]
729 raise "Cannot change value of constant #{symbol}"
730 elsif x.kind_of? String
734 # Should be an integer.
735 eval_expr expr, @TEMPORARY
736 emit "sw #{@TEMPORARY}, #{offset_reference x}\n"
741 # Set the byte at _base_ + _offset_ to _value_
742 def set_byte base, offset, value
743 emit "# set-byte #{base} #{offset} #{value}\n"
744 # If base is an integer, but offset isn't, swap them
745 if !integer?(offset) && integer?(base)
746 base, offset = [offset, base]
749 value_reg = @TEMPORARIES.pop
750 load_value_into_register value, value_reg
753 base_reg = load_value base
754 emit "sb #{value_reg}, #{offset}(#{base_reg})\n"
756 eval_binop [:add, base, offset], @TEMPORARY
757 emit "sb #{value_reg}, 0(#{@TEMPORARY})\n"
760 @TEMPORARIES.push value_reg
764 # Set the word at _base_ + _offset_ * +@WORDSIZE+ to _value_
765 def set_word base, offset, value
766 emit "# set-word #{base} #{offset} #{value}\n"
768 value_reg = @TEMPORARIES.pop
769 load_value_into_register value, value_reg
772 base_reg = load_value base
773 emit "sw #{value_reg}, #{offset * @WORDSIZE}(#{base_reg})\n"
775 offset_reg = @TEMPORARIES.pop
777 load_value_into_register offset, offset_reg
778 base_reg = load_value base
779 emit "sll #{offset_reg}, #{offset_reg}, 2\n"
780 emit "add #{@TEMPORARY}, #{base_reg}, #{offset_reg}\n"
781 emit "sw #{value_reg}, 0(#{@TEMPORARY})\n"
783 @TEMPORARIES.push offset_reg
787 @TEMPORARIES.push value_reg
791 # Define a string with the given value
794 value.each_byte do |b|
797 elsif b >= 32 && b < 127 && b != 34
800 code << sprintf("\\%03o", b)
803 emit ".ascii \"#{code}\"\n"
806 # Test if a value is a symbol
808 value.kind_of? Symbol
811 # Test if op is a symmetric binary operation (i.e. it will yield the
812 # same result if the order of its source operands is changed).
813 def symmetric_binop? op
814 [:add, :and, :mul, :or, :xor].member? op
817 # Call a function, re-using the current call frame if possible.
818 def tail_call func, *args
819 emit "# tail-call #{func} #{args.join ' '}\n"
821 # Compute number of stack arguments
822 nstackargs = number_of_stack_arguments args.length
823 # If we need more stack arguments than we have now,
824 # perform a normal call and return
825 if nstackargs > number_of_stack_arguments(@environment.args)
826 emit "# Not enough space for proper tail call; using regular call\n"
827 ret :call, func, *args
830 # Back up any stack arguments we will need after we overwrite them
831 temporaries = @TEMPORARIES.dup
832 nlocals = @environment.locals
833 (@NREGISTER_ARGS...args.length).each do |i|
834 x = @environment[args[i]]
835 if x && x[0] == :arg && x[1] >= @NREGISTER_ARGS && x[1] < i
836 # args[i] is a stack arg, but has an index < i
837 # That means that, by the time we get to pass arg[i] to the called
838 # function, it will have been overwritten. So we make a backup.
839 if temporaries.empty?
840 # Oh dear, we're out of temporaries.
841 # Store the value in the current stack frame, extending it
843 if nlocals >= max_locals
844 emit "addiu $sp, -8\n"
845 @frame_size = @frame_size + 8
847 reg = load_value args[i]
848 emit "sw #{reg}, #{local_reference nlocals}\n"
849 args[i] = [:local, nlocals]
850 nlocals = nlocals + 1
852 # Load the argument into a temporary
853 reg = temporaries.shift
854 load_value_into_register args[i], reg
855 # Update args[i] so we know how to get to it later
856 args[i] = [:reg, reg]
861 # Set stack arguments
862 (@NREGISTER_ARGS...args.length).each do |i|
867 if arg.kind_of? Array
868 # Special cases, created above
873 emit "lw #{reg}, #{local_reference arg[1]}\n"
876 load_value_into_register arg, reg
879 # Store value in designated place
880 emit "sw #{reg}, #{arg_reference i}\n"
883 # Set register arguments
884 [@NREGISTER_ARGS,args.length].min.times do |i|
886 load_value_into_register args[i], reg
889 # Restore saved registers
890 [@NREGISTER_LOCALS, @environment.locals].min.times do |n|
891 emit "lw #{local_register n}, #{local_reference n}\n"
893 # Restore return address
894 emit "lw $31, #{@frame_size - @WORDSIZE}($sp)\n"
896 # Adjust stack pointer
897 emit "addiu $sp, #{@frame_size}\n"
899 # Load function address
901 if @imports.has_key? func
902 # Load address from global offset table
903 emit "lw #{@FUNCTION}, %call16(#{func})(#{@GOT})\n"
905 # Assume label defined in this module
906 emit "lui #{@FUNCTION}, %hi(#{func})\n"
907 emit "addiu #{@FUNCTION}, %lo(#{func})\n"
911 load_value_into_register func, "#{@FUNCTION}"
915 emit "jr #{@FUNCTION}\n"
920 # Define a word with the given value
922 emit ".int #{value}\n"
925 # Write generated code to the given IO object.
928 @sections.each do |section,code|
930 io.puts ".section #{section.to_s}"
939 # Register class for big endian MIPS
940 Voodoo::CodeGenerator.register_generator MIPSGasGenerator,
941 :architecture => :mips,
945 # Register class for little endian MIPS
946 Voodoo::CodeGenerator.register_generator MIPSGasGenerator,
947 :architecture => :mipsel,