// somewhere, so bug, is leaving an empty block, I think because of switches public bool RemoveRedundentBlocks(IList <ILNode> body, ILBasicBlock head, int pos) { if (!labelGlobalRefCount.ContainsKey(head.EntryLabel()) && body.Contains(head)) { if (head.Body.Count != 2) { // we have an empty block that has data in it? throw it as an error. // Might just be extra code like after an exit that was never used or a programer error but lets record it anyway error.Warning("BasicBlock with data removed, not linked to anything so should be safe", head); } body.RemoveOrThrow(head); return(true); } return(false); }
public bool DetectSwitch_GenerateBranches(IList <ILNode> body, ILBasicBlock head, int pos) { bool modified = false; ILExpression condition; ILLabel trueLabel; ILLabel falseLabel; ILLabel fallThough; // Debug.Assert(head.EntryLabel().Name != "Block_473"); if (MatchSwitchCase(head, out trueLabel, out fallThough, out condition)) // we ignore this first match, but remember the position { List <ILExpression> cases = new List <ILExpression>(); List <ILNode> caseBlocks = new List <ILNode>(); ILLabel prev = head.EntryLabel(); ILBasicBlock startOfCases = head; cases.Add(PreSetUpCaseBlock(startOfCases, condition)); caseBlocks.Add(startOfCases); for (int i = pos - 1; i >= 0; i--) { ILBasicBlock bb = body[i] as ILBasicBlock; if (MatchSwitchCase(bb, out trueLabel, out falseLabel, out condition)) { caseBlocks.Add(bb); cases.Add(PreSetUpCaseBlock(bb, condition)); Debug.Assert(falseLabel == prev); prev = bb.EntryLabel(); startOfCases = bb; } else { break; } } // we have all the cases // head is at the "head" of the cases ILExpression left; if (startOfCases.Body[startOfCases.Body.Count - 3].Match(GMCode.Push, out left)) { startOfCases.Body.RemoveAt(startOfCases.Body.Count - 3); foreach (var e in cases) { e.Arguments.Insert(0, new ILExpression(left)); // add the expression to all the branches } } else { throw new Exception("switch failure"); } // It seems GM makes a default case that just jumps to the end of the switch but I cannot // rely on it always being there ILBasicBlock default_case = body[pos + 1] as ILBasicBlock; Debug.Assert(default_case.EntryLabel() == head.GotoLabel()); ILBasicBlock end_of_switch = labelToBasicBlock[default_case.GotoLabel()]; if ((end_of_switch.Body[1] as ILExpression).Code == GMCode.Popz) { end_of_switch.Body.RemoveAt(1); // yeaa! } else // booo { // We have a default case so now we have to find where the popz ends, // this could be bad if we had a wierd generated for loop, but we are just doing stupid search ILBasicBlock test1 = FindEndOfSwitch(end_of_switch); // we take a sample from one of the cases to make sure we do end up at the same place ILBasicBlock test2 = FindEndOfSwitch(head); if (test1 == test2) { // two matches are good enough for me test1.Body.RemoveAt(1); // yeaa! } else { error.Error("Cannot find end of switch", end_of_switch); // booo } } // tricky part, finding that damn popz // Ok, we have all the case blocks, they are all fixed, and its like a big chain of ifs now. // But for anything OTHER than lua that has switch statments, we want to mark this for latter modified |= true; } return(modified); }
// This is before the expression is processed, so ILValue's and constants havn't been assigned public bool SimplifyTernaryOperator(IList <ILNode> body, ILBasicBlock head, int pos) { Debug.Assert(body.Contains(head)); // Debug.Assert((head.Body[0] as ILLabel).Name != "Block_54"); // Debug.Assert((head.Body[0] as ILLabel).Name != "L1257"); ILExpression condExpr; ILLabel trueLabel; ILLabel falseLabel; ILExpression trueExpr; ILLabel trueFall; ILExpression falseExpr; ILLabel falseFall; ILLabel finalFalseFall; ILLabel finalTrueFall; if (head.MatchLastAndBr(GMCode.Bt, out trueLabel, out condExpr, out falseLabel) && labelGlobalRefCount[trueLabel] == 1 && labelGlobalRefCount[falseLabel] == 1 && labelToBasicBlock[trueLabel].MatchSingleAndBr(GMCode.Push, out trueExpr, out trueFall) && labelToBasicBlock[falseLabel].MatchSingleAndBr(GMCode.Push, out falseExpr, out falseFall) && trueFall == falseFall && body.Contains(labelToBasicBlock[trueFall]) // finalFall.Code == GMCode.Pop ) // (finalFall == null || finalFall.Code == GMCode.Pop) { ILBasicBlock trueBlock = labelToBasicBlock[trueLabel]; ILBasicBlock falseBlock = labelToBasicBlock[falseLabel]; ILBasicBlock fallBlock = labelToBasicBlock[trueFall]; ILExpression newExpr = ResolveTernaryExpression(condExpr, trueExpr, falseExpr); head.Body.RemoveTail(GMCode.Bt, GMCode.B); body.RemoveOrThrow(trueBlock); body.RemoveOrThrow(falseBlock); IList <ILExpression> finalFall; // figure out if its a wierd short or not if (fallBlock.MatchSingleAndBr(GMCode.Bt, out finalTrueFall, out finalFall, out finalFalseFall) && finalFall.Count == 0) { head.Body.Add(new ILExpression(GMCode.Bt, finalTrueFall, newExpr)); if (labelGlobalRefCount[trueFall] == 2) { body.RemoveOrThrow(fallBlock); } } else if (fallBlock.Body.Count == 2) // wierd break, { finalFalseFall = fallBlock.GotoLabel(); head.Body.Add(new ILExpression(GMCode.Push, null, newExpr)); // we want to push it for next pass if (labelGlobalRefCount[trueFall] == 2) { body.RemoveOrThrow(fallBlock); } } else if (fallBlock.MatchAt(1, GMCode.Pop)) // generated? wierd instance? { finalFalseFall = fallBlock.EntryLabel(); error.Info("Wierd Generated Pop here", newExpr); head.Body.Add(new ILExpression(GMCode.Push, null, newExpr)); // It should be combined in JoinBasicBlocks function // so don't remove failblock } else if (fallBlock.MatchAt(1, GMCode.Assign, out finalFall)) // This is an assignment case and unsure what its for { finalFall.Add(newExpr); finalFalseFall = fallBlock.EntryLabel(); } if (finalFalseFall == null && fallBlock.MatchLastAt(1, GMCode.Ret)) { head.Body.Add(new ILExpression(GMCode.Ret, null)); } else { Debug.Assert(finalFalseFall != null); head.Body.Add(new ILExpression(GMCode.B, finalFalseFall)); } return(true); } return(false); }
public bool MatchRepeatStructure(IList <ILNode> body, ILBasicBlock head, int pos) { ILExpression rcount; ILExpression pushZero; int dupMode = 0; ILLabel fallthough; ILLabel repeatBlock; if (head.MatchLastAt(6, GMCode.Push, out rcount) && // header for a repeat, sets it up head.MatchLastAt(5, GMCode.Dup, out dupMode) && head.MatchLastAt(4, GMCode.Push, out pushZero) && pushZero.Code == GMCode.Constant && (pushZero.Operand as ILValue).IntValue == 0 && head.MatchLastAt(3, GMCode.Sle) && head.MatchLastAndBr(GMCode.Bt, out fallthough, out repeatBlock)) { // We have to seeperate the head from other bits of the block // humm, mabye have to put this in the general build routine like we did with push V:( head.Body.RemoveTail(GMCode.Push, GMCode.Dup, GMCode.Push, GMCode.Sle, GMCode.Bt, GMCode.B); ILBasicBlock header_block; ILLabel header_label; if (head.Body.Count == 1) {// The head only has the label, so its safe to use this as the header header_block = head; header_label = head.EntryLabel(); } else { header_label = ILLabel.Generate("R"); // We have to seperate the head. header_block = new ILBasicBlock(); head.Body.Add(new ILExpression(GMCode.B, header_label)); header_block.Body.Add(header_label); body.Insert(pos + 1, header_block); // insert before the block so it looks in order } header_block.Body.Add(new ILExpression(GMCode.Repeat, repeatBlock, rcount)); header_block.Body.Add(new ILExpression(GMCode.B, fallthough)); // NOW we got to find the block that matches ILExpression subOneConstant; ILLabel footerContinue, footerfallThough; /* * * * while (!(start.MatchLastAt(5, GMCode.Push, out subOneConstant) && * subOneConstant.Code == GMCode.Constant && (subOneConstant.Operand as ILValue).IntValue == 1 && * start.MatchLastAt(4, GMCode.Sub) && * start.MatchLastAt(3, GMCode.Dup, out dupMode) && dupMode == 0 && * start.MatchLastAndBr(GMCode.Bt, out footerContinue, out footerfallThough))) * { * ILLabel next = start.GotoLabel(); * start = labelToBasicBlock[next]; * } */// ok, on more complicated stuf like an internal loop, this f***s up, so we are going to do a hack // The popz fallthough comes RIGHT after the decrment for the repeate loop, so we are going to move up one from that // then check it ILBasicBlock popzBlock = labelToBasicBlock[fallthough]; // Debug.Assert((popzBlock.Body[1] as ILExpression).Code == GMCode.Popz); popzBlock.Body.RemoveAt(1); // remove the popz ILBasicBlock footer = body[body.IndexOf(popzBlock) - 1] as ILBasicBlock; if (footer.MatchLastAt(5, GMCode.Push, out subOneConstant) && subOneConstant.Code == GMCode.Constant && (subOneConstant.Operand as ILValue).IntValue == 1 && footer.MatchLastAt(4, GMCode.Sub) && footer.MatchLastAt(3, GMCode.Dup, out dupMode) && dupMode == 0 && footer.MatchLastAndBr(GMCode.Bt, out footerContinue, out footerfallThough)) { Debug.Assert(footerfallThough == fallthough && repeatBlock == footerContinue); // sanity check footer.Body.RemoveTail(GMCode.Push, GMCode.Sub, GMCode.Dup, GMCode.Bt, GMCode.B); footer.Body.Add(new ILExpression(GMCode.B, header_block.EntryLabel())); } else { throw new Exception("F**k me"); } // Found! Some sanity checks thogh /* MAJOR BUG UNFINSHED WORK ALERT! * Ok, so this isn't used in undertale, but at some point, somone might want to do a break or continue * Inside of a repeat statment. I have NO clue why though, use a while? * Anyway, if thats the case then you MUST change the target label of evetyhing going to start, to fallthough, otherwise * the goto cleaner will start screaming at you and do alot of weird stuff * fyi, like the with statments, I am converting this thing into a while loop so I don't have to have * custom loop graph code for these things * So, for now? convert start to loop back to head, head jumps to fallthough, and we remove the popz from the fall though */ // ILLabel.Generate("Block_", nextLabelIndex++); return(true); } return(false); }