LDCはpragmaを利用してLLVMのコード生成時にvolatileLoad/volatileStoreのハンドリングをしてる。
ここではvolatileLoadの呼び出し/コード生成までみていく。
core.bitopモジュールは、LDCコンパイラを使うときは特に指定しなくても pragma(LDC_intrinsic, “ldc.bitop.vld”) を指定して volatileLoad の宣言をしている。
version (LDC)
{
pragma(LDC_intrinsic, "ldc.bitop.vld")
ubyte volatileLoad(ubyte* ptr);
pragma(LDC_intrinsic, "ldc.bitop.vld")
ushort volatileLoad(ushort* ptr);
pragma(LDC_intrinsic, "ldc.bitop.vld")
uint volatileLoad(uint* ptr);
pragma(LDC_intrinsic, "ldc.bitop.vld")
ulong volatileLoad(ulong* ptr);
pragma(LDC_intrinsic, "ldc.bitop.vst")
void volatileStore(ubyte* ptr, ubyte value);
pragma(LDC_intrinsic, "ldc.bitop.vst")
void volatileStore(ushort* ptr, ushort value);
pragma(LDC_intrinsic, "ldc.bitop.vst")
void volatileStore(uint* ptr, uint value);
pragma(LDC_intrinsic, "ldc.bitop.vst")
void volatileStore(ulong* ptr, ulong value);
}
...
宣言の一覧はこれ。
extern (C++) enum LDCPragma : int {
LLVMnone = 0,
LLVMignore,
LLVMintrinsic,
LLVMglobal_crt_ctor,
LLVMglobal_crt_dtor,
LLVMno_typeinfo,
LLVMalloca,
LLVMva_start,
LLVMva_copy,
LLVMva_end,
LLVMva_arg,
LLVMinline_asm,
LLVMinline_ir,
LLVMfence,
LLVMatomic_store,
LLVMatomic_load,
LLVMatomic_cmp_xchg,
LLVMatomic_rmw,
LLVMbitop_bt,
LLVMbitop_btc,
LLVMbitop_btr,
LLVMbitop_bts,
LLVMbitop_vld,
LLVMbitop_vst,
LLVMextern_weak
};
Dコードから命令に落とすところはこうなっている。素朴。
bool DtoLowerMagicIntrinsic(IRState *p, FuncDeclaration *fndecl, CallExp *e,
DValue *&result) {
...
if (fndecl->llvmInternal == LLVMbitop_vld) {
if (e->arguments->dim != 1) {
e->error("bitop.vld intrinsic expects 1 argument");
fatal();
}
TODO
Expression *exp1 = (*e->arguments)[0];
LLValue *ptr = DtoRVal(exp1);
result = new DImValue(e->type, DtoVolatileLoad(ptr));
return true;
}
...
ここでLLVMの中間コード生成するところに渡している。あとはごにょごにょやってLLVM IRを生成してるだけ。(だけ、とは)
LLValue *DtoVolatileLoad(LLValue *src, const char *name) {
llvm::LoadInst *ld = gIR->ir->CreateLoad(src, name);
ld->setVolatile(true);
return ld;
}
たとえば、ARM Cortex-Mとかでベアメタル環境で volatileLoad/volatileStore がほしければ core.bitop の定義をそのままもってくるとよい。