FTL should generate a unique OSR exit for each duplicated OSR exit stackmap intrinsic.
https://bugs.webkit.org/show_bug.cgi?id=149970
Reviewed by Filip Pizlo.
When we lower DFG to LLVM, we generate a stackmap intrnsic for OSR
exits. We also recorded the OSR exit inside FTL::JITCode during lowering.
This stackmap intrinsic may be duplicated or even removed by LLVM.
When the stackmap intrinsic is duplicated, we used to generate just
a single OSR exit data structure. Then, when we compiled an OSR exit, we
would look for the first record in the record list that had the same stackmap ID
as what the OSR exit data structure had. We did this even when the OSR exit
stackmap intrinsic was duplicated. This would lead us to grab the wrong FTL::StackMaps::Record.
Now, each OSR exit knows exactly which FTL::StackMaps::Record it corresponds to.
We accomplish this by having an OSRExitDescriptor that is recorded during
lowering. Each descriptor may be referenced my zero, one, or more OSRExits.
Now, no more than one stackmap intrinsic corresponds to the same index inside
JITCode's OSRExit Vector. Also, each OSRExit jump now jumps to a code location.
* ftl/FTLCompile.cpp:
(JSC::FTL::mmAllocateDataSection):
* ftl/FTLJITCode.cpp:
(JSC::FTL::JITCode::validateReferences):
(JSC::FTL::JITCode::liveRegistersToPreserveAtExceptionHandlingCallSite):
* ftl/FTLJITCode.h:
* ftl/FTLJITFinalizer.cpp:
(JSC::FTL::JITFinalizer::finalizeFunction):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::DFG::LowerDFGToLLVM::compileInvalidationPoint):
(JSC::FTL::DFG::LowerDFGToLLVM::compileIsUndefined):
(JSC::FTL::DFG::LowerDFGToLLVM::appendOSRExit):
(JSC::FTL::DFG::LowerDFGToLLVM::emitOSRExitCall):
(JSC::FTL::DFG::LowerDFGToLLVM::buildExitArguments):
(JSC::FTL::DFG::LowerDFGToLLVM::callStackmap):
* ftl/FTLOSRExit.cpp:
(JSC::FTL::OSRExitDescriptor::OSRExitDescriptor):
(JSC::FTL::OSRExitDescriptor::validateReferences):
(JSC::FTL::OSRExit::OSRExit):
(JSC::FTL::OSRExit::codeLocationForRepatch):
(JSC::FTL::OSRExit::validateReferences): Deleted.
* ftl/FTLOSRExit.h:
(JSC::FTL::OSRExit::considerAddingAsFrequentExitSite):
* ftl/FTLOSRExitCompilationInfo.h:
(JSC::FTL::OSRExitCompilationInfo::OSRExitCompilationInfo):
* ftl/FTLOSRExitCompiler.cpp:
(JSC::FTL::compileStub):
(JSC::FTL::compileFTLOSRExit):
* ftl/FTLStackMaps.cpp:
(JSC::FTL::StackMaps::computeRecordMap):
* ftl/FTLStackMaps.h:
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@191313 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp b/Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp
index bac30c6..2d0bb86 100644
--- a/Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp
+++ b/Source/JavaScriptCore/ftl/FTLOSRExitCompiler.cpp
@@ -176,15 +176,8 @@
static void compileStub(
unsigned exitID, JITCode* jitCode, OSRExit& exit, VM* vm, CodeBlock* codeBlock)
{
- StackMaps::Record* record = nullptr;
-
- for (unsigned i = jitCode->stackmaps.records.size(); i--;) {
- record = &jitCode->stackmaps.records[i];
- if (record->patchpointID == exit.m_stackmapID)
- break;
- }
-
- RELEASE_ASSERT(record->patchpointID == exit.m_stackmapID);
+ StackMaps::Record* record = &jitCode->stackmaps.records[exit.m_stackmapRecordIndex];
+ RELEASE_ASSERT(record->patchpointID == exit.m_descriptor.m_stackmapID);
// This code requires framePointerRegister is the same as callFrameRegister
static_assert(MacroAssembler::framePointerRegister == GPRInfo::callFrameRegister, "MacroAssembler::framePointerRegister and GPRInfo::callFrameRegister must be the same");
@@ -198,7 +191,7 @@
// Figure out how much space we need for those object allocations.
unsigned numMaterializations = 0;
size_t maxMaterializationNumArguments = 0;
- for (ExitTimeObjectMaterialization* materialization : exit.m_materializations) {
+ for (ExitTimeObjectMaterialization* materialization : exit.m_descriptor.m_materializations) {
numMaterializations++;
maxMaterializationNumArguments = std::max(
@@ -208,18 +201,18 @@
ScratchBuffer* scratchBuffer = vm->scratchBufferForSize(
sizeof(EncodedJSValue) * (
- exit.m_values.size() + numMaterializations + maxMaterializationNumArguments) +
+ exit.m_descriptor.m_values.size() + numMaterializations + maxMaterializationNumArguments) +
requiredScratchMemorySizeInBytes() +
codeBlock->calleeSaveRegisters()->size() * sizeof(uint64_t));
EncodedJSValue* scratch = scratchBuffer ? static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer()) : 0;
- EncodedJSValue* materializationPointers = scratch + exit.m_values.size();
+ EncodedJSValue* materializationPointers = scratch + exit.m_descriptor.m_values.size();
EncodedJSValue* materializationArguments = materializationPointers + numMaterializations;
char* registerScratch = bitwise_cast<char*>(materializationArguments + maxMaterializationNumArguments);
uint64_t* unwindScratch = bitwise_cast<uint64_t*>(registerScratch + requiredScratchMemorySizeInBytes());
HashMap<ExitTimeObjectMaterialization*, EncodedJSValue*> materializationToPointer;
unsigned materializationCount = 0;
- for (ExitTimeObjectMaterialization* materialization : exit.m_materializations) {
+ for (ExitTimeObjectMaterialization* materialization : exit.m_descriptor.m_materializations) {
materializationToPointer.add(
materialization, materializationPointers + materializationCount++);
}
@@ -253,10 +246,10 @@
jit.move(MacroAssembler::TrustedImm64(TagMask), GPRInfo::tagMaskRegister);
// Do some value profiling.
- if (exit.m_profileDataFormat != DataFormatNone) {
+ if (exit.m_descriptor.m_profileDataFormat != DataFormatNone) {
record->locations[0].restoreInto(jit, jitCode->stackmaps, registerScratch, GPRInfo::regT0);
reboxAccordingToFormat(
- exit.m_profileDataFormat, jit, GPRInfo::regT0, GPRInfo::regT1, GPRInfo::regT2);
+ exit.m_descriptor.m_profileDataFormat, jit, GPRInfo::regT0, GPRInfo::regT1, GPRInfo::regT2);
if (exit.m_kind == BadCache || exit.m_kind == BadIndexingType) {
CodeOrigin codeOrigin = exit.m_codeOriginForExitProfile;
@@ -270,8 +263,8 @@
}
}
- if (!!exit.m_valueProfile)
- jit.store64(GPRInfo::regT0, exit.m_valueProfile.getSpecFailBucket(0));
+ if (!!exit.m_descriptor.m_valueProfile)
+ jit.store64(GPRInfo::regT0, exit.m_descriptor.m_valueProfile.getSpecFailBucket(0));
}
// Materialize all objects. Don't materialize an object until all
@@ -281,7 +274,7 @@
// allocation of the former.
HashSet<ExitTimeObjectMaterialization*> toMaterialize;
- for (ExitTimeObjectMaterialization* materialization : exit.m_materializations)
+ for (ExitTimeObjectMaterialization* materialization : exit.m_descriptor.m_materializations)
toMaterialize.add(materialization);
while (!toMaterialize.isEmpty()) {
@@ -344,7 +337,7 @@
// Now that all the objects have been allocated, we populate them
// with the correct values. This time we can recover all the
// fields, including those that are only needed for the allocation.
- for (ExitTimeObjectMaterialization* materialization : exit.m_materializations) {
+ for (ExitTimeObjectMaterialization* materialization : exit.m_descriptor.m_materializations) {
for (unsigned propertyIndex = materialization->properties().size(); propertyIndex--;) {
const ExitValue& value = materialization->properties()[propertyIndex].value();
compileRecovery(
@@ -365,16 +358,16 @@
// Save all state from wherever the exit data tells us it was, into the appropriate place in
// the scratch buffer. This also does the reboxing.
- for (unsigned index = exit.m_values.size(); index--;) {
+ for (unsigned index = exit.m_descriptor.m_values.size(); index--;) {
compileRecovery(
- jit, exit.m_values[index], record, jitCode->stackmaps, registerScratch,
+ jit, exit.m_descriptor.m_values[index], record, jitCode->stackmaps, registerScratch,
materializationToPointer);
jit.store64(GPRInfo::regT0, scratch + index);
}
// Henceforth we make it look like the exiting function was called through a register
// preservation wrapper. This implies that FP must be nudged down by a certain amount. Then
- // we restore the various things according to either exit.m_values or by copying from the
+ // we restore the various things according to either exit.m_descriptor.m_values or by copying from the
// old frame, and finally we save the various callee-save registers into where the
// restoration thunk would restore them from.
@@ -422,7 +415,7 @@
// First set up SP so that our data doesn't get clobbered by signals.
unsigned conservativeStackDelta =
- (exit.m_values.numberOfLocals() + baselineCodeBlock->calleeSaveSpaceAsVirtualRegisters()) * sizeof(Register) +
+ (exit.m_descriptor.m_values.numberOfLocals() + baselineCodeBlock->calleeSaveSpaceAsVirtualRegisters()) * sizeof(Register) +
maxFrameExtentForSlowPathCall;
conservativeStackDelta = WTF::roundUpToMultipleOf(
stackAlignmentBytes(), conservativeStackDelta);
@@ -476,8 +469,8 @@
// Now get state out of the scratch buffer and place it back into the stack. The values are
// already reboxed so we just move them.
- for (unsigned index = exit.m_values.size(); index--;) {
- VirtualRegister reg = exit.m_values.virtualRegisterForIndex(index);
+ for (unsigned index = exit.m_descriptor.m_values.size(); index--;) {
+ VirtualRegister reg = exit.m_descriptor.m_values.virtualRegisterForIndex(index);
if (reg.isLocal() && reg.toLocal() < static_cast<int>(baselineVirtualRegistersForCalleeSaves))
continue;
@@ -497,7 +490,7 @@
("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(ignoringContext<DumpContext>(exit.m_descriptor.m_values)).data(),
toCString(*record).data()));
}
@@ -527,10 +520,10 @@
dataLog(" Origin: ", exit.m_codeOrigin, "\n");
if (exit.m_codeOriginForExitProfile != exit.m_codeOrigin)
dataLog(" Origin for exit profile: ", exit.m_codeOriginForExitProfile, "\n");
- dataLog(" Exit values: ", exit.m_values, "\n");
- if (!exit.m_materializations.isEmpty()) {
+ dataLog(" Exit values: ", exit.m_descriptor.m_values, "\n");
+ if (!exit.m_descriptor.m_materializations.isEmpty()) {
dataLog(" Materializations:\n");
- for (ExitTimeObjectMaterialization* materialization : exit.m_materializations)
+ for (ExitTimeObjectMaterialization* materialization : exit.m_descriptor.m_materializations)
dataLog(" ", pointerDump(materialization), "\n");
}
}