protected AstLabel DefineLabel(string name) { DefineLabelHook(); return(AstLabel.CreateLabel(name)); }
/// <summary> /// PASS 1: Analyze Branches /// </summary> private void AnalyzeBranches() { _skipPc = new HashSet <uint>(); _analyzedPc = new HashSet <uint>(); var branchesToAnalyze = new Queue <uint>(); _labels[_entryPc] = AstLabel.CreateLabel("EntryPoint"); var endPc = _instructionReader.EndPc; _pc = _entryPc; _minPc = uint.MaxValue; _maxPc = uint.MinValue; branchesToAnalyze.Enqueue(_entryPc); while (true) { HandleNewBranch: var endOfBranchFound = false; if (branchesToAnalyze.Count == 0) { break; } for (_pc = branchesToAnalyze.Dequeue(); _pc <= endPc; _pc += 4) { // If already analyzed, stop scanning this branch. if (_analyzedPc.Contains(_pc)) { break; } _analyzedPc.Add(_pc); //Console.WriteLine("%08X".Sprintf(PC)); if (_analyzedPc.Count > MaxNumberOfInstructions) { throw new InvalidDataException( $"Code sequence too long: >= {MaxNumberOfInstructions} at 0x{_entryPc:X8}"); } UpdateMinMax(_pc); //Console.WriteLine(" PC:{0:X}", PC); var instruction = _instructionReader[_pc]; var branchInfo = DynarecBranchAnalyzer.GetBranchInfo(instruction); var disassemblerInfo = _mipsDisassembler.Disassemble(_pc, instruction); LogInstruction(_pc, instruction); // Break if (disassemblerInfo.InstructionInfo.Name == "break") { break; } // Branch instruction. //else if (BranchInfo.HasFlag(DynarecBranchAnalyzer.JumpFlags.JumpAlways)) else if (branchInfo.HasFlag(DynarecBranchAnalyzer.JumpFlags.JumpInstruction)) { //Console.WriteLine("Instruction"); var jumpAddress = instruction.GetJumpAddress(_memory, _pc); // Located a jump-always instruction with a delayed slot. Process next instruction too. if (branchInfo.HasFlag(DynarecBranchAnalyzer.JumpFlags.AndLink)) { // Just a function call. Continue analyzing. } else { if (PspMemory.IsAddressValid(jumpAddress)) { if (!_labelsJump.ContainsKey(jumpAddress)) { if (AddressInsideFunction(jumpAddress)) { //Console.WriteLine("JumpAddress: {0:X8}", JumpAddress); _labelsJump[jumpAddress] = AstLabel.CreateLabel($"Jump_0x{jumpAddress:X8}"); branchesToAnalyze.Enqueue(jumpAddress); } } } endOfBranchFound = true; continue; } } else if (branchInfo.HasFlag(DynarecBranchAnalyzer.JumpFlags.BranchOrJumpInstruction)) { var branchAddress = instruction.GetBranchAddress(_pc); if (!_labels.ContainsKey(branchAddress)) { //Console.WriteLine("BranchAddress: {0:X8}", BranchAddress); UpdateMinMax(branchAddress); _labels[branchAddress] = AstLabel.CreateLabel($"Label_0x{branchAddress:X8}"); branchesToAnalyze.Enqueue(branchAddress); } } else if (branchInfo.HasFlag(DynarecBranchAnalyzer.JumpFlags.SyscallInstruction)) { // On this special Syscall if (instruction.Code == SyscallInfo.NativeCallSyscallCode) { //PC += 4; goto HandleNewBranch; } } // Jump-Always found. And we have also processed the delayed branch slot. End the branch. if (endOfBranchFound) { endOfBranchFound = false; goto HandleNewBranch; } } } //Console.WriteLine("FunctionSegment({0:X8}-{1:X8})", MinPC, MaxPC); foreach (var labelAddress in _labelsJump.Keys.ToArray()) { if (!AddressInsideFunction(labelAddress)) { _labelsJump.Remove(labelAddress); } } _cpuEmitter.BranchCount = _labels.Count; }
protected virtual void _Generate(AstNodeStmSwitch Switch) { var allCaseValues = Switch.Cases.Select(Case => Case.CaseValue); var caseValues = allCaseValues as IList <object> ?? allCaseValues.ToList(); if (caseValues.Count != caseValues.Distinct().Count()) { throw new Exception("Repeated case in switch!"); } // Check types and unique values. var endCasesLabel = AstLabel.CreateLabel("EndCasesLabel"); var defaultLabel = AstLabel.CreateLabel("DefaultLabel"); if (Switch.Cases.Length > 0) { var commonType = Switch.Cases.First().CaseValue.GetType(); if (Switch.Cases.Any(Case => Case.CaseValue.GetType() != commonType)) { throw new Exception("All cases should have the same type"); } var doneSpecialized = false; // Specialized constant-time integer switch (if possible) if (AstUtils.IsIntegerType(commonType)) { var commonMin = Switch.Cases.Min(Case => AstUtils.CastType <long>(Case.CaseValue)); var commonMax = Switch.Cases.Max(Case => AstUtils.CastType <long>(Case.CaseValue)); var casesLength = commonMax - commonMin + 1; // No processing tables greater than 4096 elements. if (casesLength <= 4096) { var labels = new AstLabel[casesLength]; for (var n = 0; n < casesLength; n++) { labels[n] = defaultLabel; } foreach (var Case in Switch.Cases) { var realValue = AstUtils.CastType <long>(Case.CaseValue); var offset = realValue - commonMin; labels[offset] = AstLabel.CreateLabel("Case_" + realValue); } /* * //var SwitchVarLocal = AstLocal.Create(AllCaseValues.First().GetType(), "SwitchVarLocal" + SwitchVarCount++); * //Generate(new AstNodeStmAssign(new AstNodeExprLocal(SwitchVarLocal), Switch.SwitchValue - new AstNodeExprCast(CommonType, CommonMin))); * //Generate(new AstNodeStmIfElse(new AstNodeExprBinop(new AstNodeExprLocal(SwitchVarLocal), "<", 0), new AstNodeStmGotoAlways(DefaultLabel))); * //Generate(new AstNodeStmIfElse(new AstNodeExprBinop(new AstNodeExprLocal(SwitchVarLocal), ">=", CasesLength), new AstNodeStmGotoAlways(DefaultLabel))); * //Generate(new AstNodeExprLocal(SwitchVarLocal)); */ Generate(Switch.SwitchValue - new AstNodeExprCast(commonType, commonMin)); Emit(OpCodes.Switch, labels); Generate(new AstNodeStmGotoAlways(defaultLabel)); foreach (var Case in Switch.Cases) { var realValue = AstUtils.CastType <long>(Case.CaseValue); var offset = realValue - commonMin; Generate(new AstNodeStmLabel(labels[offset])); { Generate(Case.Code); } Generate(new AstNodeStmGotoAlways(endCasesLabel)); } doneSpecialized = true; } else { // TODO: find a common shift and masks for all the values to reduce CasesLength. // TODO: On too large test cases, split them recursively in: // if (Var < Half) { switch(Var - LowerPartMin) { ... } } else { switch(Var - Half - UpperPartMin) { ... } } } } // Specialized switch for strings (checking length, then hash, then contents) else if (commonType == typeof(string)) { // TODO! } // Generic if/else if (!doneSpecialized) { var switchVarLocal = AstLocal.Create(caseValues.First().GetType(), "SwitchVarLocal" + _switchVarCount++); Generate(new AstNodeStmAssign(new AstNodeExprLocal(switchVarLocal), Switch.SwitchValue)); //Switch.Cases foreach (var Case in Switch.Cases) { var labelSkipThisCase = AstLabel.CreateLabel("LabelCase" + Case.CaseValue); Generate(new AstNodeStmGotoIfFalse(labelSkipThisCase, new AstNodeExprBinop(new AstNodeExprLocal(switchVarLocal), "==", new AstNodeExprImm(Case.CaseValue)))); Generate(Case.Code); Generate(new AstNodeStmGotoAlways(endCasesLabel)); Generate(new AstNodeStmLabel(labelSkipThisCase)); } } } Generate(new AstNodeStmLabel(defaultLabel)); if (Switch.CaseDefault != null) { Generate(Switch.CaseDefault.Code); } Generate(new AstNodeStmLabel(endCasesLabel)); }