Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -1188,6 +1188,14 @@ let Documentation = [MicroMipsDocs]; } +def MipsLongCall : InheritableAttr, TargetSpecificAttr { + let Spellings = [GCC<"long_call">, GCC<"far">, GCC<"near">]; + let Subjects = SubjectList<[Function]>; + let Accessors = [Accessor<"longCall", [GCC<"long_call">, GCC<"far">]>, + Accessor<"nearCall", [GCC<"near">]>]; + let Documentation = [MipsLongCallDocs]; +} + def Mode : Attr { let Spellings = [GCC<"mode">]; let Subjects = SubjectList<[Var, Enum, TypedefName, Field], ErrorDiag, Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -1323,6 +1323,20 @@ }]; } +def MipsLongCallDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +Clang supports the GNU style ``__attribute__(("CALL STYLE"))`` +attributes on MIPS targets. Where "CALL STYLE" is either ``long_call`` or +``far`` or ``near``. These attributes may be attached to a function declaration +and override the ``-mlong-calls`` command line switch. The ``long_call`` +and ``far`` attributes are synonyms and disable using the ``jal`` instruction +to call the function. The ``near`` attribute has the opposite effect - even +if code compiled using ``-mlong-calls`` switch, it forces compiler to use +the ``jal`` instruction to call the function. + }]; +} + def AVRInterruptDocs : Documentation { let Category = DocCatFunction; let Content = [{ Index: lib/CodeGen/CGCall.cpp =================================================================== --- lib/CodeGen/CGCall.cpp +++ lib/CodeGen/CGCall.cpp @@ -1802,6 +1802,13 @@ if (TargetDecl->hasAttr()) FuncAttrs.addAttribute(llvm::Attribute::Convergent); + if (const auto *LongCallAttr = TargetDecl->getAttr()) { + if (LongCallAttr->longCall()) + FuncAttrs.addAttribute("long-call"); + else if (LongCallAttr->nearCall()) + FuncAttrs.addAttribute("near-call"); + } + if (const FunctionDecl *Fn = dyn_cast(TargetDecl)) { AddAttributesFromFunctionProtoType( getContext(), FuncAttrs, Fn->getType()->getAs()); Index: lib/CodeGen/TargetInfo.cpp =================================================================== --- lib/CodeGen/TargetInfo.cpp +++ lib/CodeGen/TargetInfo.cpp @@ -6619,6 +6619,13 @@ else if (FD->hasAttr()) Fn->addFnAttr("nomicromips"); + if (const auto *LongCallAttr = FD->getAttr()) { + if (LongCallAttr->longCall()) + Fn->addFnAttr("long-call"); + else if (LongCallAttr->nearCall()) + Fn->addFnAttr("near-call"); + } + const MipsInterruptAttr *Attr = FD->getAttr(); if (!Attr) return; Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -5950,6 +5950,9 @@ case AttributeList::AT_NoMicroMips: handleSimpleAttribute(S, D, Attr); break; + case AttributeList::AT_MipsLongCall: + handleSimpleAttribute(S, D, Attr); + break; case AttributeList::AT_AMDGPUFlatWorkGroupSize: handleAMDGPUFlatWorkGroupSizeAttr(S, D, Attr); break; Index: test/CodeGen/long-call-attr.c =================================================================== --- /dev/null +++ test/CodeGen/long-call-attr.c @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -triple mips-linux-gnu -emit-llvm -o - %s | FileCheck %s + +void __attribute__((long_call)) foo1 (void); + +void __attribute__((far)) foo2 (void) {} + +// CHECK: define void @foo2() [[FAR:#[0-9]+]] + +void __attribute__((near)) foo3 (void) { foo1(); } + +// CHECK: define void @foo3() [[NEAR:#[0-9]+]] +// CHECK: call void @foo1() [[LONGCALL:#[0-9]+]] + +// CHECK: declare void @foo1() [[LONGDECL:#[0-9]+]] + +// CHECK: attributes [[FAR]] = { {{.*}} "long-call" {{.*}} } +// CHECK: attributes [[NEAR]] = { {{.*}} "near-call" {{.*}} } +// CHECK: attributes [[LONGDECL]] = { {{.*}} "long-call" {{.*}} } +// CHECK: attributes [[LONGCALL]] = { "long-call" } Index: test/Misc/pragma-attribute-supported-attributes-list.test =================================================================== --- test/Misc/pragma-attribute-supported-attributes-list.test +++ test/Misc/pragma-attribute-supported-attributes-list.test @@ -2,7 +2,7 @@ // The number of supported attributes should never go down! -// CHECK: #pragma clang attribute supports 62 attributes: +// CHECK: #pragma clang attribute supports 63 attributes: // CHECK-NEXT: AMDGPUFlatWorkGroupSize (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumSGPR (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumVGPR (SubjectMatchRule_function) @@ -31,6 +31,7 @@ // CHECK-NEXT: InternalLinkage (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record) // CHECK-NEXT: LTOVisibilityPublic (SubjectMatchRule_record) // CHECK-NEXT: MicroMips (SubjectMatchRule_function) +// CHECK-NEXT: MipsLongCall (SubjectMatchRule_function) // CHECK-NEXT: NoDebug (SubjectMatchRule_hasType_functionType, SubjectMatchRule_objc_method, SubjectMatchRule_variable_not_is_parameter) // CHECK-NEXT: NoDuplicate (SubjectMatchRule_function) // CHECK-NEXT: NoMicroMips (SubjectMatchRule_function) Index: test/Sema/attr-long-call.c =================================================================== --- /dev/null +++ test/Sema/attr-long-call.c @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -triple mips-linux-gnu -fsyntax-only -verify %s + +__attribute__((long_call(0))) void foo1(); // expected-error {{'long_call' attribute takes no arguments}} +__attribute__((far(0))) void foo2(); // expected-error {{'far' attribute takes no arguments}} +__attribute__((near(0))) void foo3(); // expected-error {{'near' attribute takes no arguments}} + +__attribute((long_call)) int a; // expected-warning {{attribute only applies to functions}} +__attribute((far)) int a; // expected-warning {{attribute only applies to functions}} +__attribute((near)) int a; // expected-warning {{attribute only applies to functions}} + +__attribute((long_call)) void foo4(); +__attribute((far)) void foo5(); +__attribute((near)) void foo6();