clang 23.0.0git
LowerItaniumCXXABI.cpp
Go to the documentation of this file.
1//===---- LowerItaniumCXXABI.cpp - Emit CIR code Itanium-specific code ---===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This provides CIR lowering logic targeting the Itanium C++ ABI. The class in
10// this file generates records that follow the Itanium C++ ABI, which is
11// documented at:
12// https://itanium-cxx-abi.github.io/cxx-abi/abi.html
13// https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html
14//
15// It also supports the closely-related ARM ABI, documented at:
16// https://developer.arm.com/documentation/ihi0041/g/
17//
18// This file partially mimics clang/lib/CodeGen/ItaniumCXXABI.cpp. The queries
19// are adapted to operate on the CIR dialect, however.
20//
21//===----------------------------------------------------------------------===//
22
23#include "CIRCXXABI.h"
24#include "LowerModule.h"
25#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
26#include "mlir/IR/ImplicitLocOpBuilder.h"
27#include "llvm/Support/ErrorHandling.h"
28
29namespace cir {
30
31namespace {
32
33class LowerItaniumCXXABI : public CIRCXXABI {
34protected:
35 bool useARMMethodPtrABI;
36 bool use32BitVTableOffsetABI;
37
38public:
39 LowerItaniumCXXABI(LowerModule &lm, bool useARMMethodPtrABI = false,
40 bool use32BitVTableOffsetABI = false)
41 : CIRCXXABI(lm), useARMMethodPtrABI(useARMMethodPtrABI),
42 use32BitVTableOffsetABI(use32BitVTableOffsetABI) {}
43
44 /// Lower the given data member pointer type to its ABI type. The returned
45 /// type is also a CIR type.
46 virtual mlir::Type
47 lowerDataMemberType(cir::DataMemberType type,
48 const mlir::TypeConverter &typeConverter) const override;
49
50 mlir::Type
51 lowerMethodType(cir::MethodType type,
52 const mlir::TypeConverter &typeConverter) const override;
53
54 mlir::TypedAttr lowerDataMemberConstant(
55 cir::DataMemberAttr attr, const mlir::DataLayout &layout,
56 const mlir::TypeConverter &typeConverter) const override;
57
58 mlir::TypedAttr
59 lowerMethodConstant(cir::MethodAttr attr, const mlir::DataLayout &layout,
60 const mlir::TypeConverter &typeConverter) const override;
61
62 mlir::Operation *
63 lowerGetRuntimeMember(cir::GetRuntimeMemberOp op, mlir::Type loweredResultTy,
64 mlir::Value loweredAddr, mlir::Value loweredMember,
65 mlir::OpBuilder &builder) const override;
66
67 void lowerGetMethod(cir::GetMethodOp op, mlir::Value &callee,
68 mlir::Value &thisArg, mlir::Value loweredMethod,
69 mlir::Value loweredObjectPtr,
70 mlir::ConversionPatternRewriter &rewriter) const override;
71
72 mlir::Value lowerBaseDataMember(cir::BaseDataMemberOp op,
73 mlir::Value loweredSrc,
74 mlir::OpBuilder &builder) const override;
75
76 mlir::Value lowerDerivedDataMember(cir::DerivedDataMemberOp op,
77 mlir::Value loweredSrc,
78 mlir::OpBuilder &builder) const override;
79
80 mlir::Value lowerBaseMethod(cir::BaseMethodOp op, mlir::Value loweredSrc,
81 mlir::OpBuilder &builder) const override;
82
83 mlir::Value lowerDerivedMethod(cir::DerivedMethodOp op,
84 mlir::Value loweredSrc,
85 mlir::OpBuilder &builder) const override;
86
87 mlir::Value lowerDataMemberCmp(cir::CmpOp op, mlir::Value loweredLhs,
88 mlir::Value loweredRhs,
89 mlir::OpBuilder &builder) const override;
90
91 mlir::Value lowerMethodCmp(cir::CmpOp op, mlir::Value loweredLhs,
92 mlir::Value loweredRhs,
93 mlir::OpBuilder &builder) const override;
94
95 mlir::Value lowerDataMemberBitcast(cir::CastOp op, mlir::Type loweredDstTy,
96 mlir::Value loweredSrc,
97 mlir::OpBuilder &builder) const override;
98
99 mlir::Value
100 lowerDataMemberToBoolCast(cir::CastOp op, mlir::Value loweredSrc,
101 mlir::OpBuilder &builder) const override;
102
103 mlir::Value lowerMethodBitcast(cir::CastOp op, mlir::Type loweredDstTy,
104 mlir::Value loweredSrc,
105 mlir::OpBuilder &builder) const override;
106
107 mlir::Value lowerMethodToBoolCast(cir::CastOp op, mlir::Value loweredSrc,
108 mlir::OpBuilder &builder) const override;
109
110 mlir::Value lowerDynamicCast(cir::DynamicCastOp op,
111 mlir::OpBuilder &builder) const override;
112 mlir::Value lowerVTableGetTypeInfo(cir::VTableGetTypeInfoOp op,
113 mlir::OpBuilder &builder) const override;
114
115 clang::CharUnits
116 getArrayCookieSizeImpl(mlir::Type elementType,
117 const mlir::DataLayout &dataLayout) const override;
118
119 mlir::Value readArrayCookieImpl(mlir::Location loc, mlir::Value allocPtr,
120 clang::CharUnits cookieSize,
121 clang::CharUnits cookieAlignment,
122 const mlir::DataLayout &dataLayout,
123 CIRBaseBuilderTy &builder) const override;
124};
125
126} // namespace
127
128std::unique_ptr<CIRCXXABI> createItaniumCXXABI(LowerModule &lm) {
129 switch (lm.getCXXABIKind()) {
130 // Note that AArch64 uses the generic ItaniumCXXABI class since it doesn't
131 // include the other 32-bit ARM oddities: constructor/destructor return values
132 // and array cookies.
133 case clang::TargetCXXABI::GenericAArch64:
134 return std::make_unique<LowerItaniumCXXABI>(
135 lm,
136 /*useARMMethodPtrABI=*/true,
137 /*use32BitVTableOffsetABI=*/false);
138 case clang::TargetCXXABI::AppleARM64:
139 // TODO: this isn't quite right, clang uses AppleARM64CXXABI which inherits
140 // from ARMCXXABI. We'll have to follow suit.
142 return std::make_unique<LowerItaniumCXXABI>(
143 lm,
144 /*useARMMethodPtrABI=*/true,
145 /*use32BitVTableOffsetABI=*/true);
146
147 case clang::TargetCXXABI::GenericItanium:
148 return std::make_unique<LowerItaniumCXXABI>(lm);
149
150 case clang::TargetCXXABI::Microsoft:
151 llvm_unreachable("Microsoft ABI is not Itanium-based");
152 default:
153 llvm_unreachable("Other Itanium ABI?");
154 }
155}
156
157static cir::IntType getPtrDiffCIRTy(LowerModule &lm) {
158 const clang::TargetInfo &target = lm.getTarget();
161 return cir::IntType::get(lm.getMLIRContext(), target.getTypeWidth(ptrdiffTy),
162 target.isTypeSigned(ptrdiffTy));
163}
164
165mlir::Type LowerItaniumCXXABI::lowerDataMemberType(
166 cir::DataMemberType type, const mlir::TypeConverter &typeConverter) const {
167 // Itanium C++ ABI 2.3.1:
168 // A data member pointer is represented as the data member's offset in bytes
169 // from the address point of an object of the base type, as a ptrdiff_t.
170 return getPtrDiffCIRTy(lm);
171}
172
173mlir::Type LowerItaniumCXXABI::lowerMethodType(
174 cir::MethodType type, const mlir::TypeConverter &typeConverter) const {
175 // Itanium C++ ABI 2.3.2:
176 // In all representations, the basic ABI properties of member function
177 // pointer types are those of the following class, where fnptr_t is the
178 // appropriate function-pointer type for a member function of this type:
179 //
180 // struct {
181 // fnptr_t ptr;
182 // ptrdiff_t adj;
183 // };
184
185 cir::IntType ptrdiffCIRTy = getPtrDiffCIRTy(lm);
186
187 // Note that clang CodeGen emits struct{ptrdiff_t, ptrdiff_t} for member
188 // function pointers. Let's follow this approach.
189 return cir::StructType::get(type.getContext(), {ptrdiffCIRTy, ptrdiffCIRTy},
190 /*packed=*/false, /*padded=*/false,
191 /*is_class=*/false);
192}
193
194mlir::TypedAttr LowerItaniumCXXABI::lowerDataMemberConstant(
195 cir::DataMemberAttr attr, const mlir::DataLayout &layout,
196 const mlir::TypeConverter &typeConverter) const {
197 int64_t memberOffset;
198 if (attr.isNullPtr()) {
199 // Itanium C++ ABI 2.3:
200 // A NULL pointer is represented as -1.
201 memberOffset = -1;
202 } else {
203 // Itanium C++ ABI 2.3:
204 // A pointer to data member is an offset from the base address of
205 // the class object containing it, represented as a ptrdiff_t.
206 // Walk the GEP-style path, accumulating the byte offset at each step.
207 memberOffset = 0;
208 mlir::Type currentTy = attr.getType().getClassTy();
209 for (int32_t idx : attr.getPath()) {
210 auto recTy = mlir::cast<cir::RecordType>(currentTy);
211 memberOffset += static_cast<int64_t>(recTy.getElementOffset(layout, idx));
212 currentTy = recTy.getMembers()[idx];
213 }
214 }
215
216 mlir::Type abiTy = lowerDataMemberType(attr.getType(), typeConverter);
217 return cir::IntAttr::get(abiTy, memberOffset);
218}
219
220mlir::TypedAttr LowerItaniumCXXABI::lowerMethodConstant(
221 cir::MethodAttr attr, const mlir::DataLayout &layout,
222 const mlir::TypeConverter &typeConverter) const {
223 cir::IntType ptrdiffCIRTy = getPtrDiffCIRTy(lm);
224
225 auto loweredMethodTy = mlir::cast<cir::StructType>(
226 lowerMethodType(attr.getType(), typeConverter));
227
228 auto zero = cir::IntAttr::get(ptrdiffCIRTy, 0);
229
230 // Itanium C++ ABI 2.3.2:
231 // In all representations, the basic ABI properties of member function
232 // pointer types are those of the following class, where fnptr_t is the
233 // appropriate function-pointer type for a member function of this type:
234 //
235 // struct {
236 // fnptr_t ptr;
237 // ptrdiff_t adj;
238 // };
239
240 if (attr.isNull()) {
241 // Itanium C++ ABI 2.3.2:
242 //
243 // In the standard representation, a null member function pointer is
244 // represented with ptr set to a null pointer. The value of adj is
245 // unspecified for null member function pointers.
246 //
247 // clang CodeGen emits struct{null, null} for null member function pointers.
248 // Let's do the same here.
249 return cir::ConstRecordAttr::get(
250 loweredMethodTy, mlir::ArrayAttr::get(attr.getContext(), {zero, zero}));
251 }
252
253 if (attr.isVirtual()) {
254 if (useARMMethodPtrABI) {
255 // ARM C++ ABI 3.2.1:
256 // This ABI specifies that adj contains twice the this
257 // adjustment, plus 1 if the member function is virtual. The
258 // least significant bit of adj then makes exactly the same
259 // discrimination as the least significant bit of ptr does for
260 // Itanium.
262 auto ptr =
263 cir::IntAttr::get(ptrdiffCIRTy, attr.getVtableOffset().value());
264 auto one = cir::IntAttr::get(ptrdiffCIRTy, 1);
265 return cir::ConstRecordAttr::get(
266 loweredMethodTy, mlir::ArrayAttr::get(attr.getContext(), {ptr, one}));
267 }
268
269 // Itanium C++ ABI 2.3.2:
270 //
271 // In the standard representation, a member function pointer for a
272 // virtual function is represented with ptr set to 1 plus the function's
273 // v-table entry offset (in bytes), converted to a function pointer as if
274 // by reinterpret_cast<fnptr_t>(uintfnptr_t(1 + offset)), where
275 // uintfnptr_t is an unsigned integer of the same size as fnptr_t.
276 auto ptr =
277 cir::IntAttr::get(ptrdiffCIRTy, 1 + attr.getVtableOffset().value());
278 return cir::ConstRecordAttr::get(
279 loweredMethodTy, mlir::ArrayAttr::get(attr.getContext(), {ptr, zero}));
280 }
281
282 // Itanium C++ ABI 2.3.2:
283 //
284 // A member function pointer for a non-virtual member function is
285 // represented with ptr set to a pointer to the function, using the base
286 // ABI's representation of function pointers.
287 auto ptr = cir::GlobalViewAttr::get(ptrdiffCIRTy, attr.getSymbol().value());
288 return cir::ConstRecordAttr::get(
289 loweredMethodTy, mlir::ArrayAttr::get(attr.getContext(), {ptr, zero}));
290}
291
292mlir::Operation *LowerItaniumCXXABI::lowerGetRuntimeMember(
293 cir::GetRuntimeMemberOp op, mlir::Type loweredResultTy,
294 mlir::Value loweredAddr, mlir::Value loweredMember,
295 mlir::OpBuilder &builder) const {
296 auto byteTy = cir::IntType::get(op.getContext(), 8, true);
297 auto bytePtrTy = cir::PointerType::get(
298 byteTy,
299 mlir::cast<cir::PointerType>(op.getAddr().getType()).getAddrSpace());
300 auto objectBytesPtr = cir::CastOp::create(
301 builder, op.getLoc(), bytePtrTy, cir::CastKind::bitcast, op.getAddr());
302 auto memberBytesPtr = cir::PtrStrideOp::create(
303 builder, op.getLoc(), bytePtrTy, objectBytesPtr, loweredMember);
304 return cir::CastOp::create(builder, op.getLoc(), op.getType(),
305 cir::CastKind::bitcast, memberBytesPtr);
306}
307
308void LowerItaniumCXXABI::lowerGetMethod(
309 cir::GetMethodOp op, mlir::Value &callee, mlir::Value &thisArg,
310 mlir::Value loweredMethod, mlir::Value loweredObjectPtr,
311 mlir::ConversionPatternRewriter &rewriter) const {
312 // In the Itanium and ARM ABIs, method pointers have the form:
313 // struct { ptrdiff_t ptr; ptrdiff_t adj; } memptr;
314 //
315 // In the Itanium ABI:
316 // - method pointers are virtual if (memptr.ptr & 1) is nonzero
317 // - the this-adjustment is (memptr.adj)
318 // - the virtual offset is (memptr.ptr - 1)
319 //
320 // In the ARM ABI:
321 // - method pointers are virtual if (memptr.adj & 1) is nonzero
322 // - the this-adjustment is (memptr.adj >> 1)
323 // - the virtual offset is (memptr.ptr)
324 // ARM uses 'adj' for the virtual flag because Thumb functions
325 // may be only single-byte aligned.
326 //
327 // If the member is virtual, the adjusted 'this' pointer points
328 // to a vtable pointer from which the virtual offset is applied.
329 //
330 // If the member is non-virtual, memptr.ptr is the address of
331 // the function to call.
332
333 mlir::ImplicitLocOpBuilder locBuilder(op.getLoc(), rewriter);
334 mlir::Type calleePtrTy = op.getCallee().getType();
335
336 cir::IntType ptrdiffCIRTy = getPtrDiffCIRTy(lm);
337 mlir::Value ptrdiffOne =
338 cir::ConstantOp::create(locBuilder, cir::IntAttr::get(ptrdiffCIRTy, 1));
339
340 mlir::Value rawAdj =
341 cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredMethod, 1);
342 mlir::Value adj = rawAdj;
343 if (useARMMethodPtrABI)
344 adj = cir::ShiftOp::create(locBuilder, ptrdiffCIRTy, adj, ptrdiffOne,
345 /*isLeftShift=*/false);
346
347 // Apply the adjustment to the 'this' pointer.
348 mlir::Type thisVoidPtrTy =
349 cir::PointerType::get(cir::VoidType::get(locBuilder.getContext()),
350 op.getObject().getType().getAddrSpace());
351 mlir::Value thisVoidPtr = cir::CastOp::create(
352 locBuilder, thisVoidPtrTy, cir::CastKind::bitcast, loweredObjectPtr);
353 thisArg =
354 cir::PtrStrideOp::create(locBuilder, thisVoidPtrTy, thisVoidPtr, adj);
355
356 // Load the "ptr" field of the member function pointer and determine if it
357 // points to a virtual function.
358 mlir::Value methodPtrField =
359 cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredMethod, 0);
360 mlir::Value virtualBit;
361 if (useARMMethodPtrABI)
362 virtualBit = cir::AndOp::create(locBuilder, rawAdj, ptrdiffOne);
363 else
364 virtualBit = cir::AndOp::create(locBuilder, methodPtrField, ptrdiffOne);
365 mlir::Value isVirtual = cir::CmpOp::create(locBuilder, cir::CmpOpKind::eq,
366 virtualBit, ptrdiffOne);
367
371
372 auto buildVirtualCallee = [&](mlir::OpBuilder &b, mlir::Location loc) {
373 // Load vtable pointer.
374 // Note that vtable pointer always point to the global address space.
375 auto vtablePtrTy =
376 cir::PointerType::get(cir::IntType::get(b.getContext(), 8, true));
377 auto vtablePtrPtrTy = cir::PointerType::get(
378 vtablePtrTy, op.getObject().getType().getAddrSpace());
379 auto vtablePtrPtr = cir::CastOp::create(b, loc, vtablePtrPtrTy,
380 cir::CastKind::bitcast, thisArg);
382 mlir::Value vtablePtr =
383 cir::LoadOp::create(b, loc, vtablePtrPtr, /*isDeref=*/false,
384 /*isVolatile=*/false,
385 /*isNontemporal=*/false,
386 /*alignment=*/mlir::IntegerAttr(),
387 /*sync_scope=*/cir::SyncScopeKindAttr{},
388 /*mem_order=*/cir::MemOrderAttr(),
389 /*invariant=*/false);
390
391 // Apply the offset.
392 // On ARM64, to reserve extra space in virtual member function pointers,
393 // we only pay attention to the low 32 bits of the offset.
394 mlir::Value vtableOffset = methodPtrField;
395 if (!useARMMethodPtrABI)
396 vtableOffset = cir::SubOp::create(b, loc, vtableOffset.getType(),
397 vtableOffset, ptrdiffOne);
398 if (use32BitVTableOffsetABI)
399 llvm_unreachable("AppleARM64 method ptr abi NYI");
400
404
405 // Apply the offset to the vtable pointer and get the pointer to the target
406 // virtual function. Then load that pointer to get the callee.
407 mlir::Value vfpAddr = cir::PtrStrideOp::create(locBuilder, vtablePtrTy,
408 vtablePtr, vtableOffset);
409 auto vfpPtrTy = cir::PointerType::get(calleePtrTy);
410 mlir::Value vfpPtr = cir::CastOp::create(locBuilder, vfpPtrTy,
411 cir::CastKind::bitcast, vfpAddr);
412 auto fnPtr = cir::LoadOp::create(b, loc, vfpPtr,
413 /*isDeref=*/false, /*isVolatile=*/false,
414 /*isNontemporal=*/false,
415 /*alignment=*/mlir::IntegerAttr(),
416 /*sync_scope=*/cir::SyncScopeKindAttr{},
417 /*mem_order=*/cir::MemOrderAttr(),
418 /*invariant=*/false);
419
420 cir::YieldOp::create(b, loc, fnPtr.getResult());
422 };
423
424 callee = cir::TernaryOp::create(
425 locBuilder, isVirtual, /*thenBuilder=*/buildVirtualCallee,
426 /*elseBuilder=*/
427 [&](mlir::OpBuilder &b, mlir::Location loc) {
428 auto fnPtr = cir::CastOp::create(b, loc, calleePtrTy,
429 cir::CastKind::int_to_ptr,
430 methodPtrField);
431 cir::YieldOp::create(b, loc, fnPtr.getResult());
432 })
433 .getResult();
434}
435
436static mlir::Value lowerDataMemberCast(mlir::Operation *op,
437 mlir::Value loweredSrc,
438 std::int64_t offset,
439 bool isDerivedToBase,
440 mlir::OpBuilder &builder) {
441 if (offset == 0)
442 return loweredSrc;
443 mlir::Location loc = op->getLoc();
444 mlir::Type ty = loweredSrc.getType();
445
446 auto getConstantInt = [&](int64_t value) -> cir::ConstantOp {
447 return cir::ConstantOp::create(builder, loc, cir::IntAttr::get(ty, value));
448 };
449
450 cir::ConstantOp nullValue = getConstantInt(-1);
451 auto isNull = cir::CmpOp::create(builder, loc, cir::CmpOpKind::eq, loweredSrc,
452 nullValue);
453
454 cir::ConstantOp offsetValue = getConstantInt(offset);
455 mlir::Value adjustedPtr;
456 if (isDerivedToBase) {
457 auto subOp = cir::SubOp::create(builder, loc, ty, loweredSrc, offsetValue);
458 subOp.setNoSignedWrap(true);
459 adjustedPtr = subOp;
460 } else {
461 auto addOp = cir::AddOp::create(builder, loc, ty, loweredSrc, offsetValue);
462 addOp.setNoSignedWrap(true);
463 adjustedPtr = addOp;
464 }
465
466 return cir::SelectOp::create(builder, loc, ty, isNull, loweredSrc,
467 adjustedPtr);
468}
469
470mlir::Value
471LowerItaniumCXXABI::lowerBaseDataMember(cir::BaseDataMemberOp op,
472 mlir::Value loweredSrc,
473 mlir::OpBuilder &builder) const {
474 return lowerDataMemberCast(op, loweredSrc, op.getOffset().getSExtValue(),
475 /*isDerivedToBase=*/true, builder);
476}
477
478mlir::Value
479LowerItaniumCXXABI::lowerDerivedDataMember(cir::DerivedDataMemberOp op,
480 mlir::Value loweredSrc,
481 mlir::OpBuilder &builder) const {
482 return lowerDataMemberCast(op, loweredSrc, op.getOffset().getSExtValue(),
483 /*isDerivedToBase=*/false, builder);
484}
485
486static mlir::Value lowerMethodCast(mlir::Operation *op, mlir::Value loweredSrc,
487 std::int64_t offset, bool isDerivedToBase,
488 bool useARMMethodPtrABI,
489 LowerModule &lowerMod,
490 mlir::OpBuilder &builder) {
491 if (offset == 0)
492 return loweredSrc;
493
494 // The this-adjustment is left-shifted by 1 on ARM, since the low bit of the
495 // adjustment field is used to encode whether the member function is virtual.
496 if (useARMMethodPtrABI)
497 offset <<= 1;
498
499 cir::IntType ptrdiffCIRTy = getPtrDiffCIRTy(lowerMod);
500 auto adjField = cir::ExtractMemberOp::create(builder, op->getLoc(),
501 ptrdiffCIRTy, loweredSrc, 1);
502
503 auto offsetValue = cir::ConstantOp::create(
504 builder, op->getLoc(), cir::IntAttr::get(ptrdiffCIRTy, offset));
505 mlir::Value adjustedAdjField;
506 if (isDerivedToBase) {
507 auto subOp = cir::SubOp::create(builder, op->getLoc(), ptrdiffCIRTy,
508 adjField, offsetValue);
509 subOp.setNoSignedWrap(true);
510 adjustedAdjField = subOp;
511 } else {
512 auto addOp = cir::AddOp::create(builder, op->getLoc(), ptrdiffCIRTy,
513 adjField, offsetValue);
514 addOp.setNoSignedWrap(true);
515 adjustedAdjField = addOp;
516 }
517
518 return cir::InsertMemberOp::create(builder, op->getLoc(), loweredSrc, 1,
519 adjustedAdjField);
520}
521
522mlir::Value
523LowerItaniumCXXABI::lowerBaseMethod(cir::BaseMethodOp op,
524 mlir::Value loweredSrc,
525 mlir::OpBuilder &builder) const {
526 return lowerMethodCast(op, loweredSrc, op.getOffset().getSExtValue(),
527 /*isDerivedToBase=*/true, useARMMethodPtrABI, lm,
528 builder);
529}
530
531mlir::Value
532LowerItaniumCXXABI::lowerDerivedMethod(cir::DerivedMethodOp op,
533 mlir::Value loweredSrc,
534 mlir::OpBuilder &builder) const {
535 return lowerMethodCast(op, loweredSrc, op.getOffset().getSExtValue(),
536 /*isDerivedToBase=*/false, useARMMethodPtrABI, lm,
537 builder);
538}
539
540mlir::Value
541LowerItaniumCXXABI::lowerDataMemberCmp(cir::CmpOp op, mlir::Value loweredLhs,
542 mlir::Value loweredRhs,
543 mlir::OpBuilder &builder) const {
544 return cir::CmpOp::create(builder, op.getLoc(), op.getKind(), loweredLhs,
545 loweredRhs);
546}
547
548mlir::Value LowerItaniumCXXABI::lowerMethodCmp(cir::CmpOp op,
549 mlir::Value loweredLhs,
550 mlir::Value loweredRhs,
551 mlir::OpBuilder &builder) const {
552 assert(op.getKind() == cir::CmpOpKind::eq ||
553 op.getKind() == cir::CmpOpKind::ne);
554
555 mlir::ImplicitLocOpBuilder locBuilder(op.getLoc(), builder);
556 cir::IntType ptrdiffCIRTy = getPtrDiffCIRTy(lm);
557 mlir::Value ptrdiffZero =
558 cir::ConstantOp::create(locBuilder, cir::IntAttr::get(ptrdiffCIRTy, 0));
559
560 mlir::Value lhsPtrField =
561 cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredLhs, 0);
562 mlir::Value rhsPtrField =
563 cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredRhs, 0);
564 mlir::Value ptrCmp =
565 cir::CmpOp::create(locBuilder, op.getKind(), lhsPtrField, rhsPtrField);
566 mlir::Value ptrCmpToNull =
567 cir::CmpOp::create(locBuilder, op.getKind(), lhsPtrField, ptrdiffZero);
568
569 mlir::Value lhsAdjField =
570 cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredLhs, 1);
571 mlir::Value rhsAdjField =
572 cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredRhs, 1);
573 mlir::Value adjCmp =
574 cir::CmpOp::create(locBuilder, op.getKind(), lhsAdjField, rhsAdjField);
575
576 auto create_and = [&](mlir::Value lhs, mlir::Value rhs) {
577 return cir::AndOp::create(locBuilder, lhs.getType(), lhs, rhs);
578 };
579 auto create_or = [&](mlir::Value lhs, mlir::Value rhs) {
580 return cir::OrOp::create(locBuilder, lhs.getType(), lhs, rhs);
581 };
582
583 // Null member function pointers on ARM clear the low bit of Adj,
584 // so the zero condition has to check that neither low bit is set.
585 if (useARMMethodPtrABI) {
586 mlir::Value one =
587 cir::ConstantOp::create(locBuilder, cir::IntAttr::get(ptrdiffCIRTy, 1));
588
589 // The low bit of the adjustment field is used to encode whether the member
590 // function is virtual, but the ARM ABI specifies that for null pointers
591 // this bit must be clear. Therefore, to test whether the member pointer is
592 // null, we need to check that bit.
593 //
594 // If we are performing an equality check, ptrCmpToNull indicates that both
595 // pointers are null (if they are equal -- we only actually test lhs).
596 // If we are performing an inequality check, ptrCmpToNull indicates that
597 // one of the pointers is not null.
598 //
599 // To apply the ARM-specific logic, if either virtual bit is set, they
600 // cannot both be null (equality case -- ptrCmpToNull &= orAdjAnd1CmpZero),
601 // and if either virtual bit is set, one of the pointers is not null
602 // (inequality case -- ptrCmpToNull |= orAdjAnd1CmpZero).
603 mlir::Value orAdj = create_or(lhsAdjField, rhsAdjField);
604 mlir::Value orAdjAnd1 = create_and(orAdj, one);
605 mlir::Value orAdjAnd1CmpZero =
606 cir::CmpOp::create(locBuilder, op.getKind(), orAdjAnd1, ptrdiffZero);
607
608 if (op.getKind() == cir::CmpOpKind::eq)
609 ptrCmpToNull = create_and(ptrCmpToNull, orAdjAnd1CmpZero);
610 else
611 ptrCmpToNull = create_or(ptrCmpToNull, orAdjAnd1CmpZero);
612 }
613
614 mlir::Value result;
615 if (op.getKind() == cir::CmpOpKind::eq) {
616 // (lhs.ptr == null || lhs.adj == rhs.adj) && lhs.ptr == rhs.ptr
617 result = create_and(ptrCmp, create_or(ptrCmpToNull, adjCmp));
618 } else {
619 // lhs.ptr == rhs.ptr && (lhs.ptr == null || lhs.adj == rhs.adj)
620 result = create_or(ptrCmp, create_and(ptrCmpToNull, adjCmp));
621 }
622
623 return result;
624}
625
626mlir::Value LowerItaniumCXXABI::lowerDataMemberBitcast(
627 cir::CastOp op, mlir::Type loweredDstTy, mlir::Value loweredSrc,
628 mlir::OpBuilder &builder) const {
629 if (loweredSrc.getType() == loweredDstTy)
630 return loweredSrc;
631
632 return cir::CastOp::create(builder, op.getLoc(), loweredDstTy,
633 cir::CastKind::bitcast, loweredSrc);
634}
635
636mlir::Value LowerItaniumCXXABI::lowerDataMemberToBoolCast(
637 cir::CastOp op, mlir::Value loweredSrc, mlir::OpBuilder &builder) const {
638 // Itanium C++ ABI 2.3:
639 // A NULL pointer is represented as -1.
640 auto nullAttr = cir::IntAttr::get(getPtrDiffCIRTy(lm), -1);
641 auto nullValue = cir::ConstantOp::create(builder, op.getLoc(), nullAttr);
642 return cir::CmpOp::create(builder, op.getLoc(), cir::CmpOpKind::ne,
643 loweredSrc, nullValue);
644}
645
646mlir::Value
647LowerItaniumCXXABI::lowerMethodBitcast(cir::CastOp op, mlir::Type loweredDstTy,
648 mlir::Value loweredSrc,
649 mlir::OpBuilder &builder) const {
650 if (loweredSrc.getType() == loweredDstTy)
651 return loweredSrc;
652
653 return loweredSrc;
654}
655
656mlir::Value LowerItaniumCXXABI::lowerMethodToBoolCast(
657 cir::CastOp op, mlir::Value loweredSrc, mlir::OpBuilder &builder) const {
658 mlir::ImplicitLocOpBuilder locBuilder(op.getLoc(), builder);
659
660 // Itanium C++ ABI 2.3.2:
661 //
662 // In the standard representation, a null member function pointer is
663 // represented with ptr set to a null pointer. The value of adj is
664 // unspecified for null member function pointers.
665 cir::IntType ptrdiffCIRTy = getPtrDiffCIRTy(lm);
666 mlir::Value ptrdiffZero =
667 cir::ConstantOp::create(locBuilder, cir::IntAttr::get(ptrdiffCIRTy, 0));
668 mlir::Value ptrField =
669 cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredSrc, 0);
670
671 mlir::Value result =
672 cir::CmpOp::create(locBuilder, cir::CmpOpKind::ne, ptrField, ptrdiffZero);
673
674 // On ARM, a member function pointer is also non-null if the low bit of 'adj'
675 // (the virtual bit) is set.
676 if (useARMMethodPtrABI) {
677 mlir::Value one =
678 cir::ConstantOp::create(locBuilder, cir::IntAttr::get(ptrdiffCIRTy, 1));
679 mlir::Value adj =
680 cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredSrc, 1);
681 mlir::Value virtualBit =
682 cir::AndOp::create(locBuilder, ptrdiffCIRTy, adj, one);
683 mlir::Value isVirtual = cir::CmpOp::create(locBuilder, cir::CmpOpKind::ne,
684 virtualBit, ptrdiffZero);
685 result = cir::OrOp::create(locBuilder, result, isVirtual);
686 }
687
688 return result;
689}
690
691static void buildBadCastCall(mlir::OpBuilder &builder, mlir::Location loc,
692 mlir::FlatSymbolRefAttr badCastFuncRef) {
693 auto callOp = cir::CallOp::create(builder, loc, badCastFuncRef,
694 /*resType=*/cir::VoidType(),
695 /*operands=*/mlir::ValueRange{});
696 callOp->setAttr(cir::CIRDialect::getNoReturnAttrName(),
697 builder.getUnitAttr());
698
699 cir::UnreachableOp::create(builder, loc);
700 builder.clearInsertionPoint();
701}
702
703static mlir::Value buildDynamicCastAfterNullCheck(cir::DynamicCastOp op,
704 mlir::OpBuilder &builder) {
705 mlir::Location loc = op->getLoc();
706 mlir::Value srcValue = op.getSrc();
707 cir::DynamicCastInfoAttr castInfo = op.getInfo().value();
708
709 // TODO(cir): consider address space
711
712 auto voidPtrTy =
713 cir::PointerType::get(cir::VoidType::get(builder.getContext()));
714
715 mlir::Value srcPtr = cir::CastOp::create(builder, loc, voidPtrTy,
716 cir::CastKind::bitcast, srcValue);
717 mlir::Value srcRtti =
718 cir::ConstantOp::create(builder, loc, castInfo.getSrcRtti());
719 mlir::Value destRtti =
720 cir::ConstantOp::create(builder, loc, castInfo.getDestRtti());
721 mlir::Value offsetHint =
722 cir::ConstantOp::create(builder, loc, castInfo.getOffsetHint());
723
724 mlir::FlatSymbolRefAttr dynCastFuncRef = castInfo.getRuntimeFunc();
725 mlir::Value dynCastFuncArgs[4] = {srcPtr, srcRtti, destRtti, offsetHint};
726
727 mlir::Value castedPtr = cir::CallOp::create(builder, loc, dynCastFuncRef,
728 voidPtrTy, dynCastFuncArgs)
729 .getResult();
730
731 assert(mlir::isa<cir::PointerType>(castedPtr.getType()) &&
732 "the return value of __dynamic_cast should be a ptr");
733
734 /// C++ [expr.dynamic.cast]p9:
735 /// A failed cast to reference type throws std::bad_cast
736 if (op.isRefCast()) {
737 // Emit a cir.if that checks the casted value.
738 mlir::Value null = cir::ConstantOp::create(
739 builder, loc,
740 cir::ConstPtrAttr::get(castedPtr.getType(),
741 builder.getI64IntegerAttr(0)));
742 mlir::Value castedPtrIsNull =
743 cir::CmpOp::create(builder, loc, cir::CmpOpKind::eq, castedPtr, null);
744 cir::IfOp::create(builder, loc, castedPtrIsNull, false,
745 [&](mlir::OpBuilder &, mlir::Location) {
746 buildBadCastCall(builder, loc,
747 castInfo.getBadCastFunc());
748 });
749 }
750
751 // Note that castedPtr is a void*. Cast it to a pointer to the destination
752 // type before return.
753 return cir::CastOp::create(builder, loc, op.getType(), cir::CastKind::bitcast,
754 castedPtr);
755}
756
758 cir::DynamicCastOp op, cir::LowerModule &lm, mlir::OpBuilder &builder) {
759 mlir::Location loc = op.getLoc();
760 bool vtableUsesRelativeLayout = op.getRelativeLayout();
761
762 // TODO(cir): consider address space in this function.
764
765 mlir::Type vtableElemTy;
766 uint64_t vtableElemAlign;
767 if (vtableUsesRelativeLayout) {
768 vtableElemTy =
769 cir::IntType::get(builder.getContext(), 32, /*isSigned=*/true);
770 vtableElemAlign = 4;
771 } else {
772 vtableElemTy = getPtrDiffCIRTy(lm);
773 vtableElemAlign = llvm::divideCeil(
775 }
776
777 mlir::Type vtableElemPtrTy = cir::PointerType::get(vtableElemTy);
778 mlir::Type i64Ty = cir::IntType::get(builder.getContext(), /*width=*/64,
779 /*isSigned=*/true);
780
781 // Access vtable to get the offset from the given object to its containing
782 // complete object.
783 // TODO: Add a specialized operation to get the object offset?
784 auto vptrPtr = cir::VTableGetVPtrOp::create(builder, loc, op.getSrc());
785 mlir::Value vptr = cir::LoadOp::create(
786 builder, loc, vptrPtr,
787 /*isDeref=*/false,
788 /*is_volatile=*/false,
789 /*isNontemporal=*/false,
790 /*alignment=*/builder.getI64IntegerAttr(vtableElemAlign),
791 /*sync_scope=*/cir::SyncScopeKindAttr(),
792 /*mem_order=*/cir::MemOrderAttr(),
793 /*invariant=*/false);
794 mlir::Value elementPtr = cir::CastOp::create(builder, loc, vtableElemPtrTy,
795 cir::CastKind::bitcast, vptr);
796 mlir::Value minusTwo =
797 cir::ConstantOp::create(builder, loc, cir::IntAttr::get(i64Ty, -2));
798 mlir::Value offsetToTopSlotPtr = cir::PtrStrideOp::create(
799 builder, loc, vtableElemPtrTy, elementPtr, minusTwo);
800 mlir::Value offsetToTop = cir::LoadOp::create(
801 builder, loc, offsetToTopSlotPtr,
802 /*isDeref=*/false,
803 /*is_volatile=*/false,
804 /*isNontemporal=*/false,
805 /*alignment=*/builder.getI64IntegerAttr(vtableElemAlign),
806 /*sync_scope=*/cir::SyncScopeKindAttr(),
807 /*mem_order=*/cir::MemOrderAttr(),
808 /*invariant=*/false);
809
810 auto voidPtrTy =
811 cir::PointerType::get(cir::VoidType::get(builder.getContext()));
812
813 // Add the offset to the given pointer to get the cast result.
814 // Cast the input pointer to a uint8_t* to allow pointer arithmetic.
815 mlir::Type u8PtrTy =
816 cir::PointerType::get(cir::IntType::get(builder.getContext(), /*width=*/8,
817 /*isSigned=*/false));
818 mlir::Value srcBytePtr = cir::CastOp::create(
819 builder, loc, u8PtrTy, cir::CastKind::bitcast, op.getSrc());
820 auto dstBytePtr =
821 cir::PtrStrideOp::create(builder, loc, u8PtrTy, srcBytePtr, offsetToTop);
822 // Cast the result to a void*.
823 return cir::CastOp::create(builder, loc, voidPtrTy, cir::CastKind::bitcast,
824 dstBytePtr);
825}
826
827mlir::Value
828LowerItaniumCXXABI::lowerDynamicCast(cir::DynamicCastOp op,
829 mlir::OpBuilder &builder) const {
830 mlir::Location loc = op->getLoc();
831 mlir::Value srcValue = op.getSrc();
832
834
835 if (op.isRefCast())
836 return buildDynamicCastAfterNullCheck(op, builder);
837
838 mlir::Value srcValueIsNotNull = cir::CastOp::create(
839 builder, loc, cir::BoolType::get(builder.getContext()),
840 cir::CastKind::ptr_to_bool, srcValue);
841 return cir::TernaryOp::create(
842 builder, loc, srcValueIsNotNull,
843 [&](mlir::OpBuilder &, mlir::Location) {
844 mlir::Value castedValue =
845 op.isCastToVoid()
846 ? buildDynamicCastToVoidAfterNullCheck(op, lm, builder)
847 : buildDynamicCastAfterNullCheck(op, builder);
848 cir::YieldOp::create(builder, loc, castedValue);
849 },
850 [&](mlir::OpBuilder &, mlir::Location) {
851 mlir::Value null = cir::ConstantOp::create(
852 builder, loc,
853 cir::ConstPtrAttr::get(op.getType(),
854 builder.getI64IntegerAttr(0)));
855 cir::YieldOp::create(builder, loc, null);
856 })
857 .getResult();
858}
859mlir::Value
860LowerItaniumCXXABI::lowerVTableGetTypeInfo(cir::VTableGetTypeInfoOp op,
861 mlir::OpBuilder &builder) const {
862 mlir::Location loc = op->getLoc();
863 auto offset = cir::ConstantOp::create(
864 builder, op->getLoc(), cir::IntAttr::get(getPtrDiffCIRTy(lm), -1));
865
866 // Cast the vptr to type_info-ptr, so that we can go backwards 1 pointer.
867 auto vptrCast = cir::CastOp::create(builder, loc, op.getType(),
868 cir::CastKind::bitcast, op.getVptr());
869
870 return cir::PtrStrideOp::create(builder, loc, vptrCast.getType(), vptrCast,
871 offset)
872 .getResult();
873}
874
875clang::CharUnits LowerItaniumCXXABI::getArrayCookieSizeImpl(
876 mlir::Type elementType, const mlir::DataLayout &dataLayout) const {
877 // The array cookie is a size_t; pad that up to the element alignment.
878 // The cookie is actually right-justified in that space.
879 clang::CharUnits sizeOfSizeT =
880 clang::CharUnits::fromQuantity(getPtrSizeInBits() / 8);
881 clang::CharUnits eltAlign = clang::CharUnits::fromQuantity(
882 dataLayout.getTypePreferredAlignment(elementType));
883 return std::max(sizeOfSizeT, eltAlign);
884}
885
886mlir::Value LowerItaniumCXXABI::readArrayCookieImpl(
887 mlir::Location loc, mlir::Value allocPtr, clang::CharUnits cookieSize,
888 clang::CharUnits cookieAlignment, const mlir::DataLayout &dataLayout,
889 CIRBaseBuilderTy &builder) const {
890 unsigned ptrSizeInBits = getPtrSizeInBits();
891 auto u8PtrTy = builder.getPointerTo(builder.getUIntNTy(8));
892 auto ptrDiffTy = builder.getSIntNTy(ptrSizeInBits);
893 auto sizeTy = builder.getUIntNTy(ptrSizeInBits);
894
895 // The element count is right-justified in the cookie.
896 clang::CharUnits sizeOfSizeT =
897 clang::CharUnits::fromQuantity(ptrSizeInBits / 8);
898 clang::CharUnits countOffset = cookieSize - sizeOfSizeT;
899
900 mlir::Value countBytePtr = allocPtr;
901 clang::CharUnits countAlignment = cookieAlignment;
902 if (!countOffset.isZero()) {
903 mlir::Value offsetVal = cir::ConstantOp::create(
904 builder, loc, cir::IntAttr::get(ptrDiffTy, countOffset.getQuantity()));
905 countBytePtr =
906 cir::PtrStrideOp::create(builder, loc, u8PtrTy, allocPtr, offsetVal);
907 countAlignment = cookieAlignment.alignmentAtOffset(countOffset);
908 }
909
910 auto countPtrTy = cir::PointerType::get(sizeTy);
911 mlir::Value countPtr = cir::CastOp::create(
912 builder, loc, countPtrTy, cir::CastKind::bitcast, countBytePtr);
913 return cir::LoadOp::create(
914 builder, loc, countPtr, /*isDeref=*/false, /*isVolatile=*/false,
915 /*isNontemporal=*/false,
916 builder.getI64IntegerAttr(countAlignment.getQuantity()),
917 cir::SyncScopeKindAttr(), cir::MemOrderAttr(),
918 /*invariant=*/false);
919}
920
921} // namespace cir
cir::PointerType getPointerTo(mlir::Type ty)
cir::IntType getUIntNTy(int n)
cir::IntType getSIntNTy(int n)
mlir::MLIRContext * getMLIRContext()
Definition LowerModule.h:48
clang::TargetCXXABI::Kind getCXXABIKind() const
Definition LowerModule.h:41
const clang::TargetInfo & getTarget() const
Definition LowerModule.h:47
CharUnits alignmentAtOffset(CharUnits offset) const
Given that this is a non-zero alignment value, what is the alignment at the given offset?
Definition CharUnits.h:207
bool isZero() const
isZero - Test whether the quantity equals zero.
Definition CharUnits.h:122
QuantityType getQuantity() const
getQuantity - Get the raw integer representation of this quantity.
Definition CharUnits.h:185
static CharUnits fromQuantity(QuantityType Quantity)
fromQuantity - Construct a CharUnits quantity from a raw integer type.
Definition CharUnits.h:63
Exposes information about the current target.
Definition TargetInfo.h:227
unsigned getTypeWidth(IntType T) const
Return the width (in bits) of the specified integer type enum.
static bool isTypeSigned(IntType T)
Returns true if the type is signed; false otherwise.
IntType getPtrDiffType(LangAS AddrSpace) const
Definition TargetInfo.h:408
uint64_t getPointerAlign(LangAS AddrSpace) const
Definition TargetInfo.h:494
static mlir::Value lowerDataMemberCast(mlir::Operation *op, mlir::Value loweredSrc, std::int64_t offset, bool isDerivedToBase, mlir::OpBuilder &builder)
std::unique_ptr< CIRCXXABI > createItaniumCXXABI(LowerModule &lm)
Creates an Itanium-family ABI.
static mlir::Value buildDynamicCastAfterNullCheck(cir::DynamicCastOp op, mlir::OpBuilder &builder)
static cir::IntType getPtrDiffCIRTy(LowerModule &lm)
static mlir::Value buildDynamicCastToVoidAfterNullCheck(cir::DynamicCastOp op, cir::LowerModule &lm, mlir::OpBuilder &builder)
static mlir::Value lowerMethodCast(mlir::Operation *op, mlir::Value loweredSrc, std::int64_t offset, bool isDerivedToBase, bool useARMMethodPtrABI, LowerModule &lowerMod, mlir::OpBuilder &builder)
static void buildBadCastCall(mlir::OpBuilder &builder, mlir::Location loc, mlir::FlatSymbolRefAttr badCastFuncRef)
const internal::VariadicAllOfMatcher< Attr > attr
const internal::VariadicAllOfMatcher< Type > type
Matches Types in the clang AST.
long int64_t
__packed_splat4 __packed_splat2 __packed_splat8 __packed_splat4 int32_t
static bool addressSpace()
static bool appleArm64CXXABI()
static bool emitCFICheck()
static bool emitVFEInfo()
static bool emitWPDInfo()
static bool emitTypeCheck()
static bool pointerAuthentication()
IntType
===-— Target Data Type Query Methods ----------------------------—===//
Definition TargetInfo.h:147