flat assembler
Message board for the users of flat assembler.

Index > Main > Please help me to understand stack

Goto page 1, 2  Next
Author
Thread Post new topic Reply to topic
FlierMate1



Joined: 31 May 2022
Posts: 118
FlierMate1 30 Jun 2022, 10:10
I thought stack grows upward, until recently I saw an article saying it grows downward. Today, this article says stack can grow ascendingly or descendingly depending on the architecture (or ABI?).
But I assume x86 and x64 stack grows downward (and heap grows upward) since I see many Assembly code subtract RSP to allocate local variables.

I did found a useful article explaining stack, but it is not enough.
From "Compiler Explorer" website, the code below:
Code:
int square(int num, int num2,int num3,bool w,int num4) {
    return num * num;
}
    

...is translated to...
Code:
square(int, int, int, bool, int):
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], edi
        mov     DWORD PTR [rbp-8], esi
        mov     DWORD PTR [rbp-12], edx
        mov     eax, ecx
        mov     DWORD PTR [rbp-20], r8d
        mov     BYTE PTR [rbp-16], al
        mov     eax, DWORD PTR [rbp-4]
        imul    eax, eax
        pop     rbp
        ret  
    

Although bool is 1 byte, the stack still allocate 4 byte for it, does that mean it need alignment? Why can't the 5th parameter be called "mov DWORD PTR [rbp - 17], r8d"?

Unlike the article I was reading, the code above didn't do this:
Code:
        push    rbp
        mov     rbp, rsp
        sub      rsp, 20   // added by me
    


How can the GCC compiler allocate local variable without subtracting the stack pointer?

And one more question, what is the difference for using stack frame, and using reserved bytes in data section (e.g Windows executable)?
I mean what if I store variables in data section, not stack?
Post 30 Jun 2022, 10:10
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 18940
Location: In your JS exploiting you and your system
revolution 30 Jun 2022, 10:30
There is no requirement for alignment in x86. You can allocate a single byte if you want to. But generally people align things for "performance".

But note that although the CPU doesn't require alignment, the OS, or some linked library you use, might need it. Again this is for "performance". Not needed, but simply dictated by the choices made by the designers of the code.

As for not allocating space by using "sub rsp,20", that is due to something called the "red zone" that some OSes guarantee not to use when delivering exceptions. Often it is 128 bytes. So you have at least 128 bytes below RSP that the OS promises not to overwrite. You would usually only use this in leaf functions where you don't call other functions from within it. Again, avoiding the use of "sub rsp,20" is for performance. You can use it if you want to.

Using the stack is usually more convenient than the data section. For re-entrant code, and multi-threaded code, the stack is going to be easier to use. Plus because it is most likely to already be in the cache it is often more performant.

A lot of these conventions about stack usage and alignment are driven by the desire for performance. So in many cases you can ignore them and code will still work. It might be "slower" or less efficient, but that may not matter for your case, so you don't need to chase performance everywhere if it means the code is easier to understand and debug, and maybe also faster to get working correctly without a bunch of confusing "performance" things cluttering the code.
Post 30 Jun 2022, 10:30
View user's profile Send private message Visit poster's website Reply with quote
FlierMate1



Joined: 31 May 2022
Posts: 118
FlierMate1 30 Jun 2022, 10:43
Thank you, @revolution, I learnt a lot from your explanation, especially I am new to "red zone" concept.
Post 30 Jun 2022, 10:43
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 18940
Location: In your JS exploiting you and your system
revolution 30 Jun 2022, 10:55
The square function above can be greatly simplified.
Code:
square:
  mov eax,edi
  imul eax,eax
  ret    
Post 30 Jun 2022, 10:55
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 18940
Location: In your JS exploiting you and your system
revolution 30 Jun 2022, 11:02
FlierMate1 wrote:
I am new to "red zone" concept.
It is an OS specific thing.

For DOS it is zero bytes.
Post 30 Jun 2022, 11:02
View user's profile Send private message Visit poster's website Reply with quote
FlierMate1



Joined: 31 May 2022
Posts: 118
FlierMate1 30 Jun 2022, 11:08
revolution wrote:
The square function above can be greatly simplified.
Code:
square:
  mov eax,edi
  imul eax,eax
  ret    


Optimization by hand surpasses the compiler auto-generated code. Very Happy But I am wondering whether compiler's optimized code will match your simplified code?
Post 30 Jun 2022, 11:08
View user's profile Send private message Reply with quote
DimonSoft



Joined: 03 Mar 2010
Posts: 1115
Location: Belarus
DimonSoft 30 Jun 2022, 14:21
FlierMate1 wrote:
I thought stack grows upward, until recently I saw an article saying it grows downward.

The thing I like most with stack and memory terminology is that the top of the stack tends to be closer to lower addresses, not upper memory.

Creates lots of headache when students start discovering third-party books on assembly where the two concepts are mixed (probably to make the books’ authors sound like if they were professionals), memory gets drawn with larger address values higher and stack starts growing down (picture-wise) with its top at the very bottom of the picture.
Post 30 Jun 2022, 14:21
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 18940
Location: In your JS exploiting you and your system
revolution 30 Jun 2022, 14:27
FlierMate1 wrote:
...I am wondering whether compiler's optimized code will match your simplified code?
Try it. What do you get?
Post 30 Jun 2022, 14:27
View user's profile Send private message Visit poster's website Reply with quote
FlierMate1



Joined: 31 May 2022
Posts: 118
FlierMate1 30 Jun 2022, 16:32
@DimonSoft, thanks for sharing your thoughts with me.

revolution wrote:
FlierMate1 wrote:
...I am wondering whether compiler's optimized code will match your simplified code?
Try it. What do you get?


Not successful. I tried all the compiler options for optimization on "Compiler Explorer" but none give clean and simplified code like yours.

You win! Human 1 vs Machine 0 Twisted Evil


Description:
Filesize: 52.66 KB
Viewed: 1858 Time(s)

op.png


Post 30 Jun 2022, 16:32
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 18940
Location: In your JS exploiting you and your system
revolution 30 Jun 2022, 17:43
FlierMate1 wrote:
Not successful. I tried all the compiler options for optimization on "Compiler Explorer" but none give clean and simplified code like yours.

You win! Human 1 vs Machine 0 Twisted Evil
That should be easy for a compiler to do. Did you try -O3 or -Os?
Post 30 Jun 2022, 17:43
View user's profile Send private message Visit poster's website Reply with quote
FlierMate1



Joined: 31 May 2022
Posts: 118
FlierMate1 30 Jun 2022, 17:53
revolution wrote:
That should be easy for a compiler to do. Did you try -O3 or -Os?


Didn't know about -O3 and -Os switches, it gives almost identical code:

Code:
square(int, int, int, bool, int):
        imul    edi, edi
        mov     eax, edi
        ret
    


Terrific.

---

I also tried C#, its square function is translated to:

Code:
Program:Square(int):int:
       mov      eax, edi
       imul     eax, edi
       ret  
    


Just a little bit register changes here and there. Wow, we have three different versions so far for the square function.
Post 30 Jun 2022, 17:53
View user's profile Send private message Reply with quote
macomics



Joined: 26 Jan 2021
Posts: 671
Location: Russia
macomics 30 Jun 2022, 18:07
Code:
#include <iostream>

int square(int num) {
    return num * num;
}

int main() {
    int sq;
    sq = square(5);
    std::cout << sq;
    return 0;
}    


Code:
square(int):
        imul    edi, edi
        mov     eax, edi
        ret
main:
        push    rax
        mov     esi, 25
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        xor     eax, eax
        pop     rdx
        ret
_GLOBAL__sub_I_square(int):
        push    rax
        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
        pop     rcx
        mov     edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
        jmp     __cxa_atexit    


Anyway, the compiler optimized badly. Why leave square if it is not called at all. He counted the result as 25, but left the square code for some reason.
And once he counted the constant, he could translate it into a string right away (once the constant gets to the output).
Post 30 Jun 2022, 18:07
View user's profile Send private message Reply with quote
FlierMate1



Joined: 31 May 2022
Posts: 118
FlierMate1 30 Jun 2022, 18:23
Yes, true. Good eyes, @macomics.

I have similar result with C# on .NET 6.0.101 compiler:

Code:
using System;

class Program
{
    static int Square(int num) => num * num;

    static void Main(string[] args){
        Console.WriteLine(Square(8));
    }
}    


Assembly output:
Code:
Program:.ctor():this:
       ret      

Program:Main(System.String[]):
       push     rax
       mov      edi, 64
       call     [System.Console:WriteLine(int)]
       nop      
       add      rsp, 8
       ret      

Program:Square(int):int:
       mov      eax, edi
       imul     eax, edi
       ret      
    


Looks like the compiler calculated the value during compile-time.
Post 30 Jun 2022, 18:23
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 18940
Location: In your JS exploiting you and your system
revolution 30 Jun 2022, 19:38
macomics wrote:
He counted the result as 25, but left the square code for some reason.
Since it create an object file it doesn't know if another file will be linked with it or not.

There is probably a switch to tell the compiler it is safe to elide unused code.
Post 30 Jun 2022, 19:38
View user's profile Send private message Visit poster's website Reply with quote
Furs



Joined: 04 Mar 2016
Posts: 1945
Furs 01 Jul 2022, 13:15
If you want it to look at all object files use LTO. (-flto)
Post 01 Jul 2022, 13:15
View user's profile Send private message Reply with quote
FlierMate1



Joined: 31 May 2022
Posts: 118
FlierMate1 09 Jul 2022, 12:35
I tested the same Square() function with Visual Basic on Compiler Explorer website, and I found a possibly typing error, should I report it?

What is "gword"?

https://godbolt.org/z/hcvsPsrKK

Code:
CompilerExplorer.Program:Main():
       push     rbp
       sub      rsp, 16
       lea      rbp, [rsp+10H]
       xor      eax, eax
       mov      qword ptr [rbp-10H], rax
       mov      edi, 8
       call     [CompilerExplorer.Program:Square(int):int]
       mov      dword ptr [rbp-04H], eax
       xor      edi, edi
       call     [CORINFO_HELP_READYTORUN_NEWARR_1]
       mov      gword ptr [rbp-10H], rax
       mov      edi, dword ptr [rbp-04H]
       mov      rsi, gword ptr [rbp-10H]
       call     [Microsoft.VisualBasic.FileSystem:Print(int,System.Object[])]
       nop      
       add      rsp, 16
       pop      rbp
       ret      

CompilerExplorer.Program:Square(int):int:
       push     rax
       mov      eax, edi
       imul     eax, edi
       jo       SHORT G_M28424_IG04
       add      rsp, 8
       ret      
G_M28424_IG04:
       call     [CORINFO_HELP_OVERFLOW]
       int3         


...from...

Code:
Module Program
    Function Square(num As Integer) As Integer
        Return num * num
    End Function

    sub Main()
        Print (Square(8))
    end sub
End Module    


Description: What is "gword"?
Filesize: 224.05 KB
Viewed: 1730 Time(s)

Screenshot 2022-07-09 203223.png


Post 09 Jul 2022, 12:35
View user's profile Send private message Reply with quote
Roman



Joined: 21 Apr 2012
Posts: 1279
Roman 09 Jul 2022, 12:36
QWORD
Code:
mov      qword [rbp-10H], rax ;ignore ptr 
mov      edi, dword  [rbp-04H]
mov      rsi, qword [rbp-10H]
    
Post 09 Jul 2022, 12:36
View user's profile Send private message Reply with quote
FlierMate1



Joined: 31 May 2022
Posts: 118
FlierMate1 09 Jul 2022, 12:39
Roman wrote:
QWORD
Code:
mov      qword [rbp-10H], rax ;ignore ptr 
mov      edi, dword  [rbp-04H]
mov      rsi, qword [rbp-10H]
    


Yes, you're right!
Post 09 Jul 2022, 12:39
View user's profile Send private message Reply with quote
FlierMate1



Joined: 31 May 2022
Posts: 118
FlierMate1 09 Jul 2022, 13:00
I reported it and there is a reply:

Quote:
dkm commented 18 seconds ago
If you're correct, then it's a bug in the compiler, not compiler explorer. There's nothing we can do about that...


Quote:
It's not a bug, they have several references to gword in their code and they have a couple of other weird ones https://github.com/dotnet/runtime/blob/11f24dd3b68da68dbbbd222dac98768f6893f30c/src/coreclr/jit/instr.cpp#L199
It seems to be a EA_GCREF, whatever that means


Code:
    unsigned size = EA_SIZE(attr);

    assert(size == 0 || size == 1 || size == 2 || size == 4 || size == 8 || size == 16 || size == 32);

    if (EA_ATTR(size) == attr)
    {
        return sizes[size];
    }
    else if (attr == EA_GCREF)
    {
        return "gword ptr ";
    }
    else if (attr == EA_BYREF)
    {
        return "bword ptr ";
    }
    else if (EA_IS_DSP_RELOC(attr))
    {
        return "rword ptr ";
    }
    else
    {
        assert(!"Unexpected");
        return "unknw ptr ";
    }
}    
Post 09 Jul 2022, 13:00
View user's profile Send private message Reply with quote
AsmGuru62



Joined: 28 Jan 2004
Posts: 1472
Location: Toronto, Canada
AsmGuru62 10 Jul 2022, 21:28
It is a Giga-Word! They finally invented the thing! Very Happy
Post 10 Jul 2022, 21:28
View user's profile Send private message Send e-mail Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  
Goto page 1, 2  Next

< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2020, Tomasz Grysztar. Also on GitHub, YouTube, Twitter.

Website powered by rwasa.