voodoo 1.0.2 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,10 @@
1
+ module Voodoo
2
+ # Generator that does nothing.
3
+ class DummyGenerator
4
+ def add *args
5
+ end
6
+
7
+ def write *args
8
+ end
9
+ end
10
+ end
@@ -31,12 +31,19 @@ module Voodoo
31
31
  # :
32
32
  # localn <-- esp
33
33
  #
34
+ # == Callee-Save Registers
35
+ #
36
+ # +ebp+, +ebx+, +edi+, +esi+, and +esp+ are callee-save registers.
37
+ #
38
+ # All other registers are caller-save registers.
39
+ #
34
40
  class I386NasmGenerator < NasmGenerator
35
41
  WORDSIZE = 4
36
42
 
37
43
  def initialize params = {}
38
44
  # Number of bytes in a word
39
- @WORDSIZE = 4
45
+ @WORDSIZE_BITS = 2
46
+ @WORDSIZE = 1 << @WORDSIZE_BITS
40
47
  # Word name in NASM lingo
41
48
  @WORD_NAME = 'dword'
42
49
  # Default alignment for code
@@ -45,101 +52,128 @@ module Voodoo
45
52
  @DATA_ALIGNMENT = @WORDSIZE
46
53
  # Default alignment for functions
47
54
  @FUNCTION_ALIGNMENT = 16
55
+ # Stack alingment
56
+ @STACK_ALIGNMENT_BITS = @WORDSIZE_BITS
57
+ @STACK_ALIGNMENT = 1 << @STACK_ALIGNMENT_BITS
48
58
  # Register used for return values
49
- @RETURN_REG = 'eax'
50
- # Register used as scratch register
51
- @SCRATCH_REG = 'ebx'
59
+ @RETURN_REG = :eax
52
60
  # Accumulator index
53
- @AX = 'eax'
61
+ @AX = :eax
54
62
  # Base index
55
- @BX = 'ebx'
63
+ @BX = :ebx
56
64
  # Count index
57
- @CX = 'ecx'
65
+ @CX = :ecx
58
66
  # Data index
59
- @DX = 'edx'
67
+ @DX = :edx
60
68
  # Base pointer
61
- @BP = 'ebp'
69
+ @BP = :ebp
62
70
  # Stack pointer
63
- @SP = 'esp'
71
+ @SP = :esp
72
+ # Registers used to store locals
73
+ @LOCAL_REGISTERS = []
74
+ @NLOCAL_REGISTERS = @LOCAL_REGISTERS.length
75
+ @LOCAL_REGISTERS_SET = Set.new @LOCAL_REGISTERS
76
+ @SAVE_FRAME_REGISTERS = [:ebx, :edi, :esi, :esp, :ebp]
77
+ @SAVED_FRAME_LAYOUT = {}
78
+ @SAVE_FRAME_REGISTERS.each_with_index { |r,i| @SAVED_FRAME_LAYOUT[r] = i }
79
+ @TEMPORARIES = [:ebx]
64
80
  super params
81
+ @saved_registers = []
82
+ in_section(:data) { emit "extern _GLOBAL_OFFSET_TABLE_\n" }
65
83
  @features.merge! \
66
84
  :'bits-per-word' => '32',
67
85
  :'byte-order' => 'little-endian',
68
86
  :'bytes-per-word' => '4'
69
87
  end
70
88
 
71
- # Call a function
89
+ # Returns the offset of the nth argument.
90
+ def arg_offset n
91
+ 8 + (n * @WORDSIZE)
92
+ end
93
+
94
+ # Emits function preamble and declare +formals+ as function arguments.
95
+ def begin_function formals, nlocals
96
+ environment = Environment.new @environment
97
+ @environment = environment
98
+ emit "push #{@BP}\nmov #{@BP}, #{@SP}\n"
99
+ formals.each_with_index do |arg,i|
100
+ environment.add_arg arg, arg_offset(i)
101
+ end
102
+ emit "sub #{@SP}, #{nlocals * @WORDSIZE}\n"
103
+ end
104
+
105
+ # Calls a function.
72
106
  def call func, *args
73
- emit "; call #{func} #{args.join ' '}\n"
74
107
  revargs = args.reverse
75
108
  revargs.each { |arg| push arg }
76
109
  use_value "call", func
77
110
  if args.length > 0
78
- emit "add esp, #{WORDSIZE * args.length}\n"
111
+ emit "add esp, #{@WORDSIZE * args.length}\n"
79
112
  end
80
113
  end
81
114
 
82
- # Emit function prologue.
83
- def emit_function_prologue formals = []
84
- emit "push ebp\nmov ebp, esp\n"
85
- end
86
-
87
- # Load the value of the nth argument
88
- def load_arg n, reg = @SCRATCH_REG
89
- "[ebp + #{n * @WORDSIZE + 8}]"
115
+ # Loads the address of the global offset table into the given register.
116
+ def load_got_address reg
117
+ lbl = gensym
118
+ emit "call #{lbl}\n"
119
+ emit "#{lbl}:\n"
120
+ emit "pop #{reg}\n"
121
+ emit "add #{reg}, _GLOBAL_OFFSET_TABLE_ + $$ - #{lbl} wrt ..gotpc\n"
122
+ reg
90
123
  end
91
124
 
92
- # Load the value of the nth local variable
93
- def load_local n, reg = @SCRATCH_REG
94
- "[ebp - #{(n + 1) * @WORDSIZE}]"
125
+ # Loads a symbol from the global offset table.
126
+ def load_symbol_from_got symbol, reg
127
+ load_got_address reg
128
+ emit "mov #{reg}, [#{reg} + #{symbol} wrt ..got]\n"
129
+ reg
95
130
  end
96
131
 
97
- # Introduce a new local variable
98
- def let symbol, *words
99
- emit "; let #{symbol} #{words.join ' '}\n"
100
- @environment.add_local symbol
101
- eval_expr words
102
- emit "push eax\n"
132
+ # If the nth local is stored in a register, returns that register.
133
+ # Otherwise, returns the offset from the frame pointer.
134
+ def local_offset_or_register n
135
+ (n + 1) * -@WORDSIZE
103
136
  end
104
137
 
105
138
  # Push a word on the stack
106
139
  def push value
107
- #emit "; push #{value}\n"
108
140
  value_ref = load_value value, "ebx"
109
141
  emit "push dword #{value_ref}\n"
110
142
  end
111
143
 
112
144
  # Call a function, re-using the current call frame if possible
113
145
  def tail_call fun, *args
114
- emit "; tail-call #{fun} #{args.join ' '}\n"
115
146
  if args.length > @environment.args
116
147
  # Not enough space to do proper tail call; do normal call instead
117
148
  emit "; not enough space for proper tail call; changed to regular call\n"
118
149
  ret :call, fun, *args
119
150
  else
120
- # Any value in the current frame that is passed to the called
121
- # function must be copied to a local variable if it would otherwise
122
- # be overwritten before it is used
123
- i = args.length - 1
124
- while i >= -1
125
- arg = (i >= 0) ? args[i] : fun
126
-
127
- if symbol?(arg)
128
- x = @environment[arg]
129
- if x && x[0] == :arg && x[1] < args.length && x[1] > i &&
130
- (i >= 0 || fun != args[x[1]])
131
- # Save value
132
- newsym = @environment.gensym
133
- let newsym, arg
134
- # Change reference
135
- if i >= 0
136
- args[i] = newsym
137
- else
138
- fun = newsym
139
- end
140
- end
151
+ # If any arguments are going to be overwritten before they are
152
+ # used, save them to new local variables and use those instead.
153
+ (args.length - 1).downto(0) do |i|
154
+ arg = args[i]
155
+ next unless symbol?(arg)
156
+ old_arg_offset = @environment[arg]
157
+ next if old_arg_offset == nil || old_arg_offset < 0
158
+ # arg is an argument that was passed on the stack.
159
+ new_arg_offset = arg_offset i
160
+ next unless old_arg_offset > new_arg_offset
161
+ # arg will be overwritten before it is used.
162
+ # Save it in a newly created temporary variable,
163
+ # then use that instead.
164
+ newsym = @environment.gensym
165
+ let newsym, arg
166
+ args[i] = newsym
167
+ end
168
+
169
+ # Same for the function we will be calling.
170
+ if symbol?(fun)
171
+ offset = @environment[fun]
172
+ if offset != nil && offset > 0
173
+ newsym = @environment.gensym
174
+ let newsym, fun
175
+ func = newsym
141
176
  end
142
- i = i - 1
143
177
  end
144
178
 
145
179
  # Set arguments
@@ -147,16 +181,15 @@ module Voodoo
147
181
  (args.length - 1).downto(0).each do |i|
148
182
  arg = args[i]
149
183
 
150
- value_ref = load_value arg, "eax"
151
- newarg_ref = "[ebp + #{(i + 2) * WORDSIZE}]"
184
+ value_ref = load_value arg, :eax
185
+ newarg_ref = "[ebp + #{arg_offset i}]"
152
186
  # Elide code if source is same as destination
153
187
  unless value_ref == newarg_ref
154
188
  if memory_operand?(value_ref)
155
189
  emit "mov eax, #{value_ref}\n"
156
- value_ref = "eax"
190
+ value_ref = :eax
157
191
  end
158
- emit "mov #{@WORD_NAME} [ebp + #{(i + 2) * WORDSIZE}], " +
159
- "#{value_ref}\n"
192
+ emit "mov #{@WORD_NAME} #{newarg_ref}, #{value_ref}\n"
160
193
  end
161
194
  end
162
195
  end
@@ -168,7 +201,7 @@ module Voodoo
168
201
  end
169
202
 
170
203
  def use_value operation, value
171
- value_ref = load_value value, "eax"
204
+ value_ref = load_value value, :eax
172
205
  emit "#{operation} #{value_ref}\n"
173
206
  end
174
207
 
@@ -1,4 +1,5 @@
1
1
  require 'voodoo/generators/common_code_generator'
2
+ require 'set'
2
3
 
3
4
  module Voodoo
4
5
  # = MIPS GNU Assembler Code Generator
@@ -53,9 +54,10 @@ module Voodoo
53
54
  # arg3
54
55
  # arg2
55
56
  # arg1
56
- # arg0
57
+ # arg0 <-- $fp points here
57
58
  # return address
58
59
  # pointer to Global Offset Table
60
+ # saved $fp
59
61
  # local0
60
62
  # :
61
63
  # local7
@@ -77,6 +79,8 @@ module Voodoo
77
79
  #
78
80
  # - Saved pointer to global offset table (computed from $25), if necessary.
79
81
  #
82
+ # - Saved value of $fp, if necessary.
83
+ #
80
84
  # - Saved values of caller's locals 0 through 7
81
85
  # (originally in $16 through $23), if necessary.
82
86
  #
@@ -94,22 +98,41 @@ module Voodoo
94
98
  # holding the address of the called function, and _gp_disp is the
95
99
  # offset between the beginning of the function and the global offset table.
96
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
+ #
97
107
  class MIPSGasGenerator < CommonCodeGenerator
98
108
  def initialize params
99
- @WORDSIZE = 4
109
+ @WORDSIZE_BITS = 2
110
+ @WORDSIZE = 1 << @WORDSIZE_BITS
100
111
  @CODE_ALIGNMENT = 0
101
112
  @DATA_ALIGNMENT = @WORDSIZE
113
+ @STACK_ALIGNMENT_BITS = 3
114
+ @STACK_ALIGNMENT = 1 << @STACK_ALIGNMENT_BITS
115
+ @FP_OFFSET = -3 * @WORDSIZE
102
116
  @FUNCTION_ALIGNMENT = @WORDSIZE
103
-
104
- @INITIAL_FRAME_SIZE = 2 * @WORDSIZE
117
+ @GP_OFFSET = -2 * @WORDSIZE
118
+ @INITIAL_FRAME_SIZE = 4 * @WORDSIZE
105
119
  @REGISTER_ARG_BASE = 4
106
120
  @NREGISTER_ARGS = 4
121
+ @LOCAL_REGISTERS = [:'$16', :'$17', :'$18', :'$19',
122
+ :'$20', :'$21', :'$22', :'$23']
123
+ @LOCAL_REGISTERS_SET = Set.new @LOCAL_REGISTERS
107
124
  @REGISTER_LOCAL_BASE = 16
108
- @NREGISTER_LOCALS = 8
125
+ @NREGISTER_LOCALS = @LOCAL_REGISTERS.length
126
+ @RA_OFFSET = -@WORDSIZE
109
127
  @RETURN = :'$2'
110
128
  @TEMPORARY = :'$1'
111
129
  @FUNCTION = :'$25'
112
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 }
113
136
  @TEMPORARIES = [:'$8', :'$9', :'$10', :'$11',
114
137
  :'$12', :'$13', :'$14', :'$15']
115
138
  @function_end_label = nil
@@ -117,6 +140,7 @@ module Voodoo
117
140
  @if_labels = []
118
141
  super params
119
142
  @output_file_suffix = '.s'
143
+ @saved_registers = []
120
144
  @features.merge! \
121
145
  :'bits-per-word' => '32',
122
146
  :'bytes-per-word' => '4'
@@ -131,37 +155,32 @@ module Voodoo
131
155
  end
132
156
  end
133
157
 
134
- def align alignment = nil
135
- unless alignment
136
- # Get default alignment
137
- case @section
138
- when :code
139
- alignment = @CODE_ALIGNMENT
140
- when :data
141
- alignment = @DATA_ALIGNMENT
142
- when :function
143
- alignment = @FUNCTION_ALIGNMENT
144
- else
145
- # Use data alignment as default
146
- alignment = @DATA_ALIGNMENT
147
- end
158
+ # Adds immediate to source and stores the result in target.
159
+ def addiu target, source, immediate, temporary = @TEMPORARY
160
+ if immediate >= -32768 && immediate < 32767
161
+ emit "addiu #{target}, #{source}, #{immediate}\n"
162
+ elsif source == "$0"
163
+ emit "lui #{target}, #{(immediate >> 16) & 0xffff}\n"
164
+ emit "ori #{target}, #{target}, #{immediate & 0xffff}\n"
165
+ else
166
+ addiu temporary, "$0", immediate
167
+ emit "addu #{target}, #{source}, #{temporary}\n"
148
168
  end
149
- emit ".align #{alignment}\n" unless alignment == 0
150
169
  end
151
170
 
152
- # Return the offset from $sp at which the nth (0-based) argument is
171
+ # Return the offset from the frame base at which the nth argument is
153
172
  # stored.
154
173
  def arg_offset n
155
- @frame_size + n * @WORDSIZE
174
+ n * @WORDSIZE
156
175
  end
157
176
 
158
- # Return an $sp-relative reference for the nth (0-based) argument
177
+ # Returns an $fp-relative reference for the nth (0-based) argument.
159
178
  def arg_reference n
160
- "#{arg_offset n}($sp)"
179
+ offset_reference(arg_offset(n))
161
180
  end
162
181
 
163
- # Return the register in which the nth (0-based) argument is stored, or
164
- # nil if not stored in a register
182
+ # Returns the register in which the nth (0-based) argument is stored, or
183
+ # nil if not stored in a register.
165
184
  def arg_register n
166
185
  if register_arg? n
167
186
  "$#{@REGISTER_ARG_BASE + n}"
@@ -170,19 +189,46 @@ module Voodoo
170
189
  end
171
190
  end
172
191
 
173
- # Test if op is a binary operation
174
- def assymetric_binop? op
175
- [:asr, :bsr, :div, :mod, :rol, :ror, :shl, :shr, :sub].member?(op)
192
+ def auto_bytes n, register
193
+ if n.kind_of? Integer
194
+ # Maintain stack alignment
195
+ n = (n + @STACK_ALIGNMENT - 1) / @STACK_ALIGNMENT * @STACK_ALIGNMENT
196
+ grow_stack n
197
+ else
198
+ temporary = @TEMPORARIES.pop
199
+ begin
200
+ load_value_into_register n, register
201
+ emit "addi #{register}, #{@STACK_ALIGNMENT - 1}\n"
202
+ emit "li #{temporary}, -#{@STACK_ALIGNMENT}\n"
203
+ emit "and #{register}, #{register}, #{temporary}\n"
204
+ emit "sub $sp, #{register}\n"
205
+ ensure
206
+ @TEMPORARIES.push temporary
207
+ end
208
+ end
209
+ emit "move #{register}, $sp\n" unless register == "$sp"
176
210
  end
177
211
 
178
- # Test if a value is an at-expression
179
- def at_expr? value
180
- value.respond_to?(:[]) && value[0] == :'@'
212
+ def auto_words n, register
213
+ if n.kind_of? Integer
214
+ auto_bytes n * @WORDSIZE, register
215
+ else
216
+ load_value_into_register n, register
217
+ if @STACK_ALIGNMENT_BITS > @WORDSIZE_BITS
218
+ bits = @STACK_ALIGNMENT_BITS - @WORDSIZE_BITS
219
+ emit "addi #{register}, #{(1 << bits) - 1}\n"
220
+ emit "srl #{register}, #{register}, #{bits}\n"
221
+ emit "sll #{register}, #{register}, #{@STACK_ALIGNMENT_BITS}\n"
222
+ else
223
+ emit "sll #{register}, #{register}, #{@WORDSIZE_BITS}\n"
224
+ end
225
+ emit "sub $sp, $sp, #{register}\n"
226
+ emit "move #{register}, $sp\n" unless register == "$sp"
227
+ end
181
228
  end
182
229
 
183
230
  # Begins a new block.
184
231
  def begin_block *code
185
- emit "# begin block\n"
186
232
  # If we are at top-level, create a frame
187
233
  if @environment == @top_level
188
234
  create_frame count_locals(code)
@@ -191,40 +237,33 @@ module Voodoo
191
237
  @environment = environment
192
238
  end
193
239
 
194
- # Emit function prologue and declare _formals_ as function arguments
240
+ # Emits function prologue and declare _formals_ as function arguments.
195
241
  def begin_function formals, nlocals
196
242
  if @environment != @top_level
197
243
  raise "Cannot begin function when already in a function"
198
244
  end
199
245
 
200
- emit "# function #{formals.join ' '}\n"
201
246
  environment = Environment.new @environment
202
- environment.add_args formals
247
+ @frame_size = 0
248
+ formals.each {|x| environment.add_arg x, arg_offset(environment.args)}
203
249
  @environment = environment
204
250
  @function_end_label = gensym
205
251
  emit_function_prologue formals, nlocals
206
252
  end
207
253
 
208
- # Test if op is a binary operation
209
- def binop? op
210
- assymetric_binop?(op) || symmetric_binop?(op)
211
- end
212
-
213
- # Define a byte with the given value
254
+ # Defines a byte with the given value.
214
255
  def byte value
215
256
  emit ".byte #{value}\n"
216
257
  end
217
258
 
218
- # Call a function.
259
+ # Calls a function.
219
260
  def call func, *args
220
- emit "# call #{func} #{args.join ' '}\n"
221
261
  # Grow the stack frame, subject to 3 conditions:
222
262
  # 1. There must be enough space to hold all arguments
223
263
  # 2. $sp must remain a multiple of 8
224
264
  # 3. We must provide space for at least 4 words
225
265
  increment = ([args.length, 4].max * @WORDSIZE + 7) / 8 * 8
226
- emit "addiu $sp, $sp, -#{increment}\n"
227
- @frame_size = @frame_size + increment
266
+ grow_stack increment
228
267
 
229
268
  # Put arguments in the right places
230
269
  n = 0
@@ -261,18 +300,20 @@ module Voodoo
261
300
  emit "nop\n"
262
301
 
263
302
  # Restore original stack frame
264
- emit "addiu $sp, $sp, #{increment}\n"
265
- @frame_size = @frame_size - increment
303
+ grow_stack -increment
266
304
 
267
305
  # restore gp
268
- emit "lw #{@GOT}, #{@frame_size - 2 * @WORDSIZE}($sp)\n"
306
+ emit "lw #{@GOT}, #{@GP_OFFSET}($fp)\n"
307
+ end
308
+
309
+ # Emits a comment.
310
+ def comment text
311
+ emit "# #{text}\n"
269
312
  end
270
313
 
271
- # Start a conditional using the specified branch instruction
314
+ # Starts a conditional using the specified branch instruction
272
315
  # after the comparison.
273
316
  def common_if comp, x, y = nil
274
- emit "# #{comp} #{x} #{y}\n"
275
-
276
317
  temporaries = @TEMPORARIES.dup
277
318
  xreg = load_value x, temporaries[0]
278
319
  temporaries.delete xreg
@@ -305,38 +346,32 @@ module Voodoo
305
346
  emit "nop\n"
306
347
  end
307
348
 
308
- # Counts the number of local variables created in
309
- # a sequence of statements.
310
- def count_locals statements
311
- count = 0
312
- each_statement(statements) do |statement|
313
- if statement[0] == :let
314
- # let introduces a single local
315
- count = count + 1
316
- end
317
- end
318
- count
349
+ # Creates an activation frame which can store nlocals local variables.
350
+ def create_frame nlocals
351
+ @frame_size = @INITIAL_FRAME_SIZE
352
+ if nlocals > 0
353
+ @frame_size = @frame_size + (nlocals * @WORDSIZE + 7) / 8 * 8
354
+ end
355
+ grow_stack @frame_size
356
+ @saved_registers = []
357
+ end
358
+
359
+ def emit_align alignment
360
+ emit ".align #{alignment}\n"
319
361
  end
320
362
 
321
- # Creates an activation frame which can store nlocals local variables
322
- def create_frame nlocals
323
- @frame_size = (@INITIAL_FRAME_SIZE + nlocals * @WORDSIZE + 7) / 8 * 8
324
- emit "addiu $sp, $sp, -#{@frame_size}\n"
363
+ # Exports symbols from the current section.
364
+ def emit_export *symbols
365
+ symbols.each { |sym| emit ".globl #{sym}\n" }
325
366
  end
326
367
 
327
- # Emit function prologue.
368
+ # Emits function prologue.
328
369
  def emit_function_prologue formals = [], nlocals = 0
329
370
  # Calculate new value for $gp
330
371
  emit "lui #{@GOT}, %hi(_gp_disp)\n"
331
372
  emit "addiu #{@GOT}, %lo(_gp_disp)\n"
332
373
  emit "addu #{@GOT}, #{@GOT}, #{@FUNCTION}\n"
333
374
 
334
- # Save parameters 0 .. 3 in the stack space that the caller
335
- # should have allocated for them.
336
- [@NREGISTER_ARGS, formals.length].min.times do |n|
337
- emit "sw $#{n + @REGISTER_ARG_BASE}, #{n * @WORDSIZE}($29)\n"
338
- end
339
-
340
375
  create_frame nlocals
341
376
 
342
377
  # Save return address
@@ -344,55 +379,96 @@ module Voodoo
344
379
 
345
380
  # Save gp
346
381
  emit "sw #{@GOT}, #{@frame_size - 2 * @WORDSIZE}($sp)\n"
382
+
383
+ # Save fp
384
+ emit "sw $fp, #{@frame_size - 3 * @WORDSIZE}($sp)\n"
385
+
386
+ # Point fp at where sp was before we created the frame
387
+ addiu "$fp", "$sp", @frame_size
388
+
389
+ # Save parameters 0 .. 3 in the stack space that the caller
390
+ # should have allocated for them.
391
+ [@NREGISTER_ARGS, formals.length].min.times do |n|
392
+ ref = offset_reference(@environment[formals[n]])
393
+ emit "sw $#{n + @REGISTER_ARG_BASE}, #{ref}\n"
394
+ end
395
+ end
396
+
397
+ # Imports labels into the current section.
398
+ def emit_import *symbols
399
+ # Record imported labels in @imports
400
+ symbols.each { |sym| @imports[sym] = sym }
401
+ end
402
+
403
+ # Emits a label type annotation.
404
+ def emit_label_type name, type
405
+ type_map = {
406
+ :code => "@function",
407
+ :data => "@object",
408
+ }
409
+ emit ".type #{name}, #{type_map[type]}\n"
410
+ end
411
+
412
+ # Emits a label size annotation.
413
+ def emit_label_size name
414
+ emit ".size #{name}, .-#{name}\n"
347
415
  end
348
416
 
349
- # End a function body
417
+ # Loads a word into a register.
418
+ def emit_load_word register, base, offset
419
+ emit "lw #{register}, #{offset * @WORDSIZE}(#{base})\n"
420
+ end
421
+
422
+ # Stores the value of a register in memory.
423
+ def emit_store_word register, base, offset
424
+ emit "sw #{register}, #{offset * @WORDSIZE}(#{base})\n"
425
+ end
426
+
427
+ # Ends a function body.
350
428
  def end_function
351
429
  if @environment == @top_level
352
430
  raise "Cannot end function when not in a function"
353
431
  end
354
432
 
355
- emit "# function epilogue\n"
356
-
357
433
  label @function_end_label
358
434
  # Restore saved locals
359
435
  [@NREGISTER_LOCALS, @environment.locals].min.times do |n|
360
436
  emit "lw #{local_register n}, #{local_reference n}\n"
361
437
  end
438
+ @saved_registers = []
362
439
  # Load return address
363
- emit "lw $31, #{@frame_size - @WORDSIZE}($sp)\n"
440
+ emit "lw $31, #{@RA_OFFSET}($fp)\n"
364
441
  # Restore stack pointer
365
- emit "addiu $sp, $sp, #{@frame_size}\n"
442
+ emit "move $sp, $fp\n"
443
+ # Restore frame pointer
444
+ emit "lw $fp, #{@FP_OFFSET}($fp)\n"
366
445
  # Return
367
446
  emit "jr $31\n"
368
447
  emit "nop\n"
369
448
 
370
- emit "# end function\n\n"
371
-
372
449
  @function_end_label = nil
373
450
  @environment = @top_level
374
451
  end
375
452
 
376
453
  # Ends the current block.
377
454
  def end_block
378
- emit "# end block\n"
379
-
380
455
  # If we are returning to top level, restore stack pointer
381
456
  if @environment.parent == @top_level
382
- emit "addiu $sp, $sp, #{@frame_size}\n"
457
+ grow_stack -@frame_size
458
+ @saved_registers = []
383
459
  end
384
460
 
385
461
  # Restore old value of @environment
386
462
  @environment = @environment.parent
387
463
  end
388
464
 
389
- # End a conditional.
465
+ # Ends a conditional.
390
466
  def end_if
391
467
  label = @if_labels.pop
392
468
  emit "#{label}:\n"
393
469
  end
394
470
 
395
- # Evaluate the binary operation expr and store the result in register
471
+ # Evaluates the binary operation expr and store the result in register.
396
472
  def eval_binop expr, register
397
473
  temporaries = @TEMPORARIES.dup
398
474
  x = load_value expr[1], temporaries[0]
@@ -433,7 +509,7 @@ module Voodoo
433
509
  end
434
510
  end
435
511
 
436
- # Evaluate the expression expr and store the result in register
512
+ # Evaluates the expression expr and store the result in register.
437
513
  def eval_expr expr, register
438
514
  if expr.length == 1
439
515
  # Load value
@@ -442,6 +518,10 @@ module Voodoo
442
518
  # Evaluate expression
443
519
  op = expr[0]
444
520
  case op
521
+ when :'auto-bytes'
522
+ auto_bytes expr[1], register
523
+ when :'auto-words'
524
+ auto_words expr[1], register
445
525
  when :call
446
526
  call *expr[1..-1]
447
527
  emit "move #{register}, #{@RETURN}\n" if register != @RETURN
@@ -461,20 +541,7 @@ module Voodoo
461
541
  end
462
542
  end
463
543
 
464
- # Export symbols from the current section
465
- def export *symbols
466
- symbols.each { |sym| emit ".globl #{sym}\n" }
467
- end
468
-
469
- # Add a function to the current section
470
- def function formals, *code
471
- nlocals = count_locals code
472
- begin_function formals, nlocals
473
- code.each { |action| add section, action }
474
- end_function
475
- end
476
-
477
- # Load byte from _base_ + _offset_ into _register_
544
+ # Loads byte from _base_ + _offset_ into _register_.
478
545
  def get_byte base, offset, register
479
546
  # If base is an integer, but offset isn't, swap them
480
547
  if !integer?(offset) && integer?(base)
@@ -490,7 +557,7 @@ module Voodoo
490
557
  end
491
558
  end
492
559
 
493
- # Load word from _base_ + _offset_ * _@WORDSIZE_ into _register_
560
+ # Loads word from _base_ + _offset_ * _@WORDSIZE_ into _register_.
494
561
  def get_word base, offset, register
495
562
  if integer? offset
496
563
  base_reg = load_value base
@@ -509,20 +576,24 @@ module Voodoo
509
576
  end
510
577
  end
511
578
 
512
- # Test if a symbol refers to a global
513
- def global? symbol
514
- symbol?(symbol) && @environment[symbol] == nil
579
+ # Jumps to an address.
580
+ def goto address
581
+ if global? address
582
+ emit "j #{address}\n"
583
+ else
584
+ register = load_value address, @TEMPORARY
585
+ emit "jr #{register}\n"
586
+ end
587
+ emit "nop\n"
515
588
  end
516
589
 
517
- # Jump to a label.
518
- def goto label
519
- emit "j #{label}\n"
520
- emit "nop\n"
590
+ # Grows the stack by n bytes.
591
+ def grow_stack n
592
+ addiu "$sp", "$sp", -n
521
593
  end
522
594
 
523
- # Start the false path of a conditional.
595
+ # Starts the false path of a conditional.
524
596
  def ifelse
525
- emit "# else\n"
526
597
  newlabel = @environment.gensym
527
598
  emit "j #{newlabel}\n"
528
599
  emit "nop\n"
@@ -531,93 +602,65 @@ module Voodoo
531
602
  @if_labels.push newlabel
532
603
  end
533
604
 
534
- # Test if x is equal to y
605
+ # Tests if x is equal to y
535
606
  def ifeq x, y
536
607
  common_if :ifeq, x, y
537
608
  end
538
609
 
539
- # Test if x is greater than or equal to y
610
+ # Tests if x is greater than or equal to y
540
611
  def ifge x, y
541
612
  common_if :ifge, x, y
542
613
  end
543
614
 
544
- # Test if x is strictly greater than y
615
+ # Tests if x is strictly greater than y
545
616
  def ifgt x, y
546
617
  common_if :ifgt, x, y
547
618
  end
548
619
 
549
- # Test if x is less than or equal to y
620
+ # Tests if x is less than or equal to y
550
621
  def ifle x, y
551
622
  common_if :ifle, x, y
552
623
  end
553
624
 
554
- # Test if x is strictly less than y
625
+ # Tests if x is strictly less than y
555
626
  def iflt x, y
556
627
  common_if :iflt, x, y
557
628
  end
558
629
 
559
- # Test if x different from y
630
+ # Tests if x different from y
560
631
  def ifne x, y
561
632
  common_if :ifne, x, y
562
633
  end
563
634
 
564
- # Import labels into the current section
565
- def import *symbols
566
- # Record imported labels in @imports
567
- symbols.each { |sym| @imports[sym] = sym }
568
- end
569
-
570
- # Test if a value is an integer
571
- def integer? value
572
- value.kind_of? Integer
573
- end
574
-
575
- # Emit a label
576
- def label name
577
- emit "#{name}:\n"
578
- end
579
-
580
- # Introduce a new local variable
635
+ # Introduces a new local variable.
581
636
  def let symbol, *expr
582
- emit "# let #{symbol} #{expr.join ' '}\n"
583
637
  n = @environment.locals
584
- @environment.add_local symbol
585
-
586
- # Extend stack frame, if necessary.
587
- # This should never be necessary, because we allocate enough
588
- # space in the top-level function or block.
589
- if @environment.locals > max_locals
590
- $stderr.puts "WARNING: Stack frame too small. Possible BUG"
591
- $stderr.puts "Frame size #{@frame_size}, max locals #{max_locals}" +
592
- ", actual locals #{@environment.locals}"
593
- # Extend frame by 8 bytes, to keep $sp a multiple of 8
594
- emit "addiu $sp, $sp, -8\n"
595
- @frame_size = @frame_size + 8
596
- end
597
-
598
638
  register = local_register n
599
639
  ref = local_reference n
600
640
  if register
601
641
  # We will use a register to store the value
642
+ @environment.add_local symbol, register
602
643
  # Save current value of register
603
644
  emit "sw #{register}, #{ref}\n"
645
+ @saved_registers << register
604
646
  # Set new value
605
647
  eval_expr expr, register
606
648
  else
607
649
  # We will use the stack to store the value
650
+ @environment.add_local symbol, local_offset(n)
608
651
  eval_expr expr, @TEMPORARY
609
652
  emit "sw #{@TEMPORARY}, #{ref}\n"
610
653
  end
611
654
  end
612
655
 
613
- # Load the value at the given address.
656
+ # Loads the value at the given address.
614
657
  def load_at address, register = @TEMPORARY
615
658
  load_value_into_register address, register
616
659
  emit "lw #{register}, 0(#{register})\n"
617
660
  register
618
661
  end
619
662
 
620
- # Load a value into a register.
663
+ # Loads a value into a register.
621
664
  # Returns the name of the register.
622
665
  # If the value was already in a register, the name of that
623
666
  # register is returned.
@@ -625,39 +668,32 @@ module Voodoo
625
668
  # that register is returned. The register to use in that case
626
669
  # may be specified using the optional second argument.
627
670
  def load_value x, register = @TEMPORARY
671
+ if substitution? x
672
+ x = substitute_number x[1]
673
+ end
674
+
628
675
  if x == 0
629
676
  return "$0"
630
677
  elsif integer? x
631
- if x >= -32768 && x <= 32767
632
- emit "addiu #{register}, $0, #{x}\n"
633
- return register
634
- else
635
- emit "lui #{register}, #{x >> 16}\n"
636
- emit "ori #{register}, #{register}, #{x & 0xffff}\n"
637
- return register
638
- end
678
+ addiu register, "$0", x
679
+ return register
639
680
  elsif symbol? x
640
681
  binding = @environment[x]
641
- if binding
642
- case binding[0]
643
- when :arg
644
- emit "lw #{register}, #{arg_reference binding[1]}\n"
645
- return register
646
- when :local
647
- n = binding[1]
648
- if register_local? n
649
- return local_register(n)
650
- else
651
- emit "lw #{register}, #{local_reference n}\n"
652
- return register
653
- end
654
- else
655
- raise "Don't know how to load #{x.inspect}"
656
- end
682
+ if register? binding
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
657
689
  else
658
690
  # Assume global
659
- emit "lui #{register}, %hi(#{x})\n"
660
- emit "addiu #{register}, %lo(#{x})\n"
691
+ if @relocated_symbols.include? x
692
+ emit "lw #{register}, %got(#{x})(#{@GOT})\n"
693
+ else
694
+ emit "lui #{register}, %hi(#{x})\n"
695
+ emit "addiu #{register}, %lo(#{x})\n"
696
+ end
661
697
  return register
662
698
  end
663
699
  elsif at_expr? x
@@ -667,7 +703,7 @@ module Voodoo
667
703
  end
668
704
  end
669
705
 
670
- # Load a value into a specific register
706
+ # Loads a value into a specific register.
671
707
  def load_value_into_register x, register
672
708
  reg = load_value x, register
673
709
  if reg != register
@@ -675,97 +711,83 @@ module Voodoo
675
711
  end
676
712
  end
677
713
 
678
- # Return the offset from $sp at which the nth (0-based) local variable is
679
- # stored. For register locals this is the offset at which the saved
714
+ # Returns the offset from the frame base at which the nth local is stored.
715
+ # For register locals this is the offset at which the saved
680
716
  # value (from the calling function) is stored.
681
717
  def local_offset n
682
- @frame_size - @INITIAL_FRAME_SIZE - (n + 1) * @WORDSIZE
718
+ -(n + 4) * @WORDSIZE
683
719
  end
684
720
 
685
- # Return an $sp-relative reference for the nth (0-based) local
721
+ # Returns an $sp-relative reference for the nth (0-based) local.
686
722
  def local_reference n
687
- "#{local_offset n}($sp)"
723
+ offset_reference(local_offset(n))
688
724
  end
689
725
 
690
- # Return the register in which the nth local (0-based) is stored, or
691
- # nil if not stored in a register
726
+ # Returns the register in which the nth local (0-based) is stored, or
727
+ # nil if not stored in a register.
692
728
  def local_register n
693
- if register_local? n
694
- "$#{@REGISTER_LOCAL_BASE + n}"
695
- else
696
- nil
697
- end
729
+ @LOCAL_REGISTERS[n]
698
730
  end
699
731
 
700
- # Compute the maximum number of locals that would fit in the current
732
+ # Computes the maximum number of locals that would fit in the current
701
733
  # frame size.
702
734
  def max_locals
703
- (@frame_size - @INITIAL_FRAME_SIZE) / @WORDSIZE
704
- end
705
-
706
- # Calculate the number of stack arguments,
707
- # given the total number of arguments.
708
- def number_of_stack_arguments n
709
- [0, n - @NREGISTER_ARGS].max
735
+ # + 1, because the initial frame size has room for 1 local
736
+ (@frame_size - @INITIAL_FRAME_SIZE) / @WORDSIZE + 1
710
737
  end
711
738
 
712
- # Returns true if the nth (0-based) argument is stored in a register
713
- def register_arg? n
714
- n < @NREGISTER_ARGS
739
+ # Given an offset relative to the base of the frame, returns
740
+ # an reference to that memory location.
741
+ def offset_reference offset
742
+ "#{offset}($fp)"
715
743
  end
716
744
 
717
- # Returns true if the nth (0-based) local is stored in a register
745
+ # Returns true if the nth (0-based) local is stored in a register.
718
746
  def register_local? n
719
747
  n < @NREGISTER_LOCALS
720
748
  end
721
749
 
722
- # Return a from a function.
750
+ # Returns from a function.
723
751
  #
724
752
  # _words_ may contain an expression to be evaluated. The result
725
753
  # of the evaluation is returned from the function.
726
754
  def ret *words
727
- emit "# return #{words.join ' '}\n"
728
755
  # Compute return value and store it in @RETURN
729
756
  eval_expr(words, @RETURN) unless words.empty?
730
757
  # Go to epilogue
731
758
  goto @function_end_label
732
759
  end
733
-
734
- # Set a variable to the result of evaluating an expression
735
- def set symbol, *expr
736
- emit "# set #{symbol} #{expr.join ' '}\n"
737
760
 
738
- x = @environment[symbol]
739
- if x == nil
740
- raise "Cannot change value of constant #{symbol}"
741
- end
742
-
743
- if x[0] == :local
744
- register = local_register x[1]
745
- else
746
- register = nil
761
+ # Saves the current frame at the given location.
762
+ def save_frame location
763
+ load_value_into_register location, @TEMPORARY
764
+ @SAVE_FRAME_REGISTERS.each_with_index do |register,i|
765
+ emit "sw #{register}, #{i * @WORDSIZE}(#{@TEMPORARY})\n"
747
766
  end
767
+ end
748
768
 
749
- if register
750
- # Set new value
751
- eval_expr expr, register
769
+ # Sets a variable to the result of evaluating an expression.
770
+ def set symbol, *expr
771
+ if at_expr? symbol
772
+ eval_expr expr, @RETURN
773
+ load_value_into_register symbol.to_s[1..-1].to_sym, @TEMPORARY
774
+ emit "sw #{@RETURN}, 0(#{@TEMPORARY})\n"
752
775
  else
753
- case x[0]
754
- when :local
755
- ref = local_reference x[1]
756
- when :arg
757
- ref = arg_reference x[1]
776
+ x = @environment[symbol]
777
+ if x == nil
778
+ raise "Cannot change value of constant #{symbol}"
779
+ elsif register? x
780
+ eval_expr expr, x
758
781
  else
759
- raise "??? #{sym} is neither a local nor an argument"
782
+ # Should be an integer.
783
+ eval_expr expr, @TEMPORARY
784
+ emit "sw #{@TEMPORARY}, #{offset_reference x}\n"
760
785
  end
761
- eval_expr expr, @TEMPORARY
762
- emit "sw #{@TEMPORARY}, #{ref}\n"
763
786
  end
764
787
  end
765
788
 
766
- # Set the byte at _base_ + _offset_ to _value_
789
+ # Sets the byte at _base_ + _offset_ to _value_.
767
790
  def set_byte base, offset, value
768
- emit "# set-byte #{base} #{offset} #{value}\n"
769
791
  # If base is an integer, but offset isn't, swap them
770
792
  if !integer?(offset) && integer?(base)
771
793
  base, offset = [offset, base]
@@ -786,10 +808,8 @@ module Voodoo
786
808
  end
787
809
  end
788
810
 
789
- # Set the word at _base_ + _offset_ * +@WORDSIZE+ to _value_
811
+ # Sets the word at _base_ + _offset_ * +@WORDSIZE+ to _value_.
790
812
  def set_word base, offset, value
791
- emit "# set-word #{base} #{offset} #{value}\n"
792
-
793
813
  value_reg = @TEMPORARIES.pop
794
814
  load_value_into_register value, value_reg
795
815
  begin
@@ -813,7 +833,7 @@ module Voodoo
813
833
  end
814
834
  end
815
835
 
816
- # Define a string with the given value
836
+ # Defines a string with the given value.
817
837
  def string value
818
838
  code = ''
819
839
  value.each_byte do |b|
@@ -828,21 +848,8 @@ module Voodoo
828
848
  emit ".ascii \"#{code}\"\n"
829
849
  end
830
850
 
831
- # Test if a value is a symbol
832
- def symbol? value
833
- value.kind_of? Symbol
834
- end
835
-
836
- # Test if op is a symmetric binary operation (i.e. it will yield the
837
- # same result if the order of its source operands is changed).
838
- def symmetric_binop? op
839
- [:add, :and, :mul, :or, :xor].member? op
840
- end
841
-
842
- # Call a function, re-using the current call frame if possible.
851
+ # Calls a function, re-using the current call frame if possible.
843
852
  def tail_call func, *args
844
- emit "# tail-call #{func} #{args.join ' '}\n"
845
-
846
853
  # Compute number of stack arguments
847
854
  nstackargs = number_of_stack_arguments args.length
848
855
  # If we need more stack arguments than we have now,
@@ -857,17 +864,16 @@ module Voodoo
857
864
  nlocals = @environment.locals
858
865
  (@NREGISTER_ARGS...args.length).each do |i|
859
866
  x = @environment[args[i]]
860
- if x && x[0] == :arg && x[1] >= @NREGISTER_ARGS && x[1] < i
861
- # args[i] is a stack arg, but has an index < i
862
- # That means that, by the time we get to pass arg[i] to the called
863
- # function, it will have been overwritten. So we make a backup.
867
+ if integer?(x) && x < arg_offset(i)
868
+ # args[i] is in a location that will have been overwritten by the
869
+ # time we get around to passing argument i to the called function.
870
+ # Make a backup of the value before we overwrite it.
864
871
  if temporaries.empty?
865
872
  # Oh dear, we're out of temporaries.
866
873
  # Store the value in the current stack frame, extending it
867
874
  # as necessary.
868
875
  if nlocals >= max_locals
869
- emit "addiu $sp, -8\n"
870
- @frame_size = @frame_size + 8
876
+ grow_stack 8
871
877
  end
872
878
  reg = load_value args[i]
873
879
  emit "sw #{reg}, #{local_reference nlocals}\n"
@@ -911,16 +917,6 @@ module Voodoo
911
917
  load_value_into_register args[i], reg
912
918
  end
913
919
 
914
- # Restore saved registers
915
- [@NREGISTER_LOCALS, @environment.locals].min.times do |n|
916
- emit "lw #{local_register n}, #{local_reference n}\n"
917
- end
918
- # Restore return address
919
- emit "lw $31, #{@frame_size - @WORDSIZE}($sp)\n"
920
-
921
- # Adjust stack pointer
922
- emit "addiu $sp, #{@frame_size}\n"
923
-
924
920
  # Load function address
925
921
  if global? func
926
922
  if @imports.has_key? func
@@ -936,13 +932,27 @@ module Voodoo
936
932
  load_value_into_register func, "#{@FUNCTION}"
937
933
  end
938
934
 
935
+ # Restore saved registers
936
+ [@NREGISTER_LOCALS, @environment.locals].min.times do |n|
937
+ emit "lw #{local_register n}, #{local_reference n}\n"
938
+ end
939
+
940
+ # Restore return address
941
+ emit "lw $31, #{@RA_OFFSET}($fp)\n"
942
+
943
+ # Restore stack pointer
944
+ emit "move $sp, $fp\n"
945
+
946
+ # Restore frame pointer
947
+ emit "lw $fp, #{@FP_OFFSET}($fp)\n"
948
+
939
949
  # Perform call
940
950
  emit "jr #{@FUNCTION}\n"
941
951
  # brach delay slot
942
952
  emit "nop\n"
943
953
  end
944
954
 
945
- # Define a word with the given value
955
+ # Defines a word with the given value
946
956
  def word value
947
957
  emit ".int #{value}\n"
948
958
  end