The generated machine code is going to vary by compiler, and even within the same compiler depending on compiler optimization settings. Sometimes optimizations will "inline" functions, removing what was a function call in the code. Sometimes external libraries will be compiled "statically" i.e. their functions get incorporated directly into the module vs being call externally (i.e. via a DLL in Windows)
I find a very good way to explore some of this behavior is with online tool https://godbolt.org/
For most accurate results related to your game you would need to work out which specific compiler was used i.e. Microsoft Visual C++ 6.0 and you can specify compiler options to output assembly to see what kind of code it can generate.
Taking a very simplistic example:
The following code:
#include <iostream>
int main()
{
printf("Hello, World!");
return 0;
}
We can see the output with x86 msvc v19 latest:
$SG34000 DB 'Hello, World!', 00H
std::_Fake_allocator const std::_Fake_alloc ORG $+1 ; std::_Fake_alloc
unsigned __int64 `__local_stdio_printf_options'::`2'::_OptionsStorage DQ 01H DUP (?) ; `__local_stdio_printf_options'::`2'::_OptionsStorage
_main PROC
push ebp
mov ebp, esp
push OFFSET $SG34000
call _printf
add esp, 4
xor eax, eax
pop ebp
ret 0
_main ENDP
But with optimize for speed:
unsigned __int64 `__local_stdio_printf_options'::`2'::_OptionsStorage DQ 01H DUP (?) ; `__local_stdio_printf_options'::`2'::_OptionsStorage
`string' DB 'Hello, World!', 00H ; `string'
_main PROC ; COMDAT
push OFFSET `string'
call _printf
add esp, 4
xor eax, eax
ret 0
_main ENDP
But if optimized for size:
unsigned __int64 `__local_stdio_printf_options'::`2'::_OptionsStorage DQ 01H DUP (?) ; `__local_stdio_printf_options'::`2'::_OptionsStorage
`string' DB 'Hello, World!', 00H ; `string'
_main PROC ; COMDAT
push OFFSET `string'
call _printf
pop ecx
xor eax, eax
ret 0
_main ENDP
Then look at GCC default output:
.LC0:
.string "Hello, World!"
main:
push rbp
mov rbp, rsp
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
mov eax, 0
pop rbp
ret
__static_initialization_and_destruction_0(int, int):
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], edi
mov DWORD PTR [rbp-8], esi
cmp DWORD PTR [rbp-4], 1
jne .L5
cmp DWORD PTR [rbp-8], 65535
jne .L5
mov edi, OFFSET FLAT:_ZStL8__ioinit
call std::ios_base::Init::Init() [complete object constructor]
mov edx, OFFSET FLAT:__dso_handle
mov esi, OFFSET FLAT:_ZStL8__ioinit
mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
call __cxa_atexit
.L5:
nop
leave
ret
_GLOBAL__sub_I_main:
push rbp
mov rbp, rsp
mov esi, 65535
mov edi, 1
call __static_initialization_and_destruction_0(int, int)
pop rbp
ret
With optimization -O3 gcc produces:
.LC0:
.string "Hello, World!"
main:
sub rsp, 8
mov edi, OFFSET FLAT:.LC0
xor eax, eax
call printf
xor eax, eax
add rsp, 8
ret
_GLOBAL__sub_I_main:
sub rsp, 8
mov edi, OFFSET FLAT:_ZStL8__ioinit
call std::ios_base::Init::Init() [complete object constructor]
mov edx, OFFSET FLAT:__dso_handle
mov esi, OFFSET FLAT:_ZStL8__ioinit
mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
add rsp, 8
jmp __cxa_atexit
Meanwhile clang produces:
__cxx_global_var_init: # @__cxx_global_var_init
push rbp
mov rbp, rsp
movabs rdi, offset std::__ioinit
call std::ios_base::Init::Init() [complete object constructor]
movabs rdi, offset std::ios_base::Init::~Init() [complete object destructor]
movabs rsi, offset std::__ioinit
movabs rdx, offset __dso_handle
call __cxa_atexit
pop rbp
ret
main: # @main
push rbp
mov rbp, rsp
sub rsp, 16
mov dword ptr [rbp - 4], 0
movabs rdi, offset .L.str
mov al, 0
call printf
xor eax, eax
add rsp, 16
pop rbp
ret
_GLOBAL__sub_I_example.cpp: # @_GLOBAL__sub_I_example.cpp
push rbp
mov rbp, rsp
call __cxx_global_var_init
pop rbp
ret
.L.str:
.asciz "Hello, World!"
Clang with optimizations:
main: # @main
push rax
mov edi, offset .L.str
xor eax, eax
call printf
xor eax, eax
pop rcx
ret
_GLOBAL__sub_I_example.cpp: # @_GLOBAL__sub_I_example.cpp
push rax
mov edi, offset std::__ioinit
call std::ios_base::Init::Init() [complete object constructor]
mov edi, offset std::ios_base::Init::~Init() [complete object destructor]
mov esi, offset std::__ioinit
mov edx, offset __dso_handle
pop rax
jmp __cxa_atexit # TAILCALL
.L.str:
.asciz "Hello, World!"
If we have a simple function called:
#include <iostream>
int addone(int n)
{
return n+1;
}
int main()
{
printf("%i",addone(1));
return 0;
}
Without optimization msvc produces:
$SG34003 DB '%i', 00H
std::_Fake_allocator const std::_Fake_alloc ORG $+1 ; std::_Fake_alloc
unsigned __int64 `__local_stdio_printf_options'::`2'::_OptionsStorage DQ 01H DUP (?) ; `__local_stdio_printf_options'::`2'::_OptionsStorage
_n$ = 8 ; size = 4
int addone(int) PROC ; addone
push ebp
mov ebp, esp
mov eax, DWORD PTR _n$[ebp]
add eax, 1
pop ebp
ret 0
int addone(int) ENDP ; addone
_main PROC
push ebp
mov ebp, esp
push 1
call int addone(int) ; addone
add esp, 4
push eax
push OFFSET $SG34003
call _printf
add esp, 8
xor eax, eax
pop ebp
ret 0
_main ENDP
but with optimizations the function "addone" is no longer called, and just the direct value "2" is used:
unsigned __int64 `__local_stdio_printf_options'::`2'::_OptionsStorage DQ 01H DUP (?) ; `__local_stdio_printf_options'::`2'::_OptionsStorage
`string' DB '%i', 00H ; `string'
_n$ = 8 ; size = 4
int addone(int) PROC ; addone, COMDAT
mov eax, DWORD PTR _n$[esp-4]
inc eax
ret 0
int addone(int) ENDP ; addone
_main PROC ; COMDAT
push 2
push OFFSET `string'
call _printf
add esp, 8
xor eax, eax
ret 0
_main ENDP