2 # = MIPS GNU Assembler Code Generator
4 # The MIPS code generator generates i386 assembly code for use with
7 # == Calling Convention
9 # The first four arguments are passed in the registers $4 through $7.
10 # Any additional arguments are passed on the stack, starting at
11 # $sp + 16. Words $sp through $sp + 12 will be available for the called
12 # function to use. $sp will always be a multiple of 8.
14 # The return address for the called function is passed in $31.
16 # When performing a position-independent call, the address of the called
17 # function is passed in $25.
19 # The called function will store its return value in $2.
21 # The called function is required to preserve the values of registers
22 # $16 through $23 and register $30.
24 # This calling convention is compatible with the System V ELF ABI.
28 # Call frames have the following layout:
30 # When a function is called, it receives a stack frame that looks like
41 # empty0 <-- $sp points here
43 # The function prologue of functions generated by this code generator
44 # creates activiation frames that look as follows:
56 # pointer to Global Offset Table
63 # padding <-- $sp points here
67 # The four empty slots provided by the caller are used to store
68 # arguments 0 through 3 (originally passed in $4 through $7),
71 # The stack frame created below the four slots provided by the caller
72 # contains the following data, in order:
74 # - Saved return address (originally passed in $31), if necessary.
76 # - Saved pointer to global offset table (computed from $25), if necessary.
78 # - Saved values of caller's locals 0 through 7
79 # (originally in $16 through $23), if necessary.
81 # - Values of our locals > 8, if necessary.
83 # - Padding to align $sp on a multiple of 8, if necessary.
86 # In accordance with the System V ELF ABI for MIPS, the pointer to
87 # the global offset table is calculated as follows:
89 # $gp = _gp_disp + $25
91 # where $gp is the register to store the pointer, $25 is the register
92 # holding the address of the called function, and _gp_disp is the
93 # offset between the beginning of the function and the global offset table.
95 class MIPSGasGenerator < CommonCodeGenerator
99 @DATA_ALIGNMENT = @WORDSIZE
100 @FUNCTION_ALIGNMENT = @WORDSIZE
102 @INITIAL_FRAME_SIZE = 2 * @WORDSIZE
103 @REGISTER_ARG_BASE = 4
105 @REGISTER_LOCAL_BASE = 16
106 @NREGISTER_LOCALS = 8
111 @TEMPORARIES = [:$8, :$9, :$10, :$11, :$12, :$13, :$14, :$15]
112 @function_end_label = nil
116 @output_file_suffix = '.s'
119 def align alignment = nil
121 # Get default alignment
124 alignment = @CODE_ALIGNMENT
126 alignment = @DATA_ALIGNMENT
128 alignment = @FUNCTION_ALIGNMENT
130 # Use data alignment as default
131 alignment = @DATA_ALIGNMENT
134 emit ".align #{alignment}\n" unless alignment == 0
137 # Return the offset from $sp at which the nth (0-based) argument is
140 @frame_size + n * @WORDSIZE
143 # Return an $sp-relative reference for the nth (0-based) argument
145 "#{arg_offset n}($sp)"
148 # Return the register in which the nth (0-based) argument is stored, or
149 # nil if not stored in a register
152 "$#{@REGISTER_ARG_BASE + n}"
158 # Test if op is a binary operation
159 def assymetric_binop? op
160 [:div, :mod, :sub].member?(op)
163 # Emit function prologue and declare _formals_ as function arguments
164 def begin_function formals, nlocals
165 if @environment != @top_level
166 raise "Cannot begin function when already in a function"
169 emit "# function #{formals.join ' '}\n"
170 environment = Environment.new @environment
171 environment.add_args formals
172 @environment = environment
173 @function_end_label = gensym
174 emit_function_prologue formals, nlocals
177 # Test if op is a binary operation
179 assymetric_binop?(op) || symmetric_binop?(op)
182 # Define a byte with the given value
184 emit ".byte #{value}\n"
189 emit "# call #{func} #{args.join ' '}\n"
190 # Grow the stack frame, subject to 3 conditions:
191 # 1. There must be enough space to hold all arguments
192 # 2. $sp must remain a multiple of 8
193 # 3. We must provide space for at least 4 words
194 increment = ([args.length, 4].max * @WORDSIZE + 7) / 8 * 8
195 emit "addiu $sp, $sp, -#{increment}\n"
196 @frame_size = @frame_size + increment
198 # Put arguments in the right places
200 while n < args.length
201 if n < @NREGISTER_ARGS
202 # Put arguments up to @NREGISTER_ARGS in registers
203 load_value_into_register args[n], arg_register(n)
205 # Put other arguments on the stack
206 load_value_into_register args[n], @TEMPORARY
207 emit "sw #{@TEMPORARY}, #{n * @WORDSIZE}($sp)\n"
212 # Load function address
214 if @imports.has_key? func
215 # Load address from global offset table
216 emit "lw #{@FUNCTION}, %call16(#{func})(#{@GOT})\n"
218 # Assume label defined in this module
219 emit "lui #{@FUNCTION}, %hi(#{func})\n"
220 emit "addiu #{@FUNCTION}, %lo(#{func})\n"
224 load_value_into_register func, "#{@FUNCTION}"
228 emit "jalr #{@FUNCTION}\n"
232 # Restore original stack frame
233 emit "addiu $sp, $sp, #{increment}\n"
234 @frame_size = @frame_size - increment
237 emit "lw #{@GOT}, #{@frame_size - 2 * @WORDSIZE}($sp)\n"
240 # Start a conditional using the specified branch instruction
241 # after the comparison.
242 def common_if comp, x, y = nil
243 emit "# #{comp} #{x} #{y}\n"
245 temporaries = @TEMPORARIES.dup
246 xreg = load_value x, temporaries[0]
247 temporaries.delete xreg
248 yreg = load_value y, temporaries[0]
249 temporaries.delete yreg
251 falselabel = @environment.gensym
252 @if_labels.push falselabel
256 emit "bne #{xreg}, #{yreg}, #{falselabel}\n"
258 emit "slt #{@TEMPORARY}, #{xreg}, #{yreg}\n"
259 emit "bne #{@TEMPORARY}, $0, #{falselabel}\n"
261 emit "slt #{@TEMPORARY}, #{yreg}, #{xreg}\n"
262 emit "beq #{@TEMPORARY}, $0, #{falselabel}\n"
264 emit "slt #{@TEMPORARY}, #{yreg}, #{xreg}\n"
265 emit "bne #{@TEMPORARY}, $0, #{falselabel}\n"
267 emit "slt #{@TEMPORARY}, #{xreg}, #{yreg}\n"
268 emit "beq #{@TEMPORARY}, $0, #{falselabel}\n"
270 emit "beq #{xreg}, #{yreg}, #{falselabel}\n"
272 raise "Unknown conditional: #{comp}"
277 # Emit function prologue.
278 def emit_function_prologue formals = [], nlocals = 0
279 # Calculate new value for $gp
280 emit "lui #{@GOT}, %hi(_gp_disp)\n"
281 emit "addiu #{@GOT}, %lo(_gp_disp)\n"
282 emit "addu #{@GOT}, #{@GOT}, #{@FUNCTION}\n"
284 # Save parameters 0 .. 3 in the stack space that the caller
285 # should have allocated for them.
286 [@NREGISTER_ARGS, formals.length].min.times do |n|
287 emit "sw $#{n + @REGISTER_ARG_BASE}, #{n * @WORDSIZE}($29)\n"
290 # Create initial frame
291 @frame_size = (@INITIAL_FRAME_SIZE + nlocals * @WORDSIZE + 7) / 8 * 8
292 emit "addiu $sp, $sp, -#{@frame_size}\n"
294 # Save return address
295 emit "sw $31, #{@frame_size - @WORDSIZE}($sp)\n"
298 emit "sw #{@GOT}, #{@frame_size - 2 * @WORDSIZE}($sp)\n"
301 # End a function body
303 if @environment == @top_level
304 raise "Cannot end function when not in a function"
307 emit "# function epilogue\n"
309 label @function_end_label
310 # Restore saved locals
311 [@NREGISTER_LOCALS, @environment.locals].min.times do |n|
312 emit "lw #{local_register n}, #{local_reference n}\n"
314 # Load return address
315 emit "lw $31, #{@frame_size - @WORDSIZE}($sp)\n"
316 # Restore stack pointer
317 emit "addiu $sp, $sp, #{@frame_size}\n"
322 emit "# end function\n\n"
324 @function_end_label = nil
325 @environment = @top_level
330 label = @if_labels.pop
334 # Evaluate the binary operation expr and store the result in register
335 def eval_binop expr, register
336 temporaries = @TEMPORARIES.dup
337 x = load_value expr[1], temporaries[0]
339 y = load_value expr[2], temporaries[0]
344 emit "div $0, #{x}, #{y}\n"
345 emit "mflo #{register}\n"
347 emit "div $0, #{x}, #{y}\n"
348 emit "mfhi #{register}\n"
350 emit "mult #{x}, #{y}\n"
351 emit "mflo #{register}\n"
353 emit "#{expr[0]} #{register}, #{x}, #{y}\n"
357 # Evaluate the expression expr and store the result in register
358 def eval_expr expr, register
361 load_value_into_register expr[0], register
363 # Evaluate expression
368 emit "move #{register}, #{@RETURN}\n" if register != @RETURN
370 get_byte expr[1], expr[2], register
372 get_word expr[1], expr[2], register
374 eval_binop [:nor, 0, expr[1]], register
377 eval_binop expr, register
379 raise "Not a magic word: #{op}"
385 # Export symbols from the current section
387 symbols.each { |sym| emit ".globl #{sym}\n" }
390 # Add a function to the current section
391 def function formals, *code
392 # Set nlocals to the number of local variables in the function
394 code.each { |action| nlocals = nlocals + 1 if action[0] == :let }
395 begin_function formals, nlocals
396 code.each { |action| add section, action }
400 # Load byte from _base_ + _offset_ into _register_
401 def get_byte base, offset, register
402 # If base is an integer, but offset isn't, swap them
403 if !integer?(offset) && integer?(base)
404 base, offset = [offset, base]
408 base_reg = load_value base
409 emit "lb #{register}, #{offset}(#{base_reg})\n"
411 eval_binop [:add, base, offset], @TEMPORARY
412 emit "lb #{register}, 0(#{@TEMPORARY})\n"
416 # Load word from _base_ + _offset_ * _@WORDSIZE_ into _register_
417 def get_word base, offset, register
419 base_reg = load_value base
420 emit "lw #{register}, #{offset * @WORDSIZE}(#{base_reg})\n"
422 offset_reg = @TEMPORARIES.pop
424 load_value_into_register offset, offset_reg
425 base_reg = load_value base
426 emit "sll #{offset_reg}, #{offset_reg}, 2\n" # multiply by @WORDSIZE
427 emit "add #{@TEMPORARY}, #{base_reg}, #{offset_reg}\n"
428 emit "lw #{register}, 0(#{@TEMPORARY})\n"
430 @TEMPORARIES.push offset_reg
435 # Test if a symbol refers to a global
437 symbol?(symbol) && @environment[symbol] == nil
446 # Start the false path of a conditional.
449 newlabel = @environment.gensym
450 emit "j #{newlabel}\n"
452 label = @if_labels.pop
454 @if_labels.push newlabel
457 # Test if x is equal to y
459 common_if :ifeq, x, y
462 # Test if x is greater than or equal to y
464 common_if :ifge, x, y
467 # Test if x is strictly greater than y
469 common_if :ifgt, x, y
472 # Test if x is less than or equal to y
474 common_if :ifle, x, y
477 # Test if x is strictly less than y
479 common_if :iflt, x, y
482 # Test if x different from y
484 common_if :ifne, x, y
487 # Import labels into the current section
489 # Record imported labels in @imports
490 symbols.each { |sym| @imports[sym] = sym }
493 # Test if a value is an integer
495 value.kind_of? Integer
503 # Introduce a new local variable
504 def let symbol, *expr
505 emit "# let #{symbol} #{expr.join ' '}\n"
506 n = @environment.locals
507 @environment.add_local symbol
509 # Extend stack frame, if necessary
510 if @environment.locals > max_locals
511 $stderr.puts "WARNING: Stack frame too small. Possible BUG"
512 # Extend frame by 8 bytes, to keep $sp a multiple of 8
513 emit "addiu $sp, $sp, -8\n"
514 @frame_size = @frame_size + 8
517 register = local_register n
518 ref = local_reference n
520 # Save current value of register
521 emit "sw #{register}, #{ref}\n"
523 eval_expr expr, register
525 eval_expr expr, @TEMPORARY
526 emit "sw #{@TEMPORARY}, #{ref}\n"
530 # Load a value into a register.
531 # Returns the name of the register.
532 # If the value was already in a register, the name of that
533 # register is returned.
534 # Else, the value is loaded into a register and the name of
535 # that register is returned. The register to use in that case
536 # may be specified using the optional second argument.
537 def load_value x, register = @TEMPORARY
541 if x >= -32768 && x <= 32767
542 emit "addiu #{register}, $0, #{x}\n"
545 emit "lui #{register}, #{x >> 16}\n"
546 emit "ori #{register}, #{register}, #{x & 0xffff}\n"
550 binding = @environment[x]
554 emit "lw #{register}, #{arg_reference binding[1]}\n"
559 return local_register(n)
561 emit "lw #{register}, #{local_reference n}\n"
565 raise "Don't know how to load #{x.inspect}"
569 emit "lui #{register}, %hi(#{x})\n"
570 emit "addiu #{register}, %lo(#{x})\n"
574 raise "Don't know how to load #{x.inspect}"
578 # Load a value into a specific register
579 def load_value_into_register x, register
580 reg = load_value x, register
582 emit "move #{register}, #{reg}\n"
586 # Return the offset from $sp at which the nth (0-based) local variable is
587 # stored. For register locals this is the offset at which the saved
588 # value (from the calling function) is stored.
590 @frame_size - @INITIAL_FRAME_SIZE - (n + 1) * @WORDSIZE
593 # Return an $sp-relative reference for the nth (0-based) local
594 def local_reference n
595 "#{local_offset n}($sp)"
598 # Return the register in which the nth local (0-based) is stored, or
599 # nil if not stored in a register
602 "$#{@REGISTER_LOCAL_BASE + n}"
608 # Compute the maximum number of locals that would fit in the current
611 (@frame_size - @INITIAL_FRAME_SIZE) / @WORDSIZE
614 # Calculate the number of stack arguments,
615 # given the total number of arguments.
616 def number_of_stack_arguments n
617 [0, n - @NREGISTER_ARGS].max
620 # Returns true if the nth (0-based) argument is stored in a register
625 # Returns true if the nth (0-based) local is stored in a register
626 def register_local? n
627 n < @NREGISTER_LOCALS
630 # Return a from a function.
632 # _words_ may contain an expression to be evaluated. The result
633 # of the evaluation is returned from the function.
635 emit "# return #{words.join ' '}\n"
636 # Compute return value and store it in @RETURN
637 eval_expr words, @RETURN
639 goto @function_end_label
642 # Set a variable to the result of evaluating an expression
643 def set symbol, *expr
644 emit "# set #{symbol} #{expr.join ' '}\n"
646 x = @environment[symbol]
648 raise "Cannot change value of constant #{symbol}"
652 register = local_register x[1]
659 eval_expr expr, register
663 ref = local_reference x[1]
665 ref = arg_reference x[1]
667 raise "??? #{sym} is neither a local nor an argument"
669 eval_expr expr, @TEMPORARY
670 emit "sw #{@TEMPORARY}, #{ref}\n"
674 # Set the byte at _base_ + _offset_ to _value_
675 def set_byte base, offset, value
676 emit "# set-byte #{base} #{offset} #{value}\n"
677 # If base is an integer, but offset isn't, swap them
678 if !integer?(offset) && integer?(base)
679 base, offset = [offset, base]
682 value_reg = @TEMPORARIES.pop
683 load_value_into_register value, value_reg
686 base_reg = load_value base
687 emit "sb #{value_reg}, #{offset}(#{base_reg})\n"
689 eval_binop [:add, base, offset], @TEMPORARY
690 emit "sb #{value_reg}, 0(#{@TEMPORARY})\n"
693 @TEMPORARIES.push value_reg
697 # Set the word at _base_ + _offset_ * +@WORDSIZE+ to _value_
698 def set_word base, offset, value
699 emit "# set-word #{base} #{offset} #{value}\n"
701 value_reg = @TEMPORARIES.pop
702 load_value_into_register value, value_reg
705 base_reg = load_value base
706 emit "sw #{value_reg}, #{offset * @WORDSIZE}(#{base_reg})\n"
708 offset_reg = @TEMPORARIES.pop
710 load_value_into_register offset, offset_reg
711 base_reg = load_value base
712 emit "sll #{offset_reg}, #{offset_reg}, 2\n"
713 emit "add #{@TEMPORARY}, #{base_reg}, #{offset_reg}\n"
714 emit "sw #{value_reg}, 0(#{@TEMPORARY})\n"
716 @TEMPORARIES.push offset_reg
720 @TEMPORARIES.push value_reg
724 # Define a string with the given value
727 value.each_byte do |b|
728 if b >= 32 && b < 128
731 code << sprintf("\\%03o", b)
734 emit ".ascii \"#{code}\"\n"
737 # Test if a value is a symbol
739 value.kind_of? Symbol
742 # Test if op is a symmetric binary operation (i.e. it will yield the
743 # same result if the order of its source operands is changed).
744 def symmetric_binop? op
745 [:add, :and, :mul, :or, :xor].member? op
748 # Call a function, re-using the current call frame if possible.
749 def tail_call func, *args
750 emit "# tail-call #{func} #{args.join ' '}\n"
752 # Compute number of stack arguments
753 nstackargs = number_of_stack_arguments args.length
754 # If we need more stack arguments than we have now,
755 # perform a normal call and return
756 if nstackargs > number_of_stack_arguments(@environment.args)
757 emit "# Not enough space for proper tail call; using regular call\n"
758 ret :call, func, *args
763 # Back up any stack arguments we will need after we overwrite them
764 temporaries = @TEMPORARIES.dup
765 nlocals = @environment.locals
766 (@NREGISTER_ARGS...args.length).each do |i|
767 x = @environment[args[i]]
768 if x && x[0] == :arg && x[1] >= @NREGISTER_ARGS && x[1] < i
769 # args[i] is a stack arg, but has an index < i
770 # That means that, by the time we get to pass arg[i] to the called
771 # function, it will have been overwritten. So we make a backup.
772 if temporaries.empty?
773 # Oh dear, we're out of temporaries.
774 # Store the value in the current stack frame, extending it
776 if nlocals >= max_locals
777 emit "addiu $sp, -8\n"
778 @frame_size = @frame_size + 8
780 reg = load_value args[i]
781 emit "sw #{reg}, #{local_reference nlocals}\n"
782 args[i] = [:local, nlocals]
783 nlocals = nlocals + 1
785 # Load the argument into a temporary
786 reg = temporaries.shift
787 load_value_into_register args[i], reg
788 # Update args[i] so we know how to get to it later
789 args[i] = [:reg, reg]
794 # Set stack arguments
795 (@NREGISTER_ARGS...args.length).each do |i|
800 if arg.kind_of? Array
801 # Special cases, created above
806 emit "lw #{reg}, #{local_reference arg[1]}\n"
809 load_value_into_register arg, reg
812 # Store value in designated place
813 emit "sw #{reg}, #{arg_reference i}\n"
816 # Set register arguments
817 [@NREGISTER_ARGS,args.length].min.times do |i|
819 load_value_into_register args[i], reg
822 # Restore saved registers
823 [@NREGISTER_LOCALS, @environment.locals].min.times do |n|
824 emit "lw #{local_register n}, #{local_reference n}\n"
826 # Restore return address
827 emit "lw $31, #{@frame_size - @WORDSIZE}($sp)\n"
829 # Adjust stack pointer
830 emit "addiu $sp, #{@frame_size}\n"
832 # Load function address
834 if @imports.has_key? func
835 # Load address from global offset table
836 emit "lw #{@FUNCTION}, %call16(#{func})(#{@GOT})\n"
838 # Assume label defined in this module
839 emit "lui #{@FUNCTION}, %hi(#{func})\n"
840 emit "addiu #{@FUNCTION}, %lo(#{func})\n"
844 load_value_into_register func, "#{@FUNCTION}"
848 emit "jr #{@FUNCTION}\n"
853 # Define a word with the given value
855 emit ".int #{value}\n"
858 # Write generated code to the given IO object.
861 @sections.each do |section,code|
863 io.puts ".section #{section.to_s}"
872 # Register class for big endian MIPS
873 Voodoo::CodeGenerator.register_generator MIPSGasGenerator,
874 :architecture => :mips,
878 # Register class for little endian MIPS
879 Voodoo::CodeGenerator.register_generator MIPSGasGenerator,
880 :architecture => :mipsel,