Skip to content

Commit

Permalink
[CIR][CIRGen] Add support for operator new with null checks
Browse files Browse the repository at this point in the history
  • Loading branch information
bcardosolopes committed Jul 4, 2024
1 parent 5acd73c commit d486bad
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 2 deletions.
30 changes: 28 additions & 2 deletions clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -891,11 +891,30 @@ mlir::Value CIRGenFunction::buildCXXNewExpr(const CXXNewExpr *E) {
// The null-check means that the initializer is conditionally
// evaluated.
ConditionalEvaluation conditional(*this);
mlir::OpBuilder::InsertPoint ifBody, postIfBody;
mlir::Location loc = getLoc(E->getSourceRange());

if (nullCheck) {
llvm_unreachable("NYI");
conditional.begin(*this);
mlir::Value nullPtr =
builder.getNullPtr(allocation.getPointer().getType(), loc);
mlir::Value nullCmpResult = builder.createCompare(
loc, mlir::cir::CmpOpKind::ne, allocation.getPointer(), nullPtr);

// mlir::Value Failed = CGF.getBuilder().createNot(Success);
builder.create<mlir::cir::IfOp>(loc, nullCmpResult,
/*withElseRegion=*/false,
[&](mlir::OpBuilder &, mlir::Location) {
ifBody = builder.saveInsertionPoint();
});
postIfBody = builder.saveInsertionPoint();
}

// All the actual work to be done should be placed inside the IfOp above,
// so change the insertion point over there.
if (ifBody.isSet())
builder.restoreInsertionPoint(ifBody);

// If there's an operator delete, enter a cleanup to call it if an
// exception is thrown.
EHScopeStack::stable_iterator operatorDeleteCleanup;
Expand Down Expand Up @@ -957,7 +976,14 @@ mlir::Value CIRGenFunction::buildCXXNewExpr(const CXXNewExpr *E) {
}

if (nullCheck) {
llvm_unreachable("NYI");
conditional.end(*this);
// resultPtr is already updated in the first null check phase.

// Reset insertion point to resume back to post ifOp.
if (postIfBody.isSet()) {
builder.create<mlir::cir::YieldOp>(loc);
builder.restoreInsertionPoint(postIfBody);
}
}

return resultPtr;
Expand Down
79 changes: 79 additions & 0 deletions clang/test/CIR/CodeGen/new-null.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-linux-gnu %s -fclangir -emit-cir -o %t.cir
// RUN: FileCheck --input-file=%t.cir -check-prefix=CIR %s
// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-linux-gnu %s -fclangir -emit-llvm -o %t.ll
// RUN: FileCheck --input-file=%t.ll -check-prefix=LLVM %s

// TODO: This file is inspired by clang/test/CodeGenCXX/new.cpp, add all tests from it.

typedef __typeof__(sizeof(0)) size_t;

// Declare an 'operator new' template to tickle a bug in __builtin_operator_new.
template<typename T> void *operator new(size_t, int (*)(T));

// Ensure that this declaration doesn't cause operator new to lose its
// 'noalias' attribute.
void *operator new[](size_t);

namespace std {
struct nothrow_t {};
}
std::nothrow_t nothrow;

// Declare the reserved placement operators.
void *operator new(size_t, void*) throw();
void operator delete(void*, void*) throw();
void *operator new[](size_t, void*) throw();
void operator delete[](void*, void*) throw();

// Declare the replaceable global allocation operators.
void *operator new(size_t, const std::nothrow_t &) throw();
void *operator new[](size_t, const std::nothrow_t &) throw();
void operator delete(void *, const std::nothrow_t &) throw();
void operator delete[](void *, const std::nothrow_t &) throw();

// Declare some other placemenet operators.
void *operator new(size_t, void*, bool) throw();
void *operator new[](size_t, void*, bool) throw();

namespace test15 {
struct A { A(); ~A(); };
// CIR-DAG: ![[TEST15A:.*]] = !cir.struct<struct "test15::A" {!cir.int<u, 8>}

void test0a(void *p) {
new (p) A();
}

// CIR-LABEL: cir.func @_ZN6test156test0bEPv(
// CIR-SAME: %[[VAL_0:.*]]: !cir.ptr<!void>
// CIR: %[[VAL_1:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["p", init] {alignment = 8 : i64}
// CIR: cir.store %[[VAL_0]], %[[VAL_1]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
// CIR: %[[VAL_2:.*]] = cir.const #cir.int<1> : !u64i
// CIR: %[[VAL_3:.*]] = cir.load %[[VAL_1]] : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
// CIR: %[[VAL_4:.*]] = cir.const #true
// CIR: %[[VAL_5:.*]] = cir.call @_ZnwmPvb(%[[VAL_2]], %[[VAL_3]], %[[VAL_4]])
// CIR: %[[VAL_6:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void>
// CIR: %[[VAL_7:.*]] = cir.cmp(ne, %[[VAL_5]], %[[VAL_6]]) : !cir.ptr<!void>, !cir.bool
// CIR: cir.if %[[VAL_7]] {
// CIR: %[[VAL_8:.*]] = cir.cast(bitcast, %[[VAL_5]] : !cir.ptr<!void>), !cir.ptr<![[TEST15A]]>
// CIR: cir.call @_ZN6test151AC1Ev(%[[VAL_8]]) : (!cir.ptr<![[TEST15A]]>) -> ()
// CIR: }
// CIR: cir.return
// CIR: }

// LLVM-LABEL: _ZN6test156test0bEPv
// LLVM: %[[VAL_0:.*]] = alloca ptr, i64 1, align 8
// LLVM: store ptr %[[VAL_1:.*]], ptr %[[VAL_0]], align 8
// LLVM: %[[VAL_2:.*]] = load ptr, ptr %[[VAL_0]], align 8
// LLVM: %[[VAL_3:.*]] = call ptr @_ZnwmPvb(i64 1, ptr %[[VAL_2]], i8 1)
// LLVM: %[[VAL_4:.*]] = icmp ne ptr %[[VAL_3]], null
// LLVM: br i1 %[[VAL_4]], label %[[VAL_5:.*]], label %[[VAL_6:.*]],
// LLVM: [[VAL_5]]: ; preds = %[[VAL_7:.*]]
// LLVM: call void @_ZN6test151AC1Ev(ptr %[[VAL_3]])
// LLVM: br label %[[VAL_6]],
// LLVM: [[VAL_6]]: ; preds = %[[VAL_5]], %[[VAL_7]]
// LLVM: ret void

void test0b(void *p) {
new (p, true) A();
}
}

0 comments on commit d486bad

Please sign in to comment.