voodoo 1.0.2 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/voodooc +85 -49
- data/lib/voodoo.rb +11 -6
- data/lib/voodoo/code_generator.rb +6 -6
- data/lib/voodoo/compiler.rb +2 -2
- data/lib/voodoo/config.rb +3 -3
- data/lib/voodoo/generators/amd64_nasm_generator.rb +196 -118
- data/lib/voodoo/generators/arm_gas_generator.rb +367 -334
- data/lib/voodoo/generators/common_code_generator.rb +467 -69
- data/lib/voodoo/generators/dummy_generator.rb +10 -0
- data/lib/voodoo/generators/i386_nasm_generator.rb +93 -60
- data/lib/voodoo/generators/mips_gas_generator.rb +275 -265
- data/lib/voodoo/generators/nasm_generator.rb +253 -251
- data/lib/voodoo/parser.rb +46 -22
- data/lib/voodoo/validator.rb +118 -60
- metadata +14 -14
- data/lib/voodoo/generators/arm_gas_generator.rb~ +0 -957
@@ -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
|
-
@
|
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 =
|
50
|
-
# Register used as scratch register
|
51
|
-
@SCRATCH_REG = 'ebx'
|
59
|
+
@RETURN_REG = :eax
|
52
60
|
# Accumulator index
|
53
|
-
@AX =
|
61
|
+
@AX = :eax
|
54
62
|
# Base index
|
55
|
-
@BX =
|
63
|
+
@BX = :ebx
|
56
64
|
# Count index
|
57
|
-
@CX =
|
65
|
+
@CX = :ecx
|
58
66
|
# Data index
|
59
|
-
@DX =
|
67
|
+
@DX = :edx
|
60
68
|
# Base pointer
|
61
|
-
@BP =
|
69
|
+
@BP = :ebp
|
62
70
|
# Stack pointer
|
63
|
-
@SP =
|
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
|
-
#
|
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
|
-
#
|
83
|
-
def
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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
|
-
#
|
93
|
-
def
|
94
|
-
|
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
|
-
#
|
98
|
-
|
99
|
-
|
100
|
-
|
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
|
-
#
|
121
|
-
#
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
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,
|
151
|
-
newarg_ref = "[ebp + #{
|
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 =
|
190
|
+
value_ref = :eax
|
157
191
|
end
|
158
|
-
emit "mov #{@WORD_NAME}
|
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,
|
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
|
-
@
|
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 =
|
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 =
|
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
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
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
|
171
|
+
# Return the offset from the frame base at which the nth argument is
|
153
172
|
# stored.
|
154
173
|
def arg_offset n
|
155
|
-
|
174
|
+
n * @WORDSIZE
|
156
175
|
end
|
157
176
|
|
158
|
-
#
|
177
|
+
# Returns an $fp-relative reference for the nth (0-based) argument.
|
159
178
|
def arg_reference n
|
160
|
-
|
179
|
+
offset_reference(arg_offset(n))
|
161
180
|
end
|
162
181
|
|
163
|
-
#
|
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
|
-
|
174
|
-
|
175
|
-
|
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
|
-
|
179
|
-
|
180
|
-
|
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
|
-
#
|
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
|
-
|
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
|
-
#
|
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
|
-
#
|
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
|
-
|
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
|
-
|
265
|
-
@frame_size = @frame_size - increment
|
303
|
+
grow_stack -increment
|
266
304
|
|
267
305
|
# restore gp
|
268
|
-
emit "lw #{@GOT}, #{@
|
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
|
-
#
|
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
|
-
#
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
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
|
-
#
|
322
|
-
def
|
323
|
-
|
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
|
-
#
|
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
|
-
#
|
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, #{@
|
440
|
+
emit "lw $31, #{@RA_OFFSET}($fp)\n"
|
364
441
|
# Restore stack pointer
|
365
|
-
emit "
|
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
|
-
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
513
|
-
def
|
514
|
-
|
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
|
-
#
|
518
|
-
def
|
519
|
-
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
|
632
|
-
|
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
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
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
|
-
|
660
|
-
|
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
|
-
#
|
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
|
-
#
|
679
|
-
#
|
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
|
-
|
718
|
+
-(n + 4) * @WORDSIZE
|
683
719
|
end
|
684
720
|
|
685
|
-
#
|
721
|
+
# Returns an $sp-relative reference for the nth (0-based) local.
|
686
722
|
def local_reference n
|
687
|
-
|
723
|
+
offset_reference(local_offset(n))
|
688
724
|
end
|
689
725
|
|
690
|
-
#
|
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
|
-
|
694
|
-
"$#{@REGISTER_LOCAL_BASE + n}"
|
695
|
-
else
|
696
|
-
nil
|
697
|
-
end
|
729
|
+
@LOCAL_REGISTERS[n]
|
698
730
|
end
|
699
731
|
|
700
|
-
#
|
732
|
+
# Computes the maximum number of locals that would fit in the current
|
701
733
|
# frame size.
|
702
734
|
def max_locals
|
703
|
-
|
704
|
-
|
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
|
-
#
|
713
|
-
|
714
|
-
|
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
|
-
#
|
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
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
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
|
-
|
750
|
-
|
751
|
-
|
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
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
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
|
-
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
861
|
-
# args[i] is a
|
862
|
-
#
|
863
|
-
#
|
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
|
-
|
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
|
-
#
|
955
|
+
# Defines a word with the given value
|
946
956
|
def word value
|
947
957
|
emit ".int #{value}\n"
|
948
958
|
end
|