From 3579031f8b0ee710392d0419cc3848402b90d094 Mon Sep 17 00:00:00 2001 From: Takeshi Yoneda Date: Tue, 31 May 2022 13:50:39 +0900 Subject: [PATCH] init Signed-off-by: Takeshi Yoneda --- internal/asm/amd64/assembler.go | 37 +- internal/asm/amd64/consts.go | 130 +- internal/asm/amd64/impl.go | 241 ++- internal/asm/amd64/impl_staticconst.go | 124 ++ internal/asm/amd64/impl_staticconst_test.go | 171 ++ internal/asm/amd64/impl_test.go | 27 + internal/asm/arm64/assembler.go | 27 +- internal/asm/arm64/consts.go | 96 +- internal/asm/arm64/impl.go | 645 +++++- internal/asm/arm64/impl_test.go | 935 ++++++++- internal/asm/assembler.go | 9 + internal/engine/compiler/compiler.go | 23 +- .../engine/compiler/compiler_memory_test.go | 6 +- .../engine/compiler/compiler_numeric_test.go | 12 +- .../engine/compiler/compiler_stack_test.go | 8 +- .../compiler/compiler_value_location.go | 6 + internal/engine/compiler/compiler_vec_test.go | 1759 +++++++++++++++++ internal/engine/compiler/engine.go | 32 +- internal/engine/compiler/impl_arm64.go | 44 +- internal/engine/compiler/impl_vec_amd64.go | 530 ++++- .../engine/compiler/impl_vec_amd64_test.go | 64 + internal/engine/compiler/impl_vec_arm64.go | 585 +++++- internal/engine/interpreter/interpreter.go | 527 ++++- .../asm/amd64_debug/debug_assembler.go | 38 +- .../asm/amd64_debug/golang_asm.go | 93 +- .../asm/amd64_debug/impl_test.go | 157 +- .../asm/arm64_debug/debug_assembler.go | 47 +- .../asm/arm64_debug/golang_asm.go | 134 +- .../asm/arm64_debug/impl_test.go | 355 ++-- .../integration_test/spectest/v2/spec_test.go | 44 +- .../spectest/v2/testdata/simd_lane.json | 948 ++++----- internal/wasm/func_validation.go | 282 ++- internal/wasm/instruction.go | 128 +- internal/wazeroir/compiler.go | 416 +++- internal/wazeroir/compiler_test.go | 14 +- internal/wazeroir/format.go | 4 +- internal/wazeroir/operations.go | 241 ++- internal/wazeroir/signature.go | 111 +- 38 files changed, 7944 insertions(+), 1106 deletions(-) create mode 100644 internal/asm/amd64/impl_staticconst.go create mode 100644 internal/asm/amd64/impl_staticconst_test.go create mode 100644 internal/engine/compiler/compiler_vec_test.go create mode 100644 internal/engine/compiler/impl_vec_amd64_test.go diff --git a/internal/asm/amd64/assembler.go b/internal/asm/amd64/assembler.go index 7fab2bf0c3a..0dec0f5641b 100644 --- a/internal/asm/amd64/assembler.go +++ b/internal/asm/amd64/assembler.go @@ -28,6 +28,18 @@ type Assembler interface { dstReg asm.Register, ) + // CompileMemoryWithIndexAndArgToRegister is the same as CompileMemoryWithIndexToRegister except that this + // also accepts one argument. + CompileMemoryWithIndexAndArgToRegister( + instruction asm.Instruction, + srcBaseReg asm.Register, + srcOffsetConst int64, + srcIndex asm.Register, + srcScale int16, + dstReg asm.Register, + arg byte, + ) + // CompileRegisterToMemoryWithIndex adds an instruction where source operand is the register `SrcReg`, // and the destination is the memory address specified as `dstBaseReg + dstOffsetConst + dstIndex*dstScale` // Note: dstScale must be one of 1, 2, 4, 8. @@ -40,6 +52,18 @@ type Assembler interface { dstScale int16, ) + // CompileRegisterToMemoryWithIndexAndArg is the same as CompileRegisterToMemoryWithIndex except that this + // also accepts one argument. + CompileRegisterToMemoryWithIndexAndArg( + instruction asm.Instruction, + srcReg asm.Register, + dstBaseReg asm.Register, + dstOffsetConst int64, + dstIndex asm.Register, + dstScale int16, + arg byte, + ) + // CompileRegisterToConst adds an instruction where source operand is the register `srcRegister`, // and the destination is the const `value`. CompileRegisterToConst(instruction asm.Instruction, srcRegister asm.Register, value int64) asm.Node @@ -57,15 +81,14 @@ type Assembler interface { CompileNoneToMemory(instruction asm.Instruction, baseReg asm.Register, offset int64) // CompileConstToMemory adds an instruction where source operand is the constant `value` and - // the destination is the memory address specified as `dstbaseReg+dstOffset`. - CompileConstToMemory(instruction asm.Instruction, value int64, dstbaseReg asm.Register, dstOffset int64) asm.Node + // the destination is the memory address specified as `dstBaseReg+dstOffset`. + CompileConstToMemory(instruction asm.Instruction, value int64, dstBaseReg asm.Register, dstOffset int64) asm.Node // CompileMemoryToConst adds an instruction where source operand is the memory address, and // the destination is the constant `value`. CompileMemoryToConst(instruction asm.Instruction, srcBaseReg asm.Register, srcOffset int64, value int64) asm.Node -} -// Mode represents a Mode for specific instruction. -// For example, ROUND** instructions' behavior can be modified "Mode" constant. -// See https://www.felixcloutier.com/x86/roundss for ROUNDSS as an example. -type Mode = byte + // CompileLoadStaticConstToRegister adds an instruction where the source operand is StaticConstant located in the memory + // and the destination is the dstReg. + CompileLoadStaticConstToRegister(instruction asm.Instruction, c asm.StaticConst, dstReg asm.Register) error +} diff --git a/internal/asm/amd64/consts.go b/internal/asm/amd64/consts.go index a6f63b1e8b6..438f8418039 100644 --- a/internal/asm/amd64/consts.go +++ b/internal/asm/amd64/consts.go @@ -305,7 +305,15 @@ const ( UD2 // MOVDQU is the MOVDQU instruction. https://www.felixcloutier.com/x86/movdqu:vmovdqu8:vmovdqu16:vmovdqu32:vmovdqu64 MOVDQU - // PINSRQ is the PINSQR instruction. https://www.felixcloutier.com/x86/pinsrb:pinsrd:pinsrq + // MOVDQA is the MOVDQA instruction. + MOVDQA + // PINSRB is the PINSRB instruction. https://www.felixcloutier.com/x86/pinsrb:pinsrd:pinsrq + PINSRB + // PINSRW is the mode PINSQRW instruction. https://www.felixcloutier.com/x86/pinsrw + PINSRW + // PINSRD is the PINSRD instruction. https://www.felixcloutier.com/x86/pinsrb:pinsrd:pinsrq + PINSRD + // PINSRQ is the PINSQRQ instruction. https://www.felixcloutier.com/x86/pinsrb:pinsrd:pinsrq PINSRQ // PADDB is the PADDB instruction. https://www.felixcloutier.com/x86/paddb:paddw:paddd:paddq PADDB @@ -315,10 +323,66 @@ const ( PADDL // PADDQ is the PADDQ instruction. https://www.felixcloutier.com/x86/paddb:paddw:paddd:paddq PADDQ + // PSUBB is the PSUBB instruction. https://www.felixcloutier.com/x86/psubb:psubw:psubd + PSUBB + // PSUBW is the PSUBW instruction. https://www.felixcloutier.com/x86/psubb:psubw:psubd + PSUBW + // PSUBL is the PSUBL instruction. https://www.felixcloutier.com/x86/psubb:psubw:psubd + PSUBL + // PSUBQ is the PSUBQ instruction. https://www.felixcloutier.com/x86/paddb:paddw:paddd:paddq + PSUBQ // ADDPS is the ADDPS instruction. https://www.felixcloutier.com/x86/addps ADDPS // ADDPD is the ADDPD instruction. https://www.felixcloutier.com/x86/addpd ADDPD + // SUBPS is the SUBPS instruction. https://www.felixcloutier.com/x86/addps + SUBPS + // SUBPD is the SUBPD instruction. https://www.felixcloutier.com/x86/subpd + SUBPD + // PMOVSXBW is the PMOVSXBW instruction https://www.felixcloutier.com/x86/pmovsx + PMOVSXBW + // PMOVSXWD is the PMOVSXWD instruction https://www.felixcloutier.com/x86/pmovsx + PMOVSXWD + // PMOVSXDQ is the PMOVSXDQ instruction https://www.felixcloutier.com/x86/pmovsx + PMOVSXDQ + // PMOVZXBW is the PMOVZXBW instruction https://www.felixcloutier.com/x86/pmovzx + PMOVZXBW + // PMOVZXWD is the PMOVZXWD instruction https://www.felixcloutier.com/x86/pmovzx + PMOVZXWD + // PMOVZXDQ is the PMOVZXDQ instruction https://www.felixcloutier.com/x86/pmovzx + PMOVZXDQ + // PSHUFB is the PSHUFB instruction https://www.felixcloutier.com/x86/pshufb + PSHUFB + // PSHUFD is the PSHUFD instruction https://www.felixcloutier.com/x86/pshufd + PSHUFD + // PXOR is the PXOR instruction https://www.felixcloutier.com/x86/pxor + PXOR + // PEXTRB is the PEXTRB instruction https://www.felixcloutier.com/x86/pextrb:pextrd:pextrq + PEXTRB + // PEXTRW is the PEXTRW instruction https://www.felixcloutier.com/x86/pextrw + PEXTRW + // PEXTRD is the PEXTRD instruction https://www.felixcloutier.com/x86/pextrb:pextrd:pextrq + PEXTRD + // PEXTRQ is the PEXTRQ instruction https://www.felixcloutier.com/x86/pextrb:pextrd:pextrq + PEXTRQ + // MOVLHPS is the MOVLHPS instruction https://www.felixcloutier.com/x86/movlhps + MOVLHPS + // INSERTPS is the INSERTPS instruction https://www.felixcloutier.com/x86/insertps + INSERTPS + // PTEST is the PTEST instruction https://www.felixcloutier.com/x86/ptest + PTEST + // PCMPEQB is the PCMPEQB instruction https://www.felixcloutier.com/x86/pcmpeqb:pcmpeqw:pcmpeqd + PCMPEQB + // PCMPEQW is the PCMPEQW instruction https://www.felixcloutier.com/x86/pcmpeqb:pcmpeqw:pcmpeqd + PCMPEQW + // PCMPEQD is the PCMPEQD instruction https://www.felixcloutier.com/x86/pcmpeqb:pcmpeqw:pcmpeqd + PCMPEQD + // PCMPEQQ is the PCMPEQQ instruction https://www.felixcloutier.com/x86/pcmpeqq + PCMPEQQ + // PADDUSB is the PADDUSB instruction https://www.felixcloutier.com/x86/paddusb:paddusw + PADDUSB + // MOVSD is the MOVSD instruction https://www.felixcloutier.com/x86/movsd + MOVSD ) // InstructionName returns the name for an instruction @@ -586,6 +650,12 @@ func InstructionName(instruction asm.Instruction) string { return "UD2" case MOVDQU: return "MOVDQU" + case PINSRB: + return "PINSRB" + case PINSRW: + return "PINSRW" + case PINSRD: + return "PINSRD" case PINSRQ: return "PINSRQ" case PADDB: @@ -600,6 +670,64 @@ func InstructionName(instruction asm.Instruction) string { return "ADDPS" case ADDPD: return "ADDPD" + case PSUBB: + return "PSUBB" + case PSUBW: + return "PSUBW" + case PSUBL: + return "PSUBL" + case PSUBQ: + return "PSUBQ" + case SUBPS: + return "SUBPS" + case SUBPD: + return "SUBPD" + case PMOVSXBW: + return "PMOVSXBW" + case PMOVSXWD: + return "PMOVSXWD" + case PMOVSXDQ: + return "PMOVSXDQ" + case PMOVZXBW: + return "PMOVZXBW" + case PMOVZXWD: + return "PMOVZXWD" + case PMOVZXDQ: + return "PMOVZXDQ" + case PSHUFB: + return "PSHUFB" + case PSHUFD: + return "PSHUFD" + case PXOR: + return "PXOR" + case PEXTRB: + return "PEXTRB" + case PEXTRW: + return "PEXTRW" + case PEXTRD: + return "PEXTRD" + case PEXTRQ: + return "PEXTRQ" + case INSERTPS: + return "INSERTPS" + case MOVLHPS: + return "MOVLHPS" + case PTEST: + return "PTEST" + case PCMPEQB: + return "PCMPEQB" + case PCMPEQW: + return "PCMPEQW" + case PCMPEQD: + return "PCMPEQD" + case PCMPEQQ: + return "PCMPEQQ" + case PADDUSB: + return "PADDUSB" + case MOVDQA: + return "MOVDQA" + case MOVSD: + return "MOVSD" } return "Unknown" } diff --git a/internal/asm/amd64/impl.go b/internal/asm/amd64/impl.go index 0baf5125464..e01b2dd31e2 100644 --- a/internal/asm/amd64/impl.go +++ b/internal/asm/amd64/impl.go @@ -38,6 +38,8 @@ type NodeImpl struct { // JumpOrigins hold all the nodes trying to jump into this node. In other words, all the nodes with .JumpTarget == this. JumpOrigins map[*NodeImpl]struct{} + + staticConst asm.StaticConst } type NodeFlag byte @@ -45,10 +47,10 @@ type NodeFlag byte const ( // NodeFlagInitializedForEncoding is always set to indicate that node is already initialized. Notably, this is used to judge // whether a jump is backward or forward before encoding. - NodeFlagInitializedForEncoding NodeFlag = (1 << iota) + NodeFlagInitializedForEncoding NodeFlag = 1 << iota NodeFlagBackwardJump // NodeFlagShortForwardJump is set to false by default and only used by forward branch jumps, which means .JumpTarget != nil and - // the target node is encoded afoter this node. False by default means that that we Encode all the jumps with JumpTarget + // the target node is encoded after this node. False by default means that we Encode all the jumps with JumpTarget // as short jump (i.e. relative signed 8-bit integer offset jump) and try to Encode as small as possible. NodeFlagShortForwardJump ) @@ -163,6 +165,7 @@ const ( OperandTypeRegister OperandTypeMemory OperandTypeConst + OperandTypeStaticConst OperandTypeBranch ) @@ -178,6 +181,8 @@ func (o OperandType) String() (ret string) { ret = "const" case OperandTypeBranch: ret = "branch" + case OperandTypeStaticConst: + ret = "static-const" } return } @@ -186,18 +191,19 @@ func (o OperandType) String() (ret string) { type OperandTypes struct{ src, dst OperandType } var ( - OperandTypesNoneToNone = OperandTypes{OperandTypeNone, OperandTypeNone} - OperandTypesNoneToRegister = OperandTypes{OperandTypeNone, OperandTypeRegister} - OperandTypesNoneToMemory = OperandTypes{OperandTypeNone, OperandTypeMemory} - OperandTypesNoneToBranch = OperandTypes{OperandTypeNone, OperandTypeBranch} - OperandTypesRegisterToNone = OperandTypes{OperandTypeRegister, OperandTypeNone} - OperandTypesRegisterToRegister = OperandTypes{OperandTypeRegister, OperandTypeRegister} - OperandTypesRegisterToMemory = OperandTypes{OperandTypeRegister, OperandTypeMemory} - OperandTypesRegisterToConst = OperandTypes{OperandTypeRegister, OperandTypeConst} - OperandTypesMemoryToRegister = OperandTypes{OperandTypeMemory, OperandTypeRegister} - OperandTypesMemoryToConst = OperandTypes{OperandTypeMemory, OperandTypeConst} - OperandTypesConstToRegister = OperandTypes{OperandTypeConst, OperandTypeRegister} - OperandTypesConstToMemory = OperandTypes{OperandTypeConst, OperandTypeMemory} + OperandTypesNoneToNone = OperandTypes{OperandTypeNone, OperandTypeNone} + OperandTypesNoneToRegister = OperandTypes{OperandTypeNone, OperandTypeRegister} + OperandTypesNoneToMemory = OperandTypes{OperandTypeNone, OperandTypeMemory} + OperandTypesNoneToBranch = OperandTypes{OperandTypeNone, OperandTypeBranch} + OperandTypesRegisterToNone = OperandTypes{OperandTypeRegister, OperandTypeNone} + OperandTypesRegisterToRegister = OperandTypes{OperandTypeRegister, OperandTypeRegister} + OperandTypesRegisterToMemory = OperandTypes{OperandTypeRegister, OperandTypeMemory} + OperandTypesRegisterToConst = OperandTypes{OperandTypeRegister, OperandTypeConst} + OperandTypesMemoryToRegister = OperandTypes{OperandTypeMemory, OperandTypeRegister} + OperandTypesMemoryToConst = OperandTypes{OperandTypeMemory, OperandTypeConst} + OperandTypesConstToRegister = OperandTypes{OperandTypeConst, OperandTypeRegister} + OperandTypesConstToMemory = OperandTypes{OperandTypeConst, OperandTypeMemory} + OperandTypesStaticConstToRegister = OperandTypes{OperandTypeStaticConst, OperandTypeRegister} ) // String implements fmt.Stringer @@ -205,17 +211,26 @@ func (o OperandTypes) String() string { return fmt.Sprintf("from:%s,to:%s", o.src, o.dst) } +const defaultMaxDisplacementForConstantPool = 1 << 30 + // AssemblerImpl implements Assembler. type AssemblerImpl struct { asm.BaseAssemblerImpl - EnablePadding bool - Root, Current *NodeImpl - Buf *bytes.Buffer - ForceReAssemble bool + EnablePadding bool + Root, Current *NodeImpl + nodeCount int + Buf *bytes.Buffer + ForceReAssemble bool + MaxDisplacementForConstantPool int + + pool constPool } +var _ Assembler = &AssemblerImpl{} + func NewAssemblerImpl() *AssemblerImpl { - return &AssemblerImpl{Buf: bytes.NewBuffer(nil), EnablePadding: true} + return &AssemblerImpl{Buf: bytes.NewBuffer(nil), EnablePadding: true, pool: newConstPool(), + MaxDisplacementForConstantPool: defaultMaxDisplacementForConstantPool} } // newNode creates a new Node and appends it into the linked list. @@ -226,8 +241,8 @@ func (a *AssemblerImpl) newNode(instruction asm.Instruction, types OperandTypes) Types: types, JumpOrigins: map[*NodeImpl]struct{}{}, } - a.addNode(n) + a.nodeCount++ return n } @@ -277,6 +292,8 @@ func (a *AssemblerImpl) EncodeNode(n *NodeImpl) (err error) { err = a.EncodeConstToMemory(n) case OperandTypesMemoryToConst: err = a.EncodeMemoryToConst(n) + case OperandTypesStaticConstToRegister: + err = a.encodeStaticConstToRegister(n) default: err = fmt.Errorf("encoder undefined for [%s] operand type", n.Types) } @@ -288,7 +305,7 @@ func (a *AssemblerImpl) Assemble() ([]byte, error) { a.InitializeNodesForEncoding() // Continue encoding until we are not forced to re-assemble which happens when - // an short relative jump ends up the offset larger than 8-bit length. + // a short relative jump ends up the offset larger than 8-bit length. for { err := a.Encode() if err != nil { @@ -318,9 +335,7 @@ func (a *AssemblerImpl) Assemble() ([]byte, error) { // InitializeNodesForEncoding initializes NodeImpl.Flag and determine all the jumps // are forward or backward jump. func (a *AssemblerImpl) InitializeNodesForEncoding() { - var count int for n := a.Root; n != nil; n = n.Next { - count++ n.Flag |= NodeFlagInitializedForEncoding if target := n.JumpTarget; target != nil { if target.isInitializedForEncoding() { @@ -336,7 +351,7 @@ func (a *AssemblerImpl) InitializeNodesForEncoding() { } // Roughly allocate the buffer by assuming an instruction has 5-bytes length on average. - a.Buf.Grow(count * 5) + a.Buf.Grow(a.nodeCount * 5) } func (a *AssemblerImpl) Encode() (err error) { @@ -361,6 +376,8 @@ func (a *AssemblerImpl) Encode() (err error) { err = fmt.Errorf("invalid relative forward jumps: %w", err) break } + + a.maybeFlushConstants(n.Next == nil) } return } @@ -600,7 +617,7 @@ func (a *AssemblerImpl) CompileReadInstructionAddress( n.readInstructionAddressBeforeTargetInstruction = beforeAcquisitionTargetInstruction } -// CompileRegisterToRegisterWithArg implements the same method as documented on asm_arm64.Assembler. +// CompileRegisterToRegisterWithArg implements the same method as documented on amd64.Assembler. func (a *AssemblerImpl) CompileRegisterToRegisterWithArg( instruction asm.Instruction, from, to asm.Register, @@ -612,7 +629,7 @@ func (a *AssemblerImpl) CompileRegisterToRegisterWithArg( n.Arg = arg } -// CompileMemoryWithIndexToRegister implements the same method as documented on asm_arm64.Assembler. +// CompileMemoryWithIndexToRegister implements the same method as documented on amd64.Assembler. func (a *AssemblerImpl) CompileMemoryWithIndexToRegister( instruction asm.Instruction, srcBaseReg asm.Register, @@ -629,7 +646,26 @@ func (a *AssemblerImpl) CompileMemoryWithIndexToRegister( n.DstReg = dstReg } -// CompileRegisterToMemoryWithIndex implements the same method as documented on asm_arm64.Assembler. +// CompileMemoryWithIndexAndArgToRegister implements the same method as documented on amd64.Assembler. +func (a *AssemblerImpl) CompileMemoryWithIndexAndArgToRegister( + instruction asm.Instruction, + srcBaseReg asm.Register, + srcOffsetConst asm.ConstantValue, + srcIndex asm.Register, + srcScale int16, + dstReg asm.Register, + arg byte, +) { + n := a.newNode(instruction, OperandTypesMemoryToRegister) + n.SrcReg = srcBaseReg + n.SrcConst = srcOffsetConst + n.SrcMemIndex = srcIndex + n.SrcMemScale = byte(srcScale) + n.DstReg = dstReg + n.Arg = arg +} + +// CompileRegisterToMemoryWithIndex implements the same method as documented on amd64.Assembler. func (a *AssemblerImpl) CompileRegisterToMemoryWithIndex( instruction asm.Instruction, srcReg, dstBaseReg asm.Register, @@ -645,7 +681,25 @@ func (a *AssemblerImpl) CompileRegisterToMemoryWithIndex( n.DstMemScale = byte(dstScale) } -// CompileRegisterToConst implements the same method as documented on asm_arm64.Assembler. +// CompileRegisterToMemoryWithIndexAndArg implements the same method as documented on amd64.Assembler. +func (a *AssemblerImpl) CompileRegisterToMemoryWithIndexAndArg( + instruction asm.Instruction, + srcReg, dstBaseReg asm.Register, + dstOffsetConst asm.ConstantValue, + dstIndex asm.Register, + dstScale int16, + arg byte, +) { + n := a.newNode(instruction, OperandTypesRegisterToMemory) + n.SrcReg = srcReg + n.DstReg = dstBaseReg + n.DstConst = dstOffsetConst + n.DstMemIndex = dstIndex + n.DstMemScale = byte(dstScale) + n.Arg = arg +} + +// CompileRegisterToConst implements the same method as documented on amd64.Assembler. func (a *AssemblerImpl) CompileRegisterToConst( instruction asm.Instruction, srcRegister asm.Register, @@ -657,19 +711,19 @@ func (a *AssemblerImpl) CompileRegisterToConst( return n } -// CompileRegisterToNone implements the same method as documented on asm_arm64.Assembler. +// CompileRegisterToNone implements the same method as documented on amd64.Assembler. func (a *AssemblerImpl) CompileRegisterToNone(instruction asm.Instruction, register asm.Register) { n := a.newNode(instruction, OperandTypesRegisterToNone) n.SrcReg = register } -// CompileNoneToRegister implements the same method as documented on asm_arm64.Assembler. +// CompileNoneToRegister implements the same method as documented on amd64.Assembler. func (a *AssemblerImpl) CompileNoneToRegister(instruction asm.Instruction, register asm.Register) { n := a.newNode(instruction, OperandTypesNoneToRegister) n.DstReg = register } -// CompileNoneToMemory implements the same method as documented on asm_arm64.Assembler. +// CompileNoneToMemory implements the same method as documented on amd64.Assembler. func (a *AssemblerImpl) CompileNoneToMemory( instruction asm.Instruction, baseReg asm.Register, @@ -680,7 +734,7 @@ func (a *AssemblerImpl) CompileNoneToMemory( n.DstConst = offset } -// CompileConstToMemory implements the same method as documented on asm_arm64.Assembler. +// CompileConstToMemory implements the same method as documented on amd64.Assembler. func (a *AssemblerImpl) CompileConstToMemory( instruction asm.Instruction, value asm.ConstantValue, @@ -694,7 +748,7 @@ func (a *AssemblerImpl) CompileConstToMemory( return n } -// CompileMemoryToConst implements the same method as documented on asm_arm64.Assembler. +// CompileMemoryToConst implements the same method as documented on amd64.Assembler. func (a *AssemblerImpl) CompileMemoryToConst( instruction asm.Instruction, srcBaseReg asm.Register, @@ -907,7 +961,7 @@ func (a *AssemblerImpl) ResolveForwardRelativeJumps(target *NodeImpl) (err error offset := offsetInBinary - (int64(origin.OffsetInBinary()) + instructionLen) if shortJump { if offset > math.MaxInt8 { - // This forces reassemble in the outer loop inside of AssemblerImpl.Assemble(). + // This forces reassemble in the outer loop inside AssemblerImpl.Assemble(). a.ForceReAssemble = true // From the next reAssemble phases, this forward jump will be encoded long jump and // allocate 32-bit offset bytes by default. This means that this `origin` node @@ -1023,7 +1077,7 @@ var registerToRegisterOpcode = map[asm.Instruction]struct { mandatoryPrefix byte srcOnModRMReg bool isSrc8bit bool - needMode bool + needArg bool requireSrcFloat, requireDstFloat bool }{ // https://www.felixcloutier.com/x86/add @@ -1089,6 +1143,7 @@ var registerToRegisterOpcode = map[asm.Instruction]struct { MOVBLSX: {opcode: []byte{0x0f, 0xbe}, isSrc8bit: true}, // https://www.felixcloutier.com/x86/movzx MOVBLZX: {opcode: []byte{0x0f, 0xb6}, isSrc8bit: true}, + MOVWLZX: {opcode: []byte{0x0f, 0xb7}, isSrc8bit: true}, // https://www.felixcloutier.com/x86/movsx:movsxd MOVBQSX: {opcode: []byte{0x0f, 0xbe}, rPrefix: RexPrefixW, isSrc8bit: true}, // https://www.felixcloutier.com/x86/movsx:movsxd @@ -1112,9 +1167,9 @@ var registerToRegisterOpcode = map[asm.Instruction]struct { POPCNTL: {mandatoryPrefix: 0xf3, opcode: []byte{0x0f, 0xb8}}, POPCNTQ: {mandatoryPrefix: 0xf3, opcode: []byte{0x0f, 0xb8}, rPrefix: RexPrefixW}, // https://www.felixcloutier.com/x86/roundss - ROUNDSS: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x3a, 0x0a}, needMode: true, requireSrcFloat: true, requireDstFloat: true}, + ROUNDSS: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x3a, 0x0a}, needArg: true, requireSrcFloat: true, requireDstFloat: true}, // https://www.felixcloutier.com/x86/roundsd - ROUNDSD: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x3a, 0x0b}, needMode: true, requireSrcFloat: true, requireDstFloat: true}, + ROUNDSD: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x3a, 0x0b}, needArg: true, requireSrcFloat: true, requireDstFloat: true}, // https://www.felixcloutier.com/x86/sqrtss SQRTSS: {mandatoryPrefix: 0xf3, opcode: []byte{0x0f, 0x51}, requireSrcFloat: true, requireDstFloat: true}, // https://www.felixcloutier.com/x86/sqrtsd @@ -1143,15 +1198,49 @@ var registerToRegisterOpcode = map[asm.Instruction]struct { XORPD: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x57}, requireSrcFloat: true, requireDstFloat: true}, XORPS: {opcode: []byte{0x0f, 0x57}, requireSrcFloat: true, requireDstFloat: true}, // https://www.felixcloutier.com/x86/pinsrb:pinsrd:pinsrq - PINSRQ: {mandatoryPrefix: 0x66, rPrefix: RexPrefixW, opcode: []byte{0x0f, 0x3a, 0x22}, requireSrcFloat: false, requireDstFloat: true, needMode: true}, + PINSRB: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x3a, 0x20}, requireSrcFloat: false, requireDstFloat: true, needArg: true}, + // https://www.felixcloutier.com/x86/pinsrw + PINSRW: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0xc4}, requireSrcFloat: false, requireDstFloat: true, needArg: true}, + // https://www.felixcloutier.com/x86/pinsrb:pinsrd:pinsrq + PINSRD: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x3a, 0x22}, requireSrcFloat: false, requireDstFloat: true, needArg: true}, + // https://www.felixcloutier.com/x86/pinsrb:pinsrd:pinsrq + PINSRQ: {mandatoryPrefix: 0x66, rPrefix: RexPrefixW, opcode: []byte{0x0f, 0x3a, 0x22}, requireSrcFloat: false, requireDstFloat: true, needArg: true}, // https://www.felixcloutier.com/x86/movdqu:vmovdqu8:vmovdqu16:vmovdqu32:vmovdqu64 MOVDQU: {mandatoryPrefix: 0xf3, opcode: []byte{0x0f, 0x6f}, requireSrcFloat: true, requireDstFloat: true}, + // https://www.felixcloutier.com/x86/movdqa:vmovdqa32:vmovdqa64 + MOVDQA: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x6f}, requireSrcFloat: true, requireDstFloat: true}, PADDB: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0xfc}, requireSrcFloat: true, requireDstFloat: true}, PADDW: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0xfd}, requireSrcFloat: true, requireDstFloat: true}, PADDL: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0xfe}, requireSrcFloat: true, requireDstFloat: true}, PADDQ: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0xd4}, requireSrcFloat: true, requireDstFloat: true}, + PSUBB: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0xf8}, requireSrcFloat: true, requireDstFloat: true}, + PSUBW: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0xf9}, requireSrcFloat: true, requireDstFloat: true}, + PSUBL: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0xfa}, requireSrcFloat: true, requireDstFloat: true}, + PSUBQ: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0xfb}, requireSrcFloat: true, requireDstFloat: true}, ADDPS: {opcode: []byte{0x0f, 0x58}, requireSrcFloat: true, requireDstFloat: true}, ADDPD: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x58}, requireSrcFloat: true, requireDstFloat: true}, + SUBPS: {opcode: []byte{0x0f, 0x5c}, requireSrcFloat: true, requireDstFloat: true}, + SUBPD: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x5c}, requireSrcFloat: true, requireDstFloat: true}, + PXOR: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0xef}, requireSrcFloat: true, requireDstFloat: true}, + PSHUFB: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x38, 0x0}, requireSrcFloat: true, requireDstFloat: true}, + PSHUFD: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x70}, requireSrcFloat: true, requireDstFloat: true, needArg: true}, + // https://www.felixcloutier.com/x86/pextrb:pextrd:pextrq + PEXTRB: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x3a, 0x14}, requireSrcFloat: true, requireDstFloat: false, needArg: true, srcOnModRMReg: true}, + // https://www.felixcloutier.com/x86/pextrb:pextrd:pextrq + PEXTRW: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0xc5}, requireSrcFloat: true, requireDstFloat: false, needArg: true}, + // https://www.felixcloutier.com/x86/pextrw + PEXTRD: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x3a, 0x16}, requireSrcFloat: true, requireDstFloat: false, needArg: true, srcOnModRMReg: true}, + // https://www.felixcloutier.com/x86/pextrb:pextrd:pextrq + PEXTRQ: {rPrefix: RexPrefixW, mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x3a, 0x16}, requireSrcFloat: true, requireDstFloat: false, needArg: true, srcOnModRMReg: true}, + INSERTPS: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x3a, 0x21}, requireSrcFloat: true, requireDstFloat: true, needArg: true}, + MOVLHPS: {opcode: []byte{0x0f, 0x16}, requireSrcFloat: true, requireDstFloat: true}, + PTEST: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x38, 0x17}, requireSrcFloat: true, requireDstFloat: true}, + PCMPEQB: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x74}, requireSrcFloat: true, requireDstFloat: true}, + PCMPEQW: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x75}, requireSrcFloat: true, requireDstFloat: true}, + PCMPEQD: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x76}, requireSrcFloat: true, requireDstFloat: true}, + PCMPEQQ: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0x38, 0x29}, requireSrcFloat: true, requireDstFloat: true}, + PADDUSB: {mandatoryPrefix: 0x66, opcode: []byte{0x0f, 0xdc}, requireSrcFloat: true, requireDstFloat: true}, + MOVSD: {mandatoryPrefix: 0xf2, opcode: []byte{0x0f, 0x10}, requireSrcFloat: true, requireDstFloat: true}, } var RegisterToRegisterShiftOpcode = map[asm.Instruction]struct { @@ -1273,7 +1362,7 @@ func (a *AssemblerImpl) EncodeRegisterToRegister(n *NodeImpl) (err error) { a.Buf.WriteByte(modRM) - if op.needMode { + if op.needArg { a.WriteConst(int64(n.Arg), 8) } return nil @@ -1314,6 +1403,7 @@ func (a *AssemblerImpl) EncodeRegisterToMemory(n *NodeImpl) (err error) { var opcode []byte var mandatoryPrefix byte var isShiftInstruction bool + var needArg bool switch n.Instruction { case CMPL: // https://www.felixcloutier.com/x86/cmp @@ -1411,6 +1501,27 @@ func (a *AssemblerImpl) EncodeRegisterToMemory(n *NodeImpl) (err error) { // https://www.felixcloutier.com/x86/movdqu:vmovdqu8:vmovdqu16:vmovdqu32:vmovdqu64 mandatoryPrefix = 0xf3 opcode = []byte{0x0f, 0x7f} + case PEXTRB: + // https://www.felixcloutier.com/x86/pextrb:pextrd:pextrq + mandatoryPrefix = 0x66 + opcode = []byte{0x0f, 0x3a, 0x14} + needArg = true + case PEXTRW: + // https://www.felixcloutier.com/x86/pextrb:pextrd:pextrq + mandatoryPrefix = 0x66 + opcode = []byte{0x0f, 0x3a, 0x15} + needArg = true + case PEXTRD: + // https://www.felixcloutier.com/x86/pextrw + mandatoryPrefix = 0x66 + opcode = []byte{0x0f, 0x3a, 0x16} + needArg = true + case PEXTRQ: + // https://www.felixcloutier.com/x86/pextrb:pextrd:pextrq + rexPrefix |= RexPrefixW + mandatoryPrefix = 0x66 + opcode = []byte{0x0f, 0x3a, 0x16} + needArg = true default: return errorEncodingUnsupported(n) } @@ -1449,6 +1560,10 @@ func (a *AssemblerImpl) EncodeRegisterToMemory(n *NodeImpl) (err error) { if displacementWidth != 0 { a.WriteConst(n.DstConst, displacementWidth) } + + if needArg { + a.WriteConst(int64(n.Arg), 8) + } return } @@ -1555,6 +1670,7 @@ func (a *AssemblerImpl) EncodeMemoryToRegister(n *NodeImpl) (err error) { var mandatoryPrefix byte var opcode []byte + var needArg bool switch n.Instruction { case ADDL: // https://www.felixcloutier.com/x86/add @@ -1656,6 +1772,50 @@ func (a *AssemblerImpl) EncodeMemoryToRegister(n *NodeImpl) (err error) { // https://www.felixcloutier.com/x86/movdqu:vmovdqu8:vmovdqu16:vmovdqu32:vmovdqu64 mandatoryPrefix = 0xf3 opcode = []byte{0x0f, 0x6f} + case PMOVSXBW: + // https://www.felixcloutier.com/x86/pmovsx + mandatoryPrefix = 0x66 + opcode = []byte{0x0f, 0x38, 0x20} + case PMOVSXWD: + // https://www.felixcloutier.com/x86/pmovsx + mandatoryPrefix = 0x66 + opcode = []byte{0x0f, 0x38, 0x23} + case PMOVSXDQ: + // https://www.felixcloutier.com/x86/pmovsx + mandatoryPrefix = 0x66 + opcode = []byte{0x0f, 0x38, 0x25} + case PMOVZXBW: + // https://www.felixcloutier.com/x86/pmovzx + mandatoryPrefix = 0x66 + opcode = []byte{0x0f, 0x38, 0x30} + case PMOVZXWD: + mandatoryPrefix = 0x66 + opcode = []byte{0x0f, 0x38, 0x33} + // https://www.felixcloutier.com/x86/pmovzx + case PMOVZXDQ: + mandatoryPrefix = 0x66 + opcode = []byte{0x0f, 0x38, 0x35} + case PINSRB: + // https://www.felixcloutier.com/x86/pinsrb:pinsrd:pinsrq + mandatoryPrefix = 0x66 + opcode = []byte{0x0f, 0x3a, 0x20} + needArg = true + case PINSRW: + // https://www.felixcloutier.com/x86/pinsrw + mandatoryPrefix = 0x66 + opcode = []byte{0x0f, 0xc4} + needArg = true + case PINSRD: + // https://www.felixcloutier.com/x86/pinsrb:pinsrd:pinsrq + mandatoryPrefix = 0x66 + opcode = []byte{0x0f, 0x3a, 0x22} + needArg = true + case PINSRQ: + // https://www.felixcloutier.com/x86/pinsrb:pinsrd:pinsrq + rexPrefix |= RexPrefixW + mandatoryPrefix = 0x66 + opcode = []byte{0x0f, 0x3a, 0x22} + needArg = true default: return errorEncodingUnsupported(n) } @@ -1681,6 +1841,9 @@ func (a *AssemblerImpl) EncodeMemoryToRegister(n *NodeImpl) (err error) { a.WriteConst(n.SrcConst, displacementWidth) } + if needArg { + a.WriteConst(int64(n.Arg), 8) + } return } diff --git a/internal/asm/amd64/impl_staticconst.go b/internal/asm/amd64/impl_staticconst.go new file mode 100644 index 00000000000..451bb0169c3 --- /dev/null +++ b/internal/asm/amd64/impl_staticconst.go @@ -0,0 +1,124 @@ +package amd64 + +import ( + "encoding/binary" + "fmt" + "math" + + "github.com/tetratelabs/wazero/internal/asm" +) + +type constPool struct { + firstUseOffsetInBinary *asm.NodeOffsetInBinary + consts []asm.StaticConst + poolSizeInBytes int + + // offsetFinalizedCallbacks holds the callbacks keyed on the constants. + // These callbacks are called when the offsets of the constants in the binary + // have been determined. + offsetFinalizedCallbacks map[string][]func(offsetOfConstInBinary int) +} + +func newConstPool() constPool { + return constPool{offsetFinalizedCallbacks: map[string][]func(offsetOfConstInBinary int){}} +} + +func (p *constPool) addConst(c asm.StaticConst) { + key := asm.StaticConstKey(c) + if _, ok := p.offsetFinalizedCallbacks[key]; !ok { + p.consts = append(p.consts, c) + p.poolSizeInBytes += len(c) + p.offsetFinalizedCallbacks[key] = []func(int){} + } +} + +func (a *AssemblerImpl) maybeFlushConstants(isEndOfFunction bool) { + if a.pool.firstUseOffsetInBinary == nil { + return + } + + if isEndOfFunction || + // Conservative strategy. + ((a.pool.poolSizeInBytes+a.Buf.Len())-int(*a.pool.firstUseOffsetInBinary)) >= a.MaxDisplacementForConstantPool { + if !isEndOfFunction { + // Adds the jump instruction to skip the constants if this is not the end of function. + // + // TODO: consider NOP padding for this jump, though this rarely happens as most functions should be + // small enough to fit all consts after the end of function. + if a.pool.poolSizeInBytes >= math.MaxInt8-2 { + // long jump + a.Buf.WriteByte(0xe9) + a.WriteConst(int64(a.pool.poolSizeInBytes), 32) + } else { + // short jump + a.Buf.WriteByte(0xeb) + a.WriteConst(int64(a.pool.poolSizeInBytes), 8) + } + } + + for _, c := range a.pool.consts { + offset := a.Buf.Len() + a.Buf.Write(c) + for _, callback := range a.pool.offsetFinalizedCallbacks[asm.StaticConstKey(c)] { + callback(offset) + } + } + + a.pool = newConstPool() + } +} + +func (a *AssemblerImpl) encodeStaticConstToRegister(n *NodeImpl) (err error) { + if n.Instruction != MOVDQU { + err = errorEncodingUnsupported(n) + return + } + + a.pool.addConst(n.staticConst) + + dstReg3Bits, rexPrefix, err := register3bits(n.DstReg, registerSpecifierPositionModRMFieldReg) + if err != nil { + return err + } + + var inst []byte // mandatory prefix + key := asm.StaticConstKey(n.staticConst) + a.pool.offsetFinalizedCallbacks[key] = append(a.pool.offsetFinalizedCallbacks[key], + func(offsetOfConstInBinary int) { + bin := a.Buf.Bytes() + displacement := offsetOfConstInBinary - int(n.OffsetInBinary()) - len(inst) + binary.LittleEndian.PutUint32(bin[n.OffsetInBinary()+uint64(len(inst)-4):], uint32(int32(displacement))) + }) + + nodeOffset := uint64(a.Buf.Len()) + a.pool.firstUseOffsetInBinary = &nodeOffset + + // https://wiki.osdev.org/X86-64_Instruction_Encoding#64-bit_addressing + modRM := 0b00_000_101 | // Indicate "MOVDQU [RIP + 32bit displacement], DstReg" encoding. + (dstReg3Bits << 3) // Place the DstReg on ModRM:reg. + + // https://www.felixcloutier.com/x86/movdqu:vmovdqu8:vmovdqu16:vmovdqu32:vmovdqu64 + inst = append(inst, 0xf3) // mandatory prefix + if rexPrefix != RexPrefixNone { + inst = append(inst, rexPrefix) + } + inst = append(inst, 0x0f, 0x6f, modRM, + 0x0, 0x0, 0x0, 0x0, // Preserve 4 bytes for displacement. + ) + + a.Buf.Write(inst) + return +} + +// CompileLoadStaticConstToRegister implements Assembler.CompileLoadStaticConstToRegister. +func (a *AssemblerImpl) CompileLoadStaticConstToRegister(instruction asm.Instruction, c asm.StaticConst, dstReg asm.Register) (err error) { + if len(c)%2 != 0 { + err = fmt.Errorf("the length of a static constant must be even but was %d", len(c)) + return + } + + n := a.newNode(instruction, OperandTypesStaticConstToRegister) + n.DstReg = dstReg + n.staticConst = c + return +} diff --git a/internal/asm/amd64/impl_staticconst_test.go b/internal/asm/amd64/impl_staticconst_test.go new file mode 100644 index 00000000000..af8f6fce073 --- /dev/null +++ b/internal/asm/amd64/impl_staticconst_test.go @@ -0,0 +1,171 @@ +package amd64 + +import ( + "github.com/tetratelabs/wazero/internal/asm" + "testing" + + "github.com/tetratelabs/wazero/internal/testing/require" +) + +func TestConstPool_addConst(t *testing.T) { + p := newConstPool() + cons := []byte{1, 2, 3, 4} + + for i := 0; i < 2; i++ { + p.addConst(cons) + require.Equal(t, 1, len(p.consts)) + require.Equal(t, len(cons), p.poolSizeInBytes) + _, ok := p.offsetFinalizedCallbacks[asm.StaticConstKey(cons)] + require.True(t, ok) + } +} + +func TestAssemblerImpl_CompileLoadStaticConstToRegister(t *testing.T) { + a := NewAssemblerImpl() + t.Run("odd bytes", func(t *testing.T) { + err := a.CompileLoadStaticConstToRegister(MOVDQU, []byte{1}, RegAX) + require.Error(t, err) + }) + t.Run("ok", func(t *testing.T) { + cons := []byte{1, 2, 3, 4} + err := a.CompileLoadStaticConstToRegister(MOVDQU, cons, RegAX) + require.NoError(t, err) + actualNode := a.Current + require.Equal(t, MOVDQU, actualNode.Instruction) + require.Equal(t, OperandTypeStaticConst, actualNode.Types.src) + require.Equal(t, OperandTypeRegister, actualNode.Types.dst) + require.Equal(t, cons, actualNode.staticConst) + }) +} + +func TestAssemblerImpl_maybeFlushConstants(t *testing.T) { + t.Run("no consts", func(t *testing.T) { + a := NewAssemblerImpl() + // Invoking maybeFlushConstants before encoding consts usage should be fine. + a.maybeFlushConstants(false) + a.maybeFlushConstants(true) + }) + + largeData := make([]byte, 256) + + tests := []struct { + name string + endOfFunction bool + dummyBodyBeforeFlush []byte + firstUseOffsetInBinary uint64 + consts []asm.StaticConst + expectedOffsetForConsts []int + exp []byte + maxDisplacement int + }{ + { + name: "end of function", + endOfFunction: true, + dummyBodyBeforeFlush: []byte{'?', '?', '?', '?'}, + consts: []asm.StaticConst{{1, 2, 3, 4, 5, 6, 7, 8}, {10, 11, 12, 13}}, + expectedOffsetForConsts: []int{4, 4 + 8}, // 4 = len(dummyBodyBeforeFlush) + firstUseOffsetInBinary: 0, + exp: []byte{'?', '?', '?', '?', 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13}, + maxDisplacement: 1 << 31, + }, + { + name: "not flush", + endOfFunction: false, + dummyBodyBeforeFlush: []byte{'?', '?', '?', '?'}, + consts: []asm.StaticConst{{1, 2, 3, 4, 5, 6, 7, 8}, {10, 11, 12, 13}}, + firstUseOffsetInBinary: 0, + exp: []byte{'?', '?', '?', '?'}, + maxDisplacement: 1 << 31, + }, + { + name: "not end of function but flush - short jump", + endOfFunction: false, + dummyBodyBeforeFlush: []byte{'?', '?', '?', '?'}, + consts: []asm.StaticConst{{1, 2, 3, 4, 5, 6, 7, 8}, {10, 11, 12, 13}}, + expectedOffsetForConsts: []int{4 + 2, 4 + 2 + 8}, // 4 = len(dummyBodyBeforeFlush), 2 = the size of jump + firstUseOffsetInBinary: 0, + exp: []byte{'?', '?', '?', '?', + 0xeb, 0x0c, // short jump with offset = len(consts[0]) + len(consts[1]) = 12 = 0xc. + 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13}, + maxDisplacement: 0, + }, + { + name: "not end of function but flush - long jump", + endOfFunction: false, + dummyBodyBeforeFlush: []byte{'?', '?', '?', '?'}, + consts: []asm.StaticConst{largeData}, + expectedOffsetForConsts: []int{4 + 5}, // 4 = len(dummyBodyBeforeFlush), 5 = the size of jump + firstUseOffsetInBinary: 0, + exp: append([]byte{'?', '?', '?', '?', + 0xe9, 0x0, 0x1, 0x0, 0x0, // short jump with offset = 256 = 0x0, 0x1, 0x0, 0x0 (in Little Endian). + }, largeData...), + maxDisplacement: 0, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + a := NewAssemblerImpl() + a.MaxDisplacementForConstantPool = tc.maxDisplacement + a.Buf.Write(tc.dummyBodyBeforeFlush) + + for i, c := range tc.consts { + a.pool.addConst(c) + key := asm.StaticConstKey(c) + i := i + a.pool.offsetFinalizedCallbacks[key] = append(a.pool.offsetFinalizedCallbacks[key], func(offsetOfConstInBinary int) { + require.Equal(t, tc.expectedOffsetForConsts[i], offsetOfConstInBinary) + }) + } + + a.pool.firstUseOffsetInBinary = &tc.firstUseOffsetInBinary + a.maybeFlushConstants(tc.endOfFunction) + + require.Equal(t, tc.exp, a.Buf.Bytes()) + }) + } +} + +func TestAssemblerImpl_encodeStaticConstToRegister(t *testing.T) { + consts := []asm.StaticConst{ + {0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, + {0x22, 0x22, 0x22, 0x22}, + {0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33}, + } + a := NewAssemblerImpl() + + a.CompileStandAlone(UD2) // insert any dummy instruction before MOVDQUs. + err := a.CompileLoadStaticConstToRegister(MOVDQU, consts[0], RegX12) + require.NoError(t, err) + err = a.CompileLoadStaticConstToRegister(MOVDQU, consts[1], RegX0) + require.NoError(t, err) + err = a.CompileLoadStaticConstToRegister(MOVDQU, consts[0], RegX0) + require.NoError(t, err) + err = a.CompileLoadStaticConstToRegister(MOVDQU, consts[2], RegX12) + require.NoError(t, err) + + actual, err := a.Assemble() + require.NoError(t, err) + + require.Equal(t, []byte{ + 0x0f, 0x0b, // dummy instruction. + // 0x2: movdqu xmm12, xmmword ptr [rip + 0x19] + // where rip = 0x0b, therefore [rip + 0x19] = [0x24] = consts[0]. + 0xf3, 0x44, 0x0f, 0x6f, 0x25, 0x19, 0x00, 0x00, 0x00, + // 0x0b: movdqu xmm0, xmmword ptr [rip + 0x19] + // where rip = 0x13, therefore [rip + 0x19] = [0x2c] = consts[1]. + 0xf3, 0x0f, 0x6f, 0x05, 0x19, 0x00, 0x00, 0x00, + // 0x13: movdqu xmm0, xmmword ptr [rip + 0x9] + // where rip = 0x1b, therefore [rip + 0x9] = [0x24] = consts[0]. + 0xf3, 0x0f, 0x6f, 0x05, 0x09, 0x00, 0x00, 0x00, + // 0x1b: movdqu xmm12, xmmword ptr [rip + 0xc] + // where rip = 0x24, therefore [rip + 0xc] = [0x30] = consts[2]. + 0xf3, 0x44, 0x0f, 0x6f, 0x25, 0x0c, 0x00, 0x00, 0x00, + // 0x24: consts[0] + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + // 0x2c: consts[1] + 0x22, 0x22, 0x22, 0x22, + // 0x30: consts[2] + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + }, actual) +} diff --git a/internal/asm/amd64/impl_test.go b/internal/asm/amd64/impl_test.go index 76510f821e0..429891f40cb 100644 --- a/internal/asm/amd64/impl_test.go +++ b/internal/asm/amd64/impl_test.go @@ -509,6 +509,33 @@ func TestAssemblerImpl_EncodeRegisterToRegister(t *testing.T) { }, exp: []byte{0xf3, 0x45, 0xf, 0x6f, 0xfa}, }, + { + n: &NodeImpl{ + Instruction: MOVDQA, + Types: OperandTypesRegisterToRegister, + SrcReg: RegX3, + DstReg: RegX10, + }, + exp: []byte{0x66, 0x44, 0xf, 0x6f, 0xd3}, + }, + { + n: &NodeImpl{ + Instruction: MOVDQA, + Types: OperandTypesRegisterToRegister, + SrcReg: RegX10, + DstReg: RegX3, + }, + exp: []byte{0x66, 0x41, 0xf, 0x6f, 0xda}, + }, + { + n: &NodeImpl{ + Instruction: MOVDQA, + Types: OperandTypesRegisterToRegister, + SrcReg: RegX10, + DstReg: RegX15, + }, + exp: []byte{0x66, 0x45, 0xf, 0x6f, 0xfa}, + }, } for _, tt := range tests { diff --git a/internal/asm/arm64/assembler.go b/internal/asm/arm64/assembler.go index 1124fc5147c..f82443f999d 100644 --- a/internal/asm/arm64/assembler.go +++ b/internal/asm/arm64/assembler.go @@ -66,13 +66,34 @@ type Assembler interface { // CompileConditionalRegisterSet adds an instruction to set 1 on dstReg if the condition satisfies, // otherwise set 0. CompileConditionalRegisterSet(cond asm.ConditionalRegisterState, dstReg asm.Register) + // CompileMemoryToVectorRegister TODO - CompileMemoryToVectorRegister(instruction asm.Instruction, srcOffsetReg, dstReg asm.Register, arrangement VectorArrangement) + CompileMemoryToVectorRegister(instruction asm.Instruction, srcBaseReg asm.Register, srcOffset asm.ConstantValue, dstReg asm.Register, arrangement VectorArrangement) + + // CompileMemoryWithRegisterOffsetToVectorRegister TODO + CompileMemoryWithRegisterOffsetToVectorRegister(instruction asm.Instruction, srcBaseReg, srcOffsetRegister asm.Register, dstReg asm.Register, arrangement VectorArrangement) + // CompileVectorRegisterToMemory TODO - CompileVectorRegisterToMemory(instruction asm.Instruction, srcReg, dstOffsetReg asm.Register, arrangement VectorArrangement) + CompileVectorRegisterToMemory(instruction asm.Instruction, srcReg, dstBaseReg asm.Register, dstOffset asm.ConstantValue, arrangement VectorArrangement) + + // CompileVectorRegisterToMemoryWithRegisterOffset TODO + CompileVectorRegisterToMemoryWithRegisterOffset(instruction asm.Instruction, srcReg, dstBaseReg, dstOffsetRegister asm.Register, arrangement VectorArrangement) + // CompileRegisterToVectorRegister TODO CompileRegisterToVectorRegister(instruction asm.Instruction, srcReg, dstReg asm.Register, arrangement VectorArrangement, index VectorIndex) + + // CompileVectorRegisterToRegister TODO + CompileVectorRegisterToRegister(instruction asm.Instruction, srcReg, dstReg asm.Register, + arrangement VectorArrangement, index VectorIndex) + // CompileVectorRegisterToVectorRegister TODO - CompileVectorRegisterToVectorRegister(instruction asm.Instruction, srcReg, dstReg asm.Register, arrangement VectorArrangement) + CompileVectorRegisterToVectorRegister(instruction asm.Instruction, srcReg, dstReg asm.Register, arrangement VectorArrangement, srcIndex, dstIndex VectorIndex) + + // CompileVectorRegisterToVectorRegisterWithConst TODO + CompileVectorRegisterToVectorRegisterWithConst(instruction asm.Instruction, srcReg, dstReg asm.Register, arrangement VectorArrangement, c asm.ConstantValue) + + // CompileLoadStaticConstToVectorRegister adds an instruction where the source operand is StaticConstant located in the memory + // and the destination is the dstReg. + CompileLoadStaticConstToVectorRegister(instruction asm.Instruction, c asm.StaticConst, dstReg asm.Register, arrangement VectorArrangement) } diff --git a/internal/asm/arm64/consts.go b/internal/asm/arm64/consts.go index 05f3f92b175..67b94d4ed14 100644 --- a/internal/asm/arm64/consts.go +++ b/internal/asm/arm64/consts.go @@ -682,27 +682,58 @@ const ( VBIT // VCNT is the CNT instruction. https://developer.arm.com/documentation/dui0802/a/A64-Advanced-SIMD-Vector-Instructions/CNT--vector- VCNT - // VMOV is the MOV instruction. https://developer.arm.com/documentation/dui0802/a/A64-Advanced-SIMD-Vector-Instructions/MOV--vector- + // VMOV has different semantics depending on the types of operands: + // * MOV(vector) if the operands are vectors and indexes are not specified. https://developer.arm.com/documentation/dui0802/a/A64-Advanced-SIMD-Vector-Instructions/MOV--vector- + // * MOV(vector, element) if the operands are vectors and indexes are specified. https://developer.arm.com/documentation/dui0802/a/A64-Advanced-SIMD-Vector-Instructions/MOV--vector--element- + // * INS(vector, element) if the src is a general purpose and the dst is a vector. https://developer.arm.com/documentation/dui0802/a/A64-Advanced-SIMD-Vector-Instructions/INS--vector---general- + // * UMOV(vector) if the dst is a general purpose and the src is a vector. https://developer.arm.com/documentation/100069/0610/A64-SIMD-Vector-Instructions/UMOV--vector- + // * LDR(SIMD&FP) if the src is memory and dst is a vector: https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/LDR--immediate--SIMD-FP---Load-SIMD-FP-Register--immediate-offset-- + // * LDR (literal, SIMD&FP) if the src is static const and dst is a vector: https://developer.arm.com/documentation/dui0801/h/A64-Floating-point-Instructions/LDR--literal--SIMD-and-FP- + // * STR(SIMD&FP) if the dst is memory and src is a vector: https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/STR--immediate--SIMD-FP---Store-SIMD-FP-register--immediate-offset-- VMOV // VUADDLV is the UADDLV instruction. https://developer.arm.com/documentation/dui0802/a/A64-Advanced-SIMD-Vector-Instructions/UADDLV--vector- VUADDLV - // VLD1 is the LD1 instruction. https://developer.arm.com/documentation/dui0802/a/A64-Advanced-SIMD-Vector-Instructions/LD1--vector--single-structure- - VLD1 - // VST1 is the ST1 instruction. https://developer.arm.com/documentation/dui0802/a/A64-Advanced-SIMD-Vector-Instructions/ST1--vector--single-structure- - VST1 - // VADD is the ADD instruction. https://developer.arm.com/documentation/dui0802/a/A64-Advanced-SIMD-Vector-Instructions/ADD--vector- + // VADD is the ADD(vector) instruction. https://developer.arm.com/documentation/dui0802/a/A64-Advanced-SIMD-Vector-Instructions/ADD--vector- VADD - // VFADDS is the FADD instruction, for single precision. https://developer.arm.com/documentation/dui0802/a/A64-Advanced-SIMD-Vector-Instructions/FADD--vector- + // VFADDS is the FADD(vector) instruction, for single precision. https://developer.arm.com/documentation/dui0802/a/A64-Advanced-SIMD-Vector-Instructions/FADD--vector- VFADDS - // VFADDD is the FADD instruction, for double precision. https://developer.arm.com/documentation/dui0802/a/A64-Advanced-SIMD-Vector-Instructions/FADD--vector- + // VFADDD is the FADD(vector) instruction, for double precision. https://developer.arm.com/documentation/dui0802/a/A64-Advanced-SIMD-Vector-Instructions/FADD--vector- VFADDD + // VSUB is the SUB(vector) instruction. https://developer.arm.com/documentation/dui0802/a/A64-Advanced-SIMD-Vector-Instructions/SUB--vector- + VSUB + // VFSUBS is the FSUB(vector) instruction, for single precision. https://developer.arm.com/documentation/dui0802/a/A64-Advanced-SIMD-Vector-Instructions/FSUB--vector- + VFSUBS + // VFSUBD is the FSUB(vector) instruction, for double precision. https://developer.arm.com/documentation/dui0802/a/A64-Advanced-SIMD-Vector-Instructions/FSUB--vector- + VFSUBD + // SSHLL is the SSHLL instruction. https://developer.arm.com/documentation/dui0801/h/A64-SIMD-Vector-Instructions/SSHLL--SSHLL2--vector- + SSHLL + // USHLL is the USHLL instruction. https://developer.arm.com/documentation/dui0801/h/A64-SIMD-Vector-Instructions/SSHLL--SSHLL2--vector- + USHLL + // LD1R is the LD1R instruction. https://developer.arm.com/documentation/ddi0596/2021-12/SIMD-FP-Instructions/LD1R--Load-one-single-element-structure-and-Replicate-to-all-lanes--of-one-register-- + LD1R + // SMOV is the SMOV instruction. https://developer.arm.com/documentation/100069/0610/A64-SIMD-Vector-Instructions/SMOV--vector- + SMOV + // DUP is the DUP instruction. https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/DUP--element---Duplicate-vector-element-to-vector-or-scalar- + DUP + // UMAXP is the UMAXP instruction. https://developer.arm.com/documentation/dui0801/g/A64-SIMD-Vector-Instructions/UMAXP--vector- + UMAXP + // UMINV is the UMINV instruction. https://developer.arm.com/documentation/100069/0610/A64-SIMD-Vector-Instructions/UMINV--vector- + UMINV + // CMEQ is the CMEQ instruction. https://developer.arm.com/documentation/dui0801/g/A64-SIMD-Vector-Instructions/CMEQ--vector--register- + CMEQ + // ADDP is the ADDP instruction. https://developer.arm.com/documentation/dui0801/g/A64-SIMD-Vector-Instructions/ADDP--vector- + ADDP + // TBL1 is the TBL instruction whose source is one vector. // https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/TBL--Table-vector-Lookup- + TBL1 + // TBL2 is the TBL instruction whose source is two vectors. // https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/TBL--Table-vector-Lookup- + TBL2 ) // VectorArrangement is the arrangement of data within a vector register. type VectorArrangement byte const ( - // VectorArrangmentNone is an arrangement indicating no data is stored. + // VectorArrangementNone is an arrangement indicating no data is stored. VectorArrangementNone VectorArrangement = iota // VectorArrangement8B is an arrangement of 8 bytes (64-bit vector) VectorArrangement8B @@ -728,12 +759,14 @@ const ( // VectorArrangementB is a size specifier of byte VectorArrangementB - // VectorArrangementH is a size specifier of halfword + // VectorArrangementH is a size specifier of word (16-bit) VectorArrangementH - // VectorArrangementS is a size specifier of word + // VectorArrangementS is a size specifier of double word (32-bit) VectorArrangementS - // VectorArrangementD is a size specifier of doubleword + // VectorArrangementD is a size specifier of quad word (64-bit) VectorArrangementD + // VectorArrangementQ is a size specifier of the entire vector (128-bit) + VectorArrangementQ ) func (v VectorArrangement) String() (ret string) { @@ -762,8 +795,12 @@ func (v VectorArrangement) String() (ret string) { ret = "S" case VectorArrangementD: ret = "D" + case VectorArrangementQ: + ret = "Q" + case VectorArrangementNone: + ret = "none" default: - ret = "unknown" + panic(v) } return } @@ -771,6 +808,9 @@ func (v VectorArrangement) String() (ret string) { // VectorIndex is the index of an element of a vector register type VectorIndex byte +// VectorIndexNone indicates no vector index specified. +const VectorIndexNone VectorIndex = ^VectorIndex(0) + // InstructionName returns the name of the given instruction func InstructionName(i asm.Instruction) string { switch i { @@ -1014,16 +1054,36 @@ func InstructionName(i asm.Instruction) string { return "VUADDLV" case VMOV: return "VMOV" - case VST1: - return "VST1" - case VLD1: - return "VLD1" case VADD: return "VADD" case VFADDS: return "VFADDS" case VFADDD: return "VFADDD" + case VSUB: + return "VSUB" + case VFSUBS: + return "VFSUBS" + case VFSUBD: + return "VFSUBD" + case SSHLL: + return "SSHLL" + case USHLL: + return "USHLL" + case LD1R: + return "LD1R" + case SMOV: + return "SMOV" + case DUP: + return "DUP" + case UMAXP: + return "UMAXP" + case UMINV: + return "UMINV" + case CMEQ: + return "CMEQ" + case ADDP: + return "ADDP" } - return "UNKNOWN" + panic("unknown instruction") } diff --git a/internal/asm/arm64/impl.go b/internal/asm/arm64/impl.go index b1e7b0ef1f3..283cb146127 100644 --- a/internal/asm/arm64/impl.go +++ b/internal/asm/arm64/impl.go @@ -2,6 +2,7 @@ package arm64 import ( "bytes" + "encoding/binary" "errors" "fmt" "math" @@ -25,8 +26,8 @@ type NodeImpl struct { SrcReg, SrcReg2, DstReg, DstReg2 asm.Register SrcConst, DstConst asm.ConstantValue - VectorArrangement VectorArrangement - VectorIndex VectorIndex + VectorArrangement VectorArrangement + SrcVectorIndex, DstVectorIndex VectorIndex // readInstructionAddressBeforeTargetInstruction holds the instruction right before the target of // read instruction address instruction. See asm.assemblerBase.CompileReadInstructionAddress. @@ -34,6 +35,8 @@ type NodeImpl struct { // JumpOrigins hold all the nodes trying to jump into this node. In other words, all the nodes with .JumpTarget == this. JumpOrigins map[*NodeImpl]struct{} + + staticConst asm.StaticConst } // AssignJumpTarget implements the same method as documented on asm.Node. @@ -106,13 +109,18 @@ func (n *NodeImpl) String() (ret string) { case OperandTypesTwoSIMDBytesToSIMDByteRegister: ret = fmt.Sprintf("%s (%s.B8, %s.B8), %s.B8", instName, RegisterName(n.SrcReg), RegisterName(n.SrcReg2), RegisterName(n.DstReg)) case OperandTypesRegisterToVectorRegister: - ret = fmt.Sprintf("%s %s, %s.%s[%d]", instName, RegisterName(n.SrcReg), RegisterName(n.DstReg), n.VectorArrangement, n.VectorIndex) + ret = fmt.Sprintf("%s %s, %s.%s[%d]", instName, RegisterName(n.SrcReg), RegisterName(n.DstReg), n.VectorArrangement, n.DstVectorIndex) + case OperandTypesVectorRegisterToRegister: + ret = fmt.Sprintf("%s %s.%s[%d], %s.%s[%d]", instName, RegisterName(n.SrcReg), n.VectorArrangement, n.SrcVectorIndex, + RegisterName(n.DstReg), n.VectorArrangement, n.DstVectorIndex) case OperandTypesVectorRegisterToMemory: ret = fmt.Sprintf("%s %s.%s, [%s]", instName, RegisterName(n.SrcReg), n.VectorArrangement, RegisterName(n.DstReg)) case OperandTypesMemoryToVectorRegister: ret = fmt.Sprintf("%s [%s], %s.%s", instName, RegisterName(n.SrcReg), RegisterName(n.DstReg), n.VectorArrangement) case OperandTypesVectorRegisterToVectorRegister: ret = fmt.Sprintf("%s %s.%[2]s, %s.%[2]s", instName, RegisterName(n.SrcReg), RegisterName(n.DstReg), n.VectorArrangement) + case OperandTypesStaticConstToVectorRegister: + ret = fmt.Sprintf("%s $%v %s.%s", instName, n.staticConst, RegisterName(n.DstReg), n.VectorArrangement) } return } @@ -134,6 +142,7 @@ const ( OperandTypeSIMDByte OperandTypeTwoSIMDBytes OperandTypeVectorRegister + OperandTypeStaticConst ) // String implements fmt.Stringer. @@ -161,6 +170,8 @@ func (o OperandType) String() (ret string) { ret = "two-simd-bytes" case OperandTypeVectorRegister: ret = "vector-register" + case OperandTypeStaticConst: + ret = "static-const" } return } @@ -186,9 +197,11 @@ var ( OperandTypesSIMDByteToRegister = OperandTypes{OperandTypeSIMDByte, OperandTypeRegister} OperandTypesTwoSIMDBytesToSIMDByteRegister = OperandTypes{OperandTypeTwoSIMDBytes, OperandTypeSIMDByte} OperandTypesRegisterToVectorRegister = OperandTypes{OperandTypeRegister, OperandTypeVectorRegister} + OperandTypesVectorRegisterToRegister = OperandTypes{OperandTypeVectorRegister, OperandTypeRegister} OperandTypesMemoryToVectorRegister = OperandTypes{OperandTypeMemory, OperandTypeVectorRegister} OperandTypesVectorRegisterToMemory = OperandTypes{OperandTypeVectorRegister, OperandTypeMemory} OperandTypesVectorRegisterToVectorRegister = OperandTypes{OperandTypeVectorRegister, OperandTypeVectorRegister} + OperandTypesStaticConstToVectorRegister = OperandTypes{OperandTypeStaticConst, OperandTypeVectorRegister} ) // String implements fmt.Stringer @@ -206,23 +219,24 @@ type AssemblerImpl struct { pool constPool } -// constPool holds 32-bit constants which are used by ldr(literal) instructions +// constPool holds asm.StaticConst which are used by ldr(literal) instructions // emitted by memory access. type constPool struct { // firstUseOffsetInBinary is the offset of the first ldr(literal) instruction // which needs to access the const in this constPool. firstUseOffsetInBinary *asm.NodeOffsetInBinary - consts []int32 + consts []asm.StaticConst + constSizeInBytes int // offsetFinalizedCallbacks holds the callbacks keyed on the constants. // These callbacks are called when the offsets of the constants in the binary // have been determined. - offsetFinalizedCallbacks map[int32][]func(offsetOfConstInBinary int) + offsetFinalizedCallbacks map[string][]func(offsetOfConstInBinary int) } func NewAssemblerImpl(temporaryRegister asm.Register) *AssemblerImpl { return &AssemblerImpl{ Buf: bytes.NewBuffer(nil), temporaryRegister: temporaryRegister, - pool: constPool{offsetFinalizedCallbacks: map[int32][]func(int){}}, + pool: constPool{offsetFinalizedCallbacks: map[string][]func(int){}}, } } @@ -295,11 +309,11 @@ func (a *AssemblerImpl) maybeFlushConstPool(endOfBinary bool) { // Also, if the offset between the first usage of the constant pool and // the first constant would exceed 2^20 -1(= 2MiB-1), which is the maximum offset // for load(literal) instruction, flush all the constants in the pool. - (a.Buf.Len()-int(*a.pool.firstUseOffsetInBinary)) >= (1<<20)-1-4 { // -4 for unconditional branch to skip the constants. + (a.Buf.Len()+a.pool.constSizeInBytes-int(*a.pool.firstUseOffsetInBinary)) >= (1<<20)-1-4 { // -4 for unconditional branch to skip the constants. // Before emitting consts, we have to add br instruction to skip the const pool. // https://github.com/golang/go/blob/release-branch.go1.15/src/cmd/internal/obj/arm64/asm7.go#L1123-L1129 - skipOffset := len(a.pool.consts) + skipOffset := a.pool.constSizeInBytes if endOfBinary { // If this is the end of binary, we never reach this block, // so offset can be zero (which is the behavior of Go's assembler). @@ -315,31 +329,34 @@ func (a *AssemblerImpl) maybeFlushConstPool(endOfBinary bool) { // Then adding the consts into the binary. for _, c := range a.pool.consts { offsetOfConst := a.Buf.Len() - a.Buf.Write([]byte{byte(c), byte(c >> 8), byte(c >> 16), byte(c >> 24)}) + a.Buf.Write(c) // Invoke callbacks for `c` with the offset of binary where we store `c`. - for _, cb := range a.pool.offsetFinalizedCallbacks[c] { + for _, cb := range a.pool.offsetFinalizedCallbacks[asm.StaticConstKey(c)] { cb(offsetOfConst) } } // After the flush, reset the constant pool. - a.pool = constPool{offsetFinalizedCallbacks: map[int32][]func(int){}} + a.pool = constPool{offsetFinalizedCallbacks: map[string][]func(int){}} } } -func (a *AssemblerImpl) setConstPoolCallback(v int32, cb func(int)) { - a.pool.offsetFinalizedCallbacks[v] = append(a.pool.offsetFinalizedCallbacks[v], cb) +func (a *AssemblerImpl) setConstPoolCallback(c asm.StaticConst, cb func(int)) { + key := asm.StaticConstKey(c) + a.pool.offsetFinalizedCallbacks[key] = append(a.pool.offsetFinalizedCallbacks[key], cb) } -func (a *AssemblerImpl) addConstPool(v int32, useOffset asm.NodeOffsetInBinary) { +func (a *AssemblerImpl) addConstPool(c asm.StaticConst, useOffset asm.NodeOffsetInBinary) { if a.pool.firstUseOffsetInBinary == nil { a.pool.firstUseOffsetInBinary = &useOffset } - if _, ok := a.pool.offsetFinalizedCallbacks[v]; !ok { - a.pool.consts = append(a.pool.consts, v) - a.pool.offsetFinalizedCallbacks[v] = []func(int){} + key := asm.StaticConstKey(c) + if _, ok := a.pool.offsetFinalizedCallbacks[key]; !ok { + a.pool.consts = append(a.pool.consts, c) + a.pool.offsetFinalizedCallbacks[key] = []func(int){} + a.pool.constSizeInBytes += len(c) } } @@ -393,12 +410,16 @@ func (a *AssemblerImpl) EncodeNode(n *NodeImpl) (err error) { err = a.EncodeTwoSIMDBytesToSIMDByteRegister(n) case OperandTypesRegisterToVectorRegister: err = a.EncodeRegisterToVectorRegister(n) + case OperandTypesVectorRegisterToRegister: + err = a.EncodeVectorRegisterToRegister(n) case OperandTypesMemoryToVectorRegister: err = a.EncodeMemoryToVectorRegister(n) case OperandTypesVectorRegisterToMemory: err = a.EncodeVectorRegisterToMemory(n) case OperandTypesVectorRegisterToVectorRegister: err = a.EncodeVectorRegisterToVectorRegister(n) + case OperandTypesStaticConstToVectorRegister: + err = a.EncodeStaticConstToVectorRegister(n) default: err = fmt.Errorf("encoder undefined for [%s] operand type", n.Types) } @@ -590,35 +611,93 @@ func (a *AssemblerImpl) CompileConditionalRegisterSet(cond asm.ConditionalRegist n.DstReg = dstReg } +// CompileMemoryToVectorRegister implements Assembler.CompileMemoryToVectorRegister func (a *AssemblerImpl) CompileMemoryToVectorRegister( - instruction asm.Instruction, srcOffsetReg, dstReg asm.Register, arrangement VectorArrangement) { + instruction asm.Instruction, srcBaseReg asm.Register, dstOffset asm.ConstantValue, dstReg asm.Register, arrangement VectorArrangement) { + n := a.newNode(instruction, OperandTypesMemoryToVectorRegister) + n.SrcReg = srcBaseReg + n.SrcConst = dstOffset + n.DstReg = dstReg + n.VectorArrangement = arrangement +} + +// CompileMemoryWithRegisterOffsetToVectorRegister implements Assembler.CompileMemoryWithRegisterOffsetToVectorRegister +func (a *AssemblerImpl) CompileMemoryWithRegisterOffsetToVectorRegister(instruction asm.Instruction, + srcBaseReg, srcOffsetRegister asm.Register, dstReg asm.Register, arrangement VectorArrangement) { n := a.newNode(instruction, OperandTypesMemoryToVectorRegister) - n.SrcReg = srcOffsetReg + n.SrcReg = srcBaseReg + n.SrcReg2 = srcOffsetRegister n.DstReg = dstReg n.VectorArrangement = arrangement } +// CompileVectorRegisterToMemory implements Assembler.CompileVectorRegisterToMemory func (a *AssemblerImpl) CompileVectorRegisterToMemory( - instruction asm.Instruction, srcReg, dstOffsetReg asm.Register, arrangement VectorArrangement) { + instruction asm.Instruction, srcReg, dstBaseReg asm.Register, dstOffset asm.ConstantValue, arrangement VectorArrangement) { + n := a.newNode(instruction, OperandTypesVectorRegisterToMemory) + n.SrcReg = srcReg + n.DstReg = dstBaseReg + n.DstConst = dstOffset + n.VectorArrangement = arrangement +} + +// CompileVectorRegisterToMemoryWithRegisterOffset implements Assembler.CompileVectorRegisterToMemoryWithRegisterOffset +func (a *AssemblerImpl) CompileVectorRegisterToMemoryWithRegisterOffset(instruction asm.Instruction, + srcReg, dstBaseReg, dstOffsetRegister asm.Register, arrangement VectorArrangement) { n := a.newNode(instruction, OperandTypesVectorRegisterToMemory) n.SrcReg = srcReg - n.DstReg = dstOffsetReg + n.DstReg = dstBaseReg + n.DstReg2 = dstOffsetRegister n.VectorArrangement = arrangement } +// CompileRegisterToVectorRegister implements Assembler.CompileRegisterToVectorRegister func (a *AssemblerImpl) CompileRegisterToVectorRegister( instruction asm.Instruction, srcReg, dstReg asm.Register, arrangement VectorArrangement, index VectorIndex) { n := a.newNode(instruction, OperandTypesRegisterToVectorRegister) n.SrcReg = srcReg n.DstReg = dstReg n.VectorArrangement = arrangement - n.VectorIndex = index + n.DstVectorIndex = index } +// CompileVectorRegisterToRegister implements Assembler.CompileVectorRegisterToRegister +func (a *AssemblerImpl) CompileVectorRegisterToRegister(instruction asm.Instruction, srcReg, dstReg asm.Register, + arrangement VectorArrangement, index VectorIndex) { + n := a.newNode(instruction, OperandTypesVectorRegisterToRegister) + n.SrcReg = srcReg + n.DstReg = dstReg + n.VectorArrangement = arrangement + n.SrcVectorIndex = index +} + +// CompileVectorRegisterToVectorRegister implements Assembler.CompileVectorRegisterToVectorRegister func (a *AssemblerImpl) CompileVectorRegisterToVectorRegister( - instruction asm.Instruction, srcReg, dstReg asm.Register, arrangement VectorArrangement) { + instruction asm.Instruction, srcReg, dstReg asm.Register, arrangement VectorArrangement, srcIndex, dstIndex VectorIndex) { + n := a.newNode(instruction, OperandTypesVectorRegisterToVectorRegister) + n.SrcReg = srcReg + n.DstReg = dstReg + n.VectorArrangement = arrangement + n.SrcVectorIndex = srcIndex + n.DstVectorIndex = dstIndex +} + +// CompileVectorRegisterToVectorRegisterWithConst implements Assembler.CompileVectorRegisterToVectorRegisterWithConst +func (a *AssemblerImpl) CompileVectorRegisterToVectorRegisterWithConst(instruction asm.Instruction, + srcReg, dstReg asm.Register, arrangement VectorArrangement, c asm.ConstantValue) { n := a.newNode(instruction, OperandTypesVectorRegisterToVectorRegister) n.SrcReg = srcReg + n.SrcConst = c + n.DstReg = dstReg + n.VectorArrangement = arrangement +} + +// CompileLoadStaticConstToVectorRegister adds an instruction where the source operand is StaticConstant located in the memory +// and the destination is the dstReg. +func (a *AssemblerImpl) CompileLoadStaticConstToVectorRegister(instruction asm.Instruction, + c asm.StaticConst, dstReg asm.Register, arrangement VectorArrangement) { + n := a.newNode(instruction, OperandTypesStaticConstToVectorRegister) + n.staticConst = c n.DstReg = dstReg n.VectorArrangement = arrangement } @@ -1643,7 +1722,9 @@ func (a *AssemblerImpl) encodeLoadOrStoreWithConstOffset( // Go's assembler adds a const into the const pool at this point, // regardless of its usage; e.g. if we enter the then block of the following if statement, // the const is not used but it is added into the const pool. - a.addConstPool(offset32, uint64(a.Buf.Len())) + var c = make([]byte, 4) + binary.LittleEndian.PutUint32(c, uint32(offset)) + a.addConstPool(c, uint64(a.Buf.Len())) // https://github.com/golang/go/blob/release-branch.go1.15/src/cmd/internal/obj/arm64/asm7.go#L3529-L3532 // If the offset is within 24-bits, we can load it with two ADD instructions. @@ -1677,7 +1758,7 @@ func (a *AssemblerImpl) encodeLoadOrStoreWithConstOffset( a.Buf.Write([]byte{tmpRegBits, 0x0, 0x0, 0b00_011_0_00}) // Set the callback for the constant, and we set properly the offset in the callback. - a.setConstPoolCallback(offset32, func(offsetOfConst int) { + a.setConstPoolCallback(c, func(offsetOfConst int) { // ldr(literal) encodes offset divided by 4. offset := (offsetOfConst - int(loadLiteralOffsetInBinary)) / 4 bin := a.Buf.Bytes() @@ -2499,16 +2580,72 @@ func checkArrangementIndexPair(arr VectorArrangement, index VectorIndex) (err er } return } +func (a *AssemblerImpl) EncodeVectorRegisterToRegister(n *NodeImpl) (err error) { + if err = checkArrangementIndexPair(n.VectorArrangement, n.SrcVectorIndex); err != nil { + return + } -func (a *AssemblerImpl) EncodeRegisterToVectorRegister(n *NodeImpl) (err error) { - if n.Instruction != VMOV { - return errorEncodingUnsupported(n) + srcVecRegBits, err := vectorRegisterBits(n.SrcReg) + if err != nil { + return err } - if err = checkArrangementIndexPair(n.VectorArrangement, n.VectorIndex); err != nil { - return + dstRegBits, err := intRegisterBits(n.DstReg) + if err != nil { + return err } + switch n.Instruction { + case VMOV, SMOV: + var imm4 byte // imm4 as in "Advanced SIMD copy" https://developer.arm.com/documentation/ddi0596/2021-12/Index-by-Encoding/Data-Processing----Scalar-Floating-Point-and-Advanced-SIMD?lang=en + isSMOV := n.Instruction == SMOV + if isSMOV { + // SMOV: https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/SMOV--Signed-Move-vector-element-to-general-purpose-register- + imm4 = 0b0101 + } else { + // VMOV is translated as "UMOV": https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/UMOV--Unsigned-Move-vector-element-to-general-purpose-register- + imm4 = 0b0111 + } + + var imm5 byte + var q byte + switch n.VectorArrangement { + case VectorArrangementB: + imm5 |= 0b1 + imm5 |= byte(n.SrcVectorIndex) << 1 + case VectorArrangementH: + imm5 |= 0b10 + imm5 |= byte(n.SrcVectorIndex) << 2 + case VectorArrangementS: + if isSMOV { + return fmt.Errorf("invalid arrangement for SMOV: %s", n.VectorArrangement.String()) + } + imm5 |= 0b100 + imm5 |= byte(n.SrcVectorIndex) << 3 + case VectorArrangementD: + if isSMOV { + return fmt.Errorf("invalid arrangement for SMOV: %s", n.VectorArrangement.String()) + } + + imm5 |= 0b1000 + imm5 |= byte(n.SrcVectorIndex) << 4 + q = 0b1 + default: + return fmt.Errorf("unsupported arrangement for VMOV: %s", n.VectorArrangement) + } + a.Buf.Write([]byte{ + (srcVecRegBits << 5) | dstRegBits, + imm4<<3 | 0b100 | srcVecRegBits>>3, + imm5, + q<<6 | 0b00001110, + }) + default: + return errorEncodingUnsupported(n) + } + return +} + +func (a *AssemblerImpl) EncodeRegisterToVectorRegister(n *NodeImpl) (err error) { srcRegBits, err := intRegisterBits(n.SrcReg) if err != nil { return err @@ -2520,7 +2657,32 @@ func (a *AssemblerImpl) EncodeRegisterToVectorRegister(n *NodeImpl) (err error) } switch n.Instruction { + case DUP: + // https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/DUP--general---Duplicate-general-purpose-register-to-vector- + var imm5 byte + switch n.VectorArrangement { + case VectorArrangementB: + imm5 = 0b1 + case VectorArrangementH: + imm5 = 0b10 + case VectorArrangementS: + imm5 = 0b100 + case VectorArrangementD: + imm5 = 0b1000 + default: + return fmt.Errorf("unsupported arrangement for DUP: %s", n.VectorArrangement) + } + a.Buf.Write([]byte{ + (srcRegBits << 5) | dstVectorRegBits, + 0b11<<2 | srcRegBits>>3, + imm5, + 0b01_001110, + }) case VMOV: + if err = checkArrangementIndexPair(n.VectorArrangement, n.DstVectorIndex); err != nil { + return + } + // VMOV is translated as "INS(Vector, Element)" // Description: https://developer.arm.com/documentation/dui0802/a/A64-Advanced-SIMD-Vector-Instructions/INS--vector---general- // Encoding: https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/INS--general---Insert-vector-element-from-general-purpose-register-?lang=en @@ -2528,16 +2690,16 @@ func (a *AssemblerImpl) EncodeRegisterToVectorRegister(n *NodeImpl) (err error) switch n.VectorArrangement { case VectorArrangementB: imm5 |= 0b1 - imm5 |= byte(n.VectorIndex) << 1 + imm5 |= byte(n.DstVectorIndex) << 1 case VectorArrangementH: imm5 |= 0b10 - imm5 |= byte(n.VectorIndex) << 2 + imm5 |= byte(n.DstVectorIndex) << 2 case VectorArrangementS: imm5 |= 0b100 - imm5 |= byte(n.VectorIndex) << 3 + imm5 |= byte(n.DstVectorIndex) << 3 case VectorArrangementD: imm5 |= 0b1000 - imm5 |= byte(n.VectorIndex) << 4 + imm5 |= byte(n.DstVectorIndex) << 4 default: return fmt.Errorf("unsupported arrangement for VMOV: %s", n.VectorArrangement) } @@ -2554,7 +2716,7 @@ func (a *AssemblerImpl) EncodeRegisterToVectorRegister(n *NodeImpl) (err error) } func (a *AssemblerImpl) EncodeMemoryToVectorRegister(n *NodeImpl) (err error) { - srcRegBits, err := intRegisterBits(n.SrcReg) + srcBaseRegBits, err := intRegisterBits(n.SrcReg) if err != nil { return err } @@ -2565,19 +2727,65 @@ func (a *AssemblerImpl) EncodeMemoryToVectorRegister(n *NodeImpl) (err error) { } switch n.Instruction { - case VLD1: - if !(n.VectorArrangement >= VectorArrangement8B && n.VectorArrangement <= VectorArrangement2D) { - return fmt.Errorf("unsupported arrangement for VLD1: %s", n.VectorArrangement) + case VMOV: // translated as LDR(immediate,SIMD&FP) + // https://developer.arm.com/documentation/ddi0596/2021-12/SIMD-FP-Instructions/LDR--immediate--SIMD-FP---Load-SIMD-FP-Register--immediate-offset--?lang=en + var size, opcode byte + var dataSize, dataSizeLog2 int64 + switch n.VectorArrangement { + case VectorArrangementB: + size, opcode, dataSize, dataSizeLog2 = 0b00, 0b01, 1, 0 + case VectorArrangementH: + size, opcode, dataSize, dataSizeLog2 = 0b01, 0b01, 2, 1 + case VectorArrangementS: + size, opcode, dataSize, dataSizeLog2 = 0b10, 0b01, 4, 2 + case VectorArrangementD: + size, opcode, dataSize, dataSizeLog2 = 0b11, 0b01, 8, 3 + case VectorArrangementQ: + size, opcode, dataSize, dataSizeLog2 = 0b00, 0b11, 16, 4 + } + const v = 1 // v as in https://developer.arm.com/documentation/ddi0596/2021-12/Index-by-Encoding/Loads-and-Stores?lang=en#ldst_pos + if n.SrcReg2 != asm.NilRegister { + offsetRegBits, err := intRegisterBits(n.SrcReg2) + if err != nil { + return err + } + a.encodeLoadOrStoreWithRegisterOffset(srcBaseRegBits, offsetRegBits, dstVectorRegBits, opcode, size, v) + } else { + err = a.encodeLoadOrStoreWithConstOffset(srcBaseRegBits, dstVectorRegBits, + n.SrcConst, opcode, size, v, dataSize, dataSizeLog2) + } + case LD1R: + if n.SrcReg2 != asm.NilRegister || n.SrcConst != 0 { + return fmt.Errorf("offset for %s is not implemented", InstructionName(LD1R)) } - // https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/LD1--multiple-structures---Load-multiple-single-element-structures-to-one--two--three--or-four-registers-?lang=en - var opcode byte = 0b0111 // One register destination. - size, q := arrangementSizeQ(n.VectorArrangement) + var size, q byte + switch n.VectorArrangement { + case VectorArrangement8B: + size, q = 0b00, 0b0 + case VectorArrangement16B: + size, q = 0b00, 0b1 + case VectorArrangement4H: + size, q = 0b01, 0b0 + case VectorArrangement8H: + size, q = 0b01, 0b1 + case VectorArrangement2S: + size, q = 0b10, 0b0 + case VectorArrangement4S: + size, q = 0b10, 0b1 + case VectorArrangement1D: + size, q = 0b11, 0b0 + case VectorArrangement2D: + size, q = 0b11, 0b1 + } + + // No offset encoding. + // https://developer.arm.com/documentation/ddi0596/2021-12/SIMD-FP-Instructions/LD1R--Load-one-single-element-structure-and-Replicate-to-all-lanes--of-one-register--?lang=en#iclass_as_post_index a.Buf.Write([]byte{ - (srcRegBits << 5) | dstVectorRegBits, - opcode<<4 | (size << 2) | srcRegBits>>3, - 0b0100_0000, - q<<6 | 0b00001100, + (srcBaseRegBits << 5) | dstVectorRegBits, + 0b11_000000 | size<<2 | srcBaseRegBits>>3, + 0b01_000000, + q<<6 | 0b1101, }) default: return errorEncodingUnsupported(n) @@ -2613,72 +2821,209 @@ func (a *AssemblerImpl) EncodeVectorRegisterToMemory(n *NodeImpl) (err error) { return err } - dstRegBits, err := intRegisterBits(n.DstReg) + dstBaseRegBits, err := intRegisterBits(n.DstReg) if err != nil { return err } switch n.Instruction { - case VST1: - if !(n.VectorArrangement >= VectorArrangement8B && n.VectorArrangement <= VectorArrangement2D) { - return fmt.Errorf("unsupported arrangement for VST1: %s", n.VectorArrangement) + case VMOV: // translated as STR(immediate,SIMD&FP) + // https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/STR--immediate--SIMD-FP---Store-SIMD-FP-register--immediate-offset-- + var size, opcode byte + var dataSize, dataSizeLog2 int64 + switch n.VectorArrangement { + case VectorArrangementB: + size, opcode, dataSize, dataSizeLog2 = 0b00, 0b00, 1, 0 + case VectorArrangementH: + size, opcode, dataSize, dataSizeLog2 = 0b01, 0b00, 2, 1 + case VectorArrangementS: + size, opcode, dataSize, dataSizeLog2 = 0b10, 0b00, 4, 2 + case VectorArrangementD: + size, opcode, dataSize, dataSizeLog2 = 0b11, 0b00, 8, 3 + case VectorArrangementQ: + size, opcode, dataSize, dataSizeLog2 = 0b00, 0b10, 16, 4 } + const v = 1 // v as in https://developer.arm.com/documentation/ddi0596/2021-12/Index-by-Encoding/Loads-and-Stores?lang=en#ldst_pos - // https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/ST1--multiple-structures---Store-multiple-single-element-structures-from-one--two--three--or-four-registers-?lang=en - var opcode byte = 0b0111 // One register destination. - size, q := arrangementSizeQ(n.VectorArrangement) - a.Buf.Write([]byte{ - (dstRegBits << 5) | srcVectorRegBits, - opcode<<4 | (size << 2) | dstRegBits>>3, - 0x0, - q<<6 | 0b00001100, - }) + if n.DstReg2 != asm.NilRegister { + offsetRegBits, err := intRegisterBits(n.DstReg2) + if err != nil { + return err + } + a.encodeLoadOrStoreWithRegisterOffset(dstBaseRegBits, offsetRegBits, srcVectorRegBits, opcode, size, v) + } else { + err = a.encodeLoadOrStoreWithConstOffset(dstBaseRegBits, srcVectorRegBits, + n.DstConst, opcode, size, v, dataSize, dataSizeLog2) + } default: return errorEncodingUnsupported(n) } return } -func (a *AssemblerImpl) EncodeVectorRegisterToVectorRegister(n *NodeImpl) error { - srcVectorRegBits, err := vectorRegisterBits(n.SrcReg) +func (a *AssemblerImpl) EncodeStaticConstToVectorRegister(n *NodeImpl) (err error) { + if n.Instruction != VMOV { + return errorEncodingUnsupported(n) + } + + dstRegBits, err := vectorRegisterBits(n.DstReg) if err != nil { return err } + loadLiteralOffsetInBinary := uint64(a.Buf.Len()) + a.addConstPool(n.staticConst, loadLiteralOffsetInBinary) + + // LDR (literal, SIMD&FP) + // https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/LDR--literal--SIMD-FP---Load-SIMD-FP-Register--PC-relative-literal-- + var opc byte + var constLength int + switch n.VectorArrangement { + case VectorArrangementS: + opc, constLength = 0b00, 4 + case VectorArrangementD: + opc, constLength = 0b01, 8 + case VectorArrangementQ: + opc, constLength = 0b10, 16 + } + + if len(n.staticConst) != constLength { + return fmt.Errorf("invalid const length for %s: want %d but was %d", + n.VectorArrangement, constLength, len(n.staticConst)) + } + + a.Buf.Write([]byte{dstRegBits, 0x0, 0x0, opc<<6 | 0b11100}) + a.setConstPoolCallback(n.staticConst, func(offsetOfConst int) { + // LDR (literal, SIMD&FP) encodes offset divided by 4. + offset := (offsetOfConst - int(loadLiteralOffsetInBinary)) / 4 + bin := a.Buf.Bytes() + bin[loadLiteralOffsetInBinary] |= byte(offset << 5) + bin[loadLiteralOffsetInBinary+1] |= byte(offset >> 3) + bin[loadLiteralOffsetInBinary+2] |= byte(offset >> 11) + }) + return +} + +func (a *AssemblerImpl) EncodeVectorRegisterToVectorRegister(n *NodeImpl) (err error) { + var srcVectorRegBits byte + if n.SrcReg != RegRZR { + srcVectorRegBits, err = vectorRegisterBits(n.SrcReg) + if err != nil { + return err + } + } + dstVectorRegBits, err := vectorRegisterBits(n.DstReg) if err != nil { return err } switch n.Instruction { - case VMOV: - if n.VectorArrangement != VectorArrangement16B { - return fmt.Errorf("unsupported arrangement for VMOV: %s", n.VectorArrangement) + case DUP: + // https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/DUP--element---Duplicate-vector-element-to-vector-or-scalar- + if n.SrcVectorIndex == VectorIndexNone { + return fmt.Errorf("source vector index must be given for %s", InstructionName(DUP)) + } + var imm5 byte + switch n.VectorArrangement { + case VectorArrangementB: + imm5 |= 0b1 + imm5 |= byte(n.SrcVectorIndex) << 1 + case VectorArrangementH: + imm5 |= 0b10 + imm5 |= byte(n.SrcVectorIndex) << 2 + case VectorArrangementS: + imm5 |= 0b100 + imm5 |= byte(n.SrcVectorIndex) << 3 + case VectorArrangementD: + imm5 |= 0b1000 + imm5 |= byte(n.SrcVectorIndex) << 4 + default: + return fmt.Errorf("unsupported arrangement for VMOV: %d", n.VectorArrangement) } - // https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/MOV--vector---Move-vector--an-alias-of-ORR--vector--register-- a.Buf.Write([]byte{ (srcVectorRegBits << 5) | dstVectorRegBits, - 0b000111<<2 | srcVectorRegBits>>3, - 0b101<<5 | srcVectorRegBits, + 0b1<<2 | srcVectorRegBits>>3, + imm5, 0b0100_1110, }) - case VADD: + case VMOV: + if n.SrcVectorIndex != VectorIndexNone && n.DstVectorIndex != VectorIndexNone { + // This case VMOV is translated as MOV(vector, element) + // https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/MOV--element---Move-vector-element-to-another-vector-element--an-alias-of-INS--element-- + var imm5, imm4 byte + switch n.VectorArrangement { + case VectorArrangementB: + imm5 |= 0b1 + imm5 |= byte(n.DstVectorIndex) << 1 + imm4 = byte(n.SrcVectorIndex) + case VectorArrangementH: + imm5 |= 0b10 + imm5 |= byte(n.DstVectorIndex) << 2 + imm4 = byte(n.SrcVectorIndex) << 1 + case VectorArrangementS: + imm5 |= 0b100 + imm5 |= byte(n.DstVectorIndex) << 3 + imm4 = byte(n.SrcVectorIndex) << 2 + case VectorArrangementD: + imm5 |= 0b1000 + imm5 |= byte(n.DstVectorIndex) << 4 + imm4 = byte(n.SrcVectorIndex) << 3 + default: + return fmt.Errorf("unsupported arrangement for VMOV: %d", n.VectorArrangement) + } + a.Buf.Write([]byte{ + (srcVectorRegBits << 5) | dstVectorRegBits, + imm4<<3 | 1<<2 | srcVectorRegBits>>3, + imm5, + 0b01101110, + }) + } else { + // This case VMOV is translated as MOV(vector) + if n.VectorArrangement != VectorArrangement16B { + return fmt.Errorf("unsupported arrangement for VMOV: %s", n.VectorArrangement) + } + // https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/MOV--vector---Move-vector--an-alias-of-ORR--vector--register-- + a.Buf.Write([]byte{ + (srcVectorRegBits << 5) | dstVectorRegBits, + 0b000111<<2 | srcVectorRegBits>>3, + 0b101<<5 | srcVectorRegBits, + 0b0100_1110, + }) + } + case VADD, VSUB: if n.VectorArrangement == VectorArrangementNone || (n.VectorArrangement >= VectorArrangementB && n.VectorArrangement <= VectorArrangementD) || (n.VectorArrangement == VectorArrangement1D) { return fmt.Errorf("unsupported arrangement for VADD: %s", n.VectorArrangement) } - // https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/ADD--vector---Add--vector-- + var u byte + switch n.Instruction { + case VADD: + // https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/ADD--vector---Add--vector-- + u = 0b0 + case VSUB: + // https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/SUB--vector---Subtract--vector-- + u = 0b1 + } + size, q := arrangementSizeQ(n.VectorArrangement) a.Buf.Write([]byte{ - (srcVectorRegBits << 5) | dstVectorRegBits, - 0b100001<<2 | srcVectorRegBits>>3, - size<<6 | 0b1<<5 | dstVectorRegBits, - q<<6 | 0b1110, + (dstVectorRegBits << 5) | dstVectorRegBits, + 0b100001<<2 | dstVectorRegBits>>3, + size<<6 | 0b1<<5 | srcVectorRegBits, + q<<6 | u<<5 | 0b1110, }) - case VFADDS, VFADDD: - var sz byte - if n.Instruction == VFADDD { + case VFADDS, VFADDD, VFSUBS, VFSUBD: + var sz, b byte + switch n.Instruction { + case VFADDS: + case VFADDD: + sz = 0b1 + case VFSUBS: + b = 0b1 + case VFSUBD: + b = 0b1 sz = 0b1 } @@ -2686,9 +3031,153 @@ func (a *AssemblerImpl) EncodeVectorRegisterToVectorRegister(n *NodeImpl) error a.Buf.Write([]byte{ (srcVectorRegBits << 5) | dstVectorRegBits, 0b110101<<2 | srcVectorRegBits>>3, - sz<<6 | 0b1<<5 | dstVectorRegBits, + b<<7 | sz<<6 | 0b1<<5 | dstVectorRegBits, 0b1<<6 | 0b1110, }) + + case SSHLL, USHLL: + // SSHLL: https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/SSHLL--SSHLL2--Signed-Shift-Left-Long--immediate-- + // USHLL: https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/USHLL--USHLL2--Unsigned-Shift-Left-Long--immediate-- + var u byte + switch n.Instruction { + case SSHLL: + u = 0b0 + case USHLL: + u = 0b1 + } + + var immb, immh byte + switch n.VectorArrangement { + case VectorArrangement8B: + if n.SrcConst < 0 || n.SrcConst > 7 { + return fmt.Errorf("shift amount on %s must be between 0 and 7 for %s but was %d", + InstructionName(n.Instruction), n.VectorArrangement, n.SrcConst) + } + immb = byte(n.SrcConst) + immh = 0b0001 + case VectorArrangement4H: + if n.SrcConst < 0 || n.SrcConst > 15 { + return fmt.Errorf("shift amount on %s must be between 0 and 15 for %s but was %d", + InstructionName(n.Instruction), n.VectorArrangement, n.SrcConst) + } + immb = byte(n.SrcConst) & 0b111 + immh = 0b0010 | byte(n.SrcConst>>3) + case VectorArrangement2S: + if n.SrcConst < 0 || n.SrcConst > 31 { + return fmt.Errorf("shift amount on %s must be between 0 and 31 for %s but was %d", + InstructionName(n.Instruction), n.VectorArrangement, n.SrcConst) + } + immb = byte(n.SrcConst) & 0b111 + immh = 0b0100 | byte(n.SrcConst>>3) + default: + return fmt.Errorf("unsupported arrangement for %s: %s", + InstructionName(n.Instruction), n.VectorArrangement) + } + + a.Buf.Write([]byte{ + (srcVectorRegBits << 5) | dstVectorRegBits, + 0b101001<<2 | srcVectorRegBits>>3, + immh<<3 | immb, + u<<5 | 0b1111, + }) + case ADDP: + var opcode byte + var size, q byte + var rm, op byte + switch n.VectorArrangement { + case VectorArrangementD: + opcode = 0b10111_0 + size, q = 0b11, 0b1 + // ADDP (scalar) https://developer.arm.com/documentation/ddi0596/2021-12/SIMD-FP-Instructions/ADDP--scalar---Add-Pair-of-elements--scalar--?lang=en + rm = 0b10001 + op = 0b1 + default: + // ADDP (vector) https://developer.arm.com/documentation/ddi0596/2021-12/SIMD-FP-Instructions/ADDP--vector---Add-Pairwise--vector--?lang=en + opcode = 0b10111_1 + size, q = arrangementSizeQ(n.VectorArrangement) + rm = dstVectorRegBits + op = 0b0 + } + a.Buf.Write([]byte{ + (srcVectorRegBits << 5) | dstVectorRegBits, + opcode<<2 | srcVectorRegBits>>3, + size<<6 | 0b1<<5 | rm, + q<<6 | op<<4 | 0b01110, + }) + case UMAXP: + // "Advanced SIMD three same" in https://developer.arm.com/documentation/ddi0596/2021-12/Index-by-Encoding/Data-Processing----Scalar-Floating-Point-and-Advanced-SIMD?lang=en + var opcode, u byte + switch n.Instruction { + case UMAXP: + // https://developer.arm.com/documentation/ddi0596/2020-12/SIMD-FP-Instructions/UMAXP--Unsigned-Maximum-Pairwise- + opcode, u = 0b10100, 0b1 + } + var size, q byte = arrangementSizeQ(n.VectorArrangement) + a.Buf.Write([]byte{ + (srcVectorRegBits << 5) | dstVectorRegBits, + opcode<<3 | 0b1<<2 | srcVectorRegBits>>3, + size<<6 | 0b1<<5 | dstVectorRegBits, + q<<6 | u<<5 | 0b01110, + }) + case UMINV: + // "Advanced SIMD across lanes" in https://developer.arm.com/documentation/ddi0596/2021-12/Index-by-Encoding/Data-Processing----Scalar-Floating-Point-and-Advanced-SIMD?lang=en + var opcode, u byte = 0b11010, 0b1 + var size, q byte = arrangementSizeQ(n.VectorArrangement) + + a.Buf.Write([]byte{ + (srcVectorRegBits << 5) | dstVectorRegBits, + opcode<<4 | 0b1<<3 | srcVectorRegBits>>3, + size<<6 | 0b11000<<1 | opcode>>4, + q<<6 | u<<5 | 0b01110, + }) + case CMEQ: + const size byte = 0b11 + if n.SrcReg == RegRZR { + // CMEQ (zero, vector) + // https://developer.arm.com/documentation/ddi0596/2021-12/SIMD-FP-Instructions/CMEQ--zero---Compare-bitwise-Equal-to-zero--vector--?lang=en + a.Buf.Write([]byte{ + (dstVectorRegBits << 5) | dstVectorRegBits, + 0b100110<<2 | dstVectorRegBits>>3, + size<<6 | 0b1<<5, + 0b01001110, + }) + } else { + // CMEQ (register, vector) + // https://developer.arm.com/documentation/ddi0596/2021-12/SIMD-FP-Instructions/CMEQ--register---Compare-bitwise-Equal--vector--?lang=en + a.Buf.Write([]byte{ + (srcVectorRegBits << 5) | dstVectorRegBits, + 0b100011<<2 | srcVectorRegBits>>3, + size<<6 | 0b1<<5 | dstVectorRegBits, + 0b01101110, + }) + } + + case TBL1, TBL2: + // Interpret dstVectorRegBits as the index register (`Rm` in the doc) + // https://developer.arm.com/documentation/ddi0596/2021-12/SIMD-FP-Instructions/TBL--Table-vector-Lookup-?lang=en + + var l byte // `len` in the doc. + switch n.Instruction { + case TBL1: + l = 0b00 + case TBL2: + l = 0b01 + } + + var q byte + switch n.VectorArrangement { + case VectorArrangement16B: + q = 0b1 + case VectorArrangement8B: + q = 0b0 + } + + a.Buf.Write([]byte{ + (srcVectorRegBits << 5) | dstVectorRegBits, + l<<5 | srcVectorRegBits>>3, + dstVectorRegBits, + q<<6 | 0b1110, + }) default: return errorEncodingUnsupported(n) } @@ -2721,7 +3210,7 @@ func intRegisterBits(r asm.Register) (ret byte, err error) { func vectorRegisterBits(r asm.Register) (ret byte, err error) { if !isVectorRegister(r) { - err = fmt.Errorf("%s is not float", RegisterName(r)) + err = fmt.Errorf("%s is not vector", RegisterName(r)) } else { ret = byte(r - RegV0) } diff --git a/internal/asm/arm64/impl_test.go b/internal/asm/arm64/impl_test.go index 7e0b0c3712e..7fab8284680 100644 --- a/internal/asm/arm64/impl_test.go +++ b/internal/asm/arm64/impl_test.go @@ -107,30 +107,32 @@ func TestNodeImpl_String(t *testing.T) { exp: "VBIT (V1.B8, V2.B8), V3.B8", }, { - in: &NodeImpl{Instruction: VLD1, Types: OperandTypesMemoryToVectorRegister, + in: &NodeImpl{Instruction: VMOV, Types: OperandTypesMemoryToVectorRegister, SrcReg: RegR1, DstReg: RegV29, VectorArrangement: VectorArrangement2S}, - exp: "VLD1 [R1], V29.2S", + exp: "VMOV [R1], V29.2S", }, { - in: &NodeImpl{Instruction: VST1, Types: OperandTypesVectorRegisterToMemory, - DstReg: RegR1, SrcReg: RegV29, VectorArrangement: VectorArrangement2S}, - exp: "VST1 V29.2S, [R1]", + in: &NodeImpl{Instruction: VMOV, Types: OperandTypesVectorRegisterToMemory, + DstReg: RegR1, SrcReg: RegV29, VectorArrangement: VectorArrangementQ}, + exp: "VMOV V29.Q, [R1]", }, { in: &NodeImpl{Instruction: VMOV, Types: OperandTypesRegisterToVectorRegister, - SrcReg: RegR1, DstReg: RegV29, VectorArrangement: VectorArrangement2D, VectorIndex: 1}, + SrcReg: RegR1, DstReg: RegV29, VectorArrangement: VectorArrangement2D, DstVectorIndex: 1}, exp: "VMOV R1, V29.2D[1]", }, { in: &NodeImpl{Instruction: VCNT, Types: OperandTypesVectorRegisterToVectorRegister, - SrcReg: RegV3, DstReg: RegV29, VectorArrangement: VectorArrangement2D, VectorIndex: 1}, + SrcReg: RegV3, DstReg: RegV29, VectorArrangement: VectorArrangement2D, SrcVectorIndex: 1}, exp: "VCNT V3.V3, V29.V3", }, } for _, tt := range tests { tc := tt - require.Equal(t, tc.exp, tc.in.String()) + t.Run(tc.exp, func(t *testing.T) { + require.Equal(t, tc.exp, tc.in.String()) + }) } } @@ -387,10 +389,11 @@ func Test_CompileConditionalRegisterSet(t *testing.T) { func Test_CompileMemoryToVectorRegister(t *testing.T) { a := NewAssemblerImpl(RegR10) - a.CompileMemoryToVectorRegister(VMOV, RegR10, RegV3, VectorArrangement1D) + a.CompileMemoryToVectorRegister(VMOV, RegR10, 10, RegV3, VectorArrangement1D) actualNode := a.Current require.Equal(t, VMOV, actualNode.Instruction) require.Equal(t, RegR10, actualNode.SrcReg) + require.Equal(t, int64(10), actualNode.SrcConst) require.Equal(t, RegV3, actualNode.DstReg) require.Equal(t, OperandTypeMemory, actualNode.Types.src) require.Equal(t, OperandTypeVectorRegister, actualNode.Types.dst) @@ -399,11 +402,12 @@ func Test_CompileMemoryToVectorRegister(t *testing.T) { func Test_CompileVectorRegisterToMemory(t *testing.T) { a := NewAssemblerImpl(RegR10) - a.CompileVectorRegisterToMemory(VMOV, RegV3, RegR10, VectorArrangement1D) + a.CompileVectorRegisterToMemory(VMOV, RegV3, RegR10, 12, VectorArrangement1D) actualNode := a.Current require.Equal(t, VMOV, actualNode.Instruction) require.Equal(t, RegV3, actualNode.SrcReg) require.Equal(t, RegR10, actualNode.DstReg) + require.Equal(t, int64(12), actualNode.DstConst) require.Equal(t, OperandTypeVectorRegister, actualNode.Types.src) require.Equal(t, OperandTypeMemory, actualNode.Types.dst) require.Equal(t, VectorArrangement1D, actualNode.VectorArrangement) @@ -419,12 +423,25 @@ func Test_CompileRegisterToVectorRegister(t *testing.T) { require.Equal(t, OperandTypeRegister, actualNode.Types.src) require.Equal(t, OperandTypeVectorRegister, actualNode.Types.dst) require.Equal(t, VectorArrangement1D, actualNode.VectorArrangement) - require.Equal(t, VectorIndex(10), actualNode.VectorIndex) + require.Equal(t, VectorIndex(10), actualNode.DstVectorIndex) +} + +func Test_CompileVectorRegisterToRegister(t *testing.T) { + a := NewAssemblerImpl(RegR10) + a.CompileVectorRegisterToRegister(VMOV, RegR10, RegV3, VectorArrangement1D, 10) + actualNode := a.Current + require.Equal(t, VMOV, actualNode.Instruction) + require.Equal(t, RegR10, actualNode.SrcReg) + require.Equal(t, RegV3, actualNode.DstReg) + require.Equal(t, OperandTypeVectorRegister, actualNode.Types.src) + require.Equal(t, OperandTypeRegister, actualNode.Types.dst) + require.Equal(t, VectorArrangement1D, actualNode.VectorArrangement) + require.Equal(t, VectorIndex(10), actualNode.SrcVectorIndex) } func Test_CompileVectorRegisterToVectorRegister(t *testing.T) { a := NewAssemblerImpl(RegR10) - a.CompileVectorRegisterToVectorRegister(VMOV, RegV3, RegV10, VectorArrangement1D) + a.CompileVectorRegisterToVectorRegister(VMOV, RegV3, RegV10, VectorArrangement1D, 1, 2) actualNode := a.Current require.Equal(t, VMOV, actualNode.Instruction) require.Equal(t, RegV3, actualNode.SrcReg) @@ -432,6 +449,8 @@ func Test_CompileVectorRegisterToVectorRegister(t *testing.T) { require.Equal(t, OperandTypeVectorRegister, actualNode.Types.src) require.Equal(t, OperandTypeVectorRegister, actualNode.Types.dst) require.Equal(t, VectorArrangement1D, actualNode.VectorArrangement) + require.Equal(t, VectorIndex(1), actualNode.SrcVectorIndex) + require.Equal(t, VectorIndex(2), actualNode.DstVectorIndex) } func Test_checkRegisterToRegisterType(t *testing.T) { @@ -493,29 +512,684 @@ func Test_validateMemoryOffset(t *testing.T) { } } -func TestAssemblerImpl_EncodeVectorRegisterToVectorRegister(t *testing.T) { - x1, x2 := RegV2, RegV10 +func TestAssemblerImpl_EncodeVectorRegisterToMemory(t *testing.T) { + // These are not supported by golang-asm, so we test here instead of integration tests. + tests := []struct { + name string + n *NodeImpl + exp []byte + }{ + // Register offset cases. + { + name: "str b11, [x12, x6]", + n: &NodeImpl{ + Instruction: VMOV, + SrcReg: RegV11, + DstReg: RegR12, + DstReg2: RegR6, + VectorArrangement: VectorArrangementB, + }, + exp: []byte{0x8b, 0x69, 0x26, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "str h11, [x12, x6]", + n: &NodeImpl{ + Instruction: VMOV, + SrcReg: RegV11, + DstReg: RegR0, + DstReg2: RegR6, + VectorArrangement: VectorArrangementH, + }, + exp: []byte{0xb, 0x68, 0x26, 0x7c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "str s11, [x29, x6]", + n: &NodeImpl{ + Instruction: VMOV, + SrcReg: RegV11, + DstReg: RegR29, + DstReg2: RegR6, + VectorArrangement: VectorArrangementS, + }, + exp: []byte{0xab, 0x6b, 0x26, 0xbc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "str d0, [x0, x0]", + n: &NodeImpl{ + Instruction: VMOV, + SrcReg: RegV0, + DstReg: RegR0, + DstReg2: RegR0, + VectorArrangement: VectorArrangementD, + }, + exp: []byte{0x0, 0x68, 0x20, 0xfc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "str q30, [x30, x29]", + n: &NodeImpl{ + Instruction: VMOV, + SrcReg: RegV30, + DstReg: RegR30, + DstReg2: RegR29, + VectorArrangement: VectorArrangementQ, + }, + exp: []byte{0xde, 0x6b, 0xbd, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + // Constant offset cases. + { + name: "str b11, [x12, #0x7b]", + n: &NodeImpl{ + Instruction: VMOV, + SrcReg: RegV11, + DstReg: RegR12, + DstConst: 0x7b, + VectorArrangement: VectorArrangementB, + }, + exp: []byte{0x8b, 0xed, 0x1, 0x3d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "ldr w10, #0xc ; str h11, [x12, x10]", + n: &NodeImpl{ + Instruction: VMOV, + SrcReg: RegV11, + DstReg: RegR12, + DstConst: 1 << 30, + VectorArrangement: VectorArrangementH, + }, + exp: []byte{0x6a, 0x0, 0x0, 0x18, 0x8b, 0x69, 0x2a, 0x7c, 0x0, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x40}, + }, + { + name: "ldr w10, #0xc ; str s11, [x12, x10]", + n: &NodeImpl{ + Instruction: VMOV, + SrcReg: RegV11, + DstReg: RegR12, + DstConst: (1 << 28) + 4, + VectorArrangement: VectorArrangementS, + }, + exp: []byte{0x6a, 0x0, 0x0, 0x18, 0x8b, 0x69, 0x2a, 0xbc, 0x0, 0x0, 0x0, 0x14, 0x4, 0x0, 0x0, 0x10}, + }, + { + name: "str d11, [x12, #0x3d8]", + n: &NodeImpl{ + Instruction: VMOV, + SrcReg: RegV11, + DstReg: RegR12, + DstConst: 0x3d8, + VectorArrangement: VectorArrangementD, + }, + exp: []byte{0x8b, 0xed, 0x1, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "str q1, [x30]", + n: &NodeImpl{ + Instruction: VMOV, + SrcReg: RegV1, + DstReg: RegR30, + DstConst: 0, + VectorArrangement: VectorArrangementQ, + }, + exp: []byte{0xc1, 0x3, 0x80, 0x3d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + } + + for _, tt := range tests { + tc := tt + t.Run(tc.name, func(t *testing.T) { + a := NewAssemblerImpl(RegR10) + err := a.EncodeVectorRegisterToMemory(tc.n) + require.NoError(t, err) + + a.maybeFlushConstPool(true) + + actual, err := a.Assemble() + require.NoError(t, err) + require.Equal(t, tc.exp, actual, hex.EncodeToString(actual)) + }) + } +} + +func TestAssemblerImpl_EncodeMemoryToVectorRegister(t *testing.T) { + // These are not supported by golang-asm, so we test here instead of integration tests. tests := []struct { - inst asm.Instruction + name string + n *NodeImpl exp []byte + }{ + // ldr Register offset cases. + { + name: "ldr b11, [x12, x8]", + n: &NodeImpl{ + Instruction: VMOV, + SrcReg: RegR12, + SrcReg2: RegR8, + DstReg: RegV11, + VectorArrangement: VectorArrangementB, + }, + exp: []byte{0x8b, 0x69, 0x68, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "ldr h11, [x30, x0]", + n: &NodeImpl{ + Instruction: VMOV, + SrcReg: RegR30, + SrcReg2: RegR0, + DstReg: RegV11, + VectorArrangement: VectorArrangementH, + }, + exp: []byte{0xcb, 0x6b, 0x60, 0x7c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "ldr s11, [x0, x30]", + n: &NodeImpl{ + Instruction: VMOV, + SrcReg: RegR0, + SrcReg2: RegR30, + DstReg: RegV11, + VectorArrangement: VectorArrangementS, + }, + exp: []byte{0xb, 0x68, 0x7e, 0xbc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "ldr d11, [x15, x15]", + n: &NodeImpl{ + Instruction: VMOV, + SrcReg: RegR15, + SrcReg2: RegR15, + DstReg: RegV11, + VectorArrangement: VectorArrangementD, + }, + exp: []byte{0xeb, 0x69, 0x6f, 0xfc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "ldr q30, [x0, x0]", + n: &NodeImpl{ + Instruction: VMOV, + SrcReg: RegR0, + SrcReg2: RegR0, + DstReg: RegV30, + VectorArrangement: VectorArrangementQ, + }, + exp: []byte{0x1e, 0x68, 0xe0, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + // ldr Constant offset cases. + { + name: "ldr b11, [x12, #0x7b]", + n: &NodeImpl{ + Instruction: VMOV, + SrcReg: RegR12, + SrcConst: 0x7b, + DstReg: RegV11, + VectorArrangement: VectorArrangementB, + }, + exp: []byte{0x8b, 0xed, 0x41, 0x3d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "str h11, [x12, w30, uxtw]", + n: &NodeImpl{ + Instruction: VMOV, + DstReg: RegV11, + SrcReg: RegR12, + VectorArrangement: VectorArrangementH, + }, + exp: []byte{0x8b, 0x1, 0x40, 0x7d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "ldr w10, #0xc ; ldr s11, [x12, x10]", + n: &NodeImpl{ + Instruction: VMOV, + DstReg: RegV11, + SrcReg: RegR12, + SrcConst: 1 << 28, + VectorArrangement: VectorArrangementS, + }, + exp: []byte{0x6a, 0x0, 0x0, 0x18, 0x8b, 0x69, 0x6a, 0xbc, 0x0, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x10}, + }, + { + name: "ldr w10, #0xc ; ldr d11, [x12, x10]", + n: &NodeImpl{ + Instruction: VMOV, + DstReg: RegV11, + SrcReg: RegR12, + SrcConst: 1<<29 + 4, + VectorArrangement: VectorArrangementD, + }, + exp: []byte{0x6a, 0x0, 0x0, 0x18, 0x8b, 0x69, 0x6a, 0xfc, 0x0, 0x0, 0x0, 0x14, 0x4, 0x0, 0x0, 0x20}, + }, + { + name: "ldr w10, #0xc ; ldr q1, [x30, x10]", + n: &NodeImpl{ + Instruction: VMOV, + DstReg: RegV1, + SrcReg: RegR30, + SrcConst: 1<<17 + 4, + VectorArrangement: VectorArrangementQ, + }, + exp: []byte{0x6a, 0x0, 0x0, 0x18, 0xc1, 0x6b, 0xea, 0x3c, 0x0, 0x0, 0x0, 0x14, 0x4, 0x0, 0x2, 0x0}, + }, + // LD1R + { + name: "ld1r {v11.8b}, [x12]", + n: &NodeImpl{ + Instruction: LD1R, + SrcReg: RegR12, + DstReg: RegV11, + VectorArrangement: VectorArrangement8B, + }, + exp: []byte{0x8b, 0xc1, 0x40, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "ld1r {v11.16b}, [x12]", + n: &NodeImpl{ + Instruction: LD1R, + SrcReg: RegR12, + DstReg: RegV11, + VectorArrangement: VectorArrangement16B, + }, + exp: []byte{0x8b, 0xc1, 0x40, 0x4d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "ld1r {v11.4h}, [x12]", + n: &NodeImpl{ + Instruction: LD1R, + SrcReg: RegR12, + DstReg: RegV11, + VectorArrangement: VectorArrangement4H, + }, + exp: []byte{0x8b, 0xc5, 0x40, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "ld1r {v9.8h}, [x0]", + n: &NodeImpl{ + Instruction: LD1R, + SrcReg: RegR0, + DstReg: RegV0, + VectorArrangement: VectorArrangement8H, + }, + exp: []byte{0x0, 0xc4, 0x40, 0x4d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "ld1r {v11.2s}, [x12]", + n: &NodeImpl{ + Instruction: LD1R, + SrcReg: RegR12, + DstReg: RegV11, + VectorArrangement: VectorArrangement2S, + }, + exp: []byte{0x8b, 0xc9, 0x40, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "ld1r {v0.4s}, [x0]", + n: &NodeImpl{ + Instruction: LD1R, + SrcReg: RegR0, + DstReg: RegV0, + VectorArrangement: VectorArrangement4S, + }, + exp: []byte{0x0, 0xc8, 0x40, 0x4d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "ld1r {v11.1d}, [x12]", + n: &NodeImpl{ + Instruction: LD1R, + SrcReg: RegR12, + DstReg: RegV11, + VectorArrangement: VectorArrangement1D, + }, + exp: []byte{0x8b, 0xcd, 0x40, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "ld1r {v0.2d}, [x0]", + n: &NodeImpl{ + Instruction: LD1R, + SrcReg: RegR0, + DstReg: RegV0, + VectorArrangement: VectorArrangement2D, + }, + exp: []byte{0x0, 0xcc, 0x40, 0x4d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + } + + for _, tt := range tests { + tc := tt + t.Run(tc.name, func(t *testing.T) { + a := NewAssemblerImpl(RegR10) + err := a.EncodeMemoryToVectorRegister(tc.n) + require.NoError(t, err) + + a.maybeFlushConstPool(true) + + actual, err := a.Assemble() + require.NoError(t, err) + require.Equal(t, tc.exp, actual, hex.EncodeToString(actual)) + }) + } +} + +func TestAssemblerImpl_EncodeVectorRegisterToVectorRegister(t *testing.T) { + tests := []struct { + name string + x1, x2 asm.Register + inst asm.Instruction + c asm.ConstantValue + arr VectorArrangement + srcIndex, dstIndex VectorIndex + exp []byte }{ // These are not supported in golang-asm, so test it here instead of integration tests. - {inst: VFADDD, exp: []byte{ - 0x4a, 0xd4, 0x6a, 0x4e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - }}, - {inst: VFADDS, exp: []byte{ - 0x4a, 0xd4, 0x2a, 0x4e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - }}, + { + x1: RegV2, + x2: RegV10, + inst: VFADDD, + exp: []byte{ + 0x4a, 0xd4, 0x6a, 0x4e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, + }, + { + x1: RegV2, + x2: RegV10, + inst: VFADDS, + exp: []byte{ + 0x4a, 0xd4, 0x2a, 0x4e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, + }, + { + x1: RegV2, + x2: RegV10, + inst: VFSUBD, + exp: []byte{ + 0x4a, 0xd4, 0xea, 0x4e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, + }, + { + x1: RegV2, + x2: RegV10, + inst: VFSUBS, + exp: []byte{ + 0x4a, 0xd4, 0xaa, 0x4e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, + }, + { + x1: RegV2, + x2: RegV10, + inst: SSHLL, + exp: []byte{ + 0x4a, 0xa4, 0x8, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, + arr: VectorArrangement8B, + }, + { + x1: RegV2, + x2: RegV10, + inst: SSHLL, exp: []byte{ + 0x4a, 0xa4, 0xf, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, + arr: VectorArrangement8B, + c: 7, + }, + { + x1: RegV2, + x2: RegV10, + inst: SSHLL, + exp: []byte{ + 0x4a, 0xa4, 0x10, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, + arr: VectorArrangement4H, + }, + { + x1: RegV2, + x2: RegV10, + inst: SSHLL, + exp: []byte{ + 0x4a, 0xa4, 0x1f, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, + arr: VectorArrangement4H, + c: 15, + }, + { + x1: RegV2, + x2: RegV10, + inst: SSHLL, + exp: []byte{ + 0x4a, 0xa4, 0x20, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, + arr: VectorArrangement2S, + }, + { + x1: RegV2, + x2: RegV10, + inst: SSHLL, + exp: []byte{ + 0x4a, 0xa4, 0x3f, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, + arr: VectorArrangement2S, + c: 31, + }, + { + x1: RegV2, + x2: RegV10, + name: "ins v10.s[2], v2.s[1]", + inst: VMOV, + exp: []byte{0x4a, 0x24, 0x14, 0x6e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + arr: VectorArrangementS, + srcIndex: 1, + dstIndex: 2, + }, + { + x1: RegV2, + x2: RegV10, + name: "ins v10.s[0], v2.s[3]", + inst: VMOV, + exp: []byte{0x4a, 0x64, 0x4, 0x6e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + arr: VectorArrangementS, + srcIndex: 3, + dstIndex: 0, + }, + { + x1: RegV2, + x2: RegV10, + name: "ins v10.b[0], v2.b[0xf]", + inst: VMOV, + exp: []byte{0x4a, 0x7c, 0x1, 0x6e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + arr: VectorArrangementB, + srcIndex: 15, + dstIndex: 0, + }, + { + x1: RegV2, + x2: RegV10, + name: "ins v10.d[1], v2.d[0]", + inst: VMOV, + exp: []byte{0x4a, 0x4, 0x18, 0x6e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + arr: VectorArrangementD, + srcIndex: 0, + dstIndex: 1, + }, + { + x1: RegV2, + x2: RegV10, + name: "dup v10.2d, v2.d[0]", + inst: DUP, + exp: []byte{0x4a, 0x4, 0x8, 0x4e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + arr: VectorArrangementD, + srcIndex: 0, + }, + { + x1: RegV2, + x2: RegV10, + name: "dup v10.2d, v2.d[1]", + inst: DUP, + exp: []byte{0x4a, 0x4, 0x18, 0x4e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + arr: VectorArrangementD, + srcIndex: 1, + }, + { + x1: RegV2, + x2: RegV10, + name: "dup v10.4s, v2.s[3]", + inst: DUP, + exp: []byte{0x4a, 0x4, 0x1c, 0x4e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + arr: VectorArrangementS, + srcIndex: 3, + }, + { + x1: RegV2, + x2: RegV10, + name: "dup v10.8h, v2.h[7]", + inst: DUP, + exp: []byte{0x4a, 0x4, 0x1e, 0x4e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + arr: VectorArrangementH, + srcIndex: 7, + }, + { + x1: RegV2, + x2: RegV10, + name: "dup v10.16b, v2.b[0xf]", + inst: DUP, + exp: []byte{0x4a, 0x4, 0x1f, 0x4e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + arr: VectorArrangementB, + srcIndex: 15, + }, + { + x1: RegV2, + x2: RegV10, + name: "umaxp v10.16b, v2.16b, v10.16b", + inst: UMAXP, + exp: []byte{0x4a, 0xa4, 0x2a, 0x6e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + arr: VectorArrangement16B, + }, + { + x1: RegV2, + x2: RegV10, + name: "umaxp v10.8h, v2.8h, v10.8h", + inst: UMAXP, + exp: []byte{0x4a, 0xa4, 0x6a, 0x6e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + arr: VectorArrangement8H, + }, + { + x1: RegV2, + x2: RegV10, + name: "umaxp v10.4s, v2.8h, v10.4s", + inst: UMAXP, + exp: []byte{0x4a, 0xa4, 0xaa, 0x6e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + arr: VectorArrangement4S, + }, + { + x1: RegV11, + x2: RegV11, + name: "addp d11, v11.2d", + inst: ADDP, + exp: []byte{0x6b, 0xb9, 0xf1, 0x5e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + arr: VectorArrangementD, + }, + { + x1: RegV2, + x2: RegV10, + name: "addp v10.16b, v2.16b, v10.16b", + inst: ADDP, + exp: []byte{0x4a, 0xbc, 0x2a, 0x4e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + arr: VectorArrangement16B, + }, + { + x1: RegV2, + x2: RegV10, + name: "addp v10.8h, v2.8h, v10.8h", + inst: ADDP, + exp: []byte{0x4a, 0xbc, 0x6a, 0x4e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + arr: VectorArrangement8H, + }, + { + x1: RegV2, + x2: RegV10, + name: "addp v10.4s, v2.8h, v10.4s", + inst: ADDP, + exp: []byte{0x4a, 0xbc, 0xaa, 0x4e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + arr: VectorArrangement4S, + }, + { + x1: RegV2, + x2: RegV10, + name: "uminv b10, v2.16b", + inst: UMINV, + exp: []byte{0x4a, 0xa8, 0x31, 0x6e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + arr: VectorArrangement16B, + }, + { + x1: RegV2, + x2: RegV10, + name: "uminv h10, v2.8h", + inst: UMINV, + exp: []byte{0x4a, 0xa8, 0x71, 0x6e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + arr: VectorArrangement8H, + }, + { + x1: RegV2, + x2: RegV10, + name: "uminv s10, v2.4s", + inst: UMINV, + exp: []byte{0x4a, 0xa8, 0xb1, 0x6e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + arr: VectorArrangement4S, + }, + { + x1: RegV2, + x2: RegV10, + name: "cmeq v10.2d, v2.2d, v10.2d", + inst: CMEQ, + exp: []byte{0x4a, 0x8c, 0xea, 0x6e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + x1: RegRZR, + x2: RegV30, + name: "cmeq v30.2d, v30.2d, #0", + inst: CMEQ, + exp: []byte{0xde, 0x9b, 0xe0, 0x4e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "tbl v1.8b, {v0.16b}, v1.8b", + x1: RegV0, + x2: RegV1, + inst: TBL1, + arr: VectorArrangement8B, + exp: []byte{0x1, 0x0, 0x1, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "tbl v1.16b, {v0.16b}, v1.16b", + x1: RegV0, + x2: RegV1, + inst: TBL1, + arr: VectorArrangement16B, + exp: []byte{0x1, 0x0, 0x1, 0x4e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "tbl v30.8b, {v0.16b, v1.16b}, v30.8b", + x1: RegV0, + x2: RegV30, + inst: TBL2, + arr: VectorArrangement8B, + exp: []byte{0x1e, 0x20, 0x1e, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "tbl v1.16b, {v31.16b, v0.16b}, v1.16b", + x1: RegV31, + x2: RegV1, + inst: TBL2, + arr: VectorArrangement16B, + exp: []byte{0xe1, 0x23, 0x1, 0x4e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, } for _, tt := range tests { tc := tt - t.Run(InstructionName(tc.inst), func(t *testing.T) { + t.Run(tc.name, func(t *testing.T) { a := NewAssemblerImpl(asm.NilRegister) err := a.EncodeVectorRegisterToVectorRegister(&NodeImpl{ - Instruction: tc.inst, - SrcReg: x1, - DstReg: x2, + Instruction: tc.inst, + SrcReg: tc.x1, + SrcConst: tc.c, + DstReg: tc.x2, + VectorArrangement: tc.arr, + SrcVectorIndex: tc.srcIndex, + DstVectorIndex: tc.dstIndex, }) require.NoError(t, err) actual, err := a.Assemble() @@ -525,3 +1199,214 @@ func TestAssemblerImpl_EncodeVectorRegisterToVectorRegister(t *testing.T) { }) } } + +func TestAssemblerImpl_EncodeVectorRegisterToRegister(t *testing.T) { + tests := []struct { + name string + n *NodeImpl + exp []byte + }{ + // These are not supported in golang-asm, so test it here instead of integration tests. + { + name: "smov w10, v0.b[0xf]", + n: &NodeImpl{ + Instruction: SMOV, + SrcReg: RegV0, + DstReg: RegR10, + VectorArrangement: VectorArrangementB, + SrcVectorIndex: 15, + }, + exp: []byte{0xa, 0x2c, 0x1f, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "smov w10, v0.b[0]", + n: &NodeImpl{ + Instruction: SMOV, + SrcReg: RegV0, + DstReg: RegR10, + VectorArrangement: VectorArrangementB, + SrcVectorIndex: 0, + }, + exp: []byte{0xa, 0x2c, 0x1, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "smov w1, v30.h[7]", + n: &NodeImpl{ + Instruction: SMOV, + SrcReg: RegV30, + DstReg: RegR1, + VectorArrangement: VectorArrangementH, + SrcVectorIndex: 7, + }, + exp: []byte{0xc1, 0x2f, 0x1e, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "smov w1, v30.h[0]", + n: &NodeImpl{ + Instruction: SMOV, + SrcReg: RegV30, + DstReg: RegR1, + VectorArrangement: VectorArrangementH, + SrcVectorIndex: 0, + }, + exp: []byte{0xc1, 0x2f, 0x2, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + } + + for _, tt := range tests { + tc := tt + t.Run(tc.name, func(t *testing.T) { + a := NewAssemblerImpl(asm.NilRegister) + err := a.EncodeVectorRegisterToRegister(tc.n) + require.NoError(t, err) + actual, err := a.Assemble() + require.NoError(t, err) + + require.Equal(t, tc.exp, actual, hex.EncodeToString(actual)) + }) + } +} + +func TestAssemblerImpl_EncodeRegisterToVectorRegister(t *testing.T) { + tests := []struct { + name string + n *NodeImpl + exp []byte + }{ + // These are not supported in golang-asm, so test it here instead of integration tests. + { + name: "dup v10.2d, x10", + n: &NodeImpl{ + Instruction: DUP, + SrcReg: RegR10, + DstReg: RegV10, + VectorArrangement: VectorArrangementD, + }, + exp: []byte{0x4a, 0xd, 0x8, 0x4e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "dup v1.4s, w30", + n: &NodeImpl{ + Instruction: DUP, + SrcReg: RegR30, + DstReg: RegV1, + VectorArrangement: VectorArrangementS, + }, + exp: []byte{0xc1, 0xf, 0x4, 0x4e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "dup v30.8h, w1", + n: &NodeImpl{ + Instruction: DUP, + SrcReg: RegR1, + DstReg: RegV30, + VectorArrangement: VectorArrangementH, + }, + exp: []byte{0x3e, 0xc, 0x2, 0x4e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + { + name: "dup v30.16b, w1", + n: &NodeImpl{ + Instruction: DUP, + SrcReg: RegR1, + DstReg: RegV30, + VectorArrangement: VectorArrangementB, + }, + exp: []byte{0x3e, 0xc, 0x1, 0x4e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + } + + for _, tt := range tests { + tc := tt + t.Run(tc.name, func(t *testing.T) { + a := NewAssemblerImpl(asm.NilRegister) + err := a.EncodeRegisterToVectorRegister(tc.n) + require.NoError(t, err) + actual, err := a.Assemble() + require.NoError(t, err) + + require.Equal(t, tc.exp, actual, hex.EncodeToString(actual)) + }) + } +} + +func TestAssemblerImpl_EncodeStaticConstToVectorRegister(t *testing.T) { + tests := []struct { + name string + n *NodeImpl + exp []byte + }{ + { + name: "ldr q8, #8", + n: &NodeImpl{ + Instruction: VMOV, + DstReg: RegV8, + VectorArrangement: VectorArrangementQ, + staticConst: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + }, + exp: []byte{ + // 0x0: ldr q8, #8 + 0x48, 0x0, 0x0, 0x9c, + // Emitted after the end of function. + // 0x4: br #4 (See AssemblerImpl.maybeFlushConstPool) + 0x0, 0x0, 0x0, 0x14, + // 0x8: consts. + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, + 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, + }, + { + name: "ldr d30, #8", + n: &NodeImpl{ + Instruction: VMOV, + DstReg: RegV30, + VectorArrangement: VectorArrangementD, + staticConst: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + }, + exp: []byte{ + // 0x0: ldr d30, #8 + 0x5e, 0x0, 0x0, 0x5c, + // Emitted after the end of function. + // 0x4: br #4 (See AssemblerImpl.maybeFlushConstPool) + 0x0, 0x0, 0x0, 0x14, + // 0x8: consts. + 0x1, 0x2, 0x3, 0x4, + 0x5, 0x6, 0x7, 0x8, + }, + }, + { + name: "ldr s8, #8", + n: &NodeImpl{ + Instruction: VMOV, + DstReg: RegV8, + VectorArrangement: VectorArrangementS, + staticConst: []byte{1, 2, 3, 4}, + }, + exp: []byte{ + // 0x0: ldr s8, #8 + 0x48, 0x0, 0x0, 0x1c, + // Emitted after the end of function. + // 0x4: br #4 (See AssemblerImpl.maybeFlushConstPool) + 0x0, 0x0, 0x0, 0x14, + // 0x8: consts. + 0x1, 0x2, 0x3, 0x4, + 0x0, 0x0, 0x0, 0x0, + }, + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + a := NewAssemblerImpl(asm.NilRegister) + err := a.EncodeStaticConstToVectorRegister(tc.n) + require.NoError(t, err) + a.maybeFlushConstPool(true) + + actual, err := a.Assemble() + require.NoError(t, err) + + require.Equal(t, tc.exp, actual, hex.EncodeToString(actual)) + }) + } +} diff --git a/internal/asm/assembler.go b/internal/asm/assembler.go index 4e1397ed5fe..100037f04cc 100644 --- a/internal/asm/assembler.go +++ b/internal/asm/assembler.go @@ -51,6 +51,15 @@ type NodeOffsetInBinary = uint64 // ConstantValue represents a constant value used in an instruction. type ConstantValue = int64 +// StaticConst represents an arbitrary constant bytes which are pooled and emitted by assembler into the binary. +// These constants can be referenced by instructions. +type StaticConst = []byte + +// StaticConstKey returns a string whose underlying bytes equal the original const. +func StaticConstKey(c StaticConst) string { + return string(c) +} + // AssemblerBase is the common interface for assemblers among multiple architectures. // // Note: some of them can be implemented in an arch-independent way, but not all can be diff --git a/internal/engine/compiler/compiler.go b/internal/engine/compiler/compiler.go index 7fca183a44f..f8295e4d580 100644 --- a/internal/engine/compiler/compiler.go +++ b/internal/engine/compiler/compiler.go @@ -404,9 +404,24 @@ type compiler interface { // // https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/valid/instructions.html#xref-syntax-instructions-syntax-instr-table-mathsf-table-fill-x compileTableFill(*wazeroir.OperationTableFill) error - // compileConstV128 adds instructions to push a constant V128 value onto the stack. + // compileV128Const adds instructions to push a constant V128 value onto the stack. // See wasm.OpcodeVecV128Const - compileConstV128(*wazeroir.OperationConstV128) error - // compileAddV128 adds instruction to add two vector values whose shape is specified as `o.Shape`. - compileAddV128(o *wazeroir.OperationAddV128) error + compileV128Const(*wazeroir.OperationV128Const) error + // compileV128Add adds instruction to add two vector values whose shape is specified as `o.Shape`. + // See wasm.OpcodeVecI8x16Add wasm.OpcodeVecI16x8Add wasm.OpcodeVecI32x4Add wasm.OpcodeVecI64x2Add wasm.OpcodeVecF32x4Add wasm.OpcodeVecF64x2Add + compileV128Add(o *wazeroir.OperationV128Add) error + // compileV128Sub adds instruction to subtract two vector values whose shape is specified as `o.Shape`. + // See wasm.OpcodeVecI8x16Sub wasm.OpcodeVecI16x8Sub wasm.OpcodeVecI32x4Sub wasm.OpcodeVecI64x2Sub wasm.OpcodeVecF32x4Sub wasm.OpcodeVecF64x2Sub + compileV128Sub(o *wazeroir.OperationV128Sub) error + compileV128Load(o *wazeroir.OperationV128Load) error + compileV128LoadLane(o *wazeroir.OperationV128LoadLane) error + compileV128Store(o *wazeroir.OperationV128Store) error + compileV128StoreLane(o *wazeroir.OperationV128StoreLane) error + compileV128ExtractLane(o *wazeroir.OperationV128ExtractLane) error + compileV128ReplaceLane(o *wazeroir.OperationV128ReplaceLane) error + compileV128Splat(o *wazeroir.OperationV128Splat) error + compileV128Shuffle(o *wazeroir.OperationV128Shuffle) error + compileV128Swizzle(o *wazeroir.OperationV128Swizzle) error + compileV128AnyTrue(o *wazeroir.OperationV128AnyTrue) error + compileV128AllTrue(o *wazeroir.OperationV128AllTrue) error } diff --git a/internal/engine/compiler/compiler_memory_test.go b/internal/engine/compiler/compiler_memory_test.go index 90cc542f03f..6cb01832b6f 100644 --- a/internal/engine/compiler/compiler_memory_test.go +++ b/internal/engine/compiler/compiler_memory_test.go @@ -78,7 +78,7 @@ func TestCompiler_compileLoad(t *testing.T) { // For testing. Arbitrary number is fine. loadTargetValue := uint64(0x12_34_56_78_9a_bc_ef_fe) baseOffset := uint32(100) - arg := &wazeroir.MemoryImmediate{Offset: 361} + arg := &wazeroir.MemoryArg{Offset: 361} offset := baseOffset + arg.Offset tests := []struct { @@ -277,7 +277,7 @@ func TestCompiler_compileStore(t *testing.T) { // For testing. Arbitrary number is fine. storeTargetValue := uint64(math.MaxUint64) baseOffset := uint32(100) - arg := &wazeroir.MemoryImmediate{Offset: 361} + arg := &wazeroir.MemoryArg{Offset: 361} offset := arg.Offset + baseOffset tests := []struct { @@ -444,7 +444,7 @@ func TestCompiler_MemoryOutOfBounds(t *testing.T) { err = compiler.compileConstI32(&wazeroir.OperationConstI32{Value: base}) require.NoError(t, err) - arg := &wazeroir.MemoryImmediate{Offset: offset} + arg := &wazeroir.MemoryArg{Offset: offset} switch targetSizeInByte { case 1: diff --git a/internal/engine/compiler/compiler_numeric_test.go b/internal/engine/compiler/compiler_numeric_test.go index d99b6816a9d..8cf975ccdfb 100644 --- a/internal/engine/compiler/compiler_numeric_test.go +++ b/internal/engine/compiler/compiler_numeric_test.go @@ -17,7 +17,7 @@ func TestCompiler_compileConsts(t *testing.T) { wazeroir.OperationKindConstI64, wazeroir.OperationKindConstF32, wazeroir.OperationKindConstF64, - wazeroir.OperationKindConstV128, + wazeroir.OperationKindV128Const, } { op := op t.Run(op.String(), func(t *testing.T) { @@ -51,8 +51,8 @@ func TestCompiler_compileConsts(t *testing.T) { err = compiler.compileConstF32(&wazeroir.OperationConstF32{Value: math.Float32frombits(uint32(val))}) case wazeroir.OperationKindConstF64: err = compiler.compileConstF64(&wazeroir.OperationConstF64{Value: math.Float64frombits(val)}) - case wazeroir.OperationKindConstV128: - err = compiler.compileConstV128(&wazeroir.OperationConstV128{Lo: val, Hi: ^val}) + case wazeroir.OperationKindV128Const: + err = compiler.compileV128Const(&wazeroir.OperationV128Const{Lo: val, Hi: ^val}) } require.NoError(t, err) @@ -60,7 +60,7 @@ func TestCompiler_compileConsts(t *testing.T) { loc := compiler.runtimeValueLocationStack().peek() require.True(t, loc.onRegister()) - if op == wazeroir.OperationKindConstV128 { + if op == wazeroir.OperationKindV128Const { require.Equal(t, runtimeValueTypeV128Hi, loc.valueType) } @@ -76,7 +76,7 @@ func TestCompiler_compileConsts(t *testing.T) { // Compiler status must be returned. require.Equal(t, nativeCallStatusCodeReturned, env.compilerStatus()) - if op == wazeroir.OperationKindConstV128 { + if op == wazeroir.OperationKindV128Const { require.Equal(t, uint64(2), env.stackPointer()) // a vector value consists of two uint64. } else { require.Equal(t, uint64(1), env.stackPointer()) @@ -87,7 +87,7 @@ func TestCompiler_compileConsts(t *testing.T) { require.Equal(t, uint32(val), env.stackTopAsUint32()) case wazeroir.OperationKindConstI64, wazeroir.OperationKindConstF64: require.Equal(t, val, env.stackTopAsUint64()) - case wazeroir.OperationKindConstV128: + case wazeroir.OperationKindV128Const: lo, hi := env.stackTopAsV128() require.Equal(t, val, lo) require.Equal(t, ^val, hi) diff --git a/internal/engine/compiler/compiler_stack_test.go b/internal/engine/compiler/compiler_stack_test.go index cd15ac8b178..c7fc9be338e 100644 --- a/internal/engine/compiler/compiler_stack_test.go +++ b/internal/engine/compiler/compiler_stack_test.go @@ -180,7 +180,7 @@ func TestCompiler_compilePick_v128(t *testing.T) { // Set up the stack before picking. if tc.isPickTargetOnRegister { - err = compiler.compileConstV128(&wazeroir.OperationConstV128{ + err = compiler.compileV128Const(&wazeroir.OperationV128Const{ Lo: pickTargetLo, Hi: pickTargetHi, }) require.NoError(t, err) @@ -618,8 +618,10 @@ func TestCompiler_compileSwap_v128(t *testing.T) { require.NoError(t, err) if tc.x1OnRegister { - err = compiler.compileConstV128(&wazeroir.OperationConstV128{Lo: x1Lo, Hi: x1Hi}) + err = compiler.compileV128Const(&wazeroir.OperationV128Const{Lo: x1Lo, Hi: x1Hi}) require.NoError(t, err) + env.stack()[0] = 0xff + env.stack()[1] = 0xff } else { lo := compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack() // lo lo.valueType = runtimeValueTypeV128Lo @@ -632,7 +634,7 @@ func TestCompiler_compileSwap_v128(t *testing.T) { _ = compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack() // Dummy value! if tc.x2OnRegister { - err = compiler.compileConstV128(&wazeroir.OperationConstV128{Lo: x2Lo, Hi: x2Hi}) + err = compiler.compileV128Const(&wazeroir.OperationV128Const{Lo: x2Lo, Hi: x2Hi}) require.NoError(t, err) } else { lo := compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack() // lo diff --git a/internal/engine/compiler/compiler_value_location.go b/internal/engine/compiler/compiler_value_location.go index 7251b2f2992..ccf06ed71f2 100644 --- a/internal/engine/compiler/compiler_value_location.go +++ b/internal/engine/compiler/compiler_value_location.go @@ -212,6 +212,12 @@ func (v *runtimeValueLocationStack) pop() (loc *runtimeValueLocation) { return } +func (v *runtimeValueLocationStack) popV128() (loc *runtimeValueLocation) { + v.sp -= 2 + loc = v.stack[v.sp] + return +} + func (v *runtimeValueLocationStack) peek() (loc *runtimeValueLocation) { loc = v.stack[v.sp-1] return diff --git a/internal/engine/compiler/compiler_vec_test.go b/internal/engine/compiler/compiler_vec_test.go new file mode 100644 index 00000000000..310954693de --- /dev/null +++ b/internal/engine/compiler/compiler_vec_test.go @@ -0,0 +1,1759 @@ +package compiler + +import ( + "encoding/binary" + "math" + "testing" + + "github.com/tetratelabs/wazero/internal/testing/require" + "github.com/tetratelabs/wazero/internal/wasm" + "github.com/tetratelabs/wazero/internal/wazeroir" +) + +func TestCompiler_compileV128Add(t *testing.T) { + // TODO +} + +func TestCompiler_compileV128Sub(t *testing.T) { + + tests := []struct { + name string + shape wazeroir.Shape + x1, x2, exp [16]byte + }{ + { + name: "i8x16", + shape: wazeroir.ShapeI8x16, + x1: [16]byte{0: 1, 2: 10, 10: 10}, + x2: [16]byte{0: 10, 4: 5, 10: 5}, + exp: [16]byte{0: i8ToU8(-9), 2: 10, 4: i8ToU8(-5), 10: 5}, + }, + // TODO: add more cases. + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + env := newCompilerEnvironment() + compiler := env.requireNewCompiler(t, newCompiler, + &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) + + err := compiler.compilePreamble() + require.NoError(t, err) + + err = compiler.compileV128Const(&wazeroir.OperationV128Const{ + Lo: binary.LittleEndian.Uint64(tc.x1[:8]), + Hi: binary.LittleEndian.Uint64(tc.x1[8:]), + }) + require.NoError(t, err) + + err = compiler.compileV128Const(&wazeroir.OperationV128Const{ + Lo: binary.LittleEndian.Uint64(tc.x2[:8]), + Hi: binary.LittleEndian.Uint64(tc.x2[8:]), + }) + require.NoError(t, err) + + err = compiler.compileV128Sub(&wazeroir.OperationV128Sub{Shape: tc.shape}) + require.NoError(t, err) + + require.Equal(t, uint64(2), compiler.runtimeValueLocationStack().sp) + require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) + + err = compiler.compileReturnFunction() + require.NoError(t, err) + + // Generate and run the code under test. + code, _, _, err := compiler.compile() + require.NoError(t, err) + env.exec(code) + + require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) + + lo, hi := env.stackTopAsV128() + var actual [16]byte + binary.LittleEndian.PutUint64(actual[:8], lo) + binary.LittleEndian.PutUint64(actual[8:], hi) + require.Equal(t, tc.exp, actual) + }) + } +} + +func TestCompiler_compileV128Load(t *testing.T) { + tests := []struct { + name string + memSetupFn func(buf []byte) + loadType wazeroir.LoadV128Type + offset uint32 + exp [16]byte + }{ + { + name: "v128 offset=0", loadType: wazeroir.LoadV128Type128, offset: 0, + memSetupFn: func(buf []byte) { + copy(buf, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}) + }, + exp: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + }, + { + name: "v128 offset=2", loadType: wazeroir.LoadV128Type128, offset: 2, + memSetupFn: func(buf []byte) { + copy(buf, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}) + }, + exp: [16]byte{3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}, + }, + { + name: "8x8s offset=0", loadType: wazeroir.LoadV128Type8x8s, offset: 0, + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 3, 0xff, 5, 0xff, 7, 0xff, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + }) + }, + exp: [16]byte{ + 1, 0, 0xff, 0xff, 3, 0, 0xff, 0xff, 5, 0, 0xff, 0xff, 7, 0, 0xff, 0xff, + }, + }, + { + name: "8x8s offset=3", loadType: wazeroir.LoadV128Type8x8s, offset: 3, + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 3, 0xff, 5, 0xff, 7, 0xff, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + }) + }, + exp: [16]byte{ + 0xff, 0xff, 5, 0, 0xff, 0xff, 7, 0, 0xff, 0xff, 9, 0, 10, 0, 11, 0, + }, + }, + { + name: "8x8u offset=0", loadType: wazeroir.LoadV128Type8x8u, offset: 0, + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 3, 0xff, 5, 0xff, 7, 0xff, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + }) + }, + exp: [16]byte{ + 1, 0, 0xff, 0, 3, 0, 0xff, 0, 5, 0, 0xff, 0, 7, 0, 0xff, 0, + }, + }, + { + name: "8x8i offset=3", loadType: wazeroir.LoadV128Type8x8u, offset: 3, + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 3, 0xff, 5, 0xff, 7, 0xff, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + }) + }, + exp: [16]byte{ + 0xff, 0, 5, 0, 0xff, 0, 7, 0, 0xff, 0, 9, 0, 10, 0, 11, 0, + }, + }, + { + name: "16x4s offset=0", loadType: wazeroir.LoadV128Type16x4s, offset: 0, + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 3, 0xff, 5, 0xff, 7, 0xff, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + }) + }, + exp: [16]byte{ + 1, 0xff, 0xff, 0xff, + 3, 0xff, 0xff, 0xff, + 5, 0xff, 0xff, 0xff, + 7, 0xff, 0xff, 0xff, + }, + }, + { + name: "16x4s offset=3", loadType: wazeroir.LoadV128Type16x4s, offset: 3, + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 3, 0xff, 5, 6, 0xff, 0xff, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + }) + }, + exp: [16]byte{ + 0xff, 5, 0, 0, + 6, 0xff, 0xff, 0xff, + 0xff, 9, 0, 0, + 10, 11, 0, 0, + }, + }, + { + name: "16x4u offset=0", loadType: wazeroir.LoadV128Type16x4u, offset: 0, + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 3, 0xff, 5, 0xff, 7, 0xff, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + }) + }, + exp: [16]byte{ + 1, 0xff, 0, 0, + 3, 0xff, 0, 0, + 5, 0xff, 0, 0, + 7, 0xff, 0, 0, + }, + }, + { + name: "16x4u offset=3", loadType: wazeroir.LoadV128Type16x4u, offset: 3, + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 3, 0xff, 5, 6, 0xff, 0xff, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + }) + }, + exp: [16]byte{ + 0xff, 5, 0, 0, + 6, 0xff, 0, 0, + 0xff, 9, 0, 0, + 10, 11, 0, 0, + }, + }, + { + name: "32x2s offset=0", loadType: wazeroir.LoadV128Type32x2s, offset: 0, + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + }) + }, + exp: [16]byte{ + 1, 0xff, 3, 0xff, 0xff, 0xff, 0xff, 0xff, + 5, 6, 7, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + { + name: "32x2s offset=2", loadType: wazeroir.LoadV128Type32x2s, offset: 2, + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + }) + }, + exp: [16]byte{ + 3, 0xff, 5, 6, 0, 0, 0, 0, + 7, 0xff, 9, 0xff, 0xff, 0xff, 0xff, 0xff, + }, + }, + { + name: "32x2u offset=0", loadType: wazeroir.LoadV128Type32x2u, offset: 0, + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + }) + }, + exp: [16]byte{ + 1, 0xff, 3, 0xff, 0, 0, 0, 0, + 5, 6, 7, 0xff, 0, 0, 0, 0, + }, + }, + { + name: "32x2u offset=2", loadType: wazeroir.LoadV128Type32x2u, offset: 2, + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + }) + }, + exp: [16]byte{ + 3, 0xff, 5, 6, 0, 0, 0, 0, + 7, 0xff, 9, 0xff, 0, 0, 0, 0, + }, + }, + { + name: "32zero offset=0", loadType: wazeroir.LoadV128Type32zero, offset: 0, + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + }) + }, + exp: [16]byte{ + 1, 0xff, 3, 0xff, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }, + }, + { + name: "32zero offset=3", loadType: wazeroir.LoadV128Type32zero, offset: 3, + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 3, 0xff, 5, 6, 0xff, 8, 9, 0xff, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + }) + }, + exp: [16]byte{ + 0xff, 5, 6, 0xff, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }, + }, + { + name: "64zero offset=0", loadType: wazeroir.LoadV128Type64zero, offset: 0, + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + }) + }, + exp: [16]byte{ + 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, + 0, 0, 0, 0, 0, 0, 0, 0, + }, + }, + { + name: "64zero offset=2", loadType: wazeroir.LoadV128Type64zero, offset: 2, + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + }) + }, + exp: [16]byte{ + 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, + 0, 0, 0, 0, 0, 0, 0, 0, + }, + }, + { + name: "8splat offset=0", loadType: wazeroir.LoadV128Type8Splat, offset: 0, + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, + }) + }, + exp: [16]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + }, + { + name: "8splat offset=1", loadType: wazeroir.LoadV128Type8Splat, offset: 1, + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, + }) + }, + exp: [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + }, + { + name: "16splat offset=0", loadType: wazeroir.LoadV128Type16Splat, offset: 0, + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, + }) + }, + exp: [16]byte{1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff}, + }, + { + name: "16splat offset=5", loadType: wazeroir.LoadV128Type16Splat, offset: 5, + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, + }) + }, + exp: [16]byte{6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7}, + }, + { + name: "32splat offset=0", loadType: wazeroir.LoadV128Type32Splat, offset: 0, + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, + }) + }, + exp: [16]byte{1, 0xff, 3, 0xff, 1, 0xff, 3, 0xff, 1, 0xff, 3, 0xff, 1, 0xff, 3, 0xff}, + }, + { + name: "32splat offset=1", loadType: wazeroir.LoadV128Type32Splat, offset: 1, + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, + }) + }, + exp: [16]byte{0xff, 3, 0xff, 5, 0xff, 3, 0xff, 5, 0xff, 3, 0xff, 5, 0xff, 3, 0xff, 5}, + }, + { + name: "64splat offset=0", loadType: wazeroir.LoadV128Type64Splat, offset: 0, + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, + }) + }, + exp: [16]byte{1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 1, 0xff, 3, 0xff, 5, 6, 7, 0xff}, + }, + { + name: "64splat offset=1", loadType: wazeroir.LoadV128Type64Splat, offset: 1, + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, + }) + }, + exp: [16]byte{0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9}, + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + env := newCompilerEnvironment() + tc.memSetupFn(env.memory()) + + compiler := env.requireNewCompiler(t, newCompiler, + &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) + + err := compiler.compilePreamble() + require.NoError(t, err) + + err = compiler.compileConstI32(&wazeroir.OperationConstI32{Value: tc.offset}) + require.NoError(t, err) + + err = compiler.compileV128Load(&wazeroir.OperationV128Load{ + Type: tc.loadType, Arg: &wazeroir.MemoryArg{}, + }) + require.NoError(t, err) + + require.Equal(t, uint64(2), compiler.runtimeValueLocationStack().sp) + require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) + loadedLocation := compiler.runtimeValueLocationStack().peek() + require.True(t, loadedLocation.onRegister()) + + err = compiler.compileReturnFunction() + require.NoError(t, err) + + // Generate and run the code under test. + code, _, _, err := compiler.compile() + require.NoError(t, err) + env.exec(code) + + require.Equal(t, uint64(2), env.stackPointer()) + lo, hi := env.stackTopAsV128() + + var actual [16]byte + binary.LittleEndian.PutUint64(actual[:8], lo) + binary.LittleEndian.PutUint64(actual[8:], hi) + require.Equal(t, tc.exp, actual) + }) + } +} + +func TestCompiler_compileV128LoadLane(t *testing.T) { + originalVecLo, originalVecHi := uint64(0), uint64(0) + tests := []struct { + name string + memSetupFn func(buf []byte) + laneIndex, laneSize byte + offset uint32 + exp [16]byte + }{ + { + name: "8_lane offset=0 laneIndex=0", + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, + }) + }, + laneSize: 8, + laneIndex: 0, + offset: 0, + exp: [16]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "8_lane offset=1 laneIndex=0", + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, + }) + }, + laneSize: 8, + laneIndex: 0, + offset: 1, + exp: [16]byte{0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "8_lane offset=1 laneIndex=5", + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, + }) + }, + laneSize: 8, + laneIndex: 5, + offset: 1, + exp: [16]byte{0, 0, 0, 0, 0, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "16_lane offset=0 laneIndex=0", + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 1, 0xa, + }) + }, + laneSize: 16, + laneIndex: 0, + offset: 0, + exp: [16]byte{1, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "16_lane offset=1 laneIndex=0", + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 1, 0xa, + }) + }, + laneSize: 16, + laneIndex: 0, + offset: 1, + exp: [16]byte{0xff, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "16_lane offset=1 laneIndex=5", + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 1, 0xa, + }) + }, + laneSize: 16, + laneIndex: 5, + offset: 1, + exp: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 1, 0, 0, 0, 0}, + }, + { + name: "32_lane offset=0 laneIndex=0", + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 1, 0xa, 0x9, 0x8, + }) + }, + laneSize: 32, + laneIndex: 0, + offset: 0, + exp: [16]byte{1, 0xff, 1, 0xa, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "32_lane offset=1 laneIndex=0", + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 1, 0xa, 0x9, 0x8, + }) + }, + laneSize: 32, + laneIndex: 0, + offset: 1, + exp: [16]byte{0xff, 1, 0xa, 0x9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "32_lane offset=1 laneIndex=3", + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 1, 0xa, 0x9, 0x8, + }) + }, + laneSize: 32, + laneIndex: 3, + offset: 1, + exp: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 1, 0xa, 0x9}, + }, + + { + name: "64_lane offset=0 laneIndex=0", + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 1, 0xa, 0x9, 0x8, 0x1, 0x2, 0x3, 0x4, + }) + }, + laneSize: 64, + laneIndex: 0, + offset: 0, + exp: [16]byte{1, 0xff, 1, 0xa, 0x9, 0x8, 0x1, 0x2, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "64_lane offset=1 laneIndex=0", + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 1, 0xa, 0x9, 0x8, 0x1, 0x2, 0x3, 0x4, + }) + }, + laneSize: 64, + laneIndex: 0, + offset: 1, + exp: [16]byte{0xff, 1, 0xa, 0x9, 0x8, 0x1, 0x2, 0x3, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "64_lane offset=3 laneIndex=1", + memSetupFn: func(buf []byte) { + copy(buf, []byte{ + 1, 0xff, 1, 0xa, 0x9, 0x8, 0x1, 0x2, 0x3, 0x4, 0xa, + }) + }, + laneSize: 64, + laneIndex: 1, + offset: 3, + exp: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0xa, 0x9, 0x8, 0x1, 0x2, 0x3, 0x4, 0xa}, + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + env := newCompilerEnvironment() + tc.memSetupFn(env.memory()) + + compiler := env.requireNewCompiler(t, newCompiler, + &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) + + err := compiler.compilePreamble() + require.NoError(t, err) + + err = compiler.compileConstI32(&wazeroir.OperationConstI32{Value: tc.offset}) + require.NoError(t, err) + + err = compiler.compileV128Const(&wazeroir.OperationV128Const{ + Lo: originalVecLo, + Hi: originalVecHi, + }) + require.NoError(t, err) + + err = compiler.compileV128LoadLane(&wazeroir.OperationV128LoadLane{ + LaneIndex: tc.laneIndex, LaneSize: tc.laneSize, Arg: &wazeroir.MemoryArg{}, + }) + require.NoError(t, err) + + require.Equal(t, uint64(2), compiler.runtimeValueLocationStack().sp) + require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) + loadedLocation := compiler.runtimeValueLocationStack().peek() + require.True(t, loadedLocation.onRegister()) + + err = compiler.compileReturnFunction() + require.NoError(t, err) + + // Generate and run the code under test. + code, _, _, err := compiler.compile() + require.NoError(t, err) + env.exec(code) + + require.Equal(t, uint64(2), env.stackPointer()) + lo, hi := env.stackTopAsV128() + + var actual [16]byte + binary.LittleEndian.PutUint64(actual[:8], lo) + binary.LittleEndian.PutUint64(actual[8:], hi) + require.Equal(t, tc.exp, actual) + }) + } +} + +func TestCompiler_compileV128Store(t *testing.T) { + tests := []struct { + name string + offset uint32 + }{ + {name: "offset=1", offset: 1}, + {name: "offset=5", offset: 5}, + {name: "offset=10", offset: 10}, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + env := newCompilerEnvironment() + + compiler := env.requireNewCompiler(t, newCompiler, + &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) + + err := compiler.compilePreamble() + require.NoError(t, err) + + err = compiler.compileConstI32(&wazeroir.OperationConstI32{Value: tc.offset}) + require.NoError(t, err) + + err = compiler.compileV128Const(&wazeroir.OperationV128Const{Lo: ^uint64(0), Hi: ^uint64(0)}) + require.NoError(t, err) + + err = compiler.compileV128Store(&wazeroir.OperationV128Store{Arg: &wazeroir.MemoryArg{}}) + require.NoError(t, err) + + require.Equal(t, uint64(0), compiler.runtimeValueLocationStack().sp) + require.Equal(t, 0, len(compiler.runtimeValueLocationStack().usedRegisters)) + + err = compiler.compileReturnFunction() + require.NoError(t, err) + + // Generate and run the code under test. + code, _, _, err := compiler.compile() + require.NoError(t, err) + env.exec(code) + + require.Equal(t, uint64(0), env.stackPointer()) + + mem := env.memory() + require.Equal(t, []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + mem[tc.offset:tc.offset+16]) + }) + } +} + +func TestCompiler_compileV128StoreLane(t *testing.T) { + vecBytes := [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} + tests := []struct { + name string + laneIndex, laneSize byte + offset uint32 + exp [16]byte + }{ + { + name: "8_lane offset=0 laneIndex=0", + laneSize: 8, + laneIndex: 0, + offset: 0, + exp: [16]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "8_lane offset=1 laneIndex=0", + laneSize: 8, + laneIndex: 0, + offset: 1, + exp: [16]byte{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "8_lane offset=3 laneIndex=5", + laneSize: 8, + laneIndex: 5, + offset: 3, + exp: [16]byte{0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "16_lane offset=0 laneIndex=0", + laneSize: 16, + laneIndex: 0, + offset: 0, + exp: [16]byte{1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "16_lane offset=1 laneIndex=0", + laneSize: 16, + laneIndex: 0, + offset: 1, + exp: [16]byte{0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "16_lane offset=5 laneIndex=7", + laneSize: 16, + laneIndex: 7, + offset: 5, + exp: [16]byte{0, 0, 0, 0, 0, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + + { + name: "32_lane offset=0 laneIndex=0", + laneSize: 32, + laneIndex: 0, + offset: 0, + exp: [16]byte{1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "32_lane offset=1 laneIndex=0", + laneSize: 32, + laneIndex: 0, + offset: 1, + exp: [16]byte{0, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "32_lane offset=5 laneIndex=3", + laneSize: 32, + laneIndex: 3, + offset: 5, + exp: [16]byte{0, 0, 0, 0, 0, 13, 14, 15, 16, 0, 0, 0, 0, 0, 0, 0}, + }, + + { + name: "64_lane offset=0 laneIndex=0", + laneSize: 64, + laneIndex: 0, + offset: 0, + exp: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "64_lane offset=1 laneIndex=0", + laneSize: 64, + laneIndex: 0, + offset: 1, + exp: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "64_lane offset=5 laneIndex=3", + laneSize: 64, + laneIndex: 1, + offset: 6, + exp: [16]byte{0, 0, 0, 0, 0, 0, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0}, + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + env := newCompilerEnvironment() + + compiler := env.requireNewCompiler(t, newCompiler, + &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) + + err := compiler.compilePreamble() + require.NoError(t, err) + + err = compiler.compileConstI32(&wazeroir.OperationConstI32{Value: tc.offset}) + require.NoError(t, err) + + err = compiler.compileV128Const(&wazeroir.OperationV128Const{ + Lo: binary.LittleEndian.Uint64(vecBytes[:8]), + Hi: binary.LittleEndian.Uint64(vecBytes[8:]), + }) + require.NoError(t, err) + + err = compiler.compileV128StoreLane(&wazeroir.OperationV128StoreLane{ + LaneIndex: tc.laneIndex, LaneSize: tc.laneSize, Arg: &wazeroir.MemoryArg{}, + }) + require.NoError(t, err) + + require.Equal(t, uint64(0), compiler.runtimeValueLocationStack().sp) + require.Equal(t, 0, len(compiler.runtimeValueLocationStack().usedRegisters)) + + err = compiler.compileReturnFunction() + require.NoError(t, err) + + // Generate and run the code under test. + code, _, _, err := compiler.compile() + require.NoError(t, err) + env.exec(code) + + require.Equal(t, tc.exp[:], env.memory()[:16]) + }) + } +} + +func TestCompiler_compileV128ExtractLane(t *testing.T) { + tests := []struct { + name string + vecBytes [16]byte + shape wazeroir.Shape + signed bool + laneIndex byte + exp uint64 + }{ + { + name: "i8x16 unsigned index=0", + vecBytes: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + shape: wazeroir.ShapeI8x16, + signed: false, + laneIndex: 0, + exp: uint64(byte(1)), + }, + { + name: "i8x16 unsigned index=15", + vecBytes: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0xff}, + shape: wazeroir.ShapeI8x16, + signed: false, + laneIndex: 15, + exp: uint64(byte(0xff)), + }, + { + name: "i8x16 signed index=0", + vecBytes: [16]byte{0xf1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + shape: wazeroir.ShapeI8x16, + signed: true, + laneIndex: 0, + exp: uint64(0xff_ff_ff_f1), + }, + { + name: "i8x16 signed index=1", + vecBytes: [16]byte{0xf0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + shape: wazeroir.ShapeI8x16, + signed: true, + laneIndex: 1, + exp: uint64(2), + }, + { + name: "i16x8 unsigned index=0", + vecBytes: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + shape: wazeroir.ShapeI16x8, + signed: false, + laneIndex: 0, + exp: uint64(uint16(0x2<<8 | 0x1)), + }, + { + name: "i16x8 unsigned index=7", + vecBytes: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0xff}, + shape: wazeroir.ShapeI16x8, + signed: false, + laneIndex: 7, + exp: uint64(uint16(0xff<<8 | 15)), + }, + { + name: "i16x8 signed index=0", + vecBytes: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + shape: wazeroir.ShapeI16x8, + signed: true, + laneIndex: 0, + exp: uint64(uint16(0x2<<8 | 0x1)), + }, + { + name: "i16x8 signed index=7", + vecBytes: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0xf1}, + shape: wazeroir.ShapeI16x8, + signed: true, + laneIndex: 7, + exp: uint64(uint32(0xffff<<16) | uint32(uint16(0xf1<<8|15))), + }, + { + name: "i32x4 index=0", + vecBytes: [16]byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}, + shape: wazeroir.ShapeI32x4, + laneIndex: 0, + exp: uint64(uint32(0x04_03_02_01)), + }, + { + name: "i32x4 index=3", + vecBytes: [16]byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}, + shape: wazeroir.ShapeI32x4, + laneIndex: 3, + exp: uint64(uint32(0x16_15_14_13)), + }, + { + name: "i64x4 index=0", + vecBytes: [16]byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}, + shape: wazeroir.ShapeI64x2, + laneIndex: 0, + exp: uint64(0x08_07_06_05_04_03_02_01), + }, + { + name: "i64x4 index=1", + vecBytes: [16]byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}, + shape: wazeroir.ShapeI64x2, + laneIndex: 1, + exp: uint64(0x16_15_14_13_12_11_10_09), + }, + { + name: "f32x4 index=0", + vecBytes: [16]byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}, + shape: wazeroir.ShapeF32x4, + laneIndex: 0, + exp: uint64(uint32(0x04_03_02_01)), + }, + { + name: "f32x4 index=3", + vecBytes: [16]byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}, + shape: wazeroir.ShapeF32x4, + laneIndex: 3, + exp: uint64(uint32(0x16_15_14_13)), + }, + { + name: "f64x4 index=0", + vecBytes: [16]byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}, + shape: wazeroir.ShapeF64x2, + laneIndex: 0, + exp: uint64(0x08_07_06_05_04_03_02_01), + }, + { + name: "f64x4 index=1", + vecBytes: [16]byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}, + shape: wazeroir.ShapeF64x2, + laneIndex: 1, + exp: uint64(0x16_15_14_13_12_11_10_09), + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + env := newCompilerEnvironment() + + compiler := env.requireNewCompiler(t, newCompiler, + &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) + + err := compiler.compilePreamble() + require.NoError(t, err) + + err = compiler.compileV128Const(&wazeroir.OperationV128Const{ + Lo: binary.LittleEndian.Uint64(tc.vecBytes[:8]), + Hi: binary.LittleEndian.Uint64(tc.vecBytes[8:]), + }) + require.NoError(t, err) + + err = compiler.compileV128ExtractLane(&wazeroir.OperationV128ExtractLane{ + LaneIndex: tc.laneIndex, + Signed: tc.signed, + Shape: tc.shape, + }) + require.NoError(t, err) + + require.Equal(t, uint64(1), compiler.runtimeValueLocationStack().sp) + require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) + + vt := compiler.runtimeValueLocationStack().peek().valueType + switch tc.shape { + case wazeroir.ShapeI8x16, wazeroir.ShapeI16x8, wazeroir.ShapeI32x4: + require.Equal(t, runtimeValueTypeI32, vt) + case wazeroir.ShapeI64x2: + require.Equal(t, runtimeValueTypeI64, vt) + case wazeroir.ShapeF32x4: + require.Equal(t, runtimeValueTypeF32, vt) + case wazeroir.ShapeF64x2: + require.Equal(t, runtimeValueTypeF64, vt) + } + + err = compiler.compileReturnFunction() + require.NoError(t, err) + + // Generate and run the code under test. + code, _, _, err := compiler.compile() + require.NoError(t, err) + env.exec(code) + + switch tc.shape { + case wazeroir.ShapeI8x16, wazeroir.ShapeI16x8, wazeroir.ShapeI32x4, wazeroir.ShapeF32x4: + require.Equal(t, uint32(tc.exp), env.stackTopAsUint32()) + case wazeroir.ShapeI64x2, wazeroir.ShapeF64x2: + require.Equal(t, tc.exp, env.stackTopAsUint64()) + } + }) + } +} + +func TestCompiler_compileV128ReplaceLane(t *testing.T) { + tests := []struct { + name string + originValueSetupFn func(*testing.T, compilerImpl) + shape wazeroir.Shape + laneIndex byte + exp [16]byte + lo, hi uint64 + }{ + { + name: "i8x16 index=0", + shape: wazeroir.ShapeI8x16, + laneIndex: 5, + originValueSetupFn: func(t *testing.T, c compilerImpl) { + err := c.compileConstI32(&wazeroir.OperationConstI32{Value: 0xff}) + require.NoError(t, err) + }, + exp: [16]byte{5: 0xff}, + }, + { + name: "i8x16 index=3", + shape: wazeroir.ShapeI8x16, + laneIndex: 5, + originValueSetupFn: func(t *testing.T, c compilerImpl) { + err := c.compileConstI32(&wazeroir.OperationConstI32{Value: 0xff << 8}) + require.NoError(t, err) + }, + exp: [16]byte{}, + }, + { + name: "i8x16 index=5", + shape: wazeroir.ShapeI8x16, + laneIndex: 5, + originValueSetupFn: func(t *testing.T, c compilerImpl) { + err := c.compileConstI32(&wazeroir.OperationConstI32{Value: 0xff}) + require.NoError(t, err) + }, + exp: [16]byte{5: 0xff}, + }, + { + name: "i16x8 index=0", + shape: wazeroir.ShapeI16x8, + laneIndex: 0, + originValueSetupFn: func(t *testing.T, c compilerImpl) { + err := c.compileConstI32(&wazeroir.OperationConstI32{Value: 0xee_ff}) + require.NoError(t, err) + }, + exp: [16]byte{0: 0xff, 1: 0xee}, + }, + { + name: "i16x8 index=3", + shape: wazeroir.ShapeI16x8, + laneIndex: 3, + originValueSetupFn: func(t *testing.T, c compilerImpl) { + err := c.compileConstI32(&wazeroir.OperationConstI32{Value: 0xaa_00}) + require.NoError(t, err) + }, + exp: [16]byte{7: 0xaa}, + }, + { + name: "i16x8 index=7", + shape: wazeroir.ShapeI16x8, + laneIndex: 3, + originValueSetupFn: func(t *testing.T, c compilerImpl) { + err := c.compileConstI32(&wazeroir.OperationConstI32{Value: 0xaa_bb << 16}) + require.NoError(t, err) + }, + exp: [16]byte{}, + }, + { + name: "i32x4 index=0", + shape: wazeroir.ShapeI32x4, + laneIndex: 0, + originValueSetupFn: func(t *testing.T, c compilerImpl) { + err := c.compileConstI32(&wazeroir.OperationConstI32{Value: 0xaa_bb_cc_dd}) + require.NoError(t, err) + }, + exp: [16]byte{0: 0xdd, 1: 0xcc, 2: 0xbb, 3: 0xaa}, + }, + { + name: "i32x4 index=3", + shape: wazeroir.ShapeI32x4, + laneIndex: 3, + originValueSetupFn: func(t *testing.T, c compilerImpl) { + err := c.compileConstI32(&wazeroir.OperationConstI32{Value: 0xaa_bb_cc_dd}) + require.NoError(t, err) + }, + exp: [16]byte{12: 0xdd, 13: 0xcc, 14: 0xbb, 15: 0xaa}, + }, + { + name: "i64x2 index=0", + shape: wazeroir.ShapeI64x2, + laneIndex: 0, + originValueSetupFn: func(t *testing.T, c compilerImpl) { + err := c.compileConstI64(&wazeroir.OperationConstI64{Value: 0xaa_bb_cc_dd_01_02_03_04}) + require.NoError(t, err) + }, + exp: [16]byte{0: 0x04, 1: 0x03, 2: 0x02, 3: 0x01, 4: 0xdd, 5: 0xcc, 6: 0xbb, 7: 0xaa}, + }, + { + name: "i64x2 index=1", + shape: wazeroir.ShapeI64x2, + laneIndex: 1, + originValueSetupFn: func(t *testing.T, c compilerImpl) { + err := c.compileConstI64(&wazeroir.OperationConstI64{Value: 0xaa_bb_cc_dd_01_02_03_04}) + require.NoError(t, err) + }, + exp: [16]byte{8: 0x04, 9: 0x03, 10: 0x02, 11: 0x01, 12: 0xdd, 13: 0xcc, 14: 0xbb, 15: 0xaa}, + }, + { + name: "f32x4 index=0", + shape: wazeroir.ShapeF32x4, + laneIndex: 0, + originValueSetupFn: func(t *testing.T, c compilerImpl) { + err := c.compileConstF32(&wazeroir.OperationConstF32{Value: math.Float32frombits(0xaa_bb_cc_dd)}) + require.NoError(t, err) + }, + exp: [16]byte{0: 0xdd, 1: 0xcc, 2: 0xbb, 3: 0xaa}, + }, + { + name: "f32x4 index=1", + shape: wazeroir.ShapeF32x4, + laneIndex: 1, + originValueSetupFn: func(t *testing.T, c compilerImpl) { + err := c.compileConstF32(&wazeroir.OperationConstF32{Value: math.Float32frombits(0xaa_bb_cc_dd)}) + require.NoError(t, err) + }, + exp: [16]byte{4: 0xdd, 5: 0xcc, 6: 0xbb, 7: 0xaa}, + }, + { + name: "f32x4 index=2", + shape: wazeroir.ShapeF32x4, + laneIndex: 2, + originValueSetupFn: func(t *testing.T, c compilerImpl) { + err := c.compileConstF32(&wazeroir.OperationConstF32{Value: math.Float32frombits(0xaa_bb_cc_dd)}) + require.NoError(t, err) + }, + exp: [16]byte{8: 0xdd, 9: 0xcc, 10: 0xbb, 11: 0xaa}, + }, + { + name: "f32x4 index=3", + shape: wazeroir.ShapeF32x4, + laneIndex: 3, + originValueSetupFn: func(t *testing.T, c compilerImpl) { + err := c.compileConstF32(&wazeroir.OperationConstF32{Value: math.Float32frombits(0xaa_bb_cc_dd)}) + require.NoError(t, err) + }, + exp: [16]byte{12: 0xdd, 13: 0xcc, 14: 0xbb, 15: 0xaa}, + }, + { + name: "f64x2 index=0", + shape: wazeroir.ShapeF64x2, + laneIndex: 0, + originValueSetupFn: func(t *testing.T, c compilerImpl) { + err := c.compileConstF64(&wazeroir.OperationConstF64{Value: math.Float64frombits(0xaa_bb_cc_dd_01_02_03_04)}) + require.NoError(t, err) + }, + exp: [16]byte{0: 0x04, 1: 0x03, 2: 0x02, 3: 0x01, 4: 0xdd, 5: 0xcc, 6: 0xbb, 7: 0xaa}, + }, + { + name: "f64x2 index=1", + shape: wazeroir.ShapeF64x2, + laneIndex: 1, + originValueSetupFn: func(t *testing.T, c compilerImpl) { + err := c.compileConstF64(&wazeroir.OperationConstF64{Value: math.Float64frombits(0xaa_bb_cc_dd_01_02_03_04)}) + require.NoError(t, err) + }, + exp: [16]byte{8: 0x04, 9: 0x03, 10: 0x02, 11: 0x01, 12: 0xdd, 13: 0xcc, 14: 0xbb, 15: 0xaa}, + }, + { + name: "f64x2 index=0 / lo,hi = 1.0", + shape: wazeroir.ShapeF64x2, + laneIndex: 0, + originValueSetupFn: func(t *testing.T, c compilerImpl) { + err := c.compileConstF64(&wazeroir.OperationConstF64{Value: math.Float64frombits(0.0)}) + require.NoError(t, err) + }, + lo: math.Float64bits(1.0), + hi: math.Float64bits(1.0), + exp: [16]byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x3f}, + }, + { + name: "f64x2 index=1 / lo,hi = 1.0", + shape: wazeroir.ShapeF64x2, + laneIndex: 1, + originValueSetupFn: func(t *testing.T, c compilerImpl) { + err := c.compileConstF64(&wazeroir.OperationConstF64{Value: math.Float64frombits(0.0)}) + require.NoError(t, err) + }, + lo: math.Float64bits(1.0), + hi: math.Float64bits(1.0), + exp: [16]byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + env := newCompilerEnvironment() + compiler := env.requireNewCompiler(t, newCompiler, + &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) + + err := compiler.compilePreamble() + require.NoError(t, err) + + err = compiler.compileV128Const(&wazeroir.OperationV128Const{Lo: tc.lo, Hi: tc.hi}) + require.NoError(t, err) + + tc.originValueSetupFn(t, compiler) + + err = compiler.compileV128ReplaceLane(&wazeroir.OperationV128ReplaceLane{ + LaneIndex: tc.laneIndex, + Shape: tc.shape, + }) + require.NoError(t, err) + + require.Equal(t, uint64(2), compiler.runtimeValueLocationStack().sp) + require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) + + err = compiler.compileReturnFunction() + require.NoError(t, err) + + // Generate and run the code under test. + code, _, _, err := compiler.compile() + require.NoError(t, err) + env.exec(code) + + lo, hi := env.stackTopAsV128() + var actual [16]byte + binary.LittleEndian.PutUint64(actual[:8], lo) + binary.LittleEndian.PutUint64(actual[8:], hi) + require.Equal(t, tc.exp, actual) + }) + } +} + +func TestCompiler_compileV128Splat(t *testing.T) { + tests := []struct { + name string + originValueSetupFn func(*testing.T, compilerImpl) + shape wazeroir.Shape + exp [16]byte + }{ + { + name: "i8x16", + originValueSetupFn: func(t *testing.T, c compilerImpl) { + err := c.compileConstI32(&wazeroir.OperationConstI32{Value: 0x1}) + require.NoError(t, err) + }, + shape: wazeroir.ShapeI8x16, + exp: [16]byte{0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1}, + }, + { + name: "i16x8", + originValueSetupFn: func(t *testing.T, c compilerImpl) { + err := c.compileConstI32(&wazeroir.OperationConstI32{Value: 0xff_11}) + require.NoError(t, err) + }, + shape: wazeroir.ShapeI16x8, + exp: [16]byte{0x11, 0xff, 0x11, 0xff, 0x11, 0xff, 0x11, 0xff, 0x11, 0xff, 0x11, 0xff, 0x11, 0xff, 0x11, 0xff}, + }, + { + name: "i32x4", + originValueSetupFn: func(t *testing.T, c compilerImpl) { + err := c.compileConstI32(&wazeroir.OperationConstI32{Value: 0xff_11_ee_22}) + require.NoError(t, err) + }, + shape: wazeroir.ShapeI32x4, + exp: [16]byte{0x22, 0xee, 0x11, 0xff, 0x22, 0xee, 0x11, 0xff, 0x22, 0xee, 0x11, 0xff, 0x22, 0xee, 0x11, 0xff}, + }, + { + name: "i64x2", + originValueSetupFn: func(t *testing.T, c compilerImpl) { + err := c.compileConstI64(&wazeroir.OperationConstI64{Value: 0xff_00_ee_00_11_00_22_00}) + require.NoError(t, err) + }, + shape: wazeroir.ShapeI64x2, + exp: [16]byte{0x00, 0x22, 0x00, 0x11, 0x00, 0xee, 0x00, 0xff, 0x00, 0x22, 0x00, 0x11, 0x00, 0xee, 0x00, 0xff}, + }, + { + name: "f32x4", + originValueSetupFn: func(t *testing.T, c compilerImpl) { + err := c.compileConstF32(&wazeroir.OperationConstF32{Value: math.Float32frombits(0xff_11_ee_22)}) + require.NoError(t, err) + }, + shape: wazeroir.ShapeF32x4, + exp: [16]byte{0x22, 0xee, 0x11, 0xff, 0x22, 0xee, 0x11, 0xff, 0x22, 0xee, 0x11, 0xff, 0x22, 0xee, 0x11, 0xff}, + }, + { + name: "f64x2", + originValueSetupFn: func(t *testing.T, c compilerImpl) { + err := c.compileConstF64(&wazeroir.OperationConstF64{Value: math.Float64frombits(0xff_00_ee_00_11_00_22_00)}) + require.NoError(t, err) + }, + shape: wazeroir.ShapeF64x2, + exp: [16]byte{0x00, 0x22, 0x00, 0x11, 0x00, 0xee, 0x00, 0xff, 0x00, 0x22, 0x00, 0x11, 0x00, 0xee, 0x00, 0xff}, + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + env := newCompilerEnvironment() + compiler := env.requireNewCompiler(t, newCompiler, + &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) + + err := compiler.compilePreamble() + require.NoError(t, err) + + tc.originValueSetupFn(t, compiler) + + err = compiler.compileV128Splat(&wazeroir.OperationV128Splat{Shape: tc.shape}) + require.NoError(t, err) + + require.Equal(t, uint64(2), compiler.runtimeValueLocationStack().sp) + require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) + + err = compiler.compileReturnFunction() + require.NoError(t, err) + + // Generate and run the code under test. + code, _, _, err := compiler.compile() + require.NoError(t, err) + env.exec(code) + + lo, hi := env.stackTopAsV128() + var actual [16]byte + binary.LittleEndian.PutUint64(actual[:8], lo) + binary.LittleEndian.PutUint64(actual[8:], hi) + require.Equal(t, tc.exp, actual) + }) + } +} + +func TestCompiler_compileV128AnyTrue(t *testing.T) { + tests := []struct { + name string + lo, hi uint64 + exp uint32 + }{ + {name: "lo == 0 && hi == 0", lo: 0, hi: 0, exp: 0}, + {name: "lo != 0", lo: 1, exp: 1}, + {name: "hi != 0", hi: 1, exp: 1}, + {name: "lo != 0 && hi != 0", lo: 1, hi: 1, exp: 1}, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + env := newCompilerEnvironment() + compiler := env.requireNewCompiler(t, newCompiler, + &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) + + err := compiler.compilePreamble() + require.NoError(t, err) + + err = compiler.compileV128Const(&wazeroir.OperationV128Const{Lo: tc.lo, Hi: tc.hi}) + require.NoError(t, err) + + err = compiler.compileV128AnyTrue(&wazeroir.OperationV128AnyTrue{}) + require.NoError(t, err) + + require.Equal(t, uint64(1), compiler.runtimeValueLocationStack().sp) + + err = compiler.compileReturnFunction() + require.NoError(t, err) + + // Generate and run the code under test. + code, _, _, err := compiler.compile() + require.NoError(t, err) + env.exec(code) + + require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) + require.Equal(t, uint64(1), env.stackPointer()) + require.Equal(t, tc.exp, env.stackTopAsUint32()) + }) + } +} + +func TestCompiler_compileV128AllTrue(t *testing.T) { + tests := []struct { + name string + shape wazeroir.Shape + lo, hi uint64 + exp uint32 + }{ + { + name: "i8x16 - true", + shape: wazeroir.ShapeI8x16, + lo: 0xffff_ffff_ffff_ffff, + hi: 0x0101_0101_0101_0101, + exp: 1, + }, + { + name: "i8x16 - false on lo", + shape: wazeroir.ShapeI8x16, + lo: 0xffff_ffff_ffff_ffff, + hi: 0x1111_1111_0011_1111, + exp: 0, + }, + { + name: "i8x16 - false on hi", + shape: wazeroir.ShapeI8x16, + lo: 0xffff_00ff_ffff_ffff, + hi: 0x1111_1111_1111_1111, + exp: 0, + }, + { + name: "i16x8 - true", + shape: wazeroir.ShapeI16x8, + lo: 0x1000_0100_0010_0001, + hi: 0x0101_0101_0101_0101, + exp: 1, + }, + { + name: "i16x8 - false on hi", + shape: wazeroir.ShapeI16x8, + lo: 0x1000_0100_0010_0001, + hi: 0x1111_1111_0000_1111, + exp: 0, + }, + { + name: "i16x8 - false on lo", + shape: wazeroir.ShapeI16x8, + lo: 0xffff_0000_ffff_ffff, + hi: 0x1111_1111_1111_1111, + exp: 0, + }, + { + name: "i32x4 - true", + shape: wazeroir.ShapeI32x4, + lo: 0x1000_0000_0010_0000, + hi: 0x0000_0001_0000_1000, + exp: 1, + }, + { + name: "i32x4 - true", + shape: wazeroir.ShapeI32x4, + lo: 0x0000_1111_1111_0000, + hi: 0x0000_0001_1000_0000, + exp: 1, + }, + { + name: "i32x4 - false on lo", + shape: wazeroir.ShapeI32x4, + lo: 0x1111_1111_0000_0000, + hi: 0x1111_1111_1111_1111, + exp: 0, + }, + { + name: "i32x4 - false on lo", + shape: wazeroir.ShapeI32x4, + lo: 0x0000_0000_1111_1111, + hi: 0x1111_1111_1111_1111, + exp: 0, + }, + { + name: "i32x4 - false on hi", + shape: wazeroir.ShapeI32x4, + lo: 0x1111_1111_1111_1111, + hi: 0x1111_1111_0000_0000, + exp: 0, + }, + { + name: "i32x4 - false on hi", + shape: wazeroir.ShapeI32x4, + lo: 0x1111_1111_1111_1111, + hi: 0x0000_0000_1111_1111, + exp: 0, + }, + + { + name: "i64x2 - true", + shape: wazeroir.ShapeI64x2, + lo: 0x1000_0000_0000_0000, + hi: 0x0000_0001_0000_0000, + exp: 1, + }, + { + name: "i64x2 - true", + shape: wazeroir.ShapeI64x2, + lo: 0x0000_0000_0010_0000, + hi: 0x0000_0000_0000_0100, + exp: 1, + }, + { + name: "i64x2 - true", + shape: wazeroir.ShapeI64x2, + lo: 0x0000_0000_0000_1000, + hi: 0x1000_0000_0000_0000, + exp: 1, + }, + { + name: "i64x2 - false on lo", + shape: wazeroir.ShapeI64x2, + lo: 0, + hi: 0x1111_1111_1111_1111, + exp: 0, + }, + { + name: "i64x2 - false on hi", + shape: wazeroir.ShapeI64x2, + lo: 0x1111_1111_1111_1111, + hi: 0, + exp: 0, + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + env := newCompilerEnvironment() + compiler := env.requireNewCompiler(t, newCompiler, + &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) + + err := compiler.compilePreamble() + require.NoError(t, err) + + err = compiler.compileV128Const(&wazeroir.OperationV128Const{Lo: tc.lo, Hi: tc.hi}) + require.NoError(t, err) + + err = compiler.compileV128AllTrue(&wazeroir.OperationV128AllTrue{Shape: tc.shape}) + require.NoError(t, err) + + require.Equal(t, 0, len(compiler.runtimeValueLocationStack().usedRegisters)) + + err = compiler.compileReturnFunction() + require.NoError(t, err) + + // Generate and run the code under test. + code, _, _, err := compiler.compile() + require.NoError(t, err) + env.exec(code) + + require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) + require.Equal(t, uint64(1), env.stackPointer()) + require.Equal(t, tc.exp, env.stackTopAsUint32()) + }) + } +} +func i8ToU8(v int8) byte { + return byte(v) +} + +func TestCompiler_compileV128Swizzle(t *testing.T) { + + tests := []struct { + name string + indexVec, baseVec [16]byte + expVec [16]byte + }{ + { + name: "1", + baseVec: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, + indexVec: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + expVec: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, + }, + { + name: "2", + baseVec: [16]byte{i8ToU8(-16), i8ToU8(-15), i8ToU8(-14), i8ToU8(-13), i8ToU8(-12), + i8ToU8(-11), i8ToU8(-10), i8ToU8(-9), i8ToU8(-8), i8ToU8(-7), i8ToU8(-6), i8ToU8(-5), + i8ToU8(-4), i8ToU8(-3), i8ToU8(-2), i8ToU8(-1)}, + indexVec: [16]byte{i8ToU8(-8), i8ToU8(-7), i8ToU8(-6), i8ToU8(-5), i8ToU8(-4), + i8ToU8(-3), i8ToU8(-2), i8ToU8(-1), 16, 17, 18, 19, 20, 21, 22, 23}, + expVec: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "3", + baseVec: [16]byte{100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115}, + indexVec: [16]byte{15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}, + expVec: [16]byte{115, 114, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100}, + }, + { + name: "4", + baseVec: [16]byte{100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115}, + indexVec: [16]byte{ + 9, 16, 10, 17, 11, 18, 12, 19, 13, 20, 14, 21, 15, 22, 16, 23, + }, + expVec: [16]byte{109, 0, 110, 0, 111, 0, 112, 0, 113, 0, 114, 0, 115, 0, 0, 0}, + }, + { + name: "5", + baseVec: [16]byte{0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73}, + indexVec: [16]byte{9, 16, 10, 17, 11, 18, 12, 19, 13, 20, 14, 21, 15, 22, 16, 23}, + expVec: [16]byte{0x6d, 0, 0x6e, 0, 0x6f, 0, 0x70, 0, 0x71, 0, 0x72, 0, 0x73, 0, 0, 0}, + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + env := newCompilerEnvironment() + compiler := env.requireNewCompiler(t, newCompiler, + &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) + + err := compiler.compilePreamble() + require.NoError(t, err) + + err = compiler.compileV128Const(&wazeroir.OperationV128Const{ + Lo: binary.LittleEndian.Uint64(tc.baseVec[:8]), + Hi: binary.LittleEndian.Uint64(tc.baseVec[8:]), + }) + require.NoError(t, err) + + err = compiler.compileV128Const(&wazeroir.OperationV128Const{ + Lo: binary.LittleEndian.Uint64(tc.indexVec[:8]), + Hi: binary.LittleEndian.Uint64(tc.indexVec[8:]), + }) + require.NoError(t, err) + + err = compiler.compileV128Swizzle(&wazeroir.OperationV128Swizzle{}) + require.NoError(t, err) + + require.Equal(t, uint64(2), compiler.runtimeValueLocationStack().sp) + require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) + + err = compiler.compileReturnFunction() + require.NoError(t, err) + + // Generate and run the code under test. + code, _, _, err := compiler.compile() + require.NoError(t, err) + env.exec(code) + + require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) + + lo, hi := env.stackTopAsV128() + var actual [16]byte + binary.LittleEndian.PutUint64(actual[:8], lo) + binary.LittleEndian.PutUint64(actual[8:], hi) + require.Equal(t, tc.expVec, actual) + }) + } +} + +func TestCompiler_compileV128Shuffle(t *testing.T) { + tests := []struct { + name string + lanes, w, v, exp [16]byte + }{ + { + name: "v only", + lanes: [16]byte{1, 1, 1, 1, 0, 0, 0, 0, 10, 10, 10, 10, 0, 0, 0, 0}, + v: [16]byte{0: 0xa, 1: 0xb, 10: 0xc}, + w: [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + exp: [16]byte{ + 0xb, 0xb, 0xb, 0xb, + 0xa, 0xa, 0xa, 0xa, + 0xc, 0xc, 0xc, 0xc, + 0xa, 0xa, 0xa, 0xa, + }, + }, + { + name: "w only", + lanes: [16]byte{17, 17, 17, 17, 16, 16, 16, 16, 26, 26, 26, 26, 16, 16, 16, 16}, + v: [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + w: [16]byte{0: 0xa, 1: 0xb, 10: 0xc}, + exp: [16]byte{ + 0xb, 0xb, 0xb, 0xb, + 0xa, 0xa, 0xa, 0xa, + 0xc, 0xc, 0xc, 0xc, + 0xa, 0xa, 0xa, 0xa, + }, + }, + { + name: "mix", + lanes: [16]byte{0, 17, 2, 19, 4, 21, 6, 23, 8, 25, 10, 27, 12, 29, 14, 31}, + v: [16]byte{ + 0x1, 0xff, 0x2, 0xff, 0x3, 0xff, 0x4, 0xff, + 0x5, 0xff, 0x6, 0xff, 0x7, 0xff, 0x8, 0xff, + }, + w: [16]byte{ + 0xff, 0x11, 0xff, 0x12, 0xff, 0x13, 0xff, 0x14, + 0xff, 0x15, 0xff, 0x16, 0xff, 0x17, 0xff, 0x18, + }, + exp: [16]byte{ + 0x1, 0x11, 0x2, 0x12, 0x3, 0x13, 0x4, 0x14, + 0x5, 0x15, 0x6, 0x16, 0x7, 0x17, 0x8, 0x18, + }, + }, + { + name: "mix", + lanes: [16]byte{0, 17, 2, 19, 4, 21, 6, 23, 8, 25, 10, 27, 12, 29, 14, 31}, + v: [16]byte{ + 0x1, 0xff, 0x2, 0xff, 0x3, 0xff, 0x4, 0xff, + 0x5, 0xff, 0x6, 0xff, 0x7, 0xff, 0x8, 0xff, + }, + w: [16]byte{ + 0xff, 0x11, 0xff, 0x12, 0xff, 0x13, 0xff, 0x14, + 0xff, 0x15, 0xff, 0x16, 0xff, 0x17, 0xff, 0x18, + }, + exp: [16]byte{ + 0x1, 0x11, 0x2, 0x12, 0x3, 0x13, 0x4, 0x14, + 0x5, 0x15, 0x6, 0x16, 0x7, 0x17, 0x8, 0x18, + }, + }, + } + /* + */ + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + env := newCompilerEnvironment() + compiler := env.requireNewCompiler(t, newCompiler, + &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) + + err := compiler.compilePreamble() + require.NoError(t, err) + + err = compiler.compileV128Const(&wazeroir.OperationV128Const{ + Lo: binary.LittleEndian.Uint64(tc.v[:8]), + Hi: binary.LittleEndian.Uint64(tc.v[8:]), + }) + require.NoError(t, err) + + err = compiler.compileV128Const(&wazeroir.OperationV128Const{ + Lo: binary.LittleEndian.Uint64(tc.w[:8]), + Hi: binary.LittleEndian.Uint64(tc.w[8:]), + }) + require.NoError(t, err) + + err = compiler.compileV128Shuffle(&wazeroir.OperationV128Shuffle{Lanes: tc.lanes}) + require.NoError(t, err) + + require.Equal(t, uint64(2), compiler.runtimeValueLocationStack().sp) + require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) + + err = compiler.compileReturnFunction() + require.NoError(t, err) + + // Generate and run the code under test. + code, _, _, err := compiler.compile() + require.NoError(t, err) + env.exec(code) + + lo, hi := env.stackTopAsV128() + var actual [16]byte + binary.LittleEndian.PutUint64(actual[:8], lo) + binary.LittleEndian.PutUint64(actual[8:], hi) + require.Equal(t, tc.exp, actual) + }) + } +} diff --git a/internal/engine/compiler/engine.go b/internal/engine/compiler/engine.go index c4a95749355..bc53f277aa6 100644 --- a/internal/engine/compiler/engine.go +++ b/internal/engine/compiler/engine.go @@ -1040,10 +1040,34 @@ func compileWasmFunction(_ wasm.Features, ir *wazeroir.CompilationResult) (*code err = compiler.compileTableSize(o) case *wazeroir.OperationTableFill: err = compiler.compileTableFill(o) - case *wazeroir.OperationConstV128: - err = compiler.compileConstV128(o) - case *wazeroir.OperationAddV128: - err = compiler.compileAddV128(o) + case *wazeroir.OperationV128Const: + err = compiler.compileV128Const(o) + case *wazeroir.OperationV128Add: + err = compiler.compileV128Add(o) + case *wazeroir.OperationV128Sub: + err = compiler.compileV128Sub(o) + case *wazeroir.OperationV128Load: + err = compiler.compileV128Load(o) + case *wazeroir.OperationV128LoadLane: + err = compiler.compileV128LoadLane(o) + case *wazeroir.OperationV128Store: + err = compiler.compileV128Store(o) + case *wazeroir.OperationV128StoreLane: + err = compiler.compileV128StoreLane(o) + case *wazeroir.OperationV128ExtractLane: + err = compiler.compileV128ExtractLane(o) + case *wazeroir.OperationV128ReplaceLane: + err = compiler.compileV128ReplaceLane(o) + case *wazeroir.OperationV128Splat: + err = compiler.compileV128Splat(o) + case *wazeroir.OperationV128Shuffle: + err = compiler.compileV128Shuffle(o) + case *wazeroir.OperationV128Swizzle: + err = compiler.compileV128Swizzle(o) + case *wazeroir.OperationV128AnyTrue: + err = compiler.compileV128AnyTrue(o) + case *wazeroir.OperationV128AllTrue: + err = compiler.compileV128AllTrue(o) default: err = errors.New("unsupported") } diff --git a/internal/engine/compiler/impl_arm64.go b/internal/engine/compiler/impl_arm64.go index 3621740c6a0..2c93ea2e9ff 100644 --- a/internal/engine/compiler/impl_arm64.go +++ b/internal/engine/compiler/impl_arm64.go @@ -159,6 +159,12 @@ func (c *arm64Compiler) pushRuntimeValueLocationOnRegister(reg asm.Register, vt return } +func (c *arm64Compiler) pushVectorRuntimeValueLocationOnRegister(reg asm.Register) { + c.locationStack.pushRuntimeValueLocationOnRegister(reg, runtimeValueTypeV128Lo) + c.locationStack.pushRuntimeValueLocationOnRegister(reg, runtimeValueTypeV128Hi) + c.markRegisterUsed(reg) +} + func (c *arm64Compiler) markRegisterUsed(regs ...asm.Register) { for _, reg := range regs { if !isZeroRegister(reg) && reg != asm.NilRegister { @@ -477,6 +483,7 @@ func (c *arm64Compiler) compileSwap(o *wazeroir.OperationSwap) error { if err := c.compileEnsureOnGeneralPurposeRegister(x); err != nil { return err } + if err := c.compileEnsureOnGeneralPurposeRegister(y); err != nil { return err } @@ -507,10 +514,10 @@ func (c *arm64Compiler) compileGlobalGet(o *wazeroir.OperationGlobalGet) error { return err } c.assembler.CompileConstToRegister(arm64.ADD, globalInstanceValueOffset, intReg) - c.assembler.CompileMemoryToVectorRegister(arm64.VLD1, intReg, resultReg, arm64.VectorArrangement2D) + c.assembler.CompileMemoryToVectorRegister(arm64.VMOV, intReg, 0, + resultReg, arm64.VectorArrangementQ) - c.pushRuntimeValueLocationOnRegister(resultReg, runtimeValueTypeV128Lo) - c.pushRuntimeValueLocationOnRegister(resultReg, runtimeValueTypeV128Hi) + c.pushVectorRuntimeValueLocationOnRegister(resultReg) } else { var intMov, floatMov = arm64.NOP, arm64.NOP @@ -560,9 +567,10 @@ func (c *arm64Compiler) compileGlobalSet(o *wazeroir.OperationGlobalSet) error { wasmValueType := c.ir.Globals[o.Index].ValType isV128 := wasmValueType == wasm.ValueTypeV128 - val := c.locationStack.pop() + var val *runtimeValueLocation if isV128 { - // The previous val is higher 64-bits, and have to use lower 64-bit's runtimeValueLocation for allocation, etc. + val = c.locationStack.popV128() + } else { val = c.locationStack.pop() } if err := c.compileEnsureOnGeneralPurposeRegister(val); err != nil { @@ -575,9 +583,9 @@ func (c *arm64Compiler) compileGlobalSet(o *wazeroir.OperationGlobalSet) error { } if isV128 { - c.assembler.CompileConstToRegister(arm64.ADD, globalInstanceValueOffset, globalInstanceAddressRegister) - c.assembler.CompileVectorRegisterToMemory(arm64.VST1, val.register, globalInstanceAddressRegister, - arm64.VectorArrangement2D) + c.assembler.CompileVectorRegisterToMemory(arm64.VMOV, + val.register, globalInstanceAddressRegister, globalInstanceValueOffset, + arm64.VectorArrangementQ) } else { var mov asm.Instruction switch c.ir.Globals[o.Index].ValType { @@ -1395,7 +1403,7 @@ func (c *arm64Compiler) compilePick(o *wazeroir.OperationPick) error { c.assembler.CompileRegisterToRegister(arm64.FMOVD, pickTarget.register, pickedRegister) case runtimeValueTypeV128Lo: c.assembler.CompileVectorRegisterToVectorRegister(arm64.VMOV, - pickTarget.register, pickedRegister, arm64.VectorArrangement16B) + pickTarget.register, pickedRegister, arm64.VectorArrangement16B, arm64.VectorIndexNone, arm64.VectorIndexNone) case runtimeValueTypeV128Hi: panic("BUG") // since pick target must point to the lower 64-bits of vectors. } @@ -4012,10 +4020,9 @@ func (c *arm64Compiler) compileLoadValueOnStackToRegister(loc *runtimeValueLocat c.assembler.CompileMemoryToRegister(arm64.FMOVD, arm64ReservedRegisterForStackBasePointerAddress, int64(loc.stackPointer)*8, loc.register) case runtimeValueTypeV128Lo: - // Stores arm64ReservedRegisterForStackBasePointerAddress+ int64(loc.stackPointer)*8 into tem register. - c.assembler.CompileConstToRegister(arm64.MOVD, int64(loc.stackPointer)*8, arm64ReservedRegisterForTemporary) - c.assembler.CompileRegisterToRegister(arm64.ADD, arm64ReservedRegisterForStackBasePointerAddress, arm64ReservedRegisterForTemporary) - c.assembler.CompileMemoryToVectorRegister(arm64.VLD1, arm64ReservedRegisterForTemporary, loc.register, arm64.VectorArrangement2D) + c.assembler.CompileMemoryToVectorRegister(arm64.VMOV, + arm64ReservedRegisterForStackBasePointerAddress, int64(loc.stackPointer)*8, loc.register, + arm64.VectorArrangementQ) // Higher 64-bits are loaded as well ^^. hi := c.locationStack.stack[loc.stackPointer+1] hi.setRegister(loc.register) @@ -4071,20 +4078,19 @@ func (c *arm64Compiler) compileReleaseAllRegistersToStack() error { func (c *arm64Compiler) compileReleaseRegisterToStack(loc *runtimeValueLocation) { switch loc.valueType { case runtimeValueTypeI32: - // Use 64-bit mov as all the values are represented as uint64 in Go so we have to clear out the higher bits. + // Use 64-bit mov as all the values are represented as uint64 in Go, so we have to clear out the higher bits. c.assembler.CompileRegisterToMemory(arm64.MOVD, loc.register, arm64ReservedRegisterForStackBasePointerAddress, int64(loc.stackPointer)*8) case runtimeValueTypeI64: c.assembler.CompileRegisterToMemory(arm64.MOVD, loc.register, arm64ReservedRegisterForStackBasePointerAddress, int64(loc.stackPointer)*8) case runtimeValueTypeF32: - // Use 64-bit mov as all the values are represented as uint64 in Go so we have to clear out the higher bits. + // Use 64-bit mov as all the values are represented as uint64 in Go, so we have to clear out the higher bits. c.assembler.CompileRegisterToMemory(arm64.FMOVD, loc.register, arm64ReservedRegisterForStackBasePointerAddress, int64(loc.stackPointer)*8) case runtimeValueTypeF64: c.assembler.CompileRegisterToMemory(arm64.FMOVD, loc.register, arm64ReservedRegisterForStackBasePointerAddress, int64(loc.stackPointer)*8) case runtimeValueTypeV128Lo: - // Stores arm64ReservedRegisterForStackBasePointerAddress + int64(loc.stackPointer)*8 into tem register. - c.assembler.CompileConstToRegister(arm64.MOVD, int64(loc.stackPointer)*8, arm64ReservedRegisterForTemporary) - c.assembler.CompileRegisterToRegister(arm64.ADD, arm64ReservedRegisterForStackBasePointerAddress, arm64ReservedRegisterForTemporary) - c.assembler.CompileVectorRegisterToMemory(arm64.VST1, loc.register, arm64ReservedRegisterForTemporary, arm64.VectorArrangement2D) + c.assembler.CompileVectorRegisterToMemory(arm64.VMOV, + loc.register, arm64ReservedRegisterForStackBasePointerAddress, int64(loc.stackPointer)*8, + arm64.VectorArrangementQ) // Higher 64-bits are released as well ^^. hi := c.locationStack.stack[loc.stackPointer+1] c.locationStack.releaseRegister(hi) diff --git a/internal/engine/compiler/impl_vec_amd64.go b/internal/engine/compiler/impl_vec_amd64.go index 4cdc7a971b9..32b25ee3105 100644 --- a/internal/engine/compiler/impl_vec_amd64.go +++ b/internal/engine/compiler/impl_vec_amd64.go @@ -6,8 +6,8 @@ import ( "github.com/tetratelabs/wazero/internal/wazeroir" ) -// compileConstV128 implements compiler.compileConstV128 for amd64 architecture. -func (c *amd64Compiler) compileConstV128(o *wazeroir.OperationConstV128) error { +// compileV128Const implements compiler.compileV128Const for amd64 architecture. +func (c *amd64Compiler) compileV128Const(o *wazeroir.OperationV128Const) error { c.maybeCompileMoveTopConditionalToFreeGeneralPurposeRegister() result, err := c.allocateRegister(registerTypeVector) @@ -42,16 +42,14 @@ func (c *amd64Compiler) compileConstV128(o *wazeroir.OperationConstV128) error { return nil } -// compileAddV128 implements compiler.compileAddV128 for amd64 architecture. -func (c *amd64Compiler) compileAddV128(o *wazeroir.OperationAddV128) error { - c.locationStack.pop() // skip higher 64-bits. - x2 := c.locationStack.pop() +// compileV128Add implements compiler.compileV128Add for amd64 architecture. +func (c *amd64Compiler) compileV128Add(o *wazeroir.OperationV128Add) error { + x2 := c.locationStack.popV128() if err := c.compileEnsureOnGeneralPurposeRegister(x2); err != nil { return err } - c.locationStack.pop() // skip higher 64-bits. - x1 := c.locationStack.pop() + x1 := c.locationStack.popV128() if err := c.compileEnsureOnGeneralPurposeRegister(x1); err != nil { return err } @@ -76,3 +74,519 @@ func (c *amd64Compiler) compileAddV128(o *wazeroir.OperationAddV128) error { c.locationStack.markRegisterUnused(x2.register) return nil } + +// compileV128Sub implements compiler.compileV128Sub for amd64 architecture. +func (c *amd64Compiler) compileV128Sub(o *wazeroir.OperationV128Sub) error { + x2 := c.locationStack.popV128() + if err := c.compileEnsureOnGeneralPurposeRegister(x2); err != nil { + return err + } + + x1 := c.locationStack.popV128() + if err := c.compileEnsureOnGeneralPurposeRegister(x1); err != nil { + return err + } + var inst asm.Instruction + switch o.Shape { + case wazeroir.ShapeI8x16: + inst = amd64.PSUBB + case wazeroir.ShapeI16x8: + inst = amd64.PSUBW + case wazeroir.ShapeI32x4: + inst = amd64.PSUBL + case wazeroir.ShapeI64x2: + inst = amd64.PSUBQ + case wazeroir.ShapeF32x4: + inst = amd64.SUBPS + case wazeroir.ShapeF64x2: + inst = amd64.SUBPD + } + c.assembler.CompileRegisterToRegister(inst, x2.register, x1.register) + + c.pushVectorRuntimeValueLocationOnRegister(x1.register) + c.locationStack.markRegisterUnused(x2.register) + return nil +} + +// compileV128Load implements compiler.compileV128Load for amd64 architecture. +func (c *amd64Compiler) compileV128Load(o *wazeroir.OperationV128Load) error { + result, err := c.allocateRegister(registerTypeVector) + if err != nil { + return err + } + + switch o.Type { + case wazeroir.LoadV128Type128: + err = c.compileV128LoadImpl(amd64.MOVDQU, o.Arg.Offset, 16, result) + case wazeroir.LoadV128Type8x8s: + err = c.compileV128LoadImpl(amd64.PMOVSXBW, o.Arg.Offset, 8, result) + case wazeroir.LoadV128Type8x8u: + err = c.compileV128LoadImpl(amd64.PMOVZXBW, o.Arg.Offset, 8, result) + case wazeroir.LoadV128Type16x4s: + err = c.compileV128LoadImpl(amd64.PMOVSXWD, o.Arg.Offset, 8, result) + case wazeroir.LoadV128Type16x4u: + err = c.compileV128LoadImpl(amd64.PMOVZXWD, o.Arg.Offset, 8, result) + case wazeroir.LoadV128Type32x2s: + err = c.compileV128LoadImpl(amd64.PMOVSXDQ, o.Arg.Offset, 8, result) + case wazeroir.LoadV128Type32x2u: + err = c.compileV128LoadImpl(amd64.PMOVZXDQ, o.Arg.Offset, 8, result) + case wazeroir.LoadV128Type8Splat: + reg, err := c.compileMemoryAccessCeilSetup(o.Arg.Offset, 1) + if err != nil { + return err + } + c.assembler.CompileMemoryWithIndexToRegister(amd64.MOVBQZX, amd64ReservedRegisterForMemory, -1, + reg, 1, reg) + // pinsrb $0, reg, result + // pxor tmpVReg, tmpVReg + // pshufb tmpVReg, result + c.locationStack.markRegisterUsed(result) + tmpVReg, err := c.allocateRegister(registerTypeVector) + if err != nil { + return err + } + c.assembler.CompileRegisterToRegisterWithArg(amd64.PINSRB, reg, result, 0) + c.assembler.CompileRegisterToRegister(amd64.PXOR, tmpVReg, tmpVReg) + c.assembler.CompileRegisterToRegister(amd64.PSHUFB, tmpVReg, result) + case wazeroir.LoadV128Type16Splat: + reg, err := c.compileMemoryAccessCeilSetup(o.Arg.Offset, 2) + if err != nil { + return err + } + c.assembler.CompileMemoryWithIndexToRegister(amd64.MOVWQZX, amd64ReservedRegisterForMemory, -2, + reg, 1, reg) + // pinsrw $0, reg, result + // pinsrw $1, reg, result + // pshufd $0, result, result (result = result[0,0,0,0]) + c.assembler.CompileRegisterToRegisterWithArg(amd64.PINSRW, reg, result, 0) + c.assembler.CompileRegisterToRegisterWithArg(amd64.PINSRW, reg, result, 1) + c.assembler.CompileRegisterToRegisterWithArg(amd64.PSHUFD, result, result, 0) + case wazeroir.LoadV128Type32Splat: + reg, err := c.compileMemoryAccessCeilSetup(o.Arg.Offset, 4) + if err != nil { + return err + } + c.assembler.CompileMemoryWithIndexToRegister(amd64.MOVLQZX, amd64ReservedRegisterForMemory, -4, + reg, 1, reg) + // pinsrd $0, reg, result + // pshufd $0, result, result (result = result[0,0,0,0]) + c.assembler.CompileRegisterToRegisterWithArg(amd64.PINSRD, reg, result, 0) + c.assembler.CompileRegisterToRegisterWithArg(amd64.PSHUFD, result, result, 0) + case wazeroir.LoadV128Type64Splat: + reg, err := c.compileMemoryAccessCeilSetup(o.Arg.Offset, 8) + if err != nil { + return err + } + c.assembler.CompileMemoryWithIndexToRegister(amd64.MOVQ, amd64ReservedRegisterForMemory, -8, + reg, 1, reg) + // pinsrq $0, reg, result + // pinsrq $1, reg, result + c.assembler.CompileRegisterToRegisterWithArg(amd64.PINSRQ, reg, result, 0) + c.assembler.CompileRegisterToRegisterWithArg(amd64.PINSRQ, reg, result, 1) + case wazeroir.LoadV128Type32zero: + err = c.compileV128LoadImpl(amd64.MOVL, o.Arg.Offset, 4, result) + case wazeroir.LoadV128Type64zero: + err = c.compileV128LoadImpl(amd64.MOVQ, o.Arg.Offset, 8, result) + } + + if err != nil { + return err + } + + c.pushVectorRuntimeValueLocationOnRegister(result) + return nil +} + +func (c *amd64Compiler) compileV128LoadImpl(inst asm.Instruction, offset uint32, targetSizeInBytes int64, dst asm.Register) error { + offsetReg, err := c.compileMemoryAccessCeilSetup(offset, targetSizeInBytes) + if err != nil { + return err + } + c.assembler.CompileMemoryWithIndexToRegister(inst, amd64ReservedRegisterForMemory, -targetSizeInBytes, + offsetReg, 1, dst) + return nil +} + +// compileV128LoadLane implements compiler.compileV128LoadLane for amd64. +func (c *amd64Compiler) compileV128LoadLane(o *wazeroir.OperationV128LoadLane) error { + targetVector := c.locationStack.popV128() + if err := c.compileEnsureOnGeneralPurposeRegister(targetVector); err != nil { + return err + } + + var insertInst asm.Instruction + switch o.LaneSize { + case 8: + insertInst = amd64.PINSRB + case 16: + insertInst = amd64.PINSRW + case 32: + insertInst = amd64.PINSRD + case 64: + insertInst = amd64.PINSRQ + } + + targetSizeInBytes := int64(o.LaneSize / 8) + offsetReg, err := c.compileMemoryAccessCeilSetup(o.Arg.Offset, targetSizeInBytes) + if err != nil { + return err + } + c.assembler.CompileMemoryWithIndexAndArgToRegister(insertInst, amd64ReservedRegisterForMemory, -targetSizeInBytes, + offsetReg, 1, targetVector.register, o.LaneIndex) + + c.pushVectorRuntimeValueLocationOnRegister(targetVector.register) + return nil +} + +// compileV128Store implements compiler.compileV128Store for amd64. +func (c *amd64Compiler) compileV128Store(o *wazeroir.OperationV128Store) error { + val := c.locationStack.popV128() + if err := c.compileEnsureOnGeneralPurposeRegister(val); err != nil { + return err + } + + const targetSizeInBytes = 16 + offsetReg, err := c.compileMemoryAccessCeilSetup(o.Arg.Offset, targetSizeInBytes) + if err != nil { + return err + } + + c.assembler.CompileRegisterToMemoryWithIndex(amd64.MOVDQU, val.register, + amd64ReservedRegisterForMemory, -targetSizeInBytes, offsetReg, 1) + + c.locationStack.markRegisterUnused(val.register, offsetReg) + return nil +} + +// compileV128StoreLane implements compiler.compileV128StoreLane for amd64. +func (c *amd64Compiler) compileV128StoreLane(o *wazeroir.OperationV128StoreLane) error { + var storeInst asm.Instruction + switch o.LaneSize { + case 8: + storeInst = amd64.PEXTRB + case 16: + storeInst = amd64.PEXTRW + case 32: + storeInst = amd64.PEXTRD + case 64: + storeInst = amd64.PEXTRQ + } + + val := c.locationStack.popV128() + if err := c.compileEnsureOnGeneralPurposeRegister(val); err != nil { + return err + } + + targetSizeInBytes := int64(o.LaneSize / 8) + offsetReg, err := c.compileMemoryAccessCeilSetup(o.Arg.Offset, targetSizeInBytes) + if err != nil { + return err + } + + c.assembler.CompileRegisterToMemoryWithIndexAndArg(storeInst, val.register, + amd64ReservedRegisterForMemory, -targetSizeInBytes, offsetReg, 1, o.LaneIndex) + + c.locationStack.markRegisterUnused(val.register, offsetReg) + return nil +} + +// compileV128ExtractLane implements compiler.compileV128ExtractLane for amd64. +func (c *amd64Compiler) compileV128ExtractLane(o *wazeroir.OperationV128ExtractLane) error { + val := c.locationStack.popV128() + if err := c.compileEnsureOnGeneralPurposeRegister(val); err != nil { + return err + } + switch o.Shape { + case wazeroir.ShapeI8x16: + result, err := c.allocateRegister(registerTypeGeneralPurpose) + if err != nil { + return err + } + c.assembler.CompileRegisterToRegisterWithArg(amd64.PEXTRB, val.register, result, o.LaneIndex) + if o.Signed { + c.assembler.CompileRegisterToRegister(amd64.MOVBQSX, result, result) + } else { + c.assembler.CompileRegisterToRegister(amd64.MOVBLZX, result, result) + } + c.pushRuntimeValueLocationOnRegister(result, runtimeValueTypeI32) + c.locationStack.markRegisterUnused(val.register) + case wazeroir.ShapeI16x8: + result, err := c.allocateRegister(registerTypeGeneralPurpose) + if err != nil { + return err + } + c.assembler.CompileRegisterToRegisterWithArg(amd64.PEXTRW, val.register, result, o.LaneIndex) + if o.Signed { + c.assembler.CompileRegisterToRegister(amd64.MOVWLSX, result, result) + } else { + c.assembler.CompileRegisterToRegister(amd64.MOVWLZX, result, result) + } + c.pushRuntimeValueLocationOnRegister(result, runtimeValueTypeI32) + c.locationStack.markRegisterUnused(val.register) + case wazeroir.ShapeI32x4: + result, err := c.allocateRegister(registerTypeGeneralPurpose) + if err != nil { + return err + } + c.assembler.CompileRegisterToRegisterWithArg(amd64.PEXTRD, val.register, result, o.LaneIndex) + c.pushRuntimeValueLocationOnRegister(result, runtimeValueTypeI32) + c.locationStack.markRegisterUnused(val.register) + case wazeroir.ShapeI64x2: + result, err := c.allocateRegister(registerTypeGeneralPurpose) + if err != nil { + return err + } + c.assembler.CompileRegisterToRegisterWithArg(amd64.PEXTRQ, val.register, result, o.LaneIndex) + c.pushRuntimeValueLocationOnRegister(result, runtimeValueTypeI64) + c.locationStack.markRegisterUnused(val.register) + case wazeroir.ShapeF32x4: + if o.LaneIndex != 0 { + c.assembler.CompileRegisterToRegisterWithArg(amd64.PSHUFD, val.register, val.register, o.LaneIndex) + } + c.pushRuntimeValueLocationOnRegister(val.register, runtimeValueTypeF32) + case wazeroir.ShapeF64x2: + if o.LaneIndex != 0 { + // This case we can assume LaneIndex == 1. + // We have to modify the val.register as, for example: + // 0b11 0b10 0b01 0b00 + // | | | | + // [x3, x2, x1, x0] -> [x0, x0, x3, x2] + // where val.register = [x3, x2, x1, x0] and each xN = 32bits. + // Then, we interpret the register as float64, therefore, the float64 value is obtained as [x3, x2]. + arg := byte(0b00_00_11_10) + c.assembler.CompileRegisterToRegisterWithArg(amd64.PSHUFD, val.register, val.register, arg) + } + c.pushRuntimeValueLocationOnRegister(val.register, runtimeValueTypeF64) + } + + return nil +} + +// compileV128ReplaceLane implements compiler.compileV128ReplaceLane for amd64. +func (c *amd64Compiler) compileV128ReplaceLane(o *wazeroir.OperationV128ReplaceLane) error { + origin := c.locationStack.pop() + if err := c.compileEnsureOnGeneralPurposeRegister(origin); err != nil { + return err + } + + vector := c.locationStack.popV128() + if err := c.compileEnsureOnGeneralPurposeRegister(vector); err != nil { + return err + } + + switch o.Shape { + case wazeroir.ShapeI8x16: + c.assembler.CompileRegisterToRegisterWithArg(amd64.PINSRB, origin.register, vector.register, o.LaneIndex) + case wazeroir.ShapeI16x8: + c.assembler.CompileRegisterToRegisterWithArg(amd64.PINSRW, origin.register, vector.register, o.LaneIndex) + case wazeroir.ShapeI32x4: + c.assembler.CompileRegisterToRegisterWithArg(amd64.PINSRD, origin.register, vector.register, o.LaneIndex) + case wazeroir.ShapeI64x2: + c.assembler.CompileRegisterToRegisterWithArg(amd64.PINSRQ, origin.register, vector.register, o.LaneIndex) + case wazeroir.ShapeF32x4: + c.assembler.CompileRegisterToRegisterWithArg(amd64.INSERTPS, origin.register, vector.register, + // In INSERTPS instruction, the destination index is encoded at 4 and 5 bits of the argument. + // See https://www.felixcloutier.com/x86/insertps + o.LaneIndex<<4, + ) + case wazeroir.ShapeF64x2: + if o.LaneIndex == 0 { + c.assembler.CompileRegisterToRegister(amd64.MOVSD, origin.register, vector.register) + } else { + c.assembler.CompileRegisterToRegister(amd64.MOVLHPS, origin.register, vector.register) + } + } + + c.pushVectorRuntimeValueLocationOnRegister(vector.register) + c.locationStack.markRegisterUnused(origin.register) + return nil +} + +// compileV128Splat implements compiler.compileV128Splat for amd64. +func (c *amd64Compiler) compileV128Splat(o *wazeroir.OperationV128Splat) (err error) { + origin := c.locationStack.pop() + if err = c.compileEnsureOnGeneralPurposeRegister(origin); err != nil { + return + } + + var result asm.Register + switch o.Shape { + case wazeroir.ShapeI8x16: + result, err = c.allocateRegister(registerTypeVector) + if err != nil { + return err + } + c.locationStack.markRegisterUsed(result) + + tmp, err := c.allocateRegister(registerTypeVector) + if err != nil { + return err + } + c.assembler.CompileRegisterToRegisterWithArg(amd64.PINSRB, origin.register, result, 0) + c.assembler.CompileRegisterToRegister(amd64.PXOR, tmp, tmp) + c.assembler.CompileRegisterToRegister(amd64.PSHUFB, tmp, result) + case wazeroir.ShapeI16x8: + result, err = c.allocateRegister(registerTypeVector) + if err != nil { + return err + } + c.locationStack.markRegisterUsed(result) + c.assembler.CompileRegisterToRegisterWithArg(amd64.PINSRW, origin.register, result, 0) + c.assembler.CompileRegisterToRegisterWithArg(amd64.PINSRW, origin.register, result, 1) + c.assembler.CompileRegisterToRegisterWithArg(amd64.PSHUFD, result, result, 0) + case wazeroir.ShapeI32x4: + result, err = c.allocateRegister(registerTypeVector) + if err != nil { + return err + } + c.locationStack.markRegisterUsed(result) + c.assembler.CompileRegisterToRegisterWithArg(amd64.PINSRD, origin.register, result, 0) + c.assembler.CompileRegisterToRegisterWithArg(amd64.PSHUFD, result, result, 0) + case wazeroir.ShapeI64x2: + result, err = c.allocateRegister(registerTypeVector) + if err != nil { + return err + } + c.locationStack.markRegisterUsed(result) + c.assembler.CompileRegisterToRegisterWithArg(amd64.PINSRQ, origin.register, result, 0) + c.assembler.CompileRegisterToRegisterWithArg(amd64.PINSRQ, origin.register, result, 1) + case wazeroir.ShapeF32x4: + result = origin.register + c.assembler.CompileRegisterToRegisterWithArg(amd64.INSERTPS, origin.register, result, 0) + c.assembler.CompileRegisterToRegisterWithArg(amd64.PSHUFD, result, result, 0) + case wazeroir.ShapeF64x2: + result = origin.register + c.assembler.CompileRegisterToRegister(amd64.MOVQ, origin.register, result) + c.assembler.CompileRegisterToRegister(amd64.MOVLHPS, origin.register, result) + } + + c.locationStack.markRegisterUnused(origin.register) + c.pushVectorRuntimeValueLocationOnRegister(result) + return nil +} + +// compileV128Shuffle implements compiler.compileV128Shuffle for amd64. +func (c *amd64Compiler) compileV128Shuffle(o *wazeroir.OperationV128Shuffle) error { + w := c.locationStack.popV128() + if err := c.compileEnsureOnGeneralPurposeRegister(w); err != nil { + return err + } + + v := c.locationStack.popV128() + if err := c.compileEnsureOnGeneralPurposeRegister(v); err != nil { + return err + } + + tmp, err := c.allocateRegister(registerTypeVector) + if err != nil { + return err + } + + consts := [32]byte{} + for i, lane := range o.Lanes { + if lane < 16 { + consts[i+16] = 0x80 + consts[i] = lane + } else { + consts[i+16] = lane - 16 + consts[i] = 0x80 + } + } + + err = c.assembler.CompileLoadStaticConstToRegister(amd64.MOVDQU, consts[:16], tmp) + if err != nil { + return err + } + c.assembler.CompileRegisterToRegister(amd64.PSHUFB, tmp, v.register) + err = c.assembler.CompileLoadStaticConstToRegister(amd64.MOVDQU, consts[16:], tmp) + if err != nil { + return err + } + c.assembler.CompileRegisterToRegister(amd64.PSHUFB, tmp, w.register) + c.assembler.CompileRegisterToRegister(amd64.ORPS, v.register, w.register) + + c.pushVectorRuntimeValueLocationOnRegister(w.register) + c.locationStack.markRegisterUnused(v.register) + return nil +} + +var swizzleConst = [16]byte{ + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, +} + +// compileV128Swizzle implements compiler.compileV128Swizzle for amd64. +func (c *amd64Compiler) compileV128Swizzle(*wazeroir.OperationV128Swizzle) error { + indexVec := c.locationStack.popV128() + if err := c.compileEnsureOnGeneralPurposeRegister(indexVec); err != nil { + return err + } + + baseVec := c.locationStack.popV128() + if err := c.compileEnsureOnGeneralPurposeRegister(baseVec); err != nil { + return err + } + + tmp, err := c.allocateRegister(registerTypeVector) + if err != nil { + return err + } + + err = c.assembler.CompileLoadStaticConstToRegister(amd64.MOVDQU, swizzleConst[:], tmp) + if err != nil { + return err + } + + c.assembler.CompileRegisterToRegister(amd64.PADDUSB, tmp, indexVec.register) + c.assembler.CompileRegisterToRegister(amd64.PSHUFB, indexVec.register, baseVec.register) + + c.pushVectorRuntimeValueLocationOnRegister(baseVec.register) + c.locationStack.markRegisterUnused(indexVec.register) + return nil +} + +// compileV128AnyTrue implements compiler.compileV128AnyTrue for amd64. +func (c *amd64Compiler) compileV128AnyTrue(*wazeroir.OperationV128AnyTrue) error { + v := c.locationStack.popV128() + if err := c.compileEnsureOnGeneralPurposeRegister(v); err != nil { + return err + } + + c.assembler.CompileRegisterToRegister(amd64.PTEST, v.register, v.register) + + c.locationStack.pushRuntimeValueLocationOnConditionalRegister(amd64.ConditionalRegisterStateNE) + c.locationStack.markRegisterUnused(v.register) + return nil +} + +// compileV128AllTrue implements compiler.compileV128AllTrue for amd64. +func (c *amd64Compiler) compileV128AllTrue(o *wazeroir.OperationV128AllTrue) error { + v := c.locationStack.popV128() + if err := c.compileEnsureOnGeneralPurposeRegister(v); err != nil { + return err + } + + tmp, err := c.allocateRegister(registerTypeVector) + if err != nil { + return err + } + + var cmpInst asm.Instruction + switch o.Shape { + case wazeroir.ShapeI8x16: + cmpInst = amd64.PCMPEQB + case wazeroir.ShapeI16x8: + cmpInst = amd64.PCMPEQW + case wazeroir.ShapeI32x4: + cmpInst = amd64.PCMPEQD + case wazeroir.ShapeI64x2: + cmpInst = amd64.PCMPEQQ + } + + c.assembler.CompileRegisterToRegister(amd64.PXOR, tmp, tmp) + c.assembler.CompileRegisterToRegister(cmpInst, v.register, tmp) + c.assembler.CompileRegisterToRegister(amd64.PTEST, tmp, tmp) + c.locationStack.markRegisterUnused(v.register, tmp) + c.locationStack.pushRuntimeValueLocationOnConditionalRegister(amd64.ConditionalRegisterStateE) + return nil +} diff --git a/internal/engine/compiler/impl_vec_amd64_test.go b/internal/engine/compiler/impl_vec_amd64_test.go new file mode 100644 index 00000000000..0ff7b4f9271 --- /dev/null +++ b/internal/engine/compiler/impl_vec_amd64_test.go @@ -0,0 +1,64 @@ +package compiler + +import ( + "encoding/binary" + "testing" + + "github.com/tetratelabs/wazero/internal/asm/amd64" + "github.com/tetratelabs/wazero/internal/testing/require" + "github.com/tetratelabs/wazero/internal/wasm" + "github.com/tetratelabs/wazero/internal/wazeroir" +) + +// TestAmd64Compiler_V128Shuffle_ConstTable_MiddleOfFunction ensures that flushing constant table in the middle of +// function works well by intentionally setting amd64.AssemblerImpl MaxDisplacementForConstantPool = 0. +func TestAmd64Compiler_V128Shuffle_ConstTable_MiddleOfFunction(t *testing.T) { + env := newCompilerEnvironment() + compiler := env.requireNewCompiler(t, newCompiler, + &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) + + err := compiler.compilePreamble() + require.NoError(t, err) + + lanes := [16]byte{1, 1, 1, 1, 0, 0, 0, 0, 10, 10, 10, 10, 0, 0, 0, 0} + v := [16]byte{0: 0xa, 1: 0xb, 10: 0xc} + w := [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + exp := [16]byte{ + 0xb, 0xb, 0xb, 0xb, + 0xa, 0xa, 0xa, 0xa, + 0xc, 0xc, 0xc, 0xc, + 0xa, 0xa, 0xa, 0xa, + } + + err = compiler.compileV128Const(&wazeroir.OperationV128Const{ + Lo: binary.LittleEndian.Uint64(v[:8]), + Hi: binary.LittleEndian.Uint64(v[8:]), + }) + require.NoError(t, err) + + err = compiler.compileV128Const(&wazeroir.OperationV128Const{ + Lo: binary.LittleEndian.Uint64(w[:8]), + Hi: binary.LittleEndian.Uint64(w[8:]), + }) + require.NoError(t, err) + + err = compiler.compileV128Shuffle(&wazeroir.OperationV128Shuffle{Lanes: lanes}) + require.NoError(t, err) + + assembler := compiler.(*amd64Compiler).assembler.(*amd64.AssemblerImpl) + assembler.MaxDisplacementForConstantPool = 0 // Ensures that constant table for shuffle will be flushed immediately. + + err = compiler.compileReturnFunction() + require.NoError(t, err) + + // Generate and run the code under test. + code, _, _, err := compiler.compile() + require.NoError(t, err) + env.exec(code) + + lo, hi := env.stackTopAsV128() + var actual [16]byte + binary.LittleEndian.PutUint64(actual[:8], lo) + binary.LittleEndian.PutUint64(actual[8:], hi) + require.Equal(t, exp, actual) +} diff --git a/internal/engine/compiler/impl_vec_arm64.go b/internal/engine/compiler/impl_vec_arm64.go index 7ec7a949055..1faa7f2ca34 100644 --- a/internal/engine/compiler/impl_vec_arm64.go +++ b/internal/engine/compiler/impl_vec_arm64.go @@ -1,12 +1,13 @@ package compiler import ( + "fmt" "github.com/tetratelabs/wazero/internal/asm" "github.com/tetratelabs/wazero/internal/asm/arm64" "github.com/tetratelabs/wazero/internal/wazeroir" ) -func (c *arm64Compiler) compileConstV128(o *wazeroir.OperationConstV128) error { +func (c *arm64Compiler) compileV128Const(o *wazeroir.OperationV128Const) error { c.maybeCompileMoveTopConditionalToFreeGeneralPurposeRegister() result, err := c.allocateRegister(registerTypeVector) @@ -33,20 +34,18 @@ func (c *arm64Compiler) compileConstV128(o *wazeroir.OperationConstV128) error { // "ins Vn.D[1], intReg" c.assembler.CompileRegisterToVectorRegister(arm64.VMOV, intReg, result, arm64.VectorArrangementD, 1) - c.pushRuntimeValueLocationOnRegister(result, runtimeValueTypeV128Lo) - c.pushRuntimeValueLocationOnRegister(result, runtimeValueTypeV128Hi) + c.pushVectorRuntimeValueLocationOnRegister(result) return nil } -func (c *arm64Compiler) compileAddV128(o *wazeroir.OperationAddV128) error { - c.locationStack.pop() // skip higher 64-bits. - x1Low := c.locationStack.pop() - if err := c.compileEnsureOnGeneralPurposeRegister(x1Low); err != nil { +func (c *arm64Compiler) compileV128Add(o *wazeroir.OperationV128Add) error { + x2 := c.locationStack.popV128() + if err := c.compileEnsureOnGeneralPurposeRegister(x2); err != nil { return err } - c.locationStack.pop() // skip higher 64-bits. - x2Low := c.locationStack.pop() - if err := c.compileEnsureOnGeneralPurposeRegister(x2Low); err != nil { + + x1 := c.locationStack.popV128() + if err := c.compileEnsureOnGeneralPurposeRegister(x1); err != nil { return err } @@ -73,12 +72,566 @@ func (c *arm64Compiler) compileAddV128(o *wazeroir.OperationAddV128) error { arr = arm64.VectorArrangement2D } - c.assembler.CompileVectorRegisterToVectorRegister(inst, x1Low.register, x2Low.register, arr) - - resultReg := x2Low.register - c.pushRuntimeValueLocationOnRegister(resultReg, runtimeValueTypeV128Lo) - c.pushRuntimeValueLocationOnRegister(resultReg, runtimeValueTypeV128Hi) + c.assembler.CompileVectorRegisterToVectorRegister(inst, x1.register, x2.register, arr, + arm64.VectorIndexNone, arm64.VectorIndexNone) - c.markRegisterUnused(x1Low.register) + c.pushVectorRuntimeValueLocationOnRegister(x2.register) + c.markRegisterUnused(x1.register) return nil } + +// compileV128Sub implements compiler.compileV128Sub for arm64. +func (c *arm64Compiler) compileV128Sub(o *wazeroir.OperationV128Sub) (err error) { + x2 := c.locationStack.popV128() + if err := c.compileEnsureOnGeneralPurposeRegister(x2); err != nil { + return err + } + + x1 := c.locationStack.popV128() + if err := c.compileEnsureOnGeneralPurposeRegister(x1); err != nil { + return err + } + + fmt.Println(arm64.RegisterName(x1.register)) + fmt.Println(arm64.RegisterName(x2.register)) + + var arr arm64.VectorArrangement + var inst asm.Instruction + switch o.Shape { + case wazeroir.ShapeI8x16: + inst = arm64.VSUB + arr = arm64.VectorArrangement16B + case wazeroir.ShapeI16x8: + inst = arm64.VSUB + arr = arm64.VectorArrangement8H + case wazeroir.ShapeI32x4: + inst = arm64.VSUB + arr = arm64.VectorArrangement4S + case wazeroir.ShapeI64x2: + inst = arm64.VSUB + arr = arm64.VectorArrangement2D + case wazeroir.ShapeF32x4: + inst = arm64.VFSUBS + arr = arm64.VectorArrangement4S + case wazeroir.ShapeF64x2: + inst = arm64.VFSUBD + arr = arm64.VectorArrangement2D + } + + c.assembler.CompileVectorRegisterToVectorRegister(inst, x2.register, x1.register, arr, + arm64.VectorIndexNone, arm64.VectorIndexNone) + + c.pushVectorRuntimeValueLocationOnRegister(x1.register) + c.markRegisterUnused(x2.register) + return +} + +// compileV128Load implements compiler.compileV128Load for arm64. +func (c *arm64Compiler) compileV128Load(o *wazeroir.OperationV128Load) (err error) { + c.maybeCompileMoveTopConditionalToFreeGeneralPurposeRegister() + result, err := c.allocateRegister(registerTypeVector) + if err != nil { + return err + } + + switch o.Type { + case wazeroir.LoadV128Type128: + offset, err := c.compileMemoryAccessOffsetSetup(o.Arg.Offset, 16) + if err != nil { + return err + } + c.assembler.CompileMemoryWithRegisterOffsetToVectorRegister(arm64.VMOV, + arm64ReservedRegisterForMemory, offset, result, arm64.VectorArrangementQ, + ) + case wazeroir.LoadV128Type8x8s: + offset, err := c.compileMemoryAccessOffsetSetup(o.Arg.Offset, 8) + if err != nil { + return err + } + c.assembler.CompileMemoryWithRegisterOffsetToVectorRegister(arm64.VMOV, + arm64ReservedRegisterForMemory, offset, result, arm64.VectorArrangementD, + ) + c.assembler.CompileVectorRegisterToVectorRegister(arm64.SSHLL, result, result, + arm64.VectorArrangement8B, arm64.VectorIndexNone, arm64.VectorIndexNone) + case wazeroir.LoadV128Type8x8u: + offset, err := c.compileMemoryAccessOffsetSetup(o.Arg.Offset, 8) + if err != nil { + return err + } + c.assembler.CompileMemoryWithRegisterOffsetToVectorRegister(arm64.VMOV, + arm64ReservedRegisterForMemory, offset, result, arm64.VectorArrangementD, + ) + c.assembler.CompileVectorRegisterToVectorRegister(arm64.USHLL, result, result, + arm64.VectorArrangement8B, arm64.VectorIndexNone, arm64.VectorIndexNone) + case wazeroir.LoadV128Type16x4s: + offset, err := c.compileMemoryAccessOffsetSetup(o.Arg.Offset, 8) + if err != nil { + return err + } + c.assembler.CompileMemoryWithRegisterOffsetToVectorRegister(arm64.VMOV, + arm64ReservedRegisterForMemory, offset, result, arm64.VectorArrangementD, + ) + c.assembler.CompileVectorRegisterToVectorRegister(arm64.SSHLL, result, result, + arm64.VectorArrangement4H, arm64.VectorIndexNone, arm64.VectorIndexNone) + case wazeroir.LoadV128Type16x4u: + offset, err := c.compileMemoryAccessOffsetSetup(o.Arg.Offset, 8) + if err != nil { + return err + } + c.assembler.CompileMemoryWithRegisterOffsetToVectorRegister(arm64.VMOV, + arm64ReservedRegisterForMemory, offset, result, arm64.VectorArrangementD, + ) + c.assembler.CompileVectorRegisterToVectorRegister(arm64.USHLL, result, result, + arm64.VectorArrangement4H, arm64.VectorIndexNone, arm64.VectorIndexNone) + case wazeroir.LoadV128Type32x2s: + offset, err := c.compileMemoryAccessOffsetSetup(o.Arg.Offset, 8) + if err != nil { + return err + } + c.assembler.CompileMemoryWithRegisterOffsetToVectorRegister(arm64.VMOV, + arm64ReservedRegisterForMemory, offset, result, arm64.VectorArrangementD, + ) + c.assembler.CompileVectorRegisterToVectorRegister(arm64.SSHLL, result, result, + arm64.VectorArrangement2S, arm64.VectorIndexNone, arm64.VectorIndexNone) + case wazeroir.LoadV128Type32x2u: + offset, err := c.compileMemoryAccessOffsetSetup(o.Arg.Offset, 8) + if err != nil { + return err + } + c.assembler.CompileMemoryWithRegisterOffsetToVectorRegister(arm64.VMOV, + arm64ReservedRegisterForMemory, offset, result, arm64.VectorArrangementD, + ) + c.assembler.CompileVectorRegisterToVectorRegister(arm64.USHLL, result, result, + arm64.VectorArrangement2S, arm64.VectorIndexNone, arm64.VectorIndexNone) + case wazeroir.LoadV128Type8Splat: + offset, err := c.compileMemoryAccessOffsetSetup(o.Arg.Offset, 1) + if err != nil { + return err + } + c.assembler.CompileRegisterToRegister(arm64.ADD, arm64ReservedRegisterForMemory, offset) + c.assembler.CompileMemoryToVectorRegister(arm64.LD1R, offset, 0, result, arm64.VectorArrangement16B) + case wazeroir.LoadV128Type16Splat: + offset, err := c.compileMemoryAccessOffsetSetup(o.Arg.Offset, 2) + if err != nil { + return err + } + c.assembler.CompileRegisterToRegister(arm64.ADD, arm64ReservedRegisterForMemory, offset) + c.assembler.CompileMemoryToVectorRegister(arm64.LD1R, offset, 0, result, arm64.VectorArrangement8H) + case wazeroir.LoadV128Type32Splat: + offset, err := c.compileMemoryAccessOffsetSetup(o.Arg.Offset, 4) + if err != nil { + return err + } + c.assembler.CompileRegisterToRegister(arm64.ADD, arm64ReservedRegisterForMemory, offset) + c.assembler.CompileMemoryToVectorRegister(arm64.LD1R, offset, 0, result, arm64.VectorArrangement4S) + case wazeroir.LoadV128Type64Splat: + offset, err := c.compileMemoryAccessOffsetSetup(o.Arg.Offset, 8) + if err != nil { + return err + } + c.assembler.CompileRegisterToRegister(arm64.ADD, arm64ReservedRegisterForMemory, offset) + c.assembler.CompileMemoryToVectorRegister(arm64.LD1R, offset, 0, result, arm64.VectorArrangement2D) + case wazeroir.LoadV128Type32zero: + offset, err := c.compileMemoryAccessOffsetSetup(o.Arg.Offset, 16) + if err != nil { + return err + } + c.assembler.CompileMemoryWithRegisterOffsetToVectorRegister(arm64.VMOV, + arm64ReservedRegisterForMemory, offset, result, arm64.VectorArrangementS, + ) + case wazeroir.LoadV128Type64zero: + offset, err := c.compileMemoryAccessOffsetSetup(o.Arg.Offset, 16) + if err != nil { + return err + } + c.assembler.CompileMemoryWithRegisterOffsetToVectorRegister(arm64.VMOV, + arm64ReservedRegisterForMemory, offset, result, arm64.VectorArrangementD, + ) + } + + c.pushVectorRuntimeValueLocationOnRegister(result) + return +} + +// compileV128LoadLane implements compiler.compileV128LoadLane for arm64. +func (c *arm64Compiler) compileV128LoadLane(o *wazeroir.OperationV128LoadLane) (err error) { + targetVector := c.locationStack.popV128() + if err = c.compileEnsureOnGeneralPurposeRegister(targetVector); err != nil { + return + } + + targetSizeInBytes := int64(o.LaneSize / 8) + source, err := c.compileMemoryAccessOffsetSetup(o.Arg.Offset, targetSizeInBytes) + if err != nil { + return err + } + + var loadInst asm.Instruction + var arr arm64.VectorArrangement + switch o.LaneSize { + case 8: + arr = arm64.VectorArrangementB + loadInst = arm64.MOVB + case 16: + arr = arm64.VectorArrangementH + loadInst = arm64.MOVH + case 32: + loadInst = arm64.MOVW + arr = arm64.VectorArrangementS + case 64: + loadInst = arm64.MOVD + arr = arm64.VectorArrangementD + } + + c.assembler.CompileMemoryWithRegisterOffsetToRegister(loadInst, arm64ReservedRegisterForMemory, source, source) + c.assembler.CompileRegisterToVectorRegister(arm64.VMOV, source, targetVector.register, arr, arm64.VectorIndex(o.LaneIndex)) + + c.pushVectorRuntimeValueLocationOnRegister(targetVector.register) + c.locationStack.markRegisterUnused(source) + return +} + +// compileV128Store implements compiler.compileV128Store for arm64. +func (c *arm64Compiler) compileV128Store(o *wazeroir.OperationV128Store) (err error) { + v := c.locationStack.popV128() + if err = c.compileEnsureOnGeneralPurposeRegister(v); err != nil { + return + } + + const targetSizeInBytes = 16 + offset, err := c.compileMemoryAccessOffsetSetup(o.Arg.Offset, targetSizeInBytes) + if err != nil { + return err + } + + c.assembler.CompileVectorRegisterToMemoryWithRegisterOffset(arm64.VMOV, + v.register, arm64ReservedRegisterForMemory, offset, arm64.VectorArrangementQ) + + c.markRegisterUnused(v.register) + return +} + +// compileV128StoreLane implements compiler.compileV128StoreLane for arm64. +func (c *arm64Compiler) compileV128StoreLane(o *wazeroir.OperationV128StoreLane) (err error) { + var arr arm64.VectorArrangement + var storeInst asm.Instruction + switch o.LaneSize { + case 8: + storeInst = arm64.MOVB + arr = arm64.VectorArrangementB + case 16: + storeInst = arm64.MOVH + arr = arm64.VectorArrangementH + case 32: + storeInst = arm64.MOVW + arr = arm64.VectorArrangementS + case 64: + storeInst = arm64.MOVD + arr = arm64.VectorArrangementD + } + + v := c.locationStack.popV128() + if err = c.compileEnsureOnGeneralPurposeRegister(v); err != nil { + return + } + + targetSizeInBytes := int64(o.LaneSize / 8) + offset, err := c.compileMemoryAccessOffsetSetup(o.Arg.Offset, targetSizeInBytes) + if err != nil { + return err + } + + c.assembler.CompileVectorRegisterToRegister(arm64.VMOV, v.register, arm64ReservedRegisterForTemporary, arr, + arm64.VectorIndex(o.LaneIndex)) + + c.assembler.CompileRegisterToMemoryWithRegisterOffset(storeInst, + arm64ReservedRegisterForTemporary, arm64ReservedRegisterForMemory, offset) + + c.locationStack.markRegisterUnused(v.register) + return +} + +// compileV128ExtractLane implements compiler.compileV128ExtractLane for arm64. +func (c *arm64Compiler) compileV128ExtractLane(o *wazeroir.OperationV128ExtractLane) (err error) { + v := c.locationStack.popV128() + if err = c.compileEnsureOnGeneralPurposeRegister(v); err != nil { + return + } + + switch o.Shape { + case wazeroir.ShapeI8x16: + result, err := c.allocateRegister(registerTypeGeneralPurpose) + if err != nil { + return err + } + var inst asm.Instruction + if o.Signed { + inst = arm64.SMOV + } else { + inst = arm64.VMOV + } + c.assembler.CompileVectorRegisterToRegister(inst, v.register, result, + arm64.VectorArrangementB, arm64.VectorIndex(o.LaneIndex)) + + c.locationStack.markRegisterUnused(v.register) + c.pushRuntimeValueLocationOnRegister(result, runtimeValueTypeI32) + case wazeroir.ShapeI16x8: + result, err := c.allocateRegister(registerTypeGeneralPurpose) + if err != nil { + return err + } + var inst asm.Instruction + if o.Signed { + inst = arm64.SMOV + } else { + inst = arm64.VMOV + } + c.assembler.CompileVectorRegisterToRegister(inst, v.register, result, + arm64.VectorArrangementH, arm64.VectorIndex(o.LaneIndex)) + + c.locationStack.markRegisterUnused(v.register) + c.pushRuntimeValueLocationOnRegister(result, runtimeValueTypeI32) + case wazeroir.ShapeI32x4: + result, err := c.allocateRegister(registerTypeGeneralPurpose) + if err != nil { + return err + } + c.assembler.CompileVectorRegisterToRegister(arm64.VMOV, v.register, result, + arm64.VectorArrangementS, arm64.VectorIndex(o.LaneIndex)) + + c.locationStack.markRegisterUnused(v.register) + c.pushRuntimeValueLocationOnRegister(result, runtimeValueTypeI32) + case wazeroir.ShapeI64x2: + result, err := c.allocateRegister(registerTypeGeneralPurpose) + if err != nil { + return err + } + c.assembler.CompileVectorRegisterToRegister(arm64.VMOV, v.register, result, + arm64.VectorArrangementD, arm64.VectorIndex(o.LaneIndex)) + + c.locationStack.markRegisterUnused(v.register) + c.pushRuntimeValueLocationOnRegister(result, runtimeValueTypeI64) + case wazeroir.ShapeF32x4: + c.assembler.CompileVectorRegisterToVectorRegister(arm64.VMOV, v.register, v.register, + arm64.VectorArrangementS, arm64.VectorIndex(o.LaneIndex), 0) + c.pushRuntimeValueLocationOnRegister(v.register, runtimeValueTypeF32) + case wazeroir.ShapeF64x2: + c.assembler.CompileVectorRegisterToVectorRegister(arm64.VMOV, v.register, v.register, + arm64.VectorArrangementD, arm64.VectorIndex(o.LaneIndex), 0) + c.pushRuntimeValueLocationOnRegister(v.register, runtimeValueTypeF64) + } + return +} + +// compileV128ReplaceLane implements compiler.compileV128ReplaceLane for arm64. +func (c *arm64Compiler) compileV128ReplaceLane(o *wazeroir.OperationV128ReplaceLane) (err error) { + origin := c.locationStack.pop() + if err = c.compileEnsureOnGeneralPurposeRegister(origin); err != nil { + return + } + + vector := c.locationStack.popV128() + if err = c.compileEnsureOnGeneralPurposeRegister(vector); err != nil { + return + } + + switch o.Shape { + case wazeroir.ShapeI8x16: + c.assembler.CompileRegisterToVectorRegister(arm64.VMOV, origin.register, vector.register, + arm64.VectorArrangementB, arm64.VectorIndex(o.LaneIndex)) + case wazeroir.ShapeI16x8: + c.assembler.CompileRegisterToVectorRegister(arm64.VMOV, origin.register, vector.register, + arm64.VectorArrangementH, arm64.VectorIndex(o.LaneIndex)) + case wazeroir.ShapeI32x4: + c.assembler.CompileRegisterToVectorRegister(arm64.VMOV, origin.register, vector.register, + arm64.VectorArrangementS, arm64.VectorIndex(o.LaneIndex)) + case wazeroir.ShapeI64x2: + c.assembler.CompileRegisterToVectorRegister(arm64.VMOV, origin.register, vector.register, + arm64.VectorArrangementD, arm64.VectorIndex(o.LaneIndex)) + case wazeroir.ShapeF32x4: + c.assembler.CompileVectorRegisterToVectorRegister(arm64.VMOV, origin.register, vector.register, + arm64.VectorArrangementS, 0, arm64.VectorIndex(o.LaneIndex)) + case wazeroir.ShapeF64x2: + c.assembler.CompileVectorRegisterToVectorRegister(arm64.VMOV, origin.register, vector.register, + arm64.VectorArrangementD, 0, arm64.VectorIndex(o.LaneIndex)) + } + + c.locationStack.markRegisterUnused(origin.register) + c.pushVectorRuntimeValueLocationOnRegister(vector.register) + return +} + +// compileV128Splat implements compiler.compileV128Splat for arm64. +func (c *arm64Compiler) compileV128Splat(o *wazeroir.OperationV128Splat) (err error) { + origin := c.locationStack.pop() + if err = c.compileEnsureOnGeneralPurposeRegister(origin); err != nil { + return + } + + var result asm.Register + switch o.Shape { + case wazeroir.ShapeI8x16: + result, err = c.allocateRegister(registerTypeVector) + if err != nil { + return + } + c.assembler.CompileRegisterToVectorRegister(arm64.DUP, origin.register, result, + arm64.VectorArrangementB, arm64.VectorIndexNone) + case wazeroir.ShapeI16x8: + result, err = c.allocateRegister(registerTypeVector) + if err != nil { + return + } + c.assembler.CompileRegisterToVectorRegister(arm64.DUP, origin.register, result, + arm64.VectorArrangementH, arm64.VectorIndexNone) + case wazeroir.ShapeI32x4: + result, err = c.allocateRegister(registerTypeVector) + if err != nil { + return + } + c.assembler.CompileRegisterToVectorRegister(arm64.DUP, origin.register, result, + arm64.VectorArrangementS, arm64.VectorIndexNone) + case wazeroir.ShapeI64x2: + result, err = c.allocateRegister(registerTypeVector) + if err != nil { + return + } + c.assembler.CompileRegisterToVectorRegister(arm64.DUP, origin.register, result, + arm64.VectorArrangementD, arm64.VectorIndexNone) + case wazeroir.ShapeF32x4: + result = origin.register + c.assembler.CompileVectorRegisterToVectorRegister(arm64.DUP, origin.register, result, + arm64.VectorArrangementS, 0, arm64.VectorIndexNone) + case wazeroir.ShapeF64x2: + result = origin.register + c.assembler.CompileVectorRegisterToVectorRegister(arm64.DUP, origin.register, result, + arm64.VectorArrangementD, 0, arm64.VectorIndexNone) + } + + c.locationStack.markRegisterUnused(origin.register) + c.pushVectorRuntimeValueLocationOnRegister(result) + return +} + +// compileV128Shuffle implements compiler.compileV128Shuffle for arm64. +func (c *arm64Compiler) compileV128Shuffle(o *wazeroir.OperationV128Shuffle) (err error) { + w := c.locationStack.popV128() + v := c.locationStack.popV128() + if err := c.compileEnsureOnGeneralPurposeRegister(v); err != nil { + return err + } + + r1 := v.register + var r2 asm.Register + if r1 < arm64.RegV30 { + r2 = v.register + 1 + } else { + r2 = v.register - 1 + } + + if w.onRegister() { + c.assembler.CompileVectorRegisterToVectorRegister(arm64.VMOV, w.register, r2, + arm64.VectorArrangement16B, arm64.VectorIndexNone, arm64.VectorIndexNone) + c.markRegisterUnused(w.register) + } else { // on stack + w.setRegister(r2) + c.compileLoadValueOnStackToRegister(w) + } + + c.locationStack.markRegisterUsed(r1, r2) + + result, err := c.allocateRegister(registerTypeVector) + if err != nil { + return err + } + + c.assembler.CompileLoadStaticConstToVectorRegister(arm64.VMOV, o.Lanes[:], result, arm64.VectorArrangementQ) + + var origin asm.Register + if r1 < r2 { + origin = r1 + } else { + origin = r2 + } + c.assembler.CompileVectorRegisterToVectorRegister(arm64.TBL2, origin, result, arm64.VectorArrangement16B, + arm64.VectorIndexNone, arm64.VectorIndexNone) + + c.locationStack.markRegisterUnused(r1, r2) + c.pushVectorRuntimeValueLocationOnRegister(result) + return +} + +// compileV128Swizzle implements compiler.compileV128Swizzle for arm64. +func (c *arm64Compiler) compileV128Swizzle(o *wazeroir.OperationV128Swizzle) (err error) { + indexVec := c.locationStack.popV128() + if err = c.compileEnsureOnGeneralPurposeRegister(indexVec); err != nil { + return + } + baseVec := c.locationStack.popV128() + if err = c.compileEnsureOnGeneralPurposeRegister(baseVec); err != nil { + return + } + + c.assembler.CompileVectorRegisterToVectorRegister(arm64.TBL1, baseVec.register, indexVec.register, + arm64.VectorArrangement16B, arm64.VectorIndexNone, arm64.VectorIndexNone) + + c.markRegisterUnused(baseVec.register) + c.pushVectorRuntimeValueLocationOnRegister(indexVec.register) + return +} + +// compileV128AnyTrue implements compiler.compileV128AnyTrue for arm64. +func (c *arm64Compiler) compileV128AnyTrue(*wazeroir.OperationV128AnyTrue) (err error) { + vector := c.locationStack.popV128() + if err = c.compileEnsureOnGeneralPurposeRegister(vector); err != nil { + return + } + + v := vector.register + c.assembler.CompileVectorRegisterToVectorRegister(arm64.UMAXP, v, v, + arm64.VectorArrangement16B, arm64.VectorIndexNone, arm64.VectorIndexNone) + c.assembler.CompileVectorRegisterToRegister(arm64.VMOV, v, arm64ReservedRegisterForTemporary, + arm64.VectorArrangementD, 0) + c.assembler.CompileTwoRegistersToNone(arm64.CMP, arm64.RegRZR, arm64ReservedRegisterForTemporary) + c.locationStack.pushRuntimeValueLocationOnConditionalRegister(arm64.CondNE) + + c.locationStack.markRegisterUnused(v) + return +} + +// compileV128AllTrue implements compiler.compileV128AllTrue for arm64. +func (c *arm64Compiler) compileV128AllTrue(o *wazeroir.OperationV128AllTrue) (err error) { + vector := c.locationStack.popV128() + if err = c.compileEnsureOnGeneralPurposeRegister(vector); err != nil { + return + } + + v := vector.register + if o.Shape == wazeroir.ShapeI64x2 { + c.assembler.CompileVectorRegisterToVectorRegister(arm64.CMEQ, arm64.RegRZR, v, + arm64.VectorArrangementNone, arm64.VectorIndexNone, arm64.VectorIndexNone) + c.assembler.CompileVectorRegisterToVectorRegister(arm64.ADDP, v, v, + arm64.VectorArrangementD, arm64.VectorIndexNone, arm64.VectorIndexNone) + c.assembler.CompileTwoRegistersToNone(arm64.FCMPD, v, v) + c.locationStack.pushRuntimeValueLocationOnConditionalRegister(arm64.CondEQ) + } else { + var arr arm64.VectorArrangement + switch o.Shape { + case wazeroir.ShapeI8x16: + arr = arm64.VectorArrangement16B + case wazeroir.ShapeI16x8: + arr = arm64.VectorArrangement8H + case wazeroir.ShapeI32x4: + arr = arm64.VectorArrangement4S + } + + c.assembler.CompileVectorRegisterToVectorRegister(arm64.UMINV, v, v, + arr, arm64.VectorIndexNone, arm64.VectorIndexNone) + c.assembler.CompileVectorRegisterToRegister(arm64.VMOV, v, arm64ReservedRegisterForTemporary, + arm64.VectorArrangementD, 0) + c.assembler.CompileTwoRegistersToNone(arm64.CMP, arm64.RegRZR, arm64ReservedRegisterForTemporary) + c.locationStack.pushRuntimeValueLocationOnConditionalRegister(arm64.CondNE) + } + c.markRegisterUnused(v) + return +} diff --git a/internal/engine/interpreter/interpreter.go b/internal/engine/interpreter/interpreter.go index e6238d6dda7..065bb92f61c 100644 --- a/internal/engine/interpreter/interpreter.go +++ b/internal/engine/interpreter/interpreter.go @@ -2,6 +2,7 @@ package interpreter import ( "context" + "encoding/binary" "fmt" "math" "math/bits" @@ -577,14 +578,55 @@ func (e *engine) lowerIR(ir *wazeroir.CompilationResult) (*code, error) { case *wazeroir.OperationTableFill: op.us = make([]uint64, 1) op.us[0] = uint64(o.TableIndex) - case *wazeroir.OperationConstV128: + case *wazeroir.OperationV128Const: op.us = make([]uint64, 2) op.us[0] = o.Lo op.us[1] = o.Hi - case *wazeroir.OperationAddV128: - op.b1 = byte(o.Shape) + case *wazeroir.OperationV128Add: + op.b1 = o.Shape + case *wazeroir.OperationV128Sub: + op.b1 = o.Shape + case *wazeroir.OperationV128Load: + op.b1 = o.Type + op.us = make([]uint64, 2) + op.us[0] = uint64(o.Arg.Alignment) + op.us[1] = uint64(o.Arg.Offset) + case *wazeroir.OperationV128LoadLane: + op.b1 = o.LaneSize + op.b2 = o.LaneIndex + op.us = make([]uint64, 2) + op.us[0] = uint64(o.Arg.Alignment) + op.us[1] = uint64(o.Arg.Offset) + case *wazeroir.OperationV128Store: + op.us = make([]uint64, 2) + op.us[0] = uint64(o.Arg.Alignment) + op.us[1] = uint64(o.Arg.Offset) + case *wazeroir.OperationV128StoreLane: + op.b1 = o.LaneSize + op.b2 = o.LaneIndex + op.us = make([]uint64, 2) + op.us[0] = uint64(o.Arg.Alignment) + op.us[1] = uint64(o.Arg.Offset) + case *wazeroir.OperationV128ExtractLane: + op.b1 = o.Shape + op.b2 = o.LaneIndex + op.b3 = o.Signed + case *wazeroir.OperationV128ReplaceLane: + op.b1 = o.Shape + op.b2 = o.LaneIndex + case *wazeroir.OperationV128Splat: + op.b1 = o.Shape + case *wazeroir.OperationV128Shuffle: + op.us = make([]uint64, 16) + for i, l := range o.Lanes { + op.us[i] = uint64(l) + } + case *wazeroir.OperationV128Swizzle: + case *wazeroir.OperationV128AnyTrue: + case *wazeroir.OperationV128AllTrue: + op.b1 = o.Shape default: - return nil, fmt.Errorf("unreachable: a bug in wazeroir engine") + panic(fmt.Errorf("BUG: unimplemented operation %s", op.kind.String())) } ret.body = append(ret.body, op) } @@ -812,14 +854,14 @@ func (ce *callEngine) callNativeFunc(ctx context.Context, callCtx *wasm.CallCont } frame.pc++ case wazeroir.OperationKindGlobalGet: - g := globals[op.us[0]] // TODO: Not yet traceable as it doesn't use the types in global.go + g := globals[op.us[0]] ce.pushValue(g.Val) if g.Type.ValType == wasm.ValueTypeV128 { ce.pushValue(g.ValHi) } frame.pc++ case wazeroir.OperationKindGlobalSet: - g := globals[op.us[0]] // TODO: Not yet traceable as it doesn't use the types in global.go + g := globals[op.us[0]] if g.Type.ValType == wasm.ValueTypeV128 { g.ValHi = ce.popValue() } @@ -1815,25 +1857,484 @@ func (ce *callEngine) callNativeFunc(ctx context.Context, callCtx *wasm.CallCont } } frame.pc++ - case wazeroir.OperationKindConstV128: + case wazeroir.OperationKindV128Const: lo, hi := op.us[0], op.us[1] ce.pushValue(lo) ce.pushValue(hi) frame.pc++ case wazeroir.OperationKindV128Add: + xHigh, xLow := ce.popValue(), ce.popValue() + yHigh, yLow := ce.popValue(), ce.popValue() switch op.b1 { + case wazeroir.ShapeI8x16: + ce.pushValue( + uint64(uint8(xLow>>8)+uint8(yLow>>8))<<8 | uint64(uint8(xLow)+uint8(yLow)) | + uint64(uint8(xLow>>24)+uint8(yLow>>24))<<24 | uint64(uint8(xLow>>16)+uint8(yLow>>16))<<16 | + uint64(uint8(xLow>>40)+uint8(yLow>>40))<<40 | uint64(uint8(xLow>>32)+uint8(yLow>>32))<<32 | + uint64(uint8(xLow>>56)+uint8(yLow>>56))<<56 | uint64(uint8(xLow>>48)+uint8(yLow>>48))<<48, + ) + ce.pushValue( + uint64(uint8(xHigh>>8)+uint8(yHigh>>8))<<8 | uint64(uint8(xHigh)+uint8(yHigh)) | + uint64(uint8(xHigh>>24)+uint8(yHigh>>24))<<24 | uint64(uint8(xHigh>>16)+uint8(yHigh>>16))<<16 | + uint64(uint8(xHigh>>40)+uint8(yHigh>>40))<<40 | uint64(uint8(xHigh>>32)+uint8(yHigh>>32))<<32 | + uint64(uint8(xHigh>>56)+uint8(yHigh>>56))<<56 | uint64(uint8(xHigh>>48)+uint8(yHigh>>48))<<48, + ) + case wazeroir.ShapeI16x8: + ce.pushValue( + uint64(uint16(xLow>>16+yLow>>16))<<16 | uint64(uint16(xLow)+uint16(yLow)) | + uint64(uint16(xLow>>48+yLow>>48))<<48 | uint64(uint16(xLow>>32+yLow>>32))<<32, + ) + ce.pushValue( + uint64(uint16(xHigh>>16)+uint16(yHigh>>16))<<16 | uint64(uint16(xHigh)+uint16(yHigh)) | + uint64(uint16(xHigh>>48)+uint16(yHigh>>48))<<48 | uint64(uint16(xHigh>>32)+uint16(yHigh>>32))<<32, + ) case wazeroir.ShapeI32x4: - xHigh, xLow := ce.popValue(), ce.popValue() - yHigh, yLow := ce.popValue(), ce.popValue() - ce.pushValue(uint64(uint32(xLow>>32+yLow>>32))<<32 + uint64(uint32(xLow)+uint32(yLow))) - ce.pushValue(uint64(uint32(xHigh>>32+yHigh>>32))<<32 + uint64(uint32(xHigh)+uint32(yHigh))) + ce.pushValue(uint64(uint32(xLow>>32)+uint32(yLow>>32))<<32 | uint64(uint32(xLow)+uint32(yLow))) + ce.pushValue(uint64(uint32(xHigh>>32)+uint32(yHigh>>32))<<32 | uint64(uint32(xHigh)+uint32(yHigh))) case wazeroir.ShapeI64x2: - xHigh, xLow := ce.popValue(), ce.popValue() - yHigh, yLow := ce.popValue(), ce.popValue() ce.pushValue(xLow + yLow) ce.pushValue(xHigh + yHigh) } frame.pc++ + case wazeroir.OperationKindV128Sub: + yHigh, yLow := ce.popValue(), ce.popValue() + xHigh, xLow := ce.popValue(), ce.popValue() + switch op.b1 { + case wazeroir.ShapeI8x16: + ce.pushValue( + uint64(uint8(xLow>>8)-uint8(yLow>>8))<<8 | uint64(uint8(xLow)-uint8(yLow)) | + uint64(uint8(xLow>>24)-uint8(yLow>>24))<<24 | uint64(uint8(xLow>>16)-uint8(yLow>>16))<<16 | + uint64(uint8(xLow>>40)-uint8(yLow>>40))<<40 | uint64(uint8(xLow>>32)-uint8(yLow>>32))<<32 | + uint64(uint8(xLow>>56)-uint8(yLow>>56))<<56 | uint64(uint8(xLow>>48)-uint8(yLow>>48))<<48, + ) + ce.pushValue( + uint64(uint8(xHigh>>8)-uint8(yHigh>>8))<<8 | uint64(uint8(xHigh)-uint8(yHigh)) | + uint64(uint8(xHigh>>24)-uint8(yHigh>>24))<<24 | uint64(uint8(xHigh>>16)-uint8(yHigh>>16))<<16 | + uint64(uint8(xHigh>>40)-uint8(yHigh>>40))<<40 | uint64(uint8(xHigh>>32)-uint8(yHigh>>32))<<32 | + uint64(uint8(xHigh>>56)-uint8(yHigh>>56))<<56 | uint64(uint8(xHigh>>48)-uint8(yHigh>>48))<<48, + ) + case wazeroir.ShapeI16x8: + ce.pushValue( + uint64(uint16(xLow>>16)-uint16(yLow>>16))<<16 | uint64(uint16(xLow)-uint16(yLow)) | + uint64(uint16(xLow>>48)-uint16(yLow>>48))<<48 | uint64(uint16(xLow>>32)-uint16(yLow>>32))<<32, + ) + ce.pushValue( + uint64(uint16(xHigh>>16)-uint16(yHigh>>16))<<16 | uint64(uint16(xHigh)-uint16(yHigh)) | + uint64(uint16(xHigh>>48)-uint16(yHigh>>48))<<48 | uint64(uint16(xHigh>>32)-uint16(yHigh>>32))<<32, + ) + case wazeroir.ShapeI32x4: + ce.pushValue(uint64(uint32(xLow>>32-yLow>>32))<<32 | uint64(uint32(xLow)-uint32(yLow))) + ce.pushValue(uint64(uint32(xHigh>>32-yHigh>>32))<<32 | uint64(uint32(xHigh)-uint32(yHigh))) + case wazeroir.ShapeI64x2: + ce.pushValue(xLow - yLow) + ce.pushValue(xHigh - yHigh) + } + frame.pc++ + case wazeroir.OperationKindV128Load: + offset := ce.popMemoryOffset(op) + switch op.b1 { + case wazeroir.LoadV128Type128: + lo, ok := memoryInst.ReadUint64Le(ctx, offset) + if !ok { + panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess) + } + ce.pushValue(lo) + hi, ok := memoryInst.ReadUint64Le(ctx, offset+8) + if !ok { + panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess) + } + ce.pushValue(hi) + case wazeroir.LoadV128Type8x8s: + data, ok := memoryInst.Read(ctx, offset, 8) + if !ok { + panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess) + } + ce.pushValue( + uint64(uint16(int8(data[3])))<<48 | uint64(uint16(int8(data[2])))<<32 | uint64(uint16(int8(data[1])))<<16 | uint64(uint16(int8(data[0]))), + ) + ce.pushValue( + uint64(uint16(int8(data[7])))<<48 | uint64(uint16(int8(data[6])))<<32 | uint64(uint16(int8(data[5])))<<16 | uint64(uint16(int8(data[4]))), + ) + case wazeroir.LoadV128Type8x8u: + data, ok := memoryInst.Read(ctx, offset, 8) + if !ok { + panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess) + } + ce.pushValue( + uint64(data[3])<<48 | uint64(data[2])<<32 | uint64(data[1])<<16 | uint64(data[0]), + ) + ce.pushValue( + uint64(data[7])<<48 | uint64(data[6])<<32 | uint64(data[5])<<16 | uint64(data[4]), + ) + case wazeroir.LoadV128Type16x4s: + data, ok := memoryInst.Read(ctx, offset, 8) + if !ok { + panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess) + } + ce.pushValue( + uint64(int16(binary.LittleEndian.Uint16(data[2:])))<<32 | + uint64(uint32(int16(binary.LittleEndian.Uint16(data)))), + ) + ce.pushValue( + uint64(uint32(int16(binary.LittleEndian.Uint16(data[6:]))))<<32 | + uint64(uint32(int16(binary.LittleEndian.Uint16(data[4:])))), + ) + case wazeroir.LoadV128Type16x4u: + data, ok := memoryInst.Read(ctx, offset, 8) + if !ok { + panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess) + } + ce.pushValue( + uint64(binary.LittleEndian.Uint16(data[2:]))<<32 | uint64(binary.LittleEndian.Uint16(data)), + ) + ce.pushValue( + uint64(binary.LittleEndian.Uint16(data[6:]))<<32 | uint64(binary.LittleEndian.Uint16(data[4:])), + ) + case wazeroir.LoadV128Type32x2s: + data, ok := memoryInst.Read(ctx, offset, 8) + if !ok { + panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess) + } + ce.pushValue(uint64(int32(binary.LittleEndian.Uint32(data)))) + ce.pushValue(uint64(int32(binary.LittleEndian.Uint32(data[4:])))) + case wazeroir.LoadV128Type32x2u: + data, ok := memoryInst.Read(ctx, offset, 8) + if !ok { + panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess) + } + ce.pushValue(uint64(binary.LittleEndian.Uint32(data))) + ce.pushValue(uint64(binary.LittleEndian.Uint32(data[4:]))) + case wazeroir.LoadV128Type8Splat: + v, ok := memoryInst.ReadByte(ctx, offset) + if !ok { + panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess) + } + v8 := uint64(v)<<56 | uint64(v)<<48 | uint64(v)<<40 | uint64(v)<<32 | + uint64(v)<<24 | uint64(v)<<16 | uint64(v)<<8 | uint64(v) + ce.pushValue(v8) + ce.pushValue(v8) + case wazeroir.LoadV128Type16Splat: + v, ok := memoryInst.ReadUint16Le(ctx, offset) + if !ok { + panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess) + } + v4 := uint64(v)<<48 | uint64(v)<<32 | uint64(v)<<16 | uint64(v) + ce.pushValue(v4) + ce.pushValue(v4) + case wazeroir.LoadV128Type32Splat: + v, ok := memoryInst.ReadUint32Le(ctx, offset) + if !ok { + panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess) + } + vv := uint64(v)<<32 | uint64(v) + ce.pushValue(vv) + ce.pushValue(vv) + case wazeroir.LoadV128Type64Splat: + lo, ok := memoryInst.ReadUint64Le(ctx, offset) + if !ok { + panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess) + } + ce.pushValue(lo) + ce.pushValue(lo) + case wazeroir.LoadV128Type32zero: + lo, ok := memoryInst.ReadUint32Le(ctx, offset) + if !ok { + panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess) + } + ce.pushValue(uint64(lo)) + ce.pushValue(0) + case wazeroir.LoadV128Type64zero: + lo, ok := memoryInst.ReadUint64Le(ctx, offset) + if !ok { + panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess) + } + ce.pushValue(lo) + ce.pushValue(0) + } + frame.pc++ + case wazeroir.OperationKindV128LoadLane: + hi, lo := ce.popValue(), ce.popValue() + offset := ce.popMemoryOffset(op) + switch op.b1 { + case 8: + b, ok := memoryInst.ReadByte(ctx, offset) + if !ok { + panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess) + } + if op.b2 < 8 { + s := op.b2 << 3 + lo = (lo & ^(0xff << s)) | uint64(b)<>(op.b2*8))) + } else { + ok = memoryInst.WriteByte(ctx, offset, byte(hi>>((op.b2-8)*8))) + } + case 16: + if op.b2 < 4 { + ok = memoryInst.WriteUint16Le(ctx, offset, uint16(lo>>(op.b2*16))) + } else { + ok = memoryInst.WriteUint16Le(ctx, offset, uint16(hi>>((op.b2-4)*16))) + } + case 32: + if op.b2 < 2 { + ok = memoryInst.WriteUint32Le(ctx, offset, uint32(lo>>(op.b2*32))) + } else { + ok = memoryInst.WriteUint32Le(ctx, offset, uint32(hi>>((op.b2-2)*32))) + } + case 64: + if op.b2 == 0 { + ok = memoryInst.WriteUint64Le(ctx, offset, lo) + } else { + ok = memoryInst.WriteUint64Le(ctx, offset, hi) + } + } + if !ok { + panic(wasmruntime.ErrRuntimeOutOfBoundsMemoryAccess) + } + frame.pc++ + case wazeroir.OperationKindV128ReplaceLane: + v := ce.popValue() + hi, lo := ce.popValue(), ce.popValue() + switch op.b1 { + case wazeroir.ShapeI8x16: + if op.b2 < 8 { + s := op.b2 << 3 + lo = (lo & ^(0xff << s)) | uint64(byte(v))<> (op.b2 * 8)) + } else { + u8 = byte(hi >> ((op.b2 - 8) * 8)) + } + if op.b3 { + // sign-extend. + v = uint64(int8(u8)) + } else { + v = uint64(u8) + } + case wazeroir.ShapeI16x8: + var u16 uint16 + if op.b2 < 4 { + u16 = uint16(lo >> (op.b2 * 16)) + } else { + u16 = uint16(hi >> ((op.b2 - 4) * 16)) + } + if op.b3 { + // sign-extend. + v = uint64(int16(u16)) + } else { + v = uint64(u16) + } + case wazeroir.ShapeI32x4, wazeroir.ShapeF32x4: + if op.b2 < 2 { + v = uint64(uint32(lo >> (op.b2 * 32))) + } else { + v = uint64(uint32(hi >> ((op.b2 - 2) * 32))) + } + case wazeroir.ShapeI64x2, wazeroir.ShapeF64x2: + if op.b2 == 0 { + v = lo + } else { + v = hi + } + } + ce.pushValue(v) + frame.pc++ + case wazeroir.OperationKindV128Splat: + v := ce.popValue() + var hi, lo uint64 + switch op.b1 { + case wazeroir.ShapeI8x16: + v8 := uint64(byte(v))<<56 | uint64(byte(v))<<48 | uint64(byte(v))<<40 | uint64(byte(v))<<32 | + uint64(byte(v))<<24 | uint64(byte(v))<<16 | uint64(byte(v))<<8 | uint64(byte(v)) + hi, lo = v8, v8 + case wazeroir.ShapeI16x8: + v4 := uint64(uint16(v))<<48 | uint64(uint16(v))<<32 | uint64(uint16(v))<<16 | uint64(uint16(v)) + hi, lo = v4, v4 + case wazeroir.ShapeI32x4, wazeroir.ShapeF32x4: + v2 := uint64(uint32(v))<<32 | uint64(uint32(v)) + lo, hi = v2, v2 + case wazeroir.ShapeI64x2, wazeroir.ShapeF64x2: + lo, hi = v, v + } + ce.pushValue(lo) + ce.pushValue(hi) + frame.pc++ + case wazeroir.OperationKindV128Swizzle: + idxHi, idxLo := ce.popValue(), ce.popValue() + baseHi, baseLo := ce.popValue(), ce.popValue() + var newVal [16]byte + for i := 0; i < 16; i++ { + var id byte + if i < 8 { + id = byte(idxLo >> (i * 8)) + } else { + id = byte(idxHi >> ((i - 8) * 8)) + } + if id < 8 { + newVal[i] = byte(baseLo >> (id * 8)) + } else if id < 16 { + newVal[i] = byte(baseHi >> ((id - 8) * 8)) + } + } + ce.pushValue(binary.LittleEndian.Uint64(newVal[:8])) + ce.pushValue(binary.LittleEndian.Uint64(newVal[8:])) + frame.pc++ + case wazeroir.OperationKindV128Shuffle: + xHi, xLo, yHi, yLo := ce.popValue(), ce.popValue(), ce.popValue(), ce.popValue() + var newVal [16]byte + for i, l := range op.us { + if l < 8 { + newVal[i] = byte(yLo >> (l * 8)) + } else if l < 16 { + newVal[i] = byte(yHi >> ((l - 8) * 8)) + } else if l < 24 { + newVal[i] = byte(xLo >> ((l - 16) * 8)) + } else if l < 32 { + newVal[i] = byte(xHi >> ((l - 24) * 8)) + } + } + ce.pushValue(binary.LittleEndian.Uint64(newVal[:8])) + ce.pushValue(binary.LittleEndian.Uint64(newVal[8:])) + frame.pc++ + case wazeroir.OperationKindV128AnyTrue: + hi, lo := ce.popValue(), ce.popValue() + if hi != 0 || lo != 0 { + ce.pushValue(1) + } else { + ce.pushValue(0) + } + frame.pc++ + case wazeroir.OperationKindV128AllTrue: + hi, lo := ce.popValue(), ce.popValue() + var ret = true + switch op.b1 { + case wazeroir.ShapeI8x16: + for i := 0; i < 16; i++ { + if i < 8 { + ret = ret && (byte(lo>>(i*8)) != 0) + } else { + ret = ret && (byte(hi>>((i-8)*8)) != 0) + } + } + case wazeroir.ShapeI16x8: + for i := 0; i < 8; i++ { + if i < 4 { + ret = ret && (uint16(lo>>(i*8)) != 0) + } else { + ret = ret && (uint16(hi>>((i-4)*8)) != 0) + } + } + case wazeroir.ShapeI32x4: + for i := 0; i < 4; i++ { + if i < 2 { + ret = ret && (uint32(lo>>(i*8)) != 0) + } else { + ret = ret && (uint32(hi>>((i-2)*8)) != 0) + } + } + case wazeroir.ShapeI64x2: + ret = ret && (lo != 0) && (hi != 0) + } + if ret { + ce.pushValue(1) + } else { + ce.pushValue(0) + } + frame.pc++ } } ce.popFrame() diff --git a/internal/integration_test/asm/amd64_debug/debug_assembler.go b/internal/integration_test/asm/amd64_debug/debug_assembler.go index e1e8bc1cfa0..6b5c6150837 100644 --- a/internal/integration_test/asm/amd64_debug/debug_assembler.go +++ b/internal/integration_test/asm/amd64_debug/debug_assembler.go @@ -191,10 +191,10 @@ func (ta *testAssembler) CompileReadInstructionAddress( func (ta *testAssembler) CompileRegisterToRegisterWithArg( instruction asm.Instruction, from, to asm.Register, - mode asm_amd64.Mode, + arg byte, ) { - ta.goasm.CompileRegisterToRegisterWithArg(instruction, from, to, mode) - ta.a.CompileRegisterToRegisterWithArg(instruction, from, to, mode) + ta.goasm.CompileRegisterToRegisterWithArg(instruction, from, to, arg) + ta.a.CompileRegisterToRegisterWithArg(instruction, from, to, arg) } // CompileMemoryWithIndexToRegister implements the same method as documented on asm_amd64.Assembler. @@ -210,6 +210,20 @@ func (ta *testAssembler) CompileMemoryWithIndexToRegister( ta.a.CompileMemoryWithIndexToRegister(instruction, srcBaseReg, srcOffsetConst, srcIndex, srcScale, dstReg) } +// CompileMemoryWithIndexAndArgToRegister implements the same method as documented on asm_amd64.Assembler. +func (ta *testAssembler) CompileMemoryWithIndexAndArgToRegister( + instruction asm.Instruction, + srcBaseReg asm.Register, + srcOffsetConst int64, + srcIndex asm.Register, + srcScale int16, + dstReg asm.Register, + arg byte, +) { + ta.goasm.CompileMemoryWithIndexAndArgToRegister(instruction, srcBaseReg, srcOffsetConst, srcIndex, srcScale, dstReg, arg) + ta.a.CompileMemoryWithIndexAndArgToRegister(instruction, srcBaseReg, srcOffsetConst, srcIndex, srcScale, dstReg, arg) +} + // CompileRegisterToMemoryWithIndex implements the same method as documented on asm_amd64.Assembler. func (ta *testAssembler) CompileRegisterToMemoryWithIndex( instruction asm.Instruction, @@ -222,6 +236,19 @@ func (ta *testAssembler) CompileRegisterToMemoryWithIndex( ta.a.CompileRegisterToMemoryWithIndex(instruction, srcReg, dstBaseReg, dstOffsetConst, dstIndex, dstScale) } +// CompileRegisterToMemoryWithIndexAndArg implements the same method as documented on asm_amd64.Assembler. +func (ta *testAssembler) CompileRegisterToMemoryWithIndexAndArg( + instruction asm.Instruction, + srcReg, dstBaseReg asm.Register, + dstOffsetConst int64, + dstIndex asm.Register, + dstScale int16, + arg byte, +) { + ta.goasm.CompileRegisterToMemoryWithIndexAndArg(instruction, srcReg, dstBaseReg, dstOffsetConst, dstIndex, dstScale, arg) + ta.a.CompileRegisterToMemoryWithIndexAndArg(instruction, srcReg, dstBaseReg, dstOffsetConst, dstIndex, dstScale, arg) +} + // CompileRegisterToConst implements the same method as documented on asm_amd64.Assembler. func (ta *testAssembler) CompileRegisterToConst( instruction asm.Instruction, @@ -273,3 +300,8 @@ func (ta *testAssembler) CompileMemoryToConst( ret2 := ta.a.CompileMemoryToConst(instruction, srcBaseReg, srcOffset, value) return &testNode{goasm: ret.(*golang_asm.GolangAsmNode), n: ret2.(*asm_amd64.NodeImpl)} } + +// CompileLoadStaticConstToRegister implements Assembler.CompileLoadStaticConstToRegister. +func (ta *testAssembler) CompileLoadStaticConstToRegister(asm.Instruction, []byte, asm.Register) (err error) { + panic("CompileLoadStaticConstToRegister cannot be supported by golang-asm") +} diff --git a/internal/integration_test/asm/amd64_debug/golang_asm.go b/internal/integration_test/asm/amd64_debug/golang_asm.go index edd3afcfb88..907da382cc2 100644 --- a/internal/integration_test/asm/amd64_debug/golang_asm.go +++ b/internal/integration_test/asm/amd64_debug/golang_asm.go @@ -62,6 +62,34 @@ func (a *assemblerGoAsmImpl) CompileMemoryWithIndexToRegister( a.AddInstruction(p) } +// CompileMemoryWithIndexAndArgToRegister implements the same method as documented on amd64.Assembler. +func (a *assemblerGoAsmImpl) CompileMemoryWithIndexAndArgToRegister( + inst asm.Instruction, + sourceBaseReg asm.Register, + sourceOffsetConst asm.ConstantValue, + sourceIndexReg asm.Register, + sourceScale int16, + destinationReg asm.Register, + arg byte, +) { + p := a.NewProg() + p.As = castAsGolangAsmInstruction[inst] + p.To.Type = obj.TYPE_REG + p.To.Reg = castAsGolangAsmRegister[destinationReg] + p.RestArgs = append(p.RestArgs, + obj.Addr{ + Reg: castAsGolangAsmRegister[sourceBaseReg], + Offset: sourceOffsetConst, + Index: castAsGolangAsmRegister[sourceIndexReg], + Scale: sourceScale, + Type: obj.TYPE_MEM, + }) + + p.From.Type = obj.TYPE_CONST + p.From.Offset = int64(arg) + a.AddInstruction(p) +} + // CompileRegisterToMemoryWithIndex implements the same method as documented on amd64.Assembler. func (a *assemblerGoAsmImpl) CompileRegisterToMemoryWithIndex( inst asm.Instruction, @@ -82,6 +110,30 @@ func (a *assemblerGoAsmImpl) CompileRegisterToMemoryWithIndex( a.AddInstruction(p) } +// CompileRegisterToMemoryWithIndexAndArg implements the same method as documented on amd64.Assembler. +func (a *assemblerGoAsmImpl) CompileRegisterToMemoryWithIndexAndArg( + inst asm.Instruction, + srcReg, dstBaseReg asm.Register, + dstOffsetConst asm.ConstantValue, + dstIndexReg asm.Register, + dstScale int16, + arg byte, +) { + p := a.NewProg() + p.As = castAsGolangAsmInstruction[inst] + p.From.Type = obj.TYPE_CONST + p.From.Offset = int64(arg) + p.RestArgs = append(p.RestArgs, + obj.Addr{Reg: castAsGolangAsmRegister[srcReg], Type: obj.TYPE_REG}) + + p.To.Type = obj.TYPE_MEM + p.To.Reg = castAsGolangAsmRegister[dstBaseReg] + p.To.Offset = dstOffsetConst + p.To.Index = castAsGolangAsmRegister[dstIndexReg] + p.To.Scale = dstScale + a.AddInstruction(p) +} + // CompileRegisterToMemory implements the same method as documented on amd64.Assembler. func (a *assemblerGoAsmImpl) CompileRegisterToMemory( inst asm.Instruction, @@ -261,12 +313,13 @@ func (a *assemblerGoAsmImpl) CompileRegisterToRegisterWithArg( from, to asm.Register, arg byte, ) { + p := a.NewProg() p.As = castAsGolangAsmInstruction[inst] - p.From.Type = obj.TYPE_CONST - p.From.Offset = int64(arg) p.To.Type = obj.TYPE_REG p.To.Reg = castAsGolangAsmRegister[to] + p.From.Type = obj.TYPE_CONST + p.From.Offset = int64(arg) p.RestArgs = append(p.RestArgs, obj.Addr{Reg: castAsGolangAsmRegister[from], Type: obj.TYPE_REG}) a.AddInstruction(p) @@ -328,6 +381,11 @@ func (a *assemblerGoAsmImpl) CompileReadInstructionAddress( }) } +// CompileLoadStaticConstToRegister implements Assembler.CompileLoadStaticConstToRegister. +func (a *assemblerGoAsmImpl) CompileLoadStaticConstToRegister(instruction asm.Instruction, c []byte, dstReg asm.Register) (err error) { + panic("CompileLoadStaticConstToRegister cannot be supported by golangasm") +} + // castAsGolangAsmRegister maps the registers to golang-asm specific register values. var castAsGolangAsmRegister = [...]int16{ amd64.RegAX: x86.REG_AX, @@ -497,6 +555,9 @@ var castAsGolangAsmInstruction = [...]obj.As{ amd64.XORPD: x86.AXORPD, amd64.XORPS: x86.AXORPS, amd64.XORQ: x86.AXORQ, + amd64.PINSRB: x86.APINSRB, + amd64.PINSRW: x86.APINSRW, + amd64.PINSRD: x86.APINSRD, amd64.PINSRQ: x86.APINSRQ, amd64.PADDB: x86.APADDB, amd64.PADDW: x86.APADDW, @@ -504,4 +565,32 @@ var castAsGolangAsmInstruction = [...]obj.As{ amd64.PADDQ: x86.APADDQ, amd64.ADDPS: x86.AADDPS, amd64.ADDPD: x86.AADDPD, + amd64.PSUBB: x86.APSUBB, + amd64.PSUBW: x86.APSUBW, + amd64.PSUBL: x86.APSUBL, + amd64.PSUBQ: x86.APSUBQ, + amd64.SUBPS: x86.ASUBPS, + amd64.SUBPD: x86.ASUBPD, + amd64.PMOVSXBW: x86.APMOVSXBW, + amd64.PMOVSXWD: x86.APMOVSXWD, + amd64.PMOVSXDQ: x86.APMOVSXDQ, + amd64.PMOVZXBW: x86.APMOVZXBW, + amd64.PMOVZXWD: x86.APMOVZXWD, + amd64.PMOVZXDQ: x86.APMOVZXDQ, + amd64.PSHUFB: x86.APSHUFB, + amd64.PSHUFD: x86.APSHUFD, + amd64.PXOR: x86.APXOR, + amd64.PEXTRB: x86.APEXTRB, + amd64.PEXTRW: x86.APEXTRW, + amd64.PEXTRD: x86.APEXTRD, + amd64.PEXTRQ: x86.APEXTRQ, + amd64.MOVLHPS: x86.AMOVLHPS, + amd64.INSERTPS: x86.AINSERTPS, + amd64.PTEST: x86.APTEST, + amd64.PCMPEQB: x86.APCMPEQB, + amd64.PCMPEQW: x86.APCMPEQW, + amd64.PCMPEQD: x86.APCMPEQL, + amd64.PCMPEQQ: x86.APCMPEQQ, + amd64.PADDUSB: x86.APADDUSB, + amd64.MOVSD: x86.AMOVSD, } diff --git a/internal/integration_test/asm/amd64_debug/impl_test.go b/internal/integration_test/asm/amd64_debug/impl_test.go index d4047961dec..c569eddfad5 100644 --- a/internal/integration_test/asm/amd64_debug/impl_test.go +++ b/internal/integration_test/asm/amd64_debug/impl_test.go @@ -816,8 +816,20 @@ func TestAssemblerImpl_EncodeRegisterToRegister(t *testing.T) { {instruction: amd64.PADDQ, srcRegs: floatRegisters, DstRegs: floatRegisters}, {instruction: amd64.ADDPS, srcRegs: floatRegisters, DstRegs: floatRegisters}, {instruction: amd64.ADDPD, srcRegs: floatRegisters, DstRegs: floatRegisters}, + {instruction: amd64.PSUBB, srcRegs: floatRegisters, DstRegs: floatRegisters}, + {instruction: amd64.PSUBW, srcRegs: floatRegisters, DstRegs: floatRegisters}, + {instruction: amd64.PSUBL, srcRegs: floatRegisters, DstRegs: floatRegisters}, + {instruction: amd64.PSUBQ, srcRegs: floatRegisters, DstRegs: floatRegisters}, + {instruction: amd64.SUBPS, srcRegs: floatRegisters, DstRegs: floatRegisters}, + {instruction: amd64.SUBPD, srcRegs: floatRegisters, DstRegs: floatRegisters}, {instruction: amd64.PINSRQ, srcRegs: intRegisters, DstRegs: floatRegisters, arg: 1}, {instruction: amd64.PINSRQ, srcRegs: intRegisters, DstRegs: floatRegisters, arg: 0}, + {instruction: amd64.PINSRD, srcRegs: intRegisters, DstRegs: floatRegisters, arg: 1}, + {instruction: amd64.PINSRD, srcRegs: intRegisters, DstRegs: floatRegisters, arg: 0}, + {instruction: amd64.PINSRW, srcRegs: intRegisters, DstRegs: floatRegisters, arg: 1}, + {instruction: amd64.PINSRW, srcRegs: intRegisters, DstRegs: floatRegisters, arg: 0}, + {instruction: amd64.PINSRB, srcRegs: intRegisters, DstRegs: floatRegisters, arg: 1}, + {instruction: amd64.PINSRB, srcRegs: intRegisters, DstRegs: floatRegisters, arg: 0}, {instruction: amd64.ADDL, srcRegs: intRegisters, DstRegs: intRegisters}, {instruction: amd64.ADDQ, srcRegs: intRegisters, DstRegs: intRegisters}, {instruction: amd64.ADDSD, srcRegs: floatRegisters, DstRegs: floatRegisters}, @@ -852,6 +864,7 @@ func TestAssemblerImpl_EncodeRegisterToRegister(t *testing.T) { {instruction: amd64.MAXSS, srcRegs: floatRegisters, DstRegs: floatRegisters}, {instruction: amd64.MINSS, srcRegs: floatRegisters, DstRegs: floatRegisters}, {instruction: amd64.MOVBLSX, srcRegs: intRegisters, DstRegs: intRegisters}, + {instruction: amd64.MOVWLZX, srcRegs: intRegisters, DstRegs: intRegisters}, {instruction: amd64.MOVBLZX, srcRegs: intRegisters, DstRegs: intRegisters}, {instruction: amd64.MOVBQSX, srcRegs: intRegisters, DstRegs: intRegisters}, {instruction: amd64.MOVLQSX, srcRegs: intRegisters, DstRegs: intRegisters}, @@ -905,6 +918,24 @@ func TestAssemblerImpl_EncodeRegisterToRegister(t *testing.T) { {instruction: amd64.XORPD, srcRegs: floatRegisters, DstRegs: floatRegisters}, {instruction: amd64.XORPS, srcRegs: floatRegisters, DstRegs: floatRegisters}, {instruction: amd64.XORQ, srcRegs: intRegisters, DstRegs: intRegisters}, + {instruction: amd64.PXOR, srcRegs: floatRegisters, DstRegs: floatRegisters}, + {instruction: amd64.PSHUFB, srcRegs: floatRegisters, DstRegs: floatRegisters}, + {instruction: amd64.PSHUFD, srcRegs: floatRegisters, DstRegs: floatRegisters, arg: 0}, + {instruction: amd64.PSHUFD, srcRegs: floatRegisters, DstRegs: floatRegisters, arg: 1}, + {instruction: amd64.PEXTRB, srcRegs: floatRegisters, DstRegs: intRegisters, arg: 0}, + {instruction: amd64.PEXTRW, srcRegs: floatRegisters, DstRegs: intRegisters, arg: 1}, + {instruction: amd64.PEXTRD, srcRegs: floatRegisters, DstRegs: intRegisters, arg: 1}, + {instruction: amd64.PEXTRQ, srcRegs: floatRegisters, DstRegs: intRegisters, arg: 1}, + {instruction: amd64.MOVLHPS, srcRegs: floatRegisters, DstRegs: floatRegisters}, + {instruction: amd64.INSERTPS, srcRegs: floatRegisters, DstRegs: floatRegisters, arg: 0}, + {instruction: amd64.INSERTPS, srcRegs: floatRegisters, DstRegs: floatRegisters, arg: 1}, + {instruction: amd64.PTEST, srcRegs: floatRegisters, DstRegs: floatRegisters}, + {instruction: amd64.PCMPEQB, srcRegs: floatRegisters, DstRegs: floatRegisters}, + {instruction: amd64.PCMPEQW, srcRegs: floatRegisters, DstRegs: floatRegisters}, + {instruction: amd64.PCMPEQD, srcRegs: floatRegisters, DstRegs: floatRegisters}, + {instruction: amd64.PCMPEQQ, srcRegs: floatRegisters, DstRegs: floatRegisters}, + {instruction: amd64.PADDUSB, srcRegs: floatRegisters, DstRegs: floatRegisters}, + {instruction: amd64.MOVSD, srcRegs: floatRegisters, DstRegs: floatRegisters}, } for _, tt := range tests { @@ -961,11 +992,16 @@ func TestAssemblerImpl_EncodeRegisterToRegister(t *testing.T) { // TODO: remove golang-asm dependency in tests. goasm, err := newGolangAsmAssembler() require.NoError(t, err) - if tc.instruction == amd64.ROUNDSD || tc.instruction == amd64.ROUNDSS || tc.instruction == amd64.PINSRQ { + + switch tc.instruction { + case amd64.ROUNDSD, amd64.ROUNDSS, amd64.PINSRQ, amd64.PINSRD, amd64.PINSRW, + amd64.PINSRB, amd64.PSHUFD, amd64.PEXTRB, amd64.PEXTRW, amd64.PEXTRD, amd64.PEXTRQ, + amd64.INSERTPS: goasm.CompileRegisterToRegisterWithArg(tc.instruction, srcReg, DstReg, tc.arg) - } else { + default: goasm.CompileRegisterToRegister(tc.instruction, srcReg, DstReg) } + bs, err := goasm.Assemble() require.NoError(t, err) @@ -975,7 +1011,6 @@ func TestAssemblerImpl_EncodeRegisterToRegister(t *testing.T) { Arg: tc.arg, }) require.NoError(t, err) - // fmt.Printf("modRM: want: 0b%b, got: 0b%b\n", bs[1], a.Buf.Bytes()[1]) require.Equal(t, bs, a.Buf.Bytes()) }) } @@ -985,6 +1020,114 @@ func TestAssemblerImpl_EncodeRegisterToRegister(t *testing.T) { } } +func TestAssemblerImpl_CompileMemoryWithIndexAndArgToRegister(t *testing.T) { + tests := []struct { + name string + instruction asm.Instruction + }{ + {name: "PINSRB", instruction: amd64.PINSRB}, + {name: "PINSRW", instruction: amd64.PINSRW}, + {name: "PINSRD", instruction: amd64.PINSRD}, + {name: "PINSRQ", instruction: amd64.PINSRQ}, + } + + dstRegs := []asm.Register{amd64.RegX0, amd64.RegX13} + indexRegs := []asm.Register{amd64.RegR12, amd64.RegAX, amd64.RegBP, amd64.RegSI} + offsets := []int64{0, 1, math.MaxInt32} + args := []byte{0, 1} + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + + for _, dstReg := range dstRegs { + for _, indexReg := range indexRegs { + for _, offset := range offsets { + for _, arg := range args { + dstReg, indexReg, offset, arg := dstReg, indexReg, offset, arg + t.Run(fmt.Sprintf("dst=%s,index=%s,offset=%d,arg=%d", + amd64.RegisterName(dstReg), amd64.RegisterName(indexReg), offset, arg), func(t *testing.T) { + + goasm, err := newGolangAsmAssembler() + require.NoError(t, err) + + a := amd64.NewAssemblerImpl() + for _, assembler := range []amd64.Assembler{goasm, a} { + assembler.CompileMemoryWithIndexAndArgToRegister(tc.instruction, + amd64.RegAX, offset, amd64.RegCX, 1, dstReg, arg) + } + + actual, err := a.Assemble() + require.NoError(t, err) + expected, err := goasm.Assemble() + require.NoError(t, err) + + require.Equal(t, expected, actual) + }) + } + + } + } + } + + }) + } +} + +func TestAssemblerImpl_CompileRegisterToMemoryWithIndexAndArg(t *testing.T) { + tests := []struct { + name string + instruction asm.Instruction + }{ + {name: "PEXTRB", instruction: amd64.PEXTRB}, + {name: "PEXTRW", instruction: amd64.PEXTRW}, + {name: "PEXTRD", instruction: amd64.PEXTRD}, + {name: "PEXTRQ", instruction: amd64.PEXTRQ}, + } + + srcRegs := []asm.Register{amd64.RegX0, amd64.RegX13} + indexRegs := []asm.Register{amd64.RegR12, amd64.RegAX, amd64.RegBP, amd64.RegSI} + offsets := []int64{0, 1, math.MaxInt32} + args := []byte{0, 1} + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + + for _, srcReg := range srcRegs { + for _, indexReg := range indexRegs { + for _, offset := range offsets { + for _, arg := range args { + srcReg, indexReg, offset, arg := srcReg, indexReg, offset, arg + t.Run(fmt.Sprintf("src=%s,index=%s,offset=%d,arg=%d", + amd64.RegisterName(srcReg), amd64.RegisterName(indexReg), offset, arg), func(t *testing.T) { + + goasm, err := newGolangAsmAssembler() + require.NoError(t, err) + + a := amd64.NewAssemblerImpl() + for _, assembler := range []amd64.Assembler{goasm, a} { + assembler.CompileRegisterToMemoryWithIndexAndArg(tc.instruction, srcReg, + amd64.RegCX, offset, indexReg, 1, arg) + } + + actual, err := a.Assemble() + require.NoError(t, err) + expected, err := goasm.Assemble() + require.NoError(t, err) + + require.Equal(t, expected, actual) + }) + } + + } + } + } + + }) + } +} + func TestAssemblerImpl_EncodeRegisterToMemory(t *testing.T) { t.Run("error", func(t *testing.T) { tests := []struct { @@ -1256,7 +1399,7 @@ func TestAssemblerImpl_EncodeMemoryToRegister(t *testing.T) { a := amd64.NewAssemblerImpl() require.EqualError(t, a.EncodeMemoryToRegister(n), "JMP is unsupported for from:memory,to:register type") }) - intRegs := []asm.Register{amd64.RegAX, amd64.RegBP, amd64.RegSI, amd64.RegDI, amd64.RegR10} + intRegs := []asm.Register{amd64.RegAX, amd64.RegBP, amd64.RegDI, amd64.RegR10} floatRegs := []asm.Register{amd64.RegX0, amd64.RegX8} scales := []byte{1, 4} tests := []struct { @@ -1285,6 +1428,12 @@ func TestAssemblerImpl_EncodeMemoryToRegister(t *testing.T) { {instruction: amd64.SUBSS, isFloatInst: true}, {instruction: amd64.UCOMISD, isFloatInst: true}, {instruction: amd64.UCOMISS, isFloatInst: true}, + {instruction: amd64.PMOVSXBW, isFloatInst: true}, + {instruction: amd64.PMOVSXWD, isFloatInst: true}, + {instruction: amd64.PMOVSXDQ, isFloatInst: true}, + {instruction: amd64.PMOVZXBW, isFloatInst: true}, + {instruction: amd64.PMOVZXWD, isFloatInst: true}, + {instruction: amd64.PMOVZXDQ, isFloatInst: true}, } for _, tt := range tests { diff --git a/internal/integration_test/asm/arm64_debug/debug_assembler.go b/internal/integration_test/asm/arm64_debug/debug_assembler.go index bb408a6bd2c..72e411f0d1a 100644 --- a/internal/integration_test/asm/arm64_debug/debug_assembler.go +++ b/internal/integration_test/asm/arm64_debug/debug_assembler.go @@ -255,16 +255,18 @@ func (ta *testAssembler) CompileConditionalRegisterSet(cond asm.ConditionalRegis ta.a.CompileConditionalRegisterSet(cond, dstReg) } -func (ta *testAssembler) CompileMemoryToVectorRegister(instruction asm.Instruction, srcOffsetReg, dstReg asm.Register, +func (ta *testAssembler) CompileMemoryToVectorRegister(instruction asm.Instruction, srcOffsetReg asm.Register, + c asm.ConstantValue, dstReg asm.Register, arrangement arm64.VectorArrangement) { - ta.goasm.CompileMemoryToVectorRegister(instruction, srcOffsetReg, dstReg, arrangement) - ta.a.CompileMemoryToVectorRegister(instruction, srcOffsetReg, dstReg, arrangement) + ta.goasm.CompileMemoryToVectorRegister(instruction, srcOffsetReg, c, dstReg, arrangement) + ta.a.CompileMemoryToVectorRegister(instruction, srcOffsetReg, c, dstReg, arrangement) } func (ta *testAssembler) CompileVectorRegisterToMemory(instruction asm.Instruction, srcReg, dstOffsetReg asm.Register, + c asm.ConstantValue, arrangement arm64.VectorArrangement) { - ta.goasm.CompileVectorRegisterToMemory(instruction, srcReg, dstOffsetReg, arrangement) - ta.a.CompileVectorRegisterToMemory(instruction, srcReg, dstOffsetReg, arrangement) + ta.goasm.CompileVectorRegisterToMemory(instruction, srcReg, dstOffsetReg, c, arrangement) + ta.a.CompileVectorRegisterToMemory(instruction, srcReg, dstOffsetReg, c, arrangement) } func (ta *testAssembler) CompileRegisterToVectorRegister(instruction asm.Instruction, srcReg, dstReg asm.Register, @@ -274,7 +276,36 @@ func (ta *testAssembler) CompileRegisterToVectorRegister(instruction asm.Instruc } func (ta *testAssembler) CompileVectorRegisterToVectorRegister(instruction asm.Instruction, srcReg, dstReg asm.Register, - arrangement arm64.VectorArrangement) { - ta.goasm.CompileVectorRegisterToVectorRegister(instruction, srcReg, dstReg, arrangement) - ta.a.CompileVectorRegisterToVectorRegister(instruction, srcReg, dstReg, arrangement) + arrangement arm64.VectorArrangement, srcIndex, dstIndex arm64.VectorIndex) { + ta.goasm.CompileVectorRegisterToVectorRegister(instruction, srcReg, dstReg, arrangement, srcIndex, dstIndex) + ta.a.CompileVectorRegisterToVectorRegister(instruction, srcReg, dstReg, arrangement, srcIndex, dstIndex) +} + +func (ta *testAssembler) CompileVectorRegisterToVectorRegisterWithConst(instruction asm.Instruction, srcReg, dstReg asm.Register, + arrangement arm64.VectorArrangement, c asm.ConstantValue) { + ta.goasm.CompileVectorRegisterToVectorRegisterWithConst(instruction, srcReg, dstReg, arrangement, c) + ta.a.CompileVectorRegisterToVectorRegisterWithConst(instruction, srcReg, dstReg, arrangement, c) +} + +func (ta *testAssembler) CompileMemoryWithRegisterOffsetToVectorRegister(instruction asm.Instruction, srcBaseReg, srcOffsetRegister asm.Register, dstReg asm.Register, arrangement arm64.VectorArrangement) { + ta.goasm.CompileMemoryWithRegisterOffsetToVectorRegister(instruction, srcBaseReg, srcOffsetRegister, dstReg, arrangement) + ta.a.CompileMemoryWithRegisterOffsetToVectorRegister(instruction, srcBaseReg, srcOffsetRegister, dstReg, arrangement) +} + +func (ta *testAssembler) CompileVectorRegisterToMemoryWithRegisterOffset(instruction asm.Instruction, srcReg, dstBaseReg, dstOffsetRegister asm.Register, arrangement arm64.VectorArrangement) { + ta.goasm.CompileVectorRegisterToMemoryWithRegisterOffset(instruction, srcReg, dstBaseReg, dstOffsetRegister, arrangement) + ta.a.CompileVectorRegisterToMemoryWithRegisterOffset(instruction, srcReg, dstBaseReg, dstOffsetRegister, arrangement) +} + +func (ta *testAssembler) CompileVectorRegisterToRegister(instruction asm.Instruction, srcReg, dstReg asm.Register, arrangement arm64.VectorArrangement, index arm64.VectorIndex) { + ta.goasm.CompileVectorRegisterToRegister(instruction, srcReg, dstReg, arrangement, index) + ta.a.CompileVectorRegisterToRegister(instruction, srcReg, dstReg, arrangement, index) +} + +// CompileLoadStaticConstToVectorRegister adds an instruction where the source operand is StaticConstant located in the memory +// and the destination is the dstReg. +func (ta *testAssembler) CompileLoadStaticConstToVectorRegister(instruction asm.Instruction, + c asm.StaticConst, dstReg asm.Register, arrangement arm64.VectorArrangement) { + ta.goasm.CompileLoadStaticConstToVectorRegister(instruction, c, dstReg, arrangement) + ta.a.CompileLoadStaticConstToVectorRegister(instruction, c, dstReg, arrangement) } diff --git a/internal/integration_test/asm/arm64_debug/golang_asm.go b/internal/integration_test/asm/arm64_debug/golang_asm.go index 6ee9fd18f4a..34d44669de1 100644 --- a/internal/integration_test/asm/arm64_debug/golang_asm.go +++ b/internal/integration_test/asm/arm64_debug/golang_asm.go @@ -332,63 +332,30 @@ func (a *assemblerGoAsmImpl) CompileSIMDByteToRegister(instruction asm.Instructi a.AddInstruction(inst) } -func createOffsetForVectorRegList(arr asm_arm64.VectorArrangement, reg asm.Register) (offset int64) { - // https://github.com/golang/go/blob/19309779ac5e2f5a2fd3cbb34421dafb2855ac21/src/cmd/asm/internal/arch/arm64.go#L372 - // https://github.com/golang/go/blob/19309779ac5e2f5a2fd3cbb34421dafb2855ac21/src/cmd/asm/internal/arch/arm64.go#L332 - // https://github.com/golang/go/blob/19309779ac5e2f5a2fd3cbb34421dafb2855ac21/src/cmd/asm/internal/asm/parse.go#L1143-L1148 - var curQ, curSize int64 - switch arr { - case asm_arm64.VectorArrangement8B: - curSize = 0 - curQ = 0 - case asm_arm64.VectorArrangement16B: - curSize = 0 - curQ = 1 - case asm_arm64.VectorArrangement4H: - curSize = 1 - curQ = 0 - case asm_arm64.VectorArrangement8H: - curSize = 1 - curQ = 1 - case asm_arm64.VectorArrangement2S: - curSize = 2 - curQ = 0 - case asm_arm64.VectorArrangement4S: - curSize = 2 - curQ = 1 - case asm_arm64.VectorArrangement1D: - curSize = 3 - curQ = 0 - case asm_arm64.VectorArrangement2D: - curSize = 3 - curQ = 1 - } - return (int64(curQ) & 1 << 30) | ((curSize & 3) << 10) | (0x7 << 12) | 1<<60 | int64(castAsGolangAsVectorRegister[reg]&31) -} - +// CompileMemoryToVectorRegister implements the same method as documented on asm_arm64.Assembler. func (a *assemblerGoAsmImpl) CompileMemoryToVectorRegister( - instruction asm.Instruction, srcOffsetReg, dstReg asm.Register, arrangement asm_arm64.VectorArrangement, + _ asm.Instruction, _ asm.Register, _ asm.ConstantValue, _ asm.Register, _ asm_arm64.VectorArrangement, ) { - inst := a.NewProg() - inst.As = castAsGolangAsmInstruction[instruction] - inst.To.Type = obj.TYPE_REGLIST - inst.To.Offset = createOffsetForVectorRegList(arrangement, dstReg) - inst.From.Type = obj.TYPE_MEM - inst.From.Reg = castAsGolangAsmRegister[srcOffsetReg] - a.AddInstruction(inst) + panic("CompileMemoryToVectorRegister is unsupported with golang-asm") } -func (a *assemblerGoAsmImpl) CompileVectorRegisterToMemory(instruction asm.Instruction, srcReg, dstOffsetReg asm.Register, - arrangement asm_arm64.VectorArrangement) { - inst := a.NewProg() - inst.As = castAsGolangAsmInstruction[instruction] - inst.To.Type = obj.TYPE_MEM - inst.To.Reg = castAsGolangAsmRegister[dstOffsetReg] - inst.From.Type = obj.TYPE_REGLIST - inst.From.Offset = createOffsetForVectorRegList(arrangement, srcReg) - a.AddInstruction(inst) +// CompileVectorRegisterToMemory implements the same method as documented on asm_arm64.Assembler. +func (a *assemblerGoAsmImpl) CompileVectorRegisterToMemory(_ asm.Instruction, _, _ asm.Register, _ asm.ConstantValue, + _ asm_arm64.VectorArrangement) { + panic("CompileVectorRegisterToMemory is unsupported with golang-asm") +} + +// CompileMemoryWithRegisterOffsetToVectorRegister implements the same method as documented on asm_arm64.Assembler. +func (a *assemblerGoAsmImpl) CompileMemoryWithRegisterOffsetToVectorRegister(_ asm.Instruction, _, _ asm.Register, _ asm.Register, _ asm_arm64.VectorArrangement) { + panic("CompileMemoryWithRegisterOffsetToVectorRegister is unsupported with golang-asm") +} + +// CompileVectorRegisterToMemoryWithRegisterOffset implements the same method as documented on asm_arm64.Assembler. +func (a *assemblerGoAsmImpl) CompileVectorRegisterToMemoryWithRegisterOffset(_ asm.Instruction, _, _, _ asm.Register, _ asm_arm64.VectorArrangement) { + panic("CompileVectorRegisterToMemoryWithRegisterOffset is unsupported with golang-asm") } +// CompileRegisterToVectorRegister implements the same method as documented on asm_arm64.Assembler. func (a *assemblerGoAsmImpl) CompileRegisterToVectorRegister(instruction asm.Instruction, srcReg, dstReg asm.Register, arrangement asm_arm64.VectorArrangement, index asm_arm64.VectorIndex) { inst := a.NewProg() @@ -401,7 +368,9 @@ func (a *assemblerGoAsmImpl) CompileRegisterToVectorRegister(instruction asm.Ins inst.From.Reg = castAsGolangAsmRegister[srcReg] a.AddInstruction(inst) } -func (a *assemblerGoAsmImpl) CompileVectorRegisterToVectorRegister(instruction asm.Instruction, srcReg, dstReg asm.Register, arrangement asm_arm64.VectorArrangement) { + +// CompileVectorRegisterToVectorRegister implements the same method as documented on asm_arm64.Assembler. +func (a *assemblerGoAsmImpl) CompileVectorRegisterToVectorRegister(instruction asm.Instruction, srcReg, dstReg asm.Register, arrangement asm_arm64.VectorArrangement, srcIndex, dstIndex asm_arm64.VectorIndex) { inst := a.NewProg() inst.As = castAsGolangAsmInstruction[instruction] @@ -414,20 +383,65 @@ func (a *assemblerGoAsmImpl) CompileVectorRegisterToVectorRegister(instruction a inst.From.Type = obj.TYPE_REG inst.From.Reg = castAsGolangAsVectorRegister[srcReg]&31 + arm64.REG_ARNG + (castAsGolangAsmArrangement[arrangement]&15)<<5 a.AddInstruction(inst) - case asm_arm64.VADD: + case asm_arm64.VADD, asm_arm64.VSUB: inst.To.Type = obj.TYPE_REG inst.To.Reg = castAsGolangAsVectorRegister[dstReg]&31 + arm64.REG_ARNG + (castAsGolangAsmArrangement[arrangement]&15)<<5 - inst.Reg = castAsGolangAsVectorRegister[srcReg]&31 + arm64.REG_ARNG + (castAsGolangAsmArrangement[arrangement]&15)<<5 + inst.Reg = castAsGolangAsVectorRegister[dstReg]&31 + arm64.REG_ARNG + (castAsGolangAsmArrangement[arrangement]&15)<<5 inst.From.Type = obj.TYPE_REG - inst.From.Reg = castAsGolangAsVectorRegister[dstReg]&31 + arm64.REG_ARNG + (castAsGolangAsmArrangement[arrangement]&15)<<5 + inst.From.Reg = castAsGolangAsVectorRegister[srcReg]&31 + arm64.REG_ARNG + (castAsGolangAsmArrangement[arrangement]&15)<<5 a.AddInstruction(inst) - case asm_arm64.VFADDD: - panic("Unsupported in golang-asm") - case asm_arm64.VFADDS: + default: panic("Unsupported in golang-asm") } } +// CompileVectorRegisterToVectorRegisterWithConst implements the same method as documented on asm_arm64.Assembler. +func (a *assemblerGoAsmImpl) CompileVectorRegisterToVectorRegisterWithConst(instruction asm.Instruction, srcReg, + dstReg asm.Register, arrangement asm_arm64.VectorArrangement, c asm.ConstantValue) { + switch instruction { + case asm_arm64.USHLL: + var dstArrangement asm_arm64.VectorArrangement + if arrangement == asm_arm64.VectorArrangement8B { + dstArrangement = asm_arm64.VectorArrangement8H + } else if arrangement == asm_arm64.VectorArrangement4H { + dstArrangement = asm_arm64.VectorArrangement4S + } else if arrangement == asm_arm64.VectorArrangement2S { + dstArrangement = asm_arm64.VectorArrangement2D + } + inst := a.NewProg() + inst.As = castAsGolangAsmInstruction[instruction] + inst.To.Type = obj.TYPE_REG + inst.To.Reg = castAsGolangAsVectorRegister[dstReg]&31 + arm64.REG_ARNG + (castAsGolangAsmArrangement[dstArrangement]&15)<<5 + inst.Reg = castAsGolangAsVectorRegister[srcReg]&31 + arm64.REG_ARNG + (castAsGolangAsmArrangement[arrangement]&15)<<5 + inst.From.Type = obj.TYPE_CONST + inst.From.Offset = c + a.AddInstruction(inst) + default: + panic("unsupported in golang-asm") + } +} + +// CompileVectorRegisterToRegister implements the same method as documented on asm_arm64.Assembler. +func (a *assemblerGoAsmImpl) CompileVectorRegisterToRegister(instruction asm.Instruction, srcReg, dstReg asm.Register, + arrangement asm_arm64.VectorArrangement, index asm_arm64.VectorIndex) { + inst := a.NewProg() + inst.To.Type = obj.TYPE_REG + inst.To.Reg = castAsGolangAsmRegister[dstReg] + inst.As = castAsGolangAsmInstruction[instruction] + inst.From.Type = obj.TYPE_REG + inst.From.Reg = (castAsGolangAsVectorRegister[srcReg] & 31) + arm64.REG_ELEM + + (castAsGolangAsmArrangement[arrangement]&15)<<5 + inst.From.Index = int16(index) + a.AddInstruction(inst) +} + +// CompileLoadStaticConstToVectorRegister adds an instruction where the source operand is StaticConstant located in the memory +// and the destination is the dstReg. +func (a *assemblerGoAsmImpl) CompileLoadStaticConstToVectorRegister(asm.Instruction, + asm.StaticConst, asm.Register, asm_arm64.VectorArrangement) { + panic("unsupported in golang-asm") +} + var castAsGolangAsmArrangement = [...]int16{ asm_arm64.VectorArrangement1D: arm64.ARNG_1D, asm_arm64.VectorArrangement2D: arm64.ARNG_2D, @@ -689,7 +703,7 @@ var castAsGolangAsmInstruction = [...]obj.As{ asm_arm64.VCNT: arm64.AVCNT, asm_arm64.VUADDLV: arm64.AVUADDLV, asm_arm64.VMOV: arm64.AVMOV, - asm_arm64.VLD1: arm64.AVLD1, - asm_arm64.VST1: arm64.AVST1, asm_arm64.VADD: arm64.AVADD, + asm_arm64.VSUB: arm64.AVSUB, + asm_arm64.USHLL: arm64.AVUSHLL, } diff --git a/internal/integration_test/asm/arm64_debug/impl_test.go b/internal/integration_test/asm/arm64_debug/impl_test.go index e63c47ddcca..9e883a2d2de 100644 --- a/internal/integration_test/asm/arm64_debug/impl_test.go +++ b/internal/integration_test/asm/arm64_debug/impl_test.go @@ -244,7 +244,7 @@ func TestAssemblerImpl_EncodeTwoRegistersToNone(t *testing.T) { { n: &arm64.NodeImpl{Instruction: arm64.FCMPS, SrcReg: arm64.RegR0, SrcReg2: arm64.RegV0}, - expErr: "R0 is not float", + expErr: "R0 is not vector", }, } @@ -349,7 +349,10 @@ func TestAssemblerImpl_EncodeRegisterToRegister(t *testing.T) { intRegs := []asm.Register{arm64.RegRZR, arm64.RegR1, arm64.RegR10, arm64.RegR30} intRegsWithoutZero := intRegs[1:] - conditionalRegs := []asm.Register{arm64.RegCondEQ, arm64.RegCondNE, arm64.RegCondHS, arm64.RegCondLO, arm64.RegCondMI, arm64.RegCondPL, arm64.RegCondVS, arm64.RegCondVC, arm64.RegCondHI, arm64.RegCondLS, arm64.RegCondGE, arm64.RegCondLT, arm64.RegCondGT, arm64.RegCondLE, arm64.RegCondAL, arm64.RegCondNV} + conditionalRegs := []asm.Register{arm64.RegCondEQ, arm64.RegCondNE, + arm64.RegCondHS, arm64.RegCondLO, arm64.RegCondMI, arm64.RegCondPL, arm64.RegCondVS, arm64.RegCondVC, + arm64.RegCondHI, arm64.RegCondLS, arm64.RegCondGE, arm64.RegCondLT, arm64.RegCondGT, arm64.RegCondLE, + arm64.RegCondAL, arm64.RegCondNV} floatRegs := []asm.Register{arm64.RegV0, arm64.RegV15, arm64.RegV31} tests := []struct { @@ -877,6 +880,7 @@ func TestAssemblerImpl_EncodeRegisterToMemory(t *testing.T) { -1, 0, 1, 2, -2, 4, -4, 0xf, -0xf, 1 << 4, 1<<4 - 1, 1<<4 + 1, -128, -256, 8 * 10, -128, 255, 4096, 4096 << 1, 32760, 32760 * 2, 32760*2 - 8, 32760*2 - 16, 1 << 27, 1 << 30, 1<<30 + 8, 1<<30 - 8, 1<<30 + 16, 1<<30 - 16, 1<<31 - 8, + (1 << 28) + 4, } intRegs := []asm.Register{ arm64.RegR0, arm64.RegR16, @@ -975,6 +979,7 @@ func TestAssemblerImpl_EncodeMemoryToRegister(t *testing.T) { -1, 0, 1, 2, -2, 0xf, -0xf, 1 << 4, 1<<4 - 1, 1<<4 + 1, -128, -256, 8 * 10, -128, 255, 4096, 4096 << 1, 32760, 32760 * 2, 32760*2 - 8, 32760*2 - 16, 1 << 27, 1 << 30, 1<<30 + 8, 1<<30 - 8, 1<<30 + 16, 1<<30 - 16, 1<<31 - 8, + (1 << 28) + 4, 1<<12<<8 + 8, 1<<12<<8 - 8, } @@ -1279,27 +1284,33 @@ func TestAssemblerImpl_EncodeVectorRegisterToVectorRegister(t *testing.T) { }{ { n: &arm64.NodeImpl{Instruction: arm64.B, - SrcReg: arm64.RegV21, - DstReg: arm64.RegV21, - Types: arm64.OperandTypesVectorRegisterToVectorRegister, + SrcReg: arm64.RegV21, + DstReg: arm64.RegV21, + Types: arm64.OperandTypesVectorRegisterToVectorRegister, + SrcVectorIndex: arm64.VectorIndexNone, + DstVectorIndex: arm64.VectorIndexNone, }, expErr: "B is unsupported for from:vector-register,to:vector-register type", }, { n: &arm64.NodeImpl{Instruction: arm64.VMOV, - SrcReg: arm64.RegV21, - DstReg: arm64.RegV21, - Types: arm64.OperandTypesVectorRegisterToVectorRegister, + SrcReg: arm64.RegV21, + DstReg: arm64.RegV21, + Types: arm64.OperandTypesVectorRegisterToVectorRegister, + SrcVectorIndex: arm64.VectorIndexNone, + DstVectorIndex: arm64.VectorIndexNone, }, - expErr: "unsupported arrangement for VMOV: unknown", + expErr: "unsupported arrangement for VMOV: none", }, { n: &arm64.NodeImpl{Instruction: arm64.VADD, - SrcReg: arm64.RegV21, - DstReg: arm64.RegV21, - Types: arm64.OperandTypesVectorRegisterToVectorRegister, + SrcReg: arm64.RegV21, + DstReg: arm64.RegV21, + Types: arm64.OperandTypesVectorRegisterToVectorRegister, + SrcVectorIndex: arm64.VectorIndexNone, + DstVectorIndex: arm64.VectorIndexNone, }, - expErr: "unsupported arrangement for VADD: unknown", + expErr: "unsupported arrangement for VADD: none", }, { n: &arm64.NodeImpl{Instruction: arm64.VADD, @@ -1307,6 +1318,8 @@ func TestAssemblerImpl_EncodeVectorRegisterToVectorRegister(t *testing.T) { DstReg: arm64.RegV21, Types: arm64.OperandTypesVectorRegisterToVectorRegister, VectorArrangement: arm64.VectorArrangement1D, + SrcVectorIndex: arm64.VectorIndexNone, + DstVectorIndex: arm64.VectorIndexNone, }, expErr: "unsupported arrangement for VADD: 1D", }, @@ -1314,27 +1327,55 @@ func TestAssemblerImpl_EncodeVectorRegisterToVectorRegister(t *testing.T) { for _, tt := range tests { tc := tt - a := arm64.NewAssemblerImpl(asm.NilRegister) - err := a.EncodeVectorRegisterToVectorRegister(tc.n) - require.EqualError(t, err, tc.expErr) + t.Run(tc.expErr, func(t *testing.T) { + a := arm64.NewAssemblerImpl(asm.NilRegister) + err := a.EncodeVectorRegisterToVectorRegister(tc.n) + require.EqualError(t, err, tc.expErr) + }) } }) vectorRegs := []asm.Register{arm64.RegV10, arm64.RegV2, arm64.RegV30} tests := []struct { - inst asm.Instruction - arr arm64.VectorArrangement + name string + inst asm.Instruction + arr arm64.VectorArrangement + needConst bool + c asm.ConstantValue + srcIndex, dstIndex arm64.VectorIndex }{ - {inst: arm64.VMOV, arr: arm64.VectorArrangement16B}, - {inst: arm64.VADD, arr: arm64.VectorArrangement2D}, - {inst: arm64.VADD, arr: arm64.VectorArrangement4S}, - {inst: arm64.VADD, arr: arm64.VectorArrangement8H}, - {inst: arm64.VADD, arr: arm64.VectorArrangement16B}, + {inst: arm64.VMOV, arr: arm64.VectorArrangement16B, srcIndex: arm64.VectorIndexNone, dstIndex: arm64.VectorIndexNone}, + {inst: arm64.VADD, arr: arm64.VectorArrangement2D, srcIndex: arm64.VectorIndexNone, dstIndex: arm64.VectorIndexNone}, + {inst: arm64.VADD, arr: arm64.VectorArrangement4S, srcIndex: arm64.VectorIndexNone, dstIndex: arm64.VectorIndexNone}, + {inst: arm64.VADD, arr: arm64.VectorArrangement8H, srcIndex: arm64.VectorIndexNone, dstIndex: arm64.VectorIndexNone}, + {inst: arm64.VADD, arr: arm64.VectorArrangement16B, srcIndex: arm64.VectorIndexNone, dstIndex: arm64.VectorIndexNone}, + { + name: "VSUB 2d", + inst: arm64.VSUB, arr: arm64.VectorArrangement2D, srcIndex: arm64.VectorIndexNone, dstIndex: arm64.VectorIndexNone, + }, + { + name: "VSUB 4s", + inst: arm64.VSUB, arr: arm64.VectorArrangement4S, srcIndex: arm64.VectorIndexNone, dstIndex: arm64.VectorIndexNone, + }, + { + name: "VSUB 8h", + inst: arm64.VSUB, arr: arm64.VectorArrangement8H, srcIndex: arm64.VectorIndexNone, dstIndex: arm64.VectorIndexNone, + }, + { + name: "VSUB 16b", + inst: arm64.VSUB, arr: arm64.VectorArrangement16B, srcIndex: arm64.VectorIndexNone, dstIndex: arm64.VectorIndexNone, + }, + {inst: arm64.USHLL, arr: arm64.VectorArrangement8B, needConst: true, srcIndex: arm64.VectorIndexNone, dstIndex: arm64.VectorIndexNone}, + {inst: arm64.USHLL, arr: arm64.VectorArrangement4H, needConst: true, srcIndex: arm64.VectorIndexNone, dstIndex: arm64.VectorIndexNone}, + {inst: arm64.USHLL, arr: arm64.VectorArrangement2S, needConst: true, srcIndex: arm64.VectorIndexNone, dstIndex: arm64.VectorIndexNone}, + {inst: arm64.USHLL, arr: arm64.VectorArrangement8B, needConst: true, c: 7, srcIndex: arm64.VectorIndexNone, dstIndex: arm64.VectorIndexNone}, + {inst: arm64.USHLL, arr: arm64.VectorArrangement4H, needConst: true, c: 15, srcIndex: arm64.VectorIndexNone, dstIndex: arm64.VectorIndexNone}, + {inst: arm64.USHLL, arr: arm64.VectorArrangement2S, needConst: true, c: 31, srcIndex: arm64.VectorIndexNone, dstIndex: arm64.VectorIndexNone}, } for _, tt := range tests { tc := tt - t.Run(fmt.Sprintf("%s.%s", arm64.InstructionName(tc.inst), tc.arr), func(t *testing.T) { + t.Run(tc.name, func(t *testing.T) { for _, src := range vectorRegs { for _, dst := range vectorRegs { src, dst := src, dst @@ -1344,12 +1385,18 @@ func TestAssemblerImpl_EncodeVectorRegisterToVectorRegister(t *testing.T) { a := arm64.NewAssemblerImpl(asm.NilRegister) for _, assembler := range []arm64.Assembler{goasm, a} { - assembler.CompileVectorRegisterToVectorRegister(tc.inst, src, dst, tc.arr) + if tc.needConst { + assembler.CompileVectorRegisterToVectorRegisterWithConst(tc.inst, src, dst, tc.arr, tc.c) + } else { + assembler.CompileVectorRegisterToVectorRegister(tc.inst, src, dst, tc.arr, tc.srcIndex, tc.dstIndex) + } } expected, err := goasm.Assemble() require.NoError(t, err) + fmt.Println(hex.EncodeToString(expected)) + actual, err := a.Assemble() require.NoError(t, err) require.Equal(t, expected, actual, hex.EncodeToString(expected)) @@ -1360,180 +1407,146 @@ func TestAssemblerImpl_EncodeVectorRegisterToVectorRegister(t *testing.T) { } } -func TestAssemblerImpl_EncodeMemoryToVectorRegister(t *testing.T) { +func TestAssemblerImpl_EncodeRegisterToVectorRegister(t *testing.T) { t.Run("error", func(t *testing.T) { tests := []struct { - n *arm64.NodeImpl - expErr string + n *arm64.NodeImpl + exp string }{ { - n: &arm64.NodeImpl{Instruction: arm64.B, - SrcReg: arm64.RegR1, - DstReg: arm64.RegV21, - Types: arm64.OperandTypesMemoryToVectorRegister, - }, - expErr: "B is unsupported for from:memory,to:vector-register type", - }, - { - n: &arm64.NodeImpl{Instruction: arm64.VLD1, - SrcReg: arm64.RegR1, - DstReg: arm64.RegV21, - Types: arm64.OperandTypesMemoryToVectorRegister, + n: &arm64.NodeImpl{ + Instruction: arm64.B, Types: arm64.OperandTypesRegisterToVectorRegister, + SrcReg: arm64.RegR0, + DstReg: arm64.RegV3, }, - expErr: "unsupported arrangement for VLD1: unknown", + exp: "B is unsupported for from:register,to:vector-register type", }, - } - - for _, tt := range tests { - tc := tt - a := arm64.NewAssemblerImpl(asm.NilRegister) - err := a.EncodeMemoryToVectorRegister(tc.n) - require.EqualError(t, err, tc.expErr) - } - }) - - regs := []asm.Register{arm64.RegR0, arm64.RegR5, arm64.RegR30} - vectorRegs := []asm.Register{arm64.RegV10, arm64.RegV2} - arrangements := []arm64.VectorArrangement{ - arm64.VectorArrangement8B, - arm64.VectorArrangement16B, - arm64.VectorArrangement4H, - arm64.VectorArrangement8H, - arm64.VectorArrangement2S, - arm64.VectorArrangement4S, - arm64.VectorArrangement1D, - arm64.VectorArrangement2D, - } - - for _, inst := range []asm.Instruction{arm64.VLD1} { - inst := inst - t.Run(arm64.InstructionName(inst), func(t *testing.T) { - for _, arr := range arrangements { - for _, offsetReg := range regs { - for _, vr := range vectorRegs { - arr, offsetReg, vr := arr, offsetReg, vr - t.Run(fmt.Sprintf("src=%s,dst=%s.%s", - arm64.RegisterName(offsetReg), arm64.RegisterName(vr), arr), func(t *testing.T) { - goasm := newGoasmAssembler(t, asm.NilRegister) - a := arm64.NewAssemblerImpl(asm.NilRegister) - - for _, assembler := range []arm64.Assembler{goasm, a} { - assembler.CompileMemoryToVectorRegister(inst, offsetReg, vr, arr) - } - - expected, err := goasm.Assemble() - require.NoError(t, err) - - actual, err := a.Assemble() - require.NoError(t, err) - require.Equal(t, expected, actual, hex.EncodeToString(expected)) - }) - } - } - } - }) - } -} - -func TestAssemblerImpl_EncodeVectorRegisterToMemory(t *testing.T) { - t.Run("error", func(t *testing.T) { - tests := []struct { - n *arm64.NodeImpl - expErr string - }{ { - n: &arm64.NodeImpl{Instruction: arm64.B, - SrcReg: arm64.RegV21, - DstReg: arm64.RegR1, - Types: arm64.OperandTypesVectorRegisterToMemory, + n: &arm64.NodeImpl{Instruction: arm64.VMOV, + SrcReg: arm64.RegR0, + DstReg: arm64.RegV3, + Types: arm64.OperandTypesRegisterToVectorRegister, + DstVectorIndex: 100, VectorArrangement: arm64.VectorArrangement1D, }, - expErr: "B is unsupported for from:vector-register,to:memory type", + exp: "invalid arrangement and index pair: 1D[100]", }, { - n: &arm64.NodeImpl{Instruction: arm64.VST1, - SrcReg: arm64.RegV21, - DstReg: arm64.RegR1, - Types: arm64.OperandTypesVectorRegisterToMemory, + n: &arm64.NodeImpl{Instruction: arm64.VMOV, + Types: arm64.OperandTypesRegisterToVectorRegister, + SrcReg: arm64.RegR0, + DstReg: arm64.RegV3, + DstVectorIndex: 0, VectorArrangement: arm64.VectorArrangement1D, }, - expErr: "unsupported arrangement for VST1: unknown", + exp: "unsupported arrangement for VMOV: 1D", }, } for _, tt := range tests { tc := tt - a := arm64.NewAssemblerImpl(asm.NilRegister) - err := a.EncodeVectorRegisterToMemory(tc.n) - require.EqualError(t, err, tc.expErr) + t.Run(tc.exp, func(t *testing.T) { + a := arm64.NewAssemblerImpl(asm.NilRegister) + err := a.EncodeRegisterToVectorRegister(tc.n) + require.EqualError(t, err, tc.exp) + }) } }) - regs := []asm.Register{arm64.RegR0, arm64.RegR5, arm64.RegR30} - vectorRegs := []asm.Register{arm64.RegV10, arm64.RegV2} - arrangements := []arm64.VectorArrangement{ - arm64.VectorArrangement8B, - arm64.VectorArrangement16B, - arm64.VectorArrangement4H, - arm64.VectorArrangement8H, - arm64.VectorArrangement2S, - arm64.VectorArrangement4S, - arm64.VectorArrangement1D, - arm64.VectorArrangement2D, + regs := []asm.Register{arm64.RegR0, arm64.RegR10, arm64.RegR30} + vectorRegs := []asm.Register{arm64.RegV0, arm64.RegV10, arm64.RegV30} + + tests := []struct { + inst asm.Instruction + arrangement arm64.VectorArrangement + index arm64.VectorIndex + }{ + { + inst: arm64.VMOV, + arrangement: arm64.VectorArrangementD, + index: 0, + }, + { + inst: arm64.VMOV, + arrangement: arm64.VectorArrangementD, + index: 1, + }, + { + inst: arm64.VMOV, + arrangement: arm64.VectorArrangementB, + index: 0, + }, + { + inst: arm64.VMOV, + arrangement: arm64.VectorArrangementB, + index: 5, + }, + { + inst: arm64.VMOV, + arrangement: arm64.VectorArrangementH, + index: 1, + }, + { + inst: arm64.VMOV, + arrangement: arm64.VectorArrangementH, + index: 4, + }, } - for _, inst := range []asm.Instruction{arm64.VST1} { - inst := inst - t.Run(arm64.InstructionName(inst), func(t *testing.T) { - for _, arr := range arrangements { - for _, offsetReg := range regs { - for _, vr := range vectorRegs { - arr, offsetReg, vr := arr, offsetReg, vr - t.Run(fmt.Sprintf("src=%s,dst=%s.%s", - arm64.RegisterName(offsetReg), arm64.RegisterName(vr), arr), func(t *testing.T) { - goasm := newGoasmAssembler(t, asm.NilRegister) - a := arm64.NewAssemblerImpl(asm.NilRegister) + for _, tt := range tests { + tc := tt + t.Run(arm64.InstructionName(tc.inst), func(t *testing.T) { + for _, r := range regs { + for _, vr := range vectorRegs { + r, vr := r, vr + t.Run(fmt.Sprintf("src=%s,dst=%s.%s[%d]", + arm64.RegisterName(r), arm64.RegisterName(vr), tc.arrangement, tc.index), func(t *testing.T) { + goasm := newGoasmAssembler(t, asm.NilRegister) + a := arm64.NewAssemblerImpl(asm.NilRegister) - for _, assembler := range []arm64.Assembler{goasm, a} { - assembler.CompileVectorRegisterToMemory(inst, vr, offsetReg, arr) - } + for _, assembler := range []arm64.Assembler{goasm, a} { + assembler.CompileRegisterToVectorRegister(tc.inst, r, vr, tc.arrangement, tc.index) + } - expected, err := goasm.Assemble() - require.NoError(t, err) + expected, err := goasm.Assemble() + require.NoError(t, err) - actual, err := a.Assemble() - require.NoError(t, err) - require.Equal(t, expected, actual, hex.EncodeToString(expected)) - fmt.Println(hex.EncodeToString(expected)) - }) - } + actual, err := a.Assemble() + require.NoError(t, err) + require.Equal(t, expected, actual) + }) } } }) } } -func TestAssemblerImpl_EncodeRegisterToVectorRegister(t *testing.T) { +func TestAssemblerImpl_EncodeVectorRegisterToRegister(t *testing.T) { t.Run("error", func(t *testing.T) { tests := []struct { n *arm64.NodeImpl expErr string }{ { - n: &arm64.NodeImpl{Instruction: arm64.B, Types: arm64.OperandTypesRegisterToVectorRegister}, - expErr: "B is unsupported for from:register,to:vector-register type", + n: &arm64.NodeImpl{Instruction: arm64.B, Types: arm64.OperandTypesVectorRegisterToRegister, + SrcReg: arm64.RegV0, + DstReg: arm64.RegR3, + }, + expErr: "B is unsupported for from:vector-register,to:register type", }, { n: &arm64.NodeImpl{Instruction: arm64.VMOV, - Types: arm64.OperandTypesRegisterToVectorRegister, - VectorIndex: 100, VectorArrangement: arm64.VectorArrangement1D, + Types: arm64.OperandTypesVectorRegisterToRegister, + SrcReg: arm64.RegV0, + DstReg: arm64.RegR3, + SrcVectorIndex: 100, VectorArrangement: arm64.VectorArrangement1D, }, expErr: "invalid arrangement and index pair: 1D[100]", }, { n: &arm64.NodeImpl{Instruction: arm64.VMOV, - Types: arm64.OperandTypesRegisterToVectorRegister, - SrcReg: arm64.RegR0, - DstReg: arm64.RegV3, - VectorIndex: 0, VectorArrangement: arm64.VectorArrangement1D, + Types: arm64.OperandTypesVectorRegisterToRegister, + SrcReg: arm64.RegV0, + DstReg: arm64.RegR3, + SrcVectorIndex: 0, VectorArrangement: arm64.VectorArrangement1D, }, expErr: "unsupported arrangement for VMOV: 1D", }, @@ -1542,7 +1555,7 @@ func TestAssemblerImpl_EncodeRegisterToVectorRegister(t *testing.T) { for _, tt := range tests { tc := tt a := arm64.NewAssemblerImpl(asm.NilRegister) - err := a.EncodeRegisterToVectorRegister(tc.n) + err := a.EncodeVectorRegisterToRegister(tc.n) require.EqualError(t, err, tc.expErr) } }) @@ -1551,60 +1564,78 @@ func TestAssemblerImpl_EncodeRegisterToVectorRegister(t *testing.T) { vectorRegs := []asm.Register{arm64.RegV0, arm64.RegV10, arm64.RegV30} tests := []struct { + name string inst asm.Instruction arrangement arm64.VectorArrangement index arm64.VectorIndex }{ { + name: "VMOV D[0]", inst: arm64.VMOV, arrangement: arm64.VectorArrangementD, index: 0, }, { + name: "VMOV D[1]", inst: arm64.VMOV, arrangement: arm64.VectorArrangementD, index: 1, }, { + name: "VMOV B[0]", inst: arm64.VMOV, arrangement: arm64.VectorArrangementB, index: 0, }, { + name: "VMOV B[15]", inst: arm64.VMOV, arrangement: arm64.VectorArrangementB, - index: 5, + index: 15, }, { + name: "VMOV H[1]", inst: arm64.VMOV, arrangement: arm64.VectorArrangementH, index: 1, }, { + name: "VMOV H[4]", inst: arm64.VMOV, arrangement: arm64.VectorArrangementH, - index: 4, + index: 7, + }, + { + name: "VMOV S[2]", + inst: arm64.VMOV, + arrangement: arm64.VectorArrangementS, + index: 2, + }, + { + name: "VMOV S[3]", + inst: arm64.VMOV, + arrangement: arm64.VectorArrangementS, + index: 3, }, } for _, tt := range tests { tc := tt - t.Run(arm64.InstructionName(tc.inst), func(t *testing.T) { + t.Run(tc.name, func(t *testing.T) { for _, r := range regs { for _, vr := range vectorRegs { r, vr := r, vr - t.Run(fmt.Sprintf("src=%s,dst=%s.%s[%d]", + t.Run(fmt.Sprintf("dst=%s,src=%s.%s[%d]", arm64.RegisterName(r), arm64.RegisterName(vr), tc.arrangement, tc.index), func(t *testing.T) { goasm := newGoasmAssembler(t, asm.NilRegister) a := arm64.NewAssemblerImpl(asm.NilRegister) for _, assembler := range []arm64.Assembler{goasm, a} { - assembler.CompileRegisterToVectorRegister(tc.inst, r, vr, tc.arrangement, tc.index) + assembler.CompileVectorRegisterToRegister(tc.inst, vr, r, tc.arrangement, tc.index) } expected, err := goasm.Assemble() require.NoError(t, err) - actual, err := a.Assemble() require.NoError(t, err) require.Equal(t, expected, actual) diff --git a/internal/integration_test/spectest/v2/spec_test.go b/internal/integration_test/spectest/v2/spec_test.go index 04dcd092c2a..effae47b905 100644 --- a/internal/integration_test/spectest/v2/spec_test.go +++ b/internal/integration_test/spectest/v2/spec_test.go @@ -27,7 +27,27 @@ func TestCompiler(t *testing.T) { spectest.Run(t, testcases, compiler.NewEngine, enabledFeatures, func(jsonname string) bool { // TODO: remove after SIMD proposal if strings.Contains(jsonname, "simd") { - return path.Base(jsonname) == "simd_const.json" + switch path.Base(jsonname) { + case "simd_address.json": + case "simd_const.json": + case "simd_align.json": + case "simd_load16_lane.json": + case "simd_load32_lane.json": + case "simd_load64_lane.json": + case "simd_load8_lane.json": + case "simd_lane.json": + case "simd_load_extend.json": + case "simd_load_splat.json": + case "simd_load_zero.json": + case "simd_store.json": + case "simd_store16_lane.json": + case "simd_store32_lane.json": + case "simd_store64_lane.json": + case "simd_store8_lane.json": + default: + return false + } + return true } return true }) @@ -37,7 +57,27 @@ func TestInterpreter(t *testing.T) { spectest.Run(t, testcases, interpreter.NewEngine, enabledFeatures, func(jsonname string) bool { // TODO: remove after SIMD proposal if strings.Contains(jsonname, "simd") { - return path.Base(jsonname) == "simd_const.json" + switch path.Base(jsonname) { + case "simd_address.json": + case "simd_const.json": + case "simd_align.json": + case "simd_load16_lane.json": + case "simd_load32_lane.json": + case "simd_load64_lane.json": + case "simd_load8_lane.json": + case "simd_lane.json": + case "simd_load_extend.json": + case "simd_load_splat.json": + case "simd_load_zero.json": + case "simd_store.json": + case "simd_store16_lane.json": + case "simd_store32_lane.json": + case "simd_store64_lane.json": + case "simd_store8_lane.json": + default: + return false + } + return true } return true }) diff --git a/internal/integration_test/spectest/v2/testdata/simd_lane.json b/internal/integration_test/spectest/v2/testdata/simd_lane.json index 35e52316e56..d998630e9d2 100644 --- a/internal/integration_test/spectest/v2/testdata/simd_lane.json +++ b/internal/integration_test/spectest/v2/testdata/simd_lane.json @@ -1,477 +1,477 @@ {"source_filename": "./simd_lane.wast", "commands": [ - {"type": "module", "line": 4, "filename": "simd_lane.0.wasm"}, - {"type": "assert_return", "line": 81, "action": {"type": "invoke", "field": "i8x16_extract_lane_s-first", "args": [{"type": "v128", "lane_type": "i8", "value": ["127", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "127"}]}, - {"type": "assert_return", "line": 82, "action": {"type": "invoke", "field": "i8x16_extract_lane_s-first", "args": [{"type": "v128", "lane_type": "i8", "value": ["127", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "127"}]}, - {"type": "assert_return", "line": 83, "action": {"type": "invoke", "field": "i8x16_extract_lane_s-first", "args": [{"type": "v128", "lane_type": "i8", "value": ["255", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "4294967295"}]}, - {"type": "assert_return", "line": 84, "action": {"type": "invoke", "field": "i8x16_extract_lane_s-first", "args": [{"type": "v128", "lane_type": "i8", "value": ["255", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "4294967295"}]}, - {"type": "assert_return", "line": 85, "action": {"type": "invoke", "field": "i8x16_extract_lane_u-first", "args": [{"type": "v128", "lane_type": "i8", "value": ["255", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "255"}]}, - {"type": "assert_return", "line": 86, "action": {"type": "invoke", "field": "i8x16_extract_lane_u-first", "args": [{"type": "v128", "lane_type": "i8", "value": ["255", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "255"}]}, - {"type": "assert_return", "line": 87, "action": {"type": "invoke", "field": "i8x16_extract_lane_s-last", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "128"]}]}, "expected": [{"type": "i32", "value": "4294967168"}]}, - {"type": "assert_return", "line": 88, "action": {"type": "invoke", "field": "i8x16_extract_lane_s-last", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "128"]}]}, "expected": [{"type": "i32", "value": "4294967168"}]}, - {"type": "assert_return", "line": 89, "action": {"type": "invoke", "field": "i8x16_extract_lane_u-last", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "255"]}]}, "expected": [{"type": "i32", "value": "255"}]}, - {"type": "assert_return", "line": 90, "action": {"type": "invoke", "field": "i8x16_extract_lane_u-last", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "255"]}]}, "expected": [{"type": "i32", "value": "255"}]}, - {"type": "assert_return", "line": 91, "action": {"type": "invoke", "field": "i8x16_extract_lane_u-last", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "128"]}]}, "expected": [{"type": "i32", "value": "128"}]}, - {"type": "assert_return", "line": 92, "action": {"type": "invoke", "field": "i8x16_extract_lane_u-last", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "128"]}]}, "expected": [{"type": "i32", "value": "128"}]}, - {"type": "assert_return", "line": 94, "action": {"type": "invoke", "field": "i16x8_extract_lane_s-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["32767", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "32767"}]}, - {"type": "assert_return", "line": 95, "action": {"type": "invoke", "field": "i16x8_extract_lane_s-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["32767", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "32767"}]}, - {"type": "assert_return", "line": 96, "action": {"type": "invoke", "field": "i16x8_extract_lane_s-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["65535", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "4294967295"}]}, - {"type": "assert_return", "line": 97, "action": {"type": "invoke", "field": "i16x8_extract_lane_s-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["65535", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "4294967295"}]}, - {"type": "assert_return", "line": 98, "action": {"type": "invoke", "field": "i16x8_extract_lane_s-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["12345", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "12345"}]}, - {"type": "assert_return", "line": 99, "action": {"type": "invoke", "field": "i16x8_extract_lane_s-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["60876", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "4294962636"}]}, - {"type": "assert_return", "line": 100, "action": {"type": "invoke", "field": "i16x8_extract_lane_u-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["65535", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "65535"}]}, - {"type": "assert_return", "line": 101, "action": {"type": "invoke", "field": "i16x8_extract_lane_u-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["65535", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "65535"}]}, - {"type": "assert_return", "line": 102, "action": {"type": "invoke", "field": "i16x8_extract_lane_u-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["12345", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "12345"}]}, - {"type": "assert_return", "line": 103, "action": {"type": "invoke", "field": "i16x8_extract_lane_u-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["60876", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "60876"}]}, - {"type": "assert_return", "line": 104, "action": {"type": "invoke", "field": "i16x8_extract_lane_s-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "32768"]}]}, "expected": [{"type": "i32", "value": "4294934528"}]}, - {"type": "assert_return", "line": 105, "action": {"type": "invoke", "field": "i16x8_extract_lane_s-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "32768"]}]}, "expected": [{"type": "i32", "value": "4294934528"}]}, - {"type": "assert_return", "line": 106, "action": {"type": "invoke", "field": "i16x8_extract_lane_s-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "6789"]}]}, "expected": [{"type": "i32", "value": "6789"}]}, - {"type": "assert_return", "line": 107, "action": {"type": "invoke", "field": "i16x8_extract_lane_s-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "39031"]}]}, "expected": [{"type": "i32", "value": "4294940791"}]}, - {"type": "assert_return", "line": 108, "action": {"type": "invoke", "field": "i16x8_extract_lane_u-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "65535"]}]}, "expected": [{"type": "i32", "value": "65535"}]}, - {"type": "assert_return", "line": 109, "action": {"type": "invoke", "field": "i16x8_extract_lane_u-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "65535"]}]}, "expected": [{"type": "i32", "value": "65535"}]}, - {"type": "assert_return", "line": 110, "action": {"type": "invoke", "field": "i16x8_extract_lane_u-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "32768"]}]}, "expected": [{"type": "i32", "value": "32768"}]}, - {"type": "assert_return", "line": 111, "action": {"type": "invoke", "field": "i16x8_extract_lane_u-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "32768"]}]}, "expected": [{"type": "i32", "value": "32768"}]}, - {"type": "assert_return", "line": 112, "action": {"type": "invoke", "field": "i16x8_extract_lane_u-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "6789"]}]}, "expected": [{"type": "i32", "value": "6789"}]}, - {"type": "assert_return", "line": 113, "action": {"type": "invoke", "field": "i16x8_extract_lane_u-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "39031"]}]}, "expected": [{"type": "i32", "value": "39031"}]}, - {"type": "assert_return", "line": 115, "action": {"type": "invoke", "field": "i32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "i32", "value": ["2147483647", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "2147483647"}]}, - {"type": "assert_return", "line": 116, "action": {"type": "invoke", "field": "i32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "i32", "value": ["2147483647", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "2147483647"}]}, - {"type": "assert_return", "line": 117, "action": {"type": "invoke", "field": "i32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "i32", "value": ["4294967295", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "4294967295"}]}, - {"type": "assert_return", "line": 118, "action": {"type": "invoke", "field": "i32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "i32", "value": ["4294967295", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "4294967295"}]}, - {"type": "assert_return", "line": 119, "action": {"type": "invoke", "field": "i32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "i32", "value": ["1234567890", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "1234567890"}]}, - {"type": "assert_return", "line": 120, "action": {"type": "invoke", "field": "i32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "i32", "value": ["3989547400", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "3989547400"}]}, - {"type": "assert_return", "line": 121, "action": {"type": "invoke", "field": "i32x4_extract_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "2147483648"]}]}, "expected": [{"type": "i32", "value": "2147483648"}]}, - {"type": "assert_return", "line": 122, "action": {"type": "invoke", "field": "i32x4_extract_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "2147483648"]}]}, "expected": [{"type": "i32", "value": "2147483648"}]}, - {"type": "assert_return", "line": 123, "action": {"type": "invoke", "field": "i32x4_extract_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "4294967295"]}]}, "expected": [{"type": "i32", "value": "4294967295"}]}, - {"type": "assert_return", "line": 124, "action": {"type": "invoke", "field": "i32x4_extract_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "4294967295"]}]}, "expected": [{"type": "i32", "value": "4294967295"}]}, - {"type": "assert_return", "line": 125, "action": {"type": "invoke", "field": "i32x4_extract_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "987654321"]}]}, "expected": [{"type": "i32", "value": "987654321"}]}, - {"type": "assert_return", "line": 126, "action": {"type": "invoke", "field": "i32x4_extract_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "3989547400"]}]}, "expected": [{"type": "i32", "value": "3989547400"}]}, - {"type": "assert_return", "line": 128, "action": {"type": "invoke", "field": "i64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "i64", "value": ["9223372036854775807", "0"]}]}, "expected": [{"type": "i64", "value": "9223372036854775807"}]}, - {"type": "assert_return", "line": 129, "action": {"type": "invoke", "field": "i64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "i64", "value": ["9223372036854775806", "0"]}]}, "expected": [{"type": "i64", "value": "9223372036854775806"}]}, - {"type": "assert_return", "line": 130, "action": {"type": "invoke", "field": "i64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "i64", "value": ["18446744073709551615", "0"]}]}, "expected": [{"type": "i64", "value": "18446744073709551615"}]}, - {"type": "assert_return", "line": 131, "action": {"type": "invoke", "field": "i64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "i64", "value": ["18446744073709551615", "0"]}]}, "expected": [{"type": "i64", "value": "18446744073709551615"}]}, - {"type": "assert_return", "line": 132, "action": {"type": "invoke", "field": "i64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "i64", "value": ["1234567890123456789", "0"]}]}, "expected": [{"type": "i64", "value": "1234567890123456789"}]}, - {"type": "assert_return", "line": 133, "action": {"type": "invoke", "field": "i64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "i64", "value": ["1311768467294899695", "0"]}]}, "expected": [{"type": "i64", "value": "1311768467294899695"}]}, - {"type": "assert_return", "line": 134, "action": {"type": "invoke", "field": "i64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "9223372036854775808"]}]}, "expected": [{"type": "i64", "value": "9223372036854775808"}]}, - {"type": "assert_return", "line": 135, "action": {"type": "invoke", "field": "i64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "9223372036854775808"]}]}, "expected": [{"type": "i64", "value": "9223372036854775808"}]}, - {"type": "assert_return", "line": 136, "action": {"type": "invoke", "field": "i64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "9223372036854775808"]}]}, "expected": [{"type": "i64", "value": "9223372036854775808"}]}, - {"type": "assert_return", "line": 137, "action": {"type": "invoke", "field": "i64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "255", "255", "255", "255", "255", "255", "255", "127"]}]}, "expected": [{"type": "i64", "value": "9223372036854775807"}]}, - {"type": "assert_return", "line": 138, "action": {"type": "invoke", "field": "i64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "32768"]}]}, "expected": [{"type": "i64", "value": "9223372036854775808"}]}, - {"type": "assert_return", "line": 139, "action": {"type": "invoke", "field": "i64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "4294967295", "2147483647"]}]}, "expected": [{"type": "i64", "value": "9223372036854775807"}]}, - {"type": "assert_return", "line": 140, "action": {"type": "invoke", "field": "i64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["18442240474082181120", "9218868437227405312"]}]}, "expected": [{"type": "i64", "value": "9218868437227405312"}]}, - {"type": "assert_return", "line": 141, "action": {"type": "invoke", "field": "i64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "1234567890123456789"]}]}, "expected": [{"type": "i64", "value": "1234567890123456789"}]}, - {"type": "assert_return", "line": 142, "action": {"type": "invoke", "field": "i64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "1311768467294899695"]}]}, "expected": [{"type": "i64", "value": "1311768467294899695"}]}, - {"type": "assert_return", "line": 144, "action": {"type": "invoke", "field": "f32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["3231711232", "0", "0", "0"]}]}, "expected": [{"type": "f32", "value": "3231711232"}]}, - {"type": "assert_return", "line": 145, "action": {"type": "invoke", "field": "f32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["2123789977", "0", "0", "0"]}]}, "expected": [{"type": "f32", "value": "2123789977"}]}, - {"type": "assert_return", "line": 146, "action": {"type": "invoke", "field": "f32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["2139095039", "0", "0", "0"]}]}, "expected": [{"type": "f32", "value": "2139095039"}]}, - {"type": "assert_return", "line": 147, "action": {"type": "invoke", "field": "f32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["2130706432", "0", "0", "0"]}]}, "expected": [{"type": "f32", "value": "2130706432"}]}, - {"type": "assert_return", "line": 148, "action": {"type": "invoke", "field": "f32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["2139095040", "0", "0", "0"]}]}, "expected": [{"type": "f32", "value": "2139095040"}]}, - {"type": "assert_return", "line": 149, "action": {"type": "invoke", "field": "f32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["2143289344", "2139095040", "0", "0"]}]}, "expected": [{"type": "f32", "value": "2143289344"}]}, - {"type": "assert_return", "line": 150, "action": {"type": "invoke", "field": "f32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["1820282235", "0", "0", "0"]}]}, "expected": [{"type": "f32", "value": "1820282235"}]}, - {"type": "assert_return", "line": 151, "action": {"type": "invoke", "field": "f32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["1376887476", "0", "0", "0"]}]}, "expected": [{"type": "f32", "value": "1376887476"}]}, - {"type": "assert_return", "line": 152, "action": {"type": "invoke", "field": "f32x4_extract_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "4271273625"]}]}, "expected": [{"type": "f32", "value": "4271273625"}]}, - {"type": "assert_return", "line": 153, "action": {"type": "invoke", "field": "f32x4_extract_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "4286578687"]}]}, "expected": [{"type": "f32", "value": "4286578687"}]}, - {"type": "assert_return", "line": 154, "action": {"type": "invoke", "field": "f32x4_extract_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "4278190080"]}]}, "expected": [{"type": "f32", "value": "4278190080"}]}, - {"type": "assert_return", "line": 155, "action": {"type": "invoke", "field": "f32x4_extract_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "4286578688"]}]}, "expected": [{"type": "f32", "value": "4286578688"}]}, - {"type": "assert_return", "line": 156, "action": {"type": "invoke", "field": "f32x4_extract_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "4286578688", "2143289344"]}]}, "expected": [{"type": "f32", "value": "2143289344"}]}, - {"type": "assert_return", "line": 157, "action": {"type": "invoke", "field": "f32x4_extract_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "1290500515"]}]}, "expected": [{"type": "f32", "value": "1290500515"}]}, - {"type": "assert_return", "line": 158, "action": {"type": "invoke", "field": "f32x4_extract_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "1536271028"]}]}, "expected": [{"type": "f32", "value": "1536271028"}]}, - {"type": "assert_return", "line": 160, "action": {"type": "invoke", "field": "f64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["13832806255468478464", "0"]}]}, "expected": [{"type": "f64", "value": "13832806255468478464"}]}, - {"type": "assert_return", "line": 161, "action": {"type": "invoke", "field": "f64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["4609434218613702656", "0"]}]}, "expected": [{"type": "f64", "value": "4609434218613702656"}]}, - {"type": "assert_return", "line": 162, "action": {"type": "invoke", "field": "f64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["9227010608267287965", "0"]}]}, "expected": [{"type": "f64", "value": "9227010608267287965"}]}, - {"type": "assert_return", "line": 163, "action": {"type": "invoke", "field": "f64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["3638571412512157", "0"]}]}, "expected": [{"type": "f64", "value": "3638571412512157"}]}, - {"type": "assert_return", "line": 164, "action": {"type": "invoke", "field": "f64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["9227875636482146303", "0"]}]}, "expected": [{"type": "f64", "value": "9227875636482146303"}]}, - {"type": "assert_return", "line": 165, "action": {"type": "invoke", "field": "f64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["4503599627370495", "0"]}]}, "expected": [{"type": "f64", "value": "4503599627370495"}]}, - {"type": "assert_return", "line": 166, "action": {"type": "invoke", "field": "f64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["18442240474082181120", "0"]}]}, "expected": [{"type": "f64", "value": "18442240474082181120"}]}, - {"type": "assert_return", "line": 167, "action": {"type": "invoke", "field": "f64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["9218868437227405312", "0"]}]}, "expected": [{"type": "f64", "value": "9218868437227405312"}]}, - {"type": "assert_return", "line": 168, "action": {"type": "invoke", "field": "f64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["18444492273895866368", "9223372036854775808"]}]}, "expected": [{"type": "f64", "value": "18444492273895866368"}]}, - {"type": "assert_return", "line": 169, "action": {"type": "invoke", "field": "f64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["9221120237041090560", "0"]}]}, "expected": [{"type": "f64", "value": "9221120237041090560"}]}, - {"type": "assert_return", "line": 170, "action": {"type": "invoke", "field": "f64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["5012481849648991189", "0"]}]}, "expected": [{"type": "f64", "value": "5012481849648991189"}]}, - {"type": "assert_return", "line": 171, "action": {"type": "invoke", "field": "f64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["4882522492018277599", "0"]}]}, "expected": [{"type": "f64", "value": "4882522492018277599"}]}, - {"type": "assert_return", "line": 172, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "4612248968380809216"]}]}, "expected": [{"type": "f64", "value": "4612248968380809216"}]}, - {"type": "assert_return", "line": 173, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "13835621005235585024"]}]}, "expected": [{"type": "f64", "value": "13835621005235585024"}]}, - {"type": "assert_return", "line": 174, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "18442240474082181119"]}]}, "expected": [{"type": "f64", "value": "18442240474082181119"}]}, - {"type": "assert_return", "line": 175, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "9218868437227405311"]}]}, "expected": [{"type": "f64", "value": "9218868437227405311"}]}, - {"type": "assert_return", "line": 176, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "18442240474082181119"]}]}, "expected": [{"type": "f64", "value": "18442240474082181119"}]}, - {"type": "assert_return", "line": 177, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "9218868437227405311"]}]}, "expected": [{"type": "f64", "value": "9218868437227405311"}]}, - {"type": "assert_return", "line": 178, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["9223372036854775808", "18442240474082181120"]}]}, "expected": [{"type": "f64", "value": "18442240474082181120"}]}, - {"type": "assert_return", "line": 179, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "9218868437227405312"]}]}, "expected": [{"type": "f64", "value": "9218868437227405312"}]}, - {"type": "assert_return", "line": 180, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["9223372036854775808", "18444492273895866368"]}]}, "expected": [{"type": "f64", "value": "18444492273895866368"}]}, - {"type": "assert_return", "line": 181, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "9221120237041090560"]}]}, "expected": [{"type": "f64", "value": "9221120237041090560"}]}, - {"type": "assert_return", "line": 182, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "4728057454347157504"]}]}, "expected": [{"type": "f64", "value": "4728057454347157504"}]}, - {"type": "assert_return", "line": 183, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "4968090884938317023"]}]}, "expected": [{"type": "f64", "value": "4968090884938317023"}]}, - {"type": "assert_return", "line": 185, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "f64", "value": "0"}]}, - {"type": "assert_return", "line": 186, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "128"]}]}, "expected": [{"type": "f64", "value": "9223372036854775808"}]}, - {"type": "assert_return", "line": 187, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "16384"]}]}, "expected": [{"type": "f64", "value": "4611686018427387904"}]}, - {"type": "assert_return", "line": 188, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "49152"]}]}, "expected": [{"type": "f64", "value": "13835058055282163712"}]}, - {"type": "assert_return", "line": 189, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "4294967295", "2146435071"]}]}, "expected": [{"type": "f64", "value": "9218868437227405311"}]}, - {"type": "assert_return", "line": 190, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "1048576"]}]}, "expected": [{"type": "f64", "value": "4503599627370496"}]}, - {"type": "assert_return", "line": 191, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "4294967295", "1048575"]}]}, "expected": [{"type": "f64", "value": "4503599627370495"}]}, - {"type": "assert_return", "line": 192, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "1", "0"]}]}, "expected": [{"type": "f64", "value": "1"}]}, - {"type": "assert_return", "line": 194, "action": {"type": "invoke", "field": "i8x16_replace_lane-first", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "127"}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["127", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 195, "action": {"type": "invoke", "field": "i8x16_replace_lane-first", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "128"}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["128", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 196, "action": {"type": "invoke", "field": "i8x16_replace_lane-first", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "255"}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["255", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 197, "action": {"type": "invoke", "field": "i8x16_replace_lane-first", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "256"}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 198, "action": {"type": "invoke", "field": "i8x16_replace_lane-last", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "4294967168"}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "128"]}]}, - {"type": "assert_return", "line": 199, "action": {"type": "invoke", "field": "i8x16_replace_lane-last", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "4294967167"}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "127"]}]}, - {"type": "assert_return", "line": 200, "action": {"type": "invoke", "field": "i8x16_replace_lane-last", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "32767"}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "255"]}]}, - {"type": "assert_return", "line": 201, "action": {"type": "invoke", "field": "i8x16_replace_lane-last", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "4294934528"}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 203, "action": {"type": "invoke", "field": "i16x8_replace_lane-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "32767"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["32767", "0", "0", "0", "0", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 204, "action": {"type": "invoke", "field": "i16x8_replace_lane-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "32768"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["32768", "0", "0", "0", "0", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 205, "action": {"type": "invoke", "field": "i16x8_replace_lane-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "65535"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["65535", "0", "0", "0", "0", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 206, "action": {"type": "invoke", "field": "i16x8_replace_lane-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "65536"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 207, "action": {"type": "invoke", "field": "i16x8_replace_lane-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "12345"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["12345", "0", "0", "0", "0", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 208, "action": {"type": "invoke", "field": "i16x8_replace_lane-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "4294962636"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["60876", "0", "0", "0", "0", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 209, "action": {"type": "invoke", "field": "i16x8_replace_lane-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "4294934528"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "32768"]}]}, - {"type": "assert_return", "line": 210, "action": {"type": "invoke", "field": "i16x8_replace_lane-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "4294934527"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "32767"]}]}, - {"type": "assert_return", "line": 211, "action": {"type": "invoke", "field": "i16x8_replace_lane-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "2147483647"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "65535"]}]}, - {"type": "assert_return", "line": 212, "action": {"type": "invoke", "field": "i16x8_replace_lane-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "2147483648"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 213, "action": {"type": "invoke", "field": "i16x8_replace_lane-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "54321"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "54321"]}]}, - {"type": "assert_return", "line": 214, "action": {"type": "invoke", "field": "i16x8_replace_lane-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "4294950111"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "48351"]}]}, - {"type": "assert_return", "line": 216, "action": {"type": "invoke", "field": "i32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}, {"type": "i32", "value": "2147483647"}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["2147483647", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 217, "action": {"type": "invoke", "field": "i32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}, {"type": "i32", "value": "4294967295"}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["4294967295", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 218, "action": {"type": "invoke", "field": "i32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}, {"type": "i32", "value": "1234567890"}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["1234567890", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 219, "action": {"type": "invoke", "field": "i32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}, {"type": "i32", "value": "3989547400"}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["3989547400", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 220, "action": {"type": "invoke", "field": "i32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}, {"type": "i32", "value": "2147483648"}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "2147483648"]}]}, - {"type": "assert_return", "line": 221, "action": {"type": "invoke", "field": "i32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}, {"type": "i32", "value": "2147483648"}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "2147483648"]}]}, - {"type": "assert_return", "line": 222, "action": {"type": "invoke", "field": "i32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}, {"type": "i32", "value": "1234567890"}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "1234567890"]}]}, - {"type": "assert_return", "line": 223, "action": {"type": "invoke", "field": "i32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}, {"type": "i32", "value": "3989547400"}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "3989547400"]}]}, - {"type": "assert_return", "line": 225, "action": {"type": "invoke", "field": "f32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "1112801280"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["1112801280", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 226, "action": {"type": "invoke", "field": "f32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "1112801280"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["1112801280", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 227, "action": {"type": "invoke", "field": "f32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "2143289344"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["2143289344", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 228, "action": {"type": "invoke", "field": "f32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "2139095040"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["2139095040", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 229, "action": {"type": "invoke", "field": "f32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["2143289344", "0", "0", "0"]}, {"type": "f32", "value": "1078523331"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["1078523331", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 230, "action": {"type": "invoke", "field": "f32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["2139095040", "0", "0", "0"]}, {"type": "f32", "value": "2123789977"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["2123789977", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 231, "action": {"type": "invoke", "field": "f32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["2139095040", "0", "0", "0"]}, {"type": "f32", "value": "2139095039"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["2139095039", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 232, "action": {"type": "invoke", "field": "f32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["2139095040", "0", "0", "0"]}, {"type": "f32", "value": "2130706432"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["2130706432", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 233, "action": {"type": "invoke", "field": "f32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "1290500515"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["1290500515", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 234, "action": {"type": "invoke", "field": "f32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "1290500515"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["1290500515", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 235, "action": {"type": "invoke", "field": "f32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "1536271028"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["1536271028", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 236, "action": {"type": "invoke", "field": "f32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "1536271028"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["1536271028", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 237, "action": {"type": "invoke", "field": "f32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "3260284928"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "3260284928"]}]}, - {"type": "assert_return", "line": 238, "action": {"type": "invoke", "field": "f32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "3260284928"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "3260284928"]}]}, - {"type": "assert_return", "line": 239, "action": {"type": "invoke", "field": "f32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "2143289344"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "2143289344"]}]}, - {"type": "assert_return", "line": 240, "action": {"type": "invoke", "field": "f32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "4286578688"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "4286578688"]}]}, - {"type": "assert_return", "line": 241, "action": {"type": "invoke", "field": "f32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "2143289344"]}, {"type": "f32", "value": "1078523331"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "1078523331"]}]}, - {"type": "assert_return", "line": 242, "action": {"type": "invoke", "field": "f32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "4286578688"]}, {"type": "f32", "value": "4271273625"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "4271273625"]}]}, - {"type": "assert_return", "line": 243, "action": {"type": "invoke", "field": "f32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "4286578688"]}, {"type": "f32", "value": "4286578687"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "4286578687"]}]}, - {"type": "assert_return", "line": 244, "action": {"type": "invoke", "field": "f32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "4286578688"]}, {"type": "f32", "value": "4278190080"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "4278190080"]}]}, - {"type": "assert_return", "line": 245, "action": {"type": "invoke", "field": "f32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "1820282235"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "1820282235"]}]}, - {"type": "assert_return", "line": 246, "action": {"type": "invoke", "field": "f32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "1820282235"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "1820282235"]}]}, - {"type": "assert_return", "line": 247, "action": {"type": "invoke", "field": "f32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "1695654580"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "1695654580"]}]}, - {"type": "assert_return", "line": 248, "action": {"type": "invoke", "field": "f32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "1376887476"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "1376887476"]}]}, - {"type": "assert_return", "line": 250, "action": {"type": "invoke", "field": "i64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "0"]}, {"type": "i64", "value": "9223372036854775807"}]}, "expected": [{"type": "v128", "lane_type": "i64", "value": ["9223372036854775807", "0"]}]}, - {"type": "assert_return", "line": 251, "action": {"type": "invoke", "field": "i64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "0"]}, {"type": "i64", "value": "18446744073709551615"}]}, "expected": [{"type": "v128", "lane_type": "i64", "value": ["18446744073709551615", "0"]}]}, - {"type": "assert_return", "line": 252, "action": {"type": "invoke", "field": "i64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "0"]}, {"type": "i64", "value": "1234567890123456789"}]}, "expected": [{"type": "v128", "lane_type": "i64", "value": ["1234567890123456789", "0"]}]}, - {"type": "assert_return", "line": 253, "action": {"type": "invoke", "field": "i64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "0"]}, {"type": "i64", "value": "1311768467294899695"}]}, "expected": [{"type": "v128", "lane_type": "i64", "value": ["1311768467294899695", "0"]}]}, - {"type": "assert_return", "line": 254, "action": {"type": "invoke", "field": "i64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "0"]}, {"type": "i64", "value": "9223372036854775808"}]}, "expected": [{"type": "v128", "lane_type": "i64", "value": ["0", "9223372036854775808"]}]}, - {"type": "assert_return", "line": 255, "action": {"type": "invoke", "field": "i64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "0"]}, {"type": "i64", "value": "9223372036854775808"}]}, "expected": [{"type": "v128", "lane_type": "i64", "value": ["0", "9223372036854775808"]}]}, - {"type": "assert_return", "line": 256, "action": {"type": "invoke", "field": "i64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "0"]}, {"type": "i64", "value": "1234567890123456789"}]}, "expected": [{"type": "v128", "lane_type": "i64", "value": ["0", "1234567890123456789"]}]}, - {"type": "assert_return", "line": 257, "action": {"type": "invoke", "field": "i64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "0"]}, {"type": "i64", "value": "1311768467294899695"}]}, "expected": [{"type": "v128", "lane_type": "i64", "value": ["0", "1311768467294899695"]}]}, - {"type": "assert_return", "line": 259, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["4607182418800017408", "4607182418800017408"]}, {"type": "f64", "value": "0"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "4607182418800017408"]}]}, - {"type": "assert_return", "line": 260, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["13830554455654793216", "13830554455654793216"]}, {"type": "f64", "value": "9223372036854775808"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["9223372036854775808", "13830554455654793216"]}]}, - {"type": "assert_return", "line": 261, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "4608308318706860032"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["4608308318706860032", "0"]}]}, - {"type": "assert_return", "line": 262, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "13831680355561635840"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["13831680355561635840", "0"]}]}, - {"type": "assert_return", "line": 263, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["18444492273895866368", "0"]}, {"type": "f64", "value": "18442240474082181119"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["18442240474082181119", "0"]}]}, - {"type": "assert_return", "line": 264, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["9221120237041090560", "0"]}, {"type": "f64", "value": "9218868437227405311"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["9218868437227405311", "0"]}]}, - {"type": "assert_return", "line": 265, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["18442240474082181120", "0"]}, {"type": "f64", "value": "9227875636482146303"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["9227875636482146303", "0"]}]}, - {"type": "assert_return", "line": 266, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["9218868437227405312", "0"]}, {"type": "f64", "value": "4503599627370495"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["4503599627370495", "0"]}]}, - {"type": "assert_return", "line": 267, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "18444492273895866368"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["18444492273895866368", "0"]}]}, - {"type": "assert_return", "line": 268, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "9221120237041090560"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["9221120237041090560", "0"]}]}, - {"type": "assert_return", "line": 269, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "18442240474082181120"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["18442240474082181120", "0"]}]}, - {"type": "assert_return", "line": 270, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "9218868437227405312"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["9218868437227405312", "0"]}]}, - {"type": "assert_return", "line": 271, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "4728057454347157504"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["4728057454347157504", "0"]}]}, - {"type": "assert_return", "line": 272, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "4728057454347157504"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["4728057454347157504", "0"]}]}, - {"type": "assert_return", "line": 273, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "4968090884938317023"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["4968090884938317023", "0"]}]}, - {"type": "assert_return", "line": 274, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "4968090884938317023"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["4968090884938317023", "0"]}]}, - {"type": "assert_return", "line": 275, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["4611686018427387904", "4611686018427387904"]}, {"type": "f64", "value": "0"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["4611686018427387904", "0"]}]}, - {"type": "assert_return", "line": 276, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["13835058055282163712", "13835058055282163712"]}, {"type": "f64", "value": "9223372036854775808"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["13835058055282163712", "9223372036854775808"]}]}, - {"type": "assert_return", "line": 277, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "4612248968380809216"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "4612248968380809216"]}]}, - {"type": "assert_return", "line": 278, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "13835621005235585024"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "13835621005235585024"]}]}, - {"type": "assert_return", "line": 279, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "18444492273895866368"]}, {"type": "f64", "value": "18442240474082181119"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "18442240474082181119"]}]}, - {"type": "assert_return", "line": 280, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "9221120237041090560"]}, {"type": "f64", "value": "9218868437227405311"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "9218868437227405311"]}]}, - {"type": "assert_return", "line": 281, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "18442240474082181120"]}, {"type": "f64", "value": "9227875636482146303"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "9227875636482146303"]}]}, - {"type": "assert_return", "line": 282, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "9218868437227405312"]}, {"type": "f64", "value": "4503599627370495"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "4503599627370495"]}]}, - {"type": "assert_return", "line": 283, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "18444492273895866368"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "18444492273895866368"]}]}, - {"type": "assert_return", "line": 284, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "9221120237041090560"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "9221120237041090560"]}]}, - {"type": "assert_return", "line": 285, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "18442240474082181120"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "18442240474082181120"]}]}, - {"type": "assert_return", "line": 286, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "9218868437227405312"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "9218868437227405312"]}]}, - {"type": "assert_return", "line": 287, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "5012481849648092922"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "5012481849648092922"]}]}, - {"type": "assert_return", "line": 288, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "5012481849648092922"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "5012481849648092922"]}]}, - {"type": "assert_return", "line": 289, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "5012481849648092922"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "5012481849648092922"]}]}, - {"type": "assert_return", "line": 290, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "4443687238071173905"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "4443687238071173905"]}]}, - {"type": "assert_return", "line": 292, "action": {"type": "invoke", "field": "v8x16_swizzle", "args": [{"type": "v128", "lane_type": "i8", "value": ["16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31"]}, {"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31"]}]}, - {"type": "assert_return", "line": 296, "action": {"type": "invoke", "field": "v8x16_swizzle", "args": [{"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}, {"type": "v128", "lane_type": "i8", "value": ["248", "249", "250", "251", "252", "253", "254", "255", "16", "17", "18", "19", "20", "21", "22", "23"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 300, "action": {"type": "invoke", "field": "v8x16_swizzle", "args": [{"type": "v128", "lane_type": "i8", "value": ["100", "101", "102", "103", "104", "105", "106", "107", "108", "109", "110", "111", "112", "113", "114", "115"]}, {"type": "v128", "lane_type": "i8", "value": ["15", "14", "13", "12", "11", "10", "9", "8", "7", "6", "5", "4", "3", "2", "1", "0"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["115", "114", "113", "112", "111", "110", "109", "108", "107", "106", "105", "104", "103", "102", "101", "100"]}]}, - {"type": "assert_return", "line": 304, "action": {"type": "invoke", "field": "v8x16_swizzle", "args": [{"type": "v128", "lane_type": "i8", "value": ["100", "101", "102", "103", "104", "105", "106", "107", "108", "109", "110", "111", "112", "113", "114", "115"]}, {"type": "v128", "lane_type": "i8", "value": ["255", "1", "254", "2", "253", "3", "252", "4", "251", "5", "250", "6", "249", "7", "248", "8"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["0", "101", "0", "102", "0", "103", "0", "104", "0", "105", "0", "106", "0", "107", "0", "108"]}]}, - {"type": "assert_return", "line": 308, "action": {"type": "invoke", "field": "v8x16_swizzle", "args": [{"type": "v128", "lane_type": "i8", "value": ["100", "101", "102", "103", "104", "105", "106", "107", "108", "109", "110", "111", "112", "113", "114", "115"]}, {"type": "v128", "lane_type": "i8", "value": ["9", "16", "10", "17", "11", "18", "12", "19", "13", "20", "14", "21", "15", "22", "16", "23"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["109", "0", "110", "0", "111", "0", "112", "0", "113", "0", "114", "0", "115", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 312, "action": {"type": "invoke", "field": "v8x16_swizzle", "args": [{"type": "v128", "lane_type": "i8", "value": ["100", "101", "102", "103", "104", "105", "106", "107", "108", "109", "110", "111", "112", "113", "114", "115"]}, {"type": "v128", "lane_type": "i8", "value": ["9", "16", "10", "17", "11", "18", "12", "19", "13", "20", "14", "21", "15", "22", "16", "23"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["109", "0", "110", "0", "111", "0", "112", "0", "113", "0", "114", "0", "115", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 316, "action": {"type": "invoke", "field": "v8x16_swizzle", "args": [{"type": "v128", "lane_type": "i16", "value": ["25701", "26215", "26729", "27243", "27757", "28271", "28785", "29299"]}, {"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["25701", "26215", "26729", "27243", "27757", "28271", "28785", "29299"]}]}, - {"type": "assert_return", "line": 320, "action": {"type": "invoke", "field": "v8x16_swizzle", "args": [{"type": "v128", "lane_type": "i32", "value": ["1684366951", "1751738987", "1819111023", "1886483059"]}, {"type": "v128", "lane_type": "i8", "value": ["15", "14", "13", "12", "11", "10", "9", "8", "7", "6", "5", "4", "3", "2", "1", "0"]}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["1936879984", "1869507948", "1802135912", "1734763876"]}]}, - {"type": "assert_return", "line": 324, "action": {"type": "invoke", "field": "v8x16_swizzle", "args": [{"type": "v128", "lane_type": "f32", "value": ["2143289344", "4290772992", "2139095040", "4286578688"]}, {"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["2143289344", "4290772992", "2139095040", "4286578688"]}]}, - {"type": "assert_return", "line": 328, "action": {"type": "invoke", "field": "v8x16_swizzle", "args": [{"type": "v128", "lane_type": "i32", "value": ["1734763876", "1802135912", "1869507932", "1936879984"]}, {"type": "v128", "lane_type": "f32", "value": ["0", "2147483648", "2139095040", "4286578688"]}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["1684300900", "6579300", "25700", "25700"]}]}, - {"type": "assert_return", "line": 333, "action": {"type": "invoke", "field": "v8x16_shuffle-1", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}, {"type": "v128", "lane_type": "i8", "value": ["16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}]}, - {"type": "assert_return", "line": 337, "action": {"type": "invoke", "field": "v8x16_shuffle-2", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}, {"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}]}, - {"type": "assert_return", "line": 341, "action": {"type": "invoke", "field": "v8x16_shuffle-3", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}, {"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["255", "254", "253", "252", "251", "250", "249", "248", "247", "246", "245", "244", "243", "242", "241", "240"]}]}, - {"type": "assert_return", "line": 345, "action": {"type": "invoke", "field": "v8x16_shuffle-4", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}, {"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["15", "14", "13", "12", "11", "10", "9", "8", "7", "6", "5", "4", "3", "2", "1", "0"]}]}, - {"type": "assert_return", "line": 349, "action": {"type": "invoke", "field": "v8x16_shuffle-5", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}, {"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 353, "action": {"type": "invoke", "field": "v8x16_shuffle-6", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}, {"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["240", "240", "240", "240", "240", "240", "240", "240", "240", "240", "240", "240", "240", "240", "240", "240"]}]}, - {"type": "assert_return", "line": 357, "action": {"type": "invoke", "field": "v8x16_shuffle-7", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}, {"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "240", "240", "240", "240", "240", "240", "240", "240"]}]}, - {"type": "assert_return", "line": 361, "action": {"type": "invoke", "field": "v8x16_shuffle-1", "args": [{"type": "v128", "lane_type": "i8", "value": ["100", "101", "102", "103", "104", "105", "106", "107", "108", "109", "110", "111", "112", "113", "114", "115"]}, {"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["100", "101", "102", "103", "104", "105", "106", "107", "108", "109", "110", "111", "112", "113", "114", "115"]}]}, - {"type": "assert_return", "line": 365, "action": {"type": "invoke", "field": "v8x16_shuffle-1", "args": [{"type": "v128", "lane_type": "i16", "value": ["256", "770", "1284", "1798", "2312", "2826", "3340", "3854"]}, {"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["256", "770", "1284", "1798", "2312", "2826", "3340", "3854"]}]}, - {"type": "assert_return", "line": 369, "action": {"type": "invoke", "field": "v8x16_shuffle-2", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}, {"type": "v128", "lane_type": "i32", "value": ["4092785136", "4160157172", "4227529208", "4294901244"]}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["4092785136", "4160157172", "4227529208", "4294901244"]}]}, - {"type": "assert_return", "line": 373, "action": {"type": "invoke", "field": "v8x16_shuffle-1", "args": [{"type": "v128", "lane_type": "i32", "value": ["66051", "67438087", "134810123", "202182159"]}, {"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["66051", "67438087", "134810123", "202182159"]}]}, - {"type": "assert_return", "line": 377, "action": {"type": "invoke", "field": "v8x16_shuffle-1", "args": [{"type": "v128", "lane_type": "f32", "value": ["1065353216", "2143289344", "2139095040", "4286578688"]}, {"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["1065353216", "2143289344", "2139095040", "4286578688"]}]}, - {"type": "assert_return", "line": 381, "action": {"type": "invoke", "field": "v8x16_shuffle-1", "args": [{"type": "v128", "lane_type": "i32", "value": ["66051", "67438087", "134810123", "202182159"]}, {"type": "v128", "lane_type": "f32", "value": ["2147483648", "2143289344", "2139095040", "4286578688"]}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["66051", "67438087", "134810123", "202182159"]}]}, - {"type": "assert_return", "line": 387, "action": {"type": "invoke", "field": "v8x16_swizzle", "args": [{"type": "v128", "lane_type": "i32", "value": ["1234567890", "305419896", "1234567890", "305419896"]}, {"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["1234567890", "305419896", "1234567890", "305419896"]}]}, - {"type": "assert_return", "line": 391, "action": {"type": "invoke", "field": "v8x16_shuffle-1", "args": [{"type": "v128", "lane_type": "i64", "value": ["12345678901234567890", "1311768467294899695"]}, {"type": "v128", "lane_type": "i64", "value": ["12345678901234567890", "1311768467294899695"]}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["3944680146", "2874452364", "2427178479", "305419896"]}]}, - {"type": "assert_malformed", "line": 398, "filename": "simd_lane.1.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 399, "filename": "simd_lane.2.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 400, "filename": "simd_lane.3.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 401, "filename": "simd_lane.4.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 402, "filename": "simd_lane.5.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 403, "filename": "simd_lane.6.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 404, "filename": "simd_lane.7.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 405, "filename": "simd_lane.8.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 406, "filename": "simd_lane.9.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 407, "filename": "simd_lane.10.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 408, "filename": "simd_lane.11.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 409, "filename": "simd_lane.12.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 410, "filename": "simd_lane.13.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 411, "filename": "simd_lane.14.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 415, "filename": "simd_lane.15.wat", "text": "malformed lane index", "module_type": "text"}, - {"type": "assert_malformed", "line": 416, "filename": "simd_lane.16.wat", "text": "malformed lane index", "module_type": "text"}, - {"type": "assert_malformed", "line": 417, "filename": "simd_lane.17.wat", "text": "malformed lane index", "module_type": "text"}, - {"type": "assert_malformed", "line": 418, "filename": "simd_lane.18.wat", "text": "malformed lane index", "module_type": "text"}, - {"type": "assert_malformed", "line": 419, "filename": "simd_lane.19.wat", "text": "malformed lane index", "module_type": "text"}, - {"type": "assert_malformed", "line": 420, "filename": "simd_lane.20.wat", "text": "malformed lane index", "module_type": "text"}, - {"type": "assert_malformed", "line": 421, "filename": "simd_lane.21.wat", "text": "malformed lane index", "module_type": "text"}, - {"type": "assert_malformed", "line": 422, "filename": "simd_lane.22.wat", "text": "malformed lane index", "module_type": "text"}, - {"type": "assert_malformed", "line": 423, "filename": "simd_lane.23.wat", "text": "malformed lane index", "module_type": "text"}, - {"type": "assert_malformed", "line": 424, "filename": "simd_lane.24.wat", "text": "malformed lane index", "module_type": "text"}, - {"type": "assert_malformed", "line": 425, "filename": "simd_lane.25.wat", "text": "malformed lane index", "module_type": "text"}, - {"type": "assert_malformed", "line": 426, "filename": "simd_lane.26.wat", "text": "malformed lane index", "module_type": "text"}, - {"type": "assert_malformed", "line": 427, "filename": "simd_lane.27.wat", "text": "malformed lane index", "module_type": "text"}, - {"type": "assert_malformed", "line": 428, "filename": "simd_lane.28.wat", "text": "malformed lane index", "module_type": "text"}, - {"type": "assert_invalid", "line": 432, "filename": "simd_lane.29.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 433, "filename": "simd_lane.30.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 434, "filename": "simd_lane.31.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 435, "filename": "simd_lane.32.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 436, "filename": "simd_lane.33.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 437, "filename": "simd_lane.34.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 438, "filename": "simd_lane.35.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 439, "filename": "simd_lane.36.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 440, "filename": "simd_lane.37.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 441, "filename": "simd_lane.38.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 442, "filename": "simd_lane.39.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 443, "filename": "simd_lane.40.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 444, "filename": "simd_lane.41.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 445, "filename": "simd_lane.42.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 446, "filename": "simd_lane.43.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 447, "filename": "simd_lane.44.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 448, "filename": "simd_lane.45.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 449, "filename": "simd_lane.46.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 450, "filename": "simd_lane.47.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 451, "filename": "simd_lane.48.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 452, "filename": "simd_lane.49.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 453, "filename": "simd_lane.50.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 454, "filename": "simd_lane.51.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 455, "filename": "simd_lane.52.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 456, "filename": "simd_lane.53.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 457, "filename": "simd_lane.54.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 458, "filename": "simd_lane.55.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 459, "filename": "simd_lane.56.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 463, "filename": "simd_lane.57.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 464, "filename": "simd_lane.58.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 465, "filename": "simd_lane.59.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 466, "filename": "simd_lane.60.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 467, "filename": "simd_lane.61.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 468, "filename": "simd_lane.62.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 469, "filename": "simd_lane.63.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 470, "filename": "simd_lane.64.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 471, "filename": "simd_lane.65.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 472, "filename": "simd_lane.66.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 473, "filename": "simd_lane.67.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_invalid", "line": 477, "filename": "simd_lane.68.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 478, "filename": "simd_lane.69.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 479, "filename": "simd_lane.70.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 480, "filename": "simd_lane.71.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 481, "filename": "simd_lane.72.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 482, "filename": "simd_lane.73.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 483, "filename": "simd_lane.74.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 484, "filename": "simd_lane.75.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 485, "filename": "simd_lane.76.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 486, "filename": "simd_lane.77.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 487, "filename": "simd_lane.78.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 488, "filename": "simd_lane.79.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 489, "filename": "simd_lane.80.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 490, "filename": "simd_lane.81.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 494, "filename": "simd_lane.82.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 495, "filename": "simd_lane.83.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 496, "filename": "simd_lane.84.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 497, "filename": "simd_lane.85.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 499, "filename": "simd_lane.86.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 500, "filename": "simd_lane.87.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 503, "filename": "simd_lane.88.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 505, "filename": "simd_lane.89.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 507, "filename": "simd_lane.90.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 510, "filename": "simd_lane.91.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_malformed", "line": 515, "filename": "simd_lane.92.wat", "text": "invalid lane length", "module_type": "text"}, - {"type": "assert_malformed", "line": 518, "filename": "simd_lane.93.wat", "text": "invalid lane length", "module_type": "text"}, - {"type": "assert_malformed", "line": 521, "filename": "simd_lane.94.wat", "text": "malformed lane index", "module_type": "text"}, - {"type": "assert_malformed", "line": 525, "filename": "simd_lane.95.wat", "text": "malformed lane index", "module_type": "text"}, - {"type": "assert_invalid", "line": 529, "filename": "simd_lane.96.wasm", "text": "invalid lane index", "module_type": "binary"}, - {"type": "assert_malformed", "line": 536, "filename": "simd_lane.97.wat", "text": "unknown operator", "module_type": "text"}, - {"type": "assert_malformed", "line": 537, "filename": "simd_lane.98.wat", "text": "unknown operator", "module_type": "text"}, - {"type": "assert_malformed", "line": 538, "filename": "simd_lane.99.wat", "text": "unknown operator", "module_type": "text"}, - {"type": "assert_malformed", "line": 539, "filename": "simd_lane.100.wat", "text": "unknown operator", "module_type": "text"}, - {"type": "assert_malformed", "line": 540, "filename": "simd_lane.101.wat", "text": "unknown operator", "module_type": "text"}, - {"type": "assert_malformed", "line": 541, "filename": "simd_lane.102.wat", "text": "unknown operator", "module_type": "text"}, - {"type": "assert_malformed", "line": 545, "filename": "simd_lane.103.wat", "text": "unknown operator", "module_type": "text"}, - {"type": "assert_malformed", "line": 549, "filename": "simd_lane.104.wat", "text": "unknown operator", "module_type": "text"}, - {"type": "assert_malformed", "line": 555, "filename": "simd_lane.105.wat", "text": "unknown operator", "module_type": "text"}, - {"type": "assert_malformed", "line": 559, "filename": "simd_lane.106.wat", "text": "unknown operator", "module_type": "text"}, - {"type": "assert_malformed", "line": 570, "filename": "simd_lane.107.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 571, "filename": "simd_lane.108.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 572, "filename": "simd_lane.109.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 573, "filename": "simd_lane.110.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 574, "filename": "simd_lane.111.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 575, "filename": "simd_lane.112.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 576, "filename": "simd_lane.113.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 577, "filename": "simd_lane.114.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 578, "filename": "simd_lane.115.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 579, "filename": "simd_lane.116.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 581, "filename": "simd_lane.117.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 582, "filename": "simd_lane.118.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 583, "filename": "simd_lane.119.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 584, "filename": "simd_lane.120.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 588, "filename": "simd_lane.121.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 589, "filename": "simd_lane.122.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 590, "filename": "simd_lane.123.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 591, "filename": "simd_lane.124.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 592, "filename": "simd_lane.125.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 593, "filename": "simd_lane.126.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 594, "filename": "simd_lane.127.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 595, "filename": "simd_lane.128.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 596, "filename": "simd_lane.129.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 597, "filename": "simd_lane.130.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 600, "filename": "simd_lane.131.wat", "text": "invalid lane length", "module_type": "text"}, - {"type": "assert_malformed", "line": 604, "filename": "simd_lane.132.wat", "text": "malformed lane index", "module_type": "text"}, - {"type": "assert_malformed", "line": 608, "filename": "simd_lane.133.wat", "text": "malformed lane index", "module_type": "text"}, - {"type": "assert_malformed", "line": 612, "filename": "simd_lane.134.wat", "text": "malformed lane index", "module_type": "text"}, - {"type": "assert_malformed", "line": 616, "filename": "simd_lane.135.wat", "text": "malformed lane index", "module_type": "text"}, - {"type": "assert_malformed", "line": 620, "filename": "simd_lane.136.wat", "text": "malformed lane index", "module_type": "text"}, - {"type": "module", "line": 628, "filename": "simd_lane.137.wasm"}, - {"type": "assert_return", "line": 674, "action": {"type": "invoke", "field": "i8x16_extract_lane_s", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "v128", "lane_type": "i8", "value": ["255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["255", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 675, "action": {"type": "invoke", "field": "i8x16_extract_lane_u", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "v128", "lane_type": "i8", "value": ["255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["255", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 676, "action": {"type": "invoke", "field": "i16x8_extract_lane_s", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "v128", "lane_type": "i16", "value": ["65535", "65535", "65535", "65535", "65535", "65535", "65535", "65535"]}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["65535", "0", "0", "0", "0", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 677, "action": {"type": "invoke", "field": "i16x8_extract_lane_u", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "v128", "lane_type": "i16", "value": ["65535", "65535", "65535", "65535", "65535", "65535", "65535", "65535"]}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["65535", "0", "0", "0", "0", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 678, "action": {"type": "invoke", "field": "i32x4_extract_lane", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}, {"type": "v128", "lane_type": "i32", "value": ["65536", "4294967295", "4294967295", "4294967295"]}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["65536", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 679, "action": {"type": "invoke", "field": "f32x4_extract_lane", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "v128", "lane_type": "f32", "value": ["2123789977", "2143289344", "2143289344", "2143289344"]}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["2123789977", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 680, "action": {"type": "invoke", "field": "i8x16_replace_lane-s", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "255"}]}, "expected": [{"type": "i32", "value": "4294967295"}]}, - {"type": "assert_return", "line": 681, "action": {"type": "invoke", "field": "i8x16_replace_lane-u", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "255"}]}, "expected": [{"type": "i32", "value": "255"}]}, - {"type": "assert_return", "line": 682, "action": {"type": "invoke", "field": "i16x8_replace_lane-s", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "65535"}]}, "expected": [{"type": "i32", "value": "4294967295"}]}, - {"type": "assert_return", "line": 683, "action": {"type": "invoke", "field": "i16x8_replace_lane-u", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "65535"}]}, "expected": [{"type": "i32", "value": "65535"}]}, - {"type": "assert_return", "line": 684, "action": {"type": "invoke", "field": "i32x4_replace_lane", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}, {"type": "i32", "value": "4294967295"}]}, "expected": [{"type": "i32", "value": "4294967295"}]}, - {"type": "assert_return", "line": 685, "action": {"type": "invoke", "field": "f32x4_replace_lane", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "1067450368"}]}, "expected": [{"type": "f32", "value": "1067450368"}]}, - {"type": "assert_return", "line": 687, "action": {"type": "invoke", "field": "i64x2_extract_lane", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "0"]}, {"type": "v128", "lane_type": "i64", "value": ["18446744073709551615", "18446744073709551615"]}]}, "expected": [{"type": "v128", "lane_type": "i64", "value": ["18446744073709551615", "0"]}]}, - {"type": "assert_return", "line": 688, "action": {"type": "invoke", "field": "f64x2_extract_lane", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "v128", "lane_type": "f64", "value": ["9214871658872686752", "9221120237041090560"]}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["9214871658872686752", "0"]}]}, - {"type": "assert_return", "line": 689, "action": {"type": "invoke", "field": "i64x2_replace_lane", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "0"]}, {"type": "i64", "value": "18446744073709551615"}]}, "expected": [{"type": "i64", "value": "18446744073709551615"}]}, - {"type": "assert_return", "line": 690, "action": {"type": "invoke", "field": "f64x2_replace_lane", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "4612811918334230528"}]}, "expected": [{"type": "f64", "value": "4612811918334230528"}]}, - {"type": "assert_return", "line": 692, "action": {"type": "invoke", "field": "as-v8x16_swizzle-operand", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}, {"type": "i32", "value": "255"}, {"type": "v128", "lane_type": "i8", "value": ["255", "15", "14", "13", "12", "11", "10", "9", "8", "7", "6", "5", "4", "3", "2", "1"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["0", "15", "14", "13", "12", "11", "10", "9", "8", "7", "6", "5", "4", "3", "2", "1"]}]}, - {"type": "assert_return", "line": 696, "action": {"type": "invoke", "field": "as-v8x16_shuffle-operands", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "255", "0", "255", "15", "255", "0", "255", "255", "255", "0", "255", "127", "255", "0", "255"]}, {"type": "i32", "value": "1"}, {"type": "v128", "lane_type": "i8", "value": ["85", "0", "85", "0", "85", "0", "85", "0", "85", "0", "85", "0", "85", "1", "85", "255"]}, {"type": "i32", "value": "0"}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["85", "255", "85", "255", "85", "255", "85", "255", "85", "255", "85", "255", "85", "255", "85", "255"]}]}, - {"type": "module", "line": 703, "filename": "simd_lane.138.wasm"}, - {"type": "assert_return", "line": 750, "action": {"type": "invoke", "field": "as-i8x16_splat-operand", "args": [{"type": "v128", "lane_type": "i8", "value": ["255", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255"]}]}, - {"type": "assert_return", "line": 751, "action": {"type": "invoke", "field": "as-i16x8_splat-operand", "args": [{"type": "v128", "lane_type": "i16", "value": ["65535", "65535", "65535", "65535", "0", "0", "0", "0"]}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["65535", "65535", "65535", "65535", "65535", "65535", "65535", "65535"]}]}, - {"type": "assert_return", "line": 752, "action": {"type": "invoke", "field": "as-i32x4_splat-operand", "args": [{"type": "v128", "lane_type": "i32", "value": ["65536", "0", "0", "0"]}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["65536", "65536", "65536", "65536"]}]}, - {"type": "assert_return", "line": 753, "action": {"type": "invoke", "field": "as-f32x4_splat-operand", "args": [{"type": "v128", "lane_type": "f32", "value": ["1078523331", "2143289344", "2143289344", "2143289344"]}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["1078523331", "1078523331", "1078523331", "1078523331"]}]}, - {"type": "assert_return", "line": 754, "action": {"type": "invoke", "field": "as-i64x2_splat-operand", "args": [{"type": "v128", "lane_type": "i64", "value": ["18446744073709551615", "0"]}]}, "expected": [{"type": "v128", "lane_type": "i64", "value": ["18446744073709551615", "18446744073709551615"]}]}, - {"type": "assert_return", "line": 755, "action": {"type": "invoke", "field": "as-f64x2_splat-operand", "args": [{"type": "v128", "lane_type": "f64", "value": ["9218868437227405312", "9221120237041090560"]}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["9218868437227405312", "9218868437227405312"]}]}, - {"type": "assert_return", "line": 756, "action": {"type": "invoke", "field": "as-i8x16_add-operands", "args": [{"type": "v128", "lane_type": "i8", "value": ["255", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16"]}, {"type": "i32", "value": "1"}, {"type": "v128", "lane_type": "i8", "value": ["16", "15", "14", "13", "12", "11", "10", "9", "8", "7", "6", "5", "4", "3", "2", "255"]}, {"type": "i32", "value": "1"}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["17", "17", "17", "17", "17", "17", "17", "17", "17", "17", "17", "17", "17", "17", "17", "17"]}]}, - {"type": "assert_return", "line": 760, "action": {"type": "invoke", "field": "as-i16x8_add-operands", "args": [{"type": "v128", "lane_type": "i16", "value": ["65535", "4", "9", "16", "25", "36", "49", "64"]}, {"type": "i32", "value": "1"}, {"type": "v128", "lane_type": "i16", "value": ["64", "49", "36", "25", "16", "9", "4", "65535"]}, {"type": "i32", "value": "1"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["65", "53", "45", "41", "41", "45", "53", "65"]}]}, - {"type": "assert_return", "line": 764, "action": {"type": "invoke", "field": "as-i32x4_add-operands", "args": [{"type": "v128", "lane_type": "i32", "value": ["4294967295", "8", "27", "64"]}, {"type": "i32", "value": "1"}, {"type": "v128", "lane_type": "i32", "value": ["64", "27", "8", "4294967295"]}, {"type": "i32", "value": "1"}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["65", "35", "35", "65"]}]}, - {"type": "assert_return", "line": 766, "action": {"type": "invoke", "field": "as-i64x2_add-operands", "args": [{"type": "v128", "lane_type": "i64", "value": ["18446744073709551615", "8"]}, {"type": "i64", "value": "1"}, {"type": "v128", "lane_type": "i64", "value": ["64", "27"]}, {"type": "i64", "value": "1"}]}, "expected": [{"type": "v128", "lane_type": "i64", "value": ["65", "9"]}]}, - {"type": "assert_return", "line": 769, "action": {"type": "invoke", "field": "swizzle-as-i8x16_add-operands", "args": [{"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}, {"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}, {"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}, {"type": "v128", "lane_type": "i8", "value": ["15", "14", "13", "12", "11", "10", "9", "8", "7", "6", "5", "4", "3", "2", "1", "0"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255"]}]}, - {"type": "assert_return", "line": 775, "action": {"type": "invoke", "field": "shuffle-as-i8x16_sub-operands", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}, {"type": "v128", "lane_type": "i8", "value": ["15", "14", "13", "12", "11", "10", "9", "8", "7", "6", "5", "4", "3", "2", "1", "0"]}, {"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}, {"type": "v128", "lane_type": "i8", "value": ["15", "14", "13", "12", "11", "10", "9", "8", "7", "6", "5", "4", "3", "2", "1", "0"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["241", "243", "245", "247", "249", "251", "253", "255", "1", "3", "5", "7", "9", "11", "13", "15"]}]}, - {"type": "assert_return", "line": 782, "action": {"type": "invoke", "field": "as-i8x16_any_true-operand", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "1"}]}, "expected": [{"type": "i32", "value": "1"}]}, - {"type": "assert_return", "line": 783, "action": {"type": "invoke", "field": "as-i16x8_any_true-operand", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "1"}]}, "expected": [{"type": "i32", "value": "1"}]}, - {"type": "assert_return", "line": 784, "action": {"type": "invoke", "field": "as-i32x4_any_true-operand1", "args": [{"type": "v128", "lane_type": "i32", "value": ["1", "0", "0", "0"]}, {"type": "i32", "value": "0"}]}, "expected": [{"type": "i32", "value": "0"}]}, - {"type": "assert_return", "line": 785, "action": {"type": "invoke", "field": "as-i32x4_any_true-operand2", "args": [{"type": "v128", "lane_type": "i64", "value": ["1", "0"]}, {"type": "i64", "value": "0"}]}, "expected": [{"type": "i32", "value": "0"}]}, - {"type": "assert_return", "line": 787, "action": {"type": "invoke", "field": "swizzle-as-i8x16_all_true-operands", "args": [{"type": "v128", "lane_type": "i8", "value": ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16"]}, {"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}]}, "expected": [{"type": "i32", "value": "1"}]}, - {"type": "assert_return", "line": 790, "action": {"type": "invoke", "field": "swizzle-as-i8x16_all_true-operands", "args": [{"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}, {"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "16"]}]}, "expected": [{"type": "i32", "value": "0"}]}, - {"type": "assert_return", "line": 793, "action": {"type": "invoke", "field": "shuffle-as-i8x16_any_true-operands", "args": [{"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}, {"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}]}, "expected": [{"type": "i32", "value": "1"}]}, - {"type": "module", "line": 799, "filename": "simd_lane.139.wasm"}, - {"type": "assert_return", "line": 821, "action": {"type": "invoke", "field": "as-v128_store-operand-1", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "1"}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["1", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 822, "action": {"type": "invoke", "field": "as-v128_store-operand-2", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "256"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["256", "0", "0", "0", "0", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 823, "action": {"type": "invoke", "field": "as-v128_store-operand-3", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}, {"type": "i32", "value": "4294967295"}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["4294967295", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 824, "action": {"type": "invoke", "field": "as-v128_store-operand-4", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "1078523331"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["1078523331", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 825, "action": {"type": "invoke", "field": "as-v128_store-operand-5", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "0"]}, {"type": "i64", "value": "18446744073709551615"}]}, "expected": [{"type": "v128", "lane_type": "i64", "value": ["18446744073709551615", "0"]}]}, - {"type": "assert_return", "line": 826, "action": {"type": "invoke", "field": "as-v128_store-operand-6", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "4614253070214989087"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["4614253070214989087", "0"]}]}, - {"type": "module", "line": 830, "filename": "simd_lane.140.wasm"}, - {"type": "assert_return", "line": 858, "action": {"type": "invoke", "field": "as-if-condition-value", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "0"}]}, - {"type": "assert_return", "line": 859, "action": {"type": "invoke", "field": "as-return-value-1", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "1"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["1", "0", "0", "0", "0", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 860, "action": {"type": "invoke", "field": "as-local_set-value", "args": [{"type": "v128", "lane_type": "i32", "value": ["4294967295", "4294967295", "4294967295", "4294967295"]}]}, "expected": [{"type": "i32", "value": "4294967295"}]}, - {"type": "assert_return", "line": 861, "action": {"type": "invoke", "field": "as-global_set-value-1", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "1078523331"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["1078523331", "0", "0", "0"]}]}, - {"type": "assert_return", "line": 863, "action": {"type": "invoke", "field": "as-return-value-2", "args": [{"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}, {"type": "v128", "lane_type": "i8", "value": ["15", "14", "13", "12", "11", "10", "9", "8", "7", "6", "5", "4", "3", "2", "1", "0"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["255", "254", "253", "252", "251", "250", "249", "248", "247", "246", "245", "244", "243", "242", "241", "240"]}]}, - {"type": "assert_return", "line": 867, "action": {"type": "invoke", "field": "as-global_set-value-2", "args": [{"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}, {"type": "v128", "lane_type": "i8", "value": ["16", "15", "14", "13", "12", "11", "10", "9", "8", "7", "6", "5", "4", "3", "2", "1"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "8", "7", "6", "5", "4", "3", "2", "1"]}]}, - {"type": "assert_return", "line": 872, "action": {"type": "invoke", "field": "as-local_set-value-1", "args": [{"type": "v128", "lane_type": "i64", "value": ["18446744073709551615", "18446744073709551615"]}]}, "expected": [{"type": "i64", "value": "18446744073709551615"}]}, - {"type": "assert_return", "line": 873, "action": {"type": "invoke", "field": "as-global_set-value-3", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "4614253070214989087"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["4614253070214989087", "0"]}]}, - {"type": "assert_malformed", "line": 877, "filename": "simd_lane.141.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 878, "filename": "simd_lane.142.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 879, "filename": "simd_lane.143.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 880, "filename": "simd_lane.144.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 881, "filename": "simd_lane.145.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 882, "filename": "simd_lane.146.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 883, "filename": "simd_lane.147.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "module", "line": 887, "filename": "simd_lane.148.wasm"}, - {"type": "module", "line": 888, "filename": "simd_lane.149.wasm"}, - {"type": "module", "line": 889, "filename": "simd_lane.150.wasm"}, - {"type": "module", "line": 890, "filename": "simd_lane.151.wasm"}, - {"type": "module", "line": 891, "filename": "simd_lane.152.wasm"}, - {"type": "module", "line": 892, "filename": "simd_lane.153.wasm"}, - {"type": "module", "line": 893, "filename": "simd_lane.154.wasm"}, - {"type": "assert_malformed", "line": 897, "filename": "simd_lane.155.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 902, "filename": "simd_lane.156.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_invalid", "line": 910, "filename": "simd_lane.157.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_malformed", "line": 918, "filename": "simd_lane.158.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 926, "filename": "simd_lane.159.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_invalid", "line": 934, "filename": "simd_lane.160.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_malformed", "line": 942, "filename": "simd_lane.161.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 950, "filename": "simd_lane.162.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_invalid", "line": 958, "filename": "simd_lane.163.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_malformed", "line": 966, "filename": "simd_lane.164.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 974, "filename": "simd_lane.165.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_invalid", "line": 982, "filename": "simd_lane.166.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_malformed", "line": 990, "filename": "simd_lane.167.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 998, "filename": "simd_lane.168.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_invalid", "line": 1006, "filename": "simd_lane.169.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_malformed", "line": 1014, "filename": "simd_lane.170.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 1022, "filename": "simd_lane.171.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_invalid", "line": 1030, "filename": "simd_lane.172.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_malformed", "line": 1038, "filename": "simd_lane.173.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 1046, "filename": "simd_lane.174.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_invalid", "line": 1054, "filename": "simd_lane.175.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 1062, "filename": "simd_lane.176.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_malformed", "line": 1070, "filename": "simd_lane.177.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 1078, "filename": "simd_lane.178.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_invalid", "line": 1086, "filename": "simd_lane.179.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 1094, "filename": "simd_lane.180.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_malformed", "line": 1102, "filename": "simd_lane.181.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 1110, "filename": "simd_lane.182.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_invalid", "line": 1118, "filename": "simd_lane.183.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 1126, "filename": "simd_lane.184.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_malformed", "line": 1134, "filename": "simd_lane.185.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 1142, "filename": "simd_lane.186.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_invalid", "line": 1150, "filename": "simd_lane.187.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 1158, "filename": "simd_lane.188.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_malformed", "line": 1166, "filename": "simd_lane.189.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 1174, "filename": "simd_lane.190.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_invalid", "line": 1182, "filename": "simd_lane.191.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 1190, "filename": "simd_lane.192.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_malformed", "line": 1198, "filename": "simd_lane.193.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 1206, "filename": "simd_lane.194.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_invalid", "line": 1214, "filename": "simd_lane.195.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_invalid", "line": 1222, "filename": "simd_lane.196.wasm", "text": "type mismatch", "module_type": "binary"}, - {"type": "assert_malformed", "line": 1230, "filename": "simd_lane.197.wat", "text": "unexpected token", "module_type": "text"}, - {"type": "assert_malformed", "line": 1238, "filename": "simd_lane.198.wat", "text": "invalid lane length", "module_type": "text"}, - {"type": "assert_invalid", "line": 1249, "filename": "simd_lane.199.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "module", "line": 4, "filename": "simd_lane.0.wasm"}, + {"type": "assert_return", "line": 81, "action": {"type": "invoke", "field": "i8x16_extract_lane_s-first", "args": [{"type": "v128", "lane_type": "i8", "value": ["127", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "127"}]}, + {"type": "assert_return", "line": 82, "action": {"type": "invoke", "field": "i8x16_extract_lane_s-first", "args": [{"type": "v128", "lane_type": "i8", "value": ["127", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "127"}]}, + {"type": "assert_return", "line": 83, "action": {"type": "invoke", "field": "i8x16_extract_lane_s-first", "args": [{"type": "v128", "lane_type": "i8", "value": ["255", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "4294967295"}]}, + {"type": "assert_return", "line": 84, "action": {"type": "invoke", "field": "i8x16_extract_lane_s-first", "args": [{"type": "v128", "lane_type": "i8", "value": ["255", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "4294967295"}]}, + {"type": "assert_return", "line": 85, "action": {"type": "invoke", "field": "i8x16_extract_lane_u-first", "args": [{"type": "v128", "lane_type": "i8", "value": ["255", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "255"}]}, + {"type": "assert_return", "line": 86, "action": {"type": "invoke", "field": "i8x16_extract_lane_u-first", "args": [{"type": "v128", "lane_type": "i8", "value": ["255", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "255"}]}, + {"type": "assert_return", "line": 87, "action": {"type": "invoke", "field": "i8x16_extract_lane_s-last", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "128"]}]}, "expected": [{"type": "i32", "value": "4294967168"}]}, + {"type": "assert_return", "line": 88, "action": {"type": "invoke", "field": "i8x16_extract_lane_s-last", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "128"]}]}, "expected": [{"type": "i32", "value": "4294967168"}]}, + {"type": "assert_return", "line": 89, "action": {"type": "invoke", "field": "i8x16_extract_lane_u-last", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "255"]}]}, "expected": [{"type": "i32", "value": "255"}]}, + {"type": "assert_return", "line": 90, "action": {"type": "invoke", "field": "i8x16_extract_lane_u-last", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "255"]}]}, "expected": [{"type": "i32", "value": "255"}]}, + {"type": "assert_return", "line": 91, "action": {"type": "invoke", "field": "i8x16_extract_lane_u-last", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "128"]}]}, "expected": [{"type": "i32", "value": "128"}]}, + {"type": "assert_return", "line": 92, "action": {"type": "invoke", "field": "i8x16_extract_lane_u-last", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "128"]}]}, "expected": [{"type": "i32", "value": "128"}]}, + {"type": "assert_return", "line": 94, "action": {"type": "invoke", "field": "i16x8_extract_lane_s-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["32767", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "32767"}]}, + {"type": "assert_return", "line": 95, "action": {"type": "invoke", "field": "i16x8_extract_lane_s-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["32767", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "32767"}]}, + {"type": "assert_return", "line": 96, "action": {"type": "invoke", "field": "i16x8_extract_lane_s-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["65535", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "4294967295"}]}, + {"type": "assert_return", "line": 97, "action": {"type": "invoke", "field": "i16x8_extract_lane_s-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["65535", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "4294967295"}]}, + {"type": "assert_return", "line": 98, "action": {"type": "invoke", "field": "i16x8_extract_lane_s-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["12345", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "12345"}]}, + {"type": "assert_return", "line": 99, "action": {"type": "invoke", "field": "i16x8_extract_lane_s-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["60876", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "4294962636"}]}, + {"type": "assert_return", "line": 100, "action": {"type": "invoke", "field": "i16x8_extract_lane_u-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["65535", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "65535"}]}, + {"type": "assert_return", "line": 101, "action": {"type": "invoke", "field": "i16x8_extract_lane_u-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["65535", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "65535"}]}, + {"type": "assert_return", "line": 102, "action": {"type": "invoke", "field": "i16x8_extract_lane_u-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["12345", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "12345"}]}, + {"type": "assert_return", "line": 103, "action": {"type": "invoke", "field": "i16x8_extract_lane_u-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["60876", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "60876"}]}, + {"type": "assert_return", "line": 104, "action": {"type": "invoke", "field": "i16x8_extract_lane_s-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "32768"]}]}, "expected": [{"type": "i32", "value": "4294934528"}]}, + {"type": "assert_return", "line": 105, "action": {"type": "invoke", "field": "i16x8_extract_lane_s-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "32768"]}]}, "expected": [{"type": "i32", "value": "4294934528"}]}, + {"type": "assert_return", "line": 106, "action": {"type": "invoke", "field": "i16x8_extract_lane_s-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "6789"]}]}, "expected": [{"type": "i32", "value": "6789"}]}, + {"type": "assert_return", "line": 107, "action": {"type": "invoke", "field": "i16x8_extract_lane_s-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "39031"]}]}, "expected": [{"type": "i32", "value": "4294940791"}]}, + {"type": "assert_return", "line": 108, "action": {"type": "invoke", "field": "i16x8_extract_lane_u-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "65535"]}]}, "expected": [{"type": "i32", "value": "65535"}]}, + {"type": "assert_return", "line": 109, "action": {"type": "invoke", "field": "i16x8_extract_lane_u-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "65535"]}]}, "expected": [{"type": "i32", "value": "65535"}]}, + {"type": "assert_return", "line": 110, "action": {"type": "invoke", "field": "i16x8_extract_lane_u-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "32768"]}]}, "expected": [{"type": "i32", "value": "32768"}]}, + {"type": "assert_return", "line": 111, "action": {"type": "invoke", "field": "i16x8_extract_lane_u-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "32768"]}]}, "expected": [{"type": "i32", "value": "32768"}]}, + {"type": "assert_return", "line": 112, "action": {"type": "invoke", "field": "i16x8_extract_lane_u-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "6789"]}]}, "expected": [{"type": "i32", "value": "6789"}]}, + {"type": "assert_return", "line": 113, "action": {"type": "invoke", "field": "i16x8_extract_lane_u-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "39031"]}]}, "expected": [{"type": "i32", "value": "39031"}]}, + {"type": "assert_return", "line": 115, "action": {"type": "invoke", "field": "i32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "i32", "value": ["2147483647", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "2147483647"}]}, + {"type": "assert_return", "line": 116, "action": {"type": "invoke", "field": "i32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "i32", "value": ["2147483647", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "2147483647"}]}, + {"type": "assert_return", "line": 117, "action": {"type": "invoke", "field": "i32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "i32", "value": ["4294967295", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "4294967295"}]}, + {"type": "assert_return", "line": 118, "action": {"type": "invoke", "field": "i32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "i32", "value": ["4294967295", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "4294967295"}]}, + {"type": "assert_return", "line": 119, "action": {"type": "invoke", "field": "i32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "i32", "value": ["1234567890", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "1234567890"}]}, + {"type": "assert_return", "line": 120, "action": {"type": "invoke", "field": "i32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "i32", "value": ["3989547400", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "3989547400"}]}, + {"type": "assert_return", "line": 121, "action": {"type": "invoke", "field": "i32x4_extract_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "2147483648"]}]}, "expected": [{"type": "i32", "value": "2147483648"}]}, + {"type": "assert_return", "line": 122, "action": {"type": "invoke", "field": "i32x4_extract_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "2147483648"]}]}, "expected": [{"type": "i32", "value": "2147483648"}]}, + {"type": "assert_return", "line": 123, "action": {"type": "invoke", "field": "i32x4_extract_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "4294967295"]}]}, "expected": [{"type": "i32", "value": "4294967295"}]}, + {"type": "assert_return", "line": 124, "action": {"type": "invoke", "field": "i32x4_extract_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "4294967295"]}]}, "expected": [{"type": "i32", "value": "4294967295"}]}, + {"type": "assert_return", "line": 125, "action": {"type": "invoke", "field": "i32x4_extract_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "987654321"]}]}, "expected": [{"type": "i32", "value": "987654321"}]}, + {"type": "assert_return", "line": 126, "action": {"type": "invoke", "field": "i32x4_extract_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "3989547400"]}]}, "expected": [{"type": "i32", "value": "3989547400"}]}, + {"type": "assert_return", "line": 128, "action": {"type": "invoke", "field": "i64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "i64", "value": ["9223372036854775807", "0"]}]}, "expected": [{"type": "i64", "value": "9223372036854775807"}]}, + {"type": "assert_return", "line": 129, "action": {"type": "invoke", "field": "i64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "i64", "value": ["9223372036854775806", "0"]}]}, "expected": [{"type": "i64", "value": "9223372036854775806"}]}, + {"type": "assert_return", "line": 130, "action": {"type": "invoke", "field": "i64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "i64", "value": ["18446744073709551615", "0"]}]}, "expected": [{"type": "i64", "value": "18446744073709551615"}]}, + {"type": "assert_return", "line": 131, "action": {"type": "invoke", "field": "i64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "i64", "value": ["18446744073709551615", "0"]}]}, "expected": [{"type": "i64", "value": "18446744073709551615"}]}, + {"type": "assert_return", "line": 132, "action": {"type": "invoke", "field": "i64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "i64", "value": ["1234567890123456789", "0"]}]}, "expected": [{"type": "i64", "value": "1234567890123456789"}]}, + {"type": "assert_return", "line": 133, "action": {"type": "invoke", "field": "i64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "i64", "value": ["1311768467294899695", "0"]}]}, "expected": [{"type": "i64", "value": "1311768467294899695"}]}, + {"type": "assert_return", "line": 134, "action": {"type": "invoke", "field": "i64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "9223372036854775808"]}]}, "expected": [{"type": "i64", "value": "9223372036854775808"}]}, + {"type": "assert_return", "line": 135, "action": {"type": "invoke", "field": "i64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "9223372036854775808"]}]}, "expected": [{"type": "i64", "value": "9223372036854775808"}]}, + {"type": "assert_return", "line": 136, "action": {"type": "invoke", "field": "i64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "9223372036854775808"]}]}, "expected": [{"type": "i64", "value": "9223372036854775808"}]}, + {"type": "assert_return", "line": 137, "action": {"type": "invoke", "field": "i64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "255", "255", "255", "255", "255", "255", "255", "127"]}]}, "expected": [{"type": "i64", "value": "9223372036854775807"}]}, + {"type": "assert_return", "line": 138, "action": {"type": "invoke", "field": "i64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "32768"]}]}, "expected": [{"type": "i64", "value": "9223372036854775808"}]}, + {"type": "assert_return", "line": 139, "action": {"type": "invoke", "field": "i64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "4294967295", "2147483647"]}]}, "expected": [{"type": "i64", "value": "9223372036854775807"}]}, + {"type": "assert_return", "line": 140, "action": {"type": "invoke", "field": "i64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["18442240474082181120", "9218868437227405312"]}]}, "expected": [{"type": "i64", "value": "9218868437227405312"}]}, + {"type": "assert_return", "line": 141, "action": {"type": "invoke", "field": "i64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "1234567890123456789"]}]}, "expected": [{"type": "i64", "value": "1234567890123456789"}]}, + {"type": "assert_return", "line": 142, "action": {"type": "invoke", "field": "i64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "1311768467294899695"]}]}, "expected": [{"type": "i64", "value": "1311768467294899695"}]}, + {"type": "assert_return", "line": 144, "action": {"type": "invoke", "field": "f32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["3231711232", "0", "0", "0"]}]}, "expected": [{"type": "f32", "value": "3231711232"}]}, + {"type": "assert_return", "line": 145, "action": {"type": "invoke", "field": "f32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["2123789977", "0", "0", "0"]}]}, "expected": [{"type": "f32", "value": "2123789977"}]}, + {"type": "assert_return", "line": 146, "action": {"type": "invoke", "field": "f32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["2139095039", "0", "0", "0"]}]}, "expected": [{"type": "f32", "value": "2139095039"}]}, + {"type": "assert_return", "line": 147, "action": {"type": "invoke", "field": "f32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["2130706432", "0", "0", "0"]}]}, "expected": [{"type": "f32", "value": "2130706432"}]}, + {"type": "assert_return", "line": 148, "action": {"type": "invoke", "field": "f32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["2139095040", "0", "0", "0"]}]}, "expected": [{"type": "f32", "value": "2139095040"}]}, + {"type": "assert_return", "line": 149, "action": {"type": "invoke", "field": "f32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["2143289344", "2139095040", "0", "0"]}]}, "expected": [{"type": "f32", "value": "2143289344"}]}, + {"type": "assert_return", "line": 150, "action": {"type": "invoke", "field": "f32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["1820282235", "0", "0", "0"]}]}, "expected": [{"type": "f32", "value": "1820282235"}]}, + {"type": "assert_return", "line": 151, "action": {"type": "invoke", "field": "f32x4_extract_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["1376887476", "0", "0", "0"]}]}, "expected": [{"type": "f32", "value": "1376887476"}]}, + {"type": "assert_return", "line": 152, "action": {"type": "invoke", "field": "f32x4_extract_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "4271273625"]}]}, "expected": [{"type": "f32", "value": "4271273625"}]}, + {"type": "assert_return", "line": 153, "action": {"type": "invoke", "field": "f32x4_extract_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "4286578687"]}]}, "expected": [{"type": "f32", "value": "4286578687"}]}, + {"type": "assert_return", "line": 154, "action": {"type": "invoke", "field": "f32x4_extract_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "4278190080"]}]}, "expected": [{"type": "f32", "value": "4278190080"}]}, + {"type": "assert_return", "line": 155, "action": {"type": "invoke", "field": "f32x4_extract_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "4286578688"]}]}, "expected": [{"type": "f32", "value": "4286578688"}]}, + {"type": "assert_return", "line": 156, "action": {"type": "invoke", "field": "f32x4_extract_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "4286578688", "2143289344"]}]}, "expected": [{"type": "f32", "value": "2143289344"}]}, + {"type": "assert_return", "line": 157, "action": {"type": "invoke", "field": "f32x4_extract_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "1290500515"]}]}, "expected": [{"type": "f32", "value": "1290500515"}]}, + {"type": "assert_return", "line": 158, "action": {"type": "invoke", "field": "f32x4_extract_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "1536271028"]}]}, "expected": [{"type": "f32", "value": "1536271028"}]}, + {"type": "assert_return", "line": 160, "action": {"type": "invoke", "field": "f64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["13832806255468478464", "0"]}]}, "expected": [{"type": "f64", "value": "13832806255468478464"}]}, + {"type": "assert_return", "line": 161, "action": {"type": "invoke", "field": "f64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["4609434218613702656", "0"]}]}, "expected": [{"type": "f64", "value": "4609434218613702656"}]}, + {"type": "assert_return", "line": 162, "action": {"type": "invoke", "field": "f64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["9227010608267287965", "0"]}]}, "expected": [{"type": "f64", "value": "9227010608267287965"}]}, + {"type": "assert_return", "line": 163, "action": {"type": "invoke", "field": "f64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["3638571412512157", "0"]}]}, "expected": [{"type": "f64", "value": "3638571412512157"}]}, + {"type": "assert_return", "line": 164, "action": {"type": "invoke", "field": "f64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["9227875636482146303", "0"]}]}, "expected": [{"type": "f64", "value": "9227875636482146303"}]}, + {"type": "assert_return", "line": 165, "action": {"type": "invoke", "field": "f64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["4503599627370495", "0"]}]}, "expected": [{"type": "f64", "value": "4503599627370495"}]}, + {"type": "assert_return", "line": 166, "action": {"type": "invoke", "field": "f64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["18442240474082181120", "0"]}]}, "expected": [{"type": "f64", "value": "18442240474082181120"}]}, + {"type": "assert_return", "line": 167, "action": {"type": "invoke", "field": "f64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["9218868437227405312", "0"]}]}, "expected": [{"type": "f64", "value": "9218868437227405312"}]}, + {"type": "assert_return", "line": 168, "action": {"type": "invoke", "field": "f64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["18444492273895866368", "9223372036854775808"]}]}, "expected": [{"type": "f64", "value": "18444492273895866368"}]}, + {"type": "assert_return", "line": 169, "action": {"type": "invoke", "field": "f64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["9221120237041090560", "0"]}]}, "expected": [{"type": "f64", "value": "9221120237041090560"}]}, + {"type": "assert_return", "line": 170, "action": {"type": "invoke", "field": "f64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["5012481849648991189", "0"]}]}, "expected": [{"type": "f64", "value": "5012481849648991189"}]}, + {"type": "assert_return", "line": 171, "action": {"type": "invoke", "field": "f64x2_extract_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["4882522492018277599", "0"]}]}, "expected": [{"type": "f64", "value": "4882522492018277599"}]}, + {"type": "assert_return", "line": 172, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "4612248968380809216"]}]}, "expected": [{"type": "f64", "value": "4612248968380809216"}]}, + {"type": "assert_return", "line": 173, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "13835621005235585024"]}]}, "expected": [{"type": "f64", "value": "13835621005235585024"}]}, + {"type": "assert_return", "line": 174, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "18442240474082181119"]}]}, "expected": [{"type": "f64", "value": "18442240474082181119"}]}, + {"type": "assert_return", "line": 175, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "9218868437227405311"]}]}, "expected": [{"type": "f64", "value": "9218868437227405311"}]}, + {"type": "assert_return", "line": 176, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "18442240474082181119"]}]}, "expected": [{"type": "f64", "value": "18442240474082181119"}]}, + {"type": "assert_return", "line": 177, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "9218868437227405311"]}]}, "expected": [{"type": "f64", "value": "9218868437227405311"}]}, + {"type": "assert_return", "line": 178, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["9223372036854775808", "18442240474082181120"]}]}, "expected": [{"type": "f64", "value": "18442240474082181120"}]}, + {"type": "assert_return", "line": 179, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "9218868437227405312"]}]}, "expected": [{"type": "f64", "value": "9218868437227405312"}]}, + {"type": "assert_return", "line": 180, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["9223372036854775808", "18444492273895866368"]}]}, "expected": [{"type": "f64", "value": "18444492273895866368"}]}, + {"type": "assert_return", "line": 181, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "9221120237041090560"]}]}, "expected": [{"type": "f64", "value": "9221120237041090560"}]}, + {"type": "assert_return", "line": 182, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "4728057454347157504"]}]}, "expected": [{"type": "f64", "value": "4728057454347157504"}]}, + {"type": "assert_return", "line": 183, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "4968090884938317023"]}]}, "expected": [{"type": "f64", "value": "4968090884938317023"}]}, + {"type": "assert_return", "line": 185, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "f64", "value": "0"}]}, + {"type": "assert_return", "line": 186, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "128"]}]}, "expected": [{"type": "f64", "value": "9223372036854775808"}]}, + {"type": "assert_return", "line": 187, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "16384"]}]}, "expected": [{"type": "f64", "value": "4611686018427387904"}]}, + {"type": "assert_return", "line": 188, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "49152"]}]}, "expected": [{"type": "f64", "value": "13835058055282163712"}]}, + {"type": "assert_return", "line": 189, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "4294967295", "2146435071"]}]}, "expected": [{"type": "f64", "value": "9218868437227405311"}]}, + {"type": "assert_return", "line": 190, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "1048576"]}]}, "expected": [{"type": "f64", "value": "4503599627370496"}]}, + {"type": "assert_return", "line": 191, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "4294967295", "1048575"]}]}, "expected": [{"type": "f64", "value": "4503599627370495"}]}, + {"type": "assert_return", "line": 192, "action": {"type": "invoke", "field": "f64x2_extract_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "1", "0"]}]}, "expected": [{"type": "f64", "value": "1"}]}, + {"type": "assert_return", "line": 194, "action": {"type": "invoke", "field": "i8x16_replace_lane-first", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "127"}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["127", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 195, "action": {"type": "invoke", "field": "i8x16_replace_lane-first", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "128"}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["128", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 196, "action": {"type": "invoke", "field": "i8x16_replace_lane-first", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "255"}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["255", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 197, "action": {"type": "invoke", "field": "i8x16_replace_lane-first", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "256"}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 198, "action": {"type": "invoke", "field": "i8x16_replace_lane-last", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "4294967168"}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "128"]}]}, + {"type": "assert_return", "line": 199, "action": {"type": "invoke", "field": "i8x16_replace_lane-last", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "4294967167"}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "127"]}]}, + {"type": "assert_return", "line": 200, "action": {"type": "invoke", "field": "i8x16_replace_lane-last", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "32767"}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "255"]}]}, + {"type": "assert_return", "line": 201, "action": {"type": "invoke", "field": "i8x16_replace_lane-last", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "4294934528"}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 203, "action": {"type": "invoke", "field": "i16x8_replace_lane-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "32767"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["32767", "0", "0", "0", "0", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 204, "action": {"type": "invoke", "field": "i16x8_replace_lane-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "32768"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["32768", "0", "0", "0", "0", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 205, "action": {"type": "invoke", "field": "i16x8_replace_lane-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "65535"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["65535", "0", "0", "0", "0", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 206, "action": {"type": "invoke", "field": "i16x8_replace_lane-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "65536"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 207, "action": {"type": "invoke", "field": "i16x8_replace_lane-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "12345"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["12345", "0", "0", "0", "0", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 208, "action": {"type": "invoke", "field": "i16x8_replace_lane-first", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "4294962636"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["60876", "0", "0", "0", "0", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 209, "action": {"type": "invoke", "field": "i16x8_replace_lane-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "4294934528"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "32768"]}]}, + {"type": "assert_return", "line": 210, "action": {"type": "invoke", "field": "i16x8_replace_lane-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "4294934527"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "32767"]}]}, + {"type": "assert_return", "line": 211, "action": {"type": "invoke", "field": "i16x8_replace_lane-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "2147483647"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "65535"]}]}, + {"type": "assert_return", "line": 212, "action": {"type": "invoke", "field": "i16x8_replace_lane-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "2147483648"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 213, "action": {"type": "invoke", "field": "i16x8_replace_lane-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "54321"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "54321"]}]}, + {"type": "assert_return", "line": 214, "action": {"type": "invoke", "field": "i16x8_replace_lane-last", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "4294950111"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "48351"]}]}, + {"type": "assert_return", "line": 216, "action": {"type": "invoke", "field": "i32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}, {"type": "i32", "value": "2147483647"}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["2147483647", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 217, "action": {"type": "invoke", "field": "i32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}, {"type": "i32", "value": "4294967295"}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["4294967295", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 218, "action": {"type": "invoke", "field": "i32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}, {"type": "i32", "value": "1234567890"}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["1234567890", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 219, "action": {"type": "invoke", "field": "i32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}, {"type": "i32", "value": "3989547400"}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["3989547400", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 220, "action": {"type": "invoke", "field": "i32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}, {"type": "i32", "value": "2147483648"}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "2147483648"]}]}, + {"type": "assert_return", "line": 221, "action": {"type": "invoke", "field": "i32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}, {"type": "i32", "value": "2147483648"}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "2147483648"]}]}, + {"type": "assert_return", "line": 222, "action": {"type": "invoke", "field": "i32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}, {"type": "i32", "value": "1234567890"}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "1234567890"]}]}, + {"type": "assert_return", "line": 223, "action": {"type": "invoke", "field": "i32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}, {"type": "i32", "value": "3989547400"}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "3989547400"]}]}, + {"type": "assert_return", "line": 225, "action": {"type": "invoke", "field": "f32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "1112801280"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["1112801280", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 226, "action": {"type": "invoke", "field": "f32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "1112801280"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["1112801280", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 227, "action": {"type": "invoke", "field": "f32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "2143289344"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["2143289344", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 228, "action": {"type": "invoke", "field": "f32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "2139095040"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["2139095040", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 229, "action": {"type": "invoke", "field": "f32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["2143289344", "0", "0", "0"]}, {"type": "f32", "value": "1078523331"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["1078523331", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 230, "action": {"type": "invoke", "field": "f32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["2139095040", "0", "0", "0"]}, {"type": "f32", "value": "2123789977"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["2123789977", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 231, "action": {"type": "invoke", "field": "f32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["2139095040", "0", "0", "0"]}, {"type": "f32", "value": "2139095039"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["2139095039", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 232, "action": {"type": "invoke", "field": "f32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["2139095040", "0", "0", "0"]}, {"type": "f32", "value": "2130706432"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["2130706432", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 233, "action": {"type": "invoke", "field": "f32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "1290500515"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["1290500515", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 234, "action": {"type": "invoke", "field": "f32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "1290500515"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["1290500515", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 235, "action": {"type": "invoke", "field": "f32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "1536271028"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["1536271028", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 236, "action": {"type": "invoke", "field": "f32x4_replace_lane-first", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "1536271028"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["1536271028", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 237, "action": {"type": "invoke", "field": "f32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "3260284928"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "3260284928"]}]}, + {"type": "assert_return", "line": 238, "action": {"type": "invoke", "field": "f32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "3260284928"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "3260284928"]}]}, + {"type": "assert_return", "line": 239, "action": {"type": "invoke", "field": "f32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "2143289344"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "2143289344"]}]}, + {"type": "assert_return", "line": 240, "action": {"type": "invoke", "field": "f32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "4286578688"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "4286578688"]}]}, + {"type": "assert_return", "line": 241, "action": {"type": "invoke", "field": "f32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "2143289344"]}, {"type": "f32", "value": "1078523331"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "1078523331"]}]}, + {"type": "assert_return", "line": 242, "action": {"type": "invoke", "field": "f32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "4286578688"]}, {"type": "f32", "value": "4271273625"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "4271273625"]}]}, + {"type": "assert_return", "line": 243, "action": {"type": "invoke", "field": "f32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "4286578688"]}, {"type": "f32", "value": "4286578687"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "4286578687"]}]}, + {"type": "assert_return", "line": 244, "action": {"type": "invoke", "field": "f32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "4286578688"]}, {"type": "f32", "value": "4278190080"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "4278190080"]}]}, + {"type": "assert_return", "line": 245, "action": {"type": "invoke", "field": "f32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "1820282235"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "1820282235"]}]}, + {"type": "assert_return", "line": 246, "action": {"type": "invoke", "field": "f32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "1820282235"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "1820282235"]}]}, + {"type": "assert_return", "line": 247, "action": {"type": "invoke", "field": "f32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "1695654580"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "1695654580"]}]}, + {"type": "assert_return", "line": 248, "action": {"type": "invoke", "field": "f32x4_replace_lane-last", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "1376887476"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "1376887476"]}]}, + {"type": "assert_return", "line": 250, "action": {"type": "invoke", "field": "i64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "0"]}, {"type": "i64", "value": "9223372036854775807"}]}, "expected": [{"type": "v128", "lane_type": "i64", "value": ["9223372036854775807", "0"]}]}, + {"type": "assert_return", "line": 251, "action": {"type": "invoke", "field": "i64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "0"]}, {"type": "i64", "value": "18446744073709551615"}]}, "expected": [{"type": "v128", "lane_type": "i64", "value": ["18446744073709551615", "0"]}]}, + {"type": "assert_return", "line": 252, "action": {"type": "invoke", "field": "i64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "0"]}, {"type": "i64", "value": "1234567890123456789"}]}, "expected": [{"type": "v128", "lane_type": "i64", "value": ["1234567890123456789", "0"]}]}, + {"type": "assert_return", "line": 253, "action": {"type": "invoke", "field": "i64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "0"]}, {"type": "i64", "value": "1311768467294899695"}]}, "expected": [{"type": "v128", "lane_type": "i64", "value": ["1311768467294899695", "0"]}]}, + {"type": "assert_return", "line": 254, "action": {"type": "invoke", "field": "i64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "0"]}, {"type": "i64", "value": "9223372036854775808"}]}, "expected": [{"type": "v128", "lane_type": "i64", "value": ["0", "9223372036854775808"]}]}, + {"type": "assert_return", "line": 255, "action": {"type": "invoke", "field": "i64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "0"]}, {"type": "i64", "value": "9223372036854775808"}]}, "expected": [{"type": "v128", "lane_type": "i64", "value": ["0", "9223372036854775808"]}]}, + {"type": "assert_return", "line": 256, "action": {"type": "invoke", "field": "i64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "0"]}, {"type": "i64", "value": "1234567890123456789"}]}, "expected": [{"type": "v128", "lane_type": "i64", "value": ["0", "1234567890123456789"]}]}, + {"type": "assert_return", "line": 257, "action": {"type": "invoke", "field": "i64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "0"]}, {"type": "i64", "value": "1311768467294899695"}]}, "expected": [{"type": "v128", "lane_type": "i64", "value": ["0", "1311768467294899695"]}]}, + {"type": "assert_return", "line": 259, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["4607182418800017408", "4607182418800017408"]}, {"type": "f64", "value": "0"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "4607182418800017408"]}]}, + {"type": "assert_return", "line": 260, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["13830554455654793216", "13830554455654793216"]}, {"type": "f64", "value": "9223372036854775808"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["9223372036854775808", "13830554455654793216"]}]}, + {"type": "assert_return", "line": 261, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "4608308318706860032"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["4608308318706860032", "0"]}]}, + {"type": "assert_return", "line": 262, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "13831680355561635840"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["13831680355561635840", "0"]}]}, + {"type": "assert_return", "line": 263, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["18444492273895866368", "0"]}, {"type": "f64", "value": "18442240474082181119"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["18442240474082181119", "0"]}]}, + {"type": "assert_return", "line": 264, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["9221120237041090560", "0"]}, {"type": "f64", "value": "9218868437227405311"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["9218868437227405311", "0"]}]}, + {"type": "assert_return", "line": 265, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["18442240474082181120", "0"]}, {"type": "f64", "value": "9227875636482146303"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["9227875636482146303", "0"]}]}, + {"type": "assert_return", "line": 266, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["9218868437227405312", "0"]}, {"type": "f64", "value": "4503599627370495"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["4503599627370495", "0"]}]}, + {"type": "assert_return", "line": 267, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "18444492273895866368"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["18444492273895866368", "0"]}]}, + {"type": "assert_return", "line": 268, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "9221120237041090560"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["9221120237041090560", "0"]}]}, + {"type": "assert_return", "line": 269, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "18442240474082181120"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["18442240474082181120", "0"]}]}, + {"type": "assert_return", "line": 270, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "9218868437227405312"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["9218868437227405312", "0"]}]}, + {"type": "assert_return", "line": 271, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "4728057454347157504"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["4728057454347157504", "0"]}]}, + {"type": "assert_return", "line": 272, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "4728057454347157504"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["4728057454347157504", "0"]}]}, + {"type": "assert_return", "line": 273, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "4968090884938317023"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["4968090884938317023", "0"]}]}, + {"type": "assert_return", "line": 274, "action": {"type": "invoke", "field": "f64x2_replace_lane-first", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "4968090884938317023"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["4968090884938317023", "0"]}]}, + {"type": "assert_return", "line": 275, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["4611686018427387904", "4611686018427387904"]}, {"type": "f64", "value": "0"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["4611686018427387904", "0"]}]}, + {"type": "assert_return", "line": 276, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["13835058055282163712", "13835058055282163712"]}, {"type": "f64", "value": "9223372036854775808"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["13835058055282163712", "9223372036854775808"]}]}, + {"type": "assert_return", "line": 277, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "4612248968380809216"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "4612248968380809216"]}]}, + {"type": "assert_return", "line": 278, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "13835621005235585024"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "13835621005235585024"]}]}, + {"type": "assert_return", "line": 279, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "18444492273895866368"]}, {"type": "f64", "value": "18442240474082181119"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "18442240474082181119"]}]}, + {"type": "assert_return", "line": 280, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "9221120237041090560"]}, {"type": "f64", "value": "9218868437227405311"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "9218868437227405311"]}]}, + {"type": "assert_return", "line": 281, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "18442240474082181120"]}, {"type": "f64", "value": "9227875636482146303"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "9227875636482146303"]}]}, + {"type": "assert_return", "line": 282, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "9218868437227405312"]}, {"type": "f64", "value": "4503599627370495"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "4503599627370495"]}]}, + {"type": "assert_return", "line": 283, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "18444492273895866368"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "18444492273895866368"]}]}, + {"type": "assert_return", "line": 284, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "9221120237041090560"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "9221120237041090560"]}]}, + {"type": "assert_return", "line": 285, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "18442240474082181120"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "18442240474082181120"]}]}, + {"type": "assert_return", "line": 286, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "9218868437227405312"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "9218868437227405312"]}]}, + {"type": "assert_return", "line": 287, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "5012481849648092922"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "5012481849648092922"]}]}, + {"type": "assert_return", "line": 288, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "5012481849648092922"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "5012481849648092922"]}]}, + {"type": "assert_return", "line": 289, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "5012481849648092922"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "5012481849648092922"]}]}, + {"type": "assert_return", "line": 290, "action": {"type": "invoke", "field": "f64x2_replace_lane-last", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "4443687238071173905"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["0", "4443687238071173905"]}]}, + {"type": "assert_return", "line": 292, "action": {"type": "invoke", "field": "v8x16_swizzle", "args": [{"type": "v128", "lane_type": "i8", "value": ["16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31"]}, {"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31"]}]}, + {"type": "assert_return", "line": 296, "action": {"type": "invoke", "field": "v8x16_swizzle", "args": [{"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}, {"type": "v128", "lane_type": "i8", "value": ["248", "249", "250", "251", "252", "253", "254", "255", "16", "17", "18", "19", "20", "21", "22", "23"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 300, "action": {"type": "invoke", "field": "v8x16_swizzle", "args": [{"type": "v128", "lane_type": "i8", "value": ["100", "101", "102", "103", "104", "105", "106", "107", "108", "109", "110", "111", "112", "113", "114", "115"]}, {"type": "v128", "lane_type": "i8", "value": ["15", "14", "13", "12", "11", "10", "9", "8", "7", "6", "5", "4", "3", "2", "1", "0"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["115", "114", "113", "112", "111", "110", "109", "108", "107", "106", "105", "104", "103", "102", "101", "100"]}]}, + {"type": "assert_return", "line": 304, "action": {"type": "invoke", "field": "v8x16_swizzle", "args": [{"type": "v128", "lane_type": "i8", "value": ["100", "101", "102", "103", "104", "105", "106", "107", "108", "109", "110", "111", "112", "113", "114", "115"]}, {"type": "v128", "lane_type": "i8", "value": ["255", "1", "254", "2", "253", "3", "252", "4", "251", "5", "250", "6", "249", "7", "248", "8"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["0", "101", "0", "102", "0", "103", "0", "104", "0", "105", "0", "106", "0", "107", "0", "108"]}]}, + {"type": "assert_return", "line": 308, "action": {"type": "invoke", "field": "v8x16_swizzle", "args": [{"type": "v128", "lane_type": "i8", "value": ["100", "101", "102", "103", "104", "105", "106", "107", "108", "109", "110", "111", "112", "113", "114", "115"]}, {"type": "v128", "lane_type": "i8", "value": ["9", "16", "10", "17", "11", "18", "12", "19", "13", "20", "14", "21", "15", "22", "16", "23"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["109", "0", "110", "0", "111", "0", "112", "0", "113", "0", "114", "0", "115", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 312, "action": {"type": "invoke", "field": "v8x16_swizzle", "args": [{"type": "v128", "lane_type": "i8", "value": ["100", "101", "102", "103", "104", "105", "106", "107", "108", "109", "110", "111", "112", "113", "114", "115"]}, {"type": "v128", "lane_type": "i8", "value": ["9", "16", "10", "17", "11", "18", "12", "19", "13", "20", "14", "21", "15", "22", "16", "23"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["109", "0", "110", "0", "111", "0", "112", "0", "113", "0", "114", "0", "115", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 316, "action": {"type": "invoke", "field": "v8x16_swizzle", "args": [{"type": "v128", "lane_type": "i16", "value": ["25701", "26215", "26729", "27243", "27757", "28271", "28785", "29299"]}, {"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["25701", "26215", "26729", "27243", "27757", "28271", "28785", "29299"]}]}, + {"type": "assert_return", "line": 320, "action": {"type": "invoke", "field": "v8x16_swizzle", "args": [{"type": "v128", "lane_type": "i32", "value": ["1684366951", "1751738987", "1819111023", "1886483059"]}, {"type": "v128", "lane_type": "i8", "value": ["15", "14", "13", "12", "11", "10", "9", "8", "7", "6", "5", "4", "3", "2", "1", "0"]}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["1936879984", "1869507948", "1802135912", "1734763876"]}]}, + {"type": "assert_return", "line": 324, "action": {"type": "invoke", "field": "v8x16_swizzle", "args": [{"type": "v128", "lane_type": "f32", "value": ["2143289344", "4290772992", "2139095040", "4286578688"]}, {"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["2143289344", "4290772992", "2139095040", "4286578688"]}]}, + {"type": "assert_return", "line": 328, "action": {"type": "invoke", "field": "v8x16_swizzle", "args": [{"type": "v128", "lane_type": "i32", "value": ["1734763876", "1802135912", "1869507932", "1936879984"]}, {"type": "v128", "lane_type": "f32", "value": ["0", "2147483648", "2139095040", "4286578688"]}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["1684300900", "6579300", "25700", "25700"]}]}, + {"type": "assert_return", "line": 333, "action": {"type": "invoke", "field": "v8x16_shuffle-1", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}, {"type": "v128", "lane_type": "i8", "value": ["16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}]}, + {"type": "assert_return", "line": 337, "action": {"type": "invoke", "field": "v8x16_shuffle-2", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}, {"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}]}, + {"type": "assert_return", "line": 341, "action": {"type": "invoke", "field": "v8x16_shuffle-3", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}, {"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["255", "254", "253", "252", "251", "250", "249", "248", "247", "246", "245", "244", "243", "242", "241", "240"]}]}, + {"type": "assert_return", "line": 345, "action": {"type": "invoke", "field": "v8x16_shuffle-4", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}, {"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["15", "14", "13", "12", "11", "10", "9", "8", "7", "6", "5", "4", "3", "2", "1", "0"]}]}, + {"type": "assert_return", "line": 349, "action": {"type": "invoke", "field": "v8x16_shuffle-5", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}, {"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 353, "action": {"type": "invoke", "field": "v8x16_shuffle-6", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}, {"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["240", "240", "240", "240", "240", "240", "240", "240", "240", "240", "240", "240", "240", "240", "240", "240"]}]}, + {"type": "assert_return", "line": 357, "action": {"type": "invoke", "field": "v8x16_shuffle-7", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}, {"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "240", "240", "240", "240", "240", "240", "240", "240"]}]}, + {"type": "assert_return", "line": 361, "action": {"type": "invoke", "field": "v8x16_shuffle-1", "args": [{"type": "v128", "lane_type": "i8", "value": ["100", "101", "102", "103", "104", "105", "106", "107", "108", "109", "110", "111", "112", "113", "114", "115"]}, {"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["100", "101", "102", "103", "104", "105", "106", "107", "108", "109", "110", "111", "112", "113", "114", "115"]}]}, + {"type": "assert_return", "line": 365, "action": {"type": "invoke", "field": "v8x16_shuffle-1", "args": [{"type": "v128", "lane_type": "i16", "value": ["256", "770", "1284", "1798", "2312", "2826", "3340", "3854"]}, {"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["256", "770", "1284", "1798", "2312", "2826", "3340", "3854"]}]}, + {"type": "assert_return", "line": 369, "action": {"type": "invoke", "field": "v8x16_shuffle-2", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}, {"type": "v128", "lane_type": "i32", "value": ["4092785136", "4160157172", "4227529208", "4294901244"]}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["4092785136", "4160157172", "4227529208", "4294901244"]}]}, + {"type": "assert_return", "line": 373, "action": {"type": "invoke", "field": "v8x16_shuffle-1", "args": [{"type": "v128", "lane_type": "i32", "value": ["66051", "67438087", "134810123", "202182159"]}, {"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["66051", "67438087", "134810123", "202182159"]}]}, + {"type": "assert_return", "line": 377, "action": {"type": "invoke", "field": "v8x16_shuffle-1", "args": [{"type": "v128", "lane_type": "f32", "value": ["1065353216", "2143289344", "2139095040", "4286578688"]}, {"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["1065353216", "2143289344", "2139095040", "4286578688"]}]}, + {"type": "assert_return", "line": 381, "action": {"type": "invoke", "field": "v8x16_shuffle-1", "args": [{"type": "v128", "lane_type": "i32", "value": ["66051", "67438087", "134810123", "202182159"]}, {"type": "v128", "lane_type": "f32", "value": ["2147483648", "2143289344", "2139095040", "4286578688"]}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["66051", "67438087", "134810123", "202182159"]}]}, + {"type": "assert_return", "line": 387, "action": {"type": "invoke", "field": "v8x16_swizzle", "args": [{"type": "v128", "lane_type": "i32", "value": ["1234567890", "305419896", "1234567890", "305419896"]}, {"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["1234567890", "305419896", "1234567890", "305419896"]}]}, + {"type": "assert_return", "line": 391, "action": {"type": "invoke", "field": "v8x16_shuffle-1", "args": [{"type": "v128", "lane_type": "i64", "value": ["12345678901234567890", "1311768467294899695"]}, {"type": "v128", "lane_type": "i64", "value": ["12345678901234567890", "1311768467294899695"]}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["3944680146", "2874452364", "2427178479", "305419896"]}]}, + {"type": "assert_malformed", "line": 398, "filename": "simd_lane.1.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 399, "filename": "simd_lane.2.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 400, "filename": "simd_lane.3.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 401, "filename": "simd_lane.4.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 402, "filename": "simd_lane.5.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 403, "filename": "simd_lane.6.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 404, "filename": "simd_lane.7.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 405, "filename": "simd_lane.8.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 406, "filename": "simd_lane.9.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 407, "filename": "simd_lane.10.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 408, "filename": "simd_lane.11.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 409, "filename": "simd_lane.12.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 410, "filename": "simd_lane.13.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 411, "filename": "simd_lane.14.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 415, "filename": "simd_lane.15.wat", "text": "malformed lane index", "module_type": "text"}, + {"type": "assert_malformed", "line": 416, "filename": "simd_lane.16.wat", "text": "malformed lane index", "module_type": "text"}, + {"type": "assert_malformed", "line": 417, "filename": "simd_lane.17.wat", "text": "malformed lane index", "module_type": "text"}, + {"type": "assert_malformed", "line": 418, "filename": "simd_lane.18.wat", "text": "malformed lane index", "module_type": "text"}, + {"type": "assert_malformed", "line": 419, "filename": "simd_lane.19.wat", "text": "malformed lane index", "module_type": "text"}, + {"type": "assert_malformed", "line": 420, "filename": "simd_lane.20.wat", "text": "malformed lane index", "module_type": "text"}, + {"type": "assert_malformed", "line": 421, "filename": "simd_lane.21.wat", "text": "malformed lane index", "module_type": "text"}, + {"type": "assert_malformed", "line": 422, "filename": "simd_lane.22.wat", "text": "malformed lane index", "module_type": "text"}, + {"type": "assert_malformed", "line": 423, "filename": "simd_lane.23.wat", "text": "malformed lane index", "module_type": "text"}, + {"type": "assert_malformed", "line": 424, "filename": "simd_lane.24.wat", "text": "malformed lane index", "module_type": "text"}, + {"type": "assert_malformed", "line": 425, "filename": "simd_lane.25.wat", "text": "malformed lane index", "module_type": "text"}, + {"type": "assert_malformed", "line": 426, "filename": "simd_lane.26.wat", "text": "malformed lane index", "module_type": "text"}, + {"type": "assert_malformed", "line": 427, "filename": "simd_lane.27.wat", "text": "malformed lane index", "module_type": "text"}, + {"type": "assert_malformed", "line": 428, "filename": "simd_lane.28.wat", "text": "malformed lane index", "module_type": "text"}, + {"type": "assert_invalid", "line": 432, "filename": "simd_lane.29.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 433, "filename": "simd_lane.30.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 434, "filename": "simd_lane.31.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 435, "filename": "simd_lane.32.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 436, "filename": "simd_lane.33.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 437, "filename": "simd_lane.34.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 438, "filename": "simd_lane.35.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 439, "filename": "simd_lane.36.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 440, "filename": "simd_lane.37.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 441, "filename": "simd_lane.38.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 442, "filename": "simd_lane.39.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 443, "filename": "simd_lane.40.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 444, "filename": "simd_lane.41.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 445, "filename": "simd_lane.42.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 446, "filename": "simd_lane.43.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 447, "filename": "simd_lane.44.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 448, "filename": "simd_lane.45.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 449, "filename": "simd_lane.46.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 450, "filename": "simd_lane.47.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 451, "filename": "simd_lane.48.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 452, "filename": "simd_lane.49.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 453, "filename": "simd_lane.50.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 454, "filename": "simd_lane.51.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 455, "filename": "simd_lane.52.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 456, "filename": "simd_lane.53.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 457, "filename": "simd_lane.54.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 458, "filename": "simd_lane.55.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 459, "filename": "simd_lane.56.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 463, "filename": "simd_lane.57.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 464, "filename": "simd_lane.58.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 465, "filename": "simd_lane.59.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 466, "filename": "simd_lane.60.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 467, "filename": "simd_lane.61.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 468, "filename": "simd_lane.62.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 469, "filename": "simd_lane.63.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 470, "filename": "simd_lane.64.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 471, "filename": "simd_lane.65.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 472, "filename": "simd_lane.66.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 473, "filename": "simd_lane.67.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_invalid", "line": 477, "filename": "simd_lane.68.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 478, "filename": "simd_lane.69.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 479, "filename": "simd_lane.70.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 480, "filename": "simd_lane.71.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 481, "filename": "simd_lane.72.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 482, "filename": "simd_lane.73.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 483, "filename": "simd_lane.74.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 484, "filename": "simd_lane.75.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 485, "filename": "simd_lane.76.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 486, "filename": "simd_lane.77.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 487, "filename": "simd_lane.78.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 488, "filename": "simd_lane.79.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 489, "filename": "simd_lane.80.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 490, "filename": "simd_lane.81.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 494, "filename": "simd_lane.82.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 495, "filename": "simd_lane.83.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 496, "filename": "simd_lane.84.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 497, "filename": "simd_lane.85.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 499, "filename": "simd_lane.86.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 500, "filename": "simd_lane.87.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 503, "filename": "simd_lane.88.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 505, "filename": "simd_lane.89.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 507, "filename": "simd_lane.90.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 510, "filename": "simd_lane.91.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_malformed", "line": 515, "filename": "simd_lane.92.wat", "text": "invalid lane length", "module_type": "text"}, + {"type": "assert_malformed", "line": 518, "filename": "simd_lane.93.wat", "text": "invalid lane length", "module_type": "text"}, + {"type": "assert_malformed", "line": 521, "filename": "simd_lane.94.wat", "text": "malformed lane index", "module_type": "text"}, + {"type": "assert_malformed", "line": 525, "filename": "simd_lane.95.wat", "text": "malformed lane index", "module_type": "text"}, + {"type": "assert_invalid", "line": 529, "filename": "simd_lane.96.wasm", "text": "invalid lane index", "module_type": "binary"}, + {"type": "assert_malformed", "line": 536, "filename": "simd_lane.97.wat", "text": "unknown operator", "module_type": "text"}, + {"type": "assert_malformed", "line": 537, "filename": "simd_lane.98.wat", "text": "unknown operator", "module_type": "text"}, + {"type": "assert_malformed", "line": 538, "filename": "simd_lane.99.wat", "text": "unknown operator", "module_type": "text"}, + {"type": "assert_malformed", "line": 539, "filename": "simd_lane.100.wat", "text": "unknown operator", "module_type": "text"}, + {"type": "assert_malformed", "line": 540, "filename": "simd_lane.101.wat", "text": "unknown operator", "module_type": "text"}, + {"type": "assert_malformed", "line": 541, "filename": "simd_lane.102.wat", "text": "unknown operator", "module_type": "text"}, + {"type": "assert_malformed", "line": 545, "filename": "simd_lane.103.wat", "text": "unknown operator", "module_type": "text"}, + {"type": "assert_malformed", "line": 549, "filename": "simd_lane.104.wat", "text": "unknown operator", "module_type": "text"}, + {"type": "assert_malformed", "line": 555, "filename": "simd_lane.105.wat", "text": "unknown operator", "module_type": "text"}, + {"type": "assert_malformed", "line": 559, "filename": "simd_lane.106.wat", "text": "unknown operator", "module_type": "text"}, + {"type": "assert_malformed", "line": 570, "filename": "simd_lane.107.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 571, "filename": "simd_lane.108.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 572, "filename": "simd_lane.109.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 573, "filename": "simd_lane.110.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 574, "filename": "simd_lane.111.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 575, "filename": "simd_lane.112.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 576, "filename": "simd_lane.113.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 577, "filename": "simd_lane.114.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 578, "filename": "simd_lane.115.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 579, "filename": "simd_lane.116.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 581, "filename": "simd_lane.117.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 582, "filename": "simd_lane.118.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 583, "filename": "simd_lane.119.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 584, "filename": "simd_lane.120.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 588, "filename": "simd_lane.121.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 589, "filename": "simd_lane.122.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 590, "filename": "simd_lane.123.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 591, "filename": "simd_lane.124.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 592, "filename": "simd_lane.125.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 593, "filename": "simd_lane.126.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 594, "filename": "simd_lane.127.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 595, "filename": "simd_lane.128.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 596, "filename": "simd_lane.129.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 597, "filename": "simd_lane.130.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 600, "filename": "simd_lane.131.wat", "text": "invalid lane length", "module_type": "text"}, + {"type": "assert_malformed", "line": 604, "filename": "simd_lane.132.wat", "text": "malformed lane index", "module_type": "text"}, + {"type": "assert_malformed", "line": 608, "filename": "simd_lane.133.wat", "text": "malformed lane index", "module_type": "text"}, + {"type": "assert_malformed", "line": 612, "filename": "simd_lane.134.wat", "text": "malformed lane index", "module_type": "text"}, + {"type": "assert_malformed", "line": 616, "filename": "simd_lane.135.wat", "text": "malformed lane index", "module_type": "text"}, + {"type": "assert_malformed", "line": 620, "filename": "simd_lane.136.wat", "text": "malformed lane index", "module_type": "text"}, + {"type": "module", "line": 628, "filename": "simd_lane.137.wasm"}, + {"type": "assert_return", "line": 674, "action": {"type": "invoke", "field": "i8x16_extract_lane_s", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "v128", "lane_type": "i8", "value": ["255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["255", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 675, "action": {"type": "invoke", "field": "i8x16_extract_lane_u", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "v128", "lane_type": "i8", "value": ["255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["255", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 676, "action": {"type": "invoke", "field": "i16x8_extract_lane_s", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "v128", "lane_type": "i16", "value": ["65535", "65535", "65535", "65535", "65535", "65535", "65535", "65535"]}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["65535", "0", "0", "0", "0", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 677, "action": {"type": "invoke", "field": "i16x8_extract_lane_u", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "v128", "lane_type": "i16", "value": ["65535", "65535", "65535", "65535", "65535", "65535", "65535", "65535"]}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["65535", "0", "0", "0", "0", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 678, "action": {"type": "invoke", "field": "i32x4_extract_lane", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}, {"type": "v128", "lane_type": "i32", "value": ["65536", "4294967295", "4294967295", "4294967295"]}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["65536", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 679, "action": {"type": "invoke", "field": "f32x4_extract_lane", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "v128", "lane_type": "f32", "value": ["2123789977", "2143289344", "2143289344", "2143289344"]}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["2123789977", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 680, "action": {"type": "invoke", "field": "i8x16_replace_lane-s", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "255"}]}, "expected": [{"type": "i32", "value": "4294967295"}]}, + {"type": "assert_return", "line": 681, "action": {"type": "invoke", "field": "i8x16_replace_lane-u", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "255"}]}, "expected": [{"type": "i32", "value": "255"}]}, + {"type": "assert_return", "line": 682, "action": {"type": "invoke", "field": "i16x8_replace_lane-s", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "65535"}]}, "expected": [{"type": "i32", "value": "4294967295"}]}, + {"type": "assert_return", "line": 683, "action": {"type": "invoke", "field": "i16x8_replace_lane-u", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "65535"}]}, "expected": [{"type": "i32", "value": "65535"}]}, + {"type": "assert_return", "line": 684, "action": {"type": "invoke", "field": "i32x4_replace_lane", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}, {"type": "i32", "value": "4294967295"}]}, "expected": [{"type": "i32", "value": "4294967295"}]}, + {"type": "assert_return", "line": 685, "action": {"type": "invoke", "field": "f32x4_replace_lane", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "1067450368"}]}, "expected": [{"type": "f32", "value": "1067450368"}]}, + {"type": "assert_return", "line": 687, "action": {"type": "invoke", "field": "i64x2_extract_lane", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "0"]}, {"type": "v128", "lane_type": "i64", "value": ["18446744073709551615", "18446744073709551615"]}]}, "expected": [{"type": "v128", "lane_type": "i64", "value": ["18446744073709551615", "0"]}]}, + {"type": "assert_return", "line": 688, "action": {"type": "invoke", "field": "f64x2_extract_lane", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "v128", "lane_type": "f64", "value": ["9214871658872686752", "9221120237041090560"]}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["9214871658872686752", "0"]}]}, + {"type": "assert_return", "line": 689, "action": {"type": "invoke", "field": "i64x2_replace_lane", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "0"]}, {"type": "i64", "value": "18446744073709551615"}]}, "expected": [{"type": "i64", "value": "18446744073709551615"}]}, + {"type": "assert_return", "line": 690, "action": {"type": "invoke", "field": "f64x2_replace_lane", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "4612811918334230528"}]}, "expected": [{"type": "f64", "value": "4612811918334230528"}]}, + {"type": "assert_return", "line": 692, "action": {"type": "invoke", "field": "as-v8x16_swizzle-operand", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}, {"type": "i32", "value": "255"}, {"type": "v128", "lane_type": "i8", "value": ["255", "15", "14", "13", "12", "11", "10", "9", "8", "7", "6", "5", "4", "3", "2", "1"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["0", "15", "14", "13", "12", "11", "10", "9", "8", "7", "6", "5", "4", "3", "2", "1"]}]}, + {"type": "assert_return", "line": 696, "action": {"type": "invoke", "field": "as-v8x16_shuffle-operands", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "255", "0", "255", "15", "255", "0", "255", "255", "255", "0", "255", "127", "255", "0", "255"]}, {"type": "i32", "value": "1"}, {"type": "v128", "lane_type": "i8", "value": ["85", "0", "85", "0", "85", "0", "85", "0", "85", "0", "85", "0", "85", "1", "85", "255"]}, {"type": "i32", "value": "0"}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["85", "255", "85", "255", "85", "255", "85", "255", "85", "255", "85", "255", "85", "255", "85", "255"]}]}, + {"type": "module", "line": 703, "filename": "simd_lane.138.wasm"}, + {"type": "assert_return", "line": 750, "action": {"type": "invoke", "field": "as-i8x16_splat-operand", "args": [{"type": "v128", "lane_type": "i8", "value": ["255", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255"]}]}, + {"type": "assert_return", "line": 751, "action": {"type": "invoke", "field": "as-i16x8_splat-operand", "args": [{"type": "v128", "lane_type": "i16", "value": ["65535", "65535", "65535", "65535", "0", "0", "0", "0"]}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["65535", "65535", "65535", "65535", "65535", "65535", "65535", "65535"]}]}, + {"type": "assert_return", "line": 752, "action": {"type": "invoke", "field": "as-i32x4_splat-operand", "args": [{"type": "v128", "lane_type": "i32", "value": ["65536", "0", "0", "0"]}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["65536", "65536", "65536", "65536"]}]}, + {"type": "assert_return", "line": 753, "action": {"type": "invoke", "field": "as-f32x4_splat-operand", "args": [{"type": "v128", "lane_type": "f32", "value": ["1078523331", "2143289344", "2143289344", "2143289344"]}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["1078523331", "1078523331", "1078523331", "1078523331"]}]}, + {"type": "assert_return", "line": 754, "action": {"type": "invoke", "field": "as-i64x2_splat-operand", "args": [{"type": "v128", "lane_type": "i64", "value": ["18446744073709551615", "0"]}]}, "expected": [{"type": "v128", "lane_type": "i64", "value": ["18446744073709551615", "18446744073709551615"]}]}, + {"type": "assert_return", "line": 755, "action": {"type": "invoke", "field": "as-f64x2_splat-operand", "args": [{"type": "v128", "lane_type": "f64", "value": ["9218868437227405312", "9221120237041090560"]}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["9218868437227405312", "9218868437227405312"]}]}, + {"type": "assert_return", "line": 756, "action": {"type": "invoke", "field": "as-i8x16_add-operands", "args": [{"type": "v128", "lane_type": "i8", "value": ["255", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16"]}, {"type": "i32", "value": "1"}, {"type": "v128", "lane_type": "i8", "value": ["16", "15", "14", "13", "12", "11", "10", "9", "8", "7", "6", "5", "4", "3", "2", "255"]}, {"type": "i32", "value": "1"}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["17", "17", "17", "17", "17", "17", "17", "17", "17", "17", "17", "17", "17", "17", "17", "17"]}]}, + {"type": "assert_return", "line": 760, "action": {"type": "invoke", "field": "as-i16x8_add-operands", "args": [{"type": "v128", "lane_type": "i16", "value": ["65535", "4", "9", "16", "25", "36", "49", "64"]}, {"type": "i32", "value": "1"}, {"type": "v128", "lane_type": "i16", "value": ["64", "49", "36", "25", "16", "9", "4", "65535"]}, {"type": "i32", "value": "1"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["65", "53", "45", "41", "41", "45", "53", "65"]}]}, + {"type": "assert_return", "line": 764, "action": {"type": "invoke", "field": "as-i32x4_add-operands", "args": [{"type": "v128", "lane_type": "i32", "value": ["4294967295", "8", "27", "64"]}, {"type": "i32", "value": "1"}, {"type": "v128", "lane_type": "i32", "value": ["64", "27", "8", "4294967295"]}, {"type": "i32", "value": "1"}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["65", "35", "35", "65"]}]}, + {"type": "assert_return", "line": 766, "action": {"type": "invoke", "field": "as-i64x2_add-operands", "args": [{"type": "v128", "lane_type": "i64", "value": ["18446744073709551615", "8"]}, {"type": "i64", "value": "1"}, {"type": "v128", "lane_type": "i64", "value": ["64", "27"]}, {"type": "i64", "value": "1"}]}, "expected": [{"type": "v128", "lane_type": "i64", "value": ["65", "9"]}]}, + {"type": "assert_return", "line": 769, "action": {"type": "invoke", "field": "swizzle-as-i8x16_add-operands", "args": [{"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}, {"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}, {"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}, {"type": "v128", "lane_type": "i8", "value": ["15", "14", "13", "12", "11", "10", "9", "8", "7", "6", "5", "4", "3", "2", "1", "0"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255", "255"]}]}, + {"type": "assert_return", "line": 775, "action": {"type": "invoke", "field": "shuffle-as-i8x16_sub-operands", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}, {"type": "v128", "lane_type": "i8", "value": ["15", "14", "13", "12", "11", "10", "9", "8", "7", "6", "5", "4", "3", "2", "1", "0"]}, {"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}, {"type": "v128", "lane_type": "i8", "value": ["15", "14", "13", "12", "11", "10", "9", "8", "7", "6", "5", "4", "3", "2", "1", "0"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["241", "243", "245", "247", "249", "251", "253", "255", "1", "3", "5", "7", "9", "11", "13", "15"]}]}, + {"type": "assert_return", "line": 782, "action": {"type": "invoke", "field": "as-i8x16_any_true-operand", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "1"}]}, "expected": [{"type": "i32", "value": "1"}]}, + {"type": "assert_return", "line": 783, "action": {"type": "invoke", "field": "as-i16x8_any_true-operand", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "1"}]}, "expected": [{"type": "i32", "value": "1"}]}, + {"type": "assert_return", "line": 784, "action": {"type": "invoke", "field": "as-i32x4_any_true-operand1", "args": [{"type": "v128", "lane_type": "i32", "value": ["1", "0", "0", "0"]}, {"type": "i32", "value": "0"}]}, "expected": [{"type": "i32", "value": "0"}]}, + {"type": "assert_return", "line": 785, "action": {"type": "invoke", "field": "as-i32x4_any_true-operand2", "args": [{"type": "v128", "lane_type": "i64", "value": ["1", "0"]}, {"type": "i64", "value": "0"}]}, "expected": [{"type": "i32", "value": "0"}]}, + {"type": "assert_return", "line": 787, "action": {"type": "invoke", "field": "swizzle-as-i8x16_all_true-operands", "args": [{"type": "v128", "lane_type": "i8", "value": ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16"]}, {"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}]}, "expected": [{"type": "i32", "value": "1"}]}, + {"type": "assert_return", "line": 790, "action": {"type": "invoke", "field": "swizzle-as-i8x16_all_true-operands", "args": [{"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}, {"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "16"]}]}, "expected": [{"type": "i32", "value": "0"}]}, + {"type": "assert_return", "line": 793, "action": {"type": "invoke", "field": "shuffle-as-i8x16_any_true-operands", "args": [{"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}, {"type": "v128", "lane_type": "i8", "value": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15"]}]}, "expected": [{"type": "i32", "value": "1"}]}, + {"type": "module", "line": 799, "filename": "simd_lane.139.wasm"}, + {"type": "assert_return", "line": 821, "action": {"type": "invoke", "field": "as-v128_store-operand-1", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "1"}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["1", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 822, "action": {"type": "invoke", "field": "as-v128_store-operand-2", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "256"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["256", "0", "0", "0", "0", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 823, "action": {"type": "invoke", "field": "as-v128_store-operand-3", "args": [{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}, {"type": "i32", "value": "4294967295"}]}, "expected": [{"type": "v128", "lane_type": "i32", "value": ["4294967295", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 824, "action": {"type": "invoke", "field": "as-v128_store-operand-4", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "1078523331"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["1078523331", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 825, "action": {"type": "invoke", "field": "as-v128_store-operand-5", "args": [{"type": "v128", "lane_type": "i64", "value": ["0", "0"]}, {"type": "i64", "value": "18446744073709551615"}]}, "expected": [{"type": "v128", "lane_type": "i64", "value": ["18446744073709551615", "0"]}]}, + {"type": "assert_return", "line": 826, "action": {"type": "invoke", "field": "as-v128_store-operand-6", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "4614253070214989087"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["4614253070214989087", "0"]}]}, + {"type": "module", "line": 830, "filename": "simd_lane.140.wasm"}, + {"type": "assert_return", "line": 858, "action": {"type": "invoke", "field": "as-if-condition-value", "args": [{"type": "v128", "lane_type": "i8", "value": ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"]}]}, "expected": [{"type": "i32", "value": "0"}]}, + {"type": "assert_return", "line": 859, "action": {"type": "invoke", "field": "as-return-value-1", "args": [{"type": "v128", "lane_type": "i16", "value": ["0", "0", "0", "0", "0", "0", "0", "0"]}, {"type": "i32", "value": "1"}]}, "expected": [{"type": "v128", "lane_type": "i16", "value": ["1", "0", "0", "0", "0", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 860, "action": {"type": "invoke", "field": "as-local_set-value", "args": [{"type": "v128", "lane_type": "i32", "value": ["4294967295", "4294967295", "4294967295", "4294967295"]}]}, "expected": [{"type": "i32", "value": "4294967295"}]}, + {"type": "assert_return", "line": 861, "action": {"type": "invoke", "field": "as-global_set-value-1", "args": [{"type": "v128", "lane_type": "f32", "value": ["0", "0", "0", "0"]}, {"type": "f32", "value": "1078523331"}]}, "expected": [{"type": "v128", "lane_type": "f32", "value": ["1078523331", "0", "0", "0"]}]}, + {"type": "assert_return", "line": 863, "action": {"type": "invoke", "field": "as-return-value-2", "args": [{"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}, {"type": "v128", "lane_type": "i8", "value": ["15", "14", "13", "12", "11", "10", "9", "8", "7", "6", "5", "4", "3", "2", "1", "0"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["255", "254", "253", "252", "251", "250", "249", "248", "247", "246", "245", "244", "243", "242", "241", "240"]}]}, + {"type": "assert_return", "line": 867, "action": {"type": "invoke", "field": "as-global_set-value-2", "args": [{"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255"]}, {"type": "v128", "lane_type": "i8", "value": ["16", "15", "14", "13", "12", "11", "10", "9", "8", "7", "6", "5", "4", "3", "2", "1"]}]}, "expected": [{"type": "v128", "lane_type": "i8", "value": ["240", "241", "242", "243", "244", "245", "246", "247", "8", "7", "6", "5", "4", "3", "2", "1"]}]}, + {"type": "assert_return", "line": 872, "action": {"type": "invoke", "field": "as-local_set-value-1", "args": [{"type": "v128", "lane_type": "i64", "value": ["18446744073709551615", "18446744073709551615"]}]}, "expected": [{"type": "i64", "value": "18446744073709551615"}]}, + {"type": "assert_return", "line": 873, "action": {"type": "invoke", "field": "as-global_set-value-3", "args": [{"type": "v128", "lane_type": "f64", "value": ["0", "0"]}, {"type": "f64", "value": "4614253070214989087"}]}, "expected": [{"type": "v128", "lane_type": "f64", "value": ["4614253070214989087", "0"]}]}, + {"type": "assert_malformed", "line": 877, "filename": "simd_lane.141.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 878, "filename": "simd_lane.142.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 879, "filename": "simd_lane.143.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 880, "filename": "simd_lane.144.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 881, "filename": "simd_lane.145.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 882, "filename": "simd_lane.146.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 883, "filename": "simd_lane.147.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "module", "line": 887, "filename": "simd_lane.148.wasm"}, + {"type": "module", "line": 888, "filename": "simd_lane.149.wasm"}, + {"type": "module", "line": 889, "filename": "simd_lane.150.wasm"}, + {"type": "module", "line": 890, "filename": "simd_lane.151.wasm"}, + {"type": "module", "line": 891, "filename": "simd_lane.152.wasm"}, + {"type": "module", "line": 892, "filename": "simd_lane.153.wasm"}, + {"type": "module", "line": 893, "filename": "simd_lane.154.wasm"}, + {"type": "assert_malformed", "line": 897, "filename": "simd_lane.155.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 902, "filename": "simd_lane.156.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_invalid", "line": 910, "filename": "simd_lane.157.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_malformed", "line": 918, "filename": "simd_lane.158.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 926, "filename": "simd_lane.159.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_invalid", "line": 934, "filename": "simd_lane.160.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_malformed", "line": 942, "filename": "simd_lane.161.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 950, "filename": "simd_lane.162.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_invalid", "line": 958, "filename": "simd_lane.163.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_malformed", "line": 966, "filename": "simd_lane.164.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 974, "filename": "simd_lane.165.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_invalid", "line": 982, "filename": "simd_lane.166.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_malformed", "line": 990, "filename": "simd_lane.167.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 998, "filename": "simd_lane.168.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_invalid", "line": 1006, "filename": "simd_lane.169.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_malformed", "line": 1014, "filename": "simd_lane.170.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 1022, "filename": "simd_lane.171.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_invalid", "line": 1030, "filename": "simd_lane.172.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_malformed", "line": 1038, "filename": "simd_lane.173.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 1046, "filename": "simd_lane.174.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_invalid", "line": 1054, "filename": "simd_lane.175.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 1062, "filename": "simd_lane.176.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_malformed", "line": 1070, "filename": "simd_lane.177.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 1078, "filename": "simd_lane.178.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_invalid", "line": 1086, "filename": "simd_lane.179.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 1094, "filename": "simd_lane.180.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_malformed", "line": 1102, "filename": "simd_lane.181.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 1110, "filename": "simd_lane.182.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_invalid", "line": 1118, "filename": "simd_lane.183.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 1126, "filename": "simd_lane.184.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_malformed", "line": 1134, "filename": "simd_lane.185.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 1142, "filename": "simd_lane.186.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_invalid", "line": 1150, "filename": "simd_lane.187.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 1158, "filename": "simd_lane.188.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_malformed", "line": 1166, "filename": "simd_lane.189.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 1174, "filename": "simd_lane.190.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_invalid", "line": 1182, "filename": "simd_lane.191.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 1190, "filename": "simd_lane.192.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_malformed", "line": 1198, "filename": "simd_lane.193.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 1206, "filename": "simd_lane.194.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_invalid", "line": 1214, "filename": "simd_lane.195.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_invalid", "line": 1222, "filename": "simd_lane.196.wasm", "text": "type mismatch", "module_type": "binary"}, + {"type": "assert_malformed", "line": 1230, "filename": "simd_lane.197.wat", "text": "unexpected token", "module_type": "text"}, + {"type": "assert_malformed", "line": 1238, "filename": "simd_lane.198.wat", "text": "invalid lane length", "module_type": "text"}, + {"type": "assert_invalid", "line": 1249, "filename": "simd_lane.199.wasm", "text": "type mismatch", "module_type": "binary"}, {"type": "assert_malformed", "line": 1259, "filename": "simd_lane.200.wat", "text": "invalid lane length", "module_type": "text"}]} diff --git a/internal/wasm/func_validation.go b/internal/wasm/func_validation.go index e54fec027fe..448976efa0b 100644 --- a/internal/wasm/func_validation.go +++ b/internal/wasm/func_validation.go @@ -31,6 +31,24 @@ func (m *Module) validateFunction(enabledFeatures Features, idx Index, functions return m.validateFunctionWithMaxStackValues(enabledFeatures, idx, functions, globals, memory, tables, maximumValuesOnStack, declaredFunctionIndexes) } +func readMemArg(pc uint64, body []byte) (align, offset uint32, read uint64, err error) { + align, num, err := leb128.DecodeUint32(bytes.NewReader(body[pc:])) + if err != nil { + err = fmt.Errorf("read memory align: %v", err) + return + } + read += num + + offset, num, err = leb128.DecodeUint32(bytes.NewReader(body[pc+num:])) + if err != nil { + err = fmt.Errorf("read memory offset: %v", err) + return + } + + read += num + return align, offset, read, nil +} + // validateFunctionWithMaxStackValues is like validateFunction, but allows overriding maxStackValues for testing. // // * maxStackValues is the maximum height of values stack which the target is allowed to reach. @@ -63,10 +81,11 @@ func (m *Module) validateFunctionWithMaxStackValues( return fmt.Errorf("unknown memory access") } pc++ - align, num, err := leb128.DecodeUint32(bytes.NewReader(body[pc:])) + align, _, read, err := readMemArg(pc, body) if err != nil { - return fmt.Errorf("read memory align: %v", err) + return err } + pc += read - 1 switch op { case OpcodeI32Load: if 1< 32/8 { @@ -239,13 +258,6 @@ func (m *Module) validateFunctionWithMaxStackValues( return err } } - pc += num - // offset - _, num, err = leb128.DecodeUint32(bytes.NewReader(body[pc:])) - if err != nil { - return fmt.Errorf("read memory offset: %v", err) - } - pc += num - 1 } else if OpcodeMemorySize <= op && op <= OpcodeMemoryGrow { if memory == nil { return fmt.Errorf("unknown memory access") @@ -1059,20 +1071,199 @@ func (m *Module) validateFunctionWithMaxStackValues( } pc += 16 valueTypeStack.push(ValueTypeV128) - case OpcodeVecI32x4Add: + case OpcodeVecI8x16Add, OpcodeVecI16x8Add, OpcodeVecI32x4Add, OpcodeVecI64x2Add, + OpcodeVecI8x16Sub, OpcodeVecI16x8Sub, OpcodeVecI32x4Sub, OpcodeVecI64x2Sub: for i := 0; i < 2; i++ { if err := valueTypeStack.popAndVerifyType(ValueTypeV128); err != nil { - return fmt.Errorf("cannot pop the operand for %s: %v", OpcodeVecI32x4AddName, err) + return fmt.Errorf("cannot pop the operand for %s: %v", vectorInstructionName[vecOpcode], err) } } valueTypeStack.push(ValueTypeV128) - case OpcodeVecI64x2Add: - for i := 0; i < 2; i++ { - if err := valueTypeStack.popAndVerifyType(ValueTypeV128); err != nil { - return fmt.Errorf("cannot pop the operand for %s: %v", OpcodeVecI64x2AddName, err) + case OpcodeVecV128AnyTrue: + if err := valueTypeStack.popAndVerifyType(ValueTypeV128); err != nil { + return fmt.Errorf("cannot pop the operand for %s: %v", vectorInstructionName[vecOpcode], err) + } + valueTypeStack.push(ValueTypeI32) + case OpcodeVecI8x16AllTrue, OpcodeVecI16x8AllTrue, OpcodeVecI32x4AllTrue, OpcodeVecI64x2AllTrue: + if err := valueTypeStack.popAndVerifyType(ValueTypeV128); err != nil { + return fmt.Errorf("cannot pop the operand for %s: %v", vectorInstructionName[vecOpcode], err) + } + valueTypeStack.push(ValueTypeI32) + case OpcodeVecV128Load, OpcodeVecV128Load8x8s, OpcodeVecV128Load8x8u, OpcodeVecV128Load16x4s, OpcodeVecV128Load16x4u, + OpcodeVecV128Load32x2s, OpcodeVecV128Load32x2u, OpcodeVecV128Load8Splat, OpcodeVecV128Load16Splat, + OpcodeVecV128Load32Splat, OpcodeVecV128Load64Splat, + OpcodeVecV128Load32zero, OpcodeVecV128Load64zero: + pc++ + align, _, read, err := readMemArg(pc, body) + if err != nil { + return err + } + pc += read - 1 + var maxAlign uint32 + switch vecOpcode { + case OpcodeVecV128Load: + maxAlign = 128 / 8 + case OpcodeVecV128Load8x8s, OpcodeVecV128Load8x8u, OpcodeVecV128Load16x4s, OpcodeVecV128Load16x4u, + OpcodeVecV128Load32x2s, OpcodeVecV128Load32x2u: + maxAlign = 64 / 8 + case OpcodeVecV128Load8Splat: + maxAlign = 1 + case OpcodeVecV128Load16Splat: + maxAlign = 16 / 8 + case OpcodeVecV128Load32Splat: + maxAlign = 32 / 8 + case OpcodeVecV128Load64Splat: + maxAlign = 64 / 8 + case OpcodeVecV128Load32zero: + maxAlign = 32 / 8 + case OpcodeVecV128Load64zero: + maxAlign = 64 / 8 + } + if 1< maxAlign { + return fmt.Errorf("invalid memory alignment %d for %s", align, OpcodeVecV128StoreName) + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return fmt.Errorf("cannot pop the operand for %s: %v", OpcodeVecV128LoadName, err) + } + valueTypeStack.push(ValueTypeV128) + case OpcodeVecV128Store: + pc++ + align, _, read, err := readMemArg(pc, body) + if err != nil { + return err + } + pc += read - 1 + if 1< 128/8 { + return fmt.Errorf("invalid memory alignment %d for %s", align, OpcodeVecV128StoreName) + } + if err := valueTypeStack.popAndVerifyType(ValueTypeV128); err != nil { + return fmt.Errorf("cannot pop the operand for %s: %v", OpcodeVecV128StoreName, err) + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return fmt.Errorf("cannot pop the operand for %s: %v", OpcodeVecV128StoreName, err) + } + case OpcodeVecV128Load8Lane, OpcodeVecV128Load16Lane, OpcodeVecV128Load32Lane, OpcodeVecV128Load64Lane: + attr := vecLoadLanes[vecOpcode] + pc++ + align, _, read, err := readMemArg(pc, body) + if err != nil { + return err + } + if 1< attr.alignMax { + return fmt.Errorf("invalid memory alignment %d for %s", align, OpcodeVecV128Load64LaneName) + } + pc += read + if pc >= uint64(len(body)) { + return fmt.Errorf("lane for %s not found", OpcodeVecV128Load64LaneName) + } + lane := body[pc] + if lane >= attr.laneCeil { + return fmt.Errorf("invalid lane index %d >= %d for %s", lane, attr.laneCeil, vectorInstructionName[vecOpcode]) + } + if err := valueTypeStack.popAndVerifyType(ValueTypeV128); err != nil { + return fmt.Errorf("cannot pop the operand for %s: %v", vectorInstructionName[vecOpcode], err) + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return fmt.Errorf("cannot pop the operand for %s: %v", vectorInstructionName[vecOpcode], err) + } + valueTypeStack.push(ValueTypeV128) + case OpcodeVecV128Store8Lane, OpcodeVecV128Store16Lane, OpcodeVecV128Store32Lane, OpcodeVecV128Store64Lane: + attr := vecStoreLanes[vecOpcode] + pc++ + align, _, read, err := readMemArg(pc, body) + if err != nil { + return err + } + if 1< attr.alignMax { + return fmt.Errorf("invalid memory alignment %d for %s", align, vectorInstructionName[vecOpcode]) + } + pc += read + if pc >= uint64(len(body)) { + return fmt.Errorf("lane for %s not found", vectorInstructionName[vecOpcode]) + } + lane := body[pc] + if lane >= attr.laneCeil { + return fmt.Errorf("invalid lane index %d >= %d", lane, attr.laneCeil) + } + if err := valueTypeStack.popAndVerifyType(ValueTypeV128); err != nil { + return fmt.Errorf("cannot pop the operand for %s: %v", vectorInstructionName[vecOpcode], err) + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return fmt.Errorf("cannot pop the operand for %s: %v", vectorInstructionName[vecOpcode], err) + } + case OpcodeVecI8x16ExtractLaneS, + OpcodeVecI8x16ExtractLaneU, + OpcodeVecI16x8ExtractLaneS, + OpcodeVecI16x8ExtractLaneU, + OpcodeVecI32x4ExtractLane, + OpcodeVecI64x2ExtractLane, + OpcodeVecF32x4ExtractLane, + OpcodeVecF64x2ExtractLane: + pc++ + if pc >= uint64(len(body)) { + return fmt.Errorf("lane for %s not found", vectorInstructionName[vecOpcode]) + } + attr := vecExtractLanes[vecOpcode] + lane := body[pc] + if lane >= attr.laneCeil { + return fmt.Errorf("invalid lane index %d >= %d for %s", lane, attr.laneCeil, vectorInstructionName[vecOpcode]) + } + if err := valueTypeStack.popAndVerifyType(ValueTypeV128); err != nil { + return fmt.Errorf("cannot pop the operand for %s: %v", vectorInstructionName[vecOpcode], err) + } + valueTypeStack.push(attr.resultType) + case OpcodeVecI8x16ReplaceLane, OpcodeVecI16x8ReplaceLane, OpcodeVecI32x4ReplaceLane, + OpcodeVecI64x2ReplaceLane, OpcodeVecF32x4ReplaceLane, OpcodeVecF64x2ReplaceLane: + pc++ + if pc >= uint64(len(body)) { + return fmt.Errorf("lane for %s not found", vectorInstructionName[vecOpcode]) + } + attr := vecReplaceLanes[vecOpcode] + lane := body[pc] + if lane >= attr.laneCeil { + return fmt.Errorf("invalid lane index %d >= %d for %s", lane, attr.laneCeil, vectorInstructionName[vecOpcode]) + } + if err := valueTypeStack.popAndVerifyType(attr.paramType); err != nil { + return fmt.Errorf("cannot pop the operand for %s: %v", vectorInstructionName[vecOpcode], err) + } + if err := valueTypeStack.popAndVerifyType(ValueTypeV128); err != nil { + return fmt.Errorf("cannot pop the operand for %s: %v", vectorInstructionName[vecOpcode], err) + } + valueTypeStack.push(ValueTypeV128) + case OpcodeVecI8x16Splat, OpcodeVecI16x8Splat, OpcodeVecI32x4Splat, + OpcodeVecI64x2Splat, OpcodeVecF32x4Splat, OpcodeVecF64x2Splat: + tp := vecSplatValueTypes[vecOpcode] + if err := valueTypeStack.popAndVerifyType(tp); err != nil { + return fmt.Errorf("cannot pop the operand for %s: %v", vectorInstructionName[vecOpcode], err) + } + valueTypeStack.push(ValueTypeV128) + case OpcodeVecI8x16Swizzle: + if err := valueTypeStack.popAndVerifyType(ValueTypeV128); err != nil { + return fmt.Errorf("cannot pop the operand for %s: %v", vectorInstructionName[vecOpcode], err) + } + if err := valueTypeStack.popAndVerifyType(ValueTypeV128); err != nil { + return fmt.Errorf("cannot pop the operand for %s: %v", vectorInstructionName[vecOpcode], err) + } + valueTypeStack.push(ValueTypeV128) + case OpcodeVecV128i8x16Shuffle: + pc++ + if pc+16 >= uint64(len(body)) { + return fmt.Errorf("16 lane indexes for %s not found", vectorInstructionName[vecOpcode]) + } + lanes := body[pc : pc+16] + for i, l := range lanes { + if l >= 32 { + return fmt.Errorf("invalid lane index[%d] %d >= %d for %s", i, l, 32, vectorInstructionName[vecOpcode]) } } + if err := valueTypeStack.popAndVerifyType(ValueTypeV128); err != nil { + return fmt.Errorf("cannot pop the operand for %s: %v", vectorInstructionName[vecOpcode], err) + } + if err := valueTypeStack.popAndVerifyType(ValueTypeV128); err != nil { + return fmt.Errorf("cannot pop the operand for %s: %v", vectorInstructionName[vecOpcode], err) + } valueTypeStack.push(ValueTypeV128) + pc += 15 default: return fmt.Errorf("TODO: SIMD instruction %s will be implemented in #506", vectorInstructionName[vecOpcode]) } @@ -1260,6 +1451,61 @@ func (m *Module) validateFunctionWithMaxStackValues( return nil } +var vecExtractLanes = [...]struct { + laneCeil byte + resultType ValueType +}{ + OpcodeVecI8x16ExtractLaneS: {laneCeil: 16, resultType: ValueTypeI32}, + OpcodeVecI8x16ExtractLaneU: {laneCeil: 16, resultType: ValueTypeI32}, + OpcodeVecI16x8ExtractLaneS: {laneCeil: 8, resultType: ValueTypeI32}, + OpcodeVecI16x8ExtractLaneU: {laneCeil: 8, resultType: ValueTypeI32}, + OpcodeVecI32x4ExtractLane: {laneCeil: 4, resultType: ValueTypeI32}, + OpcodeVecI64x2ExtractLane: {laneCeil: 2, resultType: ValueTypeI64}, + OpcodeVecF32x4ExtractLane: {laneCeil: 4, resultType: ValueTypeF32}, + OpcodeVecF64x2ExtractLane: {laneCeil: 2, resultType: ValueTypeF64}, +} + +var vecReplaceLanes = [...]struct { + laneCeil byte + paramType ValueType +}{ + OpcodeVecI8x16ReplaceLane: {laneCeil: 16, paramType: ValueTypeI32}, + OpcodeVecI16x8ReplaceLane: {laneCeil: 8, paramType: ValueTypeI32}, + OpcodeVecI32x4ReplaceLane: {laneCeil: 4, paramType: ValueTypeI32}, + OpcodeVecI64x2ReplaceLane: {laneCeil: 2, paramType: ValueTypeI64}, + OpcodeVecF32x4ReplaceLane: {laneCeil: 4, paramType: ValueTypeF32}, + OpcodeVecF64x2ReplaceLane: {laneCeil: 2, paramType: ValueTypeF64}, +} + +var vecStoreLanes = [...]struct { + alignMax uint32 + laneCeil byte +}{ + OpcodeVecV128Store64Lane: {alignMax: 64 / 8, laneCeil: 128 / 64}, + OpcodeVecV128Store32Lane: {alignMax: 32 / 8, laneCeil: 128 / 32}, + OpcodeVecV128Store16Lane: {alignMax: 16 / 8, laneCeil: 128 / 16}, + OpcodeVecV128Store8Lane: {alignMax: 1, laneCeil: 128 / 8}, +} + +var vecLoadLanes = [...]struct { + alignMax uint32 + laneCeil byte +}{ + OpcodeVecV128Load64Lane: {alignMax: 64 / 8, laneCeil: 128 / 64}, + OpcodeVecV128Load32Lane: {alignMax: 32 / 8, laneCeil: 128 / 32}, + OpcodeVecV128Load16Lane: {alignMax: 16 / 8, laneCeil: 128 / 16}, + OpcodeVecV128Load8Lane: {alignMax: 1, laneCeil: 128 / 8}, +} + +var vecSplatValueTypes = [...]ValueType{ + OpcodeVecI8x16Splat: ValueTypeI32, + OpcodeVecI16x8Splat: ValueTypeI32, + OpcodeVecI32x4Splat: ValueTypeI32, + OpcodeVecI64x2Splat: ValueTypeI64, + OpcodeVecF32x4Splat: ValueTypeF32, + OpcodeVecF64x2Splat: ValueTypeF64, +} + type valueTypeStack struct { stack []ValueType stackLimits []int @@ -1449,12 +1695,12 @@ func writeValueTypes(vts []ValueType, ret *strings.Builder) { switch len(vts) { case 0: case 1: - ret.WriteString(api.ValueTypeName(vts[0])) + ret.WriteString(ValueTypeName(vts[0])) default: - ret.WriteString(api.ValueTypeName(vts[0])) + ret.WriteString(ValueTypeName(vts[0])) for _, vt := range vts[1:] { ret.WriteString(", ") - ret.WriteString(api.ValueTypeName(vt)) + ret.WriteString(ValueTypeName(vt)) } } } diff --git a/internal/wasm/instruction.go b/internal/wasm/instruction.go index 0f4243ba8ff..f4f93378c93 100644 --- a/internal/wasm/instruction.go +++ b/internal/wasm/instruction.go @@ -330,12 +330,12 @@ const ( // Loads and stores. OpcodeVecV128Load OpcodeVec = 0x00 - OpcodeVecV128Load8x8_s OpcodeVec = 0x01 - OpcodeVecV128Load8x8_u OpcodeVec = 0x02 - OpcodeVecV128Load16x4_s OpcodeVec = 0x03 - OpcodeVecV128Load16x4_u OpcodeVec = 0x04 - OpcodeVecV128Load32x2_s OpcodeVec = 0x05 - OpcodeVecV128Load32x2_u OpcodeVec = 0x06 + OpcodeVecV128Load8x8s OpcodeVec = 0x01 + OpcodeVecV128Load8x8u OpcodeVec = 0x02 + OpcodeVecV128Load16x4s OpcodeVec = 0x03 + OpcodeVecV128Load16x4u OpcodeVec = 0x04 + OpcodeVecV128Load32x2s OpcodeVec = 0x05 + OpcodeVecV128Load32x2u OpcodeVec = 0x06 OpcodeVecV128Load8Splat OpcodeVec = 0x07 OpcodeVecV128Load16Splat OpcodeVec = 0x08 OpcodeVecV128Load32Splat OpcodeVec = 0x09 @@ -593,21 +593,21 @@ const ( // f64 misc. - OpcodeVecF64x4Ceil OpcodeVec = 0x74 - OpcodeVecF64x4Floor OpcodeVec = 0x75 - OpcodeVecF64x4Trunc OpcodeVec = 0x7a - OpcodeVecF64x4Nearest OpcodeVec = 0x94 - OpcodeVecF64x4Abs OpcodeVec = 0xec - OpcodeVecF64x4Neg OpcodeVec = 0xed - OpcodeVecF64x4Sqrt OpcodeVec = 0xef - OpcodeVecF64x4Add OpcodeVec = 0xf0 - OpcodeVecF64x4Sub OpcodeVec = 0xf1 - OpcodeVecF64x4Mul OpcodeVec = 0xf2 - OpcodeVecF64x4Div OpcodeVec = 0xf3 - OpcodeVecF64x4Min OpcodeVec = 0xf4 - OpcodeVecF64x4Max OpcodeVec = 0xf5 - OpcodeVecF64x4Pmin OpcodeVec = 0xf6 - OpcodeVecF64x4Pmax OpcodeVec = 0xf7 + OpcodeVecF64x2Ceil OpcodeVec = 0x74 + OpcodeVecF64x2Floor OpcodeVec = 0x75 + OpcodeVecF64x2Trunc OpcodeVec = 0x7a + OpcodeVecF64x2Nearest OpcodeVec = 0x94 + OpcodeVecF64x2Abs OpcodeVec = 0xec + OpcodeVecF64x2Neg OpcodeVec = 0xed + OpcodeVecF64x2Sqrt OpcodeVec = 0xef + OpcodeVecF64x2Add OpcodeVec = 0xf0 + OpcodeVecF64x2Sub OpcodeVec = 0xf1 + OpcodeVecF64x2Mul OpcodeVec = 0xf2 + OpcodeVecF64x2Div OpcodeVec = 0xf3 + OpcodeVecF64x2Min OpcodeVec = 0xf4 + OpcodeVecF64x2Max OpcodeVec = 0xf5 + OpcodeVecF64x2Pmin OpcodeVec = 0xf6 + OpcodeVecF64x2Pmax OpcodeVec = 0xf7 // conversions. @@ -1068,12 +1068,12 @@ func MiscInstructionName(oc OpcodeMisc) string { const ( OpcodeVecV128LoadName = "v128.load" - OpcodeVecV128Load8x8_sName = "v128.load8x8_s" - OpcodeVecV128Load8x8_uName = "v128.load8x8_u" - OpcodeVecV128Load16x4_sName = "v128.load16x4_s" - OpcodeVecV128Load16x4_uName = "v128.load16x4_u" - OpcodeVecV128Load32x2_sName = "v128.load32x2_s" - OpcodeVecV128Load32x2_uName = "v128.load32x2_u" + OpcodeVecV128Load8x8SName = "v128.load8x8_s" + OpcodeVecV128Load8x8UName = "v128.load8x8_u" + OpcodeVecV128Load16x4SName = "v128.load16x4_s" + OpcodeVecV128Load16x4UName = "v128.load16x4_u" + OpcodeVecV128Load32x2SName = "v128.load32x2_s" + OpcodeVecV128Load32x2UName = "v128.load32x2_u" OpcodeVecV128Load8SplatName = "v128.load8_splat" OpcodeVecV128Load16SplatName = "v128.load16_splat" OpcodeVecV128Load32SplatName = "v128.load32_splat" @@ -1096,7 +1096,7 @@ const ( OpcodeVecI8x16ReplaceLaneName = "i8x16.replace_lane" OpcodeVecI16x8ExtractLaneSName = "i16x4.extract_lane_s" OpcodeVecI16x8ExtractLaneUName = "i16x4.extract_lane_u" - OpcodeVecI16x8ReplaceLaneName = "i16x4.replace" + OpcodeVecI16x8ReplaceLaneName = "i16x4.replace_lane" OpcodeVecI32x4ExtractLaneName = "i32x4.extract_lane" OpcodeVecI32x4ReplaceLaneName = "i32x4.replace_lane" OpcodeVecI64x2ExtractLaneName = "i64x2.extract_lane" @@ -1278,21 +1278,21 @@ const ( OpcodeVecF32x4MaxName = "f32x4.max" OpcodeVecF32x4PminName = "f32x4.pmin" OpcodeVecF32x4PmaxName = "f32x4.pmax" - OpcodeVecF64x4CeilName = "f64x2.ceil" - OpcodeVecF64x4FloorName = "f64x2.floor" - OpcodeVecF64x4TruncName = "f64x2.trunc" - OpcodeVecF64x4NearestName = "f64x2.nearest" - OpcodeVecF64x4AbsName = "f64x2.abs" - OpcodeVecF64x4NegName = "f64x2.neg" - OpcodeVecF64x4SqrtName = "f64x2.sqrt" - OpcodeVecF64x4AddName = "f64x2.add" - OpcodeVecF64x4SubName = "f64x2.sub" - OpcodeVecF64x4MulName = "f64x2.mul" - OpcodeVecF64x4DivName = "f64x2.div" - OpcodeVecF64x4MinName = "f64x2.min" - OpcodeVecF64x4MaxName = "f64x2.max" - OpcodeVecF64x4PminName = "f64x2.pmin" - OpcodeVecF64x4PmaxName = "f64x2.pmax" + OpcodeVecF64x2CeilName = "f64x2.ceil" + OpcodeVecF64x2FloorName = "f64x2.floor" + OpcodeVecF64x2TruncName = "f64x2.trunc" + OpcodeVecF64x2NearestName = "f64x2.nearest" + OpcodeVecF64x2AbsName = "f64x2.abs" + OpcodeVecF64x2NegName = "f64x2.neg" + OpcodeVecF64x2SqrtName = "f64x2.sqrt" + OpcodeVecF64x2AddName = "f64x2.add" + OpcodeVecF64x2SubName = "f64x2.sub" + OpcodeVecF64x2MulName = "f64x2.mul" + OpcodeVecF64x2DivName = "f64x2.div" + OpcodeVecF64x2MinName = "f64x2.min" + OpcodeVecF64x2MaxName = "f64x2.max" + OpcodeVecF64x2PminName = "f64x2.pmin" + OpcodeVecF64x2PmaxName = "f64x2.pmax" OpcodeVecI32x4TruncSatF32x4SName = "i32x4.trunc_sat_f32x4_s" OpcodeVecI32x4TruncSatF32x4UName = "i32x4.trunc_sat_f32x4_u" OpcodeVecF32x4ConvertI32x4SName = "f32x4.convert_i32x4_s" @@ -1307,12 +1307,12 @@ const ( var vectorInstructionName = map[OpcodeVec]string{ OpcodeVecV128Load: OpcodeVecV128LoadName, - OpcodeVecV128Load8x8_s: OpcodeVecV128Load8x8_sName, - OpcodeVecV128Load8x8_u: OpcodeVecV128Load8x8_uName, - OpcodeVecV128Load16x4_s: OpcodeVecV128Load16x4_sName, - OpcodeVecV128Load16x4_u: OpcodeVecV128Load16x4_uName, - OpcodeVecV128Load32x2_s: OpcodeVecV128Load32x2_sName, - OpcodeVecV128Load32x2_u: OpcodeVecV128Load32x2_uName, + OpcodeVecV128Load8x8s: OpcodeVecV128Load8x8SName, + OpcodeVecV128Load8x8u: OpcodeVecV128Load8x8UName, + OpcodeVecV128Load16x4s: OpcodeVecV128Load16x4SName, + OpcodeVecV128Load16x4u: OpcodeVecV128Load16x4UName, + OpcodeVecV128Load32x2s: OpcodeVecV128Load32x2SName, + OpcodeVecV128Load32x2u: OpcodeVecV128Load32x2UName, OpcodeVecV128Load8Splat: OpcodeVecV128Load8SplatName, OpcodeVecV128Load16Splat: OpcodeVecV128Load16SplatName, OpcodeVecV128Load32Splat: OpcodeVecV128Load32SplatName, @@ -1517,21 +1517,21 @@ var vectorInstructionName = map[OpcodeVec]string{ OpcodeVecF32x4Max: OpcodeVecF32x4MaxName, OpcodeVecF32x4Pmin: OpcodeVecF32x4PminName, OpcodeVecF32x4Pmax: OpcodeVecF32x4PmaxName, - OpcodeVecF64x4Ceil: OpcodeVecF64x4CeilName, - OpcodeVecF64x4Floor: OpcodeVecF64x4FloorName, - OpcodeVecF64x4Trunc: OpcodeVecF64x4TruncName, - OpcodeVecF64x4Nearest: OpcodeVecF64x4NearestName, - OpcodeVecF64x4Abs: OpcodeVecF64x4AbsName, - OpcodeVecF64x4Neg: OpcodeVecF64x4NegName, - OpcodeVecF64x4Sqrt: OpcodeVecF64x4SqrtName, - OpcodeVecF64x4Add: OpcodeVecF64x4AddName, - OpcodeVecF64x4Sub: OpcodeVecF64x4SubName, - OpcodeVecF64x4Mul: OpcodeVecF64x4MulName, - OpcodeVecF64x4Div: OpcodeVecF64x4DivName, - OpcodeVecF64x4Min: OpcodeVecF64x4MinName, - OpcodeVecF64x4Max: OpcodeVecF64x4MaxName, - OpcodeVecF64x4Pmin: OpcodeVecF64x4PminName, - OpcodeVecF64x4Pmax: OpcodeVecF64x4PmaxName, + OpcodeVecF64x2Ceil: OpcodeVecF64x2CeilName, + OpcodeVecF64x2Floor: OpcodeVecF64x2FloorName, + OpcodeVecF64x2Trunc: OpcodeVecF64x2TruncName, + OpcodeVecF64x2Nearest: OpcodeVecF64x2NearestName, + OpcodeVecF64x2Abs: OpcodeVecF64x2AbsName, + OpcodeVecF64x2Neg: OpcodeVecF64x2NegName, + OpcodeVecF64x2Sqrt: OpcodeVecF64x2SqrtName, + OpcodeVecF64x2Add: OpcodeVecF64x2AddName, + OpcodeVecF64x2Sub: OpcodeVecF64x2SubName, + OpcodeVecF64x2Mul: OpcodeVecF64x2MulName, + OpcodeVecF64x2Div: OpcodeVecF64x2DivName, + OpcodeVecF64x2Min: OpcodeVecF64x2MinName, + OpcodeVecF64x2Max: OpcodeVecF64x2MaxName, + OpcodeVecF64x2Pmin: OpcodeVecF64x2PminName, + OpcodeVecF64x2Pmax: OpcodeVecF64x2PmaxName, OpcodeVecI32x4TruncSatF32x4S: OpcodeVecI32x4TruncSatF32x4SName, OpcodeVecI32x4TruncSatF32x4U: OpcodeVecI32x4TruncSatF32x4UName, OpcodeVecF32x4ConvertI32x4S: OpcodeVecF32x4ConvertI32x4SName, diff --git a/internal/wazeroir/compiler.go b/internal/wazeroir/compiler.go index 401a4624408..c8be70ee777 100644 --- a/internal/wazeroir/compiler.go +++ b/internal/wazeroir/compiler.go @@ -798,7 +798,7 @@ operatorSwitch: &OperationGlobalSet{Index: *index}, ) case wasm.OpcodeI32Load: - imm, err := c.readMemoryImmediate(wasm.OpcodeI32LoadName) + imm, err := c.readMemoryArg(wasm.OpcodeI32LoadName) if err != nil { return err } @@ -806,7 +806,7 @@ operatorSwitch: &OperationLoad{Type: UnsignedTypeI32, Arg: imm}, ) case wasm.OpcodeI64Load: - imm, err := c.readMemoryImmediate(wasm.OpcodeI64LoadName) + imm, err := c.readMemoryArg(wasm.OpcodeI64LoadName) if err != nil { return err } @@ -814,7 +814,7 @@ operatorSwitch: &OperationLoad{Type: UnsignedTypeI64, Arg: imm}, ) case wasm.OpcodeF32Load: - imm, err := c.readMemoryImmediate(wasm.OpcodeF32LoadName) + imm, err := c.readMemoryArg(wasm.OpcodeF32LoadName) if err != nil { return err } @@ -822,7 +822,7 @@ operatorSwitch: &OperationLoad{Type: UnsignedTypeF32, Arg: imm}, ) case wasm.OpcodeF64Load: - imm, err := c.readMemoryImmediate(wasm.OpcodeF64LoadName) + imm, err := c.readMemoryArg(wasm.OpcodeF64LoadName) if err != nil { return err } @@ -830,7 +830,7 @@ operatorSwitch: &OperationLoad{Type: UnsignedTypeF64, Arg: imm}, ) case wasm.OpcodeI32Load8S: - imm, err := c.readMemoryImmediate(wasm.OpcodeI32Load8SName) + imm, err := c.readMemoryArg(wasm.OpcodeI32Load8SName) if err != nil { return err } @@ -838,7 +838,7 @@ operatorSwitch: &OperationLoad8{Type: SignedInt32, Arg: imm}, ) case wasm.OpcodeI32Load8U: - imm, err := c.readMemoryImmediate(wasm.OpcodeI32Load8UName) + imm, err := c.readMemoryArg(wasm.OpcodeI32Load8UName) if err != nil { return err } @@ -846,7 +846,7 @@ operatorSwitch: &OperationLoad8{Type: SignedUint32, Arg: imm}, ) case wasm.OpcodeI32Load16S: - imm, err := c.readMemoryImmediate(wasm.OpcodeI32Load16SName) + imm, err := c.readMemoryArg(wasm.OpcodeI32Load16SName) if err != nil { return err } @@ -854,7 +854,7 @@ operatorSwitch: &OperationLoad16{Type: SignedInt32, Arg: imm}, ) case wasm.OpcodeI32Load16U: - imm, err := c.readMemoryImmediate(wasm.OpcodeI32Load16UName) + imm, err := c.readMemoryArg(wasm.OpcodeI32Load16UName) if err != nil { return err } @@ -862,7 +862,7 @@ operatorSwitch: &OperationLoad16{Type: SignedUint32, Arg: imm}, ) case wasm.OpcodeI64Load8S: - imm, err := c.readMemoryImmediate(wasm.OpcodeI64Load8SName) + imm, err := c.readMemoryArg(wasm.OpcodeI64Load8SName) if err != nil { return err } @@ -870,7 +870,7 @@ operatorSwitch: &OperationLoad8{Type: SignedInt64, Arg: imm}, ) case wasm.OpcodeI64Load8U: - imm, err := c.readMemoryImmediate(wasm.OpcodeI64Load8UName) + imm, err := c.readMemoryArg(wasm.OpcodeI64Load8UName) if err != nil { return err } @@ -878,7 +878,7 @@ operatorSwitch: &OperationLoad8{Type: SignedUint64, Arg: imm}, ) case wasm.OpcodeI64Load16S: - imm, err := c.readMemoryImmediate(wasm.OpcodeI64Load16SName) + imm, err := c.readMemoryArg(wasm.OpcodeI64Load16SName) if err != nil { return err } @@ -886,7 +886,7 @@ operatorSwitch: &OperationLoad16{Type: SignedInt64, Arg: imm}, ) case wasm.OpcodeI64Load16U: - imm, err := c.readMemoryImmediate(wasm.OpcodeI64Load16UName) + imm, err := c.readMemoryArg(wasm.OpcodeI64Load16UName) if err != nil { return err } @@ -894,7 +894,7 @@ operatorSwitch: &OperationLoad16{Type: SignedUint64, Arg: imm}, ) case wasm.OpcodeI64Load32S: - imm, err := c.readMemoryImmediate(wasm.OpcodeI64Load32SName) + imm, err := c.readMemoryArg(wasm.OpcodeI64Load32SName) if err != nil { return err } @@ -902,7 +902,7 @@ operatorSwitch: &OperationLoad32{Signed: true, Arg: imm}, ) case wasm.OpcodeI64Load32U: - imm, err := c.readMemoryImmediate(wasm.OpcodeI64Load32UName) + imm, err := c.readMemoryArg(wasm.OpcodeI64Load32UName) if err != nil { return err } @@ -910,7 +910,7 @@ operatorSwitch: &OperationLoad32{Signed: false, Arg: imm}, ) case wasm.OpcodeI32Store: - imm, err := c.readMemoryImmediate(wasm.OpcodeI32StoreName) + imm, err := c.readMemoryArg(wasm.OpcodeI32StoreName) if err != nil { return err } @@ -918,7 +918,7 @@ operatorSwitch: &OperationStore{Type: UnsignedTypeI32, Arg: imm}, ) case wasm.OpcodeI64Store: - imm, err := c.readMemoryImmediate(wasm.OpcodeI64StoreName) + imm, err := c.readMemoryArg(wasm.OpcodeI64StoreName) if err != nil { return err } @@ -926,7 +926,7 @@ operatorSwitch: &OperationStore{Type: UnsignedTypeI64, Arg: imm}, ) case wasm.OpcodeF32Store: - imm, err := c.readMemoryImmediate(wasm.OpcodeF32StoreName) + imm, err := c.readMemoryArg(wasm.OpcodeF32StoreName) if err != nil { return err } @@ -934,7 +934,7 @@ operatorSwitch: &OperationStore{Type: UnsignedTypeF32, Arg: imm}, ) case wasm.OpcodeF64Store: - imm, err := c.readMemoryImmediate(wasm.OpcodeF64StoreName) + imm, err := c.readMemoryArg(wasm.OpcodeF64StoreName) if err != nil { return err } @@ -942,7 +942,7 @@ operatorSwitch: &OperationStore{Type: UnsignedTypeF64, Arg: imm}, ) case wasm.OpcodeI32Store8: - imm, err := c.readMemoryImmediate(wasm.OpcodeI32Store8Name) + imm, err := c.readMemoryArg(wasm.OpcodeI32Store8Name) if err != nil { return err } @@ -950,7 +950,7 @@ operatorSwitch: &OperationStore8{Type: UnsignedInt32, Arg: imm}, ) case wasm.OpcodeI32Store16: - imm, err := c.readMemoryImmediate(wasm.OpcodeI32Store16Name) + imm, err := c.readMemoryArg(wasm.OpcodeI32Store16Name) if err != nil { return err } @@ -958,7 +958,7 @@ operatorSwitch: &OperationStore16{Type: UnsignedInt32, Arg: imm}, ) case wasm.OpcodeI64Store8: - imm, err := c.readMemoryImmediate(wasm.OpcodeI64Store8Name) + imm, err := c.readMemoryArg(wasm.OpcodeI64Store8Name) if err != nil { return err } @@ -966,7 +966,7 @@ operatorSwitch: &OperationStore8{Type: UnsignedInt64, Arg: imm}, ) case wasm.OpcodeI64Store16: - imm, err := c.readMemoryImmediate(wasm.OpcodeI64Store16Name) + imm, err := c.readMemoryArg(wasm.OpcodeI64Store16Name) if err != nil { return err } @@ -974,7 +974,7 @@ operatorSwitch: &OperationStore16{Type: UnsignedInt64, Arg: imm}, ) case wasm.OpcodeI64Store32: - imm, err := c.readMemoryImmediate(wasm.OpcodeI64Store32Name) + imm, err := c.readMemoryArg(wasm.OpcodeI64Store32Name) if err != nil { return err } @@ -1720,26 +1720,380 @@ operatorSwitch: } case wasm.OpcodeVecPrefix: c.pc++ - switch miscOp := c.body[c.pc]; miscOp { + switch vecOp := c.body[c.pc]; vecOp { case wasm.OpcodeVecV128Const: c.pc++ lo := binary.LittleEndian.Uint64(c.body[c.pc : c.pc+8]) c.pc += 8 hi := binary.LittleEndian.Uint64(c.body[c.pc : c.pc+8]) c.emit( - &OperationConstV128{Lo: lo, Hi: hi}, + &OperationV128Const{Lo: lo, Hi: hi}, ) c.pc += 7 + case wasm.OpcodeVecI8x16Add: + c.emit( + &OperationV128Add{Shape: ShapeI8x16}, + ) + case wasm.OpcodeVecI16x8Add: + c.emit( + &OperationV128Add{Shape: ShapeI16x8}, + ) case wasm.OpcodeVecI32x4Add: c.emit( - &OperationAddV128{Shape: ShapeI32x4}, + &OperationV128Add{Shape: ShapeI32x4}, ) case wasm.OpcodeVecI64x2Add: c.emit( - &OperationAddV128{Shape: ShapeI64x2}, + &OperationV128Add{Shape: ShapeI64x2}, + ) + case wasm.OpcodeVecI8x16Sub: + c.emit( + &OperationV128Sub{Shape: ShapeI8x16}, + ) + case wasm.OpcodeVecI16x8Sub: + c.emit( + &OperationV128Sub{Shape: ShapeI16x8}, + ) + case wasm.OpcodeVecI32x4Sub: + c.emit( + &OperationV128Sub{Shape: ShapeI32x4}, + ) + case wasm.OpcodeVecI64x2Sub: + c.emit( + &OperationV128Sub{Shape: ShapeI64x2}, + ) + case wasm.OpcodeVecV128Load: + arg, err := c.readMemoryArg(wasm.OpcodeI32LoadName) + if err != nil { + return err + } + c.emit( + &OperationV128Load{Type: LoadV128Type128, Arg: arg}, + ) + case wasm.OpcodeVecV128Load8x8s: + arg, err := c.readMemoryArg(wasm.OpcodeVecV128Load8x8SName) + if err != nil { + return err + } + c.emit( + &OperationV128Load{Type: LoadV128Type8x8s, Arg: arg}, + ) + case wasm.OpcodeVecV128Load8x8u: + arg, err := c.readMemoryArg(wasm.OpcodeVecV128Load8x8UName) + if err != nil { + return err + } + c.emit( + &OperationV128Load{Type: LoadV128Type8x8u, Arg: arg}, + ) + case wasm.OpcodeVecV128Load16x4s: + arg, err := c.readMemoryArg(wasm.OpcodeVecV128Load16x4SName) + if err != nil { + return err + } + c.emit( + &OperationV128Load{Type: LoadV128Type16x4s, Arg: arg}, + ) + case wasm.OpcodeVecV128Load16x4u: + arg, err := c.readMemoryArg(wasm.OpcodeVecV128Load16x4UName) + if err != nil { + return err + } + c.emit( + &OperationV128Load{Type: LoadV128Type16x4u, Arg: arg}, + ) + case wasm.OpcodeVecV128Load32x2s: + arg, err := c.readMemoryArg(wasm.OpcodeVecV128Load32x2SName) + if err != nil { + return err + } + c.emit( + &OperationV128Load{Type: LoadV128Type32x2s, Arg: arg}, + ) + case wasm.OpcodeVecV128Load32x2u: + arg, err := c.readMemoryArg(wasm.OpcodeVecV128Load32x2UName) + if err != nil { + return err + } + c.emit( + &OperationV128Load{Type: LoadV128Type32x2u, Arg: arg}, + ) + case wasm.OpcodeVecV128Load8Splat: + arg, err := c.readMemoryArg(wasm.OpcodeVecV128Load8SplatName) + if err != nil { + return err + } + c.emit( + &OperationV128Load{Type: LoadV128Type8Splat, Arg: arg}, + ) + case wasm.OpcodeVecV128Load16Splat: + arg, err := c.readMemoryArg(wasm.OpcodeVecV128Load16SplatName) + if err != nil { + return err + } + c.emit( + &OperationV128Load{Type: LoadV128Type16Splat, Arg: arg}, + ) + case wasm.OpcodeVecV128Load32Splat: + arg, err := c.readMemoryArg(wasm.OpcodeVecV128Load32SplatName) + if err != nil { + return err + } + c.emit( + &OperationV128Load{Type: LoadV128Type32Splat, Arg: arg}, + ) + case wasm.OpcodeVecV128Load64Splat: + arg, err := c.readMemoryArg(wasm.OpcodeVecV128Load64SplatName) + if err != nil { + return err + } + c.emit( + &OperationV128Load{Type: LoadV128Type64Splat, Arg: arg}, + ) + case wasm.OpcodeVecV128Load32zero: + arg, err := c.readMemoryArg(wasm.OpcodeVecV128Load32zeroName) + if err != nil { + return err + } + c.emit( + &OperationV128Load{Type: LoadV128Type32zero, Arg: arg}, + ) + case wasm.OpcodeVecV128Load64zero: + arg, err := c.readMemoryArg(wasm.OpcodeVecV128Load64zeroName) + if err != nil { + return err + } + c.emit( + &OperationV128Load{Type: LoadV128Type64zero, Arg: arg}, + ) + case wasm.OpcodeVecV128Load8Lane: + arg, err := c.readMemoryArg(wasm.OpcodeVecV128Load8LaneName) + if err != nil { + return err + } + c.pc++ + laneIndex := c.body[c.pc] + c.emit( + &OperationV128LoadLane{LaneIndex: laneIndex, LaneSize: 8, Arg: arg}, + ) + case wasm.OpcodeVecV128Load16Lane: + arg, err := c.readMemoryArg(wasm.OpcodeVecV128Load16LaneName) + if err != nil { + return err + } + c.pc++ + laneIndex := c.body[c.pc] + c.emit( + &OperationV128LoadLane{LaneIndex: laneIndex, LaneSize: 16, Arg: arg}, + ) + case wasm.OpcodeVecV128Load32Lane: + arg, err := c.readMemoryArg(wasm.OpcodeVecV128Load32LaneName) + if err != nil { + return err + } + c.pc++ + laneIndex := c.body[c.pc] + c.emit( + &OperationV128LoadLane{LaneIndex: laneIndex, LaneSize: 32, Arg: arg}, + ) + case wasm.OpcodeVecV128Load64Lane: + arg, err := c.readMemoryArg(wasm.OpcodeVecV128Load64LaneName) + if err != nil { + return err + } + c.pc++ + laneIndex := c.body[c.pc] + c.emit( + &OperationV128LoadLane{LaneIndex: laneIndex, LaneSize: 64, Arg: arg}, + ) + case wasm.OpcodeVecV128Store: + arg, err := c.readMemoryArg(wasm.OpcodeVecV128StoreName) + if err != nil { + return err + } + c.emit( + &OperationV128Store{Arg: arg}, + ) + case wasm.OpcodeVecV128Store8Lane: + arg, err := c.readMemoryArg(wasm.OpcodeVecV128Store8LaneName) + if err != nil { + return err + } + c.pc++ + laneIndex := c.body[c.pc] + c.emit( + &OperationV128StoreLane{LaneIndex: laneIndex, LaneSize: 8, Arg: arg}, + ) + case wasm.OpcodeVecV128Store16Lane: + arg, err := c.readMemoryArg(wasm.OpcodeVecV128Store16LaneName) + if err != nil { + return err + } + c.pc++ + laneIndex := c.body[c.pc] + c.emit( + &OperationV128StoreLane{LaneIndex: laneIndex, LaneSize: 16, Arg: arg}, + ) + case wasm.OpcodeVecV128Store32Lane: + arg, err := c.readMemoryArg(wasm.OpcodeVecV128Store32LaneName) + if err != nil { + return err + } + c.pc++ + laneIndex := c.body[c.pc] + c.emit( + &OperationV128StoreLane{LaneIndex: laneIndex, LaneSize: 32, Arg: arg}, + ) + case wasm.OpcodeVecV128Store64Lane: + arg, err := c.readMemoryArg(wasm.OpcodeVecV128Store64LaneName) + if err != nil { + return err + } + c.pc++ + laneIndex := c.body[c.pc] + c.emit( + &OperationV128StoreLane{LaneIndex: laneIndex, LaneSize: 64, Arg: arg}, + ) + case wasm.OpcodeVecI8x16ExtractLaneS: + c.pc++ + laneIndex := c.body[c.pc] + c.emit( + &OperationV128ExtractLane{LaneIndex: laneIndex, Shape: ShapeI8x16, Signed: true}, + ) + case wasm.OpcodeVecI8x16ExtractLaneU: + c.pc++ + laneIndex := c.body[c.pc] + c.emit( + &OperationV128ExtractLane{LaneIndex: laneIndex, Shape: ShapeI8x16, Signed: false}, + ) + case wasm.OpcodeVecI16x8ExtractLaneS: + c.pc++ + laneIndex := c.body[c.pc] + c.emit( + &OperationV128ExtractLane{LaneIndex: laneIndex, Shape: ShapeI16x8, Signed: true}, + ) + case wasm.OpcodeVecI16x8ExtractLaneU: + c.pc++ + laneIndex := c.body[c.pc] + c.emit( + &OperationV128ExtractLane{LaneIndex: laneIndex, Shape: ShapeI16x8, Signed: false}, + ) + case wasm.OpcodeVecI32x4ExtractLane: + c.pc++ + laneIndex := c.body[c.pc] + c.emit( + &OperationV128ExtractLane{LaneIndex: laneIndex, Shape: ShapeI32x4}, + ) + case wasm.OpcodeVecI64x2ExtractLane: + c.pc++ + laneIndex := c.body[c.pc] + c.emit( + &OperationV128ExtractLane{LaneIndex: laneIndex, Shape: ShapeI64x2}, + ) + case wasm.OpcodeVecF32x4ExtractLane: + c.pc++ + laneIndex := c.body[c.pc] + c.emit( + &OperationV128ExtractLane{LaneIndex: laneIndex, Shape: ShapeF32x4}, + ) + case wasm.OpcodeVecF64x2ExtractLane: + c.pc++ + laneIndex := c.body[c.pc] + c.emit( + &OperationV128ExtractLane{LaneIndex: laneIndex, Shape: ShapeF64x2}, + ) + case wasm.OpcodeVecI8x16ReplaceLane: + c.pc++ + laneIndex := c.body[c.pc] + c.emit( + &OperationV128ReplaceLane{LaneIndex: laneIndex, Shape: ShapeI8x16}, + ) + case wasm.OpcodeVecI16x8ReplaceLane: + c.pc++ + laneIndex := c.body[c.pc] + c.emit( + &OperationV128ReplaceLane{LaneIndex: laneIndex, Shape: ShapeI16x8}, + ) + case wasm.OpcodeVecI32x4ReplaceLane: + c.pc++ + laneIndex := c.body[c.pc] + c.emit( + &OperationV128ReplaceLane{LaneIndex: laneIndex, Shape: ShapeI32x4}, + ) + case wasm.OpcodeVecI64x2ReplaceLane: + c.pc++ + laneIndex := c.body[c.pc] + c.emit( + &OperationV128ReplaceLane{LaneIndex: laneIndex, Shape: ShapeI64x2}, + ) + case wasm.OpcodeVecF32x4ReplaceLane: + c.pc++ + laneIndex := c.body[c.pc] + c.emit( + &OperationV128ReplaceLane{LaneIndex: laneIndex, Shape: ShapeF32x4}, + ) + case wasm.OpcodeVecF64x2ReplaceLane: + c.pc++ + laneIndex := c.body[c.pc] + c.emit( + &OperationV128ReplaceLane{LaneIndex: laneIndex, Shape: ShapeF64x2}, + ) + case wasm.OpcodeVecI8x16Splat: + c.emit( + &OperationV128Splat{Shape: ShapeI8x16}, + ) + case wasm.OpcodeVecI16x8Splat: + c.emit( + &OperationV128Splat{Shape: ShapeI16x8}, + ) + case wasm.OpcodeVecI32x4Splat: + c.emit( + &OperationV128Splat{Shape: ShapeI32x4}, + ) + case wasm.OpcodeVecI64x2Splat: + c.emit( + &OperationV128Splat{Shape: ShapeI64x2}, + ) + case wasm.OpcodeVecF32x4Splat: + c.emit( + &OperationV128Splat{Shape: ShapeF32x4}, + ) + case wasm.OpcodeVecF64x2Splat: + c.emit( + &OperationV128Splat{Shape: ShapeF64x2}, + ) + case wasm.OpcodeVecI8x16Swizzle: + c.emit( + &OperationV128Swizzle{}, + ) + case wasm.OpcodeVecV128i8x16Shuffle: + c.pc++ + op := &OperationV128Shuffle{} + copy(op.Lanes[:], c.body[c.pc:c.pc+16]) + c.emit(op) + c.pc += 15 + case wasm.OpcodeVecV128AnyTrue: + c.emit( + &OperationV128AnyTrue{}, + ) + case wasm.OpcodeVecI8x16AllTrue: + c.emit( + &OperationV128AllTrue{Shape: ShapeI8x16}, + ) + case wasm.OpcodeVecI16x8AllTrue: + c.emit( + &OperationV128AllTrue{Shape: ShapeI16x8}, + ) + case wasm.OpcodeVecI32x4AllTrue: + c.emit( + &OperationV128AllTrue{Shape: ShapeI32x4}, + ) + case wasm.OpcodeVecI64x2AllTrue: + c.emit( + &OperationV128AllTrue{Shape: ShapeI64x2}, ) default: - return fmt.Errorf("unsupported vector instruction in wazeroir: 0x%x", op) + return fmt.Errorf("unsupported vector instruction in wazeroir: %s", wasm.VectorInstructionName(vecOp)) } default: return fmt.Errorf("unsupported instruction in wazeroir: 0x%x", op) @@ -1882,7 +2236,7 @@ func (c *compiler) emitDefaultValue(t wasm.ValueType) { c.emit(&OperationConstF64{Value: 0}) case wasm.ValueTypeV128: c.stackPush(UnsignedTypeV128) - c.emit(&OperationConstV128{Hi: 0, Lo: 0}) + c.emit(&OperationV128Const{Hi: 0, Lo: 0}) } } @@ -1945,7 +2299,7 @@ func (c *compiler) stackLenInUint64(ceil int) (ret int) { return } -func (c *compiler) readMemoryImmediate(tag string) (*MemoryImmediate, error) { +func (c *compiler) readMemoryArg(tag string) (*MemoryArg, error) { r := bytes.NewReader(c.body[c.pc+1:]) alignment, num, err := leb128.DecodeUint32(r) if err != nil { @@ -1957,5 +2311,5 @@ func (c *compiler) readMemoryImmediate(tag string) (*MemoryImmediate, error) { return nil, fmt.Errorf("reading offset for %s: %w", tag, err) } c.pc += num - return &MemoryImmediate{Offset: offset, Alignment: alignment}, nil + return &MemoryArg{Offset: offset, Alignment: alignment}, nil } diff --git a/internal/wazeroir/compiler_test.go b/internal/wazeroir/compiler_test.go index 7eda8f649af..f8ec6ebf203 100644 --- a/internal/wazeroir/compiler_test.go +++ b/internal/wazeroir/compiler_test.go @@ -931,7 +931,7 @@ func TestCompile_Locals(t *testing.T) { }}, }, expected: []Operation{ - &OperationConstV128{Lo: 0, Hi: 0}, + &OperationV128Const{Lo: 0, Hi: 0}, &OperationPick{Depth: 1, IsTargetVector: true}, // [p[0].low, p[0].high] -> [p[0].low, p[0].high, p[0].low, p[0].high] &OperationDrop{Depth: &InclusiveRange{Start: 0, End: 3}}, &OperationBr{Target: &BranchTarget{}}, // return! @@ -952,7 +952,7 @@ func TestCompile_Locals(t *testing.T) { }, expected: []Operation{ // [p[0].lo, p[1].hi] -> [p[0].lo, p[1].hi, 0x01, 0x02] - &OperationConstV128{Lo: 0x01, Hi: 0x02}, + &OperationV128Const{Lo: 0x01, Hi: 0x02}, // [p[0].lo, p[1].hi, 0x01, 0x02] -> [0x01, 0x02, p[0].lo, p[1].hi] &OperationSwap{Depth: 3, IsTargetVector: true}, // [0x01, 0x02, p[0].lo, p[1].hi] -> [0x02, 0x01] @@ -997,9 +997,9 @@ func TestCompile_Locals(t *testing.T) { }}, }, expected: []Operation{ - &OperationConstV128{Lo: 0, Hi: 0}, + &OperationV128Const{Lo: 0, Hi: 0}, // [p[0].lo, p[1].hi] -> [p[0].lo, p[1].hi, 0x01, 0x02] - &OperationConstV128{Lo: 0x01, Hi: 0x02}, + &OperationV128Const{Lo: 0x01, Hi: 0x02}, // [p[0].lo, p[1].hi, 0x01, 0x02] -> [0x01, 0x02, p[0].lo, p[1].hi] &OperationSwap{Depth: 3, IsTargetVector: true}, // [p[0].lo, 0x02, 0x01, p[1].hi] -> [0x02, 0x01] @@ -1023,7 +1023,7 @@ func TestCompile_Locals(t *testing.T) { }, expected: []Operation{ // [p[0].lo, p[1].hi] -> [p[0].lo, p[1].hi, 0x01, 0x02] - &OperationConstV128{Lo: 0x01, Hi: 0x02}, + &OperationV128Const{Lo: 0x01, Hi: 0x02}, // [p[0].lo, p[1].hi, 0x01, 0x02] -> [p[0].lo, p[1].hi, 0x01, 0x02, 0x01, 0x02] &OperationPick{Depth: 1, IsTargetVector: true}, // [p[0].lo, p[1].hi, 0x01, 0x02, 0x01, 0x02] -> [0x01, 0x02, 0x01, 0x02, p[0].lo, p[1].hi] @@ -1071,9 +1071,9 @@ func TestCompile_Locals(t *testing.T) { }}, }, expected: []Operation{ - &OperationConstV128{Lo: 0, Hi: 0}, + &OperationV128Const{Lo: 0, Hi: 0}, // [p[0].lo, p[1].hi] -> [p[0].lo, p[1].hi, 0x01, 0x02] - &OperationConstV128{Lo: 0x01, Hi: 0x02}, + &OperationV128Const{Lo: 0x01, Hi: 0x02}, // [p[0].lo, p[1].hi, 0x01, 0x02] -> [p[0].lo, p[1].hi, 0x01, 0x02, 0x01, 0x02] &OperationPick{Depth: 1, IsTargetVector: true}, // [p[0].lo, p[1].hi, 0x01, 0x02, 0x01, 0x2] -> [0x01, 0x02, 0x01, 0x02, p[0].lo, p[1].hi] diff --git a/internal/wazeroir/format.go b/internal/wazeroir/format.go index 33d88eb0e22..c665d032c5b 100644 --- a/internal/wazeroir/format.go +++ b/internal/wazeroir/format.go @@ -181,9 +181,9 @@ func formatOperation(w io.StringWriter, b Operation) { out = "u64" } str = fmt.Sprintf("%s.extend_from.%s", out, in) - case *OperationConstV128: + case *OperationV128Const: str = fmt.Sprintf("v128.const [%#x, %#x]", o.Lo, o.Hi) - case *OperationAddV128: + case *OperationV128Add: str = fmt.Sprintf("v128.add (shape=%s)", shapeName(o.Shape)) default: panic("unreachable: a bug in wazeroir implementation") diff --git a/internal/wazeroir/operations.go b/internal/wazeroir/operations.go index 6cc1e1771de..31fe42aa98a 100644 --- a/internal/wazeroir/operations.go +++ b/internal/wazeroir/operations.go @@ -1,6 +1,8 @@ package wazeroir -import "fmt" +import ( + "fmt" +) type UnsignedInt byte @@ -290,10 +292,34 @@ func (o OperationKind) String() (ret string) { ret = "TableGrow" case OperationKindTableFill: ret = "TableFill" - case OperationKindConstV128: + case OperationKindV128Const: ret = "ConstV128" case OperationKindV128Add: ret = "V128Add" + case OperationKindV128Sub: + ret = "V128Sub" + case OperationKindV128Load: + ret = "V128Load" + case OperationKindV128LoadLane: + ret = "V128LoadLane" + case OperationKindV128Store: + ret = "V128Store" + case OperationKindV128StoreLane: + ret = "V128StoreLane" + case OperationKindV128ExtractLane: + ret = "V128ExtractLane" + case OperationKindV128ReplaceLane: + ret = "V128ReplaceLane" + case OperationKindV128Splat: + ret = "V128Splat" + case OperationKindV128Shuffle: + ret = "V128Shuffle" + case OperationKindV128Swizzle: + ret = "V128Swizzle" + case OperationKindV128AnyTrue: + ret = "V128AnyTrue" + case OperationKindV128AllTrue: + ret = "V128AllTrue" default: panic("BUG") } @@ -388,8 +414,21 @@ const ( OperationKindTableSize OperationKindTableGrow OperationKindTableFill - OperationKindConstV128 + + OperationKindV128Const OperationKindV128Add + OperationKindV128Sub + OperationKindV128Load + OperationKindV128LoadLane + OperationKindV128Store + OperationKindV128StoreLane + OperationKindV128ExtractLane + OperationKindV128ReplaceLane + OperationKindV128Splat + OperationKindV128Shuffle + OperationKindV128Swizzle + OperationKindV128AnyTrue + OperationKindV128AllTrue ) type Label struct { @@ -569,10 +608,10 @@ func (o *OperationGlobalSet) Kind() OperationKind { return OperationKindGlobalSet } -// MemoryImmediate is the "memarg" to all memory instructions. +// MemoryArg is the "memarg" to all memory instructions. // // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#memory-instructions%E2%91%A0 -type MemoryImmediate struct { +type MemoryArg struct { // Alignment the expected alignment (expressed as the exponent of a power of 2). Default to the natural alignment. // // "Natural alignment" is defined here as the smallest power of two that can hold the size of the value type. Ex @@ -586,7 +625,7 @@ type MemoryImmediate struct { type OperationLoad struct { Type UnsignedType - Arg *MemoryImmediate + Arg *MemoryArg } func (o *OperationLoad) Kind() OperationKind { @@ -595,7 +634,7 @@ func (o *OperationLoad) Kind() OperationKind { type OperationLoad8 struct { Type SignedInt - Arg *MemoryImmediate + Arg *MemoryArg } func (o *OperationLoad8) Kind() OperationKind { @@ -604,7 +643,7 @@ func (o *OperationLoad8) Kind() OperationKind { type OperationLoad16 struct { Type SignedInt - Arg *MemoryImmediate + Arg *MemoryArg } func (o *OperationLoad16) Kind() OperationKind { @@ -613,7 +652,7 @@ func (o *OperationLoad16) Kind() OperationKind { type OperationLoad32 struct { Signed bool - Arg *MemoryImmediate + Arg *MemoryArg } func (o *OperationLoad32) Kind() OperationKind { @@ -622,7 +661,7 @@ func (o *OperationLoad32) Kind() OperationKind { type OperationStore struct { Type UnsignedType - Arg *MemoryImmediate + Arg *MemoryArg } func (o *OperationStore) Kind() OperationKind { @@ -632,7 +671,7 @@ func (o *OperationStore) Kind() OperationKind { type OperationStore8 struct { // TODO: Semantically Type doesn't affect operation so consider deleting this field. Type UnsignedInt - Arg *MemoryImmediate + Arg *MemoryArg } func (o *OperationStore8) Kind() OperationKind { @@ -642,7 +681,7 @@ func (o *OperationStore8) Kind() OperationKind { type OperationStore16 struct { // TODO: Semantically Type doesn't affect operation so consider deleting this field. Type UnsignedInt - Arg *MemoryImmediate + Arg *MemoryArg } func (o *OperationStore16) Kind() OperationKind { @@ -650,7 +689,7 @@ func (o *OperationStore16) Kind() OperationKind { } type OperationStore32 struct { - Arg *MemoryImmediate + Arg *MemoryArg } // Kind implements Operation.Kind. @@ -1167,14 +1206,14 @@ func (o *OperationTableFill) Kind() OperationKind { return OperationKindTableFill } -// OperationConstV128 implements Operation. -type OperationConstV128 struct { +// OperationV128Const implements Operation. +type OperationV128Const struct { Lo, Hi uint64 } // Kind implements Operation.Kind. -func (o *OperationConstV128) Kind() OperationKind { - return OperationKindConstV128 +func (o *OperationV128Const) Kind() OperationKind { + return OperationKindV128Const } // Shape corresponds to a shape of v128 values. @@ -1208,12 +1247,174 @@ func shapeName(s Shape) (ret string) { return } -// OperationAddV128 implements Operation. -type OperationAddV128 struct { +// OperationV128Add implements Operation. +type OperationV128Add struct { Shape Shape } // Kind implements Operation.Kind. -func (o *OperationAddV128) Kind() OperationKind { +func (o *OperationV128Add) Kind() OperationKind { return OperationKindV128Add } + +// OperationV128Sub implements Operation. +type OperationV128Sub struct { + Shape Shape +} + +// Kind implements Operation.Kind. +func (o *OperationV128Sub) Kind() OperationKind { + return OperationKindV128Sub +} + +type LoadV128Type = byte + +const ( + // LoadV128Type128 corresponds to wasm.OpcodeVecV128LoadName. + LoadV128Type128 LoadV128Type = iota + // LoadV128Type8x8s corresponds to wasm.OpcodeVecV128Load8x8SName. + LoadV128Type8x8s + // LoadV128Type8x8u corresponds to wasm.OpcodeVecV128Load8x8UName. + LoadV128Type8x8u + // LoadV128Type16x4s corresponds to wasm.OpcodeVecV128Load16x4SName + LoadV128Type16x4s + // LoadV128Type16x4u corresponds to wasm.OpcodeVecV128Load16x4UName + LoadV128Type16x4u + // LoadV128Type32x2s corresponds to wasm.OpcodeVecV128Load32x2SName + LoadV128Type32x2s + // LoadV128Type32x2u corresponds to wasm.OpcodeVecV128Load32x2UName + LoadV128Type32x2u + // LoadV128Type8Splat corresponds to wasm.OpcodeVecV128Load8SplatName + LoadV128Type8Splat + // LoadV128Type16Splat corresponds to wasm.OpcodeVecV128Load16SplatName + LoadV128Type16Splat + // LoadV128Type32Splat corresponds to wasm.OpcodeVecV128Load32SplatName + LoadV128Type32Splat + // LoadV128Type64Splat corresponds to wasm.OpcodeVecV128Load64SplatName + LoadV128Type64Splat + // LoadV128Type32zero corresponds to wasm.OpcodeVecV128Load32zeroName + LoadV128Type32zero + // LoadV128Type64zero corresponds to wasm.OpcodeVecV128Load64zeroName + LoadV128Type64zero +) + +// OperationV128Load implements Operation. +type OperationV128Load struct { + Type LoadV128Type + Arg *MemoryArg +} + +// Kind implements Operation.Kind. +func (o *OperationV128Load) Kind() OperationKind { + return OperationKindV128Load +} + +// OperationV128LoadLane implements Operation. +type OperationV128LoadLane struct { + // LaneIndex is >=0 && <(128/LaneSize). + LaneIndex byte + // LaneSize is either 8, 16, 32, or 64. + LaneSize byte + Arg *MemoryArg +} + +// Kind implements Operation.Kind. +func (o *OperationV128LoadLane) Kind() OperationKind { + return OperationKindV128LoadLane +} + +// OperationV128Store implements Operation. +type OperationV128Store struct { + Arg *MemoryArg +} + +// Kind implements Operation.Kind. +func (o *OperationV128Store) Kind() OperationKind { + return OperationKindV128Store +} + +// OperationV128StoreLane implements Operation. +type OperationV128StoreLane struct { + // LaneIndex is >=0 && <(128/LaneSize). + LaneIndex byte + // LaneSize is either 8, 16, 32, or 64. + LaneSize byte + Arg *MemoryArg +} + +// Kind implements Operation.Kind. +func (o *OperationV128StoreLane) Kind() OperationKind { + return OperationKindV128StoreLane +} + +// OperationV128ExtractLane implements Operation. +type OperationV128ExtractLane struct { + // LaneIndex is >=0 && =0 &&