package ssa import ( "fmt" "math" "strings" "github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi" ) // Opcode represents a SSA instruction. type Opcode uint32 // Instruction represents an instruction whose opcode is specified by // Opcode. Since Go doesn't have union type, we use this flattened type // for all instructions, and therefore each field has different meaning // depending on Opcode. type Instruction struct { // id is the unique ID of this instruction which ascends from 0 following the order of program. id int opcode Opcode u1, u2 uint64 v Value v2 Value v3 Value vs Values typ Type prev, next *Instruction // rValue is the (first) return value of this instruction. // For branching instructions except for OpcodeBrTable, they hold BlockID to jump cast to Value. rValue Value // rValues are the rest of the return values of this instruction. // For OpcodeBrTable, it holds the list of BlockID to jump cast to Value. rValues Values gid InstructionGroupID sourceOffset SourceOffset live bool alreadyLowered bool } // SourceOffset represents the offset of the source of an instruction. type SourceOffset int64 const sourceOffsetUnknown = -1 // Valid returns true if this source offset is valid. func (l SourceOffset) Valid() bool { return l != sourceOffsetUnknown } func (i *Instruction) annotateSourceOffset(line SourceOffset) { i.sourceOffset = line } // SourceOffset returns the source offset of this instruction. func (i *Instruction) SourceOffset() SourceOffset { return i.sourceOffset } // Opcode returns the opcode of this instruction. func (i *Instruction) Opcode() Opcode { return i.opcode } // GroupID returns the InstructionGroupID of this instruction. func (i *Instruction) GroupID() InstructionGroupID { return i.gid } // MarkLowered marks this instruction as already lowered. func (i *Instruction) MarkLowered() { i.alreadyLowered = true } // Lowered returns true if this instruction is already lowered. func (i *Instruction) Lowered() bool { return i.alreadyLowered } // resetInstruction resets this instruction to the initial state. func resetInstruction(i *Instruction) { *i = Instruction{} i.v = ValueInvalid i.v2 = ValueInvalid i.v3 = ValueInvalid i.rValue = ValueInvalid i.typ = typeInvalid i.vs = ValuesNil i.sourceOffset = sourceOffsetUnknown } // InstructionGroupID is assigned to each instruction and represents a group of instructions // where each instruction is interchangeable with others except for the last instruction // in the group which has side effects. In short, InstructionGroupID is determined by the side effects of instructions. // That means, if there's an instruction with side effect between two instructions, then these two instructions // will have different instructionGroupID. Note that each block always ends with branching, which is with side effects, // therefore, instructions in different blocks always have different InstructionGroupID(s). // // The notable application of this is used in lowering SSA-level instruction to a ISA specific instruction, // where we eagerly try to merge multiple instructions into single operation etc. Such merging cannot be done // if these instruction have different InstructionGroupID since it will change the semantics of a program. // // See passDeadCodeElimination. type InstructionGroupID uint32 // Returns Value(s) produced by this instruction if any. // The `first` is the first return value, and `rest` is the rest of the values. func (i *Instruction) Returns() (first Value, rest []Value) { if i.IsBranching() { return ValueInvalid, nil } return i.rValue, i.rValues.View() } // Return returns a Value(s) produced by this instruction if any. // If there's multiple return values, only the first one is returned. func (i *Instruction) Return() (first Value) { return i.rValue } // Args returns the arguments to this instruction. func (i *Instruction) Args() (v1, v2, v3 Value, vs []Value) { return i.v, i.v2, i.v3, i.vs.View() } // Arg returns the first argument to this instruction. func (i *Instruction) Arg() Value { return i.v } // Arg2 returns the first two arguments to this instruction. func (i *Instruction) Arg2() (Value, Value) { return i.v, i.v2 } // ArgWithLane returns the first argument to this instruction, and the lane type. func (i *Instruction) ArgWithLane() (Value, VecLane) { return i.v, VecLane(i.u1) } // Arg2WithLane returns the first two arguments to this instruction, and the lane type. func (i *Instruction) Arg2WithLane() (Value, Value, VecLane) { return i.v, i.v2, VecLane(i.u1) } // ShuffleData returns the first two arguments to this instruction and 2 uint64s `lo`, `hi`. // // Note: Each uint64 encodes a sequence of 8 bytes where each byte encodes a VecLane, // so that the 128bit integer `hi<<64|lo` packs a slice `[16]VecLane`, // where `lane[0]` is the least significant byte, and `lane[n]` is shifted to offset `n*8`. func (i *Instruction) ShuffleData() (v Value, v2 Value, lo uint64, hi uint64) { return i.v, i.v2, i.u1, i.u2 } // Arg3 returns the first three arguments to this instruction. func (i *Instruction) Arg3() (Value, Value, Value) { return i.v, i.v2, i.v3 } // Next returns the next instruction laid out next to itself. func (i *Instruction) Next() *Instruction { return i.next } // Prev returns the previous instruction laid out prior to itself. func (i *Instruction) Prev() *Instruction { return i.prev } // IsBranching returns true if this instruction is a branching instruction. func (i *Instruction) IsBranching() bool { switch i.opcode { case OpcodeJump, OpcodeBrz, OpcodeBrnz, OpcodeBrTable: return true default: return false } } // TODO: complete opcode comments. const ( OpcodeInvalid Opcode = iota // OpcodeUndefined is a placeholder for undefined opcode. This can be used for debugging to intentionally // cause a crash at certain point. OpcodeUndefined // OpcodeJump takes the list of args to the `block` and unconditionally jumps to it. OpcodeJump // OpcodeBrz branches into `blk` with `args` if the value `c` equals zero: `Brz c, blk, args`. OpcodeBrz // OpcodeBrnz branches into `blk` with `args` if the value `c` is not zero: `Brnz c, blk, args`. OpcodeBrnz // OpcodeBrTable takes the index value `index`, and branches into `labelX`. If the `index` is out of range, // it branches into the last labelN: `BrTable index, [label1, label2, ... labelN]`. OpcodeBrTable // OpcodeExitWithCode exit the execution immediately. OpcodeExitWithCode // OpcodeExitIfTrueWithCode exits the execution immediately if the value `c` is not zero. OpcodeExitIfTrueWithCode // OpcodeReturn returns from the function: `return rvalues`. OpcodeReturn // OpcodeCall calls a function specified by the symbol FN with arguments `args`: `returnvals = Call FN, args...` // This is a "near" call, which means the call target is known at compile time, and the target is relatively close // to this function. If the target cannot be reached by near call, the backend fails to compile. OpcodeCall // OpcodeCallIndirect calls a function specified by `callee` which is a function address: `returnvals = call_indirect SIG, callee, args`. // Note that this is different from call_indirect in Wasm, which also does type checking, etc. OpcodeCallIndirect // OpcodeSplat performs a vector splat operation: `v = Splat.lane x`. OpcodeSplat // OpcodeSwizzle performs a vector swizzle operation: `v = Swizzle.lane x, y`. OpcodeSwizzle // OpcodeInsertlane inserts a lane value into a vector: `v = InsertLane x, y, Idx`. OpcodeInsertlane // OpcodeExtractlane extracts a lane value from a vector: `v = ExtractLane x, Idx`. OpcodeExtractlane // OpcodeLoad loads a Type value from the [base + offset] address: `v = Load base, offset`. OpcodeLoad // OpcodeStore stores a Type value to the [base + offset] address: `Store v, base, offset`. OpcodeStore // OpcodeUload8 loads the 8-bit value from the [base + offset] address, zero-extended to 64 bits: `v = Uload8 base, offset`. OpcodeUload8 // OpcodeSload8 loads the 8-bit value from the [base + offset] address, sign-extended to 64 bits: `v = Sload8 base, offset`. OpcodeSload8 // OpcodeIstore8 stores the 8-bit value to the [base + offset] address, sign-extended to 64 bits: `Istore8 v, base, offset`. OpcodeIstore8 // OpcodeUload16 loads the 16-bit value from the [base + offset] address, zero-extended to 64 bits: `v = Uload16 base, offset`. OpcodeUload16 // OpcodeSload16 loads the 16-bit value from the [base + offset] address, sign-extended to 64 bits: `v = Sload16 base, offset`. OpcodeSload16 // OpcodeIstore16 stores the 16-bit value to the [base + offset] address, zero-extended to 64 bits: `Istore16 v, base, offset`. OpcodeIstore16 // OpcodeUload32 loads the 32-bit value from the [base + offset] address, zero-extended to 64 bits: `v = Uload32 base, offset`. OpcodeUload32 // OpcodeSload32 loads the 32-bit value from the [base + offset] address, sign-extended to 64 bits: `v = Sload32 base, offset`. OpcodeSload32 // OpcodeIstore32 stores the 32-bit value to the [base + offset] address, zero-extended to 64 bits: `Istore16 v, base, offset`. OpcodeIstore32 // OpcodeLoadSplat represents a load that replicates the loaded value to all lanes `v = LoadSplat.lane p, Offset`. OpcodeLoadSplat // OpcodeVZeroExtLoad loads a scalar single/double precision floating point value from the [p + Offset] address, // and zero-extend it to the V128 value: `v = VExtLoad p, Offset`. OpcodeVZeroExtLoad // OpcodeIconst represents the integer const. OpcodeIconst // OpcodeF32const represents the single-precision const. OpcodeF32const // OpcodeF64const represents the double-precision const. OpcodeF64const // OpcodeVconst represents the 128bit vector const. OpcodeVconst // OpcodeVbor computes binary or between two 128bit vectors: `v = bor x, y`. OpcodeVbor // OpcodeVbxor computes binary xor between two 128bit vectors: `v = bxor x, y`. OpcodeVbxor // OpcodeVband computes binary and between two 128bit vectors: `v = band x, y`. OpcodeVband // OpcodeVbandnot computes binary and-not between two 128bit vectors: `v = bandnot x, y`. OpcodeVbandnot // OpcodeVbnot negates a 128bit vector: `v = bnot x`. OpcodeVbnot // OpcodeVbitselect uses the bits in the control mask c to select the corresponding bit from x when 1 // and y when 0: `v = bitselect c, x, y`. OpcodeVbitselect // OpcodeShuffle shuffles two vectors using the given 128-bit immediate: `v = shuffle imm, x, y`. // For each byte in the immediate, a value i in [0, 15] selects the i-th byte in vector x; // i in [16, 31] selects the (i-16)-th byte in vector y. OpcodeShuffle // OpcodeSelect chooses between two values based on a condition `c`: `v = Select c, x, y`. OpcodeSelect // OpcodeVanyTrue performs a any true operation: `s = VanyTrue a`. OpcodeVanyTrue // OpcodeVallTrue performs a lane-wise all true operation: `s = VallTrue.lane a`. OpcodeVallTrue // OpcodeVhighBits performs a lane-wise extract of the high bits: `v = VhighBits.lane a`. OpcodeVhighBits // OpcodeIcmp compares two integer values with the given condition: `v = icmp Cond, x, y`. OpcodeIcmp // OpcodeVIcmp compares two integer values with the given condition: `v = vicmp Cond, x, y` on vector. OpcodeVIcmp // OpcodeIcmpImm compares an integer value with the immediate value on the given condition: `v = icmp_imm Cond, x, Y`. OpcodeIcmpImm // OpcodeIadd performs an integer addition: `v = Iadd x, y`. OpcodeIadd // OpcodeVIadd performs an integer addition: `v = VIadd.lane x, y` on vector. OpcodeVIadd // OpcodeVSaddSat performs a signed saturating vector addition: `v = VSaddSat.lane x, y` on vector. OpcodeVSaddSat // OpcodeVUaddSat performs an unsigned saturating vector addition: `v = VUaddSat.lane x, y` on vector. OpcodeVUaddSat // OpcodeIsub performs an integer subtraction: `v = Isub x, y`. OpcodeIsub // OpcodeVIsub performs an integer subtraction: `v = VIsub.lane x, y` on vector. OpcodeVIsub // OpcodeVSsubSat performs a signed saturating vector subtraction: `v = VSsubSat.lane x, y` on vector. OpcodeVSsubSat // OpcodeVUsubSat performs an unsigned saturating vector subtraction: `v = VUsubSat.lane x, y` on vector. OpcodeVUsubSat // OpcodeVImin performs a signed integer min: `v = VImin.lane x, y` on vector. OpcodeVImin // OpcodeVUmin performs an unsigned integer min: `v = VUmin.lane x, y` on vector. OpcodeVUmin // OpcodeVImax performs a signed integer max: `v = VImax.lane x, y` on vector. OpcodeVImax // OpcodeVUmax performs an unsigned integer max: `v = VUmax.lane x, y` on vector. OpcodeVUmax // OpcodeVAvgRound performs an unsigned integer avg, truncating to zero: `v = VAvgRound.lane x, y` on vector. OpcodeVAvgRound // OpcodeVImul performs an integer multiplication: `v = VImul.lane x, y` on vector. OpcodeVImul // OpcodeVIneg negates the given integer vector value: `v = VIneg x`. OpcodeVIneg // OpcodeVIpopcnt counts the number of 1-bits in the given vector: `v = VIpopcnt x`. OpcodeVIpopcnt // OpcodeVIabs returns the absolute value for the given vector value: `v = VIabs.lane x`. OpcodeVIabs // OpcodeVIshl shifts x left by (y mod lane-width): `v = VIshl.lane x, y` on vector. OpcodeVIshl // OpcodeVUshr shifts x right by (y mod lane-width), unsigned: `v = VUshr.lane x, y` on vector. OpcodeVUshr // OpcodeVSshr shifts x right by (y mod lane-width), signed: `v = VSshr.lane x, y` on vector. OpcodeVSshr // OpcodeVFabs takes the absolute value of a floating point value: `v = VFabs.lane x on vector. OpcodeVFabs // OpcodeVFmax takes the maximum of two floating point values: `v = VFmax.lane x, y on vector. OpcodeVFmax // OpcodeVFmin takes the minimum of two floating point values: `v = VFmin.lane x, y on vector. OpcodeVFmin // OpcodeVFneg negates the given floating point vector value: `v = VFneg x`. OpcodeVFneg // OpcodeVFadd performs a floating point addition: `v = VFadd.lane x, y` on vector. OpcodeVFadd // OpcodeVFsub performs a floating point subtraction: `v = VFsub.lane x, y` on vector. OpcodeVFsub // OpcodeVFmul performs a floating point multiplication: `v = VFmul.lane x, y` on vector. OpcodeVFmul // OpcodeVFdiv performs a floating point division: `v = VFdiv.lane x, y` on vector. OpcodeVFdiv // OpcodeVFcmp compares two float values with the given condition: `v = VFcmp.lane Cond, x, y` on float. OpcodeVFcmp // OpcodeVCeil takes the ceiling of the given floating point value: `v = ceil.lane x` on vector. OpcodeVCeil // OpcodeVFloor takes the floor of the given floating point value: `v = floor.lane x` on vector. OpcodeVFloor // OpcodeVTrunc takes the truncation of the given floating point value: `v = trunc.lane x` on vector. OpcodeVTrunc // OpcodeVNearest takes the nearest integer of the given floating point value: `v = nearest.lane x` on vector. OpcodeVNearest // OpcodeVMaxPseudo computes the lane-wise maximum value `v = VMaxPseudo.lane x, y` on vector defined as `x < y ? x : y`. OpcodeVMaxPseudo // OpcodeVMinPseudo computes the lane-wise minimum value `v = VMinPseudo.lane x, y` on vector defined as `y < x ? x : y`. OpcodeVMinPseudo // OpcodeVSqrt takes the minimum of two floating point values: `v = VFmin.lane x, y` on vector. OpcodeVSqrt // OpcodeVFcvtToUintSat converts a floating point value to an unsigned integer: `v = FcvtToUintSat.lane x` on vector. OpcodeVFcvtToUintSat // OpcodeVFcvtToSintSat converts a floating point value to a signed integer: `v = VFcvtToSintSat.lane x` on vector. OpcodeVFcvtToSintSat // OpcodeVFcvtFromUint converts a floating point value from an unsigned integer: `v = FcvtFromUint.lane x` on vector. // x is always a 32-bit integer lane, and the result is either a 32-bit or 64-bit floating point-sized vector. OpcodeVFcvtFromUint // OpcodeVFcvtFromSint converts a floating point value from a signed integer: `v = VFcvtFromSint.lane x` on vector. // x is always a 32-bit integer lane, and the result is either a 32-bit or 64-bit floating point-sized vector. OpcodeVFcvtFromSint // OpcodeImul performs an integer multiplication: `v = Imul x, y`. OpcodeImul // OpcodeUdiv performs the unsigned integer division `v = Udiv x, y`. OpcodeUdiv // OpcodeSdiv performs the signed integer division `v = Sdiv x, y`. OpcodeSdiv // OpcodeUrem computes the remainder of the unsigned integer division `v = Urem x, y`. OpcodeUrem // OpcodeSrem computes the remainder of the signed integer division `v = Srem x, y`. OpcodeSrem // OpcodeBand performs a binary and: `v = Band x, y`. OpcodeBand // OpcodeBor performs a binary or: `v = Bor x, y`. OpcodeBor // OpcodeBxor performs a binary xor: `v = Bxor x, y`. OpcodeBxor // OpcodeBnot performs a binary not: `v = Bnot x`. OpcodeBnot // OpcodeRotl rotates the given integer value to the left: `v = Rotl x, y`. OpcodeRotl // OpcodeRotr rotates the given integer value to the right: `v = Rotr x, y`. OpcodeRotr // OpcodeIshl does logical shift left: `v = Ishl x, y`. OpcodeIshl // OpcodeUshr does logical shift right: `v = Ushr x, y`. OpcodeUshr // OpcodeSshr does arithmetic shift right: `v = Sshr x, y`. OpcodeSshr // OpcodeClz counts the number of leading zeros: `v = clz x`. OpcodeClz // OpcodeCtz counts the number of trailing zeros: `v = ctz x`. OpcodeCtz // OpcodePopcnt counts the number of 1-bits: `v = popcnt x`. OpcodePopcnt // OpcodeFcmp compares two floating point values: `v = fcmp Cond, x, y`. OpcodeFcmp // OpcodeFadd performs a floating point addition: / `v = Fadd x, y`. OpcodeFadd // OpcodeFsub performs a floating point subtraction: `v = Fsub x, y`. OpcodeFsub // OpcodeFmul performs a floating point multiplication: `v = Fmul x, y`. OpcodeFmul // OpcodeSqmulRoundSat performs a lane-wise saturating rounding multiplication // in Q15 format: `v = SqmulRoundSat.lane x,y` on vector. OpcodeSqmulRoundSat // OpcodeFdiv performs a floating point division: `v = Fdiv x, y`. OpcodeFdiv // OpcodeSqrt takes the square root of the given floating point value: `v = sqrt x`. OpcodeSqrt // OpcodeFneg negates the given floating point value: `v = Fneg x`. OpcodeFneg // OpcodeFabs takes the absolute value of the given floating point value: `v = fabs x`. OpcodeFabs // OpcodeFcopysign copies the sign of the second floating point value to the first floating point value: // `v = Fcopysign x, y`. OpcodeFcopysign // OpcodeFmin takes the minimum of two floating point values: `v = fmin x, y`. OpcodeFmin // OpcodeFmax takes the maximum of two floating point values: `v = fmax x, y`. OpcodeFmax // OpcodeCeil takes the ceiling of the given floating point value: `v = ceil x`. OpcodeCeil // OpcodeFloor takes the floor of the given floating point value: `v = floor x`. OpcodeFloor // OpcodeTrunc takes the truncation of the given floating point value: `v = trunc x`. OpcodeTrunc // OpcodeNearest takes the nearest integer of the given floating point value: `v = nearest x`. OpcodeNearest // OpcodeBitcast is a bitcast operation: `v = bitcast x`. OpcodeBitcast // OpcodeIreduce narrow the given integer: `v = Ireduce x`. OpcodeIreduce // OpcodeSnarrow converts two input vectors x, y into a smaller lane vector by narrowing each lane, signed `v = Snarrow.lane x, y`. OpcodeSnarrow // OpcodeUnarrow converts two input vectors x, y into a smaller lane vector by narrowing each lane, unsigned `v = Unarrow.lane x, y`. OpcodeUnarrow // OpcodeSwidenLow converts low half of the smaller lane vector to a larger lane vector, sign extended: `v = SwidenLow.lane x`. OpcodeSwidenLow // OpcodeSwidenHigh converts high half of the smaller lane vector to a larger lane vector, sign extended: `v = SwidenHigh.lane x`. OpcodeSwidenHigh // OpcodeUwidenLow converts low half of the smaller lane vector to a larger lane vector, zero (unsigned) extended: `v = UwidenLow.lane x`. OpcodeUwidenLow // OpcodeUwidenHigh converts high half of the smaller lane vector to a larger lane vector, zero (unsigned) extended: `v = UwidenHigh.lane x`. OpcodeUwidenHigh // OpcodeExtIaddPairwise is a lane-wise integer extended pairwise addition producing extended results (twice wider results than the inputs): `v = extiadd_pairwise x, y` on vector. OpcodeExtIaddPairwise // OpcodeWideningPairwiseDotProductS is a lane-wise widening pairwise dot product with signed saturation: `v = WideningPairwiseDotProductS x, y` on vector. // Currently, the only lane is i16, and the result is i32. OpcodeWideningPairwiseDotProductS // OpcodeUExtend zero-extends the given integer: `v = UExtend x, from->to`. OpcodeUExtend // OpcodeSExtend sign-extends the given integer: `v = SExtend x, from->to`. OpcodeSExtend // OpcodeFpromote promotes the given floating point value: `v = Fpromote x`. OpcodeFpromote // OpcodeFvpromoteLow converts the two lower single-precision floating point lanes // to the two double-precision lanes of the result: `v = FvpromoteLow.lane x` on vector. OpcodeFvpromoteLow // OpcodeFdemote demotes the given float point value: `v = Fdemote x`. OpcodeFdemote // OpcodeFvdemote converts the two double-precision floating point lanes // to two lower single-precision lanes of the result `v = Fvdemote.lane x`. OpcodeFvdemote // OpcodeFcvtToUint converts a floating point value to an unsigned integer: `v = FcvtToUint x`. OpcodeFcvtToUint // OpcodeFcvtToSint converts a floating point value to a signed integer: `v = FcvtToSint x`. OpcodeFcvtToSint // OpcodeFcvtToUintSat converts a floating point value to an unsigned integer: `v = FcvtToUintSat x` which saturates on overflow. OpcodeFcvtToUintSat // OpcodeFcvtToSintSat converts a floating point value to a signed integer: `v = FcvtToSintSat x` which saturates on overflow. OpcodeFcvtToSintSat // OpcodeFcvtFromUint converts an unsigned integer to a floating point value: `v = FcvtFromUint x`. OpcodeFcvtFromUint // OpcodeFcvtFromSint converts a signed integer to a floating point value: `v = FcvtFromSint x`. OpcodeFcvtFromSint // OpcodeAtomicRmw is atomic read-modify-write operation: `v = atomic_rmw op, p, offset, value`. OpcodeAtomicRmw // OpcodeAtomicCas is atomic compare-and-swap operation. OpcodeAtomicCas // OpcodeAtomicLoad is atomic load operation. OpcodeAtomicLoad // OpcodeAtomicStore is atomic store operation. OpcodeAtomicStore // OpcodeFence is a memory fence operation. OpcodeFence // opcodeEnd marks the end of the opcode list. opcodeEnd ) // AtomicRmwOp represents the atomic read-modify-write operation. type AtomicRmwOp byte const ( // AtomicRmwOpAdd is an atomic add operation. AtomicRmwOpAdd AtomicRmwOp = iota // AtomicRmwOpSub is an atomic sub operation. AtomicRmwOpSub // AtomicRmwOpAnd is an atomic and operation. AtomicRmwOpAnd // AtomicRmwOpOr is an atomic or operation. AtomicRmwOpOr // AtomicRmwOpXor is an atomic xor operation. AtomicRmwOpXor // AtomicRmwOpXchg is an atomic swap operation. AtomicRmwOpXchg ) // String implements the fmt.Stringer. func (op AtomicRmwOp) String() string { switch op { case AtomicRmwOpAdd: return "add" case AtomicRmwOpSub: return "sub" case AtomicRmwOpAnd: return "and" case AtomicRmwOpOr: return "or" case AtomicRmwOpXor: return "xor" case AtomicRmwOpXchg: return "xchg" } panic(fmt.Sprintf("unknown AtomicRmwOp: %d", op)) } // returnTypesFn provides the info to determine the type of instruction. // t1 is the type of the first result, ts are the types of the remaining results. type returnTypesFn func(b *builder, instr *Instruction) (t1 Type, ts []Type) var ( returnTypesFnNoReturns returnTypesFn = func(b *builder, instr *Instruction) (t1 Type, ts []Type) { return typeInvalid, nil } returnTypesFnSingle = func(b *builder, instr *Instruction) (t1 Type, ts []Type) { return instr.typ, nil } returnTypesFnI32 = func(b *builder, instr *Instruction) (t1 Type, ts []Type) { return TypeI32, nil } returnTypesFnF32 = func(b *builder, instr *Instruction) (t1 Type, ts []Type) { return TypeF32, nil } returnTypesFnF64 = func(b *builder, instr *Instruction) (t1 Type, ts []Type) { return TypeF64, nil } returnTypesFnV128 = func(b *builder, instr *Instruction) (t1 Type, ts []Type) { return TypeV128, nil } ) // sideEffect provides the info to determine if an instruction has side effects which // is used to determine if it can be optimized out, interchanged with others, etc. type sideEffect byte const ( sideEffectUnknown sideEffect = iota // sideEffectStrict represents an instruction with side effects, and should be always alive plus cannot be reordered. sideEffectStrict // sideEffectTraps represents an instruction that can trap, and should be always alive but can be reordered within the group. sideEffectTraps // sideEffectNone represents an instruction without side effects, and can be eliminated if the result is not used, plus can be reordered within the group. sideEffectNone ) // instructionSideEffects provides the info to determine if an instruction has side effects. // Instructions with side effects must not be eliminated regardless whether the result is used or not. var instructionSideEffects = [opcodeEnd]sideEffect{ OpcodeUndefined: sideEffectStrict, OpcodeJump: sideEffectStrict, OpcodeIconst: sideEffectNone, OpcodeCall: sideEffectStrict, OpcodeCallIndirect: sideEffectStrict, OpcodeIadd: sideEffectNone, OpcodeImul: sideEffectNone, OpcodeIsub: sideEffectNone, OpcodeIcmp: sideEffectNone, OpcodeExtractlane: sideEffectNone, OpcodeInsertlane: sideEffectNone, OpcodeBand: sideEffectNone, OpcodeBor: sideEffectNone, OpcodeBxor: sideEffectNone, OpcodeRotl: sideEffectNone, OpcodeRotr: sideEffectNone, OpcodeFcmp: sideEffectNone, OpcodeFadd: sideEffectNone, OpcodeClz: sideEffectNone, OpcodeCtz: sideEffectNone, OpcodePopcnt: sideEffectNone, OpcodeLoad: sideEffectNone, OpcodeLoadSplat: sideEffectNone, OpcodeUload8: sideEffectNone, OpcodeUload16: sideEffectNone, OpcodeUload32: sideEffectNone, OpcodeSload8: sideEffectNone, OpcodeSload16: sideEffectNone, OpcodeSload32: sideEffectNone, OpcodeSExtend: sideEffectNone, OpcodeUExtend: sideEffectNone, OpcodeSwidenLow: sideEffectNone, OpcodeUwidenLow: sideEffectNone, OpcodeSwidenHigh: sideEffectNone, OpcodeUwidenHigh: sideEffectNone, OpcodeSnarrow: sideEffectNone, OpcodeUnarrow: sideEffectNone, OpcodeSwizzle: sideEffectNone, OpcodeShuffle: sideEffectNone, OpcodeSplat: sideEffectNone, OpcodeFsub: sideEffectNone, OpcodeF32const: sideEffectNone, OpcodeF64const: sideEffectNone, OpcodeIshl: sideEffectNone, OpcodeSshr: sideEffectNone, OpcodeUshr: sideEffectNone, OpcodeStore: sideEffectStrict, OpcodeIstore8: sideEffectStrict, OpcodeIstore16: sideEffectStrict, OpcodeIstore32: sideEffectStrict, OpcodeExitWithCode: sideEffectStrict, OpcodeExitIfTrueWithCode: sideEffectStrict, OpcodeReturn: sideEffectStrict, OpcodeBrz: sideEffectStrict, OpcodeBrnz: sideEffectStrict, OpcodeBrTable: sideEffectStrict, OpcodeFdiv: sideEffectNone, OpcodeFmul: sideEffectNone, OpcodeFmax: sideEffectNone, OpcodeSqmulRoundSat: sideEffectNone, OpcodeSelect: sideEffectNone, OpcodeFmin: sideEffectNone, OpcodeFneg: sideEffectNone, OpcodeFcvtToSint: sideEffectTraps, OpcodeFcvtToUint: sideEffectTraps, OpcodeFcvtFromSint: sideEffectNone, OpcodeFcvtFromUint: sideEffectNone, OpcodeFcvtToSintSat: sideEffectNone, OpcodeFcvtToUintSat: sideEffectNone, OpcodeVFcvtFromUint: sideEffectNone, OpcodeVFcvtFromSint: sideEffectNone, OpcodeFdemote: sideEffectNone, OpcodeFvpromoteLow: sideEffectNone, OpcodeFvdemote: sideEffectNone, OpcodeFpromote: sideEffectNone, OpcodeBitcast: sideEffectNone, OpcodeIreduce: sideEffectNone, OpcodeSqrt: sideEffectNone, OpcodeCeil: sideEffectNone, OpcodeFloor: sideEffectNone, OpcodeTrunc: sideEffectNone, OpcodeNearest: sideEffectNone, OpcodeSdiv: sideEffectTraps, OpcodeSrem: sideEffectTraps, OpcodeUdiv: sideEffectTraps, OpcodeUrem: sideEffectTraps, OpcodeFabs: sideEffectNone, OpcodeFcopysign: sideEffectNone, OpcodeExtIaddPairwise: sideEffectNone, OpcodeVconst: sideEffectNone, OpcodeVbor: sideEffectNone, OpcodeVbxor: sideEffectNone, OpcodeVband: sideEffectNone, OpcodeVbandnot: sideEffectNone, OpcodeVbnot: sideEffectNone, OpcodeVbitselect: sideEffectNone, OpcodeVanyTrue: sideEffectNone, OpcodeVallTrue: sideEffectNone, OpcodeVhighBits: sideEffectNone, OpcodeVIadd: sideEffectNone, OpcodeVSaddSat: sideEffectNone, OpcodeVUaddSat: sideEffectNone, OpcodeVIsub: sideEffectNone, OpcodeVSsubSat: sideEffectNone, OpcodeVUsubSat: sideEffectNone, OpcodeVIcmp: sideEffectNone, OpcodeVImin: sideEffectNone, OpcodeVUmin: sideEffectNone, OpcodeVImax: sideEffectNone, OpcodeVUmax: sideEffectNone, OpcodeVAvgRound: sideEffectNone, OpcodeVImul: sideEffectNone, OpcodeVIabs: sideEffectNone, OpcodeVIneg: sideEffectNone, OpcodeVIpopcnt: sideEffectNone, OpcodeVIshl: sideEffectNone, OpcodeVSshr: sideEffectNone, OpcodeVUshr: sideEffectNone, OpcodeVSqrt: sideEffectNone, OpcodeVFabs: sideEffectNone, OpcodeVFmin: sideEffectNone, OpcodeVFmax: sideEffectNone, OpcodeVFneg: sideEffectNone, OpcodeVFadd: sideEffectNone, OpcodeVFsub: sideEffectNone, OpcodeVFmul: sideEffectNone, OpcodeVFdiv: sideEffectNone, OpcodeVFcmp: sideEffectNone, OpcodeVCeil: sideEffectNone, OpcodeVFloor: sideEffectNone, OpcodeVTrunc: sideEffectNone, OpcodeVNearest: sideEffectNone, OpcodeVMaxPseudo: sideEffectNone, OpcodeVMinPseudo: sideEffectNone, OpcodeVFcvtToUintSat: sideEffectNone, OpcodeVFcvtToSintSat: sideEffectNone, OpcodeVZeroExtLoad: sideEffectNone, OpcodeAtomicRmw: sideEffectStrict, OpcodeAtomicLoad: sideEffectStrict, OpcodeAtomicStore: sideEffectStrict, OpcodeAtomicCas: sideEffectStrict, OpcodeFence: sideEffectStrict, OpcodeWideningPairwiseDotProductS: sideEffectNone, } // sideEffect returns true if this instruction has side effects. func (i *Instruction) sideEffect() sideEffect { if e := instructionSideEffects[i.opcode]; e == sideEffectUnknown { panic("BUG: side effect info not registered for " + i.opcode.String()) } else { return e } } // instructionReturnTypes provides the function to determine the return types of an instruction. var instructionReturnTypes = [opcodeEnd]returnTypesFn{ OpcodeExtIaddPairwise: returnTypesFnV128, OpcodeVbor: returnTypesFnV128, OpcodeVbxor: returnTypesFnV128, OpcodeVband: returnTypesFnV128, OpcodeVbnot: returnTypesFnV128, OpcodeVbandnot: returnTypesFnV128, OpcodeVbitselect: returnTypesFnV128, OpcodeVanyTrue: returnTypesFnI32, OpcodeVallTrue: returnTypesFnI32, OpcodeVhighBits: returnTypesFnI32, OpcodeVIadd: returnTypesFnV128, OpcodeVSaddSat: returnTypesFnV128, OpcodeVUaddSat: returnTypesFnV128, OpcodeVIsub: returnTypesFnV128, OpcodeVSsubSat: returnTypesFnV128, OpcodeVUsubSat: returnTypesFnV128, OpcodeVIcmp: returnTypesFnV128, OpcodeVImin: returnTypesFnV128, OpcodeVUmin: returnTypesFnV128, OpcodeVImax: returnTypesFnV128, OpcodeVUmax: returnTypesFnV128, OpcodeVImul: returnTypesFnV128, OpcodeVAvgRound: returnTypesFnV128, OpcodeVIabs: returnTypesFnV128, OpcodeVIneg: returnTypesFnV128, OpcodeVIpopcnt: returnTypesFnV128, OpcodeVIshl: returnTypesFnV128, OpcodeVSshr: returnTypesFnV128, OpcodeVUshr: returnTypesFnV128, OpcodeExtractlane: returnTypesFnSingle, OpcodeInsertlane: returnTypesFnV128, OpcodeBand: returnTypesFnSingle, OpcodeFcopysign: returnTypesFnSingle, OpcodeBitcast: returnTypesFnSingle, OpcodeBor: returnTypesFnSingle, OpcodeBxor: returnTypesFnSingle, OpcodeRotl: returnTypesFnSingle, OpcodeRotr: returnTypesFnSingle, OpcodeIshl: returnTypesFnSingle, OpcodeSshr: returnTypesFnSingle, OpcodeSdiv: returnTypesFnSingle, OpcodeSrem: returnTypesFnSingle, OpcodeUdiv: returnTypesFnSingle, OpcodeUrem: returnTypesFnSingle, OpcodeUshr: returnTypesFnSingle, OpcodeJump: returnTypesFnNoReturns, OpcodeUndefined: returnTypesFnNoReturns, OpcodeIconst: returnTypesFnSingle, OpcodeSelect: returnTypesFnSingle, OpcodeSExtend: returnTypesFnSingle, OpcodeUExtend: returnTypesFnSingle, OpcodeSwidenLow: returnTypesFnV128, OpcodeUwidenLow: returnTypesFnV128, OpcodeSwidenHigh: returnTypesFnV128, OpcodeUwidenHigh: returnTypesFnV128, OpcodeSnarrow: returnTypesFnV128, OpcodeUnarrow: returnTypesFnV128, OpcodeSwizzle: returnTypesFnSingle, OpcodeShuffle: returnTypesFnV128, OpcodeSplat: returnTypesFnV128, OpcodeIreduce: returnTypesFnSingle, OpcodeFabs: returnTypesFnSingle, OpcodeSqrt: returnTypesFnSingle, OpcodeCeil: returnTypesFnSingle, OpcodeFloor: returnTypesFnSingle, OpcodeTrunc: returnTypesFnSingle, OpcodeNearest: returnTypesFnSingle, OpcodeCallIndirect: func(b *builder, instr *Instruction) (t1 Type, ts []Type) { sigID := SignatureID(instr.u1) sig, ok := b.signatures[sigID] if !ok { panic("BUG") } switch len(sig.Results) { case 0: t1 = typeInvalid case 1: t1 = sig.Results[0] default: t1, ts = sig.Results[0], sig.Results[1:] } return }, OpcodeCall: func(b *builder, instr *Instruction) (t1 Type, ts []Type) { sigID := SignatureID(instr.u2) sig, ok := b.signatures[sigID] if !ok { panic("BUG") } switch len(sig.Results) { case 0: t1 = typeInvalid case 1: t1 = sig.Results[0] default: t1, ts = sig.Results[0], sig.Results[1:] } return }, OpcodeLoad: returnTypesFnSingle, OpcodeVZeroExtLoad: returnTypesFnV128, OpcodeLoadSplat: returnTypesFnV128, OpcodeIadd: returnTypesFnSingle, OpcodeIsub: returnTypesFnSingle, OpcodeImul: returnTypesFnSingle, OpcodeIcmp: returnTypesFnI32, OpcodeFcmp: returnTypesFnI32, OpcodeFadd: returnTypesFnSingle, OpcodeFsub: returnTypesFnSingle, OpcodeFdiv: returnTypesFnSingle, OpcodeFmul: returnTypesFnSingle, OpcodeFmax: returnTypesFnSingle, OpcodeFmin: returnTypesFnSingle, OpcodeSqmulRoundSat: returnTypesFnV128, OpcodeF32const: returnTypesFnF32, OpcodeF64const: returnTypesFnF64, OpcodeClz: returnTypesFnSingle, OpcodeCtz: returnTypesFnSingle, OpcodePopcnt: returnTypesFnSingle, OpcodeStore: returnTypesFnNoReturns, OpcodeIstore8: returnTypesFnNoReturns, OpcodeIstore16: returnTypesFnNoReturns, OpcodeIstore32: returnTypesFnNoReturns, OpcodeExitWithCode: returnTypesFnNoReturns, OpcodeExitIfTrueWithCode: returnTypesFnNoReturns, OpcodeReturn: returnTypesFnNoReturns, OpcodeBrz: returnTypesFnNoReturns, OpcodeBrnz: returnTypesFnNoReturns, OpcodeBrTable: returnTypesFnNoReturns, OpcodeUload8: returnTypesFnSingle, OpcodeUload16: returnTypesFnSingle, OpcodeUload32: returnTypesFnSingle, OpcodeSload8: returnTypesFnSingle, OpcodeSload16: returnTypesFnSingle, OpcodeSload32: returnTypesFnSingle, OpcodeFcvtToSint: returnTypesFnSingle, OpcodeFcvtToUint: returnTypesFnSingle, OpcodeFcvtFromSint: returnTypesFnSingle, OpcodeFcvtFromUint: returnTypesFnSingle, OpcodeFcvtToSintSat: returnTypesFnSingle, OpcodeFcvtToUintSat: returnTypesFnSingle, OpcodeVFcvtFromUint: returnTypesFnV128, OpcodeVFcvtFromSint: returnTypesFnV128, OpcodeFneg: returnTypesFnSingle, OpcodeFdemote: returnTypesFnF32, OpcodeFvdemote: returnTypesFnV128, OpcodeFvpromoteLow: returnTypesFnV128, OpcodeFpromote: returnTypesFnF64, OpcodeVconst: returnTypesFnV128, OpcodeVFabs: returnTypesFnV128, OpcodeVSqrt: returnTypesFnV128, OpcodeVFmax: returnTypesFnV128, OpcodeVFmin: returnTypesFnV128, OpcodeVFneg: returnTypesFnV128, OpcodeVFadd: returnTypesFnV128, OpcodeVFsub: returnTypesFnV128, OpcodeVFmul: returnTypesFnV128, OpcodeVFdiv: returnTypesFnV128, OpcodeVFcmp: returnTypesFnV128, OpcodeVCeil: returnTypesFnV128, OpcodeVFloor: returnTypesFnV128, OpcodeVTrunc: returnTypesFnV128, OpcodeVNearest: returnTypesFnV128, OpcodeVMaxPseudo: returnTypesFnV128, OpcodeVMinPseudo: returnTypesFnV128, OpcodeVFcvtToUintSat: returnTypesFnV128, OpcodeVFcvtToSintSat: returnTypesFnV128, OpcodeAtomicRmw: returnTypesFnSingle, OpcodeAtomicLoad: returnTypesFnSingle, OpcodeAtomicStore: returnTypesFnNoReturns, OpcodeAtomicCas: returnTypesFnSingle, OpcodeFence: returnTypesFnNoReturns, OpcodeWideningPairwiseDotProductS: returnTypesFnV128, } // AsLoad initializes this instruction as a store instruction with OpcodeLoad. func (i *Instruction) AsLoad(ptr Value, offset uint32, typ Type) *Instruction { i.opcode = OpcodeLoad i.v = ptr i.u1 = uint64(offset) i.typ = typ return i } // AsExtLoad initializes this instruction as a store instruction with OpcodeLoad. func (i *Instruction) AsExtLoad(op Opcode, ptr Value, offset uint32, dst64bit bool) *Instruction { i.opcode = op i.v = ptr i.u1 = uint64(offset) if dst64bit { i.typ = TypeI64 } else { i.typ = TypeI32 } return i } // AsVZeroExtLoad initializes this instruction as a store instruction with OpcodeVExtLoad. func (i *Instruction) AsVZeroExtLoad(ptr Value, offset uint32, scalarType Type) *Instruction { i.opcode = OpcodeVZeroExtLoad i.v = ptr i.u1 = uint64(offset) i.u2 = uint64(scalarType) i.typ = TypeV128 return i } // VZeroExtLoadData returns the operands for a load instruction. The returned `typ` is the scalar type of the load target. func (i *Instruction) VZeroExtLoadData() (ptr Value, offset uint32, typ Type) { return i.v, uint32(i.u1), Type(i.u2) } // AsLoadSplat initializes this instruction as a store instruction with OpcodeLoadSplat. func (i *Instruction) AsLoadSplat(ptr Value, offset uint32, lane VecLane) *Instruction { i.opcode = OpcodeLoadSplat i.v = ptr i.u1 = uint64(offset) i.u2 = uint64(lane) i.typ = TypeV128 return i } // LoadData returns the operands for a load instruction. func (i *Instruction) LoadData() (ptr Value, offset uint32, typ Type) { return i.v, uint32(i.u1), i.typ } // LoadSplatData returns the operands for a load splat instruction. func (i *Instruction) LoadSplatData() (ptr Value, offset uint32, lane VecLane) { return i.v, uint32(i.u1), VecLane(i.u2) } // AsStore initializes this instruction as a store instruction with OpcodeStore. func (i *Instruction) AsStore(storeOp Opcode, value, ptr Value, offset uint32) *Instruction { i.opcode = storeOp i.v = value i.v2 = ptr var dstSize uint64 switch storeOp { case OpcodeStore: dstSize = uint64(value.Type().Bits()) case OpcodeIstore8: dstSize = 8 case OpcodeIstore16: dstSize = 16 case OpcodeIstore32: dstSize = 32 default: panic("invalid store opcode" + storeOp.String()) } i.u1 = uint64(offset) | dstSize<<32 return i } // StoreData returns the operands for a store instruction. func (i *Instruction) StoreData() (value, ptr Value, offset uint32, storeSizeInBits byte) { return i.v, i.v2, uint32(i.u1), byte(i.u1 >> 32) } // AsIconst64 initializes this instruction as a 64-bit integer constant instruction with OpcodeIconst. func (i *Instruction) AsIconst64(v uint64) *Instruction { i.opcode = OpcodeIconst i.typ = TypeI64 i.u1 = v return i } // AsIconst32 initializes this instruction as a 32-bit integer constant instruction with OpcodeIconst. func (i *Instruction) AsIconst32(v uint32) *Instruction { i.opcode = OpcodeIconst i.typ = TypeI32 i.u1 = uint64(v) return i } // AsIadd initializes this instruction as an integer addition instruction with OpcodeIadd. func (i *Instruction) AsIadd(x, y Value) *Instruction { i.opcode = OpcodeIadd i.v = x i.v2 = y i.typ = x.Type() return i } // AsVIadd initializes this instruction as an integer addition instruction with OpcodeVIadd on a vector. func (i *Instruction) AsVIadd(x, y Value, lane VecLane) *Instruction { i.opcode = OpcodeVIadd i.v = x i.v2 = y i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsWideningPairwiseDotProductS initializes this instruction as a lane-wise integer extended pairwise addition instruction // with OpcodeIaddPairwise on a vector. func (i *Instruction) AsWideningPairwiseDotProductS(x, y Value) *Instruction { i.opcode = OpcodeWideningPairwiseDotProductS i.v = x i.v2 = y i.typ = TypeV128 return i } // AsExtIaddPairwise initializes this instruction as a lane-wise integer extended pairwise addition instruction // with OpcodeIaddPairwise on a vector. func (i *Instruction) AsExtIaddPairwise(x Value, srcLane VecLane, signed bool) *Instruction { i.opcode = OpcodeExtIaddPairwise i.v = x i.u1 = uint64(srcLane) if signed { i.u2 = 1 } i.typ = TypeV128 return i } // ExtIaddPairwiseData returns the operands for a lane-wise integer extended pairwise addition instruction. func (i *Instruction) ExtIaddPairwiseData() (x Value, srcLane VecLane, signed bool) { return i.v, VecLane(i.u1), i.u2 != 0 } // AsVSaddSat initializes this instruction as a vector addition with saturation instruction with OpcodeVSaddSat on a vector. func (i *Instruction) AsVSaddSat(x, y Value, lane VecLane) *Instruction { i.opcode = OpcodeVSaddSat i.v = x i.v2 = y i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsVUaddSat initializes this instruction as a vector addition with saturation instruction with OpcodeVUaddSat on a vector. func (i *Instruction) AsVUaddSat(x, y Value, lane VecLane) *Instruction { i.opcode = OpcodeVUaddSat i.v = x i.v2 = y i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsVIsub initializes this instruction as an integer subtraction instruction with OpcodeVIsub on a vector. func (i *Instruction) AsVIsub(x, y Value, lane VecLane) *Instruction { i.opcode = OpcodeVIsub i.v = x i.v2 = y i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsVSsubSat initializes this instruction as a vector addition with saturation instruction with OpcodeVSsubSat on a vector. func (i *Instruction) AsVSsubSat(x, y Value, lane VecLane) *Instruction { i.opcode = OpcodeVSsubSat i.v = x i.v2 = y i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsVUsubSat initializes this instruction as a vector addition with saturation instruction with OpcodeVUsubSat on a vector. func (i *Instruction) AsVUsubSat(x, y Value, lane VecLane) *Instruction { i.opcode = OpcodeVUsubSat i.v = x i.v2 = y i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsVImin initializes this instruction as a signed integer min instruction with OpcodeVImin on a vector. func (i *Instruction) AsVImin(x, y Value, lane VecLane) *Instruction { i.opcode = OpcodeVImin i.v = x i.v2 = y i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsVUmin initializes this instruction as an unsigned integer min instruction with OpcodeVUmin on a vector. func (i *Instruction) AsVUmin(x, y Value, lane VecLane) *Instruction { i.opcode = OpcodeVUmin i.v = x i.v2 = y i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsVImax initializes this instruction as a signed integer max instruction with OpcodeVImax on a vector. func (i *Instruction) AsVImax(x, y Value, lane VecLane) *Instruction { i.opcode = OpcodeVImax i.v = x i.v2 = y i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsVUmax initializes this instruction as an unsigned integer max instruction with OpcodeVUmax on a vector. func (i *Instruction) AsVUmax(x, y Value, lane VecLane) *Instruction { i.opcode = OpcodeVUmax i.v = x i.v2 = y i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsVAvgRound initializes this instruction as an unsigned integer avg instruction, truncating to zero with OpcodeVAvgRound on a vector. func (i *Instruction) AsVAvgRound(x, y Value, lane VecLane) *Instruction { i.opcode = OpcodeVAvgRound i.v = x i.v2 = y i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsVImul initializes this instruction as an integer multiplication with OpcodeVImul on a vector. func (i *Instruction) AsVImul(x, y Value, lane VecLane) *Instruction { i.opcode = OpcodeVImul i.v = x i.v2 = y i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsSqmulRoundSat initializes this instruction as a lane-wise saturating rounding multiplication // in Q15 format with OpcodeSqmulRoundSat on a vector. func (i *Instruction) AsSqmulRoundSat(x, y Value, lane VecLane) *Instruction { i.opcode = OpcodeSqmulRoundSat i.v = x i.v2 = y i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsVIabs initializes this instruction as a vector absolute value with OpcodeVIabs. func (i *Instruction) AsVIabs(x Value, lane VecLane) *Instruction { i.opcode = OpcodeVIabs i.v = x i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsVIneg initializes this instruction as a vector negation with OpcodeVIneg. func (i *Instruction) AsVIneg(x Value, lane VecLane) *Instruction { i.opcode = OpcodeVIneg i.v = x i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsVIpopcnt initializes this instruction as a Population Count instruction with OpcodeVIpopcnt on a vector. func (i *Instruction) AsVIpopcnt(x Value, lane VecLane) *Instruction { if lane != VecLaneI8x16 { panic("Unsupported lane type " + lane.String()) } i.opcode = OpcodeVIpopcnt i.v = x i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsVSqrt initializes this instruction as a sqrt instruction with OpcodeVSqrt on a vector. func (i *Instruction) AsVSqrt(x Value, lane VecLane) *Instruction { i.opcode = OpcodeVSqrt i.v = x i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsVFabs initializes this instruction as a float abs instruction with OpcodeVFabs on a vector. func (i *Instruction) AsVFabs(x Value, lane VecLane) *Instruction { i.opcode = OpcodeVFabs i.v = x i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsVFneg initializes this instruction as a float neg instruction with OpcodeVFneg on a vector. func (i *Instruction) AsVFneg(x Value, lane VecLane) *Instruction { i.opcode = OpcodeVFneg i.v = x i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsVFmax initializes this instruction as a float max instruction with OpcodeVFmax on a vector. func (i *Instruction) AsVFmax(x, y Value, lane VecLane) *Instruction { i.opcode = OpcodeVFmax i.v = x i.v2 = y i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsVFmin initializes this instruction as a float min instruction with OpcodeVFmin on a vector. func (i *Instruction) AsVFmin(x, y Value, lane VecLane) *Instruction { i.opcode = OpcodeVFmin i.v = x i.v2 = y i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsVFadd initializes this instruction as a floating point add instruction with OpcodeVFadd on a vector. func (i *Instruction) AsVFadd(x, y Value, lane VecLane) *Instruction { i.opcode = OpcodeVFadd i.v = x i.v2 = y i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsVFsub initializes this instruction as a floating point subtraction instruction with OpcodeVFsub on a vector. func (i *Instruction) AsVFsub(x, y Value, lane VecLane) *Instruction { i.opcode = OpcodeVFsub i.v = x i.v2 = y i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsVFmul initializes this instruction as a floating point multiplication instruction with OpcodeVFmul on a vector. func (i *Instruction) AsVFmul(x, y Value, lane VecLane) *Instruction { i.opcode = OpcodeVFmul i.v = x i.v2 = y i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsVFdiv initializes this instruction as a floating point division instruction with OpcodeVFdiv on a vector. func (i *Instruction) AsVFdiv(x, y Value, lane VecLane) *Instruction { i.opcode = OpcodeVFdiv i.v = x i.v2 = y i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsImul initializes this instruction as an integer addition instruction with OpcodeImul. func (i *Instruction) AsImul(x, y Value) *Instruction { i.opcode = OpcodeImul i.v = x i.v2 = y i.typ = x.Type() return i } func (i *Instruction) Insert(b Builder) *Instruction { b.InsertInstruction(i) return i } // AsIsub initializes this instruction as an integer subtraction instruction with OpcodeIsub. func (i *Instruction) AsIsub(x, y Value) *Instruction { i.opcode = OpcodeIsub i.v = x i.v2 = y i.typ = x.Type() return i } // AsIcmp initializes this instruction as an integer comparison instruction with OpcodeIcmp. func (i *Instruction) AsIcmp(x, y Value, c IntegerCmpCond) *Instruction { i.opcode = OpcodeIcmp i.v = x i.v2 = y i.u1 = uint64(c) i.typ = TypeI32 return i } // AsFcmp initializes this instruction as an integer comparison instruction with OpcodeFcmp. func (i *Instruction) AsFcmp(x, y Value, c FloatCmpCond) { i.opcode = OpcodeFcmp i.v = x i.v2 = y i.u1 = uint64(c) i.typ = TypeI32 } // AsVIcmp initializes this instruction as an integer vector comparison instruction with OpcodeVIcmp. func (i *Instruction) AsVIcmp(x, y Value, c IntegerCmpCond, lane VecLane) *Instruction { i.opcode = OpcodeVIcmp i.v = x i.v2 = y i.u1 = uint64(c) i.u2 = uint64(lane) i.typ = TypeV128 return i } // AsVFcmp initializes this instruction as a float comparison instruction with OpcodeVFcmp on Vector. func (i *Instruction) AsVFcmp(x, y Value, c FloatCmpCond, lane VecLane) *Instruction { i.opcode = OpcodeVFcmp i.v = x i.v2 = y i.u1 = uint64(c) i.typ = TypeV128 i.u2 = uint64(lane) return i } // AsVCeil initializes this instruction as an instruction with OpcodeCeil. func (i *Instruction) AsVCeil(x Value, lane VecLane) *Instruction { i.opcode = OpcodeVCeil i.v = x i.typ = x.Type() i.u1 = uint64(lane) return i } // AsVFloor initializes this instruction as an instruction with OpcodeFloor. func (i *Instruction) AsVFloor(x Value, lane VecLane) *Instruction { i.opcode = OpcodeVFloor i.v = x i.typ = x.Type() i.u1 = uint64(lane) return i } // AsVTrunc initializes this instruction as an instruction with OpcodeTrunc. func (i *Instruction) AsVTrunc(x Value, lane VecLane) *Instruction { i.opcode = OpcodeVTrunc i.v = x i.typ = x.Type() i.u1 = uint64(lane) return i } // AsVNearest initializes this instruction as an instruction with OpcodeNearest. func (i *Instruction) AsVNearest(x Value, lane VecLane) *Instruction { i.opcode = OpcodeVNearest i.v = x i.typ = x.Type() i.u1 = uint64(lane) return i } // AsVMaxPseudo initializes this instruction as an instruction with OpcodeVMaxPseudo. func (i *Instruction) AsVMaxPseudo(x, y Value, lane VecLane) *Instruction { i.opcode = OpcodeVMaxPseudo i.typ = x.Type() i.v = x i.v2 = y i.u1 = uint64(lane) return i } // AsVMinPseudo initializes this instruction as an instruction with OpcodeVMinPseudo. func (i *Instruction) AsVMinPseudo(x, y Value, lane VecLane) *Instruction { i.opcode = OpcodeVMinPseudo i.typ = x.Type() i.v = x i.v2 = y i.u1 = uint64(lane) return i } // AsSDiv initializes this instruction as an integer bitwise and instruction with OpcodeSdiv. func (i *Instruction) AsSDiv(x, y, ctx Value) *Instruction { i.opcode = OpcodeSdiv i.v = x i.v2 = y i.v3 = ctx i.typ = x.Type() return i } // AsUDiv initializes this instruction as an integer bitwise and instruction with OpcodeUdiv. func (i *Instruction) AsUDiv(x, y, ctx Value) *Instruction { i.opcode = OpcodeUdiv i.v = x i.v2 = y i.v3 = ctx i.typ = x.Type() return i } // AsSRem initializes this instruction as an integer bitwise and instruction with OpcodeSrem. func (i *Instruction) AsSRem(x, y, ctx Value) *Instruction { i.opcode = OpcodeSrem i.v = x i.v2 = y i.v3 = ctx i.typ = x.Type() return i } // AsURem initializes this instruction as an integer bitwise and instruction with OpcodeUrem. func (i *Instruction) AsURem(x, y, ctx Value) *Instruction { i.opcode = OpcodeUrem i.v = x i.v2 = y i.v3 = ctx i.typ = x.Type() return i } // AsBand initializes this instruction as an integer bitwise and instruction with OpcodeBand. func (i *Instruction) AsBand(x, amount Value) *Instruction { i.opcode = OpcodeBand i.v = x i.v2 = amount i.typ = x.Type() return i } // AsBor initializes this instruction as an integer bitwise or instruction with OpcodeBor. func (i *Instruction) AsBor(x, amount Value) { i.opcode = OpcodeBor i.v = x i.v2 = amount i.typ = x.Type() } // AsBxor initializes this instruction as an integer bitwise xor instruction with OpcodeBxor. func (i *Instruction) AsBxor(x, amount Value) { i.opcode = OpcodeBxor i.v = x i.v2 = amount i.typ = x.Type() } // AsIshl initializes this instruction as an integer shift left instruction with OpcodeIshl. func (i *Instruction) AsIshl(x, amount Value) *Instruction { i.opcode = OpcodeIshl i.v = x i.v2 = amount i.typ = x.Type() return i } // AsVIshl initializes this instruction as an integer shift left instruction with OpcodeVIshl on vector. func (i *Instruction) AsVIshl(x, amount Value, lane VecLane) *Instruction { i.opcode = OpcodeVIshl i.v = x i.v2 = amount i.u1 = uint64(lane) i.typ = x.Type() return i } // AsUshr initializes this instruction as an integer unsigned shift right (logical shift right) instruction with OpcodeUshr. func (i *Instruction) AsUshr(x, amount Value) *Instruction { i.opcode = OpcodeUshr i.v = x i.v2 = amount i.typ = x.Type() return i } // AsVUshr initializes this instruction as an integer unsigned shift right (logical shift right) instruction with OpcodeVUshr on vector. func (i *Instruction) AsVUshr(x, amount Value, lane VecLane) *Instruction { i.opcode = OpcodeVUshr i.v = x i.v2 = amount i.u1 = uint64(lane) i.typ = x.Type() return i } // AsSshr initializes this instruction as an integer signed shift right (arithmetic shift right) instruction with OpcodeSshr. func (i *Instruction) AsSshr(x, amount Value) *Instruction { i.opcode = OpcodeSshr i.v = x i.v2 = amount i.typ = x.Type() return i } // AsVSshr initializes this instruction as an integer signed shift right (arithmetic shift right) instruction with OpcodeVSshr on vector. func (i *Instruction) AsVSshr(x, amount Value, lane VecLane) *Instruction { i.opcode = OpcodeVSshr i.v = x i.v2 = amount i.u1 = uint64(lane) i.typ = x.Type() return i } // AsExtractlane initializes this instruction as an extract lane instruction with OpcodeExtractlane on vector. func (i *Instruction) AsExtractlane(x Value, index byte, lane VecLane, signed bool) *Instruction { i.opcode = OpcodeExtractlane i.v = x // We do not have a field for signedness, but `index` is a byte, // so we just encode the flag in the high bits of `u1`. i.u1 = uint64(index) if signed { i.u1 = i.u1 | 1<<32 } i.u2 = uint64(lane) switch lane { case VecLaneI8x16, VecLaneI16x8, VecLaneI32x4: i.typ = TypeI32 case VecLaneI64x2: i.typ = TypeI64 case VecLaneF32x4: i.typ = TypeF32 case VecLaneF64x2: i.typ = TypeF64 } return i } // AsInsertlane initializes this instruction as an insert lane instruction with OpcodeInsertlane on vector. func (i *Instruction) AsInsertlane(x, y Value, index byte, lane VecLane) *Instruction { i.opcode = OpcodeInsertlane i.v = x i.v2 = y i.u1 = uint64(index) i.u2 = uint64(lane) i.typ = TypeV128 return i } // AsShuffle initializes this instruction as a shuffle instruction with OpcodeShuffle on vector. func (i *Instruction) AsShuffle(x, y Value, lane []byte) *Instruction { i.opcode = OpcodeShuffle i.v = x i.v2 = y // Encode the 16 bytes as 8 bytes in u1, and 8 bytes in u2. i.u1 = uint64(lane[7])<<56 | uint64(lane[6])<<48 | uint64(lane[5])<<40 | uint64(lane[4])<<32 | uint64(lane[3])<<24 | uint64(lane[2])<<16 | uint64(lane[1])<<8 | uint64(lane[0]) i.u2 = uint64(lane[15])<<56 | uint64(lane[14])<<48 | uint64(lane[13])<<40 | uint64(lane[12])<<32 | uint64(lane[11])<<24 | uint64(lane[10])<<16 | uint64(lane[9])<<8 | uint64(lane[8]) i.typ = TypeV128 return i } // AsSwizzle initializes this instruction as an insert lane instruction with OpcodeSwizzle on vector. func (i *Instruction) AsSwizzle(x, y Value, lane VecLane) *Instruction { i.opcode = OpcodeSwizzle i.v = x i.v2 = y i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsSplat initializes this instruction as an insert lane instruction with OpcodeSplat on vector. func (i *Instruction) AsSplat(x Value, lane VecLane) *Instruction { i.opcode = OpcodeSplat i.v = x i.u1 = uint64(lane) i.typ = TypeV128 return i } // AsRotl initializes this instruction as a word rotate left instruction with OpcodeRotl. func (i *Instruction) AsRotl(x, amount Value) { i.opcode = OpcodeRotl i.v = x i.v2 = amount i.typ = x.Type() } // AsRotr initializes this instruction as a word rotate right instruction with OpcodeRotr. func (i *Instruction) AsRotr(x, amount Value) { i.opcode = OpcodeRotr i.v = x i.v2 = amount i.typ = x.Type() } // IcmpData returns the operands and comparison condition of this integer comparison instruction. func (i *Instruction) IcmpData() (x, y Value, c IntegerCmpCond) { return i.v, i.v2, IntegerCmpCond(i.u1) } // FcmpData returns the operands and comparison condition of this floating-point comparison instruction. func (i *Instruction) FcmpData() (x, y Value, c FloatCmpCond) { return i.v, i.v2, FloatCmpCond(i.u1) } // VIcmpData returns the operands and comparison condition of this integer comparison instruction on vector. func (i *Instruction) VIcmpData() (x, y Value, c IntegerCmpCond, l VecLane) { return i.v, i.v2, IntegerCmpCond(i.u1), VecLane(i.u2) } // VFcmpData returns the operands and comparison condition of this float comparison instruction on vector. func (i *Instruction) VFcmpData() (x, y Value, c FloatCmpCond, l VecLane) { return i.v, i.v2, FloatCmpCond(i.u1), VecLane(i.u2) } // ExtractlaneData returns the operands and sign flag of Extractlane on vector. func (i *Instruction) ExtractlaneData() (x Value, index byte, signed bool, l VecLane) { x = i.v index = byte(0b00001111 & i.u1) signed = i.u1>>32 != 0 l = VecLane(i.u2) return } // InsertlaneData returns the operands and sign flag of Insertlane on vector. func (i *Instruction) InsertlaneData() (x, y Value, index byte, l VecLane) { x = i.v y = i.v2 index = byte(i.u1) l = VecLane(i.u2) return } // AsFadd initializes this instruction as a floating-point addition instruction with OpcodeFadd. func (i *Instruction) AsFadd(x, y Value) { i.opcode = OpcodeFadd i.v = x i.v2 = y i.typ = x.Type() } // AsFsub initializes this instruction as a floating-point subtraction instruction with OpcodeFsub. func (i *Instruction) AsFsub(x, y Value) { i.opcode = OpcodeFsub i.v = x i.v2 = y i.typ = x.Type() } // AsFmul initializes this instruction as a floating-point multiplication instruction with OpcodeFmul. func (i *Instruction) AsFmul(x, y Value) { i.opcode = OpcodeFmul i.v = x i.v2 = y i.typ = x.Type() } // AsFdiv initializes this instruction as a floating-point division instruction with OpcodeFdiv. func (i *Instruction) AsFdiv(x, y Value) { i.opcode = OpcodeFdiv i.v = x i.v2 = y i.typ = x.Type() } // AsFmin initializes this instruction to take the minimum of two floating-points with OpcodeFmin. func (i *Instruction) AsFmin(x, y Value) { i.opcode = OpcodeFmin i.v = x i.v2 = y i.typ = x.Type() } // AsFmax initializes this instruction to take the maximum of two floating-points with OpcodeFmax. func (i *Instruction) AsFmax(x, y Value) { i.opcode = OpcodeFmax i.v = x i.v2 = y i.typ = x.Type() } // AsF32const initializes this instruction as a 32-bit floating-point constant instruction with OpcodeF32const. func (i *Instruction) AsF32const(f float32) *Instruction { i.opcode = OpcodeF32const i.typ = TypeF64 i.u1 = uint64(math.Float32bits(f)) return i } // AsF64const initializes this instruction as a 64-bit floating-point constant instruction with OpcodeF64const. func (i *Instruction) AsF64const(f float64) *Instruction { i.opcode = OpcodeF64const i.typ = TypeF64 i.u1 = math.Float64bits(f) return i } // AsVconst initializes this instruction as a vector constant instruction with OpcodeVconst. func (i *Instruction) AsVconst(lo, hi uint64) *Instruction { i.opcode = OpcodeVconst i.typ = TypeV128 i.u1 = lo i.u2 = hi return i } // AsVbnot initializes this instruction as a vector negation instruction with OpcodeVbnot. func (i *Instruction) AsVbnot(v Value) *Instruction { i.opcode = OpcodeVbnot i.typ = TypeV128 i.v = v return i } // AsVband initializes this instruction as an and vector instruction with OpcodeVband. func (i *Instruction) AsVband(x, y Value) *Instruction { i.opcode = OpcodeVband i.typ = TypeV128 i.v = x i.v2 = y return i } // AsVbor initializes this instruction as an or vector instruction with OpcodeVbor. func (i *Instruction) AsVbor(x, y Value) *Instruction { i.opcode = OpcodeVbor i.typ = TypeV128 i.v = x i.v2 = y return i } // AsVbxor initializes this instruction as a xor vector instruction with OpcodeVbxor. func (i *Instruction) AsVbxor(x, y Value) *Instruction { i.opcode = OpcodeVbxor i.typ = TypeV128 i.v = x i.v2 = y return i } // AsVbandnot initializes this instruction as an and-not vector instruction with OpcodeVbandnot. func (i *Instruction) AsVbandnot(x, y Value) *Instruction { i.opcode = OpcodeVbandnot i.typ = TypeV128 i.v = x i.v2 = y return i } // AsVbitselect initializes this instruction as a bit select vector instruction with OpcodeVbitselect. func (i *Instruction) AsVbitselect(c, x, y Value) *Instruction { i.opcode = OpcodeVbitselect i.typ = TypeV128 i.v = c i.v2 = x i.v3 = y return i } // AsVanyTrue initializes this instruction as an anyTrue vector instruction with OpcodeVanyTrue. func (i *Instruction) AsVanyTrue(x Value) *Instruction { i.opcode = OpcodeVanyTrue i.typ = TypeI32 i.v = x return i } // AsVallTrue initializes this instruction as an allTrue vector instruction with OpcodeVallTrue. func (i *Instruction) AsVallTrue(x Value, lane VecLane) *Instruction { i.opcode = OpcodeVallTrue i.typ = TypeI32 i.v = x i.u1 = uint64(lane) return i } // AsVhighBits initializes this instruction as a highBits vector instruction with OpcodeVhighBits. func (i *Instruction) AsVhighBits(x Value, lane VecLane) *Instruction { i.opcode = OpcodeVhighBits i.typ = TypeI32 i.v = x i.u1 = uint64(lane) return i } // VconstData returns the operands of this vector constant instruction. func (i *Instruction) VconstData() (lo, hi uint64) { return i.u1, i.u2 } // AsReturn initializes this instruction as a return instruction with OpcodeReturn. func (i *Instruction) AsReturn(vs wazevoapi.VarLength[Value]) *Instruction { i.opcode = OpcodeReturn i.vs = vs return i } // AsIreduce initializes this instruction as a reduction instruction with OpcodeIreduce. func (i *Instruction) AsIreduce(v Value, dstType Type) *Instruction { i.opcode = OpcodeIreduce i.v = v i.typ = dstType return i } // AsWiden initializes this instruction as a signed or unsigned widen instruction // on low half or high half of the given vector with OpcodeSwidenLow, OpcodeUwidenLow, OpcodeSwidenHigh, OpcodeUwidenHigh. func (i *Instruction) AsWiden(v Value, lane VecLane, signed, low bool) *Instruction { switch { case signed && low: i.opcode = OpcodeSwidenLow case !signed && low: i.opcode = OpcodeUwidenLow case signed && !low: i.opcode = OpcodeSwidenHigh case !signed && !low: i.opcode = OpcodeUwidenHigh } i.v = v i.u1 = uint64(lane) return i } // AsAtomicLoad initializes this instruction as an atomic load. // The size is in bytes and must be 1, 2, 4, or 8. func (i *Instruction) AsAtomicLoad(addr Value, size uint64, typ Type) *Instruction { i.opcode = OpcodeAtomicLoad i.u1 = size i.v = addr i.typ = typ return i } // AsAtomicLoad initializes this instruction as an atomic store. // The size is in bytes and must be 1, 2, 4, or 8. func (i *Instruction) AsAtomicStore(addr, val Value, size uint64) *Instruction { i.opcode = OpcodeAtomicStore i.u1 = size i.v = addr i.v2 = val i.typ = val.Type() return i } // AsAtomicRmw initializes this instruction as an atomic read-modify-write. // The size is in bytes and must be 1, 2, 4, or 8. func (i *Instruction) AsAtomicRmw(op AtomicRmwOp, addr, val Value, size uint64) *Instruction { i.opcode = OpcodeAtomicRmw i.u1 = uint64(op) i.u2 = size i.v = addr i.v2 = val i.typ = val.Type() return i } // AsAtomicCas initializes this instruction as an atomic compare-and-swap. // The size is in bytes and must be 1, 2, 4, or 8. func (i *Instruction) AsAtomicCas(addr, exp, repl Value, size uint64) *Instruction { i.opcode = OpcodeAtomicCas i.u1 = size i.v = addr i.v2 = exp i.v3 = repl i.typ = repl.Type() return i } // AsFence initializes this instruction as a memory fence. // A single byte immediate may be used to indicate fence ordering in the future // but is currently always 0 and ignored. func (i *Instruction) AsFence(order byte) *Instruction { i.opcode = OpcodeFence i.u1 = uint64(order) return i } // AtomicRmwData returns the data for this atomic read-modify-write instruction. func (i *Instruction) AtomicRmwData() (op AtomicRmwOp, size uint64) { return AtomicRmwOp(i.u1), i.u2 } // AtomicTargetSize returns the target memory size of the atomic instruction. func (i *Instruction) AtomicTargetSize() (size uint64) { return i.u1 } // ReturnVals returns the return values of OpcodeReturn. func (i *Instruction) ReturnVals() []Value { return i.vs.View() } // AsExitWithCode initializes this instruction as a trap instruction with OpcodeExitWithCode. func (i *Instruction) AsExitWithCode(ctx Value, code wazevoapi.ExitCode) { i.opcode = OpcodeExitWithCode i.v = ctx i.u1 = uint64(code) } // AsExitIfTrueWithCode initializes this instruction as a trap instruction with OpcodeExitIfTrueWithCode. func (i *Instruction) AsExitIfTrueWithCode(ctx, c Value, code wazevoapi.ExitCode) *Instruction { i.opcode = OpcodeExitIfTrueWithCode i.v = ctx i.v2 = c i.u1 = uint64(code) return i } // ExitWithCodeData returns the context and exit code of OpcodeExitWithCode. func (i *Instruction) ExitWithCodeData() (ctx Value, code wazevoapi.ExitCode) { return i.v, wazevoapi.ExitCode(i.u1) } // ExitIfTrueWithCodeData returns the context and exit code of OpcodeExitWithCode. func (i *Instruction) ExitIfTrueWithCodeData() (ctx, c Value, code wazevoapi.ExitCode) { return i.v, i.v2, wazevoapi.ExitCode(i.u1) } // InvertBrx inverts either OpcodeBrz or OpcodeBrnz to the other. func (i *Instruction) InvertBrx() { switch i.opcode { case OpcodeBrz: i.opcode = OpcodeBrnz case OpcodeBrnz: i.opcode = OpcodeBrz default: panic("BUG") } } // BranchData returns the branch data for this instruction necessary for backends. func (i *Instruction) BranchData() (condVal Value, blockArgs []Value, target BasicBlockID) { switch i.opcode { case OpcodeJump: condVal = ValueInvalid case OpcodeBrz, OpcodeBrnz: condVal = i.v default: panic("BUG") } blockArgs = i.vs.View() target = BasicBlockID(i.rValue) return } // BrTableData returns the branch table data for this instruction necessary for backends. func (i *Instruction) BrTableData() (index Value, targets Values) { if i.opcode != OpcodeBrTable { panic("BUG: BrTableData only available for OpcodeBrTable") } index = i.v targets = i.rValues return } // AsJump initializes this instruction as a jump instruction with OpcodeJump. func (i *Instruction) AsJump(vs Values, target BasicBlock) *Instruction { i.opcode = OpcodeJump i.vs = vs i.rValue = Value(target.ID()) return i } // IsFallthroughJump returns true if this instruction is a fallthrough jump. func (i *Instruction) IsFallthroughJump() bool { if i.opcode != OpcodeJump { panic("BUG: IsFallthrough only available for OpcodeJump") } return i.opcode == OpcodeJump && i.u1 != 0 } // AsFallthroughJump marks this instruction as a fallthrough jump. func (i *Instruction) AsFallthroughJump() { if i.opcode != OpcodeJump { panic("BUG: AsFallthroughJump only available for OpcodeJump") } i.u1 = 1 } // AsBrz initializes this instruction as a branch-if-zero instruction with OpcodeBrz. func (i *Instruction) AsBrz(v Value, args Values, target BasicBlock) { i.opcode = OpcodeBrz i.v = v i.vs = args i.rValue = Value(target.ID()) } // AsBrnz initializes this instruction as a branch-if-not-zero instruction with OpcodeBrnz. func (i *Instruction) AsBrnz(v Value, args Values, target BasicBlock) *Instruction { i.opcode = OpcodeBrnz i.v = v i.vs = args i.rValue = Value(target.ID()) return i } // AsBrTable initializes this instruction as a branch-table instruction with OpcodeBrTable. // targets is a list of basic block IDs cast to Values. func (i *Instruction) AsBrTable(index Value, targets Values) { i.opcode = OpcodeBrTable i.v = index i.rValues = targets } // AsCall initializes this instruction as a call instruction with OpcodeCall. func (i *Instruction) AsCall(ref FuncRef, sig *Signature, args Values) { i.opcode = OpcodeCall i.u1 = uint64(ref) i.vs = args i.u2 = uint64(sig.ID) sig.used = true } // CallData returns the call data for this instruction necessary for backends. func (i *Instruction) CallData() (ref FuncRef, sigID SignatureID, args []Value) { if i.opcode != OpcodeCall { panic("BUG: CallData only available for OpcodeCall") } ref = FuncRef(i.u1) sigID = SignatureID(i.u2) args = i.vs.View() return } // AsCallIndirect initializes this instruction as a call-indirect instruction with OpcodeCallIndirect. func (i *Instruction) AsCallIndirect(funcPtr Value, sig *Signature, args Values) *Instruction { i.opcode = OpcodeCallIndirect i.typ = TypeF64 i.vs = args i.v = funcPtr i.u1 = uint64(sig.ID) sig.used = true return i } // AsCallGoRuntimeMemmove is the same as AsCallIndirect, but with a special flag set to indicate that it is a call to the Go runtime memmove function. func (i *Instruction) AsCallGoRuntimeMemmove(funcPtr Value, sig *Signature, args Values) *Instruction { i.AsCallIndirect(funcPtr, sig, args) i.u2 = 1 return i } // CallIndirectData returns the call indirect data for this instruction necessary for backends. func (i *Instruction) CallIndirectData() (funcPtr Value, sigID SignatureID, args []Value, isGoMemmove bool) { if i.opcode != OpcodeCallIndirect { panic("BUG: CallIndirectData only available for OpcodeCallIndirect") } funcPtr = i.v sigID = SignatureID(i.u1) args = i.vs.View() isGoMemmove = i.u2 == 1 return } // AsClz initializes this instruction as a Count Leading Zeroes instruction with OpcodeClz. func (i *Instruction) AsClz(x Value) { i.opcode = OpcodeClz i.v = x i.typ = x.Type() } // AsCtz initializes this instruction as a Count Trailing Zeroes instruction with OpcodeCtz. func (i *Instruction) AsCtz(x Value) { i.opcode = OpcodeCtz i.v = x i.typ = x.Type() } // AsPopcnt initializes this instruction as a Population Count instruction with OpcodePopcnt. func (i *Instruction) AsPopcnt(x Value) { i.opcode = OpcodePopcnt i.v = x i.typ = x.Type() } // AsFneg initializes this instruction as an instruction with OpcodeFneg. func (i *Instruction) AsFneg(x Value) *Instruction { i.opcode = OpcodeFneg i.v = x i.typ = x.Type() return i } // AsSqrt initializes this instruction as an instruction with OpcodeSqrt. func (i *Instruction) AsSqrt(x Value) *Instruction { i.opcode = OpcodeSqrt i.v = x i.typ = x.Type() return i } // AsFabs initializes this instruction as an instruction with OpcodeFabs. func (i *Instruction) AsFabs(x Value) *Instruction { i.opcode = OpcodeFabs i.v = x i.typ = x.Type() return i } // AsFcopysign initializes this instruction as an instruction with OpcodeFcopysign. func (i *Instruction) AsFcopysign(x, y Value) *Instruction { i.opcode = OpcodeFcopysign i.v = x i.v2 = y i.typ = x.Type() return i } // AsCeil initializes this instruction as an instruction with OpcodeCeil. func (i *Instruction) AsCeil(x Value) *Instruction { i.opcode = OpcodeCeil i.v = x i.typ = x.Type() return i } // AsFloor initializes this instruction as an instruction with OpcodeFloor. func (i *Instruction) AsFloor(x Value) *Instruction { i.opcode = OpcodeFloor i.v = x i.typ = x.Type() return i } // AsTrunc initializes this instruction as an instruction with OpcodeTrunc. func (i *Instruction) AsTrunc(x Value) *Instruction { i.opcode = OpcodeTrunc i.v = x i.typ = x.Type() return i } // AsNearest initializes this instruction as an instruction with OpcodeNearest. func (i *Instruction) AsNearest(x Value) *Instruction { i.opcode = OpcodeNearest i.v = x i.typ = x.Type() return i } // AsBitcast initializes this instruction as an instruction with OpcodeBitcast. func (i *Instruction) AsBitcast(x Value, dstType Type) *Instruction { i.opcode = OpcodeBitcast i.v = x i.typ = dstType return i } // BitcastData returns the operands for a bitcast instruction. func (i *Instruction) BitcastData() (x Value, dstType Type) { return i.v, i.typ } // AsFdemote initializes this instruction as an instruction with OpcodeFdemote. func (i *Instruction) AsFdemote(x Value) { i.opcode = OpcodeFdemote i.v = x i.typ = TypeF32 } // AsFpromote initializes this instruction as an instruction with OpcodeFpromote. func (i *Instruction) AsFpromote(x Value) { i.opcode = OpcodeFpromote i.v = x i.typ = TypeF64 } // AsFcvtFromInt initializes this instruction as an instruction with either OpcodeFcvtFromUint or OpcodeFcvtFromSint func (i *Instruction) AsFcvtFromInt(x Value, signed bool, dst64bit bool) *Instruction { if signed { i.opcode = OpcodeFcvtFromSint } else { i.opcode = OpcodeFcvtFromUint } i.v = x if dst64bit { i.typ = TypeF64 } else { i.typ = TypeF32 } return i } // AsFcvtToInt initializes this instruction as an instruction with either OpcodeFcvtToUint or OpcodeFcvtToSint func (i *Instruction) AsFcvtToInt(x, ctx Value, signed bool, dst64bit bool, sat bool) *Instruction { switch { case signed && !sat: i.opcode = OpcodeFcvtToSint case !signed && !sat: i.opcode = OpcodeFcvtToUint case signed && sat: i.opcode = OpcodeFcvtToSintSat case !signed && sat: i.opcode = OpcodeFcvtToUintSat } i.v = x i.v2 = ctx if dst64bit { i.typ = TypeI64 } else { i.typ = TypeI32 } return i } // AsVFcvtToIntSat initializes this instruction as an instruction with either OpcodeVFcvtToSintSat or OpcodeVFcvtToUintSat func (i *Instruction) AsVFcvtToIntSat(x Value, lane VecLane, signed bool) *Instruction { if signed { i.opcode = OpcodeVFcvtToSintSat } else { i.opcode = OpcodeVFcvtToUintSat } i.v = x i.u1 = uint64(lane) return i } // AsVFcvtFromInt initializes this instruction as an instruction with either OpcodeVFcvtToSintSat or OpcodeVFcvtToUintSat func (i *Instruction) AsVFcvtFromInt(x Value, lane VecLane, signed bool) *Instruction { if signed { i.opcode = OpcodeVFcvtFromSint } else { i.opcode = OpcodeVFcvtFromUint } i.v = x i.u1 = uint64(lane) return i } // AsNarrow initializes this instruction as an instruction with either OpcodeSnarrow or OpcodeUnarrow func (i *Instruction) AsNarrow(x, y Value, lane VecLane, signed bool) *Instruction { if signed { i.opcode = OpcodeSnarrow } else { i.opcode = OpcodeUnarrow } i.v = x i.v2 = y i.u1 = uint64(lane) return i } // AsFvpromoteLow initializes this instruction as an instruction with OpcodeFvpromoteLow func (i *Instruction) AsFvpromoteLow(x Value, lane VecLane) *Instruction { i.opcode = OpcodeFvpromoteLow i.v = x i.u1 = uint64(lane) return i } // AsFvdemote initializes this instruction as an instruction with OpcodeFvdemote func (i *Instruction) AsFvdemote(x Value, lane VecLane) *Instruction { i.opcode = OpcodeFvdemote i.v = x i.u1 = uint64(lane) return i } // AsSExtend initializes this instruction as a sign extension instruction with OpcodeSExtend. func (i *Instruction) AsSExtend(v Value, from, to byte) *Instruction { i.opcode = OpcodeSExtend i.v = v i.u1 = uint64(from)<<8 | uint64(to) if to == 64 { i.typ = TypeI64 } else { i.typ = TypeI32 } return i } // AsUExtend initializes this instruction as an unsigned extension instruction with OpcodeUExtend. func (i *Instruction) AsUExtend(v Value, from, to byte) *Instruction { i.opcode = OpcodeUExtend i.v = v i.u1 = uint64(from)<<8 | uint64(to) if to == 64 { i.typ = TypeI64 } else { i.typ = TypeI32 } return i } func (i *Instruction) ExtendData() (from, to byte, signed bool) { if i.opcode != OpcodeSExtend && i.opcode != OpcodeUExtend { panic("BUG: ExtendData only available for OpcodeSExtend and OpcodeUExtend") } from = byte(i.u1 >> 8) to = byte(i.u1) signed = i.opcode == OpcodeSExtend return } // AsSelect initializes this instruction as an unsigned extension instruction with OpcodeSelect. func (i *Instruction) AsSelect(c, x, y Value) *Instruction { i.opcode = OpcodeSelect i.v = c i.v2 = x i.v3 = y i.typ = x.Type() return i } // SelectData returns the select data for this instruction necessary for backends. func (i *Instruction) SelectData() (c, x, y Value) { c = i.v x = i.v2 y = i.v3 return } // ExtendFromToBits returns the from and to bit size for the extension instruction. func (i *Instruction) ExtendFromToBits() (from, to byte) { from = byte(i.u1 >> 8) to = byte(i.u1) return } // Format returns a string representation of this instruction with the given builder. // For debugging purposes only. func (i *Instruction) Format(b Builder) string { var instSuffix string switch i.opcode { case OpcodeExitWithCode: instSuffix = fmt.Sprintf(" %s, %s", i.v.Format(b), wazevoapi.ExitCode(i.u1)) case OpcodeExitIfTrueWithCode: instSuffix = fmt.Sprintf(" %s, %s, %s", i.v2.Format(b), i.v.Format(b), wazevoapi.ExitCode(i.u1)) case OpcodeIadd, OpcodeIsub, OpcodeImul, OpcodeFadd, OpcodeFsub, OpcodeFmin, OpcodeFmax, OpcodeFdiv, OpcodeFmul: instSuffix = fmt.Sprintf(" %s, %s", i.v.Format(b), i.v2.Format(b)) case OpcodeIcmp: instSuffix = fmt.Sprintf(" %s, %s, %s", IntegerCmpCond(i.u1), i.v.Format(b), i.v2.Format(b)) case OpcodeFcmp: instSuffix = fmt.Sprintf(" %s, %s, %s", FloatCmpCond(i.u1), i.v.Format(b), i.v2.Format(b)) case OpcodeSExtend, OpcodeUExtend: instSuffix = fmt.Sprintf(" %s, %d->%d", i.v.Format(b), i.u1>>8, i.u1&0xff) case OpcodeCall, OpcodeCallIndirect: view := i.vs.View() vs := make([]string, len(view)) for idx := range vs { vs[idx] = view[idx].Format(b) } if i.opcode == OpcodeCallIndirect { instSuffix = fmt.Sprintf(" %s:%s, %s", i.v.Format(b), SignatureID(i.u1), strings.Join(vs, ", ")) } else { instSuffix = fmt.Sprintf(" %s:%s, %s", FuncRef(i.u1), SignatureID(i.u2), strings.Join(vs, ", ")) } case OpcodeStore, OpcodeIstore8, OpcodeIstore16, OpcodeIstore32: instSuffix = fmt.Sprintf(" %s, %s, %#x", i.v.Format(b), i.v2.Format(b), uint32(i.u1)) case OpcodeLoad, OpcodeVZeroExtLoad: instSuffix = fmt.Sprintf(" %s, %#x", i.v.Format(b), int32(i.u1)) case OpcodeLoadSplat: instSuffix = fmt.Sprintf(".%s %s, %#x", VecLane(i.u2), i.v.Format(b), int32(i.u1)) case OpcodeUload8, OpcodeUload16, OpcodeUload32, OpcodeSload8, OpcodeSload16, OpcodeSload32: instSuffix = fmt.Sprintf(" %s, %#x", i.v.Format(b), int32(i.u1)) case OpcodeSelect, OpcodeVbitselect: instSuffix = fmt.Sprintf(" %s, %s, %s", i.v.Format(b), i.v2.Format(b), i.v3.Format(b)) case OpcodeIconst: switch i.typ { case TypeI32: instSuffix = fmt.Sprintf("_32 %#x", uint32(i.u1)) case TypeI64: instSuffix = fmt.Sprintf("_64 %#x", i.u1) } case OpcodeVconst: instSuffix = fmt.Sprintf(" %016x %016x", i.u1, i.u2) case OpcodeF32const: instSuffix = fmt.Sprintf(" %f", math.Float32frombits(uint32(i.u1))) case OpcodeF64const: instSuffix = fmt.Sprintf(" %f", math.Float64frombits(i.u1)) case OpcodeReturn: view := i.vs.View() if len(view) == 0 { break } vs := make([]string, len(view)) for idx := range vs { vs[idx] = view[idx].Format(b) } instSuffix = fmt.Sprintf(" %s", strings.Join(vs, ", ")) case OpcodeJump: view := i.vs.View() vs := make([]string, len(view)+1) if i.IsFallthroughJump() { vs[0] = " fallthrough" } else { blockId := BasicBlockID(i.rValue) vs[0] = " " + b.BasicBlock(blockId).Name() } for idx := range view { vs[idx+1] = view[idx].Format(b) } instSuffix = strings.Join(vs, ", ") case OpcodeBrz, OpcodeBrnz: view := i.vs.View() vs := make([]string, len(view)+2) vs[0] = " " + i.v.Format(b) blockId := BasicBlockID(i.rValue) vs[1] = b.BasicBlock(blockId).Name() for idx := range view { vs[idx+2] = view[idx].Format(b) } instSuffix = strings.Join(vs, ", ") case OpcodeBrTable: // `BrTable index, [label1, label2, ... labelN]` instSuffix = fmt.Sprintf(" %s", i.v.Format(b)) instSuffix += ", [" for i, target := range i.rValues.View() { blk := b.BasicBlock(BasicBlockID(target)) if i == 0 { instSuffix += blk.Name() } else { instSuffix += ", " + blk.Name() } } instSuffix += "]" case OpcodeBand, OpcodeBor, OpcodeBxor, OpcodeRotr, OpcodeRotl, OpcodeIshl, OpcodeSshr, OpcodeUshr, OpcodeSdiv, OpcodeUdiv, OpcodeFcopysign, OpcodeSrem, OpcodeUrem, OpcodeVbnot, OpcodeVbxor, OpcodeVbor, OpcodeVband, OpcodeVbandnot, OpcodeVIcmp, OpcodeVFcmp: instSuffix = fmt.Sprintf(" %s, %s", i.v.Format(b), i.v2.Format(b)) case OpcodeUndefined: case OpcodeClz, OpcodeCtz, OpcodePopcnt, OpcodeFneg, OpcodeFcvtToSint, OpcodeFcvtToUint, OpcodeFcvtFromSint, OpcodeFcvtFromUint, OpcodeFcvtToSintSat, OpcodeFcvtToUintSat, OpcodeFdemote, OpcodeFpromote, OpcodeIreduce, OpcodeBitcast, OpcodeSqrt, OpcodeFabs, OpcodeCeil, OpcodeFloor, OpcodeTrunc, OpcodeNearest: instSuffix = " " + i.v.Format(b) case OpcodeVIadd, OpcodeExtIaddPairwise, OpcodeVSaddSat, OpcodeVUaddSat, OpcodeVIsub, OpcodeVSsubSat, OpcodeVUsubSat, OpcodeVImin, OpcodeVUmin, OpcodeVImax, OpcodeVUmax, OpcodeVImul, OpcodeVAvgRound, OpcodeVFadd, OpcodeVFsub, OpcodeVFmul, OpcodeVFdiv, OpcodeVIshl, OpcodeVSshr, OpcodeVUshr, OpcodeVFmin, OpcodeVFmax, OpcodeVMinPseudo, OpcodeVMaxPseudo, OpcodeSnarrow, OpcodeUnarrow, OpcodeSwizzle, OpcodeSqmulRoundSat: instSuffix = fmt.Sprintf(".%s %s, %s", VecLane(i.u1), i.v.Format(b), i.v2.Format(b)) case OpcodeVIabs, OpcodeVIneg, OpcodeVIpopcnt, OpcodeVhighBits, OpcodeVallTrue, OpcodeVanyTrue, OpcodeVFabs, OpcodeVFneg, OpcodeVSqrt, OpcodeVCeil, OpcodeVFloor, OpcodeVTrunc, OpcodeVNearest, OpcodeVFcvtToUintSat, OpcodeVFcvtToSintSat, OpcodeVFcvtFromUint, OpcodeVFcvtFromSint, OpcodeFvpromoteLow, OpcodeFvdemote, OpcodeSwidenLow, OpcodeUwidenLow, OpcodeSwidenHigh, OpcodeUwidenHigh, OpcodeSplat: instSuffix = fmt.Sprintf(".%s %s", VecLane(i.u1), i.v.Format(b)) case OpcodeExtractlane: var signedness string if i.u1 != 0 { signedness = "signed" } else { signedness = "unsigned" } instSuffix = fmt.Sprintf(".%s %d, %s (%s)", VecLane(i.u2), 0x0000FFFF&i.u1, i.v.Format(b), signedness) case OpcodeInsertlane: instSuffix = fmt.Sprintf(".%s %d, %s, %s", VecLane(i.u2), i.u1, i.v.Format(b), i.v2.Format(b)) case OpcodeShuffle: lanes := make([]byte, 16) for idx := 0; idx < 8; idx++ { lanes[idx] = byte(i.u1 >> (8 * idx)) } for idx := 0; idx < 8; idx++ { lanes[idx+8] = byte(i.u2 >> (8 * idx)) } // Prints Shuffle.[0 1 2 3 4 5 6 7 ...] v2, v3 instSuffix = fmt.Sprintf(".%v %s, %s", lanes, i.v.Format(b), i.v2.Format(b)) case OpcodeAtomicRmw: instSuffix = fmt.Sprintf(" %s_%d, %s, %s", AtomicRmwOp(i.u1), 8*i.u2, i.v.Format(b), i.v2.Format(b)) case OpcodeAtomicLoad: instSuffix = fmt.Sprintf("_%d, %s", 8*i.u1, i.v.Format(b)) case OpcodeAtomicStore: instSuffix = fmt.Sprintf("_%d, %s, %s", 8*i.u1, i.v.Format(b), i.v2.Format(b)) case OpcodeAtomicCas: instSuffix = fmt.Sprintf("_%d, %s, %s, %s", 8*i.u1, i.v.Format(b), i.v2.Format(b), i.v3.Format(b)) case OpcodeFence: instSuffix = fmt.Sprintf(" %d", i.u1) case OpcodeWideningPairwiseDotProductS: instSuffix = fmt.Sprintf(" %s, %s", i.v.Format(b), i.v2.Format(b)) default: panic(fmt.Sprintf("TODO: format for %s", i.opcode)) } instr := i.opcode.String() + instSuffix var rvs []string r1, rs := i.Returns() if r1.Valid() { rvs = append(rvs, r1.formatWithType(b)) } for _, v := range rs { rvs = append(rvs, v.formatWithType(b)) } if len(rvs) > 0 { return fmt.Sprintf("%s = %s", strings.Join(rvs, ", "), instr) } else { return instr } } // addArgumentBranchInst adds an argument to this instruction. func (i *Instruction) addArgumentBranchInst(b *builder, v Value) { switch i.opcode { case OpcodeJump, OpcodeBrz, OpcodeBrnz: i.vs = i.vs.Append(&b.varLengthPool, v) default: panic("BUG: " + i.opcode.String()) } } // Constant returns true if this instruction is a constant instruction. func (i *Instruction) Constant() bool { switch i.opcode { case OpcodeIconst, OpcodeF32const, OpcodeF64const: return true } return false } // ConstantVal returns the constant value of this instruction. // How to interpret the return value depends on the opcode. func (i *Instruction) ConstantVal() (ret uint64) { switch i.opcode { case OpcodeIconst, OpcodeF32const, OpcodeF64const: ret = i.u1 default: panic("TODO") } return } // String implements fmt.Stringer. func (o Opcode) String() (ret string) { switch o { case OpcodeInvalid: return "invalid" case OpcodeUndefined: return "Undefined" case OpcodeJump: return "Jump" case OpcodeBrz: return "Brz" case OpcodeBrnz: return "Brnz" case OpcodeBrTable: return "BrTable" case OpcodeExitWithCode: return "Exit" case OpcodeExitIfTrueWithCode: return "ExitIfTrue" case OpcodeReturn: return "Return" case OpcodeCall: return "Call" case OpcodeCallIndirect: return "CallIndirect" case OpcodeSplat: return "Splat" case OpcodeSwizzle: return "Swizzle" case OpcodeInsertlane: return "Insertlane" case OpcodeExtractlane: return "Extractlane" case OpcodeLoad: return "Load" case OpcodeLoadSplat: return "LoadSplat" case OpcodeStore: return "Store" case OpcodeUload8: return "Uload8" case OpcodeSload8: return "Sload8" case OpcodeIstore8: return "Istore8" case OpcodeUload16: return "Uload16" case OpcodeSload16: return "Sload16" case OpcodeIstore16: return "Istore16" case OpcodeUload32: return "Uload32" case OpcodeSload32: return "Sload32" case OpcodeIstore32: return "Istore32" case OpcodeIconst: return "Iconst" case OpcodeF32const: return "F32const" case OpcodeF64const: return "F64const" case OpcodeVconst: return "Vconst" case OpcodeShuffle: return "Shuffle" case OpcodeSelect: return "Select" case OpcodeVanyTrue: return "VanyTrue" case OpcodeVallTrue: return "VallTrue" case OpcodeVhighBits: return "VhighBits" case OpcodeIcmp: return "Icmp" case OpcodeIcmpImm: return "IcmpImm" case OpcodeVIcmp: return "VIcmp" case OpcodeIadd: return "Iadd" case OpcodeIsub: return "Isub" case OpcodeImul: return "Imul" case OpcodeUdiv: return "Udiv" case OpcodeSdiv: return "Sdiv" case OpcodeUrem: return "Urem" case OpcodeSrem: return "Srem" case OpcodeBand: return "Band" case OpcodeBor: return "Bor" case OpcodeBxor: return "Bxor" case OpcodeBnot: return "Bnot" case OpcodeRotl: return "Rotl" case OpcodeRotr: return "Rotr" case OpcodeIshl: return "Ishl" case OpcodeUshr: return "Ushr" case OpcodeSshr: return "Sshr" case OpcodeClz: return "Clz" case OpcodeCtz: return "Ctz" case OpcodePopcnt: return "Popcnt" case OpcodeFcmp: return "Fcmp" case OpcodeFadd: return "Fadd" case OpcodeFsub: return "Fsub" case OpcodeFmul: return "Fmul" case OpcodeFdiv: return "Fdiv" case OpcodeSqmulRoundSat: return "SqmulRoundSat" case OpcodeSqrt: return "Sqrt" case OpcodeFneg: return "Fneg" case OpcodeFabs: return "Fabs" case OpcodeFcopysign: return "Fcopysign" case OpcodeFmin: return "Fmin" case OpcodeFmax: return "Fmax" case OpcodeCeil: return "Ceil" case OpcodeFloor: return "Floor" case OpcodeTrunc: return "Trunc" case OpcodeNearest: return "Nearest" case OpcodeBitcast: return "Bitcast" case OpcodeIreduce: return "Ireduce" case OpcodeSnarrow: return "Snarrow" case OpcodeUnarrow: return "Unarrow" case OpcodeSwidenLow: return "SwidenLow" case OpcodeSwidenHigh: return "SwidenHigh" case OpcodeUwidenLow: return "UwidenLow" case OpcodeUwidenHigh: return "UwidenHigh" case OpcodeExtIaddPairwise: return "IaddPairwise" case OpcodeWideningPairwiseDotProductS: return "WideningPairwiseDotProductS" case OpcodeUExtend: return "UExtend" case OpcodeSExtend: return "SExtend" case OpcodeFpromote: return "Fpromote" case OpcodeFdemote: return "Fdemote" case OpcodeFvdemote: return "Fvdemote" case OpcodeFcvtToUint: return "FcvtToUint" case OpcodeFcvtToSint: return "FcvtToSint" case OpcodeFcvtToUintSat: return "FcvtToUintSat" case OpcodeFcvtToSintSat: return "FcvtToSintSat" case OpcodeFcvtFromUint: return "FcvtFromUint" case OpcodeFcvtFromSint: return "FcvtFromSint" case OpcodeAtomicRmw: return "AtomicRmw" case OpcodeAtomicCas: return "AtomicCas" case OpcodeAtomicLoad: return "AtomicLoad" case OpcodeAtomicStore: return "AtomicStore" case OpcodeFence: return "Fence" case OpcodeVbor: return "Vbor" case OpcodeVbxor: return "Vbxor" case OpcodeVband: return "Vband" case OpcodeVbandnot: return "Vbandnot" case OpcodeVbnot: return "Vbnot" case OpcodeVbitselect: return "Vbitselect" case OpcodeVIadd: return "VIadd" case OpcodeVSaddSat: return "VSaddSat" case OpcodeVUaddSat: return "VUaddSat" case OpcodeVSsubSat: return "VSsubSat" case OpcodeVUsubSat: return "VUsubSat" case OpcodeVAvgRound: return "OpcodeVAvgRound" case OpcodeVIsub: return "VIsub" case OpcodeVImin: return "VImin" case OpcodeVUmin: return "VUmin" case OpcodeVImax: return "VImax" case OpcodeVUmax: return "VUmax" case OpcodeVImul: return "VImul" case OpcodeVIabs: return "VIabs" case OpcodeVIneg: return "VIneg" case OpcodeVIpopcnt: return "VIpopcnt" case OpcodeVIshl: return "VIshl" case OpcodeVUshr: return "VUshr" case OpcodeVSshr: return "VSshr" case OpcodeVFabs: return "VFabs" case OpcodeVFmax: return "VFmax" case OpcodeVFmin: return "VFmin" case OpcodeVFneg: return "VFneg" case OpcodeVFadd: return "VFadd" case OpcodeVFsub: return "VFsub" case OpcodeVFmul: return "VFmul" case OpcodeVFdiv: return "VFdiv" case OpcodeVFcmp: return "VFcmp" case OpcodeVCeil: return "VCeil" case OpcodeVFloor: return "VFloor" case OpcodeVTrunc: return "VTrunc" case OpcodeVNearest: return "VNearest" case OpcodeVMaxPseudo: return "VMaxPseudo" case OpcodeVMinPseudo: return "VMinPseudo" case OpcodeVSqrt: return "VSqrt" case OpcodeVFcvtToUintSat: return "VFcvtToUintSat" case OpcodeVFcvtToSintSat: return "VFcvtToSintSat" case OpcodeVFcvtFromUint: return "VFcvtFromUint" case OpcodeVFcvtFromSint: return "VFcvtFromSint" case OpcodeFvpromoteLow: return "FvpromoteLow" case OpcodeVZeroExtLoad: return "VZeroExtLoad" } panic(fmt.Sprintf("unknown opcode %d", o)) }