[clang] [CIR] Upstream support for calling through method pointers (PR #176063)
Andy Kaylor via cfe-commits
cfe-commits at lists.llvm.org
Fri Jan 16 14:18:17 PST 2026
https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/176063
>From e34e57207761112870da5cc0eed122c646cf9064 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Wed, 14 Jan 2026 15:01:08 -0800
Subject: [PATCH 1/3] [CIR] Upstream support for calling through method
pointers
This adds support to CIR for calling functions through pointer to method
pointers with the Itanium ABI for x86_64 targets. The ARM-specific handling
of method pointers is not-yet implemented.
---
.../CIR/Dialect/Builder/CIRBaseBuilder.h | 30 ++++
clang/include/clang/CIR/Dialect/IR/CIROps.td | 54 ++++++
.../CIR/Dialect/IR/CIRTypeConstraints.td | 8 +
clang/include/clang/CIR/MissingFeatures.h | 6 +
clang/lib/CIR/CodeGen/CIRGenCall.h | 2 +
clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 7 +-
clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp | 44 +++++
clang/lib/CIR/CodeGen/CIRGenFunction.h | 3 +
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 47 ++++++
.../CIR/Dialect/Transforms/CXXABILowering.cpp | 12 +-
.../Transforms/TargetLowering/CIRCXXABI.h | 8 +
.../TargetLowering/LowerItaniumCXXABI.cpp | 154 +++++++++++++++++-
.../CIR/CodeGen/pointer-to-member-func.cpp | 84 ++++++++++
13 files changed, 451 insertions(+), 8 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index eadf3dd6ee0f0..2aaae86240cf2 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -710,6 +710,36 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
cir::YieldOp createYield(mlir::Location loc, mlir::ValueRange value = {}) {
return cir::YieldOp::create(*this, loc, value);
}
+
+ struct GetMethodResults {
+ mlir::Value callee;
+ mlir::Value adjustedThis;
+ };
+
+ GetMethodResults createGetMethod(mlir::Location loc, mlir::Value method,
+ mlir::Value objectPtr) {
+ // Build the callee function type.
+ auto methodFuncTy =
+ mlir::cast<cir::MethodType>(method.getType()).getMemberFuncTy();
+ auto methodFuncInputTypes = methodFuncTy.getInputs();
+
+ auto objectPtrTy = mlir::cast<cir::PointerType>(objectPtr.getType());
+ mlir::Type adjustedThisTy = getVoidPtrTy(objectPtrTy.getAddrSpace());
+
+ llvm::SmallVector<mlir::Type> calleeFuncInputTypes{adjustedThisTy};
+ calleeFuncInputTypes.insert(calleeFuncInputTypes.end(),
+ methodFuncInputTypes.begin(),
+ methodFuncInputTypes.end());
+ cir::FuncType calleeFuncTy =
+ methodFuncTy.clone(calleeFuncInputTypes, methodFuncTy.getReturnType());
+ // TODO(cir): consider the address space of the callee.
+ assert(!cir::MissingFeatures::addressSpace());
+ cir::PointerType calleeTy = getPointerTo(calleeFuncTy);
+
+ auto op = cir::GetMethodOp::create(*this, loc, calleeTy, adjustedThisTy,
+ method, objectPtr);
+ return {op.getCallee(), op.getAdjustedThis()};
+ }
};
} // namespace cir
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index ceb9899a00ac4..07950d6e42f63 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3805,6 +3805,60 @@ def CIR_GetRuntimeMemberOp : CIR_Op<"get_runtime_member"> {
let hasLLVMLowering = false;
}
+//===----------------------------------------------------------------------===//
+// GetMethodOp
+//===----------------------------------------------------------------------===//
+
+def CIR_GetMethodOp : CIR_Op<"get_method"> {
+ let summary = "Resolve a method to a function pointer as callee";
+ let description = [{
+ The `cir.get_method` operation takes a pointer to method (!cir.method) and
+ a pointer to a class object (!cir.ptr<!cir.record>>) as input, and
+ yields a function pointer that points to the actual function corresponding
+ to the input method. The operation also applies any necessary adjustments to
+ the input object pointer for calling the method and yields the adjusted
+ pointer.
+
+ This operation is generated when calling a method through a pointer-to-
+ member-function in C++:
+
+ ```cpp
+ // Foo *object;
+ // int arg;
+ // void (Foo::*method)(int);
+
+ (object->*method)(arg);
+ ```
+
+ The code above will generate CIR similar to:
+
+ ```mlir
+ %callee, %this = cir.get_method %method, %object
+ cir.call %callee(%this, %arg)
+ ```
+
+ The method type must match the callee type. That is:
+ - The return type of the method must match the return type of the callee.
+ - The first parameter of the callee must have type `!cir.ptr<!cir.void>`.
+ - Types of other parameters of the callee must match the parameters of the
+ method.
+ }];
+
+ let arguments = (ins CIR_MethodType:$method, CIR_PtrToRecordType:$object);
+ let results = (outs CIR_PtrToFunc:$callee, CIR_VoidPtrType:$adjusted_this);
+
+ let assemblyFormat = [{
+ $method `,` $object
+ `:` `(` qualified(type($method)) `,` qualified(type($object)) `)`
+ `->` `(` qualified(type($callee)) `,` qualified(type($adjusted_this)) `)`
+ attr-dict
+ }];
+
+ let hasVerifier = 1;
+ let hasLLVMLowering = false;
+ let hasCXXABILowering = true;
+}
+
//===----------------------------------------------------------------------===//
// VecCreate
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td b/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
index 3b2ec5276a677..1a5bae13c96df 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
@@ -191,6 +191,12 @@ def CIR_AnyComplexOrIntOrBoolOrFloatType
def CIR_AnyRecordType : CIR_TypeBase<"::cir::RecordType", "record type">;
+//===----------------------------------------------------------------------===//
+// Function Type predicates
+//===----------------------------------------------------------------------===//
+
+def CIR_AnyFuncType : CIR_TypeBase<"::cir::FuncType", "function type">;
+
//===----------------------------------------------------------------------===//
// Array Type predicates
//===----------------------------------------------------------------------===//
@@ -253,6 +259,8 @@ def CIR_PtrToComplexType : CIR_PtrToType<CIR_AnyComplexType>;
def CIR_PtrToRecordType : CIR_PtrToType<CIR_AnyRecordType>;
+def CIR_PtrToFunc : CIR_PtrToType<CIR_AnyFuncType>;
+
def CIR_PtrToArray : CIR_PtrToType<CIR_AnyArrayType>;
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 9336fafea04cd..70b92757a4eee 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -194,6 +194,11 @@ struct MissingFeatures {
static bool lowerModuleCodeGenOpts() { return false; }
static bool lowerModuleLangOpts() { return false; }
+ // Extra checks for lowerGetMethod in ItaniumCXXABI
+ static bool emitCFICheck() { return false; }
+ static bool emitVFEInfo() { return false; }
+ static bool emitWPDInfo() { return false; }
+
// Misc
static bool aarch64SIMDIntrinsics() { return false; }
static bool aarch64SMEIntrinsics() { return false; }
@@ -211,6 +216,7 @@ struct MissingFeatures {
static bool aggValueSlotVolatile() { return false; }
static bool alignCXXRecordDecl() { return false; }
static bool allocToken() { return false; }
+ static bool appleArm64CXXABI() { return false; }
static bool appleKext() { return false; }
static bool armComputeVolatileBitfields() { return false; }
static bool asmGoto() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.h b/clang/lib/CIR/CodeGen/CIRGenCall.h
index 55b3d9765c5c5..347bd4a7c8266 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.h
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.h
@@ -33,6 +33,8 @@ class CIRGenCalleeInfo {
CIRGenCalleeInfo(const clang::FunctionProtoType *calleeProtoTy,
clang::GlobalDecl calleeDecl)
: calleeProtoTy(calleeProtoTy), calleeDecl(calleeDecl) {}
+ CIRGenCalleeInfo(const clang::FunctionProtoType *calleeProtoTy)
+ : calleeProtoTy(calleeProtoTy) {}
CIRGenCalleeInfo(clang::GlobalDecl calleeDecl)
: calleeProtoTy(nullptr), calleeDecl(calleeDecl) {}
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index cd13498e3702f..ca6357e2ba138 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -2303,11 +2303,8 @@ RValue CIRGenFunction::emitCXXMemberCallExpr(const CXXMemberCallExpr *ce,
ReturnValueSlot returnValue) {
const Expr *callee = ce->getCallee()->IgnoreParens();
- if (isa<BinaryOperator>(callee)) {
- cgm.errorNYI(ce->getSourceRange(),
- "emitCXXMemberCallExpr: C++ binary operator");
- return RValue::get(nullptr);
- }
+ if (isa<BinaryOperator>(callee))
+ return emitCXXMemberPointerCallExpr(ce, returnValue);
const auto *me = cast<MemberExpr>(callee);
const auto *md = cast<CXXMethodDecl>(me->getMemberDecl());
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index eb894c2fb30ee..98cf75f0d69e0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -75,6 +75,50 @@ static MemberCallInfo commonBuildCXXMemberOrOperatorCall(
return {required, prefixSize};
}
+RValue
+CIRGenFunction::emitCXXMemberPointerCallExpr(const CXXMemberCallExpr *ce,
+ ReturnValueSlot returnValue) {
+ const BinaryOperator *bo =
+ cast<BinaryOperator>(ce->getCallee()->IgnoreParens());
+ const Expr *baseExpr = bo->getLHS();
+ const Expr *memFnExpr = bo->getRHS();
+
+ const auto *mpt = memFnExpr->getType()->castAs<MemberPointerType>();
+ const auto *fpt = mpt->getPointeeType()->castAs<FunctionProtoType>();
+
+ // Emit the 'this' pointer.
+ Address thisAddr = Address::invalid();
+ if (bo->getOpcode() == BO_PtrMemI)
+ thisAddr = emitPointerWithAlignment(baseExpr);
+ else
+ thisAddr = emitLValue(baseExpr).getAddress();
+
+ assert(!cir::MissingFeatures::emitTypeCheck());
+
+ // Get the member function pointer.
+ mlir::Value memFnPtr = emitScalarExpr(memFnExpr);
+
+ // Resolve the member function pointer to the actual callee and adjust the
+ // "this" pointer for call.
+ mlir::Location loc = getLoc(ce->getExprLoc());
+ auto [/*mlir::Value*/ calleePtr, /*mlir::Value*/ adjustedThis] =
+ builder.createGetMethod(loc, memFnPtr, thisAddr.getPointer());
+
+ // Prepare the call arguments.
+ CallArgList argsList;
+ argsList.add(RValue::get(adjustedThis), getContext().VoidPtrTy);
+ emitCallArgs(argsList, fpt, ce->arguments());
+
+ RequiredArgs required = RequiredArgs::getFromProtoWithExtraSlots(fpt, 1);
+
+ // Build the call.
+ CIRGenCallee callee(fpt, calleePtr.getDefiningOp());
+ assert(!cir::MissingFeatures::opCallMustTail());
+ return emitCall(cgm.getTypes().arrangeCXXMethodCall(argsList, fpt, required,
+ /*PrefixSize=*/0),
+ callee, returnValue, argsList, nullptr, loc);
+}
+
RValue CIRGenFunction::emitCXXMemberOrOperatorMemberCallExpr(
const CallExpr *ce, const CXXMethodDecl *md, ReturnValueSlot returnValue,
bool hasQualifier, NestedNameSpecifier qualifier, bool isArrow,
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 5fe1d9a4f2b76..6b47bc9975fcf 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1553,6 +1553,9 @@ class CIRGenFunction : public CIRGenTypeCache {
clang::NestedNameSpecifier qualifier, bool isArrow,
const clang::Expr *base);
+ RValue emitCXXMemberPointerCallExpr(const CXXMemberCallExpr *ce,
+ ReturnValueSlot returnValue);
+
mlir::Value emitCXXNewExpr(const CXXNewExpr *e);
void emitNewArrayInitializer(const CXXNewExpr *e, QualType elementType,
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 6c4607abb40e7..1dec653e421c4 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -2666,6 +2666,53 @@ LogicalResult cir::GetRuntimeMemberOp::verify() {
return mlir::success();
}
+//===----------------------------------------------------------------------===//
+// GetMethodOp Definitions
+//===----------------------------------------------------------------------===//
+
+LogicalResult cir::GetMethodOp::verify() {
+ cir::MethodType methodTy = getMethod().getType();
+
+ // Assume objectTy is !cir.ptr<!T>
+ cir::PointerType objectPtrTy = getObject().getType();
+ mlir::Type objectTy = objectPtrTy.getPointee();
+
+ if (methodTy.getClassTy() != objectTy)
+ return emitError() << "method class type and object type do not match";
+
+ // Assume methodFuncTy is !cir.func<!Ret (!Args)>
+ auto calleeTy = mlir::cast<cir::FuncType>(getCallee().getType().getPointee());
+ cir::FuncType methodFuncTy = methodTy.getMemberFuncTy();
+
+ // We verify at here that calleeTy is !cir.func<!Ret (!cir.ptr<!void>, !Args)>
+ // Note that the first parameter type of the callee is !cir.ptr<!void> instead
+ // of !cir.ptr<!T> because the "this" pointer may be adjusted before calling
+ // the callee.
+
+ if (methodFuncTy.getReturnType() != calleeTy.getReturnType())
+ return emitError()
+ << "method return type and callee return type do not match";
+
+ llvm::ArrayRef<mlir::Type> calleeArgsTy = calleeTy.getInputs();
+ llvm::ArrayRef<mlir::Type> methodFuncArgsTy = methodFuncTy.getInputs();
+
+ if (calleeArgsTy.empty())
+ return emitError() << "callee parameter list lacks receiver object ptr";
+
+ auto calleeThisArgPtrTy = mlir::dyn_cast<cir::PointerType>(calleeArgsTy[0]);
+ if (!calleeThisArgPtrTy ||
+ !mlir::isa<cir::VoidType>(calleeThisArgPtrTy.getPointee())) {
+ return emitError()
+ << "the first parameter of callee must be a void pointer";
+ }
+
+ if (calleeArgsTy.slice(1) != methodFuncArgsTy)
+ return emitError()
+ << "callee parameters and method parameters do not match";
+
+ return mlir::success();
+}
+
//===----------------------------------------------------------------------===//
// GetMemberOp Definitions
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
index 145f8574893f4..469dddbb118e3 100644
--- a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
@@ -59,7 +59,7 @@ class CIRGenericCXXABILoweringPattern : public mlir::ConversionPattern {
// Do not match on operations that have dedicated ABI lowering rewrite rules
if (llvm::isa<cir::AllocaOp, cir::BaseDataMemberOp, cir::ConstantOp,
cir::CmpOp, cir::DerivedDataMemberOp, cir::FuncOp,
- cir::GetRuntimeMemberOp, cir::GlobalOp>(op))
+ cir::GetMethodOp, cir::GetRuntimeMemberOp, cir::GlobalOp>(op))
return mlir::failure();
const mlir::TypeConverter *typeConverter = getTypeConverter();
@@ -252,6 +252,16 @@ mlir::LogicalResult CIRDerivedDataMemberOpABILowering::matchAndRewrite(
return mlir::success();
}
+mlir::LogicalResult CIRGetMethodOpABILowering::matchAndRewrite(
+ cir::GetMethodOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ mlir::Value loweredResults[2];
+ lowerModule->getCXXABI().lowerGetMethod(
+ op, loweredResults, adaptor.getMethod(), adaptor.getObject(), rewriter);
+ rewriter.replaceOp(op, loweredResults);
+ return mlir::success();
+}
+
mlir::LogicalResult CIRGetRuntimeMemberOpABILowering::matchAndRewrite(
cir::GetRuntimeMemberOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h
index 0dedfa7221f5f..69d8b682ab4c5 100644
--- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h
+++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h
@@ -66,6 +66,14 @@ class CIRCXXABI {
mlir::Value loweredAddr, mlir::Value loweredMember,
mlir::OpBuilder &builder) const = 0;
+ /// Lower the given cir.get_method op to a sequence of more "primitive" CIR
+ /// operations that act on the ABI types. The lowered result values will be
+ /// stored in the given loweredResults array.
+ virtual void
+ lowerGetMethod(cir::GetMethodOp op, mlir::Value (&loweredResults)[2],
+ mlir::Value loweredMethod, mlir::Value loweredObjectPtr,
+ mlir::ConversionPatternRewriter &rewriter) const = 0;
+
/// Lower the given cir.base_data_member op to a sequence of more "primitive"
/// CIR operations that act on the ABI types.
virtual mlir::Value lowerBaseDataMember(cir::BaseDataMemberOp op,
diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
index d944fa3294684..d581e18b5e37a 100644
--- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
@@ -30,8 +30,12 @@ namespace cir {
namespace {
class LowerItaniumCXXABI : public CIRCXXABI {
+protected:
+ bool useARMMethodPtrABI;
+
public:
- LowerItaniumCXXABI(LowerModule &lm) : CIRCXXABI(lm) {}
+ LowerItaniumCXXABI(LowerModule &lm, bool useARMMethodPtrABI = false)
+ : CIRCXXABI(lm), useARMMethodPtrABI(useARMMethodPtrABI) {}
/// Lower the given data member pointer type to its ABI type. The returned
/// type is also a CIR type.
@@ -56,6 +60,10 @@ class LowerItaniumCXXABI : public CIRCXXABI {
mlir::Value loweredAddr, mlir::Value loweredMember,
mlir::OpBuilder &builder) const override;
+ void lowerGetMethod(cir::GetMethodOp op, mlir::Value (&loweredResults)[2],
+ mlir::Value loweredMethod, mlir::Value loweredObjectPtr,
+ mlir::ConversionPatternRewriter &rewriter) const override;
+
mlir::Value lowerBaseDataMember(cir::BaseDataMemberOp op,
mlir::Value loweredSrc,
mlir::OpBuilder &builder) const override;
@@ -72,7 +80,26 @@ class LowerItaniumCXXABI : public CIRCXXABI {
} // namespace
std::unique_ptr<CIRCXXABI> createItaniumCXXABI(LowerModule &lm) {
- return std::make_unique<LowerItaniumCXXABI>(lm);
+ switch (lm.getCXXABIKind()) {
+ // Note that AArch64 uses the generic ItaniumCXXABI class since it doesn't
+ // include the other 32-bit ARM oddities: constructor/destructor return values
+ // and array cookies.
+ case clang::TargetCXXABI::GenericAArch64:
+ case clang::TargetCXXABI::AppleARM64:
+ // TODO: this isn't quite right, clang uses AppleARM64CXXABI which inherits
+ // from ARMCXXABI. We'll have to follow suit.
+ assert(!cir::MissingFeatures::appleArm64CXXABI());
+ return std::make_unique<LowerItaniumCXXABI>(lm,
+ /*useARMMethodPtrABI=*/true);
+
+ case clang::TargetCXXABI::GenericItanium:
+ return std::make_unique<LowerItaniumCXXABI>(lm);
+
+ case clang::TargetCXXABI::Microsoft:
+ llvm_unreachable("Microsoft ABI is not Itanium-based");
+ default:
+ llvm_unreachable("Other Itanium ABI?");
+ }
}
static cir::IntType getPtrDiffCIRTy(LowerModule &lm) {
@@ -197,6 +224,129 @@ mlir::Operation *LowerItaniumCXXABI::lowerGetRuntimeMember(
cir::CastKind::bitcast, memberBytesPtr);
}
+void LowerItaniumCXXABI::lowerGetMethod(
+ cir::GetMethodOp op, mlir::Value (&loweredResults)[2],
+ mlir::Value loweredMethod, mlir::Value loweredObjectPtr,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ // In the Itanium and ARM ABIs, method pointers have the form:
+ // struct { ptrdiff_t ptr; ptrdiff_t adj; } memptr;
+ //
+ // In the Itanium ABI:
+ // - method pointers are virtual if (memptr.ptr & 1) is nonzero
+ // - the this-adjustment is (memptr.adj)
+ // - the virtual offset is (memptr.ptr - 1)
+ //
+ // In the ARM ABI:
+ // - method pointers are virtual if (memptr.adj & 1) is nonzero
+ // - the this-adjustment is (memptr.adj >> 1)
+ // - the virtual offset is (memptr.ptr)
+ // ARM uses 'adj' for the virtual flag because Thumb functions
+ // may be only single-byte aligned.
+ //
+ // If the member is virtual, the adjusted 'this' pointer points
+ // to a vtable pointer from which the virtual offset is applied.
+ //
+ // If the member is non-virtual, memptr.ptr is the address of
+ // the function to call.
+
+ mlir::Value &callee = loweredResults[0];
+ mlir::Value &adjustedThis = loweredResults[1];
+ mlir::Type calleePtrTy = op.getCallee().getType();
+
+ cir::IntType ptrdiffCIRTy = getPtrDiffCIRTy(lm);
+ mlir::Value ptrdiffOne = cir::ConstantOp::create(
+ rewriter, op.getLoc(), cir::IntAttr::get(ptrdiffCIRTy, 1));
+
+ mlir::Value adj = cir::ExtractMemberOp::create(
+ rewriter, op.getLoc(), ptrdiffCIRTy, loweredMethod, 1);
+ if (useARMMethodPtrABI) {
+ op.emitError("ARM method ptr abi NYI");
+ return;
+ }
+
+ // Apply the adjustment to the 'this' pointer.
+ mlir::Type thisVoidPtrTy =
+ cir::PointerType::get(cir::VoidType::get(rewriter.getContext()),
+ op.getObject().getType().getAddrSpace());
+ mlir::Value thisVoidPtr =
+ cir::CastOp::create(rewriter, op.getLoc(), thisVoidPtrTy,
+ cir::CastKind::bitcast, loweredObjectPtr);
+ adjustedThis = cir::PtrStrideOp::create(rewriter, op.getLoc(), thisVoidPtrTy,
+ thisVoidPtr, adj);
+
+ // Load the "ptr" field of the member function pointer and determine if it
+ // points to a virtual function.
+ mlir::Value methodPtrField = cir::ExtractMemberOp::create(
+ rewriter, op.getLoc(), ptrdiffCIRTy, loweredMethod, 0);
+ mlir::Value virtualBit = cir::BinOp::create(
+ rewriter, op.getLoc(), cir::BinOpKind::And, methodPtrField, ptrdiffOne);
+ mlir::Value isVirtual;
+ if (useARMMethodPtrABI)
+ llvm_unreachable("ARM method ptr abi NYI");
+ else
+ isVirtual = cir::CmpOp::create(rewriter, op.getLoc(), cir::CmpOpKind::eq,
+ virtualBit, ptrdiffOne);
+
+ assert(!cir::MissingFeatures::emitCFICheck());
+ assert(!cir::MissingFeatures::emitVFEInfo());
+ assert(!cir::MissingFeatures::emitWPDInfo());
+
+ auto buildVirtualCallee = [&](mlir::OpBuilder &b, mlir::Location loc) {
+ // Load vtable pointer.
+ // Note that vtable pointer always point to the global address space.
+ auto vtablePtrTy =
+ cir::PointerType::get(cir::IntType::get(b.getContext(), 8, true));
+ auto vtablePtrPtrTy = cir::PointerType::get(
+ vtablePtrTy, op.getObject().getType().getAddrSpace());
+ auto vtablePtrPtr = cir::CastOp::create(
+ b, loc, vtablePtrPtrTy, cir::CastKind::bitcast, adjustedThis);
+ assert(!cir::MissingFeatures::opTBAA());
+ mlir::Value vtablePtr =
+ cir::LoadOp::create(b, loc, vtablePtrPtr, /*isDeref=*/false,
+ /*isVolatile=*/false,
+ /*alignment=*/mlir::IntegerAttr(),
+ /*sync_scope=*/cir::SyncScopeKindAttr{},
+ /*mem_order=*/cir::MemOrderAttr());
+
+ // Get the vtable offset.
+ mlir::Value vtableOffset = methodPtrField;
+ assert(!useARMMethodPtrABI && "ARM method ptr abi NYI");
+ vtableOffset = cir::BinOp::create(b, loc, cir::BinOpKind::Sub, vtableOffset,
+ ptrdiffOne);
+
+ assert(!cir::MissingFeatures::emitCFICheck());
+ assert(!cir::MissingFeatures::emitVFEInfo());
+ assert(!cir::MissingFeatures::emitWPDInfo());
+
+ // Apply the offset to the vtable pointer and get the pointer to the target
+ // virtual function. Then load that pointer to get the callee.
+ mlir::Value vfpAddr = cir::PtrStrideOp::create(
+ rewriter, op.getLoc(), vtablePtrTy, vtablePtr, vtableOffset);
+ auto vfpPtrTy = cir::PointerType::get(calleePtrTy);
+ mlir::Value vfpPtr = cir::CastOp::create(rewriter, op.getLoc(), vfpPtrTy,
+ cir::CastKind::bitcast, vfpAddr);
+ auto fnPtr = cir::LoadOp::create(b, loc, vfpPtr,
+ /*isDeref=*/false, /*isVolatile=*/false,
+ /*alignment=*/mlir::IntegerAttr(),
+ /*sync_scope=*/cir::SyncScopeKindAttr{},
+ /*mem_order=*/cir::MemOrderAttr());
+
+ cir::YieldOp::create(b, loc, fnPtr.getResult());
+ assert(!cir::MissingFeatures::emitCFICheck());
+ };
+
+ callee =
+ cir::TernaryOp::create(
+ rewriter, op.getLoc(), isVirtual, /*thenBuilder=*/buildVirtualCallee,
+ /*elseBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ auto fnPtr = cir::CastOp::create(
+ b, loc, calleePtrTy, cir::CastKind::int_to_ptr, methodPtrField);
+ cir::YieldOp::create(b, loc, fnPtr.getResult());
+ })
+ .getResult();
+}
+
static mlir::Value lowerDataMemberCast(mlir::Operation *op,
mlir::Value loweredSrc,
std::int64_t offset,
diff --git a/clang/test/CIR/CodeGen/pointer-to-member-func.cpp b/clang/test/CIR/CodeGen/pointer-to-member-func.cpp
index 47c5871e72290..ad081d0d06dbc 100644
--- a/clang/test/CIR/CodeGen/pointer-to-member-func.cpp
+++ b/clang/test/CIR/CodeGen/pointer-to-member-func.cpp
@@ -38,3 +38,87 @@ auto make_non_virtual() -> void (Foo::*)(int) {
// OGCG: define {{.*}} { i64, i64 } @_Z16make_non_virtualv()
// OGCG: ret { i64, i64 } { i64 ptrtoint (ptr @_ZN3Foo2m1Ei to i64), i64 0 }
+
+void call(Foo *obj, void (Foo::*func)(int), int arg) {
+ (obj->*func)(arg);
+}
+
+// CIR-BEFORE: cir.func {{.*}} @_Z4callP3FooMS_FviEi
+// CIR-BEFORE: %[[OBJ:.*]] = cir.load{{.*}} %{{.*}} : !cir.ptr<!cir.ptr<!rec_Foo>>, !cir.ptr<!rec_Foo>
+// CIR-BEFORE: %[[FUNC:.*]] = cir.load{{.*}} : !cir.ptr<!cir.method<!cir.func<(!s32i)> in !rec_Foo>>, !cir.method<!cir.func<(!s32i)> in !rec_Foo>
+// CIR-BEFORE: %[[CALLEE:.*]], %[[THIS:.*]] = cir.get_method %[[FUNC]], %[[OBJ]] : (!cir.method<!cir.func<(!s32i)> in !rec_Foo>, !cir.ptr<!rec_Foo>) -> (!cir.ptr<!cir.func<(!cir.ptr<!void>, !s32i)>>, !cir.ptr<!void>)
+// CIR-BEFORE: %[[ARG:.*]] = cir.load{{.*}} %{{.*}} : !cir.ptr<!s32i>, !s32i
+// CIR-BEFORE: cir.call %[[CALLEE]](%[[THIS]], %[[ARG]]) : (!cir.ptr<!cir.func<(!cir.ptr<!void>, !s32i)>>, !cir.ptr<!void>, !s32i) -> ()
+
+// CIR-AFTER: cir.func {{.*}} @_Z4callP3FooMS_FviEi
+// CIR-AFTER: %[[OBJ:.*]] = cir.load{{.*}} %{{.*}} : !cir.ptr<!cir.ptr<!rec_Foo>>, !cir.ptr<!rec_Foo>
+// CIR-AFTER: %[[FUNC:.*]] = cir.load{{.*}} : !cir.ptr<!rec_anon_struct>, !rec_anon_struct
+// CIR-AFTER: %[[VIRT_BIT:.*]] = cir.const #cir.int<1> : !s64i
+// CIR-AFTER: %[[ADJ:.*]] = cir.extract_member %[[FUNC]][1] : !rec_anon_struct -> !s64i
+// CIR-AFTER: %[[THIS:.*]] = cir.cast bitcast %[[OBJ]] : !cir.ptr<!rec_Foo> -> !cir.ptr<!void>
+// CIR-AFTER: %[[ADJUSTED_THIS:.*]] = cir.ptr_stride %[[THIS]], %[[ADJ]] : (!cir.ptr<!void>, !s64i) -> !cir.ptr<!void>
+// CIR-AFTER: %[[METHOD_PTR:.*]] = cir.extract_member %[[FUNC]][0] : !rec_anon_struct -> !s64i
+// CIR-AFTER: %[[VIRT_BIT_TEST:.*]] = cir.binop(and, %[[METHOD_PTR]], %[[VIRT_BIT]]) : !s64i
+// CIR-AFTER: %[[IS_VIRTUAL:.*]] = cir.cmp(eq, %[[VIRT_BIT_TEST]], %[[VIRT_BIT]]) : !s64i, !cir.bool
+// CIR-AFTER: %[[CALLEE:.*]] = cir.ternary(%[[IS_VIRTUAL]], true {
+// CIR-AFTER: %[[VTABLE_PTR:.*]] = cir.cast bitcast %[[ADJUSTED_THIS]] : !cir.ptr<!void> -> !cir.ptr<!cir.ptr<!s8i>>
+// CIR-AFTER: %[[VTABLE:.*]] = cir.load %[[VTABLE_PTR]] : !cir.ptr<!cir.ptr<!s8i>>, !cir.ptr<!s8i>
+// CIR-AFTER: %[[OFFSET:.*]] = cir.binop(sub, %[[METHOD_PTR]], %[[VIRT_BIT]]) : !s64i
+// CIR-AFTER: %[[VTABLE_SLOT:.*]] = cir.ptr_stride %[[VTABLE]], %[[OFFSET]] : (!cir.ptr<!s8i>, !s64i) -> !cir.ptr<!s8i>
+// CIR-AFTER: %[[VIRTUAL_FN_PTR:.*]] = cir.cast bitcast %[[VTABLE_SLOT]] : !cir.ptr<!s8i> -> !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!void>, !s32i)>>>
+// CIR-AFTER: %[[VIRTUAL_FN_PTR_LOAD:.*]] = cir.load %[[VIRTUAL_FN_PTR]] : !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!void>, !s32i)>>>, !cir.ptr<!cir.func<(!cir.ptr<!void>, !s32i)>>
+// CIR-AFTER: cir.yield %[[VIRTUAL_FN_PTR_LOAD]] : !cir.ptr<!cir.func<(!cir.ptr<!void>, !s32i)>>
+// CIR-AFTER: }, false {
+// CIR-AFTER: %[[CALLEE_PTR:.*]] = cir.cast int_to_ptr %[[METHOD_PTR]] : !s64i -> !cir.ptr<!cir.func<(!cir.ptr<!void>, !s32i)>>
+// CIR-AFTER: cir.yield %[[CALLEE_PTR]] : !cir.ptr<!cir.func<(!cir.ptr<!void>, !s32i)>>
+// CIR-AFTER: }) : (!cir.bool) -> !cir.ptr<!cir.func<(!cir.ptr<!void>, !s32i)>>
+// CIR-AFTER: %[[ARG:.*]] = cir.load{{.*}} %{{.*}} : !cir.ptr<!s32i>, !s32i
+// CIR-AFTER: cir.call %[[CALLEE]](%[[ADJUSTED_THIS]], %[[ARG]]) : (!cir.ptr<!cir.func<(!cir.ptr<!void>, !s32i)>>, !cir.ptr<!void>, !s32i) -> ()
+
+// LLVM: define {{.*}} @_Z4callP3FooMS_FviEi
+// LLVM: %[[OBJ:.*]] = load ptr, ptr %{{.*}}
+// LLVM: %[[MEMFN_PTR:.*]] = load { i64, i64 }, ptr %{{.*}}
+// LLVM: %[[THIS_ADJ:.*]] = extractvalue { i64, i64 } %[[MEMFN_PTR]], 1
+// LLVM: %[[ADJUSTED_THIS:.*]] = getelementptr i8, ptr %[[OBJ]], i64 %[[THIS_ADJ]]
+// LLVM: %[[PTR_FIELD:.*]] = extractvalue { i64, i64 } %[[MEMFN_PTR]], 0
+// LLVM: %[[VIRT_BIT:.*]] = and i64 %[[PTR_FIELD]], 1
+// LLVM: %[[IS_VIRTUAL:.*]] = icmp eq i64 %[[VIRT_BIT]], 1
+// LLVM: br i1 %[[IS_VIRTUAL]], label %[[HANDLE_VIRTUAL:.*]], label %[[HANDLE_NON_VIRTUAL:.*]]
+// LLVM: [[HANDLE_VIRTUAL]]:
+// LLVM: %[[VTABLE:.*]] = load ptr, ptr %[[ADJUSTED_THIS]]
+// LLVM: %[[OFFSET:.*]] = sub i64 %[[PTR_FIELD]], 1
+// LLVM: %[[VTABLE_SLOT:.*]] = getelementptr i8, ptr %[[VTABLE]], i64 %[[OFFSET]]
+// LLVM: %[[VIRTUAL_FN_PTR:.*]] = load ptr, ptr %[[VTABLE_SLOT]]
+// LLVM: br label %[[CONTINUE:.*]]
+// LLVM: [[HANDLE_NON_VIRTUAL]]:
+// LLVM: %[[FUNC_PTR:.*]] = inttoptr i64 %[[PTR_FIELD]] to ptr
+// LLVM: br label %[[CONTINUE]]
+// LLVM: [[CONTINUE]]:
+// LLVM: %[[CALLEE_PTR:.*]] = phi ptr [ %[[FUNC_PTR]], %[[HANDLE_NON_VIRTUAL]] ], [ %[[VIRTUAL_FN_PTR]], %[[HANDLE_VIRTUAL]] ]
+// LLVM: %[[ARG:.*]] = load i32, ptr %{{.+}}
+// LLVM: call void %[[CALLEE_PTR]](ptr %[[ADJUSTED_THIS]], i32 %[[ARG]])
+// LLVM: }
+
+// OGCG: define {{.*}} @_Z4callP3FooMS_FviEi
+// OGCG: %[[OBJ:.*]] = load ptr, ptr %{{.*}}
+// OGCG: %[[MEMFN_PTR:.*]] = load { i64, i64 }, ptr %{{.*}}
+// OGCG: %[[THIS_ADJ:.*]] = extractvalue { i64, i64 } %[[MEMFN_PTR]], 1
+// OGCG: %[[ADJUSTED_THIS:.*]] = getelementptr inbounds i8, ptr %[[OBJ]], i64 %[[THIS_ADJ]]
+// OGCG: %[[PTR_FIELD:.*]] = extractvalue { i64, i64 } %[[MEMFN_PTR]], 0
+// OGCG: %[[VIRT_BIT:.*]] = and i64 %[[PTR_FIELD]], 1
+// OGCG: %[[IS_VIRTUAL:.*]] = icmp ne i64 %[[VIRT_BIT]], 0
+// OGCG: br i1 %[[IS_VIRTUAL]], label %[[HANDLE_VIRTUAL:.*]], label %[[HANDLE_NON_VIRTUAL:.*]]
+// OGCG: [[HANDLE_VIRTUAL]]:
+// OGCG: %[[VTABLE:.*]] = load ptr, ptr %[[ADJUSTED_THIS]]
+// OGCG: %[[OFFSET:.*]] = sub i64 %[[PTR_FIELD]], 1
+// OGCG: %[[VTABLE_SLOT:.*]] = getelementptr i8, ptr %[[VTABLE]], i64 %[[OFFSET]]
+// OGCG: %[[VIRTUAL_FN_PTR:.*]] = load ptr, ptr %[[VTABLE_SLOT]]
+// OGCG: br label %[[CONTINUE:.*]]
+// OGCG: [[HANDLE_NON_VIRTUAL]]:
+// OGCG: %[[FUNC_PTR:.*]] = inttoptr i64 %[[PTR_FIELD]] to ptr
+// OGCG: br label %[[CONTINUE]]
+// OGCG: [[CONTINUE]]:
+// OGCG: %[[CALLEE_PTR:.*]] = phi ptr [ %[[VIRTUAL_FN_PTR]], %[[HANDLE_VIRTUAL]] ], [ %[[FUNC_PTR]], %[[HANDLE_NON_VIRTUAL]] ]
+// OGCG: %[[ARG:.*]] = load i32, ptr %{{.+}}
+// OGCG: call void %[[CALLEE_PTR]](ptr {{.*}} %[[ADJUSTED_THIS]], i32 {{.*}} %[[ARG]])
+// OGCG: }
>From b2e55c2bdf32637c7e69b57dfcdd23e424fab1eb Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Fri, 16 Jan 2026 13:58:43 -0800
Subject: [PATCH 2/3] Use ImplicitLocOpBuilder
---
.../TargetLowering/LowerItaniumCXXABI.cpp | 37 ++++++++++---------
1 file changed, 19 insertions(+), 18 deletions(-)
diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
index d581e18b5e37a..80d29b977c549 100644
--- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
@@ -23,6 +23,7 @@
#include "CIRCXXABI.h"
#include "LowerModule.h"
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
+#include "mlir/IR/ImplicitLocOpBuilder.h"
#include "llvm/Support/ErrorHandling.h"
namespace cir {
@@ -249,16 +250,17 @@ void LowerItaniumCXXABI::lowerGetMethod(
// If the member is non-virtual, memptr.ptr is the address of
// the function to call.
+ mlir::ImplicitLocOpBuilder locBuilder(op.getLoc(), rewriter);
mlir::Value &callee = loweredResults[0];
mlir::Value &adjustedThis = loweredResults[1];
mlir::Type calleePtrTy = op.getCallee().getType();
cir::IntType ptrdiffCIRTy = getPtrDiffCIRTy(lm);
- mlir::Value ptrdiffOne = cir::ConstantOp::create(
- rewriter, op.getLoc(), cir::IntAttr::get(ptrdiffCIRTy, 1));
+ mlir::Value ptrdiffOne =
+ cir::ConstantOp::create(locBuilder, cir::IntAttr::get(ptrdiffCIRTy, 1));
- mlir::Value adj = cir::ExtractMemberOp::create(
- rewriter, op.getLoc(), ptrdiffCIRTy, loweredMethod, 1);
+ mlir::Value adj =
+ cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredMethod, 1);
if (useARMMethodPtrABI) {
op.emitError("ARM method ptr abi NYI");
return;
@@ -266,26 +268,25 @@ void LowerItaniumCXXABI::lowerGetMethod(
// Apply the adjustment to the 'this' pointer.
mlir::Type thisVoidPtrTy =
- cir::PointerType::get(cir::VoidType::get(rewriter.getContext()),
+ cir::PointerType::get(cir::VoidType::get(locBuilder.getContext()),
op.getObject().getType().getAddrSpace());
- mlir::Value thisVoidPtr =
- cir::CastOp::create(rewriter, op.getLoc(), thisVoidPtrTy,
- cir::CastKind::bitcast, loweredObjectPtr);
- adjustedThis = cir::PtrStrideOp::create(rewriter, op.getLoc(), thisVoidPtrTy,
- thisVoidPtr, adj);
+ mlir::Value thisVoidPtr = cir::CastOp::create(
+ locBuilder, thisVoidPtrTy, cir::CastKind::bitcast, loweredObjectPtr);
+ adjustedThis =
+ cir::PtrStrideOp::create(locBuilder, thisVoidPtrTy, thisVoidPtr, adj);
// Load the "ptr" field of the member function pointer and determine if it
// points to a virtual function.
- mlir::Value methodPtrField = cir::ExtractMemberOp::create(
- rewriter, op.getLoc(), ptrdiffCIRTy, loweredMethod, 0);
+ mlir::Value methodPtrField =
+ cir::ExtractMemberOp::create(locBuilder, ptrdiffCIRTy, loweredMethod, 0);
mlir::Value virtualBit = cir::BinOp::create(
rewriter, op.getLoc(), cir::BinOpKind::And, methodPtrField, ptrdiffOne);
mlir::Value isVirtual;
if (useARMMethodPtrABI)
llvm_unreachable("ARM method ptr abi NYI");
else
- isVirtual = cir::CmpOp::create(rewriter, op.getLoc(), cir::CmpOpKind::eq,
- virtualBit, ptrdiffOne);
+ isVirtual = cir::CmpOp::create(locBuilder, cir::CmpOpKind::eq, virtualBit,
+ ptrdiffOne);
assert(!cir::MissingFeatures::emitCFICheck());
assert(!cir::MissingFeatures::emitVFEInfo());
@@ -320,10 +321,10 @@ void LowerItaniumCXXABI::lowerGetMethod(
// Apply the offset to the vtable pointer and get the pointer to the target
// virtual function. Then load that pointer to get the callee.
- mlir::Value vfpAddr = cir::PtrStrideOp::create(
- rewriter, op.getLoc(), vtablePtrTy, vtablePtr, vtableOffset);
+ mlir::Value vfpAddr = cir::PtrStrideOp::create(locBuilder, vtablePtrTy,
+ vtablePtr, vtableOffset);
auto vfpPtrTy = cir::PointerType::get(calleePtrTy);
- mlir::Value vfpPtr = cir::CastOp::create(rewriter, op.getLoc(), vfpPtrTy,
+ mlir::Value vfpPtr = cir::CastOp::create(locBuilder, vfpPtrTy,
cir::CastKind::bitcast, vfpAddr);
auto fnPtr = cir::LoadOp::create(b, loc, vfpPtr,
/*isDeref=*/false, /*isVolatile=*/false,
@@ -337,7 +338,7 @@ void LowerItaniumCXXABI::lowerGetMethod(
callee =
cir::TernaryOp::create(
- rewriter, op.getLoc(), isVirtual, /*thenBuilder=*/buildVirtualCallee,
+ locBuilder, isVirtual, /*thenBuilder=*/buildVirtualCallee,
/*elseBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
auto fnPtr = cir::CastOp::create(
>From 7c06428caa5b8141410d6bcc39b3a90dc8cf138f Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Fri, 16 Jan 2026 14:14:37 -0800
Subject: [PATCH 3/3] Use separate args for lowerGetMethod callee and thisArg
---
.../lib/CIR/Dialect/Transforms/CXXABILowering.cpp | 7 ++++---
.../Dialect/Transforms/TargetLowering/CIRCXXABI.h | 2 +-
.../TargetLowering/LowerItaniumCXXABI.cpp | 15 +++++++--------
3 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
index 469dddbb118e3..a8d08fb90221c 100644
--- a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
@@ -255,10 +255,11 @@ mlir::LogicalResult CIRDerivedDataMemberOpABILowering::matchAndRewrite(
mlir::LogicalResult CIRGetMethodOpABILowering::matchAndRewrite(
cir::GetMethodOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
- mlir::Value loweredResults[2];
+ mlir::Value callee;
+ mlir::Value thisArg;
lowerModule->getCXXABI().lowerGetMethod(
- op, loweredResults, adaptor.getMethod(), adaptor.getObject(), rewriter);
- rewriter.replaceOp(op, loweredResults);
+ op, callee, thisArg, adaptor.getMethod(), adaptor.getObject(), rewriter);
+ rewriter.replaceOp(op, {callee, thisArg});
return mlir::success();
}
diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h
index 69d8b682ab4c5..b2166aac0b7bd 100644
--- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h
+++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h
@@ -70,7 +70,7 @@ class CIRCXXABI {
/// operations that act on the ABI types. The lowered result values will be
/// stored in the given loweredResults array.
virtual void
- lowerGetMethod(cir::GetMethodOp op, mlir::Value (&loweredResults)[2],
+ lowerGetMethod(cir::GetMethodOp op, mlir::Value &callee, mlir::Value &thisArg,
mlir::Value loweredMethod, mlir::Value loweredObjectPtr,
mlir::ConversionPatternRewriter &rewriter) const = 0;
diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
index 80d29b977c549..d22024d6dc573 100644
--- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
@@ -61,8 +61,9 @@ class LowerItaniumCXXABI : public CIRCXXABI {
mlir::Value loweredAddr, mlir::Value loweredMember,
mlir::OpBuilder &builder) const override;
- void lowerGetMethod(cir::GetMethodOp op, mlir::Value (&loweredResults)[2],
- mlir::Value loweredMethod, mlir::Value loweredObjectPtr,
+ void lowerGetMethod(cir::GetMethodOp op, mlir::Value &callee,
+ mlir::Value &thisArg, mlir::Value loweredMethod,
+ mlir::Value loweredObjectPtr,
mlir::ConversionPatternRewriter &rewriter) const override;
mlir::Value lowerBaseDataMember(cir::BaseDataMemberOp op,
@@ -226,7 +227,7 @@ mlir::Operation *LowerItaniumCXXABI::lowerGetRuntimeMember(
}
void LowerItaniumCXXABI::lowerGetMethod(
- cir::GetMethodOp op, mlir::Value (&loweredResults)[2],
+ cir::GetMethodOp op, mlir::Value &callee, mlir::Value &thisArg,
mlir::Value loweredMethod, mlir::Value loweredObjectPtr,
mlir::ConversionPatternRewriter &rewriter) const {
// In the Itanium and ARM ABIs, method pointers have the form:
@@ -251,8 +252,6 @@ void LowerItaniumCXXABI::lowerGetMethod(
// the function to call.
mlir::ImplicitLocOpBuilder locBuilder(op.getLoc(), rewriter);
- mlir::Value &callee = loweredResults[0];
- mlir::Value &adjustedThis = loweredResults[1];
mlir::Type calleePtrTy = op.getCallee().getType();
cir::IntType ptrdiffCIRTy = getPtrDiffCIRTy(lm);
@@ -272,7 +271,7 @@ void LowerItaniumCXXABI::lowerGetMethod(
op.getObject().getType().getAddrSpace());
mlir::Value thisVoidPtr = cir::CastOp::create(
locBuilder, thisVoidPtrTy, cir::CastKind::bitcast, loweredObjectPtr);
- adjustedThis =
+ thisArg =
cir::PtrStrideOp::create(locBuilder, thisVoidPtrTy, thisVoidPtr, adj);
// Load the "ptr" field of the member function pointer and determine if it
@@ -299,8 +298,8 @@ void LowerItaniumCXXABI::lowerGetMethod(
cir::PointerType::get(cir::IntType::get(b.getContext(), 8, true));
auto vtablePtrPtrTy = cir::PointerType::get(
vtablePtrTy, op.getObject().getType().getAddrSpace());
- auto vtablePtrPtr = cir::CastOp::create(
- b, loc, vtablePtrPtrTy, cir::CastKind::bitcast, adjustedThis);
+ auto vtablePtrPtr = cir::CastOp::create(b, loc, vtablePtrPtrTy,
+ cir::CastKind::bitcast, thisArg);
assert(!cir::MissingFeatures::opTBAA());
mlir::Value vtablePtr =
cir::LoadOp::create(b, loc, vtablePtrPtr, /*isDeref=*/false,
More information about the cfe-commits
mailing list