LDC-specific language changes
LDC tries to conform to the D specification as closely as possible. There are a few deviations, and several small extensions, which are documented here.
Violations of the specification
Some parts of the D specification are hard or impossible to implement with LLVM, they should be listed here.
Inline assembler
Almost everything works, but there are a few open issues. For instance the D spec isn't clear about at all is how asm blocks mixed with normal D code (for example code between two asm blocks) interacts.
Specific issues are:
ret
In short, LLVM inline assembler is not allowed to control program flow outside of the asm blocks, see below for a bit more information.
Gotos into inline assembly
For labels inside inline asm blocks, the D spec says "They can be the target of goto statements.", this is not supported at the moment. Basically, LLVM does not allow jumping in to or out of an asm block. We work around this for jumping out of asm by converting these branches to assignments to a temporary that is then used in a switch statement right after the inline asm block to jump to the final destination. This same workaround could be applied for jumping into inline assembly.
Zero-length static arrays
The D spec says that T[0] val; does not require storage but has an address. LDC currently allocates one bit for such a construct.
Deviations from the D ABI
The D spec only specifies an ABI for x86 processors on Windows and Linux. On other architectures and platforms LDC is free to do as it pleases, and does. However, on x86 the only parts of the ABI currently implemented is:
- the callee clears any parameters from the stack
- floating point values are returned on the x87 FPU stack
- reversing order of parameters
- returning delegates and dynamic arrays in EAX/EDX.
- passing last argument in EAX
- returning small structs in EAX
This is still a work in progress and will most likely improve a lot during the coming months. The LLVM developers have so far been very helpful in explaining what is needed to implement this ABI properly.
Extended inline assembly
LDC supports an LLVM-specific variant of GCC's extended inline assembly expressions. See the inline assembly expressions page for more information.
Versions
Besides the predefined versions from the D spec (in particular, of course, LDC), LDC conditionally defines a few more version identifiers for backwards compatibility reasons: LLVM, LLVM64, Thumb, mingw32, darwin, solaris. Please migrate your code to the official identifiers; the old ones might go away soon.
Pragmas
LDC provides pragmas to access internal functions and can be used to tweak certain behavior.
LDC_intrinsic
The LDC_intrinsic pragma provides access to LLVM's built-in intrinsic functions. It requires a single string literal parameter with full name of the intrinsic. For example "llvm.sqrt.f32".
- It can only be used on function declarations or funtion template declarations.
- Any affected function declarations are not allowed to have bodies.
- The functions must translate to the same signature as the intrinsic.
- You may not take the address of intrinsics.
Example:
// provide square root intrinsics
pragma(intrinsic, "llvm.sqrt.f32")
float sqrt(float);
pragma(intrinsic, "llvm.sqrt.f64")
double sqrt(double);
pragma(intrinsic, "llvm.sqrt.f80")
real sqrt(real); // x86 only
Overloaded intrinsics can also be accessed more easily with a templated version instead, currently only one overloaded type is supported.
Example:
// templated atomic swap intrinsic
pragma(intrinsic, "llvm.atomic.swap.i#.p0i#")
T llvm_atomic_swap(T)(T* ptr, T val);
The # mark in the name is replaced with the size in bits of the type of the template parameter.
The LDC_intrinsic pragma should not be used in user-code directly, instead, please refer to the ldc.intrinsics module.
LDC_no_typeinfo
You can use this pragma to stop typeinfo from being implicitly generated for a declaration.
Example:
pragma(LDC_no_typeinfo) {
struct Opaque {}
}
LDC_no_moduleinfo
You can use this pragma to stop moduleinfo from being implicitly generated for a declaration.
LDC_alloca
This pragma allows you to access the alloca instruction of LLVM directly. It only applies to function declarations and the final LLVM type for that declaration must be: i8* (i32/i64). The size parameter will be truncated to i32 if necessary.
Example:
pragma(LDC_alloca) void* alloca(size_t);
Variadic argument handling intrinsics
Example:
alias void* va_list;
pragma(LDC_va_start) void va_start(T)(va_list ap, ref T);
pragma(LDC_va_arg) T va_arg(T)(va_list ap);
pragma(LDC_va_end) void va_end(va_list args);
pragma(LDC_va_copy) void va_copy(va_list dst, va_list src);
LDC_allow_inline
Use this pragma statement inside a non-naked function using inline asm. This will tell the optimizers that it is safe to inline this function.
Example:
int add(int a, int b) {
pragma(LDC_allow_inline);
asm { mov EAX, a; add EAX, b; }
}
LDC_inline_ir
This pragma makes it possible to use llvm assembly language from D. It was addedd mainly for the purpose of implementing some SIMD functions (those will be added to ldc.simd) which couldn't be implemented otherwise. THIS PRAGMA IS NOT WELL TESTED AND SHOULD ONLY BE USED WHEN ABSULUTELY NECESSARY.
It is used like this:
pragma(LDC_inline_ir)
R inlineIR(string s, R, P...)(P);
int add(int a, int b)
{
return inlineIR!(`
%r = add i32 %0, %1
ret i32 %r`, int)(a, b);
}
The symbol declared with pragma llvm_inline_ir must be a function template with three template parameters. The first template parameter must be string in LLVM assembly language. The second template parameter must be the return type, and the third template parmeter must be a tuple of function parameter types.
When the function template is instantiated, an LLVM function is created. The string passed as the first template parameter is used as the function's body. If the return type is void, "void" is appended to it. The function's return type is determined from the second template parameter and the parameter list is generated from the third template parameter. This function will be inlined if possible. If all the calls to the function are inlined, the function will not appear in the object file.
LDC_global_crt_ctor and LDC_global_crt_dtor
If you are doing very low-level stuff then there might be the need to execute code before the D runtime is initialized. With these 2 pragmas it possible to run code as part of the C runtime construction and destruction. A possible application is the initialization of a global mutex as it is done in monitor_.d. If the pragma is specified on a function or static method then an entry is made in the corresponding list. E.g. in monitor_.d:
extern (C) {
#pragma(LDC_global_crt_ctor, 1024)
void _STI_monitor_staticctor()
{
// ...
}
}
The optional priority is used to order the execution of the functions. (For more information see the LLVM documentation on global ctors and global dtors variables.)
This works on Linux without problems. On Windows with MS C Runtime ctors work always but dtors are invoked only if linked against the static C runtime. Dtors on Windows require at least LLVM 3.2.