private static bool HasSharedTarget(NodeBlock pred, DJumpCondition jcc) { NodeBlock trueTarget = BlockAnalysis.EffectiveTarget(jcc.trueTarget); if (trueTarget.lir.numPredecessors == 1) { return(false); } if (pred.lir.idominated.Length > 3) { return(true); } // Hack... sniff out the case we care about, the true target // probably having a conditional. if (trueTarget.lir.instructions.Length == 2 && trueTarget.lir.instructions[0].op == Opcode.Constant && trueTarget.lir.instructions[1].op == Opcode.Jump) { return(true); } // Because of edge splitting, there will always be at least 4 // dominators for the immediate dominator of a shared block. return(false); }
private IfBlock traverseIf(NodeBlock block, DJumpCondition jcc) { if (HasSharedTarget(block, jcc)) { return(traverseComplexIf(block, jcc)); } NodeBlock trueTarget = (jcc.spop == SPOpcode.jzer) ? jcc.falseTarget : jcc.trueTarget; NodeBlock falseTarget = (jcc.spop == SPOpcode.jzer) ? jcc.trueTarget : jcc.falseTarget; NodeBlock joinTarget = findJoinOfSimpleIf(block, jcc); // If there is no join block (both arms terminate control flow), // eliminate one arm and use the other as a join point. if (joinTarget == null) { joinTarget = falseTarget; } // If the false target is equivalent to the join point, eliminate // it. if (BlockAnalysis.EffectiveTarget(falseTarget) == joinTarget) { falseTarget = null; } // If the true target is equivalent to the join point, promote // the false target to the true target and undo the inversion. bool invert = false; if (BlockAnalysis.EffectiveTarget(trueTarget) == joinTarget) { trueTarget = falseTarget; falseTarget = null; invert ^= true; } // If there is always a true target and a join target. pushScope(joinTarget); ControlBlock trueArm = traverseBlock(trueTarget); popScope(); ControlBlock joinArm = traverseJoin(joinTarget); if (falseTarget == null) { return(new IfBlock(block, invert, trueArm, joinArm)); } pushScope(joinTarget); ControlBlock falseArm = traverseBlock(falseTarget); popScope(); return(new IfBlock(block, invert, trueArm, falseArm, joinArm)); }
private NodeBlock findJoinOfSimpleIf(NodeBlock block, DJumpCondition jcc) { //Debug.Assert(block.nodes.last == jcc); //Debug.Assert(block.lir.idominated[0] == jcc.falseTarget.lir || block.lir.idominated[0] == jcc.trueTarget.lir); //Debug.Assert(block.lir.idominated[1] == jcc.falseTarget.lir || block.lir.idominated[1] == jcc.trueTarget.lir); if (block.lir.idominated.Length == 2) { if (jcc.trueTarget != BlockAnalysis.EffectiveTarget(jcc.trueTarget)) { return(jcc.trueTarget); } if (jcc.falseTarget != BlockAnalysis.EffectiveTarget(jcc.falseTarget)) { return(jcc.falseTarget); } return(null); } return(graph_[block.lir.idominated[2].id]); }
private static LogicOperator ToLogicOp(DJumpCondition jcc) { NodeBlock trueTarget = BlockAnalysis.EffectiveTarget(jcc.trueTarget); bool targetIsTruthy = false; if (trueTarget.lir.instructions[0] is LConstant) { LConstant constant = (LConstant)trueTarget.lir.instructions[0]; targetIsTruthy = (constant.val == 1); } // jump on true -> 1 == || // jump on false -> 0 == && // other combinations are nonsense, so assert. ////Debug.Assert((jcc.spop == SPOpcode.jnz && targetIsTruthy) || // (jcc.spop == SPOpcode.jzer && !targetIsTruthy)); LogicOperator logicop = (jcc.spop == SPOpcode.jnz && targetIsTruthy) ? LogicOperator.Or : LogicOperator.And; return(logicop); }
private ControlBlock traverseBlockNoLoop(NodeBlock block) { if (block == null) { return(null); } DNode last = block.nodes.last; if (last.type == NodeType.JumpCondition) { return(traverseIf(block, (DJumpCondition)last)); } if (last.type == NodeType.Jump) { DJump jump = (DJump)last; NodeBlock target = BlockAnalysis.EffectiveTarget(jump.target); ControlBlock next = null; if (!isJoin(target)) { next = traverseBlock(target); } return(new StatementBlock(block, next)); } if (last.type == NodeType.Switch) { return(traverseSwitch(block, (DSwitch)last)); } //Debug.Assert(last.type == NodeType.Return); return(new ReturnBlock(block)); }
private IfBlock traverseComplexIf(NodeBlock block, DJumpCondition jcc) { // Degenerate case: using || or &&, or any combination thereof, // will generate a chain of n+1 conditional blocks, where each // |n| has a target to a shared "success" block, setting a // phony variable. We decompose this giant mess into the intended // sequence of expressions. NodeBlock join; LogicChain chain = buildLogicChain(block, null, out join); DJumpCondition finalJcc = (DJumpCondition)join.nodes.last; //Debug.Assert(finalJcc.spop == SPOpcode.jzer); // The final conditional should have the normal dominator // properties: 2 or 3 idoms, depending on the number of arms. // Because of critical edge splitting, we may have 3 idoms // even if there are only actually two arms. NodeBlock joinBlock = findJoinOfSimpleIf(join, finalJcc); // If an AND chain reaches its end, the result is 1. jzer tests // for zero, so this is effectively testing (!success). // If an OR expression reaches its end, the result is 0. jzer // tests for zero, so this is effectively testing if (failure). // // In both cases, the true target represents a failure, so flip // the targets around. NodeBlock trueBlock = finalJcc.falseTarget; NodeBlock falseBlock = finalJcc.trueTarget; // If there is no join block, both arms terminate control flow, // eliminate one arm and use the other as a join point. if (joinBlock == null) { joinBlock = falseBlock; } if (join.lir.idominated.Length == 2 || BlockAnalysis.EffectiveTarget(falseBlock) == joinBlock) { if (join.lir.idominated.Length == 3) { joinBlock = BlockAnalysis.EffectiveTarget(falseBlock); } // One-armed structure. pushScope(joinBlock); ControlBlock trueArm1 = traverseBlock(trueBlock); popScope(); ControlBlock joinArm1 = traverseBlock(joinBlock); return(new IfBlock(block, chain, trueArm1, joinArm1)); } //Debug.Assert(join.lir.idominated.Length == 3); pushScope(joinBlock); ControlBlock trueArm2 = traverseBlock(trueBlock); ControlBlock falseArm = traverseBlock(falseBlock); popScope(); ControlBlock joinArm2 = traverseBlock(joinBlock); return(new IfBlock(block, chain, trueArm2, falseArm, joinArm2)); }
private LogicChain buildLogicChain(NodeBlock block, NodeBlock earlyExitStop, out NodeBlock join) { DJumpCondition jcc = (DJumpCondition)block.nodes.last; LogicChain chain = new LogicChain(ToLogicOp(jcc)); // Grab the true target, which will be either the "1" or "0" // branch of the AND/OR expression. NodeBlock earlyExit = BlockAnalysis.EffectiveTarget(jcc.trueTarget); NodeBlock exprBlock = block; do { do { DJumpCondition childJcc = (DJumpCondition)exprBlock.nodes.last; if (BlockAnalysis.EffectiveTarget(childJcc.trueTarget) != earlyExit) { // Parse a sub-expression. NodeBlock innerJoin; LogicChain rhs = buildLogicChain(exprBlock, earlyExit, out innerJoin); AssertInnerJoinValidity(innerJoin, earlyExit); chain.append(rhs); exprBlock = innerJoin; childJcc = (DJumpCondition)exprBlock.nodes.last; } else { chain.append(childJcc.getOperand(0)); } exprBlock = childJcc.falseTarget; } while (exprBlock.nodes.last.type == NodeType.JumpCondition); do { // We have reached the end of a sequence - a block containing // a Constant and a Jump to the join point of the sequence. //Debug.Assert(exprBlock.lir.instructions[0].op == Opcode.Constant); // The next block is the join point. NodeBlock condBlock = SingleTarget(exprBlock); var last = condBlock.nodes.last; DJumpCondition condJcc; try { condJcc = (DJumpCondition)last; } catch (Exception e) { throw new LogicChainConversionException(e.Message); } join = condBlock; // If the cond block is the tagret of the early stop, we've // gone a tad too far. This is the case for a simple // expression like (a && b) || c. if (earlyExitStop != null && SingleTarget(earlyExitStop) == condBlock) { return(chain); } // If the true connects back to the early exit stop, we're // done. if (BlockAnalysis.EffectiveTarget(condJcc.trueTarget) == earlyExitStop) { return(chain); } // If the true target does not have a shared target, we're // done parsing the whole logic chain. if (!HasSharedTarget(condBlock, condJcc)) { return(chain); } // Otherwise, there is another link in the chain. This link // joins the existing chain to a new subexpression, which // actually starts hanging off the false branch of this // conditional. earlyExit = BlockAnalysis.EffectiveTarget(condJcc.trueTarget); // Build the right-hand side of the expression. NodeBlock innerJoin; LogicChain rhs = buildLogicChain(condJcc.falseTarget, earlyExit, out innerJoin); AssertInnerJoinValidity(innerJoin, earlyExit); // Build the full expression. LogicChain root = new LogicChain(ToLogicOp(condJcc)); root.append(chain); root.append(rhs); chain = root; // If the inner join's false target is a conditional, the // outer expression may continue. DJumpCondition innerJcc = (DJumpCondition)innerJoin.nodes.last; if (innerJcc.falseTarget.nodes.last.type == NodeType.JumpCondition) { exprBlock = innerJcc.falseTarget; break; } // Finally, the new expression block is always the early exit // block. It's on the "trueTarget" edge of the expression, // whereas incoming into this loop it's on the "falseTarget" // edge, but this does not matter. exprBlock = earlyExit; } while (true); } while (true); }