Skip to content

Commit

Permalink
Implement array cookie ABI for AppleARM64 targets
Browse files Browse the repository at this point in the history
This change introduces CIRGenCXXABI subclasses for ARM and AppleARM64 and
implements ARM CXXABI handling for array cookies.
  • Loading branch information
andykaylor committed Jan 29, 2025
1 parent d329c96 commit 7454149
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 5 deletions.
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,
// /*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,
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>
// 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

0 comments on commit 7454149

Please sign in to comment.