forked from OSchip/llvm-project
[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:
parent
1232cfa385
commit
080d046c91
|
@ -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); }
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]>;
|
||||
|
|
|
@ -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.
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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<
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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"
|
|
@ -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"
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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}}
|
|
@ -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}}
|
|
@ -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}}
|
Loading…
Reference in New Issue