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