# Arm64 Intrinsics

This document is intended to document proposed design decisions related to the introduction
of Arm64 Intrinsics

## Document Goals

+ Discuss design options
  + Document existing design pattern
  + Draft initial design decisions which are least likely to cause extensive rework
+ Decouple `X86`, `X64`, `ARM32` and `ARM64` development
  + Make some minimal decisions which encourage API similarity between platforms
  + Make some additional minimal decisions which allow `ARM32` and `ARM64` API's to be similar
+ Decouple CoreCLR implementation and testing from API design
+ Allow for best API design
+ Keep implementation simple

## Intrinsics in general

Use of intrinsics in general is a CoreCLR design decision to allow low level platform
specific optimizations.

At first glance, such a decision seems to violate the fundamental principles of .NET
code running on any platform.  However, the intent is not for the vast majority of
apps to use such optimizations.  The intended usage model is to allow library
developers access to low level functions which enable optimization of key
functions.  As such the use is expected to be limited, but performance critical.

## Intrinsic granularity

In general individual intrinsic will be chosen to be fine grained.  These will generally
correspond to a single assembly instruction.

## Logical Sets of Intrinsics

For various reasons, an individual CPU will have a specific set of supported instructions.  For `ARM64` the
set of supported instructions is identified by various `ID_* System registers`.
While these feature registers are only available for the OS to access, they provide
a logical grouping of instructions which are enabled/disabled together.

### API Logical Set grouping & `IsSupported`

The C# API must provide a mechanism to determine which sets of instructions are supported.
Existing design uses a separate `static class` to group the methods which correspond to each
logical set of instructions.  A single `IsSupported` property is included in each `static class`
to allow client code to alter control flow.  The `IsSupported` properties are designed so that JIT
can remove code on unused paths.  `ARM64` will use an identical approach.

### API `PlatformNotSupported` Exception

If client code calls an intrinsic which is not supported by the platform a `PlatformNotSupported`
exception must be thrown.

### JIT, VM, PAL & OS requirements

The JIT must use a set of flags corresponding to logical sets of instructions to alter code
generation.

The VM must query the OS to populate the set of JIT flags.  For the special altJit case, a
means must provide for setting the flags.

PAL must provide an OS abstraction layer.

Each OS must provide a mechanism for determining which sets of instructions are supported.

+ Linux provides the HWCAP detection mechanism which is able to detect current set of exposed
features
+ Arm64 MAC OS and Arm64 Windows OS must provide an equally capable detection mechanism.

In the event the OS fails to provides a means to detect a support for an instruction set extension
it must be treated as unsupported.

NOTE: Exceptions might be where:

+ CoreCLR is distributed as source and CMake build configuration test is used to detect these features
+ Installer detects features and sets appropriate configuration knobs
+ VM runs code inside safe try/catch blocks to test for instruction support
+ Platform requires a specific minimum set of instructions

### Intrinsics & Crossgen

For any intrinsic which may not be supported on all variants of a platform, crossgen method
compilation should be designed to allow optimal code generation.

Initial implementation will simply trap so that the JIT is forced to generate optimal platform dependent code at
runtime.  Subsequent implementations may use different approaches.

## Choice of Arm64 naming conventions

`x86`, `x64`, `ARM32` and `ARM64` will follow similar naming conventions.

### Namespaces

+ `System.Runtime.Intrinsics` is used for type definitions useful across multiple platforms
+ `System.Runtime.Intrinsics.Arm` is used type definitions shared across `ARM32` and `ARM64` platforms
+ `System.Runtime.Intrinsics.Arm.Arm64` is used for type definitions for the `ARM64` platform
  + The primary implementation of `ARM64` intrinsics will occur within this namespace
  + While `x86` and `x64` share a common namespace, this document is recommending a separate namespace
  for `ARM32` and `ARM64`.  This is because `AARCH64` is a separate `ISA` from the `AARCH32` `Arm` & `Thumb`
  instruction sets.  It is not an `ISA` extension, but rather a new `ISA`.  This is different from `x64`
  which could be viewed as a superset of `x86`.
  + The logical grouping of `ARM64` and `ARM32` instruction sets is different.  It is controlled by
  different sets of `System Registers`.

For the convenience of the end user, it may be useful to add convenience API's which expose functionality
which is common across platforms and sets of platforms.  These could be implemented in terms of the
platform specific functionality.  These API's are currently out of scope of this initial design document.

### Logical Set Class Names

Within the `System.Runtime.Intrinsics.Arm.Arm64` namespace there will be a separate `static class` for each
logical set of instructions

The sets will be chosen to match the granularity of the `ARM64` `ID_*` register fields.

#### Specific Class Names

The table below documents the set of known extensions, their identification, and their recommended intrinsic
class names.

| ID Register      | Field   | Values   | Intrinsic `static class` name |
| ---------------- | ------- | -------- | ----------------------------- |
| N/A              | N/A     | N/A      | Base                          |
| ID_AA64ISAR0_EL1 | AES     | (1b, 10b)| Aes                           |
| ID_AA64ISAR0_EL1 | Atomic  | (10b)    | Atomics                       |
| ID_AA64ISAR0_EL1 | CRC32   | (1b)     | Crc32                         |
| ID_AA64ISAR1_EL1 | DPB     | (1b)     | Dcpop                         |
| ID_AA64ISAR0_EL1 | DP      | (1b)     | Dp                            |
| ID_AA64ISAR1_EL1 | FCMA    | (1b)     | Fcma                          |
| ID_AA64PFR0_EL1  | FP      | (0b, 1b) | Fp                            |
| ID_AA64PFR0_EL1  | FP      | (1b)     | Fp16                          |
| ID_AA64ISAR1_EL1 | JSCVT   | (1b)     | Jscvt                         |
| ID_AA64ISAR1_EL1 | LRCPC   | (1b)     | Lrcpc                         |
| ID_AA64ISAR0_EL1 | AES     | (10b)    | Pmull                         |
| ID_AA64PFR0_EL1  | RAS     | (1b)     | Ras                           |
| ID_AA64ISAR0_EL1 | SHA1    | (1b)     | Sha1                          |
| ID_AA64ISAR0_EL1 | SHA2    | (1b, 10b)| Sha2                          |
| ID_AA64ISAR0_EL1 | SHA3    | (1b)     | Sha3                          |
| ID_AA64ISAR0_EL1 | SHA2    | (10b)    | Sha512                        |
| ID_AA64PFR0_EL1  | AdvSIMD | (0b, 1b) | Simd                          |
| ID_AA64PFR0_EL1  | AdvSIMD | (1b)     | SimdFp16                      |
| ID_AA64ISAR0_EL1 | RDM     | (1b)     | SimdV81                       |
| ID_AA64ISAR0_EL1 | SM3     | (1b)     | Sm3                           |
| ID_AA64ISAR0_EL1 | SM4     | (1b)     | Sm4                           |
| ID_AA64PFR0_EL1  | SVE     | (1b)     | Sve                           |

The `All`, `Simd`, and `Fp` classes will together contain the bulk of the `ARM64` intrinsics.  Most other extensions
will only add a few instruction so they should be simpler to review.

The `Base` `static class` is used to represent any intrinsic which is guaranteed to be implemented on all
`ARM64` platforms.  This set will include general purpose instructions.  For example, this would include intrinsics
such as `LeadingZeroCount` and `LeadingSignCount`.

As further extensions are released, this set of intrinsics will grow.

### Intrinsic Method Names

Intrinsics will be named to describe functionality.  Names will not correspond to specific named
assembly instructions.

Where precedent exists for common operations within the `System.Runtime.Intrinsics.X86` namespace, identical method
names will be chosen: `Add`, `Multiply`, `Load`, `Store` ...

Where `ARM` naming convention differs substantially from `XARCH`, `ARM` naming conventions will sometimes be preferred.
For instance

+ `ARM` uses `Replicate` or `Duplicate` rather than X86 `Broadcast`.
+ `ARM` uses `Across` rather than `X86` `Horizontal`.

These will need to reviewed on a case by case basis.

It is also worth noting `System.Runtime.Intrinsics.X86` naming conventions will include the suffix `Scalar` for
operations which take vector argument(s), but contain an implicit cast(s) to the base type and therefore operate only
on the first item of the argument vector(s).

### Intrinsic Method Argument and Return Types

Intrinsic methods will typically use a standard set of argument and return types:

+ Integer type: `byte`, `sbyte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`
+ Floating types: `double`, `single`, `System.Half`
+ Vector types: `Vector128<T>`, `Vector64<T>`
+ SVE will add new vector types: TBD
+ `ValueTuple<>` for return types returning multiple values

It is proposed to add the `Vector64<T>` type.  Most `ARM64` instructions support 8 byte and 16 byte forms.  8 byte
operations can execute faster with less power on some platforms. So adding `Vector64<T>` will allow exposing the full
flexibility of the instruction set and allow for optimal usage.

Some intrinsics will need to produce multiple results.  The most notable are the structured load operations `LD2`,
`LD3`, `LD4` ...  For these operations it is proposed that the intrinsic API return a `ValueTuple<>` of `Vector64<T>` or
`Vector128<T>`

#### Literal immediates

Some assembly instructions require an immediate encoded directly in the assembly instruction.  These need to be
constant at JIT time.

While the discussion is still on-going, consensus seems to be that any intrinsic must function correctly even when its
arguments are not constant.

## Intrinsic Interface Documentation

+ Namespace
+ Each `static class` will
  + Briefly document corresponding `System Register Field and Value` from ARM specification.
  + Document use of IsSupported property
  + Optionally summarize set of methods enabled by the extension
+ Each intrinsic method will
  + Document underlying `ARM64` assembly instruction
  + Optionally, briefly summarize operation performed
    + In many cases this may be unnecessary: `Add`, `Multiply`, `Load`, `Store`
    + In some cases this may be difficult to do correctly. (Crypto instructions)
  + Optionally mention corresponding compiler gcc, clang, and/or MSVC intrinsics
    + Review of existing documentation shows `ARM64` intrinsics are mostly absent or undocumented so
    initially this will not be necessary for `ARM64`
    + See gcc manual "AArch64 Built-in Functions"
    + MSVC ARM64 documentation has not been publicly released

## Phased Implementation

### Implementation Priorities

As rough guidelines for order of implementation:

+ Baseline functionality will be prioritized over architectural extensions
+ Architectural extensions will typically be prioritized in age order.  Earlier extensions will be added first
  + This is primarily driven by availability of hardware.  Features released in earlier will be prevalent in
  more hardware.
+ Priorities will be driven by optimization efforts and requests
  + Priority will be given to intrinsics which are equivalent/similar to those actively used in libraries for other
  platforms
  + Priority will be given to intrinsics which have already been implemented for other platforms

### API review

Intrinsics will extend the API of CoreCLR.  They will need to follow standard API review practices.

Initial XArch intrinsics are proposed to be added to the `netcoreapp2.1` Target Framework.  ARM64 intrinsics will
be in similar Target Frameworks as the XArch intrinsics.

Each review will identify the Target Framework API version where the API will be extended and released.

#### API review of an intrinsic `static class`

Given the need to add hundreds or thousands of intrinsics, it will be helpful to review incrementally.

A separate GitHub Issue will typically created for the review of each intrinsic `static class`.

When the `static class` exceeds a few dozen methods, it is desirable to break the review into smaller more manageable
pieces.

The extensive set of ARM64 assembly instructions make reviewing and implementing an exhaustive set a long process.
To facilitate incremental progress, initial intrinsic API for a given `static class` need not be exhaustive.

### Partial implementation of intrinsic `static class`

+ `IsSupported` must represent the state of an entire intrinsic `static class` for a given Target Framework.
+ Once API review is complete and approved, it is acceptable to implement approved methods in any order.
+ The approved API must be completed before the intrinsic `static class` is included in its Target Framework release

## Test coverage

As intrinsic support is added test coverage must be extended to provide basic testing.

## LSRA changes to allocate contiguous register ranges

Some ARM64 instructions will require allocation of contiguous blocks of registers.  These are likely limited to load and
store multiple instructions.

It is not clear if this is a new LSRA feature and if it is how much complexity this will introduce into the LSRA.

## ARM ABI Vector64<T> and Vector128<T>

For intrinsic method calls, these vector types will implicitly be treated as pass by vector register.

For other calls, ARM64 ABI conventions must be followed.  For purposes of the ABI calling conventions, these vector
types will treated as composite struct type containing a contiguous array of `T`.  They will need to follow standard
struct argument and return passing rules.

## Half precision floating point

This document will refer to half precision floating point as `Half`.

+ Machine learning and Artificial intelligence often use `Half` type to simplify storage and improve processing time.
+ CoreCLR and `CIL` in general do not have general support for a `Half` type
+ There is an open request to expose `Half` intrinsics
+ There is an outstanding proposal to add `System.Half` to support this request
https://github.com/dotnet/runtime/issues/936
+ Implementation of `Half` features will be adjusted based on
  + Implementation of the `System.Half` proposal
  + Availability of supporting hardware (extensions)
  + General language extensions supporting `Half`

**`Half` support is currently outside the scope of the initial design proposal.  It is discussed below only for
introductory purposes.**

### ARM64 Half precision support

ARM64 supports two half precision floating point formats

+ IEEE-754 compliant.
+ ARM alternative format

The two formats are similar.  IEEE-754 has support for Inifinity and NAN and therefore has a somewhat smaller range.
IEEE-754 should be preferred.

ARM64 baseline support for `Half` is limited.  The following types of operations are supported

+ Loads and Stores
+ Conversion to/from `Float`
+ Widening from `Vector128<Half>` to two `Vector128<Float>`
+ Narrowing from two `Vector128<Float>` to `Vector128<Half>`

The optional ARMv8.2-FP16 extension adds support for

+ General operations on IEEE-754 `Half` types
+ Vector operations on IEEE-754 `Half` types

These correspond to the proposed `static class`es `Fp16` and `SimdFp16`

### `Half` and ARM64 ABI

Any complete `Half` implementation must conform to the `ARM64 ABI`.

The proposed `System.Half` type must be treated as a floating point type for purposes of the ARM64 ABI

As an argument it must be passed in a floating point register.

As a structure member, it must be treated as a floating point type and enter into the HFA determination logic.

Test cases must be written and conformance must be demonstrated.

## Scalable Vector Extension Support

`SVE`, the Scalable Vector Extension introduces its own complexity.

The extension

+ Creates a set of `Z0-Z31` scalable vector registers.  These overlay existing vector registers.  Each scalar vector
register has a platform specific length
  + Any multiple of 128 bits up to 2048 bits
+ Creates a new set of `P0-P15` predicate registers.  Each predicate register has a platform specific length which is
1/8th of the scalar vector length.
+ Add an extensive set of instructions including complex load and store operations.
+ Modifies the ARM64 ABI.

Therefore implementation will not be trivial.

+ Register allocator will need changes to support predicate allocation
+ SIMD support will face similar issues
+ Open issue: Should we use `Vector<T>`, `Vector128<t>, Vector256<t>, ... Vector2048<T>`, `SVE<T>` ... in user interface
design?
  + Use of `Vector128<t>, Vector256<t>, ... Vector2048<T>` is current default proposal.
Having 16 forms of every API may create issues for framework and client developers.
However generics may provide some/sufficient relief to make this acceptable.
  + Use of `Vector<T>` may be preferred if SVE will also be used for `FEATURE_SIMD`
  + Use of `SVE<T>` may be preferred if SVE will not be used for `FEATURE_SIMD`


Given lack of available hardware and a lack of thorough understanding of the specification:

+ SVE will require a separate design
+ **SVE is considered out of scope for this document.  It is discussed above only for
introductory purposes.**

## Miscellaneous
### Handling Instruction Deprecation

Deprecation of instructions should be relatively rare

+ Do not introduce an intrinsic for a feature that is currently deprecated
+ In event an assembly instruction is deprecated
  1. Prefer emulation using alternate instructions if practical
  2. Add `SetThrowOnDeprecated()` interface to allow developers to find these issues

## Approved APIs

The following sections document APIs which have completed the API review process.

Until each API is approved it shall be marked "TBD Not Approved"

### `All`

TBD Not approved

### `Aes`

TBD Not approved

### `Atomics`

TBD Not approved

### `Crc32`

TBD Not approved

### `Dcpop`

TBD Not approved

### `Dp`

TBD Not approved

### `Fcma`

TBD Not approved

### `Fp`

TBD Not approved

### `Fp16`

TBD Not approved

### `Jscvt`

TBD Not approved

### `Lrcpc`

TBD Not approved

### `Pmull`

TBD Not approved

### `Ras`

TBD Not approved

### `Sha1`

TBD Not approved

### `Sha2`

TBD Not approved

### `Sha3`

TBD Not approved

### `Sha512`

TBD Not approved

### `Simd`

TBD Not approved

### `SimdFp16`

TBD Not approved

### `SimdV81`

TBD Not approved

### `Sm3`

TBD Not approved

### `Sm4`

TBD Not approved

### `Sve`

TBD Not approved
