flat assembler
Message board for the users of flat assembler.
Index
> Main > Use many function parameters Goto page 1, 2 Next |
Author |
|
DimonSoft 01 May 2018, 21:01
Mino wrote: Good evening. Code: proc foo c\ a, b, c, d, e, f, g, h, i, j, k, l ... endp |
|||
01 May 2018, 21:01 |
|
vivik 02 May 2018, 08:05
When you don't have enough registers, you use stack. Push pop.
|
|||
02 May 2018, 08:05 |
|
revolution 02 May 2018, 09:19
vivik wrote: When you don't have enough registers, you use stack. Push pop. |
|||
02 May 2018, 09:19 |
|
vivik 02 May 2018, 11:56
Assembly coders have more freedom over what calling convention to use. C have only a small set of them. It's not like it matters that much, who's gonna notice.
Library functions have calling conventions set in stone during compilation, but you can use whatever you want in your code. As long as caller and callee agree on what calling convention to use. |
|||
02 May 2018, 11:56 |
|
Mino 02 May 2018, 18:19
Ha, okay, I get it
But could you elaborate a little on the "proc", to see to what extent it is possible to deploy this method. Thanks |
|||
02 May 2018, 18:19 |
|
Furs 02 May 2018, 20:01
Mino wrote: Ha, okay, I get it The stack is a memory region that grows down (so when you push something, the stack pointer gets decremented and puts the value at that new location). The stack pointer is the esp register (rsp in x64). For example: Code: ; calling function with all parameters on the stack ; note parameters pushed in reverse order since stack grows down push l push k push j push i push h push g push f push e push d push c push b push a call foo ; in foo, access parameters from esp if you want (each is 4 bytes here) ; the location at esp+0 (i.e. [esp]) is the return ADDRESS of the function ; [esp+4] skips that and accesses a, [esp+8] accesses b, and so on foo: mov eax, [esp+4*3] ; accesses c, 3rd parameter, puts it in eax ; at end of function, you should "clean" all the parameters (restore stack pointer above them) so that the caller doesn't have to (this is stdcall) ret 4*12 ; because you have 12 parameters, this simply returns and then ADDS 4*12 to esp (moves it up, which is "back") I figured this is important since you said you want to write a (toy?) compiler You can put some parameters in registers and use your own custom calling convention if you want, but make sure the function and caller agree with each other (e.g. if you put first parameter into eax, then you'd better assume eax is first parameter inside the function also). |
|||
02 May 2018, 20:01 |
|
DimonSoft 02 May 2018, 22:08
Furs wrote:
A little correction here. It is generally not practical to use ESP-based addressing within a stack frame when writing the code manually or by means of a simple code generator within a compiler. Any push/pop changes the value of ESP, so in order to access the right place one will have to recalculate the constant offset for every place such access is taken. Which makes it nearly impossible to combine distinct branches of a conditional statement (in HLL terms) having common parts in them into a single piece of code. In most cases EBP-based addressing is the way to go. Especially if you’re eager to use SEH or just have easily traceable stack. |
|||
02 May 2018, 22:08 |
|
bitRAKE 03 May 2018, 08:13
Another option that almost no one uses is to pass a single pointer to an array of arguments. I like the ENTER instruction because it also can copy part of the previous stack frame - almost no one uses that anymore either.
_________________ ¯\(°_o)/¯ “languages are not safe - uses can be” Bjarne Stroustrup Last edited by bitRAKE on 03 May 2018, 09:20; edited 1 time in total |
|||
03 May 2018, 08:13 |
|
Tomasz Grysztar 03 May 2018, 08:32
bitRAKE wrote: Another option that almost no one uses is to pass a single pointer to an array of arguments. I like the ENTER instruction because it also can copy part of the previous stack frame - almost no one uses that anymore either. |
|||
03 May 2018, 08:32 |
|
revolution 03 May 2018, 09:04
Tomasz Grysztar wrote: This led to a perception of ENTER as bloated ... |
|||
03 May 2018, 09:04 |
|
vivik 03 May 2018, 12:21
What exactly ENTER X,1 does?
|
|||
03 May 2018, 12:21 |
|
bitRAKE 03 May 2018, 15:01
Code: MACRO ENTER num,k LOCAL level,temp level = k MOD 32 push rbp mov [temp],rsp ; temp is internal to the CPU REPEAT level - 1 ; skip for K = 0,1 lea rbp,[rbp-8] ; don't effect flags push qword [rbp] END REPEAT IF level > 0 push [temp] END IF mov rbp,[temp] sub rsp,num END MACRO _________________ ¯\(°_o)/¯ “languages are not safe - uses can be” Bjarne Stroustrup |
|||
03 May 2018, 15:01 |
|
Mino 03 May 2018, 15:26
Thank you very much for your answers I think I can find solutions adapted to my needs with all this
Quote:
Yes, indeed, it already gives me a wider range of possibilities than I expected! PS: The compiler will not be "toy", at least I hope so The language I have created is quite complex to compile in fact... But one thing at a time :p _________________ The best way to predict the future is to invent it. |
|||
03 May 2018, 15:26 |
|
Furs 03 May 2018, 20:03
bitRAKE wrote:
Seems kinda wasteful just to be able to access the frame of another function -- but it's not like compilers are any better, even with the "nested functions" extension, GCC does similar wasteful shit, since the compiler developers are too lazy to put some nested function optimizations for 10 years or more. "Compilers can optimize better than humans" is preached on stackoverflow, yeah right, maybe in theory with actual non-lazy compiler devs. I use ENTER x,0 when optimizing for size though (if it's better). |
|||
03 May 2018, 20:03 |
|
bitRAKE 04 May 2018, 03:41
People are afraid to use RSP in a general manner, but on modern OS's there is no such requirement within one's own code. Of course, external code has requirements.
For example, on Win64 we might: Code: ; stack aligned MOD 16 lea rbp,[_APIs] ; no alignment required on this buffer enter 0,4*3 pop rbp pop rcx rdx r8 r9 retn dq 0 ; hWnd dq lpText dq lpCaption dq 0 ; uType dq _jmp__MessageBoxW dq _continue rq 5 _APIs: lpText du "(K)not in a tree.",0 lpCaption du "What?",0 _jmp__MessageBoxW: jmp [user32.MessageBoxW] _continue: leave ; not needed we know where RBP/RSP is It wouldn't be surprising if you were to wonder how this is smaller. Two factors make it smaller: same code for multiple API calls, and the separation of code and data makes it compress better. 32-bit Windows, that data is on the stack - just POP EBP and RETN. API thunk is just used for illustrative purposes. Changes made to the API buffer prior to the ENTER instruction are persistent across calls. Whereas those afterward are discarded on stack cleanup (LEAVE). |
|||
04 May 2018, 03:41 |
|
Tomasz Grysztar 04 May 2018, 10:10
Nice trick, I never really thought of possibility to use ENTER just to move an arbitrarily sized block of data onto the stack. And now I realized it could actually be a nice method of creating initialized local variables. With fasm[g]'s "proc" macros if you define a local variable as having initial value, an instruction like MOV [EBP-X],Y is generated to ensure that it works as one might expect. The alternative could be to put the initial values into data segment and then use ENTER to copy the values to the stack. Interesting.
|
|||
04 May 2018, 10:10 |
|
rugxulo 05 May 2018, 00:54
Tomasz Grysztar wrote:
It's really only used for languages with nested functions (e.g. Pascal, Modula-2, Oberon). C-based languages usually don't support that (or only as non-standard extension). So it's less useful for them. And yes, it is slower, quite noticeably so, but only if you loop over it a million times. But maybe newest cpus aren't as bad about it? (It actually used to be faster, allegedly!) revolution wrote:
Only barely. The 186 was still limited to 20-bit addresses (1 MB of RAM, not counting hardware EMS or IBM PC UMBs beyond 640 kb), and most people didn't have the full megabyte since it cost literally thousands of dollars. (Actually most old machines didn't even support the full meg, usually capping out at 256 kb or 512 kb, but the cpu itself did support the full 1048576 bytes.) My point is that it saves a few bytes in very limited environments, but obviously that advantage disappeared with larger (386+, 32-bit) code and bloatier software libraries. So nobody cared as much beyond the 486 or such. What good is saving 1 kb on a machine with 8 MB total? Heck, just for extra code+data alignment used to pacify the quirky 486, you're wasting tons more space. So it's long been moot. Last edited by rugxulo on 06 May 2018, 02:33; edited 1 time in total |
|||
05 May 2018, 00:54 |
|
Furs 05 May 2018, 12:04
rugxulo wrote: Only barely. Code: push ebp mov ebp, esp sub esp, 1024 7 bytes is barely "barely", it's a huge amount of code. rugxulo wrote: My point is that it saves a few bytes in very limited environments |
|||
05 May 2018, 12:04 |
|
bitRAKE 05 May 2018, 14:48
It is true we went from KB (I worked testing RAM chips for a small system integrator in my teens) to MB to GB of memory on our local machines, but today the user base and update frequency play a larger role in data size - single characters on the highest volume web pages have enormous costs in time and money.
|
|||
05 May 2018, 14:48 |
|
Goto page 1, 2 Next < Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.