Add variable support with load, store, and lea instructions#65
Open
Add variable support with load, store, and lea instructions#65
Conversation
…tions Introduces first-class stack variable support to the USM IR, enabling register allocation passes to spill virtual registers to named stack slots that remain explicitly distinguishable from registers in the IR. Key changes: - lex: add VariableToken for the & sigil prefix - parse: add VariableNode (argument), VariableDeclarationNode (preamble), update FunctionNode with Variables []VariableDeclarationNode field, update ArgumentParser to handle variable arguments - gen: add VariableInfo, VariableManager interface, VariableArgumentInfo, VariableDeclarationGenerator; extend FunctionGenerationContext and ManagerCreators with VariableManager; extend FunctionInfo with Variables field and String() output; fix ReferencedTypeInfo.Equal() big.Int pointer comparison bug; add IsAutoGenerated to LabelInfo to suppress auto-labels in String() round-trips - usm/managers: add VariableList (slice+map) implementation of VariableManager - usm/isa: add load (&var → %reg), store (%reg → &var, critical), lea (&var → pointer %reg) instructions - aarch64/managers: wire in VariableManagerCreator (reuses usm implementation) - tests: parse-level and gen-level tests for variable declarations, round-trip String(), undefined/duplicate variable errors, load/store/lea https://claude.ai/code/session_01JxzeScSGcF7w9ADKnEL27q
Variables no longer require explicit declarations — they are created lazily on first use (via &name in any instruction argument) and their type is inferred during instruction validation by load, store, and lea. - parse: remove VariableDeclarationNode and preamble parsing; FunctionNode no longer has a Variables field - gen: remove VariableDeclarationGenerator and collectVariableDeclarations; VariableArgumentGenerator creates the variable lazily with a nil type - usm/isa: load/store infer variable type from target/value on first use and validate on subsequent uses; lea infers by stripping the pointer descriptor from the target type, and adds stripPointer() helper - gen: add TypeDescriptorInfo.Equal() and use it in ReferencedTypeInfo.Equal() and isPointerTo() to replace ad-hoc struct field comparisons https://claude.ai/code/session_01JxzeScSGcF7w9ADKnEL27q
…, parse/function minimal diff - gen/label_info.go: replace IsAutoGenerated bool with Declaration *core.UnmanagedSourceView (nil = auto-generated), which is the idiomatic Go way to express optional source locations - gen/label_map.go: GenerateLabel() returns LabelInfo with nil Declaration - gen/basic_block_info.go: check Declaration != nil instead of IsAutoGenerated - gen/label_definition_generator.go: store &declaration as pointer - gen/variable_info.go: fix comment — registers are not required to be SSA values (they can have multiple definitions before SSA construction) - parse/function.go: restore if n.Instructions != nil guard in stringBlock to minimise the diff from the original https://claude.ai/code/session_01JxzeScSGcF7w9ADKnEL27q
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #65 +/- ##
==========================================
+ Coverage 41.00% 41.89% +0.88%
==========================================
Files 123 130 +7
Lines 3790 4039 +249
==========================================
+ Hits 1554 1692 +138
- Misses 2134 2238 +104
- Partials 102 109 +7 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
The descriptor *N packs N consecutive pointer levels into a single entry, so adding one level of indirection to a type ending with *N must yield *(N+1), not a separate *1 appended. - isPointerTo: split into two cases — if varType ends with *N, target must be identical with *(N+1) as last descriptor; otherwise target must append a new *1 descriptor - stripPointer: if last descriptor has Amount > 1, decrement it; if Amount == 1, remove the descriptor entirely - lea Validate: remove incorrect Amount == 1 guard in the inference path (any pointer descriptor, including *2, is a valid lea target) - Add TestLeaNestedPointer (variable of type $32 *1 → target $32 *2) and TestLeaMismatchedPointer to cover the new cases https://claude.ai/code/session_01JxzeScSGcF7w9ADKnEL27q
Add PointerTo() and Deref() methods to ReferencedTypeInfo in gen/ so pointer-level arithmetic is reusable across ISAs rather than buried in the lea instruction. Both methods correctly handle the *N packed-pointer semantics (adding a level increments N; removing decrements it, dropping the descriptor when N reaches 1). lea.go loses isPointerTo and stripPointer entirely — validation and inference are now single expressions using the new methods.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR introduces support for local variables in the USM instruction set, enabling functions to read from and write to named memory locations in the local frame.
Summary
Variables are distinct from registers—they are mutable memory locations that can be accessed by multiple instructions. This change adds three new instructions (
load,store,lea) and the infrastructure to manage variables throughout the compilation pipeline.Key Changes
New Instructions:
load: Reads a variable's value into a register ($type %reg = load &var)store: Writes a register's value into a variable (store &var %reg)lea: Computes the address of a variable and stores it in a pointer register ($type * %ptr = lea &var)Variable Management:
VariableInfoto represent named, typed slots in the function's local frameVariableManagerinterface withVariableListas the default implementationFunctionInfo.VariablesParser Updates:
VariableNodeandVariableParserto parse variable references (prefixed with&)ArgumentParserto recognize variables as valid instruction argumentsFunctionParserto properly format function blocks with indentationType System:
Equal()method toTypeDescriptorInfofor type comparisonleavalidates that the target is a pointer type and infers variable type by stripping the pointer descriptorloadandstorevalidate type consistency across multiple uses of the same variableCode Generation:
VariableArgumentGeneratorto generateVariableArgumentInfoduring instruction context generationArgumentToVariable()helper for type-safe argument conversionFunctionGeneratorto initialize the variables manager for each functionNotable Implementation Details
storeis marked as a critical instruction (not eliminated by dead code elimination) due to its side effectsloadandleaare non-critical and can be eliminated if their results are unusedleainstruction bridges variables to the general pointer model, allowing pointers to be passed to functions or stored in memoryhttps://claude.ai/code/session_01JxzeScSGcF7w9ADKnEL27q