Skip to content

Commit e8d9c75

Browse files
committed
New "reverse C" calling convention in LLVM and Clang.
Test case: #define PARAMS \ const char *ptr, long p2, \ long p3, long p4, long p5, long p6 #define ARGS ptr, p2, p3, p4, p5, p6 #define UNLIKELY(x) \ __builtin_expect(x, 0) #if __has_attribute(reverse_call) #define CC __attribute__((reverse_call)) #else #define CC #endif CC const char *Next(PARAMS); const char *Fallback(PARAMS); CC const char *Parse(PARAMS) { if (UNLIKELY(!ptr)) { // This ruins everything. ptr = Fallback(ARGS); } ptr++; __attribute__((musttail)) return Next(ARGS); } Before (existing calling convention): 0000000000000000 <Parse>: 0: 41 57 push r15 2: 41 56 push r14 4: 41 55 push r13 6: 41 54 push r12 8: 53 push rbx 9: 4d 89 ce mov r14,r9 c: 4d 89 c7 mov r15,r8 f: 49 89 cc mov r12,rcx 12: 49 89 d5 mov r13,rdx 15: 48 89 f3 mov rbx,rsi 18: 48 85 ff test rdi,rdi 1b: 74 21 je 3e <Parse+0x3e> 1d: 48 83 c7 01 add rdi,0x1 21: 48 89 de mov rsi,rbx 24: 4c 89 ea mov rdx,r13 27: 4c 89 e1 mov rcx,r12 2a: 4d 89 f8 mov r8,r15 2d: 4d 89 f1 mov r9,r14 30: 5b pop rbx 31: 41 5c pop r12 33: 41 5d pop r13 35: 41 5e pop r14 37: 41 5f pop r15 39: e9 00 00 00 00 jmp 3e <Parse+0x3e> 3a: R_X86_64_PLT32 Next-0x4 3e: 31 ff xor edi,edi 40: 48 89 de mov rsi,rbx 43: 4c 89 ea mov rdx,r13 46: 4c 89 e1 mov rcx,r12 49: 4d 89 f8 mov r8,r15 4c: 4d 89 f1 mov r9,r14 4f: e8 00 00 00 00 call 54 <Parse+0x54> 50: R_X86_64_PLT32 Fallback-0x4 54: 48 89 c7 mov rdi,rax 57: eb c4 jmp 1d <Parse+0x1d> After (new reverse_call calling convention): 0000000000000000 <Parse>: 0: 48 85 db test rbx,rbx 3: 74 09 je e <Parse+0xe> 5: 48 83 c3 01 add rbx,0x1 9: e9 00 00 00 00 jmp e <Parse+0xe> a: R_X86_64_PLT32 Next-0x4 e: 50 push rax f: 31 ff xor edi,edi 11: 48 89 ee mov rsi,rbp 14: 4c 89 e2 mov rdx,r12 17: 4c 89 e9 mov rcx,r13 1a: 4d 89 f0 mov r8,r14 1d: 4d 89 f9 mov r9,r15 20: e8 00 00 00 00 call 25 <Parse+0x25> 21: R_X86_64_PLT32 Fallback-0x4 25: 48 89 c3 mov rbx,rax 28: 48 83 c4 08 add rsp,0x8 2c: 48 83 c3 01 add rbx,0x1 30: e9 00 00 00 00 jmp 35 <Parse+0x35> The fast path has gone from 24 instructions (including 5 spills) to 4 instructions (0 spills).
1 parent 0a2708d commit e8d9c75

File tree

20 files changed

+102
-1
lines changed

20 files changed

+102
-1
lines changed

clang/include/clang-c/Index.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3424,6 +3424,7 @@ enum CXCallingConv {
34243424
CXCallingConv_PreserveAll = 15,
34253425
CXCallingConv_AArch64VectorCall = 16,
34263426
CXCallingConv_SwiftAsync = 17,
3427+
CXCallingConv_ReverseCall = 18,
34273428

34283429
CXCallingConv_Invalid = 100,
34293430
CXCallingConv_Unexposed = 200

clang/include/clang/Basic/Attr.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2567,6 +2567,11 @@ def PreserveMost : DeclOrTypeAttr {
25672567
let Documentation = [PreserveMostDocs];
25682568
}
25692569

2570+
def ReverseCall : DeclOrTypeAttr {
2571+
let Spellings = [Clang<"reverse_call">];
2572+
let Documentation = [ReverseCallDocs];
2573+
}
2574+
25702575
def PreserveAll : DeclOrTypeAttr {
25712576
let Spellings = [Clang<"preserve_all">];
25722577
let Documentation = [PreserveAllDocs];

clang/include/clang/Basic/AttrDocs.td

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4864,6 +4864,20 @@ in the future.
48644864
}];
48654865
}
48664866

4867+
def ReverseCallDocs : Documentation {
4868+
let Category = DocCatCallingConvs;
4869+
let Content = [{
4870+
This calling convention makes all registers available for
4871+
passing arguments, but assigns them in reverse order from the default calling
4872+
convention to reduce the chance of overlap. There are no callee-saved
4873+
registers, and the registers that are callee-save under ``ccc`` are the first
4874+
to be assigned as arguments. This makes it very efficient to perform a series
4875+
of tail calls to other ``reverse_call`` functions in threaded code style, while
4876+
reducing the chance of spills or register shuffles when performing non-tail
4877+
calls to regular fallback functions.
4878+
}];
4879+
}
4880+
48674881
def PreserveAllDocs : Documentation {
48684882
let Category = DocCatCallingConvs;
48694883
let Content = [{

clang/include/clang/Basic/Specifiers.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ namespace clang {
280280
CC_PreserveMost, // __attribute__((preserve_most))
281281
CC_PreserveAll, // __attribute__((preserve_all))
282282
CC_AArch64VectorCall, // __attribute__((aarch64_vector_pcs))
283+
CC_ReverseCall, // __attribute__((reverse_call))
283284
};
284285

285286
/// Checks whether the given calling convention supports variadic

clang/lib/AST/ItaniumMangle.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3098,6 +3098,7 @@ StringRef CXXNameMangler::getCallingConvQualifierName(CallingConv CC) {
30983098
case CC_OpenCLKernel:
30993099
case CC_PreserveMost:
31003100
case CC_PreserveAll:
3101+
case CC_ReverseCall:
31013102
// FIXME: we should be mangling all of the above.
31023103
return "";
31033104

clang/lib/AST/Type.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3156,6 +3156,7 @@ StringRef FunctionType::getNameForCallConv(CallingConv CC) {
31563156
case CC_SwiftAsync: return "swiftasynccall";
31573157
case CC_PreserveMost: return "preserve_most";
31583158
case CC_PreserveAll: return "preserve_all";
3159+
case CC_ReverseCall: return "reverse_call";
31593160
}
31603161

31613162
llvm_unreachable("Invalid calling convention.");

clang/lib/AST/TypePrinter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -983,6 +983,9 @@ void TypePrinter::printFunctionAfter(const FunctionType::ExtInfo &Info,
983983
case CC_PreserveMost:
984984
OS << " __attribute__((preserve_most))";
985985
break;
986+
case CC_ReverseCall:
987+
OS << " __attribute__((reverse_call))";
988+
break;
986989
case CC_PreserveAll:
987990
OS << " __attribute__((preserve_all))";
988991
break;
@@ -1718,6 +1721,7 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
17181721
case attr::MSABI: OS << "ms_abi"; break;
17191722
case attr::SysVABI: OS << "sysv_abi"; break;
17201723
case attr::RegCall: OS << "regcall"; break;
1724+
case attr::ReverseCall: OS << "reverse_call"; break;
17211725
case attr::Pcs: {
17221726
OS << "pcs(";
17231727
QualType t = T->getEquivalentType();

clang/lib/Basic/Targets/X86.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@ class LLVM_LIBRARY_VISIBILITY X86TargetInfo : public TargetInfo {
358358
case CC_X86Pascal:
359359
case CC_IntelOclBicc:
360360
case CC_OpenCLKernel:
361+
case CC_ReverseCall:
361362
return CCCR_OK;
362363
case CC_SwiftAsync:
363364
return CCCR_Error;
@@ -729,6 +730,7 @@ class LLVM_LIBRARY_VISIBILITY X86_64TargetInfo : public X86TargetInfo {
729730
case CC_PreserveAll:
730731
case CC_X86RegCall:
731732
case CC_OpenCLKernel:
733+
case CC_ReverseCall:
732734
return CCCR_OK;
733735
default:
734736
return CCCR_Warning;
@@ -806,6 +808,7 @@ class LLVM_LIBRARY_VISIBILITY WindowsX86_64TargetInfo
806808
case CC_SwiftAsync:
807809
case CC_X86RegCall:
808810
case CC_OpenCLKernel:
811+
case CC_ReverseCall:
809812
return CCCR_OK;
810813
default:
811814
return CCCR_Warning;

clang/lib/CodeGen/CGCall.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ unsigned CodeGenTypes::ClangCallConvToLLVMCallConv(CallingConv CC) {
6767
case CC_PreserveAll: return llvm::CallingConv::PreserveAll;
6868
case CC_Swift: return llvm::CallingConv::Swift;
6969
case CC_SwiftAsync: return llvm::CallingConv::SwiftTail;
70+
case CC_ReverseCall: return llvm::CallingConv::ReverseC;
7071
}
7172
}
7273

@@ -242,6 +243,9 @@ static CallingConv getCallingConventionForDecl(const ObjCMethodDecl *D,
242243
if (D->hasAttr<PreserveAllAttr>())
243244
return CC_PreserveAll;
244245

246+
if (D->hasAttr<ReverseCallAttr>())
247+
return CC_ReverseCall;
248+
245249
return CC_C;
246250
}
247251

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4742,6 +4742,9 @@ static void handleCallConvAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
47424742
case ParsedAttr::AT_PreserveAll:
47434743
D->addAttr(::new (S.Context) PreserveAllAttr(S.Context, AL));
47444744
return;
4745+
case ParsedAttr::AT_ReverseCall:
4746+
D->addAttr(::new (S.Context) ReverseCallAttr(S.Context, AL));
4747+
return;
47454748
default:
47464749
llvm_unreachable("unexpected attribute kind");
47474750
}
@@ -4913,6 +4916,9 @@ bool Sema::CheckCallingConvAttr(const ParsedAttr &Attrs, CallingConv &CC,
49134916
case ParsedAttr::AT_PreserveAll:
49144917
CC = CC_PreserveAll;
49154918
break;
4919+
case ParsedAttr::AT_ReverseCall:
4920+
CC = CC_ReverseCall;
4921+
break;
49164922
default: llvm_unreachable("unexpected attribute kind");
49174923
}
49184924

@@ -8290,6 +8296,7 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
82908296
case ParsedAttr::AT_PreserveMost:
82918297
case ParsedAttr::AT_PreserveAll:
82928298
case ParsedAttr::AT_AArch64VectorPcs:
8299+
case ParsedAttr::AT_ReverseCall:
82938300
handleCallConvAttr(S, D, AL);
82948301
break;
82958302
case ParsedAttr::AT_Suppress:

clang/lib/Sema/SemaType.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ static void diagnoseBadTypeAttribute(Sema &S, const ParsedAttr &attr,
125125
case ParsedAttr::AT_Pcs: \
126126
case ParsedAttr::AT_IntelOclBicc: \
127127
case ParsedAttr::AT_PreserveMost: \
128-
case ParsedAttr::AT_PreserveAll
128+
case ParsedAttr::AT_PreserveAll: \
129+
case ParsedAttr::AT_ReverseCall
129130

130131
// Function type attributes.
131132
#define FUNCTION_TYPE_ATTRS_CASELIST \
@@ -7409,6 +7410,8 @@ static Attr *getCCTypeAttr(ASTContext &Ctx, ParsedAttr &Attr) {
74097410
return createSimpleAttr<SysVABIAttr>(Ctx, Attr);
74107411
case ParsedAttr::AT_PreserveMost:
74117412
return createSimpleAttr<PreserveMostAttr>(Ctx, Attr);
7413+
case ParsedAttr::AT_ReverseCall:
7414+
return createSimpleAttr<ReverseCallAttr>(Ctx, Attr);
74127415
case ParsedAttr::AT_PreserveAll:
74137416
return createSimpleAttr<PreserveAllAttr>(Ctx, Attr);
74147417
}

clang/tools/libclang/CXType.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,7 @@ CXCallingConv clang_getFunctionTypeCallingConv(CXType X) {
669669
TCALLINGCONV(SwiftAsync);
670670
TCALLINGCONV(PreserveMost);
671671
TCALLINGCONV(PreserveAll);
672+
TCALLINGCONV(ReverseCall);
672673
case CC_SpirFunction: return CXCallingConv_Unexposed;
673674
case CC_OpenCLKernel: return CXCallingConv_Unexposed;
674675
break;

llvm/docs/LangRef.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,14 @@ added in the future:
440440
the GHC or the HiPE convention is used. <CodeGenerator.html#id80>`_ This
441441
calling convention does not support varargs and requires the prototype of
442442
all callees to exactly match the prototype of the function definition.
443+
"``reverse_ccc``" - This calling convention makes all registers available for
444+
passing arguments, but assigns them in reverse order from ``ccc`` to reduce
445+
the chance of overlap. There are no callee-saved registers, and the
446+
registers that are callee-save under ``ccc`` are the first to be assigned
447+
as arguments. This makes it very efficient to perform a series of tail
448+
calls to other ``reverse_ccc`` functions in threaded code style, while
449+
reducing the chance of spills or register shuffles when performing non-tail
450+
calls to regular ``ccc`` fallback functions.
443451
"``swiftcc``" - This calling convention is used for Swift language.
444452
- On X86-64 RCX and R8 are available for additional integer returns, and
445453
XMM2 and XMM3 are available for additional FP/vector returns.

llvm/include/llvm/AsmParser/LLToken.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ enum Kind {
174174
kw_amdgpu_kernel,
175175
kw_amdgpu_gfx,
176176
kw_tailcc,
177+
kw_reverse_ccc,
177178

178179
// Attributes:
179180
kw_attributes,

llvm/include/llvm/IR/CallingConv.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ namespace CallingConv {
9191
/// clean up their stack.
9292
SwiftTail = 20,
9393

94+
/// ReverseC - This calling convention makes all registers available for
95+
/// arguments, but assigns them in reverse order from CallingConv::C, and
96+
/// uses no callee-saved registers.
97+
ReverseC = 21,
98+
9499
// Target - This is the start of the target-specific calling conventions,
95100
// e.g. fastcall and thiscall on X86.
96101
FirstTargetCC = 64,

llvm/lib/AsmParser/LLLexer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,7 @@ lltok::Kind LLLexer::LexIdentifier() {
627627
KEYWORD(amdgpu_kernel);
628628
KEYWORD(amdgpu_gfx);
629629
KEYWORD(tailcc);
630+
KEYWORD(reverse_ccc);
630631

631632
KEYWORD(cc);
632633
KEYWORD(c);

llvm/lib/AsmParser/LLParser.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1811,6 +1811,7 @@ void LLParser::parseOptionalDLLStorageClass(unsigned &Res) {
18111811
/// ::= 'amdgpu_cs'
18121812
/// ::= 'amdgpu_kernel'
18131813
/// ::= 'tailcc'
1814+
/// ::= 'reverse_ccc'
18141815
/// ::= 'cc' UINT
18151816
///
18161817
bool LLParser::parseOptionalCallingConv(unsigned &CC) {
@@ -1863,6 +1864,7 @@ bool LLParser::parseOptionalCallingConv(unsigned &CC) {
18631864
case lltok::kw_amdgpu_cs: CC = CallingConv::AMDGPU_CS; break;
18641865
case lltok::kw_amdgpu_kernel: CC = CallingConv::AMDGPU_KERNEL; break;
18651866
case lltok::kw_tailcc: CC = CallingConv::Tail; break;
1867+
case lltok::kw_reverse_ccc: CC = CallingConv::ReverseC; break;
18661868
case lltok::kw_cc: {
18671869
Lex.Lex();
18681870
return parseUInt32(CC);

llvm/lib/IR/AsmWriter.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ static void PrintCallingConv(unsigned cc, raw_ostream &Out) {
299299
case CallingConv::CXX_FAST_TLS: Out << "cxx_fast_tlscc"; break;
300300
case CallingConv::GHC: Out << "ghccc"; break;
301301
case CallingConv::Tail: Out << "tailcc"; break;
302+
case CallingConv::ReverseC: Out << "reverse_ccc"; break;
302303
case CallingConv::CFGuard_Check: Out << "cfguard_checkcc"; break;
303304
case CallingConv::X86_StdCall: Out << "x86_stdcallcc"; break;
304305
case CallingConv::X86_FastCall: Out << "x86_fastcallcc"; break;

llvm/lib/Target/X86/X86CallingConv.td

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,6 +1030,40 @@ def CC_X86_32_HiPE : CallingConv<[
10301030
CCIfType<[i32, f32], CCAssignToStack<4, 4>>
10311031
]>;
10321032

1033+
// X86-64 ReverseC calling convention.
1034+
def CC_X86_64_ReverseC : CallingConv<[
1035+
// Promote i1/i8/i16 arguments to i32.
1036+
CCIfType<[i1, i8, i16], CCPromoteToType<i32>>,
1037+
1038+
// Use all registers, starting with callee-save, ending with ccc
1039+
// arguments in reverse order.
1040+
CCIfType<[i32], CCAssignToReg<[
1041+
// Normally callee-save registers.
1042+
EBX,
1043+
//EBP, // Conflicts with frame pointer when -fno-omit-frame-pointer
1044+
R12D, R13D, R14D, R15D,
1045+
// Normally scratch registers.
1046+
R10D, R11D,
1047+
// Normally argument registers, in reverse order.
1048+
R9D, R8D, ECX, EDX, ESI, EDI,
1049+
// Return register
1050+
EAX]>>,
1051+
CCIfType<[i64], CCAssignToReg<[
1052+
// Normally callee-save registers.
1053+
RBX,
1054+
//RBP, // Conflicts with frame pointer when -fno-omit-frame-pointer
1055+
R12, R13, R14, R15,
1056+
// Normally scratch registers.
1057+
R10, R11,
1058+
// Normally argument registers, in reverse order.
1059+
R9, R8, RCX, RDX, RSI, RDI,
1060+
// Return register
1061+
RAX]>>,
1062+
1063+
// Otherwise it's the same as the regular C calling convention.
1064+
CCDelegateTo<CC_X86_64_C>
1065+
]>;
1066+
10331067
// X86-64 Intel OpenCL built-ins calling convention.
10341068
def CC_Intel_OCL_BI : CallingConv<[
10351069

@@ -1099,8 +1133,10 @@ def CC_X86_64 : CallingConv<[
10991133
CCIfCC<"CallingConv::X86_RegCall",
11001134
CCIfSubtarget<"isTargetWin64()", CCDelegateTo<CC_X86_Win64_RegCall>>>,
11011135
CCIfCC<"CallingConv::X86_RegCall", CCDelegateTo<CC_X86_SysV64_RegCall>>,
1136+
CCIfCC<"CallingConv::ReverseC", CCDelegateTo<CC_X86_64_ReverseC>>,
11021137
CCIfCC<"CallingConv::X86_INTR", CCCustom<"CC_X86_Intr">>,
11031138

1139+
11041140
// Mingw64 and native Win64 use Win64 CC
11051141
CCIfSubtarget<"isTargetWin64()", CCDelegateTo<CC_X86_Win64_C>>,
11061142

llvm/lib/Target/X86/X86RegisterInfo.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ X86RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
298298
switch (CC) {
299299
case CallingConv::GHC:
300300
case CallingConv::HiPE:
301+
case CallingConv::ReverseC:
301302
return CSR_NoRegs_SaveList;
302303
case CallingConv::AnyReg:
303304
if (HasAVX)
@@ -421,6 +422,7 @@ X86RegisterInfo::getCallPreservedMask(const MachineFunction &MF,
421422
switch (CC) {
422423
case CallingConv::GHC:
423424
case CallingConv::HiPE:
425+
case CallingConv::ReverseC:
424426
return CSR_NoRegs_RegMask;
425427
case CallingConv::AnyReg:
426428
if (HasAVX)

0 commit comments

Comments
 (0)