提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
本文主要是根据南京大学的软件分析课程的作业2,写一下自己的解决方法和心得体会,必须要夸一夸两位软件分析课程的老师,讲的太好了,Tai-e教学版本的设计也非常适合新手!整个算法其实就是下面的一张图(取自南京大学软件分析的课件):
一、ConstantPropagation
1. newBoundaryFact(CFG)
这个主要是对应于边界的初始化,由于常量传播是前向分析,所以应该是:
但是需要注意的是我们应该把参数值初始化为NAC,我的理解是因为现在是过程内分析,我们无法知道参数的值,只能当作变量处理,所以是NAC,具体来说代码就是:
@Override
public CPFact newBoundaryFact(CFG<Stmt> cfg) {
// TODO - finish me
//首先处理参数,参数初始化为NAC,因为过程内分析无法确定参数是否为常量
CPFact boundaryFact=new CPFact();
for(Var param:cfg.getIR().getParams()){
boundaryFact.update(param,Value.getNAC());
}
return boundaryFact;
}
2. newInitialFact()
这一步就是除了entry以外的初始化
代码:
@Override
public CPFact newInitialFact() {
// TODO - finish me
return new CPFact();
}
3. void meetInto(CPFact,CPFact)
这一步是处理将第一个Fact添加到第二个Fact中,因为是must分析,所以称之为meet
@Override
public void meetInto(CPFact fact, CPFact target) {
// TODO - finish me
//变量不重复则直接添加,变量重复则用meetValue
fact.forEach((key,value) ->{
if(!target.keySet().contains(key)){
target.update(key,value);
}else {
Value newV=meetValue(fact.get(key),target.get(key));
target.update(key,newV);
}
});
}
很显然,如果target中没有fact中的变量,那么直接添加就好了;如果有,则需要对比两个的值,也就是下面的meetValue。
meetValue的主要思想是如果两个Value中有一个是UNDEF,那么meet值由另一个变量决定;如果有一个是NAC,那么一定是NAC;如果是两个常量,看值是否相等,如果不相等,是NAC,相等就继续保持该值。
/**
* Meets two Values.
* 如果有一个值是UNDEF,那么meet的值由另一个决定
* 如果有一个是NAC,那么meet的值是NAC
* 如果都是常量,但是常量值不等,那么meet的值是NAC,否则是常量
*/
public Value meetValue(Value v1, Value v2) {
// TODO - finish me
if(v1.isUndef()){
return v2;
}else if (v2.isUndef()){
return v1;
}else if (v1.isNAC() || v2.isNAC()){
return Value.getNAC();
}else {
if(v1.getConstant()== v2.getConstant()){
return v1;
}else {
return Value.getNAC();
}
}
}
4. boolean transferNode(Stmt,CPFact,CPFact)
对应:
这里主要会有三种情况,一个是x=3这种常量直接赋值,一个是x=y,一个是x=a+b这种二元表达式的形式
- x=3:直接更新x的值
- x=y:更新x的值为y的值
- x=a+b,这里我们认为a,b都是变量是因为Tai-e会把常量存在变量里面,具体大家可以去看作业2的要求。这种就比较复杂,需要获取到后面二元表达式的每个变量值和算数结果,这部分代码是evaluate。
代码:
@Override
public boolean transferNode(Stmt stmt, CPFact in, CPFact out) {
// TODO - finish me
CPFact newOut=in.copy();
if(stmt instanceof pascal.taie.ir.stmt.DefinitionStmt){
if (stmt.getDef().isPresent()){
LValue def=stmt.getDef().get();
//LHS应该是变量
if (def instanceof Var){
//如果是x=3或者是x=y这种类型,直接更新即可
if(stmt.getUses().size()==1){
RValue use=stmt.getUses().get(0);
//如果是常量
if(use instanceof IntLiteral){
IntLiteral cons=(IntLiteral) use;
newOut.update((Var) def,Value.makeConstant(cons.getValue()));
}
//如果是变量
else if(use instanceof Var){
Var useVar=(Var) use;
newOut.update((Var) def,newOut.get(useVar));
}
}else {
//如果是二元表达式类型
DefinitionStmt definitionStmt=(DefinitionStmt) stmt;
if (definitionStmt.getRValue() instanceof BinaryExp){
BinaryExp binaryExp=(BinaryExp) definitionStmt.getRValue();
Value newVal=evaluate(binaryExp,newOut);
newOut.update((Var) def,newVal);
}
}
}
}
}
if (!newOut.equals(out)){
out.copyFrom(newOut);
return true;
}
return false;
}
这里不知道有没有更简单的方法了,但是我是用的switch和case…
/**
* 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
BinaryExp binaryExp=(BinaryExp) exp;
Var var1=binaryExp.getOperand1();
Var var2=binaryExp.getOperand2();
if(in.get(var1).isConstant()&&in.get(var2).isConstant()){
int cons1=in.get(var1).getConstant();
int cons2=in.get(var2).getConstant();
if (binaryExp instanceof ArithmeticExp){
ArithmeticExp arithmeticExp=(ArithmeticExp) binaryExp;
switch (arithmeticExp.getOperator().toString()){
case "+":
return Value.makeConstant(cons1+cons2);
case "-":
return Value.makeConstant(cons1-cons2);
case "*":
return Value.makeConstant(cons1*cons2);
case "/":
if (cons2==0){
return Value.getUndef();
}
return Value.makeConstant(cons1/cons2);
case "%":
if(cons2==0){
return Value.getUndef();
}
return Value.makeConstant(cons1%cons2);
}
}else if(binaryExp instanceof ConditionExp){
ConditionExp conditionExp=(ConditionExp) binaryExp;
switch (conditionExp.getOperator().toString()){
case "==":
return Value.makeConstant(cons1==cons2?1:0);
case "!=":
return Value.makeConstant(cons1!=cons2?1:0);
case "<":
return Value.makeConstant(cons1<cons2?1:0);
case ">":
return Value.makeConstant(cons1>cons2?1:0);
case "<=":
return Value.makeConstant(cons1<=cons2?1:0);
case ">=":
return Value.makeConstant(cons1>=cons2?1:0);
}
}else if(binaryExp instanceof ShiftExp){
ShiftExp shiftExp=(ShiftExp) binaryExp;
switch (shiftExp.getOperator().toString()){
case "<<":
return Value.makeConstant(cons1 << cons2);
case ">>":
return Value.makeConstant(cons1 >> cons2);
case ">>>":
return Value.makeConstant(cons1 >>> cons2);
}
}else if (binaryExp instanceof BitwiseExp){
BitwiseExp bitwiseExp=(BitwiseExp) binaryExp;
switch (bitwiseExp.getOperator().toString()){
case "|":
return Value.makeConstant(cons1|cons2);
case "&":
return Value.makeConstant(cons1&cons2);
case "^":
return Value.makeConstant(cons1^cons2);
}
}
}else if (in.get(var1).isNAC()||in.get(var2).isNAC()){
return Value.getNAC();
}
return Value.getUndef();
}
二、Solver.initializeForward(CFG,DataflowResult)
对应:
代码:就是要完成所有节点的初始化
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)){
result.setInFact(node, analysis.newInitialFact());
result.setOutFact(node, analysis.newInitialFact());
}
}
}
三、WorkListSolver.doSolveForward(CFG,DataflowResult)
对应:
代码:for循环里面的函数我们之前已经通过meetInto和transferNode完成了,所以主要是完成外层的循环,那么只有OUT更新了,它的后续加入worklist继续更新即可,因为如果一个node的IN不更新,那么OUT也不会更新,而它的IN就是它的prenode的OUT。
@Override
protected void doSolveForward(CFG<Node> cfg, DataflowResult<Node, Fact> result) {
// TODO - finish me
List<Node> worklist=new ArrayList<>();
//首先将所有的node都加入到worklist中
for(Node node:cfg){
worklist.add(node);
}
while (!worklist.isEmpty()){
Node nowNode=worklist.get(0);
if (!cfg.isEntry(nowNode)) {
for (Node pred:cfg.getPredsOf(nowNode)){
analysis.meetInto(result.getOutFact(pred),result.getInFact(nowNode) );
}
if (analysis.transferNode(nowNode,result.getInFact(nowNode),result.getOutFact(nowNode))){
//如果out改变了,那么就后续节点加入worklist
for (Node succ:cfg.getSuccsOf(nowNode)){
worklist.add(succ);
}
}
}
worklist.remove(0);
}
}