Просмотр исходного кода

Separate constant emission from function emission. (#3916)

Make constant emission non-recursive, and stop building a bogus
FunctionContext to emit constants.

To support this, move `InstConstantKind` from the typed instruction
definition into the `.def` file, and add more macros to allow us to
generate case labels based on whether an instruction is a constant.

---------

Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
Co-authored-by: Carbon Infra Bot <carbon-external-infra@google.com>
Richard Smith 2 лет назад
Родитель
Сommit
79c0b65288
36 измененных файлов с 625 добавлено и 469 удалено
  1. 1 0
      toolchain/check/context.cpp
  2. 2 0
      toolchain/lower/BUILD
  3. 271 0
      toolchain/lower/constant.cpp
  4. 22 0
      toolchain/lower/constant.h
  5. 33 38
      toolchain/lower/file_context.cpp
  6. 8 5
      toolchain/lower/file_context.h
  7. 10 4
      toolchain/lower/function_context.cpp
  8. 3 1
      toolchain/lower/function_context.h
  9. 0 62
      toolchain/lower/handle.cpp
  10. 11 50
      toolchain/lower/handle_aggregates.cpp
  11. 11 54
      toolchain/lower/handle_type.cpp
  12. 2 2
      toolchain/lower/testdata/array/assign_return_value.carbon
  13. 8 11
      toolchain/lower/testdata/array/base.carbon
  14. 2 2
      toolchain/lower/testdata/array/function_param.carbon
  15. 4 4
      toolchain/lower/testdata/basics/numeric_literals.carbon
  16. 2 5
      toolchain/lower/testdata/class/adapt.carbon
  17. 2 3
      toolchain/lower/testdata/class/base.carbon
  18. 2 2
      toolchain/lower/testdata/function/call/struct_param.carbon
  19. 2 2
      toolchain/lower/testdata/function/call/tuple_param.carbon
  20. 3 3
      toolchain/lower/testdata/function/call/tuple_param_with_return_slot.carbon
  21. 4 5
      toolchain/lower/testdata/index/array_element_access.carbon
  22. 2 7
      toolchain/lower/testdata/index/tuple_element_access.carbon
  23. 2 2
      toolchain/lower/testdata/index/tuple_return_value_access.carbon
  24. 4 6
      toolchain/lower/testdata/let/tuple.carbon
  25. 2 5
      toolchain/lower/testdata/operators/assignment.carbon
  26. 2 5
      toolchain/lower/testdata/pointer/address_of_field.carbon
  27. 2 5
      toolchain/lower/testdata/struct/member_access.carbon
  28. 2 5
      toolchain/lower/testdata/struct/two_entries.carbon
  29. 3 0
      toolchain/lower/testdata/tuple/one_entry.carbon
  30. 2 2
      toolchain/lower/testdata/tuple/two_entries.carbon
  31. 5 0
      toolchain/sem_ir/constant.h
  32. 1 0
      toolchain/sem_ir/file.cpp
  33. 7 1
      toolchain/sem_ir/inst_kind.cpp
  34. 124 81
      toolchain/sem_ir/inst_kind.def
  35. 3 26
      toolchain/sem_ir/inst_kind.h
  36. 61 71
      toolchain/sem_ir/typed_insts.h

+ 1 - 0
toolchain/check/context.cpp

@@ -941,6 +941,7 @@ class TypeCompleter {
       -> SemIR::ValueRepr {
     CARBON_KIND_SWITCH(inst) {
 #define CARBON_SEM_IR_INST_KIND_TYPE(...)
+#define CARBON_SEM_IR_INST_KIND_MAYBE_TYPE(...)
 #define CARBON_SEM_IR_INST_KIND(Name) case SemIR::Name::Kind:
 #include "toolchain/sem_ir/inst_kind.def"
       CARBON_FATAL() << "Type refers to non-type inst " << inst;

+ 2 - 0
toolchain/lower/BUILD

@@ -28,6 +28,8 @@ cc_library(
 cc_library(
     name = "context",
     srcs = [
+        "constant.cpp",
+        "constant.h",
         "file_context.cpp",
         "function_context.cpp",
     ] +

+ 271 - 0
toolchain/lower/constant.cpp

@@ -0,0 +1,271 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Value.h"
+#include "toolchain/lower/file_context.h"
+#include "toolchain/sem_ir/inst.h"
+
+namespace Carbon::Lower {
+
+// Context and shared functionality for lowering constant values.
+class ConstantContext {
+ public:
+  explicit ConstantContext(FileContext& file_context,
+                           llvm::MutableArrayRef<llvm::Constant*> constants)
+      : file_context_(&file_context), constants_(constants) {}
+
+  // Gets the lowered constant value for an instruction, which must have a
+  // constant value that has already been lowered.
+  auto GetConstant(SemIR::InstId inst_id) const -> llvm::Constant* {
+    return GetConstant(file_context_->sem_ir().constant_values().Get(inst_id));
+  }
+
+  // Gets the lowered constant value for a constant that has already been
+  // lowered.
+  auto GetConstant(SemIR::ConstantId const_id) const -> llvm::Constant* {
+    CARBON_CHECK(const_id.is_template() && const_id.inst_id().index >= 0)
+        << "Unexpected constant ID " << const_id;
+    CARBON_CHECK(const_id.inst_id().index <= last_lowered_constant_index_)
+        << "Queried constant " << const_id << " that has not been lowered yet";
+    return constants_[const_id.inst_id().index];
+  }
+
+  // Returns a constant for the case of a value that should never be used.
+  auto GetUnusedConstant(SemIR::TypeId /*type_id*/) const -> llvm::Constant* {
+    // TODO: Consider using a poison value of the appropriate type.
+    return nullptr;
+  }
+
+  // Gets a callable's function. Returns nullptr for a builtin.
+  auto GetFunction(SemIR::FunctionId function_id) -> llvm::Function* {
+    return file_context_->GetFunction(function_id);
+  }
+
+  // Returns a lowered type for the given type_id.
+  auto GetType(SemIR::TypeId type_id) const -> llvm::Type* {
+    return file_context_->GetType(type_id);
+  }
+
+  // Returns a lowered value to use for a value of type `type`.
+  auto GetTypeAsValue() const -> llvm::Constant* {
+    return file_context_->GetTypeAsValue();
+  }
+
+  // Sets the index of the constant we most recently lowered. This is used to
+  // check we don't look at constants that we've not lowered yet.
+  auto SetLastLoweredConstantIndex(int32_t index) {
+    last_lowered_constant_index_ = index;
+  }
+
+  auto llvm_context() const -> llvm::LLVMContext& {
+    return file_context_->llvm_context();
+  }
+  auto llvm_module() const -> llvm::Module& {
+    return file_context_->llvm_module();
+  }
+  auto sem_ir() const -> const SemIR::File& { return file_context_->sem_ir(); }
+
+ private:
+  FileContext* file_context_;
+  llvm::MutableArrayRef<llvm::Constant*> constants_;
+  int32_t last_lowered_constant_index_ = -1;
+};
+
+// For each instruction kind that can produce a constant, there is a function
+// below to convert it to an `llvm::Constant*`:
+//
+// auto Emit<InstKind>AsConstant(ConstantContext& context,
+//                               SemIR::<InstKind> inst) -> llvm::Constant*;
+
+// For constants that are always of type `type`, produce the trivial runtime
+// representation of type `type`.
+#define CARBON_SEM_IR_INST_KIND_NOT_TYPE(...)
+#define CARBON_SEM_IR_INST_KIND_MAYBE_TYPE(...)
+#define CARBON_SEM_IR_INST_KIND_CONSTANT_SYMBOLIC_ONLY(...)
+#define CARBON_SEM_IR_INST_KIND(Name)                                      \
+  static auto Emit##Name##AsConstant(                                      \
+      ConstantContext& context, SemIR::Name /*inst*/) -> llvm::Constant* { \
+    return context.GetTypeAsValue();                                       \
+  }
+#include "toolchain/sem_ir/inst_kind.def"
+
+// Emits an aggregate constant of LLVM type `Type` whose elements are the
+// contents of `refs_id`.
+template <typename ConstantType, typename Type>
+static auto EmitAggregateConstant(ConstantContext& context,
+                                  SemIR::InstBlockId refs_id, Type* llvm_type)
+    -> llvm::Constant* {
+  auto refs = context.sem_ir().inst_blocks().Get(refs_id);
+  llvm::SmallVector<llvm::Constant*> elements;
+  elements.reserve(refs.size());
+  for (auto ref : refs) {
+    elements.push_back(context.GetConstant(ref));
+  }
+
+  return ConstantType::get(llvm_type, elements);
+}
+
+static auto EmitStructValueAsConstant(ConstantContext& context,
+                                      SemIR::StructValue inst)
+    -> llvm::Constant* {
+  return EmitAggregateConstant<llvm::ConstantStruct>(
+      context, inst.elements_id,
+      cast<llvm::StructType>(context.GetType(inst.type_id)));
+}
+
+static auto EmitTupleValueAsConstant(ConstantContext& context,
+                                     SemIR::TupleValue inst)
+    -> llvm::Constant* {
+  // TODO: Add an ArrayValue instruction and stop using TupleValues to represent
+  // array constants.
+  if (context.sem_ir().types().Is<SemIR::ArrayType>(inst.type_id)) {
+    return EmitAggregateConstant<llvm::ConstantArray>(
+        context, inst.elements_id,
+        cast<llvm::ArrayType>(context.GetType(inst.type_id)));
+  }
+
+  return EmitAggregateConstant<llvm::ConstantStruct>(
+      context, inst.elements_id,
+      cast<llvm::StructType>(context.GetType(inst.type_id)));
+}
+
+static auto EmitAddrOfAsConstant(ConstantContext& /*context*/,
+                                 SemIR::AddrOf /*inst*/) -> llvm::Constant* {
+  // TODO: Constant lvalue support. For now we have no constant lvalues, so we
+  // should never form a constant AddrOf.
+  CARBON_FATAL() << "AddrOf constants not supported yet";
+}
+
+static auto EmitAssociatedEntityAsConstant(ConstantContext& context,
+                                           SemIR::AssociatedEntity inst)
+    -> llvm::Constant* {
+  return context.GetUnusedConstant(inst.type_id);
+}
+
+static auto EmitBaseDeclAsConstant(ConstantContext& context,
+                                   SemIR::BaseDecl inst) -> llvm::Constant* {
+  return context.GetUnusedConstant(inst.type_id);
+}
+
+static auto EmitBoolLiteralAsConstant(ConstantContext& context,
+                                      SemIR::BoolLiteral inst)
+    -> llvm::Constant* {
+  return llvm::ConstantInt::get(llvm::Type::getInt1Ty(context.llvm_context()),
+                                inst.value.index);
+}
+
+static auto EmitBoundMethodAsConstant(ConstantContext& context,
+                                      SemIR::BoundMethod inst)
+    -> llvm::Constant* {
+  // Propagate just the function; the object is separately provided to the
+  // enclosing call as an implicit argument.
+  return context.GetConstant(inst.function_id);
+}
+
+static auto EmitFieldDeclAsConstant(ConstantContext& context,
+                                    SemIR::FieldDecl inst) -> llvm::Constant* {
+  return context.GetUnusedConstant(inst.type_id);
+}
+
+static auto EmitFloatLiteralAsConstant(ConstantContext& context,
+                                       SemIR::FloatLiteral inst)
+    -> llvm::Constant* {
+  const llvm::APFloat& value = context.sem_ir().floats().Get(inst.float_id);
+  return llvm::ConstantFP::get(context.GetType(inst.type_id), value);
+}
+
+static auto EmitFunctionDeclAsConstant(ConstantContext& context,
+                                       SemIR::FunctionDecl inst)
+    -> llvm::Constant* {
+  // TODO: Add a FunctionValue that FunctionDecl evaluates to, and remove this.
+  return context.GetFunction(inst.function_id);
+}
+
+static auto EmitInterfaceWitnessAsConstant(ConstantContext& context,
+                                           SemIR::InterfaceWitness inst)
+    -> llvm::Constant* {
+  // TODO: For dynamic dispatch, we might want to lower witness tables as
+  // constants.
+  return context.GetUnusedConstant(inst.type_id);
+}
+
+static auto EmitIntLiteralAsConstant(ConstantContext& context,
+                                     SemIR::IntLiteral inst)
+    -> llvm::Constant* {
+  return llvm::ConstantInt::get(context.GetType(inst.type_id),
+                                context.sem_ir().ints().Get(inst.int_id));
+}
+
+static auto EmitNamespaceAsConstant(ConstantContext& context,
+                                    SemIR::Namespace inst) -> llvm::Constant* {
+  return context.GetUnusedConstant(inst.type_id);
+}
+
+static auto EmitRealLiteralAsConstant(ConstantContext& context,
+                                      SemIR::RealLiteral inst)
+    -> llvm::Constant* {
+  const Real& real = context.sem_ir().reals().Get(inst.real_id);
+  // TODO: This will probably have overflow issues, and should be fixed.
+  double val =
+      real.mantissa.getZExtValue() *
+      std::pow((real.is_decimal ? 10 : 2), real.exponent.getSExtValue());
+  llvm::APFloat llvm_val(val);
+  return llvm::ConstantFP::get(context.GetType(inst.type_id), llvm_val);
+}
+
+static auto EmitStringLiteralAsConstant(ConstantContext& /*context*/,
+                                        SemIR::StringLiteral inst)
+    -> llvm::Constant* {
+  CARBON_FATAL() << "TODO: Add support: " << inst;
+}
+
+static auto EmitStructTypeFieldAsConstant(ConstantContext& /*context*/,
+                                          SemIR::StructTypeField /*inst*/)
+    -> llvm::Constant* {
+  // A StructTypeField isn't a value, so this constant value won't ever be used.
+  // It also doesn't even have a type, so we can't use GetUnusedConstant.
+  return nullptr;
+}
+
+auto LowerConstants(FileContext& file_context,
+                    llvm::MutableArrayRef<llvm::Constant*> constants) -> void {
+  ConstantContext context(file_context, constants);
+  // Lower each constant in InstId order. This guarantees we lower the
+  // dependencies of a constant before we lower the constant itself.
+  for (auto [inst_id_val, const_id] :
+       llvm::enumerate(file_context.sem_ir().constant_values().array_ref())) {
+    if (!const_id.is_valid() || !const_id.is_template()) {
+      // We are only interested in lowering template constants.
+      continue;
+    }
+
+    if (const_id.inst_id().index != static_cast<int32_t>(inst_id_val)) {
+      // This isn't the instruction that defines the constant.
+      continue;
+    }
+
+    auto inst = file_context.sem_ir().insts().Get(const_id.inst_id());
+    llvm::Constant* value = nullptr;
+    switch (inst.kind()) {
+#define CARBON_SEM_IR_INST_KIND_CONSTANT_NEVER(...)
+#define CARBON_SEM_IR_INST_KIND_CONSTANT_SYMBOLIC_ONLY(...)
+#define CARBON_SEM_IR_INST_KIND(Name)                                \
+  case SemIR::Name::Kind:                                            \
+    value = Emit##Name##AsConstant(context, inst.As<SemIR::Name>()); \
+    break;
+#include "toolchain/sem_ir/inst_kind.def"
+
+      default:
+        CARBON_FATAL() << "Unexpected constant instruction kind " << inst;
+    }
+
+    constants[const_id.inst_id().index] = value;
+    context.SetLastLoweredConstantIndex(const_id.inst_id().index);
+  }
+}
+
+}  // namespace Carbon::Lower

+ 22 - 0
toolchain/lower/constant.h

@@ -0,0 +1,22 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef CARBON_TOOLCHAIN_LOWER_CONSTANT_H_
+#define CARBON_TOOLCHAIN_LOWER_CONSTANT_H_
+
+#include "llvm/ADT/ArrayRef.h"
+#include "toolchain/lower/file_context.h"
+
+namespace Carbon::Lower {
+
+// Forms LLVM constant values for all constants used in the file described by
+// `file_context`. The indexes in the `constants` array corresponding to
+// template constant instructions are populated with corresponding constant
+// values.
+auto LowerConstants(FileContext& file_context,
+                    llvm::MutableArrayRef<llvm::Constant*> constants) -> void;
+
+}  // namespace Carbon::Lower
+
+#endif  // CARBON_TOOLCHAIN_LOWER_CONSTANT_H_

+ 33 - 38
toolchain/lower/file_context.cpp

@@ -8,6 +8,7 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/Sequence.h"
 #include "toolchain/base/kind_switch.h"
+#include "toolchain/lower/constant.h"
 #include "toolchain/lower/function_context.h"
 #include "toolchain/sem_ir/entry_point.h"
 #include "toolchain/sem_ir/file.h"
@@ -50,6 +51,10 @@ auto FileContext::Run() -> std::unique_ptr<llvm::Module> {
 
   // TODO: Lower global variable declarations.
 
+  // Lower constants.
+  constants_.resize(sem_ir_->insts().size());
+  LowerConstants(*this, constants_);
+
   // Lower function definitions.
   for (auto i : llvm::seq(sem_ir_->functions().size())) {
     BuildFunctionDefinition(SemIR::FunctionId(i));
@@ -61,49 +66,38 @@ auto FileContext::Run() -> std::unique_ptr<llvm::Module> {
 }
 
 auto FileContext::GetGlobal(SemIR::InstId inst_id) -> llvm::Value* {
-  auto const_id = sem_ir().constant_values().Get(inst_id);
-  if (const_id.is_constant()) {
-    inst_id = const_id.inst_id();
-  }
-
-  // All builtins are types, with the same empty lowered value.
-  if (inst_id.is_builtin()) {
-    return GetTypeAsValue();
-  }
-
-  // TODO: Add a FunctionValue that FunctionDecl evaluates to, and remove this.
-  auto target = sem_ir().insts().Get(inst_id);
-  if (auto function_decl = target.TryAs<SemIR::FunctionDecl>()) {
-    return GetFunction(function_decl->function_id);
-  }
+  auto inst = sem_ir().insts().Get(inst_id);
 
-  if (target.Is<SemIR::AssociatedEntity>() || target.Is<SemIR::FieldDecl>() ||
-      target.Is<SemIR::BaseDecl>()) {
-    return llvm::ConstantStruct::getAnon(llvm_context(), {});
-  }
+  auto const_id = sem_ir().constant_values().Get(inst_id);
+  if (const_id.is_template()) {
+    // For value expressions and initializing expressions, the value produced by
+    // a constant instruction is a value representation of the constant. For
+    // initializing expressions, `FinishInit` will perform a copy if needed.
+    // TODO: Handle reference expression constants.
+    auto* const_value = constants_[const_id.inst_id().index];
+
+    // If we want a pointer to the constant, materialize a global to hold it.
+    // TODO: We could reuse the same global if the constant is used more than
+    // once.
+    auto value_rep = SemIR::GetValueRepr(sem_ir(), inst.type_id());
+    if (value_rep.kind == SemIR::ValueRepr::Pointer) {
+      llvm::StringRef name =
+          inst_namer_ ? inst_namer_->GetUnscopedNameFor(inst_id) : "";
+      llvm::StringRef sep = (name.empty() || name[0] == '.') ? "" : ".";
+      return new llvm::GlobalVariable(
+          llvm_module(), GetType(sem_ir().GetPointeeType(value_rep.type_id)),
+          /*isConstant=*/true, llvm::GlobalVariable::InternalLinkage,
+          const_value, "const" + sep + name);
+    }
 
-  if (target.type_id() == SemIR::TypeId::TypeType) {
-    return GetTypeAsValue();
+    // Otherwise, we can use the constant value directly.
+    return const_value;
   }
 
-  auto constant_id = sem_ir().constant_values().Get(inst_id);
-  if (constant_id.is_constant()) {
-    if (auto function_decl = sem_ir().insts().TryGetAs<SemIR::FunctionDecl>(
-            constant_id.inst_id())) {
-      return GetFunction(function_decl->function_id);
-    }
-    auto* value = globals_.lookup(constant_id.inst_id());
-    if (!value) {
-      // TODO: Less-hacky constant lowering.
-      FunctionContext ctx(*this, nullptr, vlog_stream_);
-      ctx.LowerInst(constant_id.inst_id());
-      value = ctx.GetValue(constant_id.inst_id());
-      globals_.insert({constant_id.inst_id(), value});
-    }
-    return value;
-  }
+  // TODO: For generics, handle references to symbolic constants.
 
-  CARBON_FATAL() << "Missing value: " << inst_id << " " << target;
+  CARBON_FATAL() << "Missing value: " << inst_id << " "
+                 << sem_ir().insts().Get(inst_id);
 }
 
 auto FileContext::BuildFunctionDecl(SemIR::FunctionId function_id)
@@ -389,6 +383,7 @@ auto FileContext::BuildType(SemIR::InstId inst_id) -> llvm::Type* {
     }
 
 #define CARBON_SEM_IR_INST_KIND_TYPE(...)
+#define CARBON_SEM_IR_INST_KIND_MAYBE_TYPE(...)
 #define CARBON_SEM_IR_INST_KIND(Name) case SemIR::Name::Kind:
 #include "toolchain/sem_ir/inst_kind.def"
       CARBON_FATAL() << "Cannot use inst as type: " << inst_id << " "

+ 8 - 5
toolchain/lower/file_context.h

@@ -42,7 +42,7 @@ class FileContext {
   }
 
   // Returns a lowered value to use for a value of type `type`.
-  auto GetTypeAsValue() -> llvm::Value* {
+  auto GetTypeAsValue() -> llvm::Constant* {
     return llvm::ConstantStruct::get(GetTypeType());
   }
 
@@ -91,16 +91,19 @@ class FileContext {
 
   // Maps callables to lowered functions. SemIR treats callables as the
   // canonical form of a function, so lowering needs to do the same.
-  llvm::SmallVector<llvm::Function*> functions_;
+  // We resize this directly to the (often large) correct size.
+  llvm::SmallVector<llvm::Function*, 0> functions_;
 
   // Provides lowered versions of types.
-  llvm::SmallVector<llvm::Type*> types_;
+  // We resize this directly to the (often large) correct size.
+  llvm::SmallVector<llvm::Type*, 0> types_;
 
   // Lowered version of the builtin type `type`.
   llvm::StructType* type_type_ = nullptr;
 
-  // Maps global instructions to their lowered values.
-  llvm::DenseMap<SemIR::InstId, llvm::Value*> globals_;
+  // Maps constants to their lowered values.
+  // We resize this directly to the (often large) correct size.
+  llvm::SmallVector<llvm::Constant*, 0> constants_;
 };
 
 }  // namespace Carbon::Lower

+ 10 - 4
toolchain/lower/function_context.cpp

@@ -47,24 +47,30 @@ auto FunctionContext::TryToReuseBlock(SemIR::InstBlockId block_id,
 
 auto FunctionContext::LowerBlock(SemIR::InstBlockId block_id) -> void {
   for (auto inst_id : sem_ir().inst_blocks().Get(block_id)) {
-    // Skip over constants. `FileContext::GetGlobal` lowers them as needed.
-    if (sem_ir().constant_values().Get(inst_id).is_constant()) {
-      continue;
-    }
     LowerInst(inst_id);
   }
 }
 
 auto FunctionContext::LowerInst(SemIR::InstId inst_id) -> void {
+  // Skip over constants. `FileContext::GetGlobal` lowers them as needed.
+  if (sem_ir().constant_values().Get(inst_id).is_constant()) {
+    return;
+  }
+
   auto inst = sem_ir().insts().Get(inst_id);
   CARBON_VLOG() << "Lowering " << inst_id << ": " << inst << "\n";
   builder_.getInserter().SetCurrentInstId(inst_id);
   switch (inst.kind()) {
+#define CARBON_SEM_IR_INST_KIND_CONSTANT_ALWAYS(Name)
 #define CARBON_SEM_IR_INST_KIND(Name)                     \
   case SemIR::Name::Kind:                                 \
     Handle##Name(*this, inst_id, inst.As<SemIR::Name>()); \
     break;
 #include "toolchain/sem_ir/inst_kind.def"
+
+    default:
+      CARBON_FATAL() << "Missing constant value for constant instruction "
+                     << inst;
   }
   builder_.getInserter().SetCurrentInstId(SemIR::InstId::Invalid);
 }

+ 3 - 1
toolchain/lower/function_context.h

@@ -160,7 +160,9 @@ class FunctionContext {
   llvm::DenseMap<SemIR::InstId, llvm::Value*> locals_;
 };
 
-// Declare handlers for each SemIR::File instruction.
+// Declare handlers for each SemIR::File instruction that is not always
+// constant.
+#define CARBON_SEM_IR_INST_KIND_CONSTANT_ALWAYS(Name)
 #define CARBON_SEM_IR_INST_KIND(Name)                                \
   auto Handle##Name(FunctionContext& context, SemIR::InstId inst_id, \
                     SemIR::Name inst) -> void;

+ 0 - 62
toolchain/lower/handle.cpp

@@ -77,12 +77,6 @@ auto HandleAssociatedConstantDecl(FunctionContext& /*context*/,
   FatalErrorIfEncountered(inst);
 }
 
-auto HandleAssociatedEntity(FunctionContext& /*context*/,
-                            SemIR::InstId /*inst_id*/,
-                            SemIR::AssociatedEntity inst) -> void {
-  FatalErrorIfEncountered(inst);
-}
-
 auto HandleBindAlias(FunctionContext& context, SemIR::InstId inst_id,
                      SemIR::BindAlias inst) -> void {
   auto type_inst_id = context.sem_ir().types().GetInstId(inst.type_id);
@@ -108,13 +102,6 @@ auto HandleBlockArg(FunctionContext& context, SemIR::InstId inst_id,
   context.SetLocal(inst_id, context.GetBlockArg(inst.block_id, inst.type_id));
 }
 
-auto HandleBoolLiteral(FunctionContext& context, SemIR::InstId inst_id,
-                       SemIR::BoolLiteral inst) -> void {
-  llvm::Value* v =
-      llvm::ConstantInt::get(context.builder().getInt1Ty(), inst.value.index);
-  context.SetLocal(inst_id, v);
-}
-
 auto HandleBoundMethod(FunctionContext& context, SemIR::InstId inst_id,
                        SemIR::BoundMethod inst) -> void {
   // Propagate just the function; the object is separately provided to the
@@ -172,11 +159,6 @@ auto HandleBranchWithArg(FunctionContext& context, SemIR::InstId /*inst_id*/,
   context.builder().ClearInsertionPoint();
 }
 
-auto HandleBuiltin(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/,
-                   SemIR::Builtin inst) -> void {
-  CARBON_FATAL() << "TODO: Add support: " << inst;
-}
-
 // Get the predicate to use for an `icmp` instruction generated for the
 // specified builtin.
 static auto GetBuiltinICmpPredicate(SemIR::BuiltinFunctionKind builtin_kind,
@@ -484,18 +466,6 @@ auto HandleDeref(FunctionContext& context, SemIR::InstId inst_id,
   context.SetLocal(inst_id, context.GetValue(inst.pointer_id));
 }
 
-auto HandleFloatLiteral(FunctionContext& context, SemIR::InstId inst_id,
-                        SemIR::FloatLiteral inst) -> void {
-  const llvm::APFloat& value = context.sem_ir().floats().Get(inst.float_id);
-  context.SetLocal(
-      inst_id, llvm::ConstantFP::get(context.builder().getDoubleTy(), value));
-}
-
-auto HandleFunctionDecl(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/,
-                        SemIR::FunctionDecl inst) -> void {
-  FatalErrorIfEncountered(inst);
-}
-
 auto HandleImplDecl(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/,
                     SemIR::ImplDecl inst) -> void {
   FatalErrorIfEncountered(inst);
@@ -547,15 +517,6 @@ auto HandleInterfaceWitnessAccess(FunctionContext& context,
   context.SetLocal(inst_id, context.GetValue(const_id.inst_id()));
 }
 
-auto HandleIntLiteral(FunctionContext& context, SemIR::InstId inst_id,
-                      SemIR::IntLiteral inst) -> void {
-  const llvm::APInt& i = context.sem_ir().ints().Get(inst.int_id);
-  // TODO: This won't offer correct semantics, but seems close enough for now.
-  llvm::Value* v =
-      llvm::ConstantInt::get(context.builder().getInt32Ty(), i.getZExtValue());
-  context.SetLocal(inst_id, v);
-}
-
 auto HandleNameRef(FunctionContext& context, SemIR::InstId inst_id,
                    SemIR::NameRef inst) -> void {
   auto type_inst_id = context.sem_ir().types().GetInstId(inst.type_id);
@@ -566,28 +527,11 @@ auto HandleNameRef(FunctionContext& context, SemIR::InstId inst_id,
   context.SetLocal(inst_id, context.GetValue(inst.value_id));
 }
 
-auto HandleNamespace(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/,
-                     SemIR::Namespace inst) -> void {
-  FatalErrorIfEncountered(inst);
-}
-
 auto HandleParam(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/,
                  SemIR::Param /*inst*/) -> void {
   CARBON_FATAL() << "Parameters should be lowered by `BuildFunctionDefinition`";
 }
 
-auto HandleRealLiteral(FunctionContext& context, SemIR::InstId inst_id,
-                       SemIR::RealLiteral inst) -> void {
-  const Real& real = context.sem_ir().reals().Get(inst.real_id);
-  // TODO: This will probably have overflow issues, and should be fixed.
-  double val =
-      real.mantissa.getZExtValue() *
-      std::pow((real.is_decimal ? 10 : 2), real.exponent.getSExtValue());
-  llvm::APFloat llvm_val(val);
-  context.SetLocal(inst_id, llvm::ConstantFP::get(
-                                context.builder().getDoubleTy(), llvm_val));
-}
-
 auto HandleReturn(FunctionContext& context, SemIR::InstId /*inst_id*/,
                   SemIR::Return /*inst*/) -> void {
   context.builder().CreateRetVoid();
@@ -618,12 +562,6 @@ auto HandleSpliceBlock(FunctionContext& context, SemIR::InstId inst_id,
   context.SetLocal(inst_id, context.GetValue(inst.result_id));
 }
 
-auto HandleStringLiteral(FunctionContext& /*context*/,
-                         SemIR::InstId /*inst_id*/, SemIR::StringLiteral inst)
-    -> void {
-  CARBON_FATAL() << "TODO: Add support: " << inst;
-}
-
 auto HandleUnaryOperatorNot(FunctionContext& context, SemIR::InstId inst_id,
                             SemIR::UnaryOperatorNot inst) -> void {
   context.SetLocal(

+ 11 - 50
toolchain/lower/handle_aggregates.cpp

@@ -148,16 +148,6 @@ auto HandleClassInit(FunctionContext& context, SemIR::InstId inst_id,
                                         "class.init"));
 }
 
-auto HandleBaseDecl(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/,
-                    SemIR::BaseDecl /*inst*/) -> void {
-  // No action to perform.
-}
-
-auto HandleFieldDecl(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/,
-                     SemIR::FieldDecl /*inst*/) -> void {
-  // No action to perform.
-}
-
 auto HandleStructAccess(FunctionContext& context, SemIR::InstId inst_id,
                         SemIR::StructAccess inst) -> void {
   auto struct_type_id = context.sem_ir().insts().Get(inst.struct_id).type_id();
@@ -203,47 +193,18 @@ auto EmitAggregateValueRepr(FunctionContext& context, SemIR::TypeId type_id,
       auto pointee_type_id = context.sem_ir().GetPointeeType(value_rep.type_id);
       auto* llvm_value_rep_type = context.GetType(pointee_type_id);
 
-      auto refs = context.sem_ir().inst_blocks().Get(refs_id);
-      if (context.builder().GetInsertBlock()) {
-        // Write the value representation to a local alloca so we can produce a
-        // pointer to it as the value representation of the struct or tuple.
-        auto* alloca =
-            context.builder().CreateAlloca(llvm_value_rep_type,
-                                           /*ArraySize=*/nullptr, name);
-        for (auto [i, ref] : llvm::enumerate(refs)) {
-          context.builder().CreateStore(context.GetValue(ref),
-                                        context.builder().CreateStructGEP(
-                                            llvm_value_rep_type, alloca, i));
-        }
-        return alloca;
-      } else {
-        // TODO: Move this out to a separate constant lowering file.
-        llvm::SmallVector<llvm::Constant*> elements;
-        elements.reserve(refs.size());
-        for (auto ref : refs) {
-          auto ref_value_rep = SemIR::GetValueRepr(
-              context.sem_ir(), context.sem_ir().insts().Get(ref).type_id());
-          auto* inner_value = llvm::cast<llvm::Constant>(context.GetValue(ref));
-          if (ref_value_rep.kind == SemIR::ValueRepr::Pointer) {
-            inner_value =
-                llvm::cast<llvm::GlobalVariable>(inner_value)->getInitializer();
-          }
-          elements.push_back(inner_value);
-        }
-        llvm::Constant* value;
-        if (auto* struct_type =
-                llvm::dyn_cast<llvm::StructType>(llvm_value_rep_type)) {
-          value = llvm::ConstantStruct::get(struct_type, elements);
-        } else if (auto* array_type =
-                       llvm::dyn_cast<llvm::ArrayType>(llvm_value_rep_type)) {
-          value = llvm::ConstantArray::get(array_type, elements);
-        } else {
-          CARBON_FATAL() << "Unknown aggregate value representation";
-        }
-        return new llvm::GlobalVariable(
-            context.llvm_module(), llvm_value_rep_type, /*isConstant=*/true,
-            llvm::GlobalVariable::InternalLinkage, value, name);
+      // Write the value representation to a local alloca so we can produce a
+      // pointer to it as the value representation of the struct or tuple.
+      auto* alloca =
+          context.builder().CreateAlloca(llvm_value_rep_type,
+                                         /*ArraySize=*/nullptr, name);
+      for (auto [i, ref] :
+           llvm::enumerate(context.sem_ir().inst_blocks().Get(refs_id))) {
+        context.builder().CreateStore(
+            context.GetValue(ref),
+            context.builder().CreateStructGEP(llvm_value_rep_type, alloca, i));
       }
+      return alloca;
     }
 
     case SemIR::ValueRepr::Custom:

+ 11 - 54
toolchain/lower/handle_type.cpp

@@ -7,64 +7,21 @@
 
 namespace Carbon::Lower {
 
-auto HandleArrayType(FunctionContext& context, SemIR::InstId inst_id,
-                     SemIR::ArrayType /*inst*/) -> void {
-  context.SetLocal(inst_id, context.GetTypeAsValue());
-}
-
-auto HandleAssociatedEntityType(FunctionContext& context, SemIR::InstId inst_id,
-                                SemIR::AssociatedEntityType /*inst*/) -> void {
-  context.SetLocal(inst_id, context.GetTypeAsValue());
-}
-
-auto HandleClassType(FunctionContext& context, SemIR::InstId inst_id,
-                     SemIR::ClassType /*inst*/) -> void {
-  context.SetLocal(inst_id, context.GetTypeAsValue());
-}
-
-auto HandleConstType(FunctionContext& context, SemIR::InstId inst_id,
-                     SemIR::ConstType /*inst*/) -> void {
-  context.SetLocal(inst_id, context.GetTypeAsValue());
-}
+// For instructions that are always of type `type`, produce the trivial runtime
+// representation of type `type`.
+#define CARBON_SEM_IR_INST_KIND_NOT_TYPE(...)
+#define CARBON_SEM_IR_INST_KIND_MAYBE_TYPE(...)
+#define CARBON_SEM_IR_INST_KIND_CONSTANT_ALWAYS(...)
+#define CARBON_SEM_IR_INST_KIND(Name)                                \
+  auto Handle##Name(FunctionContext& context, SemIR::InstId inst_id, \
+                    SemIR::Name /*inst*/) -> void {                  \
+    context.SetLocal(inst_id, context.GetTypeAsValue());             \
+  }
+#include "toolchain/sem_ir/inst_kind.def"
 
 auto HandleFacetTypeAccess(FunctionContext& context, SemIR::InstId inst_id,
                            SemIR::FacetTypeAccess /*inst*/) -> void {
   context.SetLocal(inst_id, context.GetTypeAsValue());
 }
 
-auto HandleFloatType(FunctionContext& context, SemIR::InstId inst_id,
-                     SemIR::FloatType /*inst*/) -> void {
-  context.SetLocal(inst_id, context.GetTypeAsValue());
-}
-
-auto HandleInterfaceType(FunctionContext& context, SemIR::InstId inst_id,
-                         SemIR::InterfaceType /*inst*/) -> void {
-  context.SetLocal(inst_id, context.GetTypeAsValue());
-}
-
-auto HandleIntType(FunctionContext& context, SemIR::InstId inst_id,
-                   SemIR::IntType /*inst*/) -> void {
-  context.SetLocal(inst_id, context.GetTypeAsValue());
-}
-
-auto HandlePointerType(FunctionContext& context, SemIR::InstId inst_id,
-                       SemIR::PointerType /*inst*/) -> void {
-  context.SetLocal(inst_id, context.GetTypeAsValue());
-}
-
-auto HandleStructType(FunctionContext& context, SemIR::InstId inst_id,
-                      SemIR::StructType /*inst*/) -> void {
-  context.SetLocal(inst_id, context.GetTypeAsValue());
-}
-
-auto HandleTupleType(FunctionContext& context, SemIR::InstId inst_id,
-                     SemIR::TupleType /*inst*/) -> void {
-  context.SetLocal(inst_id, context.GetTypeAsValue());
-}
-
-auto HandleUnboundElementType(FunctionContext& context, SemIR::InstId inst_id,
-                              SemIR::UnboundElementType /*inst*/) -> void {
-  context.SetLocal(inst_id, context.GetTypeAsValue());
-}
-
 }  // namespace Carbon::Lower

+ 2 - 2
toolchain/lower/testdata/array/assign_return_value.carbon

@@ -13,13 +13,13 @@ fn Run() {
 // CHECK:STDOUT: ; ModuleID = 'assign_return_value.carbon'
 // CHECK:STDOUT: source_filename = "assign_return_value.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: @tuple = internal constant { i32, i32 } { i32 12, i32 24 }
+// CHECK:STDOUT: @const.loc7_39 = internal constant { i32, i32 } { i32 12, i32 24 }
 // CHECK:STDOUT:
 // CHECK:STDOUT: define void @F(ptr sret({ i32, i32 }) %return) {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %.loc7_38.2.tuple.elem = getelementptr inbounds { i32, i32 }, ptr %return, i32 0, i32 0
 // CHECK:STDOUT:   %.loc7_38.4.tuple.elem = getelementptr inbounds { i32, i32 }, ptr %return, i32 0, i32 1
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %return, ptr align 4 @tuple, i64 8, i1 false)
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %return, ptr align 4 @const.loc7_39, i64 8, i1 false)
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 8 - 11
toolchain/lower/testdata/array/base.carbon

@@ -15,32 +15,32 @@ fn Run() {
 // CHECK:STDOUT: ; ModuleID = 'base.carbon'
 // CHECK:STDOUT: source_filename = "base.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: @tuple = internal constant [1 x i32] [i32 1]
-// CHECK:STDOUT: @tuple.1 = internal constant [2 x double] [double 0x4026333333333334, double 2.200000e+00]
-// CHECK:STDOUT: @tuple.2 = internal constant [5 x {}] poison
-// CHECK:STDOUT: @tuple.3 = internal constant { i32, i32, i32 } { i32 1, i32 2, i32 3 }
+// CHECK:STDOUT: @const.loc8_25 = internal constant [1 x i32] [i32 1]
+// CHECK:STDOUT: @const.loc9_33 = internal constant [2 x double] [double 0x4026333333333334, double 2.200000e+00]
+// CHECK:STDOUT: @const.loc10_41 = internal constant [5 x {}] zeroinitializer
+// CHECK:STDOUT: @const.loc11_37 = internal constant { i32, i32, i32 } { i32 1, i32 2, i32 3 }
 // CHECK:STDOUT:
 // CHECK:STDOUT: define void @main() {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %a.var = alloca [1 x i32], align 4
 // CHECK:STDOUT:   %.loc8_24.3.array.index = getelementptr inbounds [1 x i32], ptr %a.var, i32 0, i32 0
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a.var, ptr align 4 @tuple, i64 4, i1 false)
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a.var, ptr align 4 @const.loc8_25, i64 4, i1 false)
 // CHECK:STDOUT:   %b.var = alloca [2 x double], align 8
 // CHECK:STDOUT:   %.loc9_32.3.array.index = getelementptr inbounds [2 x double], ptr %b.var, i32 0, i32 0
 // CHECK:STDOUT:   %.loc9_32.6.array.index = getelementptr inbounds [2 x double], ptr %b.var, i32 0, i32 1
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 8 %b.var, ptr align 8 @tuple.1, i64 16, i1 false)
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 8 %b.var, ptr align 8 @const.loc9_33, i64 16, i1 false)
 // CHECK:STDOUT:   %c.var = alloca [5 x {}], align 8
 // CHECK:STDOUT:   %.loc10_40.3.array.index = getelementptr inbounds [5 x {}], ptr %c.var, i32 0, i32 0
 // CHECK:STDOUT:   %.loc10_40.6.array.index = getelementptr inbounds [5 x {}], ptr %c.var, i32 0, i32 1
 // CHECK:STDOUT:   %.loc10_40.9.array.index = getelementptr inbounds [5 x {}], ptr %c.var, i32 0, i32 2
 // CHECK:STDOUT:   %.loc10_40.12.array.index = getelementptr inbounds [5 x {}], ptr %c.var, i32 0, i32 3
 // CHECK:STDOUT:   %.loc10_40.15.array.index = getelementptr inbounds [5 x {}], ptr %c.var, i32 0, i32 4
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 1 %c.var, ptr align 1 @tuple.2, i64 0, i1 false)
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 1 %c.var, ptr align 1 @const.loc10_41, i64 0, i1 false)
 // CHECK:STDOUT:   %d.var = alloca { i32, i32, i32 }, align 8
 // CHECK:STDOUT:   %.loc11_36.2.tuple.elem = getelementptr inbounds { i32, i32, i32 }, ptr %d.var, i32 0, i32 0
 // CHECK:STDOUT:   %.loc11_36.4.tuple.elem = getelementptr inbounds { i32, i32, i32 }, ptr %d.var, i32 0, i32 1
 // CHECK:STDOUT:   %.loc11_36.6.tuple.elem = getelementptr inbounds { i32, i32, i32 }, ptr %d.var, i32 0, i32 2
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %d.var, ptr align 4 @tuple.3, i64 12, i1 false)
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %d.var, ptr align 4 @const.loc11_37, i64 12, i1 false)
 // CHECK:STDOUT:   %e.var = alloca [3 x i32], align 4
 // CHECK:STDOUT:   %.loc12_21.1.tuple.elem = getelementptr inbounds { i32, i32, i32 }, ptr %d.var, i32 0, i32 0
 // CHECK:STDOUT:   %.loc12_21.2 = load i32, ptr %.loc12_21.1.tuple.elem, align 4
@@ -61,9 +61,6 @@ fn Run() {
 // CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; uselistorder directives
-// CHECK:STDOUT: uselistorder i32 1, { 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 3 }
-// CHECK:STDOUT: uselistorder i32 2, { 0, 1, 3, 4, 2 }
-// CHECK:STDOUT: uselistorder i32 3, { 1, 0 }
 // CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 3, 2, 1, 0 }
 // CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }

+ 2 - 2
toolchain/lower/testdata/array/function_param.carbon

@@ -15,7 +15,7 @@ fn G() -> i32 {
 // CHECK:STDOUT: ; ModuleID = 'function_param.carbon'
 // CHECK:STDOUT: source_filename = "function_param.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: @tuple = internal constant [3 x i32] [i32 1, i32 2, i32 3]
+// CHECK:STDOUT: @const.loc12_11.1 = internal constant [3 x i32] [i32 1, i32 2, i32 3]
 // CHECK:STDOUT:
 // CHECK:STDOUT: define i32 @F(ptr %arr, i32 %i) {
 // CHECK:STDOUT: entry:
@@ -30,7 +30,7 @@ fn G() -> i32 {
 // CHECK:STDOUT:   %.loc12_20.4.array.index = getelementptr inbounds [3 x i32], ptr %.loc12_20.2.temp, i32 0, i32 0
 // CHECK:STDOUT:   %.loc12_20.7.array.index = getelementptr inbounds [3 x i32], ptr %.loc12_20.2.temp, i32 0, i32 1
 // CHECK:STDOUT:   %.loc12_20.10.array.index = getelementptr inbounds [3 x i32], ptr %.loc12_20.2.temp, i32 0, i32 2
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %.loc12_20.2.temp, ptr align 4 @tuple, i64 12, i1 false)
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %.loc12_20.2.temp, ptr align 4 @const.loc12_11.1, i64 12, i1 false)
 // CHECK:STDOUT:   %F.call = call i32 @F(ptr %.loc12_20.2.temp, i32 1)
 // CHECK:STDOUT:   ret i32 %F.call
 // CHECK:STDOUT: }

+ 4 - 4
toolchain/lower/testdata/basics/numeric_literals.carbon

@@ -26,8 +26,8 @@ fn F() {
 // CHECK:STDOUT: ; ModuleID = 'numeric_literals.carbon'
 // CHECK:STDOUT: source_filename = "numeric_literals.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: @tuple = internal constant [4 x i32] [i32 8, i32 9, i32 8, i32 8]
-// CHECK:STDOUT: @tuple.1 = internal constant [6 x double] [double 9.000000e-01, double 8.000000e+00, double 8.000000e+01, double 1.000000e+07, double 1.000000e+08, double 1.000000e-08]
+// CHECK:STDOUT: @const.loc15_4 = internal constant [4 x i32] [i32 8, i32 9, i32 8, i32 8]
+// CHECK:STDOUT: @const.loc23_4 = internal constant [6 x double] [double 9.000000e-01, double 8.000000e+00, double 8.000000e+01, double 1.000000e+07, double 1.000000e+08, double 1.000000e-08]
 // CHECK:STDOUT:
 // CHECK:STDOUT: define void @F() {
 // CHECK:STDOUT: entry:
@@ -36,7 +36,7 @@ fn F() {
 // CHECK:STDOUT:   %.loc15_3.6.array.index = getelementptr inbounds [4 x i32], ptr %ints.var, i32 0, i32 1
 // CHECK:STDOUT:   %.loc15_3.9.array.index = getelementptr inbounds [4 x i32], ptr %ints.var, i32 0, i32 2
 // CHECK:STDOUT:   %.loc15_3.12.array.index = getelementptr inbounds [4 x i32], ptr %ints.var, i32 0, i32 3
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %ints.var, ptr align 4 @tuple, i64 16, i1 false)
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %ints.var, ptr align 4 @const.loc15_4, i64 16, i1 false)
 // CHECK:STDOUT:   %floats.var = alloca [6 x double], align 8
 // CHECK:STDOUT:   %.loc23_3.3.array.index = getelementptr inbounds [6 x double], ptr %floats.var, i32 0, i32 0
 // CHECK:STDOUT:   %.loc23_3.6.array.index = getelementptr inbounds [6 x double], ptr %floats.var, i32 0, i32 1
@@ -44,7 +44,7 @@ fn F() {
 // CHECK:STDOUT:   %.loc23_3.12.array.index = getelementptr inbounds [6 x double], ptr %floats.var, i32 0, i32 3
 // CHECK:STDOUT:   %.loc23_3.15.array.index = getelementptr inbounds [6 x double], ptr %floats.var, i32 0, i32 4
 // CHECK:STDOUT:   %.loc23_3.18.array.index = getelementptr inbounds [6 x double], ptr %floats.var, i32 0, i32 5
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 8 %floats.var, ptr align 8 @tuple.1, i64 48, i1 false)
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 8 %floats.var, ptr align 8 @const.loc23_4, i64 48, i1 false)
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 2 - 5
toolchain/lower/testdata/class/adapt.carbon

@@ -50,13 +50,13 @@ fn DoStuff(a: Int) -> Int {
 // CHECK:STDOUT: ; ModuleID = 'adapt_class.carbon'
 // CHECK:STDOUT: source_filename = "adapt_class.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: @struct = internal constant { i32, i32 } { i32 1, i32 2 }
+// CHECK:STDOUT: @const.loc9_28 = internal constant { i32, i32 } { i32 1, i32 2 }
 // CHECK:STDOUT:
 // CHECK:STDOUT: define void @Make(ptr sret({ i32, i32 }) %return) {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %.loc9_27.2.a = getelementptr inbounds { i32, i32 }, ptr %return, i32 0, i32 0
 // CHECK:STDOUT:   %.loc9_27.4.b = getelementptr inbounds { i32, i32 }, ptr %return, i32 0, i32 1
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %return, ptr align 4 @struct, i64 8, i1 false)
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %return, ptr align 4 @const.loc9_28, i64 8, i1 false)
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -84,9 +84,6 @@ fn DoStuff(a: Int) -> Int {
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
 // CHECK:STDOUT:
-// CHECK:STDOUT: ; uselistorder directives
-// CHECK:STDOUT: uselistorder i32 1, { 0, 1, 3, 2 }
-// CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT: ; ModuleID = 'adapt_int.carbon'
 // CHECK:STDOUT: source_filename = "adapt_int.carbon"

+ 2 - 3
toolchain/lower/testdata/class/base.carbon

@@ -29,15 +29,14 @@ fn Convert(p: Derived*) -> Base* {
 // CHECK:STDOUT: ; ModuleID = 'base.carbon'
 // CHECK:STDOUT: source_filename = "base.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: @struct = internal constant { i32 } { i32 4 }
-// CHECK:STDOUT: @struct.1 = internal constant { { i32 }, i32 } { { i32 } { i32 4 }, i32 7 }
+// CHECK:STDOUT: @const.loc18_36 = internal constant { { i32 }, i32 } { { i32 } { i32 4 }, i32 7 }
 // CHECK:STDOUT:
 // CHECK:STDOUT: define void @Make(ptr sret({ { i32 }, i32 }) %return) {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %.loc18_35.2.base = getelementptr inbounds { { i32 }, i32 }, ptr %return, i32 0, i32 0
 // CHECK:STDOUT:   %.loc18_26.2.b = getelementptr inbounds { i32 }, ptr %.loc18_35.2.base, i32 0, i32 0
 // CHECK:STDOUT:   %.loc18_35.4.d = getelementptr inbounds { { i32 }, i32 }, ptr %return, i32 0, i32 1
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %return, ptr align 4 @struct.1, i64 8, i1 false)
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %return, ptr align 4 @const.loc18_36, i64 8, i1 false)
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 2 - 2
toolchain/lower/testdata/function/call/struct_param.carbon

@@ -13,7 +13,7 @@ fn Main() {
 // CHECK:STDOUT: ; ModuleID = 'struct_param.carbon'
 // CHECK:STDOUT: source_filename = "struct_param.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: @struct = internal constant { i32, i32 } { i32 2, i32 3 }
+// CHECK:STDOUT: @const.loc10_4.3 = internal constant { i32, i32 } { i32 2, i32 3 }
 // CHECK:STDOUT:
 // CHECK:STDOUT: define void @F({ i32 } %b, ptr %c) {
 // CHECK:STDOUT: entry:
@@ -22,6 +22,6 @@ fn Main() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: define void @Main() {
 // CHECK:STDOUT: entry:
-// CHECK:STDOUT:   call void @F({ i32 } { i32 1 }, ptr @struct)
+// CHECK:STDOUT:   call void @F({ i32 } { i32 1 }, ptr @const.loc10_4.3)
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }

+ 2 - 2
toolchain/lower/testdata/function/call/tuple_param.carbon

@@ -13,7 +13,7 @@ fn Main() {
 // CHECK:STDOUT: ; ModuleID = 'tuple_param.carbon'
 // CHECK:STDOUT: source_filename = "tuple_param.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: @tuple = internal constant { i32, i32 } { i32 2, i32 3 }
+// CHECK:STDOUT: @const.loc10_4.3 = internal constant { i32, i32 } { i32 2, i32 3 }
 // CHECK:STDOUT:
 // CHECK:STDOUT: define void @F({ i32 } %b, ptr %c) {
 // CHECK:STDOUT: entry:
@@ -22,6 +22,6 @@ fn Main() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: define void @Main() {
 // CHECK:STDOUT: entry:
-// CHECK:STDOUT:   call void @F({ i32 } { i32 1 }, ptr @tuple)
+// CHECK:STDOUT:   call void @F({ i32 } { i32 1 }, ptr @const.loc10_4.3)
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }

+ 3 - 3
toolchain/lower/testdata/function/call/tuple_param_with_return_slot.carbon

@@ -15,7 +15,7 @@ fn Main() {
 // CHECK:STDOUT: ; ModuleID = 'tuple_param_with_return_slot.carbon'
 // CHECK:STDOUT: source_filename = "tuple_param_with_return_slot.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: @tuple = internal constant { i32, i32 } { i32 2, i32 3 }
+// CHECK:STDOUT: @const.loc12_4.4 = internal constant { i32, i32 } { i32 2, i32 3 }
 // CHECK:STDOUT:
 // CHECK:STDOUT: define void @F(ptr sret({ i32, i32, i32 }) %return, { i32 } %b, ptr %c) {
 // CHECK:STDOUT: entry:
@@ -36,9 +36,9 @@ fn Main() {
 // CHECK:STDOUT: define void @Main() {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %.loc12_4.1.temp = alloca { i32, i32, i32 }, align 8
-// CHECK:STDOUT:   call void @F(ptr %.loc12_4.1.temp, { i32 } { i32 1 }, ptr @tuple)
+// CHECK:STDOUT:   call void @F(ptr %.loc12_4.1.temp, { i32 } { i32 1 }, ptr @const.loc12_4.4)
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; uselistorder directives
-// CHECK:STDOUT: uselistorder i32 2, { 1, 0 }
+// CHECK:STDOUT: uselistorder i32 1, { 3, 0, 1, 2 }

+ 4 - 5
toolchain/lower/testdata/index/array_element_access.carbon

@@ -17,14 +17,14 @@ fn Run() {
 // CHECK:STDOUT: ; ModuleID = 'array_element_access.carbon'
 // CHECK:STDOUT: source_filename = "array_element_access.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: @tuple = internal constant { i32, i32 } { i32 1, i32 2 }
-// CHECK:STDOUT: @tuple.1 = internal constant [2 x i32] [i32 1, i32 2]
+// CHECK:STDOUT: @const.loc6_37 = internal constant { i32, i32 } { i32 1, i32 2 }
+// CHECK:STDOUT: @const.loc8_35 = internal constant [2 x i32] [i32 1, i32 2]
 // CHECK:STDOUT:
 // CHECK:STDOUT: define void @A(ptr sret({ i32, i32 }) %return) {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %.loc6_36.2.tuple.elem = getelementptr inbounds { i32, i32 }, ptr %return, i32 0, i32 0
 // CHECK:STDOUT:   %.loc6_36.4.tuple.elem = getelementptr inbounds { i32, i32 }, ptr %return, i32 0, i32 1
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %return, ptr align 4 @tuple, i64 8, i1 false)
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %return, ptr align 4 @const.loc6_37, i64 8, i1 false)
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -32,7 +32,7 @@ fn Run() {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %.loc8_34.3.array.index = getelementptr inbounds [2 x i32], ptr %return, i32 0, i32 0
 // CHECK:STDOUT:   %.loc8_34.6.array.index = getelementptr inbounds [2 x i32], ptr %return, i32 0, i32 1
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %return, ptr align 4 @tuple.1, i64 8, i1 false)
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %return, ptr align 4 @const.loc8_35, i64 8, i1 false)
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -73,7 +73,6 @@ fn Run() {
 // CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; uselistorder directives
-// CHECK:STDOUT: uselistorder i32 1, { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 11 }
 // CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 1, 0 }
 // CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }

+ 2 - 7
toolchain/lower/testdata/index/tuple_element_access.carbon

@@ -14,7 +14,7 @@ fn Run() -> i32 {
 // CHECK:STDOUT: ; ModuleID = 'tuple_element_access.carbon'
 // CHECK:STDOUT: source_filename = "tuple_element_access.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: @tuple = internal constant { i32, i32, i32 } { i32 0, i32 1, i32 2 }
+// CHECK:STDOUT: @const.loc8_37 = internal constant { i32, i32, i32 } { i32 0, i32 1, i32 2 }
 // CHECK:STDOUT:
 // CHECK:STDOUT: define i32 @main() {
 // CHECK:STDOUT: entry:
@@ -22,7 +22,7 @@ fn Run() -> i32 {
 // CHECK:STDOUT:   %.loc8_36.2.tuple.elem = getelementptr inbounds { i32, i32, i32 }, ptr %a.var, i32 0, i32 0
 // CHECK:STDOUT:   %.loc8_36.4.tuple.elem = getelementptr inbounds { i32, i32, i32 }, ptr %a.var, i32 0, i32 1
 // CHECK:STDOUT:   %.loc8_36.6.tuple.elem = getelementptr inbounds { i32, i32, i32 }, ptr %a.var, i32 0, i32 2
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a.var, ptr align 4 @tuple, i64 12, i1 false)
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a.var, ptr align 4 @const.loc8_37, i64 12, i1 false)
 // CHECK:STDOUT:   %b.var = alloca i32, align 4
 // CHECK:STDOUT:   %.loc9_19.1.tuple.index = getelementptr inbounds { i32, i32, i32 }, ptr %a.var, i32 0, i32 0
 // CHECK:STDOUT:   %.loc9_19.2 = load i32, ptr %.loc9_19.1.tuple.index, align 4
@@ -37,9 +37,4 @@ fn Run() -> i32 {
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
 // CHECK:STDOUT:
-// CHECK:STDOUT: ; uselistorder directives
-// CHECK:STDOUT: uselistorder i32 0, { 0, 1, 2, 3, 5, 6, 7, 8, 4 }
-// CHECK:STDOUT: uselistorder i32 1, { 0, 1, 3, 4, 2 }
-// CHECK:STDOUT: uselistorder i32 2, { 0, 2, 1 }
-// CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }

+ 2 - 2
toolchain/lower/testdata/index/tuple_return_value_access.carbon

@@ -13,13 +13,13 @@ fn Run() {
 // CHECK:STDOUT: ; ModuleID = 'tuple_return_value_access.carbon'
 // CHECK:STDOUT: source_filename = "tuple_return_value_access.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: @tuple = internal constant { i32, i32 } { i32 12, i32 24 }
+// CHECK:STDOUT: @const.loc7_39 = internal constant { i32, i32 } { i32 12, i32 24 }
 // CHECK:STDOUT:
 // CHECK:STDOUT: define void @F(ptr sret({ i32, i32 }) %return) {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %.loc7_38.2.tuple.elem = getelementptr inbounds { i32, i32 }, ptr %return, i32 0, i32 0
 // CHECK:STDOUT:   %.loc7_38.4.tuple.elem = getelementptr inbounds { i32, i32 }, ptr %return, i32 0, i32 1
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %return, ptr align 4 @tuple, i64 8, i1 false)
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %return, ptr align 4 @const.loc7_39, i64 8, i1 false)
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 4 - 6
toolchain/lower/testdata/let/tuple.carbon

@@ -14,8 +14,8 @@ fn F() -> i32 {
 // CHECK:STDOUT: ; ModuleID = 'tuple.carbon'
 // CHECK:STDOUT: source_filename = "tuple.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: @tuple = internal constant { i32, i32, i32 } { i32 1, i32 2, i32 3 }
-// CHECK:STDOUT: @tuple.1 = internal constant { i32, i32 } { i32 4, i32 5 }
+// CHECK:STDOUT: @const.loc8_37 = internal constant { i32, i32, i32 } { i32 1, i32 2, i32 3 }
+// CHECK:STDOUT: @const.loc9_29 = internal constant { i32, i32 } { i32 4, i32 5 }
 // CHECK:STDOUT:
 // CHECK:STDOUT: define i32 @F() {
 // CHECK:STDOUT: entry:
@@ -23,11 +23,11 @@ fn F() -> i32 {
 // CHECK:STDOUT:   %.loc8_36.2.tuple.elem = getelementptr inbounds { i32, i32, i32 }, ptr %a.var, i32 0, i32 0
 // CHECK:STDOUT:   %.loc8_36.4.tuple.elem = getelementptr inbounds { i32, i32, i32 }, ptr %a.var, i32 0, i32 1
 // CHECK:STDOUT:   %.loc8_36.6.tuple.elem = getelementptr inbounds { i32, i32, i32 }, ptr %a.var, i32 0, i32 2
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a.var, ptr align 4 @tuple, i64 12, i1 false)
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a.var, ptr align 4 @const.loc8_37, i64 12, i1 false)
 // CHECK:STDOUT:   %b.var = alloca { i32, i32 }, align 8
 // CHECK:STDOUT:   %.loc9_28.2.tuple.elem = getelementptr inbounds { i32, i32 }, ptr %b.var, i32 0, i32 0
 // CHECK:STDOUT:   %.loc9_28.4.tuple.elem = getelementptr inbounds { i32, i32 }, ptr %b.var, i32 0, i32 1
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %b.var, ptr align 4 @tuple.1, i64 8, i1 false)
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %b.var, ptr align 4 @const.loc9_29, i64 8, i1 false)
 // CHECK:STDOUT:   %.loc10_43.1.tuple.elem = getelementptr inbounds { i32, i32, i32 }, ptr %a.var, i32 0, i32 0
 // CHECK:STDOUT:   %.loc10_43.2 = load i32, ptr %.loc10_43.1.tuple.elem, align 4
 // CHECK:STDOUT:   %.loc10_43.3.tuple.elem = getelementptr inbounds { i32, i32, i32 }, ptr %a.var, i32 0, i32 1
@@ -66,8 +66,6 @@ fn F() -> i32 {
 // CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; uselistorder directives
-// CHECK:STDOUT: uselistorder i32 1, { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 12 }
-// CHECK:STDOUT: uselistorder i32 2, { 0, 1, 3, 2 }
 // CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 1, 0 }
 // CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }

+ 2 - 5
toolchain/lower/testdata/operators/assignment.carbon

@@ -14,7 +14,7 @@ fn Main() {
 // CHECK:STDOUT: ; ModuleID = 'assignment.carbon'
 // CHECK:STDOUT: source_filename = "assignment.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: @tuple = internal constant { i32, i32 } { i32 1, i32 2 }
+// CHECK:STDOUT: @const.loc11_5 = internal constant { i32, i32 } { i32 1, i32 2 }
 // CHECK:STDOUT:
 // CHECK:STDOUT: define void @Main() {
 // CHECK:STDOUT: entry:
@@ -24,14 +24,11 @@ fn Main() {
 // CHECK:STDOUT:   %b.var = alloca { i32, i32 }, align 8
 // CHECK:STDOUT:   %.loc11_12.2.tuple.elem = getelementptr inbounds { i32, i32 }, ptr %b.var, i32 0, i32 0
 // CHECK:STDOUT:   %.loc11_12.4.tuple.elem = getelementptr inbounds { i32, i32 }, ptr %b.var, i32 0, i32 1
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %b.var, ptr align 4 @tuple, i64 8, i1 false)
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %b.var, ptr align 4 @const.loc11_5, i64 8, i1 false)
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
 // CHECK:STDOUT:
-// CHECK:STDOUT: ; uselistorder directives
-// CHECK:STDOUT: uselistorder i32 1, { 1, 2, 3, 0 }
-// CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }

+ 2 - 5
toolchain/lower/testdata/pointer/address_of_field.carbon

@@ -14,7 +14,7 @@ fn F() {
 // CHECK:STDOUT: ; ModuleID = 'address_of_field.carbon'
 // CHECK:STDOUT: source_filename = "address_of_field.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: @struct = internal constant { i32, i32 } { i32 1, i32 2 }
+// CHECK:STDOUT: @const.loc10_47 = internal constant { i32, i32 } { i32 1, i32 2 }
 // CHECK:STDOUT:
 // CHECK:STDOUT: declare void @G(ptr)
 // CHECK:STDOUT:
@@ -23,7 +23,7 @@ fn F() {
 // CHECK:STDOUT:   %s.var = alloca { i32, i32 }, align 8
 // CHECK:STDOUT:   %.loc10_46.2.a = getelementptr inbounds { i32, i32 }, ptr %s.var, i32 0, i32 0
 // CHECK:STDOUT:   %.loc10_46.4.b = getelementptr inbounds { i32, i32 }, ptr %s.var, i32 0, i32 1
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %s.var, ptr align 4 @struct, i64 8, i1 false)
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %s.var, ptr align 4 @const.loc10_47, i64 8, i1 false)
 // CHECK:STDOUT:   %.loc11_7.b = getelementptr inbounds { i32, i32 }, ptr %s.var, i32 0, i32 1
 // CHECK:STDOUT:   call void @G(ptr %.loc11_7.b)
 // CHECK:STDOUT:   ret void
@@ -32,7 +32,4 @@ fn F() {
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
 // CHECK:STDOUT:
-// CHECK:STDOUT: ; uselistorder directives
-// CHECK:STDOUT: uselistorder i32 1, { 0, 2, 3, 1 }
-// CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }

+ 2 - 5
toolchain/lower/testdata/struct/member_access.carbon

@@ -14,14 +14,14 @@ fn Run() -> i32 {
 // CHECK:STDOUT: ; ModuleID = 'member_access.carbon'
 // CHECK:STDOUT: source_filename = "member_access.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: @struct = internal constant { double, i32 } { double 0.000000e+00, i32 1 }
+// CHECK:STDOUT: @const.loc8_49 = internal constant { double, i32 } { double 0.000000e+00, i32 1 }
 // CHECK:STDOUT:
 // CHECK:STDOUT: define i32 @main() {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %x.var = alloca { double, i32 }, align 8
 // CHECK:STDOUT:   %.loc8_48.2.a = getelementptr inbounds { double, i32 }, ptr %x.var, i32 0, i32 0
 // CHECK:STDOUT:   %.loc8_48.4.b = getelementptr inbounds { double, i32 }, ptr %x.var, i32 0, i32 1
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 8 %x.var, ptr align 8 @struct, i64 16, i1 false)
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 8 %x.var, ptr align 8 @const.loc8_49, i64 16, i1 false)
 // CHECK:STDOUT:   %y.var = alloca i32, align 4
 // CHECK:STDOUT:   %.loc9_17.1.b = getelementptr inbounds { double, i32 }, ptr %x.var, i32 0, i32 1
 // CHECK:STDOUT:   %.loc9_17.2 = load i32, ptr %.loc9_17.1.b, align 4
@@ -35,7 +35,4 @@ fn Run() -> i32 {
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
 // CHECK:STDOUT:
-// CHECK:STDOUT: ; uselistorder directives
-// CHECK:STDOUT: uselistorder i32 1, { 0, 1, 2, 4, 5, 3 }
-// CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }

+ 2 - 5
toolchain/lower/testdata/struct/two_entries.carbon

@@ -13,14 +13,14 @@ fn Run() -> i32 {
 // CHECK:STDOUT: ; ModuleID = 'two_entries.carbon'
 // CHECK:STDOUT: source_filename = "two_entries.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: @struct = internal constant { i32, i32 } { i32 1, i32 2 }
+// CHECK:STDOUT: @const.loc8_47 = internal constant { i32, i32 } { i32 1, i32 2 }
 // CHECK:STDOUT:
 // CHECK:STDOUT: define i32 @main() {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %x.var = alloca { i32, i32 }, align 8
 // CHECK:STDOUT:   %.loc8_46.2.a = getelementptr inbounds { i32, i32 }, ptr %x.var, i32 0, i32 0
 // CHECK:STDOUT:   %.loc8_46.4.b = getelementptr inbounds { i32, i32 }, ptr %x.var, i32 0, i32 1
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x.var, ptr align 4 @struct, i64 8, i1 false)
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x.var, ptr align 4 @const.loc8_47, i64 8, i1 false)
 // CHECK:STDOUT:   %y.var = alloca { i32, i32 }, align 8
 // CHECK:STDOUT:   %.loc9_31.1.a = getelementptr inbounds { i32, i32 }, ptr %x.var, i32 0, i32 0
 // CHECK:STDOUT:   %.loc9_31.2 = load i32, ptr %.loc9_31.1.a, align 4
@@ -36,7 +36,4 @@ fn Run() -> i32 {
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
 // CHECK:STDOUT:
-// CHECK:STDOUT: ; uselistorder directives
-// CHECK:STDOUT: uselistorder i32 1, { 0, 1, 2, 4, 5, 3 }
-// CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }

+ 3 - 0
toolchain/lower/testdata/tuple/one_entry.carbon

@@ -24,3 +24,6 @@ fn Run() -> i32 {
 // CHECK:STDOUT:   store { i32 } %.loc9_20.3.tuple.init, ptr %y.var, align 4
 // CHECK:STDOUT:   ret i32 0
 // CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; uselistorder directives
+// CHECK:STDOUT: uselistorder i32 1, { 0, 2, 1 }

+ 2 - 2
toolchain/lower/testdata/tuple/two_entries.carbon

@@ -13,14 +13,14 @@ fn Run() -> i32 {
 // CHECK:STDOUT: ; ModuleID = 'two_entries.carbon'
 // CHECK:STDOUT: source_filename = "two_entries.carbon"
 // CHECK:STDOUT:
-// CHECK:STDOUT: @tuple = internal constant { i32, i32 } { i32 12, i32 7 }
+// CHECK:STDOUT: @const.loc8_30 = internal constant { i32, i32 } { i32 12, i32 7 }
 // CHECK:STDOUT:
 // CHECK:STDOUT: define i32 @main() {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %x.var = alloca { i32, i32 }, align 8
 // CHECK:STDOUT:   %.loc8_29.2.tuple.elem = getelementptr inbounds { i32, i32 }, ptr %x.var, i32 0, i32 0
 // CHECK:STDOUT:   %.loc8_29.4.tuple.elem = getelementptr inbounds { i32, i32 }, ptr %x.var, i32 0, i32 1
-// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x.var, ptr align 4 @tuple, i64 8, i1 false)
+// CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x.var, ptr align 4 @const.loc8_30, i64 8, i1 false)
 // CHECK:STDOUT:   %y.var = alloca { i32, i32 }, align 8
 // CHECK:STDOUT:   %.loc9_23.1.tuple.elem = getelementptr inbounds { i32, i32 }, ptr %x.var, i32 0, i32 0
 // CHECK:STDOUT:   %.loc9_23.2 = load i32, ptr %.loc9_23.1.tuple.elem, align 4

+ 5 - 0
toolchain/sem_ir/constant.h

@@ -37,6 +37,11 @@ class ConstantValueStore {
     values_[inst_id.index] = const_id;
   }
 
+  // Returns the constant values mapping as an ArrayRef whose keys are
+  // instruction indexes. Some of the elements in this mapping may be Invalid or
+  // NotConstant.
+  auto array_ref() const -> llvm::ArrayRef<ConstantId> { return values_; }
+
  private:
   const ConstantId default_;
 

+ 1 - 0
toolchain/sem_ir/file.cpp

@@ -193,6 +193,7 @@ static auto GetTypePrecedence(InstKind kind) -> int {
       return -2;
 
 #define CARBON_SEM_IR_INST_KIND_TYPE(...)
+#define CARBON_SEM_IR_INST_KIND_MAYBE_TYPE(...)
 #define CARBON_SEM_IR_INST_KIND(Name) case SemIR::Name::Kind:
 #include "toolchain/sem_ir/inst_kind.def"
       CARBON_FATAL() << "GetTypePrecedence for non-type inst kind " << kind;

+ 7 - 1
toolchain/sem_ir/inst_kind.cpp

@@ -33,7 +33,13 @@ auto InstKind::value_kind() const -> InstValueKind {
 
 auto InstKind::constant_kind() const -> InstConstantKind {
   static constexpr InstConstantKind Table[] = {
-#define CARBON_SEM_IR_INST_KIND(Name) SemIR::Name::Kind.constant_kind(),
+#define CARBON_SEM_IR_INST_KIND_CONSTANT_NEVER(...) InstConstantKind::Never,
+#define CARBON_SEM_IR_INST_KIND_CONSTANT_SYMBOLIC_ONLY(...) \
+  InstConstantKind::SymbolicOnly,
+#define CARBON_SEM_IR_INST_KIND_CONSTANT_CONDITIONAL(...) \
+  InstConstantKind::Conditional,
+#define CARBON_SEM_IR_INST_KIND_CONSTANT_ALWAYS(...) InstConstantKind::Always,
+#define CARBON_SEM_IR_INST_KIND(Name)
 #include "toolchain/sem_ir/inst_kind.def"
   };
   return Table[AsInt()];

+ 124 - 81
toolchain/sem_ir/inst_kind.def

@@ -11,14 +11,31 @@
 // - CARBON_SEM_IR_INST_KIND(Name)
 //   Invoked for each kind of semantic instruction.
 //
-// The invocation of the above macro will be wrapped in one of these, which by
-// default expand to their argument:
+// The invocation of the above macro will be wrapped in one macro from each of
+// the following sets, which by default expand to their argument:
+//
+// Whether the instruction can define a type:
 // - CARBON_SEM_IR_INST_KIND_TYPE(...)
-//   Invoked for each instruction that might define a type constant.
+//   Invoked for each instruction that is always of type `type`, and might
+//   define a type constant.
+// - CARBON_SEM_IR_INST_KIND_MAYBE_TYPE(...)
+//   Invoked for each instruction that is sometimes of type `type`, and might
+//   define a type constant.
 // - CARBON_SEM_IR_INST_KIND_NOT_TYPE(...)
 //   Invoked for each instruction that can never define a type constant. Note
 //   that such instructions can still have type `type`, but are not the
 //   canonical definition of any type.
+//
+// Whether the instruction can define a constant, see `InstConstantKind`:
+// - CARBON_SEM_IR_INST_KIND_CONSTANT_NEVER(...)
+//   Invoked when `constant_kind()` is `InstConstantKind::Never`.
+// - CARBON_SEM_IR_INST_KIND_CONSTANT_SYMBOLIC_ONLY(...)
+//   Invoked when `constant_kind()` is `InstConstantKind::SymbolicOnly`.
+// - CARBON_SEM_IR_INST_KIND_CONSTANT_CONDITIONAL(...)
+//   Invoked when `constant_kind()` is `InstConstantKind::Conditional`.
+// - CARBON_SEM_IR_INST_KIND_CONSTANT_ALWAYS(...)
+//   Invoked when `constant_kind()` is `InstConstantKind::Always`.
+//
 // Defining these is optional.
 
 #ifndef CARBON_SEM_IR_INST_KIND
@@ -28,92 +45,118 @@
 #ifndef CARBON_SEM_IR_INST_KIND_NOT_TYPE
 #define CARBON_SEM_IR_INST_KIND_NOT_TYPE(...) __VA_ARGS__
 #endif
+#ifndef CARBON_SEM_IR_INST_KIND_MAYBE_TYPE
+#define CARBON_SEM_IR_INST_KIND_MAYBE_TYPE(...) __VA_ARGS__
+#endif
 #ifndef CARBON_SEM_IR_INST_KIND_TYPE
 #define CARBON_SEM_IR_INST_KIND_TYPE(...) __VA_ARGS__
 #endif
 
-#define CARBON_SEM_IR_INST_KIND_IMPL(Name, IsType) \
-  CARBON_SEM_IR_INST_KIND_##IsType(CARBON_SEM_IR_INST_KIND(Name))
+#ifndef CARBON_SEM_IR_INST_KIND_CONSTANT_NEVER
+#define CARBON_SEM_IR_INST_KIND_CONSTANT_NEVER(...) __VA_ARGS__
+#endif
+#ifndef CARBON_SEM_IR_INST_KIND_CONSTANT_SYMBOLIC_ONLY
+#define CARBON_SEM_IR_INST_KIND_CONSTANT_SYMBOLIC_ONLY(...) __VA_ARGS__
+#endif
+#ifndef CARBON_SEM_IR_INST_KIND_CONSTANT_CONDITIONAL
+#define CARBON_SEM_IR_INST_KIND_CONSTANT_CONDITIONAL(...) __VA_ARGS__
+#endif
+#ifndef CARBON_SEM_IR_INST_KIND_CONSTANT_ALWAYS
+#define CARBON_SEM_IR_INST_KIND_CONSTANT_ALWAYS(...) __VA_ARGS__
+#endif
+
+#define CARBON_SEM_IR_INST_KIND_IMPL(Name, IsType, IsConstant) \
+  CARBON_SEM_IR_INST_KIND_##IsType(                            \
+      CARBON_SEM_IR_INST_KIND_##IsConstant(CARBON_SEM_IR_INST_KIND(Name)))
 
 // For each instruction kind declared here there is a matching definition in
 // `typed_insts.h`.
-CARBON_SEM_IR_INST_KIND_IMPL(AdaptDecl, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(AddrOf, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(AddrPattern, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(ArrayIndex, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(ArrayInit, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(ArrayType, TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(AsCompatible, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(Assign, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(AssociatedConstantDecl, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(AssociatedEntity, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(AssociatedEntityType, TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(BaseDecl, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(BindAlias, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(BindName, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(BindSymbolicName, TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(BindValue, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(BlockArg, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(BoolLiteral, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(BoundMethod, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(Branch, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(BranchIf, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(BranchWithArg, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(Builtin, TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(Call, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(ClassDecl, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(ClassElementAccess, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(ClassInit, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(ClassType, TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(ConstType, TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(Converted, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(Deref, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(FacetTypeAccess, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(FloatLiteral, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(FloatType, TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(FieldDecl, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(FunctionDecl, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(ImplDecl, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(ImportRefUnloaded, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(ImportRefLoaded, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(ImportRefUsed, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(InitializeFrom, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(InterfaceDecl, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(InterfaceType, TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(InterfaceWitness, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(InterfaceWitnessAccess, TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(IntLiteral, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(IntType, TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(NameRef, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(Namespace, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(Param, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(PointerType, TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(RealLiteral, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(ReturnExpr, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(Return, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(SpliceBlock, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(StringLiteral, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(StructAccess, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(StructInit, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(StructLiteral, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(StructTypeField, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(StructType, TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(StructValue, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(TemporaryStorage, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(Temporary, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(TupleAccess, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(TupleIndex, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(TupleInit, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(TupleLiteral, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(TupleType, TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(TupleValue, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(UnaryOperatorNot, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(UnboundElementType, TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(ValueAsRef, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(ValueOfInitializer, NOT_TYPE)
-CARBON_SEM_IR_INST_KIND_IMPL(VarStorage, NOT_TYPE)
+CARBON_SEM_IR_INST_KIND_IMPL(AdaptDecl, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(AddrOf, NOT_TYPE, CONSTANT_CONDITIONAL)
+CARBON_SEM_IR_INST_KIND_IMPL(AddrPattern, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(ArrayIndex, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(ArrayInit, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(ArrayType, TYPE, CONSTANT_CONDITIONAL)
+CARBON_SEM_IR_INST_KIND_IMPL(AsCompatible, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(Assign, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(AssociatedConstantDecl, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(AssociatedEntity, NOT_TYPE, CONSTANT_ALWAYS)
+CARBON_SEM_IR_INST_KIND_IMPL(AssociatedEntityType, TYPE, CONSTANT_CONDITIONAL)
+CARBON_SEM_IR_INST_KIND_IMPL(BaseDecl, NOT_TYPE, CONSTANT_ALWAYS)
+CARBON_SEM_IR_INST_KIND_IMPL(BindAlias, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(BindName, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(BindSymbolicName, MAYBE_TYPE,
+                             CONSTANT_SYMBOLIC_ONLY)
+CARBON_SEM_IR_INST_KIND_IMPL(BindValue, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(BlockArg, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(BoolLiteral, NOT_TYPE, CONSTANT_ALWAYS)
+CARBON_SEM_IR_INST_KIND_IMPL(BoundMethod, NOT_TYPE, CONSTANT_CONDITIONAL)
+CARBON_SEM_IR_INST_KIND_IMPL(Branch, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(BranchIf, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(BranchWithArg, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(Builtin, TYPE, CONSTANT_ALWAYS)
+CARBON_SEM_IR_INST_KIND_IMPL(Call, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(ClassDecl, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(ClassElementAccess, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(ClassInit, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(ClassType, TYPE, CONSTANT_CONDITIONAL)
+CARBON_SEM_IR_INST_KIND_IMPL(ConstType, TYPE, CONSTANT_CONDITIONAL)
+CARBON_SEM_IR_INST_KIND_IMPL(Converted, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(Deref, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(FacetTypeAccess, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(FieldDecl, NOT_TYPE, CONSTANT_ALWAYS)
+CARBON_SEM_IR_INST_KIND_IMPL(FloatLiteral, NOT_TYPE, CONSTANT_ALWAYS)
+CARBON_SEM_IR_INST_KIND_IMPL(FloatType, TYPE, CONSTANT_CONDITIONAL)
+CARBON_SEM_IR_INST_KIND_IMPL(FunctionDecl, NOT_TYPE, CONSTANT_ALWAYS)
+CARBON_SEM_IR_INST_KIND_IMPL(ImplDecl, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(ImportRefUnloaded, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(ImportRefLoaded, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(ImportRefUsed, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(InitializeFrom, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(InterfaceDecl, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(InterfaceType, TYPE, CONSTANT_CONDITIONAL)
+CARBON_SEM_IR_INST_KIND_IMPL(InterfaceWitness, NOT_TYPE, CONSTANT_CONDITIONAL)
+CARBON_SEM_IR_INST_KIND_IMPL(InterfaceWitnessAccess, MAYBE_TYPE,
+                             CONSTANT_SYMBOLIC_ONLY)
+CARBON_SEM_IR_INST_KIND_IMPL(IntLiteral, NOT_TYPE, CONSTANT_ALWAYS)
+CARBON_SEM_IR_INST_KIND_IMPL(IntType, TYPE, CONSTANT_CONDITIONAL)
+CARBON_SEM_IR_INST_KIND_IMPL(NameRef, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(Namespace, NOT_TYPE, CONSTANT_ALWAYS)
+CARBON_SEM_IR_INST_KIND_IMPL(Param, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(PointerType, TYPE, CONSTANT_CONDITIONAL)
+CARBON_SEM_IR_INST_KIND_IMPL(RealLiteral, NOT_TYPE, CONSTANT_ALWAYS)
+CARBON_SEM_IR_INST_KIND_IMPL(ReturnExpr, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(Return, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(SpliceBlock, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(StringLiteral, NOT_TYPE, CONSTANT_ALWAYS)
+CARBON_SEM_IR_INST_KIND_IMPL(StructAccess, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(StructInit, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(StructLiteral, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(StructType, TYPE, CONSTANT_CONDITIONAL)
+CARBON_SEM_IR_INST_KIND_IMPL(StructTypeField, NOT_TYPE, CONSTANT_CONDITIONAL)
+CARBON_SEM_IR_INST_KIND_IMPL(StructValue, NOT_TYPE, CONSTANT_CONDITIONAL)
+CARBON_SEM_IR_INST_KIND_IMPL(TemporaryStorage, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(Temporary, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(TupleAccess, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(TupleIndex, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(TupleInit, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(TupleLiteral, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(TupleType, TYPE, CONSTANT_CONDITIONAL)
+CARBON_SEM_IR_INST_KIND_IMPL(TupleValue, NOT_TYPE, CONSTANT_CONDITIONAL)
+CARBON_SEM_IR_INST_KIND_IMPL(UnaryOperatorNot, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(UnboundElementType, TYPE, CONSTANT_CONDITIONAL)
+CARBON_SEM_IR_INST_KIND_IMPL(ValueAsRef, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(ValueOfInitializer, NOT_TYPE, CONSTANT_NEVER)
+CARBON_SEM_IR_INST_KIND_IMPL(VarStorage, NOT_TYPE, CONSTANT_NEVER)
 
-#undef CARBON_SEM_IR_INST_KIND_NOT_TYPE
 #undef CARBON_SEM_IR_INST_KIND_TYPE
+#undef CARBON_SEM_IR_INST_KIND_MAYBE_TYPE
+#undef CARBON_SEM_IR_INST_KIND_NOT_TYPE
+
+#undef CARBON_SEM_IR_INST_KIND_CONSTANT_NEVER
+#undef CARBON_SEM_IR_INST_KIND_CONSTANT_SYMBOLIC_ONLY
+#undef CARBON_SEM_IR_INST_KIND_CONSTANT_CONDITIONAL
+#undef CARBON_SEM_IR_INST_KIND_CONSTANT_ALWAYS
+
 #undef CARBON_SEM_IR_INST_KIND_IMPL
 #undef CARBON_SEM_IR_INST_KIND

+ 3 - 26
toolchain/sem_ir/inst_kind.h

@@ -79,11 +79,7 @@ class InstKind : public CARBON_ENUM_BASE(InstKind) {
   template <typename TypedNodeId>
   constexpr auto Define(
       llvm::StringLiteral ir_name,
-      InstConstantKind constant_kind = InstConstantKind::Never) const
-      -> Definition<TypedNodeId>;
-  template <typename TypedNodeId>
-  constexpr auto Define(llvm::StringLiteral ir_name,
-                        TerminatorKind terminator_kind) const
+      TerminatorKind terminator_kind = TerminatorKind::NotTerminator) const
       -> Definition<TypedNodeId>;
 
   using EnumBase::AsInt;
@@ -134,11 +130,6 @@ class InstKind::Definition : public InstKind {
   // Returns the name to use for this instruction kind in Semantics IR.
   constexpr auto ir_name() const -> llvm::StringLiteral { return ir_name_; }
 
-  // Returns whether this kind of instruction is able to define a constant.
-  constexpr auto constant_kind() const -> InstConstantKind {
-    return constant_kind_;
-  }
-
   // Returns whether this instruction kind is a code block terminator. See
   // InstKind::terminator_kind().
   constexpr auto terminator_kind() const -> TerminatorKind {
@@ -149,32 +140,18 @@ class InstKind::Definition : public InstKind {
   friend class InstKind;
 
   constexpr Definition(InstKind kind, llvm::StringLiteral ir_name,
-                       InstConstantKind constant_kind,
                        TerminatorKind terminator_kind)
-      : InstKind(kind),
-        ir_name_(ir_name),
-        constant_kind_(constant_kind),
-        terminator_kind_(terminator_kind) {}
+      : InstKind(kind), ir_name_(ir_name), terminator_kind_(terminator_kind) {}
 
   llvm::StringLiteral ir_name_;
-  InstConstantKind constant_kind_;
   TerminatorKind terminator_kind_;
 };
 
-template <typename TypedNodeId>
-constexpr auto InstKind::Define(llvm::StringLiteral ir_name,
-                                InstConstantKind constant_kind) const
-    -> Definition<TypedNodeId> {
-  return Definition<TypedNodeId>(*this, ir_name, constant_kind,
-                                 TerminatorKind::NotTerminator);
-}
-
 template <typename TypedNodeId>
 constexpr auto InstKind::Define(llvm::StringLiteral ir_name,
                                 TerminatorKind terminator_kind) const
     -> Definition<TypedNodeId> {
-  return Definition<TypedNodeId>(*this, ir_name, InstConstantKind::Never,
-                                 terminator_kind);
+  return Definition<TypedNodeId>(*this, ir_name, terminator_kind);
 }
 
 }  // namespace Carbon::SemIR

+ 61 - 71
toolchain/sem_ir/typed_insts.h

@@ -55,8 +55,8 @@ struct AdaptDecl {
 
 struct AddrOf {
   // TODO: Make Parse::NodeId more specific.
-  static constexpr auto Kind = InstKind::AddrOf.Define<Parse::NodeId>(
-      "addr_of", InstConstantKind::Conditional);
+  static constexpr auto Kind =
+      InstKind::AddrOf.Define<Parse::NodeId>("addr_of");
 
   TypeId type_id;
   InstId lvalue_id;
@@ -73,8 +73,8 @@ struct AddrPattern {
 
 struct ArrayIndex {
   // TODO: Make Parse::NodeId more specific.
-  static constexpr auto Kind = InstKind::ArrayIndex.Define<Parse::NodeId>(
-      "array_index", InstConstantKind::Conditional);
+  static constexpr auto Kind =
+      InstKind::ArrayIndex.Define<Parse::NodeId>("array_index");
 
   TypeId type_id;
   InstId array_id;
@@ -142,8 +142,8 @@ struct ArrayInit {
 };
 
 struct ArrayType {
-  static constexpr auto Kind = InstKind::ArrayType.Define<Parse::ArrayExprId>(
-      "array_type", InstConstantKind::Conditional);
+  static constexpr auto Kind =
+      InstKind::ArrayType.Define<Parse::ArrayExprId>("array_type");
 
   TypeId type_id;
   InstId bound_id;
@@ -186,8 +186,8 @@ struct AssociatedConstantDecl {
 // This represents the entity before impl lookup is performed, and identifies
 // the slot within a witness where the constant value will be found.
 struct AssociatedEntity {
-  static constexpr auto Kind = InstKind::AssociatedEntity.Define<Parse::NodeId>(
-      "assoc_entity", InstConstantKind::Always);
+  static constexpr auto Kind =
+      InstKind::AssociatedEntity.Define<Parse::NodeId>("assoc_entity");
 
   // The type of the associated entity. This is an AssociatedEntityType.
   TypeId type_id;
@@ -200,7 +200,7 @@ struct AssociatedEntity {
 struct AssociatedEntityType {
   static constexpr auto Kind =
       InstKind::AssociatedEntityType.Define<Parse::InvalidNodeId>(
-          "assoc_entity_type", InstConstantKind::Conditional);
+          "assoc_entity_type");
 
   TypeId type_id;
   InterfaceId interface_id;
@@ -211,8 +211,8 @@ struct AssociatedEntityType {
 // element of the derived class, and the type of the `BaseDecl` instruction is
 // an `UnboundElementType`.
 struct BaseDecl {
-  static constexpr auto Kind = InstKind::BaseDecl.Define<Parse::BaseDeclId>(
-      "base_decl", InstConstantKind::Always);
+  static constexpr auto Kind =
+      InstKind::BaseDecl.Define<Parse::BaseDeclId>("base_decl");
 
   TypeId type_id;
   TypeId base_type_id;
@@ -253,8 +253,8 @@ struct BindName {
 };
 
 struct BindSymbolicName {
-  static constexpr auto Kind = InstKind::BindSymbolicName.Define<Parse::NodeId>(
-      "bind_symbolic_name", InstConstantKind::SymbolicOnly);
+  static constexpr auto Kind =
+      InstKind::BindSymbolicName.Define<Parse::NodeId>("bind_symbolic_name");
 
   TypeId type_id;
   BindNameId bind_name_id;
@@ -280,8 +280,8 @@ struct BlockArg {
 };
 
 struct BoolLiteral {
-  static constexpr auto Kind = InstKind::BoolLiteral.Define<Parse::NodeId>(
-      "bool_literal", InstConstantKind::Always);
+  static constexpr auto Kind =
+      InstKind::BoolLiteral.Define<Parse::NodeId>("bool_literal");
 
   TypeId type_id;
   BoolValue value;
@@ -290,8 +290,8 @@ struct BoolLiteral {
 // A bound method, that combines a function with the value to use for its
 // `self` parameter, such as `object.MethodName`.
 struct BoundMethod {
-  static constexpr auto Kind = InstKind::BoundMethod.Define<Parse::NodeId>(
-      "bound_method", InstConstantKind::Conditional);
+  static constexpr auto Kind =
+      InstKind::BoundMethod.Define<Parse::NodeId>("bound_method");
 
   TypeId type_id;
   // The object argument in the bound method, which will be used to initialize
@@ -344,8 +344,8 @@ struct BranchWithArg {
 
 struct Builtin {
   // Builtins don't have a parse node associated with them.
-  static constexpr auto Kind = InstKind::Builtin.Define<Parse::InvalidNodeId>(
-      "builtin", InstConstantKind::Always);
+  static constexpr auto Kind =
+      InstKind::Builtin.Define<Parse::InvalidNodeId>("builtin");
 
   TypeId type_id;
   BuiltinKind builtin_kind;
@@ -355,8 +355,7 @@ struct Call {
   // For a syntactic call, the parse node will be a CallExprStartId. However,
   // calls can arise from other syntaxes, such as operators and implicit
   // conversions.
-  static constexpr auto Kind = InstKind::Call.Define<Parse::NodeId>(
-      "call", InstConstantKind::SymbolicOnly);
+  static constexpr auto Kind = InstKind::Call.Define<Parse::NodeId>("call");
 
   TypeId type_id;
   InstId callee_id;
@@ -369,8 +368,7 @@ struct Call {
 
 struct ClassDecl {
   static constexpr auto Kind =
-      InstKind::ClassDecl.Define<Parse::AnyClassDeclId>(
-          "class_decl", InstConstantKind::Always);
+      InstKind::ClassDecl.Define<Parse::AnyClassDeclId>("class_decl");
 
   TypeId type_id;
   // TODO: For a generic class declaration, the name of the class declaration
@@ -404,8 +402,7 @@ struct ClassInit {
 
 struct ClassType {
   static constexpr auto Kind =
-      InstKind::ClassType.Define<Parse::AnyClassDeclId>(
-          "class_type", InstConstantKind::Conditional);
+      InstKind::ClassType.Define<Parse::AnyClassDeclId>("class_type");
 
   TypeId type_id;
   ClassId class_id;
@@ -414,8 +411,7 @@ struct ClassType {
 
 struct ConstType {
   static constexpr auto Kind =
-      InstKind::ConstType.Define<Parse::PrefixOperatorConstId>(
-          "const_type", InstConstantKind::Conditional);
+      InstKind::ConstType.Define<Parse::PrefixOperatorConstId>("const_type");
 
   TypeId type_id;
   TypeId inner_id;
@@ -448,18 +444,28 @@ struct FacetTypeAccess {
   InstId facet_id;
 };
 
+// A field in a class, of the form `var field: field_type;`. The type of the
+// `FieldDecl` instruction is an `UnboundElementType`.
+struct FieldDecl {
+  static constexpr auto Kind =
+      InstKind::FieldDecl.Define<Parse::BindingPatternId>("field_decl");
+
+  TypeId type_id;
+  NameId name_id;
+  ElementIndex index;
+};
+
 struct FloatLiteral {
   static constexpr auto Kind =
-      InstKind::FloatLiteral.Define<Parse::RealLiteralId>(
-          "float_literal", InstConstantKind::Always);
+      InstKind::FloatLiteral.Define<Parse::RealLiteralId>("float_literal");
 
   TypeId type_id;
   FloatId float_id;
 };
 
 struct FloatType {
-  static constexpr auto Kind = InstKind::FloatType.Define<Parse::NodeId>(
-      "float_type", InstConstantKind::Conditional);
+  static constexpr auto Kind =
+      InstKind::FloatType.Define<Parse::NodeId>("float_type");
 
   TypeId type_id;
   // TODO: Consider adding a more compact way of representing either a small
@@ -467,22 +473,9 @@ struct FloatType {
   InstId bit_width_id;
 };
 
-// A field in a class, of the form `var field: field_type;`. The type of the
-// `FieldDecl` instruction is an `UnboundElementType`.
-struct FieldDecl {
-  static constexpr auto Kind =
-      InstKind::FieldDecl.Define<Parse::BindingPatternId>(
-          "field_decl", InstConstantKind::Always);
-
-  TypeId type_id;
-  NameId name_id;
-  ElementIndex index;
-};
-
 struct FunctionDecl {
   static constexpr auto Kind =
-      InstKind::FunctionDecl.Define<Parse::AnyFunctionDeclId>(
-          "fn_decl", InstConstantKind::Always);
+      InstKind::FunctionDecl.Define<Parse::AnyFunctionDeclId>("fn_decl");
 
   TypeId type_id;
   FunctionId function_id;
@@ -571,8 +564,8 @@ struct InterfaceDecl {
 };
 
 struct InterfaceType {
-  static constexpr auto Kind = InstKind::InterfaceType.Define<Parse::NodeId>(
-      "interface_type", InstConstantKind::Conditional);
+  static constexpr auto Kind =
+      InstKind::InterfaceType.Define<Parse::NodeId>("interface_type");
 
   TypeId type_id;
   InterfaceId interface_id;
@@ -584,7 +577,7 @@ struct InterfaceType {
 struct InterfaceWitness {
   static constexpr auto Kind =
       InstKind::InterfaceWitness.Define<Parse::InvalidNodeId>(
-          "interface_witness", InstConstantKind::Conditional);
+          "interface_witness");
 
   TypeId type_id;
   InstBlockId elements_id;
@@ -603,16 +596,16 @@ struct InterfaceWitnessAccess {
 
 struct IntLiteral {
   // TODO: Make Parse::NodeId more specific.
-  static constexpr auto Kind = InstKind::IntLiteral.Define<Parse::NodeId>(
-      "int_literal", InstConstantKind::Always);
+  static constexpr auto Kind =
+      InstKind::IntLiteral.Define<Parse::NodeId>("int_literal");
 
   TypeId type_id;
   IntId int_id;
 };
 
 struct IntType {
-  static constexpr auto Kind = InstKind::IntType.Define<Parse::NodeId>(
-      "int_type", InstConstantKind::Conditional);
+  static constexpr auto Kind =
+      InstKind::IntType.Define<Parse::NodeId>("int_type");
 
   TypeId type_id;
   IntKind int_kind;
@@ -633,8 +626,7 @@ struct NameRef {
 
 struct Namespace {
   static constexpr auto Kind =
-      InstKind::Namespace.Define<Parse::AnyNamespaceId>(
-          "namespace", InstConstantKind::Always);
+      InstKind::Namespace.Define<Parse::AnyNamespaceId>("namespace");
 
   TypeId type_id;
   NameScopeId name_scope_id;
@@ -651,8 +643,8 @@ struct Param {
 
 struct PointerType {
   // TODO: Make Parse::NodeId more specific.
-  static constexpr auto Kind = InstKind::PointerType.Define<Parse::NodeId>(
-      "ptr_type", InstConstantKind::Conditional);
+  static constexpr auto Kind =
+      InstKind::PointerType.Define<Parse::NodeId>("ptr_type");
 
   TypeId type_id;
   TypeId pointee_id;
@@ -660,8 +652,7 @@ struct PointerType {
 
 struct RealLiteral {
   static constexpr auto Kind =
-      InstKind::RealLiteral.Define<Parse::RealLiteralId>(
-          "real_literal", InstConstantKind::Always);
+      InstKind::RealLiteral.Define<Parse::RealLiteralId>("real_literal");
 
   TypeId type_id;
   RealId real_id;
@@ -699,8 +690,7 @@ struct SpliceBlock {
 
 struct StringLiteral {
   static constexpr auto Kind =
-      InstKind::StringLiteral.Define<Parse::StringLiteralId>(
-          "string_literal", InstConstantKind::Always);
+      InstKind::StringLiteral.Define<Parse::StringLiteralId>("string_literal");
 
   TypeId type_id;
   StringLiteralValueId string_literal_id;
@@ -737,8 +727,8 @@ struct StructLiteral {
 struct StructType {
   // TODO: Make this more specific. It can be one of: ClassDefinitionId,
   // StructLiteralId, StructTypeLiteralId
-  static constexpr auto Kind = InstKind::StructType.Define<Parse::NodeId>(
-      "struct_type", InstConstantKind::Conditional);
+  static constexpr auto Kind =
+      InstKind::StructType.Define<Parse::NodeId>("struct_type");
 
   TypeId type_id;
   InstBlockId fields_id;
@@ -746,8 +736,8 @@ struct StructType {
 
 struct StructTypeField {
   // TODO: Make Parse::NodeId more specific.
-  static constexpr auto Kind = InstKind::StructTypeField.Define<Parse::NodeId>(
-      "struct_type_field", InstConstantKind::Conditional);
+  static constexpr auto Kind =
+      InstKind::StructTypeField.Define<Parse::NodeId>("struct_type_field");
 
   // This instruction is an implementation detail of `StructType`, and doesn't
   // produce a value, so has no type, even though it declares a field with a
@@ -758,8 +748,8 @@ struct StructTypeField {
 
 struct StructValue {
   // TODO: Make Parse::NodeId more specific.
-  static constexpr auto Kind = InstKind::StructValue.Define<Parse::NodeId>(
-      "struct_value", InstConstantKind::Conditional);
+  static constexpr auto Kind =
+      InstKind::StructValue.Define<Parse::NodeId>("struct_value");
 
   TypeId type_id;
   InstBlockId elements_id;
@@ -822,8 +812,8 @@ struct TupleLiteral {
 
 struct TupleType {
   // TODO: Make Parse::NodeId more specific.
-  static constexpr auto Kind = InstKind::TupleType.Define<Parse::NodeId>(
-      "tuple_type", InstConstantKind::Conditional);
+  static constexpr auto Kind =
+      InstKind::TupleType.Define<Parse::NodeId>("tuple_type");
 
   TypeId type_id;
   TypeBlockId elements_id;
@@ -831,8 +821,8 @@ struct TupleType {
 
 struct TupleValue {
   // TODO: Make Parse::NodeId more specific.
-  static constexpr auto Kind = InstKind::TupleValue.Define<Parse::NodeId>(
-      "tuple_value", InstConstantKind::Conditional);
+  static constexpr auto Kind =
+      InstKind::TupleValue.Define<Parse::NodeId>("tuple_value");
 
   TypeId type_id;
   InstBlockId elements_id;
@@ -853,7 +843,7 @@ struct UnaryOperatorNot {
 struct UnboundElementType {
   static constexpr auto Kind = InstKind::UnboundElementType.Define<
       Parse::NodeIdOneOf<Parse::BaseDeclId, Parse::BindingPatternId>>(
-      "unbound_element_type", InstConstantKind::Conditional);
+      "unbound_element_type");
 
   TypeId type_id;
   // The class that a value of this type is an element of.