diff --git a/llvm/test/Transforms/GVN/memdep-unknown-deadblocks.ll b/llvm/test/Transforms/GVN/memdep-unknown-deadblocks.ll new file mode 100644 index 00000000000000..02e9baab712a54 --- /dev/null +++ b/llvm/test/Transforms/GVN/memdep-unknown-deadblocks.ll @@ -0,0 +1,129 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes='function(gvn)' -S | FileCheck -check-prefixes=CHECK-GVN %s + +; Also do some runs using an GVN+O1 pipeline, using O1 to clean up after GVN. +; This is to easier see the semantic of the resulting IR and that it hopefully +; is the same as if only running O1 directly. +; +; RUN: opt < %s -passes='function(gvn),default' -S | FileCheck -check-prefixes=CHECK-GVN-O1 %s +; RUN: opt < %s -passes='default' -S | FileCheck -check-prefixes=CHECK-O1 %s + +; This is a reproducer for the miscompile reported here: +; https://github.com/llvm/llvm-project/issues/57025 +; +; Explanation of the test case: +; - Outer loop will do three iterations i:=0, i:=1 and i:=2 +; - When is i==0 the value 7 is stored to arr[0] +; - When is i!=0 the value 42 is stored to arr[i] +; - There is a nestled while loop reduced down to something just setting j:=i +; and then exiting. +; - In while.end arr[j] (i.e. the just stored to arr[i]) is loaded, +; and the loaded value is passed to the verify function. +; - There is a dead load of the not yet initialized arr[j+1] in the epilog +; block. This might seem irrelevant, but is needed to reproduce the +; miscompile. +; +; Expected semantic of the function is that verify() will be called three +; times, with the values 7, 42 and 42. + +; FIXME: The value passed to the verify function is loaded by +; %value = load i16, ptr %arr.j, align 1 +; but currently GVN is replacing it with a faulty PHI in the +; store.done block. + +declare void @verify(i16) + +define void @test(i16 %g) { +; CHECK-GVN-LABEL: @test( +; CHECK-GVN-NEXT: entry: +; CHECK-GVN-NEXT: [[ARR:%.*]] = alloca [4 x i16], align 1 +; CHECK-GVN-NEXT: br label [[FOR_BODY:%.*]] +; CHECK-GVN: for.body: +; CHECK-GVN-NEXT: [[VALUE2:%.*]] = phi i16 [ undef, [[ENTRY:%.*]] ], [ [[DEAD:%.*]], [[WHILE_END:%.*]] ] +; CHECK-GVN-NEXT: [[I:%.*]] = phi i16 [ 0, [[ENTRY]] ], [ [[NEXT_I:%.*]], [[WHILE_END]] ] +; CHECK-GVN-NEXT: [[CMP0:%.*]] = icmp eq i16 [[I]], 0 +; CHECK-GVN-NEXT: br i1 [[CMP0]], label [[STORE_IDX_0:%.*]], label [[STORE_IDX_I:%.*]] +; CHECK-GVN: store.idx.0: +; CHECK-GVN-NEXT: store i16 7, ptr [[ARR]], align 1 +; CHECK-GVN-NEXT: br label [[STORE_DONE:%.*]] +; CHECK-GVN: store.idx.i: +; CHECK-GVN-NEXT: [[ARR_I:%.*]] = getelementptr [4 x i16], ptr [[ARR]], i16 0, i16 [[I]] +; CHECK-GVN-NEXT: store i16 42, ptr [[ARR_I]], align 1 +; CHECK-GVN-NEXT: br label [[STORE_DONE]] +; CHECK-GVN: store.done: +; CHECK-GVN-NEXT: [[VALUE:%.*]] = phi i16 [ 42, [[STORE_IDX_I]] ], [ [[VALUE2]], [[STORE_IDX_0]] ] +; CHECK-GVN-NEXT: br label [[WHILE_BODY:%.*]] +; CHECK-GVN: while.body: +; CHECK-GVN-NEXT: br i1 false, label [[WHILE_BODY_WHILE_BODY_CRIT_EDGE:%.*]], label [[WHILE_END]] +; CHECK-GVN: while.body.while.body_crit_edge: +; CHECK-GVN-NEXT: br label [[WHILE_BODY]] +; CHECK-GVN: while.end: +; CHECK-GVN-NEXT: [[ARR_J:%.*]] = getelementptr [4 x i16], ptr [[ARR]], i16 0, i16 [[I]] +; CHECK-GVN-NEXT: tail call void @verify(i16 [[VALUE]]) +; CHECK-GVN-NEXT: [[NEXT_I]] = add i16 [[I]], 1 +; CHECK-GVN-NEXT: [[ARR_NEXT_I:%.*]] = getelementptr [4 x i16], ptr [[ARR]], i16 0, i16 [[NEXT_I]] +; CHECK-GVN-NEXT: [[DEAD]] = load i16, ptr [[ARR_NEXT_I]], align 1 +; CHECK-GVN-NEXT: [[CMP4:%.*]] = icmp slt i16 [[NEXT_I]], 3 +; CHECK-GVN-NEXT: br i1 [[CMP4]], label [[FOR_BODY]], label [[FOR_END:%.*]] +; CHECK-GVN: for.end: +; CHECK-GVN-NEXT: ret void +; +; CHECK-GVN-O1-LABEL: @test( +; CHECK-GVN-O1-NEXT: entry: +; CHECK-GVN-O1-NEXT: tail call void @verify(i16 42) +; CHECK-GVN-O1-NEXT: tail call void @verify(i16 42) +; CHECK-GVN-O1-NEXT: tail call void @verify(i16 42) +; CHECK-GVN-O1-NEXT: ret void +; +; CHECK-O1-LABEL: @test( +; CHECK-O1-NEXT: entry: +; CHECK-O1-NEXT: tail call void @verify(i16 7) +; CHECK-O1-NEXT: tail call void @verify(i16 42) +; CHECK-O1-NEXT: tail call void @verify(i16 42) +; CHECK-O1-NEXT: ret void +; +entry: + %arr = alloca [4 x i16], align 1 + br label %for.body + +for.body: ; preds = %epilog, %entry + %i = phi i16 [ 0, %entry ], [ %next.i, %epilog ] + %cmp0 = icmp eq i16 %i, 0 + br i1 %cmp0, label %store.idx.0, label %store.idx.i + +store.idx.0: ; preds = %for.body + store i16 7, ptr %arr, align 1 + br label %store.done + +store.idx.i: ; preds = %for.body + %arr.i = getelementptr [4 x i16], ptr %arr, i16 0, i16 %i + store i16 42, ptr %arr.i, align 1 + br label %store.done + +store.done: ; preds = %store.idx.i, %store.idx.0 + br label %while.body + +while.body: ; preds = %while.body, %store.done + %j = phi i16 [ %i, %store.done ], [ 0, %while.body ] + ; Constant foldable conditional branch. + ; Needed to reproduce the fault! + br i1 false, label %while.body, label %while.end + +while.end: ; preds = %while.body + %arr.j = getelementptr [4 x i16], ptr %arr, i16 0, i16 %j + %value = load i16, ptr %arr.j, align 1 + tail call void @verify(i16 %value) + br label %epilog + +epilog: ; preds = %while.end + %next.i = add i16 %j, 1 + %arr.next.i = getelementptr [4 x i16], ptr %arr, i16 0, i16 %next.i + ; A dead load. + ; Needed to reproduce the fault! + %dead = load i16, ptr %arr.next.i, align 1 + %cmp4 = icmp slt i16 %next.i, 3 + br i1 %cmp4, label %for.body, label %for.end + +for.end: ; preds = %epilog + ret void +}