Bug 1518210 - Wasm: Conditionally compile bounds checks based on wasm::IsHugeMemoryEnabled. r=lth
☠☠ backed out by ea9924171afd ☠ ☠
authorRyan Hunt <rhunt@eqrion.net>
Fri, 30 Aug 2019 02:33:27 +0000
changeset 551298 40e3f38af193cf11426ed48d89780c1768deb05d
parent 551297 777aa22c9e8a19fc1df9e575db3b46702805bf23
child 551299 b88d66dddefff7b557143585fb73f2cf9d3c6648
push id11865
push userbtara@mozilla.com
push dateMon, 02 Sep 2019 08:54:37 +0000
treeherdermozilla-beta@37f59c4671b3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslth
bugs1518210
milestone70.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1518210 - Wasm: Conditionally compile bounds checks based on wasm::IsHugeMemoryEnabled. r=lth This commit allows us to conditionally compile bounds checks based on runtime support for huge memory. New flags to CompileArgs and CompilerEnvironment are added for whether we are using huge memory or not, and computed based on the global flag. Differential Revision: https://phabricator.services.mozilla.com/D41868
js/src/jit/CodeGenerator.cpp
js/src/jit/Lowering.cpp
js/src/jit/WasmBCE.cpp
js/src/wasm/AsmJS.cpp
js/src/wasm/WasmBaselineCompile.cpp
js/src/wasm/WasmCode.cpp
js/src/wasm/WasmCode.h
js/src/wasm/WasmCompile.cpp
js/src/wasm/WasmCompile.h
js/src/wasm/WasmCraneliftCompile.cpp
js/src/wasm/WasmGenerator.cpp
js/src/wasm/WasmIonCompile.cpp
js/src/wasm/WasmJS.cpp
js/src/wasm/WasmValidate.cpp
js/src/wasm/WasmValidate.h
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -13281,27 +13281,23 @@ void CodeGenerator::visitWasmInterruptCh
 void CodeGenerator::visitWasmTrap(LWasmTrap* lir) {
   MOZ_ASSERT(gen->compilingWasm());
   const MWasmTrap* mir = lir->mir();
 
   masm.wasmTrap(mir->trap(), mir->bytecodeOffset());
 }
 
 void CodeGenerator::visitWasmBoundsCheck(LWasmBoundsCheck* ins) {
-#ifdef WASM_HUGE_MEMORY
-  MOZ_CRASH("No wasm bounds check for huge memory");
-#else
   const MWasmBoundsCheck* mir = ins->mir();
   Register ptr = ToRegister(ins->ptr());
   Register boundsCheckLimit = ToRegister(ins->boundsCheckLimit());
   Label ok;
   masm.wasmBoundsCheck(Assembler::Below, ptr, boundsCheckLimit, &ok);
   masm.wasmTrap(wasm::Trap::OutOfBounds, mir->bytecodeOffset());
   masm.bind(&ok);
-#endif
 }
 
 void CodeGenerator::visitWasmAlignmentCheck(LWasmAlignmentCheck* ins) {
   const MWasmAlignmentCheck* mir = ins->mir();
   Register ptr = ToRegister(ins->ptr());
   Label ok;
   masm.branchTest32(Assembler::Zero, ptr, Imm32(mir->byteSize() - 1), &ok);
   masm.wasmTrap(wasm::Trap::UnalignedAccess, mir->bytecodeOffset());
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -4201,19 +4201,16 @@ void LIRGenerator::visitWasmAddOffset(MW
 }
 
 void LIRGenerator::visitWasmLoadTls(MWasmLoadTls* ins) {
   auto* lir = new (alloc()) LWasmLoadTls(useRegisterAtStart(ins->tlsPtr()));
   define(lir, ins);
 }
 
 void LIRGenerator::visitWasmBoundsCheck(MWasmBoundsCheck* ins) {
-#ifdef WASM_HUGE_MEMORY
-  MOZ_CRASH("No bounds checking on huge memory");
-#else
   MOZ_ASSERT(!ins->isRedundant());
 
   MDefinition* index = ins->index();
   MOZ_ASSERT(index->type() == MIRType::Int32);
 
   MDefinition* boundsCheckLimit = ins->boundsCheckLimit();
   MOZ_ASSERT(boundsCheckLimit->type() == MIRType::Int32);
 
@@ -4221,17 +4218,16 @@ void LIRGenerator::visitWasmBoundsCheck(
     auto* lir = new (alloc()) LWasmBoundsCheck(useRegisterAtStart(index),
                                                useRegister(boundsCheckLimit));
     defineReuseInput(lir, ins, 0);
   } else {
     auto* lir = new (alloc()) LWasmBoundsCheck(
         useRegisterAtStart(index), useRegisterAtStart(boundsCheckLimit));
     add(lir, ins);
   }
-#endif
 }
 
 void LIRGenerator::visitWasmAlignmentCheck(MWasmAlignmentCheck* ins) {
   MDefinition* index = ins->index();
   MOZ_ASSERT(index->type() == MIRType::Int32);
 
   auto* lir = new (alloc()) LWasmAlignmentCheck(useRegisterAtStart(index));
   add(lir, ins);
--- a/js/src/jit/WasmBCE.cpp
+++ b/js/src/jit/WasmBCE.cpp
@@ -43,21 +43,16 @@ bool jit::EliminateBoundsChecks(MIRGener
           MDefinition* addr = bc->index();
 
           // Eliminate constant-address bounds checks to addresses below
           // the heap minimum.
           //
           // The payload of the MConstant will be Double if the constant
           // result is above 2^31-1, but we don't care about that for BCE.
 
-#ifndef WASM_HUGE_MEMORY
-          MOZ_ASSERT(wasm::MaxMemoryAccessSize < wasm::GuardSize,
-                     "Guard page handles partial out-of-bounds");
-#endif
-
           if (addr->isConstant() &&
               addr->toConstant()->type() == MIRType::Int32 &&
               uint32_t(addr->toConstant()->toInt32()) <
                   mir->minWasmHeapLength()) {
             bc->setRedundant();
             if (JitOptions.spectreIndexMasking) {
               bc->replaceAllUsesWith(addr);
             } else {
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -1350,17 +1350,17 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
         funcDefs_(cx),
         tables_(cx),
         globalMap_(cx),
         sigSet_(cx),
         funcImportMap_(cx),
         arrayViews_(cx),
         compilerEnv_(CompileMode::Once, Tier::Optimized, OptimizedBackend::Ion,
                      DebugEnabled::False, /* ref types */ false,
-                     /* gc types */ false),
+                     /* gc types */ false, /* huge memory */ false),
         env_(&compilerEnv_, Shareable::False, ModuleKind::AsmJS) {
     compilerEnv_.computeParameters(/* gc types */ false);
     env_.minMemoryLength = RoundUpToNextValidAsmJSHeapLength(0);
   }
 
  protected:
   MOZ_MUST_USE bool addStandardLibraryMathInfo() {
     static constexpr struct {
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -5287,38 +5287,36 @@ class BaseCompiler final : public BaseCo
       masm.branchTest32(Assembler::Zero, ptr, Imm32(access->byteSize() - 1),
                         &ok);
       masm.wasmTrap(Trap::UnalignedAccess, bytecodeOffset());
       masm.bind(&ok);
     }
 
     // Ensure no tls if we don't need it.
 
-#ifdef WASM_HUGE_MEMORY
-    // We have HeapReg and no bounds checking and need load neither
-    // memoryBase nor boundsCheckLimit from tls.
-    MOZ_ASSERT_IF(check->omitBoundsCheck, tls.isInvalid());
-#endif
+    if (env_.hugeMemoryEnabled()) {
+      // We have HeapReg and no bounds checking and need load neither
+      // memoryBase nor boundsCheckLimit from tls.
+      MOZ_ASSERT_IF(check->omitBoundsCheck, tls.isInvalid());
+    }
 #ifdef JS_CODEGEN_ARM
     // We have HeapReg on ARM and don't need to load the memoryBase from tls.
     MOZ_ASSERT_IF(check->omitBoundsCheck, tls.isInvalid());
 #endif
 
     // Bounds check if required.
 
-#ifndef WASM_HUGE_MEMORY
-    if (!check->omitBoundsCheck) {
+    if (!env_.hugeMemoryEnabled() && !check->omitBoundsCheck) {
       Label ok;
       masm.wasmBoundsCheck(Assembler::Below, ptr,
                            Address(tls, offsetof(TlsData, boundsCheckLimit)),
                            &ok);
       masm.wasmTrap(Trap::OutOfBounds, bytecodeOffset());
       masm.bind(&ok);
     }
-#endif
   }
 
 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) ||      \
     defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS32) || \
     defined(JS_CODEGEN_MIPS64)
   BaseIndex prepareAtomicMemoryAccess(MemoryAccessDesc* access,
                                       AccessCheck* check, RegI32 tls,
                                       RegI32 ptr) {
@@ -5364,22 +5362,20 @@ class BaseCompiler final : public BaseCo
     }
 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
     *temp1 = needI32();
 #endif
   }
 
   MOZ_MUST_USE bool needTlsForAccess(const AccessCheck& check) {
 #if defined(JS_CODEGEN_X86)
+    // x86 requires Tls for memory base
     return true;
-#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || \
-    defined(JS_CODEGEN_MIPS64) || !defined(WASM_HUGE_MEMORY)
-    return !check.omitBoundsCheck;
-#else
-    return false;
+#else
+    return !env_.hugeMemoryEnabled() && !check.omitBoundsCheck;
 #endif
   }
 
   // ptr and dest may be the same iff dest is I32.
   // This may destroy ptr even if ptr and dest are not the same.
   MOZ_MUST_USE bool load(MemoryAccessDesc* access, AccessCheck* check,
                          RegI32 tls, RegI32 ptr, AnyReg dest, RegI32 temp1,
                          RegI32 temp2, RegI32 temp3) {
--- a/js/src/wasm/WasmCode.cpp
+++ b/js/src/wasm/WasmCode.cpp
@@ -886,45 +886,50 @@ bool MetadataTier::clone(const MetadataT
 
   return true;
 }
 
 size_t Metadata::serializedSize() const {
   return sizeof(pod()) + SerializedVectorSize(funcTypeIds) +
          SerializedPodVectorSize(globals) + SerializedPodVectorSize(tables) +
          sizeof(moduleName) + SerializedPodVectorSize(funcNames) +
-         filename.serializedSize() + sourceMapURL.serializedSize();
+         filename.serializedSize() + sourceMapURL.serializedSize() +
+         sizeof(uint8_t);
 }
 
 uint8_t* Metadata::serialize(uint8_t* cursor) const {
   MOZ_ASSERT(!debugEnabled && debugFuncArgTypes.empty() &&
              debugFuncReturnTypes.empty());
   cursor = WriteBytes(cursor, &pod(), sizeof(pod()));
   cursor = SerializeVector(cursor, funcTypeIds);
   cursor = SerializePodVector(cursor, globals);
   cursor = SerializePodVector(cursor, tables);
   cursor = WriteBytes(cursor, &moduleName, sizeof(moduleName));
   cursor = SerializePodVector(cursor, funcNames);
   cursor = filename.serialize(cursor);
   cursor = sourceMapURL.serialize(cursor);
+  cursor = WriteScalar(cursor, uint8_t(omitsBoundsChecks));
   return cursor;
 }
 
 /* static */ const uint8_t* Metadata::deserialize(const uint8_t* cursor) {
+  uint8_t scalarOmitsBoundsChecks = 0;
   (cursor = ReadBytes(cursor, &pod(), sizeof(pod()))) &&
       (cursor = DeserializeVector(cursor, &funcTypeIds)) &&
       (cursor = DeserializePodVector(cursor, &globals)) &&
       (cursor = DeserializePodVector(cursor, &tables)) &&
       (cursor = ReadBytes(cursor, &moduleName, sizeof(moduleName))) &&
       (cursor = DeserializePodVector(cursor, &funcNames)) &&
       (cursor = filename.deserialize(cursor)) &&
-      (cursor = sourceMapURL.deserialize(cursor));
+      (cursor = sourceMapURL.deserialize(cursor)) &&
+      (cursor = ReadScalar<uint8_t>(cursor, &scalarOmitsBoundsChecks));
   debugEnabled = false;
   debugFuncArgTypes.clear();
   debugFuncReturnTypes.clear();
+  omitsBoundsChecks = !!scalarOmitsBoundsChecks;
   return cursor;
 }
 
 size_t Metadata::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const {
   return SizeOfVectorExcludingThis(funcTypeIds, mallocSizeOf) +
          globals.sizeOfExcludingThis(mallocSizeOf) +
          tables.sizeOfExcludingThis(mallocSizeOf) +
          funcNames.sizeOfExcludingThis(mallocSizeOf) +
--- a/js/src/wasm/WasmCode.h
+++ b/js/src/wasm/WasmCode.h
@@ -334,16 +334,17 @@ typedef Vector<ValTypeVector, 0, SystemA
 typedef Vector<ExprType, 0, SystemAllocPolicy> FuncReturnTypesVector;
 
 struct Metadata : public ShareableBase<Metadata>, public MetadataCacheablePod {
   FuncTypeWithIdVector funcTypeIds;
   GlobalDescVector globals;
   TableDescVector tables;
   CacheableChars filename;
   CacheableChars sourceMapURL;
+  bool omitsBoundsChecks;
 
   // namePayload points at the name section's CustomSection::payload so that
   // the Names (which are use payload-relative offsets) can be used
   // independently of the Module without duplicating the name section.
   SharedBytes namePayload;
   Maybe<Name> moduleName;
   NameVector funcNames;
 
--- a/js/src/wasm/WasmCompile.cpp
+++ b/js/src/wasm/WasmCompile.cpp
@@ -23,16 +23,17 @@
 
 #include "jit/ProcessExecutableMemory.h"
 #include "util/Text.h"
 #include "wasm/WasmBaselineCompile.h"
 #include "wasm/WasmCraneliftCompile.h"
 #include "wasm/WasmGenerator.h"
 #include "wasm/WasmIonCompile.h"
 #include "wasm/WasmOpIter.h"
+#include "wasm/WasmProcess.h"
 #include "wasm/WasmSignalHandlers.h"
 #include "wasm/WasmValidate.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
 
 uint32_t wasm::ObservedCPUFeatures() {
@@ -136,16 +137,17 @@ SharedCompileArgs CompileArgs::build(JSC
 
   target->baselineEnabled = baseline;
   target->ionEnabled = ion;
   target->craneliftEnabled = cranelift;
   target->debugEnabled = debug;
   target->sharedMemoryEnabled = sharedMemory;
   target->forceTiering = forceTiering;
   target->gcEnabled = gc;
+  target->hugeMemory = wasm::IsHugeMemoryEnabled();
 
   return target;
 }
 
 // Classify the current system as one of a set of recognizable classes.  This
 // really needs to get our tier-1 systems right.
 //
 // TODO: We don't yet have a good measure of how fast a system is.  We
@@ -431,24 +433,26 @@ static bool TieringBeneficial(uint32_t c
 
 CompilerEnvironment::CompilerEnvironment(const CompileArgs& args)
     : state_(InitialWithArgs), args_(&args) {}
 
 CompilerEnvironment::CompilerEnvironment(CompileMode mode, Tier tier,
                                          OptimizedBackend optimizedBackend,
                                          DebugEnabled debugEnabled,
                                          bool refTypesConfigured,
-                                         bool gcTypesConfigured)
+                                         bool gcTypesConfigured,
+                                         bool hugeMemory)
     : state_(InitialWithModeTierDebug),
       mode_(mode),
       tier_(tier),
       optimizedBackend_(optimizedBackend),
       debug_(debugEnabled),
       refTypes_(refTypesConfigured),
-      gcTypes_(gcTypesConfigured) {}
+      gcTypes_(gcTypesConfigured),
+      hugeMemory_(hugeMemory) {}
 
 void CompilerEnvironment::computeParameters(bool gcFeatureOptIn) {
   MOZ_ASSERT(state_ == InitialWithModeTierDebug);
 
   if (gcTypes_) {
     gcTypes_ = gcFeatureOptIn;
   }
   state_ = Computed;
@@ -463,16 +467,17 @@ void CompilerEnvironment::computeParamet
   }
 
   bool gcEnabled = args_->gcEnabled && gcFeatureOptIn;
   bool baselineEnabled = args_->baselineEnabled;
   bool ionEnabled = args_->ionEnabled;
   bool debugEnabled = args_->debugEnabled;
   bool craneliftEnabled = args_->craneliftEnabled;
   bool forceTiering = args_->forceTiering;
+  bool hugeMemory = args_->hugeMemory;
 
   bool hasSecondTier = ionEnabled || craneliftEnabled;
   MOZ_ASSERT_IF(gcEnabled || debugEnabled, baselineEnabled);
   MOZ_ASSERT_IF(forceTiering, baselineEnabled && hasSecondTier);
 
   // HasCompilerSupport() should prevent failure here
   MOZ_RELEASE_ASSERT(baselineEnabled || ionEnabled || craneliftEnabled);
 
@@ -493,16 +498,17 @@ void CompilerEnvironment::computeParamet
   }
 
   optimizedBackend_ =
       craneliftEnabled ? OptimizedBackend::Cranelift : OptimizedBackend::Ion;
 
   debug_ = debugEnabled ? DebugEnabled::True : DebugEnabled::False;
   gcTypes_ = gcEnabled;
   refTypes_ = !craneliftEnabled;
+  hugeMemory_ = hugeMemory;
   state_ = Computed;
 }
 
 template <class DecoderT>
 static bool DecodeFunctionBody(DecoderT& d, ModuleGenerator& mg,
                                uint32_t funcIndex) {
   uint32_t bodySize;
   if (!d.readVarU32(&bodySize)) {
@@ -599,17 +605,18 @@ void wasm::CompileTier2(const CompileArg
   bool gcTypesConfigured = false;  // No optimized backend support yet
   bool refTypesConfigured = !args.craneliftEnabled;
   OptimizedBackend optimizedBackend = args.craneliftEnabled
                                           ? OptimizedBackend::Cranelift
                                           : OptimizedBackend::Ion;
 
   CompilerEnvironment compilerEnv(CompileMode::Tier2, Tier::Optimized,
                                   optimizedBackend, DebugEnabled::False,
-                                  refTypesConfigured, gcTypesConfigured);
+                                  refTypesConfigured, gcTypesConfigured,
+                                  args.hugeMemory);
 
   ModuleEnvironment env(&compilerEnv, args.sharedMemoryEnabled
                                           ? Shareable::True
                                           : Shareable::False);
   if (!DecodeModuleEnvironment(d, &env)) {
     return;
   }
 
--- a/js/src/wasm/WasmCompile.h
+++ b/js/src/wasm/WasmCompile.h
@@ -52,16 +52,17 @@ struct CompileArgs : ShareableBase<Compi
 
   bool baselineEnabled;
   bool ionEnabled;
   bool craneliftEnabled;
   bool debugEnabled;
   bool sharedMemoryEnabled;
   bool forceTiering;
   bool gcEnabled;
+  bool hugeMemory;
 
   // CompileArgs has two constructors:
   //
   // - one through a factory function `build`, which checks that flags are
   // consistent with each other.
   // - one that gives complete access to underlying fields.
   //
   // You should use the first one in general, unless you have a very good
@@ -73,17 +74,18 @@ struct CompileArgs : ShareableBase<Compi
   explicit CompileArgs(ScriptedCaller&& scriptedCaller)
       : scriptedCaller(std::move(scriptedCaller)),
         baselineEnabled(false),
         ionEnabled(false),
         craneliftEnabled(false),
         debugEnabled(false),
         sharedMemoryEnabled(false),
         forceTiering(false),
-        gcEnabled(false) {}
+        gcEnabled(false),
+        hugeMemory(false) {}
 };
 
 // Return the estimated compiled (machine) code size for the given bytecode size
 // compiled at the given tier.
 
 double EstimateCompiledCodeSize(Tier tier, size_t bytecodeSize);
 
 // Compile the given WebAssembly bytecode with the given arguments into a
--- a/js/src/wasm/WasmCraneliftCompile.cpp
+++ b/js/src/wasm/WasmCraneliftCompile.cpp
@@ -222,17 +222,27 @@ static bool GenerateCraneliftCode(WasmMa
 
 class AutoCranelift {
   CraneliftStaticEnvironment staticEnv_;
   CraneliftModuleEnvironment env_;
   CraneliftCompiler* compiler_;
 
  public:
   explicit AutoCranelift(const ModuleEnvironment& env)
-      : env_(env), compiler_(nullptr) {}
+      : env_(env), compiler_(nullptr) {
+#ifdef WASM_SUPPORTS_HUGE_MEMORY
+    if (env.hugeMemoryEnabled()) {
+      // In the huge memory configuration, we always reserve the full 4 GB
+      // index space for a heap.
+      staticEnv_.staticMemoryBound = HugeIndexRange;
+    }
+#endif
+    // Otherwise, heap bounds are stored in the `boundsCheckLimit` field
+    // of TlsData.
+  }
   bool init() {
     compiler_ = cranelift_compiler_create(&staticEnv_, &env_);
     return !!compiler_;
   }
   ~AutoCranelift() {
     if (compiler_) {
       cranelift_compiler_destroy(compiler_);
     }
@@ -242,20 +252,18 @@ class AutoCranelift {
 
 CraneliftFuncCompileInput::CraneliftFuncCompileInput(
     const FuncCompileInput& func)
     : bytecode(func.begin),
       bytecodeSize(func.end - func.begin),
       index(func.index),
       offset_in_module(func.lineOrBytecode) {}
 
-#ifndef WASM_HUGE_MEMORY
 static_assert(offsetof(TlsData, boundsCheckLimit) == sizeof(size_t),
               "fix make_heap() in wasm2clif.rs");
-#endif
 
 CraneliftStaticEnvironment::CraneliftStaticEnvironment()
     :
 #ifdef JS_CODEGEN_X64
       hasSse2(Assembler::HasSSE2()),
       hasSse3(Assembler::HasSSE3()),
       hasSse41(Assembler::HasSSE41()),
       hasSse42(Assembler::HasSSE42()),
@@ -275,27 +283,17 @@ CraneliftStaticEnvironment::CraneliftSta
       hasBmi2(false),
       hasLzcnt(false),
 #endif
 #if defined(XP_WIN)
       platformIsWindows(true),
 #else
       platformIsWindows(false),
 #endif
-      staticMemoryBound(
-#ifdef WASM_HUGE_MEMORY
-          // In the huge memory configuration, we always reserve the full 4 GB
-          // index space for a heap.
-          IndexRange
-#else
-          // Otherwise, heap bounds are stored in the `boundsCheckLimit` field
-          // of TlsData.
-          0
-#endif
-          ),
+      staticMemoryBound(0),
       memoryGuardSize(OffsetGuardLimit),
       instanceTlsOffset(offsetof(TlsData, instance)),
       interruptTlsOffset(offsetof(TlsData, interrupt)),
       cxTlsOffset(offsetof(TlsData, cx)),
       realmCxOffset(JSContext::offsetOfRealm()),
       realmTlsOffset(offsetof(TlsData, realm)),
       realmFuncImportTlsOffset(offsetof(FuncImportTls, realm)) {
 }
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -1070,16 +1070,17 @@ SharedMetadata ModuleGenerator::finishMe
   metadata_->minMemoryLength = env_->minMemoryLength;
   metadata_->maxMemoryLength = env_->maxMemoryLength;
   metadata_->startFuncIndex = env_->startFuncIndex;
   metadata_->tables = std::move(env_->tables);
   metadata_->globals = std::move(env_->globals);
   metadata_->nameCustomSectionIndex = env_->nameCustomSectionIndex;
   metadata_->moduleName = env_->moduleName;
   metadata_->funcNames = std::move(env_->funcNames);
+  metadata_->omitsBoundsChecks = env_->hugeMemoryEnabled();
 
   // Copy over additional debug information.
 
   if (env_->debugEnabled()) {
     metadata_->debugEnabled = true;
 
     const size_t numFuncTypes = env_->funcTypes.length();
     if (!metadata_->debugFuncArgTypes.resize(numFuncTypes)) {
--- a/js/src/wasm/WasmIonCompile.cpp
+++ b/js/src/wasm/WasmIonCompile.cpp
@@ -570,21 +570,19 @@ class FunctionCompiler {
                              offsetof(wasm::TlsData, memoryBase),
                              MIRType::Pointer, aliases);
     curBlock_->add(load);
 #endif
     return load;
   }
 
   MWasmLoadTls* maybeLoadBoundsCheckLimit() {
-#ifdef WASM_HUGE_MEMORY
-    if (!env_.isAsmJS()) {
+    if (env_.hugeMemoryEnabled()) {
       return nullptr;
     }
-#endif
     AliasSet aliases = env_.maxMemoryLength.isSome()
                            ? AliasSet::None()
                            : AliasSet::Load(AliasSet::WasmHeapMeta);
     auto load = MWasmLoadTls::New(alloc(), tlsPointer_,
                                   offsetof(wasm::TlsData, boundsCheckLimit),
                                   MIRType::Int32, aliases);
     curBlock_->add(load);
     return load;
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -36,16 +36,17 @@
 #include "vm/Interpreter.h"
 #include "vm/StringType.h"
 #include "wasm/WasmBaselineCompile.h"
 #include "wasm/WasmCompile.h"
 #include "wasm/WasmCraneliftCompile.h"
 #include "wasm/WasmInstance.h"
 #include "wasm/WasmIonCompile.h"
 #include "wasm/WasmModule.h"
+#include "wasm/WasmProcess.h"
 #include "wasm/WasmSignalHandlers.h"
 #include "wasm/WasmStubs.h"
 #include "wasm/WasmValidate.h"
 
 #include "vm/ArrayBufferObject-inl.h"
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
@@ -489,16 +490,20 @@ bool wasm::CompileAndSerialize(const Sha
   }
 
   // The caller has ensured HasCachingSupport(). Moreover, we want to ensure
   // we go straight to tier-2 so that we synchronously call
   // JS::OptimizedEncodingListener::storeOptimizedEncoding().
   compileArgs->baselineEnabled = false;
   compileArgs->ionEnabled = true;
 
+  // The caller must ensure that huge memory support is configured the same in
+  // the receiving process of this serialized module.
+  compileArgs->hugeMemory = wasm::IsHugeMemoryEnabled();
+
   SerializeListener listener(serialized);
 
   UniqueChars error;
   UniqueCharsVector warnings;
   SharedModule module =
       CompileBuffer(*compileArgs, bytecode, &error, &warnings, &listener);
   if (!module) {
     fprintf(stderr, "Compilation error: %s\n", error ? error.get() : "oom");
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -2896,20 +2896,21 @@ bool wasm::DecodeModuleTail(Decoder& d, 
 // Validate algorithm.
 
 bool wasm::Validate(JSContext* cx, const ShareableBytes& bytecode,
                     UniqueChars* error) {
   Decoder d(bytecode.bytes, 0, error);
 
   bool gcTypesConfigured = HasGcSupport(cx);
   bool refTypesConfigured = HasReftypesSupport(cx);
-
-  CompilerEnvironment compilerEnv(CompileMode::Once, Tier::Optimized,
-                                  OptimizedBackend::Ion, DebugEnabled::False,
-                                  refTypesConfigured, gcTypesConfigured);
+  bool hugeMemory = false;
+
+  CompilerEnvironment compilerEnv(
+      CompileMode::Once, Tier::Optimized, OptimizedBackend::Ion,
+      DebugEnabled::False, refTypesConfigured, gcTypesConfigured, hugeMemory);
   ModuleEnvironment env(
       &compilerEnv,
       cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled()
           ? Shareable::True
           : Shareable::False);
   if (!DecodeModuleEnvironment(d, &env)) {
     return false;
   }
--- a/js/src/wasm/WasmValidate.h
+++ b/js/src/wasm/WasmValidate.h
@@ -66,31 +66,32 @@ struct CompilerEnvironment {
     // Value in the other two states.
     struct {
       CompileMode mode_;
       Tier tier_;
       OptimizedBackend optimizedBackend_;
       DebugEnabled debug_;
       bool refTypes_;
       bool gcTypes_;
+      bool hugeMemory_;
     };
   };
 
  public:
   // Retain a reference to the CompileArgs. A subsequent computeParameters()
   // will compute all parameters from the CompileArgs and additional values.
   explicit CompilerEnvironment(const CompileArgs& args);
 
   // Save the provided values for mode, tier, and debug, and the initial value
   // for gcTypes/refTypes. A subsequent computeParameters() will compute the
   // final value of gcTypes/refTypes.
   CompilerEnvironment(CompileMode mode, Tier tier,
                       OptimizedBackend optimizedBackend,
                       DebugEnabled debugEnabled, bool refTypesConfigured,
-                      bool gcTypesConfigured);
+                      bool gcTypesConfigured, bool hugeMemory);
 
   // Compute any remaining compilation parameters.
   void computeParameters(Decoder& d, bool gcFeatureOptIn);
 
   // Compute any remaining compilation parameters.  Only use this method if
   // the CompilerEnvironment was created with values for mode, tier, and
   // debug.
   void computeParameters(bool gcFeatureOptIn);
@@ -115,16 +116,20 @@ struct CompilerEnvironment {
   bool gcTypes() const {
     MOZ_ASSERT(isComputed());
     return gcTypes_;
   }
   bool refTypes() const {
     MOZ_ASSERT(isComputed());
     return refTypes_;
   }
+  bool hugeMemory() const {
+    MOZ_ASSERT(isComputed());
+    return hugeMemory_;
+  }
 };
 
 // ModuleEnvironment contains all the state necessary to process or render
 // functions, and all of the state necessary to validate all aspects of the
 // functions.
 //
 // A ModuleEnvironment is created by decoding all the sections before the wasm
 // code section and then used immutably during. When compiling a module using a
@@ -205,16 +210,19 @@ struct ModuleEnvironment {
   bool gcTypesEnabled() const { return compilerEnv->gcTypes(); }
   bool refTypesEnabled() const { return compilerEnv->refTypes(); }
   bool usesMemory() const { return memoryUsage != MemoryUsage::None; }
   bool usesSharedMemory() const { return memoryUsage == MemoryUsage::Shared; }
   bool isAsmJS() const { return kind == ModuleKind::AsmJS; }
   bool debugEnabled() const {
     return compilerEnv->debug() == DebugEnabled::True;
   }
+  bool hugeMemoryEnabled() const {
+    return !isAsmJS() && compilerEnv->hugeMemory();
+  }
   bool funcIsImport(uint32_t funcIndex) const {
     return funcIndex < funcImportGlobalDataOffsets.length();
   }
   bool isRefSubtypeOf(ValType one, ValType two) const {
     MOZ_ASSERT(one.isReference());
     MOZ_ASSERT(two.isReference());
 #if defined(ENABLE_WASM_REFTYPES)
 #  if defined(ENABLE_WASM_GC)