Make auto-bytes and auto-words work with non-immediate values on MIPS
[voodoo-lang.git] / lib / voodoo / generators / mips_gas_generator.rb
blob7e4f2a8da406234453e72b2efdd6cf2ebac59c8c
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_BITS = 2
103       @WORDSIZE = 1 << @WORDSIZE_BITS
104       @CODE_ALIGNMENT = 0
105       @DATA_ALIGNMENT = @WORDSIZE
106       @STACK_ALIGNMENT_BITS = 3
107       @STACK_ALIGNMENT = 1 << @STACK_ALIGNMENT_BITS
108       @FP_OFFSET = -3 * @WORDSIZE
109       @FUNCTION_ALIGNMENT = @WORDSIZE
110       @GP_OFFSET = -2 * @WORDSIZE
111       @INITIAL_FRAME_SIZE = 4 * @WORDSIZE
112       @REGISTER_ARG_BASE = 4
113       @NREGISTER_ARGS = 4
114       @REGISTER_LOCAL_BASE = 16
115       @NREGISTER_LOCALS = 8
116       @RA_OFFSET = -@WORDSIZE
117       @RETURN = :'$2'
118       @TEMPORARY = :'$1'
119       @FUNCTION = :'$25'
120       @GOT = :'$28'
121       @TEMPORARIES = [:'$8', :'$9', :'$10', :'$11',
122                       :'$12', :'$13', :'$14', :'$15']
123       @function_end_label = nil
124       @imports = {}
125       @if_labels = []
126       super params
127       @output_file_suffix = '.s'
128       @features.merge! \
129         :'bits-per-word' => '32',
130         :'bytes-per-word' => '4'
131       case @architecture
132       when :mips
133         @features[:'byte-order'] = 'big-endian'
134       when :mipsel
135         @features[:'byte-order'] = 'little-endian'
136       else
137         raise ArgumentError.new("#{self.class} does not support " +
138                                 "architecture #{@architecture}")
139       end
140     end
142     # Adds immediate to source and stores the result in target.
143     def addiu target, source, immediate, temporary = @TEMPORARY
144       if immediate >= -32768 && immediate < 32767
145         emit "addiu #{target}, #{source}, #{immediate}\n"
146       elsif source == "$0"
147         emit "lui #{target}, #{(immediate >> 16) & 0xffff}\n"
148         emit "ori #{target}, #{target}, #{immediate & 0xffff}\n"
149       else
150         addiu temporary, "$0", immediate
151         emit "addu #{target}, #{source}, #{temporary}\n"
152       end
153     end
155     def align alignment = nil
156       unless alignment
157         # Get default alignment
158         case @section
159         when :code
160           alignment = @CODE_ALIGNMENT
161         when :data
162           alignment = @DATA_ALIGNMENT
163         when :function
164           alignment = @FUNCTION_ALIGNMENT
165         else
166           # Use data alignment as default
167           alignment = @DATA_ALIGNMENT
168         end
169       end
170       emit ".align #{alignment}\n" unless alignment == 0
171     end
173     # Return the offset from the frame base at which the nth argument is
174     # stored.
175     def arg_offset n
176       n * @WORDSIZE
177     end
179     # Return an $fp-relative reference for the nth (0-based) argument
180     def arg_reference n
181       offset_reference(arg_offset(n))
182     end
184     # Return the register in which the nth (0-based) argument is stored, or
185     # nil if not stored in a register
186     def arg_register n
187       if register_arg? n
188         "$#{@REGISTER_ARG_BASE + n}"
189       else
190         nil
191       end
192     end
194     # Test if op is a binary operation
195     def assymetric_binop? op
196       [:asr, :bsr, :div, :mod, :rol, :ror, :shl, :shr, :sub].member?(op)
197     end
199     # Test if a value is an at-expression
200     def at_expr? value
201       value.respond_to?(:[]) && value[0] == :'@'
202     end
204     def auto_bytes n, register
205       if n.kind_of? Integer
206         # Maintain stack alignment
207         n = (n + @STACK_ALIGNMENT - 1) / @STACK_ALIGNMENT * @STACK_ALIGNMENT
208         grow_stack n
209       else
210         temporary = @TEMPORARIES.pop
211         begin
212           load_value_into_register n, register
213           emit "addi #{register}, #{@STACK_ALIGNMENT - 1}\n"
214           emit "li #{temporary}, -#{@STACK_ALIGNMENT}\n"
215           emit "and #{register}, #{register}, #{temporary}\n"
216           emit "sub $sp, #{register}\n"
217         ensure
218           @TEMPORARIES.push temporary
219         end
220       end
221       emit "move #{register}, $sp\n" unless register == "$sp"
222     end
224     def auto_words n, register
225       if n.kind_of? Integer
226         auto_bytes n * @WORDSIZE, register
227       else
228         load_value_into_register n, register
229         if @STACK_ALIGNMENT_BITS > @WORDSIZE_BITS
230           bits = @STACK_ALIGNMENT_BITS - @WORDSIZE_BITS
231           emit "addi #{register}, #{(1 << bits) - 1}\n"
232           emit "srl #{register}, #{register}, #{bits}\n"
233           emit "sll #{register}, #{register}, #{@STACK_ALIGNMENT_BITS}\n"
234         else
235           emit "sll #{register}, #{register}, #{@WORDSIZE_BITS}\n"
236         end
237         emit "sub $sp, $sp, #{register}\n"
238         emit "move #{register}, $sp\n" unless register == "$sp"
239       end
240     end
242     # Begins a new block.
243     def begin_block *code
244       emit "# begin block\n"
245       # If we are at top-level, create a frame
246       if @environment == @top_level
247         create_frame count_locals(code)
248       end
249       environment = Environment.new @environment
250       @environment = environment
251     end
253     # Emit function prologue and declare _formals_ as function arguments
254     def begin_function formals, nlocals
255       if @environment != @top_level
256         raise "Cannot begin function when already in a function"
257       end
259       environment = Environment.new @environment
260       @frame_size = 0
261       formals.each {|x| environment.add_arg x, arg_offset(environment.args)}
262       @environment = environment
263       @function_end_label = gensym
264       emit_function_prologue formals, nlocals
265     end
267     # Test if op is a binary operation
268     def binop? op
269       assymetric_binop?(op) || symmetric_binop?(op)
270     end
272     # Define a byte with the given value
273     def byte value
274       emit ".byte #{value}\n"
275     end
277     # Call a function.
278     def call func, *args
279       # Grow the stack frame, subject to 3 conditions:
280       # 1. There must be enough space to hold all arguments
281       # 2. $sp must remain a multiple of 8
282       # 3. We must provide space for at least 4 words
283       increment = ([args.length, 4].max * @WORDSIZE + 7) / 8 * 8
284       grow_stack increment
286       # Put arguments in the right places
287       n = 0
288       while n < args.length
289         if n < @NREGISTER_ARGS
290           # Put arguments up to @NREGISTER_ARGS in registers
291           load_value_into_register args[n], arg_register(n)
292         else
293           # Put other arguments on the stack
294           load_value_into_register args[n], @TEMPORARY
295           emit "sw #{@TEMPORARY}, #{n * @WORDSIZE}($sp)\n"
296         end
297         n = n + 1
298       end
300       # Load function address
301       if global? func
302         if @imports.has_key? func
303           # Load address from global offset table
304           emit "lw #{@FUNCTION}, %call16(#{func})(#{@GOT})\n"
305         else
306           # Assume label defined in this module
307           emit "lui #{@FUNCTION}, %hi(#{func})\n"
308           emit "addiu #{@FUNCTION}, %lo(#{func})\n"
309         end
310       else
311         # Load address
312         load_value_into_register func, "#{@FUNCTION}"
313       end
315       # Perform call
316       emit "jalr #{@FUNCTION}\n"
317       # brach delay slot
318       emit "nop\n"
320       # Restore original stack frame
321       grow_stack -increment
323       # restore gp
324       emit "lw #{@GOT}, #{@GP_OFFSET}($fp)\n"
325     end
327     # Emits a comment.
328     def comment text
329       emit "# #{text}\n"
330     end
332     # Start a conditional using the specified branch instruction
333     # after the comparison.
334     def common_if comp, x, y = nil
335       emit "# #{comp} #{x} #{y}\n"
337       temporaries = @TEMPORARIES.dup
338       xreg = load_value x, temporaries[0]
339       temporaries.delete xreg
340       yreg = load_value y, temporaries[0]
341       temporaries.delete yreg
343       falselabel = @environment.gensym
344       @if_labels.push falselabel
346       case comp
347       when :ifeq
348         emit "bne #{xreg}, #{yreg}, #{falselabel}\n"
349       when :ifge
350         emit "slt #{@TEMPORARY}, #{xreg}, #{yreg}\n"
351         emit "bne #{@TEMPORARY}, $0, #{falselabel}\n"
352       when :ifgt
353         emit "slt #{@TEMPORARY}, #{yreg}, #{xreg}\n"
354         emit "beq #{@TEMPORARY}, $0, #{falselabel}\n"
355       when :ifle
356         emit "slt #{@TEMPORARY}, #{yreg}, #{xreg}\n"
357         emit "bne #{@TEMPORARY}, $0, #{falselabel}\n"
358       when :iflt
359         emit "slt #{@TEMPORARY}, #{xreg}, #{yreg}\n"
360         emit "beq #{@TEMPORARY}, $0, #{falselabel}\n"
361       when :ifne
362         emit "beq #{xreg}, #{yreg}, #{falselabel}\n"
363       else
364         raise "Unknown conditional: #{comp}"
365       end
366       emit "nop\n"
367     end
369     # Creates an activation frame which can store nlocals local variables
370     def create_frame nlocals
371       @frame_size = @INITIAL_FRAME_SIZE
372       if nlocals > 0
373         @frame_size = @frame_size + (nlocals * @WORDSIZE + 7) / 8 * 8
374       end
375       grow_stack @frame_size
376     end
378     # Emit function prologue.
379     def emit_function_prologue formals = [], nlocals = 0
380       # Calculate new value for $gp
381       emit "lui #{@GOT}, %hi(_gp_disp)\n"
382       emit "addiu #{@GOT}, %lo(_gp_disp)\n"
383       emit "addu #{@GOT}, #{@GOT}, #{@FUNCTION}\n"
385       create_frame nlocals
387       # Save return address
388       emit "sw $31, #{@frame_size - @WORDSIZE}($sp)\n"
390       # Save gp
391       emit "sw #{@GOT}, #{@frame_size - 2 * @WORDSIZE}($sp)\n"
393       # Save fp
394       emit "sw $fp, #{@frame_size - 3 * @WORDSIZE}($sp)\n"
396       # Point fp at where sp was before we created the frame
397       addiu "$fp", "$sp", @frame_size
399       # Save parameters 0 .. 3 in the stack space that the caller
400       # should have allocated for them.
401       [@NREGISTER_ARGS, formals.length].min.times do |n|
402         ref = offset_reference(@environment[formals[n]])
403         emit "sw $#{n + @REGISTER_ARG_BASE}, #{ref}\n"
404       end
405     end
407     # End a function body
408     def end_function
409       if @environment == @top_level
410         raise "Cannot end function when not in a function"
411       end
413       label @function_end_label
414       # Restore saved locals
415       [@NREGISTER_LOCALS, @environment.locals].min.times do |n|
416         emit "lw #{local_register n}, #{local_reference n}\n"
417       end
418       # Load return address
419       emit "lw $31, #{@RA_OFFSET}($fp)\n"
420       # Restore stack pointer
421       emit "move $sp, $fp\n"
422       # Restore frame pointer
423       emit "lw $fp, #{@FP_OFFSET}($fp)\n"
424       # Return
425       emit "jr $31\n"
426       emit "nop\n"
428       emit "# end function\n\n"
430       @function_end_label = nil
431       @environment = @top_level
432     end
434     # Ends the current block.
435     def end_block
436       emit "# end block\n"
438       # If we are returning to top level, restore stack pointer
439       if @environment.parent == @top_level
440         grow_stack -@frame_size
441       end
443       # Restore old value of @environment
444       @environment = @environment.parent
445     end
447     # End a conditional.
448     def end_if
449       label = @if_labels.pop
450       emit "#{label}:\n"
451     end
453     # Evaluate the binary operation expr and store the result in register
454     def eval_binop expr, register
455       temporaries = @TEMPORARIES.dup
456       x = load_value expr[1], temporaries[0]
457       temporaries.delete x
458       y = load_value expr[2], temporaries[0]
459       temporaries.delete y
461       case expr[0]
462       when :asr
463         emit "srav #{register}, #{x}, #{y}\n"
464       when :bsr
465         emit "srlv #{register}, #{x}, #{y}\n"
466       when :div
467         emit "div $0, #{x}, #{y}\n"
468         emit "mflo #{register}\n"
469       when :mod
470         emit "div $0, #{x}, #{y}\n"
471         emit "mfhi #{register}\n"
472       when :mul
473         emit "mult #{x}, #{y}\n"
474         emit "mflo #{register}\n"
475       when :rol
476         emit "subu #{@TEMPORARY}, $0, #{y}\n"
477         emit "srlv #{@TEMPORARY}, #{x}, #{@TEMPORARY}\n"
478         emit "sllv #{register}, #{x}, #{y}\n"
479         emit "or #{register}, #{register}, #{@TEMPORARY}\n"
480       when :ror
481         emit "subu #{@TEMPORARY}, $0, #{y}\n"
482         emit "sllv #{@TEMPORARY}, #{x}, #{@TEMPORARY}\n"
483         emit "srlv #{register}, #{x}, #{y}\n"
484         emit "or #{register}, #{register}, #{@TEMPORARY}\n"
485       when :shl
486         emit "sllv #{register}, #{x}, #{y}\n"
487       when :shr
488         emit "srlv #{register}, #{x}, #{y}\n"
489       else
490         emit "#{expr[0]} #{register}, #{x}, #{y}\n"
491       end
492     end
494     # Evaluate the expression expr and store the result in register
495     def eval_expr expr, register
496       if expr.length == 1
497         # Load value
498         load_value_into_register expr[0], register
499       else
500         # Evaluate expression
501         op = expr[0]
502         case op
503         when :'auto-bytes'
504           auto_bytes expr[1], register
505         when :'auto-words'
506           auto_words expr[1], register
507         when :call
508           call *expr[1..-1]
509           emit "move #{register}, #{@RETURN}\n" if register != @RETURN
510         when :'get-byte'
511           get_byte expr[1], expr[2], register
512         when :'get-word'
513           get_word expr[1], expr[2], register
514         when :not
515             eval_binop [:nor, 0, expr[1]], register
516         else
517           if binop? op
518             eval_binop expr, register
519           else
520             raise "Not a magic word: #{op}"
521           end
522         end
523       end
524     end
526     # Export symbols from the current section
527     def export *symbols
528       symbols.each { |sym| emit ".globl #{sym}\n" }
529     end
531     # Load byte from _base_ + _offset_ into _register_
532     def get_byte base, offset, register
533       # If base is an integer, but offset isn't, swap them
534       if !integer?(offset) && integer?(base)
535         base, offset = [offset, base]
536       end
538       if integer? offset
539         base_reg = load_value base
540         emit "lb #{register}, #{offset}(#{base_reg})\n"
541       else
542         eval_binop [:add, base, offset], @TEMPORARY
543         emit "lb #{register}, 0(#{@TEMPORARY})\n"
544       end
545     end
547     # Load word from _base_ + _offset_ * _@WORDSIZE_ into _register_
548     def get_word base, offset, register
549       if integer? offset
550         base_reg = load_value base
551         emit "lw #{register}, #{offset * @WORDSIZE}(#{base_reg})\n"
552       else
553         offset_reg = @TEMPORARIES.pop
554         begin
555           load_value_into_register offset, offset_reg
556           base_reg = load_value base
557           emit "sll #{offset_reg}, #{offset_reg}, 2\n" # multiply by @WORDSIZE
558           emit "add #{@TEMPORARY}, #{base_reg}, #{offset_reg}\n"
559           emit "lw #{register}, 0(#{@TEMPORARY})\n"
560         ensure
561           @TEMPORARIES.push offset_reg
562         end
563       end
564     end
566     # Test if a symbol refers to a global
567     def global? symbol
568       symbol?(symbol) && @environment[symbol] == nil
569     end
571     # Jump to a label.
572     def goto label
573       emit "j #{label}\n"
574       emit "nop\n"
575     end
577     # Grows the stack by n bytes.
578     def grow_stack n
579       addiu "$sp", "$sp", -n
580     end
582     # Start the false path of a conditional.
583     def ifelse
584       emit "# else\n"
585       newlabel = @environment.gensym
586       emit "j #{newlabel}\n"
587       emit "nop\n"
588       label = @if_labels.pop
589       emit "#{label}:\n"
590       @if_labels.push newlabel
591     end
593     # Test if x is equal to y
594     def ifeq x, y
595       common_if :ifeq, x, y
596     end
598     # Test if x is greater than or equal to y
599     def ifge x, y
600       common_if :ifge, x, y
601     end
603     # Test if x is strictly greater than y
604     def ifgt x, y
605       common_if :ifgt, x, y
606     end
608     # Test if x is less than or equal to y
609     def ifle x, y
610       common_if :ifle, x, y
611     end
613     # Test if x is strictly less than y
614     def iflt x, y
615       common_if :iflt, x, y
616     end
618     # Test if x different from y
619     def ifne x, y
620       common_if :ifne, x, y
621     end
623     # Import labels into the current section
624     def import *symbols
625       # Record imported labels in @imports
626       symbols.each { |sym| @imports[sym] = sym }
627     end
629     # Test if a value is an integer
630     def integer? value
631       value.kind_of? Integer
632     end
634     # Emit a label
635     def label name
636       emit "#{name}:\n"
637     end
639     # Introduces a new local variable.
640     def let symbol, *expr
641       emit "# let #{symbol} #{expr.join ' '}\n"
642       n = @environment.locals
643       register = local_register n
644       ref = local_reference n
645       if register
646         # We will use a register to store the value
647         @environment.add_local symbol, register
648         # Save current value of register
649         emit "sw #{register}, #{ref}\n"
650         # Set new value
651         eval_expr expr, register
652       else
653         # We will use the stack to store the value
654         @environment.add_local symbol, local_offset(n)
655         eval_expr expr, @TEMPORARY
656         emit "sw #{@TEMPORARY}, #{ref}\n"
657       end
658     end
660     # Loads the value at the given address.
661     def load_at address, register = @TEMPORARY
662       load_value_into_register address, register
663       emit "lw #{register}, 0(#{register})\n"
664       register
665     end
667     # Loads a value into a register.
668     # Returns the name of the register.
669     # If the value was already in a register, the name of that
670     # register is returned.
671     # Else, the value is loaded into a register and the name of
672     # that register is returned. The register to use in that case
673     # may be specified using the optional second argument.
674     def load_value x, register = @TEMPORARY
675       if x == 0
676         return "$0"
677       elsif integer? x
678         addiu register, "$0", x
679         return register
680       elsif symbol? x
681         binding = @environment[x]
682         if binding.kind_of? String
683           # Value is already in a register. Return register name.
684           return binding
685         elsif binding.kind_of? Integer
686           # Load value from memory.
687           emit "lw #{register}, #{offset_reference binding}\n"
688           return register
689         else
690           # Assume global
691           emit "lui #{register}, %hi(#{x})\n"
692           emit "addiu #{register}, %lo(#{x})\n"
693           return register
694         end
695       elsif at_expr? x
696         load_at x[1], register
697       else
698         raise "Don't know how to load #{x.inspect}"
699       end
700     end
702     # Load a value into a specific register
703     def load_value_into_register x, register
704       reg = load_value x, register
705       if reg != register
706         emit "move #{register}, #{reg}\n"
707       end
708     end
710     # Return the offset from the frame base at which the nth local is stored.
711     # For register locals this is the offset at which the saved
712     # value (from the calling function) is stored.
713     def local_offset n
714       -(n + 4) * @WORDSIZE
715     end
717     # Return an $sp-relative reference for the nth (0-based) local
718     def local_reference n
719       offset_reference(local_offset(n))
720     end
722     # Return the register in which the nth local (0-based) is stored, or
723     # nil if not stored in a register
724     def local_register n
725       if register_local? n
726         "$#{@REGISTER_LOCAL_BASE + n}"
727       else
728         nil
729       end
730     end
732     # Compute the maximum number of locals that would fit in the current
733     # frame size.
734     def max_locals
735       # + 1, because the initial frame size has room for 1 local
736       (@frame_size - @INITIAL_FRAME_SIZE) / @WORDSIZE + 1
737     end
739     # Calculate the number of stack arguments,
740     # given the total number of arguments.
741     def number_of_stack_arguments n
742       [0, n - @NREGISTER_ARGS].max
743     end
745     # Given an offset relative to the base of the frame, returns
746     # an reference to that memory location.
747     def offset_reference offset
748       "#{offset}($fp)"
749     end
751     # Returns true if the nth (0-based) argument is stored in a register
752     def register_arg? n
753       n < @NREGISTER_ARGS
754     end
756     # Returns true if the nth (0-based) local is stored in a register
757     def register_local? n
758       n < @NREGISTER_LOCALS
759     end
761     # Return a from a function.
762     # 
763     # _words_ may contain an expression to be evaluated. The result
764     # of the evaluation is returned from the function.
765     def ret *words
766       emit "# return #{words.join ' '}\n"
767       # Compute return value and store it in @RETURN
768       eval_expr(words, @RETURN) unless words.empty?
769       # Go to epilogue
770       goto @function_end_label
771     end
772     
773     # Set a variable to the result of evaluating an expression
774     def set symbol, *expr
775       if at_expr? symbol
776         eval_expr expr, @RETURN
777         load_value_into_register symbol.to_s[1..-1].to_sym, @TEMPORARY
778         emit "sw #{@RETURN}, 0(#{@TEMPORARY})\n"
779       else
780         x = @environment[symbol]
781         if x == nil
782           raise "Cannot change value of constant #{symbol}"
783         elsif x.kind_of? String
784           # Register
785           eval_expr expr, x
786         else
787           # Should be an integer.
788           eval_expr expr, @TEMPORARY
789           emit "sw #{@TEMPORARY}, #{offset_reference x}\n"
790         end
791       end
792     end
794     # Set the byte at _base_ + _offset_ to _value_
795     def set_byte base, offset, value
796       emit "# set-byte #{base} #{offset} #{value}\n"
797       # If base is an integer, but offset isn't, swap them
798       if !integer?(offset) && integer?(base)
799         base, offset = [offset, base]
800       end
802       value_reg = @TEMPORARIES.pop
803       load_value_into_register value, value_reg
804       begin
805         if integer? offset
806           base_reg = load_value base
807           emit "sb #{value_reg}, #{offset}(#{base_reg})\n"
808         else
809           eval_binop [:add, base, offset], @TEMPORARY
810           emit "sb #{value_reg}, 0(#{@TEMPORARY})\n"
811         end
812       ensure
813         @TEMPORARIES.push value_reg
814       end
815     end
817     # Set the word at _base_ + _offset_ * +@WORDSIZE+ to _value_
818     def set_word base, offset, value
819       emit "# set-word #{base} #{offset} #{value}\n"
821       value_reg = @TEMPORARIES.pop
822       load_value_into_register value, value_reg
823       begin
824         if integer? offset
825           base_reg = load_value base
826           emit "sw #{value_reg}, #{offset * @WORDSIZE}(#{base_reg})\n"
827         else
828           offset_reg = @TEMPORARIES.pop
829           begin
830             load_value_into_register offset, offset_reg
831             base_reg = load_value base
832             emit "sll #{offset_reg}, #{offset_reg}, 2\n"
833             emit "add #{@TEMPORARY}, #{base_reg}, #{offset_reg}\n"
834             emit "sw #{value_reg}, 0(#{@TEMPORARY})\n"
835           ensure
836             @TEMPORARIES.push offset_reg
837           end
838         end
839       ensure
840         @TEMPORARIES.push value_reg
841       end
842     end
844     # Define a string with the given value
845     def string value
846       code = ''
847       value.each_byte do |b|
848         if b == 92
849           code << "\\\\"
850         elsif b >= 32 && b < 127 && b != 34
851           code << b.chr
852         else
853           code << sprintf("\\%03o", b)
854         end
855       end
856       emit ".ascii \"#{code}\"\n"
857     end
859     # Test if a value is a symbol
860     def symbol? value
861       value.kind_of? Symbol
862     end
864     # Test if op is a symmetric binary operation (i.e. it will yield the
865     # same result if the order of its source operands is changed).
866     def symmetric_binop? op
867       [:add, :and, :mul, :or, :xor].member? op
868     end
870     # Call a function, re-using the current call frame if possible.
871     def tail_call func, *args
872       emit "# tail-call #{func} #{args.join ' '}\n"
874       # Compute number of stack arguments
875       nstackargs = number_of_stack_arguments args.length
876       # If we need more stack arguments than we have now,
877       # perform a normal call and return
878       if nstackargs > number_of_stack_arguments(@environment.args)
879         emit "# Not enough space for proper tail call; using regular call\n"
880         ret :call, func, *args
881       end
883       # Back up any stack arguments we will need after we overwrite them
884       temporaries = @TEMPORARIES.dup
885       nlocals = @environment.locals
886       (@NREGISTER_ARGS...args.length).each do |i|
887         x = @environment[args[i]]
888         if x && x[0] == :arg && x[1] >= @NREGISTER_ARGS && x[1] < i
889           # args[i] is a stack arg, but has an index < i
890           # That means that, by the time we get to pass arg[i] to the called
891           # function, it will have been overwritten. So we make a backup.
892           if temporaries.empty?
893             # Oh dear, we're out of temporaries.
894             # Store the value in the current stack frame, extending it
895             # as necessary.
896             if nlocals >= max_locals
897               grow_stack 8
898             end
899             reg = load_value args[i]
900             emit "sw #{reg}, #{local_reference nlocals}\n"
901             args[i] = [:local, nlocals]
902             nlocals = nlocals + 1
903           else
904             # Load the argument into a temporary
905             reg = temporaries.shift
906             load_value_into_register args[i], reg
907             # Update args[i] so we know how to get to it later
908             args[i] = [:reg, reg]
909           end
910         end
911       end
913       # Set stack arguments
914       (@NREGISTER_ARGS...args.length).each do |i|
915         arg = args[i]
916         reg = @TEMPORARY
918         # Load value
919         if arg.kind_of? Array
920           # Special cases, created above
921           case arg[0]
922           when :reg
923             reg = arg[1]
924           when :local
925             emit "lw #{reg}, #{local_reference arg[1]}\n"
926           end
927         else
928           load_value_into_register arg, reg
929         end
931         # Store value in designated place
932         emit "sw #{reg}, #{arg_reference i}\n"
933       end
935       # Set register arguments
936       [@NREGISTER_ARGS,args.length].min.times do |i|
937         reg = arg_register i
938         load_value_into_register args[i], reg
939       end
941       # Load function address
942       if global? func
943         if @imports.has_key? func
944           # Load address from global offset table
945           emit "lw #{@FUNCTION}, %call16(#{func})(#{@GOT})\n"
946         else
947           # Assume label defined in this module
948           emit "lui #{@FUNCTION}, %hi(#{func})\n"
949           emit "addiu #{@FUNCTION}, %lo(#{func})\n"
950         end
951       else
952         # Load address
953         load_value_into_register func, "#{@FUNCTION}"
954       end
956       # Restore saved registers
957       [@NREGISTER_LOCALS, @environment.locals].min.times do |n|
958         emit "lw #{local_register n}, #{local_reference n}\n"
959       end
961       # Restore return address
962       emit "lw $31, #{@RA_OFFSET}($fp)\n"
964       # Restore stack pointer
965       emit "move $sp, $fp\n"
967       # Restore frame pointer
968       emit "lw $fp, #{@FP_OFFSET}($fp)\n"
970       # Perform call
971       emit "jr #{@FUNCTION}\n"
972       # brach delay slot
973       emit "nop\n"
974     end
976     # Define a word with the given value
977     def word value
978       emit ".int #{value}\n"
979     end
981     # Write generated code to the given IO object.
982     def write io
983       io.puts ".set noat"
984       @sections.each do |section,code|
985         unless code.empty?
986           io.puts ".section #{section.to_s}"
987           io.puts code
988           io.puts
989         end
990       end
991     end
993   end
995   # Register class for big endian MIPS
996   Voodoo::CodeGenerator.register_generator MIPSGasGenerator,
997                                            :architecture => :mips,
998                                            :format => :gas
1001   # Register class for little endian MIPS
1002   Voodoo::CodeGenerator.register_generator MIPSGasGenerator,
1003                                            :architecture => :mipsel,
1004                                            :format => :gas