Understanding branches within destructors

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Jeff Newman

    Understanding branches within destructors

    Hello,

    Could anyone explain to me why the following class's destructor shows
    up as having multiple branches? (At least as judged by gcov 4.1.2
    when compiled with gcc 4.1.2 ):

    struct blah
    {
    blah();
    virtual ~blah();
    };

    blah::blah()
    {
    }

    blah::~blah()
    {
    }

    int main()
    {
    blah myBlah;

    return 0;
    }


    The output from gcov showing these branches (with 1 not taken):

    1: 11:blah::~blah( )
    -: 12:{
    1: 13:}
    branch 0 never executed
    branch 1 never executed
    call 2 never executed
    branch 3 taken 0 (fallthrough)
    branch 4 taken 1
    call 5 never executed
    branch 6 never executed
    branch 7 never executed
    call 8 never executed

    Please note that if I make the destructor non-virtual, the branches go
    away. Is what I'm seeing here a relic of the tool, or is there
    something going on the language that causes this?
  • username localhost

    #2
    Re: Understanding branches within destructors

    On Oct 24, 1:46 pm, Jeff Newman <Jeffrey.M.New. ..@gmail.comwro te:
    Hello,
    >
    Could anyone explain to me why the following class's destructor shows
    up as having multiple branches?  (At least as judged by gcov 4.1.2
    when compiled with gcc 4.1.2 ):
    >
    struct blah
    {
      blah();
      virtual ~blah();
    >
    };
    >
    blah::blah()
    {
    >
    }
    >
    blah::~blah()
    {
    >
    }
    >
    int main()
    {
      blah myBlah;
    >
      return 0;
    >
    }
    >
    The output from gcov showing these branches (with 1 not taken):
    >
            1:   11:blah::~blah( )
            -:   12:{
            1:   13:}
    branch  0 never executed
    branch  1 never executed
    call    2 never executed
    branch  3 taken 0 (fallthrough)
    branch  4 taken 1
    call    5 never executed
    branch  6 never executed
    branch  7 never executed
    call    8 never executed
    >
    Please note that if I make the destructor non-virtual, the branches go
    away.  Is what I'm seeing here a relic of the tool, or is there
    something going on the language that causes this?
    The branches are most likely part of the virtual function dispatch
    code that gcc put in the destructor. My understanding is that
    compilers may locate virtual function dispatch code wherever they
    want, and gcc chooses to put in in the function itself, at least in
    the case of a virtual destructor.

    Comment

    • Jeff Newman

      #3
      Re: Understanding branches within destructors

      The branches are most likely part of the virtual function dispatch
      code that gcc put in the destructor. My understanding is that
      compilers may locate virtual function dispatch code wherever they
      want, and gcc chooses to put in in the function itself, at least in
      the case of a virtual destructor.
      What style of call would then result in taking the other branches?
      I've tried the following (which is all the cases I can think of), and
      there are still unexecuted branches.

      struct blah
      {
      blah() {};
      virtual ~blah()
      {
      };
      };


      struct derived : blah
      {
      derived() {};

      ~derived() {};
      };

      int main()
      {
      blah myBlah;
      blah* myDerived = new derived();
      delete myDerived;

      derived myDerived2;

      return 0;
      }

      The (snipped) report:
      3: 4: virtual ~blah()
      3: 5: {
      3: 6: };
      branch 0 never executed
      branch 1 never executed
      call 2 never executed
      branch 3 taken 0 (fallthrough)
      branch 4 taken 2
      call 5 never executed
      branch 6 taken 0 (fallthrough)
      branch 7 taken 1
      call 8 never executed
      -: 7:};

      Comment

      • Obnoxious User

        #4
        Re: Understanding branches within destructors

        On Fri, 24 Oct 2008 12:12:18 -0700, Jeff Newman wrote:
        >The branches are most likely part of the virtual function dispatch code
        >that gcc put in the destructor. My understanding is that compilers may
        >locate virtual function dispatch code wherever they want, and gcc
        >chooses to put in in the function itself, at least in the case of a
        >virtual destructor.
        >
        What style of call would then result in taking the other branches? I've
        tried the following (which is all the cases I can think of), and there
        are still unexecuted branches.
        >
        [snip]
        >
        The (snipped) report:
        3: 4: virtual ~blah()
        3: 5: {
        3: 6: };
        branch 0 never executed
        branch 1 never executed
        call 2 never executed
        branch 3 taken 0 (fallthrough)
        branch 4 taken 2
        call 5 never executed
        branch 6 taken 0 (fallthrough)
        branch 7 taken 1
        call 8 never executed
        -: 7:};
        It's implementation details. You're better off
        asking the experts at the gcc mailing lists.



        --
        OU
        Remember 18th of June 2008, Democracy died that afternoon.

        Comment

        • dascandy@gmail.com

          #5
          Re: Understanding branches within destructors

          On Oct 24, 8:12 pm, Jeff Newman <Jeffrey.M.New. ..@gmail.comwro te:
          The branches are most likely part of the virtual function dispatch
          code that gcc put in the destructor. My understanding is that
          compilers may locate virtual function dispatch code wherever they
          want, and gcc chooses to put in in the function itself, at least in
          the case of a virtual destructor.
          >
          What style of call would then result in taking the other branches?
          I've tried the following (which is all the cases I can think of), and
          there are still unexecuted branches.
          >
          struct blah
          {
            blah() {};
            virtual ~blah()
            {
            };
          >
          };
          >
          struct derived : blah
          {
            derived() {};
          >
            ~derived() {};
          >
          };
          >
          int main()
          {
            blah myBlah;
            blah* myDerived = new derived();
            delete myDerived;
          >
            derived myDerived2;
          >
            return 0;
          >
          }
          >
          The (snipped) report:
                  3:    4:  virtual ~blah()
                  3:    5:  {
                  3:    6:  };
          branch  0 never executed
          branch  1 never executed
          call    2 never executed
          branch  3 taken 0 (fallthrough)
          branch  4 taken 2
          call    5 never executed
          branch  6 taken 0 (fallthrough)
          branch  7 taken 1
          call    8 never executed
                  -:    7:};
          It's most likely (given that you're using GCC) that it instantiates
          two destructors - one for if it is a non-virtual base class (which
          means that it calls its parent destructors) and when it is (which
          means that it does not call its parent destructors). Your virtual
          function table contains two functions too.

          If it's nonvirtual it should still generate both of them, but it might
          notice that it has no base classes and that they are equal, and hence
          merge them. It also might not generate one of them as nobody calls it.
          In case of virtual functions, somebody outside of your scope of
          compilation might call it anyway, so it has to generate it and put it
          in the virtual function table - so it has to be made, and then has
          uncalled code.



          Could you try the test again using it as a virtual base in one
          subclass?

          Regards,
          Peter

          Comment

          • sean_in_raleigh@yahoo.com

            #6
            Re: Understanding branches within destructors

            What style of call would then result in taking the other branches?
            I've tried the following (which is all the cases I can think of), and
            there are still unexecuted branches.

            Don't forget about placement new:




            Sean

            Comment

            • James Kanze

              #7
              Re: Understanding branches within destructors

              On Oct 29, 8:52 pm, "dasca...@gmail .com" <dasca...@gmail .comwrote:
              On Oct 24, 8:12 pm, Jeff Newman <Jeffrey.M.New. ..@gmail.comwro te:
              [...concerning multiple entry points for destructors...]
              The (snipped) report:
              3: 4: virtual ~blah()
              3: 5: {
              3: 6: };
              branch 0 never executed
              branch 1 never executed
              call 2 never executed
              branch 3 taken 0 (fallthrough)
              branch 4 taken 2
              call 5 never executed
              branch 6 taken 0 (fallthrough)
              branch 7 taken 1
              call 8 never executed
              -: 7:};
              It's most likely (given that you're using GCC) that it
              instantiates two destructors - one for if it is a non-virtual
              base class (which means that it calls its parent destructors)
              and when it is (which means that it does not call its parent
              destructors). Your virtual function table contains two
              functions too.
              Either I've misunderstood what you wrote, or you got it wrong.
              The different entry points used depend on whether the destructor
              is for the most derived class or not; roughly speaking, calls
              from outside go to one, and calls from a derived class
              destructor go to the other. The difference is that the calls
              from a derived class destructor must not call the destructors of
              any virtual base classes; those from the most derived class must
              call the destructors of any virtual base classes *after* having
              called the destructors of the non-virtual base classes. (I
              think that the words "in charge" appear in output of nm -C for
              the constructor called when the class is the most derived
              class.)
              If it's nonvirtual it should still generate both of them, but
              it might notice that it has no base classes and that they are
              equal, and hence merge them. It also might not generate one of
              them as nobody calls it. In case of virtual functions,
              somebody outside of your scope of compilation might call it
              anyway, so it has to generate it and put it in the virtual
              function table - so it has to be made, and then has uncalled
              code.
              The not most derived class case is never called using virtual
              function resolution, so it needed appear in the vtable.
              Could you try the test again using it as a virtual base in one
              subclass?
              I think the test would have to be:

              class B {} ;
              class D1 : public virtual B {} ;
              class D2 : public D1 {} ;

              , then call the destructor on an instance of D1 and on an
              instance of D2. If I understand it corretly, this should call
              both destructors of D1.

              --
              James Kanze (GABI Software) email:james.kan ze@gmail.com
              Conseils en informatique orientée objet/
              Beratung in objektorientier ter Datenverarbeitu ng
              9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

              Comment

              Working...