@Override
public SetFact<Var> newBoundaryFact(CFG<Stmt> cfg) {//IN[exit]=∅
//空集->集合中没有任何var->新建一个SetFact即可
return new SetFact<>();
}
@Override
public SetFact<Var> newInitialFact() {//IN[B]=∅ 为了完成meet策略,OUT赋一样的初值∅
//空集->集合中没有任何var->新建一个SetFact即可
return new SetFact<>();
}
@Override
protected void doSolveBackward(CFG<Node> cfg, DataflowResult<Node, Fact> result) {
boolean change = false;//记录这一轮是否有至少一个变化的IN
do{
change = false;//这里我忘记恢复初值了,一度导致死循环
for (Node node : cfg) {
if(!cfg.isExit(node)){
for (Node succ : cfg.getSuccsOf(node)) {
//把每个succ的IN与target的OUT取并集
analysis.meetInto(result.getInFact(succ), result.getOutFact(node));
}
//cfg中的node在LiveVariableAnalysis中的类型是Stmt,所以调用了自己实现的代码
//利用返回值判断是否有IN值改变
if(analysis.transferNode(node, result.getInFact(node), result.getOutFact(node)) && !change){
change = true;
}
}
}
}while (change);
}
instance of
判断,否则会报错exp.invokespecial cannot be cast to Var这样的类型转换错误。 if(defB.isPresent()){//java.util.Optional中的isPresent可判断是否为空
if(defB.get() instanceof Var) {//注意一定要判断,LValue不是永远为Var->不判断时会报cannot be cast to Var
res.remove((Var) defB.get());//OUT[B]-DEF[B]
}
}
for(RValue rValue: stmt.getUses()){//注意一定要判断,LValue不是永远为Var->不判断时会报cannot be cast to Var
if(rValue instanceof Var){
res.add((Var) rValue);
}
}
关注细节 issue
evaluate函数判断常量不是看Var的isTempConst,这是理论(能有和真的存int有区别)要看实际:Value.isConstant判断
利用好copyFrom函数,然后要用中间值。如果不使用虽然比较了赋值语句对IN的修改,但没有比较旧的IN和旧的OUT。
newBoundaryFact要用cfg.getIR().getParams()获得方法的参数(不是getVars,这个是变量+%this),记得设置为NAC。(因为不分析过程外的方法,这里可能返回任何职,需要做最保守的假设。显然这是sound但是不精确的。第七课介绍的过程间分析会精确很多。)
IN依然记得赋初值。为了meet策略。
worklist中用有序队列存储,而不是set,因为有forward顺序需求
worklist添加时记得去重(用contains判断)
for(Stmt stmt: cfg.getIR().getStmts()){//这里处理控制流不可达
if(!liveCode.contains(stmt) && !deadCode.contains(stmt)){
deadCode.add(stmt);
}
}
if(analysis.transferNode(node, result.getInFact(node), result.getOutFact(node))){
for(Node pred : cfg.getPredsOf(node)){
if(!nodeQueue.contains(pred)){
nodeQueue.add(pred);
}
}
}
一个方法的子签名只包含它的方法名和方法签名的描述符,如 foo 的子签名是:“
T foo(P,Q,R)
” ,而它的完整签名是:“”。
if(jClass.isInterface()){
q.addAll(hierarchy.getDirectImplementorsOf(jClass));
q.addAll(hierarchy.getDirectSubinterfacesOf(jClass));
}else {
q.addAll(hierarchy.getDirectSubclassesOf(jClass));
}
m(…)
:不修改 fact,edge transfer 是一个恒等函数。 for(int i = 0; i < params.size(); i++){//callee中的param与Invoke中的arg值一一对应
res.update(params.get(i), callSiteOut.get(args.get(i)));
}
从被调用方法的 exit 节点的 OUT fact 中获取返回值(可能有多个,你需要思考一下该怎么处理)
返回一个将调用点等号左侧的变量映射到返回值的 fact。(对应等号左边的变量)
如果该调用点等号左侧没有变量,那么 edge transfer 函数仅会返回一个空 fact。
易错点:多个返回值的处理利用cp.meetValue
for (Var var : edge.getReturnVars()) {
val = cp.meetValue(val, returnOut.get(var));
}
/**
* Dispatches {@code Node} to specific node transfer functions for
* call nodes and non-call nodes.判断OUT是否有变化
*/
@Override
public boolean transferNode(Node node, Fact in, Fact out) {
if (icfg.isCallSite(node)) {
return transferCallNode(node, in, out);
} else {
return transferNonCallNode(node, in, out);
}
}
过程间与过程内求解器仅有两处不同:
在初始化的过程中,过程间求解器需要初始化程序中所有的 IN/OUT fact,也就是 ICFG 的全部节点。但你仅需要对 ICFG 的 entry 方法(比如 main 方法)的 entry 节点设置 boundary fact。这意味着其他方法的 entry 节点和非 entry 节点的初始 fact 是一样的。