rpiet 0.1 → 0.2

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.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/Gemfile +12 -0
  4. data/bin/color_wheel +84 -0
  5. data/bin/image_gen +39 -0
  6. data/bin/rpiet +68 -11
  7. data/images/counter.txt +7 -0
  8. data/lib/rpiet/asg/graph_interpreter.rb +39 -0
  9. data/lib/rpiet/asg/parser.rb +156 -0
  10. data/lib/rpiet/asg/visitor.rb +66 -0
  11. data/lib/rpiet/asg.rb +336 -0
  12. data/lib/rpiet/codel_chooser.rb +32 -4
  13. data/lib/rpiet/color.rb +70 -25
  14. data/lib/rpiet/cycle.rb +18 -7
  15. data/lib/rpiet/debugger/debugger.rb +298 -0
  16. data/lib/rpiet/debugger/stylesheet.css +88 -0
  17. data/lib/rpiet/direction_pointer.rb +49 -18
  18. data/lib/rpiet/event_handler.rb +62 -7
  19. data/lib/rpiet/group.rb +25 -8
  20. data/lib/rpiet/image/ascii_image.rb +8 -20
  21. data/lib/rpiet/image/image.rb +8 -3
  22. data/lib/rpiet/image/url_image.rb +28 -14
  23. data/lib/rpiet/interpreter.rb +72 -72
  24. data/lib/rpiet/ir/assembler.rb +87 -0
  25. data/lib/rpiet/ir/builder.rb +255 -0
  26. data/lib/rpiet/ir/cfg.rb +494 -0
  27. data/lib/rpiet/ir/instructions.rb +536 -0
  28. data/lib/rpiet/ir/ir_cfg_interpreter.rb +23 -0
  29. data/lib/rpiet/ir/ir_interpreter.rb +101 -0
  30. data/lib/rpiet/ir/ir_native_interpreter.rb +77 -0
  31. data/lib/rpiet/ir/jruby_backend.rb +279 -0
  32. data/lib/rpiet/ir/operands.rb +28 -0
  33. data/lib/rpiet/ir/passes/data_flow_problem.rb +32 -0
  34. data/lib/rpiet/ir/passes/flow_graph_node.rb +76 -0
  35. data/lib/rpiet/ir/passes/peephole.rb +214 -0
  36. data/lib/rpiet/ir/passes/push_pop_elimination_pass.rb +112 -0
  37. data/lib/rpiet/live_machine_state.rb +15 -0
  38. data/lib/rpiet/machine.rb +62 -32
  39. data/lib/rpiet/source.rb +83 -0
  40. data/lib/rpiet/version.rb +1 -1
  41. data/lib/rpiet.rb +2 -2
  42. data/rpiet.gemspec +19 -0
  43. data/spec/asg/visitor_spec.rb +41 -0
  44. data/spec/cycle_spec.rb +34 -34
  45. data/spec/direction_pointer_spec.rb +33 -6
  46. data/spec/group_spec.rb +73 -48
  47. data/spec/interpreter_spec.rb +161 -12
  48. data/spec/ir/assembler_spec.rb +122 -0
  49. data/spec/ir/builder_spec.rb +20 -0
  50. data/spec/ir/cfg_spec.rb +151 -0
  51. data/spec/ir/ir_interpreter_spec.rb +102 -0
  52. data/spec/ir/passes/push_pop_elimination_pass_spec.rb +34 -0
  53. data/spec/machine_spec.rb +5 -3
  54. data/spec/source_spec.rb +69 -0
  55. data/spec/spec_helper.rb +78 -0
  56. metadata +54 -16
  57. data/images/nfib.png +0 -0
@@ -0,0 +1,536 @@
1
+ module RPiet
2
+ module IR
3
+ module Instructions
4
+ class Instr
5
+ # for debugging so we can print out the node to compare against the graph interpreter
6
+ attr_accessor :graph_node, :comment
7
+ attr_reader :operands
8
+
9
+ def operation = self.class.operation_name.to_sym
10
+ alias :type :operation
11
+
12
+ def initialize(*operands)
13
+ @operands = operands
14
+ end
15
+
16
+ def constant? = false
17
+
18
+ def disasm = "#{operation}!!!!"
19
+
20
+ def execute(machine) = raise ArgumentError.new "Cannot execute a base class"
21
+
22
+ def jump? = false
23
+
24
+ def noop? = self.kind_of?(NoopInstr)
25
+
26
+ def to_ruby(cfg, bb) = raise ArgumentError.new "missing to_ruby impl"
27
+ def to_s = operation.to_s
28
+ def to_s_comment = comment ? " # #{comment}" : ""
29
+
30
+ def self.operation_name = name.sub(/.*::/, '').sub('Instr', '').downcase
31
+ def step = graph_node.step
32
+
33
+ def decode(machine, operand)
34
+ case operand
35
+ when Operands::Poperand then machine.stack.pop
36
+ when Operands::VariableOperand then operand.value
37
+ else operand
38
+ end
39
+ end
40
+
41
+ def disasm_operand(operand)
42
+ operand = operand.name if operand.kind_of?(Operands::VariableOperand)
43
+ operand.to_s
44
+ end
45
+ def ruby_operand(operand)
46
+ case operand
47
+ when Operands::Poperand then "@stack.pop"
48
+ when Operands::VariableOperand then operand.name
49
+ when Symbol then ":" + operand
50
+ when Integer then operand
51
+ when String then "'#{operand}'"
52
+ when DirectionPointer then "DirectionPointer.from_ordinal(#{operand})"
53
+ else operand
54
+ end
55
+ end
56
+
57
+ def ruby_indent(string, indent=' ')
58
+ string.split("\n").map {|line| indent + line + "\n"}.join('')
59
+ end
60
+ end
61
+
62
+ module ResultInstr
63
+ attr_reader :result
64
+
65
+ def initialize(result, *operands)
66
+ super(*operands)
67
+ @result = result
68
+ end
69
+
70
+ def disasm = "#{result.name} = #{operation}"
71
+
72
+ def to_s = "#{result} = #{operation}"
73
+ end
74
+
75
+ module TwoOperands
76
+ def operand1 = @operands[0]
77
+ def operand2 = @operands[1]
78
+
79
+ def constant?
80
+ operand1.kind_of?(Integer) && operand2.kind_of?(Integer)
81
+ end
82
+
83
+ def stack_independent?
84
+ (operand1.kind_of?(Integer) || operand1.kind_of?(Operands::VariableOperand)) ||
85
+ (operand2.kind_of?(Integer) || operand2.kind_of?(Operands::VariableOperand))
86
+ end
87
+
88
+ def ruby_assign_2
89
+ # if operand1.kind_of?(Operands::Poperand) && operand2.kind_of?(Operands::Poperand)
90
+ # "b, a = @stack.pop(2)"
91
+ #else
92
+ "b, a = #{ruby_operand(operand2)}, #{ruby_operand(operand1)}"
93
+ #end
94
+ end
95
+ end
96
+
97
+ class NoopInstr < Instr
98
+ def initialize
99
+ super
100
+ end
101
+
102
+ def disasm = operation
103
+ def execute(machine) = nil
104
+ def to_ruby(cfg, bb) = "# noop\n"
105
+ def to_s = "noop"
106
+ end
107
+
108
+ class ExitInstr < Instr
109
+ def execute(machine) = :exit
110
+ def jump? = true
111
+ def to_ruby(cfg, bb) = "return :exit\n"
112
+ def to_s = "exit"
113
+ end
114
+
115
+ class SingleOperandInstr < Instr
116
+ def initialize(operand) = super(operand)
117
+
118
+ def disasm = "#{operation} #{disasm_operand(operand)}"
119
+ def operand = @operands[0]
120
+
121
+ def to_s = "#{operation} #{operand}"
122
+ end
123
+
124
+ class MathInstr < Instr
125
+ include TwoOperands
126
+ attr_reader :oper, :result
127
+
128
+ def initialize(oper, result, operand1, operand2)
129
+ raise ArgumentError.new("must be numeric/variable operand. Got: #{operand1}") unless mathy?(operand1)
130
+ raise ArgumentError.new("must be numeric/variable operand. Got: #{operand2}") unless mathy?(operand2)
131
+ super(operand1, operand2)
132
+ @oper, @result = oper, result
133
+ end
134
+
135
+ def disasm = "#{result.name} = #{disasm_operand(operand1)} #{@oper} #{disasm_operand(operand2)}"
136
+
137
+ def execute(machine)
138
+ value2, value1 = decode(machine, operand2), decode(machine, operand1)
139
+ result.value = value1.send(oper, value2)
140
+ nil
141
+ end
142
+
143
+ def mathy?(operand)
144
+ operand.kind_of?(Integer) || operand.kind_of?(Operands::VariableOperand)
145
+ end
146
+
147
+ def to_ruby(cfg, bb)
148
+ if stack_independent?
149
+ " #{ruby_operand(result)} = #{ruby_operand(operand1)} #{oper} #{ruby_operand(operand2)}\n"
150
+ else
151
+ ruby_indent <<~"EOS"
152
+ #{ruby_assign_2}
153
+ #{ruby_operand(result)} = a #{oper} b
154
+ EOS
155
+ end
156
+ end
157
+
158
+ def to_s = "#{result} = #{operand1} #{oper} #{operand2}"
159
+ end
160
+
161
+ class AddInstr < MathInstr
162
+ def initialize(result, operand1, operand2) = super(:+, result, operand1, operand2)
163
+ end
164
+
165
+ class SubInstr < MathInstr
166
+ def initialize(result, operand1, operand2) = super(:-, result, operand1, operand2)
167
+ end
168
+
169
+ class MultInstr < MathInstr
170
+ def initialize(result, operand1, operand2) = super(:*, result, operand1, operand2)
171
+ end
172
+
173
+ class DivInstr < MathInstr
174
+ def initialize(result, operand1, operand2) = super(:/, result, operand1, operand2)
175
+
176
+ def execute(machine)
177
+ b, a = decode(machine, operand2), decode(machine, operand1)
178
+ result.value = b == 0 ? DIV_BY_ZERO_VALUE : a / b
179
+ nil
180
+ end
181
+ end
182
+
183
+ class ModInstr < MathInstr
184
+ def initialize(result, operand1, operand2) = super(:%, result, operand1, operand2)
185
+ end
186
+
187
+ class PowInstr < MathInstr
188
+ def initialize(result, operand1, operand2) = super(:**, result, operand1, operand2)
189
+ end
190
+
191
+ class CopyInstr < Instr
192
+ include ResultInstr
193
+
194
+ def operand = @operands[0]
195
+
196
+ def disasm = super + " #{disasm_operand(operand)}"
197
+
198
+ def execute(machine)
199
+ result.value = decode(machine, operand)
200
+ nil
201
+ end
202
+
203
+ def to_ruby(cfg, bb) = "#{ruby_operand(result)} = #{ruby_operand(operand)}\n"
204
+ def to_s = super + " #{operand}#{comment ? %Q{ # #{comment}} : ""}"
205
+ end
206
+
207
+ class LabelInstr < NoopInstr
208
+ attr_reader :value
209
+
210
+ def initialize(value)
211
+ super()
212
+ raise ArgumentError.new "label instr must have a label operand. Got: #{value}" unless value.kind_of?(Symbol)
213
+ @value = value
214
+ end
215
+
216
+ def disasm = "#{operation} #{disasm_operand(value)}"
217
+
218
+ def operand = @value
219
+
220
+ def to_ruby(cfg, bb) = nil
221
+ def to_s = "#{operation}(#{value})"
222
+ end
223
+
224
+ # input/output instructions
225
+ class NoutInstr < SingleOperandInstr
226
+ def execute(machine) = print decode(machine, operand)
227
+ def to_ruby(cfg, bb) = " print #{ruby_operand(operand)}\n"
228
+ end
229
+
230
+ class CoutInstr < SingleOperandInstr
231
+ def execute(machine) = print decode(machine, operand).chr
232
+ def to_ruby(cfg, bb) = " print #{ruby_operand(operand)}.chr\n"
233
+ end
234
+
235
+ class NinInstr < Instr
236
+ include ResultInstr
237
+
238
+ def execute(machine)
239
+ machine.output.print "Enter an integer: "
240
+ result.value = machine.input.gets.to_i
241
+ machine.output.puts
242
+ nil
243
+ end
244
+ def to_ruby(cfg, bb) = ruby_indent <<~"EOS"
245
+ output.print "Enter an integer: "
246
+ #{ruby_operand(result)} = input.gets.to_i
247
+ output.puts
248
+ EOS
249
+ end
250
+
251
+ class CinInstr < Instr
252
+ include ResultInstr
253
+
254
+ def execute(machine)
255
+ machine.output.print "> "
256
+ result.value = machine.input.read(1).ord
257
+ nil
258
+ end
259
+
260
+ def to_ruby(cfg, bb) = ruby_indent <<~"EOS"
261
+ output.print "> "
262
+ #{ruby_operand(result)} = input.read(1).ord
263
+ EOS
264
+ end
265
+
266
+ # instructions which manipulate the stack
267
+
268
+ class PopInstr < Instr
269
+ include ResultInstr
270
+
271
+ def execute(machine)
272
+ result.value = machine.stack.pop
273
+ nil
274
+ end
275
+
276
+ def to_ruby(cfg, bb) = " #{ruby_operand(result)} = @stack.pop\n"
277
+ end
278
+
279
+ class PushInstr < SingleOperandInstr
280
+ def execute(machine)
281
+ machine.stack.push decode(machine, operand)
282
+ nil
283
+ end
284
+ def to_ruby(cfg, bb) = " @stack.push #{ruby_operand(operand)}\n"
285
+ end
286
+
287
+ class RollInstr < Instr
288
+ include TwoOperands
289
+ def initialize(depth, num)
290
+ super
291
+ end
292
+
293
+ def execute(machine)
294
+ d, n = decode(machine, depth), decode(machine, num)
295
+ n %= d
296
+ return if d <= 0 || num == 0
297
+ stack = machine.stack
298
+ if n > 0
299
+ stack[-d..-1] = stack[-n..-1] + stack[-d...-n]
300
+ elsif n < 0
301
+ stack[-d..-1] = stack[-d...-n] + stack[-n..-1]
302
+ end
303
+ nil
304
+ end
305
+
306
+ def depth = @operands[0]
307
+ def num = @operands[1]
308
+
309
+ def disasm = "#{operation} #{disasm_operand(depth)} #{disasm_operand(num)}"
310
+
311
+ def to_ruby(cfg, bb) = ruby_indent <<~"EOS"
312
+ d, n = #{ruby_operand(depth)}, #{ruby_operand(num)}
313
+ n %= d
314
+ if d > 0 && num != 0
315
+ if n > 0
316
+ @stack[-d..-1] = @stack[-n..-1] + @stack[-d...-n]
317
+ elsif n < 0
318
+ @stack[-d..-1] = @stack[-d...-n] + @stack[-n..-1]
319
+ end
320
+ end
321
+ EOS
322
+
323
+ def to_s = "#{operation}(#{depth}, #{num})"
324
+ end
325
+
326
+ # possible jumping instructions
327
+ class JumpInstr < Instr
328
+ attr_reader :label
329
+
330
+ def initialize(label, *operands)
331
+ super(*operands)
332
+ @label = label
333
+ end
334
+
335
+ def disasm = "#{operation} #{value}"
336
+
337
+ def jump? = true
338
+
339
+ def execute(machine) = label
340
+
341
+ alias :value :label
342
+
343
+ def to_ruby(cfg, bb) = " return #{label}\n"
344
+ def to_s = "jump -> #{value}#{to_s_comment}"
345
+ end
346
+
347
+ class TwoOperandJumpInstr < JumpInstr
348
+ include TwoOperands
349
+
350
+ def initialize(operand1, operand2, label)
351
+ super(label, operand1, operand2)
352
+ end
353
+
354
+ def disasm = "#{disasm_operand(operand1)} #{doc_syntax} #{disasm_operand(operand2)} #{label}"
355
+
356
+ def to_s = "#{operand1} #{doc_syntax} #{operand2} -> #{label}"
357
+ end
358
+
359
+ class BEQInstr < TwoOperandJumpInstr
360
+ def doc_syntax = "=="
361
+ def execute(machine)
362
+ b, a = decode(machine, operand2), decode(machine, operand1)
363
+ a == b ? super : nil
364
+ end
365
+ def to_ruby(cfg, bb)
366
+ if stack_independent?
367
+ " #{ruby_operand(operand1)} == #{ruby_operand(operand2)} ? :\"#{label}\" : #{cfg.outgoing_target(bb, :fall_through)&.label}\n"
368
+ else
369
+ ruby_indent <<~"EOS"
370
+ #{ruby_assign_2}
371
+ return a == b ? :"#{label}" : :"#{cfg.outgoing_target(bb, :fall_through)&.label}"
372
+ EOS
373
+ end
374
+ end
375
+ end
376
+
377
+ class BNEInstr < TwoOperandJumpInstr
378
+ def doc_syntax = "!="
379
+ def execute(machine)
380
+ b, a = decode(machine, operand2), decode(machine, operand1)
381
+ a != b ? super : nil
382
+ end
383
+ def to_ruby(cfg, bb)
384
+ if stack_independent?
385
+ " #{ruby_operand(operand1)} != #{ruby_operand(operand2)} ? :\"#{label}\" : #{cfg.outgoing_target(bb, :fall_through)&.label}\n"
386
+ else
387
+ ruby_indent <<~"EOS"
388
+ #{ruby_assign_2}
389
+ return a != b ? :"#{label}" : :"#{cfg.outgoing_target(bb, :fall_through)&.label}"
390
+ EOS
391
+ end
392
+ end
393
+ end
394
+
395
+ class GTInstr < Instr
396
+ include ResultInstr, TwoOperands
397
+
398
+ def execute(machine)
399
+ b, a = decode(machine, operand2), decode(machine, operand1)
400
+ result.value = a > b ? 1 : 0
401
+ nil
402
+ end
403
+
404
+ def disasm = "#{disasm_operand(result)} = #{disasm_operand(operand1)} > #{disasm_operand(operand2)}"
405
+
406
+ def to_ruby(cfg, bb)
407
+ if stack_independent?
408
+ " #{ruby_operand(result)} = #{ruby_operand(operand1)} > #{ruby_operand(operand2)} ? 1 : 0\n"
409
+ else
410
+ ruby_indent <<~"EOS"
411
+ #{ruby_assign_2}
412
+ #{ruby_operand(result)} = a > b ? 1 : 0
413
+ EOS
414
+ end
415
+ end
416
+ end
417
+
418
+ class NEInstr < Instr
419
+ include ResultInstr, TwoOperands
420
+
421
+ def disasm = "#{disasm_operand(result)} = #{disasm_operand(operand1)} != #{disasm_operand(operand2)}"
422
+
423
+ def execute(machine)
424
+ b, a = decode(machine, operand2), decode(machine, operand1)
425
+ result.value = a != b ? 0 : 1
426
+ nil
427
+ end
428
+
429
+ def to_ruby(cfg, bb)
430
+ if stack_independent?
431
+ " #{ruby_operand(result)} = #{ruby_operand(operand1)} != #{ruby_operand(operand2)} ? 0 : 1\n"
432
+ else
433
+ ruby_indent <<~"EOS"
434
+ #{ruby_assign_2}
435
+ #{ruby_operand(result)} = a != b ? 0 : 1
436
+ EOS
437
+ end
438
+ end
439
+
440
+ def to_s = "#{result} = #{operand1} > #{operand2}"
441
+ end
442
+
443
+ class DPInstr < SingleOperandInstr
444
+ def execute(machine)
445
+ machine.dp.from_ordinal!(decode(machine, operand))
446
+ nil
447
+ end
448
+ def to_ruby(cfg, bb)
449
+ if operand.kind_of?(Numeric) && operand >= 0 && operand < 4
450
+ direction = DirectionPointer.new(operand)
451
+ " @dp.direction = Direction::#{direction.as_constant}\n"
452
+ else
453
+ " @dp.from_ordinal!(#{ruby_operand(operand)})\n"
454
+ end
455
+ end
456
+ end
457
+
458
+ class CCInstr < SingleOperandInstr
459
+ def execute(machine)
460
+ machine.cc.from_ordinal!(decode(machine, operand))
461
+ nil
462
+ end
463
+ def to_ruby(cfg, bb)
464
+ if operand.kind_of?(Numeric) && (operand == -1 || operand == 1)
465
+ cc = CodelChooser.new
466
+ cc.switch!(operand)
467
+ " @cc.direction = CodelChooser::#{cc.as_constant}\n"
468
+ else
469
+ " @cc.from_ordinal!(#{ruby_operand(operand)})\n"
470
+ end
471
+ end
472
+ end
473
+
474
+ class DPRotateInstr < SingleOperandInstr
475
+ include ResultInstr
476
+
477
+ def execute(machine)
478
+ machine.dp.rotate!(decode(machine, operand))
479
+ result.value = machine.dp.dup
480
+ nil
481
+ end
482
+
483
+ def to_ruby(cfg, bb) = " #{ruby_operand(result)} = @dp.rotate!(#{ruby_operand(operand)})\n"
484
+
485
+ def to_s = "#{result} = #{operation} #{operand}"
486
+ end
487
+
488
+ class CCToggleInstr < SingleOperandInstr
489
+ include ResultInstr
490
+
491
+ def execute(machine)
492
+ machine.cc.switch!(decode(machine, operand))
493
+ result.value = machine.cc.dup
494
+ nil
495
+ end
496
+
497
+ def to_ruby(cfg, bb) = " #{ruby_operand(result)} = @cc.switch!(#{ruby_operand(operand)})\n"
498
+
499
+ def to_s = super + " #{operand}"
500
+ end
501
+
502
+ class PntrInstr < Instr
503
+ def initialize(step)
504
+ super()
505
+ @step = step
506
+ end
507
+
508
+ def execute(machine)
509
+ operands[decode(machine, operands.first).ordinal+1]
510
+ end
511
+
512
+ def jump? = true
513
+
514
+ def to_ruby_pre
515
+ "@Operands#{@step} = [#{operands[1..-1].map { |o| ":#{o}"}.join(', ')}]\n"
516
+ end
517
+
518
+ def to_s = "#{operation} #{operands.first}"
519
+
520
+ def to_ruby(cfg, bb) = " @Operands#{@step}[#{ruby_operand(operands.first)}.ordinal]\n"
521
+ end
522
+
523
+ class MultiplePushInstr < Instr
524
+ def to_ruby(cfg, bb) = " @stack.push #{operands.map {|o| ruby_operand(o)}.join(', ')}\n"
525
+ end
526
+
527
+ class MultiplePopInstr < Instr
528
+ def to_ruby(cfg, bb)
529
+ count = operands.size
530
+
531
+ " #{operands.map {|o| ruby_operand(o)}.join(', ')} = @stack.pop(#{count})\n"
532
+ end
533
+ end
534
+ end
535
+ end
536
+ end
@@ -0,0 +1,23 @@
1
+ require_relative 'ir_interpreter'
2
+ require_relative 'cfg'
3
+ require_relative 'passes/peephole'
4
+
5
+ module RPiet
6
+ module IR
7
+ class IRCFGInterpreter < IRInterpreter
8
+ def process_image(image)
9
+ graph = RPiet::ASG::Parser.new(image).run
10
+ builder = RPiet::Builder.new
11
+ builder.run graph
12
+ @instructions = builder.instructions
13
+ puts "(initial) # of instr: #{@instructions.length}"
14
+ @cfg = CFG.new(@instructions)
15
+ @instructions = @cfg.instructions(Passes::Peephole)
16
+ @cfg.write_to_dot_file
17
+ puts "(post) # of instr: #{@instructions.length}"
18
+ end
19
+
20
+ def disasm = @instructions.each { |instr| puts instr.disasm }
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,101 @@
1
+ require_relative '../asg/parser'
2
+ require_relative 'builder'
3
+ require_relative 'cfg'
4
+ require_relative 'passes/peephole'
5
+
6
+ module RPiet
7
+ module IR
8
+ class IRInterpreter
9
+ include LiveMachineState
10
+
11
+ def initialize(image_or_instructions, event_handler = nil)
12
+ handle_event_handler(@event_handler = event_handler)
13
+
14
+ if image_or_instructions.kind_of?(RPiet::Image::Image)
15
+ process_image(image_or_instructions)
16
+ else
17
+ @instructions = image_or_instructions
18
+ end
19
+
20
+ @jump_table = calculate_jump_table(@instructions) # {label -> index}
21
+ reset
22
+ end
23
+
24
+ def process_image(image)
25
+ graph = RPiet::ASG::Parser.new(image).run
26
+ builder = RPiet::Builder.new
27
+ builder.run graph
28
+ @instructions = builder.instructions
29
+ puts "simple ir # of instr: #{@instructions.length}"
30
+ end
31
+
32
+ def disasm
33
+ @instructions.each do |instr|
34
+ puts instr.disasm
35
+ end
36
+ end
37
+
38
+ def reset
39
+ reset_machine
40
+ @ipc, @last_node = 0, nil
41
+ end
42
+
43
+ def calculate_jump_table(instructions)
44
+ jump_table = {}
45
+ i = 0
46
+ while i < instructions.length
47
+ instr = instructions[i]
48
+
49
+ if instr.operation == :label
50
+ jump_table[instr.operand] = i
51
+ instructions.delete(instr)
52
+ else
53
+ i += 1
54
+ end
55
+ end
56
+ jump_table
57
+ end
58
+
59
+ def next_instruction_logging
60
+ instr = @instructions[@ipc]
61
+
62
+ if instr&.graph_node && @last_node != instr.graph_node
63
+ @last_node = instr.graph_node
64
+ @event_handler.operation(self, @last_node.step, @last_node.operation)
65
+ end
66
+
67
+ @event_handler.instruction(self, instr)
68
+ instr
69
+ end
70
+
71
+ def next_instruction
72
+ @instructions[@ipc]
73
+ end
74
+
75
+ def next_step
76
+ value = next_instruction.execute(self)
77
+
78
+ if value
79
+ # FIXME: Make normative exit jump so it makes an exit bb vs randomly exiting (also removes this code)
80
+ return false if value == :exit
81
+ @ipc = @jump_table[value]
82
+ else
83
+ @ipc += 1
84
+ end
85
+ true
86
+ end
87
+
88
+ def run
89
+ while next_step do
90
+ end
91
+ end
92
+
93
+ private def handle_event_handler(event_handler)
94
+ if event_handler
95
+ alias next_instruction_orig next_instruction
96
+ alias next_instruction next_instruction_logging
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end