Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c++ > #68442 > unrolled thread
| Started by | Bonita Montero <Bonita.Montero@gmail.com> |
|---|---|
| First post | 2019-09-16 14:53 +0200 |
| Last post | 2019-09-20 08:21 +0200 |
| Articles | 12 — 5 participants |
Back to article view | Back to comp.lang.c++
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
| From | Bonita Montero <Bonita.Montero@gmail.com> |
|---|---|
| Date | 2019-09-16 14:53 +0200 |
| Subject | Strange 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]
| From | Paavo Helde <myfirstname@osa.pri.ee> |
|---|---|
| Date | 2019-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]
| From | Bonita Montero <Bonita.Montero@gmail.com> |
|---|---|
| Date | 2019-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]
| From | Tim Rentsch <tr.17687@z991.linuxsc.com> |
|---|---|
| Date | 2019-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]
| From | Paavo Helde <myfirstname@osa.pri.ee> |
|---|---|
| Date | 2019-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]
| From | Tim Rentsch <tr.17687@z991.linuxsc.com> |
|---|---|
| Date | 2019-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]
| From | "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> |
|---|---|
| Date | 2019-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]
| From | Bonita Montero <Bonita.Montero@gmail.com> |
|---|---|
| Date | 2019-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2019-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]
| From | Bonita Montero <Bonita.Montero@gmail.com> |
|---|---|
| Date | 2019-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]
| From | Paavo Helde <myfirstname@osa.pri.ee> |
|---|---|
| Date | 2019-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2019-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