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