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
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'voodoo/generators/common_code_generator'
|
2
|
+
require 'set'
|
2
3
|
|
3
4
|
module Voodoo
|
4
5
|
# = ARM GNU Assembler Code Generator
|
@@ -44,7 +45,7 @@ module Voodoo
|
|
44
45
|
# padding
|
45
46
|
# argn
|
46
47
|
# :
|
47
|
-
# arg4
|
48
|
+
# arg4 <-- r11 points here
|
48
49
|
# saved r14
|
49
50
|
# saved r11
|
50
51
|
# :
|
@@ -52,30 +53,44 @@ module Voodoo
|
|
52
53
|
#
|
53
54
|
# == Register Usage
|
54
55
|
#
|
55
|
-
# Inside a function, registers r4..r8
|
56
|
-
#
|
56
|
+
# Inside a function, registers r4..r8 and r10 are used for local variables
|
57
|
+
# and function arguments, whereas r11 is used as a frame pointer.
|
57
58
|
#
|
58
59
|
# r12 is used as a temporary, and r3 is used when another temporary
|
59
60
|
# is needed.
|
60
61
|
#
|
61
62
|
class ARMGasGenerator < CommonCodeGenerator
|
62
63
|
def initialize params
|
63
|
-
@
|
64
|
+
@WORDSIZE_BITS = 2
|
65
|
+
@WORDSIZE = 1 << @WORDSIZE_BITS
|
64
66
|
@CODE_ALIGNMENT = 4
|
65
67
|
@DATA_ALIGNMENT = @WORDSIZE
|
66
68
|
@FUNCTION_ALIGNMENT = @WORDSIZE
|
67
|
-
|
69
|
+
@STACK_ALIGNMENT_BITS = 3
|
70
|
+
@STACK_ALIGNMENT = 1 << @STACK_ALIGNMENT_BITS
|
71
|
+
# Registers used for storing local variables.
|
72
|
+
@LOCAL_REGISTERS = [:r4, :r5, :r6, :r7, :r8, :r10]
|
73
|
+
# Set of registers used for storing local variables.
|
74
|
+
@LOCAL_REGISTERS_SET = Set.new @LOCAL_REGISTERS
|
75
|
+
# Registers to be saved by save-frame.
|
76
|
+
@SAVE_FRAME_REGISTERS = [:r4, :r5, :r6, :r7, :r8,
|
77
|
+
:r9, :r10, :r11, :r13, :r14]
|
78
|
+
# Hash from register names to offsets in saved frame.
|
79
|
+
@SAVED_FRAME_LAYOUT = {}
|
80
|
+
@SAVE_FRAME_REGISTERS.each_with_index { |r,i| @SAVED_FRAME_LAYOUT[r] = i }
|
81
|
+
@INITIAL_FRAME_SIZE = 2 * @WORDSIZE
|
68
82
|
@NREGISTER_ARGS = 4
|
69
|
-
@NREGISTER_LOCALS =
|
83
|
+
@NREGISTER_LOCALS = @LOCAL_REGISTERS.length
|
84
|
+
@FP = :r11
|
70
85
|
@RETURN = :r0
|
71
|
-
@
|
86
|
+
@TEMPORARIES = [:r12, :r3, :r2, :r1]
|
72
87
|
@constants = []
|
73
88
|
@frame_offset = 0
|
74
89
|
@frame_size = 0
|
75
90
|
@function_end_label = nil
|
76
91
|
@imports = {}
|
77
92
|
@if_labels = []
|
78
|
-
@saved_registers = []
|
93
|
+
@saved_registers = [] # registers we've saved in the current frame
|
79
94
|
super params
|
80
95
|
@output_file_suffix = '.s'
|
81
96
|
@features.merge! \
|
@@ -93,54 +108,62 @@ module Voodoo
|
|
93
108
|
label
|
94
109
|
end
|
95
110
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
case @section
|
100
|
-
when :code
|
101
|
-
alignment = @CODE_ALIGNMENT
|
102
|
-
when :data
|
103
|
-
alignment = @DATA_ALIGNMENT
|
104
|
-
when :function
|
105
|
-
alignment = @FUNCTION_ALIGNMENT
|
106
|
-
else
|
107
|
-
# Use data alignment as default
|
108
|
-
alignment = @DATA_ALIGNMENT
|
109
|
-
end
|
110
|
-
end
|
111
|
-
emit ".align #{alignment}\n" unless alignment == 0
|
111
|
+
# Returns the fp-relative offset for the nth (0-based) argument.
|
112
|
+
def arg_offset n
|
113
|
+
(n - @NREGISTER_ARGS) * @WORDSIZE
|
112
114
|
end
|
113
|
-
|
114
|
-
# Returns an
|
115
|
+
|
116
|
+
# Returns an fp-relative reference for the nth (0-based) argument.
|
115
117
|
def arg_reference n
|
116
|
-
|
118
|
+
offset_reference arg_offset(n)
|
117
119
|
end
|
118
120
|
|
119
121
|
# Return the register in which the nth (0-based) argument is stored, or
|
120
122
|
# nil if not stored in a register
|
121
123
|
def arg_register n
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
"v#{n + 1}"
|
124
|
+
if n < @NREGISTER_ARGS
|
125
|
+
# We copied the argument to one of the local registers.
|
126
|
+
@LOCAL_REGISTERS[n]
|
126
127
|
else
|
127
128
|
nil
|
128
129
|
end
|
129
130
|
end
|
130
131
|
|
131
|
-
|
132
|
-
|
133
|
-
|
132
|
+
def auto_bytes value, register
|
133
|
+
if value.kind_of? Integer
|
134
|
+
grow_frame value
|
135
|
+
else
|
136
|
+
temporary = register == :sp ? @TEMPORARIES[0] : register
|
137
|
+
load_value_into_register value, temporary
|
138
|
+
auto_bytes_register temporary
|
139
|
+
end
|
140
|
+
emit "cpy #{register}, sp\n" unless register == :sp
|
134
141
|
end
|
135
|
-
|
136
|
-
#
|
137
|
-
|
138
|
-
|
142
|
+
|
143
|
+
# auto-bytes where the value is supplied in a register and the return
|
144
|
+
# value will be in sp. register must not be sp.
|
145
|
+
def auto_bytes_register register
|
146
|
+
temporary = register == @TEMPORARIES[0] ? :r3 : @TEMPORARIES[0]
|
147
|
+
emit "add #{register}, #{register}, \##{@STACK_ALIGNMENT - 1}\n"
|
148
|
+
emit "mvn #{temporary}, \##{@STACK_ALIGNMENT - 1}\n"
|
149
|
+
emit "and #{register}, #{register}, #{temporary}\n"
|
150
|
+
emit "sub sp, #{register}\n"
|
139
151
|
end
|
140
|
-
|
152
|
+
|
153
|
+
def auto_words value, register
|
154
|
+
if value.kind_of? Integer
|
155
|
+
auto_bytes(value * @WORDSIZE, register)
|
156
|
+
else
|
157
|
+
raise "Can't use :sp as a register for auto_words" if register == :sp
|
158
|
+
load_value_into_register value, register
|
159
|
+
emit "lsl #{register}, #{register}, \##{@WORDSIZE_BITS}\n"
|
160
|
+
auto_bytes_register register
|
161
|
+
emit "cpy #{register}, sp\n"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
141
165
|
# Begins a new block.
|
142
166
|
def begin_block *code
|
143
|
-
emit "# begin block\n"
|
144
167
|
# If we are starting a block at top level, create a frame
|
145
168
|
if @environment == @top_level
|
146
169
|
nlocals = count_locals code
|
@@ -156,18 +179,18 @@ module Voodoo
|
|
156
179
|
end
|
157
180
|
|
158
181
|
@function_end_label = gensym
|
159
|
-
emit "# function #{formals.join ' '}\n"
|
160
182
|
environment = Environment.new @environment
|
161
|
-
|
183
|
+
formals.each_with_index do |formal, i|
|
184
|
+
if i < @NREGISTER_ARGS
|
185
|
+
environment.add_arg formal, arg_register(i)
|
186
|
+
else
|
187
|
+
environment.add_arg formal, arg_offset(i)
|
188
|
+
end
|
189
|
+
end
|
162
190
|
@environment = environment
|
163
191
|
emit_function_prologue formals, nlocals
|
164
192
|
end
|
165
193
|
|
166
|
-
# Test if op is a binary operation
|
167
|
-
def binop? op
|
168
|
-
assymetric_binop?(op) || symmetric_binop?(op)
|
169
|
-
end
|
170
|
-
|
171
194
|
# Define a byte with the given value
|
172
195
|
def byte value
|
173
196
|
emit ".byte #{value}\n"
|
@@ -175,8 +198,6 @@ module Voodoo
|
|
175
198
|
|
176
199
|
# Call a function.
|
177
200
|
def call func, *args
|
178
|
-
emit "# call #{func} #{args.join ' '}\n"
|
179
|
-
|
180
201
|
# Calculate how many arguments need to be pushed on
|
181
202
|
# the stack, and allocate space for them.
|
182
203
|
nstack_args = number_of_stack_arguments args.length
|
@@ -186,23 +207,27 @@ module Voodoo
|
|
186
207
|
|
187
208
|
# Put stack arguments on the stack
|
188
209
|
(@NREGISTER_ARGS...args.length).each do |n|
|
189
|
-
|
190
|
-
|
191
|
-
"
|
210
|
+
with_temporary do |temporary|
|
211
|
+
load_value_into_register args[n], temporary
|
212
|
+
emit "str #{temporary}, " +
|
213
|
+
"[sp , \##{(n - @NREGISTER_ARGS) * @WORDSIZE}]\n"
|
214
|
+
end
|
192
215
|
end
|
193
216
|
|
194
217
|
# Put register arguments in the right registers
|
195
218
|
nregister_args = number_of_register_arguments args.length
|
196
219
|
nregister_args.times do |n|
|
197
|
-
load_value_into_register args[n], :"
|
220
|
+
load_value_into_register args[n], :"r#{n}"
|
198
221
|
end
|
199
222
|
|
200
223
|
# Call function
|
201
224
|
if global? func
|
202
225
|
emit "bl #{func}\n"
|
203
226
|
else
|
204
|
-
|
205
|
-
|
227
|
+
with_temporary do |temporary|
|
228
|
+
func_reg = load_value func, temporary
|
229
|
+
emit "blx #{func_reg}\n"
|
230
|
+
end
|
206
231
|
end
|
207
232
|
|
208
233
|
# Restore original stack frame
|
@@ -224,18 +249,17 @@ module Voodoo
|
|
224
249
|
# Save the registers we will clobber to the stack.
|
225
250
|
clobbered = []
|
226
251
|
nregister_vars.times do |i|
|
227
|
-
clobbered <<
|
252
|
+
clobbered << @LOCAL_REGISTERS[i]
|
228
253
|
end
|
254
|
+
clobbered << @FP
|
229
255
|
clobbered << :lr if save_lr
|
230
256
|
@saved_registers = clobbered
|
231
257
|
emit "stmfd sp!, {#{clobbered.join ', '}}\n"
|
258
|
+
emit "add #{@FP}, sp, \##{clobbered.length * @WORDSIZE}\n"
|
232
259
|
|
233
260
|
# Calculate frame size so that the stack pointer will
|
234
261
|
# be properly aligned at the end of emit_function_prologue.
|
235
|
-
@frame_size = (clobbered.length + nstack_vars) * @WORDSIZE
|
236
|
-
if @frame_size % 8 != 0
|
237
|
-
@frame_size = (@frame_size + 7) / 8 * 8
|
238
|
-
end
|
262
|
+
@frame_size = stack_align((clobbered.length + nstack_vars) * @WORDSIZE)
|
239
263
|
extra_space = @frame_size - clobbered.length * @WORDSIZE
|
240
264
|
if extra_space > 0
|
241
265
|
emit "sub sp, sp, \##{extra_space}\n"
|
@@ -243,35 +267,26 @@ module Voodoo
|
|
243
267
|
@frame_offset = 0
|
244
268
|
end
|
245
269
|
|
270
|
+
# Emits a comment.
|
271
|
+
def comment text
|
272
|
+
emit "# #{text}\n"
|
273
|
+
end
|
274
|
+
|
246
275
|
# Start a conditional using the specified branch instruction
|
247
276
|
# after the comparison.
|
248
277
|
def common_if comp, x, y = nil
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
yreg = load_value y, :a4
|
278
|
+
with_temporary do |temporary|
|
279
|
+
xreg = load_value x, temporary
|
280
|
+
yreg = load_value y, :a4
|
253
281
|
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
emit "cmp #{xreg}, #{yreg}\n"
|
258
|
-
|
259
|
-
lut = { :ifeq => "bne", :ifge => "blt", :ifgt => "ble",
|
260
|
-
:ifle => "bgt", :iflt => "bge", :ifne => "beq" }
|
261
|
-
emit "#{lut[comp]} #{falselabel}\n"
|
262
|
-
end
|
282
|
+
falselabel = @environment.gensym
|
283
|
+
@if_labels.push falselabel
|
263
284
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
if statement[0] == :let
|
270
|
-
# let introduces a single local
|
271
|
-
count = count + 1
|
272
|
-
end
|
273
|
-
end
|
274
|
-
count
|
285
|
+
emit "cmp #{xreg}, #{yreg}\n"
|
286
|
+
lut = { :ifeq => "bne", :ifge => "blt", :ifgt => "ble",
|
287
|
+
:ifle => "bgt", :iflt => "bge", :ifne => "beq" }
|
288
|
+
emit "#{lut[comp]} #{falselabel}\n"
|
289
|
+
end
|
275
290
|
end
|
276
291
|
|
277
292
|
# Destroys the current stack frame.
|
@@ -279,10 +294,7 @@ module Voodoo
|
|
279
294
|
def destroy_frame ret = false
|
280
295
|
# Set sp back to where saved registers were stored
|
281
296
|
saved = @saved_registers
|
282
|
-
|
283
|
-
if offset != 0
|
284
|
-
emit "add sp, sp, \##{offset}\n"
|
285
|
-
end
|
297
|
+
emit "sub sp, #{@FP}, \##{saved.length * @WORDSIZE}\n"
|
286
298
|
|
287
299
|
if ret
|
288
300
|
index = saved.index :lr
|
@@ -293,10 +305,15 @@ module Voodoo
|
|
293
305
|
end
|
294
306
|
end
|
295
307
|
emit "ldmfd sp!, {#{saved.join ', '}}\n"
|
296
|
-
|
308
|
+
|
297
309
|
emit_constants if ret
|
298
310
|
end
|
299
311
|
|
312
|
+
# Aligns on the next multiple of +n+ bytes.
|
313
|
+
def emit_align n
|
314
|
+
emit ".align #{n}\n"
|
315
|
+
end
|
316
|
+
|
300
317
|
# Writes any constants that need to be written to the instruction
|
301
318
|
# stream, and clears the list of constants that need to be written.
|
302
319
|
def emit_constants
|
@@ -312,7 +329,7 @@ module Voodoo
|
|
312
329
|
# Calculate the number of arguments we were passed in
|
313
330
|
# registers, the total number of values we need to save
|
314
331
|
# on the stack, then create a stack frame and save
|
315
|
-
# the
|
332
|
+
# the registers we will be using.
|
316
333
|
nregister_args = [formals.length, @NREGISTER_ARGS].min
|
317
334
|
nvars = nregister_args + nlocals
|
318
335
|
create_frame nvars, true
|
@@ -320,14 +337,44 @@ module Voodoo
|
|
320
337
|
# Move arguments that were passed in registers into
|
321
338
|
# callee-save registers.
|
322
339
|
nregister_args.times do |i|
|
323
|
-
emit "cpy
|
340
|
+
emit "cpy #{@LOCAL_REGISTERS[i]}, r#{i}\n"
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
# Emits a label type annotation.
|
345
|
+
def emit_label_type name, type
|
346
|
+
type_map = {
|
347
|
+
:code => "%function",
|
348
|
+
:data => "%object",
|
349
|
+
}
|
350
|
+
emit ".type #{name}, #{type_map[type]}\n"
|
351
|
+
end
|
352
|
+
|
353
|
+
# Emits a label size annotation.
|
354
|
+
def emit_label_size name
|
355
|
+
emit ".size #{name}, .-#{name}\n"
|
356
|
+
end
|
357
|
+
|
358
|
+
# Loads a word into a register.
|
359
|
+
def emit_load_word register, base, offset
|
360
|
+
if offset == 0
|
361
|
+
emit "ldr #{register}, [#{base}]\n"
|
362
|
+
else
|
363
|
+
emit "ldr #{register}, [#{base}, \##{offset * @WORDSIZE}]\n"
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
# Stores the value of a register in memory.
|
368
|
+
def emit_store_word register, base, offset
|
369
|
+
if offset == 0
|
370
|
+
emit "str #{register}, [#{base}]\n"
|
371
|
+
else
|
372
|
+
emit "str #{register}, [#{base}, \##{offset * @WORDSIZE}]\n"
|
324
373
|
end
|
325
374
|
end
|
326
375
|
|
327
376
|
# Ends the current block.
|
328
377
|
def end_block
|
329
|
-
emit "# end block\n"
|
330
|
-
|
331
378
|
# If we are returning to top level, restore stack pointer
|
332
379
|
# and saved registers.
|
333
380
|
if @environment.parent == @top_level
|
@@ -358,14 +405,12 @@ module Voodoo
|
|
358
405
|
raise "Cannot end function when not in a function"
|
359
406
|
end
|
360
407
|
|
361
|
-
emit "# function epilogue\n"
|
362
408
|
label @function_end_label
|
363
409
|
|
364
410
|
destroy_frame true
|
365
411
|
@frame_size = 0
|
366
412
|
@frame_offset = 0
|
367
413
|
@saved_registers = []
|
368
|
-
emit "# end function\n\n"
|
369
414
|
@environment = @top_level
|
370
415
|
end
|
371
416
|
|
@@ -394,25 +439,55 @@ module Voodoo
|
|
394
439
|
return
|
395
440
|
end
|
396
441
|
|
397
|
-
|
398
|
-
|
442
|
+
with_temporaries(2) do |t1,t2|
|
443
|
+
x = load_value expr[1], t1
|
399
444
|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
445
|
+
case op
|
446
|
+
when :mul
|
447
|
+
# Operand must be in a register for these ops.
|
448
|
+
y = load_value expr[2], t2
|
449
|
+
else
|
450
|
+
y = value_ref expr[2], t2
|
451
|
+
end
|
452
|
+
|
453
|
+
case op
|
454
|
+
when :bsr
|
455
|
+
emit "lsr #{register}, #{x}, #{y}\n"
|
456
|
+
when :or
|
457
|
+
emit "orr #{register}, #{x}, #{y}\n"
|
458
|
+
when :mul
|
459
|
+
# Can't store result in same register as first operand.
|
460
|
+
if register == x
|
461
|
+
if x == y
|
462
|
+
# All using the same register. Move x to a different
|
463
|
+
# register to make this work.
|
464
|
+
temp = (x == t1) ? t2 : t1
|
465
|
+
emit "cpy #{temp}, #{x}\n"
|
466
|
+
emit "mul #{register}, #{temp}, #{y}\n"
|
467
|
+
else
|
468
|
+
# Multiplication is commutative. Just swap x and y.
|
469
|
+
emit "mul #{register}, #{y}, #{x}\n"
|
470
|
+
end
|
471
|
+
else
|
472
|
+
# Common case, register and x are different.
|
473
|
+
emit "mul #{register}, #{x}, #{y}\n"
|
474
|
+
end
|
475
|
+
when :rol
|
476
|
+
if integer? expr[2]
|
477
|
+
y = "\##{32 - expr[2]}"
|
478
|
+
else
|
479
|
+
emit "rsb #{y}, #{y}, #32\n"
|
480
|
+
end
|
481
|
+
emit "ror #{register}, #{x}, #{y}\n"
|
482
|
+
when :shl
|
483
|
+
emit "lsl #{register}, #{x}, #{y}\n"
|
484
|
+
when :shr
|
485
|
+
emit "lsr #{register}, #{x}, #{y}\n"
|
486
|
+
when :xor
|
487
|
+
emit "eor #{register}, #{x}, #{y}\n"
|
488
|
+
else
|
489
|
+
emit "#{expr[0]} #{register}, #{x}, #{y}\n"
|
490
|
+
end
|
416
491
|
end
|
417
492
|
end
|
418
493
|
|
@@ -425,6 +500,10 @@ module Voodoo
|
|
425
500
|
# Evaluate expression
|
426
501
|
op = expr[0]
|
427
502
|
case op
|
503
|
+
when :'auto-bytes'
|
504
|
+
auto_bytes expr[1], register
|
505
|
+
when :'auto-words'
|
506
|
+
auto_words expr[1], register
|
428
507
|
when :call
|
429
508
|
call *expr[1..-1]
|
430
509
|
emit "cpy #{register}, #{@RETURN}\n" if register != @RETURN
|
@@ -434,8 +513,10 @@ module Voodoo
|
|
434
513
|
get_word expr[1], expr[2], register
|
435
514
|
when :not
|
436
515
|
load_value_into_register expr[1], register
|
437
|
-
|
438
|
-
|
516
|
+
with_temporary do |temporary|
|
517
|
+
emit "mvn #{temporary}, #0\n"
|
518
|
+
emit "eor #{register}, #{register}, #{temporary}\n"
|
519
|
+
end
|
439
520
|
else
|
440
521
|
if binop? op
|
441
522
|
eval_binop expr, register
|
@@ -451,14 +532,6 @@ module Voodoo
|
|
451
532
|
symbols.each { |sym| emit ".globl #{sym}\n" }
|
452
533
|
end
|
453
534
|
|
454
|
-
# Add a function to the current section
|
455
|
-
def function formals, *code
|
456
|
-
nlocals = count_locals code
|
457
|
-
begin_function formals, nlocals
|
458
|
-
code.each { |action| add section, action }
|
459
|
-
end_function
|
460
|
-
end
|
461
|
-
|
462
535
|
# Load byte from _base_ + _offset_ into _register_
|
463
536
|
def get_byte base, offset, register
|
464
537
|
# If base is an integer, but offset isn't, swap them
|
@@ -467,43 +540,53 @@ module Voodoo
|
|
467
540
|
end
|
468
541
|
|
469
542
|
if integer? offset
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
543
|
+
with_temporary do |temporary|
|
544
|
+
base_reg = load_value base, temporary
|
545
|
+
if offset == 0
|
546
|
+
emit "ldrb #{register}, [#{base_reg}]\n"
|
547
|
+
else
|
548
|
+
emit "ldrb #{register}, [#{base_reg}, \##{offset}]\n"
|
549
|
+
end
|
475
550
|
end
|
476
551
|
else
|
477
|
-
|
478
|
-
|
479
|
-
|
552
|
+
with_temporaries(2) do |t1,t2|
|
553
|
+
base_reg = load_value base, t1
|
554
|
+
offset_reg = load_value offset, t2
|
555
|
+
emit "ldrb #{register}, [#{base_reg}, #{offset_reg}]\n"
|
556
|
+
end
|
480
557
|
end
|
481
558
|
end
|
482
559
|
|
483
560
|
# Load word from _base_ + _offset_ * _@WORDSIZE_ into _register_
|
484
561
|
def get_word base, offset, register
|
485
562
|
if integer? offset
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
563
|
+
with_temporary do |temporary|
|
564
|
+
base_reg = load_value base, temporary
|
565
|
+
if offset == 0
|
566
|
+
emit "ldr #{register}, [#{base_reg}]\n"
|
567
|
+
else
|
568
|
+
emit "ldr #{register}, [#{base_reg}, \##{offset * @WORDSIZE}]\n"
|
569
|
+
end
|
491
570
|
end
|
492
571
|
else
|
493
|
-
|
494
|
-
|
495
|
-
|
572
|
+
with_temporaries(2) do |t1,t2|
|
573
|
+
base_reg = load_value base, t1
|
574
|
+
offset_reg = load_value offset, t2
|
575
|
+
emit "ldr #{register}, [#{base_reg}, #{offset_reg}, LSL #2]\n"
|
576
|
+
end
|
496
577
|
end
|
497
578
|
end
|
498
579
|
|
499
|
-
# Test if a symbol refers to a global
|
500
|
-
def global? symbol
|
501
|
-
symbol?(symbol) && @environment[symbol] == nil
|
502
|
-
end
|
503
|
-
|
504
580
|
# Jump to a label.
|
505
581
|
def goto label
|
506
|
-
|
582
|
+
if global? label
|
583
|
+
emit "b #{label}\n"
|
584
|
+
else
|
585
|
+
with_temporary do |temporary|
|
586
|
+
register = load_value label, temporary
|
587
|
+
emit "cpy pc, #{register}\n"
|
588
|
+
end
|
589
|
+
end
|
507
590
|
|
508
591
|
# If we have constants that need to be emitted, do so now
|
509
592
|
emit_constants
|
@@ -512,7 +595,7 @@ module Voodoo
|
|
512
595
|
# Grows the current frame by n words, plus padding to
|
513
596
|
# respect alignment rules.
|
514
597
|
def grow_frame nwords
|
515
|
-
increment = (nwords * @WORDSIZE
|
598
|
+
increment = stack_align(nwords * @WORDSIZE)
|
516
599
|
emit "sub sp, sp, \##{increment}\n"
|
517
600
|
@frame_size = @frame_size + increment
|
518
601
|
@frame_offset = @frame_offset + increment
|
@@ -520,7 +603,6 @@ module Voodoo
|
|
520
603
|
|
521
604
|
# Start the false path of a conditional.
|
522
605
|
def ifelse
|
523
|
-
emit "# else\n"
|
524
606
|
newlabel = @environment.gensym
|
525
607
|
goto newlabel
|
526
608
|
lbl = @if_labels.pop
|
@@ -564,140 +646,69 @@ module Voodoo
|
|
564
646
|
symbols.each { |sym| @imports[sym] = sym }
|
565
647
|
end
|
566
648
|
|
567
|
-
# Test if a value is an integer
|
568
|
-
def integer? value
|
569
|
-
value.kind_of? Integer
|
570
|
-
end
|
571
|
-
|
572
|
-
# Emit a label
|
573
|
-
def label name
|
574
|
-
emit "#{name}:\n"
|
575
|
-
end
|
576
|
-
|
577
649
|
# Introduce a new local variable
|
578
650
|
def let symbol, *expr
|
579
|
-
emit "# let #{symbol} #{expr.join ' '}\n"
|
580
651
|
n = @environment.locals
|
581
|
-
@environment.add_local symbol
|
582
|
-
|
583
652
|
register = local_register n
|
653
|
+
|
584
654
|
if register
|
585
655
|
# We will use a register to store the value
|
656
|
+
@environment.add_local symbol, register
|
586
657
|
eval_expr expr, register
|
587
658
|
else
|
588
659
|
# We will use the stack to store the value
|
589
|
-
|
590
|
-
|
591
|
-
|
660
|
+
offset = local_offset n
|
661
|
+
@environment.add_local symbol, offset
|
662
|
+
with_temporary do |temporary|
|
663
|
+
eval_expr expr, temporary
|
664
|
+
emit "str #{temporary}, #{offset_reference offset}\n"
|
665
|
+
end
|
592
666
|
end
|
593
667
|
end
|
594
668
|
|
595
|
-
#
|
596
|
-
def load_at address, register
|
669
|
+
# Loads the value at the given address.
|
670
|
+
def load_at address, register
|
597
671
|
load_value_into_register address, register
|
598
672
|
emit "ldr #{register}, [#{register}]\n"
|
599
673
|
register
|
600
674
|
end
|
601
675
|
|
602
|
-
#
|
603
|
-
#
|
604
|
-
#
|
605
|
-
#
|
606
|
-
#
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
if x >= 0 && x <= 255
|
612
|
-
emit "mov #{register}, \##{x}\n"
|
613
|
-
return register
|
614
|
-
elsif x >= -255 && x < 0
|
615
|
-
emit "mvn #{register}, \##{-(x + 1)}\n"
|
616
|
-
return register
|
617
|
-
else
|
618
|
-
lbl = add_constant x
|
619
|
-
emit "ldr #{register}, #{lbl}\n"
|
620
|
-
return register
|
621
|
-
end
|
622
|
-
elsif symbol? x
|
623
|
-
binding = @environment[x]
|
624
|
-
if binding
|
625
|
-
case binding[0]
|
626
|
-
when :arg
|
627
|
-
n = binding[1]
|
628
|
-
if register_arg? n
|
629
|
-
return arg_register(n)
|
630
|
-
else
|
631
|
-
emit "ldr #{register}, #{arg_reference binding[1]}\n"
|
632
|
-
return register
|
633
|
-
end
|
634
|
-
when :local
|
635
|
-
n = binding[1]
|
636
|
-
if register_local? n
|
637
|
-
return local_register(n)
|
638
|
-
else
|
639
|
-
emit "ldr #{register}, #{local_reference n}\n"
|
640
|
-
return register
|
641
|
-
end
|
642
|
-
else
|
643
|
-
raise "Don't know how to load #{x.inspect}"
|
644
|
-
end
|
645
|
-
else
|
646
|
-
# Assume global
|
647
|
-
lbl = add_constant x
|
648
|
-
emit "ldr #{register}, #{lbl}\n"
|
649
|
-
return register
|
650
|
-
end
|
651
|
-
elsif at_expr? x
|
652
|
-
load_at x[1], register
|
676
|
+
# Loads a value into some register.
|
677
|
+
# If the value is already in a register, does nothing.
|
678
|
+
# Else, loads the value into the register given as the
|
679
|
+
# second argument.
|
680
|
+
# Returns the name of the register the value is in.
|
681
|
+
def load_value x, register
|
682
|
+
ref = value_ref x, register
|
683
|
+
if register? ref
|
684
|
+
ref
|
653
685
|
else
|
654
|
-
|
686
|
+
emit "mov #{register}, #{ref}\n"
|
687
|
+
register
|
655
688
|
end
|
656
689
|
end
|
657
690
|
|
658
|
-
#
|
691
|
+
# Loads a value into a specific register.
|
659
692
|
def load_value_into_register x, register
|
660
|
-
|
661
|
-
if
|
662
|
-
|
663
|
-
|
664
|
-
end
|
665
|
-
|
666
|
-
# Returns an sp-relative reference for the nth (0-based) local.
|
667
|
-
def local_reference n
|
668
|
-
"[sp, \##{@frame_offset + (number_of_register_arguments + n) * @WORDSIZE}]"
|
669
|
-
end
|
670
|
-
|
671
|
-
# Return the register in which the nth local (0-based) is stored, or
|
672
|
-
# nil if not stored in a register
|
673
|
-
def local_register n
|
674
|
-
if register_local? n
|
675
|
-
n = n + number_of_register_arguments
|
676
|
-
if n < 5
|
677
|
-
"v#{n + 1}"
|
693
|
+
ref = value_ref x, register
|
694
|
+
if ref != register
|
695
|
+
if register? ref
|
696
|
+
emit "cpy #{register}, #{ref}\n"
|
678
697
|
else
|
679
|
-
"
|
698
|
+
emit "mov #{register}, #{ref}\n"
|
680
699
|
end
|
681
|
-
else
|
682
|
-
nil
|
683
700
|
end
|
701
|
+
register
|
684
702
|
end
|
685
703
|
|
686
|
-
#
|
687
|
-
|
688
|
-
|
689
|
-
[n, @NREGISTER_ARGS].min
|
690
|
-
end
|
691
|
-
|
692
|
-
# Calculate the number of stack arguments,
|
693
|
-
# given the total number of arguments.
|
694
|
-
def number_of_stack_arguments n = @environment.args
|
695
|
-
[0, n - @NREGISTER_ARGS].max
|
704
|
+
# Returns the fp-relative reference for the nth (0-based) local.
|
705
|
+
def local_offset n
|
706
|
+
-@INITIAL_FRAME_SIZE - ((n + number_of_register_arguments) * @WORDSIZE)
|
696
707
|
end
|
697
|
-
|
698
|
-
#
|
699
|
-
def
|
700
|
-
|
708
|
+
|
709
|
+
# Given an offset, returns an fp-relative reference.
|
710
|
+
def offset_reference offset
|
711
|
+
"[#{@FP}, \##{offset}]"
|
701
712
|
end
|
702
713
|
|
703
714
|
# Returns true if the nth (0-based) local is stored in a register
|
@@ -710,50 +721,37 @@ module Voodoo
|
|
710
721
|
# _words_ may contain an expression to be evaluated. The result
|
711
722
|
# of the evaluation is returned from the function.
|
712
723
|
def ret *words
|
713
|
-
emit "# return #{words.join ' '}\n"
|
714
724
|
# Compute return value and store it in @RETURN
|
715
725
|
eval_expr(words, @RETURN) unless words.empty?
|
716
726
|
# Go to epilogue
|
717
727
|
goto @function_end_label
|
718
728
|
end
|
719
|
-
|
729
|
+
|
720
730
|
# Set a variable to the result of evaluating an expression
|
721
731
|
def set symbol, *expr
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
end
|
728
|
-
|
729
|
-
register = nil
|
730
|
-
case x[0]
|
731
|
-
when :arg
|
732
|
-
register = arg_register x[1]
|
733
|
-
when :local
|
734
|
-
register = local_register x[1]
|
735
|
-
end
|
736
|
-
|
737
|
-
if register
|
738
|
-
# Set new value
|
739
|
-
eval_expr expr, register
|
740
|
-
else
|
741
|
-
case x[0]
|
742
|
-
when :local
|
743
|
-
ref = local_reference x[1]
|
744
|
-
when :arg
|
745
|
-
ref = arg_reference x[1]
|
746
|
-
else
|
747
|
-
raise "??? #{sym} is neither a local nor an argument"
|
732
|
+
if at_expr? symbol
|
733
|
+
with_temporaries(2) do |t1,t2|
|
734
|
+
eval_expr expr, t1
|
735
|
+
register = load_value symbol[1], t2
|
736
|
+
emit "str #{t1}, [#{register}]\n"
|
748
737
|
end
|
749
|
-
|
750
|
-
|
738
|
+
else
|
739
|
+
x = @environment[symbol]
|
740
|
+
if x == nil
|
741
|
+
raise "Cannot change value of constant #{symbol}"
|
742
|
+
elsif x.kind_of? Symbol
|
743
|
+
eval_expr expr, x
|
744
|
+
else
|
745
|
+
with_temporary do |temporary|
|
746
|
+
eval_expr expr, temporary
|
747
|
+
emit "str #{temporary}, #{offset_reference x}\n"
|
748
|
+
end
|
749
|
+
end
|
751
750
|
end
|
752
751
|
end
|
753
752
|
|
754
753
|
# Set the byte at _base_ + _offset_ to _value_
|
755
754
|
def set_byte base, offset, value
|
756
|
-
emit "# set-byte #{base} #{offset} #{value}\n"
|
757
755
|
# If base is an integer, but offset isn't, swap them
|
758
756
|
if !integer?(offset) && integer?(base)
|
759
757
|
base, offset = [offset, base]
|
@@ -761,22 +759,25 @@ module Voodoo
|
|
761
759
|
|
762
760
|
if integer? offset
|
763
761
|
base_reg = load_value base, :a4
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
762
|
+
with_temporary do |temporary|
|
763
|
+
load_value_into_register value, temporary
|
764
|
+
if offset == 0
|
765
|
+
emit "strb #{temporary}, [#{base_reg}]\n"
|
766
|
+
else
|
767
|
+
emit "strb #{temporary}, [#{base_reg}, \##{offset}]\n"
|
768
|
+
end
|
769
769
|
end
|
770
770
|
else
|
771
771
|
eval_binop [:add, base, offset], :a4
|
772
|
-
|
773
|
-
|
772
|
+
with_temporary do |temporary|
|
773
|
+
load_value_into_register value, temporary
|
774
|
+
emit "strb #{temporary}, [a4]\n"
|
775
|
+
end
|
774
776
|
end
|
775
777
|
end
|
776
778
|
|
777
779
|
# Set the word at _base_ + _offset_ * +@WORDSIZE+ to _value_
|
778
780
|
def set_word base, offset, value
|
779
|
-
emit "# set-word #{base} #{offset} #{value}\n"
|
780
781
|
# If base is an integer, but offset isn't, swap them
|
781
782
|
if !integer?(offset) && integer?(base)
|
782
783
|
base, offset = [offset, base]
|
@@ -784,18 +785,22 @@ module Voodoo
|
|
784
785
|
|
785
786
|
if integer? offset
|
786
787
|
base_reg = load_value base, :a4
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
788
|
+
with_temporary do |temporary|
|
789
|
+
load_value_into_register value, temporary
|
790
|
+
if offset == 0
|
791
|
+
emit "str #{temporary}, [#{base_reg}]\n"
|
792
|
+
else
|
793
|
+
emit "str #{temporary}, [#{base_reg}, \##{offset * @WORDSIZE}]\n"
|
794
|
+
end
|
792
795
|
end
|
793
796
|
else
|
794
797
|
load_value_into_register base, :a4
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
798
|
+
with_temporary do |temporary|
|
799
|
+
load_value_into_register offset, temporary
|
800
|
+
emit "add a4, a4, #{temporary}, LSL #2\n"
|
801
|
+
load_value_into_register value, temporary
|
802
|
+
emit "str #{temporary}, [a4]\n"
|
803
|
+
end
|
799
804
|
end
|
800
805
|
end
|
801
806
|
|
@@ -814,21 +819,8 @@ module Voodoo
|
|
814
819
|
emit ".ascii \"#{code}\"\n"
|
815
820
|
end
|
816
821
|
|
817
|
-
# Test if a value is a symbol
|
818
|
-
def symbol? value
|
819
|
-
value.kind_of? Symbol
|
820
|
-
end
|
821
|
-
|
822
|
-
# Test if op is a symmetric binary operation (i.e. it will yield the
|
823
|
-
# same result if the order of its source operands is changed).
|
824
|
-
def symmetric_binop? op
|
825
|
-
[:add, :and, :mul, :or, :xor].member? op
|
826
|
-
end
|
827
|
-
|
828
822
|
# Call a function, re-using the current call frame if possible.
|
829
823
|
def tail_call func, *args
|
830
|
-
emit "# tail-call #{func} #{args.join ' '}\n"
|
831
|
-
|
832
824
|
# Compute number of stack arguments
|
833
825
|
nstackargs = number_of_stack_arguments args.length
|
834
826
|
# If we need more stack arguments than we have now,
|
@@ -883,41 +875,82 @@ module Voodoo
|
|
883
875
|
saved = overwritten[sym]
|
884
876
|
if saved
|
885
877
|
# Saved value, load from stack
|
886
|
-
|
887
|
-
|
878
|
+
with_temporary do |temporary|
|
879
|
+
emit "ldr #{temporary}, [sp, \##{saved}]\n"
|
880
|
+
emit "str #{temporary}, #{arg_reference i}\n"
|
881
|
+
end
|
888
882
|
else
|
889
883
|
# Regular value, use load_value
|
890
|
-
|
884
|
+
with_temporary do |temporary|
|
885
|
+
reg = load_value arg, temporary
|
886
|
+
emit "str #{reg}, #{arg_reference i}\n"
|
887
|
+
end
|
891
888
|
end
|
892
|
-
emit "str #{reg}, #{arg_reference i}\n"
|
893
889
|
end
|
894
890
|
end
|
895
891
|
|
896
|
-
|
897
|
-
|
892
|
+
with_temporary do |temporary|
|
893
|
+
# Load address of function to be called
|
894
|
+
load_value_into_register func, temporary
|
898
895
|
|
899
|
-
|
900
|
-
|
901
|
-
|
896
|
+
# Destroy current activation frame and enter func
|
897
|
+
destroy_frame false
|
898
|
+
emit "bx #{temporary}\n"
|
899
|
+
end
|
902
900
|
emit_constants
|
903
901
|
end
|
904
902
|
|
903
|
+
# Returns a reference to a value.
|
904
|
+
# For immediate values that fit in 8 bits, this returns the
|
905
|
+
# value itself (in ARM syntax).
|
906
|
+
# For all other values, loads the value into a register and
|
907
|
+
# returns the name of the register. If the value is already
|
908
|
+
# in a register, the name of that register is returned. Else,
|
909
|
+
# the value is loaded into the register specified as the
|
910
|
+
# second argument.
|
911
|
+
def value_ref x, register
|
912
|
+
if substitution? x
|
913
|
+
x = substitute_number x[1]
|
914
|
+
end
|
915
|
+
|
916
|
+
if integer? x
|
917
|
+
if x >= 0 && x <= 255
|
918
|
+
return "\##{x}"
|
919
|
+
elsif x >= -255 && x < 0
|
920
|
+
emit "mvn #{register}, \##{-(x + 1)}\n"
|
921
|
+
return register
|
922
|
+
else
|
923
|
+
lbl = add_constant x
|
924
|
+
emit "ldr #{register}, #{lbl}\n"
|
925
|
+
return register
|
926
|
+
end
|
927
|
+
elsif symbol? x
|
928
|
+
binding = @environment[x]
|
929
|
+
if binding.kind_of? Symbol
|
930
|
+
# Value is already in a register. Return register name.
|
931
|
+
return binding
|
932
|
+
elsif binding.kind_of? Integer
|
933
|
+
# Value is on the stack. Load from the stack.
|
934
|
+
emit "ldr #{register}, #{offset_reference binding}\n"
|
935
|
+
return register
|
936
|
+
else
|
937
|
+
# Assume global
|
938
|
+
lbl = add_constant x
|
939
|
+
emit "ldr #{register}, #{lbl}\n"
|
940
|
+
return register
|
941
|
+
end
|
942
|
+
elsif at_expr? x
|
943
|
+
load_at x[1], register
|
944
|
+
else
|
945
|
+
raise "Don't know how to load #{x.inspect}"
|
946
|
+
end
|
947
|
+
end
|
948
|
+
|
905
949
|
# Define a word with the given value
|
906
950
|
def word value
|
907
951
|
emit ".int #{value}\n"
|
908
952
|
end
|
909
953
|
|
910
|
-
# Write generated code to the given IO object.
|
911
|
-
def write io
|
912
|
-
@sections.each do |section,code|
|
913
|
-
unless code.empty?
|
914
|
-
io.puts ".section #{section.to_s}"
|
915
|
-
io.puts code
|
916
|
-
io.puts
|
917
|
-
end
|
918
|
-
end
|
919
|
-
end
|
920
|
-
|
921
954
|
end
|
922
955
|
|
923
956
|
# Register class for little endian ARM
|