[clang] 87bf5ee - [CIR] Add basic support for data member pointers (#170939)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Dec 9 12:55:52 PST 2025
Author: Andy Kaylor
Date: 2025-12-09T12:55:47-08:00
New Revision: 87bf5ee23863bc0b467ee44b2184b2c134a98464
URL: https://github.com/llvm/llvm-project/commit/87bf5ee23863bc0b467ee44b2184b2c134a98464
DIFF: https://github.com/llvm/llvm-project/commit/87bf5ee23863bc0b467ee44b2184b2c134a98464.diff
LOG: [CIR] Add basic support for data member pointers (#170939)
This adds the minimum support for C++ data member pointer variables.
Added:
clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.cpp
clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h
clang/lib/CIR/Dialect/Transforms/TargetLowering/CMakeLists.txt
clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerModule.cpp
clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerModule.h
clang/test/CIR/CodeGen/pointer-to-data-member.cpp
clang/test/CIR/IR/invalid-data-member.cir
Modified:
clang/include/clang/CIR/Dialect/IR/CIRAttrs.h
clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
clang/include/clang/CIR/Dialect/IR/CIRTypes.td
clang/include/clang/CIR/MissingFeatures.h
clang/lib/CIR/CodeGen/CIRGenBuilder.h
clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
clang/lib/CIR/CodeGen/CIRGenModule.cpp
clang/lib/CIR/CodeGen/CIRGenModule.h
clang/lib/CIR/CodeGen/CIRGenTypes.cpp
clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
clang/lib/CIR/Dialect/IR/CIRDialect.cpp
clang/lib/CIR/Dialect/IR/CIRTypes.cpp
clang/lib/CIR/Dialect/Transforms/CMakeLists.txt
clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt
clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
clang/utils/TableGen/CIRLoweringEmitter.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.h b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.h
index 03a6a97dc8c2e..858d4d6350bed 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.h
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.h
@@ -35,6 +35,7 @@ namespace cir {
class ArrayType;
class BoolType;
class ComplexType;
+class DataMemberType;
class IntType;
class MethodType;
class PointerType;
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index 98d4636dafc29..c0279a0b20670 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -447,6 +447,57 @@ def CIR_ConstPtrAttr : CIR_Attr<"ConstPtr", "ptr", [TypedAttrInterface]> {
}];
}
+//===----------------------------------------------------------------------===//
+// DataMemberAttr
+//===----------------------------------------------------------------------===//
+
+def CIR_DataMemberAttr : CIR_Attr<"DataMember", "data_member", [
+ TypedAttrInterface
+]> {
+ let summary = "Holds a constant data member pointer value";
+ let parameters = (ins AttributeSelfTypeParameter<
+ "", "cir::DataMemberType">:$type,
+ OptionalParameter<
+ "std::optional<unsigned>">:$member_index);
+ let description = [{
+ A data member attribute is a literal attribute that represents a constant
+ pointer-to-data-member value.
+
+ The `member_index` parameter represents the index of the pointed-to member
+ within its containing record. It is an optional parameter; lack of this
+ parameter indicates a null pointer-to-data-member value.
+
+ Example:
+ ```
+ #ptr = #cir.data_member<1> : !cir.data_member<!s32i in !rec_22Point22>
+
+ #null = #cir.data_member<null> : !cir.data_member<!s32i in !rec_22Point22>
+ ```
+ }];
+
+ let builders = [
+ AttrBuilderWithInferredContext<(ins "cir::DataMemberType":$type), [{
+ return $_get(type.getContext(), type, std::nullopt);
+ }]>,
+ AttrBuilderWithInferredContext<(ins "cir::DataMemberType":$type,
+ "unsigned":$member_index), [{
+ return $_get(type.getContext(), type, member_index);
+ }]>,
+ ];
+
+ let genVerifyDecl = 1;
+
+ let assemblyFormat = [{
+ `<` ($member_index^):(`null`)? `>`
+ }];
+
+ let extraClassDeclaration = [{
+ bool isNullPtr() const {
+ return !getMemberIndex().has_value();
+ }
+ }];
+}
+
//===----------------------------------------------------------------------===//
// GlobalViewAttr
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td b/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
index ddca98eac93ab..89762249ed0c4 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
@@ -309,6 +309,13 @@ def CIR_AnyFloatOrVecOfFloatType
let cppFunctionName = "isFPOrVectorOfFPType";
}
+//===----------------------------------------------------------------------===//
+// Data member type predicates
+//===----------------------------------------------------------------------===//
+
+def CIR_AnyDataMemberType : CIR_TypeBase<"::cir::DataMemberType",
+ "data member type">;
+
//===----------------------------------------------------------------------===//
// VPtr type predicates
//===----------------------------------------------------------------------===//
@@ -322,7 +329,8 @@ def CIR_PtrToVPtr : CIR_PtrToType<CIR_AnyVPtrType>;
//===----------------------------------------------------------------------===//
defvar CIR_ScalarTypes = [
- CIR_AnyBoolType, CIR_AnyIntType, CIR_AnyFloatType, CIR_AnyPtrType
+ CIR_AnyBoolType, CIR_AnyIntType, CIR_AnyFloatType, CIR_AnyPtrType,
+ CIR_AnyDataMemberType, CIR_AnyVPtrType
];
def CIR_AnyScalarType : AnyTypeOf<CIR_ScalarTypes, "cir scalar type"> {
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
index 3e062add6633a..59b97f0c6d39a 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
@@ -305,6 +305,36 @@ def CIR_PointerType : CIR_Type<"Pointer", "ptr", [
}];
}
+//===----------------------------------------------------------------------===//
+// CIR_DataMemberType
+//===----------------------------------------------------------------------===//
+
+def CIR_DataMemberType : CIR_Type<"DataMember", "data_member",
+ [DeclareTypeInterfaceMethods<DataLayoutTypeInterface>]
+> {
+ let summary = "CIR type that represents a pointer-to-data-member in C++";
+ let description = [{
+ `cir.data_member` models a pointer-to-data-member in C++. Values of this
+ type are essentially offsets of the pointed-to member within one of its
+ containing record.
+ }];
+
+ let parameters = (ins "mlir::Type":$member_ty,
+ "cir::RecordType":$class_ty);
+
+ let builders = [
+ TypeBuilderWithInferredContext<(ins
+ "mlir::Type":$member_ty, "cir::RecordType":$class_ty
+ ), [{
+ return $_get(member_ty.getContext(), member_ty, class_ty);
+ }]>,
+ ];
+
+ let assemblyFormat = [{
+ `<` $member_ty `in` $class_ty `>`
+ }];
+}
+
//===----------------------------------------------------------------------===//
// CIR_VPtrType
//===----------------------------------------------------------------------===//
@@ -693,7 +723,7 @@ def CIRRecordType : Type<
def CIR_AnyType : AnyTypeOf<[
CIR_VoidType, CIR_BoolType, CIR_ArrayType, CIR_VectorType, CIR_IntType,
CIR_AnyFloatType, CIR_PointerType, CIR_FuncType, CIR_RecordType,
- CIR_ComplexType, CIR_VPtrType
+ CIR_ComplexType, CIR_VPtrType, CIR_DataMemberType
]>;
#endif // CLANG_CIR_DIALECT_IR_CIRTYPES_TD
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 826a4b13f5c0c..b2d94709016fa 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -189,6 +189,10 @@ struct MissingFeatures {
static bool globalCtorLexOrder() { return false; }
static bool globalCtorAssociatedData() { return false; }
+ // LowerModule handling
+ static bool lowerModuleCodeGenOpts() { return false; }
+ static bool lowerModuleLangOpts() { return false; }
+
// Misc
static bool aarch64SIMDIntrinsics() { return false; }
static bool aarch64SMEIntrinsics() { return false; }
@@ -292,6 +296,7 @@ struct MissingFeatures {
static bool lowerModeOptLevel() { return false; }
static bool loweringPrepareX86CXXABI() { return false; }
static bool loweringPrepareAArch64XXABI() { return false; }
+ static bool makeTripleAlwaysPresent() { return false; }
static bool maybeHandleStaticInExternC() { return false; }
static bool mergeAllConstants() { return false; }
static bool metaDataNode() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index 85b38120169fd..bf13eeeaea60a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -189,6 +189,11 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
return getType<cir::RecordType>(nameAttr, kind);
}
+ cir::DataMemberAttr getDataMemberAttr(cir::DataMemberType ty,
+ unsigned memberIndex) {
+ return cir::DataMemberAttr::get(ty, memberIndex);
+ }
+
// Return true if the value is a null constant such as null pointer, (+0.0)
// for floating-point or zero initializer
bool isNullValue(mlir::Attribute attr) const {
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 6820e2a403288..25ce1ba26da09 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -731,11 +731,8 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
}
mlir::Value VisitUnaryAddrOf(const UnaryOperator *e) {
- if (llvm::isa<MemberPointerType>(e->getType())) {
- cgf.cgm.errorNYI(e->getSourceRange(), "Address of member pointer");
- return builder.getNullPtr(cgf.convertType(e->getType()),
- cgf.getLoc(e->getExprLoc()));
- }
+ if (llvm::isa<MemberPointerType>(e->getType()))
+ return cgf.cgm.emitMemberPointerConstant(e);
return cgf.emitLValue(e->getSubExpr()).getPointer();
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index e1894c040dd53..41a5d9db83e2b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -1464,6 +1464,26 @@ void CIRGenModule::emitExplicitCastExprType(const ExplicitCastExpr *e,
"emitExplicitCastExprType");
}
+mlir::Value CIRGenModule::emitMemberPointerConstant(const UnaryOperator *e) {
+ assert(!cir::MissingFeatures::cxxABI());
+
+ mlir::Location loc = getLoc(e->getSourceRange());
+
+ const auto *decl = cast<DeclRefExpr>(e->getSubExpr())->getDecl();
+
+ // A member function pointer.
+ if (isa<CXXMethodDecl>(decl)) {
+ errorNYI(e->getSourceRange(), "emitMemberPointerConstant: method pointer");
+ return {};
+ }
+
+ // Otherwise, a member data pointer.
+ auto ty = mlir::cast<cir::DataMemberType>(convertType(e->getType()));
+ const auto *fieldDecl = cast<FieldDecl>(decl);
+ return cir::ConstantOp::create(
+ builder, loc, builder.getDataMemberAttr(ty, fieldDecl->getFieldIndex()));
+}
+
void CIRGenModule::emitDeclContext(const DeclContext *dc) {
for (Decl *decl : dc->decls()) {
// Unlike other DeclContexts, the contents of an ObjCImplDecl at TU scope
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 59eb5f8938129..9c0961579718d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -497,6 +497,8 @@ class CIRGenModule : public CIRGenTypeCache {
/// the given type. This is usually, but not always, an LLVM null constant.
mlir::TypedAttr emitNullConstantForBase(const CXXRecordDecl *record);
+ mlir::Value emitMemberPointerConstant(const UnaryOperator *e);
+
llvm::StringRef getMangledName(clang::GlobalDecl gd);
void emitTentativeDefinition(const VarDecl *d);
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
index 24b106b4bcee7..7f000ece8a494 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
@@ -482,6 +482,21 @@ mlir::Type CIRGenTypes::convertType(QualType type) {
break;
}
+ case Type::MemberPointer: {
+ const auto *mpt = cast<MemberPointerType>(ty);
+
+ mlir::Type memberTy = convertType(mpt->getPointeeType());
+ auto clsTy = mlir::cast<cir::RecordType>(
+ convertType(QualType(mpt->getQualifier().getAsType(), 0)));
+ if (mpt->isMemberDataPointer()) {
+ resultType = cir::DataMemberType::get(memberTy, clsTy);
+ } else {
+ assert(!cir::MissingFeatures::methodType());
+ cgm.errorNYI(SourceLocation(), "MethodType");
+ }
+ break;
+ }
+
case Type::FunctionNoProto:
case Type::FunctionProto:
resultType = convertFunctionTypeInternal(type);
diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
index ee296f171e0d9..59d7765198f9e 100644
--- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
@@ -269,6 +269,38 @@ ConstComplexAttr::verify(function_ref<InFlightDiagnostic()> emitError,
return success();
}
+//===----------------------------------------------------------------------===//
+// DataMemberAttr definitions
+//===----------------------------------------------------------------------===//
+
+LogicalResult
+DataMemberAttr::verify(function_ref<InFlightDiagnostic()> emitError,
+ cir::DataMemberType ty,
+ std::optional<unsigned> memberIndex) {
+ // DataMemberAttr without a given index represents a null value.
+ if (!memberIndex.has_value())
+ return success();
+
+ cir::RecordType recTy = ty.getClassTy();
+ if (recTy.isIncomplete())
+ return emitError()
+ << "incomplete 'cir.record' cannot be used to build a non-null "
+ "data member pointer";
+
+ unsigned memberIndexValue = memberIndex.value();
+ if (memberIndexValue >= recTy.getNumElements())
+ return emitError()
+ << "member index of a #cir.data_member attribute is out of range";
+
+ mlir::Type memberTy = recTy.getMembers()[memberIndexValue];
+ if (memberTy != ty.getMemberTy())
+ return emitError()
+ << "member type of a #cir.data_member attribute must match the "
+ "attribute type";
+
+ return success();
+}
+
//===----------------------------------------------------------------------===//
// CIR ConstArrayAttr
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index ec8cae62d6bc8..38a2cecbb8617 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -357,6 +357,12 @@ static LogicalResult checkConstantTypes(mlir::Operation *op, mlir::Type opType,
return success();
}
+ if (isa<cir::DataMemberAttr>(attrType)) {
+ // More detailed type verifications are already done in
+ // DataMemberAttr::verify. Don't need to repeat here.
+ return success();
+ }
+
if (isa<cir::ZeroAttr>(attrType)) {
if (isa<cir::RecordType, cir::ArrayType, cir::VectorType, cir::ComplexType>(
opType))
diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
index bb87056048ec5..9a37a4f4e3996 100644
--- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
@@ -750,6 +750,26 @@ BoolType::getABIAlignment(const ::mlir::DataLayout &dataLayout,
return 1;
}
+//===----------------------------------------------------------------------===//
+// DataMemberType Definitions
+//===----------------------------------------------------------------------===//
+
+llvm::TypeSize
+DataMemberType::getTypeSizeInBits(const ::mlir::DataLayout &dataLayout,
+ ::mlir::DataLayoutEntryListRef params) const {
+ // FIXME: consider size
diff erences under
diff erent ABIs
+ assert(!MissingFeatures::cxxABI());
+ return llvm::TypeSize::getFixed(64);
+}
+
+uint64_t
+DataMemberType::getABIAlignment(const ::mlir::DataLayout &dataLayout,
+ ::mlir::DataLayoutEntryListRef params) const {
+ // FIXME: consider alignment
diff erences under
diff erent ABIs
+ assert(!MissingFeatures::cxxABI());
+ return 8;
+}
+
//===----------------------------------------------------------------------===//
// VPtrType Definitions
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt
index 3fc5b06b74e4d..e3b7106c1d6b9 100644
--- a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt
+++ b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt
@@ -1,3 +1,5 @@
+add_subdirectory(TargetLowering)
+
add_clang_library(MLIRCIRTransforms
CIRCanonicalize.cpp
CIRSimplify.cpp
@@ -21,4 +23,5 @@ add_clang_library(MLIRCIRTransforms
MLIRCIR
MLIRCIRInterfaces
+ MLIRCIRTargetLowering
)
diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.cpp
new file mode 100644
index 0000000000000..86cf7ebdc8f50
--- /dev/null
+++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.cpp
@@ -0,0 +1,20 @@
+//===- CIRCXXABI.cpp ------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file partially mimics clang/lib/CodeGen/CGCXXABI.cpp. The queries are
+// adapted to operate on the CIR dialect, however.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CIRCXXABI.h"
+
+namespace cir {
+
+CIRCXXABI::~CIRCXXABI() {}
+
+} // namespace cir
diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h
new file mode 100644
index 0000000000000..003cd78eb3f26
--- /dev/null
+++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h
@@ -0,0 +1,55 @@
+//===----- CIRCXXABI.h - Interface to C++ ABIs for CIR Dialect --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file partially mimics the CodeGen/CGCXXABI.h class. The main
diff erence
+// is that this is adapted to operate on the CIR dialect.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CLANG_LIB_CIR_DIALECT_TRANSFORMS_TARGETLOWERING_CIRCXXABI_H
+#define CLANG_LIB_CIR_DIALECT_TRANSFORMS_TARGETLOWERING_CIRCXXABI_H
+
+#include "mlir/Transforms/DialectConversion.h"
+#include "clang/CIR/Dialect/IR/CIRTypes.h"
+
+namespace cir {
+
+// Forward declarations.
+class LowerModule;
+
+class CIRCXXABI {
+ friend class LowerModule;
+
+protected:
+ LowerModule &lm;
+
+ CIRCXXABI(LowerModule &lm) : lm(lm) {}
+
+public:
+ virtual ~CIRCXXABI();
+
+ /// Lower the given data member pointer type to its ABI type. The returned
+ /// type is also a CIR type.
+ virtual mlir::Type
+ lowerDataMemberType(cir::DataMemberType type,
+ const mlir::TypeConverter &typeConverter) const = 0;
+
+ /// Lower the given data member pointer constant to a constant of the ABI
+ /// type. The returned constant is represented as an attribute as well.
+ virtual mlir::TypedAttr
+ lowerDataMemberConstant(cir::DataMemberAttr attr,
+ const mlir::DataLayout &layout,
+ const mlir::TypeConverter &typeConverter) const = 0;
+};
+
+/// Creates an Itanium-family ABI.
+std::unique_ptr<CIRCXXABI> createItaniumCXXABI(LowerModule &lm);
+
+} // namespace cir
+
+#endif // CLANG_LIB_CIR_DIALECT_TRANSFORMS_TARGETLOWERING_CIRCXXABI_H
diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CMakeLists.txt b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CMakeLists.txt
new file mode 100644
index 0000000000000..158c42e729536
--- /dev/null
+++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CMakeLists.txt
@@ -0,0 +1,20 @@
+add_clang_library(MLIRCIRTargetLowering
+ CIRCXXABI.cpp
+ LowerModule.cpp
+ LowerItaniumCXXABI.cpp
+
+ DEPENDS
+ clangBasic
+
+ LINK_COMPONENTS
+ TargetParser
+
+ LINK_LIBS PUBLIC
+
+ clangBasic
+ MLIRIR
+ MLIRPass
+ MLIRDLTIDialect
+ MLIRCIR
+ MLIRCIRInterfaces
+)
diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
new file mode 100644
index 0000000000000..7089990343dc0
--- /dev/null
+++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
@@ -0,0 +1,90 @@
+//===---- LowerItaniumCXXABI.cpp - Emit CIR code Itanium-specific code ---===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This provides CIR lowering logic targeting the Itanium C++ ABI. The class in
+// this file generates records that follow the Itanium C++ ABI, which is
+// documented at:
+// https://itanium-cxx-abi.github.io/cxx-abi/abi.html
+// https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html
+//
+// It also supports the closely-related ARM ABI, documented at:
+// https://developer.arm.com/documentation/ihi0041/g/
+//
+// This file partially mimics clang/lib/CodeGen/ItaniumCXXABI.cpp. The queries
+// are adapted to operate on the CIR dialect, however.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CIRCXXABI.h"
+#include "LowerModule.h"
+#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
+#include "llvm/Support/ErrorHandling.h"
+
+namespace cir {
+
+namespace {
+
+class LowerItaniumCXXABI : public CIRCXXABI {
+public:
+ LowerItaniumCXXABI(LowerModule &lm) : CIRCXXABI(lm) {}
+
+ /// Lower the given data member pointer type to its ABI type. The returned
+ /// type is also a CIR type.
+ virtual mlir::Type
+ lowerDataMemberType(cir::DataMemberType type,
+ const mlir::TypeConverter &typeConverter) const override;
+
+ mlir::TypedAttr lowerDataMemberConstant(
+ cir::DataMemberAttr attr, const mlir::DataLayout &layout,
+ const mlir::TypeConverter &typeConverter) const override;
+};
+
+} // namespace
+
+std::unique_ptr<CIRCXXABI> createItaniumCXXABI(LowerModule &lm) {
+ return std::make_unique<LowerItaniumCXXABI>(lm);
+}
+
+static cir::IntType getPtrDiffCIRTy(LowerModule &lm) {
+ const clang::TargetInfo &target = lm.getTarget();
+ clang::TargetInfo::IntType ptr
diff Ty =
+ target.getPtrDiffType(clang::LangAS::Default);
+ return cir::IntType::get(lm.getMLIRContext(), target.getTypeWidth(ptr
diff Ty),
+ target.isTypeSigned(ptr
diff Ty));
+}
+
+mlir::Type LowerItaniumCXXABI::lowerDataMemberType(
+ cir::DataMemberType type, const mlir::TypeConverter &typeConverter) const {
+ // Itanium C++ ABI 2.3.1:
+ // A data member pointer is represented as the data member's offset in bytes
+ // from the address point of an object of the base type, as a ptr
diff _t.
+ return getPtrDiffCIRTy(lm);
+}
+
+mlir::TypedAttr LowerItaniumCXXABI::lowerDataMemberConstant(
+ cir::DataMemberAttr attr, const mlir::DataLayout &layout,
+ const mlir::TypeConverter &typeConverter) const {
+ uint64_t memberOffset;
+ if (attr.isNullPtr()) {
+ // Itanium C++ ABI 2.3:
+ // A NULL pointer is represented as -1.
+ memberOffset = -1ull;
+ } else {
+ // Itanium C++ ABI 2.3:
+ // A pointer to data member is an offset from the base address of
+ // the class object containing it, represented as a ptr
diff _t
+ unsigned memberIndex = attr.getMemberIndex().value();
+ memberOffset =
+ attr.getType().getClassTy().getElementOffset(layout, memberIndex);
+ }
+
+ mlir::Type abiTy = lowerDataMemberType(attr.getType(), typeConverter);
+ return cir::IntAttr::get(abiTy, memberOffset);
+}
+
+} // namespace cir
diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerModule.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerModule.cpp
new file mode 100644
index 0000000000000..7576e20ac8f54
--- /dev/null
+++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerModule.cpp
@@ -0,0 +1,87 @@
+//===--- LowerModule.cpp - Lower CIR Module to a Target -------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file partially mimics clang/lib/CodeGen/CodeGenModule.cpp. The queries
+// are adapted to operate on the CIR dialect, however.
+//
+//===----------------------------------------------------------------------===//
+
+#include "LowerModule.h"
+#include "CIRCXXABI.h"
+#include "mlir/IR/BuiltinAttributes.h"
+#include "mlir/IR/PatternMatch.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Basic/TargetOptions.h"
+#include "clang/CIR/MissingFeatures.h"
+#include "llvm/Support/ErrorHandling.h"
+
+namespace cir {
+
+static std::unique_ptr<CIRCXXABI> createCXXABI(LowerModule &lm) {
+ switch (lm.getCXXABIKind()) {
+ case clang::TargetCXXABI::AppleARM64:
+ case clang::TargetCXXABI::Fuchsia:
+ case clang::TargetCXXABI::GenericAArch64:
+ case clang::TargetCXXABI::GenericARM:
+ case clang::TargetCXXABI::iOS:
+ case clang::TargetCXXABI::WatchOS:
+ case clang::TargetCXXABI::GenericMIPS:
+ case clang::TargetCXXABI::GenericItanium:
+ case clang::TargetCXXABI::WebAssembly:
+ case clang::TargetCXXABI::XL:
+ return createItaniumCXXABI(lm);
+ case clang::TargetCXXABI::Microsoft:
+ llvm_unreachable("Windows ABI NYI");
+ }
+
+ llvm_unreachable("invalid C++ ABI kind");
+}
+
+LowerModule::LowerModule(clang::LangOptions langOpts,
+ clang::CodeGenOptions codeGenOpts,
+ mlir::ModuleOp &module,
+ std::unique_ptr<clang::TargetInfo> target,
+ mlir::PatternRewriter &rewriter)
+ : module(module), target(std::move(target)), abi(createCXXABI(*this)),
+ rewriter(rewriter) {}
+
+// TODO: not to create it every time
+std::unique_ptr<LowerModule>
+createLowerModule(mlir::ModuleOp module, mlir::PatternRewriter &rewriter) {
+ // Fetch target information.
+ llvm::Triple triple(mlir::cast<mlir::StringAttr>(
+ module->getAttr(cir::CIRDialect::getTripleAttrName()))
+ .getValue());
+ clang::TargetOptions targetOptions;
+ targetOptions.Triple = triple.str();
+ auto targetInfo = clang::targets::AllocateTarget(triple, targetOptions);
+
+ // FIXME(cir): This just uses the default language options. We need to account
+ // for custom options.
+ // Create context.
+ assert(!cir::MissingFeatures::lowerModuleLangOpts());
+ clang::LangOptions langOpts;
+
+ // FIXME(cir): This just uses the default code generation options. We need to
+ // account for custom options.
+ assert(!cir::MissingFeatures::lowerModuleCodeGenOpts());
+ clang::CodeGenOptions codeGenOpts;
+
+ if (auto optInfo = mlir::cast_if_present<cir::OptInfoAttr>(
+ module->getAttr(cir::CIRDialect::getOptInfoAttrName()))) {
+ codeGenOpts.OptimizationLevel = optInfo.getLevel();
+ codeGenOpts.OptimizeSize = optInfo.getSize();
+ }
+
+ return std::make_unique<LowerModule>(std::move(langOpts),
+ std::move(codeGenOpts), module,
+ std::move(targetInfo), rewriter);
+}
+
+} // namespace cir
diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerModule.h b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerModule.h
new file mode 100644
index 0000000000000..440e307f571e9
--- /dev/null
+++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerModule.h
@@ -0,0 +1,55 @@
+//===--- LowerModule.h - Abstracts CIR's module lowering --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file partially mimics clang/lib/CodeGen/CodeGenModule.h. The queries are
+// adapted to operate on the CIR dialect, however.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CLANG_LIB_CIR_DIALECT_TRANSFORMS_TARGETLOWERING_LOWERMODULE_H
+#define CLANG_LIB_CIR_DIALECT_TRANSFORMS_TARGETLOWERING_LOWERMODULE_H
+
+#include "CIRCXXABI.h"
+#include "mlir/IR/BuiltinOps.h"
+#include "clang/Basic/CodeGenOptions.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/CIR/Dialect/IR/CIRDialect.h"
+#include "clang/CIR/MissingFeatures.h"
+#include <memory>
+
+namespace cir {
+
+class LowerModule {
+ mlir::ModuleOp module;
+ const std::unique_ptr<clang::TargetInfo> target;
+ std::unique_ptr<CIRCXXABI> abi;
+ [[maybe_unused]] mlir::PatternRewriter &rewriter;
+
+public:
+ LowerModule(clang::LangOptions langOpts, clang::CodeGenOptions codeGenOpts,
+ mlir::ModuleOp &module, std::unique_ptr<clang::TargetInfo> target,
+ mlir::PatternRewriter &rewriter);
+ ~LowerModule() = default;
+
+ clang::TargetCXXABI::Kind getCXXABIKind() const {
+ assert(!cir::MissingFeatures::lowerModuleLangOpts());
+ return target->getCXXABI().getKind();
+ }
+
+ CIRCXXABI &getCXXABI() const { return *abi; }
+ const clang::TargetInfo &getTarget() const { return *target; }
+ mlir::MLIRContext *getMLIRContext() { return module.getContext(); }
+};
+
+std::unique_ptr<LowerModule> createLowerModule(mlir::ModuleOp module,
+ mlir::PatternRewriter &rewriter);
+
+} // namespace cir
+
+#endif // CLANG_LIB_CIR_DIALECT_TRANSFORMS_TARGETLOWERING_LOWERMODULE_H
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt b/clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt
index 7baff3412a84e..2525e02ae8f85 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt
@@ -18,7 +18,12 @@ add_clang_library(clangCIRLoweringDirectToLLVM
clangCIRLoweringCommon
${dialect_libs}
MLIRCIR
+ MLIRCIRTargetLowering
MLIRBuiltinToLLVMIRTranslation
MLIRLLVMToLLVMIRTranslation
MLIRIR
)
+
+target_include_directories(clangCIRLoweringDirectToLLVM PRIVATE
+ ${CLANG_SOURCE_DIR}/lib/CIR/Dialect/Transforms/TargetLowering
+ )
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index cc911cfc7d778..88ca8033b48ea 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -1755,6 +1755,14 @@ mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite(
return mlir::success();
}
attr = op.getValue();
+ } else if (mlir::isa<cir::DataMemberType>(op.getType())) {
+ assert(lowerMod && "lower module is not available");
+ auto dataMember = mlir::cast<cir::DataMemberAttr>(op.getValue());
+ mlir::DataLayout layout(op->getParentOfType<mlir::ModuleOp>());
+ mlir::TypedAttr abiValue = lowerMod->getCXXABI().lowerDataMemberConstant(
+ dataMember, layout, *typeConverter);
+ rewriter.replaceOpWithNewOp<ConstantOp>(op, abiValue);
+ return mlir::success();
} else if (const auto arrTy = mlir::dyn_cast<cir::ArrayType>(op.getType())) {
const auto constArr = mlir::dyn_cast<cir::ConstArrayAttr>(op.getValue());
if (!constArr && !isa<cir::ZeroAttr, cir::UndefAttr>(op.getValue()))
@@ -2839,8 +2847,20 @@ mlir::LogicalResult CIRToLLVMSelectOpLowering::matchAndRewrite(
return mlir::success();
}
+std::unique_ptr<cir::LowerModule> prepareLowerModule(mlir::ModuleOp module) {
+ mlir::PatternRewriter rewriter{module->getContext()};
+ // If the triple is not present, e.g. CIR modules parsed from text, we
+ // cannot init LowerModule properly. This happens in some lowering tests,
+ // but it should not happen in real compilation.
+ assert(!cir::MissingFeatures::makeTripleAlwaysPresent());
+ if (!module->hasAttr(cir::CIRDialect::getTripleAttrName()))
+ return {};
+ return cir::createLowerModule(module, rewriter);
+}
+
static void prepareTypeConverter(mlir::LLVMTypeConverter &converter,
- mlir::DataLayout &dataLayout) {
+ mlir::DataLayout &dataLayout,
+ cir::LowerModule *lowerModule) {
converter.addConversion([&](cir::PointerType type) -> mlir::Type {
unsigned addrSpace =
type.getAddrSpace() ? type.getAddrSpace().getValue().getUInt() : 0;
@@ -2850,6 +2870,13 @@ static void prepareTypeConverter(mlir::LLVMTypeConverter &converter,
assert(!cir::MissingFeatures::addressSpace());
return mlir::LLVM::LLVMPointerType::get(type.getContext());
});
+ converter.addConversion(
+ [&, lowerModule](cir::DataMemberType type) -> mlir::Type {
+ assert(lowerModule && "CXXABI is not available");
+ mlir::Type abiType =
+ lowerModule->getCXXABI().lowerDataMemberType(type, converter);
+ return converter.convertType(abiType);
+ });
converter.addConversion([&](cir::ArrayType type) -> mlir::Type {
mlir::Type ty =
convertTypeForMemory(converter, dataLayout, type.getElementType());
@@ -3118,7 +3145,8 @@ void ConvertCIRToLLVMPass::runOnOperation() {
mlir::ModuleOp module = getOperation();
mlir::DataLayout dl(module);
mlir::LLVMTypeConverter converter(&getContext());
- prepareTypeConverter(converter, dl);
+ std::unique_ptr<cir::LowerModule> lowerModule = prepareLowerModule(module);
+ prepareTypeConverter(converter, dl, lowerModule.get());
mlir::RewritePatternSet patterns(&getContext());
@@ -3126,7 +3154,7 @@ void ConvertCIRToLLVMPass::runOnOperation() {
#define GET_LLVM_LOWERING_PATTERNS_LIST
#include "clang/CIR/Dialect/IR/CIRLowering.inc"
#undef GET_LLVM_LOWERING_PATTERNS_LIST
- >(converter, patterns.getContext(), dl);
+ >(converter, patterns.getContext(), lowerModule.get(), dl);
processCIRAttrs(module);
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
index 0591de545b81d..d32f8603ee0be 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -12,6 +12,8 @@
#ifndef CLANG_CIR_LOWERTOLLVM_H
#define CLANG_CIR_LOWERTOLLVM_H
+#include "LowerModule.h"
+
#include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
#include "mlir/Transforms/DialectConversion.h"
diff --git a/clang/test/CIR/CodeGen/pointer-to-data-member.cpp b/clang/test/CIR/CodeGen/pointer-to-data-member.cpp
new file mode 100644
index 0000000000000..b116d21f01170
--- /dev/null
+++ b/clang/test/CIR/CodeGen/pointer-to-data-member.cpp
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir -Wno-unused-value -emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir -Wno-unused-value -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -Wno-unused-value -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+struct Point {
+ int x;
+ int y;
+ int z;
+};
+
+auto test1() -> int Point::* {
+ return &Point::y;
+}
+
+// CIR: cir.func {{.*}} @_Z5test1v() -> !cir.data_member<!s32i in !rec_Point> {
+// CIR: %[[RETVAL:.*]] = cir.alloca !cir.data_member<!s32i in !rec_Point>, !cir.ptr<!cir.data_member<!s32i in !rec_Point>>, ["__retval"]
+// CIR: %[[MEMBER:.*]] = cir.const #cir.data_member<1> : !cir.data_member<!s32i in !rec_Point>
+// CIR: cir.store %[[MEMBER]], %[[RETVAL]] : !cir.data_member<!s32i in !rec_Point>, !cir.ptr<!cir.data_member<!s32i in !rec_Point>>
+// CIR: %[[RET:.*]] = cir.load %[[RETVAL]] : !cir.ptr<!cir.data_member<!s32i in !rec_Point>>, !cir.data_member<!s32i in !rec_Point>
+// CIR: cir.return %[[RET]] : !cir.data_member<!s32i in !rec_Point>
+
+// LLVM: define {{.*}} i64 @_Z5test1v()
+// LLVM: %[[RETVAL:.*]] = alloca i64
+// LLVM: store i64 4, ptr %[[RETVAL]]
+// LLVM: %[[RET:.*]] = load i64, ptr %[[RETVAL]]
+// LLVM: ret i64 %[[RET]]
+
+// OGCG: define {{.*}} i64 @_Z5test1v()
+// OGCG: ret i64 4
diff --git a/clang/test/CIR/IR/invalid-data-member.cir b/clang/test/CIR/IR/invalid-data-member.cir
new file mode 100644
index 0000000000000..2941777404973
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-data-member.cir
@@ -0,0 +1,27 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+// -----
+
+!u16i = !cir.int<u, 16>
+!u32i = !cir.int<u, 32>
+!struct1 = !cir.record<struct "Struct1" {!u16i, !u32i}>
+
+// expected-error at +1 {{member type of a #cir.data_member attribute must match the attribute type}}
+#invalid_member_ty = #cir.data_member<0> : !cir.data_member<!u32i in !struct1>
+
+// -----
+
+!u16i = !cir.int<u, 16>
+!incomplete_struct = !cir.record<struct "Incomplete" incomplete>
+
+// expected-error at +1 {{incomplete 'cir.record' cannot be used to build a non-null data member pointer}}
+#incomplete_cls_member = #cir.data_member<0> : !cir.data_member<!u16i in !incomplete_struct>
+
+// -----
+
+!u16i = !cir.int<u, 16>
+!u32i = !cir.int<u, 32>
+!struct1 = !cir.record<struct "Struct1" {!u16i, !u32i}>
+
+// expected-error at +1 {{member index of a #cir.data_member attribute is out of range}}
+#invalid_member_ty = #cir.data_member<2> : !cir.data_member<!u32i in !struct1>
diff --git a/clang/utils/TableGen/CIRLoweringEmitter.cpp b/clang/utils/TableGen/CIRLoweringEmitter.cpp
index 80dc209c69a7b..c81b8941f9a39 100644
--- a/clang/utils/TableGen/CIRLoweringEmitter.cpp
+++ b/clang/utils/TableGen/CIRLoweringEmitter.cpp
@@ -60,6 +60,7 @@ void GenerateLLVMLoweringPattern(llvm::StringRef OpName,
Code << "class " << PatternName
<< " : public mlir::OpConversionPattern<cir::" << OpName << "> {\n";
+ Code << " [[maybe_unused]] cir::LowerModule *lowerMod;\n";
Code << " [[maybe_unused]] mlir::DataLayout const &dataLayout;\n";
Code << "\n";
@@ -69,10 +70,12 @@ void GenerateLLVMLoweringPattern(llvm::StringRef OpName,
Code << " " << PatternName
<< "(mlir::TypeConverter const "
- "&typeConverter, mlir::MLIRContext *context, mlir::DataLayout const "
+ "&typeConverter, mlir::MLIRContext *context, "
+ "cir::LowerModule *lowerMod, mlir::DataLayout const "
"&dataLayout)\n";
Code << " : OpConversionPattern<cir::" << OpName
- << ">(typeConverter, context), dataLayout(dataLayout)";
+ << ">(typeConverter, context), lowerMod(lowerMod), "
+ "dataLayout(dataLayout)";
if (IsRecursive) {
Code << " {\n";
Code << " setHasBoundedRewriteRecursion();\n";
More information about the cfe-commits
mailing list