Introduce a new Location abstraction to represent location data in a structured
(and more useful) way rather than hacking up a pile of attributes for it.  In
the future this will grow to represent inlined locations, fusion cases etc, but
for now we start with simple Unknown and File/Line/Col locations.  NFC.

PiperOrigin-RevId: 210485775
diff --git a/include/mlir/IR/Builders.h b/include/mlir/IR/Builders.h
index 3cc8227..a545fe1 100644
--- a/include/mlir/IR/Builders.h
+++ b/include/mlir/IR/Builders.h
@@ -18,7 +18,6 @@
 #ifndef MLIR_IR_BUILDERS_H
 #define MLIR_IR_BUILDERS_H
 
-#include "mlir/IR/Attributes.h"
 #include "mlir/IR/CFGFunction.h"
 #include "mlir/IR/MLFunction.h"
 #include "mlir/IR/Statements.h"
@@ -26,6 +25,9 @@
 namespace mlir {
 class MLIRContext;
 class Module;
+class UnknownLoc;
+class UniquedFilename;
+class FileLineColLoc;
 class Type;
 class PrimitiveType;
 class IntegerType;
@@ -59,6 +61,12 @@
   Identifier getIdentifier(StringRef str);
   Module *createModule();
 
+  // Locations.
+  UnknownLoc *getUnknownLoc();
+  UniquedFilename getUniquedFilename(StringRef filename);
+  FileLineColLoc *getFileLineColLoc(UniquedFilename filename, unsigned line,
+                                    unsigned column);
+
   // Types.
   FloatType *getBF16Type();
   FloatType *getF16Type();
@@ -185,7 +193,7 @@
 
   /// Create operation of specific op type at the current insertion point.
   template <typename OpTy, typename... Args>
-  OpPointer<OpTy> create(Attribute *location, Args... args) {
+  OpPointer<OpTy> create(Location *location, Args... args) {
     OperationState state(getContext(), location, OpTy::getOperationName());
     OpTy::build(this, &state, args...);
     auto *inst = createOperation(state);
@@ -202,16 +210,16 @@
 
   // Terminators.
 
-  ReturnInst *createReturn(Attribute *location, ArrayRef<CFGValue *> operands) {
+  ReturnInst *createReturn(Location *location, ArrayRef<CFGValue *> operands) {
     return insertTerminator(ReturnInst::create(location, operands));
   }
 
-  BranchInst *createBranch(Attribute *location, BasicBlock *dest,
+  BranchInst *createBranch(Location *location, BasicBlock *dest,
                            ArrayRef<CFGValue *> operands = {}) {
     return insertTerminator(BranchInst::create(location, dest, operands));
   }
 
-  CondBranchInst *createCondBranch(Attribute *location, CFGValue *condition,
+  CondBranchInst *createCondBranch(Location *location, CFGValue *condition,
                                    BasicBlock *trueDest,
                                    BasicBlock *falseDest) {
     return insertTerminator(
@@ -290,7 +298,7 @@
 
   /// Create operation of specific op type at the current insertion point.
   template <typename OpTy, typename... Args>
-  OpPointer<OpTy> create(Attribute *location, Args... args) {
+  OpPointer<OpTy> create(Location *location, Args... args) {
     OperationState state(getContext(), location, OpTy::getOperationName());
     OpTy::build(this, &state, args...);
     auto *stmt = createOperation(state);
@@ -311,15 +319,12 @@
     return cloneStmt;
   }
 
-  /// Create a 'for' statement with bounds that may involve MLValue operands.
-  /// When step is not specified, it is set to 1.
-  ForStmt *createFor(Attribute *location, ArrayRef<MLValue *> lbOperands,
+  // Creates for statement. When step is not specified, it is set to 1.
+  ForStmt *createFor(Location *location, ArrayRef<MLValue *> lbOperands,
                      AffineMap *lbMap, ArrayRef<MLValue *> ubOperands,
                      AffineMap *ubMap, int64_t step = 1);
 
-  /// Create if statement.
-  /// TODO: pass operands.
-  IfStmt *createIf(Attribute *location, IntegerSet *condition);
+  IfStmt *createIf(Location *location, IntegerSet *condition);
 
 private:
   StmtBlock *block = nullptr;
diff --git a/include/mlir/IR/Instructions.h b/include/mlir/IR/Instructions.h
index ad0cb05..1f75241 100644
--- a/include/mlir/IR/Instructions.h
+++ b/include/mlir/IR/Instructions.h
@@ -49,9 +49,8 @@
   /// Return the context this operation is associated with.
   MLIRContext *getContext() const;
 
-  /// The source location the operation was defined or derived from.  Note that
-  /// it is possible for this pointer to be null.
-  Attribute *getLoc() const { return location; }
+  /// The source location the operation was defined or derived from.
+  Location *getLoc() const { return location; }
 
   /// Return the BasicBlock containing this instruction.
   BasicBlock *getBlock() const { return block; }
@@ -138,8 +137,9 @@
   void emitNote(const Twine &message) const;
 
 protected:
-  Instruction(Kind kind, Attribute *location)
-      : kind(kind), location(location) {}
+  Instruction(Kind kind, Location *location) : kind(kind), location(location) {
+    assert(location && "location can never be null");
+  }
 
   // Instructions are deleted through the destroy() member because this class
   // does not have a virtual destructor.  A vtable would bloat the size of
@@ -153,7 +153,7 @@
 
   /// This holds information about the source location the operation was defined
   /// or derived from.
-  Attribute *location;
+  Location *location;
 
   friend struct llvm::ilist_traits<OperationInst>;
   friend class BasicBlock;
@@ -172,8 +172,8 @@
       public llvm::ilist_node_with_parent<OperationInst, BasicBlock>,
       private llvm::TrailingObjects<OperationInst, InstOperand, InstResult> {
 public:
-  /// Create a new OperationInst with the specific fields.
-  static OperationInst *create(Attribute *location, Identifier name,
+  /// Create a new OperationInst with the specified fields.
+  static OperationInst *create(Location *location, Identifier name,
                                ArrayRef<CFGValue *> operands,
                                ArrayRef<Type *> resultTypes,
                                ArrayRef<NamedAttribute> attributes,
@@ -312,7 +312,7 @@
 private:
   const unsigned numOperands, numResults;
 
-  OperationInst(Attribute *location, Identifier name, unsigned numOperands,
+  OperationInst(Location *location, Identifier name, unsigned numOperands,
                 unsigned numResults, ArrayRef<NamedAttribute> attributes,
                 MLIRContext *context);
   ~OperationInst();
@@ -358,8 +358,7 @@
   }
 
 protected:
-  TerminatorInst(Kind kind, Attribute *location)
-      : Instruction(kind, location) {}
+  TerminatorInst(Kind kind, Location *location) : Instruction(kind, location) {}
   ~TerminatorInst() {}
 };
 
@@ -367,7 +366,7 @@
 /// and may pass basic block arguments to the successor.
 class BranchInst : public TerminatorInst {
 public:
-  static BranchInst *create(Attribute *location, BasicBlock *dest,
+  static BranchInst *create(Location *location, BasicBlock *dest,
                             ArrayRef<CFGValue *> operands = {}) {
     return new BranchInst(location, dest, operands);
   }
@@ -400,9 +399,8 @@
   }
 
 private:
-  explicit BranchInst(Attribute *location, BasicBlock *dest,
+  explicit BranchInst(Location *location, BasicBlock *dest,
                       ArrayRef<CFGValue *> operands);
-
   BasicBlockOperand dest;
   std::vector<InstOperand> operands;
 };
@@ -415,7 +413,7 @@
   enum { trueIndex = 0, falseIndex = 1 };
 
 public:
-  static CondBranchInst *create(Attribute *location, CFGValue *condition,
+  static CondBranchInst *create(Location *location, CFGValue *condition,
                                 BasicBlock *trueDest, BasicBlock *falseDest) {
     return new CondBranchInst(location, condition, trueDest, falseDest);
   }
@@ -561,7 +559,7 @@
   }
 
 private:
-  CondBranchInst(Attribute *location, CFGValue *condition, BasicBlock *trueDest,
+  CondBranchInst(Location *location, CFGValue *condition, BasicBlock *trueDest,
                  BasicBlock *falseDest);
 
   CFGValue *condition;
@@ -581,7 +579,7 @@
       private llvm::TrailingObjects<ReturnInst, InstOperand> {
 public:
   /// Create a new ReturnInst with the specific fields.
-  static ReturnInst *create(Attribute *location, ArrayRef<CFGValue *> operands);
+  static ReturnInst *create(Location *location, ArrayRef<CFGValue *> operands);
 
   unsigned getNumOperands() const { return numOperands; }
 
@@ -606,7 +604,7 @@
     return numOperands;
   }
 
-  ReturnInst(Attribute *location, unsigned numOperands);
+  ReturnInst(Location *location, unsigned numOperands);
   ~ReturnInst();
 
   unsigned numOperands;
diff --git a/include/mlir/IR/Location.h b/include/mlir/IR/Location.h
new file mode 100644
index 0000000..613d0b6
--- /dev/null
+++ b/include/mlir/IR/Location.h
@@ -0,0 +1,137 @@
+//===- Location.h - MLIR Location Classes -----------------------*- C++ -*-===//
+//
+// Copyright 2019 The MLIR Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// =============================================================================
+//
+// These classes provide the ability to relate MLIR objects back to source
+// location position information.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_IR_LOCATION_H
+#define MLIR_IR_LOCATION_H
+
+#include "mlir/Support/LLVM.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace mlir {
+class MLIRContext;
+
+/// Location objects represent source locations information in MLIR.
+class Location {
+public:
+  enum class Kind {
+    /// This represents an unknown location.
+    Unknown,
+
+    /// This represents a file/line/column location.
+    FileLineCol,
+
+    // Represents a location as a 'void*' pointer to a front-end's opaque
+    // location information, which must live longer than the MLIR objects that
+    // refer to it.  OpaqueLocation's are never serialized.
+    //
+    // TODO: OpaqueLocation,
+
+    // Represents a value inlined through a function call.
+    // TODO: InlinedLocation,
+
+    // Represents a value composed of multiple source constructs.
+    // TODO: FusedLocation,
+  };
+
+  /// Return the classification for this location.
+  Kind getKind() const { return kind; }
+
+  /// Print the location.
+  void print(raw_ostream &os) const;
+  void dump() const;
+
+protected:
+  explicit Location(Kind kind) : kind(kind) {}
+  ~Location() {}
+
+private:
+  /// Classification of the subclass, used for type checking.
+  Kind kind : 8;
+
+  Location(const Location &) = delete;
+  void operator=(const Location &) = delete;
+};
+
+inline raw_ostream &operator<<(raw_ostream &os, const Location &loc) {
+  loc.print(os);
+  return os;
+}
+
+/// Represents an unknown location.  This is always a singleton for a given
+/// MLIRContext.
+class UnknownLoc : public Location {
+public:
+  static UnknownLoc *get(MLIRContext *context);
+
+private:
+  explicit UnknownLoc() : Location(Kind::Unknown) {}
+};
+
+/// This class is used to represent a uniqued filename in an MLIRContext.  It is
+/// a simple wrapper around a const char* to uniqued string memory.
+class UniquedFilename {
+public:
+  /// Unique the specified filename and return a stable pointer owned by the
+  /// specified context.  The filename is not allowed to contain embedded ASCII
+  /// nul (\0) characters.
+  static UniquedFilename get(StringRef filename, MLIRContext *context);
+
+  StringRef getRef() const { return string; }
+  const char *data() const { return string; }
+
+private:
+  explicit UniquedFilename(const char *string) : string(string) {}
+  const char *string;
+};
+
+/// Represents a location derived from a file/line/column location.  The column
+/// and line may be zero to represent unknown column and/or unknown line/column
+/// information.
+class FileLineColLoc : public Location {
+public:
+  /// Return a uniqued FileLineCol location object.
+  static FileLineColLoc *get(UniquedFilename filename, unsigned line,
+                             unsigned column, MLIRContext *context);
+
+  StringRef getFilename() const { return filename.getRef(); }
+
+  unsigned getLine() const { return line; }
+  unsigned getColumn() const { return column; }
+
+  /// Methods for support type inquiry through isa, cast, and dyn_cast.
+  static bool classof(const Location *loc) {
+    return loc->getKind() == Kind::FileLineCol;
+  }
+
+private:
+  FileLineColLoc(UniquedFilename filename, unsigned line, unsigned column)
+      : Location(Kind::FileLineCol), filename(filename), line(line),
+        column(column) {}
+  ~FileLineColLoc() = delete;
+
+  const UniquedFilename filename;
+  const unsigned line, column;
+};
+
+} // end namespace mlir
+
+#endif
diff --git a/include/mlir/IR/MLIRContext.h b/include/mlir/IR/MLIRContext.h
index d782be6..0ea78dd 100644
--- a/include/mlir/IR/MLIRContext.h
+++ b/include/mlir/IR/MLIRContext.h
@@ -24,7 +24,7 @@
 
 namespace mlir {
 class MLIRContextImpl;
-class Attribute;
+class Location;
 
 /// MLIRContext is the top-level object for a collection of MLIR modules.  It
 /// holds immortal uniqued objects like types, and the tables used to unique
@@ -58,7 +58,7 @@
   // notes will be dropped and errors will terminate the process with exit(1).
 
   using DiagnosticHandlerTy = std::function<void(
-      Attribute *location, StringRef message, DiagnosticKind kind)>;
+      Location *location, StringRef message, DiagnosticKind kind)>;
 
   /// Register a diagnostic handler with this LLVM context.  The handler is
   /// passed location information if present (nullptr if not) along with a
@@ -71,7 +71,7 @@
   /// This emits an diagnostic using the registered issue handle if present, or
   /// with the default behavior if not.  The MLIR compiler should not generally
   /// interact with this, it should use methods on Operation instead.
-  void emitDiagnostic(Attribute *location, const Twine &message,
+  void emitDiagnostic(Location *location, const Twine &message,
                       DiagnosticKind kind) const;
 
 private:
diff --git a/include/mlir/IR/Operation.h b/include/mlir/IR/Operation.h
index d5b2b25..f5e3b17 100644
--- a/include/mlir/IR/Operation.h
+++ b/include/mlir/IR/Operation.h
@@ -27,6 +27,7 @@
 class Attribute;
 class AttributeListStorage;
 class AbstractOperation;
+class Location;
 template <typename OpType> class ConstOpPointer;
 template <typename OpType> class OpPointer;
 template <typename ObjectType, typename ElementType> class OperandIterator;
@@ -46,7 +47,7 @@
 /// this in a collection.
 struct OperationState {
   MLIRContext *const context;
-  Attribute *location;
+  Location *location;
   Identifier name;
   SmallVector<SSAValue *, 4> operands;
   /// Types of the results of this operation.
@@ -54,14 +55,14 @@
   SmallVector<NamedAttribute, 4> attributes;
 
 public:
-  OperationState(MLIRContext *context, Attribute *location, StringRef name)
+  OperationState(MLIRContext *context, Location *location, StringRef name)
       : context(context), location(location),
         name(Identifier::get(name, context)) {}
 
-  OperationState(MLIRContext *context, Attribute *location, Identifier name)
+  OperationState(MLIRContext *context, Location *location, Identifier name)
       : context(context), location(location), name(name) {}
 
-  OperationState(MLIRContext *context, Attribute *location, StringRef name,
+  OperationState(MLIRContext *context, Location *location, StringRef name,
                  ArrayRef<SSAValue *> operands, ArrayRef<Type *> types,
                  ArrayRef<NamedAttribute> attributes = {})
       : context(context), location(location),
@@ -92,9 +93,8 @@
   /// Return the context this operation is associated with.
   MLIRContext *getContext() const;
 
-  /// The source location the operation was defined or derived from.  Note that
-  /// it is possible for this pointer to be null.
-  Attribute *getLoc() const;
+  /// The source location the operation was defined or derived from.
+  Location *getLoc() const;
 
   /// Return the function this operation is defined in.  This has a verbose
   /// name to avoid name lookup ambiguities.
diff --git a/include/mlir/IR/Statement.h b/include/mlir/IR/Statement.h
index d6d16e3..55a0aec 100644
--- a/include/mlir/IR/Statement.h
+++ b/include/mlir/IR/Statement.h
@@ -29,7 +29,7 @@
 #include "llvm/ADT/ilist_node.h"
 
 namespace mlir {
-class Attribute;
+class Location;
 class MLFunction;
 class StmtBlock;
 class ForStmt;
@@ -52,9 +52,8 @@
   /// Return the context this operation is associated with.
   MLIRContext *getContext() const;
 
-  /// The source location the operation was defined or derived from.  Note that
-  /// it is possible for this pointer to be null.
-  Attribute *getLoc() const { return location; }
+  /// The source location the operation was defined or derived from.
+  Location *getLoc() const { return location; }
 
   /// Remove this statement from its block and delete it.
   void eraseFromBlock();
@@ -156,7 +155,10 @@
   void emitNote(const Twine &message) const;
 
 protected:
-  Statement(Kind kind, Attribute *location) : kind(kind), location(location) {}
+  Statement(Kind kind, Location *location) : kind(kind), location(location) {
+    assert(location && "location should never be null");
+  }
+
   // Statements are deleted through the destroy() member because this class
   // does not have a virtual destructor.
   ~Statement();
@@ -168,7 +170,7 @@
 
   /// This holds information about the source location the operation was defined
   /// or derived from.
-  Attribute *location;
+  Location *location;
 
   // allow ilist_traits access to 'block' field.
   friend struct llvm::ilist_traits<Statement>;
diff --git a/include/mlir/IR/Statements.h b/include/mlir/IR/Statements.h
index 1aa6581..c5af5d5 100644
--- a/include/mlir/IR/Statements.h
+++ b/include/mlir/IR/Statements.h
@@ -40,7 +40,7 @@
       private llvm::TrailingObjects<OperationStmt, StmtOperand, StmtResult> {
 public:
   /// Create a new OperationStmt with the specific fields.
-  static OperationStmt *create(Attribute *location, Identifier name,
+  static OperationStmt *create(Location *location, Identifier name,
                                ArrayRef<MLValue *> operands,
                                ArrayRef<Type *> resultTypes,
                                ArrayRef<NamedAttribute> attributes,
@@ -180,7 +180,7 @@
 private:
   const unsigned numOperands, numResults;
 
-  OperationStmt(Attribute *location, Identifier name, unsigned numOperands,
+  OperationStmt(Location *location, Identifier name, unsigned numOperands,
                 unsigned numResults, ArrayRef<NamedAttribute> attributes,
                 MLIRContext *context);
   ~OperationStmt();
@@ -198,7 +198,7 @@
 /// For statement represents an affine loop nest.
 class ForStmt : public Statement, public MLValue, public StmtBlock {
 public:
-  static ForStmt *create(Attribute *location, ArrayRef<MLValue *> lbOperands,
+  static ForStmt *create(Location *location, ArrayRef<MLValue *> lbOperands,
                          AffineMap *lbMap, ArrayRef<MLValue *> ubOperands,
                          AffineMap *ubMap, int64_t step, MLIRContext *context);
 
@@ -336,7 +336,7 @@
   // Operands for the lower and upper bounds.
   std::vector<StmtOperand> operands;
 
-  explicit ForStmt(Attribute *location, unsigned numOperands, AffineMap *lbMap,
+  explicit ForStmt(Location *location, unsigned numOperands, AffineMap *lbMap,
                    AffineMap *ubMap, int64_t step, MLIRContext *context);
 };
 
@@ -412,7 +412,7 @@
 /// If statement restricts execution to a subset of the loop iteration space.
 class IfStmt : public Statement {
 public:
-  explicit IfStmt(Attribute *location, IntegerSet *condition);
+  explicit IfStmt(Location *location, IntegerSet *condition);
   ~IfStmt();
 
   IfClause *getThen() const { return thenClause; }
diff --git a/lib/IR/Builders.cpp b/lib/IR/Builders.cpp
index 376d54e..8d0f223 100644
--- a/lib/IR/Builders.cpp
+++ b/lib/IR/Builders.cpp
@@ -20,6 +20,7 @@
 #include "mlir/IR/AffineMap.h"
 #include "mlir/IR/Attributes.h"
 #include "mlir/IR/IntegerSet.h"
+#include "mlir/IR/Location.h"
 #include "mlir/IR/Module.h"
 #include "mlir/IR/Types.h"
 using namespace mlir;
@@ -33,6 +34,21 @@
 Module *Builder::createModule() { return new Module(context); }
 
 //===----------------------------------------------------------------------===//
+// Locations.
+//===----------------------------------------------------------------------===//
+
+UnknownLoc *Builder::getUnknownLoc() { return UnknownLoc::get(context); }
+
+UniquedFilename Builder::getUniquedFilename(StringRef filename) {
+  return UniquedFilename::get(filename, context);
+}
+
+FileLineColLoc *Builder::getFileLineColLoc(UniquedFilename filename,
+                                           unsigned line, unsigned column) {
+  return FileLineColLoc::get(filename, line, column, context);
+}
+
+//===----------------------------------------------------------------------===//
 // Types.
 //===----------------------------------------------------------------------===//
 
@@ -227,7 +243,7 @@
   return op;
 }
 
-ForStmt *MLFuncBuilder::createFor(Attribute *location,
+ForStmt *MLFuncBuilder::createFor(Location *location,
                                   ArrayRef<MLValue *> lbOperands,
                                   AffineMap *lbMap,
                                   ArrayRef<MLValue *> ubOperands,
@@ -238,7 +254,7 @@
   return stmt;
 }
 
-IfStmt *MLFuncBuilder::createIf(Attribute *location, IntegerSet *condition) {
+IfStmt *MLFuncBuilder::createIf(Location *location, IntegerSet *condition) {
   auto *stmt = new IfStmt(location, condition);
   block->getStatements().insert(insertPoint, stmt);
   return stmt;
diff --git a/lib/IR/Instructions.cpp b/lib/IR/Instructions.cpp
index bd48783..90a5016 100644
--- a/lib/IR/Instructions.cpp
+++ b/lib/IR/Instructions.cpp
@@ -146,8 +146,8 @@
 // OperationInst
 //===----------------------------------------------------------------------===//
 
-/// Create a new OperationInst with the specific fields.
-OperationInst *OperationInst::create(Attribute *location, Identifier name,
+/// Create a new OperationInst with the specified fields.
+OperationInst *OperationInst::create(Location *location, Identifier name,
                                      ArrayRef<CFGValue *> operands,
                                      ArrayRef<Type *> resultTypes,
                                      ArrayRef<NamedAttribute> attributes,
@@ -186,7 +186,7 @@
                 getContext());
 }
 
-OperationInst::OperationInst(Attribute *location, Identifier name,
+OperationInst::OperationInst(Location *location, Identifier name,
                              unsigned numOperands, unsigned numResults,
                              ArrayRef<NamedAttribute> attributes,
                              MLIRContext *context)
@@ -281,7 +281,7 @@
 //===----------------------------------------------------------------------===//
 
 /// Create a new OperationInst with the specific fields.
-ReturnInst *ReturnInst::create(Attribute *location,
+ReturnInst *ReturnInst::create(Location *location,
                                ArrayRef<CFGValue *> operands) {
   auto byteSize = totalSizeToAlloc<InstOperand>(operands.size());
   void *rawMem = malloc(byteSize);
@@ -296,7 +296,7 @@
   return inst;
 }
 
-ReturnInst::ReturnInst(Attribute *location, unsigned numOperands)
+ReturnInst::ReturnInst(Location *location, unsigned numOperands)
     : TerminatorInst(Kind::Return, location), numOperands(numOperands) {}
 
 void ReturnInst::destroy() {
@@ -314,7 +314,7 @@
 // BranchInst
 //===----------------------------------------------------------------------===//
 
-BranchInst::BranchInst(Attribute *location, BasicBlock *dest,
+BranchInst::BranchInst(Location *location, BasicBlock *dest,
                        ArrayRef<CFGValue *> operands)
     : TerminatorInst(Kind::Branch, location), dest(this, dest) {
   addOperands(operands);
@@ -338,7 +338,7 @@
 // CondBranchInst
 //===----------------------------------------------------------------------===//
 
-CondBranchInst::CondBranchInst(Attribute *location, CFGValue *condition,
+CondBranchInst::CondBranchInst(Location *location, CFGValue *condition,
                                BasicBlock *trueDest, BasicBlock *falseDest)
     : TerminatorInst(Kind::CondBranch, location),
       condition(condition), dests{{this}, {this}}, numTrueOperands(0) {
diff --git a/lib/IR/MLIRContext.cpp b/lib/IR/MLIRContext.cpp
index 024e1b6..46d0103 100644
--- a/lib/IR/MLIRContext.cpp
+++ b/lib/IR/MLIRContext.cpp
@@ -23,6 +23,7 @@
 #include "mlir/IR/Function.h"
 #include "mlir/IR/Identifier.h"
 #include "mlir/IR/IntegerSet.h"
+#include "mlir/IR/Location.h"
 #include "mlir/IR/OperationSet.h"
 #include "mlir/IR/StandardOps.h"
 #include "mlir/IR/Types.h"
@@ -182,12 +183,26 @@
 /// This class is completely private to this file, so everything is public.
 class MLIRContextImpl {
 public:
-  /// We put immortal objects into this allocator.
-  llvm::BumpPtrAllocator allocator;
-
   /// This is the set of all operations that are registered with the system.
   OperationSet operationSet;
 
+  /// We put location info into this allocator, since it is generally not
+  /// touched by compiler passes.
+  llvm::BumpPtrAllocator locationAllocator;
+
+  /// The singleton for UnknownLoc.
+  UnknownLoc *theUnknownLoc = nullptr;
+
+  /// These are filename locations uniqued into this MLIRContext.
+  llvm::StringMap<char, llvm::BumpPtrAllocator &> filenames;
+
+  /// FileLineColLoc uniquing.
+  DenseMap<std::tuple<const char *, unsigned, unsigned>, FileLineColLoc *>
+      fileLineColLocs;
+
+  /// We put immortal objects into this allocator.
+  llvm::BumpPtrAllocator allocator;
+
   /// This is the handler to use to report diagnostics, or null if not
   /// registered.
   MLIRContext::DiagnosticHandlerTy diagnosticHandler;
@@ -258,7 +273,7 @@
   DenseMap<const Function *, FunctionAttr *> functionAttrs;
 
 public:
-  MLIRContextImpl() : identifiers(allocator) {
+  MLIRContextImpl() : filenames(locationAllocator), identifiers(allocator) {
     registerStandardOperations(operationSet);
   }
 
@@ -293,8 +308,7 @@
 /// This emits a diagnostic using the registered issue handle if present, or
 /// with the default behavior if not.  The MLIR compiler should not generally
 /// interact with this, it should use methods on Operation instead.
-void MLIRContext::emitDiagnostic(Attribute *location,
-                                 const llvm::Twine &message,
+void MLIRContext::emitDiagnostic(Location *location, const llvm::Twine &message,
                                  DiagnosticKind kind) const {
   // If we had a handler registered, emit the diagnostic using it.
   auto handler = getImpl().diagnosticHandler;
@@ -305,6 +319,8 @@
   if (kind != DiagnosticKind::Error)
     return;
 
+  // TODO(clattner): can improve this now!
+
   // The default behavior for errors is to emit them to stderr and exit.
   llvm::errs() << message.str() << "\n";
   llvm::errs().flush();
@@ -338,6 +354,39 @@
 }
 
 //===----------------------------------------------------------------------===//
+// Location uniquing
+//===----------------------------------------------------------------------===//
+
+UnknownLoc *UnknownLoc::get(MLIRContext *context) {
+  auto &impl = context->getImpl();
+  if (auto *result = impl.theUnknownLoc)
+    return result;
+
+  impl.theUnknownLoc = impl.allocator.Allocate<UnknownLoc>();
+  new (impl.theUnknownLoc) UnknownLoc();
+  return impl.theUnknownLoc;
+}
+
+UniquedFilename UniquedFilename::get(StringRef filename, MLIRContext *context) {
+  auto &impl = context->getImpl();
+  auto it = impl.filenames.insert({filename, char()}).first;
+  return UniquedFilename(it->getKeyData());
+}
+
+FileLineColLoc *FileLineColLoc::get(UniquedFilename filename, unsigned line,
+                                    unsigned column, MLIRContext *context) {
+  auto &impl = context->getImpl();
+  auto &entry =
+      impl.fileLineColLocs[std::make_tuple(filename.data(), line, column)];
+  if (!entry) {
+    entry = impl.allocator.Allocate<FileLineColLoc>();
+    new (entry) FileLineColLoc(filename, line, column);
+  }
+
+  return entry;
+}
+
+//===----------------------------------------------------------------------===//
 // Type uniquing
 //===----------------------------------------------------------------------===//
 
diff --git a/lib/IR/Operation.cpp b/lib/IR/Operation.cpp
index 71167bd..f5d07d5 100644
--- a/lib/IR/Operation.cpp
+++ b/lib/IR/Operation.cpp
@@ -46,7 +46,7 @@
 
 /// The source location the operation was defined or derived from.  Note that
 /// it is possible for this pointer to be null.
-Attribute *Operation::getLoc() const {
+Location *Operation::getLoc() const {
   if (auto *inst = dyn_cast<OperationInst>(this))
     return inst->getLoc();
   return cast<OperationStmt>(this)->getLoc();
diff --git a/lib/IR/Statement.cpp b/lib/IR/Statement.cpp
index 3746f71..3f95a94 100644
--- a/lib/IR/Statement.cpp
+++ b/lib/IR/Statement.cpp
@@ -209,7 +209,7 @@
 //===----------------------------------------------------------------------===//
 
 /// Create a new OperationStmt with the specific fields.
-OperationStmt *OperationStmt::create(Attribute *location, Identifier name,
+OperationStmt *OperationStmt::create(Location *location, Identifier name,
                                      ArrayRef<MLValue *> operands,
                                      ArrayRef<Type *> resultTypes,
                                      ArrayRef<NamedAttribute> attributes,
@@ -233,7 +233,7 @@
   return stmt;
 }
 
-OperationStmt::OperationStmt(Attribute *location, Identifier name,
+OperationStmt::OperationStmt(Location *location, Identifier name,
                              unsigned numOperands, unsigned numResults,
                              ArrayRef<NamedAttribute> attributes,
                              MLIRContext *context)
@@ -275,7 +275,7 @@
 // ForStmt
 //===----------------------------------------------------------------------===//
 
-ForStmt *ForStmt::create(Attribute *location, ArrayRef<MLValue *> lbOperands,
+ForStmt *ForStmt::create(Location *location, ArrayRef<MLValue *> lbOperands,
                          AffineMap *lbMap, ArrayRef<MLValue *> ubOperands,
                          AffineMap *ubMap, int64_t step, MLIRContext *context) {
   assert(lbOperands.size() == lbMap->getNumOperands() &&
@@ -297,7 +297,7 @@
   return stmt;
 }
 
-ForStmt::ForStmt(Attribute *location, unsigned numOperands, AffineMap *lbMap,
+ForStmt::ForStmt(Location *location, unsigned numOperands, AffineMap *lbMap,
                  AffineMap *ubMap, int64_t step, MLIRContext *context)
     : Statement(Kind::For, location),
       MLValue(MLValueKind::ForStmt, Type::getAffineInt(context)),
@@ -357,7 +357,7 @@
 // IfStmt
 //===----------------------------------------------------------------------===//
 
-IfStmt::IfStmt(Attribute *location, IntegerSet *condition)
+IfStmt::IfStmt(Location *location, IntegerSet *condition)
     : Statement(Kind::If, location), thenClause(new IfClause(this)),
       elseClause(nullptr), condition(condition) {}
 
diff --git a/lib/Parser/Parser.cpp b/lib/Parser/Parser.cpp
index 756f25d..ebeb196 100644
--- a/lib/Parser/Parser.cpp
+++ b/lib/Parser/Parser.cpp
@@ -25,6 +25,7 @@
 #include "mlir/IR/AffineMap.h"
 #include "mlir/IR/Attributes.h"
 #include "mlir/IR/Builders.h"
+#include "mlir/IR/Location.h"
 #include "mlir/IR/MLFunction.h"
 #include "mlir/IR/MLIRContext.h"
 #include "mlir/IR/Module.h"
@@ -43,6 +44,14 @@
 /// bool value.  Failure is "true" in a boolean context.
 enum ParseResult { ParseSuccess, ParseFailure };
 
+/// Return a uniqued filename for the main file the specified SourceMgr is
+/// looking at.
+static UniquedFilename getUniquedFilename(llvm::SourceMgr &sourceMgr,
+                                          MLIRContext *context) {
+  auto *buffer = sourceMgr.getMemoryBuffer(sourceMgr.getMainFileID());
+  return UniquedFilename::get(buffer->getBufferIdentifier(), context);
+}
+
 namespace {
 class Parser;
 
@@ -54,6 +63,7 @@
   ParserState(llvm::SourceMgr &sourceMgr, Module *module,
               SMDiagnosticHandlerTy errorReporter)
       : context(module->getContext()), module(module),
+        filename(getUniquedFilename(sourceMgr, context)),
         lex(sourceMgr, errorReporter), curToken(lex.lexToken()),
         errorReporter(errorReporter), operationSet(OperationSet::get(context)) {
   }
@@ -81,6 +91,9 @@
   // This is the module we are parsing into.
   Module *const module;
 
+  /// The filename to use for location generation.
+  UniquedFilename filename;
+
   // The lexer for the source file we're parsing.
   Lexer lex;
 
@@ -122,7 +135,7 @@
 
   /// Encode the specified source location information into an attribute for
   /// attachment to the IR.
-  Attribute *getEncodedSourceLocation(llvm::SMLoc loc);
+  Location *getEncodedSourceLocation(llvm::SMLoc loc);
 
   /// Emit an error and return failure.
   ParseResult emitError(const Twine &message) {
@@ -209,16 +222,13 @@
 
 /// Encode the specified source location information into an attribute for
 /// attachment to the IR.
-Attribute *Parser::getEncodedSourceLocation(llvm::SMLoc loc) {
-  // TODO(clattner): Switch to an more structured form that includes
-  // file/line/column instead of just byte offset in the file.  This will
-  // eliminate this block of low level code poking at the SourceMgr directly.
+Location *Parser::getEncodedSourceLocation(llvm::SMLoc loc) {
   auto &sourceMgr = getSourceMgr();
-  auto fileID = sourceMgr.FindBufferContainingLoc(loc);
+  auto lineAndColumn =
+      sourceMgr.getLineAndColumn(loc, sourceMgr.getMainFileID());
 
-  auto *srcBuffer = sourceMgr.getMemoryBuffer(fileID);
-  unsigned locationEncoding = loc.getPointer() - srcBuffer->getBufferStart();
-  return builder.getIntegerAttr(locationEncoding);
+  return FileLineColLoc::get(state.filename, lineAndColumn.first,
+                             lineAndColumn.second, getContext());
 }
 
 ParseResult Parser::emitError(SMLoc loc, const Twine &message) {
@@ -1389,11 +1399,9 @@
   // We create these placeholders as having an empty name, which we know cannot
   // be created through normal user input, allowing us to distinguish them.
   auto name = Identifier::get("placeholder", getContext());
-  auto *inst =
-      // FIXME(clattner): encode the location into the placeholder instead of
-      // into the forwardReferencePlaceholders map!
-      OperationInst::create(/*location=*/nullptr, name, /*operands=*/{}, type,
-                            /*attrs=*/{}, getContext());
+  auto *inst = OperationInst::create(getEncodedSourceLocation(loc), name,
+                                     /*operands=*/{}, type,
+                                     /*attributes=*/{}, getContext());
   forwardReferencePlaceholders[inst->getResult(0)] = loc;
   return inst->getResult(0);
 }
@@ -3038,11 +3046,8 @@
   auto existingContextHandler = context->getDiagnosticHandler();
 
   // Install a new handler that uses the error reporter.
-  context->registerDiagnosticHandler([&](Attribute *location, StringRef message,
+  context->registerDiagnosticHandler([&](Location *location, StringRef message,
                                          MLIRContext::DiagnosticKind kind) {
-    auto offset = cast<IntegerAttr>(location)->getValue();
-    auto *mainBuffer = sourceMgr.getMemoryBuffer(sourceMgr.getMainFileID());
-    auto ptr = mainBuffer->getBufferStart() + offset;
     SourceMgr::DiagKind diagKind;
     switch (kind) {
     case MLIRContext::DiagnosticKind::Error:
@@ -3055,8 +3060,20 @@
       diagKind = SourceMgr::DK_Note;
       break;
     }
-    errorReporter(
-        sourceMgr.GetMessage(SMLoc::getFromPointer(ptr), diagKind, message));
+
+    StringRef filename;
+    unsigned line = 0, column = 0;
+    if (auto fileLoc = dyn_cast<FileLineColLoc>(location)) {
+      filename = fileLoc->getFilename();
+      line = fileLoc->getLine();
+      column = fileLoc->getColumn();
+    }
+
+    auto diag = llvm::SMDiagnostic(sourceMgr, SMLoc(), filename, line, column,
+                                   diagKind, message, /*LineStr=*/StringRef(),
+                                   /*Ranges=*/{}, /*FixIts=*/{});
+
+    errorReporter(diag);
   });
 
   // This is the result module we are parsing into.