Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -308,6 +308,7 @@ def TargetMips32 : TargetArch<["mips", "mipsel"]>; def TargetAnyMips : TargetArch<["mips", "mipsel", "mips64", "mips64el"]>; def TargetMSP430 : TargetArch<["msp430"]>; +def TargetRISCV : TargetArch<["riscv32", "riscv64"]>; def TargetX86 : TargetArch<["x86"]>; def TargetAnyX86 : TargetArch<["x86", "x86_64"]>; def TargetWindows : TargetArch<["x86", "x86_64", "arm", "thumb", "aarch64"]> { @@ -1374,6 +1375,17 @@ let Documentation = [MicroMipsDocs]; } +def RISCVInterrupt : InheritableAttr, TargetSpecificAttr { + let Spellings = [GCC<"interrupt">]; + let Subjects = SubjectList<[Function]>; + let Args = [EnumArgument<"Interrupt", "InterruptType", + ["user", "supervisor", "machine"], + ["user", "supervisor", "machine"], + 1>]; + let ParseKind = "Interrupt"; + let Documentation = [RISCVInterruptDocs]; +} + // This is not a TargetSpecificAttr so that is silently accepted and // ignored on other targets as encouraged by the OpenCL spec. // Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -1501,6 +1501,29 @@ }]; } +def RISCVInterruptDocs : Documentation { + let Category = DocCatFunction; + let Heading = "interrupt (RISCV)"; + let Content = [{ +Clang supports the GNU style ``__attribute__((interrupt))`` attribute on RISCV +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 service routine. + +Permissible values for this parameter are ``user``, ``supervisor``, +and ``machine``. If there is no parameter, then it defaults to machine. + +Repeated interrupt attribute on the same declaration will cause a warning +to be emitted. In case of repeated declarations, the last one prevails. + +Refer to: +https://gcc.gnu.org/onlinedocs/gcc/RISC-V-Function-Attributes.html +https://riscv.org/specifications/privileged-isa/ +The RISC-V Instruction Set Manual Volume II: Privileged Architecture +Version 1.10. + }]; +} + def AVRInterruptDocs : Documentation { let Category = DocCatFunction; let Heading = "interrupt (AVR)"; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -274,6 +274,14 @@ "MIPS 'interrupt' attribute only applies to functions that have " "%select{no parameters|a 'void' return type}0">, InGroup; +def warn_riscv_repeated_interrupt_attribute : Warning< + "repeated RISC-V 'interrupt' attribute">, InGroup; +def note_riscv_repeated_interrupt_attribute : Note< + "repeated RISC-V 'interrupt' attribute is here">; +def warn_riscv_interrupt_attribute : Warning< + "RISC-V 'interrupt' attribute only applies to functions that have " + "%select{no parameters|a 'void' return type}0">, + InGroup; def warn_unused_parameter : Warning<"unused parameter %0">, InGroup, DefaultIgnore; def warn_unused_variable : Warning<"unused variable %0">, @@ -281,7 +289,7 @@ def warn_unused_local_typedef : Warning< "unused %select{typedef|type alias}0 %1">, InGroup, DefaultIgnore; -def warn_unused_property_backing_ivar : +def warn_unused_property_backing_ivar : Warning<"ivar %0 which backs the property is not " "referenced in this property's accessor">, InGroup, DefaultIgnore; Index: lib/CodeGen/TargetInfo.cpp =================================================================== --- lib/CodeGen/TargetInfo.cpp +++ lib/CodeGen/TargetInfo.cpp @@ -8971,6 +8971,27 @@ public: RISCVTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT, unsigned XLen) : TargetCodeGenInfo(new RISCVABIInfo(CGT, XLen)) {} + + void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV, + CodeGen::CodeGenModule &CGM) const override { + const auto *FD = dyn_cast_or_null(D); + if (!FD) return; + + const auto *Attr = FD->getAttr(); + if (!Attr) + return; + + const char *Kind; + switch (Attr->getInterrupt()) { + case RISCVInterruptAttr::user: Kind = "user"; break; + case RISCVInterruptAttr::supervisor: Kind = "supervisor"; break; + case RISCVInterruptAttr::machine: Kind = "machine"; break; + } + + auto *Fn = cast(GV); + + Fn->addFnAttr("interrupt", Kind); + } }; } // namespace Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -5372,6 +5372,64 @@ handleSimpleAttribute(S, D, AL); } + +static void handleRISCVInterruptAttr(Sema &S, Decl *D, + const ParsedAttr &AL) { + // Warn about repeated attributes. + if (const auto *A = D->getAttr()) { + S.Diag(AL.getRange().getBegin(), + diag::warn_riscv_repeated_interrupt_attribute); + S.Diag(A->getLocation(), diag::note_riscv_repeated_interrupt_attribute); + return; + } + + // Check the attribute argument. Argument is optional. + if (!checkAttributeAtMostNumArgs(S, AL, 1)) + return; + + StringRef Str; + SourceLocation ArgLoc; + + // 'machine'is the default interrupt mode. + if (AL.getNumArgs() == 0) + Str = "machine"; + else if (!S.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc)) + return; + + // Semantic checks for a function with the 'interrupt' attribute: + // - Must be a function. + // - Must have no parameters. + // - Must have the 'void' return type. + // - The attribute itself must either have no argument or one of the + // valid interrupt types, see [RISCVInterruptDocs]. + + if (D->getFunctionType() == nullptr) { + S.Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type) + << "'interrupt'" << ExpectedFunction; + return; + } + + if (hasFunctionProto(D) && getFunctionOrMethodNumParams(D) != 0) { + S.Diag(D->getLocation(), diag::warn_riscv_interrupt_attribute) << 0; + return; + } + + if (!getFunctionOrMethodResultType(D)->isVoidType()) { + S.Diag(D->getLocation(), diag::warn_riscv_interrupt_attribute) << 1; + return; + } + + RISCVInterruptAttr::InterruptType Kind; + if (!RISCVInterruptAttr::ConvertStrToInterruptType(Str, Kind)) { + S.Diag(AL.getLoc(), diag::warn_attribute_type_not_supported) + << AL.getName() << Str << ArgLoc; + return; + } + + D->addAttr(::new (S.Context) RISCVInterruptAttr( + AL.getLoc(), S.Context, Kind, AL.getAttributeSpellingListIndex())); +} + static void handleInterruptAttr(Sema &S, Decl *D, const ParsedAttr &AL) { // Dispatch the interrupt attribute based on the current target. switch (S.Context.getTargetInfo().getTriple().getArch()) { @@ -5389,6 +5447,10 @@ case llvm::Triple::avr: handleAVRInterruptAttr(S, D, AL); break; + case llvm::Triple::riscv32: + case llvm::Triple::riscv64: + handleRISCVInterruptAttr(S, D, AL); + break; default: handleARMInterruptAttr(S, D, AL); break; Index: test/Sema/riscv-interrupt-attr.c =================================================================== --- test/Sema/riscv-interrupt-attr.c +++ test/Sema/riscv-interrupt-attr.c @@ -0,0 +1,62 @@ +// RUN: %clang_cc1 -triple riscv32-unknown-elf -emit-llvm -DCHECK_IR < %s| FileCheck %s +// RUN: %clang_cc1 -triple riscv64-unknown-elf -emit-llvm -DCHECK_IR < %s| FileCheck %s +// RUN: %clang_cc1 %s -triple riscv32-unknown-elf -verify -fsyntax-only +// RUN: %clang_cc1 %s -triple riscv64-unknown-elf -verify -fsyntax-only + +#if defined(CHECK_IR) +// CHECK-LABEL: @foo_user() #0 +// CHECK: ret void +__attribute__((interrupt("user"))) void foo_user(void) {} +// CHECK-LABEL: @foo_supervisor() #1 +// CHECK: ret void +__attribute__((interrupt("supervisor"))) void foo_supervisor(void) {} +// CHECK-LABEL: @foo_machine() #2 +// CHECK: ret void +__attribute__((interrupt("machine"))) void foo_machine(void) {} +// CHECK-LABEL: @foo_default() #2 +// CHECK: ret void +__attribute__((interrupt())) void foo_default(void) {} +// CHECK-LABEL: @foo_default2() #2 +// CHECK: ret void +__attribute__((interrupt())) void foo_default2(void) {} +// CHECK: attributes #0 +// CHECK: "interrupt"="user" +// CHECK: attributes #1 +// CHECK: "interrupt"="supervisor" +// CHECK: attributes #2 +// CHECK: "interrupt"="machine" +#else +struct a { int b; }; + +struct a test __attribute__((interrupt)); // expected-warning {{'interrupt' attribute only applies to functions}} + +__attribute__((interrupt("USER"))) void foo1(void) {} // expected-warning {{'interrupt' attribute argument not supported: USER}} + +__attribute__((interrupt("user", 1))) void foo2(void) {} // expected-error {{'interrupt' attribute takes no more than 1 argument}} + +__attribute__((interrupt)) int foo3(void) {return 0;} // expected-warning {{RISC-V 'interrupt' attribute only applies to functions that have a 'void' return type}} + +__attribute__((interrupt())) void foo4(); +__attribute__((interrupt())) void foo4() {}; + +__attribute__((interrupt())) void foo5(int a) {} // expected-warning {{RISC-V 'interrupt' attribute only applies to functions that have no parameters}} + +__attribute__((interrupt("user"), interrupt("supervisor"))) void foo6(void) {} // expected-warning {{repeated RISC-V 'interrupt' attribute}} \ + // expected-note {{repeated RISC-V 'interrupt' attribute is here}} + +__attribute__((interrupt, interrupt)) void foo7(void) {} // expected-warning {{repeated RISC-V 'interrupt' attribute}} \ + // expected-note {{repeated RISC-V 'interrupt' attribute is here}} + +__attribute__((interrupt(""))) void foo8(void) {} // expected-warning {{'interrupt' attribute argument not supported}} + +__attribute__((interrupt("user"))) void foo9(void); +__attribute__((interrupt("supervisor"))) void foo9(void); +__attribute__((interrupt("machine"))) void foo9(void); + +__attribute__((interrupt("user"))) void foo10(void) {} +__attribute__((interrupt("supervisor"))) void foo11(void) {} +__attribute__((interrupt("machine"))) void foo12(void) {} +__attribute__((interrupt())) void foo13(void) {} +__attribute__((interrupt)) void foo14(void) {} +#endif + Index: test/Sema/riscv-interrupt-attr.cpp =================================================================== --- test/Sema/riscv-interrupt-attr.cpp +++ test/Sema/riscv-interrupt-attr.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -x c++ -triple riscv32-unknown-elf -emit-llvm -DCHECK_IR < %s | FileCheck %s +// RUN: %clang_cc1 -x c++ -triple riscv64-unknown-elf -emit-llvm -DCHECK_IR < %s | FileCheck %s +// RUN: %clang_cc1 %s -triple riscv32-unknown-elf -verify -fsyntax-only +// RUN: %clang_cc1 %s -triple riscv64-unknown-elf -verify -fsyntax-only + +#if defined(CHECK_IR) +// CHECK-LABEL: @_Z11foo_defaultv() #0 +// CHECK: ret void +[[gnu::interrupt]] void foo_default() {} +// CHECK: attributes #0 +// CHECK: "interrupt"="machine" +#else +[[gnu::interrupt]] [[gnu::interrupt]] void foo1() {} // expected-warning {{repeated RISC-V 'interrupt' attribute}} \ + // expected-note {{repeated RISC-V 'interrupt' attribute is here}} +[[gnu::interrupt]] void foo2() {} +#endif