RAD Studio 10.1 Berlin clang Probleme durch Optimierung im Release-Build



  • Hallo,

    ich habe Problem mit dem clang Compiler im Release Build. Es sieht so aus, als würde er Funktionsaufrufe wegoptimieren und dadurch habe ich ein falsches Verhalten in meinem Formular. Im Event Handler einer Action habe ich zum Entprellen der Action einen lokalen Guard, der den Enabled Status für den Scope des Event Handlers auf false setzt. Beim Verlassen des Scopes wird der Enabled Status auf den ursprünglichen Wert zurückgesetzt. Im Debug Modus funktioniert das, im Release Modus allerdings nicht. Konkret habe ich einen TButton im Speedbutton-Modus, an dem eine TAction hängt. Der Down -Status des Speedbutton hängt also vom Checked -Status der Action ab. Unabhängig vom ursprünglichen Status wechselt der Down -Status immer nach false und springt nicht um. Dieses Verhalten tritt nur mit dem clang Compiler und nur im Release Build auf.

    namespace
    {
       template<typename T>
       T* set_state( TObject* Sender, bool State, bool* SaveState = NULL )
       {
          T* ptr = dynamic_cast<T*>( Sender );
          if( ptr )
          {
             if( SaveState ) *SaveState = ptr->Enabled;
             ptr->Enabled = State;
          }
          return ptr;
       }
    }
    
    class ScopedVCLDebounceGuard
    {
       bool     SaveState_;
       TObject* Control_;
    
    public:
       ScopedVCLDebounceGuard( TObject* Sender ) :
          SaveState_( false ),
          Control_( Sender )
          {
             if( !set_state<TControl>( Control_, false, &SaveState_ ) )
             {
                set_state<TAction>( Control_, false, &SaveState_ );
             }
          }
    
          ~ScopedVCLDebounceGuard()
          {
             if( !set_state<TControl>( Control_, SaveState_ ) )
             {
                set_state<TAction>( Control_, SaveState_ );
             }
          }
    
    private:
       ScopedVCLDebounceGuard( const ScopedVCLDebounceGuard& );
       ScopedVCLDebounceGuard& operator=( const ScopedVCLDebounceGuard& );
    };
    
    __fastcall TForm1::TForm1( TComponent* Owner )
       : TForm(Owner)
    {
    }
    
    bool TForm1::checked() const
    {
       return ActionTest->Checked;
    }
    
    void TForm1::set_checked( bool Checked )
    {
       ActionTest->Checked = Checked;
    }
    
    void __fastcall TForm1::OnActionTest( TObject* Sender )
    {
       ScopedVCLDebounceGuard Lock( Sender );
       set_checked( !checked() );
    }
    

    Hat jemand eine Idee, wie man das löst? Ich benutze das an einigen hundert Stellen in unserem Code und möchte das nicht in jedem Event Handler einzeln fixen müssen.



  • Edit:
    Es scheint wohl am TcxButton zu liegen. Ich habe das Verhalten mit einem TSpeedButton getestet und mit dem geht´s.



  • Und mit dieser Version des DebounceGuards geht´s auch im Release Modus:

    template<typename ObjType>
    class ScopedVCLDebounceGuard
    {
    	bool			SaveState_;
    	ObjType*		Control_;
    
    public:
    	ScopedVCLDebounceGuard( TObject* Sender ) :
    		SaveState_( false ),
    		Control_( dynamic_cast<ObjType*>( Sender ) )
    	{
    		if( Control_ )
    		{
    			SaveState_ 			= Control_->Enabled;
    			Control_->Enabled = false;
    		}
    	}
    
    	~ScopedVCLDebounceGuard()
    	{
    		if( Control_ )
    		{
    			Control_->Enabled 	= SaveState_;
    		}
    	}
    
    private:
    	ScopedVCLDebounceGuard( const ScopedVCLDebounceGuard& );
    	ScopedVCLDebounceGuard& operator=( const ScopedVCLDebounceGuard& );
    };
    

    Ich versteh´s mal grad so überhaupt nicht 😞



  • Hab das jetzt mit dem clang vom 10.2 Tokyo probiert, damit macht der Code genau das, was er soll. War wohl ein Bug im Compiler, die Version 3.3.6408.36116 macht´s richtig.



  • Hallo,

    also deine zweite Version ist schon mal viel besser als die erste, allerdings ist da auch immer noch zu viel if und dynamic_cast drin.
    Einfach so:

    template<typename ObjType>
    class ScopedVCLDebounceGuard
    {
        bool            SaveState_;
        ObjType*        Control_;
    
    public:
        ScopedVCLDebounceGuard( ObjType* Sender ) :
            SaveState_( Sender->Enabled ),
            Control_( Sender )
        {
            Control_->Enabled = false;
        }
    
        ~ScopedVCLDebounceGuard()
        {
            Control_->Enabled = SaveState_;
        }
     };
    

    Die Frage, wieso das in 10.2 geht aber mit 10.1 nicht, interessiert mich allerdings auch mehr.
    Einen Fehler sehe ich in der ersten Version auch nicht. Sicher, dass es nicht vielleicht mit diesem TcxButton zu tun hat?

    Kennst du zufällig eine Seite wo mal die konkrekten Features/Änderungen zw. den clang-Compiler-Versionen 10.1 -> 10.2 beschrieben wird?

    Ich teste gerade die 10.2 und die IDE stürzt bei mir doch sehr häufig mit Speicherzugriffsfehlern ab. Meine vorherigen Tests mit 10.1 waren nicht so schlimm, deshalb spiele ich mit dem Gedanken auf die 10.1 zu gehen. Solche Posts wie Deiner hier, machen mir jetzt etwas Angst...

    Wie ist es mit der 10.2er (IDE)-Stabilität bei Dir?



  • OK, deine erste Version ist doch falsch. Der set_state-Aufruf im Destruktor passt gar nicht und der Compiler hätte sich beschweren müssen.

    Oder hast du da noch eine anderes set_state() rumliegen?



  • Warum ist die falsch?



  • zu wenig argumente, bool statt &bool.

    T* set_state( TObject* Sender, bool State, bool* SaveState = NULL );
    
    ~ScopedVCLDebounceGuard()
    {
        if( !set_state<TControl>( Control_, SaveState_ ) )
        {
            set_state<TAction>( Control_, SaveState_ );
        }
    }
    

    das sowas ohne Fehler mit dem bcc kompiliert bin ich gewohnt. Wenn das beim clang-Compiler immer noch so ist, wäre ich mehr enttäuscht, als ich mir hätte vorstellen können.



  • Ich sehe in dem Code auch keinen Fehler (SaveState_ wird als 2. Parameter übergeben und der dritte Parameter ist optional, also wird der Defaultwert NULL benutzt).



  • ups hast recht


Log in to reply