Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions clang/lib/CodeGen/CGExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -705,19 +705,19 @@ class ScalarExprEmitter

// Unary Operators.
Value *VisitUnaryPostDec(const UnaryOperator *E) {
LValue LV = EmitLValue(E->getSubExpr());
LValue LV = EmitCheckedLValue(E->getSubExpr(), CodeGenFunction::TCK_Store);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussion is needed on how risky it is and whether it needs an extra qualification or gating (like what we did to roll out new bounds checks for -fbounds-safety). My sense is that if we land this early in the milestone, we can piggy back on the regular compiler qualification.

return EmitScalarPrePostIncDec(E, LV, false, false);
}
Value *VisitUnaryPostInc(const UnaryOperator *E) {
LValue LV = EmitLValue(E->getSubExpr());
LValue LV = EmitCheckedLValue(E->getSubExpr(), CodeGenFunction::TCK_Store);
return EmitScalarPrePostIncDec(E, LV, true, false);
}
Value *VisitUnaryPreDec(const UnaryOperator *E) {
LValue LV = EmitLValue(E->getSubExpr());
LValue LV = EmitCheckedLValue(E->getSubExpr(), CodeGenFunction::TCK_Store);
return EmitScalarPrePostIncDec(E, LV, false, true);
}
Value *VisitUnaryPreInc(const UnaryOperator *E) {
LValue LV = EmitLValue(E->getSubExpr());
LValue LV = EmitCheckedLValue(E->getSubExpr(), CodeGenFunction::TCK_Store);
return EmitScalarPrePostIncDec(E, LV, true, true);
}

Expand Down
282 changes: 282 additions & 0 deletions clang/test/BoundsSafety/CodeGen/array-subscript-incdec-bounds-check.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" "!prof ![0-9]+" --version 6

// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s
// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s

// Pre/post inc/dec on an array element must go through the checked-lvalue
// path so that -fbounds-safety inserts a bounds check on the write. A prior
// bug left inc/dec unchecked, so a hardening feature silently skipped a
// common write form.

int a[5];

// CHECK-O0-LABEL: define dso_local void @store(
// CHECK-O0-SAME: i32 noundef [[I:%.*]]) #[[ATTR0:[0-9]+]] {
// CHECK-O0-NEXT: [[ENTRY:.*:]]
// CHECK-O0-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4
// CHECK-O0-NEXT: store i32 [[I]], ptr [[I_ADDR]], align 4
// CHECK-O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[I_ADDR]], align 4
// CHECK-O0-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64
// CHECK-O0-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr @a, i64 [[IDXPROM]]
// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[ARRAYIDX]], i64 1, {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], getelementptr inbounds nuw (i8, ptr @a, i64 20), {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}}
// CHECK-O0: [[TRAP]]:
// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}}
// CHECK-O0: [[CONT]]:
// CHECK-O0-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: br i1 [[TMP3]], label %[[CONT2:.*]], label %[[TRAP1:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}}
// CHECK-O0: [[TRAP1]]:
// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}}
// CHECK-O0: [[CONT2]]:
// CHECK-O0-NEXT: [[TMP4:%.*]] = icmp uge ptr [[ARRAYIDX]], @a, {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: br i1 [[TMP4]], label %[[CONT4:.*]], label %[[TRAP3:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}}
// CHECK-O0: [[TRAP3]]:
// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}}
// CHECK-O0: [[CONT4]]:
// CHECK-O0-NEXT: store i32 1, ptr [[ARRAYIDX]], align 4
// CHECK-O0-NEXT: ret void
//
// CHECK-O2-LABEL: define dso_local void @store(
// CHECK-O2-SAME: i32 noundef [[I:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
// CHECK-O2-NEXT: [[ENTRY:.*:]]
// CHECK-O2-NEXT: [[IDXPROM:%.*]] = sext i32 [[I]] to i64
// CHECK-O2-NEXT: [[ARRAYIDX:%.*]] = getelementptr [4 x i8], ptr @a, i64 [[IDXPROM]]
// CHECK-O2-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARRAYIDX]], i64 4, {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], getelementptr inbounds nuw (i8, ptr @a, i64 20), {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: [[TMP2:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP0]], {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: [[TMP3:%.*]] = icmp uge ptr [[ARRAYIDX]], @a, {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: [[OR_COND3:%.*]] = and i1 [[TMP3]], [[OR_COND]], {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: br i1 [[OR_COND3]], label %[[CONT2:.*]], label %[[TRAP:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}}
// CHECK-O2: [[TRAP]]:
// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: unreachable, {{!annotation ![0-9]+}}
// CHECK-O2: [[CONT2]]:
// CHECK-O2-NEXT: store i32 1, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}}
// CHECK-O2-NEXT: ret void
//
void store(int i) { a[i] = 1; }

// CHECK-O0-LABEL: define dso_local void @postinc(
// CHECK-O0-SAME: i32 noundef [[I:%.*]]) #[[ATTR0]] {
// CHECK-O0-NEXT: [[ENTRY:.*:]]
// CHECK-O0-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4
// CHECK-O0-NEXT: store i32 [[I]], ptr [[I_ADDR]], align 4
// CHECK-O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[I_ADDR]], align 4
// CHECK-O0-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64
// CHECK-O0-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr @a, i64 [[IDXPROM]]
// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[ARRAYIDX]], i64 1, {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], getelementptr inbounds nuw (i8, ptr @a, i64 20), {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}}
// CHECK-O0: [[TRAP]]:
// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}}
// CHECK-O0: [[CONT]]:
// CHECK-O0-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: br i1 [[TMP3]], label %[[CONT2:.*]], label %[[TRAP1:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}}
// CHECK-O0: [[TRAP1]]:
// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}}
// CHECK-O0: [[CONT2]]:
// CHECK-O0-NEXT: [[TMP4:%.*]] = icmp uge ptr [[ARRAYIDX]], @a, {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: br i1 [[TMP4]], label %[[CONT4:.*]], label %[[TRAP3:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}}
// CHECK-O0: [[TRAP3]]:
// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}}
// CHECK-O0: [[CONT4]]:
// CHECK-O0-NEXT: [[TMP5:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
// CHECK-O0-NEXT: [[INC:%.*]] = add nsw i32 [[TMP5]], 1
// CHECK-O0-NEXT: store i32 [[INC]], ptr [[ARRAYIDX]], align 4
// CHECK-O0-NEXT: ret void
//
// CHECK-O2-LABEL: define dso_local void @postinc(
// CHECK-O2-SAME: i32 noundef [[I:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] {
// CHECK-O2-NEXT: [[ENTRY:.*:]]
// CHECK-O2-NEXT: [[IDXPROM:%.*]] = sext i32 [[I]] to i64
// CHECK-O2-NEXT: [[ARRAYIDX:%.*]] = getelementptr [4 x i8], ptr @a, i64 [[IDXPROM]]
// CHECK-O2-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARRAYIDX]], i64 4, {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], getelementptr inbounds nuw (i8, ptr @a, i64 20), {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: [[TMP2:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP0]], {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: [[TMP3:%.*]] = icmp uge ptr [[ARRAYIDX]], @a, {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: [[OR_COND3:%.*]] = and i1 [[TMP3]], [[OR_COND]], {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: br i1 [[OR_COND3]], label %[[CONT2:.*]], label %[[TRAP:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}}
// CHECK-O2: [[TRAP]]:
// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: unreachable, {{!annotation ![0-9]+}}
// CHECK-O2: [[CONT2]]:
// CHECK-O2-NEXT: [[TMP4:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}}
// CHECK-O2-NEXT: [[INC:%.*]] = add nsw i32 [[TMP4]], 1
// CHECK-O2-NEXT: store i32 [[INC]], ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}}
// CHECK-O2-NEXT: ret void
//
void postinc(int i) { a[i]++; }

// CHECK-O0-LABEL: define dso_local void @preinc(
// CHECK-O0-SAME: i32 noundef [[I:%.*]]) #[[ATTR0]] {
// CHECK-O0-NEXT: [[ENTRY:.*:]]
// CHECK-O0-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4
// CHECK-O0-NEXT: store i32 [[I]], ptr [[I_ADDR]], align 4
// CHECK-O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[I_ADDR]], align 4
// CHECK-O0-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64
// CHECK-O0-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr @a, i64 [[IDXPROM]]
// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[ARRAYIDX]], i64 1, {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], getelementptr inbounds nuw (i8, ptr @a, i64 20), {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}}
// CHECK-O0: [[TRAP]]:
// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}}
// CHECK-O0: [[CONT]]:
// CHECK-O0-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: br i1 [[TMP3]], label %[[CONT2:.*]], label %[[TRAP1:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}}
// CHECK-O0: [[TRAP1]]:
// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}}
// CHECK-O0: [[CONT2]]:
// CHECK-O0-NEXT: [[TMP4:%.*]] = icmp uge ptr [[ARRAYIDX]], @a, {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: br i1 [[TMP4]], label %[[CONT4:.*]], label %[[TRAP3:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}}
// CHECK-O0: [[TRAP3]]:
// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}}
// CHECK-O0: [[CONT4]]:
// CHECK-O0-NEXT: [[TMP5:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
// CHECK-O0-NEXT: [[INC:%.*]] = add nsw i32 [[TMP5]], 1
// CHECK-O0-NEXT: store i32 [[INC]], ptr [[ARRAYIDX]], align 4
// CHECK-O0-NEXT: ret void
//
// CHECK-O2-LABEL: define dso_local void @preinc(
// CHECK-O2-SAME: i32 noundef [[I:%.*]]) local_unnamed_addr #[[ATTR2]] {
// CHECK-O2-NEXT: [[ENTRY:.*:]]
// CHECK-O2-NEXT: [[IDXPROM:%.*]] = sext i32 [[I]] to i64
// CHECK-O2-NEXT: [[ARRAYIDX:%.*]] = getelementptr [4 x i8], ptr @a, i64 [[IDXPROM]]
// CHECK-O2-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARRAYIDX]], i64 4, {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], getelementptr inbounds nuw (i8, ptr @a, i64 20), {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: [[TMP2:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP0]], {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: [[TMP3:%.*]] = icmp uge ptr [[ARRAYIDX]], @a, {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: [[OR_COND3:%.*]] = and i1 [[TMP3]], [[OR_COND]], {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: br i1 [[OR_COND3]], label %[[CONT2:.*]], label %[[TRAP:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}}
// CHECK-O2: [[TRAP]]:
// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: unreachable, {{!annotation ![0-9]+}}
// CHECK-O2: [[CONT2]]:
// CHECK-O2-NEXT: [[TMP4:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}}
// CHECK-O2-NEXT: [[INC:%.*]] = add nsw i32 [[TMP4]], 1
// CHECK-O2-NEXT: store i32 [[INC]], ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}}
// CHECK-O2-NEXT: ret void
//
void preinc(int i) { ++a[i]; }

// CHECK-O0-LABEL: define dso_local void @postdec(
// CHECK-O0-SAME: i32 noundef [[I:%.*]]) #[[ATTR0]] {
// CHECK-O0-NEXT: [[ENTRY:.*:]]
// CHECK-O0-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4
// CHECK-O0-NEXT: store i32 [[I]], ptr [[I_ADDR]], align 4
// CHECK-O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[I_ADDR]], align 4
// CHECK-O0-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64
// CHECK-O0-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr @a, i64 [[IDXPROM]]
// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[ARRAYIDX]], i64 1, {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], getelementptr inbounds nuw (i8, ptr @a, i64 20), {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}}
// CHECK-O0: [[TRAP]]:
// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}}
// CHECK-O0: [[CONT]]:
// CHECK-O0-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: br i1 [[TMP3]], label %[[CONT2:.*]], label %[[TRAP1:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}}
// CHECK-O0: [[TRAP1]]:
// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}}
// CHECK-O0: [[CONT2]]:
// CHECK-O0-NEXT: [[TMP4:%.*]] = icmp uge ptr [[ARRAYIDX]], @a, {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: br i1 [[TMP4]], label %[[CONT4:.*]], label %[[TRAP3:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}}
// CHECK-O0: [[TRAP3]]:
// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}}
// CHECK-O0: [[CONT4]]:
// CHECK-O0-NEXT: [[TMP5:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
// CHECK-O0-NEXT: [[DEC:%.*]] = add nsw i32 [[TMP5]], -1
// CHECK-O0-NEXT: store i32 [[DEC]], ptr [[ARRAYIDX]], align 4
// CHECK-O0-NEXT: ret void
//
// CHECK-O2-LABEL: define dso_local void @postdec(
// CHECK-O2-SAME: i32 noundef [[I:%.*]]) local_unnamed_addr #[[ATTR2]] {
// CHECK-O2-NEXT: [[ENTRY:.*:]]
// CHECK-O2-NEXT: [[IDXPROM:%.*]] = sext i32 [[I]] to i64
// CHECK-O2-NEXT: [[ARRAYIDX:%.*]] = getelementptr [4 x i8], ptr @a, i64 [[IDXPROM]]
// CHECK-O2-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARRAYIDX]], i64 4, {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], getelementptr inbounds nuw (i8, ptr @a, i64 20), {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: [[TMP2:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP0]], {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: [[TMP3:%.*]] = icmp uge ptr [[ARRAYIDX]], @a, {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: [[OR_COND3:%.*]] = and i1 [[TMP3]], [[OR_COND]], {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: br i1 [[OR_COND3]], label %[[CONT2:.*]], label %[[TRAP:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}}
// CHECK-O2: [[TRAP]]:
// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: unreachable, {{!annotation ![0-9]+}}
// CHECK-O2: [[CONT2]]:
// CHECK-O2-NEXT: [[TMP4:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}}
// CHECK-O2-NEXT: [[DEC:%.*]] = add nsw i32 [[TMP4]], -1
// CHECK-O2-NEXT: store i32 [[DEC]], ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}}
// CHECK-O2-NEXT: ret void
//
void postdec(int i) { a[i]--; }

// CHECK-O0-LABEL: define dso_local void @predec(
// CHECK-O0-SAME: i32 noundef [[I:%.*]]) #[[ATTR0]] {
// CHECK-O0-NEXT: [[ENTRY:.*:]]
// CHECK-O0-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4
// CHECK-O0-NEXT: store i32 [[I]], ptr [[I_ADDR]], align 4
// CHECK-O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[I_ADDR]], align 4
// CHECK-O0-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64
// CHECK-O0-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr @a, i64 [[IDXPROM]]
// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[ARRAYIDX]], i64 1, {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], getelementptr inbounds nuw (i8, ptr @a, i64 20), {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}}
// CHECK-O0: [[TRAP]]:
// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}}
// CHECK-O0: [[CONT]]:
// CHECK-O0-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: br i1 [[TMP3]], label %[[CONT2:.*]], label %[[TRAP1:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}}
// CHECK-O0: [[TRAP1]]:
// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}}
// CHECK-O0: [[CONT2]]:
// CHECK-O0-NEXT: [[TMP4:%.*]] = icmp uge ptr [[ARRAYIDX]], @a, {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: br i1 [[TMP4]], label %[[CONT4:.*]], label %[[TRAP3:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}}
// CHECK-O0: [[TRAP3]]:
// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}}
// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}}
// CHECK-O0: [[CONT4]]:
// CHECK-O0-NEXT: [[TMP5:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
// CHECK-O0-NEXT: [[DEC:%.*]] = add nsw i32 [[TMP5]], -1
// CHECK-O0-NEXT: store i32 [[DEC]], ptr [[ARRAYIDX]], align 4
// CHECK-O0-NEXT: ret void
//
// CHECK-O2-LABEL: define dso_local void @predec(
// CHECK-O2-SAME: i32 noundef [[I:%.*]]) local_unnamed_addr #[[ATTR2]] {
// CHECK-O2-NEXT: [[ENTRY:.*:]]
// CHECK-O2-NEXT: [[IDXPROM:%.*]] = sext i32 [[I]] to i64
// CHECK-O2-NEXT: [[ARRAYIDX:%.*]] = getelementptr [4 x i8], ptr @a, i64 [[IDXPROM]]
// CHECK-O2-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARRAYIDX]], i64 4, {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], getelementptr inbounds nuw (i8, ptr @a, i64 20), {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: [[TMP2:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP0]], {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: [[TMP3:%.*]] = icmp uge ptr [[ARRAYIDX]], @a, {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: [[OR_COND3:%.*]] = and i1 [[TMP3]], [[OR_COND]], {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: br i1 [[OR_COND3]], label %[[CONT2:.*]], label %[[TRAP:.*]], {{!prof ![0-9]+}}, {{!annotation ![0-9]+}}
// CHECK-O2: [[TRAP]]:
// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}}
// CHECK-O2-NEXT: unreachable, {{!annotation ![0-9]+}}
// CHECK-O2: [[CONT2]]:
// CHECK-O2-NEXT: [[TMP4:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}}
// CHECK-O2-NEXT: [[DEC:%.*]] = add nsw i32 [[TMP4]], -1
// CHECK-O2-NEXT: store i32 [[DEC]], ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}}
// CHECK-O2-NEXT: ret void
//
void predec(int i) { --a[i]; }
Loading