View mode: basic / threaded / horizontal-split · Log in · Help
February 23, 2005
Re: Complex (constant) C macros -> D
<brad@domain.invalid> wrote in message 
news:cvghv0$1u9s$1@digitaldaemon.com...
> Ben Hinkle wrote:
>> <brad@domain.invalid> wrote in message 
>> news:cvg7sd$1d4k$1@digitaldaemon.com...
>>
>>>I'm just looking at a couple of projects, and wondering how I can convert 
>>>them to D.  The point of these macros to to translate a human readable 
>>>value to something that is hardware specific.
>>
>> [snip]
>>
>>>const uint SOME_COMMAND = flip (0xE0);
>>>
>>>Where SOME_COMMAND is evalutated once at runtime, before the main 
>>>function gets called.
>>
>> [snip]
>>
>> Does SOME_COMMAND really have to be const? Can it be an inlined function 
>> call instead? I'm thinking something like
>>
>> uint flip(uint x){ compute and return flipped x }
>> uint SOME_COMMAND(){ return flip(0xE0); }
>>
>> Then if you compile with -inline -O the call to SOME_COMMAND() and flip() 
>> should be inlined and the constant folding logic in the optimizer should 
>> evaluate the resulting expression at compile time. The down-sides of this 
>> approach are
>> 1) assumes inlining and constant folding are enabled
>> 2) you can't take the address of SOME_COMMAND and get a uint*
>> 3) you have to write SOME_COMMAND() in code instead of SOME_COMMAND
>>
>> I think it's worth a shot, though.
>> -Ben
> That is probably a good choice.  It doesn't need to be a constant (in my 
> case), though it is a little more code than in the C case.  It does also 
> make assumptions about the inliner/optimiser - but that is easily verified 
> with an assembler check.  I guess I was hoping that I would be able to 
> control constant munging in D just as well as I can in C with the 
> preprocessor.  I mean, with the C code I know that it will fold down to 
> just another constant - I'll need to check the assembler output with the D 
> method :)
>
> Brad

Well, technically all that the C version does is the inlining. The constant 
folding is still the same as D.
February 23, 2005
Re: Complex (constant) C macros -> D
> 
> 
> Well, technically all that the C version does is the inlining. The constant 
> folding is still the same as D. 
> 
> 

True, but would it actually work like that with plain functions?  For 
example, the optimiser would need to be clever enough to realise that 
each call to the function that has a constant input should be evaluated 
at compile time, and then just that constant injected into the code. 
And for non-const inputs the actual function code gets called.  What I 
think would happen is this

int invert (int i) { return ~i; };
int myConst () { return invert(0xF0); }

void code ()
{
 ...
 someFunc(myConst)
...
}

I think the optimiser would turn this into (with inlining, and constant 
folding)

void code ()
{
 ...
 someFunc ( ~0xF0 );
..
}
ie, the call will be inlined, but does the optimiser then further reduce 
that inlined call to the final optimisation of
 someFunc (0x0F) ?

Doing it with templates (as discussed in this thread) would do it 
properly.  I don't know enough about how the optimiser works to see if 
both the call will be inlined, and the constant folded.  And if I don't 
know the answer well enough, my first instinct (when time/space is 
critical) is NOT to trust the compiler to do it right :)

Brad
February 23, 2005
Re: Complex (constant) C macros -> D
> Is there a reason why D the following couldn't be valid D?
> 
> static int foo(int i) { return i/2; };
> const int myVar = foo(10);

I think the issue is that foo(10) may not always return the same thing 
in the general case, so you'd need to do code analysis to see that it 
does. Requiring code analysis in the spec (meaning every compiler has to 
do it) is not a good thing.

The meta-programming technique (those recursive templates) is somewhat 
different and doesn't require code analysis (except evaluating constant 
expressions, but that is easy). Basically, by instantiating (sp?) those 
templates, a function call is simulated and its "return value" can be used..


xs0
February 23, 2005
Re: Complex (constant) C macros -> D
On Wed, 23 Feb 2005 13:20:51 +1300, brad@domain.invalid wrote:

>> It might "feel a little ugly" to you, but that is exactly what module
>> constructors are for ... "SOME_COMMAND is evaluated once at runtime, before
>> the main function gets called".
>> 
>> In fact, I'd even make the flip() function a nested function within the
>> module constructor if it's not needed elsewhere in the program.
>> 
> 
> The only remaining difference is that I can't assign a value to a const 
> variable with the static module constructor method.
> I'm not particularly concerned, just found a little corner that I think 
> C does better than D, and I'd like D to be better than C everywhere :)
> 
> Is there a reason why D the following couldn't be valid D?
> 
> static int foo(int i) { return i/2; };
> const int myVar = foo(10);
> 

Oops! I forgot about the need for a 'const'. 

I've occasionally thought that a write-once type of variable would be a
useful addition to a language. I other words, once an assignment to such a
variable has been done, all other attempts at assigning something to it
would be a run-time error. It could be used for things whose value was not
knowable at compile time, but during run time, it should behave as if it
was a const.


-- 
Derek
Melbourne, Australia
23/02/2005 12:34:28 PM
February 23, 2005
Re: Complex (constant) C macros -> D
<brad@domain.invalid> wrote in message 
news:cvgm9s$25ab$1@digitaldaemon.com...
>
>>
>>
>> Well, technically all that the C version does is the inlining. The 
>> constant folding is still the same as D.
>
> True, but would it actually work like that with plain functions?  For 
> example, the optimiser would need to be clever enough to realise that each 
> call to the function that has a constant input should be evaluated at 
> compile time, and then just that constant injected into the code. And for 
> non-const inputs the actual function code gets called.  What I think would 
> happen is this
>
> int invert (int i) { return ~i; };
> int myConst () { return invert(0xF0); }
>
> void code ()
> {
>  ...
>  someFunc(myConst)
> ...
> }
>
> I think the optimiser would turn this into (with inlining, and constant 
> folding)
>
> void code ()
> {
>  ...
>  someFunc ( ~0xF0 );
> ..
> }
> ie, the call will be inlined, but does the optimiser then further reduce 
> that inlined call to the final optimisation of
>  someFunc (0x0F) ?

It's the same as with C's macros. Let me illustrate. The C code

 #define invert(x) (~x)
 #define myConst (invert(0xF0))
 ...
 someFunc( myConst )

gets expanded by the preprocessor to
 someFunc( (~(0xF0)) )
and then the compiler's constant folding takes it from there. In D the only 
difference is that there is no preprocessor to inline the "macros" - the 
compiler does it instead. After inlining the D compiler ends up with the 
exact same expression tree as the C version and from then on it is the same 
as C.

> Doing it with templates (as discussed in this thread) would do it 
> properly.  I don't know enough about how the optimiser works to see if 
> both the call will be inlined, and the constant folded.  And if I don't 
> know the answer well enough, my first instinct (when time/space is 
> critical) is NOT to trust the compiler to do it right :)
>
> Brad

That's ok - trusting the compiler can be risky. Though if dmd's inlining and 
constant folding can't produce the same result in your case as a C compiler 
would then Walter needs to spend some time and fix that.
See also http://www.digitalmars.com/d/htomodule.html the section about 
macros and http://www.digitalmars.com/d/pretod.html the section about 
macros.
Inlining is your friend!
February 23, 2005
Re: Complex (constant) C macros -> D
> 
> It's the same as with C's macros. Let me illustrate. The C code
> 
>   #define invert(x) (~x)
>   #define myConst (invert(0xF0))
>   ...
>   someFunc( myConst )
> 
> gets expanded by the preprocessor to
>   someFunc( (~(0xF0)) )
> and then the compiler's constant folding takes it from there. In D the only 
> difference is that there is no preprocessor to inline the "macros" - the 
> compiler does it instead. After inlining the D compiler ends up with the 
> exact same expression tree as the C version and from then on it is the same 
> as C.
<snip>
> That's ok - trusting the compiler can be risky. Though if dmd's inlining and 
> constant folding can't produce the same result in your case as a C compiler 
> would then Walter needs to spend some time and fix that.
> See also http://www.digitalmars.com/d/htomodule.html the section about 
> macros and http://www.digitalmars.com/d/pretod.html the section about 
> macros.
> Inlining is your friend! 
> 

That's good to hear, and it is what I expected.  I had thought that what 
might happen is
1) Constant folding occurs (ie, 0xF0 - this is as reduced as it can get)
2) Inlining occurs, the ~ operation still has to happen on 0xF0.
But if constant folding occurs after inlining, then everything is OK. 
If I am feeling particularly bored I might need to look at the assembler 
output.

I think the template method is closer to what I am wanting.

Brad
February 23, 2005
Re: Complex (constant) C macros -> D
brad@domain.invalid wrote:
> I'm just looking at a couple of projects, and wondering how I can 
> convert them to D.  The point of these macros to to translate a human 
> readable value to something that is hardware specific.  The details 
> aren't too specific, but the macros convert from one constant form to 
> another, here is the C code.

That's a bit like the following C++, from src/dmd/parse.c


         switch (token.value)
         {
#define X(tok,ector) \
             case tok:  nextToken(); e2 = parseAssignExp(); \
                        e = new ector(loc,e,e2); continue;

             X(TOKassign,    AssignExp);
             X(TOKaddass,    AddAssignExp);
             X(TOKminass,    MinAssignExp);
             X(TOKmulass,    MulAssignExp);
             X(TOKdivass,    DivAssignExp);
             X(TOKmodass,    ModAssignExp);
             X(TOKandass,    AndAssignExp);
             X(TOKorass,     OrAssignExp);
             X(TOKxorass,    XorAssignExp);
             X(TOKshlass,    ShlAssignExp);
             X(TOKshrass,    ShrAssignExp);
             X(TOKushrass,   UshrAssignExp);
             X(TOKcatass,    CatAssignExp);

#undef X
             default:
                 break;
         }

I've found some ways to rewrite this in D, but none of them "look nice".
February 23, 2005
Re: Complex (constant) C macros -> D
brad beveridge wrote:
> 
>> That's ok - trusting the compiler can be risky. Though if dmd's 
>> inlining and constant folding can't produce the same result in your 
>> case as a C compiler would then Walter needs to spend some time and 
>> fix that.
>> See also http://www.digitalmars.com/d/htomodule.html the section about 
>> macros and http://www.digitalmars.com/d/pretod.html the section about 
>> macros.
>> Inlining is your friend!
> 
> 
> That's good to hear, and it is what I expected.  I had thought that what 
> might happen is
> 1) Constant folding occurs (ie, 0xF0 - this is as reduced as it can get)
> 2) Inlining occurs, the ~ operation still has to happen on 0xF0.
> But if constant folding occurs after inlining, then everything is OK. If 
> I am feeling particularly bored I might need to look at the assembler 
> output.
> 
> I think the template method is closer to what I am wanting.
> 
> Brad
As much as replying to my own post is poor form, here I am doing it. 
The following code illustrates both methods
1) Inlining a function call of a constant
2) Generating a constant with a template
The bad news is that you were wrong Ben :)
(1) doesn't give optimal results
(2) does
D doesn't inline & then fold constants.  Which makes sense - I don't 
know much about compilers/optimisers, but I expect that an optimiser 
would need to be very smart to decide "hey, this function has constant 
inputs, maybe I should evaluate it at compile time and see if I can give 
it a constant output"

Anyhow, here is some sample code.  A little grovelling through the 
assembler output shows what is going on.

Brad

import std.stdio;

int flip(int x)
{
    return ( ((x & 0x80) >> 7) |
                          ((x & 0x40) >> 5) |
                          ((x & 0x20) >> 3) |
                          ((x & 0x10) >> 1) |
                          ((x & 0x08) << 1) |
                          ((x & 0x04) << 3) |
                          ((x & 0x02) << 5) |
                          ((x & 0x01) << 7) );
}

template tflip(int x)
{
    const int val = ( ((x & 0x80) >> 7) |
                          ((x & 0x40) >> 5) |
                          ((x & 0x20) >> 3) |
                          ((x & 0x10) >> 1) |
                          ((x & 0x08) << 1) |
                          ((x & 0x04) << 3) |
                          ((x & 0x02) << 5) |
                          ((x & 0x01) << 7) );

}

void main()
{
    // comment these lines in/out to show that constant folding happens
    // before inlining.

    // sub-optimal behaviour, the flip function is called and 0xFE
    // is bit flipped at runtime
    // note, you cannot have this int "const"
    //int i = flip(0xFE);

    // optimal method - the template is evaluated at compile time
    // and i evaluates to 0x7F, as is correct
    //const int i = tflip!(0xFE).val;
    writef("%d\n", i);
}
February 23, 2005
Re: Complex (constant) C macros -> D
"brad beveridge" <brad@nowhere.com> wrote in message 
news:cvhc9r$1vm$1@digitaldaemon.com...
> brad beveridge wrote:
>>
>>> That's ok - trusting the compiler can be risky. Though if dmd's inlining 
>>> and constant folding can't produce the same result in your case as a C 
>>> compiler would then Walter needs to spend some time and fix that.
>>> See also http://www.digitalmars.com/d/htomodule.html the section about 
>>> macros and http://www.digitalmars.com/d/pretod.html the section about 
>>> macros.
>>> Inlining is your friend!
>>
>>
>> That's good to hear, and it is what I expected.  I had thought that what 
>> might happen is
>> 1) Constant folding occurs (ie, 0xF0 - this is as reduced as it can get)
>> 2) Inlining occurs, the ~ operation still has to happen on 0xF0.
>> But if constant folding occurs after inlining, then everything is OK. If 
>> I am feeling particularly bored I might need to look at the assembler 
>> output.
>>
>> I think the template method is closer to what I am wanting.
>>
>> Brad
> As much as replying to my own post is poor form, here I am doing it. The 
> following code illustrates both methods
> 1) Inlining a function call of a constant
> 2) Generating a constant with a template
> The bad news is that you were wrong Ben :)
> (1) doesn't give optimal results
> (2) does
> D doesn't inline & then fold constants.  Which makes sense - I don't know 
> much about compilers/optimisers, but I expect that an optimiser would need 
> to be very smart to decide "hey, this function has constant inputs, maybe 
> I should evaluate it at compile time and see if I can give it a constant 
> output"
>
> Anyhow, here is some sample code.  A little grovelling through the 
> assembler output shows what is going on.
>
> Brad
>
> import std.stdio;
>
> int flip(int x)
> {
>     return ( ((x & 0x80) >> 7) |
>                           ((x & 0x40) >> 5) |
>                           ((x & 0x20) >> 3) |
>                           ((x & 0x10) >> 1) |
>                           ((x & 0x08) << 1) |
>                           ((x & 0x04) << 3) |
>                           ((x & 0x02) << 5) |
>                           ((x & 0x01) << 7) );
> }
>
> template tflip(int x)
> {
>     const int val = ( ((x & 0x80) >> 7) |
>                           ((x & 0x40) >> 5) |
>                           ((x & 0x20) >> 3) |
>                           ((x & 0x10) >> 1) |
>                           ((x & 0x08) << 1) |
>                           ((x & 0x04) << 3) |
>                           ((x & 0x02) << 5) |
>                           ((x & 0x01) << 7) );
>
> }
>
> void main()
> {
>     // comment these lines in/out to show that constant folding happens
>     // before inlining.
>
>     // sub-optimal behaviour, the flip function is called and 0xFE
>     // is bit flipped at runtime
>     // note, you cannot have this int "const"
>     //int i = flip(0xFE);
>
>     // optimal method - the template is evaluated at compile time
>     // and i evaluates to 0x7F, as is correct
>     //const int i = tflip!(0xFE).val;
>     writef("%d\n", i);
> }

I had forgotten about this bug where initializers aren't inlined. Everything 
works fine if you replace
    int i = flip(0xFE);
with
    int i;
    i = flip(0xFE);
or if you hadn't used a variable at all and instead just wrote
writef("%d\n", flip(0xFE));

Walter, is that bug fixable? It's very natural to want to put inlinable 
expressions in initializers.

-Ben
February 23, 2005
Re: Complex (constant) C macros -> D
> Though I think the syntax is on the clunky side :)

The better way:

template Foo(int n)
{
    const int Foo = n - 1;
}

const int test = Foo!(10);
1 2 3
Top | Discussion index | About this forum | D home