Day 213/214: issues with changes to debug values

I want to mention couple of issues with latest changes to debug system. When DEBUGUI_xyz defines were replaced with DEBUG_IF and DEBUG_VALUE macros in day 213/214 there are now two issues not handled:

1) if DEBUG_IF macro will be nested and top level if is false, then debug system will not see nested if or other nested variables. This means during recompilation debug system will not be able to generate GlobalConstants_xyz define in "handmade_config.h" file. Before this was not a problem, because all variables were manually listed. Now it is a problem. For example:
1
2
3
4
5
6
7
8
DEBUG_IF(Something_UseFoo)
{
    DEBUG_IF(SomethingElse_UseBar)
    {
        DEBUG_VARIABLE(r32, Renderer_Camera, DebugDistance);
        DistanceAboveTarget += DebugDistance;
    }
}

If "Something_UseFoo" is false then debug system won't be able to generate these two lines:
1
2
#define GlobalConstants_SomethingElse_UseBar 0
#define GlobalConstants_Renderer_Camera_DebugDistance 25.0f

This means code won't compile after "handmade_config.h" file is regenerated.

2) local "static" variables (local_persist) are NOT thread-safe in MSVC 2013. In C++ standard local static variables are thread safe only starting with C++11 compiler. This means if compiler supports C++11 it is required to generate thread-safe code for local static variables. Otherwise it can do whatever it wants. MSVC 2013 (and lower) are not C++11 compatible compiler, so it generates non thread-safe code. MSVC 2015 is C++11 compatible compiler - it generates thread-safe code. GCC recent versions always generate thread safe code.

Here's an example:
1
2
3
4
5
int f(int x)
{
    static int a = x;
    return a;
}


MSVC 2013 generates following assembly (x64):
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
?f@@YAHH@Z PROC						; f, COMDAT
	mov	eax, ecx
	mov	ecx, DWORD PTR ?$S1@?1??f@@YAHH@Z@4IA
	test	cl, 1
	jne	SHORT $LN4@f
	or	ecx, 1
	mov	DWORD PTR ?a@?1??f@@YAHH@Z@4HA, eax
	mov	DWORD PTR ?$S1@?1??f@@YAHH@Z@4IA, ecx
	ret	0
$LN4@f:
	mov	eax, DWORD PTR ?a@?1??f@@YAHH@Z@4HA
	ret	0


MSVC 2015 generates following assembly (x64):
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
?f@@YAHH@Z PROC						; f, COMDAT
	push	rbx
	sub	rsp, 32					; 00000020H
	mov	edx, DWORD PTR _tls_index
	mov	ebx, ecx
	mov	rax, QWORD PTR gs:88
	mov	ecx, OFFSET FLAT:_Init_thread_epoch
	mov	rax, QWORD PTR [rax+rdx*8]
	mov	edx, DWORD PTR [rcx+rax]
	cmp	DWORD PTR ?$TSS0@?1??f@@YAHH@Z@4HA, edx
	jle	SHORT $LN4@f
	lea	rcx, OFFSET FLAT:?$TSS0@?1??f@@YAHH@Z@4HA
	call	_Init_thread_header
	cmp	DWORD PTR ?$TSS0@?1??f@@YAHH@Z@4HA, -1
	jne	SHORT $LN4@f
	lea	rcx, OFFSET FLAT:?$TSS0@?1??f@@YAHH@Z@4HA
	mov	DWORD PTR ?a@?1??f@@YAHH@Z@4HA, ebx
	call	_Init_thread_footer
$LN4@f:
	mov	eax, DWORD PTR ?a@?1??f@@YAHH@Z@4HA
	add	rsp, 32					; 00000020H
	pop	rbx
	ret	0


gcc 5.2.0 generates following assembly (x64):
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
_Z1fi:
	pushq	%rbx
	subq	$32, %rsp
	cmpb	$0, _ZGVZ1fiE1a(%rip)
	movl	%ecx, %ebx
	je	.L7
.L5:
	movl	_ZZ1fiE1a(%rip), %eax
	addq	$32, %rsp
	popq	%rbx
	ret
.L7:
	leaq	_ZGVZ1fiE1a(%rip), %rcx
	call	__cxa_guard_acquire
	testl	%eax, %eax
	je	.L5
	leaq	_ZGVZ1fiE1a(%rip), %rcx
	movl	%ebx, _ZZ1fiE1a(%rip)
	call	__cxa_guard_release
	movl	%ebx, %eax
	addq	$32, %rsp
	popq	%rbx
	ret


As you can see both MSVC 2015 and gcc use some internal C runtime functions to guarantee that local static variable is initialized only once. MSVC 2013 doesn't.

You can actually make statics non thread-safe if you want - use "-fno-threadsafe-statics" for gcc and "/Zc:threadSafeInit" for MSVC 2015.

clang generates either gcc or msvc style of code - depeneding on what runtime you are targeting.

So on MSVC 2013 this can potentially lead to duplicate allocation of variables in debug system.
Thanks Martins! We'll take a look at these when we're done fussing with the debug collation...

- Casey