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.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/Gemfile +12 -0
- data/bin/color_wheel +84 -0
- data/bin/image_gen +39 -0
- data/bin/rpiet +68 -11
- data/images/counter.txt +7 -0
- data/lib/rpiet/asg/graph_interpreter.rb +39 -0
- data/lib/rpiet/asg/parser.rb +156 -0
- data/lib/rpiet/asg/visitor.rb +66 -0
- data/lib/rpiet/asg.rb +336 -0
- data/lib/rpiet/codel_chooser.rb +32 -4
- data/lib/rpiet/color.rb +70 -25
- data/lib/rpiet/cycle.rb +18 -7
- data/lib/rpiet/debugger/debugger.rb +298 -0
- data/lib/rpiet/debugger/stylesheet.css +88 -0
- data/lib/rpiet/direction_pointer.rb +49 -18
- data/lib/rpiet/event_handler.rb +62 -7
- data/lib/rpiet/group.rb +25 -8
- data/lib/rpiet/image/ascii_image.rb +8 -20
- data/lib/rpiet/image/image.rb +8 -3
- data/lib/rpiet/image/url_image.rb +28 -14
- data/lib/rpiet/interpreter.rb +72 -72
- data/lib/rpiet/ir/assembler.rb +87 -0
- data/lib/rpiet/ir/builder.rb +255 -0
- data/lib/rpiet/ir/cfg.rb +494 -0
- data/lib/rpiet/ir/instructions.rb +536 -0
- data/lib/rpiet/ir/ir_cfg_interpreter.rb +23 -0
- data/lib/rpiet/ir/ir_interpreter.rb +101 -0
- data/lib/rpiet/ir/ir_native_interpreter.rb +77 -0
- data/lib/rpiet/ir/jruby_backend.rb +279 -0
- data/lib/rpiet/ir/operands.rb +28 -0
- data/lib/rpiet/ir/passes/data_flow_problem.rb +32 -0
- data/lib/rpiet/ir/passes/flow_graph_node.rb +76 -0
- data/lib/rpiet/ir/passes/peephole.rb +214 -0
- data/lib/rpiet/ir/passes/push_pop_elimination_pass.rb +112 -0
- data/lib/rpiet/live_machine_state.rb +15 -0
- data/lib/rpiet/machine.rb +62 -32
- data/lib/rpiet/source.rb +83 -0
- data/lib/rpiet/version.rb +1 -1
- data/lib/rpiet.rb +2 -2
- data/rpiet.gemspec +19 -0
- data/spec/asg/visitor_spec.rb +41 -0
- data/spec/cycle_spec.rb +34 -34
- data/spec/direction_pointer_spec.rb +33 -6
- data/spec/group_spec.rb +73 -48
- data/spec/interpreter_spec.rb +161 -12
- data/spec/ir/assembler_spec.rb +122 -0
- data/spec/ir/builder_spec.rb +20 -0
- data/spec/ir/cfg_spec.rb +151 -0
- data/spec/ir/ir_interpreter_spec.rb +102 -0
- data/spec/ir/passes/push_pop_elimination_pass_spec.rb +34 -0
- data/spec/machine_spec.rb +5 -3
- data/spec/source_spec.rb +69 -0
- data/spec/spec_helper.rb +78 -0
- metadata +54 -16
- 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
|