The new A64 instruction set is similar to the existing A32 instruction set. Instructions are 32 bits wide and have similar syntax.
The instruction sets use a generic naming convention within the ARMv8 architecture, so that the original 32-bit instruction set states are now called:
When in AArch32 state, the instruction set is largely compatible with ARMv7, though there are differences. See, ARMv8-A Architecture Reference Manual. It also provides some new instructions to align with some of the features that are introduced in the A64 instruction set.
The Thumb instruction set was first included in the ARM7TDMI processor and originally contained only 16-bit instructions. 16-bit instructions gave much smaller programs at the cost of some performance. ARMv7 processors, including those in the Cortex-A series, support Thumb-2 technology, which extends the Thumb instruction set to provide a mix of 16-bit and 32-bit instructions. This gives performance similar to that of ARM, while retaining the reduced code size. Because of its size and performance advantages, it is increasingly common for all 32-bit code to be compiled or assembled to take advantage of Thumb-2 technology.
A new instruction set has been introduced that the core can use when in AArch64 state. In keeping with the naming convention, and reflecting the 64-bit operation, this instruction set is called:
A64 provides similar functionality to the A32 and T32 instruction sets in AArch32 or ARMv7. The design of the new A64 instruction set allowed several improvements:
- A consistent encoding scheme
The late addition of some instructions in A32 resulted in some inconsistency in the encoding scheme. For example,
STRsupport for halfwords is encoded slightly differently to the mainstream byte and word transfer instructions. The result of this is that the addressing modes are slightly different.
- Wide range of constants
A64 instructions provide a huge range of options for constants, each tailored to the requirements of specific instruction types.
Arithmetic instructions generally accept a 12-bit immediate constant.
Logical instructions generally accept a 32-bit or 64-bit constant, which has some constraints in its encoding.
MOVinstructions accept a 16-bit immediate, which can be shifted to any 16-bit boundary.
Address generation instructions are geared to addresses aligned to a 4KB page size.
There are slightly more complex rules for constants that are used in bit manipulation instructions. However, bitfield manipulation instructions can address any contiguous sequence of bits, in either the source or destination operand.
A64 provides flexible constants, but encoding them, even determining whether a particular constant can be legally encoded in a particular context, can be non-trivial.
- Data types are easier
A64 deals naturally with 64-bit signed and unsigned data types in that it offers more concise and efficient ways of manipulating 64-bit integers. This can be advantageous for all languages which provide 64-bit integers such as C or Java.
- Long offsets
A64 instructions generally provide longer offsets, both for PC-relative branches and for offset addressing.
The increased branch range makes it easier to manage inter-section jumps. Dynamically generated code is generally placed on the heap so it can, in practice, be located anywhere. The runtime system finds it much easier to manage this with increased branch ranges, and fewer fix-ups are required.
The need for literal pools (blocks of literal data embedded in the code stream) has long been a feature of ARM instruction sets. This still exists in A64. However, the larger PC-relative load offset helps considerably with the management of literal pools, making it possible to use one per compilation unit. This removes the need to manufacture locations for multiple pools in long code sequences.
Pointers are 64-bit in AArch64, which allows larger amounts of virtual memory to be addressed and gives more freedom for address mapping. However, using 64-bit pointers does incur some costs. The same piece of code typically uses more memory when running with 64-pointers than with 32-bit pointers. Each pointer is stored in memory and requires eight bytes instead of four. This might sound trivial, but can add up to a significant penalty. Additionally, the increased use of memory space that is associated with a move to 64 bits can cause a drop in the number of accesses that hit in cache. This drop of cache hits can reduce performance.
Some languages can be implemented with compressed pointers, such as Java, to circumvent the performance issue.
- Conditional constructs are used instead of IT blocks
IT blocks are a useful feature of T32, enabling efficient sequences that avoid the need for short forward branches around unexecuted instructions. However, they are sometimes difficult for hardware to handle efficiently. A64 removes these blocks and replaces them with conditional instructions such as
CSEL, or Conditional Select and
CINC, or Conditional Increment. These conditional constructs are more straightforward and easier to handle without special cases.
- Shift and rotate behavior is more intuitive
The A32 or T32 shift and rotate behavior does not always map easily to the behavior expected by high-level languages.
ARMv7 provides a barrel shifter that can be used as part of data processing instructions. However, specifying the type of shift and the amount to shift requires a certain number of opcode bits, which could be used elsewhere.
A64 instructions therefore remove options that were rarely used, and instead adds new explicit instructions to carry out more complicated shift operations.
- Code generation
When generating code, both statically and dynamically, for common arithmetic functions, A32 and T32 often require different instructions, or instruction sequences. This is to cope with different data types. These operations in A64 are much more consistent so it is much easier to generate common sequences for simple operations on differently sized data types.
For example, in T32 the same instruction can have different encodings depending on what registers are used (either a low register or a high register).
The A64 instruction set encodings are much more regular and rationalized. Consequently, an assembler for A64 typically requires fewer lines of code than an assembler for T32.
- Fixed-length instructions
All A64 instructions are the same length, unlike T32, which is a variable-length instruction set. This makes management and tracking of generated code sequences easier, particularly affecting dynamic code generators.
- Three operands map better
A32, in general, preserves a true three-operand structure for data-processing operations. T32, on the other hand, contains a significant number of two-operand instruction formats, which make it slightly less flexible when generating code. A64 sticks to a consistent three-operand syntax, which further contributes to the regularity and homogeneity of the instruction set for the benefit of compilers.