public static void Bsl_V(ILEmitterCtx context) { if (Optimizations.UseSse2) { OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; Type[] typesXorAnd = new Type[] { typeof(Vector128 <byte>), typeof(Vector128 <byte>) }; context.EmitLdvec(op.Rm); context.EmitLdvec(op.Rm); context.EmitLdvec(op.Rn); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), typesXorAnd)); context.EmitLdvec(op.Rd); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), typesXorAnd)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), typesXorAnd)); context.EmitStvec(op.Rd); if (op.RegisterSize == RegisterSize.Simd64) { EmitVectorZeroUpper(context, op.Rd); } } else { EmitVectorTernaryOpZx(context, () => { context.EmitSttmp(); context.EmitLdtmp(); context.Emit(OpCodes.Xor); context.Emit(OpCodes.And); context.EmitLdtmp(); context.Emit(OpCodes.Xor); }); } }
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); } if (exclusive) { EmitMemoryCall(context, nameof(MemoryManager.SetExclusive), op.Rn); } context.EmitLdint(op.Rn); context.EmitSttmp(); context.EmitLdarg(TranslatedSub.MemoryArgIdx); context.EmitLdtmp(); EmitReadZxCall(context, op.Size); context.EmitStintzr(op.Rt); if (pair) { context.EmitLdarg(TranslatedSub.MemoryArgIdx); context.EmitLdtmp(); context.EmitLdc_I8(1 << op.Size); context.Emit(OpCodes.Add); EmitReadZxCall(context, op.Size); context.EmitStintzr(op.Rt2); } }
public static void Ldm(ILEmitterCtx context) { OpCode32MemMult op = (OpCode32MemMult)context.CurrOp; EmitLoadFromRegister(context, op.Rn); bool writesToPc = (op.RegisterMask & (1 << RegisterAlias.Aarch32Pc)) != 0; bool writeBack = op.PostOffset != 0 && (op.Rn != RegisterAlias.Aarch32Pc || !writesToPc); if (writeBack) { context.Emit(OpCodes.Dup); } context.EmitLdc_I4(op.Offset); context.Emit(OpCodes.Add); context.EmitSttmp(); if (writeBack) { context.EmitLdc_I4(op.PostOffset); context.Emit(OpCodes.Add); EmitStoreToRegister(context, op.Rn); } int mask = op.RegisterMask; int offset = 0; for (int register = 0; mask != 0; mask >>= 1, register++) { if ((mask & 1) != 0) { context.EmitLdarg(TranslatedSub.MemoryArgIdx); context.EmitLdtmp(); context.EmitLdc_I4(offset); context.Emit(OpCodes.Add); EmitReadZxCall(context, WordSizeLog2); EmitStoreToRegister(context, register); offset += 4; } } }
public static void Stm(ILEmitterCtx context) { OpCode32MemMult op = (OpCode32MemMult)context.CurrOp; EmitLoadFromRegister(context, op.Rn); context.EmitLdc_I4(op.Offset); context.Emit(OpCodes.Add); context.EmitSttmp(); int mask = op.RegisterMask; int offset = 0; for (int register = 0; mask != 0; mask >>= 1, register++) { if ((mask & 1) != 0) { context.EmitLdarg(TranslatedSub.MemoryArgIdx); context.EmitLdtmp(); context.EmitLdc_I4(offset); context.Emit(OpCodes.Add); EmitLoadFromRegister(context, register); EmitWriteCall(context, WordSizeLog2); //Note: If Rn is also specified on the register list, //and Rn is the first register on this list, then the //value that is written to memory is the unmodified value, //before the write back. If it is on the list, but it's //not the first one, then the value written to memory //varies between CPUs. if (offset == 0 && op.PostOffset != 0) { //Emit write back after the first write. EmitLoadFromRegister(context, op.Rn); context.EmitLdc_I4(op.PostOffset); context.Emit(OpCodes.Add); EmitStoreToRegister(context, op.Rn); } offset += 4; } } }
public static void Blr(ILEmitterCtx context) { OpCodeBReg64 op = (OpCodeBReg64)context.CurrOp; context.EmitLdintzr(op.Rn); context.EmitSttmp(); context.EmitLdc_I(op.Position + 4); context.EmitStint(CpuThreadState.LrIndex); context.EmitStoreState(); context.EmitLdtmp(); context.Emit(OpCodes.Ret); }
private static void EmitVirtualCallOrJump(ILEmitterCtx context, bool isJump) { if (context.Tier == TranslationTier.Tier0) { context.Emit(OpCodes.Ret); } else { context.EmitSttmp(); context.EmitLdarg(TranslatedSub.StateArgIdx); context.EmitLdfld(typeof(CpuThreadState).GetField(nameof(CpuThreadState.CurrentTranslator), BindingFlags.Instance | BindingFlags.NonPublic)); context.EmitLdarg(TranslatedSub.StateArgIdx); context.EmitLdtmp(); context.EmitLdc_I4(isJump ? (int)CallType.VirtualJump : (int)CallType.VirtualCall); context.EmitPrivateCall(typeof(Translator), nameof(Translator.GetOrTranslateSubroutine)); 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); } } }
public static void EmitAdcsCCheck(ILEmitterCtx context) { // C = (Rd == Rn && CIn) || Rd < Rn context.EmitSttmp(); context.EmitLdtmp(); context.EmitLdtmp(); EmitAluLoadRn(context); context.Emit(OpCodes.Ceq); context.EmitLdflg((int)PState.CBit); context.Emit(OpCodes.And); context.EmitLdtmp(); EmitAluLoadRn(context); context.Emit(OpCodes.Clt_Un); context.Emit(OpCodes.Or); context.EmitStflg((int)PState.CBit); }
private static void EmitWBackIfNeeded(ILEmitterCtx context) { // Check whenever the current OpCode has post-indexed write back, if so write it. // Note: AOpCodeMemPair inherits from AOpCodeMemImm, so this works for both. if (context.CurrOp is OpCodeMemImm64 op && op.WBack) { context.EmitLdtmp(); if (op.PostIdx) { context.EmitLdc_I(op.Imm); context.Emit(OpCodes.Add); } context.EmitStint(op.Rn); } }
public static void Ldp(ILEmitterCtx context) { OpCodeMemPair64 op = (OpCodeMemPair64)context.CurrOp; void EmitReadAndStore(int rt) { if (op.Extend64) { EmitReadSx64Call(context, op.Size); } else { EmitReadZxCall(context, op.Size); } if (op is IOpCodeSimd64) { context.EmitStvec(rt); } else { context.EmitStintzr(rt); } } context.EmitLdarg(TranslatedSub.MemoryArgIdx); EmitLoadAddress(context); EmitReadAndStore(op.Rt); context.EmitLdarg(TranslatedSub.MemoryArgIdx); context.EmitLdtmp(); context.EmitLdc_I8(1 << op.Size); context.Emit(OpCodes.Add); EmitReadAndStore(op.Rt2); EmitWBackIfNeeded(context); }
public static void Stp(ILEmitterCtx context) { OpCodeMemPair64 op = (OpCodeMemPair64)context.CurrOp; context.EmitLdarg(TranslatedSub.MemoryArgIdx); EmitLoadAddress(context); if (op is IOpCodeSimd64) { context.EmitLdvec(op.Rt); } else { context.EmitLdintzr(op.Rt); } EmitWriteCall(context, op.Size); context.EmitLdarg(TranslatedSub.MemoryArgIdx); context.EmitLdtmp(); context.EmitLdc_I8(1 << op.Size); context.Emit(OpCodes.Add); if (op is IOpCodeSimd64) { context.EmitLdvec(op.Rt2); } else { context.EmitLdintzr(op.Rt2); } EmitWriteCall(context, op.Size); EmitWBackIfNeeded(context); }
private static void EmitLoadOrStore(ILEmitterCtx context, int size, AccessType accType) { OpCode32Mem op = (OpCode32Mem)context.CurrOp; if (op.Index || op.WBack) { EmitLoadFromRegister(context, op.Rn); context.EmitLdc_I4(op.Imm); context.Emit(op.Add ? OpCodes.Add : OpCodes.Sub); context.EmitSttmp(); } if (op.Index) { context.EmitLdtmp(); } else { EmitLoadFromRegister(context, op.Rn); } if ((accType & AccessType.Load) != 0) { if ((accType & AccessType.Signed) != 0) { EmitReadSx32Call(context, size); } else { EmitReadZxCall(context, size); } if (op.WBack) { context.EmitLdtmp(); EmitStoreToRegister(context, op.Rn); } if (size == DWordSizeLog2) { context.Emit(OpCodes.Dup); context.EmitLdflg((int)PState.EBit); ILLabel lblBigEndian = new ILLabel(); ILLabel lblEnd = new ILLabel(); context.Emit(OpCodes.Brtrue_S, lblBigEndian); //Little endian mode. context.Emit(OpCodes.Conv_U4); EmitStoreToRegister(context, op.Rt); context.EmitLsr(32); context.Emit(OpCodes.Conv_U4); EmitStoreToRegister(context, op.Rt | 1); context.Emit(OpCodes.Br_S, lblEnd); //Big endian mode. context.MarkLabel(lblBigEndian); context.EmitLsr(32); context.Emit(OpCodes.Conv_U4); EmitStoreToRegister(context, op.Rt); context.Emit(OpCodes.Conv_U4); EmitStoreToRegister(context, op.Rt | 1); context.MarkLabel(lblEnd); } else { EmitStoreToRegister(context, op.Rt); } } else { if (op.WBack) { context.EmitLdtmp(); EmitStoreToRegister(context, op.Rn); } EmitLoadFromRegister(context, op.Rt); if (size == DWordSizeLog2) { context.Emit(OpCodes.Conv_U8); context.EmitLdflg((int)PState.EBit); ILLabel lblBigEndian = new ILLabel(); ILLabel lblEnd = new ILLabel(); context.Emit(OpCodes.Brtrue_S, lblBigEndian); //Little endian mode. EmitLoadFromRegister(context, op.Rt | 1); context.Emit(OpCodes.Conv_U8); context.EmitLsl(32); context.Emit(OpCodes.Or); context.Emit(OpCodes.Br_S, lblEnd); //Big endian mode. context.MarkLabel(lblBigEndian); context.EmitLsl(32); EmitLoadFromRegister(context, op.Rt | 1); context.Emit(OpCodes.Conv_U8); context.Emit(OpCodes.Or); context.MarkLabel(lblEnd); } EmitWriteCall(context, size); } }
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); } }