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