From 9e93d968003006a3b43199189e6bc649143c095b Mon Sep 17 00:00:00 2001 From: Stefan O'Rear Date: Sat, 24 Feb 2024 17:41:39 -0500 Subject: [PATCH] Expand va_list description Explain how a varargs save area is constructed, and define the representation of a va_list and the operation of va_arg independently of the definition of va_start. Fixes #412. Signed-off-by: Stefan O'Rear --- riscv-cc.adoc | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/riscv-cc.adoc b/riscv-cc.adoc index 05ac053d..7c9bab50 100644 --- a/riscv-cc.adoc +++ b/riscv-cc.adoc @@ -170,6 +170,7 @@ contents of any fixed registers like `gp` and `tp` never change, NOTE: Calling convention for big-endian is *NOT* included in this specification yet, we intend to define that in future version of this specification. +[#integer-cc] === Integer Calling Convention The base integer calling convention provides eight argument registers, @@ -655,13 +656,35 @@ purposes of data races. === va_list, va_start, and va_arg -The `va_list` type is `void*`. A callee with variadic arguments is responsible -for copying the contents of registers used to pass variadic arguments to the -vararg save area, which must be contiguous with arguments passed on the stack. -The `va_start` macro initializes its `va_list` argument to point to the start -of the vararg save area. The `va_arg` macro will increment its `va_list` -argument according to the size of the given type, taking into account the -rules about 2×XLEN aligned arguments being passed in "aligned" register pairs. +The `va_list` type has the same representation as `void*` and points to a +sequence of zero or more arguments with preceding padding for alignment, +formatted and aligned as variadic arguments passed on the stack according to +the integer calling convention (<>). All standard calling conventions use the +same representation for variadic arguments to allow `va_list` types to be +shared between them. + +The `va_start` macro in a function initializes its `va_list` argument to point +to the first address at which a variadic argument could be passed to the +function. If all integer argument registers are used for named formal +arguments, the first variadic argument will have been passed on the stack by +the caller, and the `va_list` can point to the address immediately after the +last named argument passed on the stack, or the `sp` value on entry if no named +arguments were passed on the stack. If some integer argument registers were not +used for named formal arguments, then the first variadic argument may have been +passed in a register. The function is then expected to construct a _varargs +save area_ immediately below the entry `sp` and fill it with the entry values +of all integer argument registers not used for named arguments, in sequence. +The `va_list` value can then be initialized to the start of the varargs save +area, and it will iterate through any variadic arguments passed via registers +before continuing to variadic arguments passed on the stack, if any. + +The `va_arg` macro will align its `va_list` argument, fetch a value, and +increment the `va_list` according to the alignment and size of a variadic +argument of the given type, which may not be the same as the alignment and size +of the given type in memory. If the type is passed by reference, the size and +alignment used will be those of a pointer, and the fetched pointer will be used +as the address of the actual argument. The `va_copy` macro is a single pointer +copy and the `va_end` macro performs no operation. === Vector type sizes and alignments