This file contains the RISC-V additions to the standard C APIs. It merges together the compiler command-line API, the preprocessor API, and the C language extensions (function attributes and intrinsic functions).
© 2018 Palmer Dabbelt palmer@sifive.com
© 2018 SiFive, Inc
It is licensed under the Creative Commons Attribution 4.0 International License (CC-BY 4.0). The full license text is available at https://creativecommons.org/licenses/by/4.0/.
Name | Value | When defined |
---|---|---|
__riscv | 1 | Always defined. |
__riscv_xlen |
|
Always defined. |
__riscv_flen |
|
F extension is available. |
__riscv_32e | 1 | RV32E is available. |
__riscv_64e | 1 | RV64E is available. |
__riscv_vector | 1 | Implies that any of the vector extensions (v or zve* ) is available |
__riscv_v_min_vlen | (see __riscv_v_min_vlen) | The V extension or one of the Zve* extensions is available. |
__riscv_v_elen | (see __riscv_v_elen) | The V extension or one of the Zve* extensions is available. |
__riscv_v_elen_fp | (see __riscv_v_elen_fp) | The V extension or one of the Zve* extensions is available. |
__riscv_misaligned_fast | 1 | Misaligned accesses are fast. |
__riscv_misaligned_slow | 1 | Misaligned accesses are supported, but may be substantially slower than aligned accesses. |
__riscv_misaligned_avoid | 1 | Misaligned accesses are not supported and could trap. (see _riscv_misaligned{fast,slow,avoid} |
The __riscv_v_min_vlen
macro expands to the minimal VLEN, in bits, mandated
by the available vector extension, if any.
The value of __riscv_v_min_vlen
is defined by the following rules:
- 128, if the
V
extension is present; - 32, if one of the
Zve32{x,f}
extensions is present; - 64, if one of the
Zve64{x,f,d}
extensions is present; N
, if one of theZvl<N>b
extensions,N
in{32,64,128,256,512,1024}
, is present.
If multiple rules apply, the maximum value is taken.
If none of the rules apply, __riscv_v_min_vlen
is undefined.
Examples:
__riscv_v_min_vlen
is 128 forrv64gcv
__riscv_v_min_vlen
is 512 forrv32gcv_zvl512b
__riscv_v_min_vlen
is 256 forrv32gcv_zvl32b_zvl256b
__riscv_v_min_vlen
is 128 forrv64gcv_zvl32b
The __riscv_v_elen
macro expands to the supported element length, in bits,
of any non-floating-point vector operand of any vector instruction in the
available vector extension, if any. (Stricter upper bounds may apply to
particular operands of particular instructions.)
The value of __riscv_v_elen
is defined by the following rules:
- 64, if the
V
extension or one of theZve64{x,f,d}
extensions is present; and - 32, if one of the
Zve32{x,f}
extensions is present. If multiple rules apply, the maximum value is taken. If none of the rules apply,__riscv_v_elen
is undefined.
The __riscv_v_elen_fp
macro expands to the supported element length, in bits,
of any floating-point vector operand of any vector instruction in the available
vector extension, if any. (Stricter upper bounds may apply to particular
operands of particular instructions.)
The value of __riscv_v_elen_fp
is defined by the following rules:
- 64, if one of the
V
orZve64d
extensions is present; - 32, if one of the
Zve{32,64}f
extensions is present; and - 0, if one of the
Zve{32,64}x
extensions is present. If multiple rules apply, the maximum value is taken. If none of the rules apply,__riscv_v_elen_fp
is undefined.
These can be used in common library code to compile time segregate code which relies on misaligned access being fast or not. A typical compiler could (but not necessarily) map fast variant to -mno-strict-align and avoid to -mstrict-align, if specified. Perhaps obvious, but these are mutually exclusive, so only one is defined at a time for a compilation unit.
Architecture extension test macros allow checking the availability and version
of extensions at compile-time. These feature macros are optional, and compilers
that support them provide a __riscv_arch_test
macro (with the value 1
).
The naming rule for the test macros is __riscv_<ext_name>
,
where <ext_name>
is all lower-case. Examples:
- The test macro for the
A
extension is__riscv_a
. - The test macro for the
Zifencei
extension is__riscv_zifencei
. - The test macro for the
XVentanaCondOps
extension is__riscv_xventantacondops
.
Besides extensions, test macros also exist for ISA bases following the same pattern. Examples:
- The test macro for the
I
base is__riscv_i
. - The test macro for the
E
base is__riscv_e
.
The value of the test macros is derived from its version using the following formula:
<MAJOR_VERSION> * 1,000,000 + <MINOR_VERSION> * 1,000 + <REVISION_VERSION>
For example:
- F-extension v2.2 will define
__riscv_f
as2002000
.
Name | Value | When defined |
---|---|---|
__riscv_abi_rve | 1 | Defined if using ilp32e or lp64e ABI |
__riscv_float_abi_soft | 1 | Defined if using ilp32 , ilp32e , lp64 or lp64e ABI. |
__riscv_float_abi_single | 1 | Defined if using ilp32f or lp64f ABI. |
__riscv_float_abi_double | 1 | Defined if using ilp32d or lp64d ABI. |
__riscv_float_abi_quad | 1 | Defined if using ilp32q or lp64q ABI. |
Name | Value | When defined |
---|---|---|
__riscv_cmodel_medlow | 1 | Defined if using medlow code model. |
__riscv_cmodel_medany | 1 | Defined if using medany code model. |
Name | Value | When defined | Alternative |
---|---|---|---|
__riscv_cmodel_pic | 1 | GCC defines this when compiling with -fPIC , -fpic , -fPIE or -fpie . |
__PIC__ or __PIE__ |
__riscv_mul | 1 | M extension is available. |
__riscv_m |
__riscv_div | 1 | M extension is available and -mno-div is not given.*[1] |
__riscv_m |
__riscv_muldiv | 1 | M extension is available and -mno-div is not given.*[1] |
__riscv_m |
__riscv_atomic | 1 | A extension is available. |
__riscv_a |
__riscv_fdiv | 1 | F extension is available and -mno-fdiv is not given.*[1] |
__riscv_f or __riscv_d |
__riscv_fsqrt | 1 | F extension is available and -mno-fdiv is not given.*[1] |
__riscv_f or __riscv_d |
__riscv_compressed | 1 | C extension is available. |
__riscv_c |
*[1] Not all compilers provide -mno-div
and -mno-fdiv
option.
The compiler won't generate the prologue/epilogue for those functions with
naked
attributes. This attribute is usually used when you want to write a
function with an inline assembly body.
This attribute is incompatible with the interrupt
attribute.
NOTE: Be aware that compilers might have further restrictions on naked functions. Please consult your compiler's manual for more information.
__attribute__((interrupt))
, __attribute__((interrupt("user")))
, __attribute__((interrupt("supervisor")))
__attribute__((interrupt("machine")))
The interrupt attribute specifies that a function is an interrupt handler.
The compiler will save/restore all used registers in the prologue/epilogue
regardless of the ABI, all used registers including floating point
register/vector register if F
extension/vector extension is enabled. If F or
V CSRs may be modified by an interrupt function, they must be saved by the
compiler.
The interrupt attribute can have an optional parameter to specify the mode.
The possible values are user
, supervisor
, or machine
.
The default value machine
is used, if the mode is not specified.
The function can specify only one mode; the compiler should raise an error if a function declares more than one mode or an undefined mode.
This attribute is incompatible with the naked
attribute.
The target
attribute is used to enable a set of features or extensions for a
function.
For instance, you can enable the v
extension for a specific function even if
the -march
or -mcpu
options do not include the v
extension. Importantly,
this won't alter the global settings. Here is an example:
__attribute__((target("arch=+v")))
int foo(int a)
{
return a + 5;
}
Using the target
attribute for a function should not affect the translation unit scope
build attributes. For example, if a file is compiled with -march=rv64ima
and
a function is declared with __attribute__((target("arch=+zbb")))
, the
Tag_RISCV_arch
build attribute should remain rv64ima
, not rv64ima_zbb
.
The compiler may emit a mapping symbol at the beginning of a function with the target attribute if the function utilizes a different set of ISA extensions.
<ATTR-STRING>
can specify the following target attributes:
arch=
: Adds extra extensions or overrides the-march
value specified via the command line for the function.tune=
: Specifies the pipeline model and cost model associated with a specific microarchitecture or core for the function.cpu=
: Specifies the pipeline mode, cost model, and extension settings for the function.
The interactions among the arch
, tune
, and cpu
attributes mirror those of
the -march
, -mtune
, and -mcpu
options. The cpu
attribute can be seen as
a combination of arch
+ tune
but holds a lower priority than the other two.
For instance, cpu=sifive-u74
equates to arch=rv64gc
and
tune=sifive-7-series
. However, if values for arch=
or tune=
are provided,
they will override the cpu
value. Therefore, cpu=sifive-u74;arch=rv64g
is
equivalent to arch=rv64g;tune=sifive-7-series
, and
cpu=sifive-u74;tune=sifive-5-series
is equivalent to
arch=rv64gc;tune=sifive-5-series
.
The compiler should emit error if the same type of attribute is specified more
than once. For example, arch=+zbb;arch=+zba
, compiler should emit error
because arch
has specified twice.
The compiler should emit error if target attribute has specified more than once.
For example,
__attribute__((target("arch=+v"))) __attribute__((target("arch=+zbb"))) int foo(int a)
, compiler should emit error because target attribute has specified twice.
The interactions between the attribute and the command-line option are specified below:
arch=
: Its behavior depends on the syntax used: 1) Adding extra extensions: It will merge the extension list with the-march
option. 2) If a full architecture string is specified byarch=
, it will override the-march
option.tune=
: Overrides the-mtune
option and the pipeline model and cost model part of-mcpu
.cpu=
: Overrides the-mcpu
option, overrides the-mtune
option iftune=
is not present, and overrides the-march
option ifarch=
is not present.
The syntax of <ATTR-STRING>
describes below:
ATTR-STRING := ATTR-STRING ';' ATTR
| ATTR
ATTR := ARCH-ATTR
| CPU-ATTR
| TUNE-ATTR
ARCH-ATTR := 'arch=' EXTENSIONS-OR-FULLARCH
EXTENSIONS-OR-FULLARCH := <EXTENSIONS>
| <FULLARCHSTR>
EXTENSIONS := <EXTENSION> ',' <EXTENSIONS>
| <EXTENSION>
FULLARCHSTR := <full-arch-string>
EXTENSION := <OP> <EXTENSION-NAME> <VERSION>
OP := '+'
VERSION := [0-9]+ 'p' [0-9]+
| [1-9][0-9]*
|
EXTENSION-NAME := Naming rule is defined in RISC-V ISA manual
CPU-ATTR := 'cpu=' <valid-cpu-name>
TUNE-ATTR := 'tune=' <valid-tune-name>
The target attribute does not support multi-versioning. The compiler should emit an error if a function is defined more than once. For example, the following code should trigger an error because foo is declared twice:
__attribute__((target("arch=+v"))) int foo(void) { return 0; }
__attribute__((target("arch=+zbb"))) int foo(void) { return 1; }
The target_clones
attribute is used to create multiple versions of a function. The compiler will emit multiple versions based on the provided arguments.
Each TARGET-CLONES-ATTR-STRING
defines a distinguished version of the function. The TARGET-CLONES-ATTR-STRING
list must include default
indicating the translation unit scope build attributes.
The syntax of <TARGET-CLONES-ATTR-STRING>
describes below:
TARGET-CLONES-ATTR-STRING := 'arch=' EXTENSIONS
| 'default'
EXTENSIONS := <EXTENSION> ',' <EXTENSIONS>
| <EXTENSION>
EXTENSION := <OP> <EXTENSION-NAME> <VERSION>
OP := '+'
VERSION := [0-9]+ 'p' [0-9]+
| [1-9][0-9]*
|
EXTENSION-NAME := Naming rule is defined in RISC-V ISA manual
For example, the following foo
function will have three versions but share the same function signature.
__attribute__((target_clones("arch=+v", "default", "arch=+zbb")))
int foo(int a)
{
return a + 5;
}
int bar() {
// foo will be resolved by ifunc
return foo(1);
}
It makes the compiler trigger the function multi-version when there exist more than one version for the same function signature.
The target_version
attribute is used to create one version of a function. Functions with the same signature may exist with multiple versions in the same translation unit.
Each TARGET-VERSION-ATTR-STRING
defines a distinguished version of the function. If there is more than one version for the same function, it must have default
one that indicating the translation unit scope build attributes.
The syntax of <TARGET-VERSION-ATTR-STRING>
describes below:
TARGET-VERSION-ATTR-STRING := 'arch=' EXTENSIONS
| 'default'
EXTENSIONS := <EXTENSION> ',' <EXTENSIONS>
| <EXTENSION>
EXTENSION := <OP> <EXTENSION-NAME> <VERSION>
OP := '+'
VERSION := [0-9]+ 'p' [0-9]+
| [1-9][0-9]*
|
EXTENSION-NAME := Naming rule is defined in RISC-V ISA manual
For example, the following foo function has three versions.
__attribute__((target_version("arch=+v")))
int foo(int a)
{
return a + 5;
}
__attribute__((target_version("arch=+zbb")))
int foo(int a)
{
return a + 5;
}
__attribute__((target_version("default")))
int foo(int a)
{
return a + 5;
}
int bar() {
// foo will be resolved by ifunc
return foo(1);
}
It makes the compiler trigger the function multi-version when there exist more than one version for the same function signature.
Supported Syntaxes:
Style | Syntax |
---|---|
GNU | __attribute__((riscv_vector_cc))) |
C++11 | [[riscv::vector_cc]] |
C23 | [[riscv::vector_cc]] |
Functions declared with this attribute will use to the standard vector calling convention variant as defined in the RISC-V psABI, even if the function has vector arguments or a return value.
Intrinsic functions (or intrinsics or built-ins) are expanded into instruction sequences by compilers. They typically provide access to functionality that is otherwise not synthesizable by compilers. Some intrinsics expand to different code sequences depending on the available instructions from the enabled ISA extensions.
Compilers typically come with their own architecture-independent intrinsics (e.g. synchronization primitives, byte-swap, etc.). The RISC-V compiler backend can define additional target-specific intrinsics. Providing functionality via architecture-independent intrinsics is the preferred method, as it improves code portability.
Some intrinsics are only available if a particular header file is included.
RISC-V header files that enable intrinsics require the prefix riscv_
(e.g. riscv_vector.h
or riscv_crypto.h
).
RISC-V specific intrinsics use the common prefix __riscv_
to avoid namespace collisions.
The intrinsic name describes the functional behaviour of the function. In case the functionality can be expressed with a single instruction, the instruction's name (any '.' replaced by '_') is the preferred choice. Note, that intrinsics that are restricted to RISC-V vendor extensions need to include the vendor prefix (as documented in the RISC-V toolchain conventions).
If intrinsics are available for multiple data types, then function overloading is preferred over multiple type-specific functions. In case a function is only available for one data type and this type cannot be derived from the function's name, then the type should be appended to the function name, delimited by a '_' character. Typical type postfixes are "32" (32-bit), "i32" (signed 32-bit), "i8m4" (vector register group consisting of 4 signed 8-bit vector registers).
RISC-V intrinsics follow the following naming rule:
INTRINSIC ::= PREFIX NAME [ '_' TYPE ]
PREFIX ::= "__riscv_"
NAME ::= Name of the intrinsic function.
TYPE ::= Optional type postfix.
RISC-V intrinsics examples:
#include <riscv_vector.h> // make RISC-V vector intrinsics available
vint8m1_t __riscv_vadd_vv_i8m1(vint8m1_t vs2, vint8m1_t vs1, size_t vl); // vadd.vv vd, vs2, vs1
The RISC-V zihintntl extension provides the RISC-V specific intrinsic functions for generating non-temporal memory accesses. These intrinsic functions provide the domain parameter to specify the behavior of memory accesses.
In order to access the RISC-V NTLH intrinsics, it is necessary to
include the header file riscv_ntlh.h
.
The functions are only available if the compiler enables the zihintntl extension.
type __riscv_ntl_load (type *ptr, int domain);
void __riscv_ntl_store (type *ptr, type val, int domain);
There are overloaded functions of __riscv_ntl_load
and __riscv_ntl_store
. When these intrinsic functions omit the domain
argument, the domain
is implied as __RISCV_NTLH_ALL
.
type __riscv_ntl_load (type *ptr);
void __riscv_ntl_store (type *ptr, type val);
The types currently supported are:
- Integer types.
- Floating-point types.
- Fixed-length vector types.
The domain
parameter could pass the following values. Each one is mapped to the specific zihintntl instruction.
enum {
__RISCV_NTLH_INNERMOST_PRIVATE = 2,
__RISCV_NTLH_ALL_PRIVATE,
__RISCV_NTLH_INNERMOST_SHARED,
__RISCV_NTLH_ALL
};
Domain Value | Instruction |
---|---|
__RISCV_NTLH_INNERMOST_PRIVATE |
ntl.p1 |
__RISCV_NTLH_ALL_PRIVATE |
ntl.pall |
__RISCV_NTLH_INNERMOST_SHARED |
ntl.s1 |
__RISCV_NTLH_ALL |
ntl.all |
The Zicbop extension provides the prefetch instruction to allow users to optimize data access patterns by providing hints to the hardware regarding future data accesses. It is supported through a compiler-defined built-in function with three arguments that specify its behavior.
void __builtin_prefetch(const void *addr, int rw, int locality)
The locality for the built-in __builtin_prefetch
function in RISC-V can be achieved using the Non-Temporal Locality Hints (Zihintntl) extension. When a Non-Temporal Locality (NTL) Hints instruction is applied to prefetch instruction, a cache line should be prefetched into a cache level that is higher than the level specified by the NTL.
The following table presents the mapping from the __builtin_prefetch
function to the corresponding assembly instructions assuming the presence of the Zihintntl and Zicbop extensions.
Prefetch function | Assembly |
---|---|
__builtin_prefetch(ptr, 0, 0 /* locality */); |
ntl.all + prefetch.r (ptr) |
__builtin_prefetch(ptr, 0, 1 /* locality */); |
ntl.pall + prefetch.r (ptr) |
__builtin_prefetch(ptr, 0, 2 /* locality */); |
ntl.p1 + prefetch.r (ptr) |
__builtin_prefetch(ptr, 0, 3 /* locality */); |
prefetch.r (ptr) |
In order to access the RISC-V scalar bit manipulation intrinsics, it is
necessary to include the header file riscv_bitmanip.h
.
The functions are only only available if the compiler's -march
string
enables the required ISA extension. (Calling functions for not enabled
ISA extensions will lead to compile-time and/or link-time errors.)
Intrinsics operating on XLEN sized value are not available as there is no type
defined. If xlen_t
is added in the future, this can be revisited.
Unsigned types are used as that is the most logical representation for a collection of bits.
Only 32-bit and 64-bit types are supported. In order to increase compatibility, where it is feasible 32-bit intrinsics will be available on RV64. This will sometimes require additional instructions.
No type overloading is supported. This avoids complications from C integer promotion rules and how to handle signed types.
Sign extension of 32-bit values on RV64 is not reflected in the interface.
Prototype | Instruction | Extension | Notes |
---|---|---|---|
unsigned __riscv_clz_32(uint32_t x); |
clz[w] |
Zbb | |
unsigned __riscv_clz_64(uint64_t x); |
clz |
Zbb (RV64) | |
unsigned __riscv_ctz_32(uint32_t x); |
ctz[w] |
Zbb | |
unsigned __riscv_ctz_64(uint64_t x); |
ctz |
Zbb (RV64) | |
unsigned __riscv_cpop_32(uint32_t x); |
cpop[w] |
Zbb | |
unsigned __riscv_cpop_64(uint64_t x); |
cpop |
Zbb (RV64) | |
uint32_t __riscv_orc_b_32(uint32_t x); |
orc.b |
Zbb | Emulated with orc.b +sext.w on RV64 |
uint64_t __riscv_orc_b_64(uint64_t x); |
orc.b |
Zbb (RV64) | |
uint32_t __riscv_ror_32(uint32_t x, uint32_t shamt); |
ror[i][w] |
Zbb, Zbkb | |
uint64_t __riscv_ror_64(uint64_t x, uint32_t shamt); |
ror[i] |
Zbb, Zbkb (RV64) | |
uint32_t __riscv_rol_32(uint32_t x, uint32_t shamt); |
rol[w] /rori[w] |
Zbb, Zbkb | |
uint64_t __riscv_rol_64(uint64_t x, uint32_t shamt); |
rol /rori |
Zbb, Zbkb (RV64) | |
uint32_t __riscv_rev8_32(uint32_t x); |
rev8 |
Zbb, Zbkb | Emulated with rev8 +srai on RV64 |
uint64_t __riscv_rev8_64(uint64_t x); |
rev8 |
Zbb, Zbkb (RV64) | |
uint32_t __riscv_brev8_32(uint32_t x); |
brev8 |
Zbkb | Emulated with brev8 +sext.w on RV64 |
uint64_t __riscv_brev8_64(uint64_t x); |
brev8 |
Zbkb (RV64) | |
uint32_t __riscv_zip_32(uint32_t x); |
zip |
Zbkb (RV32) | No emulation for RV64 |
uint32_t __riscv_unzip_32(uint32_t x); |
unzip |
Zbkb (RV32) | No emulation for RV64 |
uint32_t __riscv_clmul_32(uint32_t rs1, uint32_t rs2); |
clmul |
Zbc, Zbkc | Emulated with clmul +sext.w on RV64 |
uint64_t __riscv_clmul_64(uint64_t rs1, uint64_t rs2); |
clmul |
Zbc, Zbkc (RV64) | |
uint32_t __riscv_clmulh_32(uint32_t rs1, uint32_t rs2); |
clmulh |
Zbc, Zbkc (RV32) | Emulation on RV64 requires 4-6 instructions |
uint64_t __riscv_clmulh_64(uint64_t rs1, uint64_t rs2); |
clmulh |
Zbc, Zbkc (RV64) | |
uint32_t __riscv_clmulr_32(uint32_t rs1, uint32_t rs2); |
clmulr |
Zbc | Emulation on RV64 requires 4-6 instructions |
uint64_t __riscv_clmulr_64(uint64_t rs1, uint64_t rs2); |
clmulr |
Zbc (RV64) | |
uint32_t __riscv_xperm4_32(uint32_t rs1, uint32_t rs2); |
xperm4 |
Zbkx (RV32) | No emulation for RV64 |
uint64_t __riscv_xperm4_64(uint64_t rs1, uint64_t rs2); |
xperm4 |
Zbkx (RV64) | |
uint32_t __riscv_xperm8_32(uint32_t rs1, uint32_t rs2); |
xperm8 |
Zbkx (RV32) | No emulation for RV64 |
uint64_t __riscv_xperm8_64(uint64_t rs1, uint64_t rs2); |
xperm8 |
Zbkx (RV64) |
In order to access the RISC-V scalar crypto intrinsics, it is necessary to
include the header file riscv_crypto.h
.
The functions are only only available if the compiler's -march
string
enables the required ISA extension. (Calling functions for not enabled
ISA extensions will lead to compile-time and/or link-time errors.)
Unsigned types are used as that is the most logical representation for a collection of bits.
Sign extension of 32-bit values on RV64 is not reflected in the interface.
Prototype | Instruction | Extension | Notes |
---|---|---|---|
uint32_t __riscv_aes32dsi(uint32_t rs1, uint32_t rs2, const int bs); |
aes32dsi |
Zknd (RV32) | bs =[0..3] |
uint32_t __riscv_aes32dsmi(uint32_t rs1, uint32_t rs2, const int bs); |
aes32dsmi |
Zknd (RV32) | bs =[0..3] |
uint64_t __riscv_aes64ds(uint64 rs1, uint64_t rs2); |
aes64ds |
Zknd (RV64) | |
uint64_t __riscv_aes64dsm(uint64 rs1, uint64_t rs2); |
aes64dsm |
Zknd (RV64) | |
uint64_t __riscv_aes64im(uint64 rs1); |
aes64im |
Zknd (RV64) | rnum =[0..10] |
uint64_t __riscv_aes64ks1i(uint64 rs1, const int rnum); |
aes64ks1i |
Zknd, Zkne (RV64) | rnum =[0..10] |
uint64_t __riscv_aes64ks2(uint64 rs1, uint64_t rs2); |
aes64ks2 |
Zknd, Zkne (RV64) | |
uint32_t __riscv_aes32esi(uint32_t rs1, uint32_t rs2, const int bs); |
aes32esi |
Zkne (RV32) | bs =[0..3] |
uint32_t __riscv_aes32esmi(uint32_t rs1, uint32_t rs2, const int bs); |
aes32esmi |
Zkne (RV32) | bs =[0..3] |
uint64_t __riscv_aes64es(uint64 rs1, uint64_t rs2); |
aes32es |
Zkne (RV64) | |
uint64_t __riscv_aes64esm(uint64 rs1, uint64_t rs2); |
aes32esm |
Zkne (RV64) | |
uint32_t __riscv_sha256sig0(uint32_t rs1); |
sha256sig0 |
Zknh | |
uint32_t __riscv_sha256sig1(uint32_t rs1); |
sha256sig1 |
Zknh | |
uint32_t __riscv_sha256sum0(uint32_t rs1); |
sha256sum0 |
Zknh | |
uint32_t __riscv_sha256sum1(uint32_t rs1); |
sha256sum1 |
Zknh | |
uint32_t __riscv_sha512sig0h(uint32_t rs1, uint32_t rs2); |
sha512sig0h |
Zknh (RV32) | |
uint32_t __riscv_sha512sig0l(uint32_t rs1, uint32_t rs2); |
sha512sig0l |
Zknh (RV32) | |
uint32_t __riscv_sha512sig1h(uint32_t rs1, uint32_t rs2); |
sha512sig1h |
Zknh (RV32) | |
uint32_t __riscv_sha512sig1l(uint32_t rs1, uint32_t rs2); |
sha512sig1l |
Zknh (RV32) | |
uint32_t __riscv_sha512sum0r(uint32_t rs1, uint32_t rs2); |
sha512sum0r |
Zknh (RV32) | |
uint32_t __riscv_sha512sum1r(uint32_t rs1, uint32_t rs2); |
sha512sum1r |
Zknh (RV32) | |
uint64_t __riscv_sha512sig0(uint64_t rs1); |
sha512sig0 |
Zknh (RV64) | |
uint64_t __riscv_sha512sig1(uint64_t rs1); |
sha512sig1 |
Zknh (RV64) | |
uint64_t __riscv_sha512sum0(uint64_t rs1); |
sha512sum0 |
Zknh (RV64) | |
uint64_t __riscv_sha512sum1(uint64_t rs1); |
sha512sum1 |
Zknh (RV64) | |
uint32_t __riscv_sm3p0(uint32_t rs1); |
sm3p0 |
Zksh | |
uint32_t __riscv_sm3p1(uint32_t rs1); |
sm3p1 |
Zksh | |
uint32_t __riscv_sm4ed(uint32_t rs1, uint32_t rs2, const int bs); |
sm4ed |
Zksed | bs =[0..3] |
uint32_t __riscv_sm4ks(uint32_t rs1, uint32_t rs2, const int bs); |
sm4ks |
Zksed | bs =[0..3] |
The functions are only available if the compiler's -march
string
enables the required ISA extension. (Calling functions for not enabled
ISA extensions will lead to compile-time and/or link-time errors.)
Intrinsics operating on XLEN sized value are not available as there is no type
defined. If xlen_t
is added in the future, this can be revisited.
Unsigned types are used as that is the most logical representation for a collection of bits.
Sign extension of 32-bit values on RV64 is not reflected in the interface.
Prototype | Instruction | Extension | Notes |
---|---|---|---|
uint32_t __riscv_mopr_32(uint32_t rs1, const int n); |
mop.r.[n] |
Zimop | Emulated with mopr.r.[n] +sext.w on RV64 n =[0..31] |
uint64_t __riscv_mopr_64(uint64_t rs1, const int n); |
mop.r.[n] |
Zimop (RV64) | n =[0..31] |
uint32_t __riscv_moprr_32(uint32_t rs1, uint32_t rs2, const int n); |
mop.rr.[n] |
Zimop | Emulated with mopr.rr.[n] +sext.w on RV64 n =[0..7] |
uint64_t __riscv_moprr_64(uint64_t rs1, uint64_t rs2, const int n); |
mop.rr.[n] |
Zimop (RV64) | n =[0..7] |
This section lists operand constraints that can be used with inline assembly statements, including both RISC-V specific and common operand constraints.
Constraint | Note | |
---|---|---|
m | An address that is held in a general-purpose register with offset. | |
A | An address that is held in a general-purpose register. | |
r | General purpose register | |
f | Floating-point register | |
i | Immediate integer operand | |
I | 12-bit signed immediate integer operand | |
K | 5-bit unsigned immediate integer operand | |
J | Zero integer immediate operand | |
s | symbol or label reference with a constant offset | |
vr | Vector register | |
vd | Vector register, excluding v0 | |
vm | Vector register, only v0 |
NOTE: Immediate value must be a compile-time constant.
The difference between m
and A
is whether the operand can have an offset;
some instructions in RISC-V do not allow an offset for the address operand,
such as atomic or vector load/store instructions.
The following example demonstrates the difference; it is trying
to load value from foo[10]
and using m
and A
to pass that address.
int *foo;
void bar() {
int x;
__asm__ volatile ("lw %0, %1" : "=r"(x) : "m" (foo[10]));
__asm__ volatile ("lw %0, %1" : "=r"(x) : "A" (foo[10]));
}
Then we compile with GCC with -O
option:
$ riscv64-unknown-elf-gcc x.c -o - -O -S
...
bar:
lui a5,%hi(foo)
ld a5,%lo(foo)(a5)
#APP
# 4 "x.c" 1
lw a4, 40(a5)
# 0 "" 2
#NO_APP
addi a5,a5,40
#APP
# 5 "x.c" 1
lw a5, 0(a5)
# 0 "" 2
#NO_APP
ret
The compiler uses an immediate offset of 40 for the m
constraint, but for the
A
constraint uses an extra addi instruction instead.
This section lists operand modifiers that can be used with inline assembly statements, including both RISC-V specific and common operand modifiers.
Modifiers | Description | Note |
---|---|---|
z | Print zero (x0 ) register for immediate 0, typically used with constraints J |
|
i | Print i if corresponding operand is immediate. |
Function multi-versioning(FMV) provides an approach to selecting the appropriate function according to the runtime environment. The final binary may contain all versions of the function, with the compiler generating all supported versions and the runtime selecting the appropriate one.
This feature is triggered by target_version/target_clones
function attribute.