This is the mail archive of the
gcc-prs@gcc.gnu.org
mailing list for the GCC project.
c++/8910: Incorrect code generation when returning class by value
- From: lgandhi at peerless dot com
- To: gcc-gnats at gcc dot gnu dot org
- Date: 12 Dec 2002 03:15:52 -0000
- Subject: c++/8910: Incorrect code generation when returning class by value
- Reply-to: lgandhi at peerless dot com
>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: