Implemented auto-bytes and auto-words for mips
[voodoo-lang.git] / lib / voodoo / generators / mips_gas_generator.rb
blob687271132f9c0a9e503ee2acb78ce512809792c0
1 require 'voodoo/generators/common_code_generator'
3 module Voodoo
4   # = MIPS GNU Assembler Code Generator
5   #
6   # The MIPS code generator generates assembly code for use with
7   # the GNU assembler.
8   #
9   # == Calling Convention
10   #
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.
15   #
16   # The return address for the called function is passed in $31.
17   #
18   # When performing a position-independent call, the address of the called
19   # function is passed in $25.
20   #
21   # The called function will store its return value in $2.
22   #
23   # The called function is required to preserve the values of registers
24   # $16 through $23 and register $30.
25   #
26   # This calling convention is compatible with the System V ELF ABI.
27   #
28   # == Call Frames
29   #
30   # Call frames have the following layout:
31   #
32   # When a function is called, it receives a stack frame that looks like
33   # the following:
34   #
35   #   :
36   #   old frame
37   #   argn
38   #   :
39   #   arg4
40   #   empty3
41   #   empty2
42   #   empty1
43   #   empty0    <-- $sp points here
44   #
45   # The function prologue of functions generated by this code generator
46   # creates activation frames that look as follows:
47   #
48   # :
49   # old frame
50   # argn
51   # :
52   # arg4
53   # arg3
54   # arg2
55   # arg1
56   # arg0       <-- $fp points here
57   # return address
58   # pointer to Global Offset Table
59   # saved $fp
60   # local0
61   # :
62   # local7
63   # local8
64   # :
65   # localn
66   # padding    <-- $sp points here
67   #
68   # In words:
69   #
70   # The four empty slots provided by the caller are used to store
71   # arguments 0 through 3 (originally passed in $4 through $7),
72   # if necessary.
73   #
74   # The stack frame created below the four slots provided by the caller
75   # contains the following data, in order:
76   #
77   # - Saved return address (originally passed in $31), if necessary.
78   #
79   # - Saved pointer to global offset table (computed from $25), if necessary.
80   #
81   # - Saved value of $fp, if necessary.
82   #
83   # - Saved values of caller's locals 0 through 7
84   #   (originally in $16 through $23), if necessary.
85   #
86   # - Values of our locals > 8, if necessary.
87   #
88   # - Padding to align $sp on a multiple of 8, if necessary.
89   #
90   #
91   # In accordance with the System V ELF ABI for MIPS, the pointer to
92   # the global offset table is calculated as follows:
93   #
94   # $gp = _gp_disp + $25
95   #
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.
99   #
100   class MIPSGasGenerator < CommonCodeGenerator
101     def initialize params
102       @WORDSIZE = 4
103       @CODE_ALIGNMENT = 0
104       @DATA_ALIGNMENT = @WORDSIZE
105       @FP_OFFSET = -3 * @WORDSIZE
106       @FUNCTION_ALIGNMENT = @WORDSIZE
107       @GP_OFFSET = -2 * @WORDSIZE
108       @INITIAL_FRAME_SIZE = 4 * @WORDSIZE
109       @REGISTER_ARG_BASE = 4
110       @NREGISTER_ARGS = 4
111       @REGISTER_LOCAL_BASE = 16
112       @NREGISTER_LOCALS = 8
113       @RA_OFFSET = -@WORDSIZE
114       @RETURN = :'$2'
115       @TEMPORARY = :'$1'
116       @FUNCTION = :'$25'
117       @GOT = :'$28'
118       @TEMPORARIES = [:'$8', :'$9', :'$10', :'$11',
119                       :'$12', :'$13', :'$14', :'$15']
120       @function_end_label = nil
121       @imports = {}
122       @if_labels = []
123       super params
124       @output_file_suffix = '.s'
125       @features.merge! \
126         :'bits-per-word' => '32',
127         :'bytes-per-word' => '4'
128       case @architecture
129       when :mips
130         @features[:'byte-order'] = 'big-endian'
131       when :mipsel
132         @features[:'byte-order'] = 'little-endian'
133       else
134         raise ArgumentError.new("#{self.class} does not support " +
135                                 "architecture #{@architecture}")
136       end
137     end
139     # Adds immediate to source and stores the result in target.
140     def addiu target, source, immediate, temporary = @TEMPORARY
141       if immediate >= -32768 && immediate < 32767
142         emit "addiu #{target}, #{source}, #{immediate}\n"
143       elsif source == "$0"
144         emit "lui #{target}, #{(immediate >> 16) & 0xffff}\n"
145         emit "ori #{target}, #{target}, #{immediate & 0xffff}\n"
146       else
147         addiu temporary, "$0", immediate
148         emit "addu #{target}, #{source}, #{temporary}\n"
149       end
150     end
152     def align alignment = nil
153       unless alignment
154         # Get default alignment
155         case @section
156         when :code
157           alignment = @CODE_ALIGNMENT
158         when :data
159           alignment = @DATA_ALIGNMENT
160         when :function
161           alignment = @FUNCTION_ALIGNMENT
162         else
163           # Use data alignment as default
164           alignment = @DATA_ALIGNMENT
165         end
166       end
167       emit ".align #{alignment}\n" unless alignment == 0
168     end
170     # Return the offset from the frame base at which the nth argument is
171     # stored.
172     def arg_offset n
173       n * @WORDSIZE
174     end
176     # Return an $fp-relative reference for the nth (0-based) argument
177     def arg_reference n
178       offset_reference(arg_offset(n))
179     end
181     # Return the register in which the nth (0-based) argument is stored, or
182     # nil if not stored in a register
183     def arg_register n
184       if register_arg? n
185         "$#{@REGISTER_ARG_BASE + n}"
186       else
187         nil
188       end
189     end
191     # Test if op is a binary operation
192     def assymetric_binop? op
193       [:asr, :bsr, :div, :mod, :rol, :ror, :shl, :shr, :sub].member?(op)
194     end
196     # Test if a value is an at-expression
197     def at_expr? value
198       value.respond_to?(:[]) && value[0] == :'@'
199     end
201     def auto_bytes n, register
202       # Maintain stack pointer at a multiple of 8 bytes.
203       n = (n + 7) / 8 * 8
204       grow_stack n
205       emit "move #{register}, $sp\n" unless register == "$sp"
206     end
208     def auto_words n, register
209       auto_bytes n * @WORDSIZE, register
210     end
212     # Begins a new block.
213     def begin_block *code
214       emit "# begin block\n"
215       # If we are at top-level, create a frame
216       if @environment == @top_level
217         create_frame count_locals(code)
218       end
219       environment = Environment.new @environment
220       @environment = environment
221     end
223     # Emit function prologue and declare _formals_ as function arguments
224     def begin_function formals, nlocals
225       if @environment != @top_level
226         raise "Cannot begin function when already in a function"
227       end
229       environment = Environment.new @environment
230       @frame_size = 0
231       formals.each {|x| environment.add_arg x, arg_offset(environment.args)}
232       @environment = environment
233       @function_end_label = gensym
234       emit_function_prologue formals, nlocals
235     end
237     # Test if op is a binary operation
238     def binop? op
239       assymetric_binop?(op) || symmetric_binop?(op)
240     end
242     # Define a byte with the given value
243     def byte value
244       emit ".byte #{value}\n"
245     end
247     # Call a function.
248     def call func, *args
249       # Grow the stack frame, subject to 3 conditions:
250       # 1. There must be enough space to hold all arguments
251       # 2. $sp must remain a multiple of 8
252       # 3. We must provide space for at least 4 words
253       increment = ([args.length, 4].max * @WORDSIZE + 7) / 8 * 8
254       grow_stack increment
256       # Put arguments in the right places
257       n = 0
258       while n < args.length
259         if n < @NREGISTER_ARGS
260           # Put arguments up to @NREGISTER_ARGS in registers
261           load_value_into_register args[n], arg_register(n)
262         else
263           # Put other arguments on the stack
264           load_value_into_register args[n], @TEMPORARY
265           emit "sw #{@TEMPORARY}, #{n * @WORDSIZE}($sp)\n"
266         end
267         n = n + 1
268       end
270       # Load function address
271       if global? func
272         if @imports.has_key? func
273           # Load address from global offset table
274           emit "lw #{@FUNCTION}, %call16(#{func})(#{@GOT})\n"
275         else
276           # Assume label defined in this module
277           emit "lui #{@FUNCTION}, %hi(#{func})\n"
278           emit "addiu #{@FUNCTION}, %lo(#{func})\n"
279         end
280       else
281         # Load address
282         load_value_into_register func, "#{@FUNCTION}"
283       end
285       # Perform call
286       emit "jalr #{@FUNCTION}\n"
287       # brach delay slot
288       emit "nop\n"
290       # Restore original stack frame
291       grow_stack -increment
293       # restore gp
294       emit "lw #{@GOT}, #{@GP_OFFSET}($fp)\n"
295     end
297     # Start a conditional using the specified branch instruction
298     # after the comparison.
299     def common_if comp, x, y = nil
300       emit "# #{comp} #{x} #{y}\n"
302       temporaries = @TEMPORARIES.dup
303       xreg = load_value x, temporaries[0]
304       temporaries.delete xreg
305       yreg = load_value y, temporaries[0]
306       temporaries.delete yreg
308       falselabel = @environment.gensym
309       @if_labels.push falselabel
311       case comp
312       when :ifeq
313         emit "bne #{xreg}, #{yreg}, #{falselabel}\n"
314       when :ifge
315         emit "slt #{@TEMPORARY}, #{xreg}, #{yreg}\n"
316         emit "bne #{@TEMPORARY}, $0, #{falselabel}\n"
317       when :ifgt
318         emit "slt #{@TEMPORARY}, #{yreg}, #{xreg}\n"
319         emit "beq #{@TEMPORARY}, $0, #{falselabel}\n"
320       when :ifle
321         emit "slt #{@TEMPORARY}, #{yreg}, #{xreg}\n"
322         emit "bne #{@TEMPORARY}, $0, #{falselabel}\n"
323       when :iflt
324         emit "slt #{@TEMPORARY}, #{xreg}, #{yreg}\n"
325         emit "beq #{@TEMPORARY}, $0, #{falselabel}\n"
326       when :ifne
327         emit "beq #{xreg}, #{yreg}, #{falselabel}\n"
328       else
329         raise "Unknown conditional: #{comp}"
330       end
331       emit "nop\n"
332     end
334     # Counts the number of local variables created in
335     # a sequence of statements.
336     def count_locals statements
337        count = 0
338        each_statement(statements) do |statement|
339          if statement[0] == :let
340            # let introduces a single local
341            count = count + 1
342          end
343        end
344        count
345     end
347     # Creates an activation frame which can store nlocals local variables
348     def create_frame nlocals
349       @frame_size = @INITIAL_FRAME_SIZE
350       if nlocals > 0
351         @frame_size = @frame_size + (nlocals * @WORDSIZE + 7) / 8 * 8
352       end
353       grow_stack @frame_size
354     end
356     # Emit function prologue.
357     def emit_function_prologue formals = [], nlocals = 0
358       # Calculate new value for $gp
359       emit "lui #{@GOT}, %hi(_gp_disp)\n"
360       emit "addiu #{@GOT}, %lo(_gp_disp)\n"
361       emit "addu #{@GOT}, #{@GOT}, #{@FUNCTION}\n"
363       create_frame nlocals
365       # Save return address
366       emit "sw $31, #{@frame_size - @WORDSIZE}($sp)\n"
368       # Save gp
369       emit "sw #{@GOT}, #{@frame_size - 2 * @WORDSIZE}($sp)\n"
371       # Save fp
372       emit "sw $fp, #{@frame_size - 3 * @WORDSIZE}($sp)\n"
374       # Point fp at where sp was before we created the frame
375       addiu "$fp", "$sp", @frame_size
377       # Save parameters 0 .. 3 in the stack space that the caller
378       # should have allocated for them.
379       [@NREGISTER_ARGS, formals.length].min.times do |n|
380         ref = offset_reference(@environment[formals[n]])
381         emit "sw $#{n + @REGISTER_ARG_BASE}, #{ref}\n"
382       end
383     end
385     # End a function body
386     def end_function
387       if @environment == @top_level
388         raise "Cannot end function when not in a function"
389       end
391       label @function_end_label
392       # Restore saved locals
393       [@NREGISTER_LOCALS, @environment.locals].min.times do |n|
394         emit "lw #{local_register n}, #{local_reference n}\n"
395       end
396       # Load return address
397       emit "lw $31, #{@RA_OFFSET}($fp)\n"
398       # Restore stack pointer
399       emit "move $sp, $fp\n"
400       # Restore frame pointer
401       emit "lw $fp, #{@FP_OFFSET}($fp)\n"
402       # Return
403       emit "jr $31\n"
404       emit "nop\n"
406       emit "# end function\n\n"
408       @function_end_label = nil
409       @environment = @top_level
410     end
412     # Ends the current block.
413     def end_block
414       emit "# end block\n"
416       # If we are returning to top level, restore stack pointer
417       if @environment.parent == @top_level
418         grow_stack -@frame_size
419       end
421       # Restore old value of @environment
422       @environment = @environment.parent
423     end
425     # End a conditional.
426     def end_if
427       label = @if_labels.pop
428       emit "#{label}:\n"
429     end
431     # Evaluate the binary operation expr and store the result in register
432     def eval_binop expr, register
433       temporaries = @TEMPORARIES.dup
434       x = load_value expr[1], temporaries[0]
435       temporaries.delete x
436       y = load_value expr[2], temporaries[0]
437       temporaries.delete y
439       case expr[0]
440       when :asr
441         emit "srav #{register}, #{x}, #{y}\n"
442       when :bsr
443         emit "srlv #{register}, #{x}, #{y}\n"
444       when :div
445         emit "div $0, #{x}, #{y}\n"
446         emit "mflo #{register}\n"
447       when :mod
448         emit "div $0, #{x}, #{y}\n"
449         emit "mfhi #{register}\n"
450       when :mul
451         emit "mult #{x}, #{y}\n"
452         emit "mflo #{register}\n"
453       when :rol
454         emit "subu #{@TEMPORARY}, $0, #{y}\n"
455         emit "srlv #{@TEMPORARY}, #{x}, #{@TEMPORARY}\n"
456         emit "sllv #{register}, #{x}, #{y}\n"
457         emit "or #{register}, #{register}, #{@TEMPORARY}\n"
458       when :ror
459         emit "subu #{@TEMPORARY}, $0, #{y}\n"
460         emit "sllv #{@TEMPORARY}, #{x}, #{@TEMPORARY}\n"
461         emit "srlv #{register}, #{x}, #{y}\n"
462         emit "or #{register}, #{register}, #{@TEMPORARY}\n"
463       when :shl
464         emit "sllv #{register}, #{x}, #{y}\n"
465       when :shr
466         emit "srlv #{register}, #{x}, #{y}\n"
467       else
468         emit "#{expr[0]} #{register}, #{x}, #{y}\n"
469       end
470     end
472     # Evaluate the expression expr and store the result in register
473     def eval_expr expr, register
474       if expr.length == 1
475         # Load value
476         load_value_into_register expr[0], register
477       else
478         # Evaluate expression
479         op = expr[0]
480         case op
481         when :'auto-bytes'
482           auto_bytes expr[1], register
483         when :'auto-words'
484           auto_words expr[1], register
485         when :call
486           call *expr[1..-1]
487           emit "move #{register}, #{@RETURN}\n" if register != @RETURN
488         when :'get-byte'
489           get_byte expr[1], expr[2], register
490         when :'get-word'
491           get_word expr[1], expr[2], register
492         when :not
493             eval_binop [:nor, 0, expr[1]], register
494         else
495           if binop? op
496             eval_binop expr, register
497           else
498             raise "Not a magic word: #{op}"
499           end
500         end
501       end
502     end
504     # Export symbols from the current section
505     def export *symbols
506       symbols.each { |sym| emit ".globl #{sym}\n" }
507     end
509     # Add a function to the current section
510     def function formals, *code
511       nlocals = count_locals code
512       begin_function formals, nlocals
513       code.each { |action| add section, action }
514       end_function
515     end
517     # Load byte from _base_ + _offset_ into _register_
518     def get_byte base, offset, register
519       # If base is an integer, but offset isn't, swap them
520       if !integer?(offset) && integer?(base)
521         base, offset = [offset, base]
522       end
524       if integer? offset
525         base_reg = load_value base
526         emit "lb #{register}, #{offset}(#{base_reg})\n"
527       else
528         eval_binop [:add, base, offset], @TEMPORARY
529         emit "lb #{register}, 0(#{@TEMPORARY})\n"
530       end
531     end
533     # Load word from _base_ + _offset_ * _@WORDSIZE_ into _register_
534     def get_word base, offset, register
535       if integer? offset
536         base_reg = load_value base
537         emit "lw #{register}, #{offset * @WORDSIZE}(#{base_reg})\n"
538       else
539         offset_reg = @TEMPORARIES.pop
540         begin
541           load_value_into_register offset, offset_reg
542           base_reg = load_value base
543           emit "sll #{offset_reg}, #{offset_reg}, 2\n" # multiply by @WORDSIZE
544           emit "add #{@TEMPORARY}, #{base_reg}, #{offset_reg}\n"
545           emit "lw #{register}, 0(#{@TEMPORARY})\n"
546         ensure
547           @TEMPORARIES.push offset_reg
548         end
549       end
550     end
552     # Test if a symbol refers to a global
553     def global? symbol
554       symbol?(symbol) && @environment[symbol] == nil
555     end
557     # Jump to a label.
558     def goto label
559       emit "j #{label}\n"
560       emit "nop\n"
561     end
563     # Grows the stack by n bytes.
564     def grow_stack n
565       addiu "$sp", "$sp", -n
566     end
568     # Start the false path of a conditional.
569     def ifelse
570       emit "# else\n"
571       newlabel = @environment.gensym
572       emit "j #{newlabel}\n"
573       emit "nop\n"
574       label = @if_labels.pop
575       emit "#{label}:\n"
576       @if_labels.push newlabel
577     end
579     # Test if x is equal to y
580     def ifeq x, y
581       common_if :ifeq, x, y
582     end
584     # Test if x is greater than or equal to y
585     def ifge x, y
586       common_if :ifge, x, y
587     end
589     # Test if x is strictly greater than y
590     def ifgt x, y
591       common_if :ifgt, x, y
592     end
594     # Test if x is less than or equal to y
595     def ifle x, y
596       common_if :ifle, x, y
597     end
599     # Test if x is strictly less than y
600     def iflt x, y
601       common_if :iflt, x, y
602     end
604     # Test if x different from y
605     def ifne x, y
606       common_if :ifne, x, y
607     end
609     # Import labels into the current section
610     def import *symbols
611       # Record imported labels in @imports
612       symbols.each { |sym| @imports[sym] = sym }
613     end
615     # Test if a value is an integer
616     def integer? value
617       value.kind_of? Integer
618     end
620     # Emit a label
621     def label name
622       emit "#{name}:\n"
623     end
625     # Introduces a new local variable.
626     def let symbol, *expr
627       emit "# let #{symbol} #{expr.join ' '}\n"
628       n = @environment.locals
629       register = local_register n
630       ref = local_reference n
631       if register
632         # We will use a register to store the value
633         @environment.add_local symbol, register
634         # Save current value of register
635         emit "sw #{register}, #{ref}\n"
636         # Set new value
637         eval_expr expr, register
638       else
639         # We will use the stack to store the value
640         @environment.add_local symbol, local_offset(n)
641         eval_expr expr, @TEMPORARY
642         emit "sw #{@TEMPORARY}, #{ref}\n"
643       end
644     end
646     # Loads the value at the given address.
647     def load_at address, register = @TEMPORARY
648       load_value_into_register address, register
649       emit "lw #{register}, 0(#{register})\n"
650       register
651     end
653     # Loads a value into a register.
654     # Returns the name of the register.
655     # If the value was already in a register, the name of that
656     # register is returned.
657     # Else, the value is loaded into a register and the name of
658     # that register is returned. The register to use in that case
659     # may be specified using the optional second argument.
660     def load_value x, register = @TEMPORARY
661       if x == 0
662         return "$0"
663       elsif integer? x
664         addiu register, "$0", x
665         return register
666       elsif symbol? x
667         binding = @environment[x]
668         if binding.kind_of? String
669           # Value is already in a register. Return register name.
670           return binding
671         elsif binding.kind_of? Integer
672           # Load value from memory.
673           emit "lw #{register}, #{offset_reference binding}\n"
674           return register
675         else
676           # Assume global
677           emit "lui #{register}, %hi(#{x})\n"
678           emit "addiu #{register}, %lo(#{x})\n"
679           return register
680         end
681       elsif at_expr? x
682         load_at x[1], register
683       else
684         raise "Don't know how to load #{x.inspect}"
685       end
686     end
688     # Load a value into a specific register
689     def load_value_into_register x, register
690       reg = load_value x, register
691       if reg != register
692         emit "move #{register}, #{reg}\n"
693       end
694     end
696     # Return the offset from the frame base at which the nth local is stored.
697     # For register locals this is the offset at which the saved
698     # value (from the calling function) is stored.
699     def local_offset n
700       -(n + 4) * @WORDSIZE
701     end
703     # Return an $sp-relative reference for the nth (0-based) local
704     def local_reference n
705       offset_reference(local_offset(n))
706     end
708     # Return the register in which the nth local (0-based) is stored, or
709     # nil if not stored in a register
710     def local_register n
711       if register_local? n
712         "$#{@REGISTER_LOCAL_BASE + n}"
713       else
714         nil
715       end
716     end
718     # Compute the maximum number of locals that would fit in the current
719     # frame size.
720     def max_locals
721       # + 1, because the initial frame size has room for 1 local
722       (@frame_size - @INITIAL_FRAME_SIZE) / @WORDSIZE + 1
723     end
725     # Calculate the number of stack arguments,
726     # given the total number of arguments.
727     def number_of_stack_arguments n
728       [0, n - @NREGISTER_ARGS].max
729     end
731     # Given an offset relative to the base of the frame, returns
732     # an reference to that memory location.
733     def offset_reference offset
734       "#{offset}($fp)"
735     end
737     # Returns true if the nth (0-based) argument is stored in a register
738     def register_arg? n
739       n < @NREGISTER_ARGS
740     end
742     # Returns true if the nth (0-based) local is stored in a register
743     def register_local? n
744       n < @NREGISTER_LOCALS
745     end
747     # Return a from a function.
748     # 
749     # _words_ may contain an expression to be evaluated. The result
750     # of the evaluation is returned from the function.
751     def ret *words
752       emit "# return #{words.join ' '}\n"
753       # Compute return value and store it in @RETURN
754       eval_expr(words, @RETURN) unless words.empty?
755       # Go to epilogue
756       goto @function_end_label
757     end
758     
759     # Set a variable to the result of evaluating an expression
760     def set symbol, *expr
761       if at_expr? symbol
762         eval_expr expr, @RETURN
763         load_value_into_register symbol.to_s[1..-1].to_sym, @TEMPORARY
764         emit "sw #{@RETURN}, 0(#{@TEMPORARY})\n"
765       else
766         x = @environment[symbol]
767         if x == nil
768           raise "Cannot change value of constant #{symbol}"
769         elsif x.kind_of? String
770           # Register
771           eval_expr expr, x
772         else
773           # Should be an integer.
774           eval_expr expr, @TEMPORARY
775           emit "sw #{@TEMPORARY}, #{offset_reference x}\n"
776         end
777       end
778     end
780     # Set the byte at _base_ + _offset_ to _value_
781     def set_byte base, offset, value
782       emit "# set-byte #{base} #{offset} #{value}\n"
783       # If base is an integer, but offset isn't, swap them
784       if !integer?(offset) && integer?(base)
785         base, offset = [offset, base]
786       end
788       value_reg = @TEMPORARIES.pop
789       load_value_into_register value, value_reg
790       begin
791         if integer? offset
792           base_reg = load_value base
793           emit "sb #{value_reg}, #{offset}(#{base_reg})\n"
794         else
795           eval_binop [:add, base, offset], @TEMPORARY
796           emit "sb #{value_reg}, 0(#{@TEMPORARY})\n"
797         end
798       ensure
799         @TEMPORARIES.push value_reg
800       end
801     end
803     # Set the word at _base_ + _offset_ * +@WORDSIZE+ to _value_
804     def set_word base, offset, value
805       emit "# set-word #{base} #{offset} #{value}\n"
807       value_reg = @TEMPORARIES.pop
808       load_value_into_register value, value_reg
809       begin
810         if integer? offset
811           base_reg = load_value base
812           emit "sw #{value_reg}, #{offset * @WORDSIZE}(#{base_reg})\n"
813         else
814           offset_reg = @TEMPORARIES.pop
815           begin
816             load_value_into_register offset, offset_reg
817             base_reg = load_value base
818             emit "sll #{offset_reg}, #{offset_reg}, 2\n"
819             emit "add #{@TEMPORARY}, #{base_reg}, #{offset_reg}\n"
820             emit "sw #{value_reg}, 0(#{@TEMPORARY})\n"
821           ensure
822             @TEMPORARIES.push offset_reg
823           end
824         end
825       ensure
826         @TEMPORARIES.push value_reg
827       end
828     end
830     # Define a string with the given value
831     def string value
832       code = ''
833       value.each_byte do |b|
834         if b == 92
835           code << "\\\\"
836         elsif b >= 32 && b < 127 && b != 34
837           code << b.chr
838         else
839           code << sprintf("\\%03o", b)
840         end
841       end
842       emit ".ascii \"#{code}\"\n"
843     end
845     # Test if a value is a symbol
846     def symbol? value
847       value.kind_of? Symbol
848     end
850     # Test if op is a symmetric binary operation (i.e. it will yield the
851     # same result if the order of its source operands is changed).
852     def symmetric_binop? op
853       [:add, :and, :mul, :or, :xor].member? op
854     end
856     # Call a function, re-using the current call frame if possible.
857     def tail_call func, *args
858       emit "# tail-call #{func} #{args.join ' '}\n"
860       # Compute number of stack arguments
861       nstackargs = number_of_stack_arguments args.length
862       # If we need more stack arguments than we have now,
863       # perform a normal call and return
864       if nstackargs > number_of_stack_arguments(@environment.args)
865         emit "# Not enough space for proper tail call; using regular call\n"
866         ret :call, func, *args
867       end
869       # Back up any stack arguments we will need after we overwrite them
870       temporaries = @TEMPORARIES.dup
871       nlocals = @environment.locals
872       (@NREGISTER_ARGS...args.length).each do |i|
873         x = @environment[args[i]]
874         if x && x[0] == :arg && x[1] >= @NREGISTER_ARGS && x[1] < i
875           # args[i] is a stack arg, but has an index < i
876           # That means that, by the time we get to pass arg[i] to the called
877           # function, it will have been overwritten. So we make a backup.
878           if temporaries.empty?
879             # Oh dear, we're out of temporaries.
880             # Store the value in the current stack frame, extending it
881             # as necessary.
882             if nlocals >= max_locals
883               grow_stack 8
884             end
885             reg = load_value args[i]
886             emit "sw #{reg}, #{local_reference nlocals}\n"
887             args[i] = [:local, nlocals]
888             nlocals = nlocals + 1
889           else
890             # Load the argument into a temporary
891             reg = temporaries.shift
892             load_value_into_register args[i], reg
893             # Update args[i] so we know how to get to it later
894             args[i] = [:reg, reg]
895           end
896         end
897       end
899       # Set stack arguments
900       (@NREGISTER_ARGS...args.length).each do |i|
901         arg = args[i]
902         reg = @TEMPORARY
904         # Load value
905         if arg.kind_of? Array
906           # Special cases, created above
907           case arg[0]
908           when :reg
909             reg = arg[1]
910           when :local
911             emit "lw #{reg}, #{local_reference arg[1]}\n"
912           end
913         else
914           load_value_into_register arg, reg
915         end
917         # Store value in designated place
918         emit "sw #{reg}, #{arg_reference i}\n"
919       end
921       # Set register arguments
922       [@NREGISTER_ARGS,args.length].min.times do |i|
923         reg = arg_register i
924         load_value_into_register args[i], reg
925       end
927       # Load function address
928       if global? func
929         if @imports.has_key? func
930           # Load address from global offset table
931           emit "lw #{@FUNCTION}, %call16(#{func})(#{@GOT})\n"
932         else
933           # Assume label defined in this module
934           emit "lui #{@FUNCTION}, %hi(#{func})\n"
935           emit "addiu #{@FUNCTION}, %lo(#{func})\n"
936         end
937       else
938         # Load address
939         load_value_into_register func, "#{@FUNCTION}"
940       end
942       # Restore saved registers
943       [@NREGISTER_LOCALS, @environment.locals].min.times do |n|
944         emit "lw #{local_register n}, #{local_reference n}\n"
945       end
947       # Restore return address
948       emit "lw $31, #{@RA_OFFSET}($fp)\n"
950       # Restore stack pointer
951       emit "move $sp, $fp\n"
953       # Restore frame pointer
954       emit "lw $fp, #{@FP_OFFSET}($fp)\n"
956       # Perform call
957       emit "jr #{@FUNCTION}\n"
958       # brach delay slot
959       emit "nop\n"
960     end
962     # Define a word with the given value
963     def word value
964       emit ".int #{value}\n"
965     end
967     # Write generated code to the given IO object.
968     def write io
969       io.puts ".set noat"
970       @sections.each do |section,code|
971         unless code.empty?
972           io.puts ".section #{section.to_s}"
973           io.puts code
974           io.puts
975         end
976       end
977     end
979   end
981   # Register class for big endian MIPS
982   Voodoo::CodeGenerator.register_generator MIPSGasGenerator,
983                                            :architecture => :mips,
984                                            :format => :gas
987   # Register class for little endian MIPS
988   Voodoo::CodeGenerator.register_generator MIPSGasGenerator,
989                                            :architecture => :mipsel,
990                                            :format => :gas