75 lines
2.7 KiB
Plaintext
75 lines
2.7 KiB
Plaintext
// boot_miscompile_bad.cm — Documents a boot compiler miscompilation bug.
|
|
//
|
|
// BUG SUMMARY:
|
|
// The boot compiler's optimizer (likely compress_slots, eliminate_moves,
|
|
// or infer_param_types) miscompiles a specific pattern when it appears
|
|
// inside streamline.cm. The pattern: an array-loaded value used as a
|
|
// dynamic index for another array store, inside a guarded block:
|
|
//
|
|
// sv = instr[j]
|
|
// if (is_number(sv) && sv >= 0 && sv < nr_slots) {
|
|
// last_ref[sv] = i // <-- miscompiled: sv reads wrong slot
|
|
// }
|
|
//
|
|
// The bug is CONTEXT-DEPENDENT on streamline.cm's exact function/closure
|
|
// structure. A standalone module with the same pattern does NOT trigger it.
|
|
// The boot optimizer's cross-function analysis (infer_param_types, type
|
|
// propagation, etc.) makes different decisions in the full streamline.cm
|
|
// context, leading to the miscompilation.
|
|
//
|
|
// SYMPTOMS:
|
|
// - 'log' is not defined (comparison error path fires on non-comparable values)
|
|
// - array index must be a number (store_dynamic with corrupted index)
|
|
// - Error line has NO reference to 'log' — the reference comes from the
|
|
// error-reporting code path of the < operator
|
|
// - Non-deterministic: different error messages on different runs
|
|
// - NOT a GC bug: persists with --heap 4GB
|
|
// - NOT slot overflow: function has only 85 raw slots
|
|
//
|
|
// TO REPRODUCE:
|
|
// In streamline.cm, replace the build_slot_liveness function body with
|
|
// this version (raw operand scanning instead of get_slot_refs):
|
|
//
|
|
// var build_slot_liveness = function(instructions, nr_slots) {
|
|
// var last_ref = array(nr_slots, -1)
|
|
// var n = length(instructions)
|
|
// var i = 0
|
|
// var j = 0
|
|
// var limit = 0
|
|
// var sv = 0
|
|
// var instr = null
|
|
//
|
|
// while (i < n) {
|
|
// instr = instructions[i]
|
|
// if (is_array(instr)) {
|
|
// j = 1
|
|
// limit = length(instr) - 2
|
|
// while (j < limit) {
|
|
// sv = instr[j]
|
|
// if (is_number(sv) && sv >= 0 && sv < nr_slots) {
|
|
// last_ref[sv] = i
|
|
// }
|
|
// j = j + 1
|
|
// }
|
|
// }
|
|
// i = i + 1
|
|
// }
|
|
// return last_ref
|
|
// }
|
|
//
|
|
// Then: rm -rf .cell/build && ./cell --dev vm_suite
|
|
//
|
|
// WORKAROUND:
|
|
// Use get_slot_refs(instr) to iterate only over known slot-reference
|
|
// positions. This produces different IR that the boot optimizer handles
|
|
// correctly, and is also more semantically correct.
|
|
//
|
|
// FIXING:
|
|
// To find the root cause, compare the boot-compiled bytecodes of
|
|
// build_slot_liveness (in the full streamline.cm context) vs the
|
|
// source-compiled bytecodes. Use disasm.ce with --optimized to see
|
|
// what the source compiler produces. The boot-compiled bytecodes
|
|
// would need a C-level MachCode dump to inspect.
|
|
|
|
return null
|