private IfScope(BlockScope parent, ControlFlowBlock head) { _parent = parent.AssertNotNull(); _head = head.AssertNotNull(); parent.Hir.AddElements(head.BalancedCode); _if.Test = head.Residue.AssertSingle(); var v_true = LocalCfg.TreeVedges(head, null).AssertSingle(e => e.Tag == PredicateType.IsTrue).Target; _ifTrue = this.InferBranch(v_true, out _trueOffsprings); var v_false = LocalCfg.TreeVedges(head, null).AssertSingle(e => e.Tag == PredicateType.IsFalse).Target; _ifFalse = this.InferBranch(v_false, out _falseOffsprings); _if.IfTrue = BlockScope.Decompile(this, _ifTrue).Hir; _if.IfFalse = BlockScope.Decompile(this, _ifFalse).Hir; }
private ComplexScope(IScope parent, BaseControlFlowGraph cfg) { _parent = parent.AssertNotNull(); _localCfg = cfg; var offsprings = new List <Offspring>(); var adjacentToFinish = cfg.Vedges(null, cfg.Finish).Select(e => e.Source).ToReadOnly(); var lastInFlow = cfg.Cflow()[adjacentToFinish.Max(v => cfg.Cflow().IndexOf(v))]; if (lastInFlow != cfg.Start) { var flow = lastInFlow.MkArray().Closure(cfg.Vertices, (vin, vout) => vout != cfg.Start && cfg.Vedge(vout, vin) != null); _localCfg = cfg.CreateView(flow, (e, vcfg) => { if (e.Source == cfg.Start) { vcfg.InheritStart(cfg.Start); vcfg.AddEigenEdge(e.ShallowClone()); } else if (e.Target == cfg.Finish) { if (e.Source == lastInFlow) { vcfg.InheritFinish(cfg.Finish); } vcfg.AddEigenEdge(e.ShallowClone()); } else { (cfg.Vedges(e.Source, null).Count() == 2).AssertTrue(); offsprings.Add(new Offspring(this, e.Source, e.Target)); } }); } Offsprings = offsprings.ToReadOnly(); _block = BlockScope.Decompile(this, _localCfg).Hir; }
private LoopScope(BlockScope parent, ControlFlowBlock head) { _parent = parent.AssertNotNull(); _head = head.AssertNotNull(); var cfg = LocalCfg; var jumpsToHead = cfg.BackVedges(null, _head).Select(e => e.Source).ToReadOnly(); jumpsToHead.AssertNotEmpty(); _bot = jumpsToHead.OrderBy(v => cfg.Cflow().IndexOf(v)).Last(); var cflow = cfg.Cflow(_head, _bot); // todo. check this // // all loops can be divided into four categories: // 1) while-do (head is test, either bot or head is continue label), // 2) while-true (neither head, nor bot are tests; actually, head is hardcoded to "CS$... = 1" // also either bot or head serve as continue label), // 3) do-while (bot is test, it is also the only option for continue // (since here we don't need to separate iteration block from other body instructions, // and because of (nota bene - never knew it) the fact that continue in do-while // first tests exit condition and only then proceeds with the next iteration), // 4) some loop with offspring embedded into either head or bot // // in the first two cases before proceeding to detect subscopes and offsprings // we need to find out what's the continue label. that's done as follows. // todo. we don't process case #4, since that's just too much for me // // if cflow features multiple jumps to head, then head is a continue label // else (if there's only one jump, i.e. from bot), then bot is a continue label // // now how we distinguish an offspring from loop's body? // answer: using regular closure algorithm, i.e. // 1) we pick a bot2 vertex (see below for options), // 2) we init the closure with cflow(head, bot2) + bot2 // 3) we expand the closure using universum of parent's local cfg // 4) ... and relation of being reachable w/o touching _test and _iter // // so, bot2 is a logical bottom of the loop's body (while iter exists on its own!) // 1) if bot ain't a continue label, then bot2 = bot // 2) if bot is a continue label, then check number of jumps to bot // 3) if the latter is one, then bot2 = bot // 4) else bot2 = the last one that jumps to bot // // ah, the last point... how do we find convergence? // for cases 1 and 3 conv is the node that receives a jump from either head or bot // for case 2 conv is the node that receives a jump from the loop's body // todo. here we use a hack, assuming that there's exactly 1 jump from the loop's body // if there're two or more jumps we will just work incorrectly // if there're zero jumps, then it's fail anyways, since we've just entered an infinite loop // todo. also verify that test has supported type // also don't forget that IL allows using almost everything as a condition of a test // cf. brzero === brnull === brfalse, brtrue === brinst var hcond = _head.Residue.IsNotEmpty(); var bcond = _bot.Residue.IsNotEmpty(); (hcond && bcond).AssertFalse(); // case #1. while-do if (hcond && !bcond) { _loop.IsWhileDo = true; _test = _head; _loop.Test = _test.Residue.AssertSingle(); parent.Hir.AddElements(_test.BalancedCode); var next1 = cfg.Vedges(_test, null).First().Target; var next2 = cfg.Vedges(_test, null).Second().Target; (cflow.Contains(next1) || cflow.Contains(next2)).AssertTrue(); _conv = cflow.Contains(next1) ? next2 : next1; _bodyHead = cflow.Contains(next1) ? next1 : next2; var botIsContinue = jumpsToHead.Count() == 1; if (botIsContinue) { var jumpsToBot = cfg.Vedges(null, _bot).Select(e => e.Source).ToReadOnly(); var lastJumpToBot = jumpsToBot.OrderBy(v => cfg.Cflow().IndexOf(v)).Last(); _continue = jumpsToBot.Count() > 1 ? _bot : null; _bodyBot = jumpsToBot.Count() > 1 ? lastJumpToBot : _bot; } else { _continue = _head; _bodyBot = _bot; } } // case #2. while-true else if (!hcond && !bcond) { _head.Residue.AssertEmpty(); var es = _head.BalancedCode.AssertSingle(); var ass = es.AssertCast <Assign>(); (ass.Rhs.AssertCast <Const>().Value.AssertCast <int>() == 1).AssertTrue(); _loop.IsWhileDo = true; _loop.Test = new Const(true); _test = _head; // todo. here we use a hack, assuming that there's exactly 1 jump from the loop's body var alienEdges = cfg.AlienEdges(cflow); var breaks = alienEdges.Except(cfg.Vedges(null, _head), cfg.Vedges(null, _bot)); _conv = breaks.Select(e => e.Target).Distinct().AssertSingle(); _bodyHead = cfg.Vedges(_head, null).AssertSingle().Target; var botIsContinue = jumpsToHead.Count() == 1; if (botIsContinue) { var jumpsToBot = cfg.Vedges(null, _bot).Select(e => e.Source).ToReadOnly(); var lastJumpToBot = jumpsToBot.OrderBy(v => cfg.Cflow().IndexOf(v)).Last(); _continue = (botIsContinue && jumpsToBot.Count() > 1) ? _bot : null; _bodyBot = (botIsContinue && jumpsToBot.Count() > 1) ? lastJumpToBot : _bot; } else { _continue = _head; _bodyBot = _bot; } } // case #3: do-while else if (!hcond && bcond) { _loop.IsDoWhile = true; _test = _bot; _loop.Test = _test.Residue.AssertSingle(); parent.Hir.AddElements(_test.BalancedCode); var next1 = cfg.Vedges(_test, null).First().Target; var next2 = cfg.Vedges(_test, null).Second().Target; (cflow.Contains(next1) || cflow.Contains(next2)).AssertTrue(); _conv = cflow.Contains(next1) ? next2 : next1; _bodyHead = cflow.Contains(next1) ? next1 : next2; var jumpsToBot = cfg.Vedges(null, _bot).Select(e => e.Source).ToReadOnly(); _continue = jumpsToBot.Count() == 1 ? null : _bot; _bodyBot = jumpsToBot.OrderBy(v => cfg.Cflow().IndexOf(v)).Last(); } // case #4: mysterious loop else { // todo. we don't process case #4, since that's just too much for me now throw AssertionHelper.Fail(); } // here we need to pass a hint to DoDecompileScopesForLoopLocals if (_continue != null) { if (_continue == _test) { _loop.Iter.Add(null); } else { _loop.Iter.SetElements(_continue.AssertThat(cfb => cfb.Residue.IsEmpty()).BalancedCode); } } _body = this.InferBody(out _offsprings); _loop.Body = BlockScope.Decompile(this, _body).Hir; }