Major enhancements to LLVM code generation and type handling: 1. String Operations: - Added StringBox length fast-path (length/len methods) - Converts i8* to handle when needed for len_h call - Consistent handle-based string operations 2. Array/Map Fast-paths: - ArrayBox: get/set/push/length operations - MapBox: get/set/has/size with handle-based keys - Optimized paths for common collection operations 3. Field Access: - getField/setField implementation with handle conversion - Proper i64 handle to pointer conversions 4. NewBox Improvements: - StringBox/IntegerBox pass-through optimizations - Fallback to env.box.new when type_id unavailable - Support for dynamic box creation 5. Documentation: - Added ARCHITECTURE.md for overall design - Added EXTERNCALL.md for external call specs - Added LOWERING_LLVM.md for LLVM lowering rules - Added PLUGIN_ABI.md for plugin interface 6. Type System: - Added UserBox type registration in nyash_box.toml - Consistent handle (i64) representation across system Results: More robust LLVM code generation with proper type handling 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2.4 KiB
2.4 KiB
LLVM Lowering Rules (Phase 15)
This document describes the active LLVM lowering rules used in Phase 15. Only the LLVM path is authoritative at this time.
General
- Box values are represented as i64 handles when crossing the NyRT boundary.
- String operations prefer i8* fast paths (AOT helpers) when possible. Handle conversions are done only at explicit boundaries.
NewBox
- StringBox:
- When constructed from a constant string, lowering produces i8* via
nyash_string_newand keeps it as i8* (no immediate handle conversion). - Builder skips redundant birth calls for StringBox.
- When constructed from a constant string, lowering produces i8* via
- Other boxes:
- Minimal birth shims exist (e.g.,
nyash.box.birth_h,nyash.box.birth_i64) using Box type ids.
- Minimal birth shims exist (e.g.,
BoxCall: String.concat fast path
- If the receiver is annotated as String (or StringBox), lower to AOT helpers directly:
concat_ss(i8*, i8*) -> i8*concat_si(i8*, i64) -> i8*(right operand is a handle coerced to string by NyRT)concat_is(i64, i8*) -> i8*
- For non‑String receivers or plugin cases, fall back to plugin/by‑id paths as needed.
BinOp Add: String concatenation
- Primary path: AOT helpers selected by operand shapes at IR time:
i8* + i8* -> concat_ssi8* + i64 -> concat_sii64 + i8* -> concat_is
- Fallback policy: keep to the minimum. Do not add implicit conversions beyond the above without clear MIR type annotations. If mixed forms miscompile, fix MIR annotations first.
ExternCall selection (console/debug)
env.console.{log,warn,error}andenv.debug.traceinspect the argument at lowering time:- If argument is
i8*, call the C‑string variant:nyash.console.{log,warn,error}/nyash.debug.trace. - Otherwise convert to
i64and call the handle variant:nyash.console.{log,warn,error}_handle/nyash.debug.trace_handle.
- If argument is
- The result values are ignored or zeroed as appropriate (side‑effecting I/O).
Return/Result mapping
- For plugin/by‑id calls that return an i64 handle but the destination is annotated as pointer‑like (String/Box/Array/Future/Unknown), the handle is cast to an opaque pointer for SSA flow. Integers/Bools remain integers.
Backend Consistency Notes
- VM/Cranelift/JIT are not MIR14‑ready and may not follow these rules yet. LLVM behavior takes precedence; other backends will be aligned later.
- Any new fallback must be justified and scoped; wide catch‑alls are prohibited to prevent backend divergence.