Skip to content
Open
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions src/asm-manual.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,75 @@ NOTE: `.option arch, +<ext>, -<ext>` is accepted and will result in enabling the
extensions that depend on `ext`, e.g. `rv32i` + `.option arch, +v, -v` will result
`rv32ifd_zve32x_zve32f_zve64x_zve64f_zve64d_zvl32b_zvl64b_zvl128b`.

=== `exact`/`noexact`

In RISC-V, the assembler and linker can do several things to change the code
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel that the text is overly verbose. Grok suggests a concise one:

In RISC-V, the assembler and linker may modify user code to optimize the final executable:

  • Compression: Converts longer instructions to shorter equivalents, e.g., lw a0, 16(a1) to c.lw a0, 16(a1) with C or Zca extensions.
  • Linker Relaxation: Replaces long symbol references with shorter sequences (see psABI document).
  • Branch Relaxation: Converts short branches with insufficient range, e.g., beq a0, a1, sym to bne a0, a1, 4; j sym for longer range.

Programmers may want unmodified assembly, but only linker relaxation can be disabled with .option norelax. Compression requires disabling extensions like .option norvc, which restricts smaller instructions and affects larger ones.

The .option exact prevents assembler or linker modifications, controlling both without altering enabled extensions, allowing flexible instruction lengths.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if RISC-V International takes a view at the moment on using LLM-derived content in its specifications. Probably best not to get into that.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the bullet points from Sam's version are clear enough. Maybe Sam can rework / reduce a bit the 4 paragraphs that follow the bullet points with a bullet point for the limitations of no-relax and norvc options and the flexibility introduced with the exact option, to address Maskray's concern.

that a user writes into a different sequence of instructions. There are features
which help static code size in the final executable:

- Compression turns longer instructions into shorter, equivalent instructions.
For example the assembler might turn `lw a0, 16(a1)` into `c.lw a0, 16(a1)`
when the C or Zca extensions are enabled.
- Linker Relaxation causes the linker to replace long symbol references with
shorter code sequences that can reference the same symbol (documented more
fully in the psABI document).

And also features which allow users to write a sequence of code which doesn't
match what appears in the final executable, but may also avoid assembler errors
in some cases:

- Branch Relaxation turns short branches of too long or unknown range into code
sequences with a longer range. For example `beq a0, a1, sym` will be turned
into `bne a0, a1, 4; j sym` because `j` has a longer range than `beq`.
- The assembler may accept the wrong mnemonic for an instruction because the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I agree with disabling allowing add in place of addi. If a user is looking for a strict mode on mnemonics like this they would need to apply it to the whole program for it be truly useful. In that use case they probably don't want to disable compression and branch relaxation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted. As I said, I don't mind either way on these mnemonics, so I can remove this point.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Recent discussion reminded me I hadn't looped back here)

On reflection, I don't mind too much either. I agree that users wanting a "strict mode" probably do still want branch relaxation and compression. I think logically "exact" meaning "exactly what is written" is an intuitive definition even if allowing/disallowing pseudos like add with immediate arguments isn't that important either way. But I don't think it's going to impact people in any meaningful way if the pseudos are still allowed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that users wanting a "strict mode" probably do still want branch relaxation and compression.

Can you clarify this statement? This reads to me as "a strict mode should keep branch relaxation and compression enabled", which is not my intention.

I think logically "exact" meaning "exactly what is written" is an intuitive definition even if allowing/disallowing pseudos like add with immediate arguments isn't that important either way. But I don't think it's going to impact people in any meaningful way if the pseudos are still allowed.

I think the add for addi is not going to impact users, and I will shortly push a patch to remove (only) this bullet.

I think that disabling branch relaxation could impact users relying on the ranges of the far branches, but if they opt into an exact mode then they should know this might happen.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you clarify this statement? This reads to me as "a strict mode should keep branch relaxation and compression enabled", which is not my intention.

Sorry to be confusing, I was referring to Craig's comment up above that points to the idea that to the extent there is a need/demand for a "strict mode", it's probably different to what is implemented in this PR. i.e. it keeps branch relaxation and compression. Which of course is not the intention of the mode introduced in this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for clarifying. I had sort of aliased "strict mode" and "exact mode" in my head but I can see how a hypothetical strict mode is not the same as this proposal.

operands match another, for instance `add a0, a1, 104` is accepted when the
correct instruction is `addi a0, a1, 104`.

There are myriad reasons why a programmer may not want the assembly they wrote
to be modified in any way, but at the moment, they can only control linker
relaxation with `.option norelax`.

Compression is only controllable by switching off the extension containing the
smaller instruction, such as with `.option norvc`, which also prevents the user
directly writing the smaller instructions, and will have further issues with
larger-than-32-bit instructions that compress to instructions in the base
architecture.

The aim of `.option exact` is to prevent the assembler or linker from making
these changes. The option controls both the assembler, and the metadata emitted
to the linker about whether it is allowed to relax code sequences or not.

This option does not change the currently enabled extensions, which allows
instructions of different lengths to still be used without error. For example:

[source,asm]
----
.option arch, +zca

lw a0, 0(a0) # assembled as 'c.lw a0, 0(a0)' (2 bytes)
c.lw a0, 0(a0) # not changed

.option exact

lw a0, 0(a0) # not changed
c.lw a0, 0(a0) # not changed, no error

.option noexact

lw a0, 0(a0) # assembled as 'c.lw a0, 0(a0)' (2 bytes)
c.lw a0, 0(a0) # not changed
----

The default behaviour is `.option noexact`.

Enabling `.option exact` implies `.option norelax` from that point onwards, so
Copy link

@a4lg a4lg May 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd better adding an example or a clarification (relation/interactions between .option exact and .option relax).

For instance, LLVM seems to enable .option relax if .option noexact is specified even if originally .option norelax.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example, (if we prefer current LLVM semantics), adding the fact that .option noexact implies .option relax will be sufficient.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The semantics here are a bit dicey.

Linker relaxations:

  • There's a default
  • There's a command-line flag to override the default for an invocation (i.e., before any parsing)
  • There's both .option relax and .option norelax for surrounding a single snippet
  • It's best to use .option push; .option relax and .option pop to avoid the issues with trying to go back to what it was when that can be changed by a command-line option.

Exact:

  • There's a default (noexact)
  • There's no command-line flag
  • There's the options, both ways. .option exact disables linker relaxations (like .option norelax), .option noexact enables linker relaxations (like .option relax) to be the opposite.
  • It's best to use .option push; .option exact and .option pop like with relax.

I would follow LLVM (because both enabling it, and not enabling it again are equally confusing), but/and our docs should also encourage people towards push/pop.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lenary FYI, I decided to warn in the documentation about flaky interactions between .option relax/norelax and .option exact/noexact as follows (raw text is in the PATCH v2 2/2 and formatted to look similar to the GNU Texinfo output):

exact
noexact

Enables or disables exact mode. Not only the exact mode disables linker relaxations, it also disables automatic instruction compression and the branch relaxation (both optionally change instruction encodings and/or instruction count). This mode is useful in some cases where the instruction sequences, as exactly written, are expected to be emitted.

Note that, in the current implementation, .option exact implies .option norelax and .option noexact implies .option relax. Due to their flaky interactions, it is strongly discouraged to use both .option relax/norelax and .option exact/noexact in the same scope.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@a4lg just to circle back on this, I'm finding that for testing the linker, the combination .option exact; .option relax is quite helpful - i.e. to turn off assembler relaxation and compression, but to still emit R_RISCV_RELAX on instructions that can have it. This works with LLVM, I'm not sure if it's something we want to make work, because I'm not sure it has applications beyond making it marginally easier to test linker relaxation.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lenary I understand the value of testing the linker but this reason alone is unconvincing for me. Rather (on the patch set for GNU Binutils), I thought it would be better to endorse regular users from using both [no]relax and [no]exact at the same scope and leave possibilities of changing the interactions. Anyway, that's not the point for this PR.

If there's a note which indicates that .option noexact implies .option relax (not just .option exact implies .option norelax), I can approve.

this should be used with `.option push; .option exact; ... ; .option pop` to be
clear about when to re-enable relaxation.

The default behaviour of some disassemblers is to reverse this process and show
`lw a0, 0(a0)` when `c.lw a0, 0(a0)` is in the binary. This can be disabled in
objdump-compatible disassemblers using the options `-M no-aliases`.

=== `pic`/`nopic`

Set the code model to PIC (position independent code) or non-PIC. This will
Expand Down
Loading