其实实验材料说的已经比较清楚了,但是我还是谈一下我自己对这个实验的一些理解。
!!! 我是先直接给答案的。但是我是建议先通过实验材料尝试自己先解决的。
首先要看几遍实验材料。然后我说的这几个点个人感觉可以帮助你理解这个实验的整体流程。
答案
Solver
protected void initializeForward(CFG<Node> cfg, DataflowResult<Node, Fact> result) {
// TODO - finish me
result.setOutFact(cfg.getEntry(),analysis.newBoundaryFact(cfg));
for (Node node : cfg.getNodes()) {
if(!cfg.isEntry(node)){
result.setOutFact(node,analysis.newInitialFact());
}
result.setInFact(node, analysis.newInitialFact());
}
}
WorkListSolver
protected void doSolveForward(CFG<Node> cfg, DataflowResult<Node, Fact> result) {
// TODO - finish me
initializeForward(cfg,result);
ArrayList<Node> worklist = new ArrayList<>();
for (Node node : cfg) {
if(!cfg.isEntry(node)){
worklist.add(node);
}
}
while(!worklist.isEmpty()){
Node node = worklist.remove(0);
Fact inFact = result.getInFact(node);
for (Node pred : cfg.getPredsOf(node)) {
analysis.meetInto(result.getOutFact(pred),inFact);
}
boolean b = analysis.transferNode(node, inFact, result.getOutFact(node));
if(!b){
worklist.addAll(cfg.getSuccsOf(node));
}
}
}
ConstantPropagation
public CPFact newBoundaryFact(CFG<Stmt> cfg) {
// TODO - finish me
CPFact cpFact = new CPFact();
for (Var param : cfg.getIR().getParams()) {
if (canHoldInt(param)) { // 只考虑可转换int类型的参数
cpFact.update(param, Value.getNAC()); // 建立参数到格上值(NAC)的映射
}
}
return cpFact;
}
@Override
public CPFact newInitialFact() {
// TODO - finish me
return new CPFact();
}
@Override
public void meetInto(CPFact fact, CPFact target) {
// TODO - finish me
for (Var var : fact.keySet()) {
Value value1 = fact.get(var);
Value value2 = target.get(var);
if(value2 != null){
target.update(var,meetValue(value1,value2));
}else{
target.update(var,value1);
}
}
}
/**
* Meets two Values.
*/
public Value meetValue(Value v1, Value v2) {
// TODO - finish me
if(v1.isNAC() || v2.isNAC()){
return Value.getNAC();
}
if(v1.isUndef()){
return v2;
}else if(v2.isUndef()){
return v1;
}
return v1.getConstant() == v2.getConstant() ? v1:Value.getNAC() ;
}
@Override
public boolean transferNode(Stmt stmt, CPFact in, CPFact out) {
// TODO - finish me
CPFact tmp = out.copy();
if(stmt instanceof DefinitionStmt definitionStmt // 在检查类型的同时将 stmt 转化为 DefinitionStmt 类型。
&& stmt.getDef().isPresent()
&& stmt.getDef().get() instanceof Var
// String y = "asd"
// 像这种,canHoldInt就是false,就直接跳过
&& canHoldInt((Var) stmt.getDef().get())){
Var lValue = (Var)definitionStmt.getLValue();
RValue rValue = definitionStmt.getRValue();
Value val = evaluate(rValue, in);
// 这一条语句必须写在out.copyFrom(in);的上面
out.copyFrom(in);
out.update(lValue,val);
}else{
// 不是赋值语句,就运用恒等函数
out.copyFrom(in);
}
return tmp.equals(out);
}
public static Value evaluate(Exp exp, CPFact in) {
// TODO - finish me
if(exp instanceof Var){
return in.get((Var) exp);
}else if (exp instanceof IntLiteral) {
return Value.makeConstant(((IntLiteral)exp).getValue());
}else if (exp instanceof BinaryExp) {
Value value1 = in.get(((BinaryExp) exp).getOperand1());
Value value2 = in.get(((BinaryExp) exp).getOperand2());
if(value1.isNAC() || value2.isNAC()){
// 易错点1:NAC / 0 = Undef
if (value1.isNAC() && value2.isConstant() && exp instanceof ArithmeticExp) {
ArithmeticExp.Op operator = ((ArithmeticExp) exp).getOperator();
if (operator == ArithmeticExp.Op.DIV || operator == ArithmeticExp.Op.REM) {
if (value2.getConstant() == 0) return Value.getUndef();
}
}
return Value.getNAC();
}
if (value1.isUndef() || value2.isUndef()) {
return Value.getUndef();
}
BinaryExp.Op operator = ((BinaryExp) exp).getOperator();
if(exp instanceof ArithmeticExp){
return switch ((ArithmeticExp.Op) operator) {
case ADD -> Value.makeConstant(value1.getConstant() + value2.getConstant());
case SUB -> Value.makeConstant(value1.getConstant() - value2.getConstant());
case MUL -> Value.makeConstant(value1.getConstant() * value2.getConstant());
case REM -> value2.getConstant() == 0 ? Value.getUndef(): Value.makeConstant(value1.getConstant() % value2.getConstant());
case DIV -> value2.getConstant() == 0 ? Value.getUndef(): Value.makeConstant(value1.getConstant() / value2.getConstant());
};
} else if (exp instanceof ShiftExp) {
return switch ((ShiftExp.Op)operator){
case SHL -> Value.makeConstant(value1.getConstant() << value2.getConstant());
case SHR -> Value.makeConstant(value1.getConstant() >> value2.getConstant());
case USHR -> Value.makeConstant(value1.getConstant() >>> value2.getConstant());
};
} else if (exp instanceof BitwiseExp) {
return switch ((BitwiseExp.Op)operator){
case OR -> Value.makeConstant(value1.getConstant() | value2.getConstant());
case AND -> Value.makeConstant(value1.getConstant() & value2.getConstant());
case XOR -> Value.makeConstant(value1.getConstant() ^ value2.getConstant());
};
}else if (exp instanceof ConditionExp) {
return switch ((ConditionExp.Op)operator){
case EQ -> Value.makeConstant(value1.getConstant() == value2.getConstant() ? 1 : 0);
case NE -> Value.makeConstant(value1.getConstant() != value2.getConstant() ? 1 : 0);
case GE -> Value.makeConstant(value1.getConstant() >= value2.getConstant() ? 1 : 0);
case GT -> Value.makeConstant(value1.getConstant() > value2.getConstant() ? 1 : 0);
case LE -> Value.makeConstant(value1.getConstant() <= value2.getConstant() ? 1 : 0);
case LT -> Value.makeConstant(value1.getConstant() < value2.getConstant() ? 1 : 0);
};
}else{
return Value.getNAC();
}
}
return Value.getNAC();
}
思路
大致流程是先后执行 Solver 和 WorkListSolver 里面的这些方法。
Forward Analysis ,没有什么好说的。 目前我们学过的里面只有 上个实验 A1的 Live Variables 是 backwords。
initialize 函数就是 这个算法的前三行, 就是初始化好一个cfg 。然后执行doSolveForward ,程序就开始运转了。

public DataflowResult<Node, Fact> solve(CFG<Node> cfg) {
DataflowResult<Node, Fact> result = initialize(cfg);
doSolve(cfg, result);
return 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) {
// TODO - finish me
result.setOutFact(cfg.getEntry(),analysis.newBoundaryFact(cfg));
for (Node node : cfg.getNodes()) {
if(!cfg.isEntry(node)){
result.setOutFact(node,analysis.newInitialFact());
}
result.setInFact(node, analysis.newInitialFact());
}
}
private void doSolve(CFG<Node> cfg, DataflowResult<Node, Fact> result) {
if (analysis.isForward()) {
doSolveForward(cfg, result);
} else {
doSolveBackward(cfg, result);
}
}
protected void doSolveForward(CFG<Node> cfg, DataflowResult<Node, Fact> result) {
// TODO - finish me
initializeForward(cfg,result);
ArrayList<Node> worklist = new ArrayList<>();
for (Node node : cfg) {
if(!cfg.isEntry(node)){
worklist.add(node);
}
}
while(!worklist.isEmpty()){
Node node = worklist.remove(0);
Fact inFact = result.getInFact(node);
for (Node pred : cfg.getPredsOf(node)) {
analysis.meetInto(result.getOutFact(pred),inFact);
}
boolean b = analysis.transferNode(node, inFact, result.getOutFact(node));
if(!b){
worklist.addAll(cfg.getSuccsOf(node));
}
}
}
worklist
doSolveForward 这个算法和上个实验的区别之一就在于这个worklist, 先把所有的node 添加进去,然后依次取出,进行如下操作。也就是对应的 analysis.transferNode() 函数。

如果OUT发生了改变,则将该node所有的后继节点添加到worklist当中。

效果
以 Assign 这个案例 为例。
class Assign {
void assign() {
int x = 1, y;
x = 2;
x = 3;
x = 4;
y = x;
}
}
out
-------------------- <Assign: void <init>()> (constprop) --------------------
[0@L1] invokespecial %this.<java.lang.Object: void <init>()>(); {}
[1@L1] return; {}
-------------------- <Assign: void assign()> (constprop) --------------------
[0@L4] x = 1; {x=1}
[1@L5] x = 2; {x=2}
[2@L6] x = 3; {x=3}
[3@L7] x = 4; {x=4}
[4@L8] y = x; {x=4, y=4}
[5@L8] return; {x=4, y=4}
stmt
这个实验和上个实验的另一个变化是这个stmt ,
A1
上次的stmt是这样的。只用关心d = a + b; 中的 d (Var)即可
[0@L4] d = a + b; [a, d]
[1@L5] b = d; [a, b]
[2@L6] c = a; [b]
[3@L7] return b; []


A2
但是这次我们有了key和value,需要同时兼顾key和value。
[0@L4] x = 1; {x=1}
[1@L5] x = 2; {x=2}
[2@L6] x = 3; {x=3}
[3@L7] x = 4; {x=4}
[4@L8] y = x; {x=4, y=4}
[5@L8] return; {x=4, y=4}
所谓的L和R其实就是 left和right ,就是等号的左边和右边。
public abstract class DefinitionStmt<L extends LValue, R extends RValue>
extends AbstractStmt {
public abstract @Nullable L getLValue();
public abstract R getRValue();
}
Fact
A1的 fact 就是一个 set。
A2的 fact 是一个 map ,相当于 out中 {x=4, y=4} 这一部分。需要 有 key和value
CPFact 继承于 MapFact 而 后者中的 map 属性 protected final Map<K, V> map; 存放着我们的数据。
{x=4, y=4}
transferNode
再次回到这个doSolveForward 方法中
while(!worklist.isEmpty()){
Node node = worklist.remove(0);
Fact inFact = result.getInFact(node);
for (Node pred : cfg.getPredsOf(node)) {
analysis.meetInto(result.getOutFact(pred),inFact);
}
boolean b = analysis.transferNode(node, inFact, result.getOutFact(node));
if(!b){
worklist.addAll(cfg.getSuccsOf(node));
}
}

然后进入到 transferNode 方法。
public boolean transferNode(Stmt stmt, CPFact in, CPFact out) {
// TODO - finish me
CPFact tmp = out.copy();
if(stmt instanceof DefinitionStmt definitionStmt // 在检查类型的同时将 stmt 转化为 DefinitionStmt 类型。
&& stmt.getDef().isPresent()
&& stmt.getDef().get() instanceof Var
&& canHoldInt((Var) stmt.getDef().get())){ // 本次作业只关注 int 类型的常量传播。提供了 canHoldInt(Var) 方法来判断一个变量能否储存 int 类型的值
Var lValue = (Var)definitionStmt.getLValue();
RValue rValue = definitionStmt.getRValue();
Value val = evaluate(rValue, in); // 计算 stmt rValue 的值
out.copyFrom(in); // 把in复制到out
out.update(lValue,val); // 看源码里面的注释,这里不多解释。
}else{
out.copyFrom(in);
}
return tmp.equals(out);
}

evaluate
这个其实代码虽长,但是也是很简单的。
前两个elseif没有什么好说的。
if(exp instanceof Var){
return in.get((Var) exp);
}else if (exp instanceof IntLiteral) {
return Value.makeConstant(((IntLiteral)exp).getValue());
}else if (exp instanceof BinaryExp) {
在 Tai-e 中把常量称作字面量(Literals)。每个
IntLiteral类的实例都表示一个程序中的整数字面量。你可以通过调用getValue()方法来获取它的值。
BinaryExp 这个类代表程序中的二元表达式。这个类的各个子类对应了表 1 中的不同种类的二元表达式,并且每个子类中都有一个内部枚举类型用于表示该类支持的运算符。例如枚举类型
ArithmeticExp.Op就代表了ArithmeticExp(算术表达式类)所支持的运算符,也就是+ - * /和%。
然后说 exp instanceof BinaryExp 这个分支。
先获取op两边的 value。
Value value1 = in.get(((BinaryExp) exp).getOperand1());
Value value2 = in.get(((BinaryExp) exp).getOperand2());
然后根据ppt 246页这个。处理一些特殊情况,然后注意一个NAC / 0 = Undef 的情况。

if(value1.isNAC() || value2.isNAC()){
if (value1.isNAC() && value2.isConstant() && exp instanceof ArithmeticExp) {
ArithmeticExp.Op operator = ((ArithmeticExp) exp).getOperator();
if (operator == ArithmeticExp.Op.DIV || operator == ArithmeticExp.Op.REM) {
if (value2.getConstant() == 0) return Value.getUndef();
}
}
return Value.getNAC();
}
if (value1.isUndef() || value2.isUndef()) {
return Value.getUndef();
}
然后根据不同的operator 进行对应的运算就可以了。
397






