This is a bit early, but as the Clever-ISA project currently has a functional* toolchain for assembly, and the emulator should be close to complete, I would like to get it looked at for future inclusion in rust.
Clever-ISA is an architecture I have been developing a specfication for since June, 2021. It is a 64-bit CISC architecture, intended to be an x86 substitute. As it applies to rust, the specification prescribes a number of recommendations intended to unify toolchains producing or handling machine code, this intended to describe those recommendations as applied to rust that I would seek approval on when bringing the target formally, which will occur after a fork of rustc is capable of compiling to the target.
*Disclaimer: a fork of GNU binutils does not yet provide an assembler, and https://github.com/LightningCreations/lc-binutils is required for that (and there are some bugs to debug wrt. that, but I expect them to be solved soon).
The Proposal is:
Clever-ISA Target Architecture
Document References
All documents in this section reference either a extension specification or a technical document from the https://github.com/Clever-ISA/Clever-ISA repository, in the specs/
folder from the root. Documents labeled with an X-
prefix refer to an extension which is a normative part of the Clever-ISA specification binding on implementations. Documents labeled with a D-
prefix refer to a technical document that prescribes some forms of interaction with the architecture. Each named document is unique between types, and can be found in markdown form using the name of the document without a prefix within the specs folder. For example, X-main
refers to the document https://github.com/Clever-ISA/Clever-ISA/blob/main/specs/main.md.
Target Names
The name of the architecture in target tuples is clever
, or alternatively clever
with a version suffix attached, such as clever1.0
, to refer to the target features that should be enabled or disabled. The canonical name clever
should be used in #[cfg(target_arch)]
. Per D-toolchain's target names section, only a published version of the specification should be accepted with an explicit version name.
Target Features
Per D-toolchain, the list of target features is the same as the named list of extensions, excluding X-main which is always presumed to be available (as all implementations must implement X-main by definition). Each target feature name enables the instructions from the corresponding extension. For example, -C target-feature="+vector"
would enable instructions from the X-vector
extension.
By their nature, any extension that has been accepted by is not stablized in a public release is both subject to change or removal according to the governance process of Clever-ISA. It is recommended that those extensions be feature-gated for the #[target_feature]
attribute until a version containing them is placed in a feature-freeze status (or at the very least, extension-freeze status when the list of extensions is stablized but the contents are not yet frozen or stable).
ABI
The C abi for Clever-ISA is prescribed by D-abi. extern "C"
, primitive types, and the vector type has the ABI prescribed by D-abi.
In particular, u128
and i128
have alignment 16 on this target.
Wrt. binaries, shared objects, etc. ELF is used. D-abi also prescribes the processor-specific portions for ELF applicable to the target
ABI impact on repr(Rust)/extern "Rust"
While this is non-binding, it is notably unnecessary for extern "Rust"
to pass vector types on Clever-ISA indirectly, as the ABI for these types is well-specified to always use the integer registers.
core::arch::clever
Intrinsics for clever would be provided by the standard library under core::arch::clever
.
The intrinsics are specified in the Intrinsics Section of D-toolchain, and would have the appropriate signatures adapted for rust. I will not list the exhaustive list for want of a (relatively) short proposal, but I will call out some specific ones with a special signature:
#[target_feature(enable="vector")]
extern "platform-intrinsic" unsafe fn __vec_unary<const OP: VecUnaryOp, const ELEM_SIZE: usize, E: Vector>(vec: &mut E);
#[target_feature(enable="vector")]
extern "platform-intrinsic" unsafe fn __vec_binary<const OP: VecBinaryOp, const ELEM_SIZE: usize, D: VectorOrScalar, S: VectorOrScalar>(a: &mut D, b: S);
#[target_feature(enable="vector")]
extern "platform-intrinsic" unsafe fn __vec_ternary<const OP: VecTernaryOp, const ELEM_SIZE: usize, D: Vector, S1: VectorOrScalar, S2: VectorOrScalar>(a: &mut D, b: S, c: S);
The enums VecUnaryOp
, VecBinaryOp
, VecTernaryOp
are #[repr(u16)]
enums that list each instruction by name and sets the discriminant to the instructions canonical opcode (For example, VecBinaryOp::Add
would have a discriminant of 0x001
- do not use specialization opcodes). The input types to each intrinsic must have at least one Vector type, __vec128
, __vec64
, __vec32
, __vec16
, and __vec8
). ELEM_SIZE must be a power of two that is at most the size of the largest vector type.
The vector types defined in the section have alignment equal to the size, except for __vec256
which is 16-byte aligned. core::arch::clever
should also have a f16
type (possibly with a different name) for use with 16-bit floating-point intrinsics.
Inline Assembly
Inline assembly on Clever-ISA uses (exclusively) the generic syntax specified by D-asm, with the directives specified by rust inline-assembly, except that the .quad
directive is deprecated. The canonical reference assembler is lc-as
from the lc-binutils project, but only the subset specified by D-asm is required.
The register classes and register names are as prescribed by D-toolchain, namely:
-
reg
which refers to a general purpose register (r0-r15), excluding the stack pointer (r7), and the base pointer (r6), -
freg
which refers to a floating-point register (f0-f7), -
veclo
which refers to a low-half vector register (v0l-v15l), -
vechi
which refers to a hi-half vector register (v0h-v15h), -
vechalf
which refers to a vector half register, eitherveclo
orvechi
, -
vec
which refers to a vector register pair (v0-v15)
Other than the vector pair registers, there are no overlapping register. A vector pair overlaps with both of its constitutent vector-half registers, but the vector-half registers don't overlap with the others. If a register operand is of clas vec
, vechalf
, veclo
, or vechi
, using the v
specifier refers to the vector pair, using the h
specifier refers to the hi half of the vector pair, and using the l
specifier refers to the lo half of the vector pair. Specifiers are not used for any other kind of register, as in-assembly size specifiers are used to refer to smaller portions of a standard register.
Each register, except for the full vec
registers are 64-bit, but can store any value up to 64-bit (note that the entire register is clobbered in this case, as moving into a smaller portion of any register zeroes the higher portion).
The following registers are assumed not to be modified when the option preserves_flags
is specified:
flags
-
fpcw
(unless the function does not have thefloat
extension enabled).fpcw.RND
,fpcw.EMASK
,fpcw.EMASKALL
, andfpcw.XOPSS
are assumed not the be modified regardless ofpreserves_flags
setting unless thefloat
extension is not enabled.
Additionally, the mode
register, stack pointer (r7), and base pointer (r6), are always assumed not to be modified. Other standard inline-assembly rules also apply.
Edit: Corrected r5 to r15.