From b422cb120cba049822c873225c96d237797a4851 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Tue, 5 Nov 2019 15:10:28 -0800 Subject: [PATCH] [llvm] Allow GlobalOp to take a region for complex initializers This allows GlobalOp to either take a value attribute (for simple constants) or a region that can contain IR instructions (that must be constant-foldable) to create a ConstantExpr initializer. Example: // A complex initializer is constructed with an initializer region. llvm.mlir.global constant @int_gep() : !llvm<"i32*"> { %0 = llvm.mlir.addressof @g2 : !llvm<"i32*"> %1 = llvm.mlir.constant(2 : i32) : !llvm.i32 %2 = llvm.getelementptr %0[%1] : (!llvm<"i32*">, !llvm.i32) -> !llvm<"i32*"> llvm.return %2 : !llvm<"i32*"> } PiperOrigin-RevId: 278717836 Change-Id: I54ed196a361dd2ca4c564570d0ca9ed12e2f1f95 --- .../include/mlir/Dialect/LLVMIR/LLVMOps.td | 34 +++++++++++++++++-- .../lib/Dialect/LLVMIR/IR/LLVMDialect.cpp | 24 ++++++++++++- .../lib/Target/LLVMIR/ConvertFromLLVMIR.cpp | 22 +++++++----- .../lib/Target/LLVMIR/ModuleTranslation.cpp | 34 +++++++++++++------ 4 files changed, 92 insertions(+), 22 deletions(-) diff --git a/third_party/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/third_party/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td index 9bce041e7c5..13334cb4cb2 100644 --- a/third_party/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td +++ b/third_party/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td @@ -495,10 +495,28 @@ def LLVM_AddressOfOp } def LLVM_GlobalOp - : LLVM_ZeroResultOp<"mlir.global", [Symbol]>, + : LLVM_ZeroResultOp<"mlir.global", + [IsolatedFromAbove, + SingleBlockImplicitTerminator<"ReturnOp">, Symbol]>, Arguments<(ins TypeAttr:$type, UnitAttr:$constant, StrAttr:$sym_name, OptionalAttr:$value, DefaultValuedAttr:$addr_space)> { + let summary = "LLVM dialect global."; + let description = [{ + Can contain an optional initializer region or attribute for simple + initializers. + + Examples: + // Initialized using an attribute. + llvm.mlir.global @a("abc") : !llvm<"[3 x i8]"> + // Initialized using a region. + llvm.mlir.global constant @b() : !llvm<"i32*"> { + %0 = llvm.constant(0 : i32) : !llvm.i32 + %1 = llvm.inttoptr %0 : !llvm.i32 to !llvm<"i32*"> + llvm.return %1 : !llvm<"i32*"> + } + }]; + let regions = (region AnyRegion:$initializer); let builders = [ OpBuilder<"Builder *builder, OperationState &result, LLVMType type, " @@ -511,10 +529,22 @@ def LLVM_GlobalOp LLVMType getType() { return type().cast(); } - /// Return the value attribute if it exists, or a null attribute. + /// Return the initializer attribute if it exists, or a null attribute. Attribute getValueOrNull() { return value().getValueOr(Attribute()); } + /// Return the initializer region. This may be empty, but if it is not it + /// terminates in an `llvm.return` op with the initializer value. + Region &getInitializerRegion() { + return getOperation()->getRegion(0); + } + /// Return the initializer block. If the initializer region is empty this + /// is nullptr. If it is not nullptr, it terminates with an `llvm.return` + /// op with the initializer value. + Block *getInitializerBlock() { + return getInitializerRegion().empty() ? + nullptr : &getInitializerRegion().front(); + } }]; let printer = "printGlobalOp(p, *this);"; diff --git a/third_party/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/third_party/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp index 10e82ec9826..7fcf460a61f 100644 --- a/third_party/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp +++ b/third_party/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp @@ -875,6 +875,7 @@ void GlobalOp::build(Builder *builder, OperationState &result, LLVMType type, if (value) result.addAttribute("value", value); result.attributes.append(attrs.begin(), attrs.end()); + result.addRegion(); } static void printGlobalOp(OpAsmPrinter &p, GlobalOp op) { @@ -894,10 +895,14 @@ static void printGlobalOp(OpAsmPrinter &p, GlobalOp op) { return; p << " : "; p.printType(op.type()); + + Region &initializer = op.getInitializerRegion(); + if (!initializer.empty()) + p.printRegion(initializer, /*printEntryBlockArgs=*/false); } // ::= `llvm.mlir.global` `constant`? `@` identifier -// `(` attribute? `)` attribute-list? (`:` type)? +// `(` attribute? `)` 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]. @@ -926,6 +931,7 @@ static ParseResult parseGlobalOp(OpAsmParser &parser, OperationState &result) { if (types.size() > 1) return parser.emitError(parser.getNameLoc(), "expected zero or one type"); + Region &initRegion = *result.addRegion(); if (types.empty()) { if (auto strAttr = value.dyn_cast_or_null()) { MLIRContext *context = parser.getBuilder().getContext(); @@ -937,6 +943,9 @@ static ParseResult parseGlobalOp(OpAsmParser &parser, OperationState &result) { return parser.emitError(parser.getNameLoc(), "type can only be omitted for string globals"); } + } else if (parser.parseOptionalRegion(initRegion, /*arguments=*/{}, + /*argTypes=*/{})) { + return failure(); } result.addAttribute("type", TypeAttr::get(types[0])); @@ -959,6 +968,19 @@ static LogicalResult verify(GlobalOp op) { "requires an i8 array type of the length equal to that of the string " "attribute"); } + + if (Block *b = op.getInitializerBlock()) { + ReturnOp ret = cast(b->getTerminator()); + if (ret.operand_type_begin() == ret.operand_type_end()) + return op.emitOpError("initializer region cannot return void"); + if (*ret.operand_type_begin() != op.getType()) + return op.emitOpError("initializer region type ") + << *ret.operand_type_begin() << " does not match global type " + << op.getType(); + + if (op.getValueOrNull()) + return op.emitOpError("cannot have both initializer value and region"); + } return success(); } diff --git a/third_party/mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp b/third_party/mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp index ebcb285e156..47b92b9e884 100644 --- a/third_party/mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp +++ b/third_party/mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp @@ -212,8 +212,6 @@ Attribute Importer::getConstantAsAttr(llvm::Constant *value) { if (auto *c = dyn_cast(value)) if (c->isString()) return b.getStringAttr(c->getAsString()); - emitError(unknownLoc) << "unhandled constant type for attribute: " - << diag(*value); return Attribute(); } @@ -226,17 +224,25 @@ GlobalOp Importer::processGlobal(llvm::GlobalVariable *GV) { Attribute valueAttr; if (GV->hasInitializer()) valueAttr = getConstantAsAttr(GV->getInitializer()); - return globals[GV] = b.create( - UnknownLoc::get(context), processType(GV->getValueType()), - GV->isConstant(), GV->getName(), valueAttr); + GlobalOp op = b.create(UnknownLoc::get(context), + processType(GV->getValueType()), + GV->isConstant(), GV->getName(), valueAttr); + if (GV->hasInitializer() && !valueAttr) { + Region &r = op.getInitializerRegion(); + currentEntryBlock = b.createBlock(&r); + b.setInsertionPoint(currentEntryBlock, currentEntryBlock->begin()); + Value *v = processConstant(GV->getInitializer()); + b.create(op.getLoc(), ArrayRef({v})); + } + return globals[GV] = op; } Value *Importer::processConstant(llvm::Constant *c) { - if (isa(c) || isa(c)) { + if (Attribute attr = getConstantAsAttr(c)) { // These constants can be represented as attributes. OpBuilder b(currentEntryBlock, currentEntryBlock->begin()); - return instMap[c] = b.create( - unknownLoc, processType(c->getType()), getConstantAsAttr(c)); + return instMap[c] = b.create(unknownLoc, + processType(c->getType()), attr); } if (auto *cn = dyn_cast(c)) { OpBuilder b(currentEntryBlock, currentEntryBlock->begin()); diff --git a/third_party/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/third_party/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp index 9b020e6401f..69f7e933d49 100644 --- a/third_party/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp +++ b/third_party/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp @@ -283,17 +283,29 @@ LogicalResult ModuleTranslation::convertBlock(Block &bb, bool ignoreArguments) { // definitions. void ModuleTranslation::convertGlobals() { for (auto op : mlirModule.getOps()) { - llvm::Constant *cst; - llvm::Type *type; - // String attributes are treated separately because they cannot appear as - // in-function constants and are thus not supported by getLLVMConstant. - if (auto strAttr = op.getValueOrNull().dyn_cast_or_null()) { - cst = llvm::ConstantDataArray::getString( - llvmModule->getContext(), strAttr.getValue(), /*AddNull=*/false); - type = cst->getType(); - } else { - type = op.getType().getUnderlyingType(); - cst = getLLVMConstant(type, op.getValueOrNull(), op.getLoc()); + llvm::Type *type = op.getType().getUnderlyingType(); + llvm::Constant *cst = llvm::UndefValue::get(type); + if (op.getValueOrNull()) { + // String attributes are treated separately because they cannot appear as + // in-function constants and are thus not supported by getLLVMConstant. + if (auto strAttr = op.getValueOrNull().dyn_cast_or_null()) { + cst = llvm::ConstantDataArray::getString( + llvmModule->getContext(), strAttr.getValue(), /*AddNull=*/false); + type = cst->getType(); + } else { + cst = getLLVMConstant(type, op.getValueOrNull(), op.getLoc()); + } + } else if (Block *initializer = op.getInitializerBlock()) { + llvm::IRBuilder<> builder(llvmModule->getContext()); + for (auto &op : initializer->without_terminator()) { + if (failed(convertOperation(op, builder)) || + !isa(valueMapping.lookup(op.getResult(0)))) { + emitError(op.getLoc(), "unemittable constant value"); + return; + } + } + ReturnOp ret = cast(initializer->getTerminator()); + cst = cast(valueMapping.lookup(ret.getOperand(0))); } auto addrSpace = op.addr_space().getLimitedValue();