diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 4922f83df0efb..ed777fcb87324 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -2224,7 +2224,7 @@ def CIR_GlobalOp : CIR_Op<"global", [ cir::GlobalOp op, mlir::Attribute init, mlir::ConversionPatternRewriter &rewriter) const; - void setupRegionInitializedLLVMGlobalOp( + mlir::LLVM::GlobalOp setupRegionInitializedLLVMGlobalOp( cir::GlobalOp op, mlir::ConversionPatternRewriter &rewriter) const; mutable mlir::LLVM::ComdatOp comdatOp = nullptr; @@ -2746,6 +2746,10 @@ def CIR_FuncOp : CIR_Op<"func", [ The `always_inline` attribute marks a function that should always be inlined. The `inline_hint` attribute suggests that the function should be inlined. + The `personality` attribute specifies the personality function to use for + exception handling. This is a symbol reference to the personality function + (e.g., `@__gxx_personality_v0` for C++ exceptions). + Example: ```mlir @@ -2792,6 +2796,7 @@ def CIR_FuncOp : CIR_Op<"func", [ OptionalAttr:$arg_attrs, OptionalAttr:$res_attrs, OptionalAttr:$aliasee, + OptionalAttr:$personality, CIR_OptionalPriorityAttr:$global_ctor_priority, CIR_OptionalPriorityAttr:$global_dtor_priority, OptionalAttr:$cxx_special_member diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 9975ee0142d77..ad8c7ca7336e6 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -93,7 +93,6 @@ struct MissingFeatures { static bool opFuncNoReturn() { return false; } static bool setFunctionAttributes() { return false; } static bool setLLVMFunctionFEnvAttributes() { return false; } - static bool setFunctionPersonality() { return false; } // CallOp handling static bool opCallAggregateArgs() { return false; } @@ -279,6 +278,7 @@ struct MissingFeatures { static bool fpConstraints() { return false; } static bool generateDebugInfo() { return false; } + static bool getRuntimeFunctionDecl() { return false; } static bool globalViewIndices() { return false; } static bool globalViewIntLowering() { return false; } static bool handleBuiltinICEArguments() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenException.cpp b/clang/lib/CIR/CodeGen/CIRGenException.cpp index 06b3aec38daa4..70812452ed999 100644 --- a/clang/lib/CIR/CodeGen/CIRGenException.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp @@ -185,6 +185,18 @@ const EHPersonality &EHPersonality::get(CIRGenFunction &cgf) { return get(cgf.cgm, dyn_cast_or_null(fg)); } +static llvm::StringRef getPersonalityFn(CIRGenModule &cgm, + const EHPersonality &personality) { + // Create the personality function type: i32 (...) + mlir::Type i32Ty = cgm.getBuilder().getI32Type(); + auto funcTy = cir::FuncType::get({}, i32Ty, /*isVarArg=*/true); + + cir::FuncOp personalityFn = cgm.createRuntimeFunction( + funcTy, personality.personalityFn, mlir::ArrayAttr(), /*isLocal=*/true); + + return personalityFn.getSymName(); +} + void CIRGenFunction::emitCXXThrowExpr(const CXXThrowExpr *e) { const llvm::Triple &triple = getTarget().getTriple(); if (cgm.getLangOpts().OpenMPIsTargetDevice && @@ -641,10 +653,14 @@ void CIRGenFunction::populateCatchHandlersIfRequired(cir::TryOp tryOp) { assert(ehStack.requiresCatchOrCleanup()); assert(!ehStack.empty()); - assert(!cir::MissingFeatures::setFunctionPersonality()); + const EHPersonality &personality = EHPersonality::get(*this); + + // Set personality function if not already set + auto funcOp = mlir::cast(curFn); + if (!funcOp.getPersonality()) + funcOp.setPersonality(getPersonalityFn(cgm, personality)); // CIR does not cache landing pads. - const EHPersonality &personality = EHPersonality::get(*this); if (personality.usesFuncletPads()) { cgm.errorNYI("getInvokeDestImpl: usesFuncletPads"); } else { diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 1ad1c2fa41aa1..edaf1c46eb097 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -2354,14 +2354,27 @@ void CIRGenModule::setCXXSpecialMemberAttr( } } +static void setWindowsItaniumDLLImport(CIRGenModule &cgm, bool isLocal, + cir::FuncOp funcOp, StringRef name) { + // In Windows Itanium environments, try to mark runtime functions + // dllimport. For Mingw and MSVC, don't. We don't really know if the user + // will link their standard library statically or dynamically. Marking + // functions imported when they are not imported can cause linker errors + // and warnings. + if (!isLocal && cgm.getTarget().getTriple().isWindowsItaniumEnvironment() && + !cgm.getCodeGenOpts().LTOVisibilityPublicStd) { + assert(!cir::MissingFeatures::getRuntimeFunctionDecl()); + assert(!cir::MissingFeatures::setDLLStorageClass()); + assert(!cir::MissingFeatures::opGlobalDLLImportExport()); + } +} + cir::FuncOp CIRGenModule::createRuntimeFunction(cir::FuncType ty, StringRef name, mlir::ArrayAttr, - [[maybe_unused]] bool isLocal, + bool isLocal, bool assumeConvergent) { if (assumeConvergent) errorNYI("createRuntimeFunction: assumeConvergent"); - if (isLocal) - errorNYI("createRuntimeFunction: local"); cir::FuncOp entry = getOrCreateCIRFunction(name, ty, GlobalDecl(), /*forVtable=*/false); @@ -2370,7 +2383,7 @@ cir::FuncOp CIRGenModule::createRuntimeFunction(cir::FuncType ty, // TODO(cir): set the attributes of the function. assert(!cir::MissingFeatures::setLLVMFunctionFEnvAttributes()); assert(!cir::MissingFeatures::opFuncCallingConv()); - assert(!cir::MissingFeatures::opGlobalDLLImportExport()); + setWindowsItaniumDLLImport(*this, isLocal, entry, name); entry.setDSOLocal(true); } diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index ffe93c5b4124b..95fc3afffb156 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -1910,6 +1910,19 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) { hasAlias = true; } + mlir::StringAttr personalityNameAttr = getPersonalityAttrName(state.name); + if (parser.parseOptionalKeyword("personality").succeeded()) { + if (parser.parseLParen().failed()) + return failure(); + mlir::StringAttr personalityAttr; + if (parser.parseOptionalSymbolName(personalityAttr).failed()) + return failure(); + state.addAttribute(personalityNameAttr, + FlatSymbolRefAttr::get(personalityAttr)); + if (parser.parseRParen().failed()) + return failure(); + } + auto parseGlobalDtorCtor = [&](StringRef keyword, llvm::function_ref prio)> createAttr) @@ -2111,6 +2124,12 @@ void cir::FuncOp::print(OpAsmPrinter &p) { p << ")"; } + if (std::optional personalityName = getPersonality()) { + p << " personality("; + p.printSymbolName(*personalityName); + p << ")"; + } + if (auto specialMemberAttr = getCxxSpecialMember()) { p << " special_member<"; p.printAttribute(*specialMemberAttr); diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 00307df62ce5a..e306cbbe8db84 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -724,8 +724,7 @@ mlir::LogicalResult CIRToLLVMIsFPClassOpLowering::matchAndRewrite( mlir::LogicalResult CIRToLLVMAssumeOpLowering::matchAndRewrite( cir::AssumeOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const { - auto cond = adaptor.getPredicate(); - rewriter.replaceOpWithNewOp(op, cond); + rewriter.replaceOpWithNewOp(op, adaptor.getPredicate()); return mlir::success(); } @@ -1138,11 +1137,11 @@ mlir::LogicalResult CIRToLLVMBrCondOpLowering::matchAndRewrite( // ZExtOp and if so, delete it if it has a single use. assert(!cir::MissingFeatures::zextOp()); - mlir::Value i1Condition = adaptor.getCond(); - + rewriter.replaceOpWithNewOp( - brOp, i1Condition, brOp.getDestTrue(), adaptor.getDestOperandsTrue(), - brOp.getDestFalse(), adaptor.getDestOperandsFalse()); + brOp, adaptor.getCond(), brOp.getDestTrue(), + adaptor.getDestOperandsTrue(), brOp.getDestFalse(), + adaptor.getDestOperandsFalse()); return mlir::success(); } @@ -1964,12 +1963,12 @@ mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewriteAlias( lowerFuncAttributes(op, /*filterArgAndResAttrs=*/false, attributes); mlir::Location loc = op.getLoc(); + mlir::OpBuilder builder(op.getContext()); auto aliasOp = rewriter.replaceOpWithNewOp( op, ty, convertLinkage(op.getLinkage()), op.getName(), op.getDsoLocal(), /*threadLocal=*/false, attributes); // Create the alias body - mlir::OpBuilder builder(op.getContext()); mlir::Block *block = builder.createBlock(&aliasOp.getInitializerRegion()); builder.setInsertionPointToStart(block); // The type of AddressOfOp is always a pointer. @@ -2037,6 +2036,9 @@ mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite( fn.setAlwaysInline(*inlineKind == cir::InlineKind::AlwaysInline); } + if (std::optional personality = op.getPersonality()) + fn.setPersonality(*personality); + fn.setVisibility_( lowerCIRVisibilityToLLVMVisibility(op.getGlobalVisibility())); @@ -2076,7 +2078,8 @@ mlir::LogicalResult CIRToLLVMGetGlobalOpLowering::matchAndRewrite( /// Replace CIR global with a region initialized LLVM global and update /// insertion point to the end of the initializer block. -void CIRToLLVMGlobalOpLowering::setupRegionInitializedLLVMGlobalOp( +mlir::LLVM::GlobalOp +CIRToLLVMGlobalOpLowering::setupRegionInitializedLLVMGlobalOp( cir::GlobalOp op, mlir::ConversionPatternRewriter &rewriter) const { const mlir::Type llvmType = convertTypeForMemory(*getTypeConverter(), dataLayout, op.getSymType()); @@ -2102,6 +2105,7 @@ void CIRToLLVMGlobalOpLowering::setupRegionInitializedLLVMGlobalOp( isDsoLocal, isThreadLocal, comdatAttr, attributes); newGlobalOp.getRegion().emplaceBlock(); rewriter.setInsertionPointToEnd(newGlobalOp.getInitializerBlock()); + return newGlobalOp; } mlir::LogicalResult @@ -2119,8 +2123,9 @@ CIRToLLVMGlobalOpLowering::matchAndRewriteRegionInitializedGlobal( // should be updated. For now, we use a custom op to initialize globals // to the appropriate value. const mlir::Location loc = op.getLoc(); - setupRegionInitializedLLVMGlobalOp(op, rewriter); - CIRAttrToValue valueConverter(op, rewriter, typeConverter); + mlir::LLVM::GlobalOp newGlobalOp = + setupRegionInitializedLLVMGlobalOp(op, rewriter); + CIRAttrToValue valueConverter(newGlobalOp, rewriter, typeConverter); mlir::Value value = valueConverter.visit(init); mlir::LLVM::ReturnOp::create(rewriter, loc, value); return mlir::success(); @@ -2816,42 +2821,45 @@ mlir::LogicalResult CIRToLLVMShiftOpLowering::matchAndRewrite( mlir::LogicalResult CIRToLLVMSelectOpLowering::matchAndRewrite( cir::SelectOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const { - auto getConstantBool = [](mlir::Value value) -> cir::BoolAttr { - auto definingOp = value.getDefiningOp(); - if (!definingOp) - return {}; - auto constValue = definingOp.getValueAttr(); - if (!constValue) - return {}; + // Helper to extract boolean constant value + auto getConstantBool = [](mlir::Value value) -> std::optional { + auto constOp = value.getDefiningOp(); + if (!constOp) + return std::nullopt; - return constValue; + auto intAttr = mlir::dyn_cast(constOp.getValue()); + if (!intAttr) + return std::nullopt; + + return !intAttr.getValue().isZero(); }; + mlir::Value condition = adaptor.getCondition(); + mlir::Value trueValue = adaptor.getTrueValue(); + mlir::Value falseValue = adaptor.getFalseValue(); + // Two special cases in the LLVMIR codegen of select op: - // - select %0, %1, false => and %0, %1 - // - select %0, true, %1 => or %0, %1 + // - select %cond, %val, false => and %cond, %val + // - select %cond, true, %val => or %cond, %val if (mlir::isa(op.getTrueValue().getType())) { - cir::BoolAttr trueValue = getConstantBool(op.getTrueValue()); - cir::BoolAttr falseValue = getConstantBool(op.getFalseValue()); - if (falseValue && !falseValue.getValue()) { - // select %0, %1, false => and %0, %1 - rewriter.replaceOpWithNewOp(op, adaptor.getCondition(), - adaptor.getTrueValue()); + // Optimization: select %cond, %val, false => and %cond, %val + std::optional falseConst = getConstantBool(falseValue); + if (falseConst && !*falseConst) { + rewriter.replaceOpWithNewOp(op, condition, trueValue); return mlir::success(); } - if (trueValue && trueValue.getValue()) { - // select %0, true, %1 => or %0, %1 - rewriter.replaceOpWithNewOp(op, adaptor.getCondition(), - adaptor.getFalseValue()); + // Optimization: select %cond, true, %val => or %cond, %val + std::optional trueConst = getConstantBool(trueValue); + if (trueConst && *trueConst) { + rewriter.replaceOpWithNewOp(op, condition, falseValue); return mlir::success(); } } - mlir::Value llvmCondition = adaptor.getCondition(); - rewriter.replaceOpWithNewOp( - op, llvmCondition, adaptor.getTrueValue(), adaptor.getFalseValue()); - + // Default case: emit standard LLVM select + rewriter.replaceOpWithNewOp(op, condition, trueValue, + falseValue); return mlir::success(); } @@ -3048,13 +3056,12 @@ static void buildCtorDtorList( mlir::LLVM::ReturnOp::create(builder, loc, result); } -// The applyPartialConversion function traverses blocks in the dominance order, -// so it does not lower and operations that are not reachachable from the -// operations passed in as arguments. Since we do need to lower such code in -// order to avoid verification errors occur, we cannot just pass the module op -// to applyPartialConversion. We must build a set of unreachable ops and -// explicitly add them, along with the module, to the vector we pass to -// applyPartialConversion. +// The applyFullConversion function performs a full conversion that legalizes +// all operations. It traverses all operations including unreachable blocks, so +// we need to collect unreachable operations and explicitly add them along with +// the module to ensure they are converted. We use one-shot conversion mode +// (allowPatternRollback = false) for better performance by avoiding rollback +// state maintenance. // // For instance, this CIR code: // @@ -3176,7 +3183,10 @@ void ConvertCIRToLLVMPass::runOnOperation() { ops.push_back(module); collectUnreachable(module, ops); - if (failed(applyPartialConversion(ops, target, std::move(patterns)))) + mlir::ConversionConfig config; + config.allowPatternRollback = false; + + if (failed(applyFullConversion(ops, target, std::move(patterns), config))) signalPassFailure(); // Emit the llvm.global_ctors array. @@ -3397,22 +3407,6 @@ mlir::LogicalResult CIRToLLVMEhInflightOpLowering::matchAndRewrite( mlir::LLVM::ExtractValueOp::create(rewriter, loc, landingPadOp, 1); rewriter.replaceOp(op, mlir::ValueRange{slot, selector}); - // Landing pads are required to be in LLVM functions with personality - // attribute. - // TODO(cir): for now hardcode personality creation in order to start - // adding exception tests, once we annotate CIR with such information, - // change it to be in FuncOp lowering instead. - mlir::OpBuilder::InsertionGuard guard(rewriter); - // Insert personality decl before the current function. - rewriter.setInsertionPoint(llvmFn); - auto personalityFnTy = - mlir::LLVM::LLVMFunctionType::get(rewriter.getI32Type(), {}, - /*isVarArg=*/true); - - const StringRef fnName = "__gxx_personality_v0"; - createLLVMFuncOpIfNotExist(rewriter, op, fnName, personalityFnTy); - llvmFn.setPersonality(fnName); - return mlir::success(); } @@ -3818,11 +3812,31 @@ mlir::LogicalResult CIRToLLVMComplexRealOpLowering::matchAndRewrite( mlir::ConversionPatternRewriter &rewriter) const { mlir::Type resultLLVMTy = getTypeConverter()->convertType(op.getType()); mlir::Value operand = adaptor.getOperand(); + + // FIXME: + // Check if we're extracting from a ComplexCreate that was already lowered + // Pattern: insertvalue(insertvalue(undef, real, 0), imag, 1) -> just use + // 'real' + if (auto secondInsert = operand.getDefiningOp()) { + if (secondInsert.getPosition() == llvm::ArrayRef{1}) { + if (auto firstInsert = secondInsert.getContainer() + .getDefiningOp()) { + if (firstInsert.getPosition() == llvm::ArrayRef{0}) { + // This is the pattern we're looking for - return the real component + // directly + rewriter.replaceOp(op, firstInsert.getValue()); + return mlir::success(); + } + } + } + } + if (mlir::isa(op.getOperand().getType())) { operand = mlir::LLVM::ExtractValueOp::create( rewriter, op.getLoc(), resultLLVMTy, operand, llvm::ArrayRef{0}); } + rewriter.replaceOp(op, operand); return mlir::success(); } @@ -3883,6 +3897,22 @@ mlir::LogicalResult CIRToLLVMComplexImagOpLowering::matchAndRewrite( mlir::Value operand = adaptor.getOperand(); mlir::Location loc = op.getLoc(); + // FIXME: + // Check if we're extracting from a ComplexCreate that was already lowered + // Pattern: insertvalue(insertvalue(undef, real, 0), imag, 1) -> just use + // 'imag' + if (auto secondInsert = operand.getDefiningOp()) { + if (secondInsert.getPosition() == llvm::ArrayRef{1}) { + if (secondInsert.getContainer() + .getDefiningOp()) { + // This is the pattern we're looking for - return the imag component + // directly + rewriter.replaceOp(op, secondInsert.getValue()); + return mlir::success(); + } + } + } + if (mlir::isa(op.getOperand().getType())) { operand = mlir::LLVM::ExtractValueOp::create( rewriter, loc, resultLLVMTy, operand, llvm::ArrayRef{1}); diff --git a/clang/test/CIR/IR/func.cir b/clang/test/CIR/IR/func.cir index d8906ab3e1301..52589c8a5e39e 100644 --- a/clang/test/CIR/IR/func.cir +++ b/clang/test/CIR/IR/func.cir @@ -44,6 +44,14 @@ cir.func @intfunc() -> !s32i { cir.func @a_empty() alias(@empty) // CHECK: cir.func @a_empty() alias(@empty) +// Should print/parse function personality. +cir.func @personality_func() personality(@__gxx_personality_v0) { + cir.return +} +// CHECK: cir.func @personality_func() personality(@__gxx_personality_v0) { +// CHECK: cir.return +// CHECK: } + // int scopes() { // { // { diff --git a/clang/test/CIR/Lowering/eh-inflight.cir b/clang/test/CIR/Lowering/eh-inflight.cir index 31e1e474a046b..89f585f439fa4 100644 --- a/clang/test/CIR/Lowering/eh-inflight.cir +++ b/clang/test/CIR/Lowering/eh-inflight.cir @@ -1,12 +1,15 @@ // RUN: cir-opt %s -cir-to-llvm -o %t.cir +!s32i = !cir.int !u8i = !cir.int module { +cir.func private @__gxx_personality_v0(...) -> !s32i + // CHECK: llvm.func @__gxx_personality_v0(...) -> i32 -cir.func @inflight_exception() { +cir.func @inflight_exception() personality(@__gxx_personality_v0) { %exception_ptr, %type_id = cir.eh.inflight_exception cir.return } @@ -19,7 +22,7 @@ cir.func @inflight_exception() { // CHECK: llvm.return // CHECK: } -cir.func @inflight_exception_with_cleanup() { +cir.func @inflight_exception_with_cleanup() personality(@__gxx_personality_v0) { %exception_ptr, %type_id = cir.eh.inflight_exception cleanup cir.return } @@ -35,7 +38,7 @@ cir.func @inflight_exception_with_cleanup() { cir.global "private" constant external @_ZTIi : !cir.ptr cir.global "private" constant external @_ZTIPKc : !cir.ptr -cir.func @inflight_exception_with_catch_type_list() { +cir.func @inflight_exception_with_catch_type_list() personality(@__gxx_personality_v0) { %exception_ptr, %type_id = cir.eh.inflight_exception [@_ZTIi, @_ZTIPKc] cir.return }