Skip to content

Commit

Permalink
Expand va_list description
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
sorear committed Feb 26, 2024
1 parent aa4698a commit 9e93d96
Showing 1 changed file with 30 additions and 7 deletions.
37 changes: 30 additions & 7 deletions riscv-cc.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 (<<integer-cc>>). 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

Expand Down

0 comments on commit 9e93d96

Please sign in to comment.