Implement ARM GNU-style interrupt attribute
This attribute allows users to use a modified C or C++ function as an ARM exception-handling function and, with care, to successfully return control to user-space after the issue has been dealt with. rdar://problem/14207019 llvm-svn: 191769
This commit is contained in:
parent
6a6b63b464
commit
a484bc00ff
|
@ -1843,6 +1843,48 @@ Which compiles to (on X86-32):
|
|||
movl %gs:(%eax), %eax
|
||||
ret
|
||||
|
||||
ARM Language Extensions
|
||||
-----------------------
|
||||
|
||||
Interrupt attribute
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Clang supports the GNU style ``__attribite__((interrupt("TYPE")))`` attribute on
|
||||
ARM targets. This attribute may be attached to a function definiton and
|
||||
instructs the backend to generate appropriate function entry/exit code so that
|
||||
it can be used directly as an interrupt service routine.
|
||||
|
||||
The parameter passed to the interrupt attribute is optional, but if
|
||||
provided it must be a string literal with one of the following values: "IRQ",
|
||||
"FIQ", "SWI", "ABORT", "UNDEF".
|
||||
|
||||
The semantics are as follows:
|
||||
|
||||
- If the function is AAPCS, Clang instructs the backend to realign the stack to
|
||||
8 bytes on entry. This is a general requirement of the AAPCS at public
|
||||
interfaces, but may not hold when an exception is taken. Doing this allows
|
||||
other AAPCS functions to be called.
|
||||
- If the CPU is M-class this is all that needs to be done since the architecture
|
||||
itself is designed in such a way that functions obeying the normal AAPCS ABI
|
||||
constraints are valid exception handlers.
|
||||
- If the CPU is not M-class, the prologue and epilogue are modified to save all
|
||||
non-banked registers that are used, so that upon return the user-mode state
|
||||
will not be corrupted. Note that to avoid unnecessary overhead, only
|
||||
general-purpose (integer) registers are saved in this way. If VFP operations
|
||||
are needed, that state must be saved manually.
|
||||
|
||||
Specifically, interrupt kinds other than "FIQ" will save all core registers
|
||||
except "lr" and "sp". "FIQ" interrupts will save r0-r7.
|
||||
- If the CPU is not M-class, the return instruction is changed to one of the
|
||||
canonical sequences permitted by the architecture for exception return. Where
|
||||
possible the function itself will make the necessary "lr" adjustments so that
|
||||
the "preferred return address" is selected.
|
||||
|
||||
Unfortunately the compiler is unable to make this guarantee for nn "UNDEF"
|
||||
handler, where the offset from "lr" to the preferred return address depends on
|
||||
the execution state of the code which generated the exception. In this case
|
||||
a sequence equivalent to "movs pc, lr" will be used.
|
||||
|
||||
Extensions for Static Analysis
|
||||
==============================
|
||||
|
||||
|
|
|
@ -210,6 +210,14 @@ def Annotate : InheritableParamAttr {
|
|||
let Args = [StringArgument<"Annotation">];
|
||||
}
|
||||
|
||||
def ARMInterrupt : InheritableAttr, TargetSpecificAttr {
|
||||
let Spellings = [GNU<"interrupt">];
|
||||
let Args = [EnumArgument<"Interrupt", "InterruptType",
|
||||
["IRQ", "FIQ", "SWI", "ABORT", "UNDEF", ""],
|
||||
["IRQ", "FIQ", "SWI", "ABORT", "UNDEF", "Generic"],
|
||||
1>];
|
||||
}
|
||||
|
||||
def AsmLabel : InheritableAttr {
|
||||
let Spellings = [];
|
||||
let Args = [StringArgument<"Label">];
|
||||
|
|
|
@ -3056,9 +3056,9 @@ public:
|
|||
Env == "android" || Env == "androideabi");
|
||||
}
|
||||
|
||||
private:
|
||||
ABIKind getABIKind() const { return Kind; }
|
||||
|
||||
private:
|
||||
ABIArgInfo classifyReturnType(QualType RetTy) const;
|
||||
ABIArgInfo classifyArgumentType(QualType RetTy, int *VFPRegs,
|
||||
unsigned &AllocatedVFP,
|
||||
|
@ -3105,6 +3105,45 @@ public:
|
|||
if (getABIInfo().isEABI()) return 88;
|
||||
return TargetCodeGenInfo::getSizeOfUnwindException();
|
||||
}
|
||||
|
||||
void SetTargetAttributes(const Decl *D, llvm::GlobalValue *GV,
|
||||
CodeGen::CodeGenModule &CGM) const {
|
||||
const FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
|
||||
if (!FD)
|
||||
return;
|
||||
|
||||
const ARMInterruptAttr *Attr = FD->getAttr<ARMInterruptAttr>();
|
||||
if (!Attr)
|
||||
return;
|
||||
|
||||
const char *Kind;
|
||||
switch (Attr->getInterrupt()) {
|
||||
case ARMInterruptAttr::Generic: Kind = ""; break;
|
||||
case ARMInterruptAttr::IRQ: Kind = "IRQ"; break;
|
||||
case ARMInterruptAttr::FIQ: Kind = "FIQ"; break;
|
||||
case ARMInterruptAttr::SWI: Kind = "SWI"; break;
|
||||
case ARMInterruptAttr::ABORT: Kind = "ABORT"; break;
|
||||
case ARMInterruptAttr::UNDEF: Kind = "UNDEF"; break;
|
||||
}
|
||||
|
||||
llvm::Function *Fn = cast<llvm::Function>(GV);
|
||||
|
||||
Fn->addFnAttr("interrupt", Kind);
|
||||
|
||||
if (cast<ARMABIInfo>(getABIInfo()).getABIKind() == ARMABIInfo::APCS)
|
||||
return;
|
||||
|
||||
// AAPCS guarantees that sp will be 8-byte aligned on any public interface,
|
||||
// however this is not necessarily true on taking any interrupt. Instruct
|
||||
// the backend to perform a realignment as part of the function prologue.
|
||||
llvm::AttrBuilder B;
|
||||
B.addStackAlignmentAttr(8);
|
||||
Fn->addAttributes(llvm::AttributeSet::FunctionIndex,
|
||||
llvm::AttributeSet::get(CGM.getLLVMContext(),
|
||||
llvm::AttributeSet::FunctionIndex,
|
||||
B));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -289,7 +289,7 @@ static bool checkFunctionOrMethodArgumentIndex(Sema &S, const Decl *D,
|
|||
/// literal.
|
||||
bool Sema::checkStringLiteralArgumentAttr(const AttributeList &Attr,
|
||||
unsigned ArgNum, StringRef &Str,
|
||||
SourceLocation *ArgLocation = 0) {
|
||||
SourceLocation *ArgLocation) {
|
||||
// Look for identifiers. If we have one emit a hint to fix it to a literal.
|
||||
if (Attr.isArgIdent(ArgNum)) {
|
||||
IdentifierLoc *Loc = Attr.getArgAsIdent(ArgNum);
|
||||
|
|
|
@ -26,6 +26,50 @@ bool TargetAttributesSema::ProcessDeclAttribute(Scope *scope, Decl *D,
|
|||
return false;
|
||||
}
|
||||
|
||||
static void HandleARMInterruptAttr(Decl *d,
|
||||
const AttributeList &Attr, Sema &S) {
|
||||
// Check the attribute arguments.
|
||||
if (Attr.getNumArgs() > 1) {
|
||||
S.Diag(Attr.getLoc(), diag::err_attribute_too_many_arguments)
|
||||
<< 1;
|
||||
return;
|
||||
}
|
||||
|
||||
StringRef Str;
|
||||
SourceLocation ArgLoc;
|
||||
|
||||
if (Attr.getNumArgs() == 0)
|
||||
Str = "";
|
||||
else if (!S.checkStringLiteralArgumentAttr(Attr, 0, Str, &ArgLoc))
|
||||
return;
|
||||
|
||||
ARMInterruptAttr::InterruptType Kind;
|
||||
if (!ARMInterruptAttr::ConvertStrToInterruptType(Str, Kind)) {
|
||||
S.Diag(Attr.getLoc(), diag::warn_attribute_type_not_supported)
|
||||
<< Attr.getName() << Str << ArgLoc;
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned Index = Attr.getAttributeSpellingListIndex();
|
||||
d->addAttr(::new (S.Context)
|
||||
ARMInterruptAttr(Attr.getLoc(), S.Context, Kind, Index));
|
||||
}
|
||||
|
||||
namespace {
|
||||
class ARMAttributesSema : public TargetAttributesSema {
|
||||
public:
|
||||
ARMAttributesSema() { }
|
||||
bool ProcessDeclAttribute(Scope *scope, Decl *D,
|
||||
const AttributeList &Attr, Sema &S) const {
|
||||
if (Attr.getName()->getName() == "interrupt") {
|
||||
HandleARMInterruptAttr(D, Attr, S);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static void HandleMSP430InterruptAttr(Decl *d,
|
||||
const AttributeList &Attr, Sema &S) {
|
||||
// Check the attribute arguments.
|
||||
|
@ -292,6 +336,9 @@ const TargetAttributesSema &Sema::getTargetAttributesSema() const {
|
|||
|
||||
const llvm::Triple &Triple(Context.getTargetInfo().getTriple());
|
||||
switch (Triple.getArch()) {
|
||||
case llvm::Triple::arm:
|
||||
case llvm::Triple::thumb:
|
||||
return *(TheTargetAttributesSema = new ARMAttributesSema);
|
||||
case llvm::Triple::msp430:
|
||||
return *(TheTargetAttributesSema = new MSP430AttributesSema);
|
||||
case llvm::Triple::x86:
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
// RUN: %clang_cc1 -triple thumb-apple-darwin -target-abi aapcs -target-cpu cortex-m3 -emit-llvm -o - %s | FileCheck %s
|
||||
// RUN: %clang_cc1 -triple arm-apple-darwin -target-abi apcs-gnu -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-APCS
|
||||
|
||||
__attribute__((interrupt)) void test_generic_interrupt() {
|
||||
// CHECK: define arm_aapcscc void @test_generic_interrupt() [[GENERIC_ATTR:#[0-9]+]]
|
||||
|
||||
// CHECK-APCS: define void @test_generic_interrupt() [[GENERIC_ATTR:#[0-9]+]]
|
||||
}
|
||||
|
||||
__attribute__((interrupt("IRQ"))) void test_irq_interrupt() {
|
||||
// CHECK: define arm_aapcscc void @test_irq_interrupt() [[IRQ_ATTR:#[0-9]+]]
|
||||
}
|
||||
|
||||
__attribute__((interrupt("FIQ"))) void test_fiq_interrupt() {
|
||||
// CHECK: define arm_aapcscc void @test_fiq_interrupt() [[FIQ_ATTR:#[0-9]+]]
|
||||
}
|
||||
|
||||
__attribute__((interrupt("SWI"))) void test_swi_interrupt() {
|
||||
// CHECK: define arm_aapcscc void @test_swi_interrupt() [[SWI_ATTR:#[0-9]+]]
|
||||
}
|
||||
|
||||
__attribute__((interrupt("ABORT"))) void test_abort_interrupt() {
|
||||
// CHECK: define arm_aapcscc void @test_abort_interrupt() [[ABORT_ATTR:#[0-9]+]]
|
||||
}
|
||||
|
||||
|
||||
__attribute__((interrupt("UNDEF"))) void test_undef_interrupt() {
|
||||
// CHECK: define arm_aapcscc void @test_undef_interrupt() [[UNDEF_ATTR:#[0-9]+]]
|
||||
}
|
||||
|
||||
// CHECK: attributes [[GENERIC_ATTR]] = { nounwind alignstack=8 {{"interrupt"[^=]}}
|
||||
// CHECK: attributes [[IRQ_ATTR]] = { nounwind alignstack=8 "interrupt"="IRQ"
|
||||
// CHECK: attributes [[FIQ_ATTR]] = { nounwind alignstack=8 "interrupt"="FIQ"
|
||||
// CHECK: attributes [[SWI_ATTR]] = { nounwind alignstack=8 "interrupt"="SWI"
|
||||
// CHECK: attributes [[ABORT_ATTR]] = { nounwind alignstack=8 "interrupt"="ABORT"
|
||||
// CHECK: attributes [[UNDEF_ATTR]] = { nounwind alignstack=8 "interrupt"="UNDEF"
|
||||
|
||||
// CHECK-APCS: attributes [[GENERIC_ATTR]] = { nounwind "interrupt"
|
|
@ -0,0 +1,16 @@
|
|||
// RUN: %clang_cc1 %s -triple arm-apple-darwin -verify -fsyntax-only
|
||||
|
||||
__attribute__((interrupt(IRQ))) void foo() {} // expected-error {{'interrupt' attribute requires a string}}
|
||||
__attribute__((interrupt("irq"))) void foo1() {} // expected-warning {{'interrupt' attribute argument not supported: irq}}
|
||||
|
||||
__attribute__((interrupt("IRQ", 1))) void foo2() {} // expected-error {{attribute takes no more than 1 argument}}
|
||||
|
||||
__attribute__((interrupt("IRQ"))) void foo3() {}
|
||||
__attribute__((interrupt("FIQ"))) void foo4() {}
|
||||
__attribute__((interrupt("SWI"))) void foo5() {}
|
||||
__attribute__((interrupt("ABORT"))) void foo6() {}
|
||||
__attribute__((interrupt("UNDEF"))) void foo7() {}
|
||||
|
||||
__attribute__((interrupt)) void foo8() {}
|
||||
__attribute__((interrupt())) void foo9() {}
|
||||
__attribute__((interrupt(""))) void foo10() {}
|
Loading…
Reference in New Issue