[spirv-writer] Generate constants

This CL updates the SPIR-V writer to generate the OpConstantTrue,
OpConstantFalse and OpConstant instructions.

Bug: tint:5
Change-Id: I660554c491e4eb569e3902fce0973fae3f27e6c0
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/17820
Reviewed-by: David Neto <dneto@google.com>
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 10605fd..dc37b6e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -264,6 +264,7 @@
   ast/else_statement_test.cc
   ast/entry_point_test.cc
   ast/fallthrough_statement_test.cc
+  ast/float_literal_test.cc
   ast/function_test.cc
   ast/identifier_expression_test.cc
   ast/if_statement_test.cc
@@ -406,9 +407,10 @@
   list(APPEND TINT_TEST_SRCS
     writer/spirv/binary_writer_test.cc
     writer/spirv/builder_test.cc
-    writer/spirv/builder_type_test.cc
     writer/spirv/builder_entry_point_test.cc
     writer/spirv/builder_function_test.cc
+    writer/spirv/builder_literal_test.cc
+    writer/spirv/builder_type_test.cc
     writer/spirv/instruction_test.cc
     writer/spirv/operand_test.cc
     writer/spirv/spv_dump.cc
diff --git a/src/ast/bool_literal.cc b/src/ast/bool_literal.cc
index e11e8a5..2ab78b9 100644
--- a/src/ast/bool_literal.cc
+++ b/src/ast/bool_literal.cc
@@ -17,7 +17,8 @@
 namespace tint {
 namespace ast {
 
-BoolLiteral::BoolLiteral(bool value) : value_(value) {}
+BoolLiteral::BoolLiteral(ast::type::Type* type, bool value)
+    : Literal(type), value_(value) {}
 
 BoolLiteral::~BoolLiteral() = default;
 
@@ -25,5 +26,9 @@
   return value_ ? "true" : "false";
 }
 
+std::string BoolLiteral::name() const {
+  return value_ ? "__bool_true" : "__bool_false";
+}
+
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/bool_literal.h b/src/ast/bool_literal.h
index c568f27..d1749b1 100644
--- a/src/ast/bool_literal.h
+++ b/src/ast/bool_literal.h
@@ -26,8 +26,9 @@
 class BoolLiteral : public Literal {
  public:
   /// Constructor
+  /// @param type the type of the literal
   /// @param value the bool literals value
-  explicit BoolLiteral(bool value);
+  BoolLiteral(ast::type::Type* type, bool value);
   ~BoolLiteral() override;
 
   /// @returns true if this is a bool literal
@@ -38,6 +39,9 @@
   /// @returns true if the bool literal is false
   bool IsFalse() const { return !value_; }
 
+  /// @returns the name for this literal. This name is unique to this value.
+  std::string name() const override;
+
   /// @returns the literal as a string
   std::string to_str() const override;
 
diff --git a/src/ast/bool_literal_test.cc b/src/ast/bool_literal_test.cc
index 190a51b..10697ee 100644
--- a/src/ast/bool_literal_test.cc
+++ b/src/ast/bool_literal_test.cc
@@ -15,6 +15,7 @@
 #include "src/ast/bool_literal.h"
 
 #include "gtest/gtest.h"
+#include "src/ast/type/bool_type.h"
 
 namespace tint {
 namespace ast {
@@ -23,21 +24,24 @@
 using BoolLiteralTest = testing::Test;
 
 TEST_F(BoolLiteralTest, True) {
-  BoolLiteral b{true};
+  ast::type::BoolType bool_type;
+  BoolLiteral b{&bool_type, true};
   ASSERT_TRUE(b.IsBool());
   ASSERT_TRUE(b.IsTrue());
   ASSERT_FALSE(b.IsFalse());
 }
 
 TEST_F(BoolLiteralTest, False) {
-  BoolLiteral b{false};
+  ast::type::BoolType bool_type;
+  BoolLiteral b{&bool_type, false};
   ASSERT_TRUE(b.IsBool());
   ASSERT_FALSE(b.IsTrue());
   ASSERT_TRUE(b.IsFalse());
 }
 
 TEST_F(BoolLiteralTest, Is) {
-  BoolLiteral b{false};
+  ast::type::BoolType bool_type;
+  BoolLiteral b{&bool_type, false};
   EXPECT_TRUE(b.IsBool());
   EXPECT_FALSE(b.IsInt());
   EXPECT_FALSE(b.IsFloat());
@@ -45,8 +49,9 @@
 }
 
 TEST_F(BoolLiteralTest, ToStr) {
-  BoolLiteral t{true};
-  BoolLiteral f{false};
+  ast::type::BoolType bool_type;
+  BoolLiteral t{&bool_type, true};
+  BoolLiteral f{&bool_type, false};
 
   EXPECT_EQ(t.to_str(), "true");
   EXPECT_EQ(f.to_str(), "false");
diff --git a/src/ast/case_statement_test.cc b/src/ast/case_statement_test.cc
index dc1f90b..27b94c5 100644
--- a/src/ast/case_statement_test.cc
+++ b/src/ast/case_statement_test.cc
@@ -18,6 +18,7 @@
 #include "src/ast/bool_literal.h"
 #include "src/ast/if_statement.h"
 #include "src/ast/nop_statement.h"
+#include "src/ast/type/bool_type.h"
 
 namespace tint {
 namespace ast {
@@ -26,7 +27,8 @@
 using CaseStatementTest = testing::Test;
 
 TEST_F(CaseStatementTest, Creation) {
-  auto b = std::make_unique<BoolLiteral>(true);
+  ast::type::BoolType bool_type;
+  auto b = std::make_unique<BoolLiteral>(&bool_type, true);
   std::vector<std::unique_ptr<Statement>> stmts;
   stmts.push_back(std::make_unique<NopStatement>());
 
@@ -40,7 +42,8 @@
 }
 
 TEST_F(CaseStatementTest, Creation_WithSource) {
-  auto b = std::make_unique<BoolLiteral>(true);
+  ast::type::BoolType bool_type;
+  auto b = std::make_unique<BoolLiteral>(&bool_type, true);
   std::vector<std::unique_ptr<Statement>> stmts;
   stmts.push_back(std::make_unique<NopStatement>());
 
@@ -60,7 +63,8 @@
 }
 
 TEST_F(CaseStatementTest, IsDefault_WithCondition) {
-  auto b = std::make_unique<BoolLiteral>(true);
+  ast::type::BoolType bool_type;
+  auto b = std::make_unique<BoolLiteral>(&bool_type, true);
   CaseStatement c;
   c.set_condition(std::move(b));
   EXPECT_FALSE(c.IsDefault());
@@ -77,7 +81,8 @@
 }
 
 TEST_F(CaseStatementTest, IsValid_NullBodyStatement) {
-  auto b = std::make_unique<BoolLiteral>(true);
+  ast::type::BoolType bool_type;
+  auto b = std::make_unique<BoolLiteral>(&bool_type, true);
   std::vector<std::unique_ptr<Statement>> stmts;
   stmts.push_back(std::make_unique<NopStatement>());
   stmts.push_back(nullptr);
@@ -87,7 +92,8 @@
 }
 
 TEST_F(CaseStatementTest, IsValid_InvalidBodyStatement) {
-  auto b = std::make_unique<BoolLiteral>(true);
+  ast::type::BoolType bool_type;
+  auto b = std::make_unique<BoolLiteral>(&bool_type, true);
   std::vector<std::unique_ptr<Statement>> stmts;
   stmts.push_back(std::make_unique<IfStatement>());
 
@@ -96,7 +102,8 @@
 }
 
 TEST_F(CaseStatementTest, ToStr_WithCondition) {
-  auto b = std::make_unique<BoolLiteral>(true);
+  ast::type::BoolType bool_type;
+  auto b = std::make_unique<BoolLiteral>(&bool_type, true);
   std::vector<std::unique_ptr<Statement>> stmts;
   stmts.push_back(std::make_unique<NopStatement>());
   CaseStatement c(std::move(b), std::move(stmts));
diff --git a/src/ast/const_initializer_expression_test.cc b/src/ast/const_initializer_expression_test.cc
index a823a33..243df54 100644
--- a/src/ast/const_initializer_expression_test.cc
+++ b/src/ast/const_initializer_expression_test.cc
@@ -16,6 +16,7 @@
 
 #include "gtest/gtest.h"
 #include "src/ast/bool_literal.h"
+#include "src/ast/type/bool_type.h"
 
 namespace tint {
 namespace ast {
@@ -24,14 +25,16 @@
 using ConstInitializerExpressionTest = testing::Test;
 
 TEST_F(ConstInitializerExpressionTest, Creation) {
-  auto b = std::make_unique<BoolLiteral>(true);
+  ast::type::BoolType bool_type;
+  auto b = std::make_unique<BoolLiteral>(&bool_type, true);
   auto b_ptr = b.get();
   ConstInitializerExpression c(std::move(b));
   EXPECT_EQ(c.literal(), b_ptr);
 }
 
 TEST_F(ConstInitializerExpressionTest, Creation_WithSource) {
-  auto b = std::make_unique<BoolLiteral>(true);
+  ast::type::BoolType bool_type;
+  auto b = std::make_unique<BoolLiteral>(&bool_type, true);
   ConstInitializerExpression c(Source{20, 2}, std::move(b));
   auto src = c.source();
   EXPECT_EQ(src.line, 20);
@@ -39,7 +42,8 @@
 }
 
 TEST_F(ConstInitializerExpressionTest, IsValid) {
-  auto b = std::make_unique<BoolLiteral>(true);
+  ast::type::BoolType bool_type;
+  auto b = std::make_unique<BoolLiteral>(&bool_type, true);
   ConstInitializerExpression c(std::move(b));
   EXPECT_TRUE(c.IsValid());
 }
@@ -50,7 +54,8 @@
 }
 
 TEST_F(ConstInitializerExpressionTest, ToStr) {
-  auto b = std::make_unique<BoolLiteral>(true);
+  ast::type::BoolType bool_type;
+  auto b = std::make_unique<BoolLiteral>(&bool_type, true);
   ConstInitializerExpression c(std::move(b));
   std::ostringstream out;
   c.to_str(out, 2);
diff --git a/src/ast/else_statement_test.cc b/src/ast/else_statement_test.cc
index 9675e0b..3e1aadc 100644
--- a/src/ast/else_statement_test.cc
+++ b/src/ast/else_statement_test.cc
@@ -19,6 +19,7 @@
 #include "src/ast/const_initializer_expression.h"
 #include "src/ast/if_statement.h"
 #include "src/ast/nop_statement.h"
+#include "src/ast/type/bool_type.h"
 
 namespace tint {
 namespace ast {
@@ -27,8 +28,9 @@
 using ElseStatementTest = testing::Test;
 
 TEST_F(ElseStatementTest, Creation) {
+  ast::type::BoolType bool_type;
   auto cond = std::make_unique<ConstInitializerExpression>(
-      std::make_unique<BoolLiteral>(true));
+      std::make_unique<BoolLiteral>(&bool_type, true));
   std::vector<std::unique_ptr<Statement>> body;
   body.push_back(std::make_unique<NopStatement>());
 
@@ -54,8 +56,9 @@
 }
 
 TEST_F(ElseStatementTest, HasCondition) {
+  ast::type::BoolType bool_type;
   auto cond = std::make_unique<ConstInitializerExpression>(
-      std::make_unique<BoolLiteral>(true));
+      std::make_unique<BoolLiteral>(&bool_type, true));
   ElseStatement e(std::move(cond), {});
   EXPECT_TRUE(e.HasCondition());
 }
@@ -102,8 +105,9 @@
 }
 
 TEST_F(ElseStatementTest, ToStr) {
+  ast::type::BoolType bool_type;
   auto cond = std::make_unique<ConstInitializerExpression>(
-      std::make_unique<BoolLiteral>(true));
+      std::make_unique<BoolLiteral>(&bool_type, true));
   std::vector<std::unique_ptr<Statement>> body;
   body.push_back(std::make_unique<NopStatement>());
 
diff --git a/src/ast/float_literal.cc b/src/ast/float_literal.cc
index 1d8b40f..467a7bd 100644
--- a/src/ast/float_literal.cc
+++ b/src/ast/float_literal.cc
@@ -14,10 +14,13 @@
 
 #include "src/ast/float_literal.h"
 
+#include <sstream>
+
 namespace tint {
 namespace ast {
 
-FloatLiteral::FloatLiteral(float value) : value_(value) {}
+FloatLiteral::FloatLiteral(ast::type::Type* type, float value)
+    : Literal(type), value_(value) {}
 
 FloatLiteral::~FloatLiteral() = default;
 
@@ -25,5 +28,13 @@
   return std::to_string(value_);
 }
 
+std::string FloatLiteral::name() const {
+  std::ostringstream out;
+  out.flags(out.flags() | std::ios_base::showpoint);
+  out.precision(std::numeric_limits<float>::max_digits10);
+  out << "__float" << value_;
+  return out.str();
+}
+
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/float_literal.h b/src/ast/float_literal.h
index baf088a..370a553 100644
--- a/src/ast/float_literal.h
+++ b/src/ast/float_literal.h
@@ -26,8 +26,9 @@
 class FloatLiteral : public Literal {
  public:
   /// Constructor
+  /// @param type the type of the literal
   /// @param value the float literals value
-  explicit FloatLiteral(float value);
+  FloatLiteral(ast::type::Type* type, float value);
   ~FloatLiteral() override;
 
   /// @returns true if this is a float literal
@@ -36,6 +37,9 @@
   /// @returns the float literal value
   float value() const { return value_; }
 
+  /// @returns the name for this literal. This name is unique to this value.
+  std::string name() const override;
+
   /// @returns the literal as a string
   std::string to_str() const override;
 
diff --git a/src/ast/float_literal_test.cc b/src/ast/float_literal_test.cc
index e9b3f4e..6f11018 100644
--- a/src/ast/float_literal_test.cc
+++ b/src/ast/float_literal_test.cc
@@ -15,6 +15,7 @@
 #include "src/ast/float_literal.h"
 
 #include "gtest/gtest.h"
+#include "src/ast/type/f32_type.h"
 
 namespace tint {
 namespace ast {
@@ -23,13 +24,15 @@
 using FloatLiteralTest = testing::Test;
 
 TEST_F(FloatLiteralTest, Value) {
-  FloatLiteral f{47.2};
+  ast::type::F32Type f32;
+  FloatLiteral f{&f32, 47.2f};
   ASSERT_TRUE(f.IsFloat());
-  EXPECT_EQ(f.value(), 47.2);
+  EXPECT_EQ(f.value(), 47.2f);
 }
 
 TEST_F(FloatLiteralTest, Is) {
-  FloatLiteral f{42};
+  ast::type::F32Type f32;
+  FloatLiteral f{&f32, 42.f};
   EXPECT_FALSE(f.IsBool());
   EXPECT_FALSE(f.IsInt());
   EXPECT_TRUE(f.IsFloat());
@@ -37,9 +40,16 @@
 }
 
 TEST_F(FloatLiteralTest, ToStr) {
-  FloatLiteral f{42.1};
+  ast::type::F32Type f32;
+  FloatLiteral f{&f32, 42.1f};
 
-  EXPECT_EQ(f.to_str(), "42.1");
+  EXPECT_EQ(f.to_str(), "42.099998");
+}
+
+TEST_F(FloatLiteralTest, ToName) {
+  ast::type::F32Type f32;
+  FloatLiteral f{&f32, 42.1f};
+  EXPECT_EQ(f.name(), "__float42.0999985");
 }
 
 }  // namespace
diff --git a/src/ast/int_literal.cc b/src/ast/int_literal.cc
index 99e476c..1fdf84f 100644
--- a/src/ast/int_literal.cc
+++ b/src/ast/int_literal.cc
@@ -17,7 +17,8 @@
 namespace tint {
 namespace ast {
 
-IntLiteral::IntLiteral(int32_t value) : value_(value) {}
+IntLiteral::IntLiteral(ast::type::Type* type, int32_t value)
+    : Literal(type), value_(value) {}
 
 IntLiteral::~IntLiteral() = default;
 
@@ -25,5 +26,9 @@
   return std::to_string(value_);
 }
 
+std::string IntLiteral::name() const {
+  return "__int" + std::to_string(value_);
+}
+
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/int_literal.h b/src/ast/int_literal.h
index b96e3b9..ccbd89c 100644
--- a/src/ast/int_literal.h
+++ b/src/ast/int_literal.h
@@ -26,8 +26,9 @@
 class IntLiteral : public Literal {
  public:
   /// Constructor
+  /// @param type the type
   /// @param value the int literals value
-  explicit IntLiteral(int32_t value);
+  IntLiteral(ast::type::Type* type, int32_t value);
   ~IntLiteral() override;
 
   /// @returns true if this is a int literal
@@ -36,6 +37,9 @@
   /// @returns the int literal value
   int32_t value() const { return value_; }
 
+  /// @returns the name for this literal. This name is unique to this value.
+  std::string name() const override;
+
   /// @returns the literal as a string
   std::string to_str() const override;
 
diff --git a/src/ast/int_literal_test.cc b/src/ast/int_literal_test.cc
index 8d35d5d..42ca665 100644
--- a/src/ast/int_literal_test.cc
+++ b/src/ast/int_literal_test.cc
@@ -15,6 +15,7 @@
 #include "src/ast/int_literal.h"
 
 #include "gtest/gtest.h"
+#include "src/ast/type/i32_type.h"
 
 namespace tint {
 namespace ast {
@@ -23,13 +24,15 @@
 using IntLiteralTest = testing::Test;
 
 TEST_F(IntLiteralTest, Value) {
-  IntLiteral i{47};
+  ast::type::I32Type i32;
+  IntLiteral i{&i32, 47};
   ASSERT_TRUE(i.IsInt());
   EXPECT_EQ(i.value(), 47);
 }
 
 TEST_F(IntLiteralTest, Is) {
-  IntLiteral i{42};
+  ast::type::I32Type i32;
+  IntLiteral i{&i32, 42};
   EXPECT_FALSE(i.IsBool());
   EXPECT_TRUE(i.IsInt());
   EXPECT_FALSE(i.IsFloat());
@@ -37,7 +40,8 @@
 }
 
 TEST_F(IntLiteralTest, ToStr) {
-  IntLiteral i{-42};
+  ast::type::I32Type i32;
+  IntLiteral i{&i32, -42};
 
   EXPECT_EQ(i.to_str(), "-42");
 }
diff --git a/src/ast/literal.cc b/src/ast/literal.cc
index 055d33d..3759274 100644
--- a/src/ast/literal.cc
+++ b/src/ast/literal.cc
@@ -24,7 +24,7 @@
 namespace tint {
 namespace ast {
 
-Literal::Literal() = default;
+Literal::Literal(ast::type::Type* type) : type_(type) {}
 
 Literal::~Literal() = default;
 
diff --git a/src/ast/literal.h b/src/ast/literal.h
index af8b831..9f91478 100644
--- a/src/ast/literal.h
+++ b/src/ast/literal.h
@@ -17,6 +17,8 @@
 
 #include <string>
 
+#include "src/ast/type/type.h"
+
 namespace tint {
 namespace ast {
 
@@ -48,12 +50,21 @@
   /// @returns the literal as a unsigned int literal
   UintLiteral* AsUint();
 
+  /// @returns the type of the literal
+  ast::type::Type* type() const { return type_; }
+
   /// @returns the literal as a string
   virtual std::string to_str() const = 0;
 
+  /// @returns the name for this literal. This name is unique to this value.
+  virtual std::string name() const = 0;
+
  protected:
   /// Constructor
-  Literal();
+  Literal(ast::type::Type* type);
+
+ private:
+  ast::type::Type* type_ = nullptr;
 };
 
 }  // namespace ast
diff --git a/src/ast/switch_statement_test.cc b/src/ast/switch_statement_test.cc
index 1f065ec..4bb71f5 100644
--- a/src/ast/switch_statement_test.cc
+++ b/src/ast/switch_statement_test.cc
@@ -20,6 +20,7 @@
 #include "src/ast/bool_literal.h"
 #include "src/ast/case_statement.h"
 #include "src/ast/identifier_expression.h"
+#include "src/ast/type/bool_type.h"
 
 namespace tint {
 namespace ast {
@@ -28,7 +29,8 @@
 using SwitchStatementTest = testing::Test;
 
 TEST_F(SwitchStatementTest, Creation) {
-  auto lit = std::make_unique<BoolLiteral>(true);
+  ast::type::BoolType bool_type;
+  auto lit = std::make_unique<BoolLiteral>(&bool_type, true);
   auto ident = std::make_unique<IdentifierExpression>("ident");
   std::vector<std::unique_ptr<CaseStatement>> body;
   body.push_back(std::make_unique<CaseStatement>(
@@ -59,7 +61,8 @@
 }
 
 TEST_F(SwitchStatementTest, IsValid) {
-  auto lit = std::make_unique<BoolLiteral>(true);
+  ast::type::BoolType bool_type;
+  auto lit = std::make_unique<BoolLiteral>(&bool_type, true);
   auto ident = std::make_unique<IdentifierExpression>("ident");
   std::vector<std::unique_ptr<CaseStatement>> body;
   body.push_back(std::make_unique<CaseStatement>(
@@ -70,7 +73,8 @@
 }
 
 TEST_F(SwitchStatementTest, IsValid_Null_Condition) {
-  auto lit = std::make_unique<BoolLiteral>(true);
+  ast::type::BoolType bool_type;
+  auto lit = std::make_unique<BoolLiteral>(&bool_type, true);
   std::vector<std::unique_ptr<CaseStatement>> body;
   body.push_back(std::make_unique<CaseStatement>(
       std::move(lit), std::vector<std::unique_ptr<Statement>>()));
@@ -81,7 +85,8 @@
 }
 
 TEST_F(SwitchStatementTest, IsValid_Invalid_Condition) {
-  auto lit = std::make_unique<BoolLiteral>(true);
+  ast::type::BoolType bool_type;
+  auto lit = std::make_unique<BoolLiteral>(&bool_type, true);
   auto ident = std::make_unique<IdentifierExpression>("");
   std::vector<std::unique_ptr<CaseStatement>> body;
   body.push_back(std::make_unique<CaseStatement>(
@@ -92,7 +97,8 @@
 }
 
 TEST_F(SwitchStatementTest, IsValid_Null_BodyStatement) {
-  auto lit = std::make_unique<BoolLiteral>(true);
+  ast::type::BoolType bool_type;
+  auto lit = std::make_unique<BoolLiteral>(&bool_type, true);
   auto ident = std::make_unique<IdentifierExpression>("ident");
   std::vector<std::unique_ptr<CaseStatement>> body;
   body.push_back(std::make_unique<CaseStatement>(
@@ -132,7 +138,8 @@
 }
 
 TEST_F(SwitchStatementTest, ToStr) {
-  auto lit = std::make_unique<BoolLiteral>(true);
+  ast::type::BoolType bool_type;
+  auto lit = std::make_unique<BoolLiteral>(&bool_type, true);
   auto ident = std::make_unique<IdentifierExpression>("ident");
   std::vector<std::unique_ptr<CaseStatement>> body;
   body.push_back(std::make_unique<CaseStatement>(
diff --git a/src/ast/uint_literal.cc b/src/ast/uint_literal.cc
index 273f9ca..562d47a 100644
--- a/src/ast/uint_literal.cc
+++ b/src/ast/uint_literal.cc
@@ -17,7 +17,8 @@
 namespace tint {
 namespace ast {
 
-UintLiteral::UintLiteral(uint32_t value) : value_(value) {}
+UintLiteral::UintLiteral(ast::type::Type* type, uint32_t value)
+    : Literal(type), value_(value) {}
 
 UintLiteral::~UintLiteral() = default;
 
@@ -25,5 +26,9 @@
   return std::to_string(value_);
 }
 
+std::string UintLiteral::name() const {
+  return "__uint" + std::to_string(value_);
+}
+
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/uint_literal.h b/src/ast/uint_literal.h
index 0b2394e..c9e7731 100644
--- a/src/ast/uint_literal.h
+++ b/src/ast/uint_literal.h
@@ -26,8 +26,9 @@
 class UintLiteral : public Literal {
  public:
   /// Constructor
+  /// @param type the type of the literal
   /// @param value the uint literals value
-  explicit UintLiteral(uint32_t value);
+  UintLiteral(ast::type::Type* type, uint32_t value);
   ~UintLiteral() override;
 
   /// @returns true if this is a uint literal
@@ -36,6 +37,9 @@
   /// @returns the uint literal value
   uint32_t value() const { return value_; }
 
+  /// @returns the name for this literal. This name is unique to this value.
+  std::string name() const override;
+
   /// @returns the literal as a string
   std::string to_str() const override;
 
diff --git a/src/ast/uint_literal_test.cc b/src/ast/uint_literal_test.cc
index 137cded..d19811c 100644
--- a/src/ast/uint_literal_test.cc
+++ b/src/ast/uint_literal_test.cc
@@ -15,6 +15,7 @@
 #include "src/ast/uint_literal.h"
 
 #include "gtest/gtest.h"
+#include "src/ast/type/u32_type.h"
 
 namespace tint {
 namespace ast {
@@ -23,13 +24,15 @@
 using UintLiteralTest = testing::Test;
 
 TEST_F(UintLiteralTest, Value) {
-  UintLiteral u{47};
+  ast::type::U32Type u32;
+  UintLiteral u{&u32, 47};
   ASSERT_TRUE(u.IsUint());
   EXPECT_EQ(u.value(), 47);
 }
 
 TEST_F(UintLiteralTest, Is) {
-  UintLiteral u{42};
+  ast::type::U32Type u32;
+  UintLiteral u{&u32, 42};
   EXPECT_FALSE(u.IsBool());
   EXPECT_FALSE(u.IsInt());
   EXPECT_FALSE(u.IsFloat());
@@ -37,7 +40,8 @@
 }
 
 TEST_F(UintLiteralTest, ToStr) {
-  UintLiteral i{42};
+  ast::type::U32Type u32;
+  UintLiteral i{&u32, 42};
 
   EXPECT_EQ(i.to_str(), "42");
 }
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index 1b71bea..e2db44d 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -2097,23 +2097,44 @@
   auto t = peek();
   if (t.IsTrue()) {
     next();  // Consume the peek
-    return std::make_unique<ast::BoolLiteral>(true);
+
+    auto type = ctx_.type_mgr->Get(std::make_unique<ast::type::BoolType>());
+    if (!type) {
+      return nullptr;
+    }
+    return std::make_unique<ast::BoolLiteral>(type, true);
   }
   if (t.IsFalse()) {
     next();  // Consume the peek
-    return std::make_unique<ast::BoolLiteral>(false);
+    auto type = ctx_.type_mgr->Get(std::make_unique<ast::type::BoolType>());
+    if (!type) {
+      return nullptr;
+    }
+    return std::make_unique<ast::BoolLiteral>(type, false);
   }
   if (t.IsIntLiteral()) {
     next();  // Consume the peek
-    return std::make_unique<ast::IntLiteral>(t.to_i32());
+    auto type = ctx_.type_mgr->Get(std::make_unique<ast::type::I32Type>());
+    if (!type) {
+      return nullptr;
+    }
+    return std::make_unique<ast::IntLiteral>(type, t.to_i32());
   }
   if (t.IsUintLiteral()) {
     next();  // Consume the peek
-    return std::make_unique<ast::UintLiteral>(t.to_u32());
+    auto type = ctx_.type_mgr->Get(std::make_unique<ast::type::U32Type>());
+    if (!type) {
+      return nullptr;
+    }
+    return std::make_unique<ast::UintLiteral>(type, t.to_u32());
   }
   if (t.IsFloatLiteral()) {
     next();  // Consume the peek
-    return std::make_unique<ast::FloatLiteral>(t.to_f32());
+    auto type = ctx_.type_mgr->Get(std::make_unique<ast::type::F32Type>());
+    if (!type) {
+      return nullptr;
+    }
+    return std::make_unique<ast::FloatLiteral>(type, t.to_f32());
   }
   return nullptr;
 }
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index f9c1584..bca5b92 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -1,5 +1,4 @@
-// Copyright 2020 The Tint Authors.
-//
+// Copyright 2020 The Tint 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
@@ -17,12 +16,16 @@
 #include <utility>
 
 #include "spirv/unified1/spirv.h"
+#include "src/ast/bool_literal.h"
+#include "src/ast/float_literal.h"
+#include "src/ast/int_literal.h"
 #include "src/ast/struct.h"
 #include "src/ast/struct_member.h"
 #include "src/ast/struct_member_offset_decoration.h"
 #include "src/ast/type/matrix_type.h"
 #include "src/ast/type/struct_type.h"
 #include "src/ast/type/vector_type.h"
+#include "src/ast/uint_literal.h"
 
 namespace tint {
 namespace writer {
@@ -212,6 +215,44 @@
   import_name_to_id_[imp->name()] = id;
 }
 
+uint32_t Builder::GenerateLiteralIfNeeded(ast::Literal* lit) {
+  auto type_id = GenerateTypeIfNeeded(lit->type());
+  if (type_id == 0) {
+    return 0;
+  }
+  auto name = lit->name();
+  auto val = const_to_id_.find(name);
+  if (val != const_to_id_.end()) {
+    return val->second;
+  }
+
+  auto result = result_op();
+  auto result_id = result.to_i();
+
+  if (lit->IsBool()) {
+    if (lit->AsBool()->IsTrue()) {
+      push_type(spv::Op::OpConstantTrue, {Operand::Int(type_id), result});
+    } else {
+      push_type(spv::Op::OpConstantFalse, {Operand::Int(type_id), result});
+    }
+  } else if (lit->IsInt()) {
+    push_type(spv::Op::OpConstant, {Operand::Int(type_id), result,
+                                    Operand::Int(lit->AsInt()->value())});
+  } else if (lit->IsUint()) {
+    push_type(spv::Op::OpConstant, {Operand::Int(type_id), result,
+                                    Operand::Int(lit->AsUint()->value())});
+  } else if (lit->IsFloat()) {
+    push_type(spv::Op::OpConstant, {Operand::Int(type_id), result,
+                                    Operand::Float(lit->AsFloat()->value())});
+  } else {
+    error_ = "unknown literal type";
+    return 0;
+  }
+
+  const_to_id_[name] = result_id;
+  return result_id;
+}
+
 uint32_t Builder::GenerateTypeIfNeeded(ast::type::Type* type) {
   if (type->IsAlias()) {
     return GenerateTypeIfNeeded(type->AsAlias()->type());
diff --git a/src/writer/spirv/builder.h b/src/writer/spirv/builder.h
index b075fa9..3045fe2 100644
--- a/src/writer/spirv/builder.h
+++ b/src/writer/spirv/builder.h
@@ -20,6 +20,7 @@
 #include <unordered_map>
 #include <vector>
 
+#include "src/ast/literal.h"
 #include "src/ast/module.h"
 #include "src/ast/struct_member.h"
 #include "src/writer/spirv/instruction.h"
@@ -136,6 +137,10 @@
   /// Generates an import instruction
   /// @param imp the import
   void GenerateImport(ast::Import* imp);
+  /// Generates a literal constant if needed
+  /// @param lit the literal to generate
+  /// @returns the ID on success or 0 on failure
+  uint32_t GenerateLiteralIfNeeded(ast::Literal* lit);
   /// Generates a type if not already created
   /// @param type the type to create
   /// @returns the ID to use for the given type. Returns 0 on unknown type.
@@ -181,6 +186,7 @@
   std::unordered_map<std::string, uint32_t> import_name_to_id_;
   std::unordered_map<std::string, uint32_t> func_name_to_id_;
   std::unordered_map<std::string, uint32_t> type_name_to_id_;
+  std::unordered_map<std::string, uint32_t> const_to_id_;
 };
 
 }  // namespace spirv
diff --git a/src/writer/spirv/builder_literal_test.cc b/src/writer/spirv/builder_literal_test.cc
new file mode 100644
index 0000000..432ac16
--- /dev/null
+++ b/src/writer/spirv/builder_literal_test.cc
@@ -0,0 +1,172 @@
+// Copyright 2020 The Tint 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.
+
+#include "gtest/gtest.h"
+#include "spirv/unified1/spirv.h"
+#include "src/ast/bool_literal.h"
+#include "src/ast/float_literal.h"
+#include "src/ast/int_literal.h"
+#include "src/ast/literal.h"
+#include "src/ast/type/bool_type.h"
+#include "src/ast/type/f32_type.h"
+#include "src/ast/type/i32_type.h"
+#include "src/ast/type/u32_type.h"
+#include "src/ast/uint_literal.h"
+#include "src/writer/spirv/builder.h"
+#include "src/writer/spirv/spv_dump.h"
+
+namespace tint {
+namespace writer {
+namespace spirv {
+
+using BuilderTest = testing::Test;
+
+TEST_F(BuilderTest, Literal_Bool_True) {
+  ast::type::BoolType bool_type;
+  ast::BoolLiteral b_true(&bool_type, true);
+
+  Builder b;
+  auto id = b.GenerateLiteralIfNeeded(&b_true);
+  ASSERT_FALSE(b.has_error()) << b.error();
+  EXPECT_EQ(2, id);
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeBool
+%2 = OpConstantTrue %1
+)");
+}
+
+TEST_F(BuilderTest, Literal_Bool_False) {
+  ast::type::BoolType bool_type;
+  ast::BoolLiteral b_false(&bool_type, false);
+
+  Builder b;
+  auto id = b.GenerateLiteralIfNeeded(&b_false);
+  ASSERT_FALSE(b.has_error()) << b.error();
+  EXPECT_EQ(2, id);
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeBool
+%2 = OpConstantFalse %1
+)");
+}
+
+TEST_F(BuilderTest, Literal_Bool_Dedup) {
+  ast::type::BoolType bool_type;
+  ast::BoolLiteral b_true(&bool_type, true);
+  ast::BoolLiteral b_false(&bool_type, false);
+
+  Builder b;
+  ASSERT_NE(b.GenerateLiteralIfNeeded(&b_true), 0);
+  ASSERT_FALSE(b.has_error()) << b.error();
+  ASSERT_NE(b.GenerateLiteralIfNeeded(&b_false), 0);
+  ASSERT_FALSE(b.has_error()) << b.error();
+  ASSERT_NE(b.GenerateLiteralIfNeeded(&b_true), 0);
+  ASSERT_FALSE(b.has_error()) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeBool
+%2 = OpConstantTrue %1
+%3 = OpConstantFalse %1
+)");
+}
+
+TEST_F(BuilderTest, Literal_I32) {
+  ast::type::I32Type i32;
+  ast::IntLiteral i(&i32, -23);
+
+  Builder b;
+  auto id = b.GenerateLiteralIfNeeded(&i);
+  ASSERT_FALSE(b.has_error()) << b.error();
+  EXPECT_EQ(2, id);
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeInt 32 1
+%2 = OpConstant %1 -23
+)");
+}
+
+TEST_F(BuilderTest, Literal_I32_Dedup) {
+  ast::type::I32Type i32;
+  ast::IntLiteral i1(&i32, -23);
+  ast::IntLiteral i2(&i32, -23);
+
+  Builder b;
+  ASSERT_NE(b.GenerateLiteralIfNeeded(&i1), 0);
+  ASSERT_NE(b.GenerateLiteralIfNeeded(&i2), 0);
+  ASSERT_FALSE(b.has_error()) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeInt 32 1
+%2 = OpConstant %1 -23
+)");
+}
+
+TEST_F(BuilderTest, Literal_U32) {
+  ast::type::U32Type u32;
+  ast::UintLiteral i(&u32, 23);
+
+  Builder b;
+  auto id = b.GenerateLiteralIfNeeded(&i);
+  ASSERT_FALSE(b.has_error()) << b.error();
+  EXPECT_EQ(2, id);
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeInt 32 0
+%2 = OpConstant %1 23
+)");
+}
+
+TEST_F(BuilderTest, Literal_U32_Dedup) {
+  ast::type::U32Type u32;
+  ast::UintLiteral i1(&u32, 23);
+  ast::UintLiteral i2(&u32, 23);
+
+  Builder b;
+  ASSERT_NE(b.GenerateLiteralIfNeeded(&i1), 0);
+  ASSERT_NE(b.GenerateLiteralIfNeeded(&i2), 0);
+  ASSERT_FALSE(b.has_error()) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeInt 32 0
+%2 = OpConstant %1 23
+)");
+}
+
+TEST_F(BuilderTest, Literal_F32) {
+  ast::type::F32Type f32;
+  ast::FloatLiteral i(&f32, 23.245f);
+
+  Builder b;
+  auto id = b.GenerateLiteralIfNeeded(&i);
+  ASSERT_FALSE(b.has_error()) << b.error();
+  EXPECT_EQ(2, id);
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 32
+%2 = OpConstant %1 23.2450008
+)");
+}
+
+TEST_F(BuilderTest, Literal_F32_Dedup) {
+  ast::type::F32Type f32;
+  ast::FloatLiteral i1(&f32, 23.245f);
+  ast::FloatLiteral i2(&f32, 23.245f);
+
+  Builder b;
+  ASSERT_NE(b.GenerateLiteralIfNeeded(&i1), 0);
+  ASSERT_NE(b.GenerateLiteralIfNeeded(&i2), 0);
+  ASSERT_FALSE(b.has_error()) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 32
+%2 = OpConstant %1 23.2450008
+)");
+}
+
+}  // namespace spirv
+}  // namespace writer
+}  // namespace tint
+;
diff --git a/src/writer/wgsl/generator_impl_array_accessor_test.cc b/src/writer/wgsl/generator_impl_array_accessor_test.cc
index 51f7c6c..4fd4c9d 100644
--- a/src/writer/wgsl/generator_impl_array_accessor_test.cc
+++ b/src/writer/wgsl/generator_impl_array_accessor_test.cc
@@ -19,6 +19,7 @@
 #include "src/ast/const_initializer_expression.h"
 #include "src/ast/identifier_expression.h"
 #include "src/ast/int_literal.h"
+#include "src/ast/type/i32_type.h"
 #include "src/writer/wgsl/generator_impl.h"
 
 namespace tint {
@@ -29,7 +30,8 @@
 using GeneratorImplTest = testing::Test;
 
 TEST_F(GeneratorImplTest, EmitExpression_ArrayAccessor) {
-  auto lit = std::make_unique<ast::IntLiteral>(5);
+  ast::type::I32Type i32;
+  auto lit = std::make_unique<ast::IntLiteral>(&i32, 5);
   auto idx = std::make_unique<ast::ConstInitializerExpression>(std::move(lit));
   auto ary = std::make_unique<ast::IdentifierExpression>("ary");
 
diff --git a/src/writer/wgsl/generator_impl_case_test.cc b/src/writer/wgsl/generator_impl_case_test.cc
index 06b6683..d23c785 100644
--- a/src/writer/wgsl/generator_impl_case_test.cc
+++ b/src/writer/wgsl/generator_impl_case_test.cc
@@ -20,6 +20,7 @@
 #include "src/ast/case_statement.h"
 #include "src/ast/identifier_expression.h"
 #include "src/ast/int_literal.h"
+#include "src/ast/type/i32_type.h"
 #include "src/writer/wgsl/generator_impl.h"
 
 namespace tint {
@@ -30,7 +31,8 @@
 using GeneratorImplTest = testing::Test;
 
 TEST_F(GeneratorImplTest, Emit_Case) {
-  auto cond = std::make_unique<ast::IntLiteral>(5);
+  ast::type::I32Type i32;
+  auto cond = std::make_unique<ast::IntLiteral>(&i32, 5);
 
   std::vector<std::unique_ptr<ast::Statement>> body;
   body.push_back(std::make_unique<ast::BreakStatement>());
diff --git a/src/writer/wgsl/generator_impl_initializer_test.cc b/src/writer/wgsl/generator_impl_initializer_test.cc
index 6a6876f..44ae88a 100644
--- a/src/writer/wgsl/generator_impl_initializer_test.cc
+++ b/src/writer/wgsl/generator_impl_initializer_test.cc
@@ -38,7 +38,8 @@
 using GeneratorImplTest = testing::Test;
 
 TEST_F(GeneratorImplTest, EmitInitializer_Bool) {
-  auto lit = std::make_unique<ast::BoolLiteral>(false);
+  ast::type::BoolType bool_type;
+  auto lit = std::make_unique<ast::BoolLiteral>(&bool_type, false);
   ast::ConstInitializerExpression expr(std::move(lit));
 
   GeneratorImpl g;
@@ -47,7 +48,8 @@
 }
 
 TEST_F(GeneratorImplTest, EmitInitializer_Int) {
-  auto lit = std::make_unique<ast::IntLiteral>(-12345);
+  ast::type::I32Type i32;
+  auto lit = std::make_unique<ast::IntLiteral>(&i32, -12345);
   ast::ConstInitializerExpression expr(std::move(lit));
 
   GeneratorImpl g;
@@ -56,7 +58,8 @@
 }
 
 TEST_F(GeneratorImplTest, EmitInitializer_UInt) {
-  auto lit = std::make_unique<ast::UintLiteral>(56779);
+  ast::type::U32Type u32;
+  auto lit = std::make_unique<ast::UintLiteral>(&u32, 56779);
   ast::ConstInitializerExpression expr(std::move(lit));
 
   GeneratorImpl g;
@@ -65,7 +68,8 @@
 }
 
 TEST_F(GeneratorImplTest, EmitInitializer_Float) {
-  auto lit = std::make_unique<ast::FloatLiteral>(1.5e27);
+  ast::type::F32Type f32;
+  auto lit = std::make_unique<ast::FloatLiteral>(&f32, 1.5e27);
   ast::ConstInitializerExpression expr(std::move(lit));
 
   GeneratorImpl g;
@@ -76,7 +80,7 @@
 TEST_F(GeneratorImplTest, EmitInitializer_Type_Float) {
   ast::type::F32Type f32;
 
-  auto lit = std::make_unique<ast::FloatLiteral>(-1.2e-5);
+  auto lit = std::make_unique<ast::FloatLiteral>(&f32, -1.2e-5);
   std::vector<std::unique_ptr<ast::Expression>> values;
   values.push_back(
       std::make_unique<ast::ConstInitializerExpression>(std::move(lit)));
@@ -91,7 +95,7 @@
 TEST_F(GeneratorImplTest, EmitInitializer_Type_Bool) {
   ast::type::BoolType b;
 
-  auto lit = std::make_unique<ast::BoolLiteral>(true);
+  auto lit = std::make_unique<ast::BoolLiteral>(&b, true);
   std::vector<std::unique_ptr<ast::Expression>> values;
   values.push_back(
       std::make_unique<ast::ConstInitializerExpression>(std::move(lit)));
@@ -106,7 +110,7 @@
 TEST_F(GeneratorImplTest, EmitInitializer_Type_Int) {
   ast::type::I32Type i32;
 
-  auto lit = std::make_unique<ast::IntLiteral>(-12345);
+  auto lit = std::make_unique<ast::IntLiteral>(&i32, -12345);
   std::vector<std::unique_ptr<ast::Expression>> values;
   values.push_back(
       std::make_unique<ast::ConstInitializerExpression>(std::move(lit)));
@@ -121,7 +125,7 @@
 TEST_F(GeneratorImplTest, EmitInitializer_Type_Uint) {
   ast::type::U32Type u32;
 
-  auto lit = std::make_unique<ast::UintLiteral>(12345);
+  auto lit = std::make_unique<ast::UintLiteral>(&u32, 12345);
   std::vector<std::unique_ptr<ast::Expression>> values;
   values.push_back(
       std::make_unique<ast::ConstInitializerExpression>(std::move(lit)));
@@ -137,9 +141,9 @@
   ast::type::F32Type f32;
   ast::type::VectorType vec(&f32, 3);
 
-  auto lit1 = std::make_unique<ast::FloatLiteral>(1.f);
-  auto lit2 = std::make_unique<ast::FloatLiteral>(2.f);
-  auto lit3 = std::make_unique<ast::FloatLiteral>(3.f);
+  auto lit1 = std::make_unique<ast::FloatLiteral>(&f32, 1.f);
+  auto lit2 = std::make_unique<ast::FloatLiteral>(&f32, 2.f);
+  auto lit3 = std::make_unique<ast::FloatLiteral>(&f32, 3.f);
   std::vector<std::unique_ptr<ast::Expression>> values;
   values.push_back(
       std::make_unique<ast::ConstInitializerExpression>(std::move(lit1)));
@@ -164,8 +168,8 @@
   std::vector<std::unique_ptr<ast::Expression>> mat_values;
 
   for (size_t i = 0; i < 3; i++) {
-    auto lit1 = std::make_unique<ast::FloatLiteral>(1.f + (i * 2));
-    auto lit2 = std::make_unique<ast::FloatLiteral>(2.f + (i * 2));
+    auto lit1 = std::make_unique<ast::FloatLiteral>(&f32, 1.f + (i * 2));
+    auto lit2 = std::make_unique<ast::FloatLiteral>(&f32, 2.f + (i * 2));
 
     std::vector<std::unique_ptr<ast::Expression>> values;
     values.push_back(
@@ -195,9 +199,9 @@
   std::vector<std::unique_ptr<ast::Expression>> ary_values;
 
   for (size_t i = 0; i < 3; i++) {
-    auto lit1 = std::make_unique<ast::FloatLiteral>(1.f + (i * 3));
-    auto lit2 = std::make_unique<ast::FloatLiteral>(2.f + (i * 3));
-    auto lit3 = std::make_unique<ast::FloatLiteral>(3.f + (i * 3));
+    auto lit1 = std::make_unique<ast::FloatLiteral>(&f32, 1.f + (i * 3));
+    auto lit2 = std::make_unique<ast::FloatLiteral>(&f32, 2.f + (i * 3));
+    auto lit3 = std::make_unique<ast::FloatLiteral>(&f32, 3.f + (i * 3));
 
     std::vector<std::unique_ptr<ast::Expression>> values;
     values.push_back(
diff --git a/src/writer/wgsl/generator_impl_switch_test.cc b/src/writer/wgsl/generator_impl_switch_test.cc
index 22f8f43..af82f6d 100644
--- a/src/writer/wgsl/generator_impl_switch_test.cc
+++ b/src/writer/wgsl/generator_impl_switch_test.cc
@@ -21,6 +21,7 @@
 #include "src/ast/identifier_expression.h"
 #include "src/ast/int_literal.h"
 #include "src/ast/switch_statement.h"
+#include "src/ast/type/i32_type.h"
 #include "src/writer/wgsl/generator_impl.h"
 
 namespace tint {
@@ -36,7 +37,8 @@
   def_body.push_back(std::make_unique<ast::BreakStatement>());
   def->set_body(std::move(def_body));
 
-  auto case_val = std::make_unique<ast::IntLiteral>(5);
+  ast::type::I32Type i32;
+  auto case_val = std::make_unique<ast::IntLiteral>(&i32, 5);
   std::vector<std::unique_ptr<ast::Statement>> case_body;
   case_body.push_back(std::make_unique<ast::BreakStatement>());