-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[Clang][AArch64] Generalise streaming mode checks for builtins. #93802
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
8a03acc
e30f1ce
46713b1
9170603
d0fd24f
e7d0128
2634350
286d81c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -559,31 +559,86 @@ SemaARM::ArmStreamingType getArmStreamingFnType(const FunctionDecl *FD) { | |
return SemaARM::ArmNonStreaming; | ||
} | ||
|
||
static void checkArmStreamingBuiltin(Sema &S, CallExpr *TheCall, | ||
const FunctionDecl *FD, | ||
SemaARM::ArmStreamingType BuiltinType) { | ||
static bool checkArmStreamingBuiltin(Sema &S, CallExpr *TheCall, | ||
FunctionDecl *FD, | ||
SemaARM::ArmStreamingType BuiltinType, | ||
unsigned BuiltinID) { | ||
SemaARM::ArmStreamingType FnType = getArmStreamingFnType(FD); | ||
if (BuiltinType == SemaARM::ArmStreamingOrSVE2p1) { | ||
// Check intrinsics that are available in [sve2p1 or sme/sme2]. | ||
llvm::StringMap<bool> CallerFeatureMap; | ||
S.Context.getFunctionFeatureMap(CallerFeatureMap, FD); | ||
if (Builtin::evaluateRequiredTargetFeatures("sve2p1", CallerFeatureMap)) | ||
BuiltinType = SemaARM::ArmStreamingCompatible; | ||
else | ||
|
||
// Check if the intrinsic is available in the right mode, i.e. | ||
// * When compiling for SME only, the caller must be in streaming mode. | ||
// * When compiling for SVE only, the caller must be in non-streaming mode. | ||
// * When compiling for both SVE and SME, the caller can be in either mode. | ||
if (BuiltinType == SemaARM::VerifyRuntimeMode) { | ||
static llvm::StringMap<bool> CallerFeatureMapWithoutSVE, | ||
CallerFeatureMapWithoutSME; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I hope I'm wrong but I think the use of static here is almost certainly bad because there's nothing stopping multiple threads from calling Random Idea: Of course you could just remove the cache and leave compile time as a worry for tomorrow. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right, not sure what I was thinking here with the 'static' variable :) I'd rather not go down the route of parsing the BuiltinTargetGuards here or making assumptions on the format if we're not going to ensure this format in the arm_sve.td file. For example, one can write |
||
|
||
// Cache the feature maps, to avoid having to recalculate this for each | ||
// builtin call. | ||
static unsigned CachedODRHash = 0; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As above. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
if (FD->getODRHash() != CachedODRHash) { | ||
auto DisableFeatures = [](llvm::StringMap<bool> &Map, StringRef S) { | ||
for (StringRef K : Map.keys()) | ||
if (K.starts_with(S)) | ||
Map[K] = false; | ||
}; | ||
|
||
CallerFeatureMapWithoutSME.clear(); | ||
S.Context.getFunctionFeatureMap(CallerFeatureMapWithoutSME, FD); | ||
DisableFeatures(CallerFeatureMapWithoutSME, "sme"); | ||
|
||
CallerFeatureMapWithoutSVE.clear(); | ||
S.Context.getFunctionFeatureMap(CallerFeatureMapWithoutSVE, FD); | ||
DisableFeatures(CallerFeatureMapWithoutSVE, "sve"); | ||
|
||
CachedODRHash = FD->getODRHash(); | ||
} | ||
|
||
// Avoid emitting diagnostics for a function that can never compile. | ||
if (FnType == SemaARM::ArmStreaming && !CallerFeatureMapWithoutSVE["sme"]) | ||
return false; | ||
|
||
// We know the builtin requires either some combination of SVE flags, or | ||
// some combination of SME flags, but we need to figure out which part | ||
// of the required features is satisfied by the target features. | ||
// | ||
// For a builtin with target guard 'sve2p1|sme2', if we compile with | ||
// '+sve2p1,+sme', then we know that it satisfies the 'sve2p1' part if we | ||
// evaluate the features for '+sve2p1,+sme,+nosme'. | ||
// | ||
// Similarly, if we compile with '+sve2,+sme2', then we know it satisfies | ||
// the 'sme2' part if we evaluate the features for '+sve2,+sme2,+nosve'. | ||
StringRef BuiltinTargetGuards( | ||
S.Context.BuiltinInfo.getRequiredFeatures(BuiltinID)); | ||
bool SatisfiesSVE = Builtin::evaluateRequiredTargetFeatures( | ||
BuiltinTargetGuards, CallerFeatureMapWithoutSME); | ||
bool SatisfiesSME = Builtin::evaluateRequiredTargetFeatures( | ||
BuiltinTargetGuards, CallerFeatureMapWithoutSVE); | ||
|
||
if ((SatisfiesSVE && SatisfiesSME) || | ||
(SatisfiesSVE && FnType == SemaARM::ArmStreamingCompatible)) | ||
return false; | ||
else if (SatisfiesSVE) | ||
BuiltinType = SemaARM::ArmNonStreaming; | ||
else if (SatisfiesSME) | ||
BuiltinType = SemaARM::ArmStreaming; | ||
else | ||
// This should be diagnosed by CodeGen | ||
return false; | ||
} | ||
|
||
if (FnType == SemaARM::ArmStreaming && | ||
if (FnType != SemaARM::ArmNonStreaming && | ||
BuiltinType == SemaARM::ArmNonStreaming) | ||
S.Diag(TheCall->getBeginLoc(), diag::warn_attribute_arm_sm_incompat_builtin) | ||
<< TheCall->getSourceRange() << "streaming"; | ||
else if (FnType == SemaARM::ArmNonStreaming && BuiltinType == SemaARM::ArmStreaming) | ||
S.Diag(TheCall->getBeginLoc(), diag::warn_attribute_arm_sm_incompat_builtin) | ||
S.Diag(TheCall->getBeginLoc(), diag::err_attribute_arm_sm_incompat_builtin) | ||
<< TheCall->getSourceRange() << "non-streaming"; | ||
else if (FnType == SemaARM::ArmStreamingCompatible && | ||
BuiltinType != SemaARM::ArmStreamingCompatible) | ||
S.Diag(TheCall->getBeginLoc(), diag::warn_attribute_arm_sm_incompat_builtin) | ||
<< TheCall->getSourceRange() << "streaming compatible"; | ||
else if (FnType != SemaARM::ArmStreaming && | ||
BuiltinType == SemaARM::ArmStreaming) | ||
S.Diag(TheCall->getBeginLoc(), diag::err_attribute_arm_sm_incompat_builtin) | ||
<< TheCall->getSourceRange() << "streaming"; | ||
else | ||
return false; | ||
|
||
return true; | ||
} | ||
|
||
static bool hasArmZAState(const FunctionDecl *FD) { | ||
|
@@ -612,7 +667,9 @@ static ArmSMEState getSMEState(unsigned BuiltinID) { | |
|
||
bool SemaARM::CheckSMEBuiltinFunctionCall(unsigned BuiltinID, | ||
CallExpr *TheCall) { | ||
if (const FunctionDecl *FD = SemaRef.getCurFunctionDecl()) { | ||
bool HasError = false; | ||
|
||
if (FunctionDecl *FD = SemaRef.getCurFunctionDecl()) { | ||
std::optional<ArmStreamingType> BuiltinType; | ||
|
||
switch (BuiltinID) { | ||
|
@@ -622,7 +679,8 @@ bool SemaARM::CheckSMEBuiltinFunctionCall(unsigned BuiltinID, | |
} | ||
|
||
if (BuiltinType) | ||
checkArmStreamingBuiltin(SemaRef, TheCall, FD, *BuiltinType); | ||
HasError |= checkArmStreamingBuiltin(SemaRef, TheCall, FD, *BuiltinType, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be wrong to return immediately? I ask because there's
which should be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
BuiltinID); | ||
|
||
if ((getSMEState(BuiltinID) & ArmZAMask) && !hasArmZAState(FD)) | ||
Diag(TheCall->getBeginLoc(), | ||
|
@@ -646,12 +704,14 @@ bool SemaARM::CheckSMEBuiltinFunctionCall(unsigned BuiltinID, | |
#undef GET_SME_IMMEDIATE_CHECK | ||
} | ||
|
||
return ParseSVEImmChecks(TheCall, ImmChecks); | ||
HasError |= ParseSVEImmChecks(TheCall, ImmChecks); | ||
return HasError; | ||
} | ||
|
||
bool SemaARM::CheckSVEBuiltinFunctionCall(unsigned BuiltinID, | ||
CallExpr *TheCall) { | ||
if (const FunctionDecl *FD = SemaRef.getCurFunctionDecl()) { | ||
bool HasError = false; | ||
if (FunctionDecl *FD = SemaRef.getCurFunctionDecl()) { | ||
std::optional<ArmStreamingType> BuiltinType; | ||
|
||
switch (BuiltinID) { | ||
|
@@ -660,7 +720,8 @@ bool SemaARM::CheckSVEBuiltinFunctionCall(unsigned BuiltinID, | |
#undef GET_SVE_STREAMING_ATTRS | ||
} | ||
if (BuiltinType) | ||
checkArmStreamingBuiltin(SemaRef, TheCall, FD, *BuiltinType); | ||
HasError |= checkArmStreamingBuiltin(SemaRef, TheCall, FD, *BuiltinType, | ||
BuiltinID); | ||
} | ||
// Range check SVE intrinsics that take immediate values. | ||
SmallVector<std::tuple<int, int, int>, 3> ImmChecks; | ||
|
@@ -673,13 +734,15 @@ bool SemaARM::CheckSVEBuiltinFunctionCall(unsigned BuiltinID, | |
#undef GET_SVE_IMMEDIATE_CHECK | ||
} | ||
|
||
return ParseSVEImmChecks(TheCall, ImmChecks); | ||
HasError |= ParseSVEImmChecks(TheCall, ImmChecks); | ||
return HasError; | ||
} | ||
|
||
bool SemaARM::CheckNeonBuiltinFunctionCall(const TargetInfo &TI, | ||
unsigned BuiltinID, | ||
CallExpr *TheCall) { | ||
if (const FunctionDecl *FD = SemaRef.getCurFunctionDecl()) { | ||
bool HasError = false; | ||
if (FunctionDecl *FD = SemaRef.getCurFunctionDecl()) { | ||
|
||
switch (BuiltinID) { | ||
default: | ||
|
@@ -688,7 +751,8 @@ bool SemaARM::CheckNeonBuiltinFunctionCall(const TargetInfo &TI, | |
#define TARGET_BUILTIN(id, ...) case NEON::BI##id: | ||
#define BUILTIN(id, ...) case NEON::BI##id: | ||
#include "clang/Basic/arm_neon.inc" | ||
checkArmStreamingBuiltin(SemaRef, TheCall, FD, ArmNonStreaming); | ||
HasError |= checkArmStreamingBuiltin(SemaRef, TheCall, FD, | ||
ArmNonStreaming, BuiltinID); | ||
break; | ||
#undef TARGET_BUILTIN | ||
#undef BUILTIN | ||
|
@@ -753,14 +817,15 @@ bool SemaARM::CheckNeonBuiltinFunctionCall(const TargetInfo &TI, | |
unsigned i = 0, l = 0, u = 0; | ||
switch (BuiltinID) { | ||
default: | ||
return false; | ||
return HasError; | ||
#define GET_NEON_IMMEDIATE_CHECK | ||
#include "clang/Basic/arm_fp16.inc" | ||
#include "clang/Basic/arm_neon.inc" | ||
#undef GET_NEON_IMMEDIATE_CHECK | ||
} | ||
|
||
return SemaRef.BuiltinConstantArgRange(TheCall, i, l, u + l); | ||
HasError |= SemaRef.BuiltinConstantArgRange(TheCall, i, l, u + l); | ||
return HasError; | ||
} | ||
|
||
bool SemaARM::CheckMVEBuiltinFunctionCall(unsigned BuiltinID, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can
FD
's constness be restored? I think you only had to remove it because you previously calledgetODRHash
.