1 require 'voodoo/generators/common_code_generator'
4 # = MIPS GNU Assembler Code Generator
6 # The MIPS code generator generates i386 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 activiation 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'
122 def align alignment = nil
124 # Get default alignment
127 alignment = @CODE_ALIGNMENT
129 alignment = @DATA_ALIGNMENT
131 alignment = @FUNCTION_ALIGNMENT
133 # Use data alignment as default
134 alignment = @DATA_ALIGNMENT
137 emit ".align #{alignment}\n" unless alignment == 0
140 # Return the offset from $sp at which the nth (0-based) argument is
143 @frame_size + n * @WORDSIZE
146 # Return an $sp-relative reference for the nth (0-based) argument
148 "#{arg_offset n}($sp)"
151 # Return the register in which the nth (0-based) argument is stored, or
152 # nil if not stored in a register
155 "$#{@REGISTER_ARG_BASE + n}"
161 # Test if op is a binary operation
162 def assymetric_binop? op
163 [:div, :mod, :sub].member?(op)
166 # Test if a value is an at-expression
168 value.respond_to?(:[]) && value[0] == :'@'
171 # Begins a new block.
172 def begin_block *code
173 emit "# begin block\n"
174 # If we are at top-level, create a frame
175 if @environment == @top_level
176 create_frame count_locals(code)
178 environment = Environment.new @environment
179 @environment = environment
182 # Emit function prologue and declare _formals_ as function arguments
183 def begin_function formals, nlocals
184 if @environment != @top_level
185 raise "Cannot begin function when already in a function"
188 emit "# function #{formals.join ' '}\n"
189 environment = Environment.new @environment
190 environment.add_args formals
191 @environment = environment
192 @function_end_label = gensym
193 emit_function_prologue formals, nlocals
196 # Test if op is a binary operation
198 assymetric_binop?(op) || symmetric_binop?(op)
201 # Define a byte with the given value
203 emit ".byte #{value}\n"
208 emit "# call #{func} #{args.join ' '}\n"
209 # Grow the stack frame, subject to 3 conditions:
210 # 1. There must be enough space to hold all arguments
211 # 2. $sp must remain a multiple of 8
212 # 3. We must provide space for at least 4 words
213 increment = ([args.length, 4].max * @WORDSIZE + 7) / 8 * 8
214 emit "addiu $sp, $sp, -#{increment}\n"
215 @frame_size = @frame_size + increment
217 # Put arguments in the right places
219 while n < args.length
220 if n < @NREGISTER_ARGS
221 # Put arguments up to @NREGISTER_ARGS in registers
222 load_value_into_register args[n], arg_register(n)
224 # Put other arguments on the stack
225 load_value_into_register args[n], @TEMPORARY
226 emit "sw #{@TEMPORARY}, #{n * @WORDSIZE}($sp)\n"
231 # Load function address
233 if @imports.has_key? func
234 # Load address from global offset table
235 emit "lw #{@FUNCTION}, %call16(#{func})(#{@GOT})\n"
237 # Assume label defined in this module
238 emit "lui #{@FUNCTION}, %hi(#{func})\n"
239 emit "addiu #{@FUNCTION}, %lo(#{func})\n"
243 load_value_into_register func, "#{@FUNCTION}"
247 emit "jalr #{@FUNCTION}\n"
251 # Restore original stack frame
252 emit "addiu $sp, $sp, #{increment}\n"
253 @frame_size = @frame_size - increment
256 emit "lw #{@GOT}, #{@frame_size - 2 * @WORDSIZE}($sp)\n"
259 # Start a conditional using the specified branch instruction
260 # after the comparison.
261 def common_if comp, x, y = nil
262 emit "# #{comp} #{x} #{y}\n"
264 temporaries = @TEMPORARIES.dup
265 xreg = load_value x, temporaries[0]
266 temporaries.delete xreg
267 yreg = load_value y, temporaries[0]
268 temporaries.delete yreg
270 falselabel = @environment.gensym
271 @if_labels.push falselabel
275 emit "bne #{xreg}, #{yreg}, #{falselabel}\n"
277 emit "slt #{@TEMPORARY}, #{xreg}, #{yreg}\n"
278 emit "bne #{@TEMPORARY}, $0, #{falselabel}\n"
280 emit "slt #{@TEMPORARY}, #{yreg}, #{xreg}\n"
281 emit "beq #{@TEMPORARY}, $0, #{falselabel}\n"
283 emit "slt #{@TEMPORARY}, #{yreg}, #{xreg}\n"
284 emit "bne #{@TEMPORARY}, $0, #{falselabel}\n"
286 emit "slt #{@TEMPORARY}, #{xreg}, #{yreg}\n"
287 emit "beq #{@TEMPORARY}, $0, #{falselabel}\n"
289 emit "beq #{xreg}, #{yreg}, #{falselabel}\n"
291 raise "Unknown conditional: #{comp}"
296 # Counts the number of local variables created in
297 # a sequence of statements.
298 def count_locals statements
300 each_statement(statements) do |statement|
301 if statement[0] == :let
302 # let introduces a single local
309 # Creates an activation frame which can store nlocals local variables
310 def create_frame nlocals
311 @frame_size = (@INITIAL_FRAME_SIZE + nlocals * @WORDSIZE + 7) / 8 * 8
312 emit "addiu $sp, $sp, -#{@frame_size}\n"
315 # Emit function prologue.
316 def emit_function_prologue formals = [], nlocals = 0
317 # Calculate new value for $gp
318 emit "lui #{@GOT}, %hi(_gp_disp)\n"
319 emit "addiu #{@GOT}, %lo(_gp_disp)\n"
320 emit "addu #{@GOT}, #{@GOT}, #{@FUNCTION}\n"
322 # Save parameters 0 .. 3 in the stack space that the caller
323 # should have allocated for them.
324 [@NREGISTER_ARGS, formals.length].min.times do |n|
325 emit "sw $#{n + @REGISTER_ARG_BASE}, #{n * @WORDSIZE}($29)\n"
330 # Save return address
331 emit "sw $31, #{@frame_size - @WORDSIZE}($sp)\n"
334 emit "sw #{@GOT}, #{@frame_size - 2 * @WORDSIZE}($sp)\n"
337 # End a function body
339 if @environment == @top_level
340 raise "Cannot end function when not in a function"
343 emit "# function epilogue\n"
345 label @function_end_label
346 # Restore saved locals
347 [@NREGISTER_LOCALS, @environment.locals].min.times do |n|
348 emit "lw #{local_register n}, #{local_reference n}\n"
350 # Load return address
351 emit "lw $31, #{@frame_size - @WORDSIZE}($sp)\n"
352 # Restore stack pointer
353 emit "addiu $sp, $sp, #{@frame_size}\n"
358 emit "# end function\n\n"
360 @function_end_label = nil
361 @environment = @top_level
364 # Ends the current block.
368 # If we are returning to top level, restore stack pointer
369 if @environment.parent == @top_level
370 emit "addiu $sp, $sp, #{@frame_size}\n"
373 # Restore old value of @environment
374 @environment = @environment.parent
379 label = @if_labels.pop
383 # Evaluate the binary operation expr and store the result in register
384 def eval_binop expr, register
385 temporaries = @TEMPORARIES.dup
386 x = load_value expr[1], temporaries[0]
388 y = load_value expr[2], temporaries[0]
393 emit "div $0, #{x}, #{y}\n"
394 emit "mflo #{register}\n"
396 emit "div $0, #{x}, #{y}\n"
397 emit "mfhi #{register}\n"
399 emit "mult #{x}, #{y}\n"
400 emit "mflo #{register}\n"
402 emit "#{expr[0]} #{register}, #{x}, #{y}\n"
406 # Evaluate the expression expr and store the result in register
407 def eval_expr expr, register
410 load_value_into_register expr[0], register
412 # Evaluate expression
417 emit "move #{register}, #{@RETURN}\n" if register != @RETURN
419 get_byte expr[1], expr[2], register
421 get_word expr[1], expr[2], register
423 eval_binop [:nor, 0, expr[1]], register
426 eval_binop expr, register
428 raise "Not a magic word: #{op}"
434 # Export symbols from the current section
436 symbols.each { |sym| emit ".globl #{sym}\n" }
439 # Add a function to the current section
440 def function formals, *code
441 nlocals = count_locals code
442 begin_function formals, nlocals
443 code.each { |action| add section, action }
447 # Load byte from _base_ + _offset_ into _register_
448 def get_byte base, offset, register
449 # If base is an integer, but offset isn't, swap them
450 if !integer?(offset) && integer?(base)
451 base, offset = [offset, base]
455 base_reg = load_value base
456 emit "lb #{register}, #{offset}(#{base_reg})\n"
458 eval_binop [:add, base, offset], @TEMPORARY
459 emit "lb #{register}, 0(#{@TEMPORARY})\n"
463 # Load word from _base_ + _offset_ * _@WORDSIZE_ into _register_
464 def get_word base, offset, register
466 base_reg = load_value base
467 emit "lw #{register}, #{offset * @WORDSIZE}(#{base_reg})\n"
469 offset_reg = @TEMPORARIES.pop
471 load_value_into_register offset, offset_reg
472 base_reg = load_value base
473 emit "sll #{offset_reg}, #{offset_reg}, 2\n" # multiply by @WORDSIZE
474 emit "add #{@TEMPORARY}, #{base_reg}, #{offset_reg}\n"
475 emit "lw #{register}, 0(#{@TEMPORARY})\n"
477 @TEMPORARIES.push offset_reg
482 # Test if a symbol refers to a global
484 symbol?(symbol) && @environment[symbol] == nil
493 # Start the false path of a conditional.
496 newlabel = @environment.gensym
497 emit "j #{newlabel}\n"
499 label = @if_labels.pop
501 @if_labels.push newlabel
504 # Test if x is equal to y
506 common_if :ifeq, x, y
509 # Test if x is greater than or equal to y
511 common_if :ifge, x, y
514 # Test if x is strictly greater than y
516 common_if :ifgt, x, y
519 # Test if x is less than or equal to y
521 common_if :ifle, x, y
524 # Test if x is strictly less than y
526 common_if :iflt, x, y
529 # Test if x different from y
531 common_if :ifne, x, y
534 # Import labels into the current section
536 # Record imported labels in @imports
537 symbols.each { |sym| @imports[sym] = sym }
540 # Test if a value is an integer
542 value.kind_of? Integer
550 # Introduce a new local variable
551 def let symbol, *expr
552 emit "# let #{symbol} #{expr.join ' '}\n"
553 n = @environment.locals
554 @environment.add_local symbol
556 # Extend stack frame, if necessary.
557 # This should never be necessary, because we allocate enough
558 # space in the top-level function or block.
559 if @environment.locals > max_locals
560 $stderr.puts "WARNING: Stack frame too small. Possible BUG"
561 $stderr.puts "Frame size #{@frame_size}, max locals #{max_locals}" +
562 ", actual locals #{@environment.locals}"
563 # Extend frame by 8 bytes, to keep $sp a multiple of 8
564 emit "addiu $sp, $sp, -8\n"
565 @frame_size = @frame_size + 8
568 register = local_register n
569 ref = local_reference n
571 # We will use a register to store the value
572 # Save current value of register
573 emit "sw #{register}, #{ref}\n"
575 eval_expr expr, register
577 # We will use the stack to store the value
578 eval_expr expr, @TEMPORARY
579 emit "sw #{@TEMPORARY}, #{ref}\n"
583 # Load the value at the given address.
584 def load_at address, register = @TEMPORARY
585 load_value_into_register address, register
586 emit "lw #{register}, 0(#{register})\n"
590 # Load a value into a register.
591 # Returns the name of the register.
592 # If the value was already in a register, the name of that
593 # register is returned.
594 # Else, the value is loaded into a register and the name of
595 # that register is returned. The register to use in that case
596 # may be specified using the optional second argument.
597 def load_value x, register = @TEMPORARY
601 if x >= -32768 && x <= 32767
602 emit "addiu #{register}, $0, #{x}\n"
605 emit "lui #{register}, #{x >> 16}\n"
606 emit "ori #{register}, #{register}, #{x & 0xffff}\n"
610 binding = @environment[x]
614 emit "lw #{register}, #{arg_reference binding[1]}\n"
619 return local_register(n)
621 emit "lw #{register}, #{local_reference n}\n"
625 raise "Don't know how to load #{x.inspect}"
629 emit "lui #{register}, %hi(#{x})\n"
630 emit "addiu #{register}, %lo(#{x})\n"
634 load_at x[1], register
636 raise "Don't know how to load #{x.inspect}"
640 # Load a value into a specific register
641 def load_value_into_register x, register
642 reg = load_value x, register
644 emit "move #{register}, #{reg}\n"
648 # Return the offset from $sp at which the nth (0-based) local variable is
649 # stored. For register locals this is the offset at which the saved
650 # value (from the calling function) is stored.
652 @frame_size - @INITIAL_FRAME_SIZE - (n + 1) * @WORDSIZE
655 # Return an $sp-relative reference for the nth (0-based) local
656 def local_reference n
657 "#{local_offset n}($sp)"
660 # Return the register in which the nth local (0-based) is stored, or
661 # nil if not stored in a register
664 "$#{@REGISTER_LOCAL_BASE + n}"
670 # Compute the maximum number of locals that would fit in the current
673 (@frame_size - @INITIAL_FRAME_SIZE) / @WORDSIZE
676 # Calculate the number of stack arguments,
677 # given the total number of arguments.
678 def number_of_stack_arguments n
679 [0, n - @NREGISTER_ARGS].max
682 # Returns true if the nth (0-based) argument is stored in a register
687 # Returns true if the nth (0-based) local is stored in a register
688 def register_local? n
689 n < @NREGISTER_LOCALS
692 # Return a from a function.
694 # _words_ may contain an expression to be evaluated. The result
695 # of the evaluation is returned from the function.
697 emit "# return #{words.join ' '}\n"
698 # Compute return value and store it in @RETURN
699 eval_expr(words, @RETURN) unless words.empty?
701 goto @function_end_label
704 # Set a variable to the result of evaluating an expression
705 def set symbol, *expr
706 emit "# set #{symbol} #{expr.join ' '}\n"
708 x = @environment[symbol]
710 raise "Cannot change value of constant #{symbol}"
714 register = local_register x[1]
721 eval_expr expr, register
725 ref = local_reference x[1]
727 ref = arg_reference x[1]
729 raise "??? #{sym} is neither a local nor an argument"
731 eval_expr expr, @TEMPORARY
732 emit "sw #{@TEMPORARY}, #{ref}\n"
736 # Set the byte at _base_ + _offset_ to _value_
737 def set_byte base, offset, value
738 emit "# set-byte #{base} #{offset} #{value}\n"
739 # If base is an integer, but offset isn't, swap them
740 if !integer?(offset) && integer?(base)
741 base, offset = [offset, base]
744 value_reg = @TEMPORARIES.pop
745 load_value_into_register value, value_reg
748 base_reg = load_value base
749 emit "sb #{value_reg}, #{offset}(#{base_reg})\n"
751 eval_binop [:add, base, offset], @TEMPORARY
752 emit "sb #{value_reg}, 0(#{@TEMPORARY})\n"
755 @TEMPORARIES.push value_reg
759 # Set the word at _base_ + _offset_ * +@WORDSIZE+ to _value_
760 def set_word base, offset, value
761 emit "# set-word #{base} #{offset} #{value}\n"
763 value_reg = @TEMPORARIES.pop
764 load_value_into_register value, value_reg
767 base_reg = load_value base
768 emit "sw #{value_reg}, #{offset * @WORDSIZE}(#{base_reg})\n"
770 offset_reg = @TEMPORARIES.pop
772 load_value_into_register offset, offset_reg
773 base_reg = load_value base
774 emit "sll #{offset_reg}, #{offset_reg}, 2\n"
775 emit "add #{@TEMPORARY}, #{base_reg}, #{offset_reg}\n"
776 emit "sw #{value_reg}, 0(#{@TEMPORARY})\n"
778 @TEMPORARIES.push offset_reg
782 @TEMPORARIES.push value_reg
786 # Define a string with the given value
789 value.each_byte do |b|
792 elsif b >= 32 && b < 127 && b != 34
795 code << sprintf("\\%03o", b)
798 emit ".ascii \"#{code}\"\n"
801 # Test if a value is a symbol
803 value.kind_of? Symbol
806 # Test if op is a symmetric binary operation (i.e. it will yield the
807 # same result if the order of its source operands is changed).
808 def symmetric_binop? op
809 [:add, :and, :mul, :or, :xor].member? op
812 # Call a function, re-using the current call frame if possible.
813 def tail_call func, *args
814 emit "# tail-call #{func} #{args.join ' '}\n"
816 # Compute number of stack arguments
817 nstackargs = number_of_stack_arguments args.length
818 # If we need more stack arguments than we have now,
819 # perform a normal call and return
820 if nstackargs > number_of_stack_arguments(@environment.args)
821 emit "# Not enough space for proper tail call; using regular call\n"
822 ret :call, func, *args
827 # Back up any stack arguments we will need after we overwrite them
828 temporaries = @TEMPORARIES.dup
829 nlocals = @environment.locals
830 (@NREGISTER_ARGS...args.length).each do |i|
831 x = @environment[args[i]]
832 if x && x[0] == :arg && x[1] >= @NREGISTER_ARGS && x[1] < i
833 # args[i] is a stack arg, but has an index < i
834 # That means that, by the time we get to pass arg[i] to the called
835 # function, it will have been overwritten. So we make a backup.
836 if temporaries.empty?
837 # Oh dear, we're out of temporaries.
838 # Store the value in the current stack frame, extending it
840 if nlocals >= max_locals
841 emit "addiu $sp, -8\n"
842 @frame_size = @frame_size + 8
844 reg = load_value args[i]
845 emit "sw #{reg}, #{local_reference nlocals}\n"
846 args[i] = [:local, nlocals]
847 nlocals = nlocals + 1
849 # Load the argument into a temporary
850 reg = temporaries.shift
851 load_value_into_register args[i], reg
852 # Update args[i] so we know how to get to it later
853 args[i] = [:reg, reg]
858 # Set stack arguments
859 (@NREGISTER_ARGS...args.length).each do |i|
864 if arg.kind_of? Array
865 # Special cases, created above
870 emit "lw #{reg}, #{local_reference arg[1]}\n"
873 load_value_into_register arg, reg
876 # Store value in designated place
877 emit "sw #{reg}, #{arg_reference i}\n"
880 # Set register arguments
881 [@NREGISTER_ARGS,args.length].min.times do |i|
883 load_value_into_register args[i], reg
886 # Restore saved registers
887 [@NREGISTER_LOCALS, @environment.locals].min.times do |n|
888 emit "lw #{local_register n}, #{local_reference n}\n"
890 # Restore return address
891 emit "lw $31, #{@frame_size - @WORDSIZE}($sp)\n"
893 # Adjust stack pointer
894 emit "addiu $sp, #{@frame_size}\n"
896 # Load function address
898 if @imports.has_key? func
899 # Load address from global offset table
900 emit "lw #{@FUNCTION}, %call16(#{func})(#{@GOT})\n"
902 # Assume label defined in this module
903 emit "lui #{@FUNCTION}, %hi(#{func})\n"
904 emit "addiu #{@FUNCTION}, %lo(#{func})\n"
908 load_value_into_register func, "#{@FUNCTION}"
912 emit "jr #{@FUNCTION}\n"
917 # Define a word with the given value
919 emit ".int #{value}\n"
922 # Write generated code to the given IO object.
925 @sections.each do |section,code|
927 io.puts ".section #{section.to_s}"
936 # Register class for big endian MIPS
937 Voodoo::CodeGenerator.register_generator MIPSGasGenerator,
938 :architecture => :mips,
942 # Register class for little endian MIPS
943 Voodoo::CodeGenerator.register_generator MIPSGasGenerator,
944 :architecture => :mipsel,