Bug 1630205: Reuse data structures even more across Cranelift compilations; r=rhunt
authorBenjamin Bouvier <benj@benj.me>
Thu, 16 Apr 2020 10:12:58 +0000
changeset 524381 8de9829f7c12b2fed6dcc5a85495b17c41a467c6
parent 524380 6f302c058d541c1a8c7bb4ee54c7fd7806981712
child 524382 5f14c7b634368ca1095a0db0ad8eafffb82f0c5b
push id113143
push userbbouvier@mozilla.com
push dateThu, 16 Apr 2020 10:14:14 +0000
treeherderautoland@8de9829f7c12 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrhunt
bugs1630205
milestone77.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 1630205: Reuse data structures even more across Cranelift compilations; r=rhunt Differential Revision: https://phabricator.services.mozilla.com/D71038
js/src/wasm/WasmCraneliftCompile.cpp
js/src/wasm/WasmCraneliftCompile.h
js/src/wasm/WasmGenerator.cpp
js/src/wasm/WasmGenerator.h
js/src/wasm/cranelift/src/bindings/mod.rs
js/src/wasm/cranelift/src/compile.rs
js/src/wasm/cranelift/src/lib.rs
js/src/wasm/cranelift/src/wasm2clif.rs
--- a/js/src/wasm/WasmCraneliftCompile.cpp
+++ b/js/src/wasm/WasmCraneliftCompile.cpp
@@ -278,23 +278,23 @@ static bool GenerateCraneliftCode(WasmMa
   return true;
 }
 
 // In Rust, a BatchCompiler variable has a lifetime constrained by those of its
 // associated StaticEnvironment and ModuleEnvironment. This RAII class ties
 // them together, as well as makes sure that the compiler is properly destroyed
 // when it exits scope.
 
-class AutoCranelift {
+class CraneliftContext {
   CraneliftStaticEnvironment staticEnv_;
   CraneliftModuleEnvironment env_;
   CraneliftCompiler* compiler_;
 
  public:
-  explicit AutoCranelift(const ModuleEnvironment& env)
+  explicit CraneliftContext(const ModuleEnvironment& env)
       : env_(env), compiler_(nullptr) {
     staticEnv_.ref_types_enabled = env.refTypesEnabled();
 #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_.static_memory_bound = HugeIndexRange;
       staticEnv_.memory_guard_size = HugeOffsetGuardLimit;
@@ -304,17 +304,17 @@ class AutoCranelift {
 #endif
     // Otherwise, heap bounds are stored in the `boundsCheckLimit` field
     // of TlsData.
   }
   bool init() {
     compiler_ = cranelift_compiler_create(&staticEnv_, &env_);
     return !!compiler_;
   }
-  ~AutoCranelift() {
+  ~CraneliftContext() {
     if (compiler_) {
       cranelift_compiler_destroy(compiler_);
     }
   }
   operator CraneliftCompiler*() { return compiler_; }
 };
 
 CraneliftFuncCompileInput::CraneliftFuncCompileInput(
@@ -429,32 +429,39 @@ bool wasm::CraneliftCompileFunctions(con
                                      const FuncCompileInputVector& inputs,
                                      CompiledCode* code, UniqueChars* error) {
   MOZ_RELEASE_ASSERT(CraneliftPlatformSupport());
 
   MOZ_ASSERT(env.tier() == Tier::Optimized);
   MOZ_ASSERT(env.optimizedBackend() == OptimizedBackend::Cranelift);
   MOZ_ASSERT(!env.isAsmJS());
 
-  AutoCranelift compiler(env);
-  if (!compiler.init()) {
-    return false;
-  }
-
   TempAllocator alloc(&lifo);
   JitContext jitContext(&alloc);
   WasmMacroAssembler masm(alloc);
   MOZ_ASSERT(IsCompilingWasm());
 
   // Swap in already-allocated empty vectors to avoid malloc/free.
   MOZ_ASSERT(code->empty());
-  if (!code->swap(masm)) {
+
+  CraneliftReusableData reusableContext;
+  if (!code->swapCranelift(masm, reusableContext)) {
     return false;
   }
 
+  if (!reusableContext) {
+    auto context = MakeUnique<CraneliftContext>(env);
+    if (!context || !context->init()) {
+      return false;
+    }
+    reusableContext.reset((void**)context.release());
+  }
+
+  CraneliftContext* compiler = (CraneliftContext*)reusableContext.get();
+
   // Disable instruction spew if we're going to disassemble after code
   // generation, or the output will be a mess.
 
   bool jitSpew = JitSpewEnabled(js::jit::JitSpew_Codegen);
   if (jitSpew) {
     DisableChannel(js::jit::JitSpew_Codegen);
   }
   auto reenableSpew = mozilla::MakeScopeExit([&] {
@@ -473,17 +480,17 @@ bool wasm::CraneliftCompileFunctions(con
 
     size_t previousStackmapCount = code->stackMaps.length();
 
     CraneliftFuncCompileInput clifInput(func);
     clifInput.stackmaps = (BD_Stackmaps*)&code->stackMaps;
 
     CraneliftCompiledFunc clifFunc;
 
-    if (!cranelift_compile_function(compiler, &clifInput, &clifFunc)) {
+    if (!cranelift_compile_function(*compiler, &clifInput, &clifFunc)) {
       *error = JS_smprintf("Cranelift error in clifFunc #%u", clifInput.index);
       return false;
     }
 
     uint32_t lineOrBytecode = func.lineOrBytecode;
     const FuncTypeWithId& funcType = *env.funcTypes[clifInput.index];
 
     FuncOffsets offsets;
@@ -538,17 +545,24 @@ bool wasm::CraneliftCompileFunctions(con
 
         JitSpew(JitSpew_Codegen, "# End of wasm cranelift code for index %d",
                 funcIndex);
       }
       js_free(codeBuf);
     }
   }
 
-  return code->swap(masm);
+  return code->swapCranelift(masm, reusableContext);
+}
+
+void wasm::CraneliftFreeReusableData(void* ptr) {
+  CraneliftContext* compiler = (CraneliftContext*)ptr;
+  if (compiler) {
+    js_delete(compiler);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //
 // Callbacks from Rust to C++.
 
 // Offsets assumed by the `make_heap()` function.
 static_assert(offsetof(wasm::TlsData, memoryBase) == 0, "memory base moved");
--- a/js/src/wasm/WasmCraneliftCompile.h
+++ b/js/src/wasm/WasmCraneliftCompile.h
@@ -31,23 +31,27 @@ namespace wasm {
 // device.  Usually you do *not* want this, you want CraneliftAvailable().
 MOZ_MUST_USE bool CraneliftPlatformSupport();
 
 // Generates code with Cranelift.
 MOZ_MUST_USE bool CraneliftCompileFunctions(
     const ModuleEnvironment& env, LifoAlloc& lifo,
     const FuncCompileInputVector& inputs, CompiledCode* code,
     UniqueChars* error);
+
+void CraneliftFreeReusableData(void* data);
 #else
 MOZ_MUST_USE inline bool CraneliftPlatformSupport() { return false; }
 
 MOZ_MUST_USE inline bool CraneliftCompileFunctions(
     const ModuleEnvironment& env, LifoAlloc& lifo,
     const FuncCompileInputVector& inputs, CompiledCode* code,
     UniqueChars* error) {
   MOZ_CRASH("Should not happen");
 }
+
+void CraneliftFreeReusableData(void* data) {}
 #endif
 
 }  // namespace wasm
 }  // namespace js
 
 #endif  // wasm_cranelift_compile_h
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -53,16 +53,25 @@ bool CompiledCode::swap(MacroAssembler& 
   callSites.swap(masm.callSites());
   callSiteTargets.swap(masm.callSiteTargets());
   trapSites.swap(masm.trapSites());
   symbolicAccesses.swap(masm.symbolicAccesses());
   codeLabels.swap(masm.codeLabels());
   return true;
 }
 
+bool CompiledCode::swapCranelift(MacroAssembler& masm,
+                                 CraneliftReusableData& data) {
+  if (!swap(masm)) {
+    return false;
+  }
+  std::swap(data, craneliftReusableData);
+  return true;
+}
+
 // ****************************************************************************
 // ModuleGenerator
 
 static const unsigned GENERATOR_LIFO_DEFAULT_CHUNK_SIZE = 4 * 1024;
 static const unsigned COMPILATION_LIFO_DEFAULT_CHUNK_SIZE = 64 * 1024;
 static const uint32_t BAD_CODE_RANGE = UINT32_MAX;
 
 ModuleGenerator::ModuleGenerator(const CompileArgs& args,
--- a/js/src/wasm/WasmGenerator.h
+++ b/js/src/wasm/WasmGenerator.h
@@ -48,40 +48,53 @@ struct FuncCompileInput {
         end(end),
         index(index),
         lineOrBytecode(lineOrBytecode),
         callSiteLineNums(std::move(callSiteLineNums)) {}
 };
 
 typedef Vector<FuncCompileInput, 8, SystemAllocPolicy> FuncCompileInputVector;
 
+void CraneliftFreeReusableData(void* ptr);
+
+struct CraneliftReusableDataDtor {
+  void operator()(void* ptr) { CraneliftFreeReusableData(ptr); }
+};
+
+using CraneliftReusableData =
+    mozilla::UniquePtr<void*, CraneliftReusableDataDtor>;
+
 // CompiledCode contains the resulting code and metadata for a set of compiled
 // input functions or stubs.
 
 struct CompiledCode {
   Bytes bytes;
   CodeRangeVector codeRanges;
   CallSiteVector callSites;
   CallSiteTargetVector callSiteTargets;
   TrapSiteVectorArray trapSites;
   SymbolicAccessVector symbolicAccesses;
   jit::CodeLabelVector codeLabels;
   StackMaps stackMaps;
+  CraneliftReusableData craneliftReusableData;
 
   MOZ_MUST_USE bool swap(jit::MacroAssembler& masm);
+  MOZ_MUST_USE bool swapCranelift(jit::MacroAssembler& masm,
+                                  CraneliftReusableData& craneliftData);
 
   void clear() {
     bytes.clear();
     codeRanges.clear();
     callSites.clear();
     callSiteTargets.clear();
     trapSites.clear();
     symbolicAccesses.clear();
     codeLabels.clear();
     stackMaps.clear();
+    // The cranelift reusable data resets itself lazily.
     MOZ_ASSERT(empty());
   }
 
   bool empty() {
     return bytes.empty() && codeRanges.empty() && callSites.empty() &&
            callSiteTargets.empty() && trapSites.empty() &&
            symbolicAccesses.empty() && codeLabels.empty() && stackMaps.empty();
   }
--- a/js/src/wasm/cranelift/src/bindings/mod.rs
+++ b/js/src/wasm/cranelift/src/bindings/mod.rs
@@ -193,16 +193,17 @@ impl FuncTypeWithId {
 
     pub fn id_tls_offset(self) -> usize {
         unsafe { low_level::funcType_idTlsOffset(self.0) }
     }
 }
 
 /// Thin wrapper for the CraneliftModuleEnvironment structure.
 
+#[derive(Clone, Copy)]
 pub struct ModuleEnvironment<'a> {
     env: &'a CraneliftModuleEnvironment,
 }
 
 impl<'a> ModuleEnvironment<'a> {
     pub fn new(env: &'a CraneliftModuleEnvironment) -> Self {
         Self { env }
     }
--- a/js/src/wasm/cranelift/src/compile.rs
+++ b/js/src/wasm/cranelift/src/compile.rs
@@ -68,84 +68,105 @@ impl CompiledFunc {
             rodata_relocs: vec![],
             code_buffer: vec![],
             code_size: 0,
             jumptables_size: 0,
             rodata_size: 0,
         }
     }
 
-    fn reset(self: &mut Self, frame_pushed: StackSize, contains_calls: bool) {
-        self.frame_pushed = frame_pushed;
-        self.contains_calls = contains_calls;
+    fn clear(&mut self) {
+        self.frame_pushed = 0;
+        self.contains_calls = false;
         self.metadata.clear();
         self.rodata_relocs.clear();
         self.code_buffer.clear();
         self.code_size = 0;
         self.jumptables_size = 0;
         self.rodata_size = 0;
     }
 }
 
 /// A batch compiler holds on to data structures that can be recycled for multiple function
 /// compilations.
-pub struct BatchCompiler<'a, 'b> {
-    static_environ: &'a bindings::StaticEnvironment,
-    environ: bindings::ModuleEnvironment<'b>,
+pub struct BatchCompiler<'static_env, 'module_env> {
+    // Attributes that are constant accross multiple compilations.
+    static_environ: &'static_env bindings::StaticEnvironment,
+    environ: bindings::ModuleEnvironment<'module_env>,
     isa: Box<dyn TargetIsa>,
-    context: Context,
+
+    // Stateless attributes.
+    func_translator: FuncTranslator,
     dummy_module_state: ModuleTranslationState,
-    trans: FuncTranslator,
+
+    // Mutable attributes.
+    /// Cranelift overall context.
+    context: Context,
+
+    /// Temporary storage for trap relocations before they're moved back to the CompiledFunc.
+    trap_relocs: Traps,
+
+    /// The translation from wasm to clif environment.
+    trans_env: TransEnv<'static_env, 'module_env>,
+
+    /// Results of the current compilation.
     pub current_func: CompiledFunc,
 }
 
-impl<'a, 'b> BatchCompiler<'a, 'b> {
+impl<'static_env, 'module_env> BatchCompiler<'static_env, 'module_env> {
     pub fn new(
-        static_environ: &'a bindings::StaticEnvironment,
-        environ: bindings::ModuleEnvironment<'b>,
+        static_environ: &'static_env bindings::StaticEnvironment,
+        environ: bindings::ModuleEnvironment<'module_env>,
     ) -> DashResult<Self> {
-        // TODO: The target ISA could be shared by multiple batch compilers across threads.
+        let isa = make_isa(static_environ)?;
+        let trans_env = TransEnv::new(&*isa, environ, static_environ);
         Ok(BatchCompiler {
             static_environ,
             environ,
-            isa: make_isa(static_environ)?,
-            context: Context::new(),
+            isa,
+            func_translator: FuncTranslator::new(),
             // TODO for Cranelift to support multi-value, feed it the real type section here.
             dummy_module_state: ModuleTranslationState::new(),
-            trans: FuncTranslator::new(),
+            context: Context::new(),
+            trap_relocs: Traps::new(),
+            trans_env,
             current_func: CompiledFunc::new(),
         })
     }
 
+    /// Clears internal data structures.
+    pub fn clear(&mut self) {
+        self.context.clear();
+        self.trap_relocs.clear();
+        self.trans_env.clear();
+        self.current_func.clear();
+    }
+
     pub fn compile(&mut self, stackmaps: bindings::Stackmaps) -> CodegenResult<()> {
         let info = self.context.compile(&*self.isa)?;
         debug!("Optimized wasm function IR: {}", self);
         self.binemit(info, stackmaps)
     }
 
     /// Translate the WebAssembly code to Cranelift IR.
     pub fn translate_wasm(&mut self, func: &bindings::FuncCompileInput) -> WasmResult<()> {
-        self.context.clear();
-
-        let tenv = &mut TransEnv::new(&*self.isa, &self.environ, self.static_environ);
-
         // Set up the signature before translating the WebAssembly byte code.
         // The translator refers to it.
         let index = FuncIndex::new(func.index as usize);
 
         self.context.func.signature =
             init_sig(&self.environ, self.static_environ.call_conv(), index)?;
         self.context.func.name = wasm_function_name(index);
 
-        self.trans.translate(
+        self.func_translator.translate(
             &self.dummy_module_state,
             func.bytecode(),
             func.offset_in_module as usize,
             &mut self.context.func,
-            tenv,
+            &mut self.trans_env,
         )?;
 
         info!("Translated wasm function {}.", func.index);
         debug!("Translated wasm function IR: {}", self);
         Ok(())
     }
 
     /// Emit binary machine code to `emitter`.
@@ -154,51 +175,53 @@ impl<'a, 'b> BatchCompiler<'a, 'b> {
         let frame_pushed = self.frame_pushed();
         let contains_calls = self.contains_calls();
 
         info!(
             "Emitting {} bytes, frame_pushed={}\n.",
             total_size, frame_pushed
         );
 
-        self.current_func.reset(frame_pushed, contains_calls);
+        self.current_func.frame_pushed = frame_pushed;
+        self.current_func.contains_calls = contains_calls;
 
         // TODO: If we can get a pointer into `size` pre-allocated bytes of memory, we wouldn't
         // have to allocate and copy here.
         // TODO(bbouvier) try to get this pointer from the C++ caller, with an unlikely callback to
         // C++ if the remaining size is smaller than  needed.
         if self.current_func.code_buffer.len() < total_size {
             let current_size = self.current_func.code_buffer.len();
             // There's no way to do a proper uninitialized reserve, so first reserve and then
             // unsafely set the final size.
             self.current_func
                 .code_buffer
                 .reserve(total_size - current_size);
             unsafe { self.current_func.code_buffer.set_len(total_size) };
         }
 
         {
-            let mut traps = Traps::new();
             let mut relocs = Relocations::new(
                 &mut self.current_func.metadata,
                 &mut self.current_func.rodata_relocs,
             );
 
             let code_buffer = &mut self.current_func.code_buffer;
             unsafe {
                 self.context.emit_to_memory(
                     &*self.isa,
                     code_buffer.as_mut_ptr(),
                     &mut relocs,
-                    &mut traps,
+                    &mut self.trap_relocs,
                     &mut NullStackmapSink {},
                 )
             };
 
-            self.current_func.metadata.append(&mut traps.metadata);
+            self.current_func
+                .metadata
+                .append(&mut self.trap_relocs.metadata);
         }
 
         if self.static_environ.ref_types_enabled {
             self.emit_stackmaps(stackmaps);
         }
 
         self.current_func.code_size = info.code_size;
         self.current_func.jumptables_size = info.jumptables_size;
@@ -255,17 +278,17 @@ impl<'a, 'b> BatchCompiler<'a, 'b> {
     /// Determine whether the current function may contain calls.
     fn contains_calls(&self) -> bool {
         // Conservatively, just check to see if it contains any function
         // signatures which could be called.
         !self.context.func.dfg.signatures.is_empty()
     }
 }
 
-impl<'a, 'b> fmt::Display for BatchCompiler<'a, 'b> {
+impl<'static_env, 'module_env> fmt::Display for BatchCompiler<'static_env, 'module_env> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!(f, "{}", self.context.func.display(self.isa.as_ref()))
     }
 }
 
 /// Create a Cranelift function name representing a WebAssembly function with `index`.
 pub fn wasm_function_name(func: FuncIndex) -> ExternalName {
     ExternalName::User {
@@ -427,16 +450,19 @@ struct Traps {
 }
 
 impl Traps {
     fn new() -> Self {
         Self {
             metadata: Vec::new(),
         }
     }
+    fn clear(&mut self) {
+        self.metadata.clear();
+    }
 }
 
 impl TrapSink for Traps {
     /// Add trap information for a specific offset.
     fn trap(&mut self, trap_offset: CodeOffset, loc: SourceLoc, trap: TrapCode) {
         // Translate the trap code into one of BaldrMonkey's trap codes.
         use ir::TrapCode::*;
         let bd_trap = match trap {
--- a/js/src/wasm/cranelift/src/lib.rs
+++ b/js/src/wasm/cranelift/src/lib.rs
@@ -80,16 +80,19 @@ pub unsafe extern "C" fn cranelift_compi
 pub unsafe extern "C" fn cranelift_compile_function(
     compiler: *mut BatchCompiler,
     data: *const FuncCompileInput,
     result: *mut CompiledFunc,
 ) -> bool {
     let compiler = compiler.as_mut().unwrap();
     let data = data.as_ref().unwrap();
 
+    // Reset the compiler to a clean state.
+    compiler.clear();
+
     if let Err(e) = compiler.translate_wasm(data) {
         error!("Wasm translation error: {}\n", e);
         info!("Translated function: {}", compiler);
         return false;
     };
 
     if let Err(e) = compiler.compile(data.stackmaps()) {
         error!("Cranelift compilation error: {}\n", e);
--- a/js/src/wasm/cranelift/src/wasm2clif.rs
+++ b/js/src/wasm/cranelift/src/wasm2clif.rs
@@ -254,21 +254,23 @@ const FN_POST_BARRIER: InstanceCall = In
     ret: None,
     failure_mode: FailureMode::Infallible,
 };
 
 // Custom trap codes specific to this embedding
 
 pub const TRAP_THROW_REPORTED: u16 = 1;
 
-/// A `TargetIsa` and `ModuleEnvironment` joined so we can implement `FuncEnvironment`.
-pub struct TransEnv<'a, 'b, 'c> {
-    isa: &'a dyn TargetIsa,
-    env: &'b bindings::ModuleEnvironment<'b>,
-    static_env: &'c bindings::StaticEnvironment,
+/// A translation context that implements `FuncEnvironment` for the specific Spidermonkey
+/// translation bits.
+pub struct TransEnv<'static_env, 'module_env> {
+    env: bindings::ModuleEnvironment<'module_env>,
+    static_env: &'static_env bindings::StaticEnvironment,
+
+    target_frontend_config: TargetFrontendConfig,
 
     /// Information about the function pointer tables `self.env` knowns about. Indexed by table
     /// index.
     tables: PrimaryMap<TableIndex, TableInfo>,
 
     /// For those signatures whose ID is stored in a global, keep track of the globals we have
     /// created so far.
     ///
@@ -302,38 +304,52 @@ pub struct TransEnv<'a, 'b, 'c> {
 
     /// The address of the `cx` field in the `wasm::TlsData` struct.
     cx_addr: PackedOption<ir::GlobalValue>,
 
     /// The address of the `realm` field in the `wasm::TlsData` struct.
     realm_addr: PackedOption<ir::GlobalValue>,
 }
 
-impl<'a, 'b, 'c> TransEnv<'a, 'b, 'c> {
+impl<'static_env, 'module_env> TransEnv<'static_env, 'module_env> {
     pub fn new(
-        isa: &'a dyn TargetIsa,
-        env: &'b bindings::ModuleEnvironment,
-        static_env: &'c bindings::StaticEnvironment,
+        isa: &dyn TargetIsa,
+        env: bindings::ModuleEnvironment<'module_env>,
+        static_env: &'static_env bindings::StaticEnvironment,
     ) -> Self {
         TransEnv {
-            isa,
             env,
             static_env,
+            target_frontend_config: isa.frontend_config(),
             tables: PrimaryMap::new(),
             signatures: HashMap::new(),
             func_gvs: SecondaryMap::new(),
             vmctx_gv: None.into(),
             instance_gv: None.into(),
             interrupt_gv: None.into(),
             symbolic: [None.into(); bindings::SymbolicAddress::Limit as usize],
             cx_addr: None.into(),
             realm_addr: None.into(),
         }
     }
 
+    pub fn clear(&mut self) {
+        self.tables.clear();
+        self.signatures.clear();
+        self.func_gvs.clear();
+        self.vmctx_gv = None.into();
+        self.instance_gv = None.into();
+        self.interrupt_gv = None.into();
+        for entry in self.symbolic.iter_mut() {
+            *entry = None.into();
+        }
+        self.cx_addr = None.into();
+        self.realm_addr = None.into();
+    }
+
     /// Get the `vmctx` global value.
     fn get_vmctx_gv(&mut self, func: &mut ir::Function) -> ir::GlobalValue {
         match self.vmctx_gv.expand() {
             Some(gv) => gv,
             None => {
                 // We need to allocate the global variable.
                 let gv = func.create_global_value(ir::GlobalValueData::VMContext);
                 self.vmctx_gv = Some(gv).into();
@@ -640,27 +656,26 @@ impl<'a, 'b, 'c> TransEnv<'a, 'b, 'c> {
             });
             (gv, 0.into())
         } else {
             (vmctx_gv, offset32(offset))
         }
     }
 }
 
-impl<'a, 'b, 'c> TargetEnvironment for TransEnv<'a, 'b, 'c> {
+impl<'static_env, 'module_env> TargetEnvironment for TransEnv<'static_env, 'module_env> {
     fn target_config(&self) -> TargetFrontendConfig {
-        self.isa.frontend_config()
+        self.target_frontend_config
     }
-
     fn pointer_type(&self) -> ir::Type {
         POINTER_TYPE
     }
 }
 
-impl<'a, 'b, 'c> FuncEnvironment for TransEnv<'a, 'b, 'c> {
+impl<'static_env, 'module_env> FuncEnvironment for TransEnv<'static_env, 'module_env> {
     fn make_global(
         &mut self,
         func: &mut ir::Function,
         index: GlobalIndex,
     ) -> WasmResult<GlobalVariable> {
         let global = self.env.global(index);
         if global.is_constant() {
             // Constant globals have a known value at compile time. We insert an instruction to