<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Home on Feng's Notes</title><link>https://zlh20040308.github.io/</link><description>Recent content in Home 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/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>大学四年回忆录</title><link>https://zlh20040308.github.io/post/%E5%A4%A7%E5%AD%A6%E5%9B%9B%E5%B9%B4%E5%9B%9E%E5%BF%86%E5%BD%95/</link><pubDate>Sat, 11 Jul 2026 00:00:00 +0800</pubDate><guid>https://zlh20040308.github.io/post/%E5%A4%A7%E5%AD%A6%E5%9B%9B%E5%B9%B4%E5%9B%9E%E5%BF%86%E5%BD%95/</guid><description>&lt;p&gt;虽说我对于大学本身没啥留恋的，但是这四年的时光多多少少还是让我成长了许多，趁着还没忘记，记录一下。&lt;/p&gt;
&lt;h2 id="择校"&gt;择校&lt;/h2&gt;
&lt;p&gt;高考完之后，我其实并没有想过将来要从事什么专业，我是2022年入学的，那个时候应该还是互联网行业最鼎盛的时期，不过选择计算机倒也不是追风口，纯粹只是经过排除法之后剩下来的结果。实际上，我当时对于计算机一无所知，甚至在人生的前十八年，我都没有正儿八经地用过电脑，就连电脑游戏也没咋玩过。&lt;/p&gt;
&lt;p&gt;至于学校，当时分数出来后我就知道大概率也就是个末流一本或者是顶流二本，所以我也就不对教学质量抱有什么期望了，想着找个生活质量高点的大学安逸地度过四年，综上，我对于大学的期望有以下几点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;区位：不能离浙江太远，不能在西北和东北。&lt;/li&gt;
&lt;li&gt;住宿：必须要是四人寝，必须上床下桌，必须要有空调，必须要有独立卫浴。&lt;/li&gt;
&lt;li&gt;学习：图书馆的环境要尽量好。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这样筛选下来后，满足条件的只有一个大学：嘉兴大学。&lt;/p&gt;
&lt;p&gt;这大学四年下来也证明了这个决策是正确的，嘉兴大学低下的教学质量（懂不懂2026年了还在教JSP的含金量啊）对于学计算机的我来说反倒是件好事，我不必分配太多精力在课内，有大量的时间在寝室和图书馆探索我喜欢的方向。&lt;/p&gt;
&lt;h2 id="大一上"&gt;大一上&lt;/h2&gt;
&lt;p&gt;这个学期学的课有：C语言和高数。为了简单起见，下文我不会罗列所有课程，一些水课（比如大学心理健康）以及一些看似核心但实际上很水的的课（比如Web开发，但实际上学的是JSP这种老掉牙技术）我都不会罗列出来了&lt;/p&gt;
&lt;p&gt;这段时间的学习方式基本上还是和高中一样，听课然后做题，当时对我来说最折磨的估计就是搭环境和对着报错抓耳挠腮，当年还没有AI，经常对着CSDN和教程弄到深夜。&lt;/p&gt;
&lt;p&gt;总的来说并不算有意思，我其实并不知道自己写这些东西是用来干嘛的，也不知道他们是怎么运作起来的。&lt;/p&gt;
&lt;p&gt;支线：学着入门CTF，当时有个学长很厉害，算是以一己之力撑起了我们学校网络安全的牌面，我也是在机缘巧合下进入了这个社团。&lt;/p&gt;
&lt;h2 id="大一下"&gt;大一下&lt;/h2&gt;
&lt;p&gt;这个学期学的课有：Java、高数、离散。&lt;/p&gt;
&lt;p&gt;这个学期算是对我影响比较大的一个学期，主要是因为接触了Java，当时网上的Java教程可谓是满天飞，以至于我当时误以为所有程序员的归宿就是学Java然后进大厂写网站。&lt;/p&gt;
&lt;p&gt;于是我也一头扎了进去，幻想从里面分一杯羹。&lt;/p&gt;
&lt;p&gt;这个学期还看教程学了下linux，不过当时我完全不知道这东西是用来干嘛的，我以为是另外一门编程语言，毕竟所有操作都是在命令行里，看着就像代码。&lt;/p&gt;
&lt;p&gt;这个学期我学到最重要的东西就是知道了计算机这玩意，想学好还是得自学，要多利用网上的资源。&lt;/p&gt;
&lt;p&gt;支线：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;基本放弃CTF。&lt;/li&gt;
&lt;li&gt;学习了一段时间的数据分析，不过没出一个月我就放弃了，因为这行业和你的编程能力没啥关系，更多是看你有没有对应的 domain knowledge。&lt;/li&gt;
&lt;li&gt;过了英语四级。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="大二上"&gt;大二上&lt;/h2&gt;
&lt;p&gt;这个学期学的课有：数电、数据库、数据结构、线代。&lt;/p&gt;
&lt;p&gt;这个学期应该是我比较迷茫的一个时期，主要是因为我意识到我其实并不喜欢学Java，我对于学习一个又一个中间件技术感到厌倦，我经常听网课听着听着就睡着了。&lt;/p&gt;
&lt;p&gt;更重要的是我知道这个赛道并不是什么康庄大道，和高考一样依旧是千军万马过独木桥。&lt;/p&gt;
&lt;p&gt;所以当时我很清楚，我在做一件自己既不感兴趣，压力又十分大的事，这使我本能地想要退缩。&lt;/p&gt;
&lt;p&gt;说是退缩，但是当时的我其实并没有想好究竟要退到哪里去，就如我之前所说，我当时以为所有程序员的归宿就是学Java进大厂，不存在其他更好的出路，否定了这条路就等同于否定了所有可能性。&lt;/p&gt;
&lt;p&gt;那还能怎么办，考研呗。&lt;/p&gt;
&lt;p&gt;但这条路比上一条路还要恶心，我从小就没有在这种同质化的考试中胜出过，在没有考研动机的情况下选择这条路对我更加令我反胃，我非常讨厌不明不白（某种程度上）地去做一件事情。&lt;/p&gt;
&lt;p&gt;嘻嘻，好像不知道怎么办了捏，那就开始焦虑吧。&lt;/p&gt;
&lt;p&gt;不过这半年也不是完全没有好事发生，在学习线性代数的时候，我学了两门很好的国外课程，一门是MIT的Strang老爷子的课，另一门就是3B1B。算是给我开了一扇窗吧，让我潜意识里意识到国外的课是要比国内的好，为我之后学习国外课程埋下了种子。&lt;/p&gt;
&lt;p&gt;支线：学习算法竞赛，其实学过算法的人都知道这个时间点其实对于学习算法来说其实有点晚了，其实我也不清楚当时是怎么想的，可能是觉得如果能拿个牌子会对将来找工作有帮助？病急乱投医了属于是。&lt;/p&gt;
&lt;h2 id="大二下"&gt;大二下&lt;/h2&gt;
&lt;p&gt;这个学期学的课有：概率统计、计组。&lt;/p&gt;
&lt;p&gt;命运的齿轮就此开始转动。&lt;/p&gt;
&lt;p&gt;在尝试了多个方向无果之后，我意识到最后兜兜转转可能自己还是得考研，虽然很不甘心，但是我依旧还是安慰自己别把事情想得太糟糕，也许这些知识放在以后找工作时也有用呢。&lt;/p&gt;
&lt;p&gt;正好这个学期开了一门计组，于是就想着先把这门学好，当时是看B站的一门课程学习的：&lt;/p&gt;
&lt;p&gt;【【CO001】课程基本信息 计算机组成原理/计算机组成与设计】 &lt;a href="https://www.bilibili.com/video/BV1Ba4y1V7GD"&gt;https://www.bilibili.com/video/BV1Ba4y1V7GD&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;其中最吸引我的便是第四章有关处理器的章节，那是我第一次亲眼见证了一个处理器是怎样从0到1被组装出来。&lt;/p&gt;
&lt;p&gt;那种激动的心情简直难以言表，在此之前，我一直以为像是CPU这种东西是我一辈子也理解不了、接触不到的。&lt;/p&gt;
&lt;p&gt;那是我第一次了解我的计算机。&lt;/p&gt;
&lt;p&gt;在学完计组后，我又开始学OS，同样也是B站上的课程：&lt;/p&gt;
&lt;p&gt;【【Linux操作系统】Lecture 1 Introduction】 &lt;a href="https://www.bilibili.com/video/BV1G7411t7yG"&gt;https://www.bilibili.com/video/BV1G7411t7yG&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这老师讲得相当不错，既有理论也有Linux实操，在我看来比计组那门课还要精彩，也就是在这时，我有了一个大胆的想法：能不能自己写一个？&lt;/p&gt;
&lt;p&gt;你别说，B站上还真有：&lt;/p&gt;
&lt;p&gt;【[完结] 循序渐进，学习开发一个RISC-V上的操作系统 - 汪辰 - 2021春】 &lt;a href="https://www.bilibili.com/video/BV1Q5411w7z5"&gt;https://www.bilibili.com/video/BV1Q5411w7z5&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;注意，这里关键词来了：RISC-V。只要你稍微在B站搜一下有关它的一些视频，那你大概率会搜到一个伟大的开源教育计划：一生一芯。&lt;/p&gt;
&lt;p&gt;点进这个计划的官网稍加了解之后，我就被这个计划的伟大愿景所吸引，随后便果断报名并一发不可收拾地做了起来，并在暑假的前一天通过了入学答辩。&lt;/p&gt;
&lt;p&gt;支线：学习Rust,做rCore，之所以会去学这两件东西其实也是在搜索有关RISC-V的相关学习资料的时候发现了OpenCamp这么个组织，从时间线上来看其实排在一生一芯之前，但这两件事都烂尾了，主要是以当时我的实力来说根本不足以掌握一个几千行代码的代码库，再加上Rust本身也难，学起来就更加困难了。&lt;/p&gt;
&lt;h2 id="大三上"&gt;大三上&lt;/h2&gt;
&lt;p&gt;这个学期学的课有：操作系统、计网、微机原理。&lt;/p&gt;
&lt;p&gt;这学期在做的事情主要就三件事：做ysyx、学Rust、做rCore。&lt;/p&gt;
&lt;p&gt;我学得不亦乐乎，尤其是ysyx，由于我的进度不错，成功申请到了寒假前往线下基地学习的机会。&lt;/p&gt;
&lt;h2 id="大三下"&gt;大三下&lt;/h2&gt;
&lt;p&gt;这个学期学的课有：编译原理。&lt;/p&gt;
&lt;p&gt;寒假在线下基地与助教沟通过后，我知道了想要在体系结构这个领域混口饭吃，还是得要有个硕士文凭。&lt;/p&gt;
&lt;p&gt;理应来说我现在有动机，是时候开始备考了是吧。&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><item><title>Debug：IntelliJ IDEA 在 NixOS 上无法编译 / 自动补全</title><link>https://zlh20040308.github.io/note/debugintellij-idea-%E5%9C%A8-nixos-%E4%B8%8A%E6%97%A0%E6%B3%95%E7%BC%96%E8%AF%91-/-%E8%87%AA%E5%8A%A8%E8%A1%A5%E5%85%A8/</link><pubDate>Sun, 28 Jun 2026 00:00:00 +0000</pubDate><guid>https://zlh20040308.github.io/note/debugintellij-idea-%E5%9C%A8-nixos-%E4%B8%8A%E6%97%A0%E6%B3%95%E7%BC%96%E8%AF%91-/-%E8%87%AA%E5%8A%A8%E8%A1%A5%E5%85%A8/</guid><description>&lt;h2 id="现象"&gt;现象&lt;/h2&gt;
&lt;p&gt;在 NixOS 上用 IDEA 打开一个 Gradle/Java 项目后：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;没有自动补全&lt;/li&gt;
&lt;li&gt;Build 失败&lt;/li&gt;
&lt;li&gt;IDEA 报错：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Abnormal build process termination:
Could not start dynamically linked executable: /home/feng/.jdks/azul-17.0.19/bin/java
NixOS cannot run dynamically linked executables intended for generic
linux environments out of the box.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="排查"&gt;排查&lt;/h2&gt;
&lt;p&gt;IDEA 试图启动自己下载的 Azul JDK 17（&lt;code&gt;~/.jdks/azul-17.0.19/bin/java&lt;/code&gt;）来跑 Build Process。&lt;/p&gt;
&lt;p&gt;手动试一下：&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;~/.jdks/azul-17.0.19/bin/java -version
# Could not start dynamically linked executable
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;确认了 — 这个二进制在 NixOS 上根本无法运行。&lt;/p&gt;
&lt;p&gt;JDK 本身没问题，是 &lt;strong&gt;NixOS 没有标准 Linux 的动态链接器&lt;/strong&gt;：&lt;/p&gt;</description></item><item><title>Getting Started</title><link>https://zlh20040308.github.io/note/getting-started/</link><pubDate>Thu, 25 Jun 2026 00:00:00 +0000</pubDate><guid>https://zlh20040308.github.io/note/getting-started/</guid><description>&lt;p&gt;This is my first note. Hugo with the &lt;a href="https://github.com/yihui/hugo-ivy"&gt;hugo-ivy&lt;/a&gt; theme looks clean.&lt;/p&gt;
&lt;h2 id="how-to-create-a-new-note"&gt;How to create a new note&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;hugo new content note/my-note-title.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then edit the file and add &lt;code&gt;categories&lt;/code&gt; and &lt;code&gt;tags&lt;/code&gt; in the frontmatter.&lt;/p&gt;
&lt;h2 id="how-to-preview"&gt;How to preview&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;hugo server -D
&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>Hello World</title><link>https://zlh20040308.github.io/post/hello-world/</link><pubDate>Thu, 25 Jun 2026 00:00:00 +0000</pubDate><guid>https://zlh20040308.github.io/post/hello-world/</guid><description>&lt;p&gt;这是我的第一篇日记。以后游记、日常都会放在这里。&lt;/p&gt;</description></item><item><title>About</title><link>https://zlh20040308.github.io/about/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://zlh20040308.github.io/about/</guid><description>&lt;p&gt;Hi, I&amp;rsquo;m Feng. This is where I keep my notes on things I learn.&lt;/p&gt;
&lt;h3 id="topics"&gt;Topics&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;NixOS / Nix&lt;/li&gt;
&lt;li&gt;Linux&lt;/li&gt;
&lt;li&gt;Programming&lt;/li&gt;
&lt;li&gt;Tools&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>