1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * 4 * Copyright 2015 Mozilla Foundation 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 #ifndef wasm_generator_h 20 #define wasm_generator_h 21 22 #include "mozilla/MemoryReporting.h" 23 24 #include "jit/MacroAssembler.h" 25 #include "threading/ProtectedData.h" 26 #include "vm/HelperThreadTask.h" 27 #include "wasm/WasmCompile.h" 28 #include "wasm/WasmModule.h" 29 #include "wasm/WasmValidate.h" 30 31 namespace js { 32 namespace wasm { 33 34 struct CompileTask; 35 using CompileTaskPtrVector = Vector<CompileTask*, 0, SystemAllocPolicy>; 36 37 // FuncCompileInput contains the input for compiling a single function. 38 39 struct FuncCompileInput { 40 const uint8_t* begin; 41 const uint8_t* end; 42 uint32_t index; 43 uint32_t lineOrBytecode; 44 Uint32Vector callSiteLineNums; 45 FuncCompileInputFuncCompileInput46 FuncCompileInput(uint32_t index, uint32_t lineOrBytecode, 47 const uint8_t* begin, const uint8_t* end, 48 Uint32Vector&& callSiteLineNums) 49 : begin(begin), 50 end(end), 51 index(index), 52 lineOrBytecode(lineOrBytecode), 53 callSiteLineNums(std::move(callSiteLineNums)) {} 54 }; 55 56 using FuncCompileInputVector = Vector<FuncCompileInput, 8, SystemAllocPolicy>; 57 58 void CraneliftFreeReusableData(void* ptr); 59 60 struct CraneliftReusableDataDtor { operatorCraneliftReusableDataDtor61 void operator()(void* ptr) { CraneliftFreeReusableData(ptr); } 62 }; 63 64 using CraneliftReusableData = 65 mozilla::UniquePtr<void*, CraneliftReusableDataDtor>; 66 67 // CompiledCode contains the resulting code and metadata for a set of compiled 68 // input functions or stubs. 69 70 struct CompiledCode { 71 Bytes bytes; 72 CodeRangeVector codeRanges; 73 CallSiteVector callSites; 74 CallSiteTargetVector callSiteTargets; 75 TrapSiteVectorArray trapSites; 76 SymbolicAccessVector symbolicAccesses; 77 jit::CodeLabelVector codeLabels; 78 StackMaps stackMaps; 79 CraneliftReusableData craneliftReusableData; 80 #ifdef ENABLE_WASM_EXCEPTIONS 81 WasmTryNoteVector tryNotes; 82 #endif 83 84 [[nodiscard]] bool swap(jit::MacroAssembler& masm); 85 [[nodiscard]] bool swapCranelift(jit::MacroAssembler& masm, 86 CraneliftReusableData& craneliftData); 87 clearCompiledCode88 void clear() { 89 bytes.clear(); 90 codeRanges.clear(); 91 callSites.clear(); 92 callSiteTargets.clear(); 93 trapSites.clear(); 94 symbolicAccesses.clear(); 95 codeLabels.clear(); 96 stackMaps.clear(); 97 #ifdef ENABLE_WASM_EXCEPTIONS 98 tryNotes.clear(); 99 #endif 100 // The cranelift reusable data resets itself lazily. 101 MOZ_ASSERT(empty()); 102 } 103 emptyCompiledCode104 bool empty() { 105 return bytes.empty() && codeRanges.empty() && callSites.empty() && 106 callSiteTargets.empty() && trapSites.empty() && 107 symbolicAccesses.empty() && codeLabels.empty() && 108 #ifdef ENABLE_WASM_EXCEPTIONS 109 tryNotes.empty() && stackMaps.empty(); 110 #else 111 stackMaps.empty(); 112 #endif 113 } 114 115 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; 116 }; 117 118 // The CompileTaskState of a ModuleGenerator contains the mutable state shared 119 // between helper threads executing CompileTasks. Each CompileTask started on a 120 // helper thread eventually either ends up in the 'finished' list or increments 121 // 'numFailed'. 122 123 struct CompileTaskState { 124 HelperThreadLockData<CompileTaskPtrVector> finished_; 125 HelperThreadLockData<uint32_t> numFailed_; 126 HelperThreadLockData<UniqueChars> errorMessage_; 127 HelperThreadLockData<ConditionVariable> condVar_; 128 CompileTaskStateCompileTaskState129 CompileTaskState() : numFailed_(0) {} ~CompileTaskStateCompileTaskState130 ~CompileTaskState() { 131 MOZ_ASSERT(finished_.refNoCheck().empty()); 132 MOZ_ASSERT(!numFailed_.refNoCheck()); 133 } 134 finishedCompileTaskState135 CompileTaskPtrVector& finished() { return finished_.ref(); } numFailedCompileTaskState136 uint32_t& numFailed() { return numFailed_.ref(); } errorMessageCompileTaskState137 UniqueChars& errorMessage() { return errorMessage_.ref(); } condVarCompileTaskState138 ConditionVariable& condVar() { return condVar_.ref(); } 139 }; 140 141 // A CompileTask holds a batch of input functions that are to be compiled on a 142 // helper thread as well as, eventually, the results of compilation. 143 144 struct CompileTask : public HelperThreadTask { 145 const ModuleEnvironment& moduleEnv; 146 const CompilerEnvironment& compilerEnv; 147 148 CompileTaskState& state; 149 LifoAlloc lifo; 150 FuncCompileInputVector inputs; 151 CompiledCode output; 152 CompileTaskCompileTask153 CompileTask(const ModuleEnvironment& moduleEnv, 154 const CompilerEnvironment& compilerEnv, CompileTaskState& state, 155 size_t defaultChunkSize) 156 : moduleEnv(moduleEnv), 157 compilerEnv(compilerEnv), 158 state(state), 159 lifo(defaultChunkSize) {} 160 161 virtual ~CompileTask() = default; 162 163 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; 164 165 void runHelperThreadTask(AutoLockHelperThreadState& locked) override; 166 ThreadType threadType() override; 167 }; 168 169 // A ModuleGenerator encapsulates the creation of a wasm module. During the 170 // lifetime of a ModuleGenerator, a sequence of FunctionGenerators are created 171 // and destroyed to compile the individual function bodies. After generating all 172 // functions, ModuleGenerator::finish() must be called to complete the 173 // compilation and extract the resulting wasm module. 174 175 class MOZ_STACK_CLASS ModuleGenerator { 176 using CompileTaskVector = Vector<CompileTask, 0, SystemAllocPolicy>; 177 using CodeOffsetVector = Vector<jit::CodeOffset, 0, SystemAllocPolicy>; 178 struct CallFarJump { 179 uint32_t funcIndex; 180 jit::CodeOffset jump; CallFarJumpCallFarJump181 CallFarJump(uint32_t fi, jit::CodeOffset j) : funcIndex(fi), jump(j) {} 182 }; 183 using CallFarJumpVector = Vector<CallFarJump, 0, SystemAllocPolicy>; 184 185 // Constant parameters 186 SharedCompileArgs const compileArgs_; 187 UniqueChars* const error_; 188 const Atomic<bool>* const cancelled_; 189 ModuleEnvironment* const moduleEnv_; 190 CompilerEnvironment* const compilerEnv_; 191 192 // Data that is moved into the result of finish() 193 UniqueLinkData linkData_; 194 UniqueMetadataTier metadataTier_; 195 MutableMetadata metadata_; 196 197 // Data scoped to the ModuleGenerator's lifetime 198 CompileTaskState taskState_; 199 LifoAlloc lifo_; 200 jit::JitContext jcx_; 201 jit::TempAllocator masmAlloc_; 202 jit::WasmMacroAssembler masm_; 203 Uint32Vector funcToCodeRange_; 204 uint32_t debugTrapCodeOffset_; 205 CallFarJumpVector callFarJumps_; 206 CallSiteTargetVector callSiteTargets_; 207 uint32_t lastPatchedCallSite_; 208 uint32_t startOfUnpatchedCallsites_; 209 CodeOffsetVector debugTrapFarJumps_; 210 211 // Parallel compilation 212 bool parallel_; 213 uint32_t outstanding_; 214 CompileTaskVector tasks_; 215 CompileTaskPtrVector freeTasks_; 216 CompileTask* currentTask_; 217 uint32_t batchedBytecode_; 218 219 // Assertions 220 DebugOnly<bool> finishedFuncDefs_; 221 222 bool allocateGlobalBytes(uint32_t bytes, uint32_t align, 223 uint32_t* globalDataOff); 224 225 bool funcIsCompiled(uint32_t funcIndex) const; 226 const CodeRange& funcCodeRange(uint32_t funcIndex) const; 227 bool linkCallSites(); 228 void noteCodeRange(uint32_t codeRangeIndex, const CodeRange& codeRange); 229 bool linkCompiledCode(CompiledCode& code); 230 bool locallyCompileCurrentTask(); 231 bool finishTask(CompileTask* task); 232 bool launchBatchCompile(); 233 bool finishOutstandingTask(); 234 bool finishCodegen(); 235 bool finishMetadataTier(); 236 UniqueCodeTier finishCodeTier(); 237 SharedMetadata finishMetadata(const Bytes& bytecode); 238 isAsmJS()239 bool isAsmJS() const { return moduleEnv_->isAsmJS(); } tier()240 Tier tier() const { return compilerEnv_->tier(); } mode()241 CompileMode mode() const { return compilerEnv_->mode(); } debugEnabled()242 bool debugEnabled() const { return compilerEnv_->debugEnabled(); } 243 244 public: 245 ModuleGenerator(const CompileArgs& args, ModuleEnvironment* moduleEnv, 246 CompilerEnvironment* compilerEnv, 247 const Atomic<bool>* cancelled, UniqueChars* error); 248 ~ModuleGenerator(); 249 [[nodiscard]] bool init(Metadata* maybeAsmJSMetadata = nullptr); 250 251 // Before finishFuncDefs() is called, compileFuncDef() must be called once 252 // for each funcIndex in the range [0, env->numFuncDefs()). 253 254 [[nodiscard]] bool compileFuncDef( 255 uint32_t funcIndex, uint32_t lineOrBytecode, const uint8_t* begin, 256 const uint8_t* end, Uint32Vector&& callSiteLineNums = Uint32Vector()); 257 258 // Must be called after the last compileFuncDef() and before finishModule() 259 // or finishTier2(). 260 261 [[nodiscard]] bool finishFuncDefs(); 262 263 // If env->mode is Once or Tier1, finishModule() must be called to generate 264 // a new Module. Otherwise, if env->mode is Tier2, finishTier2() must be 265 // called to augment the given Module with tier 2 code. 266 267 SharedModule finishModule( 268 const ShareableBytes& bytecode, 269 JS::OptimizedEncodingListener* maybeTier2Listener = nullptr); 270 [[nodiscard]] bool finishTier2(const Module& module); 271 }; 272 273 } // namespace wasm 274 } // namespace js 275 276 #endif // wasm_generator_h 277