fix compilation error
This commit is contained in:
20
CLAUDE.md
20
CLAUDE.md
@@ -179,6 +179,26 @@ When running locally with `./cell --dev`, these commands manage packages:
|
||||
|
||||
Local paths are symlinked into `.cell/packages/`. The build step compiles C files to `.cell/lib/<pkg>/<stem>.dylib`. C files in `src/` are support files linked into module dylibs, not standalone modules.
|
||||
|
||||
## Debugging Compiler Issues
|
||||
|
||||
When investigating bugs in compiled output (wrong values, missing operations, incorrect comparisons), **start from the optimizer down, not the VM up**. The compiler inspection tools will usually identify the problem faster than adding C-level tracing:
|
||||
|
||||
```
|
||||
./cell --dev streamline --types <file> # show inferred slot types — look for wrong types
|
||||
./cell --dev ir_report --events <file> # show every optimization applied and why
|
||||
./cell --dev ir_report --types <file> # show type inference results per function
|
||||
./cell --dev mcode --pretty <file> # show raw IR before optimization
|
||||
./cell --dev streamline --ir <file> # show human-readable optimized IR
|
||||
```
|
||||
|
||||
**Triage order:**
|
||||
1. `streamline --types` — are slot types correct? Wrong type inference causes wrong optimizations.
|
||||
2. `ir_report --events` — are type checks being incorrectly eliminated? Look for `known_type_eliminates_guard` on slots that shouldn't have known types.
|
||||
3. `mcode --pretty` — is the raw IR correct before optimization? If so, the bug is in streamline.
|
||||
4. Only dig into `source/mach.c` if the IR looks correct at all levels.
|
||||
|
||||
See `docs/compiler-tools.md` for the full tool reference and `docs/spec/streamline.md` for pass details.
|
||||
|
||||
## Testing
|
||||
|
||||
After any C runtime changes, run all three test suites before considering the work done:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
45466
boot/fold.cm.mcode
45466
boot/fold.cm.mcode
File diff suppressed because it is too large
Load Diff
57423
boot/mcode.cm.mcode
57423
boot/mcode.cm.mcode
File diff suppressed because it is too large
Load Diff
69093
boot/parse.cm.mcode
69093
boot/parse.cm.mcode
File diff suppressed because it is too large
Load Diff
45232
boot/streamline.cm.mcode
45232
boot/streamline.cm.mcode
File diff suppressed because it is too large
Load Diff
25219
boot/tokenize.cm.mcode
25219
boot/tokenize.cm.mcode
File diff suppressed because it is too large
Load Diff
@@ -62,12 +62,13 @@ See [Mcode IR](mcode.md) for the instruction format and complete instruction ref
|
||||
Optimizes the Mcode IR through a series of independent passes. Operates per-function:
|
||||
|
||||
1. **Backward type inference**: Infers parameter types from how they are used in typed operators (`add_int`, `store_index`, `load_field`, `push`, `pop`, etc.). Immutable `def` parameters keep their inferred type across label join points.
|
||||
2. **Type-check elimination**: When a slot's type is known, eliminates `is_<type>` + conditional jump pairs. Narrows `load_dynamic`/`store_dynamic` to typed variants.
|
||||
3. **Algebraic simplification**: Rewrites identity operations (add 0, multiply 1, divide 1) and folds same-slot comparisons.
|
||||
4. **Boolean simplification**: Fuses `not` + conditional jump into a single jump with inverted condition.
|
||||
5. **Move elimination**: Removes self-moves (`move a, a`).
|
||||
6. **Unreachable elimination**: Nops dead code after `return` until the next label.
|
||||
7. **Dead jump elimination**: Removes jumps to the immediately following label.
|
||||
2. **Write-type invariance**: Determines which local slots have a consistent write type across all instructions. Slots written by child closures (via `put`) are excluded (forced to unknown).
|
||||
3. **Type-check elimination**: When a slot's type is known, eliminates `is_<type>` + conditional jump pairs. Narrows `load_dynamic`/`store_dynamic` to typed variants.
|
||||
4. **Algebraic simplification**: Rewrites identity operations (add 0, multiply 1, divide 1) and folds same-slot comparisons.
|
||||
5. **Boolean simplification**: Fuses `not` + conditional jump into a single jump with inverted condition.
|
||||
6. **Move elimination**: Removes self-moves (`move a, a`).
|
||||
7. **Unreachable elimination**: Nops dead code after `return` until the next label.
|
||||
8. **Dead jump elimination**: Removes jumps to the immediately following label.
|
||||
|
||||
See [Streamline Optimizer](streamline.md) for detailed pass descriptions.
|
||||
|
||||
|
||||
@@ -95,7 +95,9 @@ Write type mapping:
|
||||
| `move`, `load_field`, `load_index`, `load_dynamic`, `pop`, `get` | T_UNKNOWN |
|
||||
| `invoke`, `tail_invoke` | T_UNKNOWN |
|
||||
|
||||
The result is a map of slot→type for slots where all writes agree on a single known type. Parameter slots (1..nr_args) and slot 0 are excluded.
|
||||
Before filtering, a pre-pass (`mark_closure_writes`) scans all inner functions for `put` instructions (closure variable writes). For each `put`, the pass traverses the parent chain to find the target ancestor function and marks the written slot as `closure_written`. Slots marked as closure-written are forced to T_UNKNOWN regardless of what the local write analysis infers, because the actual runtime write happens in a child function and can produce any type.
|
||||
|
||||
The result is a map of slot→type for slots where all writes agree on a single known type. Parameter slots (1..nr_args) and slot 0 are excluded. Closure-written slots are excluded (forced to unknown).
|
||||
|
||||
Common patterns this enables:
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@ function load_pipeline_module(name, env) {
|
||||
var boot_par = null
|
||||
var boot_fld = null
|
||||
var boot_mc = null
|
||||
var boot_sl = null
|
||||
var tok_result = null
|
||||
var ast = null
|
||||
var compiled = null
|
||||
@@ -105,6 +106,8 @@ function load_pipeline_module(name, env) {
|
||||
}
|
||||
ast = boot_fld(ast)
|
||||
compiled = boot_mc(ast)
|
||||
boot_sl = boot_load("streamline")
|
||||
compiled = boot_sl(compiled)
|
||||
mcode_json = json.encode(compiled)
|
||||
mach_blob = mach_compile_mcode_bin(name, mcode_json)
|
||||
if (cached) {
|
||||
|
||||
3
seed.ce
3
seed.ce
@@ -54,6 +54,7 @@ var compiled = null
|
||||
var optimized = null
|
||||
var mcode_json = null
|
||||
var out_path = null
|
||||
var build_dir = null
|
||||
|
||||
// Regenerate pipeline module seeds
|
||||
for (i = 0; i < length(pipeline_modules); i++) {
|
||||
@@ -103,7 +104,7 @@ if (fd.is_file(bootstrap_path)) {
|
||||
print('\nRegenerated ' + text(generated) + ' seed(s)')
|
||||
|
||||
if (clean) {
|
||||
var build_dir = shop.get_build_dir()
|
||||
build_dir = shop.get_build_dir()
|
||||
if (fd.is_dir(build_dir)) {
|
||||
print('Clearing build cache: ' + build_dir)
|
||||
os.system('rm -rf "' + build_dir + '"')
|
||||
|
||||
114
streamline.cm
114
streamline.cm
@@ -336,6 +336,7 @@ var streamline = function(ir, log) {
|
||||
var slot = 0
|
||||
var typ = null
|
||||
var rule = null
|
||||
var cw_keys = null
|
||||
|
||||
if (instructions == null) {
|
||||
return array(func.nr_slots)
|
||||
@@ -362,6 +363,19 @@ var streamline = function(ir, log) {
|
||||
i = i + 1
|
||||
}
|
||||
|
||||
// Closure-written slots can have any type at runtime — mark unknown
|
||||
if (func.closure_written != null) {
|
||||
cw_keys = array(func.closure_written)
|
||||
k = 0
|
||||
while (k < length(cw_keys)) {
|
||||
slot = number(cw_keys[k])
|
||||
if (slot >= 0 && slot < length(write_types)) {
|
||||
write_types[slot] = T_UNKNOWN
|
||||
}
|
||||
k = k + 1
|
||||
}
|
||||
}
|
||||
|
||||
// Filter to only slots with known (non-unknown) types
|
||||
k = 0
|
||||
while (k < length(write_types)) {
|
||||
@@ -1475,6 +1489,100 @@ var streamline = function(ir, log) {
|
||||
return null
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Pre-pass: mark closure-written slots
|
||||
// Scans child functions for 'put' instructions and annotates
|
||||
// the target ancestor func with closure_written[slot] = true.
|
||||
// =========================================================
|
||||
var mark_closure_writes = function(ir) {
|
||||
var functions = ir.functions != null ? ir.functions : []
|
||||
var fc = length(functions)
|
||||
var parent_of = array(fc, -1)
|
||||
var instrs = null
|
||||
var instr = null
|
||||
var fi = 0
|
||||
var i = 0
|
||||
var j = 0
|
||||
var level = 0
|
||||
var anc = 0
|
||||
var slot = 0
|
||||
var target = null
|
||||
|
||||
if (fc == 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Build parent_of map
|
||||
if (ir.main != null && ir.main.instructions != null) {
|
||||
instrs = ir.main.instructions
|
||||
i = 0
|
||||
while (i < length(instrs)) {
|
||||
instr = instrs[i]
|
||||
if (is_array(instr) && instr[0] == "function") {
|
||||
if (instr[2] >= 0 && instr[2] < fc) {
|
||||
parent_of[instr[2]] = fc
|
||||
}
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
fi = 0
|
||||
while (fi < fc) {
|
||||
instrs = functions[fi].instructions
|
||||
if (instrs != null) {
|
||||
i = 0
|
||||
while (i < length(instrs)) {
|
||||
instr = instrs[i]
|
||||
if (is_array(instr) && instr[0] == "function") {
|
||||
if (instr[2] >= 0 && instr[2] < fc) {
|
||||
parent_of[instr[2]] = fi
|
||||
}
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
fi = fi + 1
|
||||
}
|
||||
|
||||
// Scan for 'put' instructions and mark ancestor slots
|
||||
fi = 0
|
||||
while (fi < fc) {
|
||||
instrs = functions[fi].instructions
|
||||
if (instrs != null) {
|
||||
i = 0
|
||||
while (i < length(instrs)) {
|
||||
instr = instrs[i]
|
||||
if (is_array(instr) && instr[0] == "put") {
|
||||
slot = instr[2]
|
||||
level = instr[3]
|
||||
anc = fi
|
||||
j = 0
|
||||
while (j < level && anc >= 0) {
|
||||
anc = parent_of[anc]
|
||||
j = j + 1
|
||||
}
|
||||
if (anc >= 0) {
|
||||
if (anc == fc) {
|
||||
target = ir.main
|
||||
} else {
|
||||
target = functions[anc]
|
||||
}
|
||||
if (target != null) {
|
||||
if (target.closure_written == null) {
|
||||
target.closure_written = {}
|
||||
}
|
||||
target.closure_written[text(slot)] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
fi = fi + 1
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Compose all passes
|
||||
// =========================================================
|
||||
@@ -1530,6 +1638,12 @@ var streamline = function(ir, log) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Pre-pass: mark slots written by child closures via 'put' instructions.
|
||||
// Without this, infer_slot_write_types would assume those slots keep their
|
||||
// initial type (e.g. T_NULL from 'var x = null'), causing the type-check
|
||||
// eliminator to mis-optimize comparisons on closure-written variables.
|
||||
mark_closure_writes(ir)
|
||||
|
||||
// Process main function
|
||||
if (ir.main != null) {
|
||||
optimize_function(ir.main, log)
|
||||
|
||||
26
vm_suite.ce
26
vm_suite.ce
@@ -4284,6 +4284,32 @@ run("closure set and get", function() {
|
||||
assert_eq(o.get(), 42, "overwrite")
|
||||
})
|
||||
|
||||
run("closure write heap values visible to outer scope", function() {
|
||||
var a = null
|
||||
var b = null
|
||||
var c = null
|
||||
var d = null
|
||||
var e = null
|
||||
var f1 = function() { a = 42 }
|
||||
var f2 = function() { b = true }
|
||||
var f3 = function() { c = "hello" }
|
||||
var f4 = function() { d = {x: 1} }
|
||||
var f5 = function() { e = [1, 2] }
|
||||
f1()
|
||||
f2()
|
||||
f3()
|
||||
f4()
|
||||
f5()
|
||||
assert_eq(a, 42, "closure write number")
|
||||
assert_eq(b, true, "closure write boolean")
|
||||
assert_eq(c != null, true, "closure write text not null")
|
||||
assert_eq(c, "hello", "closure write text value")
|
||||
assert_eq(d != null, true, "closure write object not null")
|
||||
assert_eq(d.x, 1, "closure write object property")
|
||||
assert_eq(e != null, true, "closure write array not null")
|
||||
assert_eq(e[0], 1, "closure write array element")
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// STRING COMPARISON OPERATORS
|
||||
// ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user