blob: 0b1153edda071d21f3b60f9adff38e1607f353dd [file] [log] [blame]
# Copyright (C) 2019 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.
# Calling conventions
const CalleeSaveSpaceAsVirtualRegisters = constexpr Wasm::numberOfLLIntCalleeSaveRegisters
const CalleeSaveSpaceStackAligned = (CalleeSaveSpaceAsVirtualRegisters * SlotSize + StackAlignment - 1) & ~StackAlignmentMask
const WasmEntryPtrTag = constexpr WasmEntryPtrTag
if HAVE_FAST_TLS
const WTF_WASM_CONTEXT_KEY = constexpr WTF_WASM_CONTEXT_KEY
end
if X86_64
const NumberOfWasmArgumentGPRs = 6
elsif ARM64 or ARM64E
const NumberOfWasmArgumentGPRs = 8
else
error
end
const NumberOfWasmArgumentFPRs = 8
const NumberOfWasmArguments = NumberOfWasmArgumentGPRs + NumberOfWasmArgumentFPRs
# All callee saves must match the definition in WasmCallee.cpp
# These must match the definition in WasmMemoryInformation.cpp
const wasmInstance = csr0
const memoryBase = csr3
const memorySize = csr4
# This must match the definition in LowLevelInterpreter.asm
if X86_64
const PB = csr2
elsif ARM64 or ARM64E
const PB = csr7
else
error
end
macro forEachArgumentGPR(fn)
fn(0 * 8, wa0)
fn(1 * 8, wa1)
fn(2 * 8, wa2)
fn(3 * 8, wa3)
fn(4 * 8, wa4)
fn(5 * 8, wa5)
if ARM64 or ARM64E
fn(6 * 8, wa6)
fn(7 * 8, wa7)
end
end
macro forEachArgumentFPR(fn)
fn((NumberOfWasmArgumentGPRs + 0) * 8, wfa0)
fn((NumberOfWasmArgumentGPRs + 1) * 8, wfa1)
fn((NumberOfWasmArgumentGPRs + 2) * 8, wfa2)
fn((NumberOfWasmArgumentGPRs + 3) * 8, wfa3)
fn((NumberOfWasmArgumentGPRs + 4) * 8, wfa4)
fn((NumberOfWasmArgumentGPRs + 5) * 8, wfa5)
fn((NumberOfWasmArgumentGPRs + 6) * 8, wfa6)
fn((NumberOfWasmArgumentGPRs + 7) * 8, wfa7)
end
# Helper macros
# FIXME: Eventually this should be unified with the JS versions
# https://bugs.webkit.org/show_bug.cgi?id=203656
macro wasmDispatch(advanceReg)
addp advanceReg, PC
wasmNextInstruction()
end
macro wasmDispatchIndirect(offsetReg)
wasmDispatch(offsetReg)
end
macro wasmNextInstruction()
loadb [PB, PC, 1], t0
leap _g_opcodeMap, t1
jmp NumberOfJSOpcodeIDs * PtrSize[t1, t0, PtrSize], BytecodePtrTag
end
macro wasmNextInstructionWide16()
loadb OpcodeIDNarrowSize[PB, PC, 1], t0
leap _g_opcodeMapWide16, t1
jmp NumberOfJSOpcodeIDs * PtrSize[t1, t0, PtrSize], BytecodePtrTag
end
macro wasmNextInstructionWide32()
loadb OpcodeIDNarrowSize[PB, PC, 1], t0
leap _g_opcodeMapWide32, t1
jmp NumberOfJSOpcodeIDs * PtrSize[t1, t0, PtrSize], BytecodePtrTag
end
macro checkSwitchToJIT(increment, action)
loadp CodeBlock[cfr], ws0
baddis increment, Wasm::FunctionCodeBlock::m_tierUpCounter + Wasm::LLIntTierUpCounter::m_counter[ws0], .continue
action()
.continue:
end
macro checkSwitchToJITForPrologue(codeBlockRegister)
checkSwitchToJIT(
5,
macro()
move cfr, a0
move PC, a1
move wasmInstance, a2
cCall4(_slow_path_wasm_prologue_osr)
btpz r0, .recover
move r0, ws0
forEachArgumentGPR(macro (offset, gpr)
loadq -offset - 8 - CalleeSaveSpaceAsVirtualRegisters * 8[cfr], gpr
end)
forEachArgumentFPR(macro (offset, fpr)
loadd -offset - 8 - CalleeSaveSpaceAsVirtualRegisters * 8[cfr], fpr
end)
restoreCalleeSavesUsedByWasm()
restoreCallerPCAndCFR()
untagReturnAddress sp
jmp ws0, WasmEntryPtrTag
.recover:
notFunctionCodeBlockGetter(codeBlockRegister)
end)
end
macro checkSwitchToJITForLoop()
checkSwitchToJIT(
1,
macro()
storei PC, ArgumentCountIncludingThis + TagOffset[cfr]
prepareStateForCCall()
move cfr, a0
move PC, a1
move wasmInstance, a2
cCall4(_slow_path_wasm_loop_osr)
btpz r1, .recover
restoreCalleeSavesUsedByWasm()
restoreCallerPCAndCFR()
untagReturnAddress sp
move r0, a0
jmp r1, WasmEntryPtrTag
.recover:
loadi ArgumentCountIncludingThis + TagOffset[cfr], PC
end)
end
macro checkSwitchToJITForEpilogue()
checkSwitchToJIT(
10,
macro ()
callWasmSlowPath(_slow_path_wasm_epilogue_osr)
end)
end
# Wasm specific helpers
macro preserveCalleeSavesUsedByWasm()
# NOTE: We intentionally don't save memoryBase and memorySize here. See the comment
# in restoreCalleeSavesUsedByWasm() below for why.
subp CalleeSaveSpaceStackAligned, sp
if ARM64 or ARM64E
emit "stp x19, x26, [x29, #-16]"
elsif X86_64
storep PB, -0x8[cfr]
storep wasmInstance, -0x10[cfr]
else
error
end
end
macro restoreCalleeSavesUsedByWasm()
# NOTE: We intentionally don't restore memoryBase and memorySize here. These are saved
# and restored when entering Wasm by the JSToWasm wrapper and changes to them are meant
# to be observable within the same Wasm module.
if ARM64 or ARM64E
emit "ldp x19, x26, [x29, #-16]"
elsif X86_64
loadp -0x8[cfr], PB
loadp -0x10[cfr], wasmInstance
else
error
end
end
macro loadWasmInstanceFromTLS()
if HAVE_FAST_TLS
tls_loadp WTF_WASM_CONTEXT_KEY, wasmInstance
else
crash()
end
end
macro storeWasmInstanceToTLS(instance)
if HAVE_FAST_TLS
tls_storep instance, WTF_WASM_CONTEXT_KEY
else
crash()
end
end
macro reloadMemoryRegistersFromInstance(instance, scratch1, scratch2)
loadp Wasm::Instance::m_cachedMemory[instance], memoryBase
loadi Wasm::Instance::m_cachedMemorySize[instance], memorySize
cagedPrimitive(memoryBase, memorySize, scratch1, scratch2)
end
macro throwException(exception)
storei constexpr Wasm::ExceptionType::%exception%, ArgumentCountIncludingThis + PayloadOffset[cfr]
jmp _wasm_throw_from_slow_path_trampoline
end
macro callWasmSlowPath(slowPath)
prepareStateForCCall()
move cfr, a0
move PC, a1
move wasmInstance, a2
cCall4(slowPath)
restoreStateAfterCCall()
end
macro callWasmCallSlowPath(slowPath, action)
storei PC, ArgumentCountIncludingThis + TagOffset[cfr]
prepareStateForCCall()
move cfr, a0
move PC, a1
move wasmInstance, a2
cCall4(slowPath)
action(r0, r1)
end
macro wasmPrologue(codeBlockGetter, codeBlockSetter, loadWasmInstance)
# Set up the call frame and check if we should OSR.
tagReturnAddress sp
preserveCallerPCAndCFR()
preserveCalleeSavesUsedByWasm()
loadWasmInstance()
reloadMemoryRegistersFromInstance(wasmInstance, ws0, ws1)
codeBlockGetter(ws0)
codeBlockSetter(ws0)
# Get new sp in ws1 and check stack height.
loadi Wasm::FunctionCodeBlock::m_numCalleeLocals[ws0], ws1
lshiftp 3, ws1
addp maxFrameExtentForSlowPathCall, ws1
subp cfr, ws1, ws1
bpa ws1, cfr, .stackOverflow
bpbeq Wasm::Instance::m_cachedStackLimit[wasmInstance], ws1, .stackHeightOK
.stackOverflow:
throwException(StackOverflow)
.stackHeightOK:
move ws1, sp
forEachArgumentGPR(macro (offset, gpr)
storeq gpr, -offset - 8 - CalleeSaveSpaceAsVirtualRegisters * 8[cfr]
end)
forEachArgumentFPR(macro (offset, fpr)
stored fpr, -offset - 8 - CalleeSaveSpaceAsVirtualRegisters * 8[cfr]
end)
checkSwitchToJITForPrologue(ws0)
# Set up the PC.
loadp Wasm::FunctionCodeBlock::m_instructionsRawPointer[ws0], PB
move 0, PC
loadi Wasm::FunctionCodeBlock::m_numVars[ws0], ws1
subi NumberOfWasmArguments + CalleeSaveSpaceAsVirtualRegisters, ws1
btiz ws1, .zeroInitializeLocalsDone
negi ws1
sxi2q ws1, ws1
leap (NumberOfWasmArguments + CalleeSaveSpaceAsVirtualRegisters + 1) * -8[cfr], ws0
.zeroInitializeLocalsLoop:
addq 1, ws1
storeq 0, [ws0, ws1, 8]
btqz ws1, .zeroInitializeLocalsDone
jmp .zeroInitializeLocalsLoop
.zeroInitializeLocalsDone:
end
macro traceExecution()
if TRACING
callWasmSlowPath(_slow_path_wasm_trace)
end
end
# Less convenient, but required for opcodes that collide with reserved instructions (e.g. wasm_nop)
macro unprefixedWasmOp(opcodeName, opcodeStruct, fn)
commonOp(opcodeName, traceExecution, macro(size)
fn(macro(fn2)
fn2(opcodeName, opcodeStruct, size)
end)
end)
end
macro wasmOp(opcodeName, opcodeStruct, fn)
unprefixedWasmOp(wasm_%opcodeName%, opcodeStruct, fn)
end
# Same as unprefixedWasmOp, necessary for e.g. wasm_call
macro unprefixedSlowWasmOp(opcodeName)
unprefixedWasmOp(opcodeName, unusedOpcodeStruct, macro(ctx)
callWasmSlowPath(_slow_path_%opcodeName%)
dispatch(ctx)
end)
end
macro slowWasmOp(opcodeName)
unprefixedSlowWasmOp(wasm_%opcodeName%)
end
# Macro version of load operations: mload[suffix]
# loads field from the instruction stream and performs load[suffix] to dst
macro firstConstantRegisterIndex(ctx, fn)
ctx(macro(opcodeName, opcodeStruct, size)
size(FirstConstantRegisterIndexNarrow, FirstConstantRegisterIndexWide16, FirstConstantRegisterIndexWide32, fn)
end)
end
macro loadConstantOrVariable(ctx, index, loader)
firstConstantRegisterIndex(ctx, macro (firstConstantIndex)
bpgteq index, firstConstantIndex, .constant
loader([cfr, index, 8])
jmp .done
.constant:
loadp CodeBlock[cfr], t6
loadp Wasm::FunctionCodeBlock::m_constants + VectorBufferOffset[t6], t6
subp firstConstantIndex, index
loader([t6, index, 8])
.done:
end)
end
macro mloadq(ctx, field, dst)
wgets(ctx, field, dst)
loadConstantOrVariable(ctx, dst, macro (from)
loadq from, dst
end)
end
macro mloadi(ctx, field, dst)
wgets(ctx, field, dst)
loadConstantOrVariable(ctx, dst, macro (from)
loadi from, dst
end)
end
macro mloadp(ctx, field, dst)
wgets(ctx, field, dst)
loadConstantOrVariable(ctx, dst, macro (from)
loadp from, dst
end)
end
macro mloadf(ctx, field, dst)
wgets(ctx, field, t5)
loadConstantOrVariable(ctx, t5, macro (from)
loadf from, dst
end)
end
macro mloadd(ctx, field, dst)
wgets(ctx, field, t5)
loadConstantOrVariable(ctx, t5, macro (from)
loadd from, dst
end)
end
# Typed returns
macro returnq(ctx, value)
wgets(ctx, m_dst, t5)
storeq value, [cfr, t5, 8]
dispatch(ctx)
end
macro returni(ctx, value)
wgets(ctx, m_dst, t5)
storei value, [cfr, t5, 8]
dispatch(ctx)
end
macro returnf(ctx, value)
wgets(ctx, m_dst, t5)
storef value, [cfr, t5, 8]
dispatch(ctx)
end
macro returnd(ctx, value)
wgets(ctx, m_dst, t5)
stored value, [cfr, t5, 8]
dispatch(ctx)
end
# Wasm wrapper of get/getu that operate on ctx
macro wgets(ctx, field, dst)
ctx(macro(opcodeName, opcodeStruct, size)
size(getOperandNarrow, getOperandWide16, getOperandWide32, macro (get)
get(opcodeStruct, field, dst)
end)
end)
end
macro wgetu(ctx, field, dst)
ctx(macro(opcodeName, opcodeStruct, size)
size(getuOperandNarrow, getuOperandWide16, getuOperandWide32, macro (getu)
getu(opcodeStruct, field, dst)
end)
end)
end
# Control flow helpers
macro dispatch(ctx)
ctx(macro(opcodeName, opcodeStruct, size)
genericDispatchOp(wasmDispatch, size, opcodeName)
end)
end
macro jump(ctx, target)
wgets(ctx, target, t0)
btiz t0, .outOfLineJumpTarget
wasmDispatchIndirect(t0)
.outOfLineJumpTarget:
callWasmSlowPath(_slow_path_wasm_out_of_line_jump_target)
wasmNextInstruction()
end
macro doReturn()
restoreCalleeSavesUsedByWasm()
restoreCallerPCAndCFR()
ret
end
# Entry point
macro wasmCodeBlockGetter(targetRegister)
loadp Callee[cfr], targetRegister
andp ~3, targetRegister
loadp Wasm::LLIntCallee::m_codeBlock[targetRegister], targetRegister
end
op(wasm_function_prologue, macro ()
if not WEBASSEMBLY or not JSVALUE64 or C_LOOP or C_LOOP_WIN
error
end
wasmPrologue(wasmCodeBlockGetter, functionCodeBlockSetter, loadWasmInstanceFromTLS)
wasmNextInstruction()
end)
op(wasm_function_prologue_no_tls, macro ()
if not WEBASSEMBLY or not JSVALUE64 or C_LOOP or C_LOOP_WIN
error
end
wasmPrologue(wasmCodeBlockGetter, functionCodeBlockSetter, macro () end)
wasmNextInstruction()
end)
op(wasm_throw_from_slow_path_trampoline, macro ()
loadp Wasm::Instance::m_pointerToTopEntryFrame[wasmInstance], t5
loadp [t5], t5
copyCalleeSavesToEntryFrameCalleeSavesBuffer(t5)
move cfr, a0
addp PB, PC, a1
move wasmInstance, a2
# Slow paths and the throwException macro store the exception code in the ArgumentCountIncludingThis slot
loadi ArgumentCountIncludingThis + PayloadOffset[cfr], a3
cCall4(_slow_path_wasm_throw_exception)
jmp r0, ExceptionHandlerPtrTag
end)
# Disable wide version of narrow-only opcodes
noWide(wasm_enter)
noWide(wasm_wide16)
noWide(wasm_wide32)
# Opcodes that always invoke the slow path
slowWasmOp(ref_func)
slowWasmOp(table_get)
slowWasmOp(table_set)
slowWasmOp(table_size)
slowWasmOp(table_fill)
slowWasmOp(table_grow)
slowWasmOp(set_global_ref)
slowWasmOp(set_global_ref_portable_binding)
wasmOp(grow_memory, WasmGrowMemory, macro(ctx)
callWasmSlowPath(_slow_path_wasm_grow_memory)
reloadMemoryRegistersFromInstance(wasmInstance, ws0, ws1)
dispatch(ctx)
end)
# Opcodes that should eventually be shared with JS llint
_wasm_wide16:
wasmNextInstructionWide16()
_wasm_wide32:
wasmNextInstructionWide32()
_wasm_enter:
traceExecution()
checkStackPointerAlignment(t2, 0xdead00e1)
loadp CodeBlock[cfr], t2 // t2<CodeBlock> = cfr.CodeBlock
loadi Wasm::FunctionCodeBlock::m_numVars[t2], t2 // t2<size_t> = t2<CodeBlock>.m_numVars
subq CalleeSaveSpaceAsVirtualRegisters + NumberOfWasmArguments, t2
btiz t2, .opEnterDone
move cfr, t1
subq CalleeSaveSpaceAsVirtualRegisters * 8 + NumberOfWasmArguments * 8, t1
negi t2
sxi2q t2, t2
.opEnterLoop:
storeq 0, [t1, t2, 8]
addq 1, t2
btqnz t2, .opEnterLoop
.opEnterDone:
wasmDispatchIndirect(1)
unprefixedWasmOp(wasm_nop, WasmNop, macro(ctx)
dispatch(ctx)
end)
wasmOp(loop_hint, WasmLoopHint, macro(ctx)
checkSwitchToJITForLoop()
dispatch(ctx)
end)
wasmOp(mov, WasmMov, macro(ctx)
mloadq(ctx, m_src, t0)
returnq(ctx, t0)
end)
wasmOp(jtrue, WasmJtrue, macro(ctx)
mloadi(ctx, m_condition, t0)
btiz t0, .continue
jump(ctx, m_targetLabel)
.continue:
dispatch(ctx)
end)
wasmOp(jfalse, WasmJfalse, macro(ctx)
mloadi(ctx, m_condition, t0)
btinz t0, .continue
jump(ctx, m_targetLabel)
.continue:
dispatch(ctx)
end)
wasmOp(switch, WasmSwitch, macro(ctx)
mloadi(ctx, m_scrutinee, t0)
wgetu(ctx, m_tableIndex, t1)
loadp CodeBlock[cfr], t2
loadp Wasm::FunctionCodeBlock::m_jumpTables + VectorBufferOffset[t2], t2
muli sizeof Wasm::FunctionCodeBlock::JumpTable, t1
addp t1, t2
loadi VectorSizeOffset[t2], t3
bib t0, t3, .inBounds
.outOfBounds:
subi t3, 1, t0
.inBounds:
loadp VectorBufferOffset[t2], t2
muli sizeof Wasm::FunctionCodeBlock::JumpTableEntry, t0
loadi Wasm::FunctionCodeBlock::JumpTableEntry::startOffset[t2, t0], t1
loadi Wasm::FunctionCodeBlock::JumpTableEntry::dropCount[t2, t0], t3
loadi Wasm::FunctionCodeBlock::JumpTableEntry::keepCount[t2, t0], t5
dropKeep(t1, t3, t5)
loadis Wasm::FunctionCodeBlock::JumpTableEntry::target[t2, t0], t3
assert(macro(ok) btinz t3, .ok end)
wasmDispatchIndirect(t3)
end)
unprefixedWasmOp(wasm_jmp, WasmJmp, macro(ctx)
jump(ctx, m_targetLabel)
end)
unprefixedWasmOp(wasm_ret, WasmRet, macro(ctx)
checkSwitchToJITForEpilogue()
forEachArgumentGPR(macro (offset, gpr)
loadq -offset - 8 - CalleeSaveSpaceAsVirtualRegisters * 8[cfr], gpr
end)
forEachArgumentFPR(macro (offset, fpr)
loadd -offset - 8 - CalleeSaveSpaceAsVirtualRegisters * 8[cfr], fpr
end)
doReturn()
end)
# Wasm specific bytecodes
wasmOp(unreachable, WasmUnreachable, macro(ctx)
throwException(Unreachable)
end)
wasmOp(ret_void, WasmRetVoid, macro(ctx)
checkSwitchToJITForEpilogue()
doReturn()
end)
wasmOp(ref_is_null, WasmRefIsNull, macro(ctx)
mloadp(ctx, m_ref, t0)
cqeq t0, ValueNull, t0
returni(ctx, t0)
end)
wasmOp(get_global, WasmGetGlobal, macro(ctx)
loadp Wasm::Instance::m_globals[wasmInstance], t0
wgetu(ctx, m_globalIndex, t1)
loadq [t0, t1, 8], t0
returnq(ctx, t0)
end)
wasmOp(set_global, WasmSetGlobal, macro(ctx)
loadp Wasm::Instance::m_globals[wasmInstance], t0
wgetu(ctx, m_globalIndex, t1)
mloadq(ctx, m_value, t2)
storeq t2, [t0, t1, 8]
dispatch(ctx)
end)
wasmOp(get_global_portable_binding, WasmGetGlobalPortableBinding, macro(ctx)
loadp Wasm::Instance::m_globals[wasmInstance], t0
wgetu(ctx, m_globalIndex, t1)
loadq [t0, t1, 8], t0
loadq [t0], t0
returnq(ctx, t0)
end)
wasmOp(set_global_portable_binding, WasmSetGlobalPortableBinding, macro(ctx)
loadp Wasm::Instance::m_globals[wasmInstance], t0
wgetu(ctx, m_globalIndex, t1)
mloadq(ctx, m_value, t2)
loadq [t0, t1, 8], t0
storeq t2, [t0]
dispatch(ctx)
end)
macro slowPathForWasmCall(ctx, slowPath, storeWasmInstance)
callWasmCallSlowPath(
slowPath,
# callee is r0 and targetWasmInstance is r1
macro (callee, targetWasmInstance)
move callee, ws0
loadi ArgumentCountIncludingThis + TagOffset[cfr], PC
# the call might throw (e.g. indirect call with bad signature)
btpz targetWasmInstance, .throw
wgetu(ctx, m_stackOffset, ws1)
lshifti 3, ws1
subp cfr, ws1, sp
wgetu(ctx, m_numberOfStackArgs, ws1)
# Preserve the current instance
move wasmInstance, PB
storeWasmInstance(targetWasmInstance)
reloadMemoryRegistersFromInstance(targetWasmInstance, wa0, wa1)
# Load registers from stack
forEachArgumentGPR(macro (offset, gpr)
loadq CallFrameHeaderSize + offset[sp, ws1, 8], gpr
end)
forEachArgumentFPR(macro (offset, fpr)
loadd CallFrameHeaderSize + offset[sp, ws1, 8], fpr
end)
addp CallerFrameAndPCSize, sp
call ws0, SlowPathPtrTag
loadp CodeBlock[cfr], ws1
loadi Wasm::FunctionCodeBlock::m_numCalleeLocals[ws1], ws1
lshiftp 3, ws1
addp maxFrameExtentForSlowPathCall, ws1
subp cfr, ws1, sp
# We need to set PC to load information from the instruction stream, but we
# need to preserve its current value since it might contain a return value
move PC, memoryBase
move PB, wasmInstance
loadi ArgumentCountIncludingThis + TagOffset[cfr], PC
loadp CodeBlock[cfr], PB
loadp Wasm::FunctionCodeBlock::m_instructionsRawPointer[PB], PB
wgetu(ctx, m_stackOffset, ws1)
lshifti 3, ws1
negi ws1
sxi2q ws1, ws1
addp cfr, ws1
# Argument registers are also return registers, so they must be stored to the stack
# in case they contain return values.
wgetu(ctx, m_numberOfStackArgs, ws0)
move memoryBase, PC
forEachArgumentGPR(macro (offset, gpr)
storeq gpr, CallFrameHeaderSize + offset[ws1, ws0, 8]
end)
forEachArgumentFPR(macro (offset, fpr)
stored fpr, CallFrameHeaderSize + offset[ws1, ws0, 8]
end)
loadi ArgumentCountIncludingThis + TagOffset[cfr], PC
storeWasmInstance(wasmInstance)
reloadMemoryRegistersFromInstance(wasmInstance, ws0, ws1)
# Restore stack limit
loadp Wasm::Instance::m_pointerToActualStackLimit[wasmInstance], t5
loadp [t5], t5
storep t5, Wasm::Instance::m_cachedStackLimit[wasmInstance]
dispatch(ctx)
.throw:
restoreStateAfterCCall()
dispatch(ctx)
end)
end
unprefixedWasmOp(wasm_call, WasmCall, macro(ctx)
slowPathForWasmCall(ctx, _slow_path_wasm_call, storeWasmInstanceToTLS)
end)
unprefixedWasmOp(wasm_call_no_tls, WasmCallNoTls, macro(ctx)
slowPathForWasmCall(ctx, _slow_path_wasm_call_no_tls, macro(targetInstance) move targetInstance, wasmInstance end)
end)
wasmOp(call_indirect, WasmCallIndirect, macro(ctx)
slowPathForWasmCall(ctx, _slow_path_wasm_call_indirect, storeWasmInstanceToTLS)
end)
wasmOp(call_indirect_no_tls, WasmCallIndirectNoTls, macro(ctx)
slowPathForWasmCall(ctx, _slow_path_wasm_call_indirect_no_tls, macro(targetInstance) move targetInstance, wasmInstance end)
end)
wasmOp(current_memory, WasmCurrentMemory, macro(ctx)
loadp Wasm::Instance::m_cachedMemorySize[wasmInstance], t0
urshiftq 16, t0
returnq(ctx, t0)
end)
wasmOp(select, WasmSelect, macro(ctx)
mloadi(ctx, m_condition, t0)
btiz t0, .isZero
mloadq(ctx, m_nonZero, t0)
returnq(ctx, t0)
.isZero:
mloadq(ctx, m_zero, t0)
returnq(ctx, t0)
end)
# uses offset as scratch and returns result on pointer
macro emitCheckAndPreparePointer(ctx, pointer, offset, size)
leap size - 1[pointer, offset], t5
bpb t5, memorySize, .continuation
throwException(OutOfBoundsMemoryAccess)
.continuation:
addp memoryBase, pointer
end
macro wasmLoadOp(name, struct, size, fn)
wasmOp(name, struct, macro(ctx)
mloadi(ctx, m_pointer, t0)
wgetu(ctx, m_offset, t1)
emitCheckAndPreparePointer(ctx, t0, t1, size)
fn([t0, t1], t2)
returnq(ctx, t2)
end)
end
wasmLoadOp(load8_u, WasmLoad8U, 1, macro(mem, dst) loadb mem, dst end)
wasmLoadOp(load16_u, WasmLoad16U, 2, macro(mem, dst) loadh mem, dst end)
wasmLoadOp(load32_u, WasmLoad32U, 4, macro(mem, dst) loadi mem, dst end)
wasmLoadOp(load64_u, WasmLoad64U, 8, macro(mem, dst) loadq mem, dst end)
wasmLoadOp(i32_load8_s, WasmI32Load8S, 1, macro(mem, dst) loadbsi mem, dst end)
wasmLoadOp(i64_load8_s, WasmI64Load8S, 1, macro(mem, dst) loadbsq mem, dst end)
wasmLoadOp(i32_load16_s, WasmI32Load16S, 2, macro(mem, dst) loadhsi mem, dst end)
wasmLoadOp(i64_load16_s, WasmI64Load16S, 2, macro(mem, dst) loadhsq mem, dst end)
wasmLoadOp(i64_load32_s, WasmI64Load32S, 4, macro(mem, dst) loadis mem, dst end)
macro wasmStoreOp(name, struct, size, fn)
wasmOp(name, struct, macro(ctx)
mloadi(ctx, m_pointer, t0)
wgetu(ctx, m_offset, t1)
emitCheckAndPreparePointer(ctx, t0, t1, size)
mloadq(ctx, m_value, t2)
fn(t2, [t0, t1])
dispatch(ctx)
end)
end
wasmStoreOp(store8, WasmStore8, 1, macro(value, mem) storeb value, mem end)
wasmStoreOp(store16, WasmStore16, 2, macro(value, mem) storeh value, mem end)
wasmStoreOp(store32, WasmStore32, 4, macro(value, mem) storei value, mem end)
wasmStoreOp(store64, WasmStore64, 8, macro(value, mem) storeq value, mem end)
# Opcodes that don't have the `b3op` entry in wasm.json. This should be kept in sync
wasmOp(i32_div_s, WasmI32DivS, macro (ctx)
mloadi(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
btiz t1, .throwDivisionByZero
bineq t1, -1, .safe
bieq t0, constexpr INT32_MIN, .throwIntegerOverflow
.safe:
if X86_64
# FIXME: Add a way to static_asset that t0 is rax and t2 is rdx
# https://bugs.webkit.org/show_bug.cgi?id=203692
cdqi
idivi t1
elsif ARM64 or ARM64E
divis t1, t0
else
error
end
returni(ctx, t0)
.throwDivisionByZero:
throwException(DivisionByZero)
.throwIntegerOverflow:
throwException(IntegerOverflow)
end)
wasmOp(i32_div_u, WasmI32DivU, macro (ctx)
mloadi(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
btiz t1, .throwDivisionByZero
if X86_64
xori t2, t2
udivi t1
elsif ARM64 or ARM64E
divi t1, t0
else
error
end
returni(ctx, t0)
.throwDivisionByZero:
throwException(DivisionByZero)
end)
wasmOp(i32_rem_s, WasmI32RemS, macro (ctx)
mloadi(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
btiz t1, .throwDivisionByZero
bineq t1, -1, .safe
bineq t0, constexpr INT32_MIN, .safe
move 0, t2
jmp .return
.safe:
if X86_64
# FIXME: Add a way to static_asset that t0 is rax and t2 is rdx
# https://bugs.webkit.org/show_bug.cgi?id=203692
cdqi
idivi t1
elsif ARM64 or ARM64E
divis t1, t0, t2
muli t1, t2
subi t0, t2, t2
else
error
end
.return:
returni(ctx, t2)
.throwDivisionByZero:
throwException(DivisionByZero)
end)
wasmOp(i32_rem_u, WasmI32RemU, macro (ctx)
mloadi(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
btiz t1, .throwDivisionByZero
if X86_64
xori t2, t2
udivi t1
elsif ARM64 or ARM64E
divi t1, t0, t2
muli t1, t2
subi t0, t2, t2
else
error
end
returni(ctx, t2)
.throwDivisionByZero:
throwException(DivisionByZero)
end)
wasmOp(i32_ctz, WasmI32Ctz, macro (ctx)
mloadq(ctx, m_operand, t0)
tzcnti t0, t0
returni(ctx, t0)
end)
wasmOp(i32_popcnt, WasmI32Popcnt, macro (ctx)
mloadi(ctx, m_operand, a1)
prepareStateForCCall()
move PC, a0
cCall2(_slow_path_wasm_popcount)
restoreStateAfterCCall()
returni(ctx, r1)
end)
wasmOp(i64_div_s, WasmI64DivS, macro (ctx)
mloadq(ctx, m_lhs, t0)
mloadq(ctx, m_rhs, t1)
btqz t1, .throwDivisionByZero
bqneq t1, -1, .safe
bqeq t0, constexpr INT64_MIN, .throwIntegerOverflow
.safe:
if X86_64
# FIXME: Add a way to static_asset that t0 is rax and t2 is rdx
# https://bugs.webkit.org/show_bug.cgi?id=203692
cqoq
idivq t1
elsif ARM64 or ARM64E
divqs t1, t0
else
error
end
returnq(ctx, t0)
.throwDivisionByZero:
throwException(DivisionByZero)
.throwIntegerOverflow:
throwException(IntegerOverflow)
end)
wasmOp(i64_div_u, WasmI64DivU, macro (ctx)
mloadq(ctx, m_lhs, t0)
mloadq(ctx, m_rhs, t1)
btqz t1, .throwDivisionByZero
if X86_64
xorq t2, t2
udivq t1
elsif ARM64 or ARM64E
divq t1, t0
else
error
end
returnq(ctx, t0)
.throwDivisionByZero:
throwException(DivisionByZero)
end)
wasmOp(i64_rem_s, WasmI64RemS, macro (ctx)
mloadq(ctx, m_lhs, t0)
mloadq(ctx, m_rhs, t1)
btqz t1, .throwDivisionByZero
bqneq t1, -1, .safe
bqneq t0, constexpr INT64_MIN, .safe
move 0, t2
jmp .return
.safe:
if X86_64
# FIXME: Add a way to static_asset that t0 is rax and t2 is rdx
# https://bugs.webkit.org/show_bug.cgi?id=203692
cqoq
idivq t1
elsif ARM64 or ARM64E
divqs t1, t0, t2
mulq t1, t2
subq t0, t2, t2
else
error
end
.return:
returnq(ctx, t2) # rdx has the remainder
.throwDivisionByZero:
throwException(DivisionByZero)
end)
wasmOp(i64_rem_u, WasmI64RemU, macro (ctx)
mloadq(ctx, m_lhs, t0)
mloadq(ctx, m_rhs, t1)
btqz t1, .throwDivisionByZero
if X86_64
xorq t2, t2
udivq t1
elsif ARM64 or ARM64E
divq t1, t0, t2
mulq t1, t2
subq t0, t2, t2
else
error
end
returnq(ctx, t2)
.throwDivisionByZero:
throwException(DivisionByZero)
end)
wasmOp(i64_ctz, WasmI64Ctz, macro (ctx)
mloadq(ctx, m_operand, t0)
tzcntq t0, t0
returnq(ctx, t0)
end)
wasmOp(i64_popcnt, WasmI64Popcnt, macro (ctx)
mloadq(ctx, m_operand, a1)
prepareStateForCCall()
move PC, a0
cCall2(_slow_path_wasm_popcountll)
restoreStateAfterCCall()
returnq(ctx, r1)
end)
wasmOp(f32_trunc, WasmF32Trunc, macro (ctx)
mloadf(ctx, m_operand, ft0)
truncatef ft0, ft0
returnf(ctx, ft0)
end)
wasmOp(f32_nearest, WasmF32Nearest, macro (ctx)
mloadf(ctx, m_operand, ft0)
roundf ft0, ft0
returnf(ctx, ft0)
end)
wasmOp(f64_trunc, WasmF64Trunc, macro (ctx)
mloadd(ctx, m_operand, ft0)
truncated ft0, ft0
returnd(ctx, ft0)
end)
wasmOp(f64_nearest, WasmF64Nearest, macro (ctx)
mloadd(ctx, m_operand, ft0)
roundd ft0, ft0
returnd(ctx, ft0)
end)
wasmOp(i32_trunc_s_f32, WasmI32TruncSF32, macro (ctx)
mloadf(ctx, m_operand, ft0)
move 0xcf000000, t0 # INT32_MIN
fi2f t0, ft1
bfltun ft0, ft1, .outOfBoundsTrunc
move 0x4f000000, t0 # -INT32_MIN
fi2f t0, ft1
bfgtequn ft0, ft1, .outOfBoundsTrunc
truncatef2is ft0, t0
returni(ctx, t0)
.outOfBoundsTrunc:
throwException(OutOfBoundsTrunc)
end)
wasmOp(i32_trunc_s_f64, WasmI32TruncSF64, macro (ctx)
mloadd(ctx, m_operand, ft0)
move 0xc1e0000000000000, t0 # INT32_MIN
fq2d t0, ft1
bdltun ft0, ft1, .outOfBoundsTrunc
move 0x41e0000000000000, t0 # -INT32_MIN
fq2d t0, ft1
bdgtequn ft0, ft1, .outOfBoundsTrunc
truncated2is ft0, t0
returni(ctx, t0)
.outOfBoundsTrunc:
throwException(OutOfBoundsTrunc)
end)
wasmOp(i32_trunc_u_f32, WasmI32TruncUF32, macro (ctx)
mloadf(ctx, m_operand, ft0)
move 0xbf800000, t0 # -1.0
fi2f t0, ft1
bfltequn ft0, ft1, .outOfBoundsTrunc
move 0x4f800000, t0 # INT32_MIN * -2.0
fi2f t0, ft1
bfgtequn ft0, ft1, .outOfBoundsTrunc
truncatef2i ft0, t0
returni(ctx, t0)
.outOfBoundsTrunc:
throwException(OutOfBoundsTrunc)
end)
wasmOp(i32_trunc_u_f64, WasmI32TruncUF64, macro (ctx)
mloadd(ctx, m_operand, ft0)
move 0xbff0000000000000, t0 # -1.0
fq2d t0, ft1
bdltequn ft0, ft1, .outOfBoundsTrunc
move 0x41f0000000000000, t0 # INT32_MIN * -2.0
fq2d t0, ft1
bdgtequn ft0, ft1, .outOfBoundsTrunc
truncated2i ft0, t0
returni(ctx, t0)
.outOfBoundsTrunc:
throwException(OutOfBoundsTrunc)
end)
wasmOp(i64_trunc_s_f32, WasmI64TruncSF32, macro (ctx)
mloadd(ctx, m_operand, ft0)
move 0xdf000000, t0 # INT64_MIN
fi2f t0, ft1
bfltun ft0, ft1, .outOfBoundsTrunc
move 0x5f000000, t0 # -INT64_MIN
fi2f t0, ft1
bfgtequn ft0, ft1, .outOfBoundsTrunc
truncatef2qs ft0, t0
returnq(ctx, t0)
.outOfBoundsTrunc:
throwException(OutOfBoundsTrunc)
end)
wasmOp(i64_trunc_s_f64, WasmI64TruncSF64, macro (ctx)
mloadd(ctx, m_operand, ft0)
move 0xc3e0000000000000, t0 # INT64_MIN
fq2d t0, ft1
bdltun ft0, ft1, .outOfBoundsTrunc
move 0x43e0000000000000, t0 # -INT64_MIN
fq2d t0, ft1
bdgtequn ft0, ft1, .outOfBoundsTrunc
truncated2qs ft0, t0
returnq(ctx, t0)
.outOfBoundsTrunc:
throwException(OutOfBoundsTrunc)
end)
wasmOp(i64_trunc_u_f32, WasmI64TruncUF32, macro (ctx)
mloadf(ctx, m_operand, ft0)
move 0xbf800000, t0 # -1.0
fi2f t0, ft1
bfltequn ft0, ft1, .outOfBoundsTrunc
move 0x5f800000, t0 # INT64_MIN * -2.0
fi2f t0, ft1
bfgtequn ft0, ft1, .outOfBoundsTrunc
truncatef2q ft0, t0
returnq(ctx, t0)
.outOfBoundsTrunc:
throwException(OutOfBoundsTrunc)
end)
wasmOp(i64_trunc_u_f64, WasmI64TruncUF64, macro (ctx)
mloadd(ctx, m_operand, ft0)
move 0xbff0000000000000, t0 # -1.0
fq2d t0, ft1
bdltequn ft0, ft1, .outOfBoundsTrunc
move 0x43f0000000000000, t0 # INT64_MIN * -2.0
fq2d t0, ft1
bdgtequn ft0, ft1, .outOfBoundsTrunc
truncated2q ft0, t0
returnq(ctx, t0)
.outOfBoundsTrunc:
throwException(OutOfBoundsTrunc)
end)
wasmOp(f32_convert_u_i64, WasmF32ConvertUI64, macro (ctx)
mloadq(ctx, m_operand, t0)
if X86_64
cq2f t0, t1, ft0
elsif ARM64 or ARM64E
cq2f t0, ft0
else
error
end
returnf(ctx, ft0)
end)
wasmOp(f64_convert_u_i64, WasmF64ConvertUI64, macro (ctx)
mloadq(ctx, m_operand, t0)
if X86_64
cq2d t0, t1, ft0
elsif ARM64 or ARM64E
cq2d t0, ft0
else
error
end
returnd(ctx, ft0)
end)
wasmOp(i32_eqz, WasmI32Eqz, macro(ctx)
mloadi(ctx, m_operand, t0)
cieq t0, 0, t0
returni(ctx, t0)
end)
wasmOp(i64_shl, WasmI64Shl, macro(ctx)
mloadq(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
lshiftq t1, t0
returnq(ctx, t0)
end)
wasmOp(i64_shr_u, WasmI64ShrU, macro(ctx)
mloadq(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
urshiftq t1, t0
returnq(ctx, t0)
end)
wasmOp(i64_shr_s, WasmI64ShrS, macro(ctx)
mloadq(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
rshiftq t1, t0
returnq(ctx, t0)
end)
wasmOp(i64_rotr, WasmI64Rotr, macro(ctx)
mloadq(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
rrotateq t1, t0
returnq(ctx, t0)
end)
wasmOp(i64_rotl, WasmI64Rotl, macro(ctx)
mloadq(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
lrotateq t1, t0
returnq(ctx, t0)
end)
wasmOp(i64_eqz, WasmI64Eqz, macro(ctx)
mloadq(ctx, m_operand, t0)
cqeq t0, 0, t0
returni(ctx, t0)
end)
wasmOp(f32_min, WasmF32Min, macro(ctx)
mloadf(ctx, m_lhs, ft0)
mloadf(ctx, m_rhs, ft1)
bfeq ft0, ft1, .equal
bflt ft0, ft1, .lt
bfgt ft0, ft1, .return
.NaN:
addf ft0, ft1
jmp .return
.equal:
orf ft0, ft1
jmp .return
.lt:
moved ft0, ft1
.return:
returnf(ctx, ft1)
end)
wasmOp(f32_max, WasmF32Max, macro(ctx)
mloadf(ctx, m_lhs, ft0)
mloadf(ctx, m_rhs, ft1)
bfeq ft1, ft0, .equal
bflt ft1, ft0, .lt
bfgt ft1, ft0, .return
.NaN:
addf ft0, ft1
jmp .return
.equal:
andf ft0, ft1
jmp .return
.lt:
moved ft0, ft1
.return:
returnf(ctx, ft1)
end)
wasmOp(f32_copysign, WasmF32Copysign, macro(ctx)
mloadf(ctx, m_lhs, ft0)
mloadf(ctx, m_rhs, ft1)
ff2i ft1, t1
move 0x80000000, t2
andi t2, t1
ff2i ft0, t0
move 0x7fffffff, t2
andi t2, t0
ori t1, t0
fi2f t0, ft0
returnf(ctx, ft0)
end)
wasmOp(f64_min, WasmF64Min, macro(ctx)
mloadd(ctx, m_lhs, ft0)
mloadd(ctx, m_rhs, ft1)
bdeq ft0, ft1, .equal
bdlt ft0, ft1, .lt
bdgt ft0, ft1, .return
.NaN:
addd ft0, ft1
jmp .return
.equal:
ord ft0, ft1
jmp .return
.lt:
moved ft0, ft1
.return:
returnd(ctx, ft1)
end)
wasmOp(f64_max, WasmF64Max, macro(ctx)
mloadd(ctx, m_lhs, ft0)
mloadd(ctx, m_rhs, ft1)
bdeq ft1, ft0, .equal
bdlt ft1, ft0, .lt
bdgt ft1, ft0, .return
.NaN:
addd ft0, ft1
jmp .return
.equal:
andd ft0, ft1
jmp .return
.lt:
moved ft0, ft1
.return:
returnd(ctx, ft1)
end)
wasmOp(f64_copysign, WasmF64Copysign, macro(ctx)
mloadd(ctx, m_lhs, ft0)
mloadd(ctx, m_rhs, ft1)
fd2q ft1, t1
move 0x8000000000000000, t2
andq t2, t1
fd2q ft0, t0
move 0x7fffffffffffffff, t2
andq t2, t0
orq t1, t0
fq2d t0, ft0
returnd(ctx, ft0)
end)
wasmOp(f32_convert_u_i32, WasmF32ConvertUI32, macro(ctx)
mloadi(ctx, m_operand, t0)
ci2f t0, ft0
returnf(ctx, ft0)
end)
wasmOp(f64_convert_u_i32, WasmF64ConvertUI32, macro(ctx)
mloadi(ctx, m_operand, t0)
ci2d t0, ft0
returnd(ctx, ft0)
end)
wasmOp(i32_add, WasmI32Add, macro(ctx)
mloadi(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
addi t0, t1, t2
returni(ctx, t2)
end)
wasmOp(i32_sub, WasmI32Sub, macro(ctx)
mloadi(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
subi t1, t0
returni(ctx, t0)
end)
wasmOp(i32_mul, WasmI32Mul, macro(ctx)
mloadi(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
muli t0, t1
returni(ctx, t1)
end)
wasmOp(i32_and, WasmI32And, macro(ctx)
mloadi(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
andi t0, t1
returni(ctx, t1)
end)
wasmOp(i32_or, WasmI32Or, macro(ctx)
mloadi(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
ori t0, t1
returni(ctx, t1)
end)
wasmOp(i32_xor, WasmI32Xor, macro(ctx)
mloadi(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
xori t0, t1
returni(ctx, t1)
end)
wasmOp(i32_shl, WasmI32Shl, macro(ctx)
mloadi(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
lshifti t1, t0
returni(ctx, t0)
end)
wasmOp(i32_shr_u, WasmI32ShrU, macro(ctx)
mloadi(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
urshifti t1, t0
returni(ctx, t0)
end)
wasmOp(i32_shr_s, WasmI32ShrS, macro(ctx)
mloadi(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
rshifti t1, t0
returni(ctx, t0)
end)
wasmOp(i32_rotr, WasmI32Rotr, macro(ctx)
mloadi(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
rrotatei t1, t0
returni(ctx, t0)
end)
wasmOp(i32_rotl, WasmI32Rotl, macro(ctx)
mloadi(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
lrotatei t1, t0
returni(ctx, t0)
end)
wasmOp(i32_eq, WasmI32Eq, macro(ctx)
mloadi(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
cieq t0, t1, t2
andi 1, t2
returni(ctx, t2)
end)
wasmOp(i32_ne, WasmI32Ne, macro(ctx)
mloadi(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
cineq t0, t1, t2
andi 1, t2
returni(ctx, t2)
end)
wasmOp(i32_lt_s, WasmI32LtS, macro(ctx)
mloadi(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
cilt t0, t1, t2
andi 1, t2
returni(ctx, t2)
end)
wasmOp(i32_le_s, WasmI32LeS, macro(ctx)
mloadi(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
cilteq t0, t1, t2
andi 1, t2
returni(ctx, t2)
end)
wasmOp(i32_lt_u, WasmI32LtU, macro(ctx)
mloadi(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
cib t0, t1, t2
andi 1, t2
returni(ctx, t2)
end)
wasmOp(i32_le_u, WasmI32LeU, macro(ctx)
mloadi(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
cibeq t0, t1, t2
andi 1, t2
returni(ctx, t2)
end)
wasmOp(i32_gt_s, WasmI32GtS, macro(ctx)
mloadi(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
cigt t0, t1, t2
andi 1, t2
returni(ctx, t2)
end)
wasmOp(i32_ge_s, WasmI32GeS, macro(ctx)
mloadi(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
cigteq t0, t1, t2
andi 1, t2
returni(ctx, t2)
end)
wasmOp(i32_gt_u, WasmI32GtU, macro(ctx)
mloadi(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
cia t0, t1, t2
andi 1, t2
returni(ctx, t2)
end)
wasmOp(i32_ge_u, WasmI32GeU, macro(ctx)
mloadi(ctx, m_lhs, t0)
mloadi(ctx, m_rhs, t1)
ciaeq t0, t1, t2
andi 1, t2
returni(ctx, t2)
end)
wasmOp(i32_clz, WasmI32Clz, macro(ctx)
mloadi(ctx, m_operand, t0)
lzcnti t0, t1
returni(ctx, t1)
end)
wasmOp(i64_add, WasmI64Add, macro(ctx)
mloadq(ctx, m_lhs, t0)
mloadq(ctx, m_rhs, t1)
addq t0, t1
returnq(ctx, t1)
end)
wasmOp(i64_sub, WasmI64Sub, macro(ctx)
mloadq(ctx, m_lhs, t0)
mloadq(ctx, m_rhs, t1)
subq t1, t0
returnq(ctx, t0)
end)
wasmOp(i64_mul, WasmI64Mul, macro(ctx)
mloadq(ctx, m_lhs, t0)
mloadq(ctx, m_rhs, t1)
mulq t0, t1
returnq(ctx, t1)
end)
wasmOp(i64_and, WasmI64And, macro(ctx)
mloadq(ctx, m_lhs, t0)
mloadq(ctx, m_rhs, t1)
andq t0, t1
returnq(ctx, t1)
end)
wasmOp(i64_or, WasmI64Or, macro(ctx)
mloadq(ctx, m_lhs, t0)
mloadq(ctx, m_rhs, t1)
orq t0, t1
returnq(ctx, t1)
end)
wasmOp(i64_xor, WasmI64Xor, macro(ctx)
mloadq(ctx, m_lhs, t0)
mloadq(ctx, m_rhs, t1)
xorq t0, t1
returnq(ctx, t1)
end)
wasmOp(i64_eq, WasmI64Eq, macro(ctx)
mloadq(ctx, m_lhs, t0)
mloadq(ctx, m_rhs, t1)
cqeq t0, t1, t2
andi 1, t2
returni(ctx, t2)
end)
wasmOp(i64_ne, WasmI64Ne, macro(ctx)
mloadq(ctx, m_lhs, t0)
mloadq(ctx, m_rhs, t1)
cqneq t0, t1, t2
andi 1, t2
returni(ctx, t2)
end)
wasmOp(i64_lt_s, WasmI64LtS, macro(ctx)
mloadq(ctx, m_lhs, t0)
mloadq(ctx, m_rhs, t1)
cqlt t0, t1, t2
andi 1, t2
returni(ctx, t2)
end)
wasmOp(i64_le_s, WasmI64LeS, macro(ctx)
mloadq(ctx, m_lhs, t0)
mloadq(ctx, m_rhs, t1)
cqlteq t0, t1, t2
andi 1, t2
returni(ctx, t2)
end)
wasmOp(i64_lt_u, WasmI64LtU, macro(ctx)
mloadq(ctx, m_lhs, t0)
mloadq(ctx, m_rhs, t1)
cqb t0, t1, t2
andi 1, t2
returni(ctx, t2)
end)
wasmOp(i64_le_u, WasmI64LeU, macro(ctx)
mloadq(ctx, m_lhs, t0)
mloadq(ctx, m_rhs, t1)
cqbeq t0, t1, t2
andi 1, t2
returni(ctx, t2)
end)
wasmOp(i64_gt_s, WasmI64GtS, macro(ctx)
mloadq(ctx, m_lhs, t0)
mloadq(ctx, m_rhs, t1)
cqgt t0, t1, t2
andi 1, t2
returni(ctx, t2)
end)
wasmOp(i64_ge_s, WasmI64GeS, macro(ctx)
mloadq(ctx, m_lhs, t0)
mloadq(ctx, m_rhs, t1)
cqgteq t0, t1, t2
andi 1, t2
returni(ctx, t2)
end)
wasmOp(i64_gt_u, WasmI64GtU, macro(ctx)
mloadq(ctx, m_lhs, t0)
mloadq(ctx, m_rhs, t1)
cqa t0, t1, t2
andi 1, t2
returni(ctx, t2)
end)
wasmOp(i64_ge_u, WasmI64GeU, macro(ctx)
mloadq(ctx, m_lhs, t0)
mloadq(ctx, m_rhs, t1)
cqaeq t0, t1, t2
andi 1, t2
returni(ctx, t2)
end)
wasmOp(i64_clz, WasmI64Clz, macro(ctx)
mloadq(ctx, m_operand, t0)
lzcntq t0, t1
returnq(ctx, t1)
end)
wasmOp(f32_add, WasmF32Add, macro(ctx)
mloadf(ctx, m_lhs, ft0)
mloadf(ctx, m_rhs, ft1)
addf ft0, ft1
returnf(ctx, ft1)
end)
wasmOp(f32_sub, WasmF32Sub, macro(ctx)
mloadf(ctx, m_lhs, ft0)
mloadf(ctx, m_rhs, ft1)
subf ft1, ft0
returnf(ctx, ft0)
end)
wasmOp(f32_mul, WasmF32Mul, macro(ctx)
mloadf(ctx, m_lhs, ft0)
mloadf(ctx, m_rhs, ft1)
mulf ft0, ft1
returnf(ctx, ft1)
end)
wasmOp(f32_div, WasmF32Div, macro(ctx)
mloadf(ctx, m_lhs, ft0)
mloadf(ctx, m_rhs, ft1)
divf ft1, ft0
returnf(ctx, ft0)
end)
wasmOp(f32_abs, WasmF32Abs, macro(ctx)
mloadf(ctx, m_operand, ft0)
absf ft0, ft1
returnf(ctx, ft1)
end)
wasmOp(f32_neg, WasmF32Neg, macro(ctx)
mloadf(ctx, m_operand, ft0)
negf ft0, ft1
returnf(ctx, ft1)
end)
wasmOp(f32_ceil, WasmF32Ceil, macro(ctx)
mloadf(ctx, m_operand, ft0)
ceilf ft0, ft1
returnf(ctx, ft1)
end)
wasmOp(f32_floor, WasmF32Floor, macro(ctx)
mloadf(ctx, m_operand, ft0)
floorf ft0, ft1
returnf(ctx, ft1)
end)
wasmOp(f32_sqrt, WasmF32Sqrt, macro(ctx)
mloadf(ctx, m_operand, ft0)
sqrtf ft0, ft1
returnf(ctx, ft1)
end)
wasmOp(f32_eq, WasmF32Eq, macro(ctx)
mloadf(ctx, m_lhs, ft0)
mloadf(ctx, m_rhs, ft1)
cfeq ft0, ft1, t0
returni(ctx, t0)
end)
wasmOp(f32_ne, WasmF32Ne, macro(ctx)
mloadf(ctx, m_lhs, ft0)
mloadf(ctx, m_rhs, ft1)
cfnequn ft0, ft1, t0
returni(ctx, t0)
end)
wasmOp(f32_lt, WasmF32Lt, macro(ctx)
mloadf(ctx, m_lhs, ft0)
mloadf(ctx, m_rhs, ft1)
cflt ft0, ft1, t0
returni(ctx, t0)
end)
wasmOp(f32_le, WasmF32Le, macro(ctx)
mloadf(ctx, m_lhs, ft0)
mloadf(ctx, m_rhs, ft1)
cflteq ft0, ft1, t0
returni(ctx, t0)
end)
wasmOp(f32_gt, WasmF32Gt, macro(ctx)
mloadf(ctx, m_lhs, ft0)
mloadf(ctx, m_rhs, ft1)
cfgt ft0, ft1, t0
returni(ctx, t0)
end)
wasmOp(f32_ge, WasmF32Ge, macro(ctx)
mloadf(ctx, m_lhs, ft0)
mloadf(ctx, m_rhs, ft1)
cfgteq ft0, ft1, t0
returni(ctx, t0)
end)
wasmOp(f64_add, WasmF64Add, macro(ctx)
mloadd(ctx, m_lhs, ft0)
mloadd(ctx, m_rhs, ft1)
addd ft0, ft1
returnd(ctx, ft1)
end)
wasmOp(f64_sub, WasmF64Sub, macro(ctx)
mloadd(ctx, m_lhs, ft0)
mloadd(ctx, m_rhs, ft1)
subd ft1, ft0
returnd(ctx, ft0)
end)
wasmOp(f64_mul, WasmF64Mul, macro(ctx)
mloadd(ctx, m_lhs, ft0)
mloadd(ctx, m_rhs, ft1)
muld ft0, ft1
returnd(ctx, ft1)
end)
wasmOp(f64_div, WasmF64Div, macro(ctx)
mloadd(ctx, m_lhs, ft0)
mloadd(ctx, m_rhs, ft1)
divd ft1, ft0
returnd(ctx, ft0)
end)
wasmOp(f64_abs, WasmF64Abs, macro(ctx)
mloadd(ctx, m_operand, ft0)
absd ft0, ft1
returnd(ctx, ft1)
end)
wasmOp(f64_neg, WasmF64Neg, macro(ctx)
mloadd(ctx, m_operand, ft0)
negd ft0, ft1
returnd(ctx, ft1)
end)
wasmOp(f64_ceil, WasmF64Ceil, macro(ctx)
mloadd(ctx, m_operand, ft0)
ceild ft0, ft1
returnd(ctx, ft1)
end)
wasmOp(f64_floor, WasmF64Floor, macro(ctx)
mloadd(ctx, m_operand, ft0)
floord ft0, ft1
returnd(ctx, ft1)
end)
wasmOp(f64_sqrt, WasmF64Sqrt, macro(ctx)
mloadd(ctx, m_operand, ft0)
sqrtd ft0, ft1
returnd(ctx, ft1)
end)
wasmOp(f64_eq, WasmF64Eq, macro(ctx)
mloadd(ctx, m_lhs, ft0)
mloadd(ctx, m_rhs, ft1)
cdeq ft0, ft1, t0
returni(ctx, t0)
end)
wasmOp(f64_ne, WasmF64Ne, macro(ctx)
mloadd(ctx, m_lhs, ft0)
mloadd(ctx, m_rhs, ft1)
cdnequn ft0, ft1, t0
returni(ctx, t0)
end)
wasmOp(f64_lt, WasmF64Lt, macro(ctx)
mloadd(ctx, m_lhs, ft0)
mloadd(ctx, m_rhs, ft1)
cdlt ft0, ft1, t0
returni(ctx, t0)
end)
wasmOp(f64_le, WasmF64Le, macro(ctx)
mloadd(ctx, m_lhs, ft0)
mloadd(ctx, m_rhs, ft1)
cdlteq ft0, ft1, t0
returni(ctx, t0)
end)
wasmOp(f64_gt, WasmF64Gt, macro(ctx)
mloadd(ctx, m_lhs, ft0)
mloadd(ctx, m_rhs, ft1)
cdgt ft0, ft1, t0
returni(ctx, t0)
end)
wasmOp(f64_ge, WasmF64Ge, macro(ctx)
mloadd(ctx, m_lhs, ft0)
mloadd(ctx, m_rhs, ft1)
cdgteq ft0, ft1, t0
returni(ctx, t0)
end)
wasmOp(i32_wrap_i64, WasmI32WrapI64, macro(ctx)
mloadq(ctx, m_operand, t0)
returni(ctx, t0)
end)
wasmOp(i64_extend_s_i32, WasmI64ExtendSI32, macro(ctx)
mloadi(ctx, m_operand, t0)
sxi2q t0, t1
returnq(ctx, t1)
end)
wasmOp(i64_extend_u_i32, WasmI64ExtendUI32, macro(ctx)
mloadi(ctx, m_operand, t0)
zxi2q t0, t1
returnq(ctx, t1)
end)
wasmOp(f32_convert_s_i32, WasmF32ConvertSI32, macro(ctx)
mloadi(ctx, m_operand, t0)
ci2fs t0, ft0
returnf(ctx, ft0)
end)
wasmOp(f32_convert_s_i64, WasmF32ConvertSI64, macro(ctx)
mloadq(ctx, m_operand, t0)
cq2fs t0, ft0
returnf(ctx, ft0)
end)
wasmOp(f32_demote_f64, WasmF32DemoteF64, macro(ctx)
mloadd(ctx, m_operand, ft0)
cd2f ft0, ft1
returnf(ctx, ft1)
end)
wasmOp(f32_reinterpret_i32, WasmF32ReinterpretI32, macro(ctx)
mloadi(ctx, m_operand, t0)
fi2f t0, ft0
returnf(ctx, ft0)
end)
wasmOp(f64_convert_s_i32, WasmF64ConvertSI32, macro(ctx)
mloadi(ctx, m_operand, t0)
ci2ds t0, ft0
returnd(ctx, ft0)
end)
wasmOp(f64_convert_s_i64, WasmF64ConvertSI64, macro(ctx)
mloadq(ctx, m_operand, t0)
cq2ds t0, ft0
returnd(ctx, ft0)
end)
wasmOp(f64_promote_f32, WasmF64PromoteF32, macro(ctx)
mloadf(ctx, m_operand, ft0)
cf2d ft0, ft1
returnd(ctx, ft1)
end)
wasmOp(f64_reinterpret_i64, WasmF64ReinterpretI64, macro(ctx)
mloadq(ctx, m_operand, t0)
fq2d t0, ft0
returnd(ctx, ft0)
end)
wasmOp(i32_reinterpret_f32, WasmI32ReinterpretF32, macro(ctx)
mloadf(ctx, m_operand, ft0)
ff2i ft0, t0
returni(ctx, t0)
end)
wasmOp(i64_reinterpret_f64, WasmI64ReinterpretF64, macro(ctx)
mloadd(ctx, m_operand, ft0)
fd2q ft0, t0
returnq(ctx, t0)
end)
macro dropKeep(startOffset, drop, keep)
lshifti 3, startOffset
subp cfr, startOffset, startOffset
negi drop
sxi2q drop, drop
.copyLoop:
btiz keep, .done
loadq [startOffset, drop, 8], t6
storeq t6, [startOffset]
subi 1, keep
subp 8, startOffset
jmp .copyLoop
.done:
end
wasmOp(drop_keep, WasmDropKeep, macro(ctx)
wgetu(ctx, m_startOffset, t0)
wgetu(ctx, m_dropCount, t1)
wgetu(ctx, m_keepCount, t2)
dropKeep(t0, t1, t2)
dispatch(ctx)
end)