Skip to content

Commit 2dba944

Browse files
authored
Compiler (#137)
* feat(compiler): scaffold Hyperbee.ExpressionCompiler library with stub API Adds the new Hyperbee.ExpressionCompiler class library (net8.0;net9.0;net10.0) with HyperbeeCompiler static API (Compile, TryCompile, CompileWithFallback) and HyperbeeCompilerExtensions. All methods are stubs; TryCompile returns null so CompileWithFallback falls back to Expression.Compile(). Updates solution file. * feat(compiler): scaffold Hyperbee.Expressions.Compiler library with stub API * test(compiler): add CompilerType enum, ExpressionVerifier, and binary/constant/conditional tests * test(compiler): seed FEC issue regression tests * bench(compiler): add compilation and execution benchmarks * chore(compiler): remove incorrectly named Hyperbee.ExpressionCompiler project * chore(compiler): restore version.json accidentally removed in cleanup * feat(compiler): add IR instruction set (IROp, IRInstruction, IRBuilder) Add the foundational IR types for the Hyperbee expression compiler: - IROp enum with all opcodes for Phase 1+ compilation - IRInstruction readonly struct for cache-friendly instruction storage - IRBuilder with instruction stream, operand table, locals, and labels - LocalInfo and LabelInfo readonly record structs for metadata * feat(compiler): add ExpressionLowerer for Phase 1 expression types Single-pass expression tree visitor that lowers to flat IR instructions. Supports constants, parameters, binary/unary ops, method calls, conditionals (ternary + IfThen), member access, new objects, blocks, assignment, default, type conversions, short-circuit logic, and operator overloads. Unsupported types throw NotSupportedException. * feat(compiler): add ILEmissionPass for IR-to-CIL generation 1:1 mapping from IR opcodes to CIL via ILGenerator. Handles all Phase 1 operations including arithmetic, checked variants, comparisons, conversions, method calls, branching, fields, boxing/unboxing, and non-embeddable constants loaded from a closure object[] array. Uses short-form opcodes for locals 0-3 and args 0-3. * feat(compiler): wire HyperbeeCompiler pipeline and pass all Phase 1 tests Complete the compiler pipeline: expression tree pre-scan for non-embeddable constants, lowering to IR, IL emission via DynamicMethod, and delegate creation. TryCompile now returns a working delegate for all Phase 1 patterns. All 81 compiler tests and 6 issue regression tests pass across net8.0, net9.0, and net10.0. * feat(compiler): add Phase 2 exception handling with try/catch/finally/throw/goto/label - Add Leave IROp for exiting try/catch blocks - Implement TryCatch/Throw/Rethrow/Goto/Label lowering in ExpressionLowerer - Add StackSpillPass to convert Branch to Leave at exception boundaries - Update ILEmissionPass with BeginTry/BeginCatch/BeginFinally/EndTryCatch/Throw/Rethrow/Leave emission - Wire StackSpillPass into HyperbeeCompiler pipeline between lowering and emission - Extend NeedsConstantsArray scanner for TryExpression/GotoExpression/LabelExpression - Add 62 exception handling tests (basic try/catch, typed catch, catch variable, try/catch/finally, throw/rethrow, goto/label, void try, nested try, value results) - Add 4 HyperbeeCompiler.Compile native tests for Pattern 1 and Pattern 2 in IssueTests - All 143 Compiler.Tests + 10 IssueTests pass on net8.0/net9.0/net10.0 * refactor(compiler): extract Compile pipeline into discrete SRP methods LowerToIR, RunPasses, EmitDelegate each own a single responsibility. Removes stale rationale comments and dead NeedsConstantsArray wrapper. * refactor(compiler): rename RunPasses to TransformIR * feat(compiler): add Phase 3 closure support with captured variables and nested lambdas Implement support for Expression.Lambda and Expression.Invoke with captured variables using the StrongBox<T> pattern: - Add CaptureScanner to detect variables captured by nested lambdas - Extend ExpressionLowerer with Lambda/Invoke handling and StrongBox access for captured variables (load via LoadField, store via StoreField) - Rewrite inner lambdas to accept StrongBox<T> parameters and compile with System compiler, enabling shared mutable state between outer and inner delegates - Update HyperbeeCompiler to integrate capture scanning before lowering - Add ScanForNonEmbeddableConstants support for Lambda/Invoke nodes - Add ClosureTests with 12 test cases covering no-capture, single capture, multiple captures, mutable captures, and mixed scenarios - Add native HyperbeeCompiler.Compile tests for Pattern 3 in FecKnownIssues (no longer needs CompileWithFallback) All 167 compiler tests + 12 issue tests pass across net8.0/net9.0/net10.0. * feat(compiler): add Phase 4 completeness — loop, switch, array, collection init, coalesce, quote Add support for all remaining expression types to make HyperbeeCompiler a drop-in replacement for Expression.Compile(): Lowering (ExpressionLowerer): - Loop expressions with break/continue labels - Switch expressions with int/string cases, multiple test values, custom comparison - NewArrayInit and NewArrayBounds (single and multi-dimensional) - ArrayIndex (binary), ArrayLength (unary), Index (indexer property or array) - ListInit (List, Dictionary collection initializers) - MemberInit (property and field assignment bindings) - Coalesce (null coalescing for reference types and nullable value types) - TypeEqual (exact type match via GetType() == typeof(T)) - Quote (expression tree as data) - Power (Math.Pow) - Unbox (unbox.any) - DebugInfo (no-op) IL emission (ILEmissionPass): - NewArray (newarr), LoadElement/StoreElement (typed ldelem/stelem variants) - LoadArrayLength (ldlen + conv.i4), LoadAddress (ldloca) Also fixes void conditional handling in full ternary path to pop non-void results when the conditional type is void. 60 new tests across 6 test files, all passing on net8.0/net9.0/net10.0. * feat(compiler): add Phase 5 optimization — PeepholePass, allocation reduction, benchmark updates Add PeepholePass with 7 peephole patterns: StoreLocal/LoadLocal to Dup/StoreLocal, dead load elimination (LoadConst/Pop, LoadNull/Pop, LoadLocal/Pop), identity Box/UnboxAny removal, Dup/Pop elimination, and branch-to-fallthrough removal. Reduce allocations: pre-size IRBuilder lists and ExpressionLowerer dictionaries, optimize BuildConstantsMapping from O(operands*instructions) to O(operands+instructions) using a HashSet pre-scan. Update benchmarks to use HyperbeeCompiler.Compile directly (no fallback needed) and add Loop and Switch tier benchmarks. Results show Hyperbee within 1.3-1.8x of FEC compilation speed and 8-30x faster than System compiler. * fix(compiler): remove CompileWithFallback masking, fix 30 IR lowering bugs CompilerType.Hyperbee was using CompileWithFallback which silently fell back to the System compiler on any failure, masking all bugs. Changed to use HyperbeeCompiler.Compile directly. Fixed lowering bugs exposed by removing fallback: - LowerDefault: removed broken InitObj+StoreLocal pattern, use zero-initialized local - LowerConditional: non-void ternary stores branch results to temp local - LowerAndAlso/OrElse: replaced Dup across labels with result-local pattern - LowerCoalesce: rewrote both nullable and reference paths with temp locals - Added ExpressionType.Increment/Decrement to unary handler - NewExpression without constructor now handles value-type default - LowerAssign now supports IndexExpression (array/indexer assignment) - IRValidator: moved LoadArrayLength from push to neutral category Also includes allocation reduction (lazy dictionaries, reduced capacities, StackSpillPass fast-exit), nullable warning fixes, new Phase 9 tests (559 total), and FEC IssueTests patterns 4-10. * fix(compiler): Pattern 8 native, switch result-local, increment/decrement tests - Pattern 8 (box/unbox in conditional) now uses HyperbeeCompiler.Compile natively after LowerConditional fix - LowerSwitch: non-void switch uses result-local pattern so stack is empty at labels, fixing string switch without explicit comparison - Added PostIncrementAssign, PreIncrementAssign, PostDecrementAssign tests - Added string switch without explicit comparison test * feat(compiler): Phase 6 — nullable lifted ops, exception filters, RuntimeVariables - Add lifted nullable binary operations (arithmetic + comparisons) via LowerLiftedBinary, LowerLiftedComparison, LowerLiftedArithmetic - Add lifted nullable unary operations via LowerLiftedUnary - Add Nullable<T> ↔ T conversions and enum underlying-type conversions in LowerConvert (fixes TypeConversionTests for Hyperbee compiler) - Add exception filter support: BeginFilter / BeginFilteredCatch IR ops, IL emission, DeadCodePass sentinel, IRValidator stack depth - Add RuntimeVariables support: RuntimeVariablesHelper, CaptureScanner FindRuntimeVariablesCaptures, LowerRuntimeVariables with StrongBox array - Fix TypeIs to use ldtoken + Type.GetTypeFromHandle (embeddable in CompileToMethod) - Add OnesComplement to supported unary ops - Improve closure binding: BuildClosureBinder for non-invoked captured lambdas - Remove obsolete IROp entries: LoadClosureVar, StoreClosureVar, CreateDelegate - Add README.md with architecture overview and benchmark comparison table - Add FecKnownIssues patterns 11-20 (AddAssign, TypeAs, closures, filter, etc.) - Add test files: BitwiseTests, CompileToMethodTests, DynamicExpressionTests, RuntimeVariablesTests (679 tests passing across net8/net9/net10) * fix(compiler): document FEC crash on Not(bool?) as known failure FEC generates incorrect IL for Not(Nullable<bool>): calling the compiled delegate with any argument causes AccessViolationException (crashes the test host process). The bug exists because FEC does not null-guard the lifted Not operation — it attempts to dereference the nullable value without checking HasValue first. - NullableTests.Not_NullableBool(Fast): guard before delegate invocation with Assert.Fail, documenting the bug rather than crashing the host - FecKnownIssues.Pattern21: add native Hyperbee verification test with explanation of why the FEC variant cannot be safely tested Effect: 808 tests total, 807 pass, 1 documented FEC failure (no crash). Previously: 648 tests appeared to pass, then host crashed (160 tests never ran due to the fatal AccessViolationException mid-run). * feat(compiler): Phase 7 — test suite expansion, FEC policy enforcement, IR fixes Compiler fixes: - Add unsigned checked IR opcodes (AddCheckedUn, SubCheckedUn, MulCheckedUn, ConvertCheckedUn) to IROp, IRValidator, ILEmissionPass, and ExpressionLowerer - Fix float/double comparisons to use IsUnsignedOrFloat (clt.un/cgt.un for unordered NaN semantics) - Fix nullable Power lowering to emit null-propagation guard - Fix ~30 additional expression lowering correctness bugs Test infrastructure: - Remove FEC→System fallback from CompileFast — FEC failures now surface as test failures - FecKnownIssues.cs rewritten: only FEC-specific _FecBug and CompileWithFallback tests (no redundant _HyperbeeNative tests); added Pattern23 (ulong LessThan) and Pattern25 (ConvertChecked ulong→long) as verifiable _FecBug tests - Policy: System fails → remove test; FEC fails → suppress + FecKnownIssues link; Hyperbee fails → fix - Remove two invalid all-compiler-crash patterns (NewArrayInit_NullableIntArray, ConvertChecked_NullableIntToByte_Overflow) New test files (6): - BlockTests.cs, ControlFlowTests.cs, ConvertCheckedTests.cs - LambdaTests.cs, NullableArithmeticTests.cs, NullableBitwiseTests.cs Expanded test files (11): - ArrayTests, BinaryTests, CollectionInitTests, ComparisonTests, ConditionalTests - ExceptionHandlingTests, LoopTests, NullableTests, SwitchTests, TypeConversionTests, UnaryTests Result: 1,559 total test instances — 1,551 passed, 8 skipped (all FEC-only), 0 failed * feat(compiler): Phase 8 — expand test suite to 2,426 instances, fix compiler bugs Expand HyperbeeCompiler test suite from ~1,766 to 2,426 instances (0 failures): Compiler fixes: - Add RightShiftUn opcode (shr.un) for unsigned logical right shift; uint/ulong RightShift now correctly emits shr.un instead of sign-extending shr - Fix IRValidator false positive: track expected stack depth per label from branch sites so conditional sub-expressions inside arithmetic no longer fail stack-depth validation - Add lifted IsFalse/IsTrue support in LowerLiftedUnary for bool? operands New test coverage: - ComparisonTests: NaN comparisons (all 6 ops × float/double), infinity comparisons, float basic comparisons, decimal comparisons (+20 methods) - ArrayTests: 2D/3D array bounds creation, 2D read/write, zero-length arrays, nullable array, bool/long/string arrays, out-of-bounds throws (+11 methods) - BoundaryValueTests: float NaN propagation, infinity arithmetic, int boundary wrapping, float/double divide by zero (+10 methods) - UnaryTests: fix IsFalse/IsTrue nullable semantics (bool? → bool?), fix Negate_SByte (widening pattern), FEC suppress for PostIncrementAssign_Double (+fixes) - ExceptionHandlingTests: fix 3 tests with try/catch type mismatch (void vs value) - Multiple type-complete arithmetic/comparison tests across session * fix(compiler): fix null Nullable<T> constant crash, update benchmarks and README - Fix AccessViolationException in LowerConstant: Constant(null, typeof(int?)) was emitting ldnull, but stelem for a value type expects a struct on the stack. CLR zeroes locals on declaration, so a fresh temp local already holds default(Nullable<T>) — emit LoadLocal directly instead of LoadNull. - Re-enable NewArrayInit_NullableIntArray_AccessElements test (3 DataRows). - Update README compilation benchmarks to latest run (9–34x vs System, 1.11–1.47x vs FEC); add execution benchmarks table; document CompileToMethod API. * revert(expressions): remove MoveNextCompiler hook, restore clean AsyncStateMachineBuilder Reverts premature integration of HEC into hyperbee.expressions before the compiler was proven correct on state-machine patterns. MoveNextCompiler option removed from ExpressionRuntimeOptions; compiler project reference removed from expressions test project. * feat(compiler): add CompileToInstanceMethod + state-machine pattern tests - CompileToInstanceMethodTests: covers field read/write, non-embeddable constant rejection, and the StateMachineCompiler sentinel - StateMachinePatternTests: 12 tests proving HEC compiles every pattern produced by the async state machine lowerer — Switch dispatch tables, instance field read/write, IfThen+Return (suspend pattern), ref parameter calls, TryCatch wrapping, and multi-state MoveNext shapes — all verified against System compiler * feat(compiler): Milestone 1 — pluggable coroutine builder interfaces Introduce ICoroutineDelegateBuilder and ICoroutineImplementationBuilder as the abstraction layer for async state machine compilation, replacing the previous IEntryPointGenerator / IStateMachineGenerator names. "Coroutine" is the CS term for the suspend/resume pattern underlying both async/await and yield-return — not tied to state machines and safe for future .NET runtime-native async (e.g. .NET 11+). Changes: - ICoroutineBuilder.cs: defines ICoroutineDelegateBuilder (public) and ICoroutineImplementationBuilder (internal, reserved for M3) - AsyncStateMachineBuilder.cs: DefaultCoroutineDelegateBuilder replaces SystemEntryPointGenerator; raw-lambda embedding preserved for default, Constant(delegate) used for custom builders - ExpressionRuntimeOptions.cs: EntryPointGenerator → DelegateBuilder - HyperbeeCoroutineDelegateBuilder.cs: HEC implementation (was HecEntryPointGenerator) - CompilerDiagnostics.cs + IRFormatter.cs: diagnostic infrastructure - nestedCompiler wired in ExpressionLowerer for recursive HEC compilation - BlockAsyncHecTests.cs: integration tests (3 pass, 8 known HEC bugs) All Hyperbee.Expressions.Tests pass (669/3). No regressions. * refactor(compiler): rename AsyncInterpreterTaskBuilder to AsyncTaskMethodBuilderBox Replaces the misleadingly named AsyncInterpreterTaskBuilder<TResult> with AsyncTaskMethodBuilderBox<TResult>, which clearly communicates its purpose: a typed heap box around the AsyncTaskMethodBuilder<TResult> struct, required because expression tree MemberExpression access copies struct fields, making direct struct mutation impossible without a class wrapper. Adds XML doc comments explaining the struct-copy problem, the box pattern, and why StrongBox<T> alone would not suffice. * feat(compiler): Milestone 2 — HEC compiles all BlockAsync patterns, test suite 2547/21 Compiler fixes: - HyperbeeCompiler: ScanForNonEmbeddableConstants now returns true for extension nodes so AsyncBlockExpression reduction is handled correctly (SingleAwait, VoidResult) - ILEmissionPass: short branch auto-upgraded to long branch when offset exceeds ±127 bytes (fixes ConditionalAwait illegal one-byte branch) - ExpressionLowerer: struct field method calls emit Constrained prefix + LoadFieldAddress for TaskAwaiter<T> fields (fixes stack underflow in SequentialAwaits/TryCatch) Test reorganization: - BlockAsync*Tests moved from Expressions/ to Integration/ — these are pipeline integration tests, not HEC expression pattern tests - StateMachinePatternTests deleted; patterns redistributed: - Ref/out parameter tests added to MethodCallTests (AwaitOnCompleted pattern) - Switch dispatch-table tests added to SwitchTests (state-dispatch pattern) Test expansion: - MethodCallTests: +5 ref/out parameter tests - SwitchTests: +3 switch-as-dispatch-table tests (null default, Goto-bodied cases) - CompileToInstanceMethodTests: 7 → 17 tests - RuntimeVariablesTests: 5 → 13 tests Results: 2547 passed, 21 skipped, 0 failed (net9.0) * feat(compiler): Milestone 3 — ambient CoroutineBuilderContext, IExpressionCompiler, remove DelegateBuilder from options - Add CoroutineBuilderContext (AsyncLocal per-compilation + static process-wide default) with Exchange(), SetScope(), SetDefault() — compiler choice never passes through BlockAsync options - Add IExpressionCompiler interface + SystemExpressionCompiler in Hyperbee.Expressions - Add HyperbeeExpressionCompiler (DI-injectable adapter) in Hyperbee.Expressions.Compiler - HyperbeeCompiler.Compile() sets ambient via Exchange() in try/finally; adds UseAsDefault()/ClearDefault() - AsyncStateMachineBuilder reads CoroutineBuilderContext.Current directly; removes DefaultCoroutineDelegateBuilder - Remove ExpressionRuntimeOptions.DelegateBuilder — options describe behavior only, not compiler choice - Simplify AsyncBlockExpression.Reduce(); remove ResolvedOptions() - Integration tests: remove HecOptions() from all BlockAsync tests; IRCapture test uses SetScope() - Add BlockAsyncContextTests covering ambient and process-wide default paths * refactor(tests): move FEC Pattern 28 to FecKnownIssues, remove always-Inconclusive test - Add FecKnownIssues.Pattern28: Return(label, Assign(...)) inside async-lowered TryCatch generates invalid IL in FEC (incomplete error 1007 detection; FEC issue #495) - Update BlockAsyncTryCatchTests: standardize Fast suppress message to reference Pattern28 - Remove CompileFast_ShouldReturnNull_ForReturnGotoFromTryCatchWithAssign from CompilerCompatibilityTests — always-Inconclusive placeholder, now documented in FecKnownIssues - Solution skips: 23 total (2 Expressions.Tests + 21 Compiler.Tests), all FEC suppressions * fix(compiler): Phase 5 IL optimization — eliminate merge-point locals, fix void-assign and ldelema bugs Remove unnecessary merge-point result locals from conditional, logical, and coalesce lowering by leaving values on the evaluation stack at merge points (valid in CIL when stack depth is consistent across all paths). Saves 3–5 instructions and 1–2 locals per occurrence. Void-lambda with bare Assign body: set _discardResult=true to suppress the Dup that was incorrectly leaving a value on the stack before Ret. Void block ending in Assign: extend suppressAssign logic so the final Pop is not emitted after an Assign (the value was never pushed in the first place). ldelema fix: Assign to a field of a struct stored in an array element requires ldelema (managed pointer) not ldelem (value copy). Add IROp.LoadElementAddress, emit Ldelema in ILEmissionPass, handle in IRValidator/IRFormatter. New EmitInstanceForFieldAssign helper routes value-type instances through EmitLoadAddress, which now handles ArrayIndex-on-value-type via ldelema. Fix also covers mutating instance method calls on struct array elements. Add test coverage: void-lambda assign patterns (5 tests), struct-array-element field and property assignment patterns (5 tests) — all passing for System, FEC, and HEC. * refactor(compiler): remove dead code, fix Exchange perf, add benchmark delta columns - Remove IROp.Switch (never emitted), IRValidator.ValidateAlways (never called) - Remove scope subsystem: IROp.BeginScope/EndScope, IRBuilder.EnterScope/ExitScope, LocalInfo.ScopeDepth — entire subsystem was no-op (no IL emitted, no pass consumed it) - Remove CompilerDiagnostics.ILCapture (declared but never wired or invoked) - Gate CoroutineBuilderContext.Exchange on ScanForNonEmbeddableConstants to avoid unnecessary AsyncLocal writes for simple expressions; pass pre-computed result into LowerToIR to eliminate redundant traversal — restores 9-34x speedup vs SEC - Add BenchmarkExpressions (shared definitions), BenchmarkColumns (RatioToColumn for vs-SEC and vs-FEC delta columns), expand ExecutionBenchmarks to all 6 tiers - Update README with current benchmark numbers and expanded execution table * docs: comprehensive just-the-docs documentation structure Add full documentation for Hyperbee.Expressions, Hyperbee.Expressions.Compiler, and Hyperbee.Expressions.Lab following the established just-the-docs pattern used across sibling projects (hyperbee.pipeline, hyperbee.json, hyperbee.xs). Structure (27 markdown files across 4 sections): - index.md — landing page with package table, quick start, expression type index - expressions/ — parent + 12 children (async-block, enumerable-block, await, yield, for, foreach, while, using, debug, string-format, inject, configuration-value) - configuration/ — parent + 3 children (runtime-options, module-providers, dependency-injection) - compiler/ — parent + 4 children (overview, api, diagnostics, performance) - lab/ — parent + 3 children (fetch, json, map-reduce) Infrastructure: - _config.yml — adds baseurl, url, footer_content, nav_external_links - _includes/nav_footer_custom.html — branded footer - docs.projitems — updated to include all 27 pages * docs: replace non-ASCII characters with ASCII equivalents Replace em/en dashes, arrows, box-drawing, micro sign, and other non-ASCII Unicode with ASCII-only substitutions so all docs .md files are plain ASCII. * Remove non-ascii chars from docs * feat: Add IR opcodes for fused comparison branches and CIL switch * feat: Support new IR opcodes in IR pipeline diagnostics and validation * feat: Implement CIL emission for fused branch and switch instructions * perf: Implement peephole optimizations for fused comparison branches and redundant assignments * fix: Optimize redundant IROp.Leave instructions in exception blocks * feat: Enhance SwitchExpression lowering with CIL jump table optimization * test: Introduce ILComparisonTool for compiler output diagnostics * docs: Update README with minor formatting and text improvements
1 parent d4ce17a commit 2dba944

130 files changed

Lines changed: 38138 additions & 364 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Hyperbee.ExpressionCompiler.md

Lines changed: 3487 additions & 0 deletions
Large diffs are not rendered by default.

Hyperbee.Expressions.slnx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,12 @@
2222
</Folder>
2323
<Folder Name="/Solution Tests/">
2424
<Project Path="test/Hyperbee.Expressions.Benchmark/Hyperbee.Expressions.Benchmark.csproj" />
25+
<Project Path="test/Hyperbee.Expressions.Compiler.Benchmarks/Hyperbee.Expressions.Compiler.Benchmarks.csproj" />
26+
<Project Path="test/Hyperbee.Expressions.Compiler.IssueTests/Hyperbee.Expressions.Compiler.IssueTests.csproj" />
27+
<Project Path="test/Hyperbee.Expressions.Compiler.Tests/Hyperbee.Expressions.Compiler.Tests.csproj" />
2528
<Project Path="test/Hyperbee.Expressions.Tests/Hyperbee.Expressions.Tests.csproj" />
2629
</Folder>
30+
<Project Path="src/Hyperbee.Expressions.Compiler/Hyperbee.Expressions.Compiler.csproj" />
2731
<Project Path="src/Hyperbee.Expressions.Lab/Hyperbee.Expressions.Lab.csproj" />
2832
<Project Path="src/Hyperbee.Expressions/Hyperbee.Expressions.csproj" />
2933
</Solution>

docs/_config.yml

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,24 @@
1-
title: Hyperbee.Expressions
2-
description: Documentation for Hyperbee Expressions.
1+
title: Hyperbee Expressions
2+
description: Documentation for Hyperbee Expressions — async, yield, loop, and compiler extensions for .NET expression trees.
33
remote_theme: pmarsceill/just-the-docs
4+
baseurl: "/hyperbee.expressions/"
5+
url: "https://stillpoint-software.github.io"
46

5-
# Optional configuration
6-
search_enabled: true
77
aux_links:
88
"GitHub Repository":
99
- "//github.com/Stillpoint-Software/hyperbee.expressions"
10+
11+
footer_content: |
12+
<div>
13+
<span>&copy; <span id="copyright-year"></span> <a href='https://www.stillpointsoftware.net/'>Stillpoint Software</a>.</span>
14+
<script>
15+
document.getElementById("copyright-year").textContent = new Date().getFullYear();
16+
</script>
17+
</div>
18+
19+
search_enabled: true
20+
21+
nav_external_links:
22+
- title: Stillpoint Software
23+
url: https://www.stillpointsoftware.net/
24+
opens_in_new_tab: true
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<footer class="site-footer">
2+
Hyperbee Expressions Docs
3+
</footer>

docs/compiler/api.md

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
---
2+
layout: default
3+
title: API Reference
4+
parent: Compiler
5+
nav_order: 2
6+
---
7+
8+
# API Reference
9+
10+
---
11+
12+
## HyperbeeCompiler
13+
14+
`HyperbeeCompiler` is a static class -- the primary entry point for all compilation operations.
15+
16+
```csharp
17+
using Hyperbee.Expressions.Compiler;
18+
```
19+
20+
### Compile
21+
22+
```csharp
23+
static TDelegate Compile<TDelegate>( Expression<TDelegate> lambda, CompilerDiagnostics? diagnostics = null )
24+
where TDelegate : Delegate
25+
26+
static Delegate Compile( LambdaExpression lambda, CompilerDiagnostics? diagnostics = null )
27+
```
28+
29+
Compiles the expression tree and returns a delegate. Throws on unsupported patterns.
30+
31+
```csharp
32+
var fn = HyperbeeCompiler.Compile<Func<int, int>>(
33+
x => Expression.Add( x, Expression.Constant( 1 ) )
34+
);
35+
Console.WriteLine( fn( 41 ) ); // 42
36+
```
37+
38+
### TryCompile
39+
40+
```csharp
41+
static TDelegate? TryCompile<TDelegate>( Expression<TDelegate> lambda ) where TDelegate : Delegate
42+
static Delegate? TryCompile( LambdaExpression lambda )
43+
```
44+
45+
Compiles and returns `null` on failure instead of throwing.
46+
47+
```csharp
48+
var fn = HyperbeeCompiler.TryCompile( lambda );
49+
if ( fn is null )
50+
Console.WriteLine( "Compilation failed" );
51+
```
52+
53+
### CompileWithFallback
54+
55+
```csharp
56+
static TDelegate CompileWithFallback<TDelegate>( Expression<TDelegate> lambda ) where TDelegate : Delegate
57+
static Delegate CompileWithFallback( LambdaExpression lambda )
58+
```
59+
60+
Attempts HEC compilation; falls back to `lambda.Compile()` (System compiler) on failure.
61+
Use during migration when some expressions may not yet be supported.
62+
63+
```csharp
64+
var fn = HyperbeeCompiler.CompileWithFallback( lambda );
65+
```
66+
67+
### UseAsDefault / ClearDefault
68+
69+
```csharp
70+
static ICoroutineDelegateBuilder? UseAsDefault()
71+
static ICoroutineDelegateBuilder? ClearDefault()
72+
```
73+
74+
Sets or clears HEC as the process-wide default builder for `AsyncBlockExpression` reductions.
75+
Call at application startup to make HEC compile all `BlockAsync` state machines, even when the
76+
outer lambda is compiled by another compiler.
77+
78+
```csharp
79+
// In Program.cs or AssemblyInitialize
80+
HyperbeeCompiler.UseAsDefault();
81+
82+
// In test teardown
83+
HyperbeeCompiler.ClearDefault();
84+
```
85+
86+
### CompileToMethod
87+
88+
```csharp
89+
static void CompileToMethod( LambdaExpression lambda, MethodBuilder method )
90+
static bool TryCompileToMethod( LambdaExpression lambda, MethodBuilder method )
91+
```
92+
93+
Emits the expression tree directly into a `MethodBuilder`. The method must be `static` and its
94+
parameter signature must match the lambda.
95+
96+
Non-embeddable constants (object references, delegates, nested lambdas) are not permitted --
97+
all constants must be embeddable IL values (primitives, `Type` tokens, `null`).
98+
99+
```csharp
100+
var typeBuilder = moduleBuilder.DefineType( "MyType" );
101+
var methodBuilder = typeBuilder.DefineMethod(
102+
"Add",
103+
MethodAttributes.Public | MethodAttributes.Static,
104+
typeof(int), [typeof(int), typeof(int)]
105+
);
106+
107+
var a = Parameter( typeof(int), "a" );
108+
var b = Parameter( typeof(int), "b" );
109+
var lambda = Lambda( Add( a, b ), a, b );
110+
111+
HyperbeeCompiler.CompileToMethod( lambda, methodBuilder );
112+
113+
var type = typeBuilder.CreateType();
114+
var method = type.GetMethod("Add")!;
115+
Console.WriteLine( method.Invoke( null, [10, 32] ) ); // 42
116+
```
117+
118+
### CompileToInstanceMethod
119+
120+
```csharp
121+
static void CompileToInstanceMethod( LambdaExpression lambda, MethodBuilder method )
122+
static bool TryCompileToInstanceMethod( LambdaExpression lambda, MethodBuilder method )
123+
```
124+
125+
Like `CompileToMethod` but without the static-method requirement. For instance methods on value
126+
types (e.g., `IAsyncStateMachine.MoveNext()`), the lambda's first parameter maps to IL `arg.0`
127+
(the implicit `this` managed pointer).
128+
129+
---
130+
131+
## HyperbeeExpressionCompiler
132+
133+
`HyperbeeExpressionCompiler` implements `IExpressionCompiler` as a DI-friendly singleton wrapper
134+
around `HyperbeeCompiler`.
135+
136+
```csharp
137+
public sealed class HyperbeeExpressionCompiler : IExpressionCompiler
138+
{
139+
public static readonly IExpressionCompiler Instance;
140+
141+
public Delegate Compile( LambdaExpression lambda );
142+
public TDelegate Compile<TDelegate>( Expression<TDelegate> lambda ) where TDelegate : Delegate;
143+
public Delegate? TryCompile( LambdaExpression lambda );
144+
public TDelegate? TryCompile<TDelegate>( Expression<TDelegate> lambda ) where TDelegate : Delegate;
145+
146+
public static ICoroutineDelegateBuilder? UseAsDefault();
147+
}
148+
```
149+
150+
### DI Registration
151+
152+
```csharp
153+
// Register HEC as the IExpressionCompiler implementation
154+
services.AddSingleton<IExpressionCompiler>( HyperbeeExpressionCompiler.Instance );
155+
```
156+
157+
### With UseAsDefault
158+
159+
`HyperbeeExpressionCompiler.UseAsDefault()` is a convenience passthrough to
160+
`HyperbeeCompiler.UseAsDefault()`. Call it at startup to make HEC compile all `BlockAsync`
161+
state machines, even in expressions compiled by the System compiler.
162+
163+
```csharp
164+
// In Program.cs
165+
HyperbeeExpressionCompiler.UseAsDefault();
166+
```
167+
168+
---
169+
170+
## HyperbeeCompilerExtensions
171+
172+
Extension methods for `LambdaExpression`:
173+
174+
```csharp
175+
// In namespace Hyperbee.Expressions.Compiler
176+
public static TDelegate CompileHyperbee<TDelegate>( this Expression<TDelegate> lambda )
177+
where TDelegate : Delegate
178+
```
179+
180+
```csharp
181+
using Hyperbee.Expressions.Compiler;
182+
183+
var fn = lambda.CompileHyperbee();
184+
```
185+
186+
---
187+
188+
## Notes
189+
190+
- All `HyperbeeCompiler` methods are thread-safe -- there is no shared mutable state.
191+
- `CompileToMethod` and `CompileToInstanceMethod` do not support closures. Use `Compile()` for
192+
expressions with captured variables or non-embeddable constants.
193+
- See [Diagnostics](diagnostics.md) for `CompilerDiagnostics` and IR capture.
194+
- See [Performance](performance.md) for benchmark comparisons.

docs/compiler/compiler.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
layout: default
3+
title: Compiler
4+
has_children: true
5+
nav_order: 4
6+
---
7+
8+
# Compiler
9+
10+
`Hyperbee.Expressions.Compiler` is a high-performance, IR-based compiler for .NET expression trees.
11+
It is a drop-in replacement for `Expression.Compile()` that emits IL directly, bypassing the System
12+
expression interpreter.
13+
14+
---
15+
16+
## Installation
17+
18+
```
19+
dotnet add package Hyperbee.Expressions.Compiler
20+
```
21+
22+
## Quick Start
23+
24+
```csharp
25+
using Hyperbee.Expressions.Compiler;
26+
27+
// Direct replacement for lambda.Compile()
28+
var fn = HyperbeeCompiler.Compile( lambda );
29+
30+
// Or use the IExpressionCompiler interface for DI
31+
services.AddSingleton<IExpressionCompiler>( HyperbeeExpressionCompiler.Instance );
32+
```
33+
34+
---
35+
36+
## Topics
37+
38+
| Topic | Description |
39+
|-------|-------------|
40+
| [Overview](overview.md) | Architecture, compilation pipeline, optimization passes |
41+
| [API Reference](api.md) | `HyperbeeCompiler`, `HyperbeeExpressionCompiler`, `CompileToMethod` |
42+
| [Diagnostics](diagnostics.md) | IR capture, `CompilerDiagnostics`, `IRFormatter` |
43+
| [Performance](performance.md) | Benchmarks vs System compiler and FastExpressionCompiler |
44+
45+
---
46+
47+
## Highlights
48+
49+
- **9-34x faster** compilation than the System compiler
50+
- **1.16-1.54x** of FastExpressionCompiler (FEC) compilation time
51+
- **Up to 50% fewer** allocations than the System compiler
52+
- Supports all expression patterns -- including those FEC does not support
53+
- Fully compatible with `AsyncBlockExpression` state machines via ambient context
54+
- `IExpressionCompiler` interface for DI-friendly injection

0 commit comments

Comments
 (0)