Skip to content

Commit b8299a5

Browse files
andykaylorlanza
authored andcommitted
[CIR] Fix for missing side effects during null pointer initialization (#1547)
When a pointer variable is initialized with a null pointer, the AST contains a NullToPointer cast. If we just emit a null pointer of the correct type, we will miss any side effects that occur in the initializer. This change adds code to emit the initializer expression if it is not a simple constant. This results in an extra null pointer constant being emitted when the expression has side effects, but the constant emitted while visiting the expression does not have the correct type, so the alternative would be to capture the emitted constant and bitcast it to the correct type. An extra constant seems less intrusive than an unnecessary bitcast.
1 parent a8c9ca9 commit b8299a5

File tree

2 files changed

+83
-3
lines changed

2 files changed

+83
-3
lines changed

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,9 +1170,11 @@ mlir::Value CIRGenFunction::emitPromotedScalarExpr(const Expr *E,
11701170
}
11711171

11721172
[[maybe_unused]] static bool MustVisitNullValue(const Expr *E) {
1173-
// If a null pointer expression's type is the C++0x nullptr_t, then
1174-
// it's not necessarily a simple constant and it must be evaluated
1173+
// If a null pointer expression's type is the C++0x nullptr_t and
1174+
// the expression is not a simple literal, it must be evaluated
11751175
// for its potential side effects.
1176+
if (isa<IntegerLiteral>(E) || isa<CXXNullPtrLiteralExpr>(E))
1177+
return false;
11761178
return E->getType()->isNullPtrType();
11771179
}
11781180

@@ -1713,7 +1715,9 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
17131715
return emitLValue(E).getPointer();
17141716

17151717
case CK_NullToPointer: {
1716-
// FIXME: use MustVisitNullValue(E) and evaluate expr.
1718+
if (MustVisitNullValue(E))
1719+
CGF.emitIgnoredExpr(E);
1720+
17171721
// Note that DestTy is used as the MLIR type instead of a custom
17181722
// nullptr type.
17191723
mlir::Type Ty = CGF.convertType(DestTy);
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu %s -fclangir -emit-cir -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir -check-prefix=CIR %s
3+
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu %s -fclangir -emit-llvm -o %t.ll
4+
// RUN: FileCheck --input-file=%t.ll -check-prefix=LLVM %s
5+
6+
void t1() {
7+
int *p1 = nullptr;
8+
int *p2 = 0;
9+
int *p3 = (int*)0;
10+
}
11+
12+
// CIR: cir.func @_Z2t1v()
13+
// CIR-NEXT: %[[P1:.*]] = cir.alloca !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>, ["p1", init] {alignment = 8 : i64}
14+
// CIR-NEXT: %[[P2:.*]] = cir.alloca !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>, ["p2", init] {alignment = 8 : i64}
15+
// CIR-NEXT: %[[P3:.*]] = cir.alloca !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>, ["p3", init] {alignment = 8 : i64}
16+
// CIR-NEXT: %[[NULLPTR1:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i>
17+
// CIR-NEXT: cir.store %[[NULLPTR1]], %[[P1]] : !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>
18+
// CIR-NEXT: %[[NULLPTR2:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i>
19+
// CIR-NEXT: cir.store %[[NULLPTR2]], %[[P2]] : !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>
20+
// CIR-NEXT: %[[NULLPTR3:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i>
21+
// CIR-NEXT: cir.store %[[NULLPTR3]], %[[P3]] : !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>
22+
// CIR-NEXT: cir.return
23+
// CIR-NEXT: }
24+
25+
// LLVM: define{{.*}} @_Z2t1v()
26+
// LLVM-NEXT: %[[P1:.*]] = alloca ptr, i64 1, align 8
27+
// LLVM-NEXT: %[[P2:.*]] = alloca ptr, i64 1, align 8
28+
// LLVM-NEXT: %[[P3:.*]] = alloca ptr, i64 1, align 8
29+
// LLVM-NEXT: store ptr null, ptr %[[P1]], align 8
30+
// LLVM-NEXT: store ptr null, ptr %[[P2]], align 8
31+
// LLVM-NEXT: store ptr null, ptr %[[P3]], align 8
32+
// LLVM-NEXT: ret void
33+
// LLVM-NEXT: }
34+
35+
// Verify that we're capturing side effects during null pointer initialization.
36+
int t2() {
37+
int x = 0;
38+
int *p = (x = 1, nullptr);
39+
return x;
40+
}
41+
42+
// Note: An extra null pointer constant gets emitted as a result of visiting the
43+
// compound initialization expression. We could avoid this by capturing
44+
// the result of the compound initialization expression and explicitly
45+
// casting it to the required type, but a redundant constant seems less
46+
// intrusive than a redundant bitcast.
47+
48+
// CIR: cir.func @_Z2t2v()
49+
// CIR-NEXT: %[[RETVAL_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"] {alignment = 4 : i64}
50+
// CIR-NEXT: %[[X:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init] {alignment = 4 : i64}
51+
// CIR-NEXT: %[[P:.*]] = cir.alloca !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>, ["p", init] {alignment = 8 : i64}
52+
// CIR-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
53+
// CIR-NEXT: cir.store %[[ZERO]], %[[X]] : !s32i, !cir.ptr<!s32i>
54+
// CIR-NEXT: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i
55+
// CIR-NEXT: cir.store %[[ONE]], %[[X]] : !s32i, !cir.ptr<!s32i>
56+
// CIR-NEXT: %[[NULLPTR_EXTRA:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void>
57+
// CIR-NEXT: %[[NULLPTR:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i>
58+
// CIR-NEXT: cir.store %[[NULLPTR]], %[[P]] : !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>
59+
// CIR-NEXT: %[[X_VAL:.*]] = cir.load %[[X]] : !cir.ptr<!s32i>, !s32i
60+
// CIR-NEXT: cir.store %[[X_VAL]], %[[RETVAL_ADDR]] : !s32i, !cir.ptr<!s32i>
61+
// CIR-NEXT: %[[RETVAL:.*]] = cir.load %[[RETVAL_ADDR]] : !cir.ptr<!s32i>, !s32i
62+
// CIR-NEXT: cir.return %[[RETVAL]] : !s32i
63+
// CIR-NEXT: }
64+
65+
// LLVM: define{{.*}} @_Z2t2v()
66+
// LLVM-NEXT: %[[RETVAL_ADDR:.*]] = alloca i32, i64 1, align 4
67+
// LLVM-NEXT: %[[X:.*]] = alloca i32, i64 1, align 4
68+
// LLVM-NEXT: %[[P:.*]] = alloca ptr, i64 1, align 8
69+
// LLVM-NEXT: store i32 0, ptr %[[X]], align 4
70+
// LLVM-NEXT: store i32 1, ptr %[[X]], align 4
71+
// LLVM-NEXT: store ptr null, ptr %[[P]], align 8
72+
// LLVM-NEXT: %[[X_VAL:.*]] = load i32, ptr %[[X]], align 4
73+
// LLVM-NEXT: store i32 %[[X_VAL]], ptr %[[RETVAL_ADDR]], align 4
74+
// LLVM-NEXT: %[[RETVAL:.*]] = load i32, ptr %[[RETVAL_ADDR]], align 4
75+
// LLVM-NEXT: ret i32 %[[RETVAL]]
76+
// LLVM-NEXT: }

0 commit comments

Comments
 (0)