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.
Introduce a `--trace` flag to the AST tool that enables the
TracingObserver.
This allows users to see the detailed execution steps of their scripts.
Also, refactor the main and AST tool to better handle tracing and
performance
statistics.
Introduce `ValueList` type alias for `Rc<Vec<Value>>` to reduce
boilerplate and improve readability.
Add `as_slice()` method to `Value` to provide a unified way to access
the underlying values of Tuples and Records.
Update `flatten_value` and `unpack` in `VM` to utilize the new
`as_slice()` method, simplifying logic and improving consistency.
Extracts the logic for running example tests into a new function
`run_functional_tests` in `src/utils/tester.rs`. This function is then
called from `src/integration_test.rs` to simplify the test setup and
improve code organization. The changes also adjust the benchmark
thresholds slightly.
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.
This commit updates the benchmark timings and repeat counts for various
example files. The `tester.rs` script has been modified to:
- Correctly parse and use the `Benchmark-Repeat` directive.
- Implement an adaptive sampling mechanism for more accurate median
calculation, especially for long-running benchmarks.
- Improve the logic for updating and inserting benchmark and repeat
lines in example files.
- Handle potential compilation and runtime errors during benchmark
execution.
The `Value::List` and `Value::Record` variants have been consolidated
into a single `Value::Tuple` variant. This new variant uses a
`TupleData` struct to store values and an optional `Rc<Vec<Keyword>>`
for keys, allowing it to represent both ordered lists/tuples and
key-value records.
This change simplifies the internal representation and improves
performance by allowing schema sharing for records. It also includes
updates to the compiler, runtime, and tests to reflect the new
structure.
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.
This commit introduces a new module `ast::rtl` to house the standard
runtime library functions for the language. It includes implementations
for arithmetic operators (+, -, *, /, //, %), logical operators (and,
or, xor, not), bitwise shifts (<<, >>), and comparison operators (=, <>,
<, >, <=, >=).
Additionally, a `date` function for parsing datetime strings has been
added. This enhances the language's capability to handle basic
mathematical and logical operations.
Add RTL functions for arithmetic and comparisons
This commit introduces the runtime library (RTL) functions for basic
arithmetic operations, logical/bitwise operations, and comparisons. It
also includes a `date` function for parsing datetime strings.
Integration tests have been updated to cover these new functionalities.
This commit introduces the `DateTime` type to the language, enabling
users to work with dates and times. It includes:
- A new `DateTime` variant in the `Value` and `StaticType` enums.
- A `date` function for parsing date strings into `DateTime` values.
- Overloads for `+` and `-` operators to support `DateTime` arithmetic.
- Comparison operators (`>`, `<`) for `DateTime` values.
- Corresponding unit tests for `DateTime` operations.
- Updates to the `gemini.md` documentation to reflect the new
functionality.
Add DateTime type and operations
Introduce a new `DateTime` type to the language, allowing for date and
time manipulation. This includes:
* Parsing dates and datetimes from strings.
* Performing arithmetic operations (addition and subtraction) between
`DateTime` and `Int` (representing milliseconds) and between two
`DateTime` values (resulting in an `Int` duration).
* Enabling comparison operations (`>`, `<`) between `DateTime` values.
This commit refactors the type checking logic for functions and
operators to improve type safety and expressiveness.
Key changes include:
- Introduced `StaticType::FunctionOverloads` to represent functions with
multiple possible signatures, enabling better handling of operator
overloading.
- Updated the `TypeChecker` to correctly infer function types and
resolve calls with overloaded functions.
- Modified the `Environment` to register standard library functions with
specific `FunctionOverloads` signatures, replacing the previous
`StaticType::Any` approach.
- Enhanced the `StaticType::resolve_call` method to intelligently match
argument types against expected parameters, including handling of
overloaded functions.
- Added new tests to verify the correctness of type inference for lambda
returns and operator overloading.
Introduces the `TypeChecker` struct and its associated `TypeContext` for
performing static type analysis on the abstract syntax tree.
This change includes:
- A new `type_checker.rs` module.
- Integration of `TypeChecker` into the `Environment::compile` method.
- Updates to `Environment` to manage global types.
- Renaming `bound_nodes.rs`'s `BoundNode` to `TypedNode` to reflect its
type-checked nature.
- Refinements in binder and macro expansion to accommodate type
information.
The Binder has been refactored to simplify its internal types and reduce
redundancy.
Key changes include:
- Removed StaticType from LocalInfo, as it's not strictly needed for the
binder.
- Simplified `define` in CompilerScope to not take a StaticType.
- Removed StaticType from upvalues in FunctionCompiler.
- Adjusted global mappings to store only the index, not the type.
- Updated BoundKind to use a generic type parameter `T` for metadata,
defaulting to `()` for untyped bound nodes.
- Introduced `BoundNode` and `TypedNode` type aliases for clarity.
- Minor adjustments to dumper and VM to accommodate the new generic
BoundKind.
The UI layout has been significantly refactored to use
`egui::TopBottomPanel` and `egui::SidePanel` for a more organized
structure. A new menu bar has been introduced with File, Build, and
Tools menus, along with a toolbar for quick access to common actions.
Keyboard shortcuts have been updated and consolidated:
- `Shift + Enter` now compiles the code and is accessible from anywhere
in the app.
- `Ctrl + S` saves the current file.
The `Save As` functionality has been moved into a dedicated group within
the bottom panel and is now triggered by a button in the File menu. The
output log and trace logs now use `egui::ScrollArea::both` for better
scrolling experience.
Minor adjustments to default panel heights and scroll area behaviors
have been made for improved usability.
This commit replaces the `lazy_static` crate with `std::sync::OnceLock`.
This removes an external dependency and utilizes a standard library
feature for lazy initialization.
Additionally, a benchmark regression test has been added to
`src/ast/tester.rs`.
Introduces a `Symbol` struct to represent identifiers, incorporating
macro hygiene context. Updates various parts of the AST compiler and
environment to use `Symbol` instead of raw `Rc<str>` for identifiers,
improving robustness for macro expansions.
Also includes:
- Adds a new example `macro_hygiene.myc`.
- Updates `.gitignore`.
- Refactors `MacroExpander` to use `ExpansionState` for cleaner template
expansion.
- Adjusts `VM::eval_observed` to ensure `after_eval` is called
correctly.
- Resets `Environment` for each test run and compilation/dumping in
`main.rs`.