Exemple #1
0
        private void Set(byte value, string instr, AddrMode addressMode, int cycles)
        {
            var op = new OpcodeInfo();

            op.Instruction = instr;
            op.AddressMode = addressMode;
            op.Cycles      = cycles;
            if (Opcodes[value] != null)
            {
                throw new Exception("opcode " + value + " already assigned");
            }
            Opcodes[value] = op;
        }
Exemple #2
0
        //  TODO: Don't use static method, create en interface and inject in consumer.
        public static ulong CalculateExtraCycles(AddrMode addrMode, bool addressCalculationCrossedPageBoundary)
        {
            ulong extraCycles = 0;

            if (addressCalculationCrossedPageBoundary &&
                (addrMode == AddrMode.ABS_X ||
                 addrMode == AddrMode.ABS_Y ||
                 addrMode == AddrMode.IND_IX
                ))
            {
                extraCycles++;
            }
            return(extraCycles);
        }
Exemple #3
0
        protected void EmitDictionaryLookup(NodeFactory factory, ref X64Emitter encoder, Register context, Register result, GenericLookupResult lookup, bool relocsOnly)
        {
            // INVARIANT: must not trash context register

            // Find the generic dictionary slot
            int dictionarySlot = 0;

            if (!relocsOnly)
            {
                // The concrete slot won't be known until we're emitting data - don't ask for it in relocsOnly.
                dictionarySlot = factory.GenericDictionaryLayout(_dictionaryOwner).GetSlotForEntry(lookup);
            }

            // Load the generic dictionary cell
            AddrMode loadEntry = new AddrMode(
                context, null, dictionarySlot * factory.Target.PointerSize, 0, AddrModeSize.Int64);

            encoder.EmitMOV(result, ref loadEntry);

            // If there's any invalid entries, we need to test for them
            //
            // Only do this in relocsOnly to make it easier to weed out bugs - the _hasInvalidEntries
            // flag can change over the course of compilation and the bad slot helper dependency
            // should be reported by someone else - the system should not rely on it coming from here.
            if (!relocsOnly && _hasInvalidEntries)
            {
                encoder.EmitCompareToZero(result);
                encoder.EmitJE(GetBadSlotHelper(factory));
            }

            switch (lookup.LookupResultReferenceType(factory))
            {
            case GenericLookupResultReferenceType.Indirect:
                // Do another indirection
                loadEntry = new AddrMode(result, null, 0, 0, AddrModeSize.Int64);
                encoder.EmitMOV(result, ref loadEntry);
                break;

            case GenericLookupResultReferenceType.ConditionalIndirect:
                // Test result, 0x1
                // JEQ L1
                // mov result, [result-1]
                // L1:
                throw new NotImplementedException();

            default:
                break;
            }
        }
        protected void EmitDictionaryLookup(NodeFactory factory, ref X64Emitter encoder, Register context, Register result, GenericLookupResult lookup, bool relocsOnly)
        {
            // INVARIANT: must not trash context register

            // Find the generic dictionary slot
            int dictionarySlot = 0;

            if (!relocsOnly)
            {
                // The concrete slot won't be known until we're emitting data - don't ask for it in relocsOnly.
                dictionarySlot = factory.GenericDictionaryLayout(_dictionaryOwner).GetSlotForEntry(lookup);
            }

            // Load the generic dictionary cell
            AddrMode loadEntry = new AddrMode(
                context, null, dictionarySlot * factory.Target.PointerSize, 0, AddrModeSize.Int64);

            encoder.EmitMOV(result, ref loadEntry);
        }
Exemple #5
0
        public byte LSR()
        {
            Fetch();
            SetFlag(FLAGS6502.C, fetched & 0x0001);
            temp = (ushort)(fetched >> 1);
            SetFlag(FLAGS6502.Z, (temp & 0x00FF) == 0x0000);
            SetFlag(FLAGS6502.N, temp & 0x0080);

            AddrMode addr = new AddrMode(IMP);

            if (instructions[opcode].addrMode.Equals(addr))
            {
                a = (byte)(temp & 0x00FF);
            }
            else
            {
                Write(addr_abs, (byte)(temp & 0x00FF));
            }
            return(0);
        }
        protected override void EmitLoadGenericContext(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly)
        {
            // We start with Arg0 pointing to the EEType

            // Locate the VTable slot that points to the dictionary
            int vtableSlot = 0;

            if (!relocsOnly)
            {
                // The concrete slot won't be known until we're emitting data - don't ask for it in relocsOnly.
                vtableSlot = VirtualMethodSlotHelper.GetGenericDictionarySlot(factory, (TypeDesc)_dictionaryOwner);
            }

            int pointerSize = factory.Target.PointerSize;
            int slotOffset  = EETypeNode.GetVTableOffset(pointerSize) + (vtableSlot * pointerSize);

            // Load the dictionary pointer from the VTable
            AddrMode loadDictionary = new AddrMode(encoder.TargetRegister.Arg0, null, slotOffset, 0, AddrModeSize.Int64);

            encoder.EmitMOV(encoder.TargetRegister.Arg0, ref loadDictionary);
        }
        protected void EmitDictionaryLookup(NodeFactory factory, ref X64Emitter encoder, Register context, Register result, GenericLookupResult lookup, bool relocsOnly)
        {
            // INVARIANT: must not trash context register

            // Find the generic dictionary slot
            int dictionarySlot = 0;

            if (!relocsOnly)
            {
                // The concrete slot won't be known until we're emitting data - don't ask for it in relocsOnly.
                dictionarySlot = factory.GenericDictionaryLayout(_dictionaryOwner).GetSlotForEntry(lookup);
            }

            // Load the generic dictionary cell
            AddrMode loadEntry = new AddrMode(
                context, null, dictionarySlot * factory.Target.PointerSize, 0, AddrModeSize.Int64);

            encoder.EmitMOV(result, ref loadEntry);

            switch (lookup.LookupResultReferenceType(factory))
            {
            case GenericLookupResultReferenceType.Indirect:
                // Do another indirection
                loadEntry = new AddrMode(result, null, 0, 0, AddrModeSize.Int64);
                encoder.EmitMOV(result, ref loadEntry);
                break;

            case GenericLookupResultReferenceType.ConditionalIndirect:
                // Test result, 0x1
                // JEQ L1
                // mov result, [result-1]
                // L1:
                throw new NotImplementedException();

            default:
                break;
            }
        }
        protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly)
        {
            switch (Id)
            {
            case ReadyToRunHelperId.NewHelper:
            {
                TypeDesc target = (TypeDesc)Target;
                encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol(target));
                encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewObjectHelperForType(target)));
            }
            break;

            case ReadyToRunHelperId.VirtualCall:
            {
                MethodDesc targetMethod = (MethodDesc)Target;

                if (targetMethod.OwningType.IsInterface)
                {
                    encoder.EmitLEAQ(Register.R10, factory.InterfaceDispatchCell((MethodDesc)Target));
                    AddrMode jmpAddrMode = new AddrMode(Register.R10, null, 0, 0, AddrModeSize.Int64);
                    encoder.EmitJmpToAddrMode(ref jmpAddrMode);
                }
                else
                {
                    if (relocsOnly)
                    {
                        break;
                    }

                    AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64);
                    encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr);

                    int pointerSize = factory.Target.PointerSize;

                    int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod);
                    Debug.Assert(slot != -1);
                    AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize), 0, AddrModeSize.Int64);
                    encoder.EmitJmpToAddrMode(ref jmpAddrMode);
                }
            }
            break;

            case ReadyToRunHelperId.IsInstanceOf:
            {
                TypeDesc target = (TypeDesc)Target;
                encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target));
                encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, false)));
            }
            break;

            case ReadyToRunHelperId.CastClass:
            {
                TypeDesc target = (TypeDesc)Target;
                encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target));
                encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, true)));
            }
            break;

            case ReadyToRunHelperId.NewArr1:
            {
                TypeDesc target = (TypeDesc)Target;

                // TODO: Swap argument order instead
                encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg0);
                encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol(target));
                encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewArrayHelperForType(target)));
            }
            break;

            case ReadyToRunHelperId.GetNonGCStaticBase:
            {
                MetadataType target = (MetadataType)Target;
                bool         hasLazyStaticConstructor = factory.TypeSystemContext.HasLazyStaticConstructor(target);
                encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target), hasLazyStaticConstructor ? NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target) : 0);

                if (!hasLazyStaticConstructor)
                {
                    encoder.EmitRET();
                }
                else
                {
                    // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region.
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target));

                    AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32);
                    encoder.EmitCMP(ref initialized, 1);
                    encoder.EmitRETIfEqual();

                    encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result);
                    encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase));
                }
            }
            break;

            case ReadyToRunHelperId.GetThreadStaticBase:
                encoder.EmitINT3();
                break;

            case ReadyToRunHelperId.GetGCStaticBase:
            {
                MetadataType target = (MetadataType)Target;

                encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol(target));
                AddrMode loadFromRax = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64);
                encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax);
                encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax);

                if (!factory.TypeSystemContext.HasLazyStaticConstructor(target))
                {
                    encoder.EmitRET();
                }
                else
                {
                    // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region.
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target));

                    AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32);
                    encoder.EmitCMP(ref initialized, 1);
                    encoder.EmitRETIfEqual();

                    encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result);

                    encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase));
                }
            }
            break;

            case ReadyToRunHelperId.DelegateCtor:
            {
                DelegateCreationInfo target = (DelegateCreationInfo)Target;

                encoder.EmitLEAQ(encoder.TargetRegister.Arg2, target.Target);

                if (target.Thunk != null)
                {
                    Debug.Assert(target.Constructor.Method.Signature.Length == 3);
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg3, target.Thunk);
                }
                else
                {
                    Debug.Assert(target.Constructor.Method.Signature.Length == 2);
                }

                encoder.EmitJMP(target.Constructor);
            }
            break;

            case ReadyToRunHelperId.ResolveVirtualFunction:
            {
                MethodDesc targetMethod = (MethodDesc)Target;
                if (targetMethod.OwningType.IsInterface)
                {
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod));
                    encoder.EmitJMP(factory.ExternSymbol("RhpResolveInterfaceMethod"));
                }
                else
                {
                    if (relocsOnly)
                    {
                        break;
                    }

                    AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64);
                    encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr);

                    int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod);
                    Debug.Assert(slot != -1);
                    AddrMode loadFromSlot = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64);
                    encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromSlot);
                    encoder.EmitRET();
                }
            }
            break;

            case ReadyToRunHelperId.GenericLookupFromThis:
            {
                int pointerSize = factory.Target.PointerSize;

                var lookupInfo = (GenericLookupDescriptor)Target;

                // Arg0 points to the EEType

                // Locate the VTable slot that points to the dictionary
                int vtableSlot = 0;
                if (!relocsOnly)
                {
                    vtableSlot = VirtualMethodSlotHelper.GetGenericDictionarySlot(factory, (TypeDesc)lookupInfo.CanonicalOwner);
                }

                int slotOffset = EETypeNode.GetVTableOffset(pointerSize) + (vtableSlot * pointerSize);

                // Load the dictionary pointer from the VTable
                AddrMode loadDictionary = new AddrMode(encoder.TargetRegister.Arg0, null, slotOffset, 0, AddrModeSize.Int64);
                encoder.EmitMOV(encoder.TargetRegister.Arg0, ref loadDictionary);

                // What's left now is the actual dictionary lookup
                goto case ReadyToRunHelperId.GenericLookupFromDictionary;
            }

            case ReadyToRunHelperId.GenericLookupFromDictionary:
            {
                var lookupInfo = (GenericLookupDescriptor)Target;

                // Find the generic dictionary slot
                int dictionarySlot = 0;
                if (!relocsOnly)
                {
                    // The concrete slot won't be known until we're emitting data.
                    dictionarySlot = factory.GenericDictionaryLayout(lookupInfo.CanonicalOwner).GetSlotForEntry(lookupInfo.Signature);
                }

                // Load the generic dictionary cell
                AddrMode loadEntry = new AddrMode(
                    encoder.TargetRegister.Arg0, null, dictionarySlot * factory.Target.PointerSize, 0, AddrModeSize.Int64);
                encoder.EmitMOV(encoder.TargetRegister.Result, ref loadEntry);
                encoder.EmitRET();
            }
            break;

            default:
                throw new NotImplementedException();
            }
        }
        protected override void EmitCode(NodeFactory factory, ref X86Emitter encoder, bool relocsOnly)
        {
            switch (Id)
            {
            case ReadyToRunHelperId.VirtualCall:
            {
                encoder.EmitINT3();
            }
            break;

            case ReadyToRunHelperId.GetNonGCStaticBase:
            {
                MetadataType target = (MetadataType)Target;
                bool         hasLazyStaticConstructor = factory.TypeSystemContext.HasLazyStaticConstructor(target);
                encoder.EmitMOV(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target));

                if (!hasLazyStaticConstructor)
                {
                    encoder.EmitRET();
                }
                else
                {
                    // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region.
                    encoder.EmitMOV(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target), -NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target));

                    AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32);
                    encoder.EmitCMP(ref initialized, 1);
                    encoder.EmitRETIfEqual();

                    encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result);
                    encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase));
                }
            }
            break;

            case ReadyToRunHelperId.GetThreadStaticBase:
            {
                encoder.EmitINT3();
            }
            break;

            case ReadyToRunHelperId.GetGCStaticBase:
            {
                encoder.EmitINT3();
            }
            break;

            case ReadyToRunHelperId.DelegateCtor:
            {
                encoder.EmitINT3();
            }
            break;

            case ReadyToRunHelperId.ResolveVirtualFunction:
            {
                encoder.EmitINT3();
            }
            break;

            default:
                throw new NotImplementedException();
            }
        }
Exemple #10
0
        public void Execute_And_Verify(
            AddrMode addrMode
            , bool ZP_X_Should_Wrap_Over_Byte             = false
            , bool ZP_Y_Should_Wrap_Over_Byte             = false
            , bool FullAddress_Should_Cross_Page_Boundary = false
            )
        {
            if (!InsEffect.HasValue)
            {
                InsEffect = InstrEffect.Reg;
            }

            if (addrMode == AddrMode.Accumulator && FinalValue.HasValue)
            {
                throw new DotNet6502Exception($"If {nameof(AddrMode)} is {nameof(AddrMode.Accumulator)}, {nameof(FinalValue)} cannot be used.");
            }

            if (addrMode == AddrMode.Implied && FinalValue.HasValue)
            {
                throw new DotNet6502Exception($"If {nameof(addrMode)} is {nameof(AddrMode.Implied)}, {nameof(FinalValue)} cannot be used.");
            }

            if (addrMode != AddrMode.Indirect && FinalValueWord.HasValue)
            {
                throw new DotNet6502Exception($"If {nameof(AddrMode)} is other than {nameof(AddrMode.Indirect)}, {nameof(FinalValueWord)} cannot be used.");
            }

            if (InsEffect == InstrEffect.Reg)
            {
                if (ExpectedMemVal.HasValue)
                {
                    throw new DotNet6502Exception($"If {nameof(InsEffect)} is {nameof(InstrEffect.Reg)}, {nameof(ExpectedMemVal)} is not supposed to be set (only used for comparing memory address changed by write)");
                }
                if (addrMode == AddrMode.Accumulator && (ExpectedX.HasValue || ExpectedY.HasValue))
                {
                    throw new DotNet6502Exception($"If {nameof(addrMode)} is {nameof(AddrMode.Accumulator)}, {nameof(ExpectedX)} or {nameof(ExpectedY)} cannot be used, because addressing mode {nameof(AddrMode.Accumulator)} only can affect A register.");
                }
                if (ExpectedA.HasValue && !A.HasValue)
                {
                    throw new DotNet6502Exception($"If {nameof(ExpectedA)} is set, {nameof(A)} must be set to an initial value");
                }
                if (ExpectedX.HasValue && !X.HasValue)
                {
                    throw new DotNet6502Exception($"If {nameof(ExpectedX)} is set, {nameof(X)} must be set to an initial value");
                }
                if (ExpectedY.HasValue && !Y.HasValue)
                {
                    throw new DotNet6502Exception($"If {nameof(ExpectedY)} is set, {nameof(Y)} must be set to an initial value");
                }
            }
            else if (InsEffect == InstrEffect.Mem)
            {
                if (addrMode == AddrMode.Implied)
                {
                    throw new DotNet6502Exception($"If {nameof(InsEffect)} is {nameof(InstrEffect.Mem)}, {nameof(addrMode)} {nameof(AddrMode.Implied)} cannot be used.");
                }

                if (addrMode == AddrMode.Accumulator)
                {
                    throw new DotNet6502Exception($"If {nameof(InsEffect)} is {nameof(InstrEffect.Mem)}, {nameof(addrMode)} {nameof(AddrMode.Accumulator)} cannot be used.");
                }

                if (ExpectedMemVal.HasValue && addrMode == AddrMode.I)
                {
                    throw new DotNet6502Exception($"{nameof(ExpectedMemVal)} cannot be used with addressing mode {nameof(AddrMode.I)}");
                }

                if (ExpectedMemVal.HasValue && addrMode == AddrMode.Accumulator)
                {
                    throw new DotNet6502Exception($"{nameof(ExpectedMemVal)} cannot be used with addressing mode {nameof(AddrMode.Accumulator)}");
                }

                if (ExpectedMemVal.HasValue & !FinalValue.HasValue)
                {
                    throw new DotNet6502Exception($"If {nameof(ExpectedMemVal)} is set, {nameof(FinalValue)} must also be set to an initial value that the memory will have before the instruction executes");
                }

                if (ExpectedA.HasValue)
                {
                    throw new DotNet6502Exception($"If {nameof(InsEffect)} is {nameof(InstrEffect.Mem)}, {nameof(ExpectedA)} is not supposed to be set (only used for comparing register change after instruction executed)");
                }
                if (ExpectedX.HasValue)
                {
                    throw new DotNet6502Exception($"If {nameof(InsEffect)} is {nameof(InstrEffect.Mem)}, {nameof(ExpectedX)} is not supposed to be set (only used for comparing register change after instruction executed)");
                }
                if (ExpectedY.HasValue)
                {
                    throw new DotNet6502Exception($"If {nameof(InsEffect)} is {nameof(InstrEffect.Mem)}, {nameof(ExpectedY)} is not supposed to be set (only used for comparing register change after instruction executed)");
                }
            }
            else if (InsEffect == InstrEffect.RegAndMem)
            {
                if (addrMode == AddrMode.Implied)
                {
                    throw new DotNet6502Exception($"If {nameof(InsEffect)} is {nameof(InstrEffect.RegAndMem)}, {nameof(addrMode)} {nameof(AddrMode.Implied)} cannot be used.");
                }
            }

            // If processor flags aren't configured to be initialized by test, we automatically set them to the opposite of what was defined as the expected result
            if (ExpectedC.HasValue && !C.HasValue)
            {
                C = !ExpectedC.Value;
            }
            if (ExpectedZ.HasValue && !Z.HasValue)
            {
                Z = !ExpectedZ.Value;
            }
            if (ExpectedI.HasValue && !I.HasValue)
            {
                I = !ExpectedI.Value;
            }
            if (ExpectedD.HasValue && !D.HasValue)
            {
                D = !ExpectedZ.Value;
            }
            if (ExpectedB.HasValue && !B.HasValue)
            {
                B = !ExpectedB.Value;
            }
            if (ExpectedU.HasValue && !U.HasValue)
            {
                U = !ExpectedU.Value;
            }
            if (ExpectedV.HasValue && !V.HasValue)
            {
                V = !ExpectedV.Value;
            }
            if (ExpectedN.HasValue && !N.HasValue)
            {
                N = !ExpectedN.Value;
            }

            // Shorthand variables to cpu and memory
            var computer = TestContext.Computer;
            var cpu      = computer.CPU;
            var mem      = computer.Mem;

            // Init Program Counter
            if (PC.HasValue)
            {
                cpu.PC = PC.Value;
            }

            // We will start writing the instruction, and then operand, starting at the specified PC address
            var codeMemPos = cpu.PC;

            // Write instruction at start address
            mem.WriteByte(ref codeMemPos, OpCode);

            ushort ZPAddressX;
            ushort ZPAddressY;
            ushort fullAddressX;
            ushort fullAddressY;
            ushort?finalAddressUsed = null;

            switch (addrMode)
            {
            case AddrMode.Implied:
                // Implied instructions does not have operand. The complete instruction takes 1 byte.
                break;

            case AddrMode.Accumulator:
                // Accumulator addressing mode means operate on A register (instead of memory). No instruction operand is used.
                break;

            case AddrMode.Indirect:
                // JMP is the only instruction that uses Indirect addressing.
                // The instruction contains a 16 bit address which identifies the location of the least significant byte
                // of another 16 bit memory address which is the real target of the instruction.

                // Define memory address the instruction should use
                if (!FullAddress.HasValue)
                {
                    FullAddress = 0xab12;
                }

                finalAddressUsed = FullAddress.Value;

                // Initialize memory the instruction should read or write to
                if (!FinalValueWord.HasValue)
                {
                    FinalValueWord = 0x1265;     // If not specified, initialize default value to be at final memory location
                }
                mem.WriteWord(finalAddressUsed.Value, FinalValueWord.Value);

                // Write instruction operand
                mem.WriteWord(ref codeMemPos, FullAddress.Value);

                break;

            case AddrMode.Relative:
                // Relative addressing mode is used by branching instruction such as BEQ and BNE.
                if (!FinalValue.HasValue)
                {
                    FinalValue = 0xd0;     // If not specified, initialize default value to be at final relative memory location to branch to
                }
                mem.WriteByte(ref codeMemPos, FinalValue.Value);
                break;

            case AddrMode.I:
                // Initialize memory the instruction should read or write to
                if (!FinalValue.HasValue)
                {
                    FinalValue = 0x12;     // If not specified, initialize default value to be at final memory location
                }
                mem.WriteByte(ref codeMemPos, FinalValue.Value);
                break;

            case AddrMode.ZP:
                // To avoid testing mistakes, setting up a test as ZP (parameter AddrMode), when the instruction being run (property Instruction) is ZP_X or ZP_Y,
                // we initialize registers that should not be used with ZP to non-default values.
                if (!X.HasValue)
                {
                    X = 111;
                }
                if (!Y.HasValue)
                {
                    Y = 15;
                }

                // Define memory address the instruction should calculate
                if (!ZeroPageAddress.HasValue)
                {
                    ZeroPageAddress = 0x0010;
                }

                finalAddressUsed = ZeroPageAddress.Value;

                if (!FinalValue.HasValue)
                {
                    FinalValue = 0x12;     // If not specified, initialize default value to be at final memory location
                }
                // Initialize memory the instruction should read or write to
                mem.WriteByte(finalAddressUsed.Value, FinalValue.Value);

                // Write instruction operand
                mem.WriteByte(ref codeMemPos, ZeroPageAddress.Value.Lowbyte());     // Only least significant byte of the address is used in the instruction.
                break;

            case AddrMode.ZP_X:
                // Define memory address the instruction should calculate
                if (!ZeroPageAddress.HasValue)
                {
                    ZeroPageAddress = 0x0010;
                }
                // Use a default value for X index if not specified in test
                if (!X.HasValue)
                {
                    if (!ZP_X_Should_Wrap_Over_Byte)
                    {
                        X = 0x05;
                    }
                    else
                    {
                        X = 0xf5;       // Force final ZP+X address bigger than one byte (0x0010 + 0xf5 = 0x0105)
                    }
                }
                // Calculate ZeroPage + X address
                ZPAddressX = (ushort)(ZeroPageAddress + X);
                // Adjust that we expect the final address to wrap when getting larger than a byte
                if (ZP_X_Should_Wrap_Over_Byte)
                {
                    ZPAddressX = (ushort)(ZPAddressX & 0xff);
                }

                finalAddressUsed = ZPAddressX;

                // Initialize memory the instruction should read or write to
                if (!FinalValue.HasValue)
                {
                    FinalValue = 0x12;     // If not specified, initialize default value to be at final memory location
                }
                mem.WriteByte(finalAddressUsed.Value, FinalValue.Value);

                // Write instruction operand
                mem.WriteByte(ref codeMemPos, ZeroPageAddress.Value.Lowbyte());     // Only least significant byte of the address is used in the instruction.
                break;

            case AddrMode.ZP_Y:
                // Define memory address the instruction should calculate
                if (!ZeroPageAddress.HasValue)
                {
                    ZeroPageAddress = 0x0010;
                }
                // Use a default value for Y index if not specified in test
                if (!Y.HasValue)
                {
                    if (!ZP_Y_Should_Wrap_Over_Byte)
                    {
                        Y = 0x05;
                    }
                    else
                    {
                        Y = 0xf5;       // Force final ZP+Y address bigger than one byte (0x0010 + 0xf5 = 0x0105)
                    }
                }
                // Calculate ZeroPage + Y address
                ZPAddressY = (ushort)(ZeroPageAddress + Y);
                // Adjust that we expect the final address to wrap when getting larger than a byte
                if (ZP_Y_Should_Wrap_Over_Byte)
                {
                    ZPAddressY = (ushort)(ZPAddressY & 0xff);
                }

                finalAddressUsed = ZPAddressY;

                // Initialize memory the instruction should read or write to
                if (!FinalValue.HasValue)
                {
                    FinalValue = 0x12;     // If not specified, initialize default value to be at final memory location
                }
                mem.WriteByte(finalAddressUsed.Value, FinalValue.Value);

                // Write instruction operand
                mem.WriteByte(ref codeMemPos, ZeroPageAddress.Value.Lowbyte());     // Only least significant byte of the address is used in the instruction.
                break;

            case AddrMode.ABS:
                // To avoid testing mistakes, setting up a test as ABS (parameter AddrMode), when the instruction being run (property Instruction) is ABX_X or ABS_Y,
                // we initialize registers that should not be used with Absolute addressing to non-default values.
                if (!X.HasValue)
                {
                    X = 60;
                }
                if (!Y.HasValue)
                {
                    Y = 100;
                }

                // Define memory address the instruction should use
                if (!FullAddress.HasValue)
                {
                    FullAddress = 0xab12;
                }

                finalAddressUsed = FullAddress.Value;

                // Initialize memory the instruction should read or write to
                if (!FinalValue.HasValue)
                {
                    FinalValue = 0x12;     // If not specified, initialize default value to be at final memory location
                }
                mem.WriteByte(finalAddressUsed.Value, FinalValue.Value);

                // Write instruction operand
                mem.WriteWord(ref codeMemPos, FullAddress.Value);
                break;

            case AddrMode.ABS_X:

                // Define memory address the instruction should use
                if (!FullAddress.HasValue)
                {
                    FullAddress = 0xab12;
                }
                // Use a default value for X index if not specified in test
                if (!X.HasValue)
                {
                    if (!FullAddress_Should_Cross_Page_Boundary)
                    {
                        X = 0x05;
                    }
                    else
                    {
                        X = 0xf0;       // Force final FullAddress+X address crosses page boundary (0xab12 + 0xf0 = 0xac02)
                    }
                }
                // Calculate final address with X offset
                fullAddressX = (ushort)(FullAddress + X);

                finalAddressUsed = fullAddressX;

                // Initialize memory the instruction should read or write to
                if (!FinalValue.HasValue)
                {
                    FinalValue = 0x12;     // If not specified, initialize default value to be at final memory location
                }
                mem.WriteByte(finalAddressUsed.Value, FinalValue.Value);

                // Write instruction operand
                mem.WriteWord(ref codeMemPos, FullAddress.Value);

                break;

            case AddrMode.ABS_Y:

                // Define memory address the instruction should use
                if (!FullAddress.HasValue)
                {
                    FullAddress = 0xab12;
                }
                // Use a default value for X index if not specified in test
                if (!Y.HasValue)
                {
                    if (!FullAddress_Should_Cross_Page_Boundary)
                    {
                        Y = 0x05;
                    }
                    else
                    {
                        Y = 0xf0;       // Force final FullAddress+X address crosses page boundary (0xab12 + 0xf0 = 0xac02)
                    }
                }
                // Calculate final address with X offset
                fullAddressY = (ushort)(FullAddress + Y);

                finalAddressUsed = fullAddressY;

                // Initialize memory the instruction should read or write to
                if (!FinalValue.HasValue)
                {
                    FinalValue = 0x12;     // If not specified, initialize default value to be at final memory location
                }
                mem.WriteByte(finalAddressUsed.Value, FinalValue.Value);

                // Write instruction operand
                mem.WriteWord(ref codeMemPos, FullAddress.Value);

                break;

            case AddrMode.IX_IND:
                // Define memory address the instruction should use
                if (!FullAddress.HasValue)
                {
                    FullAddress = 0xab12;
                }
                // Use a default value for X index if not specified in test
                if (!X.HasValue)
                {
                    if (!ZP_X_Should_Wrap_Over_Byte)
                    {
                        X = 0x05;
                    }
                    else
                    {
                        X = 0xf0;       // Force final ZeroPage address +X to wrap around a byte (0x20 + 0xf0 = 0x10)
                    }
                }

                //Calculate locations used to calculate the actual address to read from (fullAddress)
                // index_indirect_base_address = zero page address
                if (!ZeroPageAddress.HasValue)
                {
                    ZeroPageAddress = 0x20;
                }
                // We should allways wrap around indirect ZeroPage address + X if exceeds one byte. Can be be done by truncating address to byte.
                ushort index_indirect_zeropage_address = (byte)(ZeroPageAddress + X);

                // Initialize zero page address + X the instruction will read final address from
                mem.WriteWord(index_indirect_zeropage_address, FullAddress.Value);

                finalAddressUsed = FullAddress.Value;

                // Initialize final memory the instruction should read or write to
                if (!FinalValue.HasValue)
                {
                    FinalValue = 0x12;     // If not specified, initialize default value to be at final memory location
                }
                mem.WriteByte(finalAddressUsed.Value, FinalValue.Value);

                // Write instruction operand
                mem.WriteByte(ref codeMemPos, (byte)ZeroPageAddress.Value);
                break;

            case AddrMode.IND_IX:
                // Initialize memory we will read from
                // We calculate a full address to store in a zero page address, adjusted by -Y (because the instruction will add Y when it retrieves it)
                // If we want to force crossing page boundary FullAddress + Y, we will hard code FullAddress and Y values
                if (FullAddress_Should_Cross_Page_Boundary)
                {
                    // The address to be found in zero page address
                    FullAddress = 0xab12;
                    // Adding Y to the address found in zero page will cross page boundary
                    Y = 0xff;
                }
                // Use a default value for FullAddress if not specified in test
                if (!FullAddress.HasValue)
                {
                    FullAddress = 0xab12;
                }
                // Use a default value for Y index if not specified in test
                if (!Y.HasValue)
                {
                    Y = 0x05;
                }

                //Calculate locations used to calculate the actual address to read from (fullAddress)
                // We use ZeroPage address as the "indirect indexed" zeropage address
                if (!ZeroPageAddress.HasValue)
                {
                    ZeroPageAddress = 0x86;
                }
                // The address to be found in the zero page address should be the final address - the Y register.
                ushort address_at_zeropage_address = (ushort)(FullAddress - Y);

                // Initialize indirect indexed zero page address
                mem.WriteWord(ZeroPageAddress.Value, address_at_zeropage_address);

                finalAddressUsed = FullAddress.Value;

                // Initialize final memory the instruction should read or write to
                if (!FinalValue.HasValue)
                {
                    FinalValue = 0x12;     // If not specified, initialize default value to be at final memory location
                }
                mem.WriteByte(finalAddressUsed.Value, FinalValue.Value);

                // Write instruction operand
                mem.WriteByte(ref codeMemPos, (byte)ZeroPageAddress.Value);
                break;

            default:
                throw new DotNet6502Exception($"Unhandled addressing mode: {addrMode}");
            }

            // Before we execute intruction, verify internal consistency of number of bytes the OpCode takes by our test-setup code above
            // with the value specified in OpCode.Bytes class.
            Assert.Equal(cpu.PC + cpu.InstructionList.GetOpCode(OpCode.ToByte()).Size, codeMemPos);

            // Init registers and flags
            if (A.HasValue)
            {
                cpu.A = A.Value;
            }
            if (X.HasValue)
            {
                cpu.X = X.Value;
            }
            if (Y.HasValue)
            {
                cpu.Y = Y.Value;
            }
            if (SP.HasValue)
            {
                cpu.SP = SP.Value;
            }
            if (C.HasValue)
            {
                cpu.ProcessorStatus.Carry = C.Value;
            }
            if (Z.HasValue)
            {
                cpu.ProcessorStatus.Zero = Z.Value;
            }
            if (I.HasValue)
            {
                cpu.ProcessorStatus.InterruptDisable = I.Value;
            }
            if (D.HasValue)
            {
                cpu.ProcessorStatus.Decimal = D.Value;
            }
            if (B.HasValue)
            {
                cpu.ProcessorStatus.Break = B.Value;
            }
            if (U.HasValue)
            {
                cpu.ProcessorStatus.Unused = U.Value;
            }
            if (V.HasValue)
            {
                cpu.ProcessorStatus.Overflow = V.Value;
            }
            if (N.HasValue)
            {
                cpu.ProcessorStatus.Negative = N.Value;
            }

            // Act
            var execOptions = new ExecOptions();

            if (ExpectedCycles.HasValue)
            {
                execOptions.CyclesRequested         = ExpectedCycles.Value;
                execOptions.MaxNumberOfInstructions = 1;
            }
            else
            {
                execOptions.CyclesRequested         = null;
                execOptions.MaxNumberOfInstructions = 1;
            }
            var thisExecState = cpu.Execute(mem, execOptions);

            // Assert
            // Check that we didn't find any unknown opcode
            Assert.True(thisExecState.UnknownOpCodeCount == 0);

            // Verify Program Counter
            if (ExpectedPC.HasValue)
            {
                Assert.Equal(ExpectedPC.Value, cpu.PC);
            }
            else
            {
                // If no PC check has been defined, by default verify PC has been moved forward as many bytes we used up for the instruction.
                // But skip default verification if be used a branching or jump instruction
                //      -- All that uses Relative addressing mode
                //      -- Some other instructions in Implied addressing mode
                //      -- JMP instructions
                //      -- JSR instruction
                //
                if (addrMode != AddrMode.Relative &&
                    OpCode != OpCodeId.BRK &&     // Implied addressing mode
                    OpCode != OpCodeId.RTS &&     // Implied addressing mode
                    OpCode != OpCodeId.RTI &&     // Implied addressing mode
                    OpCode != OpCodeId.JMP_IND && // Indirect addressing mode
                    OpCode != OpCodeId.JMP_ABS && // Absolute addressing mode
                    OpCode != OpCodeId.JSR        // Absolute addressing mode
                    )
                {
                    Assert.Equal(codeMemPos, cpu.PC);
                }
            }

            // Verify expected # of cycles
            if (ExpectedCycles.HasValue)
            {
                Assert.Equal(ExpectedCycles, thisExecState.CyclesConsumed);
            }

            // Verify registers (operations that affects registers)
            if (InsEffect == InstrEffect.Reg || InsEffect == InstrEffect.RegAndMem)
            {
                if (ExpectedA.HasValue)
                {
                    Assert.Equal(ExpectedA.Value, cpu.A);
                }
                if (ExpectedX.HasValue)
                {
                    Assert.Equal(ExpectedX.Value, cpu.X);
                }
                if (ExpectedY.HasValue)
                {
                    Assert.Equal(ExpectedY.Value, cpu.Y);
                }
            }
            // Verify affected memory (operations that affect memory)
            else if (InsEffect == InstrEffect.Mem || InsEffect == InstrEffect.RegAndMem)
            {
                if (!finalAddressUsed.HasValue)
                {
                    throw new DotNet6502Exception($"Incorrect use of TestSpec class, or bug in TestSpec class. Is correct addressing mode being tested? Variable {nameof(finalAddressUsed)} must be initialized in all addressing mode code paths that can modify memory");
                }
                if (ExpectedMemVal.HasValue)
                {
                    Assert.Equal(ExpectedMemVal.Value, mem[finalAddressUsed.Value]);
                }
            }

            // Verify stack pointer
            if (ExpectedSP.HasValue)
            {
                Assert.Equal(ExpectedSP.Value, cpu.SP);
            }

            // Verify status flags
            if (ExpectedC.HasValue)
            {
                Assert.Equal(ExpectedC.Value, cpu.ProcessorStatus.Carry);
            }
            if (ExpectedZ.HasValue)
            {
                Assert.Equal(ExpectedZ.Value, cpu.ProcessorStatus.Zero);
            }
            if (ExpectedI.HasValue)
            {
                Assert.Equal(ExpectedI.Value, cpu.ProcessorStatus.InterruptDisable);
            }
            if (ExpectedD.HasValue)
            {
                Assert.Equal(ExpectedD.Value, cpu.ProcessorStatus.Decimal);
            }
            if (ExpectedB.HasValue)
            {
                Assert.Equal(ExpectedB.Value, cpu.ProcessorStatus.Break);
            }
            if (ExpectedV.HasValue)
            {
                Assert.Equal(ExpectedV.Value, cpu.ProcessorStatus.Overflow);
            }
            if (ExpectedN.HasValue)
            {
                Assert.Equal(ExpectedN.Value, cpu.ProcessorStatus.Negative);
            }
        }
Exemple #11
0
 public MemImmediate(RegSP Base = default, AddrMode Mode = default, int imm = default)
 {
     this.Base = Base;
     this.Mode = Mode;
     this.imm  = imm;
 }
Exemple #12
0
 public bool SupportsAddressingMode(AddrMode mode)
 {
     return(OpCodes.Exists(x => x.AddressingMode == mode));
 }
        protected sealed override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly)
        {
            // First load the generic context into the context register.
            EmitLoadGenericContext(factory, ref encoder, relocsOnly);

            Register contextRegister = GetContextRegister(ref encoder);

            switch (_id)
            {
            case ReadyToRunHelperId.GetNonGCStaticBase:
            {
                Debug.Assert(contextRegister == encoder.TargetRegister.Arg0);

                MetadataType target = (MetadataType)_target;

                if (!factory.TypeSystemContext.HasLazyStaticConstructor(target))
                {
                    EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly);
                    encoder.EmitRET();
                }
                else
                {
                    EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg0, _lookupSignature, relocsOnly);
                    encoder.EmitMOV(encoder.TargetRegister.Result, encoder.TargetRegister.Arg0);

                    // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region.
                    int      cctorContextSize = NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target);
                    AddrMode initialized      = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize - cctorContextSize, 0, AddrModeSize.Int32);
                    encoder.EmitCMP(ref initialized, 1);
                    encoder.EmitRETIfEqual();

                    AddrMode loadCctor = new AddrMode(encoder.TargetRegister.Arg0, null, -cctorContextSize, 0, AddrModeSize.Int64);
                    encoder.EmitLEA(encoder.TargetRegister.Arg0, ref loadCctor);
                    encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result);
                    encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase));
                }
            }
            break;

            case ReadyToRunHelperId.GetGCStaticBase:
            {
                Debug.Assert(contextRegister == encoder.TargetRegister.Arg0);

                MetadataType target = (MetadataType)_target;

                EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly);

                AddrMode loadFromResult = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64);
                encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromResult);
                encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromResult);

                if (!factory.TypeSystemContext.HasLazyStaticConstructor(target))
                {
                    encoder.EmitRET();
                }
                else
                {
                    // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region.
                    GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target);
                    EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg0, nonGcRegionLookup, relocsOnly);

                    int      cctorContextSize = NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target);
                    AddrMode initialized      = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize - cctorContextSize, 0, AddrModeSize.Int32);
                    encoder.EmitCMP(ref initialized, 1);
                    encoder.EmitRETIfEqual();

                    encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result);
                    AddrMode loadCctor = new AddrMode(encoder.TargetRegister.Arg0, null, -cctorContextSize, 0, AddrModeSize.Int64);
                    encoder.EmitLEA(encoder.TargetRegister.Arg0, ref loadCctor);

                    encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase));
                }
            }
            break;

            case ReadyToRunHelperId.GetThreadStaticBase:
            {
                Debug.Assert(contextRegister == encoder.TargetRegister.Arg0);

                MetadataType target = (MetadataType)_target;

                // Look up the index cell
                EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg1, _lookupSignature, relocsOnly);

                ISymbolNode helperEntrypoint;
                if (factory.TypeSystemContext.HasLazyStaticConstructor(target))
                {
                    // There is a lazy class constructor. We need the non-GC static base because that's where the
                    // class constructor context lives.
                    GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target);
                    EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2, nonGcRegionLookup, relocsOnly);
                    int      cctorContextSize = NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target);
                    AddrMode loadCctor        = new AddrMode(encoder.TargetRegister.Arg2, null, -cctorContextSize, 0, AddrModeSize.Int64);
                    encoder.EmitLEA(encoder.TargetRegister.Arg2, ref loadCctor);

                    helperEntrypoint = factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase);
                }
                else
                {
                    helperEntrypoint = factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType);
                }

                // First arg: address of the TypeManager slot that provides the helper with
                // information about module index and the type manager instance (which is used
                // for initialization on first access).
                AddrMode loadFromArg1 = new AddrMode(encoder.TargetRegister.Arg1, null, 0, 0, AddrModeSize.Int64);
                encoder.EmitMOV(encoder.TargetRegister.Arg0, ref loadFromArg1);

                // Second arg: index of the type in the ThreadStatic section of the modules
                AddrMode loadFromArg1AndDelta = new AddrMode(encoder.TargetRegister.Arg1, null, factory.Target.PointerSize, 0, AddrModeSize.Int64);
                encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromArg1AndDelta);

                encoder.EmitJMP(helperEntrypoint);
            }
            break;

            case ReadyToRunHelperId.DelegateCtor:
            {
                // This is a weird helper. Codegen populated Arg0 and Arg1 with the values that the constructor
                // method expects. Codegen also passed us the generic context in Arg2.
                // We now need to load the delegate target method into Arg2 (using a dictionary lookup)
                // and the optional 4th parameter, and call the ctor.

                Debug.Assert(contextRegister == encoder.TargetRegister.Arg2);

                var target = (DelegateCreationInfo)_target;

                EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, _lookupSignature, relocsOnly);

                if (target.Thunk != null)
                {
                    Debug.Assert(target.Constructor.Method.Signature.Length == 3);
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg3, target.Thunk);
                }
                else
                {
                    Debug.Assert(target.Constructor.Method.Signature.Length == 2);
                }

                encoder.EmitJMP(target.Constructor);
            }
            break;

            // These are all simple: just get the thing from the dictionary and we're done
            case ReadyToRunHelperId.TypeHandle:
            case ReadyToRunHelperId.MethodHandle:
            case ReadyToRunHelperId.FieldHandle:
            case ReadyToRunHelperId.MethodDictionary:
            case ReadyToRunHelperId.MethodEntry:
            case ReadyToRunHelperId.VirtualDispatchCell:
            case ReadyToRunHelperId.DefaultConstructor:
            case ReadyToRunHelperId.ObjectAllocator:
            case ReadyToRunHelperId.TypeHandleForCasting:
            {
                EmitDictionaryLookup(factory, ref encoder, contextRegister, encoder.TargetRegister.Result, _lookupSignature, relocsOnly);
                encoder.EmitRET();
            }
            break;

            default:
                throw new NotImplementedException();
            }
        }
Exemple #14
0
 private void Set(byte value, string instr, AddrMode addressMode, int cycles)
 {
     var op = new OpcodeInfo();
     op.Instruction = instr;
     op.AddressMode = addressMode;
     op.Cycles = cycles;
     if (Opcodes[value] != null)
         throw new Exception("opcode " + value + " already assigned");
     Opcodes[value] = op;
 }
Exemple #15
0
        public void OutputGen_Returns_Correctly_Formatted_Operand_String_For_AddrMode(AddrMode addrMode, byte[] operand, string expectedOutput)
        {
            // Act
            var outputString = OutputGen.BuildOperandString(addrMode, operand);

            // Assert
            Assert.Equal(expectedOutput, outputString);
        }
        protected sealed override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly)
        {
            // First load the generic context into Arg0.
            EmitLoadGenericContext(factory, ref encoder, relocsOnly);

            switch (_id)
            {
            case ReadyToRunHelperId.GetNonGCStaticBase:
            {
                MetadataType target = (MetadataType)_target;

                if (!factory.TypeSystemContext.HasLazyStaticConstructor(target))
                {
                    EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly);
                    encoder.EmitRET();
                }
                else
                {
                    EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg0, _lookupSignature, relocsOnly);

                    // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region.
                    int cctorContextSize = NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target);

                    AddrMode loadBase = new AddrMode(encoder.TargetRegister.Arg0, null, cctorContextSize, 0, AddrModeSize.Int64);
                    encoder.EmitLEA(encoder.TargetRegister.Result, ref loadBase);

                    AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32);
                    encoder.EmitCMP(ref initialized, 1);
                    encoder.EmitRETIfEqual();

                    encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result);
                    encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase));
                }
            }
            break;

            case ReadyToRunHelperId.GetGCStaticBase:
            {
                MetadataType target = (MetadataType)_target;

                EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly);

                AddrMode loadFromResult = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64);
                encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromResult);
                encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromResult);

                if (!factory.TypeSystemContext.HasLazyStaticConstructor(target))
                {
                    encoder.EmitRET();
                }
                else
                {
                    // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region.
                    GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target);
                    EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg0, nonGcRegionLookup, relocsOnly);

                    AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32);
                    encoder.EmitCMP(ref initialized, 1);
                    encoder.EmitRETIfEqual();

                    encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result);

                    encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase));
                }
            }
            break;

            // These are all simple: just get the thing from the dictionary and we're done
            case ReadyToRunHelperId.TypeHandle:
            case ReadyToRunHelperId.MethodDictionary:
            case ReadyToRunHelperId.VirtualCall:
            case ReadyToRunHelperId.ResolveVirtualFunction:
            case ReadyToRunHelperId.MethodEntry:
            {
                EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly);
                encoder.EmitRET();
            }
            break;

            default:
                throw new NotImplementedException();
            }
        }
Exemple #17
0
        public Dictionary <ushort, string> Disassemble(ushort nStart, ushort nStop)
        {
            Console.WriteLine("START");
            var    mapLines = new Dictionary <ushort, string>();
            ushort addr = nStart;
            ushort line_addr = 0;
            byte   value = 0x00, lo = 0x00, hi = 0x00;


            AddrMode _IMP = new AddrMode(IMP);
            AddrMode _IMM = new AddrMode(IMM);
            AddrMode _ZP0 = new AddrMode(ZP0);
            AddrMode _ZPX = new AddrMode(ZPX);
            AddrMode _ZPY = new AddrMode(ZPY);
            AddrMode _IZX = new AddrMode(IZX);
            AddrMode _IZY = new AddrMode(IZY);
            AddrMode _ABS = new AddrMode(ABS);
            AddrMode _ABX = new AddrMode(ABX);
            AddrMode _ABY = new AddrMode(ABY);
            AddrMode _IND = new AddrMode(IND);
            AddrMode _REL = new AddrMode(REL);


            while (addr < nStop - 1)
            {
                line_addr = addr;
                string sInst = "$" + Hex(addr, 4) + ": ";

                byte opcode = bus.cpuRead(addr, true);
                addr++;

                sInst += instructions[opcode].name + " ";

                if (instructions[opcode].addrMode.Equals(_IMP))
                {
                    sInst += " {IMP}";
                }
                else if (instructions[opcode].addrMode.Equals(_IMM))
                {
                    value  = bus.cpuRead(addr, true); addr++;
                    sInst += "#$" + Hex(value, 2) + " {IMM}";
                }
                else if (instructions[opcode].addrMode.Equals(_ZP0))
                {
                    lo     = bus.cpuRead(addr, true); addr++;
                    hi     = 0x00;
                    sInst += "$" + Hex(lo, 2) + " {ZP0}";
                }
                else if (instructions[opcode].addrMode.Equals(_ZPX))
                {
                    lo     = bus.cpuRead(addr, true); addr++;
                    hi     = 0x00;
                    sInst += "$" + Hex(lo, 2) + ", X {ZPX}";
                }
                else if (instructions[opcode].addrMode.Equals(_ZPY))
                {
                    lo     = bus.cpuRead(addr, true); addr++;
                    hi     = 0x00;
                    sInst += "$" + Hex(lo, 2) + ", Y {ZPY}";
                }
                else if (instructions[opcode].addrMode.Equals(_IZX))
                {
                    lo     = bus.cpuRead(addr, true); addr++;
                    hi     = 0x00;
                    sInst += "($" + Hex(lo, 2) + ", X) {IZX}";
                }
                else if (instructions[opcode].addrMode.Equals(_IZY))
                {
                    lo     = bus.cpuRead(addr, true); addr++;
                    hi     = 0x00;
                    sInst += "($" + Hex(lo, 2) + "), Y {IZY}";
                }
                else if (instructions[opcode].addrMode.Equals(_ABS))
                {
                    lo     = bus.cpuRead(addr, true); addr++;
                    hi     = bus.cpuRead(addr, true); addr++;
                    sInst += "$" + Hex((ushort)((hi << 8) | lo), 4) + " {ABS}";
                }
                else if (instructions[opcode].addrMode.Equals(_ABX))
                {
                    lo     = bus.cpuRead(addr, true); addr++;
                    hi     = bus.cpuRead(addr, true); addr++;
                    sInst += "$" + Hex((ushort)((hi << 8) | lo), 4) + ", X {ABX}";
                }
                else if (instructions[opcode].addrMode.Equals(_ABY))
                {
                    lo     = bus.cpuRead(addr, true); addr++;
                    hi     = bus.cpuRead(addr, true); addr++;
                    sInst += "$" + Hex((ushort)((hi << 8) | lo), 4) + ", Y {ABY}";
                }
                else if (instructions[opcode].addrMode.Equals(_IND))
                {
                    lo     = bus.cpuRead(addr, true); addr++;
                    hi     = bus.cpuRead(addr, true); addr++;
                    sInst += "($" + Hex((ushort)((hi << 8) | lo), 4) + ") {IND}";
                }
                else if (instructions[opcode].addrMode.Equals(_REL))
                {
                    value  = bus.cpuRead(addr, true); addr++;
                    sInst += "$" + Hex(value, 2) + " [$" + Hex((ushort)(addr + value), 4) + "] {REL}";
                }


                mapLines[line_addr] = sInst;
            }



            return(mapLines);
        }
Exemple #18
0
        protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly)
        {
            switch (Id)
            {
            case ReadyToRunHelperId.NewHelper:
            {
                TypeDesc target = (TypeDesc)Target;
                encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol(target));
                encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewObjectHelperForType(target)));
            }
            break;

            case ReadyToRunHelperId.VirtualCall:
                if (relocsOnly)
                {
                    break;
                }

                AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64);
                encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr);

                {
                    int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, (MethodDesc)Target);
                    Debug.Assert(slot != -1);
                    AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64);
                    encoder.EmitJmpToAddrMode(ref jmpAddrMode);
                }
                break;

            case ReadyToRunHelperId.IsInstanceOf:
            {
                TypeDesc target = (TypeDesc)Target;
                encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target));
                encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, false)));
            }
            break;

            case ReadyToRunHelperId.CastClass:
            {
                TypeDesc target = (TypeDesc)Target;
                encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target));
                encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, true)));
            }
            break;

            case ReadyToRunHelperId.NewArr1:
            {
                TypeDesc target = (TypeDesc)Target;


                // TODO: Swap argument order instead
                // mov arg1, arg0
                encoder.Builder.EmitByte(0x48);
                encoder.Builder.EmitShort((short)((encoder.TargetRegister.Arg0 == Register.RCX) ? 0xD18B : 0xF78B));

                encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.NecessaryTypeSymbol(target));
                encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewArrayHelperForType(target)));
            }
            break;

            case ReadyToRunHelperId.GetNonGCStaticBase:
            {
                MetadataType target = (MetadataType)Target;
                if (!target.HasStaticConstructor)
                {
                    Debug.Assert(Id == ReadyToRunHelperId.GetNonGCStaticBase);
                    encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target));
                    encoder.EmitRET();
                }
                else
                {
                    // We need to trigger the cctor before returning the base
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeCctorContextSymbol(target));
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.TypeNonGCStaticsSymbol(target));
                    encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase));
                }
            }
            break;

            case ReadyToRunHelperId.GetThreadStaticBase:
                encoder.EmitINT3();
                break;

            case ReadyToRunHelperId.GetGCStaticBase:
            {
                MetadataType target = (MetadataType)Target;
                if (!target.HasStaticConstructor)
                {
                    encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol(target));
                    AddrMode loadFromRax = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64);
                    encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax);
                    encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax);
                    encoder.EmitRET();
                }
                else
                {
                    // We need to trigger the cctor before returning the base
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeCctorContextSymbol(target));
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.TypeGCStaticsSymbol(target));
                    AddrMode loadFromRdx = new AddrMode(encoder.TargetRegister.Arg1, null, 0, 0, AddrModeSize.Int64);
                    encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromRdx);
                    encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromRdx);
                    encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase));
                }
            }
            break;

            case ReadyToRunHelperId.DelegateCtor:
            {
                DelegateInfo target = (DelegateInfo)Target;

                encoder.EmitLEAQ(encoder.TargetRegister.Arg2, factory.MethodEntrypoint(target.Target));
                if (target.ShuffleThunk != null)
                {
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg3, factory.MethodEntrypoint(target.ShuffleThunk));
                }

                encoder.EmitJMP(factory.MethodEntrypoint(target.Ctor));
            }
            break;

            case ReadyToRunHelperId.InterfaceDispatch:
            {
                encoder.EmitLEAQ(Register.R10, factory.InterfaceDispatchCell((MethodDesc)Target));
                AddrMode jmpAddrMode = new AddrMode(Register.R10, null, 0, 0, AddrModeSize.Int64);
                encoder.EmitJmpToAddrMode(ref jmpAddrMode);
            }
            break;

            default:
                throw new NotImplementedException();
            }
        }
Exemple #19
0
        public static string BuildOperandString(AddrMode addrMode, byte[] operand)
        {
            switch (addrMode)
            {
            case AddrMode.I:
            {
                return($"#${operand[0].ToHex(HexPrefix)}");
            }

            case AddrMode.ZP:
            {
                return($"${operand[0].ToHex(HexPrefix)}");
            }

            case AddrMode.ZP_X:
            {
                return($"${operand[0].ToHex(HexPrefix)},X");
            }

            case AddrMode.ZP_Y:
            {
                return($"${operand[0].ToHex(HexPrefix)},Y");
            }

            case AddrMode.ABS:
            {
                return($"${operand.ToLittleEndianWord().ToHex(HexPrefix)}");
            }

            case AddrMode.ABS_X:
            {
                return($"${operand.ToLittleEndianWord().ToHex(HexPrefix)},X");
            }

            case AddrMode.ABS_Y:
            {
                return($"${operand.ToLittleEndianWord().ToHex(HexPrefix)},Y");
            }

            case AddrMode.IX_IND:
            {
                return($"(${operand[0].ToHex(HexPrefix)},X)");
            }

            case AddrMode.IND_IX:
            {
                return($"(${operand[0].ToHex(HexPrefix)}),Y");
            }

            case AddrMode.Indirect:
            {
                return($"(${operand.ToLittleEndianWord().ToHex(HexPrefix)})");
            }

            case AddrMode.Relative:
            {
                var offset = (sbyte)operand[0];
                return($"*{(offset>=0?"+":"")}{offset}");
            }

            case AddrMode.Accumulator:
            {
                return("A");
            }

            case AddrMode.Implied:
            {
                return("");
            }

            default:
                throw new DotNet6502Exception($"Bug detected! Unhandled addressing mode: {addrMode}");
            }
        }
Exemple #20
0
        protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly)
        {
            switch (Helper.Id)
            {
            case ReadyToRunHelperId.NewHelper:
                encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol((TypeDesc)Helper.Target));
                encoder.EmitJMP(factory.ExternSymbol("__allocate_object"));
                break;

            case ReadyToRunHelperId.VirtualCall:
                if (relocsOnly)
                {
                    break;
                }

                AddrMode loadFromRcx = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64);
                encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRcx);

                // TODO: More efficient lookup of the slot
                {
                    MethodDesc method     = (MethodDesc)Helper.Target;
                    TypeDesc   owningType = method.OwningType;

                    int baseSlots = 0;
                    var baseType  = owningType.BaseType;

                    while (baseType != null)
                    {
                        List <MethodDesc> baseVirtualSlots;
                        factory.VirtualSlots.TryGetValue(baseType, out baseVirtualSlots);

                        if (baseVirtualSlots != null)
                        {
                            baseSlots += baseVirtualSlots.Count;
                        }
                        baseType = baseType.BaseType;
                    }

                    List <MethodDesc> virtualSlots = factory.VirtualSlots[owningType];
                    int methodSlot = -1;
                    for (int slot = 0; slot < virtualSlots.Count; slot++)
                    {
                        if (virtualSlots[slot] == method)
                        {
                            methodSlot = slot;
                            break;
                        }
                    }

                    Debug.Assert(methodSlot != -1);
                    AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, 16 + (baseSlots + methodSlot) * factory.Target.PointerSize, 0, AddrModeSize.Int64);
                    encoder.EmitJmpToAddrMode(ref jmpAddrMode);
                }
                break;

            case ReadyToRunHelperId.IsInstanceOf:
                encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol((TypeDesc)Helper.Target));
                encoder.EmitJMP(factory.ExternSymbol("__isinst_class"));
                break;

            case ReadyToRunHelperId.CastClass:
                encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol((TypeDesc)Helper.Target));
                encoder.EmitJMP(factory.ExternSymbol("__castclass_class"));
                break;

            case ReadyToRunHelperId.NewArr1:
                encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol((TypeDesc)Helper.Target));
                encoder.EmitJMP(factory.ExternSymbol("__allocate_array"));
                break;

            case ReadyToRunHelperId.GetNonGCStaticBase:
                if (!((MetadataType)Helper.Target).HasStaticConstructor)
                {
                    Debug.Assert(Helper.Id == ReadyToRunHelperId.GetNonGCStaticBase);
                    encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol((MetadataType)Helper.Target));
                    encoder.EmitRET();
                }
                else
                {
                    // We need to trigger the cctor before returning the base
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeCctorContextSymbol((MetadataType)Helper.Target));
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.TypeNonGCStaticsSymbol((MetadataType)Helper.Target));
                    encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase));
                }
                break;

            case ReadyToRunHelperId.GetThreadStaticBase:
                encoder.EmitINT3();
                break;

            case ReadyToRunHelperId.GetGCStaticBase:
                if (!((MetadataType)Helper.Target).HasStaticConstructor)
                {
                    encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol((MetadataType)Helper.Target));
                    AddrMode loadFromRax = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64);
                    encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax);
                    encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax);
                    encoder.EmitRET();
                }
                else
                {
                    // We need to trigger the cctor before returning the base
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeCctorContextSymbol((MetadataType)Helper.Target));
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.TypeGCStaticsSymbol((MetadataType)Helper.Target));
                    AddrMode loadFromRdx = new AddrMode(encoder.TargetRegister.Arg1, null, 0, 0, AddrModeSize.Int64);
                    encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromRdx);
                    encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromRdx);
                    encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase));
                }
                break;

            case ReadyToRunHelperId.DelegateCtor:
            {
                DelegateInfo target = (DelegateInfo)Helper.Target;

                encoder.EmitLEAQ(encoder.TargetRegister.Arg2, factory.MethodEntrypoint(target.Target));
                if (target.ShuffleThunk != null)
                {
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg3, factory.MethodEntrypoint(target.ShuffleThunk));
                }

                encoder.EmitJMP(factory.MethodEntrypoint(target.Ctor));
            }
            break;

            default:
                throw new NotImplementedException();
            }
        }
Exemple #21
0
        protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly)
        {
            switch (Id)
            {
            case ReadyToRunHelperId.NewHelper:
            {
                TypeDesc target = (TypeDesc)Target;
                encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol(target));
                encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewObjectHelperForType(target)));
            }
            break;

            case ReadyToRunHelperId.VirtualCall:
            {
                MethodDesc targetMethod = (MethodDesc)Target;

                if (targetMethod.OwningType.IsInterface)
                {
                    encoder.EmitLEAQ(Register.R10, factory.InterfaceDispatchCell((MethodDesc)Target));
                    AddrMode jmpAddrMode = new AddrMode(Register.R10, null, 0, 0, AddrModeSize.Int64);
                    encoder.EmitJmpToAddrMode(ref jmpAddrMode);
                }
                else
                {
                    if (relocsOnly)
                    {
                        break;
                    }

                    AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64);
                    encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr);

                    int pointerSize = factory.Target.PointerSize;

                    int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod);
                    Debug.Assert(slot != -1);
                    AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize), 0, AddrModeSize.Int64);
                    encoder.EmitJmpToAddrMode(ref jmpAddrMode);
                }
            }
            break;

            case ReadyToRunHelperId.IsInstanceOf:
            {
                TypeDesc target = (TypeDesc)Target;
                encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target));
                encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, false)));
            }
            break;

            case ReadyToRunHelperId.CastClass:
            {
                TypeDesc target = (TypeDesc)Target;
                encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target));
                encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, true)));
            }
            break;

            case ReadyToRunHelperId.NewArr1:
            {
                TypeDesc target = (TypeDesc)Target;

                // TODO: Swap argument order instead
                encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg0);
                encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol(target));
                encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewArrayHelperForType(target)));
            }
            break;

            case ReadyToRunHelperId.GetNonGCStaticBase:
            {
                MetadataType target = (MetadataType)Target;
                bool         hasLazyStaticConstructor = factory.TypeSystemContext.HasLazyStaticConstructor(target);
                encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target), hasLazyStaticConstructor ? NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target) : 0);

                if (!hasLazyStaticConstructor)
                {
                    encoder.EmitRET();
                }
                else
                {
                    // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region.
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target));

                    AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32);
                    encoder.EmitCMP(ref initialized, 1);
                    encoder.EmitRETIfEqual();

                    encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result);
                    encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase));
                }
            }
            break;

            case ReadyToRunHelperId.GetThreadStaticBase:
                encoder.EmitINT3();
                break;

            case ReadyToRunHelperId.GetGCStaticBase:
            {
                MetadataType target = (MetadataType)Target;

                encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol(target));
                AddrMode loadFromRax = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64);
                encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax);
                encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax);

                if (!factory.TypeSystemContext.HasLazyStaticConstructor(target))
                {
                    encoder.EmitRET();
                }
                else
                {
                    // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region.
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target));

                    AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32);
                    encoder.EmitCMP(ref initialized, 1);
                    encoder.EmitRETIfEqual();

                    encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result);

                    encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase));
                }
            }
            break;

            case ReadyToRunHelperId.DelegateCtor:
            {
                DelegateCreationInfo target = (DelegateCreationInfo)Target;

                encoder.EmitLEAQ(encoder.TargetRegister.Arg2, target.Target);

                if (target.Thunk != null)
                {
                    Debug.Assert(target.Constructor.Method.Signature.Length == 3);
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg3, target.Thunk);
                }
                else
                {
                    Debug.Assert(target.Constructor.Method.Signature.Length == 2);
                }

                encoder.EmitJMP(target.Constructor);
            }
            break;

            case ReadyToRunHelperId.ResolveVirtualFunction:
            {
                MethodDesc targetMethod = (MethodDesc)Target;
                if (targetMethod.OwningType.IsInterface)
                {
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod));
                    encoder.EmitJMP(factory.ExternSymbol("RhpResolveInterfaceMethod"));
                }
                else
                {
                    if (relocsOnly)
                    {
                        break;
                    }

                    AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64);
                    encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr);

                    int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod);
                    Debug.Assert(slot != -1);
                    AddrMode loadFromSlot = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64);
                    encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromSlot);
                    encoder.EmitRET();
                }
            }
            break;

            case ReadyToRunHelperId.ResolveGenericVirtualMethod:
                encoder.EmitINT3();
                break;

            default:
                throw new NotImplementedException();
            }
        }
        protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly)
        {
            switch (Id)
            {
            case ReadyToRunHelperId.NewHelper:
            {
                TypeDesc target = (TypeDesc)Target;
                encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol(target));
                encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewObjectHelperForType(target)));
            }
            break;

            case ReadyToRunHelperId.VirtualCall:
            {
                MethodDesc targetMethod = (MethodDesc)Target;

                if (targetMethod.OwningType.IsInterface)
                {
                    encoder.EmitLEAQ(Register.R10, factory.InterfaceDispatchCell((MethodDesc)Target));
                    AddrMode jmpAddrMode = new AddrMode(Register.R10, null, 0, 0, AddrModeSize.Int64);
                    encoder.EmitJmpToAddrMode(ref jmpAddrMode);
                }
                else
                {
                    if (relocsOnly)
                    {
                        break;
                    }

                    AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64);
                    encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr);

                    int pointerSize = factory.Target.PointerSize;

                    int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod);
                    Debug.Assert(slot != -1);
                    AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize), 0, AddrModeSize.Int64);
                    encoder.EmitJmpToAddrMode(ref jmpAddrMode);
                }
            }
            break;

            case ReadyToRunHelperId.IsInstanceOf:
            {
                TypeDesc target = (TypeDesc)Target;
                encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target));
                encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, false)));
            }
            break;

            case ReadyToRunHelperId.CastClass:
            {
                TypeDesc target = (TypeDesc)Target;
                encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target));
                encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, true)));
            }
            break;

            case ReadyToRunHelperId.NewArr1:
            {
                TypeDesc target = (TypeDesc)Target;

                // TODO: Swap argument order instead
                encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg0);
                encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol(target));
                encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewArrayHelperForType(target)));
            }
            break;

            case ReadyToRunHelperId.GetNonGCStaticBase:
            {
                MetadataType target = (MetadataType)Target;
                bool         hasLazyStaticConstructor = factory.TypeSystemContext.HasLazyStaticConstructor(target);
                encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target), hasLazyStaticConstructor ? NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target) : 0);

                if (!hasLazyStaticConstructor)
                {
                    encoder.EmitRET();
                }
                else
                {
                    // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region.
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target));

                    AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32);
                    encoder.EmitCMP(ref initialized, 1);
                    encoder.EmitRETIfEqual();

                    encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result);
                    encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase));
                }
            }
            break;

            case ReadyToRunHelperId.GetThreadStaticBase:
            {
                MetadataType target = (MetadataType)Target;

                encoder.EmitLEAQ(encoder.TargetRegister.Arg2, factory.TypeThreadStaticIndex(target));

                // First arg: address of the TypeManager slot that provides the helper with
                // information about module index and the type manager instance (which is used
                // for initialization on first access).
                AddrMode loadFromArg2 = new AddrMode(encoder.TargetRegister.Arg2, null, 0, 0, AddrModeSize.Int64);
                encoder.EmitMOV(encoder.TargetRegister.Arg0, ref loadFromArg2);

                // Second arg: index of the type in the ThreadStatic section of the modules
                AddrMode loadFromArg2AndDelta = new AddrMode(encoder.TargetRegister.Arg2, null, factory.Target.PointerSize, 0, AddrModeSize.Int64);
                encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromArg2AndDelta);

                if (!factory.TypeSystemContext.HasLazyStaticConstructor(target))
                {
                    encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType));
                }
                else
                {
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target));
                    // TODO: performance optimization - inline the check verifying whether we need to trigger the cctor
                    encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase));
                }
            }
            break;

            case ReadyToRunHelperId.GetGCStaticBase:
            {
                MetadataType target = (MetadataType)Target;

                encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol(target));
                AddrMode loadFromRax = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64);
                encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax);
                encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax);

                if (!factory.TypeSystemContext.HasLazyStaticConstructor(target))
                {
                    encoder.EmitRET();
                }
                else
                {
                    // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region.
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target));

                    AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32);
                    encoder.EmitCMP(ref initialized, 1);
                    encoder.EmitRETIfEqual();

                    encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result);

                    encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase));
                }
            }
            break;

            case ReadyToRunHelperId.DelegateCtor:
            {
                DelegateCreationInfo target = (DelegateCreationInfo)Target;

                if (target.TargetNeedsVTableLookup)
                {
                    AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg1, null, 0, 0, AddrModeSize.Int64);
                    encoder.EmitMOV(encoder.TargetRegister.Arg2, ref loadFromThisPtr);

                    int slot = 0;
                    if (!relocsOnly)
                    {
                        slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, target.TargetMethod);
                    }

                    Debug.Assert(slot != -1);
                    AddrMode loadFromSlot = new AddrMode(encoder.TargetRegister.Arg2, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64);
                    encoder.EmitMOV(encoder.TargetRegister.Arg2, ref loadFromSlot);
                }
                else
                {
                    int         delta            = 0;
                    ISymbolNode targetMethodNode = target.GetTargetNode(factory);
                    if (targetMethodNode is IFatFunctionPointerNode)
                    {
                        delta = FatFunctionPointerConstants.Offset;
                    }

                    encoder.EmitLEAQ(encoder.TargetRegister.Arg2, target.GetTargetNode(factory), delta);
                }

                if (target.Thunk != null)
                {
                    Debug.Assert(target.Constructor.Method.Signature.Length == 3);
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg3, target.Thunk);
                }
                else
                {
                    Debug.Assert(target.Constructor.Method.Signature.Length == 2);
                }

                encoder.EmitJMP(target.Constructor);
            }
            break;

            case ReadyToRunHelperId.ResolveVirtualFunction:
            {
                MethodDesc targetMethod = (MethodDesc)Target;
                if (targetMethod.OwningType.IsInterface)
                {
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod));
                    encoder.EmitJMP(factory.ExternSymbol("RhpResolveInterfaceMethod"));
                }
                else
                {
                    if (relocsOnly)
                    {
                        break;
                    }

                    AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64);
                    encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr);

                    int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod);
                    Debug.Assert(slot != -1);
                    AddrMode loadFromSlot = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64);
                    encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromSlot);
                    encoder.EmitRET();
                }
            }
            break;

            default:
                throw new NotImplementedException();
            }
        }
Exemple #23
0
        protected sealed override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly)
        {
            // First load the generic context into Arg0.
            EmitLoadGenericContext(factory, ref encoder, relocsOnly);

            switch (_id)
            {
            case ReadyToRunHelperId.GetNonGCStaticBase:
            {
                MetadataType target = (MetadataType)_target;

                if (!factory.TypeSystemContext.HasLazyStaticConstructor(target))
                {
                    EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly);
                    encoder.EmitRET();
                }
                else
                {
                    EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg0, _lookupSignature, relocsOnly);
                    encoder.EmitMOV(encoder.TargetRegister.Result, encoder.TargetRegister.Arg0);

                    // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region.
                    int      cctorContextSize = NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target);
                    AddrMode initialized      = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize - cctorContextSize, 0, AddrModeSize.Int32);
                    encoder.EmitCMP(ref initialized, 1);
                    encoder.EmitRETIfEqual();

                    AddrMode loadCctor = new AddrMode(encoder.TargetRegister.Arg0, null, -cctorContextSize, 0, AddrModeSize.Int64);
                    encoder.EmitLEA(encoder.TargetRegister.Arg0, ref loadCctor);
                    encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result);
                    encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase));
                }
            }
            break;

            case ReadyToRunHelperId.GetGCStaticBase:
            {
                MetadataType target = (MetadataType)_target;

                EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly);

                AddrMode loadFromResult = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64);
                encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromResult);
                encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromResult);

                if (!factory.TypeSystemContext.HasLazyStaticConstructor(target))
                {
                    encoder.EmitRET();
                }
                else
                {
                    // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region.
                    GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target);
                    EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg0, nonGcRegionLookup, relocsOnly);

                    int      cctorContextSize = NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target);
                    AddrMode initialized      = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize - cctorContextSize, 0, AddrModeSize.Int32);
                    encoder.EmitCMP(ref initialized, 1);
                    encoder.EmitRETIfEqual();

                    encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result);
                    AddrMode loadCctor = new AddrMode(encoder.TargetRegister.Arg0, null, -cctorContextSize, 0, AddrModeSize.Int64);
                    encoder.EmitLEA(encoder.TargetRegister.Arg0, ref loadCctor);

                    encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase));
                }
            }
            break;

            case ReadyToRunHelperId.GetThreadStaticBase:
            {
                MetadataType target = (MetadataType)_target;

                // Look up the index cell
                EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg1, _lookupSignature, relocsOnly);

                ISymbolNode helperEntrypoint;
                if (factory.TypeSystemContext.HasLazyStaticConstructor(target))
                {
                    // There is a lazy class constructor. We need the non-GC static base because that's where the
                    // class constructor context lives.
                    GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target);
                    EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2, nonGcRegionLookup, relocsOnly);
                    int      cctorContextSize = NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target);
                    AddrMode loadCctor        = new AddrMode(encoder.TargetRegister.Arg2, null, -cctorContextSize, 0, AddrModeSize.Int64);
                    encoder.EmitLEA(encoder.TargetRegister.Arg2, ref loadCctor);

                    helperEntrypoint = factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase);
                }
                else
                {
                    helperEntrypoint = factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType);
                }

                // First arg: address of the TypeManager slot that provides the helper with
                // information about module index and the type manager instance (which is used
                // for initialization on first access).
                AddrMode loadFromArg1 = new AddrMode(encoder.TargetRegister.Arg1, null, 0, 0, AddrModeSize.Int64);
                encoder.EmitMOV(encoder.TargetRegister.Arg0, ref loadFromArg1);

                // Second arg: index of the type in the ThreadStatic section of the modules
                AddrMode loadFromArg1AndDelta = new AddrMode(encoder.TargetRegister.Arg1, null, factory.Target.PointerSize, 0, AddrModeSize.Int64);
                encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromArg1AndDelta);

                encoder.EmitJMP(helperEntrypoint);
            }
            break;

            // These are all simple: just get the thing from the dictionary and we're done
            case ReadyToRunHelperId.TypeHandle:
            case ReadyToRunHelperId.MethodHandle:
            case ReadyToRunHelperId.FieldHandle:
            case ReadyToRunHelperId.MethodDictionary:
            case ReadyToRunHelperId.VirtualCall:
            case ReadyToRunHelperId.ResolveVirtualFunction:
            case ReadyToRunHelperId.MethodEntry:
            {
                EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly);
                encoder.EmitRET();
            }
            break;

            default:
                throw new NotImplementedException();
            }
        }
        protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly)
        {
            switch (Id)
            {
            case ReadyToRunHelperId.VirtualCall:
            {
                MethodDesc targetMethod = (MethodDesc)Target;

                Debug.Assert(!targetMethod.OwningType.IsInterface);
                Debug.Assert(!targetMethod.CanMethodBeInSealedVTable());

                AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64);
                encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr);

                int pointerSize = factory.Target.PointerSize;

                int slot = 0;
                if (!relocsOnly)
                {
                    slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType);
                    Debug.Assert(slot != -1);
                }
                Debug.Assert(((NativeSequencePoint[])((INodeWithDebugInfo)this).GetNativeSequencePoints())[1].NativeOffset == encoder.Builder.CountBytes);

                AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize), 0, AddrModeSize.Int64);
                encoder.EmitJmpToAddrMode(ref jmpAddrMode);
            }
            break;

            case ReadyToRunHelperId.GetNonGCStaticBase:
            {
                MetadataType target = (MetadataType)Target;
                bool         hasLazyStaticConstructor = factory.PreinitializationManager.HasLazyStaticConstructor(target);
                encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target));

                if (!hasLazyStaticConstructor)
                {
                    encoder.EmitRET();
                }
                else
                {
                    // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region.
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target), -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target));

                    AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32);
                    encoder.EmitCMP(ref initialized, 1);
                    encoder.EmitRETIfEqual();

                    encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result);
                    encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase));
                }
            }
            break;

            case ReadyToRunHelperId.GetThreadStaticBase:
            {
                MetadataType target = (MetadataType)Target;

                encoder.EmitLEAQ(encoder.TargetRegister.Arg2, factory.TypeThreadStaticIndex(target));

                // First arg: address of the TypeManager slot that provides the helper with
                // information about module index and the type manager instance (which is used
                // for initialization on first access).
                AddrMode loadFromArg2 = new AddrMode(encoder.TargetRegister.Arg2, null, 0, 0, AddrModeSize.Int64);
                encoder.EmitMOV(encoder.TargetRegister.Arg0, ref loadFromArg2);

                // Second arg: index of the type in the ThreadStatic section of the modules
                AddrMode loadFromArg2AndDelta = new AddrMode(encoder.TargetRegister.Arg2, null, factory.Target.PointerSize, 0, AddrModeSize.Int32);
                encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromArg2AndDelta);

                if (!factory.PreinitializationManager.HasLazyStaticConstructor(target))
                {
                    encoder.EmitJMP(factory.ExternSymbol("RhpGetThreadStaticBaseForType"));
                }
                else
                {
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target), -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target));

                    AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg2, null, factory.Target.PointerSize, 0, AddrModeSize.Int32);
                    encoder.EmitCMP(ref initialized, 1);
                    encoder.EmitJE(factory.ExternSymbol("RhpGetThreadStaticBaseForType"));

                    encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase));
                }
            }
            break;

            case ReadyToRunHelperId.GetGCStaticBase:
            {
                MetadataType target = (MetadataType)Target;

                encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol(target));
                AddrMode loadFromRax = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64);
                encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax);

                if (!factory.PreinitializationManager.HasLazyStaticConstructor(target))
                {
                    encoder.EmitRET();
                }
                else
                {
                    // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region.
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target), -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target));

                    AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32);
                    encoder.EmitCMP(ref initialized, 1);
                    encoder.EmitRETIfEqual();

                    encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result);

                    encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase));
                }
            }
            break;

            case ReadyToRunHelperId.DelegateCtor:
            {
                DelegateCreationInfo target = (DelegateCreationInfo)Target;

                if (target.TargetNeedsVTableLookup)
                {
                    Debug.Assert(!target.TargetMethod.CanMethodBeInSealedVTable());

                    AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg1, null, 0, 0, AddrModeSize.Int64);
                    encoder.EmitMOV(encoder.TargetRegister.Arg2, ref loadFromThisPtr);

                    int slot = 0;
                    if (!relocsOnly)
                    {
                        slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, target.TargetMethod, target.TargetMethod.OwningType);
                    }

                    Debug.Assert(slot != -1);
                    AddrMode loadFromSlot = new AddrMode(encoder.TargetRegister.Arg2, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64);
                    encoder.EmitMOV(encoder.TargetRegister.Arg2, ref loadFromSlot);
                }
                else
                {
                    ISymbolNode targetMethodNode = target.GetTargetNode(factory);
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg2, target.GetTargetNode(factory));
                }

                if (target.Thunk != null)
                {
                    Debug.Assert(target.Constructor.Method.Signature.Length == 3);
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg3, target.Thunk);
                }
                else
                {
                    Debug.Assert(target.Constructor.Method.Signature.Length == 2);
                }

                encoder.EmitJMP(target.Constructor);
            }
            break;

            case ReadyToRunHelperId.ResolveVirtualFunction:
            {
                MethodDesc targetMethod = (MethodDesc)Target;
                if (targetMethod.OwningType.IsInterface)
                {
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod));
                    encoder.EmitJMP(factory.ExternSymbol("RhpResolveInterfaceMethod"));
                }
                else
                {
                    if (relocsOnly)
                    {
                        break;
                    }

                    AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64);
                    encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr);

                    Debug.Assert(!targetMethod.CanMethodBeInSealedVTable());

                    int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType);
                    Debug.Assert(slot != -1);
                    AddrMode loadFromSlot = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64);
                    encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromSlot);
                    encoder.EmitRET();
                }
            }
            break;

            default:
                throw new NotImplementedException();
            }
        }
Exemple #25
0
        protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly)
        {
            switch (Id)
            {
            case ReadyToRunHelperId.NewHelper:
                encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol((TypeDesc)Target));
                encoder.EmitJMP(factory.ExternSymbol("__allocate_object"));
                break;

            case ReadyToRunHelperId.VirtualCall:
                if (relocsOnly)
                {
                    break;
                }

                AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64);
                encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr);

                {
                    int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, (MethodDesc)Target);
                    Debug.Assert(slot != -1);
                    AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64);
                    encoder.EmitJmpToAddrMode(ref jmpAddrMode);
                }
                break;

            case ReadyToRunHelperId.IsInstanceOf:
                encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol((TypeDesc)Target));
                encoder.EmitJMP(factory.ExternSymbol("__isinst_class"));
                break;

            case ReadyToRunHelperId.CastClass:
                encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol((TypeDesc)Target));
                encoder.EmitJMP(factory.ExternSymbol("__castclass_class"));
                break;

            case ReadyToRunHelperId.NewArr1:
                encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol((TypeDesc)Target));
                encoder.EmitJMP(factory.ExternSymbol("__allocate_array"));
                break;

            case ReadyToRunHelperId.GetNonGCStaticBase:
                if (!((MetadataType)Target).HasStaticConstructor)
                {
                    Debug.Assert(Id == ReadyToRunHelperId.GetNonGCStaticBase);
                    encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol((MetadataType)Target));
                    encoder.EmitRET();
                }
                else
                {
                    // We need to trigger the cctor before returning the base
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeCctorContextSymbol((MetadataType)Target));
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.TypeNonGCStaticsSymbol((MetadataType)Target));
                    encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase));
                }
                break;

            case ReadyToRunHelperId.GetThreadStaticBase:
                encoder.EmitINT3();
                break;

            case ReadyToRunHelperId.GetGCStaticBase:
                if (!((MetadataType)Target).HasStaticConstructor)
                {
                    encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol((MetadataType)Target));
                    AddrMode loadFromRax = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64);
                    encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax);
                    encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax);
                    encoder.EmitRET();
                }
                else
                {
                    // We need to trigger the cctor before returning the base
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeCctorContextSymbol((MetadataType)Target));
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.TypeGCStaticsSymbol((MetadataType)Target));
                    AddrMode loadFromRdx = new AddrMode(encoder.TargetRegister.Arg1, null, 0, 0, AddrModeSize.Int64);
                    encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromRdx);
                    encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromRdx);
                    encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase));
                }
                break;

            case ReadyToRunHelperId.DelegateCtor:
            {
                DelegateInfo target = (DelegateInfo)Target;

                encoder.EmitLEAQ(encoder.TargetRegister.Arg2, factory.MethodEntrypoint(target.Target));
                if (target.ShuffleThunk != null)
                {
                    encoder.EmitLEAQ(encoder.TargetRegister.Arg3, factory.MethodEntrypoint(target.ShuffleThunk));
                }

                encoder.EmitJMP(factory.MethodEntrypoint(target.Ctor));
            }
            break;

            default:
                throw new NotImplementedException();
            }
        }