Program Slicing Fails to Identify Unreachable Dead Code with Logical Contradictions
Summary
WALA's program slicing tool fails to detect and filter out dead code when the unreachability depends on logical reasoning (e.g., if (a < 0 && a > 100)). This causes the slicer to incorrectly select seed statements from unreachable code paths, producing incorrect slice results.
Expected Behavior
When slicing code containing a logically contradictory condition (e.g., if (a < 0 && a > 100)), the slicer should:
- Recognize that the condition is always false through constant propagation and range analysis
- Mark the entire if-block as unreachable dead code
- Exclude unreachable code from seed statement candidates
- When using a method name (e.g.,
println) as the seed, prefer reachable statements over unreachable ones
Actual Behavior
The slicer:
- Does NOT detect the logical contradiction in the condition
- Treats the unreachable code as normal code
- Selects a println statement from the dead code block as the seed (line 25 instead of line 40)
- Produces an incorrect slice containing dead code that will never execute
Steps to Reproduce
1. Test Code
package com.ibm.wala.examples.metamorphic;
public class MT3_DeadCode_AlwaysFalseCondition {
public static void main(String[] args) {
// Input variables
int x = args.length > 0 ? Integer.parseInt(args[0]) : 10;
int y = 20;
// Computation
int a = x + y;
int b = compute(x);
// === DEAD CODE: Always-false condition ===
if (a < 0 && a > 100) { // Logically impossible: a cannot be both <0 AND >100
// This code will NEVER execute
a = a * 1000;
b = b * 1000;
int result = a + b;
System.out.println("Dead: " + result);
}
// === End of dead code ===
// Conditional branch
if (a > 15) {
b = b * 2;
} else {
b = b + 5;
}
// Target variable
int result = a + b;
// Slice seed point - THIS is where we want to slice from
System.out.println(result); // Line 40 - INTENDED SEED POINT
// Subsequent unrelated code
int z = 100;
int w = z * 2;
}
private static int compute(int input) {
int temp = input * 3;
if (temp > 20) {
temp = temp - 5;
}
return temp;
}
}
2. Run Slicing
gradle run \
-PmainClass="com.ibm.wala.examples.drivers.SlicingDriver" \
--args="-appJar build/libs/myapp.jar \
-mainClass Lcom/ibm/wala/examples/metamorphic/MT3_DeadCode_AlwaysFalseCondition \
-targetMethod println \
-applicationOnly true"
3. Observe Results
> Task :processResources NO-SOURCE
> Task :classes
> Task :run
Analyzing: build/libs/WALA-start-master-0.1.jar
Restricting to entrypoint: Lcom/ibm/wala/examples/metamorphic/MT3_DeadCode_AlwaysFalseCondition
Slice configuration -> data: NO_HEAP, control: FULL
Seed statement: com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: invokevirtual < Application, Ljava/io/PrintStream, println(Ljava/lang/String;)V > 19,21 @70 exception:22 @ src\main\java\com\ibm\wala\examples\metamorphic\MT3_DeadCode_AlwaysFalseCondition.java:25 -> System.out.println("Dead: " + result); (applicationOnly)
Backward slice (size=30, applicationOnly):
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: 3 = arraylength 1 @ src\main\java\com\ibm\wala\examples\metamorphic\MT3_DeadCode_AlwaysFalseCondition.java:12 -> int x = args.length > 0 ? Integer.parseInt(args[0]) : 10;
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: conditional branch(le, to iindex=9) 3,4 @ src\main\java\com\ibm\wala\examples\metamorphic\MT3_DeadCode_AlwaysFalseCondition.java:12 -> int x = args.length > 0 ? Integer.parseInt(args[0]) : 10;
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: 6 = arrayload 1[4] @ src\main\java\com\ibm\wala\examples\metamorphic\MT3_DeadCode_AlwaysFalseCondition.java:12 -> int x = args.length > 0 ? Integer.parseInt(args[0]) : 10;
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: 8 = invokestatic < Application, Ljava/lang/Integer, parseInt(Ljava/lang/String;)I > 6 @8 exception:7 @ src\main\java\com\ibm\wala\examples\metamorphic\MT3_DeadCode_AlwaysFalseCondition.java:12 -> int x = args.length > 0 ? Integer.parseInt(args[0]) : 10;
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: PARAM_CALLER
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: NORMAL_RET_CALLER
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: 11 = binaryop(add) 9 , 10 @ src\main\java\com\ibm\wala\examples\metamorphic\MT3_DeadCode_AlwaysFalseCondition.java:16 -> int a = x + y;
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: 13 = invokestatic < Application, Lcom/ibm/wala/examples/metamorphic/MT3_DeadCode_AlwaysFalseCondition, compute(I)I > 9 @25 exception:12 @ src\main\java\com\ibm\wala\examples\metamorphic\MT3_DeadCode_AlwaysFalseCondition.java:17 -> int b = compute(x);
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: PARAM_CALLER
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: NORMAL_RET_CALLER
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: conditional branch(ge, to iindex=42) 11,4 @ src\main\java\com\ibm\wala\examples\metamorphic\MT3_DeadCode_AlwaysFalseCondition.java:20 -> if (a < 0 && a > 100) {
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: conditional branch(le, to iindex=42) 11,14 @ src\main\java\com\ibm\wala\examples\metamorphic\MT3_DeadCode_AlwaysFalseCondition.java:20 -> if (a < 0 && a > 100) {
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: 16 = binaryop(mul) 11 , 15 @ src\main\java\com\ibm\wala\examples\metamorphic\MT3_DeadCode_AlwaysFalseCondition.java:22 -> a = a * 1000;
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: 17 = binaryop(mul) 13 , 15 @ src\main\java\com\ibm\wala\examples\metamorphic\MT3_DeadCode_AlwaysFalseCondition.java:23 -> b = b * 1000;
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: 18 = binaryop(add) 16 , 17 @ src\main\java\com\ibm\wala\examples\metamorphic\MT3_DeadCode_AlwaysFalseCondition.java:24 -> int result = a + b;
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: 19 = getstatic < Application, Ljava/lang/System, out, <Application,Ljava/io/PrintStream> > @ src\main\java\com\ibm\wala\examples\metamorphic\MT3_DeadCode_AlwaysFalseCondition.java:25 -> System.out.println("Dead: " + result);
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: [invokedynamic] 21 = invokestatic < Application, Ljava/lang/invoke/StringConcatFactory, makeConcatWithConstants(I)Ljava/lang/String; > 18 @65 exception:20 @ src\main\java\com\ibm\wala\examples\metamorphic\MT3_DeadCode_AlwaysFalseCondition.java:25 -> System.out.println("Dead: " + result);
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: NORMAL_RET_CALLER
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: invokevirtual < Application, Ljava/io/PrintStream, println(Ljava/lang/String;)V > 19,21 @70 exception:22 @ src\main\java\com\ibm\wala\examples\metamorphic\MT3_DeadCode_AlwaysFalseCondition.java:25 -> System.out.println("Dead: " + result);
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: PHI
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: PARAM_CALLEE
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: METHOD_ENTRY
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.compute(I)I :: 4 = binaryop(mul) 1 , 3 @ src\main\java\com\ibm\wala\examples\metamorphic\MT3_DeadCode_AlwaysFalseCondition.java:48 -> int temp = input * 3;
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.compute(I)I :: conditional branch(le, to iindex=11) 4,5 @ src\main\java\com\ibm\wala\examples\metamorphic\MT3_DeadCode_AlwaysFalseCondition.java:49 -> if (temp > 20) {
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.compute(I)I :: 7 = binaryop(sub) 4 , 6 @ src\main\java\com\ibm\wala\examples\metamorphic\MT3_DeadCode_AlwaysFalseCondition.java:50 -> temp = temp - 5;
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.compute(I)I :: return 8 @ src\main\java\com\ibm\wala\examples\metamorphic\MT3_DeadCode_AlwaysFalseCondition.java:52 -> return temp;
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.compute(I)I :: PHI
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.compute(I)I :: PARAM_CALLEE
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.compute(I)I :: NORMAL_RET_CALLEE
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.compute(I)I :: METHOD_ENTRY
Forward slice (size=3, applicationOnly):
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: invokevirtual < Application, Ljava/io/PrintStream, println(Ljava/lang/String;)V > 19,21 @70 exception:22 @ src\main\java\com\ibm\wala\examples\metamorphic\MT3_DeadCode_AlwaysFalseCondition.java:25 -> System.out.println("Dead: " + result);
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: EXC_RET_CALLER
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: EXC_RET_CALLEE
Backward thin slice (size=3, applicationOnly):
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: 19 = getstatic < Application, Ljava/lang/System, out, <Application,Ljava/io/PrintStream> > @ src\main\java\com\ibm\wala\examples\metamorphic\MT3_DeadCode_AlwaysFalseCondition.java:25 -> System.out.println("Dead: " + result);
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: NORMAL_RET_CALLER
com.ibm.wala.examples.metamorphic.MT3_DeadCode_AlwaysFalseCondition.main([Ljava/lang/String;)V :: invokevirtual < Application, Ljava/io/PrintStream, println(Ljava/lang/String;)V > 19,21 @70 exception:22 @ src\main\java\com\ibm\wala\examples\metamorphic\MT3_DeadCode_AlwaysFalseCondition.java:25 -> System.out.println("Dead: " + result);
WRONG: Selected line 25 (dead code) instead of line 40 (intended seed)
Backward slice includes dead code:
- Line 20:
if (a < 0 && a > 100) { - Line 22:
a = a * 1000; - Line 23:
b = b * 1000; - Line 24:
int result = a + b; - Line 25:
System.out.println("Dead: " + result);
Comparison with Baseline
Baseline Code: OriginalTest.java (identical except without the dead code block)
public class OriginalTest {
public static void main(String[] args) {
int x = args.length > 0 ? Integer.parseInt(args[0]) : 10;
int y = 20;
int a = x + y;
int b = compute(x);
// NO dead code here
if (a > 15) {
b = b * 2;
} else {
b = b + 5;
}
int result = a + b;
System.out.println(result); // Line 29 - SEED POINT
int z = 100;
int w = z * 2;
}
private static int compute(int input) {
int temp = input * 3;
if (temp > 20) {
temp = temp - 5;
}
return temp;
}
}
Expected: Since the dead code in MT3 is unreachable, the slicing results for both programs should be identical (they are semantically equivalent).
Actual: The slices are completely different because MT3 incorrectly slices from the dead code.
Attached is the code for calling Wala's slicing function. SlicingDriver.java
Would you like me to submit a PR to fix this issue? I'm willing to contribute if there's guidance on the preferred implementation approach.
Thanks for the report @bystc. As you've observed WALA's slicer does not detect the contradiction in your example and eliminate dead code. I feel for cases like this, one might want a separate pass to trim out or mark dead code before running the slicer; I don't recall slicing algorithms that bake in such reasoning. If you're interested in implementing such a dead code detection analysis, and then having a way to pass the results to the slicer, I'd be opening to reviewing a PR and advising on it.
As to the slicer choosing the wrong seed, could you map back to source-level line numbers to ensure you get the desired seed for your use case?
hi @msridhar Thanks for your reply and suggestions! As a beginner in program slicing, I agree with your thoughts on dead code detection. Currently, I mainly hope to explore more slicing-related scenarios to build up my understanding of the tool. If I encounter other questions or discover new scenarios in the future, I might need to consult you again—thank you very much for being willing to help! Regarding the seed issue, I’ll try mapping back to source-level line numbers to confirm as you suggested. Thanks for the guidance!