This commit consolidates `DefLocal` and `DefGlobal` into a single
`Define` bound kind. This simplifies the AST and makes it more
consistent.
It also introduces `DeclarationKind` to differentiate between variable
and parameter definitions.
The `UsageInfo` struct has been updated to include tracking for
`used_upvalues` and `assigned_upvalues`. The `collect_usage` function
has been modified to handle these new fields when encountering
`Address::Upvalue`.
The `map_params_to_args` function now takes `body_usage` as an argument
to ensure that parameters assigned to or used in the body are correctly
substituted. A new helper function, `collect_parameter_slots_set`, has
been added to gather all parameter slots within a pattern.
The inlining logic in `Optimizer::inline_call` has been enhanced to
prevent inlining if a parameter is used or assigned in the function body
but cannot be substituted. This addresses a bug where aggressive
inlining could lead to incorrect code generation when parameters were
reassigned.
A new integration test, `test_closure_reassignment_optimization_bug`,
has been added to specifically target and verify the fix for this
inlining issue.
Refine the type checker to correctly handle destructuring of various
collection types (tuples, vectors, matrices, lists, records).
Additionally, prevent implicit type coercion for function arguments,
ensuring that only tuples are passed to functions expecting tuple
arguments. This avoids unexpected behavior where vectors or matrices
might be treated as tuples.
Add a new example file demonstrating pattern matching and destructuring
rules.
The `flatten_tuple` function was unnecessarily recursive. It can now
simply return the elements of the tuple directly. The VM's destructuring
logic has been updated to handle nested destructuring more efficiently.
A new integration test case for multi-level destructuring has been
added.
Renames `DefDestructure` to `Destructure` to better reflect its use in
both definitions and assignments.
Introduces `bind_assign_pattern` to handle assignment destructuring in
the binder.
Adds `test_assign_destructuring` to verify assignment destructuring
functionality.
This commit introduces the `DefDestructure` bound kind and modifies the
binder, analyzer, type checker, and VM to support destructuring in `def`
statements. This allows for pattern matching on the right-hand side of a
`def` to bind multiple variables.
The parser has been updated to accept patterns in `def` statements. The
binder now handles `UntypedKind::Def` with a `target` pattern, rather
than a simple `name`. This enables destructuring.
The `Gemini.md` documentation has been updated to include a new rule for
incremental development.
The `again` keyword is introduced to facilitate explicit recursive
function calls.
It is restricted to tail-call positions to prevent dead code and ensure
TCO
optimization. Type checking is enhanced to validate argument types
against
function parameters.
This commit introduces optimizations for nested destructuring, allowing
tuples and records to be flattened and matched directly against function
arguments. This significantly improves performance by enabling more
constant folding and reducing intermediate allocations.
The changes include:
- Modifying the `Binder` to correctly count nested parameters.
- Enhancing `flatten_tuple` in the `Optimizer` to handle records and NOP
nodes.
- Updating `map_params_to_args` to recursively destructure nested
compound arguments.
- Adding integration tests to verify the correctness of tuple-to-tuple
and record-to-tuple destructuring optimizations.
The Analyzer has been refactored to decorate `TypedNode`s with their
purity and recursion status. This involves creating a new `AnalyzedNode`
type and a `NodeMetrics` struct to hold this information. The `Analyzer`
now returns an `AnalyzedNode` instead of a separate `Analysis` struct.
This change lays the groundwork for future optimizations and analysis
passes.
This commit introduces a new AST analysis pass that identifies function
purity and recursion. This information is then used by the optimizer and
specializer to make more informed decisions, particularly regarding
inlining.
The `Analyzer` struct and its associated `Analysis` struct are
responsible for traversing the AST and collecting this data.
Key changes include:
- A new `analyzer` module is added to `ast::compiler`.
- `Analyzer::analyze` performs a two-pass traversal to collect
global-to-lambda mappings and then analyze purity and recursion.
- The `Optimizer` and `Specializer` are updated to accept and utilize
the `Analysis` data.
- Recursion checks in `Optimizer` and `Specializer` are replaced with
checks against the pre-computed `Analysis.is_recursive` set.
- The `Environment` now stores and passes the `Analysis` results to the
compiler stages.
Move `Purity` enum definition from `optimizer.rs` to `types.rs` and
create a `NativeFunction` struct to hold the function and its purity.
Update `Value::Function` to store `Rc<NativeFunction>` and
`Value::make_function`
helper to simplify creation.
This change allows tracking the purity of native functions, which is
useful
for optimization and static analysis.
Introduces a `UsageInfo` struct to consolidate tracking of used and
assigned locals, globals, and identities.
Replaces multiple `HashSet` arguments in `collect_usage` with a single
`UsageInfo` struct.
Adds an `is_recursive` method to check for recursive calls within a
node, considering global indices, local slots, and identity.
Updates inlining logic to use `is_recursive` to prevent inlining of
recursive functions or closures. This includes:
- Checking for recursion when inlining a call to a lambda.
- Checking for recursion when inlining a global variable that is a
lambda.
- Checking for recursion when inlining a closure.
Updates `is_inlinable_value` to also check for closure recursion.
The purity of a lambda definition should always be `Pure`. The purity of
the lambda's body is only relevant when the lambda is called. This
change ensures that defining a lambda does not incorrectly mark the
parent scope as impure.
Refactor the optimizer to use a Purity enum instead of a boolean for
tracking function purity. This allows for a more granular representation
of purity:
- `Impure`: Functions with side effects.
- `SideEffectFree`: Functions without side effects but may not be
deterministic (e.g., `now()`, `random()`).
- `Pure`: Functions without side effects and are deterministic.
This change enhances the optimizer's ability to perform more aggressive
optimizations by accurately determining function purity.
The Optimizer now accepts a typed lambda registry, allowing it to
perform more aggressive inlining and beta-reduction on globally defined
functions. This is a significant step towards optimizing recursive and
globally defined lambdas more effectively.
The optimizer now uses a simple boolean flag (`optimization`) instead of
an `optimization_level` (u32). This simplifies the optimizer's logic and
makes it easier to enable or disable optimizations.
The command-line interface and internal testing have been updated to
reflect this change.
This commit updates the benchmark metadata in several example files. The
benchmark runs and repeat counts have been slightly adjusted due to
optimizations or minor variations in execution.
This commit introduces several improvements to the AST optimizer and
dead code elimination (DCE) logic:
- **Address Mapping:** The `Get` and `Set` operations now correctly map
local slots and global indices using the substitution map, ensuring
that remapped variables are handled properly.
- **Parameter Slot Mapping:** Parameters are now explicitly mapped to
their correct slots within the `Lambda` node, preventing potential
slot reassignments in the function body.
- **Pure Function Analysis:** The `is_pure` function has been refined to
accurately identify pure expressions, including calls to pure
functions and stateless closures.
- **Dead Code Elimination:** DCE logic in `BoundKind::Block` has been
enhanced to remove unused local definitions and pure, non-essential
expressions more effectively. Global DCE is also improved.
- **Constant Folding:** `try_fold_pure` is now more robust in folding
pure function calls with constant arguments, reducing redundant
computations.
- **Beta Reduction:** `try_beta_reduce` has been updated to avoid
reducing if the body contains `DefLocal`, ensuring correctness.
- **Global Purity Tracking:** A `global_purity` map is introduced to
track the purity of global values, enabling better optimization of
global accesses.
- **Inlinable Value Check:** The `is_inlinable_value` check now
correctly identifies stateless closures for inlining.
- **Optimization Level:** The default `optimization_level` is set to 2
to enable more aggressive optimizations.
This commit introduces purity inference to the compiler's optimizer.
This allows for more aggressive constant folding and dead code
elimination by tracking which functions and global variables
are free of side effects.
Key changes include:
- Added `global_purity` field to `Optimizer` and `Environment`.
- Modified `Optimizer::is_pure` to recursively determine if an
AST node represents a pure computation.
- Introduced `Optimizer::try_fold_pure` to replace the old
`try_fold_intrinsic`, enabling folding of pure function calls
with constant arguments.
- Updated `Environment::register_native` and
`Environment::register_constant`
to optionally record purity.
- Added purity flags to several built-in functions in `core.rs` and
`datetime.rs`.
- A new example `optimizer_purity.myc` demonstrates the new feature.
Introduce global inlining by allowing the Optimizer to access the
environment's global values. This enables replacing global variable
accesses with their constant values when appropriate.
Additionally, implement Dead Code Elimination (DCE) for global
definitions. A global definition can be removed if it was only used for
inlining within the current script and has no side effects.
Update the Optimizer struct to hold an optional reference to the global
values and modify the `Optimizer::new` constructor to accept this. The
`Environment::specialize` and `Environment::compile_script` methods are
updated to pass the global values to the optimizer.
The `SubstitutionMap` is also updated to track script-local global
substitutions and used global indices, supporting both global inlining
and global DCE.
Integrates dead code elimination (DCE) into the optimizer.
This phase removes expressions that have no side effects and are not the
result of the block.
Also includes several improvements to the existing optimization passes:
- DCE now correctly handles assignments to variables that are never used
or captured.
- Explicitly tracks captured slots in the `SubstitutionMap` to prevent
premature inlining.
- Introduces a `is_side_effect_free` helper function for more robust
purity checks.
- Updates dependencies to include `insta` for snapshot testing.
Add a new example file `examples/def_local_inlining.myc` to demonstrate
and test local inlining.
Refine the optimizer to handle local inlining more robustly by:
- Passing a `SubstitutionMap` to `visit_node` to track local variable
substitutions.
- Enabling constant propagation for local variables by checking
`sub.locals` in `BoundKind::Get`.
- Updating `BoundKind::DefLocal` and `BoundKind::Set` to maintain the
substitution map for local variables.
- Adjusting `try_beta_reduce` to use the optimizer's `visit_node` with
the substitution map for inlining.
- Re-indexing upvalues correctly when inlining captured variables into
lambdas.
The `is_tail` field on the `BoundKind::Call` node was an intermediate
representation for tail-call optimization and is no longer needed as a
distinct field. The TCO pass now embeds this information directly into
the `RuntimeMetadata` of the `ExecNode`, which is the VM's internal AST.
This simplifies the `BoundKind::Call` structure and removes redundant
information.
The `TailCall` variant has been merged into `Call` by adding an
`is_tail` boolean field. This simplifies the AST by reducing the number
of node variants and makes it easier to handle tail call optimization.
The `tco.rs` module is responsible for setting this flag when a call
occurs in tail position.
Implement PartialEq for BoundKind to allow for structural equality
checks during optimization. This enables the optimizer to terminate
early when a node no longer changes.
Also, implement PartialEq for Value to facilitate comparisons between
different Value variants.
The optimizer has been refactored to separate its phases and introduce
more aggressive collapsing capabilities.
Phase 2 (Cracking) now focuses on stateless transformations, making it
easier to reason about and potentially parallelize.
Phase 2.5 (Aggressive Collapsing) has been introduced, enabling
optimizations like:
- Beta-reduction for lambda literals.
- Cracking and inlining for constant closures.
- Folding intrinsics for constant arithmetic.
- Short-circuiting conditional expressions.
These changes aim to improve performance by reducing redundant
computations and code bloat. The maximum number of optimization passes
has also been increased to 5 to allow for more complex transformations.
Introduces a new optimizer pass that can "crack" closures, allowing for
more aggressive specialization. It also enables inlining of upvalues
that point to immutable global variables. This removes overhead for
higher-order functions and currying when arguments are statically
resolvable.
This commit renames `Map` to `Record` and updates all related AST nodes,
binders, type checkers, and runtime values to reflect this change. This
is a semantic change to better align with common programming language
terminology.
Replaces the use of `BTreeMap` and `HashMap` for maps and records with
`Vec<(Keyword, Value)>` and `Vec<(Keyword, StaticType)>`. This
simplifies the data structure and allows for ordered iteration, which is
important for serialization and comparison.
Also updates the `unpack` function in `vm.rs` to handle records as well
as lists when destructuring.
Introduce `BoundKind::Parameter` to represent function parameters.
Modify `Binder` to correctly handle parameters within lambda
definitions, ensuring they are only defined within function scopes.
Update `LambdaCollector` to register the body of lambdas as templates
for global definitions.
Adjust `Dumper` to accurately represent lambda parameters.
Update `Specializer` and `TCO` to handle the new `BoundKind::Parameter`.
Refactor `Call` and `TailCall` to use a single `args` node, often a
tuple.
Adjust type signatures in RTL to use `StaticType::Tuple` for function
parameters.
The type inference for collections (tuples, vectors, matrices, and
records) has been significantly refactored.
This includes:
- Introducing distinct `StaticType` variants for `Tuple`, `Vector`, and
`Matrix`.
- Implementing more robust type checking for these collections, allowing
for homogeneous and heterogeneous structures.
- Enhancing the `is_assignable_from` method to handle type compatibility
between these collection types.
- Updating `Value::static_type()` to correctly infer the types of
literal collections.
- Improving the type inference for map literals to create `Record`
types.
- Adding comprehensive unit tests for tuple, vector, matrix, and record
type inference.
Replaces nested `if let` statements with sequential `if let` bindings,
improving readability. This change is applied to `Dumper::log_bound`,
`LambdaCollector::visit_bound`, `Environment::call_object_method`, and
`Environment::call_lambda`.
This commit updates benchmark values in several example files to reflect
minor performance variations. It also includes adjustments to
integration
and unit tests to align with recent changes in the type checking and VM
logic, specifically concerning closures and TCO handling.
correctly initialize the `TypeChecker` with argument types during
macro expansion.
- Refactor `Specializer::compile` to perform type checking with provided
arguments before specialization and to correctly extract the return
type.
- Enhance the `Dumper` to introspect and display specialized closure
bodies.
- Update `LambdaCollector` to use `BoundNode` consistently.
- Modify `TypeChecker` to accept and inject specialized argument types
for lambdas.
Introduces `LambdaCollector` to gather lambda functions and populate the
function registry. This enables the `Specializer` to work with
user-defined functions.
The `Environment` struct is updated to manage the `function_registry`
and `monomorph_cache`, which are essential for the specialization
process.
The `link` method in `Environment` now incorporates lambda collection
and node specialization before applying TCO optimization. This ensures
that lambdas are properly processed and specialized for potential
performance gains.
The `Specializer`'s `new` constructor has been modified to accept and
initialize the `MonoCache` through an `Rc<RefCell<MonoCache>>`. This
allows the cache to be shared across different specialized functions.
Also includes minor refactoring and type adjustments in `specializer.rs`
for better clarity and consistency.
The specialization logic has been refactored to include a call to
`rtl_lookup` for host functions. This allows the specializer to
recognize and optimize calls to built-in runtime functions by directly
returning their specialized values.
Additionally, the `_rtl_lookup` field in `Specializer` has been renamed
to `rtl_lookup` for clarity. The `visit_call` method has also been
renamed to `specialize_call_logic` to better reflect its purpose, and it
now correctly handles `TailCall` nodes by preserving them.
The `Get`, `DefLocal`, and `DefGlobal` bound kinds now store the symbol
name associated with the variable. This information is useful for
debugging and provides more context in the compiled output.
Update the testing section to clarify when warnings should be eliminated
and tests run.
Add the new `specializer` module to the compiler's public API.
Add the `type_registry` module to the `rtl` module's public API.
The `unless` macro's expansion was updated to use `...` (NOP) instead of
`void`. This change also involved removing the `void` keyword from the
parser, as it is no longer used.
Boolean literals `true` and `false` are now parsed as identifiers,
matching the behavior of other constants like `NaN`. This change aligns
the AST representation of boolean literals with their runtime constants.