<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Tai-E on Feng's Notes</title><link>https://zlh20040308.github.io/tags/tai-e/</link><description>Recent content in Tai-E on Feng's Notes</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Mon, 27 Jul 2026 06:03:42 +0800</lastBuildDate><atom:link href="https://zlh20040308.github.io/tags/tai-e/index.xml" rel="self" type="application/rss+xml"/><item><title>Tai-e Assignment 7: Alias-Aware 的过程间常量传播</title><link>https://zlh20040308.github.io/note/tai-e-assignment-7-alias-aware-%E7%9A%84%E8%BF%87%E7%A8%8B%E9%97%B4%E5%B8%B8%E9%87%8F%E4%BC%A0%E6%92%AD/</link><pubDate>Mon, 27 Jul 2026 06:03:42 +0800</pubDate><guid>https://zlh20040308.github.io/note/tai-e-assignment-7-alias-aware-%E7%9A%84%E8%BF%87%E7%A8%8B%E9%97%B4%E5%B8%B8%E9%87%8F%E4%BC%A0%E6%92%AD/</guid><description>&lt;h2 id="1-实验内容"&gt;1 实验内容&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;为 Java 实现一个 alias-aware 的过程间常量传播分析&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="2-实验概览"&gt;2 实验概览&lt;/h2&gt;
&lt;p&gt;在 Java 中，对实例字段和数组的访问可以形成别名，举例来说，如果变量 x 和 y 指向相同的对象，那么 x.f 和 y.f 这两个字段访问构成了别名，因为它们实际上指向同一个字段。&lt;/p&gt;
&lt;p&gt;在别名存在的情况下，通过对一个字段/数组的访问来修改一个实例字段/数组将会同时修改与这一访问相关的所有别名值。举例来说，如果 x.f，y.f 和 z.f 互为别名，那么 store 语句 x.f = 5; 不仅将 x.f 的值修改为 5，而且同时将 y.f 和 z.f 的值设为 5。因此，为了在常量传播中精确地分析字段和数组的值，我们需要取得被分析程序的别名信息。&lt;/p&gt;
&lt;p&gt;值得注意的是，Java 中的静态字段不能拥有别名：对一个静态字段 T.f，它有唯一的符号名（即 T.f），且只能通过 T.f 被访问。&lt;/p&gt;
&lt;h2 id="3-实验过程重点"&gt;3 实验过程重点&lt;/h2&gt;
&lt;p&gt;在没有引入指针分析之前，过程间常量传播主要的问题在于 evaluate 函数能处理的 Exp 种类太少：&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-java"&gt;public static Value evaluate(Exp exp, CPFact in) {
 if (exp instanceof IntLiteral intLiteral) { ... }
 if (exp instanceof Var var) { ... }
 if (exp instanceof BinaryExp binaryExp) { ... }
 return Value.getNAC();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;它处理不了对象字段（T.f = x 和 o.f = x）和数组（a[..]），所以一个很自然的想法就是在 evaluate 里添加对这两个对象的处理。&lt;/p&gt;</description></item><item><title>Tai-e Assignment 6: 上下文敏感的指针分析</title><link>https://zlh20040308.github.io/note/tai-e-assignment-6-%E4%B8%8A%E4%B8%8B%E6%96%87%E6%95%8F%E6%84%9F%E7%9A%84%E6%8C%87%E9%92%88%E5%88%86%E6%9E%90/</link><pubDate>Sun, 26 Jul 2026 19:03:42 +0800</pubDate><guid>https://zlh20040308.github.io/note/tai-e-assignment-6-%E4%B8%8A%E4%B8%8B%E6%96%87%E6%95%8F%E6%84%9F%E7%9A%84%E6%8C%87%E9%92%88%E5%88%86%E6%9E%90/</guid><description>&lt;h2 id="1-实验内容"&gt;1 实验内容&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;为 Java 实现一个上下文敏感的指针分析框架。&lt;/li&gt;
&lt;li&gt;作为指针分析的一部分，随着指针分析一起实现调用图（call graph）构建。&lt;/li&gt;
&lt;li&gt;实现几种常见的上下文敏感策略（context sensitivity variants）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="2-实验概览"&gt;2 实验概览&lt;/h2&gt;
&lt;h2 id="3-实验过程重点"&gt;3 实验过程重点&lt;/h2&gt;
&lt;p&gt;没啥重点，其实理解起来不算困难，但是总是会在各种上下文之间迷失方向，尤其是当有很多个上下文要处理的时候，经常会把上下文填错。&lt;/p&gt;
&lt;h3 id="关于对象类型敏感"&gt;关于对象、类型敏感&lt;/h3&gt;
&lt;p&gt;要注意在实现的时候是将 recv 相关的对象/类型加入 recv 所携带的上下文，而不是 callSite 所带的上下文。&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-java"&gt;public Context selectContext(CSCallSite callSite, CSObj recv, JMethod callee) {
 return addContext(recv.getContext(), recv.getObject().getContainerType());
}
public Context selectContext(CSCallSite callSite, CSObj recv, JMethod callee) {
 return addContext(recv.getContext(), recv.getObject());
}
&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>Tai-e Assignment 5: 非上下文敏感指针分析</title><link>https://zlh20040308.github.io/note/tai-e-assignment-5-%E9%9D%9E%E4%B8%8A%E4%B8%8B%E6%96%87%E6%95%8F%E6%84%9F%E6%8C%87%E9%92%88%E5%88%86%E6%9E%90/</link><pubDate>Tue, 21 Jul 2026 07:03:41 +0800</pubDate><guid>https://zlh20040308.github.io/note/tai-e-assignment-5-%E9%9D%9E%E4%B8%8A%E4%B8%8B%E6%96%87%E6%95%8F%E6%84%9F%E6%8C%87%E9%92%88%E5%88%86%E6%9E%90/</guid><description>&lt;h2 id="1-实验内容"&gt;1 实验内容&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;为 Java 实现非上下文敏感的指针分析。&lt;/li&gt;
&lt;li&gt;为指针分析实现一个调用图的实时构建算法。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="2-实验概览"&gt;2 实验概览&lt;/h2&gt;
&lt;h2 id="3-实验过程重点"&gt;3 实验过程重点&lt;/h2&gt;
&lt;h3 id="实现-addreachable-函数"&gt;实现 AddReachable 函数&lt;/h3&gt;
&lt;p&gt;&lt;img src="./images/addreachable.png" alt="alt text"&gt;&lt;/p&gt;
&lt;p&gt;这里要注意的一点是我们并不需要显式地实现下面这行伪代码：&lt;/p&gt;
&lt;p&gt;&lt;img src="./images/stmt.png" alt="alt text"&gt;&lt;/p&gt;
&lt;p&gt;因为当我们将当前 method 加入 reachable methods 中时，其实就已经相当于把这个 method 中的 statments 加入到了 reachable statments 里面了，后面要用到这个集合的时候其实也是用来遍历与判重：&lt;/p&gt;
&lt;p&gt;&lt;img src="./images/use_stmt.png" alt="alt text"&gt;&lt;/p&gt;
&lt;p&gt;我们可以用这种方法来遍历 reachable statments ：&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-java"&gt;callGraph.reachableMethods().forEach(method -&amp;gt; {
 method.getIR().getStmts().forEach(stmt -&amp;gt; {
 ...
 });
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意先将需要处理的 stmt 从里面取出来存好再进行处理，因为如果直接在里面进行处理的话，由于你可能会在不经意间执行 addReachableMethod 这种操作，这会触发并发修改异常（java.util.ConcurrentModificationException）。&lt;/p&gt;
&lt;h3 id="处理静态方法"&gt;处理静态方法&lt;/h3&gt;
&lt;p&gt;在实现 void addReachable(JMethod) 的时候，要注意将该方法中所涉及到的静态方法加入到 ReachableMethod ，因为他不像普通方法调用一样，它是可以没有 receiver valuable 的，所以不会在 processCall 函数中被加入到 callGraph 中。但也正是因为这一点，我们可以在通过 addReachable 来将它们加入到 ReachableMethod 中。&lt;/p&gt;
&lt;p&gt;伪代码如下：&lt;/p&gt;</description></item><item><title>Tai-e Assignment 4: 类层次结构分析与过程间常量传播</title><link>https://zlh20040308.github.io/note/tai-e-assignment-4-%E7%B1%BB%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84%E5%88%86%E6%9E%90%E4%B8%8E%E8%BF%87%E7%A8%8B%E9%97%B4%E5%B8%B8%E9%87%8F%E4%BC%A0%E6%92%AD/</link><pubDate>Thu, 16 Jul 2026 19:03:41 +0800</pubDate><guid>https://zlh20040308.github.io/note/tai-e-assignment-4-%E7%B1%BB%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84%E5%88%86%E6%9E%90%E4%B8%8E%E8%BF%87%E7%A8%8B%E9%97%B4%E5%B8%B8%E9%87%8F%E4%BC%A0%E6%92%AD/</guid><description>&lt;h2 id="1-实验内容"&gt;1 实验内容&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;为 Java 实现一个类层次结构分析（Class Hierarchy Analysis，CHA）。&lt;/li&gt;
&lt;li&gt;实现过程间常量传播。&lt;/li&gt;
&lt;li&gt;实现过程间数据流传播的 worklist 求解器。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="2-实验概览"&gt;2 实验概览&lt;/h2&gt;
&lt;h2 id="3-实验过程重点"&gt;3 实验过程重点&lt;/h2&gt;
&lt;h3 id="瞎猫碰上死耗子"&gt;瞎猫碰上死耗子&lt;/h3&gt;
&lt;p&gt;我最疑惑不解的就是文档中的这段描述：&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;JClass getDeclaringClass()：返回该方法签名的声明类，即声明该方法的类。（也就是第 7 讲课件的第 24 页中所描述的 class type）；&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;那好，我们来看看这里对于 class type 的描述是什么：&lt;/p&gt;
&lt;p&gt;&lt;img src="./images/class_type.png" alt="class type"&gt;&lt;/p&gt;
&lt;p&gt;说实话，我没怎么看懂，只是感觉好像就是在描述“某个具体方法在哪个具体的类里”。&lt;/p&gt;
&lt;p&gt;当时也没多想，反正是翻译伪代码嘛，我也不管三七二十一，直接把对 special call 的处理翻译了出来。&lt;/p&gt;
&lt;p&gt;但是当我翻译下面这行伪代码的时候，我却犯了难：&lt;/p&gt;
&lt;p&gt;&lt;img src="./images/presedo_code.png" alt="presedo code"&gt;&lt;/p&gt;
&lt;p&gt;看这描述，我隐约有感觉是要通过 call site 来寻找这个 declared type，但是我找遍了 Invoke 这个类也没找到能返回 JClass 的方法。&lt;/p&gt;
&lt;p&gt;后面尝试了很多稀奇古怪的方法，最终还是将目光落在了 JClass getDeclaringClass() 这个方法上。&lt;/p&gt;
&lt;p&gt;为什么？&lt;/p&gt;
&lt;p&gt;因为 declared type 和 getDeclaringClass 都有 declare 这个词缀，并且我要找的就是能返回 JClass 的方法。&lt;/p&gt;
&lt;p&gt;真是太有理有据了，我简直就是天才，就这么干吧。&lt;/p&gt;
&lt;p&gt;这也不能全怪我，主要是这个 getDeclaringClass 连个注释都没有，我实在不知道是用来干嘛的。&lt;/p&gt;
&lt;p&gt;结果没想到真的成了，这就是我要找的那个方法。&lt;/p&gt;
&lt;h3 id="不太体面的实现"&gt;不太体面的实现&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-java"&gt;protected CPFact transferCallEdge(CallEdge&amp;lt;Stmt&amp;gt; edge, CPFact callSiteOut) {
 List&amp;lt;RValue&amp;gt; uses = edge.getSource().getUses();
 List&amp;lt;Var&amp;gt; params = edge.getCallee().getIR().getParams();
 CPFact newFact = new CPFact();
 for (int i = 0; i &amp;lt; params.size(); i++) {
 if (uses.get(i) instanceof Var var) {
 newFact.update(params.get(i), callSiteOut.get(var));
 }
 }
 return newFact;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个函数实现的有些“不太体面”，这里之所以能这样处理的前提是实参是按顺序放在 uses 里面的，但是这一点并没有在注释和文档中提到，但是我觉得既然都已经有了CallEdge&lt;Stmt&gt;这种抽象了，为何不直接把对应关系放入这个对象里呢？&lt;/p&gt;</description></item><item><title>Tai-e Assignment 3: 死代码检测</title><link>https://zlh20040308.github.io/note/tai-e-assignment-3-%E6%AD%BB%E4%BB%A3%E7%A0%81%E6%A3%80%E6%B5%8B/</link><pubDate>Sun, 12 Jul 2026 19:03:41 +0800</pubDate><guid>https://zlh20040308.github.io/note/tai-e-assignment-3-%E6%AD%BB%E4%BB%A3%E7%A0%81%E6%A3%80%E6%B5%8B/</guid><description>&lt;h2 id="1-实验内容"&gt;1 实验内容&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;为 Java 实现一个死代码（dead code）检测算法。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="2-实验概览"&gt;2 实验概览&lt;/h2&gt;
&lt;p&gt;在这次的实验作业中，我们将会通过组合前两次作业中实现的分析方法：活跃变量分析和常量传播，来实现一个 Java 的死代码检测算法。&lt;/p&gt;
&lt;p&gt;在本次作业中，我们只关注两种死代码：不可达代码（unreachable code）和无用赋值（dead assignment）。&lt;/p&gt;
&lt;h3 id="21-不可达代码"&gt;2.1 不可达代码&lt;/h3&gt;
&lt;p&gt;一个程序中永远不可能被执行的代码被称为不可达代码。我们考虑两种不可达代码：控制流不可达代码（control-flow unreachable code）和分支不可达代码（unreachable branch）。&lt;/p&gt;
&lt;h4 id="控制流不可达代码"&gt;控制流不可达代码&lt;/h4&gt;
&lt;p&gt;在一个方法中，如果不存在从程序入口到达某一段代码的控制流路径，那么这一段代码就是控制流不可达的。&lt;/p&gt;
&lt;p&gt;检测方式：这样的代码可以很简单地利用所在方法的控制流图（CFG，即 control-flow graph）检测出来。我们只需要从方法入口开始，遍历 CFG 并标记可达语句。当遍历结束时，那些没有被标记的语句就是控制流不可达的。&lt;/p&gt;
&lt;h4 id="分支不可达代码"&gt;分支不可达代码&lt;/h4&gt;
&lt;p&gt;在 Java 中有两种分支语句：if 语句和 switch 语句。它们可能会导致分支不可达代码的出现。&lt;/p&gt;
&lt;p&gt;对于一个 if 语句，如果它的条件值（通过常量传播得知）是一个常数，那么无论程序怎么执行，它两个分支中的其中一个分支都不会被走到。这样的分支被称为不可达分支。该分支下的代码也因此是不可达的，被称为分支不可达代码。&lt;/p&gt;
&lt;p&gt;检测方式：为了检测分支不可达代码，我们需要预先对被检测代码应用常量传播分析，通过它来告诉我们条件值是否为常量，然后在遍历 CFG 时，我们不进入相应的不可达分支。&lt;/p&gt;
&lt;h3 id="22-无用赋值"&gt;2.2 无用赋值&lt;/h3&gt;
&lt;p&gt;一个局部变量在一条语句中被赋值，但再也没有被该语句后面的语句读取，这样的变量和语句分别被称为无用变量（dead variable，与活跃变量 live variable 相对）和无用赋值。&lt;/p&gt;
&lt;p&gt;检测方式：为了检测无用赋值，我们需要预先对被检测代码施用活跃变量分析。对于一个赋值语句，如果它等号左侧的变量（LHS 变量）是一个无用变量（换句话说，not live），那么我们可以把它标记为一个无用赋值。&lt;/p&gt;
&lt;p&gt;具体而言，当一个 stmt 是一个赋值语句并且它的 out fact 中找不到左值，就可以把它标记为一个无用赋值。&lt;/p&gt;
&lt;h2 id="3-实验过程重点"&gt;3 实验过程重点&lt;/h2&gt;
&lt;h3 id="判别一个语句是否是一个基本块的最后一句"&gt;判别一个语句是否是一个基本块的最后一句&lt;/h3&gt;
&lt;p&gt;在实现分支不可达代码检测时，在检测到某个分支永远不会被执行之后，需要将这个分支所在的 basic block 中的所有 stmt 都加入 dead code，在添加的时候，我们需要判断此时所遍历到的 stmt 是不是该 basic block 中的最后一条 stmt。&lt;/p&gt;</description></item><item><title>Tai-e Assignment 2: 常量传播和 Worklist 求解器</title><link>https://zlh20040308.github.io/note/tai-e-assignment-2-%E5%B8%B8%E9%87%8F%E4%BC%A0%E6%92%AD%E5%92%8C-worklist-%E6%B1%82%E8%A7%A3%E5%99%A8/</link><pubDate>Fri, 10 Jul 2026 00:00:00 +0800</pubDate><guid>https://zlh20040308.github.io/note/tai-e-assignment-2-%E5%B8%B8%E9%87%8F%E4%BC%A0%E6%92%AD%E5%92%8C-worklist-%E6%B1%82%E8%A7%A3%E5%99%A8/</guid><description>&lt;h2 id="1-实验内容"&gt;1 实验内容&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;为 Java 实现常量传播算法。&lt;/li&gt;
&lt;li&gt;实现一个通用的 worklist 求解器，并用它来解决一些数据流分析问题，例如本次的常量传播。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="2-实验概览"&gt;2 实验概览&lt;/h2&gt;
&lt;p&gt;基本上和上一个实验要做的事情一样，翻译下面这个算法：&lt;/p&gt;
&lt;p&gt;&lt;img src="./images/algorithm.png" alt="常量传播算法伪代码"&gt;&lt;/p&gt;
&lt;h2 id="3-实验过程重点"&gt;3 实验过程重点&lt;/h2&gt;
&lt;h3 id="构造-newboundaryfact"&gt;构造 newBoundaryFact&lt;/h3&gt;
&lt;p&gt;在实现 newBoundaryFact() 的时候，要小心地处理每个会被分析的方法的参数。具体来说，要将它们的值初始化为 NAC ，这是因为常量传播算法是个保守的must分析，而方法的参数的值肯定会被定义，但是我们又不可能得知他是不是个常量（又或者说就算知道了也不可能知道是哪个具体常量）。&lt;/p&gt;
&lt;p&gt;要做到这一点，我们就必须要知道一个方法中有哪些参数。&lt;/p&gt;
&lt;p&gt;实验框架提供了 pascal.taie.ir.IR 这个类，里面有一个方法正好可以满足我们的需求：&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-java"&gt;/**
 * @return the parameters in this IR (&amp;quot;this&amp;quot; variable is excluded).
 * The order of the parameters in the resulting list is the same as
 * the order they are declared in the method.
 */
List&amp;lt;Var&amp;gt; getParams();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="transfernode-中对于-function-call-的处理"&gt;transferNode 中对于 function call 的处理&lt;/h3&gt;
&lt;p&gt;讲义上有个地方我觉得写得有问题：&lt;/p&gt;</description></item><item><title>Tai-e Assignment 1: 活跃变量分析与迭代求解器</title><link>https://zlh20040308.github.io/note/tai-e-assignment-1-%E6%B4%BB%E8%B7%83%E5%8F%98%E9%87%8F%E5%88%86%E6%9E%90%E4%B8%8E%E8%BF%AD%E4%BB%A3%E6%B1%82%E8%A7%A3%E5%99%A8/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0800</pubDate><guid>https://zlh20040308.github.io/note/tai-e-assignment-1-%E6%B4%BB%E8%B7%83%E5%8F%98%E9%87%8F%E5%88%86%E6%9E%90%E4%B8%8E%E8%BF%AD%E4%BB%A3%E6%B1%82%E8%A7%A3%E5%99%A8/</guid><description>&lt;h2 id="1-实验内容"&gt;1 实验内容&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;为 Java 实现一个活跃变量分析（Live Variable Analysis）。&lt;/li&gt;
&lt;li&gt;实现一个通用的迭代求解器（Iterative Solver），用于求解数据流分析问题，也就是本次作业中的活跃变量分析。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="2-实验概览"&gt;2 实验概览&lt;/h2&gt;
&lt;p&gt;这次的实验是“函数填空”的类型，实验给出基础框架，我们实现关键函数。&lt;/p&gt;
&lt;p&gt;那了解这种项目最好的方式就是通过函数调用链一个个函数往上看，看看我们所实现的函数是怎么样被框架调用的。&lt;/p&gt;
&lt;p&gt;第一个小实验是实现活跃变量分析，具体来说是要实现 LiveVariableAnalysis 中的如下 API：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SetFact newBoundaryFact(CFG)&lt;/li&gt;
&lt;li&gt;SetFact newInitialFact()&lt;/li&gt;
&lt;li&gt;void meetInto(SetFact,SetFact)&lt;/li&gt;
&lt;li&gt;boolean transferNode(Stmt,SetFact,SetFact)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们可以在IDEA中通过将光标移动到函数名上再按 Alt + F7 快捷键来查看这个函数在哪些地方被调用了。&lt;/p&gt;
&lt;p&gt;不过上面这几个函数并没有找到被调用的地方，那我们可以大胆猜测这些函数是为接下来要实现的迭代求解器服务的。&lt;/p&gt;
&lt;p&gt;第二个小实验是要实现两个方法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Solver.initializeBackward(CFG,DataflowResult)&lt;/li&gt;
&lt;li&gt;IterativeSolver.doSolveBackward(CFG,DataflowResult)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;首先看第一个方法所涉及到的函数调用链，自上而下依次是：&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-java"&gt;DataflowResult&amp;lt;Node, Fact&amp;gt; analyze(IR ir);

DataflowResult&amp;lt;Node, Fact&amp;gt; solve(CFG&amp;lt;Node&amp;gt; cfg);

DataflowResult&amp;lt;Node, Fact&amp;gt; initialize(CFG&amp;lt;Node&amp;gt; cfg);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;首先看第一个函数：&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-java"&gt;public DataflowResult&amp;lt;Node, Fact&amp;gt; analyze(IR ir) {
 CFG&amp;lt;Node&amp;gt; cfg = ir.getResult(CFGBuilder.ID);
 return solver.solve(cfg);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;把IR转换成CFG，然后传给solver进行求解。&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-java"&gt;public DataflowResult&amp;lt;Node, Fact&amp;gt; solve(CFG&amp;lt;Node&amp;gt; cfg) {
 DataflowResult&amp;lt;Node, Fact&amp;gt; result = initialize(cfg);
 doSolve(cfg, result);
 return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;根据CFG初始化DataflowResult&lt;/p&gt;</description></item></channel></rss>