diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index b4b02e24f85cc..8c6f23148c5ad 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -97,6 +97,10 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return getConstPtrAttr(t, 0); } + mlir::TypedAttr getNullDataMemberAttr(cir::DataMemberType ty) { + return cir::DataMemberAttr::get(ty); + } + mlir::TypedAttr getZeroInitAttr(mlir::Type ty) { if (mlir::isa(ty)) return cir::IntAttr::get(ty, 0); @@ -112,6 +116,8 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return getConstNullPtrAttr(ptrTy); if (auto recordTy = mlir::dyn_cast(ty)) return cir::ZeroAttr::get(recordTy); + if (auto dataMemberTy = mlir::dyn_cast(ty)) + return getNullDataMemberAttr(dataMemberTy); if (mlir::isa(ty)) { return getFalseAttr(); } diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 4922f83df0efb..ddb5bfc5a23cd 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -3614,6 +3614,8 @@ def CIR_GetRuntimeMemberOp : CIR_Op<"get_runtime_member"> { }]; let hasVerifier = 1; + + let hasLLVMLowering = false; } //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Dialect/Passes.h b/clang/include/clang/CIR/Dialect/Passes.h index 32c3e27d07dfb..98eaf884347ed 100644 --- a/clang/include/clang/CIR/Dialect/Passes.h +++ b/clang/include/clang/CIR/Dialect/Passes.h @@ -23,6 +23,7 @@ namespace mlir { std::unique_ptr createCIRCanonicalizePass(); std::unique_ptr createCIRFlattenCFGPass(); std::unique_ptr createCIRSimplifyPass(); +std::unique_ptr createCXXABILoweringPass(); std::unique_ptr createHoistAllocasPass(); std::unique_ptr createLoweringPreparePass(); std::unique_ptr createLoweringPreparePass(clang::ASTContext *astCtx); diff --git a/clang/include/clang/CIR/Dialect/Passes.td b/clang/include/clang/CIR/Dialect/Passes.td index 0f5783945f8ae..55007adf6ffb3 100644 --- a/clang/include/clang/CIR/Dialect/Passes.td +++ b/clang/include/clang/CIR/Dialect/Passes.td @@ -82,6 +82,17 @@ def GotoSolver : Pass<"cir-goto-solver"> { let dependentDialects = ["cir::CIRDialect"]; } +def CXXABILowering : Pass<"cir-cxxabi-lowering"> { + let summary = "Lower abstract C++ operations to target-specific ABI form"; + let description = [{ + This pass lowers high-level operations that represent C++ constructs in a + target-independent way to concrete, target specific operations. + }]; + let constructor = "mlir::createCXXABILoweringPass()"; + let dependentDialects = ["cir::CIRDialect"]; +} + + def LoweringPrepare : Pass<"cir-lowering-prepare"> { let summary = "Lower to more fine-grained CIR operations before lowering to " "other dialects"; diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp index 7f000ece8a494..98a6e5d3b239a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp @@ -33,8 +33,10 @@ mlir::MLIRContext &CIRGenTypes::getMLIRContext() const { /// expanding the type because we're in a recursive context. bool CIRGenTypes::isFuncParamTypeConvertible(clang::QualType type) { // Some ABIs cannot have their member pointers represented in LLVM IR unless - // certain circumstances have been reached. - assert(!type->getAs() && "NYI"); + // certain circumstances have been reached, but in CIR we represent member + // pointer types abstractly at this point so they are always convertible. + if (type->getAs()) + return true; // If this isn't a tag type, we can convert it. const TagType *tagType = type->getAs(); diff --git a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt index e3b7106c1d6b9..2a0d791edd22c 100644 --- a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt +++ b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt @@ -3,6 +3,7 @@ add_subdirectory(TargetLowering) add_clang_library(MLIRCIRTransforms CIRCanonicalize.cpp CIRSimplify.cpp + CXXABILowering.cpp FlattenCFG.cpp HoistAllocas.cpp LoweringPrepare.cpp diff --git a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp new file mode 100644 index 0000000000000..1ee871dad6834 --- /dev/null +++ b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp @@ -0,0 +1,361 @@ +//==- CXXABILowering.cpp - lower C++ operations to target-specific ABI form -=// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "PassDetail.h" +#include "TargetLowering/LowerModule.h" + +#include "mlir/IR/PatternMatch.h" +#include "mlir/Interfaces/DataLayoutInterfaces.h" +#include "mlir/Pass/Pass.h" +#include "mlir/Transforms/DialectConversion.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h" +#include "clang/CIR/Dialect/IR/CIRAttrs.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" +#include "clang/CIR/Dialect/IR/CIROpsEnums.h" +#include "clang/CIR/Dialect/Passes.h" +#include "clang/CIR/MissingFeatures.h" + +using namespace mlir; +using namespace cir; + +namespace mlir { +#define GEN_PASS_DEF_CXXABILOWERING +#include "clang/CIR/Dialect/Passes.h.inc" +} // namespace mlir + +namespace { + +template +class CIROpCXXABILoweringPattern : public mlir::OpConversionPattern { +protected: + mlir::DataLayout *dataLayout; + cir::LowerModule *lowerModule; + +public: + CIROpCXXABILoweringPattern(mlir::MLIRContext *context, + const mlir::TypeConverter &typeConverter, + mlir::DataLayout &dataLayout, + cir::LowerModule &lowerModule) + : mlir::OpConversionPattern(typeConverter, context), + dataLayout(&dataLayout), lowerModule(&lowerModule) {} +}; + +#define CIR_CXXABI_LOWERING_PATTERN(name, operation) \ + struct name : CIROpCXXABILoweringPattern { \ + using CIROpCXXABILoweringPattern::CIROpCXXABILoweringPattern; \ + \ + mlir::LogicalResult \ + matchAndRewrite(operation op, OpAdaptor adaptor, \ + mlir::ConversionPatternRewriter &rewriter) const override; \ + } + +CIR_CXXABI_LOWERING_PATTERN(CIRAllocaOpABILowering, cir::AllocaOp); +CIR_CXXABI_LOWERING_PATTERN(CIRConstantOpABILowering, cir::ConstantOp); +CIR_CXXABI_LOWERING_PATTERN(CIRFuncOpABILowering, cir::FuncOp); +CIR_CXXABI_LOWERING_PATTERN(CIRGetRuntimeMemberOpABILowering, + cir::GetRuntimeMemberOp); +CIR_CXXABI_LOWERING_PATTERN(CIRGlobalOpABILowering, cir::GlobalOp); +#undef CIR_CXXABI_LOWERING_PATTERN + +struct CXXABILoweringPass + : public impl::CXXABILoweringBase { + CXXABILoweringPass() = default; + void runOnOperation() override; +}; + +/// A generic ABI lowering rewrite pattern. This conversion pattern matches any +/// CIR dialect operations with at least one operand or result of an +/// ABI-dependent type. This conversion pattern rewrites the matched operation +/// by replacing all its ABI-dependent operands and results with their +/// lowered counterparts. +class CIRGenericCXXABILoweringPattern : public mlir::ConversionPattern { +public: + CIRGenericCXXABILoweringPattern(mlir::MLIRContext *context, + const mlir::TypeConverter &typeConverter) + : mlir::ConversionPattern(typeConverter, MatchAnyOpTypeTag(), + /*benefit=*/1, context) {} + + mlir::LogicalResult + matchAndRewrite(mlir::Operation *op, llvm::ArrayRef operands, + mlir::ConversionPatternRewriter &rewriter) const override { + // Do not match on operations that have dedicated ABI lowering rewrite rules + if (llvm::isa(op)) + return mlir::failure(); + + const mlir::TypeConverter *typeConverter = getTypeConverter(); + assert(typeConverter && + "CIRGenericCXXABILoweringPattern requires a type converter"); + bool operandsAndResultsLegal = typeConverter->isLegal(op); + bool regionsLegal = + std::all_of(op->getRegions().begin(), op->getRegions().end(), + [typeConverter](mlir::Region ®ion) { + return typeConverter->isLegal(®ion); + }); + if (operandsAndResultsLegal && regionsLegal) { + // The operation does not have any CXXABI-dependent operands or results, + // the match fails. + return mlir::failure(); + } + + assert(op->getNumRegions() == 0 && "CIRGenericCXXABILoweringPattern cannot " + "deal with operations with regions"); + + mlir::OperationState loweredOpState(op->getLoc(), op->getName()); + loweredOpState.addOperands(operands); + loweredOpState.addAttributes(op->getAttrs()); + loweredOpState.addSuccessors(op->getSuccessors()); + + // Lower all result types + llvm::SmallVector loweredResultTypes; + loweredResultTypes.reserve(op->getNumResults()); + for (mlir::Type result : op->getResultTypes()) + loweredResultTypes.push_back(typeConverter->convertType(result)); + loweredOpState.addTypes(loweredResultTypes); + + // Lower all regions + for (mlir::Region ®ion : op->getRegions()) { + mlir::Region *loweredRegion = loweredOpState.addRegion(); + rewriter.inlineRegionBefore(region, *loweredRegion, loweredRegion->end()); + if (mlir::failed( + rewriter.convertRegionTypes(loweredRegion, *getTypeConverter()))) + return mlir::failure(); + } + + // Clone the operation with lowered operand types and result types + mlir::Operation *loweredOp = rewriter.create(loweredOpState); + + rewriter.replaceOp(op, loweredOp); + return mlir::success(); + } +}; + +} // namespace + +mlir::LogicalResult CIRAllocaOpABILowering::matchAndRewrite( + cir::AllocaOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + mlir::Type allocaPtrTy = op.getType(); + mlir::Type allocaTy = op.getAllocaType(); + mlir::Type loweredAllocaPtrTy = getTypeConverter()->convertType(allocaPtrTy); + mlir::Type loweredAllocaTy = getTypeConverter()->convertType(allocaTy); + + cir::AllocaOp loweredOp = cir::AllocaOp::create( + rewriter, op.getLoc(), loweredAllocaPtrTy, loweredAllocaTy, op.getName(), + op.getAlignmentAttr(), /*dynAllocSize=*/adaptor.getDynAllocSize()); + loweredOp.setInit(op.getInit()); + loweredOp.setConstant(op.getConstant()); + loweredOp.setAnnotationsAttr(op.getAnnotationsAttr()); + + rewriter.replaceOp(op, loweredOp); + return mlir::success(); +} + +mlir::LogicalResult CIRConstantOpABILowering::matchAndRewrite( + cir::ConstantOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + + if (mlir::isa(op.getType())) { + auto dataMember = mlir::cast(op.getValue()); + mlir::DataLayout layout(op->getParentOfType()); + mlir::TypedAttr abiValue = lowerModule->getCXXABI().lowerDataMemberConstant( + dataMember, layout, *getTypeConverter()); + rewriter.replaceOpWithNewOp(op, abiValue); + return mlir::success(); + } + + llvm_unreachable("constant operand is not an CXXABI-dependent type"); +} + +mlir::LogicalResult CIRFuncOpABILowering::matchAndRewrite( + cir::FuncOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + cir::FuncType opFuncType = op.getFunctionType(); + mlir::TypeConverter::SignatureConversion signatureConversion( + opFuncType.getNumInputs()); + + for (const auto &[i, argType] : llvm::enumerate(opFuncType.getInputs())) { + mlir::Type loweredArgType = getTypeConverter()->convertType(argType); + if (!loweredArgType) + return mlir::failure(); + signatureConversion.addInputs(i, loweredArgType); + } + + mlir::Type loweredResultType = + getTypeConverter()->convertType(opFuncType.getReturnType()); + if (!loweredResultType) + return mlir::failure(); + + auto loweredFuncType = + cir::FuncType::get(signatureConversion.getConvertedTypes(), + loweredResultType, /*isVarArg=*/opFuncType.isVarArg()); + + // Create a new cir.func operation for the CXXABI-lowered function. + cir::FuncOp loweredFuncOp = rewriter.cloneWithoutRegions(op); + loweredFuncOp.setFunctionType(loweredFuncType); + rewriter.inlineRegionBefore(op.getBody(), loweredFuncOp.getBody(), + loweredFuncOp.end()); + if (mlir::failed(rewriter.convertRegionTypes( + &loweredFuncOp.getBody(), *getTypeConverter(), &signatureConversion))) + return mlir::failure(); + + rewriter.eraseOp(op); + return mlir::success(); +} + +mlir::LogicalResult CIRGlobalOpABILowering::matchAndRewrite( + cir::GlobalOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + mlir::Type ty = op.getSymType(); + mlir::Type loweredTy = getTypeConverter()->convertType(ty); + if (!loweredTy) + return mlir::failure(); + + mlir::DataLayout layout(op->getParentOfType()); + + mlir::Attribute loweredInit; + if (mlir::isa(ty)) { + cir::DataMemberAttr init = + mlir::cast_if_present(op.getInitialValueAttr()); + loweredInit = lowerModule->getCXXABI().lowerDataMemberConstant( + init, layout, *getTypeConverter()); + } else { + llvm_unreachable( + "inputs to cir.global in ABI lowering must be data member or method"); + } + + auto newOp = mlir::cast(rewriter.clone(*op.getOperation())); + newOp.setInitialValueAttr(loweredInit); + newOp.setSymType(loweredTy); + rewriter.replaceOp(op, newOp); + return mlir::success(); +} + +mlir::LogicalResult CIRGetRuntimeMemberOpABILowering::matchAndRewrite( + cir::GetRuntimeMemberOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + mlir::Type resTy = getTypeConverter()->convertType(op.getType()); + mlir::Operation *newOp = lowerModule->getCXXABI().lowerGetRuntimeMember( + op, resTy, adaptor.getAddr(), adaptor.getMember(), rewriter); + rewriter.replaceOp(op, newOp); + return mlir::success(); +} + +// Prepare the type converter for the CXXABI lowering pass. +// Even though this is a CIR-to-CIR pass, we are eliminating some CIR types. +static void prepareCXXABITypeConverter(mlir::TypeConverter &converter, + mlir::DataLayout &dataLayout, + cir::LowerModule &lowerModule) { + converter.addConversion([&](mlir::Type type) -> mlir::Type { return type; }); + // This is necessary in order to convert CIR pointer types that are pointing + // to CIR types that we are lowering in this pass. + converter.addConversion([&](cir::PointerType type) -> mlir::Type { + mlir::Type loweredPointeeType = converter.convertType(type.getPointee()); + if (!loweredPointeeType) + return {}; + return cir::PointerType::get(type.getContext(), loweredPointeeType, + type.getAddrSpace()); + }); + converter.addConversion([&](cir::DataMemberType type) -> mlir::Type { + mlir::Type abiType = + lowerModule.getCXXABI().lowerDataMemberType(type, converter); + return converter.convertType(abiType); + }); + // This is necessary in order to convert CIR function types that have argument + // or return types that use CIR types that we are lowering in this pass. + converter.addConversion([&](cir::FuncType type) -> mlir::Type { + llvm::SmallVector loweredInputTypes; + loweredInputTypes.reserve(type.getNumInputs()); + if (mlir::failed( + converter.convertTypes(type.getInputs(), loweredInputTypes))) + return {}; + + mlir::Type loweredReturnType = converter.convertType(type.getReturnType()); + if (!loweredReturnType) + return {}; + + return cir::FuncType::get(loweredInputTypes, loweredReturnType, + /*isVarArg=*/type.getVarArg()); + }); +} + +static void +populateCXXABIConversionTarget(mlir::ConversionTarget &target, + const mlir::TypeConverter &typeConverter) { + target.addLegalOp(); + + // The ABI lowering pass is interested in CIR operations with operands or + // results of CXXABI-dependent types, or CIR operations with regions whose + // block arguments are of CXXABI-dependent types. + target.addDynamicallyLegalDialect( + [&typeConverter](mlir::Operation *op) { + if (!typeConverter.isLegal(op)) + return false; + return std::all_of(op->getRegions().begin(), op->getRegions().end(), + [&typeConverter](mlir::Region ®ion) { + return typeConverter.isLegal(®ion); + }); + }); + + // Some CIR ops needs special checking for legality + target.addDynamicallyLegalOp([&typeConverter](cir::FuncOp op) { + return typeConverter.isLegal(op.getFunctionType()); + }); + target.addDynamicallyLegalOp( + [&typeConverter](cir::GlobalOp op) { + return typeConverter.isLegal(op.getSymType()); + }); +} + +//===----------------------------------------------------------------------===// +// The Pass +//===----------------------------------------------------------------------===// + +void CXXABILoweringPass::runOnOperation() { + auto module = mlir::cast(getOperation()); + mlir::MLIRContext *ctx = module.getContext(); + + // If the triple is not present, e.g. CIR modules parsed from text, we + // cannot init LowerModule properly. + assert(!cir::MissingFeatures::makeTripleAlwaysPresent()); + // If no target triple is available, skip the ABI lowering pass. + if (!module->hasAttr(cir::CIRDialect::getTripleAttrName())) + return; + + mlir::PatternRewriter rewriter(ctx); + std::unique_ptr lowerModule = + cir::createLowerModule(module, rewriter); + + mlir::DataLayout dataLayout(module); + mlir::TypeConverter typeConverter; + prepareCXXABITypeConverter(typeConverter, dataLayout, *lowerModule); + + mlir::RewritePatternSet patterns(ctx); + patterns.add(patterns.getContext(), + typeConverter); + patterns.add< + // clang-format off + CIRAllocaOpABILowering, + CIRConstantOpABILowering, + CIRFuncOpABILowering, + CIRGetRuntimeMemberOpABILowering, + CIRGlobalOpABILowering + // clang-format on + >(patterns.getContext(), typeConverter, dataLayout, *lowerModule); + + mlir::ConversionTarget target(*ctx); + populateCXXABIConversionTarget(target, typeConverter); + + if (failed(mlir::applyPartialConversion(module, target, std::move(patterns)))) + signalPassFailure(); +} + +std::unique_ptr mlir::createCXXABILoweringPass() { + return std::make_unique(); +} diff --git a/clang/lib/CIR/Lowering/CIRPasses.cpp b/clang/lib/CIR/Lowering/CIRPasses.cpp index ccc838717e421..72348ff6287b4 100644 --- a/clang/lib/CIR/Lowering/CIRPasses.cpp +++ b/clang/lib/CIR/Lowering/CIRPasses.cpp @@ -31,6 +31,7 @@ mlir::LogicalResult runCIRToCIRPasses(mlir::ModuleOp theModule, if (enableCIRSimplify) pm.addPass(mlir::createCIRSimplifyPass()); + pm.addPass(mlir::createCXXABILoweringPass()); pm.addPass(mlir::createLoweringPreparePass(&astContext)); pm.enableVerifier(enableVerifier); diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index c01b2c1487709..27dd37f14c728 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -258,9 +258,9 @@ class CIRAttrToValue { return llvm::TypeSwitch(attr) .Case([&](auto attrT) { return visitCirAttr(attrT); }) + cir::ConstPtrAttr, cir::GlobalViewAttr, cir::TypeInfoAttr, + cir::UndefAttr, cir::VTableAttr, cir::ZeroAttr>( + [&](auto attrT) { return visitCirAttr(attrT); }) .Default([&](auto attrT) { return mlir::Value(); }); } @@ -271,7 +271,6 @@ class CIRAttrToValue { mlir::Value visitCirAttr(cir::ConstArrayAttr attr); mlir::Value visitCirAttr(cir::ConstRecordAttr attr); mlir::Value visitCirAttr(cir::ConstVectorAttr attr); - mlir::Value visitCirAttr(cir::DataMemberAttr attr); mlir::Value visitCirAttr(cir::GlobalViewAttr attr); mlir::Value visitCirAttr(cir::TypeInfoAttr attr); mlir::Value visitCirAttr(cir::UndefAttr attr); @@ -282,7 +281,7 @@ class CIRAttrToValue { mlir::Operation *parentOp; mlir::ConversionPatternRewriter &rewriter; const mlir::TypeConverter *converter; - cir::LowerModule *lowerMod; + [[maybe_unused]] cir::LowerModule *lowerMod; }; /// Switches on the type of attribute and calls the appropriate conversion. @@ -526,15 +525,6 @@ mlir::Value CIRAttrToValue::visitCirAttr(cir::ConstVectorAttr attr) { mlirValues)); } -mlir::Value CIRAttrToValue::visitCirAttr(cir::DataMemberAttr attr) { - assert(lowerMod && "lower module is not available"); - mlir::DataLayout layout(parentOp->getParentOfType()); - mlir::TypedAttr init = - lowerMod->getCXXABI().lowerDataMemberConstant(attr, layout, *converter); - // Recursively lower the CIR attribute produced by the C++ ABI. - return visit(init); -} - // GlobalViewAttr visitor. mlir::Value CIRAttrToValue::visitCirAttr(cir::GlobalViewAttr globalAttr) { auto moduleOp = parentOp->getParentOfType(); @@ -1776,14 +1766,6 @@ mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite( return mlir::success(); } attr = op.getValue(); - } else if (mlir::isa(op.getType())) { - assert(lowerMod && "lower module is not available"); - auto dataMember = mlir::cast(op.getValue()); - mlir::DataLayout layout(op->getParentOfType()); - mlir::TypedAttr abiValue = lowerMod->getCXXABI().lowerDataMemberConstant( - dataMember, layout, *typeConverter); - rewriter.replaceOpWithNewOp(op, abiValue); - return mlir::success(); } else if (const auto arrTy = mlir::dyn_cast(op.getType())) { const auto constArr = mlir::dyn_cast(op.getValue()); if (!constArr && !isa(op.getValue())) @@ -2125,10 +2107,11 @@ CIRToLLVMGlobalOpLowering::matchAndRewriteRegionInitializedGlobal( cir::GlobalOp op, mlir::Attribute init, mlir::ConversionPatternRewriter &rewriter) const { // TODO: Generalize this handling when more types are needed here. - assert((isa(init))); + assert( + (isa( + init))); // TODO(cir): once LLVM's dialect has proper equivalent attributes this // should be updated. For now, we use a custom op to initialize globals @@ -2184,9 +2167,9 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite( } } else if (mlir::isa(init.value())) { + cir::ConstComplexAttr, cir::GlobalViewAttr, + cir::TypeInfoAttr, cir::UndefAttr, cir::VTableAttr, + cir::ZeroAttr>(init.value())) { // TODO(cir): once LLVM's dialect has proper equivalent attributes this // should be updated. For now, we use a custom op to initialize globals // to the appropriate value. @@ -2893,13 +2876,6 @@ 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()); @@ -3246,17 +3222,6 @@ mlir::LogicalResult CIRToLLVMGetMemberOpLowering::matchAndRewrite( } } -mlir::LogicalResult CIRToLLVMGetRuntimeMemberOpLowering::matchAndRewrite( - cir::GetRuntimeMemberOp op, OpAdaptor adaptor, - mlir::ConversionPatternRewriter &rewriter) const { - assert(lowerMod && "lowering module is not available"); - mlir::Type llvmResTy = getTypeConverter()->convertType(op.getType()); - mlir::Operation *llvmOp = lowerMod->getCXXABI().lowerGetRuntimeMember( - op, llvmResTy, adaptor.getAddr(), adaptor.getMember(), rewriter); - rewriter.replaceOp(op, llvmOp); - return mlir::success(); -} - mlir::LogicalResult CIRToLLVMUnreachableOpLowering::matchAndRewrite( cir::UnreachableOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const { diff --git a/clang/test/CIR/CodeGen/pointer-to-data-member.cpp b/clang/test/CIR/CodeGen/pointer-to-data-member.cpp index bee6562e59b8e..818cb75624a66 100644 --- a/clang/test/CIR/CodeGen/pointer-to-data-member.cpp +++ b/clang/test/CIR/CodeGen/pointer-to-data-member.cpp @@ -1,5 +1,6 @@ -// 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-cir -mmlir -mlir-print-ir-before=cir-cxxabi-lowering %s -o %t.cir 2> %t-before.cir +// RUN: FileCheck --check-prefix=CIR-BEFORE --input-file=%t-before.cir %s +// RUN: FileCheck --check-prefix=CIR-AFTER --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 @@ -12,7 +13,8 @@ struct Point { }; int Point::*pt_member = &Point::z; -// CIR: cir.global external @pt_member = #cir.data_member<2> : !cir.data_member +// CIR-BEFORE: cir.global external @pt_member = #cir.data_member<2> : !cir.data_member +// CIR-AFTER: cir.global external @pt_member = #cir.int<8> : !s64i // LLVM: @pt_member = global i64 8 // OGCG: @pt_member = global i64 8 @@ -20,12 +22,43 @@ auto test1() -> int Point::* { return &Point::y; } -// CIR: cir.func {{.*}} @_Z5test1v() -> !cir.data_member { -// CIR: %[[RETVAL:.*]] = cir.alloca !cir.data_member, !cir.ptr>, ["__retval"] -// CIR: %[[MEMBER:.*]] = cir.const #cir.data_member<1> : !cir.data_member -// CIR: cir.store %[[MEMBER]], %[[RETVAL]] : !cir.data_member, !cir.ptr> -// CIR: %[[RET:.*]] = cir.load %[[RETVAL]] : !cir.ptr>, !cir.data_member -// CIR: cir.return %[[RET]] : !cir.data_member +int Point::*pt_member_nested_region = test1(); + +// CIR-BEFORE: cir.global external @pt_member_nested_region = ctor : !cir.data_member { +// CIR-BEFORE: %[[MEMBER_PTR_ADDR:.*]] = cir.get_global @pt_member_nested_region : !cir.ptr> +// CIR-BEFORE: %[[MEMBER_PTR:.*]] = cir.call @_Z5test1v() : () -> !cir.data_member +// CIR-BEFORE: cir.store{{.*}} %[[MEMBER_PTR]], %[[MEMBER_PTR_ADDR]] : !cir.data_member, !cir.ptr> +// CIR-BEFORE: } + +// CIR-AFTER: cir.global external @pt_member_nested_region = #cir.int<-1> : !s64i +// CIR-AFTER: cir.func internal private @__cxx_global_var_init() +// CIR-AFTER: %[[MEMBER_PTR_ADDR:.*]] = cir.get_global @pt_member_nested_region : !cir.ptr +// CIR-AFTER: %[[MEMBER_PTR:.*]] = cir.call @_Z5test1v() : () -> !s64i +// CIR-AFTER: cir.store align(8) %[[MEMBER_PTR]], %[[MEMBER_PTR_ADDR]] : !s64i, !cir.ptr + +// LLVM: @pt_member_nested_region = global i64 -1, align 8 +// LLVM: define internal void @__cxx_global_var_init() +// LLVM: %[[MEMBER_PTR:.*]] = call i64 @_Z5test1v() +// LLVM: store i64 %[[MEMBER_PTR]], ptr @pt_member_nested_region, align 8 + +// OGCG: @pt_member_nested_region = global i64 -1, align 8 +// OGCG emits __cxx_global_var_init between test1() and test2(). See checks below. + +// Checks for test1() + +// CIR-BEFORE: cir.func {{.*}} @_Z5test1v() -> !cir.data_member { +// CIR-BEFORE: %[[RETVAL:.*]] = cir.alloca !cir.data_member, !cir.ptr>, ["__retval"] +// CIR-BEFORE: %[[MEMBER:.*]] = cir.const #cir.data_member<1> : !cir.data_member +// CIR-BEFORE: cir.store %[[MEMBER]], %[[RETVAL]] : !cir.data_member, !cir.ptr> +// CIR-BEFORE: %[[RET:.*]] = cir.load %[[RETVAL]] : !cir.ptr>, !cir.data_member +// CIR-BEFORE: cir.return %[[RET]] : !cir.data_member + +// CIR-AFTER: cir.func {{.*}} @_Z5test1v() -> !s64i { +// CIR-AFTER: %[[RETVAL:.*]] = cir.alloca !s64i, !cir.ptr, ["__retval"] +// CIR-AFTER: %[[OFFSET:.*]] = cir.const #cir.int<4> : !s64i +// CIR-AFTER: cir.store %[[OFFSET]], %[[RETVAL]] : !s64i, !cir.ptr +// CIR-AFTER: %[[RET:.*]] = cir.load %[[RETVAL]] : !cir.ptr, !s64i +// CIR-AFTER: cir.return %[[RET]] : !s64i // LLVM: define {{.*}} i64 @_Z5test1v() // LLVM: %[[RETVAL:.*]] = alloca i64 @@ -36,25 +69,48 @@ auto test1() -> int Point::* { // OGCG: define {{.*}} i64 @_Z5test1v() // OGCG: ret i64 4 +// OGCG: define internal void @__cxx_global_var_init() +// OGCG: %[[MEMBER_PTR:.*]] = call i64 @_Z5test1v() +// OGCG: store i64 %[[MEMBER_PTR]], ptr @pt_member_nested_region + + int test2(const Point &pt, int Point::*member) { return pt.*member; } -// CIR: cir.func {{.*}} @_Z5test2RK5PointMS_i( -// CIR-SAME: %[[PT_ARG:.*]]: !cir.ptr -// CIR-SAME: %[[MEMBER_ARG:.*]]: !cir.data_member -// CIR: %[[PT_ADDR:.*]] = cir.alloca {{.*}} ["pt", init, const] -// CIR: %[[MEMBER_ADDR:.*]] = cir.alloca {{.*}} ["member", init] -// CIR: %[[RETVAL_ADDR:.*]] = cir.alloca {{.*}} ["__retval"] -// CIR: cir.store %[[PT_ARG]], %[[PT_ADDR]] -// CIR: cir.store %[[MEMBER_ARG]], %[[MEMBER_ADDR]] -// CIR: %[[PT:.*]] = cir.load %[[PT_ADDR]] -// CIR: %[[MEMBER:.*]] = cir.load{{.*}} %[[MEMBER_ADDR]] -// CIR: %[[RT_MEMBER:.*]] = cir.get_runtime_member %[[PT]][%[[MEMBER]] : !cir.data_member] : !cir.ptr -> !cir.ptr -// CIR: %[[VAL:.*]] = cir.load{{.*}} %[[RT_MEMBER]] -// CIR: cir.store %[[VAL]], %[[RETVAL_ADDR]] -// CIR: %[[RET:.*]] = cir.load{{.*}} %[[RETVAL_ADDR]] -// CIR: cir.return %[[RET]] +// CIR-BEFORE: cir.func {{.*}} @_Z5test2RK5PointMS_i( +// CIR-BEFORE-SAME: %[[PT_ARG:.*]]: !cir.ptr +// CIR-BEFORE-SAME: %[[MEMBER_ARG:.*]]: !cir.data_member +// CIR-BEFORE: %[[PT_ADDR:.*]] = cir.alloca {{.*}} ["pt", init, const] +// CIR-BEFORE: %[[MEMBER_ADDR:.*]] = cir.alloca {{.*}} ["member", init] +// CIR-BEFORE: %[[RETVAL_ADDR:.*]] = cir.alloca {{.*}} ["__retval"] +// CIR-BEFORE: cir.store %[[PT_ARG]], %[[PT_ADDR]] +// CIR-BEFORE: cir.store %[[MEMBER_ARG]], %[[MEMBER_ADDR]] +// CIR-BEFORE: %[[PT:.*]] = cir.load %[[PT_ADDR]] +// CIR-BEFORE: %[[MEMBER:.*]] = cir.load{{.*}} %[[MEMBER_ADDR]] +// CIR-BEFORE: %[[RT_MEMBER:.*]] = cir.get_runtime_member %[[PT]][%[[MEMBER]] : !cir.data_member] : !cir.ptr -> !cir.ptr +// CIR-BEFORE: %[[VAL:.*]] = cir.load{{.*}} %[[RT_MEMBER]] +// CIR-BEFORE: cir.store %[[VAL]], %[[RETVAL_ADDR]] +// CIR-BEFORE: %[[RET:.*]] = cir.load{{.*}} %[[RETVAL_ADDR]] +// CIR-BEFORE: cir.return %[[RET]] + +// CIR-AFTER: cir.func {{.*}} @_Z5test2RK5PointMS_i( +// CIR-AFTER-SAME: %[[PT_ARG:.*]]: !cir.ptr +// CIR-AFTER-SAME: %[[MEMBER_ARG:.*]]: !s64i +// CIR-AFTER: %[[PT_ADDR:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["pt", init, const] +// CIR-AFTER: %[[MEMBER_ADDR:.*]] = cir.alloca !s64i, !cir.ptr, ["member", init] +// CIR-AFTER: %[[RETVAL_ADDR:.*]] = cir.alloca !s32i, !cir.ptr, ["__retval"] +// CIR-AFTER: cir.store %[[PT_ARG]], %[[PT_ADDR]] : !cir.ptr, !cir.ptr> +// CIR-AFTER: cir.store %[[MEMBER_ARG]], %[[MEMBER_ADDR]] : !s64i, !cir.ptr +// CIR-AFTER: %[[PT:.*]] = cir.load %[[PT_ADDR]] : !cir.ptr>, !cir.ptr +// CIR-AFTER: %[[MEMBER:.*]] = cir.load{{.*}} %[[MEMBER_ADDR]] : !cir.ptr, !s64i +// CIR-AFTER: %[[BYTE_PTR:.*]] = cir.cast bitcast %[[PT]] : !cir.ptr -> !cir.ptr +// CIR-AFTER: %[[BYTE_PTR_STRIDE:.*]] = cir.ptr_stride %[[BYTE_PTR]], %[[MEMBER]] : (!cir.ptr, !s64i) -> !cir.ptr +// CIR-AFTER: %[[VAL_ADDR:.*]] = cir.cast bitcast %[[BYTE_PTR_STRIDE]] : !cir.ptr -> !cir.ptr +// CIR-AFTER: %[[VAL:.*]] = cir.load{{.*}} %[[VAL_ADDR]] : !cir.ptr, !s32i +// CIR-AFTER: cir.store %[[VAL]], %[[RETVAL_ADDR]] : !s32i, !cir.ptr +// CIR-AFTER: %[[RET:.*]] = cir.load{{.*}} %[[RETVAL_ADDR]] : !cir.ptr, !s32i +// CIR-AFTER: cir.return %[[RET]] : !s32i // LLVM: define {{.*}} i32 @_Z5test2RK5PointMS_i(ptr %[[PT_ARG:.*]], i64 %[[MEMBER_ARG:.*]]) // LLVM: %[[PT_ADDR:.*]] = alloca ptr @@ -85,21 +141,39 @@ int test3(const Point *pt, int Point::*member) { return pt->*member; } -// CIR: cir.func {{.*}} @_Z5test3PK5PointMS_i( -// CIR-SAME: %[[PT_ARG:.*]]: !cir.ptr -// CIR-SAME: %[[MEMBER_ARG:.*]]: !cir.data_member -// CIR: %[[PT_ADDR:.*]] = cir.alloca {{.*}} ["pt", init] -// CIR: %[[MEMBER_ADDR:.*]] = cir.alloca {{.*}} ["member", init] -// CIR: %[[RETVAL_ADDR:.*]] = cir.alloca {{.*}} ["__retval"] -// CIR: cir.store %[[PT_ARG]], %[[PT_ADDR]] -// CIR: cir.store %[[MEMBER_ARG]], %[[MEMBER_ADDR]] -// CIR: %[[PT:.*]] = cir.load{{.*}} %[[PT_ADDR]] -// CIR: %[[MEMBER:.*]] = cir.load{{.*}} %[[MEMBER_ADDR]] -// CIR: %[[RT_MEMBER:.*]] = cir.get_runtime_member %[[PT]][%[[MEMBER]] : !cir.data_member] : !cir.ptr -> !cir.ptr -// CIR: %[[VAL:.*]] = cir.load{{.*}} %[[RT_MEMBER]] -// CIR: cir.store %[[VAL]], %[[RETVAL_ADDR]] -// CIR: %[[RET:.*]] = cir.load{{.*}} %[[RETVAL_ADDR]] -// CIR: cir.return %[[RET]] +// CIR-BEFORE: cir.func {{.*}} @_Z5test3PK5PointMS_i( +// CIR-BEFORE-SAME: %[[PT_ARG:.*]]: !cir.ptr +// CIR-BEFORE-SAME: %[[MEMBER_ARG:.*]]: !cir.data_member +// CIR-BEFORE: %[[PT_ADDR:.*]] = cir.alloca {{.*}} ["pt", init] +// CIR-BEFORE: %[[MEMBER_ADDR:.*]] = cir.alloca {{.*}} ["member", init] +// CIR-BEFORE: %[[RETVAL_ADDR:.*]] = cir.alloca {{.*}} ["__retval"] +// CIR-BEFORE: cir.store %[[PT_ARG]], %[[PT_ADDR]] +// CIR-BEFORE: cir.store %[[MEMBER_ARG]], %[[MEMBER_ADDR]] +// CIR-BEFORE: %[[PT:.*]] = cir.load{{.*}} %[[PT_ADDR]] +// CIR-BEFORE: %[[MEMBER:.*]] = cir.load{{.*}} %[[MEMBER_ADDR]] +// CIR-BEFORE: %[[RT_MEMBER:.*]] = cir.get_runtime_member %[[PT]][%[[MEMBER]] : !cir.data_member] : !cir.ptr -> !cir.ptr +// CIR-BEFORE: %[[VAL:.*]] = cir.load{{.*}} %[[RT_MEMBER]] +// CIR-BEFORE: cir.store %[[VAL]], %[[RETVAL_ADDR]] +// CIR-BEFORE: %[[RET:.*]] = cir.load{{.*}} %[[RETVAL_ADDR]] +// CIR-BEFORE: cir.return %[[RET]] + +// CIR-AFTER: cir.func {{.*}} @_Z5test3PK5PointMS_i( +// CIR-AFTER-SAME: %[[PT_ARG:.*]]: !cir.ptr +// CIR-AFTER-SAME: %[[MEMBER_ARG:.*]]: !s64i +// CIR-AFTER: %[[PT_ADDR:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["pt", init] +// CIR-AFTER: %[[MEMBER_ADDR:.*]] = cir.alloca !s64i, !cir.ptr, ["member", init] +// CIR-AFTER: %[[RETVAL_ADDR:.*]] = cir.alloca !s32i, !cir.ptr, ["__retval"] +// CIR-AFTER: cir.store %[[PT_ARG]], %[[PT_ADDR]] : !cir.ptr, !cir.ptr> +// CIR-AFTER: cir.store %[[MEMBER_ARG]], %[[MEMBER_ADDR]] : !s64i, !cir.ptr +// CIR-AFTER: %[[PT:.*]] = cir.load{{.*}} %[[PT_ADDR]] : !cir.ptr>, !cir.ptr +// CIR-AFTER: %[[MEMBER:.*]] = cir.load{{.*}} %[[MEMBER_ADDR]] : !cir.ptr, !s64i +// CIR-AFTER: %[[BYTE_PTR:.*]] = cir.cast bitcast %[[PT]] : !cir.ptr -> !cir.ptr +// CIR-AFTER: %[[BYTE_PTR_STRIDE:.*]] = cir.ptr_stride %[[BYTE_PTR]], %[[MEMBER]] : (!cir.ptr, !s64i) -> !cir.ptr +// CIR-AFTER: %[[VAL_ADDR:.*]] = cir.cast bitcast %[[BYTE_PTR_STRIDE]] : !cir.ptr -> !cir.ptr +// CIR-AFTER: %[[VAL:.*]] = cir.load{{.*}} %[[VAL_ADDR]] : !cir.ptr, !s32i +// CIR-AFTER: cir.store %[[VAL]], %[[RETVAL_ADDR]] : !s32i, !cir.ptr +// CIR-AFTER: %[[RET:.*]] = cir.load{{.*}} %[[RETVAL_ADDR]] : !cir.ptr, !s32i +// CIR-AFTER: cir.return %[[RET]] : !s32i // LLVM: define {{.*}} i32 @_Z5test3PK5PointMS_i(ptr %[[PT_ARG:.*]], i64 %[[MEMBER_ARG:.*]]) // LLVM: %[[PT_ADDR:.*]] = alloca ptr @@ -132,15 +206,25 @@ auto test4(int Incomplete::*member) -> int Incomplete::* { return member; } -// CIR: cir.func {{.*}} @_Z5test4M10Incompletei( -// CIR-SAME: %[[MEMBER_ARG:.*]]: !cir.data_member -// CIR: %[[MEMBER_ADDR:.*]] = cir.alloca {{.*}} ["member", init] -// CIR: %[[RETVAL_ADDR:.*]] = cir.alloca {{.*}} ["__retval"] -// CIR: cir.store %[[MEMBER_ARG]], %[[MEMBER_ADDR]] -// CIR: %[[MEMBER:.*]] = cir.load{{.*}} %[[MEMBER_ADDR]] -// CIR: cir.store %[[MEMBER]], %[[RETVAL_ADDR]] -// CIR: %[[RET:.*]] = cir.load{{.*}} %[[RETVAL_ADDR]] -// CIR: cir.return %[[RET]] +// CIR-BEFORE: cir.func {{.*}} @_Z5test4M10Incompletei( +// CIR-BEFORE-SAME: %[[MEMBER_ARG:.*]]: !cir.data_member +// CIR-BEFORE: %[[MEMBER_ADDR:.*]] = cir.alloca {{.*}} ["member", init] +// CIR-BEFORE: %[[RETVAL_ADDR:.*]] = cir.alloca {{.*}} ["__retval"] +// CIR-BEFORE: cir.store %[[MEMBER_ARG]], %[[MEMBER_ADDR]] +// CIR-BEFORE: %[[MEMBER:.*]] = cir.load{{.*}} %[[MEMBER_ADDR]] +// CIR-BEFORE: cir.store %[[MEMBER]], %[[RETVAL_ADDR]] +// CIR-BEFORE: %[[RET:.*]] = cir.load{{.*}} %[[RETVAL_ADDR]] +// CIR-BEFORE: cir.return %[[RET]] + +// CIR-AFTER: cir.func {{.*}} @_Z5test4M10Incompletei( +// CIR-AFTER-SAME: %[[MEMBER_ARG:.*]]: !s64i +// CIR-AFTER: %[[MEMBER_ADDR:.*]] = cir.alloca !s64i, !cir.ptr, ["member", init] +// CIR-AFTER: %[[RETVAL_ADDR:.*]] = cir.alloca !s64i, !cir.ptr, ["__retval"] +// CIR-AFTER: cir.store %[[MEMBER_ARG]], %[[MEMBER_ADDR]] : !s64i, !cir.ptr +// CIR-AFTER: %[[MEMBER:.*]] = cir.load{{.*}} %[[MEMBER_ADDR]] : !cir.ptr, !s64i +// CIR-AFTER: cir.store %[[MEMBER]], %[[RETVAL_ADDR]] : !s64i, !cir.ptr +// CIR-AFTER: %[[RET:.*]] = cir.load{{.*}} %[[RETVAL_ADDR]] : !cir.ptr, !s64i +// CIR-AFTER: cir.return %[[RET]] : !s64i // LLVM: define {{.*}} i64 @_Z5test4M10Incompletei(i64 %[[MEMBER_ARG:.*]]) // LLVM: %[[MEMBER_ADDR:.*]] = alloca i64 @@ -161,21 +245,39 @@ int test5(Incomplete *ic, int Incomplete::*member) { return ic->*member; } -// CIR: cir.func {{.*}} @_Z5test5P10IncompleteMS_i( -// CIR-SAME: %[[IC_ARG:.*]]: !cir.ptr -// CIR-SAME: %[[MEMBER_ARG:.*]]: !cir.data_member -// CIR: %[[IC_ADDR:.*]] = cir.alloca {{.*}} ["ic", init] -// CIR: %[[MEMBER_ADDR:.*]] = cir.alloca {{.*}} ["member", init] -// CIR: %[[RETVAL_ADDR:.*]] = cir.alloca {{.*}} ["__retval"] -// CIR: cir.store %[[IC_ARG]], %[[IC_ADDR]] -// CIR: cir.store %[[MEMBER_ARG]], %[[MEMBER_ADDR]] -// CIR: %[[IC:.*]] = cir.load{{.*}} %[[IC_ADDR]] -// CIR: %[[MEMBER:.*]] = cir.load{{.*}} %[[MEMBER_ADDR]] -// CIR: %[[RT_MEMBER:.*]] = cir.get_runtime_member %[[IC]][%[[MEMBER]] : !cir.data_member] : !cir.ptr -> !cir.ptr -// CIR: %[[VAL:.*]] = cir.load{{.*}} %[[RT_MEMBER]] -// CIR: cir.store %[[VAL]], %[[RETVAL_ADDR]] -// CIR: %[[RET:.*]] = cir.load{{.*}} %[[RETVAL_ADDR]] -// CIR: cir.return %[[RET]] +// CIR-BEFORE: cir.func {{.*}} @_Z5test5P10IncompleteMS_i( +// CIR-BEFORE-SAME: %[[IC_ARG:.*]]: !cir.ptr +// CIR-BEFORE-SAME: %[[MEMBER_ARG:.*]]: !cir.data_member +// CIR-BEFORE: %[[IC_ADDR:.*]] = cir.alloca {{.*}} ["ic", init] +// CIR-BEFORE: %[[MEMBER_ADDR:.*]] = cir.alloca {{.*}} ["member", init] +// CIR-BEFORE: %[[RETVAL_ADDR:.*]] = cir.alloca {{.*}} ["__retval"] +// CIR-BEFORE: cir.store %[[IC_ARG]], %[[IC_ADDR]] +// CIR-BEFORE: cir.store %[[MEMBER_ARG]], %[[MEMBER_ADDR]] +// CIR-BEFORE: %[[IC:.*]] = cir.load{{.*}} %[[IC_ADDR]] +// CIR-BEFORE: %[[MEMBER:.*]] = cir.load{{.*}} %[[MEMBER_ADDR]] +// CIR-BEFORE: %[[RT_MEMBER:.*]] = cir.get_runtime_member %[[IC]][%[[MEMBER]] : !cir.data_member] : !cir.ptr -> !cir.ptr +// CIR-BEFORE: %[[VAL:.*]] = cir.load{{.*}} %[[RT_MEMBER]] +// CIR-BEFORE: cir.store %[[VAL]], %[[RETVAL_ADDR]] +// CIR-BEFORE: %[[RET:.*]] = cir.load{{.*}} %[[RETVAL_ADDR]] +// CIR-BEFORE: cir.return %[[RET]] + +// CIR-AFTER: cir.func {{.*}} @_Z5test5P10IncompleteMS_i( +// CIR-AFTER-SAME: %[[IC_ARG:.*]]: !cir.ptr +// CIR-AFTER-SAME: %[[MEMBER_ARG:.*]]: !s64i +// CIR-AFTER: %[[IC_ADDR:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["ic", init] +// CIR-AFTER: %[[MEMBER_ADDR:.*]] = cir.alloca !s64i, !cir.ptr, ["member", init] +// CIR-AFTER: %[[RETVAL_ADDR:.*]] = cir.alloca !s32i, !cir.ptr, ["__retval"] +// CIR-AFTER: cir.store %[[IC_ARG]], %[[IC_ADDR]] : !cir.ptr, !cir.ptr> +// CIR-AFTER: cir.store %[[MEMBER_ARG]], %[[MEMBER_ADDR]] : !s64i, !cir.ptr +// CIR-AFTER: %[[IC:.*]] = cir.load{{.*}} %[[IC_ADDR]] : !cir.ptr>, !cir.ptr +// CIR-AFTER: %[[MEMBER:.*]] = cir.load{{.*}} %[[MEMBER_ADDR]] : !cir.ptr, !s64i +// CIR-AFTER: %[[BYTE_PTR:.*]] = cir.cast bitcast %[[IC]] : !cir.ptr -> !cir.ptr +// CIR-AFTER: %[[BYTE_PTR_STRIDE:.*]] = cir.ptr_stride %[[BYTE_PTR]], %[[MEMBER]] : (!cir.ptr, !s64i) -> !cir.ptr +// CIR-AFTER: %[[VAL_ADDR:.*]] = cir.cast bitcast %[[BYTE_PTR_STRIDE]] : !cir.ptr -> !cir.ptr +// CIR-AFTER: %[[VAL:.*]] = cir.load{{.*}} %[[VAL_ADDR]] : !cir.ptr, !s32i +// CIR-AFTER: cir.store %[[VAL]], %[[RETVAL_ADDR]] : !s32i, !cir.ptr +// CIR-AFTER: %[[RET:.*]] = cir.load{{.*}} %[[RETVAL_ADDR]] : !cir.ptr, !s32i +// CIR-AFTER: cir.return %[[RET]] : !s32i // LLVM: define {{.*}} i32 @_Z5test5P10IncompleteMS_i(ptr %[[IC_ARG:.*]], i64 %[[MEMBER_ARG:.*]]) // LLVM: %[[IC_ADDR:.*]] = alloca ptr