blob: a7be6f3a64fbeb5200c5661d33c2ee0671a6f26e [file] [log] [blame]
/*
* Copyright (C) 2016-2024 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. ``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
* 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.
*/
#include "config.h"
#include "WasmBBQPlan.h"
#if ENABLE(WEBASSEMBLY_BBQJIT)
#include "JITCompilation.h"
#include "JSToWasm.h"
#include "LinkBuffer.h"
#include "NativeCalleeRegistry.h"
#include "WasmBBQJIT.h"
#include "WasmCallee.h"
#include "WasmCalleeGroup.h"
#include "WasmCompilationContext.h"
#include "WasmFaultSignalHandler.h"
#include "WasmIRGeneratorHelpers.h"
#include "WasmTierUpCount.h"
#include "WasmTypeDefinitionInlines.h"
#include <wtf/DataLog.h>
#include <wtf/Locker.h>
#include <wtf/StdLibExtras.h>
#include <wtf/text/MakeString.h>
namespace JSC { namespace Wasm {
namespace WasmBBQPlanInternal {
static constexpr bool verbose = false;
}
BBQPlan::BBQPlan(VM& vm, Ref<ModuleInformation>&& moduleInformation, FunctionCodeIndex functionIndex, Ref<IPIntCallee>&& profiledCallee, Ref<Module>&& module, Ref<CalleeGroup>&& calleeGroup, CompletionTask&& completionTask)
: Plan(vm, WTF::move(moduleInformation), WTF::move(completionTask))
, m_profiledCallee(WTF::move(profiledCallee))
, m_module(WTF::move(module))
, m_calleeGroup(WTF::move(calleeGroup))
, m_functionIndex(functionIndex)
{
ASSERT(Options::useBBQJIT());
setMode(m_calleeGroup->mode());
Wasm::activateSignalingMemory();
dataLogLnIf(WasmBBQPlanInternal::verbose, "Starting BBQ plan for ", functionIndex);
}
FunctionAllowlist& BBQPlan::ensureGlobalBBQAllowlist()
{
static LazyNeverDestroyed<FunctionAllowlist> bbqAllowlist;
static std::once_flag initializeAllowlistFlag;
std::call_once(initializeAllowlistFlag, [] {
const char* functionAllowlistFile = Options::bbqAllowlist();
bbqAllowlist.construct(functionAllowlistFile);
});
return bbqAllowlist;
}
bool BBQPlan::dumpDisassembly(CompilationContext& context, LinkBuffer& linkBuffer, const TypeDefinition& signature, FunctionSpaceIndex functionIndexSpace)
{
if (shouldDumpDisassemblyFor(CompilationMode::BBQMode)) [[unlikely]] {
dataLogLn("Generated BBQ functionIndexSpace:(", functionIndexSpace, "),sig:(", signature.toString().ascii().data(), "),name:(", m_profiledCallee->nameWithHash(), "),wasmSize:(", m_moduleInformation->functionWasmSizeImportSpace(functionIndexSpace), ")");
if (context.bbqDisassembler)
context.bbqDisassembler->dump(linkBuffer);
linkBuffer.didAlreadyDisassemble();
return true;
}
return false;
}
void BBQPlan::work()
{
ASSERT(m_calleeGroup->runnable());
CompilationContext context;
Vector<UnlinkedWasmToWasmCall> unlinkedWasmToWasmCalls;
FunctionSpaceIndex functionIndexSpace = m_moduleInformation->toSpaceIndex(m_functionIndex);
TypeIndex typeIndex = m_moduleInformation->internalFunctionTypeIndices[m_functionIndex];
const TypeDefinition& signature = TypeInformation::get(typeIndex).expand();
Ref<BBQCallee> callee = BBQCallee::create(functionIndexSpace, m_moduleInformation->nameSection->get(functionIndexSpace), Ref { m_profiledCallee });
std::unique_ptr<InternalFunction> function = compileFunction(m_functionIndex, callee.get(), context, unlinkedWasmToWasmCalls);
LinkBuffer linkBuffer(*context.wasmEntrypointJIT, callee.ptr(), LinkBuffer::Profile::WasmBBQ, JITCompilationCanFail);
if (linkBuffer.didFailToAllocate()) [[unlikely]] {
fail(makeString("Out of executable memory while tiering up function at index "_s, m_functionIndex.rawIndex()), CompilationError::OutOfMemory);
return;
}
Vector<CodeLocationLabel<ExceptionHandlerPtrTag>> exceptionHandlerLocations;
Vector<CodeLocationLabel<WasmEntryPtrTag>> loopEntrypointLocations;
computeExceptionHandlerAndLoopEntrypointLocations(exceptionHandlerLocations, loopEntrypointLocations, function.get(), context, linkBuffer);
if (context.pcToCodeOriginMapBuilder)
context.pcToCodeOriginMap = Box<PCToCodeOriginMap>::create(WTF::move(*context.pcToCodeOriginMapBuilder), linkBuffer);
bool alreadyDumped = dumpDisassembly(context, linkBuffer, signature, functionIndexSpace);
function->entrypoint.compilation = makeUnique<Compilation>(
FINALIZE_CODE_IF((!alreadyDumped && shouldDumpDisassemblyFor(CompilationMode::BBQMode)), linkBuffer, JITCompilationPtrTag, nullptr, "BBQ functionIndexSpace:(", functionIndexSpace, "),sig:(", signature.toString().ascii().data(), "),name:(", callee->nameWithHash().ascii().data(), "),wasmSize:(", m_moduleInformation->functionWasmSizeImportSpace(functionIndexSpace), ")"),
WTF::move(context.wasmEntrypointByproducts));
CodePtr<WasmEntryPtrTag> entrypoint;
std::optional<CodeLocationLabel<WasmEntryPtrTag>> sharedLoopEntrypoint;
if (function->bbqSharedLoopEntrypoint)
sharedLoopEntrypoint = linkBuffer.locationOf<WasmEntryPtrTag>(*function->bbqSharedLoopEntrypoint);
{
callee->setEntrypoint(WTF::move(function->entrypoint), WTF::move(unlinkedWasmToWasmCalls), WTF::move(function->stackmaps), WTF::move(function->exceptionHandlers), WTF::move(exceptionHandlerLocations), WTF::move(loopEntrypointLocations), sharedLoopEntrypoint, function->osrEntryScratchBufferSize);
entrypoint = callee->entrypoint();
if (context.pcToCodeOriginMap)
NativeCalleeRegistry::singleton().addPCToCodeOriginMap(callee.ptr(), WTF::move(context.pcToCodeOriginMap));
{
Locker locker { m_calleeGroup->m_lock };
m_calleeGroup->installOptimizedCallee(locker, m_moduleInformation, m_functionIndex, callee.copyRef(), function->outgoingJITDirectCallees);
}
{
Locker locker { m_profiledCallee->tierUpCounter().m_lock };
m_profiledCallee->tierUpCounter().setCompilationStatus(mode(), IPIntTierUpCounter::CompilationStatus::Compiled);
}
}
dataLogLnIf(WasmBBQPlanInternal::verbose, "Finished BBQ ", m_functionIndex);
Locker locker { m_lock };
complete();
}
std::unique_ptr<InternalFunction> BBQPlan::compileFunction(FunctionCodeIndex functionIndex, BBQCallee& callee, CompilationContext& context, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls)
{
const auto& function = m_moduleInformation->functions[functionIndex];
TypeIndex typeIndex = m_moduleInformation->internalFunctionTypeIndices[functionIndex];
const TypeDefinition& signature = TypeInformation::get(typeIndex).expand();
FunctionSpaceIndex functionIndexSpace = m_moduleInformation->toSpaceIndex(functionIndex);
ASSERT_UNUSED(functionIndexSpace, m_moduleInformation->typeIndexFromFunctionIndexSpace(functionIndexSpace) == typeIndex);
Expected<std::unique_ptr<InternalFunction>, String> parseAndCompileResult;
beginCompilerSignpost(callee);
RELEASE_ASSERT(mode() == m_calleeGroup->mode());
parseAndCompileResult = parseAndCompileBBQ(context, m_profiledCallee.get(), callee, function, signature, unlinkedWasmToWasmCalls, m_module.get(), m_calleeGroup.get(), m_moduleInformation.get(), m_mode, functionIndex);
endCompilerSignpost(callee);
if (!parseAndCompileResult) [[unlikely]] {
fail(makeString(parseAndCompileResult.error(), ", in function at index "_s, functionIndex.rawIndex()), CompilationError::Parse); // FIXME: make this an Expected.
return nullptr;
}
return WTF::move(*parseAndCompileResult);
}
void BBQPlan::fail(String&& errorMessage, CompilationError error)
{
{
Locker locker { m_lock };
if (!m_errorMessage) {
// Multiple compiles could fail simultaneously. We arbitrarily choose the first.
Base::fail(WTF::move(errorMessage), error);
}
}
{
Locker locker { m_profiledCallee->tierUpCounter().m_lock };
m_profiledCallee->tierUpCounter().setCompilationStatus(mode(), IPIntTierUpCounter::CompilationStatus::Failed);
m_profiledCallee->tierUpCounter().setCompilationError(mode(), error);
}
}
} } // namespace JSC::Wasm
#endif // ENABLE(WEBASSEMBLY_BBQJIT)