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 TargetAnyX86 : 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 AnyX86Interrupt'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 AnyX86Interrupt'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 AnyX86Interrupt'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 AnyX86Interrupt : 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<[HasFunctionProto]>; + let ParseKind = "Interrupt"; + let HasCustomParsing = 1; + let Documentation = [AnyX86InterruptDocs]; +} + 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,56 @@ }]; } + +def AnyX86InterruptDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +Clang supports the GNU style ``__attribute__((interrupt))`` attribute on +x86/x86-64 targets.The compiler generates function entry and exit sequences +suitable for use in an interrupt handler when this attribute is present. +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 + { + uword_t ip; + uword_t cs; + uword_t flags; + uword_t sp; + uword_t ss; + }; + + __attribute__ ((interrupt)) + void f (struct interrupt_frame *frame) { + ... + } + +2. exception handler: + + The exception handler is very similar to the interrupt handler with + a different mandatory function signature: + + .. code-block:: c + + __attribute__ ((interrupt)) + void f (struct interrupt_frame *frame, uword_t error_code) { + ... + } + + and compiler pops '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 @@ -254,6 +254,13 @@ def err_bad_parameter_name : Error< "%0 cannot be the name of a parameter">; def err_parameter_name_omitted : Error<"parameter name omitted">; +def err_anyx86_interrupt_attribute : Error< + "%select{x86|x86-64}0 'interrupt' attribute only applies to functions that " + "have %select{a 'void' return type|" + "only a pointer parameter optionally followed by an integer parameter|" + "a pointer as the first parameter|a %2 type as the second parameter}1">; +def err_anyx86_interrupt_called : Error< + "interrupt service routine cannot be called directly">; def warn_mips_interrupt_attribute : Warning< "MIPS 'interrupt' attribute only applies to functions that have " "%select{no parameters|a 'void' return type}0">, Index: lib/CodeGen/TargetInfo.cpp =================================================================== --- lib/CodeGen/TargetInfo.cpp +++ lib/CodeGen/TargetInfo.cpp @@ -1609,6 +1609,10 @@ llvm::AttributeSet::FunctionIndex, B)); } + if (FD->hasAttr()) { + llvm::Function *Fn = cast(GV); + Fn->setCallingConv(llvm::CallingConv::X86_INTR); + } } } @@ -1914,6 +1918,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 { @@ -2031,6 +2045,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 @@ -4570,17 +4570,90 @@ Attr.getLoc(), S.Context, Kind, Attr.getAttributeSpellingListIndex())); } +static void handleAnyX86InterruptAttr(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) || isInstanceMethod(D) || + CXXMethodDecl::isStaticOverloadedOperator( + cast(D)->getDeclName().getCXXOverloadedOperator())) { + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) + << Attr.getName() << ExpectedFunctionWithProtoType; + return; + } + // Interrupt handler must have void return type. + if (!getFunctionOrMethodResultType(D)->isVoidType()) { + S.Diag(getFunctionOrMethodResultSourceRange(D).getBegin(), + diag::err_anyx86_interrupt_attribute) + << (S.Context.getTargetInfo().getTriple().getArch() == llvm::Triple::x86 + ? 0 + : 1) + << 0; + return; + } + // Interrupt handler must have 1 or 2 parameters. + unsigned NumParams = getFunctionOrMethodNumParams(D); + if (NumParams < 1 || NumParams > 2) { + S.Diag(D->getLocStart(), diag::err_anyx86_interrupt_attribute) + << (S.Context.getTargetInfo().getTriple().getArch() == llvm::Triple::x86 + ? 0 + : 1) + << 1; + return; + } + // The first argument must be a pointer. + if (!getFunctionOrMethodParamType(D, 0)->isPointerType()) { + S.Diag(getFunctionOrMethodParamRange(D, 0).getBegin(), + diag::err_anyx86_interrupt_attribute) + << (S.Context.getTargetInfo().getTriple().getArch() == llvm::Triple::x86 + ? 0 + : 1) + << 2; + return; + } + // The second argument, if present, must be an unsigned integer. + unsigned TypeSize = + S.Context.getTargetInfo().getTriple().getArch() == llvm::Triple::x86_64 + ? 64 + : 32; + if (NumParams == 2 && + (!getFunctionOrMethodParamType(D, 1)->isUnsignedIntegerType() || + S.Context.getTypeSize(getFunctionOrMethodParamType(D, 1)) != TypeSize)) { + S.Diag(getFunctionOrMethodParamRange(D, 1).getBegin(), + diag::err_anyx86_interrupt_attribute) + << (S.Context.getTargetInfo().getTriple().getArch() == llvm::Triple::x86 + ? 0 + : 1) + << 3 << S.Context.getIntTypeForBitwidth(TypeSize, /*Signed=*/false); + return; + } + D->addAttr(::new (S.Context) AnyX86InterruptAttr( + 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) + switch (S.Context.getTargetInfo().getTriple().getArch()) { + case llvm::Triple::msp430: handleMSP430InterruptAttr(S, D, Attr); - else if (S.Context.getTargetInfo().getTriple().getArch() == - llvm::Triple::mipsel || - S.Context.getTargetInfo().getTriple().getArch() == - llvm::Triple::mips) + break; + case llvm::Triple::mipsel: + case llvm::Triple::mips: handleMipsInterruptAttr(S, D, Attr); - else + break; + case llvm::Triple::x86: + case llvm::Triple::x86_64: + handleAnyX86InterruptAttr(S, D, Attr); + break; + default: handleARMInterruptAttr(S, D, Attr); + break; + } } static void handleAMDGPUNumVGPRAttr(Sema &S, Decl *D, Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -5053,6 +5053,12 @@ FunctionDecl *FDecl = dyn_cast_or_null(NDecl); unsigned BuiltinID = (FDecl ? FDecl->getBuiltinID() : 0); + // Functions with 'interrupt' attribute cannot be called directly. + if (FDecl && FDecl->hasAttr()) { + Diag(Fn->getExprLoc(), diag::err_anyx86_interrupt_called); + return ExprError(); + } + // Promote the function operand. // We special-case function promotion here because we only allow promoting // builtin functions to function pointers in the callee of a call. Index: test/CodeGen/attr-x86-interrupt.c =================================================================== --- test/CodeGen/attr-x86-interrupt.c +++ test/CodeGen/attr-x86-interrupt.c @@ -0,0 +1,26 @@ +// 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 +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnux32 %s -emit-llvm -o - | FileCheck %s --check-prefix=X86_64_LINUX + +#ifdef __x86_64__ +typedef __UINT64_TYPE__ uword; +#else +typedef __UINT32_TYPE__ uword; +#endif + +__attribute__((interrupt)) void foo7(int *a, uword b) {} +__attribute__((interrupt)) void foo8(int *a) {} +// X86_64_LINUX: @llvm.used = appending global [2 x i8*] [i8* bitcast (void (i32*, i64)* @foo7 to i8*), i8* bitcast (void (i32*)* @foo8 to i8*)], section "llvm.metadata" +// X86_64_LINUX: define x86_intrcc void @foo7(i32* %{{.+}}, i64 %{{.+}}) +// 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*, i64)* @foo7 to i8*), i8* bitcast (void (i32*)* @foo8 to i8*)], section "llvm.metadata" +// X86_64_WIN: define x86_intrcc void @foo7(i32* %{{.+}}, i64 %{{.+}}) +// 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/CodeGenCXX/attr-x86-interrupt.cpp =================================================================== --- test/CodeGenCXX/attr-x86-interrupt.cpp +++ test/CodeGenCXX/attr-x86-interrupt.cpp @@ -0,0 +1,35 @@ +// 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 +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnux32 %s -emit-llvm -o - | FileCheck %s --check-prefix=X86_64_LINUX + +#ifdef __x86_64__ +typedef __UINT64_TYPE__ uword; +#else +typedef __UINT32_TYPE__ uword; +#endif + +__attribute__((interrupt)) void foo7(int *a, uword b) {} +namespace S { +__attribute__((interrupt)) void foo8(int *a) {} +} +struct St { +static void foo9(int *a) __attribute__((interrupt)) {} +}; +// X86_64_LINUX: @llvm.used = appending global [3 x i8*] [i8* bitcast (void (i32*, i64)* @{{.*}}foo7{{.*}} to i8*), i8* bitcast (void (i32*)* @{{.*}}foo8{{.*}} to i8*), i8* bitcast (void (i32*)* @{{.*}}foo9{{.*}} to i8*)], section "llvm.metadata" +// X86_64_LINUX: define x86_intrcc void @{{.*}}foo7{{.*}}(i32* %{{.+}}, i64 %{{.+}}) +// X86_64_LINUX: define x86_intrcc void @{{.*}}foo8{{.*}}(i32* %{{.+}}) +// X86_64_LINUX: define linkonce_odr x86_intrcc void @{{.*}}foo9{{.*}}(i32* %{{.+}}) +// X86_LINUX: @llvm.used = appending global [3 x i8*] [i8* bitcast (void (i32*, i32)* @{{.*}}foo7{{.*}} to i8*), i8* bitcast (void (i32*)* @{{.*}}foo8{{.*}} to i8*), i8* bitcast (void (i32*)* @{{.*}}foo9{{.*}} to i8*)], section "llvm.metadata" +// X86_LINUX: define x86_intrcc void @{{.*}}foo7{{.*}}(i32* %{{.+}}, i32 %{{.+}}) +// X86_LINUX: define x86_intrcc void @{{.*}}foo8{{.*}}(i32* %{{.+}}) +// X86_LINUX: define linkonce_odr x86_intrcc void @{{.*}}foo9{{.*}}(i32* %{{.+}}) +// X86_64_WIN: @llvm.used = appending global [3 x i8*] [i8* bitcast (void (i32*, i64)* @{{.*}}foo7{{.*}} to i8*), i8* bitcast (void (i32*)* @{{.*}}foo8{{.*}} to i8*), i8* bitcast (void (i32*)* @{{.*}}foo9{{.*}} to i8*)], section "llvm.metadata" +// X86_64_WIN: define x86_intrcc void @{{.*}}foo7{{.*}}(i32* %{{.+}}, i64 %{{.+}}) +// X86_64_WIN: define x86_intrcc void @{{.*}}foo8{{.*}}(i32* %{{.+}}) +// X86_64_WIN: define linkonce_odr x86_intrcc void @{{.*}}foo9{{.*}}(i32* %{{.+}}) +// X86_WIN: @llvm.used = appending global [3 x i8*] [i8* bitcast (void (i32*, i32)* @{{.*}}foo7{{.*}} to i8*), i8* bitcast (void (i32*)* @{{.*}}foo8{{.*}} to i8*), i8* bitcast (void (i32*)* @{{.*}}foo9{{.*}} to i8*)], section "llvm.metadata" +// X86_WIN: define x86_intrcc void @{{.*}}foo7{{.*}}(i32* %{{.+}}, i32 %{{.+}}) +// X86_WIN: define x86_intrcc void @{{.*}}foo8{{.*}}(i32* %{{.+}}) +// X86_WIN: define linkonce_odr x86_intrcc void @{{.*}}foo9{{.*}}(i32* %{{.+}}) Index: test/Sema/attr-x86-interrupt.c =================================================================== --- test/Sema/attr-x86-interrupt.c +++ test/Sema/attr-x86-interrupt.c @@ -0,0 +1,55 @@ +// 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 +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnux32 -fsyntax-only -verify %s + +struct a { + int b; +}; + +struct a test __attribute__((interrupt)); // expected-warning {{'interrupt' attribute only applies to non-K&R-style functions}} + +__attribute__((interrupt)) int foo1(void) { return 0; } // expected-error-re {{{{(x86|x86-64)}} 'interrupt' attribute only applies to functions that have a 'void' return type}} +__attribute__((interrupt)) void foo2(void) {} // expected-error-re {{{{(x86|x86-64)}} 'interrupt' attribute only applies to functions that have only a pointer parameter optionally followed by an integer parameter}} +__attribute__((interrupt)) void foo3(void *a, unsigned b, int c) {} // expected-error-re {{{{(x86|x86-64)}} 'interrupt' attribute only applies to functions that have only a pointer parameter optionally followed by an integer parameter}} +__attribute__((interrupt)) void foo4(int a) {} // expected-error-re {{{{(x86|x86-64)}} 'interrupt' attribute only applies to functions that have a pointer as the first parameter}} +#ifdef _LP64 +// expected-error-re@+6 {{{{(x86|x86-64)}} 'interrupt' attribute only applies to functions that have a 'unsigned long' type as the second parameter}} +#elif defined(__x86_64__) +// expected-error-re@+4 {{{{(x86|x86-64)}} 'interrupt' attribute only applies to functions that have a 'unsigned long long' type as the second parameter}} +#else +// expected-error-re@+2 {{{{(x86|x86-64)}} 'interrupt' attribute only applies to functions that have a 'unsigned int' type as the second parameter}} +#endif +__attribute__((interrupt)) void foo5(void *a, float b) {} +#ifdef _LP64 +// expected-error-re@+6 {{{{(x86|x86-64)}} 'interrupt' attribute only applies to functions that have a 'unsigned long' type as the second parameter}} +#elif defined(__x86_64__) +// expected-error-re@+4 {{{{(x86|x86-64)}} 'interrupt' attribute only applies to functions that have a 'unsigned long long' type as the second parameter}} +#else +// expected-error-re@+2 {{{{(x86|x86-64)}} 'interrupt' attribute only applies to functions that have a 'unsigned int' type as the second parameter}} +#endif +__attribute__((interrupt)) void foo6(float *a, int b) {} + +#ifdef _LP64 +// expected-error-re@+4 {{{{(x86|x86-64)}} 'interrupt' attribute only applies to functions that have a 'unsigned long' type as the second parameter}} +#elif defined(__x86_64__) +// expected-error-re@+2 {{{{(x86|x86-64)}} 'interrupt' attribute only applies to functions that have a 'unsigned long long' type as the second parameter}} +#endif +__attribute__((interrupt)) void foo7(int *a, unsigned b) {} +__attribute__((interrupt)) void foo8(int *a) {} + +void g(void (*fp)(int *)); +int main(int argc, char **argv) { + void *ptr = (void *)&foo7; + g(foo8); + + (void)ptr; +#ifndef __x86_64__ + // expected-error@+2 {{interrupt service routine cannot be called directly}} +#endif + foo7((int *)argv, argc); + foo8((int *)argv); // expected-error {{interrupt service routine cannot be called directly}} + return 0; +} + Index: test/SemaCXX/attr-x86-interrupt.cpp =================================================================== --- test/SemaCXX/attr-x86-interrupt.cpp +++ test/SemaCXX/attr-x86-interrupt.cpp @@ -0,0 +1,72 @@ +// 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 +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnux32 -fsyntax-only -verify %s + +struct a { + int b; + static void foo(int *a) __attribute__((interrupt)) {} + void *operator new(__SIZE_TYPE__) throw() __attribute__((interrupt)) { return 0; } // expected-warning {{'interrupt' attribute only applies to non-K&R-style functions}} +}; + +struct a test __attribute__((interrupt)); // expected-warning {{'interrupt' attribute only applies to non-K&R-style functions}} + +__attribute__((interrupt)) int foo1(void) { return 0; } // expected-error-re {{{{(x86|x86-64)}} 'interrupt' attribute only applies to functions that have a 'void' return type}} +__attribute__((interrupt)) void foo2(void) {} // expected-error-re {{{{(x86|x86-64)}} 'interrupt' attribute only applies to functions that have only a pointer parameter optionally followed by an integer parameter}} +__attribute__((interrupt)) void foo3(void *a, unsigned b, int c) {} // expected-error-re {{{{(x86|x86-64)}} 'interrupt' attribute only applies to functions that have only a pointer parameter optionally followed by an integer parameter}} +__attribute__((interrupt)) void foo4(int a) {} // expected-error-re {{{{(x86|x86-64)}} 'interrupt' attribute only applies to functions that have a pointer as the first parameter}} +#ifdef _LP64 +// expected-error-re@+6 {{{{(x86|x86-64)}} 'interrupt' attribute only applies to functions that have a 'unsigned long' type as the second parameter}} +#elif defined(__x86_64__) +// expected-error-re@+4 {{{{(x86|x86-64)}} 'interrupt' attribute only applies to functions that have a 'unsigned long long' type as the second parameter}} +#else +// expected-error-re@+2 {{{{(x86|x86-64)}} 'interrupt' attribute only applies to functions that have a 'unsigned int' type as the second parameter}} +#endif +__attribute__((interrupt)) void foo5(void *a, float b) {} +#ifdef _LP64 +// expected-error-re@+6 {{{{(x86|x86-64)}} 'interrupt' attribute only applies to functions that have a 'unsigned long' type as the second parameter}} +#elif defined(__x86_64__) +// expected-error-re@+4 {{{{(x86|x86-64)}} 'interrupt' attribute only applies to functions that have a 'unsigned long long' type as the second parameter}} +#else +// expected-error-re@+2 {{{{(x86|x86-64)}} 'interrupt' attribute only applies to functions that have a 'unsigned int' type as the second parameter}} +#endif +__attribute__((interrupt)) void foo6(float *a, int b) {} + +#ifdef _LP64 +// expected-error-re@+4 {{{{(x86|x86-64)}} 'interrupt' attribute only applies to functions that have a 'unsigned long' type as the second parameter}} +#elif defined(__x86_64__) +// expected-error-re@+2 {{{{(x86|x86-64)}} 'interrupt' attribute only applies to functions that have a 'unsigned long long' type as the second parameter}} +#endif +__attribute__((interrupt)) void foo7(int *a, unsigned b) {} +__attribute__((interrupt)) void foo8(int *a) {} +template +__attribute__((interrupt)) void foo9(T *a) {} + +template +void bar(T *a) { + foo9(a); // expected-error {{interrupt service routine cannot be called directly}} +} + +template +void bar1(Fn F) { + F(0); +} +__attribute__((interrupt)) void foo(int *) {} + +void g(void (*fp)(int *)); +int main(int argc, char **argv) { + void *ptr = (void *)&foo7; + g(foo8); + (void)ptr; + a::foo(ptr); // expected-error {{interrupt service routine cannot be called directly}} + bar1(foo); +#ifndef __x86_64__ + // expected-error@+2 {{interrupt service routine cannot be called directly}} +#endif + foo7((int *)argv, argc); + foo8((int *)argv); // expected-error {{interrupt service routine cannot be called directly}} + bar(argv); // expected-note {{in instantiation of function template specialization 'bar' requested here}} + return 0; +} +