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.
@@ -12,15 +12,16 @@ module Voodoo
12
12
  #
13
13
  # To use the functionality from this class, a subclass must define the
14
14
  # following methods and constants:
15
- # - #emit_function_prologue
16
- # - #load_arg
17
- # - #load_local
15
+ # - #begin_function
18
16
  # - @CODE_ALIGNMENT
19
17
  # - @DATA_ALIGNMENT
20
18
  # - @FUNCTION_ALIGNMENT
21
- # - @SCRATCH_REG
19
+ # - @STACK_ALIGNMENT
20
+ # - @STACK_ALIGNMENT_BITS
21
+ # - @TEMPORARIES
22
22
  # - @WORD_NAME
23
23
  # - @WORDSIZE
24
+ # - @WORDSIZE_BITS
24
25
  # - @AX, @BX, @CX, @DX, @BP, and @SP
25
26
  class NasmGenerator < CommonCodeGenerator
26
27
  def initialize params = {}
@@ -35,26 +36,44 @@ module Voodoo
35
36
 
36
37
  # Export symbols from the current section
37
38
  def export *symbols
38
- emit "global #{symbols.join ', '}\n"
39
+ case real_section_name(section)
40
+ when ".text"
41
+ symbols.each { |sym| emit "global #{sym}:function\n" }
42
+ else
43
+ @relocated_symbols.merge symbols
44
+ symbols.each { |sym| emit "global #{sym}:data #{sym}.end-#{sym}\n" }
45
+ end
39
46
  end
40
47
 
41
48
  # Continue execution at the given address
42
49
  def goto value
43
- emit "; goto #{value}\n"
44
- value_ref = load_value value, @SCRATCH_REG
45
- emit "jmp #{value_ref}\n"
50
+ with_temporary do |temporary|
51
+ value_ref = load_value value, temporary
52
+ emit "jmp #{value_ref}\n"
53
+ end
46
54
  end
47
55
 
48
56
  # Import labels into the current section
49
57
  def import *symbols
58
+ if real_section_name(section) != ".text"
59
+ @relocated_symbols.merge symbols
60
+ end
50
61
  emit "extern #{symbols.join ', '}\n"
51
62
  end
52
63
 
53
- # Define a label in the current section
54
- def label name
64
+ # Emits a label.
65
+ def emit_label name
55
66
  emit "#{name}:\n"
56
67
  end
57
68
 
69
+ def emit_label_size name
70
+ emit ".end:\n"
71
+ end
72
+
73
+ def emit_label_type name, type
74
+ # nothing to do
75
+ end
76
+
58
77
  #
59
78
  # == Data definition
60
79
  #
@@ -105,45 +124,21 @@ module Voodoo
105
124
  # == Alignment
106
125
  #
107
126
 
108
- def align alignment = nil
109
- unless alignment
110
- # Get default alignment
111
- case @section
112
- when :code
113
- alignment = @CODE_ALIGNMENT
114
- when :data
115
- alignment = @DATA_ALIGNMENT
116
- when :function
117
- alignment = @FUNCTION_ALIGNMENT
118
- else
119
- # Use data alignment as default
120
- alignment = @DATA_ALIGNMENT
121
- end
122
- end
123
- emit "align #{alignment}\n" unless alignment == 0
127
+ def emit_align alignment
128
+ emit "align #{alignment}\n"
124
129
  end
125
130
 
126
131
  #
127
132
  # == Functions
128
133
  #
129
134
 
130
- # Emit function preamble and declare _formals_ as function arguments
131
- def begin_function *formals
132
- emit "; function #{formals.join ' '}\n"
133
- environment = Environment.new @environment
134
- environment.add_args formals
135
- @environment = environment
136
- emit_function_prologue formals
137
- end
138
-
139
- # Emit function epilogue.
135
+ # Emits function epilogue.
140
136
  def emit_function_epilogue formals = []
141
137
  emit "leave\n"
142
138
  end
143
139
 
144
- # End a function body
140
+ # Ends a function body
145
141
  def end_function
146
- emit "; end function\n\n"
147
142
  if @environment == @top_level
148
143
  raise "Cannot end function when not in a function"
149
144
  else
@@ -151,12 +146,11 @@ module Voodoo
151
146
  end
152
147
  end
153
148
 
154
- # Return a from a function.
149
+ # Returns from a function.
155
150
  #
156
151
  # _words_ may contain an expression to be evaluated. The result
157
152
  # of the evaluation is returned from the function.
158
153
  def ret *words
159
- emit "; return #{words.join ' '}\n"
160
154
  eval_expr(words) unless words.empty?
161
155
  emit_function_epilogue
162
156
  emit "ret\n"
@@ -168,7 +162,6 @@ module Voodoo
168
162
 
169
163
  # Begins a new block.
170
164
  def begin_block *code
171
- emit "; begin block\n"
172
165
  # If entering a block at top level,
173
166
  # Save @BP, then set @BP to @SP
174
167
  if @environment == @top_level
@@ -181,79 +174,116 @@ module Voodoo
181
174
 
182
175
  # Ends the current block.
183
176
  def end_block
184
- emit "; end block\n"
185
-
186
- # De-allocate block's variables
187
- nvars = @environment.locals - @environment.parent.locals
188
- emit "add #{@SP}, #{nvars * @WORDSIZE}\n" unless nvars == 0
189
-
190
177
  # Restore old value of @environment
191
178
  @environment = @environment.parent
192
179
 
193
180
  # If returning to top level, restore old @BP
194
- emit "pop #{@BP}\n" if @environment == @top_level
181
+ emit "leave\n" if @environment == @top_level
195
182
  end
196
183
 
197
184
  #
198
- # == Conditionals
185
+ # == Memory Allocation
199
186
  #
200
187
 
201
- # End a conditional body
202
- def end_if
203
- label = @if_labels.pop
204
- emit "#{label}:\n"
188
+ # Allocates n bytes on the stack and stores a pointer to the allocated
189
+ # memory in the specified register. The number of bytes is rounded up
190
+ # to the nearest multiple of @STACK_ALIGNMENT.
191
+ def auto_bytes n, register = @RETURN_REG
192
+ if n.kind_of? Integer
193
+ auto_bytes_immediate n, register
194
+ else
195
+ load_value_into_register n, register
196
+ auto_bytes_register register, register
197
+ end
205
198
  end
206
199
 
207
- #
208
- # == Value Classification
209
- #
200
+ # Implements auto_bytes where the number of bytes to allocate is given
201
+ # as an immediate value.
202
+ def auto_bytes_immediate nbytes, register
203
+ nbytes = ((nbytes + @STACK_ALIGNMENT - 1) >> @STACK_ALIGNMENT_BITS) <<
204
+ @STACK_ALIGNMENT_BITS
205
+ emit "sub #{@SP}, #{nbytes}\n"
206
+ emit "mov #{register}, #{@SP}\n" if register != @SP
207
+ end
210
208
 
211
- # Test if a value is an at-expression
212
- def at_expr? value
213
- value.respond_to?(:[]) && value[0] == :'@'
209
+ # Implements auto_bytes where the number of bytes is supplied in a
210
+ # register.
211
+ def auto_bytes_register nbytes, register = @RETURN_REG
212
+ emit "add #{nbytes}, #{@STACK_ALIGNMENT - 1}\n"
213
+ emit "shr #{nbytes}, #{@STACK_ALIGNMENT_BITS}\n"
214
+ emit "shl #{nbytes}, #{@STACK_ALIGNMENT_BITS}\n"
215
+ emit "sub #{@SP}, #{nbytes}\n"
216
+ emit "mov #{register}, #{@SP}\n" if register != @SP
214
217
  end
215
218
 
216
- # Test if op is a binary operation
217
- def binop? op
218
- [:asr, :bsr, :div, :mod, :rol, :ror, :shl, :shr, :sub].member?(op) ||
219
- symmetric_operation?(op)
219
+ # Allocates n words on the stack and stores a pointer to the allocated
220
+ # memory in the specified register.
221
+ def auto_words n, register = @RETURN_REG
222
+ if n.kind_of? Integer
223
+ auto_bytes_immediate n * @WORDSIZE, register
224
+ else
225
+ load_value_into_register n, register
226
+ if @STACK_ALIGNMENT_BITS > @WORDSIZE_BITS
227
+ emit "add #{register}, " +
228
+ "#{(1 << @STACK_ALIGNMENT_BITS >> @WORDSIZE_BITS) - 1}\n"
229
+ emit "shr #{register}, #{@STACK_ALIGNMENT_BITS - @WORDSIZE_BITS}\n"
230
+ emit "shl #{register}, #{STACK_ALIGNMENT_BITS}\n"
231
+ else
232
+ emit "shl #{register}, #{@WORDSIZE_BITS}\n"
233
+ end
234
+ emit "sub #{@SP}, #{register}\n"
235
+ emit "mov #{register}, #{@SP}\n" if register != @SP
236
+ end
220
237
  end
221
238
 
222
- # Test if a value is an integer
223
- def integer? value
224
- value.kind_of? Integer
239
+ #
240
+ # == Variables
241
+ #
242
+
243
+ # Introduces a new local variable.
244
+ def let symbol, *words
245
+ loc = local_offset_or_register @environment.locals
246
+ @environment.add_local symbol, loc
247
+ set symbol, *words
225
248
  end
226
249
 
227
- # Test if a symbol refers to a global
228
- def global? symbol
229
- symbol?(symbol) && @environment[symbol] == nil
250
+ #
251
+ # == Conditionals
252
+ #
253
+
254
+ # End a conditional body
255
+ def end_if
256
+ label = @if_labels.pop
257
+ emit "#{label}:\n"
230
258
  end
231
259
 
260
+ #
261
+ # == Value Classification
262
+ #
263
+
232
264
  # Tests if an operand is an immediate operand
233
265
  def immediate_operand? operand
234
- integer?(operand) || global?(operand)
266
+ integer?(operand)
235
267
  end
236
268
 
237
269
  # Tests if an operand is a memory operand
238
270
  def memory_operand? operand
239
- operand[0] == ?[
240
- end
241
-
242
- # Test if a value is a symbol
243
- def symbol? value
244
- value.kind_of? Symbol
245
- end
246
-
247
- # Test if op is a symmetric operation (i.e. it will yield the
248
- # same result if the order of its source operands is changed).
249
- def symmetric_operation? op
250
- [:add, :and, :mul, :or, :xor].member? op
271
+ operand.kind_of?(String) && operand[0] == ?[
251
272
  end
252
273
 
253
274
  #
254
275
  # == Loading Values
255
276
  #
256
277
 
278
+ # Loads a word into a register.
279
+ def emit_load_word register, base, offset = 0
280
+ if offset == 0
281
+ emit "mov #{register}, [#{base}]\n"
282
+ else
283
+ emit "mov #{register}, [#{base} + #{offset} * #{@WORDSIZE}]\n"
284
+ end
285
+ end
286
+
257
287
  # Create a value reference to an address.
258
288
  # Invoking this code may clobber @BX and/or @CX
259
289
  def load_address base, offset, scale
@@ -302,8 +332,9 @@ module Voodoo
302
332
 
303
333
  # Load the value at the given address.
304
334
  # Invoking this code may clobber @BX.
305
- def load_at address, reg = @SCRATCH_REG
306
- if integer?(address) || global?(address)
335
+ def load_at address, reg
336
+ if integer?(address) || (global?(address) &&
337
+ !@relocated_symbols.include?(address))
307
338
  "[#{address}]"
308
339
  else
309
340
  load_value_into_register address, @BX
@@ -311,31 +342,41 @@ module Voodoo
311
342
  end
312
343
  end
313
344
 
314
- # Load the value associated with the given symbol.
315
- # Returns a string that can be used to refer to the loaded value.
316
- def load_symbol symbol, reg = @SCRATCH_REG
345
+ # Loads the value associated with the given symbol.
346
+ def load_symbol symbol, reg
317
347
  x = @environment[symbol]
318
- if x
319
- case x[0]
320
- when :arg
321
- load_arg x[1], reg
322
- when :local
323
- load_local x[1], reg
324
- else
325
- raise "Invalid variable type: #{x[0]}"
326
- end
348
+ if x.kind_of? Symbol
349
+ x
350
+ elsif x.kind_of? Integer
351
+ offset_reference x
327
352
  else
328
353
  # Assume global
329
- symbol.to_s
354
+ if @relocated_symbols.include? symbol
355
+ load_symbol_from_got symbol, reg
356
+ else
357
+ symbol
358
+ end
330
359
  end
331
360
  end
332
361
 
333
- # Load a value.
362
+ # Loads a value.
334
363
  # Returns a string that can be used to refer to the loaded value.
335
- def load_value value, reg = @SCRATCH_REG
364
+ def load_value value, reg
365
+ if substitution? value
366
+ value = substitute_number value[1]
367
+ end
368
+
336
369
  if integer? value
337
- # Integers can be used as is
338
- value
370
+ if @WORDSIZE > 4 && (value < -2147483648 || value > 2147483647)
371
+ # AMD64 can load immediate values that are outside the range
372
+ # that can be represented as a 32-bit signed integer, but
373
+ # only with a mov instruction that loads the value into a
374
+ # register.
375
+ emit "mov #{@WORD_NAME} #{reg}, #{value}\n"
376
+ reg
377
+ else
378
+ value
379
+ end
339
380
  elsif symbol? value
340
381
  load_symbol value, reg
341
382
  elsif at_expr? value
@@ -345,20 +386,25 @@ module Voodoo
345
386
  end
346
387
  end
347
388
 
348
- # Load a value into a register.
389
+ # Loads a value into a register.
349
390
  def load_value_into_register value, register
350
- load_code = load_value(value), register
351
- if load_code == register.to_s
352
- load_code
353
- else
354
- "mov #{register}, #{load_code}\n"
355
- end
391
+ value_ref = load_value value, register
392
+ set_register register, value_ref
356
393
  end
357
394
 
358
395
  #
359
396
  # == Storing Values
360
397
  #
361
398
 
399
+ # Stores the value of a register in memory.
400
+ def emit_store_word register, base, offset = 0
401
+ if offset == 0
402
+ emit "mov [#{base}], #{register}\n"
403
+ else
404
+ emit "mov [#{base} + #{offset} * #{@WORDSIZE}], #{register}\n"
405
+ end
406
+ end
407
+
362
408
  # Evaluate the expr in words and store the result in target
363
409
  def set target, *words
364
410
  if integer? target
@@ -367,35 +413,10 @@ module Voodoo
367
413
  raise "Cannot change value of global #{target}"
368
414
  end
369
415
 
370
- emit "; set #{target} #{words.join ' '}\n"
371
- if words.length == 1
372
- if words[0] == target
373
- emit "; nothing to do; destination equals source\n"
374
- else
375
- target_ref = load_value target, @BX
376
- if integer?(words[0])
377
- if words[0].to_i == 0
378
- # Set destination to 0
379
- emit "xor #{@AX}, #{@AX}\n"
380
- emit "mov #{target_ref}, #{@AX}\n"
381
- else
382
- # Load immediate
383
- emit "mov #{@WORD_NAME} #{target_ref}, #{words[0]}\n"
384
- end
385
- else
386
- # Copy source to destination
387
- eval_expr words, @RETURN_REG
388
- emit "mov #{target_ref}, #{@RETURN_REG}\n"
389
- end
390
- end
391
- else
392
- op = words[0]
393
-
394
- if words.length == 3 && binop?(op)
395
- # Binary operation
396
- binop op, target, words[1], words[2]
416
+ if words.length != 1 || words[0] != target
417
+ if symbol?(target) && symbol?(@environment[target])
418
+ eval_expr words, @environment[target]
397
419
  else
398
- # Not a binary operation
399
420
  eval_expr words, @RETURN_REG
400
421
  target_ref = load_value target, @BX
401
422
  emit "mov #{target_ref}, #{@RETURN_REG}\n"
@@ -405,12 +426,11 @@ module Voodoo
405
426
 
406
427
  # Set the byte at _base_ + _offset_ to _value_
407
428
  def set_byte base, offset, value
408
- emit "; set-byte #{base} #{offset} #{value}\n"
409
429
  if immediate_operand?(value)
410
430
  value_ref = value
411
431
  else
412
432
  load_value_into_register value, @RETURN_REG
413
- value_ref = "al"
433
+ value_ref = :al
414
434
  end
415
435
  addr_ref = load_address base, offset, 1
416
436
  emit "mov byte #{addr_ref}, #{value_ref}\n"
@@ -418,9 +438,8 @@ module Voodoo
418
438
 
419
439
  # Set the word at _base_ + _offset_ * +@WORDSIZE+ to _value_
420
440
  def set_word base, offset, value
421
- emit "; set-word #{base} #{offset} #{value}\n"
422
441
  if immediate_operand?(value)
423
- value_ref = value
442
+ value_ref = load_value value, @RETURN_REG
424
443
  else
425
444
  load_value_into_register value, @RETURN_REG
426
445
  value_ref = @RETURN_REG
@@ -429,69 +448,6 @@ module Voodoo
429
448
  emit "mov #{@WORD_NAME} #{addr_ref}, #{value_ref}\n"
430
449
  end
431
450
 
432
- #
433
- # == Binary Operations
434
- #
435
-
436
- # Emit code for a binary operation
437
- def binop op, target, x, y
438
- if target == x
439
- binop2 op, target, y
440
- elsif symmetric_operation?(op) && y == target
441
- binop2 op, target, x
442
- else
443
- # Cases that are handled specially
444
- return div(target, x, y) if op == :div
445
- return mod(target, x, y) if op == :mod
446
- return mul(target, x, y) if op == :mul
447
-
448
- target_ref = load_value target, @BX
449
- x_ref = load_value x, @DX
450
- y_ref = load_value y, @CX
451
-
452
- mnemonic = action_to_mnemonic op
453
- if [:asr, :bsr, :rol, :ror, :shl, :shr].member? op
454
- emit "mov cl, #{y_ref}\n" unless y_ref == @CX
455
- y_ref = 'cl'
456
- end
457
-
458
- if memory_operand?(target_ref)
459
- if memory_operand?(x_ref) || memory_operand?(y_ref)
460
- emit "mov #{@AX}, #{x_ref}\n"
461
- emit "#{mnemonic} #{@AX}, #{y_ref}\n"
462
- emit "mov #{target_ref}, #{@AX}\n"
463
- else
464
- emit "mov #{@WORD_NAME} #{target_ref}, #{x_ref}\n"
465
- emit "#{mnemonic} #{@WORD_NAME} #{target_ref}, #{y_ref}\n"
466
- end
467
- else
468
- raise "Can't happen: target_ref is #{target_ref.inspect}"
469
- end
470
- end
471
- end
472
-
473
- # Emit code for a binary operation where the first operand
474
- # is also the target
475
- def binop2 op, target, y
476
- # Cases that are handled specially
477
- return div2(target, target, y) if op == :div
478
- return mod2(target, y) if op == :mod
479
- return mul2(target, y) if op == :mul
480
-
481
- target_ref = load_value target, @AX
482
- y_ref = load_value y, @CX
483
- mnemonic = action_to_mnemonic op
484
- if [:asr, :bsr, :rol, :ror, :shl, :shr].member? op
485
- emit "mov cl, #{y_ref}\n" unless y_ref == @CX
486
- emit "#{mnemonic} #{@WORD_NAME} #{target_ref}, cl\n"
487
- elsif memory_operand?(target_ref) && memory_operand?(y_ref)
488
- emit "mov #{@CX}, #{y_ref}\n"
489
- emit "#{mnemonic} #{target_ref}, #{@CX}\n"
490
- else
491
- emit "#{mnemonic} #{@WORD_NAME} #{target_ref}, #{y_ref}\n"
492
- end
493
- end
494
-
495
451
  # Divide x by y and store the quotient in target
496
452
  def div target, x, y
497
453
  eval_div x, y
@@ -535,15 +491,17 @@ module Voodoo
535
491
  # Perform division.
536
492
  # The quotient is stored in @AX, the remainder in @DX.
537
493
  def eval_div x, y
538
- x_ref = load_value_into_register x, @AX
539
- y_ref = load_value y, @SCRATCH_REG
540
- emit "mov #{@DX}, #{@AX}\n"
541
- emit "sar #{@DX}, #{@WORDSIZE * 8 - 1}\n"
542
- if immediate_operand?(y_ref)
543
- set_register @BX, y_ref
544
- emit "idiv #{@BX}\n"
545
- else
546
- emit "idiv #{@WORD_NAME} #{y_ref}\n"
494
+ with_temporary do |temporary|
495
+ x_ref = load_value_into_register x, @AX
496
+ y_ref = load_value y, temporary
497
+ emit "mov #{@DX}, #{@AX}\n"
498
+ emit "sar #{@DX}, #{@WORDSIZE * 8 - 1}\n"
499
+ if immediate_operand?(y_ref)
500
+ set_register @BX, y_ref
501
+ emit "idiv #{@BX}\n"
502
+ else
503
+ emit "idiv #{@WORD_NAME} #{y_ref}\n"
504
+ end
547
505
  end
548
506
  end
549
507
 
@@ -561,33 +519,45 @@ module Voodoo
561
519
  op = words[0]
562
520
  case op
563
521
  when :asr, :bsr, :rol, :ror, :shl, :shr
564
- load_value_into_register words[2], @CX
522
+ if integer? words[2]
523
+ y_ref = words[2]
524
+ else
525
+ load_value_into_register words[2], @CX
526
+ y_ref = :cl
527
+ end
565
528
  load_value_into_register words[1], register
566
- emit "#{action_to_mnemonic op} #{register}, cl\n"
529
+ emit "#{action_to_mnemonic op} #{register}, #{y_ref}\n"
530
+ when :'auto-bytes'
531
+ auto_bytes words[1], register
532
+ when :'auto-words'
533
+ auto_words words[1], register
567
534
  when :call
568
535
  call *words[1..-1]
536
+ emit "mov #{register}, #{@RETURN_REG}\n" if register != @RETURN_REG
569
537
  when :div
570
538
  eval_div words[1], words[2]
571
539
  set_register register, @AX
572
540
  when :'get-byte'
573
- # Clear register
574
- set_register register, 0
575
541
  # Get address reference
576
542
  address_ref = load_address words[1], words[2], 1
577
543
  # Load byte from address
578
544
  case register
579
- when 'eax', 'rax'
580
- set_register 'al', address_ref
581
- when 'ebx', 'rbx'
582
- set_register 'bl', address_ref
583
- when 'ecx', 'rcx'
584
- set_register 'cl', address_ref
585
- when 'edx', 'rdx'
586
- set_register 'dl', address_ref
545
+ when :eax, :rax
546
+ set_register register, 0
547
+ set_register :al, address_ref
548
+ when :ebx, :rbx
549
+ set_register register, 0
550
+ set_register :bl, address_ref
551
+ when :ecx, :rcx
552
+ set_register register, 0
553
+ set_register :cl, address_ref
554
+ when :edx, :rdx
555
+ set_register register, 0
556
+ set_register :dl, address_ref
587
557
  else
588
- set_register @BX, 0
589
- set_register 'bl', address_ref
590
- set_register register, @BX
558
+ set_register @AX, 0
559
+ set_register :al, address_ref
560
+ set_register register, @AX
591
561
  end
592
562
  when :'get-word'
593
563
  address_ref = load_address words[1], words[2], @WORDSIZE
@@ -604,7 +574,7 @@ module Voodoo
604
574
  if binop?(op)
605
575
  x_ref = load_value words[1], @DX
606
576
  y_ref = load_value words[2], @BX
607
- emit "mov #{register}, #{x_ref}\n"
577
+ emit "mov #{register}, #{x_ref}\n" unless register == x_ref
608
578
  emit "#{op} #{register}, #{y_ref}\n"
609
579
  else
610
580
  raise "Not a magic word: #{words[0]}"
@@ -629,9 +599,11 @@ module Voodoo
629
599
  end
630
600
  elsif immediate_operand? y_ref
631
601
  emit "imul #{register}, #{x_ref}, #{y_ref}\n"
632
- else
633
- emit "mov #{register}, #{x_ref}\n"
602
+ elsif y_ref != register
603
+ emit "mov #{register}, #{x_ref}\n" unless x_ref == register
634
604
  emit "imul #{register}, #{y_ref}\n"
605
+ else
606
+ emit "imul #{register}, #{x_ref}\n"
635
607
  end
636
608
  end
637
609
 
@@ -642,13 +614,44 @@ module Voodoo
642
614
  # Start a conditional using the specified branch instruction
643
615
  # after the comparison.
644
616
  def common_if branch, x, y = nil
645
- load_value_into_register y, @DX if y
646
- load_value_into_register x, @AX
617
+ # Inverses of branches. E.g.
618
+ # cmp x, y
619
+ # jle somewhere
620
+ # is the same as
621
+ # cmp y, x
622
+ # jgt somewhere
623
+ inverse_branch = {
624
+ :je => :je,
625
+ :jge => :jl,
626
+ :jg => :jle,
627
+ :jl => :jge,
628
+ :jle => :jg,
629
+ :jne => :jne,
630
+ }
631
+
632
+ y_ref = load_value y, @DX
633
+ x_ref = load_value x, @AX
634
+ if immediate_operand?(x_ref)
635
+ # Can't have an immediate value as the first operand.
636
+ if immediate_operand?(y_ref)
637
+ # Both are immediates. Put the first in a register.
638
+ emit "mov #{@AX}, #{x_ref}\n"
639
+ x_ref = @AX
640
+ else
641
+ # y isn't immediate; swap x and y.
642
+ x_ref, y_ref = [y_ref, x_ref]
643
+ branch = inverse_branch[branch]
644
+ end
645
+ elsif memory_operand?(x_ref) && memory_operand?(y_ref)
646
+ # Can't have two memory operands. Move the first into a register.
647
+ emit "mov #{@AX}, #{x_ref}\n"
648
+ x_ref = @AX
649
+ end
647
650
  truelabel = @environment.gensym
648
651
  falselabel = @environment.gensym
649
652
  @if_labels.push falselabel
650
653
 
651
- emit "cmp #{@AX}, #{@DX}\n"
654
+ emit "cmp #{@WORD_NAME} #{x_ref}, #{y_ref}\n"
652
655
  emit "#{branch} #{truelabel}\n"
653
656
  emit "jmp #{falselabel}\n"
654
657
  emit "#{truelabel}:\n"
@@ -662,7 +665,6 @@ module Voodoo
662
665
 
663
666
  # Start the false path of a conditional.
664
667
  def ifelse
665
- emit "; else\n"
666
668
  newlabel = @environment.gensym
667
669
  emit "jmp #{newlabel}\n"
668
670
  label = @if_labels.pop
@@ -672,38 +674,32 @@ module Voodoo
672
674
 
673
675
  # Test if x is equal to y
674
676
  def ifeq x, y
675
- emit "; ifeq #{x} #{y}\n"
676
- common_if 'je', x, y
677
+ common_if :je, x, y
677
678
  end
678
679
 
679
680
  # Test if x is greater than or equal to y
680
681
  def ifge x, y
681
- emit "; ifge #{x} #{y}\n"
682
- common_if 'jge', x, y
682
+ common_if :jge, x, y
683
683
  end
684
684
 
685
685
  # Test if x is strictly greater than y
686
686
  def ifgt x, y
687
- emit "; ifgt #{x} #{y}\n"
688
- common_if 'jg', x, y
687
+ common_if :jg, x, y
689
688
  end
690
689
 
691
690
  # Test if x is less than or equal to y
692
691
  def ifle x, y
693
- emit "; ifle #{x} #{y}\n"
694
- common_if 'jle', x, y
692
+ common_if :jle, x, y
695
693
  end
696
694
 
697
695
  # Test if x is strictly less than y
698
696
  def iflt x, y
699
- emit "; iflt #{x} #{y}\n"
700
- common_if 'jl', x, y
697
+ common_if :jl, x, y
701
698
  end
702
699
 
703
700
  # Test if x different from y
704
701
  def ifne x, y
705
- emit "; ifne #{x} #{y}\n"
706
- common_if 'jne', x, y
702
+ common_if :jne, x, y
707
703
  end
708
704
 
709
705
  #
@@ -724,13 +720,19 @@ module Voodoo
724
720
 
725
721
  # Emit a comment
726
722
  def comment text
727
- emit ";#{text}\n"
723
+ emit "; #{text}\n"
728
724
  end
729
725
 
730
- # Load a value into a register
731
- def load_value_into_register value, register
732
- value_ref = load_value value, register
733
- set_register register, value_ref
726
+ # Returns a memory reference for the address at the given offset
727
+ # from the frame pointer.
728
+ def offset_reference offset
729
+ if offset > 0
730
+ "[#{@BP} + #{offset}]"
731
+ elsif offset < 0
732
+ "[#{@BP} - #{-offset}]"
733
+ else
734
+ "[#{@BP}]"
735
+ end
734
736
  end
735
737
 
736
738
  # Set a register to a value.