diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -1546,7 +1546,7 @@ /// 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 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 @@ 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(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 @@ 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 @@ 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 @@ /// 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 --- a/clang/include/clang/AST/TypeProperties.td +++ b/clang/include/clang/AST/TypeProperties.td @@ -249,13 +249,17 @@ 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 @@ 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 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -962,6 +962,19 @@ let Documentation = [Undocumented]; } +def CmseNSEntry : InheritableAttr, TargetSpecificAttr { + let Spellings = [GNU<"cmse_nonsecure_entry">]; + let Subjects = SubjectList<[Function]>; + let LangOpts = [Cmse]; + let Documentation = [ArmCmseNSEntryDocs]; +} + +def CmseNSCall : TypeAttr, TargetSpecificAttr { + 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 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -4853,3 +4853,28 @@ }]; } + +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 +`_ 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 +`_ for more information. + }]; +} diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -377,6 +377,9 @@ 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; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2894,6 +2894,10 @@ def warn_attribute_address_multiple_identical_qualifiers : Warning< "multiple identical address spaces specified for type">, InGroup; +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 @@ InGroup; 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; 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 --- a/clang/include/clang/CodeGen/CGFunctionInfo.h +++ b/clang/include/clang/CodeGen/CGFunctionInfo.h @@ -508,6 +508,9 @@ /// 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 @@ 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 @@ 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 @@ 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 @@ 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 --- 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 --- 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()) + 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 --- 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 (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 --- 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()->getCmseNSCallAttr()) + Diag(D.getIdentifierLoc(), diag::err_function_decl_cmse_ns_call); + SmallVector TemplateParamLists; for (TemplateParameterList *TPL : TemplateParamListsRef) TemplateParamLists.push_back(TPL); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- 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(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(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 --- 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(S.Context.getCanonicalType(ToType))) { + if (const auto *FromFn = + dyn_cast(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 --- 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 --- 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 diff --git a/clang/test/AST/ast-dump-arm-attr.c b/clang/test/AST/ast-dump-arm-attr.c --- 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 --- /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 --- /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 --- 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 --- 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 --- 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 --- /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 --- /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 --- /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}}