Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -2114,6 +2114,7 @@ struct ParsedTargetAttr { std::vector Features; StringRef Architecture; + StringRef BranchProtection; bool DuplicateArchitecture = false; bool operator ==(const ParsedTargetAttr &Other) const { return DuplicateArchitecture == Other.DuplicateArchitecture && @@ -2186,6 +2187,11 @@ if (Feature.startswith("fpmath=") || Feature.startswith("tune=")) continue; + if (Feature.startswith("branch-protection=")) { + Ret.BranchProtection = Feature.split('=').second.trim(); + continue; + } + // While we're here iterating check for a different target cpu. if (Feature.startswith("arch=")) { if (!Ret.Architecture.empty()) Index: clang/include/clang/Basic/AttrDocs.td =================================================================== --- clang/include/clang/Basic/AttrDocs.td +++ clang/include/clang/Basic/AttrDocs.td @@ -1793,6 +1793,10 @@ of the feature, as well as ``arch="CPU"`` which will change the default "CPU" for the function. +For AArch64, the attribute also allows the "branch-protection=" option, +where the permissible arguments and their effect on code generation are the same +as for the command-line option ``-mbranch-protection``. + Example "subtarget features" from the x86 backend include: "mmx", "sse", "sse4.2", "avx", "xop" and largely correspond to the machine specific options handled by the front end. Index: clang/include/clang/Basic/CodeGenOptions.h =================================================================== --- clang/include/clang/Basic/CodeGenOptions.h +++ clang/include/clang/Basic/CodeGenOptions.h @@ -109,13 +109,13 @@ Embed_Marker // Embed a marker as a placeholder for bitcode. }; - enum SignReturnAddressScope { + enum class SignReturnAddressScope { None, // No signing for any function NonLeaf, // Sign the return address of functions that spill LR All // Sign the return address of all functions }; - enum SignReturnAddressKeyValue { AKey, BKey }; + enum class SignReturnAddressKeyValue { AKey, BKey }; enum class FramePointerKind { None, // Omit all frame pointers. Index: clang/include/clang/Basic/CodeGenOptions.def =================================================================== --- clang/include/clang/Basic/CodeGenOptions.def +++ clang/include/clang/Basic/CodeGenOptions.def @@ -360,8 +360,8 @@ /// Whether to emit an address-significance table into the object file. CODEGENOPT(Addrsig, 1, 0) -ENUM_CODEGENOPT(SignReturnAddress, SignReturnAddressScope, 2, None) -ENUM_CODEGENOPT(SignReturnAddressKey, SignReturnAddressKeyValue, 1, AKey) +ENUM_CODEGENOPT(SignReturnAddress, SignReturnAddressScope, 2, SignReturnAddressScope::None) +ENUM_CODEGENOPT(SignReturnAddressKey, SignReturnAddressKeyValue, 1, SignReturnAddressKeyValue::AKey) CODEGENOPT(BranchTargetEnforcement, 1, 0) /// Whether to emit unused static constants. Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2591,6 +2591,8 @@ "integral compile time constant expression">; def err_attribute_requires_opencl_version : Error< "%0 attribute requires OpenCL version %1%select{| or above}2">; +def err_invalid_branch_protection_spec : Error< + "invalid or misplaced branch protection specification '%0'">; def warn_unsupported_target_attribute : Warning<"%select{unsupported|duplicate}0%select{| architecture}1 '%2' in" " the 'target' attribute string; 'target' attribute ignored">, Index: clang/include/clang/Basic/TargetInfo.h =================================================================== --- clang/include/clang/Basic/TargetInfo.h +++ clang/include/clang/Basic/TargetInfo.h @@ -16,6 +16,7 @@ #include "clang/Basic/AddressSpaces.h" #include "clang/Basic/LLVM.h" +#include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/Specifiers.h" #include "clang/Basic/TargetCXXABI.h" #include "clang/Basic/TargetOptions.h" @@ -1103,6 +1104,23 @@ return true; } + struct BranchProtectionInfo { + CodeGenOptions::SignReturnAddressScope SignReturnAddr = + CodeGenOptions::SignReturnAddressScope::None; + CodeGenOptions::SignReturnAddressKeyValue SignKey = + CodeGenOptions::SignReturnAddressKeyValue::AKey; + bool BranchTargetEnforcement = false; + }; + + /// Determine if this TargetInfo supports the given branch protection + /// specification + virtual bool validateBranchProtection(StringRef Spec, + BranchProtectionInfo &BPI, + StringRef &Err) const { + Err = ""; + return false; + } + /// Perform initialization based on the user configured /// set of features (e.g., +sse4). /// Index: clang/lib/Basic/Targets/AArch64.h =================================================================== --- clang/lib/Basic/Targets/AArch64.h +++ clang/lib/Basic/Targets/AArch64.h @@ -49,6 +49,9 @@ StringRef getABI() const override; bool setABI(const std::string &Name) override; + bool validateBranchProtection(StringRef, BranchProtectionInfo &, + StringRef &) const override; + bool isValidCPUName(StringRef Name) const override; void fillValidCPUList(SmallVectorImpl &Values) const override; bool setCPU(const std::string &Name) override; Index: clang/lib/Basic/Targets/AArch64.cpp =================================================================== --- clang/lib/Basic/Targets/AArch64.cpp +++ clang/lib/Basic/Targets/AArch64.cpp @@ -103,6 +103,52 @@ return true; } +bool AArch64TargetInfo::validateBranchProtection(StringRef Spec, + BranchProtectionInfo &BPI, + StringRef &Err) const { + BPI = {}; + if (Spec == "none") { + // defaults are fine + return true; + } + + if (Spec == "standard") { + BPI.SignReturnAddr = CodeGenOptions::SignReturnAddressScope::NonLeaf; + BPI.BranchTargetEnforcement = true; + return true; + } + + SmallVector Opts; + Spec.split(Opts, "+"); + for (int I = 0, E = Opts.size(); I != E; ++I) { + StringRef Opt = Opts[I].trim(); + if (Opt == "bti") { + BPI.BranchTargetEnforcement = true; + continue; + } + if (Opt == "pac-ret") { + BPI.SignReturnAddr = CodeGenOptions::SignReturnAddressScope::NonLeaf; + for (; I + 1 != E; ++I) { + StringRef PACOpt = Opts[I + 1].trim(); + if (PACOpt == "leaf") + BPI.SignReturnAddr = CodeGenOptions::SignReturnAddressScope::All; + else if (PACOpt == "b-key") + BPI.SignKey = CodeGenOptions::SignReturnAddressKeyValue::BKey; + else + break; + } + continue; + } + if (Opt == "") + Err = ""; + else + Err = Opt; + return false; + } + + return true; +} + bool AArch64TargetInfo::isValidCPUName(StringRef Name) const { return Name == "generic" || llvm::AArch64::parseCPUArch(Name) != llvm::AArch64::ArchKind::INVALID; Index: clang/lib/CodeGen/TargetInfo.cpp =================================================================== --- clang/lib/CodeGen/TargetInfo.cpp +++ clang/lib/CodeGen/TargetInfo.cpp @@ -5055,23 +5055,38 @@ const FunctionDecl *FD = dyn_cast_or_null(D); if (!FD) return; - llvm::Function *Fn = cast(GV); - auto Kind = CGM.getCodeGenOpts().getSignReturnAddress(); - if (Kind != CodeGenOptions::SignReturnAddressScope::None) { + CodeGenOptions::SignReturnAddressScope Scope = CGM.getCodeGenOpts().getSignReturnAddress(); + CodeGenOptions::SignReturnAddressKeyValue Key = CGM.getCodeGenOpts().getSignReturnAddressKey(); + bool BranchTargetEnforcement = CGM.getCodeGenOpts().BranchTargetEnforcement; + if (const auto *TA = FD->getAttr()) { + TargetAttr::ParsedTargetAttr Attr = TA->parse(); + if (!Attr.BranchProtection.empty()) { + TargetInfo::BranchProtectionInfo BPI; + StringRef Error; + (void)CGM.getTarget().validateBranchProtection(Attr.BranchProtection, + BPI, Error); + assert(Error.empty()); + Scope = BPI.SignReturnAddr; + Key = BPI.SignKey; + BranchTargetEnforcement = BPI.BranchTargetEnforcement; + } + } + + auto *Fn = cast(GV); + if (Scope != CodeGenOptions::SignReturnAddressScope::None) { Fn->addFnAttr("sign-return-address", - Kind == CodeGenOptions::SignReturnAddressScope::All + Scope == CodeGenOptions::SignReturnAddressScope::All ? "all" : "non-leaf"); - auto Key = CGM.getCodeGenOpts().getSignReturnAddressKey(); Fn->addFnAttr("sign-return-address-key", Key == CodeGenOptions::SignReturnAddressKeyValue::AKey ? "a_key" : "b_key"); } - if (CGM.getCodeGenOpts().BranchTargetEnforcement) + if (BranchTargetEnforcement) Fn->addFnAttr("branch-target-enforcement"); } }; Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -2990,6 +2990,19 @@ << Unsupported << None << CurFeature; } + TargetInfo::BranchProtectionInfo BPI; + StringRef Error; + if (!ParsedAttrs.BranchProtection.empty() && + !Context.getTargetInfo().validateBranchProtection( + ParsedAttrs.BranchProtection, BPI, Error)) { + if (Error.empty()) + return Diag(LiteralLoc, diag::warn_unsupported_target_attribute) + << Unsupported << None << "branch-protection"; + else + return Diag(LiteralLoc, diag::err_invalid_branch_protection_spec) + << Error; + } + return false; } Index: clang/test/CodeGen/aarch64-branch-protection-attr.c =================================================================== --- /dev/null +++ clang/test/CodeGen/aarch64-branch-protection-attr.c @@ -0,0 +1,81 @@ +// REQUIRES: aarch64-registered-target +// RUN: %clang_cc1 -triple aarch64-unknown-unknown-eabi -emit-llvm -target-cpu generic -target-feature +v8.5a %s -o - \ +// RUN: | FileCheck %s --check-prefix=CHECK --check-prefix=NO-OVERRIDE +// RUN: %clang_cc1 -triple aarch64-unknown-unknown-eabi -emit-llvm -target-cpu generic -target-feature +v8.5a %s -o - \ +// RUN: -msign-return-address=non-leaf -msign-return-address-key=a_key -mbranch-target-enforce \ +// RUN: | FileCheck %s --check-prefix=CHECK --check-prefix=OVERRIDE + +void missing() {} +// NO-OVERRIDE: define void @missing() #[[#NONE:]] +// OVERRIDE: define void @missing() #[[#STD:]] + +__attribute__ ((target("branch-protection=none"))) +void none() {} +// NO-OVERRIDE: define void @none() #[[#NONE]] +// OVERRIDE: define void @none() #[[#NONE:]] + + __attribute__ ((target("branch-protection=standard"))) +void std() {} +// NO-OVERRIDE: define void @std() #[[#STD:]] +// OVERRIDE: define void @std() #[[#STD]] + +__attribute__ ((target("branch-protection=bti"))) +void btionly() {} +// NO-OVERRIDE: define void @btionly() #[[#BTI:]] +// OVERRIDE: define void @btionly() #[[#BTI:]] + +__attribute__ ((target("branch-protection=pac-ret"))) +void paconly() {} +// NO-OVERRIDE: define void @paconly() #[[#PAC:]] +// OVERRIDE: define void @paconly() #[[#PAC:]] + +__attribute__ ((target("branch-protection=pac-ret+bti"))) +void pacbti0() {} +// NO-OVERRIDE: define void @pacbti0() #[[#PACBTI:]] +// OVERRIDE: define void @pacbti0() #[[#PACBTI:]] + +__attribute__ ((target("branch-protection=bti+pac-ret"))) +void pacbti1() {} +// NO-OVERRIDE: define void @pacbti1() #[[#PACBTI]] +// OVERRIDE: define void @pacbti1() #[[#PACBTI]] + +__attribute__ ((target("branch-protection=pac-ret+leaf"))) +void leaf() {} +// NO-OVERRIDE: define void @leaf() #[[#PACLEAF:]] +// OVERRIDE: define void @leaf() #[[#PACLEAF:]] + +__attribute__ ((target("branch-protection=pac-ret+b-key"))) +void bkey() {} +// NO-OVERRIDE: define void @bkey() #[[#PACBKEY:]] +// OVERRIDE: define void @bkey() #[[#PACBKEY:]] + +__attribute__ ((target("branch-protection=pac-ret+b-key+leaf"))) +void bkeyleaf0() {} +// NO-OVERRIDE: define void @bkeyleaf0() #[[#PACBKEYLEAF:]] +// OVERRIDE: define void @bkeyleaf0() #[[#PACBKEYLEAF:]] + +__attribute__ ((target("branch-protection=pac-ret+leaf+b-key"))) +void bkeyleaf1() {} +// NO-OVERRIDE: define void @bkeyleaf1() #[[#PACBKEYLEAF]] +// OVERRIDE: define void @bkeyleaf1() #[[#PACBKEYLEAF]] + +__attribute__ ((target("branch-protection=pac-ret+leaf+bti"))) +void btileaf() {} +// NO-OVERRIDE: define void @btileaf() #[[#BTIPACLEAF:]] +// OVERRIDE: define void @btileaf() #[[#BTIPACLEAF:]] + +// CHECK-DAG: attributes #[[#NONE]] + +// CHECK-DAG: attributes #[[#STD]] = { {{.*}} "branch-target-enforcement" {{.*}} "sign-return-address"="non-leaf" "sign-return-address-key"="a_key" + +// CHECK-DAG: attributes #[[#BTI]] = { {{.*}}"branch-target-enforcement" + +// CHECK-DAG: attributes #[[#PAC]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-key"="a_key" + +// CHECK-DAG: attributes #[[#PACLEAF]] = { {{.*}} "sign-return-address"="all" "sign-return-address-key"="a_key" + +// CHECK-DAG: attributes #[[#PACBKEY]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-key"="b_key" + +// CHECK-DAG: attributes #[[#PACBKEYLEAF]] = { {{.*}} "sign-return-address"="all" "sign-return-address-key"="b_key" + +// CHECK-DAG: attributes #[[#BTIPACLEAF]] = { {{.*}}"branch-target-enforcement" {{.*}} "sign-return-address"="all" "sign-return-address-key"="a_key" Index: clang/test/Sema/attr-target.c =================================================================== --- clang/test/Sema/attr-target.c +++ clang/test/Sema/attr-target.c @@ -17,5 +17,6 @@ int __attribute__((target("arch=hiss,arch=woof"))) pine_tree() { return 4; } //expected-warning@+1 {{duplicate 'arch=' in the 'target' attribute string; 'target' attribute ignored}} int __attribute__((target("arch=ivybridge,arch=haswell"))) oak_tree() { return 4; } - +//expected-warning@+1 {{unsupported 'branch-protection' in the 'target' attribute string; 'target' attribute ignored}} +int __attribute__((target("branch-protection=none"))) birch_tree() { return 5; } Index: clang/test/Sema/branch-protection-attr-err.c =================================================================== --- /dev/null +++ clang/test/Sema/branch-protection-attr-err.c @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -triple aarch64 -verify -fsyntax-only %s + +__attribute__((target("branch-protection=foo"))) // expected-error {{invalid or misplaced branch protection specification 'foo'}} +void badvalue0() {} + +__attribute__((target("branch-protection=+bti"))) // expected-error {{invalid or misplaced branch protection specification ''}} +void badvalue1() {} + +__attribute__((target("branch-protection=bti+"))) // expected-error {{invalid or misplaced branch protection specification ''}} +void badvalue2() {} + +__attribute__((target("branch-protection=pac-ret+bkey"))) // expected-error {{invalid or misplaced branch protection specification 'bkey'}} +void badvalue3() {} + +__attribute__((target("branch-protection=bti+leaf"))) // expected-error {{invalid or misplaced branch protection specification 'leaf'}} +void badoption0() {} + +__attribute__((target("branch-protection=bti+leaf+pac-ret"))) // expected-error {{invalid or misplaced branch protection specification 'leaf'}} +void badorder0() {} + +__attribute__ ((target("branch-protection=pac-ret+bti+leaf"))) // expected-error {{invalid or misplaced branch protection specification 'leaf'}} +void badorder1() {}