Skip to content

Commit 080d046

Browse files
[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
1 parent 1232cfa commit 080d046

24 files changed

+367
-25
lines changed

clang/include/clang/AST/Type.h

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1546,7 +1546,7 @@ class alignas(8) Type : public ExtQualsTypeCommonBase {
15461546

15471547
/// Extra information which affects how the function is called, like
15481548
/// regparm and the calling convention.
1549-
unsigned ExtInfo : 12;
1549+
unsigned ExtInfo : 13;
15501550

15511551
/// The ref-qualifier associated with a \c FunctionProtoType.
15521552
///
@@ -3568,39 +3568,42 @@ class FunctionType : public Type {
35683568
class ExtInfo {
35693569
friend class FunctionType;
35703570

3571-
// Feel free to rearrange or add bits, but if you go over 12,
3572-
// you'll need to adjust both the Bits field below and
3573-
// Type::FunctionTypeBitfields.
3571+
// Feel free to rearrange or add bits, but if you go over 16, you'll need to
3572+
// adjust the Bits field below, and if you add bits, you'll need to adjust
3573+
// Type::FunctionTypeBitfields::ExtInfo as well.
35743574

3575-
// | CC |noreturn|produces|nocallersavedregs|regparm|nocfcheck|
3576-
// |0 .. 4| 5 | 6 | 7 |8 .. 10| 11 |
3575+
// | CC |noreturn|produces|nocallersavedregs|regparm|nocfcheck|cmsenscall|
3576+
// |0 .. 4| 5 | 6 | 7 |8 .. 10| 11 | 12 |
35773577
//
35783578
// regparm is either 0 (no regparm attribute) or the regparm value+1.
35793579
enum { CallConvMask = 0x1F };
35803580
enum { NoReturnMask = 0x20 };
35813581
enum { ProducesResultMask = 0x40 };
35823582
enum { NoCallerSavedRegsMask = 0x80 };
35833583
enum { NoCfCheckMask = 0x800 };
3584+
enum { CmseNSCallMask = 0x1000 };
35843585
enum {
35853586
RegParmMask = ~(CallConvMask | NoReturnMask | ProducesResultMask |
3586-
NoCallerSavedRegsMask | NoCfCheckMask),
3587+
NoCallerSavedRegsMask | NoCfCheckMask | CmseNSCallMask),
35873588
RegParmOffset = 8
35883589
}; // Assumed to be the last field
35893590
uint16_t Bits = CC_C;
35903591

35913592
ExtInfo(unsigned Bits) : Bits(static_cast<uint16_t>(Bits)) {}
35923593

3593-
public:
3594-
// Constructor with no defaults. Use this when you know that you
3595-
// have all the elements (when reading an AST file for example).
3596-
ExtInfo(bool noReturn, bool hasRegParm, unsigned regParm, CallingConv cc,
3597-
bool producesResult, bool noCallerSavedRegs, bool NoCfCheck) {
3598-
assert((!hasRegParm || regParm < 7) && "Invalid regparm value");
3599-
Bits = ((unsigned)cc) | (noReturn ? NoReturnMask : 0) |
3600-
(producesResult ? ProducesResultMask : 0) |
3601-
(noCallerSavedRegs ? NoCallerSavedRegsMask : 0) |
3602-
(hasRegParm ? ((regParm + 1) << RegParmOffset) : 0) |
3603-
(NoCfCheck ? NoCfCheckMask : 0);
3594+
public:
3595+
// Constructor with no defaults. Use this when you know that you
3596+
// have all the elements (when reading an AST file for example).
3597+
ExtInfo(bool noReturn, bool hasRegParm, unsigned regParm, CallingConv cc,
3598+
bool producesResult, bool noCallerSavedRegs, bool NoCfCheck,
3599+
bool cmseNSCall) {
3600+
assert((!hasRegParm || regParm < 7) && "Invalid regparm value");
3601+
Bits = ((unsigned)cc) | (noReturn ? NoReturnMask : 0) |
3602+
(producesResult ? ProducesResultMask : 0) |
3603+
(noCallerSavedRegs ? NoCallerSavedRegsMask : 0) |
3604+
(hasRegParm ? ((regParm + 1) << RegParmOffset) : 0) |
3605+
(NoCfCheck ? NoCfCheckMask : 0) |
3606+
(cmseNSCall ? CmseNSCallMask : 0);
36043607
}
36053608

36063609
// Constructor with all defaults. Use when for example creating a
@@ -3613,6 +3616,7 @@ class FunctionType : public Type {
36133616

36143617
bool getNoReturn() const { return Bits & NoReturnMask; }
36153618
bool getProducesResult() const { return Bits & ProducesResultMask; }
3619+
bool getCmseNSCall() const { return Bits & CmseNSCallMask; }
36163620
bool getNoCallerSavedRegs() const { return Bits & NoCallerSavedRegsMask; }
36173621
bool getNoCfCheck() const { return Bits & NoCfCheckMask; }
36183622
bool getHasRegParm() const { return (Bits >> RegParmOffset) != 0; }
@@ -3650,6 +3654,13 @@ class FunctionType : public Type {
36503654
return ExtInfo(Bits & ~ProducesResultMask);
36513655
}
36523656

3657+
ExtInfo withCmseNSCall(bool cmseNSCall) const {
3658+
if (cmseNSCall)
3659+
return ExtInfo(Bits | CmseNSCallMask);
3660+
else
3661+
return ExtInfo(Bits & ~CmseNSCallMask);
3662+
}
3663+
36533664
ExtInfo withNoCallerSavedRegs(bool noCallerSavedRegs) const {
36543665
if (noCallerSavedRegs)
36553666
return ExtInfo(Bits | NoCallerSavedRegsMask);
@@ -3722,6 +3733,7 @@ class FunctionType : public Type {
37223733
/// type.
37233734
bool getNoReturnAttr() const { return getExtInfo().getNoReturn(); }
37243735

3736+
bool getCmseNSCallAttr() const { return getExtInfo().getCmseNSCall(); }
37253737
CallingConv getCallConv() const { return getExtInfo().getCC(); }
37263738
ExtInfo getExtInfo() const { return ExtInfo(FunctionTypeBits.ExtInfo); }
37273739

clang/include/clang/AST/TypeProperties.td

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,13 +249,17 @@ let Class = FunctionType in {
249249
def : Property<"noCfCheck", Bool> {
250250
let Read = [{ node->getExtInfo().getNoCfCheck() }];
251251
}
252+
def : Property<"cmseNSCall", Bool> {
253+
let Read = [{ node->getExtInfo().getCmseNSCall() }];
254+
}
252255
}
253256

254257
let Class = FunctionNoProtoType in {
255258
def : Creator<[{
256259
auto extInfo = FunctionType::ExtInfo(noReturn, hasRegParm, regParm,
257260
callingConvention, producesResult,
258-
noCallerSavedRegs, noCfCheck);
261+
noCallerSavedRegs, noCfCheck,
262+
cmseNSCall);
259263
return ctx.getFunctionNoProtoType(returnType, extInfo);
260264
}]>;
261265
}
@@ -288,7 +292,8 @@ let Class = FunctionProtoType in {
288292
def : Creator<[{
289293
auto extInfo = FunctionType::ExtInfo(noReturn, hasRegParm, regParm,
290294
callingConvention, producesResult,
291-
noCallerSavedRegs, noCfCheck);
295+
noCallerSavedRegs, noCfCheck,
296+
cmseNSCall);
292297
FunctionProtoType::ExtProtoInfo epi;
293298
epi.ExtInfo = extInfo;
294299
epi.Variadic = variadic;

clang/include/clang/Basic/Attr.td

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,19 @@ def Cleanup : InheritableAttr {
962962
let Documentation = [Undocumented];
963963
}
964964

965+
def CmseNSEntry : InheritableAttr, TargetSpecificAttr<TargetARM> {
966+
let Spellings = [GNU<"cmse_nonsecure_entry">];
967+
let Subjects = SubjectList<[Function]>;
968+
let LangOpts = [Cmse];
969+
let Documentation = [ArmCmseNSEntryDocs];
970+
}
971+
972+
def CmseNSCall : TypeAttr, TargetSpecificAttr<TargetARM> {
973+
let Spellings = [GNU<"cmse_nonsecure_call">];
974+
let LangOpts = [Cmse];
975+
let Documentation = [ArmCmseNSCallDocs];
976+
}
977+
965978
def Cold : InheritableAttr {
966979
let Spellings = [GCC<"cold">];
967980
let Subjects = SubjectList<[Function]>;

clang/include/clang/Basic/AttrDocs.td

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4853,3 +4853,28 @@ other than overloading.
48534853

48544854
}];
48554855
}
4856+
4857+
def ArmCmseNSCallDocs : Documentation {
4858+
let Category = DocCatType;
4859+
let Content = [{
4860+
This attribute declares a non-secure function type. When compiling for secure
4861+
state, a call to such a function would switch from secure to non-secure state.
4862+
All non-secure function calls must happen only through a function pointer, and
4863+
a non-secure function type should only be used as a base type of a pointer.
4864+
See `ARMv8-M Security Extensions: Requirements on Development
4865+
Tools - Engineering Specification Documentation
4866+
<https://developer.arm.com/docs/ecm0359818/latest/>`_ for more information.
4867+
}];
4868+
}
4869+
4870+
def ArmCmseNSEntryDocs : Documentation {
4871+
let Category = DocCatFunction;
4872+
let Content = [{
4873+
This attribute declares a function that can be called from non-secure state, or
4874+
from secure state. Entering from and returning to non-secure state would switch
4875+
to and from secure state, respectively, and prevent flow of information
4876+
to non-secure state, except via return values. See `ARMv8-M Security Extensions:
4877+
Requirements on Development Tools - Engineering Specification Documentation
4878+
<https://developer.arm.com/docs/ecm0359818/latest/>`_ for more information.
4879+
}];
4880+
}

clang/include/clang/Basic/DiagnosticDriverKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,9 @@ def err_drv_ropi_incompatible_with_cxx : Error<
377377
def err_stack_tagging_requires_hardware_feature : Error<
378378
"'-fsanitize=memtag' requires hardware support (+memtag)">;
379379

380+
def err_cmse_pi_are_incompatible : Error<
381+
"cmse is not compatible with %select{RWPI|ROPI}0">;
382+
380383
def warn_target_unsupported_nan2008 : Warning<
381384
"ignoring '-mnan=2008' option because the '%0' architecture does not support it">,
382385
InGroup<UnsupportedNan>;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2894,6 +2894,10 @@ def err_attribute_address_multiple_qualifiers : Error<
28942894
def warn_attribute_address_multiple_identical_qualifiers : Warning<
28952895
"multiple identical address spaces specified for type">,
28962896
InGroup<DuplicateDeclSpecifier>;
2897+
def err_attribute_not_clinkage : Error<
2898+
"function type with %0 attribute must have C linkage">;
2899+
def err_function_decl_cmse_ns_call : Error<
2900+
"functions may not be declared with 'cmse_nonsecure_call' attribute">;
28972901
def err_attribute_address_function_type : Error<
28982902
"function type may not be qualified with an address space">;
28992903
def err_as_qualified_auto_decl : Error<
@@ -3114,6 +3118,9 @@ def warn_attribute_weak_on_local : Warning<
31143118
InGroup<IgnoredAttributes>;
31153119
def warn_weak_identifier_undeclared : Warning<
31163120
"weak identifier %0 never declared">;
3121+
def warn_attribute_cmse_entry_static : Warning<
3122+
"'cmse_nonsecure_entry' cannot be applied to functions with internal linkage">,
3123+
InGroup<IgnoredAttributes>;
31173124
def err_attribute_weak_static : Error<
31183125
"weak declaration cannot have internal linkage">;
31193126
def err_attribute_selectany_non_extern_data : Error<

clang/include/clang/CodeGen/CGFunctionInfo.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,9 @@ class CGFunctionInfo final
508508
/// Whether this is a chain call.
509509
unsigned ChainCall : 1;
510510

511+
/// Whether this function is a CMSE nonsecure call
512+
unsigned CmseNSCall : 1;
513+
511514
/// Whether this function is noreturn.
512515
unsigned NoReturn : 1;
513516

@@ -598,6 +601,8 @@ class CGFunctionInfo final
598601

599602
bool isChainCall() const { return ChainCall; }
600603

604+
bool isCmseNSCall() const { return CmseNSCall; }
605+
601606
bool isNoReturn() const { return NoReturn; }
602607

603608
/// In ARC, whether this function retains its return value. This
@@ -635,7 +640,8 @@ class CGFunctionInfo final
635640
FunctionType::ExtInfo getExtInfo() const {
636641
return FunctionType::ExtInfo(isNoReturn(), getHasRegParm(), getRegParm(),
637642
getASTCallingConvention(), isReturnsRetained(),
638-
isNoCallerSavedRegs(), isNoCfCheck());
643+
isNoCallerSavedRegs(), isNoCfCheck(),
644+
isCmseNSCall());
639645
}
640646

641647
CanQualType getReturnType() const { return getArgsBuffer()[0].type; }
@@ -676,6 +682,7 @@ class CGFunctionInfo final
676682
ID.AddBoolean(HasRegParm);
677683
ID.AddInteger(RegParm);
678684
ID.AddBoolean(NoCfCheck);
685+
ID.AddBoolean(CmseNSCall);
679686
ID.AddInteger(Required.getOpaqueData());
680687
ID.AddBoolean(HasExtParameterInfos);
681688
if (HasExtParameterInfos) {
@@ -703,6 +710,7 @@ class CGFunctionInfo final
703710
ID.AddBoolean(info.getHasRegParm());
704711
ID.AddInteger(info.getRegParm());
705712
ID.AddBoolean(info.getNoCfCheck());
713+
ID.AddBoolean(info.getCmseNSCall());
706714
ID.AddInteger(required.getOpaqueData());
707715
ID.AddBoolean(!paramInfos.empty());
708716
if (!paramInfos.empty()) {

clang/lib/AST/TypePrinter.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,8 @@ void TypePrinter::printFunctionAfter(const FunctionType::ExtInfo &Info,
909909

910910
if (Info.getNoReturn())
911911
OS << " __attribute__((noreturn))";
912+
if (Info.getCmseNSCall())
913+
OS << " __attribute__((cmse_nonsecure_call))";
912914
if (Info.getProducesResult())
913915
OS << " __attribute__((ns_returns_retained))";
914916
if (Info.getRegParm())
@@ -1519,6 +1521,7 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
15191521
case attr::SPtr:
15201522
case attr::UPtr:
15211523
case attr::AddressSpace:
1524+
case attr::CmseNSCall:
15221525
llvm_unreachable("This attribute should have been handled already");
15231526

15241527
case attr::NSReturnsRetained:

clang/lib/CodeGen/CGCall.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,7 @@ CGFunctionInfo *CGFunctionInfo::create(unsigned llvmCC,
815815
FI->ASTCallingConvention = info.getCC();
816816
FI->InstanceMethod = instanceMethod;
817817
FI->ChainCall = chainCall;
818+
FI->CmseNSCall = info.getCmseNSCall();
818819
FI->NoReturn = info.getNoReturn();
819820
FI->ReturnsRetained = info.getProducesResult();
820821
FI->NoCallerSavedRegs = info.getNoCallerSavedRegs();
@@ -1877,6 +1878,9 @@ void CodeGenModule::ConstructAttributeList(
18771878
if (FI.isNoReturn())
18781879
FuncAttrs.addAttribute(llvm::Attribute::NoReturn);
18791880

1881+
if (FI.isCmseNSCall())
1882+
FuncAttrs.addAttribute("cmse_nonsecure_call");
1883+
18801884
// If we have information about the function prototype, we can learn
18811885
// attributes from there.
18821886
AddAttributesFromFunctionProtoType(getContext(), FuncAttrs,
@@ -2004,6 +2008,9 @@ void CodeGenModule::ConstructAttributeList(
20042008
}
20052009

20062010
if (!AttrOnCallSite) {
2011+
if (TargetDecl && TargetDecl->hasAttr<CmseNSEntryAttr>())
2012+
FuncAttrs.addAttribute("cmse_nonsecure_entry");
2013+
20072014
bool DisableTailCalls = false;
20082015

20092016
if (CodeGenOpts.DisableTailCalls)

clang/lib/Driver/ToolChains/Clang.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4430,14 +4430,24 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
44304430
bool IsPIE;
44314431
std::tie(RelocationModel, PICLevel, IsPIE) = ParsePICArgs(TC, Args);
44324432

4433-
const char *RMName = RelocationModelName(RelocationModel);
4433+
bool IsROPI = RelocationModel == llvm::Reloc::ROPI ||
4434+
RelocationModel == llvm::Reloc::ROPI_RWPI;
4435+
bool IsRWPI = RelocationModel == llvm::Reloc::RWPI ||
4436+
RelocationModel == llvm::Reloc::ROPI_RWPI;
4437+
4438+
if (Args.hasArg(options::OPT_mcmse) &&
4439+
!Args.hasArg(options::OPT_fallow_unsupported)) {
4440+
if (IsROPI)
4441+
D.Diag(diag::err_cmse_pi_are_incompatible) << IsROPI;
4442+
if (IsRWPI)
4443+
D.Diag(diag::err_cmse_pi_are_incompatible) << !IsRWPI;
4444+
}
44344445

4435-
if ((RelocationModel == llvm::Reloc::ROPI ||
4436-
RelocationModel == llvm::Reloc::ROPI_RWPI) &&
4437-
types::isCXX(Input.getType()) &&
4446+
if (IsROPI && types::isCXX(Input.getType()) &&
44384447
!Args.hasArg(options::OPT_fallow_unsupported))
44394448
D.Diag(diag::err_drv_ropi_incompatible_with_cxx);
44404449

4450+
const char *RMName = RelocationModelName(RelocationModel);
44414451
if (RMName) {
44424452
CmdArgs.push_back("-mrelocation-model");
44434453
CmdArgs.push_back(RMName);

0 commit comments

Comments
 (0)