proj-plbook-plChTargetLanguagesConcordance2

Continued from Target Languages Concordance part I

Concordance of instructions supported by two platforms

In addition to the above, instructions or intrinsics for each of the following is provided by two platforms in this study:

Arithmetic:

Memory access:

Stack ops:

Atomics and Sync:

Control flow:

Data structures:

Misc:

Arithmetic

Constant loads

LuaJIT?2 and CIL have instructions to load various higher-level data structures such as strings.

JVM and LuaJIT?2 use constant tables/constant pools. (i think that LuaJIT?2's constant load instructions can be used for both immediate constants and constant tables, depending on if their argumens is negative or not). Note: CIL does not have a runtime-accessible constant table; it has a constant table for use at compile-time but "Compilers inspect this information, at compile time, when importing metadata, but the value of the constant itself, if used, becomes embedded into the CIL stream the compiler emits. There are no CIL instructions to access the Constant table at runtime" ([1] section II.22.9, page 216).

JVM provides instructions to push constants of null, or 0,1 sometimes 2, or -1...5, depending on type. CIL provides instructions to load -1..8 to i32 as well as null.

PC-relative instructions

RISC-V has an instruction to load PC-relative constant, and ARM has PC-relative addition.

Andreas Olofsson of Adapteva said on a blog that he is "not convinced that ((AUIPC)) is essential" [2], however a commenter, Chris, from the RISC-V project explained that it was useful [3].

Add, subtract, multiply, divide

LLVM, CIL, ARM have 32-bit signed integer addition and subtraction with overflow. Only LLVM, CIL have multiplication with overflow, as well as unsigned and 64-bit variants of addition, subtraction, multiplication with overflow.

LLVM offers 'constrained' floating point operations, which means that the rounding and exception modes are respected when those instructions are used. RISC-V provides a similar facility through its rounding mode and exception mode special registers; except that RISC-V does not provide a floating-point mod/remainder instruction.

integer addition with overflow (64-bit and unsigned variants):

integer subtraction with overflow (64-bit and unsigned variants):

integer multiplication, lower bits, with overflow:

floating point constrained operations (similar to corresponding RISC-V operations, not shown):

Moves (copies)

None.

Shifts

WASM and ARM provide right rotate.

Rotate right:

Logical

None.

Compares

WASM and LLVM provide inequality (integer and floating point), integer greater-than-or-equal-to, less-than-or-equal-to, floating point greater-than-or-equal-to (they also provide that for integer, but so do other platforms, so those are listed above rather than here).

Note that some other platforms provide these operations, but only as branches rather than vanilla compares.

Integer inequality:

Integer greater-than-or-equal-to, less-than-or-equal-to:

Floating point:

Misc integer arith

CLZ, CTZ, POPCNT are provided by WASM and LLVM (but only as LLVM intrinsics).

LLVM and ARM provide various byteswaps.

byte swap:

Floating-point-specific

WASM and LLVM provide abs(-olute-value), although LLVM abs is an intrinsic.

RISC-V and LLVM provide rounding in the form of conversion operations and rounding modes (LLVM requires the 'constrained' intrinsics to use these). WASM and LLVM provide rounding in the form of ceil, floor, trunc, nearest instructions (but WASM does not provide rounding mode control for other instructions; see [4] and [5]).

RISC-V and LLVM support IEEE exception flags and rounding modes (but LLVM only supports these with 'constrained' intrinsics. Because two platforms support this functionality it is included here, but note that some of the LLVM 'constrained' intrinsics are listed above, in the section 'Add, subtract, multiply, divide'.

Andreas Olofsson of Adapteva noted in a blog post that the Epiphany ISA does not include operations like RISC-V's exception flags, coercion operations, and rounding modes, because they were not needed for Epiphany's use case [6].

RISC-V provides an FCLASS instruction to report the attributes of a floating-point number. Andreas Olofsson of Adapteva said in a blog post that he felt that this instruction was "not essential" [7]. CIL provides ckfinite to check if a floating-point number is finite.

Fused multiply-add is provided by RISC-V and LLVM (but only as an LLVM intrinsic).

pow is provided by LLVM as an intrinsic and by LuaJIT?2.

some notes on the default rounding mode of other platforms: " WebAssembly? uses “non-stop” mode, and floating point exceptions are not otherwise observable. In particular, neither alternate floating point exception handling attributes nor the non-computational operators on status flags are supported. There is no observable difference between quiet and signalling NaN?. However, positive infinity, negative infinity, and NaN? are still always produced as result values to indicate overflow, invalid, and divide-by-zero conditions, as specified by IEEE 754-2008.

WebAssembly? uses the round-to-nearest ties-to-even rounding attribute, except where otherwise specified. Non-default directed rounding attributes are not supported. " -- [8]

" By default, LLVM optimization passes assume that the rounding mode is round-to-nearest and that floating-point exceptions will not be monitored. Constrained FP intrinsics are used to support non-default rounding modes and accurately preserve exception behavior " -- [9]

Fused multiply-add

Signs

absolute value:

Rounding

floor, ceiling, trunc:

nearest int or rounding-mode determined rounding:

FRM (3-bit floating point rounding mode):

Classify

Floating-point exceptions

FFLAGS (5-bit Accrued Exception Flags):

Pow

Misc floating-point

FCSR (32-bit floating point status and control register):

Conversions

LLVM and ARM provide instructions to convert from 8-bit or 16-bit quantities to larger ones.

Integer conversions from 8-bit or 16-bit to larger

sign extend:

zero extend:

Memory access

Loads and stores

Variable loads and stores

WASM and LuaJIT?2 support loads and stores to/from global variables. JVM and CIL provide short instructions to load/store the first 4 variables.

locals:

globals:

Stack ops

JVM, CIL provide dup.

dup:

Atomics and Sync

RISC-V and LLVM provide the AMOs (Atomic Memory Operations): SWAP, ADD, AND, OR, XOR, MIN, MAX, MINU, MAXU.

RISC-V provides load-release/store-conditional. LLVM provides compare-and-swap. These are different operations but in some sense they are similar in that each can be used as a primitive upon which to build synchronization/consensus/atomicity.

C/C++ are not included in this concordance, but it's worth noting that (as of 2019) C/C++ atomics [10] [11] offer the following atomics (this is a rough summary): (compare and) exchange, load, store, test-and-set flag, clear flag, add, sub, or, xor, and, fence.

Non-Cortex ARM instructions are not included in this concordance, but ARMv8.1-A provides the AMOs: SWP, CAS, LDADD, LDCLR, LDEOR, LDSET, LDSMAX, LDSMIN, LDUMAX, LDUMIN ([12] slide 14) (and in [13] there is also talk of MemAtomicOp?_BIC (result = data AND NOT(value)) and MemAtomicOp?_ORR (result = data OR value), so perhaps those were already in there in some other update?).

Note that the intersection of the C/C++ AMOs with ARMv8.1-A AMOs with the set of AMOs provided by RISC-V and LLVM is (if you assume that ARM's BIC corresponds to an AND): swap, add, and, or, xor.

RISC-V Geneology lists only AMOSWAP and AMOADD as appearing "in at least three" of the instruction set architectures under consideration in that paper.

load-release and store-conditional (LR/SC)

Compare-and-swap (CAS)

AMOs

swap:

add:

xor:

and:

or:

min:

unsigned min:

max:

unsigned max:

Control flow

RISC-V and ARM (all of the hardware processor ISAs included in this study) both provide unconditional jump instructions which also place the source address in a 'link register'. In fact, all of RISC-V unconditional jumps do this. JVM used to provide a similar instruction with JSR, but this has been effectively deprecated.

RISC-V and ARM (all of the hardware processor ISAs included in this study) both provide unconditional indirect branch instructions to an arbitrary location in code. The other platforms do provide indirect jumps, but either require an enumerated set of all potential destinations, or provide indirect jumps for higher-level constructs only (such as LuaJIT?2, which provides the CALL instruction to call a function).

Jumps (unconditional branch)

Jump to immediate / direct branch

Jump to register / indirect branch

Jump to register / indirect branch

Conditional branches

ARM and JVM provide <0, >=0.

Note: Instead of many conditional branches, WASM and LLVM instead provide separate comparison ops, and boolean conditional branch (listed above rather than here, because boolean conditional branch is instead grouped with branch-if-not-zero, which is offered by more than two platforms).

unary compare <0:

unary compare >=0:

Conditional non-branches

WASM and LLVM provide SELECT. Note that these are the same two platforms that separate compares and boolean-conditional-branch.

Subroutines

LuaJIT?2 and CIL provides tail calls.

WASM and Lua provide structured control flow loops.

JVM and CIL provide invoke instructions for object-oriented calling.

tailcall:

Loops:

OOP calls:

Allocation

None.

Data Structures

JVM, CIL provide OOP data structures.

LuaJIT?2 and CIL provide strings (but not many operations specifically for them, as far as i can tell; operations not shown).

OOP

creation, memory:

types:

accessors:

Misc

Cycle counters are provided by RISC-V and LLVM (as LLVM intrinsics).

RISC-V and ARM provide Control-and-Status-Registers. Note that these are all of the hardware processor ISAs in our dataset.

Memory operations such as memcpy are provided by LLVM intrinsics and CIL.

cycle counters:

special registers:

memory:


Concordance of instructions supported by only one platform

Unlike the above sections for classes of instructions supported by n platforms, where n>1, here we won't bother to introduce/provide a list at the beginning of the section of the instructions classes in this section.

Arithmetic

Constant loads

RISC-V alone provides LUI and AUIPC.

Andreas Olofsson of Adapteva said on a blog that he is "not convinced that ((AUIPC)) is essential" [14], however a commenter, Chris, from the RISC-V project explained that it was useful [15].

Load upper bits:

Load PC-relative constant:

Add, subtract, multiply, divide

RISC-V alone also provides integer multiplication instructions returning the high-order bits. Andreas Olofsson of Adapteva said on a blog that these operations had a "high expense/benefit ratio" [16].

LLVM intrinsics alone has saturated addition and subtraction (in both signed and unsigned forms); fixed point multiplication (in both signed and unsigned and signed saturated forms).

ARM alone has PC-relative addition.

JVM alone has increment (which operates directly on variables, not on the stack).

Integer addition

Increment:

PC-relative add:

Integer multiplication

Multiply, undefined upon overflow:

Multiply, upper bits:

Shifts

WASM alone provides left rotate.

Rotate left:

Logical

ARM alone also provides and bit-clear (bitwise-AND (x, bitwise-NOT (y)).

Compares

WASM alone provides equals-zero compares.

LLVM alone breaks out each floating point comparison into 'ordered' and 'unordered', which provide two options for what to do with NaNs?.

JVM alone provides trinary comparisons for floats, doubles, and longs.

Equals zero:

Trinary compare:

Floating-point comparisons

Trinary compare:

Other:

Misc integer arith

LLVM alone provides bitreverse, and funnel shifts.

Other bit manipulation:

Floating-point-specific

RISC-V alone provides copy-negated-sign (FSGNJN) and XOR-sign, although these could be thought of as binary generalizations replacements for the unary sign operations negate and abs, which are provided by other systems.

Andreas Olofsson of Adapteva said in a blog post that he felt that RISC-V's floating point sign instructions (FSGNJ, FSGNJN, FSGNJX) were "not essential" [17].

RISC-V alone provides three fused multiply-add variants.

In a blog post, Andreas Olofsson of Adapteva questioned whether the FNMSUB fused multiply-add instruction variant provided by RISC-V is needed [18].

LLVM alone provides, as intrinsics only, certain other variants of min, max, and round, canonicalize, fma, and the standard C library math functions sin, cos, pow, exp, exp2, log, log10, log2.

Fused multiply-add

Signs

other binary sign ops:

Conversions

CIL alone provides 'native' types (native int ('i'), native float ('r')).

CIL alone provides conversion variants with overflow detection.

LLVM alone provides intrinsics to convert to and from 16-bit floating point.

LLVM alone provides conversions between pointers and ints; however such conversions are unneeded in some other systems, where pointers are untyped or the same type as ints.

LLVM alone provides 'addrspacecast' to convert pointers between different 'address spaces'.

JVM alone provides conversions to chars, but not vice versa, as far as i can tell.

Memory access

Loads and stores

ARM alone provides load multiple and store multiple.

CIL alone provides load/store 'native' integers (type 'i'), and load/store opaque references (type 'O').

Load/store multiple:

Load/store native:

Reference (pointer) loads and stores:

Variable loads and stores

CIL alone provides loads of the addresses of local variables. WASM provides a function TEE_LOCAL which sets a local but then also returns its argument. LuaJIT?2 also provides loads and stores from/to upvalues.

Stack ops

ARM alone provides push, pop. JVM alone provides swap.

push, pop:

swap:

Atomics and Sync

LLVM alone provides some rarely seen AMOs: sub nand fadd fsub.

Control flow

Conditional branches

ARM alone also provides branch-if-overflow, branch-if-not-overflow, unsigned >, unsigned <=.

JVM alone also provides >0, <=0.

LuaJIT?2 alone provides equality/inequality compares against strings.

Subroutines

LuaJIT?2 alone provides special instructions for iterators, and for closures.

iterators:

closures:

Other structured control flow

WASM alone provides blocks and if/else.

Data Structures

LLVM alone provides a shufflevector operation.

CIL alone provides address-of-array-element and address-of-object-field accessors.

CIL alone provides polymorphic boxing and unboxing instructions.

LuaJIT?2 alone provides a cat (concatenate) operation (i think that LuaJIT?2 len is on strings and tables. I don't know if cat is only for strings or if it applies to tables also).

LLVM alone provides vector reduction ops, and masked vector ops.

array misc:

array accessors:

OOP accessors:

misc:

vector reduction:

masked vectors (LLVM Intrinsics):

Misc

LLVM alone provides phi, and various other intrinsics.

ARM alone provides interrupt handling, and 'event' hints for power saving.

JVM alone provides a compression instruction instruction (wide), and implementation-dependent instructions.

LuaJIT?2 alone provides function header instructions.

CIL provides various misc. prefixes such as readonly, no.rangecheck.

misc misc:

Full concordance

Arithmetic

Constant loads

WASM, JVM, CIL have instructions to directly load i32, i64, f32, f64 constants (but not unsigned?). RISC-V has various instructions that can be specialized to directly load i64 (or i32, if the chip is RV32I instead of RV64I); everything else must be synthesized/coerced.

Lua has f64 only, but LuaJIT?2 does have an instruction to load 16-bit immediate constants. LuaJIT?2 uses constant tables. LuaJIT?2 and CIL have instructions to load various higher-level data structures such as strings.

JVM uses a constant pool rather than immediate constants. JVM does provide immediate constants of 8- and 16-bits (extended to ints of 32 bits) via bipush and sipush.

JVM provides instructions to push constants of null, or 0,1 sometimes 2, or -1...5, depending on type. CIL provides instructions to load -1..8 to i32 as well as null.

RISC-V alone provides LUI.

LLVM doesn't need constant loads because constants can be assigned to a variable in the LLVM IR without an instruction.

Load upper bits:

Moves (copies)

Moves are provided by RISC-V, ARM, LuaJIT?2. RISC-V provides MOVs as pseudoinstructions. WASM, JVM, and CIL have a stack and LLVM has SSA assignment instead of MOVs.

Add, subtract, multiply, divide

All of RISC-V, WASM, LLVM, ARM, JVM, CIL have 32-bit signed ADD and SUB. LuaJIT?2 has 64-bit signed ADD and SUB.

RISC-V, WASM, LLVM, JVM, CIL have all 4 combinations of integer, float, 64-bit, 32-bit variants of ADD and SUB.

All of RISC-V, WASM, LLVM, ARM, JVM, CIL provide an integer multiplication which returns the lower half of the resulting bits (the "low order bits"; equivalently, the result mod 2^bitwidth).

RISC-V alone also provides integer multiplication instructions returning the high-order bits. Andreas Olofsson of Adapteva said on a blog that these operations had a "high expense/benefit ratio" [19].

RISC-V, WASM, LLVM, JVM, CIL have 64-bit and 32-bit signed integer variants of DIV and REM, and 64-bit and 32-bit float variants of MUL and DIV. LuaJIT?2 provides only 64-bit variants of MUL, DIV, MOD.

RISC-V, WASM, LLVM, CIL have all 4 combinations of 64-bit, 32-bit, signed, unsigned integer variants, of DIV and REM, and 64-bit and 32-bit float variants of MUL and DIV.

Andreas Olofsson of Adapteva noted in a blog post that RISC-V's FDIV (floating point division) instruction is "expensive" and that it was a "tough call" whether to include such an instruction in his Epiphany ISA [20]. In the same blog post, he noted that Epiphany did not have integer division or remainder because they didn't fit Epiphany's intended use cases.

RISC-V floating point operations are only included in the floating point extensions, not the base integer instruction set. RISC-V integer multiplication, division, and remainder are only included in the M extension, not the base integer instruction set.

LLVM, JVM, LuaJIT?2, CIL have floating point remainder/mod.

LLVM intrinsics alone has saturated addition and subtraction (in both signed and unsigned forms); fixed point multiplication (in both signed and unsigned and signed saturated forms).

Integer addition and subtraction with overflow or carry, signed 32-bit, is provided by LLVM intrinsics, ARM, CIL. LLVM, CIL also have multiplication with overflow, as well as unsigned and 64-bit variants of addition, subtraction, multiplication with overflow.

LLVM offers 'constrained' floating point operations, which means that the rounding and exception modes are respected when those instructions are used.

JVM and CIL have integer negation and ARM has reverse subtraction.

JVM alone has increment (which operates directly on variables, not on the stack).

Polymorphic arithmetic

Used for both integers and floats

Integer division truncates towards zero.

Integer division or negation throws DivideByZeroException? if division by zero is attempted. Integer division or negation throws System.ArithmeticException? if the result cannot be represented in the result type, for example, for (the smallest representable integer value) / -1.

Integer negation is twos-complement negation. Neg(the most negative number) = the most negative number (note that in twos-complement, the true negation of the most negative number cannot be represented).

For integer remainder, " result = value1 rem value2 satisfies the following conditions:

result<=value2

Floating-point division is per IEC 60559:1989. Division of a finite number by 0 produces the correctly signed infinite value and 0 / 0 = NaN?, infinity / infinity = NaN?, anything / infinity = 0.

Negation of a floating point number cannot overflow. neg(NaN?) = NaN?.

Floating-point overflow returns +inf or -inf. For floating-point types, 0 * infinity = NaN?.

For floating-point remainder,

"rem is defined similarly as for integer operands, except that, if value2 is zero or value1 is infinity, result is NaN?. If value2 is infinity, result is value1. This definition is different from the one for floating-point remainder in the IEC 60559:1989 Standard. That Standard specifies that value1 div value2 is the nearest integer instead of truncating towards zero. System.Math.IEEERemainder(see Partition IV) provides the IEC 60559:1989 behavior.

...

Example:

For the various floating-point values of 10.0 and 6.0, rem gives the same values; System.Math.IEEERemainder, however, gives the following values.

Integer addition

Increment:

Integer subtraction

Subtract:

Negation

Reverse subtract:

Integer multiplication

Multiply, lower bits:

Multiply, undefined upon overflow:

Multiply, upper bits:

Integer division and remainder

Div:

Rem:

Floating point add, sub, mul, div

Add:

Sub:

Mul:

Div:

Remainder/mod:

Shifts

All of RISC-V, WASM, LLVM, ARM, CIL provide 32-bit left shift, logical/unsigned right shift, and arithmetic/signed right shift. RISC-V, WASM, LLVM, CIL also provide 64-bit variants.

WASM and ARM provide 32-bit right rotate. WASM alone provides left rotates (and 64-bit variant of right rotate).

Shift left:

Shift right logical/shift right unsigned:

Shift right arithmetic/shift right signed:

Rotate left:

Rotate right:

Logical

All of RISC-V, WASM, LLVM, ARM, JVM, CIL provide AND, OR, and XOR. RISC-V provides one instruction which is 32- or 64- bits depending on the register size, where WASM and JVM provide separate instructions for each size. ARM only provides 32-bit.

ARM, CIL provide bitwise NOT.

ARM alone also provides and bit-clear (bitwise-AND (x, bitwise-NOT (y)).

LuaJIT?2 provides boolean NOT.

Andreas Olofsson of Adapteva said on a blog that he felt that the immediate variants provided by RISC-V (ANDI, ORI, XORI) could have been left out (and were left out in his architecture, Epiphany) [23].

AND:

OR:

XOR:

NOT:

misc:

Compares

In this section we are only talking about compares which produce a result value, not branch, which is treated in the control flow section.

RISC-V, WASM, LLVM provide < (less-than) in integer (signed and unsigned) and floating point. All of RISC-V and WASM and LLVM provide floating point == (equality), < (less-than) and <= (less-than-or-equal-to) in both 32- and 64-bit. RISC-V and LLVM provide one instruction which is 32- or 64- bits depending on the register/value size, whereas WASM provides separate instructions for each size.

WASM and LLVM also provide integer <=, >, >= (less-than-or-equal-to, greater-than, and greater-than-or-equal-to) in all of 32- and 64-bit and unsigned and signed. WASM and LLVM also provide integer ==, != (equality, inequality), and floating point !=, >, >= (inequality, greater-than, greater-than-or-equal-to), all in both 32- and 64-bit, although i believe these would be trivial to synthesize on RISC-V. WASM also provides ==0 (equality with zero), which would also be trivial to synthesize on RISC-V or LLVM.

CIL provides ==, < > in both integer and floating point, both 32- and 64-bit, and < > in both signed and unsigned.

WASM alone provides equals-zero compares.

LLVM also breaks out each floating point comparison into 'ordered' and 'unordered', which provide two options for what to do with NaNs?.

JVM alone provides trinary comparisons for floats, doubles, and longs. JVM provides compare-and-branch instructions for integers and pointers rather than separate compare instructions.

CIL also has separate pointer types and provides various comparisons over pointers through the same polymorphic compare instructions used for other types.

Andreas Olofsson of Adapteva said on a blog that he felt that the SLT* instructions provided by RISC-V could have been left out (and were left out in his architecture, Epiphany) [24].

Polymorphic

Integer comparisons

Less than:

Greater than, greater than or equal to, less than or equal to:

Equality and inequality:

Equals zero:

Trinary compare:

Floating-point comparisons

Equality:

Less than:

Less than or equal to:

Trinary compare:

Other:

Misc integer arith

CLZ, CTZ, POPCNT are provided by WASM and LLVM (but only as LLVM intrinsics).

LLVM and ARM provide various byteswaps. LLVM alone provides bitreverse, and funnel shifts.

The pattern so far seems to be that with a few exceptions (multiply upper bits, ROTs, equals-zero), LLVM provides all of the integer and floating-point arithmetic that either RISC-V or WASM provides, and then some; although for some functionality LLVM requires intrinsics rather than ordinary instructions (floating-point rounding and error modes; CLZ/CTZ/POPCNT).

Other bit manipulation:

byte swap:

Floating-point-specific

SQRT, COPYSIGN, MIN, MAX are provided by RISC-V, WASM, LLVM (but only as LLVM intrinsics).

Andreas Olofsson of Adapteva noted in a blog post that RISC-V's FSQRT instruction is "expensive" and that it was a "tough call" whether to include such an instruction in his Epiphany ISA [25].

In the same blog post, Andreas Olofsson of Adapteva questioned whether the FMIN and FMAX instructions provided by RISC-V are needed [26].

RISC-V, WASM, LLVM each provide additional sign operations. RISC-V alone provides copy-negated-sign (FSGNJN) and XOR-sign. WASM and LLVM provide abs(-olute-value), although LLVM abs is an intrinsic. WASM, LLVM, JVM, LuaJIT?2 provide neg(-ate).

Andreas Olofsson of Adapteva said in a blog post that he felt that RISC-V's floating point sign instructions (FSGNJ, FSGNJN, FSGNJX) were "not essential" [27].

RISC-V and LLVM provide rounding in the form of conversion operations and rounding modes (LLVM requires the 'constrained' intrinsics to use these). WASM and LLVM provide rounding in the form of ceil, floor, trunc, nearest instructions (but WASM does not provide rounding mode control for other instructions; see [28] and [29]).

RISC-V and LLVM support IEEE exception flags and rounding modes (but LLVM only supports these with 'constrained' instrinsics.

Andreas Olofsson of Adapteva noted in a blog post that the Epiphany ISA does not include operations like RISC-V's exception flags, coercion operations, and rounding modes, because they were not needed for Epiphany's use case [30].

RISC-V provides an FCLASS instruction to report the attributes of a floating-point number. Andreas Olofsson of Adapteva said in a blog post that he felt that this instruction was "not essential" [31]. CIL provides ckfinite to check if a floating-point number is finite.

Fused multiply-add is provided by RISC-V and LLVM (but only as an LLVM intrinsic). RISC-V alone provides three fused multiply-add variants.

In a blog post, Andreas Olofsson of Adapteva questioned whether the FNMSUB fused multiply-add instruction variant provided by RISC-V is needed [32].

Both RISC-V and WASM provide all floating-point operations in both 32- and 64-bits.

pow is provided by LLVM as an intrinsic and by LuaJIT?2.

LLVM alone provides, as intrinsics only, certain other variants of min, max, and round, canonicalize, fma, and the standard C library math functions sin, cos, pow, exp, exp2, log, log10, log2.

" WebAssembly? uses “non-stop” mode, and floating point exceptions are not otherwise observable. In particular, neither alternate floating point exception handling attributes nor the non-computational operators on status flags are supported. There is no observable difference between quiet and signalling NaN?. However, positive infinity, negative infinity, and NaN? are still always produced as result values to indicate overflow, invalid, and divide-by-zero conditions, as specified by IEEE 754-2008.

WebAssembly? uses the round-to-nearest ties-to-even rounding attribute, except where otherwise specified. Non-default directed rounding attributes are not supported. " -- [33]

" By default, LLVM optimization passes assume that the rounding mode is round-to-nearest and that floating-point exceptions will not be monitored. Constrained FP intrinsics are used to support non-default rounding modes and accurately preserve exception behavior " -- [34]

Sqrt

Fused multiply-add

Signs

copysign:

other binary sign ops:

absolute value:

negation:

Rounding

floor, ceiling, trunc:

nearest int or rounding-mode determined rounding:

FRM (3-bit floating point rounding mode):

Min, max

Classify

Floating-point exceptions

FFLAGS (5-bit Accrued Exception Flags):

Pow

Misc floating-point

FCSR (32-bit floating point status and control register):

Conversions

RISC-V, WASM, LLVM, JVM, CIL provide conversions from signed integer to floating-point, in both 32- and 64- bits. RISC-V, WASM, LLVM provide conversions from unsigned integer to floating point, in both 32- and 64- bits. RISC-V, WASM, LLVM, CIL provide coercions between integer and floating point in both 32- and 64-bit.

RISC-V provides conversions from floats to integers (both signed and unsigned) with various rounding modes. LLVM provides 'constrained' intrinsic variants of floating-point truncation and extension which provide choice of rounding mode and exception handling mode. WASM only provides trunctation from float to integer (both signed and unsigned); but WASM also provides ceiling, floor, and nearest as separate unary operations on floats. LLVM provides conversion from float to integer (both signed and unsigned), with rounding to nearest. JVM, CIL provides conversion from float to integer, rounding-towards-zero.

WASM, LLVM, JVM, CIL provide conversions from 32-bit integers to 64-bit integers and vice versa, and also from 32-bit floats to 64-bit floats and vice versa. In RISC-V, the registers are either 32-bits (RV32I) or 64-bits (RV64I); if they are only 32-bits, then 64-bit quantities cannot be expressed, and if they are 64-bits, then all quantities are always stored in registers 64-bit format (i think?). CIL silently truncates high-order bits upon overflow when converting one integer type to another.

ARM, LLVM, CIL provide conversions from 8-bit and 16-bit integers to 32-bit, in both signed and unsigned variants. LLVM, CIL also provide conversions from other smaller bitwidths to other larger bitwidths. CIL also provides conversions to 8-bit and 16-bit integers from larger integers or from floating-point.

JVM provides conversions from 32-bit integers to 16-bit and 8-bit integers, and to chars, but not vice versa, as far as i can tell.

LLVM alone provides intrinsics to convert to and from 16-bit floating point.

LLVM alone provides conversions between pointers and ints; however such conversions are unneeded in RISC-V, which is untyped, and WASM, where pointers ('memarg') are just a pair of u32s ({offset u32, align u32}).

LLVM alone provides 'addrspacecast' to convert pointers between different 'address spaces'.

CIL alone provides conversion variants with overflow detection.

CIL alone provides 'native' types (native int ('i'), native float ('r')).

Polymorphic (on the source) conversions

Coercions between values of the same size

Coercions between integers and floating point of the same size: i32 to f32:

f32 to i32:

i64 to f64:

f64 to i64:

Conversions from signed integer to floating-point

Conversions from unsigned integer to floating-point

Conversions from floating point to signed integer

Conversions from floating point to unsigned integer

Integer conversions from larger bitwidth to smaller

Integer conversions from smaller bitwidth to larger

sign extend:

zero extend:

Floating-point conversions between different bitwidths

Ptr/int conversions

Misc conversions

Memory access

Loads and stores

RISC-V, WASM, ARM, CIL provide integer loads from 8-bit, 16-bit, and 32-bit memory locations, and when loading a quantity smaller than the destination, both signed and unsigned are provided. RISC-V supports loading into whatever size the registers are (32- or 64-bits), and ARM and CIL supports loading into 32-bit registers (CIL also supports loading 64-bit integers as 64-bits), in contrast to WASM which supports explictly loading any quantity as 32- or 64-bits. RISC-V, WASM, CIL support floating point loads and stores of either 32 or 64 bits.

RISC-V, WASM, ARM, CIL support stores to 8-bit, 16-bit, 32-bit quantities. RISC-V supports storing to 64-bits if the registers are 64-bits, whereas WASM, CIL always supports stores of 64-bits. RISC-V supports storing from whatever size the registers are (obviously), whereas WASM, CIL supports stores from 32 or 64 bit values.

LLVM provides loads and stores of all of these data types, but as far as i can tell, the type of the value being loaded must match the type variable being loaded to/stored from (as opposed to e.g. RISC-V and WASM, which provide operations like LB and i64.load8_s to load an 8-bit value into a register/variable of 64-bit type) (todo is this correct?).

ARM alone provides load multiple and store multiple.

CIL alone provides load/store 'native' integers (type 'i'), and load/store opaque references (type 'O').

Andreas Olofsson of Adapteva indicated on a blog that he left out instructions equivalent to RISC-V's LB and LH from his Epiphany ISA, and that he regretted doing so [35].

Polymorphic loads and stores

Integer loads

Load 32-bit and 64-bit integers:

Load 8-bit and 16-bit integers, unsigned:

Load 8-bit and 16-bit integers, signed:

Load multiple:

Load native:

Floating point loads

Reference (pointer) loads and stores

Integer stores

i64, i32:

i16:

i8:

Store multiple:

native:

Floating point stores

Variable loads and stores

WASM, JVM, CIL support loads and stores to/from local variables. WASM and LuaJIT?2 also supports loads and stores to/from global variables. WASM alone also provides a function TEE_LOCAL which sets a local but then also returns its argument. JVM specializes load/store instructions by type. JVM and CIL provide short instructions to load/store the first 4 variables. LuaJIT?2 alone also provides loads and stores from/to upvalues. CIL alone provides loads of the addresses of local variables.

locals:

globals:

upvalues:

Stack ops

WASM, JVM, CIL provides drop (called 'pop' in JVM and CIL). ARM alone provides push, pop. JVM, CIL provide dup. JVM alone provides swap.

push, pop:

dup:

drop:

swap:

Atomics and Sync

RISC-V, LLVM, ARM, JVM, CIL provide various FENCE/sync barrier/monitor/volatile instructions/prefixes. RISC-V and LLVM provide the AMOs (Atomic Memory Operations): SWAP, ADD, AND, OR, XOR, MIN, MAX, MINU, MAXU.

RISC-V alone provides load-release, store-conditional, FENCE instructions and data.

RISC-V atomics are only included in the A extension, not the base integer instruction set. FENCE and FENCE.I are in the base instruction set.

LLVM alone provides compare-and-swap, and more AMOs: sub nand fadd fsub.

Andreas Olofsson of Adapteva said on a blog that he felt that the FENCE instruction(s) provided by RISC-V could have been left out (and were left out in his architecture, Epiphany), commenting "Benefit minimal in good SW imho" [36].

load-release and store-conditional (LR/SC)

Compare-and-swap (CAS)

AMOs

swap:

add:

xor:

and:

or:

min:

unsigned min:

max:

unsigned max:

other:

Fences

Control flow

Jumps (unconditional branch)

All of RISC-V, WASM, LLVM, ARM, JVM, LuaJIT?2, CIL provide unconditional jumps to an immediate/a label. RISC-V and ARM provide an unconstrained indirect branch (JALR), CIL provides an unconstrained indirect branch to a method (jmp), and WASM, LLVM, JVM, CIL provide an indirect branch to target constrained to one of an enumerated set of possible targets (BR_TABLE and switch and indirectbr and JVM's switches and CIL's switch). JVM used to provide other indirect branches (ret) but this is now deprecated, probably due to the difficulty it added to verification. LLVM and JVM provide switch-like instructions (switch, indirectbr, lookupswitch); switch and lookupswitch are like a C switch statement, and indirectbr jumps to an address in a variable; JVM's tableswitch takes an index into a list of enumerated labels to be branched to; however all of them must contain an enumerated set of labels representing all possible jump targets. RISC-V and ARM provide link register variants of these branch instructions.

Although LuaJIT?2 doesn't have an indirect branch instruction, it does have a higher-level CALL instruction, which is indirect (that is, the function to be called is taken from a register, rather than specified as an immediate in the bytecode).

Jump to immediate / direct branch

Jump to register / indirect branch

Conditional branches

RISC-V provides ==, !=, < and >=, but not <= or >. LuaJIT?2, ARM, JVM, CIL provides these and also <=, >, ==0, !=0.

ARM and JVM also provide <0, >=0.

ARM and RISC-V provide unsigned versions of < and >=.

JVM also provides >0, <=0, and for references (pointers), ==null, !=null.

ARM alone also provides branch-if-overflow, branch-if-not-overflow, unsigned >, unsigned <=. Note that ARM's "compare and branches" really require two instructions, one to 'compare' and one to 'branch' based on the result of the compare, as seen by the state of the processor's flags.

LuaJIT?2 provides equality/inequality compares against constant (both immediate and constant table) strings, numbers, and 'primitives' (null/false/true).

WASM and LLVM instead provide separate comparison ops, and boolean conditional branch. These are similar to ARM in that there are two steps needed; but differ in that in ARM, the branch condition is expressed in the branch step, rather than in the compare step; and in ARM, the extra state of the flags is needed.

branch: eq:

ne:

lt:

ge:

le:

gt:

unsigned lt:

unsigned ge:

unsigned le:

unsigned gt:

unary compare ==0 or null or false:

unary compare !=0 or nonnull or true (also, boolean conditional branch):

unary compare <0:

unary compare >=0:

other:

Conditional non-branches

WASM and LLVM provide SELECT.

Subroutines

WASM, LLVM, JVM, LuaJIT?2, CIL provide CALL or INVOKE, and RETURN. WASM, JVM, CIL alone provide various forms of CALL_INDIRECT. LLVM, JVM, CIL provide exception handling.

LuaJIT?2 and CIL provides tail calls.

LuaJIT?2 alone provides special instructions for iterators, and for closures.

JVM provides various forms of invoke for object-oriented purposes. JVM specializes return by type.

LLVM, LuaLIT?2, CIL provide argument handling, sometimes variadic.

call:

call with exception handling or other multiple return possibilities:

variadic argument handling and argument handling:

tailcall:

iterators:

return:

exception handling:

closures:

indirect branch form of call:

Other structured control flow

WASM and Lua provide structured control flow. WASM alone provides blocks and if/else, and both provide loops.

Loops:

Misc control flow

RISC-V, WASM, LLVM (and possibly others) provide ILLEGAL/UNREACHABLE instructions. RISC-V, WASM, LLVM, ARM, JVM, NOP provide NOP.

Illegal or unreachable instruction:

NOP:

Allocation

WASM alone provides a linear memory which is growable. CIL provides heap allocation. LLVM alone provides stack allocation. LLVM alone provides various garbage collection, memory use markers, and ARC intrinsics.

See also the creation operations in in the Data Structures section, below, as this also usually causes allocation.

Linear memory sizing:

heap allocation:

stack frame:

gc and memory usage and ARC:

Data Structures

LLVM, JVM, CIL, LuaJIT?2 provide vectors/arrays and aggregates (JVM and CIL provides aggregates via OOP) (LuaJIT?2 tables serve as both arrays and aggregates, i think).

JVM, LuaJIT?2, CIL provide a length operation.

CIL alone provides address-of-array-element and address-of-object-field accessors. LLVM alone provides a shufflevector operation.

JVM, CIL provide OOP data structures.

LLVM alone provides vector reduction ops, and masked vector ops.

LuaJIT?2 alone provides a cat (concatenate) operation (i think that LuaJIT?2 len is on strings and tables. I don't know if cat is only for strings or if it applies to tables also).

LuaJIT?2 and CIL provide strings (but not many operations specifically for them, as far as i can tell).

CIL alone provides polymorphic boxing and unboxing instructions.

Vectors and arrays

creation:

length:

accessors:

misc:

OOP

creation, memory:

types:

accessors:

misc:

Lua len and concat

Lua len and concat (in LuaJIT?2, is concat only for strings, or is it generic?):

Tables and aggregates

Tables:

Misc data structures

masked vectors (LLVM Intrinsics):

Misc

Cycle counters are provided by RISC-V and LLVM (as LLVM intrinsics).

RISC-V, LLVM, ARM provide supervisor call.

RISC-V, LLVM, ARM, JVM, CIL provide breakpoint.

RISC-V and ARM provide Control-and-Status-Registers.

Memory operations such as memcpy are provided by LLVM intrinsics and CIL.

LLVM alone provides phi, and various other intrinsics.

ARM alone provides interrupt handling, and 'event' hints for power saving.

JVM alone provides a compression instruction instruction (wide), and implementation-dependent instructions.

LuaJIT?2 alone provides function header instructions.

CIL provides various misc. prefixes such as readonly, no.rangecheck.

cycle counters:

supervisor call:

breakpoint:

special registers:

memory:

misc misc:



Continued at Target Languages Concordance part III