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 { - // 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 { - // 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 { - // 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 { + // 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 { let Spellings = [GNU<"force_align_arg_pointer">]; // Technically, this appertains to a FunctionDecl, but the target-specific 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/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: lib/CodeGen/TargetInfo.cpp =================================================================== --- lib/CodeGen/TargetInfo.cpp +++ lib/CodeGen/TargetInfo.cpp @@ -1574,6 +1574,10 @@ llvm::AttributeSet::FunctionIndex, B)); } + if (FD->hasAttr()) { + llvm::Function *Fn = cast(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(D)) { + if (FD->hasAttr()) { + llvm::Function *Fn = cast(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(D)) { + if (FD->hasAttr()) { + llvm::Function *Fn = cast(GV); + Fn->setCallingConv(llvm::CallingConv::X86_INTR); + } + } + addStackProbeSizeTargetAttribute(D, GV, CGM); } } 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: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -4969,6 +4969,10 @@ Fn->getValueKind(), FDecl); } } + if (NDecl && NDecl->hasAttr()) { + Diag(Fn->getExprLoc(), diag::err_interrupt_function_called); + return ExprError(); + } } else if (isa(NakedFn)) NDecl = cast(NakedFn)->getMemberDecl(); 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; +} +