ABataev updated this revision to Diff 43676.
ABataev marked 3 inline comments as done.
ABataev added a comment.

Update after review


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
@@ -4965,6 +4965,10 @@
                            Fn->getValueKind(), FDecl);
       }
     }
+    if (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,17 +4545,73 @@
       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::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_interrupt_function_wrong_return_type);
+    return;
+  }
+  // Interrupt handler must have 1 or 2 parameters.
+  unsigned 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, 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_interrupt_function_wrong_second_arg)
+        << S.Context.getIntTypeForBitwidth(TypeSize, /*Signed=*/false);
+    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)
+  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:
+    handleIAInterruptAttr(S, D, Attr);
+    break;
+  default:
     handleARMInterruptAttr(S, D, Attr);
+    break;
+  }
 }
 
 static void handleMips16Attribute(Sema &S, Decl *D, const AttributeList &Attr) {
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -259,6 +259,16 @@
    "MIPS 'interrupt' attribute only applies to functions that have "
    "%select{no parameters|a 'void' return type}0">,
    InGroup<IgnoredAttributes>;
+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 %0 type as the second argument">;
+def err_interrupt_function_called : Error<
+  "interrupt service routine can't be used directly">;
 def warn_unused_parameter : Warning<"unused parameter %0">,
   InGroup<UnusedParameter>, DefaultIgnore;
 def warn_unused_variable : Warning<"unused variable %0">,
Index: include/clang/Basic/AttrDocs.td
===================================================================
--- include/clang/Basic/AttrDocs.td
+++ include/clang/Basic/AttrDocs.td
@@ -1859,3 +1859,56 @@
 
   }];
 }
+
+def IAInterruptDocs : 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/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<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<TargetAnyX86> {
+  // 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 = [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,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/Sema/attr-x86-interrupt.c
===================================================================
--- test/Sema/attr-x86-interrupt.c
+++ test/Sema/attr-x86-interrupt.c
@@ -0,0 +1,56 @@
+// 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}}
+
+#ifdef _LP64
+#elif defined(_ILP32)
+#else
+#endif
+
+__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}}
+#ifdef _LP64
+// expected-error@+6 {{interrupt service routine should have 'unsigned long' type as the second argument}}
+#elif defined(__x86_64__)
+// expected-error@+4 {{interrupt service routine should have 'unsigned long long' type as the second argument}}
+#else
+// expected-error@+2 {{interrupt service routine should have 'unsigned int' type as the second argument}}
+#endif
+__attribute__((interrupt)) void foo5(void *a, float b) {}
+#ifdef _LP64
+// expected-error@+6 {{interrupt service routine should have 'unsigned long' type as the second argument}}
+#elif defined(__x86_64__)
+// expected-error@+4 {{interrupt service routine should have 'unsigned long long' type as the second argument}}
+#else
+// expected-error@+2 {{interrupt service routine should have 'unsigned int' type as the second argument}}
+#endif
+__attribute__((interrupt)) void foo6(float *a, int b) {}
+
+#ifdef _LP64
+// expected-error@+4 {{interrupt service routine should have 'unsigned long' type as the second argument}}
+#elif defined(__x86_64__)
+// expected-error@+2 {{interrupt service routine should have 'unsigned long long' type as the second argument}}
+#endif
+__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;
+#ifndef __x86_64__
+  // expected-error@+2 {{interrupt service routine can't be used directly}}
+#endif
+  foo7((int *)argv, argc);
+  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

Reply via email to