Index: test/Transforms/NaCl/pnacl-sjlj-eh-bug.ll |
diff --git a/test/Transforms/NaCl/pnacl-sjlj-eh-bug.ll b/test/Transforms/NaCl/pnacl-sjlj-eh-bug.ll |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e0796881285cc717c710b3ff4ed7024e831c3b43 |
--- /dev/null |
+++ b/test/Transforms/NaCl/pnacl-sjlj-eh-bug.ll |
@@ -0,0 +1,81 @@ |
+; RUN: opt %s -pnacl-sjlj-eh -O2 -S | FileCheck %s |
+ |
+; datalayout must be specified for GVN to work. |
+target datalayout = "p:32:32:32" |
+ |
+; This must be declared for expanding "invoke" and "landingpad" instructions. |
+@__pnacl_eh_stack = external thread_local global i8* |
+ |
+declare i1 @might_be_setjmp() |
+declare void @external_func(i32* %ptr) |
+declare void @var_is_nonzero() |
+ |
+ |
+; Test for a bug in which PNaClSjLjEH would transform |
+; @invoke_optimize_test() such that the call to @var_is_nonzero() |
+; could get optimized away by a later optimization pass. This |
+; happened because PNaClSjLjEH generated code similar to |
+; @branch_optimize_test() below. |
+ |
+define void @invoke_optimize_test() { |
+ %var = alloca i32 |
+ store i32 0, i32* %var |
+ |
+ invoke void @external_func(i32* %var) |
+ to label %exit unwind label %lpad |
+ |
+lpad: |
+ landingpad i32 personality i8* null |
+ catch i8* null |
+ %value = load i32* %var |
+ %is_zero = icmp eq i32 %value, 0 |
+ br i1 %is_zero, label %exit, label %do_call |
+ |
+do_call: |
+ call void @var_is_nonzero() |
+ ret void |
+ |
+exit: |
+ ret void |
+} |
+; CHECK: define void @invoke_optimize_test() |
+; CHECK: @var_is_nonzero() |
+ |
+ |
+; In @branch_optimize_test(), the optimizer can optimize away the call |
+; to @var_is_nonzero(), because it can assume that %var always |
+; contains 0 on the "iffalse" branch. |
+; |
+; The passes "-gvn -instcombine" are enough to do this. |
+; |
+; The optimizer can do this regardless of whether @might_be_setjmp() |
+; is setjmp() or a normal function. It doesn't need to know that |
+; @might_be_setjmp() might return twice, because storing to %var |
+; between setjmp() and longjmp() leaves %var pointing to an undefined |
+; value. |
+ |
+define void @branch_optimize_test() { |
+ %var = alloca i32 |
+ store i32 0, i32* %var |
+ |
+ %cond = call i1 @might_be_setjmp() returns_twice |
+ br i1 %cond, label %iftrue, label %iffalse |
+ |
+iftrue: |
+ call void @external_func(i32* %var) |
+ ret void |
+ |
+iffalse: |
+ %value = load i32* %var |
+ %is_zero = icmp eq i32 %value, 0 |
+ br i1 %is_zero, label %exit, label %do_call |
+ |
+do_call: |
+ call void @var_is_nonzero() |
+ ret void |
+ |
+exit: |
+ ret void |
+} |
+; CHECK: define void @branch_optimize_test() |
+; CHECK-NOT: @var_is_nonzero |