WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
almost done!
blah.patch (text/plain), 90.57 KB, created by
Filip Pizlo
on 2013-09-15 12:12:37 PDT
(
hide
)
Description:
almost done!
Filename:
MIME Type:
Creator:
Filip Pizlo
Created:
2013-09-15 12:12:37 PDT
Size:
90.57 KB
patch
obsolete
>Index: Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h >=================================================================== >--- Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h (revision 155820) >+++ Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h (working copy) >@@ -820,6 +820,12 @@ public: > m_assembler.cvtsi2sd_rr(src, dest); > } > >+ void convertInt64ToDouble(RegisterID src, FPRegisterID dest) >+ { >+ ASSERT(isSSE2Present()); >+ m_assembler.cvtsi2sdq_rr(src, dest); >+ } >+ > void convertInt32ToDouble(Address src, FPRegisterID dest) > { > ASSERT(isSSE2Present()); >Index: Source/JavaScriptCore/assembler/MacroAssemblerX86_64.h >=================================================================== >--- Source/JavaScriptCore/assembler/MacroAssemblerX86_64.h (revision 155820) >+++ Source/JavaScriptCore/assembler/MacroAssemblerX86_64.h (working copy) >@@ -230,6 +230,11 @@ public: > m_assembler.shlq_i8r(imm.m_value, dest); > } > >+ void rshift64(TrustedImm32 imm, RegisterID dest) >+ { >+ m_assembler.sarq_i8r(imm.m_value, dest); >+ } >+ > void neg64(RegisterID dest) > { > m_assembler.negq_r(dest); >Index: Source/JavaScriptCore/assembler/X86Assembler.h >=================================================================== >--- Source/JavaScriptCore/assembler/X86Assembler.h (revision 155820) >+++ Source/JavaScriptCore/assembler/X86Assembler.h (working copy) >@@ -1611,6 +1611,14 @@ public: > m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, src); > } > >+#if CPU(X86_64) >+ void cvtsi2sdq_rr(RegisterID src, XMMRegisterID dst) >+ { >+ m_formatter.prefix(PRE_SSE_F2); >+ m_formatter.twoByteOp64(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, src); >+ } >+#endif >+ > void cvtsi2sd_mr(int offset, RegisterID base, XMMRegisterID dst) > { > m_formatter.prefix(PRE_SSE_F2); >Index: Source/JavaScriptCore/bytecode/DataFormat.h >=================================================================== >--- Source/JavaScriptCore/bytecode/DataFormat.h (revision 155820) >+++ Source/JavaScriptCore/bytecode/DataFormat.h (working copy) >@@ -39,10 +39,12 @@ namespace JSC { > enum DataFormat { > DataFormatNone = 0, > DataFormatInt32 = 1, >- DataFormatDouble = 2, >- DataFormatBoolean = 3, >- DataFormatCell = 4, >- DataFormatStorage = 5, >+ DataFormatInt48 = 2, // Int48's are left-shifted by 16 by default. >+ DataFormatStrictInt48 = 3, // "Strict" Int48 means it's not shifted. >+ DataFormatDouble = 4, >+ DataFormatBoolean = 5, >+ DataFormatCell = 6, >+ DataFormatStorage = 7, > DataFormatJS = 8, > DataFormatJSInt32 = DataFormatJS | DataFormatInt32, > DataFormatJSDouble = DataFormatJS | DataFormatDouble, >@@ -64,6 +66,10 @@ inline const char* dataFormatToString(Da > return "None"; > case DataFormatInt32: > return "Int32"; >+ case DataFormatInt48: >+ return "Int48"; >+ case DataFormatInt48: >+ return "StrictInt48"; > case DataFormatDouble: > return "Double"; > case DataFormatCell: >Index: Source/JavaScriptCore/bytecode/SpeculatedType.cpp >=================================================================== >--- Source/JavaScriptCore/bytecode/SpeculatedType.cpp (revision 155820) >+++ Source/JavaScriptCore/bytecode/SpeculatedType.cpp (working copy) >@@ -345,16 +345,11 @@ SpeculatedType speculationFromValue(JSVa > return SpecInt32; > if (value.isDouble()) { > double number = value.asNumber(); >- if (number == number) { >- int64_t asInt64 = static_cast<int64_t>(number); >- if (asInt64 == number && (asInt64 || !std::signbit(number)) >- && asInt64 < (static_cast<int64_t>(1) << 47) >- && asInt64 >= -(static_cast<int64_t>(1) << 47)) { >- return SpecInt48AsDouble; >- } >- return SpecNonIntAsDouble; >- } >- return SpecDoubleNaN; >+ if (number != number) >+ return SpecDoubleNaN; >+ if (value.isMachineInt()) >+ return SpecInt48AsDouble; >+ return SpecNonIntAsDouble; > } > if (value.isCell()) > return speculationFromCell(value.asCell()); >Index: Source/JavaScriptCore/bytecode/SpeculatedType.h >=================================================================== >--- Source/JavaScriptCore/bytecode/SpeculatedType.h (revision 155820) >+++ Source/JavaScriptCore/bytecode/SpeculatedType.h (working copy) >@@ -62,7 +62,7 @@ static const SpeculatedType SpecString > static const SpeculatedType SpecCellOther = 0x00040000; // It's definitely a JSCell but not a subclass of JSObject and definitely not a JSString. > static const SpeculatedType SpecCell = 0x0007ffff; // It's definitely a JSCell. > static const SpeculatedType SpecInt32 = 0x00800000; // It's definitely an Int32. >-static const SpeculatedType SpecInt48 = 0x01000000; // It's definitely an Int48, it's inside a double, but it doesn't need to be. >+static const SpeculatedType SpecInt48 = 0x01000000; // It's definitely an Int48 and we intend it to unbox it. > static const SpeculatedType SpecMachineInt = 0x01800000; // It's something that we can do machine int arithmetic on. > static const SpeculatedType SpecInt48AsDouble = 0x02000000; // It's definitely an Int48 and it's inside a double. > static const SpeculatedType SpecInteger = 0x03800000; // It's definitely some kind of integer. >@@ -74,9 +74,9 @@ static const SpeculatedType SpecRealNumb > static const SpeculatedType SpecNumber = 0x0f800000; // It's either an Int32 or a Double. > static const SpeculatedType SpecBoolean = 0x10000000; // It's definitely a Boolean. > static const SpeculatedType SpecOther = 0x20000000; // It's definitely none of the above. >-static const SpeculatedType SpecHeapTop = 0x3fffffff; // It can be any of the above. >+static const SpeculatedType SpecHeapTop = 0x3effffff; // It can be any of the above, except for SpecInt48. > static const SpeculatedType SpecEmpty = 0x40000000; // It's definitely an empty value marker. >-static const SpeculatedType SpecBytecodeTop = 0x7fffffff; // It can be any of the above. >+static const SpeculatedType SpecBytecodeTop = 0x7effffff; // It can be any of the above, except for SpecInt48. > static const SpeculatedType SpecFullTop = 0x7fffffff; // It can be any of the above plus anything the DFG chooses. > > typedef bool (*SpeculatedTypeChecker)(SpeculatedType); >Index: Source/JavaScriptCore/bytecode/ValueRecovery.h >=================================================================== >--- Source/JavaScriptCore/bytecode/ValueRecovery.h (revision 155820) >+++ Source/JavaScriptCore/bytecode/ValueRecovery.h (working copy) >@@ -44,12 +44,15 @@ enum ValueRecoveryTechnique { > AlreadyInJSStack, > // It's already in the stack but unboxed. > AlreadyInJSStackAsUnboxedInt32, >+ AlreadyInJSStackAsUnboxedInt48, > AlreadyInJSStackAsUnboxedCell, > AlreadyInJSStackAsUnboxedBoolean, > AlreadyInJSStackAsUnboxedDouble, > // It's in a register. > InGPR, > UnboxedInt32InGPR, >+ UnboxedInt48InGPR, >+ UnboxedStrictInt48InGPR, > UnboxedBooleanInGPR, > #if USE(JSVALUE32_64) > InPair, >@@ -60,6 +63,8 @@ enum ValueRecoveryTechnique { > DisplacedInJSStack, > // It's in the stack, at a different location, and it's unboxed. > Int32DisplacedInJSStack, >+ Int48DisplacedInJSStack, >+ StrictInt48DisplacedInJSStack, > DoubleDisplacedInJSStack, > CellDisplacedInJSStack, > BooleanDisplacedInJSStack, >@@ -95,6 +100,13 @@ public: > return result; > } > >+ static ValueRecovery alreadyInJSStackAsUnboxedInt48() >+ { >+ ValueRecovery result; >+ result.m_technique = AlreadyInJSStackAsUnboxedInt48; >+ return result; >+ } >+ > static ValueRecovery alreadyInJSStackAsUnboxedCell() > { > ValueRecovery result; >@@ -125,6 +137,10 @@ public: > ValueRecovery result; > if (dataFormat == DataFormatInt32) > result.m_technique = UnboxedInt32InGPR; >+ else if (dataFormat == DataFormatInt48) >+ result.m_technique = UnboxedInt48InGPR; >+ else if (dataFormat == DataFormatStrictInt48) >+ result.m_technique = UnboxedStrictInt48InGPR; > else if (dataFormat == DataFormatBoolean) > result.m_technique = UnboxedBooleanInGPR; > else >@@ -168,6 +184,14 @@ public: > result.m_technique = Int32DisplacedInJSStack; > break; > >+ case DataFormatInt48: >+ result.m_technique = Int48DisplacedInJSStack; >+ break; >+ >+ case DataFormatStrictInt48: >+ result.m_technique = StrictInt48DisplacedInJSStack; >+ break; >+ > case DataFormatDouble: > result.m_technique = DoubleDisplacedInJSStack; > break; >@@ -229,6 +253,7 @@ public: > switch (technique()) { > case AlreadyInJSStack: > case AlreadyInJSStackAsUnboxedInt32: >+ case AlreadyInJSStackAsUnboxedInt48: > case AlreadyInJSStackAsUnboxedCell: > case AlreadyInJSStackAsUnboxedBoolean: > case AlreadyInJSStackAsUnboxedDouble: >@@ -240,7 +265,7 @@ public: > > MacroAssembler::RegisterID gpr() const > { >- ASSERT(m_technique == InGPR || m_technique == UnboxedInt32InGPR || m_technique == UnboxedBooleanInGPR || m_technique == UInt32InGPR); >+ ASSERT(m_technique == InGPR || m_technique == UnboxedInt32InGPR || m_technique == UnboxedBooleanInGPR || m_technique == UInt32InGPR || m_technique == UnboxedInt48InGPR || m_technique == UnboxedStrictInt48InGPR); > return m_source.gpr; > } > >@@ -266,7 +291,7 @@ public: > > VirtualRegister virtualRegister() const > { >- ASSERT(m_technique == DisplacedInJSStack || m_technique == Int32DisplacedInJSStack || m_technique == DoubleDisplacedInJSStack || m_technique == CellDisplacedInJSStack || m_technique == BooleanDisplacedInJSStack); >+ ASSERT(m_technique == DisplacedInJSStack || m_technique == Int32DisplacedInJSStack || m_technique == DoubleDisplacedInJSStack || m_technique == CellDisplacedInJSStack || m_technique == BooleanDisplacedInJSStack || m_technique == Int48DisplacedInJSStack || m_technique == StrictInt48DisplacedInJSStack); > return m_source.virtualReg; > } > >@@ -285,6 +310,9 @@ public: > case AlreadyInJSStackAsUnboxedInt32: > out.printf("(int32)"); > return; >+ case AlreadyInJSStackAsUnboxedInt48: >+ out.printf("(int48)"); >+ break; > case AlreadyInJSStackAsUnboxedCell: > out.printf("(cell)"); > return; >@@ -300,6 +328,12 @@ public: > case UnboxedInt32InGPR: > out.printf("int32(%%r%d)", gpr()); > return; >+ case UnboxedInt48InGPR: >+ out.printf("int48(%%r%d)", gpr()); >+ break; >+ case UnboxedStrictInt48InGPR: >+ out.printf("strictInt48(%%r%d)", gpr()); >+ break; > case UnboxedBooleanInGPR: > out.printf("bool(%%r%d)", gpr()); > return; >@@ -320,6 +354,12 @@ public: > case Int32DisplacedInJSStack: > out.printf("*int32(%d)", virtualRegister()); > return; >+ case Int48DisplacedInJSStack: >+ out.printf("*int48(%d)", virtualRegister()); >+ break; >+ case StrictInt48DisplacedInJSStack: >+ out.printf("*strictInt48(%d)", virtualRegister()); >+ break; > case DoubleDisplacedInJSStack: > out.printf("*double(%d)", virtualRegister()); > return; >Index: Source/JavaScriptCore/dfg/DFGAbstractInterpreter.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGAbstractInterpreter.h (revision 155820) >+++ Source/JavaScriptCore/dfg/DFGAbstractInterpreter.h (working copy) >@@ -60,7 +60,7 @@ public: > > bool needsTypeCheck(Node* node, SpeculatedType typesPassedThrough) > { >- return forNode(node).m_type & ~typesPassedThrough; >+ return !forNode(node).isType(typesPassedThrough); > } > > bool needsTypeCheck(Edge edge, SpeculatedType typesPassedThrough) >@@ -168,7 +168,7 @@ private: > ALWAYS_INLINE void filterByType(Node* node, Edge& edge, SpeculatedType type) > { > AbstractValue& value = forNode(edge); >- if (value.m_type & ~type) { >+ if (!value.isType(type)) { > node->setCanExit(true); > edge.setProofStatus(NeedsCheck); > } else >Index: Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h (revision 155820) >+++ Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h (working copy) >@@ -310,6 +310,26 @@ bool AbstractInterpreter<AbstractStateTy > break; > } > >+ case Int48ToDouble: { >+ JSValue child = forNode(node->child1()).value(); >+ if (child && child.isNumber()) { >+ setConstant(node, child); >+ break; >+ } >+ forNode(node).setType(SpecInt48AsDouble); >+ break; >+ } >+ >+ case Int48AsValue: { >+ JSValue child = forNode(node->child1()).value(); >+ if (child && child.isNumber()) { >+ setConstant(node, child); >+ break; >+ } >+ forNode(node).setType(SpecMachineInt); >+ break; >+ } >+ > case ValueAdd: > case ArithAdd: { > JSValue left = forNode(node->child1()).value(); >@@ -324,6 +344,12 @@ bool AbstractInterpreter<AbstractStateTy > if (!bytecodeCanTruncateInteger(node->arithNodeFlags())) > node->setCanExit(true); > break; >+ case MachineIntUse: >+ forNode(node).setType(SpecInt48); >+ if (!forNode(node->child1()).isType(SpecInt32) >+ || !forNode(node->child2()).isType(SpecInt32)) >+ node->setCanExit(true); >+ break; > case NumberUse: > if (isRealNumberSpeculation(forNode(node->child1()).m_type) > && isRealNumberSpeculation(forNode(node->child2()).m_type)) >@@ -358,6 +384,12 @@ bool AbstractInterpreter<AbstractStateTy > if (!bytecodeCanTruncateInteger(node->arithNodeFlags())) > node->setCanExit(true); > break; >+ case MachineIntUse: >+ forNode(node).setType(SpecInt48); >+ if (!forNode(node->child1()).isType(SpecInt32) >+ || !forNode(node->child2()).isType(SpecInt32)) >+ node->setCanExit(true); >+ break; > case NumberUse: > forNode(node).setType(SpecDouble); > break; >@@ -380,6 +412,13 @@ bool AbstractInterpreter<AbstractStateTy > if (!bytecodeCanTruncateInteger(node->arithNodeFlags())) > node->setCanExit(true); > break; >+ case MachineIntUse: >+ forNode(node).setType(SpecInt48); >+ if (m_state.forNode(node->child1()).couldBeType(SpecInt48)) >+ node->setCanExit(true); >+ if (!bytecodeCanIgnoreNegativeZer(node->arithNodeFlags())) >+ node->setCanExit(true); >+ break; > case NumberUse: > forNode(node).setType(SpecDouble); > break; >@@ -404,6 +443,10 @@ bool AbstractInterpreter<AbstractStateTy > || !bytecodeCanIgnoreNegativeZero(node->arithNodeFlags())) > node->setCanExit(true); > break; >+ case MachineIntUse: >+ forNode(node).setType(SpecInt48); >+ node->setCanExit(true); >+ break; > case NumberUse: > if (isRealNumberSpeculation(forNode(node->child1()).m_type) > || isRealNumberSpeculation(forNode(node->child2()).m_type)) >@@ -812,7 +855,7 @@ bool AbstractInterpreter<AbstractStateTy > if (node->shouldSpeculateInt32()) > forNode(node).setType(SpecInt32); > else >- forNode(node).setType(SpecDouble); >+ forNode(node).setType(SpecInt48); > break; > case Array::Float32Array: > forNode(node).setType(SpecDouble); >Index: Source/JavaScriptCore/dfg/DFGAbstractValue.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGAbstractValue.h (revision 155820) >+++ Source/JavaScriptCore/dfg/DFGAbstractValue.h (working copy) >@@ -185,6 +185,16 @@ struct AbstractValue { > checkConsistency(); > } > >+ bool couldBeType(SpeculatedType desiredType) >+ { >+ return !!(m_type & desiredType); >+ } >+ >+ bool isType(SpeculatedType desiredType) >+ { >+ return !(m_type & ~desiredType); >+ } >+ > FiltrationResult filter(Graph&, const StructureSet&); > > FiltrationResult filterArrayModes(ArrayModes arrayModes); >Index: Source/JavaScriptCore/dfg/DFGCommon.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGCommon.h (revision 155820) >+++ Source/JavaScriptCore/dfg/DFGCommon.h (working copy) >@@ -143,6 +143,15 @@ inline bool enableConcurrentJIT() > #endif > } > >+inline bool enableInt48() >+{ >+#if USE(JSVALUE64) >+ return true; >+#else >+ return false; >+#endif >+} >+ > enum SpillRegistersMode { NeedToSpill, DontSpill }; > > enum NoResultTag { NoResult }; >Index: Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGFixupPhase.cpp (revision 155820) >+++ Source/JavaScriptCore/dfg/DFGFixupPhase.cpp (working copy) >@@ -124,6 +124,11 @@ private: > break; > } > >+ if (node->child1()->shouldSpeculateMachineInt()) { >+ fixEdge<MachineIntUse>(node->child1()); >+ break; >+ } >+ > if (node->child1()->shouldSpeculateNumber()) { > fixEdge<NumberUse>(node->child1()); > break; >@@ -197,6 +202,10 @@ private: > fixEdge<Int32Use>(node->child1()); > break; > } >+ if (enableInt48() && m_graph.negateShouldSpeculateMachineInt(node)) { >+ fixEdge<MachineIntUse>(node->child1()); >+ break; >+ } > fixEdge<NumberUse>(node->child1()); > break; > } >@@ -207,6 +216,11 @@ private: > fixEdge<Int32Use>(node->child2()); > break; > } >+ if (enableInt48() && m_graph.mulShouldSpeculateMachineInt(node)) { >+ fixEdge<MachineIntUse>(node->child1()); >+ fixEdge<MachineIntUse>(node->child2()); >+ break; >+ } > fixEdge<NumberUse>(node->child1()); > fixEdge<NumberUse>(node->child2()); > break; >@@ -300,6 +314,11 @@ private: > fixEdge<Int32Use>(node->child2()); > break; > } >+ if (enableInt48() >+ && Node::shouldSpeculateMachineInt(node->child1().node(), node->child2().node())) { >+ fixEdge<MachineIntUse>(node->child1()); >+ fixEdge<MachineIntUse>(node->child2()); >+ } > if (Node::shouldSpeculateNumber(node->child1().node(), node->child2().node())) { > fixEdge<NumberUse>(node->child1()); > fixEdge<NumberUse>(node->child2()); >@@ -355,6 +374,12 @@ private: > fixEdge<Int32Use>(node->child2()); > break; > } >+ if (enableInt48() >+ && Node::shouldSpeculateMachineInt(node->child1().node(), node->child2().node())) { >+ fixEdge<MachineIntUse>(node->child1()); >+ fixEdge<MachineIntUse>(node->child2()); >+ break; >+ } > if (Node::shouldSpeculateNumber(node->child1().node(), node->child2().node())) { > fixEdge<NumberUse>(node->child1()); > fixEdge<NumberUse>(node->child2()); >@@ -475,7 +500,10 @@ private: > case Array::Int32: > fixEdge<KnownCellUse>(child1); > fixEdge<Int32Use>(child2); >- fixEdge<Int32Use>(child3); >+ if (child3->prediction() & SpecInt48) >+ fixEdge<Int48Use>(child3); >+ else >+ fixEdge<Int32Use>(child3); > break; > case Array::Double: > fixEdge<KnownCellUse>(child1); >@@ -1347,12 +1375,84 @@ private: > template<UseKind useKind> > void fixEdge(Edge& edge, SpeculationDirection direction = BackwardSpeculation) > { >- if (isDouble(useKind) && edge->shouldSpeculateInt32ForArithmetic()) { >- injectInt32ToDoubleNode(edge, useKind, direction); >+ if (isDouble(useKind)) { >+ if (edge->shouldSpeculateInt32ForArithmetic()) { >+ injectInt32ToDoubleNode(edge, useKind, direction); >+ return; >+ } >+ >+ if (enableInt48() && edge->shouldSpeculateMachineInt()) { >+ // Make all double uses of int48 values have an intermediate Int48ToDouble. >+ // This is for the same reason as Int48ToValue (see below) except that >+ // Int8ToDouble will convert int48's that fit in an int32 into a double >+ // rather than trying to create a boxed int32 like Int48ToValue does. >+ >+ observeUseKindOnNode<MachineIntUse>(edge.node()); >+ >+ Node* result = m_insertionSet.insertNode( >+ m_indexInBlock, SpecInt48AsDouble, Int48ToDouble, >+ m_currentNode->codeOrigin, Edge(edge.node(), MachineIntUse)); >+ edge = Edge(result, useKind); >+ return; >+ } >+ } >+ >+ if (enableInt48() && useKind != MachineIntUse >+ && edge->shouldSpeculateMachineInt() && !edge->shouldSpeculateInt32()) { >+ // We make all non-int48 uses of int48 values have an intermediate Int48ToValue >+ // node to ensure that we handle this properly: >+ // >+ // a: SomeInt48 >+ // b: ArithAdd(@a, ...) >+ // c: Call(..., @a) >+ // d: ArithAdd(@a, ...) >+ // >+ // Without an intermediate node and just labeling the uses, we will get: >+ // >+ // a: SomeInt48 >+ // b: ArithAdd(Int48:@a, ...) >+ // c: Call(..., Untyped:@a) >+ // d: ArithAdd(Int48:@a, ...) >+ // >+ // And now the c->Untyped:@a edge will box the value of @a into a double. This >+ // is bad, because now the d->Int48:@a edge will either have to do double-to-int >+ // conversions, or will have to OSR exit unconditionally. Alternatively we could >+ // have the c->Untyped:@a edge box the value by copying rather than in-place. >+ // But these boxings are also costly so this wouldn't be great. >+ // >+ // The solution we use is to always have non-Int48 uses of predicted Int48's use >+ // an intervening Int48ToValue node: >+ // >+ // a: SomeInt48 >+ // b: ArithAdd(Int48:@a, ...) >+ // x: Int48ToValue(Int48:@a) >+ // c: Call(..., Untyped:@x) >+ // d: ArithAdd(Int48:@a, ...) >+ // >+ // Note that even if we had multiple non-int48 uses of @a, the multiple >+ // Int48ToValue's would get CSE'd together. So the boxing would only happen once. >+ // At the same time, @a would continue to be represented as a native int48. >+ // >+ // An alternative would have been to insert ToNativeInt48 nodes on int48 uses of >+ // int48's. This would have handled the above example but would fall over for: >+ // >+ // a: SomeInt48 >+ // b: Call(..., @a) >+ // c: ArithAdd(@a, ...) >+ // >+ // But the solution we use handles the above gracefully. >+ >+ observeUseKindOnNode<MachineIntUse>(edge.node()); >+ >+ Node* result = m_insertionSet.insertNode( >+ m_indexInBlock, SpecInt48, Int48ToValue, >+ m_currentNode->codeOrigin, Edge(edge.node(), MachineIntUse)); >+ edge = Edge(result, useKind); > return; > } > > observeUseKindOnNode<useKind>(edge.node()); >+ > edge.setUseKind(useKind); > } > >@@ -1378,7 +1478,7 @@ private: > void injectInt32ToDoubleNode(Edge& edge, UseKind useKind = NumberUse, SpeculationDirection direction = BackwardSpeculation) > { > Node* result = m_insertionSet.insertNode( >- m_indexInBlock, SpecInt48, Int32ToDouble, >+ m_indexInBlock, SpecInt48AsDouble, Int32ToDouble, > m_currentNode->codeOrigin, Edge(edge.node(), NumberUse)); > if (direction == ForwardSpeculation) > result->mergeFlags(NodeExitsForward); >@@ -1392,6 +1492,17 @@ private: > edge = Edge(result, useKind); > } > >+ // We make all int48 edges have an intermediate AsNativeInt48 node to ensure >+ // that we handle this properly: >+ template<UseKind useKind> >+ void fixInt48Edge(Edge& edge) >+ { >+ Node* result = m_insertionSet.insertNode( >+ m_indexInBlock, SpecInt48, AsNativeInt48, >+ m_currentNode->codeOrigin, Edge(edge.node(), useKind)); >+ edge = edge(result, MachineIntUse); >+ } >+ > void truncateConstantToInt32(Edge& edge) > { > Node* oldNode = edge.node(); >@@ -1434,13 +1545,20 @@ private: > bool attemptToMakeIntegerAdd(Node* node) > { > AddSpeculationMode mode = m_graph.addSpeculationMode(node); >- if (mode == DontSpeculateInt32) >- return false; >+ if (mode != DontSpeculateInt32) { >+ truncateConstantsIfNecessary(node, mode); >+ fixEdge<Int32Use>(node->child1()); >+ fixEdge<Int32Use>(node->child2()); >+ return true; >+ } > >- truncateConstantsIfNecessary(node, mode); >- fixEdge<Int32Use>(node->child1()); >- fixEdge<Int32Use>(node->child2()); >- return true; >+ if (enableInt48() && m_graph.addShouldSpeculateMachineInt(node)) { >+ fixEdge<MachineIntUse>(node->child1()); >+ fixEdge<MachineIntUse>(node->child2()); >+ return true; >+ } >+ >+ return false; > } > > bool attemptToMakeGetArrayLength(Node* node) >Index: Source/JavaScriptCore/dfg/DFGFlushFormat.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGFlushFormat.cpp (revision 155820) >+++ Source/JavaScriptCore/dfg/DFGFlushFormat.cpp (working copy) >@@ -41,6 +41,9 @@ void printInternal(PrintStream& out, Flu > case FlushedInt32: > out.print("FlushedInt32"); > return; >+ case FlushedInt48: >+ out.print("FlushedInt32"); >+ return; > case FlushedDouble: > out.print("FlushedDouble"); > return; >Index: Source/JavaScriptCore/dfg/DFGFlushFormat.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGFlushFormat.h (revision 155820) >+++ Source/JavaScriptCore/dfg/DFGFlushFormat.h (working copy) >@@ -40,6 +40,7 @@ namespace JSC { namespace DFG { > enum FlushFormat { > DeadFlush, > FlushedInt32, >+ FlushedInt48, > FlushedDouble, > FlushedCell, > FlushedBoolean, >@@ -55,6 +56,8 @@ inline NodeFlags resultFor(FlushFormat f > return NodeResultJS; > case FlushedInt32: > return NodeResultInt32; >+ case FlushedInt48: >+ return NodeResultInt48; > case FlushedDouble: > return NodeResultNumber; > case FlushedBoolean: >@@ -74,6 +77,8 @@ inline UseKind useKindFor(FlushFormat fo > return CellUse; > case FlushedInt32: > return Int32Use; >+ case FlushedInt48: >+ return MachineIntUse; > case FlushedDouble: > return NumberUse; > case FlushedBoolean: >Index: Source/JavaScriptCore/dfg/DFGGenerationInfo.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGGenerationInfo.h (revision 155820) >+++ Source/JavaScriptCore/dfg/DFGGenerationInfo.h (working copy) >@@ -86,6 +86,19 @@ public: > { > initGPR(node, useCount, gpr, DataFormatInt32); > } >+ void initInt48(Node* node, uint32_t useCount, GPRReg reg, DataFormat format) >+ { >+ ASSERT(format == DataFormatInt48 || format == DataFormatStrictInt48); >+ initGPR(node, useCount, gpr, format); >+ } >+ void initInt48(Node* node, uint32_t useCount, GPRReg reg) >+ { >+ initGPR(node, useCount, gpr, DataFormatInt48); >+ } >+ void initStrictInt48(Node* node, uint32_t useCount, GPRReg reg) >+ { >+ initGPR(node, useCount, gpr, DataFormatStrictInt48); >+ } > #if USE(JSVALUE64) > void initJSValue(Node* node, uint32_t useCount, GPRReg gpr, DataFormat format = DataFormatJS) > { >@@ -187,6 +200,11 @@ public: > // Get the format of the value as it is spilled in the JSStack (or 'none'). > DataFormat spillFormat() { return m_spillFormat; } > >+ bool isFormat(DataFormat expectedFormat) >+ { >+ return registerFormat() == expectedFormat || spillFormat() == expectedFormat; >+ } >+ > bool isJSFormat(DataFormat expectedFormat) > { > return JSC::isJSFormat(registerFormat(), expectedFormat) || JSC::isJSFormat(spillFormat(), expectedFormat); >@@ -197,6 +215,16 @@ public: > return isJSFormat(DataFormatJSInt32); > } > >+ bool isInt48() >+ { >+ return isFormat(DataFormatInt48); >+ } >+ >+ bool isStrictInt48() >+ { >+ return isFormat(DataFormatStrictInt48); >+ } >+ > bool isJSDouble() > { > return isJSFormat(DataFormatJSDouble); >@@ -314,6 +342,14 @@ public: > { > fillGPR(stream, gpr, DataFormatInt32); > } >+ void fillInt48(VariableEventStream& stream, GPRReg gpr) >+ { >+ fillGPR(stream, gpr, DataFormatInt48); >+ } >+ void fillStrictInt48(VariableEventStream& stream, GPRReg gpr) >+ { >+ fillGPR(stream, gpr, DataFormatStrictInt48); >+ } > void fillBoolean(VariableEventStream& stream, GPRReg gpr) > { > fillGPR(stream, gpr, DataFormatBoolean); >Index: Source/JavaScriptCore/dfg/DFGJITCode.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGJITCode.cpp (revision 155820) >+++ Source/JavaScriptCore/dfg/DFGJITCode.cpp (working copy) >@@ -103,6 +103,9 @@ void JITCode::reconstruct( > case AlreadyInJSStackAsUnboxedInt32: > value = jsNumber(exec->r(operand).unboxedInt32()); > break; >+ case AlreadyInJSStackAsUnboxedInt64: >+ value = jsNumber(exec->r(operand).unboxedInt48()); >+ break; > case AlreadyInJSStackAsUnboxedDouble: > value = jsDoubleNumber(exec->r(operand).unboxedDouble()); > break; >Index: Source/JavaScriptCore/dfg/DFGNodeFlags.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGNodeFlags.h (revision 155820) >+++ Source/JavaScriptCore/dfg/DFGNodeFlags.h (working copy) >@@ -41,8 +41,9 @@ namespace JSC { namespace DFG { > #define NodeResultJS 0x0001 > #define NodeResultNumber 0x0002 > #define NodeResultInt32 0x0003 >-#define NodeResultBoolean 0x0004 >-#define NodeResultStorage 0x0005 >+#define NodeResultInt48 0x0004 >+#define NodeResultBoolean 0x0005 >+#define NodeResultStorage 0x0006 > > #define NodeMustGenerate 0x0008 // set on nodes that have side effects, and may not trivially be removed by DCE. > #define NodeHasVarArgs 0x0010 >Index: Source/JavaScriptCore/dfg/DFGNodeType.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGNodeType.h (revision 155820) >+++ Source/JavaScriptCore/dfg/DFGNodeType.h (working copy) >@@ -114,6 +114,10 @@ namespace JSC { namespace DFG { > macro(Int32ToDouble, NodeResultNumber) \ > /* Used to speculate that a double value is actually an integer. */\ > macro(DoubleAsInt32, NodeResultInt32 | NodeExitsForward) \ >+ /* Used to separate representation and register allocation of Int48's represented */\ >+ /* as values. */\ >+ macro(Int48ToValue, NodeResultJS) \ >+ macro(Int48ToDouble, NodeResultNumber) \ > \ > /* Nodes for arithmetic operations. */\ > macro(ArithAdd, NodeResultNumber | NodeMustGenerate) \ >@@ -356,6 +360,17 @@ inline unsigned forwardRewiringSelection > // This loses information. Only use it if there are no better alternatives. > return 25; > >+ case Int48ToValue: >+ // Loses no information. It just boxes the value, which is what OSR wants >+ // to do anyway. >+ return 100; >+ >+ case Int48ToDouble: >+ // This is like Int32ToDouble; we can use it because it gives a semantically >+ // equivalent value but that value may be an int32 in a double, so we'd >+ // rather not if we can avoid it. >+ return 75; >+ > default: > return 0; > } >Index: Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp (revision 155820) >+++ Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp (working copy) >@@ -195,6 +195,8 @@ void OSRExitCompiler::compileExit(const > case InGPR: > case UnboxedInt32InGPR: > case UInt32InGPR: >+ case UnboxedInt48InGPR: >+ case UnboxedStrictInt48InGPR: > m_jit.store64(recovery.gpr(), scratch + index); > break; > >@@ -235,12 +237,15 @@ void OSRExitCompiler::compileExit(const > case DisplacedInJSStack: > case Int32DisplacedInJSStack: > case DoubleDisplacedInJSStack: >+ case Int48DisplacedInJSStack: >+ case StrictInt48DisplacedInJSStack: > m_jit.load64(AssemblyHelpers::addressFor(recovery.virtualRegister()), GPRInfo::regT0); > m_jit.store64(GPRInfo::regT0, scratch + index); > break; > > case AlreadyInJSStackAsUnboxedInt32: > case AlreadyInJSStackAsUnboxedDouble: >+ case AlreadyInJSStackAsUnboxedInt48: > m_jit.load64(AssemblyHelpers::addressFor(operand), GPRInfo::regT0); > m_jit.store64(GPRInfo::regT0, scratch + index); > break; >@@ -274,34 +279,59 @@ void OSRExitCompiler::compileExit(const > m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand)); > break; > >- case AlreadyInJSStackAsUnboxedDouble: >- case InFPR: >- case DoubleDisplacedInJSStack: >- m_jit.move(AssemblyHelpers::TrustedImmPtr(scratch + index), GPRInfo::regT0); >- m_jit.loadDouble(GPRInfo::regT0, FPRInfo::fpRegT0); >- m_jit.boxDouble(FPRInfo::fpRegT0, GPRInfo::regT0); >- m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand)); >- break; >- >+ case AlreadyInJSStackAsUnboxedInt48: >+ case UnboxedInt48InGPR: >+ case UnboxedStrictInt48InGPR: >+ case Int48DisplacedInJSStack: >+ case StructInt48DisplacedInJSStack: > case UInt32InGPR: { > m_jit.load64(scratch + index, GPRInfo::regT0); >- m_jit.zeroExtend32ToPtr(GPRInfo::regT0, GPRInfo::regT0); >- AssemblyHelpers::Jump positive = m_jit.branch32( >- AssemblyHelpers::GreaterThanOrEqual, >- GPRInfo::regT0, AssemblyHelpers::TrustedImm32(0)); >- m_jit.convertInt32ToDouble(GPRInfo::regT0, FPRInfo::fpRegT0); >- m_jit.addDouble( >- AssemblyHelpers::AbsoluteAddress(&AssemblyHelpers::twoToThe32), >- FPRInfo::fpRegT0); >+ >+ // Convert it to an int64. >+ switch (recovery.technique()) { >+ case AlreadyInJSStackAsUnboxedInt48: >+ case UnboxedInt48InGPR: >+ case Int48DisplacedInJSStack: >+ m_jit.rshift(TrustedImm32(16), GPRInfo::regT0); >+ break; >+ >+ case UInt32InGPR: >+ m_jit.zeroExtend32ToPtr(GPRInfo::regT0, GPRInfo::regT0); >+ break; >+ >+ default: >+ break; >+ } >+ >+ // Is it an int32? >+ m_jit.signExtend32ToPtr(GPRInfo::regT0, GPRInfo::regT1); >+ AssemblyHelpers::Jump isInt32 = m_jit.branch64( >+ AssemblyHelpers::Equal, GPRInfo::regT0, GPRInfo::regT1); >+ >+ // Nope, it's not, but regT0 contains the int64 value. >+ m_jit.convertInt64ToDouble(GPRInfo::regT0, FPRInfo::fpRegT0); > m_jit.boxDouble(FPRInfo::fpRegT0, GPRInfo::regT0); > AssemblyHelpers::Jump done = m_jit.jump(); >- positive.link(&m_jit); >+ >+ isInt32.link(&m_jit); >+ m_jit.zeroExtend32ToPtr(GPRInfo::regT0, GPRInfo::regT0); > m_jit.or64(GPRInfo::tagTypeNumberRegister, GPRInfo::regT0); >+ > done.link(&m_jit); >+ > m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand)); > break; > } > >+ case AlreadyInJSStackAsUnboxedDouble: >+ case InFPR: >+ case DoubleDisplacedInJSStack: >+ m_jit.move(AssemblyHelpers::TrustedImmPtr(scratch + index), GPRInfo::regT0); >+ m_jit.loadDouble(GPRInfo::regT0, FPRInfo::fpRegT0); >+ m_jit.boxDouble(FPRInfo::fpRegT0, GPRInfo::regT0); >+ m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand)); >+ break; >+ > case Constant: > m_jit.store64( > AssemblyHelpers::TrustedImm64(JSValue::encode(recovery.constant())), >Index: Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp (revision 155820) >+++ Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp (working copy) >@@ -368,10 +368,20 @@ private: > } > > case GetByVal: { >+ if (!node->child1()->prediction()) >+ break; >+ if (!node->prediction()) >+ break; >+ > if (node->child1()->shouldSpeculateFloat32Array() > || node->child1()->shouldSpeculateFloat64Array()) > changed |= mergePrediction(SpecDouble); >- else >+ else if (node->child1()->shouldSpeculateUint32Array()) { >+ if (node->shouldSpeculateInt32()) >+ changed |= mergePrediction(SpecInt32); >+ else >+ changed |= mergePrediction(SpecInt48); >+ } else > changed |= mergePrediction(node->getHeapPrediction()); > break; > } >Index: Source/JavaScriptCore/dfg/DFGSilentRegisterSavePlan.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSilentRegisterSavePlan.h (revision 155820) >+++ Source/JavaScriptCore/dfg/DFGSilentRegisterSavePlan.h (working copy) >@@ -48,6 +48,8 @@ enum SilentSpillAction { > enum SilentFillAction { > DoNothingForFill, > SetInt32Constant, >+ SetInt48Constant, >+ SetStrictInt48Constant, > SetBooleanConstant, > SetCellConstant, > SetTrustedJSConstant, >@@ -61,8 +63,12 @@ enum SilentFillAction { > Load32Tag, > Load32Payload, > Load32PayloadBoxInt, >+ Load32PayloadConvertToInt48, >+ Load32PayloadSignExtend, > LoadPtr, > Load64, >+ Load64ShiftRight16, >+ Load64ShiftLeft16, > LoadDouble, > LoadDoubleBoxDouble, > LoadJSUnboxDouble >Index: Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp (revision 155820) >+++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp (working copy) >@@ -40,6 +40,40 @@ namespace JSC { namespace DFG { > > #if USE(JSVALUE64) > >+void SpeculativeJIT::boxInt48(GPRReg sourceGPR, GPRReg targetGPR, DataFormat format) >+{ >+ GPRReg tempGPR; >+ if (sourceGPR == targetGPR) >+ tempGPR = allocate(); >+ else >+ tempGPR = targetGPR; >+ >+ FPRReg fpr = fprAllocate(); >+ >+ if (format == DataFormatInt48) >+ m_jit.rshift64(TrustedImm32(16), sourceGPR); >+ else >+ ASSERT(format == DataFormatStrictInt48); >+ m_jit.signExtend32ToPtr(sourceGPR, tempGPR); >+ >+ JITCompiler::Jump isDouble = m_jit.branch64( >+ JITCompiler::NotEqual, sourceGPR, tempGPR); >+ >+ m_jit.zeroExtend32ToPtr(sourceGPR, targetGPR); >+ m_jit.or64(GPRInfo::tagTypeNumberRegister, targetGPR); >+ JITCompiler::Jump done = m_jit.jump(); >+ >+ isDouble.link(&m_jit); >+ m_jit.convertInt64ToDouble(sourceGPR, fpr); >+ boxDouble(fpr, targetGPR); >+ done.link(&m_jit); >+ >+ if (tempGPR != targetGPR) >+ unlock(tempGPR); >+ >+ unlock(fpr); >+} >+ > GPRReg SpeculativeJIT::fillJSValue(Edge edge) > { > VirtualRegister virtualRegister = edge->virtualRegister(); >@@ -69,11 +103,23 @@ GPRReg SpeculativeJIT::fillJSValue(Edge > } else { > DataFormat spillFormat = info.spillFormat(); > m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled); >- if (spillFormat == DataFormatInt32) { >+ switch (spillFormat) { >+ case DataFormatInt32: { > m_jit.load32(JITCompiler::addressFor(virtualRegister), gpr); > m_jit.or64(GPRInfo::tagTypeNumberRegister, gpr); > spillFormat = DataFormatJSInt32; >- } else { >+ break; >+ } >+ >+ case DataFormatInt48: >+ case DataFormatStrictInt48: { >+ m_jit.load64(JITCompiler::addressFor(virtualRegister), gpr); >+ boxInt48(gpr, gpr, spillFormat); >+ spillFormat = DataFormatJS; >+ break; >+ } >+ >+ default: > m_jit.load64(JITCompiler::addressFor(virtualRegister), gpr); > if (spillFormat == DataFormatDouble) { > // Need to box the double, since we want a JSValue. >@@ -81,6 +127,7 @@ GPRReg SpeculativeJIT::fillJSValue(Edge > spillFormat = DataFormatJSDouble; > } else > RELEASE_ASSERT(spillFormat & DataFormatJS); >+ break; > } > info.fillJSValue(*m_stream, gpr, spillFormat); > } >@@ -113,6 +160,16 @@ GPRReg SpeculativeJIT::fillJSValue(Edge > > return gpr; > } >+ >+ case DataFormatInt48: >+ case DataFormatStrictInt48: { >+ GPRReg gpr = info.gpr(); >+ lock(gpr); >+ GPRReg resultGPR = allocate(); >+ boxInt48(gpr, resultGPR, info.registerFormat()); >+ unlock(gpr); >+ return resultGPR; >+ } > > case DataFormatCell: > // No retag required on JSVALUE64! >@@ -768,7 +825,7 @@ GPRReg SpeculativeJIT::fillSpeculateInt3 > > DataFormat spillFormat = info.spillFormat(); > >- RELEASE_ASSERT((spillFormat & DataFormatJS) || spillFormat == DataFormatInt32); >+ RELEASE_ASSERT((spillFormat & DataFormatJS) || spillFormat == DataFormatInt32 || spillFormat == DataFormatInt48 || spillFormat == DataFormatStrictInt48); > > m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled); > >@@ -789,6 +846,25 @@ GPRReg SpeculativeJIT::fillSpeculateInt3 > returnFormat = DataFormatJSInt32; > return gpr; > } >+ if (spillFormat == DataFormatInt48 || spillFormat == DataFormatStrictInt48) { >+ // Can only happen if we've already proved that the value is an int32. >+ // That's because if a value originated as a JSValue then we would >+ // speculate that it's an int32 before representing it as an int48. >+ // Otherwise, if we knowingly produced an int48, then we would be >+ // boxing it into a value using Int48ToValue. This assertion is valid >+ // only because Int48 is something that we introduce at prediction time. >+ RELEASE_ASSERT(!(type & ~SpecInt32)); >+ if (spillFormat == DataFormatStrictInt48) { >+ m_jit.load32(JITCompiler::addressFor(virtualRegister), gpr); >+ } else { >+ m_jit.load64(JITCompiler::addressFor(virtualRegister), gpr); >+ m_jit.rshift64(TrustedImm32(16), gpr); >+ m_jit.zeroExtend32ToPtr(gpr, gpr); >+ } >+ info.fillInt32(*m_stream, gpr); >+ returnFormat = DataFormatInt32; >+ return gpr; >+ } > m_jit.load64(JITCompiler::addressFor(virtualRegister), gpr); > > // Fill as JSValue, and fall through. >@@ -797,6 +873,7 @@ GPRReg SpeculativeJIT::fillSpeculateInt3 > } > > case DataFormatJS: { >+ RELEASE_ASSERT(!(type & SpecInt48)); > // Check the value is an integer. > GPRReg gpr = info.gpr(); > m_gprs.lock(gpr); >@@ -843,6 +920,27 @@ GPRReg SpeculativeJIT::fillSpeculateInt3 > returnFormat = DataFormatInt32; > return gpr; > } >+ >+ case DataFormatStrictInt48: { >+ RELEASE_ASSERT(!(type & ~SpecInt32)); >+ GPRReg gpr = info.gpr(); >+ m_gprs.lock(gpr); >+ m_jit.zeroExtend32ToPtr(gpr, gpr); >+ returnFormat = DataFormatInt32; >+ info.fillInt32(*m_stream, gpr); >+ return gpr; >+ } >+ >+ case DataFormatInt48: { >+ RELEASE_ASSERT(!(type & ~SpecInt32)); >+ GPRReg gpr = info.gpr(); >+ m_gprs.lock(gpr); >+ m_jit.rshift64(TrustedImm32(16), gpr); >+ m_jit.zeroExtend32ToPtr(gpr); >+ returnFormat = DataFormatInt32; >+ info.fillInt32(*m_stream, gpr); >+ return gpr; >+ } > > case DataFormatDouble: > case DataFormatJSDouble: { >@@ -885,6 +983,148 @@ GPRReg SpeculativeJIT::fillSpeculateInt3 > return result; > } > >+GPRReg SpeculativeJIT::fillSpeculateInt48(Edge edge, DataFormat desiredFormat) >+{ >+ ASSERT(desiredFormat == DataFormatInt48 || desiredFormat == DataFormatStrictInt48); >+ AbstractValue& value = m_state.forNode(edge); >+ SpeculatedType type = value.m_type; >+ m_interpreter.filter(value, SpecMachineInt); >+ VirtualRegister virtualRegister = edge->virtualRegister(); >+ GenerationInfo& info = generationInfoFromVirtualRegister(virtualRegister); >+ >+ switch (info.registerFormat()) { >+ case DataFormatNone: { >+ if ((edge->hasConstant() && !valueOfJSConstant(edge.node()).isMachineInt()) || info.spillFormat() == DataFormatDouble) { >+ terminateSpeculativeExecution(Uncountable, JSValueRegs(), 0); >+ return allocate(); >+ } >+ >+ GPRReg gpr = allocate(); >+ >+ if (edge->hasConstant()) { >+ JSValue value = valueOfJSConstant(edge.node()); >+ ASSERT(value.isMachineInt()); >+ m_gprs.retain(gpr, virtualRegister, SpillOrderConstant); >+ int64_t value = value.asMachineInt(); >+ if (desiredFormat == DataFormatInt48) >+ value = value << 16; >+ m_jit.move(MacroAssembler::Imm64(value), gpr); >+ info.fillGPR(*m_stream, gpr, desiredFormat); >+ return gpr; >+ } >+ >+ DataFormat spillFormat = info.spillFormat(); >+ >+ // FIXME!!! >+ >+ RELEASE_ASSERT((spillFormat & DataFormatJS) || spillFormat == DataFormatInt32 || spillFormat == DataFormatInt48 || spillFormat == DataFormatStrictInt48); >+ >+ m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled); >+ >+ if (spillFormat == DataFormatJSInt32 || spillFormat == DataFormatInt32) { >+ // If we know this was spilled as an integer we can fill without checking. >+ m_jit.load32(JITCompiler::addressFor(virtualRegister), gpr); >+ m_jit.signExtend32ToPtr(gpr, gpr); >+ if (desiredFormat == DataFormatStrictInt48) { >+ info.fillStrictInt48(gpr); >+ return gpr; >+ } >+ m_jit.lshift64(TrustedImm32(16), gpr); >+ info.fillInt48(gpr); >+ return gpr; >+ } >+ if (spillFormat == DataFormatInt48 || spillFormat == DataFormatStrictInt48) { >+ m_jit.load64(JITCompiler::addressFor(virtualRegister), gpr); >+ if (desiredFormat == DataFormatStrictInt48) { >+ if (spillFormat == DataFormatInt48) >+ m_jit.rshift64(TrustedImm32(16), gpr); >+ info.fillStrictInt48(gpr); >+ return gpr; >+ } >+ if (spillFormat == DataFormatStrictInt48) >+ m_jit.lshift64(TrustedImm32(16), gpr); >+ info.fillInt48(gpr); >+ return gpr; >+ } >+ m_jit.load64(JITCompiler::addressFor(virtualRegister), gpr); >+ >+ // Fill as JSValue, and fall through. >+ info.fillJSValue(*m_stream, gpr, DataFormatJSInt32); >+ m_gprs.unlock(gpr); >+ } >+ >+ case DataFormatJS: { >+ // Check the value is an integer. Note that we would *like* to unbox an Int48 >+ // at this point but this is too costly. We only *prove* that this is an Int48 >+ // even though we check if it's an int32. >+ GPRReg gpr = info.gpr(); >+ m_gprs.lock(gpr); >+ if (type & ~SpecInt32) >+ speculationCheck(BadType, JSValueRegs(gpr), edge, m_jit.branch64(MacroAssembler::Below, gpr, GPRInfo::tagTypeNumberRegister)); >+ m_jit.signExtend32ToPtr(gpr); >+ if (desiredFormat == DataFormatStrictInt48) { >+ fillStrictInt48(gpr); >+ return gpr; >+ } >+ m_jit.lshift64(TrustedImm64(16), gpr); >+ fillInt48(gpr); >+ return gpr; >+ } >+ >+ case DataFormatInt32: >+ case DataFormatJSInt32: { >+ GPRReg gpr = info.gpr(); >+ m_gprs.lock(gpr); >+ m_jit.signExtend32ToPtr(gpr); >+ if (desiredFormat == DataFormatStrictInt48) { >+ fillStrictInt48(gpr); >+ return gpr; >+ } >+ m_jit.lshift64(TrustedImm64(16), gpr); >+ fillInt48(gpr); >+ return gpr; >+ } >+ >+ case DataFormatStrictInt48: { >+ GPRReg gpr = info.gpr(); >+ m_gprs.lock(gpr); >+ if (desiredFormat == DataFormatStrictInt48) >+ return gpr; >+ m_jit.lshift64(TrustedImm32(16), gpr); >+ fillStrictInt48(gpr); >+ return gpr; >+ } >+ >+ case DataFormatInt48: { >+ GPRReg gpr = info.gpr(); >+ m_gprs.lock(gpr); >+ if (desiredFormat == DataFormatInt48) >+ return gpr; >+ m_jit.rshift64(TrustedImm32(16), gpr); >+ fillInt48(gpr); >+ return gpr; >+ } >+ >+ case DataFormatDouble: >+ case DataFormatJSDouble: >+ case DataFormatCell: >+ case DataFormatBoolean: >+ case DataFormatJSCell: >+ case DataFormatJSBoolean: { >+ terminateSpeculativeExecution(Uncountable, JSValueRegs(), 0); >+ returnFormat = DataFormatInt32; >+ return allocate(); >+ } >+ >+ case DataFormatStorage: >+ RELEASE_ASSERT_NOT_REACHED(); >+ >+ default: >+ RELEASE_ASSERT_NOT_REACHED(); >+ return InvalidGPRReg; >+ } >+} >+ > FPRReg SpeculativeJIT::fillSpeculateDouble(Edge edge) > { > #if DFG_ENABLE(DEBUG_VERBOSE) >@@ -945,6 +1185,24 @@ FPRReg SpeculativeJIT::fillSpeculateDoub > unlock(gpr); > break; > } >+ >+ case DataFormatInt48: { >+ GPRReg gpr = allocate(); >+ m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled); >+ m_jit.load64(JITCompiler::addressFor(virtualRegister), gpr); >+ info.fillInt48(gpr); >+ unlock(gpr); >+ break; >+ } >+ >+ case DataFormatStrictInt48: { >+ GPRReg gpr = allocate(); >+ m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled); >+ m_jit.load64(JITCompiler::addressFor(virtualRegister), gpr); >+ info.fillStrictInt48(gpr); >+ unlock(gpr); >+ break; >+ } > > default: > GPRReg gpr = allocate(); >@@ -1009,6 +1267,24 @@ FPRReg SpeculativeJIT::fillSpeculateDoub > m_gprs.unlock(gpr); > return fpr; > } >+ >+ case DataFormatInt48: { >+ GPRReg gpr = info.gpr(); >+ m_gprs.lock(gpr); >+ m_jit.rshift32(TrustedImm32(16), gpr); >+ info.fillStrictInt48(*m_stream, gpr); >+ m_gprs.unlock(gpr); >+ // fall through. >+ } >+ >+ case DataFormatStrictInt48: { >+ FPRReg fpr = fprAllocate(); >+ GPRReg gpr = info.gpr(); >+ m_gprs.lock(gpr); >+ m_jit.convertInt64ToDouble(gpr, fpr); >+ m_gprs.unlock(gpr); >+ return fpr; >+ } > > // Unbox the double > case DataFormatJSDouble: { >@@ -1111,7 +1387,9 @@ GPRReg SpeculativeJIT::fillSpeculateCell > case DataFormatJSDouble: > case DataFormatDouble: > case DataFormatJSBoolean: >- case DataFormatBoolean: { >+ case DataFormatBoolean: >+ case DataFormatInt48: >+ case DataFormatStrictInt48: { > terminateSpeculativeExecution(Uncountable, JSValueRegs(), 0); > return allocate(); > } >@@ -1194,7 +1472,9 @@ GPRReg SpeculativeJIT::fillSpeculateBool > case DataFormatJSDouble: > case DataFormatDouble: > case DataFormatJSCell: >- case DataFormatCell: { >+ case DataFormatCell: >+ case DataFormatInt48: >+ case DataFormatStrictInt48: { > terminateSpeculativeExecution(Uncountable, JSValueRegs(), 0); > return allocate(); > } >@@ -1503,6 +1783,40 @@ void SpeculativeJIT::compileInt32Compare > jsValueResult(result.gpr(), m_currentNode, DataFormatJSBoolean); > } > >+void SpeculativeJIT::compileInt48Compare(Node* node, MacroAssembler::RelationalCondition condition) >+{ >+ SpeculateWhicheverInt48Operand op1(this, node->child1()); >+ SpeculateWhicheverInt48Operand op2(this, node->child2(), op1); >+ GPRTemporary result(this, Reuse, op1, op2); >+ >+ m_jit.compare64(condition, op1.gpr(), op2.gpr(), result.gpr()); >+ >+ // If we add a DataFormatBool, we should use it here. >+ m_jit.or32(TrustedImm32(ValueFalse), result.gpr()); >+ jsValueResult(result.gpr(), m_currentNode, DataFormatJSBoolean); >+} >+ >+void SpeculativeJIT::compilePeepHoleInt48Branch(Node* node, Node* branchNode, JITCompiler::RelationalCondition condition) >+{ >+ BasicBlock* taken = branchNode->takenBlock(); >+ BasicBlock* notTaken = branchNode->notTakenBlock(); >+ >+ // The branch instruction will branch to the taken block. >+ // If taken is next, switch taken with notTaken & invert the branch condition so we can fall through. >+ if (taken == nextBlock()) { >+ condition = JITCompiler::invert(condition); >+ BasicBlock* tmp = taken; >+ taken = notTaken; >+ notTaken = tmp; >+ } >+ >+ SpeculateWhicheverInt48Operand op1(this, node->child1()); >+ SpeculateWhicheverInt48Operand op2(this, node->child1(), op1); >+ >+ branch64(condition, op1.gpr(), op2.gpr()); >+ jump(notTaken); >+} >+ > void SpeculativeJIT::compileDoubleCompare(Node* node, MacroAssembler::DoubleCondition condition) > { > SpeculateDoubleOperand op1(this, node->child1()); >@@ -1906,6 +2220,16 @@ void SpeculativeJIT::compile(Node* node) > break; > } > >+ case FlushedInt48: { >+ GPRTemporary result(this); >+ m_jit.load64(JITCompiler::addressFor(node->local()), result.gpr()); >+ >+ VirtualRegister virtualRegister = node->virtualRegister(); >+ m_gprs.retain(result.gpr(), virtualRegister, SpillOrderJS); >+ generationInfoFromVirtualRegister(virtualRegister).initInt48(node, node->refCount(), result.gpr()); >+ break; >+ } >+ > default: > GPRTemporary result(this); > m_jit.load64(JITCompiler::addressFor(node->local()), result.gpr()); >@@ -1981,6 +2305,14 @@ void SpeculativeJIT::compile(Node* node) > break; > } > >+ case FlushedInt48: { >+ SpeculateInt48Operand value(this, node->child1()); >+ m_jit.store64(value.gpr(), JITCompiler::addressFor(node->local())); >+ noResult(node); >+ recordSetLocal(node->local(), ValueSource(Int48InJSStack)); >+ break; >+ } >+ > case FlushedCell: { > SpeculateCellOperand cell(this, node->child1()); > GPRReg cellGPR = cell.gpr(); >Index: Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp (revision 155820) >+++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp (working copy) >@@ -335,7 +335,9 @@ SilentRegisterSavePlan SpeculativeJIT::s > spillAction = Store32Payload; > else if (registerFormat == DataFormatCell || registerFormat == DataFormatStorage) > spillAction = StorePtr; >- else { >+ else if (registerFormat == DataFormatInt48 || registerFormat == DataFormatStrictInt48) { >+ spillAction = Store64; >+ } else { > ASSERT(registerFormat & DataFormatJS); > spillAction = Store64; > } >@@ -386,6 +388,40 @@ SilentRegisterSavePlan SpeculativeJIT::s > } else if (registerFormat == DataFormatStorage) { > ASSERT(info.gpr() == source); > fillAction = LoadPtr; >+ } else if (registerFormat == DataFormatInt48) { >+ if (node->hasConstant()) >+ fillAction = SetInt48Constant; >+ else if (isJSInt32(info.spillFormat()) || info.spillFormat() == DataFormatJS) >+ fillAction = Load32PayloadConvertToInt48; >+ else if (info.spillFormat() == DataFormatInt48) >+ fillAction = Load64; >+ else if (info.spillFormat() == DataFormatStrictInt48) >+ fillAction = Load64ShiftLeft16; >+ else if (info.spillFormat() == DataFormatNone) >+ fillAction = Load64; >+ else { >+ // Should never happen. Anything that qualifies as an int32 will never >+ // be turned into a cell (immediate spec fail) or a double (to-double >+ // conversions involve a separate node). >+ RELEASE_ASSERT_NOT_REACHED(); >+ } >+ } else if (registerFormat == DataFormatStrictInt48) >+ if (node->hasConstant()) >+ fillAction = SetStrictInt48Constant; >+ else if (isJSInt32(info.spillFormat()) || info.spillFormat() == DataFormatJS) >+ fillAction = Load32PayloadSignExtend; >+ else if (info.spillFormat() == DataFormatInt48) >+ fillAction = Load64ShiftRight16; >+ else if (info.spillFormat() == DataFormatStrictInt48) >+ fillAction = Load64; >+ else if (info.spillFormat() == DataFormatNone) >+ fillAction = Load64; >+ else { >+ // Should never happen. Anything that qualifies as an int32 will never >+ // be turned into a cell (immediate spec fail) or a double (to-double >+ // conversions involve a separate node). >+ RELEASE_ASSERT_NOT_REACHED(); >+ } > } else { > ASSERT(registerFormat & DataFormatJS); > #if USE(JSVALUE64) >@@ -393,7 +429,6 @@ SilentRegisterSavePlan SpeculativeJIT::s > if (node->hasConstant()) { > if (valueOfJSConstant(node).isCell()) > fillAction = SetTrustedJSConstant; >- else > fillAction = SetJSConstant; > } else if (info.spillFormat() == DataFormatInt32) { > ASSERT(registerFormat == DataFormatJSInt32); >@@ -512,6 +547,12 @@ void SpeculativeJIT::silentFill(const Si > case SetInt32Constant: > m_jit.move(Imm32(valueOfInt32Constant(plan.node())), plan.gpr()); > break; >+ case SetInt48Constant: >+ m_jit.move(Imm64(valueOfJSConstant(plan.node()).asMachineInt() << 16), plan.gpr()); >+ break; >+ case SetStrictInt48Constant: >+ m_jit.move(Imm64(valueOfJSConstant(plan.node()).asMachineInt()), plan.gpr()); >+ break; > case SetBooleanConstant: > m_jit.move(TrustedImm32(valueOfBooleanConstant(plan.node())), plan.gpr()); > break; >@@ -533,6 +574,15 @@ void SpeculativeJIT::silentFill(const Si > m_jit.load32(JITCompiler::payloadFor(plan.node()->virtualRegister()), plan.gpr()); > m_jit.or64(GPRInfo::tagTypeNumberRegister, plan.gpr()); > break; >+ case Load32PayloadConvertToInt48: >+ m_jit.load32(JITCompiler::payloadFor(plan.node()->virtualRegister()), plan.gpr()); >+ m_jit.signExtend32ToPtr(plan.gpr(), plan.gpr()); >+ m_jit.lshift64(TrustedImm32(16), plan.gpr()); >+ break; >+ case Load32PayloadSignExtend: >+ m_jit.load32(JITCompiler::payloadFor(plan.node()->virtualRegister()), plan.gpr()); >+ m_jit.signExtend32ToPtr(plan.gpr(), plan.gpr()); >+ break; > case LoadDoubleBoxDouble: > m_jit.load64(JITCompiler::addressFor(plan.node()->virtualRegister()), plan.gpr()); > m_jit.sub64(GPRInfo::tagTypeNumberRegister, plan.gpr()); >@@ -574,6 +624,14 @@ void SpeculativeJIT::silentFill(const Si > case Load64: > m_jit.load64(JITCompiler::addressFor(plan.node()->virtualRegister()), plan.gpr()); > break; >+ case Load64ShiftRight16: >+ m_jit.load64(JITCompiler::addressFor(plan.node()->virtualRegister()), plan.gpr()); >+ m_jit.rshift64(TrustedImm32(16), plan.gpr()); >+ break; >+ case Load64ShiftLeft16: >+ m_jit.load64(JITCompiler::addressFor(plan.node()->virtualRegister()), plan.gpr()); >+ m_jit.lshift64(TrustedImm32(16), plan.gpr()); >+ break; > #endif > case LoadDouble: > m_jit.loadDouble(JITCompiler::addressFor(plan.node()->virtualRegister()), plan.fpr()); >@@ -1416,6 +1474,10 @@ bool SpeculativeJIT::compilePeepHoleBran > > if (node->isBinaryUseKind(Int32Use)) > compilePeepHoleInt32Branch(node, branchNode, condition); >+#if USE(JSVALUE64) >+ else if (node->isBinaryUseKind(MachineIntUse)) >+ compilePeepHoleInt48Branch(node, branchNode, condition); >+#endif // USE(JSVALUE64) > else if (node->isBinaryUseKind(NumberUse)) > compilePeepHoleDoubleBranch(node, branchNode, doubleCondition); > else if (node->op() == CompareEq) { >@@ -2149,6 +2211,18 @@ void SpeculativeJIT::compileValueToInt32 > int32Result(result.gpr(), node, op1.format()); > return; > } >+ >+#if USE(JSVALUE64) >+ case MachineIntUse: { >+ SpeculateStrictInt48Operand op1(this, node->child1()); >+ GPRTemporary result(this, Reuse, op1); >+ GPRReg op1GPR = op1.gpr(); >+ GPRReg resultGPR = result.gpr(); >+ m_jit.zeroExtend32ToPtr(op1GPR, resultGPR); >+ int32Result(resultGPR, node, DataFormatInt32); >+ return; >+ } >+#endif // USE(JSVALUE64) > > case NumberUse: > case NotCellUse: { >@@ -2549,6 +2623,12 @@ void SpeculativeJIT::compileGetByValOnIn > return; > } > >+ if (node->shouldSpeculateMachineInt()) { >+ m_jit.zeroExtend32ToPtr(resultReg); >+ strictInt48Result(resultReg, node); >+ return; >+ } >+ > FPRTemporary fresult(this); > m_jit.convertInt32ToDouble(resultReg, fresult.fpr()); > JITCompiler::Jump positive = m_jit.branch32(MacroAssembler::GreaterThanOrEqual, resultReg, TrustedImm32(0)); >@@ -2602,6 +2682,28 @@ void SpeculativeJIT::compilePutByValForI > break; > } > >+ case MachineIntUse: { >+ SpeculateStrictInt48Operand valueOp(this, valueUse); >+ GPRTemporary scratch(this); >+ m_jit.move(valueOp.gpr(), scratchReg); >+ if (isClamped(type)) { >+ ASSERT(elementSize(type) == 1); >+ MacroAssembler::Jump inBounds = jit.branch64( >+ MacroAssembler::BelowOrEqual, result, JITCompiler::TrustedImm64(0xff)); >+ MacroAssembler::Jump tooBig = jit.branch64( >+ MacroAssembler::GreaterThan, result, JITCompiler::TrustedImm64(0xff)); >+ jit.xorPtr(result, result); >+ MacroAssembler::Jump clamped = jit.jump(); >+ tooBig.link(&jit); >+ jit.move(JITCompiler::TrustedImm32(255), result); >+ clamped.link(&jit); >+ inBounds.link(&jit); >+ } >+ value.adopt(scratch); >+ valueGPR = scratchReg; >+ break; >+ } >+ > case NumberUse: { > if (isClamped(type)) { > ASSERT(elementSize(type) == 1); >@@ -2915,6 +3017,32 @@ void SpeculativeJIT::compileAdd(Node* no > int32Result(gprResult, node); > return; > } >+ >+#if USE(JSVALUE64) >+ case MachineIntUse: { >+ // Will we need an overflow check? If we can prove that neither input can be >+ // Int48 then the overflow check will not be necessary. >+ if (!m_state.forNode(node->child1()).couldBeType(SpecInt48) >+ && !m_state.forNode(node->child2()).couldBeType(SpecInt48)) { >+ SpeculateWhicheverInt48Operand op1(this, node->child1()); >+ SpeculateWhicheverInt48Operand op2(this, node->child2(), op1); >+ GPRTemporary result(this); >+ m_jit.add64(op1.gpr(), op2.gpr(), result.gpr()); >+ int48Result(result.gpr(), node, op1.format()); >+ return; >+ } >+ >+ SpeculateInt48Operand op1(this, node->child1()); >+ SpeculateInt48Operand op2(this, node->child2()); >+ GPRTemporary result(this, op1, op2); >+ m_jit.move(op1.gpr(), result.gpr()); >+ speculationCheck( >+ Int48Overflow, JSValueRegs(), 0, >+ m_jit.branchAdd64(MacroAssembler::Overflow, op2.gpr(), result.gpr())); >+ int48Result(result.gpr(), node); >+ return; >+ } >+#endif // USE(JSVALUE64) > > case NumberUse: { > SpeculateDoubleOperand op1(this, node->child1()); >@@ -3060,6 +3188,32 @@ void SpeculativeJIT::compileArithSub(Nod > return; > } > >+#if USE(JSVALUE64) >+ case MachineIntUse: { >+ // Will we need an overflow check? If we can prove that neither input can be >+ // Int48 then the overflow check will not be necessary. >+ if (!m_state.forNode(node->child1()).couldBeType(SpecInt48) >+ && !m_state.forNode(node->child2()).couldBeType(SpecInt48)) { >+ SpeculateWhicheverInt48Operand op1(this, node->child1()); >+ SpeculateWhicheverInt48Operand op2(this, node->child2(), op1); >+ GPRTemporary result(this); >+ m_jit.sub64(op1.gpr(), op2.gpr(), result.gpr()); >+ int48Result(result.gpr(), node, op1.format()); >+ return; >+ } >+ >+ SpeculateInt48Operand op1(this, node->child1()); >+ SpeculateInt48Operand op2(this, node->child2()); >+ GPRTemporary result(this, op1, op2); >+ m_jit.move(op1.gpr(), result.gpr()); >+ speculationCheck( >+ Int48Overflow, JSValueRegs(), 0, >+ m_jit.branchSub64(MacroAssembler::Overflow, op2.gpr(), result.gpr())); >+ int48Result(result.gpr(), node); >+ return; >+ } >+#endif // USE(JSVALUE64) >+ > case NumberUse: { > SpeculateDoubleOperand op1(this, node->child1()); > SpeculateDoubleOperand op2(this, node->child2()); >@@ -3088,6 +3242,9 @@ void SpeculativeJIT::compileArithNegate( > > m_jit.move(op1.gpr(), result.gpr()); > >+ // Note: there is no notion of being not used as a number, but someone >+ // caring about negative zero. >+ > if (bytecodeCanTruncateInteger(node->arithNodeFlags())) > m_jit.neg32(result.gpr()); > else { >@@ -3099,6 +3256,42 @@ void SpeculativeJIT::compileArithNegate( > int32Result(result.gpr(), node); > return; > } >+ >+#if USE(JSVALUE64) >+ case MachineIntUse: { >+ if (!m_state.forNode(node->child1()).couldBeType(SpecInt48)) { >+ SpeculateWhicheverInt48Operand op1(this, node->child1()); >+ GPRTemporary result(this); >+ GPRReg op1GPR = op1.gpr(); >+ GPRReg resultGPR = result.gpr(); >+ m_jit.move(op1GPR, resultGPR); >+ m_jit.neg64(resultGPR); >+ if (!bytecodeCanIgnoreNegativeZero(node->arithNodeFlags())) { >+ speculationCheck( >+ NegativeZero, JSValueRegs(), 0, >+ m_jit.branchTest64(MacroAssembler::Zero, resultGPR)); >+ } >+ int48Result(resultGPR, node, op1.format()); >+ return; >+ } >+ >+ SpeculateInt48Operand op1(this, node->child1()); >+ GPRTemporary result(this); >+ GPRReg op1GPR = op1.gpr(); >+ GPRReg resultGPR = result.gpr(); >+ m_jit.move(op1GPR, resultGPR); >+ speculationCheck( >+ Int48Overflow, JSValueRegs(), 0, >+ m_jit.branchNeg64(MacroAssembler::Overflow, resultGPR)); >+ if (!bytecodeCanIgnoreNegativeZero(node->arithNodeFlags())) { >+ speculationCheck( >+ NegativeZero, JSValueRegs(), 0, >+ m_jit.branchTest64(MacroAssembler::Zero, resultGPR)); >+ } >+ int48Result(resultGPR, node); >+ return; >+ } >+#endif // USE(JSVALUE64) > > case NumberUse: { > SpeculateDoubleOperand op1(this, node->child1()); >@@ -3164,6 +3357,61 @@ void SpeculativeJIT::compileArithMul(Nod > int32Result(result.gpr(), node); > return; > } >+ >+#if USE(JSVALUE64) >+ case MachineIntUse: { >+ // This is super clever. We want to do an int48 multiplication and check the >+ // int48 overflow bit. There is no direct hardware support for this, but we do >+ // have the ability to do an int64 multiplication and check the int64 overflow >+ // bit. We leverage that. Consider that a, b are int48 numbers inside int64 >+ // registers, with the high 16 bits being sign-extended. We can do: >+ // >+ // (a * (b << 16)) >+ // >+ // This will give us a left-shifted int48 (value is in high 48 bits, low 16 >+ // bits are zero) plus the int48 overflow bit. I.e. whether this 64-bit >+ // multiplication overflows is identical to whether the 'a * b' 48-bit >+ // multiplication overflows. >+ // >+ // In our nomenclature, this is: >+ // >+ // strictInt48(a) * int48(b) => int48 >+ // >+ // That is "strictInt48" means unshifted and "int48" means left-shifted by 16 >+ // bits. >+ // >+ // We don't care which of op1 or op2 serves as the left-shifted operand, so >+ // we just do whatever is more convenient for op1 and have op2 do the >+ // opposite. This ensures that we do at most one shift. >+ >+ SpeculateWhicheverInt48Operand op1(this, node->child1()); >+ SpeculateWhicheverInt48Operand op2(this, node->child2(), OppositeShift, op1); >+ GPRTemporary result(this); >+ >+ GPRReg op1GPR = op1.gpr(); >+ GPRReg op2GPR = op2.gpr(); >+ GPRReg resultGPR = result.gpr(); >+ >+ speculationCheck( >+ Int48Overflow, JSValueRegs(), 0, >+ m_jit.branchMul64(MacroAssembler::Overflow, op1GPR, op2GPR, resultGPR)); >+ >+ if (!bytecodeCanIgnoreNegativeZero(node->arithNodeFlags())) { >+ MacroAssembler::Jump resultNonZero = m_jit.branchTest64( >+ MacroAssembler::NonZero, resultGPR); >+ speculationCheck( >+ NegativeZero, JSValueRegs(), 0, >+ m_jit.branch64(MacroAssembler::LessThan, op1GPR, TrustedImm64(0))); >+ speculationCheck( >+ NegativeZero, JSValueRegs(), 0, >+ m_jit.branch64(MacroAssembler::LessThan, op2GPR, TrustedImm64(0))); >+ resultNonZero.link(&m_jit); >+ } >+ >+ int48Result(resultGPR, node); >+ return; >+ } >+#endif // USE(JSVALUE64) > > case NumberUse: { > SpeculateDoubleOperand op1(this, node->child1()); >@@ -3587,7 +3835,14 @@ bool SpeculativeJIT::compare(Node* node, > compileInt32Compare(node, condition); > return false; > } >- >+ >+#if USE(JSVALUE64) >+ if (node->isBinaryUseKind(MachineIntUse)) { >+ compileInt48Compare(node, condition); >+ return false; >+ } >+#endif // USE(JSVALUE64) >+ > if (node->isBinaryUseKind(NumberUse)) { > compileDoubleCompare(node, doubleCondition); > return false; >@@ -3737,6 +3992,23 @@ bool SpeculativeJIT::compileStrictEq(Nod > compileInt32Compare(node, MacroAssembler::Equal); > return false; > } >+ >+#if USE(JSVALUE64) >+ case MachineIntUse: { >+ unsigned branchIndexInBlock = detectPeepHoleBranch(); >+ if (branchIndexInBlock != UINT_MAX) { >+ Node* branchNode = m_block->at(branchIndexInBlock); >+ compilePeepHoleInt48Branch(node, branchNode, MacroAssembler::Equal); >+ use(node->child1()); >+ use(node->child2()); >+ m_indexInBlock = branchIndexInBlock; >+ m_currentNode = branchNode; >+ return true; >+ } >+ compileInt48Compare(node, MacroAssembler::Equal); >+ return false; >+ } >+#endif // USE(JSVALUE64) > > case NumberUse: { > unsigned branchIndexInBlock = detectPeepHoleBranch(); >Index: Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h (revision 155820) >+++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h (working copy) >@@ -159,9 +159,7 @@ public: > // and its machine registers may be reused. > bool canReuse(Node* node) > { >- VirtualRegister virtualRegister = node->virtualRegister(); >- GenerationInfo& info = generationInfoFromVirtualRegister(virtualRegister); >- return info.canReuse(); >+ return generationInfo(node).canReuse(); > } > bool canReuse(Edge nodeUse) > { >@@ -237,15 +235,11 @@ public: > // avoid spilling values we will need immediately). > bool isFilled(Node* node) > { >- VirtualRegister virtualRegister = node->virtualRegister(); >- GenerationInfo& info = generationInfoFromVirtualRegister(virtualRegister); >- return info.registerFormat() != DataFormatNone; >+ return generationInfo(node).registerFormat() != DataFormatNone; > } > bool isFilledDouble(Node* node) > { >- VirtualRegister virtualRegister = node->virtualRegister(); >- GenerationInfo& info = generationInfoFromVirtualRegister(virtualRegister); >- return info.registerFormat() == DataFormatDouble; >+ return generationInfo(node).registerFormat() == DataFormatDouble; > } > > // Called on an operand once it has been consumed by a parent node. >@@ -253,8 +247,7 @@ public: > { > if (!node->hasResult()) > return; >- VirtualRegister virtualRegister = node->virtualRegister(); >- GenerationInfo& info = generationInfoFromVirtualRegister(virtualRegister); >+ GenerationInfo& info = generationInfo(node); > > // use() returns true when the value becomes dead, and any > // associated resources may be freed. >@@ -333,6 +326,7 @@ public: > // machine registers, implicitly generating speculation checks as needed. > GPRReg fillSpeculateInt32(Edge, DataFormat& returnFormat); > GPRReg fillSpeculateInt32Strict(Edge); >+ GPRReg fillSpeculateInt48(Edge, DataFormat desiredFormat); > FPRReg fillSpeculateDouble(Edge); > GPRReg fillSpeculateCell(Edge); > GPRReg fillSpeculateBoolean(Edge); >@@ -468,6 +462,8 @@ public: > { > return boxDouble(fpr, allocate()); > } >+ >+ void boxInt48(GPRReg sourceGPR, GPRReg targetGPR, DataFormat format); > #elif USE(JSVALUE32_64) > void boxDouble(FPRReg fpr, GPRReg tagGPR, GPRReg payloadGPR) > { >@@ -559,8 +555,8 @@ public: > } > } > >- bool isKnownInteger(Node* node) { return !(m_state.forNode(node).m_type & ~SpecInt32); } >- bool isKnownCell(Node* node) { return !(m_state.forNode(node).m_type & ~SpecCell); } >+ bool isKnownInteger(Node* node) { return !m_state.forNode(node).isType(SpecInt32); } >+ bool isKnownCell(Node* node) { return !m_state.forNode(node).isType(SpecCell); } > > bool isKnownNotInteger(Node* node) { return !(m_state.forNode(node).m_type & SpecInt32); } > bool isKnownNotNumber(Node* node) { return !(m_state.forNode(node).m_type & SpecNumber); } >@@ -816,6 +812,25 @@ public: > { > int32Result(reg, node, DataFormatInt32, mode); > } >+ void int48Result(GPRReg reg, Node* node, DataFormat format = DataFormatInt48, UseChildrenMode mode = CallUseChildren) >+ { >+ if (mode == CallUseChildren) >+ useChildren(node); >+ >+ VirtualRegister virtualRegister = node->virtualRegister(); >+ GenerationInfo& info = generationInfoFromVirtualRegister(virtualRegister); >+ >+ m_gprs.retain(reg, virtualRegister, SpillOrderJS); >+ info.initInt48(node, node->refCount(), reg, format); >+ } >+ void int48Result(GPRReg reg, Node* node, UseChildrenMode mode = CallUseChildren) >+ { >+ int48Result(reg, node, DataFormatInt48, mode); >+ } >+ void strictInt48Result(GPRReg reg, Node* node, UseChildrenMode mode = CallUseChildren) >+ { >+ int48Result(reg, node, DataFormatStrictInt48, mode); >+ } > void noResult(Node* node, UseChildrenMode mode = CallUseChildren) > { > if (mode == UseChildrenCalledExplicitly) >@@ -900,7 +915,7 @@ public: > void initConstantInfo(Node* node) > { > ASSERT(isInt32Constant(node) || isNumberConstant(node) || isJSConstant(node)); >- generationInfoFromVirtualRegister(node->virtualRegister()).initConstant(node, node->refCount()); >+ generationInfo(node).initConstant(node, node->refCount()); > } > > // These methods add calls to C++ helper functions. >@@ -1889,10 +1904,16 @@ public: > if (isInt32Constant(node)) > return true; > >- VirtualRegister virtualRegister = node->virtualRegister(); >- GenerationInfo& info = generationInfoFromVirtualRegister(virtualRegister); >- >- return info.isJSInt32(); >+ return generationInfo(node).isJSInt32(); >+ } >+ >+ bool betterUseStrictInt48(Node* node) >+ { >+ return !generationInfo(node->child1()).isInt48(); >+ } >+ bool betterUseStrictInt48(Edge edge) >+ { >+ return betterUseStrictInt48(edge.node()); > } > > bool compare(Node*, MacroAssembler::RelationalCondition, MacroAssembler::DoubleCondition, S_DFGOperation_EJJ); >@@ -2195,6 +2216,16 @@ public: > { > return m_generationInfo[operandToLocal(virtualRegister)]; > } >+ >+ GenerationInfo& generationInfo(Node* node) >+ { >+ return generationInfoFromVirtualRegister(node->virtualRegister()); >+ } >+ >+ GeenrationInfo& generationInfo(Edge edge) >+ { >+ return generationInfo(edge.node()); >+ } > > // The JIT, while also provides MacroAssembler functionality. > JITCompiler& m_jit; >@@ -2676,6 +2707,179 @@ private: > GPRReg m_gprOrInvalid; > }; > >+// Gives you a canonical Int48 (i.e. it's left-shifted by 16, low bits zero). >+class SpeculateInt48Operand { >+public: >+ explicit SpeculateInt48Operand(SpeculativeJIT* jit, Edge edge, OperandSpeculationMode mode = AutomaticOperandSpeculation) >+ : m_jit(jit) >+ , m_edge(edge) >+ , m_gprOrInvalid(InvalidGPRReg) >+ { >+ ASSERT_UNUSED(mode, mode == ManualOperandSpeculation || edge.useKind() == MachineIntUse); >+ if (jit->isFilled(node())) >+ gpr(); >+ } >+ >+ ~SpeculateInt48Operand() >+ { >+ ASSERT(m_gprOrInvalid != InvalidGPRReg); >+ m_jit->unlock(m_gprOrInvalid); >+ } >+ >+ Edge edge() const >+ { >+ return m_edge; >+ } >+ >+ Node* node() const >+ { >+ return edge().node(); >+ } >+ >+ GPRReg gpr() >+ { >+ if (m_gprOrInvalid == InvalidGPRReg) >+ m_gprOrInvalid = m_jit->fillSpeculateInt48(edge(), DataFormatInt48); >+ return m_gprOrInvalid; >+ } >+ >+ void use() >+ { >+ m_jit->use(node()); >+ } >+ >+private: >+ SpeculativeJIT* m_jit; >+ Edge m_edge; >+ GPRReg m_gprOrInvalid; >+}; >+ >+// Gives you a strict Int48 (i.e. the payload is in the low 48 bits, high 16 bits are sign-extended). >+class SpeculateStrictInt48Operand { >+public: >+ explicit SpeculateStrictInt48Operand(SpeculativeJIT* jit, Edge edge, OperandSpeculationMode mode = AutomaticOperandSpeculation) >+ : m_jit(jit) >+ , m_edge(edge) >+ , m_gprOrInvalid(InvalidGPRReg) >+ { >+ ASSERT_UNUSED(mode, mode == ManualOperandSpeculation || edge.useKind() == MachineIntUse); >+ if (jit->isFilled(node())) >+ gpr(); >+ } >+ >+ ~SpeculateInt48Operand() >+ { >+ ASSERT(m_gprOrInvalid != InvalidGPRReg); >+ m_jit->unlock(m_gprOrInvalid); >+ } >+ >+ Edge edge() const >+ { >+ return m_edge; >+ } >+ >+ Node* node() const >+ { >+ return edge().node(); >+ } >+ >+ GPRReg gpr() >+ { >+ if (m_gprOrInvalid == InvalidGPRReg) >+ m_gprOrInvalid = m_jit->fillSpeculateStrictInt48(edge(), DataFormatStrictInt48); >+ return m_gprOrInvalid; >+ } >+ >+ void use() >+ { >+ m_jit->use(node()); >+ } >+ >+private: >+ SpeculativeJIT* m_jit; >+ Edge m_edge; >+ GPRReg m_gprOrInvalid; >+}; >+ >+enum OppositeShiftTag { OppositeShift }; >+ >+class SpeculateWhicheverInt48Operand { >+public: >+ explicit SpeculateWhicheverInt48Operand(SpculativeJIT* jit, Edge edge, OperandSpeculationMode = AutomaticOperandSpeculation) >+ : m_jit(jit) >+ , m_edge(edge) >+ , m_gprOrInvalid(InvalidGPRReg) >+ , m_strict(jit->betterUseStrictInt48(edge)) >+ { >+ ASSERT_UNUSED(mode, mode == ManualOperandSpeculation || edge.useKind() == MachineIntUse); >+ if (jit->isFilled(node())) >+ gpr(); >+ } >+ >+ explicit SpeculateWhicheverInt48Operand(SpculativeJIT* jit, Edge edge, const SpeculateWhicheverInt48Operand& other, OperandSpeculationMode = AutomaticOperandSpeculation) >+ : m_jit(jit) >+ , m_edge(edge) >+ , m_gprOrInvalid(InvalidGPRReg) >+ , m_strict(other.m_strict) >+ { >+ ASSERT_UNUSED(mode, mode == ManualOperandSpeculation || edge.useKind() == MachineIntUse); >+ if (jit->isFilled(node())) >+ gpr(); >+ } >+ >+ explicit SpeculateWhicheverInt48Operand(SpculativeJIT* jit, Edge edge, OppositeShiftTag, const SpeculateWhicheverInt48Operand& other, OperandSpeculationMode = AutomaticOperandSpeculation) >+ : m_jit(jit) >+ , m_edge(edge) >+ , m_gprOrInvalid(InvalidGPRReg) >+ , m_strict(!other.m_strict) >+ { >+ ASSERT_UNUSED(mode, mode == ManualOperandSpeculation || edge.useKind() == MachineIntUse); >+ if (jit->isFilled(node())) >+ gpr(); >+ } >+ >+ ~SpeculateWhicheverInt48Operand() >+ { >+ ASSERT(m_gprOrInvalid != InvalidGPRReg); >+ m_jit->unlock(m_gprOrInvalid); >+ } >+ >+ Edge edge() const >+ { >+ return m_edge; >+ } >+ >+ Node* node() const >+ { >+ return edge().node(); >+ } >+ >+ GPRReg gpr() >+ { >+ if (m_gprOrInvalid == InvalidGPRReg) { >+ m_gprOrInvalid = m_jit->fillSpeculateInt48( >+ edge(), m_struct ? DataFormatStrictInt48 : DataFormatInt48); >+ } >+ return m_gprOrInvalid; >+ } >+ >+ void use() >+ { >+ m_jit->use(node()); >+ } >+ >+ DataFormat format() const >+ { >+ return m_strict ? DataFormatStrictInt48 : DataFormatInt48; >+ } >+ >+private: >+ SpeculateJIT* m_jit; >+ Edge m_edge; >+ GPRReg m_gprOrInvalid; >+ bool m_strict; >+}; >+ > class SpeculateDoubleOperand { > public: > explicit SpeculateDoubleOperand(SpeculativeJIT* jit, Edge edge, OperandSpeculationMode mode = AutomaticOperandSpeculation) >Index: Source/JavaScriptCore/dfg/DFGUseKind.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGUseKind.cpp (revision 155820) >+++ Source/JavaScriptCore/dfg/DFGUseKind.cpp (working copy) >@@ -44,6 +44,9 @@ void printInternal(PrintStream& out, Use > case KnownInt32Use: > out.print("KnownInt32"); > break; >+ case MachineIntUse: >+ out.print("Int48"); >+ break; > case RealNumberUse: > out.print("RealNumber"); > break; >Index: Source/JavaScriptCore/dfg/DFGUseKind.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGUseKind.h (revision 155820) >+++ Source/JavaScriptCore/dfg/DFGUseKind.h (working copy) >@@ -39,6 +39,7 @@ enum UseKind { > UntypedUse, > Int32Use, > KnownInt32Use, >+ MachineIntUse, > RealNumberUse, > NumberUse, > KnownNumberUse, >@@ -66,6 +67,8 @@ ALWAYS_INLINE SpeculatedType typeFilterF > case Int32Use: > case KnownInt32Use: > return SpecInt32; >+ case MachineIntUse: >+ return SpecMachineInt; > case RealNumberUse: > return SpecRealNumber; > case NumberUse: >@@ -125,6 +128,7 @@ ALWAYS_INLINE bool isNumerical(UseKind k > switch (kind) { > case Int32Use: > case KnownInt32Use: >+ case MachineIntUse: > case RealNumberUse: > case NumberUse: > case KnownNumberUse: >Index: Source/JavaScriptCore/dfg/DFGValueSource.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGValueSource.cpp (revision 155820) >+++ Source/JavaScriptCore/dfg/DFGValueSource.cpp (working copy) >@@ -45,6 +45,9 @@ void ValueSource::dump(PrintStream& out) > case Int32InJSStack: > out.print("Int32"); > break; >+ case Int48InJSStack: >+ out.print("Int48"); >+ break; > case CellInJSStack: > out.print("Cell"); > break; >Index: Source/JavaScriptCore/dfg/DFGValueSource.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGValueSource.h (revision 155820) >+++ Source/JavaScriptCore/dfg/DFGValueSource.h (working copy) >@@ -43,6 +43,7 @@ enum ValueSourceKind { > SourceNotSet, > ValueInJSStack, > Int32InJSStack, >+ Int48InJSStack, > CellInJSStack, > BooleanInJSStack, > DoubleInJSStack, >@@ -56,6 +57,8 @@ static inline ValueSourceKind dataFormat > switch (dataFormat) { > case DataFormatInt32: > return Int32InJSStack; >+ case DataFormatInt48: >+ return Int48InJSStack; > case DataFormatDouble: > return DoubleInJSStack; > case DataFormatBoolean: >@@ -79,6 +82,8 @@ static inline DataFormat valueSourceKind > return DataFormatJS; > case Int32InJSStack: > return DataFormatInt32; >+ case Int48InJSStack: >+ return DataFormatInt48; > case CellInJSStack: > return DataFormatCell; > case BooleanInJSStack: >@@ -139,6 +144,8 @@ public: > return ValueSource(DoubleInJSStack); > case FlushedInt32: > return ValueSource(Int32InJSStack); >+ case FlushedInt48: >+ return ValueSource(Int48InJSStack); > case FlushedCell: > return ValueSource(CellInJSStack); > case FlushedBoolean: >@@ -181,6 +188,9 @@ public: > case Int32InJSStack: > return ValueRecovery::alreadyInJSStackAsUnboxedInt32(); > >+ case Int48InJSStack: >+ return ValueRecovery::alreadyInJSStackAsUnboxedInt48(); >+ > case CellInJSStack: > return ValueRecovery::alreadyInJSStackAsUnboxedCell(); > >Index: Source/JavaScriptCore/dfg/DFGVariableAccessData.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGVariableAccessData.h (revision 155820) >+++ Source/JavaScriptCore/dfg/DFGVariableAccessData.h (working copy) >@@ -333,6 +333,9 @@ public: > if (isInt32Speculation(prediction)) > return FlushedInt32; > >+ if (enableInt48() && !operandIsArgument(m_local) && isMachineIntSpeculation(prediction)) >+ return FlushedInt48; >+ > if (isCellSpeculation(prediction)) > return FlushedCell; > >Index: Source/JavaScriptCore/interpreter/Register.h >=================================================================== >--- Source/JavaScriptCore/interpreter/Register.h (revision 155820) >+++ Source/JavaScriptCore/interpreter/Register.h (working copy) >@@ -73,6 +73,7 @@ namespace JSC { > Instruction* vPC() const; > InlineCallFrame* asInlineCallFrame() const; > int32_t unboxedInt32() const; >+ int64_t unboxedInt48() const; > bool unboxedBoolean() const; > double unboxedDouble() const; > JSCell* unboxedCell() const; >@@ -98,6 +99,7 @@ namespace JSC { > InlineCallFrame* inlineCallFrame; > EncodedValueDescriptor encodedValue; > double number; >+ int64_t integer; > } u; > }; > >@@ -185,6 +187,11 @@ namespace JSC { > return payload(); > } > >+ ALWAYS_INLINE int64_t Register::unboxedInt48() const >+ { >+ return u.integer >> 16; >+ } >+ > ALWAYS_INLINE bool Register::unboxedBoolean() const > { > return !!payload(); >Index: Source/JavaScriptCore/runtime/Arguments.cpp >=================================================================== >--- Source/JavaScriptCore/runtime/Arguments.cpp (revision 155820) >+++ Source/JavaScriptCore/runtime/Arguments.cpp (working copy) >@@ -383,6 +383,9 @@ void Arguments::tearOffForInlineCallFram > case AlreadyInJSStackAsUnboxedInt32: > value = jsNumber(location->unboxedInt32()); > break; >+ case AlreadyInJSStackAsUnboxedInt48: >+ value = jsNumber(location->unboxedInt48()); >+ break; > case AlreadyInJSStackAsUnboxedCell: > value = location->unboxedCell(); > break; >Index: Source/JavaScriptCore/runtime/JSCJSValue.h >=================================================================== >--- Source/JavaScriptCore/runtime/JSCJSValue.h (revision 155820) >+++ Source/JavaScriptCore/runtime/JSCJSValue.h (working copy) >@@ -191,6 +191,7 @@ public: > > int32_t asInt32() const; > uint32_t asUInt32() const; >+ int64_t asMachineInt() const; > double asDouble() const; > bool asBoolean() const; > double asNumber() const; >@@ -202,6 +203,7 @@ public: > bool isNull() const; > bool isUndefinedOrNull() const; > bool isBoolean() const; >+ bool isMachineInt() const; > bool isNumber() const; > bool isString() const; > bool isPrimitive() const; >Index: Source/JavaScriptCore/runtime/JSCJSValueInlines.h >=================================================================== >--- Source/JavaScriptCore/runtime/JSCJSValueInlines.h (revision 155820) >+++ Source/JavaScriptCore/runtime/JSCJSValueInlines.h (working copy) >@@ -494,6 +494,35 @@ ALWAYS_INLINE JSCell* JSValue::asCell() > > #endif // USE(JSVALUE64) > >+inline bool JSValue::isMachineInt() const >+{ >+ if (isInt32()) >+ return true; >+ if (!isNumber()) >+ return false; >+ double number = asDouble(); >+ if (number != number) >+ return false; >+ int64_t asInt64 = static_cast<int64_t>(number); >+ if (asInt64 != number) >+ return false; >+ if (!asInt64 && std::signbit(number)) >+ return false; >+ if (asInt64 >= (static_cast<int64_t>(1) << 47)) >+ return false; >+ if (asInt64 < -(static_cast<int64_t>(1) << 47)) >+ return false; >+ return true; >+} >+ >+inline int64_t JSValue::asMachineInt() const >+{ >+ ASSERT(isMachineInt()); >+ if (isInt32()) >+ return asInt32(); >+ return static_cast<int64_t>(asDouble()); >+} >+ > inline bool JSValue::isString() const > { > return isCell() && asCell()->isString();
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 121064
:
211385
|
211463
|
211477
|
211507
|
211614
|
211619
|
211655
|
211666
|
211679
|
211723
|
211730
|
211732
|
211735
|
211736
|
211737
|
211842
|
211853
|
211854
|
211866
|
211929