【静态分析】软件分析课程实验A1-活跃变量分析和迭代求解器

1 作业导览

  • 为 Java 实现一个活跃变量分析(Live Variable Analysis)。
  • 实现一个通用的迭代求解器(Iterative Solver),用于求解数据流分析问题,也就是本次作业中的活跃变量分析。

Live Variable Analysis 详见【静态分析】静态分析笔记03 - 数据流分析(应用)-CSDN博客。

我们已经在 Tai-e 的框架代码中提供了你需要的一切基础设施,包括但不限于:程序分析接口、常用数据结构(如数据流信息的表示)、必要的程序信息(如控制流图)等内容。由此,你就可以便利地在 Tai-e 上实现各类数据流分析了。例如在本次作业中,你要在框架代码中补全一些关键部分,从而实现要求的活跃变量分析与迭代求解器。

需要特别注意的是,所有作业文档都只会简要地介绍本次作业中必要的一些 API。因此,如果要搞清楚 API 的实现机制、真正理解框架代码,你必须留出时间阅读、理解相关类的源代码和相应的注释。这是你提升快速上手复杂代码能力的必经之路。

2 实现活跃变量分析

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

为了在 Tai-e 中实现活跃变量分析,你需要了解的类有:

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

    这是一个抽象的数据流分析类,是具体的数据流分析与求解器之间的接口。也就是说,一个具体的数据流分析(如活跃变量分析)需要实现它的接口,而求解器(如迭代求解器)需要通过它的接口来求解数据流。

    需要注意的是,这里的API与课程中介绍的稍有区别:为了简单起见,这里的transfer函数处理的是单条语句而非程序块。

  • pascal.taie.ir.exp.Exp

    这是 Tai-e 的 IR 中的一个关键接口,用于表示程序中的所有表达式。它含有很多子类,对应各类具体的表达式。下面是一个 Exp 类继承结构的简单图示:

在 Tai-e 的 IR 中,我们把表达式分为两类:LValue 和 RValue。前者表示赋值语句左侧的表达式,如变量(x = … )、字段访问(x.f = …)或数组访问(x[i] = …);后者对应地表示赋值语句右侧的表达式,如数值字面量(… = 1;)或二元表达式(… = a + b;)。而有些表达式既可用于左值,也可用于右值,就比如变量(用Var类表示)。因为本次作业只进行活跃变量分析,所以你实际上只需要关注 Var 类就足够了。

pascal.taie.ir.stmt.Stmt

这是 Tai-e 的 IR 中的另一个关键接口,它用于表示程序中的所有语句。对于一个典型的程序设计语言来说,每个表达式都属于某条特定的语句。为了实现活跃变量分析,你需要获得某条语句中定义或使用的所有表达式中的变量。Stmt 类贴心地为这两种操作提供了对应的接口:

Optional<LValue> getDef()
List<RValue> getUses()

每条 Stmt 至多只可能定义一个变量、而可能使用零或多个变量,因此我们使用 Optional 和 List 包装了 getDef()getUses() 的结果。

  • pascal.taie.analysis.dataflow.fact.SetFact<Var>

    这个泛型类用于把 data fact 组织成一个集合。它提供了各种集合操作,如添加、删除元素,取交集、并集等。

  • pascal.taie.analysis.dataflow.analysis.LiveVariableAnalysis

    这个类通过实现 DataflowAnalysis 的接口来定义具体的活跃变量分析。你需要按照第 2.2 节中的说明补全这个类。

需要注意:

  • 本次作业的每个基本块仅含有一条指令
  • 实验的结果最后存储在参数 DataflowResult<Node, Fact> result
  • 用到了Java的一些特性
    • Optional特性:isPresent() 和 get()的使用
    • 泛型特性

2.2 你的任务

你的第一个任务是实现 LiveVariableAnalysis 中的如下 API:

  • SetFact newBoundaryFact(CFG)
  • SetFact newInitialFact()
  • void meetInto(SetFact,SetFact)
  • boolean transferNode(Stmt,SetFact,SetFact)

此处 meetInto() 的设计可能与你设想的稍有差别:它接受 facttarget 两个参数并把 fact 集合并入 target 集合。这个函数有什么用呢?考虑一下上图中 meetInto() 附近的那行伪代码,它会取出 B 的所有后继,然后把它们 IN facts 的并集赋给 OUT[B]。如果这行代码用 meetInto() 来实现,那么我们就可以根据下图所示,用 meetInto(IN[S], OUT[B])B 的每个后继 SIN fact 直接并入到 OUT[B] 中:

这种设计还有更多的优化空间:我们可以忽略一些不变的 IN fact 来提高效率。例如在某次迭代中,如果 IN[S2] 有改变而 IN[S3] 没有,我们就不必依照课堂上讲述的办法求出 IN[S2]IN[S3] 的并,而大可以忽略 IN[S3],只把 IN[S2] 并入 OUT[S1]。不过这个优化超出了本次作业的范畴,你不用考虑实现它。

当然,为了实现上面所说的 meet 策略,你需要在初始化阶段给每条语句的 OUT[S] 赋上和 IN[S] 一样的初值。

除了 meetInto() ,其他 API 的实现都直接依照了课上讲述的算法。需要注意的是,框架中LiveVariableAnalysis 的 API 都继承自 DataflowAnalysis,而该类为了支持各种数据流分析,接口中可能包含了一些你用不上的参数。因此,某些参数在作业中用不上是正常现象。

我们已经提供了四个 API 的框架代码,你的任务就是补全标有“TODO – finish me”的部分。

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

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

    /**
     * 负责创建和初始化虚拟结点的 Data Flow Fact
     *
     * @return 初始化好的节点
     */
    @Override
    public SetFact<Var> newBoundaryFact(CFG<Stmt> cfg) {
        // TODO - finish me
        return new SetFact<>();
    }

    /**
     * 负责创建和初始化控制流图中除了 Entry 和 Exit 之外的结点的 Data Flow Fact
     * 控制流图中一个结点的 IN 和 OUT 分别对应一个 Data Flow Fact ,记录当前程序点时变量的状态
     *
     * @return 初始化好的节点
     */
    @Override
    public SetFact<Var> newInitialFact() {
        // TODO - finish me
        // 节点初始化为空
        return new SetFact<>();
    }

    /**
     * 负责处理 transfer function 之前可能遇到多个 IN 时的合并处理
     */
    @Override
    public void meetInto(SetFact<Var> fact, SetFact<Var> target) {
        // TODO - finish me
        target.union(fact);
    }

    /**
     * 负责实现控制流图中结点的 transfer function:
     * IN[B] = 本块中 use 出现在 define 之前的变量 U(OUT[B] 的 live 情况 - 本块中 define 的变量)。
     * 由于本次作业的每个基本块仅含有一条指令,所以可以不用考虑变量的 use 是否出现在 define 之前
     *
     * @return 如果 IN 改变,返回 true;否则返回 false
     */
    @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;
    }
}

3 实现迭代求解器

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

为了在 Tai-e 中实现迭代求解器,你需要了解的类有:

  • pascal.taie.analysis.dataflow.fact.DataflowResult

    该类对象用于维护数据流分析的 CFG 中的 fact。你可以通过它的 API 获取、设置 CFG 节点的 IN factsOUT facts

  • pascal.taie.analysis.graph.cfg.CFG

    这个类用于表示程序中方法的控制流图(control-flow graphs)。它是可迭代的,也就是说你可以通过一个 for 循环遍历其中的所有节点:

CFG<Node> cfg = ...;
for (Node node : cfg) {
    ...
}

你可以通过 CFG.getPredsOf(Node)CFG.getSuccsOf(Node) 这两个方法遍历 CFG 中节点的所有前驱和后继,比如你可以写:

for (Node succ : cfg.getSuccsOf(node)) {
    ...
}
  • pascal.taie.analysis.dataflow.solver.Solver

    这是数据流分析求解器的基类,包含了求解器的抽象功能。本次作业要实现的迭代求解器就是它的一个派生类(下次作业中的 worklist 求解器也是)。Tai-e 会构建待分析程序的 CFG 并传给 Solver.solve(CFG),然后世界就开始运转了。你可能注意到了,这个类中有两组 initializedoSolve 方法,分别用于处理前向和后向的数据流分析。虽然稍显冗余,但在这样的设计下代码结构会更干净、直接一些。这个类也是需要你补全实现的,这在第 3.2 节中进一步解释。

  • pascal.taie.analysis.dataflow.solver.IterativeSolver

    这个类扩展了 Solver 的功能并实现了迭代求解算法。当然,它也有待你补全。

3.2 你的任务

你的第二个任务是实现上面提到的 Solver 类的两个方法:

  • Solver.initializeBackward(CFG,DataflowResult)
  • IterativeSolver.doSolveBackward(CFG,DataflowResult)

因为活跃变量分析是一个后向分析,所以你只需要关注后向分析相关的方法就足够了。在 initializeBackward() 中,你需要实现第 2.2 节中算法的前三行;在 doSolveBackward() 则要完成对应的 while 循环。

提示

每个 Solver 对象都在字段 analysis 中保存了相应的数据流分析对象,在本次作业中就是一个 LiveVariableAnalysis 对象。你需要用它提供的相关接口来实现分析求解器。

/**
     * 实现算法前三行的初始化操作
     * 调用函数 newBoundaryFact 和 newInitialFact (DataflowAnalysis 中)
     */
    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());
            }
        }
    }
/**
     * 完成 while 循环
     */
    @Override
    protected void doSolveBackward(CFG<Node> cfg, DataflowResult<Node, Fact> result) {
        // TODO - finish me
        boolean flag = true;
        while (flag) {
            flag = false;
            for (Node node : cfg) {
                if (cfg.isExit(node)) continue;  // 出口节点不需要特殊处理
                // 获取当前节点的 OUT 和 IN
                Fact outFact = result.getOutFact(node);
                Fact inFact = result.getInFact(node);
                // 对于每个节点,调用函数 meetInto 和 transferNode (DataflowAnalysis 中)
                for (Node succs : cfg.getSuccsOf(node)) {
                    Fact succsInFact = result.getInFact(succs);  // 获取 succs 节点的 IN 的 Fact 值
                    analysis.meetInto(succsInFact, outFact);
                }
                if (analysis.transferNode(node, inFact, outFact)) {
                    flag = true;
                }
            }
        }
    }

4 运行与测试

你可以按我们在 Tai-e 框架(教学版)配置指南 中提到的方式来运行你的分析。

在本作业中,Tai-e 对输入类的每个方法进行活跃变量分析,并输出分析的结果,也就是每个语句的 OUT fact 所包含的数据流信息(活跃变量):

--------------------<Assign: int assign(int,int,int)> (livevar)--------------------
[0@L4] d = a + b; null
[1@L5] b = d; null
[2@L6] c = a; null
[3@L7] return b; null

输出结果的每一行以该语句的位置信息开始,例如 [0@L4] 表示该语句在 IR 中的索引为 0,且在源码中处于第 4 行。输出结果的每一行末尾为该语句的 OUT fact,(当你未完成作业时结果为 null)。当你完成了所有的空缺代码后,分析的输出应当形如:

--------------------<Assign: int assign(int,int,int)> (livevar)--------------------
[0@L4] d = a + b; [a, d]
[1@L5] b = d; [a, b]
[2@L6] c = a; [b]
[3@L7] return b; []

注意:此处 return b; 这条语句也用到了 b 变量。

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

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

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

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

相关文章

facenet人脸检测+人脸识别+性别识别+表情识别+年龄识别的C++部署

文章目录 一. 人脸检测二.人脸识别facenet2.1 训练人脸识别模型2.2 导出ONNX2.3 测试 三.人脸属性&#xff08;性别、年龄、表情、是否戴口罩&#xff09;3.1 训练3.2 导出ONNX3.3 测试 四. 集成应用五、Jetson 部署5.1 NX5.2 NANO 一. 人脸检测 代码位置&#xff1a;1.detect …

深入理解数据结构第五弹——排序(2)——快速排序

排序&#xff08;1&#xff09;&#xff1a;深入了解数据结构第四弹——排序&#xff08;1&#xff09;——插入排序和希尔排序-CSDN博客 前言&#xff1a; 在前面我们已经讲过了几种排序方式&#xff0c;他们的效率有快有慢&#xff0c;今天我们来学习一种非常高效的排序方式…

【windows-搭建Ubuntu22LTS】

一、环境要求 1. windows版本要求 至少Windows 10 2020年5月(2004) 版, Windows 10 2019年5月(1903) 版&#xff0c;或者 Windows 10 2019年11月(1909) 版 2. 控制面板开启相关的程序(需要重启) 二、Microsoft store安装unbuntu 下载后直接运行&#xff08;稍微等会&#…

Linux软件安装和部署Java代码

文章目录 1.软件安装1.1.软件安装方式1.2.常用软件安装1.2.1 安装jdk1.2.2 安装Tomcat1.2.3 安装MySQL1.2.4 安装lrzsz 2.项目部署2.1.手工部署项目2.2 通过Shell脚本自动部署项目 1.软件安装 1.1.软件安装方式 &#xff08;1&#xff09;二进制发布包安装&#xff1a; 软件已…

基于SSM的学校在线考试系统的设计与实现

功能需求 管理员模块 管理员模块是整个学校在线考试系统中最为重要的管理者&#xff0c;能够对网站内的各种信息进行管理&#xff0c;能够对教师、学生的个人资料进行管理&#xff0c;对于已经离校的学生将其剔除考试名单&#xff0c;将新入校的学生纳入到考试名单中。对于入…

用 element ui 实现季度选择器

由于在数据项目中经常以各种时间条件查询数据&#xff0c;所以时间选择器&#xff08;DatePicker&#xff09;组件是很常用的组件。但是在我使用的 Element UI 中&#xff0c;缺少了季度选择器的功能。 简易实现 一开始我根据时间范围使用 select 去遍历,如 2024-Q1、2023-Q4…

win/mac达芬奇19下载:DaVinci Resolve Studio 19

DaVinci Resolve Studio 19 是一款功能强大的视频编辑和调色软件&#xff0c;广泛应用于电影、电视和网络节目的后期制作。这款软件不仅提供了专业的剪辑、调色和音频处理工具&#xff0c;还引入了全新的DaVinci Neural Engine AI工具&#xff0c;对100多项功能进行了大规模升级…

美化博客文章(持续更新)

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;游戏实现&#xff1a;贪吃蛇​​​​​​ &#x1f337;追光的人&#xff0c;终会万丈光芒 前言&#xff1a; 该文提供我的一些文章设计的一些方法 目录 1.应用超链接 1.应用超链接

差速机器人模型LQR 控制仿真——路径模拟

LQR路径跟踪要求路径中带角度&#xff0c;即坐标&#xff08;x,y,yaw&#xff09;&#xff0c;而一般我们的规划出来的路径不带角度。这里通过总结相关方法&#xff0c;并提供一个案例。 将点路径拟合成一条完整的线路径算法 将点路径拟合成一条完整的线路径是一个常见的问题…

【Java开发指南 | 第十五篇】Java Character 类、String 类

读者可订阅专栏&#xff1a;Java开发指南 |【CSDN秋说】 文章目录 Java Character 类转义序列 Java String 类连接字符串 Java Character 类 Character 类是 Java 中用来表示字符的包装类&#xff0c;它提供了一系列静态方法用于对字符进行操作&#xff0c;其主要分为静态方法…

06 JavaScript学习:语句

JavaScript 语句是用来执行特定任务或操作的一组指令。它可以包括变量声明、条件语句、循环语句、函数调用等。JavaScript 语句以分号结尾&#xff0c;每个语句都会被解释器执行。 分号 ; 在JavaScript中&#xff0c;分号&#xff08;;&#xff09;用于表示语句的结束。尽管在…

python爬虫-----深入了解 requests 库(第二十五天)

&#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; &#x1f388;&#x1f388;所属专栏&#xff1a;python爬虫学习&#x1f388;&#x1f388; ✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天…

【汇编语言】初识汇编

【汇编语言】初识汇编 文章目录 【汇编语言】初识汇编前言由机器语言到汇编语言机器语言与机器指令汇编语言与汇编指令汇编语言程序示例 计算机组成指令和数据的表示计算机的存储单元计算机的总线 内存读写与地址空间CPU对存储器的读写内存地址空间 总结 前言 为什么要学习汇编…

Numpy重修系列(一) --- 初识Numpy

一、为什么使用Numpy&#xff1f; 1.1、简介 Python科学计算基础包&#xff0c;提供 多维数组对象 、派生对象&#xff08;掩码数组、矩阵&#xff09; 数组的快速操作&#xff08;数学计算、逻辑、形状变化、排序、选择、输入输出、离散傅里叶变换、基本线性代数、基本统计运…

数据分析案例-中国黄金股票市场的EDA与价格预测

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

【数据结构】单链表经典算法题的巧妙解题思路

目录 题目 1.移除链表元素 2.反转链表 3.链表的中间节点 4.合并两个有序链表 5.环形链表的约瑟夫问题 解析 题目1&#xff1a;创建新链表 题目2&#xff1a;巧用三个指针 题目3&#xff1a;快慢指针 题目4&#xff1a;哨兵位节点 题目5&#xff1a;环形链表 介绍完了…

Activity——spring方式创建activiti所需数据表结构

文章目录 前言依赖引入编写数据库连接等配置配置日志文件编写java代码生成数据库表结构问题反馈与解决思路问题一&#xff1a;Cause: java.sql.SQLSyntaxErrorException: Table activiti_02.act_ge_property doesnt exist 为什么文件名必须写死&#xff1f; 前言 在之前创建ac…

循序渐进丨使用 Python 向 MogDB 数据库批量操作数据的方法

当我们有时候需要向数据库里批量插入数据&#xff0c;或者批量导出数据时&#xff0c;除了使用传统的gsql copy命令&#xff0c;也可以通过Python的驱动psycopg2进行批量操作。本文介绍了使用psycopg2里的executemany、copy_from、copy_to、copy_expert等方式来批量操作 MogDB …

js-pytorch:开启前端+AI新世界

嗨&#xff0c; 大家好&#xff0c; 我是 徐小夕。最近在 github 上发现一款非常有意思的框架—— js-pytorch。它可以让前端轻松使用 javascript 来运行深度学习框架。作为一名资深前端技术玩家&#xff0c; 今天就和大家分享一下这款框架。 往期精彩 Nocode/Doc&#xff0c;可…

python爬虫之爬取携程景点评价(5)

一、景点部分评价爬取 【携程攻略】携程旅游攻略,自助游,自驾游,出游,自由行攻略指南 (ctrip.com) import requests from bs4 import BeautifulSoupif __name__ __main__:url https://m.ctrip.com/webapp/you/commentWeb/commentList?seo0&businessId22176&busines…