Skip to content
Open
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
1 change: 1 addition & 0 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ struct MissingFeatures {
static bool aggValueSlotMayOverlap() { return false; }
static bool aggValueSlotVolatile() { return false; }
static bool alignCXXRecordDecl() { return false; }
static bool allocToken() { return false; }
static bool appleKext() { return false; }
static bool armComputeVolatileBitfields() { return false; }
static bool asmGoto() { return false; }
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
#include "mlir/IR/BuiltinAttributes.h"
#include "mlir/IR/Value.h"
#include "mlir/Support/LLVM.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/Expr.h"
#include "clang/AST/GlobalDecl.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/OperatorKinds.h"
#include "clang/CIR/Dialect/IR/CIRTypes.h"
#include "clang/CIR/MissingFeatures.h"
#include "llvm/Support/ErrorHandling.h"
Expand Down Expand Up @@ -924,8 +926,14 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
case Builtin::BI__addressof:
case Builtin::BI__builtin_addressof:
case Builtin::BI__builtin_function_start:
return errorBuiltinNYI(*this, e, builtinID);
case Builtin::BI__builtin_operator_new:
return emitNewOrDeleteBuiltinCall(
e->getCallee()->getType()->castAs<FunctionProtoType>(), e, OO_New);
case Builtin::BI__builtin_operator_delete:
emitNewOrDeleteBuiltinCall(
e->getCallee()->getType()->castAs<FunctionProtoType>(), e, OO_Delete);
return RValue::get(nullptr);
case Builtin::BI__builtin_is_aligned:
case Builtin::BI__builtin_align_up:
case Builtin::BI__builtin_align_down:
Expand Down
31 changes: 31 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprCXX.h"
#include "clang/Basic/OperatorKinds.h"
#include "clang/CIR/MissingFeatures.h"

using namespace clang;
Expand Down Expand Up @@ -648,6 +649,36 @@ static RValue emitNewDeleteCall(CIRGenFunction &cgf,
return rv;
}

RValue CIRGenFunction::emitNewOrDeleteBuiltinCall(const FunctionProtoType *type,
const CallExpr *callExpr,
OverloadedOperatorKind op) {
CallArgList args;
emitCallArgs(args, type, callExpr->arguments());
// Find the allocation or deallocation function that we're calling.
ASTContext &astContext = getContext();
assert(op == OO_New || op == OO_Delete);
DeclarationName name = astContext.DeclarationNames.getCXXOperatorName(op);

clang::DeclContextLookupResult lookupResult =
astContext.getTranslationUnitDecl()->lookup(name);
for (const auto *decl : lookupResult) {
if (const auto *funcDecl = dyn_cast<FunctionDecl>(decl)) {
if (astContext.hasSameType(funcDecl->getType(), QualType(type, 0))) {
if (sanOpts.has(SanitizerKind::AllocToken)) {
// TODO: Set !alloc_token metadata.
assert(!cir::MissingFeatures::allocToken());
cgm.errorNYI("Alloc token sanitizer not yet supported!");
}

// Emit the call to operator new/delete.
return emitNewDeleteCall(*this, funcDecl, type, args);
}
}
}

llvm_unreachable("predeclared global operator new/delete is missing");
}

namespace {
/// Calls the given 'operator delete' on a single object.
struct CallObjectDelete final : EHScopeStack::Cleanup {
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "clang/AST/ExprCXX.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/Type.h"
#include "clang/Basic/OperatorKinds.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/MissingFeatures.h"
#include "clang/CIR/TypeEvaluationKind.h"
Expand Down Expand Up @@ -1487,6 +1488,10 @@ class CIRGenFunction : public CIRGenTypeCache {

RValue emitCXXPseudoDestructorExpr(const CXXPseudoDestructorExpr *expr);

RValue emitNewOrDeleteBuiltinCall(const FunctionProtoType *type,
const CallExpr *callExpr,
OverloadedOperatorKind op);

void emitCXXTemporary(const CXXTemporary *temporary, QualType tempType,
Address ptr);

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


void test_builtins_basic() {
__builtin_operator_delete(__builtin_operator_new(4));
// CIR-LABEL: test_builtins_basic
// CIR: [[P:%.*]] = cir.call @_Znwm({{%.*}}) : (!u64i) -> !cir.ptr<!void>
Copy link
Contributor Author

@HendrikHuebner HendrikHuebner Nov 18, 2025

Choose a reason for hiding this comment

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

OGCG actually generates a bunch of attributes, something like: call noalias noundef nonnull ptr @_Znwm(i64 noundef 4).

Is it okay to ignore these with the wildcard syntax in the test?

Copy link
Contributor

Choose a reason for hiding this comment

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

Where are these set? And why is it not set in CIR pipeline?
I would prefer not to ignroe them in this case as CIRGen is creating the call in this case?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think they're being set in CodeGenModule::ConstructAttributeList in classic codegen. This is a known hole in CIR codegen. Even if we were setting these the same as OGCG, I'd want to use a wildcard match here.

// CIR: cir.call @_ZdlPv([[P]]) {{.*}}: (!cir.ptr<!void>) -> ()
// CIR: cir.return

// LLVM-LABEL: test_builtins_basic
// LLVM: [[P:%.*]] = call ptr @_Znwm(i64 4)
// LLVM: call void @_ZdlPv(ptr [[P]])
// LLVM: ret void

// OGCG-LABEL: test_builtins_basic
// OGCG: [[P:%.*]] = call {{.*}} ptr @_Znwm(i64 {{.*}} 4)
// OGCG: call void @_ZdlPv(ptr {{.*}} [[P]])
// OGCG: ret void
}

void test_sized_delete() {
__builtin_operator_delete(__builtin_operator_new(4), 4);

// CIR-LABEL: test_sized_delete
// CIR: [[P:%.*]] = cir.call @_Znwm({{%.*}}) : (!u64i) -> !cir.ptr<!void>
// CIR: cir.call @_ZdlPvm([[P]], {{%.*}}) {{.*}}: (!cir.ptr<!void>, !u64i) -> ()
// CIR: cir.return

// LLVM-LABEL: test_sized_delete
// LLVM: [[P:%.*]] = call ptr @_Znwm(i64 4)
// LLVM: call void @_ZdlPvm(ptr [[P]], i64 4)
// LLVM: ret void

// OGCG-LABEL: test_sized_delete
// OGCG: [[P:%.*]] = call {{.*}} ptr @_Znwm(i64 {{.*}} 4)
// OGCG: call void @_ZdlPvm(ptr {{.*}} [[P]], i64 {{.*}} 4)
// OGCG: ret void
}