Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CIR][CIRGen] Implement array cookie ABI for AppleARM64 targets #1301

Merged
merged 3 commits into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
84 changes: 79 additions & 5 deletions clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,29 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
classifyRTTIUniqueness(QualType CanTy, cir::GlobalLinkageKind Linkage) const;
friend class CIRGenItaniumRTTIBuilder;
};

class CIRGenARMCXXABI : public CIRGenItaniumCXXABI {
public:
CIRGenARMCXXABI(CIRGenModule &CGM) : CIRGenItaniumCXXABI(CGM) {}
// TODO: When implemented, /*UseARMMethodPtrABI=*/true,
bcardosolopes marked this conversation as resolved.
Show resolved Hide resolved
// /*UseARMGuardVarABI=*/true) {}

CharUnits getArrayCookieSizeImpl(QualType elementType) override;
Address initializeArrayCookie(CIRGenFunction &CGF, Address NewPtr,
mlir::Value NumElements, const CXXNewExpr *E,
QualType ElementType) override;
};

class CIRGenAppleARM64CXXABI : public CIRGenARMCXXABI {
public:
CIRGenAppleARM64CXXABI(CIRGenModule &CGM) : CIRGenARMCXXABI(CGM) {
Use32BitVTableOffsetABI = true;
}

// ARM64 libraries are prepared for non-unique RTTI.
bool shouldRTTIBeUnique() const override { return false; }
};

} // namespace

CIRGenCXXABI::AddedStructorArgs CIRGenItaniumCXXABI::getImplicitConstructorArgs(
Expand Down Expand Up @@ -404,12 +427,11 @@ CIRGenCXXABI *clang::CIRGen::CreateCIRGenItaniumCXXABI(CIRGenModule &CGM) {
switch (CGM.getASTContext().getCXXABIKind()) {
case TargetCXXABI::GenericItanium:
case TargetCXXABI::GenericAArch64:
case TargetCXXABI::AppleARM64:
// TODO: this isn't quite right, clang uses AppleARM64CXXABI which inherits
// from ARMCXXABI. We'll have to follow suit.
assert(!cir::MissingFeatures::appleArm64CXXABI());
return new CIRGenItaniumCXXABI(CGM);

case TargetCXXABI::AppleARM64:
return new CIRGenAppleARM64CXXABI(CGM);

default:
llvm_unreachable("bad or NYI ABI kind");
}
Expand Down Expand Up @@ -2700,4 +2722,56 @@ Address CIRGenItaniumCXXABI::initializeArrayCookie(CIRGenFunction &CGF,
auto OffsetOp = CGF.getBuilder().getSignedInt(Loc, Offset, /*width=*/32);
auto DataPtr = CGF.getBuilder().createPtrStride(Loc, CastOp, OffsetOp);
return Address(DataPtr, NewPtr.getType(), NewPtr.getAlignment());
}
}

CharUnits CIRGenARMCXXABI::getArrayCookieSizeImpl(QualType elementType) {
// ARM says that the cookie is always:
// struct array_cookie {
// std::size_t element_size; // element_size != 0
// std::size_t element_count;
// };
// But the base ABI doesn't give anything an alignment greater than
// 8, so we can dismiss this as typical ABI-author blindness to
// actual language complexity and round up to the element alignment.
return std::max(CharUnits::fromQuantity(2 * CGM.SizeSizeInBytes),
getContext().getTypeAlignInChars(elementType));
}

Address CIRGenARMCXXABI::initializeArrayCookie(CIRGenFunction &CGF,
bcardosolopes marked this conversation as resolved.
Show resolved Hide resolved
Address NewPtr,
mlir::Value NumElements,
const CXXNewExpr *E,
QualType ElementType) {
assert(requiresArrayCookie(E));

// The cookie is always at the start of the buffer.
auto CookiePtr =
CGF.getBuilder().createPtrBitcast(NewPtr.getPointer(), CGF.SizeTy);
Address Cookie = Address(CookiePtr, CGF.SizeTy, NewPtr.getAlignment());

ASTContext &Ctx = getContext();
CharUnits SizeSize = CGF.getSizeSize();
mlir::Location Loc = CGF.getLoc(E->getSourceRange());

// The first element is the element size.
mlir::Value ElementSize = CGF.getBuilder().getConstInt(
Loc, CGF.SizeTy, Ctx.getTypeSizeInChars(ElementType).getQuantity());
CGF.getBuilder().createStore(Loc, ElementSize, Cookie);

// The second element is the element count.
auto OffsetOp = CGF.getBuilder().getSignedInt(Loc, 1, /*width=*/32);
auto DataPtr =
CGF.getBuilder().createPtrStride(Loc, Cookie.getPointer(), OffsetOp);
Cookie = Address(DataPtr, CGF.SizeTy, NewPtr.getAlignment());
CGF.getBuilder().createStore(Loc, NumElements, Cookie);

// Finally, compute a pointer to the actual data buffer by skipping
// over the cookie completely.
CharUnits CookieSize = CIRGenARMCXXABI::getArrayCookieSizeImpl(ElementType);
OffsetOp = CGF.getBuilder().getSignedInt(Loc, CookieSize.getQuantity(),
/*width=*/32);
auto CastOp = CGF.getBuilder().createPtrBitcast(
NewPtr.getPointer(), CGF.getBuilder().getUIntNTy(8));
DataPtr = CGF.getBuilder().createPtrStride(Loc, CastOp, OffsetOp);
return Address(DataPtr, NewPtr.getType(), NewPtr.getAlignment());
}
64 changes: 64 additions & 0 deletions clang/test/CIR/CodeGen/applearm64-array-cookies.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// RUN: %clang_cc1 -std=c++20 -triple=arm64e-apple-darwin -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s

class C {
public:
~C();
};

void t_constant_size_nontrivial() {
auto p = new C[3];
}

// CHECK: cir.func @_Z26t_constant_size_nontrivialv()
// CHECK: %0 = cir.alloca !cir.ptr<!ty_C>, !cir.ptr<!cir.ptr<!ty_C>>, ["p", init] {alignment = 8 : i64}
// CHECK: %[[#NUM_ELEMENTS:]] = cir.const #cir.int<3> : !u64i
// CHECK: %[[#SIZE_WITHOUT_COOKIE:]] = cir.const #cir.int<3> : !u64i
// CHECK: %[[#ALLOCATION_SIZE:]] = cir.const #cir.int<19> : !u64i
// CHECK: %4 = cir.call @_Znam(%[[#ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
bcardosolopes marked this conversation as resolved.
Show resolved Hide resolved
// CHECK: %5 = cir.cast(bitcast, %4 : !cir.ptr<!void>), !cir.ptr<!u64i>
// CHECK: %[[#ELEMENT_SIZE:]] = cir.const #cir.int<1> : !u64i
// CHECK: cir.store %[[#ELEMENT_SIZE]], %5 : !u64i, !cir.ptr<!u64i>
// CHECK: %[[#SECOND_COOKIE_OFFSET:]] = cir.const #cir.int<1> : !s32i
// CHECK: %8 = cir.ptr_stride(%5 : !cir.ptr<!u64i>, %[[#SECOND_COOKIE_OFFSET]] : !s32i), !cir.ptr<!u64i>
// CHECK: cir.store %[[#NUM_ELEMENTS]], %8 : !u64i, !cir.ptr<!u64i>
// CHECK: %[[#COOKIE_SIZE:]] = cir.const #cir.int<16> : !s32i
// CHECK: %10 = cir.cast(bitcast, %4 : !cir.ptr<!void>), !cir.ptr<!u8i>
// CHECK: %11 = cir.ptr_stride(%10 : !cir.ptr<!u8i>, %[[#COOKIE_SIZE]] : !s32i), !cir.ptr<!u8i>
// CHECK: %12 = cir.cast(bitcast, %11 : !cir.ptr<!u8i>), !cir.ptr<!ty_C>
// CHECK: cir.store %12, %0 : !cir.ptr<!ty_C>, !cir.ptr<!cir.ptr<!ty_C>>
// CHECK: cir.return
// CHECK: }

class D {
public:
int x;
~D();
};

void t_constant_size_nontrivial2() {
auto p = new D[3];
}

// In this test SIZE_WITHOUT_COOKIE isn't used, but it would be if there were
// an initializer.

// CHECK: cir.func @_Z27t_constant_size_nontrivial2v()
// CHECK: %0 = cir.alloca !cir.ptr<!ty_D>, !cir.ptr<!cir.ptr<!ty_D>>, ["p", init] {alignment = 8 : i64}
// CHECK: %[[#NUM_ELEMENTS:]] = cir.const #cir.int<3> : !u64i
// CHECK: %[[#SIZE_WITHOUT_COOKIE:]] = cir.const #cir.int<12> : !u64i
// CHECK: %[[#ALLOCATION_SIZE:]] = cir.const #cir.int<28> : !u64i
// CHECK: %4 = cir.call @_Znam(%[[#ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
// CHECK: %5 = cir.cast(bitcast, %4 : !cir.ptr<!void>), !cir.ptr<!u64i>
// CHECK: %[[#ELEMENT_SIZE:]] = cir.const #cir.int<4> : !u64i
// CHECK: cir.store %[[#ELEMENT_SIZE]], %5 : !u64i, !cir.ptr<!u64i>
// CHECK: %[[#SECOND_COOKIE_OFFSET:]] = cir.const #cir.int<1> : !s32i
// CHECK: %8 = cir.ptr_stride(%5 : !cir.ptr<!u64i>, %[[#SECOND_COOKIE_OFFSET]] : !s32i), !cir.ptr<!u64i>
// CHECK: cir.store %[[#NUM_ELEMENTS]], %8 : !u64i, !cir.ptr<!u64i>
// CHECK: %[[#COOKIE_SIZE:]] = cir.const #cir.int<16> : !s32i
// CHECK: %10 = cir.cast(bitcast, %4 : !cir.ptr<!void>), !cir.ptr<!u8i>
// CHECK: %11 = cir.ptr_stride(%10 : !cir.ptr<!u8i>, %[[#COOKIE_SIZE]] : !s32i), !cir.ptr<!u8i>
// CHECK: %12 = cir.cast(bitcast, %11 : !cir.ptr<!u8i>), !cir.ptr<!ty_D>
// CHECK: cir.store %12, %0 : !cir.ptr<!ty_D>, !cir.ptr<!cir.ptr<!ty_D>>
// CHECK: cir.return
// CHECK: }
45 changes: 45 additions & 0 deletions clang/test/CIR/Lowering/applearm64-new.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// RUN: %clang_cc1 -triple=arm64e-apple-darwin -fclangir -emit-llvm %s -o %t.ll
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM

class C {
public:
~C();
};

void t_constant_size_nontrivial() {
auto p = new C[3];
}

// Note: The below differs from the IR emitted by clang without -fclangir in
// several respects. (1) The alloca here has an extra "i64 1"
// (2) The operator new call is missing "noalias noundef nonnull" on
// the call and "noundef" on the argument, (3) The getelementptr is
// missing "inbounds"

// LLVM: @_Z26t_constant_size_nontrivialv()
// LLVM: %[[ALLOCA:.*]] = alloca ptr, i64 1, align 8
// LLVM: %[[COOKIE_PTR:.*]] = call ptr @_Znam(i64 19)
// LLVM: store i64 1, ptr %[[COOKIE_PTR]], align 8
// LLVM: %[[NUM_ELEMENTS_PTR:.*]] = getelementptr i64, ptr %[[COOKIE_PTR]], i64 1
// LLVM: store i64 3, ptr %[[NUM_ELEMENTS_PTR]], align 8
// LLVM: %[[ALLOCATED_PTR:.*]] = getelementptr i8, ptr %[[COOKIE_PTR]], i64 16
// LLVM: store ptr %[[ALLOCATED_PTR]], ptr %[[ALLOCA]], align 8

class D {
public:
int x;
~D();
};

void t_constant_size_nontrivial2() {
auto p = new D[3];
}

// LLVM: @_Z27t_constant_size_nontrivial2v()
// LLVM: %[[ALLOCA:.*]] = alloca ptr, i64 1, align 8
// LLVM: %[[COOKIE_PTR:.*]] = call ptr @_Znam(i64 28)
// LLVM: store i64 4, ptr %[[COOKIE_PTR]], align 8
// LLVM: %[[NUM_ELEMENTS_PTR:.*]] = getelementptr i64, ptr %[[COOKIE_PTR]], i64 1
// LLVM: store i64 3, ptr %[[NUM_ELEMENTS_PTR]], align 8
// LLVM: %[[ALLOCATED_PTR:.*]] = getelementptr i8, ptr %[[COOKIE_PTR]], i64 16
// LLVM: store ptr %[[ALLOCATED_PTR]], ptr %[[ALLOCA]], align 8