静态分析-常量传播

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

本文主要是根据南京大学的软件分析课程的作业2,写一下自己的解决方法和心得体会,必须要夸一夸两位软件分析课程的老师,讲的太好了,Tai-e教学版本的设计也非常适合新手!整个算法其实就是下面的一张图(取自南京大学软件分析的课件):
在这里插入图片描述


一、ConstantPropagation

1. newBoundaryFact(CFG)

这个主要是对应于边界的初始化,由于常量传播是前向分析,所以应该是:
在这里插入图片描述
但是需要注意的是我们应该把参数值初始化为NAC,我的理解是因为现在是过程内分析,我们无法知道参数的值,只能当作变量处理,所以是NAC,具体来说代码就是:

    @Override
    public CPFact newBoundaryFact(CFG<Stmt> cfg) {
        // TODO - finish me
        //首先处理参数,参数初始化为NAC,因为过程内分析无法确定参数是否为常量
        CPFact boundaryFact=new CPFact();
        for(Var param:cfg.getIR().getParams()){
            boundaryFact.update(param,Value.getNAC());
        }
        return boundaryFact;

    }

2. newInitialFact()

这一步就是除了entry以外的初始化
在这里插入图片描述
代码:

    @Override
    public CPFact newInitialFact() {
        // TODO - finish me
        return new CPFact();
    }

3. void meetInto(CPFact,CPFact)

这一步是处理将第一个Fact添加到第二个Fact中,因为是must分析,所以称之为meet
在这里插入图片描述

    @Override
    public void meetInto(CPFact fact, CPFact target) {
        // TODO - finish me
        //变量不重复则直接添加,变量重复则用meetValue
        fact.forEach((key,value) ->{
            if(!target.keySet().contains(key)){
                target.update(key,value);
            }else {
                Value newV=meetValue(fact.get(key),target.get(key));
                target.update(key,newV);
            }
        });
    }

很显然,如果target中没有fact中的变量,那么直接添加就好了;如果有,则需要对比两个的值,也就是下面的meetValue。

meetValue的主要思想是如果两个Value中有一个是UNDEF,那么meet值由另一个变量决定;如果有一个是NAC,那么一定是NAC;如果是两个常量,看值是否相等,如果不相等,是NAC,相等就继续保持该值。

    /**
     * Meets two Values.
     * 如果有一个值是UNDEF,那么meet的值由另一个决定
     * 如果有一个是NAC,那么meet的值是NAC
     * 如果都是常量,但是常量值不等,那么meet的值是NAC,否则是常量
     */
    public Value meetValue(Value v1, Value v2) {
        // TODO - finish me
        if(v1.isUndef()){
            return v2;
        }else if (v2.isUndef()){
            return v1;
        }else if (v1.isNAC() || v2.isNAC()){
            return Value.getNAC();
        }else {
            if(v1.getConstant()== v2.getConstant()){
                return v1;
            }else {
                return Value.getNAC();
            }
        }
    }

4. boolean transferNode(Stmt,CPFact,CPFact)

对应:
在这里插入图片描述
这里主要会有三种情况,一个是x=3这种常量直接赋值,一个是x=y,一个是x=a+b这种二元表达式的形式

  • x=3:直接更新x的值
  • x=y:更新x的值为y的值
  • x=a+b,这里我们认为a,b都是变量是因为Tai-e会把常量存在变量里面,具体大家可以去看作业2的要求。这种就比较复杂,需要获取到后面二元表达式的每个变量值和算数结果,这部分代码是evaluate。
    代码:
    @Override
    public boolean transferNode(Stmt stmt, CPFact in, CPFact out) {
        // TODO - finish me
        CPFact newOut=in.copy();
        if(stmt instanceof pascal.taie.ir.stmt.DefinitionStmt){
            if (stmt.getDef().isPresent()){
                LValue def=stmt.getDef().get();
                //LHS应该是变量
                if (def instanceof Var){
                    //如果是x=3或者是x=y这种类型,直接更新即可
                    if(stmt.getUses().size()==1){
                        RValue use=stmt.getUses().get(0);
                        //如果是常量
                        if(use instanceof IntLiteral){
                            IntLiteral cons=(IntLiteral) use;
                            newOut.update((Var) def,Value.makeConstant(cons.getValue()));
                        }
                        //如果是变量
                        else if(use instanceof Var){
                            Var useVar=(Var) use;
                            newOut.update((Var) def,newOut.get(useVar));
                        }
                    }else {
                        //如果是二元表达式类型
                        DefinitionStmt definitionStmt=(DefinitionStmt) stmt;
                        if (definitionStmt.getRValue() instanceof BinaryExp){
                            BinaryExp binaryExp=(BinaryExp) definitionStmt.getRValue();
                            Value newVal=evaluate(binaryExp,newOut);
                            newOut.update((Var) def,newVal);
                        }

                    }
                }
            }

        }
        if (!newOut.equals(out)){
            out.copyFrom(newOut);
            return true;
        }
        return false;
    }

这里不知道有没有更简单的方法了,但是我是用的switch和case…

 /**
     * 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) {
        // TODO - finish me
        BinaryExp binaryExp=(BinaryExp) exp;
        Var var1=binaryExp.getOperand1();
        Var var2=binaryExp.getOperand2();

        if(in.get(var1).isConstant()&&in.get(var2).isConstant()){
            int cons1=in.get(var1).getConstant();
            int cons2=in.get(var2).getConstant();
            if (binaryExp instanceof ArithmeticExp){
                ArithmeticExp arithmeticExp=(ArithmeticExp) binaryExp;
                switch (arithmeticExp.getOperator().toString()){
                    case "+":
                        return Value.makeConstant(cons1+cons2);
                    case  "-":
                        return Value.makeConstant(cons1-cons2);
                    case "*":
                        return Value.makeConstant(cons1*cons2);
                    case "/":
                        if (cons2==0){
                            return Value.getUndef();
                        }
                        return Value.makeConstant(cons1/cons2);
                    case "%":
                        if(cons2==0){
                            return Value.getUndef();
                        }
                        return Value.makeConstant(cons1%cons2);
                }
            }else if(binaryExp instanceof ConditionExp){
                ConditionExp conditionExp=(ConditionExp) binaryExp;
                switch (conditionExp.getOperator().toString()){
                    case "==":
                        return Value.makeConstant(cons1==cons2?1:0);
                    case "!=":
                        return Value.makeConstant(cons1!=cons2?1:0);
                    case "<":
                        return Value.makeConstant(cons1<cons2?1:0);
                    case ">":
                        return Value.makeConstant(cons1>cons2?1:0);
                    case "<=":
                        return Value.makeConstant(cons1<=cons2?1:0);
                    case ">=":
                        return Value.makeConstant(cons1>=cons2?1:0);

                }
            }else if(binaryExp instanceof ShiftExp){
                ShiftExp shiftExp=(ShiftExp) binaryExp;
                switch (shiftExp.getOperator().toString()){
                    case "<<":
                        return Value.makeConstant(cons1 << cons2);
                    case ">>":
                        return Value.makeConstant(cons1 >> cons2);
                    case ">>>":
                        return Value.makeConstant(cons1 >>> cons2);
                }
            }else if (binaryExp instanceof BitwiseExp){
                BitwiseExp bitwiseExp=(BitwiseExp) binaryExp;
                switch (bitwiseExp.getOperator().toString()){
                    case "|":
                        return Value.makeConstant(cons1|cons2);
                    case "&":
                        return Value.makeConstant(cons1&cons2);
                    case "^":
                        return Value.makeConstant(cons1^cons2);
                }
            }
        }else if (in.get(var1).isNAC()||in.get(var2).isNAC()){
            return Value.getNAC();
        }
        return Value.getUndef();
    }

二、Solver.initializeForward(CFG,DataflowResult)

对应:
在这里插入图片描述
代码:就是要完成所有节点的初始化

    protected void initializeForward(CFG<Node> cfg, DataflowResult<Node, Fact> result) {
        // TODO - finish me
        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());
            }
        }
    }

三、WorkListSolver.doSolveForward(CFG,DataflowResult)

对应:
在这里插入图片描述
代码:for循环里面的函数我们之前已经通过meetInto和transferNode完成了,所以主要是完成外层的循环,那么只有OUT更新了,它的后续加入worklist继续更新即可,因为如果一个node的IN不更新,那么OUT也不会更新,而它的IN就是它的prenode的OUT。

    @Override
    protected void doSolveForward(CFG<Node> cfg, DataflowResult<Node, Fact> result) {
        // TODO - finish me
        List<Node> worklist=new ArrayList<>();
        //首先将所有的node都加入到worklist中
        for(Node node:cfg){
            worklist.add(node);
        }
        while (!worklist.isEmpty()){
            Node nowNode=worklist.get(0);
            if (!cfg.isEntry(nowNode)) {
                for (Node pred:cfg.getPredsOf(nowNode)){
                    analysis.meetInto(result.getOutFact(pred),result.getInFact(nowNode) );
                }
                if (analysis.transferNode(nowNode,result.getInFact(nowNode),result.getOutFact(nowNode))){
                    //如果out改变了,那么就后续节点加入worklist
                    for (Node succ:cfg.getSuccsOf(nowNode)){
                        worklist.add(succ);
                    }
                }
            }
            worklist.remove(0);
        }
    }

四、运算结果

在这里插入图片描述

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值