Calling conventions for functions on various architectures?

Talk about non-Acorn classic computers/hardware/software here (including retro consoles)
Post Reply
alex_farlie
Posts: 142
Joined: Sun Jul 07, 2013 9:46 pm
Contact:

Calling conventions for functions on various architectures?

Post by alex_farlie » Thu Aug 23, 2018 7:35 am

(This is under the other vintage computer heading, as it's a software architecture question, applicable to many systems, not just Acorn machines.)

On various systems, in assembled code there are calling conventions for how values are passed to functions are handled.

Typically there are 2 approaches used...

The C style convention - With the parameters passed right to left using a stack. which the calling function is responsible for cleaning up.
The Pascal style convention - With the parameters left to right , which the called function is responsible for cleaning up.

I've seen a few examples of how these nominally worked on ia32 (i.e 80x86)... (An additional note is that in real mode (i.e the original 16bit segmented "real" memory model , you had to distinguish between near (ie local) and far (non local) function calls due to the segmented memory.), and the calling conventions on this architecture are well documented elsewhere.

Adding an additional note for clarification, per a subsequent comment:

However, the dominant platform on 80x86 (ia32) Windows uses neither "C" or "Pascal" but the "__stdcall" (https://docs.microsoft.com/en-us/cpp/cpp/stdcall) extensively, which although the parameters are passed right to left as for a "C" style ( "__cdecl" style function) , it's the responsibility of the called function (callee) to do the cleanup. This approach is used extensively by the Windows API/ABI, and is one of the reasons why in assembler code written for that platform a "RET n" is frequently encountered at the end of functions, over a standard one.

Other than "__stdcall" and "__cdecl" there are other conventions used for more specific use-cases (https://docs.microsoft.com/en-us/cpp/cp ... onventions).

Generally though these differences, are hidden by the library definitions and include files used by a compiler and linker. It is another reason why for different platform and architecture combinations, various different library definitions and linkages have to be provided to compilers.

The calling conventions for functions, (along with the stack frame layout , register assignments/assumptions and formats for executable/relocatable code, amongst other things) are what form part of the Application Binary Interface for a given platform, running on a specific architecture, with such ABI's usally being documented alongside the functions provided by that platform or it's libraries ( the Application Programming Interface or API).
End of edit

RISC OS on ARM uses it own ABI, of which the Arm Procedure Call Standard (APCS) is part. The APCS had at least 2 versions, as it was revised when ARM chipsets (ARMv3 onwards? ) developed some time after the original, moved from having a combined PC and flags register (26 bit architecture), to having the processor flags in a seperate status register. My understanding was that on APCS at least 4(?) function arguments/parameters were passed in registers. SWI calls on RISC OS if I recall used registers to transfer parameters to system calls if I recall correctly. ( I am however sure that ArmLinux used a different method for sycalls and SWI's)

The BBC Micro (6502) based, did not I think have a 'calling' convention as such, although it could be considered to be that OSWORD, used a 'pointer to parameter' block convention, the pointer being passed in the X and Y registers, with the code calling OSWORD being responsible for cleaning up any memory allocations needed for the parameter block concerned. In the parameter block , the values were written at increasing memory locations ( so effectively left to right, going up in memory.). (Some Oswords are noted in the documentation not to preserve the parameter blocks concerned.)

This got me wondering , how was parameter passing for functions undertaken on other systems ?
Last edited by alex_farlie on Fri Aug 24, 2018 9:25 am, edited 1 time in total.

User avatar
BigEd
Posts: 2517
Joined: Sun Jan 24, 2010 10:24 am
Location: West
Contact:

Re: Calling conventions for functions on various architectures?

Post by BigEd » Thu Aug 23, 2018 7:43 am

(I'd say yes, OSWORD is a calling convention. It's used consistently as part of the MOS ABI, and makes sense, so could serve as a template for anyone building something modular in 6502 land.)

User avatar
jgharston
Posts: 3566
Joined: Thu Sep 24, 2009 11:22 am
Location: Whitby/Sheffield
Contact:

Re: Calling conventions for functions on various architectures?

Post by jgharston » Thu Aug 23, 2018 8:58 pm

The registers/control block selection depends on how much data the called function needs. CP/M on the Z80 uses a mixture of OSBYTE-type calls and OSWORD-type calls:
C=function, DE=>control block
C=function, DE=data value

eg:
C=2, E=character: write to console output
C=15, DE=>control block: open file

But this is system calls. The OP asked about function calls, that would be the calls in a program to other bits of the program. Z80 C compilers usually push parameters left to right, call the function, then drop the pushed parameters, eg:

myfunc(a, b, c);
becomes
LD HL,(_a)
PUSH HL
LD HL,(_b)
PUSH HL
LD HL,(_c)
PUSH HL
LD IX,0
ADD IX,SP ; IX=>parameters on the stack
CALL _myfunc
POP HL
POP HL
POP HL

That also has the side effect of the called function being entered with the rightmost parameter in the primary register, so a function with one parameter is often optimised to:

myfunc(a);
becomes
LD HL,(_a)
CALL _myfunc

Code: Select all

$ bbcbasic
PDP11 BBC BASIC IV Version 0.25
(C) Copyright J.G.Harston 1989,2005-2015
>_

User avatar
Richard Russell
Posts: 804
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: Calling conventions for functions on various architectures?

Post by Richard Russell » Fri Aug 24, 2018 12:56 am

alex_farlie wrote:
Thu Aug 23, 2018 7:35 am
I've seen a few examples of how these nominally worked on ia32 (i.e 80x86)…
MS Windows (which is hardly an insignificant IA-32 platform!) uses neither the 'C' nor the 'Pascal' ABI, using your own terminology. The parameters are pushed right-to-left (as in 'cdecl') but it is the responsibility of the callee (called function) to clean up the stack.

alex_farlie
Posts: 142
Joined: Sun Jul 07, 2013 9:46 pm
Contact:

Re: Calling conventions for functions on various architectures?

Post by alex_farlie » Fri Aug 24, 2018 9:31 am

Richard Russell wrote:
Fri Aug 24, 2018 12:56 am
alex_farlie wrote:
Thu Aug 23, 2018 7:35 am
I've seen a few examples of how these nominally worked on ia32 (i.e 80x86)…
MS Windows (which is hardly an insignificant IA-32 platform!) uses neither the 'C' nor the 'Pascal' ABI, using your own terminology. The parameters are pushed right-to-left (as in 'cdecl') but it is the responsibility of the callee (called function) to clean up the stack.
Thank you for (once again) finding something I should have mentioned originally :D I've expanded the original post.

Phlamethrower
Posts: 103
Joined: Fri Nov 24, 2017 1:35 pm
Contact:

Re: Calling conventions for functions on various architectures?

Post by Phlamethrower » Fri Aug 24, 2018 12:46 pm

alex_farlie wrote:
Thu Aug 23, 2018 7:35 am
My understanding was that on APCS at least 4(?) function arguments/parameters were passed in registers.
Yes, the basic approach is that the first four (integer) values are passed in registers, with the rest being pushed to the stack. But there are many different variants of APCS, both for 32bit and 64bit versions of the architecture.

ARM's docs for these are at http://infocenter.arm.com/help/topic/co ... index.html, although OS's may also make their own variants in order to suit their needs.
SWI calls on RISC OS if I recall used registers to transfer parameters to system calls if I recall correctly. ( I am however sure that ArmLinux used a different method for sycalls and SWI's)
Correct on both counts. I suspect the APCS came along too late in order to inform the design of the OS's SWI interface, which is why there's no real consistency on how SWIs accept and return values.

For Linux (and I suspect most other non-RISC OS systems) the main change was to use a register to pass in the SWI number instead of using the field in the instruction - which helps avoid D-cache pollution.

https://wiki.debian.org/ArmEabiPort#Sys ... _interface

Unfortunately the implementation means that it's impossible to run ARM EABI code ontop of RISC OS (or vice-versa) without trapping every single SWI instruction (everything uses SWI number 0, which under RISC OS is OS_WriteC).

User avatar
jgharston
Posts: 3566
Joined: Thu Sep 24, 2009 11:22 am
Location: Whitby/Sheffield
Contact:

Re: Calling conventions for functions on various architectures?

Post by jgharston » Fri Aug 24, 2018 7:56 pm

Phlamethrower wrote:
Fri Aug 24, 2018 12:46 pm
Unfortunately the implementation means that it's impossible to run ARM EABI code ontop of RISC OS (or vice-versa) without trapping every single SWI instruction (everything uses SWI number 0, which under RISC OS is OS_WriteC).
Yes, that's really really really annoying, especially as the Acorn specification of the SWI number includes an Operating System field, so if they'd used SWI &FFFFFF (and allow assemblers to truncate from SWI -1) for EBI code it would have fitted side by side seamlessly.

Code: Select all

$ bbcbasic
PDP11 BBC BASIC IV Version 0.25
(C) Copyright J.G.Harston 1989,2005-2015
>_

Coeus
Posts: 1265
Joined: Mon Jul 25, 2016 11:05 am
Contact:

Re: Calling conventions for functions on various architectures?

Post by Coeus » Fri Aug 24, 2018 10:03 pm

alex_farlie wrote:
Thu Aug 23, 2018 7:35 am
Typically there are 2 approaches used...
The C style convention - With the parameters passed right to left using a stack. which the calling function is responsible for cleaning up.
The Pascal style convention - With the parameters left to right , which the called function is responsible for cleaning up.
Maybe I am being dense here but I don't understand what you mean by left to right (or right to left) with reference to the stack. Stacks are usually thought of as going up and down, not left to right.

So, when you say "passed right to left using a stack" do you mean that the rightmost parameter as it appears in the source code is pushed onto the stack first and the leftmost parameter pushed last? For any architecture that has the stack growing downwards in memory that means the layout of the parameters actually received goes left to right over ascending addresses and obviously vice vera for the other convention.

Also, when you say the question of who POPs the parameters from the stack when the procedure returns differs between C and Pascal is this across multiple ABIs on multiple processors? One could start out with the idea that there should be more calls to procedures than definitions so having the code to POP the parameters in the callee should save space. There are a couple of problems with this approach, though:
  1. The last thing to be pushed onto the stack is the return address. That means the callee cannot simply pop and discard a certain number of items from the stack and then return as this would discard the return address. To resolve this requires either:
    1. Specific support in the instruction set so that the return instruction can POP the return address and then adjust the stack pointer to discard a specifed number of items before actually jumping to the return address.
    2. Some clumsy assembler that POPs the return address into a register the ABI allows to be trashed, POPs the parameters, re-pushes the return address and then does the return.
    So this issue is about the target processor.
  2. In C, function calls with a variable number of parameters are possible. Whilst code written by the programmer within the callee may be able to work out how many were passed (for example in printf-link functions by the format string) the compiler's code generator does not know this and therefore could not generate code in the callee to POP those parameters. The caller does know, however. That would mean either using "caller POPs" for all calls or for just calls to variadics.
alex_farlie wrote:
Thu Aug 23, 2018 7:35 am
However, the dominant platform on 80x86 (ia32) Windows uses neither "C" or "Pascal" but the "__stdcall" (https://docs.microsoft.com/en-us/cpp/cpp/stdcall) extensively, which although the parameters are passed right to left as for a "C" style ( "__cdecl" style function) , it's the responsibility of the called function (callee) to do the cleanup. This approach is used extensively by the Windows API/ABI, and is one of the reasons why in assembler code written for that platform a "RET n" is frequently encountered at the end of functions, over a standard one.
Interestingly I thought the early Windows code was written in Modula 2 which is from the Pascal family.

The other thing to bear in mind here is that these are conventions for high level languages. An OS written when it was expected the majority of software would be compiled from a high level language would prefer to define its API in terms of procedure calls. This is the case with Unix, for example, and whatever trap/SWI mechanism is used to transfer control from user program to kernel is taken care of by library code.

Going back to the days of the BBC Micro and OSBYTE/OSWORD the convention here is obviously different because these were designed to be called from assembly language. That's not to say Acorn weren't expecting high level languages to be implemented but most that were implemented were either interpreters working directly from the source language or interpreters of an intermediate code.

A possible exception to this early 8-bit approach is the CP/M calling convention which apparently comes from the ABI of PL/M.

alex_farlie
Posts: 142
Joined: Sun Jul 07, 2013 9:46 pm
Contact:

Re: Calling conventions for functions on various architectures?

Post by alex_farlie » Fri Aug 24, 2018 11:28 pm

Coeus wrote:
Fri Aug 24, 2018 10:03 pm
alex_farlie wrote:
Thu Aug 23, 2018 7:35 am
Typically there are 2 approaches used...
The C style convention - With the parameters passed right to left using a stack. which the calling function is responsible for cleaning up.
The Pascal style convention - With the parameters left to right , which the called function is responsible for cleaning up.
Maybe I am being dense here but I don't understand what you mean by left to right (or right to left) with reference to the stack. Stacks are usually thought of as going up and down, not left to right.

So, when you say "passed right to left using a stack" do you mean that the rightmost parameter as it appears in the source code is pushed onto the stack first and the leftmost parameter pushed last? For any architecture that has the stack growing downwards in memory that means the layout of
the parameters actually received goes left to right over ascending addresses and obviously vice vera for the other convention.
That is my understanding based on the examples I've seen...
Coeus wrote:
Fri Aug 24, 2018 10:03 pm
Also, when you say the question of who POPs the parameters from the stack when the procedure returns differs between C and Pascal is this across multiple ABIs on multiple processors?
Different platforms , and different architectures can have different ABI's . Even between different OS on the same architecture, Risc OS differs from ArmLinux (ON ARM) as mentioned previously. :oops: :oops: Amend as I mangled this when posting
Last edited by alex_farlie on Sat Aug 25, 2018 1:19 pm, edited 2 times in total.

alex_farlie
Posts: 142
Joined: Sun Jul 07, 2013 9:46 pm
Contact:

Re: Calling conventions for functions on various architectures?

Post by alex_farlie » Fri Aug 24, 2018 11:31 pm

jgharston wrote:
Fri Aug 24, 2018 7:56 pm
Phlamethrower wrote:
Fri Aug 24, 2018 12:46 pm
Unfortunately the implementation means that it's impossible to run ARM EABI code ontop of RISC OS (or vice-versa) without trapping every single SWI instruction (everything uses SWI number 0, which under RISC OS is OS_WriteC).
Yes, that's really really really annoying, especially as the Acorn specification of the SWI number includes an Operating System field, so if they'd used SWI &FFFFFF (and allow assemblers to truncate from SWI -1) for EBI code it would have fitted side by side seamlessly.
I even tried to get ReactOS to actually respect the OS field when it wrote it it's NativeAPI stuff... It' didn't happen and each OS uses SWI's differently.

Coeus
Posts: 1265
Joined: Mon Jul 25, 2016 11:05 am
Contact:

Re: Calling conventions for functions on various architectures?

Post by Coeus » Sat Aug 25, 2018 1:06 pm

alex_farlie wrote:
Fri Aug 24, 2018 11:28 pm
Different platforms , and different architectures can have different ABI's . Even between different OS on the same architecture, Risc OS differs from ArmLinux on ia32. as mentioned previously.
Perhaps I wasn't completely clear in what I was getting at. I meant does Pascal use the left to right and callee tidies up convention across multiple processors and C use the right to left convention and caller tidies up across multiple processors?

The reason behind the question was to see to what extent these things are determined by language, OS and processor architecture.

Taking ia32, C and Pascal as an example it would not be surprising if the designers of a Pascal compiler went for callee tidies up because the processor instruction set has specific support for doing that, i.e. the RET n instruction. They probably would not have been looking ahead to C being implemented on the same processor and, as I said, C variadic functions require caller tidies up because it is only the caller that knows what it pushed.

There doesn't seem to me to be anything to choose between left to right and right to left. It's a bit like big endian vs. little endian byte order.

BTW, as a technical correction ia32 is Intel. ArmLinux runs on ARM which is different, even if ARM has 32 and 64 bit versions.
Last edited by Coeus on Sat Aug 25, 2018 1:08 pm, edited 1 time in total.

alex_farlie
Posts: 142
Joined: Sun Jul 07, 2013 9:46 pm
Contact:

Re: Calling conventions for functions on various architectures?

Post by alex_farlie » Sat Aug 25, 2018 1:25 pm

Coeus wrote:
Sat Aug 25, 2018 1:06 pm
alex_farlie wrote:
Fri Aug 24, 2018 11:28 pm
Different platforms , and different architectures can have different ABI's . Even between different OS on the same architecture, Risc OS differs from ArmLinux on ia32. as mentioned previously.
Perhaps I wasn't completely clear in what I was getting at. I meant does Pascal use the left to right and callee tidies up convention across multiple processors and C use the right to left convention and caller tidies up across multiple processors?
I'm not sure :oops: , so good question.
Last edited by alex_farlie on Sat Aug 25, 2018 1:44 pm, edited 2 times in total.

User avatar
jgharston
Posts: 3566
Joined: Thu Sep 24, 2009 11:22 am
Location: Whitby/Sheffield
Contact:

Re: Calling conventions for functions on various architectures?

Post by jgharston » Sat Aug 25, 2018 5:02 pm

Coeus wrote:
Fri Aug 24, 2018 10:03 pm
alex_farlie wrote:
Thu Aug 23, 2018 7:35 am
Typically there are 2 approaches used...
The C style convention - With the parameters passed right to left using a stack. which the calling function is responsible for cleaning up.
The Pascal style convention - With the parameters left to right , which the called function is responsible for cleaning up.
Maybe I am being dense here but I don't understand what you mean by left to right (or right to left) with reference to the stack. Stacks are usually thought of as going up and down, not left to right.
Go through the parameters from left to right, pushing them on the stack as you go:

left to right:
myfunc(a, b, c)
evaluate a
push it on the stack
evaluate b
push it on the stack
evaluate c
push it on the stack
call myfunc, stack has => c, b, a

right to left:
myfunc(a, b, c)
now, I'm not sure how this actually works, so this is guesswork
when compiler compiled the code it found three parameters, so 'three' is embedded into the code so it can find where 'c' is.
evaluate c
push it on the stack
evaluate b
push it on the stack
evaluate a
push it on the stack
call myfunc, stack has => a, b, c

To be honest, I have no idea how a right-to-left parameter parser would even function, so I have no idea how it would even be implemented, as you need to work backwards through what you're processing without knowing until you've processed things how far backwards you need to go. The 6502 and Z80 'C' code I'm familiar with parses left to right, in the order that you would naturally expect execution to occur. The compiler compilers we studied at uni 30 years ago also created compilers that parsed in execution order left to right.

Code: Select all

$ bbcbasic
PDP11 BBC BASIC IV Version 0.25
(C) Copyright J.G.Harston 1989,2005-2015
>_

User avatar
Richard Russell
Posts: 804
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: Calling conventions for functions on various architectures?

Post by Richard Russell » Sat Aug 25, 2018 5:31 pm

jgharston wrote:
Sat Aug 25, 2018 5:02 pm
To be honest, I have no idea how a right-to-left parameter parser would even function, so I have no idea how it would even be implemented, as you need to work backwards through what you're processing without knowing until you've processed things how far backwards you need to go.
A compiler isn't constrained to scan the source code just once (for that matter nor is an interpreter). All it needs to do is to scan the parameters in two passes: in the first pass it does nothing but count them (it may be as trivial as looking for commas) and in the second pass it does the parsing and evaluating. Since it already knows how many parameters there are, and hence how much stack space they will occupy, there is no problem.

Indeed it is usually the case that the compiler will already know the function's signature, i.e. how many parameters it takes and what type, having been told that previously in the code (or in a header file) in which case it doesn't even need two passes. But since C does not mandate that a function be declared in advance the compiler may need to resort to the two-pass technique.

It's important to appreciate that pushing parameters 'right-to-left' isn't in any way strange, it's what results in the parameters being in their natural order in memory (the first parameter is at the lowest address and the subsequent parameters are stored at increasing addresses). This makes it particularly easy for the called function to locate each parameter. Pushing parameters 'left-to-right' causes them to end up in reverse order in memory (assuming a conventional top-down stack that is).

alex_farlie
Posts: 142
Joined: Sun Jul 07, 2013 9:46 pm
Contact:

Re: Calling conventions for functions on various architectures?

Post by alex_farlie » Sat Aug 25, 2018 8:26 pm

Richard Russell wrote:
Sat Aug 25, 2018 5:31 pm
jgharston wrote:
Sat Aug 25, 2018 5:02 pm
To be honest, I have no idea how a right-to-left parameter parser would even function, so I have no idea how it would even be implemented, as you need to work backwards through what you're processing without knowing until you've processed things how far backwards you need to go.
A compiler isn't constrained to scan the source code just once (for that matter nor is an interpreter). All it needs to do is to scan the parameters in two passes: in the first pass it does nothing but count them (it may be as trivial as looking for commas) and in the second pass it does the parsing and evaluating. Since it already knows how many parameters there are, and hence how much stack space they will occupy, there is no problem.

Indeed it is usually the case that the compiler will already know the function's signature, i.e. how many parameters it takes and what type, having been told that previously in the code (or in a header file) in which case it doesn't even need two passes. But since C does not mandate that a function be declared in advance the compiler may need to resort to the two-pass technique.

It's important to appreciate that pushing parameters 'right-to-left' isn't in any way strange, it's what results in the parameters being in their natural order in memory (the first parameter is at the lowest address and the subsequent parameters are stored at increasing addresses). This makes it particularly easy for the called function to locate each parameter. Pushing parameters 'left-to-right' causes them to end up in reverse order in memory (assuming a conventional top-down stack that is).
The following are some related issues (best had in a different discussion.)
: Were there any architectures (OR OS) that had ascending stacks? ( I am of course aware that a stack and a 'heap' are different things architecturally speaking)

: As I said previously.. The calling convention isn't the whole picture.. There is also how groups of parameters/local variables, and also the equally important return address) get grouped, or in one word "framing." Unless I am mis-understanding again?

Some early systems in which assembler code was written directly didn't use framing as such, but nearly all compiled code
on modern systems use framing of some kind, which means compilers supply a 'prolog' and 'epilog' to functions to ensure the appropriate stack frames are generated?
(:oops: I should have probably looked into an OS/Kernel programming option at college.. I'm still learning things about it..
Last edited by danielj on Sat Aug 25, 2018 10:05 pm, edited 4 times in total.
Reason: Reinstate older version to keep context in thread.

User avatar
Richard Russell
Posts: 804
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: Calling conventions for functions on various architectures?

Post by Richard Russell » Sat Aug 25, 2018 8:54 pm

alex_farlie wrote:
Sat Aug 25, 2018 8:26 pm
The calling convention isn't the whole picture.
It is however the subject of this thread! :roll:

alex_farlie
Posts: 142
Joined: Sun Jul 07, 2013 9:46 pm
Contact:

Re: Calling conventions for functions on various architectures?

Post by alex_farlie » Sat Aug 25, 2018 9:15 pm

Richard Russell wrote:
Sat Aug 25, 2018 8:54 pm
alex_farlie wrote:
Sat Aug 25, 2018 8:26 pm
The calling convention isn't the whole picture.
It is however the subject of this thread! :roll:
Thank you, for the explanation about parameter passing, this is why your expertise is something I very much appreciate on this forum.


Edited 2018-08-25
Last edited by danielj on Sat Aug 25, 2018 10:03 pm, edited 3 times in total.

Coeus
Posts: 1265
Joined: Mon Jul 25, 2016 11:05 am
Contact:

Re: Calling conventions for functions on various architectures?

Post by Coeus » Sat Aug 25, 2018 9:23 pm

alex_farlie wrote:
Sat Aug 25, 2018 8:26 pm
Were there any architectures (OR OS) that had ascending stacks?
HP-UX on HP-PA (A RISC processor). In fact with RISC it is not uncommon for there not to be a specific stack pointer with specific PUSH and POP instructions but some index registers which can be used to reference memory and which can be incremented and decremented and thus used to implement a stack.
alex_farlie wrote:
Sat Aug 25, 2018 8:26 pm
There is also how groups of parameters/local variables, and also the equally important return address) get grouped, or in one word "framing." Unless I am mis-understanding again?
Giving this a name makes it sound like something complicated but as far as I can see it is straightforward. If a called function needs space for local variable on the stack it simply adjusts the stack pointer to give the appropriate amount of space, i.e. for a stack growing downwards it subtracts an amount that covers these variables from the stack pointer (in the prologue you mention). Now calls to other procedures will work without clobbering those variables. When it comes time to return the top item on the stack will be a local variable, not the return address so the epilogue must increment the stack pointer by the same amount as it was previously decremented thus making the return address the top item. One variation on this is to have a "frame pointer" register. The prologue would push the previous frame pointer value onto the stack and copy the stack pointer to the frame pointer and then decrement the stack pointer. The epilogue would copy the frame pointer back to the stack pointer, pop the old value of the frame pointer and then it would be ready to return.
alex_farlie wrote:
Sat Aug 25, 2018 8:26 pm
On any given modern OS/Architecure, My understanding was that it was more complex, given that each application "process" (and "thread" which are process like got it's own stack and its own heap. I am sure in reading compiler (or possibly linker documentation) for a DOS development system both a STACK and HEAP to allocate, had to be given to the compiler or linker..
The DOS era was probably the peak of complexity from the perspective of trying to do any kind of multi-processing with the stacks and heaps of different things all in the same address space. More modern OSes give each process a separate virtual address space so in each case the heap can simply grow from the end of the space occupied by the code and any shared libraries. For threads there are a number of options. On Linux a thread is a process that happens to share all the segments except the stack and a small amount of thread-local storage with the other threads running in the same program. That means these stacks could be at the same virtual address and means there is no issue with the stack of one thread colliding with the stack of another thread. Others OSes may do things differently.

User avatar
Richard Russell
Posts: 804
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: Calling conventions for functions on various architectures?

Post by Richard Russell » Sat Aug 25, 2018 10:09 pm

Coeus wrote:
Sat Aug 25, 2018 9:23 pm
Giving this a name makes it sound like something complicated but as far as I can see it is straightforward.
In essence yes, but in the real world it can rapidly get more complex. For example the requirement for an exception handler to be able to 'unwind' the stack can impose complications. When parameters are passed in registers (common in more modern architectures) there is sometimes a requirement nevertheless to allocate equivalent 'shadow' stack space for them. The ABI may call for a stack red zone (which implies interrupts using a separate stack). And so on... not necessarily what I would call "straightforward"!

Post Reply