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