From c8a0745b9e5fb3513fc38a0d29b62f64f6800fc0 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Tue, 14 Jan 2025 19:52:00 +0100 Subject: [PATCH 1/3] Fix the ConstantValueToInitializerTransformer Currently, the ConstantValueToInitializerTransformer assumes that when a Field is written *anywhere* within , it does not need to have initialization definition. However, this initialization could happen in a branch. --- .../ConstantValueToInitializerTransformer.java | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/main/java/soot/toolkits/scalar/ConstantValueToInitializerTransformer.java b/src/main/java/soot/toolkits/scalar/ConstantValueToInitializerTransformer.java index ad5a66a4c89..bcda21c46d7 100644 --- a/src/main/java/soot/toolkits/scalar/ConstantValueToInitializerTransformer.java +++ b/src/main/java/soot/toolkits/scalar/ConstantValueToInitializerTransformer.java @@ -38,13 +38,10 @@ import soot.Type; import soot.Unit; import soot.UnitPatchingChain; -import soot.Value; -import soot.ValueBox; import soot.VoidType; import soot.jimple.AssignStmt; import soot.jimple.Constant; import soot.jimple.DoubleConstant; -import soot.jimple.FieldRef; import soot.jimple.FloatConstant; import soot.jimple.InstanceFieldRef; import soot.jimple.IntConstant; @@ -123,7 +120,7 @@ public void transformClass(SootClass sc) { if (sf.isStatic()) { Stmt initStmt = jimp.newAssignStmt(jimp.newStaticFieldRef(sf.makeRef()), constant); if (smInit == null) { - smInit = getOrCreateInitializer(sc, alreadyInitialized); + smInit = getOrCreateInitializer(sc); } if (smInit != null) { smInit.getActiveBody().getUnits().addFirst(initStmt); @@ -196,7 +193,7 @@ private boolean isInstanceFieldAssignedConstantInBody(SootField sf, Constant con return false; } - private SootMethod getOrCreateInitializer(SootClass sc, Set alreadyInitialized) { + private SootMethod getOrCreateInitializer(SootClass sc) { // Create a static initializer if we don't already have one SootMethod smInit = sc.getMethodByNameUnsafe(SootMethod.staticInitializerName); if (smInit == null) { @@ -206,17 +203,6 @@ private SootMethod getOrCreateInitializer(SootClass sc, Set alreadyIn smInit.setModifiers(Modifier.PUBLIC | Modifier.STATIC); } else if (smInit.isPhantom()) { return null; - } else { - // We need to collect those variables that are already initialized somewhere - for (Unit u : smInit.retrieveActiveBody().getUnits()) { - Stmt s = (Stmt) u; - for (ValueBox vb : s.getDefBoxes()) { - Value value = vb.getValue(); - if (value instanceof FieldRef) { - alreadyInitialized.add(((FieldRef) value).getField()); - } - } - } } return smInit; } From 4047aae55472e871d7c4c100639e7e6abce4e424 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Wed, 15 Jan 2025 08:48:42 +0100 Subject: [PATCH 2/3] Improve type assigner performance Parallelize minimize typing check and reuse local defs/uses. --- .../toolkits/typing/fast/TypeResolver.java | 55 +++++++++-- .../toolkits/typing/fast/UseChecker.java | 97 +++++++++++++------ 2 files changed, 118 insertions(+), 34 deletions(-) diff --git a/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java b/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java index 069c4b78b3c..6fe917ec2a9 100644 --- a/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java +++ b/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java @@ -36,6 +36,9 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import soot.ArrayType; import soot.BooleanType; @@ -69,6 +72,7 @@ import soot.jimple.SpecialInvokeExpr; import soot.jimple.Stmt; import soot.jimple.toolkits.typing.Util; +import soot.jimple.toolkits.typing.fast.UseChecker.UseCheckerCache; import soot.toolkits.scalar.LocalDefs; /** @@ -83,6 +87,9 @@ * @author Ben Bellamy */ public class TypeResolver { + private static final int SINGLE_THREAD_LIMIT = 100000; + private static int NUM_CORES = Math.max(1, Runtime.getRuntime().availableProcessors() - 2); + protected final JimpleBody jb; private List assignments; @@ -90,10 +97,12 @@ public class TypeResolver { private Set singleAssignments; private BitSet simple; private final LocalGenerator localGenerator; + private final UseCheckerCache useCheckerCache; public TypeResolver(JimpleBody jb) { this.jb = jb; this.localGenerator = Scene.v().createLocalGenerator(jb); + this.useCheckerCache = new UseCheckerCache(jb); } @@ -402,7 +411,7 @@ private ITyping typePromotion(ITyping tg) { } protected UseChecker createUseChecker(JimpleBody jb) { - return new UseChecker(jb); + return new UseChecker(jb, useCheckerCache); } protected TypePromotionUseVisitor createTypePromotionUseVisitor(JimpleBody jb, ITyping tg) { @@ -453,14 +462,46 @@ protected CastInsertionUseVisitor createCastInsertionUseVisitor(ITyping tg, IHie private ITyping minCasts(Collection sigma, IHierarchy h, int[] count) { count[0] = -1; ITyping r = null; - for (ITyping tg : sigma) { - int n = this.insertCasts(tg, h, true); - if (count[0] == -1 || n < count[0]) { - count[0] = n; - r = tg; + if (sigma.size() <= SINGLE_THREAD_LIMIT) { + for (ITyping tg : sigma) { + int n = this.insertCasts(tg, h, true); + if (count[0] == -1 || n < count[0]) { + count[0] = n; + r = tg; + } } + return r; + } else { + ExecutorService executionService = Executors.newFixedThreadPool(NUM_CORES); + ITyping[] minTyping = new ITyping[1]; + try { + for (ITyping tg : sigma) { + executionService.submit(new Runnable() { + + @Override + public void run() { + int n = insertCasts(tg, h, true); + if (count[0] == -1 || n < count[0]) { + synchronized (count) { + if (count[0] == -1 || n < count[0]) { + count[0] = n; + minTyping[0] = tg; + } + } + } + } + + }); + } + } finally { + executionService.shutdown(); + try { + executionService.awaitTermination(100, TimeUnit.DAYS); + } catch (InterruptedException e) { + } + } + return minTyping[0]; } - return r; } static class WorklistElement { diff --git a/src/main/java/soot/jimple/toolkits/typing/fast/UseChecker.java b/src/main/java/soot/jimple/toolkits/typing/fast/UseChecker.java index b6af0b11e5d..5973997f4e4 100644 --- a/src/main/java/soot/jimple/toolkits/typing/fast/UseChecker.java +++ b/src/main/java/soot/jimple/toolkits/typing/fast/UseChecker.java @@ -117,13 +117,53 @@ public class UseChecker extends AbstractStmtSwitch { private ITyping tg; private IUseVisitor uv; - private LocalDefs defs = null; - private LocalUses uses = null; - private static final Logger logger = LoggerFactory.getLogger(UseChecker.class); + public static class UseCheckerCache { + private LocalDefs defs = null; + private LocalUses uses = null; + + private final Type objectType = Scene.v().getObjectType(); + private final JimpleBody body; + + public UseCheckerCache(JimpleBody body) { + this.body = body; + } + + public Type getObjectType() { + return objectType; + } + + public LocalDefs getDefs() { + LocalDefs d = defs; + if (d == null) { + d = G.v().soot_toolkits_scalar_LocalDefsFactory().newLocalDefs(body); + defs = d; + } + return d; + } + + public LocalUses getUses() { + LocalUses u = uses; + if (u != null) { + return u; + } + u = LocalUses.Factory.newLocalUses(body, getDefs()); + uses = u; + return u; + } + } + + private UseCheckerCache cache; + public UseChecker(JimpleBody jb) { this.jb = jb; + cache = new UseCheckerCache(jb); + } + + public UseChecker(JimpleBody jb, UseCheckerCache cache) { + this.jb = jb; + this.cache = cache; } public void check(ITyping tg, IUseVisitor uv) { @@ -203,6 +243,9 @@ public void caseAssignStmt(AssignStmt stmt) { Value lhs = stmt.getLeftOp(); Value rhs = stmt.getRightOp(); Type tlhs = null; + LocalDefs defs = cache.defs; + LocalUses uses = cache.uses; + final IUseVisitor uv = this.uv; if (lhs instanceof Local) { tlhs = this.tg.get((Local) lhs); @@ -221,10 +264,10 @@ public void caseAssignStmt(AssignStmt stmt) { // is java.lang.Object if (rhs instanceof Local) { Type rhsType = this.tg.get((Local) rhs); - if ((tgType == Scene.v().getObjectType() && rhsType instanceof PrimType) || tgType instanceof WeakObjectType) { + if ((tgType == cache.getObjectType() && rhsType instanceof PrimType) || tgType instanceof WeakObjectType) { if (defs == null) { - defs = G.v().soot_toolkits_scalar_LocalDefsFactory().newLocalDefs(jb); - uses = LocalUses.Factory.newLocalUses(jb, defs); + defs = cache.getDefs(); + uses = cache.getUses(); } // Check the original type of the array from the alloc site @@ -254,9 +297,9 @@ public void caseAssignStmt(AssignStmt stmt) { this.handleArrayRef(aref, stmt); - aref.setBase((Local) this.uv.visit(aref.getBase(), at, stmt)); - stmt.setRightOp(this.uv.visit(rhs, tlhs, stmt)); - stmt.setLeftOp(this.uv.visit(lhs, tlhs, stmt)); + aref.setBase((Local) uv.visit(aref.getBase(), at, stmt)); + stmt.setRightOp(uv.visit(rhs, tlhs, stmt)); + stmt.setLeftOp(uv.visit(lhs, tlhs, stmt)); } else if (lhs instanceof FieldRef) { tlhs = ((FieldRef) lhs).getFieldRef().type(); if (lhs instanceof InstanceFieldRef) { @@ -269,7 +312,7 @@ public void caseAssignStmt(AssignStmt stmt) { rhs = stmt.getRightOp(); if (rhs instanceof Local) { - stmt.setRightOp(this.uv.visit(rhs, tlhs, stmt)); + stmt.setRightOp(uv.visit(rhs, tlhs, stmt)); } else if (rhs instanceof ArrayRef) { ArrayRef aref = (ArrayRef) rhs; Local base = (Local) aref.getBase(); @@ -287,11 +330,11 @@ public void caseAssignStmt(AssignStmt stmt) { // For some fixed type T, we assume that we can fix the array to T[]. if (bt instanceof RefType || bt instanceof NullType) { String btName = bt instanceof NullType ? null : ((RefType) bt).getSootClass().getName(); - if (btName == null || Scene.v().getObjectType().toString().equals(btName) || "java.io.Serializable".equals(btName) + if (btName == null || cache.getObjectType().toString().equals(btName) || "java.io.Serializable".equals(btName) || "java.lang.Cloneable".equals(btName)) { if (defs == null) { - defs = G.v().soot_toolkits_scalar_LocalDefsFactory().newLocalDefs(jb); - uses = LocalUses.Factory.newLocalUses(jb, defs); + defs = cache.getDefs(); + uses = cache.getUses(); } // First, we check the definitions. If we can see the definitions and know the array type // that way, we are safe. @@ -409,39 +452,39 @@ public void caseAssignStmt(AssignStmt stmt) { this.handleArrayRef(aref, stmt); - aref.setBase((Local) this.uv.visit(aref.getBase(), at, stmt)); - stmt.setRightOp(this.uv.visit(rhs, trhs, stmt)); + aref.setBase((Local) uv.visit(aref.getBase(), at, stmt)); + stmt.setRightOp(uv.visit(rhs, trhs, stmt)); } else if (rhs instanceof InstanceFieldRef) { this.handleInstanceFieldRef((InstanceFieldRef) rhs, stmt); - stmt.setRightOp(this.uv.visit(rhs, tlhs, stmt)); + stmt.setRightOp(uv.visit(rhs, tlhs, stmt)); } else if (rhs instanceof BinopExpr) { this.handleBinopExpr((BinopExpr) rhs, stmt, tlhs); } else if (rhs instanceof InvokeExpr) { this.handleInvokeExpr((InvokeExpr) rhs, stmt); - stmt.setRightOp(this.uv.visit(rhs, tlhs, stmt)); + stmt.setRightOp(uv.visit(rhs, tlhs, stmt)); } else if (rhs instanceof CastExpr) { - stmt.setRightOp(this.uv.visit(rhs, tlhs, stmt)); + stmt.setRightOp(uv.visit(rhs, tlhs, stmt)); } else if (rhs instanceof InstanceOfExpr) { InstanceOfExpr ioe = (InstanceOfExpr) rhs; - ioe.setOp(this.uv.visit(ioe.getOp(), Scene.v().getObjectType(), stmt)); - stmt.setRightOp(this.uv.visit(rhs, tlhs, stmt)); + ioe.setOp(uv.visit(ioe.getOp(), Scene.v().getObjectType(), stmt)); + stmt.setRightOp(uv.visit(rhs, tlhs, stmt)); } else if (rhs instanceof NewArrayExpr) { NewArrayExpr nae = (NewArrayExpr) rhs; - nae.setSize(this.uv.visit(nae.getSize(), IntType.v(), stmt)); - stmt.setRightOp(this.uv.visit(rhs, tlhs, stmt)); + nae.setSize(uv.visit(nae.getSize(), IntType.v(), stmt)); + stmt.setRightOp(uv.visit(rhs, tlhs, stmt)); } else if (rhs instanceof NewMultiArrayExpr) { NewMultiArrayExpr nmae = (NewMultiArrayExpr) rhs; for (int i = 0, e = nmae.getSizeCount(); i < e; i++) { - nmae.setSize(i, this.uv.visit(nmae.getSize(i), IntType.v(), stmt)); + nmae.setSize(i, uv.visit(nmae.getSize(i), IntType.v(), stmt)); } - stmt.setRightOp(this.uv.visit(rhs, tlhs, stmt)); + stmt.setRightOp(uv.visit(rhs, tlhs, stmt)); } else if (rhs instanceof LengthExpr) { - stmt.setRightOp(this.uv.visit(rhs, tlhs, stmt)); + stmt.setRightOp(uv.visit(rhs, tlhs, stmt)); } else if (rhs instanceof NegExpr) { - ((NegExpr) rhs).setOp(this.uv.visit(((NegExpr) rhs).getOp(), tlhs, stmt)); + ((NegExpr) rhs).setOp(uv.visit(((NegExpr) rhs).getOp(), tlhs, stmt)); } else if (rhs instanceof Constant) { if (!(rhs instanceof NullConstant)) { - stmt.setRightOp(this.uv.visit(rhs, tlhs, stmt)); + stmt.setRightOp(uv.visit(rhs, tlhs, stmt)); } } } From abed0195afd5b5fc05eca8c0f3d80ffae890f95b Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Wed, 15 Jan 2025 08:53:13 +0100 Subject: [PATCH 3/3] Checkstyle --- src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java b/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java index 6fe917ec2a9..dcaff372626 100644 --- a/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java +++ b/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java @@ -498,6 +498,7 @@ public void run() { try { executionService.awaitTermination(100, TimeUnit.DAYS); } catch (InterruptedException e) { + throw new RuntimeException("Interrupted during type resolving", e); } } return minTyping[0];