ABataev created this revision. ABataev added reviewers: aaron.ballman, rjmccall. ABataev added a subscriber: cfe-commits.
This attribute may be attached to a function definition and instructs the backend to generate appropriate function entry/exit code so that it can be used directly as an interrupt handler. The ``IRET`` instruction, instead of the ``RET`` instruction, is used to return from interrupt or exception handlers. All registers, except for the ``EFLAGS`` register which is restored by the ``IRET`` instruction, are preserved by the compiler. Any interruptible-without-stack-switch code must be compiled with -mno-red-zone since interrupt handlers can and will, because of the hardware design, touch the red zone. 1. interrupt handler must be declared with a mandatory pointer argument: .. code-block:: c struct interrupt_frame; __attribute__ ((interrupt)) void f (struct interrupt_frame *frame) { ... } and user must properly define the structure the pointer pointing to. 2. exception handler: The exception handler is very similar to the interrupt handler with a different mandatory function signature: .. code-block:: c #ifdef __x86_64__ typedef unsigned long long int uword_t; #else typedef unsigned int uword_t; #endif struct interrupt_frame; __attribute__ ((interrupt)) void f (struct interrupt_frame *frame, uword_t error_code) { ... } and compiler pops the error code off stack before the 'IRET' instruction. The exception handler should only be used for exceptions which push an error code and all other exceptions must use the interrupt handler. The system will crash if the wrong handler is used. http://reviews.llvm.org/D15709 Files: include/clang/Basic/Attr.td include/clang/Basic/AttrDocs.td include/clang/Basic/DiagnosticSemaKinds.td lib/CodeGen/TargetInfo.cpp lib/Sema/SemaDeclAttr.cpp lib/Sema/SemaExpr.cpp test/CodeGen/attr-x86-interrupt.c test/Sema/attr-x86-interrupt.c
Index: lib/CodeGen/TargetInfo.cpp =================================================================== --- lib/CodeGen/TargetInfo.cpp +++ lib/CodeGen/TargetInfo.cpp @@ -1574,6 +1574,10 @@ llvm::AttributeSet::FunctionIndex, B)); } + if (FD->hasAttr<IAInterruptAttr>()) { + llvm::Function *Fn = cast<llvm::Function>(GV); + Fn->setCallingConv(llvm::CallingConv::X86_INTR); + } } } @@ -1879,6 +1883,16 @@ ('T' << 24); return llvm::ConstantInt::get(CGM.Int32Ty, Sig); } + + void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV, + CodeGen::CodeGenModule &CGM) const override { + if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D)) { + if (FD->hasAttr<IAInterruptAttr>()) { + llvm::Function *Fn = cast<llvm::Function>(GV); + Fn->setCallingConv(llvm::CallingConv::X86_INTR); + } + } + } }; class PS4TargetCodeGenInfo : public X86_64TargetCodeGenInfo { @@ -1996,6 +2010,13 @@ CodeGen::CodeGenModule &CGM) const { TargetCodeGenInfo::setTargetAttributes(D, GV, CGM); + if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D)) { + if (FD->hasAttr<IAInterruptAttr>()) { + llvm::Function *Fn = cast<llvm::Function>(GV); + Fn->setCallingConv(llvm::CallingConv::X86_INTR); + } + } + addStackProbeSizeTargetAttribute(D, GV, CGM); } } Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -4969,6 +4969,10 @@ Fn->getValueKind(), FDecl); } } + if (NDecl && NDecl->hasAttr<IAInterruptAttr>()) { + Diag(Fn->getExprLoc(), diag::err_interrupt_function_called); + return ExprError(); + } } else if (isa<MemberExpr>(NakedFn)) NDecl = cast<MemberExpr>(NakedFn)->getMemberDecl(); Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -4545,6 +4545,49 @@ Attr.getLoc(), S.Context, Kind, Attr.getAttributeSpellingListIndex())); } +static void handleIAInterruptAttr(Sema &S, Decl *D, const AttributeList &Attr) { + // Semantic checks for a function with the 'interrupt' attribute. + // a) Must be a function. + // b) Must have the 'void' return type. + // c) Must take 1 or 2 arguments. + // d) The 1st argument must be a pointer. + // e) The 2nd argument (if any) must be an unsigned integer. + if (!isFunctionOrMethod(D) || !hasFunctionProto(D) || + !D->getDeclContext()->isFileContext()) { + S.Diag(Attr.getLoc(), diag::err_attribute_wrong_decl_type) + << Attr.getName() << ExpectedFunction; + return; + } + // Interrupt handler must have void return type. + if (!getFunctionOrMethodResultType(D)->isVoidType()) { + S.Diag(getFunctionOrMethodResultSourceRange(D).getBegin(), + diag::err_interrupt_function_wrong_return_type); + return; + } + // Interrupt handler must have 1 or 2 parameters. + auto NumParams = getFunctionOrMethodNumParams(D); + if (NumParams < 1 || NumParams > 2) { + S.Diag(D->getLocStart(), diag::err_interrupt_function_wrong_args); + return; + } + // The first argument must be a pointer. + if (!getFunctionOrMethodParamType(D, 0)->isPointerType()) { + S.Diag(getFunctionOrMethodParamRange(D, 0).getBegin(), + diag::err_interrupt_function_wrong_first_arg); + return; + } + // The second argument must be an unsigned integer. + if (NumParams == 2 && + !getFunctionOrMethodParamType(D, 1)->isUnsignedIntegerType()) { + S.Diag(getFunctionOrMethodParamRange(D, 1).getBegin(), + diag::err_interrupt_function_wrong_second_arg); + return; + } + D->addAttr(::new (S.Context) IAInterruptAttr( + Attr.getLoc(), S.Context, Attr.getAttributeSpellingListIndex())); + D->addAttr(UsedAttr::CreateImplicit(S.Context)); +} + static void handleInterruptAttr(Sema &S, Decl *D, const AttributeList &Attr) { // Dispatch the interrupt attribute based on the current target. if (S.Context.getTargetInfo().getTriple().getArch() == llvm::Triple::msp430) @@ -4554,6 +4597,11 @@ S.Context.getTargetInfo().getTriple().getArch() == llvm::Triple::mips) handleMipsInterruptAttr(S, D, Attr); + else if (S.Context.getTargetInfo().getTriple().getArch() == + llvm::Triple::x86 || + S.Context.getTargetInfo().getTriple().getArch() == + llvm::Triple::x86_64) + handleIAInterruptAttr(S, D, Attr); else handleARMInterruptAttr(S, D, Attr); } Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2490,6 +2490,16 @@ "%0 attribute cannot be applied to a base specifier">; def err_invalid_attribute_on_virtual_function : Error< "%0 attribute cannot be applied to virtual functions">; +def err_interrupt_function_wrong_return_type : Error< + "interrupt service routine must have void return value">; +def err_interrupt_function_wrong_args : Error< + "interrupt service routine can only have a pointer argument and an optional integer argument">; +def err_interrupt_function_wrong_first_arg : Error< + "interrupt service routine should have a pointer as the first argument">; +def err_interrupt_function_wrong_second_arg : Error< + "interrupt service routine should have one of unsigned integer types as the second argument">; +def err_interrupt_function_called : Error< + "interrupt service routine can't be used directly">; // Availability attribute def warn_availability_unknown_platform : Warning< Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -1859,3 +1859,59 @@ }]; } + +def IAInterruptDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +Clang supports the GNU style ``__attribute__((interrupt))`` attribute on +x86 targets. This attribute may be attached to a function definition and +instructs the backend to generate appropriate function entry/exit code so that +it can be used directly as an interrupt handler. +The ``IRET`` instruction, instead of the ``RET`` instruction, is used to return +from interrupt or exception handlers. All registers, except for the ``EFLAGS`` +register which is restored by the ``IRET`` instruction, are preserved by the +compiler. +Any interruptible-without-stack-switch code must be compiled with -mno-red-zone +since interrupt handlers can and will, because of the hardware design, touch +the red zone. + +1. interrupt handler must be declared with a mandatory pointer argument: + + .. code-block:: c + + struct interrupt_frame; + + __attribute__ ((interrupt)) + void f (struct interrupt_frame *frame) { + ... + } + + and user must properly define the structure the pointer pointing to. + +2. exception handler: + + The exception handler is very similar to the interrupt handler with + a different mandatory function signature: + + .. code-block:: c + + #ifdef __x86_64__ + typedef unsigned long long int uword_t; + #else + typedef unsigned int uword_t; + #endif + + struct interrupt_frame; + + __attribute__ ((interrupt)) + void f (struct interrupt_frame *frame, uword_t error_code) { + ... + } + + and compiler pops the error code off stack before the 'IRET' instruction. + + The exception handler should only be used for exceptions which push an + error code and all other exceptions must use the interrupt handler. + The system will crash if the wrong handler is used. + }]; +} Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -252,6 +252,7 @@ def TargetMips : TargetArch<["mips", "mipsel"]>; def TargetMSP430 : TargetArch<["msp430"]>; def TargetX86 : TargetArch<["x86"]>; +def TargetIA : TargetArch<["x86", "x86_64"]>; def TargetWindows : TargetArch<["x86", "x86_64", "arm", "thumb"]> { let OSes = ["Win32"]; } @@ -426,8 +427,8 @@ } def ARMInterrupt : InheritableAttr, TargetSpecificAttr<TargetARM> { - // NOTE: If you add any additional spellings, MSP430Interrupt's and - // MipsInterrupt's spellings must match. + // NOTE: If you add any additional spellings, MSP430Interrupt's, + // MipsInterrupt's and IAInterrupt's spellings must match. let Spellings = [GNU<"interrupt">]; let Args = [EnumArgument<"Interrupt", "InterruptType", ["IRQ", "FIQ", "SWI", "ABORT", "UNDEF", ""], @@ -845,8 +846,8 @@ } def MSP430Interrupt : InheritableAttr, TargetSpecificAttr<TargetMSP430> { - // NOTE: If you add any additional spellings, ARMInterrupt's and - // MipsInterrupt's spellings must match. + // NOTE: If you add any additional spellings, ARMInterrupt's, MipsInterrupt's + // and IAInterrupt's spellings must match. let Spellings = [GNU<"interrupt">]; let Args = [UnsignedArgument<"Number">]; let ParseKind = "Interrupt"; @@ -861,8 +862,8 @@ } def MipsInterrupt : InheritableAttr, TargetSpecificAttr<TargetMips> { - // NOTE: If you add any additional spellings, ARMInterrupt's and - // MSP430Interrupt's spellings must match. + // NOTE: If you add any additional spellings, ARMInterrupt's, + // MSP430Interrupt's and IAInterrupt's spellings must match. let Spellings = [GNU<"interrupt">]; let Subjects = SubjectList<[Function]>; let Args = [EnumArgument<"Interrupt", "InterruptType", @@ -1527,6 +1528,16 @@ let Documentation = [Undocumented]; } +def IAInterrupt : InheritableAttr, TargetSpecificAttr<TargetIA> { + // NOTE: If you add any additional spellings, ARMInterrupt's, + // MSP430Interrupt's and MipsInterrupt's spellings must match. + let Spellings = [GNU<"interrupt">]; + let Subjects = SubjectList<[Function]>; + let ParseKind = "Interrupt"; + let HasCustomParsing = 1; + let Documentation = [IAInterruptDocs]; +} + def X86ForceAlignArgPointer : InheritableAttr, TargetSpecificAttr<TargetX86> { let Spellings = [GNU<"force_align_arg_pointer">]; // Technically, this appertains to a FunctionDecl, but the target-specific Index: test/CodeGen/attr-x86-interrupt.c =================================================================== --- test/CodeGen/attr-x86-interrupt.c +++ test/CodeGen/attr-x86-interrupt.c @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -emit-llvm -o - | FileCheck %s --check-prefix=X86_64_LINUX +// RUN: %clang_cc1 -triple i386-unknown-linux-gnu %s -emit-llvm -o - | FileCheck %s --check-prefix=X86_LINUX +// RUN: %clang_cc1 -triple x86_64-pc-win32 %s -emit-llvm -o - | FileCheck %s --check-prefix=X86_64_WIN +// RUN: %clang_cc1 -triple i386-pc-win32 %s -emit-llvm -o - | FileCheck %s --check-prefix=X86_WIN + +__attribute__((interrupt)) void foo7(int *a, unsigned b) {} +__attribute__((interrupt)) void foo8(int *a) {} +// X86_64_LINUX: @llvm.used = appending global [2 x i8*] [i8* bitcast (void (i32*, i32)* @foo7 to i8*), i8* bitcast (void (i32*)* @foo8 to i8*)], section "llvm.metadata" +// X86_64_LINUX: define x86_intrcc void @foo7(i32* %{{.+}}, i32 %{{.+}}) +// X86_64_LINUX: define x86_intrcc void @foo8(i32* %{{.+}}) +// X86_LINUX: @llvm.used = appending global [2 x i8*] [i8* bitcast (void (i32*, i32)* @foo7 to i8*), i8* bitcast (void (i32*)* @foo8 to i8*)], section "llvm.metadata" +// X86_LINUX: define x86_intrcc void @foo7(i32* %{{.+}}, i32 %{{.+}}) +// X86_LINUX: define x86_intrcc void @foo8(i32* %{{.+}}) +// X86_64_WIN: @llvm.used = appending global [2 x i8*] [i8* bitcast (void (i32*, i32)* @foo7 to i8*), i8* bitcast (void (i32*)* @foo8 to i8*)], section "llvm.metadata" +// X86_64_WIN: define x86_intrcc void @foo7(i32* %{{.+}}, i32 %{{.+}}) +// X86_64_WIN: define x86_intrcc void @foo8(i32* %{{.+}}) +// X86_WIN: @llvm.used = appending global [2 x i8*] [i8* bitcast (void (i32*, i32)* @foo7 to i8*), i8* bitcast (void (i32*)* @foo8 to i8*)], section "llvm.metadata" +// X86_WIN: define x86_intrcc void @foo7(i32* %{{.+}}, i32 %{{.+}}) +// X86_WIN: define x86_intrcc void @foo8(i32* %{{.+}}) Index: test/Sema/attr-x86-interrupt.c =================================================================== --- test/Sema/attr-x86-interrupt.c +++ test/Sema/attr-x86-interrupt.c @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsyntax-only -verify %s +// RUN: %clang_cc1 -triple i386-unknown-linux-gnu -fsyntax-only -verify %s +// RUN: %clang_cc1 -triple x86_64-pc-win32 -fsyntax-only -verify %s +// RUN: %clang_cc1 -triple i386-pc-win32 -fsyntax-only -verify %s + +struct a { + int b; +}; + +struct a test __attribute__((interrupt)); // expected-error {{'interrupt' attribute only applies to functions}} + +__attribute__((interrupt)) int foo1(void) { return 0; } // expected-error {{interrupt service routine must have void return value}} +__attribute__((interrupt)) void foo2(void) {} // expected-error {{interrupt service routine can only have a pointer argument and an optional integer argument}} +__attribute__((interrupt)) void foo3(void *a, unsigned b, int c) {} // expected-error {{interrupt service routine can only have a pointer argument and an optional integer argument}} +__attribute__((interrupt)) void foo4(int a) {} // expected-error {{interrupt service routine should have a pointer as the first argument}} +__attribute__((interrupt)) void foo5(void *a, float b) {} // expected-error {{interrupt service routine should have one of unsigned integer types as the second argument}} +__attribute__((interrupt)) void foo6(float *a, int b) {} // expected-error {{interrupt service routine should have one of unsigned integer types as the second argument}} +__attribute__((interrupt)) void foo7(int *a, unsigned b) {} +__attribute__((interrupt)) void foo8(int *a) {} +int main(int argc, char **argv) { + void *ptr = (void *)&foo7; + (void)ptr; + foo7((int *)argv, argc); // expected-error {{interrupt service routine can't be used directly}} + foo8((int *)argv); // expected-error {{interrupt service routine can't be used directly}} + return 0; +} +
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits