Skip to content

Commit 1fd196c

Browse files
authored
[AArch64] Diagnose more functions when FP not enabled (#90832)
When using a hard-float ABI for a target without FP registers, it's not possible to correctly generate code for functions with arguments which must be passed in floating-point registers. This is diagnosed in CodeGen instead of Sema, to more closely match GCC's behaviour around inline functions, which is relied on by the Linux kernel. Previously, this only checked function signatures as they were code-generated, but this missed some cases: * Calls to functions not defined in this translation unit. * Calls through function pointers. * Calls to variadic functions, where the variadic arguments have a floating-point type. This adds checks to function calls, as well as definitions, so that these cases are correctly diagnosed.
1 parent 1530f31 commit 1fd196c

File tree

5 files changed

+114
-32
lines changed

5 files changed

+114
-32
lines changed

clang/lib/CodeGen/CGCall.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5050,13 +5050,14 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
50505050
(TargetDecl->hasAttr<TargetAttr>() ||
50515051
(CurFuncDecl && CurFuncDecl->hasAttr<TargetAttr>())))
50525052
checkTargetFeatures(Loc, FD);
5053-
5054-
// Some architectures (such as x86-64) have the ABI changed based on
5055-
// attribute-target/features. Give them a chance to diagnose.
5056-
CGM.getTargetCodeGenInfo().checkFunctionCallABI(
5057-
CGM, Loc, dyn_cast_or_null<FunctionDecl>(CurCodeDecl), FD, CallArgs);
50585053
}
50595054

5055+
// Some architectures (such as x86-64) have the ABI changed based on
5056+
// attribute-target/features. Give them a chance to diagnose.
5057+
CGM.getTargetCodeGenInfo().checkFunctionCallABI(
5058+
CGM, Loc, dyn_cast_or_null<FunctionDecl>(CurCodeDecl),
5059+
dyn_cast_or_null<FunctionDecl>(TargetDecl), CallArgs, RetTy);
5060+
50605061
// 1. Set up the arguments.
50615062

50625063
// If we're using inalloca, insert the allocation after the stack save.

clang/lib/CodeGen/TargetInfo.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ class TargetCodeGenInfo {
9494
virtual void checkFunctionCallABI(CodeGenModule &CGM, SourceLocation CallLoc,
9595
const FunctionDecl *Caller,
9696
const FunctionDecl *Callee,
97-
const CallArgList &Args) const {}
97+
const CallArgList &Args,
98+
QualType ReturnType) const {}
9899

99100
/// Determines the size of struct _Unwind_Exception on this platform,
100101
/// in 8-bit units. The Itanium ABI defines this as:

clang/lib/CodeGen/Targets/AArch64.cpp

Lines changed: 71 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,22 @@ class AArch64TargetCodeGenInfo : public TargetCodeGenInfo {
170170

171171
void checkFunctionCallABI(CodeGenModule &CGM, SourceLocation CallLoc,
172172
const FunctionDecl *Caller,
173-
const FunctionDecl *Callee,
174-
const CallArgList &Args) const override;
173+
const FunctionDecl *Callee, const CallArgList &Args,
174+
QualType ReturnType) const override;
175+
176+
private:
177+
// Diagnose calls between functions with incompatible Streaming SVE
178+
// attributes.
179+
void checkFunctionCallABIStreaming(CodeGenModule &CGM, SourceLocation CallLoc,
180+
const FunctionDecl *Caller,
181+
const FunctionDecl *Callee) const;
182+
// Diagnose calls which must pass arguments in floating-point registers when
183+
// the selected target does not have floating-point registers.
184+
void checkFunctionCallABISoftFloat(CodeGenModule &CGM, SourceLocation CallLoc,
185+
const FunctionDecl *Caller,
186+
const FunctionDecl *Callee,
187+
const CallArgList &Args,
188+
QualType ReturnType) const;
175189
};
176190

177191
class WindowsAArch64TargetCodeGenInfo : public AArch64TargetCodeGenInfo {
@@ -853,37 +867,42 @@ static bool isStreamingCompatible(const FunctionDecl *F) {
853867
return false;
854868
}
855869

870+
// Report an error if an argument or return value of type Ty would need to be
871+
// passed in a floating-point register.
872+
static void diagnoseIfNeedsFPReg(DiagnosticsEngine &Diags,
873+
const StringRef ABIName,
874+
const AArch64ABIInfo &ABIInfo,
875+
const QualType &Ty, const NamedDecl *D) {
876+
const Type *HABase = nullptr;
877+
uint64_t HAMembers = 0;
878+
if (Ty->isFloatingType() || Ty->isVectorType() ||
879+
ABIInfo.isHomogeneousAggregate(Ty, HABase, HAMembers)) {
880+
Diags.Report(D->getLocation(), diag::err_target_unsupported_type_for_abi)
881+
<< D->getDeclName() << Ty << ABIName;
882+
}
883+
}
884+
885+
// If we are using a hard-float ABI, but do not have floating point registers,
886+
// then report an error for any function arguments or returns which would be
887+
// passed in floating-pint registers.
856888
void AArch64TargetCodeGenInfo::checkFunctionABI(
857889
CodeGenModule &CGM, const FunctionDecl *FuncDecl) const {
858890
const AArch64ABIInfo &ABIInfo = getABIInfo<AArch64ABIInfo>();
859891
const TargetInfo &TI = ABIInfo.getContext().getTargetInfo();
860892

861-
// If we are using a hard-float ABI, but do not have floating point
862-
// registers, then report an error for any function arguments or returns
863-
// which would be passed in floating-pint registers.
864-
auto CheckType = [&CGM, &TI, &ABIInfo](const QualType &Ty,
865-
const NamedDecl *D) {
866-
const Type *HABase = nullptr;
867-
uint64_t HAMembers = 0;
868-
if (Ty->isFloatingType() || Ty->isVectorType() ||
869-
ABIInfo.isHomogeneousAggregate(Ty, HABase, HAMembers)) {
870-
CGM.getDiags().Report(D->getLocation(),
871-
diag::err_target_unsupported_type_for_abi)
872-
<< D->getDeclName() << Ty << TI.getABI();
873-
}
874-
};
875-
876893
if (!TI.hasFeature("fp") && !ABIInfo.isSoftFloat()) {
877-
CheckType(FuncDecl->getReturnType(), FuncDecl);
894+
diagnoseIfNeedsFPReg(CGM.getDiags(), TI.getABI(), ABIInfo,
895+
FuncDecl->getReturnType(), FuncDecl);
878896
for (ParmVarDecl *PVD : FuncDecl->parameters()) {
879-
CheckType(PVD->getType(), PVD);
897+
diagnoseIfNeedsFPReg(CGM.getDiags(), TI.getABI(), ABIInfo, PVD->getType(),
898+
PVD);
880899
}
881900
}
882901
}
883902

884-
void AArch64TargetCodeGenInfo::checkFunctionCallABI(
903+
void AArch64TargetCodeGenInfo::checkFunctionCallABIStreaming(
885904
CodeGenModule &CGM, SourceLocation CallLoc, const FunctionDecl *Caller,
886-
const FunctionDecl *Callee, const CallArgList &Args) const {
905+
const FunctionDecl *Callee) const {
887906
if (!Caller || !Callee || !Callee->hasAttr<AlwaysInlineAttr>())
888907
return;
889908

@@ -903,6 +922,37 @@ void AArch64TargetCodeGenInfo::checkFunctionCallABI(
903922
<< Callee->getDeclName();
904923
}
905924

925+
// If the target does not have floating-point registers, but we are using a
926+
// hard-float ABI, there is no way to pass floating-point, vector or HFA values
927+
// to functions, so we report an error.
928+
void AArch64TargetCodeGenInfo::checkFunctionCallABISoftFloat(
929+
CodeGenModule &CGM, SourceLocation CallLoc, const FunctionDecl *Caller,
930+
const FunctionDecl *Callee, const CallArgList &Args,
931+
QualType ReturnType) const {
932+
const AArch64ABIInfo &ABIInfo = getABIInfo<AArch64ABIInfo>();
933+
const TargetInfo &TI = ABIInfo.getContext().getTargetInfo();
934+
935+
if (!Caller || TI.hasFeature("fp") || ABIInfo.isSoftFloat())
936+
return;
937+
938+
diagnoseIfNeedsFPReg(CGM.getDiags(), TI.getABI(), ABIInfo, ReturnType,
939+
Caller);
940+
941+
for (const CallArg &Arg : Args)
942+
diagnoseIfNeedsFPReg(CGM.getDiags(), TI.getABI(), ABIInfo, Arg.getType(),
943+
Caller);
944+
}
945+
946+
void AArch64TargetCodeGenInfo::checkFunctionCallABI(CodeGenModule &CGM,
947+
SourceLocation CallLoc,
948+
const FunctionDecl *Caller,
949+
const FunctionDecl *Callee,
950+
const CallArgList &Args,
951+
QualType ReturnType) const {
952+
checkFunctionCallABIStreaming(CGM, CallLoc, Caller, Callee);
953+
checkFunctionCallABISoftFloat(CGM, CallLoc, Caller, Callee, Args, ReturnType);
954+
}
955+
906956
void AArch64ABIInfo::appendAttributeMangling(TargetClonesAttr *Attr,
907957
unsigned Index,
908958
raw_ostream &Out) const {

clang/lib/CodeGen/Targets/X86.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1482,8 +1482,8 @@ class X86_64TargetCodeGenInfo : public TargetCodeGenInfo {
14821482

14831483
void checkFunctionCallABI(CodeGenModule &CGM, SourceLocation CallLoc,
14841484
const FunctionDecl *Caller,
1485-
const FunctionDecl *Callee,
1486-
const CallArgList &Args) const override;
1485+
const FunctionDecl *Callee, const CallArgList &Args,
1486+
QualType ReturnType) const override;
14871487
};
14881488
} // namespace
14891489

@@ -1558,9 +1558,15 @@ static bool checkAVXParam(DiagnosticsEngine &Diag, ASTContext &Ctx,
15581558
return false;
15591559
}
15601560

1561-
void X86_64TargetCodeGenInfo::checkFunctionCallABI(
1562-
CodeGenModule &CGM, SourceLocation CallLoc, const FunctionDecl *Caller,
1563-
const FunctionDecl *Callee, const CallArgList &Args) const {
1561+
void X86_64TargetCodeGenInfo::checkFunctionCallABI(CodeGenModule &CGM,
1562+
SourceLocation CallLoc,
1563+
const FunctionDecl *Caller,
1564+
const FunctionDecl *Callee,
1565+
const CallArgList &Args,
1566+
QualType ReturnType) const {
1567+
if (!Callee)
1568+
return;
1569+
15641570
llvm::StringMap<bool> CallerMap;
15651571
llvm::StringMap<bool> CalleeMap;
15661572
unsigned ArgIndex = 0;

clang/test/CodeGen/aarch64-soft-float-abi-errors.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,15 @@ inline void test_float_arg_inline(float a) {}
6969
inline void test_float_arg_inline_used(float a) {}
7070
// nofp-hard-opt-error@-1 {{'a' requires 'float' type support, but ABI 'aapcs' does not support it}}
7171
void use_inline() { test_float_arg_inline_used(1.0f); }
72+
// nofp-hard-error@-1 {{'use_inline' requires 'float' type support, but ABI 'aapcs' does not support it}}
7273

7374
// The always_inline attribute causes an inline function to always be
7475
// code-genned, even at -O0, so we always emit the error.
7576
__attribute((always_inline))
7677
inline void test_float_arg_always_inline_used(float a) {}
7778
// nofp-hard-error@-1 {{'a' requires 'float' type support, but ABI 'aapcs' does not support it}}
7879
void use_always_inline() { test_float_arg_always_inline_used(1.0f); }
80+
// nofp-hard-error@-1 {{'use_always_inline' requires 'float' type support, but ABI 'aapcs' does not support it}}
7981

8082
// Floating-point expressions, global variables and local variables do not
8183
// affect the ABI, so are allowed. GCC does reject some uses of floating point
@@ -97,3 +99,25 @@ int test_var_double(int a) {
9799
d *= 6.0;
98100
return (int)d;
99101
}
102+
103+
extern void extern_float_arg(float);
104+
extern float extern_float_ret(void);
105+
void call_extern_float_arg() { extern_float_arg(1.0f); }
106+
// nofp-hard-error@-1 {{'call_extern_float_arg' requires 'float' type support, but ABI 'aapcs' does not support it}}
107+
void call_extern_float_ret() { extern_float_ret(); }
108+
// nofp-hard-error@-1 {{'call_extern_float_ret' requires 'float' type support, but ABI 'aapcs' does not support it}}
109+
110+
// Definitions of variadic functions, and calls to them which only use integer
111+
// argument registers, are both fine.
112+
void variadic(int, ...);
113+
void call_variadic_int() { variadic(0, 1); }
114+
115+
// Calls to variadic functions with floating-point arguments are an error,
116+
// since this would require floating-point registers.
117+
void call_variadic_double() { variadic(0, 1.0); }
118+
// nofp-hard-error@-1 {{'call_variadic_double' requires 'double' type support, but ABI 'aapcs' does not support it}}
119+
120+
// Calls through function pointers are also diagnosed.
121+
void (*fptr)(float);
122+
void call_indirect() { fptr(1.0f); }
123+
// nofp-hard-error@-1 {{'call_indirect' requires 'float' type support, but ABI 'aapcs' does not support it}}

0 commit comments

Comments
 (0)