[clang] 080d046 - [ARM][CMSE] Implement CMSE attributes
Momchil Velikov via cfe-commits
cfe-commits at lists.llvm.org
Tue Mar 24 03:21:52 PDT 2020
Author: Momchil Velikov
Date: 2020-03-24T10:21:26Z
New Revision: 080d046c91d26bd3b0afba817cf5c2f99d1288ff
URL: https://github.com/llvm/llvm-project/commit/080d046c91d26bd3b0afba817cf5c2f99d1288ff
DIFF: https://github.com/llvm/llvm-project/commit/080d046c91d26bd3b0afba817cf5c2f99d1288ff.diff
LOG: [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
Added:
clang/test/CodeGen/arm-cmse-attr.c
clang/test/CodeGen/arm-cmse-call.c
clang/test/Sema/arm-cmse.c
clang/test/Sema/arm-no-cmse.c
clang/test/SemaCXX/arm-cmse.cpp
Modified:
clang/include/clang/AST/Type.h
clang/include/clang/AST/TypeProperties.td
clang/include/clang/Basic/Attr.td
clang/include/clang/Basic/AttrDocs.td
clang/include/clang/Basic/DiagnosticDriverKinds.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/CodeGen/CGFunctionInfo.h
clang/lib/AST/TypePrinter.cpp
clang/lib/CodeGen/CGCall.cpp
clang/lib/Driver/ToolChains/Clang.cpp
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaDeclAttr.cpp
clang/lib/Sema/SemaExpr.cpp
clang/lib/Sema/SemaType.cpp
clang/lib/Serialization/ASTWriter.cpp
clang/test/AST/ast-dump-arm-attr.c
clang/test/Driver/ropi-rwpi.c
clang/test/Driver/save-temps.c
clang/test/Misc/pragma-attribute-supported-attributes-list.test
Removed:
################################################################################
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 05c6167dcbb2..16db1d5cbc3a 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -1546,7 +1546,7 @@ class alignas(8) Type : public ExtQualsTypeCommonBase {
/// Extra information which affects how the function is called, like
/// regparm and the calling convention.
- unsigned ExtInfo : 12;
+ unsigned ExtInfo : 13;
/// The ref-qualifier associated with a \c FunctionProtoType.
///
@@ -3568,12 +3568,12 @@ class FunctionType : public Type {
class ExtInfo {
friend class FunctionType;
- // Feel free to rearrange or add bits, but if you go over 12,
- // you'll need to adjust both the Bits field below and
- // Type::FunctionTypeBitfields.
+ // Feel free to rearrange or add bits, but if you go over 16, you'll need to
+ // adjust the Bits field below, and if you add bits, you'll need to adjust
+ // Type::FunctionTypeBitfields::ExtInfo as well.
- // | CC |noreturn|produces|nocallersavedregs|regparm|nocfcheck|
- // |0 .. 4| 5 | 6 | 7 |8 .. 10| 11 |
+ // | CC |noreturn|produces|nocallersavedregs|regparm|nocfcheck|cmsenscall|
+ // |0 .. 4| 5 | 6 | 7 |8 .. 10| 11 | 12 |
//
// regparm is either 0 (no regparm attribute) or the regparm value+1.
enum { CallConvMask = 0x1F };
@@ -3581,26 +3581,29 @@ class FunctionType : public Type {
enum { ProducesResultMask = 0x40 };
enum { NoCallerSavedRegsMask = 0x80 };
enum { NoCfCheckMask = 0x800 };
+ enum { CmseNSCallMask = 0x1000 };
enum {
RegParmMask = ~(CallConvMask | NoReturnMask | ProducesResultMask |
- NoCallerSavedRegsMask | NoCfCheckMask),
+ NoCallerSavedRegsMask | NoCfCheckMask | CmseNSCallMask),
RegParmOffset = 8
}; // Assumed to be the last field
uint16_t Bits = CC_C;
ExtInfo(unsigned Bits) : Bits(static_cast<uint16_t>(Bits)) {}
- public:
- // Constructor with no defaults. Use this when you know that you
- // have all the elements (when reading an AST file for example).
- ExtInfo(bool noReturn, bool hasRegParm, unsigned regParm, CallingConv cc,
- bool producesResult, bool noCallerSavedRegs, bool NoCfCheck) {
- assert((!hasRegParm || regParm < 7) && "Invalid regparm value");
- Bits = ((unsigned)cc) | (noReturn ? NoReturnMask : 0) |
- (producesResult ? ProducesResultMask : 0) |
- (noCallerSavedRegs ? NoCallerSavedRegsMask : 0) |
- (hasRegParm ? ((regParm + 1) << RegParmOffset) : 0) |
- (NoCfCheck ? NoCfCheckMask : 0);
+ public:
+ // Constructor with no defaults. Use this when you know that you
+ // have all the elements (when reading an AST file for example).
+ ExtInfo(bool noReturn, bool hasRegParm, unsigned regParm, CallingConv cc,
+ bool producesResult, bool noCallerSavedRegs, bool NoCfCheck,
+ bool cmseNSCall) {
+ assert((!hasRegParm || regParm < 7) && "Invalid regparm value");
+ Bits = ((unsigned)cc) | (noReturn ? NoReturnMask : 0) |
+ (producesResult ? ProducesResultMask : 0) |
+ (noCallerSavedRegs ? NoCallerSavedRegsMask : 0) |
+ (hasRegParm ? ((regParm + 1) << RegParmOffset) : 0) |
+ (NoCfCheck ? NoCfCheckMask : 0) |
+ (cmseNSCall ? CmseNSCallMask : 0);
}
// Constructor with all defaults. Use when for example creating a
@@ -3613,6 +3616,7 @@ class FunctionType : public Type {
bool getNoReturn() const { return Bits & NoReturnMask; }
bool getProducesResult() const { return Bits & ProducesResultMask; }
+ bool getCmseNSCall() const { return Bits & CmseNSCallMask; }
bool getNoCallerSavedRegs() const { return Bits & NoCallerSavedRegsMask; }
bool getNoCfCheck() const { return Bits & NoCfCheckMask; }
bool getHasRegParm() const { return (Bits >> RegParmOffset) != 0; }
@@ -3650,6 +3654,13 @@ class FunctionType : public Type {
return ExtInfo(Bits & ~ProducesResultMask);
}
+ ExtInfo withCmseNSCall(bool cmseNSCall) const {
+ if (cmseNSCall)
+ return ExtInfo(Bits | CmseNSCallMask);
+ else
+ return ExtInfo(Bits & ~CmseNSCallMask);
+ }
+
ExtInfo withNoCallerSavedRegs(bool noCallerSavedRegs) const {
if (noCallerSavedRegs)
return ExtInfo(Bits | NoCallerSavedRegsMask);
@@ -3722,6 +3733,7 @@ class FunctionType : public Type {
/// type.
bool getNoReturnAttr() const { return getExtInfo().getNoReturn(); }
+ bool getCmseNSCallAttr() const { return getExtInfo().getCmseNSCall(); }
CallingConv getCallConv() const { return getExtInfo().getCC(); }
ExtInfo getExtInfo() const { return ExtInfo(FunctionTypeBits.ExtInfo); }
diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td
index 3cf56e5a5629..4afd9790d875 100644
--- a/clang/include/clang/AST/TypeProperties.td
+++ b/clang/include/clang/AST/TypeProperties.td
@@ -249,13 +249,17 @@ let Class = FunctionType in {
def : Property<"noCfCheck", Bool> {
let Read = [{ node->getExtInfo().getNoCfCheck() }];
}
+ def : Property<"cmseNSCall", Bool> {
+ let Read = [{ node->getExtInfo().getCmseNSCall() }];
+ }
}
let Class = FunctionNoProtoType in {
def : Creator<[{
auto extInfo = FunctionType::ExtInfo(noReturn, hasRegParm, regParm,
callingConvention, producesResult,
- noCallerSavedRegs, noCfCheck);
+ noCallerSavedRegs, noCfCheck,
+ cmseNSCall);
return ctx.getFunctionNoProtoType(returnType, extInfo);
}]>;
}
@@ -288,7 +292,8 @@ let Class = FunctionProtoType in {
def : Creator<[{
auto extInfo = FunctionType::ExtInfo(noReturn, hasRegParm, regParm,
callingConvention, producesResult,
- noCallerSavedRegs, noCfCheck);
+ noCallerSavedRegs, noCfCheck,
+ cmseNSCall);
FunctionProtoType::ExtProtoInfo epi;
epi.ExtInfo = extInfo;
epi.Variadic = variadic;
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index db1589bea814..5a90b2be2cbf 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -962,6 +962,19 @@ def Cleanup : InheritableAttr {
let Documentation = [Undocumented];
}
+def CmseNSEntry : InheritableAttr, TargetSpecificAttr<TargetARM> {
+ let Spellings = [GNU<"cmse_nonsecure_entry">];
+ let Subjects = SubjectList<[Function]>;
+ let LangOpts = [Cmse];
+ let Documentation = [ArmCmseNSEntryDocs];
+}
+
+def CmseNSCall : TypeAttr, TargetSpecificAttr<TargetARM> {
+ let Spellings = [GNU<"cmse_nonsecure_call">];
+ let LangOpts = [Cmse];
+ let Documentation = [ArmCmseNSCallDocs];
+}
+
def Cold : InheritableAttr {
let Spellings = [GCC<"cold">];
let Subjects = SubjectList<[Function]>;
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index d349b99a6d85..a1cf25ed3929 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -4853,3 +4853,28 @@ other than overloading.
}];
}
+
+def ArmCmseNSCallDocs : Documentation {
+ let Category = DocCatType;
+ let Content = [{
+This attribute declares a non-secure function type. When compiling for secure
+state, a call to such a function would switch from secure to non-secure state.
+All non-secure function calls must happen only through a function pointer, and
+a non-secure function type should only be used as a base type of a pointer.
+See `ARMv8-M Security Extensions: Requirements on Development
+Tools - Engineering Specification Documentation
+<https://developer.arm.com/docs/ecm0359818/latest/>`_ for more information.
+ }];
+}
+
+def ArmCmseNSEntryDocs : Documentation {
+ let Category = DocCatFunction;
+ let Content = [{
+This attribute declares a function that can be called from non-secure state, or
+from secure state. Entering from and returning to non-secure state would switch
+to and from secure state, respectively, and prevent flow of information
+to non-secure state, except via return values. See `ARMv8-M Security Extensions:
+Requirements on Development Tools - Engineering Specification Documentation
+<https://developer.arm.com/docs/ecm0359818/latest/>`_ for more information.
+ }];
+}
diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index 2d13295bd301..5a3249215189 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -377,6 +377,9 @@ def err_drv_ropi_incompatible_with_cxx : Error<
def err_stack_tagging_requires_hardware_feature : Error<
"'-fsanitize=memtag' requires hardware support (+memtag)">;
+def err_cmse_pi_are_incompatible : Error<
+ "cmse is not compatible with %select{RWPI|ROPI}0">;
+
def warn_target_unsupported_nan2008 : Warning<
"ignoring '-mnan=2008' option because the '%0' architecture does not support it">,
InGroup<UnsupportedNan>;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index ca9333d50e42..1dfa24a20737 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2894,6 +2894,10 @@ def err_attribute_address_multiple_qualifiers : Error<
def warn_attribute_address_multiple_identical_qualifiers : Warning<
"multiple identical address spaces specified for type">,
InGroup<DuplicateDeclSpecifier>;
+def err_attribute_not_clinkage : Error<
+ "function type with %0 attribute must have C linkage">;
+def err_function_decl_cmse_ns_call : Error<
+ "functions may not be declared with 'cmse_nonsecure_call' attribute">;
def err_attribute_address_function_type : Error<
"function type may not be qualified with an address space">;
def err_as_qualified_auto_decl : Error<
@@ -3114,6 +3118,9 @@ def warn_attribute_weak_on_local : Warning<
InGroup<IgnoredAttributes>;
def warn_weak_identifier_undeclared : Warning<
"weak identifier %0 never declared">;
+def warn_attribute_cmse_entry_static : Warning<
+ "'cmse_nonsecure_entry' cannot be applied to functions with internal linkage">,
+ InGroup<IgnoredAttributes>;
def err_attribute_weak_static : Error<
"weak declaration cannot have internal linkage">;
def err_attribute_selectany_non_extern_data : Error<
diff --git a/clang/include/clang/CodeGen/CGFunctionInfo.h b/clang/include/clang/CodeGen/CGFunctionInfo.h
index 588c96afe402..eaf5a3d5aad7 100644
--- a/clang/include/clang/CodeGen/CGFunctionInfo.h
+++ b/clang/include/clang/CodeGen/CGFunctionInfo.h
@@ -508,6 +508,9 @@ class CGFunctionInfo final
/// Whether this is a chain call.
unsigned ChainCall : 1;
+ /// Whether this function is a CMSE nonsecure call
+ unsigned CmseNSCall : 1;
+
/// Whether this function is noreturn.
unsigned NoReturn : 1;
@@ -598,6 +601,8 @@ class CGFunctionInfo final
bool isChainCall() const { return ChainCall; }
+ bool isCmseNSCall() const { return CmseNSCall; }
+
bool isNoReturn() const { return NoReturn; }
/// In ARC, whether this function retains its return value. This
@@ -635,7 +640,8 @@ class CGFunctionInfo final
FunctionType::ExtInfo getExtInfo() const {
return FunctionType::ExtInfo(isNoReturn(), getHasRegParm(), getRegParm(),
getASTCallingConvention(), isReturnsRetained(),
- isNoCallerSavedRegs(), isNoCfCheck());
+ isNoCallerSavedRegs(), isNoCfCheck(),
+ isCmseNSCall());
}
CanQualType getReturnType() const { return getArgsBuffer()[0].type; }
@@ -676,6 +682,7 @@ class CGFunctionInfo final
ID.AddBoolean(HasRegParm);
ID.AddInteger(RegParm);
ID.AddBoolean(NoCfCheck);
+ ID.AddBoolean(CmseNSCall);
ID.AddInteger(Required.getOpaqueData());
ID.AddBoolean(HasExtParameterInfos);
if (HasExtParameterInfos) {
@@ -703,6 +710,7 @@ class CGFunctionInfo final
ID.AddBoolean(info.getHasRegParm());
ID.AddInteger(info.getRegParm());
ID.AddBoolean(info.getNoCfCheck());
+ ID.AddBoolean(info.getCmseNSCall());
ID.AddInteger(required.getOpaqueData());
ID.AddBoolean(!paramInfos.empty());
if (!paramInfos.empty()) {
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 6c6cd3c9a74b..824c9a611876 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -909,6 +909,8 @@ void TypePrinter::printFunctionAfter(const FunctionType::ExtInfo &Info,
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 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
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 c582b9a32d07..20da9f24a3b5 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -815,6 +815,7 @@ CGFunctionInfo *CGFunctionInfo::create(unsigned llvmCC,
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 @@ void CodeGenModule::ConstructAttributeList(
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 @@ void CodeGenModule::ConstructAttributeList(
}
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 3ff361643c05..80a957bf4579 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -4430,14 +4430,24 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
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 (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 ((RelocationModel == llvm::Reloc::ROPI ||
- RelocationModel == llvm::Reloc::ROPI_RWPI) &&
- types::isCXX(Input.getType()) &&
+ 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 13fae1e9fed5..336b41535b19 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -8762,6 +8762,9 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
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 5ea5103c3000..061a7d0225ed 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1992,6 +1992,20 @@ static void handleCommonAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
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 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
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 335ccabfd964..2e1231c57c28 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -8029,6 +8029,24 @@ ExprResult Sema::ActOnConditionalOp(SourceLocation QuestionLoc,
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 @@ checkPointerTypesForAssignment(Sema &S, QualType LHSType, QualType RHSType) {
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 356b51e439a6..55ce028fb8c2 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -129,6 +129,7 @@ static void diagnoseBadTypeAttribute(Sema &S, const ParsedAttr &attr,
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 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, ParsedAttr &attr,
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 ec75ff5965a0..f7c58ed11d9f 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -500,6 +500,7 @@ void ASTWriter::WriteTypeAbbrevs() {
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
diff --git a/clang/test/AST/ast-dump-arm-attr.c b/clang/test/AST/ast-dump-arm-attr.c
index 41328165d210..82a797615009 100644
--- a/clang/test/AST/ast-dump-arm-attr.c
+++ b/clang/test/AST/ast-dump-arm-attr.c
@@ -1,5 +1,9 @@
// RUN: %clang_cc1 -triple arm-apple-darwin -ast-dump -ast-dump-filter Test %s | FileCheck --strict-whitespace %s
+// RUN: %clang_cc1 -triple armv8m.base-none-eabi -mcmse -ast-dump -ast-dump-filter Test %s | FileCheck --strict-whitespace %s --check-prefix=CHECK-CMSE
__attribute__((interrupt)) void Test(void);
// CHECK: FunctionDecl{{.*}}Test
// CHECK-NEXT: ARMInterruptAttr
+
+typedef int (*CmseTest)(int a) __attribute__((cmse_nonsecure_call));
+// CHECK-CMSE: TypedefDecl{{.*}}CmseTest{{.*}}__attribute__((cmse_nonsecure_call))
diff --git a/clang/test/CodeGen/arm-cmse-attr.c b/clang/test/CodeGen/arm-cmse-attr.c
new file mode 100644
index 000000000000..041ed3f64a7e
--- /dev/null
+++ b/clang/test/CodeGen/arm-cmse-attr.c
@@ -0,0 +1,43 @@
+// RUN: %clang_cc1 -triple thumbv8m.base-none-eabi -O1 -emit-llvm %s -o - 2>&1 | \
+// RUN: FileCheck %s --check-prefix=CHECK-NOSE --check-prefix=CHECK
+// RUN: %clang_cc1 -triple thumbebv8m.base-none-eabi -O1 -emit-llvm %s -o - 2>&1 | \
+// RUN: FileCheck %s --check-prefix=CHECK-NOSE --check-prefix=CHECK
+// RUN: %clang_cc1 -triple thumbv8m.base-none-eabi -mcmse -O1 -emit-llvm %s -o - 2>&1 | \
+// RUN: FileCheck %s --check-prefix=CHECK-SE --check-prefix=CHECK
+// RUN: %clang_cc1 -triple thumbebv8m.base-none-eabi -mcmse -O1 -emit-llvm %s -o - 2>&1 | \
+// RUN: FileCheck %s --check-prefix=CHECK-SE --check-prefix=CHECK
+
+typedef void (*callback_t)(void) __attribute__((cmse_nonsecure_call));
+typedef void callback2_t(void) __attribute__((cmse_nonsecure_call));
+
+void f1(callback_t fptr)
+{
+ fptr();
+}
+
+void f2(callback2_t *fptr)
+{
+ fptr();
+}
+
+void f3() __attribute__((cmse_nonsecure_entry));
+void f3()
+{
+}
+
+void f4() __attribute__((cmse_nonsecure_entry))
+{
+}
+
+// CHECK: define void @f1(void ()* nocapture %fptr) {{[^#]*}}#0 {
+// CHECK: call void %fptr() #2
+// CHECK: define void @f2(void ()* nocapture %fptr) {{[^#]*}}#0 {
+// CHECK: call void %fptr() #2
+// CHECK: define void @f3() {{[^#]*}}#1 {
+// CHECK: define void @f4() {{[^#]*}}#1 {
+
+// CHECK-NOSE-NOT: cmse_nonsecure_entry
+// CHECK-NOSE-NOT: cmse_nonsecure_call
+// CHECK-SE: attributes #0 = { nounwind
+// CHECK-SE: attributes #1 = { {{.*}} "cmse_nonsecure_entry"
+// CHECK-SE: attributes #2 = { {{.*}} "cmse_nonsecure_call"
diff --git a/clang/test/CodeGen/arm-cmse-call.c b/clang/test/CodeGen/arm-cmse-call.c
new file mode 100644
index 000000000000..8bbab23d9ea6
--- /dev/null
+++ b/clang/test/CodeGen/arm-cmse-call.c
@@ -0,0 +1,77 @@
+// RUN: %clang_cc1 -triple thumbv8m.base-none-eabi -mcmse -O1 -emit-llvm %s -o - 2>&1 | \
+// RUN: FileCheck %s --check-prefix=CHECK
+// RUN: %clang_cc1 -triple thumbebv8m.base-none-eabi -mcmse -O1 -emit-llvm %s -o - 2>&1 | \
+// RUN: FileCheck %s --check-prefix=CHECK
+
+typedef void fn_t(void);
+fn_t s;
+fn_t *p0 __attribute__((cmse_nonsecure_call));
+
+typedef fn_t *pfn_t __attribute__((cmse_nonsecure_call));
+pfn_t p1;
+pfn_t a0[4];
+extern pfn_t a1[];
+
+typedef void (*pfn1_t)(int) __attribute__((cmse_nonsecure_call));
+pfn1_t p2;
+
+typedef fn_t *apfn_t[4] __attribute__((cmse_nonsecure_call));
+apfn_t a2;
+
+typedef pfn_t apfn1_t[4] __attribute__((cmse_nonsecure_call));
+apfn1_t a3;
+
+typedef void (*apfn2_t[4])(void) __attribute__((cmse_nonsecure_call));
+apfn2_t a4;
+
+void (*b[4])(int) __attribute__((cmse_nonsecure_call));
+
+void f(int i) {
+ s();
+// CHECK: call void @s() #[[#A1:]]
+
+ p0();
+// CHECK: %[[#P0:]] = load {{.*}} @p0
+// CHECK: call void %[[#P0]]() #[[#A2:]]
+
+ p1();
+// CHECK: %[[#P1:]] = load {{.*}} @p1
+// CHECK: call void %[[#P1]]() #[[#A2]]
+
+ p2(i);
+// CHECK: %[[#P2:]] = load {{.*}} @p2
+// CHECK: call void %[[#P2]](i32 %i) #[[#A2]]
+
+ a0[i]();
+// CHECK: %[[EP0:.*]] = getelementptr {{.*}} @a0
+// CHECK: %[[#E0:]] = load {{.*}} %[[EP0]]
+// CHECK: call void %[[#E0]]() #[[#A2]]
+
+ a1[i]();
+// CHECK: %[[EP1:.*]] = getelementptr {{.*}} @a1
+// CHECK: %[[#E1:]] = load {{.*}} %[[EP1]]
+// CHECK: call void %[[#E1]]() #[[#A2]]
+
+ a2[i]();
+// CHECK: %[[EP2:.*]] = getelementptr {{.*}} @a2
+// CHECK: %[[#E2:]] = load {{.*}} %[[EP2]]
+// CHECK: call void %[[#E2]]() #[[#A2]]
+
+ a3[i]();
+// CHECK: %[[EP3:.*]] = getelementptr {{.*}} @a3
+// CHECK: %[[#E3:]] = load {{.*}} %[[EP3]]
+// CHECK: call void %[[#E3]]() #[[#A2]]
+
+ a4[i]();
+// CHECK: %[[EP4:.*]] = getelementptr {{.*}} @a4
+// CHECK: %[[#E4:]] = load {{.*}} %[[EP4]]
+// CHECK: call void %[[#E4]]() #[[#A2]]
+
+ b[i](i);
+// CHECK: %[[EP5:.*]] = getelementptr {{.*}} @b
+// CHECK: %[[#E5:]] = load {{.*}} %[[EP5]]
+// CHECK: call void %[[#E5]](i32 %i) #[[#A2]]
+}
+
+// CHECK: attributes #[[#A1]] = { nounwind }
+// CHECK: attributes #[[#A2]] = { nounwind "cmse_nonsecure_call"
diff --git a/clang/test/Driver/ropi-rwpi.c b/clang/test/Driver/ropi-rwpi.c
index f22c8d004891..756910821839 100644
--- a/clang/test/Driver/ropi-rwpi.c
+++ b/clang/test/Driver/ropi-rwpi.c
@@ -21,6 +21,14 @@
// RUN: %clang -target arm-none-eabi -x c++ -fropi -frwpi -### -c %s 2>&1 | FileCheck --check-prefix=CXX %s
// RUN: %clang -target arm-none-eabi -x c++ -fallow-unsupported -fropi -### -c %s 2>&1 | FileCheck --check-prefix=ROPI %s
+// RUN: %clang -target arm-none-eabi -march=armv8m.main -fropi -mcmse -### -c %s 2>&1 | FileCheck --check-prefix=ROPI-CMSE %s
+// RUN: %clang -target arm-none-eabi -march=armv8m.main -frwpi -mcmse -### -c %s 2>&1 | FileCheck --check-prefix=RWPI-CMSE %s
+// RUN: %clang -target arm-none-eabi -march=armv8m.main -frwpi -fropi -mcmse -### -c %s 2>&1 | FileCheck --check-prefix=ROPI-CMSE --check-prefix=RWPI-CMSE %s
+
+// RUN: %clang -target arm-none-eabi -march=armv8m.main -frwpi -mcmse -fallow-unsupported -### -c %s 2>&1 | FileCheck --check-prefix=RWPI-CMSE-ALLOW-UNSUPPORTED --check-prefix=ROPI-CMSE-ALLOW-UNSUPPORTED %s
+// RUN: %clang -target arm-none-eabi -march=armv8m.main -fropi -mcmse -fallow-unsupported -### -c %s 2>&1 | FileCheck --check-prefix=ROPI-CMSE-ALLOW-UNSUPPORTED %s
+// RUN: %clang -target arm-none-eabi -march=armv8m.main -frwpi -fropi -mcmse -fallow-unsupported -### -c %s 2>&1 | FileCheck --check-prefix=ROPI-CMSE-ALLOW-UNSUPPORTED --check-prefix=RWPI-CMSE-ALLOW-UNSUPPORTED %s
+
// STATIC: "-mrelocation-model" "static"
@@ -36,3 +44,8 @@
// PIC: error: embedded and GOT-based position independence are incompatible
// CXX: error: ROPI is not compatible with c++
+
+// ROPI-CMSE: error: cmse is not compatible with ROPI
+// RWPI-CMSE: error: cmse is not compatible with RWPI
+// ROPI-CMSE-ALLOW-UNSUPPORTED-NOT: error: cmse is not compatible with ROPI
+// RWPI-CMSE-ALLOW-UNSUPPORTED-NOT: error: cmse is not compatible with RWPI
diff --git a/clang/test/Driver/save-temps.c b/clang/test/Driver/save-temps.c
index 29d1b7d9ac8d..ae46f1e6b565 100644
--- a/clang/test/Driver/save-temps.c
+++ b/clang/test/Driver/save-temps.c
@@ -82,3 +82,11 @@
// RUN: | FileCheck %s -check-prefix=CHECK-SAVE-TEMPS
// CHECK-SAVE-TEMPS: "-cc1as"
// CHECK-SAVE-TEMPS: "-dwarf-version={{.}}"
+
+// RUN: %clang --target=arm-arm-none-eabi -march=armv8-m.main -mcmse -save-temps -c -v %s 2>&1 \
+// RUN: | FileCheck %s -check-prefix=CHECK-SAVE-TEMPS-CMSE
+// RUN: %clang --target=arm-arm-none-eabi -march=armv8-m.main -mcmse -x assembler -c -v %s 2>&1 \
+// RUN: | FileCheck %s -check-prefix=CHECK-SAVE-TEMPS-CMSE
+// CHECK-SAVE-TEMPS-CMSE: -cc1as
+// CHECK-SAVE-TEMPS-CMSE: +8msecext
+// CHECK-SAVE-TEMPS-CMSE-NOT: '+cmse' is not a recognized feature for this target (ignoring feature)
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index 11a70119d4c1..ffef2c717cce 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -39,6 +39,7 @@
// CHECK-NEXT: Callback (SubjectMatchRule_function)
// CHECK-NEXT: Capability (SubjectMatchRule_record, SubjectMatchRule_type_alias)
// CHECK-NEXT: CarriesDependency (SubjectMatchRule_variable_is_parameter, SubjectMatchRule_objc_method, SubjectMatchRule_function)
+// CHECK-NEXT: CmseNSEntry (SubjectMatchRule_function)
// CHECK-NEXT: Cold (SubjectMatchRule_function)
// CHECK-NEXT: Common (SubjectMatchRule_variable)
// CHECK-NEXT: ConstInit (SubjectMatchRule_variable_is_global)
diff --git a/clang/test/Sema/arm-cmse.c b/clang/test/Sema/arm-cmse.c
new file mode 100644
index 000000000000..2148cc1aeb96
--- /dev/null
+++ b/clang/test/Sema/arm-cmse.c
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -triple thumbv8m.base-none-eabi -mcmse -verify %s
+
+typedef void (*callback_ns_1t)() __attribute__((cmse_nonsecure_call));
+typedef void (*callback_1t)();
+typedef void (*callback_ns_2t)() __attribute__((cmse_nonsecure_call));
+typedef void (*callback_2t)();
+
+void foo(callback_ns_1t nsfptr, // expected-error{{functions may not be declared with 'cmse_nonsecure_call' attribute}}
+ callback_1t fptr) __attribute__((cmse_nonsecure_call))
+{
+ callback_1t fp1 = nsfptr; // expected-warning{{incompatible function pointer types initializing 'callback_1t'}}
+ callback_ns_1t fp2 = fptr; // expected-warning{{incompatible function pointer types initializing 'callback_ns_1t'}}
+ callback_2t fp3 = fptr;
+ callback_ns_2t fp4 = nsfptr;
+}
+
+static void bar() __attribute__((cmse_nonsecure_entry)) // expected-warning{{'cmse_nonsecure_entry' cannot be applied to functions with internal linkage}}
+{
+}
+
+typedef void nonsecure_fn_t(int) __attribute__((cmse_nonsecure_call));
+extern nonsecure_fn_t baz; // expected-error{{functions may not be declared with 'cmse_nonsecure_call' attribute}}
+
+int v0 __attribute__((cmse_nonsecure_call)); // expected-warning {{'cmse_nonsecure_call' only applies to function types; type here is 'int'}}
+int v1 __attribute__((cmse_nonsecure_entry)); // expected-warning {{'cmse_nonsecure_entry' attribute only applies to functions}}
+
+void fn0() __attribute__((cmse_nonsecure_entry));
+void fn1() __attribute__((cmse_nonsecure_entry(1))); // expected-error {{'cmse_nonsecure_entry' attribute takes no arguments}}
+
+typedef void (*fn2_t)() __attribute__((cmse_nonsecure_call("abc"))); // expected-error {{'cmse_nonsecure_call' attribute takes no argument}}
diff --git a/clang/test/Sema/arm-no-cmse.c b/clang/test/Sema/arm-no-cmse.c
new file mode 100644
index 000000000000..9b4baae56d1f
--- /dev/null
+++ b/clang/test/Sema/arm-no-cmse.c
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 -triple thumbv8m.base-none-eabi -verify %s
+
+typedef void (*callback_ns_1t)()
+ __attribute__((cmse_nonsecure_call)); // expected-warning{{'cmse_nonsecure_call' attribute ignored}}
+
+void f()
+ __attribute__((cmse_nonsecure_entry)) {} // expected-warning{{'cmse_nonsecure_entry' attribute ignored}}
diff --git a/clang/test/SemaCXX/arm-cmse.cpp b/clang/test/SemaCXX/arm-cmse.cpp
new file mode 100644
index 000000000000..dbf97e2f4a3b
--- /dev/null
+++ b/clang/test/SemaCXX/arm-cmse.cpp
@@ -0,0 +1,5 @@
+// RUN: %clang_cc1 -triple thumbv8m.base-none-eabi -mcmse -verify %s
+
+extern "C" void foo() __attribute__((cmse_nonsecure_entry)) {}
+
+void bar() __attribute__((cmse_nonsecure_entry)) {} // expected-error{{function type with 'cmse_nonsecure_entry' attribute must have C linkage}}
More information about the cfe-commits
mailing list