Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c++ > #68442 > unrolled thread

Strange code-generation

Started byBonita Montero <Bonita.Montero@gmail.com>
First post2019-09-16 14:53 +0200
Last post2019-09-20 08:21 +0200
Articles 12 — 5 participants

Back to article view | Back to comp.lang.c++


Contents

  Strange code-generation Bonita Montero <Bonita.Montero@gmail.com> - 2019-09-16 14:53 +0200
    Re: Strange code-generation Paavo Helde <myfirstname@osa.pri.ee> - 2019-09-16 16:59 +0300
      Re: Strange code-generation Bonita Montero <Bonita.Montero@gmail.com> - 2019-09-16 21:08 +0200
      Re: Strange code-generation Tim Rentsch <tr.17687@z991.linuxsc.com> - 2019-09-18 08:47 -0700
        Re: Strange code-generation Paavo Helde <myfirstname@osa.pri.ee> - 2019-09-18 21:06 +0300
          Re: Strange code-generation Tim Rentsch <tr.17687@z991.linuxsc.com> - 2019-11-09 05:52 -0800
          Re: Strange code-generation "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> - 2019-11-09 19:15 +0100
    Re: Strange code-generation Bonita Montero <Bonita.Montero@gmail.com> - 2019-09-19 19:05 +0200
      Re: Strange code-generation David Brown <david.brown@hesbynett.no> - 2019-09-19 19:34 +0200
        Re: Strange code-generation Bonita Montero <Bonita.Montero@gmail.com> - 2019-09-19 20:05 +0200
          Re: Strange code-generation Paavo Helde <myfirstname@osa.pri.ee> - 2019-09-19 22:00 +0300
          Re: Strange code-generation David Brown <david.brown@hesbynett.no> - 2019-09-20 08:21 +0200

#68442 — Strange code-generation

FromBonita Montero <Bonita.Montero@gmail.com>
Date2019-09-16 14:53 +0200
SubjectStrange code-generation
Message-ID<qlo0kb$uhe$1@news.albasani.net>
I just experimented a bit with downcasting to a derived class.
So here's the code:

struct A
{
     int a, b, c;
};

struct B
{
     int d, e, f;
};

struct D : public A, public B
{
     int i, j, k;
};

D *f( B &b )
{
     return &static_cast<D &>(b);
}

This is what you will expect ... with g++:

     leaq    -12(%rdi), %rax
     ret

This is what makes MSVC from the above code

     xor     edx, edx
     lea     rax, QWORD PTR [rcx-12]
     test    rcx, rcx
     cmove   rax, rdx
     ret     0

So MSVC keeps a nullpointer when the input was also a nullpointer.
Is there any requirement of the standard that mandates this behaviour?
I think it would be even stupid to include that into the standard.
C++ isn't a language with child-proof locks.

[toc] | [next] | [standalone]


#68443

FromPaavo Helde <myfirstname@osa.pri.ee>
Date2019-09-16 16:59 +0300
Message-ID<qlo4fl$hv4$1@dont-email.me>
In reply to#68442
On 16.09.2019 15:53, Bonita Montero wrote:
> I just experimented a bit with downcasting to a derived class.
> So here's the code:
>
> struct A
> {
>      int a, b, c;
> };
>
> struct B
> {
>      int d, e, f;
> };
>
> struct D : public A, public B
> {
>      int i, j, k;
> };
>
> D *f( B &b )
> {
>      return &static_cast<D &>(b);
> }
>
> This is what you will expect ... with g++:
>
>      leaq    -12(%rdi), %rax
>      ret
>
> This is what makes MSVC from the above code
>
>      xor     edx, edx
>      lea     rax, QWORD PTR [rcx-12]
>      test    rcx, rcx
>      cmove   rax, rdx
>      ret     0
>
> So MSVC keeps a nullpointer when the input was also a nullpointer.
> Is there any requirement of the standard that mandates this behaviour?
> I think it would be even stupid to include that into the standard.

In standard C++ one cannot legally construct a "null reference", so the 
standard cannot mandate any behavior regarding them. The special 
handling of null only applies to the pointer form of static_cast<>.

However, an implementation may define behavior for things which are UB 
by the standard. MSVC supports things like this==nullptr (see e.g. 
CWnd::GetSafeHwnd() function), so maybe it supports "null references" as 
well, possibly for backward compatibility with their own old code.

[toc] | [prev] | [next] | [standalone]


#68457

FromBonita Montero <Bonita.Montero@gmail.com>
Date2019-09-16 21:08 +0200
Message-ID<qlomim$iv8$1@news.albasani.net>
In reply to#68443
I think this is relevant:
"A prvalue of type “pointer to cv1 B,” where B is a class type,
can be converted to a prvalue of type “pointer to cv2 D,” where
D is a class derived (Clause 10) from B, if a valid standard
conversion from “pointer to D” to “pointer to B” exists (4.10),
cv2 is the same cv-qualification as, or greater cv-qualification
than, cv1, and B is neither a virtual base class of D nor a base
class of a virtual base class of D. The null pointer value (4.10)
                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
is converted to the null pointer value of the destination type
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  ..."
The compiler just handles it the same way for references because
the internal representation is the same as with pointers.

[toc] | [prev] | [next] | [standalone]


#68506

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2019-09-18 08:47 -0700
Message-ID<867e654ndj.fsf@linuxsc.com>
In reply to#68443
Paavo Helde <myfirstname@osa.pri.ee> writes:

> On 16.09.2019 15:53, Bonita Montero wrote:
>
>> I just experimented a bit with downcasting to a derived class.
>> So here's the code:
>>
>> struct A
>> {
>>      int a, b, c;
>> };
>>
>> struct B
>> {
>>      int d, e, f;
>> };
>>
>> struct D : public A, public B
>> {
>>      int i, j, k;
>> };
>>
>> D *f( B &b )
>> {
>>      return &static_cast<D &>(b);
>> }
>>
>> This is what you will expect ... with g++:
>>
>>      leaq    -12(%rdi), %rax
>>      ret
>>
>> This is what makes MSVC from the above code
>>
>>      xor     edx, edx
>>      lea     rax, QWORD PTR [rcx-12]
>>      test    rcx, rcx
>>      cmove   rax, rdx
>>      ret     0
>>
>> So MSVC keeps a nullpointer when the input was also a nullpointer.
>> Is there any requirement of the standard that mandates this behaviour?
>> I think it would be even stupid to include that into the standard.
>
> In standard C++ one cannot legally construct a "null reference", so
> the standard cannot mandate any behavior regarding them.  [...]

Sure it could.  The Standard doesn't say that they can't exist,
and even if they couldn't exist the Standard could still specify
a behavior for how they must be treated.  And it might even be
useful to give such a specification, for implementations that
choose to provide null references by means of an extension.

[toc] | [prev] | [next] | [standalone]


#68511

FromPaavo Helde <myfirstname@osa.pri.ee>
Date2019-09-18 21:06 +0300
Message-ID<qltrnv$t19$1@dont-email.me>
In reply to#68506
On 18.09.2019 18:47, Tim Rentsch wrote:
> Paavo Helde <myfirstname@osa.pri.ee> writes:
>>
>> In standard C++ one cannot legally construct a "null reference", so
>> the standard cannot mandate any behavior regarding them.  [...]
>
> Sure it could.  The Standard doesn't say that they can't exist,
> and even if they couldn't exist the Standard could still specify
> a behavior for how they must be treated.  And it might even be
> useful to give such a specification, for implementations that
> choose to provide null references by means of an extension.

By that logic the C++ standard could also prescribe how the SQL language 
or the invisible unicorn in my garage must behave, in the case someone 
happens to incorporate them into their C++ implementation.

More to the point, the standard contains the following verbiage:
"A reference shall be initialized to refer to a valid object or
function. [ Note: in particular, a null reference cannot exist in a 
well-defined program, because the only way to create such a reference 
would be to bind it to the “object” obtained by indirection through a 
null pointer, which causes undefined behavior. ]"

Without contradicting itself, the standard cannot say in one place "A 
reference shall be initialized to refer to a valid object" and "behavior 
is undefined" and then go on and discuss in another place what happens 
if a program violates this "shall", thus defining the behavior.

[toc] | [prev] | [next] | [standalone]


#69676

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2019-11-09 05:52 -0800
Message-ID<86y2wpcg25.fsf@linuxsc.com>
In reply to#68511
Paavo Helde <myfirstname@osa.pri.ee> writes:

> On 18.09.2019 18:47, Tim Rentsch wrote:
>
>> Paavo Helde <myfirstname@osa.pri.ee> writes:
>>
>>> In standard C++ one cannot legally construct a "null reference", so
>>> the standard cannot mandate any behavior regarding them.  [...]
>>
>> Sure it could.  The Standard doesn't say that they can't exist,
>> and even if they couldn't exist the Standard could still specify
>> a behavior for how they must be treated.  And it might even be
>> useful to give such a specification, for implementations that
>> choose to provide null references by means of an extension.
>
> By that logic the C++ standard could also prescribe how the SQL
> language or the invisible unicorn in my garage must behave, in the
> case someone happens to incorporate them into their C++
> implementation.

These analogies are pretty much meaningless, because the terms
are not in the universe of discourse for the C++ language.  The
term "reference" is in the C++ universe of discourse, and every
C++ programmer understands what "null reference" means, in that
context, by analogy with "null pointer".  In fact the term "null
reference" appears in the C++ standard, as you point out.

> More to the point, the standard contains the following verbiage:
> "A reference shall be initialized to refer to a valid object or
> function.  [ Note: in particular, a null reference cannot exist in a
> well-defined program, because the only way to create such a reference
> would be to bind it to the ?object?  obtained by indirection through a
> null pointer, which causes undefined behavior. ]"

Saying "a null reference cannot exist in a well-defined program"
is a circular statement;  it has no information content, because
of what is meant by well-defined.  Also, just because a construct
has undefined behavior doesn't mean the result can't exist.
Obviously it can exist, because the implementation is free to
define the meaning.

> Without contradicting itself, the standard cannot say in one place "A
> reference shall be initialized to refer to a valid object" and
> "behavior is undefined" and then go on and discuss in another place
> what happens if a program violates this "shall", thus defining the
> behavior.

There is no contradiction.  What would (hypothetically) be being
defined is not declaring/initializing a null reference, but using
a null reference.  Do you not understand the distinction?


(Sorry to be so long in responding, life has been unusually
chaotic of late.)

[toc] | [prev] | [next] | [standalone]


#69689

From"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>
Date2019-11-09 19:15 +0100
Message-ID<qq6vnq$7sn$1@dont-email.me>
In reply to#68511
On 18.09.2019 20:06, Paavo Helde wrote:
> On 18.09.2019 18:47, Tim Rentsch wrote:
>> Paavo Helde <myfirstname@osa.pri.ee> writes:
>>>
>>> In standard C++ one cannot legally construct a "null reference", so
>>> the standard cannot mandate any behavior regarding them.  [...]
>>
>> Sure it could.  The Standard doesn't say that they can't exist,
>> and even if they couldn't exist the Standard could still specify
>> a behavior for how they must be treated.  And it might even be
>> useful to give such a specification, for implementations that
>> choose to provide null references by means of an extension.
> 
> By that logic the C++ standard could also prescribe how the SQL language 
> or the invisible unicorn in my garage must behave, in the case someone 
> happens to incorporate them into their C++ implementation.
> 
> More to the point, the standard contains the following verbiage:
> "A reference shall be initialized to refer to a valid object or
> function. [ Note: in particular, a null reference cannot exist in a 
> well-defined program, because the only way to create such a reference 
> would be to bind it to the “object” obtained by indirection through a 
> null pointer, which causes undefined behavior. ]"


Well, dereferencing a nullpointer is explicitly supported in a 
typeid-expression.

So for a short time there is a nullreference in a valid program.

Happily notes in ISO standards are non-normative, as I recall, but the 
wording in the sentence before that note is arguably defective. ;-)


> Without contradicting itself, the standard cannot say in one place "A 
> reference shall be initialized to refer to a valid object" and "behavior 
> is undefined" and then go on and discuss in another place what happens 
> if a program violates this "shall", thus defining the behavior.

I think that holds even taking into account the typeid special case.

- Alf

[toc] | [prev] | [next] | [standalone]


#68524

FromBonita Montero <Bonita.Montero@gmail.com>
Date2019-09-19 19:05 +0200
Message-ID<qm0cgp$qrg$1@news.albasani.net>
In reply to#68442
> D *f( B &b )
> {
>      return &static_cast<D &>(b);
> }

Look wat happnens if I do this instead with MSVC:

D *f( B &b )
{
     __assume(&b != nullptr);
     return &static_cast<D &>(b);
}

The resulting code isn't this:

>      xor     edx, edx
>      lea     rax, QWORD PTR [rcx-12]
>      test    rcx, rcx
>      cmove   rax, rdx
>      ret     0

But this:

     lea	    rax, QWORD PTR [rcx-12]
     ret	    0

MSVC is such a cool compiler.

[toc] | [prev] | [next] | [standalone]


#68525

FromDavid Brown <david.brown@hesbynett.no>
Date2019-09-19 19:34 +0200
Message-ID<qm0e7p$g9k$1@dont-email.me>
In reply to#68524
On 19/09/2019 19:05, Bonita Montero wrote:
>> D *f( B &b )
>> {
>>      return &static_cast<D &>(b);
>> }
> 
> Look wat happnens if I do this instead with MSVC:
> 
> D *f( B &b )
> {
>      __assume(&b != nullptr);
>      return &static_cast<D &>(b);
> }
> 
> The resulting code isn't this:
> 
>>      xor     edx, edx
>>      lea     rax, QWORD PTR [rcx-12]
>>      test    rcx, rcx
>>      cmove   rax, rdx
>>      ret     0
> 
> But this:
> 
>      lea        rax, QWORD PTR [rcx-12]
>      ret        0
> 
> MSVC is such a cool compiler.

As you said in your first post, gcc generates that anyway - without an 
"__assume".  That makes gcc a cooler compiler :-)

[toc] | [prev] | [next] | [standalone]


#68527

FromBonita Montero <Bonita.Montero@gmail.com>
Date2019-09-19 20:05 +0200
Message-ID<qm0g0l$jcq$1@news.albasani.net>
In reply to#68525
>>> D *f( B &b )
>>> {
>>>      return &static_cast<D &>(b);
>>> }
>>
>> Look wat happnens if I do this instead with MSVC:
>>
>> D *f( B &b )
>> {
>>      __assume(&b != nullptr);
>>      return &static_cast<D &>(b);
>> }
>>
>> The resulting code isn't this:
>>
>>>      xor     edx, edx
>>>      lea     rax, QWORD PTR [rcx-12]
>>>      test    rcx, rcx
>>>      cmove   rax, rdx
>>>      ret     0
>>
>> But this:
>>
>>      lea        rax, QWORD PTR [rcx-12]
>>      ret        0
>>
>> MSVC is such a cool compiler.

> As you said in your first post, gcc generates that anyway
> - without an "__assume".  That makes gcc a cooler compiler :-)

I just found the following:

gcc handles null-Pointers according to the standard:

D *f( B *b )
{
     return static_cast<D *>(b);
}

This results in this:

     testq    %rdi, %rdi
     je       .L3
     leaq    -12(%rdi), %rax
     ret
.L3:
     xorl    %eax, %eax
     ret

So on one side gcc is so clever as to assume that references
are never null, but on the other side you have no way to say
gcc that a pointer to be downcastet won't be null.

[toc] | [prev] | [next] | [standalone]


#68529

FromPaavo Helde <myfirstname@osa.pri.ee>
Date2019-09-19 22:00 +0300
Message-ID<qm0j97$go6$1@dont-email.me>
In reply to#68527
On 19.09.2019 21:05, Bonita Montero wrote:
>
> gcc handles null-Pointers according to the standard:
>
> D *f( B *b )
> {
>      return static_cast<D *>(b);
> }
>
> This results in this:
>
>      testq    %rdi, %rdi
>      je       .L3
>      leaq    -12(%rdi), %rax
>      ret
> .L3:
>      xorl    %eax, %eax
>      ret
>
> So on one side gcc is so clever as to assume that references
> are never null, but on the other side you have no way to say
> gcc that a pointer to be downcastet won't be null.

Except that you have:

D *f( B *b ) {
   if (b) {
     return static_cast<D *>(b);
   } else {
     __builtin_unreachable();
   }
}

 > g++ -O2 -c test1.cpp -S

_Z1fP1B:
.LFB0:
         .cfi_startproc
         leaq    -12(%rdi), %rax
         ret
         .cfi_endproc

[toc] | [prev] | [next] | [standalone]


#68550

FromDavid Brown <david.brown@hesbynett.no>
Date2019-09-20 08:21 +0200
Message-ID<qm1r5n$hk6$1@dont-email.me>
In reply to#68527
On 19/09/2019 20:05, Bonita Montero wrote:
>>>> D *f( B &b )
>>>> {
>>>>      return &static_cast<D &>(b);
>>>> }
>>>
>>> Look wat happnens if I do this instead with MSVC:
>>>
>>> D *f( B &b )
>>> {
>>>      __assume(&b != nullptr);
>>>      return &static_cast<D &>(b);
>>> }
>>>
>>> The resulting code isn't this:
>>>
>>>>      xor     edx, edx
>>>>      lea     rax, QWORD PTR [rcx-12]
>>>>      test    rcx, rcx
>>>>      cmove   rax, rdx
>>>>      ret     0
>>>
>>> But this:
>>>
>>>      lea        rax, QWORD PTR [rcx-12]
>>>      ret        0
>>>
>>> MSVC is such a cool compiler.
> 
>> As you said in your first post, gcc generates that anyway
>> - without an "__assume".  That makes gcc a cooler compiler :-)
> 
> I just found the following:
> 
> gcc handles null-Pointers according to the standard:

Good.

> 
> D *f( B *b )
> {
>      return static_cast<D *>(b);
> }
> 
> This results in this:
> 
>      testq    %rdi, %rdi
>      je       .L3
>      leaq    -12(%rdi), %rax
>      ret
> .L3:
>      xorl    %eax, %eax
>      ret
> 
> So on one side gcc is so clever as to assume that references
> are never null, but on the other side you have no way to say
> gcc that a pointer to be downcastet won't be null.

Didn't you read the answer I gave you to your question "is there an 
equivalent to MSVC's __assume in gcc" ?  Use that macro:

D *f( B *b )
{
     assume(b);
     return static_cast<D *>(b);
}

f(B*):
         lea     rax, [rdi-12]
         ret

(Or write the __builtin_unreachable() directly, as Paavo did, if you 
prefer.)

Thus (in this example) gcc gives you as good or better code for the 
plain C++, and gives you as good tools for giving the compiler extra 
information to generate more efficient results.

I'm happy to see MSVC generating good code and providing useful extras - 
but it is nothing special here.

(clang handles this like gcc.)

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.c++


csiph-web