if (0) vs segédváltozó

Volt egy olyan kérdés, hogy segédváltozóval olyan fájdalmas-e megoldani C-ben a hibakezelést. Erre jött az a válasz, hogy nincs általános válasz. Próbájuk ki!

Első kör: C++, Visual Studio 2010, Release fordítás. (O2)

GOTO-val

#include "stdafx.h"
#include <iostream>

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
013E1000  push        ebp  
013E1001  mov         ebp,esp  
	int i = 0;
013E1003  xor         eax,eax  


	if (argc > 4)
013E1005  cmp         dword ptr [argc],4  
013E1009  jle         err+3 (13E100Eh)  
		goto err;

	
	if (0)
	{
err:
		i--;
013E100B  or          eax,0FFFFFFFFh  
	}

	i++;

	cout << i;
013E100E  mov         ecx,dword ptr [__imp_std::cout (13E2048h)]  
013E1014  inc         eax  
013E1015  push        eax  
013E1016  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (13E2044h)]  


	return 0;
013E101C  xor         eax,eax  
}
013E101E  pop         ebp  
013E101F  ret

Segédváltozóval:

int _tmain(int argc, _TCHAR* argv[])
{
01311000  push        ebp  
01311001  mov         ebp,esp  
	int i = 0;
01311003  xor         eax,eax  
	bool err = false;


	if (argc > 4)
01311005  cmp         dword ptr [argc],4  
01311009  jle         wmain+0Eh (131100Eh)  
	{
		err = true;
	}

	
	if (err)
	{
		i--;
0131100B  or          eax,0FFFFFFFFh  
	}

	i++;

	cout << i;
0131100E  mov         ecx,dword ptr [__imp_std::cout (1312048h)]  
01311014  inc         eax  
01311015  push        eax  
01311016  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (1312044h)]  


	return 0;
0131101C  xor         eax,eax  
}
0131101E  pop         ebp  
0131101F  ret 

Szóval tökugyanaz. GCC/Clang nem tudom mit csinál, de meglepődnék, ha nem optimalizálna ki egy ilyet.

Na de mi a helyzet .NET-ben?

C#, VS2010, .NET 4.0, Release fordítás

GOTO-val

Error	1	No such label 'err' within the scope of the goto statement	c:\users\saxus\documents\visual studio 2010\Projects\ifgoto\ConsoleApplication1\Program.cs	13	17	ConsoleApplication1

;)

Segédváltozóval


            var err = false;
00000000  push        ebp 
00000001  mov         ebp,esp 
00000003  sub         esp,0Ch 
00000006  mov         dword ptr [ebp-0Ch],ecx 
00000009  cmp         dword ptr ds:[00670B0Ch],0 
00000010  je          00000017 
00000012  call        6AF15676 
00000017  xor         edx,edx 
00000019  mov         dword ptr [ebp-8],edx 
0000001c  mov         dword ptr [ebp-4],0 
00000023  xor         edx,edx 
00000025  mov         dword ptr [ebp-4],edx 
            var i = 0;
00000028  xor         edx,edx 
0000002a  mov         dword ptr [ebp-8],edx 

            if (args.Length > 4)
0000002d  mov         eax,dword ptr [ebp-0Ch] 
00000030  cmp         dword ptr [eax+4],4 
00000034  jle         00000043 
                err = true;
00000036  mov         eax,1 
0000003b  and         eax,0FFh 
00000040  mov         dword ptr [ebp-4],eax 

            if (err)
00000043  cmp         dword ptr [ebp-4],0 
00000047  je          0000004C 
            {
                i--;
00000049  dec         dword ptr [ebp-8] 
            }

            i++;
0000004c  inc         dword ptr [ebp-8] 

            Console.WriteLine(i);
0000004f  mov         ecx,dword ptr [ebp-8] 
00000052  call        6A663F44 
        }
00000057  nop 
00000058  mov         esp,ebp 
0000005a  pop         ebp 
0000005b  ret

.NET 4.5-tel egyébként centire ugyanezt a kódot adja. A helyzetet viszont erősen árnyalja az, hogy a JIT-nek megvan az a képessége, hogy ha látja azt, hogy egy függvényt nagyon sokszor hívunk, akkor nekiáll újraoptimalizálni. Viszont erősen úgy tűnik, hogy nem teszi, még ha végtelen ciklusban futtatom, akkor sem. De az is elképzelhető, hogy erre jelenleg nem készítették fel a .NET jitterét.

tl;dr

Egy jobb C/C++ fordítónál erős az esély, hogy nem számít a segédváltozó, ha statikusan ki lehet értékelni. Más nyelveknél számíthat. Hja és ismételten igaz: ismerni kell a fordítót/VM-et, amit használunk és ne akarjuk okosabbak lenni annál, ha nem muszáj.

Hozzászólások

> Viszont erősen úgy tűnik, hogy nem teszi, még ha végtelen ciklusban futtatom, akkor sem.
Ezt mibol latod?

Hagyom futni, aztán teszek egy breakpointot, majd nyomok egy CTRL+ALT+D-t (Dissassembly), hogy lássam, mi fut épp. Feltételezésem szerint, ha a jitter újrafordítaná a kódot, akkor az új függvényt kellene látnom.

Erősen aláhúzva hozzáteszem: szerintem.

----------------
Lvl86 Troll, "hobbifejlesztő" - Think Wishfully™

FYI:

Xi Wang, Nickolai Zeldovich, M. Frans Kaashoek, and Armando Solar-Lezama: Towards Optimization-Safe Systems: Analyzing the Impact of Undefined Behavior

"This paper studies an emerging class of software bugs called optimization-unstable code: code that is unexpectedly discarded by compiler optimizations due to undefined behavior in the program. Unstable code is present in many systems, including the Linux kernel and the Postgres database. The consequences of unstable code range from incorrect functionality to missing security checks. To reason about unstable code, this paper proposes a novel model, which views unstable code in terms of optimizations that leverage undefined behavior. Using this model, we introduce a new static checker called Stack that precisely identifies unstable code. Applying Stack to widely used systems has uncovered 160 new bugs that have been confirmed and fixed by developers"

http://people.csail.mit.edu/nickolai/papers/wang-stack.pdf

Az alabbi teljesen szabalyos C kod pedig nondeterministic:
https://gist.github.com/anonymous/d2e55bb6f735546d9916

C-ben rengeteg ilyen nemdeterminisztikus kod van, ez a fenti pelda eppen abbol van, hogy a + operator operandusaunak kiertekelesenek sorrendje nem definialt a szabvany szintjen, forditofuggo, hogy mi fordul le ezen esetben.
Ez a nyelv hianyossaga, de van meg mas forditofuggo dolog is rendesen.

Ettol fuggetlenul, aki ilyen side effectel rendelkezo kodot ir, az menjen a fenebe.