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::RecordType::get(type.getContext(), {ptrdiffCIRTy, ptrdiffCIRTy},
190 /*packed=*/false, /*padded=*/false,
191 cir::RecordType::Struct);
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 unsigned memberIndex = attr.getMemberIndex().value();
207 memberOffset =
208 attr.getType().getClassTy().getElementOffset(layout, memberIndex);
209 }
210
211 mlir::Type abiTy = lowerDataMemberType(attr.getType(), typeConverter);
212 return cir::IntAttr::get(abiTy, memberOffset);
213}
214
215mlir::TypedAttr LowerItaniumCXXABI::lowerMethodConstant(
216 cir::MethodAttr attr, const mlir::DataLayout &layout,
217 const mlir::TypeConverter &typeConverter) const {
218 cir::IntType ptrdiffCIRTy = getPtrDiffCIRTy(lm);
219
220 // lowerMethodType returns the CIR type used to represent the method pointer
221 // in an ABI-specific way. That's why lowerMethodType returns cir::RecordType
222 // here.
223 auto loweredMethodTy = mlir::cast<cir::RecordType>(
224 lowerMethodType(attr.getType(), typeConverter));
225
226 auto zero = cir::IntAttr::get(ptrdiffCIRTy, 0);
227
228 // Itanium C++ ABI 2.3.2:
229 // In all representations, the basic ABI properties of member function
230 // pointer types are those of the following class, where fnptr_t is the
231 // appropriate function-pointer type for a member function of this type:
232 //
233 // struct {
234 // fnptr_t ptr;
235 // ptrdiff_t adj;
236 // };
237
238 if (attr.isNull()) {
239 // Itanium C++ ABI 2.3.2:
240 //
241 // In the standard representation, a null member function pointer is
242 // represented with ptr set to a null pointer. The value of adj is
243 // unspecified for null member function pointers.
244 //
245 // clang CodeGen emits struct{null, null} for null member function pointers.
246 // Let's do the same here.
247 return cir::ConstRecordAttr::get(
248 loweredMethodTy, mlir::ArrayAttr::get(attr.getContext(), {zero, zero}));
249 }
250
251 if (attr.isVirtual()) {
252 if (useARMMethodPtrABI) {
253 // ARM C++ ABI 3.2.1:
254 // This ABI specifies that adj contains twice the this
255 // adjustment, plus 1 if the member function is virtual. The
256 // least significant bit of adj then makes exactly the same
257 // discrimination as the least significant bit of ptr does for
258 // Itanium.
260 auto ptr =
261 cir::IntAttr::get(ptrdiffCIRTy, attr.getVtableOffset().value());
262 auto one = cir::IntAttr::get(ptrdiffCIRTy, 1);
263 return cir::ConstRecordAttr::get(
264 loweredMethodTy, mlir::ArrayAttr::get(attr.getContext(), {ptr, one}));
265 }
266
267 // Itanium C++ ABI 2.3.2:
268 //
269 // In the standard representation, a member function pointer for a
270 // virtual function is represented with ptr set to 1 plus the function's
271 // v-table entry offset (in bytes), converted to a function pointer as if
272 // by reinterpret_cast<fnptr_t>(uintfnptr_t(1 + offset)), where
273 // uintfnptr_t is an unsigned integer of the same size as fnptr_t.
274 auto ptr =
275 cir::IntAttr::get(ptrdiffCIRTy, 1 + attr.getVtableOffset().value());
276 return cir::ConstRecordAttr::get(
277 loweredMethodTy, mlir::ArrayAttr::get(attr.getContext(), {ptr, zero}));
278 }
279
280 // Itanium C++ ABI 2.3.2:
281 //
282 // A member function pointer for a non-virtual member function is
283 // represented with ptr set to a pointer to the function, using the base
284 // ABI's representation of function pointers.
285 auto ptr = cir::GlobalViewAttr::get(ptrdiffCIRTy, attr.getSymbol().value());
286 return cir::ConstRecordAttr::get(
287 loweredMethodTy, mlir::ArrayAttr::get(attr.getContext(), {ptr, zero}));
288}
289
290mlir::Operation *LowerItaniumCXXABI::lowerGetRuntimeMember(
291 cir::GetRuntimeMemberOp op, mlir::Type loweredResultTy,
292 mlir::Value loweredAddr, mlir::Value loweredMember,
293 mlir::OpBuilder &builder) const {
294 auto byteTy = cir::IntType::get(op.getContext(), 8, true);
295 auto bytePtrTy = cir::PointerType::get(
296 byteTy,
297 mlir::cast<cir::PointerType>(op.getAddr().getType()).getAddrSpace());
298 auto objectBytesPtr = cir::CastOp::create(
299 builder, op.getLoc(), bytePtrTy, cir::CastKind::bitcast, op.getAddr());
300 auto memberBytesPtr = cir::PtrStrideOp::create(
301 builder, op.getLoc(), bytePtrTy, objectBytesPtr, loweredMember);
302 return cir::CastOp::create(builder, op.getLoc(), op.getType(),
303 cir::CastKind::bitcast, memberBytesPtr);
304}
305
306void LowerItaniumCXXABI::lowerGetMethod(
307 cir::GetMethodOp op, mlir::Value &callee, mlir::Value &thisArg,
308 mlir::Value loweredMethod, mlir::Value loweredObjectPtr,
309 mlir::ConversionPatternRewriter &rewriter) const {
310 // In the Itanium and ARM ABIs, method pointers have the form:
311 // struct { ptrdiff_t ptr; ptrdiff_t adj; } memptr;
312 //
313 // In the Itanium ABI:
314 // - method pointers are virtual if (memptr.ptr & 1) is nonzero
315 // - the this-adjustment is (memptr.adj)
316 // - the virtual offset is (memptr.ptr - 1)
317 //
318 // In the ARM ABI:
319 // - method pointers are virtual if (memptr.adj & 1) is nonzero
320 // - the this-adjustment is (memptr.adj >> 1)
321 // - the virtual offset is (memptr.ptr)
322 // ARM uses 'adj' for the virtual flag because Thumb functions
323 // may be only single-byte aligned.
324 //
325 // If the member is virtual, the adjusted 'this' pointer points
326 // to a vtable pointer from which the virtual offset is applied.
327 //
328 // If the member is non-virtual, memptr.ptr is the address of
329 // the function to call.
330
331 mlir::ImplicitLocOpBuilder locBuilder(op.getLoc(), rewriter);
332 mlir::Type calleePtrTy = op.getCallee().getType();
333
334 cir::IntType ptrdiffCIRTy = getPtrDiffCIRTy(lm);
335 mlir::Value ptrdiffOne =
336 cir::ConstantOp::create(locBuilder, cir::IntAttr::get(ptrdiffCIRTy, 1));
337
338 mlir::Value rawAdj =
339 cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredMethod, 1);
340 mlir::Value adj = rawAdj;
341 if (useARMMethodPtrABI)
342 adj = cir::ShiftOp::create(locBuilder, ptrdiffCIRTy, adj, ptrdiffOne,
343 /*isLeftShift=*/false);
344
345 // Apply the adjustment to the 'this' pointer.
346 mlir::Type thisVoidPtrTy =
347 cir::PointerType::get(cir::VoidType::get(locBuilder.getContext()),
348 op.getObject().getType().getAddrSpace());
349 mlir::Value thisVoidPtr = cir::CastOp::create(
350 locBuilder, thisVoidPtrTy, cir::CastKind::bitcast, loweredObjectPtr);
351 thisArg =
352 cir::PtrStrideOp::create(locBuilder, thisVoidPtrTy, thisVoidPtr, adj);
353
354 // Load the "ptr" field of the member function pointer and determine if it
355 // points to a virtual function.
356 mlir::Value methodPtrField =
357 cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredMethod, 0);
358 mlir::Value virtualBit;
359 if (useARMMethodPtrABI)
360 virtualBit = cir::AndOp::create(locBuilder, rawAdj, ptrdiffOne);
361 else
362 virtualBit = cir::AndOp::create(locBuilder, methodPtrField, ptrdiffOne);
363 mlir::Value isVirtual = cir::CmpOp::create(locBuilder, cir::CmpOpKind::eq,
364 virtualBit, ptrdiffOne);
365
369
370 auto buildVirtualCallee = [&](mlir::OpBuilder &b, mlir::Location loc) {
371 // Load vtable pointer.
372 // Note that vtable pointer always point to the global address space.
373 auto vtablePtrTy =
374 cir::PointerType::get(cir::IntType::get(b.getContext(), 8, true));
375 auto vtablePtrPtrTy = cir::PointerType::get(
376 vtablePtrTy, op.getObject().getType().getAddrSpace());
377 auto vtablePtrPtr = cir::CastOp::create(b, loc, vtablePtrPtrTy,
378 cir::CastKind::bitcast, thisArg);
380 mlir::Value vtablePtr =
381 cir::LoadOp::create(b, loc, vtablePtrPtr, /*isDeref=*/false,
382 /*isVolatile=*/false,
383 /*alignment=*/mlir::IntegerAttr(),
384 /*sync_scope=*/cir::SyncScopeKindAttr{},
385 /*mem_order=*/cir::MemOrderAttr());
386
387 // Apply the offset.
388 // On ARM64, to reserve extra space in virtual member function pointers,
389 // we only pay attention to the low 32 bits of the offset.
390 mlir::Value vtableOffset = methodPtrField;
391 if (!useARMMethodPtrABI)
392 vtableOffset = cir::SubOp::create(b, loc, vtableOffset.getType(),
393 vtableOffset, ptrdiffOne);
394 if (use32BitVTableOffsetABI)
395 llvm_unreachable("AppleARM64 method ptr abi NYI");
396
400
401 // Apply the offset to the vtable pointer and get the pointer to the target
402 // virtual function. Then load that pointer to get the callee.
403 mlir::Value vfpAddr = cir::PtrStrideOp::create(locBuilder, vtablePtrTy,
404 vtablePtr, vtableOffset);
405 auto vfpPtrTy = cir::PointerType::get(calleePtrTy);
406 mlir::Value vfpPtr = cir::CastOp::create(locBuilder, vfpPtrTy,
407 cir::CastKind::bitcast, vfpAddr);
408 auto fnPtr = cir::LoadOp::create(b, loc, vfpPtr,
409 /*isDeref=*/false, /*isVolatile=*/false,
410 /*alignment=*/mlir::IntegerAttr(),
411 /*sync_scope=*/cir::SyncScopeKindAttr{},
412 /*mem_order=*/cir::MemOrderAttr());
413
414 cir::YieldOp::create(b, loc, fnPtr.getResult());
416 };
417
418 callee = cir::TernaryOp::create(
419 locBuilder, isVirtual, /*thenBuilder=*/buildVirtualCallee,
420 /*elseBuilder=*/
421 [&](mlir::OpBuilder &b, mlir::Location loc) {
422 auto fnPtr = cir::CastOp::create(b, loc, calleePtrTy,
423 cir::CastKind::int_to_ptr,
424 methodPtrField);
425 cir::YieldOp::create(b, loc, fnPtr.getResult());
426 })
427 .getResult();
428}
429
430static mlir::Value lowerDataMemberCast(mlir::Operation *op,
431 mlir::Value loweredSrc,
432 std::int64_t offset,
433 bool isDerivedToBase,
434 mlir::OpBuilder &builder) {
435 if (offset == 0)
436 return loweredSrc;
437 mlir::Location loc = op->getLoc();
438 mlir::Type ty = loweredSrc.getType();
439
440 auto getConstantInt = [&](int64_t value) -> cir::ConstantOp {
441 return cir::ConstantOp::create(builder, loc, cir::IntAttr::get(ty, value));
442 };
443
444 cir::ConstantOp nullValue = getConstantInt(-1);
445 auto isNull = cir::CmpOp::create(builder, loc, cir::CmpOpKind::eq, loweredSrc,
446 nullValue);
447
448 cir::ConstantOp offsetValue = getConstantInt(offset);
449 mlir::Value adjustedPtr;
450 if (isDerivedToBase) {
451 auto subOp = cir::SubOp::create(builder, loc, ty, loweredSrc, offsetValue);
452 subOp.setNoSignedWrap(true);
453 adjustedPtr = subOp;
454 } else {
455 auto addOp = cir::AddOp::create(builder, loc, ty, loweredSrc, offsetValue);
456 addOp.setNoSignedWrap(true);
457 adjustedPtr = addOp;
458 }
459
460 return cir::SelectOp::create(builder, loc, ty, isNull, loweredSrc,
461 adjustedPtr);
462}
463
464mlir::Value
465LowerItaniumCXXABI::lowerBaseDataMember(cir::BaseDataMemberOp op,
466 mlir::Value loweredSrc,
467 mlir::OpBuilder &builder) const {
468 return lowerDataMemberCast(op, loweredSrc, op.getOffset().getSExtValue(),
469 /*isDerivedToBase=*/true, builder);
470}
471
472mlir::Value
473LowerItaniumCXXABI::lowerDerivedDataMember(cir::DerivedDataMemberOp op,
474 mlir::Value loweredSrc,
475 mlir::OpBuilder &builder) const {
476 return lowerDataMemberCast(op, loweredSrc, op.getOffset().getSExtValue(),
477 /*isDerivedToBase=*/false, builder);
478}
479
480static mlir::Value lowerMethodCast(mlir::Operation *op, mlir::Value loweredSrc,
481 std::int64_t offset, bool isDerivedToBase,
482 bool useARMMethodPtrABI,
483 LowerModule &lowerMod,
484 mlir::OpBuilder &builder) {
485 if (offset == 0)
486 return loweredSrc;
487
488 // The this-adjustment is left-shifted by 1 on ARM, since the low bit of the
489 // adjustment field is used to encode whether the member function is virtual.
490 if (useARMMethodPtrABI)
491 offset <<= 1;
492
493 cir::IntType ptrdiffCIRTy = getPtrDiffCIRTy(lowerMod);
494 auto adjField = cir::ExtractMemberOp::create(builder, op->getLoc(),
495 ptrdiffCIRTy, loweredSrc, 1);
496
497 auto offsetValue = cir::ConstantOp::create(
498 builder, op->getLoc(), cir::IntAttr::get(ptrdiffCIRTy, offset));
499 mlir::Value adjustedAdjField;
500 if (isDerivedToBase) {
501 auto subOp = cir::SubOp::create(builder, op->getLoc(), ptrdiffCIRTy,
502 adjField, offsetValue);
503 subOp.setNoSignedWrap(true);
504 adjustedAdjField = subOp;
505 } else {
506 auto addOp = cir::AddOp::create(builder, op->getLoc(), ptrdiffCIRTy,
507 adjField, offsetValue);
508 addOp.setNoSignedWrap(true);
509 adjustedAdjField = addOp;
510 }
511
512 return cir::InsertMemberOp::create(builder, op->getLoc(), loweredSrc, 1,
513 adjustedAdjField);
514}
515
516mlir::Value
517LowerItaniumCXXABI::lowerBaseMethod(cir::BaseMethodOp op,
518 mlir::Value loweredSrc,
519 mlir::OpBuilder &builder) const {
520 return lowerMethodCast(op, loweredSrc, op.getOffset().getSExtValue(),
521 /*isDerivedToBase=*/true, useARMMethodPtrABI, lm,
522 builder);
523}
524
525mlir::Value
526LowerItaniumCXXABI::lowerDerivedMethod(cir::DerivedMethodOp op,
527 mlir::Value loweredSrc,
528 mlir::OpBuilder &builder) const {
529 return lowerMethodCast(op, loweredSrc, op.getOffset().getSExtValue(),
530 /*isDerivedToBase=*/false, useARMMethodPtrABI, lm,
531 builder);
532}
533
534mlir::Value
535LowerItaniumCXXABI::lowerDataMemberCmp(cir::CmpOp op, mlir::Value loweredLhs,
536 mlir::Value loweredRhs,
537 mlir::OpBuilder &builder) const {
538 return cir::CmpOp::create(builder, op.getLoc(), op.getKind(), loweredLhs,
539 loweredRhs);
540}
541
542mlir::Value LowerItaniumCXXABI::lowerMethodCmp(cir::CmpOp op,
543 mlir::Value loweredLhs,
544 mlir::Value loweredRhs,
545 mlir::OpBuilder &builder) const {
546 assert(op.getKind() == cir::CmpOpKind::eq ||
547 op.getKind() == cir::CmpOpKind::ne);
548
549 mlir::ImplicitLocOpBuilder locBuilder(op.getLoc(), builder);
550 cir::IntType ptrdiffCIRTy = getPtrDiffCIRTy(lm);
551 mlir::Value ptrdiffZero =
552 cir::ConstantOp::create(locBuilder, cir::IntAttr::get(ptrdiffCIRTy, 0));
553
554 mlir::Value lhsPtrField =
555 cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredLhs, 0);
556 mlir::Value rhsPtrField =
557 cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredRhs, 0);
558 mlir::Value ptrCmp =
559 cir::CmpOp::create(locBuilder, op.getKind(), lhsPtrField, rhsPtrField);
560 mlir::Value ptrCmpToNull =
561 cir::CmpOp::create(locBuilder, op.getKind(), lhsPtrField, ptrdiffZero);
562
563 mlir::Value lhsAdjField =
564 cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredLhs, 1);
565 mlir::Value rhsAdjField =
566 cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredRhs, 1);
567 mlir::Value adjCmp =
568 cir::CmpOp::create(locBuilder, op.getKind(), lhsAdjField, rhsAdjField);
569
570 auto create_and = [&](mlir::Value lhs, mlir::Value rhs) {
571 return cir::AndOp::create(locBuilder, lhs.getType(), lhs, rhs);
572 };
573 auto create_or = [&](mlir::Value lhs, mlir::Value rhs) {
574 return cir::OrOp::create(locBuilder, lhs.getType(), lhs, rhs);
575 };
576
577 // Null member function pointers on ARM clear the low bit of Adj,
578 // so the zero condition has to check that neither low bit is set.
579 if (useARMMethodPtrABI) {
580 mlir::Value one =
581 cir::ConstantOp::create(locBuilder, cir::IntAttr::get(ptrdiffCIRTy, 1));
582
583 // The low bit of the adjustment field is used to encode whether the member
584 // function is virtual, but the ARM ABI specifies that for null pointers
585 // this bit must be clear. Therefore, to test whether the member pointer is
586 // null, we need to check that bit.
587 //
588 // If we are performing an equality check, ptrCmpToNull indicates that both
589 // pointers are null (if they are equal -- we only actually test lhs).
590 // If we are performing an inequality check, ptrCmpToNull indicates that
591 // one of the pointers is not null.
592 //
593 // To apply the ARM-specific logic, if either virtual bit is set, they
594 // cannot both be null (equality case -- ptrCmpToNull &= orAdjAnd1CmpZero),
595 // and if either virtual bit is set, one of the pointers is not null
596 // (inequality case -- ptrCmpToNull |= orAdjAnd1CmpZero).
597 mlir::Value orAdj = create_or(lhsAdjField, rhsAdjField);
598 mlir::Value orAdjAnd1 = create_and(orAdj, one);
599 mlir::Value orAdjAnd1CmpZero =
600 cir::CmpOp::create(locBuilder, op.getKind(), orAdjAnd1, ptrdiffZero);
601
602 if (op.getKind() == cir::CmpOpKind::eq)
603 ptrCmpToNull = create_and(ptrCmpToNull, orAdjAnd1CmpZero);
604 else
605 ptrCmpToNull = create_or(ptrCmpToNull, orAdjAnd1CmpZero);
606 }
607
608 mlir::Value result;
609 if (op.getKind() == cir::CmpOpKind::eq) {
610 // (lhs.ptr == null || lhs.adj == rhs.adj) && lhs.ptr == rhs.ptr
611 result = create_and(ptrCmp, create_or(ptrCmpToNull, adjCmp));
612 } else {
613 // lhs.ptr == rhs.ptr && (lhs.ptr == null || lhs.adj == rhs.adj)
614 result = create_or(ptrCmp, create_and(ptrCmpToNull, adjCmp));
615 }
616
617 return result;
618}
619
620mlir::Value LowerItaniumCXXABI::lowerDataMemberBitcast(
621 cir::CastOp op, mlir::Type loweredDstTy, mlir::Value loweredSrc,
622 mlir::OpBuilder &builder) const {
623 if (loweredSrc.getType() == loweredDstTy)
624 return loweredSrc;
625
626 return cir::CastOp::create(builder, op.getLoc(), loweredDstTy,
627 cir::CastKind::bitcast, loweredSrc);
628}
629
630mlir::Value LowerItaniumCXXABI::lowerDataMemberToBoolCast(
631 cir::CastOp op, mlir::Value loweredSrc, mlir::OpBuilder &builder) const {
632 // Itanium C++ ABI 2.3:
633 // A NULL pointer is represented as -1.
634 auto nullAttr = cir::IntAttr::get(getPtrDiffCIRTy(lm), -1);
635 auto nullValue = cir::ConstantOp::create(builder, op.getLoc(), nullAttr);
636 return cir::CmpOp::create(builder, op.getLoc(), cir::CmpOpKind::ne,
637 loweredSrc, nullValue);
638}
639
640mlir::Value
641LowerItaniumCXXABI::lowerMethodBitcast(cir::CastOp op, mlir::Type loweredDstTy,
642 mlir::Value loweredSrc,
643 mlir::OpBuilder &builder) const {
644 if (loweredSrc.getType() == loweredDstTy)
645 return loweredSrc;
646
647 return loweredSrc;
648}
649
650mlir::Value LowerItaniumCXXABI::lowerMethodToBoolCast(
651 cir::CastOp op, mlir::Value loweredSrc, mlir::OpBuilder &builder) const {
652 mlir::ImplicitLocOpBuilder locBuilder(op.getLoc(), builder);
653
654 // Itanium C++ ABI 2.3.2:
655 //
656 // In the standard representation, a null member function pointer is
657 // represented with ptr set to a null pointer. The value of adj is
658 // unspecified for null member function pointers.
659 cir::IntType ptrdiffCIRTy = getPtrDiffCIRTy(lm);
660 mlir::Value ptrdiffZero =
661 cir::ConstantOp::create(locBuilder, cir::IntAttr::get(ptrdiffCIRTy, 0));
662 mlir::Value ptrField =
663 cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredSrc, 0);
664
665 mlir::Value result =
666 cir::CmpOp::create(locBuilder, cir::CmpOpKind::ne, ptrField, ptrdiffZero);
667
668 // On ARM, a member function pointer is also non-null if the low bit of 'adj'
669 // (the virtual bit) is set.
670 if (useARMMethodPtrABI) {
671 mlir::Value one =
672 cir::ConstantOp::create(locBuilder, cir::IntAttr::get(ptrdiffCIRTy, 1));
673 mlir::Value adj =
674 cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredSrc, 1);
675 mlir::Value virtualBit =
676 cir::AndOp::create(locBuilder, ptrdiffCIRTy, adj, one);
677 mlir::Value isVirtual = cir::CmpOp::create(locBuilder, cir::CmpOpKind::ne,
678 virtualBit, ptrdiffZero);
679 result = cir::OrOp::create(locBuilder, result, isVirtual);
680 }
681
682 return result;
683}
684
685static void buildBadCastCall(mlir::OpBuilder &builder, mlir::Location loc,
686 mlir::FlatSymbolRefAttr badCastFuncRef) {
687 auto callOp = cir::CallOp::create(builder, loc, badCastFuncRef,
688 /*resType=*/cir::VoidType(),
689 /*operands=*/mlir::ValueRange{});
690 callOp->setAttr(cir::CIRDialect::getNoReturnAttrName(),
691 builder.getUnitAttr());
692
693 cir::UnreachableOp::create(builder, loc);
694 builder.clearInsertionPoint();
695}
696
697static mlir::Value buildDynamicCastAfterNullCheck(cir::DynamicCastOp op,
698 mlir::OpBuilder &builder) {
699 mlir::Location loc = op->getLoc();
700 mlir::Value srcValue = op.getSrc();
701 cir::DynamicCastInfoAttr castInfo = op.getInfo().value();
702
703 // TODO(cir): consider address space
705
706 auto voidPtrTy =
707 cir::PointerType::get(cir::VoidType::get(builder.getContext()));
708
709 mlir::Value srcPtr = cir::CastOp::create(builder, loc, voidPtrTy,
710 cir::CastKind::bitcast, srcValue);
711 mlir::Value srcRtti =
712 cir::ConstantOp::create(builder, loc, castInfo.getSrcRtti());
713 mlir::Value destRtti =
714 cir::ConstantOp::create(builder, loc, castInfo.getDestRtti());
715 mlir::Value offsetHint =
716 cir::ConstantOp::create(builder, loc, castInfo.getOffsetHint());
717
718 mlir::FlatSymbolRefAttr dynCastFuncRef = castInfo.getRuntimeFunc();
719 mlir::Value dynCastFuncArgs[4] = {srcPtr, srcRtti, destRtti, offsetHint};
720
721 mlir::Value castedPtr = cir::CallOp::create(builder, loc, dynCastFuncRef,
722 voidPtrTy, dynCastFuncArgs)
723 .getResult();
724
725 assert(mlir::isa<cir::PointerType>(castedPtr.getType()) &&
726 "the return value of __dynamic_cast should be a ptr");
727
728 /// C++ [expr.dynamic.cast]p9:
729 /// A failed cast to reference type throws std::bad_cast
730 if (op.isRefCast()) {
731 // Emit a cir.if that checks the casted value.
732 mlir::Value null = cir::ConstantOp::create(
733 builder, loc,
734 cir::ConstPtrAttr::get(castedPtr.getType(),
735 builder.getI64IntegerAttr(0)));
736 mlir::Value castedPtrIsNull =
737 cir::CmpOp::create(builder, loc, cir::CmpOpKind::eq, castedPtr, null);
738 cir::IfOp::create(builder, loc, castedPtrIsNull, false,
739 [&](mlir::OpBuilder &, mlir::Location) {
740 buildBadCastCall(builder, loc,
741 castInfo.getBadCastFunc());
742 });
743 }
744
745 // Note that castedPtr is a void*. Cast it to a pointer to the destination
746 // type before return.
747 return cir::CastOp::create(builder, loc, op.getType(), cir::CastKind::bitcast,
748 castedPtr);
749}
750
752 cir::DynamicCastOp op, cir::LowerModule &lm, mlir::OpBuilder &builder) {
753 mlir::Location loc = op.getLoc();
754 bool vtableUsesRelativeLayout = op.getRelativeLayout();
755
756 // TODO(cir): consider address space in this function.
758
759 mlir::Type vtableElemTy;
760 uint64_t vtableElemAlign;
761 if (vtableUsesRelativeLayout) {
762 vtableElemTy =
763 cir::IntType::get(builder.getContext(), 32, /*isSigned=*/true);
764 vtableElemAlign = 4;
765 } else {
766 vtableElemTy = getPtrDiffCIRTy(lm);
767 vtableElemAlign = llvm::divideCeil(
769 }
770
771 mlir::Type vtableElemPtrTy = cir::PointerType::get(vtableElemTy);
772 mlir::Type i64Ty = cir::IntType::get(builder.getContext(), /*width=*/64,
773 /*isSigned=*/true);
774
775 // Access vtable to get the offset from the given object to its containing
776 // complete object.
777 // TODO: Add a specialized operation to get the object offset?
778 auto vptrPtr = cir::VTableGetVPtrOp::create(builder, loc, op.getSrc());
779 mlir::Value vptr = cir::LoadOp::create(
780 builder, loc, vptrPtr,
781 /*isDeref=*/false,
782 /*is_volatile=*/false,
783 /*alignment=*/builder.getI64IntegerAttr(vtableElemAlign),
784 /*sync_scope=*/cir::SyncScopeKindAttr(),
785 /*mem_order=*/cir::MemOrderAttr());
786 mlir::Value elementPtr = cir::CastOp::create(builder, loc, vtableElemPtrTy,
787 cir::CastKind::bitcast, vptr);
788 mlir::Value minusTwo =
789 cir::ConstantOp::create(builder, loc, cir::IntAttr::get(i64Ty, -2));
790 mlir::Value offsetToTopSlotPtr = cir::PtrStrideOp::create(
791 builder, loc, vtableElemPtrTy, elementPtr, minusTwo);
792 mlir::Value offsetToTop = cir::LoadOp::create(
793 builder, loc, offsetToTopSlotPtr,
794 /*isDeref=*/false,
795 /*is_volatile=*/false,
796 /*alignment=*/builder.getI64IntegerAttr(vtableElemAlign),
797 /*sync_scope=*/cir::SyncScopeKindAttr(),
798 /*mem_order=*/cir::MemOrderAttr());
799
800 auto voidPtrTy =
801 cir::PointerType::get(cir::VoidType::get(builder.getContext()));
802
803 // Add the offset to the given pointer to get the cast result.
804 // Cast the input pointer to a uint8_t* to allow pointer arithmetic.
805 mlir::Type u8PtrTy =
806 cir::PointerType::get(cir::IntType::get(builder.getContext(), /*width=*/8,
807 /*isSigned=*/false));
808 mlir::Value srcBytePtr = cir::CastOp::create(
809 builder, loc, u8PtrTy, cir::CastKind::bitcast, op.getSrc());
810 auto dstBytePtr =
811 cir::PtrStrideOp::create(builder, loc, u8PtrTy, srcBytePtr, offsetToTop);
812 // Cast the result to a void*.
813 return cir::CastOp::create(builder, loc, voidPtrTy, cir::CastKind::bitcast,
814 dstBytePtr);
815}
816
817mlir::Value
818LowerItaniumCXXABI::lowerDynamicCast(cir::DynamicCastOp op,
819 mlir::OpBuilder &builder) const {
820 mlir::Location loc = op->getLoc();
821 mlir::Value srcValue = op.getSrc();
822
824
825 if (op.isRefCast())
826 return buildDynamicCastAfterNullCheck(op, builder);
827
828 mlir::Value srcValueIsNotNull = cir::CastOp::create(
829 builder, loc, cir::BoolType::get(builder.getContext()),
830 cir::CastKind::ptr_to_bool, srcValue);
831 return cir::TernaryOp::create(
832 builder, loc, srcValueIsNotNull,
833 [&](mlir::OpBuilder &, mlir::Location) {
834 mlir::Value castedValue =
835 op.isCastToVoid()
836 ? buildDynamicCastToVoidAfterNullCheck(op, lm, builder)
837 : buildDynamicCastAfterNullCheck(op, builder);
838 cir::YieldOp::create(builder, loc, castedValue);
839 },
840 [&](mlir::OpBuilder &, mlir::Location) {
841 mlir::Value null = cir::ConstantOp::create(
842 builder, loc,
843 cir::ConstPtrAttr::get(op.getType(),
844 builder.getI64IntegerAttr(0)));
845 cir::YieldOp::create(builder, loc, null);
846 })
847 .getResult();
848}
849mlir::Value
850LowerItaniumCXXABI::lowerVTableGetTypeInfo(cir::VTableGetTypeInfoOp op,
851 mlir::OpBuilder &builder) const {
852 mlir::Location loc = op->getLoc();
853 auto offset = cir::ConstantOp::create(
854 builder, op->getLoc(), cir::IntAttr::get(getPtrDiffCIRTy(lm), -1));
855
856 // Cast the vptr to type_info-ptr, so that we can go backwards 1 pointer.
857 auto vptrCast = cir::CastOp::create(builder, loc, op.getType(),
858 cir::CastKind::bitcast, op.getVptr());
859
860 return cir::PtrStrideOp::create(builder, loc, vptrCast.getType(), vptrCast,
861 offset)
862 .getResult();
863}
864
865clang::CharUnits LowerItaniumCXXABI::getArrayCookieSizeImpl(
866 mlir::Type elementType, const mlir::DataLayout &dataLayout) const {
867 // The array cookie is a size_t; pad that up to the element alignment.
868 // The cookie is actually right-justified in that space.
869 clang::CharUnits sizeOfSizeT =
870 clang::CharUnits::fromQuantity(getPtrSizeInBits() / 8);
871 clang::CharUnits eltAlign = clang::CharUnits::fromQuantity(
872 dataLayout.getTypePreferredAlignment(elementType));
873 return std::max(sizeOfSizeT, eltAlign);
874}
875
876mlir::Value LowerItaniumCXXABI::readArrayCookieImpl(
877 mlir::Location loc, mlir::Value allocPtr, clang::CharUnits cookieSize,
878 clang::CharUnits cookieAlignment, const mlir::DataLayout &dataLayout,
879 CIRBaseBuilderTy &builder) const {
880 unsigned ptrSizeInBits = getPtrSizeInBits();
881 auto u8PtrTy = builder.getPointerTo(builder.getUIntNTy(8));
882 auto ptrDiffTy = builder.getSIntNTy(ptrSizeInBits);
883 auto sizeTy = builder.getUIntNTy(ptrSizeInBits);
884
885 // The element count is right-justified in the cookie.
886 clang::CharUnits sizeOfSizeT =
887 clang::CharUnits::fromQuantity(ptrSizeInBits / 8);
888 clang::CharUnits countOffset = cookieSize - sizeOfSizeT;
889
890 mlir::Value countBytePtr = allocPtr;
891 clang::CharUnits countAlignment = cookieAlignment;
892 if (!countOffset.isZero()) {
893 mlir::Value offsetVal = cir::ConstantOp::create(
894 builder, loc, cir::IntAttr::get(ptrDiffTy, countOffset.getQuantity()));
895 countBytePtr =
896 cir::PtrStrideOp::create(builder, loc, u8PtrTy, allocPtr, offsetVal);
897 countAlignment = cookieAlignment.alignmentAtOffset(countOffset);
898 }
899
900 auto countPtrTy = cir::PointerType::get(sizeTy);
901 mlir::Value countPtr = cir::CastOp::create(
902 builder, loc, countPtrTy, cir::CastKind::bitcast, countBytePtr);
903 return cir::LoadOp::create(
904 builder, loc, countPtr, /*isDeref=*/false, /*isVolatile=*/false,
905 builder.getI64IntegerAttr(countAlignment.getQuantity()),
906 cir::SyncScopeKindAttr(), cir::MemOrderAttr());
907}
908
909} // namespace cir
__device__ __2f16 b
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
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