Skip to content

Commit 338f352

Browse files
authored
Add ldc.attributes.callingConvention("...") (#4299)
Add ldc.attributes.callingConvention("...")
1 parent a912d59 commit 338f352

File tree

11 files changed

+358
-4
lines changed

11 files changed

+358
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
- Bit fields support. (#4015)
55
- macOS on Apple M1: linking with `-g` is working again without unaligned pointer warnings/errors. This fixes file:line debug information in exception backtraces (requiring `atos`, a macOS development tool installed with Xcode), without the need to set MACOSX_DEPLOYMENT_TARGET=11 and using a modified LLVM. (#4291)
66
- New commandline option `-fno-delete-null-pointer-checks`, mimicking the same commandline option of GCC and Clang. (#4297)
7+
- New UDA `ldc.attributes.callingConvention("...")`, which overrides the default calling convention. For expert use only! (#4299)
78

89
#### Platform support
910
- Initial ABI support for 64-bit RISC-V. (#4007)

dmd/id.d

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,7 @@ immutable Msgtable[] msgtable =
566566
{ "udaSection", "section" },
567567
{ "udaTarget", "target" },
568568
{ "udaAssumeUsed", "_assumeUsed" },
569+
{ "udaCallingConvention", "callingConvention" },
569570
{ "udaWeak", "_weak" },
570571
{ "udaCompute", "compute" },
571572
{ "udaKernel", "_kernel" },

dmd/id.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ struct Id
8585
static Identifier *udaOptStrategy;
8686
static Identifier *udaTarget;
8787
static Identifier *udaAssumeUsed;
88+
static Identifier *udaCallingConvention;
8889
static Identifier *udaWeak;
8990
static Identifier *udaAllocSize;
9091
static Identifier *udaLLVMAttr;

gen/functions.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -684,7 +684,7 @@ void DtoDeclareFunction(FuncDeclaration *fdecl, const bool willDefine) {
684684
}
685685

686686
func->setCallingConv(forceC ? gABI->callingConv(LINK::c)
687-
: gABI->callingConv(fdecl));
687+
: getCallingConvention(fdecl));
688688

689689
IF_LOG Logger::cout() << "func = " << *func << std::endl;
690690

@@ -1441,3 +1441,21 @@ DValue *DtoArgument(Parameter *fnarg, Expression *argexp) {
14411441

14421442
return arg;
14431443
}
1444+
1445+
////////////////////////////////////////////////////////////////////////////////
1446+
1447+
/* Gives precedence to user-specified calling convention using ldc.attributes.callingConvention,
1448+
* before querying the ABI.
1449+
*/
1450+
llvm::CallingConv::ID getCallingConvention(FuncDeclaration *fdecl)
1451+
{
1452+
llvm::CallingConv::ID retval;
1453+
1454+
// First check if there is an override by a UDA
1455+
// If callconv is MaxID-1, then the "default" calling convention is specified. Behave as if no UDA was specified at all.
1456+
bool userOverride = hasCallingConventionUDA(fdecl, &retval);
1457+
if (userOverride && (retval != llvm::CallingConv::MaxID - 1))
1458+
return retval;
1459+
1460+
return gABI->callingConv(fdecl);
1461+
}

gen/functions.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
#pragma once
1515

16+
#include "llvm/IR/CallingConv.h"
17+
1618
class DValue;
1719
class Expression;
1820
class FuncDeclaration;
@@ -43,3 +45,5 @@ void emitABIReturnAsmStmt(IRAsmBlock *asmblock, const Loc &loc,
4345
FuncDeclaration *fdecl);
4446

4547
DValue *DtoArgument(Parameter *fnarg, Expression *argexp);
48+
49+
llvm::CallingConv::ID getCallingConvention(FuncDeclaration *fdecl);

gen/tocall.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1050,7 +1050,7 @@ DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval,
10501050
llvm::Intrinsic::getAttributes(gIR->context(), cf->getIntrinsicID());
10511051
}
10521052
} else if (dfnval) {
1053-
call->setCallingConv(gABI->callingConv(dfnval->func));
1053+
call->setCallingConv(getCallingConvention(dfnval->func));
10541054
} else {
10551055
call->setCallingConv(gABI->callingConv(tf, iab.hasContext));
10561056
}

gen/uda.cpp

Lines changed: 142 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,27 @@ StructLiteralExp *getMagicAttribute(Dsymbol *sym, const Identifier *id,
110110
return nullptr;
111111
}
112112

113+
/// Returns the _last_ StructLiteralExp magic attribute with identifier `id`
114+
/// from the ldc magic module with identifier `from` (attributes or dcompute) if
115+
/// it is applied to `sym`, otherwise returns nullptr.
116+
StructLiteralExp *getLastMagicAttribute(Dsymbol *sym, const Identifier *id,
117+
const Identifier *from) {
118+
if (!sym->userAttribDecl)
119+
return nullptr;
120+
121+
// Loop over all UDAs and find the last match
122+
StructLiteralExp *lastMatch = nullptr;
123+
Expressions *attrs = sym->userAttribDecl->getAttributes();
124+
expandTuples(attrs);
125+
for (auto attr : *attrs) {
126+
if (auto sle = attr->isStructLiteralExp())
127+
if (isFromMagicModule(sle, from) && id == sle->sd->ident)
128+
lastMatch = sle;
129+
}
130+
131+
return lastMatch;
132+
}
133+
113134
/// Calls `action` for each magic attribute with identifier `id` from
114135
/// the ldc magic module with identifier `from` (attributes or dcompute)
115136
/// applied to `sym`.
@@ -366,6 +387,105 @@ void applyAttrAssumeUsed(IRState &irs, StructLiteralExp *sle,
366387
irs.usedArray.push_back(symbol);
367388
}
368389

390+
/// Tries to recognize the calling convention,
391+
/// TODO: Add support "cc <n>"
392+
/// Returns true if succesful, with the calling convention in 'callconv'.
393+
/// Returns false if unsuccesful.
394+
bool parseCallingConvention(llvm::StringRef name,
395+
llvm::CallingConv::ID *callconv) {
396+
397+
llvm::CallingConv::ID conv_id =
398+
llvm::StringSwitch<llvm::CallingConv::ID>(name)
399+
// Names recognized by Clang (see Clang's
400+
// CodeGenTypes::ClangCallConvToLLVMCallConv):
401+
.Case("stdcall", llvm::CallingConv::X86_StdCall)
402+
.Case("fastcall", llvm::CallingConv::X86_FastCall)
403+
.Case("regcall", llvm::CallingConv::X86_RegCall)
404+
.Case("thiscall", llvm::CallingConv::X86_ThisCall)
405+
.Case("ms_abi", llvm::CallingConv::Win64)
406+
.Case("sysv_abi", llvm::CallingConv::X86_64_SysV)
407+
.Case("pcs(\"aapcs\")", llvm::CallingConv::ARM_AAPCS)
408+
.Case("pcs(\"aapcs-vfp\")", llvm::CallingConv::ARM_AAPCS_VFP)
409+
.Case("intel_ocl_bicc", llvm::CallingConv::Intel_OCL_BI)
410+
.Case("pascal",
411+
llvm::CallingConv::C) // Check Clang, this may change in future
412+
.Case("vectorcall", llvm::CallingConv::X86_VectorCall)
413+
.Case("aarch64_vector_pcs", llvm::CallingConv::AArch64_VectorCall)
414+
.Case("preserve_most", llvm::CallingConv::PreserveMost)
415+
.Case("preserve_all", llvm::CallingConv::PreserveAll)
416+
.Case("swiftcall", llvm::CallingConv::Swift)
417+
#if LDC_LLVM_VER >= 1300
418+
.Case("swiftasynccall", llvm::CallingConv::SwiftTail)
419+
#endif
420+
421+
// Names recognized in LLVM IR (see LLVM's
422+
// LLParser::parseOptionalCallingConv):
423+
.Case("ccc", llvm::CallingConv::C)
424+
.Case("fastcc", llvm::CallingConv::Fast)
425+
.Case("coldcc", llvm::CallingConv::Cold)
426+
#if LDC_LLVM_VER >= 1000
427+
.Case("cfguard_checkcc", llvm::CallingConv::CFGuard_Check)
428+
#endif
429+
.Case("x86_stdcallcc", llvm::CallingConv::X86_StdCall)
430+
.Case("x86_fastcallcc", llvm::CallingConv::X86_FastCall)
431+
.Case("x86_regcallcc", llvm::CallingConv::X86_RegCall)
432+
.Case("x86_thiscallcc", llvm::CallingConv::X86_ThisCall)
433+
.Case("x86_vectorcallcc", llvm::CallingConv::X86_VectorCall)
434+
.Case("arm_apcscc", llvm::CallingConv::ARM_APCS)
435+
.Case("arm_aapcscc", llvm::CallingConv::ARM_AAPCS)
436+
.Case("arm_aapcs_vfpcc", llvm::CallingConv::ARM_AAPCS_VFP)
437+
.Case("aarch64_vector_pcs", llvm::CallingConv::AArch64_VectorCall)
438+
#if LDC_LLVM_VER >= 1000
439+
.Case("aarch64_sve_vector_pcs",
440+
llvm::CallingConv::AArch64_SVE_VectorCall)
441+
#endif
442+
.Case("msp430_intrcc", llvm::CallingConv::MSP430_INTR)
443+
.Case("avr_intrcc", llvm::CallingConv::AVR_INTR)
444+
.Case("avr_signalcc", llvm::CallingConv::AVR_SIGNAL)
445+
.Case("ptx_kernel", llvm::CallingConv::PTX_Kernel)
446+
.Case("ptx_device", llvm::CallingConv::PTX_Device)
447+
.Case("spir_kernel", llvm::CallingConv::SPIR_KERNEL)
448+
.Case("spir_func", llvm::CallingConv::SPIR_FUNC)
449+
.Case("intel_ocl_bicc", llvm::CallingConv::Intel_OCL_BI)
450+
.Case("x86_64_sysvcc", llvm::CallingConv::X86_64_SysV)
451+
.Case("win64cc", llvm::CallingConv::Win64)
452+
.Case("webkit_jscc", llvm::CallingConv::WebKit_JS)
453+
.Case("anyregcc", llvm::CallingConv::AnyReg)
454+
.Case("preserve_mostcc", llvm::CallingConv::PreserveMost)
455+
.Case("preserve_allcc", llvm::CallingConv::PreserveAll)
456+
.Case("ghccc", llvm::CallingConv::GHC)
457+
.Case("swiftcc", llvm::CallingConv::Swift)
458+
#if LDC_LLVM_VER >= 1300
459+
.Case("swifttailcc", llvm::CallingConv::SwiftTail)
460+
#endif
461+
.Case("x86_intrcc", llvm::CallingConv::X86_INTR)
462+
.Case("hhvmcc", llvm::CallingConv::HHVM)
463+
.Case("hhvm_ccc", llvm::CallingConv::HHVM_C)
464+
.Case("cxx_fast_tlscc", llvm::CallingConv::CXX_FAST_TLS)
465+
.Case("amdgpu_vs", llvm::CallingConv::AMDGPU_VS)
466+
#if LDC_LLVM_VER >= 1200
467+
.Case("amdgpu_gfx", llvm::CallingConv::AMDGPU_Gfx)
468+
#endif
469+
.Case("amdgpu_ls", llvm::CallingConv::AMDGPU_LS)
470+
.Case("amdgpu_hs", llvm::CallingConv::AMDGPU_HS)
471+
.Case("amdgpu_es", llvm::CallingConv::AMDGPU_ES)
472+
.Case("amdgpu_gs", llvm::CallingConv::AMDGPU_GS)
473+
.Case("amdgpu_ps", llvm::CallingConv::AMDGPU_PS)
474+
.Case("amdgpu_cs", llvm::CallingConv::AMDGPU_CS)
475+
.Case("amdgpu_kernel", llvm::CallingConv::AMDGPU_KERNEL)
476+
#if LDC_LLVM_VER >= 1000
477+
.Case("tailcc", llvm::CallingConv::Tail)
478+
#endif
479+
480+
.Case("default", llvm::CallingConv::MaxID - 1)
481+
.Default(llvm::CallingConv::MaxID);
482+
483+
bool success = (conv_id != llvm::CallingConv::MaxID);
484+
if (success && callconv)
485+
*callconv = conv_id;
486+
return success;
487+
}
488+
369489
} // anonymous namespace
370490

371491
void applyVarDeclUDAs(VarDeclaration *decl, llvm::GlobalVariable *gvar) {
@@ -394,7 +514,8 @@ void applyVarDeclUDAs(VarDeclaration *decl, llvm::GlobalVariable *gvar) {
394514
} else if (ident == Id::udaWeak) {
395515
// @weak is applied elsewhere
396516
} else if (ident == Id::udaDynamicCompile ||
397-
ident == Id::udaDynamicCompileEmit) {
517+
ident == Id::udaDynamicCompileEmit ||
518+
ident == Id::udaCallingConvention) {
398519
sle->error(
399520
"Special attribute `ldc.attributes.%s` is only valid for functions",
400521
ident->toChars());
@@ -450,7 +571,7 @@ void applyFuncDeclUDAs(FuncDeclaration *decl, IrFunction *irFunc) {
450571
} else if (ident == Id::udaAssumeUsed) {
451572
applyAttrAssumeUsed(*gIR, sle, func);
452573
} else if (ident == Id::udaWeak || ident == Id::udaKernel ||
453-
ident == Id::udaNoSanitize) {
574+
ident == Id::udaNoSanitize || ident==Id::udaCallingConvention) {
454575
// These UDAs are applied elsewhere, thus should silently be ignored here.
455576
} else if (ident == Id::udaDynamicCompile) {
456577
irFunc->dynamicCompile = true;
@@ -498,6 +619,25 @@ void applyFuncDeclUDAs(FuncDeclaration *decl, IrFunction *irFunc) {
498619
}
499620
}
500621

622+
/// Checks whether 'fd' has the @ldc.attributes.callingConvention("...") UDA applied.
623+
/// If so, it returns the calling convention in 'callconv'.
624+
bool hasCallingConventionUDA(FuncDeclaration *fd,
625+
llvm::CallingConv::ID *callconv) {
626+
auto sle =
627+
getLastMagicAttribute(fd, Id::udaCallingConvention, Id::attributes);
628+
if (!sle)
629+
return false;
630+
631+
checkStructElems(sle, {Type::tstring});
632+
auto name = getFirstElemString(sle);
633+
bool success = parseCallingConvention(name, callconv);
634+
if (!success)
635+
sle->warning("Ignoring unrecognized calling convention name '%s' for "
636+
"`@ldc.attributes.callingConvention`",
637+
name.str().c_str());
638+
return success;
639+
}
640+
501641
/// Checks whether 'sym' has the @ldc.attributes._weak() UDA applied.
502642
bool hasWeakUDA(Dsymbol *sym) {
503643
auto sle = getMagicAttribute(sym, Id::udaWeak, Id::attributes);

gen/uda.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
#pragma once
1616

17+
#include "llvm/IR/CallingConv.h"
18+
1719
class Dsymbol;
1820
class FuncDeclaration;
1921
class VarDeclaration;
@@ -25,6 +27,7 @@ class GlobalVariable;
2527
void applyFuncDeclUDAs(FuncDeclaration *decl, IrFunction *irFunc);
2628
void applyVarDeclUDAs(VarDeclaration *decl, llvm::GlobalVariable *gvar);
2729

30+
bool hasCallingConventionUDA(FuncDeclaration *fd, llvm::CallingConv::ID *callconv);
2831
bool hasWeakUDA(Dsymbol *sym);
2932
bool hasKernelAttr(Dsymbol *sym);
3033
/// Must match ldc.dcompute.Compilefor + 1 == DComputeCompileFor

runtime/druntime/src/ldc/attributes.d

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,35 @@ private struct _assumeUsed
7575
{
7676
}
7777

78+
/++
79+
+ Meant for expert use. Overrides the default calling convention. The supported
80+
+ names for calling conventions depends on the target.
81+
+ If specified multiple times, the last applied UDA is used.
82+
+ The calling convention is not part of the type of the function, which means that
83+
+ this attribute cannot be used in combination with function pointers (the function
84+
+ referenced by a function pointer will be called using the default calling convention).
85+
+ Semantic analysis does NOT (yet?) check for correctness. For example when
86+
+ this is applied to a class function, it is up to the user to ensure that the base
87+
+ function and all overrides (in child classes) have the same calling convention
88+
+ applied.
89+
+
90+
+ Example (for X86):
91+
+ ---
92+
+ import ldc.attributes;
93+
+
94+
+ @callingConvention("vectorcall"): // all functions in scope get this UDA
95+
+
96+
+ int vector_call_convention() { return 42; }
97+
+
98+
+ @callingConvention("default") // overrides the first UDA
99+
+ int func_with_default_calling_convention() { return 1; }
100+
+ ---
101+
+/
102+
struct callingConvention
103+
{
104+
string convention;
105+
}
106+
78107
/++
79108
+ When applied to a function, marks this function for dynamic compilation.
80109
+ Calls to the function will be to the dynamically compiled function,

0 commit comments

Comments
 (0)