【静态分析】软件分析课程实验A2-常量传播和Worklist求解器

Tai-e官网:

概述 | Tai-e

参考:

https://www.cnblogs.com/gonghr/p/17979609

--------------------------------------------------------

1 作业导览

  • 为 Java 实现常量传播算法。
  • 实现一个通用的 worklist 求解器,并用它来解决一些数据流分析问题,例如本次的常量传播。

在本次实验作业中,你要在 Tai-e 框架下实现常量传播算法和 worklist 求解器的关键部分。

2 实现常量传播

2.1 分析范围

在此次作业中,你需要实现针对 int 类型的常量传播。注意,在 Java 中,booleanbytecharshort 类型在运行时实际上都以 int 值的形式进行表示和计算,因此你的分析算法也应当能处理这些类型。其他基本数据类型(例如 longfloatdouble)以及引用类型(例如 class types、array types)不在此次作业的考虑范围内,所以你可以在分析时忽略它们的值。

至于语句的处理,你只需要关注等号左侧为变量且右侧只能是如下几类表达式的赋值语句:

  • 常量,如 x = 1
  • 变量,如 x = y
  • 二元运算表达式,如 x = a + bx = a >> b

下表列举出了除逻辑运算符以外所有作用在 int 类型上的二元运算符,请确保你的算法能正确处理它们:

运算类型运算符
Arithmetic+ - * / %
Condition== != < > <= >=
Shift<< >> >>>
Bitwise| & ^

逻辑运算符怎么办?

在上表中,我们没有列出逻辑运算符。这是因为,在 Java 中,逻辑运算符与(&&)和或(||)没有对应的字节码表示,而是以分支跳转的形式实现的。例如以下语句:

a = b || c;

将被转换为如下的语义等价的语句:

if (b) {
    t = true; // t is temp variable
} else if (c) {
    t = true;
} else {
    t = false;
}
a = t;

由于作业中的常量传播算法能够处理常量赋值(如 t = true)、变量赋值(如 a = t)和分支语句(如 if (b) {…}),所以它自然能够处理 &&||

至于等号左侧为变量、等号右侧为其它表达式的赋值语句,例如方法调用(x = m(...))和字段 load(x = o.f),你需要对它们进行保守的近似处理(也许会不够精确),即把它们当作 x = NAC。后续的作业将逐步解锁方法调用和字段访问的精确分析技巧。

对于上面没有提到的其它语句(例如字段存储 o.f = x),我们只需要使用恒等函数作为它们的 transfer 函数。在未来我们引入别名分析后,你就可以对字段存储进行精确处理了。

2.2 Tai-e 中你需要了解的类

这一节将会介绍与 IR 相关的类(pascal.taie.ir.*)以及和本次的分析算法相关的类(pascal.taie.analysis.*)。细致地了解它们将有助于完成常量分析算法。

  • pascal.taie.ir.IR

    这是 Tai-e 的 IR 的核心数据结构。它的每个实例储存了一个 Java 方法的各种信息,例如变量(variables)、参数(parameters)、语句(Stmts)等等。这里需要注意一点,一个方法体的中间表示由多个中间表示语句(Stmts)组成。如果想要加深理解,你可以自行阅读 API、源码实现以及注释。

  • pascal.taie.ir.exp.Exp

    我们已经在作业 1 中介绍过了这个接口。这是 Tai-e 的 IR 中的一个关键接口,用于表示程序中的所有表达式。它含有很多子类,对应各类具体的表达式。具体细节查看:【静态分析】软件分析课程实验A1-活跃变量分析和迭代求解器-CSDN博客

  • 在本次作业中,你需要处理更多 Exp 接口的子类。下图是一个继承关系的简单示意图(已略去与本次作业无关的类)。

  • 下面我们将逐一介绍这些子类。

  • pascal.taie.ir.exp.Var

    这个类代表 IR 中的变量。

  • pascal.taie.ir.exp.IntLiteral

    根据 Java 的语言规范,我们在 Tai-e 中把常量称作字面量(Literals)。每个 IntLiteral 类的实例都表示一个程序中的整数字面量。你可以通过调用 getValue() 方法来获取它的值。

  • pascal.taie.ir.exp.BinaryExp

    这个类代表程序中的二元表达式。这个类的各个子类对应了不同种类的二元表达式,并且每个子类中都有一个内部枚举类型用于表示该类支持的运算符。例如枚举类型 ArithmeticExp.Op 就代表了ArithmeticExp(算术表达式类)所支持的运算符,也就是 + - * /%

需要指出的是,在 Tai-e 中,BinaryExp 的两个操作数都是 Var 类型的。例如下面的语句

x = y + 6;

在 Tai-e 中会被转化成如下的 IR:

%intconst0 = 6;     // %intconst* are temp variables introduced
x = y + %intconst0; // by Tai-e to hold constant int values
  • 这样的设计简化了分析的实现:在获取 BinaryExp 的操作数时,你只需要考虑它是变量的这一种可能,而不用担心它是常量或其它可能了。

  • pascal.taie.ir.stmt.DefinitionStmt

    这是 Stmt 的一个子类。它表示了程序中所有的赋值语句,(即形如 x = yx = m(…) 的语句)。这个类很简单。你可以通过阅读源码来决定如何使用它。

  • pascal.taie.analysis.dataflow.analysis.DataflowAnalysis

    这是具体数据流分析算法需要实现的接口。和作业 1 一样,它会被求解器调用。在本次作业中你只需要关注前 5 个 API。这些 API 会被你在后面完成的 worklist 求解器调用。

  • pascal.taie.analysis.dataflow.analysis.constprop.Value

    这个类表示了常量分析中格上的抽象值。格的定义见【静态分析】静态分析笔记04 - 数据流分析(理论)-CSDN博客

  • 它的代码和注释解释了它的用法。你应该用下列的静态方法获取格上抽象值(即该类的实例):

    • Value getNAC(): 返回 NAC
    • Value getUndef(): 返回 UNDEF
    • Value makeConstant(int): 返回给定整数在格上对应的抽象值
  • pascal.taie.analysis.dataflow.analysis.constprop.CPFact

    这个类表示常量传播中的 data facts,即一个从变量(Var)到格上抽象值(Value)的映射。该类提供了各种 map 相关的操作,例如键值对的查询、更新等等。这些操作大多继承自 pascal.taie.analysis.dataflow.fact.MapFact。这些类的注释都很充分,所以你应该通过阅读源码来决定如何使用其中的 API。

  • pascal.taie.analysis.dataflow.analysis.constprop.ConstantPropagation

    这个类实现了 DataflowAnalysis。你需要在其中编写完整的常量传播算法。具体要求见第 2.3 节。

2.3 你的任务 [重点!]

首先,你需要完成 ConstantPropagation 的下述 API:

  • CPFact newBoundaryFact(CFG)

  • CPFact newInitialFact()

  • void meetInto(CPFact,CPFact)

  • boolean transferNode(Stmt,CPFact,CPFact)

你已经在作业 1 中见到过这几个 API,他们是从 DataflowAnalysis 中继承下来的,需要注意的是:在实现 newBoundaryFact() 的时候,你要小心地处理每个会被分析的方法的参数。具体来说,你要将它们的值初始化为 NAC (请思考:为什么要这么做?)。

原因是为了 safe-approximation ,我们不知道通过形参传递过来的参数是否是常量,所以为了安全,假设所有参数都是 NAC ,当然这样会导致精度损失问题,后面通过过程间分析可以有效解决这个问题。

提示

正如第 2.1 节中提到的,本次作业只关注 int 类型的常量传播。为了实现这一点,框架代码在 ConstantPropagation 类中提供了 canHoldInt(Var) 方法来判断一个变量能否储存 int 类型的值。你需要利用这个方法来判断一个变量是否在本次作业的分析范围内,并忽略那些不在范围内的变量(例如 float 类型的变量)。

此外,你还需要实现下面两个辅助方法:

  • Value meetValue(Value,Value)

    该方法对应着格上的 meet 操作(⊓),详情见【静态分析】静态分析笔记04 - 数据流分析(理论)-CSDN博客

  • 你应当在 meetInto() 方法中调用它。

  • Value evaluate(Exp,CPFact)

    这个方法会计算表达式(Exp)的值(Value)。当然,此处的值是格上的抽象值。你需要参考【静态分析】静态分析笔记04 - 数据流分析(理论)-CSDN博客

  • 来实现它的三种情况。对于其它情况,该方法会像我们在第 2.1 节提到的那样返回 NAC你应该在 transferNode() 方法中调用它来进行表达式的求值。

提示

  1. 和作业 1 一样,我们对 meetInto() 的设计比较特殊。如果你不记得了,可以再回顾一下作业 1 文档的相关部分。
  2. 条件表达式(如 a == b)的值由 0(若为 False)和 1(若为 True)来表示。
  3. 对于除以 0 的情况(出现在 /% 中),我们规定结果为 UNDEF。例如,对于 x = a / 0x 的值将会是 UNDEF

算法伪代码描述

  • newBoundaryFact :负责创建和初始化虚拟结点的 Data Flow Fact。但是注意要把方法参数初始化为 NAC 。 根据题目要求,不是所有类型的参数都考虑,只有能转换成 int 类型的参数才考虑,所以别忘了用 canHoldInt 过滤一下。
  • newInitialFact :负责创建和初始化控制流图中除了 EntryExit 之外的结点的 Data Flow Fact。控制流图中一个结点的 INOUT 分别对应一个 Data Flow Fact ,记录当前程序点时变量的状态。直接创建一个空的 CPFact 即可,方法体内还没有开始扫描。
  • meetInto :负责处理 transfer function 之前可能遇到多个 OUT 时的合并处理。具体的合并操作通过调用 meetValue 来处理。
  • meetValue :负责对格上的值进行合并。

meet 操作

  • NAC ⊓ v = NAC(非常量)
  • UNDEF ⊓ v = v(未初始化的变量不是我们分析的目标)
  • c ⊓ v = ?
    • c ⊓ c = c
    • c1 ⊓ c2 = NAC
  • transferNode :负责实现控制流图中结点的 transfer function 。如果 OUT 改变,返回 true ;否则返回 false

stmt 表示结点中的一条中间表示,一个结点只有一个中间表示。

题目要求只需要对赋值语句处理,所以用 DefinitionStmt 类型过滤。

对于所有赋值语句,只考虑具有左值,并且左值是变量且类型可以转换成 int 的语句。这些语句的右值是一个表达式,可能是常量,也能是变量、二元表达式。这个右值表达式的值将通过 evaluate 函数计算。

对于其他类型的语句,不做处理,out 直接复制 in 即可,相当于经过一个恒等函数。

  • evaluate :负责表达式值的计算。

表达式分三种情况讨论

  1. 常量:直接赋值。
  2. 变量:获取变量的值再赋值。
  3. 二元运算:针对共 12 中运算分别处理。

/*
 * Tai-e: A Static Analysis Framework for Java
 *
 * Copyright (C) 2022 Tian Tan <tiantan@nju.edu.cn>
 * Copyright (C) 2022 Yue Li <yueli@nju.edu.cn>
 *
 * This file is part of Tai-e.
 *
 * Tai-e is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation, either version 3
 * of the License, or (at your option) any later version.
 *
 * Tai-e is distributed in the hope that it will be useful,but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
 * Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with Tai-e. If not, see <https://www.gnu.org/licenses/>.
 */

package pascal.taie.analysis.dataflow.analysis.constprop;

import pascal.taie.analysis.dataflow.analysis.AbstractDataflowAnalysis;
import pascal.taie.analysis.graph.cfg.CFG;
import pascal.taie.config.AnalysisConfig;
import pascal.taie.ir.IR;
import pascal.taie.ir.exp.ArithmeticExp;
import pascal.taie.ir.exp.BinaryExp;
import pascal.taie.ir.exp.BitwiseExp;
import pascal.taie.ir.exp.ConditionExp;
import pascal.taie.ir.exp.Exp;
import pascal.taie.ir.exp.IntLiteral;
import pascal.taie.ir.exp.ShiftExp;
import pascal.taie.ir.exp.Var;
import pascal.taie.ir.stmt.DefinitionStmt;
import pascal.taie.ir.stmt.Stmt;
import pascal.taie.language.type.PrimitiveType;
import pascal.taie.language.type.Type;
import pascal.taie.util.AnalysisException;

import pascal.taie.ir.exp.*;


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) {
        // 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 v1 = fact.get(var);
            Value v2 = target.get(var);
            target.update(var, meetValue(v1, v2));
        }
    }

    /**
     * Meets two Values.
     */
    public Value meetValue(Value v1, Value v2) {
        // TODO - finish me
        if (v1.isNAC() || v2.isNAC()) {
            return Value.getNAC();
        } else if (v1.isUndef()) {
            return v2;
        } else if (v2.isUndef()) {
            return v1;
        } else if (v1.isConstant() && v2.isConstant()) {
            if (v1.getConstant() == v2.getConstant()) {
                return v1;
            } else {
                return Value.getNAC();
            }
        } else {
            return Value.getNAC();
        }
    }

    @Override
    public boolean transferNode(Stmt stmt, CPFact in, CPFact out) {
        // TODO - finish me
        CPFact copy = in.copy();   // 复制in给copy,避免影响in。
        if (stmt instanceof DefinitionStmt) { // 只处理赋值语句
            if (stmt.getDef().isPresent()) {  // 如果左值存在
                LValue lValue = stmt.getDef().get();  // 获取左值
                if ((lValue instanceof Var) && canHoldInt((Var) lValue)) {  // 对于符合条件的左值
                    copy.update((Var) lValue, evaluate(((DefinitionStmt<?, ?>)  stmt).getRValue(), copy));  // 计算右值表达式的值用来更新左值变量在格上的值
                }
            }
        }
        return out.copyFrom(copy);  // copy复制给out。copy和in相比,有更新,返回true;反之返回false
    }

    /**
     * @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) {
        // 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 v1 = in.get(((BinaryExp) exp).getOperand1()); // 获取运算分量在格上的值
            Value v2 = in.get(((BinaryExp) exp).getOperand2());
            if (v1.isNAC() || v2.isNAC()) {
                if (v1.isNAC() && v2.isConstant() && exp instanceof ArithmeticExp) {  // x = a / 0,x = a % 0,x 的值将会是 UNDEF
                    ArithmeticExp.Op operator = ((ArithmeticExp) exp).getOperator();
                    if (operator == ArithmeticExp.Op.DIV || operator == ArithmeticExp.Op.REM) {
                        if (v2.getConstant() == 0) return Value.getUndef();
                    }
                }
                return Value.getNAC();
            }
            if (v1.isUndef() || v2.isUndef()) {
                return Value.getUndef();
            }
            if (exp instanceof ArithmeticExp) {
                ArithmeticExp.Op operator = ((ArithmeticExp) exp).getOperator();
                switch (operator) {
                    case ADD -> {
                        return Value.makeConstant(v1.getConstant() + v2.getConstant());
                    }
                    case DIV -> {
                        if (v2.getConstant() == 0) return Value.getUndef();
                        return Value.makeConstant(v1.getConstant() / v2.getConstant());
                    }
                    case MUL -> {
                        return Value.makeConstant(v1.getConstant() * v2.getConstant());
                    }
                    case SUB -> {
                        return Value.makeConstant(v1.getConstant() - v2.getConstant());
                    }
                    case REM -> {
                        if (v2.getConstant() == 0) return Value.getUndef();
                        return Value.makeConstant(v1.getConstant() % v2.getConstant());
                    }
                }
            } else if (exp instanceof ConditionExp) {
                ConditionExp.Op operator = ((ConditionExp) exp).getOperator();
                switch (operator) {
                    case EQ -> {
                        if (v1.getConstant() == v2.getConstant()) return Value.makeConstant(1);
                        else return Value.makeConstant(0);
                    }
                    case GE -> {
                        if (v1.getConstant() >= v2.getConstant()) return Value.makeConstant(1);
                        else return Value.makeConstant(0);
                    }
                    case GT -> {
                        if (v1.getConstant() > v2.getConstant()) return Value.makeConstant(1);
                        else return Value.makeConstant(0);
                    }
                    case LE -> {
                        if (v1.getConstant() <= v2.getConstant()) return Value.makeConstant(1);
                        else return Value.makeConstant(0);
                    }
                    case LT -> {
                        if (v1.getConstant() < v2.getConstant()) return Value.makeConstant(1);
                        else return Value.makeConstant(0);
                    }
                    case NE -> {
                        if (v1.getConstant() != v2.getConstant()) return Value.makeConstant(1);
                        else return Value.makeConstant(0);
                    }
                }
            } else if (exp instanceof BitwiseExp) {
                BitwiseExp.Op operator = ((BitwiseExp) exp).getOperator();
                switch (operator) {
                    case OR -> {
                        return Value.makeConstant(v1.getConstant() | v2.getConstant());
                    }
                    case AND -> {
                        return Value.makeConstant(v1.getConstant() & v2.getConstant());
                    }
                    case XOR -> {
                        return Value.makeConstant(v1.getConstant() ^ v2.getConstant());
                    }
                }
            } else if (exp instanceof ShiftExp) {
                ShiftExp.Op operator = ((ShiftExp) exp).getOperator();
                switch (operator) {
                    case SHL -> {
                        return Value.makeConstant(v1.getConstant() << v2.getConstant());
                    }
                    case SHR -> {
                        return Value.makeConstant(v1.getConstant() >> v2.getConstant());
                    }
                    case USHR -> {
                        return Value.makeConstant(v1.getConstant() >>> v2.getConstant());
                    }
                }
            }
            else {  // 二元表达式中的其他类型表达式
                return Value.getNAC();
            }
        }
        return Value.getNAC();
    }
}

3 实现 Worklist 求解器

3.1 Tai-e 中你需要了解的类

与迭代求解器类似,你需要清楚 DataflowResultCFGSolver 的相关用法(我们已经在作业 1 中介绍过了)。除此之外,你还需要知道:

  • pascal.taie.analysis.dataflow.solver.WorkListSolver

    该类继承了 Solver 类,实现了 worklist 算法。它的实现是不完整的,在本此作业中你需要完成它。

3.2 你的任务 [重点!]

你的第二个任务是完成下述两个 API 的实现:

考虑到常量传播是一个前向分析,你只需要关注前向分析相关的方法。initializeForward() 方法的具体实现参考如图前三行。

doSolveForward() 则包含了你要实现的算法的主体部分。

  • Solver.initializeForward(CFG,DataflowResult)
  • WorkListSolver.doSolveForward(CFG,DataflowResult)

提示

  1. 讲义中的 worklist 算法通过比较 old_OUTOUT[B] 来决定后继节点是否应当加入 worklist 中,这个做法比较低效。Tai-e 中 DataflowAnalysis.transferNode() 会返回此次 transfer 是否改变了 OUT fact。利用好这一点可以避免多余的判断;
  2. 与作业 1 类似,不要忘了在 Solver.initializeForward() 中初始化每个语句的 INOUT

initializeForward :初始化所有的 Data Flow Fact

doSolveForward :负责实现 Worklist 求解器具体步骤。

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)) continue;
            result.setInFact(node, analysis.newInitialFact());
            result.setOutFact(node, analysis.newInitialFact());
        }
    }
/*
 * Tai-e: A Static Analysis Framework for Java
 *
 * Copyright (C) 2022 Tian Tan <tiantan@nju.edu.cn>
 * Copyright (C) 2022 Yue Li <yueli@nju.edu.cn>
 *
 * This file is part of Tai-e.
 *
 * Tai-e is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation, either version 3
 * of the License, or (at your option) any later version.
 *
 * Tai-e is distributed in the hope that it will be useful,but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
 * Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with Tai-e. If not, see <https://www.gnu.org/licenses/>.
 */

package pascal.taie.analysis.dataflow.solver;

import pascal.taie.analysis.dataflow.analysis.DataflowAnalysis;
import pascal.taie.analysis.dataflow.fact.DataflowResult;
import pascal.taie.analysis.graph.cfg.CFG;

import java.util.*;


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) {
        // TODO - finish me
        ArrayDeque<Node> worklist = new ArrayDeque<>();   // 双端堆栈当队列用
        for (Node node : cfg) {   // 添加所有结点到队列中
            if (cfg.isEntry(node)) {
                continue;
            }
            worklist.addLast(node);
        }
        while (!worklist.isEmpty()) {
            Node node = worklist.pollFirst();  // 弹出队头结点
            for (Node pred : cfg.getPredsOf(node)) {  // 对该结点以及所有前驱结点的OUT做meet
                analysis.meetInto(result.getOutFact(pred), result.getInFact(node));
            }
            boolean f = analysis.transferNode(node, result.getInFact(node), result.getOutFact(node));
            if (f) {  // 如果该节点OUT发生了变化,将其所有后继节点添加到队列
                for (Node succ : cfg.getSuccsOf(node)) {
                    worklist.addLast(succ);
                }
            }
        }
    }

    @Override
    protected void doSolveBackward(CFG<Node> cfg, DataflowResult<Node, Fact> result) {
        throw new UnsupportedOperationException();
    }
}

4 运行与测试

你可以按我们在 Tai-e 框架(教学版)配置指南 中提到的方式来运行分析。在本作业中,Tai-e 对输入类的每个方法进行常量传播分析,并输出分析的结果,也就是每个语句的 OUT fact 所包含的数据流信息(变量的格上对应值)。

--------------------<Assign: void assign()> (constprop)--------------------

[0@L4] x = 1; null

[1@L5] x = 2; null

[2@L6] x = 3; null

[3@L7] x = 4; null

[4@L8] y = x; null

[5@L8] return; null

当你未完成作业的时候,OUT fact 的结果为 null,当你完成了所有空缺代码后,分析的输出应当形如:

--------------------<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}

此外,Tai-e 将被分析方法的控制流图输出到 output/ 文件夹,它们被存储为 .dot 文件,你可以用可视化工具Graphviz来查看这些控制流图。

我们为本次作业提供了测试驱动类 pascal.taie.analysis.dataflow.analysis.constprop.CPTest,你可以按照 Tai-e 框架(教学版)配置指南 中描述的方式来测试你的实现是否正确。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/620469.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

部分树上问题及图的联通性(图论学习总结部分内容)

文章目录 前言三、部分树上问题及图的联通性最小生成树知识点例题 e g 1 : eg1: eg1: 走廊泼水节&#xff08;克鲁斯卡尔思想的灵活运用&#xff09; e g 2 &#xff1a; eg2&#xff1a; eg2&#xff1a; B-Picnic Planning e g 3 eg3 eg3&#xff1a;L - Classic Problem&…

无线麦克风哪个好?无线麦克风如何选择?2024高品质产品推荐整理

​在如今的数字化时代&#xff0c;无线麦克风已经逐渐渗透到我们生活的方方面面。无论是专业的自媒体人、带货主播&#xff0c;还是日常生活中的普通用户&#xff0c;无线麦克风都发挥着不可或缺的作用。而在选择无线麦克风时&#xff0c;收音降噪效果和性价比无疑是大家最为关…

Electron下复用窗口关闭、最小化和最大化按钮

在macOS下&#xff0c;创建窗口时设置&#xff1a; new BrowserWindow({titleBarStyle: hidden, // 关闭默认的titlebartrafficLightPosition: { x: 18, y: 18 }, // 交通灯距离窗口左侧和窗口上侧的像素距离 })效果&#xff1a; 在window下可以这样设置&#xff0c; new Br…

Java基于Geth1.8实现节点同步、合约部署,以及踩坑记录—主节点控制台卡死、节点同步出错的解决方案

前言&#xff1a;本文将从一个区块链入门小白的视角&#xff0c;来一步步的讲解如何实现区块链数据上链&#xff0c;链上数据查询&#xff0c;geth多节点同步。以及讲解在上链过程中&#xff0c;我踩过的坑及其解决方案。如果有不对的地方&#xff0c;还请大佬指教&#xff01;…

白酒:酒精度数对白酒贮存老熟的影响研究

云仓酒庄豪迈白酒作为一种品质的白酒&#xff0c;其酒精度数对白酒贮存老熟的影响是一个值得探讨的话题。酒精度数作为白酒的一个重要参数&#xff0c;不仅决定了酒体的基本风格&#xff0c;更在很大程度上影响了白酒在贮存过程中的变化和老熟过程。 首先&#xff0c;酒精度数的…

华为配置智能无损网络综合

配置智能无损网络综合示例 适用产品和版本 安装了P系列单板的CE16800、CE6866、CE6866K、CE8851-32CQ8DQ-P、CE8851K系列交换机V300R020C00或更高版本。 安装了SAN系列单板的CE16800、CE6860-SAN、CE8850-SAN系列交换机V300R020C10或更高版本。 CE6860-HAM、CE8850-HAM系列交换…

HR人才测评:应变能力与岗位胜任力素质测评

什么是应变能力 应变能力在职场中可以说是必备的素质之一&#xff0c;它指的是从业者需要长期活动或者是行为来迎接即将到来的挑战&#xff0c;做提前的思考&#xff0c;以适应未来的挑战&#xff0c;具有随机应变的意思。在外界还未发生变化或者是已经发生变化时&#xff0c;…

python(环境安装)搭建、pycharm安装、背景改为白色详细文章

安装python环境 1、下载python安装包 Welcome to Python.org&#xff08;官网链接&#xff09; 2、点击下载、windows、python3.12.3 安装python 执行安装程序、安装选项 选择下面两项 翻译 Use admin privieges when installing py.exe是使用administrator超级管理员用户安…

MySQL从入门到高级 --- 6.函数

文章目录 第六章&#xff1a;6.函数6.1 聚合函数6.2 数学函数6.3 字符串函数6.4 日期函数6.4.1 日期格式 6.5 控制流函数6.5.1 if逻辑判断语句6.5.2 case when语句 6.6 窗口函数6.6.1 序号函数6.6.2 开窗聚合函数6.6.3 分布函数6.6.4 前后函数6.6.5 头尾函数6.6.6 其他函数6.7 …

core.sshd.xxxxxx文件过大

背景 【紧急】【应用分组】应用: 接入点服务, 分组: 观众预发, ip: xx.xx.xx.xx 【/】&#xff0c;磁盘使用率已连续2次大于90% [当前值:100%]。报警时间: 2024-05-13 14:07:01 原因 登录机器查看&#xff0c;发现根目录下有大量的崩溃文件将 / 打满 处理 1&#xff0c; 删…

SSL证书助力工业和信息化领域数据安全,确保传输数据的保密性、完整性

工业和信息化领域数据包括工业数据、电信数据和无线电数据等&#xff0c;是国家重要基础性战略资源&#xff0c;随着工业领域数字化、网络化、智能化加速提质升级&#xff0c;数据泄露、勒索攻击等网络风险日益增加&#xff0c;由此加强工业和信息化领域数据安全管理&#xff0…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-15.1,2,3-GPIO中断控制实验

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

山姆·奥特曼接受All-in Podcast采访

前言 在“All-in Podcast”播客中&#xff0c;OpenAI的CEO山姆奥特曼广泛讨论了人工智能的多个关键议题。他涉及了推理计算、开源模型的发展、GPT-5语言模型的进展&#xff0c;并对AI监管、全民基本收入&#xff08;UBI&#xff09;政策、智能体如何改变应用交互&#xff0c;以…

Springboot自动装配源码分析

版本 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.4.RELEASE</version><relativePath/> <!-- lookup parent from repository --> </par…

GPT搜索鸽了!改升级GPT-4

最近OpenAI太反常&#xff0c;消息一会一变&#xff0c;直让人摸不着头脑。 奥特曼最新宣布&#xff1a;5月13日开发布会&#xff0c;不是GPT-5&#xff0c;也不是盛传的GPT搜索引擎&#xff0c;改成对ChatGP和GPT-4的升级&#xff5e; 消息一出&#xff0c;大伙儿都蒙了。 之…

【cocos creator】2.4.0 import android.support.v4.app.ActivityCompat;失败的解决方案

时间是2024年5月&#xff0c;某cocos creator项目用的是2.4.0编辑器。需求是获取录音权限&#xff0c;需要import ActivityCompat。但是失败&#xff0c;提示Cannot resolve symbol app。 尝试了一些方案失败之后&#xff0c;决定升级cocos creator编辑器版本。升级到2.4.10。…

Maven:继承和聚合

Maven高级 分模块设计和开发 如果在我们自己的项目中全部功能在同一个项目中开发,在其他项目中想要使用我们封装的组件和工具类并不方便 不方便项目的维护和管理 项目中的通用组件难以复用 所以我们需要使用分模块设计 分模块设计 在项目设计阶段,可以将大的项目拆分成若…

【快捷上手】UnrealEngine 的 关卡流 LevelStreaming 的三种加载方式

关键词&#xff1a; Unreal Engine&#xff0c;UE&#xff0c; LevelStreaming&#xff0c;动态&#xff0c;关卡&#xff0c;加载&#xff0c;切换关卡&#xff0c;换地图&#xff0c;子地图&#xff0c;子场景&#xff0c;子关卡&#xff0c;分包加载&#xff0c;动态载入 …

IT服务台的演变趋势

在技术进步和用户期望变化的推动下&#xff0c;IT服务台正在经历重大变化。IT服务台的未来将主要受到以下趋势的推动&#xff1a; 先进的人工智能和认知技术 预计高级人工智能 &#xff08;AI&#xff09; 和认知技术在 IT 服务台中的集成度会更高。通过将 IT 服务台集成到 IT…

点是否在三角形内C++源码实现

原理 思路&#xff1a; 面积和&#xff1a; abc obcaocabo,应该有更简洁的方法&#xff0c;但是这个方法思路更简单 代码实现: 注意二维向量的叉乘后&#xff0c;是垂直于平面的向量&#xff0c;相当于z为0三维向量叉乘&#xff0c;所以只有z维度有值&#xff0c;xy0. flo…