Esempio n. 1
0
        private static void EmitVirtualCallOrJump(ILEmitterCtx context, bool isJump)
        {
            if (context.Tier == TranslationTier.Tier0)
            {
                context.Emit(OpCodes.Dup);

                context.EmitSttmp();
                context.EmitLdarg(TranslatedSub.StateArgIdx);

                context.EmitFieldLoad(typeof(CpuThreadState).GetField(nameof(CpuThreadState.CurrentTranslator),
                                                                      BindingFlags.Instance |
                                                                      BindingFlags.NonPublic));

                context.EmitLdarg(TranslatedSub.StateArgIdx);
                context.EmitLdtmp();

                context.EmitPrivateCall(typeof(Translator), nameof(Translator.TranslateVirtualSubroutine));

                context.Emit(OpCodes.Ret);
            }
            else
            {
                context.EmitSttmp();
                context.EmitLdarg(TranslatedSub.StateArgIdx);

                context.EmitFieldLoad(typeof(CpuThreadState).GetField(nameof(CpuThreadState.CurrentTranslator),
                                                                      BindingFlags.Instance |
                                                                      BindingFlags.NonPublic));

                context.EmitLdarg(TranslatedSub.StateArgIdx);
                context.EmitLdtmp();

                context.EmitPrivateCall(typeof(Translator), nameof(Translator.GetOrTranslateVirtualSubroutine));

                context.EmitLdarg(TranslatedSub.StateArgIdx);
                context.EmitLdarg(TranslatedSub.MemoryArgIdx);

                if (isJump)
                {
                    //The tail prefix allows the JIT to jump to the next function,
                    //while releasing the stack space used by the current one.
                    //This is ideal for BR ARM instructions, which are
                    //basically indirect tail calls.
                    context.Emit(OpCodes.Tailcall);
                }

                MethodInfo mthdInfo = typeof(ArmSubroutine).GetMethod("Invoke");

                context.EmitCall(mthdInfo, isVirtual: true);

                if (!isJump)
                {
                    EmitContinueOrReturnCheck(context);
                }
                else
                {
                    context.Emit(OpCodes.Ret);
                }
            }
        }
Esempio n. 2
0
        public static void EmitCall(ILEmitterCtx context, long imm)
        {
            if (context.Tier == TranslationTier.Tier0)
            {
                context.EmitStoreContext();

                context.TranslateAhead(imm);

                context.EmitLdc_I8(imm);

                context.Emit(OpCodes.Ret);

                return;
            }

            if (!context.TryOptEmitSubroutineCall())
            {
                context.HasSlowCall = true;

                context.EmitStoreContext();

                context.TranslateAhead(imm);

                context.EmitLdarg(TranslatedSub.StateArgIdx);

                context.EmitLdfld(typeof(CpuThreadState).GetField(nameof(CpuThreadState.CurrentTranslator),
                                                                  BindingFlags.Instance |
                                                                  BindingFlags.NonPublic));

                context.EmitLdarg(TranslatedSub.StateArgIdx);
                context.EmitLdc_I8(imm);
                context.EmitLdc_I4((int)CallType.Call);

                context.EmitPrivateCall(typeof(Translator), nameof(Translator.GetOrTranslateSubroutine));

                context.EmitLdarg(TranslatedSub.StateArgIdx);
                context.EmitLdarg(TranslatedSub.MemoryArgIdx);

                context.EmitCall(typeof(TranslatedSub), nameof(TranslatedSub.Execute));
            }

            EmitContinueOrReturnCheck(context);
        }
Esempio n. 3
0
        private static void EmitExceptionCall(ILEmitterCtx context, string mthdName)
        {
            OpCodeException64 op = (OpCodeException64)context.CurrOp;

            context.EmitStoreState();

            context.EmitLdarg(TranslatedSub.StateArgIdx);

            context.EmitLdc_I8(op.Position);
            context.EmitLdc_I4(op.Id);

            context.EmitPrivateCall(typeof(CpuThreadState), mthdName);

            //Check if the thread should still be running, if it isn't then we return 0
            //to force a return to the dispatcher and then exit the thread.
            context.EmitLdarg(TranslatedSub.StateArgIdx);

            context.EmitCallPropGet(typeof(CpuThreadState), nameof(CpuThreadState.Running));

            ILLabel lblEnd = new ILLabel();

            context.Emit(OpCodes.Brtrue_S, lblEnd);

            context.EmitLdc_I8(0);

            context.Emit(OpCodes.Ret);

            context.MarkLabel(lblEnd);

            if (context.CurrBlock.Next != null)
            {
                context.EmitLoadState(context.CurrBlock.Next);
            }
            else
            {
                context.EmitLdc_I8(op.Position + 4);

                context.Emit(OpCodes.Ret);
            }
        }
Esempio n. 4
0
        public static void Und(ILEmitterCtx context)
        {
            OpCode64 op = context.CurrOp;

            context.EmitStoreState();

            context.EmitLdarg(TranslatedSub.StateArgIdx);

            context.EmitLdc_I8(op.Position);
            context.EmitLdc_I4(op.RawOpCode);

            context.EmitPrivateCall(typeof(CpuThreadState), nameof(CpuThreadState.OnUndefined));

            if (context.CurrBlock.Next != null)
            {
                context.EmitLoadState(context.CurrBlock.Next);
            }
            else
            {
                context.EmitLdc_I8(op.Position + 4);

                context.Emit(OpCodes.Ret);
            }
        }
Esempio n. 5
0
        private static void EmitLoad(ILEmitterCtx context, AccessType accType, bool pair)
        {
            OpCodeMemEx64 op = (OpCodeMemEx64)context.CurrOp;

            bool ordered   = (accType & AccessType.Ordered) != 0;
            bool exclusive = (accType & AccessType.Exclusive) != 0;

            if (ordered)
            {
                EmitBarrier(context);
            }

            context.EmitLdint(op.Rn);
            context.EmitSttmp();

            if (exclusive)
            {
                context.EmitLdarg(TranslatedSub.StateArgIdx);
                context.EmitLdtmp();

                context.EmitPrivateCall(typeof(CpuThreadState), nameof(CpuThreadState.SetExclusiveAddress));
            }

            void WriteExclusiveValue(string propName)
            {
                if (op.Size < 3)
                {
                    context.Emit(OpCodes.Conv_U8);
                }

                context.EmitSttmp2();
                context.EmitLdarg(TranslatedSub.StateArgIdx);
                context.EmitLdtmp2();

                context.EmitCallPrivatePropSet(typeof(CpuThreadState), propName);

                context.EmitLdtmp2();

                if (op.Size < 3)
                {
                    context.Emit(OpCodes.Conv_U4);
                }
            }

            if (pair)
            {
                //Exclusive loads should be atomic. For pairwise loads, we need to
                //read all the data at once. For a 32-bits pairwise load, we do a
                //simple 64-bits load, for a 128-bits load, we need to call a special
                //method to read 128-bits atomically.
                if (op.Size == 2)
                {
                    context.EmitLdarg(TranslatedSub.MemoryArgIdx);
                    context.EmitLdtmp();

                    EmitReadZxCall(context, 3);

                    context.Emit(OpCodes.Dup);

                    //Mask low half.
                    context.Emit(OpCodes.Conv_U4);

                    if (exclusive)
                    {
                        WriteExclusiveValue(nameof(CpuThreadState.ExclusiveValueLow));
                    }

                    context.EmitStintzr(op.Rt);

                    //Shift high half.
                    context.EmitLsr(32);
                    context.Emit(OpCodes.Conv_U4);

                    if (exclusive)
                    {
                        WriteExclusiveValue(nameof(CpuThreadState.ExclusiveValueHigh));
                    }

                    context.EmitStintzr(op.Rt2);
                }
                else if (op.Size == 3)
                {
                    context.EmitLdarg(TranslatedSub.MemoryArgIdx);
                    context.EmitLdtmp();

                    context.EmitPrivateCall(typeof(MemoryManager), nameof(MemoryManager.AtomicReadInt128));

                    context.Emit(OpCodes.Dup);

                    //Load low part of the vector.
                    context.EmitLdc_I4(0);
                    context.EmitLdc_I4(3);

                    VectorHelper.EmitCall(context, nameof(VectorHelper.VectorExtractIntZx));

                    if (exclusive)
                    {
                        WriteExclusiveValue(nameof(CpuThreadState.ExclusiveValueLow));
                    }

                    context.EmitStintzr(op.Rt);

                    //Load high part of the vector.
                    context.EmitLdc_I4(1);
                    context.EmitLdc_I4(3);

                    VectorHelper.EmitCall(context, nameof(VectorHelper.VectorExtractIntZx));

                    if (exclusive)
                    {
                        WriteExclusiveValue(nameof(CpuThreadState.ExclusiveValueHigh));
                    }

                    context.EmitStintzr(op.Rt2);
                }
                else
                {
                    throw new InvalidOperationException($"Invalid store size of {1 << op.Size} bytes.");
                }
            }
            else
            {
                //8, 16, 32 or 64-bits (non-pairwise) load.
                context.EmitLdarg(TranslatedSub.MemoryArgIdx);
                context.EmitLdtmp();

                EmitReadZxCall(context, op.Size);

                if (exclusive)
                {
                    WriteExclusiveValue(nameof(CpuThreadState.ExclusiveValueLow));
                }

                context.EmitStintzr(op.Rt);
            }
        }
Esempio n. 6
0
        public static void Clrex(ILEmitterCtx context)
        {
            context.EmitLdarg(TranslatedSub.StateArgIdx);

            context.EmitPrivateCall(typeof(CpuThreadState), nameof(CpuThreadState.ClearExclusiveAddress));
        }
Esempio n. 7
0
        private static void EmitStore(ILEmitterCtx context, AccessType accType, bool pair)
        {
            OpCodeMemEx64 op = (OpCodeMemEx64)context.CurrOp;

            bool ordered   = (accType & AccessType.Ordered) != 0;
            bool exclusive = (accType & AccessType.Exclusive) != 0;

            if (ordered)
            {
                EmitBarrier(context);
            }

            if (exclusive)
            {
                ILLabel lblEx  = new ILLabel();
                ILLabel lblEnd = new ILLabel();

                context.EmitLdarg(TranslatedSub.StateArgIdx);
                context.EmitLdint(op.Rn);

                context.EmitPrivateCall(typeof(CpuThreadState), nameof(CpuThreadState.CheckExclusiveAddress));

                context.Emit(OpCodes.Brtrue_S, lblEx);

                //Address check failed, set error right away and do not store anything.
                context.EmitLdc_I4(1);
                context.EmitStintzr(op.Rs);

                context.Emit(OpCodes.Br, lblEnd);

                //Address check passsed.
                context.MarkLabel(lblEx);

                context.EmitLdarg(TranslatedSub.MemoryArgIdx);
                context.EmitLdint(op.Rn);

                context.EmitLdarg(TranslatedSub.StateArgIdx);

                context.EmitCallPrivatePropGet(typeof(CpuThreadState), nameof(CpuThreadState.ExclusiveValueLow));

                void EmitCast()
                {
                    //The input should be always int64.
                    switch (op.Size)
                    {
                    case 0: context.Emit(OpCodes.Conv_U1); break;

                    case 1: context.Emit(OpCodes.Conv_U2); break;

                    case 2: context.Emit(OpCodes.Conv_U4); break;
                    }
                }

                EmitCast();

                if (pair)
                {
                    context.EmitLdarg(TranslatedSub.StateArgIdx);

                    context.EmitCallPrivatePropGet(typeof(CpuThreadState), nameof(CpuThreadState.ExclusiveValueHigh));

                    EmitCast();

                    context.EmitLdintzr(op.Rt);

                    EmitCast();

                    context.EmitLdintzr(op.Rt2);

                    EmitCast();

                    switch (op.Size)
                    {
                    case 2: context.EmitPrivateCall(typeof(MemoryManager), nameof(MemoryManager.AtomicCompareExchange2xInt32)); break;

                    case 3: context.EmitPrivateCall(typeof(MemoryManager), nameof(MemoryManager.AtomicCompareExchangeInt128));  break;

                    default: throw new InvalidOperationException($"Invalid store size of {1 << op.Size} bytes.");
                    }
                }
                else
                {
                    context.EmitLdintzr(op.Rt);

                    EmitCast();

                    switch (op.Size)
                    {
                    case 0: context.EmitCall(typeof(MemoryManager), nameof(MemoryManager.AtomicCompareExchangeByte));  break;

                    case 1: context.EmitCall(typeof(MemoryManager), nameof(MemoryManager.AtomicCompareExchangeInt16)); break;

                    case 2: context.EmitCall(typeof(MemoryManager), nameof(MemoryManager.AtomicCompareExchangeInt32)); break;

                    case 3: context.EmitCall(typeof(MemoryManager), nameof(MemoryManager.AtomicCompareExchangeInt64)); break;

                    default: throw new InvalidOperationException($"Invalid store size of {1 << op.Size} bytes.");
                    }
                }

                //The value returned is a bool, true if the values compared
                //were equal and the new value was written, false otherwise.
                //We need to invert this result, as on ARM 1 indicates failure,
                //and 0 success on those instructions.
                context.EmitLdc_I4(1);

                context.Emit(OpCodes.Xor);
                context.Emit(OpCodes.Dup);
                context.Emit(OpCodes.Conv_U8);

                context.EmitStintzr(op.Rs);

                //Only clear the exclusive monitor if the store was successful (Rs = false).
                context.Emit(OpCodes.Brtrue_S, lblEnd);

                Clrex(context);

                context.MarkLabel(lblEnd);
            }
            else
            {
                void EmitWrite(int rt, long offset)
                {
                    context.EmitLdarg(TranslatedSub.MemoryArgIdx);
                    context.EmitLdint(op.Rn);

                    if (offset != 0)
                    {
                        context.EmitLdc_I8(offset);

                        context.Emit(OpCodes.Add);
                    }

                    context.EmitLdintzr(rt);

                    EmitWriteCall(context, op.Size);
                }

                EmitWrite(op.Rt, 0);

                if (pair)
                {
                    EmitWrite(op.Rt2, 1 << op.Size);
                }
            }
        }