Hello,
I've noticed that whenever you write a program in Visual Studio that uses structs (or classes), the assembly code generated doesn't contain a single MASM STRUCTURE, even though it would perfectly fine. Instead, VC++ does something I cannot quite understand, but would love an explanation of. But my question is this: why doesn't it use assembly STRUCTs? Is it some kind of performance or program size matter? I doubt it's the latter, since both my code and my exe size were a lot smaller.
Let me give you an example. This is the C++ program we're asking VC++ to compile:
#include <cstdio>
#include <cmath>
struct MyType
{
int age;
};
const char* sz = "I am %d years old\n";
int main()
{
MyType obj;
obj.age = 30;
printf(sz, obj.age);
}
And this is the assembly code it generates for us. Notice that it doesn't use something like MyType STRUCT anywhere; instead it simply does this, which makes no sense to me at all:
_TEXT SEGMENT
_obj$ = -4
... (ca. 13 lines later)
mov DWORD PTR _obj$[ebp], 30
This is the complete code generated by Visual Studio:
TITLE C:\Users\Thomas\source\C#\MultiCompiler2\bin\Debug\program1.cpp
.686P
.XMM
include listing.inc
.model flat
INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES
PUBLIC ?sz@@3PBDB
CONST SEGMENT
$SG17424 DB 'I am %d years old', 0aH, 00H
CONST ENDS
_DATA SEGMENT
?sz@@3PBDB DD FLAT:$SG17424
_DATA ENDS
PUBLIC ___local_stdio_printf_options
PUBLIC __vfprintf_l
PUBLIC _printf
PUBLIC _main
PUBLIC ?_OptionsStorage@?1??__local_stdio_printf_options@@9@4_KA
EXTRN ___acrt_iob_func:PROC
EXTRN ___stdio_common_vfprintf:PROC
_BSS SEGMENT
?_OptionsStorage@?1??__local_stdio_printf_options@@9@4_KA DQ 01H DUP (?)
_BSS ENDS
_TEXT SEGMENT
_obj$ = -4
_main PROC
00000 55 push ebp
00001 8b ec mov ebp, esp
00003 51 push ecx
00004 c7 45 fc 1e 00
00 00 mov DWORD PTR _obj$[ebp], 30
0000b 8b 45 fc mov eax, DWORD PTR _obj$[ebp]
0000e 50 push eax
0000f 8b 0d 00 00 00
00 mov ecx, DWORD PTR ?sz@@3PBDB
00015 51 push ecx
00016 e8 00 00 00 00 call _printf
0001b 83 c4 08 add esp, 8
0001e 33 c0 xor eax, eax
00020 8b e5 mov esp, ebp
00022 5d pop ebp
00023 c3 ret 0
_main ENDP
_TEXT ENDS
_TEXT SEGMENT
__Result$ = -16
tv73 = -12
tv75 = -8
__ArgList$ = -4
__Format$ = 8
_printf PROC
00000 55 push ebp
00001 8b ec mov ebp, esp
00003 83 ec 10 sub esp, 16
00006 8d 45 0c lea eax, DWORD PTR __Format$[ebp+4]
00009 89 45 fc mov DWORD PTR __ArgList$[ebp], eax
0000c 8b 4d 08 mov ecx, DWORD PTR __Format$[ebp]
0000f 89 4d f8 mov DWORD PTR tv75[ebp], ecx
00012 6a 01 push 1
00014 e8 00 00 00 00 call ___acrt_iob_func
00019 83 c4 04 add esp, 4
0001c 89 45 f4 mov DWORD PTR tv73[ebp], eax
0001f 8b 55 fc mov edx, DWORD PTR __ArgList$[ebp]
00022 52 push edx
00023 6a 00 push 0
00025 8b 45 f8 mov eax, DWORD PTR tv75[ebp]
00028 50 push eax
00029 8b 4d f4 mov ecx, DWORD PTR tv73[ebp]
0002c 51 push ecx
0002d e8 00 00 00 00 call __vfprintf_l
00032 83 c4 10 add esp, 16
00035 89 45 f0 mov DWORD PTR __Result$[ebp], eax
00038 c7 45 fc 00 00
00 00 mov DWORD PTR __ArgList$[ebp], 0
0003f 8b 45 f0 mov eax, DWORD PTR __Result$[ebp]
00042 8b e5 mov esp, ebp
00044 5d pop ebp
00045 c3 ret 0
_printf ENDP
_TEXT ENDS
_TEXT SEGMENT
__Stream$ = 8
__Format$ = 12
__Locale$ = 16
__ArgList$ = 20
__vfprintf_l PROC
00000 55 push ebp
00001 8b ec mov ebp, esp
00003 8b 45 14 mov eax, DWORD PTR __ArgList$[ebp]
00006 50 push eax
00007 8b 4d 10 mov ecx, DWORD PTR __Locale$[ebp]
0000a 51 push ecx
0000b 8b 55 0c mov edx, DWORD PTR __Format$[ebp]
0000e 52 push edx
0000f 8b 45 08 mov eax, DWORD PTR __Stream$[ebp]
00012 50 push eax
00013 e8 00 00 00 00 call ___local_stdio_printf_options
00018 8b 48 04 mov ecx, DWORD PTR [eax+4]
0001b 51 push ecx
0001c 8b 10 mov edx, DWORD PTR [eax]
0001e 52 push edx
0001f e8 00 00 00 00 call ___stdio_common_vfprintf ; bytes=5
00024 83 c4 18 add esp, 24
00027 5d pop ebp
00028 c3 ret 0
__vfprintf_l ENDP
_TEXT ENDS
_TEXT SEGMENT
___local_stdio_printf_options PROC
00000 55 push ebp
00001 8b ec mov ebp, esp
00003 b8 00 00 00 00 mov eax, OFFSET ?_OptionsStorage@?1??__local_stdio_printf_options@@9@4_KA
00008 5d pop ebp
00009 c3 ret 0
___local_stdio_printf_options ENDP
_TEXT ENDS
END
That assembly code became a 99,5 KB (101.888 bytes) executable.
Now, when I compile my own assembly code, which does exactly the same if we're focusing on what the C++ code does, I get a 1,5 KB executable:
include \masm32\include\masm32rt.inc
MyType STRUCT
age DWORD 0
MyType ENDS
.data
sz db "I am %d years old", 10, 0
.code
start:
call main
inkey
exit
main proc
LOCAL obj: MyType
mov obj.age, 30
cls
push obj.age
push OFFSET sz
call crt_printf
ret
main endp
end start
How can it accomplish the same by doing _obj$ = -4? Is it because I only use one object and it knows my program isn't truly object oriented?
I'd love to know!
Thank you in advance.
What I have tried:
I've tried figuring out how it works. If you're making an object oriented language that translates to MASM, wouldn't it be a good idea to instead create a STRUCT for all structs/classes in the original language? That way you could put methods (since functions cannot be in STRUCTS in MASM) in the global scope and give them an extra "this" parameter that would be invisible in the original language, which would be of e.g. type MyType. That's the kind of logic I've tried to find in the asm code, but the way VC++ does it doesn't even come close AFAIK.