Announcement

Collapse
No announcement yet.

ASM: When to push and pop

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • ASM: When to push and pop

    Im confused about when I should be saving and restoring registers with inline asm.

    I have a Function_1 which changes the esi, edi, ecx and ebx registers with inline asm operations. In the middle of that function, I make a call to another Function_2 which also changes several of those same registers.

    I compiled two versions of the code, when where I push all the registers prior to making the Function_2 call and the caller pops the variables back on the return. This works. But so does the 2nd compiled version where I just comment out all the pushes and pops. That implies that im just saving extra copies on the stack and that somewhere along the line PB is pushing and popping those registers as well?

    So is the only time I want to push and pop is within the same procedure/function when i want to temporarily store a register for use later within that same procedure/function?

    Thanks
    -Mike

  • #2
    Just saw the following note in the archives. Item 2 seems to confirm that registers are automatically saved and restored by PB? Is this a PB feature or is this how all inline asm works?

    -----------
    A few notes about threads...
    1- Globals are used to share data between threads.

    2- Register variables are always automatically thread-safe.

    3- You may call a PowerBASIC function from Visual Basic to initiate a new thread as many times as needed. It is always thread-safe.

    4- You may call a PowerBASIC function from PowerBASIC to initiate a new thread as many times as needed. It is always thread-safe.

    5- You may never, ever call back a Visual Basic function from a PowerBASIC thread. You must perform all thread functionality from PowerBASIC code only, because Visual Basic is not thread-safe. To do otherwise is to guarantee failure.

    Regards,

    Bob Zale
    ---------


    PowerBASIC Inc.


    ------------------

    Comment


    • #3
      Register Variables are always thread-safe because the O/S saves & restores the registers when it does a context switch (read: process or thread switch).

      Have you reviewed the Inline Assembly chapter in the help file for information on when and how to save the registers? Remember, Register Variables only occupy some of the registers available to inline assembly code.

      Of course, if you are not certain what the compiler is doing, you can always disassemble the EXE and check out the generated code

      ------------------
      Lance
      PowerBASIC Support
      mailto:[email protected][email protected]</A>
      Lance
      mailto:[email protected]

      Comment


      • #4
        Thanks for the clarification on the threading issue.

        I just re-read the help. It recommends preserving the required registers prior to any asm instructions and restoring them afterward. Pretty much a "better safe than sorry" approach.

        Still, im curious why in the following code for instance, its not necessary to push and pop the variables when making a call to a second function which changes those registers.

        Code:
        #COMPILE EXE
        #DIM ALL
        #REGISTER NONE
        
        FUNCTION PBMAIN()
           CALL First
        END FUNCTION
        
        SUB FIRST()
           LOCAL y AS LONG
        
           !mov ebx, 1      ; the following registers require preserving ??
           !mov esi, 2
           !mov edi, 3
        
           CALL Second      ' note we have not preserved ebx, esi, and edi registers
        
           !mov eax, ebx     ; add up our 3 registers
           !add eax, esi
           !add eax, edi
           !mov y, eax
        
           MSGBOX "Y = " & STR$(y) & "  Expected 15" ' should total 15 since we didnt preserve our registers but instead it returns 6
        END SUB
        
        SUB SECOND()
            !mov ebx, 4      ; put new values in ebx, esi and edi
            !mov esi, 5
            !mov edi, 6
            'If we were to add them up, they total 15
            'We have not preserved or restored these registers here either
        END SUB
        Hmm... i probably just need to learn how to use asm better before i start asking what is probably a bunch of trivial questions to most of you



        ------------------


        [This message has been edited by Mike Joseph (edited April 04, 2000).]

        Comment


        • #5
          Unfortunately, I not a "guru" with assembly... there are many assembly programmers on this BBS that are better than I will ever be!

          My understanding is that PB saves and restores the registers around calls to subs and functions to ensure that your code does not crash due to unexpected register changes - your code sems to confirm this approach.

          In addition, certain registers may be used to retrieve a function return value and store it appropriately - if the registers used in this operation were not restored after the function code returns, how could you guarantee that assembly code that surrounds the call would work as expected?

          The "safe" approach suggested in the help file is there for a reason - if you fail to preserve and restore registers you open up the possibility of unpredictable behavior in your app code.

          How likely this possibility is depends on the code as the compiler does a pretty good job for you, but unless you do it yourself, you may not be able to "guarantee" success in all circumstances.



          ------------------
          Lance
          PowerBASIC Support
          mailto:[email protected][email protected]</A>
          Lance
          mailto:[email protected]

          Comment


          • #6
            Code:
            Mike--
               
            As Lance says:
             
            My understanding is that PB saves and restores the registers around calls to subs and functions to ensure that your code does not crash due to unexpected register changes - your code seems to confirm this approach.
            Actually the saving occurs (in this case, anyway) within the called sub or function. Here's the disassembly of your SUB SECOND: :0040111D 55 push ebp :0040111E 8BEC mov ebp, esp :00401120 53 push ebx ; <- * :00401121 56 push esi ; <- * :00401122 57 push edi ; <- * :00401123 83EC5C sub esp, 0000005C :00401126 6A00 push 00000000 :00401128 6A00 push 00000000 :0040112A 6A00 push 00000000 :0040112C BB04000000 mov ebx, 00000004 :00401131 BE05000000 mov esi, 00000005 :00401136 BF06000000 mov edi, 00000006 :0040113B 8D65F4 lea esp, dword ptr [ebp-0C] :0040113E 5F pop edi ; <- * :0040113F 5E pop esi ; <- * :00401140 5B pop ebx ; <- * :00401141 5D pop ebp :00401142 C3 ret The pushing and popping at * constitutes the automatic saving of registers that Lance describes. Aside from standard stack frame construction and retrieval of the return address ("lea esp, dword ptr [ebp-0C]"), I don't have an explanation for the some of the other stack manipulation. That "sub esp, 0000005C" turns up regularly in many PB-compiled routines, as does the pushing of constant zero dwords or registers that have been cleared to zero.

            ------------------
            -- Greg
            [email protected]

            Comment


            • #7
              Mike,

              The normal convention is 32 bit windows is for a procedure (SUB or FUNCTION)
              to preserve the EBX ESI and EDI registers. This means you can routinely
              use the EAX ECX & EDX registers within your SUB or FUNCTION without and
              special conditions but if you call another SUB or FUNCTION, either your own
              or an intrinsic compiler on, u need to preserve any of the registers that
              your own code is using if they are EAX ECX or EDX. This is because any other
              SUB or FUNCTION can use them freely as well which will destroy the values
              you have in them.

              In practical terms, if you are using ECX as a counter in a loop that calls
              another SUB or FUNCTION, you push the ECX register before you call it and
              pop it after.

              Now if you were running a loop that used eax for transient values per
              iteration, ecx as the loop counter and edx for adding a result to, then
              the code to do the exernal SUB or FUNCTION would be something like as
              follows,

              -
              -
              ! push ecx
              ! push edx
              rv = MyFunction(var,var etc ... )
              ! pop edx
              ! pop ecx
              -
              -

              This means you can routinely use high level functions in you inline asembler
              as long as you take care of the registers that you use within your own code.

              Regards,

              [email protected]

              ------------------
              hutch at movsd dot com
              The MASM Forum

              www.masm32.com

              Comment

              Working...
              X