Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inline Asm Constraints and Modifiers - RVC, Raw Encodings, Pairs #92

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

lenary
Copy link
Contributor

@lenary lenary commented Oct 15, 2024

We have customers with usecases that want more kinds of register
constraints and modifiers. This change proposes support for these
constraints and modifiers, and their names.

Broadly, these are intended to make it easier for users who want to
manually assemble instructions inside inline assembly blocks, either
using the existing instruction formats, or using the raw form of the
.insn directive. This makes it easier for hardware designers to
experiment on new ISA extensions, and makes it easier to support
the use of proprietary extensions with unmodified open-source
toolchains.

There are three groups of additions here:

  • Constraints for RVC-compatible registers. These use the c prefix on
    an existing register constraint, so cr gives a GPR between x8-x15,
    and cf does the same for an FPR between f8-f15.

    I'm not aware of compressed vector instructions, but we could add
    cvr, cvd and cvm in the future if the core architecture ends up
    having the concept of a vector register with an RVC encoding.

  • A modifier, N, to print the raw encoding of a register. This is used
    when using .insn <length>, <encoding>, where the user wants to pass
    a value to the instruction in a known register, but where the
    instruction doesn't follow the existing instruction formats, so the
    assembly parser is not expecting a register name, just a raw integer.

  • Constraints for even-odd pairs of registers. These use the P prefix
    on an existing register constraint. At the moment, this only defines
    Pr to mean an even-odd pair of GPRs. (We use P as a prefix as p
    already means "pointer" in GCC's target-independent constraints).

    I think this will print as the even register in the even-odd register
    pair, but I'm still working on the details around this.

    While the concept of even-odd register pairs is reasonably "new",
    there are places in the architecture where these already exist - the
    doubleword/quad CAS in Zacas, and they are also present in the Zilsd
    specification.


There's also a commit which fixes a header for the Assembly Constraints table.

We have customers with usecases that want more kinds of register
constraints and modifiers. This change proposes support for these
constraints and modifiers, and their names.

Broadly, these are intended to make it easier for users who want to
manually assemble instructions inside inline assembly blocks, either
using the existing instruction formats, or using the raw form of the
`.insn` directive. This makes it easier for hardware designers to
experiment on new ISA extensions, and makes it easier to support
the use of proprietary extensions with unmodified open-source
toolchains.

There are three groups of additions here:

- Constraints for RVC-compatible registers. These use the `c` prefix on
  an existing register constraint, so `cr` gives a GPR between x8-x15,
  and `cf` does the same for an FPR between f8-f15.

  I'm not aware of compressed vector instructions, but we could add
  `cvr`, `cvd` and `cvm` in the future if the core architecture ends up
  having the concept of a vector register with an RVC encoding.

- A modifier, `N`, to print the raw encoding of a register. This is used
  when using `.insn <length>, <encoding>`, where the user wants to pass
  a value to the instruction in a known register, but where the
  instruction doesn't follow the existing instruction formats, so the
  assembly parser is not expecting a register name, just a raw integer.

- Constraints for even-odd pairs of registers. These use the `P` prefix
  on an existing register constraint. At the moment, this only defines
  `Pr` to mean an even-odd pair of GPRs. (We use `P` as a prefix as `p`
  already means "pointer" in GCC's target-independent constraints).

  I think this will print as the even register in the even-odd register
  pair, but I'm still working on the details around this.

  While the concept of even-odd register pairs is reasonably "new",
  there are places in the architecture where these already exist - the
  doubleword/quad CAS in Zacas, and they are also present in the Zilsd
  specification.

Signed-off-by: Sam Elliott <[email protected]>
@@ -746,13 +746,18 @@ statements, including both RISC-V specific and common operand constraints.
|K |5-bit unsigned immediate integer operand |
|J |Zero integer immediate operand |
|s |symbol or label reference with a constant offset |
|cr |RVC general purpose register (`x8`-`x15`) |
|cf |RVC floating point register (`f8`-`f15`) |
|Pr |Even-odd general purpose register pair |
Copy link
Contributor

Choose a reason for hiding this comment

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

Do any other targets support pair? I haven't been able to find any in LLVM and I don't know how to implement it in LLVM.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

AArch64 supports tuples of 8 64-bit registers, as implemented here llvm/llvm-project@7d94043

I noticed that the NXP LLVM fork with Zilsd support seems to allocate both v2i<xlen> and i<2*xlen> to the paired register class, but maybe we only need to do the former? I'm not sure, as it would be nice to be able to pass uint<2*xlen>_t to a GPR pair constraint.

I understand that pair constraint is the most complex part of this proposal. I have most of a patch for the other constraints, which I will try to finish in the next few days and post to LLVM.

lenary added a commit to lenary/llvm-project that referenced this pull request Oct 16, 2024
This change implements support for the `cr` and `cf` register
constraints (which allocate a RVC GPR or RVC FPR respectively), and the
`N` modifier (which prints the raw encoding of a register rather than the
name).

The intention behind these additions is to make it easier to use inline
assembly when assembling raw instructions that are not supported by the
compiler, for instance when experimenting with new instructions or when
supporting proprietary extensions outside the toolchain.

These implement part of my proposal in riscv-non-isa/riscv-c-api-doc#92

As part of the implementation, I felt there was not enough coverage of
inline assembly and the "in X" floating-point extensions, so I have
added more regression tests around these configurations.
@lenary
Copy link
Contributor Author

lenary commented Oct 16, 2024

I have an LLVM implementation of the c* constraints and the N modifier here llvm/llvm-project#112561 which has been approved.

I am still working on the implementation of the Pr constraint.

lenary added a commit to llvm/llvm-project that referenced this pull request Oct 18, 2024
This change implements support for the `cr` and `cf` register
constraints (which allocate a RVC GPR or RVC FPR respectively), and the
`N` modifier (which prints the raw encoding of a register rather than
the name).

The intention behind these additions is to make it easier to use inline
assembly when assembling raw instructions that are not supported by the
compiler, for instance when experimenting with new instructions or when
supporting proprietary extensions outside the toolchain.

These implement part of my proposal in riscv-non-isa/riscv-c-api-doc#92

As part of the implementation, I felt there was not enough coverage of
inline assembly and the "in X" floating-point extensions, so I have
added more regression tests around these configurations.
@lenary
Copy link
Contributor Author

lenary commented Oct 18, 2024

There was a question on #39 about whether the paired constraint means "an even GPR", or a "even-odd GPR pair", as both would likely print exactly the same as the even subregister.

This matters for liveness analysis as, if Pr meant "an even GPR", the odd GPR in the pair might be allocated to a different input/output for the inline assembly block, which would conflict with the use of the even/odd value for a pair.

So, to be clear, Pr means "treat this as an even-odd GPR pair for liveness" and "print this as the even subregister", so if the compiler choses a0/a1, then a0 is printed, but a1 will also be defined/used by the inline assembly block.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants