[CIR] Add basic support for data member pointers (#170939)

This adds the minimum support for C++ data member pointer variables.
This commit is contained in:
Andy Kaylor
2025-12-09 12:55:47 -08:00
committed by GitHub
parent 019a294771
commit 87bf5ee238
26 changed files with 631 additions and 12 deletions

View File

@@ -35,6 +35,7 @@ namespace cir {
class ArrayType;
class BoolType;
class ComplexType;
class DataMemberType;
class IntType;
class MethodType;
class PointerType;

View File

@@ -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
//===----------------------------------------------------------------------===//

View File

@@ -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"> {

View File

@@ -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

View File

@@ -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; }

View File

@@ -189,6 +189,11 @@ public:
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 {

View File

@@ -731,11 +731,8 @@ public:
}
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();
}

View File

@@ -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

View File

@@ -497,6 +497,8 @@ public:
/// 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);

View File

@@ -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);

View File

@@ -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
//===----------------------------------------------------------------------===//

View File

@@ -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))

View File

@@ -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 differences under different ABIs
assert(!MissingFeatures::cxxABI());
return llvm::TypeSize::getFixed(64);
}
uint64_t
DataMemberType::getABIAlignment(const ::mlir::DataLayout &dataLayout,
::mlir::DataLayoutEntryListRef params) const {
// FIXME: consider alignment differences under different ABIs
assert(!MissingFeatures::cxxABI());
return 8;
}
//===----------------------------------------------------------------------===//
// VPtrType Definitions
//===----------------------------------------------------------------------===//

View File

@@ -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
)

View File

@@ -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

View File

@@ -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 difference
// 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

View File

@@ -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
)

View File

@@ -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 ptrdiffTy =
target.getPtrDiffType(clang::LangAS::Default);
return cir::IntType::get(lm.getMLIRContext(), target.getTypeWidth(ptrdiffTy),
target.isTypeSigned(ptrdiffTy));
}
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 ptrdiff_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 ptrdiff_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

View File

@@ -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

View File

@@ -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

View File

@@ -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
)

View File

@@ -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);

View File

@@ -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"

View File

@@ -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

View File

@@ -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@+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@+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@+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>

View File

@@ -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";