1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828 |
- /*
- * Copyright (C) 2011, 2012, 2013 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- #include "config.h"
- #include "DFGAbstractState.h"
- #if ENABLE(DFG_JIT)
- #include "CodeBlock.h"
- #include "DFGBasicBlock.h"
- #include "GetByIdStatus.h"
- #include "Operations.h"
- #include "PutByIdStatus.h"
- #include "StringObject.h"
- namespace JSC { namespace DFG {
- AbstractState::AbstractState(Graph& graph)
- : m_codeBlock(graph.m_codeBlock)
- , m_graph(graph)
- , m_variables(m_codeBlock->numParameters(), graph.m_localVars)
- , m_block(0)
- {
- }
- AbstractState::~AbstractState() { }
- void AbstractState::beginBasicBlock(BasicBlock* basicBlock)
- {
- ASSERT(!m_block);
-
- ASSERT(basicBlock->variablesAtHead.numberOfLocals() == basicBlock->valuesAtHead.numberOfLocals());
- ASSERT(basicBlock->variablesAtTail.numberOfLocals() == basicBlock->valuesAtTail.numberOfLocals());
- ASSERT(basicBlock->variablesAtHead.numberOfLocals() == basicBlock->variablesAtTail.numberOfLocals());
-
- for (size_t i = 0; i < basicBlock->size(); i++)
- forNode(basicBlock->at(i)).clear();
- m_variables = basicBlock->valuesAtHead;
- m_haveStructures = false;
- for (size_t i = 0; i < m_variables.numberOfArguments(); ++i) {
- if (m_variables.argument(i).m_currentKnownStructure.isNeitherClearNorTop()) {
- m_haveStructures = true;
- break;
- }
- }
- for (size_t i = 0; i < m_variables.numberOfLocals(); ++i) {
- if (m_variables.local(i).m_currentKnownStructure.isNeitherClearNorTop()) {
- m_haveStructures = true;
- break;
- }
- }
-
- basicBlock->cfaShouldRevisit = false;
- basicBlock->cfaHasVisited = true;
- m_block = basicBlock;
- m_isValid = true;
- m_foundConstants = false;
- m_branchDirection = InvalidBranchDirection;
- }
- void AbstractState::initialize(Graph& graph)
- {
- BasicBlock* root = graph.m_blocks[0].get();
- root->cfaShouldRevisit = true;
- root->cfaHasVisited = false;
- root->cfaFoundConstants = false;
- for (size_t i = 0; i < root->valuesAtHead.numberOfArguments(); ++i) {
- Node* node = root->variablesAtHead.argument(i);
- ASSERT(node->op() == SetArgument);
- if (!node->variableAccessData()->shouldUnboxIfPossible()) {
- root->valuesAtHead.argument(i).makeTop();
- continue;
- }
-
- SpeculatedType prediction = node->variableAccessData()->prediction();
- if (isInt32Speculation(prediction))
- root->valuesAtHead.argument(i).set(SpecInt32);
- else if (isBooleanSpeculation(prediction))
- root->valuesAtHead.argument(i).set(SpecBoolean);
- else if (isCellSpeculation(prediction))
- root->valuesAtHead.argument(i).set(SpecCell);
- else
- root->valuesAtHead.argument(i).makeTop();
-
- root->valuesAtTail.argument(i).clear();
- }
- for (size_t i = 0; i < root->valuesAtHead.numberOfLocals(); ++i) {
- Node* node = root->variablesAtHead.local(i);
- if (node && node->variableAccessData()->isCaptured())
- root->valuesAtHead.local(i).makeTop();
- else
- root->valuesAtHead.local(i).clear();
- root->valuesAtTail.local(i).clear();
- }
- for (BlockIndex blockIndex = 1 ; blockIndex < graph.m_blocks.size(); ++blockIndex) {
- BasicBlock* block = graph.m_blocks[blockIndex].get();
- if (!block)
- continue;
- if (!block->isReachable)
- continue;
- block->cfaShouldRevisit = false;
- block->cfaHasVisited = false;
- block->cfaFoundConstants = false;
- for (size_t i = 0; i < block->valuesAtHead.numberOfArguments(); ++i) {
- block->valuesAtHead.argument(i).clear();
- block->valuesAtTail.argument(i).clear();
- }
- for (size_t i = 0; i < block->valuesAtHead.numberOfLocals(); ++i) {
- block->valuesAtHead.local(i).clear();
- block->valuesAtTail.local(i).clear();
- }
- if (!block->isOSRTarget)
- continue;
- if (block->bytecodeBegin != graph.m_osrEntryBytecodeIndex)
- continue;
- for (size_t i = 0; i < graph.m_mustHandleValues.size(); ++i) {
- AbstractValue value;
- value.setMostSpecific(graph.m_mustHandleValues[i]);
- int operand = graph.m_mustHandleValues.operandForIndex(i);
- block->valuesAtHead.operand(operand).merge(value);
- #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLogF(" Initializing Block #%u, operand r%d, to ", blockIndex, operand);
- block->valuesAtHead.operand(operand).dump(WTF::dataFile());
- dataLogF("\n");
- #endif
- }
- block->cfaShouldRevisit = true;
- }
- }
- bool AbstractState::endBasicBlock(MergeMode mergeMode)
- {
- ASSERT(m_block);
-
- BasicBlock* block = m_block; // Save the block for successor merging.
-
- block->cfaFoundConstants = m_foundConstants;
- block->cfaDidFinish = m_isValid;
- block->cfaBranchDirection = m_branchDirection;
-
- if (!m_isValid) {
- reset();
- return false;
- }
-
- bool changed = false;
-
- if (mergeMode != DontMerge || !ASSERT_DISABLED) {
- for (size_t argument = 0; argument < block->variablesAtTail.numberOfArguments(); ++argument) {
- #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLogF(" Merging state for argument %zu.\n", argument);
- #endif
- AbstractValue& destination = block->valuesAtTail.argument(argument);
- changed |= mergeStateAtTail(destination, m_variables.argument(argument), block->variablesAtTail.argument(argument));
- }
-
- for (size_t local = 0; local < block->variablesAtTail.numberOfLocals(); ++local) {
- #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLogF(" Merging state for local %zu.\n", local);
- #endif
- AbstractValue& destination = block->valuesAtTail.local(local);
- changed |= mergeStateAtTail(destination, m_variables.local(local), block->variablesAtTail.local(local));
- }
- }
-
- ASSERT(mergeMode != DontMerge || !changed);
-
- #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLogF(" Branch direction = %s\n", branchDirectionToString(m_branchDirection));
- #endif
-
- reset();
-
- if (mergeMode != MergeToSuccessors)
- return changed;
-
- return mergeToSuccessors(m_graph, block);
- }
- void AbstractState::reset()
- {
- m_block = 0;
- m_isValid = false;
- m_branchDirection = InvalidBranchDirection;
- }
- AbstractState::BooleanResult AbstractState::booleanResult(Node* node, AbstractValue& value)
- {
- JSValue childConst = value.value();
- if (childConst) {
- if (childConst.toBoolean(m_codeBlock->globalObjectFor(node->codeOrigin)->globalExec()))
- return DefinitelyTrue;
- return DefinitelyFalse;
- }
- // Next check if we can fold because we know that the source is an object or string and does not equal undefined.
- if (isCellSpeculation(value.m_type)
- && value.m_currentKnownStructure.hasSingleton()) {
- Structure* structure = value.m_currentKnownStructure.singleton();
- if (!structure->masqueradesAsUndefined(m_codeBlock->globalObjectFor(node->codeOrigin))
- && structure->typeInfo().type() != StringType)
- return DefinitelyTrue;
- }
-
- return UnknownBooleanResult;
- }
- bool AbstractState::startExecuting(Node* node)
- {
- ASSERT(m_block);
- ASSERT(m_isValid);
-
- m_didClobber = false;
-
- node->setCanExit(false);
-
- if (!node->shouldGenerate())
- return false;
-
- return true;
- }
- bool AbstractState::startExecuting(unsigned indexInBlock)
- {
- return startExecuting(m_block->at(indexInBlock));
- }
- void AbstractState::executeEdges(Node* node)
- {
- DFG_NODE_DO_TO_CHILDREN(m_graph, node, filterEdgeByUse);
- }
- void AbstractState::executeEdges(unsigned indexInBlock)
- {
- executeEdges(m_block->at(indexInBlock));
- }
- void AbstractState::verifyEdge(Node*, Edge edge)
- {
- RELEASE_ASSERT(!(forNode(edge).m_type & ~typeFilterFor(edge.useKind())));
- }
- void AbstractState::verifyEdges(Node* node)
- {
- DFG_NODE_DO_TO_CHILDREN(m_graph, node, verifyEdge);
- }
- bool AbstractState::executeEffects(unsigned indexInBlock, Node* node)
- {
- if (!ASSERT_DISABLED)
- verifyEdges(node);
-
- switch (node->op()) {
- case JSConstant:
- case WeakJSConstant:
- case PhantomArguments: {
- forNode(node).set(m_graph.valueOfJSConstant(node));
- break;
- }
-
- case Identity: {
- forNode(node) = forNode(node->child1());
- break;
- }
-
- case GetLocal: {
- VariableAccessData* variableAccessData = node->variableAccessData();
- if (variableAccessData->prediction() == SpecNone) {
- m_isValid = false;
- break;
- }
- AbstractValue value = m_variables.operand(variableAccessData->local());
- if (!variableAccessData->isCaptured()) {
- if (value.isClear())
- node->setCanExit(true);
- }
- if (value.value())
- m_foundConstants = true;
- forNode(node) = value;
- break;
- }
-
- case GetLocalUnlinked: {
- AbstractValue value = m_variables.operand(node->unlinkedLocal());
- if (value.value())
- m_foundConstants = true;
- forNode(node) = value;
- break;
- }
-
- case SetLocal: {
- m_variables.operand(node->local()) = forNode(node->child1());
- break;
- }
-
- case MovHintAndCheck: {
- // Don't need to do anything. A MovHint is effectively a promise that the SetLocal
- // was dead.
- break;
- }
-
- case MovHint:
- case ZombieHint: {
- RELEASE_ASSERT_NOT_REACHED();
- break;
- }
-
- case SetArgument:
- // Assert that the state of arguments has been set.
- ASSERT(!m_block->valuesAtHead.operand(node->local()).isClear());
- break;
-
- case BitAnd:
- case BitOr:
- case BitXor:
- case BitRShift:
- case BitLShift:
- case BitURShift: {
- JSValue left = forNode(node->child1()).value();
- JSValue right = forNode(node->child2()).value();
- if (left && right && left.isInt32() && right.isInt32()) {
- int32_t a = left.asInt32();
- int32_t b = right.asInt32();
- bool constantWasSet;
- switch (node->op()) {
- case BitAnd:
- constantWasSet = trySetConstant(node, JSValue(a & b));
- break;
- case BitOr:
- constantWasSet = trySetConstant(node, JSValue(a | b));
- break;
- case BitXor:
- constantWasSet = trySetConstant(node, JSValue(a ^ b));
- break;
- case BitRShift:
- constantWasSet = trySetConstant(node, JSValue(a >> static_cast<uint32_t>(b)));
- break;
- case BitLShift:
- constantWasSet = trySetConstant(node, JSValue(a << static_cast<uint32_t>(b)));
- break;
- case BitURShift:
- constantWasSet = trySetConstant(node, JSValue(static_cast<uint32_t>(a) >> static_cast<uint32_t>(b)));
- break;
- default:
- RELEASE_ASSERT_NOT_REACHED();
- constantWasSet = false;
- }
- if (constantWasSet) {
- m_foundConstants = true;
- break;
- }
- }
- forNode(node).set(SpecInt32);
- break;
- }
-
- case UInt32ToNumber: {
- JSValue child = forNode(node->child1()).value();
- if (child && child.isNumber()) {
- ASSERT(child.isInt32());
- if (trySetConstant(node, JSValue(child.asUInt32()))) {
- m_foundConstants = true;
- break;
- }
- }
- if (!node->canSpeculateInteger())
- forNode(node).set(SpecDouble);
- else {
- forNode(node).set(SpecInt32);
- node->setCanExit(true);
- }
- break;
- }
-
- case DoubleAsInt32: {
- JSValue child = forNode(node->child1()).value();
- if (child && child.isNumber()) {
- double asDouble = child.asNumber();
- int32_t asInt = JSC::toInt32(asDouble);
- if (bitwise_cast<int64_t>(static_cast<double>(asInt)) == bitwise_cast<int64_t>(asDouble)
- && trySetConstant(node, JSValue(asInt))) {
- m_foundConstants = true;
- break;
- }
- }
- node->setCanExit(true);
- forNode(node).set(SpecInt32);
- break;
- }
-
- case ValueToInt32: {
- JSValue child = forNode(node->child1()).value();
- if (child && child.isNumber()) {
- bool constantWasSet;
- if (child.isInt32())
- constantWasSet = trySetConstant(node, child);
- else
- constantWasSet = trySetConstant(node, JSValue(JSC::toInt32(child.asDouble())));
- if (constantWasSet) {
- m_foundConstants = true;
- break;
- }
- }
-
- forNode(node).set(SpecInt32);
- break;
- }
- case Int32ToDouble:
- case ForwardInt32ToDouble: {
- JSValue child = forNode(node->child1()).value();
- if (child && child.isNumber()
- && trySetConstant(node, JSValue(JSValue::EncodeAsDouble, child.asNumber()))) {
- m_foundConstants = true;
- break;
- }
- if (isInt32Speculation(forNode(node->child1()).m_type))
- forNode(node).set(SpecDoubleReal);
- else
- forNode(node).set(SpecDouble);
- break;
- }
-
- case ValueAdd:
- case ArithAdd: {
- JSValue left = forNode(node->child1()).value();
- JSValue right = forNode(node->child2()).value();
- if (left && right && left.isNumber() && right.isNumber()
- && trySetConstant(node, JSValue(left.asNumber() + right.asNumber()))) {
- m_foundConstants = true;
- break;
- }
- switch (node->binaryUseKind()) {
- case Int32Use:
- forNode(node).set(SpecInt32);
- if (!nodeCanTruncateInteger(node->arithNodeFlags()))
- node->setCanExit(true);
- break;
- case NumberUse:
- if (isRealNumberSpeculation(forNode(node->child1()).m_type)
- && isRealNumberSpeculation(forNode(node->child2()).m_type))
- forNode(node).set(SpecDoubleReal);
- else
- forNode(node).set(SpecDouble);
- break;
- default:
- RELEASE_ASSERT(node->op() == ValueAdd);
- clobberWorld(node->codeOrigin, indexInBlock);
- forNode(node).set(SpecString | SpecInt32 | SpecNumber);
- break;
- }
- break;
- }
-
- case MakeRope: {
- forNode(node).set(m_graph.m_vm.stringStructure.get());
- break;
- }
-
- case ArithSub: {
- JSValue left = forNode(node->child1()).value();
- JSValue right = forNode(node->child2()).value();
- if (left && right && left.isNumber() && right.isNumber()
- && trySetConstant(node, JSValue(left.asNumber() - right.asNumber()))) {
- m_foundConstants = true;
- break;
- }
- switch (node->binaryUseKind()) {
- case Int32Use:
- forNode(node).set(SpecInt32);
- if (!nodeCanTruncateInteger(node->arithNodeFlags()))
- node->setCanExit(true);
- break;
- case NumberUse:
- forNode(node).set(SpecDouble);
- break;
- default:
- RELEASE_ASSERT_NOT_REACHED();
- break;
- }
- break;
- }
-
- case ArithNegate: {
- JSValue child = forNode(node->child1()).value();
- if (child && child.isNumber()
- && trySetConstant(node, JSValue(-child.asNumber()))) {
- m_foundConstants = true;
- break;
- }
- switch (node->child1().useKind()) {
- case Int32Use:
- forNode(node).set(SpecInt32);
- if (!nodeCanTruncateInteger(node->arithNodeFlags()))
- node->setCanExit(true);
- break;
- case NumberUse:
- forNode(node).set(SpecDouble);
- break;
- default:
- RELEASE_ASSERT_NOT_REACHED();
- break;
- }
- break;
- }
-
- case ArithMul: {
- JSValue left = forNode(node->child1()).value();
- JSValue right = forNode(node->child2()).value();
- if (left && right && left.isNumber() && right.isNumber()
- && trySetConstant(node, JSValue(left.asNumber() * right.asNumber()))) {
- m_foundConstants = true;
- break;
- }
- switch (node->binaryUseKind()) {
- case Int32Use:
- forNode(node).set(SpecInt32);
- if (!nodeCanTruncateInteger(node->arithNodeFlags())
- || !nodeCanIgnoreNegativeZero(node->arithNodeFlags()))
- node->setCanExit(true);
- break;
- case NumberUse:
- if (isRealNumberSpeculation(forNode(node->child1()).m_type)
- || isRealNumberSpeculation(forNode(node->child2()).m_type))
- forNode(node).set(SpecDoubleReal);
- else
- forNode(node).set(SpecDouble);
- break;
- default:
- RELEASE_ASSERT_NOT_REACHED();
- break;
- }
- break;
- }
- case ArithIMul: {
- forNode(node).set(SpecInt32);
- break;
- }
-
- case ArithDiv:
- case ArithMin:
- case ArithMax:
- case ArithMod: {
- JSValue left = forNode(node->child1()).value();
- JSValue right = forNode(node->child2()).value();
- if (left && right && left.isNumber() && right.isNumber()) {
- double a = left.asNumber();
- double b = right.asNumber();
- bool constantWasSet;
- switch (node->op()) {
- case ArithDiv:
- constantWasSet = trySetConstant(node, JSValue(a / b));
- break;
- case ArithMin:
- constantWasSet = trySetConstant(node, JSValue(a < b ? a : (b <= a ? b : a + b)));
- break;
- case ArithMax:
- constantWasSet = trySetConstant(node, JSValue(a > b ? a : (b >= a ? b : a + b)));
- break;
- case ArithMod:
- constantWasSet = trySetConstant(node, JSValue(fmod(a, b)));
- break;
- default:
- RELEASE_ASSERT_NOT_REACHED();
- constantWasSet = false;
- break;
- }
- if (constantWasSet) {
- m_foundConstants = true;
- break;
- }
- }
- switch (node->binaryUseKind()) {
- case Int32Use:
- forNode(node).set(SpecInt32);
- node->setCanExit(true);
- break;
- case NumberUse:
- forNode(node).set(SpecDouble);
- break;
- default:
- RELEASE_ASSERT_NOT_REACHED();
- break;
- }
- break;
- }
-
- case ArithAbs: {
- JSValue child = forNode(node->child1()).value();
- if (child && child.isNumber()
- && trySetConstant(node, JSValue(fabs(child.asNumber())))) {
- m_foundConstants = true;
- break;
- }
- switch (node->child1().useKind()) {
- case Int32Use:
- forNode(node).set(SpecInt32);
- node->setCanExit(true);
- break;
- case NumberUse:
- forNode(node).set(SpecDouble);
- break;
- default:
- RELEASE_ASSERT_NOT_REACHED();
- break;
- }
- break;
- }
-
- case ArithSqrt: {
- JSValue child = forNode(node->child1()).value();
- if (child && child.isNumber()
- && trySetConstant(node, JSValue(sqrt(child.asNumber())))) {
- m_foundConstants = true;
- break;
- }
- forNode(node).set(SpecDouble);
- break;
- }
-
- case LogicalNot: {
- bool didSetConstant = false;
- switch (booleanResult(node, forNode(node->child1()))) {
- case DefinitelyTrue:
- didSetConstant = trySetConstant(node, jsBoolean(false));
- break;
- case DefinitelyFalse:
- didSetConstant = trySetConstant(node, jsBoolean(true));
- break;
- default:
- break;
- }
- if (didSetConstant) {
- m_foundConstants = true;
- break;
- }
- switch (node->child1().useKind()) {
- case BooleanUse:
- case Int32Use:
- case NumberUse:
- case UntypedUse:
- break;
- case ObjectOrOtherUse:
- node->setCanExit(true);
- break;
- default:
- RELEASE_ASSERT_NOT_REACHED();
- break;
- }
- forNode(node).set(SpecBoolean);
- break;
- }
-
- case IsUndefined:
- case IsBoolean:
- case IsNumber:
- case IsString:
- case IsObject:
- case IsFunction: {
- node->setCanExit(node->op() == IsUndefined && m_codeBlock->globalObjectFor(node->codeOrigin)->masqueradesAsUndefinedWatchpoint()->isStillValid());
- JSValue child = forNode(node->child1()).value();
- if (child) {
- bool constantWasSet;
- switch (node->op()) {
- case IsUndefined:
- if (m_codeBlock->globalObjectFor(node->codeOrigin)->masqueradesAsUndefinedWatchpoint()->isStillValid()) {
- constantWasSet = trySetConstant(node, jsBoolean(
- child.isCell()
- ? false
- : child.isUndefined()));
- } else {
- constantWasSet = trySetConstant(node, jsBoolean(
- child.isCell()
- ? child.asCell()->structure()->masqueradesAsUndefined(m_codeBlock->globalObjectFor(node->codeOrigin))
- : child.isUndefined()));
- }
- break;
- case IsBoolean:
- constantWasSet = trySetConstant(node, jsBoolean(child.isBoolean()));
- break;
- case IsNumber:
- constantWasSet = trySetConstant(node, jsBoolean(child.isNumber()));
- break;
- case IsString:
- constantWasSet = trySetConstant(node, jsBoolean(isJSString(child)));
- break;
- case IsObject:
- if (child.isNull() || !child.isObject()) {
- constantWasSet = trySetConstant(node, jsBoolean(child.isNull()));
- break;
- }
- default:
- constantWasSet = false;
- break;
- }
- if (constantWasSet) {
- m_foundConstants = true;
- break;
- }
- }
- forNode(node).set(SpecBoolean);
- break;
- }
- case TypeOf: {
- VM* vm = m_codeBlock->vm();
- JSValue child = forNode(node->child1()).value();
- AbstractValue& abstractChild = forNode(node->child1());
- if (child) {
- JSValue typeString = jsTypeStringForValue(*vm, m_codeBlock->globalObjectFor(node->codeOrigin), child);
- if (trySetConstant(node, typeString)) {
- m_foundConstants = true;
- break;
- }
- } else if (isNumberSpeculation(abstractChild.m_type)) {
- if (trySetConstant(node, vm->smallStrings.numberString())) {
- forNode(node->child1()).filter(SpecNumber);
- m_foundConstants = true;
- break;
- }
- } else if (isStringSpeculation(abstractChild.m_type)) {
- if (trySetConstant(node, vm->smallStrings.stringString())) {
- forNode(node->child1()).filter(SpecString);
- m_foundConstants = true;
- break;
- }
- } else if (isFinalObjectSpeculation(abstractChild.m_type) || isArraySpeculation(abstractChild.m_type) || isArgumentsSpeculation(abstractChild.m_type)) {
- if (trySetConstant(node, vm->smallStrings.objectString())) {
- forNode(node->child1()).filter(SpecFinalObject | SpecArray | SpecArguments);
- m_foundConstants = true;
- break;
- }
- } else if (isFunctionSpeculation(abstractChild.m_type)) {
- if (trySetConstant(node, vm->smallStrings.functionString())) {
- forNode(node->child1()).filter(SpecFunction);
- m_foundConstants = true;
- break;
- }
- } else if (isBooleanSpeculation(abstractChild.m_type)) {
- if (trySetConstant(node, vm->smallStrings.booleanString())) {
- forNode(node->child1()).filter(SpecBoolean);
- m_foundConstants = true;
- break;
- }
- }
- switch (node->child1().useKind()) {
- case StringUse:
- case CellUse:
- node->setCanExit(true);
- break;
- case UntypedUse:
- break;
- default:
- RELEASE_ASSERT_NOT_REACHED();
- break;
- }
- forNode(node).set(m_graph.m_vm.stringStructure.get());
- break;
- }
-
- case CompareLess:
- case CompareLessEq:
- case CompareGreater:
- case CompareGreaterEq:
- case CompareEq:
- case CompareEqConstant: {
- bool constantWasSet = false;
- JSValue leftConst = forNode(node->child1()).value();
- JSValue rightConst = forNode(node->child2()).value();
- if (leftConst && rightConst && leftConst.isNumber() && rightConst.isNumber()) {
- double a = leftConst.asNumber();
- double b = rightConst.asNumber();
- switch (node->op()) {
- case CompareLess:
- constantWasSet = trySetConstant(node, jsBoolean(a < b));
- break;
- case CompareLessEq:
- constantWasSet = trySetConstant(node, jsBoolean(a <= b));
- break;
- case CompareGreater:
- constantWasSet = trySetConstant(node, jsBoolean(a > b));
- break;
- case CompareGreaterEq:
- constantWasSet = trySetConstant(node, jsBoolean(a >= b));
- break;
- case CompareEq:
- constantWasSet = trySetConstant(node, jsBoolean(a == b));
- break;
- default:
- RELEASE_ASSERT_NOT_REACHED();
- constantWasSet = false;
- break;
- }
- }
-
- if (!constantWasSet && (node->op() == CompareEqConstant || node->op() == CompareEq)) {
- SpeculatedType leftType = forNode(node->child1()).m_type;
- SpeculatedType rightType = forNode(node->child2()).m_type;
- if ((isInt32Speculation(leftType) && isOtherSpeculation(rightType))
- || (isOtherSpeculation(leftType) && isInt32Speculation(rightType)))
- constantWasSet = trySetConstant(node, jsBoolean(false));
- }
-
- if (constantWasSet) {
- m_foundConstants = true;
- break;
- }
-
- forNode(node).set(SpecBoolean);
-
- // This is overly conservative. But the only thing this prevents is store elimination,
- // and how likely is it, really, that you'll have redundant stores across a comparison
- // operation? Comparison operations are typically at the end of basic blocks, so
- // unless we have global store elimination (super unlikely given how unprofitable that
- // optimization is to begin with), you aren't going to be wanting to store eliminate
- // across an equality op.
- node->setCanExit(true);
- break;
- }
-
- case CompareStrictEq:
- case CompareStrictEqConstant: {
- Node* leftNode = node->child1().node();
- Node* rightNode = node->child2().node();
- JSValue left = forNode(leftNode).value();
- JSValue right = forNode(rightNode).value();
- if (left && right && left.isNumber() && right.isNumber()
- && trySetConstant(node, jsBoolean(left.asNumber() == right.asNumber()))) {
- m_foundConstants = true;
- break;
- }
- forNode(node).set(SpecBoolean);
- node->setCanExit(true); // This is overly conservative.
- break;
- }
-
- case StringCharCodeAt:
- node->setCanExit(true);
- forNode(node).set(SpecInt32);
- break;
-
- case StringFromCharCode:
- forNode(node).set(SpecString);
- break;
- case StringCharAt:
- node->setCanExit(true);
- forNode(node).set(m_graph.m_vm.stringStructure.get());
- break;
-
- case GetByVal: {
- node->setCanExit(true);
- switch (node->arrayMode().type()) {
- case Array::SelectUsingPredictions:
- case Array::Unprofiled:
- case Array::Undecided:
- RELEASE_ASSERT_NOT_REACHED();
- break;
- case Array::ForceExit:
- m_isValid = false;
- break;
- case Array::Generic:
- clobberWorld(node->codeOrigin, indexInBlock);
- forNode(node).makeTop();
- break;
- case Array::String:
- forNode(node).set(m_graph.m_vm.stringStructure.get());
- break;
- case Array::Arguments:
- forNode(node).makeTop();
- break;
- case Array::Int32:
- if (node->arrayMode().isOutOfBounds()) {
- clobberWorld(node->codeOrigin, indexInBlock);
- forNode(node).makeTop();
- } else
- forNode(node).set(SpecInt32);
- break;
- case Array::Double:
- if (node->arrayMode().isOutOfBounds()) {
- clobberWorld(node->codeOrigin, indexInBlock);
- forNode(node).makeTop();
- } else if (node->arrayMode().isSaneChain())
- forNode(node).set(SpecDouble);
- else
- forNode(node).set(SpecDoubleReal);
- break;
- case Array::Contiguous:
- case Array::ArrayStorage:
- case Array::SlowPutArrayStorage:
- if (node->arrayMode().isOutOfBounds())
- clobberWorld(node->codeOrigin, indexInBlock);
- forNode(node).makeTop();
- break;
- case Array::Int8Array:
- forNode(node).set(SpecInt32);
- break;
- case Array::Int16Array:
- forNode(node).set(SpecInt32);
- break;
- case Array::Int32Array:
- forNode(node).set(SpecInt32);
- break;
- case Array::Uint8Array:
- forNode(node).set(SpecInt32);
- break;
- case Array::Uint8ClampedArray:
- forNode(node).set(SpecInt32);
- break;
- case Array::Uint16Array:
- forNode(node).set(SpecInt32);
- break;
- case Array::Uint32Array:
- if (node->shouldSpeculateInteger())
- forNode(node).set(SpecInt32);
- else
- forNode(node).set(SpecDouble);
- break;
- case Array::Float32Array:
- forNode(node).set(SpecDouble);
- break;
- case Array::Float64Array:
- forNode(node).set(SpecDouble);
- break;
- default:
- RELEASE_ASSERT_NOT_REACHED();
- break;
- }
- break;
- }
-
- case PutByVal:
- case PutByValAlias: {
- node->setCanExit(true);
- switch (node->arrayMode().modeForPut().type()) {
- case Array::ForceExit:
- m_isValid = false;
- break;
- case Array::Generic:
- clobberWorld(node->codeOrigin, indexInBlock);
- break;
- case Array::Int32:
- if (node->arrayMode().isOutOfBounds())
- clobberWorld(node->codeOrigin, indexInBlock);
- break;
- case Array::Double:
- if (node->arrayMode().isOutOfBounds())
- clobberWorld(node->codeOrigin, indexInBlock);
- break;
- case Array::Contiguous:
- case Array::ArrayStorage:
- if (node->arrayMode().isOutOfBounds())
- clobberWorld(node->codeOrigin, indexInBlock);
- break;
- case Array::SlowPutArrayStorage:
- if (node->arrayMode().mayStoreToHole())
- clobberWorld(node->codeOrigin, indexInBlock);
- break;
- default:
- break;
- }
- break;
- }
-
- case ArrayPush:
- node->setCanExit(true);
- clobberWorld(node->codeOrigin, indexInBlock);
- forNode(node).set(SpecNumber);
- break;
-
- case ArrayPop:
- node->setCanExit(true);
- clobberWorld(node->codeOrigin, indexInBlock);
- forNode(node).makeTop();
- break;
-
- case RegExpExec:
- forNode(node).makeTop();
- break;
- case RegExpTest:
- forNode(node).set(SpecBoolean);
- break;
-
- case Jump:
- break;
-
- case Branch: {
- Node* child = node->child1().node();
- BooleanResult result = booleanResult(node, forNode(child));
- if (result == DefinitelyTrue) {
- m_branchDirection = TakeTrue;
- break;
- }
- if (result == DefinitelyFalse) {
- m_branchDirection = TakeFalse;
- break;
- }
- // FIXME: The above handles the trivial cases of sparse conditional
- // constant propagation, but we can do better:
- // We can specialize the source variable's value on each direction of
- // the branch.
- node->setCanExit(true); // This is overly conservative.
- m_branchDirection = TakeBoth;
- break;
- }
-
- case Return:
- m_isValid = false;
- break;
-
- case Throw:
- case ThrowReferenceError:
- m_isValid = false;
- node->setCanExit(true);
- break;
-
- case ToPrimitive: {
- JSValue childConst = forNode(node->child1()).value();
- if (childConst && childConst.isNumber() && trySetConstant(node, childConst)) {
- m_foundConstants = true;
- break;
- }
-
- ASSERT(node->child1().useKind() == UntypedUse);
-
- AbstractValue& source = forNode(node->child1());
- AbstractValue& destination = forNode(node);
-
- // NB. The more canonical way of writing this would have been:
- //
- // destination = source;
- // if (destination.m_type & !(SpecNumber | SpecString | SpecBoolean)) {
- // destination.filter(SpecNumber | SpecString | SpecBoolean);
- // AbstractValue string;
- // string.set(vm->stringStructure);
- // destination.merge(string);
- // }
- //
- // The reason why this would, in most other cases, have been better is that
- // then destination would preserve any non-SpeculatedType knowledge of source.
- // As it stands, the code below forgets any non-SpeculatedType knowledge that
- // source would have had. Fortunately, though, for things like strings and
- // numbers and booleans, we don't care about the non-SpeculatedType knowedge:
- // the structure won't tell us anything we don't already know, and neither
- // will ArrayModes. And if the source was a meaningful constant then we
- // would have handled that above. Unfortunately, this does mean that
- // ToPrimitive will currently forget string constants. But that's not a big
- // deal since we don't do any optimization on those currently.
-
- clobberWorld(node->codeOrigin, indexInBlock);
-
- SpeculatedType type = source.m_type;
- if (type & ~(SpecNumber | SpecString | SpecBoolean))
- type = (SpecTop & ~SpecCell) | SpecString;
- destination.set(type);
- break;
- }
-
- case ToString: {
- switch (node->child1().useKind()) {
- case StringObjectUse:
- // This also filters that the StringObject has the primordial StringObject
- // structure.
- forNode(node->child1()).filter(m_graph.globalObjectFor(node->codeOrigin)->stringObjectStructure());
- node->setCanExit(true); // We could be more precise but it's likely not worth it.
- break;
- case StringOrStringObjectUse:
- node->setCanExit(true); // We could be more precise but it's likely not worth it.
- break;
- case CellUse:
- case UntypedUse:
- clobberWorld(node->codeOrigin, indexInBlock);
- break;
- default:
- RELEASE_ASSERT_NOT_REACHED();
- break;
- }
- forNode(node).set(m_graph.m_vm.stringStructure.get());
- break;
- }
-
- case NewStringObject: {
- #if ENABLE(DETACHED_JIT)
- #pragma message "[SECURE JSCORE] try to enable this assert..."
- #else
- ASSERT(node->structure()->classInfo() == &StringObject::s_info);
- #endif
- forNode(node).set(node->structure());
- break;
- }
-
- case NewArray:
- node->setCanExit(true);
- forNode(node).set(m_graph.globalObjectFor(node->codeOrigin)->arrayStructureForIndexingTypeDuringAllocation(node->indexingType()));
- m_haveStructures = true;
- break;
-
- case NewArrayBuffer:
- node->setCanExit(true);
- forNode(node).set(m_graph.globalObjectFor(node->codeOrigin)->arrayStructureForIndexingTypeDuringAllocation(node->indexingType()));
- m_haveStructures = true;
- break;
- case NewArrayWithSize:
- node->setCanExit(true);
- forNode(node).set(SpecArray);
- m_haveStructures = true;
- break;
-
- case NewRegexp:
- forNode(node).set(m_graph.globalObjectFor(node->codeOrigin)->regExpStructure());
- m_haveStructures = true;
- break;
-
- case ConvertThis: {
- AbstractValue& source = forNode(node->child1());
- AbstractValue& destination = forNode(node);
-
- destination = source;
- destination.merge(SpecObjectOther);
- break;
- }
- case CreateThis: {
- forNode(node).set(SpecFinalObject);
- break;
- }
-
- case AllocationProfileWatchpoint:
- node->setCanExit(true);
- break;
- case NewObject:
- forNode(node).set(node->structure());
- m_haveStructures = true;
- break;
-
- case CreateActivation:
- forNode(node).set(m_codeBlock->globalObjectFor(node->codeOrigin)->activationStructure());
- m_haveStructures = true;
- break;
-
- case CreateArguments:
- forNode(node).set(m_codeBlock->globalObjectFor(node->codeOrigin)->argumentsStructure());
- m_haveStructures = true;
- break;
-
- case TearOffActivation:
- case TearOffArguments:
- // Does nothing that is user-visible.
- break;
- case CheckArgumentsNotCreated:
- if (isEmptySpeculation(
- m_variables.operand(
- m_graph.argumentsRegisterFor(node->codeOrigin)).m_type))
- m_foundConstants = true;
- else
- node->setCanExit(true);
- break;
-
- case GetMyArgumentsLength:
- // We know that this executable does not escape its arguments, so we can optimize
- // the arguments a bit. Note that this is not sufficient to force constant folding
- // of GetMyArgumentsLength, because GetMyArgumentsLength is a clobbering operation.
- // We perform further optimizations on this later on.
- if (node->codeOrigin.inlineCallFrame)
- forNode(node).set(jsNumber(node->codeOrigin.inlineCallFrame->arguments.size() - 1));
- else
- forNode(node).set(SpecInt32);
- node->setCanExit(
- !isEmptySpeculation(
- m_variables.operand(
- m_graph.argumentsRegisterFor(node->codeOrigin)).m_type));
- break;
-
- case GetMyArgumentsLengthSafe:
- // This potentially clobbers all structures if the arguments object had a getter
- // installed on the length property.
- clobberWorld(node->codeOrigin, indexInBlock);
- // We currently make no guarantee about what this returns because it does not
- // speculate that the length property is actually a length.
- forNode(node).makeTop();
- break;
-
- case GetMyArgumentByVal:
- node->setCanExit(true);
- // We know that this executable does not escape its arguments, so we can optimize
- // the arguments a bit. Note that this ends up being further optimized by the
- // ArgumentsSimplificationPhase.
- forNode(node).makeTop();
- break;
-
- case GetMyArgumentByValSafe:
- node->setCanExit(true);
- // This potentially clobbers all structures if the property we're accessing has
- // a getter. We don't speculate against this.
- clobberWorld(node->codeOrigin, indexInBlock);
- // And the result is unknown.
- forNode(node).makeTop();
- break;
-
- case NewFunction: {
- AbstractValue& value = forNode(node);
- value = forNode(node->child1());
-
- if (!(value.m_type & SpecEmpty)) {
- m_foundConstants = true;
- break;
- }
- value.set((value.m_type & ~SpecEmpty) | SpecFunction);
- break;
- }
- case NewFunctionExpression:
- case NewFunctionNoCheck:
- forNode(node).set(m_codeBlock->globalObjectFor(node->codeOrigin)->functionStructure());
- break;
-
- case GetCallee:
- forNode(node).set(SpecFunction);
- break;
-
- case SetCallee:
- case SetMyScope:
- break;
-
- case GetScope: // FIXME: We could get rid of these if we know that the JSFunction is a constant. https://bugs.webkit.org/show_bug.cgi?id=106202
- case GetMyScope:
- case SkipTopScope:
- forNode(node).set(SpecObjectOther);
- break;
- case SkipScope: {
- JSValue child = forNode(node->child1()).value();
- if (child && trySetConstant(node, JSValue(jsCast<JSScope*>(child.asCell())->next()))) {
- m_foundConstants = true;
- break;
- }
- forNode(node).set(SpecObjectOther);
- break;
- }
- case GetScopeRegisters:
- forNode(node).clear(); // The result is not a JS value.
- break;
- case GetScopedVar:
- forNode(node).makeTop();
- break;
-
- case PutScopedVar:
- clobberCapturedVars(node->codeOrigin);
- break;
-
- case GetById:
- case GetByIdFlush:
- node->setCanExit(true);
- if (!node->prediction()) {
- m_isValid = false;
- break;
- }
- if (isCellSpeculation(node->child1()->prediction())) {
- if (Structure* structure = forNode(node->child1()).bestProvenStructure()) {
- GetByIdStatus status = GetByIdStatus::computeFor(
- m_graph.m_vm, structure,
- m_graph.m_codeBlock->identifier(node->identifierNumber()));
- if (status.isSimple()) {
- // Assert things that we can't handle and that the computeFor() method
- // above won't be able to return.
- ASSERT(status.structureSet().size() == 1);
- ASSERT(status.chain().isEmpty());
-
- if (status.specificValue())
- forNode(node).set(status.specificValue());
- else
- forNode(node).makeTop();
- forNode(node->child1()).filter(status.structureSet());
-
- m_foundConstants = true;
- break;
- }
- }
- }
- clobberWorld(node->codeOrigin, indexInBlock);
- forNode(node).makeTop();
- break;
-
- case GetArrayLength:
- node->setCanExit(true); // Lies, but it's true for the common case of JSArray, so it's good enough.
- forNode(node).set(SpecInt32);
- break;
-
- case CheckExecutable: {
- // FIXME: We could track executables in AbstractValue, which would allow us to get rid of these checks
- // more thoroughly. https://bugs.webkit.org/show_bug.cgi?id=106200
- // FIXME: We could eliminate these entirely if we know the exact value that flows into this.
- // https://bugs.webkit.org/show_bug.cgi?id=106201
- node->setCanExit(true);
- break;
- }
- case CheckStructure:
- case ForwardCheckStructure: {
- // FIXME: We should be able to propagate the structure sets of constants (i.e. prototypes).
- AbstractValue& value = forNode(node->child1());
- ASSERT(!(value.m_type & ~SpecCell)); // Edge filtering should have already ensured this.
- // If this structure check is attempting to prove knowledge already held in
- // the futurePossibleStructure set then the constant folding phase should
- // turn this into a watchpoint instead.
- StructureSet& set = node->structureSet();
- if (value.m_futurePossibleStructure.isSubsetOf(set)
- || value.m_currentKnownStructure.isSubsetOf(set))
- m_foundConstants = true;
- if (!value.m_currentKnownStructure.isSubsetOf(set))
- node->setCanExit(true);
- value.filter(set);
- m_haveStructures = true;
- break;
- }
-
- case StructureTransitionWatchpoint:
- case ForwardStructureTransitionWatchpoint: {
- AbstractValue& value = forNode(node->child1());
- // It's only valid to issue a structure transition watchpoint if we already
- // know that the watchpoint covers a superset of the structures known to
- // belong to the set of future structures that this value may have.
- // Currently, we only issue singleton watchpoints (that check one structure)
- // and our futurePossibleStructure set can only contain zero, one, or an
- // infinity of structures.
- ASSERT(value.m_futurePossibleStructure.isSubsetOf(StructureSet(node->structure())));
-
- value.filter(node->structure());
- m_haveStructures = true;
- node->setCanExit(true);
- break;
- }
-
- case PutStructure:
- case PhantomPutStructure:
- if (!forNode(node->child1()).m_currentKnownStructure.isClear()) {
- clobberStructures(indexInBlock);
- forNode(node->child1()).set(node->structureTransitionData().newStructure);
- m_haveStructures = true;
- }
- break;
- case GetButterfly:
- case AllocatePropertyStorage:
- case ReallocatePropertyStorage:
- forNode(node).clear(); // The result is not a JS value.
- break;
- case CheckArray: {
- if (node->arrayMode().alreadyChecked(m_graph, node, forNode(node->child1()))) {
- m_foundConstants = true;
- break;
- }
- node->setCanExit(true); // Lies, but this is followed by operations (like GetByVal) that always exit, so there is no point in us trying to be clever here.
- switch (node->arrayMode().type()) {
- case Array::String:
- forNode(node->child1()).filter(SpecString);
- break;
- case Array::Int32:
- case Array::Double:
- case Array::Contiguous:
- case Array::ArrayStorage:
- case Array::SlowPutArrayStorage:
- break;
- case Array::Arguments:
- forNode(node->child1()).filter(SpecArguments);
- break;
- case Array::Int8Array:
- forNode(node->child1()).filter(SpecInt8Array);
- break;
- case Array::Int16Array:
- forNode(node->child1()).filter(SpecInt16Array);
- break;
- case Array::Int32Array:
- forNode(node->child1()).filter(SpecInt32Array);
- break;
- case Array::Uint8Array:
- forNode(node->child1()).filter(SpecUint8Array);
- break;
- case Array::Uint8ClampedArray:
- forNode(node->child1()).filter(SpecUint8ClampedArray);
- break;
- case Array::Uint16Array:
- forNode(node->child1()).filter(SpecUint16Array);
- break;
- case Array::Uint32Array:
- forNode(node->child1()).filter(SpecUint32Array);
- break;
- case Array::Float32Array:
- forNode(node->child1()).filter(SpecFloat32Array);
- break;
- case Array::Float64Array:
- forNode(node->child1()).filter(SpecFloat64Array);
- break;
- default:
- RELEASE_ASSERT_NOT_REACHED();
- break;
- }
- forNode(node->child1()).filterArrayModes(node->arrayMode().arrayModesThatPassFiltering());
- m_haveStructures = true;
- break;
- }
- case Arrayify: {
- if (node->arrayMode().alreadyChecked(m_graph, node, forNode(node->child1()))) {
- m_foundConstants = true;
- break;
- }
- ASSERT(node->arrayMode().conversion() == Array::Convert
- || node->arrayMode().conversion() == Array::RageConvert);
- node->setCanExit(true);
- clobberStructures(indexInBlock);
- forNode(node->child1()).filterArrayModes(node->arrayMode().arrayModesThatPassFiltering());
- m_haveStructures = true;
- break;
- }
- case ArrayifyToStructure: {
- AbstractValue& value = forNode(node->child1());
- StructureSet set = node->structure();
- if (value.m_futurePossibleStructure.isSubsetOf(set)
- || value.m_currentKnownStructure.isSubsetOf(set))
- m_foundConstants = true;
- node->setCanExit(true);
- clobberStructures(indexInBlock);
- value.filter(set);
- m_haveStructures = true;
- break;
- }
- case GetIndexedPropertyStorage: {
- forNode(node).clear();
- break;
- }
- case GetByOffset: {
- forNode(node).makeTop();
- break;
- }
-
- case PutByOffset: {
- break;
- }
-
- case CheckFunction: {
- JSValue value = forNode(node->child1()).value();
- if (value == node->function()) {
- m_foundConstants = true;
- ASSERT(value);
- break;
- }
-
- node->setCanExit(true); // Lies! We can do better.
- forNode(node->child1()).filterByValue(node->function());
- break;
- }
-
- case PutById:
- case PutByIdDirect:
- node->setCanExit(true);
- if (Structure* structure = forNode(node->child1()).bestProvenStructure()) {
- PutByIdStatus status = PutByIdStatus::computeFor(
- m_graph.m_vm,
- m_graph.globalObjectFor(node->codeOrigin),
- structure,
- m_graph.m_codeBlock->identifier(node->identifierNumber()),
- node->op() == PutByIdDirect);
- if (status.isSimpleReplace()) {
- forNode(node->child1()).filter(structure);
- m_foundConstants = true;
- break;
- }
- if (status.isSimpleTransition()) {
- clobberStructures(indexInBlock);
- forNode(node->child1()).set(status.newStructure());
- m_haveStructures = true;
- m_foundConstants = true;
- break;
- }
- }
- clobberWorld(node->codeOrigin, indexInBlock);
- break;
-
- case GetGlobalVar:
- forNode(node).makeTop();
- break;
-
- case GlobalVarWatchpoint:
- node->setCanExit(true);
- break;
-
- case PutGlobalVar:
- case PutGlobalVarCheck:
- break;
-
- case CheckHasInstance:
- node->setCanExit(true);
- // Sadly, we don't propagate the fact that we've done CheckHasInstance
- break;
-
- case InstanceOf:
- node->setCanExit(true);
- // Again, sadly, we don't propagate the fact that we've done InstanceOf
- forNode(node).set(SpecBoolean);
- break;
-
- case Phi:
- case Flush:
- case PhantomLocal:
- case Breakpoint:
- break;
-
- case Call:
- case Construct:
- case Resolve:
- case ResolveBase:
- case ResolveBaseStrictPut:
- case ResolveGlobal:
- node->setCanExit(true);
- clobberWorld(node->codeOrigin, indexInBlock);
- forNode(node).makeTop();
- break;
- case GarbageValue:
- clobberWorld(node->codeOrigin, indexInBlock);
- forNode(node).makeTop();
- break;
- case ForceOSRExit:
- node->setCanExit(true);
- m_isValid = false;
- break;
-
- case CheckWatchdogTimer:
- node->setCanExit(true);
- break;
-
- case Phantom:
- case InlineStart:
- case Nop:
- case CountExecution:
- break;
-
- case LastNodeType:
- RELEASE_ASSERT_NOT_REACHED();
- break;
- }
-
- return m_isValid;
- }
- bool AbstractState::executeEffects(unsigned indexInBlock)
- {
- return executeEffects(indexInBlock, m_block->at(indexInBlock));
- }
- bool AbstractState::execute(unsigned indexInBlock)
- {
- Node* node = m_block->at(indexInBlock);
- if (!startExecuting(node))
- return true;
-
- executeEdges(node);
- return executeEffects(indexInBlock, node);
- }
- inline void AbstractState::clobberWorld(const CodeOrigin& codeOrigin, unsigned indexInBlock)
- {
- clobberCapturedVars(codeOrigin);
- clobberStructures(indexInBlock);
- }
- inline void AbstractState::clobberCapturedVars(const CodeOrigin& codeOrigin)
- {
- if (codeOrigin.inlineCallFrame) {
- const BitVector_shared& capturedVars = codeOrigin.inlineCallFrame->capturedVars;
- for (size_t i = capturedVars.size(); i--;) {
- if (!capturedVars.quickGet(i))
- continue;
- m_variables.local(i).makeTop();
- }
- } else {
- for (size_t i = m_codeBlock->m_numVars; i--;) {
- if (m_codeBlock->isCaptured(i))
- m_variables.local(i).makeTop();
- }
- }
- for (size_t i = m_variables.numberOfArguments(); i--;) {
- if (m_codeBlock->isCaptured(argumentToOperand(i)))
- m_variables.argument(i).makeTop();
- }
- }
- inline void AbstractState::clobberStructures(unsigned indexInBlock)
- {
- if (!m_haveStructures)
- return;
- for (size_t i = indexInBlock + 1; i--;)
- forNode(m_block->at(i)).clobberStructures();
- for (size_t i = m_variables.numberOfArguments(); i--;)
- m_variables.argument(i).clobberStructures();
- for (size_t i = m_variables.numberOfLocals(); i--;)
- m_variables.local(i).clobberStructures();
- m_haveStructures = false;
- m_didClobber = true;
- }
- inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, AbstractValue& inVariable, Node* node)
- {
- if (!node)
- return false;
-
- AbstractValue source;
-
- if (node->variableAccessData()->isCaptured()) {
- // If it's captured then we know that whatever value was stored into the variable last is the
- // one we care about. This is true even if the variable at tail is dead, which might happen if
- // the last thing we did to the variable was a GetLocal and then ended up now using the
- // GetLocal's result.
-
- source = inVariable;
- #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLogF(" Transfering ");
- source.dump(WTF::dataFile());
- dataLogF(" from last access due to captured variable.\n");
- #endif
- } else {
- #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLogF(" It's live, node @%u.\n", node->index());
- #endif
-
- switch (node->op()) {
- case Phi:
- case SetArgument:
- case PhantomLocal:
- case Flush:
- // The block transfers the value from head to tail.
- source = inVariable;
- #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLogF(" Transfering ");
- source.dump(WTF::dataFile());
- dataLogF(" from head to tail.\n");
- #endif
- break;
-
- case GetLocal:
- // The block refines the value with additional speculations.
- source = forNode(node);
- #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLogF(" Refining to ");
- source.dump(WTF::dataFile());
- dataLogF("\n");
- #endif
- break;
-
- case SetLocal:
- // The block sets the variable, and potentially refines it, both
- // before and after setting it.
- if (node->variableAccessData()->shouldUseDoubleFormat()) {
- // FIXME: This unnecessarily loses precision.
- source.set(SpecDouble);
- } else
- source = forNode(node->child1());
- #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLogF(" Setting to ");
- source.dump(WTF::dataFile());
- dataLogF("\n");
- #endif
- break;
-
- default:
- RELEASE_ASSERT_NOT_REACHED();
- break;
- }
- }
-
- if (destination == source) {
- // Abstract execution did not change the output value of the variable, for this
- // basic block, on this iteration.
- #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLogF(" Not changed!\n");
- #endif
- return false;
- }
-
- // Abstract execution reached a new conclusion about the speculations reached about
- // this variable after execution of this basic block. Update the state, and return
- // true to indicate that the fixpoint must go on!
- destination = source;
- #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLogF(" Changed!\n");
- #endif
- return true;
- }
- inline bool AbstractState::merge(BasicBlock* from, BasicBlock* to)
- {
- ASSERT(from->variablesAtTail.numberOfArguments() == to->variablesAtHead.numberOfArguments());
- ASSERT(from->variablesAtTail.numberOfLocals() == to->variablesAtHead.numberOfLocals());
-
- bool changed = false;
-
- for (size_t argument = 0; argument < from->variablesAtTail.numberOfArguments(); ++argument) {
- AbstractValue& destination = to->valuesAtHead.argument(argument);
- changed |= mergeVariableBetweenBlocks(destination, from->valuesAtTail.argument(argument), to->variablesAtHead.argument(argument), from->variablesAtTail.argument(argument));
- }
-
- for (size_t local = 0; local < from->variablesAtTail.numberOfLocals(); ++local) {
- AbstractValue& destination = to->valuesAtHead.local(local);
- changed |= mergeVariableBetweenBlocks(destination, from->valuesAtTail.local(local), to->variablesAtHead.local(local), from->variablesAtTail.local(local));
- }
- if (!to->cfaHasVisited)
- changed = true;
-
- to->cfaShouldRevisit |= changed;
-
- return changed;
- }
- inline bool AbstractState::mergeToSuccessors(Graph& graph, BasicBlock* basicBlock)
- {
- Node* terminal = basicBlock->last();
-
- ASSERT(terminal->isTerminal());
-
- switch (terminal->op()) {
- case Jump: {
- ASSERT(basicBlock->cfaBranchDirection == InvalidBranchDirection);
- #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLogF(" Merging to block #%u.\n", terminal->takenBlockIndex());
- #endif
- return merge(basicBlock, graph.m_blocks[terminal->takenBlockIndex()].get());
- }
-
- case Branch: {
- ASSERT(basicBlock->cfaBranchDirection != InvalidBranchDirection);
- bool changed = false;
- #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLogF(" Merging to block #%u.\n", terminal->takenBlockIndex());
- #endif
- if (basicBlock->cfaBranchDirection != TakeFalse)
- changed |= merge(basicBlock, graph.m_blocks[terminal->takenBlockIndex()].get());
- #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLogF(" Merging to block #%u.\n", terminal->notTakenBlockIndex());
- #endif
- if (basicBlock->cfaBranchDirection != TakeTrue)
- changed |= merge(basicBlock, graph.m_blocks[terminal->notTakenBlockIndex()].get());
- return changed;
- }
-
- case Return:
- case Throw:
- case ThrowReferenceError:
- ASSERT(basicBlock->cfaBranchDirection == InvalidBranchDirection);
- return false;
-
- default:
- RELEASE_ASSERT_NOT_REACHED();
- return false;
- }
- }
- inline bool AbstractState::mergeVariableBetweenBlocks(AbstractValue& destination, AbstractValue& source, Node* destinationNode, Node* sourceNode)
- {
- if (!destinationNode)
- return false;
-
- ASSERT_UNUSED(sourceNode, sourceNode);
-
- // FIXME: We could do some sparse conditional propagation here!
-
- return destination.merge(source);
- }
- void AbstractState::dump(PrintStream& out)
- {
- bool first = true;
- for (size_t i = 0; i < m_block->size(); ++i) {
- Node* node = m_block->at(i);
- AbstractValue& value = forNode(node);
- if (value.isClear())
- continue;
- if (first)
- first = false;
- else
- out.printf(" ");
- out.printf("@%lu:", static_cast<unsigned long>(node->index()));
- value.dump(out);
- }
- }
- } } // namespace JSC::DFG
- #endif // ENABLE(DFG_JIT)
|