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/Attributes.h" 23 #include "mozilla/MemoryReporting.h" 24 25 #include "jit/MacroAssembler.h" 26 #include "threading/ProtectedData.h" 27 #include "vm/HelperThreadTask.h" 28 #include "wasm/WasmCompile.h" 29 #include "wasm/WasmModule.h" 30 #include "wasm/WasmValidate.h" 31 32 namespace JS { 33 class OptimizedEncodingListener; 34 } 35 36 namespace js { 37 namespace wasm { 38 39 struct CompileTask; 40 using CompileTaskPtrVector = Vector<CompileTask*, 0, SystemAllocPolicy>; 41 42 // FuncCompileInput contains the input for compiling a single function. 43 44 struct FuncCompileInput { 45 const uint8_t* begin; 46 const uint8_t* end; 47 uint32_t index; 48 uint32_t lineOrBytecode; 49 Uint32Vector callSiteLineNums; 50 FuncCompileInputFuncCompileInput51 FuncCompileInput(uint32_t index, uint32_t lineOrBytecode, 52 const uint8_t* begin, const uint8_t* end, 53 Uint32Vector&& callSiteLineNums) 54 : begin(begin), 55 end(end), 56 index(index), 57 lineOrBytecode(lineOrBytecode), 58 callSiteLineNums(std::move(callSiteLineNums)) {} 59 }; 60 61 using FuncCompileInputVector = Vector<FuncCompileInput, 8, SystemAllocPolicy>; 62 63 void CraneliftFreeReusableData(void* ptr); 64 65 struct CraneliftReusableDataDtor { operatorCraneliftReusableDataDtor66 void operator()(void* ptr) { CraneliftFreeReusableData(ptr); } 67 }; 68 69 using CraneliftReusableData = 70 mozilla::UniquePtr<void*, CraneliftReusableDataDtor>; 71 72 // CompiledCode contains the resulting code and metadata for a set of compiled 73 // input functions or stubs. 74 75 struct CompiledCode { 76 Bytes bytes; 77 CodeRangeVector codeRanges; 78 CallSiteVector callSites; 79 CallSiteTargetVector callSiteTargets; 80 TrapSiteVectorArray trapSites; 81 SymbolicAccessVector symbolicAccesses; 82 jit::CodeLabelVector codeLabels; 83 StackMaps stackMaps; 84 CraneliftReusableData craneliftReusableData; 85 #ifdef ENABLE_WASM_EXCEPTIONS 86 WasmTryNoteVector tryNotes; 87 #endif 88 89 [[nodiscard]] bool swap(jit::MacroAssembler& masm); 90 [[nodiscard]] bool swapCranelift(jit::MacroAssembler& masm, 91 CraneliftReusableData& craneliftData); 92 clearCompiledCode93 void clear() { 94 bytes.clear(); 95 codeRanges.clear(); 96 callSites.clear(); 97 callSiteTargets.clear(); 98 trapSites.clear(); 99 symbolicAccesses.clear(); 100 codeLabels.clear(); 101 stackMaps.clear(); 102 #ifdef ENABLE_WASM_EXCEPTIONS 103 tryNotes.clear(); 104 #endif 105 // The cranelift reusable data resets itself lazily. 106 MOZ_ASSERT(empty()); 107 } 108 emptyCompiledCode109 bool empty() { 110 return bytes.empty() && codeRanges.empty() && callSites.empty() && 111 callSiteTargets.empty() && trapSites.empty() && 112 symbolicAccesses.empty() && codeLabels.empty() && 113 #ifdef ENABLE_WASM_EXCEPTIONS 114 tryNotes.empty() && stackMaps.empty(); 115 #else 116 stackMaps.empty(); 117 #endif 118 } 119 120 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; 121 }; 122 123 // The CompileTaskState of a ModuleGenerator contains the mutable state shared 124 // between helper threads executing CompileTasks. Each CompileTask started on a 125 // helper thread eventually either ends up in the 'finished' list or increments 126 // 'numFailed'. 127 128 struct CompileTaskState { 129 HelperThreadLockData<CompileTaskPtrVector> finished_; 130 HelperThreadLockData<uint32_t> numFailed_; 131 HelperThreadLockData<UniqueChars> errorMessage_; 132 HelperThreadLockData<ConditionVariable> condVar_; 133 CompileTaskStateCompileTaskState134 CompileTaskState() : numFailed_(0) {} ~CompileTaskStateCompileTaskState135 ~CompileTaskState() { 136 MOZ_ASSERT(finished_.refNoCheck().empty()); 137 MOZ_ASSERT(!numFailed_.refNoCheck()); 138 } 139 finishedCompileTaskState140 CompileTaskPtrVector& finished() { return finished_.ref(); } numFailedCompileTaskState141 uint32_t& numFailed() { return numFailed_.ref(); } errorMessageCompileTaskState142 UniqueChars& errorMessage() { return errorMessage_.ref(); } condVarCompileTaskState143 ConditionVariable& condVar() { return condVar_.ref(); } 144 }; 145 146 // A CompileTask holds a batch of input functions that are to be compiled on a 147 // helper thread as well as, eventually, the results of compilation. 148 149 struct CompileTask : public HelperThreadTask { 150 const ModuleEnvironment& moduleEnv; 151 const CompilerEnvironment& compilerEnv; 152 153 CompileTaskState& state; 154 LifoAlloc lifo; 155 FuncCompileInputVector inputs; 156 CompiledCode output; 157 CompileTaskCompileTask158 CompileTask(const ModuleEnvironment& moduleEnv, 159 const CompilerEnvironment& compilerEnv, CompileTaskState& state, 160 size_t defaultChunkSize) 161 : moduleEnv(moduleEnv), 162 compilerEnv(compilerEnv), 163 state(state), 164 lifo(defaultChunkSize) {} 165 166 virtual ~CompileTask() = default; 167 168 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; 169 170 void runHelperThreadTask(AutoLockHelperThreadState& locked) override; 171 ThreadType threadType() override; 172 }; 173 174 // A ModuleGenerator encapsulates the creation of a wasm module. During the 175 // lifetime of a ModuleGenerator, a sequence of FunctionGenerators are created 176 // and destroyed to compile the individual function bodies. After generating all 177 // functions, ModuleGenerator::finish() must be called to complete the 178 // compilation and extract the resulting wasm module. 179 180 class MOZ_STACK_CLASS ModuleGenerator { 181 using CompileTaskVector = Vector<CompileTask, 0, SystemAllocPolicy>; 182 using CodeOffsetVector = Vector<jit::CodeOffset, 0, SystemAllocPolicy>; 183 struct CallFarJump { 184 uint32_t funcIndex; 185 jit::CodeOffset jump; CallFarJumpCallFarJump186 CallFarJump(uint32_t fi, jit::CodeOffset j) : funcIndex(fi), jump(j) {} 187 }; 188 using CallFarJumpVector = Vector<CallFarJump, 0, SystemAllocPolicy>; 189 190 // Constant parameters 191 SharedCompileArgs const compileArgs_; 192 UniqueChars* const error_; 193 UniqueCharsVector* const warnings_; 194 const Atomic<bool>* const cancelled_; 195 ModuleEnvironment* const moduleEnv_; 196 CompilerEnvironment* const compilerEnv_; 197 198 // Data that is moved into the result of finish() 199 UniqueLinkData linkData_; 200 UniqueMetadataTier metadataTier_; 201 MutableMetadata metadata_; 202 203 // Data scoped to the ModuleGenerator's lifetime 204 CompileTaskState taskState_; 205 LifoAlloc lifo_; 206 jit::JitContext jcx_; 207 jit::TempAllocator masmAlloc_; 208 jit::WasmMacroAssembler masm_; 209 Uint32Vector funcToCodeRange_; 210 uint32_t debugTrapCodeOffset_; 211 CallFarJumpVector callFarJumps_; 212 CallSiteTargetVector callSiteTargets_; 213 uint32_t lastPatchedCallSite_; 214 uint32_t startOfUnpatchedCallsites_; 215 CodeOffsetVector debugTrapFarJumps_; 216 217 // Parallel compilation 218 bool parallel_; 219 uint32_t outstanding_; 220 CompileTaskVector tasks_; 221 CompileTaskPtrVector freeTasks_; 222 CompileTask* currentTask_; 223 uint32_t batchedBytecode_; 224 225 // Assertions 226 DebugOnly<bool> finishedFuncDefs_; 227 228 bool allocateGlobalBytes(uint32_t bytes, uint32_t align, 229 uint32_t* globalDataOff); 230 231 bool funcIsCompiled(uint32_t funcIndex) const; 232 const CodeRange& funcCodeRange(uint32_t funcIndex) const; 233 bool linkCallSites(); 234 void noteCodeRange(uint32_t codeRangeIndex, const CodeRange& codeRange); 235 bool linkCompiledCode(CompiledCode& code); 236 bool locallyCompileCurrentTask(); 237 bool finishTask(CompileTask* task); 238 bool launchBatchCompile(); 239 bool finishOutstandingTask(); 240 bool finishCodegen(); 241 bool finishMetadataTier(); 242 UniqueCodeTier finishCodeTier(); 243 SharedMetadata finishMetadata(const Bytes& bytecode); 244 isAsmJS()245 bool isAsmJS() const { return moduleEnv_->isAsmJS(); } tier()246 Tier tier() const { return compilerEnv_->tier(); } mode()247 CompileMode mode() const { return compilerEnv_->mode(); } debugEnabled()248 bool debugEnabled() const { return compilerEnv_->debugEnabled(); } 249 250 void warnf(const char* msg, ...) MOZ_FORMAT_PRINTF(2, 3); 251 252 public: 253 ModuleGenerator(const CompileArgs& args, ModuleEnvironment* moduleEnv, 254 CompilerEnvironment* compilerEnv, 255 const Atomic<bool>* cancelled, UniqueChars* error, 256 UniqueCharsVector* warnings); 257 ~ModuleGenerator(); 258 [[nodiscard]] bool init(Metadata* maybeAsmJSMetadata = nullptr); 259 260 // Before finishFuncDefs() is called, compileFuncDef() must be called once 261 // for each funcIndex in the range [0, env->numFuncDefs()). 262 263 [[nodiscard]] bool compileFuncDef( 264 uint32_t funcIndex, uint32_t lineOrBytecode, const uint8_t* begin, 265 const uint8_t* end, Uint32Vector&& callSiteLineNums = Uint32Vector()); 266 267 // Must be called after the last compileFuncDef() and before finishModule() 268 // or finishTier2(). 269 270 [[nodiscard]] bool finishFuncDefs(); 271 272 // If env->mode is Once or Tier1, finishModule() must be called to generate 273 // a new Module. Otherwise, if env->mode is Tier2, finishTier2() must be 274 // called to augment the given Module with tier 2 code. 275 276 SharedModule finishModule( 277 const ShareableBytes& bytecode, 278 JS::OptimizedEncodingListener* maybeTier2Listener = nullptr); 279 [[nodiscard]] bool finishTier2(const Module& module); 280 }; 281 282 } // namespace wasm 283 } // namespace js 284 285 #endif // wasm_generator_h 286