www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Zero-cost version-dependent function call at -O0.

reply Johan Engelen <j j.nl> writes:
How would you solve this problem: do an optional function call 
depending on some version(X). If version(X) is not defined, there 
should be no call and no extra code at -O0.

```
{
...
    foo();  // either compiles to a function call, or to _nothing_.
...
}
```

In C, you could do something like:
```
#if X
   void foo() {......}
#else
   #define foo()
#endif
```

How would you do this in D?

I can think of `mixin(foo())` but there is probably a nicer way 
that preserves normal function calling syntax.

Cheers,
   Johan
Jun 25
next sibling parent reply Anonymouse <asdf asdf.net> writes:
On Sunday, 25 June 2017 at 15:58:48 UTC, Johan Engelen wrote:
 How would you solve this problem: do an optional function call 
 depending on some version(X). If version(X) is not defined, 
 there should be no call and no extra code at -O0.

 ```
 {
 ...
    foo();  // either compiles to a function call, or to 
 _nothing_.
 ...
 }
 ```

 In C, you could do something like:
 ```
 #if X
   void foo() {......}
 #else
   #define foo()
 #endif
 ```

 How would you do this in D?

 I can think of `mixin(foo())` but there is probably a nicer way 
 that preserves normal function calling syntax.

 Cheers,
   Johan
Am I missing something, or can't you just version both the function and the function ćall? version(X) void foo() { /* ... */ } void main() { version(X) { foo(); } }
Jun 25
parent Johan Engelen <j j.nl> writes:
On Sunday, 25 June 2017 at 16:29:20 UTC, Anonymouse wrote:
 Am I missing something, or can't you just version both the 
 function and the function ćall?

 version(X)
 void foo() { /* ... */ }

 void main()
 {
     version(X)
     {
         foo();
     }
 }
I am hoping for something where "foo()" would just work. "version(X) foo();" isn't bad, but it adds a lot of noise. -Johan
Jun 25
prev sibling next sibling parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Sunday, 25 June 2017 at 15:58:48 UTC, Johan Engelen wrote:
 [...]
 If version(X) is not defined,  there should be no call and no 
 extra code at -O0.
 [...]

 In C, you could do something like:
 ```
 #if X
   void foo() {......}
 #else
   #define foo()
 #endif
 ```

 How would you do this in D?
By requiring the compiler to inline the empty foo: ``` version (Foo) { void foo() { import std.stdio; writeln("foo"); } } else { pragma(inline, true) void foo() {} } void main(string[] args) { foo(); } ``` See [1] for full assembly, shortened output as follows: ``` void example.foo(): ret _Dmain: xor eax, eax mov qword ptr [rsp - 16], rdi mov qword ptr [rsp - 8], rsi ret ``` As you can see, while the code for the function itself will still be emitted, since it's empty the inlining will result in no instructions as the result. [1] https://godbolt.org/g/RLt6vN
Jun 25
parent reply Johan Engelen <j j.nl> writes:
On Sunday, 25 June 2017 at 16:31:52 UTC, Moritz Maxeiner wrote:
 On Sunday, 25 June 2017 at 15:58:48 UTC, Johan Engelen wrote:
 [...]
 If version(X) is not defined,  there should be no call and no 
 extra code at -O0.
 [...]

 In C, you could do something like:
 ```
 #if X
   void foo() {......}
 #else
   #define foo()
 #endif
 ```

 How would you do this in D?
By requiring the compiler to inline the empty foo:
This won't work. Semantically, there is still a call and e.g. profiling will see it: https://godbolt.org/g/AUCeuu -Johan
Jun 25
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Sunday, 25 June 2017 at 21:55:22 UTC, Johan Engelen wrote:
 On Sunday, 25 June 2017 at 16:31:52 UTC, Moritz Maxeiner wrote:
 By requiring the compiler to inline the empty foo:
This won't work.
Yes, it does; comment out the call to `foo` and notice no change in the assembly in my link.
 Semantically, there is still a call
You asked for
 no call and no extra code at -O0
and that is exactly what this solution provides.
 and e.g. profiling will see it:
 https://godbolt.org/g/AUCeuu
The solution obviously does *not* work if you change the premise of your question after the fact by artificially injecting instructions into all function bodies (read what `-finstrument-functions` does).
Jun 25
parent reply Johan Engelen <j j.nl> writes:
On Sunday, 25 June 2017 at 22:23:44 UTC, Moritz Maxeiner wrote:
 The solution obviously does *not* work if you change the 
 premise of your question after the fact by artificially 
 injecting instructions into all function bodies
I meant semantically no call. I am asking for a little more imagination, such that I don't have to specify all obvious details. For example, the always inline solution also doesn't work well when `foo` takes parameters. Regardless, perhaps in the meanwhile you've come up with an other solution? I am now thinking about introducing a noop intrinsic...
 (read what `-finstrument-functions` does).
:-)
Jun 25
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Sunday, 25 June 2017 at 22:53:07 UTC, Johan Engelen wrote:
 I meant semantically no call.
In the existing language, I think version (or static if) at the usage and definition points both is as good as you're going to get. unless you use a preprocessor lol But the good news about version is you could use compiler errors about missing name to know where it needs to be (unless there's another matching overload in scope[!]). So it would be mildly verbose to look at in the code, but not really difficult to get right (and you could write it with autocomplete, like vim's `:abbr foo version(foo) foo`).
 I am now thinking about introducing a noop intrinsic...
That'd be kinda tricky because the arguments would still be liable to be evaluated... so the intrinsic would need to cover and disable that too and I think you'd be in for a fight to get that approved.
Jun 25
next sibling parent Johan Engelen <j j.nl> writes:
On Sunday, 25 June 2017 at 23:02:28 UTC, Adam D. Ruppe wrote:
 That'd be kinda tricky because the arguments would still be 
 liable to be evaluated...
Well.. I guess someone might argue that's a mis-feature of my preprocessor example: "foo(i++)" may not do what you want. (So the C code would have to do "(void)param" for all params, to ensure evaluation and avoid compiler warning? =)) So I think I got things to work with inline IR! ^_^ https://godbolt.org/g/HVGTbx ``` version(none) { void foo(int a, int b, int c) { /*...*/ }; } else { pragma(LDC_inline_ir) R __ir(string s, R, P...)(P); alias foo = __ir!(`ret i32 0`, int, int, int, int); } void bar() { int a; foo(a++,2,3); } ``` -Johan
Jun 26
prev sibling parent Johan Engelen <j j.nl> writes:
On Sunday, 25 June 2017 at 23:02:28 UTC, Adam D. Ruppe wrote:
 On Sunday, 25 June 2017 at 22:53:07 UTC, Johan Engelen wrote:
 I meant semantically no call.
In the existing language, I think version (or static if) at the usage and definition points both is as good as you're going to get.
At the usage _and_ at definition point, indeed, that's a very good idea. - Johan
Jun 26
prev sibling parent Guillaume Piolat <first.last gmail.com> writes:
On Sunday, 25 June 2017 at 15:58:48 UTC, Johan Engelen wrote:
 In C, you could do something like:
 ```
 #if X
   void foo() {......}
 #else
   #define foo()
 #endif
 ```
Curious no one has mentionned it. Just use alias. version(X) alias myFunc = impl1; else alias myFunc = impl2; I do it a lot for ldc.intrinsics else they don't provide speed benefits, often not being inlined.
Jun 25