Switch FTL GetById/PutById IC's over to using AnyRegCC
https://bugs.webkit.org/show_bug.cgi?id=124094

Source/JavaScriptCore: 

Reviewed by Sam Weinig.
        
This closes the loop on inline caches (IC's) in the FTL. The goal is to have IC's
in LLVM-generated code that are just as efficient (if not more so) than what a
custom JIT could do. As in zero sources of overhead. Not a single extra instruction
or even register allocation pathology. We accomplish this by having two thingies in
LLVM. First is the llvm.experimental.patchpoint intrinsic, which is sort of an
inline machine code snippet that we can fill in with whatever we want and then
modify subsequently. But you have only two choices of how to pass values to a
patchpoint: (1) via the calling convention or (2) via the stackmap. Neither are good
for operands to an IC (like the base pointer for a GetById, for example). (1) is bad
because it results in things being pinned to certain registers a priori; a custom
JIT (like the DFG) will not pin IC operands to any registers a priori but will allow
the register allocator to do whatever it wants. (2) is bad because the operands may
be spilled or may be represented in other crazy ways. You generally want an IC to
have its operands in registers. Also, patchpoints only return values using the
calling convention, which is unfortunate since it pins the return value to a
register a priori. This is where the second thingy comes in: the AnyRegCC. This is
a special calling convention only for use with patchpoints. It means that arguments
passed "by CC" in the patchpoint can be placed in any register, and the register
that gets used is reported as part of the stackmap. It also means that the return
value (if there is one) can be placed in any register, and the stackmap will tell
you which one it was. Thus, patchpoints combined with AnyRegCC mean that you not
only get the kind of self-modifying code that you want for IC's, but you also get
all of the register allocation goodness that a custom JIT would have given you.
Except that you're getting it from LLVM and not a custom JIT. Awesome.
        
Even though all of the fun stuff is on the LLVM side, this patch was harder than
you'd expect.
        
First the obvious bits:
        
- IC patchpoints now use AnyRegCC instead of the C CC. (CC = calling convention.)
        
- FTL::fixFunctionBasedOnStackMaps() now correctly figures out which registers the
  IC is supposed to use instead of assuming C CC argument registers.
        
And then all of the stuff that broke and that this patch fixes:
        
- IC sizing based on generating a dummy IC (what FTLInlineCacheSize did) is totally
  bad on x86-64, where various register permutations lead to bizarre header bytes
  and eclectic SIB encodings. I changed that to have magic constants, for now.
        
- Slow path calls didn't preserve the CC return register.
        
- Repatch's scratch register allocation would get totally confused if the operand
  registers weren't one of the DFG-style "temp" registers. And by "totally confused"
  I mean that it would crash.
        
- We assumed that r10 is callee-saved. It's not. That one dude's PPT about x86-64
  cdecl that I found on the intertubes was not a trustworthy source of information,
  apparently.
        
- Call repatching didn't know that the FTL does its IC slow calls via specially
  generated thunks. This was particularly fun to fix: basically, now when we relink
  an IC call in the FTL, we use the old call target to find the SlowPathCallKey,
  which tells us everything we need to know to generate (or look up) a new thunk for
  the new function we want to call.
        
* assembler/MacroAssemblerCodeRef.h:
(JSC::MacroAssemblerCodePtr::MacroAssemblerCodePtr):
(JSC::MacroAssemblerCodePtr::isEmptyValue):
(JSC::MacroAssemblerCodePtr::isDeletedValue):
(JSC::MacroAssemblerCodePtr::hash):
(JSC::MacroAssemblerCodePtr::emptyValue):
(JSC::MacroAssemblerCodePtr::deletedValue):
(JSC::MacroAssemblerCodePtrHash::hash):
(JSC::MacroAssemblerCodePtrHash::equal):
* assembler/MacroAssemblerX86Common.h:
* assembler/RepatchBuffer.h:
(JSC::RepatchBuffer::RepatchBuffer):
(JSC::RepatchBuffer::codeBlock):
* ftl/FTLAbbreviations.h:
(JSC::FTL::setInstructionCallingConvention):
* ftl/FTLCompile.cpp:
(JSC::FTL::fixFunctionBasedOnStackMaps):
* ftl/FTLInlineCacheSize.cpp:
(JSC::FTL::sizeOfGetById):
(JSC::FTL::sizeOfPutById):
* ftl/FTLJITFinalizer.cpp:
(JSC::FTL::JITFinalizer::finalizeFunction):
* ftl/FTLLocation.cpp:
(JSC::FTL::Location::forStackmaps):
* ftl/FTLLocation.h:
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::compileGetById):
(JSC::FTL::LowerDFGToLLVM::compilePutById):
* ftl/FTLOSRExitCompiler.cpp:
(JSC::FTL::compileStub):
* ftl/FTLSlowPathCall.cpp:
* ftl/FTLSlowPathCallKey.h:
(JSC::FTL::SlowPathCallKey::withCallTarget):
* ftl/FTLStackMaps.cpp:
(JSC::FTL::StackMaps::Location::directGPR):
(JSC::FTL::StackMaps::Location::restoreInto):
* ftl/FTLStackMaps.h:
* ftl/FTLThunks.h:
(JSC::FTL::generateIfNecessary):
(JSC::FTL::keyForThunk):
(JSC::FTL::Thunks::keyForSlowPathCallThunk):
* jit/FPRInfo.h:
(JSC::FPRInfo::toIndex):
* jit/GPRInfo.h:
(JSC::GPRInfo::toIndex):
(JSC::GPRInfo::debugName):
* jit/RegisterSet.cpp:
(JSC::RegisterSet::calleeSaveRegisters):
* jit/RegisterSet.h:
(JSC::RegisterSet::filter):
* jit/Repatch.cpp:
(JSC::readCallTarget):
(JSC::repatchCall):
(JSC::repatchByIdSelfAccess):
(JSC::tryCacheGetByID):
(JSC::tryCachePutByID):
(JSC::tryBuildPutByIdList):
(JSC::resetGetByID):
(JSC::resetPutByID):
* jit/ScratchRegisterAllocator.h:
(JSC::ScratchRegisterAllocator::lock):

Source/WTF: 

Reviewed by Sam Weinig.
        
I needed to add another set operation, namely filter(), which is an in-place set
intersection.

* wtf/BitVector.cpp:
(WTF::BitVector::filterSlow):
* wtf/BitVector.h:
(WTF::BitVector::filter):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@159039 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/ftl/FTLAbbreviations.h b/Source/JavaScriptCore/ftl/FTLAbbreviations.h
index 2af8aca..2dac00a 100644
--- a/Source/JavaScriptCore/ftl/FTLAbbreviations.h
+++ b/Source/JavaScriptCore/ftl/FTLAbbreviations.h
@@ -269,6 +269,7 @@
     LValue args[] = { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 };
     return buildCall(builder, function, args, 8);
 }
+static inline void setInstructionCallingConvention(LValue instruction, LCallConv callingConvention) { llvm->SetInstructionCallConv(instruction, callingConvention); }
 static inline LValue buildExtractValue(LBuilder builder, LValue aggVal, unsigned index) { return llvm->BuildExtractValue(builder, aggVal, index, ""); }
 static inline LValue buildSelect(LBuilder builder, LValue condition, LValue taken, LValue notTaken) { return llvm->BuildSelect(builder, condition, taken, notTaken, ""); }
 static inline LValue buildBr(LBuilder builder, LBasicBlock destination) { return llvm->BuildBr(builder, destination); }
diff --git a/Source/JavaScriptCore/ftl/FTLCompile.cpp b/Source/JavaScriptCore/ftl/FTLCompile.cpp
index 2de7057..1fb9696 100644
--- a/Source/JavaScriptCore/ftl/FTLCompile.cpp
+++ b/Source/JavaScriptCore/ftl/FTLCompile.cpp
@@ -188,14 +188,12 @@
             
             StackMaps::Record& record = iter->value;
             
-            UNUSED_PARAM(record); // FIXME: use AnyRegs.
-
             // FIXME: LLVM should tell us which registers are live.
             RegisterSet usedRegisters = RegisterSet::allRegisters();
             
-            GPRReg callFrameRegister = GPRInfo::argumentGPR0;
-            GPRReg base = GPRInfo::argumentGPR1;
-            GPRReg result = GPRInfo::returnValueGPR;
+            GPRReg result = record.locations[0].directGPR();
+            GPRReg callFrameRegister = record.locations[1].directGPR();
+            GPRReg base = record.locations[2].directGPR();
             
             JITGetByIdGenerator gen(
                 codeBlock, getById.codeOrigin(), usedRegisters, callFrameRegister,
@@ -224,19 +222,17 @@
             
             StackMaps::Record& record = iter->value;
             
-            UNUSED_PARAM(record); // FIXME: use AnyRegs.
-
             // FIXME: LLVM should tell us which registers are live.
             RegisterSet usedRegisters = RegisterSet::allRegisters();
             
-            GPRReg callFrameRegister = GPRInfo::argumentGPR0;
-            GPRReg base = GPRInfo::argumentGPR1;
-            GPRReg value = GPRInfo::argumentGPR2;
+            GPRReg callFrameRegister = record.locations[0].directGPR();
+            GPRReg base = record.locations[1].directGPR();
+            GPRReg value = record.locations[2].directGPR();
             
             JITPutByIdGenerator gen(
                 codeBlock, putById.codeOrigin(), usedRegisters, callFrameRegister,
-                JSValueRegs(base), JSValueRegs(value), GPRInfo::argumentGPR3, false,
-                putById.ecmaMode(), putById.putKind());
+                JSValueRegs(base), JSValueRegs(value), MacroAssembler::scratchRegister,
+                false, putById.ecmaMode(), putById.putKind());
             
             MacroAssembler::Label begin = slowPathJIT.label();
             
diff --git a/Source/JavaScriptCore/ftl/FTLInlineCacheSize.cpp b/Source/JavaScriptCore/ftl/FTLInlineCacheSize.cpp
index 8f2984a..f023da7 100644
--- a/Source/JavaScriptCore/ftl/FTLInlineCacheSize.cpp
+++ b/Source/JavaScriptCore/ftl/FTLInlineCacheSize.cpp
@@ -33,38 +33,18 @@
 
 namespace JSC { namespace FTL {
 
-static size_t s_sizeOfGetById;
-static size_t s_sizeOfPutById;
+// These sizes are x86-64-specific, and were found empirically. They have to cover the worst
+// possible combination of registers leading to the largest possible encoding of each
+// instruction in the IC.
 
 size_t sizeOfGetById()
 {
-    if (s_sizeOfGetById)
-        return s_sizeOfGetById;
-    
-    MacroAssembler jit;
-    
-    JITGetByIdGenerator generator(
-        0, CodeOrigin(), RegisterSet(), GPRInfo::callFrameRegister,
-        JSValueRegs(GPRInfo::regT6), JSValueRegs(GPRInfo::regT7), false);
-    generator.generateFastPath(jit);
-    
-    return s_sizeOfGetById = jit.m_assembler.codeSize();
+    return 29;
 }
 
 size_t sizeOfPutById()
 {
-    if (s_sizeOfPutById)
-        return s_sizeOfPutById;
-    
-    MacroAssembler jit;
-    
-    JITPutByIdGenerator generator(
-        0, CodeOrigin(), RegisterSet(), GPRInfo::callFrameRegister,
-        JSValueRegs(GPRInfo::regT6), JSValueRegs(GPRInfo::regT7), GPRInfo::regT8, false,
-        NotStrictMode, NotDirect);
-    generator.generateFastPath(jit);
-    
-    return s_sizeOfPutById = jit.m_assembler.codeSize();
+    return 32;
 }
 
 } } // namespace JSC::FTL
diff --git a/Source/JavaScriptCore/ftl/FTLJITFinalizer.cpp b/Source/JavaScriptCore/ftl/FTLJITFinalizer.cpp
index 178fd27..a8a99b6 100644
--- a/Source/JavaScriptCore/ftl/FTLJITFinalizer.cpp
+++ b/Source/JavaScriptCore/ftl/FTLJITFinalizer.cpp
@@ -75,7 +75,7 @@
                 CodeLocationLabel(
                     m_plan.vm.ftlThunks->getOSRExitGenerationThunk(
                         m_plan.vm, Location::forStackmaps(
-                            jitCode->stackmaps, iter->value.locations[0])).code()));
+                            &jitCode->stackmaps, iter->value.locations[0])).code()));
         }
         
         jitCode->initializeExitThunks(
diff --git a/Source/JavaScriptCore/ftl/FTLLocation.cpp b/Source/JavaScriptCore/ftl/FTLLocation.cpp
index 0ecdf7b..3f7ab7d 100644
--- a/Source/JavaScriptCore/ftl/FTLLocation.cpp
+++ b/Source/JavaScriptCore/ftl/FTLLocation.cpp
@@ -35,7 +35,7 @@
 
 namespace JSC { namespace FTL {
 
-Location Location::forStackmaps(const StackMaps& stackmaps, const StackMaps::Location& location)
+Location Location::forStackmaps(const StackMaps* stackmaps, const StackMaps::Location& location)
 {
     switch (location.kind) {
     case StackMaps::Location::Unprocessed:
@@ -53,7 +53,8 @@
         return forConstant(location.offset);
         
     case StackMaps::Location::ConstantIndex:
-        return forConstant(stackmaps.constants[location.offset].integer);
+        ASSERT(stackmaps);
+        return forConstant(stackmaps->constants[location.offset].integer);
     }
     
     RELEASE_ASSERT_NOT_REACHED();
diff --git a/Source/JavaScriptCore/ftl/FTLLocation.h b/Source/JavaScriptCore/ftl/FTLLocation.h
index 05ddf5b..90f5dde 100644
--- a/Source/JavaScriptCore/ftl/FTLLocation.h
+++ b/Source/JavaScriptCore/ftl/FTLLocation.h
@@ -84,7 +84,9 @@
         return result;
     }
 
-    static Location forStackmaps(const StackMaps&, const StackMaps::Location&);
+    // You can pass a null StackMaps if you are confident that the location doesn't
+    // involve a wide constant.
+    static Location forStackmaps(const StackMaps*, const StackMaps::Location&);
     
     Kind kind() const { return m_kind; }
     
diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
index d058cff..6222964 100644
--- a/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
+++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
@@ -1275,10 +1275,12 @@
 
         // Arguments: id, bytes, target, numArgs, args...
         unsigned stackmapID = m_stackmapIDs++;
-        setJSValue(m_out.call(
+        LValue call = m_out.call(
             m_out.patchpointInt64Intrinsic(),
             m_out.constInt32(stackmapID), m_out.constInt32(sizeOfGetById()),
-            constNull(m_out.ref8), m_out.constInt32(2), m_callFrame, base));
+            constNull(m_out.ref8), m_out.constInt32(2), m_callFrame, base);
+        setInstructionCallingConvention(call, LLVMAnyRegCallConv);
+        setJSValue(call);
         
         m_ftlState.getByIds.append(GetByIdDescriptor(stackmapID, m_node->codeOrigin, uid));
     }
@@ -1294,10 +1296,11 @@
 
         // Arguments: id, bytes, target, numArgs, args...
         unsigned stackmapID = m_stackmapIDs++;
-        m_out.call(
+        LValue call = m_out.call(
             m_out.patchpointVoidIntrinsic(),
             m_out.constInt32(stackmapID), m_out.constInt32(sizeOfPutById()),
             constNull(m_out.ref8), m_out.constInt32(3), m_callFrame, base, value);
+        setInstructionCallingConvention(call, LLVMAnyRegCallConv);
         
         m_ftlState.putByIds.append(PutByIdDescriptor(
             stackmapID, m_node->codeOrigin, uid,
diff --git a/Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp b/Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp
index f0f3a95..1813993 100644
--- a/Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp
+++ b/Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp
@@ -176,8 +176,8 @@
     exit.m_code = FINALIZE_CODE_IF(
         shouldShowDisassembly(),
         patchBuffer,
-        ("FTL OSR exit #%u (bc#%u, %s) from %s, with operands = %s, and record = %s",
-            exitID, exit.m_codeOrigin.bytecodeIndex,
+        ("FTL OSR exit #%u (%s, %s) from %s, with operands = %s, and record = %s",
+            exitID, toCString(exit.m_codeOrigin).data(),
             exitKindToString(exit.m_kind), toCString(*codeBlock).data(),
             toCString(ignoringContext<DumpContext>(exit.m_values)).data(),
             toCString(*record).data()));
diff --git a/Source/JavaScriptCore/ftl/FTLSlowPathCall.cpp b/Source/JavaScriptCore/ftl/FTLSlowPathCall.cpp
index 7f90443..3cf6ae0 100644
--- a/Source/JavaScriptCore/ftl/FTLSlowPathCall.cpp
+++ b/Source/JavaScriptCore/ftl/FTLSlowPathCall.cpp
@@ -64,20 +64,23 @@
         m_offsetToSavingArea =
             (std::max(m_numArgs, NUMBER_OF_ARGUMENT_REGISTERS) - NUMBER_OF_ARGUMENT_REGISTERS) * wordSize;
         
-        unsigned numArgumentRegistersThatNeedSaving = 0;
-        for (unsigned i = std::min(NUMBER_OF_ARGUMENT_REGISTERS, numArgs); i--;) {
-            if (m_usedRegisters.get(GPRInfo::toArgumentRegister(i)))
-                numArgumentRegistersThatNeedSaving++;
-        }
+        for (unsigned i = std::min(NUMBER_OF_ARGUMENT_REGISTERS, numArgs); i--;)
+            m_callingConventionRegisters.set(GPRInfo::toArgumentRegister(i));
+        if (returnRegister != InvalidGPRReg)
+            m_callingConventionRegisters.set(GPRInfo::returnValueGPR);
+        m_callingConventionRegisters.filter(m_usedRegisters);
+        
+        unsigned numberOfCallingConventionRegisters =
+            m_callingConventionRegisters.numberOfSetRegisters();
         
         size_t offsetToThunkSavingArea =
             m_offsetToSavingArea +
-            numArgumentRegistersThatNeedSaving * wordSize;
+            numberOfCallingConventionRegisters * wordSize;
         
         m_stackBytesNeeded =
             offsetToThunkSavingArea +
             stackBytesNeededForReturnAddress +
-            (m_usedRegisters.numberOfSetRegisters() - numArgumentRegistersThatNeedSaving) * wordSize;
+            (m_usedRegisters.numberOfSetRegisters() - numberOfCallingConventionRegisters) * wordSize;
         
         size_t stackAlignment = 16;
         
@@ -87,11 +90,13 @@
         
         m_thunkSaveSet = m_usedRegisters;
         
-        for (unsigned i = std::min(NUMBER_OF_ARGUMENT_REGISTERS, numArgs); i--;) {
-            if (!m_usedRegisters.get(GPRInfo::toArgumentRegister(i)))
+        // This relies on all calling convention registers also being temp registers.
+        unsigned stackIndex = 0;
+        for (unsigned i = GPRInfo::numberOfRegisters; i--;) {
+            GPRReg reg = GPRInfo::toRegister(i);
+            if (!m_callingConventionRegisters.get(reg))
                 continue;
-            GPRReg reg = GPRInfo::toArgumentRegister(i);
-            m_jit.storePtr(reg, CCallHelpers::Address(CCallHelpers::stackPointerRegister, m_offsetToSavingArea + i * wordSize));
+            m_jit.storePtr(reg, CCallHelpers::Address(CCallHelpers::stackPointerRegister, m_offsetToSavingArea + (stackIndex++) * wordSize));
             m_thunkSaveSet.clear(reg);
         }
         
@@ -103,11 +108,12 @@
         if (m_returnRegister != InvalidGPRReg)
             m_jit.move(GPRInfo::returnValueGPR, m_returnRegister);
         
-        for (unsigned i = std::min(NUMBER_OF_ARGUMENT_REGISTERS, m_numArgs); i--;) {
-            if (!m_usedRegisters.get(GPRInfo::toArgumentRegister(i)))
+        unsigned stackIndex = 0;
+        for (unsigned i = GPRInfo::numberOfRegisters; i--;) {
+            GPRReg reg = GPRInfo::toRegister(i);
+            if (!m_callingConventionRegisters.get(reg))
                 continue;
-            GPRReg reg = GPRInfo::toArgumentRegister(i);
-            m_jit.loadPtr(CCallHelpers::Address(CCallHelpers::stackPointerRegister, m_offsetToSavingArea + i * wordSize), reg);
+            m_jit.loadPtr(CCallHelpers::Address(CCallHelpers::stackPointerRegister, m_offsetToSavingArea + (stackIndex++) * wordSize), reg);
         }
         
         m_jit.addPtr(CCallHelpers::TrustedImm32(m_stackBytesNeeded), CCallHelpers::stackPointerRegister);
@@ -139,6 +145,7 @@
 private:
     State& m_state;
     RegisterSet m_usedRegisters;
+    RegisterSet m_callingConventionRegisters;
     CCallHelpers& m_jit;
     unsigned m_numArgs;
     GPRReg m_returnRegister;
diff --git a/Source/JavaScriptCore/ftl/FTLSlowPathCallKey.h b/Source/JavaScriptCore/ftl/FTLSlowPathCallKey.h
index 4a3569ec..0c7c329 100644
--- a/Source/JavaScriptCore/ftl/FTLSlowPathCallKey.h
+++ b/Source/JavaScriptCore/ftl/FTLSlowPathCallKey.h
@@ -61,6 +61,11 @@
     void* callTarget() const { return m_callTarget; }
     ptrdiff_t offset() const { return m_offset; }
     
+    SlowPathCallKey withCallTarget(void* callTarget)
+    {
+        return SlowPathCallKey(usedRegisters(), callTarget, offset());
+    }
+    
     void dump(PrintStream&) const;
     
     enum EmptyValueTag { EmptyValue };
diff --git a/Source/JavaScriptCore/ftl/FTLStackMaps.cpp b/Source/JavaScriptCore/ftl/FTLStackMaps.cpp
index f688cb1..5ef364c 100644
--- a/Source/JavaScriptCore/ftl/FTLStackMaps.cpp
+++ b/Source/JavaScriptCore/ftl/FTLStackMaps.cpp
@@ -66,15 +66,15 @@
     out.print("(", kind, ", reg", dwarfRegNum, ", ", offset, ")");
 }
 
-GPRReg StackMaps::Location::directGPR(StackMaps& stackmaps) const
+GPRReg StackMaps::Location::directGPR() const
 {
-    return FTL::Location::forStackmaps(stackmaps, *this).directGPR();
+    return FTL::Location::forStackmaps(nullptr, *this).directGPR();
 }
 
 void StackMaps::Location::restoreInto(
     MacroAssembler& jit, StackMaps& stackmaps, char* savedRegisters, GPRReg result) const
 {
-    FTL::Location::forStackmaps(stackmaps, *this).restoreInto(jit, savedRegisters, result);
+    FTL::Location::forStackmaps(&stackmaps, *this).restoreInto(jit, savedRegisters, result);
 }
 
 bool StackMaps::Record::parse(DataView* view, unsigned& offset)
diff --git a/Source/JavaScriptCore/ftl/FTLStackMaps.h b/Source/JavaScriptCore/ftl/FTLStackMaps.h
index 13d1966..0d9e94a 100644
--- a/Source/JavaScriptCore/ftl/FTLStackMaps.h
+++ b/Source/JavaScriptCore/ftl/FTLStackMaps.h
@@ -65,7 +65,7 @@
         void parse(DataView*, unsigned& offset);
         void dump(PrintStream& out) const;
         
-        GPRReg directGPR(StackMaps&) const;
+        GPRReg directGPR() const;
         void restoreInto(MacroAssembler&, StackMaps&, char* savedRegisters, GPRReg result) const;
     };
     
diff --git a/Source/JavaScriptCore/ftl/FTLThunks.h b/Source/JavaScriptCore/ftl/FTLThunks.h
index ffa0245..bbcdbdd 100644
--- a/Source/JavaScriptCore/ftl/FTLThunks.h
+++ b/Source/JavaScriptCore/ftl/FTLThunks.h
@@ -44,19 +44,38 @@
 MacroAssemblerCodeRef osrExitGenerationThunkGenerator(VM&, const Location&);
 MacroAssemblerCodeRef slowPathCallThunkGenerator(VM&, const SlowPathCallKey&);
 
-template<typename MapType, typename KeyType, typename GeneratorType>
+template<typename KeyTypeArgument>
+struct ThunkMap {
+    typedef KeyTypeArgument KeyType;
+    typedef HashMap<KeyType, MacroAssemblerCodeRef> ToThunkMap;
+    typedef HashMap<MacroAssemblerCodePtr, KeyType> FromThunkMap;
+    
+    ToThunkMap m_toThunk;
+    FromThunkMap m_fromThunk;
+};
+
+template<typename MapType, typename GeneratorType>
 MacroAssemblerCodeRef generateIfNecessary(
-    VM& vm, MapType& map, const KeyType& key, GeneratorType generator)
+    VM& vm, MapType& map, const typename MapType::KeyType& key, GeneratorType generator)
 {
-    typename MapType::iterator iter = map.find(key);
-    if (iter != map.end())
+    typename MapType::ToThunkMap::iterator iter = map.m_toThunk.find(key);
+    if (iter != map.m_toThunk.end())
         return iter->value;
     
     MacroAssemblerCodeRef result = generator(vm, key);
-    map.add(key, result);
+    map.m_toThunk.add(key, result);
+    map.m_fromThunk.add(result.code(), key);
     return result;
 }
 
+template<typename MapType>
+typename MapType::KeyType keyForThunk(MapType& map, MacroAssemblerCodePtr ptr)
+{
+    typename MapType::FromThunkMap::iterator iter = map.m_fromThunk.find(ptr);
+    RELEASE_ASSERT(iter != map.m_fromThunk.end());
+    return iter->value;
+}
+
 class Thunks {
 public:
     MacroAssemblerCodeRef getOSRExitGenerationThunk(VM& vm, const Location& location)
@@ -71,9 +90,14 @@
             vm, m_slowPathCallThunks, key, slowPathCallThunkGenerator);
     }
     
+    SlowPathCallKey keyForSlowPathCallThunk(MacroAssemblerCodePtr ptr)
+    {
+        return keyForThunk(m_slowPathCallThunks, ptr);
+    }
+    
 private:
-    HashMap<Location, MacroAssemblerCodeRef> m_osrExitThunks;
-    HashMap<SlowPathCallKey, MacroAssemblerCodeRef> m_slowPathCallThunks;
+    ThunkMap<Location> m_osrExitThunks;
+    ThunkMap<SlowPathCallKey> m_slowPathCallThunks;
 };
 
 } } // namespace JSC::FTL