常量传播
常量传播(Constant Propagation)是一种编译器优化技术,旨在识别和利用在程序中使用的常量值,以替换变量或表达式的计算。这种优化可以显著提高程序的性能,因为它可以减少不必要的计算并消除冗余的代码。以下是常量传播的一些关键概念和工作原理:
常量值:在编程中,常量是指在程序执行期间不会改变的值,例如整数、浮点数、字符串等。常量传播的目标是识别和利用这些不变的值。
基本思想:常量传播的基本思想是跟踪程序中各个变量和表达式的值,并在编译器的优化阶段用已知的常量值替换它们。这样可以减少不必要的运算,提高程序的执行速度。
数据流分析:常量传播通常依赖于数据流分析技术,通过分析变量和常量值在程序中的传播和依赖关系,以确定哪些变量在某个点(例如,某个基本块或路径)的值是常量。
替代常量:一旦编译器确定某个变量或表达式的值在某个点是常量,它就会用该常量值替代变量或表达式,从而消除了不必要的计算。这个过程被称为"常量折叠"(Constant Folding)。
条件分支:在常量传播中,编译器还会处理条件分支语句,例如 if 和 switch。如果编译器能够确定条件表达式的值在编译时是常量,它可以根据条件的值来消除或简化分支。
限制和挑战:常量传播并不总是可行的,因为有些程序可能包含动态生成的数据,或者依赖于用户输入等不确定因素。在这种情况下,编译器可能无法确定常量值,因此无法进行常量传播。
总的来说,常量传播是一种用于提高程序性能的重要编译器优化技术。它可以消除不必要的计算和代码,从而减少程序的执行时间和资源消耗。然而,它需要精确的数据流分析和对程序的深入理解,以确保正确性和安全性。
java样例
class BranchConstant {
void constant1(boolean b) {
int x = 2;
int y = 2;
int z;
if (b) {
z = x + y;
} else {
z = x * y;
}
int n = z;
}
void constant2(boolean b) {
int x;
if (b) {
x = 10;
}
int y = x;
}
}
分析结果
-------------------- <BranchConstant: void <init>()> (constprop) --------------------
[0@L1] invokespecial %this.<java.lang.Object: void <init>()>(); {}
[1@L1] return; {}
-------------------- <BranchConstant: void constant1(boolean)> (constprop) --------------------
[0@L4] x = 2; {b=NAC, x=2}
[1@L5] y = 2; {b=NAC, x=2, y=2}
[2@L7] %intconst0 = 0; {%intconst0=0, b=NAC, x=2, y=2}
[3@L7] if (b == %intconst0) goto 8; {%intconst0=0, b=NAC, x=2, y=2}
[4@L7] goto 5; {%intconst0=0, b=NAC, x=2, y=2}
[5@L7] nop; {%intconst0=0, b=NAC, x=2, y=2}
[6@L8] z = x + y; {%intconst0=0, b=NAC, x=2, y=2, z=4}
[7@L7] goto 10; {%intconst0=0, b=NAC, x=2, y=2, z=4}
[8@L7] nop; {%intconst0=0, b=NAC, x=2, y=2}
[9@L10] z = x * y; {%intconst0=0, b=NAC, x=2, y=2, z=4}
[10@L10] nop; {%intconst0=0, b=NAC, x=2, y=2, z=4}
[11@L12] n = z; {%intconst0=0, b=NAC, n=4, x=2, y=2, z=4}
[12@L12] return; {%intconst0=0, b=NAC, n=4, x=2, y=2, z=4}
-------------------- <BranchConstant: void constant2(boolean)> (constprop) --------------------
[0@L17] %intconst0 = 0; {%intconst0=0, b=NAC}
[1@L17] if (b == %intconst0) goto 5; {%intconst0=0, b=NAC}
[2@L17] goto 3; {%intconst0=0, b=NAC}
[3@L17] nop; {%intconst0=0, b=NAC}
[4@L18] x = 10; {%intconst0=0, b=NAC, x=10}
[5@L18] nop; {%intconst0=0, b=NAC, x=10}
[6@L20] y = x; {%intconst0=0, b=NAC, x=10, y=10}
[7@L20] return; {%intconst0=0, b=NAC, x=10, y=10}
常量传播算法设计
格定义:变量在程序中只有三种状态UNDEF、CONSTANT、NAC
方向:forward
边界条件:将方法的参数定义为NAC
初始化:将每一个BB处定义为空(UNDEF)
交汇处理:
转换函数:OUT[B] = gen ∪(IN[B] - {lvalue, _})
数据结构与算法实现
数据结构:
使用键值对来保存变量信息。实现如下:
public class CPFact extends MapFact<Var, Value> {
public CPFact() {
this(Collections.emptyMap());
}
private CPFact(Map<Var, Value> map) {
super(map);
}
/**
* @return the value of given variable in this fact,
* or UNDEF the variable is absent in this fact.
*/
@Override
public Value get(Var key) {
return map.getOrDefault(key, Value.getUndef());
}
@Override
public boolean update(Var key, Value value) {
if (value.isUndef()) {
// if the client code sets variable key to UNDEF,
// then we remove the variable from the CPFact
// as we use absence to represent UNDEF.
return remove(key) != null;
} else {
return super.update(key, value);
}
}
@Override
public CPFact copy() {
return new CPFact(this.map);
}
}
常亮传播算法实现
- ConstantPropagation。实现如下:
public class ConstantPropagation extends
AbstractDataflowAnalysis<Stmt, CPFact> {
public static final String ID = "constprop";
public ConstantPropagation(AnalysisConfig config) {
super(config);
}
@Override
public boolean isForward() {
return true;
}
@Override
public CPFact newBoundaryFact(CFG<Stmt> cfg) {
CPFact cpFact = new CPFact();
List<Var> vars = cfg.getIR().getParams();
for(Var var : vars){
if(canHoldInt(var))
cpFact.update(var, Value.getNAC());
}
return cpFact;
}
@Override
public CPFact newInitialFact() {
return new CPFact();
}
@Override
public void meetInto(CPFact fact, CPFact target) {
for(Var var : fact.keySet())
target.update(var, meetValue(fact.get(var), target.get(var)));
}
/**
* Meets two Values.
*/
public Value meetValue(Value v1, Value v2) {
if(v1.isNAC() || v2.isNAC())
return Value.getNAC();
if(v1.isUndef())
return v2;
if(v2.isUndef())
return v1;
if(v1.getConstant() == v2.getConstant())
return v1;
return Value.getNAC();
}
@Override
public boolean transferNode(Stmt stmt, CPFact in, CPFact out) {
// x = 2
// x = y
// x = y op z
//OUT = gen ∪ (IN - {x, _})
if(stmt instanceof DefinitionStmt<?, ?> definitionStmt && definitionStmt.getLValue() instanceof Var lValue && canHoldInt(lValue)){
RValue rValue = definitionStmt.getRValue();
Value value = evaluate(rValue, in);
CPFact tmp = in.copy();
tmp.update(lValue, value);
return out.copyFrom(tmp);
}
return out.copyFrom(in);
}
/**
* @return true if the given variable can hold integer value, otherwise false.
*/
public static boolean canHoldInt(Var var) {
Type type = var.getType();
if (type instanceof PrimitiveType) {
switch ((PrimitiveType) type) {
case BYTE:
case SHORT:
case INT:
case CHAR:
case BOOLEAN:
return true;
}
}
return false;
}
/**
* Evaluates the {@link Value} of given expression.
*
* @param exp the expression to be evaluated
* @param in IN fact of the statement
* @return the resulting {@link Value}
*/
public static Value evaluate(Exp exp, CPFact in) {
if(exp instanceof IntLiteral intLiteral){ // x = c
return Value.makeConstant(intLiteral.getValue());
}else if(exp instanceof Var var){ // x = y
return in.get(var);
}
//x = y op z
//x = val(y) op val(z) val(y) and val(z) is constant
//x = NAC val(y) or val(z) is NAC
//x = UNDEF op = %/ and val(z) == 0
if(! (exp instanceof BinaryExp binaryExp))
return Value.getNAC();
Var op1 = binaryExp.getOperand1();
Var op2 = binaryExp.getOperand2();
Value value1 = in.get(op1);
Value value2 = in.get(op2); //value2 = NAC/UNDEF -> res = NAC/UNDEF
if(!value2.isConstant())
return value2;
if(!value1.isConstant() && value2.getConstant() == 0 && binaryExp instanceof ArithmeticExp){// value1 == NAC/UNDEF value2 == 0 && op == (%/) -> undef
return switch (((ArithmeticExp) exp).getOperator()){
case DIV, REM -> Value.getUndef();
default -> Value.getNAC();
};
}
if(!value1.isConstant())
return value1;
// value1 is constant && value2 is constant(not zero)
int constant1 = value1.getConstant();
int constant2 = value2.getConstant();
if(binaryExp instanceof ArithmeticExp arithmeticExp){
return Value.makeConstant(
switch (arithmeticExp.getOperator()){
case ADD -> constant1 + constant2;
case SUB -> constant1 - constant2;
case MUL -> constant1 * constant2;
case DIV -> constant1 / constant2;
case REM -> constant1 % constant2;
});
}else if(binaryExp instanceof BitwiseExp bitwiseExp){
return Value.makeConstant(
switch (bitwiseExp.getOperator()){
case OR -> constant1 | constant2;
case AND -> constant1 & constant2;
case XOR -> constant1 ^ constant2;
}
);
}else if(binaryExp instanceof ConditionExp conditionExp){
return Value.makeConstant(
switch (conditionExp.getOperator()){
case EQ -> constant1 == constant2;
case NE -> constant1 != constant2;
case LT -> constant1 < constant2;
case GT -> constant1 > constant2;
case LE -> constant1 <= constant2;
case GE -> constant1 >= constant2;
} ? 1 : 0
);
}else if(binaryExp instanceof ShiftExp shiftExp){
return Value.makeConstant(
switch (shiftExp.getOperator()){
case SHL -> constant1 << constant2;
case SHR -> constant1 >> constant2;
case USHR -> constant1 >>> constant2;
}
);
} else {
return Value.getNAC();
}
}
}
- WorkList算法实现:
public abstract class Solver<Node, Fact> {
protected final DataflowAnalysis<Node, Fact> analysis;
protected Solver(DataflowAnalysis<Node, Fact> analysis) {
this.analysis = analysis;
}
/**
* Static factory method to create a new solver for given analysis.
*/
public static <Node, Fact> Solver<Node, Fact> makeSolver(
DataflowAnalysis<Node, Fact> analysis) {
return new WorkListSolver<>(analysis);
}
/**
* Starts this solver on the given CFG.
*
* @param cfg control-flow graph where the analysis is performed on
* @return the analysis result
*/
public DataflowResult<Node, Fact> solve(CFG<Node> cfg) {
DataflowResult<Node, Fact> result = initialize(cfg);
doSolve(cfg, result);
return result;
}
/**
* Creates and initializes a new data-flow result for given CFG.
*
* @return the initialized data-flow result
*/
private DataflowResult<Node, Fact> initialize(CFG<Node> cfg) {
DataflowResult<Node, Fact> result = new DataflowResult<>();
if (analysis.isForward()) {
initializeForward(cfg, result);
} else {
initializeBackward(cfg, result);
}
return result;
}
protected void initializeForward(CFG<Node> cfg, DataflowResult<Node, Fact> result) {
result.setInFact(cfg.getEntry(), analysis.newBoundaryFact(cfg));
result.setOutFact(cfg.getEntry(), analysis.newBoundaryFact(cfg));
for(Node node : cfg){
if(!cfg.isEntry(node)){
result.setInFact(node, analysis.newInitialFact());
result.setOutFact(node, analysis.newInitialFact());
}
}
}
protected void initializeBackward(CFG<Node> cfg, DataflowResult<Node, Fact> result) {
throw new UnsupportedOperationException();
}
/**
* Solves the data-flow problem for given CFG.
*/
private void doSolve(CFG<Node> cfg, DataflowResult<Node, Fact> result) {
if (analysis.isForward()) {
doSolveForward(cfg, result);
} else {
doSolveBackward(cfg, result);
}
}
protected abstract void doSolveForward(CFG<Node> cfg, DataflowResult<Node, Fact> result);
protected abstract void doSolveBackward(CFG<Node> cfg, DataflowResult<Node, Fact> result);
}
class WorkListSolver<Node, Fact> extends Solver<Node, Fact> {
WorkListSolver(DataflowAnalysis<Node, Fact> analysis) {
super(analysis);
}
@Override
protected void doSolveForward(CFG<Node> cfg, DataflowResult<Node, Fact> result) {
Queue<Node> workList = new ArrayDeque<>();
for(Node node : cfg){
workList.add(node);
}
while(!workList.isEmpty()){
Node node = workList.poll();
Fact inFact = result.getInFact(node);
Fact outFact = result.getOutFact(node);
for(Node pre : cfg.getPredsOf(node))
analysis.meetInto(result.getOutFact(pre), inFact);
if(analysis.transferNode(node, inFact, outFact)){
for(Node succ : cfg.getSuccsOf(node)){
if(!workList.contains(succ)){
workList.add(succ);
}
}
}
}
}
@Override
protected void doSolveBackward(CFG<Node> cfg, DataflowResult<Node, Fact> result) {
throw new UnsupportedOperationException();
}
}