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