Page 1 of 2

how to push local variables onto the stack?

Posted: Fri Jul 31, 2015 5:52 pm
by Keya
I just found out that we cant push local variables :(
The helpfile says:
Local variables in PureBasic are directly indexed by the stack pointer, which means if the stack pointer change via an ASM instruction (like PUSH, POP etc..) the variable index will be wrong and direct variable reference won't work anymore.
But it doesn't give any suggestions or workarounds

I hope you won't tell me i have to use a debugger and manually fiddle around with "p.v_var+4", "p.v_var+8" etc trying to get the stack alignment correct as that is a really ugly laborious slow and errorprone way, and i thought that is job for the compiler not programmer so i hope Purebasic has a practical solution :). Or if we cant use local variables should I simply be using a different type, like Global, or...?
Thankyou for any tips

Code: Select all

Procedure Test()
  Protected xvar1.l = $11
  Protected xvar2.l = $22
  Protected xvar3.l = $33  
  PrintN("xvar1=" + Hex(xvar1))  ;$11
  PrintN("xvar2=" + Hex(xvar2))  ;$22
  PrintN("xvar3=" + Hex(xvar3))  ;$33
  EnableASM
  ! int 3
  ! push dword [p.v_xvar1]  ;$11
  ! push dword [p.v_xvar2]  ;should be $22 but is $11
  ! push dword [p.v_xvar3]  ;should be $33 but is $11
  DisableASM
EndProcedure
 
OpenConsole()
Test()

Re: how to push local variables onto the stack?

Posted: Fri Jul 31, 2015 6:08 pm
by wilbert
You can usually work around it by moving values first to registers and push those or use mov to put values on the stack.
In this case I have no idea what to advice since I have no idea what you are trying to do. Pushing three variables without correcting the stack pointer seems like asking for problems to me.

Re: how to push local variables onto the stack?

Posted: Fri Jul 31, 2015 6:27 pm
by Keya
for example to push parameters before calling the address of an API function directly

Re: how to push local variables onto the stack?

Posted: Fri Jul 31, 2015 7:05 pm
by wilbert
Two possible workarounds.

Code: Select all

Procedure.i calloc(num, size)
  !extrn _calloc
  !mov eax, [p.v_size]
  !mov ecx, [p.v_num]
  !push eax
  !push ecx
  !call _calloc
  !add esp, 8
  ProcedureReturn
EndProcedure

Debug calloc(4, 50)

Code: Select all

Procedure.i calloc(num, size)
  !extrn _calloc
  
  !mov eax, [p.v_size]
  !mov [esp - 4], eax
  
  !mov eax, [p.v_num]
  !mov [esp - 8], eax
  
  !sub esp, 8
  !call _calloc
  !add esp, 8
  
  ProcedureReturn
EndProcedure

Debug calloc(4, 50)

Re: how to push local variables onto the stack?

Posted: Fri Jul 31, 2015 7:09 pm
by Keya
Excellent, thankyou very much! :)
It seems I can also use the Prototype function for this, but i like to also know how to do it properly myself!

Re: how to push local variables onto the stack?

Posted: Sun Aug 02, 2015 9:14 am
by Thorium
You just need to add the right value to the variables stack index.
On x86 that would be 4 byte per push and on x64 8 byte per push.

So something like this:

Code: Select all

Procedure Test()
  
  Protected TestVar1
  Protected TestVar2
  
  TestVar1 = 1
  TestVar2 = 2
  
  !push qword [p.v_TestVar1]
  !push qword [p.v_TestVar2+8]
  
  
  !pop qword [p.v_TestVar2+8]
  !pop qword [p.v_TestVar1]
  
  
  Debug TestVar1
  Debug TestVar2
    
EndProcedure

Test()
If you push something to the stack, the stack pointer changes and you need to correct local variable accesses like that.

Re: how to push local variables onto the stack?

Posted: Sun Aug 02, 2015 9:45 am
by Keya
the PB compiler should do be doing those adjustments for us before it sends to FASM. How it is at the moment is very restrictive as you have to keep track of every variable in its position on the stack, and then never change the code, or if you do you have to restart and debug it all again, yucky!

Re: how to push local variables onto the stack?

Posted: Wed Sep 23, 2015 9:00 am
by DoubleDutch
Can't you just copy the stack pointer to another register, then use that as the index to the local variables?

You will be able to push/pop from the stack as much as you like then (as long as the position is back to normal before the return).

Re: how to push local variables onto the stack?

Posted: Wed Sep 23, 2015 4:05 pm
by Tenaja
Keya wrote:the PB compiler should do be doing those adjustments for us before it sends to FASM. How it is at the moment is very restrictive as you have to keep track of every variable in its position on the stack, and then never change the code, or if you do you have to restart and debug it all again, yucky!
The compiler does not "process" or "evaluate" your hand-written asm. It only outputs it directly.

PB uses [the register typically used as a frame pointer] for general use, to increase his number of registers available. This gives him another reg for optimizing his PB-generated code. I believe he investigated using it a few years back (IIRC, when looking into gosub/return within procs), and decided it was too much of an overhaul to implement.
Can't you just copy the stack pointer to another register, then use that as the index to the local variables?

You will be able to push/pop from the stack as much as you like then (as long as the position is back to normal before the return).
That sounds like a great alternative, if you need more space than Wilbert's method allows, with limited registers.

Re: how to push local variables onto the stack?

Posted: Wed Sep 23, 2015 4:35 pm
by wilbert
I believe it won't work.
Local variables are defined relative to the esp/rsp register.
For example

Code: Select all

%define p.v_test esp+40
So if you change the stack register, you can't use the defined p.v_test anymore unless you compensate for it like Thorium suggested.
You can of course copy the stack register value to another register but you don't know what the relative offset should be.

Re: how to push local variables onto the stack?

Posted: Sat May 06, 2017 2:49 pm
by Tristano
Interesting thread...

May I ask why the use of "Protected" keyword?

My understanding is that this keyword is needed to create a local (ie: local to the procedure) variable having the same name as an already existing global var. But I see it often used in procedure using ASM, so I was wondering if there are some added benefits from using Protected" when no naming conflict with a global variable is present. Are there?

Re: how to push local variables onto the stack?

Posted: Sat May 06, 2017 8:43 pm
by Tenaja
Tristano wrote:Interesting thread...

May I ask why the use of "Protected" keyword?

My understanding is that this keyword is needed to create a local (ie: local to the procedure) variable having the same name as an already existing global var. But I see it often used in procedure using ASM, so I was wondering if there are some added benefits from using Protected" when no naming conflict with a global variable is present. Are there?
It is good practice to always use protected (local) variables within procedures, because you never know when you might reuse the code in another program that might have a conflicting global var name. Or, you might improve your code...etc.

Re: how to push local variables onto the stack?

Posted: Sat May 06, 2017 8:48 pm
by Fig
I am not sure to fully understand, forgive me if i am wrong.

Using pop and push is equivalent to increment/decrement (+/-4/8) Esp and Mov from [Esp+/-4/8] to register.
You can assume stack memory is in L1 cache (your local variables are part of the stack already), instead of pushing and poping, you would better Mov again you local variable in your register after you call function you need.

So, instead of

Code: Select all

Protected xvar1.l = $11
Mov Eax,DWORD [p.v_xvar1]
...
Push Eax
...
Pop Eax
You can do

Code: Select all

Protected xvar1.l = $11
Mov Eax,DWORD [p.v_xvar1]
...
Mov DWORD [p.v_xvar1],Eax
...
Mov Eax,DWORD [p.v_xvar1]
So you can create local variables and use them to store your registers instead of using push/pop, use Mov.

It will be as fast (or close enough) and safer.
Image

Re: how to push local variables onto the stack?

Posted: Mon May 08, 2017 9:06 am
by Tristano
Forgive my question, but I'm still learning FASM:
wilbert wrote:I believe it won't work.
Local variables are defined relative to the esp/rsp register.
For example

Code: Select all

%define p.v_test esp+40
... but you don't know what the relative offset should be.
... so local variables are macros, not labels?

This means that at each occurence of the (above) variable p.v_test its value is actually replaced by esp+40, thus making its value always realtive?

Re: how to push local variables onto the stack?

Posted: Mon May 08, 2017 9:19 am
by Fig
Yep, that's why using "push" (or changing Esp in any way) messes with local variables.