Skip to content

Commit 9cc98e3

Browse files
authoredFeb 15, 2024
[AArch64] Add soft-float ABI (#74460)
This adds support for the AArch64 soft-float ABI. The specification for this ABI was added by ARM-software/abi-aa#232. Because all existing AArch64 hardware has floating-point hardware, we expect this to be a niche option, only used for embedded systems on R-profile systems. We are going to document that SysV-like systems should only ever use the base (hard-float) PCS variant: ARM-software/abi-aa#233. For that reason, I've not added an option to select the ABI independently of the FPU hardware, instead the new ABI is enabled iff the target architecture does not have an FPU. For testing, I have run this through an ABI fuzzer, but since this is the first implementation it can only test for internal consistency (callers and callees agree on the PCS), not for conformance to the ABI spec.
1 parent 32fcfcd commit 9cc98e3

17 files changed

+404
-28
lines changed
 

‎clang/include/clang/Basic/DiagnosticCommonKinds.td

+2
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,8 @@ def warn_target_unrecognized_env : Warning<
356356
def warn_knl_knm_isa_support_removed : Warning<
357357
"KNL, KNM related Intel Xeon Phi CPU's specific ISA's supports will be removed in LLVM 19.">,
358358
InGroup<DiagGroup<"knl-knm-isa-support-removed">>;
359+
def err_target_unsupported_abi_with_fpu : Error<
360+
"'%0' ABI is not supported with FPU">;
359361

360362
// Source manager
361363
def err_cannot_open_file : Error<"cannot open file '%0': %1">, DefaultFatal;

‎clang/include/clang/Basic/DiagnosticSemaKinds.td

+2
Original file line numberDiff line numberDiff line change
@@ -11314,6 +11314,8 @@ def err_omp_wrong_dependency_iterator_type : Error<
1131411314
def err_target_unsupported_type
1131511315
: Error<"%0 requires %select{|%2 bit size}1 %3 %select{|return }4type support,"
1131611316
" but target '%5' does not support it">;
11317+
def err_target_unsupported_type_for_abi
11318+
: Error<"%0 requires %1 type support, but ABI '%2' does not support it">;
1131711319
def err_omp_lambda_capture_in_declare_target_not_to : Error<
1131811320
"variable captured in declare target region must appear in a to clause">;
1131911321
def err_omp_device_type_mismatch : Error<

‎clang/include/clang/Basic/TargetInfo.h

+8
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ class TargetInfo : public TransferrableTargetInfo,
231231
bool HasIbm128;
232232
bool HasLongDouble;
233233
bool HasFPReturn;
234+
bool HasFPTypes;
234235
bool HasStrictFP;
235236

236237
unsigned char MaxAtomicPromoteWidth, MaxAtomicInlineWidth;
@@ -689,6 +690,9 @@ class TargetInfo : public TransferrableTargetInfo,
689690
/// on this target.
690691
virtual bool hasFPReturn() const { return HasFPReturn; }
691692

693+
/// Determine whether floating point types are supported for this target.
694+
virtual bool hasFPTypes() const { return HasFPTypes; }
695+
692696
/// Determine whether constrained floating point is supported on this target.
693697
virtual bool hasStrictFP() const { return HasStrictFP; }
694698

@@ -1331,6 +1335,10 @@ class TargetInfo : public TransferrableTargetInfo,
13311335
return false;
13321336
}
13331337

1338+
/// Make changes to the supported types which depend on both the target
1339+
/// features and ABI.
1340+
virtual void setSupportedArgTypes() {}
1341+
13341342
/// Use the specified unit for FP math.
13351343
///
13361344
/// \return False on error (invalid unit name).

‎clang/lib/Basic/TargetInfo.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ TargetInfo::TargetInfo(const llvm::Triple &T) : Triple(T) {
6767
HasFullBFloat16 = false;
6868
HasLongDouble = true;
6969
HasFPReturn = true;
70+
HasFPTypes = true;
7071
HasStrictFP = false;
7172
PointerWidth = PointerAlign = 32;
7273
BoolWidth = BoolAlign = 8;

‎clang/lib/Basic/Targets.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,7 @@ TargetInfo::CreateTargetInfo(DiagnosticsEngine &Diags,
830830
Target->setSupportedOpenCLOpts();
831831
Target->setCommandLineOpenCLOpts();
832832
Target->setMaxAtomicWidth();
833+
Target->setSupportedArgTypes();
833834

834835
if (!Opts->DarwinTargetVariantTriple.empty())
835836
Target->DarwinTargetVariantTriple =

‎clang/lib/Basic/Targets/AArch64.cpp

+23-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "AArch64.h"
14+
#include "clang/Basic/Diagnostic.h"
1415
#include "clang/Basic/LangOptions.h"
1516
#include "clang/Basic/TargetBuiltins.h"
1617
#include "clang/Basic/TargetInfo.h"
@@ -199,13 +200,32 @@ AArch64TargetInfo::AArch64TargetInfo(const llvm::Triple &Triple,
199200
StringRef AArch64TargetInfo::getABI() const { return ABI; }
200201

201202
bool AArch64TargetInfo::setABI(const std::string &Name) {
202-
if (Name != "aapcs" && Name != "darwinpcs")
203+
if (Name != "aapcs" && Name != "aapcs-soft" && Name != "darwinpcs")
203204
return false;
204205

205206
ABI = Name;
206207
return true;
207208
}
208209

210+
void AArch64TargetInfo::setSupportedArgTypes() {
211+
if (!(FPU & FPUMode) && ABI != "aapcs-soft") {
212+
// When a hard-float ABI is used on a target without an FPU, all
213+
// floating-point argument and return types are rejected because they must
214+
// be passed in FP registers.
215+
HasFPTypes = false;
216+
}
217+
}
218+
219+
bool AArch64TargetInfo::validateTarget(DiagnosticsEngine &Diags) const {
220+
if (hasFeature("fp") && ABI == "aapcs-soft") {
221+
// aapcs-soft is not allowed for targets with an FPU, to avoid there being
222+
// two incomatible ABIs.
223+
Diags.Report(diag::err_target_unsupported_abi_with_fpu) << ABI;
224+
return false;
225+
}
226+
return true;
227+
}
228+
209229
bool AArch64TargetInfo::validateBranchProtection(StringRef Spec, StringRef,
210230
BranchProtectionInfo &BPI,
211231
StringRef &Err) const {
@@ -686,7 +706,8 @@ bool AArch64TargetInfo::hasFeature(StringRef Feature) const {
686706
return llvm::StringSwitch<bool>(Feature)
687707
.Cases("aarch64", "arm64", "arm", true)
688708
.Case("fmv", HasFMV)
689-
.Cases("neon", "fp", "simd", FPU & NeonMode)
709+
.Case("fp", FPU & FPUMode)
710+
.Cases("neon", "simd", FPU & NeonMode)
690711
.Case("jscvt", HasJSCVT)
691712
.Case("fcma", HasFCMA)
692713
.Case("rng", HasRandGen)

‎clang/lib/Basic/Targets/AArch64.h

+3
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ class LLVM_LIBRARY_VISIBILITY AArch64TargetInfo : public TargetInfo {
9696

9797
StringRef getABI() const override;
9898
bool setABI(const std::string &Name) override;
99+
void setSupportedArgTypes() override;
99100

100101
bool validateBranchProtection(StringRef Spec, StringRef Arch,
101102
BranchProtectionInfo &BPI,
@@ -199,6 +200,8 @@ class LLVM_LIBRARY_VISIBILITY AArch64TargetInfo : public TargetInfo {
199200
bool hasInt128Type() const override;
200201

201202
bool hasBitIntType() const override { return true; }
203+
204+
bool validateTarget(DiagnosticsEngine &Diags) const override;
202205
};
203206

204207
class LLVM_LIBRARY_VISIBILITY AArch64leTargetInfo : public AArch64TargetInfo {

‎clang/lib/CodeGen/CodeGenModule.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ createTargetCodeGenInfo(CodeGenModule &CGM) {
145145
Kind = AArch64ABIKind::DarwinPCS;
146146
else if (Triple.isOSWindows())
147147
return createWindowsAArch64TargetCodeGenInfo(CGM, AArch64ABIKind::Win64);
148+
else if (Target.getABI() == "aapcs-soft")
149+
Kind = AArch64ABIKind::AAPCSSoft;
148150

149151
return createAArch64TargetCodeGenInfo(CGM, Kind);
150152
}

‎clang/lib/CodeGen/TargetInfo.h

+1
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ enum class AArch64ABIKind {
416416
AAPCS = 0,
417417
DarwinPCS,
418418
Win64,
419+
AAPCSSoft,
419420
};
420421

421422
std::unique_ptr<TargetCodeGenInfo>

‎clang/lib/CodeGen/Targets/AArch64.cpp

+12-5
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ class AArch64ABIInfo : public ABIInfo {
5353
Address EmitDarwinVAArg(Address VAListAddr, QualType Ty,
5454
CodeGenFunction &CGF) const;
5555

56-
Address EmitAAPCSVAArg(Address VAListAddr, QualType Ty,
57-
CodeGenFunction &CGF) const;
56+
Address EmitAAPCSVAArg(Address VAListAddr, QualType Ty, CodeGenFunction &CGF,
57+
AArch64ABIKind Kind) const;
5858

5959
Address EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
6060
QualType Ty) const override {
@@ -65,7 +65,7 @@ class AArch64ABIInfo : public ABIInfo {
6565

6666
return Kind == AArch64ABIKind::Win64 ? EmitMSVAArg(CGF, VAListAddr, Ty)
6767
: isDarwinPCS() ? EmitDarwinVAArg(VAListAddr, Ty, CGF)
68-
: EmitAAPCSVAArg(VAListAddr, Ty, CGF);
68+
: EmitAAPCSVAArg(VAListAddr, Ty, CGF, Kind);
6969
}
7070

7171
Address EmitMSVAArg(CodeGenFunction &CGF, Address VAListAddr,
@@ -482,6 +482,11 @@ bool AArch64SwiftABIInfo::isLegalVectorType(CharUnits VectorSize,
482482
}
483483

484484
bool AArch64ABIInfo::isHomogeneousAggregateBaseType(QualType Ty) const {
485+
// For the soft-float ABI variant, no types are considered to be homogeneous
486+
// aggregates.
487+
if (Kind == AArch64ABIKind::AAPCSSoft)
488+
return false;
489+
485490
// Homogeneous aggregates for AAPCS64 must have base types of a floating
486491
// point type or a short-vector type. This is the same as the 32-bit ABI,
487492
// but with the difference that any floating-point type is allowed,
@@ -513,7 +518,8 @@ bool AArch64ABIInfo::isZeroLengthBitfieldPermittedInHomogeneousAggregate()
513518
}
514519

515520
Address AArch64ABIInfo::EmitAAPCSVAArg(Address VAListAddr, QualType Ty,
516-
CodeGenFunction &CGF) const {
521+
CodeGenFunction &CGF,
522+
AArch64ABIKind Kind) const {
517523
ABIArgInfo AI = classifyArgumentType(Ty, /*IsVariadic=*/true,
518524
CGF.CurFnInfo->getCallingConvention());
519525
// Empty records are ignored for parameter passing purposes.
@@ -538,7 +544,8 @@ Address AArch64ABIInfo::EmitAAPCSVAArg(Address VAListAddr, QualType Ty,
538544
BaseTy = ArrTy->getElementType();
539545
NumRegs = ArrTy->getNumElements();
540546
}
541-
bool IsFPR = BaseTy->isFloatingPointTy() || BaseTy->isVectorTy();
547+
bool IsFPR = Kind != AArch64ABIKind::AAPCSSoft &&
548+
(BaseTy->isFloatingPointTy() || BaseTy->isVectorTy());
542549

543550
// The AArch64 va_list type and handling is specified in the Procedure Call
544551
// Standard, section B.4:

‎clang/lib/Sema/Sema.cpp

+34
Original file line numberDiff line numberDiff line change
@@ -1944,6 +1944,21 @@ Sema::SemaDiagnosticBuilder Sema::Diag(SourceLocation Loc, unsigned DiagID,
19441944
return DB;
19451945
}
19461946

1947+
static bool typeIsOrContainsFloat(const Type &Ty) {
1948+
if (Ty.isFloatingType())
1949+
return true;
1950+
1951+
if (const RecordDecl *Decl = Ty.getAsRecordDecl()) {
1952+
for (const FieldDecl *FD : Decl->fields()) {
1953+
const Type &FieldType = *FD->getType();
1954+
if (typeIsOrContainsFloat(FieldType))
1955+
return true;
1956+
}
1957+
}
1958+
1959+
return false;
1960+
}
1961+
19471962
void Sema::checkTypeSupport(QualType Ty, SourceLocation Loc, ValueDecl *D) {
19481963
if (isUnevaluatedContext() || Ty.isNull())
19491964
return;
@@ -2088,6 +2103,25 @@ void Sema::checkTypeSupport(QualType Ty, SourceLocation Loc, ValueDecl *D) {
20882103
!Builtin::evaluateRequiredTargetFeatures("sme", CallerFeatureMap))
20892104
Diag(D->getLocation(), diag::err_sve_vector_in_non_sve_target) << Ty;
20902105
}
2106+
2107+
// Don't allow any floating-point types (including structs containing
2108+
// floats) for ABIs which do not support them.
2109+
if (!TI.hasFPTypes() && typeIsOrContainsFloat(*UnqualTy)) {
2110+
PartialDiagnostic PD = PDiag(diag::err_target_unsupported_type_for_abi);
2111+
2112+
if (D)
2113+
PD << D;
2114+
else
2115+
PD << "expression";
2116+
2117+
if (Diag(Loc, PD, FD) << Ty << TI.getABI()) {
2118+
if (D)
2119+
D->setInvalidDecl();
2120+
}
2121+
2122+
if (D)
2123+
targetDiag(D->getLocation(), diag::note_defined_here, FD) << D;
2124+
}
20912125
};
20922126

20932127
CheckType(Ty);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// RUN: %clang_cc1 -triple aarch64 -target-feature +fp-armv8 -target-abi aapcs -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,HARD
2+
// RUN: %clang_cc1 -triple aarch64 -target-feature -fp-armv8 -target-abi aapcs-soft -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,SOFT
3+
4+
// See also llvm/test/CodeGen/AArch64/soft-float-abi.ll, which checks the LLVM
5+
// backend parts of the soft-float ABI.
6+
7+
// The va_list type does not change between the ABIs
8+
// CHECK: %struct.__va_list = type { ptr, ptr, ptr, i32, i32 }
9+
10+
// Floats are passed in integer registers, this will be handled by the backend.
11+
// CHECK: define dso_local half @test0(half noundef %a)
12+
// CHECK: define dso_local bfloat @test1(bfloat noundef %a)
13+
// CHECK: define dso_local float @test2(float noundef %a)
14+
// CHECK: define dso_local double @test3(double noundef %a)
15+
// CHECK: define dso_local fp128 @test4(fp128 noundef %a)
16+
__fp16 test0(__fp16 a) { return a; }
17+
__bf16 test1(__bf16 a) { return a; }
18+
float test2(float a) { return a; }
19+
double test3(double a) { return a; }
20+
long double test4(long double a) { return a; }
21+
22+
// No types are considered to be HFAs or HVAs by the soft-float PCS, so these
23+
// are converted to integer types.
24+
struct A {
25+
float x;
26+
};
27+
// SOFT: define dso_local i32 @test10(i64 %a.coerce)
28+
// HARD: define dso_local %struct.A @test10([1 x float] alignstack(8) %a.coerce)
29+
struct A test10(struct A a) { return a; }
30+
31+
struct B {
32+
double x;
33+
double y;
34+
};
35+
// SOFT: define dso_local [2 x i64] @test11([2 x i64] %a.coerce)
36+
// HARD: define dso_local %struct.B @test11([2 x double] alignstack(8) %a.coerce)
37+
struct B test11(struct B a) { return a; }
38+
39+
#include <stdarg.h>
40+
41+
// For variadic arguments, va_arg will always retreive
42+
// CHECK-LABEL: define dso_local double @test20(i32 noundef %a, ...)
43+
// CHECK: %vl = alloca %struct.__va_list, align 8
44+
// SOFT: %gr_offs_p = getelementptr inbounds %struct.__va_list, ptr %vl, i32 0, i32 3
45+
// SOFT: %reg_top_p = getelementptr inbounds %struct.__va_list, ptr %vl, i32 0, i32 1
46+
// HARD: %vr_offs_p = getelementptr inbounds %struct.__va_list, ptr %vl, i32 0, i32 4
47+
// HARD: %reg_top_p = getelementptr inbounds %struct.__va_list, ptr %vl, i32 0, i32 2
48+
double test20(int a, ...) {
49+
va_list vl;
50+
va_start(vl, a);
51+
return va_arg(vl, double);
52+
}
53+
54+
// Vector types are only available for targets with the correct hardware, and
55+
// their calling-convention is left undefined by the soft-float ABI, so they
56+
// aren't tested here.

‎clang/test/CodeGen/attr-target-clones-aarch64.c

+18-18
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-attributes --check-globals --include-generated-funcs
2-
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -S -emit-llvm -o - %s | FileCheck %s
3-
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature -fmv -S -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-NOFMV
2+
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature -fp-armv8 -S -emit-llvm -o - %s | FileCheck %s
3+
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature -fp-armv8 -target-feature -fmv -S -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-NOFMV
44

55
int __attribute__((target_clones("lse+aes", "sve2"))) ftc(void) { return 0; }
66
int __attribute__((target_clones("sha2", "sha2+memtag2", " default "))) ftc_def(void) { return 1; }
@@ -414,23 +414,23 @@ inline int __attribute__((target_clones("fp16", "sve2-bitperm+fcma", "default"))
414414
// CHECK-NOFMV-NEXT: ret i32 [[ADD5]]
415415
//
416416
//.
417-
// CHECK: attributes #[[ATTR0:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+fp-armv8,+lse,+neon" }
418-
// CHECK: attributes #[[ATTR1:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+fp-armv8,+fullfp16,+neon,+sve,+sve2" }
419-
// CHECK: attributes #[[ATTR2:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
420-
// CHECK: attributes #[[ATTR3:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+fp-armv8,+neon,+sha2" }
421-
// CHECK: attributes #[[ATTR4:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+fp-armv8,+mte,+neon,+sha2" }
422-
// CHECK: attributes #[[ATTR5:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+fp-armv8,+neon" }
423-
// CHECK: attributes #[[ATTR6:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+crc,+dotprod,+fp-armv8,+neon" }
424-
// CHECK: attributes #[[ATTR7:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+fp-armv8,+neon,+rand" }
425-
// CHECK: attributes #[[ATTR8:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+predres,+rcpc" }
426-
// CHECK: attributes #[[ATTR9:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+fp-armv8,+fullfp16,+neon,+sve,+sve2,+sve2-aes,+wfxt" }
427-
// CHECK: attributes #[[ATTR10:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+fp-armv8,+fullfp16,+neon" }
428-
// CHECK: attributes #[[ATTR11:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+complxnum,+fp-armv8,+fullfp16,+neon,+sve,+sve2,+sve2-bitperm" }
429-
// CHECK: attributes #[[ATTR12:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bti" }
430-
// CHECK: attributes #[[ATTR13:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+fp-armv8,+fullfp16,+neon,+sb,+sve" }
417+
// CHECK: attributes #[[ATTR0:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+lse,+neon,-fp-armv8" }
418+
// CHECK: attributes #[[ATTR1:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+fullfp16,+neon,+sve,+sve2,-fp-armv8" }
419+
// CHECK: attributes #[[ATTR2:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="-fp-armv8" }
420+
// CHECK: attributes #[[ATTR3:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+neon,+sha2,-fp-armv8" }
421+
// CHECK: attributes #[[ATTR4:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+mte,+neon,+sha2,-fp-armv8" }
422+
// CHECK: attributes #[[ATTR5:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+neon,-fp-armv8" }
423+
// CHECK: attributes #[[ATTR6:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+crc,+dotprod,+neon,-fp-armv8" }
424+
// CHECK: attributes #[[ATTR7:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+neon,+rand,-fp-armv8" }
425+
// CHECK: attributes #[[ATTR8:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+predres,+rcpc,-fp-armv8" }
426+
// CHECK: attributes #[[ATTR9:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+fullfp16,+neon,+sve,+sve2,+sve2-aes,+wfxt,-fp-armv8" }
427+
// CHECK: attributes #[[ATTR10:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+fullfp16,+neon,-fp-armv8" }
428+
// CHECK: attributes #[[ATTR11:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+complxnum,+fullfp16,+neon,+sve,+sve2,+sve2-bitperm,-fp-armv8" }
429+
// CHECK: attributes #[[ATTR12:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bti,-fp-armv8" }
430+
// CHECK: attributes #[[ATTR13:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+fullfp16,+neon,+sb,+sve,-fp-armv8" }
431431
//.
432-
// CHECK-NOFMV: attributes #[[ATTR0:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="-fmv" }
433-
// CHECK-NOFMV: attributes #[[ATTR1:[0-9]+]] = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="-fmv" }
432+
// CHECK-NOFMV: attributes #[[ATTR0:[0-9]+]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="-fmv,-fp-armv8" }
433+
// CHECK-NOFMV: attributes #[[ATTR1:[0-9]+]] = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="-fmv,-fp-armv8" }
434434
//.
435435
// CHECK: [[META0:![0-9]+]] = !{i32 1, !"wchar_size", i32 4}
436436
// CHECK: [[META1:![0-9]+]] = !{!"{{.*}}clang version {{.*}}"}
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Hard-float, valid
2+
// RUN: %clang --target=aarch64-none-elf -c %s -o /dev/null
3+
// RUN: %clang --target=aarch64-none-elf -mabi=aapcs -c %s -o /dev/null
4+
// RUN: %clang --target=aarch64-none-elf -march=armv8-r -c %s -o /dev/null
5+
// RUN: %clang --target=aarch64-none-elf -march=armv8-r -mabi=aapcs -c %s -o /dev/null
6+
// RUN: %clang --target=aarch64-none-elf -march=armv8-r+fp -mabi=aapcs -c %s -o /dev/null
7+
8+
// Soft-float, no FP
9+
// RUN: %clang --target=aarch64-none-elf -march=armv8-r+nofp -mabi=aapcs-soft -c %s -o /dev/null
10+
// RUN: %clang --target=aarch64-none-elf -mgeneral-regs-only -mabi=aapcs-soft -c %s -o /dev/null
11+
12+
// Soft-float, FP hardware: Rejected, to avoid having two incompatible ABIs for common targets.
13+
// RUN: not %clang --target=aarch64-none-elf -mabi=aapcs-soft -c %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=INVALID-SOFT
14+
// RUN: not %clang --target=aarch64-none-elf -march=armv8-r+fp -mabi=aapcs-soft -c %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=INVALID-SOFT
15+
// RUN: not %clang --target=aarch64-none-elf -march=armv8-r+nofp+fp -mabi=aapcs-soft -c %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=INVALID-SOFT
16+
17+
// No FP, hard-float. This is accepted by the driver, but any use of
18+
// floating-point types will be rejected by Sema (tested elsewhere).
19+
// RUN: %clang --target=aarch64-none-elf -march=armv8-r+nofp -c %s -o /dev/null
20+
// RUN: %clang --target=aarch64-none-elf -march=armv8-r+nofp -mabi=aapcs -c %s -o /dev/null
21+
// RUN: %clang --target=aarch64-none-elf -mgeneral-regs-only -mabi=aapcs -c %s -o /dev/null
22+
23+
// INVALID-SOFT: error: 'aapcs-soft' ABI is not supported with FPU

‎clang/test/Preprocessor/aarch64-target-features.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -342,15 +342,15 @@
342342

343343
// RUN: %clang -target aarch64 -march=armv8-a+fp+simd+crc+crypto -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-MARCH-1 %s
344344
// RUN: %clang -target aarch64 -march=armv8-a+nofp+nosimd+nocrc+nocrypto+fp+simd+crc+crypto -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-MARCH-1 %s
345-
// RUN: %clang -target aarch64 -march=armv8-a+nofp+nosimd+nocrc+nocrypto -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-MARCH-2 %s
346-
// RUN: %clang -target aarch64 -march=armv8-a+fp+simd+crc+crypto+nofp+nosimd+nocrc+nocrypto -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-MARCH-2 %s
345+
// RUN: %clang -target aarch64 -march=armv8-a+nofp+nosimd+nocrc+nocrypto -mabi=aapcs-soft -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-MARCH-2 %s
346+
// RUN: %clang -target aarch64 -march=armv8-a+fp+simd+crc+crypto+nofp+nosimd+nocrc+nocrypto -mabi=aapcs-soft -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-MARCH-2 %s
347347
// RUN: %clang -target aarch64 -march=armv8-a+nosimd -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-MARCH-3 %s
348348
// CHECK-MARCH-1: "-cc1"{{.*}} "-triple" "aarch64{{.*}}" "-target-feature" "+v8a" "-target-feature" "+aes" "-target-feature" "+crc" "-target-feature" "+crypto" "-target-feature" "+fp-armv8" "-target-feature" "+sha2" "-target-feature" "+neon"
349349
// CHECK-MARCH-2: "-cc1"{{.*}} "-triple" "aarch64{{.*}}" "-target-feature" "-fp-armv8"{{.*}} "-target-feature" "-neon"
350350
// CHECK-MARCH-3: "-cc1"{{.*}} "-triple" "aarch64{{.*}}" "-target-feature" "-neon"
351351

352352
// While we're checking +nofp, also make sure it stops defining __ARM_FP
353-
// RUN: %clang -target aarch64-none-linux-gnu -march=armv8-r+nofp -x c -E -dM %s -o - | FileCheck -check-prefix=CHECK-NOFP %s
353+
// RUN: %clang -target aarch64-none-linux-gnu -march=armv8-r+nofp -mabi=aapcs-soft -x c -E -dM %s -o - | FileCheck -check-prefix=CHECK-NOFP %s
354354
// CHECK-NOFP-NOT: #define __ARM_FP{{ }}
355355

356356
// Check +sm4:
+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +fp-armv8 -fsyntax-only -target-abi aapcs -verify=fp-hard %s
2+
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature -fp-armv8 -fsyntax-only -target-abi aapcs-soft -verify=nofp-soft %s
3+
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature -fp-armv8 -fsyntax-only -target-abi aapcs -verify=nofp-hard %s
4+
// No run line needed for soft-float ABI with an FPU because that is rejected by the driver
5+
6+
// With the hard-float ABI and a target with an FPU, FP arguments are passed in
7+
// FP registers, no diagnostics needed.
8+
// fp-hard-no-diagnostics
9+
10+
// With the soft-float ABI, FP arguments are passed in integer registers, no
11+
// diagnostics needed.
12+
// nofp-soft-no-diagnostics
13+
14+
// With the hard-float ABI but no FPU, FP arguments cannot be passed in an
15+
// ABI-compatible way, so we report errors for these cases:
16+
17+
struct HFA {
18+
float x, y;
19+
};
20+
21+
struct non_HFA {
22+
float x;
23+
int y;
24+
};
25+
26+
float test_float_ret(void) { return 3.141f; } // #1
27+
// nofp-hard-error@#1 {{'test_float_ret' requires 'float' type support, but ABI 'aapcs' does not support it}}
28+
// nofp-hard-note@#1 {{'test_float_ret' defined here}}
29+
30+
struct HFA test_hfa_ret(void) { return (struct HFA){}; } // #2
31+
// nofp-hard-error@#2 {{'test_hfa_ret' requires 'struct HFA' type support, but ABI 'aapcs' does not support it}}
32+
// nofp-hard-note@#2 {{'test_hfa_ret' defined here}}
33+
34+
struct non_HFA test_non_hfa_ret(void) { return (struct non_HFA){}; } // #3
35+
// nofp-hard-error@#3 {{'test_non_hfa_ret' requires 'struct non_HFA' type support, but ABI 'aapcs' does not support it}}
36+
// nofp-hard-note@#3 {{'test_non_hfa_ret' defined here}}
37+
38+
void test_float_arg(float a) {} // #4
39+
// nofp-hard-error@#4 {{'test_float_arg' requires 'float' type support, but ABI 'aapcs' does not support it}}
40+
// nofp-hard-note@#4 {{'test_float_arg' defined here}}
41+
42+
void test_hfa_arg(struct HFA a) {} // #5
43+
// nofp-hard-error@#5 {{'test_hfa_arg' requires 'struct HFA' type support, but ABI 'aapcs' does not support it}}
44+
// nofp-hard-note@#5 {{'test_hfa_arg' defined here}}
45+
46+
void test_non_hfa_arg(struct non_HFA a) {} // #6
47+
// nofp-hard-error@#6 {{'test_non_hfa_arg' requires 'struct non_HFA' type support, but ABI 'aapcs' does not support it}}
48+
// nofp-hard-note@#6 {{'test_non_hfa_arg' defined here}}
49+
50+
int test_expr_float(int a) { return a + 1.0f; } // #7
51+
// nofp-hard-error@#7 {{expression requires 'float' type support, but ABI 'aapcs' does not support it}}
52+
53+
int test_expr_double(int a) { return a + 1.0; } // #8
54+
// nofp-hard-error@#8 {{expression requires 'double' type support, but ABI 'aapcs' does not support it}}
+161
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 4
2+
; RUN: llc --mtriple aarch64-none-eabi < %s -mattr=-fp-armv8 | FileCheck %s
3+
4+
; See also clang/test/CodeGen/aarch64-soft-float-abi.c, which tests the clang
5+
; parts of the soft-float ABI.
6+
7+
; FP types up to 64-bit are passed in a general purpose register.
8+
define half @test0(half %a, half %b) {
9+
; CHECK-LABEL: test0:
10+
; CHECK: // %bb.0: // %entry
11+
; CHECK-NEXT: mov w0, w1
12+
; CHECK-NEXT: ret
13+
entry:
14+
ret half %b
15+
}
16+
17+
define bfloat @test1(i32 %a, bfloat %b) {
18+
; CHECK-LABEL: test1:
19+
; CHECK: // %bb.0: // %entry
20+
; CHECK-NEXT: mov w0, w1
21+
; CHECK-NEXT: ret
22+
entry:
23+
ret bfloat %b
24+
}
25+
26+
define float @test2(i64 %a, float %b) {
27+
; CHECK-LABEL: test2:
28+
; CHECK: // %bb.0: // %entry
29+
; CHECK-NEXT: mov w0, w1
30+
; CHECK-NEXT: ret
31+
entry:
32+
ret float %b
33+
}
34+
35+
define double @test3(half %a, double %b) {
36+
; CHECK-LABEL: test3:
37+
; CHECK: // %bb.0: // %entry
38+
; CHECK-NEXT: mov x0, x1
39+
; CHECK-NEXT: ret
40+
entry:
41+
ret double %b
42+
}
43+
44+
; fp128 is passed in a pair of GPRs.
45+
define fp128 @test4(fp128 %a, fp128 %b) {
46+
; CHECK-LABEL: test4:
47+
; CHECK: // %bb.0: // %entry
48+
; CHECK-NEXT: mov x1, x3
49+
; CHECK-NEXT: mov x0, x2
50+
; CHECK-NEXT: ret
51+
entry:
52+
ret fp128 %b
53+
}
54+
55+
; fp128 is passed in an aligned pair of GPRs, leaving one register unused is
56+
; necessary.
57+
define fp128 @test5(float %a, fp128 %b) {
58+
; CHECK-LABEL: test5:
59+
; CHECK: // %bb.0: // %entry
60+
; CHECK-NEXT: mov x1, x3
61+
; CHECK-NEXT: mov x0, x2
62+
; CHECK-NEXT: ret
63+
entry:
64+
ret fp128 %b
65+
}
66+
67+
; If the alignment of an fp128 leaves a register unused, it remains unused even
68+
; if a later argument could fit in it.
69+
define i64 @test6(i64 %a, fp128 %b, i64 %c) {
70+
; CHECK-LABEL: test6:
71+
; CHECK: // %bb.0: // %entry
72+
; CHECK-NEXT: mov x0, x4
73+
; CHECK-NEXT: ret
74+
entry:
75+
ret i64 %c
76+
}
77+
78+
; HFAs are all bit-casted to integer types in the frontend when using the
79+
; soft-float ABI, so they get passed in the same way as non-homeogeneous
80+
; aggregates. The IR is identical to the equivalent integer types, so nothing
81+
; to test here.
82+
83+
; The PCS for vector and HVA types is not defined by the soft-float ABI because
84+
; these types are only defined by the ACLE when vector hardware is available,
85+
; so nothing to test here.
86+
87+
; The front-end generates IR for va_arg which always reads from the integer
88+
; register save area, and never the floating-point register save area. The
89+
; layout of the va_list type remains the same, the floating-point related
90+
; fields are unused. The only change needed in the backend is in va_start, to
91+
; not attempt to save the floating-point registers or set the FP fields in the
92+
; va_list.
93+
%struct.__va_list = type { ptr, ptr, ptr, i32, i32 }
94+
declare void @llvm.va_start(ptr)
95+
define double @test20(i32 %a, ...) {
96+
; CHECK-LABEL: test20:
97+
; CHECK: // %bb.0: // %entry
98+
; CHECK-NEXT: sub sp, sp, #96
99+
; CHECK-NEXT: .cfi_def_cfa_offset 96
100+
; CHECK-NEXT: mov w8, #-56 // =0xffffffc8
101+
; CHECK-NEXT: add x10, sp, #8
102+
; CHECK-NEXT: add x9, sp, #96
103+
; CHECK-NEXT: str x8, [sp, #88]
104+
; CHECK-NEXT: add x10, x10, #56
105+
; CHECK-NEXT: ldrsw x8, [sp, #88]
106+
; CHECK-NEXT: stp x1, x2, [sp, #8]
107+
; CHECK-NEXT: stp x3, x4, [sp, #24]
108+
; CHECK-NEXT: stp x5, x6, [sp, #40]
109+
; CHECK-NEXT: stp x7, x9, [sp, #56]
110+
; CHECK-NEXT: str x10, [sp, #72]
111+
; CHECK-NEXT: tbz w8, #31, .LBB7_3
112+
; CHECK-NEXT: // %bb.1: // %vaarg.maybe_reg
113+
; CHECK-NEXT: add w9, w8, #8
114+
; CHECK-NEXT: cmn w8, #8
115+
; CHECK-NEXT: str w9, [sp, #88]
116+
; CHECK-NEXT: b.gt .LBB7_3
117+
; CHECK-NEXT: // %bb.2: // %vaarg.in_reg
118+
; CHECK-NEXT: ldr x9, [sp, #72]
119+
; CHECK-NEXT: add x8, x9, x8
120+
; CHECK-NEXT: b .LBB7_4
121+
; CHECK-NEXT: .LBB7_3: // %vaarg.on_stack
122+
; CHECK-NEXT: ldr x8, [sp, #64]
123+
; CHECK-NEXT: add x9, x8, #8
124+
; CHECK-NEXT: str x9, [sp, #64]
125+
; CHECK-NEXT: .LBB7_4: // %vaarg.end
126+
; CHECK-NEXT: ldr x0, [x8]
127+
; CHECK-NEXT: add sp, sp, #96
128+
; CHECK-NEXT: ret
129+
entry:
130+
%vl = alloca %struct.__va_list, align 8
131+
call void @llvm.va_start(ptr nonnull %vl)
132+
%gr_offs_p = getelementptr inbounds %struct.__va_list, ptr %vl, i64 0, i32 3
133+
%gr_offs = load i32, ptr %gr_offs_p, align 8
134+
%0 = icmp sgt i32 %gr_offs, -1
135+
br i1 %0, label %vaarg.on_stack, label %vaarg.maybe_reg
136+
137+
vaarg.maybe_reg: ; preds = %entry
138+
%new_reg_offs = add nsw i32 %gr_offs, 8
139+
store i32 %new_reg_offs, ptr %gr_offs_p, align 8
140+
%inreg = icmp slt i32 %gr_offs, -7
141+
br i1 %inreg, label %vaarg.in_reg, label %vaarg.on_stack
142+
143+
vaarg.in_reg: ; preds = %vaarg.maybe_reg
144+
%reg_top_p = getelementptr inbounds %struct.__va_list, ptr %vl, i64 0, i32 1
145+
%reg_top = load ptr, ptr %reg_top_p, align 8
146+
%1 = sext i32 %gr_offs to i64
147+
%2 = getelementptr inbounds i8, ptr %reg_top, i64 %1
148+
br label %vaarg.end
149+
150+
vaarg.on_stack: ; preds = %vaarg.maybe_reg, %entry
151+
%stack = load ptr, ptr %vl, align 8
152+
%new_stack = getelementptr inbounds i8, ptr %stack, i64 8
153+
store ptr %new_stack, ptr %vl, align 8
154+
br label %vaarg.end
155+
156+
vaarg.end: ; preds = %vaarg.on_stack, %vaarg.in_reg
157+
%vaargs.addr = phi ptr [ %2, %vaarg.in_reg ], [ %stack, %vaarg.on_stack ]
158+
%3 = load double, ptr %vaargs.addr, align 8
159+
ret double %3
160+
}
161+

0 commit comments

Comments
 (0)
Please sign in to comment.