Var CrossSite::ProfileThunk(RecyclableObject* callable, CallInfo callInfo, ...) { JavascriptFunction* function = JavascriptFunction::FromVar(callable); Assert(function->GetTypeId() == TypeIds_Function); Assert(function->GetEntryPoint() == CrossSite::ProfileThunk); RUNTIME_ARGUMENTS(args, callInfo); ScriptContext * scriptContext = function->GetScriptContext(); // It is not safe to access the function body if the script context is not alive. scriptContext->VerifyAliveWithHostContext(!function->IsExternal(), scriptContext->GetThreadContext()->GetPreviousHostScriptContext()); JavascriptMethod entryPoint; FunctionInfo *funcInfo = function->GetFunctionInfo(); TTD_XSITE_LOG(callable->GetScriptContext(), "DefaultOrProfileThunk", callable); #ifdef ENABLE_WASM if (WasmScriptFunction::Is(function)) { AsmJsFunctionInfo* asmInfo = funcInfo->GetFunctionBody()->GetAsmJsFunctionInfo(); Assert(asmInfo); if (asmInfo->IsWasmDeferredParse()) { entryPoint = WasmLibrary::WasmDeferredParseExternalThunk; } else { entryPoint = Js::AsmJsExternalEntryPoint; } } else #endif if (funcInfo->HasBody()) { #if ENABLE_DEBUG_CONFIG_OPTIONS char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE]; #endif entryPoint = ScriptFunction::FromVar(function)->GetEntryPointInfo()->jsMethod; if (funcInfo->IsDeferred() && scriptContext->IsProfiling()) { // if the current entrypoint is deferred parse we need to update it appropriately for the profiler mode. entryPoint = Js::ScriptContext::GetProfileModeThunk(entryPoint); } OUTPUT_TRACE(Js::ScriptProfilerPhase, _u("CrossSite::ProfileThunk FunctionNumber : %s, Entrypoint : 0x%08X\n"), funcInfo->GetFunctionProxy()->GetDebugNumberSet(debugStringBuffer), entryPoint); } else { entryPoint = ProfileEntryThunk; } return CommonThunk(function, entryPoint, args); }
Var BoxAsmJsReturnValue(ScriptFunction* func, int intRetVal, double doubleRetVal, float floatRetVal, __m128 simdRetVal) { // ExternalEntryPoint doesn't know the return value, so it will send garbage for everything except actual return type Var returnValue = nullptr; // make call and convert primitive type back to Var AsmJsFunctionInfo* info = func->GetFunctionBody()->GetAsmJsFunctionInfo(); switch (info->GetReturnType().which()) { case AsmJsRetType::Void: returnValue = JavascriptOperators::OP_LdUndef(func->GetScriptContext()); break; case AsmJsRetType::Signed:{ returnValue = JavascriptNumber::ToVar(intRetVal, func->GetScriptContext()); break; } case AsmJsRetType::Double:{ returnValue = JavascriptNumber::New(doubleRetVal, func->GetScriptContext()); break; } case AsmJsRetType::Float:{ returnValue = JavascriptNumber::New(floatRetVal, func->GetScriptContext()); break; } case AsmJsRetType::Float32x4: { X86SIMDValue simdVal; simdVal.m128_value = simdRetVal; returnValue = JavascriptSIMDFloat32x4::New(&X86SIMDValue::ToSIMDValue(simdVal), func->GetScriptContext()); break; } case AsmJsRetType::Int32x4: { X86SIMDValue simdVal; simdVal.m128_value = simdRetVal; returnValue = JavascriptSIMDInt32x4::New(&X86SIMDValue::ToSIMDValue(simdVal), func->GetScriptContext()); break; } case AsmJsRetType::Float64x2: { X86SIMDValue simdVal; simdVal.m128_value = simdRetVal; returnValue = JavascriptSIMDFloat64x2::New(&X86SIMDValue::ToSIMDValue(simdVal), func->GetScriptContext()); break; } default: Assume(UNREACHED); } return returnValue; }
int GetStackSizeForAsmJsUnboxing(ScriptFunction* func) { AsmJsFunctionInfo* info = func->GetFunctionBody()->GetAsmJsFunctionInfo(); int argSize = info->GetArgByteSize() + MachPtr; argSize = ::Math::Align<int32>(argSize, 16); if (argSize < 32) { argSize = 32; // convention is to always allocate spill space for rcx,rdx,r8,r9 } PROBE_STACK_CALL(func->GetScriptContext(), func, argSize + Js::Constants::MinStackDefault); return argSize; }
Var CrossSite::DefaultThunk(RecyclableObject* callable, CallInfo callInfo, ...) { JavascriptFunction* function = JavascriptFunction::FromVar(callable); Assert(function->GetTypeId() == TypeIds_Function); Assert(function->GetEntryPoint() == CrossSite::DefaultThunk); RUNTIME_ARGUMENTS(args, callInfo); // It is not safe to access the function body if the script context is not alive. function->GetScriptContext()->VerifyAliveWithHostContext(!function->IsExternal(), ThreadContext::GetContextForCurrentThread()->GetPreviousHostScriptContext()); JavascriptMethod entryPoint; FunctionInfo *funcInfo = function->GetFunctionInfo(); TTD_XSITE_LOG(callable->GetScriptContext(), "DefaultOrProfileThunk", callable); if (funcInfo->HasBody()) { #ifdef ASMJS_PLAT if (funcInfo->GetFunctionProxy()->IsFunctionBody() && funcInfo->GetFunctionBody()->GetIsAsmJsFunction()) { #ifdef ENABLE_WASM AsmJsFunctionInfo* asmInfo = funcInfo->GetFunctionBody()->GetAsmJsFunctionInfo(); if (asmInfo && asmInfo->IsWasmDeferredParse()) { entryPoint = WasmLibrary::WasmDeferredParseExternalThunk; } else #endif { entryPoint = Js::AsmJsExternalEntryPoint; } } else #endif { entryPoint = ScriptFunction::FromVar(function)->GetEntryPointInfo()->jsMethod; } } else { entryPoint = funcInfo->GetOriginalEntryPoint(); } return CommonThunk(function, entryPoint, args); }
int GetStackSizeForAsmJsUnboxing(ScriptFunction* func) { AsmJsFunctionInfo* info = func->GetFunctionBody()->GetAsmJsFunctionInfo(); int argSize = MachPtr; for (ArgSlot i = 0; i < info->GetArgCount(); i++) { if (info->GetArgType(i).isSIMD()) { argSize += sizeof(AsmJsSIMDValue); } else { argSize += MachPtr; } } argSize = ::Math::Align<int32>(argSize, 16); if (argSize < 32) { argSize = 32; // convention is to always allocate spill space for rcx,rdx,r8,r9 } PROBE_STACK_CALL(func->GetScriptContext(), func, argSize); return argSize; }
Var AsmJsExternalEntryPoint(RecyclableObject* entryObject, CallInfo callInfo, ...) { ARGUMENTS(args, callInfo); ScriptFunction* func = (ScriptFunction*)entryObject; FunctionBody* body = func->GetFunctionBody(); AsmJsFunctionInfo* info = body->GetAsmJsFunctionInfo(); ScriptContext* scriptContext = func->GetScriptContext(); const uint argInCount = callInfo.Count - 1; int argSize = info->GetArgByteSize(); char* dst; Var returnValue = 0; AsmJsModuleInfo::EnsureHeapAttached(func); argSize = ::Math::Align<int32>(argSize, 8); // Allocate stack space for args __asm { sub esp, argSize mov dst, esp }; // Unbox Var to primitive type { int32 intVal; double doubleVal; float floatVal; for (ArgSlot i = 0; i < info->GetArgCount(); i++) { if (info->GetArgType(i).isInt()) { if (i < argInCount) { intVal = JavascriptMath::ToInt32(args.Values[i + 1], scriptContext); } else { intVal = 0; } *(int32*)dst = intVal; dst += sizeof(int32); } else if (info->GetArgType(i).isFloat()) { if (i < argInCount) { floatVal = (float)(JavascriptConversion::ToNumber(args.Values[i + 1], scriptContext)); } else { floatVal = (float)(JavascriptNumber::NaN); } *(float*)dst = floatVal; dst += sizeof(float); } else if (info->GetArgType(i).isDouble()) { if (i < argInCount) { doubleVal = JavascriptConversion::ToNumber(args.Values[i + 1], scriptContext); } else { doubleVal = JavascriptNumber::NaN; } *(double*)dst = doubleVal; dst += sizeof(double); } else if (info->GetArgType(i).isSIMD()) { AsmJsVarType argType = info->GetArgType(i); AsmJsSIMDValue simdVal; // SIMD values are copied unaligned. // SIMD values cannot be implicitly coerced from/to other types. If the SIMD parameter is missing (i.e. Undefined), we throw type error since there is not equivalent SIMD value to coerce to. switch (argType.which()) { case AsmJsType::Int32x4: if (i >= argInCount || !JavascriptSIMDInt32x4::Is(args.Values[i + 1])) { JavascriptError::ThrowTypeError(scriptContext, JSERR_SimdInt32x4TypeMismatch, L"Int32x4"); } simdVal = ((JavascriptSIMDInt32x4*)(args.Values[i + 1]))->GetValue(); break; case AsmJsType::Float32x4: if (i >= argInCount || !JavascriptSIMDFloat32x4::Is(args.Values[i + 1])) { JavascriptError::ThrowTypeError(scriptContext, JSERR_SimdFloat32x4TypeMismatch, L"Float32x4"); } simdVal = ((JavascriptSIMDFloat32x4*)(args.Values[i + 1]))->GetValue(); break; case AsmJsType::Float64x2: if (i >= argInCount || !JavascriptSIMDFloat64x2::Is(args.Values[i + 1])) { JavascriptError::ThrowTypeError(scriptContext, JSERR_SimdFloat64x2TypeMismatch, L"Float64x2"); } simdVal = ((JavascriptSIMDFloat64x2*)(args.Values[i + 1]))->GetValue(); break; default: Assert(UNREACHED); } *(AsmJsSIMDValue*)dst = simdVal; dst += sizeof(AsmJsSIMDValue); } else { AssertMsg(UNREACHED, "Invalid function arg type."); } } } const void * asmJSEntryPoint = func->GetEntryPointInfo()->address; // make call and convert primitive type back to Var switch (info->GetReturnType().which()) { case AsmJsRetType::Void: __asm { mov ecx, asmJSEntryPoint #ifdef _CONTROL_FLOW_GUARD call[__guard_check_icall_fptr] #endif push func call ecx } returnValue = JavascriptOperators::OP_LdUndef(func->GetScriptContext()); break; case AsmJsRetType::Signed:{ int32 ival = 0; __asm { mov ecx, asmJSEntryPoint #ifdef _CONTROL_FLOW_GUARD call[__guard_check_icall_fptr] #endif push func call ecx mov ival, eax } returnValue = JavascriptNumber::ToVar(ival, func->GetScriptContext()); break; } case AsmJsRetType::Double:{ double dval = 0; __asm { mov ecx, asmJSEntryPoint #ifdef _CONTROL_FLOW_GUARD call[__guard_check_icall_fptr] #endif push func call ecx movsd dval, xmm0 } returnValue = JavascriptNumber::New(dval, func->GetScriptContext()); break; } case AsmJsRetType::Float:{ float fval = 0; __asm { mov ecx, asmJSEntryPoint #ifdef _CONTROL_FLOW_GUARD call[__guard_check_icall_fptr] #endif push func call ecx movss fval, xmm0 } returnValue = JavascriptNumber::New((double)fval, func->GetScriptContext()); break; } case AsmJsRetType::Int32x4: AsmJsSIMDValue simdVal; simdVal.Zero(); __asm { mov ecx, asmJSEntryPoint #ifdef _CONTROL_FLOW_GUARD call[__guard_check_icall_fptr] #endif push func call ecx movups simdVal, xmm0 } returnValue = JavascriptSIMDInt32x4::New(&simdVal, func->GetScriptContext()); break; case AsmJsRetType::Float32x4: simdVal.Zero(); __asm { mov ecx, asmJSEntryPoint #ifdef _CONTROL_FLOW_GUARD call[__guard_check_icall_fptr] #endif push func call ecx movups simdVal, xmm0 } returnValue = JavascriptSIMDFloat32x4::New(&simdVal, func->GetScriptContext()); break; case AsmJsRetType::Float64x2: simdVal.Zero(); __asm { mov ecx, asmJSEntryPoint #ifdef _CONTROL_FLOW_GUARD call[__guard_check_icall_fptr] #endif push func call ecx movups simdVal, xmm0 } returnValue = JavascriptSIMDFloat64x2::New(&simdVal, func->GetScriptContext()); break; default: Assume(UNREACHED); } return returnValue; }
void * UnboxAsmJsArguments(ScriptFunction* func, Var * origArgs, char * argDst, CallInfo callInfo) { void * address = func->GetEntryPointInfo()->address; Assert(address); AsmJsFunctionInfo* info = func->GetFunctionBody()->GetAsmJsFunctionInfo(); ScriptContext* scriptContext = func->GetScriptContext(); AsmJsModuleInfo::EnsureHeapAttached(func); uint actualArgCount = callInfo.Count - 1; // -1 for ScriptFunction argDst = argDst + MachPtr; // add one first so as to skip the ScriptFunction argument for (ArgSlot i = 0; i < info->GetArgCount(); i++) { if (info->GetArgType(i).isInt()) { int32 intVal; if (i < actualArgCount) { intVal = JavascriptMath::ToInt32(*origArgs, scriptContext); } else { intVal = 0; } *(int64*)(argDst) = 0; *(int32*)argDst = intVal; argDst = argDst + MachPtr; } else if (info->GetArgType(i).isFloat()) { float floatVal; if (i < actualArgCount) { floatVal = (float)(JavascriptConversion::ToNumber(*origArgs, scriptContext)); } else { floatVal = (float)(JavascriptNumber::NaN); } *(int64*)(argDst) = 0; *(float*)argDst = floatVal; argDst = argDst + MachPtr; } else if (info->GetArgType(i).isDouble()) { double doubleVal; if (i < actualArgCount) { doubleVal = JavascriptConversion::ToNumber(*origArgs, scriptContext); } else { doubleVal = JavascriptNumber::NaN; } *(int64*)(argDst) = 0; *(double*)argDst = doubleVal; argDst = argDst + MachPtr; } else if (info->GetArgType(i).isSIMD()) { AsmJsVarType argType = info->GetArgType(i); AsmJsSIMDValue simdVal = { 0, 0, 0, 0 }; // SIMD values are copied unaligned. // SIMD values cannot be implicitly coerced from/to other types. If the SIMD parameter is missing (i.e. Undefined), we throw type error since there is not equivalent SIMD value to coerce to. switch (argType.which()) { case AsmJsType::Int32x4: if (!JavascriptSIMDInt32x4::Is(*origArgs)) { JavascriptError::ThrowTypeError(scriptContext, JSERR_SimdInt32x4TypeMismatch, L"Int32x4"); } simdVal = ((JavascriptSIMDInt32x4*)(*origArgs))->GetValue(); break; case AsmJsType::Float32x4: if (!JavascriptSIMDFloat32x4::Is(*origArgs)) { JavascriptError::ThrowTypeError(scriptContext, JSERR_SimdFloat32x4TypeMismatch, L"Float32x4"); } simdVal = ((JavascriptSIMDFloat32x4*)(*origArgs))->GetValue(); break; case AsmJsType::Float64x2: if (!JavascriptSIMDFloat64x2::Is(*origArgs)) { JavascriptError::ThrowTypeError(scriptContext, JSERR_SimdFloat64x2TypeMismatch, L"Float64x2"); } simdVal = ((JavascriptSIMDFloat64x2*)(*origArgs))->GetValue(); break; default: Assert(UNREACHED); } *(AsmJsSIMDValue*)argDst = simdVal; argDst = argDst + sizeof(AsmJsSIMDValue); } ++origArgs; } // for convenience, lets take the opportunity to return the asm.js entrypoint address return address; }
// returns an array containing the size of each argument uint *GetArgsSizesArray(ScriptFunction* func) { AsmJsFunctionInfo* info = func->GetFunctionBody()->GetAsmJsFunctionInfo(); return info->GetArgsSizesArray(); }
void* AsmJsEncoder::Encode( FunctionBody* functionBody ) { Assert( functionBody ); mFunctionBody = functionBody; #if DBG_DUMP AsmJsJitTemplate::Globals::CurrentEncodingFunction = mFunctionBody; #endif AsmJsFunctionInfo* asmInfo = functionBody->GetAsmJsFunctionInfo(); FunctionEntryPointInfo* entryPointInfo = ((FunctionEntryPointInfo*)(functionBody->GetDefaultEntryPointInfo())); // number of var on the stack + ebp + eip mIntOffset = asmInfo->GetIntByteOffset() + GetOffset<Var>(); mDoubleOffset = asmInfo->GetDoubleByteOffset() + GetOffset<Var>(); mFloatOffset = asmInfo->GetFloatByteOffset() + GetOffset<Var>(); mSimdOffset = asmInfo->GetSimdByteOffset() + GetOffset<Var>(); NoRecoverMemoryArenaAllocator localAlloc(_u("BE-AsmJsEncoder"), GetPageAllocator(), Js::Throw::OutOfMemory); mLocalAlloc = &localAlloc; mRelocLabelMap = Anew( mLocalAlloc, RelocLabelMap, mLocalAlloc ); mTemplateData = AsmJsJitTemplate::InitTemplateData(); mEncodeBufferSize = GetEncodeBufferSize(functionBody); mEncodeBuffer = AnewArray((&localAlloc), BYTE, mEncodeBufferSize); mPc = mEncodeBuffer; mReader.Create( functionBody ); ip = mReader.GetIP(); #ifdef ENABLE_DEBUG_CONFIG_OPTIONS if( PHASE_TRACE( Js::AsmjsEncoderPhase, mFunctionBody ) ) { Output::Print( _u("\n\n") ); functionBody->DumpFullFunctionName(); Output::Print( _u("\n StackSize = %d , Offsets: Var = %d, Int = %d, Double = %d\n"), mFunctionBody->GetAsmJsFunctionInfo()->GetTotalSizeinBytes(), GetOffset<Var>(), GetOffset<int>(), GetOffset<double>() ); } #endif AsmJsJitTemplate::FunctionEntry::ApplyTemplate( this, mPc ); while( ReadOp() ){} AsmJsJitTemplate::FunctionExit::ApplyTemplate( this, mPc ); AsmJsJitTemplate::FreeTemplateData( mTemplateData ); #if DBG_DUMP AsmJsJitTemplate::Globals::CurrentEncodingFunction = nullptr; #endif ApplyRelocs(); ptrdiff_t codeSize = mPc - mEncodeBuffer; if( codeSize > 0 ) { Assert( ::Math::FitsInDWord( codeSize ) ); BYTE *buffer; EmitBufferAllocation *allocation = GetCodeGenAllocator()->emitBufferManager.AllocateBuffer( codeSize, &buffer, 0, 0 ); functionBody->GetAsmJsFunctionInfo()->mTJBeginAddress = buffer; if (buffer == nullptr) { Js::Throw::OutOfMemory(); } if (!GetCodeGenAllocator()->emitBufferManager.CommitBuffer(allocation, buffer, codeSize, mEncodeBuffer)) { Js::Throw::OutOfMemory(); } functionBody->GetScriptContext()->GetThreadContext()->SetValidCallTargetForCFG(buffer); // TODO: improve this once EntryPoint cleanup work is complete! #if 0 const char16 *const functionName = functionBody->GetDisplayName(); const char16 *const suffix = _u("TJ"); char16 functionNameArray[256]; const size_t functionNameCharLength = functionBody->GetDisplayNameLength(); wcscpy_s(functionNameArray, 256, functionName); wcscpy_s(&functionNameArray[functionNameCharLength], 256 - functionNameCharLength, suffix); #endif JS_ETW(EventWriteMethodLoad(functionBody->GetScriptContext(), (void *)buffer, codeSize, EtwTrace::GetFunctionId(functionBody), 0 /* methodFlags - for future use*/, MethodType_Jit, EtwTrace::GetSourceId(functionBody), functionBody->GetLineNumber(), functionBody->GetColumnNumber(), functionBody->GetDisplayName())); entryPointInfo->SetTJCodeGenDone(); // set the codegen to done state for TJ entryPointInfo->SetCodeSize(codeSize); return buffer; } return nullptr; }
Var AsmJsExternalEntryPoint(RecyclableObject* entryObject, CallInfo callInfo, ...) { ARGUMENTS(args, callInfo); ScriptFunction* func = (ScriptFunction*)entryObject; FunctionBody* body = func->GetFunctionBody(); AsmJsFunctionInfo* info = body->GetAsmJsFunctionInfo(); int argSize = info->GetArgByteSize(); void* dst; Var returnValue = 0; argSize = ::Math::Align<int32>(argSize, 8); // Allocate stack space for args PROBE_STACK_CALL(func->GetScriptContext(), func, argSize + Js::Constants::MinStackDefault); dst = _alloca(argSize); const void * asmJSEntryPoint = UnboxAsmJsArguments(func, args.Values + 1, ((char*)dst) - MachPtr, callInfo); // make call and convert primitive type back to Var switch (info->GetReturnType().which()) { case AsmJsRetType::Void: __asm { mov ecx, asmJSEntryPoint #ifdef _CONTROL_FLOW_GUARD call[__guard_check_icall_fptr] #endif push func call ecx } returnValue = JavascriptOperators::OP_LdUndef(func->GetScriptContext()); break; case AsmJsRetType::Signed:{ int32 ival = 0; __asm { mov ecx, asmJSEntryPoint #ifdef _CONTROL_FLOW_GUARD call[__guard_check_icall_fptr] #endif push func call ecx mov ival, eax } returnValue = JavascriptNumber::ToVar(ival, func->GetScriptContext()); break; } case AsmJsRetType::Int64: { int32 iLow = 0, iHigh = 0; __asm { mov ecx, asmJSEntryPoint #ifdef _CONTROL_FLOW_GUARD call[__guard_check_icall_fptr] #endif push func call ecx mov iLow, eax; mov iHigh, edx; } #if ENABLE_DEBUG_CONFIG_OPTIONS if (CONFIG_FLAG(WasmI64)) { uint64 lHigh = ((uint64)iHigh) << 32; uint64 lLow = (uint64)(uint32)iLow; returnValue = CreateI64ReturnObject((int64)(lHigh | lLow), func->GetScriptContext()); break; } #endif JavascriptError::ThrowTypeError(func->GetScriptContext(), WASMERR_InvalidTypeConversion); } case AsmJsRetType::Double:{ double dval = 0; __asm { mov ecx, asmJSEntryPoint #ifdef _CONTROL_FLOW_GUARD call[__guard_check_icall_fptr] #endif push func call ecx movsd dval, xmm0 } returnValue = JavascriptNumber::NewWithCheck(dval, func->GetScriptContext()); break; } case AsmJsRetType::Float:{ float fval = 0; __asm { mov ecx, asmJSEntryPoint #ifdef _CONTROL_FLOW_GUARD call[__guard_check_icall_fptr] #endif push func call ecx movss fval, xmm0 } returnValue = JavascriptNumber::NewWithCheck((double)fval, func->GetScriptContext()); break; } #ifdef ENABLE_WASM_SIMD case AsmJsRetType::Bool32x4: case AsmJsRetType::Bool16x8: case AsmJsRetType::Bool8x16: case AsmJsRetType::Float32x4: case AsmJsRetType::Float64x2: case AsmJsRetType::Int64x2: case AsmJsRetType::Int32x4: case AsmJsRetType::Int16x8: case AsmJsRetType::Int8x16: case AsmJsRetType::Uint32x4: case AsmJsRetType::Uint16x8: case AsmJsRetType::Uint8x16: AsmJsSIMDValue simdVal; simdVal.Zero(); __asm { mov ecx, asmJSEntryPoint #ifdef _CONTROL_FLOW_GUARD call[__guard_check_icall_fptr] #endif push func call ecx movups simdVal, xmm0 } // Todo:: support test return object (like int64) for wasm.simd JavascriptError::ThrowTypeError(func->GetScriptContext(), WASMERR_InvalidTypeConversion); break; #endif default: Assume(UNREACHED); JavascriptError::ThrowTypeError(func->GetScriptContext(), WASMERR_InvalidTypeConversion); } return returnValue; }
Var BoxAsmJsReturnValue(ScriptFunction* func, int64 intRetVal, double doubleRetVal, float floatRetVal, __m128 simdRetVal) { // ExternalEntryPoint doesn't know the return value, so it will send garbage for everything except actual return type Var returnValue = nullptr; // make call and convert primitive type back to Var AsmJsFunctionInfo* info = func->GetFunctionBody()->GetAsmJsFunctionInfo(); ScriptContext* scriptContext = func->GetScriptContext(); switch (info->GetReturnType().which()) { case AsmJsRetType::Void: returnValue = JavascriptOperators::OP_LdUndef(scriptContext); break; case AsmJsRetType::Signed: { returnValue = JavascriptNumber::ToVar((int)intRetVal, scriptContext); break; } case AsmJsRetType::Int64: { #if ENABLE_DEBUG_CONFIG_OPTIONS if (CONFIG_FLAG(WasmI64)) { returnValue = CreateI64ReturnObject(intRetVal, scriptContext); break; } #endif JavascriptError::ThrowTypeError(scriptContext, WASMERR_InvalidTypeConversion); } case AsmJsRetType::Double: { returnValue = JavascriptNumber::NewWithCheck(doubleRetVal, scriptContext); break; } case AsmJsRetType::Float: { returnValue = JavascriptNumber::NewWithCheck(floatRetVal, scriptContext); break; } #ifdef ENABLE_WASM_SIMD case AsmJsRetType::Bool32x4: case AsmJsRetType::Bool16x8: case AsmJsRetType::Bool8x16: case AsmJsRetType::Float64x2: case AsmJsRetType::Float32x4: case AsmJsRetType::Int64x2: case AsmJsRetType::Int32x4: case AsmJsRetType::Int16x8: case AsmJsRetType::Int8x16: case AsmJsRetType::Uint32x4: case AsmJsRetType::Uint16x8: case AsmJsRetType::Uint8x16: // Todo:: support test return object (like int64) for wasm.simd JavascriptError::ThrowTypeError(scriptContext, WASMERR_InvalidTypeConversion); #endif default: Assume(UNREACHED); JavascriptError::ThrowTypeError(scriptContext, WASMERR_InvalidTypeConversion); } return returnValue; }
void * UnboxAsmJsArguments(ScriptFunction* func, Var * origArgs, char * argDst, CallInfo callInfo) { void * address = reinterpret_cast<void*>(func->GetEntryPointInfo()->jsMethod); Assert(address); AsmJsFunctionInfo* info = func->GetFunctionBody()->GetAsmJsFunctionInfo(); ScriptContext* scriptContext = func->GetScriptContext(); #if ENABLE_DEBUG_CONFIG_OPTIONS bool allowTestInputs = CONFIG_FLAG(WasmI64); #endif ArgumentReader reader(&callInfo, origArgs); uint actualArgCount = reader.Info.Count - 1; // -1 for ScriptFunction argDst = argDst + MachPtr; // add one first so as to skip the ScriptFunction argument for (ArgSlot i = 0; i < info->GetArgCount(); i++) { if (info->GetArgType(i).isInt()) { int32 intVal; if (i < actualArgCount) { #if ENABLE_DEBUG_CONFIG_OPTIONS if (allowTestInputs && JavascriptString::Is(*origArgs)) { intVal = (int32)ConvertStringToInt64(*origArgs, scriptContext); } else #endif intVal = JavascriptMath::ToInt32(*origArgs, scriptContext); } else { intVal = 0; } #if TARGET_64 *(int64*)(argDst) = 0; #endif *(int32*)argDst = intVal; argDst = argDst + MachPtr; } else if (info->GetArgType(i).isInt64()) { #if ENABLE_DEBUG_CONFIG_OPTIONS if (!allowTestInputs) #endif { JavascriptError::ThrowTypeError(scriptContext, WASMERR_InvalidTypeConversion); } #if ENABLE_DEBUG_CONFIG_OPTIONS int64 val; if (i < actualArgCount) { if (JavascriptString::Is(*origArgs)) { val = ConvertStringToInt64(*origArgs, scriptContext); } else if (JavascriptObject::Is(*origArgs)) { RecyclableObject* object = RecyclableObject::FromVar(*origArgs); PropertyRecord const * lowPropRecord = nullptr; PropertyRecord const * highPropRecord = nullptr; scriptContext->GetOrAddPropertyRecord(_u("low"), (int)wcslen(_u("low")), &lowPropRecord); scriptContext->GetOrAddPropertyRecord(_u("high"), (int)wcslen(_u("high")), &highPropRecord); Var low = JavascriptOperators::OP_GetProperty(object, lowPropRecord->GetPropertyId(), scriptContext); Var high = JavascriptOperators::OP_GetProperty(object, highPropRecord->GetPropertyId(), scriptContext); uint64 lowVal = JavascriptMath::ToInt32(low, scriptContext); uint64 highVal = JavascriptMath::ToInt32(high, scriptContext); val = (highVal << 32) | (lowVal & 0xFFFFFFFF); } else { int32 intVal = JavascriptMath::ToInt32(*origArgs, scriptContext); val = (int64)intVal; } } else { val = 0; } *(int64*)(argDst) = val; argDst += sizeof(int64); #endif } else if (info->GetArgType(i).isFloat()) { float floatVal; if (i < actualArgCount) { #if ENABLE_DEBUG_CONFIG_OPTIONS if (allowTestInputs && JavascriptString::Is(*origArgs)) { int32 val = (int32)ConvertStringToInt64(*origArgs, scriptContext); floatVal = *(float*)&val; } else #endif floatVal = (float)(JavascriptConversion::ToNumber(*origArgs, scriptContext)); } else { floatVal = (float)(JavascriptNumber::NaN); } #if TARGET_64 *(int64*)(argDst) = 0; #endif *(float*)argDst = floatVal; argDst = argDst + MachPtr; } else if (info->GetArgType(i).isDouble()) { double doubleVal; if (i < actualArgCount) { #if ENABLE_DEBUG_CONFIG_OPTIONS if (allowTestInputs && JavascriptString::Is(*origArgs)) { int64 val = ConvertStringToInt64(*origArgs, scriptContext); doubleVal = *(double*)&val; } else #endif doubleVal = JavascriptConversion::ToNumber(*origArgs, scriptContext); } else { doubleVal = JavascriptNumber::NaN; } *(double*)argDst = doubleVal; argDst = argDst + sizeof(double); } else if (info->GetArgType(i).isSIMD()) { // Todo:: support test input for wasm.simd JavascriptError::ThrowTypeError(scriptContext, WASMERR_InvalidTypeConversion); } else { Assert(UNREACHED); JavascriptError::ThrowTypeError(scriptContext, WASMERR_InvalidTypeConversion); } ++origArgs; } AsmJsModuleInfo::EnsureHeapAttached(func); // for convenience, lets take the opportunity to return the asm.js entrypoint address return address; }