voodoo 0.7.0 → 1.0.0
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 +71 -16
- data/lib/voodoo/compiler.rb +41 -13
- data/lib/voodoo/config.rb +5 -0
- data/lib/voodoo/generators/amd64_nasm_generator.rb +4 -0
- data/lib/voodoo/generators/arm_elf_generator.rb +33 -0
- data/lib/voodoo/generators/arm_gas_generator.rb +927 -0
- data/lib/voodoo/generators/common_code_generator.rb +19 -6
- data/lib/voodoo/generators/gas_elf_generator.rb +2 -2
- data/lib/voodoo/generators/i386_nasm_generator.rb +6 -1
- data/lib/voodoo/generators/mips_gas_generator.rb +14 -4
- data/lib/voodoo/generators/nasm_elf_generator.rb +1 -1
- data/lib/voodoo/generators/nasm_generator.rb +6 -10
- data/lib/voodoo/parser.rb +307 -128
- data/lib/voodoo/validator.rb +391 -0
- metadata +13 -11
- data/lib/voodoo/generators/generator_api1.rb +0 -95
@@ -0,0 +1,927 @@
|
|
1
|
+
require 'voodoo/generators/common_code_generator'
|
2
|
+
|
3
|
+
module Voodoo
|
4
|
+
# = ARM GNU Assembler Code Generator
|
5
|
+
#
|
6
|
+
# The ARM code generator generates assembly code for use with
|
7
|
+
# the GNU assembler.
|
8
|
+
#
|
9
|
+
# == Calling Convention
|
10
|
+
#
|
11
|
+
# The first four arguments are passed in the registers r0 through r3.
|
12
|
+
# Any additional arguments are passed on the stack, starting at
|
13
|
+
# r13. r13 will always be a multiple of 8.
|
14
|
+
#
|
15
|
+
# The return address for the called function is passed in r14.
|
16
|
+
#
|
17
|
+
# The called function will store its return value in r0.
|
18
|
+
#
|
19
|
+
# The called function is required to preserve the values of registers
|
20
|
+
# r4 through r11 and register r13.
|
21
|
+
#
|
22
|
+
# This calling convention is compatible with the Procedure Call
|
23
|
+
# Standard for the ARM Architecture (AAPCS).
|
24
|
+
#
|
25
|
+
# == Call Frames
|
26
|
+
#
|
27
|
+
# Call frames have the following layout:
|
28
|
+
#
|
29
|
+
# When a function is called, it receives a stack frame that looks like
|
30
|
+
# the following:
|
31
|
+
#
|
32
|
+
# :
|
33
|
+
# old frame
|
34
|
+
# padding
|
35
|
+
# argn
|
36
|
+
# :
|
37
|
+
# arg4 <-- r13 points here
|
38
|
+
#
|
39
|
+
# The function prologue of functions generated by this code generator
|
40
|
+
# creates activation frames that look as follows:
|
41
|
+
#
|
42
|
+
# :
|
43
|
+
# old frame
|
44
|
+
# padding
|
45
|
+
# argn
|
46
|
+
# :
|
47
|
+
# arg4
|
48
|
+
# saved r14
|
49
|
+
# saved r11
|
50
|
+
# :
|
51
|
+
# saved r4 <-- r13 points here
|
52
|
+
#
|
53
|
+
# == Register Usage
|
54
|
+
#
|
55
|
+
# Inside a function, registers r4..r8, r10, and r11 are used for
|
56
|
+
# local variables and function arguments.
|
57
|
+
#
|
58
|
+
# r12 is used as a temporary, and r3 is used when another temporary
|
59
|
+
# is needed.
|
60
|
+
#
|
61
|
+
class ARMGasGenerator < CommonCodeGenerator
|
62
|
+
def initialize params
|
63
|
+
@WORDSIZE = 4
|
64
|
+
@CODE_ALIGNMENT = 4
|
65
|
+
@DATA_ALIGNMENT = @WORDSIZE
|
66
|
+
@FUNCTION_ALIGNMENT = @WORDSIZE
|
67
|
+
|
68
|
+
@NREGISTER_ARGS = 4
|
69
|
+
@NREGISTER_LOCALS = 7
|
70
|
+
@RETURN = :r0
|
71
|
+
@TEMPORARY = :r12
|
72
|
+
@constants = []
|
73
|
+
@frame_offset = 0
|
74
|
+
@frame_size = 0
|
75
|
+
@function_end_label = nil
|
76
|
+
@imports = {}
|
77
|
+
@if_labels = []
|
78
|
+
@saved_registers = []
|
79
|
+
super params
|
80
|
+
@output_file_suffix = '.s'
|
81
|
+
@features.merge! \
|
82
|
+
:'bits-per-word' => '32',
|
83
|
+
:'byte-order' => 'little-endian',
|
84
|
+
:'bytes-per-word' => 4
|
85
|
+
end
|
86
|
+
|
87
|
+
# Create an entry in the constants table,
|
88
|
+
# returning the label that will refer to the constant.
|
89
|
+
# The value may be an integer or a label.
|
90
|
+
def add_constant value
|
91
|
+
label = gensym
|
92
|
+
@constants << [label, value]
|
93
|
+
label
|
94
|
+
end
|
95
|
+
|
96
|
+
def align alignment = nil
|
97
|
+
unless alignment
|
98
|
+
# Get default alignment
|
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
|
112
|
+
end
|
113
|
+
|
114
|
+
# Returns an sp-relative reference for the nth (0-based) argument.
|
115
|
+
def arg_reference n
|
116
|
+
"[sp, \##{@frame_size + (n - @NREGISTER_ARGS) * @WORDSIZE}]"
|
117
|
+
end
|
118
|
+
|
119
|
+
# Return the register in which the nth (0-based) argument is stored, or
|
120
|
+
# nil if not stored in a register
|
121
|
+
def arg_register n
|
122
|
+
# The first @NREGISTER_ARGS arguments are in the v registers,
|
123
|
+
# the rest are on the stack.
|
124
|
+
if register_arg? n
|
125
|
+
"v#{n + 1}"
|
126
|
+
else
|
127
|
+
nil
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Test if op is a binary operation
|
132
|
+
def assymetric_binop? op
|
133
|
+
[:asr, :bsr, :div, :mod, :rol, :ror, :shl, :shr, :sub].member?(op)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Test if a value is an at-expression
|
137
|
+
def at_expr? value
|
138
|
+
value.respond_to?(:[]) && value[0] == :'@'
|
139
|
+
end
|
140
|
+
|
141
|
+
# Begins a new block.
|
142
|
+
def begin_block *code
|
143
|
+
emit "# begin block\n"
|
144
|
+
# If we are starting a block at top level, create a frame
|
145
|
+
if @environment == @top_level
|
146
|
+
nlocals = count_locals code
|
147
|
+
create_frame nlocals, false
|
148
|
+
end
|
149
|
+
@environment = Environment.new @environment
|
150
|
+
end
|
151
|
+
|
152
|
+
# Emit function prologue and declare _formals_ as function arguments
|
153
|
+
def begin_function formals, nlocals
|
154
|
+
if @environment != @top_level
|
155
|
+
raise "Can only begin a function at top level"
|
156
|
+
end
|
157
|
+
|
158
|
+
@function_end_label = gensym
|
159
|
+
emit "# function #{formals.join ' '}\n"
|
160
|
+
environment = Environment.new @environment
|
161
|
+
environment.add_args formals
|
162
|
+
@environment = environment
|
163
|
+
emit_function_prologue formals, nlocals
|
164
|
+
end
|
165
|
+
|
166
|
+
# Test if op is a binary operation
|
167
|
+
def binop? op
|
168
|
+
assymetric_binop?(op) || symmetric_binop?(op)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Define a byte with the given value
|
172
|
+
def byte value
|
173
|
+
emit ".byte #{value}\n"
|
174
|
+
end
|
175
|
+
|
176
|
+
# Call a function.
|
177
|
+
def call func, *args
|
178
|
+
emit "# call #{func} #{args.join ' '}\n"
|
179
|
+
|
180
|
+
# Calculate how many arguments need to be pushed on
|
181
|
+
# the stack, and allocate space for them.
|
182
|
+
nstack_args = number_of_stack_arguments args.length
|
183
|
+
old_frame_offset = @frame_offset
|
184
|
+
old_frame_size = @frame_size
|
185
|
+
grow_frame nstack_args if nstack_args > 0
|
186
|
+
|
187
|
+
# Put stack arguments on the stack
|
188
|
+
(@NREGISTER_ARGS...args.length).each do |n|
|
189
|
+
load_value_into_register args[n], @TEMPORARY
|
190
|
+
emit "str #{@TEMPORARY}, " +
|
191
|
+
"[sp , \##{(n - @NREGISTER_ARGS) * @WORDSIZE}]\n"
|
192
|
+
end
|
193
|
+
|
194
|
+
# Put register arguments in the right registers
|
195
|
+
nregister_args = number_of_register_arguments args.length
|
196
|
+
nregister_args.times do |n|
|
197
|
+
load_value_into_register args[n], :"a#{n + 1}"
|
198
|
+
end
|
199
|
+
|
200
|
+
# Call function
|
201
|
+
if global? func
|
202
|
+
emit "bl #{func}\n"
|
203
|
+
else
|
204
|
+
func_reg = load_value func
|
205
|
+
emit "blx #{func_reg}\n"
|
206
|
+
end
|
207
|
+
|
208
|
+
# Restore original stack frame
|
209
|
+
if old_frame_size != @frame_size
|
210
|
+
emit "add sp, sp, \##{@frame_size - old_frame_size}\n"
|
211
|
+
@frame_offset = old_frame_offset
|
212
|
+
@frame_size = old_frame_size
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# Creates a stack frame for the given number of arguments
|
217
|
+
# and local variables.
|
218
|
+
def create_frame nvars, save_lr = true
|
219
|
+
# Calculate how many variables we will store in registers,
|
220
|
+
# and how many on the stack.
|
221
|
+
nregister_vars = [nvars, @NREGISTER_LOCALS].min
|
222
|
+
nstack_vars = nvars - nregister_vars
|
223
|
+
|
224
|
+
# Save the registers we will clobber to the stack.
|
225
|
+
clobbered = []
|
226
|
+
nregister_vars.times do |i|
|
227
|
+
clobbered << :"v#{i < 5 ? i + 1 : i + 2}"
|
228
|
+
end
|
229
|
+
clobbered << :lr if save_lr
|
230
|
+
@saved_registers = clobbered
|
231
|
+
emit "stmfd sp!, {#{clobbered.join ', '}}\n"
|
232
|
+
|
233
|
+
# Calculate frame size so that the stack pointer will
|
234
|
+
# 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
|
239
|
+
extra_space = @frame_size - clobbered.length * @WORDSIZE
|
240
|
+
if extra_space > 0
|
241
|
+
emit "sub sp, sp, \##{extra_space}\n"
|
242
|
+
end
|
243
|
+
@frame_offset = 0
|
244
|
+
end
|
245
|
+
|
246
|
+
# Start a conditional using the specified branch instruction
|
247
|
+
# after the comparison.
|
248
|
+
def common_if comp, x, y = nil
|
249
|
+
emit "# #{comp} #{x} #{y}\n"
|
250
|
+
|
251
|
+
xreg = load_value x, @TEMPORARY
|
252
|
+
yreg = load_value y, :a4
|
253
|
+
|
254
|
+
falselabel = @environment.gensym
|
255
|
+
@if_labels.push falselabel
|
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
|
263
|
+
|
264
|
+
# Counts the number of local variables created in
|
265
|
+
# a sequence of statements.
|
266
|
+
def count_locals statements
|
267
|
+
count = 0
|
268
|
+
each_statement(statements) do |statement|
|
269
|
+
if statement[0] == :let
|
270
|
+
# let introduces a single local
|
271
|
+
count = count + 1
|
272
|
+
end
|
273
|
+
end
|
274
|
+
count
|
275
|
+
end
|
276
|
+
|
277
|
+
# Destroys the current stack frame.
|
278
|
+
# If ret is true, loads the saved value of lr into pc.
|
279
|
+
def destroy_frame ret = false
|
280
|
+
# Set sp back to where saved registers were stored
|
281
|
+
saved = @saved_registers
|
282
|
+
offset = @frame_size - saved.length * @WORDSIZE
|
283
|
+
if offset != 0
|
284
|
+
emit "add sp, sp, \##{offset}\n"
|
285
|
+
end
|
286
|
+
|
287
|
+
if ret
|
288
|
+
index = saved.index :lr
|
289
|
+
if index
|
290
|
+
saved[index] = :pc
|
291
|
+
else
|
292
|
+
raise "Request to load saved lr into pc, but lr has not been saved"
|
293
|
+
end
|
294
|
+
end
|
295
|
+
emit "ldmfd sp!, {#{saved.join ', '}}\n"
|
296
|
+
|
297
|
+
emit_constants if ret
|
298
|
+
end
|
299
|
+
|
300
|
+
# Writes any constants that need to be written to the instruction
|
301
|
+
# stream, and clears the list of constants that need to be written.
|
302
|
+
def emit_constants
|
303
|
+
@constants.each do |x|
|
304
|
+
label x[0]
|
305
|
+
word x[1]
|
306
|
+
end
|
307
|
+
@constants = []
|
308
|
+
end
|
309
|
+
|
310
|
+
# Emit function prologue.
|
311
|
+
def emit_function_prologue formals = [], nlocals = 0
|
312
|
+
# Calculate the number of arguments we were passed in
|
313
|
+
# registers, the total number of values we need to save
|
314
|
+
# on the stack, then create a stack frame and save
|
315
|
+
# the v registers we will be using.
|
316
|
+
nregister_args = [formals.length, @NREGISTER_ARGS].min
|
317
|
+
nvars = nregister_args + nlocals
|
318
|
+
create_frame nvars, true
|
319
|
+
|
320
|
+
# Move arguments that were passed in registers into
|
321
|
+
# callee-save registers.
|
322
|
+
nregister_args.times do |i|
|
323
|
+
emit "cpy v#{i + 1}, a#{i + 1}\n"
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
# Ends the current block.
|
328
|
+
def end_block
|
329
|
+
emit "# end block\n"
|
330
|
+
|
331
|
+
# If we are returning to top level, restore stack pointer
|
332
|
+
# and saved registers.
|
333
|
+
if @environment.parent == @top_level
|
334
|
+
offset = @frame_size - @saved_registers.length * @WORDSIZE
|
335
|
+
if offset > 0
|
336
|
+
emit "add sp, sp, \##{offset}\n"
|
337
|
+
end
|
338
|
+
emit "ldmfd sp!, {#{@saved_registers.join ', '}}\n"
|
339
|
+
@frame_size = 0
|
340
|
+
@frame_offset = 0
|
341
|
+
@saved_registers = []
|
342
|
+
|
343
|
+
# If we need to emit constants, do so now
|
344
|
+
unless @constants.empty?
|
345
|
+
lbl = gensym
|
346
|
+
goto lbl
|
347
|
+
label lbl
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
# Restore old value of @environment
|
352
|
+
@environment = @environment.parent
|
353
|
+
end
|
354
|
+
|
355
|
+
# Ends a function body.
|
356
|
+
def end_function
|
357
|
+
if @environment == @top_level
|
358
|
+
raise "Cannot end function when not in a function"
|
359
|
+
end
|
360
|
+
|
361
|
+
emit "# function epilogue\n"
|
362
|
+
label @function_end_label
|
363
|
+
|
364
|
+
destroy_frame true
|
365
|
+
@frame_size = 0
|
366
|
+
@frame_offset = 0
|
367
|
+
@saved_registers = []
|
368
|
+
emit "# end function\n\n"
|
369
|
+
@environment = @top_level
|
370
|
+
end
|
371
|
+
|
372
|
+
# Ends a conditional.
|
373
|
+
def end_if
|
374
|
+
label @if_labels.pop
|
375
|
+
end
|
376
|
+
|
377
|
+
# Evaluate the binary operation expr and store the result in register
|
378
|
+
def eval_binop expr, register
|
379
|
+
op = expr[0]
|
380
|
+
|
381
|
+
# Emulation for div and mod, which ARM does not have instructions for
|
382
|
+
case op
|
383
|
+
when :div
|
384
|
+
func = :"__aeabi_idiv"
|
385
|
+
import func unless @imports.has_key? func
|
386
|
+
call func, expr[1], expr[2]
|
387
|
+
emit "cpy #{register}, r0\n" if register != :r0
|
388
|
+
return
|
389
|
+
when :mod
|
390
|
+
func = :"__aeabi_idivmod"
|
391
|
+
import func unless @imports.has_key? func
|
392
|
+
call func, expr[1], expr[2]
|
393
|
+
emit "cpy #{register}, r1\n" if register != :r1
|
394
|
+
return
|
395
|
+
end
|
396
|
+
|
397
|
+
x = load_value expr[1], :a4
|
398
|
+
y = load_value expr[2], @TEMPORARY
|
399
|
+
|
400
|
+
case op
|
401
|
+
when :bsr
|
402
|
+
emit "lsr #{register}, #{x}, #{y}\n"
|
403
|
+
when :or
|
404
|
+
emit "orr #{register}, #{x}, #{y}\n"
|
405
|
+
when :rol
|
406
|
+
emit "rsb #{y}, #{y}, #32\n"
|
407
|
+
emit "ror #{register}, #{x}, #{y}\n"
|
408
|
+
when :shl
|
409
|
+
emit "lsl #{register}, #{x}, #{y}\n"
|
410
|
+
when :shr
|
411
|
+
emit "lsr #{register}, #{x}, #{y}\n"
|
412
|
+
when :xor
|
413
|
+
emit "eor #{register}, #{x}, #{y}\n"
|
414
|
+
else
|
415
|
+
emit "#{expr[0]} #{register}, #{x}, #{y}\n"
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
# Evaluates the expression +expr+ and stores the result in +register+.
|
420
|
+
def eval_expr expr, register
|
421
|
+
if expr.length == 1
|
422
|
+
# Load value
|
423
|
+
load_value_into_register expr[0], register
|
424
|
+
else
|
425
|
+
# Evaluate expression
|
426
|
+
op = expr[0]
|
427
|
+
case op
|
428
|
+
when :call
|
429
|
+
call *expr[1..-1]
|
430
|
+
emit "cpy #{register}, #{@RETURN}\n" if register != @RETURN
|
431
|
+
when :'get-byte'
|
432
|
+
get_byte expr[1], expr[2], register
|
433
|
+
when :'get-word'
|
434
|
+
get_word expr[1], expr[2], register
|
435
|
+
when :not
|
436
|
+
load_value_into_register expr[1], register
|
437
|
+
emit "mvn #{@TEMPORARY}, #0\n"
|
438
|
+
emit "eor #{register}, #{register}, #{@TEMPORARY}\n"
|
439
|
+
else
|
440
|
+
if binop? op
|
441
|
+
eval_binop expr, register
|
442
|
+
else
|
443
|
+
raise "Not a magic word: #{op}"
|
444
|
+
end
|
445
|
+
end
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
# Export symbols from the current section
|
450
|
+
def export *symbols
|
451
|
+
symbols.each { |sym| emit ".globl #{sym}\n" }
|
452
|
+
end
|
453
|
+
|
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
|
+
# Load byte from _base_ + _offset_ into _register_
|
463
|
+
def get_byte base, offset, register
|
464
|
+
# If base is an integer, but offset isn't, swap them
|
465
|
+
if !integer?(offset) && integer?(base)
|
466
|
+
base, offset = [offset, base]
|
467
|
+
end
|
468
|
+
|
469
|
+
if integer? offset
|
470
|
+
base_reg = load_value base
|
471
|
+
if offset == 0
|
472
|
+
emit "ldrb #{register}, [#{base_reg}]\n"
|
473
|
+
else
|
474
|
+
emit "ldrb #{register}, [#{base_reg}, \##{offset}]\n"
|
475
|
+
end
|
476
|
+
else
|
477
|
+
base_reg = load_value base
|
478
|
+
offset_reg = load_value offset, :a4
|
479
|
+
emit "ldrb #{register}, [#{base_reg}, #{offset_reg}]\n"
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
# Load word from _base_ + _offset_ * _@WORDSIZE_ into _register_
|
484
|
+
def get_word base, offset, register
|
485
|
+
if integer? offset
|
486
|
+
base_reg = load_value base
|
487
|
+
if offset == 0
|
488
|
+
emit "ldr #{register}, [#{base_reg}]\n"
|
489
|
+
else
|
490
|
+
emit "ldr #{register}, [#{base_reg}, \##{offset * @WORDSIZE}]\n"
|
491
|
+
end
|
492
|
+
else
|
493
|
+
base_reg = load_value base
|
494
|
+
offset_reg = load_value offset, :a4
|
495
|
+
emit "ldr #{register}, [#{base_reg}, #{offset_reg}, LSL #2]\n"
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
# Test if a symbol refers to a global
|
500
|
+
def global? symbol
|
501
|
+
symbol?(symbol) && @environment[symbol] == nil
|
502
|
+
end
|
503
|
+
|
504
|
+
# Jump to a label.
|
505
|
+
def goto label
|
506
|
+
emit "b #{label}\n"
|
507
|
+
|
508
|
+
# If we have constants that need to be emitted, do so now
|
509
|
+
emit_constants
|
510
|
+
end
|
511
|
+
|
512
|
+
# Grows the current frame by n words, plus padding to
|
513
|
+
# respect alignment rules.
|
514
|
+
def grow_frame nwords
|
515
|
+
increment = (nwords * @WORDSIZE + 7) / 8 * 8
|
516
|
+
emit "sub sp, sp, \##{increment}\n"
|
517
|
+
@frame_size = @frame_size + increment
|
518
|
+
@frame_offset = @frame_offset + increment
|
519
|
+
end
|
520
|
+
|
521
|
+
# Start the false path of a conditional.
|
522
|
+
def ifelse
|
523
|
+
emit "# else\n"
|
524
|
+
newlabel = @environment.gensym
|
525
|
+
goto newlabel
|
526
|
+
lbl = @if_labels.pop
|
527
|
+
label lbl
|
528
|
+
@if_labels.push newlabel
|
529
|
+
end
|
530
|
+
|
531
|
+
# Test if x is equal to y
|
532
|
+
def ifeq x, y
|
533
|
+
common_if :ifeq, x, y
|
534
|
+
end
|
535
|
+
|
536
|
+
# Test if x is greater than or equal to y
|
537
|
+
def ifge x, y
|
538
|
+
common_if :ifge, x, y
|
539
|
+
end
|
540
|
+
|
541
|
+
# Test if x is strictly greater than y
|
542
|
+
def ifgt x, y
|
543
|
+
common_if :ifgt, x, y
|
544
|
+
end
|
545
|
+
|
546
|
+
# Test if x is less than or equal to y
|
547
|
+
def ifle x, y
|
548
|
+
common_if :ifle, x, y
|
549
|
+
end
|
550
|
+
|
551
|
+
# Test if x is strictly less than y
|
552
|
+
def iflt x, y
|
553
|
+
common_if :iflt, x, y
|
554
|
+
end
|
555
|
+
|
556
|
+
# Test if x different from y
|
557
|
+
def ifne x, y
|
558
|
+
common_if :ifne, x, y
|
559
|
+
end
|
560
|
+
|
561
|
+
# Import labels into the current section
|
562
|
+
def import *symbols
|
563
|
+
# Record imported labels in @imports
|
564
|
+
symbols.each { |sym| @imports[sym] = sym }
|
565
|
+
end
|
566
|
+
|
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
|
+
# Introduce a new local variable
|
578
|
+
def let symbol, *expr
|
579
|
+
emit "# let #{symbol} #{expr.join ' '}\n"
|
580
|
+
n = @environment.locals
|
581
|
+
@environment.add_local symbol
|
582
|
+
|
583
|
+
register = local_register n
|
584
|
+
if register
|
585
|
+
# We will use a register to store the value
|
586
|
+
eval_expr expr, register
|
587
|
+
else
|
588
|
+
# We will use the stack to store the value
|
589
|
+
ref = local_reference n
|
590
|
+
eval_expr expr, @TEMPORARY
|
591
|
+
emit "str #{@TEMPORARY}, #{ref}\n"
|
592
|
+
end
|
593
|
+
end
|
594
|
+
|
595
|
+
# Load the value at the given address.
|
596
|
+
def load_at address, register = @TEMPORARY
|
597
|
+
load_value_into_register address, register
|
598
|
+
emit "ldr #{register}, [#{register}]\n"
|
599
|
+
register
|
600
|
+
end
|
601
|
+
|
602
|
+
# Load a value into a register.
|
603
|
+
# Returns the name of the register.
|
604
|
+
# If the value was already in a register, the name of that
|
605
|
+
# register is returned.
|
606
|
+
# Else, the value is loaded into a register and the name of
|
607
|
+
# that register is returned. The register to use in that case
|
608
|
+
# may be specified using the optional second argument.
|
609
|
+
def load_value x, register = @TEMPORARY
|
610
|
+
if integer? x
|
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
|
653
|
+
else
|
654
|
+
raise "Don't know how to load #{x.inspect}"
|
655
|
+
end
|
656
|
+
end
|
657
|
+
|
658
|
+
# Load a value into a specific register
|
659
|
+
def load_value_into_register x, register
|
660
|
+
reg = load_value x, register
|
661
|
+
if reg != register
|
662
|
+
emit "cpy #{register}, #{reg}\n"
|
663
|
+
end
|
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}"
|
678
|
+
else
|
679
|
+
"v#{n + 2}"
|
680
|
+
end
|
681
|
+
else
|
682
|
+
nil
|
683
|
+
end
|
684
|
+
end
|
685
|
+
|
686
|
+
# Calculate the number of register arguments,
|
687
|
+
# given the total number of arguments.
|
688
|
+
def number_of_register_arguments n = @environment.args
|
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
|
696
|
+
end
|
697
|
+
|
698
|
+
# Returns true if the nth (0-based) argument is stored in a register
|
699
|
+
def register_arg? n
|
700
|
+
n < @NREGISTER_ARGS
|
701
|
+
end
|
702
|
+
|
703
|
+
# Returns true if the nth (0-based) local is stored in a register
|
704
|
+
def register_local? n
|
705
|
+
(n + number_of_register_arguments) < @NREGISTER_LOCALS
|
706
|
+
end
|
707
|
+
|
708
|
+
# Returns from a function.
|
709
|
+
#
|
710
|
+
# _words_ may contain an expression to be evaluated. The result
|
711
|
+
# of the evaluation is returned from the function.
|
712
|
+
def ret *words
|
713
|
+
emit "# return #{words.join ' '}\n"
|
714
|
+
# Compute return value and store it in @RETURN
|
715
|
+
eval_expr(words, @RETURN) unless words.empty?
|
716
|
+
# Go to epilogue
|
717
|
+
goto @function_end_label
|
718
|
+
end
|
719
|
+
|
720
|
+
# Set a variable to the result of evaluating an expression
|
721
|
+
def set symbol, *expr
|
722
|
+
emit "# set #{symbol} #{expr.join ' '}\n"
|
723
|
+
|
724
|
+
x = @environment[symbol]
|
725
|
+
if x == nil
|
726
|
+
raise "Cannot change value of constant #{symbol}"
|
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"
|
748
|
+
end
|
749
|
+
eval_expr expr, @TEMPORARY
|
750
|
+
emit "str #{@TEMPORARY}, #{ref}\n"
|
751
|
+
end
|
752
|
+
end
|
753
|
+
|
754
|
+
# Set the byte at _base_ + _offset_ to _value_
|
755
|
+
def set_byte base, offset, value
|
756
|
+
emit "# set-byte #{base} #{offset} #{value}\n"
|
757
|
+
# If base is an integer, but offset isn't, swap them
|
758
|
+
if !integer?(offset) && integer?(base)
|
759
|
+
base, offset = [offset, base]
|
760
|
+
end
|
761
|
+
|
762
|
+
if integer? offset
|
763
|
+
base_reg = load_value base, :a4
|
764
|
+
load_value_into_register value, @TEMPORARY
|
765
|
+
if offset == 0
|
766
|
+
emit "strb #{@TEMPORARY}, [#{base_reg}]\n"
|
767
|
+
else
|
768
|
+
emit "strb #{@TEMPORARY}, [#{base_reg}, \##{offset}]\n"
|
769
|
+
end
|
770
|
+
else
|
771
|
+
eval_binop [:add, base, offset], :a4
|
772
|
+
load_value_into_register value, @TEMPORARY
|
773
|
+
emit "strb #{@TEMPORARY}, [a4]\n"
|
774
|
+
end
|
775
|
+
end
|
776
|
+
|
777
|
+
# Set the word at _base_ + _offset_ * +@WORDSIZE+ to _value_
|
778
|
+
def set_word base, offset, value
|
779
|
+
emit "# set-word #{base} #{offset} #{value}\n"
|
780
|
+
# If base is an integer, but offset isn't, swap them
|
781
|
+
if !integer?(offset) && integer?(base)
|
782
|
+
base, offset = [offset, base]
|
783
|
+
end
|
784
|
+
|
785
|
+
if integer? offset
|
786
|
+
base_reg = load_value base, :a4
|
787
|
+
load_value_into_register value, @TEMPORARY
|
788
|
+
if offset == 0
|
789
|
+
emit "str #{@TEMPORARY}, [#{base_reg}]\n"
|
790
|
+
else
|
791
|
+
emit "str #{@TEMPORARY}, [#{base_reg}, \##{offset * @WORDSIZE}]\n"
|
792
|
+
end
|
793
|
+
else
|
794
|
+
load_value_into_register base, :a4
|
795
|
+
load_value_into_register offset, @TEMPORARY
|
796
|
+
emit "add a4, a4, #{@TEMPORARY}, LSL #2\n"
|
797
|
+
load_value_into_register value, @TEMPORARY
|
798
|
+
emit "str #{@TEMPORARY}, [a4]\n"
|
799
|
+
end
|
800
|
+
end
|
801
|
+
|
802
|
+
# Define a string with the given value
|
803
|
+
def string value
|
804
|
+
code = ''
|
805
|
+
value.each_byte do |b|
|
806
|
+
if b == 92
|
807
|
+
code << "\\\\"
|
808
|
+
elsif b >= 32 && b < 127 && b != 34
|
809
|
+
code << b.chr
|
810
|
+
else
|
811
|
+
code << sprintf("\\%03o", b)
|
812
|
+
end
|
813
|
+
end
|
814
|
+
emit ".ascii \"#{code}\"\n"
|
815
|
+
end
|
816
|
+
|
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
|
+
# Call a function, re-using the current call frame if possible.
|
829
|
+
def tail_call func, *args
|
830
|
+
emit "# tail-call #{func} #{args.join ' '}\n"
|
831
|
+
|
832
|
+
# Compute number of stack arguments
|
833
|
+
nstackargs = number_of_stack_arguments args.length
|
834
|
+
# If we need more stack arguments than we have now,
|
835
|
+
# perform a normal call and return
|
836
|
+
if nstackargs > number_of_stack_arguments(@environment.args)
|
837
|
+
emit "# Not enough space for proper tail call; using regular call\n"
|
838
|
+
ret :call, func, *args
|
839
|
+
end
|
840
|
+
|
841
|
+
# We will assign arguments from left to right.
|
842
|
+
# Find places that we will overwrite before we read them,
|
843
|
+
# and store their values in some newly allocated stack space.
|
844
|
+
old_frame_offset = @frame_offset
|
845
|
+
old_frame_size = @frame_size
|
846
|
+
overwritten = {}
|
847
|
+
(@NREGISTER_ARGS...args.length).each do |i|
|
848
|
+
arg = args[i]
|
849
|
+
arg = arg[1] if at_expr? arg
|
850
|
+
if symbol?(arg)
|
851
|
+
binding = @environment[arg]
|
852
|
+
if binding[0] == :arg && binding[1] >= @NREGISTER_ARGS &&
|
853
|
+
binding[1] < i
|
854
|
+
# Argument i is a stack argument, but the value we will assign
|
855
|
+
# it is a stack argument that comes before it, so we will
|
856
|
+
# have overwritten it by the time we get to it.
|
857
|
+
overwritten[arg] = nil
|
858
|
+
end
|
859
|
+
end
|
860
|
+
end
|
861
|
+
|
862
|
+
unless overwritten.empty?
|
863
|
+
# Allocate space for arguments to be saved
|
864
|
+
grow_frame overwritten.length
|
865
|
+
# Save values
|
866
|
+
offset = 0
|
867
|
+
overwritten.each_key do |key|
|
868
|
+
reg = load_value key
|
869
|
+
emit "str #{reg}, [sp, \##{offset}]\n"
|
870
|
+
overwritten[key] = offset
|
871
|
+
offset = offset + @WORDSIZE
|
872
|
+
end
|
873
|
+
end
|
874
|
+
|
875
|
+
# Assign arguments
|
876
|
+
args.each_index do |i|
|
877
|
+
arg = args[i]
|
878
|
+
if register_arg? i
|
879
|
+
load_value_into_register arg, "a#{i + 1}"
|
880
|
+
else
|
881
|
+
# Test if this is a value we saved
|
882
|
+
sym = at_expr?(arg) ? arg[1] : arg
|
883
|
+
saved = overwritten[sym]
|
884
|
+
if saved
|
885
|
+
# Saved value, load from stack
|
886
|
+
reg = @TEMPORARY
|
887
|
+
emit "ldr #{reg}, [sp, \##{saved}]\n"
|
888
|
+
else
|
889
|
+
# Regular value, use load_value
|
890
|
+
reg = load_value arg
|
891
|
+
end
|
892
|
+
emit "str #{reg}, #{arg_reference i}\n"
|
893
|
+
end
|
894
|
+
end
|
895
|
+
|
896
|
+
# Load address of function to be called into @TEMPORARY
|
897
|
+
load_value_into_register func, @TEMPORARY
|
898
|
+
|
899
|
+
# Destroy current activation frame and enter func
|
900
|
+
destroy_frame false
|
901
|
+
emit "bx #{@TEMPORARY}\n"
|
902
|
+
emit_constants
|
903
|
+
end
|
904
|
+
|
905
|
+
# Define a word with the given value
|
906
|
+
def word value
|
907
|
+
emit ".int #{value}\n"
|
908
|
+
end
|
909
|
+
|
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
|
+
end
|
922
|
+
|
923
|
+
# Register class for little endian ARM
|
924
|
+
Voodoo::CodeGenerator.register_generator ARMGasGenerator,
|
925
|
+
:architecture => :arm,
|
926
|
+
:format => :gas
|
927
|
+
end
|