Created attachment 24682 [details] reduced testcase This program (wrong0.i): static int *a; int b, d; int *c = &b; int main() { int e[3] = {2, 1, -2139220656}; a = &e[0]; d = (e[2] < 0) || (e[2] > (7 >> e[2])); *c = d; printf("%d\n", b); } I beleive the right-shift with a known negative value is undefined, but since the first expression is always true, the program as a whole is still well-defined, or? Since e[2] is always less than 0, I would expect the second expression (after the '||') to never be evaluated. However, SimplifyCFGPass does not understand that the first expression is always true and so merges them so that they are always both executed. I wonder why this is allowed...? %1 = load i32, i32* %arrayidx1, align 4, !tbaa !6 %cmp = icmp slt i32 %1, 0 %shr = ashr i32 7, %1 %cmp4 = icmp sgt i32 %1, %shr %2 = select i1 %cmp, i1 true, i1 %cmp4 Then InstCombine decides to remove the first check against less than zero: %arrayidx1 = getelementptr inbounds [3 x i32], [3 x i32]* %e, i64 0, i64 2 %1 = load i32, i32* %arrayidx1, align 4, !tbaa !6 %shr = lshr i32 7, %1 %2 = icmp ugt i32 %1, %shr %lor.ext = zext i1 %2 to i32 store i32 %lor.ext, i32* @d, align 4, !tbaa !6 So it seems that the user has written a well-defined program, but the compiler has failed to realize this through the known constants. clang -march=arch13 -O1 wrong0.i -o a.out -w; ./a.out 1 clang -march=arch13 -O2 wrong0.i -o a.out -w; ./a.out 0 clang -march=arch13 -O2 wrong0.i -o a.out -mllvm -enable-gvn-memdep=false -w; ./a.out 1 clang -march=arch13 -O2 wrong0.i -o a.out -mllvm -memssa-check-limit=0 -w; ./a.out 0 The store of '0' to @d is then later produced by GVN: *** IR Dump After MergedLoadStoreMotionPass *** ; Function Attrs: nofree nounwind define dso_local signext i32 @main() local_unnamed_addr #0 { entry: %e = alloca [3 x i32], align 4 %0 = bitcast [3 x i32]* %e to i8* call void @llvm.lifetime.start.p0i8(i64 12, i8* nonnull %0) #2 call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 4 dereferenceable(12) %0, i8* noundef nonnull align 4 dereferenceable(12) bitcast ([3 x i32]* @__const.main.e to i8*), i64 12, i1 false) %arrayidx = getelementptr inbounds [3 x i32], [3 x i32]* %e, i64 0, i64 0 store i32* %arrayidx, i32** @a, align 8, !tbaa !2 %arrayidx1 = getelementptr inbounds [3 x i32], [3 x i32]* %e, i64 0, i64 2 %1 = load i32, i32* %arrayidx1, align 4, !tbaa !6 %shr = lshr i32 7, %1 %2 = icmp ugt i32 %1, %shr %lor.ext = zext i1 %2 to i32 store i32 %lor.ext, i32* @d, align 4, !tbaa !6 %3 = load i32*, i32** @c, align 8, !tbaa !2 store i32 %lor.ext, i32* %3, align 4, !tbaa !6 %4 = load i32, i32* @b, align 4, !tbaa !6 %call = call signext i32 (i8*, ...) @printf(i8* nonnull dereferenceable(1) getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32 signext %4) call void @llvm.lifetime.end.p0i8(i64 12, i8* nonnull %0) #2 ret i32 0 } *** IR Dump After GVN *** ; Function Attrs: nofree nounwind define dso_local signext i32 @main() local_unnamed_addr #0 { entry: %e = alloca [3 x i32], align 4 %0 = bitcast [3 x i32]* %e to i8* call void @llvm.lifetime.start.p0i8(i64 12, i8* nonnull %0) #2 call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 4 dereferenceable(12) %0, i8* noundef nonnull align 4 dereferenceable(12) bitcast ([3 x i32]* @__const.main.e to i8*), i64 12, i1 false) %arrayidx = getelementptr inbounds [3 x i32], [3 x i32]* %e, i64 0, i64 0 store i32* %arrayidx, i32** @a, align 8, !tbaa !2 %arrayidx1 = getelementptr inbounds [3 x i32], [3 x i32]* %e, i64 0, i64 2 store i32 0, i32* @d, align 4, !tbaa !6 %1 = load i32*, i32** @c, align 8, !tbaa !2 store i32 0, i32* %1, align 4, !tbaa !6 %2 = load i32, i32* @b, align 4, !tbaa !6 %call = call signext i32 (i8*, ...) @printf(i8* nonnull dereferenceable(1) getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32 signext %2) call void @llvm.lifetime.end.p0i8(i64 12, i8* nonnull %0) #2 ret i32 0 }
A reduced input: ``` define i1 @f(i32 %i1) { entry: %cmp = icmp slt i32 %i1, 0 %shr = ashr i32 7, %i1 %cmp4 = icmp sgt i32 %i1, %shr %i2 = select i1 %cmp, i1 true, i1 %cmp4 ret i1 %i2 } ``` It's again due to the old transformation in InstCombine 'select i1 %a, i1 true, i1 %b' => 'or i1 %a, %b'. The former expression effectively stops propagation of the result of invalid shift whereas the latter one allows its propagation, starting reasoning between two icmps and simplifying the result. The transformation should be disabled; I'm sending patches to minimize performance impact after disabling it (https://reviews.llvm.org/D93065).
I think I saw this again: define dso_local signext i32 @f(i32 signext %g, i32 zeroext %h) local_unnamed_addr #0 { entry: %cmp = icmp slt i32 %g, 0 %shr = ashr i32 7, %h %cmp1 = icmp sgt i32 %g, %shr %0 = select i1 %cmp, i1 true, i1 %cmp1 %lor.ext = zext i1 %0 to i32 ret i32 %lor.ext } => *** IR Dump After InstCombinePass *** define dso_local signext i32 @f(i32 signext %g, i32 zeroext %h) local_unnamed_addr #0 { entry: %shr = lshr i32 7, %h %0 = icmp ult i32 %shr, %g %lor.ext = zext i1 %0 to i32 ret i32 %lor.ext } This doesn't look right - the check if g < 0 is simply removed for some reason. opt -O2 fun.ll -march=z14 Input: define i32 @f(i32 signext %g, i32 zeroext %h) #0 { entry: %g.addr = alloca i32, align 4 %h.addr = alloca i32, align 4 store i32 %g, i32* %g.addr, align 4 store i32 %h, i32* %h.addr, align 4 %0 = load i32, i32* %g.addr, align 4 %cmp = icmp slt i32 %0, 0 br i1 %cmp, label %lor.end, label %lor.rhs lor.rhs: ; preds = %entry %1 = load i32, i32* %g.addr, align 4 %2 = load i32, i32* %h.addr, align 4 %shr = ashr i32 7, %2 %cmp1 = icmp sgt i32 %1, %shr br label %lor.end lor.end: ; preds = %lor.rhs, %entry %3 = phi i1 [ true, %entry ], [ %cmp1, %lor.rhs ] %lor.ext = zext i1 %3 to i32 ret i32 %lor.ext }
(In reply to Jonas Paulsson from comment #2) > I think I saw this again: > > define dso_local signext i32 @f(i32 signext %g, i32 zeroext %h) > local_unnamed_addr #0 { > entry: > %cmp = icmp slt i32 %g, 0 > %shr = ashr i32 7, %h > %cmp1 = icmp sgt i32 %g, %shr > %0 = select i1 %cmp, i1 true, i1 %cmp1 > %lor.ext = zext i1 %0 to i32 > ret i32 %lor.ext > } > > => > > *** IR Dump After InstCombinePass *** > define dso_local signext i32 @f(i32 signext %g, i32 zeroext %h) > local_unnamed_addr #0 { > entry: > %shr = lshr i32 7, %h > %0 = icmp ult i32 %shr, %g > %lor.ext = zext i1 %0 to i32 > ret i32 %lor.ext > } > > This doesn't look right - the check if g < 0 is simply removed for some > reason. > > opt -O2 fun.ll -march=z14 > > Input: > define i32 @f(i32 signext %g, i32 zeroext %h) #0 { > entry: > %g.addr = alloca i32, align 4 > %h.addr = alloca i32, align 4 > store i32 %g, i32* %g.addr, align 4 > store i32 %h, i32* %h.addr, align 4 > %0 = load i32, i32* %g.addr, align 4 > %cmp = icmp slt i32 %0, 0 > br i1 %cmp, label %lor.end, label %lor.rhs > > lor.rhs: ; preds = %entry > > %1 = load i32, i32* %g.addr, align 4 > %2 = load i32, i32* %h.addr, align 4 > %shr = ashr i32 7, %2 > %cmp1 = icmp sgt i32 %1, %shr > br label %lor.end > > lor.end: ; preds = %lor.rhs, %entry > > %3 = phi i1 [ true, %entry ], [ %cmp1, %lor.rhs ] > %lor.ext = zext i1 %3 to i32 > ret i32 %lor.ext > } Yep, https://alive2.llvm.org/ce/z/NGCDRB
I'll make a patch that conditionally disables select -> and/or transformation. I think now it's time to start ditching out the optimization.
https://reviews.llvm.org/D99674
Fixed via https://reviews.llvm.org/rG5207cde5cb4147155c469e1861427ea9d569bd5a