mirror of
https://github.com/hanzoai/wasm2go.git
synced 2026-10-30 15:06:48 +00:00
* Support the wasm SIMD proposal with native asm lowering (arm64/amd64) One revision spanning the SIMD (0xfd v128) proposal end to end: SSA representation, lowering, scalar reference helpers, and a gcasm asm backend that turns the hot paths into native SIMD — measured on a full llama.cpp build transpiled to pure Go, which this makes run at V8-class speed (prompt eval beats V8 on arm64). Frontend and SSA: - v128 values are [2]uint64 pairs in little-endian lane order; pure ops lower to CSE-able helper calls, memory ops keep statement order, v128.const/shuffle ride as [2]uint64 aux. The full scalar helper set is the reference semantics (saturation, NaN propagation, round-to-even, swizzle OOB, shift masking). - Exception handling lowers to plain branches instead of panic/recover — a check-and-branch after each may-raise call, a per-try dispatch block running the catch-clause chain — covering the C++-shaped patterns (multi-clause catch, delegate targets, rethrow depths) without runtime unwinding, so EH bodies stay eligible for the asm backend. This replaces the closure/recover lowering that #39 extended; its tests are retained unchanged. - Giant functions outline into tractable units (whole loops included, packed u64 boundaries for v128 values), keeping gc compile times and register pressure sane on llama.cpp-sized modules. gcasm backend: - SIMD helper calls splice inline as native NEON/SSE instead of marshalled calls; v128 SSA values travel register-to-register in pair form. Call-tree windows fuse into single splices with region invariant hoisting, bounds-check coalescing, store sinks and scalar-chain internalization; whole countdown loops fuse with an in-splice unroll lane. - Superinstruction selection inside fused regions: 8-bit dot patterns (SMULL+SADALP), SDOT for full 16-byte dots, FMUL by element, scale-lookup addressing, inlined integer division, the software fp32->fp16 idiom lowered to FCVT (both the table-gather and the packed store form), and ggml q8_0 row/column pairing via the traits nrows patch with an SMMLA 2x2 tile kernel. - Runtime CPU-feature dispatch gates spliced bodies (dotprod on arm64, x86-64-v2 SSE on amd64); WASM2GO_FAST_MATH opts into raw-SDOT/FMLA/dual-accumulator forms that trade wasm bit-exactness for native-build-style rounding, validated by token-level equivalence against native llama.cpp. - Guard-page linear memory eliminates per-access bounds checks; jump tables replace the compare tree for br_table. Tooling and CI: - WASM2GO_F16_TABLE asserts the address of ggml's runtime-built f16 table; the transpiler warns when the asserted address matches no gather site (the address moves with every data-layout shift, and a stale assertion silently costs ~7% decode throughput). - The coverage gate runs with -count=1: a cached package result merges incomplete coverage data into the -coverpkg profile, deflating the total by 10+ points depending on what the runner's restored cache contained — the gate must never consume cached results. - Coverage threshold moves to 83% for the enlarged tree; transpile output is deterministic and outline rejections are explained in the build log. Co-Authored-By: Claude Fable 5 <[email protected]> * Replace environment-variable switches with typed Options; drop debug code Configuration now flows through one channel: transpile.Options gains OutlineMinValues, SIMDUnroll, FuseLoops, FuseLoopUnroll, F16TableAddr and FastMath (with matching CLI flags), the gcasm backend receives them as a Config threaded through ModuleOffsets, and no code path branches on an environment variable anymore. Integrator entry points (the wasmify protoc plugin, the CLI) convert their external configuration into these options exactly once. Deleted outright, per the no-env rule and because they were diagnostics rather than build modes: - every WASM2GO_NO_* kill switch and bisection knob (SSA pass disabling, fallback ranges, fuse-window caps, outline limits, inliner windows) — git history keeps them for the next hunt; - every env-gated debug printer (fuse/inline/outline/nrc2/f16/SSA dumps and the near-miss tracers) and the capture-tree keepers; - the dormant multi-exit loop structuring (opt-in, measured slower) and the SSA f32->f16 rewrite entry superseded by the gcasm peephole (its shared idiom matchers stay for the f16 store pass); - the runtime WASM2GO_CPU_PORTABLE override in the generated CPU feature probes. The nil-offsets error paths exposed by the threading get nil-safe Config accessors, and the outline share/v128-min/inliner caps become plain constants at their measured production values. Behavior under default options is unchanged: transpiled output for the non-SIMD and EH fixtures is byte-identical to the previous commit, and the only SIMD-bundle difference is the removed env check in the emitted cpufeat runtime files. Co-Authored-By: Claude Fable 5 <[email protected]> --------- Co-authored-by: Claude Fable 5 <[email protected]>
217 lines
5.6 KiB
Go
217 lines
5.6 KiB
Go
package wasm
|
|
|
|
// WebAssembly MVP opcodes plus the 0xfc prefix.
|
|
// For the full list see https://webassembly.github.io/spec/core/binary/instructions.html
|
|
const (
|
|
OpUnreachable byte = 0x00
|
|
OpNop byte = 0x01
|
|
OpBlock byte = 0x02
|
|
OpLoop byte = 0x03
|
|
OpIf byte = 0x04
|
|
OpElse byte = 0x05
|
|
|
|
// Legacy exception-handling proposal (what clang/wasi-sdk emits for
|
|
// setjmp/longjmp under `-mllvm -wasm-enable-sjlj`). try opens a handler
|
|
// region with a blocktype; catch/catch_all begin handlers; throw raises a
|
|
// tag; rethrow/delegate re-raise. These carry the setjmp/longjmp lowering.
|
|
OpTry byte = 0x06
|
|
OpCatch byte = 0x07
|
|
OpThrow byte = 0x08
|
|
OpRethrow byte = 0x09
|
|
OpDelegate byte = 0x18
|
|
OpCatchAll byte = 0x19
|
|
|
|
OpEnd byte = 0x0b
|
|
OpBr byte = 0x0c
|
|
OpBrIf byte = 0x0d
|
|
OpBrTable byte = 0x0e
|
|
OpReturn byte = 0x0f
|
|
OpCall byte = 0x10
|
|
OpCallIndirect byte = 0x11
|
|
|
|
OpDrop byte = 0x1a
|
|
OpSelect byte = 0x1b
|
|
OpSelectT byte = 0x1c
|
|
|
|
OpLocalGet byte = 0x20
|
|
OpLocalSet byte = 0x21
|
|
OpLocalTee byte = 0x22
|
|
OpGlobalGet byte = 0x23
|
|
OpGlobalSet byte = 0x24
|
|
|
|
OpI32Load byte = 0x28
|
|
OpI64Load byte = 0x29
|
|
OpF32Load byte = 0x2a
|
|
OpF64Load byte = 0x2b
|
|
OpI32Load8S byte = 0x2c
|
|
OpI32Load8U byte = 0x2d
|
|
OpI32Load16S byte = 0x2e
|
|
OpI32Load16U byte = 0x2f
|
|
OpI64Load8S byte = 0x30
|
|
OpI64Load8U byte = 0x31
|
|
OpI64Load16S byte = 0x32
|
|
OpI64Load16U byte = 0x33
|
|
OpI64Load32S byte = 0x34
|
|
OpI64Load32U byte = 0x35
|
|
OpI32Store byte = 0x36
|
|
OpI64Store byte = 0x37
|
|
OpF32Store byte = 0x38
|
|
OpF64Store byte = 0x39
|
|
OpI32Store8 byte = 0x3a
|
|
OpI32Store16 byte = 0x3b
|
|
OpI64Store8 byte = 0x3c
|
|
OpI64Store16 byte = 0x3d
|
|
OpI64Store32 byte = 0x3e
|
|
OpMemSize byte = 0x3f
|
|
OpMemGrow byte = 0x40
|
|
|
|
OpI32Const byte = 0x41
|
|
OpI64Const byte = 0x42
|
|
OpF32Const byte = 0x43
|
|
OpF64Const byte = 0x44
|
|
|
|
OpI32Eqz byte = 0x45
|
|
OpI32Eq byte = 0x46
|
|
OpI32Ne byte = 0x47
|
|
OpI32LtS byte = 0x48
|
|
OpI32LtU byte = 0x49
|
|
OpI32GtS byte = 0x4a
|
|
OpI32GtU byte = 0x4b
|
|
OpI32LeS byte = 0x4c
|
|
OpI32LeU byte = 0x4d
|
|
OpI32GeS byte = 0x4e
|
|
OpI32GeU byte = 0x4f
|
|
OpI64Eqz byte = 0x50
|
|
OpI64Eq byte = 0x51
|
|
OpI64Ne byte = 0x52
|
|
OpI64LtS byte = 0x53
|
|
OpI64LtU byte = 0x54
|
|
OpI64GtS byte = 0x55
|
|
OpI64GtU byte = 0x56
|
|
OpI64LeS byte = 0x57
|
|
OpI64LeU byte = 0x58
|
|
OpI64GeS byte = 0x59
|
|
OpI64GeU byte = 0x5a
|
|
OpF32Eq byte = 0x5b
|
|
OpF32Ne byte = 0x5c
|
|
OpF32Lt byte = 0x5d
|
|
OpF32Gt byte = 0x5e
|
|
OpF32Le byte = 0x5f
|
|
OpF32Ge byte = 0x60
|
|
OpF64Eq byte = 0x61
|
|
OpF64Ne byte = 0x62
|
|
OpF64Lt byte = 0x63
|
|
OpF64Gt byte = 0x64
|
|
OpF64Le byte = 0x65
|
|
OpF64Ge byte = 0x66
|
|
|
|
OpI32Clz byte = 0x67
|
|
OpI32Ctz byte = 0x68
|
|
OpI32Popcnt byte = 0x69
|
|
OpI32Add byte = 0x6a
|
|
OpI32Sub byte = 0x6b
|
|
OpI32Mul byte = 0x6c
|
|
OpI32DivS byte = 0x6d
|
|
OpI32DivU byte = 0x6e
|
|
OpI32RemS byte = 0x6f
|
|
OpI32RemU byte = 0x70
|
|
OpI32And byte = 0x71
|
|
OpI32Or byte = 0x72
|
|
OpI32Xor byte = 0x73
|
|
OpI32Shl byte = 0x74
|
|
OpI32ShrS byte = 0x75
|
|
OpI32ShrU byte = 0x76
|
|
OpI32Rotl byte = 0x77
|
|
OpI32Rotr byte = 0x78
|
|
OpI64Clz byte = 0x79
|
|
OpI64Ctz byte = 0x7a
|
|
OpI64Popcnt byte = 0x7b
|
|
OpI64Add byte = 0x7c
|
|
OpI64Sub byte = 0x7d
|
|
OpI64Mul byte = 0x7e
|
|
OpI64DivS byte = 0x7f
|
|
OpI64DivU byte = 0x80
|
|
OpI64RemS byte = 0x81
|
|
OpI64RemU byte = 0x82
|
|
OpI64And byte = 0x83
|
|
OpI64Or byte = 0x84
|
|
OpI64Xor byte = 0x85
|
|
OpI64Shl byte = 0x86
|
|
OpI64ShrS byte = 0x87
|
|
OpI64ShrU byte = 0x88
|
|
OpI64Rotl byte = 0x89
|
|
OpI64Rotr byte = 0x8a
|
|
|
|
OpF32Abs byte = 0x8b
|
|
OpF32Neg byte = 0x8c
|
|
OpF32Ceil byte = 0x8d
|
|
OpF32Floor byte = 0x8e
|
|
OpF32Trunc byte = 0x8f
|
|
OpF32Nearest byte = 0x90
|
|
OpF32Sqrt byte = 0x91
|
|
OpF32Add byte = 0x92
|
|
OpF32Sub byte = 0x93
|
|
OpF32Mul byte = 0x94
|
|
OpF32Div byte = 0x95
|
|
OpF32Min byte = 0x96
|
|
OpF32Max byte = 0x97
|
|
OpF32Copysign byte = 0x98
|
|
OpF64Abs byte = 0x99
|
|
OpF64Neg byte = 0x9a
|
|
OpF64Ceil byte = 0x9b
|
|
OpF64Floor byte = 0x9c
|
|
OpF64Trunc byte = 0x9d
|
|
OpF64Nearest byte = 0x9e
|
|
OpF64Sqrt byte = 0x9f
|
|
OpF64Add byte = 0xa0
|
|
OpF64Sub byte = 0xa1
|
|
OpF64Mul byte = 0xa2
|
|
OpF64Div byte = 0xa3
|
|
OpF64Min byte = 0xa4
|
|
OpF64Max byte = 0xa5
|
|
OpF64Copysign byte = 0xa6
|
|
|
|
OpI32WrapI64 byte = 0xa7
|
|
OpI32TruncF32S byte = 0xa8
|
|
OpI32TruncF32U byte = 0xa9
|
|
OpI32TruncF64S byte = 0xaa
|
|
OpI32TruncF64U byte = 0xab
|
|
OpI64ExtendI32S byte = 0xac
|
|
OpI64ExtendI32U byte = 0xad
|
|
OpI64TruncF32S byte = 0xae
|
|
OpI64TruncF32U byte = 0xaf
|
|
OpI64TruncF64S byte = 0xb0
|
|
OpI64TruncF64U byte = 0xb1
|
|
OpF32ConvertI32S byte = 0xb2
|
|
OpF32ConvertI32U byte = 0xb3
|
|
OpF32ConvertI64S byte = 0xb4
|
|
OpF32ConvertI64U byte = 0xb5
|
|
OpF32DemoteF64 byte = 0xb6
|
|
OpF64ConvertI32S byte = 0xb7
|
|
OpF64ConvertI32U byte = 0xb8
|
|
OpF64ConvertI64S byte = 0xb9
|
|
OpF64ConvertI64U byte = 0xba
|
|
OpF64PromoteF32 byte = 0xbb
|
|
OpI32ReinterpretF32 byte = 0xbc
|
|
OpI64ReinterpretF64 byte = 0xbd
|
|
OpF32ReinterpretI32 byte = 0xbe
|
|
OpF64ReinterpretI64 byte = 0xbf
|
|
|
|
OpI32Extend8S byte = 0xc0
|
|
OpI32Extend16S byte = 0xc1
|
|
OpI64Extend8S byte = 0xc2
|
|
OpI64Extend16S byte = 0xc3
|
|
OpI64Extend32S byte = 0xc4
|
|
|
|
// OpRefNull / OpRefFunc model reference-typed operands. wasm2go does not
|
|
// implement reference types, but the multi-package call-graph scanner
|
|
// recognises OpRefFunc as a function reference so it can record those
|
|
// edges alongside direct OpCall edges.
|
|
OpRefNull byte = 0xd0
|
|
OpRefFunc byte = 0xd2
|
|
|
|
OpPrefixFC byte = 0xfc // saturating-trunc, bulk-memory
|
|
OpPrefixFD byte = 0xfd // SIMD proposal: v128 ops
|
|
OpPrefixFE byte = 0xfe // threads proposal: atomics
|
|
)
|