voodoo 0.6.3 → 0.7.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.
@@ -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