/ wasm / WasmB3IRGenerator.cpp
WasmB3IRGenerator.cpp
   1  /*
   2   * Copyright (C) 2016-2020 Apple Inc. All rights reserved.
   3   *
   4   * Redistribution and use in source and binary forms, with or without
   5   * modification, are permitted provided that the following conditions
   6   * are met:
   7   * 1. Redistributions of source code must retain the above copyright
   8   *    notice, this list of conditions and the following disclaimer.
   9   * 2. Redistributions in binary form must reproduce the above copyright
  10   *    notice, this list of conditions and the following disclaimer in the
  11   *    documentation and/or other materials provided with the distribution.
  12   *
  13   * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
  14   * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  15   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  16   * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
  17   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  20   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  21   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  22   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  23   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24   */
  25  
  26  #include "config.h"
  27  #include "WasmB3IRGenerator.h"
  28  
  29  #if ENABLE(WEBASSEMBLY)
  30  
  31  #include "AllowMacroScratchRegisterUsageIf.h"
  32  #include "B3BasicBlockInlines.h"
  33  #include "B3CCallValue.h"
  34  #include "B3ConstPtrValue.h"
  35  #include "B3FixSSA.h"
  36  #include "B3Generate.h"
  37  #include "B3InsertionSet.h"
  38  #include "B3StackmapGenerationParams.h"
  39  #include "B3SwitchValue.h"
  40  #include "B3UpsilonValue.h"
  41  #include "B3Validate.h"
  42  #include "B3ValueInlines.h"
  43  #include "B3ValueKey.h"
  44  #include "B3Variable.h"
  45  #include "B3VariableValue.h"
  46  #include "B3WasmAddressValue.h"
  47  #include "B3WasmBoundsCheckValue.h"
  48  #include "JSCJSValueInlines.h"
  49  #include "JSWebAssemblyInstance.h"
  50  #include "ScratchRegisterAllocator.h"
  51  #include "WasmCallingConvention.h"
  52  #include "WasmContextInlines.h"
  53  #include "WasmExceptionType.h"
  54  #include "WasmFunctionParser.h"
  55  #include "WasmInstance.h"
  56  #include "WasmMemory.h"
  57  #include "WasmOSREntryData.h"
  58  #include "WasmOpcodeOrigin.h"
  59  #include "WasmOperations.h"
  60  #include "WasmSignatureInlines.h"
  61  #include "WasmThunks.h"
  62  #include <limits>
  63  #include <wtf/Optional.h>
  64  #include <wtf/StdLibExtras.h>
  65  
  66  void dumpProcedure(void* ptr)
  67  {
  68      JSC::B3::Procedure* proc = static_cast<JSC::B3::Procedure*>(ptr);
  69      proc->dump(WTF::dataFile());
  70  }
  71  
  72  namespace JSC { namespace Wasm {
  73  
  74  using namespace B3;
  75  
  76  namespace {
  77  namespace WasmB3IRGeneratorInternal {
  78  static constexpr bool verbose = false;
  79  }
  80  }
  81  
  82  class B3IRGenerator {
  83  public:
  84      using ExpressionType = Value*;
  85      using ResultList = Vector<ExpressionType, 8>;
  86  
  87      struct ControlData {
  88          ControlData(Procedure& proc, Origin origin, BlockSignature signature, BlockType type, BasicBlock* continuation, BasicBlock* special = nullptr)
  89              : controlBlockType(type)
  90              , m_signature(signature)
  91              , continuation(continuation)
  92              , special(special)
  93          {
  94              if (type == BlockType::Loop) {
  95                  for (unsigned i = 0; i < signature->argumentCount(); ++i)
  96                      phis.append(proc.add<Value>(Phi, toB3Type(signature->argument(i)), origin));
  97              } else {
  98                  for (unsigned i = 0; i < signature->returnCount(); ++i)
  99                      phis.append(proc.add<Value>(Phi, toB3Type(signature->returnType(i)), origin));
 100              }
 101          }
 102  
 103          ControlData()
 104          {
 105          }
 106  
 107          static bool isIf(const ControlData& control) { return control.blockType() == BlockType::If; }
 108          static bool isTopLevel(const ControlData& control) { return control.blockType() == BlockType::TopLevel; }
 109  
 110          void dump(PrintStream& out) const
 111          {
 112              switch (blockType()) {
 113              case BlockType::If:
 114                  out.print("If:       ");
 115                  break;
 116              case BlockType::Block:
 117                  out.print("Block:    ");
 118                  break;
 119              case BlockType::Loop:
 120                  out.print("Loop:     ");
 121                  break;
 122              case BlockType::TopLevel:
 123                  out.print("TopLevel: ");
 124                  break;
 125              }
 126              out.print("Continuation: ", *continuation, ", Special: ");
 127              if (special)
 128                  out.print(*special);
 129              else
 130                  out.print("None");
 131          }
 132  
 133          BlockType blockType() const { return controlBlockType; }
 134  
 135          BlockSignature signature() const { return m_signature; }
 136  
 137          bool hasNonVoidresult() const { return m_signature->returnsVoid(); }
 138  
 139          BasicBlock* targetBlockForBranch()
 140          {
 141              if (blockType() == BlockType::Loop)
 142                  return special;
 143              return continuation;
 144          }
 145  
 146          void convertIfToBlock()
 147          {
 148              ASSERT(blockType() == BlockType::If);
 149              controlBlockType = BlockType::Block;
 150              special = nullptr;
 151          }
 152  
 153          SignatureArgCount branchTargetArity() const
 154          {
 155              if (blockType() == BlockType::Loop)
 156                  return m_signature->argumentCount();
 157              return m_signature->returnCount();
 158          }
 159  
 160          Type branchTargetType(unsigned i) const
 161          {
 162              ASSERT(i < branchTargetArity());
 163              if (blockType() == BlockType::Loop)
 164                  return m_signature->argument(i);
 165              return m_signature->returnType(i);
 166          }
 167  
 168      private:
 169          friend class B3IRGenerator;
 170          BlockType controlBlockType;
 171          BlockSignature m_signature;
 172          BasicBlock* continuation;
 173          BasicBlock* special;
 174          ResultList phis;
 175      };
 176  
 177      using ControlType = ControlData;
 178      using ExpressionList = Vector<ExpressionType, 1>;
 179  
 180      using ControlEntry = FunctionParser<B3IRGenerator>::ControlEntry;
 181      using ControlStack = FunctionParser<B3IRGenerator>::ControlStack;
 182      using Stack = FunctionParser<B3IRGenerator>::Stack;
 183      using TypedExpression = FunctionParser<B3IRGenerator>::TypedExpression;
 184  
 185      static_assert(std::is_same_v<ResultList, FunctionParser<B3IRGenerator>::ResultList>);
 186  
 187      typedef String ErrorType;
 188      typedef Unexpected<ErrorType> UnexpectedResult;
 189      typedef Expected<std::unique_ptr<InternalFunction>, ErrorType> Result;
 190      typedef Expected<void, ErrorType> PartialResult;
 191  
 192      static ExpressionType emptyExpression() { return nullptr; };
 193  
 194      template <typename ...Args>
 195      NEVER_INLINE UnexpectedResult WARN_UNUSED_RETURN fail(Args... args) const
 196      {
 197          using namespace FailureHelper; // See ADL comment in WasmParser.h.
 198          return UnexpectedResult(makeString("WebAssembly.Module failed compiling: "_s, makeString(args)...));
 199      }
 200  #define WASM_COMPILE_FAIL_IF(condition, ...) do { \
 201          if (UNLIKELY(condition))                  \
 202              return fail(__VA_ARGS__);             \
 203      } while (0)
 204  
 205      B3IRGenerator(const ModuleInformation&, Procedure&, InternalFunction*, Vector<UnlinkedWasmToWasmCall>&, unsigned& osrEntryScratchBufferSize, MemoryMode, CompilationMode, unsigned functionIndex, unsigned loopIndexForOSREntry, TierUpCount*);
 206  
 207      PartialResult WARN_UNUSED_RETURN addArguments(const Signature&);
 208      PartialResult WARN_UNUSED_RETURN addLocal(Type, uint32_t);
 209      ExpressionType addConstant(Type, uint64_t);
 210  
 211      // References
 212      PartialResult WARN_UNUSED_RETURN addRefIsNull(ExpressionType value, ExpressionType& result);
 213      PartialResult WARN_UNUSED_RETURN addRefFunc(uint32_t index, ExpressionType& result);
 214  
 215      // Tables
 216      PartialResult WARN_UNUSED_RETURN addTableGet(unsigned, ExpressionType index, ExpressionType& result);
 217      PartialResult WARN_UNUSED_RETURN addTableSet(unsigned, ExpressionType index, ExpressionType value);
 218      PartialResult WARN_UNUSED_RETURN addTableInit(unsigned, unsigned, ExpressionType dstOffset, ExpressionType srcOffset, ExpressionType length);
 219      PartialResult WARN_UNUSED_RETURN addElemDrop(unsigned);
 220      PartialResult WARN_UNUSED_RETURN addTableSize(unsigned, ExpressionType& result);
 221      PartialResult WARN_UNUSED_RETURN addTableGrow(unsigned, ExpressionType fill, ExpressionType delta, ExpressionType& result);
 222      PartialResult WARN_UNUSED_RETURN addTableFill(unsigned, ExpressionType offset, ExpressionType fill, ExpressionType count);
 223      PartialResult WARN_UNUSED_RETURN addTableCopy(unsigned, unsigned, ExpressionType dstOffset, ExpressionType srcOffset, ExpressionType length);
 224  
 225      // Locals
 226      PartialResult WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result);
 227      PartialResult WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value);
 228  
 229      // Globals
 230      PartialResult WARN_UNUSED_RETURN getGlobal(uint32_t index, ExpressionType& result);
 231      PartialResult WARN_UNUSED_RETURN setGlobal(uint32_t index, ExpressionType value);
 232  
 233      // Memory
 234      PartialResult WARN_UNUSED_RETURN load(LoadOpType, ExpressionType pointer, ExpressionType& result, uint32_t offset);
 235      PartialResult WARN_UNUSED_RETURN store(StoreOpType, ExpressionType pointer, ExpressionType value, uint32_t offset);
 236      PartialResult WARN_UNUSED_RETURN addGrowMemory(ExpressionType delta, ExpressionType& result);
 237      PartialResult WARN_UNUSED_RETURN addCurrentMemory(ExpressionType& result);
 238      PartialResult WARN_UNUSED_RETURN addMemoryFill(ExpressionType dstAddress, ExpressionType targetValue, ExpressionType count);
 239      PartialResult WARN_UNUSED_RETURN addMemoryCopy(ExpressionType dstAddress, ExpressionType srcAddress, ExpressionType count);
 240      PartialResult WARN_UNUSED_RETURN addMemoryInit(unsigned, ExpressionType dstAddress, ExpressionType srcAddress, ExpressionType length);
 241      PartialResult WARN_UNUSED_RETURN addDataDrop(unsigned);
 242  
 243      // Atomics
 244      PartialResult WARN_UNUSED_RETURN atomicLoad(ExtAtomicOpType, Type, ExpressionType pointer, ExpressionType& result, uint32_t offset);
 245      PartialResult WARN_UNUSED_RETURN atomicStore(ExtAtomicOpType, Type, ExpressionType pointer, ExpressionType value, uint32_t offset);
 246      PartialResult WARN_UNUSED_RETURN atomicBinaryRMW(ExtAtomicOpType, Type, ExpressionType pointer, ExpressionType value, ExpressionType& result, uint32_t offset);
 247      PartialResult WARN_UNUSED_RETURN atomicCompareExchange(ExtAtomicOpType, Type, ExpressionType pointer, ExpressionType expected, ExpressionType value, ExpressionType& result, uint32_t offset);
 248  
 249      PartialResult WARN_UNUSED_RETURN atomicWait(ExtAtomicOpType, ExpressionType pointer, ExpressionType value, ExpressionType timeout, ExpressionType& result, uint32_t offset);
 250      PartialResult WARN_UNUSED_RETURN atomicNotify(ExtAtomicOpType, ExpressionType pointer, ExpressionType value, ExpressionType& result, uint32_t offset);
 251      PartialResult WARN_UNUSED_RETURN atomicFence(ExtAtomicOpType, uint8_t flags);
 252  
 253      // Basic operators
 254      template<OpType>
 255      PartialResult WARN_UNUSED_RETURN addOp(ExpressionType arg, ExpressionType& result);
 256      template<OpType>
 257      PartialResult WARN_UNUSED_RETURN addOp(ExpressionType left, ExpressionType right, ExpressionType& result);
 258      PartialResult WARN_UNUSED_RETURN addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result);
 259  
 260  
 261      // Control flow
 262      ControlData WARN_UNUSED_RETURN addTopLevel(BlockSignature);
 263      PartialResult WARN_UNUSED_RETURN addBlock(BlockSignature, Stack& enclosingStack, ControlType& newBlock, Stack& newStack);
 264      PartialResult WARN_UNUSED_RETURN addLoop(BlockSignature, Stack& enclosingStack, ControlType& block, Stack& newStack, uint32_t loopIndex);
 265      PartialResult WARN_UNUSED_RETURN addIf(ExpressionType condition, BlockSignature, Stack& enclosingStack, ControlType& result, Stack& newStack);
 266      PartialResult WARN_UNUSED_RETURN addElse(ControlData&, const Stack&);
 267      PartialResult WARN_UNUSED_RETURN addElseToUnreachable(ControlData&);
 268  
 269      PartialResult WARN_UNUSED_RETURN addReturn(const ControlData&, const Stack& returnValues);
 270      PartialResult WARN_UNUSED_RETURN addBranch(ControlData&, ExpressionType condition, const Stack& returnValues);
 271      PartialResult WARN_UNUSED_RETURN addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTargets, const Stack& expressionStack);
 272      PartialResult WARN_UNUSED_RETURN endBlock(ControlEntry&, Stack& expressionStack);
 273      PartialResult WARN_UNUSED_RETURN addEndToUnreachable(ControlEntry&, const Stack& = { });
 274  
 275      PartialResult WARN_UNUSED_RETURN endTopLevel(BlockSignature, const Stack&) { return { }; }
 276  
 277      // Calls
 278      PartialResult WARN_UNUSED_RETURN addCall(uint32_t calleeIndex, const Signature&, Vector<ExpressionType>& args, ResultList& results);
 279      PartialResult WARN_UNUSED_RETURN addCallIndirect(unsigned tableIndex, const Signature&, Vector<ExpressionType>& args, ResultList& results);
 280      PartialResult WARN_UNUSED_RETURN addUnreachable();
 281      B3::Value* createCallPatchpoint(BasicBlock*, Origin, const Signature&, Vector<ExpressionType>& args, const ScopedLambda<void(PatchpointValue*)>& patchpointFunctor);
 282  
 283      void dump(const ControlStack&, const Stack* expressionStack);
 284      void setParser(FunctionParser<B3IRGenerator>* parser) { m_parser = parser; };
 285      void didFinishParsingLocals() { }
 286      void didPopValueFromStack() { }
 287  
 288      Value* constant(B3::Type, uint64_t bits, Optional<Origin> = WTF::nullopt);
 289      Value* framePointer();
 290      void insertConstants();
 291  
 292      B3::Type toB3ResultType(BlockSignature);
 293  
 294  private:
 295      void emitExceptionCheck(CCallHelpers&, ExceptionType);
 296  
 297      void emitEntryTierUpCheck();
 298      void emitLoopTierUpCheck(uint32_t loopIndex, const Stack& enclosingStack);
 299  
 300      void emitWriteBarrierForJSWrapper();
 301      ExpressionType emitCheckAndPreparePointer(ExpressionType pointer, uint32_t offset, uint32_t sizeOfOp);
 302      B3::Kind memoryKind(B3::Opcode memoryOp);
 303      ExpressionType emitLoadOp(LoadOpType, ExpressionType pointer, uint32_t offset);
 304      void emitStoreOp(StoreOpType, ExpressionType pointer, ExpressionType value, uint32_t offset);
 305  
 306      ExpressionType sanitizeAtomicResult(ExtAtomicOpType, Type, ExpressionType result);
 307      ExpressionType emitAtomicLoadOp(ExtAtomicOpType, Type, ExpressionType pointer, uint32_t offset);
 308      void emitAtomicStoreOp(ExtAtomicOpType, Type, ExpressionType pointer, ExpressionType value, uint32_t offset);
 309      ExpressionType emitAtomicBinaryRMWOp(ExtAtomicOpType, Type, ExpressionType pointer, ExpressionType value, uint32_t offset);
 310      ExpressionType emitAtomicCompareExchange(ExtAtomicOpType, Type, ExpressionType pointer, ExpressionType expected, ExpressionType value, uint32_t offset);
 311  
 312      void unify(const ExpressionType phi, const ExpressionType source);
 313      void unifyValuesWithBlock(const Stack& resultStack, const ResultList& stack);
 314  
 315      void emitChecksForModOrDiv(B3::Opcode, ExpressionType left, ExpressionType right);
 316  
 317      int32_t WARN_UNUSED_RETURN fixupPointerPlusOffset(ExpressionType&, uint32_t);
 318      ExpressionType WARN_UNUSED_RETURN fixupPointerPlusOffsetForAtomicOps(ExtAtomicOpType, ExpressionType, uint32_t);
 319  
 320      void restoreWasmContextInstance(Procedure&, BasicBlock*, Value*);
 321      enum class RestoreCachedStackLimit { No, Yes };
 322      void restoreWebAssemblyGlobalState(RestoreCachedStackLimit, const MemoryInformation&, Value* instance, Procedure&, BasicBlock*);
 323  
 324      Origin origin();
 325  
 326      uint32_t outerLoopIndex() const
 327      {
 328          if (m_outerLoops.isEmpty())
 329              return UINT32_MAX;
 330          return m_outerLoops.last();
 331      }
 332  
 333      FunctionParser<B3IRGenerator>* m_parser { nullptr };
 334      const ModuleInformation& m_info;
 335      const MemoryMode m_mode { MemoryMode::BoundsChecking };
 336      const CompilationMode m_compilationMode { CompilationMode::BBQMode };
 337      const unsigned m_functionIndex { UINT_MAX };
 338      const unsigned m_loopIndexForOSREntry { UINT_MAX };
 339      TierUpCount* m_tierUp { nullptr };
 340  
 341      Procedure& m_proc;
 342      BasicBlock* m_rootBlock { nullptr };
 343      BasicBlock* m_currentBlock { nullptr };
 344      Vector<uint32_t> m_outerLoops;
 345      Vector<Variable*> m_locals;
 346      Vector<UnlinkedWasmToWasmCall>& m_unlinkedWasmToWasmCalls; // List each call site and the function index whose address it should be patched with.
 347      unsigned& m_osrEntryScratchBufferSize;
 348      HashMap<ValueKey, Value*> m_constantPool;
 349      HashMap<BlockSignature, B3::Type> m_tupleMap;
 350      InsertionSet m_constantInsertionValues;
 351      Value* m_framePointer { nullptr };
 352      GPRReg m_memoryBaseGPR { InvalidGPRReg };
 353      GPRReg m_boundsCheckingSizeGPR { InvalidGPRReg };
 354      GPRReg m_wasmContextInstanceGPR { InvalidGPRReg };
 355      bool m_makesCalls { false };
 356  
 357      Value* m_instanceValue { nullptr }; // Always use the accessor below to ensure the instance value is materialized when used.
 358      bool m_usesInstanceValue { false };
 359      Value* instanceValue()
 360      {
 361          m_usesInstanceValue = true;
 362          return m_instanceValue;
 363      }
 364  
 365      uint32_t m_maxNumJSCallArguments { 0 };
 366      unsigned m_numImportFunctions;
 367  };
 368  
 369  // Memory accesses in WebAssembly have unsigned 32-bit offsets, whereas they have signed 32-bit offsets in B3.
 370  int32_t B3IRGenerator::fixupPointerPlusOffset(ExpressionType& ptr, uint32_t offset)
 371  {
 372      if (static_cast<uint64_t>(offset) > static_cast<uint64_t>(std::numeric_limits<int32_t>::max())) {
 373          ptr = m_currentBlock->appendNew<Value>(m_proc, Add, origin(), ptr, m_currentBlock->appendNew<Const64Value>(m_proc, origin(), offset));
 374          return 0;
 375      }
 376      return offset;
 377  }
 378  
 379  void B3IRGenerator::restoreWasmContextInstance(Procedure& proc, BasicBlock* block, Value* arg)
 380  {
 381      if (Context::useFastTLS()) {
 382          PatchpointValue* patchpoint = block->appendNew<PatchpointValue>(proc, B3::Void, Origin());
 383          if (CCallHelpers::storeWasmContextInstanceNeedsMacroScratchRegister())
 384              patchpoint->clobber(RegisterSet::macroScratchRegisters());
 385          patchpoint->append(ConstrainedValue(arg, ValueRep::SomeRegister));
 386          patchpoint->setGenerator(
 387              [=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
 388                  AllowMacroScratchRegisterUsageIf allowScratch(jit, CCallHelpers::storeWasmContextInstanceNeedsMacroScratchRegister());
 389                  jit.storeWasmContextInstance(params[0].gpr());
 390              });
 391          return;
 392      }
 393  
 394      // FIXME: Because WasmToWasm call clobbers wasmContextInstance register and does not restore it, we need to restore it in the caller side.
 395      // This prevents us from using ArgumentReg to this (logically) immutable pinned register.
 396      PatchpointValue* patchpoint = block->appendNew<PatchpointValue>(proc, B3::Void, Origin());
 397      Effects effects = Effects::none();
 398      effects.writesPinned = true;
 399      effects.reads = B3::HeapRange::top();
 400      patchpoint->effects = effects;
 401      patchpoint->clobberLate(RegisterSet(m_wasmContextInstanceGPR));
 402      patchpoint->append(arg, ValueRep::SomeRegister);
 403      GPRReg wasmContextInstanceGPR = m_wasmContextInstanceGPR;
 404      patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& param) {
 405          jit.move(param[0].gpr(), wasmContextInstanceGPR);
 406      });
 407  }
 408  
 409  B3IRGenerator::B3IRGenerator(const ModuleInformation& info, Procedure& procedure, InternalFunction* compilation, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, unsigned& osrEntryScratchBufferSize, MemoryMode mode, CompilationMode compilationMode, unsigned functionIndex, unsigned loopIndexForOSREntry, TierUpCount* tierUp)
 410      : m_info(info)
 411      , m_mode(mode)
 412      , m_compilationMode(compilationMode)
 413      , m_functionIndex(functionIndex)
 414      , m_loopIndexForOSREntry(loopIndexForOSREntry)
 415      , m_tierUp(tierUp)
 416      , m_proc(procedure)
 417      , m_unlinkedWasmToWasmCalls(unlinkedWasmToWasmCalls)
 418      , m_osrEntryScratchBufferSize(osrEntryScratchBufferSize)
 419      , m_constantInsertionValues(m_proc)
 420      , m_numImportFunctions(info.importFunctionCount())
 421  {
 422      m_rootBlock = m_proc.addBlock();
 423      m_currentBlock = m_rootBlock;
 424  
 425      // FIXME we don't really need to pin registers here if there's no memory. It makes wasm -> wasm thunks simpler for now. https://bugs.webkit.org/show_bug.cgi?id=166623
 426      const PinnedRegisterInfo& pinnedRegs = PinnedRegisterInfo::get();
 427  
 428      m_memoryBaseGPR = pinnedRegs.baseMemoryPointer;
 429      m_proc.pinRegister(m_memoryBaseGPR);
 430  
 431      m_wasmContextInstanceGPR = pinnedRegs.wasmContextInstancePointer;
 432      if (!Context::useFastTLS())
 433          m_proc.pinRegister(m_wasmContextInstanceGPR);
 434  
 435      if (mode != MemoryMode::Signaling) {
 436          m_boundsCheckingSizeGPR = pinnedRegs.boundsCheckingSizeRegister;
 437          m_proc.pinRegister(m_boundsCheckingSizeGPR);
 438      }
 439  
 440      if (info.memory) {
 441          m_proc.setWasmBoundsCheckGenerator([=] (CCallHelpers& jit, GPRReg pinnedGPR) {
 442              AllowMacroScratchRegisterUsage allowScratch(jit);
 443              switch (m_mode) {
 444              case MemoryMode::BoundsChecking:
 445                  ASSERT_UNUSED(pinnedGPR, m_boundsCheckingSizeGPR == pinnedGPR);
 446                  break;
 447              case MemoryMode::Signaling:
 448                  ASSERT_UNUSED(pinnedGPR, InvalidGPRReg == pinnedGPR);
 449                  break;
 450              }
 451              this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsMemoryAccess);
 452          });
 453      }
 454  
 455      {
 456          auto* calleeMoveLocation = &compilation->calleeMoveLocation;
 457          static_assert(CallFrameSlot::codeBlock * sizeof(Register) < WasmCallingConvention::headerSizeInBytes, "We rely on this here for now.");
 458          static_assert(CallFrameSlot::callee * sizeof(Register) < WasmCallingConvention::headerSizeInBytes, "We rely on this here for now.");
 459          B3::PatchpointValue* getCalleePatchpoint = m_currentBlock->appendNew<B3::PatchpointValue>(m_proc, B3::Int64, Origin());
 460          getCalleePatchpoint->resultConstraints = { B3::ValueRep::SomeRegister };
 461          getCalleePatchpoint->effects = B3::Effects::none();
 462          getCalleePatchpoint->setGenerator(
 463              [=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
 464                  GPRReg result = params[0].gpr();
 465                  MacroAssembler::DataLabelPtr moveLocation = jit.moveWithPatch(MacroAssembler::TrustedImmPtr(nullptr), result);
 466                  jit.addLinkTask([calleeMoveLocation, moveLocation] (LinkBuffer& linkBuffer) {
 467                      *calleeMoveLocation = linkBuffer.locationOf<WasmEntryPtrTag>(moveLocation);
 468                  });
 469              });
 470  
 471          B3::Value* offsetOfCallee = m_currentBlock->appendNew<B3::Const64Value>(m_proc, Origin(), CallFrameSlot::callee * sizeof(Register));
 472          m_currentBlock->appendNew<B3::MemoryValue>(m_proc, B3::Store, Origin(),
 473              getCalleePatchpoint,
 474              m_currentBlock->appendNew<B3::Value>(m_proc, B3::Add, Origin(), framePointer(), offsetOfCallee));
 475  
 476          // FIXME: We shouldn't have to store zero into the CodeBlock* spot in the call frame,
 477          // but there are places that interpret non-null CodeBlock slot to mean a valid CodeBlock.
 478          // When doing unwinding, we'll need to verify that the entire runtime is OK with a non-null
 479          // CodeBlock not implying that the CodeBlock is valid.
 480          // https://bugs.webkit.org/show_bug.cgi?id=165321
 481          B3::Value* offsetOfCodeBlock = m_currentBlock->appendNew<B3::Const64Value>(m_proc, Origin(), CallFrameSlot::codeBlock * sizeof(Register));
 482          m_currentBlock->appendNew<B3::MemoryValue>(m_proc, B3::Store, Origin(),
 483              m_currentBlock->appendNew<B3::Const64Value>(m_proc, Origin(), 0),
 484              m_currentBlock->appendNew<B3::Value>(m_proc, B3::Add, Origin(), framePointer(), offsetOfCodeBlock));
 485      }
 486  
 487      {
 488          B3::PatchpointValue* stackOverflowCheck = m_currentBlock->appendNew<B3::PatchpointValue>(m_proc, pointerType(), Origin());
 489          m_instanceValue = stackOverflowCheck;
 490          stackOverflowCheck->appendSomeRegister(framePointer());
 491          stackOverflowCheck->clobber(RegisterSet::macroScratchRegisters());
 492          if (!Context::useFastTLS()) {
 493              // FIXME: Because WasmToWasm call clobbers wasmContextInstance register and does not restore it, we need to restore it in the caller side.
 494              // This prevents us from using ArgumentReg to this (logically) immutable pinned register.
 495              stackOverflowCheck->effects.writesPinned = false;
 496              stackOverflowCheck->effects.readsPinned = true;
 497              stackOverflowCheck->resultConstraints = { ValueRep::reg(m_wasmContextInstanceGPR) };
 498          }
 499          stackOverflowCheck->numGPScratchRegisters = 2;
 500          stackOverflowCheck->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
 501              const Checked<int32_t> wasmFrameSize = params.proc().frameSize();
 502              const unsigned minimumParentCheckSize = WTF::roundUpToMultipleOf(stackAlignmentBytes(), 1024);
 503              const unsigned extraFrameSize = WTF::roundUpToMultipleOf(stackAlignmentBytes(), std::max<uint32_t>(
 504                  // This allows us to elide stack checks for functions that are terminal nodes in the call
 505                  // tree, (e.g they don't make any calls) and have a small enough frame size. This works by
 506                  // having any such terminal node have its parent caller include some extra size in its
 507                  // own check for it. The goal here is twofold:
 508                  // 1. Emit less code.
 509                  // 2. Try to speed things up by skipping stack checks.
 510                  minimumParentCheckSize,
 511                  // This allows us to elide stack checks in the Wasm -> Embedder call IC stub. Since these will
 512                  // spill all arguments to the stack, we ensure that a stack check here covers the
 513                  // stack that such a stub would use.
 514                  (Checked<uint32_t>(m_maxNumJSCallArguments) * sizeof(Register) + JSCallingConvention::headerSizeInBytes).unsafeGet()
 515              ));
 516              const int32_t checkSize = m_makesCalls ? (wasmFrameSize + extraFrameSize).unsafeGet() : wasmFrameSize.unsafeGet();
 517              bool needUnderflowCheck = static_cast<unsigned>(checkSize) > Options::reservedZoneSize();
 518              bool needsOverflowCheck = m_makesCalls || wasmFrameSize >= minimumParentCheckSize || needUnderflowCheck;
 519  
 520              GPRReg contextInstance = Context::useFastTLS() ? params[0].gpr() : m_wasmContextInstanceGPR;
 521  
 522              // This allows leaf functions to not do stack checks if their frame size is within
 523              // certain limits since their caller would have already done the check.
 524              if (needsOverflowCheck) {
 525                  AllowMacroScratchRegisterUsage allowScratch(jit);
 526                  GPRReg fp = params[1].gpr();
 527                  GPRReg scratch1 = params.gpScratch(0);
 528                  GPRReg scratch2 = params.gpScratch(1);
 529  
 530                  if (Context::useFastTLS())
 531                      jit.loadWasmContextInstance(contextInstance);
 532  
 533                  jit.loadPtr(CCallHelpers::Address(contextInstance, Instance::offsetOfCachedStackLimit()), scratch2);
 534                  jit.addPtr(CCallHelpers::TrustedImm32(-checkSize), fp, scratch1);
 535                  MacroAssembler::JumpList overflow;
 536                  if (UNLIKELY(needUnderflowCheck))
 537                      overflow.append(jit.branchPtr(CCallHelpers::Above, scratch1, fp));
 538                  overflow.append(jit.branchPtr(CCallHelpers::Below, scratch1, scratch2));
 539                  jit.addLinkTask([overflow] (LinkBuffer& linkBuffer) {
 540                      linkBuffer.link(overflow, CodeLocationLabel<JITThunkPtrTag>(Thunks::singleton().stub(throwStackOverflowFromWasmThunkGenerator).code()));
 541                  });
 542              } else if (m_usesInstanceValue && Context::useFastTLS()) {
 543                  // No overflow check is needed, but the instance values still needs to be correct.
 544                  AllowMacroScratchRegisterUsageIf allowScratch(jit, CCallHelpers::loadWasmContextInstanceNeedsMacroScratchRegister());
 545                  jit.loadWasmContextInstance(contextInstance);
 546              } else {
 547                  // We said we'd return a pointer. We don't actually need to because it isn't used, but the patchpoint conservatively said it had effects (potential stack check) which prevent it from getting removed.
 548              }
 549          });
 550      }
 551  
 552      emitEntryTierUpCheck();
 553  
 554      if (m_compilationMode == CompilationMode::OMGForOSREntryMode)
 555          m_currentBlock = m_proc.addBlock();
 556  }
 557  
 558  void B3IRGenerator::restoreWebAssemblyGlobalState(RestoreCachedStackLimit restoreCachedStackLimit, const MemoryInformation& memory, Value* instance, Procedure& proc, BasicBlock* block)
 559  {
 560      restoreWasmContextInstance(proc, block, instance);
 561  
 562      if (restoreCachedStackLimit == RestoreCachedStackLimit::Yes) {
 563          // The Instance caches the stack limit, but also knows where its canonical location is.
 564          Value* pointerToActualStackLimit = block->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(), instanceValue(), safeCast<int32_t>(Instance::offsetOfPointerToActualStackLimit()));
 565          Value* actualStackLimit = block->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(), pointerToActualStackLimit);
 566          block->appendNew<MemoryValue>(m_proc, Store, origin(), actualStackLimit, instanceValue(), safeCast<int32_t>(Instance::offsetOfCachedStackLimit()));
 567      }
 568  
 569      if (!!memory) {
 570          const PinnedRegisterInfo* pinnedRegs = &PinnedRegisterInfo::get();
 571          RegisterSet clobbers;
 572          clobbers.set(pinnedRegs->baseMemoryPointer);
 573          clobbers.set(pinnedRegs->boundsCheckingSizeRegister);
 574          if (!isARM64())
 575              clobbers.set(RegisterSet::macroScratchRegisters());
 576  
 577          B3::PatchpointValue* patchpoint = block->appendNew<B3::PatchpointValue>(proc, B3::Void, origin());
 578          Effects effects = Effects::none();
 579          effects.writesPinned = true;
 580          effects.reads = B3::HeapRange::top();
 581          patchpoint->effects = effects;
 582          patchpoint->clobber(clobbers);
 583          patchpoint->numGPScratchRegisters = Gigacage::isEnabled(Gigacage::Primitive) ? 1 : 0;
 584  
 585          patchpoint->append(instance, ValueRep::SomeRegister);
 586          patchpoint->setGenerator([pinnedRegs] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
 587              AllowMacroScratchRegisterUsage allowScratch(jit);
 588              GPRReg baseMemory = pinnedRegs->baseMemoryPointer;
 589              GPRReg scratchOrBoundsCheckingSize = Gigacage::isEnabled(Gigacage::Primitive) ? params.gpScratch(0) : pinnedRegs->boundsCheckingSizeRegister;
 590  
 591              jit.loadPtr(CCallHelpers::Address(params[0].gpr(), Instance::offsetOfCachedBoundsCheckingSize()), pinnedRegs->boundsCheckingSizeRegister);
 592              jit.loadPtr(CCallHelpers::Address(params[0].gpr(), Instance::offsetOfCachedMemory()), baseMemory);
 593  
 594              jit.cageConditionally(Gigacage::Primitive, baseMemory, pinnedRegs->boundsCheckingSizeRegister, scratchOrBoundsCheckingSize);
 595          });
 596      }
 597  }
 598  
 599  void B3IRGenerator::emitExceptionCheck(CCallHelpers& jit, ExceptionType type)
 600  {
 601      jit.move(CCallHelpers::TrustedImm32(static_cast<uint32_t>(type)), GPRInfo::argumentGPR1);
 602      auto jumpToExceptionStub = jit.jump();
 603  
 604      jit.addLinkTask([jumpToExceptionStub] (LinkBuffer& linkBuffer) {
 605          linkBuffer.link(jumpToExceptionStub, CodeLocationLabel<JITThunkPtrTag>(Thunks::singleton().stub(throwExceptionFromWasmThunkGenerator).code()));
 606      });
 607  }
 608  
 609  Value* B3IRGenerator::constant(B3::Type type, uint64_t bits, Optional<Origin> maybeOrigin)
 610  {
 611      auto result = m_constantPool.ensure(ValueKey(opcodeForConstant(type), type, static_cast<int64_t>(bits)), [&] {
 612          Value* result = m_proc.addConstant(maybeOrigin ? *maybeOrigin : origin(), type, bits);
 613          m_constantInsertionValues.insertValue(0, result);
 614          return result;
 615      });
 616      return result.iterator->value;
 617  }
 618  
 619  Value* B3IRGenerator::framePointer()
 620  {
 621      if (!m_framePointer) {
 622          m_framePointer = m_proc.add<B3::Value>(B3::FramePointer, Origin());
 623          ASSERT(m_framePointer);
 624          m_constantInsertionValues.insertValue(0, m_framePointer);
 625      }
 626      return m_framePointer;
 627  }
 628  
 629  void B3IRGenerator::insertConstants()
 630  {
 631      m_constantInsertionValues.execute(m_proc.at(0));
 632  }
 633  
 634  B3::Type B3IRGenerator::toB3ResultType(BlockSignature returnType)
 635  {
 636      if (returnType->returnsVoid())
 637          return B3::Void;
 638  
 639      if (returnType->returnCount() == 1)
 640          return toB3Type(returnType->returnType(0));
 641  
 642      auto result = m_tupleMap.ensure(returnType, [&] {
 643          Vector<B3::Type> result;
 644          for (unsigned i = 0; i < returnType->returnCount(); ++i)
 645              result.append(toB3Type(returnType->returnType(i)));
 646          return m_proc.addTuple(WTFMove(result));
 647      });
 648      return result.iterator->value;
 649  }
 650  
 651  auto B3IRGenerator::addLocal(Type type, uint32_t count) -> PartialResult
 652  {
 653      size_t newSize = m_locals.size() + count;
 654      ASSERT(!(CheckedUint32(count) + m_locals.size()).hasOverflowed());
 655      ASSERT(newSize <= maxFunctionLocals);
 656      WASM_COMPILE_FAIL_IF(!m_locals.tryReserveCapacity(newSize), "can't allocate memory for ", newSize, " locals");
 657  
 658      for (uint32_t i = 0; i < count; ++i) {
 659          Variable* local = m_proc.addVariable(toB3Type(type));
 660          m_locals.uncheckedAppend(local);
 661          auto val = isRefType(type) ? JSValue::encode(jsNull()) : 0;
 662          m_currentBlock->appendNew<VariableValue>(m_proc, Set, Origin(), local, constant(toB3Type(type), val, Origin()));
 663      }
 664      return { };
 665  }
 666  
 667  auto B3IRGenerator::addArguments(const Signature& signature) -> PartialResult
 668  {
 669      ASSERT(!m_locals.size());
 670      WASM_COMPILE_FAIL_IF(!m_locals.tryReserveCapacity(signature.argumentCount()), "can't allocate memory for ", signature.argumentCount(), " arguments");
 671  
 672      m_locals.grow(signature.argumentCount());
 673      CallInformation wasmCallInfo = wasmCallingConvention().callInformationFor(signature, CallRole::Callee);
 674  
 675      for (size_t i = 0; i < signature.argumentCount(); ++i) {
 676          B3::Type type = toB3Type(signature.argument(i));
 677          B3::Value* argument;
 678          auto rep = wasmCallInfo.params[i];
 679          if (rep.isReg()) {
 680              argument = m_currentBlock->appendNew<B3::ArgumentRegValue>(m_proc, Origin(), rep.reg());
 681              if (type == B3::Int32 || type == B3::Float)
 682                  argument = m_currentBlock->appendNew<B3::Value>(m_proc, B3::Trunc, Origin(), argument);
 683          } else {
 684              ASSERT(rep.isStack());
 685              B3::Value* address = m_currentBlock->appendNew<B3::Value>(m_proc, B3::Add, Origin(), framePointer(),
 686                  m_currentBlock->appendNew<B3::Const64Value>(m_proc, Origin(), rep.offsetFromFP()));
 687              argument = m_currentBlock->appendNew<B3::MemoryValue>(m_proc, B3::Load, type, Origin(), address);
 688          }
 689  
 690          Variable* argumentVariable = m_proc.addVariable(argument->type());
 691          m_locals[i] = argumentVariable;
 692          m_currentBlock->appendNew<VariableValue>(m_proc, Set, Origin(), argumentVariable, argument);
 693      }
 694  
 695      return { };
 696  }
 697  
 698  auto B3IRGenerator::addRefIsNull(ExpressionType value, ExpressionType& result) -> PartialResult
 699  {
 700      result = m_currentBlock->appendNew<Value>(m_proc, B3::Equal, origin(), value, m_currentBlock->appendNew<Const64Value>(m_proc, origin(), JSValue::encode(jsNull())));
 701      return { };
 702  }
 703  
 704  auto B3IRGenerator::addTableGet(unsigned tableIndex, ExpressionType index, ExpressionType& result) -> PartialResult
 705  {
 706      // FIXME: Emit this inline <https://bugs.webkit.org/show_bug.cgi?id=198506>.
 707      result = m_currentBlock->appendNew<CCallValue>(m_proc, toB3Type(Externref), origin(),
 708          m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunction<OperationPtrTag>(operationGetWasmTableElement)),
 709          instanceValue(), m_currentBlock->appendNew<Const32Value>(m_proc, origin(), tableIndex), index);
 710  
 711      {
 712          CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(),
 713              m_currentBlock->appendNew<Value>(m_proc, Equal, origin(), result, m_currentBlock->appendNew<Const64Value>(m_proc, origin(), 0)));
 714  
 715          check->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
 716              this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTableAccess);
 717          });
 718      }
 719  
 720      return { };
 721  }
 722  
 723  auto B3IRGenerator::addTableSet(unsigned tableIndex, ExpressionType index, ExpressionType value) -> PartialResult
 724  {
 725      // FIXME: Emit this inline <https://bugs.webkit.org/show_bug.cgi?id=198506>.
 726      auto shouldThrow = m_currentBlock->appendNew<CCallValue>(m_proc, B3::Int32, origin(),
 727          m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunction<OperationPtrTag>(operationSetWasmTableElement)),
 728          instanceValue(), m_currentBlock->appendNew<Const32Value>(m_proc, origin(), tableIndex), index, value);
 729  
 730      {
 731          CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(),
 732              m_currentBlock->appendNew<Value>(m_proc, Equal, origin(), shouldThrow, m_currentBlock->appendNew<Const32Value>(m_proc, origin(), 0)));
 733  
 734          check->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
 735              this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTableAccess);
 736          });
 737      }
 738  
 739      return { };
 740  }
 741  
 742  auto B3IRGenerator::addRefFunc(uint32_t index, ExpressionType& result) -> PartialResult
 743  {
 744      // FIXME: Emit this inline <https://bugs.webkit.org/show_bug.cgi?id=198506>.
 745  
 746      result = m_currentBlock->appendNew<CCallValue>(m_proc, B3::Int64, origin(),
 747          m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunction<OperationPtrTag>(operationWasmRefFunc)),
 748          instanceValue(), addConstant(Type::I32, index));
 749  
 750      return { };
 751  }
 752  
 753  auto B3IRGenerator::addTableInit(unsigned elementIndex, unsigned tableIndex, ExpressionType dstOffset, ExpressionType srcOffset, ExpressionType length) -> PartialResult
 754  {
 755      auto result = m_currentBlock->appendNew<CCallValue>(
 756          m_proc, toB3Type(I32), origin(),
 757          m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunction<OperationPtrTag>(operationWasmTableInit)),
 758          instanceValue(),
 759          m_currentBlock->appendNew<Const32Value>(m_proc, origin(), elementIndex),
 760          m_currentBlock->appendNew<Const32Value>(m_proc, origin(), tableIndex),
 761          dstOffset, srcOffset, length);
 762  
 763      {
 764          CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(),
 765              m_currentBlock->appendNew<Value>(m_proc, Equal, origin(), result, m_currentBlock->appendNew<Const32Value>(m_proc, origin(), 0)));
 766  
 767          check->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
 768              this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTableAccess);
 769          });
 770      }
 771  
 772      return { };
 773  }
 774  
 775  auto B3IRGenerator::addElemDrop(unsigned elementIndex) -> PartialResult
 776  {
 777      m_currentBlock->appendNew<CCallValue>(
 778          m_proc, B3::Void, origin(),
 779          m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunction<OperationPtrTag>(operationWasmElemDrop)),
 780          instanceValue(),
 781          m_currentBlock->appendNew<Const32Value>(m_proc, origin(), elementIndex));
 782  
 783      return { };
 784  }
 785  
 786  auto B3IRGenerator::addTableSize(unsigned tableIndex, ExpressionType& result) -> PartialResult
 787  {
 788      // FIXME: Emit this inline <https://bugs.webkit.org/show_bug.cgi?id=198506>.
 789      result = m_currentBlock->appendNew<CCallValue>(m_proc, toB3Type(I32), origin(),
 790          m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunction<OperationPtrTag>(operationGetWasmTableSize)),
 791          instanceValue(), m_currentBlock->appendNew<Const32Value>(m_proc, origin(), tableIndex));
 792  
 793      return { };
 794  }
 795  
 796  auto B3IRGenerator::addTableGrow(unsigned tableIndex, ExpressionType fill, ExpressionType delta, ExpressionType& result) -> PartialResult
 797  {
 798      result = m_currentBlock->appendNew<CCallValue>(m_proc, toB3Type(I32), origin(),
 799          m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunction<OperationPtrTag>(operationWasmTableGrow)),
 800          instanceValue(), m_currentBlock->appendNew<Const32Value>(m_proc, origin(), tableIndex), fill, delta);
 801  
 802      return { };
 803  }
 804  
 805  auto B3IRGenerator::addTableFill(unsigned tableIndex, ExpressionType offset, ExpressionType fill, ExpressionType count) -> PartialResult
 806  {
 807      auto result = m_currentBlock->appendNew<CCallValue>(m_proc, toB3Type(I32), origin(),
 808          m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunction<OperationPtrTag>(operationWasmTableFill)),
 809          instanceValue(), m_currentBlock->appendNew<Const32Value>(m_proc, origin(), tableIndex), offset, fill, count);
 810  
 811      {
 812          CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(),
 813              m_currentBlock->appendNew<Value>(m_proc, Equal, origin(), result, m_currentBlock->appendNew<Const32Value>(m_proc, origin(), 0)));
 814  
 815          check->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
 816              this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTableAccess);
 817          });
 818      }
 819  
 820      return { };
 821  }
 822  
 823  auto B3IRGenerator::addTableCopy(unsigned dstTableIndex, unsigned srcTableIndex, ExpressionType dstOffset, ExpressionType srcOffset, ExpressionType length) -> PartialResult
 824  {
 825      auto result = m_currentBlock->appendNew<CCallValue>(
 826          m_proc, toB3Type(I32), origin(),
 827          m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunction<OperationPtrTag>(operationWasmTableCopy)),
 828          instanceValue(),
 829          m_currentBlock->appendNew<Const32Value>(m_proc, origin(), dstTableIndex),
 830          m_currentBlock->appendNew<Const32Value>(m_proc, origin(), srcTableIndex),
 831          dstOffset, srcOffset, length);
 832  
 833      {
 834          CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(),
 835              m_currentBlock->appendNew<Value>(m_proc, Equal, origin(), result, m_currentBlock->appendNew<Const32Value>(m_proc, origin(), 0)));
 836  
 837          check->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
 838              this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTableAccess);
 839          });
 840      }
 841  
 842      return { };
 843  }
 844  
 845  auto B3IRGenerator::getLocal(uint32_t index, ExpressionType& result) -> PartialResult
 846  {
 847      ASSERT(m_locals[index]);
 848      result = m_currentBlock->appendNew<VariableValue>(m_proc, B3::Get, origin(), m_locals[index]);
 849      return { };
 850  }
 851  
 852  auto B3IRGenerator::addUnreachable() -> PartialResult
 853  {
 854      B3::PatchpointValue* unreachable = m_currentBlock->appendNew<B3::PatchpointValue>(m_proc, B3::Void, origin());
 855      unreachable->setGenerator([this] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
 856          this->emitExceptionCheck(jit, ExceptionType::Unreachable);
 857      });
 858      unreachable->effects.terminal = true;
 859      return { };
 860  }
 861  
 862  auto B3IRGenerator::addGrowMemory(ExpressionType delta, ExpressionType& result) -> PartialResult
 863  {
 864      result = m_currentBlock->appendNew<CCallValue>(m_proc, Int32, origin(),
 865          m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunction<OperationPtrTag>(operationGrowMemory)),
 866          framePointer(), instanceValue(), delta);
 867  
 868      restoreWebAssemblyGlobalState(RestoreCachedStackLimit::No, m_info.memory, instanceValue(), m_proc, m_currentBlock);
 869  
 870      return { };
 871  }
 872  
 873  auto B3IRGenerator::addCurrentMemory(ExpressionType& result) -> PartialResult
 874  {
 875      static_assert(sizeof(decltype(static_cast<Memory*>(nullptr)->size())) == sizeof(uint64_t), "codegen relies on this size");
 876      Value* memory = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Int64, origin(), instanceValue(), safeCast<int32_t>(Instance::offsetOfMemory()));
 877      Value* handle = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Int64, origin(), memory, safeCast<int32_t>(Memory::offsetOfHandle()));
 878      Value* size = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Int64, origin(), handle, safeCast<int32_t>(MemoryHandle::offsetOfSize()));
 879  
 880      constexpr uint32_t shiftValue = 16;
 881      static_assert(PageCount::pageSize == 1ull << shiftValue, "This must hold for the code below to be correct.");
 882      Value* numPages = m_currentBlock->appendNew<Value>(m_proc, ZShr, origin(),
 883          size, m_currentBlock->appendNew<Const32Value>(m_proc, origin(), shiftValue));
 884  
 885      result = m_currentBlock->appendNew<Value>(m_proc, Trunc, origin(), numPages);
 886  
 887      return { };
 888  }
 889  
 890  auto B3IRGenerator::addMemoryFill(ExpressionType dstAddress, ExpressionType targetValue, ExpressionType count) -> PartialResult
 891  {
 892      auto result = m_currentBlock->appendNew<CCallValue>(
 893          m_proc, toB3Type(I32), origin(),
 894          m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunction<OperationPtrTag>(operationWasmMemoryFill)),
 895          instanceValue(),
 896          dstAddress, targetValue, count);
 897  
 898      {
 899          CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(),
 900              m_currentBlock->appendNew<Value>(m_proc, Equal, origin(), result, m_currentBlock->appendNew<Const32Value>(m_proc, origin(), 0)));
 901  
 902          check->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
 903              this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTableAccess);
 904          });
 905      }
 906  
 907      return { };
 908  }
 909  
 910  auto B3IRGenerator::addMemoryInit(unsigned dataSegmentIndex, ExpressionType dstAddress, ExpressionType srcAddress, ExpressionType length) -> PartialResult
 911  {
 912      auto result = m_currentBlock->appendNew<CCallValue>(
 913          m_proc, toB3Type(I32), origin(),
 914          m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunction<OperationPtrTag>(operationWasmMemoryInit)),
 915          instanceValue(),
 916          m_currentBlock->appendNew<Const32Value>(m_proc, origin(), dataSegmentIndex),
 917          dstAddress, srcAddress, length);
 918  
 919      {
 920          CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(),
 921              m_currentBlock->appendNew<Value>(m_proc, Equal, origin(), result, m_currentBlock->appendNew<Const32Value>(m_proc, origin(), 0)));
 922  
 923          check->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
 924              this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTableAccess);
 925          });
 926      }
 927  
 928      return { };
 929  }
 930  
 931  auto B3IRGenerator::addMemoryCopy(ExpressionType dstAddress, ExpressionType srcAddress, ExpressionType count) -> PartialResult
 932  {
 933      auto result = m_currentBlock->appendNew<CCallValue>(
 934          m_proc, toB3Type(I32), origin(),
 935          m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunction<OperationPtrTag>(operationWasmMemoryCopy)),
 936          instanceValue(),
 937          dstAddress, srcAddress, count);
 938  
 939      {
 940          CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(),
 941              m_currentBlock->appendNew<Value>(m_proc, Equal, origin(), result, m_currentBlock->appendNew<Const32Value>(m_proc, origin(), 0)));
 942  
 943          check->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
 944              this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTableAccess);
 945          });
 946      }
 947  
 948      return { };
 949  }
 950  
 951  auto B3IRGenerator::addDataDrop(unsigned dataSegmentIndex) -> PartialResult
 952  {
 953      m_currentBlock->appendNew<CCallValue>(
 954          m_proc, B3::Void, origin(),
 955          m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunction<OperationPtrTag>(operationWasmDataDrop)),
 956          instanceValue(),
 957          m_currentBlock->appendNew<Const32Value>(m_proc, origin(), dataSegmentIndex));
 958  
 959      return { };
 960  }
 961  
 962  auto B3IRGenerator::setLocal(uint32_t index, ExpressionType value) -> PartialResult
 963  {
 964      ASSERT(m_locals[index]);
 965      m_currentBlock->appendNew<VariableValue>(m_proc, B3::Set, origin(), m_locals[index], value);
 966      return { };
 967  }
 968  
 969  auto B3IRGenerator::getGlobal(uint32_t index, ExpressionType& result) -> PartialResult
 970  {
 971      const Wasm::GlobalInformation& global = m_info.globals[index];
 972      Value* globalsArray = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(), instanceValue(), safeCast<int32_t>(Instance::offsetOfGlobals()));
 973      switch (global.bindingMode) {
 974      case Wasm::GlobalInformation::BindingMode::EmbeddedInInstance:
 975          result = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, toB3Type(global.type), origin(), globalsArray, safeCast<int32_t>(index * sizeof(Register)));
 976          break;
 977      case Wasm::GlobalInformation::BindingMode::Portable: {
 978          ASSERT(global.mutability == Wasm::GlobalInformation::Mutability::Mutable);
 979          Value* pointer = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, B3::Int64, origin(), globalsArray, safeCast<int32_t>(index * sizeof(Register)));
 980          result = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, toB3Type(global.type), origin(), pointer);
 981          break;
 982      }
 983      }
 984      return { };
 985  }
 986  
 987  auto B3IRGenerator::setGlobal(uint32_t index, ExpressionType value) -> PartialResult
 988  {
 989      const Wasm::GlobalInformation& global = m_info.globals[index];
 990      ASSERT(toB3Type(global.type) == value->type());
 991      Value* globalsArray = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(), instanceValue(), safeCast<int32_t>(Instance::offsetOfGlobals()));
 992      switch (global.bindingMode) {
 993      case Wasm::GlobalInformation::BindingMode::EmbeddedInInstance:
 994          m_currentBlock->appendNew<MemoryValue>(m_proc, Store, origin(), value, globalsArray, safeCast<int32_t>(index * sizeof(Register)));
 995          if (isRefType(global.type))
 996              emitWriteBarrierForJSWrapper();
 997          break;
 998      case Wasm::GlobalInformation::BindingMode::Portable: {
 999          ASSERT(global.mutability == Wasm::GlobalInformation::Mutability::Mutable);
1000          Value* pointer = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, B3::Int64, origin(), globalsArray, safeCast<int32_t>(index * sizeof(Register)));
1001          m_currentBlock->appendNew<MemoryValue>(m_proc, Store, origin(), value, pointer);
1002          // We emit a write-barrier onto JSWebAssemblyGlobal, not JSWebAssemblyInstance.
1003          if (isRefType(global.type)) {
1004              Value* instance = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(), instanceValue(), safeCast<int32_t>(Instance::offsetOfOwner()));
1005              Value* cell = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(), pointer, Wasm::Global::offsetOfOwner() - Wasm::Global::offsetOfValue());
1006              Value* cellState = m_currentBlock->appendNew<MemoryValue>(m_proc, Load8Z, Int32, origin(), cell, safeCast<int32_t>(JSCell::cellStateOffset()));
1007              Value* vm = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(), instance, safeCast<int32_t>(JSWebAssemblyInstance::offsetOfVM()));
1008              Value* threshold = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Int32, origin(), vm, safeCast<int32_t>(VM::offsetOfHeapBarrierThreshold()));
1009  
1010              BasicBlock* fenceCheckPath = m_proc.addBlock();
1011              BasicBlock* fencePath = m_proc.addBlock();
1012              BasicBlock* doSlowPath = m_proc.addBlock();
1013              BasicBlock* continuation = m_proc.addBlock();
1014  
1015              m_currentBlock->appendNewControlValue(m_proc, B3::Branch, origin(),
1016                  m_currentBlock->appendNew<Value>(m_proc, Above, origin(), cellState, threshold),
1017                  FrequentedBlock(continuation), FrequentedBlock(fenceCheckPath, FrequencyClass::Rare));
1018              fenceCheckPath->addPredecessor(m_currentBlock);
1019              continuation->addPredecessor(m_currentBlock);
1020              m_currentBlock = fenceCheckPath;
1021  
1022              Value* shouldFence = m_currentBlock->appendNew<MemoryValue>(m_proc, Load8Z, Int32, origin(), vm, safeCast<int32_t>(VM::offsetOfHeapMutatorShouldBeFenced()));
1023              m_currentBlock->appendNewControlValue(m_proc, B3::Branch, origin(),
1024                  shouldFence,
1025                  FrequentedBlock(fencePath), FrequentedBlock(doSlowPath));
1026              fencePath->addPredecessor(m_currentBlock);
1027              doSlowPath->addPredecessor(m_currentBlock);
1028              m_currentBlock = fencePath;
1029  
1030              B3::PatchpointValue* doFence = m_currentBlock->appendNew<B3::PatchpointValue>(m_proc, B3::Void, origin());
1031              doFence->setGenerator([] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
1032                  jit.memoryFence();
1033              });
1034  
1035              Value* cellStateLoadAfterFence = m_currentBlock->appendNew<MemoryValue>(m_proc, Load8Z, Int32, origin(), cell, safeCast<int32_t>(JSCell::cellStateOffset()));
1036              m_currentBlock->appendNewControlValue(m_proc, B3::Branch, origin(),
1037                  m_currentBlock->appendNew<Value>(m_proc, Above, origin(), cellStateLoadAfterFence, m_currentBlock->appendNew<Const32Value>(m_proc, origin(), blackThreshold)),
1038                  FrequentedBlock(continuation), FrequentedBlock(doSlowPath, FrequencyClass::Rare));
1039              doSlowPath->addPredecessor(m_currentBlock);
1040              continuation->addPredecessor(m_currentBlock);
1041              m_currentBlock = doSlowPath;
1042  
1043              Value* writeBarrierAddress = m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunction<OperationPtrTag>(operationWasmWriteBarrierSlowPath));
1044              m_currentBlock->appendNew<CCallValue>(m_proc, B3::Void, origin(), writeBarrierAddress, cell, vm);
1045              m_currentBlock->appendNewControlValue(m_proc, Jump, origin(), continuation);
1046  
1047              continuation->addPredecessor(m_currentBlock);
1048              m_currentBlock = continuation;
1049          }
1050          break;
1051      }
1052      }
1053      return { };
1054  }
1055  
1056  inline void B3IRGenerator::emitWriteBarrierForJSWrapper()
1057  {
1058      Value* cell = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(), instanceValue(), safeCast<int32_t>(Instance::offsetOfOwner()));
1059      Value* cellState = m_currentBlock->appendNew<MemoryValue>(m_proc, Load8Z, Int32, origin(), cell, safeCast<int32_t>(JSCell::cellStateOffset()));
1060      Value* vm = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(), cell, safeCast<int32_t>(JSWebAssemblyInstance::offsetOfVM()));
1061      Value* threshold = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Int32, origin(), vm, safeCast<int32_t>(VM::offsetOfHeapBarrierThreshold()));
1062  
1063      BasicBlock* fenceCheckPath = m_proc.addBlock();
1064      BasicBlock* fencePath = m_proc.addBlock();
1065      BasicBlock* doSlowPath = m_proc.addBlock();
1066      BasicBlock* continuation = m_proc.addBlock();
1067  
1068      m_currentBlock->appendNewControlValue(m_proc, B3::Branch, origin(),
1069          m_currentBlock->appendNew<Value>(m_proc, Above, origin(), cellState, threshold),
1070          FrequentedBlock(continuation), FrequentedBlock(fenceCheckPath, FrequencyClass::Rare));
1071      fenceCheckPath->addPredecessor(m_currentBlock);
1072      continuation->addPredecessor(m_currentBlock);
1073      m_currentBlock = fenceCheckPath;
1074  
1075      Value* shouldFence = m_currentBlock->appendNew<MemoryValue>(m_proc, Load8Z, Int32, origin(), vm, safeCast<int32_t>(VM::offsetOfHeapMutatorShouldBeFenced()));
1076      m_currentBlock->appendNewControlValue(m_proc, B3::Branch, origin(),
1077          shouldFence,
1078          FrequentedBlock(fencePath), FrequentedBlock(doSlowPath));
1079      fencePath->addPredecessor(m_currentBlock);
1080      doSlowPath->addPredecessor(m_currentBlock);
1081      m_currentBlock = fencePath;
1082  
1083      B3::PatchpointValue* doFence = m_currentBlock->appendNew<B3::PatchpointValue>(m_proc, B3::Void, origin());
1084      doFence->setGenerator([] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
1085          jit.memoryFence();
1086      });
1087  
1088      Value* cellStateLoadAfterFence = m_currentBlock->appendNew<MemoryValue>(m_proc, Load8Z, Int32, origin(), cell, safeCast<int32_t>(JSCell::cellStateOffset()));
1089      m_currentBlock->appendNewControlValue(m_proc, B3::Branch, origin(),
1090          m_currentBlock->appendNew<Value>(m_proc, Above, origin(), cellStateLoadAfterFence, m_currentBlock->appendNew<Const32Value>(m_proc, origin(), blackThreshold)),
1091          FrequentedBlock(continuation), FrequentedBlock(doSlowPath, FrequencyClass::Rare));
1092      doSlowPath->addPredecessor(m_currentBlock);
1093      continuation->addPredecessor(m_currentBlock);
1094      m_currentBlock = doSlowPath;
1095  
1096      Value* writeBarrierAddress = m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunction<OperationPtrTag>(operationWasmWriteBarrierSlowPath));
1097      m_currentBlock->appendNew<CCallValue>(m_proc, B3::Void, origin(), writeBarrierAddress, cell, vm);
1098      m_currentBlock->appendNewControlValue(m_proc, Jump, origin(), continuation);
1099  
1100      continuation->addPredecessor(m_currentBlock);
1101      m_currentBlock = continuation;
1102  }
1103  
1104  inline Value* B3IRGenerator::emitCheckAndPreparePointer(ExpressionType pointer, uint32_t offset, uint32_t sizeOfOperation)
1105  {
1106      ASSERT(m_memoryBaseGPR);
1107  
1108      switch (m_mode) {
1109      case MemoryMode::BoundsChecking: {
1110          // We're not using signal handling only when the memory is not shared.
1111          // Regardless of signaling, we must check that no memory access exceeds the current memory size.
1112          ASSERT(m_boundsCheckingSizeGPR);
1113          ASSERT(sizeOfOperation + offset > offset);
1114          m_currentBlock->appendNew<WasmBoundsCheckValue>(m_proc, origin(), m_boundsCheckingSizeGPR, pointer, sizeOfOperation + offset - 1);
1115          break;
1116      }
1117  
1118      case MemoryMode::Signaling: {
1119          // We've virtually mapped 4GiB+redzone for this memory. Only the user-allocated pages are addressable, contiguously in range [0, current],
1120          // and everything above is mapped PROT_NONE. We don't need to perform any explicit bounds check in the 4GiB range because WebAssembly register
1121          // memory accesses are 32-bit. However WebAssembly register + offset accesses perform the addition in 64-bit which can push an access above
1122          // the 32-bit limit (the offset is unsigned 32-bit). The redzone will catch most small offsets, and we'll explicitly bounds check any
1123          // register + large offset access. We don't think this will be generated frequently.
1124          //
1125          // We could check that register + large offset doesn't exceed 4GiB+redzone since that's technically the limit we need to avoid overflowing the
1126          // PROT_NONE region, but it's better if we use a smaller immediate because it can codegens better. We know that anything equal to or greater
1127          // than the declared 'maximum' will trap, so we can compare against that number. If there was no declared 'maximum' then we still know that
1128          // any access equal to or greater than 4GiB will trap, no need to add the redzone.
1129          if (offset >= Memory::fastMappedRedzoneBytes()) {
1130              size_t maximum = m_info.memory.maximum() ? m_info.memory.maximum().bytes() : std::numeric_limits<uint32_t>::max();
1131              m_currentBlock->appendNew<WasmBoundsCheckValue>(m_proc, origin(), pointer, sizeOfOperation + offset - 1, maximum);
1132          }
1133          break;
1134      }
1135      }
1136  
1137      pointer = m_currentBlock->appendNew<Value>(m_proc, ZExt32, origin(), pointer);
1138      return m_currentBlock->appendNew<WasmAddressValue>(m_proc, origin(), pointer, m_memoryBaseGPR);
1139  }
1140  
1141  inline uint32_t sizeOfLoadOp(LoadOpType op)
1142  {
1143      switch (op) {
1144      case LoadOpType::I32Load8S:
1145      case LoadOpType::I32Load8U:
1146      case LoadOpType::I64Load8S:
1147      case LoadOpType::I64Load8U:
1148          return 1;
1149      case LoadOpType::I32Load16S:
1150      case LoadOpType::I64Load16S:
1151      case LoadOpType::I32Load16U:
1152      case LoadOpType::I64Load16U:
1153          return 2;
1154      case LoadOpType::I32Load:
1155      case LoadOpType::I64Load32S:
1156      case LoadOpType::I64Load32U:
1157      case LoadOpType::F32Load:
1158          return 4;
1159      case LoadOpType::I64Load:
1160      case LoadOpType::F64Load:
1161          return 8;
1162      }
1163      RELEASE_ASSERT_NOT_REACHED();
1164  }
1165  
1166  inline B3::Kind B3IRGenerator::memoryKind(B3::Opcode memoryOp)
1167  {
1168      if (m_mode == MemoryMode::Signaling || m_info.memory.isShared())
1169          return trapping(memoryOp);
1170      return memoryOp;
1171  }
1172  
1173  inline Value* B3IRGenerator::emitLoadOp(LoadOpType op, ExpressionType pointer, uint32_t uoffset)
1174  {
1175      int32_t offset = fixupPointerPlusOffset(pointer, uoffset);
1176  
1177      switch (op) {
1178      case LoadOpType::I32Load8S: {
1179          return m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load8S), origin(), pointer, offset);
1180      }
1181  
1182      case LoadOpType::I64Load8S: {
1183          Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load8S), origin(), pointer, offset);
1184          return m_currentBlock->appendNew<Value>(m_proc, SExt32, origin(), value);
1185      }
1186  
1187      case LoadOpType::I32Load8U: {
1188          return m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load8Z), origin(), pointer, offset);
1189      }
1190  
1191      case LoadOpType::I64Load8U: {
1192          Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load8Z), origin(), pointer, offset);
1193          return m_currentBlock->appendNew<Value>(m_proc, ZExt32, origin(), value);
1194      }
1195  
1196      case LoadOpType::I32Load16S: {
1197          return m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load16S), origin(), pointer, offset);
1198      }
1199  
1200      case LoadOpType::I64Load16S: {
1201          Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load16S), origin(), pointer, offset);
1202          return m_currentBlock->appendNew<Value>(m_proc, SExt32, origin(), value);
1203      }
1204  
1205      case LoadOpType::I32Load16U: {
1206          return m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load16Z), origin(), pointer, offset);
1207      }
1208  
1209      case LoadOpType::I64Load16U: {
1210          Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load16Z), origin(), pointer, offset);
1211          return m_currentBlock->appendNew<Value>(m_proc, ZExt32, origin(), value);
1212      }
1213  
1214      case LoadOpType::I32Load: {
1215          return m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load), Int32, origin(), pointer, offset);
1216      }
1217  
1218      case LoadOpType::I64Load32U: {
1219          Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load), Int32, origin(), pointer, offset);
1220          return m_currentBlock->appendNew<Value>(m_proc, ZExt32, origin(), value);
1221      }
1222  
1223      case LoadOpType::I64Load32S: {
1224          Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load), Int32, origin(), pointer, offset);
1225          return m_currentBlock->appendNew<Value>(m_proc, SExt32, origin(), value);
1226      }
1227  
1228      case LoadOpType::I64Load: {
1229          return m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load), Int64, origin(), pointer, offset);
1230      }
1231  
1232      case LoadOpType::F32Load: {
1233          return m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load), Float, origin(), pointer, offset);
1234      }
1235  
1236      case LoadOpType::F64Load: {
1237          return m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load), Double, origin(), pointer, offset);
1238      }
1239      }
1240      RELEASE_ASSERT_NOT_REACHED();
1241  }
1242  
1243  auto B3IRGenerator::load(LoadOpType op, ExpressionType pointer, ExpressionType& result, uint32_t offset) -> PartialResult
1244  {
1245      ASSERT(pointer->type() == Int32);
1246  
1247      if (UNLIKELY(sumOverflows<uint32_t>(offset, sizeOfLoadOp(op)))) {
1248          // FIXME: Even though this is provably out of bounds, it's not a validation error, so we have to handle it
1249          // as a runtime exception. However, this may change: https://bugs.webkit.org/show_bug.cgi?id=166435
1250          B3::PatchpointValue* throwException = m_currentBlock->appendNew<B3::PatchpointValue>(m_proc, B3::Void, origin());
1251          throwException->setGenerator([this] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
1252              this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsMemoryAccess);
1253          });
1254  
1255          switch (op) {
1256          case LoadOpType::I32Load8S:
1257          case LoadOpType::I32Load16S:
1258          case LoadOpType::I32Load:
1259          case LoadOpType::I32Load16U:
1260          case LoadOpType::I32Load8U:
1261              result = constant(Int32, 0);
1262              break;
1263          case LoadOpType::I64Load8S:
1264          case LoadOpType::I64Load8U:
1265          case LoadOpType::I64Load16S:
1266          case LoadOpType::I64Load32U:
1267          case LoadOpType::I64Load32S:
1268          case LoadOpType::I64Load:
1269          case LoadOpType::I64Load16U:
1270              result = constant(Int64, 0);
1271              break;
1272          case LoadOpType::F32Load:
1273              result = constant(Float, 0);
1274              break;
1275          case LoadOpType::F64Load:
1276              result = constant(Double, 0);
1277              break;
1278          }
1279  
1280      } else
1281          result = emitLoadOp(op, emitCheckAndPreparePointer(pointer, offset, sizeOfLoadOp(op)), offset);
1282  
1283      return { };
1284  }
1285  
1286  inline uint32_t sizeOfStoreOp(StoreOpType op)
1287  {
1288      switch (op) {
1289      case StoreOpType::I32Store8:
1290      case StoreOpType::I64Store8:
1291          return 1;
1292      case StoreOpType::I32Store16:
1293      case StoreOpType::I64Store16:
1294          return 2;
1295      case StoreOpType::I32Store:
1296      case StoreOpType::I64Store32:
1297      case StoreOpType::F32Store:
1298          return 4;
1299      case StoreOpType::I64Store:
1300      case StoreOpType::F64Store:
1301          return 8;
1302      }
1303      RELEASE_ASSERT_NOT_REACHED();
1304  }
1305  
1306  
1307  inline void B3IRGenerator::emitStoreOp(StoreOpType op, ExpressionType pointer, ExpressionType value, uint32_t uoffset)
1308  {
1309      int32_t offset = fixupPointerPlusOffset(pointer, uoffset);
1310  
1311      switch (op) {
1312      case StoreOpType::I64Store8:
1313          value = m_currentBlock->appendNew<Value>(m_proc, Trunc, origin(), value);
1314          FALLTHROUGH;
1315  
1316      case StoreOpType::I32Store8:
1317          m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Store8), origin(), value, pointer, offset);
1318          return;
1319  
1320      case StoreOpType::I64Store16:
1321          value = m_currentBlock->appendNew<Value>(m_proc, Trunc, origin(), value);
1322          FALLTHROUGH;
1323  
1324      case StoreOpType::I32Store16:
1325          m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Store16), origin(), value, pointer, offset);
1326          return;
1327  
1328      case StoreOpType::I64Store32:
1329          value = m_currentBlock->appendNew<Value>(m_proc, Trunc, origin(), value);
1330          FALLTHROUGH;
1331  
1332      case StoreOpType::I64Store:
1333      case StoreOpType::I32Store:
1334      case StoreOpType::F32Store:
1335      case StoreOpType::F64Store:
1336          m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Store), origin(), value, pointer, offset);
1337          return;
1338      }
1339      RELEASE_ASSERT_NOT_REACHED();
1340  }
1341  
1342  auto B3IRGenerator::store(StoreOpType op, ExpressionType pointer, ExpressionType value, uint32_t offset) -> PartialResult
1343  {
1344      ASSERT(pointer->type() == Int32);
1345  
1346      if (UNLIKELY(sumOverflows<uint32_t>(offset, sizeOfStoreOp(op)))) {
1347          // FIXME: Even though this is provably out of bounds, it's not a validation error, so we have to handle it
1348          // as a runtime exception. However, this may change: https://bugs.webkit.org/show_bug.cgi?id=166435
1349          B3::PatchpointValue* throwException = m_currentBlock->appendNew<B3::PatchpointValue>(m_proc, B3::Void, origin());
1350          throwException->setGenerator([this] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
1351              this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsMemoryAccess);
1352          });
1353      } else
1354          emitStoreOp(op, emitCheckAndPreparePointer(pointer, offset, sizeOfStoreOp(op)), value, offset);
1355  
1356      return { };
1357  }
1358  
1359  inline B3::Width accessWidth(ExtAtomicOpType op)
1360  {
1361      return static_cast<B3::Width>(memoryLog2Alignment(op));
1362  }
1363  
1364  inline uint32_t sizeOfAtomicOpMemoryAccess(ExtAtomicOpType op)
1365  {
1366      return bytesForWidth(accessWidth(op));
1367  }
1368  
1369  inline Value* B3IRGenerator::sanitizeAtomicResult(ExtAtomicOpType op, Type valueType, ExpressionType result)
1370  {
1371      auto sanitize32 = [&](ExpressionType result) {
1372          switch (accessWidth(op)) {
1373          case B3::Width8:
1374              return m_currentBlock->appendNew<Value>(m_proc, BitAnd, origin(), result, constant(Int32, 0xff));
1375          case B3::Width16:
1376              return m_currentBlock->appendNew<Value>(m_proc, BitAnd, origin(), result, constant(Int32, 0xffff));
1377          default:
1378              return result;
1379          }
1380      };
1381  
1382      switch (valueType) {
1383      case Type::I64: {
1384          if (accessWidth(op) == B3::Width64)
1385              return result;
1386          return m_currentBlock->appendNew<Value>(m_proc, ZExt32, origin(), sanitize32(result));
1387      }
1388      case Type::I32:
1389          return sanitize32(result);
1390      default:
1391          RELEASE_ASSERT_NOT_REACHED();
1392          return nullptr;
1393      }
1394  }
1395  
1396  Value* B3IRGenerator::fixupPointerPlusOffsetForAtomicOps(ExtAtomicOpType op, ExpressionType ptr, uint32_t offset)
1397  {
1398      auto pointer = m_currentBlock->appendNew<Value>(m_proc, Add, origin(), ptr, m_currentBlock->appendNew<Const64Value>(m_proc, origin(), offset));
1399      if (accessWidth(op) != B3::Width8) {
1400          CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(),
1401              m_currentBlock->appendNew<Value>(m_proc, BitAnd, origin(), pointer, constant(pointerType(), sizeOfAtomicOpMemoryAccess(op) - 1)));
1402          check->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
1403              this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsMemoryAccess);
1404          });
1405      }
1406      return pointer;
1407  }
1408  
1409  inline Value* B3IRGenerator::emitAtomicLoadOp(ExtAtomicOpType op, Type valueType, ExpressionType pointer, uint32_t uoffset)
1410  {
1411      pointer = fixupPointerPlusOffsetForAtomicOps(op, pointer, uoffset);
1412  
1413      ExpressionType value = nullptr;
1414      switch (accessWidth(op)) {
1415      case B3::Width8:
1416      case B3::Width16:
1417      case B3::Width32:
1418          value = constant(Int32, 0);
1419          break;
1420      case B3::Width64:
1421          value = constant(Int64, 0);
1422          break;
1423      }
1424  
1425      return sanitizeAtomicResult(op, valueType, m_currentBlock->appendNew<AtomicValue>(m_proc, memoryKind(AtomicXchgAdd), origin(), accessWidth(op), value, pointer));
1426  }
1427  
1428  auto B3IRGenerator::atomicLoad(ExtAtomicOpType op, Type valueType, ExpressionType pointer, ExpressionType& result, uint32_t offset) -> PartialResult
1429  {
1430      ASSERT(pointer->type() == Int32);
1431  
1432      if (UNLIKELY(sumOverflows<uint32_t>(offset, sizeOfAtomicOpMemoryAccess(op)))) {
1433          // FIXME: Even though this is provably out of bounds, it's not a validation error, so we have to handle it
1434          // as a runtime exception. However, this may change: https://bugs.webkit.org/show_bug.cgi?id=166435
1435          B3::PatchpointValue* throwException = m_currentBlock->appendNew<B3::PatchpointValue>(m_proc, B3::Void, origin());
1436          throwException->setGenerator([this] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
1437              this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsMemoryAccess);
1438          });
1439  
1440          switch (valueType) {
1441          case Type::I32:
1442              result = constant(Int32, 0);
1443              break;
1444          case Type::I64:
1445              result = constant(Int64, 0);
1446              break;
1447          default:
1448              RELEASE_ASSERT_NOT_REACHED();
1449              break;
1450          }
1451      } else
1452          result = emitAtomicLoadOp(op, valueType, emitCheckAndPreparePointer(pointer, offset, sizeOfAtomicOpMemoryAccess(op)), offset);
1453  
1454      return { };
1455  }
1456  
1457  inline void B3IRGenerator::emitAtomicStoreOp(ExtAtomicOpType op, Type valueType, ExpressionType pointer, ExpressionType value, uint32_t uoffset)
1458  {
1459      pointer = fixupPointerPlusOffsetForAtomicOps(op, pointer, uoffset);
1460  
1461      if (valueType == Type::I64 && accessWidth(op) != B3::Width64)
1462          value = m_currentBlock->appendNew<B3::Value>(m_proc, B3::Trunc, Origin(), value);
1463      m_currentBlock->appendNew<AtomicValue>(m_proc, memoryKind(AtomicXchg), origin(), accessWidth(op), value, pointer);
1464  }
1465  
1466  auto B3IRGenerator::atomicStore(ExtAtomicOpType op, Type valueType, ExpressionType pointer, ExpressionType value, uint32_t offset) -> PartialResult
1467  {
1468      ASSERT(pointer->type() == Int32);
1469  
1470      if (UNLIKELY(sumOverflows<uint32_t>(offset, sizeOfAtomicOpMemoryAccess(op)))) {
1471          // FIXME: Even though this is provably out of bounds, it's not a validation error, so we have to handle it
1472          // as a runtime exception. However, this may change: https://bugs.webkit.org/show_bug.cgi?id=166435
1473          B3::PatchpointValue* throwException = m_currentBlock->appendNew<B3::PatchpointValue>(m_proc, B3::Void, origin());
1474          throwException->setGenerator([this] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
1475              this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsMemoryAccess);
1476          });
1477      } else
1478          emitAtomicStoreOp(op, valueType, emitCheckAndPreparePointer(pointer, offset, sizeOfAtomicOpMemoryAccess(op)), value, offset);
1479  
1480      return { };
1481  }
1482  
1483  inline Value* B3IRGenerator::emitAtomicBinaryRMWOp(ExtAtomicOpType op, Type valueType, ExpressionType pointer, ExpressionType value, uint32_t uoffset)
1484  {
1485      pointer = fixupPointerPlusOffsetForAtomicOps(op, pointer, uoffset);
1486  
1487      B3::Opcode opcode = B3::Nop;
1488      switch (op) {
1489      case ExtAtomicOpType::I32AtomicRmw8AddU:
1490      case ExtAtomicOpType::I32AtomicRmw16AddU:
1491      case ExtAtomicOpType::I32AtomicRmwAdd:
1492      case ExtAtomicOpType::I64AtomicRmw8AddU:
1493      case ExtAtomicOpType::I64AtomicRmw16AddU:
1494      case ExtAtomicOpType::I64AtomicRmw32AddU:
1495      case ExtAtomicOpType::I64AtomicRmwAdd:
1496          opcode = AtomicXchgAdd;
1497          break;
1498      case ExtAtomicOpType::I32AtomicRmw8SubU:
1499      case ExtAtomicOpType::I32AtomicRmw16SubU:
1500      case ExtAtomicOpType::I32AtomicRmwSub:
1501      case ExtAtomicOpType::I64AtomicRmw8SubU:
1502      case ExtAtomicOpType::I64AtomicRmw16SubU:
1503      case ExtAtomicOpType::I64AtomicRmw32SubU:
1504      case ExtAtomicOpType::I64AtomicRmwSub:
1505          opcode = AtomicXchgSub;
1506          break;
1507      case ExtAtomicOpType::I32AtomicRmw8AndU:
1508      case ExtAtomicOpType::I32AtomicRmw16AndU:
1509      case ExtAtomicOpType::I32AtomicRmwAnd:
1510      case ExtAtomicOpType::I64AtomicRmw8AndU:
1511      case ExtAtomicOpType::I64AtomicRmw16AndU:
1512      case ExtAtomicOpType::I64AtomicRmw32AndU:
1513      case ExtAtomicOpType::I64AtomicRmwAnd:
1514          opcode = AtomicXchgAnd;
1515          break;
1516      case ExtAtomicOpType::I32AtomicRmw8OrU:
1517      case ExtAtomicOpType::I32AtomicRmw16OrU:
1518      case ExtAtomicOpType::I32AtomicRmwOr:
1519      case ExtAtomicOpType::I64AtomicRmw8OrU:
1520      case ExtAtomicOpType::I64AtomicRmw16OrU:
1521      case ExtAtomicOpType::I64AtomicRmw32OrU:
1522      case ExtAtomicOpType::I64AtomicRmwOr:
1523          opcode = AtomicXchgOr;
1524          break;
1525      case ExtAtomicOpType::I32AtomicRmw8XorU:
1526      case ExtAtomicOpType::I32AtomicRmw16XorU:
1527      case ExtAtomicOpType::I32AtomicRmwXor:
1528      case ExtAtomicOpType::I64AtomicRmw8XorU:
1529      case ExtAtomicOpType::I64AtomicRmw16XorU:
1530      case ExtAtomicOpType::I64AtomicRmw32XorU:
1531      case ExtAtomicOpType::I64AtomicRmwXor:
1532          opcode = AtomicXchgXor;
1533          break;
1534      case ExtAtomicOpType::I32AtomicRmw8XchgU:
1535      case ExtAtomicOpType::I32AtomicRmw16XchgU:
1536      case ExtAtomicOpType::I32AtomicRmwXchg:
1537      case ExtAtomicOpType::I64AtomicRmw8XchgU:
1538      case ExtAtomicOpType::I64AtomicRmw16XchgU:
1539      case ExtAtomicOpType::I64AtomicRmw32XchgU:
1540      case ExtAtomicOpType::I64AtomicRmwXchg:
1541          opcode = AtomicXchg;
1542          break;
1543      default:
1544          RELEASE_ASSERT_NOT_REACHED();
1545          break;
1546      }
1547  
1548      if (valueType == Type::I64 && accessWidth(op) != B3::Width64)
1549          value = m_currentBlock->appendNew<B3::Value>(m_proc, B3::Trunc, Origin(), value);
1550  
1551      return sanitizeAtomicResult(op, valueType, m_currentBlock->appendNew<AtomicValue>(m_proc, memoryKind(opcode), origin(), accessWidth(op), value, pointer));
1552  }
1553  
1554  auto B3IRGenerator::atomicBinaryRMW(ExtAtomicOpType op, Type valueType, ExpressionType pointer, ExpressionType value, ExpressionType& result, uint32_t offset) -> PartialResult
1555  {
1556      ASSERT(pointer->type() == Int32);
1557  
1558      if (UNLIKELY(sumOverflows<uint32_t>(offset, sizeOfAtomicOpMemoryAccess(op)))) {
1559          // FIXME: Even though this is provably out of bounds, it's not a validation error, so we have to handle it
1560          // as a runtime exception. However, this may change: https://bugs.webkit.org/show_bug.cgi?id=166435
1561          B3::PatchpointValue* throwException = m_currentBlock->appendNew<B3::PatchpointValue>(m_proc, B3::Void, origin());
1562          throwException->setGenerator([this] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
1563              this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsMemoryAccess);
1564          });
1565  
1566          switch (valueType) {
1567          case Type::I32:
1568              result = constant(Int32, 0);
1569              break;
1570          case Type::I64:
1571              result = constant(Int64, 0);
1572              break;
1573          default:
1574              RELEASE_ASSERT_NOT_REACHED();
1575              break;
1576          }
1577      } else
1578          result = emitAtomicBinaryRMWOp(op, valueType, emitCheckAndPreparePointer(pointer, offset, sizeOfAtomicOpMemoryAccess(op)), value, offset);
1579  
1580      return { };
1581  }
1582  
1583  Value* B3IRGenerator::emitAtomicCompareExchange(ExtAtomicOpType op, Type valueType, ExpressionType pointer, ExpressionType expected, ExpressionType value, uint32_t uoffset)
1584  {
1585      pointer = fixupPointerPlusOffsetForAtomicOps(op, pointer, uoffset);
1586  
1587      B3::Width accessWidth = Wasm::accessWidth(op);
1588  
1589      if (widthForType(toB3Type(valueType)) == accessWidth)
1590          return sanitizeAtomicResult(op, valueType, m_currentBlock->appendNew<AtomicValue>(m_proc, memoryKind(AtomicStrongCAS), origin(), accessWidth, expected, value, pointer));
1591  
1592      Value* maximum = nullptr;
1593      switch (valueType) {
1594      case Type::I64: {
1595          switch (accessWidth) {
1596          case B3::Width8:
1597              maximum = constant(Int64, UINT8_MAX);
1598              break;
1599          case B3::Width16:
1600              maximum = constant(Int64, UINT16_MAX);
1601              break;
1602          case B3::Width32:
1603              maximum = constant(Int64, UINT32_MAX);
1604              break;
1605          case B3::Width64:
1606              RELEASE_ASSERT_NOT_REACHED();
1607          }
1608          break;
1609      }
1610      case Type::I32:
1611          switch (accessWidth) {
1612          case B3::Width8:
1613              maximum = constant(Int32, UINT8_MAX);
1614              break;
1615          case B3::Width16:
1616              maximum = constant(Int32, UINT16_MAX);
1617              break;
1618          case B3::Width32:
1619          case B3::Width64:
1620              RELEASE_ASSERT_NOT_REACHED();
1621          }
1622          break;
1623      default:
1624          RELEASE_ASSERT_NOT_REACHED();
1625      }
1626  
1627      BasicBlock* failureCase = m_proc.addBlock();
1628      BasicBlock* successCase = m_proc.addBlock();
1629      BasicBlock* continuation = m_proc.addBlock();
1630  
1631      auto condition = m_currentBlock->appendNew<Value>(m_proc, Above, origin(), expected, maximum);
1632      m_currentBlock->appendNewControlValue(m_proc, B3::Branch, origin(), condition, FrequentedBlock(failureCase, FrequencyClass::Rare), FrequentedBlock(successCase, FrequencyClass::Normal));
1633      failureCase->addPredecessor(m_currentBlock);
1634      successCase->addPredecessor(m_currentBlock);
1635  
1636      m_currentBlock = successCase;
1637      B3::UpsilonValue* successValue = nullptr;
1638      {
1639          auto truncatedExpected = expected;
1640          auto truncatedValue = value;
1641          if (valueType == Type::I64) {
1642              truncatedExpected = m_currentBlock->appendNew<B3::Value>(m_proc, B3::Trunc, Origin(), expected);
1643              truncatedValue = m_currentBlock->appendNew<B3::Value>(m_proc, B3::Trunc, Origin(), value);
1644          }
1645  
1646          auto result = m_currentBlock->appendNew<AtomicValue>(m_proc, memoryKind(AtomicStrongCAS), origin(), accessWidth, truncatedExpected, truncatedValue, pointer);
1647          successValue = m_currentBlock->appendNew<B3::UpsilonValue>(m_proc, origin(), result);
1648          m_currentBlock->appendNewControlValue(m_proc, Jump, origin(), continuation);
1649          continuation->addPredecessor(m_currentBlock);
1650      }
1651  
1652      m_currentBlock = failureCase;
1653      B3::UpsilonValue* failureValue = nullptr;
1654      {
1655          Value* addingValue = nullptr;
1656          switch (accessWidth) {
1657          case B3::Width8:
1658          case B3::Width16:
1659          case B3::Width32:
1660              addingValue = constant(Int32, 0);
1661              break;
1662          case B3::Width64:
1663              addingValue = constant(Int64, 0);
1664              break;
1665          }
1666          auto result = m_currentBlock->appendNew<AtomicValue>(m_proc, memoryKind(AtomicXchgAdd), origin(), accessWidth, addingValue, pointer);
1667          failureValue = m_currentBlock->appendNew<B3::UpsilonValue>(m_proc, origin(), result);
1668          m_currentBlock->appendNewControlValue(m_proc, Jump, origin(), continuation);
1669          continuation->addPredecessor(m_currentBlock);
1670      }
1671  
1672      m_currentBlock = continuation;
1673      Value* phi = continuation->appendNew<Value>(m_proc, Phi, accessWidth == B3::Width64 ? Int64 : Int32, origin());
1674      successValue->setPhi(phi);
1675      failureValue->setPhi(phi);
1676      return sanitizeAtomicResult(op, valueType, phi);
1677  }
1678  
1679  auto B3IRGenerator::atomicCompareExchange(ExtAtomicOpType op, Type valueType, ExpressionType pointer, ExpressionType expected, ExpressionType value, ExpressionType& result, uint32_t offset) -> PartialResult
1680  {
1681      ASSERT(pointer->type() == Int32);
1682  
1683      if (UNLIKELY(sumOverflows<uint32_t>(offset, sizeOfAtomicOpMemoryAccess(op)))) {
1684          // FIXME: Even though this is provably out of bounds, it's not a validation error, so we have to handle it
1685          // as a runtime exception. However, this may change: https://bugs.webkit.org/show_bug.cgi?id=166435
1686          B3::PatchpointValue* throwException = m_currentBlock->appendNew<B3::PatchpointValue>(m_proc, B3::Void, origin());
1687          throwException->setGenerator([this] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
1688              this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsMemoryAccess);
1689          });
1690  
1691          switch (valueType) {
1692          case Type::I32:
1693              result = constant(Int32, 0);
1694              break;
1695          case Type::I64:
1696              result = constant(Int64, 0);
1697              break;
1698          default:
1699              RELEASE_ASSERT_NOT_REACHED();
1700              break;
1701          }
1702      } else
1703          result = emitAtomicCompareExchange(op, valueType, emitCheckAndPreparePointer(pointer, offset, sizeOfAtomicOpMemoryAccess(op)), expected, value, offset);
1704  
1705      return { };
1706  }
1707  
1708  auto B3IRGenerator::atomicWait(ExtAtomicOpType op, ExpressionType pointer, ExpressionType value, ExpressionType timeout, ExpressionType& result, uint32_t offset) -> PartialResult
1709  {
1710      if (op == ExtAtomicOpType::MemoryAtomicWait32) {
1711          result = m_currentBlock->appendNew<CCallValue>(m_proc, Int32, origin(),
1712              m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunction<OperationPtrTag>(operationMemoryAtomicWait32)),
1713              instanceValue(), pointer, m_currentBlock->appendNew<Const32Value>(m_proc, origin(), offset), value, timeout);
1714      } else {
1715          result = m_currentBlock->appendNew<CCallValue>(m_proc, Int32, origin(),
1716              m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunction<OperationPtrTag>(operationMemoryAtomicWait64)),
1717              instanceValue(), pointer, m_currentBlock->appendNew<Const32Value>(m_proc, origin(), offset), value, timeout);
1718      }
1719  
1720      {
1721          CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(),
1722              m_currentBlock->appendNew<Value>(m_proc, LessThan, origin(), result, m_currentBlock->appendNew<Const32Value>(m_proc, origin(), 0)));
1723  
1724          check->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
1725              this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsMemoryAccess);
1726          });
1727      }
1728  
1729      return { };
1730  }
1731  
1732  auto B3IRGenerator::atomicNotify(ExtAtomicOpType, ExpressionType pointer, ExpressionType count, ExpressionType& result, uint32_t offset) -> PartialResult
1733  {
1734      result = m_currentBlock->appendNew<CCallValue>(m_proc, Int32, origin(),
1735          m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunction<OperationPtrTag>(operationMemoryAtomicNotify)),
1736          instanceValue(), pointer, m_currentBlock->appendNew<Const32Value>(m_proc, origin(), offset), count);
1737  
1738      {
1739          CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(),
1740              m_currentBlock->appendNew<Value>(m_proc, LessThan, origin(), result, m_currentBlock->appendNew<Const32Value>(m_proc, origin(), 0)));
1741  
1742          check->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
1743              this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsMemoryAccess);
1744          });
1745      }
1746  
1747      return { };
1748  }
1749  
1750  auto B3IRGenerator::atomicFence(ExtAtomicOpType, uint8_t) -> PartialResult
1751  {
1752      m_currentBlock->appendNew<FenceValue>(m_proc, origin());
1753      return { };
1754  }
1755  
1756  auto B3IRGenerator::addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result) -> PartialResult
1757  {
1758      result = m_currentBlock->appendNew<Value>(m_proc, B3::Select, origin(), condition, nonZero, zero);
1759      return { };
1760  }
1761  
1762  B3IRGenerator::ExpressionType B3IRGenerator::addConstant(Type type, uint64_t value)
1763  {
1764  
1765      return constant(toB3Type(type), value);
1766  }
1767  
1768  void B3IRGenerator::emitEntryTierUpCheck()
1769  {
1770      if (!m_tierUp)
1771          return;
1772  
1773      ASSERT(m_tierUp);
1774      Value* countDownLocation = constant(pointerType(), reinterpret_cast<uint64_t>(&m_tierUp->m_counter), Origin());
1775  
1776      PatchpointValue* patch = m_currentBlock->appendNew<PatchpointValue>(m_proc, B3::Void, Origin());
1777      Effects effects = Effects::none();
1778      // FIXME: we should have a more precise heap range for the tier up count.
1779      effects.reads = B3::HeapRange::top();
1780      effects.writes = B3::HeapRange::top();
1781      patch->effects = effects;
1782      patch->clobber(RegisterSet::macroScratchRegisters());
1783  
1784      patch->append(countDownLocation, ValueRep::SomeRegister);
1785      patch->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
1786          AllowMacroScratchRegisterUsage allowScratch(jit);
1787          CCallHelpers::Jump tierUp = jit.branchAdd32(CCallHelpers::PositiveOrZero, CCallHelpers::TrustedImm32(TierUpCount::functionEntryIncrement()), CCallHelpers::Address(params[0].gpr()));
1788          CCallHelpers::Label tierUpResume = jit.label();
1789  
1790          params.addLatePath([=] (CCallHelpers& jit) {
1791              tierUp.link(&jit);
1792  
1793              const unsigned extraPaddingBytes = 0;
1794              RegisterSet registersToSpill = { };
1795              registersToSpill.add(GPRInfo::argumentGPR1);
1796              unsigned numberOfStackBytesUsedForRegisterPreservation = ScratchRegisterAllocator::preserveRegistersToStackForCall(jit, registersToSpill, extraPaddingBytes);
1797  
1798              jit.move(MacroAssembler::TrustedImm32(m_functionIndex), GPRInfo::argumentGPR1);
1799              MacroAssembler::Call call = jit.nearCall();
1800  
1801              ScratchRegisterAllocator::restoreRegistersFromStackForCall(jit, registersToSpill, RegisterSet(), numberOfStackBytesUsedForRegisterPreservation, extraPaddingBytes);
1802              jit.jump(tierUpResume);
1803  
1804              jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
1805                  MacroAssembler::repatchNearCall(linkBuffer.locationOfNearCall<NoPtrTag>(call), CodeLocationLabel<JITThunkPtrTag>(Thunks::singleton().stub(triggerOMGEntryTierUpThunkGenerator).code()));
1806              });
1807          });
1808      });
1809  }
1810  
1811  void B3IRGenerator::emitLoopTierUpCheck(uint32_t loopIndex, const Stack& enclosingStack)
1812  {
1813      uint32_t outerLoopIndex = this->outerLoopIndex();
1814      m_outerLoops.append(loopIndex);
1815  
1816      if (!m_tierUp)
1817          return;
1818  
1819      Origin origin = this->origin();
1820      ASSERT(m_tierUp->osrEntryTriggers().size() == loopIndex);
1821      m_tierUp->osrEntryTriggers().append(TierUpCount::TriggerReason::DontTrigger);
1822      m_tierUp->outerLoops().append(outerLoopIndex);
1823  
1824      Value* countDownLocation = constant(pointerType(), reinterpret_cast<uint64_t>(&m_tierUp->m_counter), origin);
1825  
1826      Vector<ExpressionType> stackmap;
1827      for (auto& local : m_locals) {
1828          Value* result = m_currentBlock->appendNew<VariableValue>(m_proc, B3::Get, origin, local);
1829          stackmap.append(result);
1830      }
1831      for (unsigned controlIndex = 0; controlIndex < m_parser->controlStack().size(); ++controlIndex) {
1832          auto& expressionStack = m_parser->controlStack()[controlIndex].enclosedExpressionStack;
1833          for (TypedExpression value : expressionStack)
1834              stackmap.append(value);
1835      }
1836      for (TypedExpression value : enclosingStack)
1837          stackmap.append(value);
1838  
1839      PatchpointValue* patch = m_currentBlock->appendNew<PatchpointValue>(m_proc, B3::Void, origin);
1840      Effects effects = Effects::none();
1841      // FIXME: we should have a more precise heap range for the tier up count.
1842      effects.reads = B3::HeapRange::top();
1843      effects.writes = B3::HeapRange::top();
1844      effects.exitsSideways = true;
1845      patch->effects = effects;
1846  
1847      patch->clobber(RegisterSet::macroScratchRegisters());
1848      RegisterSet clobberLate;
1849      clobberLate.add(GPRInfo::argumentGPR0);
1850      patch->clobberLate(clobberLate);
1851  
1852      patch->append(countDownLocation, ValueRep::SomeRegister);
1853      patch->appendVectorWithRep(stackmap, ValueRep::ColdAny);
1854  
1855      TierUpCount::TriggerReason* forceEntryTrigger = &(m_tierUp->osrEntryTriggers().last());
1856      static_assert(!static_cast<uint8_t>(TierUpCount::TriggerReason::DontTrigger), "the JIT code assumes non-zero means 'enter'");
1857      static_assert(sizeof(TierUpCount::TriggerReason) == 1, "branchTest8 assumes this size");
1858      patch->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
1859          AllowMacroScratchRegisterUsage allowScratch(jit);
1860          CCallHelpers::Jump forceOSREntry = jit.branchTest8(CCallHelpers::NonZero, CCallHelpers::AbsoluteAddress(forceEntryTrigger));
1861          CCallHelpers::Jump tierUp = jit.branchAdd32(CCallHelpers::PositiveOrZero, CCallHelpers::TrustedImm32(TierUpCount::loopIncrement()), CCallHelpers::Address(params[0].gpr()));
1862          MacroAssembler::Label tierUpResume = jit.label();
1863  
1864          OSREntryData& osrEntryData = m_tierUp->addOSREntryData(m_functionIndex, loopIndex);
1865          // First argument is the countdown location.
1866          for (unsigned i = 1; i < params.value()->numChildren(); ++i)
1867              osrEntryData.values().constructAndAppend(params[i], params.value()->child(i)->type());
1868          OSREntryData* osrEntryDataPtr = &osrEntryData;
1869  
1870          params.addLatePath([=] (CCallHelpers& jit) {
1871              AllowMacroScratchRegisterUsage allowScratch(jit);
1872              forceOSREntry.link(&jit);
1873              tierUp.link(&jit);
1874  
1875              jit.probe(tagCFunction<JITProbePtrTag>(operationWasmTriggerOSREntryNow), osrEntryDataPtr);
1876              jit.branchTestPtr(CCallHelpers::Zero, GPRInfo::argumentGPR0).linkTo(tierUpResume, &jit);
1877              jit.farJump(GPRInfo::argumentGPR1, WasmEntryPtrTag);
1878          });
1879      });
1880  }
1881  
1882  auto B3IRGenerator::addLoop(BlockSignature signature, Stack& enclosingStack, ControlType& block, Stack& newStack, uint32_t loopIndex) -> PartialResult
1883  {
1884      BasicBlock* body = m_proc.addBlock();
1885      BasicBlock* continuation = m_proc.addBlock();
1886  
1887      block = ControlData(m_proc, origin(), signature, BlockType::Loop, continuation, body);
1888  
1889      ExpressionList args;
1890      {
1891          unsigned offset = enclosingStack.size() - signature->argumentCount();
1892          for (unsigned i = 0; i < signature->argumentCount(); ++i) {
1893              TypedExpression value = enclosingStack.at(offset + i);
1894              auto* upsilon = m_currentBlock->appendNew<UpsilonValue>(m_proc, origin(), value);
1895              Value* phi = block.phis[i];
1896              body->append(phi);
1897              upsilon->setPhi(phi);
1898              newStack.constructAndAppend(value.type(), phi);
1899          }
1900          enclosingStack.shrink(offset);
1901      }
1902  
1903      m_currentBlock->appendNewControlValue(m_proc, Jump, origin(), body);
1904      if (loopIndex == m_loopIndexForOSREntry) {
1905          dataLogLnIf(WasmB3IRGeneratorInternal::verbose, "Setting up for OSR entry");
1906  
1907          m_currentBlock = m_rootBlock;
1908          Value* pointer = m_rootBlock->appendNew<ArgumentRegValue>(m_proc, Origin(), GPRInfo::argumentGPR0);
1909  
1910          unsigned indexInBuffer = 0;
1911          auto loadFromScratchBuffer = [&] (B3::Type type) {
1912              size_t offset = sizeof(uint64_t) * indexInBuffer++;
1913              RELEASE_ASSERT(type.isNumeric());
1914              return m_currentBlock->appendNew<MemoryValue>(m_proc, Load, type, origin(), pointer, offset);
1915          };
1916  
1917          for (auto& local : m_locals)
1918              m_currentBlock->appendNew<VariableValue>(m_proc, Set, Origin(), local, loadFromScratchBuffer(local->type()));
1919  
1920          auto connectControlEntry = [&](const ControlData& data, Stack& expressionStack) {
1921              // For each stack entry enclosed by this loop we need to replace the value with a phi so we can fill it on OSR entry.
1922              BasicBlock* sourceBlock = nullptr;
1923              unsigned blockIndex = 0;
1924              B3::InsertionSet insertionSet(m_proc);
1925              for (unsigned i = 0; i < expressionStack.size(); i++) {
1926                  TypedExpression value = expressionStack[i];
1927                  if (value->isConstant()) {
1928                      ++indexInBuffer;
1929                      continue;
1930                  }
1931  
1932                  if (value->owner != sourceBlock) {
1933                      if (sourceBlock)
1934                          insertionSet.execute(sourceBlock);
1935                      ASSERT(insertionSet.isEmpty());
1936                      dataLogLnIf(WasmB3IRGeneratorInternal::verbose && sourceBlock, "Executed insertion set into: ", *sourceBlock);
1937                      blockIndex = 0;
1938                      sourceBlock = value->owner;
1939                  }
1940  
1941                  while (sourceBlock->at(blockIndex++) != value)
1942                      ASSERT(blockIndex < sourceBlock->size());
1943                  ASSERT(sourceBlock->at(blockIndex - 1) == value);
1944  
1945                  auto* phi = data.continuation->appendNew<Value>(m_proc, Phi,  value->type(), value->origin());
1946                  expressionStack[i] = TypedExpression { value.type(), phi };
1947                  m_currentBlock->appendNew<UpsilonValue>(m_proc, value->origin(), loadFromScratchBuffer(value->type()), phi);
1948  
1949                  auto* sourceUpsilon = m_proc.add<UpsilonValue>(value->origin(), value, phi);
1950                  insertionSet.insertValue(blockIndex, sourceUpsilon);
1951              }
1952              if (sourceBlock)
1953                  insertionSet.execute(sourceBlock);
1954          };
1955  
1956          for (unsigned controlIndex = 0; controlIndex < m_parser->controlStack().size(); ++controlIndex) {
1957              auto& data = m_parser->controlStack()[controlIndex].controlData;
1958              auto& expressionStack = m_parser->controlStack()[controlIndex].enclosedExpressionStack;
1959              connectControlEntry(data, expressionStack);
1960          }
1961          connectControlEntry(block, enclosingStack);
1962  
1963          m_osrEntryScratchBufferSize = indexInBuffer;
1964          m_currentBlock->appendNewControlValue(m_proc, Jump, origin(), body);
1965          body->addPredecessor(m_currentBlock);
1966      }
1967  
1968      m_currentBlock = body;
1969      emitLoopTierUpCheck(loopIndex, enclosingStack);
1970      return { };
1971  }
1972  
1973  B3IRGenerator::ControlData B3IRGenerator::addTopLevel(BlockSignature signature)
1974  {
1975      return ControlData(m_proc, Origin(), signature, BlockType::TopLevel, m_proc.addBlock());
1976  }
1977  
1978  auto B3IRGenerator::addBlock(BlockSignature signature, Stack& enclosingStack, ControlType& newBlock, Stack& newStack) -> PartialResult
1979  {
1980      BasicBlock* continuation = m_proc.addBlock();
1981  
1982      splitStack(signature, enclosingStack, newStack);
1983      newBlock = ControlData(m_proc, origin(), signature, BlockType::Block, continuation);
1984      return { };
1985  }
1986  
1987  auto B3IRGenerator::addIf(ExpressionType condition, BlockSignature signature, Stack& enclosingStack, ControlType& result, Stack& newStack) -> PartialResult
1988  {
1989      // FIXME: This needs to do some kind of stack passing.
1990  
1991      BasicBlock* taken = m_proc.addBlock();
1992      BasicBlock* notTaken = m_proc.addBlock();
1993      BasicBlock* continuation = m_proc.addBlock();
1994  
1995      m_currentBlock->appendNew<Value>(m_proc, B3::Branch, origin(), condition);
1996      m_currentBlock->setSuccessors(FrequentedBlock(taken), FrequentedBlock(notTaken));
1997      taken->addPredecessor(m_currentBlock);
1998      notTaken->addPredecessor(m_currentBlock);
1999  
2000      m_currentBlock = taken;
2001      splitStack(signature, enclosingStack, newStack);
2002      result = ControlData(m_proc, origin(), signature, BlockType::If, continuation, notTaken);
2003      return { };
2004  }
2005  
2006  auto B3IRGenerator::addElse(ControlData& data, const Stack& currentStack) -> PartialResult
2007  {
2008      unifyValuesWithBlock(currentStack, data.phis);
2009      m_currentBlock->appendNewControlValue(m_proc, Jump, origin(), data.continuation);
2010      return addElseToUnreachable(data);
2011  }
2012  
2013  auto B3IRGenerator::addElseToUnreachable(ControlData& data) -> PartialResult
2014  {
2015      ASSERT(data.blockType() == BlockType::If);
2016      m_currentBlock = data.special;
2017      data.convertIfToBlock();
2018      return { };
2019  }
2020  
2021  auto B3IRGenerator::addReturn(const ControlData&, const Stack& returnValues) -> PartialResult
2022  {
2023      CallInformation wasmCallInfo = wasmCallingConvention().callInformationFor(m_parser->signature(), CallRole::Callee);
2024  
2025      PatchpointValue* patch = m_proc.add<PatchpointValue>(B3::Void, origin());
2026      patch->setGenerator([] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
2027          auto calleeSaves = params.code().calleeSaveRegisterAtOffsetList();
2028  
2029          for (RegisterAtOffset calleeSave : calleeSaves)
2030              jit.load64ToReg(CCallHelpers::Address(GPRInfo::callFrameRegister, calleeSave.offset()), calleeSave.reg());
2031  
2032          jit.emitFunctionEpilogue();
2033          jit.ret();
2034      });
2035      patch->effects.terminal = true;
2036  
2037      RELEASE_ASSERT(returnValues.size() >= wasmCallInfo.results.size());
2038      unsigned offset = returnValues.size() - wasmCallInfo.results.size();
2039      for (unsigned i = 0; i < wasmCallInfo.results.size(); ++i) {
2040          B3::ValueRep rep = wasmCallInfo.results[i];
2041          if (rep.isStack()) {
2042              B3::Value* address = m_currentBlock->appendNew<B3::Value>(m_proc, B3::Add, Origin(), framePointer(), constant(pointerType(), rep.offsetFromFP()));
2043              m_currentBlock->appendNew<B3::MemoryValue>(m_proc, B3::Store, Origin(), returnValues[offset + i], address);
2044          } else {
2045              ASSERT(rep.isReg());
2046              patch->append(returnValues[offset + i], rep);
2047          }
2048      }
2049  
2050      m_currentBlock->append(patch);
2051      return { };
2052  }
2053  
2054  auto B3IRGenerator::addBranch(ControlData& data, ExpressionType condition, const Stack& returnValues) -> PartialResult
2055  {
2056      unifyValuesWithBlock(returnValues, data.phis);
2057  
2058      BasicBlock* target = data.targetBlockForBranch();
2059      if (condition) {
2060          BasicBlock* continuation = m_proc.addBlock();
2061          m_currentBlock->appendNew<Value>(m_proc, B3::Branch, origin(), condition);
2062          m_currentBlock->setSuccessors(FrequentedBlock(target), FrequentedBlock(continuation));
2063          target->addPredecessor(m_currentBlock);
2064          continuation->addPredecessor(m_currentBlock);
2065          m_currentBlock = continuation;
2066      } else {
2067          m_currentBlock->appendNewControlValue(m_proc, Jump, origin(), FrequentedBlock(target));
2068          target->addPredecessor(m_currentBlock);
2069      }
2070  
2071      return { };
2072  }
2073  
2074  auto B3IRGenerator::addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTarget, const Stack& expressionStack) -> PartialResult
2075  {
2076      for (size_t i = 0; i < targets.size(); ++i)
2077          unifyValuesWithBlock(expressionStack, targets[i]->phis);
2078      unifyValuesWithBlock(expressionStack, defaultTarget.phis);
2079  
2080      SwitchValue* switchValue = m_currentBlock->appendNew<SwitchValue>(m_proc, origin(), condition);
2081      switchValue->setFallThrough(FrequentedBlock(defaultTarget.targetBlockForBranch()));
2082      for (size_t i = 0; i < targets.size(); ++i)
2083          switchValue->appendCase(SwitchCase(i, FrequentedBlock(targets[i]->targetBlockForBranch())));
2084  
2085      return { };
2086  }
2087  
2088  auto B3IRGenerator::endBlock(ControlEntry& entry, Stack& expressionStack) -> PartialResult
2089  {
2090      ControlData& data = entry.controlData;
2091  
2092      ASSERT(expressionStack.size() == data.signature()->returnCount());
2093      if (data.blockType() != BlockType::Loop)
2094          unifyValuesWithBlock(expressionStack, data.phis);
2095  
2096      m_currentBlock->appendNewControlValue(m_proc, Jump, origin(), data.continuation);
2097      data.continuation->addPredecessor(m_currentBlock);
2098  
2099      return addEndToUnreachable(entry, expressionStack);
2100  }
2101  
2102  auto B3IRGenerator::addEndToUnreachable(ControlEntry& entry, const Stack& expressionStack) -> PartialResult
2103  {
2104      ControlData& data = entry.controlData;
2105      m_currentBlock = data.continuation;
2106  
2107      if (data.blockType() == BlockType::If) {
2108          data.special->appendNewControlValue(m_proc, Jump, origin(), m_currentBlock);
2109          m_currentBlock->addPredecessor(data.special);
2110      }
2111  
2112      if (data.blockType() != BlockType::Loop) {
2113          for (unsigned i = 0; i < data.signature()->returnCount(); ++i) {
2114              Value* result = data.phis[i];
2115              m_currentBlock->append(result);
2116              entry.enclosedExpressionStack.constructAndAppend(data.signature()->returnType(i), result);
2117          }
2118      } else {
2119          m_outerLoops.removeLast();
2120          for (unsigned i = 0; i < data.signature()->returnCount(); ++i) {
2121              if (i < expressionStack.size())
2122                  entry.enclosedExpressionStack.append(expressionStack[i]);
2123              else {
2124                  Type returnType = data.signature()->returnType(i);
2125                  entry.enclosedExpressionStack.constructAndAppend(returnType, constant(toB3Type(returnType), 0xbbadbeef));
2126              }
2127          }
2128      }
2129  
2130      // TopLevel does not have any code after this so we need to make sure we emit a return here.
2131      if (data.blockType() == BlockType::TopLevel)
2132          return addReturn(entry.controlData, entry.enclosedExpressionStack);
2133  
2134      return { };
2135  }
2136  
2137  
2138  B3::Value* B3IRGenerator::createCallPatchpoint(BasicBlock* block, Origin origin, const Signature& signature, Vector<ExpressionType>& args, const ScopedLambda<void(PatchpointValue*)>& patchpointFunctor)
2139  {
2140      Vector<B3::ConstrainedValue> constrainedArguments;
2141      CallInformation wasmCallInfo = wasmCallingConvention().callInformationFor(signature);
2142      for (unsigned i = 0; i < args.size(); ++i)
2143          constrainedArguments.append(B3::ConstrainedValue(args[i], wasmCallInfo.params[i]));
2144  
2145      m_proc.requestCallArgAreaSizeInBytes(WTF::roundUpToMultipleOf(stackAlignmentBytes(), wasmCallInfo.headerAndArgumentStackSizeInBytes));
2146  
2147      B3::Type returnType = toB3ResultType(&signature);
2148      B3::PatchpointValue* patchpoint = block->appendNew<B3::PatchpointValue>(m_proc, returnType, origin);
2149      patchpoint->clobberEarly(RegisterSet::macroScratchRegisters());
2150      patchpoint->clobberLate(RegisterSet::volatileRegistersForJSCall());
2151      patchpointFunctor(patchpoint);
2152      patchpoint->appendVector(constrainedArguments);
2153  
2154      if (returnType != B3::Void)
2155          patchpoint->resultConstraints = WTFMove(wasmCallInfo.results);
2156      return patchpoint;
2157  }
2158  
2159  auto B3IRGenerator::addCall(uint32_t functionIndex, const Signature& signature, Vector<ExpressionType>& args, ResultList& results) -> PartialResult
2160  {
2161      ASSERT(signature.argumentCount() == args.size());
2162  
2163      m_makesCalls = true;
2164      B3::Type returnType = toB3ResultType(&signature);
2165  
2166      auto fillResults = [&] (Value* callResult) {
2167          ASSERT(returnType == callResult->type());
2168  
2169          switch (returnType.kind()) {
2170          case B3::Void: {
2171              break;
2172          }
2173          case B3::Tuple: {
2174              const Vector<B3::Type>& tuple = m_proc.tupleForType(returnType);
2175              ASSERT(signature.returnCount() == tuple.size());
2176              for (unsigned i = 0; i < signature.returnCount(); ++i)
2177                  results.append(m_currentBlock->appendNew<ExtractValue>(m_proc, origin(), tuple[i], callResult, i));
2178              break;
2179          }
2180          default: {
2181              results.append(callResult);
2182              break;
2183          }
2184          }
2185      };
2186  
2187      Vector<UnlinkedWasmToWasmCall>* unlinkedWasmToWasmCalls = &m_unlinkedWasmToWasmCalls;
2188  
2189      if (m_info.isImportedFunctionFromFunctionIndexSpace(functionIndex)) {
2190          m_maxNumJSCallArguments = std::max(m_maxNumJSCallArguments, static_cast<uint32_t>(args.size()));
2191  
2192          // FIXME: imports can be linked here, instead of generating a patchpoint, because all import stubs are generated before B3 compilation starts. https://bugs.webkit.org/show_bug.cgi?id=166462
2193          Value* targetInstance = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(), instanceValue(), safeCast<int32_t>(Instance::offsetOfTargetInstance(functionIndex)));
2194          // The target instance is 0 unless the call is wasm->wasm.
2195          Value* isWasmCall = m_currentBlock->appendNew<Value>(m_proc, NotEqual, origin(), targetInstance, m_currentBlock->appendNew<Const64Value>(m_proc, origin(), 0));
2196  
2197          BasicBlock* isWasmBlock = m_proc.addBlock();
2198          BasicBlock* isEmbedderBlock = m_proc.addBlock();
2199          BasicBlock* continuation = m_proc.addBlock();
2200          m_currentBlock->appendNewControlValue(m_proc, B3::Branch, origin(), isWasmCall, FrequentedBlock(isWasmBlock), FrequentedBlock(isEmbedderBlock));
2201  
2202          Value* wasmCallResult = createCallPatchpoint(isWasmBlock, origin(), signature, args,
2203              scopedLambdaRef<void(PatchpointValue*)>([=] (PatchpointValue* patchpoint) -> void {
2204                  patchpoint->effects.writesPinned = true;
2205                  patchpoint->effects.readsPinned = true;
2206                  // We need to clobber all potential pinned registers since we might be leaving the instance.
2207                  // We pessimistically assume we could be calling to something that is bounds checking.
2208                  // FIXME: We shouldn't have to do this: https://bugs.webkit.org/show_bug.cgi?id=172181
2209                  patchpoint->clobberLate(PinnedRegisterInfo::get().toSave(MemoryMode::BoundsChecking));
2210                  patchpoint->setGenerator([unlinkedWasmToWasmCalls, functionIndex] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
2211                      AllowMacroScratchRegisterUsage allowScratch(jit);
2212                      CCallHelpers::Call call = jit.threadSafePatchableNearCall();
2213                      jit.addLinkTask([unlinkedWasmToWasmCalls, call, functionIndex] (LinkBuffer& linkBuffer) {
2214                          unlinkedWasmToWasmCalls->append({ linkBuffer.locationOfNearCall<WasmEntryPtrTag>(call), functionIndex });
2215                      });
2216                  });
2217              }));
2218          UpsilonValue* wasmCallResultUpsilon = returnType == B3::Void ? nullptr : isWasmBlock->appendNew<UpsilonValue>(m_proc, origin(), wasmCallResult);
2219          isWasmBlock->appendNewControlValue(m_proc, Jump, origin(), continuation);
2220  
2221          // FIXME: Let's remove this indirection by creating a PIC friendly IC
2222          // for calls out to the embedder. This shouldn't be that hard to do. We could probably
2223          // implement the IC to be over Context*.
2224          // https://bugs.webkit.org/show_bug.cgi?id=170375
2225          Value* jumpDestination = isEmbedderBlock->appendNew<MemoryValue>(m_proc,
2226              Load, pointerType(), origin(), instanceValue(), safeCast<int32_t>(Instance::offsetOfWasmToEmbedderStub(functionIndex)));
2227  
2228          Value* embedderCallResult = createCallPatchpoint(isEmbedderBlock, origin(), signature, args,
2229              scopedLambdaRef<void(PatchpointValue*)>([=] (PatchpointValue* patchpoint) -> void {
2230                  patchpoint->effects.writesPinned = true;
2231                  patchpoint->effects.readsPinned = true;
2232                  patchpoint->append(jumpDestination, ValueRep::SomeRegister);
2233                  // We need to clobber all potential pinned registers since we might be leaving the instance.
2234                  // We pessimistically assume we could be calling to something that is bounds checking.
2235                  // FIXME: We shouldn't have to do this: https://bugs.webkit.org/show_bug.cgi?id=172181
2236                  patchpoint->clobberLate(PinnedRegisterInfo::get().toSave(MemoryMode::BoundsChecking));
2237                  patchpoint->setGenerator([returnType] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
2238                      AllowMacroScratchRegisterUsage allowScratch(jit);
2239                      jit.call(params[params.proc().resultCount(returnType)].gpr(), WasmEntryPtrTag);
2240                  });
2241              }));
2242          UpsilonValue* embedderCallResultUpsilon = returnType == B3::Void ? nullptr : isEmbedderBlock->appendNew<UpsilonValue>(m_proc, origin(), embedderCallResult);
2243          isEmbedderBlock->appendNewControlValue(m_proc, Jump, origin(), continuation);
2244  
2245          m_currentBlock = continuation;
2246  
2247          if (returnType != B3::Void) {
2248              Value* phi = continuation->appendNew<Value>(m_proc, Phi, returnType, origin());
2249              wasmCallResultUpsilon->setPhi(phi);
2250              embedderCallResultUpsilon->setPhi(phi);
2251              fillResults(phi);
2252          }
2253  
2254          // The call could have been to another WebAssembly instance, and / or could have modified our Memory.
2255          restoreWebAssemblyGlobalState(RestoreCachedStackLimit::Yes, m_info.memory, instanceValue(), m_proc, continuation);
2256      } else {
2257  
2258          Value* patch = createCallPatchpoint(m_currentBlock, origin(), signature, args,
2259              scopedLambdaRef<void(PatchpointValue*)>([=] (PatchpointValue* patchpoint) -> void {
2260                  patchpoint->effects.writesPinned = true;
2261                  patchpoint->effects.readsPinned = true;
2262  
2263                  // We need to clobber the size register since the LLInt always bounds checks
2264                  if (m_mode == MemoryMode::Signaling || m_info.memory.isShared())
2265                      patchpoint->clobberLate(RegisterSet { PinnedRegisterInfo::get().boundsCheckingSizeRegister });
2266                  patchpoint->setGenerator([unlinkedWasmToWasmCalls, functionIndex] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
2267                      AllowMacroScratchRegisterUsage allowScratch(jit);
2268                      CCallHelpers::Call call = jit.threadSafePatchableNearCall();
2269                      jit.addLinkTask([unlinkedWasmToWasmCalls, call, functionIndex] (LinkBuffer& linkBuffer) {
2270                          unlinkedWasmToWasmCalls->append({ linkBuffer.locationOfNearCall<WasmEntryPtrTag>(call), functionIndex });
2271                      });
2272                  });
2273              }));
2274          fillResults(patch);
2275      }
2276  
2277      return { };
2278  }
2279  
2280  auto B3IRGenerator::addCallIndirect(unsigned tableIndex, const Signature& signature, Vector<ExpressionType>& args, ResultList& results) -> PartialResult
2281  {
2282      ExpressionType calleeIndex = args.takeLast();
2283      ASSERT(signature.argumentCount() == args.size());
2284  
2285      m_makesCalls = true;
2286      // Note: call indirect can call either WebAssemblyFunction or WebAssemblyWrapperFunction. Because
2287      // WebAssemblyWrapperFunction is like calling into the embedder, we conservatively assume all call indirects
2288      // can be to the embedder for our stack check calculation.
2289      m_maxNumJSCallArguments = std::max(m_maxNumJSCallArguments, static_cast<uint32_t>(args.size()));
2290  
2291      ExpressionType callableFunctionBuffer;
2292      ExpressionType instancesBuffer;
2293      ExpressionType callableFunctionBufferLength;
2294      {
2295          ExpressionType table = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(),
2296              instanceValue(), safeCast<int32_t>(Instance::offsetOfTablePtr(m_numImportFunctions, tableIndex)));
2297          callableFunctionBuffer = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(),
2298              table, safeCast<int32_t>(FuncRefTable::offsetOfFunctions()));
2299          instancesBuffer = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(),
2300              table, safeCast<int32_t>(FuncRefTable::offsetOfInstances()));
2301          callableFunctionBufferLength = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Int32, origin(),
2302              table, safeCast<int32_t>(Table::offsetOfLength()));
2303      }
2304  
2305      // Check the index we are looking for is valid.
2306      {
2307          CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(),
2308              m_currentBlock->appendNew<Value>(m_proc, AboveEqual, origin(), calleeIndex, callableFunctionBufferLength));
2309  
2310          check->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
2311              this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsCallIndirect);
2312          });
2313      }
2314  
2315      calleeIndex = m_currentBlock->appendNew<Value>(m_proc, ZExt32, origin(), calleeIndex);
2316  
2317      ExpressionType callableFunction;
2318      {
2319          // Compute the offset in the table index space we are looking for.
2320          ExpressionType offset = m_currentBlock->appendNew<Value>(m_proc, Mul, origin(),
2321              calleeIndex, constant(pointerType(), sizeof(WasmToWasmImportableFunction)));
2322          callableFunction = m_currentBlock->appendNew<Value>(m_proc, Add, origin(), callableFunctionBuffer, offset);
2323  
2324          // Check that the WasmToWasmImportableFunction is initialized. We trap if it isn't. An "invalid" SignatureIndex indicates it's not initialized.
2325          // FIXME: when we have trap handlers, we can just let the call fail because Signature::invalidIndex is 0. https://bugs.webkit.org/show_bug.cgi?id=177210
2326          static_assert(sizeof(WasmToWasmImportableFunction::signatureIndex) == sizeof(uint64_t), "Load codegen assumes i64");
2327          ExpressionType calleeSignatureIndex = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Int64, origin(), callableFunction, safeCast<int32_t>(WasmToWasmImportableFunction::offsetOfSignatureIndex()));
2328          {
2329              CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(),
2330                  m_currentBlock->appendNew<Value>(m_proc, Equal, origin(),
2331                      calleeSignatureIndex,
2332                      m_currentBlock->appendNew<Const64Value>(m_proc, origin(), Signature::invalidIndex)));
2333  
2334              check->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
2335                  this->emitExceptionCheck(jit, ExceptionType::NullTableEntry);
2336              });
2337          }
2338  
2339          // Check the signature matches the value we expect.
2340          {
2341              ExpressionType expectedSignatureIndex = m_currentBlock->appendNew<Const64Value>(m_proc, origin(), SignatureInformation::get(signature));
2342              CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(),
2343                  m_currentBlock->appendNew<Value>(m_proc, NotEqual, origin(), calleeSignatureIndex, expectedSignatureIndex));
2344  
2345              check->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
2346                  this->emitExceptionCheck(jit, ExceptionType::BadSignature);
2347              });
2348          }
2349      }
2350  
2351      // Do a context switch if needed.
2352      {
2353          Value* offset = m_currentBlock->appendNew<Value>(m_proc, Mul, origin(),
2354              calleeIndex, constant(pointerType(), sizeof(Instance*)));
2355          Value* newContextInstance = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(),
2356              m_currentBlock->appendNew<Value>(m_proc, Add, origin(), instancesBuffer, offset));
2357  
2358          BasicBlock* continuation = m_proc.addBlock();
2359          BasicBlock* doContextSwitch = m_proc.addBlock();
2360  
2361          Value* isSameContextInstance = m_currentBlock->appendNew<Value>(m_proc, Equal, origin(),
2362              newContextInstance, instanceValue());
2363          m_currentBlock->appendNewControlValue(m_proc, B3::Branch, origin(),
2364              isSameContextInstance, FrequentedBlock(continuation), FrequentedBlock(doContextSwitch));
2365  
2366          PatchpointValue* patchpoint = doContextSwitch->appendNew<PatchpointValue>(m_proc, B3::Void, origin());
2367          patchpoint->effects.writesPinned = true;
2368          // We pessimistically assume we're calling something with BoundsChecking memory.
2369          // FIXME: We shouldn't have to do this: https://bugs.webkit.org/show_bug.cgi?id=172181
2370          patchpoint->clobber(PinnedRegisterInfo::get().toSave(MemoryMode::BoundsChecking));
2371          patchpoint->clobber(RegisterSet::macroScratchRegisters());
2372          patchpoint->append(newContextInstance, ValueRep::SomeRegister);
2373          patchpoint->append(instanceValue(), ValueRep::SomeRegister);
2374          patchpoint->numGPScratchRegisters = Gigacage::isEnabled(Gigacage::Primitive) ? 1 : 0;
2375  
2376          patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
2377              AllowMacroScratchRegisterUsage allowScratch(jit);
2378              GPRReg newContextInstance = params[0].gpr();
2379              GPRReg oldContextInstance = params[1].gpr();
2380              const PinnedRegisterInfo& pinnedRegs = PinnedRegisterInfo::get();
2381              GPRReg baseMemory = pinnedRegs.baseMemoryPointer;
2382              ASSERT(newContextInstance != baseMemory);
2383              jit.loadPtr(CCallHelpers::Address(oldContextInstance, Instance::offsetOfCachedStackLimit()), baseMemory);
2384              jit.storePtr(baseMemory, CCallHelpers::Address(newContextInstance, Instance::offsetOfCachedStackLimit()));
2385              jit.storeWasmContextInstance(newContextInstance);
2386              ASSERT(pinnedRegs.boundsCheckingSizeRegister != baseMemory);
2387              // FIXME: We should support more than one memory size register
2388              //   see: https://bugs.webkit.org/show_bug.cgi?id=162952
2389              ASSERT(pinnedRegs.boundsCheckingSizeRegister != newContextInstance);
2390              GPRReg scratchOrBoundsCheckingSize = Gigacage::isEnabled(Gigacage::Primitive) ? params.gpScratch(0) : pinnedRegs.boundsCheckingSizeRegister;
2391  
2392              jit.loadPtr(CCallHelpers::Address(newContextInstance, Instance::offsetOfCachedBoundsCheckingSize()), pinnedRegs.boundsCheckingSizeRegister); // Memory size.
2393              jit.loadPtr(CCallHelpers::Address(newContextInstance, Instance::offsetOfCachedMemory()), baseMemory); // Memory::void*.
2394  
2395              jit.cageConditionally(Gigacage::Primitive, baseMemory, pinnedRegs.boundsCheckingSizeRegister, scratchOrBoundsCheckingSize);
2396          });
2397          doContextSwitch->appendNewControlValue(m_proc, Jump, origin(), continuation);
2398  
2399          m_currentBlock = continuation;
2400      }
2401  
2402      ExpressionType calleeCode = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(),
2403          m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(), callableFunction,
2404              safeCast<int32_t>(WasmToWasmImportableFunction::offsetOfEntrypointLoadLocation())));
2405  
2406      B3::Type returnType = toB3ResultType(&signature);
2407      ExpressionType callResult = createCallPatchpoint(m_currentBlock, origin(), signature, args,
2408          scopedLambdaRef<void(PatchpointValue*)>([=] (PatchpointValue* patchpoint) -> void {
2409              patchpoint->effects.writesPinned = true;
2410              patchpoint->effects.readsPinned = true;
2411              // We need to clobber all potential pinned registers since we might be leaving the instance.
2412              // We pessimistically assume we're always calling something that is bounds checking so
2413              // because the wasm->wasm thunk unconditionally overrides the size registers.
2414              // FIXME: We should not have to do this, but the wasm->wasm stub assumes it can
2415              // use all the pinned registers as scratch: https://bugs.webkit.org/show_bug.cgi?id=172181
2416              patchpoint->clobberLate(PinnedRegisterInfo::get().toSave(MemoryMode::BoundsChecking));
2417  
2418              patchpoint->append(calleeCode, ValueRep::SomeRegister);
2419              patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
2420                  AllowMacroScratchRegisterUsage allowScratch(jit);
2421                  jit.call(params[params.proc().resultCount(returnType)].gpr(), WasmEntryPtrTag);
2422              });
2423          }));
2424  
2425      switch (returnType.kind()) {
2426      case B3::Void: {
2427          break;
2428      }
2429      case B3::Tuple: {
2430          const Vector<B3::Type>& tuple = m_proc.tupleForType(returnType);
2431          for (unsigned i = 0; i < signature.returnCount(); ++i)
2432              results.append(m_currentBlock->appendNew<ExtractValue>(m_proc, origin(), tuple[i], callResult, i));
2433          break;
2434      }
2435      default: {
2436          results.append(callResult);
2437          break;
2438      }
2439      }
2440  
2441      // The call could have been to another WebAssembly instance, and / or could have modified our Memory.
2442      restoreWebAssemblyGlobalState(RestoreCachedStackLimit::Yes, m_info.memory, instanceValue(), m_proc, m_currentBlock);
2443  
2444      return { };
2445  }
2446  
2447  void B3IRGenerator::unify(const ExpressionType phi, const ExpressionType source)
2448  {
2449      m_currentBlock->appendNew<UpsilonValue>(m_proc, origin(), source, phi);
2450  }
2451  
2452  void B3IRGenerator::unifyValuesWithBlock(const Stack& resultStack, const ResultList& result)
2453  {
2454      ASSERT(result.size() <= resultStack.size());
2455  
2456      for (size_t i = 0; i < result.size(); ++i)
2457          unify(result[result.size() - 1 - i], resultStack.at(resultStack.size() - 1 - i));
2458  }
2459  
2460  static void dumpExpressionStack(const CommaPrinter& comma, const B3IRGenerator::Stack& expressionStack)
2461  {
2462      dataLog(comma, "ExpressionStack:");
2463      for (const auto& expression : expressionStack)
2464          dataLog(comma, *expression);
2465  }
2466  
2467  void B3IRGenerator::dump(const ControlStack& controlStack, const Stack* expressionStack)
2468  {
2469      dataLogLn("Constants:");
2470      for (const auto& constant : m_constantPool)
2471          dataLogLn(deepDump(m_proc, constant.value));
2472  
2473      dataLogLn("Processing Graph:");
2474      dataLog(m_proc);
2475      dataLogLn("With current block:", *m_currentBlock);
2476      dataLogLn("Control stack:");
2477      ASSERT(controlStack.size());
2478      for (size_t i = controlStack.size(); i--;) {
2479          dataLog("  ", controlStack[i].controlData, ": ");
2480          CommaPrinter comma(", ", "");
2481          dumpExpressionStack(comma, *expressionStack);
2482          expressionStack = &controlStack[i].enclosedExpressionStack;
2483          dataLogLn();
2484      }
2485      dataLogLn();
2486  }
2487  
2488  auto B3IRGenerator::origin() -> Origin
2489  {
2490      OpcodeOrigin origin(m_parser->currentOpcode(), m_parser->currentOpcodeStartingOffset());
2491      ASSERT(isValidOpType(static_cast<uint8_t>(origin.opcode())));
2492      return bitwise_cast<Origin>(origin);
2493  }
2494  
2495  Expected<std::unique_ptr<InternalFunction>, String> parseAndCompile(CompilationContext& compilationContext, const FunctionData& function, const Signature& signature, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, unsigned& osrEntryScratchBufferSize, const ModuleInformation& info, MemoryMode mode, CompilationMode compilationMode, uint32_t functionIndex, uint32_t loopIndexForOSREntry, TierUpCount* tierUp)
2496  {
2497      auto result = makeUnique<InternalFunction>();
2498  
2499      compilationContext.embedderEntrypointJIT = makeUnique<CCallHelpers>();
2500      compilationContext.wasmEntrypointJIT = makeUnique<CCallHelpers>();
2501  
2502      Procedure procedure;
2503  
2504      procedure.setOriginPrinter([] (PrintStream& out, Origin origin) {
2505          if (origin.data())
2506              out.print("Wasm: ", bitwise_cast<OpcodeOrigin>(origin));
2507      });
2508      
2509      // This means we cannot use either StackmapGenerationParams::usedRegisters() or
2510      // StackmapGenerationParams::unavailableRegisters(). In exchange for this concession, we
2511      // don't strictly need to run Air::reportUsedRegisters(), which saves a bit of CPU time at
2512      // optLevel=1.
2513      procedure.setNeedsUsedRegisters(false);
2514      
2515      procedure.setOptLevel(compilationMode == CompilationMode::BBQMode
2516          ? Options::webAssemblyBBQB3OptimizationLevel()
2517          : Options::webAssemblyOMGOptimizationLevel());
2518  
2519      B3IRGenerator irGenerator(info, procedure, result.get(), unlinkedWasmToWasmCalls, osrEntryScratchBufferSize, mode, compilationMode, functionIndex, loopIndexForOSREntry, tierUp);
2520      FunctionParser<B3IRGenerator> parser(irGenerator, function.data.data(), function.data.size(), signature, info);
2521      WASM_FAIL_IF_HELPER_FAILS(parser.parse());
2522  
2523      irGenerator.insertConstants();
2524  
2525      procedure.resetReachability();
2526      if (ASSERT_ENABLED)
2527          validate(procedure, "After parsing:\n");
2528  
2529      dataLogIf(WasmB3IRGeneratorInternal::verbose, "Pre SSA: ", procedure);
2530      fixSSA(procedure);
2531      dataLogIf(WasmB3IRGeneratorInternal::verbose, "Post SSA: ", procedure);
2532      
2533      {
2534          B3::prepareForGeneration(procedure);
2535          B3::generate(procedure, *compilationContext.wasmEntrypointJIT);
2536          compilationContext.wasmEntrypointByproducts = procedure.releaseByproducts();
2537          result->entrypoint.calleeSaveRegisters = procedure.calleeSaveRegisterAtOffsetList();
2538      }
2539  
2540      return result;
2541  }
2542  
2543  // Custom wasm ops. These are the ones too messy to do in wasm.json.
2544  
2545  void B3IRGenerator::emitChecksForModOrDiv(B3::Opcode operation, ExpressionType left, ExpressionType right)
2546  {
2547      ASSERT(operation == Div || operation == Mod || operation == UDiv || operation == UMod);
2548      const B3::Type type = left->type();
2549  
2550      {
2551          CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(),
2552              m_currentBlock->appendNew<Value>(m_proc, Equal, origin(), right, constant(type, 0)));
2553  
2554          check->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
2555              this->emitExceptionCheck(jit, ExceptionType::DivisionByZero);
2556          });
2557      }
2558  
2559      if (operation == Div) {
2560          int64_t min = type == Int32 ? std::numeric_limits<int32_t>::min() : std::numeric_limits<int64_t>::min();
2561  
2562          CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(),
2563              m_currentBlock->appendNew<Value>(m_proc, BitAnd, origin(),
2564                  m_currentBlock->appendNew<Value>(m_proc, Equal, origin(), left, constant(type, min)),
2565                  m_currentBlock->appendNew<Value>(m_proc, Equal, origin(), right, constant(type, -1))));
2566  
2567          check->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
2568              this->emitExceptionCheck(jit, ExceptionType::IntegerOverflow);
2569          });
2570      }
2571  }
2572  
2573  template<>
2574  auto B3IRGenerator::addOp<OpType::I32DivS>(ExpressionType left, ExpressionType right, ExpressionType& result) -> PartialResult
2575  {
2576      const B3::Opcode op = Div;
2577      emitChecksForModOrDiv(op, left, right);
2578      result = m_currentBlock->appendNew<Value>(m_proc, op, origin(), left, right);
2579      return { };
2580  }
2581  
2582  template<>
2583  auto B3IRGenerator::addOp<OpType::I32RemS>(ExpressionType left, ExpressionType right, ExpressionType& result) -> PartialResult
2584  {
2585      const B3::Opcode op = Mod;
2586      emitChecksForModOrDiv(op, left, right);
2587      result = m_currentBlock->appendNew<Value>(m_proc, chill(op), origin(), left, right);
2588      return { };
2589  }
2590  
2591  template<>
2592  auto B3IRGenerator::addOp<OpType::I32DivU>(ExpressionType left, ExpressionType right, ExpressionType& result) -> PartialResult
2593  {
2594      const B3::Opcode op = UDiv;
2595      emitChecksForModOrDiv(op, left, right);
2596      result = m_currentBlock->appendNew<Value>(m_proc, op, origin(), left, right);
2597      return { };
2598  }
2599  
2600  template<>
2601  auto B3IRGenerator::addOp<OpType::I32RemU>(ExpressionType left, ExpressionType right, ExpressionType& result) -> PartialResult
2602  {
2603      const B3::Opcode op = UMod;
2604      emitChecksForModOrDiv(op, left, right);
2605      result = m_currentBlock->appendNew<Value>(m_proc, op, origin(), left, right);
2606      return { };
2607  }
2608  
2609  template<>
2610  auto B3IRGenerator::addOp<OpType::I64DivS>(ExpressionType left, ExpressionType right, ExpressionType& result) -> PartialResult
2611  {
2612      const B3::Opcode op = Div;
2613      emitChecksForModOrDiv(op, left, right);
2614      result = m_currentBlock->appendNew<Value>(m_proc, op, origin(), left, right);
2615      return { };
2616  }
2617  
2618  template<>
2619  auto B3IRGenerator::addOp<OpType::I64RemS>(ExpressionType left, ExpressionType right, ExpressionType& result) -> PartialResult
2620  {
2621      const B3::Opcode op = Mod;
2622      emitChecksForModOrDiv(op, left, right);
2623      result = m_currentBlock->appendNew<Value>(m_proc, chill(op), origin(), left, right);
2624      return { };
2625  }
2626  
2627  template<>
2628  auto B3IRGenerator::addOp<OpType::I64DivU>(ExpressionType left, ExpressionType right, ExpressionType& result) -> PartialResult
2629  {
2630      const B3::Opcode op = UDiv;
2631      emitChecksForModOrDiv(op, left, right);
2632      result = m_currentBlock->appendNew<Value>(m_proc, op, origin(), left, right);
2633      return { };
2634  }
2635  
2636  template<>
2637  auto B3IRGenerator::addOp<OpType::I64RemU>(ExpressionType left, ExpressionType right, ExpressionType& result) -> PartialResult
2638  {
2639      const B3::Opcode op = UMod;
2640      emitChecksForModOrDiv(op, left, right);
2641      result = m_currentBlock->appendNew<Value>(m_proc, op, origin(), left, right);
2642      return { };
2643  }
2644  
2645  template<>
2646  auto B3IRGenerator::addOp<OpType::I32Ctz>(ExpressionType arg, ExpressionType& result) -> PartialResult
2647  {
2648      PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int32, origin());
2649      patchpoint->append(arg, ValueRep::SomeRegister);
2650      patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
2651          jit.countTrailingZeros32(params[1].gpr(), params[0].gpr());
2652      });
2653      patchpoint->effects = Effects::none();
2654      result = patchpoint;
2655      return { };
2656  }
2657  
2658  template<>
2659  auto B3IRGenerator::addOp<OpType::I64Ctz>(ExpressionType arg, ExpressionType& result) -> PartialResult
2660  {
2661      PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int64, origin());
2662      patchpoint->append(arg, ValueRep::SomeRegister);
2663      patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
2664          jit.countTrailingZeros64(params[1].gpr(), params[0].gpr());
2665      });
2666      patchpoint->effects = Effects::none();
2667      result = patchpoint;
2668      return { };
2669  }
2670  
2671  template<>
2672  auto B3IRGenerator::addOp<OpType::I32Popcnt>(ExpressionType arg, ExpressionType& result) -> PartialResult
2673  {
2674  #if CPU(X86_64)
2675      if (MacroAssembler::supportsCountPopulation()) {
2676          PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int32, origin());
2677          patchpoint->append(arg, ValueRep::SomeRegister);
2678          patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
2679              jit.countPopulation32(params[1].gpr(), params[0].gpr());
2680          });
2681          patchpoint->effects = Effects::none();
2682          result = patchpoint;
2683          return { };
2684      }
2685  #endif
2686  
2687      Value* funcAddress = m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunction<OperationPtrTag>(operationPopcount32));
2688      result = m_currentBlock->appendNew<CCallValue>(m_proc, Int32, origin(), Effects::none(), funcAddress, arg);
2689      return { };
2690  }
2691  
2692  template<>
2693  auto B3IRGenerator::addOp<OpType::I64Popcnt>(ExpressionType arg, ExpressionType& result) -> PartialResult
2694  {
2695  #if CPU(X86_64)
2696      if (MacroAssembler::supportsCountPopulation()) {
2697          PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int64, origin());
2698          patchpoint->append(arg, ValueRep::SomeRegister);
2699          patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
2700              jit.countPopulation64(params[1].gpr(), params[0].gpr());
2701          });
2702          patchpoint->effects = Effects::none();
2703          result = patchpoint;
2704          return { };
2705      }
2706  #endif
2707  
2708      Value* funcAddress = m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunction<OperationPtrTag>(operationPopcount64));
2709      result = m_currentBlock->appendNew<CCallValue>(m_proc, Int64, origin(), Effects::none(), funcAddress, arg);
2710      return { };
2711  }
2712  
2713  template<>
2714  auto B3IRGenerator::addOp<F64ConvertUI64>(ExpressionType arg, ExpressionType& result) -> PartialResult
2715  {
2716      PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Double, origin());
2717      if (isX86())
2718          patchpoint->numGPScratchRegisters = 1;
2719      patchpoint->clobber(RegisterSet::macroScratchRegisters());
2720      patchpoint->append(ConstrainedValue(arg, ValueRep::SomeRegister));
2721      patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
2722          AllowMacroScratchRegisterUsage allowScratch(jit);
2723  #if CPU(X86_64)
2724          jit.convertUInt64ToDouble(params[1].gpr(), params[0].fpr(), params.gpScratch(0));
2725  #else
2726          jit.convertUInt64ToDouble(params[1].gpr(), params[0].fpr());
2727  #endif
2728      });
2729      patchpoint->effects = Effects::none();
2730      result = patchpoint;
2731      return { };
2732  }
2733  
2734  template<>
2735  auto B3IRGenerator::addOp<OpType::F32ConvertUI64>(ExpressionType arg, ExpressionType& result) -> PartialResult
2736  {
2737      PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Float, origin());
2738      if (isX86())
2739          patchpoint->numGPScratchRegisters = 1;
2740      patchpoint->clobber(RegisterSet::macroScratchRegisters());
2741      patchpoint->append(ConstrainedValue(arg, ValueRep::SomeRegister));
2742      patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
2743          AllowMacroScratchRegisterUsage allowScratch(jit);
2744  #if CPU(X86_64)
2745          jit.convertUInt64ToFloat(params[1].gpr(), params[0].fpr(), params.gpScratch(0));
2746  #else
2747          jit.convertUInt64ToFloat(params[1].gpr(), params[0].fpr());
2748  #endif
2749      });
2750      patchpoint->effects = Effects::none();
2751      result = patchpoint;
2752      return { };
2753  }
2754  
2755  template<>
2756  auto B3IRGenerator::addOp<OpType::F64Nearest>(ExpressionType arg, ExpressionType& result) -> PartialResult
2757  {
2758      PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Double, origin());
2759      patchpoint->append(arg, ValueRep::SomeRegister);
2760      patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
2761          jit.roundTowardNearestIntDouble(params[1].fpr(), params[0].fpr());
2762      });
2763      patchpoint->effects = Effects::none();
2764      result = patchpoint;
2765      return { };
2766  }
2767  
2768  template<>
2769  auto B3IRGenerator::addOp<OpType::F32Nearest>(ExpressionType arg, ExpressionType& result) -> PartialResult
2770  {
2771      PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Float, origin());
2772      patchpoint->append(arg, ValueRep::SomeRegister);
2773      patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
2774          jit.roundTowardNearestIntFloat(params[1].fpr(), params[0].fpr());
2775      });
2776      patchpoint->effects = Effects::none();
2777      result = patchpoint;
2778      return { };
2779  }
2780  
2781  template<>
2782  auto B3IRGenerator::addOp<OpType::F64Trunc>(ExpressionType arg, ExpressionType& result) -> PartialResult
2783  {
2784      PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Double, origin());
2785      patchpoint->append(arg, ValueRep::SomeRegister);
2786      patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
2787          jit.roundTowardZeroDouble(params[1].fpr(), params[0].fpr());
2788      });
2789      patchpoint->effects = Effects::none();
2790      result = patchpoint;
2791      return { };
2792  }
2793  
2794  template<>
2795  auto B3IRGenerator::addOp<OpType::F32Trunc>(ExpressionType arg, ExpressionType& result) -> PartialResult
2796  {
2797      PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Float, origin());
2798      patchpoint->append(arg, ValueRep::SomeRegister);
2799      patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
2800          jit.roundTowardZeroFloat(params[1].fpr(), params[0].fpr());
2801      });
2802      patchpoint->effects = Effects::none();
2803      result = patchpoint;
2804      return { };
2805  }
2806  
2807  template<>
2808  auto B3IRGenerator::addOp<OpType::I32TruncSF64>(ExpressionType arg, ExpressionType& result) -> PartialResult
2809  {
2810      Value* max = constant(Double, bitwise_cast<uint64_t>(-static_cast<double>(std::numeric_limits<int32_t>::min())));
2811      Value* min = constant(Double, bitwise_cast<uint64_t>(static_cast<double>(std::numeric_limits<int32_t>::min()) - 1.0));
2812      Value* outOfBounds = m_currentBlock->appendNew<Value>(m_proc, BitAnd, origin(),
2813          m_currentBlock->appendNew<Value>(m_proc, LessThan, origin(), arg, max),
2814          m_currentBlock->appendNew<Value>(m_proc, GreaterThan, origin(), arg, min));
2815      outOfBounds = m_currentBlock->appendNew<Value>(m_proc, Equal, origin(), outOfBounds, constant(Int32, 0));
2816      CheckValue* trap = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(), outOfBounds);
2817      trap->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams&) {
2818          this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTrunc);
2819      });
2820      PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int32, origin());
2821      patchpoint->append(arg, ValueRep::SomeRegister);
2822      patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
2823          jit.truncateDoubleToInt32(params[1].fpr(), params[0].gpr());
2824      });
2825      patchpoint->effects = Effects::none();
2826      result = patchpoint;
2827      return { };
2828  }
2829  
2830  template<>
2831  auto B3IRGenerator::addOp<OpType::I32TruncSF32>(ExpressionType arg, ExpressionType& result) -> PartialResult
2832  {
2833      Value* max = constant(Float, bitwise_cast<uint32_t>(-static_cast<float>(std::numeric_limits<int32_t>::min())));
2834      Value* min = constant(Float, bitwise_cast<uint32_t>(static_cast<float>(std::numeric_limits<int32_t>::min())));
2835      Value* outOfBounds = m_currentBlock->appendNew<Value>(m_proc, BitAnd, origin(),
2836          m_currentBlock->appendNew<Value>(m_proc, LessThan, origin(), arg, max),
2837          m_currentBlock->appendNew<Value>(m_proc, GreaterEqual, origin(), arg, min));
2838      outOfBounds = m_currentBlock->appendNew<Value>(m_proc, Equal, origin(), outOfBounds, constant(Int32, 0));
2839      CheckValue* trap = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(), outOfBounds);
2840      trap->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams&) {
2841          this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTrunc);
2842      });
2843      PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int32, origin());
2844      patchpoint->append(arg, ValueRep::SomeRegister);
2845      patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
2846          jit.truncateFloatToInt32(params[1].fpr(), params[0].gpr());
2847      });
2848      patchpoint->effects = Effects::none();
2849      result = patchpoint;
2850      return { };
2851  }
2852  
2853  
2854  template<>
2855  auto B3IRGenerator::addOp<OpType::I32TruncUF64>(ExpressionType arg, ExpressionType& result) -> PartialResult
2856  {
2857      Value* max = constant(Double, bitwise_cast<uint64_t>(static_cast<double>(std::numeric_limits<int32_t>::min()) * -2.0));
2858      Value* min = constant(Double, bitwise_cast<uint64_t>(-1.0));
2859      Value* outOfBounds = m_currentBlock->appendNew<Value>(m_proc, BitAnd, origin(),
2860          m_currentBlock->appendNew<Value>(m_proc, LessThan, origin(), arg, max),
2861          m_currentBlock->appendNew<Value>(m_proc, GreaterThan, origin(), arg, min));
2862      outOfBounds = m_currentBlock->appendNew<Value>(m_proc, Equal, origin(), outOfBounds, constant(Int32, 0));
2863      CheckValue* trap = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(), outOfBounds);
2864      trap->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams&) {
2865          this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTrunc);
2866      });
2867      PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int32, origin());
2868      patchpoint->append(arg, ValueRep::SomeRegister);
2869      patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
2870          jit.truncateDoubleToUint32(params[1].fpr(), params[0].gpr());
2871      });
2872      patchpoint->effects = Effects::none();
2873      result = patchpoint;
2874      return { };
2875  }
2876  
2877  template<>
2878  auto B3IRGenerator::addOp<OpType::I32TruncUF32>(ExpressionType arg, ExpressionType& result) -> PartialResult
2879  {
2880      Value* max = constant(Float, bitwise_cast<uint32_t>(static_cast<float>(std::numeric_limits<int32_t>::min()) * static_cast<float>(-2.0)));
2881      Value* min = constant(Float, bitwise_cast<uint32_t>(static_cast<float>(-1.0)));
2882      Value* outOfBounds = m_currentBlock->appendNew<Value>(m_proc, BitAnd, origin(),
2883          m_currentBlock->appendNew<Value>(m_proc, LessThan, origin(), arg, max),
2884          m_currentBlock->appendNew<Value>(m_proc, GreaterThan, origin(), arg, min));
2885      outOfBounds = m_currentBlock->appendNew<Value>(m_proc, Equal, origin(), outOfBounds, constant(Int32, 0));
2886      CheckValue* trap = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(), outOfBounds);
2887      trap->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams&) {
2888          this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTrunc);
2889      });
2890      PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int32, origin());
2891      patchpoint->append(arg, ValueRep::SomeRegister);
2892      patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
2893          jit.truncateFloatToUint32(params[1].fpr(), params[0].gpr());
2894      });
2895      patchpoint->effects = Effects::none();
2896      result = patchpoint;
2897      return { };
2898  }
2899  
2900  template<>
2901  auto B3IRGenerator::addOp<OpType::I64TruncSF64>(ExpressionType arg, ExpressionType& result) -> PartialResult
2902  {
2903      Value* max = constant(Double, bitwise_cast<uint64_t>(-static_cast<double>(std::numeric_limits<int64_t>::min())));
2904      Value* min = constant(Double, bitwise_cast<uint64_t>(static_cast<double>(std::numeric_limits<int64_t>::min())));
2905      Value* outOfBounds = m_currentBlock->appendNew<Value>(m_proc, BitAnd, origin(),
2906          m_currentBlock->appendNew<Value>(m_proc, LessThan, origin(), arg, max),
2907          m_currentBlock->appendNew<Value>(m_proc, GreaterEqual, origin(), arg, min));
2908      outOfBounds = m_currentBlock->appendNew<Value>(m_proc, Equal, origin(), outOfBounds, constant(Int32, 0));
2909      CheckValue* trap = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(), outOfBounds);
2910      trap->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams&) {
2911          this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTrunc);
2912      });
2913      PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int64, origin());
2914      patchpoint->append(arg, ValueRep::SomeRegister);
2915      patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
2916          jit.truncateDoubleToInt64(params[1].fpr(), params[0].gpr());
2917      });
2918      patchpoint->effects = Effects::none();
2919      result = patchpoint;
2920      return { };
2921  }
2922  
2923  template<>
2924  auto B3IRGenerator::addOp<OpType::I64TruncUF64>(ExpressionType arg, ExpressionType& result) -> PartialResult
2925  {
2926      Value* max = constant(Double, bitwise_cast<uint64_t>(static_cast<double>(std::numeric_limits<int64_t>::min()) * -2.0));
2927      Value* min = constant(Double, bitwise_cast<uint64_t>(-1.0));
2928      Value* outOfBounds = m_currentBlock->appendNew<Value>(m_proc, BitAnd, origin(),
2929          m_currentBlock->appendNew<Value>(m_proc, LessThan, origin(), arg, max),
2930          m_currentBlock->appendNew<Value>(m_proc, GreaterThan, origin(), arg, min));
2931      outOfBounds = m_currentBlock->appendNew<Value>(m_proc, Equal, origin(), outOfBounds, constant(Int32, 0));
2932      CheckValue* trap = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(), outOfBounds);
2933      trap->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams&) {
2934          this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTrunc);
2935      });
2936  
2937      Value* signBitConstant;
2938      if (isX86()) {
2939          // Since x86 doesn't have an instruction to convert floating points to unsigned integers, we at least try to do the smart thing if
2940          // the numbers are would be positive anyway as a signed integer. Since we cannot materialize constants into fprs we have b3 do it
2941          // so we can pool them if needed.
2942          signBitConstant = constant(Double, bitwise_cast<uint64_t>(static_cast<double>(std::numeric_limits<uint64_t>::max() - std::numeric_limits<int64_t>::max())));
2943      }
2944      PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int64, origin());
2945      patchpoint->append(arg, ValueRep::SomeRegister);
2946      if (isX86()) {
2947          patchpoint->append(signBitConstant, ValueRep::SomeRegister);
2948          patchpoint->numFPScratchRegisters = 1;
2949      }
2950      patchpoint->clobber(RegisterSet::macroScratchRegisters());
2951      patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
2952          AllowMacroScratchRegisterUsage allowScratch(jit);
2953          FPRReg scratch = InvalidFPRReg;
2954          FPRReg constant = InvalidFPRReg;
2955          if (isX86()) {
2956              scratch = params.fpScratch(0);
2957              constant = params[2].fpr();
2958          }
2959          jit.truncateDoubleToUint64(params[1].fpr(), params[0].gpr(), scratch, constant);
2960      });
2961      patchpoint->effects = Effects::none();
2962      result = patchpoint;
2963      return { };
2964  }
2965  
2966  template<>
2967  auto B3IRGenerator::addOp<OpType::I64TruncSF32>(ExpressionType arg, ExpressionType& result) -> PartialResult
2968  {
2969      Value* max = constant(Float, bitwise_cast<uint32_t>(-static_cast<float>(std::numeric_limits<int64_t>::min())));
2970      Value* min = constant(Float, bitwise_cast<uint32_t>(static_cast<float>(std::numeric_limits<int64_t>::min())));
2971      Value* outOfBounds = m_currentBlock->appendNew<Value>(m_proc, BitAnd, origin(),
2972          m_currentBlock->appendNew<Value>(m_proc, LessThan, origin(), arg, max),
2973          m_currentBlock->appendNew<Value>(m_proc, GreaterEqual, origin(), arg, min));
2974      outOfBounds = m_currentBlock->appendNew<Value>(m_proc, Equal, origin(), outOfBounds, constant(Int32, 0));
2975      CheckValue* trap = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(), outOfBounds);
2976      trap->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams&) {
2977          this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTrunc);
2978      });
2979      PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int64, origin());
2980      patchpoint->append(arg, ValueRep::SomeRegister);
2981      patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
2982          jit.truncateFloatToInt64(params[1].fpr(), params[0].gpr());
2983      });
2984      patchpoint->effects = Effects::none();
2985      result = patchpoint;
2986      return { };
2987  }
2988  
2989  template<>
2990  auto B3IRGenerator::addOp<OpType::I64TruncUF32>(ExpressionType arg, ExpressionType& result) -> PartialResult
2991  {
2992      Value* max = constant(Float, bitwise_cast<uint32_t>(static_cast<float>(std::numeric_limits<int64_t>::min()) * static_cast<float>(-2.0)));
2993      Value* min = constant(Float, bitwise_cast<uint32_t>(static_cast<float>(-1.0)));
2994      Value* outOfBounds = m_currentBlock->appendNew<Value>(m_proc, BitAnd, origin(),
2995          m_currentBlock->appendNew<Value>(m_proc, LessThan, origin(), arg, max),
2996          m_currentBlock->appendNew<Value>(m_proc, GreaterThan, origin(), arg, min));
2997      outOfBounds = m_currentBlock->appendNew<Value>(m_proc, Equal, origin(), outOfBounds, constant(Int32, 0));
2998      CheckValue* trap = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(), outOfBounds);
2999      trap->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams&) {
3000          this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTrunc);
3001      });
3002  
3003      Value* signBitConstant;
3004      if (isX86()) {
3005          // Since x86 doesn't have an instruction to convert floating points to unsigned integers, we at least try to do the smart thing if
3006          // the numbers would be positive anyway as a signed integer. Since we cannot materialize constants into fprs we have b3 do it
3007          // so we can pool them if needed.
3008          signBitConstant = constant(Float, bitwise_cast<uint32_t>(static_cast<float>(std::numeric_limits<uint64_t>::max() - std::numeric_limits<int64_t>::max())));
3009      }
3010      PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int64, origin());
3011      patchpoint->append(arg, ValueRep::SomeRegister);
3012      if (isX86()) {
3013          patchpoint->append(signBitConstant, ValueRep::SomeRegister);
3014          patchpoint->numFPScratchRegisters = 1;
3015      }
3016      patchpoint->clobber(RegisterSet::macroScratchRegisters());
3017      patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
3018          AllowMacroScratchRegisterUsage allowScratch(jit);
3019          FPRReg scratch = InvalidFPRReg;
3020          FPRReg constant = InvalidFPRReg;
3021          if (isX86()) {
3022              scratch = params.fpScratch(0);
3023              constant = params[2].fpr();
3024          }
3025          jit.truncateFloatToUint64(params[1].fpr(), params[0].gpr(), scratch, constant);
3026      });
3027      patchpoint->effects = Effects::none();
3028      result = patchpoint;
3029      return { };
3030  }
3031  
3032  } } // namespace JSC::Wasm
3033  
3034  #include "WasmB3IRGeneratorInlines.h"
3035  
3036  #endif // ENABLE(WEBASSEMBLY)