diff --git a/docs/bodyinterceptors.md b/docs/bodyinterceptors.md index 4039ee4508e..bd1de886a9a 100644 --- a/docs/bodyinterceptors.md +++ b/docs/bodyinterceptors.md @@ -101,7 +101,9 @@ Obviously, the code segmentl2 = 2; l3 = 3;is unreachable. It will b ### CopyPropagator -CopyPropagator is aBodyInterceptorthat supports the global copy propagation and constant propagation. +CopyPropagator is aBodyInterceptorthat supports the global copy propagation and constant propagation. + +Refer [Sreekala, S. K. and Vineeth Kumar Paleri. “Copy Propagation subsumes Constant Propagation.” ArXiv abs/2207.03894 (2022): n. pag.](https://arxiv.org/pdf/2207.03894) Example for global copy propagation: diff --git a/shared-test-resources/interceptors/CopyPropagatorTest.class b/shared-test-resources/interceptors/CopyPropagatorTest.class new file mode 100644 index 00000000000..5f0ebb5f6b7 Binary files /dev/null and b/shared-test-resources/interceptors/CopyPropagatorTest.class differ diff --git a/shared-test-resources/interceptors/CopyPropagatorTest.java b/shared-test-resources/interceptors/CopyPropagatorTest.java new file mode 100644 index 00000000000..b0a9ebbe41c --- /dev/null +++ b/shared-test-resources/interceptors/CopyPropagatorTest.java @@ -0,0 +1,11 @@ +public class CopyPropagatorTest { + + void tc1(int w) { + int y, z = 0; + y = w; + w = 10; + z = 20; + int x = y + z; + } + +} \ No newline at end of file diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/CopyPropagatorTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/CopyPropagatorTest.java index 1415c00a12d..6503c11d978 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/CopyPropagatorTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/CopyPropagatorTest.java @@ -1,12 +1,14 @@ package sootup.java.bytecode.interceptors; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; import categories.TestCategories; import java.nio.file.Paths; +import java.util.Arrays; import java.util.Collections; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import sootup.core.graph.MutableStmtGraph; @@ -28,6 +30,9 @@ import sootup.core.signatures.MethodSignature; import sootup.core.types.VoidType; import sootup.core.util.ImmutableUtils; +import sootup.core.util.Utils; +import sootup.core.views.View; +import sootup.java.bytecode.inputlocation.JavaClassPathAnalysisInputLocation; import sootup.java.bytecode.inputlocation.PathBasedAnalysisInputLocation; import sootup.java.core.JavaIdentifierFactory; import sootup.java.core.interceptors.CopyPropagator; @@ -128,6 +133,44 @@ public class CopyPropagatorTest { JAssignStmt estmt13 = JavaJimple.newAssignStmt(r5, NullConstant.getInstance(), noStmtPositionInfo); + public View setUp() { + String baseDir = "../shared-test-resources/interceptors/"; + JavaClassPathAnalysisInputLocation inputLocation = + new JavaClassPathAnalysisInputLocation( + baseDir, SourceType.Library, Collections.emptyList()); + final JavaView view = new JavaView(Arrays.asList(inputLocation)); + return view; + } + + @Test + public void testCopyPropagationWithRedefinition() { + View view = setUp(); + final MethodSignature methodSignature = + view.getIdentifierFactory() + .getMethodSignature( + "CopyPropagatorTest", "tc1", "void", Collections.singletonList("int")); + Body bodyBefore = view.getMethod(methodSignature).get().getBody(); + final Body.BodyBuilder builder = Body.builder(bodyBefore, Collections.emptySet()); + new CopyPropagator().interceptBody(builder, view); + Body bodyAfter = builder.build(); + assertEquals( + Stream.of( + "CopyPropagatorTest this", + "int l1", + "unknown l2, l3, l4", + "this := @this: CopyPropagatorTest", + "l1 := @parameter0: int", + "l3 = 0", + "l2 = l1", + "l1 = 10", + "l3 = 20", + // l2 should not be replaced with l1 as l1 gets redefined + "l4 = l2 + 20", + "return") + .collect(Collectors.toList()), + Utils.filterJimple(bodyAfter.toString())); + } + @Test public void testEqualStmt() { assertTrue(eestmt4.equivTo(eestmt4.withRValue(NullConstant.getInstance()))); diff --git a/sootup.java.core/src/main/java/sootup/java/core/interceptors/CopyPropagator.java b/sootup.java.core/src/main/java/sootup/java/core/interceptors/CopyPropagator.java index 2e4d15867ff..4509c3fe262 100644 --- a/sootup.java.core/src/main/java/sootup/java/core/interceptors/CopyPropagator.java +++ b/sootup.java.core/src/main/java/sootup/java/core/interceptors/CopyPropagator.java @@ -22,6 +22,7 @@ */ import com.google.common.collect.Lists; +import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -91,7 +92,48 @@ else if (rhs instanceof JCastExpr && rhs.getType() instanceof ReferenceType) { } // if rhs is a local, then replace use, if it is possible else if (rhs instanceof Local && !rhs.equivTo(use)) { - newStmt = replaceUse(stmtGraph, newStmt, use, rhs); + Local m = (Local) rhs; + if (use != m) { + Integer defCount = m.getDefs(stmtGraph.getStmts()).size(); + if (defCount == null || defCount == 0) { + throw new RuntimeException("Variable " + m + " used without definition!"); + } else if (defCount == 1) { + newStmt = replaceUse(stmtGraph, newStmt, use, rhs); + continue; + } + + List path = stmtGraph.getExtendedBasicBlockPathBetween(defStmt, newStmt); + if (path == null) { + // no path in the extended basic block + continue; + } + { + boolean isRedefined = false; + Iterator pathIt = path.iterator(); + // Skip first node + pathIt.next(); + // Make sure that m is not redefined along path + while (pathIt.hasNext()) { + Stmt s = (Stmt) pathIt.next(); + if (newStmt == s) { + // Don't look at the last statement + // since it is evaluated after the uses. + break; + } + if (s instanceof AbstractDefinitionStmt) { + if (((AbstractDefinitionStmt) s).getLeftOp() == m) { + isRedefined = true; + break; + } + } + } + + if (isRedefined) { + continue; + } + } + newStmt = replaceUse(stmtGraph, newStmt, use, rhs); + } } } }