diff --git a/llvm/include/llvm/IR/Attributes.h b/llvm/include/llvm/IR/Attributes.h index 2b500ed58d0d6..e734466ce20e0 100644 --- a/llvm/include/llvm/IR/Attributes.h +++ b/llvm/include/llvm/IR/Attributes.h @@ -399,6 +399,10 @@ class AttributeSet { [[nodiscard]] LLVM_ABI AttributeSet addAttributes(LLVMContext &C, AttributeSet AS) const; + /// Add attributes to the attribute set. Returns a new set because attribute + /// sets are immutable. + AttributeSet addAttributes(LLVMContext &C, const AttrBuilder &B) const; + /// Remove the specified attribute from this set. Returns a new set because /// attribute sets are immutable. [[nodiscard]] LLVM_ABI AttributeSet diff --git a/llvm/include/llvm/IR/GlobalVariable.h b/llvm/include/llvm/IR/GlobalVariable.h index 388e1d7cfa808..d1d42cebc1352 100644 --- a/llvm/include/llvm/IR/GlobalVariable.h +++ b/llvm/include/llvm/IR/GlobalVariable.h @@ -219,6 +219,11 @@ class GlobalVariable : public GlobalObject, public ilist_node { Attrs = Attrs.addAttribute(getContext(), Kind, Val); } + /// Add attributes to this global. + void addAttributes(const AttrBuilder &AttrBuilder) { + Attrs = Attrs.addAttributes(getContext(), AttrBuilder); + } + /// Return true if the attribute exists. bool hasAttribute(Attribute::AttrKind Kind) const { return Attrs.hasAttribute(Kind); diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp index d1fbcb9e893a7..4ac2ebd55dcac 100644 --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -954,6 +954,19 @@ AttributeSet AttributeSet::addAttributes(LLVMContext &C, return get(C, B); } +AttributeSet AttributeSet::addAttributes(LLVMContext &C, + const AttrBuilder &B) const { + if (!hasAttributes()) + return get(C, B); + + if (!B.hasAttributes()) + return *this; + + AttrBuilder Merged(C, *this); + Merged.merge(B); + return get(C, Merged); +} + AttributeSet AttributeSet::removeAttribute(LLVMContext &C, Attribute::AttrKind Kind) const { if (!hasAttribute(Kind)) return *this; diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td index 3f27f6d9ae8b7..9d27d45d13752 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td @@ -1313,7 +1313,8 @@ def LLVM_GlobalOp : LLVM_Op<"mlir.global", OptionalAttr:$section, OptionalAttr:$comdat, OptionalAttr:$dbg_exprs, - DefaultValuedAttr:$visibility_ + DefaultValuedAttr:$visibility_, + OptionalAttr:$target_specific_attrs ); let summary = "LLVM dialect global."; let description = [{ @@ -1411,6 +1412,21 @@ def LLVM_GlobalOp : LLVM_Op<"mlir.global", llvm.mlir.global private constant @y(dense<1.0> : tensor<8xf32>) { alignment = 32 : i64 } : !llvm.array<8 x f32> ``` + The `target_specific_attrs` attribute provides a mechanism to preserve + target-specific LLVM IR attributes that are not explicitly modeled in the + LLVM dialect. + + The attribute is an array containing either string attributes or + two-element array attributes of strings. The value of a standalone string + attribute is interpreted as the name of an LLVM IR attribute on the global. + A two-element array is interpreted as a key-value pair. + + Example: + + ```mlir + llvm.mlir.global external @example() { + target_specific_attrs = ["value-less-attr", ["int-attr", "4"], ["string-attr", "string"]]} : f64 + ``` }]; let regions = (region AnyRegion:$initializer); diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp index a207cceef88b5..0f3ecec535d9a 100644 --- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp @@ -1395,6 +1395,67 @@ LogicalResult ModuleImport::convertIFunc(llvm::GlobalIFunc *ifunc) { return success(); } +/// Converts LLVM string, integer, and enum attributes into MLIR attributes, +/// skipping those in `attributesToSkip` and emitting a warning at `loc` for +/// any other unsupported attributes. +static ArrayAttr +convertLLVMAttributesToMLIR(Location loc, MLIRContext *context, + llvm::AttributeSet attributes, + ArrayRef attributesToSkip = {}) { + SmallVector mlirAttributes; + for (llvm::Attribute attr : attributes) { + StringRef attrName; + if (attr.isStringAttribute()) + attrName = attr.getKindAsString(); + else + attrName = llvm::Attribute::getNameFromAttrKind(attr.getKindAsEnum()); + if (llvm::is_contained(attributesToSkip, attrName)) + continue; + + auto keyAttr = StringAttr::get(context, attrName); + if (attr.isStringAttribute()) { + StringRef val = attr.getValueAsString(); + if (val.empty()) { + // For string attributes without values, add only the attribute name. + mlirAttributes.push_back(keyAttr); + continue; + } + // For string attributes with a value, create a [name, value] pair. + mlirAttributes.push_back( + ArrayAttr::get(context, {keyAttr, StringAttr::get(context, val)})); + continue; + } + if (attr.isIntAttribute()) { + // For integer attributes, convert the value to a string and create a + // [name, value] pair. + auto val = std::to_string(attr.getValueAsInt()); + mlirAttributes.push_back( + ArrayAttr::get(context, {keyAttr, StringAttr::get(context, val)})); + continue; + } + if (attr.isEnumAttribute()) { + // For enum attributes, add only the attribute name. + mlirAttributes.push_back(keyAttr); + continue; + } + + emitWarning(loc) + << "'" << attrName + << "' attribute is invalid on current operation, skipping it"; + } + return ArrayAttr::get(context, mlirAttributes); +} + +/// Converts LLVM attributes from `globalVar` into MLIR attributes and adds them +/// to `globalOp` as target-specific attributes. +static void processTargetSpecificAttrs(llvm::GlobalVariable *globalVar, + GlobalOp globalOp) { + ArrayAttr targetSpecificAttrs = convertLLVMAttributesToMLIR( + globalOp.getLoc(), globalOp.getContext(), globalVar->getAttributes()); + if (!targetSpecificAttrs.empty()) + globalOp.setTargetSpecificAttrsAttr(targetSpecificAttrs); +} + LogicalResult ModuleImport::convertGlobal(llvm::GlobalVariable *globalVar) { // Insert the global after the last one or at the start of the module. OpBuilder::InsertionGuard guard = setGlobalInsertionPoint(); @@ -1460,6 +1521,8 @@ LogicalResult ModuleImport::convertGlobal(llvm::GlobalVariable *globalVar) { if (globalVar->hasComdat()) globalOp.setComdatAttr(comdatMapping.lookup(globalVar->getComdat())); + processTargetSpecificAttrs(globalVar, globalOp); + return success(); } @@ -2512,7 +2575,7 @@ static void processMemoryEffects(llvm::Function *func, LLVMFuncOp funcOp) { // List of LLVM IR attributes that map to an explicit attribute on the MLIR // LLVMFuncOp. -static constexpr std::array kExplicitAttributes{ +static constexpr std::array kExplicitLLVMFuncOpAttributes{ StringLiteral("aarch64_in_za"), StringLiteral("aarch64_inout_za"), StringLiteral("aarch64_new_za"), @@ -2530,6 +2593,7 @@ static constexpr std::array kExplicitAttributes{ StringLiteral("frame-pointer"), StringLiteral("instrument-function-entry"), StringLiteral("instrument-function-exit"), + StringLiteral("memory"), StringLiteral("no-infs-fp-math"), StringLiteral("no-nans-fp-math"), StringLiteral("no-signed-zeros-fp-math"), @@ -2544,61 +2608,17 @@ static constexpr std::array kExplicitAttributes{ StringLiteral("willreturn"), }; +/// Converts LLVM attributes from `func` into MLIR attributes and adds them +/// to `funcOp` as passthrough attributes, skipping those listed in +/// `kExplicitLLVMFuncAttributes`. static void processPassthroughAttrs(llvm::Function *func, LLVMFuncOp funcOp) { - MLIRContext *context = funcOp.getContext(); - SmallVector passthroughs; llvm::AttributeSet funcAttrs = func->getAttributes().getAttributes( llvm::AttributeList::AttrIndex::FunctionIndex); - for (llvm::Attribute attr : funcAttrs) { - // Skip the memory attribute since the LLVMFuncOp has an explicit memory - // attribute. - if (attr.hasAttribute(llvm::Attribute::Memory)) - continue; - - // Skip invalid type attributes. - if (attr.isTypeAttribute()) { - emitWarning(funcOp.getLoc(), - "type attributes on a function are invalid, skipping it"); - continue; - } - - StringRef attrName; - if (attr.isStringAttribute()) - attrName = attr.getKindAsString(); - else - attrName = llvm::Attribute::getNameFromAttrKind(attr.getKindAsEnum()); - auto keyAttr = StringAttr::get(context, attrName); - - // Skip attributes that map to an explicit attribute on the LLVMFuncOp. - if (llvm::is_contained(kExplicitAttributes, attrName)) - continue; - - if (attr.isStringAttribute()) { - StringRef val = attr.getValueAsString(); - if (val.empty()) { - passthroughs.push_back(keyAttr); - continue; - } - passthroughs.push_back( - ArrayAttr::get(context, {keyAttr, StringAttr::get(context, val)})); - continue; - } - if (attr.isIntAttribute()) { - auto val = std::to_string(attr.getValueAsInt()); - passthroughs.push_back( - ArrayAttr::get(context, {keyAttr, StringAttr::get(context, val)})); - continue; - } - if (attr.isEnumAttribute()) { - passthroughs.push_back(keyAttr); - continue; - } - - llvm_unreachable("unexpected attribute kind"); - } - - if (!passthroughs.empty()) - funcOp.setPassthroughAttr(ArrayAttr::get(context, passthroughs)); + ArrayAttr passthroughAttr = + convertLLVMAttributesToMLIR(funcOp.getLoc(), funcOp.getContext(), + funcAttrs, kExplicitLLVMFuncOpAttributes); + if (!passthroughAttr.empty()) + funcOp.setPassthroughAttr(passthroughAttr); } void ModuleImport::processFunctionAttributes(llvm::Function *func, diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp index 2685b5c91426e..f660e8ac4f3ed 100644 --- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp @@ -1081,6 +1081,83 @@ static void addRuntimePreemptionSpecifier(bool dsoLocalRequested, gv->setDSOLocal(true); } +/// Attempts to translate an MLIR attribute identified by `key`, optionally with +/// the given `value`, into an LLVM IR attribute. Reports errors at `loc` if +/// any. If the attribute name corresponds to a known LLVM IR attribute kind, +/// creates the LLVM attribute of that kind; otherwise, keeps it as a string +/// attribute. Performs additional checks for attributes known to have or not +/// have a value in order to avoid assertions inside LLVM upon construction. +static FailureOr +convertMLIRAttributeToLLVM(Location loc, llvm::LLVMContext &ctx, StringRef key, + StringRef value = StringRef()) { + auto kind = llvm::Attribute::getAttrKindFromName(key); + if (kind == llvm::Attribute::None) + return llvm::Attribute::get(ctx, key, value); + + if (llvm::Attribute::isIntAttrKind(kind)) { + if (value.empty()) + return emitError(loc) << "LLVM attribute '" << key << "' expects a value"; + + int64_t result; + if (!value.getAsInteger(/*Radix=*/0, result)) + return llvm::Attribute::get(ctx, kind, result); + return llvm::Attribute::get(ctx, key, value); + } + + if (!value.empty()) + return emitError(loc) << "LLVM attribute '" << key + << "' does not expect a value, found '" << value + << "'"; + + return llvm::Attribute::get(ctx, kind); +} + +/// Converts the MLIR attributes listed in the given array attribute into LLVM +/// attributes. Returns an `AttrBuilder` containing the converted attributes. +/// Reports error to `loc` if any and returns immediately. Expects `arrayAttr` +/// to contain either string attributes, treated as value-less LLVM attributes, +/// or array attributes containing two string attributes, with the first string +/// being the name of the corresponding LLVM attribute and the second string +/// beings its value. Note that even integer attributes are expected to have +/// their values expressed as strings. +static FailureOr +convertMLIRAttributesToLLVM(Location loc, llvm::LLVMContext &ctx, + ArrayAttr arrayAttr, StringRef arrayAttrName) { + llvm::AttrBuilder attrBuilder(ctx); + if (!arrayAttr) + return attrBuilder; + + for (Attribute attr : arrayAttr) { + if (auto stringAttr = dyn_cast(attr)) { + FailureOr llvmAttr = + convertMLIRAttributeToLLVM(loc, ctx, stringAttr.getValue()); + if (failed(llvmAttr)) + return failure(); + attrBuilder.addAttribute(*llvmAttr); + continue; + } + + auto arrayAttr = dyn_cast(attr); + if (!arrayAttr || arrayAttr.size() != 2) + return emitError(loc) << "expected '" << arrayAttrName + << "' to contain string or array attributes"; + + auto keyAttr = dyn_cast(arrayAttr[0]); + auto valueAttr = dyn_cast(arrayAttr[1]); + if (!keyAttr || !valueAttr) + return emitError(loc) << "expected arrays within '" << arrayAttrName + << "' to contain two strings"; + + FailureOr llvmAttr = convertMLIRAttributeToLLVM( + loc, ctx, keyAttr.getValue(), valueAttr.getValue()); + if (failed(llvmAttr)) + return failure(); + attrBuilder.addAttribute(*llvmAttr); + } + + return attrBuilder; +} + LogicalResult ModuleTranslation::convertGlobalsAndAliases() { // Mapping from compile unit to its respective set of global variables. DenseMap> allGVars; @@ -1191,6 +1268,15 @@ LogicalResult ModuleTranslation::convertGlobalsAndAliases() { } } } + + // Forward the target-specific attributes to LLVM. + FailureOr convertedTargetSpecificAttrs = + convertMLIRAttributesToLLVM(op.getLoc(), var->getContext(), + op.getTargetSpecificAttrsAttr(), + op.getTargetSpecificAttrsAttrName()); + if (failed(convertedTargetSpecificAttrs)) + return failure(); + var->addAttributes(*convertedTargetSpecificAttrs); } // Create all llvm::GlobalAlias @@ -1381,44 +1467,6 @@ LogicalResult ModuleTranslation::convertGlobalsAndAliases() { return success(); } -/// Attempts to add an attribute identified by `key`, optionally with the given -/// `value` to LLVM function `llvmFunc`. Reports errors at `loc` if any. If the -/// attribute has a kind known to LLVM IR, create the attribute of this kind, -/// otherwise keep it as a string attribute. Performs additional checks for -/// attributes known to have or not have a value in order to avoid assertions -/// inside LLVM upon construction. -static LogicalResult checkedAddLLVMFnAttribute(Location loc, - llvm::Function *llvmFunc, - StringRef key, - StringRef value = StringRef()) { - auto kind = llvm::Attribute::getAttrKindFromName(key); - if (kind == llvm::Attribute::None) { - llvmFunc->addFnAttr(key, value); - return success(); - } - - if (llvm::Attribute::isIntAttrKind(kind)) { - if (value.empty()) - return emitError(loc) << "LLVM attribute '" << key << "' expects a value"; - - int64_t result; - if (!value.getAsInteger(/*Radix=*/0, result)) - llvmFunc->addFnAttr( - llvm::Attribute::get(llvmFunc->getContext(), kind, result)); - else - llvmFunc->addFnAttr(key, value); - return success(); - } - - if (!value.empty()) - return emitError(loc) << "LLVM attribute '" << key - << "' does not expect a value, found '" << value - << "'"; - - llvmFunc->addFnAttr(kind); - return success(); -} - /// Return a representation of `value` as metadata. static llvm::Metadata *convertIntegerToMetadata(llvm::LLVMContext &context, const llvm::APInt &value) { @@ -1454,45 +1502,6 @@ static llvm::MDNode *convertIntegerArrayToMDNode(llvm::LLVMContext &context, return llvm::MDNode::get(context, mdValues); } -/// Attaches the attributes listed in the given array attribute to `llvmFunc`. -/// Reports error to `loc` if any and returns immediately. Expects `attributes` -/// to be an array attribute containing either string attributes, treated as -/// value-less LLVM attributes, or array attributes containing two string -/// attributes, with the first string being the name of the corresponding LLVM -/// attribute and the second string beings its value. Note that even integer -/// attributes are expected to have their values expressed as strings. -static LogicalResult -forwardPassthroughAttributes(Location loc, std::optional attributes, - llvm::Function *llvmFunc) { - if (!attributes) - return success(); - - for (Attribute attr : *attributes) { - if (auto stringAttr = dyn_cast(attr)) { - if (failed( - checkedAddLLVMFnAttribute(loc, llvmFunc, stringAttr.getValue()))) - return failure(); - continue; - } - - auto arrayAttr = dyn_cast(attr); - if (!arrayAttr || arrayAttr.size() != 2) - return emitError(loc) - << "expected 'passthrough' to contain string or array attributes"; - - auto keyAttr = dyn_cast(arrayAttr[0]); - auto valueAttr = dyn_cast(arrayAttr[1]); - if (!keyAttr || !valueAttr) - return emitError(loc) - << "expected arrays within 'passthrough' to contain two strings"; - - if (failed(checkedAddLLVMFnAttribute(loc, llvmFunc, keyAttr.getValue(), - valueAttr.getValue()))) - return failure(); - } - return success(); -} - LogicalResult ModuleTranslation::convertOneFunction(LLVMFuncOp func) { // Clear the block, branch value mappings, they are only relevant within one // function. @@ -1864,9 +1873,13 @@ LogicalResult ModuleTranslation::convertFunctionSignatures() { } // Forward the pass-through attributes to LLVM. - if (failed(forwardPassthroughAttributes( - function.getLoc(), function.getPassthrough(), llvmFunc))) + FailureOr convertedPassthroughAttrs = + convertMLIRAttributesToLLVM(function.getLoc(), llvmFunc->getContext(), + function.getPassthroughAttr(), + function.getPassthroughAttrName()); + if (failed(convertedPassthroughAttrs)) return failure(); + llvmFunc->addFnAttrs(*convertedPassthroughAttrs); // Convert visibility attribute. llvmFunc->setVisibility(convertVisibilityToLLVM(function.getVisibility_())); diff --git a/mlir/test/Target/LLVMIR/Import/function-attributes.ll b/mlir/test/Target/LLVMIR/Import/function-attributes.ll index 0b13645853cea..683a0c48d7377 100644 --- a/mlir/test/Target/LLVMIR/Import/function-attributes.ll +++ b/mlir/test/Target/LLVMIR/Import/function-attributes.ll @@ -426,3 +426,8 @@ declare void @nounwind_attribute() nounwind ; CHECK-LABEL: @willreturn_attribute ; CHECK-SAME: attributes {will_return} declare void @willreturn_attribute() willreturn + +// ----- + +; expected-warning @unknown {{'preallocated' attribute is invalid on current operation, skipping it}} +declare void @test() preallocated(i32) diff --git a/mlir/test/Target/LLVMIR/Import/global-variables.ll b/mlir/test/Target/LLVMIR/Import/global-variables.ll index b8bbdbab2e2ca..102162aaae9c2 100644 --- a/mlir/test/Target/LLVMIR/Import/global-variables.ll +++ b/mlir/test/Target/LLVMIR/Import/global-variables.ll @@ -186,19 +186,16 @@ ; CHECK-SAME: {addr_space = 0 : i32, dso_local} : !llvm.array<2 x f32> @array_constant = internal constant [2 x float] [float 1., float 2.] -; CHECK: llvm.mlir.global internal constant @nested_array_constant -; CHECK-SAME-LITERAL: (dense<[[1, 2], [3, 4]]> : tensor<2x2xi32>) -; CHECK-SAME-LITERAL: {addr_space = 0 : i32, dso_local} : !llvm.array<2 x array<2 x i32>> +; CHECK{LITERAL}: llvm.mlir.global internal constant @nested_array_constant(dense<[[1, 2], [3, 4]]> : tensor<2x2xi32>) +; CHECK-SAME: {addr_space = 0 : i32, dso_local} : !llvm.array<2 x array<2 x i32>> @nested_array_constant = internal constant [2 x [2 x i32]] [[2 x i32] [i32 1, i32 2], [2 x i32] [i32 3, i32 4]] -; CHECK: llvm.mlir.global internal constant @nested_array_constant3 -; CHECK-SAME-LITERAL: (dense<[[[1, 2], [3, 4]]]> : tensor<1x2x2xi32>) -; CHECK-SAME-LITERAL: {addr_space = 0 : i32, dso_local} : !llvm.array<1 x array<2 x array<2 x i32>>> +; CHECK{LITERAL}: llvm.mlir.global internal constant @nested_array_constant3(dense<[[[1, 2], [3, 4]]]> : tensor<1x2x2xi32>) +; CHECK-SAME: {addr_space = 0 : i32, dso_local} : !llvm.array<1 x array<2 x array<2 x i32>>> @nested_array_constant3 = internal constant [1 x [2 x [2 x i32]]] [[2 x [2 x i32]] [[2 x i32] [i32 1, i32 2], [2 x i32] [i32 3, i32 4]]] -; CHECK: llvm.mlir.global internal constant @nested_array_vector -; CHECK-SAME-LITERAL: (dense<[[[1, 2], [3, 4]]]> : vector<1x2x2xi32>) -; CHECK-SAME-LITERAL: {addr_space = 0 : i32, dso_local} : !llvm.array<1 x array<2 x vector<2xi32>>> +; CHECK{LITERAL}: llvm.mlir.global internal constant @nested_array_vector(dense<[[[1, 2], [3, 4]]]> : vector<1x2x2xi32>) +; CHECK-SAME: {addr_space = 0 : i32, dso_local} : !llvm.array<1 x array<2 x vector<2xi32>>> @nested_array_vector = internal constant [1 x [2 x <2 x i32>]] [[2 x <2 x i32>] [<2 x i32> , <2 x i32> ]] ; CHECK: llvm.mlir.global internal constant @vector_constant_zero @@ -221,9 +218,8 @@ ; CHECK-SAME: {addr_space = 0 : i32, dso_local} : !llvm.array<1 x array<2 x vector<2xi32>>> @nested_array_vector_zero = internal constant [1 x [2 x <2 x i32>]] zeroinitializer -; CHECK: llvm.mlir.global internal constant @nested_bool_array_constant -; CHECK-SAME-LITERAL: (dense<[[true, false]]> : tensor<1x2xi1>) -; CHECK-SAME-LITERAL: {addr_space = 0 : i32, dso_local} : !llvm.array<1 x array<2 x i1>> +; CHECK{LITERAL}: llvm.mlir.global internal constant @nested_bool_array_constant(dense<[[true, false]]> : tensor<1x2xi1>) +; CHECK-SAME: {addr_space = 0 : i32, dso_local} : !llvm.array<1 x array<2 x i1>> @nested_bool_array_constant = internal constant [1 x [2 x i1]] [[2 x i1] [i1 1, i1 0]] ; CHECK: llvm.mlir.global internal constant @quad_float_constant @@ -358,3 +354,18 @@ declare void @"mlir.llvm.nameless_global_2"() ; CHECK: llvm.mlir.global private unnamed_addr constant @mlir.llvm.nameless_global_0("0\00") @0 = private unnamed_addr constant [2 x i8] c"0\00" + +; // ----- + +; CHECK-LABEL: llvm.mlir.global external @target_specific_attrs_only +; CHECK-SAME: target_specific_attrs = {{\[\[}}"memory", "0"], ["int-attr", "4"], "no-enum-attr", ["string-attr", "string"]]} +@target_specific_attrs_only = external global double #0 +attributes #0 = { readnone "int-attr"="4" "no-enum-attr" "string-attr"="string" } + +; // ----- + +; CHECK-LABEL: llvm.mlir.global external @target_specific_attrs_combined +; CHECK-SAME: alignment = 4 : i64, section = "mysection", +; CHECK-SAME: target_specific_attrs = ["norecurse", ["bss-section", "my_bss.1"]]} +@target_specific_attrs_combined = global i32 2, align 4, section "mysection" #0 +attributes #0 = { norecurse "bss-section"="my_bss.1" } diff --git a/mlir/test/Target/LLVMIR/llvmir-invalid.mlir b/mlir/test/Target/LLVMIR/llvmir-invalid.mlir index b09ceeeb86cc0..c263afe553750 100644 --- a/mlir/test/Target/LLVMIR/llvmir-invalid.mlir +++ b/mlir/test/Target/LLVMIR/llvmir-invalid.mlir @@ -29,6 +29,26 @@ llvm.func @passthrough_wrong_type() attributes { // ----- +// expected-error @below{{LLVM attribute 'readonly' does not expect a value}} +llvm.mlir.global external @target_specific_attrs_unexpected_value() {target_specific_attrs = [["readonly", "42"]]} : f64 + +// ----- + +// expected-error @below{{LLVM attribute 'alignstack' expects a value}} +llvm.mlir.global external @target_specific_attrs_expected_value() {target_specific_attrs = ["alignstack"]} : f64 + +// ----- + +// expected-error @below{{expected 'target_specific_attrs' to contain string or array attributes}} +llvm.mlir.global external @target_specific_attrs_wrong_type() {target_specific_attrs = [42]} : f64 + +// ----- + +// expected-error @below{{expected arrays within 'target_specific_attrs' to contain two strings}} +llvm.mlir.global external @target_specific_attrs_wrong_type() {target_specific_attrs = [[ 42, 42 ]]} : f64 + +// ----- + llvm.func @unary_float_intr_wrong_type(%arg0 : i32) -> i32 { // expected-error @below{{op operand #0 must be floating point LLVM type or LLVM dialect-compatible vector of floating point LLVM type}} %0 = "llvm.intr.exp"(%arg0) : (i32) -> i32 diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir index fc1993b50ba2d..69814f2748e1d 100644 --- a/mlir/test/Target/LLVMIR/llvmir.mlir +++ b/mlir/test/Target/LLVMIR/llvmir.mlir @@ -3028,3 +3028,15 @@ llvm.mlir.global internal constant @test_array_attr_struct_with_struct() : !llvm %0 = llvm.mlir.constant([#llvm.zero, [2 : i32, 1.0 : f32], #llvm.undef]) : !llvm.array<3 x struct<(i32, f32)>> llvm.return %0 : !llvm.array<3 x struct<(i32, f32)>> } + +// ----- + +// CHECK: @target_specific_attrs_only = external global double #[[ATTRS:[0-9]+]] +// CHECK: attributes #[[ATTRS]] = { memory(none) "int-attr"="4" "no-enum-attr" "string-attr"="string" } +llvm.mlir.global external @target_specific_attrs_only() {target_specific_attrs = [["memory", "0"], ["int-attr", "4"], "no-enum-attr", ["string-attr", "string"]]} : f64 + +// ----- + +// CHECK: @target_specific_attrs_combined = global i32 2, section "mysection", align 4 #[[ATTRS:[0-9]+]] +// CHECK: attributes #[[ATTRS]] = { norecurse "bss-section"="my_bss.1" } +llvm.mlir.global external @target_specific_attrs_combined(2 : i32) {alignment = 4 : i64, section = "mysection", target_specific_attrs = ["norecurse", ["bss-section", "my_bss.1"]]} : i32