Files
hakorune/docs/private/roadmap/phases/phase-15.8/wasm-export-fix.md

4.0 KiB
Raw Blame History

WASM Export Section Fix (Phase 15.8 Week 3)

Date: 2025-10-01 Status: Fixed Branch: wasm-development

Problem

llvmlite generates WASM binaries with incorrect export section:

  • Export name: Usually correct (Main.main)
  • Export index: WRONG - points to import function instead of module function
  • Root cause: llvmlite doesn't account for import functions when calculating export indices

Example

Imports:
  0: __linear_memory (memory)
  1: ny_check_safepoint (func)
  2: nyash.string.to_i8p_h (func)

Module functions:
  2: add_simple
  3: Main.main  ← Correct index

llvmlite export:
  Main.main → index 1  ❌ Points to ny_check_safepoint (import!)

Correct export:
  Main.main → index 3  ✅ Points to actual Main.main function

Impact

  • Error: Cannot convert undefined to BigInt
  • Cause: Wrong function signature (import expects parameters, entry expects 0)
  • Result: WASM execution fails immediately

Solution

3-step post-processing pipeline:

1. Remove Incorrect Export

# tools/wasm_remove_export.py
def remove_exports(input_path, output_path):
    # Strips section_id=7 (Export) from WASM binary

2. Calculate Correct Index

# tools/wasm_calc_export_index.py
def calc_export_index(mir_json_path, entry_name="Main.main"):
    # func_index = NUM_IMPORTS + function_position_in_json
    # NUM_IMPORTS = 2 (ny_check_safepoint, nyash.string.to_i8p_h)

3. Add Correct Export

# src/llvm_py/tools/wasm_add_export.py
def add_export(wasm_path, output_path, func_name, func_index):
    # Inserts Export section with correct index

Integration

Updated tools/build_wasm.sh:

# Step 2.5: Fix export section
python3 tools/wasm_remove_export.py input.wasm temp_noexp.wasm
EXPORT_INDEX=$(python3 tools/wasm_calc_export_index.py input.json "Main.main")
python3 src/llvm_py/tools/wasm_add_export.py temp_noexp.wasm output.wasm "Main.main" "$EXPORT_INDEX"

Results

Before fix:

Error: Cannot convert undefined to BigInt
Export: Main.main → index 1 (wrong function)

After fix:

🚀 Calling Main.main()...
[DEBUG] ny_check_safepoint called
[DEBUG] to_i8p_h(42) type=bigint
✅ Main.main() returned: 42

Test Case

/tmp/test_call_minimal.json:

{
  "functions": [
    {
      "name": "add_simple",
      "params": [{"name": "a", "reg": 0}, {"name": "b", "reg": 1}],
      "blocks": [{"id": 0, "instructions": [
        {"op": "binop", "operation": "+", "lhs": 0, "rhs": 1, "dst": 2},
        {"op": "ret", "value": 2}
      ]}]
    },
    {
      "name": "Main.main",
      "params": [],
      "blocks": [{"id": 0, "instructions": [
        {"op": "const", "dst": 0, "value": {"type": "i64", "value": 10}},
        {"op": "const", "dst": 1, "value": {"type": "i64", "value": 32}},
        {"op": "call", "func": "add_simple", "args": [0, 1], "dst": 2},
        {"op": "ret", "value": 2}
      ]}]
    }
  ]
}

Files Modified

  • tools/build_wasm.sh - Pipeline integration
  • tools/wasm_remove_export.py - NEW (52 lines)
  • tools/wasm_calc_export_index.py - NEW (38 lines)
  • src/llvm_py/tools/wasm_add_export.py - Already existed, used correctly

Future Work

  • Handle multiple entry points (not just Main.main)
  • Auto-detect NUM_IMPORTS from WASM binary instead of hardcoding
  • Upstream fix to llvmlite (if possible)
  • Phase 15.8 Week 3: Function call tests failing
  • call命令引数解決バグ: Related to safepoint insertion
  • PHI debug assertion: Unrelated but also fixed this week

Verification

# Build WASM
./tools/build_wasm.sh test.json -o test.wasm

# Verify export
python3 /tmp/parse_export.py test.wasm
# Output: Export 0: 'Main.main' (kind=func, index=3) ✅

# Run WASM
node src/llvm_py/tools/wasm_runner.js test.wasm
# Output: ✅ Main.main() returned: 42

結論: llvmliteのWASM export制限を完全克服関数呼び出し完全動作