voodoo 1.0.2 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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