南京大学《软件分析》实验A2 简单讲解

其实实验材料说的已经比较清楚了,但是我还是谈一下我自己对这个实验的一些理解。

!!! 我是先直接给答案的。但是我是建议先通过实验材料尝试自己先解决的。

首先要看几遍实验材料。然后我说的这几个点个人感觉可以帮助你理解这个实验的整体流程。

答案

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 进行对应的运算就可以了。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值