diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td @@ -52,6 +52,7 @@ static StringRef getNoUndefAttrName() { return "llvm.noundef"; } static StringRef getSExtAttrName() { return "llvm.signext"; } static StringRef getZExtAttrName() { return "llvm.zeroext"; } + static StringRef getTBAAAttrName() { return "llvm.tbaa"; } /// Verifies if the attribute is a well-formed value for "llvm.struct_attrs" static LogicalResult verifyStructAttr( 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 @@ -205,6 +205,10 @@ code setAliasScopeMetadataCode = [{ moduleTranslation.setAliasScopeMetadata(op, inst); }]; + + code setTBAAMetadataCode = [{ + moduleTranslation.setTBAAMetadata(op, inst); + }]; } // Memory-related operations. @@ -360,6 +364,7 @@ # setNonTemporalMetadataCode # setAccessGroupsMetadataCode # setAliasScopeMetadataCode + # setTBAAMetadataCode # [{ $res = inst; }]; @@ -397,7 +402,8 @@ }] # setAlignmentCode # setNonTemporalMetadataCode # setAccessGroupsMetadataCode - # setAliasScopeMetadataCode; + # setAliasScopeMetadataCode + # setTBAAMetadataCode; // FIXME: Import attributes. string mlirBuilder = [{ $_op = $_builder.create($_location, $value, $addr); @@ -992,6 +998,7 @@ }]; let regions = (region SizedRegion<1>:$body); let assemblyFormat = "$sym_name attr-dict-with-keyword $body"; + let hasRegionVerifier = 1; } def LLVM_AliasScopeDomainMetadataOp : LLVM_Op<"alias_scope_domain", [ @@ -1074,6 +1081,152 @@ let assemblyFormat = "$sym_name attr-dict"; } +def LLVM_TBAARootMetadataOp : LLVM_Op<"tbaa_root", [ + HasParent<"MetadataOp">, Symbol +]> { + let arguments = (ins + SymbolNameAttr:$sym_name, + StrAttr:$identity + ); + let summary = "LLVM dialect TBAA root node metadata."; + let description = [{ + Defines a TBAA root node. + + Example: + llvm.metadata @tbaa { + llvm.tbaa_root @tbaa_root_0 {identity = "Simple C/C++ TBAA"} + llvm.return + } + + See the following link for more details: + https://llvm.org/docs/LangRef.html#tbaa-metadata + }]; + let assemblyFormat = [{ + $sym_name ` ` `{` `id` `=` $identity `}` attr-dict + }]; + let hasVerifier = 1; +} + +def LLVM_TBAATypeDescriptorOp : LLVM_Op<"tbaa_type_desc", [ + HasParent<"MetadataOp">, Symbol +]> { + let arguments = (ins + SymbolNameAttr:$sym_name, + OptionalAttr:$identity, + FlatSymbolRefArrayAttr:$members, + DenseI64ArrayAttr:$offsets + ); + let summary = "LLVM dialect TBAA node describing a type."; + let description = [{ + Defines a TBAA node describing a type. + + Example: + llvm.metadata @tbaa { + llvm.tbaa_root @tbaa_root_0 {identity = "Simple C/C++ TBAA"} + llvm.tbaa_type_desc @tbaa_type_desc_1 { + identity = "omnipotent char", + members = [@tbaa_root_0], + offsets = array + } + llvm.tbaa_type_desc @tbaa_type_desc_2 { + identity = "long long", + members = [@tbaa_type_desc_1], + offsets = array + } + llvm.tbaa_type_desc @tbaa_type_desc_3 { + identity = "agg2_t", + members = [@tbaa_type_desc_2, @tbaa_type_desc_2], + offsets = array + } + llvm.tbaa_type_desc @tbaa_type_desc_5 { + identity = "int", + members = [@tbaa_type_desc_1], + offsets = array + } + llvm.tbaa_type_desc @tbaa_type_desc_6 { + identity = "agg1_t", + members = [@tbaa_type_desc_5, @tbaa_type_desc_5], + offsets = array + } + llvm.return + } + + See the following link for more details: + https://llvm.org/docs/LangRef.html#tbaa-metadata + }]; + + // Interleave member types and offsets for better matching + // LLVM IR text representation. + let assemblyFormat = [{ + $sym_name ` ` `{` + ( `id` `=` $identity^ )? `,` + `members` `=` `{` custom($members, $offsets) `}` + `}` attr-dict + }]; + let hasVerifier = 1; +} + +def LLVM_TBAATagOp : LLVM_Op<"tbaa_tag", [ + HasParent<"MetadataOp">, Symbol +]> { + let arguments = (ins + SymbolNameAttr:$sym_name, + FlatSymbolRefAttr:$base_type, + FlatSymbolRefAttr:$access_type, + I64Attr:$offset, + UnitAttr:$constant + ); + let summary = "LLVM dialect TBAA node describing a memory access."; + let description = [{ + Defines a TBAA node describing a memory access. + + Example: + llvm.metadata @tbaa { + llvm.tbaa_root @tbaa_root_0 {identity = "Simple C/C++ TBAA"} + llvm.tbaa_type_desc @tbaa_type_desc_1 { + identity = "omnipotent char", + members = [@tbaa_root_0], + offsets = array + } + llvm.tbaa_type_desc @tbaa_type_desc_2 { + identity = "long long", + members = [@tbaa_type_desc_1], + offsets = array + } + llvm.tbaa_type_desc @tbaa_type_desc_3 { + identity = "agg2_t", + members = [@tbaa_type_desc_2, @tbaa_type_desc_2], + offsets = array + } + llvm.tbaa_tag @tbaa_tag_4 { + access_type = @tbaa_type_desc_2, + base_type = @tbaa_type_desc_3, + offset = 8 : i64 + } + llvm.tbaa_type_desc @tbaa_type_desc_5 { + identity = "int", + members = [@tbaa_type_desc_1], + offsets = array + } + llvm.tbaa_type_desc @tbaa_type_desc_6 { + identity = "agg1_t", + members = [@tbaa_type_desc_5, @tbaa_type_desc_5], + offsets = array + } + llvm.tbaa_tag @tbaa_tag_7 { + access_type = @tbaa_type_desc_5, + base_type = @tbaa_type_desc_6, + offset = 0 : i64 + } + llvm.return + } + + See the following link for more details: + https://llvm.org/docs/LangRef.html#tbaa-metadata + }]; + let assemblyFormat = "$sym_name attr-dict"; +} + def LLVM_GlobalOp : LLVM_Op<"mlir.global", [IsolatedFromAbove, SingleBlockImplicitTerminator<"ReturnOp">, Symbol]> { let arguments = (ins diff --git a/mlir/include/mlir/Target/LLVMIR/ModuleImport.h b/mlir/include/mlir/Target/LLVMIR/ModuleImport.h --- a/mlir/include/mlir/Target/LLVMIR/ModuleImport.h +++ b/mlir/include/mlir/Target/LLVMIR/ModuleImport.h @@ -149,6 +149,17 @@ /// implement the fastmath interface. void setFastmathFlagsAttr(llvm::Instruction *inst, Operation *op) const; + /// Converts LLVM metadata to corresponding MLIR representation, + /// e.g. metadata nodes referenced via !tbaa are converted to + /// TBAA operations hosted inside a MetadataOp. + LogicalResult convertMetadata(); + + /// Returns SymbolRefAttr representing TBAA metadata `node` + /// in `tbaaMapping`. + SymbolRefAttr lookupTBAAAttr(const llvm::MDNode *node) { + return tbaaMapping.lookup(node); + } + private: /// Clears the block and value mapping before processing a new region. void clearBlockAndValueMapping() { @@ -219,6 +230,25 @@ /// them fails. All operations are inserted at the start of the current /// function entry block. FailureOr convertConstantExpr(llvm::Constant *constant); + /// Returns symbol name to be used for MetadataOp containing + /// TBAA metadata operations. It must not conflict with the user + /// name space. + StringRef getTBAAMetadataOpName() const { return "__tbaa"; } + /// Returns a terminated MetadataOp into which TBAA metadata + /// operations can be placed. The MetadataOp is created + /// on the first invocation of this function. + MetadataOp getTBAAMetadataOp(); + /// Performs conversion of LLVM TBAA metadata starting from + /// `node`. On exit from this function all nodes reachable + /// from `node` are converted, and tbaaMapping map is updated + /// (unless all dependencies have been converted by a previous + /// invocation of this function). + LogicalResult processTBAAMetadata(const llvm::MDNode *node); + /// Returns unique string name of a symbol that may be used + /// for a TBAA metadata operation. The name will contain + /// the provided `basename` and will be uniqued via + /// tbaaNodeCounter (see below). + std::string getNewTBAANodeName(StringRef basename); /// Builder pointing at where the next instruction should be generated. OpBuilder builder; @@ -251,6 +281,16 @@ LLVM::TypeFromLLVMIRTranslator typeTranslator; /// Stateful debug information importer. std::unique_ptr debugImporter; + /// A terminated MetadataOp where TBAA metadata operations + /// can be inserted. + MetadataOp tbaaMetadataOp{}; + /// Mapping between LLVM TBAA metadata nodes and symbol references + /// to the LLVMIR dialect TBAA operations corresponding to these + /// nodes. + DenseMap tbaaMapping; + /// A counter to be used as a unique suffix for symbols + /// defined by TBAA operations. + unsigned tbaaNodeCounter = 0; }; } // namespace LLVM diff --git a/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h b/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h --- a/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h +++ b/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h @@ -148,6 +148,10 @@ // Sets LLVM metadata for memory operations that have alias scope information. void setAliasScopeMetadata(Operation *op, llvm::Instruction *inst); + /// Sets LLVM TBAA metadata for memory operations that have + /// TBAA attributes. + void setTBAAMetadata(Operation *op, llvm::Instruction *inst); + /// Converts the type from MLIR LLVM dialect to LLVM. llvm::Type *convertType(Type type); @@ -291,6 +295,14 @@ /// metadata nodes for them and their domains. LogicalResult createAliasScopeMetadata(); + /// Returns the LLVM metadata corresponding to a reference to an mlir LLVM + /// dialect TBAATagOp operation. + llvm::MDNode *getTBAANode(Operation &memOp, SymbolRefAttr tagRef) const; + + /// Process tbaa LLVM Metadata operations and create LLVM + /// metadata nodes for them. + LogicalResult createTBAAMetadata(); + /// Translates dialect attributes attached to the given operation. LogicalResult convertDialectAttributes(Operation *op); @@ -333,10 +345,14 @@ /// attribute. DenseMap loopOptionsMetadataMapping; - /// Mapping from an access scope metadata operation to its LLVM metadata. + /// Mapping from an alias scope metadata operation to its LLVM metadata. /// This map is populated on module entry. DenseMap aliasScopeMetadataMapping; + /// Mapping from a tbaa metadata operation to its LLVM metadata. + /// This map is populated on module entry. + DenseMap tbaaMetadataMapping; + /// Stack of user-specified state elements, useful when translating operations /// with regions. SmallVector> stack; 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 @@ -22,6 +22,7 @@ #include "mlir/IR/Matchers.h" #include "mlir/Transforms/InliningUtils.h" +#include "llvm/ADT/SCCIterator.h" #include "llvm/ADT/TypeSwitch.h" #include "llvm/AsmParser/Parser.h" #include "llvm/Bitcode/BitcodeReader.h" @@ -681,8 +682,18 @@ llvm::function_ref verifySymbolType) { if (Attribute attribute = op->getAttr(attributeName)) { - // The attribute is already verified to be a symbol ref array attribute via - // a constraint in the operation definition. + // Verify that the attribute is a symbol ref array attribute, + // because this constraint is not verified for all attribute + // names processed here (e.g. 'tbaa'). This verification + // is redundant in some cases. + if (!(attribute.isa() && + llvm::all_of(attribute.cast(), [&](Attribute attr) { + return attr && attr.isa(); + }))) + return op->emitOpError("attribute '") + << attributeName + << "' failed to satisfy constraint: symbol ref array attribute"; + for (SymbolRefAttr symbolRef : attribute.cast().getAsRange()) { StringAttr metadataName = symbolRef.getRootReference(); @@ -742,6 +753,11 @@ op, LLVMDialect::getNoAliasScopesAttrName()))) return failure(); + // tbaa + if (failed(verifyOpMetadata(op, + LLVMDialect::getTBAAAttrName()))) + return failure(); + return success(); } @@ -2566,6 +2582,217 @@ return {}; } +//===----------------------------------------------------------------------===// +// Utilities for LLVM::MetadataOp +//===----------------------------------------------------------------------===// + +namespace { +// A node of the TBAA graph. +struct TBAAGraphNode { + // Symbol name defined by a TBAA operation. + StringRef symbol; + // Operands (if any) of the TBAA operation. + SmallVector operands; +}; + +// TBAA graph. +class TBAAGraph { + // Mapping between symbol names defined by TBAA + // operations and corresponding TBAAGraphNode's. + DenseMap nodeMap; + // Synthetic root node that has all graph nodes + // in its operands list. + TBAAGraphNode root; + +public: + using iterator = SmallVectorImpl::iterator; + + iterator begin() { return root.operands.begin(); } + iterator end() { return root.operands.end(); } + TBAAGraphNode *getEntryNode() { return &root; } + + // Add new graph node corresponding to `symbol` + // defined by a TBAA operation. + void addNodeDefinition(StringAttr symbol) { + TBAAGraphNode &node = nodeMap[symbol]; + assert(node.symbol.empty() && "node is already in the graph"); + node.symbol = symbol; + root.operands.push_back(&node); + } + + // Get a pointer to TBAAGraphNode corresponding + // to `symbol`. The node must be already in the graph. + TBAAGraphNode *operator[](StringAttr symbol) { + auto it = nodeMap.find(symbol); + assert(it != nodeMap.end() && "node must be in the graph"); + return &it->second; + } +}; +} // end anonymous namespace +namespace llvm { +// GraphTraits definitions for using TBAAGraph with +// scc_iterator. +template <> +struct GraphTraits { + using NodeRef = TBAAGraphNode *; + using ChildIteratorType = SmallVectorImpl::iterator; + static ChildIteratorType child_begin(NodeRef ref) { + return ref->operands.begin(); + } + static ChildIteratorType child_end(NodeRef ref) { + return ref->operands.end(); + } +}; +template <> +struct GraphTraits : public GraphTraits { + static NodeRef getEntryNode(TBAAGraph *graph) { + return graph->getEntryNode(); + } + static ChildIteratorType nodes_begin(TBAAGraph *graph) { + return graph->begin(); + } + static ChildIteratorType nodes_end(TBAAGraph *graph) { return graph->end(); } +}; +} // end namespace llvm + +LogicalResult MetadataOp::verifyRegions() { + // Verify correctness of TBAA-related symbol references. + Region &body = getBody(); + // Symbol names defined by TBAARootMetadataOp and TBAATypeDescriptorOp. + llvm::SmallDenseSet definedGraphSymbols; + // Complete TBAA graph consisting of TBAARootMetadataOp, + // TBAATypeDescriptorOp, and TBAATagOp symbols. It is used + // for detecting cycles in the TBAA graph, which is illegal. + TBAAGraph tbaaGraph; + + for (Operation &op : body.getOps()) + if (isa(op) || + isa(op)) { + StringAttr symbolDef = cast(op).getNameAttr(); + definedGraphSymbols.insert(symbolDef); + tbaaGraph.addNodeDefinition(symbolDef); + } else if (auto tagOp = dyn_cast(op)) { + tbaaGraph.addNodeDefinition(tagOp.getSymNameAttr()); + } + + // Verify that TBAA metadata operations refer symbols + // from definedGraphSymbols only. Note that TBAATagOp + // cannot refer a symbol defined by TBAATagOp. + auto verifyReference = [&](Operation &op, StringAttr symbolName, + StringAttr referencingAttr) -> LogicalResult { + if (definedGraphSymbols.contains(symbolName)) + return success(); + return op.emitOpError() + << "expected " << referencingAttr << " to reference a symbol from '" + << (*this)->getName() << " @" << getSymName() + << "' defined by either '" + << LLVM::TBAARootMetadataOp::getOperationName() << "' or '" + << LLVM::TBAATypeDescriptorOp::getOperationName() + << "' while it references '@" << symbolName.getValue() << "'"; + }; + for (Operation &op : body.getOps()) { + if (auto tdOp = dyn_cast(op)) { + SmallVectorImpl &operands = + tbaaGraph[tdOp.getSymNameAttr()]->operands; + for (Attribute attr : tdOp.getMembers()) { + StringAttr symbolRef = attr.cast().getAttr(); + if (failed(verifyReference(op, symbolRef, tdOp.getMembersAttrName()))) + return failure(); + + // Since the reference is valid, we have to be able + // to find TBAAGraphNode corresponding to the operand. + operands.push_back(tbaaGraph[symbolRef]); + } + } + + if (auto tagOp = dyn_cast(op)) { + SmallVectorImpl &operands = + tbaaGraph[tagOp.getSymNameAttr()]->operands; + if (failed(verifyReference(op, tagOp.getBaseTypeAttr().getAttr(), + tagOp.getBaseTypeAttrName()))) + return failure(); + if (failed(verifyReference(op, tagOp.getAccessTypeAttr().getAttr(), + tagOp.getAccessTypeAttrName()))) + return failure(); + + operands.push_back(tbaaGraph[tagOp.getBaseTypeAttr().getAttr()]); + operands.push_back(tbaaGraph[tagOp.getAccessTypeAttr().getAttr()]); + } + } + + // Detect cycles in the TBAA graph. + for (llvm::scc_iterator sccIt = llvm::scc_begin(&tbaaGraph); + !sccIt.isAtEnd(); ++sccIt) { + if (!sccIt.hasCycle()) + continue; + auto diagOut = emitOpError() << "has cycle in TBAA graph (graph closure: <"; + llvm::interleaveComma( + *sccIt, diagOut, [&](TBAAGraphNode *node) { diagOut << node->symbol; }); + return diagOut << ">)"; + } + return success(); +} + +//===----------------------------------------------------------------------===// +// Utilities for TBAA related operations/attributes +//===----------------------------------------------------------------------===// + +static ParseResult parseTBAAMembers(OpAsmParser &parser, ArrayAttr &members, + DenseI64ArrayAttr &offsets) { + SmallVector membersVec; + SmallVector offsetsVec; + auto parseMembers = [&]() { + // Parse a pair of `<@tbaa_type_desc_sym, integer-offset>`. + FlatSymbolRefAttr member; + int64_t offset; + if (parser.parseLess() || parser.parseAttribute(member, Type()) || + parser.parseComma() || parser.parseInteger(offset) || + parser.parseGreater()) + return failure(); + + membersVec.push_back(member); + offsetsVec.push_back(offset); + return success(); + }; + + if (parser.parseCommaSeparatedList(parseMembers)) + return failure(); + + members = ArrayAttr::get(parser.getContext(), membersVec); + offsets = DenseI64ArrayAttr::get(parser.getContext(), offsetsVec); + return success(); +} + +static void printTBAAMembers(OpAsmPrinter &printer, + LLVM::TBAATypeDescriptorOp tdOp, ArrayAttr members, + DenseI64ArrayAttr offsets) { + llvm::interleaveComma( + llvm::zip(members, offsets.asArrayRef()), printer, [&](auto it) { + // Print `<@tbaa_type_desc_sym, integer-offset>`. + printer << '<' << std::get<0>(it) << ", " << std::get<1>(it) << '>'; + }); +} + +LogicalResult TBAARootMetadataOp::verify() { + if (!getIdentity().empty()) + return success(); + return emitOpError() << "expected non-empty " << getIdentityAttrName(); +} + +LogicalResult TBAATypeDescriptorOp::verify() { + // Verify that the members and offsets arrays have the same + // number of elements. + ArrayAttr members = getMembers(); + StringAttr membersName = getMembersAttrName(); + if (members.size() != getOffsets().size()) + return emitOpError() << "expected the same number of elements in " + << membersName << " and " << getOffsetsAttrName() + << ": " << members.size() + << " != " << getOffsets().size(); + + return success(); +} + //===----------------------------------------------------------------------===// // OpAsmDialectInterface //===----------------------------------------------------------------------===// diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp --- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp @@ -69,8 +69,8 @@ /// dialect attributes. static ArrayRef getSupportedMetadataImpl() { static const SmallVector convertibleMetadata = { - llvm::LLVMContext::MD_prof // profiling metadata - }; + llvm::LLVMContext::MD_prof, // profiling metadata + llvm::LLVMContext::MD_tbaa}; return convertibleMetadata; } @@ -127,6 +127,20 @@ return failure(); } +/// Attaches the given TBAA metadata `node` to the imported operation. +/// Returns success, if the metadata has been converted and the attachment +/// succeeds, failure - otherwise. +static LogicalResult setTBAAAttrs(const llvm::MDNode *node, Operation *op, + LLVM::ModuleImport &moduleImport) { + SymbolRefAttr tbaaTagSym = moduleImport.lookupTBAAAttr(node); + if (!tbaaTagSym) + return failure(); + + op->setAttr(LLVMDialect::getTBAAAttrName(), + ArrayAttr::get(op->getContext(), tbaaTagSym)); + return success(); +} + namespace { /// Implementation of the dialect interface that converts operations belonging @@ -151,6 +165,8 @@ // Call metadata specific handlers. if (kind == llvm::LLVMContext::MD_prof) return setProfilingAttrs(builder, node, op, moduleImport); + if (kind == llvm::LLVMContext::MD_tbaa) + return setTBAAAttrs(node, op, moduleImport); // A handler for a supported metadata kind is missing. llvm_unreachable("unknown metadata type"); diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp --- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp @@ -27,6 +27,7 @@ #include "llvm/ADT/StringSet.h" #include "llvm/IR/Constants.h" #include "llvm/IR/InlineAsm.h" +#include "llvm/IR/InstIterator.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Metadata.h" @@ -41,13 +42,24 @@ // Utility to print an LLVM value as a string for passing to emitError(). // FIXME: Diagnostic should be able to natively handle types that have // operator << (raw_ostream&) defined. -static std::string diag(llvm::Value &value) { +static std::string diag(const llvm::Value &value) { std::string str; llvm::raw_string_ostream os(str); os << value; return os.str(); } +// Utility to print an LLVM metadata node as a string for passing +// to emitError(). The module argument is needed to print the nodes +// canonically numbered. +static std::string diagMD(const llvm::Metadata *node, + const llvm::Module *module) { + std::string str; + llvm::raw_string_ostream os(str); + node->print(os, module, /*IsForDebug=*/true); + return os.str(); +} + /// Returns the name of the global_ctors global variables. static constexpr StringRef getGlobalCtorsVarName() { return "llvm.global_ctors"; @@ -328,6 +340,277 @@ builder.setInsertionPointToStart(mlirModule.getBody()); } +MetadataOp ModuleImport::getTBAAMetadataOp() { + if (tbaaMetadataOp) + return tbaaMetadataOp; + + OpBuilder::InsertionGuard guard(builder); + Location loc = mlirModule.getLoc(); + + builder.setInsertionPointToEnd(mlirModule.getBody()); + tbaaMetadataOp = builder.create(loc, getTBAAMetadataOpName()); + builder.createBlock(&tbaaMetadataOp.getBody()); + builder.create(loc, Value{}); + + return tbaaMetadataOp; +} + +std::string ModuleImport::getNewTBAANodeName(StringRef basename) { + return (Twine("tbaa_") + Twine(basename) + Twine('_') + + Twine(tbaaNodeCounter++)) + .str(); +} + +LogicalResult ModuleImport::processTBAAMetadata(const llvm::MDNode *node) { + Location loc = mlirModule.getLoc(); + SmallVector workList; + SetVector nodesToConvert; + workList.push_back(node); + while (!workList.empty()) { + const llvm::MDNode *current = workList.pop_back_val(); + if (tbaaMapping.count(current)) + continue; + // Allow cycles in TBAA metadata. Just import it as-is, + // and diagnose the problem during LLVMIR dialect verification. + if (!nodesToConvert.insert(current)) + continue; + for (const llvm::MDOperand &operand : current->operands()) + if (auto *opNode = dyn_cast_or_null(operand.get())) + workList.push_back(opNode); + } + + // If `node` is a valid TBAA root node, then return its identity + // string, otherwise return std::nullopt. + auto getIdentityIfRootNode = + [&](const llvm::MDNode *node) -> std::optional { + // Root node, e.g.: + // !0 = !{!"Simple C/C++ TBAA"} + if (node->getNumOperands() != 1) + return std::nullopt; + // If the operand is MDString, then assume that this is a root node. + if (auto op0 = dyn_cast(node->getOperand(0))) + return op0->getString(); + return std::nullopt; + }; + + // If `node` looks like a TBAA type descriptor metadata, + // then return true, if it is a valid node, and false otherwise. + // If it does not look like a TBAA type descriptor metadata, then + // return std::nullopt. + // If `identity` and `memberTypes/Offsets` are non-null, then they will + // contain the converted metadata operands for a valid TBAA node (i.e. when + // true is returned). + auto isTypeDescriptorNode = + [&](const llvm::MDNode *node, StringRef *identity = nullptr, + SmallVectorImpl *memberTypes = nullptr, + SmallVectorImpl *memberOffsets = + nullptr) -> std::optional { + unsigned numOperands = node->getNumOperands(); + // Type descriptor, e.g.: + // !1 = !{!"int", !0, /*optional*/i64 0} /* scalar int type */ + // !2 = !{!"agg_t", !1, i64 0} /* struct agg_t { int x; } */ + if (numOperands < 2) + return std::nullopt; + + // TODO: support "new" format (D41501) for type descriptors, + // where the first operand is an MDNode. + auto identityNode = dyn_cast(node->getOperand(0)); + if (!identityNode) + return std::nullopt; + + // This should be a type descriptor node. + if (identity) + *identity = identityNode->getString(); + + for (unsigned pairNum = 0, e = numOperands / 2; pairNum < e; ++pairNum) { + const auto *memberNode = + dyn_cast(node->getOperand(2 * pairNum + 1)); + if (!memberNode) { + emitError(loc) << "operand '" << 2 * pairNum + 1 << "' must be MDNode: " + << diagMD(node, llvmModule.get()); + return false; + } + int64_t offset = 0; + if (2 * pairNum + 2 >= numOperands) { + // Allow for optional 0 offset in 2-operand nodes. + if (numOperands != 2) { + emitError(loc) << "missing member offset: " + << diagMD(node, llvmModule.get()); + return false; + } + } else { + auto *offsetCI = llvm::mdconst::dyn_extract( + node->getOperand(2 * pairNum + 2)); + if (!offsetCI) { + emitError(loc) << "operand '" << 2 * pairNum + 2 + << "' must be ConstantInt: " + << diagMD(node, llvmModule.get()); + return false; + } + offset = offsetCI->getZExtValue(); + } + + if (memberTypes) + memberTypes->push_back(tbaaMapping.lookup(memberNode)); + if (memberOffsets) + memberOffsets->push_back(offset); + } + + return true; + }; + + // If `node` looks like a TBAA access tag metadata, + // then return true, if it is a valid node, and false otherwise. + // If it does not look like a TBAA access tag metadata, then + // return std::nullopt. + // If the other arguments are non-null, then they will contain + // the converted metadata operands for a valid TBAA node (i.e. when true is + // returned). + auto isTagNode = + [&](const llvm::MDNode *node, SymbolRefAttr *baseSymRef = nullptr, + SymbolRefAttr *accessSymRef = nullptr, int64_t *offset = nullptr, + bool *isConstant = nullptr) -> std::optional { + // Access tag, e.g.: + // !3 = !{!1, !1, i64 0} /* scalar int access */ + // !4 = !{!2, !1, i64 0} /* agg_t::x access */ + // + // Optional 4th argument is ConstantInt 0/1 identifying whether + // the location being accessed is "constant" (see for details: + // https://llvm.org/docs/LangRef.html#representation). + unsigned numOperands = node->getNumOperands(); + if (numOperands != 3 && numOperands != 4) + return std::nullopt; + auto baseMD = dyn_cast(node->getOperand(0)); + auto accessMD = dyn_cast(node->getOperand(1)); + auto offsetCI = + llvm::mdconst::dyn_extract(node->getOperand(2)); + if (!baseMD || !accessMD || !offsetCI) + return std::nullopt; + // TODO: support "new" TBAA format, if needed (see D41501). + // In the "old" format the first operand of the access type + // metadata is MDString. We have to distinguish the formats, + // because access tags have the same structure, but different + // meaning for the operands. + if (accessMD->getNumOperands() < 1 || + !isa(accessMD->getOperand(0))) + return std::nullopt; + bool isConst = false; + if (numOperands == 4) { + auto isConstantCI = + llvm::mdconst::dyn_extract(node->getOperand(3)); + if (!isConstantCI) { + emitError(loc) << "operand '3' must be ConstantInt: " + << diagMD(node, llvmModule.get()); + return false; + } + isConst = isConstantCI->getValue()[0]; + } + if (baseSymRef) + *baseSymRef = tbaaMapping.lookup(baseMD); + if (accessSymRef) + *accessSymRef = tbaaMapping.lookup(accessMD); + if (offset) + *offset = offsetCI->getZExtValue(); + if (isConstant) + *isConstant = isConst; + return true; + }; + + // Insert new operations before the terminator. + OpBuilder::InsertionGuard guard(builder); + builder.setInsertionPoint(&getTBAAMetadataOp().getBody().back().back()); + StringAttr metadataOpName = SymbolTable::getSymbolName(getTBAAMetadataOp()); + + // On the first walk, create SymbolRefAttr's and map them + // to nodes in `nodesToConvert`. + for (const auto *current : nodesToConvert) { + if (std::optional identity = getIdentityIfRootNode(current)) { + if (identity.value().empty()) + return emitError(loc) << "TBAA root node must have non-empty identity: " + << diagMD(current, llvmModule.get()); + + // The root nodes do not have operands, so we can create + // the TBAARootMetadataOp on the first walk. + auto rootNode = builder.create( + loc, getNewTBAANodeName("root"), identity.value()); + tbaaMapping.try_emplace(current, FlatSymbolRefAttr::get(rootNode)); + continue; + } + if (std::optional isValid = isTypeDescriptorNode(current)) { + if (!isValid.value()) + return failure(); + tbaaMapping.try_emplace( + current, FlatSymbolRefAttr::get(builder.getContext(), + getNewTBAANodeName("type_desc"))); + continue; + } + if (std::optional isValid = isTagNode(current)) { + if (!isValid.value()) + return failure(); + // TBAATagOp symbols must be referred by their fully qualified + // names, so create a path to TBAATagOp symbol. + tbaaMapping.try_emplace( + current, SymbolRefAttr::get( + builder.getContext(), metadataOpName, + FlatSymbolRefAttr::get(builder.getContext(), + getNewTBAANodeName("tag")))); + continue; + } + return emitError(loc) << "unsupported TBAA node format: " + << diagMD(current, llvmModule.get()); + } + + // On the second walk, create TBAA operations using the symbol names from the + // map. + for (const auto *current : nodesToConvert) { + StringRef identity; + SmallVector memberTypes; + SmallVector memberOffsets; + if (std::optional isValid = isTypeDescriptorNode( + current, &identity, &memberTypes, &memberOffsets)) { + assert(isValid.value() && "type descriptor node must be valid"); + + builder.create( + loc, tbaaMapping.lookup(current).getLeafReference(), + builder.getStringAttr(identity), builder.getArrayAttr(memberTypes), + memberOffsets); + continue; + } + SymbolRefAttr baseSymRef, accessSymRef; + int64_t offset; + bool isConstant; + if (std::optional isValid = isTagNode( + current, &baseSymRef, &accessSymRef, &offset, &isConstant)) { + assert(isValid.value() && "access tag node must be valid"); + builder.create( + loc, tbaaMapping.lookup(current).getLeafReference(), + baseSymRef.getLeafReference(), accessSymRef.getLeafReference(), + offset, isConstant); + continue; + } + } + + return success(); +} + +LogicalResult ModuleImport::convertMetadata() { + OpBuilder::InsertionGuard guard(builder); + builder.setInsertionPoint(mlirModule.getBody(), mlirModule.getBody()->end()); + for (const llvm::Function &func : llvmModule->functions()) + for (const llvm::Instruction &inst : llvm::instructions(func)) { + llvm::AAMDNodes nodes = inst.getAAMetadata(); + if (!nodes) + continue; + + if (const llvm::MDNode *tbaaMD = nodes.TBAA) + if (failed(processTBAAMetadata(tbaaMD))) + return failure(); + // TODO: only TBAA metadata is currently supported. + } + + return success(); +} + LogicalResult ModuleImport::convertGlobals() { for (llvm::GlobalVariable &globalVar : llvmModule->globals()) { if (globalVar.getName() == getGlobalCtorsVarName() || @@ -1275,6 +1558,8 @@ ModuleImport moduleImport(module.get(), std::move(llvmModule)); if (failed(moduleImport.initializeImportInterface())) return {}; + if (failed(moduleImport.convertMetadata())) + return {}; if (failed(moduleImport.convertGlobals())) return {}; if (failed(moduleImport.convertFunctions())) 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 @@ -1175,6 +1175,114 @@ populateScopeMetadata(LLVMDialect::getNoAliasScopesAttrName(), "noalias"); } +llvm::MDNode *ModuleTranslation::getTBAANode(Operation &memOp, + SymbolRefAttr tagRef) const { + StringAttr metadataName = tagRef.getRootReference(); + StringAttr tagName = tagRef.getLeafReference(); + auto metadataOp = SymbolTable::lookupNearestSymbolFrom( + memOp.getParentOp(), metadataName); + Operation *tagOp = SymbolTable::lookupNearestSymbolFrom(metadataOp, tagName); + return tbaaMetadataMapping.lookup(tagOp); +} + +void ModuleTranslation::setTBAAMetadata(Operation *op, + llvm::Instruction *inst) { + auto tbaa = op->getAttrOfType(LLVMDialect::getTBAAAttrName()); + if (!tbaa || tbaa.empty()) + return; + // LLVM IR currently does not support attaching more than one + // TBAA access tag to a memory accessing instruction. + // It may be useful to support this in future, but for the time being + // just ignore the metadata if MLIR operation has multiple access tags. + if (tbaa.size() > 1) { + op->emitWarning() << "TBAA access tags were not translated, because LLVM " + "IR only supports a single tag per instruction"; + return; + } + SymbolRefAttr tagRef = tbaa[0].cast(); + llvm::MDNode *tagNode = getTBAANode(*op, tagRef); + inst->setMetadata(llvm::LLVMContext::MD_tbaa, tagNode); +} + +LogicalResult ModuleTranslation::createTBAAMetadata() { + llvm::LLVMContext &ctx = llvmModule->getContext(); + llvm::IntegerType *offsetTy = llvm::IntegerType::get(ctx, 64); + + // Walk TBAA metadata and create MDNode's with placeholder + // operands for the references of other TBAA nodes. + for (auto metadata : getModuleBody(mlirModule).getOps()) { + for (auto &op : metadata.getBody().getOps()) { + SmallVector operands; + if (auto rootOp = dyn_cast(op)) { + operands.push_back(llvm::MDString::get(ctx, rootOp.getIdentity())); + } else if (auto tdOp = dyn_cast(op)) { + operands.push_back(llvm::MDString::get( + ctx, tdOp.getIdentity().value_or(llvm::StringRef{}))); + for (int64_t offset : tdOp.getOffsets()) { + operands.push_back(nullptr); // Placeholder for the member type. + operands.push_back(llvm::ConstantAsMetadata::get( + llvm::ConstantInt::get(offsetTy, offset))); + } + } else if (auto tagOp = dyn_cast(op)) { + operands.push_back(nullptr); // Placeholder for the base type. + operands.push_back(nullptr); // Placeholder for the access type. + operands.push_back(llvm::ConstantAsMetadata::get( + llvm::ConstantInt::get(offsetTy, tagOp.getOffset()))); + if (tagOp.getConstant()) + operands.push_back(llvm::ConstantAsMetadata::get( + llvm::ConstantInt::get(offsetTy, 1))); + } + + if (operands.empty()) + continue; + + tbaaMetadataMapping.insert({&op, llvm::MDNode::get(ctx, operands)}); + } + } + + // Walk TBAA metadata second time and update the placeholder + // references. + for (auto metadata : getModuleBody(mlirModule).getOps()) { + for (auto &op : metadata.getBody().getOps()) { + SmallVector refNames; + SmallVector operandIndices; + if (auto tdOp = dyn_cast(op)) { + // The type references are in 1, 3, 5, etc. positions. + unsigned opNum = 1; + for (Attribute typeAttr : tdOp.getMembers()) { + refNames.push_back(typeAttr.cast().getValue()); + operandIndices.push_back(opNum); + opNum += 2; + } + } else if (auto tagOp = dyn_cast(op)) { + refNames.push_back(tagOp.getBaseType()); + operandIndices.push_back(0); + refNames.push_back(tagOp.getAccessType()); + operandIndices.push_back(1); + } + + if (refNames.empty()) + continue; + + llvm::MDNode *descNode = tbaaMetadataMapping.lookup(&op); + for (auto [refName, opNum] : llvm::zip(refNames, operandIndices)) { + // refDef availability in the parent MetadataOp + // is checked by module verifier. + Operation *refDef = SymbolTable::lookupSymbolIn(metadata, refName); + llvm::MDNode *refNode = tbaaMetadataMapping.lookup(refDef); + if (!refNode) { + op.emitOpError() << "llvm::MDNode missing for the member '@" + << refName << "'"; + return failure(); + } + descNode->replaceOperandWith(opNum, refNode); + } + } + } + + return success(); +} + llvm::Type *ModuleTranslation::convertType(Type type) { return typeTranslator.translateType(type); } @@ -1268,6 +1376,8 @@ return nullptr; if (failed(translator.createAliasScopeMetadata())) return nullptr; + if (failed(translator.createTBAAMetadata())) + return nullptr; if (failed(translator.convertFunctions())) return nullptr; diff --git a/mlir/test/Dialect/LLVMIR/tbaa-invalid.mlir b/mlir/test/Dialect/LLVMIR/tbaa-invalid.mlir new file mode 100644 --- /dev/null +++ b/mlir/test/Dialect/LLVMIR/tbaa-invalid.mlir @@ -0,0 +1,181 @@ +// RUN: mlir-opt -split-input-file -verify-diagnostics %s + +module { + llvm.metadata @__tbaa { + llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"} + llvm.tbaa_tag @tbaa_tag_1 {access_type = @tbaa_root_0, base_type = @tbaa_root_0, offset = 0 : i64} + llvm.return + } + llvm.func @tbaa(%arg0: !llvm.ptr) { + %0 = llvm.mlir.constant(1 : i8) : i8 + // expected-error@below {{expected '@tbaa_tag_1' to specify a fully qualified reference}} + llvm.store %0, %arg0 {llvm.tbaa = [@tbaa_tag_1]} : i8, !llvm.ptr + llvm.return + } +} + +// ----- + +llvm.func @tbaa(%arg0: !llvm.ptr) { + %0 = llvm.mlir.constant(1 : i8) : i8 + // expected-error@below {{attribute 'llvm.tbaa' failed to satisfy constraint: symbol ref array attribute}} + llvm.store %0, %arg0 {llvm.tbaa = ["sym"]} : i8, !llvm.ptr + llvm.return +} + +// ----- + +module { + llvm.func @tbaa(%arg0: !llvm.ptr) { + %0 = llvm.mlir.constant(1 : i8) : i8 + // expected-error@below {{expected '@metadata::@group1' to resolve to a llvm.tbaa_tag}} + llvm.store %0, %arg0 {llvm.tbaa = [@metadata::@group1]} : i8, !llvm.ptr + llvm.return + } + llvm.metadata @metadata { + llvm.access_group @group1 + llvm.return + } +} + +// ----- + +module { + llvm.func @tbaa(%arg0: !llvm.ptr) { + %0 = llvm.mlir.constant(1 : i8) : i8 + // expected-error@below {{expected '@metadata::@sym' to be a valid reference}} + llvm.store %0, %arg0 {llvm.tbaa = [@metadata::@sym]} : i8, !llvm.ptr + llvm.return + } + llvm.metadata @metadata { + llvm.return + } +} + +// ----- + +llvm.func @tbaa(%arg0: !llvm.ptr) { + %0 = llvm.mlir.constant(1 : i8) : i8 + // expected-error@below {{expected '@tbaa::@sym' to reference a metadata op}} + llvm.store %0, %arg0 {llvm.tbaa = [@tbaa::@sym]} : i8, !llvm.ptr + llvm.return +} + +// ----- + +llvm.func @tbaa() { + // expected-error@below {{expects parent op 'llvm.metadata'}} + llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"} + llvm.return +} + +// ----- + +module { + llvm.metadata @__tbaa { + llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"} + llvm.return + } + + llvm.func @tbaa() { + // expected-error@below {{expects parent op 'llvm.metadata'}} + llvm.tbaa_type_desc @tbaa_type_desc_1 {id = "omnipotent char", members = {<@tbaa_root_0, 0>}} + llvm.return + } +} + +// ----- + +module { + llvm.metadata @__tbaa { + llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"} + llvm.return + } + + llvm.func @tbaa() { + // expected-error@below {{expects parent op 'llvm.metadata'}} + llvm.tbaa_tag @tbaa_tag_1 {access_type = @tbaa_root_0, base_type = @tbaa_root_0, offset = 0 : i64} + llvm.return + } +} + +// ----- + +module { + llvm.metadata @__tbaa { + // expected-error@below {{expected non-empty "identity"}} + llvm.tbaa_root @tbaa_root_0 {id = ""} + llvm.return + } +} + +// ----- + + "builtin.module"() ({ + "llvm.metadata"() ({ + "llvm.tbaa_root"() {identity = "Simple C/C++ TBAA", sym_name = "tbaa_root_0"} : () -> () + "llvm.tbaa_type_desc"() {identity = "omnipotent char", members = [@tbaa_root_0], offsets = array, sym_name = "tbaa_type_desc_1"} : () -> () + // expected-error@below {{expected the same number of elements in "members" and "offsets": 2 != 1}} + "llvm.tbaa_type_desc"() {identity = "agg_t", members = [@tbaa_type_desc_1, @tbaa_type_desc_1], offsets = array, sym_name = "tbaa_type_desc_2"} : () -> () + "llvm.return"() : () -> () + }) {sym_name = "__tbaa"} : () -> () + }) : () -> () + +// ----- + +module { + llvm.metadata @__tbaa { + llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"} + // expected-error@below {{expected "base_type" to reference a symbol from 'llvm.metadata @__tbaa' defined by either 'llvm.tbaa_root' or 'llvm.tbaa_type_desc' while it references '@tbaa_root_2'}} + llvm.tbaa_tag @tbaa_tag_1 {access_type = @tbaa_root_0, base_type = @tbaa_root_2, offset = 0 : i64} + llvm.return + } +} + +// ----- + +module { + llvm.metadata @__tbaa { + llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"} + // expected-error@below {{expected "access_type" to reference a symbol from 'llvm.metadata @__tbaa' defined by either 'llvm.tbaa_root' or 'llvm.tbaa_type_desc' while it references '@tbaa_root_2'}} + llvm.tbaa_tag @tbaa_tag_1 {access_type = @tbaa_root_2, base_type = @tbaa_root_0, offset = 0 : i64} + llvm.return + } +} + +// ----- + +module { + llvm.metadata @__tbaa { + llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"} + llvm.tbaa_type_desc @tbaa_type_desc_1 {id = "omnipotent char", members = {<@tbaa_root_0, 0>}} + llvm.tbaa_type_desc @tbaa_type_desc_2 {id = "long long", members = {<@tbaa_type_desc_1, 0>}} + // expected-error@below {{expected "members" to reference a symbol from 'llvm.metadata @__tbaa' defined by either 'llvm.tbaa_root' or 'llvm.tbaa_type_desc' while it references '@tbaa_type_desc_4'}} + llvm.tbaa_type_desc @tbaa_type_desc_3 {id = "agg2_t", members = {<@tbaa_type_desc_2, 0>, <@tbaa_type_desc_4, 8>}} + llvm.return + } +} + +// ----- + +module { + llvm.metadata @__tbaa { + llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"} + llvm.tbaa_tag @tbaa_tag_1 {access_type = @tbaa_root_0, base_type = @tbaa_root_0, offset = 0 : i64} + // expected-error@below {{expected "access_type" to reference a symbol from 'llvm.metadata @__tbaa' defined by either 'llvm.tbaa_root' or 'llvm.tbaa_type_desc' while it references '@tbaa_tag_1'}} + llvm.tbaa_tag @tbaa_tag_2 {access_type = @tbaa_tag_1, base_type = @tbaa_root_0, offset = 0 : i64} + llvm.return + } +} + +// ----- + +module { + // expected-error@below {{has cycle in TBAA graph (graph closure: )}} + llvm.metadata @__tbaa { + llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"} + llvm.tbaa_type_desc @tbaa_type_desc_1 {id = "omnipotent char", members = {<@tbaa_type_desc_2, 0>}} + llvm.tbaa_type_desc @tbaa_type_desc_2 {id = "long long", members = {<@tbaa_type_desc_1, 0>}} + llvm.return + } +} diff --git a/mlir/test/Dialect/LLVMIR/tbaa-roundtrip.mlir b/mlir/test/Dialect/LLVMIR/tbaa-roundtrip.mlir new file mode 100644 --- /dev/null +++ b/mlir/test/Dialect/LLVMIR/tbaa-roundtrip.mlir @@ -0,0 +1,105 @@ +// RUN: mlir-opt %s | mlir-opt | FileCheck %s + +module { + llvm.metadata @__tbaa { + llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"} + llvm.tbaa_tag @tbaa_tag_1 {access_type = @tbaa_root_0, base_type = @tbaa_root_0, offset = 0 : i64} + llvm.tbaa_root @tbaa_root_2 {id = "Other language TBAA"} + llvm.tbaa_tag @tbaa_tag_3 {access_type = @tbaa_root_2, base_type = @tbaa_root_2, offset = 0 : i64} + llvm.return + } + llvm.func @tbaa1(%arg0: !llvm.ptr, %arg1: !llvm.ptr) { + %0 = llvm.mlir.constant(1 : i8) : i8 + llvm.store %0, %arg0 {tbaa = [@__tbaa::@tbaa_tag_1]} : i8, !llvm.ptr + llvm.store %0, %arg1 {tbaa = [@__tbaa::@tbaa_tag_3]} : i8, !llvm.ptr + llvm.return + } +} + +// CHECK-LABEL: llvm.metadata @__tbaa { +// CHECK: llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"} +// CHECK: llvm.tbaa_tag @tbaa_tag_1 {access_type = @tbaa_root_0, base_type = @tbaa_root_0, offset = 0 : i64} +// CHECK: llvm.tbaa_root @tbaa_root_2 {id = "Other language TBAA"} +// CHECK: llvm.tbaa_tag @tbaa_tag_3 {access_type = @tbaa_root_2, base_type = @tbaa_root_2, offset = 0 : i64} +// CHECK: llvm.return +// CHECK: } +// CHECK: llvm.func @tbaa1(%[[VAL_0:.*]]: !llvm.ptr, %[[VAL_1:.*]]: !llvm.ptr) { +// CHECK: %[[VAL_2:.*]] = llvm.mlir.constant(1 : i8) : i8 +// CHECK: llvm.store %[[VAL_2]], %[[VAL_0]] {tbaa = [@__tbaa::@tbaa_tag_1]} : i8, !llvm.ptr +// CHECK: llvm.store %[[VAL_2]], %[[VAL_1]] {tbaa = [@__tbaa::@tbaa_tag_3]} : i8, !llvm.ptr +// CHECK: llvm.return +// CHECK: } + +module { + llvm.metadata @__tbaa { + llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"} + llvm.tbaa_type_desc @tbaa_type_desc_1 {id = "omnipotent char", members = {<@tbaa_root_0, 0>}} + llvm.tbaa_type_desc @tbaa_type_desc_2 {id = "long long", members = {<@tbaa_type_desc_1, 0>}} + llvm.tbaa_type_desc @tbaa_type_desc_3 {id = "agg2_t", members = {<@tbaa_type_desc_2, 0>, <@tbaa_type_desc_2, 8>}} + llvm.tbaa_tag @tbaa_tag_4 {access_type = @tbaa_type_desc_2, base_type = @tbaa_type_desc_3, offset = 8 : i64} + llvm.tbaa_type_desc @tbaa_type_desc_5 {id = "int", members = {<@tbaa_type_desc_1, 0>}} + llvm.tbaa_type_desc @tbaa_type_desc_6 {id = "agg1_t", members = {<@tbaa_type_desc_5, 0>, <@tbaa_type_desc_5, 4>}} + llvm.tbaa_tag @tbaa_tag_7 {access_type = @tbaa_type_desc_5, base_type = @tbaa_type_desc_6, offset = 0 : i64} + llvm.return + } + llvm.func @tbaa2(%arg0: !llvm.ptr, %arg1: !llvm.ptr) { + %0 = llvm.mlir.constant(0 : i32) : i32 + %1 = llvm.mlir.constant(1 : i32) : i32 + %2 = llvm.getelementptr inbounds %arg1[%0, 1] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"struct.agg2_t", (i64, i64)> + %3 = llvm.load %2 {tbaa = [@__tbaa::@tbaa_tag_4]} : !llvm.ptr -> i64 + %4 = llvm.trunc %3 : i64 to i32 + %5 = llvm.getelementptr inbounds %arg0[%0, 0] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"struct.agg1_t", (i32, i32)> + llvm.store %4, %5 {tbaa = [@__tbaa::@tbaa_tag_7]} : i32, !llvm.ptr + llvm.return + } +} + +// CHECK-LABEL: llvm.metadata @__tbaa { +// CHECK: llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"} +// CHECK: llvm.tbaa_type_desc @tbaa_type_desc_1 {id = "omnipotent char", members = {<@tbaa_root_0, 0>}} +// CHECK: llvm.tbaa_type_desc @tbaa_type_desc_2 {id = "long long", members = {<@tbaa_type_desc_1, 0>}} +// CHECK: llvm.tbaa_type_desc @tbaa_type_desc_3 {id = "agg2_t", members = {<@tbaa_type_desc_2, 0>, <@tbaa_type_desc_2, 8>}} +// CHECK: llvm.tbaa_tag @tbaa_tag_4 {access_type = @tbaa_type_desc_2, base_type = @tbaa_type_desc_3, offset = 8 : i64} +// CHECK: llvm.tbaa_type_desc @tbaa_type_desc_5 {id = "int", members = {<@tbaa_type_desc_1, 0>}} +// CHECK: llvm.tbaa_type_desc @tbaa_type_desc_6 {id = "agg1_t", members = {<@tbaa_type_desc_5, 0>, <@tbaa_type_desc_5, 4>}} +// CHECK: llvm.tbaa_tag @tbaa_tag_7 {access_type = @tbaa_type_desc_5, base_type = @tbaa_type_desc_6, offset = 0 : i64} +// CHECK: llvm.return +// CHECK: } +// CHECK: llvm.func @tbaa2(%[[VAL_0:.*]]: !llvm.ptr, %[[VAL_1:.*]]: !llvm.ptr) { +// CHECK: %[[VAL_2:.*]] = llvm.mlir.constant(0 : i32) : i32 +// CHECK: %[[VAL_3:.*]] = llvm.mlir.constant(1 : i32) : i32 +// CHECK: %[[VAL_4:.*]] = llvm.getelementptr inbounds %[[VAL_1]]{{\[}}%[[VAL_2]], 1] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"struct.agg2_t", (i64, i64)> +// CHECK: %[[VAL_5:.*]] = llvm.load %[[VAL_4]] {tbaa = [@__tbaa::@tbaa_tag_4]} : !llvm.ptr -> i64 +// CHECK: %[[VAL_6:.*]] = llvm.trunc %[[VAL_5]] : i64 to i32 +// CHECK: %[[VAL_7:.*]] = llvm.getelementptr inbounds %[[VAL_0]]{{\[}}%[[VAL_2]], 0] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"struct.agg1_t", (i32, i32)> +// CHECK: llvm.store %[[VAL_6]], %[[VAL_7]] {tbaa = [@__tbaa::@tbaa_tag_7]} : i32, !llvm.ptr +// CHECK: llvm.return +// CHECK: } + +module { + llvm.metadata @__tbaa { + llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"} + llvm.tbaa_tag @tbaa_tag_1 {access_type = @tbaa_root_0, base_type = @tbaa_root_0, offset = 0 : i64} + llvm.tbaa_root @tbaa_root_2 {id = "Other language TBAA"} + llvm.tbaa_tag @tbaa_tag_3 {access_type = @tbaa_root_2, base_type = @tbaa_root_2, offset = 0 : i64} + llvm.return + } + llvm.func @tbaa1(%arg0: !llvm.ptr) { + %0 = llvm.mlir.constant(1 : i8) : i8 + llvm.store %0, %arg0 {tbaa = [@__tbaa::@tbaa_tag_1, @__tbaa::@tbaa_tag_3]} : i8, !llvm.ptr + llvm.return + } +} + +// CHECK-LABEL: llvm.metadata @__tbaa { +// CHECK: llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"} +// CHECK: llvm.tbaa_tag @tbaa_tag_1 {access_type = @tbaa_root_0, base_type = @tbaa_root_0, offset = 0 : i64} +// CHECK: llvm.tbaa_root @tbaa_root_2 {id = "Other language TBAA"} +// CHECK: llvm.tbaa_tag @tbaa_tag_3 {access_type = @tbaa_root_2, base_type = @tbaa_root_2, offset = 0 : i64} +// CHECK: llvm.return +// CHECK: } +// CHECK: llvm.func @tbaa1(%[[VAL_0:.*]]: !llvm.ptr) { +// CHECK: %[[VAL_1:.*]] = llvm.mlir.constant(1 : i8) : i8 +// CHECK: llvm.store %[[VAL_1]], %[[VAL_0]] {tbaa = [@__tbaa::@tbaa_tag_1, @__tbaa::@tbaa_tag_3]} : i8, !llvm.ptr +// CHECK: llvm.return +// CHECK: } diff --git a/mlir/test/Target/LLVMIR/Import/import-failure.ll b/mlir/test/Target/LLVMIR/Import/import-failure.ll --- a/mlir/test/Target/LLVMIR/Import/import-failure.ll +++ b/mlir/test/Target/LLVMIR/Import/import-failure.ll @@ -118,3 +118,90 @@ define void @foo() { ret void } + +; // ----- + +; CHECK: error: TBAA root node must have non-empty identity: !2 = !{!""} +define dso_local void @tbaa(ptr %0) { + store i8 1, ptr %0, align 4, !tbaa !2 + ret void +} + +!0 = !{!""} +!1 = !{!"omnipotent char", !0, i64 0} +!2 = !{!1, !1, i64 0} + +; // ----- + +; CHECK: error: unsupported TBAA node format: !0 = !{!1, i64 0, i64 0} +define dso_local void @tbaa(ptr %0) { + store i8 1, ptr %0, align 4, !tbaa !2 + ret void +} + +!0 = !{!"Simple C/C++ TBAA"} +!1 = !{!"omnipotent char", !0, i64 0} +!2 = !{!1, i64 0, i64 0} + +; // ----- + +; CHECK: error: operand '1' must be MDNode: !1 = !{!"omnipotent char", i64 0, i64 0} +define dso_local void @tbaa(ptr %0) { + store i8 1, ptr %0, align 4, !tbaa !2 + ret void +} + +!0 = !{!"Simple C/C++ TBAA"} +!1 = !{!"omnipotent char", i64 0, i64 0} +!2 = !{!1, !1, i64 0} + +; // ----- + +; CHECK: error: missing member offset: !1 = !{!"agg_t", !2, i64 0, !2} +define dso_local void @tbaa(ptr %0) { + store i8 1, ptr %0, align 4, !tbaa !3 + ret void +} + +!0 = !{!"Simple C/C++ TBAA"} +!1 = !{!"omnipotent char", !0, i64 0} +!2 = !{!"agg_t", !1, i64 0, !1} +!3 = !{!2, !1, i64 0} + +; // ----- + +; CHECK: error: operand '4' must be ConstantInt: !1 = !{!"agg_t", !2, i64 0, !2, double 1.000000e+00} +define dso_local void @tbaa(ptr %0) { + store i8 1, ptr %0, align 4, !tbaa !3 + ret void +} + +!0 = !{!"Simple C/C++ TBAA"} +!1 = !{!"omnipotent char", !0, i64 0} +!2 = !{!"agg_t", !1, i64 0, !1, double 1.0} +!3 = !{!2, !1, i64 0} + +; // ----- + +; CHECK: error: operand '3' must be ConstantInt: !0 = !{!1, !1, i64 0, double 1.000000e+00} +define dso_local void @tbaa(ptr %0) { + store i8 1, ptr %0, align 4, !tbaa !2 + ret void +} + +!0 = !{!"Simple C/C++ TBAA"} +!1 = !{!"omnipotent char", !0, i64 0} +!2 = !{!1, !1, i64 0, double 1.0} + +; // ----- + +; CHECK: error: unsupported TBAA node format: !0 = !{!1, !1, i64 0, i64 4} +define dso_local void @tbaa(ptr %0) { + store i32 1, ptr %0, align 4, !tbaa !2 + ret void +} + +!2 = !{!3, !3, i64 0, i64 4} +!3 = !{!4, i64 4, !"int"} +!4 = !{!5, i64 1, !"omnipotent char"} +!5 = !{!"Simple C++ TBAA"} diff --git a/mlir/test/Target/LLVMIR/Import/tbaa.ll b/mlir/test/Target/LLVMIR/Import/tbaa.ll new file mode 100644 --- /dev/null +++ b/mlir/test/Target/LLVMIR/Import/tbaa.ll @@ -0,0 +1,69 @@ +; RUN: mlir-translate -import-llvm -split-input-file %s | FileCheck %s + +// ----- + +; CHECK-LABEL: llvm.metadata @__tbaa { +; CHECK-NEXT: llvm.tbaa_root @[[R0:tbaa_root_[0-9]+]] {id = "Simple C/C++ TBAA"} +; CHECK-NEXT: llvm.tbaa_tag @[[T0:tbaa_tag_[0-9]+]] {access_type = @[[R0]], base_type = @[[R0]], offset = 0 : i64} +; CHECK-NEXT: llvm.tbaa_root @[[R1:tbaa_root_[0-9]+]] {id = "Other language TBAA"} +; CHECK-NEXT: llvm.tbaa_tag @[[T1:tbaa_tag_[0-9]+]] {access_type = @[[R1]], base_type = @[[R1]], offset = 0 : i64} +; CHECK-NEXT: llvm.return +; CHECK-NEXT: } +; CHECK: llvm.func @tbaa1 +; CHECK: llvm.store %{{.*}}, %{{.*}} { +; CHECK-SAME: tbaa = [@__tbaa::@[[T0]]] +; CHECK-SAME: } : i8, !llvm.ptr +; CHECK: llvm.store %{{.*}}, %{{.*}} { +; CHECK-SAME: tbaa = [@__tbaa::@[[T1]]] +; CHECK-SAME: } : i8, !llvm.ptr +define dso_local void @tbaa1(ptr %0, ptr %1) { + store i8 1, ptr %0, align 4, !tbaa !1 + store i8 1, ptr %1, align 4, !tbaa !3 + ret void +} + +!0 = !{!"Simple C/C++ TBAA"} +!1 = !{!0, !0, i64 0} +!2 = !{!"Other language TBAA"} +!3 = !{!2, !2, i64 0} + +// ----- + +; CHECK-LABEL: llvm.metadata @__tbaa { +; CHECK-NEXT: llvm.tbaa_root @[[R0:tbaa_root_[0-9]+]] {id = "Simple C/C++ TBAA"} +; CHECK-NEXT: llvm.tbaa_tag @[[T0:tbaa_tag_[0-9]+]] {access_type = @[[D1:tbaa_type_desc_[0-9]+]], base_type = @[[D2:tbaa_type_desc_[0-9]+]], offset = 8 : i64} +; CHECK-NEXT: llvm.tbaa_type_desc @[[D1]] {id = "long long", members = {<@[[D0:tbaa_type_desc_[0-9]+]], 0>}} +; CHECK-NEXT: llvm.tbaa_type_desc @[[D0]] {id = "omnipotent char", members = {<@[[R0]], 0>}} +; CHECK-NEXT: llvm.tbaa_type_desc @[[D2]] {id = "agg2_t", members = {<@[[D1]], 0>, <@[[D1]], 8>}} +; CHECK-NEXT: llvm.tbaa_tag @[[T1:tbaa_tag_[0-9]+]] {access_type = @[[D3:tbaa_type_desc_[0-9]+]], base_type = @[[D4:tbaa_type_desc_[0-9]+]], offset = 0 : i64} +; CHECK-NEXT: llvm.tbaa_type_desc @[[D3]] {id = "int", members = {<@[[D0]], 0>}} +; CHECK-NEXT: llvm.tbaa_type_desc @[[D4]] {id = "agg1_t", members = {<@[[D3]], 0>, <@[[D3]], 4>}} +; CHECK-NEXT: llvm.return +; CHECK-NEXT: } +; CHECK: llvm.func @tbaa2 +; CHECK: llvm.load %{{.*}} { +; CHECK-SAME: tbaa = [@__tbaa::@[[T0]]] +; CHECK-SAME: } : !llvm.ptr -> i64 +; CHECK: llvm.store %{{.*}}, %{{.*}} { +; CHECK-SAME: tbaa = [@__tbaa::@[[T1]]] +; CHECK-SAME: } : i32, !llvm.ptr +%struct.agg2_t = type { i64, i64 } +%struct.agg1_t = type { i32, i32 } + +define dso_local void @tbaa2(ptr %0, ptr %1) { + %3 = getelementptr inbounds %struct.agg2_t, ptr %1, i32 0, i32 1 + %4 = load i64, ptr %3, align 8, !tbaa !6 + %5 = trunc i64 %4 to i32 + %6 = getelementptr inbounds %struct.agg1_t, ptr %0, i32 0, i32 0 + store i32 %5, ptr %6, align 4, !tbaa !11 + ret void +} + +!6 = !{!7, !8, i64 8} +!7 = !{!"agg2_t", !8, i64 0, !8, i64 8} +!8 = !{!"long long", !9, i64 0} +!9 = !{!"omnipotent char", !10, i64 0} +!10 = !{!"Simple C/C++ TBAA"} +!11 = !{!12, !13, i64 0} +!12 = !{!"agg1_t", !13, i64 0, !13, i64 4} +!13 = !{!"int", !9, i64 0} diff --git a/mlir/test/Target/LLVMIR/tbaa.mlir b/mlir/test/Target/LLVMIR/tbaa.mlir new file mode 100644 --- /dev/null +++ b/mlir/test/Target/LLVMIR/tbaa.mlir @@ -0,0 +1,36 @@ +// RUN: mlir-translate -mlir-to-llvmir -split-input-file %s | FileCheck %s + +module { + llvm.metadata @__tbaa { + llvm.tbaa_root @tbaa_root_0 {id = "Simple C/C++ TBAA"} + llvm.tbaa_type_desc @tbaa_type_desc_1 {id = "omnipotent char", members = {<@tbaa_root_0, 0>}} + llvm.tbaa_type_desc @tbaa_type_desc_2 {id = "long long", members = {<@tbaa_type_desc_1, 0>}} + llvm.tbaa_type_desc @tbaa_type_desc_3 {id = "agg2_t", members = {<@tbaa_type_desc_2, 0>, <@tbaa_type_desc_2, 8>}} + llvm.tbaa_tag @tbaa_tag_4 {access_type = @tbaa_type_desc_2, base_type = @tbaa_type_desc_3, offset = 8 : i64} + llvm.tbaa_type_desc @tbaa_type_desc_5 {id = "int", members = {<@tbaa_type_desc_1, 0>}} + llvm.tbaa_type_desc @tbaa_type_desc_6 {id = "agg1_t", members = {<@tbaa_type_desc_5, 0>, <@tbaa_type_desc_5, 4>}} + llvm.tbaa_tag @tbaa_tag_7 {access_type = @tbaa_type_desc_5, base_type = @tbaa_type_desc_6, offset = 0 : i64, constant} + llvm.return + } + llvm.func @tbaa2(%arg0: !llvm.ptr, %arg1: !llvm.ptr) { + %0 = llvm.mlir.constant(0 : i32) : i32 + %1 = llvm.mlir.constant(1 : i32) : i32 + %2 = llvm.getelementptr inbounds %arg1[%0, 1] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"struct.agg2_t", (i64, i64)> + // CHECK: load i64, ptr %{{.*}},{{.*}}!tbaa ![[LTAG:[0-9]*]] + %3 = llvm.load %2 {llvm.tbaa = [@__tbaa::@tbaa_tag_4]} : !llvm.ptr -> i64 + %4 = llvm.trunc %3 : i64 to i32 + %5 = llvm.getelementptr inbounds %arg0[%0, 0] : (!llvm.ptr, i32) -> !llvm.ptr, !llvm.struct<"struct.agg1_t", (i32, i32)> + // CHECK: store i32 %{{.*}}, ptr %{{.*}},{{.*}}!tbaa ![[STAG:[0-9]*]] + llvm.store %4, %5 {llvm.tbaa = [@__tbaa::@tbaa_tag_7]} : i32, !llvm.ptr + llvm.return + } +} + +// CHECK-DAG: ![[LTAG]] = !{![[AGG2T:[0-9]*]], ![[I64T:[0-9]*]], i64 8} +// CHECK-DAG: ![[AGG2T]] = !{!"agg2_t", ![[I64T]], i64 0, ![[I64T]], i64 8} +// CHECK-DAG: ![[I64T]] = !{!"long long", ![[CHART:[0-9]*]], i64 0} +// CHECK-DAG: ![[CHART]] = !{!"omnipotent char", ![[ROOT:[0-9]*]], i64 0} +// CHECK-DAG: ![[ROOT]] = !{!"Simple C/C++ TBAA"} +// CHECK-DAG: ![[STAG]] = !{![[AGG1T:[0-9]*]], ![[I32T:[0-9]*]], i64 0, i64 1} +// CHECK-DAG: ![[AGG1T]] = !{!"agg1_t", ![[I32T]], i64 0, ![[I32T]], i64 4} +// CHECK-DAG: ![[I32T]] = !{!"int", ![[CHART]], i64 0}