text: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
   * vim: set ts=8 sts=4 et sw=4 tw=99:
   *
   * 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/MemoryReporting.h"
  
  #include "jit/MacroAssembler.h"
  #include "wasm/WasmCompile.h"
  #include "wasm/WasmModule.h"
  #include "wasm/WasmValidate.h"
  
  namespace js {
  namespace wasm {
  
  struct CompileTask;
  typedef Vector<CompileTask*, 0, SystemAllocPolicy> CompileTaskPtrVector;
  
  // 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))
      {}
  };
  
  typedef Vector<FuncCompileInput, 8, SystemAllocPolicy> FuncCompileInputVector;
  
  // 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;
      CallFarJumpVector    callFarJumps;
      SymbolicAccessVector symbolicAccesses;
      jit::CodeLabelVector codeLabels;
  
      MOZ_MUST_USE bool swap(jit::MacroAssembler& masm);
  
      void clear() {
          bytes.clear();
          codeRanges.clear();
          callSites.clear();
          callSiteTargets.clear();
          trapSites.clear();
          callFarJumps.clear();
          symbolicAccesses.clear();
          codeLabels.clear();
          MOZ_ASSERT(empty());
      }
  
      bool empty() {
          return bytes.empty() &&
                 codeRanges.empty() &&
                 callSites.empty() &&
                 callSiteTargets.empty() &&
                 trapSites.empty() &&
                 callFarJumps.empty() &&
                 symbolicAccesses.empty() &&
                 codeLabels.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
  {
      CompileTaskPtrVector finished;
      uint32_t             numFailed;
      UniqueChars          errorMessage;
  
      CompileTaskState() : numFailed(0) {}
      ~CompileTaskState() { MOZ_ASSERT(finished.empty()); MOZ_ASSERT(!numFailed); }
  };
  
  typedef ExclusiveWaitableData<CompileTaskState> ExclusiveCompileTaskState;
  
  // 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
  {
      const ModuleEnvironment&          env;
      ExclusiveCompileTaskState&        state;
      ExclusiveDeferredValidationState& dvs;
      LifoAlloc                         lifo;
      FuncCompileInputVector            inputs;
      CompiledCode                      output;
  
      CompileTask(const ModuleEnvironment& env, ExclusiveCompileTaskState& state,
                  ExclusiveDeferredValidationState& dvs, size_t defaultChunkSize)
        : env(env),
          state(state),
          dvs(dvs),
          lifo(defaultChunkSize)
      {}
  
      size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
  };
  
  // 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
  {
      typedef Vector<CompileTask, 0, SystemAllocPolicy> CompileTaskVector;
      typedef Vector<jit::CodeOffset, 0, SystemAllocPolicy> CodeOffsetVector;
  
      // Constant parameters
      SharedCompileArgs const         compileArgs_;
      UniqueChars* const              error_;
      const Atomic<bool>* const       cancelled_;
      ModuleEnvironment* const        env_;
  
      // Data that is moved into the result of finish()
      UniqueLinkData                  linkData_;
      UniqueMetadataTier              metadataTier_;
      MutableMetadata                 metadata_;
  
      // Data scoped to the ModuleGenerator's lifetime
      ExclusiveCompileTaskState       taskState_;
      LifoAlloc                       lifo_;
      jit::JitContext                 jcx_;
      jit::TempAllocator              masmAlloc_;
      jit::WasmMacroAssembler         masm_;
      Uint32Vector                    funcToCodeRange_;
      uint32_t                        debugTrapCodeOffset_;
      CallFarJumpVector               callFarJumps_;
      CallSiteTargetVector            callSiteTargets_;
      uint32_t                        lastPatchedCallSite_;
      uint32_t                        startOfUnpatchedCallsites_;
      CodeOffsetVector                debugTrapFarJumps_;
  
      // Data accumulated for deferred validation.  Is shared and mutable.
      ExclusiveDeferredValidationState deferredValidationState_;
  
      // Parallel compilation
      bool                            parallel_;
      uint32_t                        outstanding_;
      CompileTaskVector               tasks_;
      CompileTaskPtrVector            freeTasks_;
      CompileTask*                    currentTask_;
      uint32_t                        batchedBytecode_;
  
      // Assertions
      DebugOnly<bool>                 finishedFuncDefs_;
  
      bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOff);
  
      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(const CompiledCode& code);
      bool finishTask(CompileTask* task);
      bool launchBatchCompile();
      bool finishOutstandingTask();
      bool finishCodegen();
      bool finishMetadataTier();
      UniqueCodeTier finishCodeTier();
      bool finishMetadata(const Bytes& bytecode);
  
      bool isAsmJS() const { return env_->isAsmJS(); }
      Tier tier() const { return env_->tier(); }
      CompileMode mode() const { return env_->mode(); }
      bool debugEnabled() const { return env_->debugEnabled(); }
  
    public:
      ModuleGenerator(const CompileArgs& args, ModuleEnvironment* env,
                      const Atomic<bool>* cancelled, UniqueChars* error);
      ~ModuleGenerator();
      MOZ_MUST_USE bool init(Metadata* maybeAsmJSMetadata = nullptr);
  
      // Before finishFuncDefs() is called, compileFuncDef() must be called once
      // for each funcIndex in the range [0, env->numFuncDefs()).
  
      MOZ_MUST_USE 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().
  
      MOZ_MUST_USE 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,
                                UniqueLinkData* maybeLinkDataOut = nullptr);
      MOZ_MUST_USE bool finishTier2(const Module& module);
  
      ExclusiveDeferredValidationState& deferredValidationState() {
          return deferredValidationState_;
      }
  };
  
  } // namespace wasm
  } // namespace js
  
  #endif // wasm_generator_h
  