Under some conditions, may read a member variable before initializing it

XMLWordPrintable

    • Type: Bug
    • Resolution: Unresolved
    • Priority: Medium

      The attached test case is a cutdown from the actual code. Build it ...

      % cl6x -mv64+ -O2 -mf5 --symdebug:none -s file.cpp
      

      The compiler generated assembly is in file.asm

      The test case has these lines ...

      class level3_class
      {
      private:
      	void (incomplete_struct::*mf_)(); 
      	void (*minv_)(void *, void (incomplete_struct::*)());
      	level2_class l2m_;
      	int int_member_;
      
      public:
      	#pragma FUNC_CANNOT_INLINE
      	level3_class(void (incomplete_struct::*f)(), incomplete_struct *o)
      		: int_member_((f && o) ? 3 : 0)
      		  // condition needs to be present to trigger bug
      	{
      		if(f && o)
      		// condition must be present to trigger bug (because otherwise
      		// construction of l2m_ is optimized)
      		{
      			// at least minv_ must be present and written to to trigger the bug
      			mf_ = f;
      			minv_ = &somefunc;
      			level2_class(o, empty_struct{}).swap(l2m_);
      		}
      	}
      };
      
      level3_class buildlevel3_class(
          void (incomplete_struct::*f)(),
          incomplete_struct *o)
      {
      	return level3_class(f, o);
      }
      

      Focus on this line ...

      			level2_class(o, empty_struct{}).swap(l2m_);
      

      The member variable l2m_ is initialized by the default constructor for level2_class ...

      class level2_class
      {
      	void *ptr_to_void_;
      	level1_struct *ptr_to_level1_struct_;
      
      public:
      	level2_class()
      	    : ptr_to_void_(nullptr), ptr_to_level1_struct_(nullptr)
      	{}
      

      When l2m_ goes out of scope, the destructor is called ...

      	~level2_class()
      	{
      	    if (ptr_to_level1_struct_)
      		    ptr_to_level1_struct_->callme();
      	}
      

      In this specific case, ptr_to_level1_struct is NULL, therefore callme does not get called. Except it does.

      The code generated by the compiler to write NULL to the member variables ptr_to_void_ and ptr_to_level1_struct_ is ...

                 STNDW   .D1T2   B9:B8,*A10(12)    ; [A_D64P] |17|
      

      The registers B9 and B8 are zero, and the register A10 holds the this pointer. The problem is an instruction which occurs a few cycles earlier ...

      || [ A0]   LDW     .D2T1   *B6(4),A0         ; [B_D64P] |4459| 
      

      B6 holds this+12. This is the same address as the 2nd 32-bit word of memory written by the STNDW. Thus, this loads garbage into A0. It goes downhill from that point. Here is one of the problem instructions that happen later ...

         [ A0]   CALL    .S1     _ZN13level1_struct6callmeEv ; [A_S64P] |43|
      

      A0 should be 0. Because it holds garbage instead, the function is incorrectly called.

            Assignee:
            TI User
            Reporter:
            TI User
            Votes:
            1 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:

                Connection: Intermediate to External PROD System
                EXTSYNC-6036 - Missing dependency edge due to inco...
                SYNCHRONIZED
                • Last Sync Date: