Browse Source

Start adding builtins to SemanticsIR (#2356)

This starts adding builtins with TypeType and IntegerLiteralType. Note, structurally that's all they are, and not directly accessible in any way.

Adds a type field to SemanticsNode. Now, IntegerLiteralType can be identified as having type=TypeType, and IntegerLiteral as type=IntegerLiteralType. The current iteration doesn't do anything for type propagation, because I wanted to avoid making this too big.

This also switches the Identifier IR to instead BindName, with some side-effects. I'd been trying to think how to provide a name for TypeType, and switching around how things worked seemed like a better approach. And while I think it's the right direction (e.g., alias should just be a BindName), I also realized I don't need to name TypeType: there's probably a keyword to refer to the builtin, so it shouldn't use regular name lookup.
Jon Ross-Perkins 3 years ago
parent
commit
57090142e8

+ 3 - 2
toolchain/driver/driver.cpp

@@ -162,8 +162,9 @@ auto Driver::RunDumpSubcommand(DiagnosticConsumer& consumer,
     return !tokenized_source.has_errors() && !parse_tree.has_errors();
   }
 
-  SemanticsIR semantics_ir;
-  semantics_ir.Build(tokenized_source, parse_tree);
+  const SemanticsIR builtin_ir = SemanticsIR::MakeBuiltinIR();
+  const SemanticsIR semantics_ir =
+      SemanticsIR::MakeFromParseTree(builtin_ir, tokenized_source, parse_tree);
   if (dump_mode == DumpMode::SemanticsIR) {
     consumer.Flush();
     semantics_ir.Print(output_stream_);

+ 12 - 0
toolchain/semantics/BUILD

@@ -4,6 +4,17 @@
 
 package(default_visibility = ["//visibility:public"])
 
+cc_library(
+    name = "semantics_builtin_kind",
+    srcs = ["semantics_builtin_kind.cpp"],
+    hdrs = ["semantics_builtin_kind.h"],
+    textual_hdrs = ["semantics_builtin_kind.def"],
+    deps = [
+        "//common:ostream",
+        "@llvm-project//llvm:Support",
+    ],
+)
+
 cc_library(
     name = "semantics_node_kind",
     srcs = ["semantics_node_kind.cpp"],
@@ -28,6 +39,7 @@ cc_library(
         "semantics_node.h",
     ],
     deps = [
+        ":semantics_builtin_kind",
         ":semantics_node_kind",
         "//common:check",
         "//common:ostream",

+ 19 - 0
toolchain/semantics/semantics_builtin_kind.cpp

@@ -0,0 +1,19 @@
+// 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 "toolchain/semantics/semantics_builtin_kind.h"
+
+#include "llvm/ADT/StringRef.h"
+
+namespace Carbon {
+
+auto SemanticsBuiltinKind::name() const -> llvm::StringRef {
+  static constexpr llvm::StringLiteral Names[] = {
+#define CARBON_SEMANTICS_BUILTIN_KIND(Name) #Name,
+#include "toolchain/semantics/semantics_builtin_kind.def"
+  };
+  return Names[static_cast<int>(kind_)];
+}
+
+}  // namespace Carbon

+ 23 - 0
toolchain/semantics/semantics_builtin_kind.def

@@ -0,0 +1,23 @@
+// 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
+//
+// Note that this is an X-macro header.
+//
+// It does not use `#include` guards, and instead is designed to be `#include`ed
+// after the x-macro is defined in order for its inclusion to expand to the
+// desired output. The x-macro for this header is `CARBON_PARSE_NODE_KIND`. The
+// definition provided will be removed at the end of this file to clean up.
+
+#ifndef CARBON_SEMANTICS_BUILTIN_KIND
+#error "Must define the x-macro to use this file."
+#endif
+
+CARBON_SEMANTICS_BUILTIN_KIND(TypeType)
+CARBON_SEMANTICS_BUILTIN_KIND(IntegerLiteralType)
+
+// Keep invalid last, so that we can use values as array indices without needing
+// an invalid entry.
+CARBON_SEMANTICS_BUILTIN_KIND(Invalid)
+
+#undef CARBON_SEMANTICS_BUILTIN_KIND

+ 78 - 0
toolchain/semantics/semantics_builtin_kind.h

@@ -0,0 +1,78 @@
+// 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_SEMANTICS_SEMANTICS_BUILTIN_KIND_H_
+#define CARBON_TOOLCHAIN_SEMANTICS_SEMANTICS_BUILTIN_KIND_H_
+
+#include <cstdint>
+
+#include "common/ostream.h"
+
+namespace Carbon {
+
+class SemanticsBuiltinKind {
+ private:
+  // Note that this must be declared earlier in the class so that its type can
+  // be used, for example in the conversion operator.
+  enum class KindEnum : uint8_t {
+#define CARBON_SEMANTICS_BUILTIN_KIND(Name) Name,
+#include "toolchain/semantics/semantics_builtin_kind.def"
+  };
+
+ public:
+  // The count of enum values excluding Invalid.
+  static constexpr uint8_t ValidCount = static_cast<uint8_t>(KindEnum::Invalid);
+
+  // `clang-format` has a bug with spacing around `->` returns in macros. See
+  // https://bugs.llvm.org/show_bug.cgi?id=48320 for details.
+#define CARBON_SEMANTICS_BUILTIN_KIND(Name)            \
+  static constexpr auto Name()->SemanticsBuiltinKind { \
+    return SemanticsBuiltinKind(KindEnum::Name);       \
+  }
+#include "toolchain/semantics/semantics_builtin_kind.def"
+
+  // The default constructor is deleted because objects of this type should
+  // always be constructed using the above factory functions for each unique
+  // kind.
+  SemanticsBuiltinKind() = delete;
+
+  friend auto operator==(SemanticsBuiltinKind lhs, SemanticsBuiltinKind rhs)
+      -> bool {
+    return lhs.kind_ == rhs.kind_;
+  }
+  friend auto operator!=(SemanticsBuiltinKind lhs, SemanticsBuiltinKind rhs)
+      -> bool {
+    return lhs.kind_ != rhs.kind_;
+  }
+
+  // Gets a friendly name for the token for logging or debugging.
+  [[nodiscard]] auto name() const -> llvm::StringRef;
+
+  // Support conversion to and from an int32_t for SemanticNode storage.
+  auto AsInt() -> int32_t { return static_cast<int32_t>(kind_); }
+  static auto FromInt(int32_t val) -> SemanticsBuiltinKind {
+    return SemanticsBuiltinKind(static_cast<KindEnum>(val));
+  }
+
+  // Enable conversion to our private enum, including in a `constexpr`
+  // context, to enable usage in `switch` and `case`. The enum remains
+  // private and nothing else should be using this function.
+  // NOLINTNEXTLINE(google-explicit-constructor)
+  constexpr operator KindEnum() const { return kind_; }
+
+  void Print(llvm::raw_ostream& out) const { out << name(); }
+
+ private:
+  constexpr explicit SemanticsBuiltinKind(KindEnum k) : kind_(k) {}
+
+  KindEnum kind_;
+};
+
+// We expect the builtin kind to fit compactly into 8 bits.
+static_assert(sizeof(SemanticsBuiltinKind) == 1,
+              "Kind objects include padding!");
+
+}  // namespace Carbon
+
+#endif  // CARBON_TOOLCHAIN_SEMANTICS_SEMANTICS_BUILTIN_KIND_H_

+ 49 - 3
toolchain/semantics/semantics_ir.cpp

@@ -7,19 +7,65 @@
 #include "common/check.h"
 #include "llvm/Support/FormatVariadic.h"
 #include "toolchain/lexer/tokenized_buffer.h"
+#include "toolchain/semantics/semantics_builtin_kind.h"
 #include "toolchain/semantics/semantics_node.h"
 #include "toolchain/semantics/semantics_parse_tree_handler.h"
 
 namespace Carbon {
 
-auto SemanticsIR::Build(const TokenizedBuffer& tokens,
-                        const ParseTree& parse_tree) -> void {
-  SemanticsParseTreeHandler(tokens, parse_tree, *this).Build();
+auto SemanticsIR::MakeBuiltinIR() -> SemanticsIR {
+  SemanticsIR semantics;
+  static constexpr auto BuiltinIR = SemanticsCrossReferenceIRId(0);
+  auto block_id = semantics.AddNodeBlock();
+  semantics.cross_references_.resize_for_overwrite(
+      SemanticsBuiltinKind::ValidCount);
+
+  // TODO: The type-type should be replaced with a constraint. It will probably
+  // remain a builtin, just reimplemented as equivalent to `constraint type {}`.
+  auto builtin_type_type = semantics.AddNode(
+      block_id, SemanticsNode::MakeBuiltin(SemanticsBuiltinKind::TypeType(),
+                                           SemanticsNodeId(0)));
+  semantics.cross_references_[SemanticsBuiltinKind::TypeType().AsInt()] =
+      SemanticsCrossReference(BuiltinIR, block_id, builtin_type_type);
+  CARBON_CHECK(builtin_type_type.id == 0)
+      << "TypeType's type must be self-referential.";
+
+  auto builtin_int32 = semantics.AddNode(
+      block_id,
+      SemanticsNode::MakeBuiltin(SemanticsBuiltinKind::IntegerLiteralType(),
+                                 builtin_type_type));
+  semantics
+      .cross_references_[SemanticsBuiltinKind::IntegerLiteralType().AsInt()] =
+      SemanticsCrossReference(BuiltinIR, block_id, builtin_int32);
+
+  CARBON_CHECK(semantics.node_blocks_.size() == 1)
+      << "BuildBuiltins should only produce 1 block, actual: "
+      << semantics.node_blocks_.size();
+  return semantics;
+}
+
+auto SemanticsIR::MakeFromParseTree(const SemanticsIR& builtin_ir,
+                                    const TokenizedBuffer& tokens,
+                                    const ParseTree& parse_tree)
+    -> SemanticsIR {
+  SemanticsIR semantics(builtin_ir);
+  SemanticsParseTreeHandler(tokens, parse_tree, semantics).Build();
+  return semantics;
 }
 
 auto SemanticsIR::Print(llvm::raw_ostream& out) const -> void {
   constexpr int Indent = 2;
 
+  out << "cross_reference_irs.size == " << cross_reference_irs_.size() << ",\n";
+
+  out << "cross_references = {\n";
+  for (int32_t i = 0; i < static_cast<int32_t>(cross_references_.size()); ++i) {
+    out.indent(Indent);
+    out << SemanticsNodeId::MakeCrossReference(i) << " = \""
+        << cross_references_[i] << "\";\n";
+  }
+  out << "},\n";
+
   out << "identifiers = {\n";
   for (int32_t i = 0; i < static_cast<int32_t>(identifiers_.size()); ++i) {
     out.indent(Indent);

+ 66 - 4
toolchain/semantics/semantics_ir.h

@@ -16,12 +16,43 @@ class SemanticsIRForTest;
 
 namespace Carbon {
 
+// The ID of a cross-referenced IR (within cross_reference_irs_).
+struct SemanticsCrossReferenceIRId {
+  SemanticsCrossReferenceIRId() : id(-1) {}
+  constexpr explicit SemanticsCrossReferenceIRId(int32_t id) : id(id) {}
+
+  auto Print(llvm::raw_ostream& out) const -> void { out << "ir" << id; }
+
+  int32_t id;
+};
+
+// A cross-reference between node blocks or IRs; essentially, anything that's
+// not in the same SemanticsNodeBlock as the referencing node.
+struct SemanticsCrossReference {
+  SemanticsCrossReference() = default;
+  SemanticsCrossReference(SemanticsCrossReferenceIRId ir,
+                          SemanticsNodeBlockId node_block, SemanticsNodeId node)
+      : ir(ir), node_block(node_block), node(node) {}
+
+  auto Print(llvm::raw_ostream& out) const -> void {
+    out << "xref(" << ir << ", " << node_block << ", " << node << ")";
+  }
+
+  SemanticsCrossReferenceIRId ir;
+  SemanticsNodeBlockId node_block;
+  SemanticsNodeId node;
+};
+
 // Provides semantic analysis on a ParseTree.
 class SemanticsIR {
  public:
+  // Produces the builtins.
+  static auto MakeBuiltinIR() -> SemanticsIR;
+
   // Adds the IR for the provided ParseTree.
-  auto Build(const TokenizedBuffer& tokens, const ParseTree& parse_tree)
-      -> void;
+  static auto MakeFromParseTree(const SemanticsIR& builtin_ir,
+                                const TokenizedBuffer& tokens,
+                                const ParseTree& parse_tree) -> SemanticsIR;
 
   // Prints the full IR.
   auto Print(llvm::raw_ostream& out) const -> void;
@@ -29,12 +60,23 @@ class SemanticsIR {
  private:
   friend class SemanticsParseTreeHandler;
 
+  // For the builtin IR only.
+  SemanticsIR() : SemanticsIR(*this) {}
+  // For most IRs.
+  SemanticsIR(const SemanticsIR& builtins)
+      : cross_reference_irs_({&builtins, this}),
+        cross_references_(builtins.cross_references_) {}
+
+  // Adds an identifier, returning an ID to reference it.
+  // TODO: Deduplicate strings.
+  // TODO: Probably make generic for all strings, including literals.
   auto AddIdentifier(llvm::StringRef identifier) -> SemanticsIdentifierId {
     SemanticsIdentifierId id(identifiers_.size());
     identifiers_.push_back(identifier);
     return id;
   }
 
+  // Adds an integer literal, returning an ID to reference it.
   auto AddIntegerLiteral(llvm::APInt integer_literal)
       -> SemanticsIntegerLiteralId {
     SemanticsIntegerLiteralId id(integer_literals_.size());
@@ -42,22 +84,42 @@ class SemanticsIR {
     return id;
   }
 
-  // Starts a new node block.
+  // Adds an empty new node block, returning an ID to reference it and add
+  // items.
   auto AddNodeBlock() -> SemanticsNodeBlockId {
     SemanticsNodeBlockId id(node_blocks_.size());
     node_blocks_.resize(node_blocks_.size() + 1);
     return id;
   }
 
-  auto AddNode(SemanticsNodeBlockId block_id, SemanticsNode node) {
+  // Adds a node to a specified block, returning an ID to reference the node.
+  auto AddNode(SemanticsNodeBlockId block_id, SemanticsNode node)
+      -> SemanticsNodeId {
     auto& block = node_blocks_[block_id.id];
     SemanticsNodeId node_id(block.size());
     block.push_back(node);
     return node_id;
   }
 
+  // Related IRs. There will always be at least 2 entries, the builtin IR (used
+  // for references of builtins) followed by the current IR (used for references
+  // crossing node blocks).
+  llvm::SmallVector<const SemanticsIR*> cross_reference_irs_;
+
+  // Cross-references within the current IR across node blocks, and to other
+  // IRs. The first entries will always be builtins, at indices matching
+  // SemanticsBuiltinKind ordering.
+  // TODO: Deduplicate cross-references after they can be added outside
+  // builtins.
+  llvm::SmallVector<SemanticsCrossReference> cross_references_;
+
+  // Storage for identifiers.
   llvm::SmallVector<llvm::StringRef> identifiers_;
+
+  // Storage for integer literals.
   llvm::SmallVector<llvm::APInt> integer_literals_;
+
+  // Storage for blocks within the IR.
   llvm::SmallVector<llvm::SmallVector<SemanticsNode>> node_blocks_;
 };
 

+ 5 - 0
toolchain/semantics/semantics_node.cpp

@@ -4,6 +4,8 @@
 
 #include "toolchain/semantics/semantics_node.h"
 
+#include "toolchain/semantics/semantics_builtin_kind.h"
+
 namespace Carbon {
 
 static auto PrintArgs(llvm::raw_ostream& /*out*/,
@@ -29,6 +31,9 @@ void SemanticsNode::Print(llvm::raw_ostream& out) const {
 #include "toolchain/semantics/semantics_node_kind.def"
   }
   out << ")";
+  if (type_.id != -1) {
+    out << ": " << type_;
+  }
 }
 
 }  // namespace Carbon

+ 84 - 35
toolchain/semantics/semantics_node.h

@@ -9,18 +9,45 @@
 
 #include "common/check.h"
 #include "common/ostream.h"
+#include "toolchain/semantics/semantics_builtin_kind.h"
 #include "toolchain/semantics/semantics_node_kind.h"
 
 namespace Carbon {
 
 // Type-safe storage of Node IDs.
 struct SemanticsNodeId {
+  static constexpr int32_t CrossReferenceBit = 0x8000'0000;
+
+  // Constructs a cross-reference node ID.
+  static auto MakeCrossReference(int32_t id) -> SemanticsNodeId {
+    return SemanticsNodeId(id | CrossReferenceBit);
+  }
+  // Constructs a cross-reference node ID for a builtin. This relies on
+  // SemanticsIR guarantees for builtin cross-reference placement.
+  static auto MakeBuiltinReference(SemanticsBuiltinKind kind)
+      -> SemanticsNodeId {
+    return MakeCrossReference(kind.AsInt());
+  }
+
   SemanticsNodeId() : id(-1) {}
   explicit SemanticsNodeId(int32_t id) : id(id) {}
   SemanticsNodeId(SemanticsNodeId const&) = default;
   auto operator=(const SemanticsNodeId& other) -> SemanticsNodeId& = default;
 
-  void Print(llvm::raw_ostream& out) const { out << "node" << id; }
+  auto is_cross_reference() const -> bool { return id & CrossReferenceBit; }
+  // Returns the ID for a cross-reference, just handling removal of the marker
+  // bit.
+  auto GetAsCrossReference() const -> int32_t {
+    return id & ~CrossReferenceBit;
+  }
+
+  auto Print(llvm::raw_ostream& out) const -> void {
+    if (is_cross_reference()) {
+      out << "node_xref" << GetAsCrossReference();
+    } else {
+      out << "node" << id;
+    }
+  }
 
   int32_t id;
 };
@@ -30,7 +57,7 @@ struct SemanticsIdentifierId {
   SemanticsIdentifierId() : id(-1) {}
   explicit SemanticsIdentifierId(int32_t id) : id(id) {}
 
-  void Print(llvm::raw_ostream& out) const { out << "ident" << id; }
+  auto Print(llvm::raw_ostream& out) const -> void { out << "ident" << id; }
 
   int32_t id;
 };
@@ -40,7 +67,7 @@ struct SemanticsIntegerLiteralId {
   SemanticsIntegerLiteralId() : id(-1) {}
   explicit SemanticsIntegerLiteralId(int32_t id) : id(id) {}
 
-  void Print(llvm::raw_ostream& out) const { out << "int" << id; }
+  auto Print(llvm::raw_ostream& out) const -> void { out << "int" << id; }
 
   int32_t id;
 };
@@ -50,7 +77,7 @@ struct SemanticsNodeBlockId {
   SemanticsNodeBlockId() : id(-1) {}
   explicit SemanticsNodeBlockId(int32_t id) : id(id) {}
 
-  void Print(llvm::raw_ostream& out) const { out << "block" << id; }
+  auto Print(llvm::raw_ostream& out) const -> void { out << "block" << id; }
 
   int32_t id;
 };
@@ -64,8 +91,8 @@ class SemanticsNode {
 
   static auto MakeBinaryOperatorAdd(SemanticsNodeId lhs, SemanticsNodeId rhs)
       -> SemanticsNode {
-    return SemanticsNode(SemanticsNodeKind::BinaryOperatorAdd(), lhs.id,
-                         rhs.id);
+    return SemanticsNode(SemanticsNodeKind::BinaryOperatorAdd(),
+                         SemanticsNodeId(), lhs.id, rhs.id);
   }
   auto GetAsBinaryOperatorAdd() const
       -> std::pair<SemanticsNodeId, SemanticsNodeId> {
@@ -73,27 +100,51 @@ class SemanticsNode {
     return {SemanticsNodeId(arg0_), SemanticsNodeId(arg1_)};
   }
 
+  static auto MakeBindName(SemanticsIdentifierId name, SemanticsNodeId node)
+      -> SemanticsNode {
+    return SemanticsNode(SemanticsNodeKind::BindName(), SemanticsNodeId(),
+                         name.id, node.id);
+  }
+  auto GetAsBindName() const
+      -> std::pair<SemanticsIdentifierId, SemanticsNodeId> {
+    CARBON_CHECK(kind_ == SemanticsNodeKind::BindName());
+    return {SemanticsIdentifierId(arg0_), SemanticsNodeId(arg1_)};
+  }
+
+  static auto MakeBuiltin(SemanticsBuiltinKind builtin_kind,
+                          SemanticsNodeId type) -> SemanticsNode {
+    return SemanticsNode(SemanticsNodeKind::Builtin(), type,
+                         builtin_kind.AsInt());
+  }
+  auto GetAsBuiltin() const -> SemanticsBuiltinKind {
+    CARBON_CHECK(kind_ == SemanticsNodeKind::Builtin());
+    return SemanticsBuiltinKind::FromInt(arg0_);
+  }
+
   static auto MakeCodeBlock(SemanticsNodeBlockId node_block) -> SemanticsNode {
-    return SemanticsNode(SemanticsNodeKind::CodeBlock(), node_block.id);
+    return SemanticsNode(SemanticsNodeKind::CodeBlock(), SemanticsNodeId(),
+                         node_block.id);
   }
   auto GetAsCodeBlock() const -> SemanticsNodeBlockId {
     CARBON_CHECK(kind_ == SemanticsNodeKind::CodeBlock());
     return SemanticsNodeBlockId(arg0_);
   }
 
-  static auto MakeFunctionDeclaration(SemanticsNodeId name) -> SemanticsNode {
-    return SemanticsNode(SemanticsNodeKind::FunctionDeclaration(), name.id);
+  // TODO: The signature should be added as a parameter.
+  static auto MakeFunctionDeclaration() -> SemanticsNode {
+    return SemanticsNode(SemanticsNodeKind::FunctionDeclaration(),
+                         SemanticsNodeId());
   }
-  auto GetAsFunctionDeclaration() const -> SemanticsNodeId {
+  auto GetAsFunctionDeclaration() const -> NoArgs {
     CARBON_CHECK(kind_ == SemanticsNodeKind::FunctionDeclaration());
-    return SemanticsNodeId(arg0_);
+    return {};
   }
 
   static auto MakeFunctionDefinition(SemanticsNodeId decl,
                                      SemanticsNodeBlockId node_block)
       -> SemanticsNode {
-    return SemanticsNode(SemanticsNodeKind::FunctionDefinition(), decl.id,
-                         node_block.id);
+    return SemanticsNode(SemanticsNodeKind::FunctionDefinition(),
+                         SemanticsNodeId(), decl.id, node_block.id);
   }
   auto GetAsFunctionDefinition() const
       -> std::pair<SemanticsNodeId, SemanticsNodeBlockId> {
@@ -101,18 +152,12 @@ class SemanticsNode {
     return {SemanticsNodeId(arg0_), SemanticsNodeBlockId(arg1_)};
   }
 
-  static auto MakeIdentifier(SemanticsIdentifierId identifier)
-      -> SemanticsNode {
-    return SemanticsNode(SemanticsNodeKind::Identifier(), identifier.id);
-  }
-  auto GetAsIdentifier() const -> SemanticsIdentifierId {
-    CARBON_CHECK(kind_ == SemanticsNodeKind::Identifier());
-    return SemanticsIdentifierId(arg0_);
-  }
-
   static auto MakeIntegerLiteral(SemanticsIntegerLiteralId integer)
       -> SemanticsNode {
-    return SemanticsNode(SemanticsNodeKind::IntegerLiteral(), integer.id);
+    return SemanticsNode(SemanticsNodeKind::IntegerLiteral(),
+                         SemanticsNodeId::MakeBuiltinReference(
+                             SemanticsBuiltinKind::IntegerLiteralType()),
+                         integer.id);
   }
   auto GetAsIntegerLiteral() const -> SemanticsIntegerLiteralId {
     CARBON_CHECK(kind_ == SemanticsNodeKind::IntegerLiteral());
@@ -120,7 +165,7 @@ class SemanticsNode {
   }
 
   static auto MakeReturn() -> SemanticsNode {
-    return SemanticsNode(SemanticsNodeKind::Return());
+    return SemanticsNode(SemanticsNodeKind::Return(), SemanticsNodeId());
   }
   auto GetAsReturn() const -> NoArgs {
     CARBON_CHECK(kind_ == SemanticsNodeKind::Return());
@@ -128,34 +173,38 @@ class SemanticsNode {
   }
 
   static auto MakeReturnExpression(SemanticsNodeId expr) -> SemanticsNode {
-    return SemanticsNode(SemanticsNodeKind::ReturnExpression(), expr.id);
+    return SemanticsNode(SemanticsNodeKind::ReturnExpression(),
+                         SemanticsNodeId(), expr.id);
   }
   auto GetAsReturnExpression() const -> SemanticsNodeId {
     CARBON_CHECK(kind_ == SemanticsNodeKind::ReturnExpression());
     return SemanticsNodeId(arg0_);
   }
 
-  SemanticsNode() : SemanticsNode(SemanticsNodeKind::Invalid()) {}
+  SemanticsNode()
+      : SemanticsNode(SemanticsNodeKind::Invalid(), SemanticsNodeId()) {}
 
   auto kind() -> SemanticsNodeKind { return kind_; }
+  auto type() -> SemanticsNodeId { return type_; }
 
-  void Print(llvm::raw_ostream& out) const;
+  auto Print(llvm::raw_ostream& out) const -> void;
 
  private:
-  explicit SemanticsNode(SemanticsNodeKind kind, int32_t arg0 = -1,
-                         int32_t arg1 = -1)
-      : kind_(kind), arg0_(arg0), arg1_(arg1) {}
+  explicit SemanticsNode(SemanticsNodeKind kind, SemanticsNodeId type,
+                         int32_t arg0 = -1, int32_t arg1 = -1)
+      : kind_(kind), type_(type), arg0_(arg0), arg1_(arg1) {}
 
   SemanticsNodeKind kind_;
+  SemanticsNodeId type_;
   int32_t arg0_;
   int32_t arg1_;
 };
 
-// TODO: This is currently 12 bytes because we sometimes have 2 arguments for a
-// pair of SemanticsNodes. If SemanticsNode was tracked in 3.5 bytes, we could
-// potentially change SemanticsNode to 8 bytes. This may be worth investigating
-// further.
-static_assert(sizeof(SemanticsNode) == 12, "Unexpected SemanticsNode size");
+// TODO: This is currently 16 bytes because we sometimes have 2 arguments for a
+// pair of SemanticsNodes. However, SemanticsNodeKind is 1 byte; if args
+// were 3.5 bytes, we could potentially shrink SemanticsNode by 4 bytes. This
+// may be worth investigating further.
+static_assert(sizeof(SemanticsNode) == 16, "Unexpected SemanticsNode size");
 
 }  // namespace Carbon
 

+ 2 - 1
toolchain/semantics/semantics_node_kind.def

@@ -16,10 +16,11 @@
 CARBON_SEMANTICS_NODE_KIND(Invalid)
 
 CARBON_SEMANTICS_NODE_KIND(BinaryOperatorAdd)
+CARBON_SEMANTICS_NODE_KIND(BindName)
+CARBON_SEMANTICS_NODE_KIND(Builtin)
 CARBON_SEMANTICS_NODE_KIND(CodeBlock)
 CARBON_SEMANTICS_NODE_KIND(FunctionDeclaration)
 CARBON_SEMANTICS_NODE_KIND(FunctionDefinition)
-CARBON_SEMANTICS_NODE_KIND(Identifier)
 CARBON_SEMANTICS_NODE_KIND(IntegerLiteral)
 CARBON_SEMANTICS_NODE_KIND(Return)
 CARBON_SEMANTICS_NODE_KIND(ReturnExpression)

+ 12 - 11
toolchain/semantics/semantics_parse_tree_handler.cpp

@@ -19,10 +19,6 @@ auto SemanticsParseTreeHandler::Build() -> void {
   for (auto it = range.begin();; ++it) {
     auto parse_node = *it;
     switch (auto parse_kind = parse_tree_->node_kind(parse_node)) {
-      case ParseNodeKind::DeclaredName(): {
-        HandleDeclaredName(parse_node);
-        break;
-      }
       case ParseNodeKind::FunctionDefinition(): {
         HandleFunctionDefinition(parse_node);
         break;
@@ -55,6 +51,7 @@ auto SemanticsParseTreeHandler::Build() -> void {
         HandleReturnStatement(parse_node);
         break;
       }
+      case ParseNodeKind::DeclaredName():
       case ParseNodeKind::FunctionIntroducer():
       case ParseNodeKind::ParameterListEnd():
       case ParseNodeKind::StatementEnd(): {
@@ -112,11 +109,13 @@ auto SemanticsParseTreeHandler::PopWithResult(ParseNodeKind pop_parse_kind)
   return node_id;
 }
 
-auto SemanticsParseTreeHandler::HandleDeclaredName(ParseTree::Node parse_node)
-    -> void {
-  auto text = parse_tree_->GetNodeText(parse_node);
-  auto identifier_id = semantics_->AddIdentifier(text);
-  Push(parse_node, SemanticsNode::MakeIdentifier(identifier_id));
+auto SemanticsParseTreeHandler::AddIdentifier(ParseTree::Node decl_node)
+    -> SemanticsIdentifierId {
+  CARBON_CHECK(parse_tree_->node_kind(decl_node) ==
+               ParseNodeKind::DeclaredName())
+      << parse_tree_->node_kind(decl_node);
+  auto text = parse_tree_->GetNodeText(decl_node);
+  return semantics_->AddIdentifier(text);
 }
 
 auto SemanticsParseTreeHandler::HandleFunctionDefinition(
@@ -134,10 +133,12 @@ auto SemanticsParseTreeHandler::HandleFunctionDefinition(
 auto SemanticsParseTreeHandler::HandleFunctionDefinitionStart(
     ParseTree::Node parse_node) -> void {
   Pop(ParseNodeKind::ParameterList());
-  auto name_node_id = PopWithResult(ParseNodeKind::DeclaredName());
+  auto name = AddIdentifier(node_stack_.back().parse_node);
+  node_stack_.pop_back();
   Pop(ParseNodeKind::FunctionIntroducer());
 
-  auto decl_id = AddNode(SemanticsNode::MakeFunctionDeclaration(name_node_id));
+  auto decl_id = AddNode(SemanticsNode::MakeFunctionDeclaration());
+  AddNode(SemanticsNode::MakeBindName(name, decl_id));
   auto block_id = semantics_->AddNodeBlock();
   AddNode(SemanticsNode::MakeFunctionDefinition(decl_id, block_id));
   node_block_stack_.push_back(block_id);

+ 3 - 1
toolchain/semantics/semantics_parse_tree_handler.h

@@ -5,7 +5,6 @@
 #ifndef CARBON_TOOLCHAIN_SEMANTICS_SEMANTICS_PARSE_TREE_HANDLER_H_
 #define CARBON_TOOLCHAIN_SEMANTICS_SEMANTICS_PARSE_TREE_HANDLER_H_
 
-#include "common/check.h"
 #include "toolchain/parser/parse_tree.h"
 #include "toolchain/semantics/semantics_ir.h"
 #include "toolchain/semantics/semantics_node.h"
@@ -30,6 +29,9 @@ class SemanticsParseTreeHandler {
     llvm::Optional<SemanticsNodeId> result_id;
   };
 
+  // Adds an identifier for a DeclaredName node, returning its reference.
+  auto AddIdentifier(ParseTree::Node decl_node) -> SemanticsIdentifierId;
+
   // Adds a node to the current block, returning the produced ID.
   auto AddNode(SemanticsNode node) -> SemanticsNodeId;
 

+ 5 - 0
toolchain/semantics/testdata/empty.carbon → toolchain/semantics/testdata/basics/empty.carbon

@@ -4,6 +4,11 @@
 //
 // AUTOUPDATE
 // RUN: %{carbon-run-semantics}
+// CHECK:STDOUT: cross_reference_irs.size == 2,
+// CHECK:STDOUT: cross_references = {
+// CHECK:STDOUT:   node_xref0 = "xref(ir0, block0, node0)";
+// CHECK:STDOUT:   node_xref1 = "xref(ir0, block0, node1)";
+// CHECK:STDOUT: },
 // CHECK:STDOUT: identifiers = {
 // CHECK:STDOUT: },
 // CHECK:STDOUT: integer_literals = {

+ 8 - 3
toolchain/semantics/testdata/function/basic.carbon

@@ -4,6 +4,11 @@
 //
 // AUTOUPDATE
 // RUN: %{carbon-run-semantics}
+// CHECK:STDOUT: cross_reference_irs.size == 2,
+// CHECK:STDOUT: cross_references = {
+// CHECK:STDOUT:   node_xref0 = "xref(ir0, block0, node0)";
+// CHECK:STDOUT:   node_xref1 = "xref(ir0, block0, node1)";
+// CHECK:STDOUT: },
 // CHECK:STDOUT: identifiers = {
 // CHECK:STDOUT:   ident0 = "Foo";
 // CHECK:STDOUT: },
@@ -11,9 +16,9 @@
 // CHECK:STDOUT: },
 // CHECK:STDOUT: node_blocks = {
 // CHECK:STDOUT:   block0 = {
-// CHECK:STDOUT:     node0 = Identifier(ident0);
-// CHECK:STDOUT:     node1 = FunctionDeclaration(node0);
-// CHECK:STDOUT:     node2 = FunctionDefinition(node1, block1);
+// CHECK:STDOUT:     node0 = FunctionDeclaration();
+// CHECK:STDOUT:     node1 = BindName(ident0, node0);
+// CHECK:STDOUT:     node2 = FunctionDefinition(node0, block1);
 // CHECK:STDOUT:   },
 // CHECK:STDOUT:   block1 = {
 // CHECK:STDOUT:   },

+ 14 - 9
toolchain/semantics/testdata/function/order.carbon

@@ -4,6 +4,11 @@
 //
 // AUTOUPDATE
 // RUN: %{carbon-run-semantics}
+// CHECK:STDOUT: cross_reference_irs.size == 2,
+// CHECK:STDOUT: cross_references = {
+// CHECK:STDOUT:   node_xref0 = "xref(ir0, block0, node0)";
+// CHECK:STDOUT:   node_xref1 = "xref(ir0, block0, node1)";
+// CHECK:STDOUT: },
 // CHECK:STDOUT: identifiers = {
 // CHECK:STDOUT:   ident0 = "Foo";
 // CHECK:STDOUT:   ident1 = "Bar";
@@ -13,15 +18,15 @@
 // CHECK:STDOUT: },
 // CHECK:STDOUT: node_blocks = {
 // CHECK:STDOUT:   block0 = {
-// CHECK:STDOUT:     node0 = Identifier(ident0);
-// CHECK:STDOUT:     node1 = FunctionDeclaration(node0);
-// CHECK:STDOUT:     node2 = FunctionDefinition(node1, block1);
-// CHECK:STDOUT:     node3 = Identifier(ident1);
-// CHECK:STDOUT:     node4 = FunctionDeclaration(node3);
-// CHECK:STDOUT:     node5 = FunctionDefinition(node4, block2);
-// CHECK:STDOUT:     node6 = Identifier(ident2);
-// CHECK:STDOUT:     node7 = FunctionDeclaration(node6);
-// CHECK:STDOUT:     node8 = FunctionDefinition(node7, block3);
+// CHECK:STDOUT:     node0 = FunctionDeclaration();
+// CHECK:STDOUT:     node1 = BindName(ident0, node0);
+// CHECK:STDOUT:     node2 = FunctionDefinition(node0, block1);
+// CHECK:STDOUT:     node3 = FunctionDeclaration();
+// CHECK:STDOUT:     node4 = BindName(ident1, node3);
+// CHECK:STDOUT:     node5 = FunctionDefinition(node3, block2);
+// CHECK:STDOUT:     node6 = FunctionDeclaration();
+// CHECK:STDOUT:     node7 = BindName(ident2, node6);
+// CHECK:STDOUT:     node8 = FunctionDefinition(node6, block3);
 // CHECK:STDOUT:   },
 // CHECK:STDOUT:   block1 = {
 // CHECK:STDOUT:   },

+ 10 - 5
toolchain/semantics/testdata/return/binary_op.carbon

@@ -4,6 +4,11 @@
 //
 // AUTOUPDATE
 // RUN: %{carbon-run-semantics}
+// CHECK:STDOUT: cross_reference_irs.size == 2,
+// CHECK:STDOUT: cross_references = {
+// CHECK:STDOUT:   node_xref0 = "xref(ir0, block0, node0)";
+// CHECK:STDOUT:   node_xref1 = "xref(ir0, block0, node1)";
+// CHECK:STDOUT: },
 // CHECK:STDOUT: identifiers = {
 // CHECK:STDOUT:   ident0 = "Main";
 // CHECK:STDOUT: },
@@ -13,13 +18,13 @@
 // CHECK:STDOUT: },
 // CHECK:STDOUT: node_blocks = {
 // CHECK:STDOUT:   block0 = {
-// CHECK:STDOUT:     node0 = Identifier(ident0);
-// CHECK:STDOUT:     node1 = FunctionDeclaration(node0);
-// CHECK:STDOUT:     node2 = FunctionDefinition(node1, block1);
+// CHECK:STDOUT:     node0 = FunctionDeclaration();
+// CHECK:STDOUT:     node1 = BindName(ident0, node0);
+// CHECK:STDOUT:     node2 = FunctionDefinition(node0, block1);
 // CHECK:STDOUT:   },
 // CHECK:STDOUT:   block1 = {
-// CHECK:STDOUT:     node0 = IntegerLiteral(int0);
-// CHECK:STDOUT:     node1 = IntegerLiteral(int1);
+// CHECK:STDOUT:     node0 = IntegerLiteral(int0): node_xref1;
+// CHECK:STDOUT:     node1 = IntegerLiteral(int1): node_xref1;
 // CHECK:STDOUT:     node2 = BinaryOperatorAdd(node0, node1);
 // CHECK:STDOUT:     node3 = ReturnExpression(node2);
 // CHECK:STDOUT:   },

+ 9 - 4
toolchain/semantics/testdata/return/literal.carbon

@@ -4,6 +4,11 @@
 //
 // AUTOUPDATE
 // RUN: %{carbon-run-semantics}
+// CHECK:STDOUT: cross_reference_irs.size == 2,
+// CHECK:STDOUT: cross_references = {
+// CHECK:STDOUT:   node_xref0 = "xref(ir0, block0, node0)";
+// CHECK:STDOUT:   node_xref1 = "xref(ir0, block0, node1)";
+// CHECK:STDOUT: },
 // CHECK:STDOUT: identifiers = {
 // CHECK:STDOUT:   ident0 = "Main";
 // CHECK:STDOUT: },
@@ -12,12 +17,12 @@
 // CHECK:STDOUT: },
 // CHECK:STDOUT: node_blocks = {
 // CHECK:STDOUT:   block0 = {
-// CHECK:STDOUT:     node0 = Identifier(ident0);
-// CHECK:STDOUT:     node1 = FunctionDeclaration(node0);
-// CHECK:STDOUT:     node2 = FunctionDefinition(node1, block1);
+// CHECK:STDOUT:     node0 = FunctionDeclaration();
+// CHECK:STDOUT:     node1 = BindName(ident0, node0);
+// CHECK:STDOUT:     node2 = FunctionDefinition(node0, block1);
 // CHECK:STDOUT:   },
 // CHECK:STDOUT:   block1 = {
-// CHECK:STDOUT:     node0 = IntegerLiteral(int0);
+// CHECK:STDOUT:     node0 = IntegerLiteral(int0): node_xref1;
 // CHECK:STDOUT:     node1 = ReturnExpression(node0);
 // CHECK:STDOUT:   },
 // CHECK:STDOUT: }

+ 8 - 3
toolchain/semantics/testdata/return/trivial.carbon

@@ -4,6 +4,11 @@
 //
 // AUTOUPDATE
 // RUN: %{carbon-run-semantics}
+// CHECK:STDOUT: cross_reference_irs.size == 2,
+// CHECK:STDOUT: cross_references = {
+// CHECK:STDOUT:   node_xref0 = "xref(ir0, block0, node0)";
+// CHECK:STDOUT:   node_xref1 = "xref(ir0, block0, node1)";
+// CHECK:STDOUT: },
 // CHECK:STDOUT: identifiers = {
 // CHECK:STDOUT:   ident0 = "Main";
 // CHECK:STDOUT: },
@@ -11,9 +16,9 @@
 // CHECK:STDOUT: },
 // CHECK:STDOUT: node_blocks = {
 // CHECK:STDOUT:   block0 = {
-// CHECK:STDOUT:     node0 = Identifier(ident0);
-// CHECK:STDOUT:     node1 = FunctionDeclaration(node0);
-// CHECK:STDOUT:     node2 = FunctionDefinition(node1, block1);
+// CHECK:STDOUT:     node0 = FunctionDeclaration();
+// CHECK:STDOUT:     node1 = BindName(ident0, node0);
+// CHECK:STDOUT:     node2 = FunctionDefinition(node0, block1);
 // CHECK:STDOUT:   },
 // CHECK:STDOUT:   block1 = {
 // CHECK:STDOUT:     node0 = Return();