WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
Patch
bug-186177-20181101144756.patch (text/plain), 41.06 KB, created by
Caio Lima
on 2018-11-01 10:47:59 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Caio Lima
Created:
2018-11-01 10:47:59 PDT
Size:
41.06 KB
patch
obsolete
>Subversion Revision: 237645 >diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index b87fe99b9cccfafaee2ac45d7a49320a34c131e2..b59b51f16e47bc663c2a349b830a99eb979b954a 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -1,3 +1,70 @@ >+2018-11-01 Caio Lima <ticaiolima@gmail.com> >+ >+ [BigInt] Add support to BigInt into ValueAdd >+ https://bugs.webkit.org/show_bug.cgi?id=186177 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ We are adding a very primitive specialization case of BigInts into ValueAdd. >+ When compiling a speculated version of this node to BigInt, we are currently >+ calling 'operationAddBigInt', a function that expects only BigInts as >+ parameter and effectly add numbers using JSBigInt::add. To properly >+ speculate BigInt operands, we changed ArithProfile to observe when >+ its result is a BigInt. With this new observation, we are able to identify >+ when ValueAdd results into a String or BigInt. >+ >+ Here are some numbers for this specialization running >+ microbenchmarks: >+ >+ big-int-simple-add 21.5411+-1.1096 ^ 15.3502+-0.7027 ^ definitely 1.4033x faster >+ big-int-add-prediction-propagation 13.7762+-0.5578 ^ 10.8117+-0.5330 ^ definitely 1.2742x faster >+ >+ * bytecode/ArithProfile.cpp: >+ (JSC::ArithProfile::emitObserveResult): >+ (JSC::ArithProfile::shouldEmitSetNonNumeric const): >+ (JSC::ArithProfile::shouldEmitSetBigInt const): >+ (JSC::ArithProfile::emitSetNonNumeric const): >+ (JSC::ArithProfile::emitSetBigInt const): >+ (WTF::printInternal): >+ (JSC::ArithProfile::shouldEmitSetNonNumber const): Deleted. >+ (JSC::ArithProfile::emitSetNonNumber const): Deleted. >+ * bytecode/ArithProfile.h: >+ (JSC::ArithProfile::observedUnaryInt): >+ (JSC::ArithProfile::observedUnaryNumber): >+ (JSC::ArithProfile::observedBinaryIntInt): >+ (JSC::ArithProfile::observedBinaryNumberInt): >+ (JSC::ArithProfile::observedBinaryIntNumber): >+ (JSC::ArithProfile::observedBinaryNumberNumber): >+ (JSC::ArithProfile::didObserveNonInt32 const): >+ (JSC::ArithProfile::didObserveNonNumeric const): >+ (JSC::ArithProfile::didObserveBigInt const): >+ (JSC::ArithProfile::setObservedNonNumeric): >+ (JSC::ArithProfile::setObservedBigInt): >+ (JSC::ArithProfile::observeResult): >+ (JSC::ArithProfile::didObserveNonNumber const): Deleted. >+ (JSC::ArithProfile::setObservedNonNumber): Deleted. >+ * dfg/DFGByteCodeParser.cpp: >+ (JSC::DFG::ByteCodeParser::makeSafe): >+ * dfg/DFGFixupPhase.cpp: >+ (JSC::DFG::FixupPhase::fixupNode): >+ * dfg/DFGNode.h: >+ (JSC::DFG::Node::mayHaveNonNumericResult): >+ (JSC::DFG::Node::mayHaveBigIntResult): >+ (JSC::DFG::Node::mayHaveNonNumberResult): Deleted. >+ * dfg/DFGNodeFlags.cpp: >+ (JSC::DFG::dumpNodeFlags): >+ * dfg/DFGNodeFlags.h: >+ * dfg/DFGOperations.cpp: >+ * dfg/DFGOperations.h: >+ * dfg/DFGPredictionPropagationPhase.cpp: >+ * dfg/DFGSpeculativeJIT.cpp: >+ (JSC::DFG::SpeculativeJIT::compileValueAdd): >+ * ftl/FTLLowerDFGToB3.cpp: >+ (JSC::FTL::DFG::LowerDFGToB3::compileValueAdd): >+ * runtime/CommonSlowPaths.cpp: >+ (JSC::updateArithProfileForUnaryArithOp): >+ (JSC::updateArithProfileForBinaryArithOp): >+ > 2018-10-31 Tadeu Zagallo <tzagallo@apple.com> > > Adjust inlining threshold for new bytecode format >diff --git a/Source/JavaScriptCore/bytecode/ArithProfile.cpp b/Source/JavaScriptCore/bytecode/ArithProfile.cpp >index 1fa7c79894bde96bf431ecc48a8e66be03076186..999933b8c0eced2b721bd810a48311dd416f6af3 100644 >--- a/Source/JavaScriptCore/bytecode/ArithProfile.cpp >+++ b/Source/JavaScriptCore/bytecode/ArithProfile.cpp >@@ -34,17 +34,28 @@ namespace JSC { > #if ENABLE(JIT) > void ArithProfile::emitObserveResult(CCallHelpers& jit, JSValueRegs regs, TagRegistersMode mode) > { >- if (!shouldEmitSetDouble() && !shouldEmitSetNonNumber()) >+ if (!shouldEmitSetDouble() && !shouldEmitSetNonNumeric() && !shouldEmitSetBigInt()) > return; > >- CCallHelpers::Jump isInt32 = jit.branchIfInt32(regs, mode); >+ CCallHelpers::JumpList done; >+ CCallHelpers::JumpList nonNumeric; >+ >+ done.append(jit.branchIfInt32(regs, mode)); > CCallHelpers::Jump notDouble = jit.branchIfNotDoubleKnownNotInt32(regs, mode); > emitSetDouble(jit); >- CCallHelpers::Jump done = jit.jump(); >+ done.append(jit.jump()); >+ > notDouble.link(&jit); >- emitSetNonNumber(jit); >+ >+ nonNumeric.append(jit.branchIfNotCell(regs, mode)); >+ nonNumeric.append(jit.branchIfNotBigInt(regs.payloadGPR())); >+ emitSetBigInt(jit); >+ done.append(jit.jump()); >+ >+ nonNumeric.link(&jit); >+ emitSetNonNumeric(jit); >+ > done.link(&jit); >- isInt32.link(&jit); > } > > bool ArithProfile::shouldEmitSetDouble() const >@@ -59,16 +70,28 @@ void ArithProfile::emitSetDouble(CCallHelpers& jit) const > jit.or32(CCallHelpers::TrustedImm32(ArithProfile::Int32Overflow | ArithProfile::Int52Overflow | ArithProfile::NegZeroDouble | ArithProfile::NonNegZeroDouble), CCallHelpers::AbsoluteAddress(addressOfBits())); > } > >-bool ArithProfile::shouldEmitSetNonNumber() const >+bool ArithProfile::shouldEmitSetNonNumeric() const >+{ >+ uint32_t mask = ArithProfile::NonNumeric; >+ return (m_bits & mask) != mask; >+} >+ >+bool ArithProfile::shouldEmitSetBigInt() const > { >- uint32_t mask = ArithProfile::NonNumber; >+ uint32_t mask = ArithProfile::BigInt; > return (m_bits & mask) != mask; > } > >-void ArithProfile::emitSetNonNumber(CCallHelpers& jit) const >+void ArithProfile::emitSetNonNumeric(CCallHelpers& jit) const > { >- if (shouldEmitSetNonNumber()) >- jit.or32(CCallHelpers::TrustedImm32(ArithProfile::NonNumber), CCallHelpers::AbsoluteAddress(addressOfBits())); >+ if (shouldEmitSetNonNumeric()) >+ jit.or32(CCallHelpers::TrustedImm32(ArithProfile::NonNumeric), CCallHelpers::AbsoluteAddress(addressOfBits())); >+} >+ >+void ArithProfile::emitSetBigInt(CCallHelpers& jit) const >+{ >+ if (shouldEmitSetBigInt()) >+ jit.or32(CCallHelpers::TrustedImm32(ArithProfile::BigInt), CCallHelpers::AbsoluteAddress(addressOfBits())); > } > #endif // ENABLE(JIT) > >@@ -95,8 +118,8 @@ void printInternal(PrintStream& out, const ArithProfile& profile) > out.print(separator, "NonNegZeroDouble"); > separator = "|"; > } >- if (profile.didObserveNonNumber()) { >- out.print(separator, "NonNumber"); >+ if (profile.didObserveNonNumeric()) { >+ out.print(separator, "NonNumeric"); > separator = "|"; > } > if (profile.didObserveInt32Overflow()) { >@@ -107,6 +130,10 @@ void printInternal(PrintStream& out, const ArithProfile& profile) > out.print(separator, "Int52Overflow"); > separator = "|"; > } >+ if (profile.didObserveBigInt()) { >+ out.print(separator, "BigInt"); >+ separator = "|"; >+ } > } > if (profile.tookSpecialFastPath()) > out.print(separator, "Took special fast path."); >diff --git a/Source/JavaScriptCore/bytecode/ArithProfile.h b/Source/JavaScriptCore/bytecode/ArithProfile.h >index 645c258e557be9e47a427afc3a7fac4497e686ab..b1bda5b33434faa64f5b63b7c2a2cb86f4c2631f 100644 >--- a/Source/JavaScriptCore/bytecode/ArithProfile.h >+++ b/Source/JavaScriptCore/bytecode/ArithProfile.h >@@ -68,7 +68,7 @@ private: > > struct ArithProfile { > private: >- static constexpr uint32_t numberOfFlagBits = 5; >+ static constexpr uint32_t numberOfFlagBits = 6; > static constexpr uint32_t rhsResultTypeShift = numberOfFlagBits; > static constexpr uint32_t lhsResultTypeShift = rhsResultTypeShift + ResultType::numBitsNeeded; > static constexpr uint32_t rhsObservedTypeShift = lhsResultTypeShift + ResultType::numBitsNeeded; >@@ -121,21 +121,21 @@ public: > { > constexpr ObservedType observedInt32 { ObservedType().withInt32() }; > constexpr uint32_t bits = observedInt32.bits() << lhsObservedTypeShift; >- static_assert(bits == 0x400000, ""); >+ static_assert(bits == 0x800000, ""); > return fromInt(bits); > } > static constexpr ArithProfile observedUnaryNumber() > { > constexpr ObservedType observedNumber { ObservedType().withNumber() }; > constexpr uint32_t bits = observedNumber.bits() << lhsObservedTypeShift; >- static_assert(bits == 0x800000, ""); >+ static_assert(bits == 0x1000000, ""); > return fromInt(bits); > } > static constexpr ArithProfile observedBinaryIntInt() > { > constexpr ObservedType observedInt32 { ObservedType().withInt32() }; > constexpr uint32_t bits = (observedInt32.bits() << lhsObservedTypeShift) | (observedInt32.bits() << rhsObservedTypeShift); >- static_assert(bits == 0x480000, ""); >+ static_assert(bits == 0x900000, ""); > return fromInt(bits); > } > static constexpr ArithProfile observedBinaryNumberInt() >@@ -143,7 +143,7 @@ public: > constexpr ObservedType observedNumber { ObservedType().withNumber() }; > constexpr ObservedType observedInt32 { ObservedType().withInt32() }; > constexpr uint32_t bits = (observedNumber.bits() << lhsObservedTypeShift) | (observedInt32.bits() << rhsObservedTypeShift); >- static_assert(bits == 0x880000, ""); >+ static_assert(bits == 0x1100000, ""); > return fromInt(bits); > } > static constexpr ArithProfile observedBinaryIntNumber() >@@ -151,23 +151,24 @@ public: > constexpr ObservedType observedNumber { ObservedType().withNumber() }; > constexpr ObservedType observedInt32 { ObservedType().withInt32() }; > constexpr uint32_t bits = (observedInt32.bits() << lhsObservedTypeShift) | (observedNumber.bits() << rhsObservedTypeShift); >- static_assert(bits == 0x500000, ""); >+ static_assert(bits == 0xa00000, ""); > return fromInt(bits); > } > static constexpr ArithProfile observedBinaryNumberNumber() > { > constexpr ObservedType observedNumber { ObservedType().withNumber() }; > constexpr uint32_t bits = (observedNumber.bits() << lhsObservedTypeShift) | (observedNumber.bits() << rhsObservedTypeShift); >- static_assert(bits == 0x900000, ""); >+ static_assert(bits == 0x1200000, ""); > return fromInt(bits); > } > > enum ObservedResults { > NonNegZeroDouble = 1 << 0, > NegZeroDouble = 1 << 1, >- NonNumber = 1 << 2, >+ NonNumeric = 1 << 2, > Int32Overflow = 1 << 3, > Int52Overflow = 1 << 4, >+ BigInt = 1 << 5, > }; > > ResultType lhsResultType() const { return ResultType((m_bits >> lhsResultTypeShift) & resultTypeMask); } >@@ -195,17 +196,19 @@ public: > > bool tookSpecialFastPath() const { return m_bits & specialFastPathBit; } > >- bool didObserveNonInt32() const { return hasBits(NonNegZeroDouble | NegZeroDouble | NonNumber); } >+ bool didObserveNonInt32() const { return hasBits(NonNegZeroDouble | NegZeroDouble | NonNumeric | BigInt); } > bool didObserveDouble() const { return hasBits(NonNegZeroDouble | NegZeroDouble); } > bool didObserveNonNegZeroDouble() const { return hasBits(NonNegZeroDouble); } > bool didObserveNegZeroDouble() const { return hasBits(NegZeroDouble); } >- bool didObserveNonNumber() const { return hasBits(NonNumber); } >+ bool didObserveNonNumeric() const { return hasBits(NonNumeric); } >+ bool didObserveBigInt() const { return hasBits(BigInt); } > bool didObserveInt32Overflow() const { return hasBits(Int32Overflow); } > bool didObserveInt52Overflow() const { return hasBits(Int52Overflow); } > > void setObservedNonNegZeroDouble() { setBit(NonNegZeroDouble); } > void setObservedNegZeroDouble() { setBit(NegZeroDouble); } >- void setObservedNonNumber() { setBit(NonNumber); } >+ void setObservedNonNumeric() { setBit(NonNumeric); } >+ void setObservedBigInt() { setBit(BigInt); } > void setObservedInt32Overflow() { setBit(Int32Overflow); } > void setObservedInt52Overflow() { setBit(Int52Overflow); } > >@@ -219,7 +222,11 @@ public: > m_bits |= Int32Overflow | Int52Overflow | NonNegZeroDouble | NegZeroDouble; > return; > } >- m_bits |= NonNumber; >+ if (value && value.isBigInt()) { >+ m_bits |= BigInt; >+ return; >+ } >+ m_bits |= NonNumeric; > } > > void lhsSawInt32() { setLhsObservedType(lhsObservedType().withInt32()); } >@@ -261,7 +268,7 @@ public: > > #if ENABLE(JIT) > // Sets (Int32Overflow | Int52Overflow | NonNegZeroDouble | NegZeroDouble) if it sees a >- // double. Sets NonNumber if it sees a non-number. >+ // double. Sets NonNumeric if it sees a non-numeric. > void emitObserveResult(CCallHelpers&, JSValueRegs, TagRegistersMode = HaveTagRegisters); > > // Sets (Int32Overflow | Int52Overflow | NonNegZeroDouble | NegZeroDouble). >@@ -269,8 +276,12 @@ public: > void emitSetDouble(CCallHelpers&) const; > > // Sets NonNumber. >- void emitSetNonNumber(CCallHelpers&) const; >- bool shouldEmitSetNonNumber() const; >+ void emitSetNonNumeric(CCallHelpers&) const; >+ bool shouldEmitSetNonNumeric() const; >+ >+ // Sets BigInt >+ void emitSetBigInt(CCallHelpers&) const; >+ bool shouldEmitSetBigInt() const; > #endif // ENABLE(JIT) > > constexpr uint32_t bits() const { return m_bits; } >diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >index 7ded83f04974ae087f16120dd70007fd659269bf..669ad8b9cca657481e382208e45dc132bba3e71a 100644 >--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >@@ -941,8 +941,10 @@ private: > case ValueAdd: > if (arithProfile->didObserveDouble()) > node->mergeFlags(NodeMayHaveDoubleResult); >- if (arithProfile->didObserveNonNumber()) >- node->mergeFlags(NodeMayHaveNonNumberResult); >+ if (arithProfile->didObserveNonNumeric()) >+ node->mergeFlags(NodeMayHaveNonNumericResult); >+ if (arithProfile->didObserveBigInt()) >+ node->mergeFlags(NodeMayHaveBigIntResult); > break; > > case ArithMul: { >@@ -954,8 +956,8 @@ private: > node->mergeFlags(NodeMayNegZeroInBaseline); > if (arithProfile->didObserveDouble()) > node->mergeFlags(NodeMayHaveDoubleResult); >- if (arithProfile->didObserveNonNumber()) >- node->mergeFlags(NodeMayHaveNonNumberResult); >+ if (arithProfile->didObserveNonNumeric()) >+ node->mergeFlags(NodeMayHaveNonNumericResult); > break; > } > case ValueNegate: >@@ -966,11 +968,10 @@ private: > node->mergeFlags(NodeMayNegZeroInBaseline); > if (arithProfile->didObserveInt32Overflow() || m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow)) > node->mergeFlags(NodeMayOverflowInt32InBaseline); >- if (arithProfile->didObserveNonNumber()) { >- // FIXME: We should add support to BigInt into speculation >- // https://bugs.webkit.org/show_bug.cgi?id=182470 >- node->mergeFlags(NodeMayHaveNonNumberResult); >- } >+ if (arithProfile->didObserveNonNumeric()) >+ node->mergeFlags(NodeMayHaveNonNumericResult); >+ if (arithProfile->didObserveBigInt()) >+ node->mergeFlags(NodeMayHaveBigIntResult); > break; > } > >diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >index 91a26dd15393a851ea31b9b76fd573f0acef166b..937b2a1ad55ad4f397fbbf9d1c5dc50b592c27a4 100644 >--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >@@ -287,8 +287,14 @@ private: > } > } > >- fixEdge<UntypedUse>(child1); >- fixEdge<UntypedUse>(child2); >+ if (Node::shouldSpeculateBigInt(child1.node(), child2.node())) { >+ fixEdge<BigIntUse>(child1); >+ fixEdge<BigIntUse>(child2); >+ } else { >+ fixEdge<UntypedUse>(child1); >+ fixEdge<UntypedUse>(child2); >+ } >+ > node->setResult(NodeResultJS); > break; > } >diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h >index 7d58930b5b7c8d15b628f9eebec9847526ea736c..9952806583e727dd7f583578896a4947b6837b16 100644 >--- a/Source/JavaScriptCore/dfg/DFGNode.h >+++ b/Source/JavaScriptCore/dfg/DFGNode.h >@@ -1144,9 +1144,14 @@ public: > return m_flags & NodeMayHaveDoubleResult; > } > >- bool mayHaveNonNumberResult() >+ bool mayHaveNonNumericResult() > { >- return m_flags & NodeMayHaveNonNumberResult; >+ return m_flags & NodeMayHaveNonNumericResult; >+ } >+ >+ bool mayHaveBigIntResult() >+ { >+ return m_flags & NodeMayHaveBigIntResult; > } > > bool hasNewArrayBufferData() >@@ -2869,7 +2874,7 @@ private: > > unsigned m_index { std::numeric_limits<unsigned>::max() }; > unsigned m_op : 10; // real type is NodeType >- unsigned m_flags : 20; >+ unsigned m_flags : 21; > // The virtual register number (spill location) associated with this . > VirtualRegister m_virtualRegister; > // The number of uses of the result of this operation (+1 for 'must generate' nodes, which have side-effects). >diff --git a/Source/JavaScriptCore/dfg/DFGNodeFlags.cpp b/Source/JavaScriptCore/dfg/DFGNodeFlags.cpp >index 34084219020f1121c4d8edbb46b3de68ac29206f..a091c098d5095ee12d18b6d5fe83941f66cf4717 100644 >--- a/Source/JavaScriptCore/dfg/DFGNodeFlags.cpp >+++ b/Source/JavaScriptCore/dfg/DFGNodeFlags.cpp >@@ -88,8 +88,11 @@ void dumpNodeFlags(PrintStream& actualOut, NodeFlags flags) > if (flags & NodeMayHaveDoubleResult) > out.print(comma, "MayHaveDoubleResult"); > >- if (flags & NodeMayHaveNonNumberResult) >- out.print(comma, "MayHaveNonNumberResult"); >+ if (flags & NodeMayHaveBigIntResult) >+ out.print(comma, "MayHaveBigIntResult"); >+ >+ if (flags & NodeMayHaveNonNumericResult) >+ out.print(comma, "MayHaveNonNumericResult"); > > if (flags & NodeMayOverflowInt52) > out.print(comma, "MayOverflowInt52"); >diff --git a/Source/JavaScriptCore/dfg/DFGNodeFlags.h b/Source/JavaScriptCore/dfg/DFGNodeFlags.h >index 4abaddf2493c63f54065cd301a34bbc670dbfd1a..7e96a2684ced895ea0c14d67ccdab7783daf3310 100644 >--- a/Source/JavaScriptCore/dfg/DFGNodeFlags.h >+++ b/Source/JavaScriptCore/dfg/DFGNodeFlags.h >@@ -46,31 +46,32 @@ namespace JSC { namespace DFG { > #define NodeMustGenerate 0x0008 // set on nodes that have side effects, and may not trivially be removed by DCE. > #define NodeHasVarArgs 0x0010 > >-#define NodeBehaviorMask 0x07e0 >-#define NodeMayHaveDoubleResult 0x0020 >-#define NodeMayOverflowInt52 0x0040 >-#define NodeMayOverflowInt32InBaseline 0x0080 >-#define NodeMayOverflowInt32InDFG 0x0100 >-#define NodeMayNegZeroInBaseline 0x0200 >-#define NodeMayNegZeroInDFG 0x0400 >-#define NodeMayHaveNonNumberResult 0x0800 >-#define NodeMayHaveNonIntResult (NodeMayHaveDoubleResult | NodeMayHaveNonNumberResult) >+#define NodeBehaviorMask 0x007e0 >+#define NodeMayHaveDoubleResult 0x00020 >+#define NodeMayOverflowInt52 0x00040 >+#define NodeMayOverflowInt32InBaseline 0x00080 >+#define NodeMayOverflowInt32InDFG 0x00100 >+#define NodeMayNegZeroInBaseline 0x00200 >+#define NodeMayNegZeroInDFG 0x00400 >+#define NodeMayHaveNonNumericResult 0x00800 >+#define NodeMayHaveBigIntResult 0x01000 >+#define NodeMayHaveNonIntResult (NodeMayHaveDoubleResult | NodeMayHaveNonNumericResult | NodeMayHaveBigIntResult) > >-#define NodeBytecodeBackPropMask 0x1f000 >+#define NodeBytecodeBackPropMask 0x3e000 > #define NodeBytecodeUseBottom 0x00000 >-#define NodeBytecodeUsesAsNumber 0x01000 // The result of this computation may be used in a context that observes fractional, or bigger-than-int32, results. >-#define NodeBytecodeNeedsNegZero 0x02000 // The result of this computation may be used in a context that observes -0. >-#define NodeBytecodeUsesAsOther 0x04000 // The result of this computation may be used in a context that distinguishes between NaN and other things (like undefined). >+#define NodeBytecodeUsesAsNumber 0x02000 // The result of this computation may be used in a context that observes fractional, or bigger-than-int32, results. >+#define NodeBytecodeNeedsNegZero 0x04000 // The result of this computation may be used in a context that observes -0. >+#define NodeBytecodeUsesAsOther 0x08000 // The result of this computation may be used in a context that distinguishes between NaN and other things (like undefined). > #define NodeBytecodeUsesAsValue (NodeBytecodeUsesAsNumber | NodeBytecodeNeedsNegZero | NodeBytecodeUsesAsOther) >-#define NodeBytecodeUsesAsInt 0x08000 // The result of this computation is known to be used in a context that prefers, but does not require, integer values. >-#define NodeBytecodeUsesAsArrayIndex 0x10000 // The result of this computation is known to be used in a context that strongly prefers integer values, to the point that we should avoid using doubles if at all possible. >+#define NodeBytecodeUsesAsInt 0x10000 // The result of this computation is known to be used in a context that prefers, but does not require, integer values. >+#define NodeBytecodeUsesAsArrayIndex 0x20000 // The result of this computation is known to be used in a context that strongly prefers integer values, to the point that we should avoid using doubles if at all possible. > > #define NodeArithFlagsMask (NodeBehaviorMask | NodeBytecodeBackPropMask) > >-#define NodeIsFlushed 0x20000 // Computed by CPSRethreadingPhase, will tell you which local nodes are backwards-reachable from a Flush. >+#define NodeIsFlushed 0x40000 // Computed by CPSRethreadingPhase, will tell you which local nodes are backwards-reachable from a Flush. > >-#define NodeMiscFlag1 0x40000 >-#define NodeMiscFlag2 0x80000 >+#define NodeMiscFlag1 0x80000 >+#define NodeMiscFlag2 0x100000 > > typedef uint32_t NodeFlags; > >diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp >index d30483be453f4f3c320df66938d74507e357235e..df3fd1e56ee99e0639fbb389e8a55e6d6e21b280 100644 >--- a/Source/JavaScriptCore/dfg/DFGOperations.cpp >+++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp >@@ -1301,11 +1301,22 @@ JSCell* JIT_OPERATION operationBitAndBigInt(ExecState* exec, JSCell* op1, JSCell > { > VM* vm = &exec->vm(); > NativeCallFrameTracer tracer(vm, exec); >+ >+ JSBigInt* leftOperand = jsCast<JSBigInt*>(op1); >+ JSBigInt* rightOperand = jsCast<JSBigInt*>(op2); >+ >+ return JSBigInt::bitwiseAnd(*vm, leftOperand, rightOperand); >+} >+ >+JSCell* JIT_OPERATION operationAddBigInt(ExecState* exec, JSCell* op1, JSCell* op2) >+{ >+ VM* vm = &exec->vm(); >+ NativeCallFrameTracer tracer(vm, exec); > > JSBigInt* leftOperand = jsCast<JSBigInt*>(op1); > JSBigInt* rightOperand = jsCast<JSBigInt*>(op2); > >- return JSBigInt::bitwiseAnd(*vm, leftOperand, rightOperand); >+ return JSBigInt::add(*vm, leftOperand, rightOperand); > } > > JSCell* JIT_OPERATION operationBitOrBigInt(ExecState* exec, JSCell* op1, JSCell* op2) >diff --git a/Source/JavaScriptCore/dfg/DFGOperations.h b/Source/JavaScriptCore/dfg/DFGOperations.h >index 2a294a74af5d0b622fcc5c48d472a7e1f839171d..3498791d5d61cbc5b950a03986d3465377fea84f 100644 >--- a/Source/JavaScriptCore/dfg/DFGOperations.h >+++ b/Source/JavaScriptCore/dfg/DFGOperations.h >@@ -166,6 +166,7 @@ size_t JIT_OPERATION operationCompareStrictEqCell(ExecState*, JSCell* op1, JSCel > JSCell* JIT_OPERATION operationSubBigInt(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL; > JSCell* JIT_OPERATION operationBitAndBigInt(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL; > JSCell* JIT_OPERATION operationBitOrBigInt(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL; >+JSCell* JIT_OPERATION operationAddBigInt(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL; > size_t JIT_OPERATION operationSameValue(ExecState*, EncodedJSValue, EncodedJSValue) WTF_INTERNAL; > JSCell* JIT_OPERATION operationCreateActivationDirect(ExecState*, Structure*, JSScope*, SymbolTable*, EncodedJSValue); > JSCell* JIT_OPERATION operationCreateDirectArguments(ExecState*, Structure*, uint32_t length, uint32_t minCapacity); >diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp >index 0d7129320f86d118ce32fb6f520816b8d19d3ce3..fe89b988483702b909b641a98f7a38b6483b5784 100644 >--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp >+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp >@@ -195,11 +195,15 @@ private: > } else if (isStringOrStringObjectSpeculation(left) || isStringOrStringObjectSpeculation(right)) { > // left or right is definitely something other than a number. > changed |= mergePrediction(SpecString); >- } else { >+ } else if (isBigIntSpeculation(left) && isBigIntSpeculation(right)) >+ changed |= mergePrediction(SpecBigInt); >+ else { > changed |= mergePrediction(SpecInt32Only); > if (node->mayHaveDoubleResult()) > changed |= mergePrediction(SpecBytecodeDouble); >- if (node->mayHaveNonNumberResult()) >+ if (node->mayHaveBigIntResult()) >+ changed |= mergePrediction(SpecBigInt); >+ if (node->mayHaveNonNumericResult()) > changed |= mergePrediction(SpecString); > } > } >@@ -265,7 +269,7 @@ private: > changed |= mergePrediction(SpecInt32Only); > if (node->mayHaveDoubleResult()) > changed |= mergePrediction(SpecBytecodeDouble); >- if (node->mayHaveNonNumberResult()) >+ if (node->mayHaveBigIntResult()) > changed |= mergePrediction(SpecBigInt); > } > } >@@ -292,11 +296,8 @@ private: > changed |= mergePrediction(speculatedDoubleTypeForPrediction(node->child1()->prediction())); > else { > changed |= mergePrediction(SpecInt32Only); >- if (node->op() == ValueNegate && node->mayHaveNonNumberResult()) { >- // FIXME: We should add support to BigInt into speculatio >- // https://bugs.webkit.org/show_bug.cgi?id=182470 >+ if (node->op() == ValueNegate && node->mayHaveBigIntResult()) > changed |= mergePrediction(SpecBigInt); >- } > if (node->mayHaveDoubleResult()) > changed |= mergePrediction(SpecBytecodeDouble); > } >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >index 02b1a7fc2850f8d16be56229327e82e1d2a9e44e..fb4c5d792c5c5c28ab9f9ed9529d0984c2d25847 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >@@ -3837,6 +3837,25 @@ void SpeculativeJIT::compileValueAdd(Node* node) > Edge& leftChild = node->child1(); > Edge& rightChild = node->child2(); > >+ if (node->isBinaryUseKind(BigIntUse)) { >+ SpeculateCellOperand left(this, node->child1()); >+ SpeculateCellOperand right(this, node->child2()); >+ GPRReg leftGPR = left.gpr(); >+ GPRReg rightGPR = right.gpr(); >+ >+ speculateBigInt(leftChild, leftGPR); >+ speculateBigInt(rightChild, rightGPR); >+ >+ flushRegisters(); >+ GPRFlushedCallResult result(this); >+ GPRReg resultGPR = result.gpr(); >+ callOperation(operationAddBigInt, resultGPR, leftGPR, rightGPR); >+ m_jit.exceptionCheck(); >+ >+ cellResult(resultGPR, node); >+ return; >+ } >+ > if (isKnownNotNumber(leftChild.node()) || isKnownNotNumber(rightChild.node())) { > JSValueOperand left(this, leftChild); > JSValueOperand right(this, rightChild); >diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp >index 3825ad7c277f30a24a34790fd0d1f95415989d5a..a538990c0ce0d0327d627c1f543d4a85fc96ad47 100644 >--- a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp >+++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp >@@ -1862,6 +1862,15 @@ private: > > void compileValueAdd() > { >+ if (m_node->isBinaryUseKind(BigIntUse)) { >+ LValue left = lowBigInt(m_node->child1()); >+ LValue right = lowBigInt(m_node->child2()); >+ >+ LValue result = vmCall(pointerType(), m_out.operation(operationAddBigInt), m_callFrame, left, right); >+ setJSValue(result); >+ return; >+ } >+ > CodeBlock* baselineCodeBlock = m_ftlState.graph.baselineCodeBlockFor(m_node->origin.semantic); > ArithProfile* arithProfile = baselineCodeBlock->arithProfileForBytecodeOffset(m_node->origin.semantic.bytecodeIndex); > const Instruction* instruction = baselineCodeBlock->instructions().at(m_node->origin.semantic.bytecodeIndex).ptr(); >diff --git a/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp b/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp >index 70e34492491abea5771756346a715dbec5e9e127..5163c33ee90fc48b7586e27829310d4422f67c08 100644 >--- a/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp >+++ b/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp >@@ -418,8 +418,10 @@ static void updateArithProfileForUnaryArithOp(OpNegate::Metadata& metadata, JSVa > profile.setObservedInt52Overflow(); > } > } >- } else >- profile.setObservedNonNumber(); >+ } else if (result.isBigInt()) >+ profile.setObservedBigInt(); >+ else >+ profile.setObservedNonNumeric(); > } > #else > static void updateArithProfileForUnaryArithOp(OpNegate::Metadata&, JSValue, JSValue) { } >@@ -474,8 +476,10 @@ static void updateArithProfileForBinaryArithOp(ExecState* exec, const Instructio > profile.setObservedInt52Overflow(); > } > } >- } else >- profile.setObservedNonNumber(); >+ } else if (result.isBigInt()) >+ profile.setObservedBigInt(); >+ else >+ profile.setObservedNonNumeric(); > } > #else > static void updateArithProfileForBinaryArithOp(ExecState*, const Instruction*, JSValue, JSValue, JSValue) { } >diff --git a/Tools/ChangeLog b/Tools/ChangeLog >index 14db23935126fcb1ff5de14d4df5c10a872e82c2..9af638b962fa29ec77ad631b9e9c3b85286dd2a5 100644 >--- a/Tools/ChangeLog >+++ b/Tools/ChangeLog >@@ -1,3 +1,12 @@ >+2018-11-01 Caio Lima <ticaiolima@gmail.com> >+ >+ [BigInt] Add support to BigInt into ValueAdd >+ https://bugs.webkit.org/show_bug.cgi?id=186177 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * Scripts/run-jsc-benchmarks: >+ > 2018-10-31 Guillaume Emont <guijemont@igalia.com> > > Don't run JIT tests on 32-bit platforms >diff --git a/Tools/Scripts/run-jsc-benchmarks b/Tools/Scripts/run-jsc-benchmarks >index 4da1c6c6d018d08a93acb7368b973d3daf757a63..2ccfe923d30e50b7b209a2480432e672f6cb3ed0 100755 >--- a/Tools/Scripts/run-jsc-benchmarks >+++ b/Tools/Scripts/run-jsc-benchmarks >@@ -49,6 +49,7 @@ SUNSPIDER_PATH = PERFORMANCETESTS_PATH + "SunSpider" + "tests" + "sunspider-1.0" > LONGSPIDER_PATH = PERFORMANCETESTS_PATH + "LongSpider" > V8_PATH = PERFORMANCETESTS_PATH + "SunSpider" + "tests" + "v8-v6" > TAILBENCH_PATH = PERFORMANCETESTS_PATH + "TailBench9000" >+BIGINTBENCH_PATH = PERFORMANCETESTS_PATH + "BigIntBench" > MICROBENCHMARKS_PATH = OPENSOURCE_PATH + "JSTests" + "microbenchmarks" > OPENSOURCE_OCTANE_PATH = PERFORMANCETESTS_PATH + "Octane" > OCTANE_WRAPPER_PATH = OPENSOURCE_OCTANE_PATH + "wrappers" >@@ -231,6 +232,7 @@ $includeOctane=true > $includeCompressionBench = false > $includeSixSpeed = false > $includeTailBench = true >+$includeBigIntBench = false > $measureGC=false > $benchmarkPattern=nil > $verbosity=0 >@@ -1745,6 +1747,23 @@ class TailBenchBenchmark > end > end > >+class BigIntBenchBenchmark >+ include Benchmark >+ >+ def initialize(name) >+ @name = name >+ end >+ >+ def emitRunCode(plan) >+ emitBenchRunCode(fullname, plan, SingleFileTimedBenchmarkParameters.new(ensureFile("BigIntBench-#{@name}", "#{BIGINTBENCH_PATH}/#{@name}.js"))) >+ end >+ >+ def environment >+ {"JSC_useBigInt" => "true"} >+ end >+end >+ >+ > class MicrobenchmarksBenchmark > include Benchmark > >@@ -2828,6 +2847,7 @@ begin > ['--compression-bench', GetoptLong::NO_ARGUMENT], > ['--six-speed', GetoptLong::NO_ARGUMENT], > ['--tail-bench', GetoptLong::NO_ARGUMENT], >+ ['--big-int-bench', GetoptLong::NO_ARGUMENT], > ['--benchmarks', GetoptLong::REQUIRED_ARGUMENT], > ['--measure-gc', GetoptLong::OPTIONAL_ARGUMENT], > ['--force-vm-kind', GetoptLong::REQUIRED_ARGUMENT], >@@ -2940,6 +2960,9 @@ begin > when '--six-speed' > resetBenchOptionsIfNecessary > $includeSixSpeed = true >+ when '--big-int-bench' >+ resetBenchOptionsIfNecessary >+ $includeBigIntBench = true > when '--benchmarks' > $benchmarkPattern = Regexp.new(arg) > when '--measure-gc' >@@ -3168,6 +3191,15 @@ begin > TAILBENCH.add TailBenchBenchmark.new(name); > } > >+ BIGINTBENCH = BenchmarkSuite.new("BigIntBench", :geometricMean, 0) >+ Dir.foreach(BIGINTBENCH_PATH) { >+ | filename | >+ if filename =~ /\.js$/ >+ name = $~.pre_match >+ BIGINTBENCH.add BigIntBenchBenchmark.new(name) >+ end >+ } >+ > MICROBENCHMARKS = BenchmarkSuite.new("Microbenchmarks", :geometricMean, 0) > Dir.foreach(MICROBENCHMARKS_PATH) { > | filename | >@@ -3322,6 +3354,10 @@ begin > $suites << MICROBENCHMARKS > end > >+ if $includeBigIntBench and not BIGINTBENCH.empty? >+ $suites << BIGINTBENCH >+ end >+ > if $includeAsmBench and not ASMBENCH.empty? > if ASMBENCH_PATH > $suites << ASMBENCH >diff --git a/JSTests/ChangeLog b/JSTests/ChangeLog >index 84031335e3021c07a6265305332687084a4ef78b..371b9d872dbfd17a5e03cd05e4ca934901238888 100644 >--- a/JSTests/ChangeLog >+++ b/JSTests/ChangeLog >@@ -1,3 +1,15 @@ >+2018-11-01 Caio Lima <ticaiolima@gmail.com> >+ >+ [BigInt] Add support to BigInt into ValueAdd >+ https://bugs.webkit.org/show_bug.cgi?id=186177 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * stress/big-int-negate-jit.js: >+ * stress/value-add-big-int-and-string.js: Added. >+ * stress/value-add-big-int-prediction-propagation.js: Added. >+ * stress/value-add-big-int-untyped.js: Added. >+ > 2018-10-31 Tadeu Zagallo <tzagallo@apple.com> > > REGRESSION(r237547): Exception handlers should be aware of wide opcodes >diff --git a/JSTests/stress/big-int-negate-jit.js b/JSTests/stress/big-int-negate-jit.js >index ab497831c965e87436ab904078bedb0269b58ca0..6baa8e7ba0f06f92aa283523d249fe4717e8ea27 100644 >--- a/JSTests/stress/big-int-negate-jit.js >+++ b/JSTests/stress/big-int-negate-jit.js >@@ -44,5 +44,5 @@ for (let i = 0; i < 100000; i++) { > } > > if (numberOfDFGCompiles(mixedSpeculationNegateBigInt) > 1) >- throw "Failed negateBigInt(). We should have compiled a single negate for the BigInt type."; >+ throw "Failed mixedSpeculationNegateBigInt(). We should have compiled a single negate for the BigInt type."; > >diff --git a/JSTests/stress/value-add-big-int-and-string.js b/JSTests/stress/value-add-big-int-and-string.js >new file mode 100644 >index 0000000000000000000000000000000000000000..eb0a4ead0c1d0307aff9e5f8fca89a5c32b57523 >--- /dev/null >+++ b/JSTests/stress/value-add-big-int-and-string.js >@@ -0,0 +1,18 @@ >+//@ runBigIntEnabled >+ >+function assert(v, e) { >+ if (v !== e) >+ throw new Error("Expected value: " + e + " but got: " + v) >+} >+ >+function bigIntOperations(a, b) { >+ let c = a + b; >+ return a + c; >+} >+noInline(bigIntOperations); >+ >+for (let i = 0; i < 100000; i++) { >+ let out = bigIntOperations(0b1111n, "16"); >+ assert(out, "151516"); >+} >+ >diff --git a/JSTests/stress/value-add-big-int-prediction-propagation.js b/JSTests/stress/value-add-big-int-prediction-propagation.js >new file mode 100644 >index 0000000000000000000000000000000000000000..4463711c98035cde03fe49fabe7aac1e1df40897 >--- /dev/null >+++ b/JSTests/stress/value-add-big-int-prediction-propagation.js >@@ -0,0 +1,18 @@ >+//@ runBigIntEnabled >+ >+function assert(v, e) { >+ if (v !== e) >+ throw new Error("Expected value: " + e + " but got: " + v) >+} >+ >+function bigIntPropagation(a, b) { >+ let c = a + b; >+ return c + 0n; >+} >+noInline(bigIntPropagation); >+ >+for (let i = 0; i < 100000; i++) { >+ let out = bigIntPropagation(0xffffffffffffffffffffffffffffffn, 0x1n); >+ assert(out, 0x1000000000000000000000000000000n) >+} >+ >diff --git a/JSTests/stress/value-add-big-int-untyped.js b/JSTests/stress/value-add-big-int-untyped.js >new file mode 100644 >index 0000000000000000000000000000000000000000..02a66d3b8cd7db42019d5b7546d44b5cd176fa8c >--- /dev/null >+++ b/JSTests/stress/value-add-big-int-untyped.js >@@ -0,0 +1,26 @@ >+//@ runBigIntEnabled >+ >+function assert(v, e) { >+ if (v !== e) >+ throw new Error("Expected value: " + e + " but got: " + v) >+} >+ >+function bigIntOperations(a, b) { >+ let c = a + b; >+ return a + c; >+} >+noInline(bigIntOperations); >+ >+c = 0; >+let o = { valueOf: function () { >+ c++; >+ return 0b1111n; >+}}; >+ >+for (let i = 0; i < 100000; i++) { >+ let out = bigIntOperations(o, 0b1010n); >+ assert(out, 40n); >+} >+ >+assert(c, 200000); >+ >diff --git a/PerformanceTests/BigIntBench/big-int-add-prediction-propagation.js b/PerformanceTests/BigIntBench/big-int-add-prediction-propagation.js >new file mode 100644 >index 0000000000000000000000000000000000000000..3fd4e5c7072b2774ead5e49de604a5d7d01e960f >--- /dev/null >+++ b/PerformanceTests/BigIntBench/big-int-add-prediction-propagation.js >@@ -0,0 +1,21 @@ >+function assert(v, e) { >+ if (v !== e) >+ throw new Error("Expected value: " + e + " but got: " + v) >+} >+ >+function bigIntOperations(a, b) { >+ let c = a + b; >+ return c & 0b111111111n; >+} >+noInline(bigIntOperations); >+ >+for (let i = 0; i < 100000; i++) { >+ let out = bigIntOperations(0xffffffffffffffffffffffffffffffn, 0x1n); >+ assert(out, 0n) >+} >+ >+for (let i = 0; i < 100000; i++) { >+ let out = bigIntOperations(0b111111n, 0b1n); >+ assert(out, 0b1000000n) >+} >+ >diff --git a/PerformanceTests/BigIntBench/big-int-simple-add.js b/PerformanceTests/BigIntBench/big-int-simple-add.js >new file mode 100644 >index 0000000000000000000000000000000000000000..68f883d05d18e8e9b30a13eb469aebe18e2e32d9 >--- /dev/null >+++ b/PerformanceTests/BigIntBench/big-int-simple-add.js >@@ -0,0 +1,15 @@ >+function bigInt(a, b) { >+ let c = a + b; >+ return a + c + b; >+} >+noInline(bigInt); >+ >+for (let i = 0; i < 100000; i++) { >+ bigInt(0b1111n, 0b1010n); >+} >+ >+let out; >+for (let i = 0; i < 100000; i++) { >+ out = bigInt(0xffffffffffffffffffn, 0xaaffffffffffffffffffn); >+} >+ >diff --git a/PerformanceTests/BigIntBench/big-int-simple-sub.js b/PerformanceTests/BigIntBench/big-int-simple-sub.js >new file mode 100644 >index 0000000000000000000000000000000000000000..c7de284f822243b3b3604ddf32a6e56c78ef4b97 >--- /dev/null >+++ b/PerformanceTests/BigIntBench/big-int-simple-sub.js >@@ -0,0 +1,17 @@ >+function bigInt(a, b) { >+ let c = a - b; >+ return a - c - b; >+} >+noInline(bigInt); >+ >+for (let i = 0; i < 100000; i++) { >+ bigInt(0b1111n, 0b1010n); >+} >+ >+let out; >+for (let i = 0; i < 100000; i++) { >+ out = bigInt(0xffffffffffffffffffn, 0xaaffffffffffffffffffn); >+} >+ >+print(out); >+ >diff --git a/PerformanceTests/ChangeLog b/PerformanceTests/ChangeLog >index deb1bc96e65c715d7933e7f612a3e604dbeac942..f53dc8e696163dc7d5cc260f67f9777184bde13b 100644 >--- a/PerformanceTests/ChangeLog >+++ b/PerformanceTests/ChangeLog >@@ -1,3 +1,24 @@ >+2018-11-01 Caio Lima <ticaiolima@gmail.com> >+ >+ [BigInt] Add support to BigInt into ValueAdd >+ https://bugs.webkit.org/show_bug.cgi?id=186177 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ The idea of BigIntBench is to provide a set of microbenchmarks and >+ benchmarks to evaluate how fast BigInt computations are happening on >+ JSC implementation. >+ >+ Now, we are adding microbenchmarks in this set, >+ but the plan is to move these tests to "JSTest/microbenchmarks" when >+ BigInt is enabled by default. After that, the focus of Bigint bench is >+ to provide a set of tests that represents real use cases of BigInt in >+ JS programs. >+ >+ * BigIntBench/big-int-add-prediction-propagation.js: Added. >+ * BigIntBench/big-int-simple-add.js: Added. >+ * BigIntBench/big-int-simple-sub.js: Added. >+ > 2018-10-25 Saam Barati <sbarati@apple.com> > > Check in corresponding C code in JetStream 2
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 186177
:
351418
|
351721
|
351908
|
351921
|
352640
|
352648
|
352873
|
352874
| 353619