[ARM][CMSE] Implement CMSE attributes
This patch adds CMSE attributes `cmse_nonsecure_call` and
`cmse_nonsecure_entry`. As usual, specification is available here:
https://developer.arm.com/docs/ecm0359818/latest
Patch by Javed Absar, Bradley Smith, David Green, Momchil Velikov,
possibly others.
Differential Revision: https://reviews.llvm.org/D71129
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 6c6cd3c..824c9a6 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -909,6 +909,8 @@
if (Info.getNoReturn())
OS << " __attribute__((noreturn))";
+ if (Info.getCmseNSCall())
+ OS << " __attribute__((cmse_nonsecure_call))";
if (Info.getProducesResult())
OS << " __attribute__((ns_returns_retained))";
if (Info.getRegParm())
@@ -1519,6 +1521,7 @@
case attr::SPtr:
case attr::UPtr:
case attr::AddressSpace:
+ case attr::CmseNSCall:
llvm_unreachable("This attribute should have been handled already");
case attr::NSReturnsRetained:
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index c582b9a..20da9f2 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -815,6 +815,7 @@
FI->ASTCallingConvention = info.getCC();
FI->InstanceMethod = instanceMethod;
FI->ChainCall = chainCall;
+ FI->CmseNSCall = info.getCmseNSCall();
FI->NoReturn = info.getNoReturn();
FI->ReturnsRetained = info.getProducesResult();
FI->NoCallerSavedRegs = info.getNoCallerSavedRegs();
@@ -1877,6 +1878,9 @@
if (FI.isNoReturn())
FuncAttrs.addAttribute(llvm::Attribute::NoReturn);
+ if (FI.isCmseNSCall())
+ FuncAttrs.addAttribute("cmse_nonsecure_call");
+
// If we have information about the function prototype, we can learn
// attributes from there.
AddAttributesFromFunctionProtoType(getContext(), FuncAttrs,
@@ -2004,6 +2008,9 @@
}
if (!AttrOnCallSite) {
+ if (TargetDecl && TargetDecl->hasAttr<CmseNSEntryAttr>())
+ FuncAttrs.addAttribute("cmse_nonsecure_entry");
+
bool DisableTailCalls = false;
if (CodeGenOpts.DisableTailCalls)
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 3ff3616..80a957b 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -4430,14 +4430,24 @@
bool IsPIE;
std::tie(RelocationModel, PICLevel, IsPIE) = ParsePICArgs(TC, Args);
- const char *RMName = RelocationModelName(RelocationModel);
+ bool IsROPI = RelocationModel == llvm::Reloc::ROPI ||
+ RelocationModel == llvm::Reloc::ROPI_RWPI;
+ bool IsRWPI = RelocationModel == llvm::Reloc::RWPI ||
+ RelocationModel == llvm::Reloc::ROPI_RWPI;
- if ((RelocationModel == llvm::Reloc::ROPI ||
- RelocationModel == llvm::Reloc::ROPI_RWPI) &&
- types::isCXX(Input.getType()) &&
+ if (Args.hasArg(options::OPT_mcmse) &&
+ !Args.hasArg(options::OPT_fallow_unsupported)) {
+ if (IsROPI)
+ D.Diag(diag::err_cmse_pi_are_incompatible) << IsROPI;
+ if (IsRWPI)
+ D.Diag(diag::err_cmse_pi_are_incompatible) << !IsRWPI;
+ }
+
+ if (IsROPI && types::isCXX(Input.getType()) &&
!Args.hasArg(options::OPT_fallow_unsupported))
D.Diag(diag::err_drv_ropi_incompatible_with_cxx);
+ const char *RMName = RelocationModelName(RelocationModel);
if (RMName) {
CmdArgs.push_back("-mrelocation-model");
CmdArgs.push_back(RMName);
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 13fae1e..336b415 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -8762,6 +8762,9 @@
QualType R = TInfo->getType();
assert(R->isFunctionType());
+ if (R.getCanonicalType()->castAs<FunctionType>()->getCmseNSCallAttr())
+ Diag(D.getIdentifierLoc(), diag::err_function_decl_cmse_ns_call);
+
SmallVector<TemplateParameterList *, 4> TemplateParamLists;
for (TemplateParameterList *TPL : TemplateParamListsRef)
TemplateParamLists.push_back(TPL);
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 5ea5103..061a7d0 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1992,6 +1992,20 @@
D->addAttr(CA);
}
+static void handleCmseNSEntryAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+ if (S.LangOpts.CPlusPlus && !D->getDeclContext()->isExternCContext()) {
+ S.Diag(AL.getLoc(), diag::err_attribute_not_clinkage) << AL;
+ return;
+ }
+
+ if (cast<FunctionDecl>(D)->getStorageClass() == SC_Static) {
+ S.Diag(AL.getLoc(), diag::warn_attribute_cmse_entry_static);
+ return;
+ }
+
+ D->addAttr(::new (S.Context) CmseNSEntryAttr(S.Context, AL));
+}
+
static void handleNakedAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
if (checkAttrMutualExclusion<DisableTailCallsAttr>(S, D, AL))
return;
@@ -7145,6 +7159,9 @@
case ParsedAttr::AT_NoDebug:
handleNoDebugAttr(S, D, AL);
break;
+ case ParsedAttr::AT_CmseNSEntry:
+ handleCmseNSEntryAttr(S, D, AL);
+ break;
case ParsedAttr::AT_StdCall:
case ParsedAttr::AT_CDecl:
case ParsedAttr::AT_FastCall:
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 335ccab..2e1231c 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -8029,6 +8029,24 @@
ColonLoc, result, VK, OK);
}
+// Check if we have a conversion between incompatible cmse function pointer
+// types, that is, a conversion between a function pointer with the
+// cmse_nonsecure_call attribute and one without.
+static bool IsInvalidCmseNSCallConversion(Sema &S, QualType FromType,
+ QualType ToType) {
+ if (const auto *ToFn =
+ dyn_cast<FunctionType>(S.Context.getCanonicalType(ToType))) {
+ if (const auto *FromFn =
+ dyn_cast<FunctionType>(S.Context.getCanonicalType(FromType))) {
+ FunctionType::ExtInfo ToEInfo = ToFn->getExtInfo();
+ FunctionType::ExtInfo FromEInfo = FromFn->getExtInfo();
+
+ return ToEInfo.getCmseNSCall() != FromEInfo.getCmseNSCall();
+ }
+ }
+ return false;
+}
+
// checkPointerTypesForAssignment - This is a very tricky routine (despite
// being closely modeled after the C99 spec:-). The odd characteristic of this
// routine is it effectively iqnores the qualifiers on the top level pointee.
@@ -8167,6 +8185,8 @@
if (!S.getLangOpts().CPlusPlus &&
S.IsFunctionConversion(ltrans, rtrans, ltrans))
return Sema::IncompatibleFunctionPointer;
+ if (IsInvalidCmseNSCallConversion(S, ltrans, rtrans))
+ return Sema::IncompatibleFunctionPointer;
return ConvTy;
}
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 356b51e..55ce028 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -129,6 +129,7 @@
case ParsedAttr::AT_NSReturnsRetained: \
case ParsedAttr::AT_NoReturn: \
case ParsedAttr::AT_Regparm: \
+ case ParsedAttr::AT_CmseNSCall: \
case ParsedAttr::AT_AnyX86NoCallerSavedRegisters: \
case ParsedAttr::AT_AnyX86NoCfCheck: \
CALLING_CONV_ATTRS_CASELIST
@@ -7093,6 +7094,25 @@
return true;
}
+ if (attr.getKind() == ParsedAttr::AT_CmseNSCall) {
+ // Delay if this is not a function type.
+ if (!unwrapped.isFunctionType())
+ return false;
+
+ // Ignore if we don't have CMSE enabled.
+ if (!S.getLangOpts().Cmse) {
+ S.Diag(attr.getLoc(), diag::warn_attribute_ignored) << attr;
+ attr.setInvalid();
+ return true;
+ }
+
+ // Otherwise we can process right away.
+ FunctionType::ExtInfo EI =
+ unwrapped.get()->getExtInfo().withCmseNSCall(true);
+ type = unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), EI));
+ return true;
+ }
+
// ns_returns_retained is not always a type attribute, but if we got
// here, we're treating it as one right now.
if (attr.getKind() == ParsedAttr::AT_NSReturnsRetained) {
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index ec75ff5..f7c58ed 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -500,6 +500,7 @@
Abv->Add(BitCodeAbbrevOp(0)); // ProducesResult
Abv->Add(BitCodeAbbrevOp(0)); // NoCallerSavedRegs
Abv->Add(BitCodeAbbrevOp(0)); // NoCfCheck
+ Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // CmseNSCall
// FunctionProtoType
Abv->Add(BitCodeAbbrevOp(0)); // IsVariadic
Abv->Add(BitCodeAbbrevOp(0)); // HasTrailingReturn