rpiet 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/lib/rpiet/ir/cfg.rb
ADDED
@@ -0,0 +1,494 @@
|
|
1
|
+
require_relative 'instructions'
|
2
|
+
require 'rgl/adjacency'
|
3
|
+
require 'rgl/dot'
|
4
|
+
require 'rgl/edge_properties_map'
|
5
|
+
require 'rgl/traversal'
|
6
|
+
|
7
|
+
module RPiet
|
8
|
+
module IR
|
9
|
+
# Basic Block is a collection of instructions. It will commonly be referred to as bb
|
10
|
+
# in code and in comments. bbs is multiple basic blacks.
|
11
|
+
class BasicBlock
|
12
|
+
attr_reader :label, :instrs
|
13
|
+
attr_accessor :debug
|
14
|
+
|
15
|
+
def initialize(label)
|
16
|
+
@label, @instrs = label, []
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_instr(instr) = @instrs << instr
|
20
|
+
|
21
|
+
def last_instr
|
22
|
+
instrs&.last
|
23
|
+
end
|
24
|
+
|
25
|
+
# If this bb only meaningfully contains a jump (e.g. jump + noops/label)
|
26
|
+
def only_contains_jump?
|
27
|
+
instrs&.last.class == Instructions::JumpInstr && !instrs[0...-2].find { |instr| !instr.noop? }
|
28
|
+
end
|
29
|
+
|
30
|
+
# Maybe a bit hacky but inspect is for dot output. consider changing this.
|
31
|
+
def inspect
|
32
|
+
str = "name: #{@label}\n\n"
|
33
|
+
str << "#{@instrs.map {|i| i}.join('\l')}\\l" if !@instrs.empty?
|
34
|
+
str
|
35
|
+
end
|
36
|
+
alias :to_s :inspect
|
37
|
+
end
|
38
|
+
|
39
|
+
# CFG - Control Flow Graph
|
40
|
+
#
|
41
|
+
# A control flow graph is a directed graph which show the all paths in which a
|
42
|
+
# program can flow/traverse. Having this form for your program opens up the
|
43
|
+
# ability to perform various compiler optimizations.
|
44
|
+
#
|
45
|
+
# An if statement, for example, shows a possible flow if the test stmt of the
|
46
|
+
# if succeeds. If it fails then the the flow moves past the if body. The if
|
47
|
+
# body itself flows to that same point:
|
48
|
+
#
|
49
|
+
# if test
|
50
|
+
# foo
|
51
|
+
# end
|
52
|
+
# bar
|
53
|
+
#
|
54
|
+
# +--------------------+ +----------------+
|
55
|
+
# | bb: start | true | bb: if_body |
|
56
|
+
# +--------------------+------------>>>+----------------+
|
57
|
+
# | branch_true test | | call foo |
|
58
|
+
# +--------------------+ +----------------+
|
59
|
+
# | |
|
60
|
+
# | false |
|
61
|
+
# | |
|
62
|
+
# v |
|
63
|
+
# +--------------------+ |
|
64
|
+
# | bb after_if | |
|
65
|
+
# +--------------------+<<<--------------------+
|
66
|
+
# | call bar |
|
67
|
+
# +--------------------+
|
68
|
+
#
|
69
|
+
# This CFG also generates an additional exit BB. This is the last flow node
|
70
|
+
# before execution terminates. Entry BB is the first node you encounter
|
71
|
+
# during execution and where you typically start on any analysis.
|
72
|
+
class CFG
|
73
|
+
attr_reader :entry_bb, :exit_bb
|
74
|
+
|
75
|
+
def initialize(instrs)
|
76
|
+
@graph, @bb_map, @edge_labels = RGL::DirectedAdjacencyGraph.new, {}, {}
|
77
|
+
build(instrs)
|
78
|
+
end
|
79
|
+
|
80
|
+
def basic_blocks
|
81
|
+
@bb_map.values
|
82
|
+
end
|
83
|
+
|
84
|
+
# Get a sequential list of instructions from this CFG that can be executed.
|
85
|
+
# This will linearize and optimize the CFG before making the instruction
|
86
|
+
# list.
|
87
|
+
def instructions(*passes)
|
88
|
+
passes.each do |pass|
|
89
|
+
pass.new(self).run
|
90
|
+
end
|
91
|
+
|
92
|
+
bbs = linearize
|
93
|
+
|
94
|
+
#write_to_dot_file
|
95
|
+
bbs.each_with_object([]) { |bb, arr| arr.concat bb.instrs }
|
96
|
+
end
|
97
|
+
|
98
|
+
def write_to_dot_file
|
99
|
+
@graph.write_to_graphic_file
|
100
|
+
end
|
101
|
+
|
102
|
+
def outgoing_edges(bb, edge_type=nil)
|
103
|
+
if block_given?
|
104
|
+
@graph.edges.each do |edge|
|
105
|
+
yield edge if outgoing_edges_match?(bb, edge, edge_type)
|
106
|
+
end
|
107
|
+
else
|
108
|
+
@graph.edges.select { |edge| outgoing_edges_match?(bb, edge, edge_type) }
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def outgoing_target(bb, edge_type)
|
113
|
+
outgoing_edges(bb, edge_type).map { |edge| edge.target }&.first
|
114
|
+
end
|
115
|
+
|
116
|
+
def outgoing_targets(bb)
|
117
|
+
outgoing_edges(bb).map { |edge| edge.target }
|
118
|
+
end
|
119
|
+
|
120
|
+
def incoming_sources(bb)
|
121
|
+
@graph.edges.select { |edge| edge.target == bb }.map {|edge| edge.source}
|
122
|
+
end
|
123
|
+
|
124
|
+
def new_bb(label)
|
125
|
+
@bb_map[label] = bb = BasicBlock.new(label)
|
126
|
+
@graph.add_vertex(bb)
|
127
|
+
@graph.set_vertex_options(bb, shape: 'box', style: 'rounded')
|
128
|
+
bb
|
129
|
+
end
|
130
|
+
|
131
|
+
def remove_bb(bb)
|
132
|
+
@bb_map.delete(bb.label)
|
133
|
+
@graph.remove_vertex(bb)
|
134
|
+
incoming_sources(bb).each { |other| remove_edge(other, bb) }
|
135
|
+
outgoing_targets(bb).each { |other| remove_edge(bb, other) }
|
136
|
+
end
|
137
|
+
|
138
|
+
def add_forward_edge(source_bb, target_label, forward_refs)
|
139
|
+
target_bb = @bb_map[target_label]
|
140
|
+
|
141
|
+
if target_bb
|
142
|
+
add_jump_edge(source_bb, target_bb)
|
143
|
+
else
|
144
|
+
forward_refs[target_label] ||= []
|
145
|
+
forward_refs[target_label] << source_bb
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def add_edge(source_bb, target_bb, edge_type)
|
150
|
+
@edge_labels[[source_bb, target_bb]] = edge_type
|
151
|
+
@graph.add_edge(source_bb, target_bb)
|
152
|
+
@graph.set_edge_options(source_bb, target_bb, label: edge_type.to_s, color: edge_type_color(edge_type))
|
153
|
+
target_bb
|
154
|
+
end
|
155
|
+
|
156
|
+
def remove_edge(source_bb, target_bb)
|
157
|
+
@edge_labels.delete([source_bb, target_bb])
|
158
|
+
@graph.remove_edge(source_bb, target_bb)
|
159
|
+
end
|
160
|
+
|
161
|
+
def edge_type_color(edge_type) = edge_type == :jump ? 'blue' : 'green'
|
162
|
+
|
163
|
+
def add_jump_edge(source_bb, target_bb) = add_edge(source_bb, target_bb, :jump)
|
164
|
+
def add_fallthrough_edge(source_bb, target_bb) = add_edge(source_bb, target_bb, :fall_through)
|
165
|
+
|
166
|
+
def build(instrs)
|
167
|
+
forward_references = {}
|
168
|
+
|
169
|
+
current_bb = new_bb(:entry)
|
170
|
+
@entry_bb = current_bb
|
171
|
+
@exit_bb = new_bb(:exit)
|
172
|
+
@exit_bb.instrs << Instructions::LabelInstr.new(:exit)
|
173
|
+
|
174
|
+
just_jumped = false
|
175
|
+
|
176
|
+
instrs.each do |instr|
|
177
|
+
case instr
|
178
|
+
when Instructions::TwoOperandJumpInstr # :gt, :bne, :beq
|
179
|
+
just_jumped = false
|
180
|
+
current_bb.add_instr(instr)
|
181
|
+
add_forward_edge(current_bb, instr.value, forward_references)
|
182
|
+
current_bb = add_fallthrough_edge(current_bb, new_bb("fall_thru_#{instr.object_id}"))
|
183
|
+
when Instructions::ExitInstr
|
184
|
+
just_jumped = true
|
185
|
+
add_jump(current_bb, @exit_bb.label, instr)
|
186
|
+
add_jump_edge(current_bb, @exit_bb)
|
187
|
+
when Instructions::JumpInstr # :jump
|
188
|
+
current_bb.add_instr(instr)
|
189
|
+
add_forward_edge(current_bb, instr.value, forward_references)
|
190
|
+
just_jumped = true
|
191
|
+
when Instructions::LabelInstr
|
192
|
+
label = instr.value
|
193
|
+
bb = new_bb(label)
|
194
|
+
if just_jumped # jump foo\nlabel something_else\n
|
195
|
+
just_jumped = false
|
196
|
+
else
|
197
|
+
add_fallthrough_edge(current_bb, bb)
|
198
|
+
end
|
199
|
+
current_bb = bb
|
200
|
+
forward_references[label]&.each do |source_bb|
|
201
|
+
add_jump_edge(source_bb, bb)
|
202
|
+
end
|
203
|
+
current_bb.add_instr(instr)
|
204
|
+
else
|
205
|
+
just_jumped = false
|
206
|
+
current_bb.add_instr(instr)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
has_edge = @graph.edges.find { |edge| edge.source == current_bb }
|
211
|
+
add_fallthrough_edge(current_bb, @exit_bb) unless has_edge
|
212
|
+
|
213
|
+
@edge_props = RGL::EdgePropertiesMap.new(@edge_labels, true)
|
214
|
+
end
|
215
|
+
|
216
|
+
def cull
|
217
|
+
#show_single_jump_bbs
|
218
|
+
cull_dead_bbs
|
219
|
+
cull_isolated_bbs
|
220
|
+
remove_empty_bbs
|
221
|
+
jump_reduction
|
222
|
+
cull_dead_bbs
|
223
|
+
cull_isolated_bbs
|
224
|
+
recombine_pntr
|
225
|
+
end
|
226
|
+
|
227
|
+
def show_single_jump_bbs
|
228
|
+
@bb_map.each_value do |bb|
|
229
|
+
puts "BB: #{bb.label}"
|
230
|
+
puts "---------------"
|
231
|
+
puts bb.instrs.map { |i| i.disasm }.join("\n")
|
232
|
+
puts
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# doing up to 3 branches + 1 jump can be optimized by recombining these back
|
237
|
+
# into a pntr instruction which can just use a jump table
|
238
|
+
def recombine_pntr
|
239
|
+
pntrs = {}
|
240
|
+
@bb_map.each_value do |bb|
|
241
|
+
last_instr = bb.last_instr
|
242
|
+
#puts "BB #{bb.label} #{last_instr} #{bb.instrs.size}"
|
243
|
+
if bb.label.to_s =~ /pntr_3_(\d+)/
|
244
|
+
pntrs[$1] ||= []
|
245
|
+
pntrs[$1] << [$1, "3", bb]
|
246
|
+
next
|
247
|
+
end
|
248
|
+
if last_instr.kind_of?(Instructions::BEQInstr)
|
249
|
+
if last_instr.label.to_s =~ /pntr_(\d+)_(\d+)/
|
250
|
+
#puts "1: #{$1}, 2: #{$2}"
|
251
|
+
pntrs[$2] ||= []
|
252
|
+
pntrs[$2] << [$2, $1, bb]
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
pntrs.each_value.sort { |(step, i, _), (step2, i2, _)| step <=> step2 && i <=> i2 }
|
258
|
+
|
259
|
+
pntrs.each_value do |list|
|
260
|
+
new_instr = Instructions::PntrInstr.new(list[0][0])
|
261
|
+
zero_bb = nil
|
262
|
+
list.each.each_with_index do |(step, _, bb), i|
|
263
|
+
if i != 3
|
264
|
+
label = bb.instrs.last.label
|
265
|
+
if i == 0
|
266
|
+
compare = bb.instrs.last.operand1
|
267
|
+
new_instr.operands << compare
|
268
|
+
bb.instrs[-1] = new_instr
|
269
|
+
zero_bb = bb
|
270
|
+
else
|
271
|
+
add_edge(zero_bb, outgoing_target(bb, :jump), :jump)
|
272
|
+
remove_bb(bb)
|
273
|
+
end
|
274
|
+
new_instr.operands << label
|
275
|
+
else
|
276
|
+
if bb.only_contains_jump?
|
277
|
+
jump_bb = outgoing_target(bb, :jump)
|
278
|
+
remove_bb(bb)
|
279
|
+
bb = jump_bb
|
280
|
+
end
|
281
|
+
add_edge(zero_bb, bb, :jump)
|
282
|
+
new_instr.operands << bb.label
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
def jump_reduction
|
289
|
+
@bb_map.each_value do |bb|
|
290
|
+
#puts "BB: #{bb.label}"
|
291
|
+
if @graph.out_degree(bb) == 1
|
292
|
+
next_bb = outgoing_target(bb, :fall_through)
|
293
|
+
|
294
|
+
if next_bb && @graph.out_degree(next_bb) == 1 && next_bb.only_contains_jump?
|
295
|
+
#puts "JUMP REPLACE: #{bb.label} -> #{next_bb.label}"
|
296
|
+
jump = next_bb.instrs.last
|
297
|
+
if bb.instrs[-1].kind_of?(Instructions::JumpInstr)
|
298
|
+
bb.instrs[-1] = jump
|
299
|
+
else
|
300
|
+
bb.instrs << jump
|
301
|
+
end
|
302
|
+
remove_edge(bb, next_bb)
|
303
|
+
add_edge(bb, outgoing_target(next_bb, :jump), :jump)
|
304
|
+
next
|
305
|
+
end
|
306
|
+
|
307
|
+
next_bb = outgoing_target(bb, :jump)
|
308
|
+
if next_bb && @graph.out_degree(next_bb) == 1 && next_bb.only_contains_jump?
|
309
|
+
#puts "JUMP REPLACE: #{bb.label} -> #{next_bb.label}"
|
310
|
+
jump = next_bb.instrs.last
|
311
|
+
bb.instrs[-1] = jump
|
312
|
+
remove_edge(bb, next_bb)
|
313
|
+
add_edge(bb, outgoing_target(next_bb, :jump), :jump)
|
314
|
+
next
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
def remove_empty_bbs
|
321
|
+
# This is a bit wonky but pntr[3] needs fall through to jump to body for pntr[3]
|
322
|
+
# I do not think the pattern can occur any other way so I am assuming a lot in this
|
323
|
+
# method about its structure. For CFG interp this has no impact but for displaying
|
324
|
+
# The CFG itself it removes an empty bb. It also makes recombining the pntr to
|
325
|
+
# a jump table simpler.
|
326
|
+
@bb_map.each_value do |bb|
|
327
|
+
if @graph.out_degree(bb) == 1 && bb.instrs.empty?
|
328
|
+
target = outgoing_target(bb, :fall_through)
|
329
|
+
source = incoming_sources(bb).first
|
330
|
+
remove_bb(bb)
|
331
|
+
add_edge(source, target, :fall_through)
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
def cull_dead_bbs
|
337
|
+
@bb_map.each_value do |bb|
|
338
|
+
jump_bb = outgoing_target(bb, :jump)
|
339
|
+
|
340
|
+
# How we make CFG will only have a fall_through edge if next bb has an incoming edge
|
341
|
+
# AND/OR if we have a jump. We do not act on the incoming edge so we only cull if we
|
342
|
+
# see a constant jump
|
343
|
+
next unless jump_bb
|
344
|
+
|
345
|
+
fallthrough_bb = outgoing_target(bb, :fall_through)
|
346
|
+
last_instr = bb.instrs.last
|
347
|
+
if last_instr&.constant? # We can elminate an edge
|
348
|
+
result = last_instr.execute(nil)
|
349
|
+
if result
|
350
|
+
remove_edge(bb, fallthrough_bb)
|
351
|
+
else
|
352
|
+
remove_edge(bb, jump_bb)
|
353
|
+
puts "DELETING: #{bb.label}:#{last_instr}"
|
354
|
+
bb.instrs.delete(last_instr)
|
355
|
+
end
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
def cull_isolated_bbs
|
361
|
+
@bb_map.each do |label, bb|
|
362
|
+
next if bb == @entry_bb
|
363
|
+
|
364
|
+
in_degree = incoming_sources(bb).size
|
365
|
+
if in_degree == 0 # If we cannot make it to this bb delete itself and its outgoing edged
|
366
|
+
remove_bb(bb)
|
367
|
+
end
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
def combine_bbs(bb)
|
372
|
+
other_bb = outgoing_targets(bb)[0]
|
373
|
+
puts "combining #{bb.label} with #{other_bb.label}" if debug
|
374
|
+
bb.instrs.concat(other_bb.instrs)
|
375
|
+
remove_edge(bb, other_bb)
|
376
|
+
replace_edge(bb, other_bb, :fall_through)
|
377
|
+
replace_edge(bb, other_bb, :jump)
|
378
|
+
end
|
379
|
+
|
380
|
+
def replace_edge(bb, other_bb, edge_type)
|
381
|
+
other_edge = outgoing_edges(other_bb, edge_type)
|
382
|
+
if other_edge
|
383
|
+
remove_edge(edge.source, edge.target)
|
384
|
+
add_edge(bb, edge.target, edge_type)
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
# Linearization rearranges basic blocks around fall throughs all being sequentially after
|
389
|
+
# the previous fall through bb. All bbs jumped to will get put after that.
|
390
|
+
#
|
391
|
+
# For these moved blocks we need to add a jump to the bb where execution continues from
|
392
|
+
# that bb unless the next bb happens to be right after it.
|
393
|
+
#
|
394
|
+
# The opposite case is true where last instr is a jump but the jump location happens to
|
395
|
+
# be the next bb. In that case we remove the jump.
|
396
|
+
#
|
397
|
+
# Piet only has two edge types (fall_through, jump) which makes this simpler than
|
398
|
+
# other languages which may return (from procedures/functions) or may raise exceptions.
|
399
|
+
def linearize
|
400
|
+
sorted_list, visited = [], {@exit_bb => true}
|
401
|
+
linearize_inner(sorted_list, visited, @entry_bb)
|
402
|
+
sorted_list << @exit_bb
|
403
|
+
recalculate_jumps(sorted_list)
|
404
|
+
sorted_list
|
405
|
+
end
|
406
|
+
|
407
|
+
def postorder_bbs
|
408
|
+
order = []
|
409
|
+
@graph.depth_first_visit(@entry_bb) { |v| order << v }
|
410
|
+
order
|
411
|
+
end
|
412
|
+
|
413
|
+
def postorder_bbs2
|
414
|
+
result, work_list, visited = [], [@entry_bb], {@entry_bb => true}
|
415
|
+
|
416
|
+
while !work_list.empty?
|
417
|
+
bb = work_list.last
|
418
|
+
all_children_visited = true
|
419
|
+
outgoing_targets(bb).each do |dest|
|
420
|
+
next if visited[dest]
|
421
|
+
visited[dest] = true
|
422
|
+
all_children_visited = false
|
423
|
+
if @graph.out_degree(dest) == 0 # should only be exit bb
|
424
|
+
result << dest
|
425
|
+
else
|
426
|
+
work_list << dest
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
if all_children_visited
|
431
|
+
work_list.pop
|
432
|
+
result << bb
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
result
|
437
|
+
end
|
438
|
+
|
439
|
+
def preorder_bbs
|
440
|
+
postorder_bbs.reverse
|
441
|
+
end
|
442
|
+
|
443
|
+
private
|
444
|
+
|
445
|
+
def linearize_inner(sorted_list, visited, bb)
|
446
|
+
return if visited[bb]
|
447
|
+
visited[bb] = true
|
448
|
+
sorted_list << bb
|
449
|
+
|
450
|
+
fall_through = outgoing_target(bb, :fall_through)
|
451
|
+
linearize_inner(sorted_list, visited, fall_through) if fall_through
|
452
|
+
outgoing_targets(bb).each do |jump|
|
453
|
+
next if jump == fall_through
|
454
|
+
linearize_inner(sorted_list, visited, jump) if jump
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
def outgoing_edges_match?(bb, edge, edge_type)
|
459
|
+
edge.source == bb && (!edge_type || @edge_props.edge_property(edge.source, edge.target) == edge_type)
|
460
|
+
end
|
461
|
+
|
462
|
+
def check_for_unneeded_jump(current_bb, next_bb, jump)
|
463
|
+
if jump.label == next_bb.label
|
464
|
+
remove_edge(current_bb, next_bb)
|
465
|
+
add_edge(current_bb, next_bb, :fall_through)
|
466
|
+
current_bb.instrs.pop
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
def check_for_needed_jump(current_bb, next_bb, last_instr)
|
471
|
+
return if current_bb == exit_bb
|
472
|
+
dest = outgoing_target(current_bb, :fall_through)
|
473
|
+
add_jump(current_bb, dest.label, last_instr) if dest && dest != next_bb
|
474
|
+
end
|
475
|
+
|
476
|
+
def add_jump(bb, label, last_instr)
|
477
|
+
jump = Instructions::JumpInstr.new(label)
|
478
|
+
jump.graph_node = last_instr&.graph_node
|
479
|
+
bb.instrs << jump
|
480
|
+
end
|
481
|
+
|
482
|
+
def recalculate_jumps(bbs)
|
483
|
+
bbs.each_with_index do |bb, index|
|
484
|
+
last_instr = bb.instrs.last
|
485
|
+
if last_instr&.operation == :jump
|
486
|
+
check_for_unneeded_jump(bb, bbs[index + 1], last_instr)
|
487
|
+
else
|
488
|
+
check_for_needed_jump(bb, bbs[index + 1], last_instr)
|
489
|
+
end
|
490
|
+
end
|
491
|
+
end
|
492
|
+
end
|
493
|
+
end
|
494
|
+
end
|