Skip to content

Commit 7c22251

Browse files
authored
[CIR][CIRGen] Implement array cookie ABI for AppleARM64 targets (#1301)
This change introduces CIRGenCXXABI subclasses for ARM and AppleARM64 and implements ARM CXXABI handling for array cookies.
1 parent de1cd92 commit 7c22251

File tree

3 files changed

+175
-5
lines changed

3 files changed

+175
-5
lines changed

clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp

+80-5
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,30 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
360360
classifyRTTIUniqueness(QualType CanTy, cir::GlobalLinkageKind Linkage) const;
361361
friend class CIRGenItaniumRTTIBuilder;
362362
};
363+
364+
class CIRGenARMCXXABI : public CIRGenItaniumCXXABI {
365+
public:
366+
CIRGenARMCXXABI(CIRGenModule &CGM) : CIRGenItaniumCXXABI(CGM) {
367+
// TODO(cir): When implemented, /*UseARMMethodPtrABI=*/true,
368+
// /*UseARMGuardVarABI=*/true) {}
369+
assert(!cir::MissingFeatures::appleArm64CXXABI());
370+
}
371+
CharUnits getArrayCookieSizeImpl(QualType elementType) override;
372+
Address initializeArrayCookie(CIRGenFunction &CGF, Address NewPtr,
373+
mlir::Value NumElements, const CXXNewExpr *E,
374+
QualType ElementType) override;
375+
};
376+
377+
class CIRGenAppleARM64CXXABI : public CIRGenARMCXXABI {
378+
public:
379+
CIRGenAppleARM64CXXABI(CIRGenModule &CGM) : CIRGenARMCXXABI(CGM) {
380+
Use32BitVTableOffsetABI = true;
381+
}
382+
383+
// ARM64 libraries are prepared for non-unique RTTI.
384+
bool shouldRTTIBeUnique() const override { return false; }
385+
};
386+
363387
} // namespace
364388

365389
CIRGenCXXABI::AddedStructorArgs CIRGenItaniumCXXABI::getImplicitConstructorArgs(
@@ -404,12 +428,11 @@ CIRGenCXXABI *clang::CIRGen::CreateCIRGenItaniumCXXABI(CIRGenModule &CGM) {
404428
switch (CGM.getASTContext().getCXXABIKind()) {
405429
case TargetCXXABI::GenericItanium:
406430
case TargetCXXABI::GenericAArch64:
407-
case TargetCXXABI::AppleARM64:
408-
// TODO: this isn't quite right, clang uses AppleARM64CXXABI which inherits
409-
// from ARMCXXABI. We'll have to follow suit.
410-
assert(!cir::MissingFeatures::appleArm64CXXABI());
411431
return new CIRGenItaniumCXXABI(CGM);
412432

433+
case TargetCXXABI::AppleARM64:
434+
return new CIRGenAppleARM64CXXABI(CGM);
435+
413436
default:
414437
llvm_unreachable("bad or NYI ABI kind");
415438
}
@@ -2700,4 +2723,56 @@ Address CIRGenItaniumCXXABI::initializeArrayCookie(CIRGenFunction &CGF,
27002723
auto OffsetOp = CGF.getBuilder().getSignedInt(Loc, Offset, /*width=*/32);
27012724
auto DataPtr = CGF.getBuilder().createPtrStride(Loc, CastOp, OffsetOp);
27022725
return Address(DataPtr, NewPtr.getType(), NewPtr.getAlignment());
2703-
}
2726+
}
2727+
2728+
CharUnits CIRGenARMCXXABI::getArrayCookieSizeImpl(QualType elementType) {
2729+
// ARM says that the cookie is always:
2730+
// struct array_cookie {
2731+
// std::size_t element_size; // element_size != 0
2732+
// std::size_t element_count;
2733+
// };
2734+
// But the base ABI doesn't give anything an alignment greater than
2735+
// 8, so we can dismiss this as typical ABI-author blindness to
2736+
// actual language complexity and round up to the element alignment.
2737+
return std::max(CharUnits::fromQuantity(2 * CGM.SizeSizeInBytes),
2738+
getContext().getTypeAlignInChars(elementType));
2739+
}
2740+
2741+
Address CIRGenARMCXXABI::initializeArrayCookie(CIRGenFunction &cgf,
2742+
Address newPtr,
2743+
mlir::Value numElements,
2744+
const CXXNewExpr *expr,
2745+
QualType elementType) {
2746+
assert(requiresArrayCookie(expr));
2747+
2748+
// The cookie is always at the start of the buffer.
2749+
auto cookiePtr =
2750+
cgf.getBuilder().createPtrBitcast(newPtr.getPointer(), cgf.SizeTy);
2751+
Address cookie = Address(cookiePtr, cgf.SizeTy, newPtr.getAlignment());
2752+
2753+
ASTContext &ctx = getContext();
2754+
CharUnits sizeSize = cgf.getSizeSize();
2755+
mlir::Location loc = cgf.getLoc(expr->getSourceRange());
2756+
2757+
// The first element is the element size.
2758+
mlir::Value elementSize = cgf.getBuilder().getConstInt(
2759+
loc, cgf.SizeTy, ctx.getTypeSizeInChars(elementType).getQuantity());
2760+
cgf.getBuilder().createStore(loc, elementSize, cookie);
2761+
2762+
// The second element is the element count.
2763+
auto offsetOp = cgf.getBuilder().getSignedInt(loc, 1, /*width=*/32);
2764+
auto dataPtr =
2765+
cgf.getBuilder().createPtrStride(loc, cookie.getPointer(), offsetOp);
2766+
cookie = Address(dataPtr, cgf.SizeTy, newPtr.getAlignment());
2767+
cgf.getBuilder().createStore(loc, numElements, cookie);
2768+
2769+
// Finally, compute a pointer to the actual data buffer by skipping
2770+
// over the cookie completely.
2771+
CharUnits cookieSize = CIRGenARMCXXABI::getArrayCookieSizeImpl(elementType);
2772+
offsetOp = cgf.getBuilder().getSignedInt(loc, cookieSize.getQuantity(),
2773+
/*width=*/32);
2774+
auto castOp = cgf.getBuilder().createPtrBitcast(
2775+
newPtr.getPointer(), cgf.getBuilder().getUIntNTy(8));
2776+
dataPtr = cgf.getBuilder().createPtrStride(loc, castOp, offsetOp);
2777+
return Address(dataPtr, newPtr.getType(), newPtr.getAlignment());
2778+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// RUN: %clang_cc1 -std=c++20 -triple=arm64e-apple-darwin -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s
3+
4+
class C {
5+
public:
6+
~C();
7+
};
8+
9+
void t_constant_size_nontrivial() {
10+
auto p = new C[3];
11+
}
12+
13+
// CHECK: cir.func @_Z26t_constant_size_nontrivialv()
14+
// CHECK: %[[#NUM_ELEMENTS:]] = cir.const #cir.int<3> : !u64i
15+
// CHECK: %[[#SIZE_WITHOUT_COOKIE:]] = cir.const #cir.int<3> : !u64i
16+
// CHECK: %[[#ALLOCATION_SIZE:]] = cir.const #cir.int<19> : !u64i
17+
// CHECK: %[[#ALLOC_PTR:]] = cir.call @_Znam(%[[#ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
18+
// CHECK: %[[#COOKIE_PTR:]] = cir.cast(bitcast, %[[#ALLOC_PTR]] : !cir.ptr<!void>), !cir.ptr<!u64i>
19+
// CHECK: %[[#ELEMENT_SIZE:]] = cir.const #cir.int<1> : !u64i
20+
// CHECK: cir.store %[[#ELEMENT_SIZE]], %[[#COOKIE_PTR]] : !u64i, !cir.ptr<!u64i>
21+
// CHECK: %[[#SECOND_COOKIE_OFFSET:]] = cir.const #cir.int<1> : !s32i
22+
// CHECK: %[[#COOKIE_PTR2:]] = cir.ptr_stride(%[[#COOKIE_PTR]] : !cir.ptr<!u64i>, %[[#SECOND_COOKIE_OFFSET]] : !s32i), !cir.ptr<!u64i>
23+
// CHECK: cir.store %[[#NUM_ELEMENTS]], %[[#COOKIE_PTR2]] : !u64i, !cir.ptr<!u64i>
24+
// CHECK: %[[#COOKIE_SIZE:]] = cir.const #cir.int<16> : !s32i
25+
// CHECK: %[[#ALLOC_AS_I8:]] = cir.cast(bitcast, %[[#ALLOC_PTR]] : !cir.ptr<!void>), !cir.ptr<!u8i>
26+
// CHECK: cir.ptr_stride(%[[#ALLOC_AS_I8]] : !cir.ptr<!u8i>, %[[#COOKIE_SIZE]] : !s32i), !cir.ptr<!u8i>
27+
28+
class D {
29+
public:
30+
int x;
31+
~D();
32+
};
33+
34+
void t_constant_size_nontrivial2() {
35+
auto p = new D[3];
36+
}
37+
38+
// In this test SIZE_WITHOUT_COOKIE isn't used, but it would be if there were
39+
// an initializer.
40+
41+
// CHECK: cir.func @_Z27t_constant_size_nontrivial2v()
42+
// CHECK: %[[#NUM_ELEMENTS:]] = cir.const #cir.int<3> : !u64i
43+
// CHECK: %[[#SIZE_WITHOUT_COOKIE:]] = cir.const #cir.int<12> : !u64i
44+
// CHECK: %[[#ALLOCATION_SIZE:]] = cir.const #cir.int<28> : !u64i
45+
// CHECK: %[[#ALLOC_PTR:]] = cir.call @_Znam(%[[#ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
46+
// CHECK: %[[#COOKIE_PTR:]] = cir.cast(bitcast, %[[#ALLOC_PTR]] : !cir.ptr<!void>), !cir.ptr<!u64i>
47+
// CHECK: %[[#ELEMENT_SIZE:]] = cir.const #cir.int<4> : !u64i
48+
// CHECK: cir.store %[[#ELEMENT_SIZE]], %[[#COOKIE_PTR]] : !u64i, !cir.ptr<!u64i>
49+
// CHECK: %[[#SECOND_COOKIE_OFFSET:]] = cir.const #cir.int<1> : !s32i
50+
// CHECK: %[[#COOKIE_PTR2:]] = cir.ptr_stride(%[[#COOKIE_PTR]] : !cir.ptr<!u64i>, %[[#SECOND_COOKIE_OFFSET]] : !s32i), !cir.ptr<!u64i>
51+
// CHECK: cir.store %[[#NUM_ELEMENTS]], %[[#COOKIE_PTR2]] : !u64i, !cir.ptr<!u64i>
52+
// CHECK: %[[#COOKIE_SIZE:]] = cir.const #cir.int<16> : !s32i
53+
// CHECK: %[[#ALLOC_AS_I8:]] = cir.cast(bitcast, %[[#ALLOC_PTR]] : !cir.ptr<!void>), !cir.ptr<!u8i>
54+
// CHECK: cir.ptr_stride(%[[#ALLOC_AS_I8]] : !cir.ptr<!u8i>, %[[#COOKIE_SIZE]] : !s32i), !cir.ptr<!u8i>
+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// RUN: %clang_cc1 -triple=arm64e-apple-darwin -fclangir -emit-llvm %s -o %t.ll
2+
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM
3+
4+
class C {
5+
public:
6+
~C();
7+
};
8+
9+
void t_constant_size_nontrivial() {
10+
auto p = new C[3];
11+
}
12+
13+
// Note: The below differs from the IR emitted by clang without -fclangir in
14+
// several respects. (1) The alloca here has an extra "i64 1"
15+
// (2) The operator new call is missing "noalias noundef nonnull" on
16+
// the call and "noundef" on the argument, (3) The getelementptr is
17+
// missing "inbounds"
18+
19+
// LLVM: @_Z26t_constant_size_nontrivialv()
20+
// LLVM: %[[COOKIE_PTR:.*]] = call ptr @_Znam(i64 19)
21+
// LLVM: store i64 1, ptr %[[COOKIE_PTR]], align 8
22+
// LLVM: %[[NUM_ELEMENTS_PTR:.*]] = getelementptr i64, ptr %[[COOKIE_PTR]], i64 1
23+
// LLVM: store i64 3, ptr %[[NUM_ELEMENTS_PTR]], align 8
24+
// LLVM: %[[ALLOCATED_PTR:.*]] = getelementptr i8, ptr %[[COOKIE_PTR]], i64 16
25+
26+
class D {
27+
public:
28+
int x;
29+
~D();
30+
};
31+
32+
void t_constant_size_nontrivial2() {
33+
auto p = new D[3];
34+
}
35+
36+
// LLVM: @_Z27t_constant_size_nontrivial2v()
37+
// LLVM: %[[COOKIE_PTR:.*]] = call ptr @_Znam(i64 28)
38+
// LLVM: store i64 4, ptr %[[COOKIE_PTR]], align 8
39+
// LLVM: %[[NUM_ELEMENTS_PTR:.*]] = getelementptr i64, ptr %[[COOKIE_PTR]], i64 1
40+
// LLVM: store i64 3, ptr %[[NUM_ELEMENTS_PTR]], align 8
41+
// LLVM: %[[ALLOCATED_PTR:.*]] = getelementptr i8, ptr %[[COOKIE_PTR]], i64 16

0 commit comments

Comments
 (0)