Repository: tetratelabs/wazero
Pull request #1667 (2023-08-28 19:49:01)

wazevo: passes float_exprs, local_set/tee spectests
follow up to https://github.com/tetratelabs/wazero/issues/1496.

- spec tests `local_set` and `local_tee` only missed one convert+one demote instruction to pass
- we also implement the other float conv instructions used in `float_exprs` so that we pass that spec test too




Patch:
diff --git a/internal/engine/wazevo/backend/isa/arm64/instr.go b/internal/engine/wazevo/backend/isa/arm64/instr.go
index 20d67493..67acb766 100644
--- a/internal/engine/wazevo/backend/isa/arm64/instr.go
+++ b/internal/engine/wazevo/backend/isa/arm64/instr.go
@@ -100,6 +100,7 @@ var defKinds = [numInstructionKinds]defKind{
 	vecMisc:         defKindRD,
 	vecLanes:        defKindRD,
 	vecRRR:          defKindRD,
+	fpuToInt:        defKindRD,
 	intToFpu:        defKindRD,
 	cCmpImm:         defKindNone,
 }
@@ -203,6 +204,7 @@ var useKinds = [numInstructionKinds]useKind{
 	vecMisc:         useKindRN,
 	vecLanes:        useKindRN,
 	vecRRR:          useKindRNRM,
+	fpuToInt:        useKindRN,
 	intToFpu:        useKindRN,
 }
 
@@ -904,9 +906,17 @@ func (i *instruction) String() (str string) {
 	case fpuMovFromVec:
 		panic("TODO")
 	case fpuRR:
-		size := is64SizeBitToSize(i.u3)
-		str = fmt.Sprintf("%s %s, %s", fpuUniOp(i.u1).String(),
-			formatVRegSized(i.rd.nr(), size), formatVRegSized(i.rn.nr(), size))
+		dstSz := is64SizeBitToSize(i.u3)
+		srcSz := dstSz
+		op := fpuUniOp(i.u1)
+		switch op {
+		case fpuUniOpCvt32To64:
+			srcSz = 32
+		case fpuUniOpCvt64To32:
+			srcSz = 64
+		}
+		str = fmt.Sprintf("%s %s, %s", op.String(),
+			formatVRegSized(i.rd.nr(), dstSz), formatVRegSized(i.rn.nr(), srcSz))
 	case fpuRRR:
 		size := is64SizeBitToSize(i.u3)
 		str = fmt.Sprintf("%s %s, %s, %s", fpuBinOp(i.u1).String(),
@@ -938,7 +948,24 @@ func (i *instruction) String() (str string) {
 	case loadFpuConst128:
 		panic("TODO")
 	case fpuToInt:
-		panic("TODO")
+		var op, src, dst string
+		if signed := i.u1 == 1; signed {
+			op = "fcvtzs"
+		} else {
+			op = "fcvtzu"
+		}
+		if src64 := i.u2 == 1; src64 {
+			src = formatVRegWidthVec(i.rn.nr(), vecArrangementD)
+		} else {
+			src = formatVRegWidthVec(i.rn.nr(), vecArrangementS)
+		}
+		if dst64 := i.u3 == 1; dst64 {
+			dst = formatVRegSized(i.rd.nr(), 64)
+		} else {
+			dst = formatVRegSized(i.rd.nr(), 32)
+		}
+		str = fmt.Sprintf("%s %s, %s", op, dst, src)
+
 	case intToFpu:
 		var op, src, dst string
 		if signed := i.u1 == 1; signed {
@@ -1315,6 +1342,21 @@ func (i *instruction) asUDF() {
 	i.kind = udf
 }
 
+func (i *instruction) asFpuToInt(rd, rn operand, rdSigned, src64bit, dst64bit bool) {
+	i.kind = fpuToInt
+	i.rn = rn
+	i.rd = rd
+	if rdSigned {
+		i.u1 = 1
+	}
+	if src64bit {
+		i.u2 = 1
+	}
+	if dst64bit {
+		i.u3 = 1
+	}
+}
+
 func (i *instruction) asIntToFpu(rd, rn operand, rnSigned, src64bit, dst64bit bool) {
 	i.kind = intToFpu
 	i.rn = rn
@@ -1472,6 +1514,7 @@ type fpuUniOp byte
 const (
 	fpuUniOpNeg fpuUniOp = iota
 	fpuUniOpCvt32To64
+	fpuUniOpCvt64To32
 	fpuUniOpSqrt
 	fpuUniOpRoundPlus
 	fpuUniOpRoundMinus
@@ -1487,6 +1530,8 @@ func (f fpuUniOp) String() string {
 		return "fneg"
 	case fpuUniOpCvt32To64:
 		return "fcvt"
+	case fpuUniOpCvt64To32:
+		return "fcvt"
 	case fpuUniOpSqrt:
 		return "fsqrt"
 	case fpuUniOpRoundPlus:
diff --git a/internal/engine/wazevo/backend/isa/arm64/instr_encoding.go b/internal/engine/wazevo/backend/isa/arm64/instr_encoding.go
index a3625be6..1b1ab41b 100644
--- a/internal/engine/wazevo/backend/isa/arm64/instr_encoding.go
+++ b/internal/engine/wazevo/backend/isa/arm64/instr_encoding.go
@@ -257,7 +257,7 @@ func (i *instruction) encode(c backend.Compiler) {
 		))
 	case brTableSequence:
 		encodeBrTableSequence(c, i.rn.reg(), i.targets)
-	case intToFpu:
+	case fpuToInt, intToFpu:
 		c.Emit4Bytes(encodeCnvBetweenFloatInt(i))
 	case fpuRR:
 		c.Emit4Bytes(encodeFloatDataOneSource(
@@ -322,6 +322,9 @@ func encodeFloatDataOneSource(op fpuUniOp, rd, rn uint32, dst64bit bool) uint32
 	switch op {
 	case fpuUniOpCvt32To64:
 		opcode = 0b000101
+	case fpuUniOpCvt64To32:
+		opcode = 0b000100
+		ptype = 0b01
 	case fpuUniOpNeg:
 		opcode = 0b000010
 		if dst64bit {
@@ -393,8 +396,26 @@ func encodeCnvBetweenFloatInt(i *instruction) uint32 {
 		} else {
 			ptype = 0b00
 		}
-	case fpuToInt:
-		panic("TODO")
+	case fpuToInt: // Either FCVTZU or FCVTZS.
+		rmode = 0b11
+
+		signed := i.u1 == 1
+		src64bit := i.u2 == 1
+		dst64bit := i.u3 == 1
+
+		if signed {
+			opcode = 0b000
+		} else {
+			opcode = 0b001
+		}
+		if dst64bit {
+			sf = 0b1
+		}
+		if src64bit {
+			ptype = 0b01
+		} else {
+			ptype = 0b00
+		}
 	}
 	return sf<<31 | 0b1111<<25 | ptype<<22 | 0b1<<21 | rmode<<19 | opcode<<16 | rn<<5 | rd
 }
diff --git a/internal/engine/wazevo/backend/isa/arm64/lower_instr.go b/internal/engine/wazevo/backend/isa/arm64/lower_instr.go
index 20dee76b..5cf140b2 100644
--- a/internal/engine/wazevo/backend/isa/arm64/lower_instr.go
+++ b/internal/engine/wazevo/backend/isa/arm64/lower_instr.go
@@ -205,6 +205,14 @@ func (m *machine) LowerInstr(instr *ssa.Instruction) {
 		x := instr.Arg()
 		result := instr.Return()
 		m.lowerPopcnt(x, result)
+	case ssa.OpcodeFcvtToSint:
+		x := instr.Arg()
+		result := instr.Return()
+		m.lowerFpuToInt(result, x, true, x.Type() == ssa.TypeF64, result.Type().Bits() == 64)
+	case ssa.OpcodeFcvtToUint:
+		x := instr.Arg()
+		result := instr.Return()
+		m.lowerFpuToInt(result, x, false, x.Type() == ssa.TypeF64, result.Type().Bits() == 64)
 	case ssa.OpcodeFcvtFromSint:
 		x := instr.Arg()
 		result := instr.Return()
@@ -213,6 +221,13 @@ func (m *machine) LowerInstr(instr *ssa.Instruction) {
 		x := instr.Arg()
 		result := instr.Return()
 		m.lowerIntToFpu(result, x, false, x.Type() == ssa.TypeI64, result.Type().Bits() == 64)
+	case ssa.OpcodeFdemote:
+		v := instr.Arg()
+		rn := m.getOperand_NR(m.compiler.ValueDefinition(v), extModeNone)
+		rd := operandNR(m.compiler.VRegOf(instr.Return()))
+		cnt := m.allocateInstr()
+		cnt.asFpuRR(fpuUniOpCvt64To32, rd, rn, false)
+		m.insert(cnt)
 	case ssa.OpcodeFpromote:
 		v := instr.Arg()
 		rn := m.getOperand_NR(m.compiler.ValueDefinition(v), extModeNone)
@@ -400,6 +415,14 @@ func (m *machine) lowerFpuUniOp(op fpuUniOp, in, out ssa.Value) {
 	m.insert(neg)
 }
 
+func (m *machine) lowerFpuToInt(dst, src ssa.Value, signed, src64bit, dst64bit bool) {
+	rn := m.getOperand_NR(m.compiler.ValueDefinition(src), extModeNone)
+	rd := operandNR(m.compiler.VRegOf(dst))
+	cvt := m.allocateInstr()
+	cvt.asFpuToInt(rd, rn, signed, src64bit, dst64bit)
+	m.insert(cvt)
+}
+
 func (m *machine) lowerIntToFpu(dst, src ssa.Value, signed, src64bit, dst64bit bool) {
 	rn := m.getOperand_NR(m.compiler.ValueDefinition(src), extModeNone)
 	rd := operandNR(m.compiler.VRegOf(dst))
diff --git a/internal/engine/wazevo/frontend/lower.go b/internal/engine/wazevo/frontend/lower.go
index da47fe4e..5770e350 100644
--- a/internal/engine/wazevo/frontend/lower.go
+++ b/internal/engine/wazevo/frontend/lower.go
@@ -472,9 +472,20 @@ func (c *Compiler) lowerOpcode(op wasm.Opcode) {
 		x := state.pop()
 		v := builder.AllocateInstruction().AsNearest(x).Insert(builder).Return()
 		state.push(v)
-
-	case wasm.OpcodeI64TruncF64S:
-		panic("TODO")
+	case wasm.OpcodeI64TruncF64S, wasm.OpcodeI64TruncF32S,
+		wasm.OpcodeI32TruncF64S, wasm.OpcodeI32TruncF32S,
+		wasm.OpcodeI64TruncF64U, wasm.OpcodeI64TruncF32U,
+		wasm.OpcodeI32TruncF64U, wasm.OpcodeI32TruncF32U:
+		if state.unreachable {
+			return
+		}
+		x := state.pop()
+		trunc := builder.AllocateInstruction()
+		trunc.AsFcvtToInt(x,
+			op == wasm.OpcodeI64TruncF64S || op == wasm.OpcodeI64TruncF32S || op == wasm.OpcodeI32TruncF32S || op == wasm.OpcodeI32TruncF64S,
+			op == wasm.OpcodeI64TruncF64S || op == wasm.OpcodeI64TruncF32S || op == wasm.OpcodeI64TruncF64U || op == wasm.OpcodeI64TruncF32U,
+		)
+		state.push(trunc.Insert(builder).Return())
 	case wasm.OpcodeI32ReinterpretF32:
 		if state.unreachable {
 			return
@@ -1253,6 +1264,14 @@ func (c *Compiler) lowerOpcode(op wasm.Opcode) {
 		)
 		builder.InsertInstruction(cvt)
 		state.push(cvt.Return())
+	case wasm.OpcodeF32DemoteF64:
+		if state.unreachable {
+			return
+		}
+		cvt := builder.AllocateInstruction()
+		cvt.AsFdemote(state.pop())
+		builder.InsertInstruction(cvt)
+		state.push(cvt.Return())
 	case wasm.OpcodeF64PromoteF32:
 		if state.unreachable {
 			return
diff --git a/internal/engine/wazevo/ssa/instructions.go b/internal/engine/wazevo/ssa/instructions.go
index c89d4795..1e42dec5 100644
--- a/internal/engine/wazevo/ssa/instructions.go
+++ b/internal/engine/wazevo/ssa/instructions.go
@@ -727,8 +727,7 @@ const (
 	// OpcodeFpromote promotes the given floating point value: `v = Fpromote x`.
 	OpcodeFpromote
 
-	// OpcodeFdemote ...
-	// `v = fdemote x`.
+	// OpcodeFdemote demotes the given float point value: `v = Fdemote x`.
 	OpcodeFdemote
 
 	// OpcodeFvdemote ...
@@ -866,8 +865,11 @@ var instructionSideEffects = [opcodeEnd]sideEffect{
 	OpcodeSelect:             sideEffectFalse,
 	OpcodeFmin:               sideEffectFalse,
 	OpcodeFneg:               sideEffectFalse,
+	OpcodeFcvtToSint:         sideEffectFalse,
+	OpcodeFcvtToUint:         sideEffectFalse,
 	OpcodeFcvtFromSint:       sideEffectFalse,
 	OpcodeFcvtFromUint:       sideEffectFalse,
+	OpcodeFdemote:            sideEffectFalse, // fixme check
 	OpcodeFpromote:           sideEffectFalse,
 	OpcodeBitcast:            sideEffectFalse,
 	OpcodeIreduce:            sideEffectFalse,
@@ -987,9 +989,12 @@ var instructionReturnTypes = [opcodeEnd]returnTypesFn{
 	OpcodeSload8:             returnTypesFnSingle,
 	OpcodeSload16:            returnTypesFnSingle,
 	OpcodeSload32:            returnTypesFnSingle,
+	OpcodeFcvtToSint:         returnTypesFnSingle,
+	OpcodeFcvtToUint:         returnTypesFnSingle,
 	OpcodeFcvtFromSint:       returnTypesFnSingle,
 	OpcodeFcvtFromUint:       returnTypesFnSingle,
 	OpcodeFneg:               returnTypesFnSingle,
+	OpcodeFdemote:            returnTypesFnF32,
 	OpcodeFpromote:           returnTypesFnF64,
 }
 
@@ -1562,6 +1567,13 @@ func (i *Instruction) BitcastData() (x Value, dstType Type) {
 	return i.v, i.typ
 }
 
+// AsFdemote initializes this instruction as an instruction with OpcodeFdemote.
+func (i *Instruction) AsFdemote(x Value) {
+	i.opcode = OpcodeFdemote
+	i.v = x
+	i.typ = TypeF32
+}
+
 // AsFpromote initializes this instruction as an instruction with OpcodeFpromote.
 func (i *Instruction) AsFpromote(x Value) {
 	i.opcode = OpcodeFpromote
@@ -1584,6 +1596,21 @@ func (i *Instruction) AsFcvtFromInt(x Value, signed bool, dst64bit bool) {
 	}
 }
 
+// AsFcvtToInt initializes this instruction as an instruction with either OpcodeFcvtToUint or OpcodeFcvtToSint
+func (i *Instruction) AsFcvtToInt(x Value, signed bool, dst64bit bool) {
+	if signed {
+		i.opcode = OpcodeFcvtToSint
+	} else {
+		i.opcode = OpcodeFcvtToUint
+	}
+	i.v = x
+	if dst64bit {
+		i.typ = TypeI64
+	} else {
+		i.typ = TypeI32
+	}
+}
+
 // AsSExtend initializes this instruction as a sign extension instruction with OpcodeSExtend.
 func (i *Instruction) AsSExtend(v Value, from, to byte) {
 	i.opcode = OpcodeSExtend
@@ -1735,8 +1762,9 @@ func (i *Instruction) Format(b Builder) string {
 		OpcodeSdiv, OpcodeUdiv, OpcodeFcopysign, OpcodeSrem, OpcodeUrem:
 		instSuffix = fmt.Sprintf(" %s, %s", i.v.Format(b), i.v2.Format(b))
 	case OpcodeUndefined:
-	case OpcodeClz, OpcodeCtz, OpcodePopcnt, OpcodeFneg, OpcodeFcvtFromSint, OpcodeFcvtFromUint, OpcodeFpromote,
-		OpcodeIreduce, OpcodeBitcast, OpcodeSqrt, OpcodeFabs, OpcodeCeil, OpcodeFloor, OpcodeTrunc, OpcodeNearest:
+	case OpcodeClz, OpcodeCtz, OpcodePopcnt, OpcodeFneg, OpcodeFcvtToSint, OpcodeFcvtToUint, OpcodeFcvtFromSint,
+		OpcodeFcvtFromUint, OpcodeFdemote, OpcodeFpromote, OpcodeIreduce, OpcodeBitcast, OpcodeSqrt, OpcodeFabs,
+		OpcodeCeil, OpcodeFloor, OpcodeTrunc, OpcodeNearest:
 		instSuffix = " " + i.v.Format(b)
 	default:
 		panic(fmt.Sprintf("TODO: format for %s", i.opcode))


Test patch:
diff --git a/internal/engine/wazevo/backend/backend_test.go b/internal/engine/wazevo/backend/backend_test.go
index 8a1d31f9..b19cc0a7 100644
--- a/internal/engine/wazevo/backend/backend_test.go
+++ b/internal/engine/wazevo/backend/backend_test.go
@@ -1596,6 +1596,52 @@ L1 (SSA Block: blk0):
 	str w11, [sp, #0x10]
 	ldr x30, [sp], #0x10
 	ret
+`,
+		},
+		{
+			name: "float_conversions",
+			m:    testcases.FloatConversions.Module,
+			afterLoweringARM64: `
+L1 (SSA Block: blk0):
+	mov v2?.8b, v0.8b
+	mov v3?.8b, v1.8b
+	fcvtzs x4?, d2?
+	fcvtzs x5?, s3?
+	fcvtzs w6?, d2?
+	fcvtzs w7?, s3?
+	fcvtzu x8?, d2?
+	fcvtzu x9?, s3?
+	fcvtzu w10?, d2?
+	fcvtzu w11?, s3?
+	fcvt s12?, d2?
+	fcvt d13?, s3?
+	mov v1.8b, v13?.8b
+	mov v0.8b, v12?.8b
+	mov x7, x11?
+	mov x6, x10?
+	mov x5, x9?
+	mov x4, x8?
+	mov x3, x7?
+	mov x2, x6?
+	mov x1, x5?
+	mov x0, x4?
+	ret
+`,
+			afterFinalizeARM64: `
+L1 (SSA Block: blk0):
+	str x30, [sp, #-0x10]!
+	fcvtzs x0, d0
+	fcvtzs x1, s1
+	fcvtzs w2, d0
+	fcvtzs w3, s1
+	fcvtzu x4, d0
+	fcvtzu x5, s1
+	fcvtzu w6, d0
+	fcvtzu w7, s1
+	fcvt s0, d0
+	fcvt d1, s1
+	ldr x30, [sp], #0x10
+	ret
 `,
 		},
 		{
diff --git a/internal/engine/wazevo/backend/isa/arm64/instr_encoding_test.go b/internal/engine/wazevo/backend/isa/arm64/instr_encoding_test.go
index d2fdd97d..d839b743 100644
--- a/internal/engine/wazevo/backend/isa/arm64/instr_encoding_test.go
+++ b/internal/engine/wazevo/backend/isa/arm64/instr_encoding_test.go
@@ -528,6 +528,9 @@ func TestInstruction_encode(t *testing.T) {
 		{want: "41c0221e", setup: func(i *instruction) {
 			i.asFpuRR(fpuUniOpCvt32To64, operandNR(v1VReg), operandNR(v2VReg), true)
 		}},
+		{want: "4140621e", setup: func(i *instruction) {
+			i.asFpuRR(fpuUniOpCvt64To32, operandNR(v1VReg), operandNR(v2VReg), true)
+		}},
 		{want: "4140211e", setup: func(i *instruction) {
 			i.asFpuRR(fpuUniOpNeg, operandNR(v1VReg), operandNR(v2VReg), false)
 		}},
@@ -578,6 +581,21 @@ func TestInstruction_encode(t *testing.T) {
 				}
 			}
 		}},
+		{want: "4201391e4201399e4201791e4201799e4201381e4201389e4201781e4201789e", setup: func(i *instruction) {
+			i.asNop0()
+			cur := i
+			trueFalse := []bool{false, true}
+			for _, rnSigned := range trueFalse {
+				for _, src64bit := range trueFalse {
+					for _, dst64bit := range trueFalse {
+						i := &instruction{prev: cur}
+						cur.next = i
+						i.asFpuToInt(operandNR(v2VReg), operandNR(x10VReg), rnSigned, src64bit, dst64bit)
+						cur = i
+					}
+				}
+			}
+		}},
 	} {
 		tc := tc
 		t.Run(tc.want, func(t *testing.T) {
diff --git a/internal/engine/wazevo/e2e_test.go b/internal/engine/wazevo/e2e_test.go
index ca756481..4f61809a 100644
--- a/internal/engine/wazevo/e2e_test.go
+++ b/internal/engine/wazevo/e2e_test.go
@@ -62,7 +62,7 @@ func TestSpectestV1(t *testing.T) {
 		{name: "f64_bitwise"},
 		{name: "f64_cmp"},
 		{name: "fac"},
-		//{name: "float_exprs"},
+		{name: "float_exprs"},
 		{name: "float_literals"},
 		{name: "float_memory"},
 		{name: "float_misc"},
@@ -83,8 +83,8 @@ func TestSpectestV1(t *testing.T) {
 		{name: "load"},
 		//{name: "loop"},
 		{name: "local_get"},
-		//{name: "local_set"},
-		//{name: "local_tee"},
+		{name: "local_set"},
+		{name: "local_tee"},
 		{name: "memory"},
 		{name: "memory_size"},
 		{name: "memory_grow"},
diff --git a/internal/engine/wazevo/frontend/frontend_test.go b/internal/engine/wazevo/frontend/frontend_test.go
index 61892bd2..0ddd1192 100644
--- a/internal/engine/wazevo/frontend/frontend_test.go
+++ b/internal/engine/wazevo/frontend/frontend_test.go
@@ -660,6 +660,23 @@ blk0: (exec_ctx:i64, module_ctx:i64, v2:f32, v3:f32, v4:f64, v5:f64)
 	v16:i32 = Fcmp le, v4, v5
 	v17:i32 = Fcmp ge, v4, v5
 	Jump blk_ret, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17
+`,
+		},
+		{
+			name: "float conversions", m: testcases.FloatConversions.Module,
+			exp: `
+blk0: (exec_ctx:i64, module_ctx:i64, v2:f64, v3:f32)
+	v4:i64 = FcvtToSint v2
+	v5:i64 = FcvtToSint v3
+	v6:i32 = FcvtToSint v2
+	v7:i32 = FcvtToSint v3
+	v8:i64 = FcvtToUint v2
+	v9:i64 = FcvtToUint v3
+	v10:i32 = FcvtToUint v2
+	v11:i32 = FcvtToUint v3
+	v12:f32 = Fdemote v2
+	v13:f64 = Fpromote v3
+	Jump blk_ret, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13
 `,
 		},
 		{
diff --git a/internal/engine/wazevo/testcases/testcases.go b/internal/engine/wazevo/testcases/testcases.go
index 16cac353..81e9eaf2 100644
--- a/internal/engine/wazevo/testcases/testcases.go
+++ b/internal/engine/wazevo/testcases/testcases.go
@@ -856,6 +856,45 @@ var (
 			wasm.OpcodeEnd,
 		}, []wasm.ValueType{}),
 	}
+	FloatConversions = TestCase{
+		Name: "float_conversions",
+		Module: SingleFunctionModule(wasm.FunctionType{
+			Params:  []wasm.ValueType{f64, f32},
+			Results: []wasm.ValueType{i64, i64, i32, i32, i64, i64, i32, i32, f32, f64},
+		}, []byte{
+			wasm.OpcodeLocalGet, 0,
+			wasm.OpcodeI64TruncF64S,
+
+			wasm.OpcodeLocalGet, 1,
+			wasm.OpcodeI64TruncF32S,
+
+			wasm.OpcodeLocalGet, 0,
+			wasm.OpcodeI32TruncF64S,
+
+			wasm.OpcodeLocalGet, 1,
+			wasm.OpcodeI32TruncF32S,
+
+			wasm.OpcodeLocalGet, 0,
+			wasm.OpcodeI64TruncF64U,
+
+			wasm.OpcodeLocalGet, 1,
+			wasm.OpcodeI64TruncF32U,
+
+			wasm.OpcodeLocalGet, 0,
+			wasm.OpcodeI32TruncF64U,
+
+			wasm.OpcodeLocalGet, 1,
+			wasm.OpcodeI32TruncF32U,
+
+			wasm.OpcodeLocalGet, 0,
+			wasm.OpcodeF32DemoteF64,
+
+			wasm.OpcodeLocalGet, 1,
+			wasm.OpcodeF64PromoteF32,
+
+			wasm.OpcodeEnd,
+		}, []wasm.ValueType{}),
+	}
 	FibonacciRecursive = TestCase{
 		Name: "recursive_fibonacci",
 		Module: SingleFunctionModule(i32_i32, []byte{