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
37public:
38 LowerItaniumCXXABI(LowerModule &lm, bool useARMMethodPtrABI = false)
39 : CIRCXXABI(lm), useARMMethodPtrABI(useARMMethodPtrABI) {}
40
41 /// Lower the given data member pointer type to its ABI type. The returned
42 /// type is also a CIR type.
43 virtual mlir::Type
44 lowerDataMemberType(cir::DataMemberType type,
45 const mlir::TypeConverter &typeConverter) const override;
46
47 mlir::Type
48 lowerMethodType(cir::MethodType type,
49 const mlir::TypeConverter &typeConverter) const override;
50
51 mlir::TypedAttr lowerDataMemberConstant(
52 cir::DataMemberAttr attr, const mlir::DataLayout &layout,
53 const mlir::TypeConverter &typeConverter) const override;
54
55 mlir::TypedAttr
56 lowerMethodConstant(cir::MethodAttr attr, const mlir::DataLayout &layout,
57 const mlir::TypeConverter &typeConverter) const override;
58
59 mlir::Operation *
60 lowerGetRuntimeMember(cir::GetRuntimeMemberOp op, mlir::Type loweredResultTy,
61 mlir::Value loweredAddr, mlir::Value loweredMember,
62 mlir::OpBuilder &builder) const override;
63
64 void lowerGetMethod(cir::GetMethodOp op, mlir::Value &callee,
65 mlir::Value &thisArg, mlir::Value loweredMethod,
66 mlir::Value loweredObjectPtr,
67 mlir::ConversionPatternRewriter &rewriter) const override;
68
69 mlir::Value lowerBaseDataMember(cir::BaseDataMemberOp op,
70 mlir::Value loweredSrc,
71 mlir::OpBuilder &builder) const override;
72
73 mlir::Value lowerDerivedDataMember(cir::DerivedDataMemberOp op,
74 mlir::Value loweredSrc,
75 mlir::OpBuilder &builder) const override;
76
77 mlir::Value lowerBaseMethod(cir::BaseMethodOp op, mlir::Value loweredSrc,
78 mlir::OpBuilder &builder) const override;
79
80 mlir::Value lowerDerivedMethod(cir::DerivedMethodOp op,
81 mlir::Value loweredSrc,
82 mlir::OpBuilder &builder) const override;
83
84 mlir::Value lowerDataMemberCmp(cir::CmpOp op, mlir::Value loweredLhs,
85 mlir::Value loweredRhs,
86 mlir::OpBuilder &builder) const override;
87
88 mlir::Value lowerMethodCmp(cir::CmpOp op, mlir::Value loweredLhs,
89 mlir::Value loweredRhs,
90 mlir::OpBuilder &builder) const override;
91
92 mlir::Value lowerDataMemberBitcast(cir::CastOp op, mlir::Type loweredDstTy,
93 mlir::Value loweredSrc,
94 mlir::OpBuilder &builder) const override;
95
96 mlir::Value
97 lowerDataMemberToBoolCast(cir::CastOp op, mlir::Value loweredSrc,
98 mlir::OpBuilder &builder) const override;
99
100 mlir::Value lowerMethodBitcast(cir::CastOp op, mlir::Type loweredDstTy,
101 mlir::Value loweredSrc,
102 mlir::OpBuilder &builder) const override;
103
104 mlir::Value lowerMethodToBoolCast(cir::CastOp op, mlir::Value loweredSrc,
105 mlir::OpBuilder &builder) const override;
106
107 mlir::Value lowerDynamicCast(cir::DynamicCastOp op,
108 mlir::OpBuilder &builder) const override;
109 mlir::Value lowerVTableGetTypeInfo(cir::VTableGetTypeInfoOp op,
110 mlir::OpBuilder &builder) const override;
111
112 clang::CharUnits
113 getArrayCookieSizeImpl(mlir::Type elementType,
114 const mlir::DataLayout &dataLayout) const override;
115
116 mlir::Value readArrayCookieImpl(mlir::Location loc, mlir::Value allocPtr,
117 clang::CharUnits cookieSize,
118 clang::CharUnits cookieAlignment,
119 const mlir::DataLayout &dataLayout,
120 CIRBaseBuilderTy &builder) const override;
121};
122
123} // namespace
124
125std::unique_ptr<CIRCXXABI> createItaniumCXXABI(LowerModule &lm) {
126 switch (lm.getCXXABIKind()) {
127 // Note that AArch64 uses the generic ItaniumCXXABI class since it doesn't
128 // include the other 32-bit ARM oddities: constructor/destructor return values
129 // and array cookies.
130 case clang::TargetCXXABI::GenericAArch64:
131 case clang::TargetCXXABI::AppleARM64:
132 // TODO: this isn't quite right, clang uses AppleARM64CXXABI which inherits
133 // from ARMCXXABI. We'll have to follow suit.
135 return std::make_unique<LowerItaniumCXXABI>(lm,
136 /*useARMMethodPtrABI=*/true);
137
138 case clang::TargetCXXABI::GenericItanium:
139 return std::make_unique<LowerItaniumCXXABI>(lm);
140
141 case clang::TargetCXXABI::Microsoft:
142 llvm_unreachable("Microsoft ABI is not Itanium-based");
143 default:
144 llvm_unreachable("Other Itanium ABI?");
145 }
146}
147
148static cir::IntType getPtrDiffCIRTy(LowerModule &lm) {
149 const clang::TargetInfo &target = lm.getTarget();
152 return cir::IntType::get(lm.getMLIRContext(), target.getTypeWidth(ptrdiffTy),
153 target.isTypeSigned(ptrdiffTy));
154}
155
156mlir::Type LowerItaniumCXXABI::lowerDataMemberType(
157 cir::DataMemberType type, const mlir::TypeConverter &typeConverter) const {
158 // Itanium C++ ABI 2.3.1:
159 // A data member pointer is represented as the data member's offset in bytes
160 // from the address point of an object of the base type, as a ptrdiff_t.
161 return getPtrDiffCIRTy(lm);
162}
163
164mlir::Type LowerItaniumCXXABI::lowerMethodType(
165 cir::MethodType type, const mlir::TypeConverter &typeConverter) const {
166 // Itanium C++ ABI 2.3.2:
167 // In all representations, the basic ABI properties of member function
168 // pointer types are those of the following class, where fnptr_t is the
169 // appropriate function-pointer type for a member function of this type:
170 //
171 // struct {
172 // fnptr_t ptr;
173 // ptrdiff_t adj;
174 // };
175
176 cir::IntType ptrdiffCIRTy = getPtrDiffCIRTy(lm);
177
178 // Note that clang CodeGen emits struct{ptrdiff_t, ptrdiff_t} for member
179 // function pointers. Let's follow this approach.
180 return cir::RecordType::get(type.getContext(), {ptrdiffCIRTy, ptrdiffCIRTy},
181 /*packed=*/false, /*padded=*/false,
182 cir::RecordType::Struct);
183}
184
185mlir::TypedAttr LowerItaniumCXXABI::lowerDataMemberConstant(
186 cir::DataMemberAttr attr, const mlir::DataLayout &layout,
187 const mlir::TypeConverter &typeConverter) const {
188 int64_t memberOffset;
189 if (attr.isNullPtr()) {
190 // Itanium C++ ABI 2.3:
191 // A NULL pointer is represented as -1.
192 memberOffset = -1;
193 } else {
194 // Itanium C++ ABI 2.3:
195 // A pointer to data member is an offset from the base address of
196 // the class object containing it, represented as a ptrdiff_t
197 unsigned memberIndex = attr.getMemberIndex().value();
198 memberOffset =
199 attr.getType().getClassTy().getElementOffset(layout, memberIndex);
200 }
201
202 mlir::Type abiTy = lowerDataMemberType(attr.getType(), typeConverter);
203 return cir::IntAttr::get(abiTy, memberOffset);
204}
205
206mlir::TypedAttr LowerItaniumCXXABI::lowerMethodConstant(
207 cir::MethodAttr attr, const mlir::DataLayout &layout,
208 const mlir::TypeConverter &typeConverter) const {
209 cir::IntType ptrdiffCIRTy = getPtrDiffCIRTy(lm);
210
211 // lowerMethodType returns the CIR type used to represent the method pointer
212 // in an ABI-specific way. That's why lowerMethodType returns cir::RecordType
213 // here.
214 auto loweredMethodTy = mlir::cast<cir::RecordType>(
215 lowerMethodType(attr.getType(), typeConverter));
216
217 auto zero = cir::IntAttr::get(ptrdiffCIRTy, 0);
218
219 // Itanium C++ ABI 2.3.2:
220 // In all representations, the basic ABI properties of member function
221 // pointer types are those of the following class, where fnptr_t is the
222 // appropriate function-pointer type for a member function of this type:
223 //
224 // struct {
225 // fnptr_t ptr;
226 // ptrdiff_t adj;
227 // };
228
229 if (attr.isNull()) {
230 // Itanium C++ ABI 2.3.2:
231 //
232 // In the standard representation, a null member function pointer is
233 // represented with ptr set to a null pointer. The value of adj is
234 // unspecified for null member function pointers.
235 //
236 // clang CodeGen emits struct{null, null} for null member function pointers.
237 // Let's do the same here.
238 return cir::ConstRecordAttr::get(
239 loweredMethodTy, mlir::ArrayAttr::get(attr.getContext(), {zero, zero}));
240 }
241
242 if (attr.isVirtual()) {
243 if (useARMMethodPtrABI) {
244 // ARM C++ ABI 3.2.1:
245 // This ABI specifies that adj contains twice the this
246 // adjustment, plus 1 if the member function is virtual. The
247 // least significant bit of adj then makes exactly the same
248 // discrimination as the least significant bit of ptr does for
249 // Itanium.
250 llvm_unreachable("ARM method ptr abi NYI");
251 }
252
253 // Itanium C++ ABI 2.3.2:
254 //
255 // In the standard representation, a member function pointer for a
256 // virtual function is represented with ptr set to 1 plus the function's
257 // v-table entry offset (in bytes), converted to a function pointer as if
258 // by reinterpret_cast<fnptr_t>(uintfnptr_t(1 + offset)), where
259 // uintfnptr_t is an unsigned integer of the same size as fnptr_t.
260 auto ptr =
261 cir::IntAttr::get(ptrdiffCIRTy, 1 + attr.getVtableOffset().value());
262 return cir::ConstRecordAttr::get(
263 loweredMethodTy, mlir::ArrayAttr::get(attr.getContext(), {ptr, zero}));
264 }
265
266 // Itanium C++ ABI 2.3.2:
267 //
268 // A member function pointer for a non-virtual member function is
269 // represented with ptr set to a pointer to the function, using the base
270 // ABI's representation of function pointers.
271 auto ptr = cir::GlobalViewAttr::get(ptrdiffCIRTy, attr.getSymbol().value());
272 return cir::ConstRecordAttr::get(
273 loweredMethodTy, mlir::ArrayAttr::get(attr.getContext(), {ptr, zero}));
274}
275
276mlir::Operation *LowerItaniumCXXABI::lowerGetRuntimeMember(
277 cir::GetRuntimeMemberOp op, mlir::Type loweredResultTy,
278 mlir::Value loweredAddr, mlir::Value loweredMember,
279 mlir::OpBuilder &builder) const {
280 auto byteTy = cir::IntType::get(op.getContext(), 8, true);
281 auto bytePtrTy = cir::PointerType::get(
282 byteTy,
283 mlir::cast<cir::PointerType>(op.getAddr().getType()).getAddrSpace());
284 auto objectBytesPtr = cir::CastOp::create(
285 builder, op.getLoc(), bytePtrTy, cir::CastKind::bitcast, op.getAddr());
286 auto memberBytesPtr = cir::PtrStrideOp::create(
287 builder, op.getLoc(), bytePtrTy, objectBytesPtr, loweredMember);
288 return cir::CastOp::create(builder, op.getLoc(), op.getType(),
289 cir::CastKind::bitcast, memberBytesPtr);
290}
291
292void LowerItaniumCXXABI::lowerGetMethod(
293 cir::GetMethodOp op, mlir::Value &callee, mlir::Value &thisArg,
294 mlir::Value loweredMethod, mlir::Value loweredObjectPtr,
295 mlir::ConversionPatternRewriter &rewriter) const {
296 // In the Itanium and ARM ABIs, method pointers have the form:
297 // struct { ptrdiff_t ptr; ptrdiff_t adj; } memptr;
298 //
299 // In the Itanium ABI:
300 // - method pointers are virtual if (memptr.ptr & 1) is nonzero
301 // - the this-adjustment is (memptr.adj)
302 // - the virtual offset is (memptr.ptr - 1)
303 //
304 // In the ARM ABI:
305 // - method pointers are virtual if (memptr.adj & 1) is nonzero
306 // - the this-adjustment is (memptr.adj >> 1)
307 // - the virtual offset is (memptr.ptr)
308 // ARM uses 'adj' for the virtual flag because Thumb functions
309 // may be only single-byte aligned.
310 //
311 // If the member is virtual, the adjusted 'this' pointer points
312 // to a vtable pointer from which the virtual offset is applied.
313 //
314 // If the member is non-virtual, memptr.ptr is the address of
315 // the function to call.
316
317 mlir::ImplicitLocOpBuilder locBuilder(op.getLoc(), rewriter);
318 mlir::Type calleePtrTy = op.getCallee().getType();
319
320 cir::IntType ptrdiffCIRTy = getPtrDiffCIRTy(lm);
321 mlir::Value ptrdiffOne =
322 cir::ConstantOp::create(locBuilder, cir::IntAttr::get(ptrdiffCIRTy, 1));
323
324 mlir::Value adj =
325 cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredMethod, 1);
326 if (useARMMethodPtrABI) {
327 op.emitError("ARM method ptr abi NYI");
328 return;
329 }
330
331 // Apply the adjustment to the 'this' pointer.
332 mlir::Type thisVoidPtrTy =
333 cir::PointerType::get(cir::VoidType::get(locBuilder.getContext()),
334 op.getObject().getType().getAddrSpace());
335 mlir::Value thisVoidPtr = cir::CastOp::create(
336 locBuilder, thisVoidPtrTy, cir::CastKind::bitcast, loweredObjectPtr);
337 thisArg =
338 cir::PtrStrideOp::create(locBuilder, thisVoidPtrTy, thisVoidPtr, adj);
339
340 // Load the "ptr" field of the member function pointer and determine if it
341 // points to a virtual function.
342 mlir::Value methodPtrField =
343 cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredMethod, 0);
344 mlir::Value virtualBit =
345 cir::AndOp::create(rewriter, op.getLoc(), methodPtrField, ptrdiffOne);
346 mlir::Value isVirtual;
347 if (useARMMethodPtrABI)
348 llvm_unreachable("ARM method ptr abi NYI");
349 else
350 isVirtual = cir::CmpOp::create(locBuilder, cir::CmpOpKind::eq, virtualBit,
351 ptrdiffOne);
352
356
357 auto buildVirtualCallee = [&](mlir::OpBuilder &b, mlir::Location loc) {
358 // Load vtable pointer.
359 // Note that vtable pointer always point to the global address space.
360 auto vtablePtrTy =
361 cir::PointerType::get(cir::IntType::get(b.getContext(), 8, true));
362 auto vtablePtrPtrTy = cir::PointerType::get(
363 vtablePtrTy, op.getObject().getType().getAddrSpace());
364 auto vtablePtrPtr = cir::CastOp::create(b, loc, vtablePtrPtrTy,
365 cir::CastKind::bitcast, thisArg);
367 mlir::Value vtablePtr =
368 cir::LoadOp::create(b, loc, vtablePtrPtr, /*isDeref=*/false,
369 /*isVolatile=*/false,
370 /*alignment=*/mlir::IntegerAttr(),
371 /*sync_scope=*/cir::SyncScopeKindAttr{},
372 /*mem_order=*/cir::MemOrderAttr());
373
374 // Get the vtable offset.
375 mlir::Value vtableOffset = methodPtrField;
376 assert(!useARMMethodPtrABI && "ARM method ptr abi NYI");
377 vtableOffset = cir::SubOp::create(b, loc, vtableOffset.getType(),
378 vtableOffset, ptrdiffOne);
379
383
384 // Apply the offset to the vtable pointer and get the pointer to the target
385 // virtual function. Then load that pointer to get the callee.
386 mlir::Value vfpAddr = cir::PtrStrideOp::create(locBuilder, vtablePtrTy,
387 vtablePtr, vtableOffset);
388 auto vfpPtrTy = cir::PointerType::get(calleePtrTy);
389 mlir::Value vfpPtr = cir::CastOp::create(locBuilder, vfpPtrTy,
390 cir::CastKind::bitcast, vfpAddr);
391 auto fnPtr = cir::LoadOp::create(b, loc, vfpPtr,
392 /*isDeref=*/false, /*isVolatile=*/false,
393 /*alignment=*/mlir::IntegerAttr(),
394 /*sync_scope=*/cir::SyncScopeKindAttr{},
395 /*mem_order=*/cir::MemOrderAttr());
396
397 cir::YieldOp::create(b, loc, fnPtr.getResult());
399 };
400
401 callee = cir::TernaryOp::create(
402 locBuilder, isVirtual, /*thenBuilder=*/buildVirtualCallee,
403 /*elseBuilder=*/
404 [&](mlir::OpBuilder &b, mlir::Location loc) {
405 auto fnPtr = cir::CastOp::create(b, loc, calleePtrTy,
406 cir::CastKind::int_to_ptr,
407 methodPtrField);
408 cir::YieldOp::create(b, loc, fnPtr.getResult());
409 })
410 .getResult();
411}
412
413static mlir::Value lowerDataMemberCast(mlir::Operation *op,
414 mlir::Value loweredSrc,
415 std::int64_t offset,
416 bool isDerivedToBase,
417 mlir::OpBuilder &builder) {
418 if (offset == 0)
419 return loweredSrc;
420 mlir::Location loc = op->getLoc();
421 mlir::Type ty = loweredSrc.getType();
422
423 auto getConstantInt = [&](int64_t value) -> cir::ConstantOp {
424 return cir::ConstantOp::create(builder, loc, cir::IntAttr::get(ty, value));
425 };
426
427 cir::ConstantOp nullValue = getConstantInt(-1);
428 auto isNull = cir::CmpOp::create(builder, loc, cir::CmpOpKind::eq, loweredSrc,
429 nullValue);
430
431 cir::ConstantOp offsetValue = getConstantInt(offset);
432 mlir::Value adjustedPtr;
433 if (isDerivedToBase) {
434 auto subOp = cir::SubOp::create(builder, loc, ty, loweredSrc, offsetValue);
435 subOp.setNoSignedWrap(true);
436 adjustedPtr = subOp;
437 } else {
438 auto addOp = cir::AddOp::create(builder, loc, ty, loweredSrc, offsetValue);
439 addOp.setNoSignedWrap(true);
440 adjustedPtr = addOp;
441 }
442
443 return cir::SelectOp::create(builder, loc, ty, isNull, loweredSrc,
444 adjustedPtr);
445}
446
447mlir::Value
448LowerItaniumCXXABI::lowerBaseDataMember(cir::BaseDataMemberOp op,
449 mlir::Value loweredSrc,
450 mlir::OpBuilder &builder) const {
451 return lowerDataMemberCast(op, loweredSrc, op.getOffset().getSExtValue(),
452 /*isDerivedToBase=*/true, builder);
453}
454
455mlir::Value
456LowerItaniumCXXABI::lowerDerivedDataMember(cir::DerivedDataMemberOp op,
457 mlir::Value loweredSrc,
458 mlir::OpBuilder &builder) const {
459 return lowerDataMemberCast(op, loweredSrc, op.getOffset().getSExtValue(),
460 /*isDerivedToBase=*/false, builder);
461}
462
463static mlir::Value lowerMethodCast(mlir::Operation *op, mlir::Value loweredSrc,
464 std::int64_t offset, bool isDerivedToBase,
465 LowerModule &lowerMod,
466 mlir::OpBuilder &builder) {
467 if (offset == 0)
468 return loweredSrc;
469
470 cir::IntType ptrdiffCIRTy = getPtrDiffCIRTy(lowerMod);
471 auto adjField = cir::ExtractMemberOp::create(builder, op->getLoc(),
472 ptrdiffCIRTy, loweredSrc, 1);
473
474 auto offsetValue = cir::ConstantOp::create(
475 builder, op->getLoc(), cir::IntAttr::get(ptrdiffCIRTy, offset));
476 mlir::Value adjustedAdjField;
477 if (isDerivedToBase) {
478 auto subOp = cir::SubOp::create(builder, op->getLoc(), ptrdiffCIRTy,
479 adjField, offsetValue);
480 subOp.setNoSignedWrap(true);
481 adjustedAdjField = subOp;
482 } else {
483 auto addOp = cir::AddOp::create(builder, op->getLoc(), ptrdiffCIRTy,
484 adjField, offsetValue);
485 addOp.setNoSignedWrap(true);
486 adjustedAdjField = addOp;
487 }
488
489 return cir::InsertMemberOp::create(builder, op->getLoc(), loweredSrc, 1,
490 adjustedAdjField);
491}
492
493mlir::Value
494LowerItaniumCXXABI::lowerBaseMethod(cir::BaseMethodOp op,
495 mlir::Value loweredSrc,
496 mlir::OpBuilder &builder) const {
497 return lowerMethodCast(op, loweredSrc, op.getOffset().getSExtValue(),
498 /*isDerivedToBase=*/true, lm, builder);
499}
500
501mlir::Value
502LowerItaniumCXXABI::lowerDerivedMethod(cir::DerivedMethodOp op,
503 mlir::Value loweredSrc,
504 mlir::OpBuilder &builder) const {
505 return lowerMethodCast(op, loweredSrc, op.getOffset().getSExtValue(),
506 /*isDerivedToBase=*/false, lm, builder);
507}
508
509mlir::Value
510LowerItaniumCXXABI::lowerDataMemberCmp(cir::CmpOp op, mlir::Value loweredLhs,
511 mlir::Value loweredRhs,
512 mlir::OpBuilder &builder) const {
513 return cir::CmpOp::create(builder, op.getLoc(), op.getKind(), loweredLhs,
514 loweredRhs);
515}
516
517mlir::Value LowerItaniumCXXABI::lowerMethodCmp(cir::CmpOp op,
518 mlir::Value loweredLhs,
519 mlir::Value loweredRhs,
520 mlir::OpBuilder &builder) const {
521 assert(op.getKind() == cir::CmpOpKind::eq ||
522 op.getKind() == cir::CmpOpKind::ne);
523
524 mlir::ImplicitLocOpBuilder locBuilder(op.getLoc(), builder);
525 cir::IntType ptrdiffCIRTy = getPtrDiffCIRTy(lm);
526 mlir::Value ptrdiffZero =
527 cir::ConstantOp::create(locBuilder, cir::IntAttr::get(ptrdiffCIRTy, 0));
528
529 mlir::Value lhsPtrField =
530 cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredLhs, 0);
531 mlir::Value rhsPtrField =
532 cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredRhs, 0);
533 mlir::Value ptrCmp =
534 cir::CmpOp::create(locBuilder, op.getKind(), lhsPtrField, rhsPtrField);
535 mlir::Value ptrCmpToNull =
536 cir::CmpOp::create(locBuilder, op.getKind(), lhsPtrField, ptrdiffZero);
537
538 mlir::Value lhsAdjField =
539 cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredLhs, 1);
540 mlir::Value rhsAdjField =
541 cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredRhs, 1);
542 mlir::Value adjCmp =
543 cir::CmpOp::create(locBuilder, op.getKind(), lhsAdjField, rhsAdjField);
544
545 auto create_and = [&](mlir::Value lhs, mlir::Value rhs) {
546 return cir::AndOp::create(locBuilder, lhs.getType(), lhs, rhs);
547 };
548 auto create_or = [&](mlir::Value lhs, mlir::Value rhs) {
549 return cir::OrOp::create(locBuilder, lhs.getType(), lhs, rhs);
550 };
551
552 mlir::Value result;
553 if (op.getKind() == cir::CmpOpKind::eq) {
554 // (lhs.ptr == null || lhs.adj == rhs.adj) && lhs.ptr == rhs.ptr
555 result = create_and(ptrCmp, create_or(ptrCmpToNull, adjCmp));
556 } else {
557 // (lhs.ptr != null && lhs.adj != rhs.adj) || lhs.ptr != rhs.ptr
558 result = create_or(ptrCmp, create_and(ptrCmpToNull, adjCmp));
559 }
560
561 return result;
562}
563
564mlir::Value LowerItaniumCXXABI::lowerDataMemberBitcast(
565 cir::CastOp op, mlir::Type loweredDstTy, mlir::Value loweredSrc,
566 mlir::OpBuilder &builder) const {
567 if (loweredSrc.getType() == loweredDstTy)
568 return loweredSrc;
569
570 return cir::CastOp::create(builder, op.getLoc(), loweredDstTy,
571 cir::CastKind::bitcast, loweredSrc);
572}
573
574mlir::Value LowerItaniumCXXABI::lowerDataMemberToBoolCast(
575 cir::CastOp op, mlir::Value loweredSrc, mlir::OpBuilder &builder) const {
576 // Itanium C++ ABI 2.3:
577 // A NULL pointer is represented as -1.
578 auto nullAttr = cir::IntAttr::get(getPtrDiffCIRTy(lm), -1);
579 auto nullValue = cir::ConstantOp::create(builder, op.getLoc(), nullAttr);
580 return cir::CmpOp::create(builder, op.getLoc(), cir::CmpOpKind::ne,
581 loweredSrc, nullValue);
582}
583
584mlir::Value
585LowerItaniumCXXABI::lowerMethodBitcast(cir::CastOp op, mlir::Type loweredDstTy,
586 mlir::Value loweredSrc,
587 mlir::OpBuilder &builder) const {
588 if (loweredSrc.getType() == loweredDstTy)
589 return loweredSrc;
590
591 return loweredSrc;
592}
593
594mlir::Value LowerItaniumCXXABI::lowerMethodToBoolCast(
595 cir::CastOp op, mlir::Value loweredSrc, mlir::OpBuilder &builder) const {
596 // Itanium C++ ABI 2.3.2:
597 //
598 // In the standard representation, a null member function pointer is
599 // represented with ptr set to a null pointer. The value of adj is
600 // unspecified for null member function pointers.
601 cir::IntType ptrdiffCIRTy = getPtrDiffCIRTy(lm);
602 mlir::Value ptrdiffZero = cir::ConstantOp::create(
603 builder, op.getLoc(), cir::IntAttr::get(ptrdiffCIRTy, 0));
604 mlir::Value ptrField = cir::ExtractMemberOp::create(
605 builder, op.getLoc(), ptrdiffCIRTy, loweredSrc, 0);
606 return cir::CmpOp::create(builder, op.getLoc(), cir::CmpOpKind::ne, ptrField,
607 ptrdiffZero);
608}
609
610static void buildBadCastCall(mlir::OpBuilder &builder, mlir::Location loc,
611 mlir::FlatSymbolRefAttr badCastFuncRef) {
612 cir::CallOp::create(builder, loc, badCastFuncRef, /*resType=*/cir::VoidType(),
613 /*operands=*/mlir::ValueRange{});
614 // TODO(cir): Set the 'noreturn' attribute on the function.
616
617 cir::UnreachableOp::create(builder, loc);
618 builder.clearInsertionPoint();
619}
620
621static mlir::Value buildDynamicCastAfterNullCheck(cir::DynamicCastOp op,
622 mlir::OpBuilder &builder) {
623 mlir::Location loc = op->getLoc();
624 mlir::Value srcValue = op.getSrc();
625 cir::DynamicCastInfoAttr castInfo = op.getInfo().value();
626
627 // TODO(cir): consider address space
629
630 auto voidPtrTy =
631 cir::PointerType::get(cir::VoidType::get(builder.getContext()));
632
633 mlir::Value srcPtr = cir::CastOp::create(builder, loc, voidPtrTy,
634 cir::CastKind::bitcast, srcValue);
635 mlir::Value srcRtti =
636 cir::ConstantOp::create(builder, loc, castInfo.getSrcRtti());
637 mlir::Value destRtti =
638 cir::ConstantOp::create(builder, loc, castInfo.getDestRtti());
639 mlir::Value offsetHint =
640 cir::ConstantOp::create(builder, loc, castInfo.getOffsetHint());
641
642 mlir::FlatSymbolRefAttr dynCastFuncRef = castInfo.getRuntimeFunc();
643 mlir::Value dynCastFuncArgs[4] = {srcPtr, srcRtti, destRtti, offsetHint};
644
645 mlir::Value castedPtr = cir::CallOp::create(builder, loc, dynCastFuncRef,
646 voidPtrTy, dynCastFuncArgs)
647 .getResult();
648
649 assert(mlir::isa<cir::PointerType>(castedPtr.getType()) &&
650 "the return value of __dynamic_cast should be a ptr");
651
652 /// C++ [expr.dynamic.cast]p9:
653 /// A failed cast to reference type throws std::bad_cast
654 if (op.isRefCast()) {
655 // Emit a cir.if that checks the casted value.
656 mlir::Value null = cir::ConstantOp::create(
657 builder, loc,
658 cir::ConstPtrAttr::get(castedPtr.getType(),
659 builder.getI64IntegerAttr(0)));
660 mlir::Value castedPtrIsNull =
661 cir::CmpOp::create(builder, loc, cir::CmpOpKind::eq, castedPtr, null);
662 cir::IfOp::create(builder, loc, castedPtrIsNull, false,
663 [&](mlir::OpBuilder &, mlir::Location) {
664 buildBadCastCall(builder, loc,
665 castInfo.getBadCastFunc());
666 });
667 }
668
669 // Note that castedPtr is a void*. Cast it to a pointer to the destination
670 // type before return.
671 return cir::CastOp::create(builder, loc, op.getType(), cir::CastKind::bitcast,
672 castedPtr);
673}
674
676 cir::DynamicCastOp op, cir::LowerModule &lm, mlir::OpBuilder &builder) {
677 mlir::Location loc = op.getLoc();
678 bool vtableUsesRelativeLayout = op.getRelativeLayout();
679
680 // TODO(cir): consider address space in this function.
682
683 mlir::Type vtableElemTy;
684 uint64_t vtableElemAlign;
685 if (vtableUsesRelativeLayout) {
686 vtableElemTy =
687 cir::IntType::get(builder.getContext(), 32, /*isSigned=*/true);
688 vtableElemAlign = 4;
689 } else {
690 vtableElemTy = getPtrDiffCIRTy(lm);
691 vtableElemAlign = llvm::divideCeil(
693 }
694
695 mlir::Type vtableElemPtrTy = cir::PointerType::get(vtableElemTy);
696 mlir::Type i64Ty = cir::IntType::get(builder.getContext(), /*width=*/64,
697 /*isSigned=*/true);
698
699 // Access vtable to get the offset from the given object to its containing
700 // complete object.
701 // TODO: Add a specialized operation to get the object offset?
702 auto vptrPtr = cir::VTableGetVPtrOp::create(builder, loc, op.getSrc());
703 mlir::Value vptr = cir::LoadOp::create(
704 builder, loc, vptrPtr,
705 /*isDeref=*/false,
706 /*is_volatile=*/false,
707 /*alignment=*/builder.getI64IntegerAttr(vtableElemAlign),
708 /*sync_scope=*/cir::SyncScopeKindAttr(),
709 /*mem_order=*/cir::MemOrderAttr());
710 mlir::Value elementPtr = cir::CastOp::create(builder, loc, vtableElemPtrTy,
711 cir::CastKind::bitcast, vptr);
712 mlir::Value minusTwo =
713 cir::ConstantOp::create(builder, loc, cir::IntAttr::get(i64Ty, -2));
714 mlir::Value offsetToTopSlotPtr = cir::PtrStrideOp::create(
715 builder, loc, vtableElemPtrTy, elementPtr, minusTwo);
716 mlir::Value offsetToTop = cir::LoadOp::create(
717 builder, loc, offsetToTopSlotPtr,
718 /*isDeref=*/false,
719 /*is_volatile=*/false,
720 /*alignment=*/builder.getI64IntegerAttr(vtableElemAlign),
721 /*sync_scope=*/cir::SyncScopeKindAttr(),
722 /*mem_order=*/cir::MemOrderAttr());
723
724 auto voidPtrTy =
725 cir::PointerType::get(cir::VoidType::get(builder.getContext()));
726
727 // Add the offset to the given pointer to get the cast result.
728 // Cast the input pointer to a uint8_t* to allow pointer arithmetic.
729 mlir::Type u8PtrTy =
730 cir::PointerType::get(cir::IntType::get(builder.getContext(), /*width=*/8,
731 /*isSigned=*/false));
732 mlir::Value srcBytePtr = cir::CastOp::create(
733 builder, loc, u8PtrTy, cir::CastKind::bitcast, op.getSrc());
734 auto dstBytePtr =
735 cir::PtrStrideOp::create(builder, loc, u8PtrTy, srcBytePtr, offsetToTop);
736 // Cast the result to a void*.
737 return cir::CastOp::create(builder, loc, voidPtrTy, cir::CastKind::bitcast,
738 dstBytePtr);
739}
740
741mlir::Value
742LowerItaniumCXXABI::lowerDynamicCast(cir::DynamicCastOp op,
743 mlir::OpBuilder &builder) const {
744 mlir::Location loc = op->getLoc();
745 mlir::Value srcValue = op.getSrc();
746
748
749 if (op.isRefCast())
750 return buildDynamicCastAfterNullCheck(op, builder);
751
752 mlir::Value srcValueIsNotNull = cir::CastOp::create(
753 builder, loc, cir::BoolType::get(builder.getContext()),
754 cir::CastKind::ptr_to_bool, srcValue);
755 return cir::TernaryOp::create(
756 builder, loc, srcValueIsNotNull,
757 [&](mlir::OpBuilder &, mlir::Location) {
758 mlir::Value castedValue =
759 op.isCastToVoid()
760 ? buildDynamicCastToVoidAfterNullCheck(op, lm, builder)
761 : buildDynamicCastAfterNullCheck(op, builder);
762 cir::YieldOp::create(builder, loc, castedValue);
763 },
764 [&](mlir::OpBuilder &, mlir::Location) {
765 mlir::Value null = cir::ConstantOp::create(
766 builder, loc,
767 cir::ConstPtrAttr::get(op.getType(),
768 builder.getI64IntegerAttr(0)));
769 cir::YieldOp::create(builder, loc, null);
770 })
771 .getResult();
772}
773mlir::Value
774LowerItaniumCXXABI::lowerVTableGetTypeInfo(cir::VTableGetTypeInfoOp op,
775 mlir::OpBuilder &builder) const {
776 mlir::Location loc = op->getLoc();
777 auto offset = cir::ConstantOp::create(
778 builder, op->getLoc(), cir::IntAttr::get(getPtrDiffCIRTy(lm), -1));
779
780 // Cast the vptr to type_info-ptr, so that we can go backwards 1 pointer.
781 auto vptrCast = cir::CastOp::create(builder, loc, op.getType(),
782 cir::CastKind::bitcast, op.getVptr());
783
784 return cir::PtrStrideOp::create(builder, loc, vptrCast.getType(), vptrCast,
785 offset)
786 .getResult();
787}
788
789clang::CharUnits LowerItaniumCXXABI::getArrayCookieSizeImpl(
790 mlir::Type elementType, const mlir::DataLayout &dataLayout) const {
791 // The array cookie is a size_t; pad that up to the element alignment.
792 // The cookie is actually right-justified in that space.
793 clang::CharUnits sizeOfSizeT =
794 clang::CharUnits::fromQuantity(getPtrSizeInBits() / 8);
795 clang::CharUnits eltAlign = clang::CharUnits::fromQuantity(
796 dataLayout.getTypePreferredAlignment(elementType));
797 return std::max(sizeOfSizeT, eltAlign);
798}
799
800mlir::Value LowerItaniumCXXABI::readArrayCookieImpl(
801 mlir::Location loc, mlir::Value allocPtr, clang::CharUnits cookieSize,
802 clang::CharUnits cookieAlignment, const mlir::DataLayout &dataLayout,
803 CIRBaseBuilderTy &builder) const {
804 unsigned ptrSizeInBits = getPtrSizeInBits();
805 auto u8PtrTy = builder.getPointerTo(builder.getUIntNTy(8));
806 auto ptrDiffTy = builder.getSIntNTy(ptrSizeInBits);
807 auto sizeTy = builder.getUIntNTy(ptrSizeInBits);
808
809 // The element count is right-justified in the cookie.
810 clang::CharUnits sizeOfSizeT =
811 clang::CharUnits::fromQuantity(ptrSizeInBits / 8);
812 clang::CharUnits countOffset = cookieSize - sizeOfSizeT;
813
814 mlir::Value countBytePtr = allocPtr;
815 clang::CharUnits countAlignment = cookieAlignment;
816 if (!countOffset.isZero()) {
817 mlir::Value offsetVal = cir::ConstantOp::create(
818 builder, loc, cir::IntAttr::get(ptrDiffTy, countOffset.getQuantity()));
819 countBytePtr =
820 cir::PtrStrideOp::create(builder, loc, u8PtrTy, allocPtr, offsetVal);
821 countAlignment = cookieAlignment.alignmentAtOffset(countOffset);
822 }
823
824 auto countPtrTy = cir::PointerType::get(sizeTy);
825 mlir::Value countPtr = cir::CastOp::create(
826 builder, loc, countPtrTy, cir::CastKind::bitcast, countBytePtr);
827 return cir::LoadOp::create(
828 builder, loc, countPtr, /*isDeref=*/false, /*isVolatile=*/false,
829 builder.getI64IntegerAttr(countAlignment.getQuantity()),
830 cir::SyncScopeKindAttr(), cir::MemOrderAttr());
831}
832
833} // 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 void buildBadCastCall(mlir::OpBuilder &builder, mlir::Location loc, mlir::FlatSymbolRefAttr badCastFuncRef)
static mlir::Value lowerMethodCast(mlir::Operation *op, mlir::Value loweredSrc, std::int64_t offset, bool isDerivedToBase, LowerModule &lowerMod, mlir::OpBuilder &builder)
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 opFuncNoReturn()
static bool emitWPDInfo()
static bool emitTypeCheck()
IntType
===-— Target Data Type Query Methods ----------------------------—===//
Definition TargetInfo.h:147