]> granicus.if.org Git - llvm/commitdiff
Fix a crash when the default of a switch is removed
authorAndrew Kaylor <andrew.kaylor@intel.com>
Mon, 3 Jun 2019 17:54:15 +0000 (17:54 +0000)
committerAndrew Kaylor <andrew.kaylor@intel.com>
Mon, 3 Jun 2019 17:54:15 +0000 (17:54 +0000)
This patch fixes a problem that occurs in LowerSwitch when a switch statement has a PHI node as its condition, and the PHI node only has two incoming blocks, and one of those incoming blocks is through an unreachable default in the switch statement. When this condition occurs, LowerSwitch holds a pointer to the condition value, but removes the switch block as a predecessor of the PHI block, causing the PHI node to be replaced. LowerSwitch then tries to use its stale pointer to the original condition value, causing a crash.

Differential Revision: https://reviews.llvm.org/D62560

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@362427 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Transforms/Utils/LowerSwitch.cpp
test/Transforms/LowerSwitch/condition-phi-unreachable-default.ll [new file with mode: 0644]

index 680b5d31a42c945cc8c9963b2d5f1c97f4d2eab3..8062fe499083296c6f0b045783519def79cee66c 100644 (file)
@@ -584,6 +584,11 @@ void LowerSwitch::processSwitchInst(SwitchInst *SI,
         PopSucc->removePredecessor(OrigBlock);
       return;
     }
+
+    // If the condition was a PHI node with the switch block as a predecessor
+    // removing predecessors may have caused the condition to be erased.
+    // Getting the condition value again here protects against that.
+    Val = SI->getCondition();
   }
 
   // Create a new, empty default block so that the new hierarchy of
diff --git a/test/Transforms/LowerSwitch/condition-phi-unreachable-default.ll b/test/Transforms/LowerSwitch/condition-phi-unreachable-default.ll
new file mode 100644 (file)
index 0000000..c53602b
--- /dev/null
@@ -0,0 +1,36 @@
+; RUN: opt < %s -lowerswitch -S | FileCheck %s
+
+; This test verifies -lowerswitch does not crash when an removing an
+; unreachable default branch causes a PHI node used as the switch
+; condition to be erased.
+
+define void @f() local_unnamed_addr {
+entry:
+  br label %sw.epilog
+
+sw.epilog:                                        ; preds = %sw.epilog.outer, %for.body
+  %i = phi i32 [ undef, %for.body ], [ 0, %entry ]
+  br i1 undef, label %for.body, label %for.end
+
+for.body:                                         ; preds = %sw.epilog
+  switch i32 %i, label %sw.epilog [
+    i32 0, label %sw.epilog.outer.backedge.loopexit
+    i32 1, label %sw.epilog.outer.backedge
+  ]
+
+sw.epilog.outer.backedge.loopexit:                ; preds = %for.body
+  br label %for.end
+
+sw.epilog.outer.backedge:                         ; preds = %for.body
+  unreachable
+
+for.end:                                          ; preds = %sw.epilog
+  ret void
+}
+
+; The phi and the switch should both be eliminated.
+; CHECK: @f()
+; CHECK: sw.epilog:
+; CHECK-NOT: phi
+; CHECK: for.body:
+; CHECK-NOT: switch