This is the mail archive of the gcc-bugs@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

exceptions optimizer bug in 1.0.3


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




Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]