voodoo 0.6.3 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -76,6 +76,10 @@ module Voodoo
76
76
  @CX = 'rcx'
77
77
  # Data index
78
78
  @DX = 'rdx'
79
+ # Base pointer
80
+ @BP = 'rbp'
81
+ # Stack pointer
82
+ @SP = 'rsp'
79
83
  super params
80
84
  end
81
85
 
@@ -55,11 +55,14 @@ module Voodoo
55
55
  # of the following methods, which must be implemented by subclasses:
56
56
  #
57
57
  # - #align
58
+ # - #asr
59
+ # - #bsr
58
60
  # - #byte
59
61
  # - #call
60
62
  # - #end_if
61
63
  # - #export
62
64
  # - #begin_function
65
+ # - #block
63
66
  # - #ifelse
64
67
  # - #ifeq
65
68
  # - #ifge
@@ -71,9 +74,13 @@ module Voodoo
71
74
  # - #label
72
75
  # - #let
73
76
  # - #ret
77
+ # - #rol
78
+ # - #ror
74
79
  # - #set
75
80
  # - #set_byte
76
81
  # - #set_word
82
+ # - #shl
83
+ # - #shr
77
84
  # - #string
78
85
  # - #word
79
86
  #
@@ -82,6 +89,8 @@ module Voodoo
82
89
  code.each do |action|
83
90
  keyword, args = action[0], action[1..-1]
84
91
  case keyword
92
+ when :block
93
+ block *args
85
94
  when :function
86
95
  function args[0], *args[1..-1]
87
96
  when :ifeq, :ifge, :ifgt, :ifle, :iflt, :ifne
@@ -121,6 +130,34 @@ module Voodoo
121
130
  add :functions, [:function, formals] + code
122
131
  end
123
132
 
133
+ # Processes code in its own block. Local variables can be
134
+ # introduced inside the block. They will be deleted at the
135
+ # end of the block.
136
+ def block *code
137
+ begin_block *code
138
+ code.each { |action| add section, action }
139
+ end_block
140
+ end
141
+
142
+ # Invokes block with each statement in the given list of statements.
143
+ # This iterator also descends into nested statements, calling
144
+ # block first with the outer statement, and then for each inner
145
+ # statement.
146
+ def each_statement statements, &block
147
+ statements.each do |statement|
148
+ yield statement
149
+ case statement[0]
150
+ when :block
151
+ each_statement statement[1..-1], &block
152
+ when :function
153
+ each_statement statement[2..-1], &block
154
+ when :ifeq, :ifle, :iflt, :ifge, :ifgt, :ifne
155
+ each_statement statement[2], &block
156
+ each_statement(statement[3], &block) if statement.length > 3
157
+ end
158
+ end
159
+ end
160
+
124
161
  # Add a function to the current section
125
162
  def function formals, *code
126
163
  begin_function *formals
@@ -197,8 +234,11 @@ module Voodoo
197
234
  class Environment
198
235
  @@gensym_counter = 0
199
236
 
200
- attr_reader :args, :locals, :symbols
237
+ attr_reader :args, :locals, :parent, :symbols
201
238
 
239
+ # Creates a new environment.
240
+ # If parent is specified, it will become the new environment's parent.
241
+ # The new environment inherits all symbols from the parent environment.
202
242
  def initialize parent = nil
203
243
  ## Parent environment
204
244
  @parent = parent
@@ -210,37 +250,47 @@ module Voodoo
210
250
  @locals = parent ? parent.locals : 0
211
251
  end
212
252
 
253
+ # Adds symbol as an argument in this environment.
213
254
  def add_arg symbol
214
255
  @symbols[symbol] = [:arg, @args]
215
256
  @args = @args + 1
216
257
  end
217
258
 
259
+ # Adds each of symbols to the arguments in
260
+ # this environment.
218
261
  def add_args symbols
219
262
  symbols.each { |sym| add_arg sym }
220
263
  end
221
264
 
265
+ # Adds symbol as a local variable in this environment.
222
266
  def add_local symbol
223
267
  @symbols[symbol] = [:local, @locals]
224
268
  @locals = @locals + 1
225
269
  end
226
270
 
271
+ # Adds each of symbols to the local variables in
272
+ # this environment.
227
273
  def add_locals symbols
228
274
  symbols.each { |sym| add_local sym }
229
275
  end
230
276
 
277
+ # Generates a new, unique symbol.
231
278
  def gensym
232
279
  Environment.gensym
233
280
  end
234
281
 
282
+ # Looks up symbol in this environment.
235
283
  def [] symbol
236
284
  @symbols[symbol]
237
285
  end
238
286
 
287
+ # Generates a new, unique symbol.
239
288
  def self.gensym
240
289
  @@gensym_counter = @@gensym_counter + 1
241
290
  "_G#{@@gensym_counter}".to_sym
242
291
  end
243
292
 
293
+ # Returns an initial, top-level environment.
244
294
  def self.initial_environment
245
295
  Environment.new
246
296
  end
@@ -56,6 +56,10 @@ module Voodoo
56
56
  @CX = 'ecx'
57
57
  # Data index
58
58
  @DX = 'edx'
59
+ # Base pointer
60
+ @BP = 'ebp'
61
+ # Stack pointer
62
+ @SP = 'esp'
59
63
  super params
60
64
  end
61
65
 
@@ -160,7 +160,7 @@ module Voodoo
160
160
 
161
161
  # Test if op is a binary operation
162
162
  def assymetric_binop? op
163
- [:div, :mod, :sub].member?(op)
163
+ [:asr, :bsr, :div, :mod, :rol, :ror, :shl, :shr, :sub].member?(op)
164
164
  end
165
165
 
166
166
  # Test if a value is an at-expression
@@ -168,6 +168,17 @@ module Voodoo
168
168
  value.respond_to?(:[]) && value[0] == :'@'
169
169
  end
170
170
 
171
+ # Begins a new block.
172
+ def begin_block *code
173
+ emit "# begin block\n"
174
+ # If we are at top-level, create a frame
175
+ if @environment == @top_level
176
+ create_frame count_locals(code)
177
+ end
178
+ environment = Environment.new @environment
179
+ @environment = environment
180
+ end
181
+
171
182
  # Emit function prologue and declare _formals_ as function arguments
172
183
  def begin_function formals, nlocals
173
184
  if @environment != @top_level
@@ -282,6 +293,25 @@ module Voodoo
282
293
  emit "nop\n"
283
294
  end
284
295
 
296
+ # Counts the number of local variables created in
297
+ # a sequence of statements.
298
+ def count_locals statements
299
+ count = 0
300
+ each_statement(statements) do |statement|
301
+ if statement[0] == :let
302
+ # let introduces a single local
303
+ count = count + 1
304
+ end
305
+ end
306
+ count
307
+ end
308
+
309
+ # Creates an activation frame which can store nlocals local variables
310
+ def create_frame nlocals
311
+ @frame_size = (@INITIAL_FRAME_SIZE + nlocals * @WORDSIZE + 7) / 8 * 8
312
+ emit "addiu $sp, $sp, -#{@frame_size}\n"
313
+ end
314
+
285
315
  # Emit function prologue.
286
316
  def emit_function_prologue formals = [], nlocals = 0
287
317
  # Calculate new value for $gp
@@ -295,9 +325,7 @@ module Voodoo
295
325
  emit "sw $#{n + @REGISTER_ARG_BASE}, #{n * @WORDSIZE}($29)\n"
296
326
  end
297
327
 
298
- # Create initial frame
299
- @frame_size = (@INITIAL_FRAME_SIZE + nlocals * @WORDSIZE + 7) / 8 * 8
300
- emit "addiu $sp, $sp, -#{@frame_size}\n"
328
+ create_frame nlocals
301
329
 
302
330
  # Save return address
303
331
  emit "sw $31, #{@frame_size - @WORDSIZE}($sp)\n"
@@ -333,6 +361,19 @@ module Voodoo
333
361
  @environment = @top_level
334
362
  end
335
363
 
364
+ # Ends the current block.
365
+ def end_block
366
+ emit "# end block\n"
367
+
368
+ # If we are returning to top level, restore stack pointer
369
+ if @environment.parent == @top_level
370
+ emit "addiu $sp, $sp, #{@frame_size}\n"
371
+ end
372
+
373
+ # Restore old value of @environment
374
+ @environment = @environment.parent
375
+ end
376
+
336
377
  # End a conditional.
337
378
  def end_if
338
379
  label = @if_labels.pop
@@ -348,6 +389,10 @@ module Voodoo
348
389
  temporaries.delete y
349
390
 
350
391
  case expr[0]
392
+ when :asr
393
+ emit "srav #{register}, #{x}, #{y}\n"
394
+ when :bsr
395
+ emit "srlv #{register}, #{x}, #{y}\n"
351
396
  when :div
352
397
  emit "div $0, #{x}, #{y}\n"
353
398
  emit "mflo #{register}\n"
@@ -357,6 +402,20 @@ module Voodoo
357
402
  when :mul
358
403
  emit "mult #{x}, #{y}\n"
359
404
  emit "mflo #{register}\n"
405
+ when :rol
406
+ emit "subu #{@TEMPORARY}, $0, #{y}\n"
407
+ emit "srlv #{@TEMPORARY}, #{x}, #{@TEMPORARY}\n"
408
+ emit "sllv #{register}, #{x}, #{y}\n"
409
+ emit "or #{register}, #{register}, #{@TEMPORARY}\n"
410
+ when :ror
411
+ emit "subu #{@TEMPORARY}, $0, #{y}\n"
412
+ emit "sllv #{@TEMPORARY}, #{x}, #{@TEMPORARY}\n"
413
+ emit "srlv #{register}, #{x}, #{y}\n"
414
+ emit "or #{register}, #{register}, #{@TEMPORARY}\n"
415
+ when :shl
416
+ emit "sllv #{register}, #{x}, #{y}\n"
417
+ when :shr
418
+ emit "srlv #{register}, #{x}, #{y}\n"
360
419
  else
361
420
  emit "#{expr[0]} #{register}, #{x}, #{y}\n"
362
421
  end
@@ -397,9 +456,7 @@ module Voodoo
397
456
 
398
457
  # Add a function to the current section
399
458
  def function formals, *code
400
- # Set nlocals to the number of local variables in the function
401
- nlocals = 0
402
- code.each { |action| nlocals = nlocals + 1 if action[0] == :let }
459
+ nlocals = count_locals code
403
460
  begin_function formals, nlocals
404
461
  code.each { |action| add section, action }
405
462
  end_function
@@ -514,9 +571,13 @@ module Voodoo
514
571
  n = @environment.locals
515
572
  @environment.add_local symbol
516
573
 
517
- # Extend stack frame, if necessary
574
+ # Extend stack frame, if necessary.
575
+ # This should never be necessary, because we allocate enough
576
+ # space in the top-level function or block.
518
577
  if @environment.locals > max_locals
519
578
  $stderr.puts "WARNING: Stack frame too small. Possible BUG"
579
+ $stderr.puts "Frame size #{@frame_size}, max locals #{max_locals}" +
580
+ ", actual locals #{@environment.locals}"
520
581
  # Extend frame by 8 bytes, to keep $sp a multiple of 8
521
582
  emit "addiu $sp, $sp, -8\n"
522
583
  @frame_size = @frame_size + 8
@@ -525,11 +586,13 @@ module Voodoo
525
586
  register = local_register n
526
587
  ref = local_reference n
527
588
  if register
589
+ # We will use a register to store the value
528
590
  # Save current value of register
529
591
  emit "sw #{register}, #{ref}\n"
530
592
  # Set new value
531
593
  eval_expr expr, register
532
594
  else
595
+ # We will use the stack to store the value
533
596
  eval_expr expr, @TEMPORARY
534
597
  emit "sw #{@TEMPORARY}, #{ref}\n"
535
598
  end
@@ -651,7 +714,7 @@ module Voodoo
651
714
  def ret *words
652
715
  emit "# return #{words.join ' '}\n"
653
716
  # Compute return value and store it in @RETURN
654
- eval_expr words, @RETURN
717
+ eval_expr(words, @RETURN) unless words.empty?
655
718
  # Go to epilogue
656
719
  goto @function_end_label
657
720
  end
@@ -21,7 +21,7 @@ module Voodoo
21
21
  # - @SCRATCH_REG
22
22
  # - @WORD_NAME
23
23
  # - @WORDSIZE
24
- # - @AX, @BX, @CX, and @DX
24
+ # - @AX, @BX, @CX, @DX, @BP, and @SP
25
25
  class NasmGenerator < CommonCodeGenerator
26
26
  def initialize params = {}
27
27
  super params
@@ -166,11 +166,43 @@ module Voodoo
166
166
  # of the evaluation is returned from the function.
167
167
  def ret *words
168
168
  emit "; return #{words.join ' '}\n"
169
- eval_expr words
169
+ eval_expr(words) unless words.empty?
170
170
  emit_function_epilogue
171
171
  emit "ret\n"
172
172
  end
173
173
 
174
+ #
175
+ # == Blocks
176
+ #
177
+
178
+ # Begins a new block.
179
+ def begin_block *code
180
+ emit "; begin block\n"
181
+ # If entering a block at top level,
182
+ # Save @BP, then set @BP to @SP
183
+ if @environment == @top_level
184
+ emit "push #{@BP}\n"
185
+ emit "mov #{@BP}, #{@SP}\n"
186
+ end
187
+ environment = Environment.new @environment
188
+ @environment = environment
189
+ end
190
+
191
+ # Ends the current block.
192
+ def end_block
193
+ emit "; end block\n"
194
+
195
+ # De-allocate block's variables
196
+ nvars = @environment.locals - @environment.parent.locals
197
+ emit "add #{@SP}, #{nvars * @WORDSIZE}\n" unless nvars == 0
198
+
199
+ # Restore old value of @environment
200
+ @environment = @environment.parent
201
+
202
+ # If returning to top level, restore old @BP
203
+ emit "pop #{@BP}\n" if @environment == @top_level
204
+ end
205
+
174
206
  #
175
207
  # == Conditionals
176
208
  #
@@ -192,7 +224,8 @@ module Voodoo
192
224
 
193
225
  # Test if op is a binary operation
194
226
  def binop? op
195
- [:div, :mod, :sub].member?(op) || symmetric_operation?(op)
227
+ [:asr, :bsr, :div, :mod, :rol, :ror, :shl, :shr, :sub].member?(op) ||
228
+ symmetric_operation?(op)
196
229
  end
197
230
 
198
231
  # Test if a value is an integer
@@ -416,18 +449,24 @@ module Voodoo
416
449
  return mod(target, x, y) if op == :mod
417
450
  return mul(target, x, y) if op == :mul
418
451
 
419
- target_ref = load_value target, @AX
452
+ target_ref = load_value target, @BX
420
453
  x_ref = load_value x, @DX
421
- y_ref = load_value y, @BX
454
+ y_ref = load_value y, @CX
455
+
456
+ mnemonic = action_to_mnemonic op
457
+ if [:asr, :bsr, :rol, :ror, :shl, :shr].member? op
458
+ emit "mov cl, #{y_ref}\n" unless y_ref == @CX
459
+ y_ref = 'cl'
460
+ end
422
461
 
423
462
  if memory_operand?(target_ref)
424
463
  if memory_operand?(x_ref) || memory_operand?(y_ref)
425
- emit "mov #{@CX}, #{x_ref}\n"
426
- emit "#{op} #{@CX}, #{y_ref}\n"
427
- emit "mov #{target_ref}, #{@CX}\n"
464
+ emit "mov #{@AX}, #{x_ref}\n"
465
+ emit "#{mnemonic} #{@AX}, #{y_ref}\n"
466
+ emit "mov #{target_ref}, #{@AX}\n"
428
467
  else
429
468
  emit "mov #{@WORD_NAME} #{target_ref}, #{x_ref}\n"
430
- emit "#{op} #{@WORD_NAME} #{target_ref}, #{y_ref}\n"
469
+ emit "#{mnemonic} #{@WORD_NAME} #{target_ref}, #{y_ref}\n"
431
470
  end
432
471
  else
433
472
  raise "Can't happen: target_ref is #{target_ref.inspect}"
@@ -443,13 +482,17 @@ module Voodoo
443
482
  return mod2(target, y) if op == :mod
444
483
  return mul2(target, y) if op == :mul
445
484
 
446
- target_ref = load_value target, @BX
447
- y_ref = load_value y, @DX
448
- if memory_operand?(target_ref) && memory_operand?(y_ref)
449
- emit "mov #{@AX}, #{y_ref}\n"
450
- emit "#{op} #{target_ref}, #{@AX}\n"
485
+ target_ref = load_value target, @AX
486
+ y_ref = load_value y, @CX
487
+ mnemonic = action_to_mnemonic op
488
+ if [:asr, :bsr, :rol, :ror, :shl, :shr].member? op
489
+ emit "mov cl, #{y_ref}\n" unless y_ref == @CX
490
+ emit "#{mnemonic} #{@WORD_NAME} #{target_ref}, cl\n"
491
+ elsif memory_operand?(target_ref) && memory_operand?(y_ref)
492
+ emit "mov #{@CX}, #{y_ref}\n"
493
+ emit "#{mnemonic} #{target_ref}, #{@CX}\n"
451
494
  else
452
- emit "#{op} #{@WORD_NAME} #{target_ref}, #{y_ref}\n"
495
+ emit "#{mnemonic} #{@WORD_NAME} #{target_ref}, #{y_ref}\n"
453
496
  end
454
497
  end
455
498
 
@@ -510,6 +553,7 @@ module Voodoo
510
553
 
511
554
  # Evaluate an expression.
512
555
  # The result is stored in _register_ (@RETURN_REG by default).
556
+ # The following registers may be clobbered: @AX, @BX, @CX, @DX
513
557
  def eval_expr words, register = @RETURN_REG
514
558
  if words.length == 1
515
559
  if words[0] == 0
@@ -520,6 +564,10 @@ module Voodoo
520
564
  else
521
565
  op = words[0]
522
566
  case op
567
+ when :asr, :bsr, :rol, :ror, :shl, :shr
568
+ load_value_into_register words[2], @CX
569
+ load_value_into_register words[1], register
570
+ emit "#{action_to_mnemonic op} #{register}, cl\n"
523
571
  when :call
524
572
  call *words[1..-1]
525
573
  when :div
@@ -666,6 +714,18 @@ module Voodoo
666
714
  # == Miscellaneous
667
715
  #
668
716
 
717
+ # Translates a Voodoo action name to an x86 mnemonic
718
+ def action_to_mnemonic action
719
+ case action
720
+ when :asr
721
+ :sar
722
+ when :bsr
723
+ :shr
724
+ else
725
+ action
726
+ end
727
+ end
728
+
669
729
  # Emit a comment
670
730
  def comment text
671
731
  emit ";#{text}\n"
data/lib/voodoo/parser.rb CHANGED
@@ -139,16 +139,10 @@ module Voodoo
139
139
  body = parse_body :function
140
140
  [:function, words[1..-1], body]
141
141
  elsif is_conditional?(words[0])
142
- # Conditional. Parse true part
143
- true_body = parse_body :conditional
144
- # Parse false part, if present
145
- if true_body[-1] == :else
146
- true_body = true_body[0..-2]
147
- false_body = parse_body :conditional
148
- else
149
- false_body = []
150
- end
151
- [words[0], words[1..-1], true_body, false_body]
142
+ parse_conditional1 words[0], words[1..-1]
143
+ elsif words[0] == :block
144
+ body = parse_body :block
145
+ [:block] + body
152
146
  else
153
147
  # Statement or data declaration; simply return it
154
148
  words
@@ -173,7 +167,7 @@ module Voodoo
173
167
  break
174
168
  elsif kind == :conditional && statement[0] == :else
175
169
  # Done parsing body, but there is another one coming up
176
- body << :else
170
+ body << statement
177
171
  break
178
172
  else
179
173
  # Parsed a statement. Add it to body.
@@ -293,6 +287,53 @@ module Voodoo
293
287
  def is_conditional? symbol
294
288
  [:ifeq, :ifge, :ifgt, :ifle, :iflt, :ifne].member? symbol
295
289
  end
296
- end
297
290
 
291
+ #
292
+ # Private methods
293
+ #
294
+ private
295
+
296
+ # Parses a conditional statement
297
+ def parse_conditional1 condition, operands
298
+ # Parse first clause and condition for next clause
299
+ consequent, next_condition = split_if_clause parse_body(:conditional)
300
+ if next_condition == nil
301
+ # No next clause
302
+ alternative = []
303
+ elsif next_condition == :else
304
+ # Next clause is else without if
305
+ alternative = parse_body :conditional
306
+ else
307
+ # Next clause is else with if
308
+ alternative = [parse_conditional1(next_condition[0],
309
+ next_condition[1])]
310
+ end
311
+ [condition, operands, consequent, alternative]
312
+ end
313
+
314
+ # Splits a parsed if-clause into two parts:
315
+ # 1. The list of statements making up the clause proper
316
+ # 2. The condition for the next clause:
317
+ # - If there is no next clause, nil
318
+ # - If the next clause is introduced by else without a condition, :else
319
+ # - If the next clause is introduced by else iflt x y, [:iflt [:x, :y]]
320
+ # - And so on for other if.. instances
321
+ def split_if_clause clause
322
+ last = clause[-1]
323
+ if last.respond_to?(:[]) && last.length > 0 && last[0] == :else
324
+ clause = clause[0..-2]
325
+ if last.length > 1
326
+ # Else if
327
+ [clause, [last[1], last[2..-1]]]
328
+ else
329
+ # Only else
330
+ [clause, :else]
331
+ end
332
+ else
333
+ # No else
334
+ [clause, nil]
335
+ end
336
+ end
337
+
338
+ end
298
339
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: voodoo
3
3
  version: !ruby/object:Gem::Version
4
- hash: 1
4
+ hash: 3
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 6
9
- - 3
10
- version: 0.6.3
8
+ - 7
9
+ - 0
10
+ version: 0.7.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Robbert Haarman
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-11-21 00:00:00 +01:00
18
+ date: 2011-01-15 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies: []
21
21