diff --git a/internal/engine/wazevo/backend/backend_test.go b/internal/engine/wazevo/backend/backend_test.go index 592e20a8d7..0fe7d4be0a 100644 --- a/internal/engine/wazevo/backend/backend_test.go +++ b/internal/engine/wazevo/backend/backend_test.go @@ -1394,126 +1394,124 @@ L1 (SSA Block: blk0): L1 (SSA Block: blk0): mov x130?, x2 mov v131?.8b, v0.8b - orr w289?, wzr, #0x1 - madd w133?, w130?, w289?, wzr - orr w288?, wzr, #0x2 - madd w135?, w130?, w288?, wzr - orr w287?, wzr, #0x3 - madd w137?, w130?, w287?, wzr - orr w286?, wzr, #0x4 - madd w139?, w130?, w286?, wzr - movz w285?, #0x5, lsl 0 - madd w141?, w130?, w285?, wzr - orr w284?, wzr, #0x6 - madd w143?, w130?, w284?, wzr - orr w283?, wzr, #0x7 - madd w145?, w130?, w283?, wzr - orr w282?, wzr, #0x8 - madd w147?, w130?, w282?, wzr - movz w281?, #0x9, lsl 0 - madd w149?, w130?, w281?, wzr - movz w280?, #0xa, lsl 0 - madd w151?, w130?, w280?, wzr - movz w279?, #0xb, lsl 0 - madd w153?, w130?, w279?, wzr - orr w278?, wzr, #0xc - madd w155?, w130?, w278?, wzr - movz w277?, #0xd, lsl 0 - madd w157?, w130?, w277?, wzr - orr w276?, wzr, #0xe - madd w159?, w130?, w276?, wzr - orr w275?, wzr, #0xf - madd w161?, w130?, w275?, wzr - orr w274?, wzr, #0x10 - madd w163?, w130?, w274?, wzr - movz w273?, #0x11, lsl 0 - madd w165?, w130?, w273?, wzr - movz w272?, #0x12, lsl 0 - madd w167?, w130?, w272?, wzr - movz w271?, #0x13, lsl 0 - madd w169?, w130?, w271?, wzr - movz w270?, #0x14, lsl 0 - madd w171?, w130?, w270?, wzr - add w172?, w169?, w171? - add w173?, w167?, w172? - add w174?, w165?, w173? - add w175?, w163?, w174? - add w176?, w161?, w175? - add w177?, w159?, w176? - add w178?, w157?, w177? - add w179?, w155?, w178? - add w180?, w153?, w179? - add w181?, w151?, w180? - add w182?, w149?, w181? - add w183?, w147?, w182? - add w184?, w145?, w183? - add w185?, w143?, w184? - add w186?, w141?, w185? - add w187?, w139?, w186? - add w188?, w137?, w187? - add w189?, w135?, w188? - add w190?, w133?, w189? - ldr s269?, #8; b 8; data.f32 1.000000 - fmul s192?, s131?, s269? - ldr s268?, #8; b 8; data.f32 2.000000 - fmul s194?, s131?, s268? - ldr s267?, #8; b 8; data.f32 3.000000 - fmul s196?, s131?, s267? - ldr s266?, #8; b 8; data.f32 4.000000 - fmul s198?, s131?, s266? - ldr s265?, #8; b 8; data.f32 5.000000 - fmul s200?, s131?, s265? - ldr s264?, #8; b 8; data.f32 6.000000 - fmul s202?, s131?, s264? - ldr s263?, #8; b 8; data.f32 7.000000 - fmul s204?, s131?, s263? - ldr s262?, #8; b 8; data.f32 8.000000 - fmul s206?, s131?, s262? - ldr s261?, #8; b 8; data.f32 9.000000 - fmul s208?, s131?, s261? - ldr s260?, #8; b 8; data.f32 10.000000 - fmul s210?, s131?, s260? - ldr s259?, #8; b 8; data.f32 11.000000 - fmul s212?, s131?, s259? - ldr s258?, #8; b 8; data.f32 12.000000 - fmul s214?, s131?, s258? - ldr s257?, #8; b 8; data.f32 13.000000 - fmul s216?, s131?, s257? - ldr s256?, #8; b 8; data.f32 14.000000 - fmul s218?, s131?, s256? - ldr s255?, #8; b 8; data.f32 15.000000 - fmul s220?, s131?, s255? - ldr s254?, #8; b 8; data.f32 16.000000 - fmul s222?, s131?, s254? - ldr s253?, #8; b 8; data.f32 17.000000 - fmul s224?, s131?, s253? - ldr s252?, #8; b 8; data.f32 18.000000 - fmul s226?, s131?, s252? - ldr s251?, #8; b 8; data.f32 19.000000 - fmul s228?, s131?, s251? - ldr s250?, #8; b 8; data.f32 20.000000 - fmul s230?, s131?, s250? - fadd s231?, s228?, s230? - fadd s232?, s226?, s231? - fadd s233?, s224?, s232? - fadd s234?, s222?, s233? - fadd s235?, s220?, s234? - fadd s236?, s218?, s235? - fadd s237?, s216?, s236? - fadd s238?, s214?, s237? - fadd s239?, s212?, s238? - fadd s240?, s210?, s239? - fadd s241?, s208?, s240? - fadd s242?, s206?, s241? - fadd s243?, s204?, s242? - fadd s244?, s202?, s243? - fadd s245?, s200?, s244? - fadd s246?, s198?, s245? - fadd s247?, s196?, s246? - fadd s248?, s194?, s247? - fadd s249?, s192?, s248? - mov v0.8b, v249?.8b - mov x0, x190? + orr w286?, wzr, #0x2 + madd w133?, w130?, w286?, wzr + orr w285?, wzr, #0x3 + madd w135?, w130?, w285?, wzr + orr w284?, wzr, #0x4 + madd w137?, w130?, w284?, wzr + movz w283?, #0x5, lsl 0 + madd w139?, w130?, w283?, wzr + orr w282?, wzr, #0x6 + madd w141?, w130?, w282?, wzr + orr w281?, wzr, #0x7 + madd w143?, w130?, w281?, wzr + orr w280?, wzr, #0x8 + madd w145?, w130?, w280?, wzr + movz w279?, #0x9, lsl 0 + madd w147?, w130?, w279?, wzr + movz w278?, #0xa, lsl 0 + madd w149?, w130?, w278?, wzr + movz w277?, #0xb, lsl 0 + madd w151?, w130?, w277?, wzr + orr w276?, wzr, #0xc + madd w153?, w130?, w276?, wzr + movz w275?, #0xd, lsl 0 + madd w155?, w130?, w275?, wzr + orr w274?, wzr, #0xe + madd w157?, w130?, w274?, wzr + orr w273?, wzr, #0xf + madd w159?, w130?, w273?, wzr + orr w272?, wzr, #0x10 + madd w161?, w130?, w272?, wzr + movz w271?, #0x11, lsl 0 + madd w163?, w130?, w271?, wzr + movz w270?, #0x12, lsl 0 + madd w165?, w130?, w270?, wzr + movz w269?, #0x13, lsl 0 + madd w167?, w130?, w269?, wzr + movz w268?, #0x14, lsl 0 + madd w169?, w130?, w268?, wzr + add w170?, w167?, w169? + add w171?, w165?, w170? + add w172?, w163?, w171? + add w173?, w161?, w172? + add w174?, w159?, w173? + add w175?, w157?, w174? + add w176?, w155?, w175? + add w177?, w153?, w176? + add w178?, w151?, w177? + add w179?, w149?, w178? + add w180?, w147?, w179? + add w181?, w145?, w180? + add w182?, w143?, w181? + add w183?, w141?, w182? + add w184?, w139?, w183? + add w185?, w137?, w184? + add w186?, w135?, w185? + add w187?, w133?, w186? + add w188?, w130?, w187? + ldr s267?, #8; b 8; data.f32 1.000000 + fmul s190?, s131?, s267? + ldr s266?, #8; b 8; data.f32 2.000000 + fmul s192?, s131?, s266? + ldr s265?, #8; b 8; data.f32 3.000000 + fmul s194?, s131?, s265? + ldr s264?, #8; b 8; data.f32 4.000000 + fmul s196?, s131?, s264? + ldr s263?, #8; b 8; data.f32 5.000000 + fmul s198?, s131?, s263? + ldr s262?, #8; b 8; data.f32 6.000000 + fmul s200?, s131?, s262? + ldr s261?, #8; b 8; data.f32 7.000000 + fmul s202?, s131?, s261? + ldr s260?, #8; b 8; data.f32 8.000000 + fmul s204?, s131?, s260? + ldr s259?, #8; b 8; data.f32 9.000000 + fmul s206?, s131?, s259? + ldr s258?, #8; b 8; data.f32 10.000000 + fmul s208?, s131?, s258? + ldr s257?, #8; b 8; data.f32 11.000000 + fmul s210?, s131?, s257? + ldr s256?, #8; b 8; data.f32 12.000000 + fmul s212?, s131?, s256? + ldr s255?, #8; b 8; data.f32 13.000000 + fmul s214?, s131?, s255? + ldr s254?, #8; b 8; data.f32 14.000000 + fmul s216?, s131?, s254? + ldr s253?, #8; b 8; data.f32 15.000000 + fmul s218?, s131?, s253? + ldr s252?, #8; b 8; data.f32 16.000000 + fmul s220?, s131?, s252? + ldr s251?, #8; b 8; data.f32 17.000000 + fmul s222?, s131?, s251? + ldr s250?, #8; b 8; data.f32 18.000000 + fmul s224?, s131?, s250? + ldr s249?, #8; b 8; data.f32 19.000000 + fmul s226?, s131?, s249? + ldr s248?, #8; b 8; data.f32 20.000000 + fmul s228?, s131?, s248? + fadd s229?, s226?, s228? + fadd s230?, s224?, s229? + fadd s231?, s222?, s230? + fadd s232?, s220?, s231? + fadd s233?, s218?, s232? + fadd s234?, s216?, s233? + fadd s235?, s214?, s234? + fadd s236?, s212?, s235? + fadd s237?, s210?, s236? + fadd s238?, s208?, s237? + fadd s239?, s206?, s238? + fadd s240?, s204?, s239? + fadd s241?, s202?, s240? + fadd s242?, s200?, s241? + fadd s243?, s198?, s242? + fadd s244?, s196?, s243? + fadd s245?, s194?, s244? + fadd s246?, s192?, s245? + fadd s247?, s190?, s246? + mov v0.8b, v247?.8b + mov x0, x188? ret `, afterFinalizeARM64: ` @@ -1539,47 +1537,44 @@ L1 (SSA Block: blk0): str q27, [sp, #-0x10]! movz x27, #0x120, lsl 0 str x27, [sp, #-0x10]! - orr w8, wzr, #0x1 + orr w8, wzr, #0x2 madd w8, w2, w8, wzr - orr w9, wzr, #0x2 + orr w9, wzr, #0x3 madd w9, w2, w9, wzr - orr w10, wzr, #0x3 + orr w10, wzr, #0x4 madd w10, w2, w10, wzr - orr w11, wzr, #0x4 + movz w11, #0x5, lsl 0 madd w11, w2, w11, wzr - movz w12, #0x5, lsl 0 + orr w12, wzr, #0x6 madd w12, w2, w12, wzr - orr w13, wzr, #0x6 + orr w13, wzr, #0x7 madd w13, w2, w13, wzr - orr w14, wzr, #0x7 + orr w14, wzr, #0x8 madd w14, w2, w14, wzr - orr w15, wzr, #0x8 + movz w15, #0x9, lsl 0 madd w15, w2, w15, wzr - movz w16, #0x9, lsl 0 + movz w16, #0xa, lsl 0 madd w16, w2, w16, wzr - movz w17, #0xa, lsl 0 + movz w17, #0xb, lsl 0 madd w17, w2, w17, wzr - movz w19, #0xb, lsl 0 + orr w19, wzr, #0xc madd w19, w2, w19, wzr - orr w20, wzr, #0xc + movz w20, #0xd, lsl 0 madd w20, w2, w20, wzr - movz w21, #0xd, lsl 0 + orr w21, wzr, #0xe madd w21, w2, w21, wzr - orr w22, wzr, #0xe + orr w22, wzr, #0xf madd w22, w2, w22, wzr - orr w23, wzr, #0xf + orr w23, wzr, #0x10 madd w23, w2, w23, wzr - orr w24, wzr, #0x10 + movz w24, #0x11, lsl 0 madd w24, w2, w24, wzr - movz w25, #0x11, lsl 0 + movz w25, #0x12, lsl 0 madd w25, w2, w25, wzr - movz w26, #0x12, lsl 0 + movz w26, #0x13, lsl 0 madd w26, w2, w26, wzr - movz w29, #0x13, lsl 0 + movz w29, #0x14, lsl 0 madd w29, w2, w29, wzr - movz w30, #0x14, lsl 0 - madd w30, w2, w30, wzr - add w29, w29, w30 add w26, w26, w29 add w25, w25, w26 add w24, w24, w25 @@ -1598,6 +1593,7 @@ L1 (SSA Block: blk0): add w10, w10, w11 add w9, w9, w10 add w8, w8, w9 + add w8, w2, w8 ldr s8, #8; b 8; data.f32 1.000000 fmul s8, s0, s8 ldr s9, #8; b 8; data.f32 2.000000 diff --git a/internal/engine/wazevo/frontend/frontend_test.go b/internal/engine/wazevo/frontend/frontend_test.go index f50d3218cc..7ddb821292 100644 --- a/internal/engine/wazevo/frontend/frontend_test.go +++ b/internal/engine/wazevo/frontend/frontend_test.go @@ -95,8 +95,8 @@ blk0: (exec_ctx:i64, module_ctx:i64, v2:i32, v3:i32) blk0: (exec_ctx:i64, module_ctx:i64) v2:i32 = Iconst_32 0x0 v3:i64 = Iconst_64 0x0 - v4:f32 = F32const 0.000000 - v5:f64 = F64const 0.000000 + v4:f32 = F32const 0 + v5:f64 = F64const 0 Jump blk_ret `, expAfterOpt: ` @@ -136,8 +136,8 @@ blk0: (exec_ctx:i64, module_ctx:i64, v2:i32) blk0: (exec_ctx:i64, module_ctx:i64, v2:i64, v3:f32, v4:f64) v5:i32 = Iconst_32 0x0 v6:i64 = Iconst_64 0x0 - v7:f32 = F32const 0.000000 - v8:f64 = F64const 0.000000 + v7:f32 = F32const 0 + v8:f64 = F64const 0 v9:i64 = Iadd v2, v2 v10:i64 = Isub v9, v2 v11:f32 = Fadd v3, v3 @@ -204,8 +204,8 @@ blk1: () <-- (blk0) blk0: (exec_ctx:i64, module_ctx:i64) v2:i32 = Iconst_32 0x0 v3:i64 = Iconst_64 0x0 - v4:f32 = F32const 0.000000 - v5:f64 = F64const 0.000000 + v4:f32 = F32const 0 + v5:f64 = F64const 0 Jump blk1 blk1: () <-- (blk0) @@ -311,8 +311,8 @@ blk3: () <-- (blk1) blk0: (exec_ctx:i64, module_ctx:i64) v2:i32 = Iconst_32 0x0 v3:i64 = Iconst_64 0x0 - v4:f32 = F32const 0.000000 - v5:f64 = F64const 0.000000 + v4:f32 = F32const 0 + v5:f64 = F64const 0 Jump blk1 blk1: () <-- (blk0) @@ -885,45 +885,45 @@ blk0: (exec_ctx:i64, module_ctx:i64, v2:i32, v3:f32) v60:i32 = Iadd v9, v59 v61:i32 = Iadd v7, v60 v62:i32 = Iadd v5, v61 - v63:f32 = F32const 1.000000 + v63:f32 = F32const 1 v64:f32 = Fmul v3, v63 - v65:f32 = F32const 2.000000 + v65:f32 = F32const 2 v66:f32 = Fmul v3, v65 - v67:f32 = F32const 3.000000 + v67:f32 = F32const 3 v68:f32 = Fmul v3, v67 - v69:f32 = F32const 4.000000 + v69:f32 = F32const 4 v70:f32 = Fmul v3, v69 - v71:f32 = F32const 5.000000 + v71:f32 = F32const 5 v72:f32 = Fmul v3, v71 - v73:f32 = F32const 6.000000 + v73:f32 = F32const 6 v74:f32 = Fmul v3, v73 - v75:f32 = F32const 7.000000 + v75:f32 = F32const 7 v76:f32 = Fmul v3, v75 - v77:f32 = F32const 8.000000 + v77:f32 = F32const 8 v78:f32 = Fmul v3, v77 - v79:f32 = F32const 9.000000 + v79:f32 = F32const 9 v80:f32 = Fmul v3, v79 - v81:f32 = F32const 10.000000 + v81:f32 = F32const 10 v82:f32 = Fmul v3, v81 - v83:f32 = F32const 11.000000 + v83:f32 = F32const 11 v84:f32 = Fmul v3, v83 - v85:f32 = F32const 12.000000 + v85:f32 = F32const 12 v86:f32 = Fmul v3, v85 - v87:f32 = F32const 13.000000 + v87:f32 = F32const 13 v88:f32 = Fmul v3, v87 - v89:f32 = F32const 14.000000 + v89:f32 = F32const 14 v90:f32 = Fmul v3, v89 - v91:f32 = F32const 15.000000 + v91:f32 = F32const 15 v92:f32 = Fmul v3, v91 - v93:f32 = F32const 16.000000 + v93:f32 = F32const 16 v94:f32 = Fmul v3, v93 - v95:f32 = F32const 17.000000 + v95:f32 = F32const 17 v96:f32 = Fmul v3, v95 - v97:f32 = F32const 18.000000 + v97:f32 = F32const 18 v98:f32 = Fmul v3, v97 - v99:f32 = F32const 19.000000 + v99:f32 = F32const 19 v100:f32 = Fmul v3, v99 - v101:f32 = F32const 20.000000 + v101:f32 = F32const 20 v102:f32 = Fmul v3, v101 v103:f32 = Fadd v100, v102 v104:f32 = Fadd v98, v103 @@ -1356,10 +1356,10 @@ blk0: (exec_ctx:i64, module_ctx:i64) v4:i64 = Iconst_64 0x2 v5:i64 = Load module_ctx, 0x10 Store v4, v5, 0x8 - v6:f32 = F32const 3.000000 + v6:f32 = F32const 3 v7:i64 = Load module_ctx, 0x18 Store v6, v7, 0x8 - v8:f64 = F64const 4.000000 + v8:f64 = F64const 4 v9:i64 = Load module_ctx, 0x20 Store v8, v9, 0x8 Jump blk_ret, v2, v4, v6, v8 diff --git a/internal/engine/wazevo/ssa/instructions.go b/internal/engine/wazevo/ssa/instructions.go index 4dbe0883af..fa3df6e824 100644 --- a/internal/engine/wazevo/ssa/instructions.go +++ b/internal/engine/wazevo/ssa/instructions.go @@ -1732,27 +1732,30 @@ func (i *Instruction) InsertlaneData() (x, y Value, index byte, l VecLane) { } // AsFadd initializes this instruction as a floating-point addition instruction with OpcodeFadd. -func (i *Instruction) AsFadd(x, y Value) { +func (i *Instruction) AsFadd(x, y Value) *Instruction { i.opcode = OpcodeFadd i.v = x i.v2 = y i.typ = x.Type() + return i } // AsFsub initializes this instruction as a floating-point subtraction instruction with OpcodeFsub. -func (i *Instruction) AsFsub(x, y Value) { +func (i *Instruction) AsFsub(x, y Value) *Instruction { i.opcode = OpcodeFsub i.v = x i.v2 = y i.typ = x.Type() + return i } // AsFmul initializes this instruction as a floating-point multiplication instruction with OpcodeFmul. -func (i *Instruction) AsFmul(x, y Value) { +func (i *Instruction) AsFmul(x, y Value) *Instruction { i.opcode = OpcodeFmul i.v = x i.v2 = y i.typ = x.Type() + return i } // AsFdiv initializes this instruction as a floating-point division instruction with OpcodeFdiv. @@ -2391,9 +2394,9 @@ func (i *Instruction) Format(b Builder) string { case OpcodeVconst: instSuffix = fmt.Sprintf(" %016x %016x", i.u1, i.u2) case OpcodeF32const: - instSuffix = fmt.Sprintf(" %f", math.Float32frombits(uint32(i.u1))) + instSuffix = fmt.Sprintf(" %g", math.Float32frombits(uint32(i.u1))) case OpcodeF64const: - instSuffix = fmt.Sprintf(" %f", math.Float64frombits(i.u1)) + instSuffix = fmt.Sprintf(" %g", math.Float64frombits(i.u1)) case OpcodeReturn: if len(i.vs) == 0 { break diff --git a/internal/engine/wazevo/ssa/pass.go b/internal/engine/wazevo/ssa/pass.go index 17c8486f60..3446311e11 100644 --- a/internal/engine/wazevo/ssa/pass.go +++ b/internal/engine/wazevo/ssa/pass.go @@ -2,6 +2,7 @@ package ssa import ( "fmt" + "math" "sort" "github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi" @@ -17,9 +18,10 @@ func (b *builder) RunPasses() { passSortSuccessors(b) passDeadBlockEliminationOpt(b) passRedundantPhiEliminationOpt(b) - // The result of passCalculateImmediateDominators will be used by various passes below. + // The result of passCalculateImmediateDominators and passCollectValueIdToInstructionMapping + // will be used by various passes below. passCalculateImmediateDominators(b) - passNopInstElimination(b) + passCollectValueIdToInstructionMapping(b) // TODO: implement either conversion of irreducible CFG into reducible one, or irreducible CFG detection where we panic. // WebAssembly program shouldn't result in irreducible CFG, but we should handle it properly in just in case. @@ -33,6 +35,9 @@ func (b *builder) RunPasses() { // Arithmetic simplifications. // and more! + passConstFoldingOpt(b) + passNopInstElimination(b) + // passDeadCodeEliminationOpt could be more accurate if we do this after other optimizations. passDeadCodeEliminationOpt(b) b.donePasses = true @@ -174,9 +179,6 @@ func passDeadCodeEliminationOpt(b *builder) { if nvid >= len(b.valueRefCounts) { b.valueRefCounts = append(b.valueRefCounts, make([]int, b.nextValueID)...) } - if nvid >= len(b.valueIDToInstruction) { - b.valueIDToInstruction = append(b.valueIDToInstruction, make([]*Instruction, b.nextValueID)...) - } // First, we gather all the instructions with side effects. liveInstructions := b.instStack[:0] @@ -195,14 +197,6 @@ func passDeadCodeEliminationOpt(b *builder) { // The strict side effect should create different instruction groups. gid++ } - - r1, rs := cur.Returns() - if r1.Valid() { - b.valueIDToInstruction[r1.ID()] = cur - } - for _, r := range rs { - b.valueIDToInstruction[r.ID()] = cur - } } } @@ -309,26 +303,13 @@ func (b *builder) clearBlkVisited() { // passNopInstElimination eliminates the instructions which is essentially a no-op. func passNopInstElimination(b *builder) { - if int(b.nextValueID) >= len(b.valueIDToInstruction) { - b.valueIDToInstruction = append(b.valueIDToInstruction, make([]*Instruction, b.nextValueID)...) - } - for blk := b.blockIteratorBegin(); blk != nil; blk = b.blockIteratorNext() { for cur := blk.rootInstr; cur != nil; cur = cur.next { - r1, rs := cur.Returns() - if r1.Valid() { - b.valueIDToInstruction[r1.ID()] = cur - } - for _, r := range rs { - b.valueIDToInstruction[r.ID()] = cur - } - } - } - - for blk := b.blockIteratorBegin(); blk != nil; blk = b.blockIteratorNext() { - for cur := blk.rootInstr; cur != nil; cur = cur.next { - switch cur.Opcode() { + op := cur.Opcode() + switch op { // TODO: add more logics here. + // Amount := (Const $someValue) + // (Shift X, Amount) where Amount == x.Type.Bits() => X case OpcodeIshl, OpcodeSshr, OpcodeUshr: x, amount := cur.Arg2() definingInst := b.valueIDToInstruction[amount.ID()] @@ -348,11 +329,171 @@ func passNopInstElimination(b *builder) { b.alias(cur.Return(), x) } } + // When Op is Iadd, Bor, Bxor, Rotl or Rotr, and Z is Iconst 0: + // (Op X, Z) => X + // (Op Z, Y) => Y + case OpcodeIadd, OpcodeBor, OpcodeBxor, OpcodeRotl, OpcodeRotr: + x, y := cur.Arg2() + definingInst := b.valueIDToInstruction[y.ID()] + if definingInst == nil { + if definingInst = b.valueIDToInstruction[x.ID()]; definingInst == nil { + continue + } else { + x = y + } + } + if definingInst.Constant() && definingInst.ConstantVal() == 0 { + b.alias(cur.Return(), x) + } + // When Op is Imul and Z is Iconst 1: + // (Op X, Z) => X + // (Op Z, Y) => Y + // TODO: This is also valid for UDiv, SDiv, but they are trapping, so we would + // need to update passDeadCodeEliminationOpt to account for this case and mark them dead. + case OpcodeImul: + x, y := cur.Arg2() + definingInst := b.valueIDToInstruction[y.ID()] + if definingInst == nil { + if definingInst = b.valueIDToInstruction[x.ID()]; definingInst == nil { + continue + } else { + x = y + } + } + if definingInst.Constant() && definingInst.ConstantVal() == 1 { + b.alias(cur.Return(), x) + } + default: + continue + } + } + } +} + +func passCollectValueIdToInstructionMapping(b *builder) { + if int(b.nextValueID) >= len(b.valueIDToInstruction) { + b.valueIDToInstruction = append(b.valueIDToInstruction, make([]*Instruction, b.nextValueID)...) + } + + for blk := b.blockIteratorBegin(); blk != nil; blk = b.blockIteratorNext() { + for cur := blk.rootInstr; cur != nil; cur = cur.next { + r1, rs := cur.Returns() + if r1.Valid() { + b.valueIDToInstruction[r1.ID()] = cur + } + for _, r := range rs { + b.valueIDToInstruction[r.ID()] = cur + } + } + } +} + +// passConstFoldingOptMaxIter controls the max number of iterations per-BB in passConstFoldingOpt, before giving up. +const passConstFoldingOptMaxIter = math.MaxInt + +// passConstFoldingOpt scans all instructions for arithmetic operations over constants, +// and replaces them with a const of their result. Repeats for each basic blocks until +// a fixed point is reached or num iter == passConstFoldingOptMaxIter. +func passConstFoldingOpt(b *builder) { + for blk := b.blockIteratorBegin(); blk != nil; blk = b.blockIteratorNext() { + for cur := blk.rootInstr; cur != nil; cur = cur.next { + // The fixed point is reached through a simple iteration over the list of instructions. + // Note: Instead of just an unbounded loop with a flag, we may also add an upper bound to the number of iterations. + for iter, isFixedPoint := 0, false; iter < passConstFoldingOptMaxIter && !isFixedPoint; iter++ { + isFixedPoint = true + op := cur.Opcode() + switch op { + // X := Const xc + // Y := Const yc + // - (op X, Y) => Const (xc yc); e.g. if op is Iadd => xc + yc. + case OpcodeIadd, OpcodeIsub, OpcodeImul: + x, y := cur.Arg2() + xDef := b.valueIDToInstruction[x.ID()] + yDef := b.valueIDToInstruction[y.ID()] + if xDef == nil || yDef == nil { + // If we are adding some parameter, ignore. + continue + } + if xDef.Constant() && yDef.Constant() { + isFixedPoint = false + // Mutate the instruction to an Iconst. + cur.opcode = OpcodeIconst + // Clear the references to operands. + cur.v, cur.v2 = ValueInvalid, ValueInvalid + // We assume all the types are consistent. + // Signed integers are 2 complement, so we can just apply the operations. + // Operations are evaluated over uint64s and will be bitcasted at the use-sites. + xc, yc := xDef.ConstantVal(), yDef.ConstantVal() + cur.u1 = evalArithmeticOp(op, xc, yc) + } + // X := Const xc + // Y := Const yc + // - (op X, Y) => Const (xc yc); e.g. if op is Fadd => xc + yc. + case OpcodeFadd, OpcodeFsub, OpcodeFmul: + x, y := cur.Arg2() + xDef := b.valueIDToInstruction[x.ID()] + yDef := b.valueIDToInstruction[y.ID()] + if xDef == nil || yDef == nil { + // If we are composing some parameter, ignore. + continue + } + if xDef.Constant() && yDef.Constant() { + isFixedPoint = false + // Mutate the instruction to an Iconst. + // Clear the references to operands. + cur.v, cur.v2 = ValueInvalid, ValueInvalid + // We assume all the types are consistent. + if x.Type().Bits() == 64 { + cur.opcode = OpcodeF64const + yc := math.Float64frombits(yDef.ConstantVal()) + xc := math.Float64frombits(xDef.ConstantVal()) + switch op { + case OpcodeFadd: + cur.u1 = math.Float64bits(xc + yc) + case OpcodeFsub: + cur.u1 = math.Float64bits(xc - yc) + case OpcodeFmul: + cur.u1 = math.Float64bits(xc * yc) + } + } else { + cur.opcode = OpcodeF32const + yc := math.Float32frombits(uint32(yDef.ConstantVal())) + xc := math.Float32frombits(uint32(xDef.ConstantVal())) + switch op { + case OpcodeFadd: + cur.u1 = uint64(math.Float32bits(xc + yc)) + case OpcodeFsub: + cur.u1 = uint64(math.Float32bits(xc - yc)) + case OpcodeFmul: + cur.u1 = uint64(math.Float32bits(xc * yc)) + } + } + } + } } } } } +func evalArithmeticOp(op Opcode, xc uint64, yc uint64) uint64 { + switch op { + case OpcodeIadd: + return xc + yc + case OpcodeIsub: + return xc - yc + case OpcodeImul: + return xc * yc + case OpcodeBor: + return xc | yc + case OpcodeBand: + return xc & yc + case OpcodeBxor: + return xc ^ yc + default: + panic("unhandled default case " + op.String()) + } +} + // passSortSuccessors sorts the successors of each block in the natural program order. func passSortSuccessors(b *builder) { for i := 0; i < b.basicBlocksPool.Allocated(); i++ { diff --git a/internal/engine/wazevo/ssa/pass_test.go b/internal/engine/wazevo/ssa/pass_test.go index 2a831a67f1..f5e46b7636 100644 --- a/internal/engine/wazevo/ssa/pass_test.go +++ b/internal/engine/wazevo/ssa/pass_test.go @@ -1,6 +1,7 @@ package ssa import ( + "math" "testing" "github.com/tetratelabs/wazero/internal/testing/require" @@ -9,6 +10,9 @@ import ( func TestBuilder_passes(t *testing.T) { for _, tc := range []struct { name string + // prePass is run before the pass is executed, and can be used to configure the environment + // (e.g. init `*builder` fields). + prePass, // pass is the optimization pass to run. pass, // postPass is run after the pass is executed, and can be used to test a pass that depends on another pass. @@ -193,8 +197,9 @@ blk2: () <-- (blk1) `, }, { - name: "dead code", - pass: passDeadCodeEliminationOpt, + name: "dead code", + prePass: passCollectValueIdToInstructionMapping, + pass: passDeadCodeEliminationOpt, setup: func(b *builder) func(*testing.T) { entry, end := b.AllocateBasicBlock(), b.AllocateBasicBlock() @@ -286,6 +291,7 @@ blk1: () <-- (blk0) }, { name: "nop elimination", + prePass: passCollectValueIdToInstructionMapping, pass: passNopInstElimination, postPass: passDeadCodeEliminationOpt, setup: func(b *builder) (verifier func(t *testing.T)) { @@ -310,8 +316,24 @@ blk1: () <-- (blk0) nonZeroI64 := b.AllocateInstruction().AsIconst64(64*245 + 1).Insert(b).Return() nonZeroSshr := b.AllocateInstruction().AsSshr(i64Param, nonZeroI64).Insert(b).Return() + // Iadd32 x + 0 should resolve to const. + zeroI32 := b.AllocateInstruction().AsIconst32(0).Insert(b).Return() + nopIadd32 := b.AllocateInstruction().AsIadd(i32Param, zeroI32).Insert(b).Return() + + // Iadd32 0 + x should resolve to const. + zeroI32_2 := b.AllocateInstruction().AsIconst32(0).Insert(b).Return() + nopIadd32_2 := b.AllocateInstruction().AsIadd(zeroI32_2, i32Param).Insert(b).Return() + + // Iadd64 x + 0 should resolve to const. + zeroI64 := b.AllocateInstruction().AsIconst64(0).Insert(b).Return() + nopIadd64 := b.AllocateInstruction().AsIadd(i64Param, zeroI64).Insert(b).Return() + + // Iadd64 0 + x should resolve to const. + zeroI64_2 := b.AllocateInstruction().AsIconst64(0).Insert(b).Return() + nopIadd64_2 := b.AllocateInstruction().AsIadd(zeroI64_2, i64Param).Insert(b).Return() + ret := b.AllocateInstruction() - ret.AsReturn([]Value{nopIshl, nopUshr, nonZeroIshl, nonZeroSshr}) + ret.AsReturn([]Value{nopIshl, nopUshr, nonZeroIshl, nonZeroSshr, nopIadd32, nopIadd32_2, nopIadd64, nopIadd64_2}) b.InsertInstruction(ret) return nil }, @@ -325,7 +347,15 @@ blk0: (v0:i32, v1:i64) v7:i32 = Ishl v0, v6 v8:i64 = Iconst_64 0x3d41 v9:i64 = Sshr v1, v8 - Return v3, v5, v7, v9 + v10:i32 = Iconst_32 0x0 + v11:i32 = Iadd v0, v10 + v12:i32 = Iconst_32 0x0 + v13:i32 = Iadd v12, v0 + v14:i64 = Iconst_64 0x0 + v15:i64 = Iadd v1, v14 + v16:i64 = Iconst_64 0x0 + v17:i64 = Iadd v16, v1 + Return v3, v5, v7, v9, v11, v13, v15, v17 `, after: ` blk0: (v0:i32, v1:i64) @@ -333,7 +363,280 @@ blk0: (v0:i32, v1:i64) v7:i32 = Ishl v0, v6 v8:i64 = Iconst_64 0x3d41 v9:i64 = Sshr v1, v8 - Return v0, v1, v7, v9 + Return v0, v1, v7, v9, v0, v0, v1, v1 +`, + }, + { + name: "const folding", + prePass: passCollectValueIdToInstructionMapping, + pass: passConstFoldingOpt, + postPass: passDeadCodeEliminationOpt, + setup: func(b *builder) (verifier func(t *testing.T)) { + entry := b.AllocateBasicBlock() + b.SetCurrentBlock(entry) + + // Iadd32 const1 + const2 should resolve to Const (const1 + const2). + nonZeroI32_1 := b.AllocateInstruction().AsIconst32(0x1).Insert(b).Return() + nonZeroI32_2 := b.AllocateInstruction().AsIconst32(0x2).Insert(b).Return() + foldIaddI32_1 := b.AllocateInstruction().AsIadd(nonZeroI32_1, nonZeroI32_2).Insert(b).Return() + + // Iadd32 foldedConst1, const3 should resolve to Const (foldedConst1, const3). + nonZeroI32_3 := b.AllocateInstruction().AsIconst32(0x3).Insert(b).Return() + foldIaddI32_2 := b.AllocateInstruction().AsIadd(foldIaddI32_1, nonZeroI32_3).Insert(b).Return() + + // Isub32 foldedConst1, const3 should resolve to Const (const4, foldedConst2). + nonZeroI32_4 := b.AllocateInstruction().AsIconst32(0x4).Insert(b).Return() + foldIsubI32_1 := b.AllocateInstruction().AsIsub(nonZeroI32_4, foldIaddI32_2).Insert(b).Return() + + // Imul32 foldedConst, foldedConst should resolve to IConst32 (foldedConst * foldedConst). + foldImulI32_1 := b.AllocateInstruction().AsImul(foldIsubI32_1, foldIsubI32_1).Insert(b).Return() + + // Iadd64 const1 + const2 should resolve to Const (const1 + const2). + nonZeroI64_1 := b.AllocateInstruction().AsIconst64(0x1).Insert(b).Return() + nonZeroI64_2 := b.AllocateInstruction().AsIconst64(0x2).Insert(b).Return() + foldIaddI64_1 := b.AllocateInstruction().AsIadd(nonZeroI64_1, nonZeroI64_2).Insert(b).Return() + + // Iadd64 foldedConst1, const3 should resolve to Const (foldedConst1, const3). + nonZeroI64_3 := b.AllocateInstruction().AsIconst64(0x3).Insert(b).Return() + foldIaddI64_2 := b.AllocateInstruction().AsIadd(foldIaddI64_1, nonZeroI64_3).Insert(b).Return() + + // Isub64 const4, foldedConst1 should resolve to Const (const4, foldedConst2). + nonZeroI64_4 := b.AllocateInstruction().AsIconst64(0x4).Insert(b).Return() + foldIsubI64_1 := b.AllocateInstruction().AsIsub(nonZeroI64_4, foldIaddI64_2).Insert(b).Return() + + // Imul64 foldedConst, foldedConst should resolve to IConst64 (foldedConst * foldedConst). + foldImulI64_1 := b.AllocateInstruction().AsImul(foldIsubI64_1, foldIsubI64_1).Insert(b).Return() + + // Fadd32 const1 + const2 should resolve to Const (const1 + const2). + nonZeroF32_1 := b.AllocateInstruction().AsF32const(1.0).Insert(b).Return() + nonZeroF32_2 := b.AllocateInstruction().AsF32const(2.0).Insert(b).Return() + foldFaddF32_1 := b.AllocateInstruction().AsFadd(nonZeroF32_1, nonZeroF32_2).Insert(b).Return() + + // Fadd32 foldedConst1, const3 should resolve to Const (foldedConst1 + const3). + nonZeroF32_3 := b.AllocateInstruction().AsF32const(3.0).Insert(b).Return() + foldIaddF32_2 := b.AllocateInstruction().AsFadd(foldFaddF32_1, nonZeroF32_3).Insert(b).Return() + + // Fsub32 const4, foldedConst1 should resolve to Const (const4 - foldedConst2). + nonZeroF32_4 := b.AllocateInstruction().AsF32const(4.0).Insert(b).Return() + foldIsubF32_1 := b.AllocateInstruction().AsFsub(nonZeroF32_4, foldIaddF32_2).Insert(b).Return() + + // Fmul32 foldedConst, foldedConst should resolve to FConst32 (foldedConst * foldedConst). + foldFmulF32_1 := b.AllocateInstruction().AsFmul(foldIsubF32_1, foldIsubF32_1).Insert(b).Return() + + // Fadd64 const1 + const2 should resolve to FConst64 (const1 + const2). + nonZeroF64_1 := b.AllocateInstruction().AsF64const(1.0).Insert(b).Return() + nonZeroF64_2 := b.AllocateInstruction().AsF64const(2.0).Insert(b).Return() + // This intermediate value won't be dropped because it is referenced in the result. + foldFaddF64_1 := b.AllocateInstruction().AsFadd(nonZeroF64_1, nonZeroF64_2).Insert(b).Return() + + // Fadd64 foldedConst1, const3 should resolve to FConst64 (foldedConst1 + const3). + nonZeroF64_3 := b.AllocateInstruction().AsF64const(3.0).Insert(b).Return() + foldFaddF64_2 := b.AllocateInstruction().AsFadd(foldFaddF64_1, nonZeroF64_3).Insert(b).Return() + + // Fsub64 const4, foldedConst1 should resolve to FConst64 (const4 - foldedConst2). + nonZeroF64_4 := b.AllocateInstruction().AsF64const(4.0).Insert(b).Return() + foldFsubF64_1 := b.AllocateInstruction().AsFsub(nonZeroF64_4, foldFaddF64_2).Insert(b).Return() + + // Fmul64 foldedConst, foldedConst should resolve to FConst64 (foldedConst * foldedConst). + foldFmulF64_1 := b.AllocateInstruction().AsFmul(foldFsubF64_1, foldFsubF64_1).Insert(b).Return() + + ret := b.AllocateInstruction() + ret.AsReturn([]Value{ + foldImulI32_1, + foldIsubI64_1, + foldImulI64_1, + foldIsubF32_1, + foldFmulF32_1, + foldFaddF64_1, + foldFsubF64_1, + foldFmulF64_1, + }) + b.InsertInstruction(ret) + return nil + }, + before: ` +blk0: () + v0:i32 = Iconst_32 0x1 + v1:i32 = Iconst_32 0x2 + v2:i32 = Iadd v0, v1 + v3:i32 = Iconst_32 0x3 + v4:i32 = Iadd v2, v3 + v5:i32 = Iconst_32 0x4 + v6:i32 = Isub v5, v4 + v7:i32 = Imul v6, v6 + v8:i64 = Iconst_64 0x1 + v9:i64 = Iconst_64 0x2 + v10:i64 = Iadd v8, v9 + v11:i64 = Iconst_64 0x3 + v12:i64 = Iadd v10, v11 + v13:i64 = Iconst_64 0x4 + v14:i64 = Isub v13, v12 + v15:i64 = Imul v14, v14 + v16:f32 = F32const 1 + v17:f32 = F32const 2 + v18:f32 = Fadd v16, v17 + v19:f32 = F32const 3 + v20:f32 = Fadd v18, v19 + v21:f32 = F32const 4 + v22:f32 = Fsub v21, v20 + v23:f32 = Fmul v22, v22 + v24:f64 = F64const 1 + v25:f64 = F64const 2 + v26:f64 = Fadd v24, v25 + v27:f64 = F64const 3 + v28:f64 = Fadd v26, v27 + v29:f64 = F64const 4 + v30:f64 = Fsub v29, v28 + v31:f64 = Fmul v30, v30 + Return v7, v14, v15, v22, v23, v26, v30, v31 +`, + after: ` +blk0: () + v7:i32 = Iconst_32 0x4 + v14:i64 = Iconst_64 0xfffffffffffffffe + v15:i64 = Iconst_64 0x4 + v22:f32 = F32const -2 + v23:f32 = F32const 4 + v26:f64 = F64const 3 + v30:f64 = F64const -2 + v31:f64 = F64const 4 + Return v7, v14, v15, v22, v23, v26, v30, v31 +`, + }, + { + name: "const folding (overflow)", + prePass: passCollectValueIdToInstructionMapping, + pass: passConstFoldingOpt, + postPass: passDeadCodeEliminationOpt, + setup: func(b *builder) (verifier func(t *testing.T)) { + entry := b.AllocateBasicBlock() + b.SetCurrentBlock(entry) + + maxI32 := b.AllocateInstruction().AsIconst32(math.MaxInt32).Insert(b).Return() + oneI32 := b.AllocateInstruction().AsIconst32(1).Insert(b).Return() + // Iadd MaxInt32, 1 overflows and wraps around to 0x80000000 (min representable Int32) + wrapI32 := b.AllocateInstruction().AsIadd(maxI32, oneI32).Insert(b).Return() + // Imul MaxInt32, MaxInt32 overflows and wraps around to 0x1. + mulI32 := b.AllocateInstruction().AsImul(maxI32, maxI32).Insert(b).Return() + + // Explicitly using the constant because math.MinInt32 is not representable. + minI32 := b.AllocateInstruction().AsIconst32(0x80000000).Insert(b).Return() + // Isub 0x80000000, 1 overflows and wraps around to 0x7fffffff (max representable Int32) + negWrapI32 := b.AllocateInstruction().AsIsub(minI32, oneI32).Insert(b).Return() + + maxI64 := b.AllocateInstruction().AsIconst64(math.MaxInt64).Insert(b).Return() + oneI64 := b.AllocateInstruction().AsIconst64(1).Insert(b).Return() + // Iadd MaxInt64, 1 overflows and wraps around to 0x8000000000000000 (min representable Int64) + wrapI64 := b.AllocateInstruction().AsIadd(maxI64, oneI64).Insert(b).Return() + // Imul MaxInt64, MaxInt32 overflows and wraps around to 0x1. + mulI64 := b.AllocateInstruction().AsImul(maxI64, maxI64).Insert(b).Return() + + // Explicitly using the constant because math.MinInt64 is not representable. + minI64 := b.AllocateInstruction().AsIconst64(0x8000000000000000).Insert(b).Return() + // Isub 0x8000000000000000, 1 overflows and wraps around to 0x7fffffffffffffff (max representable Int64) + negWrapI64 := b.AllocateInstruction().AsIsub(minI64, oneI64).Insert(b).Return() + + maxF32 := b.AllocateInstruction().AsF32const(math.MaxFloat32).Insert(b).Return() + oneF32 := b.AllocateInstruction().AsF32const(1.0).Insert(b).Return() + // Fadd MaxFloat32, 1 absorbs the value and returns MaxFloat32. + addF32 := b.AllocateInstruction().AsFadd(maxF32, oneF32).Insert(b).Return() + // Fadd MaxFloat32, MaxFloat32 returns +Inf. + addF32_2 := b.AllocateInstruction().AsFadd(maxF32, maxF32).Insert(b).Return() + // Fmul MaxFloat32, MaxFloat32 returns +Inf. + mulF32 := b.AllocateInstruction().AsFmul(maxF32, maxF32).Insert(b).Return() + + minF32 := b.AllocateInstruction().AsF32const(-math.MaxFloat32).Insert(b).Return() + // Fsub -MaxFloat32, 1 absorbs the value and returns -MaxFloat32. + subF32 := b.AllocateInstruction().AsFsub(minF32, oneF32).Insert(b).Return() + // Fsub -MaxFloat32, -MaxFloat32 returns ?? + subF32_2 := b.AllocateInstruction().AsFadd(minF32, minF32).Insert(b).Return() + // Fmul returns +Inf. + mulMinF32 := b.AllocateInstruction().AsFmul(minF32, minF32).Insert(b).Return() + + maxF64 := b.AllocateInstruction().AsF64const(math.MaxFloat64).Insert(b).Return() + oneF64 := b.AllocateInstruction().AsF64const(1.0).Insert(b).Return() + // Fadd MaxFloat64, 1 absorbs the value and returns MaxFloat64. + addF64 := b.AllocateInstruction().AsFadd(maxF64, oneF64).Insert(b).Return() + // Fadd MaxFloat64, MaxFloat64 returns +Inf. + addF64_2 := b.AllocateInstruction().AsFadd(maxF64, maxF64).Insert(b).Return() + // Fmul MaxFloat64, MaxFloat64 returns +Inf. + mulF64 := b.AllocateInstruction().AsFmul(maxF64, maxF64).Insert(b).Return() + + minF64 := b.AllocateInstruction().AsF64const(-math.MaxFloat64).Insert(b).Return() + // Fsub -MaxFloat64, 1 absorbs the value and returns -MaxFloat64. + subF64 := b.AllocateInstruction().AsFsub(minF64, oneF64).Insert(b).Return() + // Fsub -MaxFloat64, -MaxFloat64 returns -Inf. + subF64_2 := b.AllocateInstruction().AsFadd(minF64, minF64).Insert(b).Return() + // Fmul -MaxFloat64, -MaxFloat64 returns +Inf. + mulMinF64 := b.AllocateInstruction().AsFmul(minF64, minF64).Insert(b).Return() + + ret := b.AllocateInstruction() + ret.AsReturn([]Value{ + wrapI32, mulI32, negWrapI32, + wrapI64, mulI64, negWrapI64, + addF32, addF32_2, mulF32, + subF32, subF32_2, mulMinF32, + addF64, addF64_2, mulF64, + subF64, subF64_2, mulMinF64, + }) + b.InsertInstruction(ret) + return nil + }, + before: ` +blk0: () + v0:i32 = Iconst_32 0x7fffffff + v1:i32 = Iconst_32 0x1 + v2:i32 = Iadd v0, v1 + v3:i32 = Imul v0, v0 + v4:i32 = Iconst_32 0x80000000 + v5:i32 = Isub v4, v1 + v6:i64 = Iconst_64 0x7fffffffffffffff + v7:i64 = Iconst_64 0x1 + v8:i64 = Iadd v6, v7 + v9:i64 = Imul v6, v6 + v10:i64 = Iconst_64 0x8000000000000000 + v11:i64 = Isub v10, v7 + v12:f32 = F32const 3.4028235e+38 + v13:f32 = F32const 1 + v14:f32 = Fadd v12, v13 + v15:f32 = Fadd v12, v12 + v16:f32 = Fmul v12, v12 + v17:f32 = F32const -3.4028235e+38 + v18:f32 = Fsub v17, v13 + v19:f32 = Fadd v17, v17 + v20:f32 = Fmul v17, v17 + v21:f64 = F64const 1.7976931348623157e+308 + v22:f64 = F64const 1 + v23:f64 = Fadd v21, v22 + v24:f64 = Fadd v21, v21 + v25:f64 = Fmul v21, v21 + v26:f64 = F64const -1.7976931348623157e+308 + v27:f64 = Fsub v26, v22 + v28:f64 = Fadd v26, v26 + v29:f64 = Fmul v26, v26 + Return v2, v3, v5, v8, v9, v11, v14, v15, v16, v18, v19, v20, v23, v24, v25, v27, v28, v29 +`, + after: ` +blk0: () + v2:i32 = Iconst_32 0x80000000 + v3:i32 = Iconst_32 0x1 + v5:i32 = Iconst_32 0x7fffffff + v8:i64 = Iconst_64 0x8000000000000000 + v9:i64 = Iconst_64 0x1 + v11:i64 = Iconst_64 0x7fffffffffffffff + v14:f32 = F32const 3.4028235e+38 + v15:f32 = F32const +Inf + v16:f32 = F32const +Inf + v18:f32 = F32const -3.4028235e+38 + v19:f32 = F32const -Inf + v20:f32 = F32const +Inf + v23:f64 = F64const 1.7976931348623157e+308 + v24:f64 = F64const +Inf + v25:f64 = F64const +Inf + v27:f64 = F64const -1.7976931348623157e+308 + v28:f64 = F64const -Inf + v29:f64 = F64const +Inf + Return v2, v3, v5, v8, v9, v11, v14, v15, v16, v18, v19, v20, v23, v24, v25, v27, v28, v29 `, }, } { @@ -342,6 +645,9 @@ blk0: (v0:i32, v1:i64) b := NewBuilder().(*builder) verifier := tc.setup(b) require.Equal(t, tc.before, b.Format()) + if tc.prePass != nil { + tc.prePass(b) + } tc.pass(b) if verifier != nil { verifier(t)