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,3 +1,5 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
1
3
|
module Voodoo
|
2
4
|
# Common base class for code generators.
|
3
5
|
#
|
@@ -11,7 +13,7 @@ module Voodoo
|
|
11
13
|
# - #has_feature?
|
12
14
|
# - #output_file_name
|
13
15
|
# - #output_file_suffix
|
14
|
-
# - #
|
16
|
+
# - #saved_frame_size
|
15
17
|
#
|
16
18
|
# This class contains base implementations of some of these methods,
|
17
19
|
# which can be used and/or overridden by subclasses.
|
@@ -20,11 +22,16 @@ module Voodoo
|
|
20
22
|
# is provided on the main page of the documentation of the Voodoo module.
|
21
23
|
#
|
22
24
|
class CommonCodeGenerator
|
25
|
+
# == Public Interface
|
26
|
+
#
|
27
|
+
# These methods implement the public interface of code generators.
|
28
|
+
|
23
29
|
# Initializes the code generator.
|
24
|
-
# _params_
|
25
|
-
#
|
26
|
-
#
|
27
|
-
# format,
|
30
|
+
# _params_ is a hash of key-value pairs, and can be used to pass additional
|
31
|
+
# parameters to the generator.
|
32
|
+
# Standard parameters are :architecture and :format, which indicate the
|
33
|
+
# architecture and the output format. If these are not supplied, default
|
34
|
+
# values will be used. Subclasses may define additional parameters.
|
28
35
|
def initialize params = {}
|
29
36
|
@architecture = params[:architecture] || Config.default_architecture
|
30
37
|
@format = params[:format] || Config.default_format
|
@@ -39,8 +46,10 @@ module Voodoo
|
|
39
46
|
@top_level = Environment.initial_environment
|
40
47
|
@environment = @top_level
|
41
48
|
@output_file_suffix = '.o'
|
49
|
+
@open_labels = [] # Labels for which we must emit size annotations
|
50
|
+
@relocated_symbols = Set.new
|
42
51
|
@features = {
|
43
|
-
:voodoo => "1.
|
52
|
+
:voodoo => "1.1" # Voodoo language version
|
44
53
|
}
|
45
54
|
end
|
46
55
|
|
@@ -51,35 +60,41 @@ module Voodoo
|
|
51
60
|
# add :data, [:align], [:label, :xyzzy], [:word, 42]
|
52
61
|
#
|
53
62
|
# This method implements the required functionality in terms
|
54
|
-
# of
|
63
|
+
# of a number of methods for individual incantations. These
|
64
|
+
# must be implemented by subclasses, although default implementations
|
65
|
+
# may be provided by CommonCodeGenerator. The following list contains
|
66
|
+
# the methods that the add method relies on. Methods that are provided
|
67
|
+
# by this class have been marked with a star. In general, these methods
|
68
|
+
# will require some functionality to be implemented by subclasses.
|
55
69
|
#
|
56
|
-
# - #align
|
57
|
-
# - #
|
58
|
-
# - #bsr
|
70
|
+
# - #align (*)
|
71
|
+
# - #block (*)
|
59
72
|
# - #byte
|
60
73
|
# - #call
|
74
|
+
# - #comment
|
75
|
+
# - #emit_label_size
|
61
76
|
# - #end_if
|
62
|
-
# - #export
|
63
|
-
# - #
|
64
|
-
# - #
|
65
|
-
# - #
|
77
|
+
# - #export (*)
|
78
|
+
# - #function (*)
|
79
|
+
# - #goto
|
80
|
+
# - #group
|
66
81
|
# - #ifeq
|
67
82
|
# - #ifge
|
68
83
|
# - #ifgt
|
69
84
|
# - #ifle
|
70
85
|
# - #iflt
|
71
86
|
# - #ifne
|
72
|
-
# - #import
|
73
|
-
# - #label
|
87
|
+
# - #import (*)
|
88
|
+
# - #label (*)
|
74
89
|
# - #let
|
90
|
+
# - #restore_frame (*)
|
91
|
+
# - #restore_locals (*)
|
75
92
|
# - #ret
|
76
|
-
# - #
|
77
|
-
# - #
|
93
|
+
# - #save_frame (*)
|
94
|
+
# - #save_locals (*)
|
78
95
|
# - #set
|
79
96
|
# - #set_byte
|
80
97
|
# - #set_word
|
81
|
-
# - #shl
|
82
|
-
# - #shr
|
83
98
|
# - #string
|
84
99
|
# - #word
|
85
100
|
#
|
@@ -89,35 +104,60 @@ module Voodoo
|
|
89
104
|
keyword, args = action[0], action[1..-1]
|
90
105
|
case keyword
|
91
106
|
when :block
|
107
|
+
emit_voodoo :block
|
92
108
|
block *args
|
109
|
+
emit_voodoo :end, :block
|
93
110
|
when :function
|
111
|
+
emit_voodoo :function, *args[0]
|
94
112
|
function args[0], *args[1..-1]
|
113
|
+
emit_voodoo :end, :function
|
114
|
+
when :group
|
115
|
+
emit_voodoo :group
|
116
|
+
old_open_labels = @open_labels
|
117
|
+
begin
|
118
|
+
@open_labels = []
|
119
|
+
args.each { |statement| add section, statement }
|
120
|
+
ensure
|
121
|
+
@open_labels = old_open_labels
|
122
|
+
end
|
123
|
+
emit_voodoo :end, :group
|
95
124
|
when :ifeq, :ifge, :ifgt, :ifle, :iflt, :ifne
|
125
|
+
emit_voodoo keyword, *args[0]
|
96
126
|
truebody = action[2]
|
97
127
|
falsebody = action[3]
|
98
128
|
send keyword, action[1][0], action[1][1]
|
99
129
|
add section, *truebody
|
100
130
|
if falsebody && !falsebody.empty?
|
131
|
+
emit_voodoo :else
|
101
132
|
ifelse
|
102
133
|
add section, *falsebody
|
103
134
|
end
|
135
|
+
emit_voodoo :end, :if
|
104
136
|
end_if
|
137
|
+
when :label, :string, :word
|
138
|
+
send *action
|
105
139
|
when :return
|
140
|
+
emit_voodoo *action
|
106
141
|
send :ret, *args
|
107
|
-
when :'set-word'
|
108
|
-
send :set_word, *args
|
109
|
-
when :'set-byte'
|
110
|
-
send :set_byte, *args
|
111
|
-
when :'tail-call'
|
112
|
-
send :tail_call, *args
|
113
142
|
else
|
114
|
-
|
143
|
+
emit_voodoo *action
|
144
|
+
underscored = keyword.to_s.gsub('-', '_').to_sym
|
145
|
+
send underscored, *args
|
146
|
+
end
|
147
|
+
|
148
|
+
# If we are on top-level and we have open labels and we just
|
149
|
+
# emitted something that isn't a label, emit size annotations
|
150
|
+
# for all open labels.
|
151
|
+
if !@open_labels.empty? && keyword != :label &&
|
152
|
+
@environment == @top_level
|
153
|
+
@open_labels.each { |name| emit_label_size name }
|
154
|
+
@open_labels.clear
|
115
155
|
end
|
116
156
|
end
|
117
157
|
end
|
118
158
|
end
|
119
159
|
|
120
|
-
#
|
160
|
+
# Adds a function.
|
121
161
|
#
|
122
162
|
# Parameters:
|
123
163
|
# [formals] an Array of formal parameter names
|
@@ -129,15 +169,106 @@ module Voodoo
|
|
129
169
|
add :functions, [:function, formals] + code
|
130
170
|
end
|
131
171
|
|
172
|
+
# Returns a hash describing the features supported by this code generator.
|
173
|
+
# The keys in this hash are the names of the supported features.
|
174
|
+
# The associated values are strings providing additional information about
|
175
|
+
# the feature, such as a version number.
|
176
|
+
def features
|
177
|
+
@features
|
178
|
+
end
|
179
|
+
|
180
|
+
# Returns a new, unused symbol.
|
181
|
+
def gensym
|
182
|
+
Environment.gensym
|
183
|
+
end
|
184
|
+
|
185
|
+
# Returns true if a feature is supported by this generator,
|
186
|
+
# false otherwise.
|
187
|
+
def has_feature? name
|
188
|
+
@features.has_key? name
|
189
|
+
end
|
190
|
+
|
191
|
+
# Given an input file name, returns the canonical output file name
|
192
|
+
# for this code generator.
|
193
|
+
def output_file_name input_name
|
194
|
+
input_name.sub(/\.voo$/, '') + @output_file_suffix
|
195
|
+
end
|
196
|
+
|
197
|
+
# Returns the canonical output file suffix for this code generator.
|
198
|
+
def output_file_suffix
|
199
|
+
@output_file_suffix
|
200
|
+
end
|
201
|
+
|
202
|
+
# == Helper Methods
|
203
|
+
#
|
204
|
+
# These methods are intended to be used by subclasses.
|
205
|
+
|
206
|
+
# Emits a directive to align the code or data following this
|
207
|
+
# statement. If _alignment_ is given, aligns on the next multiple
|
208
|
+
# of _alignment_ bytes. Else, uses the default alignment for the
|
209
|
+
# current section.
|
210
|
+
#
|
211
|
+
# This method requires the presence of a default_alignment method
|
212
|
+
# to calculate the default alignment for a given section, and an
|
213
|
+
# emit_align method to actually emit the target-specific code to
|
214
|
+
# align to a multiple of a given number of bytes. An implementation
|
215
|
+
# of default_alignment is provided in this class.
|
216
|
+
def align alignment = nil
|
217
|
+
alignment = default_alignment if alignment == nil
|
218
|
+
emit_align alignment unless alignment == 0
|
219
|
+
end
|
220
|
+
|
221
|
+
# Tests if op is a binary operation.
|
222
|
+
def assymetric_binop? op
|
223
|
+
[:asr, :bsr, :div, :mod, :rol, :ror, :shl, :shr, :sub].member?(op)
|
224
|
+
end
|
225
|
+
|
226
|
+
# Tests if a value is an at-expression.
|
227
|
+
def at_expr? value
|
228
|
+
value.respond_to?(:[]) && value[0] == :'@'
|
229
|
+
end
|
230
|
+
|
231
|
+
# Tests if op is a binary operation.
|
232
|
+
def binop? op
|
233
|
+
assymetric_binop?(op) || symmetric_binop?(op)
|
234
|
+
end
|
235
|
+
|
132
236
|
# Processes code in its own block. Local variables can be
|
133
237
|
# introduced inside the block. They will be deleted at the
|
134
238
|
# end of the block.
|
239
|
+
#
|
240
|
+
# This method requires subclasses to implement begin_block and
|
241
|
+
# end_block.
|
135
242
|
def block *code
|
136
243
|
begin_block *code
|
137
244
|
code.each { |action| add section, action }
|
138
245
|
end_block
|
139
246
|
end
|
140
247
|
|
248
|
+
# Returns the number of local variable slots required by
|
249
|
+
# a sequence of statements.
|
250
|
+
def count_locals statements
|
251
|
+
count_locals_helper statements, 0, 0
|
252
|
+
end
|
253
|
+
|
254
|
+
# Returns the default alignment for the given section.
|
255
|
+
# If no section is specified, returns the alignment for the current
|
256
|
+
# section.
|
257
|
+
def default_alignment section = @section
|
258
|
+
# Get default alignment
|
259
|
+
case section
|
260
|
+
when :code
|
261
|
+
@CODE_ALIGNMENT
|
262
|
+
when :data
|
263
|
+
@DATA_ALIGNMENT
|
264
|
+
when :function
|
265
|
+
@FUNCTION_ALIGNMENT
|
266
|
+
else
|
267
|
+
# Use data alignment as default
|
268
|
+
@DATA_ALIGNMENT
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
141
272
|
# Invokes block with each statement in the given list of statements.
|
142
273
|
# This iterator also descends into nested statements, calling
|
143
274
|
# block first with the outer statement, and then for each inner
|
@@ -157,38 +288,108 @@ module Voodoo
|
|
157
288
|
end
|
158
289
|
end
|
159
290
|
|
160
|
-
#
|
161
|
-
|
162
|
-
|
163
|
-
# the feature, such as a version number.
|
164
|
-
def features
|
165
|
-
@features
|
291
|
+
# Adds code to the current section.
|
292
|
+
def emit code
|
293
|
+
@sections[real_section_name(@section)] << code
|
166
294
|
end
|
167
295
|
|
168
|
-
#
|
296
|
+
# Emits a label.
|
297
|
+
def emit_label name
|
298
|
+
emit "#{name}:\n"
|
299
|
+
end
|
300
|
+
|
301
|
+
# Emits a comment with the given Voodoo code.
|
302
|
+
def emit_voodoo *code
|
303
|
+
comment code.join(' ')
|
304
|
+
end
|
305
|
+
|
306
|
+
# Declares that the given symbols are to be externally visible.
|
307
|
+
# Requires subclasses to implement emit_export.
|
308
|
+
def export *symbols
|
309
|
+
if real_section_name(section) == ".data"
|
310
|
+
@relocated_symbols.merge symbols
|
311
|
+
end
|
312
|
+
emit_export *symbols
|
313
|
+
end
|
314
|
+
|
315
|
+
# Adds a function to the current section.
|
316
|
+
# Requires subclasses to implement begin_function and end_function.
|
169
317
|
def function formals, *code
|
170
|
-
|
318
|
+
nlocals = count_locals code
|
319
|
+
begin_function formals, nlocals
|
171
320
|
code.each { |action| add section, action }
|
172
321
|
end_function
|
173
322
|
end
|
174
323
|
|
175
|
-
#
|
176
|
-
def
|
177
|
-
|
324
|
+
# Tests if a symbol refers to a global.
|
325
|
+
def global? symbol
|
326
|
+
symbol?(symbol) && @environment[symbol] == nil
|
178
327
|
end
|
179
328
|
|
180
|
-
#
|
181
|
-
|
182
|
-
|
329
|
+
# Declares that the given symbols are imported from an external object.
|
330
|
+
# Requires subclasses to implement emit_import.
|
331
|
+
def import *symbols
|
332
|
+
if real_section_name(section) == ".data"
|
333
|
+
@relocated_symbols.merge symbols
|
334
|
+
end
|
335
|
+
emit_import *symbols
|
183
336
|
end
|
184
337
|
|
185
|
-
#
|
186
|
-
|
187
|
-
|
188
|
-
|
338
|
+
# Executes a block of code using the given section as the current section.
|
339
|
+
def in_section name, &block
|
340
|
+
oldsection = @section
|
341
|
+
self.section = name
|
342
|
+
begin
|
343
|
+
yield
|
344
|
+
ensure
|
345
|
+
self.section = oldsection
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
# Tests if a value is an integer.
|
350
|
+
def integer? value
|
351
|
+
value.kind_of? Integer
|
352
|
+
end
|
353
|
+
|
354
|
+
# Creates a label.
|
355
|
+
# Besides emitting the label name, this also annotates top-level
|
356
|
+
# objects with type and size as required for ELF shared libraries.
|
357
|
+
# Requires subclasses to emit emit_label and emit_label_type.
|
358
|
+
def label name
|
359
|
+
# If we are at top level, emit type directive and arrange to
|
360
|
+
# emit size directive.
|
361
|
+
if @environment == @top_level
|
362
|
+
case real_section_name(section)
|
363
|
+
when ".text"
|
364
|
+
type = :code
|
365
|
+
else
|
366
|
+
type = :data
|
367
|
+
end
|
368
|
+
emit_label_type name, type
|
369
|
+
@open_labels << name
|
370
|
+
end
|
371
|
+
emit_label name
|
372
|
+
end
|
373
|
+
|
374
|
+
# Returns the register in which the nth local (0-based) is stored, or
|
375
|
+
# nil if not stored in a register.
|
376
|
+
def local_register n
|
377
|
+
@LOCAL_REGISTERS[n + number_of_register_arguments]
|
189
378
|
end
|
190
379
|
|
191
|
-
#
|
380
|
+
# Calculates the number of register arguments,
|
381
|
+
# given the total number of arguments.
|
382
|
+
def number_of_register_arguments n = @environment.args
|
383
|
+
[n, @NREGISTER_ARGS].min
|
384
|
+
end
|
385
|
+
|
386
|
+
# Calculate the number of stack arguments,
|
387
|
+
# given the total number of arguments.
|
388
|
+
def number_of_stack_arguments n = @environment.args
|
389
|
+
[0, n - @NREGISTER_ARGS].max
|
390
|
+
end
|
391
|
+
|
392
|
+
# Gets the real name of a section.
|
192
393
|
# Given a section name which may be an alias, this method returns the
|
193
394
|
# real name of the section.
|
194
395
|
def real_section_name name
|
@@ -204,7 +405,114 @@ module Voodoo
|
|
204
405
|
name
|
205
406
|
end
|
206
407
|
|
207
|
-
#
|
408
|
+
# Returns true if operand is a register, false otherwise.
|
409
|
+
def register? x
|
410
|
+
x.kind_of? Symbol
|
411
|
+
end
|
412
|
+
|
413
|
+
# Returns true if the nth (0-based) argument is stored in a register.
|
414
|
+
def register_arg? n
|
415
|
+
n < @NREGISTER_ARGS
|
416
|
+
end
|
417
|
+
|
418
|
+
# Given some local variable names, returns the registers those variables
|
419
|
+
# are stored in. If no variable names are given, returns all registers
|
420
|
+
# used to store local variables.
|
421
|
+
#
|
422
|
+
# Requires @LOCAL_REGISTERS_SET, a set of registers that are used to
|
423
|
+
# store local variables.
|
424
|
+
def registers_for_locals locals = []
|
425
|
+
locals = @environment.symbols.keys if locals.empty?
|
426
|
+
registers = []
|
427
|
+
locals.each do |sym|
|
428
|
+
reg = @environment[sym]
|
429
|
+
registers << reg if @LOCAL_REGISTERS_SET.include? @environment[sym]
|
430
|
+
end
|
431
|
+
registers
|
432
|
+
end
|
433
|
+
|
434
|
+
# Restores the frame saved at the given location.
|
435
|
+
# Requires @SAVE_FRAME_REGISTERS, an array of register names that
|
436
|
+
# must be restored.
|
437
|
+
def restore_frame frame
|
438
|
+
restore_registers_from_frame frame, @SAVE_FRAME_REGISTERS
|
439
|
+
end
|
440
|
+
|
441
|
+
# Restores local variables from a saved frame.
|
442
|
+
def restore_locals frame, *locals
|
443
|
+
restore_registers_from_frame frame, registers_for_locals(locals)
|
444
|
+
end
|
445
|
+
|
446
|
+
# Helper function for restore_frame and restore_locals.
|
447
|
+
#
|
448
|
+
# Requires @SAVED_FRAME_LAYOUT, a map from register names to positions
|
449
|
+
# in a saved frame, emit_load_word to load registers from memory, and
|
450
|
+
# load_value_into_register to load a Voodoo value into a CPU register.
|
451
|
+
def restore_registers_from_frame frame, registers
|
452
|
+
with_temporary do |temporary|
|
453
|
+
load_value_into_register frame, temporary
|
454
|
+
registers.each do |register|
|
455
|
+
i = @SAVED_FRAME_LAYOUT[register]
|
456
|
+
emit_load_word register, temporary, i
|
457
|
+
end
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
# Saves the current frame to the given location.
|
462
|
+
#
|
463
|
+
# Requires @SAVE_FRAME_REGISTERS, an array of names of registers
|
464
|
+
# that must be saved, and @LOCAL_REGISTERS, the list of registers
|
465
|
+
# that are used to store values of local variables.
|
466
|
+
def save_frame frame
|
467
|
+
registers_to_save = @SAVE_FRAME_REGISTERS -
|
468
|
+
(@saved_registers & @LOCAL_REGISTERS)
|
469
|
+
save_registers_to_frame frame, registers_to_save
|
470
|
+
end
|
471
|
+
|
472
|
+
# Saves the current frame to the given location.
|
473
|
+
# If locals are given, saves those locals to the
|
474
|
+
# frame, too. If no locals are given, saves all
|
475
|
+
# locals.
|
476
|
+
#
|
477
|
+
# Requires @SAVE_FRAME_REGISTERS, an array of names of registers
|
478
|
+
# that must be saved, and @LOCAL_REGISTERS, the list of registers
|
479
|
+
# that are used to store values of local variables.
|
480
|
+
def save_frame_and_locals frame, *locals
|
481
|
+
registers_to_save = (@SAVE_FRAME_REGISTERS -
|
482
|
+
(@saved_registers & @LOCAL_REGISTERS)) |
|
483
|
+
registers_for_locals(locals)
|
484
|
+
save_registers_to_frame frame, registers_to_save
|
485
|
+
end
|
486
|
+
|
487
|
+
# Saves local variables to the given frame.
|
488
|
+
# If no locals are specified, saves all locals.
|
489
|
+
# If locals are specified, saves only the specified ones.
|
490
|
+
def save_locals frame, *locals
|
491
|
+
save_registers_to_frame frame, registers_for_locals(locals)
|
492
|
+
end
|
493
|
+
|
494
|
+
# Helper function for save_frame and save_locals.
|
495
|
+
#
|
496
|
+
# Requires @SAVED_FRAME_LAYOUT, a map from register names to positions
|
497
|
+
# in a saved frame, emit_store_word to store registers in memory, and
|
498
|
+
# load_value_into_register to load a Voodoo value into a CPU register.
|
499
|
+
def save_registers_to_frame frame, registers
|
500
|
+
return if registers.empty?
|
501
|
+
with_temporary do |temporary|
|
502
|
+
load_value_into_register frame, temporary
|
503
|
+
registers.each do |register|
|
504
|
+
i = @SAVED_FRAME_LAYOUT[register]
|
505
|
+
emit_store_word register, temporary, i
|
506
|
+
end
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
# Returns the number of bytes necessary to save the current frame.
|
511
|
+
def saved_frame_size
|
512
|
+
@SAVED_FRAME_LAYOUT.length * @WORDSIZE
|
513
|
+
end
|
514
|
+
|
515
|
+
# Sets the current section.
|
208
516
|
def section= name
|
209
517
|
real_name = real_section_name name
|
210
518
|
@section = name
|
@@ -213,40 +521,90 @@ module Voodoo
|
|
213
521
|
end
|
214
522
|
end
|
215
523
|
|
524
|
+
# Returns the name of the current section.
|
525
|
+
# If a name is given, sets the name of the current section first.
|
216
526
|
def section name = nil
|
217
527
|
self.section = name if name
|
218
528
|
@section
|
219
529
|
end
|
220
530
|
|
221
|
-
#
|
531
|
+
# Sets up _alias_name_ to refer to the same section as _original_name_.
|
222
532
|
def section_alias alias_name, original_name
|
223
533
|
@section_aliases[alias_name] = original_name
|
224
534
|
end
|
225
535
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
536
|
+
# Given n, returns the nearest multiple of @STACK_ALIGNMENT
|
537
|
+
# that is >= n.
|
538
|
+
def stack_align n
|
539
|
+
(n + @STACK_ALIGNMENT - 1) / @STACK_ALIGNMENT * @STACK_ALIGNMENT
|
540
|
+
end
|
541
|
+
|
542
|
+
# Tests if a value is a substitution.
|
543
|
+
def substitution? x
|
544
|
+
x.respond_to?(:[]) && x[0] == :'%'
|
545
|
+
end
|
546
|
+
|
547
|
+
# Substitutes a numeric value for a given substitution key.
|
548
|
+
def substitute_number key
|
549
|
+
case key
|
550
|
+
when :'saved-frame-size'
|
551
|
+
saved_frame_size
|
552
|
+
else
|
553
|
+
@features[key].to_i
|
233
554
|
end
|
234
555
|
end
|
556
|
+
|
557
|
+
# Tests if a value is a symbol.
|
558
|
+
def symbol? value
|
559
|
+
value.kind_of? Symbol
|
560
|
+
end
|
235
561
|
|
236
|
-
#
|
237
|
-
#
|
238
|
-
def
|
239
|
-
|
562
|
+
# Test if op is a symmetric binary operation (i.e. it will yield the
|
563
|
+
# same result if the order of its source operands is changed).
|
564
|
+
def symmetric_binop? op
|
565
|
+
[:add, :and, :mul, :or, :xor].member? op
|
240
566
|
end
|
241
567
|
|
242
|
-
#
|
243
|
-
|
244
|
-
|
568
|
+
# Executes a block of code, passing the block the name of +n+
|
569
|
+
# unused temporary registers.
|
570
|
+
#
|
571
|
+
# Requires @TEMPORARIES, an array of temporary registers.
|
572
|
+
def with_temporaries n, &block
|
573
|
+
if @TEMPORARIES.length < n
|
574
|
+
raise "Out of temporary registers"
|
575
|
+
else
|
576
|
+
temporaries = @TEMPORARIES.shift n
|
577
|
+
begin
|
578
|
+
yield *temporaries
|
579
|
+
ensure
|
580
|
+
@TEMPORARIES.unshift *temporaries
|
581
|
+
end
|
582
|
+
end
|
583
|
+
end
|
584
|
+
|
585
|
+
# Executes a block of code, passing the block the name of an unused
|
586
|
+
# temporary register as its first argument.
|
587
|
+
def with_temporary &block
|
588
|
+
with_temporaries 1, &block
|
589
|
+
end
|
590
|
+
|
591
|
+
# Writes generated code to the given IO object.
|
592
|
+
def write io
|
593
|
+
@sections.each do |section,code|
|
594
|
+
unless code.empty?
|
595
|
+
io.puts ".section #{section.to_s}"
|
596
|
+
io.puts code
|
597
|
+
io.puts
|
598
|
+
end
|
599
|
+
end
|
245
600
|
end
|
246
601
|
|
602
|
+
# Used for scoping. Maintains a symbol table and keeps track of the number
|
603
|
+
# of bytes that have been allocated on the stack.
|
247
604
|
class Environment
|
248
605
|
@@gensym_counter = 0
|
249
606
|
|
607
|
+
attr_accessor :bytes, :offset
|
250
608
|
attr_reader :args, :locals, :parent, :symbols
|
251
609
|
|
252
610
|
# Creates a new environment.
|
@@ -261,30 +619,54 @@ module Voodoo
|
|
261
619
|
@args = parent ? parent.args : 0
|
262
620
|
## Number of local variables
|
263
621
|
@locals = parent ? parent.locals : 0
|
622
|
+
## Offset between base pointer and end of frame.
|
623
|
+
@offset = parent ? parent.offset : 0
|
624
|
+
## Number of bytes allocated in this environment.
|
625
|
+
@bytes = 0
|
264
626
|
end
|
265
627
|
|
266
628
|
# Adds symbol as an argument in this environment.
|
267
|
-
|
268
|
-
|
629
|
+
# +info+ can be used by the code generator to store information it needs
|
630
|
+
# about the symbol.
|
631
|
+
def add_arg symbol, info = nil
|
632
|
+
@symbols[symbol] = info
|
269
633
|
@args = @args + 1
|
270
634
|
end
|
271
635
|
|
272
636
|
# Adds each of symbols to the arguments in
|
273
637
|
# this environment.
|
638
|
+
# Each entry in +symbols+ can be either a symbol,
|
639
|
+
# or an array where the first element is the symbol and the second
|
640
|
+
# element is extra information to be stored with the symbol.
|
274
641
|
def add_args symbols
|
275
|
-
symbols.each
|
642
|
+
symbols.each do |sym|
|
643
|
+
if sym.respond_to? :[]
|
644
|
+
add_arg *sym
|
645
|
+
else
|
646
|
+
add_arg sym
|
647
|
+
end
|
648
|
+
end
|
276
649
|
end
|
277
650
|
|
278
651
|
# Adds symbol as a local variable in this environment.
|
279
|
-
def add_local symbol
|
280
|
-
@symbols[symbol] =
|
652
|
+
def add_local symbol, info = nil
|
653
|
+
@symbols[symbol] = info
|
281
654
|
@locals = @locals + 1
|
282
655
|
end
|
283
656
|
|
284
657
|
# Adds each of symbols to the local variables in
|
285
658
|
# this environment.
|
659
|
+
# Each entry in +symbols+ can be either a symbol,
|
660
|
+
# or an array where the first element is the symbol and the second
|
661
|
+
# element is extra information to be stored with the symbol.
|
286
662
|
def add_locals symbols
|
287
|
-
symbols.each
|
663
|
+
symbols.each do |sym|
|
664
|
+
if sym.respond_to? :[]
|
665
|
+
add_local *sym
|
666
|
+
else
|
667
|
+
add_local sym
|
668
|
+
end
|
669
|
+
end
|
288
670
|
end
|
289
671
|
|
290
672
|
# Generates a new, unique symbol.
|
@@ -309,5 +691,21 @@ module Voodoo
|
|
309
691
|
end
|
310
692
|
end
|
311
693
|
|
694
|
+
private
|
695
|
+
|
696
|
+
# Returns the number of local variable slots required for the given
|
697
|
+
# statements, given the current count and required number of slots.
|
698
|
+
def count_locals_helper statements, count, max
|
699
|
+
statements.each do |statement|
|
700
|
+
case statement[0]
|
701
|
+
when :block
|
702
|
+
max = count_locals_helper statement[1..-1], count, max
|
703
|
+
when :let
|
704
|
+
count = count + 1
|
705
|
+
max = count if count > max
|
706
|
+
end
|
707
|
+
end
|
708
|
+
max
|
709
|
+
end
|
312
710
|
end
|
313
711
|
end
|