Skip to content

Commit d6af43a

Browse files
committed
ABCD (upper bounds checks)
1 parent d084dc8 commit d6af43a

9 files changed

+426
-123
lines changed
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
Class {
2+
#name : 'DRPiABCDDeadBranchEliminationTest',
3+
#superclass : 'DRPiNodesTest',
4+
#category : 'Druid-Tests-Optimizations',
5+
#package : 'Druid-Tests',
6+
#tag : 'Optimizations'
7+
}
8+
9+
{ #category : 'tests' }
10+
DRPiABCDDeadBranchEliminationTest >> testABCD1 [
11+
12+
"
13+
/->-|b_end0|
14+
|b_i| -> |b_if1| -> |b_end1| (unreachable)
15+
"
16+
17+
| cfg b_end0 b_end1 b_i x y b_if1 |
18+
cfg := DRControlFlowGraph new.
19+
20+
b_end0 := cfg newBasicBlockWith: [ :block | block storeSInt64: 2 at: 777 ].
21+
b_end1 := cfg newBasicBlockWith: [ :block | block storeSInt64: 2 at: 888 ].
22+
23+
b_i := cfg newBasicBlockWith: [ :block |
24+
x := block loadSInt64: 111.
25+
y := block subtract: 1 from: x.
26+
].
27+
28+
b_if1 := cfg newBasicBlockWith: [ :block | |cmp|
29+
cmp := block less: y than: x.
30+
block jumpIf: cmp to: b_end0 ifFalseTo: b_end1
31+
].
32+
33+
cfg initialBasicBlock jumpTo: b_i.
34+
b_i jumpTo: b_if1.
35+
36+
37+
self optimize: cfg with: { DRPiABCDDeadBranchElimination }.
38+
39+
40+
self assert: (cfg blocks includes: b_end0).
41+
self assert: (cfg blocks includes: b_end1) not.
42+
43+
self assert: b_if1 endInstruction isJump.
44+
self assert: b_if1 endInstruction isConditionalBranch not.
45+
self assert: b_if1 endInstruction target equals: b_end0.
46+
47+
]
48+
49+
{ #category : 'tests' }
50+
DRPiABCDDeadBranchEliminationTest >> testABCD2 [
51+
52+
"
53+
/->-|b_end0|
54+
|b_i| -> |b_if1| -> |b_end1| (unreachable)
55+
"
56+
57+
| cfg b_end0 b_end1 b_i x y b_if1 |
58+
cfg := DRControlFlowGraph new.
59+
60+
b_end0 := cfg newBasicBlockWith: [ :block | block storeSInt64: 2 at: 777 ].
61+
b_end1 := cfg newBasicBlockWith: [ :block | block storeSInt64: 2 at: 888 ].
62+
63+
b_i := cfg newBasicBlockWith: [ :block |
64+
x := block loadSInt64: 111.
65+
y := block add: 1 to: x.
66+
].
67+
68+
b_if1 := cfg newBasicBlockWith: [ :block | |cmp|
69+
cmp := block less: y than: x.
70+
block jumpIf: cmp to: b_end0 ifFalseTo: b_end1
71+
].
72+
73+
cfg initialBasicBlock jumpTo: b_i.
74+
b_i jumpTo: b_if1.
75+
76+
77+
self optimize: cfg with: { DRPiABCDDeadBranchElimination }.
78+
79+
80+
self assert: (cfg blocks includes: b_end0).
81+
self assert: (cfg blocks includes: b_end1) not.
82+
83+
self assert: b_if1 endInstruction isJump.
84+
self assert: b_if1 endInstruction isConditionalBranch not.
85+
self assert: b_if1 endInstruction target equals: b_end0.
86+
87+
]
88+
89+
{ #category : 'tests' }
90+
DRPiABCDDeadBranchEliminationTest >> testABCDGraph [
91+
| cfg b_i b_end limit0 limit1 st0 st1 b_while b_for j1 b_if1 st3 limit3 j0 b_if2 t0 b_if2_after_check2 b_if2_after_check1 j4 cmp_for dummyOperand1 jmp_for b_check_failed |
92+
93+
"This is the running example in the ABCD paper, reproduced it just to have something to validate our results agains
94+
The code is kinda ugly, I don't know if there's a nicer way to build this self-referencial/cyclic IRs"
95+
96+
" /->------------------------------------------------------------------------------------->-\
97+
/ V--<-------------------<-\ /->---------------------/->--\
98+
|b_i| -> |b_while| -> |b_if1| -> |b_for| -> |b_if2| -> |b_if2_after_check1| -> |b_if2_after_check2| -> |b_end|
99+
\-<------------------------------------------------------<-/
100+
"
101+
102+
"Check Figure 3 in ABCD: Eliminating Array Bounds Checks on Demand for it to make *a bit* more sense"
103+
104+
cfg := DRControlFlowGraph new.
105+
106+
b_end := cfg newBasicBlockWith: [ :block | block storeSInt64: 2 at: 888 ].
107+
b_check_failed := cfg newBasicBlockWith: [ :block | block storeSInt64: 2 at: 777 ].
108+
109+
b_i := cfg newBasicBlockWith: [ :block |
110+
dummyOperand1 := block copy: 1234.
111+
limit0 := block loadFramePointer.
112+
st0 := block copy: -1.
113+
].
114+
cfg initialBasicBlock jumpTo: b_i.
115+
116+
b_if2_after_check2 := cfg newBasicBlockWith: [ :block |
117+
j4 := block add: 1 to: dummyOperand1 "j1".
118+
].
119+
120+
b_if2_after_check1 := cfg newBasicBlockWith: [ :block | |check|
121+
t0 := block add: 1 to: dummyOperand1 "j1".
122+
check := block less: t0 than: limit0.
123+
block jumpIf: check to: b_if2_after_check2 ifFalseTo: b_check_failed.
124+
].
125+
b_if2 := cfg newBasicBlockWith: [ :block | |check|
126+
check := block less: dummyOperand1 "j1" than: limit0.
127+
block jumpIf: check to: b_if2_after_check1 ifFalseTo: b_check_failed.
128+
].
129+
130+
b_for := cfg newBasicBlock.
131+
132+
b_if2_after_check2 jumpTo: b_for.
133+
134+
b_if1 := cfg newBasicBlockWith: [ :block |
135+
st3 := block add: 1 to: dummyOperand1 "st1".
136+
limit3 := block subtract: 1 from: dummyOperand1 "limit1".
137+
j0 := block copy: st3.
138+
block jumpTo: b_for.
139+
].
140+
141+
cmp_for := b_for less: dummyOperand1 than: limit3.
142+
jmp_for := b_for jumpIf: cmp_for to: b_if2 ifFalseTo: b_i.
143+
144+
j1 := b_for phiWithVariables: {1234 asDRValue . 4567 asDRValue }.
145+
cmp_for replaceOperand: dummyOperand1 by: j1.
146+
147+
b_if2_after_check2 instructions first replaceOperand: dummyOperand1 by: j1.
148+
b_if2_after_check1 instructions first replaceOperand: dummyOperand1 by: j1.
149+
b_if2 instructions first replaceOperand: dummyOperand1 by: j1.
150+
151+
j1 replaceOperand: j1 operands first by: j0.
152+
j1 replaceOperand: j1 operands second by: j4.
153+
154+
b_while := cfg newBasicBlockWith: [ :block | |cmp|
155+
limit1 := block phiWithVariables: {}.
156+
st1 := block phiWithVariables: {}.
157+
cmp := block less: st1 than: limit1.
158+
block jumpIf: cmp to: b_if1 ifFalseTo: b_end
159+
].
160+
161+
b_if1 instructions first replaceOperand: dummyOperand1 by: st1.
162+
b_if1 instructions second replaceOperand: dummyOperand1 by: limit1.
163+
164+
b_i jumpTo: b_while.
165+
166+
jmp_for falseBranch removePredecessor: jmp_for basicBlock.
167+
jmp_for newFalseBranch: b_while.
168+
jmp_for basicBlock predecessors add: jmp_for basicBlock predecessors removeFirst.
169+
170+
limit1 operands: {limit0. limit3}.
171+
st1 operands: {st0. st3}.
172+
173+
cfg validate.
174+
175+
176+
self optimize: cfg with: { DRPiABCDDeadBranchElimination }.
177+
178+
179+
self assert: b_if2 successors size equals: 1.
180+
self assert: b_if2 successors unique ~= b_check_failed.
181+
182+
self assert: b_if2_after_check1 successors size equals: 1.
183+
self assert: b_if2 successors unique ~= b_check_failed.
184+
185+
self assert: (cfg blocks includes: b_check_failed) not.
186+
187+
]

Druid-Tests/DRPiNodesInsertionTest.class.st

Lines changed: 0 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -6,92 +6,6 @@ Class {
66
#tag : 'Optimizations'
77
}
88

9-
{ #category : 'tests' }
10-
DRPiNodesInsertionTest >> testABCDGraph [
11-
| cfg b_i b_end limit0 limit1 st0 st1 b_while b_for j1 b_if1 st3 limit3 j0 b_if2 t0 b_if2_after_check2 b_if2_after_check1 j4 cmp_for dummyOperand1 jmp_for |
12-
13-
"This is the running example in the ABCD paper, reproduced it just to have something to validate our results agains
14-
The code is kinda ugly, I don't know if there's a nicer way to build this self-referencial/cyclic IRs"
15-
16-
" /->------------------------------------------------------------------------------------->-\
17-
/ V--<-------------------<-\ /->---------------------/->--\
18-
|b_i| -> |b_while| -> |b_if1| -> |b_for| -> |b_if2| -> |b_if2_after_check1| -> |b_if2_after_check2| -> |b_end|
19-
\-<------------------------------------------------------<-/
20-
"
21-
22-
"Check Figure 3 in ABCD: Eliminating Array Bounds Checks on Demand for it to make *a bit* more sense"
23-
24-
cfg := DRControlFlowGraph new.
25-
26-
b_end := cfg newBasicBlockWith: [ :block | block storeSInt64: 2 at: 888 ].
27-
28-
b_i := cfg newBasicBlockWith: [ :block |
29-
dummyOperand1 := block copy: 1234.
30-
limit0 := block loadFramePointer.
31-
st0 := block copy: -1.
32-
].
33-
cfg initialBasicBlock jumpTo: b_i.
34-
35-
b_if2_after_check2 := cfg newBasicBlockWith: [ :block |
36-
j4 := block add: 10 to: dummyOperand1 "j1".
37-
].
38-
39-
b_if2_after_check1 := cfg newBasicBlockWith: [ :block | |check|
40-
t0 := block add: 11 to: dummyOperand1 "j1".
41-
check := block less: t0 than: limit0.
42-
block jumpIf: check to: b_if2_after_check2 ifFalseTo: b_end.
43-
].
44-
b_if2 := cfg newBasicBlockWith: [ :block | |check|
45-
check := block less: dummyOperand1 "j1" than: limit0.
46-
block jumpIf: check to: b_if2_after_check1 ifFalseTo: b_end.
47-
].
48-
49-
b_for := cfg newBasicBlock.
50-
51-
b_if2_after_check2 jumpTo: b_for.
52-
53-
cmp_for := b_for less: dummyOperand1 than: limit0.
54-
jmp_for := b_for jumpIf: cmp_for to: b_if2 ifFalseTo: b_i.
55-
56-
j1 := b_for phiWithVariables: {1234 asDRValue . 4567 asDRValue }.
57-
cmp_for replaceOperand: dummyOperand1 by: j1.
58-
59-
b_if2_after_check2 instructions first replaceOperand: dummyOperand1 by: j1.
60-
b_if2_after_check1 instructions first replaceOperand: dummyOperand1 by: j1.
61-
b_if2 instructions first replaceOperand: dummyOperand1 by: j1.
62-
63-
b_if1 := cfg newBasicBlockWith: [ :block |
64-
st3 := block add: 12 to: dummyOperand1 "st1".
65-
limit3 := block subtract: 1 from: dummyOperand1 "limit1".
66-
j0 := block copy: st3.
67-
block jumpTo: b_for.
68-
].
69-
70-
j1 replaceOperand: j1 operands first by: j0.
71-
j1 replaceOperand: j1 operands second by: j4.
72-
73-
b_while := cfg newBasicBlockWith: [ :block | |cmp|
74-
limit1 := block phiWith: {limit0. limit3}.
75-
st1 := block phiWith: {st0. st3}.
76-
cmp := block less: st1 than: limit1.
77-
block jumpIf: cmp to: b_if1 ifFalseTo: b_end
78-
].
79-
80-
b_if1 instructions first replaceOperand: dummyOperand1 by: st1.
81-
b_if1 instructions second replaceOperand: dummyOperand1 by: limit1.
82-
83-
b_i jumpTo: b_while.
84-
85-
jmp_for falseBranch removePredecessor: jmp_for basicBlock.
86-
jmp_for newFalseBranch: b_while.
87-
jmp_for basicBlock predecessors add: jmp_for basicBlock predecessors removeFirst.
88-
89-
90-
cfg validate.
91-
self insertPiNodes: cfg.
92-
93-
]
94-
959
{ #category : 'tests' }
9610
DRPiNodesInsertionTest >> testInsertPiNodesEqualsCondition [
9711

Druid-Tests/DRPiNodesTest.class.st

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ DRPiNodesTest >> insertPiNodes: cfg [
2323
DRPiNodesTest >> optimize: cfg with: optimisations [
2424

2525
self insertPiNodes: cfg.
26-
26+
cfg inspect.
2727
cfg applyOptimisation: ((optimisations collect: [ :o | o new ]) reduce: [ :o1 :o2 | o1 then: o2 ]).
28-
28+
cfg inspect.
2929
cfg applyOptimisation: DRCopyPropagation new.
3030
cfg applyOptimisation: DRDeadCodeElimination new.
3131
]

0 commit comments

Comments
 (0)