1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 19:48:20 +02:00

Add Runtime.Features to detect SIMD instruction set

This commit is contained in:
MineGame159 2023-04-02 15:03:46 +02:00
parent 893beb432f
commit 7dad948f20
5 changed files with 160 additions and 14 deletions

View file

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

View file

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

View file

@ -6,6 +6,12 @@ using System.Collections;
namespace System namespace System
{ {
struct RuntimeFeatures
{
public bool SSE, SSE2;
public bool AVX, AVX2, AVX512;
}
[StaticInitPriority(101)] [StaticInitPriority(101)]
static class Runtime static class Runtime
{ {
@ -359,6 +365,9 @@ namespace System
static List<ErrorHandler> sErrorHandlers ~ DeleteContainerAndItems!(_); static List<ErrorHandler> sErrorHandlers ~ DeleteContainerAndItems!(_);
static bool sInsideErrorHandler; static bool sInsideErrorHandler;
static bool sQueriedFeatures = false;
static RuntimeFeatures sFeatures;
public static this() public static this()
{ {
BfRtCallbacks.sCallbacks.Init(); BfRtCallbacks.sCallbacks.Init();
@ -466,5 +475,91 @@ namespace System
} }
return .ContinueFailure; 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
} }
} }

View file

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

View file

@ -157,6 +157,7 @@ static const BuiltinEntry gIntrinEntries[] =
{"bswap"}, {"bswap"},
{"cast"}, {"cast"},
{"cos"}, {"cos"},
{"cpuid"},
{"debugtrap"}, {"debugtrap"},
{"div"}, {"div"},
{"eq"}, {"eq"},
@ -193,6 +194,7 @@ static const BuiltinEntry gIntrinEntries[] =
{"va_arg"}, {"va_arg"},
{"va_end"}, {"va_end"},
{"va_start"}, {"va_start"},
{"xgetbv"},
{"xor"}, {"xor"},
}; };
@ -2844,6 +2846,7 @@ void BfIRCodeGen::HandleNextCmd()
{ llvm::Intrinsic::bswap, -1}, { llvm::Intrinsic::bswap, -1},
{ (llvm::Intrinsic::ID)-2, -1}, // cast, { (llvm::Intrinsic::ID)-2, -1}, // cast,
{ llvm::Intrinsic::cos, 0, -1}, { llvm::Intrinsic::cos, 0, -1},
{ (llvm::Intrinsic::ID)-2, -1}, // cpuid
{ llvm::Intrinsic::debugtrap, -1}, // debugtrap, { llvm::Intrinsic::debugtrap, -1}, // debugtrap,
{ (llvm::Intrinsic::ID)-2, -1}, // div { (llvm::Intrinsic::ID)-2, -1}, // div
{ (llvm::Intrinsic::ID)-2, -1}, // eq { (llvm::Intrinsic::ID)-2, -1}, // eq
@ -2880,6 +2883,7 @@ void BfIRCodeGen::HandleNextCmd()
{ (llvm::Intrinsic::ID)-2, -1}, // va_arg, { (llvm::Intrinsic::ID)-2, -1}, // va_arg,
{ llvm::Intrinsic::vaend, -1}, // va_end, { llvm::Intrinsic::vaend, -1}, // va_end,
{ llvm::Intrinsic::vastart, -1}, // va_start, { llvm::Intrinsic::vastart, -1}, // va_start,
{ (llvm::Intrinsic::ID)-2, -1}, // xgetbv
{ (llvm::Intrinsic::ID)-2, -1}, // xor { (llvm::Intrinsic::ID)-2, -1}, // xor
}; };
BF_STATIC_ASSERT(BF_ARRAY_COUNT(intrinsics) == BfIRIntrinsic_COUNT); BF_STATIC_ASSERT(BF_ARRAY_COUNT(intrinsics) == BfIRIntrinsic_COUNT);
@ -3067,20 +3071,9 @@ void BfIRCodeGen::HandleNextCmd()
switch (intrinsicData->mIntrinsic) switch (intrinsicData->mIntrinsic)
{ {
case BfIRIntrinsic__PLATFORM: 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; break;
case BfIRIntrinsic_Add: case BfIRIntrinsic_Add:
@ -3299,6 +3292,62 @@ void BfIRCodeGen::HandleNextCmd()
} }
} }
break; 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: case BfIRIntrinsic_Not:
{ {
auto val0 = TryToVector(args[0]); auto val0 = TryToVector(args[0]);