public inbox for gcc-prs@sourceware.org
help / color / mirror / Atom feed
* c++/8910: Incorrect code generation when returning class by value
@ 2002-12-11 19:16 lgandhi
  0 siblings, 0 replies; 2+ messages in thread
From: lgandhi @ 2002-12-11 19:16 UTC (permalink / raw)
  To: gcc-gnats


>Number:         8910
>Category:       c++
>Synopsis:       Incorrect code generation when returning class by value
>Confidential:   no
>Severity:       critical
>Priority:       medium
>Responsible:    unassigned
>State:          open
>Class:          wrong-code
>Submitter-Id:   net
>Arrival-Date:   Wed Dec 11 19:16:00 PST 2002
>Closed-Date:
>Last-Modified:
>Originator:     Lakhbir Gandhi
>Release:        gcc version 2.95.4 20010321 (Peerless-JBurk) derived from  gcc 2.95.3 with DaveK's modifications for VxWorksPPC.
>Organization:
>Environment:
Compiler#1: gcc 2.95.3 with DaveK's modifications for VxWorksPPC.
Compiler#2: cygwin 2.95.3-5
Host: PIII MS Windows 2000 666Mhz 768MB
Shell: Cmd
Compile command: c++<ppc> test.cpp -g -c -o test.o
Disassembly command: objdump<ppc> -d -r -S test.o 
>Description:
Hello,
 
I want to report a bug wherein if a method returns a class by value, incorrect code is generated.
It manifests itself when copy constructors are not available, in my case I had my copy constructor declared 'explicit' to prevent the compiler from generating expensive temporary objects.
 
 
Both the ppc & cygwin compilers produce the same result so I don't think DaveK's modifications have anything to do with it.
 
The test code is as follows: (copy-paste into test.cpp)
 
    class A  {
 

    };
 
class B  {
    public :
        
            B(int  init);
            B(A  init);
explicit  B(const B & init);
 
         operator A();
        
    };
 
    class C  {
    
        public :
         operator B();
        
    
    };
    
     C::operator B() {
        return B(12345);
    }
    
 
The class setup is described so:
 
class A is any class.
class B contains conversion constructor and operator for class A.
class B also has a conversion constructor from int.
(I have used int as an example, but all other datatypes intrisic and user-defined cause the same behaviour)
class C defines a conversion operator to class B.
The copy constructor for class B is declared explicit to prevent the compiler from generating expensive temporary B objects.
C's conversion operator returns a B object by value, not as a reference or pointer. 

The implementation of C::operator B() elects to return an object B which is initialized by the integer 12345.
Because class B has a conversion constructor from int, all we should see in the assembly code generated is a call to B::B(int).
 
However I get the following:
 

MiscTest.ppc.o:     file format elf32-powerpc
 
Disassembly of section .text:
 
00000000 <C::operator B(void)>:
        
    
    };
    
     C::operator B() {
   0: 94 21 ff c0  stwu r1,-64(r1)
   4: 7c 08 02 a6  mflr r0
   8: 93 61 00 2c  stw r27,44(r1)
   c: 93 81 00 30  stw r28,48(r1)
  10: 93 a1 00 34  stw r29,52(r1)
  14: 93 c1 00 38  stw r30,56(r1)
  18: 93 e1 00 3c  stw r31,60(r1)
  1c: 90 01 00 44  stw r0,68(r1)
  20: 7c 3f 0b 78  mr r31,r1
  24: 7c 7d 1b 78  mr r29,r3
  28: 7c 9c 23 78  mr r28,r4
        return B(12345);
  2c: 3b 7f 00 09  addi r27,r31,9
  30: 38 1f 00 18  addi r0,r31,24
  34: 7c 03 03 78  mr r3,r0
  38: 38 80 30 39  li r4,12345
  3c: 48 00 00 01  bl 3c <C::operator B(void)+0x3c>
   3c: R_PPC_REL24 B::B(int)
  40: 7c 60 1b 78  mr r0,r3
  44: 39 3f 00 18  addi r9,r31,24
  48: 7f 63 db 78  mr r3,r27
  4c: 7d 24 4b 78  mr r4,r9
  50: 4c c6 31 82  crclr 4*cr1+eq
  54: 48 00 00 01  bl 54 <C::operator B(void)+0x54>
   54: R_PPC_REL24 B::operator A(void)
  58: 88 1f 00 09  lbz r0,9(r31)
  5c: 39 20 00 00  li r9,0
  60: 99 3f 00 08  stb r9,8(r31)
  64: 7f a3 eb 78  mr r3,r29
  68: 38 9f 00 08  addi r4,r31,8
  6c: 48 00 00 01  bl 6c <C::operator B(void)+0x6c>
   6c: R_PPC_REL24 B::B(A)
  70: 7c 60 1b 78  mr r0,r3
  74: 48 00 00 0c  b 80 <C::operator B(void)+0x80>
  78: 48 00 00 08  b 80 <C::operator B(void)+0x80>
  7c: 48 00 00 04  b 80 <C::operator B(void)+0x80>
    }
  80: 7f a3 eb 78  mr r3,r29
  84: 81 61 00 00  lwz r11,0(r1)
  88: 80 0b 00 04  lwz r0,4(r11)
  8c: 7c 08 03 a6  mtlr r0
  90: 83 6b ff ec  lwz r27,-20(r11)
  94: 83 8b ff f0  lwz r28,-16(r11)
  98: 83 ab ff f4  lwz r29,-12(r11)
  9c: 83 cb ff f8  lwz r30,-8(r11)
  a0: 83 eb ff fc  lwz r31,-4(r11)
  a4: 7d 61 5b 78  mr r1,r11
  a8: 4e 80 00 20  blr

 
At offset 3c above, the correct call to B::B(int) is generated.
But at offset 54, there is a call to B::operator A !!!!! 
To add insult to injury, this returned A is used to call B::B(A) at offset 6c !
This causes C::operator B() to return the B object created above, which is initialized by an A instead of an int which was the intent.
 
This is incorrect behavior on two accounts:
1) ISO/IEC 14882:1998 in section 12.3.4 specifies that "At most one user-defined conversion (constructor or conversion function) is implictely applied to a single value", and here we see two such conversions, from B to A and then back to B.
2) The B object created by B::B(A) at offset 6c is returned instead of the the B object created by B::B(int) at offset 3c.
 
I have examined the rtl and it contains the incorrect return semantic described above, so rtl to asm conversion is not the issue.
Either the bug is in gcc/cp/parse.y or tree generation is incorrect or the resolution of candidate functions is incorrect or has the wrong data or the code in gcc/cp/typeck.c between line 7449 and line 7453 in function c_expand_return does not implement  the correct semantics. Please be aware this is just a conjecture.
 


Now if I do not declare B::B(B&) explicit, the correct code gets generated at offset 30 as shown below.
 
 
MiscTest.ppc.o:     file format elf32-powerpc
 
Disassembly of section .text:
 
00000000 <C::operator B(void)>:
        
    
    };
    
     C::operator B() {
   0: 94 21 ff e0  stwu r1,-32(r1)
   4: 7c 08 02 a6  mflr r0
   8: 93 81 00 10  stw r28,16(r1)
   c: 93 a1 00 14  stw r29,20(r1)
  10: 93 c1 00 18  stw r30,24(r1)
  14: 93 e1 00 1c  stw r31,28(r1)
  18: 90 01 00 24  stw r0,36(r1)
  1c: 7c 3f 0b 78  mr r31,r1
  20: 7c 7d 1b 78  mr r29,r3
  24: 7c 9c 23 78  mr r28,r4
        return B(12345);
  28: 7f a3 eb 78  mr r3,r29
  2c: 38 80 30 39  li r4,12345
  30: 48 00 00 01  bl 30 <C::operator B(void)+0x30>
   30: R_PPC_REL24 B::B(int)
  34: 7c 60 1b 78  mr r0,r3
  38: 48 00 00 0c  b 44 <C::operator B(void)+0x44>
  3c: 48 00 00 08  b 44 <C::operator B(void)+0x44>
  40: 48 00 00 04  b 44 <C::operator B(void)+0x44>
    }
  44: 7f a3 eb 78  mr r3,r29
  48: 81 61 00 00  lwz r11,0(r1)
  4c: 80 0b 00 04  lwz r0,4(r11)
  50: 7c 08 03 a6  mtlr r0
  54: 83 8b ff f0  lwz r28,-16(r11)
  58: 83 ab ff f4  lwz r29,-12(r11)
  5c: 83 cb ff f8  lwz r30,-8(r11)
  60: 83 eb ff fc  lwz r31,-4(r11)
  64: 7d 61 5b 78  mr r1,r11
  68: 4e 80 00 20  blr

This code should have been generated for the explicit copy constructor case because as you can see there is no mention of B::B(B&)  here!
I think it is a definite bug.
 
I have tried commenting out the conversion operator B::operator A() and the conversion constructor B::B(A) one at a time, the compiler then generates the error messages shown below.
 
Change#1:
//        B::B(A  init);

result#1:
 
MiscTest.cpp: In method `C::operator B()':
MiscTest.cpp:29: no matching function for call to `B::B (B)'
MiscTest.cpp:11: candidates are: B::B(int)

 
change#2:
//         B::operator A();

resullt#2:
MiscTest.cpp: In method `C::operator B()':
MiscTest.cpp:29: no matching function for call to `B::B (B)'
MiscTest.cpp:11: candidates are: B::B(int)
MiscTest.cpp:14:                 B::B(A)

I am a newbie to gcc innards, so I may be way off the mark. Sorry I don't have more details yet, I just ran into this problem last friday.
 
VisualC++ however does not display this behaviour and generates correct code.

Regards, 

Lakhbir Gandhi 
Consulting Engineer 
Peerless Systems Imaging Products (PSIP)
>How-To-Repeat:

>Fix:

>Release-Note:
>Audit-Trail:
>Unformatted:


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: c++/8910: Incorrect code generation when returning class by value
@ 2002-12-12  7:27 bangerth
  0 siblings, 0 replies; 2+ messages in thread
From: bangerth @ 2002-12-12  7:27 UTC (permalink / raw)
  To: gcc-bugs, gcc-prs, lgandhi, nobody

Synopsis: Incorrect code generation when returning class by value

State-Changed-From-To: open->closed
State-Changed-By: bangerth
State-Changed-When: Thu Dec 12 07:27:45 2002
State-Changed-Why:
    This is not a bug. In your conversion operator, you
    generate an unnamed object B(1234), which will then be
    assigned to the return value. However, you made the copy 
    constructor of B explicit. The standard says:
    
    An explicit constructor constructs objects just like non-explicit con-
      structors,  but  does  so  only where the direct-initialization syntax
      (_dcl.init_) or  where  casts  (_expr.static.cast_,  _expr.cast_)  are
      explicitly  used.
    
    Assigning to the return value does not happen with an
    explicit syntax, so the copy constructor cannot be used.
    
    That's it. The rest is just that gcc finds a conversion
    sequence that does what you want. Upshot: when you declare
    a class with an "explicit" copy constructor, you may not
    use it as a return value for functions that return by
    value (rather than by reference, for example).

http://gcc.gnu.org/cgi-bin/gnatsweb.pl?cmd=view%20audit-trail&database=gcc&pr=8910


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2002-12-12 15:27 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2002-12-11 19:16 c++/8910: Incorrect code generation when returning class by value lgandhi
2002-12-12  7:27 bangerth

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).