diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td index 267389774bd5a..236e111b63a63 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td @@ -1093,8 +1093,9 @@ def LLVM_TBAATagArrayAttr //===----------------------------------------------------------------------===// // ConstantRangeAttr //===----------------------------------------------------------------------===// + def LLVM_ConstantRangeAttr : LLVM_Attr<"ConstantRange", "constant_range"> { - let parameters = (ins + let parameters = (ins "::llvm::APInt":$lower, "::llvm::APInt":$upper ); @@ -1119,9 +1120,19 @@ def LLVM_ConstantRangeAttr : LLVM_Attr<"ConstantRange", "constant_range"> { return $_get($_ctxt, ::llvm::APInt(bitWidth, lower), ::llvm::APInt(bitWidth, upper)); }]> ]; + /*let extraClassDeclaration = [{ + ::llvm::APInt getLower() const { + return getImpl()->getLower(); + } + ::llvm::APInt getUpper() const { + return getImpl()->getUpper(); + } + }];*/ + let hasCustomAssemblyFormat = 1; let genVerifyDecl = 1; + let genStorageClass = 0; } diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrs.h b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrs.h index 3ede857733242..caf2b59b9c363 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrs.h +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrs.h @@ -23,6 +23,57 @@ namespace mlir { namespace LLVM { +namespace detail { +struct ConstantRangeAttrStorage : public AttributeStorage { + ConstantRangeAttrStorage(llvm::APInt lower, llvm::APInt upper) + : lower(lower), upper(upper) {} + + /// The hash key for this storage is a pair of the integer and type params. + using KeyTy = std::pair; + + /// Define the comparison function for the key type. + bool operator==(const KeyTy &key) const { + if (lower.getBitWidth() != key.first.getBitWidth() || + upper.getBitWidth() != key.second.getBitWidth()) { + return false; + } + return lower == key.first && upper == key.second; + } + + /// Define a hash function for the key type. + /// Note: This isn't necessary because std::pair, unsigned, and Type all have + /// hash functions already available. + static llvm::hash_code hashKey(const KeyTy &key) { + return llvm::hash_combine(key.first, key.second); + } + + /// Define a construction function for the key type. + /// Note: This isn't necessary because KeyTy can be directly constructed with + /// the given parameters. + static KeyTy getKey(llvm::APInt lower, llvm::APInt upper) { + return KeyTy(lower, upper); + } + + /// Define a construction method for creating a new instance of this storage. + static ConstantRangeAttrStorage *construct(mlir::StorageUniquer::StorageAllocator &allocator, const KeyTy &key) { + return new (allocator.allocate()) + ConstantRangeAttrStorage(key.first, key.second); + } + + /// Construct an instance of the key from this storage class. + KeyTy getAsKey() const { + return KeyTy(lower, upper); + } + + llvm::APInt getLower() const { return lower; } + llvm::APInt getUpper() const { return upper; } + + /// The parametric data held by the storage class. + llvm::APInt lower; + llvm::APInt upper; +}; +} + /// This class represents the base attribute for all debug info attributes. class DINodeAttr : public Attribute { public: @@ -96,6 +147,9 @@ using linkage::Linkage; #include "mlir/Dialect/LLVMIR/LLVMAttrInterfaces.h.inc" + + + #define GET_ATTRDEF_CLASSES #include "mlir/Dialect/LLVMIR/LLVMOpsAttrDefs.h.inc" diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td index 24889b60afd5b..551a05d1dd442 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td @@ -1164,12 +1164,12 @@ def LLVM_AddressOfOp : LLVM_Op<"mlir.addressof", let arguments = (ins FlatSymbolRefAttr:$global_name); let results = (outs LLVM_AnyPointer:$res); - let summary = "Creates a pointer pointing to a global or a function"; + let summary = "Creates a pointer pointing to a global, alias or a function"; let description = [{ - Creates an SSA value containing a pointer to a global variable or constant - defined by `llvm.mlir.global`. The global value can be defined after its - first referenced. If the global value is a constant, storing into it is not + Creates an SSA value containing a pointer to a global value (function, + variable or alias). The global value can be defined after its first + referenced. If the global value is a constant, storing into it is not allowed. Examples: @@ -1187,10 +1187,19 @@ def LLVM_AddressOfOp : LLVM_Op<"mlir.addressof", // The function address can be used for indirect calls. llvm.call %2() : !llvm.ptr, () -> () + + // Get the address of an aliased global. + %3 = llvm.mlir.addressof @const_alias : !llvm.ptr } // Define the global. llvm.mlir.global @const(42 : i32) : i32 + + // Define an alias. + llvm.mlir.alias @const_alias : i32 { + %0 = llvm.mlir.addressof @const : !llvm.ptr + llvm.return %0 : !llvm.ptr + } ``` }]; @@ -1209,6 +1218,14 @@ def LLVM_AddressOfOp : LLVM_Op<"mlir.addressof", build($_builder, $_state, LLVM::LLVMPointerType::get($_builder.getContext()), func.getName()); $_state.addAttributes(attrs); + }]>, + OpBuilder<(ins "AliasOp":$alias, + CArg<"ArrayRef", "{}">:$attrs), + [{ + build($_builder, $_state, + LLVM::LLVMPointerType::get($_builder.getContext(), alias.getAddrSpace()), + alias.getSymName()); + $_state.addAttributes(attrs); }]> ]; @@ -1220,7 +1237,11 @@ def LLVM_AddressOfOp : LLVM_Op<"mlir.addressof", /// Return the llvm.func operation that is referenced here. LLVMFuncOp getFunction(SymbolTableCollection &symbolTable); + /// Return the llvm.mlir.alias operation that defined the value referenced + /// here. + AliasOp getAlias(SymbolTableCollection &symbolTable); }]; + let assemblyFormat = "$global_name attr-dict `:` qualified(type($res))"; let hasFolder = 1; @@ -1450,6 +1471,85 @@ def LLVM_GlobalDtorsOp : LLVM_Op<"mlir.global_dtors", [ let hasVerifier = 1; } +def LLVM_AliasOp : LLVM_Op<"mlir.alias", + [IsolatedFromAbove, SingleBlockImplicitTerminator<"ReturnOp">, Symbol]> { + let arguments = (ins + TypeAttr:$alias_type, + StrAttr:$sym_name, + Linkage:$linkage, + UnitAttr:$dso_local, + UnitAttr:$thread_local_, + OptionalAttr:$unnamed_addr, + DefaultValuedAttr:$visibility_ + ); + let summary = "LLVM dialect alias."; + let description = [{ + `llvm.mlir.alias` is a top level operation that defines a global alias for + global variables and functions. The operation is always initialized by + using a initializer region which could be a direct map to another global + value or contain some address computation on top of it. + + It uses a symbol for its value, which will be uniqued by the module + with respect to other symbols in it. + + Similarly to functions and globals, they can also have a linkage attribute. + This attribute is placed between `llvm.mlir.alias` and the symbol name. If + the attribute is omitted, `external` linkage is assumed by default. + + Examples: + + ```mlir + // Global alias use @-identifiers. + llvm.mlir.alias external @foo_alias {addr_space = 0 : i32} : !llvm.ptr { + %0 = llvm.mlir.addressof @some_function : !llvm.ptr + llvm.return %0 : !llvm.ptr + } + + // More complex initialization. + llvm.mlir.alias linkonce_odr hidden @glob + {addr_space = 0 : i32, dso_local} : !llvm.array<32 x i32> { + %0 = llvm.mlir.constant(1234 : i64) : i64 + %1 = llvm.mlir.addressof @glob.private : !llvm.ptr + %2 = llvm.ptrtoint %1 : !llvm.ptr to i64 + %3 = llvm.add %2, %0 : i64 + %4 = llvm.inttoptr %3 : i64 to !llvm.ptr + llvm.return %4 : !llvm.ptr + } + ``` + }]; + let regions = (region SizedRegion<1>:$initializer); + + let builders = [ + OpBuilder<(ins "Type":$type, "Linkage":$linkage, + "StringRef":$name, + CArg<"bool", "false">:$dsoLocal, + CArg<"bool", "false">:$thread_local_, + CArg<"ArrayRef", "{}">:$attrs)> + ]; + + let extraClassDeclaration = [{ + /// Return the LLVM type of the global alias. + Type getType() { + return getAliasType(); + } + /// Return the initializer region. It's always present and terminates + /// with an `llvm.return` op with the initializer value. + Region &getInitializerRegion() { + return getOperation()->getRegion(0); + } + Block &getInitializerBlock() { + return getInitializerRegion().front(); + } + // Retrieve address space information from the initializer block + // result. + unsigned getAddrSpace(); + }]; + + let hasCustomAssemblyFormat = 1; + let hasVerifier = 1; + let hasRegionVerifier = 1; +} + def LLVM_ComdatSelectorOp : LLVM_Op<"comdat_selector", [Symbol]> { let arguments = (ins SymbolNameAttr:$sym_name, diff --git a/mlir/include/mlir/Target/LLVMIR/ModuleImport.h b/mlir/include/mlir/Target/LLVMIR/ModuleImport.h index 33c9af7c6335a..24f97ccf62660 100644 --- a/mlir/include/mlir/Target/LLVMIR/ModuleImport.h +++ b/mlir/include/mlir/Target/LLVMIR/ModuleImport.h @@ -67,6 +67,9 @@ class ModuleImport { /// Converts all global variables of the LLVM module to MLIR global variables. LogicalResult convertGlobals(); + /// Converts all aliases of the LLVM module to MLIR variables. + LogicalResult convertAliases(); + /// Converts the data layout of the LLVM module to an MLIR data layout /// specification. LogicalResult convertDataLayout(); @@ -284,6 +287,9 @@ class ModuleImport { LogicalResult convertGlobal(llvm::GlobalVariable *globalVar); /// Imports the magic globals "global_ctors" and "global_dtors". LogicalResult convertGlobalCtorsAndDtors(llvm::GlobalVariable *globalVar); + /// Converts an LLVM global alias variable into an MLIR LLVM dialect alias + /// operation if a conversion exists. Otherwise, returns failure. + LogicalResult convertAlias(llvm::GlobalAlias *alias); /// Returns personality of `func` as a FlatSymbolRefAttr. FlatSymbolRefAttr getPersonalityAsAttr(llvm::Function *func); /// Imports `bb` into `block`, which must be initially empty. @@ -393,6 +399,8 @@ class ModuleImport { Operation *constantInsertionOp = nullptr; /// Operation to insert the next global after. Operation *globalInsertionOp = nullptr; + /// Operation to insert the next alias after. + Operation *aliasInsertionOp = nullptr; /// Operation to insert comdat selector operations into. ComdatOp globalComdatOp = nullptr; /// The current context. diff --git a/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h b/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h index 1b62437761ed9..ec6182fb34413 100644 --- a/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h +++ b/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h @@ -188,6 +188,12 @@ class ModuleTranslation { return globalsMapping.lookup(op); } + /// Finds an LLVM IR global value that corresponds to the given MLIR operation + /// defining a global alias value. + llvm::GlobalValue *lookupAlias(Operation *op) { + return aliasesMapping.lookup(op); + } + /// Returns the OpenMP IR builder associated with the LLVM IR module being /// constructed. llvm::OpenMPIRBuilder *getOpenMPBuilder(); @@ -321,7 +327,13 @@ class ModuleTranslation { LogicalResult convertFunctionSignatures(); LogicalResult convertFunctions(); LogicalResult convertComdats(); - LogicalResult convertGlobals(); + + /// Handle conversion for both globals and global aliases. + /// + /// - Create named global variables that correspond to llvm.mlir.global + /// definitions, similarly Convert llvm.global_ctors and global_dtors ops. + /// - Create global alias that correspond to llvm.mlir.alias. + LogicalResult convertGlobalsAndAliases(); LogicalResult convertOneFunction(LLVMFuncOp func); LogicalResult convertBlockImpl(Block &bb, bool ignoreArguments, llvm::IRBuilderBase &builder, @@ -366,6 +378,10 @@ class ModuleTranslation { /// Mappings between llvm.mlir.global definitions and corresponding globals. DenseMap globalsMapping; + /// Mappings between llvm.mlir.alias definitions and corresponding global + /// aliases. + DenseMap aliasesMapping; + /// A stateful object used to translate types. TypeToLLVMIRTranslator typeTranslator; diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp index e4f9d6f987401..495a858d24862 100644 --- a/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp +++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp @@ -257,6 +257,14 @@ DIRecursiveTypeAttrInterface DISubprogramAttr::getRecSelf(DistinctAttr recId) { // ConstantRangeAttr //===----------------------------------------------------------------------===// +llvm::APInt ConstantRangeAttr::getLower() const { + return getImpl()->getLower(); +} + +llvm::APInt ConstantRangeAttr::getUpper() const { + return getImpl()->getUpper(); +} + Attribute ConstantRangeAttr::parse(AsmParser &parser, Type odsType) { llvm::SMLoc loc = parser.getCurrentLocation(); IntegerType widthType; diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp index acd319ee239b0..36f2541cbcff8 100644 --- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp +++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp @@ -2038,6 +2038,11 @@ LLVMFuncOp AddressOfOp::getFunction(SymbolTableCollection &symbolTable) { symbolTable.lookupSymbolIn(parentLLVMModule(*this), getGlobalNameAttr())); } +AliasOp AddressOfOp::getAlias(SymbolTableCollection &symbolTable) { + return dyn_cast_or_null( + symbolTable.lookupSymbolIn(parentLLVMModule(*this), getGlobalNameAttr())); +} + LogicalResult AddressOfOp::verifySymbolUses(SymbolTableCollection &symbolTable) { Operation *symbol = @@ -2045,15 +2050,17 @@ AddressOfOp::verifySymbolUses(SymbolTableCollection &symbolTable) { auto global = dyn_cast_or_null(symbol); auto function = dyn_cast_or_null(symbol); + auto alias = dyn_cast_or_null(symbol); - if (!global && !function) - return emitOpError( - "must reference a global defined by 'llvm.mlir.global' or 'llvm.func'"); + if (!global && !function && !alias) + return emitOpError("must reference a global defined by 'llvm.mlir.global', " + "'llvm.mlir.alias' or 'llvm.func'"); LLVMPointerType type = getType(); - if (global && global.getAddrSpace() != type.getAddressSpace()) + if ((global && global.getAddrSpace() != type.getAddressSpace()) || + (alias && alias.getAddrSpace() != type.getAddressSpace())) return emitOpError("pointer address space must match address space of the " - "referenced global"); + "referenced global or alias"); return success(); } @@ -2194,42 +2201,58 @@ static LogicalResult verifyComdat(Operation *op, return success(); } -// operation ::= `llvm.mlir.global` linkage? visibility? -// (`unnamed_addr` | `local_unnamed_addr`)? -// `thread_local`? `constant`? `@` identifier -// `(` attribute? `)` (`comdat(` symbol-ref-id `)`)? -// attribute-list? (`:` type)? region? -// -// The type can be omitted for string attributes, in which case it will be -// inferred from the value of the string as [strlen(value) x i8]. -ParseResult GlobalOp::parse(OpAsmParser &parser, OperationState &result) { +/// Parse common attributes that might show up in the same order in both +/// GlobalOp and AliasOp. +template +static ParseResult parseCommonGlobalAndAlias(OpAsmParser &parser, + OperationState &result) { MLIRContext *ctx = parser.getContext(); // Parse optional linkage, default to External. - result.addAttribute(getLinkageAttrName(result.name), + result.addAttribute(OpType::getLinkageAttrName(result.name), LLVM::LinkageAttr::get( ctx, parseOptionalLLVMKeyword( parser, result, LLVM::Linkage::External))); // Parse optional visibility, default to Default. - result.addAttribute(getVisibility_AttrName(result.name), + result.addAttribute(OpType::getVisibility_AttrName(result.name), parser.getBuilder().getI64IntegerAttr( parseOptionalLLVMKeyword( parser, result, LLVM::Visibility::Default))); // Parse optional UnnamedAddr, default to None. - result.addAttribute(getUnnamedAddrAttrName(result.name), + result.addAttribute(OpType::getUnnamedAddrAttrName(result.name), parser.getBuilder().getI64IntegerAttr( parseOptionalLLVMKeyword( parser, result, LLVM::UnnamedAddr::None))); if (succeeded(parser.parseOptionalKeyword("thread_local"))) - result.addAttribute(getThreadLocal_AttrName(result.name), + result.addAttribute(OpType::getThreadLocal_AttrName(result.name), parser.getBuilder().getUnitAttr()); + return success(); +} + +// operation ::= `llvm.mlir.global` linkage? visibility? +// (`unnamed_addr` | `local_unnamed_addr`)? +// `thread_local`? `constant`? `@` identifier +// `(` attribute? `)` (`comdat(` symbol-ref-id `)`)? +// attribute-list? (`:` type)? region? +// +// The type can be omitted for string attributes, in which case it will be +// inferred from the value of the string as [strlen(value) x i8]. +ParseResult GlobalOp::parse(OpAsmParser &parser, OperationState &result) { + // Call into common parsing between GlobalOp and AliasOp. + if (parseCommonGlobalAndAlias(parser, result).failed()) + return failure(); + if (succeeded(parser.parseOptionalKeyword("constant"))) result.addAttribute(getConstantAttrName(result.name), parser.getBuilder().getUnitAttr()); + if (succeeded(parser.parseOptionalKeyword("local_unnamed_addr"))) { + // TODO: do something real here + } + StringAttr name; if (parser.parseSymbolName(name, getSymNameAttrName(result.name), result.attributes) || @@ -2430,6 +2453,145 @@ LogicalResult GlobalDtorsOp::verify() { return success(); } +//===----------------------------------------------------------------------===// +// Builder, printer and verifier for LLVM::AliasOp. +//===----------------------------------------------------------------------===// + +void AliasOp::build(OpBuilder &builder, OperationState &result, Type type, + Linkage linkage, StringRef name, bool dsoLocal, + bool threadLocal, ArrayRef attrs) { + result.addAttribute(getSymNameAttrName(result.name), + builder.getStringAttr(name)); + result.addAttribute(getAliasTypeAttrName(result.name), TypeAttr::get(type)); + if (dsoLocal) + result.addAttribute(getDsoLocalAttrName(result.name), + builder.getUnitAttr()); + if (threadLocal) + result.addAttribute(getThreadLocal_AttrName(result.name), + builder.getUnitAttr()); + + result.addAttribute(getLinkageAttrName(result.name), + LinkageAttr::get(builder.getContext(), linkage)); + result.attributes.append(attrs.begin(), attrs.end()); + + result.addRegion(); +} + +void AliasOp::print(OpAsmPrinter &p) { + p << ' ' << stringifyLinkage(getLinkage()) << ' '; + StringRef visibility = stringifyVisibility(getVisibility_()); + if (!visibility.empty()) + p << visibility << ' '; + + if (std::optional unnamedAddr = getUnnamedAddr()) { + StringRef str = stringifyUnnamedAddr(*unnamedAddr); + if (!str.empty()) + p << str << ' '; + } + + if (getThreadLocal_()) + p << "thread_local "; + + p.printSymbolName(getSymName()); + p.printOptionalAttrDict((*this)->getAttrs(), + {SymbolTable::getSymbolAttrName(), + getAliasTypeAttrName(), getLinkageAttrName(), + getUnnamedAddrAttrName(), getThreadLocal_AttrName(), + getVisibility_AttrName(), getUnnamedAddrAttrName()}); + + // Print the trailing type. + p << " : " << getType() << ' '; + // Print the initializer region. + p.printRegion(getInitializerRegion(), /*printEntryBlockArgs=*/false); +} + +// operation ::= `llvm.mlir.alias` linkage? visibility? +// (`unnamed_addr` | `local_unnamed_addr`)? +// `thread_local`? `@` identifier +// `(` attribute? `)` +// attribute-list? `:` type region +// +ParseResult AliasOp::parse(OpAsmParser &parser, OperationState &result) { + // Call into common parsing between GlobalOp and AliasOp. + if (parseCommonGlobalAndAlias(parser, result).failed()) + return failure(); + + StringAttr name; + if (parser.parseSymbolName(name, getSymNameAttrName(result.name), + result.attributes)) + return failure(); + + SmallVector types; + if (parser.parseOptionalAttrDict(result.attributes) || + parser.parseOptionalColonTypeList(types)) + return failure(); + + if (types.size() > 1) + return parser.emitError(parser.getNameLoc(), "expected zero or one type"); + + Region &initRegion = *result.addRegion(); + if (parser.parseRegion(initRegion).failed()) + return failure(); + + result.addAttribute(getAliasTypeAttrName(result.name), + TypeAttr::get(types[0])); + return success(); +} + +LogicalResult AliasOp::verify() { + bool validType = isCompatibleOuterType(getType()) + ? !llvm::isa(getType()) + : llvm::isa(getType()); + if (!validType) + return emitOpError( + "expects type to be a valid element type for an LLVM global alias"); + + // This matches LLVM IR verification logic, see llvm/lib/IR/Verifier.cpp + switch (getLinkage()) { + case Linkage::External: + case Linkage::Internal: + case Linkage::Private: + case Linkage::Weak: + case Linkage::Linkonce: + case Linkage::LinkonceODR: + case Linkage::AvailableExternally: + break; + default: + return emitOpError() + << "'" << stringifyLinkage(getLinkage()) + << "' linkage not supported in aliases, available options: private, " + "internal, linkonce, weak, linkonce_odr, weak_odr, external or " + "available_externally"; + } + + return success(); +} + +LogicalResult AliasOp::verifyRegions() { + Block &b = getInitializerBlock(); + auto ret = cast(b.getTerminator()); + if (ret.getNumOperands() == 0 || + !isa(ret.getOperand(0).getType())) + return emitOpError("initializer region must always return a pointer"); + + for (Operation &op : b) { + auto iface = dyn_cast(op); + if (!iface || !iface.hasNoEffect()) + return op.emitError() + << "ops with side effects are not allowed in alias initializers"; + } + + return success(); +} + +unsigned AliasOp::getAddrSpace() { + Block &initializer = getInitializerBlock(); + auto ret = cast(initializer.getTerminator()); + auto ptrTy = cast(ret.getOperand(0).getType()); + return ptrTy.getAddressSpace(); +} + //===----------------------------------------------------------------------===// // ShuffleVectorOp //===----------------------------------------------------------------------===// diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMTypeSyntax.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMTypeSyntax.cpp index d700dc52d42d2..f9f908ec3ff86 100644 --- a/mlir/lib/Dialect/LLVMIR/IR/LLVMTypeSyntax.cpp +++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMTypeSyntax.cpp @@ -157,7 +157,7 @@ static Type parseVectorType(AsmParser &parser) { /// error at `subtypesLoc` in case of failure. static LLVMStructType trySetStructBody(LLVMStructType type, ArrayRef subtypes, bool isPacked, - AsmParser &parser, SMLoc subtypesLoc) { + AsmParser &parser, SMLoc subtypesLoc, Location loc) { for (Type t : subtypes) { if (!LLVMStructType::isValidElementType(t)) { parser.emitError(subtypesLoc) @@ -169,6 +169,16 @@ static LLVMStructType trySetStructBody(LLVMStructType type, if (succeeded(type.setBody(subtypes, isPacked))) return type; + + long l = random(); + std::string newid = type.getName().str() + "." +std::to_string(l); + auto nty = LLVMStructType::getIdentifiedChecked( + [loc] { return emitError(loc); }, loc.getContext(), newid); + if (succeeded(nty.setBody(subtypes, isPacked))) + return nty; + + + parser.emitError(subtypesLoc) << "identified type already used with a different body"; return LLVMStructType(); @@ -249,9 +259,12 @@ Type LLVMStructType::parse(AsmParser &parser) { if (!isIdentified) return LLVMStructType::getLiteralChecked([loc] { return emitError(loc); }, loc.getContext(), {}, isPacked); + + + auto type = LLVMStructType::getIdentifiedChecked( [loc] { return emitError(loc); }, loc.getContext(), name); - return trySetStructBody(type, {}, isPacked, parser, kwLoc); + return trySetStructBody(type, {}, isPacked, parser, kwLoc, loc); } // Parse subtypes. For identified structs, put the identifier of the struct on @@ -274,7 +287,7 @@ Type LLVMStructType::parse(AsmParser &parser) { [loc] { return emitError(loc); }, loc.getContext(), subtypes, isPacked); auto type = LLVMStructType::getIdentifiedChecked( [loc] { return emitError(loc); }, loc.getContext(), name); - return trySetStructBody(type, subtypes, isPacked, parser, subtypesLoc); + return trySetStructBody(type, subtypes, isPacked, parser, subtypesLoc, loc); } /// Parses a type appearing inside another LLVM dialect-compatible type. This diff --git a/mlir/lib/IR/CMakeLists.txt b/mlir/lib/IR/CMakeLists.txt index 5c1f3dbb84355..118fa316a653f 100644 --- a/mlir/lib/IR/CMakeLists.txt +++ b/mlir/lib/IR/CMakeLists.txt @@ -66,6 +66,7 @@ add_mlir_library(MLIRIR MLIRSideEffectInterfacesIncGen MLIRSymbolInterfacesIncGen MLIRTensorEncodingIncGen + MLIRLLVMInterfacesIncGen LINK_LIBS PUBLIC MLIRSupport diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp index 2084e527773ca..3afea87ca92c1 100644 --- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp @@ -447,15 +447,21 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder, addressOfOp.getGlobal(moduleTranslation.symbolTable()); LLVM::LLVMFuncOp function = addressOfOp.getFunction(moduleTranslation.symbolTable()); + LLVM::AliasOp alias = addressOfOp.getAlias(moduleTranslation.symbolTable()); // The verifier should not have allowed this. - assert((global || function) && - "referencing an undefined global or function"); - - moduleTranslation.mapValue( - addressOfOp.getResult(), - global ? moduleTranslation.lookupGlobal(global) - : moduleTranslation.lookupFunction(function.getName())); + assert((global || function || alias) && + "referencing an undefined global, function, or alias"); + + llvm::Value *llvmValue = nullptr; + if (global) + llvmValue = moduleTranslation.lookupGlobal(global); + else if (alias) + llvmValue = moduleTranslation.lookupAlias(alias); + else + llvmValue = moduleTranslation.lookupFunction(function.getName()); + + moduleTranslation.mapValue(addressOfOp.getResult(), llvmValue); return success(); } diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp index f6826a2362bfd..36b544c56738e 100644 --- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp @@ -647,6 +647,16 @@ LogicalResult ModuleImport::convertGlobals() { return success(); } +LogicalResult ModuleImport::convertAliases() { + for (llvm::GlobalAlias &alias : llvmModule->aliases()) { + if (failed(convertAlias(&alias))) { + return emitError(UnknownLoc::get(context)) + << "unhandled global alias: " << diag(alias); + } + } + return success(); +} + LogicalResult ModuleImport::convertDataLayout() { Location loc = mlirModule.getLoc(); DataLayoutImporter dataLayoutImporter(context, llvmModule->getDataLayout()); @@ -947,6 +957,38 @@ ModuleImport::getOrCreateNamelessSymbolName(llvm::GlobalVariable *globalVar) { return symbolRef; } +LogicalResult ModuleImport::convertAlias(llvm::GlobalAlias *alias) { + // Insert the global after the last one or at the start of the module. + OpBuilder::InsertionGuard guard(builder); + if (!aliasInsertionOp) + builder.setInsertionPointToStart(mlirModule.getBody()); + else + builder.setInsertionPointAfter(aliasInsertionOp); + + Type type = convertType(alias->getValueType()); + AliasOp aliasOp = builder.create( + mlirModule.getLoc(), type, convertLinkageFromLLVM(alias->getLinkage()), + alias->getName(), + /*dso_local=*/alias->isDSOLocal(), + /*thread_local=*/alias->isThreadLocal(), + /*attrs=*/ArrayRef()); + aliasInsertionOp = aliasOp; + + clearRegionState(); + Block *block = builder.createBlock(&aliasOp.getInitializerRegion()); + setConstantInsertionPointToStart(block); + FailureOr initializer = convertConstantExpr(alias->getAliasee()); + if (failed(initializer)) + return failure(); + builder.create(aliasOp.getLoc(), *initializer); + + if (alias->hasAtLeastLocalUnnamedAddr()) + aliasOp.setUnnamedAddr(convertUnnamedAddrFromLLVM(alias->getUnnamedAddr())); + aliasOp.setVisibility_(convertVisibilityFromLLVM(alias->getVisibility())); + + return success(); +} + LogicalResult ModuleImport::convertGlobal(llvm::GlobalVariable *globalVar) { // Insert the global after the last one or at the start of the module. OpBuilder::InsertionGuard guard(builder); @@ -1043,7 +1085,8 @@ ModuleImport::convertGlobalCtorsAndDtors(llvm::GlobalVariable *globalVar) { // GlobalCtorsOps and GlobalDtorsOps do not support non-null data fields. if (!data->isNullValue()) - return failure(); + emitError(mlirModule.getLoc()) << "GlobalCtorsOps and GlobalDtorsOps do not support non-null data fields we are just ignoring it and going to continue"; + //return failure(); funcs.push_back(FlatSymbolRefAttr::get(context, func->getName())); priorities.push_back(priority->getValue().getZExtValue()); @@ -1084,7 +1127,7 @@ ModuleImport::getConstantsToConvert(llvm::Constant *constant) { llvm::Constant *current = workList.back(); // References of global objects are just pointers to the object. Avoid // walking the elements of these here. - if (isa(current)) { + if (isa(current) || isa(current)) { orderedSet.insert(current); workList.pop_back(); continue; @@ -1180,6 +1223,14 @@ FailureOr ModuleImport::convertConstant(llvm::Constant *constant) { return builder.create(loc, type, symbolRef).getResult(); } + // Convert global alias accesses. + if (auto *globalAliasObj = dyn_cast(constant)) { + Type type = convertType(globalAliasObj->getType()); + StringRef aliaseeName = globalAliasObj->getName(); + FlatSymbolRefAttr symbolRef = FlatSymbolRefAttr::get(context, aliaseeName); + return builder.create(loc, type, symbolRef).getResult(); + } + // Convert constant expressions. if (auto *constExpr = dyn_cast(constant)) { // Convert the constant expression to a temporary LLVM instruction and @@ -1517,7 +1568,13 @@ LogicalResult ModuleImport::convertIntrinsic(llvm::CallInst *inst) { return success(); Location loc = translateLoc(inst->getDebugLoc()); - return emitError(loc) << "unhandled intrinsic: " << diag(*inst); + + emitError(loc) << "unhandled intrinsic: " << diag(*inst); + + auto rty = inst->getType(); + auto result = builder.create(loc, convertType(rty)); + mapValue(inst, result); + return success(); } LogicalResult ModuleImport::convertInstruction(llvm::Instruction *inst) { @@ -2356,6 +2413,8 @@ mlir::translateLLVMIRToModule(std::unique_ptr llvmModule, return {}; if (failed(moduleImport.convertFunctions())) return {}; - + if (failed(moduleImport.convertAliases())) + return {}; + return module; } diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp index 4367100e3aca6..ed61cb255be8f 100644 --- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp @@ -746,6 +746,8 @@ void ModuleTranslation::forgetMapping(Region ®ion) { branchMapping.erase(&op); if (isa(op)) globalsMapping.erase(&op); + if (isa(op)) + aliasesMapping.erase(&op); if (isa(op)) callMapping.erase(&op); llvm::append_range( @@ -1026,12 +1028,15 @@ static void addRuntimePreemptionSpecifier(bool dsoLocalRequested, gv->setDSOLocal(true); } -/// Create named global variables that correspond to llvm.mlir.global -/// definitions. Convert llvm.global_ctors and global_dtors ops. -LogicalResult ModuleTranslation::convertGlobals() { +LogicalResult ModuleTranslation::convertGlobalsAndAliases() { // Mapping from compile unit to its respective set of global variables. DenseMap> allGVars; + // First, create all global variables and global aliases in LLVM IR. A global + // or alias body may refer to another global/alias or itself, so all the + // mapping needs to happen prior to body conversion. + + // Create all llvm::GlobalVariable for (auto op : getModuleBody(mlirModule).getOps()) { llvm::Type *type = convertType(op.getType()); llvm::Constant *cst = nullptr; @@ -1135,9 +1140,35 @@ LogicalResult ModuleTranslation::convertGlobals() { } } - // Convert global variable bodies. This is done after all global variables - // have been created in LLVM IR because a global body may refer to another - // global or itself. So all global variables need to be mapped first. + // Create all llvm::GlobalAlias + for (auto op : getModuleBody(mlirModule).getOps()) { + llvm::Type *type = convertType(op.getType()); + llvm::Constant *cst = nullptr; + llvm::GlobalValue::LinkageTypes linkage = + convertLinkageToLLVM(op.getLinkage()); + llvm::Module &llvmMod = *llvmModule; + + // Note address space and aliasee info isn't set just yet. + llvm::GlobalAlias *var = llvm::GlobalAlias::create( + type, op.getAddrSpace(), linkage, op.getSymName(), /*placeholder*/ cst, + &llvmMod); + + var->setThreadLocalMode(op.getThreadLocal_() + ? llvm::GlobalAlias::GeneralDynamicTLSModel + : llvm::GlobalAlias::NotThreadLocal); + + // Note there is no need to setup the comdat because GlobalAlias calls into + // the aliasee comdat information automatically. + + if (op.getUnnamedAddr().has_value()) + var->setUnnamedAddr(convertUnnamedAddrToLLVM(*op.getUnnamedAddr())); + + var->setVisibility(convertVisibilityToLLVM(op.getVisibility_())); + + aliasesMapping.try_emplace(op, var); + } + + // Convert global variable bodies. for (auto op : getModuleBody(mlirModule).getOps()) { if (Block *initializer = op.getInitializerBlock()) { llvm::IRBuilder<> builder(llvmModule->getContext()); @@ -1248,6 +1279,29 @@ LogicalResult ModuleTranslation::convertGlobals() { llvm::MDTuple::get(getLLVMContext(), globals)); } + // Convert global alias bodies. + for (auto op : getModuleBody(mlirModule).getOps()) { + Block &initializer = op.getInitializerBlock(); + llvm::IRBuilder<> builder(llvmModule->getContext()); + + for (mlir::Operation &op : initializer.without_terminator()) { + if (failed(convertOperation(op, builder))) + return emitError(op.getLoc(), "fail to convert alias initializer"); + if (!isa(lookupValue(op.getResult(0)))) + return emitError(op.getLoc(), "unemittable constant value"); + } + + auto ret = cast(initializer.getTerminator()); + auto *cst = cast(lookupValue(ret.getOperand(0))); + assert(aliasesMapping.count(op)); + auto *alias = cast(aliasesMapping[op]); + alias->setAliasee(cst); + } + + for (auto op : getModuleBody(mlirModule).getOps()) + if (failed(convertDialectAttributes(op, {}))) + return failure(); + return success(); } @@ -2055,7 +2109,7 @@ mlir::translateModuleToLLVMIR(Operation *module, llvm::LLVMContext &llvmContext, return nullptr; if (failed(translator.convertFunctionSignatures())) return nullptr; - if (failed(translator.convertGlobals())) + if (failed(translator.convertGlobalsAndAliases())) return nullptr; if (failed(translator.createTBAAMetadata())) return nullptr; @@ -2066,8 +2120,8 @@ mlir::translateModuleToLLVMIR(Operation *module, llvm::LLVMContext &llvmContext, // Convert other top-level operations if possible. for (Operation &o : getModuleBody(module).getOperations()) { - if (!isa(&o) && + if (!isa(&o) && !o.hasTrait() && failed(translator.convertOperation(o, llvmBuilder))) { return nullptr; diff --git a/mlir/test/Dialect/LLVMIR/alias.mlir b/mlir/test/Dialect/LLVMIR/alias.mlir new file mode 100644 index 0000000000000..807843a27e6fa --- /dev/null +++ b/mlir/test/Dialect/LLVMIR/alias.mlir @@ -0,0 +1,141 @@ +// RUN: mlir-opt %s -split-input-file --verify-roundtrip | FileCheck %s + +llvm.func internal @callee() -> !llvm.ptr attributes {dso_local} { + %0 = llvm.mlir.zero : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +llvm.mlir.alias external @foo_alias : !llvm.ptr { + %0 = llvm.mlir.addressof @callee : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +llvm.mlir.alias external @_ZTV1D : !llvm.struct<(array<3 x ptr>)> { + %0 = llvm.mlir.addressof @callee : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +// CHECK: llvm.mlir.alias external @foo_alias : !llvm.ptr { +// CHECK: %[[ADDR:.*]] = llvm.mlir.addressof @callee : !llvm.ptr +// CHECK: llvm.return %[[ADDR]] : !llvm.ptr +// CHECK: } +// CHECK: llvm.mlir.alias external @_ZTV1D : !llvm.struct<(array<3 x ptr>)> { +// CHECK: %[[ADDR:.*]] = llvm.mlir.addressof @callee : !llvm.ptr +// CHECK: llvm.return %[[ADDR]] : !llvm.ptr +// CHECK: } + +// ----- + +llvm.mlir.global external @zed(42 : i32) : i32 + +llvm.mlir.alias external @foo : i32 { + %0 = llvm.mlir.addressof @zed : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +llvm.mlir.alias external @foo2 : i16 { + %0 = llvm.mlir.addressof @zed : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +// CHECK: llvm.mlir.alias external @foo : i32 { +// CHECK: %[[ADDR:.*]] = llvm.mlir.addressof @zed : !llvm.ptr +// CHECK: llvm.return %[[ADDR]] : !llvm.ptr +// CHECK: } +// CHECK: llvm.mlir.alias external @foo2 : i16 { +// CHECK: %[[ADDR:.*]] = llvm.mlir.addressof @zed : !llvm.ptr +// CHECK: llvm.return %[[ADDR]] : !llvm.ptr +// CHECK: } + +// ----- + +llvm.mlir.global private constant @glob.private(dense<0> : tensor<32xi32>) : !llvm.array<32 x i32> + +llvm.mlir.alias linkonce_odr hidden @glob {dso_local} : !llvm.array<32 x i32> { + %0 = llvm.mlir.constant(1234 : i64) : i64 + %1 = llvm.mlir.addressof @glob.private : !llvm.ptr + %2 = llvm.ptrtoint %1 : !llvm.ptr to i64 + %3 = llvm.add %2, %0 : i64 + %4 = llvm.inttoptr %3 : i64 to !llvm.ptr + llvm.return %4 : !llvm.ptr +} + +// CHECK: llvm.mlir.global private constant @glob.private(dense<0> : tensor<32xi32>) +// CHECK: llvm.mlir.alias linkonce_odr hidden @glob {dso_local} : !llvm.array<32 x i32> { +// CHECK: %[[CST:.*]] = llvm.mlir.constant(1234 : i64) : i64 +// CHECK: %[[ADDR:.*]] = llvm.mlir.addressof @glob.private : !llvm.ptr +// CHECK: %[[INTADDR:.*]] = llvm.ptrtoint %[[ADDR]] : !llvm.ptr to i64 +// CHECK: %[[BACKTOPTR:.*]] = llvm.add %[[INTADDR]], %[[CST]] : i64 +// CHECK: %[[RET_ADDR:.*]] = llvm.inttoptr %[[BACKTOPTR]] : i64 to !llvm.ptr +// CHECK: llvm.return %[[RET_ADDR]] : !llvm.ptr +// CHECK: } + +// ----- + +llvm.mlir.global external @v1(0 : i32) : i32 +llvm.mlir.alias external @a3 : i32 { + %0 = llvm.mlir.addressof @v1 : !llvm.ptr + %1 = llvm.addrspacecast %0 : !llvm.ptr to !llvm.ptr<2> + llvm.return %1 : !llvm.ptr<2> +} + +// CHECK: llvm.mlir.alias external @a3 : i32 { +// CHECK: %[[ADDR:.*]] = llvm.mlir.addressof @v1 : !llvm.ptr +// CHECK: %1 = llvm.addrspacecast %[[ADDR]] : !llvm.ptr to !llvm.ptr<2> +// CHECK: llvm.return %1 : !llvm.ptr<2> +// CHECK: } + +// ----- + +llvm.mlir.global private @g1(0 : i32) {dso_local} : i32 + +llvm.mlir.alias private @a1 {dso_local} : i32 { + %0 = llvm.mlir.addressof @g1 : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +llvm.mlir.global internal constant @g2() : !llvm.ptr { + %0 = llvm.mlir.addressof @a1 : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +llvm.mlir.alias private @a2 {dso_local} : !llvm.ptr { + %0 = llvm.mlir.addressof @a1 : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +llvm.mlir.global internal constant @g3() : !llvm.ptr { + %0 = llvm.mlir.addressof @a2 : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +// CHECK: llvm.mlir.alias private @a1 {dso_local} : i32 { +// CHECK: %[[ADDR:.*]] = llvm.mlir.addressof @g1 : !llvm.ptr +// CHECK: llvm.return %[[ADDR]] : !llvm.ptr +// CHECK: } +// CHECK: llvm.mlir.global internal constant @g2() +// CHECK: %[[ADDR:.*]] = llvm.mlir.addressof @a1 : !llvm.ptr +// CHECK: llvm.return %[[ADDR]] : !llvm.ptr +// CHECK: } +// CHECK: llvm.mlir.alias private @a2 {dso_local} : !llvm.ptr { +// CHECK: %[[ADDR:.*]] = llvm.mlir.addressof @a1 : !llvm.ptr +// CHECK: llvm.return %[[ADDR]] : !llvm.ptr +// CHECK: } +// CHECK: llvm.mlir.global internal constant @g3() +// CHECK: %[[ADDR:.*]] = llvm.mlir.addressof @a2 : !llvm.ptr +// CHECK: llvm.return %[[ADDR]] : !llvm.ptr +// CHECK: } + +// ----- + +llvm.mlir.global private @g30(0 : i32) {dso_local} : i32 + +llvm.mlir.alias private unnamed_addr thread_local @a30 {dso_local} : i32 { + %0 = llvm.mlir.addressof @g30 : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +// CHECK: llvm.mlir.alias private unnamed_addr thread_local @a30 {dso_local} : i32 { +// CHECK: %0 = llvm.mlir.addressof @g30 : !llvm.ptr +// CHECK: llvm.return %0 : !llvm.ptr +// CHECK: } diff --git a/mlir/test/Dialect/LLVMIR/invalid.mlir b/mlir/test/Dialect/LLVMIR/invalid.mlir index 25806d9d0edd7..967645c577d2d 100644 --- a/mlir/test/Dialect/LLVMIR/invalid.mlir +++ b/mlir/test/Dialect/LLVMIR/invalid.mlir @@ -1695,3 +1695,53 @@ llvm.func @wrong_number_of_bundle_tags() { } : (i32, i32) -> () llvm.return } + +// ----- + +llvm.mlir.global external @x(42 : i32) : i32 + +// expected-error@+1 {{expects type to be a valid element type for an LLVM global alias}} +llvm.mlir.alias external @y : !llvm.label { + %0 = llvm.mlir.addressof @x : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +// ----- + +llvm.mlir.global external @x(42 : i32) : i32 + +// expected-error@+1 {{linkage not supported in aliases, available options}} +llvm.mlir.alias appending @y2 : i32 { + %0 = llvm.mlir.addressof @x : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +// ----- + +// expected-error@+1 {{initializer region must always return a pointer}} +llvm.mlir.alias external @y3 : i32 { + %c = llvm.mlir.constant(42 : i64) : i64 + llvm.return %c : i64 +} + +// ----- + +llvm.mlir.global external @x(42 : i32) : i32 + +llvm.mlir.alias external @y4 : i32 { + %0 = llvm.mlir.addressof @x : !llvm.ptr + // expected-error@+1 {{ops with side effects are not allowed in alias initializers}} + %2 = llvm.load %0 : !llvm.ptr -> i32 + llvm.return %0 : !llvm.ptr +} + +// ----- + +llvm.mlir.global external @x(42 : i32) : i32 + +llvm.mlir.alias external @y5 : i32 { + // expected-error@+1 {{pointer address space must match address space}} + %0 = llvm.mlir.addressof @x : !llvm.ptr<4> + llvm.return %0 : !llvm.ptr<4> +} + diff --git a/mlir/test/Target/LLVMIR/Import/alias.ll b/mlir/test/Target/LLVMIR/Import/alias.ll new file mode 100644 index 0000000000000..9f86da3ecc71c --- /dev/null +++ b/mlir/test/Target/LLVMIR/Import/alias.ll @@ -0,0 +1,88 @@ +; RUN: mlir-translate --import-llvm %s -split-input-file | FileCheck %s + +@foo_alias = alias ptr, ptr @callee + +; CHECK: llvm.mlir.alias external @foo_alias : !llvm.ptr { +; CHECK: %[[ADDR:.*]] = llvm.mlir.addressof @callee : !llvm.ptr +; CHECK: llvm.return %[[ADDR]] : !llvm.ptr +; CHECK: } + +define internal ptr @callee() { +entry: + ret ptr null +} + +; ----- + +@zed = global i32 42 +@foo = alias i32, ptr @zed +@foo2 = alias i16, ptr @zed + +; CHECK: llvm.mlir.alias external @foo : i32 { +; CHECK: %[[ADDR:.*]] = llvm.mlir.addressof @zed : !llvm.ptr +; CHECK: llvm.return %[[ADDR]] : !llvm.ptr +; CHECK: } +; CHECK: llvm.mlir.alias external @foo2 : i16 { +; CHECK: %[[ADDR:.*]] = llvm.mlir.addressof @zed : !llvm.ptr +; CHECK: llvm.return %[[ADDR]] : !llvm.ptr +; CHECK: } + +; ----- + +@v1 = global i32 0 +@a3 = alias i32, addrspacecast (ptr @v1 to ptr addrspace(2)) +; CHECK: llvm.mlir.alias external @a3 : i32 { +; CHECK: %[[ADDR:.*]] = llvm.mlir.addressof @v1 : !llvm.ptr +; CHECK: %[[CASTED_ADDR:.*]] = llvm.addrspacecast %[[ADDR]] : !llvm.ptr to !llvm.ptr<2> +; CHECK: llvm.return %[[CASTED_ADDR]] : !llvm.ptr<2> +; CHECK: } + +; ----- + +@some_name = constant { [3 x ptr] } { [3 x ptr] [ptr null, ptr null, ptr null] } +@vtable = alias { [3 x ptr] }, ptr @some_name + +; CHECK: llvm.mlir.alias external @vtable : !llvm.struct<(array<3 x ptr>)> { +; CHECK: %[[ADDR:.*]] = llvm.mlir.addressof @some_name : !llvm.ptr +; CHECK: llvm.return %[[ADDR]] : !llvm.ptr +; CHECK: } + +; ----- + +@glob.private = private constant [32 x i32] zeroinitializer +@glob = linkonce_odr hidden alias [32 x i32], inttoptr (i64 add (i64 ptrtoint (ptr @glob.private to i64), i64 1234) to ptr) + +; CHECK: llvm.mlir.alias linkonce_odr hidden @glob {dso_local} : !llvm.array<32 x i32> { +; CHECK: %[[CST:.*]] = llvm.mlir.constant(1234 : i64) : i64 +; CHECK: %[[ADDR:.*]] = llvm.mlir.addressof @glob.private : !llvm.ptr +; CHECK: %[[PTRTOINT:.*]] = llvm.ptrtoint %[[ADDR]] : !llvm.ptr to i64 +; CHECK: %[[INTTOPTR:.*]] = llvm.add %[[PTRTOINT]], %[[CST]] : i64 +; CHECK: %[[RET:.*]] = llvm.inttoptr %[[INTTOPTR]] : i64 to !llvm.ptr +; CHECK: llvm.return %[[RET]] : !llvm.ptr + +; ----- + +@g1 = private global i32 0 +@g2 = internal constant ptr @a1 +@g3 = internal constant ptr @a2 +@a1 = private alias i32, ptr @g1 +@a2 = private alias ptr, ptr @a1 + +; CHECK: llvm.mlir.alias private @a1 {dso_local} : i32 { +; CHECK: %[[ADDR:.*]] = llvm.mlir.addressof @g1 : !llvm.ptr +; CHECK: llvm.return %[[ADDR]] : !llvm.ptr +; CHECK: } +; CHECK: llvm.mlir.alias private @a2 {dso_local} : !llvm.ptr { +; CHECK-NEXT: %[[ADDR:.*]] = llvm.mlir.addressof @a1 : !llvm.ptr +; CHECK-NEXT: llvm.return %[[ADDR]] : !llvm.ptr +; CHECK-NEXT: } + +; CHECK: llvm.mlir.global internal constant @g2() {addr_space = 0 : i32, dso_local} : !llvm.ptr { +; CHECK-NEXT: %[[ADDR:.*]] = llvm.mlir.addressof @a1 : !llvm.ptr +; CHECK-NEXT: llvm.return %[[ADDR]] : !llvm.ptr +; CHECK-NEXT: } + +; CHECK: llvm.mlir.global internal constant @g3() {addr_space = 0 : i32, dso_local} : !llvm.ptr { +; CHECK-NEXT: %[[ADDR:.*]] = llvm.mlir.addressof @a2 : !llvm.ptr +; CHECK-NEXT: llvm.return %[[ADDR]] : !llvm.ptr +; CHECK-NEXT: } diff --git a/mlir/test/Target/LLVMIR/alias.mlir b/mlir/test/Target/LLVMIR/alias.mlir new file mode 100644 index 0000000000000..56832a4900746 --- /dev/null +++ b/mlir/test/Target/LLVMIR/alias.mlir @@ -0,0 +1,92 @@ +// RUN: mlir-translate -mlir-to-llvmir %s -split-input-file | FileCheck %s + +llvm.func internal @callee() -> !llvm.ptr attributes {dso_local} { + %0 = llvm.mlir.zero : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +llvm.mlir.alias external @foo_alias : !llvm.ptr { + %0 = llvm.mlir.addressof @callee : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +llvm.mlir.alias external @_ZTV1D : !llvm.struct<(array<3 x ptr>)> { + %0 = llvm.mlir.addressof @callee : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +// CHECK: @foo_alias = alias ptr, ptr @callee +// CHECK: @_ZTV1D = alias { [3 x ptr] }, ptr @callee + +// ----- + +llvm.mlir.global external @zed(42 : i32) : i32 + +llvm.mlir.alias external @foo : i32 { + %0 = llvm.mlir.addressof @zed : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +llvm.mlir.alias external @foo2 : i16 { + %0 = llvm.mlir.addressof @zed : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +// CHECK: @foo = alias i32, ptr @zed +// CHECK: @foo2 = alias i16, ptr @zed + +// ----- + +llvm.mlir.global private constant @glob.private(dense<0> : tensor<32xi32>) {dso_local} : !llvm.array<32 x i32> + +llvm.mlir.alias linkonce_odr hidden @glob {dso_local} : !llvm.array<32 x i32> { + %0 = llvm.mlir.constant(1234 : i64) : i64 + %1 = llvm.mlir.addressof @glob.private : !llvm.ptr + %2 = llvm.ptrtoint %1 : !llvm.ptr to i64 + %3 = llvm.add %2, %0 : i64 + %4 = llvm.inttoptr %3 : i64 to !llvm.ptr + llvm.return %4 : !llvm.ptr +} + +// CHECK: @glob = linkonce_odr hidden alias [32 x i32], inttoptr (i64 add (i64 ptrtoint (ptr @glob.private to i64), i64 1234) to ptr) + +// ----- + +llvm.mlir.global external @v1(0 : i32) : i32 +llvm.mlir.alias external @a3 : i32 { + %0 = llvm.mlir.addressof @v1 : !llvm.ptr + %1 = llvm.addrspacecast %0 : !llvm.ptr to !llvm.ptr<2> + llvm.return %1 : !llvm.ptr<2> +} + +// CHECK: @a3 = alias i32, addrspacecast (ptr @v1 to ptr addrspace(2)) + +// ----- + +llvm.mlir.global private @g1(0 : i32) {dso_local} : i32 + +llvm.mlir.alias private @a1 {dso_local} : i32 { + %0 = llvm.mlir.addressof @g1 : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +llvm.mlir.global internal constant @g2() {dso_local} : !llvm.ptr { + %0 = llvm.mlir.addressof @a1 : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +llvm.mlir.alias private @a2 {dso_local} : !llvm.ptr { + %0 = llvm.mlir.addressof @a1 : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +llvm.mlir.global internal constant @g3() {dso_local} : !llvm.ptr { + %0 = llvm.mlir.addressof @a2 : !llvm.ptr + llvm.return %0 : !llvm.ptr +} + +// CHECK: @g1 = private global i32 0 +// CHECK: @g2 = internal constant ptr @a1 +// CHECK: @g3 = internal constant ptr @a2 +// CHECK: @a1 = private alias i32, ptr @g1 +// CHECK: @a2 = private alias ptr, ptr @a1