Parcourir la source

Add Runtime.Features to detect SIMD instruction set

MineGame159 il y a 2 ans
Parent
commit
7dad948f20

+ 1 - 2
BeefLibs/corlib/src/Numerics/X86/SSE.bf

@@ -2,8 +2,7 @@ namespace System.Numerics.X86
 {
 	static class SSE
 	{
-		[Intrinsic(":add_ps")]
-		public static extern v128 add_ps(v128 a, v128 b);
+		public static bool IsSupported => Runtime.Features.SSE;
 
 		[Inline]
 		public static v128 add_ss(v128 a, v128 b)

+ 1 - 0
BeefLibs/corlib/src/Numerics/X86/SSE2.bf

@@ -2,5 +2,6 @@ namespace System.Numerics.X86
 {
 	static class SSE2
 	{
+		public static bool IsSupported => Runtime.Features.SSE2;
 	}
 }

+ 95 - 0
BeefLibs/corlib/src/Runtime.bf

@@ -6,6 +6,12 @@ using System.Collections;
 
 namespace System
 {
+	struct RuntimeFeatures
+	{
+		public bool SSE, SSE2;
+		public bool AVX, AVX2, AVX512;
+	}
+
 	[StaticInitPriority(101)]
 	static class Runtime
 	{
@@ -359,6 +365,9 @@ namespace System
 		static List<ErrorHandler> sErrorHandlers ~ DeleteContainerAndItems!(_);
 		static bool sInsideErrorHandler;
 
+		static bool sQueriedFeatures = false;
+		static RuntimeFeatures sFeatures;
+
 		public static this()
 		{
 			BfRtCallbacks.sCallbacks.Init();
@@ -466,5 +475,91 @@ namespace System
 			}
 			return .ContinueFailure;
 		}
+
+		public static RuntimeFeatures Features
+		{
+			get
+			{
+				if (!sQueriedFeatures)
+				{
+#if BF_MACHINE_X86 || BF_MACHINE_X64
+					QueryFeaturesX86();
+#else
+					sFeatures = .();
+					sQueriedFeatures = true;
+#endif
+				}
+
+				return sFeatures;
+			}
+		}
+
+#if BF_MACHINE_X86 || BF_MACHINE_X64
+		private static void QueryFeaturesX86()
+		{
+			sFeatures = .();
+			sQueriedFeatures = true;
+
+			uint32 _ = 0;
+
+			// 0: Basic information
+			uint32 maxBasicLeaf = 0;
+			cpuid(0, 0, &maxBasicLeaf, &_, &_, &_);
+
+			if (maxBasicLeaf < 1)
+			{
+			    // Earlier Intel 486, CPUID not implemented
+			    return;
+			}
+
+			// 1: Processor Info and Feature Bits
+			uint32 procInfoEcx = 0;
+			uint32 procInfoEdx = 0;
+			cpuid(1, 0, &_, &_, &procInfoEcx, &procInfoEdx);
+
+			sFeatures.SSE = (procInfoEdx & (1 << 25)) != 0;
+			sFeatures.SSE2 = (procInfoEdx & (1 << 26)) != 0;
+
+			// 7: Extended Features
+			uint32 extendedFeaturesEbx = 0;
+			cpuid(7, 0, &_, &extendedFeaturesEbx, &_, &_);
+
+			// `XSAVE` and `AVX` support:
+			if ((procInfoEcx & (1 << 26)) != 0)
+			{
+			    // Here the CPU supports `XSAVE`
+
+			    // Detect `OSXSAVE`, that is, whether the OS is AVX enabled and
+			    // supports saving the state of the AVX/AVX2 vector registers on
+			    // context-switches
+			    if ((procInfoEcx & (1 << 27)) != 0)
+				{
+			        // The OS must have signaled the CPU that it supports saving and restoring the
+			        uint64 xcr0 = xgetbv(0);
+
+			        bool avxSupport = (xcr0 & 6) == 6;
+			        bool avx512Support = (xcr0 & 224) == 224;
+
+			        // Only if the OS and the CPU support saving/restoring the AVX registers we enable `xsave` support
+			        if (avxSupport)
+					{
+			            sFeatures.AVX = (procInfoEcx & (1 << 28)) != 0;
+			            sFeatures.AVX2 = (extendedFeaturesEbx & (1 << 5)) != 0;
+
+			            // For AVX-512 the OS also needs to support saving/restoring
+			            // the extended state, only then we enable AVX-512 support:
+			            if (avx512Support)
+			                sFeatures.AVX512 = (extendedFeaturesEbx & (1 << 16)) != 0;
+			        }
+			    }
+			}
+		}
+
+		[Intrinsic("cpuid")]
+		private static extern void cpuid(uint32 leaf, uint32 subleaf, uint32* eax, uint32* ebx, uint32* ecx, uint32* edx);
+
+		[Intrinsic("xgetbv")]
+		private static extern uint64 xgetbv(uint32 xcr);
+#endif
 	}
 }

+ 2 - 0
IDEHelper/Compiler/BfIRBuilder.h

@@ -441,6 +441,7 @@ enum BfIRIntrinsic : uint8
 	BfIRIntrinsic_BSwap,
 	BfIRIntrinsic_Cast,
 	BfIRIntrinsic_Cos,
+	BfIRIntrinsic_Cpuid,
 	BfIRIntrinsic_DebugTrap,
 	BfIRIntrinsic_Div,
 	BfIRIntrinsic_Eq,
@@ -477,6 +478,7 @@ enum BfIRIntrinsic : uint8
 	BfIRIntrinsic_VAArg,
 	BfIRIntrinsic_VAEnd,
 	BfIRIntrinsic_VAStart,
+	BfIRIntrinsic_Xgetbv,
 	BfIRIntrinsic_Xor,
 
 	BfIRIntrinsic_COUNT,

+ 61 - 12
IDEHelper/Compiler/BfIRCodeGen.cpp

@@ -157,6 +157,7 @@ static const BuiltinEntry gIntrinEntries[] =
 	{"bswap"},
 	{"cast"},
 	{"cos"},
+	{"cpuid"},
 	{"debugtrap"},
 	{"div"},
 	{"eq"},
@@ -193,6 +194,7 @@ static const BuiltinEntry gIntrinEntries[] =
 	{"va_arg"},
 	{"va_end"},
 	{"va_start"},
+	{"xgetbv"},
 	{"xor"},
 };
 
@@ -2844,6 +2846,7 @@ void BfIRCodeGen::HandleNextCmd()
 				{ llvm::Intrinsic::bswap, -1},
 				{ (llvm::Intrinsic::ID)-2, -1}, // cast,
 				{ llvm::Intrinsic::cos, 0, -1},
+				{ (llvm::Intrinsic::ID)-2, -1}, // cpuid
 				{ llvm::Intrinsic::debugtrap, -1}, // debugtrap,
 				{ (llvm::Intrinsic::ID)-2, -1}, // div
 				{ (llvm::Intrinsic::ID)-2, -1}, // eq
@@ -2880,6 +2883,7 @@ void BfIRCodeGen::HandleNextCmd()
 				{ (llvm::Intrinsic::ID)-2, -1}, // va_arg,
 				{ llvm::Intrinsic::vaend, -1}, // va_end,
 				{ llvm::Intrinsic::vastart, -1}, // va_start,
+				{ (llvm::Intrinsic::ID)-2, -1}, // xgetbv
 				{ (llvm::Intrinsic::ID)-2, -1}, // xor
 			};
 			BF_STATIC_ASSERT(BF_ARRAY_COUNT(intrinsics) == BfIRIntrinsic_COUNT);
@@ -3068,18 +3072,7 @@ void BfIRCodeGen::HandleNextCmd()
 				{
 				case BfIRIntrinsic__PLATFORM:
 					{
-						if (intrinsicData->mName == "add_ps")
-						{
-							auto val0 = TryToVector(args[0], llvm::Type::getFloatTy(*mLLVMContext));
-							auto val1 = TryToVector(args[0], llvm::Type::getFloatTy(*mLLVMContext));
-							//SetResult(curId, TryToVector(mIRBuilder->CreateFAdd(val0, val1), GetElemType(args[0])));
-
-							SetResult(curId, mIRBuilder->CreateFAdd(val0, val1));
-						}
-						else
-						{
-							FatalError(StrFormat("Unable to find intrinsic '%s'", intrinsicData->mName.c_str()));
-						}
+						FatalError(StrFormat("Unable to find intrinsic '%s'", intrinsicData->mName.c_str()));
 					}
 					break;
 
@@ -3299,6 +3292,62 @@ void BfIRCodeGen::HandleNextCmd()
 						}
 					}
 					break;
+				case BfIRIntrinsic_Cpuid:
+					{
+						llvm::Type* elemType = llvm::Type::getInt32Ty(*mLLVMContext);
+
+						// Check argument errors
+						if (args.size() != 6 || !args[0]->getType()->isIntegerTy(32) || !args[1]->getType()->isIntegerTy(32))
+							FatalError("Intrinsic argument error");
+
+						for (int i = 2; i < 6; i++)
+						{
+							llvm::Type* type = args[i]->getType();
+
+							if (!type->isPointerTy() || !type->getPointerElementType()->isIntegerTy(32))
+								FatalError("Intrinsic argument error");
+						}
+
+						// Get asm return type
+						llvm::SmallVector<llvm::Type*, 4> asmReturnTypes;
+						asmReturnTypes.push_back(elemType);
+						asmReturnTypes.push_back(elemType);
+						asmReturnTypes.push_back(elemType);
+						asmReturnTypes.push_back(elemType);
+
+						llvm::Type* returnType = llvm::StructType::get(*mLLVMContext, asmReturnTypes);
+
+						// Get asm function
+						llvm::SmallVector<llvm::Type*, 2> funcParams;
+						funcParams.push_back(elemType);
+						funcParams.push_back(elemType);
+
+						llvm::FunctionType* funcType = llvm::FunctionType::get(returnType, funcParams, false);
+						llvm::InlineAsm* func = llvm::InlineAsm::get(funcType, "xchgq %rbx,${1:q}\ncpuid\nxchgq %rbx,${1:q}", "={ax},=r,={cx},={dx},0,2,~{dirflag},~{fpsr},~{flags}", false);
+
+						// Call asm function
+						llvm::SmallVector<llvm::Value*, 2> funcArgs;
+						funcArgs.push_back(args[0]);
+						funcArgs.push_back(args[1]);
+
+						llvm::Value* asmResult = mIRBuilder->CreateCall(func, funcArgs);
+
+						// Store results
+						mIRBuilder->CreateStore(mIRBuilder->CreateExtractValue(asmResult, 0), args[2]);
+						mIRBuilder->CreateStore(mIRBuilder->CreateExtractValue(asmResult, 1), args[3]);
+						mIRBuilder->CreateStore(mIRBuilder->CreateExtractValue(asmResult, 2), args[4]);
+						mIRBuilder->CreateStore(mIRBuilder->CreateExtractValue(asmResult, 3), args[5]);
+					}
+					break;
+				case BfIRIntrinsic_Xgetbv:
+					{
+						if (args.size() != 1 || !args[0]->getType()->isIntegerTy(32))
+							FatalError("Intrinsic argument error");
+
+						auto func = mLLVMModule->getOrInsertFunction("llvm.x86.xgetbv", llvm::Type::getInt64Ty(*mLLVMContext), llvm::Type::getInt32Ty(*mLLVMContext));
+						SetResult(curId, mIRBuilder->CreateCall(func, args[0]));
+					}
+					break;
 				case BfIRIntrinsic_Not:
 					{
 						auto val0 = TryToVector(args[0]);