My problem is about the constructor/destructor execution optimization: there is a significant difference of the execution order between PC and AVR (8-bit) platforms.
I created a small source to demonstrate it. The important part is the class C which is instantiated within the operator<<().
Let's see the PC version first:
(The class F here is only to see the float operations in the output)
#include <iostream>
struct C
{
C()
{
std::cout << "C created" << std::endl;
}
~C()
{
std::cout << "C deleted" << std::endl;
}
};
struct D
{
D & operator<<(float f)
{
C _c;
std::cout << "D << " << f << std::endl;
value = f;
return *this;
}
float value;
};
struct F
{
F(float f):
value(f)
{
std::cout << "F created: " << value << std::endl;
}
operator float()
{
return value;
}
F operator+(float f)
{
std::cout << value << "+" << f << "=" << value+f << std::endl;
return F(value+f);
}
float value;
};
F f1(3.5f);
F f2(4.2f);
D d;
int main()
{
std::cout << "main() started" << std::endl;
d << (f1+f2);
}
I compiled it for my PC (this source is in main.cpp):
g++ -o main -O4 -Wall main.cpp
The execution result is here:
$ ./main
F created: 3.5
F created: 4.2
main() started
3.5+4.2=7.7
F created: 7.7
C created
D << 7.7
C deleted
The execution order is correct here, everything is as expected.
To compile it for AVR (8-bit family) the std::cout calls removed, and the class C is slightly modified:
struct C
{
C()
{
asm("cli");
}
~C()
{
asm("sei");
}
};
struct D
{
D & operator<<(float f)
{
C _c;
value = f;
return *this;
}
float value;
};
float f1(3.5f);
float f2(4.2f);
D d;
int main()
{
d << (f1+f2);
}
It is compiled for AVR (8-bit family):
avr-g++ -o main -O4 -Wall main.cpp
Let's see the result of the main function:
00000028 <main>:
28: f8 94 cli
2a: 20 91 68 00 lds r18, 0x0068
2e: 30 91 69 00 lds r19, 0x0069
32: 40 91 6a 00 lds r20, 0x006A
36: 50 91 6b 00 lds r21, 0x006B
3a: 60 91 64 00 lds r22, 0x0064
3e: 70 91 65 00 lds r23, 0x0065
42: 80 91 66 00 lds r24, 0x0066
46: 90 91 67 00 lds r25, 0x0067
4a: 2e d0 rcall .+92 ; 0xa8 <__addsf3>
4c: 60 93 60 00 sts 0x0060, r22
50: 70 93 61 00 sts 0x0061, r23
54: 80 93 62 00 sts 0x0062, r24
58: 90 93 63 00 sts 0x0063, r25
5c: 78 94 sei
5e: 80 e0 ldi r24, 0x00 ; 0
60: 90 e0 ldi r25, 0x00 ; 0
62: 08 95 ret
Every operations are executed between 'cli' and 'sei' here, which means the class C is instantiated earlier than expected. I expected the addition to be executed first, and only the assignment is placed between 'cli' and 'sei'. Somehow the whole expression is optimized into the operator<<() function.
I tried to create temporary variables and other tricks but cannot get the expected behavior. Unfortunately I do not know the C++ standard deep enough, so I cannot decide if it is a compiler bug or such an execution order change is allowed by the standard. At least I do not understand the difference between platforms.
Please help me how can I get the expected execution order in AVR platform!
I would expect something like this:
0000002a <main>:
2a: 20 91 68 00 lds r18, 0x0068
2e: 30 91 69 00 lds r19, 0x0069
32: 40 91 6a 00 lds r20, 0x006A
36: 50 91 6b 00 lds r21, 0x006B
3a: 60 91 64 00 lds r22, 0x0064
3e: 70 91 65 00 lds r23, 0x0065
42: 80 91 66 00 lds r24, 0x0066
46: 90 91 67 00 lds r25, 0x0067
4a: 2e d0 rcall .+92 ; 0xa8 <__addsf3>
4c: f8 94 cli
4e: 60 93 60 00 sts 0x0060, r22
52: 70 93 61 00 sts 0x0061, r23
56: 80 93 62 00 sts 0x0062, r24
5a: 90 93 63 00 sts 0x0063, r25
5e: 78 94 sei
60: 80 e0 ldi r24, 0x00 ; 0
62: 90 e0 ldi r25, 0x00 ; 0
64: 08 95 ret
I tried g++ versions 4.9.2 and 5.3.0 with the same result.
Thanx in advance,
Gyorgy Kovesdi
Additions to the first comments: The global objects' order is not important, only the class C, which is local to a function, and only its constructor against the operations like f1+f2 in this example. The asm instruction is also not relevant, the constructor can have any content, it is just an example.