This is the mail archive of the
gcc-bugs@gcc.gnu.org
mailing list for the GCC project.
exceptions optimizer bug in 1.0.3
- To: egcs-bugs at cygnus dot com
- Subject: exceptions optimizer bug in 1.0.3
- From: ulrich dot kunitz at gefm dot deuba dot com
- Date: Mon, 18 May 1998 16:51:58 +0100
Hi,
I've found a bug caused by a wrong optimization of an exception region.
The code is from the OOC OmniBroker (www.ooc.com). egcs doesn't compile
OB_2.0.4/test/Test2_skel.c right with -O1. The bug doesn't occur without
optimizations. That's is good because OOC uses everything including
exceptions, multiple inheritance and templates.
I'm using
egcs-2.90.29 980515 (egcs-1.0.3 release)
GNU ld 2.9.1
GNU assembler 2.9.1
GNU objdump 2.9.1
Linux version 2.0.30 (root@cannes) (gcc version 2.7.2.1)
// Please don't tell me to upgrade the kernel, I usually
// know what I'm doing.
Processor intel Pentium 133
// No upgrade planned until Merced prices will match my budget.
IMHO the bug is caused by the optimizer (-O1) which merges an exception
region
with non exception regions. The appended parts of the objdump output show
that. According gcc/except.c exception regions will not be optimized, but
egcs-1.0.3 is exactly doing this here. If egcs would do that the
right way I wouldn't have noticed.
The assembler output of the compiler shows, that the compiler generated an
exception region but the function call which raises the exception is
outside
that region. The optimizer merged code parts with different exception
handler
sets.
Nevertheless using an exception regions table is good, because
there is almost no overhead if no exceptions are thrown. We don't have
to manipulate a dynamic exception context.
Thinking about it, brings me to following ideas. Instead of a simple table
of
exception regions we should define a map between minimal exceptions regions
(MER) and their handlers. A minimal exception region is a throw area, a
function call or a explicit cast statement.
An example:
try {
// MER1_BEGIN
throw Exception();
// MER1_END
try {
// MER2_BEGIN
f();
// MER2_END
}
// EH1
catch (const Exception& ex) {
// MER3_BEGIN
throw OtherException();
// MER3_END
}
}
// EH2
catch (const Exception& ex) {
cerr << "something happened\n";
}
The map would be
MER1_BEGIN MER1_END EH2
MER2_BEGIN MER2_END EH1, EH2
MER2_BEGIN MER3_END EH2
There are no double entries for a single MER. Only one search per frame
would
be necessary for a raised exception. The optimal representation of such a
map
is left as an exercise for friends of Knuth TAOP Volume 3 (smile).
The optimizer should than split the code inside optimization regions with
borders defined by the minimal exception regions. A merger between
different
optimization regions is only allowed if the have they same exception
handler
set. The set is allowed to be empty (smile). All other optimizations are
inside the optimization regions. But I have to admit, that I'm a newbie in
gcc
programming. So don't expect me to write the code for that.
Let's look deeper in the current bug: They objdump listing is generated
with
objdump -lrSC OB-2.0.4/test/Test2_skel.o. The C++ source for the object
file has been generated by the OmniBorker IDL parser. The flags
-march=pentium
-g -O1 -fno-implicit-templates -c has been used for the compilation.
The listings are from the method
BDispatchStatus
TestInterfaceEx_skel::_OB_dispatch(const char* _ob_op,
OBFixSeq< CORBA_Octet >& _ob_seq,
bool _ob_sw,
CORBA_ULong _ob_offIn,
CORBA_ULong _ob_offOut)
A large construct with nested if statements can be found there. The if
conditions are followed by blocks handling a particular CORBA method.
Blocks
of CORBA methods with the same signature are similar. All blocks are
calling
the implementation of its CORBA method. If the CORBA method throws
exceptions
according the IDL definition, they are catched inside the blocks.
The block we look first should call the virtual function opVoidEx() which
is
always raising an exception. But the actual call of opVoidEx() isn't here.
Instead there is a jump to 35ed at 5cb. According to the assembler output
of
egcs the exception region is from 5b9 to 5d1. The code there will never
raise an exception.
Other jumps to 35ed can be found inside the nested if construct. But the
other
blocks don't catch exceptions.
---------------------------------------------------------------------------
-
/home/kunitz/src/OB-2.0.4/test/Test2_skel.cpp:56
if(strcmp(_ob_op, "opVoidEx") == 0)
590: 8b 75 0c movl 0xc(%ebp),%esi
593: c7 85 b4 fc ff movl $0x1b0,0xfffffcb4(%ebp)
598: ff b0 01 00 00
599: R_386_32 .rodata
59d: bb 09 00 00 00 movl $0x9,%ebx
5a2: 8b bd b4 fc ff movl 0xfffffcb4(%ebp),%edi
5a7: ff
5a8: 89 d9 movl %ebx,%ecx
5aa: fc cld
5ab: 31 c0 xorl %eax,%eax
5ad: f3 a6 repz cmpsb %ds:(%esi),%es:(%edi)
5af: 74 04 je 5b5
<TestInterfaceEx_skel::_OB_dispatch(char const *, OBFixSeq<unsigned char>
&, bool, unsigned long, unsigned long)+0x31>
5b1: 19 c0 sbbl %eax,%eax
5b3: 0c 01 orb $0x1,%al
5b5: 85 c0 testl %eax,%eax
5b7: 75 1b jne 5d4
<TestInterfaceEx_skel::_OB_dispatch(char const *, OBFixSeq<unsigned char>
&, bool, unsigned long, unsigned long)+0x50>
/home/kunitz/src/OB-2.0.4/test/Test2_skel.cpp:61
{
try
{
opVoidEx();
5b9: 83 7d 08 00 cmpl $0x0,0x8(%ebp)
5bd: 74 06 je 5c5
<TestInterfaceEx_skel::_OB_dispatch(char const *, OBFixSeq<unsigned char>
&, bool, unsigned long, unsigned long)+0x41>
5bf: 8b 55 08 movl 0x8(%ebp),%edx
5c2: 8b 42 04 movl 0x4(%edx),%eax
5c5: 8b 58 04 movl 0x4(%eax),%ebx
5c8: 83 c3 08 addl $0x8,%ebx
/home/kunitz/src/OB-2.0.4/test/Test2_skel.cpp:71
CORBA_ULong _ob_cnt = _ob_offOut;
_ob_seq.length(0);
_ob_seq.length(_ob_cnt);
#ifdef OB_CLEAR_MEM
memset(_ob_seq.data(), 0, _ob_seq.length());
#endif
return OBDispatchStatusOK;
5cb: e9 1d 30 00 00 jmp 35ed
<TestInterfaceEx_skel::_OB_dispatch(char const *, OBFixSeq<unsigned char>
&, bool, unsigned long, unsigned long)+0x3069>
5d0: 90 nop
5d1: 8d 76 00 leal 0x0(%esi),%esi
/home/kunitz/src/OB-2.0.4/test/Test2_skel.cpp:89
}
catch(const ExVoid& _ob_except)
{
CORBA_ULong _ob_cnt = _ob_offOut;
OBMarshalCount(_ob_except, _ob_cnt);
_ob_seq.length(0);
_ob_seq.length(_ob_cnt);
#ifdef OB_CLEAR_MEM
memset(_ob_seq.data(), 0, _ob_seq.length());
#endif
CORBA_Octet* _ob_o = _ob_seq.data() + _ob_offOut;
OBMarshal(_ob_except, _ob_o);
return OBDispatchStatusExcept;
}
}
---------------------------------------------------------------------------
-
Let's look at the code around 35ed. The virtual function is selected at
35e7
as at 5c8 above. The code which prepares the call of the virtual function
at
3604 starts at 35ed. This call at 3604 actually has at least two differing
exception handler sets. The exception region concept can't handle this.
When opVoidEx() is called here the raised exception isn't handled by the
right
exception handler because opVoidEx() has been called outside its defined
exception region. They optimizer doesn't seem to recognize exception
regions
in this case.
---------------------------------------------------------------------------
-
else if(strcmp(_ob_op, "op_DATA_CONVERSION_Ex") == 0)
35bc: 8b 75 0c movl 0xc(%ebp),%esi
35bf: bf 58 09 00 00 movl $0x958,%edi
35c0: R_386_32 .rodata
35c4: b9 16 00 00 00 movl $0x16,%ecx
35c9: fc cld
35ca: 31 c0 xorl %eax,%eax
35cc: f3 a6 repz cmpsb %ds:(%esi),%es:(%edi)
35ce: 74 04 je 35d4
<TestInterfaceEx_skel::_OB_dispatch(char const *, OBFixSeq<unsigned char>
&, bool, unsigned long, unsigned long)+0x3050>
35d0: 19 c0 sbbl %eax,%eax
35d2: 0c 01 orb $0x1,%al
35d4: 85 c0 testl %eax,%eax
35d6: 75 40 jne 3618
<TestInterfaceEx_skel::_OB_dispatch(char const *, OBFixSeq<unsigned char>
&, bool, unsigned long, unsigned long)+0x3094>
/home/kunitz/src/OB-2.0.4/test/Test2_skel.cpp:1472
{
op_DATA_CONVERSION_Ex();
35d8: 83 7d 08 00 cmpl $0x0,0x8(%ebp)
35dc: 74 06 je 35e4
<TestInterfaceEx_skel::_OB_dispatch(char const *, OBFixSeq<unsigned char>
&, bool, unsigned long, unsigned long)+0x3060>
35de: 8b 55 08 movl 0x8(%ebp),%edx
35e1: 8b 42 04 movl 0x4(%edx),%eax
35e4: 8b 58 04 movl 0x4(%eax),%ebx
35e7: 81 c3 80 01 00 addl $0x180,%ebx
35ec: 00
35ed: 31 f6 xorl %esi,%esi
35ef: 83 7d 08 00 cmpl $0x0,0x8(%ebp)
35f3: 74 06 je 35fb
<TestInterfaceEx_skel::_OB_dispatch(char const *, OBFixSeq<unsigned char>
&, bool, unsigned long, unsigned long)+0x3077>
35f5: 8b 4d 08 movl 0x8(%ebp),%ecx
35f8: 8b 71 04 movl 0x4(%ecx),%esi
35fb: 0f bf 03 movswl (%ebx),%eax
35fe: 01 f0 addl %esi,%eax
3600: 50 pushl %eax
3601: 8b 43 04 movl 0x4(%ebx),%eax
3604: ff d0 call *%eax
/home/kunitz/src/OB-2.0.4/test/Test2_skel.cpp:1476
CORBA_ULong _ob_cnt = _ob_offOut;
_ob_seq.length(0);
3606: 6a 00 pushl $0x0
3608: 8b 7d 10 movl 0x10(%ebp),%edi
360b: 57 pushl %edi
360c: e8 fc ff ff ff call 360d
<TestInterfaceEx_skel::_OB_dispatch(char const *, OBFixSeq<unsigned char>
&, bool, unsigned long, unsigned long)+0x3089>
360d: R_386_PC32 OBFixSeq<unsigned char>::length(unsigned
long)
/home/kunitz/src/OB-2.0.4/test/Test2_skel.cpp:1477
_ob_seq.length(_ob_cnt);
3611: 8b 55 1c movl 0x1c(%ebp),%edx
3614: 52 pushl %edx
/home/kunitz/src/OB-2.0.4/test/Test2_skel.cpp:1482
#ifdef OB_CLEAR_MEM
memset(_ob_seq.data(), 0, _ob_seq.length());
#endif
return OBDispatchStatusOK;
3615: eb 5d jmp 3674
<TestInterfaceEx_skel::_OB_dispatch(char const *, OBFixSeq<unsigned char>
&, bool, unsigned long, unsigned long)+0x30f0>
3617: 90 nop
/home/kunitz/src/OB-2.0.4/test/Test2_skel.cpp:1484
}
---------------------------------------------------------------------------
-
This mail proves at least that I'm still able to read assembler listings.
Ciao, Uli