public static void EmitSseOrSse2CallF(AILEmitterCtx Context, string Name, bool Scalar) { AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; int SizeF = Op.Size & 1; void Ldvec(int Reg) { Context.EmitLdvec(Reg); if (SizeF == 1) { AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToDouble)); } } Ldvec(Op.Rn); Type Type; Type BaseType; if (SizeF == 0) { Type = typeof(Sse); BaseType = typeof(Vector128 <float>); } else /* if (SizeF == 1) */ { Type = typeof(Sse2); BaseType = typeof(Vector128 <double>); } if (Op is AOpCodeSimdReg BinOp) { Ldvec(BinOp.Rm); Context.EmitCall(Type.GetMethod(Name, new Type[] { BaseType, BaseType })); } else { Context.EmitCall(Type.GetMethod(Name, new Type[] { BaseType })); } if (SizeF == 1) { AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorDoubleToSingle)); } Context.EmitStvec(Op.Rd); if (Scalar) { if (SizeF == 0) { EmitVectorZero32_128(Context, Op.Rd); } else /* if (SizeF == 1) */ { EmitVectorZeroUpper(Context, Op.Rd); } } else if (Op.RegisterSize == ARegisterSize.SIMD64) { EmitVectorZeroUpper(Context, Op.Rd); } }
private static void EmitSseCall(AILEmitterCtx Context, string Name, Type Type) { AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; void Ldvec(int Reg) { Context.EmitLdvec(Reg); switch (Op.Size) { case 0: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToSByte)); break; case 1: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToInt16)); break; case 2: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToInt32)); break; case 3: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToInt64)); break; } } Ldvec(Op.Rn); Type BaseType = null; switch (Op.Size) { case 0: BaseType = typeof(Vector128 <sbyte>); break; case 1: BaseType = typeof(Vector128 <short>); break; case 2: BaseType = typeof(Vector128 <int>); break; case 3: BaseType = typeof(Vector128 <long>); break; } if (Op is AOpCodeSimdReg BinOp) { Ldvec(BinOp.Rm); Context.EmitCall(Type.GetMethod(Name, new Type[] { BaseType, BaseType })); } else { Context.EmitCall(Type.GetMethod(Name, new Type[] { BaseType })); } switch (Op.Size) { case 0: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSByteToSingle)); break; case 1: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt16ToSingle)); break; case 2: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt32ToSingle)); break; case 3: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt64ToSingle)); break; } Context.EmitStvec(Op.Rd); if (Op.RegisterSize == ARegisterSize.SIMD64) { EmitVectorZeroUpper(Context, Op.Rd); } }
public static void EmitLdvecWithCastToDouble(AILEmitterCtx Context, int Reg) { Context.EmitLdvec(Reg); AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToDouble)); }
private static void EmitSimdMemSs(AILEmitterCtx Context, bool IsLoad) { AOpCodeSimdMemSs Op = (AOpCodeSimdMemSs)Context.CurrOp; //TODO: Replicate mode. int Offset = 0; for (int SElem = 0; SElem < Op.SElems; SElem++) { int Rt = (Op.Rt + SElem) & 0x1f; if (IsLoad) { Context.EmitLdvec(Rt); Context.EmitLdc_I4(Op.Index); Context.EmitLdc_I4(Op.Size); Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); Context.EmitLdint(Op.Rn); Context.EmitLdc_I8(Offset); Context.Emit(OpCodes.Add); EmitReadZxCall(Context, Op.Size); ASoftFallback.EmitCall(Context, nameof(ASoftFallback.InsertVec)); Context.EmitStvec(Rt); if (Op.RegisterSize == ARegisterSize.SIMD64) { EmitVectorZeroUpper(Context, Rt); } } else { Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); Context.EmitLdint(Op.Rn); Context.EmitLdc_I8(Offset); Context.Emit(OpCodes.Add); Context.EmitLdvec(Rt); Context.EmitLdc_I4(Op.Index); Context.EmitLdc_I4(Op.Size); ASoftFallback.EmitCall(Context, nameof(ASoftFallback.ExtractVec)); EmitWriteCall(Context, Op.Size); } Offset += 1 << Op.Size; } if (Op.WBack) { Context.EmitLdint(Op.Rn); if (Op.Rm != ARegisters.ZRIndex) { Context.EmitLdint(Op.Rm); } else { Context.EmitLdc_I8(Offset); } Context.Emit(OpCodes.Add); Context.EmitStint(Op.Rn); } }
public static void EmitSaturatingNarrowOp( AILEmitterCtx Context, Action Emit, bool SignedSrc, bool SignedDst, bool Scalar) { AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; int Elems = !Scalar ? 8 >> Op.Size : 1; int ESize = 8 << Op.Size; int Part = !Scalar && (Op.RegisterSize == ARegisterSize.SIMD128) ? Elems : 0; long TMaxValue = SignedDst ? (1 << (ESize - 1)) - 1 : (long)(~0UL >> (64 - ESize)); long TMinValue = SignedDst ? -((1 << (ESize - 1))) : 0; Context.EmitLdc_I8(0L); Context.EmitSttmp(); if (Part != 0) { Context.EmitLdvec(Op.Rd); Context.EmitStvectmp(); } for (int Index = 0; Index < Elems; Index++) { AILLabel LblLe = new AILLabel(); AILLabel LblGeEnd = new AILLabel(); EmitVectorExtract(Context, Op.Rn, Index, Op.Size + 1, SignedSrc); Emit(); Context.Emit(OpCodes.Dup); Context.EmitLdc_I8(TMaxValue); Context.Emit(SignedSrc ? OpCodes.Ble_S : OpCodes.Ble_Un_S, LblLe); Context.Emit(OpCodes.Pop); Context.EmitLdc_I8(TMaxValue); Context.EmitLdc_I8(0x8000000L); Context.EmitSttmp(); Context.Emit(OpCodes.Br_S, LblGeEnd); Context.MarkLabel(LblLe); Context.Emit(OpCodes.Dup); Context.EmitLdc_I8(TMinValue); Context.Emit(SignedSrc ? OpCodes.Bge_S : OpCodes.Bge_Un_S, LblGeEnd); Context.Emit(OpCodes.Pop); Context.EmitLdc_I8(TMinValue); Context.EmitLdc_I8(0x8000000L); Context.EmitSttmp(); Context.MarkLabel(LblGeEnd); if (Scalar) { EmitVectorZeroLowerTmp(Context); } EmitVectorInsertTmp(Context, Part + Index, Op.Size); } Context.EmitLdvectmp(); Context.EmitStvec(Op.Rd); if (Part == 0) { EmitVectorZeroUpper(Context, Op.Rd); } Context.EmitLdarg(ATranslatedSub.StateArgIdx); Context.EmitLdarg(ATranslatedSub.StateArgIdx); Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Fpsr)); Context.EmitLdtmp(); Context.Emit(OpCodes.Conv_I4); Context.Emit(OpCodes.Or); Context.EmitCallPropSet(typeof(AThreadState), nameof(AThreadState.Fpsr)); }
public static void Xtn_V(AILEmitterCtx Context) { AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; int Elems = 8 >> Op.Size; int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; if (AOptimizations.UseSse41 && Op.Size < 2) { void EmitZeroVector() { switch (Op.Size) { case 0: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt16Zero)); break; case 1: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt32Zero)); break; } } //For XTN, first operand is source, second operand is 0. //For XTN2, first operand is 0, second operand is source. if (Part != 0) { EmitZeroVector(); } EmitLdvecWithSignedCast(Context, Op.Rn, Op.Size + 1); //Set mask to discard the upper half of the wide elements. switch (Op.Size) { case 0: Context.EmitLdc_I4(0x00ff); break; case 1: Context.EmitLdc_I4(0x0000ffff); break; } Type WideType = IntTypesPerSizeLog2[Op.Size + 1]; Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), new Type[] { WideType })); WideType = VectorIntTypesPerSizeLog2[Op.Size + 1]; Type[] WideTypes = new Type[] { WideType, WideType }; Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), WideTypes)); if (Part == 0) { EmitZeroVector(); } //Pack values with signed saturation, the signed saturation shouldn't //saturate anything since the upper bits were masked off. Type SseType = Op.Size == 0 ? typeof(Sse2) : typeof(Sse41); Context.EmitCall(SseType.GetMethod(nameof(Sse2.PackUnsignedSaturate), WideTypes)); if (Part != 0) { //For XTN2, we additionally need to discard the upper bits //of the target register and OR the result with it. EmitVectorZeroUpper(Context, Op.Rd); EmitLdvecWithUnsignedCast(Context, Op.Rd, Op.Size); Type NarrowType = VectorUIntTypesPerSizeLog2[Op.Size]; Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Or), new Type[] { NarrowType, NarrowType })); } EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size); } else { if (Part != 0) { Context.EmitLdvec(Op.Rd); Context.EmitStvectmp(); } for (int Index = 0; Index < Elems; Index++) { EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size + 1); EmitVectorInsertTmp(Context, Part + Index, Op.Size); } Context.EmitLdvectmp(); Context.EmitStvec(Op.Rd); if (Part == 0) { EmitVectorZeroUpper(Context, Op.Rd); } } }