Title: [89961] trunk/Source/_javascript_Core
Revision
89961
Author
[email protected]
Date
2011-06-28 14:33:38 -0700 (Tue, 28 Jun 2011)

Log Message

https://bugs.webkit.org/show_bug.cgi?id=63563
DFG JIT - add support for double arith to speculative path

Reviewed by Oliver Hunt.

Add integer support for div & mod, add double support for div, mod,
add, sub & mul, dynamically selecting based on operand types.

* dfg/DFGJITCodeGenerator.cpp:
(JSC::DFG::FPRTemporary::FPRTemporary):
* dfg/DFGJITCodeGenerator.h:
* dfg/DFGJITCompiler.h:
(JSC::DFG::JITCompiler::assembler):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::fillSpeculateDouble):
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculateDoubleOperand::SpeculateDoubleOperand):
(JSC::DFG::SpeculateDoubleOperand::~SpeculateDoubleOperand):
(JSC::DFG::SpeculateDoubleOperand::index):
(JSC::DFG::SpeculateDoubleOperand::fpr):

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (89960 => 89961)


--- trunk/Source/_javascript_Core/ChangeLog	2011-06-28 21:28:31 UTC (rev 89960)
+++ trunk/Source/_javascript_Core/ChangeLog	2011-06-28 21:33:38 UTC (rev 89961)
@@ -1,3 +1,27 @@
+2011-06-28  Gavin Barraclough  <[email protected]>
+
+        Reviewed by Oliver Hunt.
+
+        https://bugs.webkit.org/show_bug.cgi?id=63563
+        DFG JIT - add support for double arith to speculative path
+
+        Add integer support for div & mod, add double support for div, mod,
+        add, sub & mul, dynamically selecting based on operand types.
+
+        * dfg/DFGJITCodeGenerator.cpp:
+        (JSC::DFG::FPRTemporary::FPRTemporary):
+        * dfg/DFGJITCodeGenerator.h:
+        * dfg/DFGJITCompiler.h:
+        (JSC::DFG::JITCompiler::assembler):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::fillSpeculateDouble):
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT.h:
+        (JSC::DFG::SpeculateDoubleOperand::SpeculateDoubleOperand):
+        (JSC::DFG::SpeculateDoubleOperand::~SpeculateDoubleOperand):
+        (JSC::DFG::SpeculateDoubleOperand::index):
+        (JSC::DFG::SpeculateDoubleOperand::fpr):
+
 2011-06-28  Oliver Hunt  <[email protected]>
 
         Fix interpreter build.

Modified: trunk/Source/_javascript_Core/dfg/DFGJITCodeGenerator.cpp (89960 => 89961)


--- trunk/Source/_javascript_Core/dfg/DFGJITCodeGenerator.cpp	2011-06-28 21:28:31 UTC (rev 89960)
+++ trunk/Source/_javascript_Core/dfg/DFGJITCodeGenerator.cpp	2011-06-28 21:33:38 UTC (rev 89961)
@@ -636,6 +636,28 @@
         m_fpr = m_jit->fprAllocate();
 }
 
+FPRTemporary::FPRTemporary(JITCodeGenerator* jit, SpeculateDoubleOperand& op1)
+    : m_jit(jit)
+    , m_fpr(InvalidFPRReg)
+{
+    if (m_jit->canReuse(op1.index()))
+        m_fpr = m_jit->reuse(op1.fpr());
+    else
+        m_fpr = m_jit->fprAllocate();
+}
+
+FPRTemporary::FPRTemporary(JITCodeGenerator* jit, SpeculateDoubleOperand& op1, SpeculateDoubleOperand& op2)
+    : m_jit(jit)
+    , m_fpr(InvalidFPRReg)
+{
+    if (m_jit->canReuse(op1.index()))
+        m_fpr = m_jit->reuse(op1.fpr());
+    else if (m_jit->canReuse(op2.index()))
+        m_fpr = m_jit->reuse(op2.fpr());
+    else
+        m_fpr = m_jit->fprAllocate();
+}
+
 } } // namespace JSC::DFG
 
 #endif

Modified: trunk/Source/_javascript_Core/dfg/DFGJITCodeGenerator.h (89960 => 89961)


--- trunk/Source/_javascript_Core/dfg/DFGJITCodeGenerator.h	2011-06-28 21:28:31 UTC (rev 89960)
+++ trunk/Source/_javascript_Core/dfg/DFGJITCodeGenerator.h	2011-06-28 21:33:38 UTC (rev 89961)
@@ -39,6 +39,7 @@
 
 class SpeculateIntegerOperand;
 class SpeculateStrictInt32Operand;
+class SpeculateDoubleOperand;
 class SpeculateCellOperand;
 
 
@@ -1035,6 +1036,8 @@
     FPRTemporary(JITCodeGenerator*);
     FPRTemporary(JITCodeGenerator*, DoubleOperand&);
     FPRTemporary(JITCodeGenerator*, DoubleOperand&, DoubleOperand&);
+    FPRTemporary(JITCodeGenerator*, SpeculateDoubleOperand&);
+    FPRTemporary(JITCodeGenerator*, SpeculateDoubleOperand&, SpeculateDoubleOperand&);
 
     ~FPRTemporary()
     {

Modified: trunk/Source/_javascript_Core/dfg/DFGJITCompiler.h (89960 => 89961)


--- trunk/Source/_javascript_Core/dfg/DFGJITCompiler.h	2011-06-28 21:28:31 UTC (rev 89960)
+++ trunk/Source/_javascript_Core/dfg/DFGJITCompiler.h	2011-06-28 21:33:38 UTC (rev 89961)
@@ -106,6 +106,7 @@
     Graph& graph() { return m_graph; }
     CodeBlock* codeBlock() { return m_codeBlock; }
     JSGlobalData* globalData() { return m_globalData; }
+    AssemblerType_T& assembler() { return m_assembler; }
 
 #if CPU(X86_64)
     void preserveReturnAddressAfterCall(GPRReg reg)

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (89960 => 89961)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2011-06-28 21:28:31 UTC (rev 89960)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2011-06-28 21:33:38 UTC (rev 89961)
@@ -174,6 +174,121 @@
     return result;
 }
 
+FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex)
+{
+    Node& node = m_jit.graph()[nodeIndex];
+    VirtualRegister virtualRegister = node.virtualRegister();
+    GenerationInfo& info = m_generationInfo[virtualRegister];
+
+    if (info.registerFormat() == DataFormatNone) {
+        GPRReg gpr = allocate();
+
+        if (node.isConstant()) {
+            if (isInt32Constant(nodeIndex)) {
+                FPRReg fpr = fprAllocate();
+                m_jit.move(MacroAssembler::ImmPtr(reinterpret_cast<void*>(reinterpretDoubleToIntptr(static_cast<double>(valueOfInt32Constant(nodeIndex))))), gpr);
+                m_jit.movePtrToDouble(gpr, fpr);
+                unlock(gpr);
+
+                m_fprs.retain(fpr, virtualRegister, SpillOrderDouble);
+                info.fillDouble(fpr);
+                return fpr;
+            }
+            if (isDoubleConstant(nodeIndex)) {
+                FPRReg fpr = fprAllocate();
+                m_jit.move(MacroAssembler::ImmPtr(reinterpret_cast<void*>(reinterpretDoubleToIntptr(valueOfDoubleConstant(nodeIndex)))), gpr);
+                m_jit.movePtrToDouble(gpr, fpr);
+                unlock(gpr);
+
+                m_fprs.retain(fpr, virtualRegister, SpillOrderDouble);
+                info.fillDouble(fpr);
+                return fpr;
+            }
+            ASSERT(isJSConstant(nodeIndex));
+            JSValue jsValue = valueOfJSConstant(nodeIndex);
+            m_jit.move(MacroAssembler::ImmPtr(JSValue::encode(jsValue)), gpr);
+            m_gprs.retain(gpr, virtualRegister, SpillOrderConstant);
+            info.fillJSValue(gpr, DataFormatJS);
+            unlock(gpr);
+        } else {
+            DataFormat spillFormat = info.spillFormat();
+            ASSERT(spillFormat & DataFormatJS);
+            m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled);
+            m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), gpr);
+            info.fillJSValue(gpr, spillFormat);
+            unlock(gpr);
+        }
+    }
+
+    switch (info.registerFormat()) {
+    case DataFormatNone:
+        // Should have filled, above.
+        ASSERT_NOT_REACHED();
+        
+    case DataFormatCell:
+    case DataFormatJSCell:
+    case DataFormatJS: {
+        GPRReg jsValueGpr = info.gpr();
+        m_gprs.lock(jsValueGpr);
+        FPRReg fpr = fprAllocate();
+        GPRReg tempGpr = allocate();
+
+        JITCompiler::Jump isInteger = m_jit.branchPtr(MacroAssembler::AboveOrEqual, jsValueGpr, GPRInfo::tagTypeNumberRegister);
+
+        speculationCheck(m_jit.branchTestPtr(MacroAssembler::Zero, jsValueGpr, GPRInfo::tagTypeNumberRegister));
+
+        // First, if we get here we have a double encoded as a JSValue
+        m_jit.move(jsValueGpr, tempGpr);
+        m_jit.addPtr(GPRInfo::tagTypeNumberRegister, tempGpr);
+        m_jit.movePtrToDouble(tempGpr, fpr);
+        JITCompiler::Jump hasUnboxedDouble = m_jit.jump();
+
+        // Finally, handle integers.
+        isInteger.link(&m_jit);
+        m_jit.convertInt32ToDouble(jsValueGpr, fpr);
+        hasUnboxedDouble.link(&m_jit);
+
+        m_gprs.release(jsValueGpr);
+        m_gprs.unlock(jsValueGpr);
+        m_gprs.unlock(tempGpr);
+        m_fprs.retain(fpr, virtualRegister, SpillOrderDouble);
+        info.fillDouble(fpr);
+        return fpr;
+    }
+
+    case DataFormatJSInteger:
+    case DataFormatInteger: {
+        FPRReg fpr = fprAllocate();
+        GPRReg gpr = info.gpr();
+        m_gprs.lock(gpr);
+        m_jit.convertInt32ToDouble(gpr, fpr);
+        m_gprs.unlock(gpr);
+        return fpr;
+    }
+
+    // Unbox the double
+    case DataFormatJSDouble: {
+        GPRReg gpr = info.gpr();
+        FPRReg fpr = unboxDouble(gpr);
+
+        m_gprs.release(gpr);
+        m_fprs.retain(fpr, virtualRegister, SpillOrderDouble);
+
+        info.fillDouble(fpr);
+        return fpr;
+    }
+
+    case DataFormatDouble: {
+        FPRReg fpr = info.fpr();
+        m_fprs.lock(fpr);
+        return fpr;
+    }
+    }
+
+    ASSERT_NOT_REACHED();
+    return InvalidFPRReg;
+}
+
 GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex)
 {
     Node& node = m_jit.graph()[nodeIndex];
@@ -430,120 +545,180 @@
     }
 
     case ValueToNumber: {
-        SpeculateIntegerOperand op1(this, node.child1);
-        GPRTemporary result(this, op1);
-        m_jit.move(op1.gpr(), result.gpr());
-        integerResult(result.gpr(), m_compileIndex, op1.format());
+        if (isInteger(node.child1)) {
+            SpeculateIntegerOperand op1(this, node.child1);
+            GPRTemporary result(this, op1);
+            m_jit.move(op1.gpr(), result.gpr());
+            integerResult(result.gpr(), m_compileIndex, op1.format());
+            break;
+        }
+        SpeculateDoubleOperand op1(this, node.child1);
+        FPRTemporary result(this, op1);
+        m_jit.moveDouble(op1.fpr(), result.fpr());
+        doubleResult(result.fpr(), m_compileIndex);
         break;
     }
 
     case ValueAdd:
     case ArithAdd: {
-        if (isInt32Constant(node.child1)) {
-            int32_t imm1 = valueOfInt32Constant(node.child1);
-            SpeculateIntegerOperand op2(this, node.child2);
-            GPRTemporary result(this);
+        if (isInteger(node.child1) || isInteger(node.child2)) {
+            if (isInt32Constant(node.child1)) {
+                int32_t imm1 = valueOfInt32Constant(node.child1);
+                SpeculateIntegerOperand op2(this, node.child2);
+                GPRTemporary result(this);
 
-            speculationCheck(m_jit.branchAdd32(MacroAssembler::Overflow, op2.gpr(), Imm32(imm1), result.gpr()));
+                speculationCheck(m_jit.branchAdd32(MacroAssembler::Overflow, op2.gpr(), Imm32(imm1), result.gpr()));
 
-            integerResult(result.gpr(), m_compileIndex);
-            break;
-        }
-            
-        if (isInt32Constant(node.child2)) {
+                integerResult(result.gpr(), m_compileIndex);
+                break;
+            }
+                
+            if (isInt32Constant(node.child2)) {
+                SpeculateIntegerOperand op1(this, node.child1);
+                int32_t imm2 = valueOfInt32Constant(node.child2);
+                GPRTemporary result(this);
+
+                speculationCheck(m_jit.branchAdd32(MacroAssembler::Overflow, op1.gpr(), Imm32(imm2), result.gpr()));
+
+                integerResult(result.gpr(), m_compileIndex);
+                break;
+            }
+                
             SpeculateIntegerOperand op1(this, node.child1);
-            int32_t imm2 = valueOfInt32Constant(node.child2);
-            GPRTemporary result(this);
+            SpeculateIntegerOperand op2(this, node.child2);
+            GPRTemporary result(this, op1, op2);
 
-            speculationCheck(m_jit.branchAdd32(MacroAssembler::Overflow, op1.gpr(), Imm32(imm2), result.gpr()));
+            GPRReg gpr1 = op1.gpr();
+            GPRReg gpr2 = op2.gpr();
+            GPRReg gprResult = result.gpr();
+            MacroAssembler::Jump check = m_jit.branchAdd32(MacroAssembler::Overflow, gpr1, gpr2, gprResult);
 
-            integerResult(result.gpr(), m_compileIndex);
+            if (gpr1 == gprResult)
+                speculationCheck(check, SpeculationRecovery(SpeculativeAdd, gprResult, gpr2));
+            else if (gpr2 == gprResult)
+                speculationCheck(check, SpeculationRecovery(SpeculativeAdd, gprResult, gpr1));
+            else
+                speculationCheck(check);
+
+            integerResult(gprResult, m_compileIndex);
             break;
         }
-            
-        SpeculateIntegerOperand op1(this, node.child1);
-        SpeculateIntegerOperand op2(this, node.child2);
-        GPRTemporary result(this, op1, op2);
 
-        GPRReg gpr1 = op1.gpr();
-        GPRReg gpr2 = op2.gpr();
-        GPRReg gprResult = result.gpr();
-        MacroAssembler::Jump check = m_jit.branchAdd32(MacroAssembler::Overflow, gpr1, gpr2, gprResult);
+        SpeculateDoubleOperand op1(this, node.child1);
+        SpeculateDoubleOperand op2(this, node.child2);
+        FPRTemporary result(this, op1, op2);
 
-        if (gpr1 == gprResult)
-            speculationCheck(check, SpeculationRecovery(SpeculativeAdd, gprResult, gpr2));
-        else if (gpr2 == gprResult)
-            speculationCheck(check, SpeculationRecovery(SpeculativeAdd, gprResult, gpr1));
-        else
-            speculationCheck(check);
+        FPRReg reg1 = op1.fpr();
+        FPRReg reg2 = op2.fpr();
+        m_jit.addDouble(reg1, reg2, result.fpr());
 
-        integerResult(gprResult, m_compileIndex);
+        doubleResult(result.fpr(), m_compileIndex);
         break;
     }
 
     case ArithSub: {
-        if (isInt32Constant(node.child2)) {
+        if (isInteger(node.child1) || isInteger(node.child2)) {
+            if (isInt32Constant(node.child2)) {
+                SpeculateIntegerOperand op1(this, node.child1);
+                int32_t imm2 = valueOfInt32Constant(node.child2);
+                GPRTemporary result(this);
+
+                speculationCheck(m_jit.branchSub32(MacroAssembler::Overflow, op1.gpr(), Imm32(imm2), result.gpr()));
+
+                integerResult(result.gpr(), m_compileIndex);
+                break;
+            }
+                
             SpeculateIntegerOperand op1(this, node.child1);
-            int32_t imm2 = valueOfInt32Constant(node.child2);
+            SpeculateIntegerOperand op2(this, node.child2);
             GPRTemporary result(this);
 
-            speculationCheck(m_jit.branchSub32(MacroAssembler::Overflow, op1.gpr(), Imm32(imm2), result.gpr()));
+            speculationCheck(m_jit.branchSub32(MacroAssembler::Overflow, op1.gpr(), op2.gpr(), result.gpr()));
 
             integerResult(result.gpr(), m_compileIndex);
             break;
         }
-            
-        SpeculateIntegerOperand op1(this, node.child1);
-        SpeculateIntegerOperand op2(this, node.child2);
-        GPRTemporary result(this);
+        SpeculateDoubleOperand op1(this, node.child1);
+        SpeculateDoubleOperand op2(this, node.child2);
+        FPRTemporary result(this, op1);
 
-        speculationCheck(m_jit.branchSub32(MacroAssembler::Overflow, op1.gpr(), op2.gpr(), result.gpr()));
+        FPRReg reg1 = op1.fpr();
+        FPRReg reg2 = op2.fpr();
+        m_jit.subDouble(reg1, reg2, result.fpr());
 
-        integerResult(result.gpr(), m_compileIndex);
+        doubleResult(result.fpr(), m_compileIndex);
         break;
     }
 
     case ArithMul: {
-        SpeculateIntegerOperand op1(this, node.child1);
-        SpeculateIntegerOperand op2(this, node.child2);
-        GPRTemporary result(this);
+        if (isInteger(node.child1) && isInteger(node.child2)) {
+            SpeculateIntegerOperand op1(this, node.child1);
+            SpeculateIntegerOperand op2(this, node.child2);
+            GPRTemporary result(this);
 
-        GPRReg reg1 = op1.gpr();
-        GPRReg reg2 = op2.gpr();
-        speculationCheck(m_jit.branchMul32(MacroAssembler::Overflow, reg1, reg2, result.gpr()));
+            GPRReg reg1 = op1.gpr();
+            GPRReg reg2 = op2.gpr();
+            speculationCheck(m_jit.branchMul32(MacroAssembler::Overflow, reg1, reg2, result.gpr()));
 
-        MacroAssembler::Jump resultNonZero = m_jit.branchTest32(MacroAssembler::NonZero, result.gpr());
-        speculationCheck(m_jit.branch32(MacroAssembler::LessThan, reg1, TrustedImm32(0)));
-        speculationCheck(m_jit.branch32(MacroAssembler::LessThan, reg2, TrustedImm32(0)));
-        resultNonZero.link(&m_jit);
+            MacroAssembler::Jump resultNonZero = m_jit.branchTest32(MacroAssembler::NonZero, result.gpr());
+            speculationCheck(m_jit.branch32(MacroAssembler::LessThan, reg1, TrustedImm32(0)));
+            speculationCheck(m_jit.branch32(MacroAssembler::LessThan, reg2, TrustedImm32(0)));
+            resultNonZero.link(&m_jit);
 
-        integerResult(result.gpr(), m_compileIndex);
+            integerResult(result.gpr(), m_compileIndex);
+            break;
+        }
+        SpeculateDoubleOperand op1(this, node.child1);
+        SpeculateDoubleOperand op2(this, node.child2);
+        FPRTemporary result(this, op1, op2);
+
+        FPRReg reg1 = op1.fpr();
+        FPRReg reg2 = op2.fpr();
+        
+        m_jit.mulDouble(reg1, reg2, result.fpr());
+        
+        doubleResult(result.fpr(), m_compileIndex);
         break;
     }
 
     case ArithDiv: {
-        SpeculateIntegerOperand op1(this, node.child1);
-        SpeculateIntegerOperand op2(this, node.child2);
-        GPRTemporary result(this, op1, op2);
+        SpeculateDoubleOperand op1(this, node.child1);
+        SpeculateDoubleOperand op2(this, node.child2);
+        FPRTemporary result(this, op1);
 
-        op1.gpr();
-        op2.gpr();
-        terminateSpeculativeExecution();
+        FPRReg reg1 = op1.fpr();
+        FPRReg reg2 = op2.fpr();
+        m_jit.divDouble(reg1, reg2, result.fpr());
 
-        integerResult(result.gpr(), m_compileIndex);
+        doubleResult(result.fpr(), m_compileIndex);
         break;
     }
 
     case ArithMod: {
         SpeculateIntegerOperand op1(this, node.child1);
         SpeculateIntegerOperand op2(this, node.child2);
-        GPRTemporary result(this, op1, op2);
+        GPRTemporary eax(this, X86Registers::eax);
+        GPRTemporary edx(this, X86Registers::edx);
+        GPRReg op1Gpr = op1.gpr();
+        GPRReg op2Gpr = op2.gpr();
 
-        op1.gpr();
-        op2.gpr();
-        terminateSpeculativeExecution();
+        speculationCheck(m_jit.branchTestPtr(JITCompiler::Zero, op2Gpr));
 
-        integerResult(result.gpr(), m_compileIndex);
+        GPRReg temp2 = InvalidGPRReg;
+        if (op2Gpr == X86Registers::eax || op2Gpr == X86Registers::edx) {
+            temp2 = allocate();
+            m_jit.move(op2Gpr, temp2);
+            op2Gpr = temp2;
+        }
+
+        m_jit.move(op1Gpr, eax.gpr());
+        m_jit.assembler().cdq();
+        m_jit.assembler().idivl_r(op2Gpr);
+
+        if (temp2 != InvalidGPRReg)
+            unlock(temp2);
+
+        integerResult(edx.gpr(), m_compileIndex);
         break;
     }
 

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h (89960 => 89961)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2011-06-28 21:28:31 UTC (rev 89960)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2011-06-28 21:33:38 UTC (rev 89961)
@@ -129,6 +129,7 @@
     // machine registers, implicitly generating speculation checks as needed.
     GPRReg fillSpeculateInt(NodeIndex, DataFormat& returnFormat);
     GPRReg fillSpeculateIntStrict(NodeIndex);
+    FPRReg fillSpeculateDouble(NodeIndex);
     GPRReg fillSpeculateCell(NodeIndex);
 
 private:
@@ -317,6 +318,42 @@
     GPRReg m_gprOrInvalid;
 };
 
+class SpeculateDoubleOperand {
+public:
+    explicit SpeculateDoubleOperand(SpeculativeJIT* jit, NodeIndex index)
+        : m_jit(jit)
+        , m_index(index)
+        , m_fprOrInvalid(InvalidFPRReg)
+    {
+        ASSERT(m_jit);
+        if (jit->isFilled(index))
+            fpr();
+    }
+
+    ~SpeculateDoubleOperand()
+    {
+        ASSERT(m_fprOrInvalid != InvalidFPRReg);
+        m_jit->unlock(m_fprOrInvalid);
+    }
+
+    NodeIndex index() const
+    {
+        return m_index;
+    }
+
+    FPRReg fpr()
+    {
+        if (m_fprOrInvalid == InvalidFPRReg)
+            m_fprOrInvalid = m_jit->fillSpeculateDouble(index());
+        return m_fprOrInvalid;
+    }
+
+private:
+    SpeculativeJIT* m_jit;
+    NodeIndex m_index;
+    FPRReg m_fprOrInvalid;
+};
+
 class SpeculateCellOperand {
 public:
     explicit SpeculateCellOperand(SpeculativeJIT* jit, NodeIndex index)
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to