WasmGenerator.h (13582B)
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/Maybe.h" 24 #include "mozilla/MemoryReporting.h" 25 26 #include "jit/MacroAssembler.h" 27 #include "jit/PerfSpewer.h" 28 #include "threading/ProtectedData.h" 29 #include "vm/HelperThreadTask.h" 30 #include "wasm/WasmCompile.h" 31 #include "wasm/WasmConstants.h" 32 #include "wasm/WasmMetadata.h" 33 #include "wasm/WasmModule.h" 34 35 namespace JS { 36 class OptimizedEncodingListener; 37 } 38 39 namespace js { 40 namespace wasm { 41 42 struct CompileTask; 43 using CompileTaskPtrVector = Vector<CompileTask*, 0, SystemAllocPolicy>; 44 45 // FuncCompileInput contains the input for compiling a single function. 46 47 struct FuncCompileInput { 48 const uint8_t* begin; 49 const uint8_t* end; 50 uint32_t index; 51 uint32_t lineOrBytecode; 52 Uint32Vector callSiteLineNums; 53 54 FuncCompileInput(uint32_t index, uint32_t lineOrBytecode, 55 const uint8_t* begin, const uint8_t* end, 56 Uint32Vector&& callSiteLineNums) 57 : begin(begin), 58 end(end), 59 index(index), 60 lineOrBytecode(lineOrBytecode), 61 callSiteLineNums(std::move(callSiteLineNums)) {} 62 63 uint32_t bytecodeSize() const { 64 static_assert(wasm::MaxFunctionBytes <= UINT32_MAX); 65 return uint32_t(end - begin); 66 } 67 }; 68 69 using FuncCompileInputVector = Vector<FuncCompileInput, 8, SystemAllocPolicy>; 70 71 struct FuncCompileOutput { 72 FuncCompileOutput( 73 uint32_t index, FeatureUsage featureUsage, 74 CallRefMetricsRange callRefMetricsRange = CallRefMetricsRange(), 75 AllocSitesRange allocSitesRange = AllocSitesRange()) 76 : index(index), 77 featureUsage(featureUsage), 78 callRefMetricsRange(callRefMetricsRange), 79 allocSitesRange(allocSitesRange) {} 80 81 uint32_t index; 82 FeatureUsage featureUsage; 83 CallRefMetricsRange callRefMetricsRange; 84 AllocSitesRange allocSitesRange; 85 }; 86 87 using FuncCompileOutputVector = Vector<FuncCompileOutput, 8, SystemAllocPolicy>; 88 89 // CompiledCode contains the resulting code and metadata for a set of compiled 90 // input functions or stubs. 91 92 struct CompiledCode { 93 CompiledCode() : featureUsage(FeatureUsage::None) {} 94 95 FuncCompileOutputVector funcs; 96 Bytes bytes; 97 CodeRangeVector codeRanges; 98 InliningContext inliningContext; 99 CallSites callSites; 100 CallSiteTargetVector callSiteTargets; 101 TrapSites trapSites; 102 SymbolicAccessVector symbolicAccesses; 103 jit::CodeLabelVector codeLabels; 104 StackMaps stackMaps; 105 TryNoteVector tryNotes; 106 CodeRangeUnwindInfoVector codeRangeUnwindInfos; 107 CallRefMetricsPatchVector callRefMetricsPatches; 108 AllocSitePatchVector allocSitesPatches; 109 FuncIonPerfSpewerVector funcIonSpewers; 110 FuncBaselinePerfSpewerVector funcBaselineSpewers; 111 FeatureUsage featureUsage; 112 CompileStats compileStats; 113 114 [[nodiscard]] bool swap(jit::MacroAssembler& masm); 115 116 void clear() { 117 funcs.clear(); 118 bytes.clear(); 119 codeRanges.clear(); 120 inliningContext.clear(); 121 callSites.clear(); 122 callSiteTargets.clear(); 123 trapSites.clear(); 124 symbolicAccesses.clear(); 125 codeLabels.clear(); 126 stackMaps.clear(); 127 tryNotes.clear(); 128 codeRangeUnwindInfos.clear(); 129 callRefMetricsPatches.clear(); 130 allocSitesPatches.clear(); 131 funcIonSpewers.clear(); 132 funcBaselineSpewers.clear(); 133 featureUsage = FeatureUsage::None; 134 compileStats.clear(); 135 MOZ_ASSERT(empty()); 136 } 137 138 bool empty() { 139 return funcs.empty() && bytes.empty() && codeRanges.empty() && 140 inliningContext.empty() && callSites.empty() && 141 callSiteTargets.empty() && trapSites.empty() && 142 symbolicAccesses.empty() && codeLabels.empty() && tryNotes.empty() && 143 stackMaps.empty() && codeRangeUnwindInfos.empty() && 144 callRefMetricsPatches.empty() && allocSitesPatches.empty() && 145 funcIonSpewers.empty() && funcBaselineSpewers.empty() && 146 featureUsage == FeatureUsage::None && compileStats.empty(); 147 } 148 149 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; 150 }; 151 152 // The CompileTaskState of a ModuleGenerator contains the mutable state shared 153 // between helper threads executing CompileTasks. Each CompileTask started on a 154 // helper thread eventually either ends up in the 'finished' list or increments 155 // 'numFailed'. 156 157 struct CompileTaskState { 158 HelperThreadLockData<CompileTaskPtrVector> finished_; 159 HelperThreadLockData<uint32_t> numFailed_; 160 HelperThreadLockData<UniqueChars> errorMessage_; 161 HelperThreadLockData<ConditionVariable> condVar_; 162 163 CompileTaskState() : numFailed_(0) {} 164 ~CompileTaskState() { 165 MOZ_ASSERT(finished_.refNoCheck().empty()); 166 MOZ_ASSERT(!numFailed_.refNoCheck()); 167 } 168 169 CompileTaskPtrVector& finished() { return finished_.ref(); } 170 uint32_t& numFailed() { return numFailed_.ref(); } 171 UniqueChars& errorMessage() { return errorMessage_.ref(); } 172 ConditionVariable& condVar() { return condVar_.ref(); } 173 }; 174 175 // A CompileTask holds a batch of input functions that are to be compiled on a 176 // helper thread as well as, eventually, the results of compilation. 177 178 struct CompileTask : public HelperThreadTask { 179 const CodeMetadata& codeMeta; 180 const CodeTailMetadata* codeTailMeta; 181 const CompilerEnvironment& compilerEnv; 182 const CompileState compileState; 183 184 CompileTaskState& state; 185 LifoAlloc lifo; 186 FuncCompileInputVector inputs; 187 CompiledCode output; 188 189 CompileTask(const CodeMetadata& codeMeta, 190 const CodeTailMetadata* codeTailMeta, 191 const CompilerEnvironment& compilerEnv, CompileState compileState, 192 CompileTaskState& state, size_t defaultChunkSize) 193 : codeMeta(codeMeta), 194 codeTailMeta(codeTailMeta), 195 compilerEnv(compilerEnv), 196 compileState(compileState), 197 state(state), 198 lifo(defaultChunkSize, js::MallocArena) {} 199 200 virtual ~CompileTask() = default; 201 202 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; 203 204 void runHelperThreadTask(AutoLockHelperThreadState& locked) override; 205 ThreadType threadType() override; 206 207 const char* getName() override { return "WasmCompileTask"; } 208 }; 209 210 // A ModuleGenerator encapsulates the creation of a wasm module. During the 211 // lifetime of a ModuleGenerator, a sequence of FunctionGenerators are created 212 // and destroyed to compile the individual function bodies. After generating all 213 // functions, ModuleGenerator::finish() must be called to complete the 214 // compilation and extract the resulting wasm module. 215 216 class MOZ_STACK_CLASS ModuleGenerator { 217 using CompileTaskVector = Vector<CompileTask, 0, SystemAllocPolicy>; 218 using CodeOffsetVector = Vector<jit::CodeOffset, 0, SystemAllocPolicy>; 219 220 // Encapsulates the macro assembler state so that we can create a new one for 221 // each code block. Not heap allocated because the macro assembler is a 222 // 'stack class'. 223 struct MacroAssemblerScope { 224 jit::TempAllocator masmAlloc; 225 jit::WasmMacroAssembler masm; 226 227 explicit MacroAssemblerScope(LifoAlloc& lifo); 228 ~MacroAssemblerScope() = default; 229 }; 230 231 // Encapsulates all the results of creating a code block. 232 struct CodeBlockResult { 233 UniqueCodeBlock codeBlock; 234 UniqueLinkData linkData; 235 FuncIonPerfSpewerVector funcIonSpewers; 236 FuncBaselinePerfSpewerVector funcBaselineSpewers; 237 }; 238 239 // Constant parameters 240 SharedCompileArgs const compileArgs_; 241 const CompileState compileState_; 242 UniqueChars* const error_; 243 UniqueCharsVector* const warnings_; 244 const mozilla::Atomic<bool>* const cancelled_; 245 const CodeMetadata* const codeMeta_; 246 const CompilerEnvironment* const compilerEnv_; 247 248 // Data that is used for partial tiering 249 SharedCode partialTieringCode_; 250 251 // Data that is used for compiling a complete tier 252 mozilla::TimeStamp completeTierStartTime_; 253 254 // Data that is moved into the Module/Code as the result of finish() 255 BytecodeRangeVector funcDefRanges_; 256 FeatureUsageVector funcDefFeatureUsages_; 257 CallRefMetricsRangeVector funcDefCallRefMetrics_; 258 AllocSitesRangeVector funcDefAllocSites_; 259 FuncImportVector funcImports_; 260 CodeBlockResult sharedStubs_; 261 MutableCodeMetadataForAsmJS codeMetaForAsmJS_; 262 FeatureUsage featureUsage_; 263 264 // Data that is used to construct a CodeBlock 265 UniqueCodeBlock codeBlock_; 266 UniqueLinkData linkData_; 267 LifoAlloc lifo_; 268 mozilla::Maybe<MacroAssemblerScope> masmScope_; 269 jit::WasmMacroAssembler* masm_; 270 uint32_t debugStubCodeOffset_; 271 uint32_t requestTierUpStubCodeOffset_; 272 uint32_t updateCallRefMetricsStubCodeOffset_; 273 CallFarJumpVector callFarJumps_; 274 CallSiteTargetVector callSiteTargets_; 275 FuncIonPerfSpewerVector funcIonSpewers_; 276 FuncBaselinePerfSpewerVector funcBaselineSpewers_; 277 uint32_t lastPatchedCallSite_; 278 uint32_t startOfUnpatchedCallsites_; 279 uint32_t numCallRefMetrics_; 280 uint32_t numAllocSites_; 281 CompileAndLinkStats tierStats_; 282 283 // Parallel compilation 284 bool parallel_; 285 uint32_t outstanding_; 286 CompileTaskState taskState_; 287 CompileTaskVector tasks_; 288 CompileTaskPtrVector freeTasks_; 289 CompileTask* currentTask_; 290 uint32_t batchedBytecode_; 291 292 // Assertions 293 mozilla::DebugOnly<bool> finishedFuncDefs_; 294 295 bool funcIsCompiledInBlock(uint32_t funcIndex) const; 296 const CodeRange& funcCodeRangeInBlock(uint32_t funcIndex) const; 297 bool linkCallSites(); 298 void noteCodeRange(uint32_t codeRangeIndex, const CodeRange& codeRange); 299 bool linkCompiledCode(CompiledCode& code); 300 [[nodiscard]] bool initTasks(); 301 bool locallyCompileCurrentTask(); 302 bool finishTask(CompileTask* task); 303 bool launchBatchCompile(); 304 bool finishOutstandingTask(); 305 306 // Begins the creation of a code block. All code compiled during this time 307 // will go into this code block. All previous code blocks must be finished. 308 [[nodiscard]] bool startCodeBlock(CodeBlockKind kind); 309 // Finish the creation of a code block. This will move all the compiled code 310 // and metadata into the code block and initialize it. 311 [[nodiscard]] bool finishCodeBlock(CodeBlockResult* result); 312 313 // Generate a code block containing all stubs that are shared between the 314 // different tiers. 315 [[nodiscard]] bool prepareTier1(); 316 317 // Starts the creation of a complete tier of wasm code. Every function 318 // defined in this module must be compiled, then finishTier must be 319 // called. 320 [[nodiscard]] bool startCompleteTier(); 321 // Starts the creation of a partial tier of wasm code. The specified function 322 // must be compiled, then finishTier must be called. 323 [[nodiscard]] bool startPartialTier(uint32_t funcIndex); 324 // Finishes a complete or partial tier of wasm code. 325 [[nodiscard]] bool finishTier(CompileAndLinkStats* tierStats, 326 CodeBlockResult* result); 327 328 bool isAsmJS() const { return codeMeta_->isAsmJS(); } 329 Tier tier() const { return compilerEnv_->tier(); } 330 CompileMode mode() const { return compilerEnv_->mode(); } 331 bool debugEnabled() const { return compilerEnv_->debugEnabled(); } 332 bool compilingTier1() const { 333 return compileState_ == CompileState::Once || 334 compileState_ == CompileState::EagerTier1 || 335 compileState_ == CompileState::LazyTier1; 336 } 337 338 void warnf(const char* msg, ...) MOZ_FORMAT_PRINTF(2, 3); 339 340 public: 341 ModuleGenerator(const CodeMetadata& codeMeta, 342 const CompilerEnvironment& compilerEnv, 343 CompileState compilerState, 344 const mozilla::Atomic<bool>* cancelled, UniqueChars* error, 345 UniqueCharsVector* warnings); 346 ~ModuleGenerator(); 347 [[nodiscard]] bool initializeCompleteTier( 348 CodeMetadataForAsmJS* codeMetaForAsmJS = nullptr); 349 [[nodiscard]] bool initializePartialTier(const Code& code, 350 uint32_t maybeFuncIndex); 351 352 // Before finishFuncDefs() is called, compileFuncDef() must be called once 353 // for each funcIndex in the range [0, env->numFuncDefs()). 354 355 [[nodiscard]] bool compileFuncDef( 356 uint32_t funcIndex, uint32_t lineOrBytecode, const uint8_t* begin, 357 const uint8_t* end, Uint32Vector&& callSiteLineNums = Uint32Vector()); 358 359 // Must be called after the last compileFuncDef() and before finishModule() 360 // or finishTier2(). 361 362 [[nodiscard]] bool finishFuncDefs(); 363 364 // If env->mode is Once or Tier1, finishModule() must be called to generate 365 // a new Module. Otherwise, if env->mode is Tier2, finishTier2() must be 366 // called to augment the given Module with tier 2 code. `moduleMeta` 367 // is passed as mutable only because we have to std::move field(s) out of 368 // it; if that in future gets cleaned up, the parameter should be changed 369 // to being SharedModuleMetadata. 370 371 SharedModule finishModule( 372 const BytecodeBufferOrSource& bytecode, ModuleMetadata& moduleMeta, 373 JS::OptimizedEncodingListener* maybeCompleteTier2Listener); 374 [[nodiscard]] bool finishTier2(const Module& module); 375 [[nodiscard]] bool finishPartialTier2(); 376 }; 377 378 } // namespace wasm 379 } // namespace js 380 381 #endif // wasm_generator_h