Title: [194542] trunk/Source/_javascript_Core
Revision
194542
Author
fpi...@apple.com
Date
2016-01-04 11:33:35 -0800 (Mon, 04 Jan 2016)

Log Message

B3 patchpoints should allow requesting scratch registers
https://bugs.webkit.org/show_bug.cgi?id=152669

Reviewed by Benjamin Poulain.

Scratch registers are something that we often need in many patchpoint use cases. In LLVM's
patchpoints, we didn't have a good way to request scratch registers. So, our current FTL code
often does crazy scratch register allocation madness even when it would be better to just ask
the backend for some registers. This patch adds a mechanism for requesting scratch registers
in B3, and wires it all the way to all of our register allocation and liveness
infrastructure.

>From the standpoint of a patchpoint, a "scratch register" is an instruction argument that
only admits Tmp and is defined early (like an early clobber register) and is used late (like
what we previously called LateUse, except that this time it's also a warm use). We already
had the beginning of support for early def's because of early clobbers, and we already
supported late uses albeit cold ones. I really only needed to add one new role: "Scratch",
which means both early def and late use in much the same way as "UseDef" means both early
use and late def. But, it feels better to complete the set of roles, so I added LateColdUse
to differentiate from LateUse (which is now a warm use) and EarlyDef to differentiate from
Def (which is, and always has been, a late def). Forcing the code to deal with the full
matrix of possibilities resulted in what is probably a progression in how we handle defs in
the register and stack allocators. The new Inst::forEachDef(Inst*, Inst*, callback) fully
recognizes that a "def" is something that can come from either the preceding instruction or
the succeeding one.

This doesn't add any new functionality to FTL B3 yet, but the new scratch register mechanism
is covered by new testb3 tests.

* b3/B3CheckSpecial.cpp:
(JSC::B3::CheckSpecial::isValid):
(JSC::B3::CheckSpecial::admitsStack):
(JSC::B3::CheckSpecial::generate):
* b3/B3LowerToAir.cpp:
(JSC::B3::Air::LowerToAir::lower):
* b3/B3PatchpointSpecial.cpp:
(JSC::B3::PatchpointSpecial::forEachArg):
(JSC::B3::PatchpointSpecial::isValid):
(JSC::B3::PatchpointSpecial::admitsStack):
(JSC::B3::PatchpointSpecial::generate):
* b3/B3PatchpointValue.cpp:
(JSC::B3::PatchpointValue::dumpMeta):
(JSC::B3::PatchpointValue::PatchpointValue):
* b3/B3PatchpointValue.h:
* b3/B3StackmapGenerationParams.cpp:
(JSC::B3::StackmapGenerationParams::unavailableRegisters):
* b3/B3StackmapGenerationParams.h:
(JSC::B3::StackmapGenerationParams::gpScratch):
(JSC::B3::StackmapGenerationParams::fpScratch):
* b3/B3StackmapSpecial.cpp:
(JSC::B3::StackmapSpecial::forEachArgImpl):
(JSC::B3::StackmapSpecial::isValidImpl):
(JSC::B3::StackmapSpecial::admitsStackImpl):
(JSC::B3::StackmapSpecial::repsImpl):
(JSC::B3::StackmapSpecial::isArgValidForValue):
(JSC::B3::StackmapSpecial::appendRepsImpl): Deleted.
* b3/B3StackmapSpecial.h:
* b3/air/AirAllocateStack.cpp:
(JSC::B3::Air::allocateStack):
* b3/air/AirArg.cpp:
(WTF::printInternal):
* b3/air/AirArg.h:
(JSC::B3::Air::Arg::isAnyUse):
(JSC::B3::Air::Arg::isColdUse):
(JSC::B3::Air::Arg::isEarlyUse):
(JSC::B3::Air::Arg::isLateUse):
(JSC::B3::Air::Arg::isAnyDef):
(JSC::B3::Air::Arg::isEarlyDef):
(JSC::B3::Air::Arg::isLateDef):
(JSC::B3::Air::Arg::isZDef):
(JSC::B3::Air::Arg::Arg):
(JSC::B3::Air::Arg::imm):
(JSC::B3::Air::Arg::isDef): Deleted.
* b3/air/AirBasicBlock.h:
(JSC::B3::Air::BasicBlock::at):
(JSC::B3::Air::BasicBlock::get):
(JSC::B3::Air::BasicBlock::last):
* b3/air/AirEliminateDeadCode.cpp:
(JSC::B3::Air::eliminateDeadCode):
* b3/air/AirFixPartialRegisterStalls.cpp:
(JSC::B3::Air::fixPartialRegisterStalls):
* b3/air/AirInst.cpp:
(JSC::B3::Air::Inst::hasArgEffects):
* b3/air/AirInst.h:
* b3/air/AirInstInlines.h:
(JSC::B3::Air::Inst::extraEarlyClobberedRegs):
(JSC::B3::Air::Inst::forEachDef):
(JSC::B3::Air::Inst::forEachDefWithExtraClobberedRegs):
(JSC::B3::Air::Inst::reportUsedRegisters):
(JSC::B3::Air::Inst::forEachTmpWithExtraClobberedRegs): Deleted.
* b3/air/AirIteratedRegisterCoalescing.cpp:
* b3/air/AirLiveness.h:
(JSC::B3::Air::AbstractLiveness::AbstractLiveness):
(JSC::B3::Air::AbstractLiveness::LocalCalc::execute):
* b3/air/AirSpillEverything.cpp:
(JSC::B3::Air::spillEverything):
* b3/air/AirTmpWidth.cpp:
(JSC::B3::Air::TmpWidth::recompute):
* b3/air/AirUseCounts.h:
(JSC::B3::Air::UseCounts::UseCounts):
* b3/testb3.cpp:
(JSC::B3::testPatchpointAny):
(JSC::B3::testPatchpointGPScratch):
(JSC::B3::testPatchpointFPScratch):
(JSC::B3::testPatchpointLotsOfLateAnys):
(JSC::B3::run):

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (194541 => 194542)


--- trunk/Source/_javascript_Core/ChangeLog	2016-01-04 19:27:43 UTC (rev 194541)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-01-04 19:33:35 UTC (rev 194542)
@@ -1,3 +1,112 @@
+2016-01-04  Filip Pizlo  <fpi...@apple.com>
+
+        B3 patchpoints should allow requesting scratch registers
+        https://bugs.webkit.org/show_bug.cgi?id=152669
+
+        Reviewed by Benjamin Poulain.
+
+        Scratch registers are something that we often need in many patchpoint use cases. In LLVM's
+        patchpoints, we didn't have a good way to request scratch registers. So, our current FTL code
+        often does crazy scratch register allocation madness even when it would be better to just ask
+        the backend for some registers. This patch adds a mechanism for requesting scratch registers
+        in B3, and wires it all the way to all of our register allocation and liveness
+        infrastructure.
+
+        From the standpoint of a patchpoint, a "scratch register" is an instruction argument that
+        only admits Tmp and is defined early (like an early clobber register) and is used late (like
+        what we previously called LateUse, except that this time it's also a warm use). We already
+        had the beginning of support for early def's because of early clobbers, and we already
+        supported late uses albeit cold ones. I really only needed to add one new role: "Scratch",
+        which means both early def and late use in much the same way as "UseDef" means both early
+        use and late def. But, it feels better to complete the set of roles, so I added LateColdUse
+        to differentiate from LateUse (which is now a warm use) and EarlyDef to differentiate from
+        Def (which is, and always has been, a late def). Forcing the code to deal with the full
+        matrix of possibilities resulted in what is probably a progression in how we handle defs in
+        the register and stack allocators. The new Inst::forEachDef(Inst*, Inst*, callback) fully
+        recognizes that a "def" is something that can come from either the preceding instruction or
+        the succeeding one.
+
+        This doesn't add any new functionality to FTL B3 yet, but the new scratch register mechanism
+        is covered by new testb3 tests.
+
+        * b3/B3CheckSpecial.cpp:
+        (JSC::B3::CheckSpecial::isValid):
+        (JSC::B3::CheckSpecial::admitsStack):
+        (JSC::B3::CheckSpecial::generate):
+        * b3/B3LowerToAir.cpp:
+        (JSC::B3::Air::LowerToAir::lower):
+        * b3/B3PatchpointSpecial.cpp:
+        (JSC::B3::PatchpointSpecial::forEachArg):
+        (JSC::B3::PatchpointSpecial::isValid):
+        (JSC::B3::PatchpointSpecial::admitsStack):
+        (JSC::B3::PatchpointSpecial::generate):
+        * b3/B3PatchpointValue.cpp:
+        (JSC::B3::PatchpointValue::dumpMeta):
+        (JSC::B3::PatchpointValue::PatchpointValue):
+        * b3/B3PatchpointValue.h:
+        * b3/B3StackmapGenerationParams.cpp:
+        (JSC::B3::StackmapGenerationParams::unavailableRegisters):
+        * b3/B3StackmapGenerationParams.h:
+        (JSC::B3::StackmapGenerationParams::gpScratch):
+        (JSC::B3::StackmapGenerationParams::fpScratch):
+        * b3/B3StackmapSpecial.cpp:
+        (JSC::B3::StackmapSpecial::forEachArgImpl):
+        (JSC::B3::StackmapSpecial::isValidImpl):
+        (JSC::B3::StackmapSpecial::admitsStackImpl):
+        (JSC::B3::StackmapSpecial::repsImpl):
+        (JSC::B3::StackmapSpecial::isArgValidForValue):
+        (JSC::B3::StackmapSpecial::appendRepsImpl): Deleted.
+        * b3/B3StackmapSpecial.h:
+        * b3/air/AirAllocateStack.cpp:
+        (JSC::B3::Air::allocateStack):
+        * b3/air/AirArg.cpp:
+        (WTF::printInternal):
+        * b3/air/AirArg.h:
+        (JSC::B3::Air::Arg::isAnyUse):
+        (JSC::B3::Air::Arg::isColdUse):
+        (JSC::B3::Air::Arg::isEarlyUse):
+        (JSC::B3::Air::Arg::isLateUse):
+        (JSC::B3::Air::Arg::isAnyDef):
+        (JSC::B3::Air::Arg::isEarlyDef):
+        (JSC::B3::Air::Arg::isLateDef):
+        (JSC::B3::Air::Arg::isZDef):
+        (JSC::B3::Air::Arg::Arg):
+        (JSC::B3::Air::Arg::imm):
+        (JSC::B3::Air::Arg::isDef): Deleted.
+        * b3/air/AirBasicBlock.h:
+        (JSC::B3::Air::BasicBlock::at):
+        (JSC::B3::Air::BasicBlock::get):
+        (JSC::B3::Air::BasicBlock::last):
+        * b3/air/AirEliminateDeadCode.cpp:
+        (JSC::B3::Air::eliminateDeadCode):
+        * b3/air/AirFixPartialRegisterStalls.cpp:
+        (JSC::B3::Air::fixPartialRegisterStalls):
+        * b3/air/AirInst.cpp:
+        (JSC::B3::Air::Inst::hasArgEffects):
+        * b3/air/AirInst.h:
+        * b3/air/AirInstInlines.h:
+        (JSC::B3::Air::Inst::extraEarlyClobberedRegs):
+        (JSC::B3::Air::Inst::forEachDef):
+        (JSC::B3::Air::Inst::forEachDefWithExtraClobberedRegs):
+        (JSC::B3::Air::Inst::reportUsedRegisters):
+        (JSC::B3::Air::Inst::forEachTmpWithExtraClobberedRegs): Deleted.
+        * b3/air/AirIteratedRegisterCoalescing.cpp:
+        * b3/air/AirLiveness.h:
+        (JSC::B3::Air::AbstractLiveness::AbstractLiveness):
+        (JSC::B3::Air::AbstractLiveness::LocalCalc::execute):
+        * b3/air/AirSpillEverything.cpp:
+        (JSC::B3::Air::spillEverything):
+        * b3/air/AirTmpWidth.cpp:
+        (JSC::B3::Air::TmpWidth::recompute):
+        * b3/air/AirUseCounts.h:
+        (JSC::B3::Air::UseCounts::UseCounts):
+        * b3/testb3.cpp:
+        (JSC::B3::testPatchpointAny):
+        (JSC::B3::testPatchpointGPScratch):
+        (JSC::B3::testPatchpointFPScratch):
+        (JSC::B3::testPatchpointLotsOfLateAnys):
+        (JSC::B3::run):
+
 2016-01-04  Csaba Osztrogonác  <o...@webkit.org>
 
         Fix the !ENABLE(INTL) build after r193493

Modified: trunk/Source/_javascript_Core/b3/B3CheckSpecial.cpp (194541 => 194542)


--- trunk/Source/_javascript_Core/b3/B3CheckSpecial.cpp	2016-01-04 19:27:43 UTC (rev 194541)
+++ trunk/Source/_javascript_Core/b3/B3CheckSpecial.cpp	2016-01-04 19:33:35 UTC (rev 194542)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -124,7 +124,8 @@
 bool CheckSpecial::isValid(Inst& inst)
 {
     return hiddenBranch(inst).isValidForm()
-        && isValidImpl(numB3Args(inst), m_numCheckArgs + 1, inst);
+        && isValidImpl(numB3Args(inst), m_numCheckArgs + 1, inst)
+        && inst.args.size() - m_numCheckArgs - 1 == inst.origin->numChildren() - numB3Args(inst);
 }
 
 bool CheckSpecial::admitsStack(Inst& inst, unsigned argIndex)
@@ -142,8 +143,7 @@
     StackmapValue* value = inst.origin->as<StackmapValue>();
     ASSERT(value);
 
-    Vector<ValueRep> reps;
-    appendRepsImpl(context, m_numCheckArgs + 1, inst, reps);
+    Vector<ValueRep> reps = repsImpl(context, numB3Args(inst), m_numCheckArgs + 1, inst);
 
     // Set aside the args that are relevant to undoing the operation. This is because we don't want to
     // capture all of inst in the closure below.

Modified: trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp (194541 => 194542)


--- trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp	2016-01-04 19:27:43 UTC (rev 194541)
+++ trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp	2016-01-04 19:33:35 UTC (rev 194542)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -2007,6 +2007,11 @@
             }
             
             fillStackmap(inst, patchpointValue, 0);
+
+            for (unsigned i = patchpointValue->numGPScratchRegisters; i--;)
+                inst.args.append(m_code.newTmp(Arg::GP));
+            for (unsigned i = patchpointValue->numFPScratchRegisters; i--;)
+                inst.args.append(m_code.newTmp(Arg::FP));
             
             m_insts.last().append(WTFMove(inst));
             m_insts.last().appendVector(after);

Modified: trunk/Source/_javascript_Core/b3/B3PatchpointSpecial.cpp (194541 => 194542)


--- trunk/Source/_javascript_Core/b3/B3PatchpointSpecial.cpp	2016-01-04 19:27:43 UTC (rev 194541)
+++ trunk/Source/_javascript_Core/b3/B3PatchpointSpecial.cpp	2016-01-04 19:33:35 UTC (rev 194542)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -46,32 +46,56 @@
 
 void PatchpointSpecial::forEachArg(Inst& inst, const ScopedLambda<Inst::EachArgCallback>& callback)
 {
-    // FIXME: Allow B3 Patchpoints to specify LateUse.
-    // https://bugs.webkit.org/show_bug.cgi?id=151335
-    
-    if (inst.origin->type() == Void) {
-        forEachArgImpl(0, 1, inst, SameAsRep, callback);
-        return;
-    }
+    unsigned argIndex = 1;
 
-    callback(inst.args[1], Arg::Def, inst.origin->airType(), inst.origin->airWidth());
-    forEachArgImpl(0, 2, inst, SameAsRep, callback);
+    if (inst.origin->type() != Void)
+        callback(inst.args[argIndex++], Arg::Def, inst.origin->airType(), inst.origin->airWidth());
+
+    forEachArgImpl(0, argIndex, inst, SameAsRep, callback);
+    argIndex += inst.origin->numChildren();
+
+    for (unsigned i = inst.origin->as<PatchpointValue>()->numGPScratchRegisters; i--;)
+        callback(inst.args[argIndex++], Arg::Scratch, Arg::GP, Arg::conservativeWidth(Arg::GP));
+    for (unsigned i = inst.origin->as<PatchpointValue>()->numFPScratchRegisters; i--;)
+        callback(inst.args[argIndex++], Arg::Scratch, Arg::FP, Arg::conservativeWidth(Arg::FP));
 }
 
 bool PatchpointSpecial::isValid(Inst& inst)
 {
-    if (inst.origin->type() == Void)
-        return isValidImpl(0, 1, inst);
+    PatchpointValue* patchpoint = inst.origin->as<PatchpointValue>();
+    unsigned argIndex = 1;
 
-    if (inst.args.size() < 2)
+    if (inst.origin->type() != Void) {
+        if (argIndex >= inst.args.size())
+            return false;
+        
+        if (!isArgValidForValue(inst.args[argIndex], patchpoint))
+            return false;
+        if (!isArgValidForRep(code(), inst.args[argIndex], patchpoint->resultConstraint))
+            return false;
+        argIndex++;
+    }
+
+    if (!isValidImpl(0, argIndex, inst))
         return false;
-    PatchpointValue* patchpoint = inst.origin->as<PatchpointValue>();
-    if (!isArgValidForValue(inst.args[1], patchpoint))
+    argIndex += patchpoint->numChildren();
+
+    if (argIndex + patchpoint->numGPScratchRegisters + patchpoint->numFPScratchRegisters
+        != inst.args.size())
         return false;
-    if (!isArgValidForRep(code(), inst.args[1], patchpoint->resultConstraint))
-        return false;
 
-    return isValidImpl(0, 2, inst);
+    for (unsigned i = patchpoint->numGPScratchRegisters; i--;) {
+        Arg arg = inst.args[argIndex++];
+        if (!arg.isGPTmp())
+            return false;
+    }
+    for (unsigned i = patchpoint->numFPScratchRegisters; i--;) {
+        Arg arg = inst.args[argIndex++];
+        if (!arg.isFPTmp())
+            return false;
+    }
+
+    return true;
 }
 
 bool PatchpointSpecial::admitsStack(Inst& inst, unsigned argIndex)
@@ -99,16 +123,24 @@
 CCallHelpers::Jump PatchpointSpecial::generate(
     Inst& inst, CCallHelpers& jit, GenerationContext& context)
 {
-    StackmapValue* value = inst.origin->as<StackmapValue>();
+    PatchpointValue* value = inst.origin->as<PatchpointValue>();
     ASSERT(value);
 
     Vector<ValueRep> reps;
     unsigned offset = 1;
     if (inst.origin->type() != Void)
         reps.append(repForArg(*context.code, inst.args[offset++]));
-    appendRepsImpl(context, offset, inst, reps);
+    reps.appendVector(repsImpl(context, 0, offset, inst));
+    offset += value->numChildren();
+
+    StackmapGenerationParams params(value, reps, context);
+
+    for (unsigned i = value->numGPScratchRegisters; i--;)
+        params.m_gpScratch.append(inst.args[offset++].gpr());
+    for (unsigned i = value->numFPScratchRegisters; i--;)
+        params.m_fpScratch.append(inst.args[offset++].fpr());
     
-    value->m_generator->run(jit, StackmapGenerationParams(value, reps, context));
+    value->m_generator->run(jit, params);
 
     return CCallHelpers::Jump();
 }

Modified: trunk/Source/_javascript_Core/b3/B3PatchpointValue.cpp (194541 => 194542)


--- trunk/Source/_javascript_Core/b3/B3PatchpointValue.cpp	2016-01-04 19:27:43 UTC (rev 194541)
+++ trunk/Source/_javascript_Core/b3/B3PatchpointValue.cpp	2016-01-04 19:33:35 UTC (rev 194542)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -38,6 +38,10 @@
 {
     Base::dumpMeta(comma, out);
     out.print(comma, "resultConstraint = ", resultConstraint);
+    if (numGPScratchRegisters)
+        out.print(comma, "numGPScratchRegisters = ", numGPScratchRegisters);
+    if (numFPScratchRegisters)
+        out.print(comma, "numFPScratchRegisters = ", numFPScratchRegisters);
 }
 
 PatchpointValue::PatchpointValue(unsigned index, Type type, Origin origin)

Modified: trunk/Source/_javascript_Core/b3/B3PatchpointValue.h (194541 => 194542)


--- trunk/Source/_javascript_Core/b3/B3PatchpointValue.h	2016-01-04 19:27:43 UTC (rev 194541)
+++ trunk/Source/_javascript_Core/b3/B3PatchpointValue.h	2016-01-04 19:33:35 UTC (rev 194542)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -56,6 +56,12 @@
     // is Void. Otherwise you can set this to any input constraint.
     ValueRep resultConstraint;
 
+    // The number of scratch registers that this patchpoint gets. The scratch register is guaranteed
+    // to be different from any input register and the destination register. It's also guaranteed not
+    // to be clobbered either early or late. These are 0 by default.
+    uint8_t numGPScratchRegisters { 0 };
+    uint8_t numFPScratchRegisters { 0 };
+
 protected:
     void dumpMeta(CommaPrinter&, PrintStream&) const override;
 

Modified: trunk/Source/_javascript_Core/b3/B3StackmapGenerationParams.cpp (194541 => 194542)


--- trunk/Source/_javascript_Core/b3/B3StackmapGenerationParams.cpp	2016-01-04 19:27:43 UTC (rev 194541)
+++ trunk/Source/_javascript_Core/b3/B3StackmapGenerationParams.cpp	2016-01-04 19:33:35 UTC (rev 194542)
@@ -50,6 +50,12 @@
         unsavedCalleeSaves.clear(regAtOffset.reg());
 
     result.merge(unsavedCalleeSaves);
+
+    for (GPRReg gpr : m_gpScratch)
+        result.clear(gpr);
+    for (FPRReg fpr : m_fpScratch)
+        result.clear(fpr);
+    
     return result;
 }
 

Modified: trunk/Source/_javascript_Core/b3/B3StackmapGenerationParams.h (194541 => 194542)


--- trunk/Source/_javascript_Core/b3/B3StackmapGenerationParams.h	2016-01-04 19:27:43 UTC (rev 194541)
+++ trunk/Source/_javascript_Core/b3/B3StackmapGenerationParams.h	2016-01-04 19:33:35 UTC (rev 194542)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -59,13 +59,28 @@
     // This tells you the registers that were used.
     const RegisterSet& usedRegisters() const;
 
-    // This is a useful helper if you want to do register allocation inside of a patchpoint. You
-    // can only use callee-save registers if they were saved in the prologue. This gives you the
-    // used register set that's useful for such settings by returning:
+    // This is a useful helper if you want to do register allocation inside of a patchpoint. The
+    // usedRegisters() set is not directly useful for this purpose because:
     //
-    //     usedRegisters() | (RegisterSet::calleeSaveRegisters() - proc.calleeSaveRegisters())
-    RegisterSet unavailableRegisters() const;
+    // - You can only use callee-save registers for scratch if they were saved in the prologue. So,
+    //   if a register is callee-save, it's not enough that it's not in usedRegisters().
+    //
+    // - Scratch registers are going to be in usedRegisters() at the patchpoint. So, if you want to
+    //   find one of your requested scratch registers using usedRegisters(), you'll have a bad time.
+    //
+    // This gives you the used register set that's useful for allocating scratch registers. This set
+    // is defined as:
+    //
+    //     (usedRegisters() | (RegisterSet::calleeSaveRegisters() - proc.calleeSaveRegisters()))
+    //     - gpScratchRegisters - fpScratchRegisters
+    //
+    // I.e. it is like usedRegisters() but also includes unsaved callee-saves and excludes scratch
+    // registers.
+    JS_EXPORT_PRIVATE RegisterSet unavailableRegisters() const;
 
+    GPRReg gpScratch(unsigned index) const { return m_gpScratch[index]; }
+    FPRReg fpScratch(unsigned index) const { return m_fpScratch[index]; }
+
     // This is provided for convenience; it means that you don't have to capture it if you don't want to.
     Procedure& proc() const;
     
@@ -90,6 +105,8 @@
 
     StackmapValue* m_value;
     Vector<ValueRep> m_reps;
+    Vector<GPRReg> m_gpScratch;
+    Vector<FPRReg> m_fpScratch;
     Air::GenerationContext& m_context;
 };
 

Modified: trunk/Source/_javascript_Core/b3/B3StackmapSpecial.cpp (194541 => 194542)


--- trunk/Source/_javascript_Core/b3/B3StackmapSpecial.cpp	2016-01-04 19:27:43 UTC (rev 194541)
+++ trunk/Source/_javascript_Core/b3/B3StackmapSpecial.cpp	2016-01-04 19:33:35 UTC (rev 194542)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -81,9 +81,9 @@
     // Check that insane things have not happened.
     ASSERT(inst.args.size() >= numIgnoredAirArgs);
     ASSERT(value->children().size() >= numIgnoredB3Args);
-    ASSERT(inst.args.size() - numIgnoredAirArgs == value->children().size() - numIgnoredB3Args);
+    ASSERT(inst.args.size() - numIgnoredAirArgs >= value->children().size() - numIgnoredB3Args);
     
-    for (unsigned i = 0; i < inst.args.size() - numIgnoredAirArgs; ++i) {
+    for (unsigned i = 0; i < value->children().size() - numIgnoredB3Args; ++i) {
         Arg& arg = inst.args[i + numIgnoredAirArgs];
         ConstrainedValue child = value->constrainedChild(i + numIgnoredB3Args);
 
@@ -103,12 +103,12 @@
                 role = Arg::ColdUse;
                 break;
             case ValueRep::LateColdAny:
-                role = Arg::LateUse;
+                role = Arg::LateColdUse;
                 break;
             }
             break;
         case ForceLateUse:
-            role = Arg::LateUse;
+            role = Arg::LateColdUse;
             break;
         }
 
@@ -129,13 +129,13 @@
     ASSERT(value->children().size() >= numIgnoredB3Args);
 
     // For the Inst to be valid, it needs to have the right number of arguments.
-    if (inst.args.size() - numIgnoredAirArgs != value->children().size() - numIgnoredB3Args)
+    if (inst.args.size() - numIgnoredAirArgs < value->children().size() - numIgnoredB3Args)
         return false;
 
     // Regardless of constraints, stackmaps have some basic requirements for their arguments. For
     // example, you can't have a non-FP-offset address. This verifies those conditions as well as the
     // argument types.
-    for (unsigned i = 0; i < inst.args.size() - numIgnoredAirArgs; ++i) {
+    for (unsigned i = 0; i < value->children().size() - numIgnoredB3Args; ++i) {
         Value* child = value->child(i + numIgnoredB3Args);
         Arg& arg = inst.args[i + numIgnoredAirArgs];
 
@@ -167,6 +167,11 @@
 
     unsigned stackmapArgIndex = argIndex - numIgnoredAirArgs + numIgnoredB3Args;
 
+    if (stackmapArgIndex >= value->numChildren()) {
+        // It's not a stackmap argument, so as far as we are concerned, it doesn't admit stack.
+        return false;
+    }
+
     if (stackmapArgIndex >= value->m_reps.size()) {
         // This means that there was no constraint.
         return true;
@@ -180,11 +185,13 @@
     return false;
 }
 
-void StackmapSpecial::appendRepsImpl(
-    GenerationContext& context, unsigned numIgnoredArgs, Inst& inst, Vector<ValueRep>& result)
+Vector<ValueRep> StackmapSpecial::repsImpl(
+    GenerationContext& context, unsigned numIgnoredB3Args, unsigned numIgnoredAirArgs, Inst& inst)
 {
-    for (unsigned i = numIgnoredArgs; i < inst.args.size(); ++i)
-        result.append(repForArg(*context.code, inst.args[i]));
+    Vector<ValueRep> result;
+    for (unsigned i = 0; i < inst.origin->numChildren() - numIgnoredB3Args; ++i)
+        result.append(repForArg(*context.code, inst.args[i + numIgnoredAirArgs]));
+    return result;
 }
 
 bool StackmapSpecial::isArgValidForValue(const Air::Arg& arg, Value* value)

Modified: trunk/Source/_javascript_Core/b3/B3StackmapSpecial.h (194541 => 194542)


--- trunk/Source/_javascript_Core/b3/B3StackmapSpecial.h	2016-01-04 19:27:43 UTC (rev 194541)
+++ trunk/Source/_javascript_Core/b3/B3StackmapSpecial.h	2016-01-04 19:33:35 UTC (rev 194542)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -69,7 +69,8 @@
         Air::Inst&, unsigned argIndex);
 
     // Appends the reps for the Inst's args, starting with numIgnoredArgs, to the given vector.
-    void appendRepsImpl(Air::GenerationContext&, unsigned numIgnoredArgs, Air::Inst&, Vector<ValueRep>&);
+    Vector<ValueRep> repsImpl(
+        Air::GenerationContext&, unsigned numIgnoredB3Args, unsigned numIgnoredAirArgs, Air::Inst&);
 
     static bool isArgValidForValue(const Air::Arg&, Value*);
     static bool isArgValidForRep(Air::Code&, const Air::Arg&, const ValueRep&);

Modified: trunk/Source/_javascript_Core/b3/air/AirAllocateStack.cpp (194541 => 194542)


--- trunk/Source/_javascript_Core/b3/air/AirAllocateStack.cpp	2016-01-04 19:27:43 UTC (rev 194541)
+++ trunk/Source/_javascript_Core/b3/air/AirAllocateStack.cpp	2016-01-04 19:33:35 UTC (rev 194542)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -143,14 +143,13 @@
     for (BasicBlock* block : code) {
         StackSlotLiveness::LocalCalc localCalc(liveness, block);
 
-        auto interfere = [&] (Inst& inst) {
+        auto interfere = [&] (unsigned instIndex) {
             if (verbose)
                 dataLog("Interfering: ", WTF::pointerListDump(localCalc.live()), "\n");
 
-            inst.forEachArg(
-                [&] (Arg& arg, Arg::Role role, Arg::Type, Arg::Width) {
-                    if (!Arg::isDef(role))
-                        return;
+            Inst::forEachDef<Arg>(
+                block->get(instIndex), block->get(instIndex + 1),
+                [&] (Arg& arg, Arg::Role, Arg::Type, Arg::Width) {
                     if (!arg.isStack())
                         return;
                     StackSlot* slot = arg.stackSlot();
@@ -167,12 +166,10 @@
         for (unsigned instIndex = block->size(); instIndex--;) {
             if (verbose)
                 dataLog("Analyzing: ", block->at(instIndex), "\n");
-            Inst& inst = block->at(instIndex);
-            interfere(inst);
+            interfere(instIndex);
             localCalc.execute(instIndex);
         }
-        Inst nop;
-        interfere(nop);
+        interfere(-1);
     }
 
     if (verbose) {

Modified: trunk/Source/_javascript_Core/b3/air/AirArg.cpp (194541 => 194542)


--- trunk/Source/_javascript_Core/b3/air/AirArg.cpp	2016-01-04 19:27:43 UTC (rev 194541)
+++ trunk/Source/_javascript_Core/b3/air/AirArg.cpp	2016-01-04 19:33:35 UTC (rev 194542)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -193,6 +193,15 @@
     case Arg::LateUse:
         out.print("LateUse");
         return;
+    case Arg::LateColdUse:
+        out.print("LateColdUse");
+        return;
+    case Arg::EarlyDef:
+        out.print("EarlyDef");
+        return;
+    case Arg::Scratch:
+        out.print("Scratch");
+        return;
     }
 
     RELEASE_ASSERT_NOT_REACHED();

Modified: trunk/Source/_javascript_Core/b3/air/AirArg.h (194541 => 194542)


--- trunk/Source/_javascript_Core/b3/air/AirArg.h	2016-01-04 19:27:43 UTC (rev 194541)
+++ trunk/Source/_javascript_Core/b3/air/AirArg.h	2016-01-04 19:33:35 UTC (rev 194542)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -92,10 +92,14 @@
         ColdUse,
 
         // LateUse means that the Inst will read from this value after doing its Def's. Note that LateUse
-        // on an Addr or Index still means Use on the internal temporaries. LateUse also currently also
-        // implies ColdUse.
+        // on an Addr or Index still means Use on the internal temporaries. Note that specifying the
+        // same Tmp once as Def and once as LateUse has undefined behavior: the use may happen before
+        // the def, or it may happen after it.
         LateUse,
 
+        // Combination of LateUse and ColdUse.
+        LateColdUse,
+
         // Def means that the Inst will write to this value after doing everything else.
         //
         // For Tmp: The Inst will write to this Tmp.
@@ -127,6 +131,16 @@
         // This is a combined Use and ZDef. It means that both things happen.
         UseZDef,
 
+        // This is like Def, but implies that the assignment occurs before the start of the Inst's
+        // execution rather than after. Note that specifying the same Tmp once as EarlyDef and once
+        // as Use has undefined behavior: the use may happen before the def, or it may happen after
+        // it.
+        EarlyDef,
+
+        // Some instructions need a scratch register. We model this by saying that the temporary is
+        // defined early and used late. This role implies that.
+        Scratch,
+
         // This is a special kind of use that is only valid for addresses. It means that the
         // instruction will evaluate the address _expression_ and consume the effective address, but it
         // will neither load nor store. This is an escaping use, because now the address may be
@@ -171,10 +185,13 @@
         case UseDef:
         case UseZDef:
         case LateUse:
+        case LateColdUse:
+        case Scratch:
             return true;
         case Def:
         case ZDef:
         case UseAddr:
+        case EarlyDef:
             return false;
         }
     }
@@ -183,14 +200,17 @@
     {
         switch (role) {
         case ColdUse:
-        case LateUse:
+        case LateColdUse:
             return true;
         case Use:
         case UseDef:
         case UseZDef:
+        case LateUse:
         case Def:
         case ZDef:
         case UseAddr:
+        case Scratch:
+        case EarlyDef:
             return false;
         }
     }
@@ -213,6 +233,9 @@
         case ZDef:
         case UseAddr:
         case LateUse:
+        case LateColdUse:
+        case Scratch:
+        case EarlyDef:
             return false;
         }
     }
@@ -220,26 +243,83 @@
     // Returns true if the Role implies that the Inst will Use the Arg after doing everything else.
     static bool isLateUse(Role role)
     {
-        return role == LateUse;
+        switch (role) {
+        case LateUse:
+        case LateColdUse:
+        case Scratch:
+            return true;
+        case ColdUse:
+        case Use:
+        case UseDef:
+        case UseZDef:
+        case Def:
+        case ZDef:
+        case UseAddr:
+        case EarlyDef:
+            return false;
+        }
     }
 
     // Returns true if the Role implies that the Inst will Def the Arg.
-    static bool isDef(Role role)
+    static bool isAnyDef(Role role)
     {
         switch (role) {
         case Use:
         case ColdUse:
         case UseAddr:
         case LateUse:
+        case LateColdUse:
             return false;
         case Def:
         case UseDef:
         case ZDef:
         case UseZDef:
+        case EarlyDef:
+        case Scratch:
             return true;
         }
     }
 
+    // Returns true if the Role implies that the Inst will Def the Arg before start of execution.
+    static bool isEarlyDef(Role role)
+    {
+        switch (role) {
+        case Use:
+        case ColdUse:
+        case UseAddr:
+        case LateUse:
+        case Def:
+        case UseDef:
+        case ZDef:
+        case UseZDef:
+        case LateColdUse:
+            return false;
+        case EarlyDef:
+        case Scratch:
+            return true;
+        }
+    }
+
+    // Returns true if the Role implies that the Inst will Def the Arg after the end of execution.
+    static bool isLateDef(Role role)
+    {
+        switch (role) {
+        case Use:
+        case ColdUse:
+        case UseAddr:
+        case LateUse:
+        case EarlyDef:
+        case Scratch:
+        case LateColdUse:
+            return false;
+        case Def:
+        case UseDef:
+        case ZDef:
+        case UseZDef:
+            return true;
+        }
+    }
+
     // Returns true if the Role implies that the Inst will ZDef the Arg.
     static bool isZDef(Role role)
     {
@@ -250,6 +330,9 @@
         case LateUse:
         case Def:
         case UseDef:
+        case EarlyDef:
+        case Scratch:
+        case LateColdUse:
             return false;
         case ZDef:
         case UseZDef:
@@ -331,6 +414,11 @@
     {
     }
 
+    Arg(Reg reg)
+        : Arg(Air::Tmp(reg))
+    {
+    }
+
     static Arg imm(int64_t value)
     {
         Arg result;
@@ -845,7 +933,7 @@
         case CallArg:
             return isValidAddrForm(offset());
         case Index:
-            return isValidIndexForm(offset(), scale(), width);
+            return isValidIndexForm(scale(), offset(), width);
         case RelCond:
         case ResCond:
         case DoubleCond:
@@ -882,7 +970,7 @@
     {
         switch (m_kind) {
         case Tmp:
-            ASSERT(isAnyUse(argRole) || isDef(argRole));
+            ASSERT(isAnyUse(argRole) || isAnyDef(argRole));
             functor(m_base, argRole, argType, argWidth);
             break;
         case Addr:

Modified: trunk/Source/_javascript_Core/b3/air/AirBasicBlock.h (194541 => 194542)


--- trunk/Source/_javascript_Core/b3/air/AirBasicBlock.h	2016-01-04 19:27:43 UTC (rev 194541)
+++ trunk/Source/_javascript_Core/b3/air/AirBasicBlock.h	2016-01-04 19:33:35 UTC (rev 194542)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -64,6 +64,11 @@
     const Inst& at(unsigned index) const { return m_insts[index]; }
     Inst& at(unsigned index) { return m_insts[index]; }
 
+    Inst* get(unsigned index)
+    {
+        return index < size() ? &at(index) : nullptr;
+    }
+
     const Inst& last() const { return m_insts.last(); }
     Inst& last() { return m_insts.last(); }
 

Modified: trunk/Source/_javascript_Core/b3/air/AirEliminateDeadCode.cpp (194541 => 194542)


--- trunk/Source/_javascript_Core/b3/air/AirEliminateDeadCode.cpp	2016-01-04 19:27:43 UTC (rev 194541)
+++ trunk/Source/_javascript_Core/b3/air/AirEliminateDeadCode.cpp	2016-01-04 19:33:35 UTC (rev 194542)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -81,8 +81,10 @@
         bool storesToLive = false;
         inst.forEachArg(
             [&] (Arg& arg, Arg::Role role, Arg::Type, Arg::Width) {
-                if (!Arg::isDef(role))
+                if (!Arg::isAnyDef(role))
                     return;
+                if (role == Arg::Scratch)
+                    return;
                 storesToLive |= isArgLive(arg);
             });
         return storesToLive;

Modified: trunk/Source/_javascript_Core/b3/air/AirFixPartialRegisterStalls.cpp (194541 => 194542)


--- trunk/Source/_javascript_Core/b3/air/AirFixPartialRegisterStalls.cpp	2016-01-04 19:27:43 UTC (rev 194541)
+++ trunk/Source/_javascript_Core/b3/air/AirFixPartialRegisterStalls.cpp	2016-01-04 19:33:35 UTC (rev 194542)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -116,7 +116,7 @@
     inst.forEachTmp([&] (Tmp& tmp, Arg::Role role, Arg::Type, Arg::Width) {
         ASSERT_WITH_MESSAGE(tmp.isReg(), "This phase must be run after register allocation.");
 
-        if (tmp.isFPR() && Arg::isDef(role))
+        if (tmp.isFPR() && Arg::isAnyDef(role))
             localDistance.add(tmp.fpr(), distanceToBlockEnd);
     });
 }
@@ -205,7 +205,7 @@
                 RegisterSet uses;
                 inst.forEachTmp([&] (Tmp& tmp, Arg::Role role, Arg::Type, Arg::Width) {
                     if (tmp.isFPR()) {
-                        if (Arg::isDef(role))
+                        if (Arg::isAnyDef(role))
                             defs.set(tmp.fpr());
                         if (Arg::isAnyUse(role))
                             uses.set(tmp.fpr());

Modified: trunk/Source/_javascript_Core/b3/air/AirInst.cpp (194541 => 194542)


--- trunk/Source/_javascript_Core/b3/air/AirInst.cpp	2016-01-04 19:27:43 UTC (rev 194541)
+++ trunk/Source/_javascript_Core/b3/air/AirInst.cpp	2016-01-04 19:33:35 UTC (rev 194542)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -39,7 +39,7 @@
     bool result = false;
     forEachArg(
         [&] (Arg&, Arg::Role role, Arg::Type, Arg::Width) {
-            if (Arg::isDef(role))
+            if (Arg::isAnyDef(role))
                 result = true;
         });
     return result;

Modified: trunk/Source/_javascript_Core/b3/air/AirInst.h (194541 => 194542)


--- trunk/Source/_javascript_Core/b3/air/AirInst.h	2016-01-04 19:27:43 UTC (rev 194541)
+++ trunk/Source/_javascript_Core/b3/air/AirInst.h	2016-01-04 19:33:35 UTC (rev 194542)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -115,7 +115,7 @@
             });
     }
 
-    // Thing can be either Arg or Tmp.
+    // Thing can be either Arg, Tmp, or StackSlot*.
     template<typename Thing, typename Functor>
     void forEach(const Functor&);
 
@@ -127,11 +127,19 @@
     const RegisterSet& extraClobberedRegs();
     const RegisterSet& extraEarlyClobberedRegs();
 
-    // Iterate over the Defs and the extra clobbered registers. You must supply the next instruction if
-    // there is one.
-    template<typename Functor>
-    void forEachTmpWithExtraClobberedRegs(Inst* nextInst, const Functor& functor);
+    // Iterate over all Def's that happen at the end of an instruction. You supply a pair
+    // instructions. The instructions must appear next to each other, in that order, in some basic
+    // block. You can pass null for the first instruction when analyzing what happens at the top of
+    // a basic block. You can pass null for the second instruction when analyzing what happens at the
+    // bottom of a basic block.
+    template<typename Thing, typename Functor>
+    static void forEachDef(Inst* prevInst, Inst* nextInst, const Functor&);
 
+    // Iterate over all Def's that happen at the end of this instruction, including extra clobbered
+    // registers. Note that Thing can only be Arg or Tmp when you use this functor.
+    template<typename Thing, typename Functor>
+    static void forEachDefWithExtraClobberedRegs(Inst* prevInst, Inst* nextInst, const Functor&);
+
     // Use this to report which registers are live. This should be done just before codegen. Note
     // that for efficiency, reportUsedRegisters() only works if hasSpecial() returns true.
     void reportUsedRegisters(const RegisterSet&);

Modified: trunk/Source/_javascript_Core/b3/air/AirInstInlines.h (194541 => 194542)


--- trunk/Source/_javascript_Core/b3/air/AirInstInlines.h	2016-01-04 19:27:43 UTC (rev 194541)
+++ trunk/Source/_javascript_Core/b3/air/AirInstInlines.h	2016-01-04 19:33:35 UTC (rev 194542)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -95,24 +95,48 @@
     return args[0].special()->extraEarlyClobberedRegs(*this);
 }
 
-template<typename Functor>
-inline void Inst::forEachTmpWithExtraClobberedRegs(Inst* nextInst, const Functor& functor)
+template<typename Thing, typename Functor>
+inline void Inst::forEachDef(Inst* prevInst, Inst* nextInst, const Functor& functor)
 {
-    forEachTmp(
-        [&] (Tmp& tmpArg, Arg::Role role, Arg::Type argType, Arg::Width argWidth) {
-            functor(tmpArg, role, argType, argWidth);
-        });
+    if (prevInst) {
+        prevInst->forEach<Thing>(
+            [&] (Thing& thing, Arg::Role role, Arg::Type argType, Arg::Width argWidth) {
+                if (Arg::isLateDef(role))
+                    functor(thing, role, argType, argWidth);
+            });
+    }
 
+    if (nextInst) {
+        nextInst->forEach<Thing>(
+            [&] (Thing& thing, Arg::Role role, Arg::Type argType, Arg::Width argWidth) {
+                if (Arg::isEarlyDef(role))
+                    functor(thing, role, argType, argWidth);
+            });
+    }
+}
+
+template<typename Thing, typename Functor>
+inline void Inst::forEachDefWithExtraClobberedRegs(
+    Inst* prevInst, Inst* nextInst, const Functor& functor)
+{
+    forEachDef<Thing>(prevInst, nextInst, functor);
+
+    Arg::Role regDefRole;
+    
     auto reportReg = [&] (Reg reg) {
         Arg::Type type = reg.isGPR() ? Arg::GP : Arg::FP;
-        functor(Tmp(reg), Arg::Def, type, Arg::conservativeWidth(type));
+        functor(Thing(reg), regDefRole, type, Arg::conservativeWidth(type));
     };
 
-    if (hasSpecial())
-        extraClobberedRegs().forEach(reportReg);
+    if (prevInst && prevInst->hasSpecial()) {
+        regDefRole = Arg::Def;
+        prevInst->extraClobberedRegs().forEach(reportReg);
+    }
 
-    if (nextInst && nextInst->hasSpecial())
+    if (nextInst && nextInst->hasSpecial()) {
+        regDefRole = Arg::EarlyDef;
         nextInst->extraEarlyClobberedRegs().forEach(reportReg);
+    }
 }
 
 inline void Inst::reportUsedRegisters(const RegisterSet& usedRegisters)

Modified: trunk/Source/_javascript_Core/b3/air/AirIteratedRegisterCoalescing.cpp (194541 => 194542)


--- trunk/Source/_javascript_Core/b3/air/AirIteratedRegisterCoalescing.cpp	2016-01-04 19:27:43 UTC (rev 194541)
+++ trunk/Source/_javascript_Core/b3/air/AirIteratedRegisterCoalescing.cpp	2016-01-04 19:33:35 UTC (rev 194542)
@@ -770,40 +770,43 @@
             typename TmpLiveness<type>::LocalCalc localCalc(liveness, block);
             for (unsigned instIndex = block->size(); instIndex--;) {
                 Inst& inst = block->at(instIndex);
-                Inst* nextInst = instIndex + 1 < block->size() ? &block->at(instIndex + 1) : nullptr;
-                build(inst, nextInst, localCalc);
+                Inst* nextInst = block->get(instIndex + 1);
+                build(&inst, nextInst, localCalc);
                 localCalc.execute(instIndex);
             }
+            build(nullptr, &block->at(0), localCalc);
         }
     }
 
-    void build(Inst& inst, Inst* nextInst, const typename TmpLiveness<type>::LocalCalc& localCalc)
+    void build(Inst* prevInst, Inst* nextInst, const typename TmpLiveness<type>::LocalCalc& localCalc)
     {
-        inst.forEachTmpWithExtraClobberedRegs(
-            nextInst,
-            [&] (const Tmp& arg, Arg::Role role, Arg::Type argType, Arg::Width) {
-                if (!Arg::isDef(role) || argType != type)
+        Inst::forEachDefWithExtraClobberedRegs<Tmp>(
+            prevInst, nextInst,
+            [&] (const Tmp& arg, Arg::Role, Arg::Type argType, Arg::Width) {
+                if (argType != type)
                     return;
                 
                 // All the Def()s interfere with each other and with all the extra clobbered Tmps.
-                // We should not use forEachDefAndExtraClobberedTmp() here since colored Tmps
+                // We should not use forEachDefWithExtraClobberedRegs() here since colored Tmps
                 // do not need interference edges in our implementation.
-                inst.forEachTmp([&] (Tmp& otherArg, Arg::Role role, Arg::Type argType, Arg::Width) {
-                    if (!Arg::isDef(role) || argType != type)
-                        return;
-
-                    addEdge(arg, otherArg);
-                });
+                Inst::forEachDef<Tmp>(
+                    prevInst, nextInst,
+                    [&] (Tmp& otherArg, Arg::Role, Arg::Type argType, Arg::Width) {
+                        if (argType != type)
+                            return;
+                        
+                        addEdge(arg, otherArg);
+                    });
             });
 
-        if (mayBeCoalescable(inst)) {
+        if (prevInst && mayBeCoalescable(*prevInst)) {
             // We do not want the Use() of this move to interfere with the Def(), even if it is live
             // after the Move. If we were to add the interference edge, it would be impossible to
             // coalesce the Move even if the two Tmp never interfere anywhere.
             Tmp defTmp;
             Tmp useTmp;
-            inst.forEachTmp([&defTmp, &useTmp] (Tmp& argTmp, Arg::Role role, Arg::Type, Arg::Width) {
-                if (Arg::isDef(role))
+            prevInst->forEachTmp([&defTmp, &useTmp] (Tmp& argTmp, Arg::Role role, Arg::Type, Arg::Width) {
+                if (Arg::isLateDef(role))
                     defTmp = argTmp;
                 else {
                     ASSERT(Arg::isEarlyUse(role));
@@ -822,7 +825,7 @@
             ASSERT(nextMoveIndex <= m_activeMoves.size());
             m_activeMoves.ensureSize(nextMoveIndex + 1);
 
-            for (const Arg& arg : inst.args) {
+            for (const Arg& arg : prevInst->args) {
                 auto& list = m_moveList[AbsoluteTmpMapper<type>::absoluteIndex(arg.tmp())];
                 list.add(nextMoveIndex);
             }
@@ -832,26 +835,20 @@
                     addEdge(defTmp, liveTmp);
             }
 
-            // The next instruction could have early clobbers. We need to consider those now.
-            if (nextInst && nextInst->hasSpecial()) {
-                nextInst->extraEarlyClobberedRegs().forEach([&] (Reg reg) {
-                    if (reg.isGPR() == (type == Arg::GP)) {
-                        for (const Tmp& liveTmp : localCalc.live())
-                            addEdge(Tmp(reg), liveTmp);
-                    }
-                });
-            }
+            // The next instruction could have early clobbers or early def's. We need to consider
+            // those now.
+            addEdges(nullptr, nextInst, localCalc.live());
         } else
-            addEdges(inst, nextInst, localCalc.live());
+            addEdges(prevInst, nextInst, localCalc.live());
     }
 
-    void addEdges(Inst& inst, Inst* nextInst, typename TmpLiveness<type>::LocalCalc::Iterable liveTmps)
+    void addEdges(Inst* prevInst, Inst* nextInst, typename TmpLiveness<type>::LocalCalc::Iterable liveTmps)
     {
         // All the Def()s interfere with everthing live.
-        inst.forEachTmpWithExtraClobberedRegs(
-            nextInst,
-            [&] (const Tmp& arg, Arg::Role role, Arg::Type argType, Arg::Width) {
-                if (!Arg::isDef(role) || argType != type)
+        Inst::forEachDefWithExtraClobberedRegs<Tmp>(
+            prevInst, nextInst,
+            [&] (const Tmp& arg, Arg::Role, Arg::Type argType, Arg::Width) {
+                if (argType != type)
                     return;
                 
                 for (const Tmp& liveTmp : liveTmps) {
@@ -1082,6 +1079,12 @@
     void iteratedRegisterCoalescingOnType()
     {
         HashSet<unsigned> unspillableTmps;
+
+        // FIXME: If a Tmp is used only from a Scratch role and that argument is !admitsStack, then
+        // we should add the Tmp to unspillableTmps. That will help avoid relooping only to turn the
+        // Tmp into an unspillable Tmp.
+        // https://bugs.webkit.org/show_bug.cgi?id=152699
+        
         while (true) {
             ++m_numIterations;
 
@@ -1242,15 +1245,12 @@
                         break;
                     }
 
-                    if (Arg::isAnyUse(role)) {
-                        Tmp newTmp = m_code.newTmp(type);
-                        insertionSet.insert(instIndex, move, inst.origin, arg, newTmp);
-                        tmp = newTmp;
+                    tmp = m_code.newTmp(type);
+                    unspillableTmps.add(AbsoluteTmpMapper<type>::absoluteIndex(tmp));
 
-                        // Any new Fill() should never be spilled.
-                        unspillableTmps.add(AbsoluteTmpMapper<type>::absoluteIndex(tmp));
-                    }
-                    if (Arg::isDef(role))
+                    if (Arg::isAnyUse(role) && role != Arg::Scratch)
+                        insertionSet.insert(instIndex, move, inst.origin, arg, tmp);
+                    if (Arg::isAnyDef(role))
                         insertionSet.insert(instIndex + 1, move, inst.origin, tmp, arg);
                 });
             }

Modified: trunk/Source/_javascript_Core/b3/air/AirLiveness.h (194541 => 194542)


--- trunk/Source/_javascript_Core/b3/air/AirLiveness.h	2016-01-04 19:27:43 UTC (rev 194541)
+++ trunk/Source/_javascript_Core/b3/air/AirLiveness.h	2016-01-04 19:33:35 UTC (rev 194542)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -118,6 +118,13 @@
                 for (size_t instIndex = block->size(); instIndex--;)
                     localCalc.execute(instIndex);
 
+                // Handle the early def's of the first instruction.
+                block->at(0).forEach<typename Adapter::Thing>(
+                    [&] (typename Adapter::Thing& thing, Arg::Role role, Arg::Type type, Arg::Width) {
+                        if (Arg::isEarlyDef(role) && Adapter::acceptsType(type))
+                            m_workset.remove(Adapter::valueToIndex(thing));
+                    });
+
                 Vector<unsigned>& liveAtHead = m_liveAtHead[block];
 
                 // We only care about Tmps that were discovered in this iteration. It is impossible
@@ -213,10 +220,21 @@
         {
             Inst& inst = m_block->at(instIndex);
             auto& workset = m_liveness.m_workset;
-            // First handle def's.
+
+            // First handle the early def's of the next instruction.
+            if (instIndex + 1 < m_block->size()) {
+                Inst& nextInst = m_block->at(instIndex + 1);
+                nextInst.forEach<typename Adapter::Thing>(
+                    [&] (typename Adapter::Thing& thing, Arg::Role role, Arg::Type type, Arg::Width) {
+                        if (Arg::isEarlyDef(role) && Adapter::acceptsType(type))
+                            workset.remove(Adapter::valueToIndex(thing));
+                    });
+            }
+            
+            // Then handle def's.
             inst.forEach<typename Adapter::Thing>(
                 [&] (typename Adapter::Thing& thing, Arg::Role role, Arg::Type type, Arg::Width) {
-                    if (Arg::isDef(role) && Adapter::acceptsType(type))
+                    if (Arg::isLateDef(role) && Adapter::acceptsType(type))
                         workset.remove(Adapter::valueToIndex(thing));
                 });
 

Modified: trunk/Source/_javascript_Core/b3/air/AirSpillEverything.cpp (194541 => 194542)


--- trunk/Source/_javascript_Core/b3/air/AirSpillEverything.cpp	2016-01-04 19:27:43 UTC (rev 194541)
+++ trunk/Source/_javascript_Core/b3/air/AirSpillEverything.cpp	2016-01-04 19:33:35 UTC (rev 194542)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -53,7 +53,7 @@
 
         usedRegisters[block].resize(block->size() + 1);
 
-        auto setUsedRegisters = [&] (unsigned index, Inst& inst) {
+        auto setUsedRegisters = [&] (unsigned index) {
             RegisterSet& registerSet = usedRegisters[block][index];
             for (Tmp tmp : gpLocalCalc.live()) {
                 if (tmp.isReg())
@@ -66,23 +66,20 @@
 
             // Gotta account for dead assignments to registers. These may happen because the input
             // code is suboptimal.
-            inst.forEachTmpWithExtraClobberedRegs(
-                index < block->size() ? &block->at(index) : nullptr,
-                [&registerSet] (const Tmp& tmp, Arg::Role role, Arg::Type, Arg::Width) {
-                    if (tmp.isReg() && Arg::isDef(role))
+            Inst::forEachDefWithExtraClobberedRegs<Tmp>(
+                block->get(index - 1), block->get(index),
+                [&] (const Tmp& tmp, Arg::Role, Arg::Type, Arg::Width) {
+                    if (tmp.isReg())
                         registerSet.set(tmp.reg());
                 });
         };
 
         for (unsigned instIndex = block->size(); instIndex--;) {
-            Inst& inst = block->at(instIndex);
-            setUsedRegisters(instIndex + 1, inst);
+            setUsedRegisters(instIndex + 1);
             gpLocalCalc.execute(instIndex);
             fpLocalCalc.execute(instIndex);
         }
-
-        Inst nop;
-        setUsedRegisters(0, nop);
+        setUsedRegisters(0);
     }
 
     // Allocate a stack slot for each tmp.
@@ -133,6 +130,7 @@
                     switch (role) {
                     case Arg::Use:
                     case Arg::ColdUse:
+                    case Arg::EarlyDef:
                         for (Reg reg : regsInPriorityOrder(type)) {
                             if (!setBefore.get(reg)) {
                                 setBefore.set(reg);
@@ -154,6 +152,8 @@
                     case Arg::UseDef:
                     case Arg::UseZDef:
                     case Arg::LateUse:
+                    case Arg::LateColdUse:
+                    case Arg::Scratch:
                         for (Reg reg : regsInPriorityOrder(type)) {
                             if (!setBefore.get(reg) && !setAfter.get(reg)) {
                                 setAfter.set(reg);
@@ -174,14 +174,10 @@
 
                     Opcode move = type == Arg::GP ? Move : MoveDouble;
 
-                    if (Arg::isAnyUse(role)) {
-                        insertionSet.insert(
-                            instIndex, move, inst.origin, arg, tmp);
-                    }
-                    if (Arg::isDef(role)) {
-                        insertionSet.insert(
-                            instIndex + 1, move, inst.origin, tmp, arg);
-                    }
+                    if (Arg::isAnyUse(role) && role != Arg::Scratch)
+                        insertionSet.insert(instIndex, move, inst.origin, arg, tmp);
+                    if (Arg::isAnyDef(role))
+                        insertionSet.insert(instIndex + 1, move, inst.origin, tmp, arg);
                 });
         }
         insertionSet.execute(block);

Modified: trunk/Source/_javascript_Core/b3/air/AirTmpWidth.cpp (194541 => 194542)


--- trunk/Source/_javascript_Core/b3/air/AirTmpWidth.cpp	2016-01-04 19:27:43 UTC (rev 194541)
+++ trunk/Source/_javascript_Core/b3/air/AirTmpWidth.cpp	2016-01-04 19:33:35 UTC (rev 194542)
@@ -119,7 +119,7 @@
 
                     if (Arg::isZDef(role))
                         widths.def = std::max(widths.def, width);
-                    else if (Arg::isDef(role))
+                    else if (Arg::isAnyDef(role))
                         widths.def = Arg::conservativeWidth(type);
                 });
         }

Modified: trunk/Source/_javascript_Core/b3/air/AirUseCounts.h (194541 => 194542)


--- trunk/Source/_javascript_Core/b3/air/AirUseCounts.h	2016-01-04 19:27:43 UTC (rev 194541)
+++ trunk/Source/_javascript_Core/b3/air/AirUseCounts.h	2016-01-04 19:33:35 UTC (rev 194542)
@@ -84,7 +84,7 @@
                             counts.numWarmUses += frequency;
                         if (Arg::isColdUse(role))
                             counts.numColdUses += frequency;
-                        if (Arg::isDef(role))
+                        if (Arg::isAnyDef(role))
                             counts.numDefs += frequency;
                     });
             }

Modified: trunk/Source/_javascript_Core/b3/testb3.cpp (194541 => 194542)


--- trunk/Source/_javascript_Core/b3/testb3.cpp	2016-01-04 19:27:43 UTC (rev 194541)
+++ trunk/Source/_javascript_Core/b3/testb3.cpp	2016-01-04 19:33:35 UTC (rev 194542)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -6157,6 +6157,72 @@
     CHECK(compileAndRun<int>(proc, 1, 2) == 3);
 }
 
+void testPatchpointGPScratch()
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+    Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
+    PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Int32, Origin());
+    patchpoint->append(arg1, ValueRep::SomeRegister);
+    patchpoint->append(arg2, ValueRep::SomeRegister);
+    patchpoint->numGPScratchRegisters = 2;
+    patchpoint->setGenerator(
+        [&] (CCallHelpers& jit, const StackmapGenerationParams& params) {
+            AllowMacroScratchRegisterUsage allowScratch(jit);
+            // We shouldn't have spilled the inputs, so we assert that they're in registers.
+            CHECK(params.size() == 3);
+            CHECK(params[0].isGPR());
+            CHECK(params[1].isGPR());
+            CHECK(params[2].isGPR());
+            CHECK(params.gpScratch(0) != InvalidGPRReg);
+            CHECK(params.gpScratch(0) != params[0].gpr());
+            CHECK(params.gpScratch(0) != params[1].gpr());
+            CHECK(params.gpScratch(0) != params[2].gpr());
+            CHECK(params.gpScratch(1) != InvalidGPRReg);
+            CHECK(params.gpScratch(1) != params.gpScratch(0));
+            CHECK(params.gpScratch(1) != params[0].gpr());
+            CHECK(params.gpScratch(1) != params[1].gpr());
+            CHECK(params.gpScratch(1) != params[2].gpr());
+            CHECK(!params.unavailableRegisters().get(params.gpScratch(0)));
+            CHECK(!params.unavailableRegisters().get(params.gpScratch(1)));
+            add32(jit, params[1].gpr(), params[2].gpr(), params[0].gpr());
+        });
+    root->appendNew<ControlValue>(proc, Return, Origin(), patchpoint);
+
+    CHECK(compileAndRun<int>(proc, 1, 2) == 3);
+}
+
+void testPatchpointFPScratch()
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+    Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
+    PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Int32, Origin());
+    patchpoint->append(arg1, ValueRep::SomeRegister);
+    patchpoint->append(arg2, ValueRep::SomeRegister);
+    patchpoint->numFPScratchRegisters = 2;
+    patchpoint->setGenerator(
+        [&] (CCallHelpers& jit, const StackmapGenerationParams& params) {
+            AllowMacroScratchRegisterUsage allowScratch(jit);
+            // We shouldn't have spilled the inputs, so we assert that they're in registers.
+            CHECK(params.size() == 3);
+            CHECK(params[0].isGPR());
+            CHECK(params[1].isGPR());
+            CHECK(params[2].isGPR());
+            CHECK(params.fpScratch(0) != InvalidFPRReg);
+            CHECK(params.fpScratch(1) != InvalidFPRReg);
+            CHECK(params.fpScratch(1) != params.fpScratch(0));
+            CHECK(!params.unavailableRegisters().get(params.fpScratch(0)));
+            CHECK(!params.unavailableRegisters().get(params.fpScratch(1)));
+            add32(jit, params[1].gpr(), params[2].gpr(), params[0].gpr());
+        });
+    root->appendNew<ControlValue>(proc, Return, Origin(), patchpoint);
+
+    CHECK(compileAndRun<int>(proc, 1, 2) == 3);
+}
+
 void testPatchpointLotsOfLateAnys()
 {
     Procedure proc;
@@ -9598,6 +9664,8 @@
     RUN(testPatchpointFixedRegister());
     RUN(testPatchpointAny(ValueRep::WarmAny));
     RUN(testPatchpointAny(ValueRep::ColdAny));
+    RUN(testPatchpointGPScratch());
+    RUN(testPatchpointFPScratch());
     RUN(testPatchpointLotsOfLateAnys());
     RUN(testPatchpointAnyImm(ValueRep::WarmAny));
     RUN(testPatchpointAnyImm(ValueRep::ColdAny));

Modified: trunk/Source/_javascript_Core/dfg/DFGCommon.h (194541 => 194542)


--- trunk/Source/_javascript_Core/dfg/DFGCommon.h	2016-01-04 19:27:43 UTC (rev 194541)
+++ trunk/Source/_javascript_Core/dfg/DFGCommon.h	2016-01-04 19:33:35 UTC (rev 194542)
@@ -38,7 +38,7 @@
 // We are in the middle of an experimental transition from LLVM to B3 as the backend for the FTL. We don't
 // yet know how it will turn out. For now, this flag will control whether FTL uses B3. Remember to set this
 // to 0 before committing!
-#define FTL_USES_B3 0
+#define FTL_USES_B3 1
 
 struct Node;
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to