public IILLabel DefineLabel(string name = null) { var result = new ILLabel(name, _lastLabelIndex); _lastLabelIndex++; _instructions.Add(result); return result; }
public ILLabelWrapper(ILLabel source, ILPanelEditor editor, string path, string name = null, string label = null) : base(source, editor, path, BuildName(name, editor.Panel, source, "Label"), String.IsNullOrEmpty(label) ? GetLabel(source) : label) { this.source = source; this.source.MouseDoubleClick += OnMouseDoubleClick; }
ControlFlowGraph BuildGraph(List<ILNode> nodes, ILLabel entryLabel) { int index = 0; List<ControlFlowNode> cfNodes = new List<ControlFlowNode>(); ControlFlowNode entryPoint = new ControlFlowNode(index++, 0, ControlFlowNodeType.EntryPoint); cfNodes.Add(entryPoint); ControlFlowNode regularExit = new ControlFlowNode(index++, -1, ControlFlowNodeType.RegularExit); cfNodes.Add(regularExit); ControlFlowNode exceptionalExit = new ControlFlowNode(index++, -1, ControlFlowNodeType.ExceptionalExit); cfNodes.Add(exceptionalExit); // Create graph nodes labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>(); Dictionary<ILNode, ControlFlowNode> astNodeToCfNode = new Dictionary<ILNode, ControlFlowNode>(); foreach(ILBasicBlock node in nodes) { ControlFlowNode cfNode = new ControlFlowNode(index++, -1, ControlFlowNodeType.Normal); cfNodes.Add(cfNode); astNodeToCfNode[node] = cfNode; cfNode.UserData = node; // Find all contained labels foreach (ILLabel label in node.EnumerateSelfAndChildrenRecursive().OfType<ILLabel>()) { labelToCfNode[label] = cfNode; } } // Entry endge ControlFlowNode entryNode = labelToCfNode[entryLabel]; ControlFlowEdge entryEdge = new ControlFlowEdge(entryPoint, entryNode, JumpType.Normal); entryPoint.Outgoing.Add(entryEdge); entryNode.Incoming.Add(entryEdge); // Create edges foreach(ILBasicBlock node in nodes) { ControlFlowNode source = astNodeToCfNode[node]; var allBranchTargets = from e in node.EnumerateSelfAndChildrenRecursive().OfType<ILExpression>() where e.IsBranch() from t in e.GetBranchTargets() select t; // Find all branches foreach(ILLabel target in allBranchTargets) { ControlFlowNode destination; // Labels which are out of out scope will not be int the collection // Insert self edge only if we are sure we are a loop if (labelToCfNode.TryGetValue(target, out destination) && (destination != source || target == node.Body.FirstOrDefault())) { ControlFlowEdge edge = new ControlFlowEdge(source, destination, JumpType.Normal); source.Outgoing.Add(edge); destination.Incoming.Add(edge); } } } return new ControlFlowGraph(cfNodes.ToArray()); }
ControlFlowGraph BuildGraph(List<ILNode> nodes, ILLabel entryLabel) { int index = 0; List<ControlFlowNode> cfNodes = new List<ControlFlowNode>(); ControlFlowNode entryPoint = new ControlFlowNode(index++, 0, ControlFlowNodeType.EntryPoint); cfNodes.Add(entryPoint); ControlFlowNode regularExit = new ControlFlowNode(index++, -1, ControlFlowNodeType.RegularExit); cfNodes.Add(regularExit); ControlFlowNode exceptionalExit = new ControlFlowNode(index++, -1, ControlFlowNodeType.ExceptionalExit); cfNodes.Add(exceptionalExit); // Create graph nodes labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>(); Dictionary<ILNode, ControlFlowNode> astNodeToCfNode = new Dictionary<ILNode, ControlFlowNode>(); foreach(ILNode node in nodes) { ControlFlowNode cfNode = new ControlFlowNode(index++, -1, ControlFlowNodeType.Normal); cfNodes.Add(cfNode); astNodeToCfNode[node] = cfNode; cfNode.UserData = node; // Find all contained labels foreach(ILLabel label in node.GetSelfAndChildrenRecursive<ILLabel>()) { labelToCfNode[label] = cfNode; } } // Entry endge ControlFlowNode entryNode = labelToCfNode[entryLabel]; ControlFlowEdge entryEdge = new ControlFlowEdge(entryPoint, entryNode, JumpType.Normal); entryPoint.Outgoing.Add(entryEdge); entryNode.Incoming.Add(entryEdge); // Create edges foreach(ILNode node in nodes) { ControlFlowNode source = astNodeToCfNode[node]; // Find all branches foreach(ILExpression child in node.GetSelfAndChildrenRecursive<ILExpression>()) { IEnumerable<ILLabel> targets = child.GetBranchTargets(); if (targets != null) { foreach(ILLabel target in targets) { ControlFlowNode destination; // Labels which are out of out scope will not be int the collection if (labelToCfNode.TryGetValue(target, out destination) && destination != source) { ControlFlowEdge edge = new ControlFlowEdge(source, destination, JumpType.Normal); source.Outgoing.Add(edge); destination.Incoming.Add(edge); } } } } } return new ControlFlowGraph(cfNodes.ToArray()); }
ControlFlowGraph BuildGraph(List<ILNode> nodes, ILLabel entryLabel) { cached_ControlFlowGraph.Nodes.Clear(); int index = 0; var cfNodes = cached_ControlFlowGraph.Nodes; ControlFlowNode entryPoint = new ControlFlowNode(index++, 0, ControlFlowNodeType.EntryPoint); cfNodes.Add(entryPoint); ControlFlowNode regularExit = new ControlFlowNode(index++, null, ControlFlowNodeType.RegularExit); cfNodes.Add(regularExit); ControlFlowNode exceptionalExit = new ControlFlowNode(index++, null, ControlFlowNodeType.ExceptionalExit); cfNodes.Add(exceptionalExit); // Create graph nodes labelToCfNode.Clear(); Dictionary<ILNode, ControlFlowNode> astNodeToCfNode = new Dictionary<ILNode, ControlFlowNode>(); List<ILLabel> listLabels = null; foreach(ILBasicBlock node in nodes) { ControlFlowNode cfNode = new ControlFlowNode(index++, null, ControlFlowNodeType.Normal); cfNodes.Add(cfNode); astNodeToCfNode[node] = cfNode; cfNode.UserData = node; // Find all contained labels foreach(ILLabel label in node.GetSelfAndChildrenRecursive<ILLabel>(listLabels ?? (listLabels = new List<ILLabel>()))) { labelToCfNode[label] = cfNode; } } // Entry endge ControlFlowNode entryNode = labelToCfNode[entryLabel]; ControlFlowEdge entryEdge = new ControlFlowEdge(entryPoint, entryNode, JumpType.Normal); entryPoint.Outgoing.Add(entryEdge); entryNode.Incoming.Add(entryEdge); // Create edges List<ILExpression> listExpressions = null; foreach(ILBasicBlock node in nodes) { ControlFlowNode source = astNodeToCfNode[node]; // Find all branches foreach(ILLabel target in node.GetSelfAndChildrenRecursive<ILExpression>(listExpressions ?? (listExpressions = new List<ILExpression>()), e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) { ControlFlowNode destination; // Labels which are out of out scope will not be in the collection // Insert self edge only if we are sure we are a loop if (labelToCfNode.TryGetValue(target, out destination) && (destination != source || target == node.Body.FirstOrDefault())) { ControlFlowEdge edge = new ControlFlowEdge(source, destination, JumpType.Normal); source.Outgoing.Add(edge); destination.Incoming.Add(edge); } } } return cached_ControlFlowGraph; }
/// <summary> /// Generates CIL for the statement. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo) { // Generate code for the start of the statement. var statementLocals = new StatementLocals() { NonDefaultSourceSpanBehavior = true }; GenerateStartOfStatement(generator, optimizationInfo, statementLocals); // Unlike in .NET, in javascript there are no restrictions on what can appear inside // try, catch and finally blocks. The one restriction which causes problems is the // inability to jump out of .NET finally blocks. This is required when break, continue // or return statements appear inside of a finally block. To work around this, when // inside a finally block these instructions throw an exception instead. // Setting the InsideTryCatchOrFinally flag converts BR instructions into LEAVE // instructions so that the finally block is executed correctly. var previousInsideTryCatchOrFinally = optimizationInfo.InsideTryCatchOrFinally; optimizationInfo.InsideTryCatchOrFinally = true; // Finally requires two exception nested blocks. if (this.FinallyBlock != null) generator.BeginExceptionBlock(); // Begin the exception block. generator.BeginExceptionBlock(); // Generate code for the try block. this.TryBlock.GenerateCode(generator, optimizationInfo); // Generate code for the catch block. if (this.CatchBlock != null) { // Begin a catch block. The exception is on the top of the stack. generator.BeginCatchBlock(typeof(JavaScriptException)); // Create a new DeclarativeScope. this.CatchScope.GenerateScopeCreation(generator, optimizationInfo); // Store the error object in the variable provided. generator.Call(ReflectionHelpers.JavaScriptException_ErrorObject); var catchVariable = new NameExpression(this.CatchScope, this.CatchVariableName); catchVariable.GenerateSet(generator, optimizationInfo, PrimitiveType.Any, false); // Make sure the scope is reverted even if an exception is thrown. generator.BeginExceptionBlock(); // Emit code for the statements within the catch block. this.CatchBlock.GenerateCode(generator, optimizationInfo); // Revert the scope. generator.BeginFinallyBlock(); this.CatchScope.GenerateScopeDestruction(generator, optimizationInfo); generator.EndExceptionBlock(); } // Generate code for the finally block. if (this.FinallyBlock != null) { generator.BeginFinallyBlock(); var branches = new List<ILLabel>(); var previousStackSize = optimizationInfo.LongJumpStackSizeThreshold; optimizationInfo.LongJumpStackSizeThreshold = optimizationInfo.BreakOrContinueStackSize; var previousCallback = optimizationInfo.LongJumpCallback; optimizationInfo.LongJumpCallback = (generator2, label) => { // It is not possible to branch out of a finally block - therefore instead of // generating LEAVE instructions we throw an exception then catch it to transfer // control out of the finally block. generator2.LoadInt32(branches.Count); generator2.NewObject(ReflectionHelpers.LongJumpException_Constructor); generator2.Throw(); // Record any branches that are made within the finally code. branches.Add(label); }; // Emit code for the finally block. this.FinallyBlock.GenerateCode(generator, optimizationInfo); // End the main exception block. generator.EndExceptionBlock(); // Begin a catch block to catch any LongJumpExceptions. The exception object is on // the top of the stack. generator.BeginCatchBlock(typeof(LongJumpException)); if (branches.Count > 0) { // switch (exception.RouteID) // { // case 0: goto label1; // case 1: goto label2; // } ILLabel[] switchLabels = new ILLabel[branches.Count]; for (int i = 0; i < branches.Count; i++) switchLabels[i] = generator.CreateLabel(); generator.Call(ReflectionHelpers.LongJumpException_RouteID); generator.Switch(switchLabels); for (int i = 0; i < branches.Count; i++) { generator.DefineLabelPosition(switchLabels[i]); generator.Leave(branches[i]); } } // Reset the state we clobbered. optimizationInfo.LongJumpStackSizeThreshold = previousStackSize; optimizationInfo.LongJumpCallback = previousCallback; } // End the exception block. generator.EndExceptionBlock(); // Reset the InsideTryCatchOrFinally flag. optimizationInfo.InsideTryCatchOrFinally = previousInsideTryCatchOrFinally; // Generate code for the end of the statement. GenerateEndOfStatement(generator, optimizationInfo, statementLocals); }
protected ILExpression CreateExpression(GMCode code, GM_Type[] types, ILLabel operand) { Debug.Assert(operand != null); ILExpression e = new ILExpression(code, operand); e.Types = types; e.Extra = (int)(CurrentRaw & 0xFFFF); e.AddILRange(CurrentPC); return e; }
/// <summary> /// Defines the position of the given label. /// </summary> /// <param name="label"> The label to define. </param> public abstract void DefineLabelPosition(ILLabel label);
void AnalyzeMoveNext() { MethodDefinition moveNextMethod = enumeratorType.Methods.FirstOrDefault(m => m.Name == "MoveNext"); ILBlock ilMethod = CreateILAst(moveNextMethod); if (ilMethod.Body.Count == 0) throw new SymbolicAnalysisFailedException(); ILExpression lastReturnArg; if (!ilMethod.Body.Last().Match(ILCode.Ret, out lastReturnArg)) throw new SymbolicAnalysisFailedException(); // There are two possibilities: if (lastReturnArg.Code == ILCode.Ldloc) { // a) the compiler uses a variable for returns (in debug builds, or when there are try-finally blocks) returnVariable = (ILVariable)lastReturnArg.Operand; returnLabel = ilMethod.Body.ElementAtOrDefault(ilMethod.Body.Count - 2) as ILLabel; if (returnLabel == null) throw new SymbolicAnalysisFailedException(); } else { // b) the compiler directly returns constants returnVariable = null; returnLabel = null; // In this case, the last return must return false. if (lastReturnArg.Code != ILCode.Ldc_I4 || (int)lastReturnArg.Operand != 0) throw new SymbolicAnalysisFailedException(); } ILTryCatchBlock tryFaultBlock = ilMethod.Body[0] as ILTryCatchBlock; List<ILNode> body; int bodyLength; if (tryFaultBlock != null) { // there are try-finally blocks if (returnVariable == null) // in this case, we must use a return variable throw new SymbolicAnalysisFailedException(); // must be a try-fault block: if (tryFaultBlock.CatchBlocks.Count != 0 || tryFaultBlock.FinallyBlock != null || tryFaultBlock.FaultBlock == null) throw new SymbolicAnalysisFailedException(); ILBlock faultBlock = tryFaultBlock.FaultBlock; // Ensure the fault block contains the call to Dispose(). if (faultBlock.Body.Count != 2) throw new SymbolicAnalysisFailedException(); MethodReference disposeMethodRef; ILExpression disposeArg; if (!faultBlock.Body[0].Match(ILCode.Call, out disposeMethodRef, out disposeArg)) throw new SymbolicAnalysisFailedException(); if (GetMethodDefinition(disposeMethodRef) != disposeMethod || !disposeArg.MatchThis()) throw new SymbolicAnalysisFailedException(); if (!faultBlock.Body[1].Match(ILCode.Endfinally)) throw new SymbolicAnalysisFailedException(); body = tryFaultBlock.TryBlock.Body; bodyLength = body.Count; } else { // no try-finally blocks body = ilMethod.Body; if (returnVariable == null) bodyLength = body.Count - 1; // all except for the return statement else bodyLength = body.Count - 2; // all except for the return label and statement } // Now verify that the last instruction in the body is 'ret(false)' if (returnVariable != null) { // If we don't have a return variable, we already verified that above. // If we do have one, check for 'stloc(returnVariable, ldc.i4(0))' // Maybe might be a jump to the return label after the stloc: ILExpression leave = body.ElementAtOrDefault(bodyLength - 1) as ILExpression; if (leave != null && (leave.Code == ILCode.Br || leave.Code == ILCode.Leave) && leave.Operand == returnLabel) bodyLength--; ILExpression store0 = body.ElementAtOrDefault(bodyLength - 1) as ILExpression; if (store0 == null || store0.Code != ILCode.Stloc || store0.Operand != returnVariable) throw new SymbolicAnalysisFailedException(); if (store0.Arguments[0].Code != ILCode.Ldc_I4 || (int)store0.Arguments[0].Operand != 0) throw new SymbolicAnalysisFailedException(); bodyLength--; // don't conside the stloc instruction to be part of the body } // The last element in the body usually is a label pointing to the 'ret(false)' returnFalseLabel = body.ElementAtOrDefault(bodyLength - 1) as ILLabel; // Note: in Roslyn-compiled code, returnFalseLabel may be null. var rangeAnalysis = new StateRangeAnalysis(body[0], StateRangeAnalysisMode.IteratorMoveNext, stateField); int pos = rangeAnalysis.AssignStateRanges(body, bodyLength); rangeAnalysis.EnsureLabelAtPos(body, ref pos, ref bodyLength); var labels = rangeAnalysis.CreateLabelRangeMapping(body, pos, bodyLength); ConvertBody(body, pos, bodyLength, labels); }
/// <summary> /// Unconditionally branches to the given label. /// </summary> /// <param name="label"> The label to branch to. </param> public override void Branch(ILLabel label) { Log("br", label); this.generator.Branch(label); }
/// <summary> /// Branches to the given label if the two values on the top of the stack are not equal. /// </summary> /// <param name="label"> The label to branch to. </param> public abstract void BranchIfNotEqual(ILLabel label);
/// <summary> /// Unconditionally branches to the given label. Unlike the regular branch instruction, /// this instruction can exit out of try, filter and catch blocks. /// </summary> /// <param name="label"> The label to branch to. </param> public override void Leave(ILLabel label) { Log("leave", label); this.generator.Leave(label); }
/// <summary> /// Outputs an instruction and a label to the log. /// </summary> /// <param name="instruction"> The instruction to output. </param> /// <param name="label"> The label to output. </param> private void Log(string instruction, ILLabel label) { LogInstruction(instruction, string.Empty); AppendLabel(label); }
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); }
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 List <ILBasicBlock> GetAllCaseBlocks(IList <ILNode> body, ILBasicBlock head, int pos, out ILExpression condition, out ILLabel fallout) { ILExpression fswitch = new ILExpression(GMCode.Switch, null); List <ILBasicBlock> caseBlocks = new List <ILBasicBlock>(); int swtichStart = pos; ILLabel trueLabel; ILLabel falseLabel; while (MatchSwitchCase(head, out trueLabel, out falseLabel, out condition)) { caseBlocks.Add(head); head = body.ElementAtOrDefault(++swtichStart) as ILBasicBlock; } ILBasicBlock switchHead = caseBlocks.First(); if (!switchHead.ElementAtLastOrDefault(5).isNodeResolved()) { fallout = null; condition = null; return(null); } // caseBlocks.Reverse(); // reverse the order so its correct Debug.Assert(switchHead.MatchLastAt(6, GMCode.Push, out condition)); // return false; switchHead.RemoveAt(switchHead.Body.Count - 6); // ugh, might have to change the matchLastAt fallout = caseBlocks.Last().OperandLabelLastAt(0); return(caseBlocks); }
/// <summary> /// Branches to the given label if the first value on the stack is greater than the second /// value on the stack. If the operands are integers then they are treated as if they are /// unsigned. If the operands are floating point numbers then a NaN value will trigger a /// branch. /// </summary> /// <param name="label"> The label to branch to. </param> public abstract void BranchIfGreaterThanUnsigned(ILLabel label);
/// <summary> /// Branches to the given label if the value on the top of the stack is zero. /// </summary> /// <param name="label"> The label to branch to. </param> public override void BranchIfZero(ILLabel label) { Log("brfalse", label); this.generator.BranchIfZero(label); }
/// <summary> /// Branches to the given label if the first value on the stack is less than or equal to /// the second value on the stack. /// </summary> /// <param name="label"> The label to branch to. </param> public abstract void BranchIfLessThanOrEqual(ILLabel label);
/// <summary> /// Branches to the given label if the value on the top of the stack is non-zero, true or /// non-null. /// </summary> /// <param name="label"> The label to branch to. </param> public override void BranchIfNotZero(ILLabel label) { Log("brtrue", label); this.generator.BranchIfNotZero(label); }
/// <summary> /// Branches to the given label if the value on the top of the stack is non-zero, true or /// non-null. /// </summary> /// <param name="label"> The label to branch to. </param> public void BranchIfTrue(ILLabel label) { BranchIfNotZero(label); }
/// <summary> /// Branches to the given label if the two values on the top of the stack are not equal. /// </summary> /// <param name="label"> The label to branch to. </param> public override void BranchIfNotEqual(ILLabel label) { Log("bne", label); this.generator.BranchIfNotEqual(label); }
/// <summary> /// Creates a jump table. A value is popped from the stack - this value indicates the /// index of the label in the <paramref name="labels"/> array to jump to. /// </summary> /// <param name="labels"> A array of labels. </param> public abstract void Switch(ILLabel[] labels);
/// <summary> /// Branches to the given label if the first value on the stack is greater than the second /// value on the stack. /// </summary> /// <param name="label"> The label to branch to. </param> public override void BranchIfGreaterThan(ILLabel label) { Log("bgt", label); this.generator.BranchIfGreaterThan(label); }
ILExpression MakeGoTo(ILLabel targetLabel) { Debug.Assert(targetLabel != null); if (targetLabel == returnFalseLabel) return new ILExpression(ILCode.YieldBreak, null); else return new ILExpression(ILCode.Br, targetLabel); }
/// <summary> /// Branches to the given label if the first value on the stack is greater than the second /// value on the stack. If the operands are integers then they are treated as if they are /// unsigned. If the operands are floating point numbers then a NaN value will trigger a /// branch. /// </summary> /// <param name="label"> The label to branch to. </param> public override void BranchIfGreaterThanUnsigned(ILLabel label) { Log("bgt.un", label); this.generator.BranchIfGreaterThanUnsigned(label); }
List<ILNode> FindLoops(HashSet<ControlFlowNode> nodes, ControlFlowNode entryPoint, bool excludeEntryPoint) { List<ILNode> result = new List<ILNode>(); Queue<ControlFlowNode> agenda = new Queue<ControlFlowNode>(); agenda.Enqueue(entryPoint); while(agenda.Count > 0) { ControlFlowNode node = agenda.Dequeue(); if (nodes.Contains(node) && node.DominanceFrontier.Contains(node) && (node != entryPoint || !excludeEntryPoint)) { HashSet<ControlFlowNode> loopContents = new HashSet<ControlFlowNode>(); FindLoopContents(nodes, loopContents, node, node); // Move the content into loop block nodes.ExceptWith(loopContents); ILLabel entryLabel = new ILLabel() { Name = "Loop_" + (nextBlockIndex++) }; ((ILBlock)node.UserData).Body.Insert(0, entryLabel); result.Add(new ILLoop() { ContentBlock = new ILBlock(FindLoops(loopContents, node, true)) { EntryPoint = entryLabel } }); } // Using the dominator tree should ensure we find the the widest loop first foreach(var child in node.DominatorTreeChildren) { agenda.Enqueue(child); } } // Add whatever is left foreach(var node in nodes) { result.Add((ILNode)node.UserData); } return result; }
/// <summary> /// Branches to the given label if the first value on the stack is greater than or equal to /// the second value on the stack. /// </summary> /// <param name="label"> The label to branch to. </param> public override void BranchIfGreaterThanOrEqual(ILLabel label) { Log("bge", label); this.generator.BranchIfGreaterThanOrEqual(label); }
/// <summary> /// Searches for the given label in the break/continue stack. /// </summary> /// <param name="label"></param> /// <returns> The depth of the label in the stack. Zero indicates the bottom of the stack. /// <c>-1</c> is returned if the label was not found. </returns> private int GetBreakOrContinueLabelDepth(ILLabel label) { if (label == null) throw new ArgumentNullException("label"); int depth = this.breakOrContinueStack.Count - 1; foreach (var info in this.breakOrContinueStack) { if (info.BreakTarget == label) return depth; if (info.ContinueTarget == label) return depth; depth --; } return -1; }
/// <summary> /// Branches to the given label if the first value on the stack is less than the second /// value on the stack. /// </summary> /// <param name="label"> The label to branch to. </param> public override void BranchIfLessThan(ILLabel label) { Log("blt", label); this.generator.BranchIfLessThan(label); }
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); }
/// <summary> /// Branches to the given label if the first value on the stack is less than or equal to /// the second value on the stack. /// </summary> /// <param name="label"> The label to branch to. </param> public override void BranchIfLessThanOrEqual(ILLabel label) { Log("ble", label); this.generator.BranchIfLessThanOrEqual(label); }
/// <summary> /// Branches to the given label if the first value on the stack is greater than or equal to /// the second value on the stack. /// </summary> /// <param name="label"> The label to branch to. </param> public abstract void BranchIfGreaterThanOrEqual(ILLabel label);
/// <summary> /// Branches to the given label if the first value on the stack is less than or equal to /// the second value on the stack. If the operands are integers then they are treated as /// if they are unsigned. If the operands are floating point numbers then a NaN value will /// trigger a branch. /// </summary> /// <param name="label"> The label to branch to. </param> public override void BranchIfLessThanOrEqualUnsigned(ILLabel label) { Log("ble.un", label); this.generator.BranchIfLessThanOrEqualUnsigned(label); }
/// <summary> /// Branches to the given label if the first value on the stack is less than the second /// value on the stack. /// </summary> /// <param name="label"> The label to branch to. </param> public abstract void BranchIfLessThan(ILLabel label);
/// <summary> /// Branches to the given label if the value on the top of the stack is zero, false or /// null. /// </summary> /// <param name="label"> The label to branch to. </param> public void BranchIfFalse(ILLabel label) { BranchIfZero(label); }
/// <summary> /// Branches to the given label if the first value on the stack is less than the second /// value on the stack. If the operands are integers then they are treated as if they are /// unsigned. If the operands are floating point numbers then a NaN value will trigger a /// branch. /// </summary> /// <param name="label"> The label to branch to. </param> public abstract void BranchIfLessThanUnsigned(ILLabel label);
/// <summary> /// Branches to the given label if the value on the top of the stack is zero, false or /// null. /// </summary> /// <param name="label"> The label to branch to. </param> public void BranchIfNull(ILLabel label) { BranchIfZero(label); }
/// <summary> /// Branches to the given label if the value on the top of the stack is non-zero, true or /// non-null. /// </summary> /// <param name="label"> The label to branch to. </param> public void BranchIfNotNull(ILLabel label) { BranchIfNotZero(label); }
/// <summary> /// Branches to the given label if the value on the top of the stack is zero, false or /// null. /// </summary> /// <param name="label"> The label to branch to. </param> public abstract void BranchIfZero(ILLabel label);
/// <summary> /// Unconditionally branches to the given label. Unlike the regular branch instruction, /// this instruction can exit out of try, filter and catch blocks. /// </summary> /// <param name="label"> The label to branch to. </param> public abstract void Leave(ILLabel label);
/// <summary> /// Branches to the given label if the first value on the stack is greater than the second /// value on the stack. /// </summary> /// <param name="label"> The label to branch to. </param> public abstract void BranchIfGreaterThan(ILLabel label);
void AnalyzeMoveNext() { MethodDefinition moveNextMethod = enumeratorType.Methods.FirstOrDefault(m => m.Name == "MoveNext"); ILBlock ilMethod = CreateILAst(moveNextMethod); if (ilMethod.Body.Count == 0) throw new YieldAnalysisFailedException(); ILExpression lastReturnArg; if (!ilMethod.Body.Last().Match(ILCode.Ret, out lastReturnArg)) throw new YieldAnalysisFailedException(); // There are two possibilities: if (lastReturnArg.Code == ILCode.Ldloc) { // a) the compiler uses a variable for returns (in debug builds, or when there are try-finally blocks) returnVariable = (ILVariable)lastReturnArg.Operand; returnLabel = ilMethod.Body.ElementAtOrDefault(ilMethod.Body.Count - 2) as ILLabel; if (returnLabel == null) throw new YieldAnalysisFailedException(); } else { // b) the compiler directly returns constants returnVariable = null; returnLabel = null; // In this case, the last return must return false. if (lastReturnArg.Code != ILCode.Ldc_I4 || (int)lastReturnArg.Operand != 0) throw new YieldAnalysisFailedException(); } ILTryCatchBlock tryFaultBlock = ilMethod.Body[0] as ILTryCatchBlock; List<ILNode> body; int bodyLength; if (tryFaultBlock != null) { // there are try-finally blocks if (returnVariable == null) // in this case, we must use a return variable throw new YieldAnalysisFailedException(); // must be a try-fault block: if (tryFaultBlock.CatchBlocks.Count != 0 || tryFaultBlock.FinallyBlock != null || tryFaultBlock.FaultBlock == null) throw new YieldAnalysisFailedException(); ILBlock faultBlock = tryFaultBlock.FaultBlock; // Ensure the fault block contains the call to Dispose(). if (faultBlock.Body.Count != 2) throw new YieldAnalysisFailedException(); MethodReference disposeMethodRef; ILExpression disposeArg; if (!faultBlock.Body[0].Match(ILCode.Call, out disposeMethodRef, out disposeArg)) throw new YieldAnalysisFailedException(); if (GetMethodDefinition(disposeMethodRef) != disposeMethod || !LoadFromArgument.This.Match(disposeArg)) throw new YieldAnalysisFailedException(); if (!faultBlock.Body[1].Match(ILCode.Endfinally)) throw new YieldAnalysisFailedException(); body = tryFaultBlock.TryBlock.Body; bodyLength = body.Count; } else { // no try-finally blocks body = ilMethod.Body; if (returnVariable == null) bodyLength = body.Count - 1; // all except for the return statement else bodyLength = body.Count - 2; // all except for the return label and statement } // Now verify that the last instruction in the body is 'ret(false)' if (returnVariable != null) { // If we don't have a return variable, we already verified that above. // If we do have one, check for 'stloc(returnVariable, ldc.i4(0))' // Maybe might be a jump to the return label after the stloc: ILExpression leave = body.ElementAtOrDefault(bodyLength - 1) as ILExpression; if (leave != null && (leave.Code == ILCode.Br || leave.Code == ILCode.Leave) && leave.Operand == returnLabel) bodyLength--; ILExpression store0 = body.ElementAtOrDefault(bodyLength - 1) as ILExpression; if (store0 == null || store0.Code != ILCode.Stloc || store0.Operand != returnVariable) throw new YieldAnalysisFailedException(); if (store0.Arguments[0].Code != ILCode.Ldc_I4 || (int)store0.Arguments[0].Operand != 0) throw new YieldAnalysisFailedException(); bodyLength--; // don't conside the stloc instruction to be part of the body } // verify that the last element in the body is a label pointing to the 'ret(false)' returnFalseLabel = body.ElementAtOrDefault(bodyLength - 1) as ILLabel; if (returnFalseLabel == null) throw new YieldAnalysisFailedException(); InitStateRanges(body[0]); int pos = AssignStateRanges(body, bodyLength, forDispose: false); if (pos > 0 && body[pos - 1] is ILLabel) { pos--; } else { // ensure that the first element at body[pos] is a label: ILLabel newLabel = new ILLabel(); newLabel.Name = "YieldReturnEntryPoint"; ranges[newLabel] = ranges[body[pos]]; // give the label the range of the instruction at body[pos] body.Insert(pos, newLabel); bodyLength++; } List<KeyValuePair<ILLabel, StateRange>> labels = new List<KeyValuePair<ILLabel, StateRange>>(); for (int i = pos; i < bodyLength; i++) { ILLabel label = body[i] as ILLabel; if (label != null) { labels.Add(new KeyValuePair<ILLabel, StateRange>(label, ranges[label])); } } ConvertBody(body, pos, bodyLength, labels); }
/// <summary> /// Branches to the given label if the first value on the stack is greater than or equal to /// the second value on the stack. If the operands are integers then they are treated as /// if they are unsigned. If the operands are floating point numbers then a NaN value will /// trigger a branch. /// </summary> /// <param name="label"> The label to branch to. </param> public abstract void BranchIfGreaterThanOrEqualUnsigned(ILLabel label);
void ConvertBody(List<ILNode> body, int startPos, int bodyLength, List<KeyValuePair<ILLabel, StateRange>> labels) { newBody = new List<ILNode>(); newBody.Add(MakeGoTo(labels, 0)); List<SetState> stateChanges = new List<SetState>(); int currentState = -1; // Copy all instructions from the old body to newBody. for (int pos = startPos; pos < bodyLength; pos++) { ILExpression expr = body[pos] as ILExpression; if (expr != null && expr.Code == ILCode.Stfld && expr.Arguments[0].MatchThis()) { // Handle stores to 'state' or 'current' if (GetFieldDefinition(expr.Operand as FieldReference) == stateField) { if (expr.Arguments[1].Code != ILCode.Ldc_I4) throw new SymbolicAnalysisFailedException(); currentState = (int)expr.Arguments[1].Operand; stateChanges.Add(new SetState(newBody.Count, currentState)); } else if (GetFieldDefinition(expr.Operand as FieldReference) == currentField) { newBody.Add(new ILExpression(ILCode.YieldReturn, null, expr.Arguments[1])); } else { newBody.Add(body[pos]); } } else if (returnVariable != null && expr != null && expr.Code == ILCode.Stloc && expr.Operand == returnVariable) { // handle store+branch to the returnVariable ILExpression br = body.ElementAtOrDefault(++pos) as ILExpression; if (br == null || !(br.Code == ILCode.Br || br.Code == ILCode.Leave) || br.Operand != returnLabel || expr.Arguments[0].Code != ILCode.Ldc_I4) throw new SymbolicAnalysisFailedException(); int val = (int)expr.Arguments[0].Operand; if (val == 0) { newBody.Add(new ILExpression(ILCode.YieldBreak, null)); } else if (val == 1) { newBody.Add(MakeGoTo(labels, currentState)); } else { throw new SymbolicAnalysisFailedException(); } } else if (expr != null && expr.Code == ILCode.Ret) { if (expr.Arguments.Count != 1 || expr.Arguments[0].Code != ILCode.Ldc_I4) throw new SymbolicAnalysisFailedException(); // handle direct return (e.g. in release builds) int val = (int)expr.Arguments[0].Operand; if (val == 0) { newBody.Add(new ILExpression(ILCode.YieldBreak, null)); } else if (val == 1) { newBody.Add(MakeGoTo(labels, currentState)); } else { throw new SymbolicAnalysisFailedException(); } } else if (expr != null && expr.Code == ILCode.Call && expr.Arguments.Count == 1 && expr.Arguments[0].MatchThis()) { MethodDefinition method = GetMethodDefinition(expr.Operand as MethodReference); if (method == null) throw new SymbolicAnalysisFailedException(); StateRange stateRange; if (method == disposeMethod) { // Explicit call to dispose is used for "yield break;" within the method. ILExpression br = body.ElementAtOrDefault(++pos) as ILExpression; if (br == null || !(br.Code == ILCode.Br || br.Code == ILCode.Leave) || br.Operand != returnFalseLabel) throw new SymbolicAnalysisFailedException(); newBody.Add(new ILExpression(ILCode.YieldBreak, null)); } else if (finallyMethodToStateRange.TryGetValue(method, out stateRange)) { // Call to Finally-method int index = stateChanges.FindIndex(ss => stateRange.Contains(ss.NewState)); if (index < 0) throw new SymbolicAnalysisFailedException(); ILLabel label = new ILLabel(); label.Name = "JumpOutOfTryFinally" + stateChanges[index].NewState; newBody.Add(new ILExpression(ILCode.Leave, label)); SetState stateChange = stateChanges[index]; // Move all instructions from stateChange.Pos to newBody.Count into a try-block stateChanges.RemoveRange(index, stateChanges.Count - index); // remove all state changes up to the one we found ILTryCatchBlock tryFinally = new ILTryCatchBlock(); tryFinally.TryBlock = new ILBlock(newBody.GetRange(stateChange.NewBodyPos, newBody.Count - stateChange.NewBodyPos)); newBody.RemoveRange(stateChange.NewBodyPos, newBody.Count - stateChange.NewBodyPos); // remove all nodes that we just moved into the try block tryFinally.CatchBlocks = new List<ILTryCatchBlock.CatchBlock>(); tryFinally.FinallyBlock = ConvertFinallyBlock(method); newBody.Add(tryFinally); newBody.Add(label); } } else { newBody.Add(body[pos]); } } newBody.Add(new ILExpression(ILCode.YieldBreak, null)); }
protected JSExpression Translate_Br(ILExpression node, ILLabel targetLabel) { return new JSGotoExpression(targetLabel.Name); }
/// <summary> /// Branches to the given label if the first value on the stack is less than or equal to /// the second value on the stack. If the operands are integers then they are treated as /// if they are unsigned. If the operands are floating point numbers then a NaN value will /// trigger a branch. /// </summary> /// <param name="label"> The label to branch to. </param> public abstract void BranchIfLessThanOrEqualUnsigned(ILLabel label);
protected ILLabel GetLabel(int position) { ILLabel l; if (!labels.TryGetValue(position, out l)) labels[position] = l = new ILLabel(position); return l; }
/// <summary> /// Group input into a set of blocks that can be later arbitraliby schufled. /// The method adds necessary branches to make control flow between blocks /// explicit and thus order independent. /// </summary> void SplitToMovableBlocks(ILBlock block) { // Remve no-ops // TODO: Assign the no-op range to someting block.Body = block.Body.Where(n => !(n is ILExpression && ((ILExpression)n).OpCode == OpCodes.Nop)).ToList(); List<ILNode> moveableBlocks = new List<ILNode>(); ILMoveableBlock moveableBlock = new ILMoveableBlock() { OriginalOrder = (nextBlockIndex++) }; moveableBlocks.Add(moveableBlock); block.EntryPoint = new ILLabel() { Name = "Block_" + moveableBlock.OriginalOrder }; moveableBlock.Body.Add(block.EntryPoint); if (block.Body.Count > 0) { moveableBlock.Body.Add(block.Body[0]); for (int i = 1; i < block.Body.Count; i++) { ILNode lastNode = block.Body[i - 1]; ILNode currNode = block.Body[i]; // Insert split if ((currNode is ILLabel && !(lastNode is ILLabel)) || lastNode is ILTryCatchBlock || currNode is ILTryCatchBlock || (lastNode is ILExpression) && ((ILExpression)lastNode).OpCode.IsBranch() || (currNode is ILExpression) && ((ILExpression)currNode).OpCode.IsBranch()) { ILBlock lastBlock = moveableBlock; moveableBlock = new ILMoveableBlock() { OriginalOrder = (nextBlockIndex++) }; moveableBlocks.Add(moveableBlock); // Explicit branch from one block to other // (unless the last expression was unconditional branch) if (!(lastNode is ILExpression) || ((ILExpression)lastNode).OpCode.CanFallThough()) { ILLabel blockLabel = new ILLabel() { Name = "Block_" + moveableBlock.OriginalOrder }; lastBlock.Body.Add(new ILExpression(OpCodes.Br, blockLabel)); moveableBlock.Body.Add(blockLabel); } } moveableBlock.Body.Add(currNode); } } block.Body = moveableBlocks; return; }
/// <summary> /// Unconditionally branches to the given label. /// </summary> /// <param name="label"> The label to branch to. </param> public abstract void Branch(ILLabel label);
/// <summary> /// Pushes information about break or continue targets to a stack. /// </summary> /// <param name="labelNames"> The label names associated with the break or continue target. /// Can be <c>null</c>. </param> /// <param name="breakTarget"> The IL label to jump to if a break statement is encountered. </param> /// <param name="continueTarget"> The IL label to jump to if a continue statement is /// encountered. Can be <c>null</c>. </param> /// <param name="labelledOnly"> <c>true</c> if break or continue statements without a label /// should ignore this entry; <c>false</c> otherwise. </param> public void PushBreakOrContinueInfo(IList<string> labelNames, ILLabel breakTarget, ILLabel continueTarget, bool labelledOnly) { if (breakTarget == null) throw new ArgumentNullException("breakTarget"); // Check the label doesn't already exist. if (labelNames != null) { foreach (var labelName in labelNames) foreach (var info in this.breakOrContinueStack) if (info.LabelNames != null && info.LabelNames.Contains(labelName) == true) throw new JavaScriptException(this.Engine, "SyntaxError", string.Format("Label '{0}' has already been declared", labelName)); } // Push the info to the stack. this.breakOrContinueStack.Push(new BreakOrContinueInfo() { LabelNames = labelNames, LabelledOnly = labelledOnly, BreakTarget = breakTarget, ContinueTarget = continueTarget }); }
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); }
/// <summary> /// Emits code to branch between statements, even if code generation is within a finally /// block (where unconditional branches are not allowed). /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="targetLabel"> The label to jump to. </param> public void EmitLongJump(ILGenerator generator, ILLabel targetLabel) { if (this.LongJumpCallback == null) { // Code generation is not inside a finally block. if (this.InsideTryCatchOrFinally == true) generator.Leave(targetLabel); else generator.Branch(targetLabel); } else { // The long jump is occurring within a finally block. // Check if the target is inside or outside the finally block. int depth = this.GetBreakOrContinueLabelDepth(targetLabel); if (depth < this.LongJumpStackSizeThreshold) { // The target label is outside the finally block. Call the callback to emit // the jump. this.LongJumpCallback(generator, targetLabel); } else { // The target label is inside the finally block. if (this.InsideTryCatchOrFinally == true) generator.Leave(targetLabel); else generator.Branch(targetLabel); } } }
/// <summary> /// Branches to the given label if the value on the top of the stack is non-zero, true or /// non-null. /// </summary> /// <param name="label"> The label to branch to. </param> public abstract void BranchIfNotZero(ILLabel label);
bool MatchSwitchCaseAndBuildExpression(ILBasicBlock head, out ILLabel trueLabel, out ILLabel falseLabel, out ILExpression expr) { ILExpression condition; if (MatchSwitchCase(head, out trueLabel, out falseLabel, out condition)) { var ranges = head.JoinILRangesFromTail(5); expr = new ILExpression(GMCode.Bt, trueLabel, new ILExpression(GMCode.Seq, null, condition)); expr.WithILRanges(ranges); return(true); } trueLabel = default(ILLabel); falseLabel = default(ILLabel); expr = default(ILExpression); return(false); }