2 * Copyright (C) 2015 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #ifndef WASMFunctionCompiler_h
27 #define WASMFunctionCompiler_h
29 #if ENABLE(WEBASSEMBLY)
31 #include "BinarySwitch.h"
32 #include "CCallHelpers.h"
34 #include "JITOperations.h"
35 #include "LinkBuffer.h"
36 #include "MaxFrameExtentForSlowPathCall.h"
42 static int32_t JIT_OPERATION operationConvertJSValueToInt32(ExecState* exec, EncodedJSValue value)
44 return JSValue::decode(value).toInt32(exec);
47 static double JIT_OPERATION operationConvertJSValueToDouble(ExecState* exec, EncodedJSValue value)
49 return JSValue::decode(value).toNumber(exec);
52 #if !CPU(X86) && !CPU(X86_64)
53 static int32_t JIT_OPERATION operationDiv(int32_t left, int32_t right)
58 static int32_t JIT_OPERATION operationMod(int32_t left, int32_t right)
63 static uint32_t JIT_OPERATION operationUnsignedDiv(uint32_t left, uint32_t right)
68 static uint32_t JIT_OPERATION operationUnsignedMod(uint32_t left, uint32_t right)
74 class WASMFunctionCompiler : private CCallHelpers {
76 typedef int Expression;
77 typedef int Statement;
78 typedef int ExpressionList;
83 enum class JumpCondition { Zero, NonZero };
85 WASMFunctionCompiler(VM& vm, CodeBlock* codeBlock, JSWASMModule* module, unsigned stackHeight)
86 : CCallHelpers(&vm, codeBlock)
88 , m_stackHeight(stackHeight)
92 void startFunction(const Vector<WASMType>& arguments, uint32_t numberOfI32LocalVariables, uint32_t numberOfF32LocalVariables, uint32_t numberOfF64LocalVariables)
94 m_calleeSaveSpace = WTF::roundUpToMultipleOf(sizeof(StackSlot), RegisterSet::webAssemblyCalleeSaveRegisters().numberOfSetRegisters() * sizeof(void*));
95 m_codeBlock->setCalleeSaveRegisters(RegisterSet::webAssemblyCalleeSaveRegisters());
97 emitFunctionPrologue();
98 emitPutImmediateToCallFrameHeader(m_codeBlock, JSStack::CodeBlock);
100 m_beginLabel = label();
102 addPtr(TrustedImm32(-m_calleeSaveSpace - WTF::roundUpToMultipleOf(stackAlignmentRegisters(), m_stackHeight) * sizeof(StackSlot) - maxFrameExtentForSlowPathCall), GPRInfo::callFrameRegister, GPRInfo::regT1);
103 m_stackOverflow = branchPtr(Above, AbsoluteAddress(m_vm->addressOfStackLimit()), GPRInfo::regT1);
105 move(GPRInfo::regT1, stackPointerRegister);
106 checkStackPointerAlignment();
108 emitSaveCalleeSaves();
109 emitMaterializeTagCheckRegisters();
111 m_numberOfLocals = arguments.size() + numberOfI32LocalVariables + numberOfF32LocalVariables + numberOfF64LocalVariables;
113 unsigned localIndex = 0;
114 for (size_t i = 0; i < arguments.size(); ++i) {
115 Address address(GPRInfo::callFrameRegister, CallFrame::argumentOffset(i) * sizeof(Register));
116 switch (arguments[i]) {
119 loadValueAndConvertToInt32(address, GPRInfo::regT0);
121 loadValueAndConvertToInt32(address, GPRInfo::regT0, GPRInfo::regT1);
123 store32(GPRInfo::regT0, localAddress(localIndex++));
128 loadValueAndConvertToDouble(address, FPRInfo::fpRegT0, GPRInfo::regT0, GPRInfo::regT1);
130 loadValueAndConvertToDouble(address, FPRInfo::fpRegT0, GPRInfo::regT0, GPRInfo::regT1, GPRInfo::regT2, FPRInfo::fpRegT1);
132 if (arguments[i] == WASMType::F32)
133 convertDoubleToFloat(FPRInfo::fpRegT0, FPRInfo::fpRegT0);
134 storeDouble(FPRInfo::fpRegT0, localAddress(localIndex++));
137 ASSERT_NOT_REACHED();
140 for (uint32_t i = 0; i < numberOfI32LocalVariables; ++i)
141 store32(TrustedImm32(0), localAddress(localIndex++));
142 for (uint32_t i = 0; i < numberOfF32LocalVariables; ++i)
143 store32(TrustedImm32(0), localAddress(localIndex++));
144 for (uint32_t i = 0; i < numberOfF64LocalVariables; ++i) {
146 store64(TrustedImm64(0), localAddress(localIndex++));
148 store32(TrustedImm32(0), localAddress(localIndex));
149 store32(TrustedImm32(0), localAddress(localIndex).withOffset(4));
154 m_codeBlock->setNumParameters(1 + arguments.size());
159 ASSERT(!m_tempStackTop);
161 // FIXME: Remove these if the last statement is a return statement.
163 JSValueRegs returnValueRegs(GPRInfo::returnValueGPR);
165 JSValueRegs returnValueRegs(GPRInfo::returnValueGPR2, GPRInfo::returnValueGPR);
167 moveTrustedValue(jsUndefined(), returnValueRegs);
168 emitRestoreCalleeSaves();
169 emitFunctionEpilogue();
172 m_stackOverflow.link(this);
173 if (maxFrameExtentForSlowPathCall)
174 addPtr(TrustedImm32(-maxFrameExtentForSlowPathCall), stackPointerRegister);
175 setupArgumentsWithExecState(TrustedImmPtr(m_codeBlock));
176 appendCallWithExceptionCheck(operationThrowStackOverflowError);
178 // FIXME: Implement arity check.
179 Label arityCheck = label();
180 emitFunctionPrologue();
181 emitPutImmediateToCallFrameHeader(m_codeBlock, JSStack::CodeBlock);
184 if (!m_divideErrorJumpList.empty()) {
185 m_divideErrorJumpList.link(this);
187 setupArgumentsExecState();
188 appendCallWithExceptionCheck(operationThrowDivideError);
191 if (!m_exceptionChecks.empty()) {
192 m_exceptionChecks.link(this);
194 copyCalleeSavesToVMCalleeSavesBuffer();
196 // lookupExceptionHandler is passed two arguments, the VM and the exec (the CallFrame*).
197 move(TrustedImmPtr(vm()), GPRInfo::argumentGPR0);
198 move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR1);
201 // FIXME: should use the call abstraction, but this is currently in the SpeculativeJIT layer!
202 poke(GPRInfo::argumentGPR0);
203 poke(GPRInfo::argumentGPR1, 1);
205 m_calls.append(std::make_pair(call(), FunctionPtr(lookupExceptionHandlerFromCallerFrame).value()));
206 jumpToExceptionHandler();
209 LinkBuffer patchBuffer(*m_vm, *this, m_codeBlock, JITCompilationMustSucceed);
211 for (const auto& iterator : m_calls)
212 patchBuffer.link(iterator.first, FunctionPtr(iterator.second));
214 for (size_t i = 0; i < m_callCompilationInfo.size(); ++i) {
215 CallCompilationInfo& compilationInfo = m_callCompilationInfo[i];
216 CallLinkInfo& info = *compilationInfo.callLinkInfo;
217 info.setCallLocations(patchBuffer.locationOfNearCall(compilationInfo.callReturnLocation),
218 patchBuffer.locationOf(compilationInfo.hotPathBegin),
219 patchBuffer.locationOfNearCall(compilationInfo.hotPathOther));
222 MacroAssemblerCodePtr withArityCheck = patchBuffer.locationOf(arityCheck);
223 CodeRef result = FINALIZE_CODE(patchBuffer, ("Baseline JIT code for WebAssembly"));
224 m_codeBlock->setJITCode(adoptRef(new DirectJITCode(result, withArityCheck, JITCode::BaselineJIT)));
225 m_codeBlock->capabilityLevel();
228 void buildSetLocal(uint32_t localIndex, int, WASMType type)
233 load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT0);
235 store32(GPRInfo::regT0, localAddress(localIndex));
238 loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT0);
240 storeDouble(FPRInfo::fpRegT0, localAddress(localIndex));
243 ASSERT_NOT_REACHED();
247 void buildSetGlobal(uint32_t globalIndex, int, WASMType type)
249 move(TrustedImmPtr(&m_module->globalVariables()[globalIndex]), GPRInfo::regT0);
253 load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT1);
254 store32(GPRInfo::regT1, GPRInfo::regT0);
257 loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT0);
258 storeDouble(FPRInfo::fpRegT0, GPRInfo::regT0);
261 ASSERT_NOT_REACHED();
266 void buildReturn(int, WASMExpressionType returnType)
269 JSValueRegs returnValueRegs(GPRInfo::returnValueGPR);
271 JSValueRegs returnValueRegs(GPRInfo::returnValueGPR2, GPRInfo::returnValueGPR);
273 switch (returnType) {
274 case WASMExpressionType::I32:
275 load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::returnValueGPR);
277 or64(GPRInfo::tagTypeNumberRegister, GPRInfo::returnValueGPR);
279 move(TrustedImm32(JSValue::Int32Tag), GPRInfo::returnValueGPR2);
283 case WASMExpressionType::F32:
284 case WASMExpressionType::F64:
285 loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT0);
286 if (returnType == WASMExpressionType::F32)
287 convertFloatToDouble(FPRInfo::fpRegT0, FPRInfo::fpRegT0);
289 boxDouble(FPRInfo::fpRegT0, GPRInfo::returnValueGPR);
291 boxDouble(FPRInfo::fpRegT0, GPRInfo::returnValueGPR2, GPRInfo::returnValueGPR);
295 case WASMExpressionType::Void:
296 moveTrustedValue(jsUndefined(), returnValueRegs);
299 ASSERT_NOT_REACHED();
301 emitRestoreCalleeSaves();
302 emitFunctionEpilogue();
306 int buildImmediateI32(uint32_t immediate)
308 store32(Imm32(immediate), temporaryAddress(m_tempStackTop++));
312 int buildImmediateF32(float immediate)
314 store32(Imm32(bitwise_cast<int32_t>(immediate)), temporaryAddress(m_tempStackTop++));
318 int buildImmediateF64(double immediate)
321 store64(Imm64(bitwise_cast<int64_t>(immediate)), temporaryAddress(m_tempStackTop++));
325 int32_t int32Values[2];
328 store32(Imm32(u.int32Values[0]), temporaryAddress(m_tempStackTop - 1));
329 store32(Imm32(u.int32Values[1]), temporaryAddress(m_tempStackTop - 1).withOffset(4));
334 int buildGetLocal(uint32_t localIndex, WASMType type)
339 load32(localAddress(localIndex), GPRInfo::regT0);
340 store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop++));
343 loadDouble(localAddress(localIndex), FPRInfo::fpRegT0);
344 storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop++));
347 ASSERT_NOT_REACHED();
352 int buildGetGlobal(uint32_t globalIndex, WASMType type)
354 move(TrustedImmPtr(&m_module->globalVariables()[globalIndex]), GPRInfo::regT0);
358 load32(GPRInfo::regT0, GPRInfo::regT0);
359 store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop++));
362 loadDouble(GPRInfo::regT0, FPRInfo::fpRegT0);
363 storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop++));
366 ASSERT_NOT_REACHED();
371 int buildUnaryI32(int, WASMOpExpressionI32 op)
373 load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT0);
375 case WASMOpExpressionI32::Negate:
376 neg32(GPRInfo::regT0);
378 case WASMOpExpressionI32::BitNot:
379 xor32(TrustedImm32(-1), GPRInfo::regT0);
381 case WASMOpExpressionI32::CountLeadingZeros:
382 countLeadingZeros32(GPRInfo::regT0, GPRInfo::regT0);
384 case WASMOpExpressionI32::LogicalNot: {
385 // FIXME: Don't use branches.
386 Jump zero = branchTest32(Zero, GPRInfo::regT0);
387 move(TrustedImm32(0), GPRInfo::regT0);
390 move(TrustedImm32(1), GPRInfo::regT0);
394 case WASMOpExpressionI32::Abs: {
395 // FIXME: Don't use branches.
396 Jump end = branchTest32(PositiveOrZero, GPRInfo::regT0);
397 neg32(GPRInfo::regT0);
402 ASSERT_NOT_REACHED();
404 store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop - 1));
408 int buildUnaryF32(int, WASMOpExpressionF32 op)
410 loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT1);
412 case WASMOpExpressionF32::Negate:
413 convertFloatToDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT1);
414 negateDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0);
415 convertDoubleToFloat(FPRInfo::fpRegT0, FPRInfo::fpRegT0);
417 case WASMOpExpressionF32::Abs:
418 convertFloatToDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT1);
419 absDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0);
420 convertDoubleToFloat(FPRInfo::fpRegT0, FPRInfo::fpRegT0);
422 case WASMOpExpressionF32::Ceil:
423 callOperation(ceilf, FPRInfo::fpRegT1, FPRInfo::fpRegT0);
425 case WASMOpExpressionF32::Floor:
426 callOperation(floorf, FPRInfo::fpRegT1, FPRInfo::fpRegT0);
428 case WASMOpExpressionF32::Sqrt:
429 convertFloatToDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT1);
430 sqrtDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0);
431 convertDoubleToFloat(FPRInfo::fpRegT0, FPRInfo::fpRegT0);
434 ASSERT_NOT_REACHED();
436 storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop - 1));
440 int buildBinaryI32(int, int, WASMOpExpressionI32 op)
442 load32(temporaryAddress(m_tempStackTop - 2), GPRInfo::regT0);
443 load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT1);
445 case WASMOpExpressionI32::Add:
446 add32(GPRInfo::regT1, GPRInfo::regT0);
448 case WASMOpExpressionI32::Sub:
449 sub32(GPRInfo::regT1, GPRInfo::regT0);
451 case WASMOpExpressionI32::Mul:
452 mul32(GPRInfo::regT1, GPRInfo::regT0);
454 case WASMOpExpressionI32::SDiv:
455 case WASMOpExpressionI32::UDiv:
456 case WASMOpExpressionI32::SMod:
457 case WASMOpExpressionI32::UMod: {
458 m_divideErrorJumpList.append(branchTest32(Zero, GPRInfo::regT1));
459 if (op == WASMOpExpressionI32::SDiv || op == WASMOpExpressionI32::SMod) {
460 Jump denominatorNotNeg1 = branch32(NotEqual, GPRInfo::regT1, TrustedImm32(-1));
461 m_divideErrorJumpList.append(branch32(Equal, GPRInfo::regT0, TrustedImm32(-2147483647-1)));
462 denominatorNotNeg1.link(this);
464 #if CPU(X86) || CPU(X86_64)
465 ASSERT(GPRInfo::regT0 == X86Registers::eax);
466 move(GPRInfo::regT1, X86Registers::ecx);
467 if (op == WASMOpExpressionI32::SDiv || op == WASMOpExpressionI32::SMod) {
469 m_assembler.idivl_r(X86Registers::ecx);
471 ASSERT(op == WASMOpExpressionI32::UDiv || op == WASMOpExpressionI32::UMod);
472 xor32(X86Registers::edx, X86Registers::edx);
473 m_assembler.divl_r(X86Registers::ecx);
475 if (op == WASMOpExpressionI32::SMod || op == WASMOpExpressionI32::UMod)
476 move(X86Registers::edx, GPRInfo::regT0);
478 // FIXME: We should be able to do an inline div on ARMv7 and ARM64.
480 case WASMOpExpressionI32::SDiv:
481 callOperation(operationDiv, GPRInfo::regT0, GPRInfo::regT1, GPRInfo::regT0);
483 case WASMOpExpressionI32::UDiv:
484 callOperation(operationUnsignedDiv, GPRInfo::regT0, GPRInfo::regT1, GPRInfo::regT0);
486 case WASMOpExpressionI32::SMod:
487 callOperation(operationMod, GPRInfo::regT0, GPRInfo::regT1, GPRInfo::regT0);
489 case WASMOpExpressionI32::UMod:
490 callOperation(operationUnsignedMod, GPRInfo::regT0, GPRInfo::regT1, GPRInfo::regT0);
493 ASSERT_NOT_REACHED();
498 case WASMOpExpressionI32::BitOr:
499 or32(GPRInfo::regT1, GPRInfo::regT0);
501 case WASMOpExpressionI32::BitAnd:
502 and32(GPRInfo::regT1, GPRInfo::regT0);
504 case WASMOpExpressionI32::BitXor:
505 xor32(GPRInfo::regT1, GPRInfo::regT0);
507 case WASMOpExpressionI32::LeftShift:
508 lshift32(GPRInfo::regT1, GPRInfo::regT0);
510 case WASMOpExpressionI32::ArithmeticRightShift:
511 rshift32(GPRInfo::regT1, GPRInfo::regT0);
513 case WASMOpExpressionI32::LogicalRightShift:
514 urshift32(GPRInfo::regT1, GPRInfo::regT0);
517 ASSERT_NOT_REACHED();
520 store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop - 1));
524 int buildBinaryF32(int, int, WASMOpExpressionF32 op)
526 loadDouble(temporaryAddress(m_tempStackTop - 2), FPRInfo::fpRegT0);
527 loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT1);
528 convertFloatToDouble(FPRInfo::fpRegT0, FPRInfo::fpRegT0);
529 convertFloatToDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT1);
531 case WASMOpExpressionF32::Add:
532 addDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0);
534 case WASMOpExpressionF32::Sub:
535 subDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0);
537 case WASMOpExpressionF32::Mul:
538 mulDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0);
540 case WASMOpExpressionF32::Div:
541 divDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0);
544 RELEASE_ASSERT_NOT_REACHED();
546 convertDoubleToFloat(FPRInfo::fpRegT0, FPRInfo::fpRegT0);
548 storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop - 1));
552 int buildRelationalI32(int, int, WASMOpExpressionI32 op)
554 load32(temporaryAddress(m_tempStackTop - 2), GPRInfo::regT0);
555 load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT1);
556 RelationalCondition condition;
558 case WASMOpExpressionI32::EqualI32:
561 case WASMOpExpressionI32::NotEqualI32:
562 condition = NotEqual;
564 case WASMOpExpressionI32::SLessThanI32:
565 condition = LessThan;
567 case WASMOpExpressionI32::ULessThanI32:
570 case WASMOpExpressionI32::SLessThanOrEqualI32:
571 condition = LessThanOrEqual;
573 case WASMOpExpressionI32::ULessThanOrEqualI32:
574 condition = BelowOrEqual;
576 case WASMOpExpressionI32::SGreaterThanI32:
577 condition = GreaterThan;
579 case WASMOpExpressionI32::UGreaterThanI32:
582 case WASMOpExpressionI32::SGreaterThanOrEqualI32:
583 condition = GreaterThanOrEqual;
585 case WASMOpExpressionI32::UGreaterThanOrEqualI32:
586 condition = AboveOrEqual;
589 RELEASE_ASSERT_NOT_REACHED();
591 compare32(condition, GPRInfo::regT0, GPRInfo::regT1, GPRInfo::regT0);
593 store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop - 1));
597 int buildRelationalF32(int, int, WASMOpExpressionI32 op)
599 loadDouble(temporaryAddress(m_tempStackTop - 2), FPRInfo::fpRegT0);
600 loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT1);
601 convertFloatToDouble(FPRInfo::fpRegT0, FPRInfo::fpRegT0);
602 convertFloatToDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT1);
603 DoubleCondition condition;
605 case WASMOpExpressionI32::EqualF32:
606 condition = DoubleEqual;
608 case WASMOpExpressionI32::NotEqualF32:
609 condition = DoubleNotEqual;
611 case WASMOpExpressionI32::LessThanF32:
612 condition = DoubleLessThan;
614 case WASMOpExpressionI32::LessThanOrEqualF32:
615 condition = DoubleLessThanOrEqual;
617 case WASMOpExpressionI32::GreaterThanF32:
618 condition = DoubleGreaterThan;
620 case WASMOpExpressionI32::GreaterThanOrEqualF32:
621 condition = DoubleGreaterThanOrEqual;
624 RELEASE_ASSERT_NOT_REACHED();
627 Jump trueCase = branchDouble(condition, FPRInfo::fpRegT0, FPRInfo::fpRegT1);
628 store32(TrustedImm32(0), temporaryAddress(m_tempStackTop - 1));
631 store32(TrustedImm32(1), temporaryAddress(m_tempStackTop - 1));
636 int buildRelationalF64(int, int, WASMOpExpressionI32 op)
638 loadDouble(temporaryAddress(m_tempStackTop - 2), FPRInfo::fpRegT0);
639 loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT1);
640 DoubleCondition condition;
642 case WASMOpExpressionI32::EqualF64:
643 condition = DoubleEqual;
645 case WASMOpExpressionI32::NotEqualF64:
646 condition = DoubleNotEqual;
648 case WASMOpExpressionI32::LessThanF64:
649 condition = DoubleLessThan;
651 case WASMOpExpressionI32::LessThanOrEqualF64:
652 condition = DoubleLessThanOrEqual;
654 case WASMOpExpressionI32::GreaterThanF64:
655 condition = DoubleGreaterThan;
657 case WASMOpExpressionI32::GreaterThanOrEqualF64:
658 condition = DoubleGreaterThanOrEqual;
661 RELEASE_ASSERT_NOT_REACHED();
664 Jump trueCase = branchDouble(condition, FPRInfo::fpRegT0, FPRInfo::fpRegT1);
665 store32(TrustedImm32(0), temporaryAddress(m_tempStackTop - 1));
668 store32(TrustedImm32(1), temporaryAddress(m_tempStackTop - 1));
673 int buildCallInternal(uint32_t functionIndex, int, const WASMSignature& signature, WASMExpressionType returnType)
675 boxArgumentsAndAdjustStackPointer(signature.arguments);
677 JSFunction* function = m_module->functions()[functionIndex].get();
678 move(TrustedImmPtr(function), GPRInfo::regT0);
680 callAndUnboxResult(returnType);
684 int buildCallIndirect(uint32_t functionPointerTableIndex, int, int, const WASMSignature& signature, WASMExpressionType returnType)
686 boxArgumentsAndAdjustStackPointer(signature.arguments);
688 const Vector<JSFunction*>& functions = m_module->functionPointerTables()[functionPointerTableIndex].functions;
689 move(TrustedImmPtr(functions.data()), GPRInfo::regT0);
690 load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT1);
692 and32(TrustedImm32(functions.size() - 1), GPRInfo::regT1);
693 loadPtr(BaseIndex(GPRInfo::regT0, GPRInfo::regT1, timesPtr()), GPRInfo::regT0);
695 callAndUnboxResult(returnType);
699 int buildCallImport(uint32_t functionImportIndex, int, const WASMSignature& signature, WASMExpressionType returnType)
701 boxArgumentsAndAdjustStackPointer(signature.arguments);
703 JSFunction* function = m_module->importedFunctions()[functionImportIndex].get();
704 move(TrustedImmPtr(function), GPRInfo::regT0);
706 callAndUnboxResult(returnType);
710 void appendExpressionList(int&, int) { }
712 void linkTarget(JumpTarget& target)
714 target.label = label();
715 target.jumpList.link(this);
718 void jumpToTarget(JumpTarget& target)
720 if (target.label.isSet())
723 target.jumpList.append(jump());
726 void jumpToTargetIf(JumpCondition condition, int, JumpTarget& target)
728 load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT0);
730 Jump taken = branchTest32((condition == JumpCondition::Zero) ? Zero : NonZero, GPRInfo::regT0);
731 if (target.label.isSet())
732 taken.linkTo(target.label, this);
734 target.jumpList.append(taken);
739 m_breakTargets.append(JumpTarget());
740 m_continueTargets.append(JumpTarget());
745 m_breakTargets.removeLast();
746 m_continueTargets.removeLast();
751 m_breakTargets.append(JumpTarget());
756 m_breakTargets.removeLast();
761 m_breakLabelTargets.append(JumpTarget());
762 m_continueLabelTargets.append(JumpTarget());
764 linkTarget(m_continueLabelTargets.last());
769 linkTarget(m_breakLabelTargets.last());
771 m_breakLabelTargets.removeLast();
772 m_continueLabelTargets.removeLast();
775 JumpTarget& breakTarget()
777 return m_breakTargets.last();
780 JumpTarget& continueTarget()
782 return m_continueTargets.last();
785 JumpTarget& breakLabelTarget(uint32_t labelIndex)
787 return m_breakLabelTargets[labelIndex];
790 JumpTarget& continueLabelTarget(uint32_t labelIndex)
792 return m_continueLabelTargets[labelIndex];
795 void buildSwitch(int, const Vector<int64_t>& cases, Vector<JumpTarget>& targets, JumpTarget defaultTarget)
797 load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT0);
799 BinarySwitch binarySwitch(GPRInfo::regT0, cases, BinarySwitch::Int32);
800 while (binarySwitch.advance(*this)) {
801 unsigned index = binarySwitch.caseIndex();
802 jump(targets[index].label);
804 binarySwitch.fallThrough().linkTo(defaultTarget.label, this);
814 Address localAddress(unsigned localIndex) const
816 ASSERT(localIndex < m_numberOfLocals);
817 return Address(GPRInfo::callFrameRegister, -m_calleeSaveSpace - (localIndex + 1) * sizeof(StackSlot));
820 Address temporaryAddress(unsigned temporaryIndex) const
822 ASSERT(m_numberOfLocals + temporaryIndex < m_stackHeight);
823 return Address(GPRInfo::callFrameRegister, -m_calleeSaveSpace - (m_numberOfLocals + temporaryIndex + 1) * sizeof(StackSlot));
826 void appendCall(const FunctionPtr& function)
828 m_calls.append(std::make_pair(call(), function.value()));
831 void appendCallWithExceptionCheck(const FunctionPtr& function)
833 appendCall(function);
834 m_exceptionChecks.append(emitExceptionCheck());
837 Call emitNakedCall(CodePtr function)
839 Call nakedCall = nearCall();
840 m_calls.append(std::make_pair(nakedCall, function.executableAddress()));
844 void appendCallSetResult(const FunctionPtr& function, GPRReg result)
846 appendCall(function);
847 move(GPRInfo::returnValueGPR, result);
851 void appendCallSetResult(const FunctionPtr& function, FPRReg result)
853 appendCall(function);
854 m_assembler.fstpl(0, stackPointerRegister);
855 loadDouble(stackPointerRegister, result);
857 #elif CPU(ARM) && !CPU(ARM_HARDFP)
858 void appendCallSetResult(const FunctionPtr& function, FPRReg result)
860 appendCall(function);
861 m_assembler.vmov(result, GPRInfo::returnValueGPR, GPRInfo::returnValueGPR2);
863 #else // CPU(X86_64) || (CPU(ARM) && CPU(ARM_HARDFP)) || CPU(ARM64) || CPU(MIPS) || CPU(SH4)
864 void appendCallSetResult(const FunctionPtr& function, FPRReg result)
866 appendCall(function);
867 moveDouble(FPRInfo::returnValueFPR, result);
872 void callOperation(Z_JITOperation_EJ operation, GPRReg src, GPRReg dst)
874 setupArgumentsWithExecState(src);
875 appendCallSetResult(operation, dst);
878 void callOperation(D_JITOperation_EJ operation, GPRReg src, FPRReg dst)
880 setupArgumentsWithExecState(src);
881 appendCallSetResult(operation, dst);
884 // EncodedJSValue in JSVALUE32_64 is a 64-bit integer. When being compiled in ARM EABI, it must be aligned even-numbered register (r0, r2 or [sp]).
885 // To avoid assemblies from using wrong registers, let's occupy r1 or r3 with a dummy argument when necessary.
886 #if (COMPILER_SUPPORTS(EABI) && CPU(ARM)) || CPU(MIPS)
887 #define EABI_32BIT_DUMMY_ARG TrustedImm32(0),
889 #define EABI_32BIT_DUMMY_ARG
892 void callOperation(Z_JITOperation_EJ operation, GPRReg srcTag, GPRReg srcPayload, GPRReg dst)
894 setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG srcPayload, srcTag);
895 appendCallSetResult(operation, dst);
898 void callOperation(D_JITOperation_EJ operation, GPRReg srcTag, GPRReg srcPayload, FPRReg dst)
900 setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG srcPayload, srcTag);
901 appendCallSetResult(operation, dst);
905 void callOperation(float JIT_OPERATION (*operation)(float), FPRegisterID src, FPRegisterID dst)
908 appendCallSetResult(operation, dst);
911 void callOperation(int32_t JIT_OPERATION (*operation)(int32_t, int32_t), GPRReg src1, GPRReg src2, GPRReg dst)
913 setupArguments(src1, src2);
914 appendCallSetResult(operation, dst);
917 void callOperation(uint32_t JIT_OPERATION (*operation)(uint32_t, uint32_t), GPRReg src1, GPRReg src2, GPRReg dst)
919 setupArguments(src1, src2);
920 appendCallSetResult(operation, dst);
923 void boxArgumentsAndAdjustStackPointer(const Vector<WASMType>& arguments)
925 size_t argumentCount = arguments.size();
926 int stackOffset = -m_calleeSaveSpace - WTF::roundUpToMultipleOf(stackAlignmentRegisters(), m_numberOfLocals + m_tempStackTop + argumentCount + 1 + JSStack::CallFrameHeaderSize);
928 storeTrustedValue(jsUndefined(), Address(GPRInfo::callFrameRegister, (stackOffset + CallFrame::thisArgumentOffset()) * sizeof(Register)));
930 for (size_t i = 0; i < argumentCount; ++i) {
931 Address address(GPRInfo::callFrameRegister, (stackOffset + CallFrame::argumentOffset(i)) * sizeof(Register));
932 switch (arguments[i]) {
934 load32(temporaryAddress(m_tempStackTop - argumentCount + i), GPRInfo::regT0);
936 or64(GPRInfo::tagTypeNumberRegister, GPRInfo::regT0);
937 store64(GPRInfo::regT0, address);
939 store32(GPRInfo::regT0, address.withOffset(PayloadOffset));
940 store32(TrustedImm32(JSValue::Int32Tag), address.withOffset(TagOffset));
944 ASSERT_NOT_REACHED();
947 m_tempStackTop -= argumentCount;
949 addPtr(TrustedImm32(stackOffset * sizeof(Register) + sizeof(CallerFrameAndPC)), GPRInfo::callFrameRegister, stackPointerRegister);
950 store32(TrustedImm32(argumentCount + 1), Address(stackPointerRegister, JSStack::ArgumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset - sizeof(CallerFrameAndPC)));
953 void callAndUnboxResult(WASMExpressionType returnType)
955 // regT0 holds callee.
957 store64(GPRInfo::regT0, Address(stackPointerRegister, JSStack::Callee * static_cast<int>(sizeof(Register)) - sizeof(CallerFrameAndPC)));
959 store32(GPRInfo::regT0, Address(stackPointerRegister, JSStack::Callee * static_cast<int>(sizeof(Register)) + PayloadOffset - sizeof(CallerFrameAndPC)));
960 store32(TrustedImm32(JSValue::CellTag), Address(stackPointerRegister, JSStack::Callee * static_cast<int>(sizeof(Register)) + TagOffset - sizeof(CallerFrameAndPC)));
963 DataLabelPtr addressOfLinkedFunctionCheck;
964 Jump slowCase = branchPtrWithPatch(NotEqual, GPRInfo::regT0, addressOfLinkedFunctionCheck, TrustedImmPtr(0));
966 CallLinkInfo* info = m_codeBlock->addCallLinkInfo();
967 info->setUpCall(CallLinkInfo::Call, CodeOrigin(), GPRInfo::regT0);
968 m_callCompilationInfo.append(CallCompilationInfo());
969 m_callCompilationInfo.last().hotPathBegin = addressOfLinkedFunctionCheck;
970 m_callCompilationInfo.last().callLinkInfo = info;
971 m_callCompilationInfo.last().hotPathOther = nearCall();
975 move(TrustedImmPtr(info), GPRInfo::regT2);
976 m_callCompilationInfo.last().callReturnLocation = emitNakedCall(m_vm->getCTIStub(linkCallThunkGenerator).code());
979 addPtr(TrustedImm32(-m_calleeSaveSpace - WTF::roundUpToMultipleOf(stackAlignmentRegisters(), m_stackHeight) * sizeof(StackSlot) - maxFrameExtentForSlowPathCall), GPRInfo::callFrameRegister, stackPointerRegister);
980 checkStackPointerAlignment();
982 switch (returnType) {
983 case WASMExpressionType::I32:
984 store32(GPRInfo::returnValueGPR, temporaryAddress(m_tempStackTop++));
986 case WASMExpressionType::Void:
989 ASSERT_NOT_REACHED();
994 void loadValueAndConvertToInt32(Address address, GPRReg dst)
996 JSValueRegs tempRegs(dst);
997 loadValue(address, tempRegs);
998 Jump checkJSInt32 = branchIfInt32(tempRegs);
1000 callOperation(operationConvertJSValueToInt32, dst, dst);
1002 checkJSInt32.link(this);
1005 void loadValueAndConvertToDouble(Address address, FPRReg dst, GPRReg scratch1, GPRReg scratch2)
1007 JSValueRegs tempRegs(scratch1);
1008 loadValue(address, tempRegs);
1009 Jump checkJSInt32 = branchIfInt32(tempRegs);
1010 Jump checkJSNumber = branchIfNumber(tempRegs, scratch2);
1013 callOperation(operationConvertJSValueToDouble, tempRegs.gpr(), dst);
1016 checkJSInt32.link(this);
1017 convertInt32ToDouble(tempRegs.gpr(), dst);
1020 checkJSNumber.link(this);
1021 unboxDoubleWithoutAssertions(tempRegs.gpr(), dst);
1025 void loadValueAndConvertToInt32(Address address, GPRReg dst, GPRReg scratch)
1027 JSValueRegs tempRegs(scratch, dst);
1028 loadValue(address, tempRegs);
1029 Jump checkJSInt32 = branchIfInt32(tempRegs);
1031 callOperation(operationConvertJSValueToInt32, tempRegs.tagGPR(), tempRegs.payloadGPR(), dst);
1033 checkJSInt32.link(this);
1036 void loadValueAndConvertToDouble(Address address, FPRReg dst, GPRReg scratch1, GPRReg scratch2, GPRReg scratch3, FPRReg fpScratch)
1038 JSValueRegs tempRegs(scratch2, scratch1);
1039 loadValue(address, tempRegs);
1040 Jump checkJSInt32 = branchIfInt32(tempRegs);
1041 Jump checkJSNumber = branchIfNumber(tempRegs, scratch3);
1044 callOperation(operationConvertJSValueToDouble, tempRegs.tagGPR(), tempRegs.payloadGPR(), dst);
1047 checkJSInt32.link(this);
1048 convertInt32ToDouble(tempRegs.payloadGPR(), dst);
1051 checkJSNumber.link(this);
1052 unboxDouble(tempRegs.tagGPR(), tempRegs.payloadGPR(), dst, fpScratch);
1057 JSWASMModule* m_module;
1058 unsigned m_stackHeight;
1059 unsigned m_numberOfLocals;
1060 unsigned m_tempStackTop { 0 };
1061 unsigned m_calleeSaveSpace;
1063 Vector<JumpTarget> m_breakTargets;
1064 Vector<JumpTarget> m_continueTargets;
1065 Vector<JumpTarget> m_breakLabelTargets;
1066 Vector<JumpTarget> m_continueLabelTargets;
1069 Jump m_stackOverflow;
1070 JumpList m_divideErrorJumpList;
1071 JumpList m_exceptionChecks;
1073 Vector<std::pair<Call, void*>> m_calls;
1074 Vector<CallCompilationInfo> m_callCompilationInfo;
1079 #endif // ENABLE(WEBASSEMBLY)
1081 #endif // WASMFunctionCompiler_h