WebKit Bugzilla
Attachment 368939 Details for
Bug 186174
: [BigInt] Add ValueMod into DFG
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-186174-20190503134559.patch (text/plain), 40.03 KB, created by
Caio Lima
on 2019-05-03 09:46:02 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Caio Lima
Created:
2019-05-03 09:46:02 PDT
Size:
40.03 KB
patch
obsolete
>Subversion Revision: 244907 >diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index c05005e6603b8f6563045eda392a959d85d413b3..c97c01babac48f3fc417037aaba97db9027b88bd 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -1,3 +1,85 @@ >+2019-05-03 Caio Lima <ticaiolima@gmail.com> >+ >+ [BigInt] Add ValueMod into DFG >+ https://bugs.webkit.org/show_bug.cgi?id=186174 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ This patch is introducing a new DFG node called ValueMod, that is >+ responsible to handle BigInt and Untyped specialization of op_mod. >+ With the introduction of BigInt, we think that cases with >+ ValueMod(Untyped, Untyped) can be more common and we introduced >+ support for such kind of node. >+ >+ * dfg/DFGAbstractInterpreter.h: >+ * dfg/DFGAbstractInterpreterInlines.h: >+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::handleConstantDivOp): >+ >+ We are abstracting the constant rules of division operations. It >+ includes ArithDiv, ValueDiv, ArithMod and ValueMod, since they perform >+ the same analysis. This is necessary because AI can still prove a >+ constant even if prediction propagation and fixup phases fail. >+ >+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects): >+ * dfg/DFGBackwardsPropagationPhase.cpp: >+ (JSC::DFG::BackwardsPropagationPhase::propagate): >+ * dfg/DFGByteCodeParser.cpp: >+ (JSC::DFG::ByteCodeParser::makeSafe): >+ (JSC::DFG::ByteCodeParser::parseBlock): >+ >+ Here we check if lhs and rhs have number result to emit ArithMod. >+ Otherwise, we need to fallback to ValueMod and let fixup replace this >+ operation when possible. >+ >+ * dfg/DFGClobberize.h: >+ (JSC::DFG::clobberize): >+ >+ ValueMod(BigIntUse) doesn't clobberize world because it only calls >+ `operationModBigInt`. >+ >+ * dfg/DFGDoesGC.cpp: >+ (JSC::DFG::doesGC): >+ >+ ValueMod(BigIntUse) can trigger GC since it allocates intermediate >+ JSBigInt to perform calculation. ValueMod(UntypedUse) can trigger GC >+ because it can execute arbritary code from user. >+ >+ * dfg/DFGFixupPhase.cpp: >+ (JSC::DFG::FixupPhase::fixupArithDivInt32): >+ >+ Function created to simplify readability of ArithDiv/AirthMod fixup >+ operation. >+ >+ (JSC::DFG::FixupPhase::fixupArithDiv): >+ (JSC::DFG::FixupPhase::fixupNode): >+ >+ Following the same fixup rules of ArithDiv. >+ >+ * dfg/DFGNodeType.h: >+ * dfg/DFGOperations.cpp: >+ (JSC::DFG::binaryOp): >+ * dfg/DFGOperations.h: >+ * dfg/DFGPredictionPropagationPhase.cpp: >+ >+ ValueMod follows the same prediction propagation rules of ArithMod and >+ the same rules for `doDoubleVoting`. >+ >+ * dfg/DFGSafeToExecute.h: >+ (JSC::DFG::safeToExecute): >+ * dfg/DFGSpeculativeJIT.cpp: >+ (JSC::DFG::SpeculativeJIT::compileValueMod): >+ * dfg/DFGSpeculativeJIT.h: >+ * dfg/DFGSpeculativeJIT32_64.cpp: >+ (JSC::DFG::SpeculativeJIT::compile): >+ * dfg/DFGSpeculativeJIT64.cpp: >+ (JSC::DFG::SpeculativeJIT::compile): >+ * dfg/DFGValidate.cpp: >+ * ftl/FTLCapabilities.cpp: >+ (JSC::FTL::canCompile): >+ * ftl/FTLLowerDFGToB3.cpp: >+ (JSC::FTL::DFG::LowerDFGToB3::compileNode): >+ (JSC::FTL::DFG::LowerDFGToB3::compileValueMod): >+ > 2019-05-03 Commit Queue <commit-queue@webkit.org> > > Unreviewed, rolling out r244881. >diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreter.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreter.h >index fba129968994ebf0da26b6e72b8a13c8fbaa74e6..5d5988c29af2b87491c252e55c7eb241a297d4e6 100644 >--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreter.h >+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreter.h >@@ -253,6 +253,8 @@ private: > void verifyEdges(Node*); > void executeDoubleUnaryOpEffects(Node*, double(*equivalentFunction)(double)); > >+ bool handleConstantDivOp(Node*); >+ > CodeBlock* m_codeBlock; > Graph& m_graph; > VM& m_vm; >diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >index 74d82ea8c5f16b68328d45ff0414d0da356535d9..ac28dcdbb3a20a9266c99a027e340a7e11a60b80 100644 >--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >@@ -233,6 +233,57 @@ inline ToThisResult isToThisAnIdentity(VM& vm, bool isStrictMode, AbstractValue& > return ToThisResult::Dynamic; > } > >+template<typename AbstractStateType> >+bool AbstractInterpreter<AbstractStateType>::handleConstantDivOp(Node* node) >+{ >+ JSValue left = forNode(node->child1()).value(); >+ JSValue right = forNode(node->child2()).value(); >+ >+ if (left && right) { >+ NodeType op = node->op(); >+ bool isDivOperation = op == ValueDiv || op == ArithDiv; >+ >+ // Only possible case of ValueOp below is UntypedUse, >+ // so we need to reflect clobberize rules. >+ bool isClobbering = op == ValueDiv || op == ValueMod; >+ >+ if (left.isInt32() && right.isInt32()) { >+ double doubleResult; >+ if (isDivOperation) >+ doubleResult = left.asNumber() / right.asNumber(); >+ else >+ doubleResult = fmod(left.asNumber(), right.asNumber()); >+ >+ if (node->hasArithMode()) { >+ if (!shouldCheckOverflow(node->arithMode())) >+ doubleResult = toInt32(doubleResult); >+ else if (!shouldCheckNegativeZero(node->arithMode())) >+ doubleResult += 0; // Sanitizes zero. >+ } >+ >+ JSValue valueResult = jsNumber(doubleResult); >+ if (valueResult.isInt32()) { >+ if (isClobbering) >+ didFoldClobberWorld(); >+ setConstant(node, valueResult); >+ return true; >+ } >+ } else if (left.isNumber() && right.isNumber()) { >+ if (isClobbering) >+ didFoldClobberWorld(); >+ >+ if (isDivOperation) >+ setConstant(node, jsDoubleNumber(left.asNumber() / right.asNumber())); >+ else >+ setConstant(node, jsDoubleNumber(fmod(left.asNumber(), right.asNumber()))); >+ >+ return true; >+ } >+ } >+ >+ return false; >+} >+ > template<typename AbstractStateType> > bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimit, Node* node) > { >@@ -911,7 +962,11 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi > break; > } > >+ case ValueMod: > case ValueDiv: { >+ if (handleConstantDivOp(node)) >+ break; >+ > if (node->binaryUseKind() == BigIntUse) > setTypeForNode(node, SpecBigInt); > else { >@@ -921,68 +976,26 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi > break; > } > >+ case ArithMod: > case ArithDiv: { >- JSValue left = forNode(node->child1()).value(); >- JSValue right = forNode(node->child2()).value(); >- switch (node->binaryUseKind()) { >- case Int32Use: >- if (left && right && left.isInt32() && right.isInt32()) { >- double doubleResult = left.asNumber() / right.asNumber(); >- if (!shouldCheckOverflow(node->arithMode())) >- doubleResult = toInt32(doubleResult); >- else if (!shouldCheckNegativeZero(node->arithMode())) >- doubleResult += 0; // Sanitizes zero. >- JSValue valueResult = jsNumber(doubleResult); >- if (valueResult.isInt32()) { >- setConstant(node, valueResult); >- break; >- } >- } >- setNonCellTypeForNode(node, SpecInt32Only); >- break; >- case DoubleRepUse: >- if (left && right && left.isNumber() && right.isNumber()) { >- setConstant(node, jsDoubleNumber(left.asNumber() / right.asNumber())); >- break; >- } >- setNonCellTypeForNode(node, >- typeOfDoubleQuotient( >- forNode(node->child1()).m_type, forNode(node->child2()).m_type)); >- break; >- default: >- RELEASE_ASSERT_NOT_REACHED(); >+ if (handleConstantDivOp(node)) > break; >- } >- break; >- } > >- case ArithMod: { >- JSValue left = forNode(node->child1()).value(); >- JSValue right = forNode(node->child2()).value(); > switch (node->binaryUseKind()) { > case Int32Use: >- if (left && right && left.isInt32() && right.isInt32()) { >- double doubleResult = fmod(left.asNumber(), right.asNumber()); >- if (!shouldCheckOverflow(node->arithMode())) >- doubleResult = toInt32(doubleResult); >- else if (!shouldCheckNegativeZero(node->arithMode())) >- doubleResult += 0; // Sanitizes zero. >- JSValue valueResult = jsNumber(doubleResult); >- if (valueResult.isInt32()) { >- setConstant(node, valueResult); >- break; >- } >- } > setNonCellTypeForNode(node, SpecInt32Only); > break; > case DoubleRepUse: >- if (left && right && left.isNumber() && right.isNumber()) { >- setConstant(node, jsDoubleNumber(fmod(left.asNumber(), right.asNumber()))); >- break; >+ if (node->op() == ArithDiv) { >+ setNonCellTypeForNode(node, >+ typeOfDoubleQuotient( >+ forNode(node->child1()).m_type, forNode(node->child2()).m_type)); >+ } else { >+ setNonCellTypeForNode(node, >+ typeOfDoubleBinaryOp( >+ forNode(node->child1()).m_type, forNode(node->child2()).m_type)); > } >- setNonCellTypeForNode(node, >- typeOfDoubleBinaryOp( >- forNode(node->child1()).m_type, forNode(node->child2()).m_type)); >+ > break; > default: > RELEASE_ASSERT_NOT_REACHED(); >diff --git a/Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp >index 6ecc199d72670bdfa96e0b1b24da48c0f36c5279..90d575b418509e39dbe8177552efde647e239a02 100644 >--- a/Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp >+++ b/Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp >@@ -363,6 +363,7 @@ private: > break; > } > >+ case ValueMod: > case ArithMod: { > flags |= NodeBytecodeUsesAsNumber; > flags &= ~NodeBytecodeUsesAsOther; >diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >index 06fb181d3d71755422deb55d0e67384ec4307359..775621340df038bda31eca4d5780b785826aadb2 100644 >--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >@@ -935,7 +935,7 @@ private: > if (m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, NegativeZero)) > node->mergeFlags(NodeMayNegZeroInDFG); > >- if (!isX86() && node->op() == ArithMod) >+ if (!isX86() && (node->op() == ArithMod || node->op() == ValueMod)) > return node; > > { >@@ -996,6 +996,7 @@ private: > case ArithAdd: > case ArithSub: > case ValueAdd: >+ case ValueMod: > case ArithMod: // for ArithMod "MayOverflow" means we tried to divide by zero, or we saw double. > node->mergeFlags(NodeMayOverflowInt32InBaseline); > break; >@@ -5104,7 +5105,10 @@ void ByteCodeParser::parseBlock(unsigned limit) > auto bytecode = currentInstruction->as<OpMod>(); > Node* op1 = get(bytecode.m_lhs); > Node* op2 = get(bytecode.m_rhs); >- set(bytecode.m_dst, makeSafe(addToGraph(ArithMod, op1, op2))); >+ if (op1->hasNumberResult() && op2->hasNumberResult()) >+ set(bytecode.m_dst, makeSafe(addToGraph(ArithMod, op1, op2))); >+ else >+ set(bytecode.m_dst, makeSafe(addToGraph(ValueMod, op1, op2))); > NEXT_OPCODE(op_mod); > } > >diff --git a/Source/JavaScriptCore/dfg/DFGClobberize.h b/Source/JavaScriptCore/dfg/DFGClobberize.h >index b1598d48fe30e21a6b0310be1089eba8609227a0..72dbaab46134a35452f4b10fc364513303a087dc 100644 >--- a/Source/JavaScriptCore/dfg/DFGClobberize.h >+++ b/Source/JavaScriptCore/dfg/DFGClobberize.h >@@ -680,6 +680,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu > case ValueSub: > case ValueMul: > case ValueDiv: >+ case ValueMod: > if (node->isBinaryUseKind(BigIntUse)) { > def(PureValue(node)); > return; >diff --git a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp >index 10991a5576005976cc140ea65b9bd494134997e3..5560e8e2393c985fdd79bf0e13b245a6dccf8168 100644 >--- a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp >+++ b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp >@@ -379,6 +379,7 @@ bool doesGC(Graph& graph, Node* node) > case ValueSub: > case ValueMul: > case ValueDiv: >+ case ValueMod: > case ValueBitNot: > case ValueNegate: > #else >diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >index a0cd01aa34dd75b338adf7d1f48df6e73668be0d..f929fe3fdfa5ee5d2580e010ea91a36f439c3bbc 100644 >--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >@@ -75,38 +75,45 @@ public: > } > > private: >- void fixupArithDiv(Node* node, Edge& leftChild, Edge& rightChild) >+ >+ void fixupArithDivInt32(Node* node, Edge& leftChild, Edge& rightChild) > { >- if (m_graph.binaryArithShouldSpeculateInt32(node, FixupPass)) { >- if (optimizeForX86() || optimizeForARM64() || optimizeForARMv7IDIVSupported()) { >- fixIntOrBooleanEdge(leftChild); >- fixIntOrBooleanEdge(rightChild); >- if (bytecodeCanTruncateInteger(node->arithNodeFlags())) >- node->setArithMode(Arith::Unchecked); >- else if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags())) >- node->setArithMode(Arith::CheckOverflow); >- else >- node->setArithMode(Arith::CheckOverflowAndNegativeZero); >- return; >- } >- >- // This will cause conversion nodes to be inserted later. >- fixDoubleOrBooleanEdge(leftChild); >- fixDoubleOrBooleanEdge(rightChild); >- >- // We don't need to do ref'ing on the children because we're stealing them from >- // the original division. >- Node* newDivision = m_insertionSet.insertNode(m_indexInBlock, SpecBytecodeDouble, *node); >- newDivision->setResult(NodeResultDouble); >- >- node->setOp(DoubleAsInt32); >- node->children.initialize(Edge(newDivision, DoubleRepUse), Edge(), Edge()); >- if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags())) >+ if (optimizeForX86() || optimizeForARM64() || optimizeForARMv7IDIVSupported()) { >+ fixIntOrBooleanEdge(leftChild); >+ fixIntOrBooleanEdge(rightChild); >+ if (bytecodeCanTruncateInteger(node->arithNodeFlags())) >+ node->setArithMode(Arith::Unchecked); >+ else if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags())) > node->setArithMode(Arith::CheckOverflow); > else > node->setArithMode(Arith::CheckOverflowAndNegativeZero); > return; > } >+ >+ // This will cause conversion nodes to be inserted later. >+ fixDoubleOrBooleanEdge(leftChild); >+ fixDoubleOrBooleanEdge(rightChild); >+ >+ // We don't need to do ref'ing on the children because we're stealing them from >+ // the original division. >+ Node* newDivision = m_insertionSet.insertNode(m_indexInBlock, SpecBytecodeDouble, *node); >+ newDivision->setResult(NodeResultDouble); >+ >+ node->setOp(DoubleAsInt32); >+ node->children.initialize(Edge(newDivision, DoubleRepUse), Edge(), Edge()); >+ if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags())) >+ node->setArithMode(Arith::CheckOverflow); >+ else >+ node->setArithMode(Arith::CheckOverflowAndNegativeZero); >+ >+ } >+ >+ void fixupArithDiv(Node* node, Edge& leftChild, Edge& rightChild) >+ { >+ if (m_graph.binaryArithShouldSpeculateInt32(node, FixupPass)) { >+ fixupArithDivInt32(node, leftChild, rightChild); >+ return; >+ } > > fixDoubleOrBooleanEdge(leftChild); > fixDoubleOrBooleanEdge(rightChild); >@@ -474,6 +481,7 @@ private: > if (Node::shouldSpeculateBigInt(leftChild.node(), rightChild.node())) { > fixEdge<BigIntUse>(node->child1()); > fixEdge<BigIntUse>(node->child2()); >+ node->clearFlags(NodeMustGenerate); > break; > } > >@@ -508,6 +516,7 @@ private: > break; > } > >+ case ValueMod: > case ValueDiv: { > Edge& leftChild = node->child1(); > Edge& rightChild = node->child2(); >@@ -515,6 +524,7 @@ private: > if (Node::shouldSpeculateBigInt(leftChild.node(), rightChild.node())) { > fixEdge<BigIntUse>(leftChild); > fixEdge<BigIntUse>(rightChild); >+ node->clearFlags(NodeMustGenerate); > break; > } > >@@ -523,7 +533,12 @@ private: > fixEdge<UntypedUse>(rightChild); > break; > } >- node->setOp(ArithDiv); >+ >+ if (op == ValueDiv) >+ node->setOp(ArithDiv); >+ else >+ node->setOp(ArithMod); >+ > node->setResult(NodeResultNumber); > fixupArithDiv(node, leftChild, rightChild); > break; >diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h >index 7f67872b7bd07ab77c44cc725b04b5ecbf4efb64..b2c0b41e3de7588b8804552d12f2be998e204c7f 100644 >--- a/Source/JavaScriptCore/dfg/DFGNodeType.h >+++ b/Source/JavaScriptCore/dfg/DFGNodeType.h >@@ -179,6 +179,7 @@ namespace JSC { namespace DFG { > macro(ValueSub, NodeResultJS | NodeMustGenerate) \ > macro(ValueMul, NodeResultJS | NodeMustGenerate) \ > macro(ValueDiv, NodeResultJS | NodeMustGenerate) \ >+ macro(ValueMod, NodeResultJS | NodeMustGenerate) \ > \ > /* Add of values that always convers its inputs to strings. May have two or three kids. */\ > macro(StrCat, NodeResultJS | NodeMustGenerate) \ >diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp >index fea350da028fe031092d780ee6989c81ebfa0312..44b8881ee81c2a2c5d3d64d30ba5f5a4326038ad 100644 >--- a/Source/JavaScriptCore/dfg/DFGOperations.cpp >+++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp >@@ -213,6 +213,33 @@ static ALWAYS_INLINE void putWithThis(ExecState* exec, EncodedJSValue encodedBas > baseValue.putInline(exec, ident, putValue, slot); > } > >+template<typename BigIntOperation, typename NumberOperation> >+static ALWAYS_INLINE EncodedJSValue binaryOp(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, BigIntOperation&& bigIntOp, NumberOperation&& numberOp, const char* errorMessage) >+{ >+ VM* vm = &exec->vm(); >+ NativeCallFrameTracer tracer(vm, exec); >+ auto scope = DECLARE_THROW_SCOPE(*vm); >+ >+ JSValue op1 = JSValue::decode(encodedOp1); >+ JSValue op2 = JSValue::decode(encodedOp2); >+ >+ auto leftNumeric = op1.toNumeric(exec); >+ RETURN_IF_EXCEPTION(scope, encodedJSValue()); >+ auto rightNumeric = op2.toNumeric(exec); >+ RETURN_IF_EXCEPTION(scope, encodedJSValue()); >+ >+ if (WTF::holds_alternative<JSBigInt*>(leftNumeric) || WTF::holds_alternative<JSBigInt*>(rightNumeric)) { >+ if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric)) >+ RELEASE_AND_RETURN(scope, JSValue::encode(bigIntOp(exec, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric)))); >+ >+ return throwVMTypeError(exec, scope, errorMessage); >+ } >+ >+ scope.release(); >+ >+ return JSValue::encode(jsNumber(numberOp(WTF::get<double>(leftNumeric), WTF::get<double>(rightNumeric)))); >+} >+ > template<typename BigIntOperation, typename Int32Operation> > static ALWAYS_INLINE EncodedJSValue bitwiseBinaryOp(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, BigIntOperation&& bigIntOp, Int32Operation&& int32Op, const char* errorMessage) > { >@@ -380,6 +407,19 @@ JSCell* JIT_OPERATION operationToObject(ExecState* exec, JSGlobalObject* globalO > RELEASE_AND_RETURN(scope, value.toObject(exec, globalObject)); > } > >+EncodedJSValue JIT_OPERATION operationValueMod(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) >+{ >+ auto bigIntOp = [] (ExecState* exec, JSBigInt* left, JSBigInt* right) -> JSBigInt* { >+ return JSBigInt::remainder(exec, left, right); >+ }; >+ >+ auto numberOp = [] (double left, double right) -> double { >+ return jsMod(left, right); >+ }; >+ >+ return binaryOp(exec, encodedOp1, encodedOp2, bigIntOp, numberOp, "Invalid mix of BigInt and other type in remainder operation."); >+} >+ > EncodedJSValue JIT_OPERATION operationValueBitNot(ExecState* exec, EncodedJSValue encodedOp1) > { > VM* vm = &exec->vm(); >@@ -497,33 +537,15 @@ EncodedJSValue JIT_OPERATION operationValueAddNotNumber(ExecState* exec, Encoded > > EncodedJSValue JIT_OPERATION operationValueDiv(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) > { >- VM* vm = &exec->vm(); >- NativeCallFrameTracer tracer(vm, exec); >- auto scope = DECLARE_THROW_SCOPE(*vm); >- >- JSValue op1 = JSValue::decode(encodedOp1); >- JSValue op2 = JSValue::decode(encodedOp2); >- >- auto leftNumeric = op1.toNumeric(exec); >- RETURN_IF_EXCEPTION(scope, encodedJSValue()); >- auto rightNumeric = op2.toNumeric(exec); >- RETURN_IF_EXCEPTION(scope, encodedJSValue()); >- >- if (WTF::holds_alternative<JSBigInt*>(leftNumeric) || WTF::holds_alternative<JSBigInt*>(rightNumeric)) { >- if (WTF::holds_alternative<JSBigInt*>(leftNumeric) && WTF::holds_alternative<JSBigInt*>(rightNumeric)) { >- JSBigInt* result = JSBigInt::divide(exec, WTF::get<JSBigInt*>(leftNumeric), WTF::get<JSBigInt*>(rightNumeric)); >- RETURN_IF_EXCEPTION(scope, encodedJSValue()); >- return JSValue::encode(result); >- } >- >- return throwVMTypeError(exec, scope, "Invalid mix of BigInt and other type in division operation."); >- } >+ auto bigIntOp = [] (ExecState* exec, JSBigInt* left, JSBigInt* right) -> JSBigInt* { >+ return JSBigInt::divide(exec, left, right); >+ }; > >- scope.release(); >+ auto numberOp = [] (double left, double right) -> double { >+ return left / right; >+ }; > >- double a = WTF::get<double>(leftNumeric); >- double b = WTF::get<double>(rightNumeric); >- return JSValue::encode(jsNumber(a / b)); >+ return binaryOp(exec, encodedOp1, encodedOp2, bigIntOp, numberOp, "Invalid mix of BigInt and other type in division operation."); > } > > double JIT_OPERATION operationArithAbs(ExecState* exec, EncodedJSValue encodedOp1) >@@ -1352,6 +1374,17 @@ JSCell* JIT_OPERATION operationMulBigInt(ExecState* exec, JSCell* op1, JSCell* o > return JSBigInt::multiply(exec, leftOperand, rightOperand); > } > >+JSCell* JIT_OPERATION operationModBigInt(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::remainder(exec, leftOperand, rightOperand); >+} >+ > JSCell* JIT_OPERATION operationDivBigInt(ExecState* exec, JSCell* op1, JSCell* op2) > { > VM* vm = &exec->vm(); >diff --git a/Source/JavaScriptCore/dfg/DFGOperations.h b/Source/JavaScriptCore/dfg/DFGOperations.h >index 949e39a9b8634cd71cadab20cbf90dcfa4d8a7c1..0ce007921d9b7bfd7051270f9617d8bd017c8d09 100644 >--- a/Source/JavaScriptCore/dfg/DFGOperations.h >+++ b/Source/JavaScriptCore/dfg/DFGOperations.h >@@ -50,6 +50,7 @@ JSCell* JIT_OPERATION operationObjectCreateObject(ExecState*, JSObject*) WTF_INT > JSCell* JIT_OPERATION operationCreateThis(ExecState*, JSObject* constructor, uint32_t inlineCapacity) WTF_INTERNAL; > EncodedJSValue JIT_OPERATION operationToThis(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL; > EncodedJSValue JIT_OPERATION operationToThisStrict(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL; >+EncodedJSValue JIT_OPERATION operationValueMod(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL; > EncodedJSValue JIT_OPERATION operationValueBitNot(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL; > EncodedJSValue JIT_OPERATION operationValueBitAnd(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL; > EncodedJSValue JIT_OPERATION operationValueBitOr(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL; >@@ -168,6 +169,7 @@ size_t JIT_OPERATION operationRegExpTestGeneric(ExecState*, JSGlobalObject*, Enc > size_t JIT_OPERATION operationCompareStrictEqCell(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL; > JSCell* JIT_OPERATION operationSubBigInt(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL; > JSCell* JIT_OPERATION operationMulBigInt(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL; >+JSCell* JIT_OPERATION operationModBigInt(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL; > JSCell* JIT_OPERATION operationDivBigInt(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL; > JSCell* JIT_OPERATION operationBitAndBigInt(ExecState*, JSCell* op1, JSCell* op2) WTF_INTERNAL; > JSCell* JIT_OPERATION operationBitNotBigInt(ExecState*, JSCell* op1) WTF_INTERNAL; >diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp >index 6e3fb25d81b8b8df5e01b9f8fd3ada9e9059519f..098af63e64ed9c047ab37a54e0b82b430715ded8 100644 >--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp >+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp >@@ -348,6 +348,7 @@ private: > } > > case ValueDiv: >+ case ValueMod: > case ArithDiv: > case ArithMod: { > SpeculatedType left = node->child1()->prediction(); >@@ -360,11 +361,11 @@ private: > changed |= mergePrediction(SpecInt32Only); > else > changed |= mergePrediction(SpecBytecodeDouble); >- } else if (op == ValueDiv && isBigIntSpeculation(left) && isBigIntSpeculation(right)) >+ } else if ((op == ValueDiv || op == ValueMod) && isBigIntSpeculation(left) && isBigIntSpeculation(right)) > changed |= mergePrediction(SpecBigInt); > else { > changed |= mergePrediction(SpecInt32Only | SpecBytecodeDouble); >- if (op == ValueDiv && (node->mayHaveBigIntResult() >+ if ((op == ValueDiv || op == ValueMod) && (node->mayHaveBigIntResult() > || (left & SpecBigInt) > || (right & SpecBigInt))) > changed |= mergePrediction(SpecBigInt); >@@ -601,6 +602,7 @@ private: > case ArithMax: > case ArithMod: > case ValueDiv: >+ case ValueMod: > case ArithDiv: { > SpeculatedType left = node->child1()->prediction(); > SpeculatedType right = node->child2()->prediction(); >@@ -1117,6 +1119,7 @@ private: > case ValueSub: > case ValueMul: > case ValueDiv: >+ case ValueMod: > case ArithAdd: > case ArithSub: > case ArithNegate: >diff --git a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h >index 992bcf8a9b27bf10a6ec68c4cc6016fd7bfc0d5a..2bf682b56dda0c9b40ef6322888445a0d46b5770 100644 >--- a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h >+++ b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h >@@ -238,6 +238,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno > case ValueSub: > case ValueMul: > case ValueDiv: >+ case ValueMod: > case TryGetById: > case DeleteById: > case DeleteByVal: >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >index 3c0390725e83572eba1b6297603f0c30f583d917..ac976413107c7b019b59ffb51ebcfbf7b1fa727d 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >@@ -5221,6 +5221,44 @@ void SpeculativeJIT::compileArithFRound(Node* node) > doubleResult(result.fpr(), node); > } > >+void SpeculativeJIT::compileValueMod(Node* node) >+{ >+ Edge& leftChild = node->child1(); >+ Edge& rightChild = node->child2(); >+ >+ if (leftChild.useKind() == BigIntUse && rightChild.useKind() == BigIntUse) { >+ SpeculateCellOperand left(this, leftChild); >+ SpeculateCellOperand right(this, rightChild); >+ GPRReg leftGPR = left.gpr(); >+ GPRReg rightGPR = right.gpr(); >+ >+ speculateBigInt(leftChild, leftGPR); >+ speculateBigInt(rightChild, rightGPR); >+ >+ flushRegisters(); >+ GPRFlushedCallResult result(this); >+ GPRReg resultGPR = result.gpr(); >+ >+ callOperation(operationModBigInt, resultGPR, leftGPR, rightGPR); >+ >+ m_jit.exceptionCheck(); >+ cellResult(resultGPR, node); >+ return; >+ } >+ >+ DFG_ASSERT(m_jit.graph(), node, node->binaryUseKind() == UntypedUse, node->binaryUseKind()); >+ JSValueOperand op1(this, leftChild); >+ JSValueOperand op2(this, rightChild); >+ JSValueRegs op1Regs = op1.jsValueRegs(); >+ JSValueRegs op2Regs = op2.jsValueRegs(); >+ flushRegisters(); >+ JSValueRegsFlushedCallResult result(this); >+ JSValueRegs resultRegs = result.regs(); >+ callOperation(operationValueMod, resultRegs, op1Regs, op2Regs); >+ m_jit.exceptionCheck(); >+ jsValueResult(resultRegs, node); >+} >+ > void SpeculativeJIT::compileArithMod(Node* node) > { > switch (node->binaryUseKind()) { >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h >index e65de38b0bb1becead325f552f4685c7ffa0f314..e5c5c567334dc6209d64d8c13e297cb971b7b1eb 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h >@@ -1356,6 +1356,7 @@ public: > void compileValueDiv(Node*); > void compileArithDiv(Node*); > void compileArithFRound(Node*); >+ void compileValueMod(Node*); > void compileArithMod(Node*); > void compileArithPow(Node*); > void compileArithRounding(Node*); >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp >index 1e3374553a8c7bfa02e782aa1b72da7b151dddfa..db2d4649f691dbfee937bd5baf1d9a4aadd4bb7a 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp >@@ -2087,6 +2087,11 @@ void SpeculativeJIT::compile(Node* node) > break; > } > >+ case ValueMod: { >+ compileValueMod(node); >+ break; >+ } >+ > case ArithMod: { > compileArithMod(node); > break; >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >index f009e6b08b3f0c97ad91f8c303a6e2a0172ecca5..f5697242f2b01a6eeacb89da3f37fad577452ed6 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >@@ -2233,6 +2233,11 @@ void SpeculativeJIT::compile(Node* node) > break; > } > >+ case ValueMod: { >+ compileValueMod(node); >+ break; >+ } >+ > case ArithMod: { > compileArithMod(node); > break; >diff --git a/Source/JavaScriptCore/dfg/DFGValidate.cpp b/Source/JavaScriptCore/dfg/DFGValidate.cpp >index 04dfdcb7d683cb243d72e84d65d178b432cababd..0aa5b09feed4c1531b553f80c289f37115524770 100644 >--- a/Source/JavaScriptCore/dfg/DFGValidate.cpp >+++ b/Source/JavaScriptCore/dfg/DFGValidate.cpp >@@ -257,6 +257,7 @@ public: > case ValueSub: > case ValueMul: > case ValueDiv: >+ case ValueMod: > case ArithAdd: > case ArithSub: > case ArithMul: >diff --git a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp >index 316ab8deeee7477aed83ff75e207023dd491bea4..c5d4f1824e9ab76892c9f0b7998aa0d22826b322 100644 >--- a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp >+++ b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp >@@ -98,6 +98,7 @@ inline CapabilityLevel canCompile(Node* node) > case ValueSub: > case ValueMul: > case ValueDiv: >+ case ValueMod: > case StrCat: > case ArithAdd: > case ArithClz32: >diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp >index c2564d317b11c99e67bf3743431a18973727da6c..d749f34fbe343b787aae81996726edbfd9fc60e9 100644 >--- a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp >+++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp >@@ -780,6 +780,9 @@ private: > case ArithDiv: > compileArithDiv(); > break; >+ case ValueMod: >+ compileValueMod(); >+ break; > case ArithMod: > compileArithMod(); > break; >@@ -2538,6 +2541,24 @@ private: > } > } > >+ void compileValueMod() >+ { >+ if (m_node->binaryUseKind() == BigIntUse) { >+ LValue left = lowBigInt(m_node->child1()); >+ LValue right = lowBigInt(m_node->child2()); >+ >+ LValue result = vmCall(pointerType(), m_out.operation(operationModBigInt), m_callFrame, left, right); >+ setJSValue(result); >+ return; >+ } >+ >+ DFG_ASSERT(m_graph, m_node, m_node->binaryUseKind() == UntypedUse, m_node->binaryUseKind()); >+ LValue left = lowJSValue(m_node->child1()); >+ LValue right = lowJSValue(m_node->child2()); >+ LValue result = vmCall(Int64, m_out.operation(operationValueMod), m_callFrame, left, right); >+ setJSValue(result); >+ } >+ > void compileArithMod() > { > switch (m_node->binaryUseKind()) { >diff --git a/JSTests/ChangeLog b/JSTests/ChangeLog >index 37b413a12d5abd87da0cccc770619be5ccd2f496..1e7585a7a0716e79bca42d3fa05914f4fa3295f1 100644 >--- a/JSTests/ChangeLog >+++ b/JSTests/ChangeLog >@@ -1,3 +1,15 @@ >+2019-05-03 Caio Lima <ticaiolima@gmail.com> >+ >+ [BigInt] Add ValueMod into DFG >+ https://bugs.webkit.org/show_bug.cgi?id=186174 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * microbenchmarks/mod-untyped.js: Added. >+ * stress/big-int-mod-osr.js: Added. >+ * stress/value-div-ai-rule.js: Added. >+ * stress/value-mod-ai-rule.js: Added. >+ > 2019-05-02 Michael Saboff <msaboff@apple.com> > > Unreviewed rollout of r244862. >diff --git a/JSTests/microbenchmarks/mod-untyped.js b/JSTests/microbenchmarks/mod-untyped.js >new file mode 100644 >index 0000000000000000000000000000000000000000..800864d03d1133012e697418f5ee12d17ca5249f >--- /dev/null >+++ b/JSTests/microbenchmarks/mod-untyped.js >@@ -0,0 +1,39 @@ >+let assert = { >+ sameValue: function(i, e, m) { >+ if (i !== e) >+ throw new Error(m); >+ } >+} >+ >+function untypedMod(x, y) { >+ return x % y; >+} >+noInline(untypedMod); >+ >+let o = {valueOf: () => 10}; >+ >+for (let i = 0; i < 100000; i++) { >+ let r = untypedMod(30, o); >+ assert.sameValue(r, 0, 30 + " % {valueOf: () => 10} = " + r); >+} >+ >+o2 = {valueOf: () => 10000}; >+ >+for (let i = 0; i < 100000; i++) { >+ let r = untypedMod(o2, o); >+ assert.sameValue(r, 0, "{valueOf: () => 10000} % {valueOf: () => 10} = " + r); >+} >+ >+o = Object(10); >+let r = untypedMod(30, o); >+assert.sameValue(r, 0, 30 + " % Object(10) = " + r); >+ >+o2 = Object(3240); >+r = untypedMod(o2, o); >+assert.sameValue(r, 0, "Object(3240) % Object(10) = " + r); >+ >+for (let i = 0; i < 100000; i++) { >+ let r = untypedMod("9", "8"); >+ assert.sameValue(r, 1, "9 % 8 = " + r); >+} >+ >diff --git a/JSTests/stress/big-int-mod-osr.js b/JSTests/stress/big-int-mod-osr.js >new file mode 100644 >index 0000000000000000000000000000000000000000..8c109052c2b9c74e5a4476a0c861d570122b1008 >--- /dev/null >+++ b/JSTests/stress/big-int-mod-osr.js >@@ -0,0 +1,30 @@ >+//@ runBigIntEnabled >+ >+let assert = { >+ sameValue: function(i, e, m) { >+ if (i !== e) >+ throw new Error(m); >+ } >+} >+ >+function bigIntMod(x, y) { >+ return x % y; >+} >+noInline(bigIntMod); >+ >+for (let i = 0; i < 10000; i++) { >+ let r = bigIntMod(3n, 10n); >+ assert.sameValue(r, 3n, 3n + " % " + 10n + " = " + r); >+} >+ >+let r = bigIntMod(3, 10); >+assert.sameValue(r, 3, 3 + " % " + 10 + " = " + r); >+ >+for (let i = 0; i < 10000; i++) { >+ let r = bigIntMod(3n, 10n); >+ assert.sameValue(r, 3n, 3n + " % " + 10n + " = " + r); >+} >+ >+r = bigIntMod("3", "10"); >+assert.sameValue(r, 3, 3 + " % " + 10 + " = " + r); >+ >diff --git a/JSTests/stress/value-div-ai-rule.js b/JSTests/stress/value-div-ai-rule.js >new file mode 100644 >index 0000000000000000000000000000000000000000..816b71b4a888d09b98cd065db854efae4e7853fd >--- /dev/null >+++ b/JSTests/stress/value-div-ai-rule.js >@@ -0,0 +1,38 @@ >+function assert(a, e) { >+ if (a !== e) >+ throw new Error("Expected: " + e + " bug got: " + a); >+} >+ >+(() => { >+ let predicate = true; >+ function foo(a) { >+ let v = a; >+ if (predicate) >+ v = 10; >+ >+ let c = v / 2; >+ return c; >+ } >+ noInline(foo); >+ >+ for (let i = 0; i < 10000; i++) { >+ assert(foo("10"), 5); >+ } >+})(); >+ >+(() => { >+ let predicate = true; >+ function foo(a) { >+ let v = a; >+ if (predicate) >+ v = 10.5; >+ >+ let c = v / 2; >+ return c; >+ } >+ noInline(foo); >+ >+ for (let i = 0; i < 10000; i++) { >+ assert(foo("10"), 5.25); >+ } >+})(); >diff --git a/JSTests/stress/value-mod-ai-rule.js b/JSTests/stress/value-mod-ai-rule.js >new file mode 100644 >index 0000000000000000000000000000000000000000..6a44dab42d5378b3ee13d867a3fc489f463229fc >--- /dev/null >+++ b/JSTests/stress/value-mod-ai-rule.js >@@ -0,0 +1,39 @@ >+function assert(a, e) { >+ if (a !== e) >+ throw new Error("Expected: " + e + " bug got: " + a); >+} >+ >+(() => { >+ let predicate = true; >+ function foo(a) { >+ let v = a; >+ if (predicate) >+ v = 10; >+ >+ let c = v % 2; >+ return c; >+ } >+ noInline(foo); >+ >+ for (let i = 0; i < 10000; i++) { >+ assert(foo("10"), 0); >+ } >+})(); >+ >+(() => { >+ let predicate = true; >+ function foo(a) { >+ let v = a; >+ if (predicate) >+ v = 10.5; >+ >+ let c = v % 2; >+ return c; >+ } >+ noInline(foo); >+ >+ for (let i = 0; i < 10000; i++) { >+ assert(foo("10"), 0.5); >+ } >+})(); >+ >diff --git a/PerformanceTests/BigIntBench/big-int-simple-mod.js b/PerformanceTests/BigIntBench/big-int-simple-mod.js >new file mode 100644 >index 0000000000000000000000000000000000000000..a50dc6ff372d153523a9c873cea55d32304e1133 >--- /dev/null >+++ b/PerformanceTests/BigIntBench/big-int-simple-mod.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/ChangeLog b/PerformanceTests/ChangeLog >index 3bbf37a598e4d88fc76bf9954cbde54fccff6c33..0960943e9cb2c6ce2ffc9bd15f4e0a4ba86799fd 100644 >--- a/PerformanceTests/ChangeLog >+++ b/PerformanceTests/ChangeLog >@@ -1,3 +1,12 @@ >+2019-05-03 Caio Lima <ticaiolima@gmail.com> >+ >+ [BigInt] Add ValueMod into DFG >+ https://bugs.webkit.org/show_bug.cgi?id=186174 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * BigIntBench/big-int-simple-mod.js: Added. >+ > 2019-04-16 Jon Lee <jonlee@apple.com> > > [MotionMark] The text "kkkj" is causing Firefox console warning: `unreachable code after return statement`
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 186174
:
357312
|
357558
|
357559
|
357630
|
357669
|
357671
|
358259
|
358260
|
359609
|
359875
|
359877
|
359973
|
360005
|
360926
|
362254
|
364011
|
367063
|
368939
|
368952
|
369347
|
369361
|
369381
|
369390
|
369393