perf(ast): place NodeId field after Span in structs (#20584)

Alter the code in `ast_tools` which re-orders struct fields, to make `NodeId` always occupy a consistent "slot" in every AST node - at byte position 8, just after `Span`.

This will be a sizeable gain once we start utilizing the `NodeId` fields more. Because, for example, every variant of `Expression` has its `NodeId` stored in same location as all the other variants, `Expression::node_id()` is just 1 operation - a pointer read - rather than a nest of branches, or a lookup table.

Also alter the algorithm for ordering struct fields to fill in the 4-byte gap after `NodeId` field with other field(s), to avoid excess padding.

The new algorithm also prioritizes keeping fields in definition order as much as possible, rather than sorting them strictly in order of size and alignment. This is mildly advantageous because field definition order is the order the AST is walked in, so it avoids bouncing between cache lines while iterating through the fields of a struct when visiting the AST.

No types change size in the process. Fields remain packed to keep type sizes the minimum they can be - they just change order.
This commit is contained in:
overlookmotel committed 2026-03-20 22:15:56 +00:00
1 parent e98466632e
commit c6ea0a068d
18 files changed
+5644 -5550

No files matched your search

+4 -4
View File
@@ -54,22 +54,22 @@ export const PROGRAM_OFFSET = 0;
/**
* Byte offset of pointer to start of source text, relative to start of `Program`.
*/
export const SOURCE_START_OFFSET = 8;
export const SOURCE_START_OFFSET = 16;
/**
* Byte offset of length of source text, relative to start of `Program`.
*/
export const SOURCE_LEN_OFFSET = 16;
export const SOURCE_LEN_OFFSET = 24;
/**
* Byte offset of comments `Vec` pointer, relative to start of `Program`.
*/
export const COMMENTS_OFFSET = 24;
export const COMMENTS_OFFSET = 32;
/**
* Byte offset of comments `Vec` length, relative to start of `Program`.
*/
export const COMMENTS_LEN_OFFSET = 32;
export const COMMENTS_LEN_OFFSET = 40;
/**
* Size of `Comment` struct in bytes.
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+3 -5
View File
@@ -429,6 +429,7 @@ impl AstKind<'_> {
}
/// Get [`NodeId`] of an [`AstKind`].
// `node_id` field is in consistent position in all AST structs, so this boils down to 1 instruction.
#[inline]
pub fn node_id(&self) -> NodeId {
match self {
@@ -624,11 +625,8 @@ impl AstKind<'_> {
}
/// Set [`NodeId`] of an [`AstKind`].
#[expect(
clippy::inline_always,
reason = "enables compile-time match elimination in semantic builder"
)]
#[inline(always)]
// `node_id` field is in consistent position in all AST structs, so this boils down to 1 instruction.
#[inline]
pub fn set_node_id(&self, node_id: NodeId) {
match self {
Self::Program(it) => it.set_node_id(node_id),
+163 -163
View File
@@ -57,264 +57,264 @@ pub static STRUCTS: phf::Map<&'static str, StructDetails> = ::phf::Map {
(0, 4),
],
entries: &[
("JSXElement", StructDetails { field_order: Some(&[4, 0, 1, 2, 3]) }),
("JSDocNullableType", StructDetails { field_order: Some(&[2, 0, 1, 3]) }),
("WithClause", StructDetails { field_order: Some(&[2, 0, 3, 1]) }),
("NewExpression", StructDetails { field_order: Some(&[4, 0, 1, 2, 3, 5]) }),
("NumericLiteral", StructDetails { field_order: Some(&[3, 0, 1, 2, 4]) }),
("JSXElement", StructDetails { field_order: Some(&[1, 0, 2, 3, 4]) }),
("JSDocNullableType", StructDetails { field_order: Some(&[1, 0, 3, 2]) }),
("WithClause", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("NewExpression", StructDetails { field_order: Some(&[1, 0, 3, 4, 5, 2]) }),
("NumericLiteral", StructDetails { field_order: Some(&[1, 0, 4, 3, 2]) }),
("TSUnknownKeyword", StructDetails { field_order: Some(&[1, 0]) }),
("UnicodePropertyEscape", StructDetails { field_order: Some(&[0, 3, 4, 1, 2]) }),
("TSInterfaceDeclaration", StructDetails { field_order: Some(&[5, 0, 1, 2, 3, 4, 7, 6]) }),
("ObjectAssignmentTarget", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("SwitchCase", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("TSPropertySignature", StructDetails { field_order: Some(&[3, 0, 4, 5, 6, 1, 2]) }),
("CatchClause", StructDetails { field_order: Some(&[3, 0, 1, 2, 4]) }),
("ForInStatement", StructDetails { field_order: Some(&[4, 0, 1, 2, 3, 5]) }),
("AssignmentExpression", StructDetails { field_order: Some(&[3, 0, 4, 1, 2]) }),
("BlockStatement", StructDetails { field_order: Some(&[2, 0, 1, 3]) }),
("StringLiteral", StructDetails { field_order: Some(&[3, 0, 1, 2, 4]) }),
("TSInterfaceDeclaration", StructDetails { field_order: Some(&[1, 0, 3, 4, 5, 6, 7, 2]) }),
("ObjectAssignmentTarget", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("SwitchCase", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("TSPropertySignature", StructDetails { field_order: Some(&[1, 0, 2, 3, 4, 5, 6]) }),
("CatchClause", StructDetails { field_order: Some(&[1, 0, 3, 4, 2]) }),
("ForInStatement", StructDetails { field_order: Some(&[1, 0, 3, 4, 5, 2]) }),
("AssignmentExpression", StructDetails { field_order: Some(&[1, 0, 2, 3, 4]) }),
("BlockStatement", StructDetails { field_order: Some(&[1, 0, 3, 2]) }),
("StringLiteral", StructDetails { field_order: Some(&[1, 0, 3, 4, 2]) }),
("TSNumberKeyword", StructDetails { field_order: Some(&[1, 0]) }),
("ImportNamespaceSpecifier", StructDetails { field_order: Some(&[2, 0, 1]) }),
("TSTypeParameterInstantiation", StructDetails { field_order: Some(&[2, 0, 1]) }),
("ImportNamespaceSpecifier", StructDetails { field_order: Some(&[1, 0, 2]) }),
("TSTypeParameterInstantiation", StructDetails { field_order: Some(&[1, 0, 2]) }),
("ScopeId", StructDetails { field_order: None }),
("ImportDeclaration", StructDetails { field_order: Some(&[4, 0, 1, 2, 5, 3, 6]) }),
("ImportDeclaration", StructDetails { field_order: Some(&[1, 0, 4, 5, 2, 6, 3]) }),
("SymbolId", StructDetails { field_order: None }),
("PrivateIdentifier", StructDetails { field_order: Some(&[2, 0, 1]) }),
("TSModuleBlock", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("TSEnumDeclaration", StructDetails { field_order: Some(&[3, 0, 1, 2, 4, 5]) }),
("JSXNamespacedName", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("PrivateIdentifier", StructDetails { field_order: Some(&[1, 0, 2]) }),
("TSModuleBlock", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("TSEnumDeclaration", StructDetails { field_order: Some(&[1, 0, 4, 5, 2, 3]) }),
("JSXNamespacedName", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("TSIntrinsicKeyword", StructDetails { field_order: Some(&[1, 0]) }),
("BindingRestElement", StructDetails { field_order: Some(&[2, 0, 1]) }),
("TSTypeQuery", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("SwitchStatement", StructDetails { field_order: Some(&[3, 0, 1, 2, 4]) }),
("BindingRestElement", StructDetails { field_order: Some(&[1, 0, 2]) }),
("TSTypeQuery", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("SwitchStatement", StructDetails { field_order: Some(&[1, 0, 3, 4, 2]) }),
("TSAnyKeyword", StructDetails { field_order: Some(&[1, 0]) }),
("TSAsExpression", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("ReturnStatement", StructDetails { field_order: Some(&[2, 0, 1]) }),
("ArrayAssignmentTarget", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("TSAsExpression", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("ReturnStatement", StructDetails { field_order: Some(&[1, 0, 2]) }),
("ArrayAssignmentTarget", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("ThisExpression", StructDetails { field_order: Some(&[1, 0]) }),
("IgnoreGroup", StructDetails { field_order: None }),
("TSInterfaceBody", StructDetails { field_order: Some(&[2, 0, 1]) }),
("CatchParameter", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("TSInterfaceBody", StructDetails { field_order: Some(&[1, 0, 2]) }),
("CatchParameter", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("RawTransferData", StructDetails { field_order: None }),
("RawTransferMetadata", StructDetails { field_order: Some(&[0, 3, 4, 5, 1, 2]) }),
("ObjectProperty", StructDetails { field_order: Some(&[3, 0, 4, 1, 2, 5, 6, 7]) }),
("ObjectProperty", StructDetails { field_order: Some(&[1, 0, 2, 6, 7, 3, 4, 5]) }),
("DebuggerStatement", StructDetails { field_order: Some(&[1, 0]) }),
("TSModuleDeclaration", StructDetails { field_order: Some(&[3, 0, 1, 2, 5, 6, 4]) }),
("TSModuleDeclaration", StructDetails { field_order: Some(&[1, 0, 3, 4, 5, 6, 2]) }),
(
"MethodDefinition",
StructDetails { field_order: Some(&[4, 0, 5, 1, 2, 3, 6, 7, 8, 9, 10, 11]) },
StructDetails { field_order: Some(&[1, 0, 2, 6, 7, 8, 3, 4, 5, 9, 10, 11]) },
),
("ExportEntry", StructDetails { field_order: Some(&[1, 0, 2, 3, 4, 5, 6]) }),
("DoWhileStatement", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("FunctionBody", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("DoWhileStatement", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("FunctionBody", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("Dot", StructDetails { field_order: None }),
("UnaryExpression", StructDetails { field_order: Some(&[2, 0, 3, 1]) }),
("TSLiteralType", StructDetails { field_order: Some(&[2, 0, 1]) }),
("ObjectExpression", StructDetails { field_order: Some(&[2, 0, 1]) }),
("TSRestType", StructDetails { field_order: Some(&[2, 0, 1]) }),
("TemplateElement", StructDetails { field_order: Some(&[2, 0, 1, 3, 4]) }),
("JSXSpreadChild", StructDetails { field_order: Some(&[2, 0, 1]) }),
("TSOptionalType", StructDetails { field_order: Some(&[2, 0, 1]) }),
("IfStatement", StructDetails { field_order: Some(&[4, 0, 1, 2, 3]) }),
("UnaryExpression", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("TSLiteralType", StructDetails { field_order: Some(&[1, 0, 2]) }),
("ObjectExpression", StructDetails { field_order: Some(&[1, 0, 2]) }),
("TSRestType", StructDetails { field_order: Some(&[1, 0, 2]) }),
("TemplateElement", StructDetails { field_order: Some(&[1, 0, 4, 2, 3]) }),
("JSXSpreadChild", StructDetails { field_order: Some(&[1, 0, 2]) }),
("TSOptionalType", StructDetails { field_order: Some(&[1, 0, 2]) }),
("IfStatement", StructDetails { field_order: Some(&[1, 0, 2, 3, 4]) }),
("Character", StructDetails { field_order: Some(&[0, 2, 1]) }),
("LookAroundAssertion", StructDetails { field_order: Some(&[0, 2, 1]) }),
("EcmaScriptModule", StructDetails { field_order: Some(&[4, 0, 1, 2, 3]) }),
("RegExp", StructDetails { field_order: None }),
("SpreadElement", StructDetails { field_order: Some(&[2, 0, 1]) }),
("SpreadElement", StructDetails { field_order: Some(&[1, 0, 2]) }),
("Span", StructDetails { field_order: None }),
("Disjunction", StructDetails { field_order: None }),
("AssignmentTargetPropertyIdentifier", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("ConditionalExpression", StructDetails { field_order: Some(&[4, 0, 1, 2, 3]) }),
("AssignmentTargetPropertyIdentifier", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("ConditionalExpression", StructDetails { field_order: Some(&[1, 0, 2, 3, 4]) }),
(
"PropertyDefinition",
StructDetails {
field_order: Some(&[5, 0, 6, 1, 2, 3, 4, 7, 8, 9, 10, 11, 12, 13, 14]),
field_order: Some(&[1, 0, 2, 6, 7, 8, 9, 3, 4, 5, 10, 11, 12, 13, 14]),
},
),
("Comment", StructDetails { field_order: None }),
("LabeledStatement", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("StaticMemberExpression", StructDetails { field_order: Some(&[3, 0, 1, 2, 4]) }),
("LabeledStatement", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("StaticMemberExpression", StructDetails { field_order: Some(&[1, 0, 3, 4, 2]) }),
("CharacterClassEscape", StructDetails { field_order: None }),
("TSTupleType", StructDetails { field_order: Some(&[2, 0, 1]) }),
("TSTypeParameter", StructDetails { field_order: Some(&[4, 0, 1, 2, 3, 5, 6, 7]) }),
("TSTupleType", StructDetails { field_order: Some(&[1, 0, 2]) }),
("TSTypeParameter", StructDetails { field_order: Some(&[1, 0, 5, 6, 7, 2, 3, 4]) }),
("ErrorLabel", StructDetails { field_order: Some(&[1, 0]) }),
("RawTransferMetadata2", StructDetails { field_order: Some(&[0, 3, 4, 5, 1, 2]) }),
("ChainExpression", StructDetails { field_order: Some(&[2, 0, 1]) }),
("BindingProperty", StructDetails { field_order: Some(&[3, 0, 1, 2, 4, 5]) }),
("ChainExpression", StructDetails { field_order: Some(&[1, 0, 2]) }),
("BindingProperty", StructDetails { field_order: Some(&[1, 0, 4, 5, 2, 3]) }),
("NullLiteral", StructDetails { field_order: Some(&[1, 0]) }),
("TSObjectKeyword", StructDetails { field_order: Some(&[1, 0]) }),
("IdentifierName", StructDetails { field_order: Some(&[2, 0, 1]) }),
("IdentifierName", StructDetails { field_order: Some(&[1, 0, 2]) }),
("JSXOpeningFragment", StructDetails { field_order: Some(&[1, 0]) }),
("AssignmentTargetRest", StructDetails { field_order: Some(&[2, 0, 1]) }),
("TSIndexedAccessType", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("AssignmentTargetRest", StructDetails { field_order: Some(&[1, 0, 2]) }),
("TSIndexedAccessType", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("Modifiers", StructDetails { field_order: None }),
("CommentNewlines", StructDetails { field_order: None }),
("JSXText", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("ExportAllDeclaration", StructDetails { field_order: Some(&[4, 0, 1, 2, 3, 5]) }),
("TSMappedType", StructDetails { field_order: Some(&[5, 0, 1, 2, 3, 4, 7, 8, 6]) }),
("JSXText", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("ExportAllDeclaration", StructDetails { field_order: Some(&[1, 0, 3, 4, 5, 2]) }),
("TSMappedType", StructDetails { field_order: Some(&[1, 0, 3, 4, 5, 6, 7, 8, 2]) }),
("TSStringKeyword", StructDetails { field_order: Some(&[1, 0]) }),
("TSNamedTupleMember", StructDetails { field_order: Some(&[3, 0, 1, 2, 4]) }),
("TSTypeLiteral", StructDetails { field_order: Some(&[2, 0, 1]) }),
("TSTemplateLiteralType", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("JSXAttribute", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("AssignmentTargetPropertyProperty", StructDetails { field_order: Some(&[3, 0, 1, 2, 4]) }),
("TSNamedTupleMember", StructDetails { field_order: Some(&[1, 0, 3, 4, 2]) }),
("TSTypeLiteral", StructDetails { field_order: Some(&[1, 0, 2]) }),
("TSTemplateLiteralType", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("JSXAttribute", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("AssignmentTargetPropertyProperty", StructDetails { field_order: Some(&[1, 0, 3, 4, 2]) }),
("RegExpPattern", StructDetails { field_order: None }),
("TSEnumBody", StructDetails { field_order: Some(&[2, 0, 1, 3]) }),
("Decorator", StructDetails { field_order: Some(&[2, 0, 1]) }),
("TSEnumBody", StructDetails { field_order: Some(&[1, 0, 3, 2]) }),
("Decorator", StructDetails { field_order: Some(&[1, 0, 2]) }),
("EmptyStatement", StructDetails { field_order: Some(&[1, 0]) }),
("ForOfStatement", StructDetails { field_order: Some(&[4, 0, 6, 1, 2, 3, 5]) }),
("ForOfStatement", StructDetails { field_order: Some(&[1, 0, 6, 3, 4, 5, 2]) }),
("ReferenceId", StructDetails { field_order: None }),
("V8IntrinsicExpression", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("WithStatement", StructDetails { field_order: Some(&[3, 0, 1, 2, 4]) }),
("V8IntrinsicExpression", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("WithStatement", StructDetails { field_order: Some(&[1, 0, 3, 4, 2]) }),
("SourceType", StructDetails { field_order: None }),
("ThrowStatement", StructDetails { field_order: Some(&[2, 0, 1]) }),
("IdentifierReference", StructDetails { field_order: Some(&[2, 0, 1, 3]) }),
("Program", StructDetails { field_order: Some(&[6, 0, 8, 1, 2, 3, 4, 5, 7]) }),
("TaggedTemplateExpression", StructDetails { field_order: Some(&[4, 0, 1, 2, 3]) }),
("ThrowStatement", StructDetails { field_order: Some(&[1, 0, 2]) }),
("IdentifierReference", StructDetails { field_order: Some(&[1, 0, 3, 2]) }),
("Program", StructDetails { field_order: Some(&[1, 0, 8, 3, 4, 5, 6, 7, 2]) }),
("TaggedTemplateExpression", StructDetails { field_order: Some(&[1, 0, 2, 3, 4]) }),
("JSXEmptyExpression", StructDetails { field_order: Some(&[1, 0]) }),
("JSXClosingFragment", StructDetails { field_order: Some(&[1, 0]) }),
("TSTypePredicate", StructDetails { field_order: Some(&[3, 0, 1, 4, 2]) }),
("AssignmentPattern", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("Class", StructDetails { field_order: Some(&[8, 0, 10, 1, 2, 3, 4, 5, 6, 7, 11, 12, 9]) }),
("TSTypePredicate", StructDetails { field_order: Some(&[1, 0, 3, 2, 4]) }),
("AssignmentPattern", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("Class", StructDetails { field_order: Some(&[1, 0, 10, 3, 4, 5, 6, 7, 8, 9, 11, 12, 2]) }),
("StaticImport", StructDetails { field_order: None }),
("TSThisType", StructDetails { field_order: Some(&[1, 0]) }),
("NamedReference", StructDetails { field_order: None }),
("JSDocUnknownType", StructDetails { field_order: Some(&[1, 0]) }),
("BigIntLiteral", StructDetails { field_order: Some(&[3, 0, 1, 2, 4]) }),
("ForStatement", StructDetails { field_order: Some(&[5, 0, 1, 2, 3, 4, 6]) }),
("ParenthesizedExpression", StructDetails { field_order: Some(&[2, 0, 1]) }),
("ContinueStatement", StructDetails { field_order: Some(&[2, 0, 1]) }),
("TSQualifiedName", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("ArrayPattern", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("TSTypeOperator", StructDetails { field_order: Some(&[2, 0, 3, 1]) }),
("UpdateExpression", StructDetails { field_order: Some(&[2, 0, 3, 4, 1]) }),
("BigIntLiteral", StructDetails { field_order: Some(&[1, 0, 3, 4, 2]) }),
("ForStatement", StructDetails { field_order: Some(&[1, 0, 3, 4, 5, 6, 2]) }),
("ParenthesizedExpression", StructDetails { field_order: Some(&[1, 0, 2]) }),
("ContinueStatement", StructDetails { field_order: Some(&[1, 0, 2]) }),
("TSQualifiedName", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("ArrayPattern", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("TSTypeOperator", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("UpdateExpression", StructDetails { field_order: Some(&[1, 0, 2, 3, 4]) }),
("CharacterClass", StructDetails { field_order: Some(&[0, 2, 3, 4, 1]) }),
("ClassString", StructDetails { field_order: Some(&[0, 2, 1]) }),
("ImportSpecifier", StructDetails { field_order: Some(&[3, 0, 1, 2, 4]) }),
("TSIntersectionType", StructDetails { field_order: Some(&[2, 0, 1]) }),
("ImportAttribute", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("ImportSpecifier", StructDetails { field_order: Some(&[1, 0, 3, 4, 2]) }),
("TSIntersectionType", StructDetails { field_order: Some(&[1, 0, 2]) }),
("ImportAttribute", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("TSNeverKeyword", StructDetails { field_order: Some(&[1, 0]) }),
("JSXSpreadAttribute", StructDetails { field_order: Some(&[2, 0, 1]) }),
("CallExpression", StructDetails { field_order: Some(&[4, 0, 1, 2, 3, 5, 6]) }),
("Hashbang", StructDetails { field_order: Some(&[2, 0, 1]) }),
("TSUnionType", StructDetails { field_order: Some(&[2, 0, 1]) }),
("TSTypeReference", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("JSXSpreadAttribute", StructDetails { field_order: Some(&[1, 0, 2]) }),
("CallExpression", StructDetails { field_order: Some(&[1, 0, 4, 5, 6, 2, 3]) }),
("Hashbang", StructDetails { field_order: Some(&[1, 0, 2]) }),
("TSUnionType", StructDetails { field_order: Some(&[1, 0, 2]) }),
("TSTypeReference", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("Error", StructDetails { field_order: Some(&[4, 0, 1, 2, 3]) }),
("ArrayExpression", StructDetails { field_order: Some(&[2, 0, 1]) }),
("ArrayExpression", StructDetails { field_order: Some(&[1, 0, 2]) }),
("IndexedReference", StructDetails { field_order: None }),
("TSSymbolKeyword", StructDetails { field_order: Some(&[1, 0]) }),
("TSInferType", StructDetails { field_order: Some(&[2, 0, 1]) }),
("AssignmentTargetWithDefault", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("TemplateLiteral", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("PrivateInExpression", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("JSXFragment", StructDetails { field_order: Some(&[4, 0, 1, 2, 3]) }),
("TSInferType", StructDetails { field_order: Some(&[1, 0, 2]) }),
("AssignmentTargetWithDefault", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("TemplateLiteral", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("PrivateInExpression", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("JSXFragment", StructDetails { field_order: Some(&[1, 0, 2, 3, 4]) }),
("ImportEntry", StructDetails { field_order: None }),
("JSXExpressionContainer", StructDetails { field_order: Some(&[2, 0, 1]) }),
("TSTypeAssertion", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("JSXExpressionContainer", StructDetails { field_order: Some(&[1, 0, 2]) }),
("TSTypeAssertion", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
(
"AccessorProperty",
StructDetails { field_order: Some(&[5, 0, 6, 1, 2, 3, 4, 7, 8, 9, 10, 11]) },
StructDetails { field_order: Some(&[1, 0, 2, 6, 7, 8, 9, 3, 4, 5, 10, 11]) },
),
("Modifier", StructDetails { field_order: None }),
("LabelIdentifier", StructDetails { field_order: Some(&[2, 0, 1]) }),
("TSEnumMember", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("LabelIdentifier", StructDetails { field_order: Some(&[1, 0, 2]) }),
("TSEnumMember", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("TSUndefinedKeyword", StructDetails { field_order: Some(&[1, 0]) }),
("TSImportTypeQualifiedName", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("TSImportTypeQualifiedName", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("BoundaryAssertion", StructDetails { field_order: None }),
("TSClassImplements", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("BreakStatement", StructDetails { field_order: Some(&[2, 0, 1]) }),
("TSConstructorType", StructDetails { field_order: Some(&[4, 0, 6, 1, 2, 3, 5]) }),
("TSCallSignatureDeclaration", StructDetails { field_order: Some(&[5, 0, 1, 2, 3, 4, 6]) }),
("ExportDefaultDeclaration", StructDetails { field_order: Some(&[2, 0, 1]) }),
("TSGlobalDeclaration", StructDetails { field_order: Some(&[3, 0, 1, 2, 5, 4]) }),
("TSFunctionType", StructDetails { field_order: Some(&[5, 0, 1, 2, 3, 4, 6]) }),
("VariableDeclarator", StructDetails { field_order: Some(&[4, 0, 5, 1, 2, 3, 6]) }),
("TSClassImplements", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("BreakStatement", StructDetails { field_order: Some(&[1, 0, 2]) }),
("TSConstructorType", StructDetails { field_order: Some(&[1, 0, 6, 3, 4, 5, 2]) }),
("TSCallSignatureDeclaration", StructDetails { field_order: Some(&[1, 0, 3, 4, 5, 6, 2]) }),
("ExportDefaultDeclaration", StructDetails { field_order: Some(&[1, 0, 2]) }),
("TSGlobalDeclaration", StructDetails { field_order: Some(&[1, 0, 3, 4, 5, 2]) }),
("TSFunctionType", StructDetails { field_order: Some(&[1, 0, 3, 4, 5, 6, 2]) }),
("VariableDeclarator", StructDetails { field_order: Some(&[1, 0, 2, 4, 5, 6, 3]) }),
("StaticExport", StructDetails { field_order: None }),
("MetaProperty", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("TSInterfaceHeritage", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("MetaProperty", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("TSInterfaceHeritage", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("CharacterClassRange", StructDetails { field_order: None }),
("LogicalExpression", StructDetails { field_order: Some(&[3, 0, 1, 4, 2]) }),
("LogicalExpression", StructDetails { field_order: Some(&[1, 0, 3, 2, 4]) }),
("TSBigIntKeyword", StructDetails { field_order: Some(&[1, 0]) }),
("TSConditionalType", StructDetails { field_order: Some(&[5, 0, 1, 2, 3, 4, 6]) }),
("TSConditionalType", StructDetails { field_order: Some(&[1, 0, 3, 4, 5, 6, 2]) }),
("CapturingGroup", StructDetails { field_order: None }),
("JSXOpeningElement", StructDetails { field_order: Some(&[4, 0, 1, 2, 3]) }),
("ImportExpression", StructDetails { field_order: Some(&[3, 0, 1, 2, 4]) }),
("ImportDefaultSpecifier", StructDetails { field_order: Some(&[2, 0, 1]) }),
("JSXOpeningElement", StructDetails { field_order: Some(&[1, 0, 2, 3, 4]) }),
("ImportExpression", StructDetails { field_order: Some(&[1, 0, 3, 4, 2]) }),
("ImportDefaultSpecifier", StructDetails { field_order: Some(&[1, 0, 2]) }),
("Pattern", StructDetails { field_order: None }),
("FormalParameterRest", StructDetails { field_order: Some(&[4, 0, 1, 2, 3]) }),
("TSImportType", StructDetails { field_order: Some(&[5, 0, 1, 2, 3, 4]) }),
("FormalParameter", StructDetails { field_order: Some(&[5, 0, 1, 2, 3, 4, 6, 7, 8, 9]) }),
("FormalParameterRest", StructDetails { field_order: Some(&[1, 0, 2, 3, 4]) }),
("TSImportType", StructDetails { field_order: Some(&[1, 0, 2, 3, 4, 5]) }),
("FormalParameter", StructDetails { field_order: Some(&[1, 0, 6, 7, 8, 9, 2, 3, 4, 5]) }),
("TemplateElementValue", StructDetails { field_order: None }),
("TSThisParameter", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("RegExpLiteral", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("TSThisParameter", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("RegExpLiteral", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("RegExpFlags", StructDetails { field_order: None }),
("TSSatisfiesExpression", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("ClassBody", StructDetails { field_order: Some(&[2, 0, 1]) }),
("TSSatisfiesExpression", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("ClassBody", StructDetails { field_order: Some(&[1, 0, 2]) }),
("ClassStringDisjunction", StructDetails { field_order: Some(&[0, 2, 1]) }),
(
"TSMethodSignature",
StructDetails { field_order: Some(&[6, 0, 1, 8, 9, 10, 2, 3, 4, 5, 7]) },
StructDetails { field_order: Some(&[1, 0, 3, 8, 9, 10, 4, 5, 6, 7, 2]) },
),
("WhileStatement", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("JSXIdentifier", StructDetails { field_order: Some(&[2, 0, 1]) }),
("WhileStatement", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("JSXIdentifier", StructDetails { field_order: Some(&[1, 0, 2]) }),
(
"ArrowFunctionExpression",
StructDetails { field_order: Some(&[5, 0, 7, 8, 1, 2, 3, 4, 6, 9, 10]) },
StructDetails { field_order: Some(&[1, 0, 7, 8, 3, 4, 5, 6, 2, 9, 10]) },
),
("Alternative", StructDetails { field_order: None }),
("TSArrayType", StructDetails { field_order: Some(&[2, 0, 1]) }),
("PrivateFieldExpression", StructDetails { field_order: Some(&[3, 0, 1, 2, 4]) }),
("ExportNamedDeclaration", StructDetails { field_order: Some(&[5, 0, 1, 2, 3, 6, 4]) }),
("Directive", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("TryStatement", StructDetails { field_order: Some(&[4, 0, 1, 2, 3]) }),
("AwaitExpression", StructDetails { field_order: Some(&[2, 0, 1]) }),
("StaticBlock", StructDetails { field_order: Some(&[2, 0, 1, 3]) }),
("TSTypeAliasDeclaration", StructDetails { field_order: Some(&[4, 0, 1, 2, 3, 6, 5]) }),
("VariableDeclaration", StructDetails { field_order: Some(&[2, 0, 3, 1, 4]) }),
("TSExportAssignment", StructDetails { field_order: Some(&[2, 0, 1]) }),
("JSDocNonNullableType", StructDetails { field_order: Some(&[2, 0, 1, 3]) }),
("TSArrayType", StructDetails { field_order: Some(&[1, 0, 2]) }),
("PrivateFieldExpression", StructDetails { field_order: Some(&[1, 0, 3, 4, 2]) }),
("ExportNamedDeclaration", StructDetails { field_order: Some(&[1, 0, 3, 4, 5, 2, 6]) }),
("Directive", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("TryStatement", StructDetails { field_order: Some(&[1, 0, 2, 3, 4]) }),
("AwaitExpression", StructDetails { field_order: Some(&[1, 0, 2]) }),
("StaticBlock", StructDetails { field_order: Some(&[1, 0, 3, 2]) }),
("TSTypeAliasDeclaration", StructDetails { field_order: Some(&[1, 0, 3, 4, 5, 6, 2]) }),
("VariableDeclaration", StructDetails { field_order: Some(&[1, 0, 2, 4, 3]) }),
("TSExportAssignment", StructDetails { field_order: Some(&[1, 0, 2]) }),
("JSDocNonNullableType", StructDetails { field_order: Some(&[1, 0, 3, 2]) }),
("FixedSizeAllocatorMetadata", StructDetails { field_order: Some(&[1, 0, 2]) }),
("TSNonNullExpression", StructDetails { field_order: Some(&[2, 0, 1]) }),
("BinaryExpression", StructDetails { field_order: Some(&[3, 0, 1, 4, 2]) }),
("TSNamespaceExportDeclaration", StructDetails { field_order: Some(&[2, 0, 1]) }),
("TSNonNullExpression", StructDetails { field_order: Some(&[1, 0, 2]) }),
("BinaryExpression", StructDetails { field_order: Some(&[1, 0, 3, 2, 4]) }),
("TSNamespaceExportDeclaration", StructDetails { field_order: Some(&[1, 0, 2]) }),
("BooleanLiteral", StructDetails { field_order: Some(&[1, 0, 2]) }),
(
"TSConstructSignatureDeclaration",
StructDetails { field_order: Some(&[4, 0, 1, 2, 3, 5]) },
StructDetails { field_order: Some(&[1, 0, 3, 4, 5, 2]) },
),
("FormalParameters", StructDetails { field_order: Some(&[3, 0, 4, 1, 2]) }),
("FormalParameters", StructDetails { field_order: Some(&[1, 0, 2, 3, 4]) }),
(
"Function",
StructDetails {
field_order: Some(&[7, 0, 9, 1, 10, 11, 12, 2, 3, 4, 5, 6, 8, 13, 14]),
field_order: Some(&[1, 0, 9, 3, 10, 11, 12, 4, 5, 6, 7, 8, 2, 13, 14]),
},
),
("TSNullKeyword", StructDetails { field_order: Some(&[1, 0]) }),
("Elision", StructDetails { field_order: Some(&[1, 0]) }),
("TSIndexSignature", StructDetails { field_order: Some(&[3, 0, 1, 2, 4, 5]) }),
("TSIndexSignatureName", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("TSIndexSignature", StructDetails { field_order: Some(&[1, 0, 4, 5, 2, 3]) }),
("TSIndexSignatureName", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("Super", StructDetails { field_order: Some(&[1, 0]) }),
("Quantifier", StructDetails { field_order: Some(&[0, 1, 2, 4, 3]) }),
("ObjectPattern", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("ObjectPattern", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("DynamicImport", StructDetails { field_order: None }),
("TSBooleanKeyword", StructDetails { field_order: Some(&[1, 0]) }),
("TSParenthesizedType", StructDetails { field_order: Some(&[2, 0, 1]) }),
("TSParenthesizedType", StructDetails { field_order: Some(&[1, 0, 2]) }),
("NameSpan", StructDetails { field_order: Some(&[1, 0]) }),
("ComputedMemberExpression", StructDetails { field_order: Some(&[3, 0, 1, 2, 4]) }),
("YieldExpression", StructDetails { field_order: Some(&[2, 0, 3, 1]) }),
("TSTypeAnnotation", StructDetails { field_order: Some(&[2, 0, 1]) }),
("JSXMemberExpression", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("TSImportEqualsDeclaration", StructDetails { field_order: Some(&[3, 0, 1, 2, 4]) }),
("ComputedMemberExpression", StructDetails { field_order: Some(&[1, 0, 3, 4, 2]) }),
("YieldExpression", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("TSTypeAnnotation", StructDetails { field_order: Some(&[1, 0, 2]) }),
("JSXMemberExpression", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("TSImportEqualsDeclaration", StructDetails { field_order: Some(&[1, 0, 3, 4, 2]) }),
("TSVoidKeyword", StructDetails { field_order: Some(&[1, 0]) }),
("SequenceExpression", StructDetails { field_order: Some(&[2, 0, 1]) }),
("ExportSpecifier", StructDetails { field_order: Some(&[3, 0, 1, 2, 4]) }),
("TSTypeParameterDeclaration", StructDetails { field_order: Some(&[2, 0, 1]) }),
("BindingIdentifier", StructDetails { field_order: Some(&[2, 0, 1, 3]) }),
("JSXClosingElement", StructDetails { field_order: Some(&[2, 0, 1]) }),
("SequenceExpression", StructDetails { field_order: Some(&[1, 0, 2]) }),
("ExportSpecifier", StructDetails { field_order: Some(&[1, 0, 3, 4, 2]) }),
("TSTypeParameterDeclaration", StructDetails { field_order: Some(&[1, 0, 2]) }),
("BindingIdentifier", StructDetails { field_order: Some(&[1, 0, 3, 2]) }),
("JSXClosingElement", StructDetails { field_order: Some(&[1, 0, 2]) }),
("CommentNodeId", StructDetails { field_order: None }),
("TSExternalModuleReference", StructDetails { field_order: Some(&[2, 0, 1]) }),
("TSInstantiationExpression", StructDetails { field_order: Some(&[3, 0, 1, 2]) }),
("ExpressionStatement", StructDetails { field_order: Some(&[2, 0, 1]) }),
("TSExternalModuleReference", StructDetails { field_order: Some(&[1, 0, 2]) }),
("TSInstantiationExpression", StructDetails { field_order: Some(&[1, 0, 2, 3]) }),
("ExpressionStatement", StructDetails { field_order: Some(&[1, 0, 2]) }),
("NonMaxU32", StructDetails { field_order: None }),
],
};
+4 -4
View File
@@ -54,22 +54,22 @@ export const PROGRAM_OFFSET = 0;
/**
* Byte offset of pointer to start of source text, relative to start of `Program`.
*/
export const SOURCE_START_OFFSET = 8;
export const SOURCE_START_OFFSET = 16;
/**
* Byte offset of length of source text, relative to start of `Program`.
*/
export const SOURCE_LEN_OFFSET = 16;
export const SOURCE_LEN_OFFSET = 24;
/**
* Byte offset of comments `Vec` pointer, relative to start of `Program`.
*/
export const COMMENTS_OFFSET = 24;
export const COMMENTS_OFFSET = 32;
/**
* Byte offset of comments `Vec` length, relative to start of `Program`.
*/
export const COMMENTS_LEN_OFFSET = 32;
export const COMMENTS_LEN_OFFSET = 40;
/**
* Size of `Comment` struct in bytes.
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+183 -85
View File
@@ -6,7 +6,7 @@
use std::{
borrow::Cow,
cmp::{Ordering, max, min},
cmp::{max, min},
num,
sync::atomic,
};
@@ -55,13 +55,17 @@ impl Generator for AssertLayouts {
struct LayoutCalculator<'s> {
schema: &'s mut Schema,
span_type_id: TypeId,
node_id_cell_type_id: TypeId,
}
impl LayoutCalculator<'_> {
/// Calculate layouts of all types.
fn calculate(schema: &mut Schema) {
let span_type_id = schema.type_names["Span"];
let mut calculator = LayoutCalculator { schema, span_type_id };
let node_id_cell_type_id =
schema.type_by_name("NodeId").as_primitive().unwrap().containers.cell_id.unwrap();
let mut calculator = LayoutCalculator { schema, span_type_id, node_id_cell_type_id };
for type_id in calculator.schema.types.indices() {
calculator.calculate_type(type_id);
@@ -130,7 +134,103 @@ impl LayoutCalculator<'_> {
}
}
}
}
struct StructState<'s> {
layout_64: PlatformLayout,
layout_32: PlatformLayout,
struct_def: &'s mut StructDef,
current_align_64: u32,
current_align_32: u32,
}
struct FieldData {
index: usize,
layout: Layout,
priority: u64,
}
const MAX_ALIGN: u32 = 1 << 31;
impl<'s> StructState<'s> {
fn new(struct_def: &'s mut StructDef) -> Self {
Self {
layout_64: PlatformLayout::from_size_align(0, 1),
layout_32: PlatformLayout::from_size_align(0, 1),
struct_def,
current_align_64: MAX_ALIGN,
current_align_32: MAX_ALIGN,
}
}
fn add_field(&mut self, field_data: &FieldData) {
let struct_name = self.struct_def.name();
let offset_64 = Self::update(
&mut self.layout_64,
&field_data.layout.layout_64,
&mut self.current_align_64,
struct_name,
);
let offset_32 = Self::update(
&mut self.layout_32,
&field_data.layout.layout_32,
&mut self.current_align_32,
struct_name,
);
// Store offset on `field`
let field = &mut self.struct_def.fields[field_data.index];
field.offset = Offset { offset_64, offset_32 };
}
fn update(
layout: &mut PlatformLayout,
field_layout: &PlatformLayout,
current_align: &mut u32,
struct_name: &str,
) -> u32 {
// Check alignment is correct for this field
let offset = layout.size;
assert!(
offset.is_multiple_of(field_layout.align),
"Incorrect alignment for struct fields in `{struct_name}`"
);
// Update alignment
layout.align = max(layout.align, field_layout.align);
// Update niche.
// Take the largest niche. Preference for (in order):
// * Largest single range of niche values.
// * Largest number of niche values at start of range.
// * Earlier field (earlier after re-ordering).
if let Some(field_niche) = &field_layout.niche
&& layout.niche.as_ref().is_none_or(|niche| {
field_niche.count_max() > niche.count_max()
|| (field_niche.count_max() == niche.count_max()
&& field_niche.count_start > niche.count_start)
})
{
let mut niche = field_niche.clone();
niche.offset += offset;
layout.niche = Some(niche);
}
// Next field starts after this one
if field_layout.size > 0 {
let next_offset = offset + field_layout.size;
layout.size = next_offset;
// Get highest alignment next field can have
*current_align = next_offset & next_offset.wrapping_neg();
}
// Return offset of this field
offset
}
}
impl LayoutCalculator<'_> {
/// Calculate layout for a struct.
///
/// All structs in AST are `#[repr(C)]`. In a `#[repr(C)]` struct, compiler does not re-order the fields,
@@ -152,11 +252,13 @@ impl LayoutCalculator<'_> {
///
/// Fields are ordered according to the following rules, in order:
///
/// 1. If field is `span: Span` it goes first.
/// 2. If field is ZST, it goes last.
/// 3. Fields with higher alignment on 64-bit systems go first.
/// 4. Fields with higher alignment on 32-bit systems go first.
/// 5. Otherwise, retain original field order.
/// 1. `span: Span` goes first.
/// 2. `node_id: Cell<NodeId>` goes next.
/// 3. ZST fields go last.
/// 4. Fields with higher alignment on 64-bit systems go first.
/// 5. Fields with higher alignment on 32-bit systems go first.
/// 6. If the highest-aligned field cannot go next, take the next field which has suitable alignment.
/// 7. Otherwise, retain original field order.
///
/// This ordering scheme does not match `#[repr(Rust)]`, but is equally efficient in terms of packing
/// structs into as few bytes as possible.
@@ -167,6 +269,8 @@ impl LayoutCalculator<'_> {
///
/// `span: Span` is always first to make `Expression::get_span` etc branchless, because the `span` field
/// is in the same position for all of `Expression`'s variants.
/// `node_id: Cell<NodeId>` goes after `span: Span` for the same reason - so it has consistent position
/// in all AST structs, which makes `AstKind::node_id` and `AstKind::set_node_id` branchless.
///
/// Ordering by 64-bit alignment first, then 32-alignment creates a layout that is optimally packed
/// on both platforms. Fields will be ordered `u64`, `usize`, `u32`, so `usize` will have same alignment
@@ -180,97 +284,91 @@ impl LayoutCalculator<'_> {
/// If they did, this would screw up sorting the fields in `generate_struct_details`.
fn calculate_struct(&mut self, type_id: TypeId) -> Layout {
// Get layout of fields' types and calculate optimal field order
struct FieldData {
index: usize,
layout: Layout,
is_span: bool,
is_zst: bool,
}
let mut zst_fields = vec![];
let mut field_order = self
let mut fields = self
.schema
.struct_def(type_id)
.field_indices()
.map(|index| {
.filter_map(|index| {
let field = &self.schema.struct_def(type_id).fields[index];
let is_span = field.type_id == self.span_type_id && field.name() == "span";
let layout = self.calculate_type(field.type_id);
let is_zst = layout.layout_64.size == 0 && layout.layout_32.size == 0;
FieldData { index, layout: layout.clone(), is_span, is_zst }
let layout = self.calculate_type(field.type_id).clone();
if layout.layout_64.size == 0 || layout.layout_32.size == 0 {
assert!(
layout.layout_64.size == 0 && layout.layout_32.size == 0,
"ZST field must have 0 size on all platforms"
);
zst_fields.push(FieldData { index, layout, priority: 0 });
return None;
}
let field = &self.schema.struct_def(type_id).fields[index];
let priority = if field.type_id == self.span_type_id && field.name() == "span" {
// Highest priority
u64::MAX
} else if field.type_id == self.node_id_cell_type_id && field.name() == "node_id" {
// 2nd highest priority
u64::MAX - 1
} else {
// Prioritize fields with higher alignment on 64-bit systems, then 32-bit systems
(u64::from(layout.layout_64.align) << 32) | u64::from(layout.layout_32.align)
};
Some(FieldData { index, layout, priority })
})
.collect::<Vec<_>>();
field_order.sort_unstable_by(|f1, f2| {
let mut order = f1.is_span.cmp(&f2.is_span).reverse();
if order == Ordering::Equal {
order = f1.is_zst.cmp(&f2.is_zst);
if order == Ordering::Equal {
order = f1.layout.layout_64.align.cmp(&f2.layout.layout_64.align).reverse();
if order == Ordering::Equal {
order = f1.layout.layout_32.align.cmp(&f2.layout.layout_32.align).reverse();
if order == Ordering::Equal {
order = f1.index.cmp(&f2.index);
}
}
fields.sort_by(|f1, f2| f1.priority.cmp(&f2.priority).reverse());
// Add fields (except ZST fields)
let mut state = StructState::new(self.schema.struct_def_mut(type_id));
while !fields.is_empty() {
// Find next field with alignment <= current alignment for both 32 bit and 64 bit.
// `other_fields` is sorted by alignment in descending order,
// so this will pick the next field with largest alignment which can be placed at current offset.
let index = fields.iter().position(|f| {
f.layout.layout_64.align <= state.current_align_64
&& f.layout.layout_32.align <= state.current_align_32
});
if let Some(index) = index {
// Found field with suitable alignment - add it to struct
let field_data = fields.remove(index);
state.add_field(&field_data);
} else {
// No field with suitable alignment found.
// Double desired alignment, and add padding so it's aligned.
// Go round loop again to find next field which satisfies new looser alignment requirements.
let smallest_align_64 = fields.last().unwrap().layout.layout_64.align;
if state.current_align_64 < smallest_align_64 {
// No field meets alignment requirement for 64 bit
state.current_align_64 *= 2;
state.layout_64.size =
state.layout_64.size.next_multiple_of(state.current_align_64);
} else {
// No field meets alignment requirement for 32 bit
state.current_align_32 *= 2;
state.layout_32.size =
state.layout_32.size.next_multiple_of(state.current_align_32);
}
}
order
});
}
// Calculate offset of each field, and size + alignment of struct
let mut layout_64 = PlatformLayout::from_size_align(0, 1);
let mut layout_32 = PlatformLayout::from_size_align(0, 1);
// Add ZST fields last, in original order.
// Order of ZSTs doesn't matter, but keeping in original order is relied on by `generate_struct_details`.
let StructState { mut layout_64, mut layout_32, struct_def, .. } = state;
let struct_def = self.schema.struct_def_mut(type_id);
for field_data in &field_order {
fn update(
layout: &mut PlatformLayout,
field_layout: &PlatformLayout,
struct_name: &str,
) -> u32 {
// Field should already be aligned as we've re-ordered fields to ensure they're tightly packed.
// This shouldn't break as long as all fields are aligned on `< 16`.
// If we introduce a `u128` into AST, we might need to change the field ordering algorithm
// to fill in the gap between `span` and the `u128` field.
let offset = layout.size;
assert!(
offset.is_multiple_of(field_layout.align),
"Incorrect alignment for struct fields in `{struct_name}`"
);
// Update alignment
layout.align = max(layout.align, field_layout.align);
// Update niche.
// Take the largest niche. Preference for (in order):
// * Largest single range of niche values.
// * Largest number of niche values at start of range.
// * Earlier field (earlier after re-ordering).
if let Some(field_niche) = &field_layout.niche
&& layout.niche.as_ref().is_none_or(|niche| {
field_niche.count_max() > niche.count_max()
|| (field_niche.count_max() == niche.count_max()
&& field_niche.count_start > niche.count_start)
})
{
let mut niche = field_niche.clone();
niche.offset += offset;
layout.niche = Some(niche);
}
// Next field starts after this one
layout.size = offset + field_layout.size;
// Return offset of this field
offset
for field_data in &zst_fields {
fn update(layout: &mut PlatformLayout, align: u32) -> u32 {
layout.size = layout.size.next_multiple_of(align);
layout.align = max(layout.align, align);
layout.size
}
let offset_64 = update(&mut layout_64, &field_data.layout.layout_64, struct_def.name());
let offset_32 = update(&mut layout_32, &field_data.layout.layout_32, struct_def.name());
// Store offset on `field`
let field = &mut struct_def.fields[field_data.index];
field.offset = Offset { offset_64, offset_32 };
let offset_64 = update(&mut layout_64, field_data.layout.layout_64.align);
let offset_32 = update(&mut layout_32, field_data.layout.layout_32.align);
struct_def.fields[field_data.index].offset = Offset { offset_64, offset_32 };
}
// Round up size to alignment
+3 -5
View File
@@ -190,6 +190,7 @@ impl Generator for AstKindGenerator {
///@@line_break
/// Get [`NodeId`] of an [`AstKind`].
///@ `node_id` field is in consistent position in all AST structs, so this boils down to 1 instruction.
#[inline]
pub fn node_id(&self) -> NodeId {
match self {
@@ -199,11 +200,8 @@ impl Generator for AstKindGenerator {
///@@line_break
/// Set [`NodeId`] of an [`AstKind`].
#[expect(
clippy::inline_always,
reason = "enables compile-time match elimination in semantic builder"
)]
#[inline(always)]
///@ `node_id` field is in consistent position in all AST structs, so this boils down to 1 instruction.
#[inline]
pub fn set_node_id(&self, node_id: NodeId) {
match self {
#set_node_id_match_arms