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.
- 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
|