157 lines
4.0 KiB
Markdown
157 lines
4.0 KiB
Markdown
|
|
# 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
|
|||
|
|
```python
|
|||
|
|
# tools/wasm_remove_export.py
|
|||
|
|
def remove_exports(input_path, output_path):
|
|||
|
|
# Strips section_id=7 (Export) from WASM binary
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. Calculate Correct Index
|
|||
|
|
```python
|
|||
|
|
# 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
|
|||
|
|
```python
|
|||
|
|
# 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`:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 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`:
|
|||
|
|
```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)
|
|||
|
|
|
|||
|
|
## Related Issues
|
|||
|
|
|
|||
|
|
- **Phase 15.8 Week 3**: Function call tests failing
|
|||
|
|
- **call命令引数解決バグ**: Related to safepoint insertion
|
|||
|
|
- **PHI debug assertion**: Unrelated but also fixed this week
|
|||
|
|
|
|||
|
|
## Verification
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 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制限を完全克服!関数呼び出し完全動作✅
|