diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index d5204b2869667..6cbc895fe0308 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -428,6 +428,7 @@ def TargetX86 : TargetArch<["x86"]>; def TargetAnyX86 : TargetArch<["x86", "x86_64"]>; def TargetWebAssembly : TargetArch<["wasm32", "wasm64"]>; def TargetNVPTX : TargetArch<["nvptx", "nvptx64"]>; +def TargetXtensa : TargetArch<["xtensa"]>; def TargetWindows : TargetSpec { let OSes = ["Win32"]; } @@ -1725,11 +1726,22 @@ def MipsLongCall : InheritableAttr, TargetSpecificAttr { def MipsShortCall : InheritableAttr, TargetSpecificAttr { let Spellings = [GCC<"short_call">, GCC<"near">]; let Subjects = SubjectList<[Function]>; - let Documentation = [MipsShortCallStyleDocs]; - let SimpleHandler = 1; + let Documentation = [ShortCallStyleDocs]; + let ParseKind = "ShortCall"; + let HasCustomParsing = 1; + let SemaHandler = 1; } def : MutualExclusions<[MipsLongCall, MipsShortCall]>; +def XtensaShortCall : InheritableAttr, TargetSpecificAttr { + let Spellings = [GCC<"short_call">, GCC<"near">]; + let Subjects = SubjectList<[Function]>; + let Documentation = [ShortCallStyleDocs]; + let ParseKind = "ShortCall"; + let HasCustomParsing = 1; + let SemaHandler = 1; +} + def M68kInterrupt : InheritableAttr, TargetSpecificAttr { // NOTE: If you add any additional spellings, ARMInterrupt's, MipsInterrupt's // MSP430Interrupt's and AnyX86Interrupt's spellings must match. diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 2c950231255d7..27693e46f2b35 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -2296,18 +2296,19 @@ as ``-mlong-calls`` and ``-mno-long-calls``. }]; } -def MipsShortCallStyleDocs : Documentation { +def ShortCallStyleDocs : Documentation { let Category = DocCatFunction; let Heading = "short_call, near"; let Content = [{ Clang supports the ``__attribute__((long_call))``, ``__attribute__((far))``, ``__attribute__((short__call))``, and ``__attribute__((near))`` attributes -on MIPS targets. These attributes may only be added to function declarations -and change the code generated by the compiler when directly calling +on MIPS and Xtensa targets. These attributes may only be added to function +declarations and change the code generated by the compiler when directly calling the function. The ``short_call`` and ``near`` attributes are synonyms and -allow calls to the function to be made using the ``jal`` instruction, which -requires the function to be located in the same naturally aligned 256MB segment -as the caller. The ``long_call`` and ``far`` attributes are synonyms and +allow calls to the function to be made using the ``jal`` instruction for MIPS and +``calln`` instruction for Xtensa, which requires the function to be located +in the same naturally aligned 256MB segment as the caller. +The ``long_call`` and ``far`` attributes are synonyms and require the use of a different call sequence that works regardless of the distance between the functions. diff --git a/clang/lib/CodeGen/Targets/Xtensa.cpp b/clang/lib/CodeGen/Targets/Xtensa.cpp index b615ce7913526..22b11b78a249f 100644 --- a/clang/lib/CodeGen/Targets/Xtensa.cpp +++ b/clang/lib/CodeGen/Targets/Xtensa.cpp @@ -257,6 +257,16 @@ class XtensaTargetCodeGenInfo : public TargetCodeGenInfo { public: XtensaTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT) : TargetCodeGenInfo(std::make_unique(CGT)) {} + + void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV, + CodeGen::CodeGenModule &CGM) const override { + const FunctionDecl *FD = dyn_cast_or_null(D); + if (!FD) + return; + llvm::Function *Fn = cast(GV); + if (FD->hasAttr()) + Fn->addFnAttr("short-call"); + } }; } // namespace diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index ed69e802c95dd..effa7c959e4d0 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -7605,6 +7605,32 @@ static void handleAVRSignalAttr(Sema &S, Decl *D, const ParsedAttr &AL) { handleSimpleAttribute(S, D, AL); } +static void handleMipsShortCall(Sema &S, Decl *D, const ParsedAttr &AL) { + if (!isFunctionOrMethod(D)) { + S.Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type) + << "'short_call'" << ExpectedFunction; + return; + } + + if (!AL.checkExactlyNumArgs(S, 0)) + return; + + handleSimpleAttribute(S, D, AL); +} + +static void handleXtensaShortCall(Sema &S, Decl *D, const ParsedAttr &AL){ + if (!isFunctionOrMethod(D)) { + S.Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type) + << "'short_call'" << ExpectedFunction; + return; + } + + if (!AL.checkExactlyNumArgs(S, 0)) + return; + + handleSimpleAttribute(S, D, AL); +} + static void handleBPFPreserveAIRecord(Sema &S, RecordDecl *RD) { // Add preserve_access_index attribute to all fields and inner records. for (auto *D : RD->decls()) { @@ -7802,6 +7828,20 @@ static void handleRISCVInterruptAttr(Sema &S, Decl *D, D->addAttr(::new (S.Context) RISCVInterruptAttr(S.Context, AL, Kind)); } +static void handleShortCallAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + switch (S.Context.getTargetInfo().getTriple().getArch()) { + case llvm::Triple::xtensa: + handleXtensaShortCall(S, D, AL); + break; + case llvm::Triple::mips64: + case llvm::Triple::mips: + handleMipsShortCall(S, D, AL); + break; + default: + break; + } +} + 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()) { @@ -9493,6 +9533,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_UsingIfExists: handleSimpleAttribute(S, D, AL); break; + case ParsedAttr::AT_ShortCall: + handleShortCallAttr(S, D, AL); + break; } } diff --git a/clang/test/CodeGen/Xtensa/xtensa-short-call.c b/clang/test/CodeGen/Xtensa/xtensa-short-call.c new file mode 100644 index 0000000000000..54858ff5d4ec1 --- /dev/null +++ b/clang/test/CodeGen/Xtensa/xtensa-short-call.c @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -triple xtensa -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple xtensa -S -o - %s | FileCheck %s --check-prefix=ASM + +void foo1 (void); +void __attribute__((short_call)) foo (void); +void __attribute__((near)) bar (void) { foo1(); foo(); } + +// CHECK: define{{.*}} void @bar() [[NEAR:#[0-9]+]] + +// CHECK: declare void @foo() [[SHORTDECL:#[0-9]+]] + +// CHECK: attributes [[NEAR]] = { {{.*}} "short-call" {{.*}} } +// CHECK: attributes [[SHORTDECL]] = { {{.*}} "short-call" {{.*}} } + +// ASM: callx8 a8 +// ASM: call8 foo diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index eaf6d34421bbe..41ae06bcdf583 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -91,7 +91,7 @@ // CHECK-NEXT: MinVectorWidth (SubjectMatchRule_function) // CHECK-NEXT: Mips16 (SubjectMatchRule_function) // CHECK-NEXT: MipsLongCall (SubjectMatchRule_function) -// CHECK-NEXT: MipsShortCall (SubjectMatchRule_function) +// CHECK-NEXT: ShortCall (SubjectMatchRule_function) // CHECK-NEXT: NSConsumed (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: NSConsumesSelf (SubjectMatchRule_objc_method) // CHECK-NEXT: NSErrorDomain (SubjectMatchRule_enum) diff --git a/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp b/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp index e248cde518ffc..71853631556e6 100644 --- a/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp +++ b/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp @@ -1395,6 +1395,7 @@ XtensaTargetLowering::LowerCall(CallLoweringInfo &CLI, std::string name; unsigned char TF = 0; + bool HasShortCallAttr = false; // Accept direct calls by converting symbolic call addresses to the // associated Target* opcodes. @@ -1414,9 +1415,12 @@ XtensaTargetLowering::LowerCall(CallLoweringInfo &CLI, const GlobalValue *GV = G->getGlobal(); name = GV->getName().str(); + if (auto *F = dyn_cast(GV)) + if (F->hasFnAttribute("short-call")) + HasShortCallAttr = true; } - if ((!name.empty()) && isLongCall(name.c_str())) { + if (!name.empty() && isLongCall(name.c_str()) && !HasShortCallAttr) { // Create a constant pool entry for the callee address XtensaCP::XtensaCPModifier Modifier = XtensaCP::no_modifier;