void propagate(Node& node) { if (!node.shouldGenerate()) return; NodeType op = node.op(); NodeFlags flags = node.flags() & NodeBackPropMask; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog(" %s @%u: %s ", Graph::opName(op), m_compileIndex, nodeFlagsAsString(flags)); #endif bool changed = false; switch (op) { case JSConstant: case WeakJSConstant: { changed |= setPrediction(speculationFromValue(m_graph.valueOfJSConstant(m_compileIndex))); break; } case GetLocal: { VariableAccessData* variableAccessData = node.variableAccessData(); SpeculatedType prediction = variableAccessData->prediction(); if (prediction) changed |= mergePrediction(prediction); changed |= variableAccessData->mergeFlags(flags); break; } case SetLocal: { VariableAccessData* variableAccessData = node.variableAccessData(); changed |= variableAccessData->predict(m_graph[node.child1()].prediction()); changed |= m_graph[node.child1()].mergeFlags(variableAccessData->flags()); break; } case Flush: { // Make sure that the analysis knows that flushed locals escape. VariableAccessData* variableAccessData = node.variableAccessData(); changed |= variableAccessData->mergeFlags(NodeUsedAsValue); break; } case BitAnd: case BitOr: case BitXor: case BitRShift: case BitLShift: case BitURShift: { changed |= setPrediction(SpecInt32); flags |= NodeUsedAsInt; flags &= ~(NodeUsedAsNumber | NodeNeedsNegZero); changed |= m_graph[node.child1()].mergeFlags(flags); changed |= m_graph[node.child2()].mergeFlags(flags); break; } case ValueToInt32: { changed |= setPrediction(SpecInt32); flags |= NodeUsedAsInt; flags &= ~(NodeUsedAsNumber | NodeNeedsNegZero); changed |= m_graph[node.child1()].mergeFlags(flags); break; } case ArrayPop: { changed |= mergePrediction(node.getHeapPrediction()); changed |= mergeDefaultFlags(node); break; } case ArrayPush: { changed |= mergePrediction(node.getHeapPrediction()); changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue); changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsValue); break; } case RegExpExec: case RegExpTest: { changed |= mergePrediction(node.getHeapPrediction()); changed |= mergeDefaultFlags(node); break; } case StringCharCodeAt: { changed |= mergePrediction(SpecInt32); changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue); changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt); break; } case ArithMod: { SpeculatedType left = m_graph[node.child1()].prediction(); SpeculatedType right = m_graph[node.child2()].prediction(); if (left && right) { if (isInt32Speculation(mergeSpeculations(left, right)) && nodeCanSpeculateInteger(node.arithNodeFlags())) changed |= mergePrediction(SpecInt32); else changed |= mergePrediction(SpecDouble); } flags |= NodeUsedAsValue; changed |= m_graph[node.child1()].mergeFlags(flags); changed |= m_graph[node.child2()].mergeFlags(flags); break; } case UInt32ToNumber: { if (nodeCanSpeculateInteger(node.arithNodeFlags())) changed |= mergePrediction(SpecInt32); else changed |= mergePrediction(SpecNumber); changed |= m_graph[node.child1()].mergeFlags(flags); break; } case ValueAdd: { SpeculatedType left = m_graph[node.child1()].prediction(); SpeculatedType right = m_graph[node.child2()].prediction(); if (left && right) { if (isNumberSpeculation(left) && isNumberSpeculation(right)) { if (m_graph.addShouldSpeculateInteger(node)) changed |= mergePrediction(SpecInt32); else changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right)); } else if (!(left & SpecNumber) || !(right & SpecNumber)) { // left or right is definitely something other than a number. changed |= mergePrediction(SpecString); } else changed |= mergePrediction(SpecString | SpecInt32 | SpecDouble); } if (isNotNegZero(node.child1().index()) || isNotNegZero(node.child2().index())) flags &= ~NodeNeedsNegZero; changed |= m_graph[node.child1()].mergeFlags(flags); changed |= m_graph[node.child2()].mergeFlags(flags); break; } case ArithAdd: { SpeculatedType left = m_graph[node.child1()].prediction(); SpeculatedType right = m_graph[node.child2()].prediction(); if (left && right) { if (m_graph.addShouldSpeculateInteger(node)) changed |= mergePrediction(SpecInt32); else changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right)); } if (isNotNegZero(node.child1().index()) || isNotNegZero(node.child2().index())) flags &= ~NodeNeedsNegZero; changed |= m_graph[node.child1()].mergeFlags(flags); changed |= m_graph[node.child2()].mergeFlags(flags); break; } case ArithSub: { SpeculatedType left = m_graph[node.child1()].prediction(); SpeculatedType right = m_graph[node.child2()].prediction(); if (left && right) { if (m_graph.addShouldSpeculateInteger(node)) changed |= mergePrediction(SpecInt32); else changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right)); } if (isNotZero(node.child1().index()) || isNotZero(node.child2().index())) flags &= ~NodeNeedsNegZero; changed |= m_graph[node.child1()].mergeFlags(flags); changed |= m_graph[node.child2()].mergeFlags(flags); break; } case ArithNegate: if (m_graph[node.child1()].prediction()) { if (m_graph.negateShouldSpeculateInteger(node)) changed |= mergePrediction(SpecInt32); else changed |= mergePrediction(speculatedDoubleTypeForPrediction(m_graph[node.child1()].prediction())); } changed |= m_graph[node.child1()].mergeFlags(flags); break; case ArithMin: case ArithMax: { SpeculatedType left = m_graph[node.child1()].prediction(); SpeculatedType right = m_graph[node.child2()].prediction(); if (left && right) { if (isInt32Speculation(mergeSpeculations(left, right)) && nodeCanSpeculateInteger(node.arithNodeFlags())) changed |= mergePrediction(SpecInt32); else changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right)); } flags |= NodeUsedAsNumber; changed |= m_graph[node.child1()].mergeFlags(flags); changed |= m_graph[node.child2()].mergeFlags(flags); break; } case ArithMul: { SpeculatedType left = m_graph[node.child1()].prediction(); SpeculatedType right = m_graph[node.child2()].prediction(); if (left && right) { if (m_graph.mulShouldSpeculateInteger(node)) changed |= mergePrediction(SpecInt32); else changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right)); } // As soon as a multiply happens, we can easily end up in the part // of the double domain where the point at which you do truncation // can change the outcome. So, ArithMul always checks for overflow // no matter what, and always forces its inputs to check as well. flags |= NodeUsedAsNumber | NodeNeedsNegZero; changed |= m_graph[node.child1()].mergeFlags(flags); changed |= m_graph[node.child2()].mergeFlags(flags); break; } case ArithDiv: { SpeculatedType left = m_graph[node.child1()].prediction(); SpeculatedType right = m_graph[node.child2()].prediction(); if (left && right) { if (isInt32Speculation(mergeSpeculations(left, right)) && nodeCanSpeculateInteger(node.arithNodeFlags())) changed |= mergePrediction(SpecInt32); else changed |= mergePrediction(SpecDouble); } // As soon as a multiply happens, we can easily end up in the part // of the double domain where the point at which you do truncation // can change the outcome. So, ArithMul always checks for overflow // no matter what, and always forces its inputs to check as well. flags |= NodeUsedAsNumber | NodeNeedsNegZero; changed |= m_graph[node.child1()].mergeFlags(flags); changed |= m_graph[node.child2()].mergeFlags(flags); break; } case ArithSqrt: { changed |= setPrediction(SpecDouble); changed |= m_graph[node.child1()].mergeFlags(flags | NodeUsedAsValue); break; } case ArithAbs: { SpeculatedType child = m_graph[node.child1()].prediction(); if (nodeCanSpeculateInteger(node.arithNodeFlags())) changed |= mergePrediction(child); else changed |= setPrediction(speculatedDoubleTypeForPrediction(child)); flags &= ~NodeNeedsNegZero; changed |= m_graph[node.child1()].mergeFlags(flags); break; } case LogicalNot: case CompareLess: case CompareLessEq: case CompareGreater: case CompareGreaterEq: case CompareEq: case CompareStrictEq: case InstanceOf: case IsUndefined: case IsBoolean: case IsNumber: case IsString: case IsObject: case IsFunction: { changed |= setPrediction(SpecBoolean); changed |= mergeDefaultFlags(node); break; } case GetById: { changed |= mergePrediction(node.getHeapPrediction()); changed |= mergeDefaultFlags(node); break; } case GetByIdFlush: changed |= mergePrediction(node.getHeapPrediction()); changed |= mergeDefaultFlags(node); break; case GetByVal: { if (m_graph[node.child1()].shouldSpeculateFloat32Array() || m_graph[node.child1()].shouldSpeculateFloat64Array()) changed |= mergePrediction(SpecDouble); else changed |= mergePrediction(node.getHeapPrediction()); changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue); changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt); break; } case GetMyArgumentByValSafe: { changed |= mergePrediction(node.getHeapPrediction()); changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt); break; } case GetMyArgumentsLengthSafe: { changed |= setPrediction(SpecInt32); break; } case GetScopeRegisters: case GetButterfly: case GetIndexedPropertyStorage: case AllocatePropertyStorage: case ReallocatePropertyStorage: { changed |= setPrediction(SpecOther); changed |= mergeDefaultFlags(node); break; } case GetByOffset: { changed |= mergePrediction(node.getHeapPrediction()); changed |= mergeDefaultFlags(node); break; } case Call: case Construct: { changed |= mergePrediction(node.getHeapPrediction()); for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); ++childIdx) { Edge edge = m_graph.m_varArgChildren[childIdx]; changed |= m_graph[edge].mergeFlags(NodeUsedAsValue); } break; } case ConvertThis: { SpeculatedType prediction = m_graph[node.child1()].prediction(); if (prediction) { if (prediction & ~SpecObjectMask) { prediction &= SpecObjectMask; prediction = mergeSpeculations(prediction, SpecObjectOther); } changed |= mergePrediction(prediction); } changed |= mergeDefaultFlags(node); break; } case GetGlobalVar: { changed |= mergePrediction(node.getHeapPrediction()); break; } case PutGlobalVar: case PutGlobalVarCheck: { changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue); break; } case GetScopedVar: case Resolve: case ResolveBase: case ResolveBaseStrictPut: case ResolveGlobal: { SpeculatedType prediction = node.getHeapPrediction(); changed |= mergePrediction(prediction); break; } case GetScope: { changed |= setPrediction(SpecCellOther); break; } case GetCallee: { changed |= setPrediction(SpecFunction); break; } case CreateThis: case NewObject: { changed |= setPrediction(SpecFinalObject); changed |= mergeDefaultFlags(node); break; } case NewArray: { changed |= setPrediction(SpecArray); for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); ++childIdx) { Edge edge = m_graph.m_varArgChildren[childIdx]; changed |= m_graph[edge].mergeFlags(NodeUsedAsValue); } break; } case NewArrayWithSize: { changed |= setPrediction(SpecArray); changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt); break; } case NewArrayBuffer: { changed |= setPrediction(SpecArray); break; } case NewRegexp: { changed |= setPrediction(SpecObjectOther); break; } case StringCharAt: { changed |= setPrediction(SpecString); changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue); changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt); break; } case StrCat: { changed |= setPrediction(SpecString); for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); ++childIdx) changed |= m_graph[m_graph.m_varArgChildren[childIdx]].mergeFlags(NodeUsedAsNumber); break; } case ToPrimitive: { SpeculatedType child = m_graph[node.child1()].prediction(); if (child) { if (isObjectSpeculation(child)) { // I'd love to fold this case into the case below, but I can't, because // removing SpecObjectMask from something that only has an object // prediction and nothing else means we have an ill-formed SpeculatedType // (strong predict-none). This should be killed once we remove all traces // of static (aka weak) predictions. changed |= mergePrediction(SpecString); } else if (child & SpecObjectMask) { // Objects get turned into strings. So if the input has hints of objectness, // the output will have hinsts of stringiness. changed |= mergePrediction( mergeSpeculations(child & ~SpecObjectMask, SpecString)); } else changed |= mergePrediction(child); } changed |= m_graph[node.child1()].mergeFlags(flags); break; } case CreateActivation: { changed |= setPrediction(SpecObjectOther); break; } case CreateArguments: { // At this stage we don't try to predict whether the arguments are ours or // someone else's. We could, but we don't, yet. changed |= setPrediction(SpecArguments); break; } case NewFunction: case NewFunctionNoCheck: case NewFunctionExpression: { changed |= setPrediction(SpecFunction); break; } case PutByValAlias: case GetArrayLength: case Int32ToDouble: case DoubleAsInt32: case GetLocalUnlinked: case GetMyArgumentsLength: case GetMyArgumentByVal: case PhantomPutStructure: case PhantomArguments: case CheckArray: case Arrayify: { // This node should never be visible at this stage of compilation. It is // inserted by fixup(), which follows this phase. ASSERT_NOT_REACHED(); break; } case PutByVal: changed |= m_graph[m_graph.varArgChild(node, 0)].mergeFlags(NodeUsedAsValue); changed |= m_graph[m_graph.varArgChild(node, 1)].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt); changed |= m_graph[m_graph.varArgChild(node, 2)].mergeFlags(NodeUsedAsValue); break; case PutScopedVar: case Return: case Throw: changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue); break; case PutById: case PutByIdDirect: changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue); changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsValue); break; case PutByOffset: changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue); changed |= m_graph[node.child3()].mergeFlags(NodeUsedAsValue); break; case Phi: break; #ifndef NDEBUG // These get ignored because they don't return anything. case DFG::Jump: case Branch: case Breakpoint: case CheckHasInstance: case ThrowReferenceError: case ForceOSRExit: case SetArgument: case CheckStructure: case ForwardCheckStructure: case StructureTransitionWatchpoint: case ForwardStructureTransitionWatchpoint: case CheckFunction: case PutStructure: case TearOffActivation: case TearOffArguments: case CheckNumber: case CheckArgumentsNotCreated: case GlobalVarWatchpoint: case GarbageValue: changed |= mergeDefaultFlags(node); break; // These gets ignored because it doesn't do anything. case Phantom: case InlineStart: case Nop: break; case LastNodeType: ASSERT_NOT_REACHED(); break; #else default: changed |= mergeDefaultFlags(node); break; #endif } #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog("%s\n", speculationToString(m_graph[m_compileIndex].prediction())); #endif m_changed |= changed; }
void propagate(Node* node) { NodeFlags flags = node->flags() & NodeBytecodeBackPropMask; switch (node->op()) { case GetLocal: { VariableAccessData* variableAccessData = node->variableAccessData(); flags &= ~NodeBytecodeUsesAsInt; // We don't care about cross-block uses-as-int. m_changed |= variableAccessData->mergeFlags(flags); break; } case SetLocal: { VariableAccessData* variableAccessData = node->variableAccessData(); if (!variableAccessData->isLoadedFrom()) break; flags = variableAccessData->flags(); RELEASE_ASSERT(!(flags & ~NodeBytecodeBackPropMask)); flags |= NodeBytecodeUsesAsNumber; // Account for the fact that control flow may cause overflows that our modeling can't handle. node->child1()->mergeFlags(flags); break; } case Flush: { VariableAccessData* variableAccessData = node->variableAccessData(); m_changed |= variableAccessData->mergeFlags(NodeBytecodeUsesAsValue); break; } case MovHint: case Check: break; case BitAnd: case BitOr: case BitXor: case BitRShift: case BitLShift: case BitURShift: case ArithIMul: { flags |= NodeBytecodeUsesAsInt; flags &= ~(NodeBytecodeUsesAsNumber | NodeBytecodeNeedsNegZero | NodeBytecodeUsesAsOther); flags &= ~NodeBytecodeUsesAsArrayIndex; node->child1()->mergeFlags(flags); node->child2()->mergeFlags(flags); break; } case StringCharCodeAt: { node->child1()->mergeFlags(NodeBytecodeUsesAsValue); node->child2()->mergeFlags(NodeBytecodeUsesAsValue | NodeBytecodeUsesAsInt | NodeBytecodeUsesAsArrayIndex); break; } case UInt32ToNumber: { node->child1()->mergeFlags(flags); break; } case ValueAdd: { if (isNotNegZero(node->child1().node()) || isNotNegZero(node->child2().node())) flags &= ~NodeBytecodeNeedsNegZero; if (node->child1()->hasNumberResult() || node->child2()->hasNumberResult()) flags &= ~NodeBytecodeUsesAsOther; if (!isWithinPowerOfTwo<32>(node->child1()) && !isWithinPowerOfTwo<32>(node->child2())) flags |= NodeBytecodeUsesAsNumber; if (!m_allowNestedOverflowingAdditions) flags |= NodeBytecodeUsesAsNumber; node->child1()->mergeFlags(flags); node->child2()->mergeFlags(flags); break; } case ArithAdd: { flags &= ~NodeBytecodeUsesAsOther; if (isNotNegZero(node->child1().node()) || isNotNegZero(node->child2().node())) flags &= ~NodeBytecodeNeedsNegZero; if (!isWithinPowerOfTwo<32>(node->child1()) && !isWithinPowerOfTwo<32>(node->child2())) flags |= NodeBytecodeUsesAsNumber; if (!m_allowNestedOverflowingAdditions) flags |= NodeBytecodeUsesAsNumber; node->child1()->mergeFlags(flags); node->child2()->mergeFlags(flags); break; } case ArithClz32: { flags &= ~(NodeBytecodeUsesAsNumber | NodeBytecodeNeedsNegZero | NodeBytecodeUsesAsOther | ~NodeBytecodeUsesAsArrayIndex); flags |= NodeBytecodeUsesAsInt; node->child1()->mergeFlags(flags); break; } case ArithSub: { flags &= ~NodeBytecodeUsesAsOther; if (isNotNegZero(node->child1().node()) || isNotPosZero(node->child2().node())) flags &= ~NodeBytecodeNeedsNegZero; if (!isWithinPowerOfTwo<32>(node->child1()) && !isWithinPowerOfTwo<32>(node->child2())) flags |= NodeBytecodeUsesAsNumber; if (!m_allowNestedOverflowingAdditions) flags |= NodeBytecodeUsesAsNumber; node->child1()->mergeFlags(flags); node->child2()->mergeFlags(flags); break; } case ArithNegate: { flags &= ~NodeBytecodeUsesAsOther; node->child1()->mergeFlags(flags); break; } case ArithMul: { // As soon as a multiply happens, we can easily end up in the part // of the double domain where the point at which you do truncation // can change the outcome. So, ArithMul always forces its inputs to // check for overflow. Additionally, it will have to check for overflow // itself unless we can prove that there is no way for the values // produced to cause double rounding. if (!isWithinPowerOfTwo<22>(node->child1().node()) && !isWithinPowerOfTwo<22>(node->child2().node())) flags |= NodeBytecodeUsesAsNumber; node->mergeFlags(flags); flags |= NodeBytecodeUsesAsNumber | NodeBytecodeNeedsNegZero; flags &= ~NodeBytecodeUsesAsOther; node->child1()->mergeFlags(flags); node->child2()->mergeFlags(flags); break; } case ArithDiv: { flags |= NodeBytecodeUsesAsNumber | NodeBytecodeNeedsNegZero; flags &= ~NodeBytecodeUsesAsOther; node->child1()->mergeFlags(flags); node->child2()->mergeFlags(flags); break; } case ArithMod: { flags |= NodeBytecodeUsesAsNumber; flags &= ~NodeBytecodeUsesAsOther; node->child1()->mergeFlags(flags); node->child2()->mergeFlags(flags & ~NodeBytecodeNeedsNegZero); break; } case GetByVal: { node->child1()->mergeFlags(NodeBytecodeUsesAsValue); node->child2()->mergeFlags(NodeBytecodeUsesAsNumber | NodeBytecodeUsesAsOther | NodeBytecodeUsesAsInt | NodeBytecodeUsesAsArrayIndex); break; } case NewArrayWithSize: { node->child1()->mergeFlags(NodeBytecodeUsesAsValue | NodeBytecodeUsesAsInt | NodeBytecodeUsesAsArrayIndex); break; } case NewTypedArray: { // Negative zero is not observable. NaN versus undefined are only observable // in that you would get a different exception message. So, like, whatever: we // claim here that NaN v. undefined is observable. node->child1()->mergeFlags(NodeBytecodeUsesAsInt | NodeBytecodeUsesAsNumber | NodeBytecodeUsesAsOther | NodeBytecodeUsesAsArrayIndex); break; } case StringCharAt: { node->child1()->mergeFlags(NodeBytecodeUsesAsValue); node->child2()->mergeFlags(NodeBytecodeUsesAsValue | NodeBytecodeUsesAsInt | NodeBytecodeUsesAsArrayIndex); break; } case ToString: case CallStringConstructor: { node->child1()->mergeFlags(NodeBytecodeUsesAsNumber | NodeBytecodeUsesAsOther); break; } case ToPrimitive: case ToNumber: { node->child1()->mergeFlags(flags); break; } case PutByValDirect: case PutByVal: { m_graph.varArgChild(node, 0)->mergeFlags(NodeBytecodeUsesAsValue); m_graph.varArgChild(node, 1)->mergeFlags(NodeBytecodeUsesAsNumber | NodeBytecodeUsesAsOther | NodeBytecodeUsesAsInt | NodeBytecodeUsesAsArrayIndex); m_graph.varArgChild(node, 2)->mergeFlags(NodeBytecodeUsesAsValue); break; } case Switch: { SwitchData* data = node->switchData(); switch (data->kind) { case SwitchImm: // We don't need NodeBytecodeNeedsNegZero because if the cases are all integers // then -0 and 0 are treated the same. We don't need NodeBytecodeUsesAsOther // because if all of the cases are integers then NaN and undefined are // treated the same (i.e. they will take default). node->child1()->mergeFlags(NodeBytecodeUsesAsNumber | NodeBytecodeUsesAsInt); break; case SwitchChar: { // We don't need NodeBytecodeNeedsNegZero because if the cases are all strings // then -0 and 0 are treated the same. We don't need NodeBytecodeUsesAsOther // because if all of the cases are single-character strings then NaN // and undefined are treated the same (i.e. they will take default). node->child1()->mergeFlags(NodeBytecodeUsesAsNumber); break; } case SwitchString: // We don't need NodeBytecodeNeedsNegZero because if the cases are all strings // then -0 and 0 are treated the same. node->child1()->mergeFlags(NodeBytecodeUsesAsNumber | NodeBytecodeUsesAsOther); break; case SwitchCell: // There is currently no point to being clever here since this is used for switching // on objects. mergeDefaultFlags(node); break; } break; } case Identity: // This would be trivial to handle but we just assert that we cannot see these yet. RELEASE_ASSERT_NOT_REACHED(); break; // Note: ArithSqrt, ArithUnary and other math intrinsics don't have special // rules in here because they are always followed by Phantoms to signify that if the // method call speculation fails, the bytecode may use the arguments in arbitrary ways. // This corresponds to that possibility of someone doing something like: // Math.sin = function(x) { doArbitraryThingsTo(x); } default: mergeDefaultFlags(node); break; } }