Section 14.4 of User-Level ISA, Version 2.1 describes JALR thus:
Note that the JALR instruction does not treat the 12-bit immediate as multiples of 2 bytes,
unlike the conditional branch instructions. This avoids one more immediate format in hardware.
In practice, most uses of JALR will have either a zero immediate or be paired with a LUI or
AUIPC, so the slight reduction in range is not significant.
The JALR instruction ignores the lowest bit of the calculated target address. This both
simplifies the hardware slightly and allows the low bit of function pointers to be used to store
auxiliary information. Although there is potentially a slight loss of error checking in this case,
in practice jumps to an incorrect instruction address will usually quickly raise an exception.
The "slight loss of error checking" probably refers to the validation
that occurs when a function pointer is required to be on a 16-bit
boundary. A random erroneous value will have a 50/50 chance of being
caught before the jump, but the likelihood is that even the erroneous
even address would "usually quickly raise an exception".
The intent, as I read it of the above section is that the low order bit
in the function pointer can be used without concern that the JALR will
use it for address calculation.
However, if in the JALR instruction the low order bit of the immediate is set this is not the case.
The JALR will branch to a target when an auxiliary bit is zero, and two
bytes following that target when that auxiliary bit is set.
I will call this the "double odd" effect.
Three design decision were necessary and combined to raise this issue.
a) the JALR immediate can be odd; unlike other control flow instructions which have a word aligned value.
b) the addressing register can have an odd value (except of course x0).
The reason is well explained in the excerpt above.
c) RVC allows for instructions to start on a 16-bit boundary.
Decisions a and b allow for a carry from the LSB to occur which meant the LSB of the register could advance the target address.
Decision c allowed for both possible target addresses to be executed.
(One or the other would have caused an alignment trap without CSV present).
This allows a potential hazard and a potential trojan code opportunity.
Again, note that this functionality only comes about as a result of the RVC specification.
The base ISAs only allow 32 bit aligned instructions, and unaligned fetches would result in exceptions.
This NSE proposed to an ISA that is already at V2.1 is really only
applicable as it supports a RVC that is currently only at 1.9.
Arguably necessary base ISA extensions that are required for optimal RVC
definition can be included; as (arguably) already done for C.JAL and
C.JALR, redefining the return address from PC+4 to PC+<instruction
length>.
Marking the odd immediate JALR as NSE would highlight the possibility
that the low order bit of the function pointer may have unintended
consequences.
That the intent of allowing it to be used for auxiliary purposes is not guaranteed.
Possible uses of this functionality:
I posted on isa-dev ( and also on sw-dev, my bad, apparently)
"enhanced subroutine invocation mode: A proposal for a significant use of the JALR odd bits "side-effect"".
Obviously, the functionality can be leveraged to some useful purpose.
Therefore, I do not recommend prohibiting the behaviour all together.
(This could be done by redefining JALR to effectively clear both low order bits before they are added.)
Rather, I propose that they be defined as NSE, which would highlight
that even this defined behaviour cannot be relied upon unless so
specified by the HW arch.
In any event, I am concerned about the hazards and the coding that may occur.
Hazards:
Hardware implementation limitations on jumps into 32bit instructions.
(1) First, is it allowable in all situations for a jump into a RV32/64I instruction?
Must this be supported on all "compliant" RVC implementations?
These questions are relevant to RVC irrespective of this NSE proposal.
If a 32 bit instruction is already cached, perhaps along with
decoding of determined exclusion settings might these clash with an
execution RVC that is included within the same instruction?
A HW implementation may determine that the tagging of the
instruction is best done using the instructions high order word. That
would definely clash with the ability to execute the RVC instruction
within it.
(2) The "double odd" JALR functionality can be used to selectively
vector between two RVC instructions (allowing drop through or more
typically I expect two C.J ).
However, there would be the temptation to use a LUI x0,<immediate including RVC in upper word> trick.
This is described in the "simpler" implementation of the
enhanced subroutine invocation mode: A proposal for a significant use of the JALR odd bits "side-effect".
If there is no guarantee that every RVC must allow proper
sub-instruction execution, then unreliable execution would likely ensue.
Marking odd offset JALR as NSE would highlight this hazard.
Latent bugs and exploits.
(a) In that this "double odd" feature is anticipated to be rarely
used, it is also less likely to be extensively tested in a HW
implementation.
The explicit awareness that a NSE designation provides would
highlight the need to test this case if they are indeed supporting it.
(b) Excluding the low order bit from an adder that handles address calculation might seem a viable trade-off.
Why encumber that block and use extra traces for a non-existent situation?
This could certainly be decided intentionally or unintentionally (see (a) above).
Such an implementation would likely describe this in the errata as a feature and not a bug.
Defining this case as NSE encourages for such design decisions to be made explicitly and intentionally.
(c) Assuming this "double odd" behaviour is operational on all hardware is risky.
Due to the two HW implementation considerations above, it is
very possible that the behaviour is only approximately there or it is
absent.
Defining JALR with odd immediate as NSE helps to address the likely variation in implementation.
It allows the eco-system to tolerate such plausible variants.
It gives the producer the opportunity to legitimately claim its implementation feature rich, rather than deficient.
This informs the consumer, and allows better comparison of competitive products.
Especially for what would
otherwise be a deficiency that most could care less about.
(d) Highlighting this "double odd" behaviour with NSE and allowing
variants that do not carry from the LSB could be desirable for hardened
and hardening systems.
This ability to have code act in two distinctly different ways when the pointer is odd vs even allows for Trojan code.
Software can be tested and proven correct, but with a minor change to an address pointer the code is hijacked.
The pointer oddness could be performed in a linkage editor and potentially would not affect any other code so linked.
Or it could be deemed to be harmless, a typical "transparently hide an auxiliary bit" that many applications use.
a scan to see that the bit is not checked anywhere in the code would pass.
or a generalized routine that is innocuous but validates
the setting of the bit would justify its cause to be set.
Prohibiting the carry from LSB would avoid concern for this attack vector.
Designating NSE would allow for a legitimate "hardened" implementation.