Another feature/optimization request (also: macro proposal)

downs default_357-line at yahoo.de
Wed Jan 2 20:39:52 PST 2008


Okay, this is more of a compiler than language feature request.

While writing the OpenGL API for dglut, I found myself repeatedly using the following idiom:

> 
> void glDoStuff(GLenum foo, int bar, void delegate()[] dgs...) {
>   auto oldState=glGetStateFooBar(foo);
>   scope(exit) glStateFooBar(foo, oldState);
>   glStateFooBar(foo, bar);
>   
>   foreach (dg; dgs) dg();
> }
> 

Seeing as this, worst-case, comes down to two function call layers, just to do some easier OpenGL stuff, I decided to test how this common situation gets inlined.

For this, I dumped EBP during several parts of the code.

> 
> import std.stdio;
> void test(void delegate()[] dgs...) {
>         writefln("test: ", getESP());
>         foreach (dg; dgs) dg();
> }
> 
> void *getESP() {
>         void *res;
>         asm { mov res, EBP; }
>         return res;
> }
> 
> void main() {
>         writefln("main: ", getESP());
>         test({ writefln("dg: ", getESP()); }); 
> }
> 

The result: DMD doesn't inline at all. GDC inlines test, but not the delegate.

Because this might cause significant slowdowns, I'd like to request that the language spec guarantee the following:

that in a case such as,

>
> auto foo={}; foo();
> // or
> auto bar=[{}];
> foreach (b; bar) b(); // foreach can get unrolled at compile-time because the array is static
>

foo (and b) always get inlined.

Note that I propose no kind of complex value tracking, just this one, simple case.

The effect of this will be to place user-defined control structures taking delegate parameters on the same level as built-in statements.

 * ALTERNATIVE PROPOSAL *

Another way to achieve this would be the introduction of a "block" type. This type would behave as follows:

 - When used in a parameter list, block behaves like typesafe variadic void delegate()[], with the following modifications:
 - All delegate and function *literals* that do not take parameters are implicitly converted to block. This conversion will make the following changes:
   - If the literal returns a value, this value is stored until the control flow re-enters the function in which they were declared.
     - At this point, the value is returned.
 - non-literal delegate() variables are also implicitly convertible to block.
   - Since the above modifications cannot be applied, the variables are treated as if they were wrapped in a literal.
 - Block variables can be called like void delegate() variables. This causes immediate inlining.
 - Lexically speaking, each block constitutes a scope.

The above example, rewritten using block, would look something like this:

>
> void glDoStuff(GLenum foo, int bar, block b) {
>   auto oldState=glGetStateFooBar(foo);
>   scope(exit) glStateFooBar(foo, oldState);
>   glStateFooBar(foo, bar);
>   b(); // gets inlined
> }
>

Since blocks are evaluated completely at compile-time, features like block AST introspection or modification might eventually be added,
thus tuning blocks into the foundation of macros.

The obligatory: what do you think? :)

 -- downs



More information about the Digitalmars-d mailing list