diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td @@ -29,6 +29,15 @@ let assemblyFormat = "`<` $CallingConv `>`"; } +//===----------------------------------------------------------------------===// +// ComdatAttr +//===----------------------------------------------------------------------===// + +def ComdatAttr : LLVM_Attr<"Comdat", "comdat"> { + let parameters = (ins "comdat::Comdat":$comdat); + let assemblyFormat = "$comdat"; +} + //===----------------------------------------------------------------------===// // LinkageAttr //===----------------------------------------------------------------------===// diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrs.h b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrs.h --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrs.h +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrs.h @@ -66,6 +66,7 @@ // TODO: this shouldn't be needed after we unify the attribute generation, i.e. // --gen-attr-* and --gen-attrdef-*. using cconv::CConv; +using comdat::Comdat; using linkage::Linkage; } // namespace LLVM } // namespace mlir diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMEnums.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMEnums.td --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMEnums.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMEnums.td @@ -589,6 +589,42 @@ "::mlir::LLVM::LinkageAttr::get($_builder.getContext(), $0)"; } + +//===----------------------------------------------------------------------===// +// Comdat +//===----------------------------------------------------------------------===// + +def ComdatAny + : LLVM_EnumAttrCase<"Any", "any", "Any", 0>; +def ComdatExactMatch + : LLVM_EnumAttrCase<"ExactMatch", "exactmatch", "ExactMatch", 1>; +def ComdatLargest + : LLVM_EnumAttrCase<"Largest", "largest", "Largest", 2>; +def ComdatNoDeduplicate + : LLVM_EnumAttrCase<"NoDeduplicate", "nodeduplicate", "NoDeduplicate", 3>; +def ComdatSameSize + : LLVM_EnumAttrCase<"SameSize", "samesize", "SameSize", 4>; + +def ComdatEnum : LLVM_EnumAttr< + "Comdat", + "::llvm::Comdat::SelectionKind", + "LLVM Comdat Types", + [ComdatAny, ComdatExactMatch, ComdatLargest, + ComdatNoDeduplicate, ComdatSameSize]> { + let cppNamespace = "::mlir::LLVM::comdat"; +} + +def Comdat : DialectAttr< + LLVM_Dialect, + CPred<"$_self.isa<::mlir::LLVM::ComdatAttr>()">, + "LLVM Comdat selection kind"> { + let storageType = "::mlir::LLVM::ComdatAttr"; + let returnType = "::mlir::LLVM::Comdat"; + let convertFromStorage = "$_self.getComdat()"; + let constBuilderCall = + "::mlir::LLVM::ComdatAttr::get($_builder.getContext(), $0)"; +} + //===----------------------------------------------------------------------===// // UnnamedAddr //===----------------------------------------------------------------------===// diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td @@ -1316,6 +1316,7 @@ DefaultValuedAttr, "0">:$addr_space, OptionalAttr:$unnamed_addr, OptionalAttr:$section, + OptionalAttr:$comdat, DefaultValuedAttr:$visibility_ ); let summary = "LLVM dialect global."; @@ -1424,6 +1425,7 @@ CArg<"unsigned", "0">:$addrSpace, CArg<"bool", "false">:$dsoLocal, CArg<"bool", "false">:$thread_local_, + CArg<"std::optional", "std::nullopt">:$comdat, CArg<"ArrayRef", "{}">:$attrs)> ]; @@ -1515,6 +1517,31 @@ let hasVerifier = 1; } +def LLVM_ComdatOp : LLVM_Op<"mlir.comdat", [Symbol]> { + let arguments = (ins + SymbolNameAttr:$sym_name, + Comdat:$comdat + ); + let summary = "LLVM dialect comdat declaration"; + + let description = [{ + Provides access to object file COMDAT section/group functionality. + + Examples: + ```mlir + llvm.mlir.comdat @any_comdat any + llvm.mlir.global internal constant @has_any_comdat(1 : i64) comdat(@any_comdat) : i64 + ``` + }]; + + let assemblyFormat = "$sym_name $comdat attr-dict"; + let llvmBuilder = [{ + llvm::Module *module = moduleTranslation.getLLVMModule(); + llvm::Comdat *comdat = module->getOrInsertComdat($sym_name); + comdat->setSelectionKind(convertComdatToLLVM($comdat)); + }]; +} + def LLVM_LLVMFuncOp : LLVM_Op<"func", [ AutomaticAllocationScope, IsolatedFromAbove, FunctionOpInterface, CallableOpInterface diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp --- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp +++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp @@ -38,6 +38,7 @@ #include #include +#include using namespace mlir; using namespace mlir::LLVM; @@ -1601,6 +1602,7 @@ bool isConstant, Linkage linkage, StringRef name, Attribute value, uint64_t alignment, unsigned addrSpace, bool dsoLocal, bool threadLocal, + std::optional comdat, ArrayRef attrs) { result.addAttribute(getSymNameAttrName(result.name), builder.getStringAttr(name)); @@ -1616,6 +1618,9 @@ if (threadLocal) result.addAttribute(getThreadLocal_AttrName(result.name), builder.getUnitAttr()); + if (comdat) + result.addAttribute(getComdatAttrName(result.name), + builder.getStringAttr(*comdat)); // Only add an alignment attribute if the "alignment" input // is different from 0. The value must also be a power of two, but @@ -1652,6 +1657,9 @@ if (auto value = getValueOrNull()) p.printAttribute(value); p << ')'; + if (auto cd = getComdat()) + p << " comdat(@" << *cd << ')'; + // Note that the alignment attribute is printed using the // default syntax here, even though it is an inherent attribute // (as defined in https://mlir.llvm.org/docs/LangRef/#attributes) @@ -1660,7 +1668,7 @@ getGlobalTypeAttrName(), getConstantAttrName(), getValueAttrName(), getLinkageAttrName(), getUnnamedAddrAttrName(), getThreadLocal_AttrName(), - getVisibility_AttrName()}); + getVisibility_AttrName(), getComdatAttrName()}); // Print the trailing type unless it's a string global. if (llvm::dyn_cast_or_null(getValueOrNull())) @@ -1768,6 +1776,15 @@ return failure(); } + if (succeeded(parser.parseOptionalKeyword("comdat"))) { + StringAttr comdat; + if (parser.parseLParen() || + parser.parseSymbolName(comdat) || parser.parseRParen()) + return failure(); + + result.addAttribute(getComdatAttrName(result.name), comdat); + } + SmallVector types; if (parser.parseOptionalAttrDict(result.attributes) || parser.parseOptionalColonTypeList(types)) @@ -1850,6 +1867,12 @@ } } + if(getComdat()) { + auto *op = SymbolTable::lookupNearestSymbolFrom(*this, getComdatAttr()); + if (!llvm::isa_and_nonnull(op)) + return emitOpError() << "expected comdat symbol"; + } + std::optional alignAttr = getAlignment(); if (alignAttr.has_value()) { uint64_t value = alignAttr.value(); diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp --- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp @@ -714,6 +714,11 @@ : llvm::GlobalValue::NotThreadLocal, addrSpace); + if (auto comdat = op.getComdat()) { + auto *llcomdat = llvmModule->getOrInsertComdat(*comdat); + var->setComdat(llcomdat); + } + if (op.getUnnamedAddr().has_value()) var->setUnnamedAddr(convertUnnamedAddrToLLVM(*op.getUnnamedAddr())); diff --git a/mlir/test/Dialect/LLVMIR/comdat.mlir b/mlir/test/Dialect/LLVMIR/comdat.mlir new file mode 100644 --- /dev/null +++ b/mlir/test/Dialect/LLVMIR/comdat.mlir @@ -0,0 +1,12 @@ +// RUN: mlir-opt -split-input-file -verify-diagnostics %s | FileCheck %s + +// CHECK: llvm.mlir.comdat @any_comdat any +llvm.mlir.comdat @any_comdat any +// CHECK: llvm.mlir.comdat @exactmatch_comdat exactmatch +llvm.mlir.comdat @exactmatch_comdat exactmatch +// CHECK: llvm.mlir.comdat @largest_comdat largest +llvm.mlir.comdat @largest_comdat largest +// CHECK: llvm.mlir.comdat @nodeduplicate_comdat nodeduplicate +llvm.mlir.comdat @nodeduplicate_comdat nodeduplicate +// CHECK: llvm.mlir.comdat @samesize_comdat samesize +llvm.mlir.comdat @samesize_comdat samesize diff --git a/mlir/test/Dialect/LLVMIR/global.mlir b/mlir/test/Dialect/LLVMIR/global.mlir --- a/mlir/test/Dialect/LLVMIR/global.mlir +++ b/mlir/test/Dialect/LLVMIR/global.mlir @@ -64,6 +64,11 @@ // CHECK: llvm.mlir.global external @has_addr_space(32 : i64) {addr_space = 3 : i32} : i64 llvm.mlir.global external @has_addr_space(32 : i64) {addr_space = 3: i32} : i64 +// CHECK: llvm.mlir.comdat @any_comdat any +llvm.mlir.comdat @any_comdat any +// CHECK: llvm.mlir.global external @any() comdat(@any_comdat) {addr_space = 0 : i32} : i64 +llvm.mlir.global @any() comdat(@any_comdat) : i64 + // CHECK-LABEL: references func.func @references() { // CHECK: llvm.mlir.addressof @".string" : !llvm.ptr diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir --- a/mlir/test/Target/LLVMIR/llvmir.mlir +++ b/mlir/test/Target/LLVMIR/llvmir.mlir @@ -1,5 +1,19 @@ // RUN: mlir-translate -mlir-to-llvmir -split-input-file %s | FileCheck %s + +// Comdat sections +// CHECK: $any_comdat = comdat any +llvm.mlir.comdat @any_comdat any +// CHECK: $exactmatch_comdat = comdat exactmatch +llvm.mlir.comdat @exactmatch_comdat exactmatch +// CHECK: $largest_comdat = comdat largest +llvm.mlir.comdat @largest_comdat largest +// CHECK: $nodeduplicate_comdat = comdat nodeduplicate +llvm.mlir.comdat @nodeduplicate_comdat nodeduplicate +// CHECK: $samesize_comdat = comdat samesize +llvm.mlir.comdat @samesize_comdat samesize + + // CHECK: @global_aligned32 = private global i64 42, align 32 "llvm.mlir.global"() ({}) {sym_name = "global_aligned32", global_type = i64, value = 42 : i64, linkage = #llvm.linkage, alignment = 32} : () -> () @@ -162,6 +176,20 @@ // CHECK: @sectionvar = internal constant [10 x i8] c"teststring", section ".mysection" llvm.mlir.global internal constant @sectionvar("teststring") {section = ".mysection"}: !llvm.array<10 x i8> +// +// Comdat attribute. +// +// CHECK: @has_any_comdat = internal constant i64 1, comdat($any_comdat) +llvm.mlir.global internal constant @has_any_comdat(1 : i64) comdat(@any_comdat) : i64 +// CHECK: @has_exactmatch_comdat = internal constant i64 1, comdat($exactmatch_comdat) +llvm.mlir.global internal constant @has_exactmatch_comdat(1 : i64) comdat(@exactmatch_comdat) : i64 +// CHECK: @has_largest_comdat = internal constant i64 1, comdat($largest_comdat) +llvm.mlir.global internal constant @has_largest_comdat(1 : i64) comdat(@largest_comdat) : i64 +// CHECK: @has_nodeduplicate_comdat = internal constant i64 1, comdat($nodeduplicate_comdat) +llvm.mlir.global internal constant @has_nodeduplicate_comdat(1 : i64) comdat(@nodeduplicate_comdat) : i64 +// CHECK: @has_samesize_comdat = internal constant i64 1, comdat($samesize_comdat) +llvm.mlir.global internal constant @has_samesize_comdat(1 : i64) comdat(@samesize_comdat) : i64 + // // Declarations of the allocation functions to be linked against. These are // inserted before other functions in the module.