1 require 'voodoo/generators/common_code_generator'
5 # = MIPS GNU Assembler Code Generator
7 # The MIPS code generator generates assembly code for use with
10 # == Calling Convention
12 # The first four arguments are passed in the registers $4 through $7.
13 # Any additional arguments are passed on the stack, starting at
14 # $sp + 16. Words $sp through $sp + 12 will be available for the called
15 # function to use. $sp will always be a multiple of 8.
17 # The return address for the called function is passed in $31.
19 # When performing a position-independent call, the address of the called
20 # function is passed in $25.
22 # The called function will store its return value in $2.
24 # The called function is required to preserve the values of registers
25 # $16 through $23 and register $30.
27 # This calling convention is compatible with the System V ELF ABI.
31 # Call frames have the following layout:
33 # When a function is called, it receives a stack frame that looks like
44 # empty0 <-- $sp points here
46 # The function prologue of functions generated by this code generator
47 # creates activation frames that look as follows:
57 # arg0 <-- $fp points here
59 # pointer to Global Offset Table
67 # padding <-- $sp points here
71 # The four empty slots provided by the caller are used to store
72 # arguments 0 through 3 (originally passed in $4 through $7),
75 # The stack frame created below the four slots provided by the caller
76 # contains the following data, in order:
78 # - Saved return address (originally passed in $31), if necessary.
80 # - Saved pointer to global offset table (computed from $25), if necessary.
82 # - Saved value of $fp, if necessary.
84 # - Saved values of caller's locals 0 through 7
85 # (originally in $16 through $23), if necessary.
87 # - Values of our locals > 8, if necessary.
89 # - Padding to align $sp on a multiple of 8, if necessary.
92 # In accordance with the System V ELF ABI for MIPS, the pointer to
93 # the global offset table is calculated as follows:
95 # $gp = _gp_disp + $25
97 # where $gp is the register to store the pointer, $25 is the register
98 # holding the address of the called function, and _gp_disp is the
99 # offset between the beginning of the function and the global offset table.
101 # Callee-Save Registers
103 # Registers $16 through $23 and $28 through $30 are callee-save.
105 # All other registers are caller-save.
107 class MIPSGasGenerator < CommonCodeGenerator
108 def initialize params
110 @WORDSIZE = 1 << @WORDSIZE_BITS
112 @DATA_ALIGNMENT = @WORDSIZE
113 @STACK_ALIGNMENT_BITS = 3
114 @STACK_ALIGNMENT = 1 << @STACK_ALIGNMENT_BITS
115 @FP_OFFSET = -3 * @WORDSIZE
116 @FUNCTION_ALIGNMENT = @WORDSIZE
117 @GP_OFFSET = -2 * @WORDSIZE
118 @INITIAL_FRAME_SIZE = 4 * @WORDSIZE
119 @REGISTER_ARG_BASE = 4
121 @LOCAL_REGISTERS = [:'$16', :'$17', :'$18', :'$19',
122 :'$20', :'$21', :'$22', :'$23']
123 @LOCAL_REGISTERS_SET = Set.new @LOCAL_REGISTERS
124 @REGISTER_LOCAL_BASE = 16
125 @NREGISTER_LOCALS = @LOCAL_REGISTERS.length
126 @RA_OFFSET = -@WORDSIZE
131 @SAVE_FRAME_REGISTERS = [:'$16', :'$17', :'$18', :'$19', :'$20',
132 :'$21', :'$22', :'$23', :'$28', :'$29',
134 @SAVED_FRAME_LAYOUT = {}
135 @SAVE_FRAME_REGISTERS.each_with_index { |r,i| @SAVED_FRAME_LAYOUT[r] = i }
136 @TEMPORARIES = [:'$8', :'$9', :'$10', :'$11',
137 :'$12', :'$13', :'$14', :'$15']
138 @function_end_label = nil
142 @output_file_suffix = '.s'
143 @saved_registers = []
145 :'bits-per-word' => '32',
146 :'bytes-per-word' => '4'
149 @features[:'byte-order'] = 'big-endian'
151 @features[:'byte-order'] = 'little-endian'
153 raise ArgumentError.new("#{self.class} does not support " +
154 "architecture #{@architecture}")
158 # Adds immediate to source and stores the result in target.
159 def addiu target, source, immediate, temporary = @TEMPORARY
160 if immediate >= -32768 && immediate < 32767
161 emit "addiu #{target}, #{source}, #{immediate}\n"
163 emit "lui #{target}, #{(immediate >> 16) & 0xffff}\n"
164 emit "ori #{target}, #{target}, #{immediate & 0xffff}\n"
166 addiu temporary, "$0", immediate
167 emit "addu #{target}, #{source}, #{temporary}\n"
171 # Return the offset from the frame base at which the nth argument is
177 # Returns an $fp-relative reference for the nth (0-based) argument.
179 offset_reference(arg_offset(n))
182 # Returns the register in which the nth (0-based) argument is stored, or
183 # nil if not stored in a register.
186 "$#{@REGISTER_ARG_BASE + n}"
192 def auto_bytes n, register
193 if n.kind_of? Integer
194 # Maintain stack alignment
195 n = (n + @STACK_ALIGNMENT - 1) / @STACK_ALIGNMENT * @STACK_ALIGNMENT
198 temporary = @TEMPORARIES.pop
200 load_value_into_register n, register
201 emit "addi #{register}, #{@STACK_ALIGNMENT - 1}\n"
202 emit "li #{temporary}, -#{@STACK_ALIGNMENT}\n"
203 emit "and #{register}, #{register}, #{temporary}\n"
204 emit "sub $sp, #{register}\n"
206 @TEMPORARIES.push temporary
209 emit "move #{register}, $sp\n" unless register == "$sp"
212 def auto_words n, register
213 if n.kind_of? Integer
214 auto_bytes n * @WORDSIZE, register
216 load_value_into_register n, register
217 if @STACK_ALIGNMENT_BITS > @WORDSIZE_BITS
218 bits = @STACK_ALIGNMENT_BITS - @WORDSIZE_BITS
219 emit "addi #{register}, #{(1 << bits) - 1}\n"
220 emit "srl #{register}, #{register}, #{bits}\n"
221 emit "sll #{register}, #{register}, #{@STACK_ALIGNMENT_BITS}\n"
223 emit "sll #{register}, #{register}, #{@WORDSIZE_BITS}\n"
225 emit "sub $sp, $sp, #{register}\n"
226 emit "move #{register}, $sp\n" unless register == "$sp"
230 # Begins a new block.
231 def begin_block *code
232 # If we are at top-level, create a frame
233 if @environment == @top_level
234 create_frame count_locals(code)
236 environment = Environment.new @environment
237 @environment = environment
240 # Emits function prologue and declare _formals_ as function arguments.
241 def begin_function formals, nlocals
242 if @environment != @top_level
243 raise "Cannot begin function when already in a function"
246 environment = Environment.new @environment
248 formals.each {|x| environment.add_arg x, arg_offset(environment.args)}
249 @environment = environment
250 @function_end_label = gensym
251 emit_function_prologue formals, nlocals
254 # Defines a byte with the given value.
256 emit ".byte #{value}\n"
261 # Grow the stack frame, subject to 3 conditions:
262 # 1. There must be enough space to hold all arguments
263 # 2. $sp must remain a multiple of 8
264 # 3. We must provide space for at least 4 words
265 increment = ([args.length, 4].max * @WORDSIZE + 7) / 8 * 8
268 # Put arguments in the right places
270 while n < args.length
271 if n < @NREGISTER_ARGS
272 # Put arguments up to @NREGISTER_ARGS in registers
273 load_value_into_register args[n], arg_register(n)
275 # Put other arguments on the stack
276 load_value_into_register args[n], @TEMPORARY
277 emit "sw #{@TEMPORARY}, #{n * @WORDSIZE}($sp)\n"
282 # Load function address
284 @symbol_tracker.use func
285 if @imports.has_key? func
286 # Load address from global offset table
287 emit "lw #{@FUNCTION}, %call16(#{func})(#{@GOT})\n"
289 # Assume label defined in this module
290 emit "lui #{@FUNCTION}, %hi(#{func})\n"
291 emit "addiu #{@FUNCTION}, %lo(#{func})\n"
295 load_value_into_register func, "#{@FUNCTION}"
299 emit "jalr #{@FUNCTION}\n"
303 # Restore original stack frame
304 grow_stack -increment
307 emit "lw #{@GOT}, #{@GP_OFFSET}($fp)\n"
315 # Starts a conditional using the specified branch instruction
316 # after the comparison.
317 def common_if comp, x, y = nil
318 temporaries = @TEMPORARIES.dup
319 xreg = load_value x, temporaries[0]
320 temporaries.delete xreg
321 yreg = load_value y, temporaries[0]
322 temporaries.delete yreg
324 falselabel = @environment.gensym
325 @if_labels.push falselabel
329 emit "bne #{xreg}, #{yreg}, #{falselabel}\n"
331 emit "slt #{@TEMPORARY}, #{xreg}, #{yreg}\n"
332 emit "bne #{@TEMPORARY}, $0, #{falselabel}\n"
334 emit "slt #{@TEMPORARY}, #{yreg}, #{xreg}\n"
335 emit "beq #{@TEMPORARY}, $0, #{falselabel}\n"
337 emit "slt #{@TEMPORARY}, #{yreg}, #{xreg}\n"
338 emit "bne #{@TEMPORARY}, $0, #{falselabel}\n"
340 emit "slt #{@TEMPORARY}, #{xreg}, #{yreg}\n"
341 emit "beq #{@TEMPORARY}, $0, #{falselabel}\n"
343 emit "beq #{xreg}, #{yreg}, #{falselabel}\n"
345 raise "Unknown conditional: #{comp}"
350 # Creates an activation frame which can store nlocals local variables.
351 def create_frame nlocals
352 @frame_size = @INITIAL_FRAME_SIZE
354 @frame_size = @frame_size + (nlocals * @WORDSIZE + 7) / 8 * 8
356 grow_stack @frame_size
357 @saved_registers = []
360 def emit_align alignment
361 emit ".align #{alignment}\n"
364 # Exports symbols from the current section.
365 def emit_export *symbols
366 symbols.each { |sym| emit ".globl #{sym}\n" }
369 # Emits function prologue.
370 def emit_function_prologue formals = [], nlocals = 0
371 # Calculate new value for $gp
372 emit "lui #{@GOT}, %hi(_gp_disp)\n"
373 emit "addiu #{@GOT}, %lo(_gp_disp)\n"
374 emit "addu #{@GOT}, #{@GOT}, #{@FUNCTION}\n"
378 # Save return address
379 emit "sw $31, #{@frame_size - @WORDSIZE}($sp)\n"
382 emit "sw #{@GOT}, #{@frame_size - 2 * @WORDSIZE}($sp)\n"
385 emit "sw $fp, #{@frame_size - 3 * @WORDSIZE}($sp)\n"
387 # Point fp at where sp was before we created the frame
388 addiu "$fp", "$sp", @frame_size
390 # Save parameters 0 .. 3 in the stack space that the caller
391 # should have allocated for them.
392 [@NREGISTER_ARGS, formals.length].min.times do |n|
393 ref = offset_reference(@environment[formals[n]])
394 emit "sw $#{n + @REGISTER_ARG_BASE}, #{ref}\n"
398 # Imports labels into the current section.
399 def emit_import *symbols
400 # Record imported labels in @imports
401 symbols.each { |sym| @imports[sym] = sym }
404 # Emits a label type annotation.
405 def emit_label_type name, type
407 :code => "@function",
410 emit ".type #{name}, #{type_map[type]}\n"
413 # Emits a label size annotation.
414 def emit_label_size name
415 emit ".size #{name}, .-#{name}\n"
418 # Loads a word into a register.
419 def emit_load_word register, base, offset
420 emit "lw #{register}, #{offset * @WORDSIZE}(#{base})\n"
423 # Stores the value of a register in memory.
424 def emit_store_word register, base, offset
425 emit "sw #{register}, #{offset * @WORDSIZE}(#{base})\n"
428 # Ends a function body.
430 if @environment == @top_level
431 raise "Cannot end function when not in a function"
434 label @function_end_label
435 # Restore saved locals
436 [@NREGISTER_LOCALS, @environment.locals].min.times do |n|
437 emit "lw #{local_register n}, #{local_reference n}\n"
439 @saved_registers = []
440 # Load return address
441 emit "lw $31, #{@RA_OFFSET}($fp)\n"
442 # Restore stack pointer
443 emit "move $sp, $fp\n"
444 # Restore frame pointer
445 emit "lw $fp, #{@FP_OFFSET}($fp)\n"
450 @function_end_label = nil
451 @environment = @top_level
454 # Ends the current block.
456 # If we are returning to top level, restore stack pointer
457 if @environment.parent == @top_level
458 grow_stack -@frame_size
459 @saved_registers = []
462 # Restore old value of @environment
463 @environment = @environment.parent
466 # Ends a conditional.
468 label = @if_labels.pop
472 # Evaluates the binary operation expr and store the result in register.
473 def eval_binop expr, register
474 temporaries = @TEMPORARIES.dup
475 x = load_value expr[1], temporaries[0]
477 y = load_value expr[2], temporaries[0]
482 emit "srav #{register}, #{x}, #{y}\n"
484 emit "srlv #{register}, #{x}, #{y}\n"
486 emit "div $0, #{x}, #{y}\n"
487 emit "mflo #{register}\n"
489 emit "div $0, #{x}, #{y}\n"
490 emit "mfhi #{register}\n"
492 emit "mult #{x}, #{y}\n"
493 emit "mflo #{register}\n"
495 emit "subu #{@TEMPORARY}, $0, #{y}\n"
496 emit "srlv #{@TEMPORARY}, #{x}, #{@TEMPORARY}\n"
497 emit "sllv #{register}, #{x}, #{y}\n"
498 emit "or #{register}, #{register}, #{@TEMPORARY}\n"
500 emit "subu #{@TEMPORARY}, $0, #{y}\n"
501 emit "sllv #{@TEMPORARY}, #{x}, #{@TEMPORARY}\n"
502 emit "srlv #{register}, #{x}, #{y}\n"
503 emit "or #{register}, #{register}, #{@TEMPORARY}\n"
505 emit "sllv #{register}, #{x}, #{y}\n"
507 emit "srlv #{register}, #{x}, #{y}\n"
509 emit "#{expr[0]} #{register}, #{x}, #{y}\n"
513 # Evaluates the expression expr and store the result in register.
514 def eval_expr expr, register
517 load_value_into_register expr[0], register
519 # Evaluate expression
523 auto_bytes expr[1], register
525 auto_words expr[1], register
528 emit "move #{register}, #{@RETURN}\n" if register != @RETURN
530 get_byte expr[1], expr[2], register
532 get_word expr[1], expr[2], register
534 eval_binop [:nor, 0, expr[1]], register
537 eval_binop expr, register
539 raise "Not a magic word: #{op}"
545 # Loads byte from _base_ + _offset_ into _register_.
546 def get_byte base, offset, register
547 # If base is an integer, but offset isn't, swap them
548 if !integer?(offset) && integer?(base)
549 base, offset = [offset, base]
553 base_reg = load_value base
554 emit "lb #{register}, #{offset}(#{base_reg})\n"
556 eval_binop [:add, base, offset], @TEMPORARY
557 emit "lb #{register}, 0(#{@TEMPORARY})\n"
561 # Loads word from _base_ + _offset_ * _@WORDSIZE_ into _register_.
562 def get_word base, offset, register
564 base_reg = load_value base
565 emit "lw #{register}, #{offset * @WORDSIZE}(#{base_reg})\n"
567 offset_reg = @TEMPORARIES.pop
569 load_value_into_register offset, offset_reg
570 base_reg = load_value base
571 emit "sll #{offset_reg}, #{offset_reg}, 2\n" # multiply by @WORDSIZE
572 emit "add #{@TEMPORARY}, #{base_reg}, #{offset_reg}\n"
573 emit "lw #{register}, 0(#{@TEMPORARY})\n"
575 @TEMPORARIES.push offset_reg
580 # Jumps to an address.
583 emit "j #{address}\n"
585 register = load_value address, @TEMPORARY
586 emit "jr #{register}\n"
591 # Grows the stack by n bytes.
593 addiu "$sp", "$sp", -n
596 # Starts the false path of a conditional.
598 newlabel = @environment.gensym
599 emit "j #{newlabel}\n"
601 label = @if_labels.pop
603 @if_labels.push newlabel
606 # Tests if x is equal to y
608 common_if :ifeq, x, y
611 # Tests if x is greater than or equal to y
613 common_if :ifge, x, y
616 # Tests if x is strictly greater than y
618 common_if :ifgt, x, y
621 # Tests if x is less than or equal to y
623 common_if :ifle, x, y
626 # Tests if x is strictly less than y
628 common_if :iflt, x, y
631 # Tests if x different from y
633 common_if :ifne, x, y
636 # Introduces a new local variable.
637 def let symbol, *expr
638 n = @environment.locals
639 register = local_register n
640 ref = local_reference n
642 # We will use a register to store the value
643 @environment.add_local symbol, register
644 # Save current value of register
645 emit "sw #{register}, #{ref}\n"
646 @saved_registers << register
648 eval_expr expr, register
650 # We will use the stack to store the value
651 @environment.add_local symbol, local_offset(n)
652 eval_expr expr, @TEMPORARY
653 emit "sw #{@TEMPORARY}, #{ref}\n"
657 # Loads the value at the given address.
658 def load_at address, register = @TEMPORARY
659 load_value_into_register address, register
660 emit "lw #{register}, 0(#{register})\n"
664 # Loads a value into a register.
665 # Returns the name of the register.
666 # If the value was already in a register, the name of that
667 # register is returned.
668 # Else, the value is loaded into a register and the name of
669 # that register is returned. The register to use in that case
670 # may be specified using the optional second argument.
671 def load_value x, register = @TEMPORARY
673 x = substitute_number x[1]
679 addiu register, "$0", x
682 binding = @environment[x]
684 # Value is already in a register. Return register name.
686 elsif binding.kind_of? Integer
687 # Load value from memory.
688 emit "lw #{register}, #{offset_reference binding}\n"
692 if @relocated_symbols.include? x
693 emit "lw #{register}, %got(#{x})(#{@GOT})\n"
695 emit "lui #{register}, %hi(#{x})\n"
696 emit "addiu #{register}, %lo(#{x})\n"
701 load_at x[1], register
703 raise "Don't know how to load #{x.inspect}"
707 # Loads a value into a specific register.
708 def load_value_into_register x, register
709 reg = load_value x, register
711 emit "move #{register}, #{reg}\n"
715 # Returns the offset from the frame base at which the nth local is stored.
716 # For register locals this is the offset at which the saved
717 # value (from the calling function) is stored.
722 # Returns an $sp-relative reference for the nth (0-based) local.
723 def local_reference n
724 offset_reference(local_offset(n))
727 # Returns the register in which the nth local (0-based) is stored, or
728 # nil if not stored in a register.
733 # Computes the maximum number of locals that would fit in the current
736 # + 1, because the initial frame size has room for 1 local
737 (@frame_size - @INITIAL_FRAME_SIZE) / @WORDSIZE + 1
740 # Given an offset relative to the base of the frame, returns
741 # an reference to that memory location.
742 def offset_reference offset
746 # Returns true if the nth (0-based) local is stored in a register.
747 def register_local? n
748 n < @NREGISTER_LOCALS
751 # Returns from a function.
753 # _words_ may contain an expression to be evaluated. The result
754 # of the evaluation is returned from the function.
756 # Compute return value and store it in @RETURN
757 eval_expr(words, @RETURN) unless words.empty?
759 goto @function_end_label
762 # Saves the current frame at the given location.
763 def save_frame location
764 load_value_into_register location, @TEMPORARY
765 @SAVE_FRAME_REGISTERS.each_with_index do |register,i|
766 emit "sw #{register}, #{i * @WORDSIZE}(#{@TEMPORARY})\n"
770 # Sets a variable to the result of evaluating an expression.
771 def set symbol, *expr
773 eval_expr expr, @RETURN
774 load_value_into_register symbol.to_s[1..-1].to_sym, @TEMPORARY
775 emit "sw #{@RETURN}, 0(#{@TEMPORARY})\n"
777 x = @environment[symbol]
779 raise "Cannot change value of constant #{symbol}"
783 # Should be an integer.
784 eval_expr expr, @TEMPORARY
785 emit "sw #{@TEMPORARY}, #{offset_reference x}\n"
790 # Sets the byte at _base_ + _offset_ to _value_.
791 def set_byte base, offset, value
792 # If base is an integer, but offset isn't, swap them
793 if !integer?(offset) && integer?(base)
794 base, offset = [offset, base]
797 value_reg = @TEMPORARIES.pop
798 load_value_into_register value, value_reg
801 base_reg = load_value base
802 emit "sb #{value_reg}, #{offset}(#{base_reg})\n"
804 eval_binop [:add, base, offset], @TEMPORARY
805 emit "sb #{value_reg}, 0(#{@TEMPORARY})\n"
808 @TEMPORARIES.push value_reg
812 # Sets the word at _base_ + _offset_ * +@WORDSIZE+ to _value_.
813 def set_word base, offset, value
814 value_reg = @TEMPORARIES.pop
815 load_value_into_register value, value_reg
818 base_reg = load_value base
819 emit "sw #{value_reg}, #{offset * @WORDSIZE}(#{base_reg})\n"
821 offset_reg = @TEMPORARIES.pop
823 load_value_into_register offset, offset_reg
824 base_reg = load_value base
825 emit "sll #{offset_reg}, #{offset_reg}, 2\n"
826 emit "add #{@TEMPORARY}, #{base_reg}, #{offset_reg}\n"
827 emit "sw #{value_reg}, 0(#{@TEMPORARY})\n"
829 @TEMPORARIES.push offset_reg
833 @TEMPORARIES.push value_reg
837 # Defines a string with the given value.
840 value.each_byte do |b|
843 elsif b >= 32 && b < 127 && b != 34
846 code << sprintf("\\%03o", b)
849 emit ".ascii \"#{code}\"\n"
852 # Calls a function, re-using the current call frame if possible.
853 def tail_call func, *args
854 # Compute number of stack arguments
855 nstackargs = number_of_stack_arguments args.length
856 # If we need more stack arguments than we have now,
857 # perform a normal call and return
858 if nstackargs > number_of_stack_arguments(@environment.args)
859 emit "# Not enough space for proper tail call; using regular call\n"
860 ret :call, func, *args
863 # Back up any stack arguments we will need after we overwrite them
864 temporaries = @TEMPORARIES.dup
865 nlocals = @environment.locals
866 (@NREGISTER_ARGS...args.length).each do |i|
867 x = @environment[args[i]]
868 if integer?(x) && x < arg_offset(i)
869 # args[i] is in a location that will have been overwritten by the
870 # time we get around to passing argument i to the called function.
871 # Make a backup of the value before we overwrite it.
872 if temporaries.empty?
873 # Oh dear, we're out of temporaries.
874 # Store the value in the current stack frame, extending it
876 if nlocals >= max_locals
879 reg = load_value args[i]
880 emit "sw #{reg}, #{local_reference nlocals}\n"
881 args[i] = [:local, nlocals]
882 nlocals = nlocals + 1
884 # Load the argument into a temporary
885 reg = temporaries.shift
886 load_value_into_register args[i], reg
887 # Update args[i] so we know how to get to it later
888 args[i] = [:reg, reg]
893 # Set stack arguments
894 (@NREGISTER_ARGS...args.length).each do |i|
899 if arg.kind_of? Array
900 # Special cases, created above
905 emit "lw #{reg}, #{local_reference arg[1]}\n"
908 load_value_into_register arg, reg
911 # Store value in designated place
912 emit "sw #{reg}, #{arg_reference i}\n"
915 # Set register arguments
916 [@NREGISTER_ARGS,args.length].min.times do |i|
918 load_value_into_register args[i], reg
921 # Load function address
923 if @imports.has_key? func
924 # Load address from global offset table
925 emit "lw #{@FUNCTION}, %call16(#{func})(#{@GOT})\n"
927 # Assume label defined in this module
928 emit "lui #{@FUNCTION}, %hi(#{func})\n"
929 emit "addiu #{@FUNCTION}, %lo(#{func})\n"
933 load_value_into_register func, "#{@FUNCTION}"
936 # Restore saved registers
937 [@NREGISTER_LOCALS, @environment.locals].min.times do |n|
938 emit "lw #{local_register n}, #{local_reference n}\n"
941 # Restore return address
942 emit "lw $31, #{@RA_OFFSET}($fp)\n"
944 # Restore stack pointer
945 emit "move $sp, $fp\n"
947 # Restore frame pointer
948 emit "lw $fp, #{@FP_OFFSET}($fp)\n"
951 emit "jr #{@FUNCTION}\n"
956 # Defines a word with the given value
958 emit ".int #{value}\n"
961 # Write generated code to the given IO object.
964 @sections.each do |section,code|
966 io.puts ".section #{section.to_s}"
975 # Register class for big endian MIPS
976 Voodoo::CodeGenerator.register_generator MIPSGasGenerator,
977 :architecture => :mips,
981 # Register class for little endian MIPS
982 Voodoo::CodeGenerator.register_generator MIPSGasGenerator,
983 :architecture => :mipsel,