Skip to content

Commit

Permalink
[AArch64] Add soft-float ABI (#84146)
Browse files Browse the repository at this point in the history
This is re-working of #74460, which adds a soft-float ABI for AArch64.
That was reverted because it causes errors when building the linux and
fuchsia kernels.

The problem is that GCC's implementation of the ABI compatibility checks
when using the hard-float ABI on a target without FP registers does it's
checks after optimisation. The previous version of this patch reported
errors for all uses of floating-point types, which is stricter than what
GCC does in practice.

This changes two things compared to the first version:
* Only check the types of function arguments and returns, not the types
of other values. This is more relaxed than GCC, while still guaranteeing
ABI compatibility.
* Move the check from Sema to CodeGen, so that inline functions are only
checked if they are actually used. There are some cases in the linux
kernel which depend on this behaviour of GCC.
  • Loading branch information
ostannard committed Mar 19, 2024
1 parent d93363a commit ef395a4
Show file tree
Hide file tree
Showing 19 changed files with 470 additions and 47 deletions.
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticCommonKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,8 @@ def warn_target_unrecognized_env : Warning<
def warn_knl_knm_isa_support_removed : Warning<
"KNL, KNM related Intel Xeon Phi CPU's specific ISA's supports will be removed in LLVM 19.">,
InGroup<DiagGroup<"knl-knm-isa-support-removed">>;
def err_target_unsupported_abi_with_fpu : Error<
"'%0' ABI is not supported with FPU">;

// Source manager
def err_cannot_open_file : Error<"cannot open file '%0': %1">, DefaultFatal;
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/DiagnosticFrontendKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,8 @@ def err_builtin_needs_feature : Error<"%0 needs target feature %1">;
def err_function_needs_feature : Error<
"always_inline function %1 requires target feature '%2', but would "
"be inlined into function %0 that is compiled without support for '%2'">;

let CategoryName = "Codegen ABI Check" in {
def err_function_always_inline_attribute_mismatch : Error<
"always_inline function %1 and its caller %0 have mismatching %2 attributes">;
def err_function_always_inline_new_za : Error<
Expand All @@ -292,6 +294,10 @@ def warn_avx_calling_convention
InGroup<DiagGroup<"psabi">>;
def err_avx_calling_convention : Error<warn_avx_calling_convention.Summary>;

def err_target_unsupported_type_for_abi
: Error<"%0 requires %1 type support, but ABI '%2' does not support it">;
}

def err_alias_to_undefined : Error<
"%select{alias|ifunc}0 must point to a defined "
"%select{variable or |}1function">;
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticIDs.h
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,9 @@ class DiagnosticIDs : public RefCountedBase<DiagnosticIDs> {
/// category.
static bool isARCDiagnostic(unsigned DiagID);

/// Return true if a given diagnostic is a codegen-time ABI check.
static bool isCodegenABICheckDiagnostic(unsigned DiagID);

/// Enumeration describing how the emission of a diagnostic should
/// be treated when it occurs during C++ template argument deduction.
enum SFINAEResponse {
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/Basic/DiagnosticIDs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -861,10 +861,18 @@ bool DiagnosticIDs::isUnrecoverable(unsigned DiagID) const {
if (isARCDiagnostic(DiagID))
return false;

if (isCodegenABICheckDiagnostic(DiagID))
return false;

return true;
}

bool DiagnosticIDs::isARCDiagnostic(unsigned DiagID) {
unsigned cat = getCategoryNumberForDiag(DiagID);
return DiagnosticIDs::getCategoryNameFromID(cat).starts_with("ARC ");
}

bool DiagnosticIDs::isCodegenABICheckDiagnostic(unsigned DiagID) {
unsigned cat = getCategoryNumberForDiag(DiagID);
return DiagnosticIDs::getCategoryNameFromID(cat) == "Codegen ABI Check";
}
16 changes: 14 additions & 2 deletions clang/lib/Basic/Targets/AArch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//

#include "AArch64.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/TargetBuiltins.h"
#include "clang/Basic/TargetInfo.h"
Expand Down Expand Up @@ -200,13 +201,23 @@ AArch64TargetInfo::AArch64TargetInfo(const llvm::Triple &Triple,
StringRef AArch64TargetInfo::getABI() const { return ABI; }

bool AArch64TargetInfo::setABI(const std::string &Name) {
if (Name != "aapcs" && Name != "darwinpcs")
if (Name != "aapcs" && Name != "aapcs-soft" && Name != "darwinpcs")
return false;

ABI = Name;
return true;
}

bool AArch64TargetInfo::validateTarget(DiagnosticsEngine &Diags) const {
if (hasFeature("fp") && ABI == "aapcs-soft") {
// aapcs-soft is not allowed for targets with an FPU, to avoid there being
// two incomatible ABIs.
Diags.Report(diag::err_target_unsupported_abi_with_fpu) << ABI;
return false;
}
return true;
}

bool AArch64TargetInfo::validateBranchProtection(StringRef Spec, StringRef,
BranchProtectionInfo &BPI,
StringRef &Err) const {
Expand Down Expand Up @@ -681,7 +692,8 @@ bool AArch64TargetInfo::hasFeature(StringRef Feature) const {
return llvm::StringSwitch<bool>(Feature)
.Cases("aarch64", "arm64", "arm", true)
.Case("fmv", HasFMV)
.Cases("neon", "fp", "simd", FPU & NeonMode)
.Case("fp", FPU & FPUMode)
.Cases("neon", "simd", FPU & NeonMode)
.Case("jscvt", HasJSCVT)
.Case("fcma", HasFCMA)
.Case("rng", HasRandGen)
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Basic/Targets/AArch64.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ class LLVM_LIBRARY_VISIBILITY AArch64TargetInfo : public TargetInfo {
bool hasInt128Type() const override;

bool hasBitIntType() const override { return true; }

bool validateTarget(DiagnosticsEngine &Diags) const override;
};

class LLVM_LIBRARY_VISIBILITY AArch64leTargetInfo : public AArch64TargetInfo {
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1361,6 +1361,8 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn,
FunctionArgList Args;
QualType ResTy = BuildFunctionArgList(GD, Args);

CGM.getTargetCodeGenInfo().checkFunctionABI(CGM, FD);

if (FD->isInlineBuiltinDeclaration()) {
// When generating code for a builtin with an inline declaration, use a
// mangled name to hold the actual body, while keeping an external
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ createTargetCodeGenInfo(CodeGenModule &CGM) {
Kind = AArch64ABIKind::DarwinPCS;
else if (Triple.isOSWindows())
return createWindowsAArch64TargetCodeGenInfo(CGM, AArch64ABIKind::Win64);
else if (Target.getABI() == "aapcs-soft")
Kind = AArch64ABIKind::AAPCSSoft;

return createAArch64TargetCodeGenInfo(CGM, Kind);
}
Expand Down
16 changes: 8 additions & 8 deletions clang/lib/CodeGen/ModuleBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ namespace {

bool HandleTopLevelDecl(DeclGroupRef DG) override {
// FIXME: Why not return false and abort parsing?
if (Diags.hasErrorOccurred())
if (Diags.hasUnrecoverableErrorOccurred())
return true;

HandlingTopLevelDeclRAII HandlingDecl(*this);
Expand All @@ -206,7 +206,7 @@ namespace {
}

void HandleInlineFunctionDefinition(FunctionDecl *D) override {
if (Diags.hasErrorOccurred())
if (Diags.hasUnrecoverableErrorOccurred())
return;

assert(D->doesThisDeclarationHaveABody());
Expand All @@ -233,7 +233,7 @@ namespace {
/// client hack on the type, which can occur at any point in the file
/// (because these can be defined in declspecs).
void HandleTagDeclDefinition(TagDecl *D) override {
if (Diags.hasErrorOccurred())
if (Diags.hasUnrecoverableErrorOccurred())
return;

// Don't allow re-entrant calls to CodeGen triggered by PCH
Expand Down Expand Up @@ -269,7 +269,7 @@ namespace {
}

void HandleTagDeclRequiredDefinition(const TagDecl *D) override {
if (Diags.hasErrorOccurred())
if (Diags.hasUnrecoverableErrorOccurred())
return;

// Don't allow re-entrant calls to CodeGen triggered by PCH
Expand All @@ -283,7 +283,7 @@ namespace {

void HandleTranslationUnit(ASTContext &Ctx) override {
// Release the Builder when there is no error.
if (!Diags.hasErrorOccurred() && Builder)
if (!Diags.hasUnrecoverableErrorOccurred() && Builder)
Builder->Release();

// If there are errors before or when releasing the Builder, reset
Expand All @@ -297,14 +297,14 @@ namespace {
}

void AssignInheritanceModel(CXXRecordDecl *RD) override {
if (Diags.hasErrorOccurred())
if (Diags.hasUnrecoverableErrorOccurred())
return;

Builder->RefreshTypeCacheForClass(RD);
}

void CompleteTentativeDefinition(VarDecl *D) override {
if (Diags.hasErrorOccurred())
if (Diags.hasUnrecoverableErrorOccurred())
return;

Builder->EmitTentativeDefinition(D);
Expand All @@ -315,7 +315,7 @@ namespace {
}

void HandleVTable(CXXRecordDecl *RD) override {
if (Diags.hasErrorOccurred())
if (Diags.hasUnrecoverableErrorOccurred())
return;

Builder->EmitVTable(RD);
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/CodeGen/TargetInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ class TargetCodeGenInfo {
/// Provides a convenient hook to handle extra target-specific globals.
virtual void emitTargetGlobals(CodeGen::CodeGenModule &CGM) const {}

/// Any further codegen related checks that need to be done on a function
/// signature in a target specific manner.
virtual void checkFunctionABI(CodeGenModule &CGM,
const FunctionDecl *Decl) const {}

/// Any further codegen related checks that need to be done on a function call
/// in a target specific manner.
virtual void checkFunctionCallABI(CodeGenModule &CGM, SourceLocation CallLoc,
Expand Down Expand Up @@ -416,6 +421,7 @@ enum class AArch64ABIKind {
AAPCS = 0,
DarwinPCS,
Win64,
AAPCSSoft,
};

std::unique_ptr<TargetCodeGenInfo>
Expand Down
50 changes: 45 additions & 5 deletions clang/lib/CodeGen/Targets/AArch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class AArch64ABIInfo : public ABIInfo {
AArch64ABIInfo(CodeGenTypes &CGT, AArch64ABIKind Kind)
: ABIInfo(CGT), Kind(Kind) {}

bool isSoftFloat() const { return Kind == AArch64ABIKind::AAPCSSoft; }

private:
AArch64ABIKind getABIKind() const { return Kind; }
bool isDarwinPCS() const { return Kind == AArch64ABIKind::DarwinPCS; }
Expand Down Expand Up @@ -55,8 +57,8 @@ class AArch64ABIInfo : public ABIInfo {
Address EmitDarwinVAArg(Address VAListAddr, QualType Ty,
CodeGenFunction &CGF) const;

Address EmitAAPCSVAArg(Address VAListAddr, QualType Ty,
CodeGenFunction &CGF) const;
Address EmitAAPCSVAArg(Address VAListAddr, QualType Ty, CodeGenFunction &CGF,
AArch64ABIKind Kind) const;

Address EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
QualType Ty) const override {
Expand All @@ -67,7 +69,7 @@ class AArch64ABIInfo : public ABIInfo {

return Kind == AArch64ABIKind::Win64 ? EmitMSVAArg(CGF, VAListAddr, Ty)
: isDarwinPCS() ? EmitDarwinVAArg(VAListAddr, Ty, CGF)
: EmitAAPCSVAArg(VAListAddr, Ty, CGF);
: EmitAAPCSVAArg(VAListAddr, Ty, CGF, Kind);
}

Address EmitMSVAArg(CodeGenFunction &CGF, Address VAListAddr,
Expand Down Expand Up @@ -163,6 +165,9 @@ class AArch64TargetCodeGenInfo : public TargetCodeGenInfo {
return TargetCodeGenInfo::isScalarizableAsmOperand(CGF, Ty);
}

void checkFunctionABI(CodeGenModule &CGM,
const FunctionDecl *Decl) const override;

void checkFunctionCallABI(CodeGenModule &CGM, SourceLocation CallLoc,
const FunctionDecl *Caller,
const FunctionDecl *Callee,
Expand Down Expand Up @@ -494,6 +499,11 @@ bool AArch64SwiftABIInfo::isLegalVectorType(CharUnits VectorSize,
}

bool AArch64ABIInfo::isHomogeneousAggregateBaseType(QualType Ty) const {
// For the soft-float ABI variant, no types are considered to be homogeneous
// aggregates.
if (Kind == AArch64ABIKind::AAPCSSoft)
return false;

// Homogeneous aggregates for AAPCS64 must have base types of a floating
// point type or a short-vector type. This is the same as the 32-bit ABI,
// but with the difference that any floating-point type is allowed,
Expand Down Expand Up @@ -525,7 +535,8 @@ bool AArch64ABIInfo::isZeroLengthBitfieldPermittedInHomogeneousAggregate()
}

Address AArch64ABIInfo::EmitAAPCSVAArg(Address VAListAddr, QualType Ty,
CodeGenFunction &CGF) const {
CodeGenFunction &CGF,
AArch64ABIKind Kind) const {
ABIArgInfo AI = classifyArgumentType(Ty, /*IsVariadic=*/true,
CGF.CurFnInfo->getCallingConvention());
// Empty records are ignored for parameter passing purposes.
Expand All @@ -550,7 +561,8 @@ Address AArch64ABIInfo::EmitAAPCSVAArg(Address VAListAddr, QualType Ty,
BaseTy = ArrTy->getElementType();
NumRegs = ArrTy->getNumElements();
}
bool IsFPR = BaseTy->isFloatingPointTy() || BaseTy->isVectorTy();
bool IsFPR = Kind != AArch64ABIKind::AAPCSSoft &&
(BaseTy->isFloatingPointTy() || BaseTy->isVectorTy());

// The AArch64 va_list type and handling is specified in the Procedure Call
// Standard, section B.4:
Expand Down Expand Up @@ -841,6 +853,34 @@ static bool isStreamingCompatible(const FunctionDecl *F) {
return false;
}

void AArch64TargetCodeGenInfo::checkFunctionABI(
CodeGenModule &CGM, const FunctionDecl *FuncDecl) const {
const AArch64ABIInfo &ABIInfo = getABIInfo<AArch64ABIInfo>();
const TargetInfo &TI = ABIInfo.getContext().getTargetInfo();

// If we are using a hard-float ABI, but do not have floating point
// registers, then report an error for any function arguments or returns
// which would be passed in floating-pint registers.
auto CheckType = [&CGM, &TI, &ABIInfo](const QualType &Ty,
const NamedDecl *D) {
const Type *HABase = nullptr;
uint64_t HAMembers = 0;
if (Ty->isFloatingType() || Ty->isVectorType() ||
ABIInfo.isHomogeneousAggregate(Ty, HABase, HAMembers)) {
CGM.getDiags().Report(D->getLocation(),
diag::err_target_unsupported_type_for_abi)
<< D->getDeclName() << Ty << TI.getABI();
}
};

if (!TI.hasFeature("fp") && !ABIInfo.isSoftFloat()) {
CheckType(FuncDecl->getReturnType(), FuncDecl);
for (ParmVarDecl *PVD : FuncDecl->parameters()) {
CheckType(PVD->getType(), PVD);
}
}
}

void AArch64TargetCodeGenInfo::checkFunctionCallABI(
CodeGenModule &CGM, SourceLocation CallLoc, const FunctionDecl *Caller,
const FunctionDecl *Callee, const CallArgList &Args) const {
Expand Down

0 comments on commit ef395a4

Please sign in to comment.