【静态分析】软件分析课程实验A3-死代码检测

news2024/10/6 4:11:39

官网:

作业 3:死代码检测 | Tai-e

参考:

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

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

1 作业导览

  • 为 Java 实现一个死代码(dead code)检测算法。

从程序中去除死代码是一种常见的编译优化策略。其中最困难的问题是如何检测到程序中的死代码。在这次的实验作业中,你将会通过组合你前两次作业中实现的分析方法:活跃变量分析常量传播,来实现一个 Java 的死代码检测算法。在本文档中,我们将会明确界定本次作业中所讨论的死代码的范畴,你的任务就是实现一个检测算法识别它们。

idea打开实验作业仓库的 A3/tai-e/,并按【静态分析】软件分析课程实验-前置准备-CSDN博客进行配置。

2 死代码检测介绍

死代码指的是程序中不可达的(unreachable)代码(即不会被执行的代码),或者是执行结果永远不会被其他计算过程用到的代码。去除死代码可以在不影响程序输出的前提下简化程序、提高效率。在本次作业中,我们只关注两种死代码:不可达代码(unreachable code)和无用赋值(dead assignment)。

2.1 不可达代码

一个程序中永远不可能被执行的代码被称为不可达代码。我们考虑两种不可达代码:控制流不可达代码(control-flow unreachable code)和分支不可达代码(unreachable branch)。这两种代码的介绍如下。

控制流不可达代码. 在一个方法中,如果不存在从程序入口到达某一段代码的控制流路径,那么这一段代码就是控制流不可达的。比如,由于返回语句是一个方法的出口,所以跟在它后面的代码是不可达的。例如在下面的代码中,第 4 行和第 5 行的代码是控制流不可达的:

int controlFlowUnreachable() {
    int x = 1;
    return x;
    int z = 42; // control-flow unreachable code
    foo(z); // control-flow unreachable code
}

检测方式:这样的代码可以很简单地利用所在方法的控制流图(CFG,即 control-flow graph)检测出来。我们只需要从方法入口开始,遍历 CFG 并标记可达语句。当遍历结束时,那些没有被标记的语句就是控制流不可达的。

分支不可达代码. 在 Java 中有两种分支语句:if 语句和 switch 语句。它们可能会导致分支不可达代码的出现。

对于一个 if 语句,如果它的条件值(通过常量传播得知)是一个常数,那么无论程序怎么执行,它两个分支中的其中一个分支都不会被走到。这样的分支被称为不可达分支。该分支下的代码也因此是不可达的,被称为分支不可达代码。如下面的代码片段所示,由于第 3 行 if 语句的条件是永真的,所以它条件为假时对应的分支为不可达分支,该分支下的代码(第 6 行)是分支不可达代码。

int unreachableIfBranch() {
    int a = 1, b = 0, c;
    if (a > b)
        c = 2333;
    else
        c = 6666; // unreachable branch
    return c;
}

对于一个 switch 语句,如果它的条件值是一个常数,那么不符合条件值的 case 分支就可能是不可达的。如下面的代码片段所示,第 3 行 switch 语句的条件值(变量 x 的值)永远是 2 ,因此分支 “case 1” 和 “default” 是不可达的。注意,尽管分支 “case 3” 同样没法匹配上条件值(也就是 2),但它依旧是可达的,因为控制流可以从分支 “case 2” 流到它。

int unreachableSwitchBranch() {
    int x = 2, y;
    switch (x) {
        case 1: y = 100; break; // unreachable branch
        case 2: y = 200;
        case 3: y = 300; break; // fall through
        default: y = 666; // unreachable branch
    }
    return y;
}

检测方式:为了检测分支不可达代码,我们需要预先对被检测代码应用常量传播分析,通过它来告诉我们条件值是否为常量,然后在遍历 CFG 时,我们不进入相应的不可达分支。

2.2 无用赋值

一个局部变量在一条语句中被赋值,但再也没有被该语句后面的语句读取,这样的变量和语句分别被称为无用变量(dead variable,与活跃变量 live variable 相对)和无用赋值。无用赋值不会影响程序的输出,因而可以被去除。如下面的代码片段所示,第 3 行和第 5 行的语句都是无用赋值。

int deadAssign() {
    int a, b, c;
    a = 0; // dead assignment
    a = 1;
    b = a * 2; // dead assignment
    c = 3;
    return c;
}

检测方式:为了检测无用赋值,我们需要预先对被检测代码施用活跃变量分析。对于一个赋值语句,如果它等号左侧的变量(LHS 变量)是一个无用变量(换句话说,not live),那么我们可以把它标记为一个无用赋值。

但需要注意的是,以上讨论有一种例外情况:有时即使等号左边的变量 x 是无用变量,它所属的赋值语句 x = expr 也不能被去除,因为右边的表达式 expr 可能带有某些副作用。例如,当 expr 是一个方法调用(x = m())时,它就有可能带有副作用。对于这种情况,我们提供了一个 API 供你检查等号右边的表达式是否可能带有副作用(在第 3.2 节说明)。如果带有副作用,那么为了保证 safety,即使 x 不是一个活跃变量,你也不应该把这个赋值语句标记为死代码。

3 实现死代码检测器

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

为了实现死代码检测算法,你需要知道 CFGIR,还有其他与活跃变量分析、常量传播分析结果有关的类(比如 CPFactDataflowResult 等),不过你已经在之前的作业中使用过了它们,应该对它们很熟悉了!接下来我们介绍更多本次作业中将会用到的和 CFG 以及 IR 有关的类。

  • pascal.taie.analysis.graph.cfg.Edge

    这个类表示 CFG 中的边(提示:CFG 中的节点是 Stmt)。它具有方法 getKind(),可以用来得知某个边的种类(你可以通过阅读类 Edge.Kind 的注释来理解各个种类的含义),并且你可以像下面这样检查边的种类:

    Edge<Stmt> edge = ...;
    if (edge.getKind() == Edge.Kind.IF_TRUE) { ... }
    

在这次作业中,你需要考虑四种边:IF_TRUEIF_FALSESWITCH_CASESWITCH_DEFAULTIF_TRUEIF_FALSE 表示从 if 语句到它的两个分支的出边,就像下面的例子所示:

SWITCH_CASESWITCH_DEFAULT 表示从 switch 语句到它的 case 分支和 default 分支的出边,就像下面的例子所示:

对于 SWITCH_CASE 边,你可以通过 getCaseValue() 方法来获取它们对应的 case 分支的条件值(比如在上面的例子中,调用 case 1 对应的出边的 getCaseValue() 方法会返回值 1,调用 case 3 对应的 out edge 的 getCaseValue() 方法会返回值 3)。

pascal.taie.ir.stmt.IfStmt 的子类)

这个类表示程序中的 if 语句。

值得注意的是,在 Tai-e 的 IR 中,while 循环和 for 循环也被转换成了 If 语句。比如下面这个用 Java 写的循环:

while (a > b) {
    x = 233;
}
y = 666;

在 Tai-e 中将会被转化成像这样的 IR:

0:  if (a > b) goto 2;
1:  goto 4;
2:  x = 233;
3:  goto 0;
4:  y = 666;

因此,你的算法实现不需多加改变就能自然而然地支持检测与循环相关的死代码。比如,如果 ab 都是常量并且 a <= b,那么你的分析算法应该把语句 x = 233 标记成死代码。

  • pascal.taie.ir.stmt.SwitchStmtStmt 的子类)

    这个类表示程序中的 switch 语句。你需要阅读它的源代码和注释来决定如何使用它。

  • pascal.taie.ir.stmt.AssignStmtStmt 的子类)

    这个类表示程序中的赋值语句(比如 x = ...;)。你可能会觉得它有点像你之前看到过的 DefinitionStmt。下面的部分的类继承关系图展示了这两个类的关系:

  • 事实上,AssignStmtDefinitionStmt 两个子类的其中一个(另一个是 Invoke,它表示程序中的方法调用)。这意味着除了等号右侧是方法调用的赋值语句,其他赋值语句都用 AssignStmt 表示。正如第 2.2 节所说的,方法调用可能含有很多副作用,因此对于像 x = m(); 这样的语句,即使 x 之后再也不会被用到(换言之,x 是无用变量),这条语句也不会被认为是无用赋值。因此,本次作业中所有可能的无用赋值都只可能是 AssignStmt 的实例。你只需要关注 AssignStmt 这个类即可。

  • pascal.taie.analysis.dataflow.analysis.DeadCodeDetection

    这个类是实现死代码检测的类。你需要根据第 3.2 节的指导来补完它。

3.2 你的任务 [重点!]

你需要完成 DeadCodeDetection 中的一个API:

  • Set<Stmt> analyze(IR)

这个方法将一个 IR 作为输入,返回一个包含 IR 中死代码的集合。你的任务是找出第 2 节中描述的两种死代码(也就是不可达代码和无用赋值),然后将它们加入到作为结果返回的集合中。 为了简单起见,你不需要考虑由删除死代码而产生的新的死代码。就拿我们前面在介绍无用赋值时用过的例子来说,当下列代码中第 3 行和第 5 行的无用赋值被删除后,第 4 行的 a = 1 会变成新的无用赋值,只不过在本次作业中,你不必把它识别为死代码(即不加入到结果集中)。

int deadAssign() {
    int a, b, c;
    a = 0; // dead assignment
    a = 1;
    b = a * 2; // dead assignment
    c = 3;
    return c;
}

死代码检测依赖活跃变量分析和常量传播分析的结果。因此,为了让死代码检测能跑起来,你需要先补全 LiveVariableAnalysis.javaConstantPropagation.java。你可以拷贝你之前作业中的实现。另外,你也需要完成一个同时支持前向分析和后向分析的 worklist 求解器。你可以从作业 2 中拷贝你之前对 Solver.javaWorkListSolver.java 的实现,并在这次作业中实现 Solver.initializeBackward()WorkListSolver.doSolveBackward()。不过不用担心,这次作业中我们不会要求你提交这些代码的源文件,所以即使你之前作业中的实现并不是完全正确的,它们也不会影响你本次作业的分数。

/*
 * 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;

import pascal.taie.analysis.dataflow.fact.SetFact;
import pascal.taie.analysis.graph.cfg.CFG;
import pascal.taie.config.AnalysisConfig;
import pascal.taie.ir.exp.LValue;
import pascal.taie.ir.exp.RValue;
import pascal.taie.ir.exp.Var;
import pascal.taie.ir.stmt.Stmt;

import java.util.List;
import java.util.Optional;

/**
 * Implementation of classic live variable analysis.
 */
public class LiveVariableAnalysis extends
        AbstractDataflowAnalysis<Stmt, SetFact<Var>> {

    public static final String ID = "livevar";

    public LiveVariableAnalysis(AnalysisConfig config) {
        super(config);
    }

    @Override
    public boolean isForward() {
        return false;
    }

    @Override
    public SetFact<Var> newBoundaryFact(CFG<Stmt> cfg) {
        // TODO - finish me
        return new SetFact<>();
    }

    @Override
    public SetFact<Var> newInitialFact() {
        // TODO - finish me
        return new SetFact<>();
    }

    @Override
    public void meetInto(SetFact<Var> fact, SetFact<Var> target) {
        // TODO - finish me
        target.union(fact);
    }

    @Override
    public boolean transferNode(Stmt stmt, SetFact<Var> in, SetFact<Var> out) {
        // TODO - finish me
        Optional<LValue> def = stmt.getDef();
        List<RValue> uses = stmt.getUses();
        SetFact<Var> newSetFact = new SetFact<>();
        newSetFact.union(out);
        if(def.isPresent()) {
            if(def.get() instanceof Var) {
                newSetFact.remove((Var) def.get());
            }
        }
        for (RValue use : uses) {
            if (use instanceof Var) {
                newSetFact.add((Var) use);
            }
        }
        if (!in.equals(newSetFact)) {
            in.set(newSetFact);
            return true;
        }
        return false;
    }
}
/*
 * 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.*;
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;

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。有更新,返回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();
    }
}
/*
 * 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;

/**
 * Base class for data-flow analysis solver, which provides common
 * functionalities for different solver implementations.
 *
 * @param <Node> type of CFG nodes
 * @param <Fact> type of data-flow facts
 */
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) {
        // 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());
        }
    }

    protected void initializeBackward(CFG<Node> cfg, DataflowResult<Node, Fact> result) {
        // TODO - finish me
        // Init Exit node
        result.setInFact(cfg.getExit(), analysis.newBoundaryFact(cfg));

        // Init other nodes
        for (Node node : cfg) {
            if (!cfg.isExit(node)) {
                result.setInFact(node, analysis.newInitialFact());
                result.setOutFact(node, analysis.newInitialFact());
            }
        }
    }

    /**
     * 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);
}
/*
 * 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.ArrayDeque;

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
        // 向下分析,对于块B,通过IN[B]计算OUT[B], IN[B]是前驱OUT的处理
        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(may analysis)
                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) {
        // TODO - finish me
        // 向上分析,通过OUT[B]计算IN[B], OUT[B]是后继IN的处理
        ArrayDeque<Node> worklist = new ArrayDeque<>();
        for (Node node : cfg) {
            if (cfg.isExit(node)) {
                continue;
            }
            worklist.addFirst(node);
        }
        while (!worklist.isEmpty()) {
            Node node = worklist.pollFirst();
            for (Node succ : cfg.getSuccsOf(node)) {  // 求B的OUT
                analysis.meetInto(result.getInFact(succ), result.getOutFact(node));  // may analysis
            }
            boolean f = analysis.transferNode(node, result.getInFact(node), result.getOutFact(node));
            if (f) {
                for (Node pred : cfg.getPredsOf(node)) {
                    worklist.addFirst(pred);
                }
            }
        }
    }
}

提示

  1. 在这次作业中,Tai-e 会在运行死代码检测之前自动运行活跃变量分析和常量传播分析。我们在 DeadCodeDetection.analyze() 中提供了用来获得这两种分析算法针对目标 IR 的分析结果,这样你可以直接使用它们。另外,analyze() 方法包含获取 IRCFG 的代码。
  2. 正如第 2.2 节提到的那样,某些赋值语句等号右侧的表达式可能含有副作用,因此不能被当作 dead assignments。我们在 DeadCodeDetection 中提供了一个辅助方法 hasNoSideEffect(RValue),用来帮助你检查一个表达式是否含有副作用。
  3. 在遍历 CFG 时,你需要对当前正在访问的节点使用 CFG.getOutEdgesOf() 来帮助获得之后要被访问的后继节点。这个 API 返回给定节点在 CFG 上的出边,所以你可以用边的信息(在第 3.1 节介绍过)来帮助找出分支不可达代码。
  4. 当在寻找分支不可达代码时,你可以使用 ConstantPropagation.evaluate() 来计算 if 和 switch 语句的条件值。

整体思路

记录所有可达的语句,没有被记录的语句都是不可达的死代码。

不能直接遍历控制流图中的 IR ,这些 IR 有可能是不可达的,而是使用队列(stmts)记录所有即将被访问的语句,进行遍历,对于每条语句根据其不同的类型进行不同处理。

除了队列以外,还需要一个集合(reached)来判断某条语句是否访问过,避免重复访问,防止死循环。

最后使用一个集合(reachable)记录哪些语句是可达的(语句先访问再判断是否可达)。

有几个易错点:

  • AssignStmt 处理时,左值要先判断能否转成成 Var ,防止类型转换异常。
  • SwitchStmt 处理时,如果所有 case 都匹配不到,可能会执行 default 中控制流。

新语法知识:

在Java 14及更高版本中,可以使用" Pattern Matching for instanceof "特性来将 instanceof 的结果转化并赋值给一个新的变量。这种语法可以简化类型判断和类型转换的代码。注意,被转换的对象必须是finaleffectively final的,以确保转换后的变量是不可变的。此外,这种语法只适用于局部变量,不能用于成员变量或静态变量的赋值。


if (obj instanceof MyClass myObj) {
    // 将obj转换为MyClass类型,并赋值给myObj变量
    // 可以在if语句的代码块中使用myObj
    myObj.doSomething();
} else {
    // obj不是MyClass类型的处理逻辑
    // ...
}
/*
 * 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;

import pascal.taie.analysis.MethodAnalysis;
import pascal.taie.analysis.dataflow.analysis.constprop.CPFact;
import pascal.taie.analysis.dataflow.analysis.constprop.ConstantPropagation;
import pascal.taie.analysis.dataflow.analysis.constprop.Value;
import pascal.taie.analysis.dataflow.fact.DataflowResult;
import pascal.taie.analysis.dataflow.fact.SetFact;
import pascal.taie.analysis.graph.cfg.CFG;
import pascal.taie.analysis.graph.cfg.CFGBuilder;
import pascal.taie.analysis.graph.cfg.Edge;
import pascal.taie.config.AnalysisConfig;
import pascal.taie.ir.IR;
import pascal.taie.ir.exp.ArithmeticExp;
import pascal.taie.ir.exp.ArrayAccess;
import pascal.taie.ir.exp.CastExp;
import pascal.taie.ir.exp.FieldAccess;
import pascal.taie.ir.exp.NewExp;
import pascal.taie.ir.exp.RValue;
import pascal.taie.ir.exp.Var;
import pascal.taie.ir.stmt.AssignStmt;
import pascal.taie.ir.stmt.If;
import pascal.taie.ir.stmt.Stmt;
import pascal.taie.ir.stmt.SwitchStmt;

import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

import pascal.taie.ir.exp.*;
import java.util.*;


public class DeadCodeDetection extends MethodAnalysis {

    public static final String ID = "deadcode";

    public DeadCodeDetection(AnalysisConfig config) {
        super(config);
    }

    @Override
    public Set<Stmt> analyze(IR ir) {
        // obtain CFG
        CFG<Stmt> cfg = ir.getResult(CFGBuilder.ID);
        // obtain result of constant propagation
        DataflowResult<Stmt, CPFact> constants =
                ir.getResult(ConstantPropagation.ID);
        // obtain result of live variable analysis
        DataflowResult<Stmt, SetFact<Var>> liveVars =
                ir.getResult(LiveVariableAnalysis.ID);
        // keep statements (dead code) sorted in the resulting set
        Set<Stmt> deadCode = new TreeSet<>(Comparator.comparing(Stmt::getIndex));
        // TODO - finish me
        // Your task is to recognize dead code in ir and add it to deadCode
        // 检测控制流不可达代码、分支不可达和无用赋值
        ArrayDeque<Stmt> stmts = new ArrayDeque<>();  // 队列
        Set<Stmt> reachable = new HashSet<>();
        Set<Stmt> reached = new HashSet<>();
        stmts.addLast(cfg.getEntry());  // 第一个访问结点是方法的入口
        reachable.add(cfg.getExit());   // 方法的入口和出口肯定是可达的
        reachable.add(cfg.getEntry());
        while (!stmts.isEmpty()) {
            Stmt stmt = stmts.pollFirst();  // 弹出队头
            reached.add(stmt);  // 记录弹出结点被访问
            // 无用赋值语句处理,本次作业中所有可能的无用赋值都只可能是 AssignStmt 的实例
            if (stmt instanceof AssignStmt assignStmt) {
                SetFact<Var> liveVarsResult = liveVars.getResult(assignStmt);  // 获取当前语句执行后的活跃变量结果
                LValue lValue = assignStmt.getLValue();  // 获取当前语句的左值
                RValue rValue = assignStmt.getRValue();  // 获取当前语句的右值
                boolean f = true;  // 左值是死变量,右值没有side effect的语句是死代码
                if (lValue instanceof Var) {  // 易错点1:要判断左值是否能转化成变量类型
                    if (!liveVarsResult.contains((Var) lValue)) {  // dead
                        if (hasNoSideEffect(rValue)) {  // no side effect
                            f = false;
                        }
                    }
                }
                if (f) {  // 如果不是特殊情况,那么当前语句可达
                    reachable.add(assignStmt);
                }
                for (Stmt succ : cfg.getSuccsOf(assignStmt)) {  // 后继结点加入队列
                    if (!reached.contains(succ))
                        stmts.addLast(succ);
                }
            } else if (stmt instanceof If ifStmt) {  // if语句处理
                CPFact result = constants.getResult(ifStmt);  // 获取常量传播结果
                ConditionExp condition = ifStmt.getCondition(); // 获取if条件表达式
                ConditionExp.Op operator = condition.getOperator(); // 获取运算符
                Value evaluate = ConstantPropagation.evaluate(condition, result); // 计算if条件表达式
                reachable.add(ifStmt);  // 当前if语句可达
                if (evaluate.isConstant()) {  // 如果if条件表达式是个常数,那么只可能到达一个分支
                    if (evaluate.getConstant() == 1) {  // 永远true
                        for (Edge<Stmt> edge : cfg.getOutEdgesOf(ifStmt)) {  // 所有出边
                            if (edge.getKind() == Edge.Kind.IF_TRUE) {  // true边一定到
                                Stmt target = edge.getTarget();
                                if (!reached.contains(target))
                                    stmts.add(target);  // 目标结点添加到队列
                            }
                        }
                    } else {  // 永远false
                        for (Edge<Stmt> edge : cfg.getOutEdgesOf(stmt)) {  // 所有出边
                            if (edge.getKind() == Edge.Kind.IF_FALSE) {  // false边一定到
                                Stmt target = edge.getTarget();
                                if (!reached.contains(target))
                                    stmts.add(target);  // 目标节点添加到队列
                            }
                        }
                    }
                } else {  // 如果if条件表达式不是个常数,那么两条分支都可能,按照控制流执行
                    for (Stmt succ : cfg.getSuccsOf(stmt)) {
                        if (!reached.contains(succ))
                            stmts.addLast(succ);
                    }
                }
            } else if (stmt instanceof SwitchStmt switchStmt) {  // switch语句处理
                Var var = switchStmt.getVar();  // 获取switch表达式的变量
                CPFact result = constants.getResult(switchStmt);  // 获取常量传播结果
                reachable.add(switchStmt);  // 当前switch语句可达
                if (result.get(var).isConstant()) {  // 如果switch表达式是常数,只可能到达几个分支
                    int constant = result.get(var).getConstant();  // 获取表达式的常量值
                    boolean match = false;  // 易错点2:记录是否匹配case,如果没有,将执行default
                    for (Edge<Stmt> edge : cfg.getOutEdgesOf(switchStmt)) {  // 获取所有出边
                        if (edge.getKind() == Edge.Kind.SWITCH_CASE) {  // 如果是case类型边
                            int caseValue = edge.getCaseValue();  // 获取case值
                            if (caseValue == constant) {   // 如果是匹配的case
                                match = true;
                                if (!reached.contains(edge.getTarget()))
                                    stmts.addLast(edge.getTarget());
                            }
                        }
                    }
                    if (!match) {  // 如果不匹配,执行default
                        Stmt defaultTarget = switchStmt.getDefaultTarget();  // 获取default对应的目标语句
                        if (!reached.contains(defaultTarget))
                            stmts.addLast(defaultTarget);
                    }
                } else {  // 如果switch表达式不是常数,每个case都可能执行,按照控制流执行
                    for (Stmt succ : cfg.getSuccsOf(switchStmt)) {
                        if (!reached.contains(succ))
                            stmts.addLast(succ);
                    }
                }
            } else {  // 执行到的其他类型语句,按照控制流执行
                reachable.add(stmt);
                for (Stmt succ : cfg.getSuccsOf(stmt)) {
                    if (!reached.contains(succ))
                        stmts.addLast(succ);
                }
            }
        }
        for (Stmt stmt : ir.getStmts()) {  // 遍历当前方法的所有IR,如果不可达,那么就是死代码
            if (!reachable.contains(stmt)) {
                deadCode.add(stmt);
            }
        }
        return deadCode;
    }

    /**
     * @return true if given RValue has no side effect, otherwise false.
     */
    private static boolean hasNoSideEffect(RValue rvalue) {
        // new expression modifies the heap
        if (rvalue instanceof NewExp ||
                // cast may trigger ClassCastException
                rvalue instanceof CastExp ||
                // static field access may trigger class initialization
                // instance field access may trigger NPE
                rvalue instanceof FieldAccess ||
                // array access may trigger NPE
                rvalue instanceof ArrayAccess) {
            return false;
        }
        if (rvalue instanceof ArithmeticExp) {
            ArithmeticExp.Op op = ((ArithmeticExp) rvalue).getOperator();
            // may trigger DivideByZeroException
            return op != ArithmeticExp.Op.DIV && op != ArithmeticExp.Op.REM;
        }
        return true;
    }
}

4 运行与测试

你可以参考 Tai-e 框架(教学版)配置指南 来运行分析算法。在这次作业中,Tai-e 为输入的类中的每一个方法运行活跃变量分析、常量传播分析和死代码检测算法。为了帮助调试,它会如下输出三个分析算法的结果:

--------------------<DeadAssignment: void deadAssign()> (livevar)--------------------

[0@L4] x = 1; null

[1@L5] %intconst0 = 2; null

[2@L5] y = x + %intconst0; null

[3@L6] %intconst1 = 3; null

[4@L6] z = x + %intconst1; null

[5@L7] invokevirtual %this.<DeadAssignment: void use(int)>(z); null

[6@L8] a = x; null

[7@L8] return; null

--------------------<DeadAssignment: void deadAssign()> (constprop)--------------------

[0@L4] x = 1; null

[1@L5] %intconst0 = 2; null

[2@L5] y = x + %intconst0; null

[3@L6] %intconst1 = 3; null

[4@L6] z = x + %intconst1; null

[5@L7] invokevirtual %this.<DeadAssignment: void use(int)>(z); null

[6@L8] a = x; null

[7@L8] return; null

--------------------<DeadAssignment: void deadAssign()> (deadcode)--------------------

当未完成这三个分析算法的时候,OUT facts 都为 null,并且没有代码被标记为死代码。在你完成了三个分析算法后,输出应当形如:

--------------------<DeadAssignment: void deadAssign()> (livevar)--------------------

[0@L4] x = 1; [%this, x]

[1@L5] %intconst0 = 2; [%intconst0, %this, x]

[2@L5] y = x + %intconst0; [%this, x]

[3@L6] %intconst1 = 3; [%intconst1, %this, x]

[4@L6] z = x + %intconst1; [%this, x, z]

[5@L7] invokevirtual %this.<DeadAssignment: void use(int)>(z); [x]

[6@L8] a = x; []

[7@L8] return; []

--------------------<DeadAssignment: void deadAssign()> (constprop)--------------------

[0@L4] x = 1; {x=1}

[1@L5] %intconst0 = 2; {%intconst0=2, x=1}

[2@L5] y = x + %intconst0; {%intconst0=2, x=1, y=3}

[3@L6] %intconst1 = 3; {%intconst0=2, %intconst1=3, x=1, y=3}

[4@L6] z = x + %intconst1; {%intconst0=2, %intconst1=3, x=1, y=3, z=4}

[5@L7] invokevirtual %this.<DeadAssignment: void use(int)>(z); {%intconst0=2, %intconst1=3, x=1, y=3, z=4}

[6@L8] a = x; {%intconst0=2, %intconst1=3, a=1, x=1, y=3, z=4}

[7@L8] return; {%intconst0=2, %intconst1=3, a=1, x=1, y=3, z=4}

--------------------<DeadAssignment: void deadAssign()> (deadcode)--------------------

[2@L5] y = x + %intconst0;

[6@L8] a = x;

此外,Tai-e 会把它分析的目标方法的控制流图输出到文件夹 output/ 里。CFGs 会被存储成 .dot 文件,并且可以通过 Graphviz

可视化。

我们为这次作业提供了测试驱动 pascal.taie.analysis.dataflow.analysis.DeadCodeTest。你可以按照 Tai-e 框架(教学版)配置指南 所介绍的那样使用它来测试你的实现。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Matten:视频生成与Mamba-Attention

Matten:视频生成与Mamba-Attention 摘要IntroductionRelated WorkMethodology Matten: Video Generation with Mamba-Attention 摘要 在本文中&#xff0c;作者介绍了Matten&#xff0c;一种具有Mamba-Attention架构的尖端潜在扩散模型&#xff0c;用于视频生成。在极小的计算…

数据结构(一)绪论

2024年5月11日 一稿 数据元素+数据项 逻辑结构 集合 线性结构 树形结构 </

商家转账到零钱开通揭秘,微信支付商户如何玩转分销返佣现金营销

在数字支付日益普及的今天&#xff0c;微信支付凭借其便捷、安全的特性&#xff0c;成为了众多商户的首选支付工具。而微信支付商户平台上的“商家转账到零钱”功能&#xff0c;更是为商户们打开了一扇全新的营销大门。今天&#xff0c;我们就来一起揭秘这个功能聊聊如何快速开…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-15.5讲 GPIO中断实验-通用中断驱动编写

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

Android 几种系统升级方式详解

目录 ◆ 概述 ● 几种启动模式 ● MISC分区 ● CACHE分区 ● 几种系统升级方式 ◆ Recovery升级 ● 升级包构成&#xff0c;签名&#xff0c;制作 ● 升级脚本 ● 升级过程 ◆ OTA升级 ● 升级包构成&#xff0c;制作 ● 升级脚本 ● 升级过程 ◆ fastboot升级 ◆ ADB升级 几…

成为一名算法工程师需要掌握哪些技术栈

成为算法工程师需要学习的编程技能主要包括以下几个方面&#xff1a; Python&#xff1a;Python是算法工程师最常使用的编程语言之一。它拥有简洁易读的语法和丰富的库&#xff0c;如NumPy、Pandas、SciPy、Matplotlib等&#xff0c;这些库为数据处理、科学计算和可视化提供了…

C++的数据结构(二)

一、链表的基本概念 链表&#xff08;Linked List&#xff09;是一种物理存储单元上非连续的、非顺序的线性数据结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列节点&#xff08;链表中每一个元素称为节点&#xff09;组成&#xff0c;节点…

飞利浦|西圣开放式耳机怎么选?爆款机型深度对比!

现在&#xff0c;开放式耳机以其独特的非入耳设计&#xff0c;成为了市场上的新宠。开放式耳机在佩戴上不仅能让我们长时间享受舒适的体验&#xff0c;更告别了入耳式耳机会导致的耳闷头昏的烦恼。但是现在&#xff0c;面对市场上琳琅满目的品牌和型号&#xff0c;许多消费者感…

vue3属性透传(透传 Attributes),支持多个根节点,且可以在JavaScript 中访问透传 Attributes

支持多个根节点&#xff0c;且可以在JavaScript 中访问透传 Attributes Index.vue: <script setup> import { ref, onMounted } from vue import Child from ./Child.vue import ./index.cssconst handleClick () > {console.log(1) }onMounted(() > {}) </s…

静态IP设置:小白必看攻略

在现代网络环境中&#xff0c;IP地址是连接互联网的基石。它就像网络世界中的门牌号&#xff0c;让数据能够在庞大的网络海洋中准确找到目标。其中&#xff0c;静态IP地址由于其固定不变的特性&#xff0c;在某些特殊应用场景下尤为重要。那么&#xff0c;如何设置静态IP地址呢…

Ubuntu18.04设置SSH密钥登录

我们一般使用 VSCode 、MobaXterm、PuTTY等 SSH 客户端来远程管理 Linux 服务器。但是&#xff0c;一般的密码方式登录&#xff0c;容易有密码被暴力破解的问题。所以&#xff0c;一般我们会将 SSH 的端口设置为默认的 22 以外的端口&#xff0c;或者禁用 root 账户登录。但是即…

json-server 模拟接口服务

前端开发经常需要模拟接口请求&#xff0c;可以通过 json-server 实现。 1. 安装 json-server 在前端项目的终端命令行中执行 npm i json-server2. 创建数据源 在项目中新建文件 db.json &#xff0c;与 package.json 同级&#xff0c;内容为模拟的数据 注意 json 文件对格式…

40道 Nginx 高频面试题

Nginx 是一款轻量级的 Web 服务器、反向代理服务器&#xff0c;由于它的内存占用少&#xff0c;启动极快&#xff0c;高并发能力强&#xff0c;在互联网项目中广泛应用。那么关于 Nginx 的核心面试题有哪些呢&#xff1f;今天给大家整理了40道Nginx 高频面试题&#xff0c;最近…

Java 常见集合类

集合的整体框架 Java 的集合&#xff0c;也可以叫做容器&#xff0c;根据集合的整体框架可以看出&#xff0c;主要是两大集合接口&#xff1a;第一个是 Collection 接口&#xff0c;主要用来存放单一的元素对象&#xff1b;另一个是 Map 接口&#xff0c;主要用于存储键值对。…

使用AudioCraft(MusicGen)生成音乐

AudioCraft 是一个 PyTorch 库,用于音频生成的深度学习研究。AudioCraft 包含两个先进的AI生成模型:AudioGen和MusicGen,它们共同致力于生成高质量的音频内容。 MusicGen是一个简单且可控的音乐生成模型。它利用Meta提供的20K小时授权音乐进行训练,能够根据文本描述或已有…

1_1. Linux简介

1_1. Linux简介 文章目录 1_1. Linux简介1. 我们用linux来干嘛2. 计算机组成3. 操作系统4. Linux哲学思想5. Linux目录6. Linux分区类型 1. 我们用linux来干嘛 1. 大家都知道linux是一个操作系统&#xff0c;它是一个基础的软件&#xff0c;操作系统是硬件与应用程序的中间层。…

JS中的arguments是什么?

arguments是当我们不确定有多少个参数传递时&#xff0c;就可以使用argument来获取。在js中&#xff0c;arguments实际上就是当前函数的一个内置对象&#xff0c;存储了我们传递的所有实参。arguents的展示形式就是一个伪数组&#xff0c;所以我们可以对它进行遍历。 我们先来…

使用J-Link Commander / JFlash 烧写固件程序(以STM32F103C8T6为例)

使用JFlash 烧写流程 运行JFlash, 点击Project Settings 配置Jlink为SWD方式,选择连接设备为STM32F103C8T6, 点击确定. 选择要烧录的Bin文件 设置bin文件烧录地址, 点击OK(地址要在0x08000000-0x0800FFFF范围内) Note : STM32F103C8T6 Flash大小为 64KB&#xff0c; 地址范围…

Windows只能安装在GPT磁盘上

转换磁盘分区形式 步骤1. 先按照正常流程使用Windows系统安装光盘或系统U盘引导计算机。 步骤2. 在Windows安装程序中点击“开始安装”&#xff0c;然后按ShiftF10打开命令提示符。 步骤3. 依次输入以下命令&#xff0c;并在每一行命令后按一次Enter键执行。 步骤4. 等待转换…

[Linux][网络][数据链路层][一][以太网][局域网原理]详细讲解

目录 0.对比理解"数据链路层"和网络层1.以太网1.认识以太网2.以太网帧格式3.认识MAC地址4.以太网帧格式如何封装/解包&#xff1f;5.以太网帧格式如何分用&#xff1f; 2.重谈局域网通信原理0.如何形象的理解&#xff1f;1.理解局域网通信2.在发送数据的时候&#xf…