LLVM Dialect: introduce llvm.global
authorAlex Zinenko <zinenko@google.com>
Fri, 9 Aug 2019 12:01:23 +0000 (05:01 -0700)
committerA. Unique TensorFlower <gardener@tensorflow.org>
Fri, 9 Aug 2019 12:01:52 +0000 (05:01 -0700)
Introduce an operation that defines global constants and variables in the LLVM
dialect, to reflect the corresponding LLVM IR capability. This operation is
expected to live in the top-level module and behaves similarly to
llvm.constant.  It currently does not model many of the attributes supported by
the LLVM IR for global values (memory space, alignment, thread-local, linkage)
and will be extended as the relevant use cases appear.

PiperOrigin-RevId: 262539445

mlir/g3doc/Dialects/LLVM.md
mlir/include/mlir/IR/OpImplementation.h
mlir/include/mlir/LLVMIR/LLVMOps.td
mlir/lib/LLVMIR/IR/LLVMDialect.cpp
mlir/lib/Parser/Parser.cpp
mlir/test/LLVMIR/global.mlir [new file with mode: 0644]

index 03d80bd..566e506 100644 (file)
@@ -283,6 +283,27 @@ Examples:
 %3 = llvm.constant(splat<vector<4xf32>, 1.0>) : !llvm<"<4 x float>">
 ```
 
+#### `llvm.global`
+
+Since MLIR allows for arbitrary operations to be present at the top level,
+global variables are defined using the `llvm.global` operation. Both global
+constants and variables can be defined, and the value must be initialized in
+both cases. The initialization and type syntax is similar to `llvm.constant` and
+may use two types: one for MLIR attribute and another for the LLVM value. These
+types must be compatible. `llvm.global` must appear at top-level of the
+enclosing module. It uses an @-identifier for its value, which will be uniqued
+by the module with respect to other @-identifiers in it.
+
+Examples:
+
+```mlir {.mlir}
+// Global values use @-identifiers.
+llvm.global constant @cst(42 : i32) : !llvm.i32
+
+// Non-constant values must also be initialized.
+llvm.global @variable(32.0 : f32) : !llvm.float
+```
+
 #### `llvm.undef`
 
 Unlike LLVM IR, MLIR does not have first-class undefined values. Such values
index 23a0cd1..49a5314 100644 (file)
@@ -305,6 +305,14 @@ public:
   parseOptionalAttributeDict(SmallVectorImpl<NamedAttribute> &result) = 0;
 
   //===--------------------------------------------------------------------===//
+  // Identifier Parsing
+  //===--------------------------------------------------------------------===//
+
+  virtual ParseResult
+  parseSymbolName(StringAttr &result, StringRef attrName,
+                  SmallVectorImpl<NamedAttribute> &attrs) = 0;
+
+  //===--------------------------------------------------------------------===//
   // Operand Parsing
   //===--------------------------------------------------------------------===//
 
index 129c329..32b17d0 100644 (file)
@@ -352,6 +352,29 @@ def LLVM_UnreachableOp : LLVM_TerminatorOp<"unreachable", []> {
 
 // Pseudo-operations (do not appear in LLVM IR but necessary for the dialect to
 // work correctly).
+def LLVM_GlobalOp
+    : LLVM_ZeroResultOp<"global">,
+      Arguments<(ins TypeAttr:$type, UnitAttr:$constant, StrAttr:$sym_name,
+                 AnyAttr:$value)> {
+
+  let builders = [
+    OpBuilder<"Builder *builder, OperationState *result, LLVMType type, "
+              "bool isConstant, StringRef name, Attribute value, "
+              "ArrayRef<NamedAttribute> attrs = {}">
+  ];
+
+  let extraClassDeclaration = [{
+    /// Return the LLVM type of the global.
+    LLVMType getType() {
+      return type().cast<LLVMType>();
+    }
+  }];
+
+  let printer = "printGlobalOp(p, *this);";
+  let parser = "return parseGlobalOp(parser, result);";
+  let verifier = "return ::verify(*this);";
+}
+
 def LLVM_LLVMFuncOp : LLVM_ZeroResultOp<"func",
       [NativeOpTrait<"IsIsolatedFromAbove">, NativeOpTrait<"FunctionLike">]> {
   let summary = "LLVM dialect function, has wrapped LLVM IR function type";
index 2469f9c..30c9eb5 100644 (file)
@@ -724,6 +724,66 @@ static ParseResult parseConstantOp(OpAsmParser *parser,
 }
 
 //===----------------------------------------------------------------------===//
+// Builder, printer and verifier for LLVM::GlobalOp.
+//===----------------------------------------------------------------------===//
+
+void GlobalOp::build(Builder *builder, OperationState *result, LLVMType type,
+                     bool isConstant, StringRef name, Attribute value,
+                     ArrayRef<NamedAttribute> attrs) {
+  result->addAttribute(SymbolTable::getSymbolAttrName(),
+                       builder->getStringAttr(name));
+  result->addAttribute("type", builder->getTypeAttr(type));
+  if (isConstant)
+    result->addAttribute("constant", builder->getUnitAttr());
+  result->addAttribute("value", value);
+  result->attributes.append(attrs.begin(), attrs.end());
+}
+
+static void printGlobalOp(OpAsmPrinter *p, GlobalOp op) {
+  *p << op.getOperationName() << ' ';
+  if (op.constant())
+    *p << "constant ";
+  *p << '@' << op.sym_name() << '(';
+  p->printAttribute(op.value());
+  *p << ')';
+  p->printOptionalAttrDict(op.getAttrs(), {SymbolTable::getSymbolAttrName(),
+                                           "type", "constant", "value"});
+  *p << " : ";
+  p->printType(op.type());
+}
+
+// <operation> ::= `llvm.global` `constant`? `@` identifier `(` attribute `)`
+//                  attribute-list? : type
+static ParseResult parseGlobalOp(OpAsmParser *parser, OperationState *result) {
+  if (succeeded(parser->parseOptionalKeyword("constant")))
+    result->addAttribute("constant", parser->getBuilder().getUnitAttr());
+
+  Attribute value;
+  StringAttr name;
+  Type type;
+  if (parser->parseSymbolName(name, SymbolTable::getSymbolAttrName(),
+                              result->attributes) ||
+      parser->parseLParen() ||
+      parser->parseAttribute(value, "value", result->attributes) ||
+      parser->parseRParen() ||
+      parser->parseOptionalAttributeDict(result->attributes) ||
+      parser->parseColonType(type))
+    return failure();
+
+  result->addAttribute("type", parser->getBuilder().getTypeAttr(type));
+  return success();
+}
+
+static LogicalResult verify(GlobalOp op) {
+  if (!llvm::PointerType::isValidElementType(op.getType().getUnderlyingType()))
+    return op.emitOpError(
+        "expects type to be a valid element type for an LLVM pointer");
+  if (op.getParentOp() && !isa<ModuleOp>(op.getParentOp()))
+    return op.emitOpError("must appear at the module level");
+  return success();
+}
+
+//===----------------------------------------------------------------------===//
 // Builder, printer and verifier for LLVM::LLVMFuncOp.
 //===----------------------------------------------------------------------===//
 
index 14280bb..09f3052 100644 (file)
@@ -3350,6 +3350,22 @@ public:
   }
 
   //===--------------------------------------------------------------------===//
+  // Identifier Parsing
+  //===--------------------------------------------------------------------===//
+
+  /// Parse an @-identifier and store it (without the '@' symbol) in a string
+  /// attribute named 'attrName'.
+  ParseResult parseSymbolName(StringAttr &result, StringRef attrName,
+                              SmallVectorImpl<NamedAttribute> &attrs) override {
+    if (parser.getToken().isNot(Token::at_identifier))
+      return failure();
+    result = getBuilder().getStringAttr(parser.getTokenSpelling().drop_front());
+    attrs.push_back(getBuilder().getNamedAttr(attrName, result));
+    parser.consumeToken();
+    return success();
+  }
+
+  //===--------------------------------------------------------------------===//
   // Operand Parsing
   //===--------------------------------------------------------------------===//
 
diff --git a/mlir/test/LLVMIR/global.mlir b/mlir/test/LLVMIR/global.mlir
new file mode 100644 (file)
index 0000000..69e5570
--- /dev/null
@@ -0,0 +1,37 @@
+// RUN: mlir-opt -split-input-file -verify-diagnostics %s | FileCheck %s
+
+// CHECK: llvm.global @global(42 : i64) : !llvm.i64
+llvm.global @global(42 : i64) : !llvm.i64
+
+// CHECK: llvm.global constant @constant(3.700000e+01 : f64) : !llvm.float
+llvm.global constant @constant(37.0) : !llvm.float
+
+// CHECK: llvm.global constant @string("foobar") : !llvm<"[6 x i8]">
+llvm.global constant @string("foobar") : !llvm<"[6 x i8]">
+
+// -----
+
+// expected-error @+1 {{op requires attribute 'sym_name'}}
+"llvm.global"() {type = !llvm.i64, constant, value = 42 : i64} : () -> ()
+
+// -----
+
+// expected-error @+1 {{op requires attribute 'type'}}
+"llvm.global"() {sym_name = "foo", constant, value = 42 : i64} : () -> ()
+
+// -----
+
+// expected-error @+1 {{op requires attribute 'value'}}
+"llvm.global"() {sym_name = "foo", type = !llvm.i64, constant} : () -> ()
+
+// -----
+
+// expected-error @+1 {{expects type to be a valid element type for an LLVM pointer}}
+llvm.global constant @constant(37.0) : !llvm<"label">
+
+// -----
+
+func @foo() {
+  // expected-error @+1 {{must appear at the module level}}
+  llvm.global @bar(42) : !llvm.i32
+}