[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
This commit is contained in:
Momchil Velikov 2020-03-24 09:32:51 +00:00
parent 1232cfa385
commit 080d046c91
24 changed files with 367 additions and 25 deletions

View File

@ -1546,7 +1546,7 @@ protected:
/// 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 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
/// 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); }

View File

@ -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;

View File

@ -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]>;

View File

@ -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.
}];
}

View File

@ -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>;

View File

@ -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<

View File

@ -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 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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()) {

View File

@ -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:

View File

@ -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)

View File

@ -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 ((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);

View File

@ -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);

View File

@ -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:

View File

@ -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;
}

View File

@ -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) {

View File

@ -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

View File

@ -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))

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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}}

View File

@ -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}}

View File

@ -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}}