home bbs files messages ]

Forums before death by AOL, social media and spammers... "We can't have nice things"

   comp.lang.asm.x86      Ahh, the lost art of x86 assembly      4,675 messages   

[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]

   Message 3,768 of 4,675   
   James Harris to Rod Pemberton   
   Re: Locals, parameters, callee-save regi   
   07 Jan 19 10:44:10   
   
   From: james.harris.1@nospicedham.gmail.com   
      
   On 07/01/2019 07:55, Rod Pemberton wrote:   
   > On Sun, 6 Jan 2019 16:59:38 +0000   
   > James Harris  wrote:   
   >   
   >> That said, what I am writing is a hand translation of some HLL code   
   >> I've written, and all of this is sacrificial.   
   >   
   > If it's sacrificial code, why bother with finding the "best way"?   
      
   Partly because learnings from this experience can feed into other   
   projects. But also that pushing callee-saves before EBP rather than   
   after seemed so sensible I wondered if I was missing something.   
      
   >   
   > JH> My query, though, is over where and how it is best to add the   
   > JH> preservation of callee-save registers   
   >   
   > It would seem that the code only needs to work.  It shouldn't have any   
   > need to be optimized.  Should it?  Why optimize something you're   
   > intending to throw away?  Hence, I think your first working solution   
   > would've been sufficient.   
      
   Yes, this is not about optimisation for CPU time but about convenience -   
   optimisation for programmer time, if you like - mine!   
      
   >   
   > So, I'm clearly missing the point as to why this endeavor mattered at   
   > all now ...  Was this just an intellectual exercise?  Or, was this   
   > preparation for the future? etc.   
      
   OK. At the risk of boring you I can explain that. You may remember   
   suggesting to me a short while ago that I consider Nasm macros -   
   something I had generally avoided. Well, I took your advice and wrote   
   some macros to help with this project. You'll get the idea of what three   
   of the macros do from this example of how they might be used:   
      
      proc p, parm1, parm2, parm3   
        invoke q, arg1, arg2   
      endproc   
      
   Those three uses define a procedure, p, with three parameters, then   
   invoke another procedure, q, with two parameters, then end procedure p.   
      
   Since the performance of the generated code did not matter I wrote the   
   macros to be convenient rather than to generate fast code.   
      
   The proc macro set _._procname to the procedure name (p, in the example)   
   and began the generated code with the conventional   
      
        push ebp   
        mov ebp, esp   
      
   In endproc the procedure end code included   
      
      _._procname %+ ._finish   
        mov esp, ebp   
        pop ebp   
        ret   
      
   The finish label was intended for return statements, such that (and   
   remember that I am hand-translating HLL code) a statement like   
      
      return 1   
      
   could be effected with code which included   
      
      mov eax, 1   
      jmp _._procname %+ ._finish   
      
   I initially handled exceptions in a separate macro but eventually   
   decided it would be more convenient, less prone to error and easier to   
   work with to move exception handling into the invoke macro. To show how   
   that might work out in practice, here's code for procedure call with two   
   parameters.   
      
      push dword [parm1]   
      push dword [parm2]   
      call q   
      lea esp, [esp + 8]   
      cmp dword [___exception], 0   
      jne _._procname %+ ._finish   
      test eax, eax ;Set N and Z based on the return code   
      
   The caller pushes the args, calls the callee, removes the args it has   
   pushed (an approach which works whether the callee expects a fixed or a   
   variable number of arguments) then makes a sharp exit from the procedure   
   if an exception had occurred.   
      
   That's the background needed to answer your query and explain why I   
   asked the question in this thread - i.e. the problem with the above code   
   sequences is that, especially with exception handling, there's no point   
   at which callee-saves are restored. The issue, then, was how to ensure   
   they would be. The best answer seemed to be to push callee-saves before   
   EBP or, in fact, to treat EBP as just one of a number of callee saves.   
      
   Therefore procedures now begin with executable code such as   
      
   p:   
      push edi   
      push esi   
      push edx   
      push ecx   
      push ebx   
      push ebp   
      mov ebp, esp   
      
   and they end with   
      
   p._finish:   
      mov esp, ebp   
      pop ebp   
      pop ebx   
      pop ecx   
      pop edx   
      pop esi   
      pop edi   
      ret   
      
   The label p._finish can be jumped to from anywhere in the procedure,   
   including return statements and wherever exceptions are detected.   
      
   Usefully, a procedure can now include code which messes with ESP around   
   another procedure invocation such as   
      
      sub esp, 40   
      ...   
      mov eax, 1   
      jmp ._finish   
      
   or   
      
      push eax ;Save it across this call   
      invoke q, eax   
      pop eax   
      
   The epilogue code which restores ESP and then the callee-saves means   
   that arbitrary extra uses of the stack can be made in the procedure   
   without affecting its ability to return to its caller correctly.   
      
      
   --   
   James Harris   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   

[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]


(c) 1994,  bbs@darkrealms.ca