Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit b14e56e

Browse files
authored
Merge pull request #292 from ruby-syntax-tree/opt
Sea of nodes optimizations and convenience functions
2 parents b7e0044 + 9e09fd0 commit b14e56e

File tree

4 files changed

+113
-42
lines changed

4 files changed

+113
-42
lines changed

lib/syntax_tree/yarv/control_flow_graph.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,10 @@ def to_dfg
203203
DataFlowGraph.compile(self)
204204
end
205205

206+
def to_son
207+
to_dfg.to_son
208+
end
209+
206210
def to_mermaid
207211
output = StringIO.new
208212
output.puts("flowchart TD")

lib/syntax_tree/yarv/instruction_sequence.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,14 @@ def to_cfg
273273
ControlFlowGraph.compile(self)
274274
end
275275

276+
def to_dfg
277+
to_cfg.to_dfg
278+
end
279+
280+
def to_son
281+
to_dfg.to_son
282+
end
283+
276284
def disasm
277285
fmt = Disassembler.new
278286
fmt.enqueue(self)

lib/syntax_tree/yarv/sea_of_nodes.rb

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@ def compile
118118

119119
connect_local_graphs_control(local_graphs)
120120
connect_local_graphs_data(local_graphs)
121-
cleanup
121+
cleanup_phi_nodes
122+
cleanup_insn_nodes
122123

123124
SeaOfNodes.new(dfg, nodes, local_graphs).tap(&:verify)
124125
end
@@ -311,23 +312,13 @@ def connect_local_graphs_data(local_graphs)
311312
# We don't always build things in an optimal way. Go back and fix up
312313
# some mess we left. Ideally we wouldn't create these problems in the
313314
# first place.
314-
def cleanup
315+
def cleanup_phi_nodes
315316
nodes.dup.each do |node| # dup because we're mutating
316317
next unless node.is_a?(PhiNode)
317318

318319
if node.inputs.size == 1
319320
# Remove phi nodes with a single input.
320-
node.inputs.each do |producer_edge|
321-
node.outputs.each do |consumer_edge|
322-
connect(
323-
producer_edge.from,
324-
consumer_edge.to,
325-
producer_edge.type,
326-
consumer_edge.label
327-
)
328-
end
329-
end
330-
321+
connect_over(node)
331322
remove(node)
332323
elsif node.inputs.map(&:from).uniq.size == 1
333324
# Remove phi nodes where all inputs are the same.
@@ -344,6 +335,66 @@ def cleanup
344335
end
345336
end
346337

338+
# Eliminate as many unnecessary nodes as we can.
339+
def cleanup_insn_nodes
340+
nodes.dup.each do |node|
341+
next unless node.is_a?(InsnNode)
342+
343+
case node.insn
344+
when AdjustStack
345+
# If there are any inputs to the adjust stack that are immediately
346+
# discarded, we can remove them from the input list.
347+
number = node.insn.number
348+
349+
node.inputs.dup.each do |input_edge|
350+
next if input_edge.type != :data
351+
352+
from = input_edge.from
353+
next unless from.is_a?(InsnNode)
354+
355+
if from.inputs.empty? && from.outputs.size == 1
356+
number -= 1
357+
remove(input_edge.from)
358+
elsif from.insn.is_a?(Dup)
359+
number -= 1
360+
connect_over(from)
361+
remove(from)
362+
363+
new_edge = node.inputs.last
364+
new_edge.from.outputs.delete(new_edge)
365+
node.inputs.delete(new_edge)
366+
end
367+
end
368+
369+
if number == 0
370+
connect_over(node)
371+
remove(node)
372+
else
373+
next_node =
374+
if number == 1
375+
InsnNode.new(Pop.new, node.offset)
376+
else
377+
InsnNode.new(AdjustStack.new(number), node.offset)
378+
end
379+
380+
next_node.inputs.concat(node.inputs)
381+
next_node.outputs.concat(node.outputs)
382+
383+
# Dynamically finding the index of the node in the nodes array
384+
# because we're mutating the array as we go.
385+
nodes[nodes.index(node)] = next_node
386+
end
387+
when Jump
388+
# When you have a jump instruction that only has one input and one
389+
# output, you can just connect over top of it and remove it.
390+
if node.inputs.size == 1 && node.outputs.size == 1
391+
connect_over(node)
392+
remove(node)
393+
end
394+
end
395+
end
396+
end
397+
347398
# Connect one node to another.
348399
def connect(from, to, type, label = nil)
349400
raise if from == to
@@ -354,6 +405,20 @@ def connect(from, to, type, label = nil)
354405
to.inputs << edge
355406
end
356407

408+
# Connect all of the inputs to all of the outputs of a node.
409+
def connect_over(node)
410+
node.inputs.each do |producer_edge|
411+
node.outputs.each do |consumer_edge|
412+
connect(
413+
producer_edge.from,
414+
consumer_edge.to,
415+
producer_edge.type,
416+
producer_edge.label
417+
)
418+
end
419+
end
420+
end
421+
357422
# Remove a node from the graph.
358423
def remove(node)
359424
node.inputs.each do |producer_edge|

test/yarv_test.rb

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ def test_cfg
302302
iseq = SyntaxTree::YARV::InstructionSequence.from(iseq.to_a)
303303
cfg = SyntaxTree::YARV::ControlFlowGraph.compile(iseq)
304304

305-
assert_equal(<<~CFG, cfg.disasm)
305+
assert_equal(<<~DISASM, cfg.disasm)
306306
== cfg: #<ISeq:<compiled>@<compiled>:1 (1,0)-(1,0)>
307307
block_0
308308
0000 putobject 100
@@ -325,7 +325,7 @@ def test_cfg
325325
0014 opt_plus <calldata!mid:+, argc:1, ARGS_SIMPLE>
326326
0016 leave
327327
== to: leaves
328-
CFG
328+
DISASM
329329
end
330330

331331
def test_dfg
@@ -334,7 +334,7 @@ def test_dfg
334334
cfg = SyntaxTree::YARV::ControlFlowGraph.compile(iseq)
335335
dfg = SyntaxTree::YARV::DataFlowGraph.compile(cfg)
336336

337-
assert_equal(<<~DFG, dfg.disasm)
337+
assert_equal(<<~DISASM, dfg.disasm)
338338
== dfg: #<ISeq:<compiled>@<compiled>:1 (1,0)-(1,0)>
339339
block_0
340340
0000 putobject 100 # out: out_0
@@ -363,7 +363,7 @@ def test_dfg
363363
0014 opt_plus <calldata!mid:+, argc:1, ARGS_SIMPLE> # in: in_0, in_1; out: 16
364364
0016 leave # in: 14
365365
== to: leaves
366-
DFG
366+
DISASM
367367
end
368368

369369
def test_son
@@ -373,14 +373,13 @@ def test_son
373373
dfg = SyntaxTree::YARV::DataFlowGraph.compile(cfg)
374374
son = SyntaxTree::YARV::SeaOfNodes.compile(dfg)
375375

376-
assert_equal(<<~SON, son.to_mermaid)
376+
assert_equal(<<~MERMAID, son.to_mermaid)
377377
flowchart TD
378378
node_0("0000 putobject 14")
379379
node_2("0002 putobject_INT2FIX_0_")
380380
node_3("0003 opt_lt &lt;calldata!mid:&lt;, argc:1, ARGS_SIMPLE&gt;")
381381
node_5("0005 branchunless 0011")
382382
node_7("0007 putobject -1")
383-
node_9("0009 jump 0012")
384383
node_11("0011 putobject_INT2FIX_1_")
385384
node_12("0012 putobject 100")
386385
node_14("0014 opt_plus &lt;calldata!mid:+, argc:1, ARGS_SIMPLE&gt;")
@@ -397,28 +396,26 @@ def test_son
397396
linkStyle 3 stroke:green;
398397
node_5 --> |branch0| node_11
399398
linkStyle 4 stroke:red;
400-
node_5 --> |fallthrough| node_9
399+
node_5 --> |fallthrough| node_1000
401400
linkStyle 5 stroke:red;
402401
node_7 --> |0009| node_1001
403402
linkStyle 6 stroke:green;
404-
node_9 --> |branch0| node_1000
405-
linkStyle 7 stroke:red;
406403
node_11 --> |branch0| node_1000
407-
linkStyle 8 stroke:red;
404+
linkStyle 7 stroke:red;
408405
node_11 --> |0011| node_1001
409-
linkStyle 9 stroke:green;
406+
linkStyle 8 stroke:green;
410407
node_12 --> |1| node_14
411-
linkStyle 10 stroke:green;
408+
linkStyle 9 stroke:green;
412409
node_14 --> node_16
413-
linkStyle 11 stroke:red;
410+
linkStyle 10 stroke:red;
414411
node_14 --> |0| node_16
415-
linkStyle 12 stroke:green;
412+
linkStyle 11 stroke:green;
416413
node_1000 --> node_14
417-
linkStyle 13 stroke:red;
414+
linkStyle 12 stroke:red;
418415
node_1001 -.-> node_1000
419416
node_1001 --> |0| node_14
420-
linkStyle 15 stroke:green;
421-
SON
417+
linkStyle 14 stroke:green;
418+
MERMAID
422419
end
423420

424421
def test_son_indirect_basic_block_argument
@@ -428,15 +425,14 @@ def test_son_indirect_basic_block_argument
428425
dfg = SyntaxTree::YARV::DataFlowGraph.compile(cfg)
429426
son = SyntaxTree::YARV::SeaOfNodes.compile(dfg)
430427

431-
assert_equal(<<~SON, son.to_mermaid)
428+
assert_equal(<<~MERMAID, son.to_mermaid)
432429
flowchart TD
433430
node_0("0000 putobject 100")
434431
node_2("0002 putobject 14")
435432
node_4("0004 putobject_INT2FIX_0_")
436433
node_5("0005 opt_lt &lt;calldata!mid:&lt;, argc:1, ARGS_SIMPLE&gt;")
437434
node_7("0007 branchunless 0013")
438435
node_9("0009 putobject -1")
439-
node_11("0011 jump 0014")
440436
node_13("0013 putobject_INT2FIX_1_")
441437
node_14("0014 opt_plus &lt;calldata!mid:+, argc:1, ARGS_SIMPLE&gt;")
442438
node_16("0016 leave")
@@ -454,26 +450,24 @@ def test_son_indirect_basic_block_argument
454450
linkStyle 4 stroke:green;
455451
node_7 --> |branch0| node_13
456452
linkStyle 5 stroke:red;
457-
node_7 --> |fallthrough| node_11
453+
node_7 --> |fallthrough| node_1002
458454
linkStyle 6 stroke:red;
459455
node_9 --> |0011| node_1004
460456
linkStyle 7 stroke:green;
461-
node_11 --> |branch0| node_1002
462-
linkStyle 8 stroke:red;
463457
node_13 --> |branch0| node_1002
464-
linkStyle 9 stroke:red;
458+
linkStyle 8 stroke:red;
465459
node_13 --> |0013| node_1004
466-
linkStyle 10 stroke:green;
460+
linkStyle 9 stroke:green;
467461
node_14 --> node_16
468-
linkStyle 11 stroke:red;
462+
linkStyle 10 stroke:red;
469463
node_14 --> |0| node_16
470-
linkStyle 12 stroke:green;
464+
linkStyle 11 stroke:green;
471465
node_1002 --> node_14
472-
linkStyle 13 stroke:red;
466+
linkStyle 12 stroke:red;
473467
node_1004 -.-> node_1002
474468
node_1004 --> |1| node_14
475-
linkStyle 15 stroke:green;
476-
SON
469+
linkStyle 14 stroke:green;
470+
MERMAID
477471
end
478472

479473
private

0 commit comments

Comments
 (0)