So, after being asked to support dynamic loading in llvm-d[1] at DConf 2017 I essentially had the following two options: - have two separate declarations of the bindings, one with the function signatures (linking) and one with function pointers (loading)
- Have one declaration and derive the other at compile time

Since this is D and I was reminded of Andrei's keynote I thought to myself "use the Compiler, Dummy" and went about introspecting[2] the function signatures[3]. The relevant code looks like this:

---
import functions = llvm.functions.link;

template isCFunction(alias scope_, string member)
{
    static if (isFunction!(__traits(getMember, scope_, member)) &&
(functionLinkage!(__traits(getMember, scope_, member)) == "C" || functionLinkage!(__traits(getMember, scope_, member)) == "Windows")) {
        enum isCFunction = true;
    } else {
        enum isCFunction = false;
    }
}

template CFunctions(alias mod)
{
    alias isCFunction(string member) = .isCFunction!(mod, member);
alias CFunctions = Filter!(isCFunction, __traits(allMembers, mod));
}

string declareStubs()
{
    import std.array : appender;
    auto code = appender!string;
    foreach (fn; CFunctions!functions) {
        code.put("typeof(functions."); code.put(fn);
        code.put(")* "); code.put(fn); code.put(";\n");
    }
    return code.data;
}

mixin (declareStubs);
---

Now, the above code essentially searches through all symbols in the llvm.functions.link module, filters out anything that's not an extern(System) function and then generates code declaring a function pointer for each of them, and then finally mixes that code in.

This increases compilation time on my Broadwell i5-5200U from faster than my terminal emulator can print to 4 seconds. Yikes. From experience I knew that it wouldn't be cheap, but that's a hefty cost for a conceptually simple use case (from my PoV).

You might ask why I need to use CTFE here (as the interpreter is not cheap): It's because I want the mixed-in function pointers to be at module scope and I'm not aware of any other way to do that currently; if the new static foreach works the way I suspect, then that part could essentially be replaced (at module scope) with something like

---
static foreach (fn; CFunctions!functions) {
    typeof(functions.fn)* __traits(identifier, fn);
}
---

Looks much nicer, but will it be fast? I don't know, but I hope so, since it shouldn't need to spin up CTFE.

TLDR: Compile time introspection still increases compile time from below human response time (i.e. without encouraging context switching in your brain) to breaking your concentration even for simple use cases.

[1] https://github.com/Calrama/llvm-d
[2] https://github.com/Calrama/llvm-d/blob/master/source/llvm/functions/load.d [3] https://github.com/Calrama/llvm-d/blob/master/source/llvm/functions/link.d

Reply via email to