rpiet 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
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