/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * * Copyright 2015 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_generator_h #define wasm_generator_h #include "mozilla/Attributes.h" #include "mozilla/MemoryReporting.h" #include "jit/MacroAssembler.h" #include "threading/ProtectedData.h" #include "vm/HelperThreadTask.h" #include "wasm/WasmCompile.h" #include "wasm/WasmModule.h" #include "wasm/WasmValidate.h" namespace JS { class OptimizedEncodingListener; } namespace js { namespace wasm { struct CompileTask; using CompileTaskPtrVector = Vector; // FuncCompileInput contains the input for compiling a single function. struct FuncCompileInput { const uint8_t* begin; const uint8_t* end; uint32_t index; uint32_t lineOrBytecode; Uint32Vector callSiteLineNums; FuncCompileInput(uint32_t index, uint32_t lineOrBytecode, const uint8_t* begin, const uint8_t* end, Uint32Vector&& callSiteLineNums) : begin(begin), end(end), index(index), lineOrBytecode(lineOrBytecode), callSiteLineNums(std::move(callSiteLineNums)) {} }; using FuncCompileInputVector = Vector; // CompiledCode contains the resulting code and metadata for a set of compiled // input functions or stubs. struct CompiledCode { Bytes bytes; CodeRangeVector codeRanges; CallSiteVector callSites; CallSiteTargetVector callSiteTargets; TrapSiteVectorArray trapSites; SymbolicAccessVector symbolicAccesses; jit::CodeLabelVector codeLabels; StackMaps stackMaps; TryNoteVector tryNotes; [[nodiscard]] bool swap(jit::MacroAssembler& masm); void clear() { bytes.clear(); codeRanges.clear(); callSites.clear(); callSiteTargets.clear(); trapSites.clear(); symbolicAccesses.clear(); codeLabels.clear(); stackMaps.clear(); tryNotes.clear(); MOZ_ASSERT(empty()); } bool empty() { return bytes.empty() && codeRanges.empty() && callSites.empty() && callSiteTargets.empty() && trapSites.empty() && symbolicAccesses.empty() && codeLabels.empty() && tryNotes.empty() && stackMaps.empty(); } size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; }; // The CompileTaskState of a ModuleGenerator contains the mutable state shared // between helper threads executing CompileTasks. Each CompileTask started on a // helper thread eventually either ends up in the 'finished' list or increments // 'numFailed'. struct CompileTaskState { HelperThreadLockData finished_; HelperThreadLockData numFailed_; HelperThreadLockData errorMessage_; HelperThreadLockData condVar_; CompileTaskState() : numFailed_(0) {} ~CompileTaskState() { MOZ_ASSERT(finished_.refNoCheck().empty()); MOZ_ASSERT(!numFailed_.refNoCheck()); } CompileTaskPtrVector& finished() { return finished_.ref(); } uint32_t& numFailed() { return numFailed_.ref(); } UniqueChars& errorMessage() { return errorMessage_.ref(); } ConditionVariable& condVar() { return condVar_.ref(); } }; // A CompileTask holds a batch of input functions that are to be compiled on a // helper thread as well as, eventually, the results of compilation. struct CompileTask : public HelperThreadTask { const ModuleEnvironment& moduleEnv; const CompilerEnvironment& compilerEnv; CompileTaskState& state; LifoAlloc lifo; FuncCompileInputVector inputs; CompiledCode output; CompileTask(const ModuleEnvironment& moduleEnv, const CompilerEnvironment& compilerEnv, CompileTaskState& state, size_t defaultChunkSize) : moduleEnv(moduleEnv), compilerEnv(compilerEnv), state(state), lifo(defaultChunkSize) {} virtual ~CompileTask() = default; size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; void runHelperThreadTask(AutoLockHelperThreadState& locked) override; ThreadType threadType() override; }; // A ModuleGenerator encapsulates the creation of a wasm module. During the // lifetime of a ModuleGenerator, a sequence of FunctionGenerators are created // and destroyed to compile the individual function bodies. After generating all // functions, ModuleGenerator::finish() must be called to complete the // compilation and extract the resulting wasm module. class MOZ_STACK_CLASS ModuleGenerator { using CompileTaskVector = Vector; using CodeOffsetVector = Vector; struct CallFarJump { uint32_t funcIndex; jit::CodeOffset jump; CallFarJump(uint32_t fi, jit::CodeOffset j) : funcIndex(fi), jump(j) {} }; using CallFarJumpVector = Vector; // Constant parameters SharedCompileArgs const compileArgs_; UniqueChars* const error_; UniqueCharsVector* const warnings_; const Atomic* const cancelled_; ModuleEnvironment* const moduleEnv_; CompilerEnvironment* const compilerEnv_; // Data that is moved into the result of finish() UniqueLinkData linkData_; UniqueMetadataTier metadataTier_; MutableMetadata metadata_; // Data scoped to the ModuleGenerator's lifetime CompileTaskState taskState_; LifoAlloc lifo_; jit::TempAllocator masmAlloc_; jit::WasmMacroAssembler masm_; Uint32Vector funcToCodeRange_; uint32_t debugTrapCodeOffset_; CallFarJumpVector callFarJumps_; CallSiteTargetVector callSiteTargets_; uint32_t lastPatchedCallSite_; uint32_t startOfUnpatchedCallsites_; // Parallel compilation bool parallel_; uint32_t outstanding_; CompileTaskVector tasks_; CompileTaskPtrVector freeTasks_; CompileTask* currentTask_; uint32_t batchedBytecode_; // Assertions DebugOnly finishedFuncDefs_; bool allocateInstanceDataBytes(uint32_t bytes, uint32_t align, uint32_t* instanceDataOffset); bool allocateInstanceDataBytesN(uint32_t bytes, uint32_t align, uint32_t count, uint32_t* instanceDataOffset); bool funcIsCompiled(uint32_t funcIndex) const; const CodeRange& funcCodeRange(uint32_t funcIndex) const; bool linkCallSites(); void noteCodeRange(uint32_t codeRangeIndex, const CodeRange& codeRange); bool linkCompiledCode(CompiledCode& code); bool locallyCompileCurrentTask(); bool finishTask(CompileTask* task); bool launchBatchCompile(); bool finishOutstandingTask(); bool finishCodegen(); bool finishMetadataTier(); UniqueCodeTier finishCodeTier(); SharedMetadata finishMetadata(const Bytes& bytecode); bool isAsmJS() const { return moduleEnv_->isAsmJS(); } Tier tier() const { return compilerEnv_->tier(); } CompileMode mode() const { return compilerEnv_->mode(); } bool debugEnabled() const { return compilerEnv_->debugEnabled(); } void warnf(const char* msg, ...) MOZ_FORMAT_PRINTF(2, 3); public: ModuleGenerator(const CompileArgs& args, ModuleEnvironment* moduleEnv, CompilerEnvironment* compilerEnv, const Atomic* cancelled, UniqueChars* error, UniqueCharsVector* warnings); ~ModuleGenerator(); [[nodiscard]] bool init(Metadata* maybeAsmJSMetadata = nullptr); // Before finishFuncDefs() is called, compileFuncDef() must be called once // for each funcIndex in the range [0, env->numFuncDefs()). [[nodiscard]] bool compileFuncDef( uint32_t funcIndex, uint32_t lineOrBytecode, const uint8_t* begin, const uint8_t* end, Uint32Vector&& callSiteLineNums = Uint32Vector()); // Must be called after the last compileFuncDef() and before finishModule() // or finishTier2(). [[nodiscard]] bool finishFuncDefs(); // If env->mode is Once or Tier1, finishModule() must be called to generate // a new Module. Otherwise, if env->mode is Tier2, finishTier2() must be // called to augment the given Module with tier 2 code. SharedModule finishModule( const ShareableBytes& bytecode, JS::OptimizedEncodingListener* maybeTier2Listener = nullptr); [[nodiscard]] bool finishTier2(const Module& module); }; } // namespace wasm } // namespace js #endif // wasm_generator_h