public bool DetectSwitch_GenerateSwitch(IList <ILNode> body, ILBasicBlock head, int pos) { ILExpression condition; ILLabel trueLabel; ILLabel fallThough; // REMEMBER: The searching goes backwards, so we find the FIRST case here, so here is the problem // Evey time we run this, we have to search for the first block of the case statement backwards and if // the push var is not resolved, we have to drop out... evey single time till the push IS resolved // so be sure to run this at the bottom of the decision chain. Its fine if the push is a simple var // but I can bet cash this runs 2-3 times if the push is some kind of expression like 5 + (3 % switch_var)) // we could change the way the optimizing loop works by going from the start and building a que of things // to remove, delete or change hummm... Take longer but it would make building this functions MUCH simpler if (MatchSwitchCase(head, out trueLabel, out fallThough, out condition) && !MatchSwitchCase(body.ElementAtOrDefault(pos - 1) as ILBasicBlock, out trueLabel, out fallThough, out condition) ) // && head.MatchLastAt(6, GMCode.Push,out switch_expr)) { List <ILBasicBlock> caseBlocks = GetAllCaseBlocks(body, head, pos, out condition, out fallThough); if (caseBlocks == null) { return(false); } ILExpression fswitch = new ILExpression(GMCode.Switch, null); FakeSwitch args = new FakeSwitch(); args.SwitchExpression = condition; args.CaseExpressions = caseBlocks.Select(bb => new KeyValuePair <ILExpression, ILLabel>((bb.ElementAtLast(3) as ILExpression).Arguments[0], (bb.ElementAtLast(1) as ILExpression).Operand as ILLabel) ).ToList(); fswitch.Operand = args; // search the blocks for ending popz's and remove the popz HashSet <ILBasicBlock> blocks_done = new HashSet <ILBasicBlock>(); Stack <ILBasicBlock> agenda = new Stack <ILBasicBlock>(caseBlocks); while (agenda.Count > 0) { ILBasicBlock bb = agenda.Pop(); if (blocks_done.Contains(bb)) { continue; // already did it } ILExpression popz = bb.Body.OfType <ILExpression>().Where(e => e.Code == GMCode.Popz).SingleOrDefault(); if (popz != null) { bb.Body.Remove(popz); // remove it } else { ILLabel exit = bb.OperandLabelLastAt(0); if (exit != null && !blocks_done.Contains(labelToBasicBlock[exit])) { agenda.Push(labelToBasicBlock[exit]); } exit = bb.OperandLabelLastAt(1); // check if we have a bt or something if (exit != null && !blocks_done.Contains(labelToBasicBlock[exit])) { agenda.Push(labelToBasicBlock[exit]); } } blocks_done.Add(bb); } ILBasicBlock startOfAllCases = caseBlocks.First(); caseBlocks.Remove(startOfAllCases); startOfAllCases.Body.RemoveTail(GMCode.Dup, GMCode.Push, GMCode.Seq, GMCode.Bt, GMCode.B); startOfAllCases.Body.Add(fswitch); startOfAllCases.Body.Add(new ILExpression(GMCode.B, fallThough)); // end_of_switch.EntryLabel())); body.RemoveAll(caseBlocks); return(true); } return(false); }
public bool DetectSwitchAndConvertToBranches(IList <ILNode> body, ILBasicBlock head, int pos) { ILExpression condition; ILLabel trueLabel; ILLabel fallThough; // REMEMBER: The searching goes backwards, so we find the LAST case here, so here is the problem // Evey time we run this, we have to search for the first block of the case statement backwards and if // the push var is not resolved, we have to drop out... evey single time till the push IS resolved // so be sure to run this at the bottom of the decision chain. Its fine if the push is a simple var // but I can bet cash this runs 2-3 times if the push is some kind of expression like 5 + (3 % switch_var)) // we could change the way the optimizing loop works by going from the start and building a que of things // to remove, delete or change hummm... Take longer but it would make building this functions MUCH simpler if (MatchSwitchCase(head, out trueLabel, out fallThough, out condition)) // && head.MatchLastAt(6, GMCode.Push,out switch_expr)) { List <ILBasicBlock> caseBlocks = GetAllCaseBlocks(body, head, pos, out condition, out fallThough); foreach (var bb in caseBlocks) // replace the dup statements { Debug.Assert(bb.MatchLastAt(5, GMCode.Dup)); bb.Body[bb.Body.Count - 5] = new ILExpression(GMCode.Push, null, condition); ILExpression expr = bb.Body[bb.Body.Count - 3] as ILExpression; // expr.Code = GMCode.Case; // conver the equals to a case } // search the blocks for ending popz's HashSet <ILBasicBlock> blocks_done = new HashSet <ILBasicBlock>(); Stack <ILBasicBlock> agenda = new Stack <ILBasicBlock>(caseBlocks); while (agenda.Count > 0) { ILBasicBlock bb = agenda.Pop(); if (blocks_done.Contains(bb)) { continue; // already did it } ILExpression popz = bb.Body.OfType <ILExpression>().Where(e => e.Code == GMCode.Popz).SingleOrDefault(); if (popz != null) { bb.Body.Remove(popz); // remove it blocks_done.Add(bb); } else { ILLabel exit = bb.OperandLabelLastAt(0); if (exit != null && !blocks_done.Contains(labelToBasicBlock[exit])) { agenda.Push(labelToBasicBlock[exit]); } exit = bb.OperandLabelLastAt(1); // check if we have a bt or something if (exit != null && !blocks_done.Contains(labelToBasicBlock[exit])) { agenda.Push(labelToBasicBlock[exit]); } } } return(true); } return(false); }