Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions introduction.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,29 @@ This specification uses the following terms and abbreviations:
| RV64ILP32Q | Draft
|===

NOTE: ABI for big-endian is *NOT* included in this specification, we intend to
define that in future version of this specification.
=== Big-endian ABI Status

[width=80%]
|===
| ABI Name | Status

| ILP32 (BE) | Draft
| ILP32F (BE) | Draft
| ILP32D (BE) | Draft
| ILP32E (BE) | Draft
| LP64 (BE) | Draft
| LP64F (BE) | Draft
| LP64D (BE) | Draft
| LP64Q (BE) | Draft
| RV64ILP32 (BE) | Draft
| RV64ILP32F (BE) | Draft
| RV64ILP32D (BE) | Draft
| RV64ILP32Q (BE) | Draft
|===

NOTE: This specification now includes preliminary support for big-endian ABIs.
Big-endian support has been implemented in GNU GCC and LLVM/Clang toolchains.
The big-endian variants follow the same calling conventions as their
little-endian counterparts, with only the data byte ordering differing.

:sectnums:
27 changes: 22 additions & 5 deletions riscv-cc.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -166,9 +166,10 @@ the calling convention are unspecified upon exit, the contents of all
callee-saved registers must be restored to what was set on entry, and the
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.
NOTE: Big-endian calling conventions follow the same rules as little-endian
calling conventions. The only difference is in the byte ordering of multi-byte
values in memory and registers. Register usage, argument passing, and return
value conventions remain the same.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this address #265 (comment)?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a draft for that: we already pass 2×XLEN bits scalar in what we expected:

calars that are 2×XLEN bits wide are passed in a pair of argument registers,
with the low-order XLEN bits in the lower-numbered register and the high-order
XLEN bits in the higher-numbered register.  If no argument registers are
available, the scalar is passed on the stack by value.  If exactly one
register is available, the low-order XLEN bits are passed in the register and
the high-order XLEN bits are passed on the stack.

So I tried to add a paragraph to clarify also give an example for that, also adding a NOTE to describe the rationale.

The other thing I added is for Variadic arguments with 2×XLEN-bit and Aggregates with XLEN < size <= XLEN *2.

I didn't check with GCC implementation yet, but IIRC that's may not match GCC's default big-endian behavior.

diff --git a/riscv-cc.adoc b/riscv-cc.adoc
index 0768360..037b47f 100644
--- a/riscv-cc.adoc
+++ b/riscv-cc.adoc
@@ -191,6 +191,16 @@ available, the scalar is passed on the stack by value.  If exactly one
 register is available, the low-order XLEN bits are passed in the register and
 the high-order XLEN bits are passed on the stack.
 
+This register-pair ordering is defined in terms of value significance and is
+independent of endianness.  For example, on RV32BE a 64-bit scalar returned
+in a0/a1 places bits [31:0] (the least-significant XLEN bits) in a0 and
+bits [63:32] in a1; memory layout remains big-endian.
+
+NOTE: Defining the register-pair ordering independent of endianness allows
+RV32_Zdinx and Zilsd paired load/store paths to be used directly for argument
+passing and return without extra swaps.  Memory layout remains governed by the
+target endianness.
+
 Scalars wider than 2×XLEN bits are passed by reference and are replaced in the
 argument list with the address.
 
@@ -198,7 +208,10 @@ Aggregates whose total size is no more than XLEN bits are passed in
 a register, with the fields laid out as though they were passed in memory. If
 no register is available, the aggregate is passed on the stack.
 Aggregates whose total size is no more than 2×XLEN bits are passed in a pair
-of registers; if only one register is available, the first XLEN bits are passed
+of registers with the fields laid out as though they were passed in memory:
+the lower-numbered register holds the lower-addressed XLEN-sized chunk of
+the aggregate and the higher-numbered register holds the next chunk;
+if only one register is available, the first XLEN bits are passed
 in a register and the remaining bits are passed on the stack. If no registers are
 available, the aggregate is passed on the stack. Bits unused due to
 padding, and bits past the end of an aggregate whose size in bits is not
@@ -231,7 +244,10 @@ same manner as named arguments, with one exception.  Variadic arguments with
 even-numbered), or on the stack by value if none is available. After a
 variadic argument has been passed on the stack, all future arguments will also
 be passed on the stack (i.e. the last argument register may be left unused due
-to the aligned register pair rule).
+to the aligned register pair rule).  For 2×XLEN scalars placed in an aligned
+register pair, the lower-numbered register holds the least-significant XLEN bits
+and the higher-numbered register holds the most-significant XLEN bits,
+regardless of endianness.
 
 Values are returned in the same manner as a first named argument of the same
 type would be passed.  If such an argument would have been passed by


[#integer-cc]
=== Integer Calling Convention
Expand All @@ -191,14 +192,27 @@ available, the scalar is passed on the stack by value. If exactly one
register is available, the low-order XLEN bits are passed in the register and
the high-order XLEN bits are passed on the stack.

This register-pair ordering is defined in terms of value significance and is
independent of endianness. For example, on RV32BE a 64-bit scalar returned
in a0/a1 places bits [31:0] (the least-significant XLEN bits) in a0 and
bits [63:32] in a1; memory layout remains big-endian.

NOTE: Defining the register-pair ordering independent of endianness allows
RV32_Zdinx and Zilsd paired load/store paths to be used directly for argument
passing and return without extra swaps. Memory layout remains governed by the
target endianness.

Scalars wider than 2×XLEN bits are passed by reference and are replaced in the
argument list with the address.

Aggregates whose total size is no more than XLEN bits are passed in
a register, with the fields laid out as though they were passed in memory. If
no register is available, the aggregate is passed on the stack.
Aggregates whose total size is no more than 2×XLEN bits are passed in a pair
of registers; if only one register is available, the first XLEN bits are passed
of registers with the fields laid out as though they were passed in memory:
the lower-numbered register holds the lower-addressed XLEN-sized chunk of
the aggregate and the higher-numbered register holds the next chunk;
if only one register is available, the first XLEN bits are passed
in a register and the remaining bits are passed on the stack. If no registers are
available, the aggregate is passed on the stack. Bits unused due to
padding, and bits past the end of an aggregate whose size in bits is not
Expand Down Expand Up @@ -231,7 +245,10 @@ same manner as named arguments, with one exception. Variadic arguments with
even-numbered), or on the stack by value if none is available. After a
variadic argument has been passed on the stack, all future arguments will also
be passed on the stack (i.e. the last argument register may be left unused due
to the aligned register pair rule).
to the aligned register pair rule). For 2×XLEN scalars placed in an aligned
register pair, the lower-numbered register holds the least-significant XLEN bits
and the higher-numbered register holds the most-significant XLEN bits,
regardless of endianness.

Values are returned in the same manner as a first named argument of the same
type would be passed. If such an argument would have been passed by
Expand Down
50 changes: 50 additions & 0 deletions riscv-elf.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,56 @@ type-name = identifier-nondigit *identifier-char
identifier-nondigit = ALPHA / "_"
identifier-char = identifier-nondigit / "_"
----

== Data Representation

The data representation for RISC-V follows the conventions of the target endianness.
RISC-V supports both little-endian and big-endian byte ordering.

=== Byte Ordering

RISC-V implementations can support either little-endian or big-endian byte ordering:

* **Little-endian**: The least significant byte is stored at the lowest memory address.
This is the default and most common byte ordering for RISC-V.
* **Big-endian**: The most significant byte is stored at the lowest memory address.

The endianness is fixed at the system level and cannot be changed dynamically.
All data types follow the byte ordering of the target system.

IMPORTANT: RISC-V instructions are always stored in little-endian format,
regardless of the data endianness. This means that in big-endian systems,
data is big-endian but instructions remain little-endian.

=== Relocations and Byte Ordering

When generating big-endian output, the following relocations write their
data values in big-endian byte order:

* `R_RISCV_32`
* `R_RISCV_64`
* `R_RISCV_ADD16`
* `R_RISCV_ADD32`
* `R_RISCV_ADD64`
* `R_RISCV_SUB16`
* `R_RISCV_SUB32`
* `R_RISCV_SUB64`
* `R_RISCV_SET16`
* `R_RISCV_SET32`
* `R_RISCV_SET64`
* `R_RISCV_32_PCREL`
* `R_RISCV_PLT32`
* `R_RISCV_JUMP_SLOT`
* `R_RISCV_TLS_DTPMOD32`
* `R_RISCV_TLS_DTPMOD64`
* `R_RISCV_TLS_DTPREL64`
* `R_RISCV_TLS_DTPREL32`
* `R_RISCV_TLS_TPREL64`
* `R_RISCV_TLS_TPREL32`

All other relocations that modify instruction fields continue to use
little-endian byte order, as RISC-V instructions are always little-endian.

== ELF Object Files

The ELF object file format for RISC-V follows the
Expand Down