protected AstLabel DefineLabel(string name)
 {
     DefineLabelHook();
     return(AstLabel.CreateLabel(name));
 }
Example #2
0
            /// <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));
        }