Skip to content

Commit d486bad

Browse files
committed
[CIR][CIRGen] Add support for operator new with null checks
1 parent 5acd73c commit d486bad

File tree

2 files changed

+107
-2
lines changed

2 files changed

+107
-2
lines changed

clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -891,11 +891,30 @@ mlir::Value CIRGenFunction::buildCXXNewExpr(const CXXNewExpr *E) {
891891
// The null-check means that the initializer is conditionally
892892
// evaluated.
893893
ConditionalEvaluation conditional(*this);
894+
mlir::OpBuilder::InsertPoint ifBody, postIfBody;
895+
mlir::Location loc = getLoc(E->getSourceRange());
894896

895897
if (nullCheck) {
896-
llvm_unreachable("NYI");
898+
conditional.begin(*this);
899+
mlir::Value nullPtr =
900+
builder.getNullPtr(allocation.getPointer().getType(), loc);
901+
mlir::Value nullCmpResult = builder.createCompare(
902+
loc, mlir::cir::CmpOpKind::ne, allocation.getPointer(), nullPtr);
903+
904+
// mlir::Value Failed = CGF.getBuilder().createNot(Success);
905+
builder.create<mlir::cir::IfOp>(loc, nullCmpResult,
906+
/*withElseRegion=*/false,
907+
[&](mlir::OpBuilder &, mlir::Location) {
908+
ifBody = builder.saveInsertionPoint();
909+
});
910+
postIfBody = builder.saveInsertionPoint();
897911
}
898912

913+
// All the actual work to be done should be placed inside the IfOp above,
914+
// so change the insertion point over there.
915+
if (ifBody.isSet())
916+
builder.restoreInsertionPoint(ifBody);
917+
899918
// If there's an operator delete, enter a cleanup to call it if an
900919
// exception is thrown.
901920
EHScopeStack::stable_iterator operatorDeleteCleanup;
@@ -957,7 +976,14 @@ mlir::Value CIRGenFunction::buildCXXNewExpr(const CXXNewExpr *E) {
957976
}
958977

959978
if (nullCheck) {
960-
llvm_unreachable("NYI");
979+
conditional.end(*this);
980+
// resultPtr is already updated in the first null check phase.
981+
982+
// Reset insertion point to resume back to post ifOp.
983+
if (postIfBody.isSet()) {
984+
builder.create<mlir::cir::YieldOp>(loc);
985+
builder.restoreInsertionPoint(postIfBody);
986+
}
961987
}
962988

963989
return resultPtr;

clang/test/CIR/CodeGen/new-null.cpp

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// RUN: %clang_cc1 -std=c++14 -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++14 -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+
// TODO: This file is inspired by clang/test/CodeGenCXX/new.cpp, add all tests from it.
7+
8+
typedef __typeof__(sizeof(0)) size_t;
9+
10+
// Declare an 'operator new' template to tickle a bug in __builtin_operator_new.
11+
template<typename T> void *operator new(size_t, int (*)(T));
12+
13+
// Ensure that this declaration doesn't cause operator new to lose its
14+
// 'noalias' attribute.
15+
void *operator new[](size_t);
16+
17+
namespace std {
18+
struct nothrow_t {};
19+
}
20+
std::nothrow_t nothrow;
21+
22+
// Declare the reserved placement operators.
23+
void *operator new(size_t, void*) throw();
24+
void operator delete(void*, void*) throw();
25+
void *operator new[](size_t, void*) throw();
26+
void operator delete[](void*, void*) throw();
27+
28+
// Declare the replaceable global allocation operators.
29+
void *operator new(size_t, const std::nothrow_t &) throw();
30+
void *operator new[](size_t, const std::nothrow_t &) throw();
31+
void operator delete(void *, const std::nothrow_t &) throw();
32+
void operator delete[](void *, const std::nothrow_t &) throw();
33+
34+
// Declare some other placemenet operators.
35+
void *operator new(size_t, void*, bool) throw();
36+
void *operator new[](size_t, void*, bool) throw();
37+
38+
namespace test15 {
39+
struct A { A(); ~A(); };
40+
// CIR-DAG: ![[TEST15A:.*]] = !cir.struct<struct "test15::A" {!cir.int<u, 8>}
41+
42+
void test0a(void *p) {
43+
new (p) A();
44+
}
45+
46+
// CIR-LABEL: cir.func @_ZN6test156test0bEPv(
47+
// CIR-SAME: %[[VAL_0:.*]]: !cir.ptr<!void>
48+
// CIR: %[[VAL_1:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["p", init] {alignment = 8 : i64}
49+
// CIR: cir.store %[[VAL_0]], %[[VAL_1]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
50+
// CIR: %[[VAL_2:.*]] = cir.const #cir.int<1> : !u64i
51+
// CIR: %[[VAL_3:.*]] = cir.load %[[VAL_1]] : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
52+
// CIR: %[[VAL_4:.*]] = cir.const #true
53+
// CIR: %[[VAL_5:.*]] = cir.call @_ZnwmPvb(%[[VAL_2]], %[[VAL_3]], %[[VAL_4]])
54+
// CIR: %[[VAL_6:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void>
55+
// CIR: %[[VAL_7:.*]] = cir.cmp(ne, %[[VAL_5]], %[[VAL_6]]) : !cir.ptr<!void>, !cir.bool
56+
// CIR: cir.if %[[VAL_7]] {
57+
// CIR: %[[VAL_8:.*]] = cir.cast(bitcast, %[[VAL_5]] : !cir.ptr<!void>), !cir.ptr<![[TEST15A]]>
58+
// CIR: cir.call @_ZN6test151AC1Ev(%[[VAL_8]]) : (!cir.ptr<![[TEST15A]]>) -> ()
59+
// CIR: }
60+
// CIR: cir.return
61+
// CIR: }
62+
63+
// LLVM-LABEL: _ZN6test156test0bEPv
64+
// LLVM: %[[VAL_0:.*]] = alloca ptr, i64 1, align 8
65+
// LLVM: store ptr %[[VAL_1:.*]], ptr %[[VAL_0]], align 8
66+
// LLVM: %[[VAL_2:.*]] = load ptr, ptr %[[VAL_0]], align 8
67+
// LLVM: %[[VAL_3:.*]] = call ptr @_ZnwmPvb(i64 1, ptr %[[VAL_2]], i8 1)
68+
// LLVM: %[[VAL_4:.*]] = icmp ne ptr %[[VAL_3]], null
69+
// LLVM: br i1 %[[VAL_4]], label %[[VAL_5:.*]], label %[[VAL_6:.*]],
70+
// LLVM: [[VAL_5]]: ; preds = %[[VAL_7:.*]]
71+
// LLVM: call void @_ZN6test151AC1Ev(ptr %[[VAL_3]])
72+
// LLVM: br label %[[VAL_6]],
73+
// LLVM: [[VAL_6]]: ; preds = %[[VAL_5]], %[[VAL_7]]
74+
// LLVM: ret void
75+
76+
void test0b(void *p) {
77+
new (p, true) A();
78+
}
79+
}

0 commit comments

Comments
 (0)