tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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