removed no longer necessary code from mips_gas_generator
[voodoo-lang.git] / lib / voodoo / generators / mips_gas_generator.rb
blob6ccb370c9fb145de9632cf6e8a73be633c734443
1 require 'voodoo/generators/common_code_generator'
2 require 'set'
4 module Voodoo
5   # = MIPS GNU Assembler Code Generator
6   #
7   # The MIPS code generator generates assembly code for use with
8   # the GNU assembler.
9   #
10   # == Calling Convention
11   #
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.
16   #
17   # The return address for the called function is passed in $31.
18   #
19   # When performing a position-independent call, the address of the called
20   # function is passed in $25.
21   #
22   # The called function will store its return value in $2.
23   #
24   # The called function is required to preserve the values of registers
25   # $16 through $23 and register $30.
26   #
27   # This calling convention is compatible with the System V ELF ABI.
28   #
29   # == Call Frames
30   #
31   # Call frames have the following layout:
32   #
33   # When a function is called, it receives a stack frame that looks like
34   # the following:
35   #
36   #   :
37   #   old frame
38   #   argn
39   #   :
40   #   arg4
41   #   empty3
42   #   empty2
43   #   empty1
44   #   empty0    <-- $sp points here
45   #
46   # The function prologue of functions generated by this code generator
47   # creates activation frames that look as follows:
48   #
49   # :
50   # old frame
51   # argn
52   # :
53   # arg4
54   # arg3
55   # arg2
56   # arg1
57   # arg0       <-- $fp points here
58   # return address
59   # pointer to Global Offset Table
60   # saved $fp
61   # local0
62   # :
63   # local7
64   # local8
65   # :
66   # localn
67   # padding    <-- $sp points here
68   #
69   # In words:
70   #
71   # The four empty slots provided by the caller are used to store
72   # arguments 0 through 3 (originally passed in $4 through $7),
73   # if necessary.
74   #
75   # The stack frame created below the four slots provided by the caller
76   # contains the following data, in order:
77   #
78   # - Saved return address (originally passed in $31), if necessary.
79   #
80   # - Saved pointer to global offset table (computed from $25), if necessary.
81   #
82   # - Saved value of $fp, if necessary.
83   #
84   # - Saved values of caller's locals 0 through 7
85   #   (originally in $16 through $23), if necessary.
86   #
87   # - Values of our locals > 8, if necessary.
88   #
89   # - Padding to align $sp on a multiple of 8, if necessary.
90   #
91   #
92   # In accordance with the System V ELF ABI for MIPS, the pointer to
93   # the global offset table is calculated as follows:
94   #
95   # $gp = _gp_disp + $25
96   #
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.
100   #
101   # Callee-Save Registers
102   #
103   # Registers $16 through $23 and $28 through $30 are callee-save.
104   #
105   # All other registers are caller-save.
106   #
107   class MIPSGasGenerator < CommonCodeGenerator
108     def initialize params
109       @WORDSIZE_BITS = 2
110       @WORDSIZE = 1 << @WORDSIZE_BITS
111       @CODE_ALIGNMENT = 0
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
120       @NREGISTER_ARGS = 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
127       @RETURN = :'$2'
128       @TEMPORARY = :'$1'
129       @FUNCTION = :'$25'
130       @GOT = :'$28'
131       @SAVE_FRAME_REGISTERS = [:'$16', :'$17', :'$18', :'$19', :'$20',
132                                :'$21', :'$22', :'$23', :'$28', :'$29',
133                                :'$30']
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
139       @imports = {}
140       @if_labels = []
141       super params
142       @output_file_suffix = '.s'
143       @features.merge! \
144         :'bits-per-word' => '32',
145         :'bytes-per-word' => '4'
146       case @architecture
147       when :mips
148         @features[:'byte-order'] = 'big-endian'
149       when :mipsel
150         @features[:'byte-order'] = 'little-endian'
151       else
152         raise ArgumentError.new("#{self.class} does not support " +
153                                 "architecture #{@architecture}")
154       end
155     end
157     # Adds immediate to source and stores the result in target.
158     def addiu target, source, immediate, temporary = @TEMPORARY
159       if immediate >= -32768 && immediate < 32767
160         emit "addiu #{target}, #{source}, #{immediate}\n"
161       elsif source == "$0"
162         emit "lui #{target}, #{(immediate >> 16) & 0xffff}\n"
163         emit "ori #{target}, #{target}, #{immediate & 0xffff}\n"
164       else
165         addiu temporary, "$0", immediate
166         emit "addu #{target}, #{source}, #{temporary}\n"
167       end
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     # Returns an $fp-relative reference for the nth (0-based) argument.
177     def arg_reference n
178       offset_reference(arg_offset(n))
179     end
181     # Returns 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     def auto_bytes n, register
192       if n.kind_of? Integer
193         # Maintain stack alignment
194         n = (n + @STACK_ALIGNMENT - 1) / @STACK_ALIGNMENT * @STACK_ALIGNMENT
195         grow_stack n
196       else
197         temporary = @TEMPORARIES.pop
198         begin
199           load_value_into_register n, register
200           emit "addi #{register}, #{@STACK_ALIGNMENT - 1}\n"
201           emit "li #{temporary}, -#{@STACK_ALIGNMENT}\n"
202           emit "and #{register}, #{register}, #{temporary}\n"
203           emit "sub $sp, #{register}\n"
204         ensure
205           @TEMPORARIES.push temporary
206         end
207       end
208       emit "move #{register}, $sp\n" unless register == "$sp"
209     end
211     def auto_words n, register
212       if n.kind_of? Integer
213         auto_bytes n * @WORDSIZE, register
214       else
215         load_value_into_register n, register
216         if @STACK_ALIGNMENT_BITS > @WORDSIZE_BITS
217           bits = @STACK_ALIGNMENT_BITS - @WORDSIZE_BITS
218           emit "addi #{register}, #{(1 << bits) - 1}\n"
219           emit "srl #{register}, #{register}, #{bits}\n"
220           emit "sll #{register}, #{register}, #{@STACK_ALIGNMENT_BITS}\n"
221         else
222           emit "sll #{register}, #{register}, #{@WORDSIZE_BITS}\n"
223         end
224         emit "sub $sp, $sp, #{register}\n"
225         emit "move #{register}, $sp\n" unless register == "$sp"
226       end
227     end
229     # Begins a new block.
230     def begin_block *code
231       # If we are at top-level, create a frame
232       if @environment == @top_level
233         create_frame count_locals(code)
234       end
235       environment = Environment.new @environment
236       @environment = environment
237     end
239     # Emits function prologue and declare _formals_ as function arguments.
240     def begin_function formals, nlocals
241       if @environment != @top_level
242         raise "Cannot begin function when already in a function"
243       end
245       environment = Environment.new @environment
246       @frame_size = 0
247       formals.each {|x| environment.add_arg x, arg_offset(environment.args)}
248       @environment = environment
249       @function_end_label = gensym
250       emit_function_prologue formals, nlocals
251     end
253     # Defines a byte with the given value.
254     def byte value
255       emit ".byte #{value}\n"
256     end
258     # Calls a function.
259     def call func, *args
260       # Grow the stack frame, subject to 3 conditions:
261       # 1. There must be enough space to hold all arguments
262       # 2. $sp must remain a multiple of 8
263       # 3. We must provide space for at least 4 words
264       increment = ([args.length, 4].max * @WORDSIZE + 7) / 8 * 8
265       grow_stack increment
267       # Put arguments in the right places
268       n = 0
269       while n < args.length
270         if n < @NREGISTER_ARGS
271           # Put arguments up to @NREGISTER_ARGS in registers
272           load_value_into_register args[n], arg_register(n)
273         else
274           # Put other arguments on the stack
275           load_value_into_register args[n], @TEMPORARY
276           emit "sw #{@TEMPORARY}, #{n * @WORDSIZE}($sp)\n"
277         end
278         n = n + 1
279       end
281       # Load function address
282       if global? func
283         if @imports.has_key? func
284           # Load address from global offset table
285           emit "lw #{@FUNCTION}, %call16(#{func})(#{@GOT})\n"
286         else
287           # Assume label defined in this module
288           emit "lui #{@FUNCTION}, %hi(#{func})\n"
289           emit "addiu #{@FUNCTION}, %lo(#{func})\n"
290         end
291       else
292         # Load address
293         load_value_into_register func, "#{@FUNCTION}"
294       end
296       # Perform call
297       emit "jalr #{@FUNCTION}\n"
298       # brach delay slot
299       emit "nop\n"
301       # Restore original stack frame
302       grow_stack -increment
304       # restore gp
305       emit "lw #{@GOT}, #{@GP_OFFSET}($fp)\n"
306     end
308     # Emits a comment.
309     def comment text
310       emit "# #{text}\n"
311     end
313     # Starts a conditional using the specified branch instruction
314     # after the comparison.
315     def common_if comp, x, y = nil
316       temporaries = @TEMPORARIES.dup
317       xreg = load_value x, temporaries[0]
318       temporaries.delete xreg
319       yreg = load_value y, temporaries[0]
320       temporaries.delete yreg
322       falselabel = @environment.gensym
323       @if_labels.push falselabel
325       case comp
326       when :ifeq
327         emit "bne #{xreg}, #{yreg}, #{falselabel}\n"
328       when :ifge
329         emit "slt #{@TEMPORARY}, #{xreg}, #{yreg}\n"
330         emit "bne #{@TEMPORARY}, $0, #{falselabel}\n"
331       when :ifgt
332         emit "slt #{@TEMPORARY}, #{yreg}, #{xreg}\n"
333         emit "beq #{@TEMPORARY}, $0, #{falselabel}\n"
334       when :ifle
335         emit "slt #{@TEMPORARY}, #{yreg}, #{xreg}\n"
336         emit "bne #{@TEMPORARY}, $0, #{falselabel}\n"
337       when :iflt
338         emit "slt #{@TEMPORARY}, #{xreg}, #{yreg}\n"
339         emit "beq #{@TEMPORARY}, $0, #{falselabel}\n"
340       when :ifne
341         emit "beq #{xreg}, #{yreg}, #{falselabel}\n"
342       else
343         raise "Unknown conditional: #{comp}"
344       end
345       emit "nop\n"
346     end
348     # Creates an activation frame which can store nlocals local variables.
349     def create_frame nlocals
350       @frame_size = @INITIAL_FRAME_SIZE
351       if nlocals > 0
352         @frame_size = @frame_size + (nlocals * @WORDSIZE + 7) / 8 * 8
353       end
354       grow_stack @frame_size
355     end
357     def emit_align alignment
358       emit ".align #{alignment}\n"
359     end
361     # Emits function prologue.
362     def emit_function_prologue formals = [], nlocals = 0
363       # Calculate new value for $gp
364       emit "lui #{@GOT}, %hi(_gp_disp)\n"
365       emit "addiu #{@GOT}, %lo(_gp_disp)\n"
366       emit "addu #{@GOT}, #{@GOT}, #{@FUNCTION}\n"
368       create_frame nlocals
370       # Save return address
371       emit "sw $31, #{@frame_size - @WORDSIZE}($sp)\n"
373       # Save gp
374       emit "sw #{@GOT}, #{@frame_size - 2 * @WORDSIZE}($sp)\n"
376       # Save fp
377       emit "sw $fp, #{@frame_size - 3 * @WORDSIZE}($sp)\n"
379       # Point fp at where sp was before we created the frame
380       addiu "$fp", "$sp", @frame_size
382       # Save parameters 0 .. 3 in the stack space that the caller
383       # should have allocated for them.
384       [@NREGISTER_ARGS, formals.length].min.times do |n|
385         ref = offset_reference(@environment[formals[n]])
386         emit "sw $#{n + @REGISTER_ARG_BASE}, #{ref}\n"
387       end
388     end
390     # Loads a word into a register.
391     def emit_load_word register, base, offset
392       emit "lw #{register}, #{offset * @WORDSIZE}(#{base})\n"
393     end
395     # Stores the value of a register in memory.
396     def emit_store_word register, base, offset
397       emit "sw #{register}, #{offset * @WORDSIZE}(#{base})\n"
398     end
400     # Ends a function body.
401     def end_function
402       if @environment == @top_level
403         raise "Cannot end function when not in a function"
404       end
406       label @function_end_label
407       # Restore saved locals
408       [@NREGISTER_LOCALS, @environment.locals].min.times do |n|
409         emit "lw #{local_register n}, #{local_reference n}\n"
410       end
411       # Load return address
412       emit "lw $31, #{@RA_OFFSET}($fp)\n"
413       # Restore stack pointer
414       emit "move $sp, $fp\n"
415       # Restore frame pointer
416       emit "lw $fp, #{@FP_OFFSET}($fp)\n"
417       # Return
418       emit "jr $31\n"
419       emit "nop\n"
421       @function_end_label = nil
422       @environment = @top_level
423     end
425     # Ends the current block.
426     def end_block
427       # If we are returning to top level, restore stack pointer
428       if @environment.parent == @top_level
429         grow_stack -@frame_size
430       end
432       # Restore old value of @environment
433       @environment = @environment.parent
434     end
436     # Ends a conditional.
437     def end_if
438       label = @if_labels.pop
439       emit "#{label}:\n"
440     end
442     # Evaluates the binary operation expr and store the result in register.
443     def eval_binop expr, register
444       temporaries = @TEMPORARIES.dup
445       x = load_value expr[1], temporaries[0]
446       temporaries.delete x
447       y = load_value expr[2], temporaries[0]
448       temporaries.delete y
450       case expr[0]
451       when :asr
452         emit "srav #{register}, #{x}, #{y}\n"
453       when :bsr
454         emit "srlv #{register}, #{x}, #{y}\n"
455       when :div
456         emit "div $0, #{x}, #{y}\n"
457         emit "mflo #{register}\n"
458       when :mod
459         emit "div $0, #{x}, #{y}\n"
460         emit "mfhi #{register}\n"
461       when :mul
462         emit "mult #{x}, #{y}\n"
463         emit "mflo #{register}\n"
464       when :rol
465         emit "subu #{@TEMPORARY}, $0, #{y}\n"
466         emit "srlv #{@TEMPORARY}, #{x}, #{@TEMPORARY}\n"
467         emit "sllv #{register}, #{x}, #{y}\n"
468         emit "or #{register}, #{register}, #{@TEMPORARY}\n"
469       when :ror
470         emit "subu #{@TEMPORARY}, $0, #{y}\n"
471         emit "sllv #{@TEMPORARY}, #{x}, #{@TEMPORARY}\n"
472         emit "srlv #{register}, #{x}, #{y}\n"
473         emit "or #{register}, #{register}, #{@TEMPORARY}\n"
474       when :shl
475         emit "sllv #{register}, #{x}, #{y}\n"
476       when :shr
477         emit "srlv #{register}, #{x}, #{y}\n"
478       else
479         emit "#{expr[0]} #{register}, #{x}, #{y}\n"
480       end
481     end
483     # Evaluates the expression expr and store the result in register.
484     def eval_expr expr, register
485       if expr.length == 1
486         # Load value
487         load_value_into_register expr[0], register
488       else
489         # Evaluate expression
490         op = expr[0]
491         case op
492         when :'auto-bytes'
493           auto_bytes expr[1], register
494         when :'auto-words'
495           auto_words expr[1], register
496         when :call
497           call *expr[1..-1]
498           emit "move #{register}, #{@RETURN}\n" if register != @RETURN
499         when :'get-byte'
500           get_byte expr[1], expr[2], register
501         when :'get-word'
502           get_word expr[1], expr[2], register
503         when :not
504             eval_binop [:nor, 0, expr[1]], register
505         else
506           if binop? op
507             eval_binop expr, register
508           else
509             raise "Not a magic word: #{op}"
510           end
511         end
512       end
513     end
515     # Exports symbols from the current section.
516     def export *symbols
517       symbols.each { |sym| emit ".globl #{sym}\n" }
518     end
520     # Loads byte from _base_ + _offset_ into _register_.
521     def get_byte base, offset, register
522       # If base is an integer, but offset isn't, swap them
523       if !integer?(offset) && integer?(base)
524         base, offset = [offset, base]
525       end
527       if integer? offset
528         base_reg = load_value base
529         emit "lb #{register}, #{offset}(#{base_reg})\n"
530       else
531         eval_binop [:add, base, offset], @TEMPORARY
532         emit "lb #{register}, 0(#{@TEMPORARY})\n"
533       end
534     end
536     # Loads word from _base_ + _offset_ * _@WORDSIZE_ into _register_.
537     def get_word base, offset, register
538       if integer? offset
539         base_reg = load_value base
540         emit "lw #{register}, #{offset * @WORDSIZE}(#{base_reg})\n"
541       else
542         offset_reg = @TEMPORARIES.pop
543         begin
544           load_value_into_register offset, offset_reg
545           base_reg = load_value base
546           emit "sll #{offset_reg}, #{offset_reg}, 2\n" # multiply by @WORDSIZE
547           emit "add #{@TEMPORARY}, #{base_reg}, #{offset_reg}\n"
548           emit "lw #{register}, 0(#{@TEMPORARY})\n"
549         ensure
550           @TEMPORARIES.push offset_reg
551         end
552       end
553     end
555     # Jumps to an address.
556     def goto address
557       if global? address
558         emit "j #{address}\n"
559       else
560         register = load_value address, @TEMPORARY
561         emit "jr #{register}\n"
562       end
563       emit "nop\n"
564     end
566     # Grows the stack by n bytes.
567     def grow_stack n
568       addiu "$sp", "$sp", -n
569     end
571     # Starts the false path of a conditional.
572     def ifelse
573       newlabel = @environment.gensym
574       emit "j #{newlabel}\n"
575       emit "nop\n"
576       label = @if_labels.pop
577       emit "#{label}:\n"
578       @if_labels.push newlabel
579     end
581     # Tests if x is equal to y
582     def ifeq x, y
583       common_if :ifeq, x, y
584     end
586     # Tests if x is greater than or equal to y
587     def ifge x, y
588       common_if :ifge, x, y
589     end
591     # Tests if x is strictly greater than y
592     def ifgt x, y
593       common_if :ifgt, x, y
594     end
596     # Tests if x is less than or equal to y
597     def ifle x, y
598       common_if :ifle, x, y
599     end
601     # Tests if x is strictly less than y
602     def iflt x, y
603       common_if :iflt, x, y
604     end
606     # Tests if x different from y
607     def ifne x, y
608       common_if :ifne, x, y
609     end
611     # Imports labels into the current section.
612     def import *symbols
613       # Record imported labels in @imports
614       symbols.each { |sym| @imports[sym] = sym }
615     end
617     # Emits a label.
618     def label name
619       emit "#{name}:\n"
620     end
622     # Introduces a new local variable.
623     def let symbol, *expr
624       n = @environment.locals
625       register = local_register n
626       ref = local_reference n
627       if register
628         # We will use a register to store the value
629         @environment.add_local symbol, register
630         # Save current value of register
631         emit "sw #{register}, #{ref}\n"
632         # Set new value
633         eval_expr expr, register
634       else
635         # We will use the stack to store the value
636         @environment.add_local symbol, local_offset(n)
637         eval_expr expr, @TEMPORARY
638         emit "sw #{@TEMPORARY}, #{ref}\n"
639       end
640     end
642     # Loads the value at the given address.
643     def load_at address, register = @TEMPORARY
644       load_value_into_register address, register
645       emit "lw #{register}, 0(#{register})\n"
646       register
647     end
649     # Loads a value into a register.
650     # Returns the name of the register.
651     # If the value was already in a register, the name of that
652     # register is returned.
653     # Else, the value is loaded into a register and the name of
654     # that register is returned. The register to use in that case
655     # may be specified using the optional second argument.
656     def load_value x, register = @TEMPORARY
657       if substitution? x
658         x = substitute_number x[1]
659       end
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 register? binding
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     # Loads 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     # Returns 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     # Returns an $sp-relative reference for the nth (0-based) local.
704     def local_reference n
705       offset_reference(local_offset(n))
706     end
708     # Returns 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       @LOCAL_REGISTERS[n]
712     end
714     # Computes the maximum number of locals that would fit in the current
715     # frame size.
716     def max_locals
717       # + 1, because the initial frame size has room for 1 local
718       (@frame_size - @INITIAL_FRAME_SIZE) / @WORDSIZE + 1
719     end
721     # Given an offset relative to the base of the frame, returns
722     # an reference to that memory location.
723     def offset_reference offset
724       "#{offset}($fp)"
725     end
727     # Returns true if the nth (0-based) local is stored in a register.
728     def register_local? n
729       n < @NREGISTER_LOCALS
730     end
732     # Returns from a function.
733     # 
734     # _words_ may contain an expression to be evaluated. The result
735     # of the evaluation is returned from the function.
736     def ret *words
737       # Compute return value and store it in @RETURN
738       eval_expr(words, @RETURN) unless words.empty?
739       # Go to epilogue
740       goto @function_end_label
741     end
743     # Saves the current frame at the given location.
744     def save_frame location
745       load_value_into_register location, @TEMPORARY
746       @SAVE_FRAME_REGISTERS.each_with_index do |register,i|
747         emit "sw #{register}, #{i * @WORDSIZE}(#{@TEMPORARY})\n"
748       end
749     end
751     # Sets a variable to the result of evaluating an expression.
752     def set symbol, *expr
753       if at_expr? symbol
754         eval_expr expr, @RETURN
755         load_value_into_register symbol.to_s[1..-1].to_sym, @TEMPORARY
756         emit "sw #{@RETURN}, 0(#{@TEMPORARY})\n"
757       else
758         x = @environment[symbol]
759         if x == nil
760           raise "Cannot change value of constant #{symbol}"
761         elsif register? x
762           eval_expr expr, x
763         else
764           # Should be an integer.
765           eval_expr expr, @TEMPORARY
766           emit "sw #{@TEMPORARY}, #{offset_reference x}\n"
767         end
768       end
769     end
771     # Sets the byte at _base_ + _offset_ to _value_.
772     def set_byte base, offset, value
773       # If base is an integer, but offset isn't, swap them
774       if !integer?(offset) && integer?(base)
775         base, offset = [offset, base]
776       end
778       value_reg = @TEMPORARIES.pop
779       load_value_into_register value, value_reg
780       begin
781         if integer? offset
782           base_reg = load_value base
783           emit "sb #{value_reg}, #{offset}(#{base_reg})\n"
784         else
785           eval_binop [:add, base, offset], @TEMPORARY
786           emit "sb #{value_reg}, 0(#{@TEMPORARY})\n"
787         end
788       ensure
789         @TEMPORARIES.push value_reg
790       end
791     end
793     # Sets the word at _base_ + _offset_ * +@WORDSIZE+ to _value_.
794     def set_word base, offset, value
795       value_reg = @TEMPORARIES.pop
796       load_value_into_register value, value_reg
797       begin
798         if integer? offset
799           base_reg = load_value base
800           emit "sw #{value_reg}, #{offset * @WORDSIZE}(#{base_reg})\n"
801         else
802           offset_reg = @TEMPORARIES.pop
803           begin
804             load_value_into_register offset, offset_reg
805             base_reg = load_value base
806             emit "sll #{offset_reg}, #{offset_reg}, 2\n"
807             emit "add #{@TEMPORARY}, #{base_reg}, #{offset_reg}\n"
808             emit "sw #{value_reg}, 0(#{@TEMPORARY})\n"
809           ensure
810             @TEMPORARIES.push offset_reg
811           end
812         end
813       ensure
814         @TEMPORARIES.push value_reg
815       end
816     end
818     # Defines a string with the given value.
819     def string value
820       code = ''
821       value.each_byte do |b|
822         if b == 92
823           code << "\\\\"
824         elsif b >= 32 && b < 127 && b != 34
825           code << b.chr
826         else
827           code << sprintf("\\%03o", b)
828         end
829       end
830       emit ".ascii \"#{code}\"\n"
831     end
833     # Calls a function, re-using the current call frame if possible.
834     def tail_call func, *args
835       # Compute number of stack arguments
836       nstackargs = number_of_stack_arguments args.length
837       # If we need more stack arguments than we have now,
838       # perform a normal call and return
839       if nstackargs > number_of_stack_arguments(@environment.args)
840         emit "# Not enough space for proper tail call; using regular call\n"
841         ret :call, func, *args
842       end
844       # Back up any stack arguments we will need after we overwrite them
845       temporaries = @TEMPORARIES.dup
846       nlocals = @environment.locals
847       (@NREGISTER_ARGS...args.length).each do |i|
848         x = @environment[args[i]]
849         if integer?(x) && x < arg_offset(i)
850           # args[i] is in a location that will have been overwritten by the
851           # time we get around to passing argument i to the called function.
852           # Make a backup of the value before we overwrite it.
853           if temporaries.empty?
854             # Oh dear, we're out of temporaries.
855             # Store the value in the current stack frame, extending it
856             # as necessary.
857             if nlocals >= max_locals
858               grow_stack 8
859             end
860             reg = load_value args[i]
861             emit "sw #{reg}, #{local_reference nlocals}\n"
862             args[i] = [:local, nlocals]
863             nlocals = nlocals + 1
864           else
865             # Load the argument into a temporary
866             reg = temporaries.shift
867             load_value_into_register args[i], reg
868             # Update args[i] so we know how to get to it later
869             args[i] = [:reg, reg]
870           end
871         end
872       end
874       # Set stack arguments
875       (@NREGISTER_ARGS...args.length).each do |i|
876         arg = args[i]
877         reg = @TEMPORARY
879         # Load value
880         if arg.kind_of? Array
881           # Special cases, created above
882           case arg[0]
883           when :reg
884             reg = arg[1]
885           when :local
886             emit "lw #{reg}, #{local_reference arg[1]}\n"
887           end
888         else
889           load_value_into_register arg, reg
890         end
892         # Store value in designated place
893         emit "sw #{reg}, #{arg_reference i}\n"
894       end
896       # Set register arguments
897       [@NREGISTER_ARGS,args.length].min.times do |i|
898         reg = arg_register i
899         load_value_into_register args[i], reg
900       end
902       # Load function address
903       if global? func
904         if @imports.has_key? func
905           # Load address from global offset table
906           emit "lw #{@FUNCTION}, %call16(#{func})(#{@GOT})\n"
907         else
908           # Assume label defined in this module
909           emit "lui #{@FUNCTION}, %hi(#{func})\n"
910           emit "addiu #{@FUNCTION}, %lo(#{func})\n"
911         end
912       else
913         # Load address
914         load_value_into_register func, "#{@FUNCTION}"
915       end
917       # Restore saved registers
918       [@NREGISTER_LOCALS, @environment.locals].min.times do |n|
919         emit "lw #{local_register n}, #{local_reference n}\n"
920       end
922       # Restore return address
923       emit "lw $31, #{@RA_OFFSET}($fp)\n"
925       # Restore stack pointer
926       emit "move $sp, $fp\n"
928       # Restore frame pointer
929       emit "lw $fp, #{@FP_OFFSET}($fp)\n"
931       # Perform call
932       emit "jr #{@FUNCTION}\n"
933       # brach delay slot
934       emit "nop\n"
935     end
937     # Defines a word with the given value
938     def word value
939       emit ".int #{value}\n"
940     end
942     # Write generated code to the given IO object.
943     def write io
944       io.puts ".set noat"
945       @sections.each do |section,code|
946         unless code.empty?
947           io.puts ".section #{section.to_s}"
948           io.puts code
949           io.puts
950         end
951       end
952     end
954   end
956   # Register class for big endian MIPS
957   Voodoo::CodeGenerator.register_generator MIPSGasGenerator,
958                                            :architecture => :mips,
959                                            :format => :gas
962   # Register class for little endian MIPS
963   Voodoo::CodeGenerator.register_generator MIPSGasGenerator,
964                                            :architecture => :mipsel,
965                                            :format => :gas