public static void Aesd_V(ArmEmitterContext context) { OpCodeSimd op = (OpCodeSimd)context.CurrOp; Operand d = GetVec(op.Rd); Operand n = GetVec(op.Rn); Operand res; if (Optimizations.UseAesni) { res = context.AddIntrinsic(Intrinsic.X86Aesdeclast, context.AddIntrinsic(Intrinsic.X86Xorpd, d, n), context.VectorZero()); } else { res = context.Call(new _V128_V128_V128(SoftFallback.Decrypt), d, n); } context.Copy(d, res); }
public static void Urshl_V(ArmEmitterContext context) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; Operand res = context.VectorZero(); int elems = op.GetBytesCount() >> op.Size; for (int index = 0; index < elems; index++) { Operand ne = EmitVectorExtractZx(context, op.Rn, index, op.Size); Operand me = EmitVectorExtractZx(context, op.Rm, index, op.Size); Operand e = context.Call(new _U64_U64_U64_Bool_S32(SoftFallback.UnsignedShlReg), ne, me, Const(1), Const(op.Size)); res = EmitVectorInsert(context, res, e, index, op.Size); } context.Copy(GetVec(op.Rd), res); }
public static void Sshl_V(ArmEmitterContext context) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; Operand res = context.VectorZero(); int elems = op.GetBytesCount() >> op.Size; for (int index = 0; index < elems; index++) { Operand ne = EmitVectorExtractSx(context, op.Rn, index, op.Size); Operand me = EmitVectorExtractSx(context, op.Rm, index, op.Size); Operand e = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShlReg)), ne, me, Const(0), Const(op.Size)); res = EmitVectorInsert(context, res, e, index, op.Size); } context.Copy(GetVec(op.Rd), res); }
public static void EmitTailContinue(ArmEmitterContext context, Operand address, bool allowRejit = false) { bool useTailContinue = true; // Left option here as it may be useful if we need to return to managed rather than tail call in future. (eg. for debug) if (useTailContinue) { if (allowRejit) { address = context.BitwiseOr(address, Const(1L)); } Operand fallbackAddr = context.Call(new _U64_U64(NativeInterface.GetFunctionAddress), address); EmitNativeCall(context, fallbackAddr, true); } else { context.Return(address); } }
public static void Aese_V(ArmEmitterContext context) { OpCodeSimd op = (OpCodeSimd)context.CurrOp; Operand d = GetVec(op.Rd); Operand n = GetVec(op.Rn); Operand res; if (Optimizations.UseAesni) { res = context.AddIntrinsic(Intrinsic.X86Aesenclast, context.AddIntrinsic(Intrinsic.X86Xorpd, d, n), context.VectorZero()); } else { res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Encrypt)), d, n); } context.Copy(d, res); }
public static Operand EmitLoadExclusive( ArmEmitterContext context, Operand address, bool exclusive, int size) { Delegate fallbackMethodDlg = null; if (exclusive) { switch (size) { case 0: fallbackMethodDlg = new _U8_U64(NativeInterface.ReadByteExclusive); break; case 1: fallbackMethodDlg = new _U16_U64(NativeInterface.ReadUInt16Exclusive); break; case 2: fallbackMethodDlg = new _U32_U64(NativeInterface.ReadUInt32Exclusive); break; case 3: fallbackMethodDlg = new _U64_U64(NativeInterface.ReadUInt64Exclusive); break; case 4: fallbackMethodDlg = new _V128_U64(NativeInterface.ReadVector128Exclusive); break; } } else { switch (size) { case 0: fallbackMethodDlg = new _U8_U64(NativeInterface.ReadByte); break; case 1: fallbackMethodDlg = new _U16_U64(NativeInterface.ReadUInt16); break; case 2: fallbackMethodDlg = new _U32_U64(NativeInterface.ReadUInt32); break; case 3: fallbackMethodDlg = new _U64_U64(NativeInterface.ReadUInt64); break; case 4: fallbackMethodDlg = new _V128_U64(NativeInterface.ReadVector128); break; } } return(context.Call(fallbackMethodDlg, address)); }
public static Operand EmitLoadExclusive( ArmEmitterContext context, Operand address, bool exclusive, int size) { MethodInfo info = null; if (exclusive) { switch (size) { case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByteExclusive)); break; case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16Exclusive)); break; case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32Exclusive)); break; case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64Exclusive)); break; case 4: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128Exclusive)); break; } } else { switch (size) { case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte)); break; case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16)); break; case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)); break; case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)); break; case 4: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128)); break; } } return(context.Call(info, address)); }
public static void Msr(ArmEmitterContext context) { OpCodeSystem op = (OpCodeSystem)context.CurrOp; MethodInfo info; switch (GetPackedId(op)) { case 0b11_011_0100_0010_000: EmitSetNzcv(context); return; case 0b11_011_0100_0100_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpcr)); break; case 0b11_011_0100_0100_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsr)); break; case 0b11_011_1101_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl0)); break; default: throw new NotImplementedException($"Unknown MSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); } context.Call(info, GetIntOrZR(context, op.Rt)); }
public static void Msr(ArmEmitterContext context) { OpCodeSystem op = (OpCodeSystem)context.CurrOp; Delegate dlg; switch (GetPackedId(op)) { case 0b11_011_0100_0010_000: EmitSetNzcv(context); return; case 0b11_011_0100_0100_000: dlg = new _Void_U64(NativeInterface.SetFpcr); break; case 0b11_011_0100_0100_001: dlg = new _Void_U64(NativeInterface.SetFpsr); break; case 0b11_011_1101_0000_010: dlg = new _Void_U64(NativeInterface.SetTpidrEl0); break; default: throw new NotImplementedException($"Unknown MSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); } context.Call(dlg, GetIntOrZR(context, op.Rt)); }
public static void Mrrc(ArmEmitterContext context) { OpCode32System op = (OpCode32System)context.CurrOp; if (op.Coproc != 15) { InstEmit.Und(context); return; } int opc = op.MrrcOp; MethodInfo info; switch (op.CRm) { case 14: // Timer. switch (opc) { case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); break; default: throw new NotImplementedException($"Unknown MRRC Opc1 0x{opc:X16} at 0x{op.Address:X16}."); } break; default: throw new NotImplementedException($"Unknown MRRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); } Operand result = context.Call(info); SetIntA32(context, op.Rt, context.ConvertI64ToI32(result)); SetIntA32(context, op.CRn, context.ConvertI64ToI32(context.ShiftRightUI(result, Const(32)))); }
private static void EmitReadVectorFallback( ArmEmitterContext context, Operand address, Operand vector, int rt, int elem, int size) { Delegate fallbackMethodDlg = null; switch (size) { case 0: fallbackMethodDlg = new _U8_U64(NativeInterface.ReadByte); break; case 1: fallbackMethodDlg = new _U16_U64(NativeInterface.ReadUInt16); break; case 2: fallbackMethodDlg = new _U32_U64(NativeInterface.ReadUInt32); break; case 3: fallbackMethodDlg = new _U64_U64(NativeInterface.ReadUInt64); break; case 4: fallbackMethodDlg = new _V128_U64(NativeInterface.ReadVector128); break; } Operand value = context.Call(fallbackMethodDlg, address); switch (size) { case 0: value = context.VectorInsert8(vector, value, elem); break; case 1: value = context.VectorInsert16(vector, value, elem); break; case 2: value = context.VectorInsert(vector, value, elem); break; case 3: value = context.VectorInsert(vector, value, elem); break; } context.Copy(GetVec(rt), value); }
private static void EmitReadVectorFallback( ArmEmitterContext context, Operand address, Operand vector, int rt, int elem, int size) { MethodInfo info = null; switch (size) { case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte)); break; case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16)); break; case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)); break; case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)); break; case 4: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128)); break; } Operand value = context.Call(info, address); switch (size) { case 0: value = context.VectorInsert8(vector, value, elem); break; case 1: value = context.VectorInsert16(vector, value, elem); break; case 2: value = context.VectorInsert(vector, value, elem); break; case 3: value = context.VectorInsert(vector, value, elem); break; } context.Copy(GetVec(rt), value); }
private static Operand EmitSatQ(ArmEmitterContext context, Operand value, int eSize, bool signed) { Debug.Assert(eSize <= 32); long intMin = signed ? -(1L << (eSize - 1)) : 0; long intMax = signed ? (1L << (eSize - 1)) - 1 : (1L << eSize) - 1; Operand gt = context.ICompareGreater(value, Const(value.Type, intMax)); Operand lt = context.ICompareLess(value, Const(value.Type, intMin)); value = context.ConditionalSelect(gt, Const(value.Type, intMax), value); value = context.ConditionalSelect(lt, Const(value.Type, intMin), value); Operand lblNoSat = Label(); context.BranchIfFalse(lblNoSat, context.BitwiseOr(gt, lt)); context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetFpsrQc))); context.MarkLabel(lblNoSat); return(value); }
public static void Rbit_V(ArmEmitterContext context) { OpCodeSimd op = (OpCodeSimd)context.CurrOp; Operand res = context.VectorZero(); int elems = op.RegisterSize == RegisterSize.Simd128 ? 16 : 8; for (int index = 0; index < elems; index++) { Operand ne = EmitVectorExtractZx(context, op.Rn, index, 0); ne = context.ConvertI64ToI32(ne); Operand de = context.Call(new _U32_U32(SoftFallback.ReverseBits8), ne); de = context.ZeroExtend32(OperandType.I64, de); res = EmitVectorInsert(context, res, de, index, 0); } context.Copy(GetVec(op.Rd), res); }
private static void EmitWriteIntFallback(ArmEmitterContext context, Operand address, int rt, int size) { Delegate fallbackMethodDlg = null; switch (size) { case 0: fallbackMethodDlg = new _Void_U64_U8(NativeInterface.WriteByte); break; case 1: fallbackMethodDlg = new _Void_U64_U16(NativeInterface.WriteUInt16); break; case 2: fallbackMethodDlg = new _Void_U64_U32(NativeInterface.WriteUInt32); break; case 3: fallbackMethodDlg = new _Void_U64_U64(NativeInterface.WriteUInt64); break; } Operand value = GetInt(context, rt); if (size < 3 && value.Type == OperandType.I64) { value = context.ConvertI64ToI32(value); } context.Call(fallbackMethodDlg, address, value); }
private static void EmitWriteIntFallback(ArmEmitterContext context, Operand address, int rt, int size) { MethodInfo info = null; switch (size) { case 0: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteByte)); break; case 1: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt16)); break; case 2: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt32)); break; case 3: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.WriteUInt64)); break; } Operand value = GetInt(context, rt); if (size < 3 && value.Type == OperandType.I64) { value = context.ConvertI64ToI32(value); } context.Call(info, address, value); }
public static void Clrex(ArmEmitterContext context) { context.Call(new _Void(NativeInterface.ClearExclusive)); }
private static void EmitTableVectorLookup(ArmEmitterContext context, bool isTbl) { OpCodeSimdTbl op = (OpCodeSimdTbl)context.CurrOp; if (Optimizations.UseSsse3) { Operand d = GetVec(op.Rd); Operand m = GetVec(op.Rm); Operand res; Operand mask = X86GetAllElements(context, 0x0F0F0F0F0F0F0F0FL); // Fast path for single register table. { Operand n = GetVec(op.Rn); Operand mMask = context.AddIntrinsic(Intrinsic.X86Pcmpgtb, m, mask); mMask = context.AddIntrinsic(Intrinsic.X86Por, mMask, m); res = context.AddIntrinsic(Intrinsic.X86Pshufb, n, mMask); } for (int index = 1; index < op.Size; index++) { Operand ni = GetVec((op.Rn + index) & 0x1F); Operand idxMask = X86GetAllElements(context, 0x1010101010101010L * index); Operand mSubMask = context.AddIntrinsic(Intrinsic.X86Psubb, m, idxMask); Operand mMask = context.AddIntrinsic(Intrinsic.X86Pcmpgtb, mSubMask, mask); mMask = context.AddIntrinsic(Intrinsic.X86Por, mMask, mSubMask); Operand res2 = context.AddIntrinsic(Intrinsic.X86Pshufb, ni, mMask); res = context.AddIntrinsic(Intrinsic.X86Por, res, res2); } if (!isTbl) { Operand idxMask = X86GetAllElements(context, (0x1010101010101010L * op.Size) - 0x0101010101010101L); Operand zeroMask = context.VectorZero(); Operand mPosMask = context.AddIntrinsic(Intrinsic.X86Pcmpgtb, m, idxMask); Operand mNegMask = context.AddIntrinsic(Intrinsic.X86Pcmpgtb, zeroMask, m); Operand mMask = context.AddIntrinsic(Intrinsic.X86Por, mPosMask, mNegMask); Operand dMask = context.AddIntrinsic(Intrinsic.X86Pand, d, mMask); res = context.AddIntrinsic(Intrinsic.X86Por, res, dMask); } if (op.RegisterSize == RegisterSize.Simd64) { res = context.VectorZeroUpper64(res); } context.Copy(d, res); } else { Operand d = GetVec(op.Rd); List <Operand> args = new List <Operand>(); if (!isTbl) { args.Add(d); } args.Add(GetVec(op.Rm)); args.Add(Const(op.RegisterSize == RegisterSize.Simd64 ? 8 : 16)); for (int index = 0; index < op.Size; index++) { args.Add(GetVec((op.Rn + index) & 0x1F)); } Delegate dlg = null; switch (op.Size) { case 1: dlg = isTbl ? (Delegate) new _V128_V128_S32_V128(SoftFallback.Tbl1) : (Delegate) new _V128_V128_V128_S32_V128(SoftFallback.Tbx1); break; case 2: dlg = isTbl ? (Delegate) new _V128_V128_S32_V128_V128(SoftFallback.Tbl2) : (Delegate) new _V128_V128_V128_S32_V128_V128(SoftFallback.Tbx2); break; case 3: dlg = isTbl ? (Delegate) new _V128_V128_S32_V128_V128_V128(SoftFallback.Tbl3) : (Delegate) new _V128_V128_V128_S32_V128_V128_V128(SoftFallback.Tbx3); break; case 4: dlg = isTbl ? (Delegate) new _V128_V128_S32_V128_V128_V128_V128(SoftFallback.Tbl4) : (Delegate) new _V128_V128_V128_S32_V128_V128_V128_V128(SoftFallback.Tbx4); break; } context.Copy(d, context.Call(dlg, args.ToArray())); } }
public static void Tbl_V(ArmEmitterContext context) { OpCodeSimdTbl op = (OpCodeSimdTbl)context.CurrOp; if (Optimizations.UseSsse3) { Operand n = GetVec(op.Rn); Operand m = GetVec(op.Rm); Operand mask = X86GetAllElements(context, 0x0F0F0F0F0F0F0F0FL); Operand mMask = context.AddIntrinsic(Intrinsic.X86Pcmpgtb, m, mask); mMask = context.AddIntrinsic(Intrinsic.X86Por, mMask, m); Operand res = context.AddIntrinsic(Intrinsic.X86Pshufb, n, mMask); for (int index = 1; index < op.Size; index++) { Operand ni = GetVec((op.Rn + index) & 0x1f); Operand indexMask = X86GetAllElements(context, 0x1010101010101010L * index); Operand mMinusMask = context.AddIntrinsic(Intrinsic.X86Psubb, m, indexMask); Operand mMask2 = context.AddIntrinsic(Intrinsic.X86Pcmpgtb, mMinusMask, mask); mMask2 = context.AddIntrinsic(Intrinsic.X86Por, mMask2, mMinusMask); Operand res2 = context.AddIntrinsic(Intrinsic.X86Pshufb, ni, mMask2); res = context.AddIntrinsic(Intrinsic.X86Por, res, res2); } if (op.RegisterSize == RegisterSize.Simd64) { res = context.VectorZeroUpper64(res); } context.Copy(GetVec(op.Rd), res); } else { Operand[] args = new Operand[1 + op.Size]; args[0] = GetVec(op.Rm); for (int index = 0; index < op.Size; index++) { args[1 + index] = GetVec((op.Rn + index) & 0x1f); } Delegate dlg = null; switch (op.Size) { case 1: dlg = op.RegisterSize == RegisterSize.Simd64 ? (Delegate) new _V128_V128_V128(SoftFallback.Tbl1_V64) : (Delegate) new _V128_V128_V128(SoftFallback.Tbl1_V128); break; case 2: dlg = op.RegisterSize == RegisterSize.Simd64 ? (Delegate) new _V128_V128_V128_V128(SoftFallback.Tbl2_V64) : (Delegate) new _V128_V128_V128_V128(SoftFallback.Tbl2_V128); break; case 3: dlg = op.RegisterSize == RegisterSize.Simd64 ? (Delegate) new _V128_V128_V128_V128_V128(SoftFallback.Tbl3_V64) : (Delegate) new _V128_V128_V128_V128_V128(SoftFallback.Tbl3_V128); break; case 4: dlg = op.RegisterSize == RegisterSize.Simd64 ? (Delegate) new _V128_V128_V128_V128_V128_V128(SoftFallback.Tbl4_V64) : (Delegate) new _V128_V128_V128_V128_V128_V128(SoftFallback.Tbl4_V128); break; } context.Copy(GetVec(op.Rd), context.Call(dlg, args)); } }
public static void EmitStoreExclusive( ArmEmitterContext context, Operand address, Operand value, bool exclusive, int size, int rs, bool a32) { if (size < 3) { value = context.ConvertI64ToI32(value); } if (exclusive) { void SetRs(Operand value) { if (a32) { SetIntA32(context, rs, value); } else { SetIntOrZR(context, rs, value); } } Operand arg0 = context.LoadArgument(OperandType.I64, 0); Operand exAddrPtr = context.Add(arg0, Const((long)NativeContext.GetExclusiveAddressOffset())); Operand exAddr = context.Load(address.Type, exAddrPtr); // STEP 1: Check if we have exclusive access to this memory region. If not, fail and skip store. Operand maskedAddress = context.BitwiseAnd(address, Const(address.Type, GetExclusiveAddressMask())); Operand exFailed = context.ICompareNotEqual(exAddr, maskedAddress); Operand lblExit = Label(); SetRs(exFailed); context.BranchIfTrue(lblExit, exFailed); // STEP 2: We have exclusive access, make sure that the address is valid. Operand isUnalignedAddr = InstEmitMemoryHelper.EmitAddressCheck(context, address, size); Operand lblFastPath = Label(); context.BranchIfFalse(lblFastPath, isUnalignedAddr); // The call is not expected to return (it should throw). context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess)), address); // STEP 3: We have exclusive access and the address is valid, attempt the store using CAS. context.MarkLabel(lblFastPath); Operand physAddr = InstEmitMemoryHelper.EmitPtPointerLoad(context, address, null, write: true); Operand exValuePtr = context.Add(arg0, Const((long)NativeContext.GetExclusiveValueOffset())); Operand exValue = size switch { 0 => context.Load8(exValuePtr), 1 => context.Load16(exValuePtr), 2 => context.Load(OperandType.I32, exValuePtr), 3 => context.Load(OperandType.I64, exValuePtr), _ => context.Load(OperandType.V128, exValuePtr) }; Operand currValue = size switch { 0 => context.CompareAndSwap8(physAddr, exValue, value), 1 => context.CompareAndSwap16(physAddr, exValue, value), _ => context.CompareAndSwap(physAddr, exValue, value) }; // STEP 4: Check if we succeeded by comparing expected and in-memory values. Operand storeFailed; if (size == 4) { Operand currValueLow = context.VectorExtract(OperandType.I64, currValue, 0); Operand currValueHigh = context.VectorExtract(OperandType.I64, currValue, 1); Operand exValueLow = context.VectorExtract(OperandType.I64, exValue, 0); Operand exValueHigh = context.VectorExtract(OperandType.I64, exValue, 1); storeFailed = context.BitwiseOr( context.ICompareNotEqual(currValueLow, exValueLow), context.ICompareNotEqual(currValueHigh, exValueHigh)); } else { storeFailed = context.ICompareNotEqual(currValue, exValue); } SetRs(storeFailed); context.MarkLabel(lblExit); } else { InstEmitMemoryHelper.EmitWriteIntAligned(context, address, value, size); } }
// VCVT (floating-point to integer, floating-point) | VCVT (integer to floating-point, floating-point). public static void Vcvt_FI(ArmEmitterContext context) { OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp; bool toInteger = (op.Opc2 & 0b100) != 0; OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32; if (toInteger) { bool unsigned = (op.Opc2 & 1) == 0; bool roundWithFpscr = op.Opc != 1; if (!roundWithFpscr && Optimizations.UseSse41) { EmitSse41ConvertInt32(context, FPRoundingMode.TowardsZero, !unsigned); } else { Operand toConvert = ExtractScalar(context, floatSize, op.Vm); Operand asInteger; // TODO: Fast Path. if (roundWithFpscr) { MethodInfo info; if (floatSize == OperandType.FP64) { info = unsigned ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.DoubleToUInt32)) : typeof(SoftFallback).GetMethod(nameof(SoftFallback.DoubleToInt32)); } else { info = unsigned ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.FloatToUInt32)) : typeof(SoftFallback).GetMethod(nameof(SoftFallback.FloatToInt32)); } asInteger = context.Call(info, toConvert); } else { // Round towards zero. asInteger = EmitSaturateFloatToInt(context, toConvert, unsigned); } InsertScalar(context, op.Vd, asInteger); } } else { bool unsigned = op.Opc == 0; Operand toConvert = ExtractScalar(context, OperandType.I32, op.Vm); Operand asFloat = EmitFPConvert(context, toConvert, floatSize, !unsigned); InsertScalar(context, op.Vd, asFloat); } }
private static void EmitFcmpOrFcmpe(ArmEmitterContext context, bool signalNaNs) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; bool cmpWithZero = !(op is OpCodeSimdFcond) ? op.Bit3 : false; if (Optimizations.FastFP && (signalNaNs ? Optimizations.UseAvx : Optimizations.UseSse2)) { Operand n = GetVec(op.Rn); Operand m = cmpWithZero ? context.VectorZero() : GetVec(op.Rm); CmpCondition cmpOrdered = signalNaNs ? CmpCondition.OrderedS : CmpCondition.OrderedQ; Operand lblNaN = Label(); Operand lblEnd = Label(); if (op.Size == 0) { Operand ordMask = context.AddIntrinsic(Intrinsic.X86Cmpss, n, m, Const((int)cmpOrdered)); Operand isOrdered = context.AddIntrinsicInt(Intrinsic.X86Cvtsi2si, ordMask); context.BranchIfFalse(lblNaN, isOrdered); Operand cf = context.AddIntrinsicInt(Intrinsic.X86Comissge, n, m); Operand zf = context.AddIntrinsicInt(Intrinsic.X86Comisseq, n, m); Operand nf = context.AddIntrinsicInt(Intrinsic.X86Comisslt, n, m); SetFlag(context, PState.VFlag, Const(0)); SetFlag(context, PState.CFlag, cf); SetFlag(context, PState.ZFlag, zf); SetFlag(context, PState.NFlag, nf); } else /* if (op.Size == 1) */ { Operand ordMask = context.AddIntrinsic(Intrinsic.X86Cmpsd, n, m, Const((int)cmpOrdered)); Operand isOrdered = context.AddIntrinsicLong(Intrinsic.X86Cvtsi2si, ordMask); context.BranchIfFalse(lblNaN, isOrdered); Operand cf = context.AddIntrinsicInt(Intrinsic.X86Comisdge, n, m); Operand zf = context.AddIntrinsicInt(Intrinsic.X86Comisdeq, n, m); Operand nf = context.AddIntrinsicInt(Intrinsic.X86Comisdlt, n, m); SetFlag(context, PState.VFlag, Const(0)); SetFlag(context, PState.CFlag, cf); SetFlag(context, PState.ZFlag, zf); SetFlag(context, PState.NFlag, nf); } context.Branch(lblEnd); context.MarkLabel(lblNaN); SetFlag(context, PState.VFlag, Const(1)); SetFlag(context, PState.CFlag, Const(1)); SetFlag(context, PState.ZFlag, Const(0)); SetFlag(context, PState.NFlag, Const(0)); context.MarkLabel(lblEnd); } else { OperandType type = op.Size != 0 ? OperandType.FP64 : OperandType.FP32; Operand ne = context.VectorExtract(type, GetVec(op.Rn), 0); Operand me; if (cmpWithZero) { me = op.Size == 0 ? ConstF(0f) : ConstF(0d); } else { me = context.VectorExtract(type, GetVec(op.Rm), 0); } Delegate dlg = op.Size != 0 ? (Delegate) new _S32_F64_F64_Bool(SoftFloat64.FPCompare) : (Delegate) new _S32_F32_F32_Bool(SoftFloat32.FPCompare); Operand nzcv = context.Call(dlg, ne, me, Const(signalNaNs)); EmitSetNzcv(context, nzcv); } }
public static void Vcvt_FI(ArmEmitterContext context) { OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp; bool toInteger = (op.Opc2 & 0b100) != 0; OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32; if (toInteger) { bool unsigned = (op.Opc2 & 1) == 0; bool roundWithFpscr = op.Opc != 1; Operand toConvert = ExtractScalar(context, floatSize, op.Vm); Operand asInteger; // TODO: Fast Path. if (roundWithFpscr) { // These need to get the FPSCR value, so it's worth noting we'd need to do a c# call at some point. if (floatSize == OperandType.FP64) { if (unsigned) { asInteger = context.Call(new _U32_F64(SoftFallback.DoubleToUInt32), toConvert); } else { asInteger = context.Call(new _S32_F64(SoftFallback.DoubleToInt32), toConvert); } } else { if (unsigned) { asInteger = context.Call(new _U32_F32(SoftFallback.FloatToUInt32), toConvert); } else { asInteger = context.Call(new _S32_F32(SoftFallback.FloatToInt32), toConvert); } } } else { // Round towards zero. asInteger = EmitSaturateFloatToInt(context, toConvert, unsigned); } InsertScalar(context, op.Vd, asInteger); } else { bool unsigned = op.Opc == 0; Operand toConvert = ExtractScalar(context, OperandType.I32, op.Vm); Operand asFloat = EmitFPConvert(context, toConvert, floatSize, !unsigned); InsertScalar(context, op.Vd, asFloat); } }
public static void Fcvt_S(ArmEmitterContext context) { OpCodeSimd op = (OpCodeSimd)context.CurrOp; if (op.Size == 0 && op.Opc == 1) // Single -> Double. { if (Optimizations.UseSse2) { Operand n = GetVec(op.Rn); Operand res = context.AddIntrinsic(Intrinsic.X86Cvtss2sd, context.VectorZero(), n); context.Copy(GetVec(op.Rd), res); } else { Operand ne = context.VectorExtract(OperandType.FP32, GetVec(op.Rn), 0); Operand res = context.ConvertToFP(OperandType.FP64, ne); context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), res, 0)); } } else if (op.Size == 1 && op.Opc == 0) // Double -> Single. { if (Optimizations.UseSse2) { Operand n = GetVec(op.Rn); Operand res = context.AddIntrinsic(Intrinsic.X86Cvtsd2ss, context.VectorZero(), n); context.Copy(GetVec(op.Rd), res); } else { Operand ne = context.VectorExtract(OperandType.FP64, GetVec(op.Rn), 0); Operand res = context.ConvertToFP(OperandType.FP32, ne); context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), res, 0)); } } else if (op.Size == 0 && op.Opc == 3) // Single -> Half. { Operand ne = context.VectorExtract(OperandType.FP32, GetVec(op.Rn), 0); Delegate dlg = new _U16_F32(SoftFloat32_16.FPConvert); Operand res = context.Call(dlg, ne); res = context.ZeroExtend16(OperandType.I64, res); context.Copy(GetVec(op.Rd), EmitVectorInsert(context, context.VectorZero(), res, 0, 1)); } else if (op.Size == 3 && op.Opc == 0) // Half -> Single. { Operand ne = EmitVectorExtractZx(context, op.Rn, 0, 1); Delegate dlg = new _F32_U16(SoftFloat16_32.FPConvert); Operand res = context.Call(dlg, ne); context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), res, 0)); } else if (op.Size == 1 && op.Opc == 3) // Double -> Half. { throw new NotImplementedException("Double-precision to half-precision."); } else if (op.Size == 3 && op.Opc == 1) // Double -> Half. { throw new NotImplementedException("Half-precision to double-precision."); } else // Invalid encoding. { Debug.Assert(false, $"type == {op.Size} && opc == {op.Opc}"); } }
private static void EmitTableBranch(ArmEmitterContext context, Operand guestAddress, bool isJump) { context.StoreToContext(); if (guestAddress.Type == OperandType.I32) { guestAddress = context.ZeroExtend32(OperandType.I64, guestAddress); } // Store the target guest address into the native context. The stubs uses this address to dispatch into the // next translation. Operand nativeContext = context.LoadArgument(OperandType.I64, 0); Operand dispAddressAddr = context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset())); context.Store(dispAddressAddr, guestAddress); Operand hostAddress; // If address is mapped onto the function table, we can skip the table walk. Otherwise we fallback // onto the dispatch stub. if (guestAddress.Kind == OperandKind.Constant && context.FunctionTable.IsValid(guestAddress.Value)) { Operand hostAddressAddr = !context.HasPtc ? Const(ref context.FunctionTable.GetValue(guestAddress.Value)) : Const(ref context.FunctionTable.GetValue(guestAddress.Value), new Symbol(SymbolType.FunctionTable, guestAddress.Value)); hostAddress = context.Load(OperandType.I64, hostAddressAddr); } else { hostAddress = !context.HasPtc ? Const((long)context.Stubs.DispatchStub) : Const((long)context.Stubs.DispatchStub, Ptc.DispatchStubSymbol); } if (isJump) { context.Tailcall(hostAddress, nativeContext); } else { OpCode op = context.CurrOp; Operand returnAddress = context.Call(hostAddress, OperandType.I64, nativeContext); context.LoadFromContext(); // Note: The return value of a translated function is always an Int64 with the address execution has // returned to. We expect this address to be immediately after the current instruction, if it isn't we // keep returning until we reach the dispatcher. Operand nextAddr = Const((long)op.Address + op.OpCodeSizeInBytes); // Try to continue within this block. // If the return address isn't to our next instruction, we need to return so the JIT can figure out // what to do. Operand lblContinue = context.GetLabel(nextAddr.Value); context.BranchIf(lblContinue, returnAddress, nextAddr, Comparison.Equal, BasicBlockFrequency.Cold); context.Return(returnAddress); } }
private static void EmitBranchFallback(ArmEmitterContext context, Operand address, bool isJump) { Operand fallbackAddr = context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)), address); EmitNativeCall(context, fallbackAddr, isJump); }
public static void Clrex(ArmEmitterContext context) { context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ClearExclusive))); }
private static void EmitVcmpOrVcmpe(ArmEmitterContext context, bool signalNaNs) { OpCode32SimdS op = (OpCode32SimdS)context.CurrOp; bool cmpWithZero = (op.Opc & 2) != 0; int sizeF = op.Size & 1; if (Optimizations.FastFP && (signalNaNs ? Optimizations.UseAvx : Optimizations.UseSse2)) { CmpCondition cmpOrdered = signalNaNs ? CmpCondition.OrderedS : CmpCondition.OrderedQ; bool doubleSize = sizeF != 0; int shift = doubleSize ? 1 : 2; Operand m = GetVecA32(op.Vm >> shift); Operand n = GetVecA32(op.Vd >> shift); n = EmitSwapScalar(context, n, op.Vd, doubleSize); m = cmpWithZero ? context.VectorZero() : EmitSwapScalar(context, m, op.Vm, doubleSize); Operand lblNaN = Label(); Operand lblEnd = Label(); if (!doubleSize) { Operand ordMask = context.AddIntrinsic(Intrinsic.X86Cmpss, n, m, Const((int)cmpOrdered)); Operand isOrdered = context.AddIntrinsicInt(Intrinsic.X86Cvtsi2si, ordMask); context.BranchIfFalse(lblNaN, isOrdered); Operand cf = context.AddIntrinsicInt(Intrinsic.X86Comissge, n, m); Operand zf = context.AddIntrinsicInt(Intrinsic.X86Comisseq, n, m); Operand nf = context.AddIntrinsicInt(Intrinsic.X86Comisslt, n, m); SetFpFlag(context, FPState.VFlag, Const(0)); SetFpFlag(context, FPState.CFlag, cf); SetFpFlag(context, FPState.ZFlag, zf); SetFpFlag(context, FPState.NFlag, nf); } else { Operand ordMask = context.AddIntrinsic(Intrinsic.X86Cmpsd, n, m, Const((int)cmpOrdered)); Operand isOrdered = context.AddIntrinsicLong(Intrinsic.X86Cvtsi2si, ordMask); context.BranchIfFalse(lblNaN, isOrdered); Operand cf = context.AddIntrinsicInt(Intrinsic.X86Comisdge, n, m); Operand zf = context.AddIntrinsicInt(Intrinsic.X86Comisdeq, n, m); Operand nf = context.AddIntrinsicInt(Intrinsic.X86Comisdlt, n, m); SetFpFlag(context, FPState.VFlag, Const(0)); SetFpFlag(context, FPState.CFlag, cf); SetFpFlag(context, FPState.ZFlag, zf); SetFpFlag(context, FPState.NFlag, nf); } context.Branch(lblEnd); context.MarkLabel(lblNaN); SetFpFlag(context, FPState.VFlag, Const(1)); SetFpFlag(context, FPState.CFlag, Const(1)); SetFpFlag(context, FPState.ZFlag, Const(0)); SetFpFlag(context, FPState.NFlag, Const(0)); context.MarkLabel(lblEnd); } else { OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32; Operand ne = ExtractScalar(context, type, op.Vd); Operand me; if (cmpWithZero) { me = sizeF == 0 ? ConstF(0f) : ConstF(0d); } else { me = ExtractScalar(context, type, op.Vm); } MethodInfo info = sizeF != 0 ? typeof(SoftFloat64).GetMethod(nameof(SoftFloat64.FPCompare)) : typeof(SoftFloat32).GetMethod(nameof(SoftFloat32.FPCompare)); Operand nzcv = context.Call(info, ne, me, Const(signalNaNs)); EmitSetFpscrNzcv(context, nzcv); } }