Index: clang/include/clang/AST/Type.h =================================================================== --- clang/include/clang/AST/Type.h +++ clang/include/clang/AST/Type.h @@ -1542,7 +1542,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. /// @@ -3510,8 +3510,8 @@ // you'll need to adjust both the Bits field below and // Type::FunctionTypeBitfields. - // | 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 }; @@ -3519,9 +3519,10 @@ 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; @@ -3532,35 +3533,38 @@ // 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 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); - } - - // Constructor with all defaults. Use when for example creating a - // function known to use defaults. - ExtInfo() = default; - - // Constructor with just the calling convention, which is an important part - // of the canonical type. - ExtInfo(CallingConv CC) : Bits(CC) {} - - bool getNoReturn() const { return Bits & NoReturnMask; } - bool getProducesResult() const { return Bits & ProducesResultMask; } - bool getNoCallerSavedRegs() const { return Bits & NoCallerSavedRegsMask; } - bool getNoCfCheck() const { return Bits & NoCfCheckMask; } - bool getHasRegParm() const { return (Bits >> RegParmOffset) != 0; } - - unsigned getRegParm() const { - unsigned RegParm = (Bits & RegParmMask) >> RegParmOffset; - if (RegParm > 0) - --RegParm; - return RegParm; - } + (NoCfCheck ? NoCfCheckMask : 0) | + (cmseNSCall ? CmseNSCallMask : 0); + } + + // Constructor with all defaults. Use when for example creating a + // function known to use defaults. + ExtInfo() = default; + + // Constructor with just the calling convention, which is an important part + // of the canonical type. + ExtInfo(CallingConv CC) : Bits(CC) {} + + 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; } + + unsigned getRegParm() const { + unsigned RegParm = (Bits & RegParmMask) >> RegParmOffset; + if (RegParm > 0) + --RegParm; + return RegParm; + } CallingConv getCC() const { return CallingConv(Bits & CallConvMask); } @@ -3588,6 +3592,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); @@ -3660,6 +3671,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); } Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -150,6 +150,14 @@ isa(S)}], "non-K&R-style functions">; +def FunctionTypeDef : SubsetSubject(S) && + dyn_cast(S)->getType()->isFunctionPointerType()) || + (isa(S) && + (dyn_cast(S)->getUnderlyingType()->isFunctionPointerType() || + isa(dyn_cast(S)->getUnderlyingType())))}], + "function types">; + // A subject that matches the implicit object parameter of a non-static member // function. Accepted as a function type attribute on the type of such a // member function. @@ -924,6 +932,21 @@ let Documentation = [Undocumented]; } +def CmseNSEntry : InheritableAttr, TargetSpecificAttr { + let Spellings = [GNU<"cmse_nonsecure_entry">]; + let Subjects = SubjectList<[Function]>; + let LangOpts = [Cmse]; + let Documentation = [Undocumented]; +} + +def CmseNSCall : InheritableAttr, TargetSpecificAttr { + let Spellings = [GNU<"cmse_nonsecure_call">]; + let Subjects = SubjectList<[FunctionTypeDef], WarnDiag, + "ExpectedFunctionType">; + let LangOpts = [Cmse]; + let Documentation = [Undocumented]; +} + def Cold : InheritableAttr { let Spellings = [GCC<"cold">]; let Subjects = SubjectList<[Function]>; Index: clang/include/clang/Basic/DiagnosticDriverKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticDriverKinds.td +++ clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -368,6 +368,11 @@ def err_stack_tagging_requires_hardware_feature : Error< "'-fsanitize=memtag' requires hardware support (+memtag)">; +def err_cmse_rwpi_are_incompatible : Error< + "cmse is not compatible with rwpi">; +def err_cmse_ropi_are_incompatible : Error< + "cmse is not compatible with ropi">; + def warn_target_unsupported_nan2008 : Warning< "ignoring '-mnan=2008' option because the '%0' architecture does not support it">, InGroup; Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2765,6 +2765,8 @@ 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_attribute_address_function_type : Error< "function type may not be qualified with an address space">; def err_as_qualified_auto_decl : Error< @@ -2979,6 +2981,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< @@ -3044,6 +3049,7 @@ def warn_attribute_wrong_decl_type : Warning< "%0 attribute only applies to %select{" "functions" + "|function types" "|unions" "|variables and functions" "|functions and methods" Index: clang/include/clang/CodeGen/CGFunctionInfo.h =================================================================== --- clang/include/clang/CodeGen/CGFunctionInfo.h +++ clang/include/clang/CodeGen/CGFunctionInfo.h @@ -498,6 +498,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; @@ -589,6 +592,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 @@ -626,7 +631,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; } @@ -667,6 +673,7 @@ ID.AddBoolean(HasRegParm); ID.AddInteger(RegParm); ID.AddBoolean(NoCfCheck); + ID.AddBoolean(CmseNSCall); ID.AddInteger(Required.getOpaqueData()); ID.AddBoolean(HasExtParameterInfos); if (HasExtParameterInfos) { @@ -694,6 +701,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()) { Index: clang/include/clang/Sema/ParsedAttr.h =================================================================== --- clang/include/clang/Sema/ParsedAttr.h +++ clang/include/clang/Sema/ParsedAttr.h @@ -940,6 +940,7 @@ /// warn_attribute_wrong_decl_type and err_attribute_wrong_decl_type. enum AttributeDeclKind { ExpectedFunction, + ExpectedFunctionType, ExpectedUnion, ExpectedVariableOrFunction, ExpectedFunctionOrMethod, Index: clang/lib/AST/TypePrinter.cpp =================================================================== --- clang/lib/AST/TypePrinter.cpp +++ 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()) Index: clang/lib/CodeGen/CGCall.cpp =================================================================== --- clang/lib/CodeGen/CGCall.cpp +++ clang/lib/CodeGen/CGCall.cpp @@ -814,6 +814,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(); @@ -1842,6 +1843,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, @@ -1964,6 +1968,9 @@ } if (!AttrOnCallSite) { + if (TargetDecl && TargetDecl->hasAttr()) + FuncAttrs.addAttribute("cmse_nonsecure_entry"); + bool DisableTailCalls = false; if (CodeGenOpts.DisableTailCalls) Index: clang/lib/Driver/ToolChains/Clang.cpp =================================================================== --- clang/lib/Driver/ToolChains/Clang.cpp +++ clang/lib/Driver/ToolChains/Clang.cpp @@ -4132,6 +4132,18 @@ const char *RMName = RelocationModelName(RelocationModel); + if ((RelocationModel == llvm::Reloc::ROPI || + RelocationModel == llvm::Reloc::ROPI_RWPI) && + Args.hasArg(options::OPT_mcmse) && + !Args.hasArg(options::OPT_fallow_unsupported)) + D.Diag(diag::err_cmse_ropi_are_incompatible); + + if ((RelocationModel == llvm::Reloc::RWPI || + RelocationModel == llvm::Reloc::ROPI_RWPI) && + Args.hasArg(options::OPT_mcmse) && + !Args.hasArg(options::OPT_fallow_unsupported)) + D.Diag(diag::err_cmse_rwpi_are_incompatible); + if ((RelocationModel == llvm::Reloc::ROPI || RelocationModel == llvm::Reloc::ROPI_RWPI) && types::isCXX(Input.getType()) && Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -1989,6 +1989,15 @@ 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.getAttrName(); + 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; @@ -7113,6 +7122,12 @@ case ParsedAttr::AT_NoInstrumentFunction: // Interacts with -pg. handleSimpleAttribute(S, D, AL); break; + case ParsedAttr::AT_CmseNSEntry: + handleCmseNSEntryAttr(S, D, AL); + break; + case ParsedAttr::AT_CmseNSCall: + handleSimpleAttribute(S, D, AL); + break; case ParsedAttr::AT_NoStackProtector: // Interacts with -fstack-protector options. handleSimpleAttribute(S, D, AL); Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -7909,6 +7909,25 @@ 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) { + CanQualType CanTo = S.Context.getCanonicalType(ToType); + CanQualType CanFrom = S.Context.getCanonicalType(FromType); + + if (!isa(CanTo) || !isa(CanFrom)) + return false; + + const FunctionType *ToFn = cast(CanTo); + const FunctionType *FromFn = cast(CanFrom); + FunctionType::ExtInfo ToEInfo = ToFn->getExtInfo(); + FunctionType::ExtInfo FromEInfo = FromFn->getExtInfo(); + + return (ToEInfo.getCmseNSCall() != FromEInfo.getCmseNSCall()); +} + // 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. @@ -8045,6 +8064,8 @@ if (!S.getLangOpts().CPlusPlus && S.IsFunctionConversion(ltrans, rtrans, ltrans)) return Sema::IncompatiblePointer; + if (IsInvalidCmseNSCallConversion(S, ltrans, rtrans)) + return Sema::IncompatiblePointer; return ConvTy; } Index: clang/lib/Sema/SemaType.cpp =================================================================== --- clang/lib/Sema/SemaType.cpp +++ clang/lib/Sema/SemaType.cpp @@ -128,6 +128,8 @@ case ParsedAttr::AT_NSReturnsRetained: \ case ParsedAttr::AT_NoReturn: \ case ParsedAttr::AT_Regparm: \ + case ParsedAttr::AT_CmseNSEntry: \ + case ParsedAttr::AT_CmseNSCall: \ case ParsedAttr::AT_AnyX86NoCallerSavedRegisters: \ case ParsedAttr::AT_AnyX86NoCfCheck: \ CALLING_CONV_ATTRS_CASELIST @@ -6893,6 +6895,59 @@ 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.getAttrName(); + attr.setInvalid(); + return true; + } + + // Check that we are attached to a function type + if (state.getDeclarator().getTypeObject(0).Kind != + clang::DeclaratorChunk::Pointer && + state.getDeclarator().getDeclSpec().getStorageClassSpec() != + DeclSpec::SCS_typedef) { + S.Diag(attr.getLoc(), diag::warn_attribute_wrong_decl_type) + << attr.getAttrName() << ExpectedFunctionType; + 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; + } + + if (attr.getKind() == ParsedAttr::AT_CmseNSEntry) { + // 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.getAttrName(); + attr.setInvalid(); + return true; + } + + if (state.getDeclarator().getDeclSpec().getStorageClassSpec() == + DeclSpec::SCS_static) { + S.Diag(attr.getLoc(), diag::warn_attribute_cmse_entry_static); + attr.setInvalid(); + return true; + } + + // Otherwise we can process right away. + 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) { Index: clang/lib/Serialization/ASTReader.cpp =================================================================== --- clang/lib/Serialization/ASTReader.cpp +++ clang/lib/Serialization/ASTReader.cpp @@ -6500,14 +6500,14 @@ } case TYPE_FUNCTION_NO_PROTO: { - if (Record.size() != 8) { + if (Record.size() != 9) { Error("incorrect encoding of no-proto function type"); return QualType(); } QualType ResultType = readType(*Loc.F, Record, Idx); FunctionType::ExtInfo Info(Record[1], Record[2], Record[3], (CallingConv)Record[4], Record[5], Record[6], - Record[7]); + Record[7], Record[8]); return Context.getFunctionNoProtoType(ResultType, Info); } @@ -6521,9 +6521,10 @@ static_cast(Record[4]), /*produces*/ Record[5], /*nocallersavedregs*/ Record[6], - /*nocfcheck*/ Record[7]); + /*nocfcheck*/ Record[7], + /*cmsenscall*/ Record[8]); - unsigned Idx = 8; + unsigned Idx = 9; EPI.Variadic = Record[Idx++]; EPI.HasTrailingReturn = Record[Idx++]; Index: clang/lib/Serialization/ASTWriter.cpp =================================================================== --- clang/lib/Serialization/ASTWriter.cpp +++ clang/lib/Serialization/ASTWriter.cpp @@ -278,6 +278,7 @@ Record.push_back(C.getProducesResult()); Record.push_back(C.getNoCallerSavedRegs()); Record.push_back(C.getNoCfCheck()); + Record.push_back(C.getCmseNSCall()); if (C.getHasRegParm() || C.getRegParm() || C.getProducesResult()) AbbrevToUse = 0; @@ -900,6 +901,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 Index: clang/test/AST/ast-dump-arm-attr.c =================================================================== --- clang/test/AST/ast-dump-arm-attr.c +++ 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)) Index: clang/test/CodeGen/arm-cmse-attr.c =================================================================== --- /dev/null +++ clang/test/CodeGen/arm-cmse-attr.c @@ -0,0 +1,42 @@ +// 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)) +{ +} + +// CHECK-NOSE: warning: 'cmse_nonsecure_call' attribute ignored +// CHECK-NOSE: warning: 'cmse_nonsecure_entry' attribute ignored +// CHECK-SE-NOT: warning: 'cmse_nonsecure_call' attribute ignored +// CHECK-SE-NOT: warning: 'cmse_nonsecure_entry' attribute ignored + +// 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-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" Index: clang/test/CodeGen/arm-cmse-call.c =================================================================== --- /dev/null +++ clang/test/CodeGen/arm-cmse-call.c @@ -0,0 +1,39 @@ +// RUN: %clang_cc1 -triple thumbv8m.base-none-eabi -O1 -emit-llvm %s -o - 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-NOSE +// RUN: %clang_cc1 -triple thumbebv8m.base-none-eabi -O1 -emit-llvm %s -o - 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-NOSE +// RUN: %clang_cc1 -triple thumbv8m.base-none-eabi -mcmse -O1 -emit-llvm %s -o - 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-SE +// RUN: %clang_cc1 -triple thumbebv8m.base-none-eabi -mcmse -O1 -emit-llvm %s -o - 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-SE + +#include + +typedef void (*callback_t)(void); + +typedef void __attribute__((cmse_nonsecure_call)) (*ns_callback_t)(void); + +void foo(callback_t fptr) __attribute__((cmse_nonsecure_entry)) +{ + if (cmse_nonsecure_caller()) + fptr = cmse_nsfptr_create(fptr); + + if (cmse_is_nsfptr(fptr)) { + ((ns_callback_t)fptr)(); + } else { + fptr(); + } +} + +// CHECK-NOSE: warning: 'cmse_nonsecure_call' attribute ignored +// CHECK-NOSE: warning: 'cmse_nonsecure_entry' attribute ignored + +// CHECK-SE: define void @foo(void ()* %fptr) {{[^#]*}}#[[A1:[0-9]]] { +// CHECK-SE: call i8* @llvm.returnaddress(i32 0) #[[A2:[0-9]]] +// CHECK-SE: %[[FN:.*]] = inttoptr i32 {{.*}} to void ()* +// CHECK-SE: call void %[[FN]]() #[[A3:[0-9]]] +// CHECK-SE: call void %[[FN]]() #[[A2:[0-9]]] + +// CHECK-SE: attributes #[[A1]] = { nounwind "cmse_nonsecure_entry" +// CHECK-SE: attributes #[[A2]] = { nounwind } +// CHECK-SE: attributes #[[A3]] = { nounwind "cmse_nonsecure_call" Index: clang/test/CodeGen/arm-cmse-diag.c =================================================================== --- /dev/null +++ clang/test/CodeGen/arm-cmse-diag.c @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -triple thumbv8m.base-none-eabi -mcmse -emit-llvm %s -o - 2>&1 | \ +// RUN: FileCheck %s +// RUN: %clang_cc1 -triple thumbebv8m.base-none-eabi -mcmse -emit-llvm %s -o - 2>&1 | \ +// RUN: FileCheck %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, + callback_1t fptr) __attribute__((cmse_nonsecure_call)) +{ + callback_1t fp1 = nsfptr; + callback_ns_1t fp2 = fptr; + callback_2t fp3 = fptr; + callback_ns_2t fp4 = nsfptr; +} + +static void bar() __attribute__((cmse_nonsecure_entry)) +{ +} + +// CHECK: warning: 'cmse_nonsecure_call' attribute only applies to function types +// CHECK: warning: incompatible function pointer types initializing 'callback_1t' +// CHECK: warning: incompatible function pointer types initializing 'callback_ns_1t' +// CHECK-NOT: warning: incompatible pointer types initializing 'callback_2t' +// CHECK-NOT: warning: incompatible pointer types initializing 'callback_ns_2t' +// CHECK: warning: cmse_nonsecure_entry cannot be applied to functions with internal linkage + +// CHECK-NOT: attributes #[[A:[0-9]]] = { cmse_nonsecure_call Index: clang/test/CodeGenCXX/arm-cmse-diag.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/arm-cmse-diag.cpp @@ -0,0 +1,15 @@ +// RUN: not %clang_cc1 -triple thumbv8m.base-none-eabi -mcmse -emit-llvm %s -o - 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-CXX +// RUN: not %clang_cc1 -triple thumbebv8m.base-none-eabi -mcmse -emit-llvm %s -o - 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-CXX +// RUN: %clang_cc1 -triple thumbv8m.base-none-eabi -mcmse -emit-llvm -xc %s -o - 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-C +// RUN: %clang_cc1 -triple thumbebv8m.base-none-eabi -mcmse -emit-llvm -xc %s -o - 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-C + +void func() __attribute__((cmse_nonsecure_entry)) +{ +} + +// CHECK-CXX: 'cmse_nonsecure_entry' attribute must have C linkage +// CHECK-C-NOT: 'cmse_nonsecure_entry' attribute must have C linkage Index: clang/test/CodeGenCXX/arm-cmse.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/arm-cmse.cpp @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple thumbv8m.base-none-eabi -mcmse -emit-llvm %s -o - 2>&1 | \ +// RUN: FileCheck %s +// RUN: %clang_cc1 -triple thumbebv8m.base-none-eabi -mcmse -emit-llvm %s -o - 2>&1 | \ +// RUN: FileCheck %s + +extern "C" void func() __attribute__((cmse_nonsecure_entry)) { +} + +// CHECK-NOT: 'cmse_nonsecure_entry' attribute must have C linkage Index: clang/test/Driver/ropi-rwpi.c =================================================================== --- clang/test/Driver/ropi-rwpi.c +++ 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 Index: clang/test/Driver/save-temps.c =================================================================== --- clang/test/Driver/save-temps.c +++ 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) Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test =================================================================== --- clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -38,6 +38,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)