mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-04 22:02:52 +01:00
Add tracking of function parameters (#2552)
For parameters (and in the future, arguments too; generally comma-separated lists) track two node blocks: 1. param_ir: The complete IR. 2. param_refs: Nodes within the IR that are the "root" parameter. param_refs should allow quick counting of the # of parameters, and more efficient comparison of call args with function parameters. param_ir should be necessary to generate the actual signature. In order to construct this, this refactors the node_block_stack into its own class, which is reused in params_stack. These carry references to the underlying SmallVector for lazy modification in order to avoid a dependency cycle with SemanticsIR (also see notes on empty node blocks below). When finalized, the block pair is pushed onto finished_params_stack. That's because node_stack only has space for one thing, and this is two things -- so I'm essentially choosing a trade-off of adding another stack in order to avoid consuming more space in the expectation that most parse nodes have 0 or 1 things to return, and 2 will be very rare. As factored, this currently consolidates most empty node blocks into a single canonical empty node block. This is because I think empty blocks, i.e. `()`, will be very common. In order to achieve this, SemanticsNodeBlockStack does lazy creation. An alternative approach would have been to use 1 node block per parameter. We decided against this in order to reduce the number of vectors being created.
This commit is contained in:
@@ -5,6 +5,8 @@
|
||||
#ifndef CARBON_TOOLCHAIN_SEMANTICS_SEMANTICS_NODE_STACK_H_
|
||||
#define CARBON_TOOLCHAIN_SEMANTICS_SEMANTICS_NODE_STACK_H_
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "toolchain/parser/parse_node_kind.h"
|
||||
#include "toolchain/parser/parse_tree.h"
|
||||
@@ -36,21 +38,19 @@ class SemanticsNodeStack {
|
||||
auto Push(ParseTree::Node parse_node) -> void {
|
||||
PushEntry(
|
||||
{.parse_node = parse_node, .node_id = SemanticsNodeId::MakeInvalid()},
|
||||
/*is_node_id=*/true);
|
||||
DebugLog::None);
|
||||
}
|
||||
|
||||
// Pushes a parse tree node onto the stack.
|
||||
auto Push(ParseTree::Node parse_node, SemanticsNodeId node_id) -> void {
|
||||
PushEntry({.parse_node = parse_node, .node_id = node_id},
|
||||
/*is_node_id=*/true);
|
||||
PushEntry({.parse_node = parse_node, .node_id = node_id}, DebugLog::NodeId);
|
||||
}
|
||||
|
||||
// Pushes a PatternBinding parse tree node onto the stack with its name.
|
||||
auto Push(ParseTree::Node parse_node, SemanticsStringId name_id) -> void {
|
||||
CARBON_CHECK(parse_tree_->node_kind(parse_node) ==
|
||||
ParseNodeKind::PatternBinding);
|
||||
PushEntry({.parse_node = parse_node, .name_id = name_id},
|
||||
/*is_node_id=*/false);
|
||||
PushEntry({.parse_node = parse_node, .name_id = name_id}, DebugLog::NameId);
|
||||
}
|
||||
|
||||
// Pops the top of the stack without any verification.
|
||||
@@ -59,6 +59,12 @@ class SemanticsNodeStack {
|
||||
// Pops the top of the stack.
|
||||
auto PopAndDiscardSoloParseNode(ParseNodeKind pop_parse_kind) -> void;
|
||||
|
||||
// Pops the top of the stack, and discards the ID.
|
||||
auto PopAndDiscardId() -> void;
|
||||
|
||||
// Pops the top of the stack, and discards the ID.
|
||||
auto PopAndDiscardId(ParseNodeKind pop_parse_kind) -> void;
|
||||
|
||||
// Pops the top of the stack and returns the parse_node.
|
||||
auto PopForSoloParseNode() -> ParseTree::Node;
|
||||
|
||||
@@ -95,21 +101,34 @@ class SemanticsNodeStack {
|
||||
auto PrintForStackDump(llvm::raw_ostream& output) const -> void;
|
||||
|
||||
private:
|
||||
// An entry in node_stack_.
|
||||
// An entry in stack_.
|
||||
struct Entry {
|
||||
// The node associated with the stack entry.
|
||||
ParseTree::Node parse_node;
|
||||
|
||||
// The entries will evaluate as invalid if and only if they're a solo
|
||||
// parse_node. Invalid is used instead of optional to save space.
|
||||
//
|
||||
// A discriminator isn't needed because the caller can determine which field
|
||||
// is used based on the ParseNodeKind.
|
||||
union {
|
||||
// The node_id will be invalid if and only if it's a solo parse_node.
|
||||
SemanticsNodeId node_id;
|
||||
// The name_id is provided for PatternBindings.
|
||||
|
||||
// Right now name_id is exclusively for PatternBinding, which is enforced.
|
||||
SemanticsStringId name_id;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(Entry) == 8, "Unexpected Entry size");
|
||||
|
||||
// Pushes an entry onto the stack. is_node_id is provided for debug output
|
||||
// only.
|
||||
auto PushEntry(Entry entry, bool is_node_id) -> void;
|
||||
// Which Entry union member to log.
|
||||
enum DebugLog {
|
||||
None,
|
||||
NodeId,
|
||||
NameId,
|
||||
};
|
||||
|
||||
// Pushes an entry onto the stack.
|
||||
auto PushEntry(Entry entry, DebugLog debug_log) -> void;
|
||||
|
||||
// Pops an entry.
|
||||
auto PopEntry() -> Entry;
|
||||
@@ -121,12 +140,10 @@ class SemanticsNodeStack {
|
||||
auto RequireParseKind(Entry entry, ParseNodeKind require_kind) -> void;
|
||||
|
||||
// Requires an entry to have a invalid node_id.
|
||||
// Also works with name_id in the union due to type compatibility.
|
||||
auto RequireSoloParseNode(Entry entry) -> void;
|
||||
|
||||
// Requires an entry to have a valid node_id.
|
||||
// Also works with name_id in the union due to type compatibility.
|
||||
auto RequireNodeId(Entry entry) -> void;
|
||||
// Requires an entry to have a valid id.
|
||||
auto RequireValidId(Entry entry) -> void;
|
||||
|
||||
// The file's parse tree.
|
||||
const ParseTree* parse_tree_;
|
||||
|
||||
Reference in New Issue
Block a user