-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathConstantComparisonPropagation.java
More file actions
152 lines (142 loc) · 7.01 KB
/
ConstantComparisonPropagation.java
File metadata and controls
152 lines (142 loc) · 7.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package com.compilerprogramming.ezlang.compiler;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.Map;
/**
* The goal of this pass is to detect conditional branching based
* on comparison with a constant. Example
*
* <pre>
* if (i == 5)
* {
* // some use of i
* }
* </pre>
*
* SCCP generates a lattice value for i, and if i changes outside the if block, then
* it is not classed as a constant. This means that inside the if block, we cannot exploit
* the knowledge that i is a constant locally.
*
* To enable this, we need to detect such comparisons and then insert a temp
* variable which is set to the constant value. The new temp variable is inserted
* inside the if block only and does not affect the meaning of i outside the if block.
* After this transformation we can run SCCP again to take advantage of the local
* knowledge.
*
* This technique can be extended to null checks too, but we do not do that yet.
*/
public class ConstantComparisonPropagation {
private final CompiledFunction function;
private DominatorTree domTree;
private Map<Register, SSAEdges.SSADef> ssaDefUse;
private boolean updated = false;
public ConstantComparisonPropagation(CompiledFunction function) {
this.function = function;
}
public boolean apply(EnumSet<Options> options) {
if (options.contains(Options.CCP)) {
updated = false;
domTree = new DominatorTree(function.entry);
ssaDefUse = SSAEdges.buildDefUseChains(function);
walkBlocks();
}
return updated;
}
private void walkBlocks() {
walkBlock(function.entry);
}
private void walkBlock(BasicBlock block) {
propagateConstantsInComparisons(block);
for (BasicBlock c : block.dominatedChildren) {
walkBlock(c);
}
}
private void propagateConstantsInComparisons(BasicBlock block) {
if (block == function.exit) return;
// Get terminal instruction
Instruction instruction = block.instructions.getLast();
if (instruction instanceof Instruction.ConditionalBranch cbr) {
if (cbr.condition() instanceof Operand.RegisterOperand conditionVar) {
SSAEdges.SSADef defUse = ssaDefUse.get(conditionVar.reg);
// If the condition var was result of == with constant value
if (defUse.instruction.block.bid == block.bid
&& defUse.instruction instanceof Instruction.Binary binary
&& binary.binOp.equals("==")) {
// Get the constant and the register operands
// from the binary
Operand.ConstantOperand constantOp = null;
Operand.RegisterOperand registerOp = null;
if (binary.left() instanceof Operand.ConstantOperand leftConstant
&& binary.right() instanceof Operand.RegisterOperand rightReg) {
constantOp = leftConstant;
registerOp = rightReg;
} else if (binary.right() instanceof Operand.ConstantOperand rightConstant
&& binary.left() instanceof Operand.RegisterOperand leftReg) {
constantOp = rightConstant;
registerOp = leftReg;
}
if (constantOp != null) {
// Since reg is constant in true branch
// We can replace all uses of reg with the constant
// in blocks dominated by the true block
BasicBlock trueBlock = cbr.trueBlock;
// I am not sure if this scenario can occur but
// for safety we check that the register we will
// replace is not immediately used inside the true block
// in a phi, because we intend to add the definition of
// the replacement after any phis
if (!checkUsedInPhi(trueBlock, registerOp.reg)) {
// Create a temp and move constant to it.
// We insert the new instruction at the top of the True Block,
// where it should dominate all uses of it
// We could just replace with a constant here instead of creating
// a temp and a move instruction but that would be less general a solution as it
// would not work for other scenarios such as null status which we will
// be adding in future
var replacementRegister = function.registerPool.newTempReg(registerOp.reg.type);
var defInst = new Instruction.Move(constantOp, new Operand.TempRegisterOperand(replacementRegister));
insertAtBeginning(trueBlock, defInst);
var replacementRegisterUses = SSAEdges.addDef(ssaDefUse, replacementRegister, defInst); // Update SSA Def Use chains, add def for new reg
Iterator<Instruction> useIter = ssaDefUse.get(registerOp.reg).useList.iterator();
while (useIter.hasNext()) {
Instruction use = useIter.next();
if (trueBlock.dominates(use.block)) {
use.replaceUse(registerOp.reg, replacementRegister);
// Update SSA Def use chains
useIter.remove(); // No longer a use of old register
replacementRegisterUses.addUse(use); // Use of new temp register
}
}
}
}
}
}
}
}
/* Check if the register is used in a phi instruction within the block */
private static boolean checkUsedInPhi(BasicBlock block, Register register) {
for (Instruction instruction : block.instructions) {
if (instruction instanceof Instruction.Phi phi) {
for (int i = 0; i < phi.numInputs(); i++) {
if (phi.isRegisterInput(i)) {
Register phiUse = phi.inputAsRegister(i);
if (phiUse.equals(register)) return true;
}
}
}
else break;
}
return false;
}
/* Insert instruction at the start of BB after any phis */
private static void insertAtBeginning(BasicBlock block, Instruction instruction) {
int pos = 0; // insertion point
for (; pos < block.instructions.size(); pos++) {
if (!(block.instructions.get(pos) instanceof Instruction.Phi))
break;
}
if (pos == block.instructions.size())
throw new IllegalStateException();
block.add(pos, instruction);
}
}