Kaynağa Gözat

Refactor SemIR YAML printing to use dashed lists. (#3330)

This standardizes on having ValueStore and related structures provide
printing, removing the handlers in file.cpp.

The `[]` is provided for empty sequences versus if there was simply
nothing, in which case it would be a sequence when non-empty, and a null
value when empty. Consistently (and explicitly) providing sequences
feels easier to understand.

The changes to the output yaml are overall more terse. My hope is that
this is an improvement for most readers.

Also fixes printing of APInt, defaulting to unsigned for consistency
with Carbon's use.
Jon Ross-Perkins 2 yıl önce
ebeveyn
işleme
74c3c665fa

+ 2 - 0
toolchain/base/BUILD

@@ -41,6 +41,8 @@ cc_test(
     deps = [
         ":value_store",
         "//testing/base:gtest_main",
+        "//testing/base:test_raw_ostream",
+        "//toolchain/testing:yaml_test_helpers",
         "@com_google_googletest//:gtest",
     ],
 )

+ 91 - 18
toolchain/base/value_store.h

@@ -11,7 +11,9 @@
 #include "common/ostream.h"
 #include "llvm/ADT/APInt.h"
 #include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/STLFunctionalExtras.h"
 #include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/YAMLParser.h"
 #include "toolchain/base/index_base.h"
 
@@ -84,8 +86,67 @@ namespace Internal {
 // Used as a parent class for non-printable types. This is just for
 // std::conditional, not as an API.
 class ValueStoreNotPrintable {};
+
+// Provides YAML printing of a value.
+template <typename ValueT>
+inline auto PrintValue(llvm::raw_ostream& out, const ValueT& val) {
+  out << val;
+};
+inline auto PrintValue(llvm::raw_ostream& out, const llvm::APInt& val) {
+  val.print(out, /*isSigned=*/false);
+};
+inline auto PrintValue(llvm::raw_ostream& out, const llvm::StringRef& val) {
+  out << "\"" << llvm::yaml::escape(val) << "\"";
+};
+
+struct DefaultPrinter {
+  template <typename ValueT>
+  void operator()(llvm::raw_ostream& out, const ValueT& value) {
+    PrintValue(out, value);
+  }
+};
+
 }  // namespace Internal
 
+// Provides YAML printing of a list. The first line indent applies even if there
+// is no label, in which case it applies to the first element.
+template <typename ValueT>
+inline auto PrintValueRange(
+    llvm::raw_ostream& out, llvm::iterator_range<const ValueT*> range,
+    std::optional<llvm::StringRef> label, int first_line_indent,
+    int later_indent, bool trailing_newline,
+    llvm::function_ref<void(llvm::raw_ostream&, const ValueT& val)> print =
+        Internal::DefaultPrinter()) {
+  out.indent(first_line_indent);
+  if (label) {
+    out << *label << ":";
+    if (range.empty()) {
+      // Add a space between the `:` and the `[]` printed below.
+      out << " ";
+    } else {
+      out << "\n";
+      out.indent(later_indent);
+    }
+  }
+  if (range.empty()) {
+    out << "[]";
+    if (trailing_newline) {
+      out << "\n";
+    }
+    return;
+  }
+  std::string sep_str = "\n";
+  sep_str.append(later_indent, ' ');
+  llvm::ListSeparator sep(sep_str);
+  for (const auto& val : range) {
+    out << sep << "- ";
+    print(out, val);
+  }
+  if (trailing_newline) {
+    out << "\n";
+  }
+}
+
 // A simple wrapper for accumulating values, providing IDs to later retrieve the
 // value. This does not do deduplication.
 template <typename IdT, typename ValueT = typename IdT::IndexedType>
@@ -94,6 +155,9 @@ class ValueStore
                               Printable<ValueStore<IdT, ValueT>>,
                               Internal::ValueStoreNotPrintable> {
  public:
+  using PrintFn =
+      llvm::function_ref<void(llvm::raw_ostream&, const ValueT& val)>;
+
   // Stores the value and returns an ID to reference it.
   auto Add(ValueT value) -> IdT {
     IdT id = IdT(values_.size());
@@ -125,12 +189,17 @@ class ValueStore
   auto Reserve(size_t size) -> void { values_.reserve(size); }
 
   // These are to support printable structures, and are not guaranteed.
-  auto Print(llvm::raw_ostream& out) const -> void { Print(out, 0); }
-  auto Print(llvm::raw_ostream& out, int indent) const -> void {
-    for (const auto& value : values_) {
-      out.indent(indent);
-      out << "- " << value << "\n";
-    }
+  auto Print(llvm::raw_ostream& out) const -> void {
+    Print(out, std::nullopt, 0, 0);
+  }
+  auto Print(llvm::raw_ostream& out, std::optional<llvm::StringRef> label,
+             int first_line_indent, int later_indent,
+             // This decays so that `const llvm::APInt` printing catches the
+             // specialization.
+             PrintFn print = Internal::DefaultPrinter()) const -> void {
+    PrintValueRange(out, llvm::iterator_range(values_), label,
+                    first_line_indent, later_indent, /*trailing_newline=*/true,
+                    print);
   }
 
   auto array_ref() const -> llvm::ArrayRef<ValueT> { return values_; }
@@ -145,6 +214,9 @@ class ValueStore
 template <>
 class ValueStore<StringId> : public Printable<ValueStore<StringId>> {
  public:
+  using PrintFn =
+      llvm::function_ref<void(llvm::raw_ostream&, const llvm::StringRef& val)>;
+
   // Returns an ID to reference the value. May return an existing ID if the
   // string was previously added.
   auto Add(llvm::StringRef value) -> StringId {
@@ -162,12 +234,15 @@ class ValueStore<StringId> : public Printable<ValueStore<StringId>> {
     return values_[id.index];
   }
 
-  auto Print(llvm::raw_ostream& out) const -> void { Print(out, 0); }
-  auto Print(llvm::raw_ostream& out, int indent) const -> void {
-    for (auto value : values_) {
-      out.indent(indent);
-      out << "- \"" << llvm::yaml::escape(value) << "\"\n";
-    }
+  auto Print(llvm::raw_ostream& out) const -> void {
+    Print(out, std::nullopt, 0, 0);
+  }
+  auto Print(llvm::raw_ostream& out, std::optional<llvm::StringRef> label,
+             int first_line_indent, int later_indent,
+             PrintFn print = Internal::DefaultPrinter()) const -> void {
+    PrintValueRange(out, llvm::iterator_range(values_), label,
+                    first_line_indent, later_indent, /*trailing_newline=*/true,
+                    print);
   }
 
  private:
@@ -188,12 +263,10 @@ class SharedValueStores : public Printable<SharedValueStores> {
 
   auto Print(llvm::raw_ostream& out) const -> void {
     out << "shared_values:\n"
-        << "  - integers:\n";
-    integers_.Print(out, 6);
-    out << "  - reals:\n";
-    reals_.Print(out, 6);
-    out << "  - strings:\n";
-    strings_.Print(out, 6);
+        << "  - ";
+    integers_.Print(out, "integers", 0, 6);
+    reals_.Print(out, "reals", 4, 6);
+    strings_.Print(out, "strings", 4, 6);
   }
 
  private:

+ 42 - 2
toolchain/base/value_store_test.cpp

@@ -7,11 +7,17 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-namespace Carbon {
+#include "testing/base/test_raw_ostream.h"
+#include "toolchain/testing/yaml_test_helpers.h"
+
+namespace Carbon::Testing {
 namespace {
 
+using ::testing::ElementsAre;
 using ::testing::Eq;
+using ::testing::IsEmpty;
 using ::testing::Not;
+using ::testing::Pair;
 
 TEST(ValueStore, Integer) {
   SharedValueStores value_stores;
@@ -72,5 +78,39 @@ TEST(ValueStore, String) {
   EXPECT_THAT(value_stores.strings().Add(b), Eq(b_id));
 }
 
+auto MatchSharedValues(testing::Matcher<Yaml::SequenceValue> integers,
+                       testing::Matcher<Yaml::SequenceValue> reals,
+                       testing::Matcher<Yaml::SequenceValue> strings) -> auto {
+  return Yaml::IsYaml(Yaml::Sequence(ElementsAre(Yaml::Mapping(ElementsAre(Pair(
+      "shared_values", Yaml::Sequence(ElementsAre(Yaml::Mapping(ElementsAre(
+                           Pair("integers", Yaml::Sequence(integers)),
+                           Pair("reals", Yaml::Sequence(reals)),
+                           Pair("strings", Yaml::Sequence(strings))))))))))));
+}
+
+TEST(ValueStore, PrintEmpty) {
+  SharedValueStores value_stores;
+  TestRawOstream out;
+  value_stores.Print(out);
+  EXPECT_THAT(Yaml::Value::FromText(out.TakeStr()),
+              MatchSharedValues(IsEmpty(), IsEmpty(), IsEmpty()));
+}
+
+TEST(ValueStore, PrintVals) {
+  SharedValueStores value_stores;
+  llvm::APInt apint(64, 8, /*isSigned=*/true);
+  value_stores.integers().Add(apint);
+  value_stores.reals().Add(
+      Real{.mantissa = apint, .exponent = apint, .is_decimal = true});
+  value_stores.strings().Add("foo'\"baz");
+  TestRawOstream out;
+  value_stores.Print(out);
+
+  EXPECT_THAT(Yaml::Value::FromText(out.TakeStr()),
+              MatchSharedValues(ElementsAre(Yaml::Scalar("8")),
+                                ElementsAre(Yaml::Scalar("8*10^8")),
+                                ElementsAre(Yaml::Scalar("foo'\"baz"))));
+}
+
 }  // namespace
-}  // namespace Carbon
+}  // namespace Carbon::Testing

+ 15 - 22
toolchain/check/testdata/basics/builtin_nodes.carbon

@@ -9,25 +9,18 @@
 // CHECK:STDOUT: - filename: builtin_nodes.carbon
 // CHECK:STDOUT:   sem_ir:
 // CHECK:STDOUT:   - cross_reference_irs_size: 1
-// CHECK:STDOUT:     functions: [
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     classes: [
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     types: [
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     type_blocks: [
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     nodes: [
-// CHECK:STDOUT:       {kind: CrossReference, arg0: ir0, arg1: nodeTypeType, type: typeTypeType},
-// CHECK:STDOUT:       {kind: CrossReference, arg0: ir0, arg1: nodeError, type: typeError},
-// CHECK:STDOUT:       {kind: CrossReference, arg0: ir0, arg1: nodeBoolType, type: typeTypeType},
-// CHECK:STDOUT:       {kind: CrossReference, arg0: ir0, arg1: nodeIntegerType, type: typeTypeType},
-// CHECK:STDOUT:       {kind: CrossReference, arg0: ir0, arg1: nodeFloatingPointType, type: typeTypeType},
-// CHECK:STDOUT:       {kind: CrossReference, arg0: ir0, arg1: nodeStringType, type: typeTypeType},
-// CHECK:STDOUT:       {kind: CrossReference, arg0: ir0, arg1: nodeFunctionType, type: typeTypeType},
-// CHECK:STDOUT:       {kind: CrossReference, arg0: ir0, arg1: nodeNamespaceType, type: typeTypeType},
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     node_blocks: [
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:     ]
+// CHECK:STDOUT:     functions: []
+// CHECK:STDOUT:     classes: []
+// CHECK:STDOUT:     types: []
+// CHECK:STDOUT:     type_blocks: []
+// CHECK:STDOUT:     nodes:
+// CHECK:STDOUT:       - {kind: CrossReference, arg0: ir0, arg1: nodeTypeType, type: typeTypeType}
+// CHECK:STDOUT:       - {kind: CrossReference, arg0: ir0, arg1: nodeError, type: typeError}
+// CHECK:STDOUT:       - {kind: CrossReference, arg0: ir0, arg1: nodeBoolType, type: typeTypeType}
+// CHECK:STDOUT:       - {kind: CrossReference, arg0: ir0, arg1: nodeIntegerType, type: typeTypeType}
+// CHECK:STDOUT:       - {kind: CrossReference, arg0: ir0, arg1: nodeFloatingPointType, type: typeTypeType}
+// CHECK:STDOUT:       - {kind: CrossReference, arg0: ir0, arg1: nodeStringType, type: typeTypeType}
+// CHECK:STDOUT:       - {kind: CrossReference, arg0: ir0, arg1: nodeFunctionType, type: typeTypeType}
+// CHECK:STDOUT:       - {kind: CrossReference, arg0: ir0, arg1: nodeNamespaceType, type: typeTypeType}
+// CHECK:STDOUT:     node_blocks:
+// CHECK:STDOUT:       - []

+ 26 - 48
toolchain/check/testdata/basics/multifile_raw_and_textual_ir.carbon

@@ -17,30 +17,19 @@ fn B() {}
 // CHECK:STDOUT: - filename: a.carbon
 // CHECK:STDOUT:   sem_ir:
 // CHECK:STDOUT:   - cross_reference_irs_size: 1
-// CHECK:STDOUT:     functions: [
-// CHECK:STDOUT:       {name: str0, param_refs: block0, body: [block1]},
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     classes: [
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     types: [
-// CHECK:STDOUT:       {node: nodeFunctionType, value_rep: {kind: copy, type: type0}},
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     type_blocks: [
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     nodes: [
-// CHECK:STDOUT:       {kind: FunctionDeclaration, arg0: function0, type: type0},
-// CHECK:STDOUT:       {kind: Return},
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     node_blocks: [
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:         node+1,
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:         node+0,
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:     ]
+// CHECK:STDOUT:     functions:
+// CHECK:STDOUT:       - {name: str0, param_refs: block0, body: [block1]}
+// CHECK:STDOUT:     classes: []
+// CHECK:STDOUT:     types:
+// CHECK:STDOUT:       - {node: nodeFunctionType, value_rep: {kind: copy, type: type0}}
+// CHECK:STDOUT:     type_blocks: []
+// CHECK:STDOUT:     nodes:
+// CHECK:STDOUT:       - {kind: FunctionDeclaration, arg0: function0, type: type0}
+// CHECK:STDOUT:       - {kind: Return}
+// CHECK:STDOUT:     node_blocks:
+// CHECK:STDOUT:       - []
+// CHECK:STDOUT:       - - node+1
+// CHECK:STDOUT:       - - node+0
 // CHECK:STDOUT:
 // CHECK:STDOUT: file "a.carbon" {
 // CHECK:STDOUT:   %A: <function> = fn_decl @A
@@ -53,30 +42,19 @@ fn B() {}
 // CHECK:STDOUT: - filename: b.carbon
 // CHECK:STDOUT:   sem_ir:
 // CHECK:STDOUT:   - cross_reference_irs_size: 1
-// CHECK:STDOUT:     functions: [
-// CHECK:STDOUT:       {name: str1, param_refs: block0, body: [block1]},
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     classes: [
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     types: [
-// CHECK:STDOUT:       {node: nodeFunctionType, value_rep: {kind: copy, type: type0}},
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     type_blocks: [
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     nodes: [
-// CHECK:STDOUT:       {kind: FunctionDeclaration, arg0: function0, type: type0},
-// CHECK:STDOUT:       {kind: Return},
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     node_blocks: [
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:         node+1,
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:         node+0,
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:     ]
+// CHECK:STDOUT:     functions:
+// CHECK:STDOUT:       - {name: str1, param_refs: block0, body: [block1]}
+// CHECK:STDOUT:     classes: []
+// CHECK:STDOUT:     types:
+// CHECK:STDOUT:       - {node: nodeFunctionType, value_rep: {kind: copy, type: type0}}
+// CHECK:STDOUT:     type_blocks: []
+// CHECK:STDOUT:     nodes:
+// CHECK:STDOUT:       - {kind: FunctionDeclaration, arg0: function0, type: type0}
+// CHECK:STDOUT:       - {kind: Return}
+// CHECK:STDOUT:     node_blocks:
+// CHECK:STDOUT:       - []
+// CHECK:STDOUT:       - - node+1
+// CHECK:STDOUT:       - - node+0
 // CHECK:STDOUT:
 // CHECK:STDOUT: file "b.carbon" {
 // CHECK:STDOUT:   %B: <function> = fn_decl @B

+ 26 - 48
toolchain/check/testdata/basics/multifile_raw_ir.carbon

@@ -17,54 +17,32 @@ fn B() {}
 // CHECK:STDOUT: - filename: a.carbon
 // CHECK:STDOUT:   sem_ir:
 // CHECK:STDOUT:   - cross_reference_irs_size: 1
-// CHECK:STDOUT:     functions: [
-// CHECK:STDOUT:       {name: str0, param_refs: block0, body: [block1]},
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     classes: [
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     types: [
-// CHECK:STDOUT:       {node: nodeFunctionType, value_rep: {kind: copy, type: type0}},
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     type_blocks: [
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     nodes: [
-// CHECK:STDOUT:       {kind: FunctionDeclaration, arg0: function0, type: type0},
-// CHECK:STDOUT:       {kind: Return},
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     node_blocks: [
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:         node+1,
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:         node+0,
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:     ]
+// CHECK:STDOUT:     functions:
+// CHECK:STDOUT:       - {name: str0, param_refs: block0, body: [block1]}
+// CHECK:STDOUT:     classes: []
+// CHECK:STDOUT:     types:
+// CHECK:STDOUT:       - {node: nodeFunctionType, value_rep: {kind: copy, type: type0}}
+// CHECK:STDOUT:     type_blocks: []
+// CHECK:STDOUT:     nodes:
+// CHECK:STDOUT:       - {kind: FunctionDeclaration, arg0: function0, type: type0}
+// CHECK:STDOUT:       - {kind: Return}
+// CHECK:STDOUT:     node_blocks:
+// CHECK:STDOUT:       - []
+// CHECK:STDOUT:       - - node+1
+// CHECK:STDOUT:       - - node+0
 // CHECK:STDOUT: - filename: b.carbon
 // CHECK:STDOUT:   sem_ir:
 // CHECK:STDOUT:   - cross_reference_irs_size: 1
-// CHECK:STDOUT:     functions: [
-// CHECK:STDOUT:       {name: str1, param_refs: block0, body: [block1]},
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     classes: [
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     types: [
-// CHECK:STDOUT:       {node: nodeFunctionType, value_rep: {kind: copy, type: type0}},
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     type_blocks: [
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     nodes: [
-// CHECK:STDOUT:       {kind: FunctionDeclaration, arg0: function0, type: type0},
-// CHECK:STDOUT:       {kind: Return},
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     node_blocks: [
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:         node+1,
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:         node+0,
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:     ]
+// CHECK:STDOUT:     functions:
+// CHECK:STDOUT:       - {name: str1, param_refs: block0, body: [block1]}
+// CHECK:STDOUT:     classes: []
+// CHECK:STDOUT:     types:
+// CHECK:STDOUT:       - {node: nodeFunctionType, value_rep: {kind: copy, type: type0}}
+// CHECK:STDOUT:     type_blocks: []
+// CHECK:STDOUT:     nodes:
+// CHECK:STDOUT:       - {kind: FunctionDeclaration, arg0: function0, type: type0}
+// CHECK:STDOUT:       - {kind: Return}
+// CHECK:STDOUT:     node_blocks:
+// CHECK:STDOUT:       - []
+// CHECK:STDOUT:       - - node+1
+// CHECK:STDOUT:       - - node+0

+ 61 - 86
toolchain/check/testdata/basics/raw_and_textual_ir.carbon

@@ -15,92 +15,67 @@ fn Foo(n: i32) -> (i32, f64) {
 // CHECK:STDOUT: - filename: raw_and_textual_ir.carbon
 // CHECK:STDOUT:   sem_ir:
 // CHECK:STDOUT:   - cross_reference_irs_size: 1
-// CHECK:STDOUT:     functions: [
-// CHECK:STDOUT:       {name: str0, param_refs: block1, return_type: type3, return_slot: node+4, body: [block4]},
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     classes: [
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     types: [
-// CHECK:STDOUT:       {node: nodeIntegerType, value_rep: {kind: copy, type: type0}},
-// CHECK:STDOUT:       {node: node+1, value_rep: {kind: unknown, type: type<invalid>}},
-// CHECK:STDOUT:       {node: nodeFloatingPointType, value_rep: {kind: copy, type: type2}},
-// CHECK:STDOUT:       {node: node+3, value_rep: {kind: pointer, type: type4}},
-// CHECK:STDOUT:       {node: node+5, value_rep: {kind: copy, type: type4}},
-// CHECK:STDOUT:       {node: nodeFunctionType, value_rep: {kind: copy, type: type5}},
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     type_blocks: [
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:         typeTypeType,
-// CHECK:STDOUT:         typeTypeType,
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:         type0,
-// CHECK:STDOUT:         type2,
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     nodes: [
-// CHECK:STDOUT:       {kind: Parameter, arg0: str1, type: type0},
-// CHECK:STDOUT:       {kind: TupleType, arg0: typeBlock0, type: typeTypeType},
-// CHECK:STDOUT:       {kind: TupleLiteral, arg0: block2, type: type1},
-// CHECK:STDOUT:       {kind: TupleType, arg0: typeBlock1, type: typeTypeType},
-// CHECK:STDOUT:       {kind: VarStorage, arg0: str2, type: type3},
-// CHECK:STDOUT:       {kind: PointerType, arg0: type3, type: typeTypeType},
-// CHECK:STDOUT:       {kind: FunctionDeclaration, arg0: function0, type: type5},
-// CHECK:STDOUT:       {kind: NameReference, arg0: str1, arg1: node+0, type: type0},
-// CHECK:STDOUT:       {kind: IntegerLiteral, arg0: int3, type: type0},
-// CHECK:STDOUT:       {kind: BinaryOperatorAdd, arg0: node+7, arg1: node+8, type: type0},
-// CHECK:STDOUT:       {kind: RealLiteral, arg0: real0, type: type2},
-// CHECK:STDOUT:       {kind: TupleLiteral, arg0: block5, type: type3},
-// CHECK:STDOUT:       {kind: TupleAccess, arg0: node+4, arg1: member0, type: type0},
-// CHECK:STDOUT:       {kind: InitializeFrom, arg0: node+9, arg1: node+12, type: type0},
-// CHECK:STDOUT:       {kind: TupleAccess, arg0: node+4, arg1: member1, type: type2},
-// CHECK:STDOUT:       {kind: InitializeFrom, arg0: node+10, arg1: node+14, type: type2},
-// CHECK:STDOUT:       {kind: TupleInit, arg0: node+11, arg1: block6, type: type3},
-// CHECK:STDOUT:       {kind: ReturnExpression, arg0: node+16},
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     node_blocks: [
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:         node+0,
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:         nodeIntegerType,
-// CHECK:STDOUT:         nodeFloatingPointType,
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:         node+0,
-// CHECK:STDOUT:         node+1,
-// CHECK:STDOUT:         node+2,
-// CHECK:STDOUT:         node+3,
-// CHECK:STDOUT:         node+4,
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:         node+7,
-// CHECK:STDOUT:         node+8,
-// CHECK:STDOUT:         node+9,
-// CHECK:STDOUT:         node+10,
-// CHECK:STDOUT:         node+11,
-// CHECK:STDOUT:         node+12,
-// CHECK:STDOUT:         node+13,
-// CHECK:STDOUT:         node+14,
-// CHECK:STDOUT:         node+15,
-// CHECK:STDOUT:         node+16,
-// CHECK:STDOUT:         node+17,
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:         node+9,
-// CHECK:STDOUT:         node+10,
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:         node+13,
-// CHECK:STDOUT:         node+15,
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:         node+5,
-// CHECK:STDOUT:         node+6,
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:     ]
+// CHECK:STDOUT:     functions:
+// CHECK:STDOUT:       - {name: str0, param_refs: block1, return_type: type3, return_slot: node+4, body: [block4]}
+// CHECK:STDOUT:     classes: []
+// CHECK:STDOUT:     types:
+// CHECK:STDOUT:       - {node: nodeIntegerType, value_rep: {kind: copy, type: type0}}
+// CHECK:STDOUT:       - {node: node+1, value_rep: {kind: unknown, type: type<invalid>}}
+// CHECK:STDOUT:       - {node: nodeFloatingPointType, value_rep: {kind: copy, type: type2}}
+// CHECK:STDOUT:       - {node: node+3, value_rep: {kind: pointer, type: type4}}
+// CHECK:STDOUT:       - {node: node+5, value_rep: {kind: copy, type: type4}}
+// CHECK:STDOUT:       - {node: nodeFunctionType, value_rep: {kind: copy, type: type5}}
+// CHECK:STDOUT:     type_blocks:
+// CHECK:STDOUT:       - - typeTypeType
+// CHECK:STDOUT:         - typeTypeType
+// CHECK:STDOUT:       - - type0
+// CHECK:STDOUT:         - type2
+// CHECK:STDOUT:     nodes:
+// CHECK:STDOUT:       - {kind: Parameter, arg0: str1, type: type0}
+// CHECK:STDOUT:       - {kind: TupleType, arg0: typeBlock0, type: typeTypeType}
+// CHECK:STDOUT:       - {kind: TupleLiteral, arg0: block2, type: type1}
+// CHECK:STDOUT:       - {kind: TupleType, arg0: typeBlock1, type: typeTypeType}
+// CHECK:STDOUT:       - {kind: VarStorage, arg0: str2, type: type3}
+// CHECK:STDOUT:       - {kind: PointerType, arg0: type3, type: typeTypeType}
+// CHECK:STDOUT:       - {kind: FunctionDeclaration, arg0: function0, type: type5}
+// CHECK:STDOUT:       - {kind: NameReference, arg0: str1, arg1: node+0, type: type0}
+// CHECK:STDOUT:       - {kind: IntegerLiteral, arg0: int3, type: type0}
+// CHECK:STDOUT:       - {kind: BinaryOperatorAdd, arg0: node+7, arg1: node+8, type: type0}
+// CHECK:STDOUT:       - {kind: RealLiteral, arg0: real0, type: type2}
+// CHECK:STDOUT:       - {kind: TupleLiteral, arg0: block5, type: type3}
+// CHECK:STDOUT:       - {kind: TupleAccess, arg0: node+4, arg1: member0, type: type0}
+// CHECK:STDOUT:       - {kind: InitializeFrom, arg0: node+9, arg1: node+12, type: type0}
+// CHECK:STDOUT:       - {kind: TupleAccess, arg0: node+4, arg1: member1, type: type2}
+// CHECK:STDOUT:       - {kind: InitializeFrom, arg0: node+10, arg1: node+14, type: type2}
+// CHECK:STDOUT:       - {kind: TupleInit, arg0: node+11, arg1: block6, type: type3}
+// CHECK:STDOUT:       - {kind: ReturnExpression, arg0: node+16}
+// CHECK:STDOUT:     node_blocks:
+// CHECK:STDOUT:       - []
+// CHECK:STDOUT:       - - node+0
+// CHECK:STDOUT:       - - nodeIntegerType
+// CHECK:STDOUT:         - nodeFloatingPointType
+// CHECK:STDOUT:       - - node+0
+// CHECK:STDOUT:         - node+1
+// CHECK:STDOUT:         - node+2
+// CHECK:STDOUT:         - node+3
+// CHECK:STDOUT:         - node+4
+// CHECK:STDOUT:       - - node+7
+// CHECK:STDOUT:         - node+8
+// CHECK:STDOUT:         - node+9
+// CHECK:STDOUT:         - node+10
+// CHECK:STDOUT:         - node+11
+// CHECK:STDOUT:         - node+12
+// CHECK:STDOUT:         - node+13
+// CHECK:STDOUT:         - node+14
+// CHECK:STDOUT:         - node+15
+// CHECK:STDOUT:         - node+16
+// CHECK:STDOUT:         - node+17
+// CHECK:STDOUT:       - - node+9
+// CHECK:STDOUT:         - node+10
+// CHECK:STDOUT:       - - node+13
+// CHECK:STDOUT:         - node+15
+// CHECK:STDOUT:       - - node+5
+// CHECK:STDOUT:         - node+6
 // CHECK:STDOUT:
 // CHECK:STDOUT: file "raw_and_textual_ir.carbon" {
 // CHECK:STDOUT:   %.loc11: type = ptr_type (i32, f64)

+ 61 - 86
toolchain/check/testdata/basics/raw_ir.carbon

@@ -15,89 +15,64 @@ fn Foo(n: i32) -> (i32, f64) {
 // CHECK:STDOUT: - filename: raw_ir.carbon
 // CHECK:STDOUT:   sem_ir:
 // CHECK:STDOUT:   - cross_reference_irs_size: 1
-// CHECK:STDOUT:     functions: [
-// CHECK:STDOUT:       {name: str0, param_refs: block1, return_type: type3, return_slot: node+4, body: [block4]},
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     classes: [
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     types: [
-// CHECK:STDOUT:       {node: nodeIntegerType, value_rep: {kind: copy, type: type0}},
-// CHECK:STDOUT:       {node: node+1, value_rep: {kind: unknown, type: type<invalid>}},
-// CHECK:STDOUT:       {node: nodeFloatingPointType, value_rep: {kind: copy, type: type2}},
-// CHECK:STDOUT:       {node: node+3, value_rep: {kind: pointer, type: type4}},
-// CHECK:STDOUT:       {node: node+5, value_rep: {kind: copy, type: type4}},
-// CHECK:STDOUT:       {node: nodeFunctionType, value_rep: {kind: copy, type: type5}},
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     type_blocks: [
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:         typeTypeType,
-// CHECK:STDOUT:         typeTypeType,
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:         type0,
-// CHECK:STDOUT:         type2,
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     nodes: [
-// CHECK:STDOUT:       {kind: Parameter, arg0: str1, type: type0},
-// CHECK:STDOUT:       {kind: TupleType, arg0: typeBlock0, type: typeTypeType},
-// CHECK:STDOUT:       {kind: TupleLiteral, arg0: block2, type: type1},
-// CHECK:STDOUT:       {kind: TupleType, arg0: typeBlock1, type: typeTypeType},
-// CHECK:STDOUT:       {kind: VarStorage, arg0: str2, type: type3},
-// CHECK:STDOUT:       {kind: PointerType, arg0: type3, type: typeTypeType},
-// CHECK:STDOUT:       {kind: FunctionDeclaration, arg0: function0, type: type5},
-// CHECK:STDOUT:       {kind: NameReference, arg0: str1, arg1: node+0, type: type0},
-// CHECK:STDOUT:       {kind: IntegerLiteral, arg0: int3, type: type0},
-// CHECK:STDOUT:       {kind: BinaryOperatorAdd, arg0: node+7, arg1: node+8, type: type0},
-// CHECK:STDOUT:       {kind: RealLiteral, arg0: real0, type: type2},
-// CHECK:STDOUT:       {kind: TupleLiteral, arg0: block5, type: type3},
-// CHECK:STDOUT:       {kind: TupleAccess, arg0: node+4, arg1: member0, type: type0},
-// CHECK:STDOUT:       {kind: InitializeFrom, arg0: node+9, arg1: node+12, type: type0},
-// CHECK:STDOUT:       {kind: TupleAccess, arg0: node+4, arg1: member1, type: type2},
-// CHECK:STDOUT:       {kind: InitializeFrom, arg0: node+10, arg1: node+14, type: type2},
-// CHECK:STDOUT:       {kind: TupleInit, arg0: node+11, arg1: block6, type: type3},
-// CHECK:STDOUT:       {kind: ReturnExpression, arg0: node+16},
-// CHECK:STDOUT:     ]
-// CHECK:STDOUT:     node_blocks: [
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:         node+0,
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:         nodeIntegerType,
-// CHECK:STDOUT:         nodeFloatingPointType,
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:         node+0,
-// CHECK:STDOUT:         node+1,
-// CHECK:STDOUT:         node+2,
-// CHECK:STDOUT:         node+3,
-// CHECK:STDOUT:         node+4,
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:         node+7,
-// CHECK:STDOUT:         node+8,
-// CHECK:STDOUT:         node+9,
-// CHECK:STDOUT:         node+10,
-// CHECK:STDOUT:         node+11,
-// CHECK:STDOUT:         node+12,
-// CHECK:STDOUT:         node+13,
-// CHECK:STDOUT:         node+14,
-// CHECK:STDOUT:         node+15,
-// CHECK:STDOUT:         node+16,
-// CHECK:STDOUT:         node+17,
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:         node+9,
-// CHECK:STDOUT:         node+10,
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:         node+13,
-// CHECK:STDOUT:         node+15,
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:       [
-// CHECK:STDOUT:         node+5,
-// CHECK:STDOUT:         node+6,
-// CHECK:STDOUT:       ],
-// CHECK:STDOUT:     ]
+// CHECK:STDOUT:     functions:
+// CHECK:STDOUT:       - {name: str0, param_refs: block1, return_type: type3, return_slot: node+4, body: [block4]}
+// CHECK:STDOUT:     classes: []
+// CHECK:STDOUT:     types:
+// CHECK:STDOUT:       - {node: nodeIntegerType, value_rep: {kind: copy, type: type0}}
+// CHECK:STDOUT:       - {node: node+1, value_rep: {kind: unknown, type: type<invalid>}}
+// CHECK:STDOUT:       - {node: nodeFloatingPointType, value_rep: {kind: copy, type: type2}}
+// CHECK:STDOUT:       - {node: node+3, value_rep: {kind: pointer, type: type4}}
+// CHECK:STDOUT:       - {node: node+5, value_rep: {kind: copy, type: type4}}
+// CHECK:STDOUT:       - {node: nodeFunctionType, value_rep: {kind: copy, type: type5}}
+// CHECK:STDOUT:     type_blocks:
+// CHECK:STDOUT:       - - typeTypeType
+// CHECK:STDOUT:         - typeTypeType
+// CHECK:STDOUT:       - - type0
+// CHECK:STDOUT:         - type2
+// CHECK:STDOUT:     nodes:
+// CHECK:STDOUT:       - {kind: Parameter, arg0: str1, type: type0}
+// CHECK:STDOUT:       - {kind: TupleType, arg0: typeBlock0, type: typeTypeType}
+// CHECK:STDOUT:       - {kind: TupleLiteral, arg0: block2, type: type1}
+// CHECK:STDOUT:       - {kind: TupleType, arg0: typeBlock1, type: typeTypeType}
+// CHECK:STDOUT:       - {kind: VarStorage, arg0: str2, type: type3}
+// CHECK:STDOUT:       - {kind: PointerType, arg0: type3, type: typeTypeType}
+// CHECK:STDOUT:       - {kind: FunctionDeclaration, arg0: function0, type: type5}
+// CHECK:STDOUT:       - {kind: NameReference, arg0: str1, arg1: node+0, type: type0}
+// CHECK:STDOUT:       - {kind: IntegerLiteral, arg0: int3, type: type0}
+// CHECK:STDOUT:       - {kind: BinaryOperatorAdd, arg0: node+7, arg1: node+8, type: type0}
+// CHECK:STDOUT:       - {kind: RealLiteral, arg0: real0, type: type2}
+// CHECK:STDOUT:       - {kind: TupleLiteral, arg0: block5, type: type3}
+// CHECK:STDOUT:       - {kind: TupleAccess, arg0: node+4, arg1: member0, type: type0}
+// CHECK:STDOUT:       - {kind: InitializeFrom, arg0: node+9, arg1: node+12, type: type0}
+// CHECK:STDOUT:       - {kind: TupleAccess, arg0: node+4, arg1: member1, type: type2}
+// CHECK:STDOUT:       - {kind: InitializeFrom, arg0: node+10, arg1: node+14, type: type2}
+// CHECK:STDOUT:       - {kind: TupleInit, arg0: node+11, arg1: block6, type: type3}
+// CHECK:STDOUT:       - {kind: ReturnExpression, arg0: node+16}
+// CHECK:STDOUT:     node_blocks:
+// CHECK:STDOUT:       - []
+// CHECK:STDOUT:       - - node+0
+// CHECK:STDOUT:       - - nodeIntegerType
+// CHECK:STDOUT:         - nodeFloatingPointType
+// CHECK:STDOUT:       - - node+0
+// CHECK:STDOUT:         - node+1
+// CHECK:STDOUT:         - node+2
+// CHECK:STDOUT:         - node+3
+// CHECK:STDOUT:         - node+4
+// CHECK:STDOUT:       - - node+7
+// CHECK:STDOUT:         - node+8
+// CHECK:STDOUT:         - node+9
+// CHECK:STDOUT:         - node+10
+// CHECK:STDOUT:         - node+11
+// CHECK:STDOUT:         - node+12
+// CHECK:STDOUT:         - node+13
+// CHECK:STDOUT:         - node+14
+// CHECK:STDOUT:         - node+15
+// CHECK:STDOUT:         - node+16
+// CHECK:STDOUT:         - node+17
+// CHECK:STDOUT:       - - node+9
+// CHECK:STDOUT:         - node+10
+// CHECK:STDOUT:       - - node+13
+// CHECK:STDOUT:         - node+15
+// CHECK:STDOUT:       - - node+5
+// CHECK:STDOUT:         - node+6

+ 27 - 7
toolchain/driver/testdata/dump_shared_values.carbon

@@ -6,15 +6,35 @@
 //
 // AUTOUPDATE
 
-var a: f64 = 1.0;
-var b: String = "ab\"c";
+// The value 8 is significant because it can show as negative due to APInt.
+var int1: i32 = 1;
+var int2: i32 = 8;
+var real1: f64 = 1.0;
+var real2: f64 = 0.8e8;
+var real3: f64 = 0.8e9;
+var str1: String = "abc";
+var str2: String = "ab'\"c";
 
 // CHECK:STDOUT: shared_values:
 // CHECK:STDOUT:   - integers:
+// CHECK:STDOUT:       - 32
+// CHECK:STDOUT:       - 1
+// CHECK:STDOUT:       - 32
+// CHECK:STDOUT:       - 8
 // CHECK:STDOUT:       - 64
-// CHECK:STDOUT:   - reals:
+// CHECK:STDOUT:       - 64
+// CHECK:STDOUT:       - 64
+// CHECK:STDOUT:     reals:
 // CHECK:STDOUT:       - 10*10^-1
-// CHECK:STDOUT:   - strings:
-// CHECK:STDOUT:       - "a"
-// CHECK:STDOUT:       - "b"
-// CHECK:STDOUT:       - "ab\"c"
+// CHECK:STDOUT:       - 8*10^7
+// CHECK:STDOUT:       - 8*10^8
+// CHECK:STDOUT:     strings:
+// CHECK:STDOUT:       - "int1"
+// CHECK:STDOUT:       - "int2"
+// CHECK:STDOUT:       - "real1"
+// CHECK:STDOUT:       - "real2"
+// CHECK:STDOUT:       - "real3"
+// CHECK:STDOUT:       - "str1"
+// CHECK:STDOUT:       - "abc"
+// CHECK:STDOUT:       - "str2"
+// CHECK:STDOUT:       - "ab'\"c"

+ 9 - 49
toolchain/sem_ir/file.cpp

@@ -6,7 +6,6 @@
 
 #include "common/check.h"
 #include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/Sequence.h"
 #include "llvm/ADT/SmallVector.h"
 #include "toolchain/base/value_store.h"
 #include "toolchain/sem_ir/builtin_kind.h"
@@ -130,66 +129,27 @@ auto File::Verify() const -> ErrorOr<Success> {
   return Success();
 }
 
-static constexpr int BaseIndent = 4;
-static constexpr int IndentStep = 2;
-
-// Prints a list of elements.
-template <typename T, typename PrintT =
-                          std::function<void(llvm::raw_ostream&, const T& val)>>
-static auto PrintList(
-    llvm::raw_ostream& out, llvm::StringLiteral name, llvm::ArrayRef<T> list,
-    PrintT print = [](llvm::raw_ostream& out, const T& val) { out << val; }) {
-  out.indent(BaseIndent);
-  out << name << ": [\n";
-  for (const auto& element : list) {
-    out.indent(BaseIndent + IndentStep);
-    print(out, element);
-    out << ",\n";
-  }
-  out.indent(BaseIndent);
-  out << "]\n";
-}
-
-// PrintBlock is only used for vectors.
-template <typename BlockIdT, typename BlockValueStoreT>
-static auto PrintBlock(llvm::raw_ostream& out, llvm::StringLiteral block_name,
-                       const BlockValueStoreT& blocks) {
-  out.indent(BaseIndent);
-  out << block_name << ": [\n";
-  for (int index : llvm::seq(blocks.size())) {
-    auto block = blocks.Get(BlockIdT(index));
-    out.indent(BaseIndent + IndentStep);
-    out << "[\n";
-
-    for (const auto& node : block) {
-      out.indent(BaseIndent + 2 * IndentStep);
-      out << node << ",\n";
-    }
-    out.indent(BaseIndent + IndentStep);
-    out << "],\n";
-  }
-  out.indent(BaseIndent);
-  out << "]\n";
-}
-
 auto File::Print(llvm::raw_ostream& out, bool include_builtins) const -> void {
   out << "- filename: " << filename_ << "\n"
       << "  sem_ir:\n"
       << "  - cross_reference_irs_size: " << cross_reference_irs_.size()
       << "\n";
 
-  PrintList(out, "functions", functions_.array_ref());
-  PrintList(out, "classes", classes_.array_ref());
-  PrintList(out, "types", types_.array_ref());
-  PrintBlock<TypeBlockId>(out, "type_blocks", type_blocks_);
+  static constexpr int FirstLineIndent = 4;
+  static constexpr int LaterIndent = 6;
+  functions_.Print(out, "functions", FirstLineIndent, LaterIndent);
+  classes_.Print(out, "classes", FirstLineIndent, LaterIndent);
+  types_.Print(out, "types", FirstLineIndent, LaterIndent);
+  type_blocks_.Print(out, "type_blocks", FirstLineIndent, LaterIndent);
 
   auto nodes = nodes_.array_ref();
   if (!include_builtins) {
     nodes = nodes.drop_front(BuiltinKind::ValidCount);
   }
-  PrintList(out, "nodes", nodes);
+  PrintValueRange(out, llvm::iterator_range(nodes), "nodes", FirstLineIndent,
+                  LaterIndent, /*trailing_newline=*/true);
 
-  PrintBlock<NodeBlockId>(out, "node_blocks", node_blocks_);
+  node_blocks_.Print(out, "node_blocks", FirstLineIndent, LaterIndent);
 }
 
 // Map a node kind representing a type into an integer describing the

+ 14 - 0
toolchain/sem_ir/value_stores.h

@@ -88,6 +88,20 @@ class BlockValueStore {
   // Returns the requested block.
   auto Get(IdT id) -> llvm::MutableArrayRef<ValueT> { return values_.Get(id); }
 
+  auto Print(llvm::raw_ostream& out) const -> void {
+    Print(out, std::nullopt, 0, 0);
+  }
+  auto Print(llvm::raw_ostream& out, std::optional<llvm::StringRef> label,
+             int first_line_indent, int later_indent) const -> void {
+    values_.Print(out, label, first_line_indent, later_indent,
+                  [&](llvm::raw_ostream& out,
+                      const llvm::MutableArrayRef<ValueT>& value) {
+                    PrintValueRange<ValueT>(out, llvm::iterator_range(value),
+                                            std::nullopt, 0, later_indent + 2,
+                                            /*trailing_newline=*/false);
+                  });
+  }
+
   auto size() const -> int { return values_.size(); }
 
  protected: