// internal void asCScriptFunction::JITCompile() { if( funcType != asFUNC_SCRIPT ) return; asASSERT( scriptData ); asIJITCompiler *jit = engine->GetJITCompiler(); if( !jit ) return; // Make sure the function has been compiled with JitEntry instructions // For functions that has JitEntry this will be a quick test asUINT length; asDWORD *byteCode = GetByteCode(&length); asDWORD *end = byteCode + length; bool foundJitEntry = false; while( byteCode < end ) { // Determine the instruction asEBCInstr op = asEBCInstr(*(asBYTE*)byteCode); if( op == asBC_JitEntry ) { foundJitEntry = true; break; } // Move to next instruction byteCode += asBCTypeSize[asBCInfo[op].type]; } if( !foundJitEntry ) { asCString msg; msg.Format(TXT_NO_JIT_IN_FUNC_s, GetDeclaration()); engine->WriteMessage("", 0, 0, asMSGTYPE_WARNING, msg.AddressOf()); } // Release the previous function, if any if( scriptData->jitFunction ) { engine->jitCompiler->ReleaseJITFunction(scriptData->jitFunction); scriptData->jitFunction = 0; } // Compile for native system int r = jit->CompileFunction(this, &scriptData->jitFunction); if( r < 0 ) asASSERT( scriptData->jitFunction == 0 ); }
Js::FunctionInfo *InliningDecider::Inline(Js::FunctionBody *const inliner, Js::FunctionInfo* functionInfo, bool isConstructorCall, bool isPolymorphicCall, uint16 constantArgInfo, Js::ProfileId callSiteId, uint recursiveInlineDepth, bool allowRecursiveInlining) { #if defined(DBG_DUMP) || defined(ENABLE_DEBUG_CONFIG_OPTIONS) char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE]; char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE]; #endif Js::FunctionProxy * proxy = functionInfo->GetFunctionProxy(); if (proxy && proxy->IsFunctionBody()) { if (isLoopBody && PHASE_OFF(Js::InlineInJitLoopBodyPhase, this->topFunc)) { INLINE_TESTTRACE_VERBOSE(_u("INLINING: Skip Inline: Jit loop body: %s (%s)\n"), this->topFunc->GetDisplayName(), this->topFunc->GetDebugNumberSet(debugStringBuffer)); return nullptr; } // Note: disable inline for debugger, as we can't bailout at return from function. // Alternative can be generate this bailout as part of inline, which can be done later as perf improvement. const auto inlinee = proxy->GetFunctionBody(); Assert(this->jitMode == ExecutionMode::FullJit); if (PHASE_OFF(Js::InlinePhase, inlinee) || PHASE_OFF(Js::GlobOptPhase, inlinee) || !ContinueInliningUserDefinedFunctions(this->bytecodeInlinedCount) || this->isInDebugMode) { return nullptr; } if (functionInfo->IsDeferred() || inlinee->GetByteCode() == nullptr) { // DeferredParse... INLINE_TESTTRACE(_u("INLINING: Skip Inline: No bytecode\tInlinee: %s (%s)\tCaller: %s (%s)\n"), inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2)); return nullptr; } if (inlinee->GetHasTry()) { INLINE_TESTTRACE(_u("INLINING: Skip Inline: Has try\tInlinee: %s (%s)\tCaller: %s (%s)\n"), inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2)); return nullptr; } // This is a hard limit as the argOuts array is statically sized. if (inlinee->GetInParamsCount() > Js::InlineeCallInfo::MaxInlineeArgoutCount) { INLINE_TESTTRACE(_u("INLINING: Skip Inline: Params count greater then MaxInlineeArgoutCount\tInlinee: %s (%s)\tParamcount: %d\tMaxInlineeArgoutCount: %d\tCaller: %s (%s)\n"), inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inlinee->GetInParamsCount(), Js::InlineeCallInfo::MaxInlineeArgoutCount, inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2)); return nullptr; } if (inlinee->GetInParamsCount() == 0) { // Inline candidate has no params, not even a this pointer. This can only be the global function, // which we shouldn't inline. INLINE_TESTTRACE(_u("INLINING: Skip Inline: Params count is zero!\tInlinee: %s (%s)\tParamcount: %d\tCaller: %s (%s)\n"), inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inlinee->GetInParamsCount(), inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2)); return nullptr; } if (inlinee->GetDontInline()) { INLINE_TESTTRACE(_u("INLINING: Skip Inline: Do not inline\tInlinee: %s (%s)\tCaller: %s (%s)\n"), inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2)); return nullptr; } // Do not inline a call to a class constructor if it isn't part of a new expression since the call will throw a TypeError anyway. if (inlinee->IsClassConstructor() && !isConstructorCall) { INLINE_TESTTRACE(_u("INLINING: Skip Inline: Class constructor without new keyword\tInlinee: %s (%s)\tCaller: %s (%s)\n"), inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2)); return nullptr; } if (!DeciderInlineIntoInliner(inlinee, inliner, isConstructorCall, isPolymorphicCall, constantArgInfo, recursiveInlineDepth, allowRecursiveInlining)) { return nullptr; } #if defined(ENABLE_DEBUG_CONFIG_OPTIONS) TraceInlining(inliner, inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inlinee->GetByteCodeCount(), this->topFunc, this->bytecodeInlinedCount, inlinee, callSiteId, this->isLoopBody); #endif this->bytecodeInlinedCount += inlinee->GetByteCodeCount(); return inlinee; } Js::OpCode builtInInlineCandidateOpCode; ValueType builtInReturnType; GetBuiltInInfo(functionInfo, &builtInInlineCandidateOpCode, &builtInReturnType); if(builtInInlineCandidateOpCode == 0 && builtInReturnType.IsUninitialized()) { return nullptr; } Assert(this->jitMode == ExecutionMode::FullJit); if (builtInInlineCandidateOpCode != 0 && ( PHASE_OFF(Js::InlinePhase, inliner) || PHASE_OFF(Js::GlobOptPhase, inliner) || isConstructorCall )) { return nullptr; } // Note: for built-ins at this time we don't have enough data (the instr) to decide whether it's going to be inlined. return functionInfo; }
int Compile(KIMLBYTE *&code, KIMLUINT &codeSize) { auto compiler = new Compiler(); if (!compiler) { showError("Cannot initialize compiler service.\n"); return RET_FAIL_COMPILER; } std::vector<std::string> files; for (auto it = includeFiles.begin(); it != includeFiles.end(); ++it) { files.push_back(*it); } files.push_back(inputfile); bool failed = false; FILE *fp; for (auto it = files.begin(); it != files.end(); ++it) { fp = fopen(it->c_str(), "r"); if (!fp) { showError("Cannot open: %s.\n", it->c_str()); delete compiler; return RET_FAIL_IO; } if (!compiler->Parse(it->c_str(), fp)) { failed = true; } fclose(fp); } if (failed) { showError("*** Parsing failed.\n"); delete compiler; return RET_FAIL_COMPILER; } if (!compiler->Compile(hasDebugInfo)) { auto &errors = compiler->GetErrors(); for (unsigned int i = 0, c = errors.size(); i < c; ++i) { auto &error = errors[i]; KIMLCSTRING msg = compileErrorMessages[error.code]; showError("%s[%u]: %s\n", files[error.compileUnitIndex].c_str(), error.line, msg); } delete compiler; return RET_FAIL_COMPILER; } if (!compiler->Finish()) { showError("*** Failed to finish compilation.\n"); delete compiler; return RET_FAIL_COMPILER; } if (!compiler->GetByteCode(&code, &codeSize)) { showError("*** Failed to export bytecode.\n"); delete compiler; return RET_FAIL_COMPILER; } delete compiler; return RET_OK; }