[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
This commit is contained in:
parent
1195278c1b
commit
b422cb120c
@ -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<AnyAttr>:$value,
|
||||
DefaultValuedAttr<NonNegativeI32Attr, "0">:$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<LLVMType>();
|
||||
}
|
||||
/// 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);";
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
// <operation> ::= `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<StringAttr>()) {
|
||||
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<ReturnOp>(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();
|
||||
}
|
||||
|
||||
|
@ -212,8 +212,6 @@ Attribute Importer::getConstantAsAttr(llvm::Constant *value) {
|
||||
if (auto *c = dyn_cast<llvm::ConstantDataArray>(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<GlobalOp>(
|
||||
UnknownLoc::get(context), processType(GV->getValueType()),
|
||||
GV->isConstant(), GV->getName(), valueAttr);
|
||||
GlobalOp op = b.create<GlobalOp>(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<ReturnOp>(op.getLoc(), ArrayRef<Value *>({v}));
|
||||
}
|
||||
return globals[GV] = op;
|
||||
}
|
||||
|
||||
Value *Importer::processConstant(llvm::Constant *c) {
|
||||
if (isa<llvm::ConstantInt>(c) || isa<llvm::ConstantDataArray>(c)) {
|
||||
if (Attribute attr = getConstantAsAttr(c)) {
|
||||
// These constants can be represented as attributes.
|
||||
OpBuilder b(currentEntryBlock, currentEntryBlock->begin());
|
||||
return instMap[c] = b.create<ConstantOp>(
|
||||
unknownLoc, processType(c->getType()), getConstantAsAttr(c));
|
||||
return instMap[c] = b.create<ConstantOp>(unknownLoc,
|
||||
processType(c->getType()), attr);
|
||||
}
|
||||
if (auto *cn = dyn_cast<llvm::ConstantPointerNull>(c)) {
|
||||
OpBuilder b(currentEntryBlock, currentEntryBlock->begin());
|
||||
|
@ -283,17 +283,29 @@ LogicalResult ModuleTranslation::convertBlock(Block &bb, bool ignoreArguments) {
|
||||
// definitions.
|
||||
void ModuleTranslation::convertGlobals() {
|
||||
for (auto op : mlirModule.getOps<LLVM::GlobalOp>()) {
|
||||
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<StringAttr>()) {
|
||||
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<StringAttr>()) {
|
||||
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<llvm::Constant>(valueMapping.lookup(op.getResult(0)))) {
|
||||
emitError(op.getLoc(), "unemittable constant value");
|
||||
return;
|
||||
}
|
||||
}
|
||||
ReturnOp ret = cast<ReturnOp>(initializer->getTerminator());
|
||||
cst = cast<llvm::Constant>(valueMapping.lookup(ret.getOperand(0)));
|
||||
}
|
||||
|
||||
auto addrSpace = op.addr_space().getLimitedValue();
|
||||
|
Loading…
x
Reference in New Issue
Block a user