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

riscv-rt: partial compatibility with RV32E #243

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

romancardenas
Copy link
Contributor

Avoids using x16+ registers in assembly code. This is necessary to provide compatibility with RV32E.
Note that there is still work to do. Namely, the trap frame must adapt to the target base instruction set.

Solves #189

Related: #239

@romancardenas romancardenas requested a review from a team as a code owner November 9, 2024 09:19
@romancardenas romancardenas changed the title Compatibility with RV32E riscv-rt: partial compatibility with RV32E Nov 9, 2024
Comment on lines 573 to 604
/// `x1`: return address, stores the address to return to after a function call or interrupt.
pub ra: usize,
/// `x5`: temporary register `t0`, used for intermediate values.
pub t0: usize,
/// `x6`: temporary register `t1`, used for intermediate values.
pub t1: usize,
/// `x7`: temporary register `t2`, used for intermediate values.
pub t2: usize,
/// `x28`: temporary register `t3`, used for intermediate values.
pub t3: usize,
/// `x29`: temporary register `t4`, used for intermediate values.
pub t4: usize,
/// `x30`: temporary register `t5`, used for intermediate values.
pub t5: usize,
/// `x31`: temporary register `t6`, used for intermediate values.
pub t6: usize,
/// `x10`: argument register `a0`. Used to pass the first argument to a function.
pub a0: usize,
/// `x11`: argument register `a1`. Used to pass the second argument to a function.
pub a1: usize,
/// `x12`: argument register `a2`. Used to pass the third argument to a function.
pub a2: usize,
/// `x13`: argument register `a3`. Used to pass the fourth argument to a function.
pub a3: usize,
/// `x14`: argument register `a4`. Used to pass the fifth argument to a function.
pub a4: usize,
/// `x15`: argument register `a5`. Used to pass the sixth argument to a function.
pub a5: usize,
/// `x16`: argument register `a6`. Used to pass the seventh argument to a function.
pub a6: usize,
/// `x17`: argument register `a7`. Used to pass the eighth argument to a function.
pub a7: usize,
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it be a good idea to order these fields by there x* name (similar to the spec)?

Also, maybe we use coniditional compilation to not include the x16-x31 registers for RV32E builds?

Mostly thinking about downstream users that may use a pointer to the TrapFrame, which they may or may not try to map to memory layout.

It may not be necessary, but could be a helpful reminder that those registers don't exist on RV32E platforms.

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'd try to avoid changing the order of the attributes in case users use C or assembly code that relies on the current order.

I'm working on feature-gating registers for RVE32. It's a bit tricky, as we need to add "trash" padding to ensure that the stack is 16-byte aligned. I'll open a PR shortly with all this.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'd try to avoid changing the order of the attributes in case users use C or assembly code that relies on the current order.

I guess that's also part of what I'm asking, "should we intentionally break downstream users compiling RV32E?" Especially regarding not providing them in-memory registers that don't exist on the platform.

For non-RV32E users, I agree with you, we should avoid breaking them without a good reason.

I'm working on feature-gating registers for RVE32. It's a bit tricky, as we need to add "trash" padding to ensure that the stack is 16-byte aligned. I'll open a PR shortly with all this.

Sounds good, I'll keep an eye out for it.

Maybe for the missing registers on RV32E targets:

pub struct TrapFrame {
    #[cfg(not(rv32e))]
    pub a6: usize,
    #[cfg(rv32e)]
    _a6: usize,
    ...
}

@romancardenas
Copy link
Contributor Author

I've been working on a complete riscv-target-parser crate to help us in the build scripts. Also, I think it is time to stop using the riscv, riscv32, and riscv64 cfg flags and just use target_arch.

My main challenge now is configuring riscv-rt-macros according to flags triggered in riscv-rt. I don't know how to do this without features, but I will read elsewhere and see if there is any other approach. I don't love letting users activate a riscve feature regardless of the target.

@rmsyn
Copy link
Contributor

rmsyn commented Nov 19, 2024

My main challenge now is configuring riscv-rt-macros according to flags triggered in riscv-rt. I don't know how to do this without features

If this specifically about RV32E stuff, maybe a combination of target_arch and target_feature flags in a #[cfg(...)]? Although, it looks like there are some restrictions on using target_feature on riscv* targets.

I would be happy to help out, if you have some work-in-progress code you would like extra eyes on.

@romancardenas
Copy link
Contributor Author

This is the best way I could think of. I tried forwarding environment variables from build scripts to no avail, as procedural macros are compiled separately from regular crates. The macro code can be squeezed a bit, but more or less this is it.

Copy link
Contributor

@rmsyn rmsyn left a comment

Choose a reason for hiding this comment

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

This is the best way I could think of. I tried forwarding environment variables from build scripts to no avail, as procedural macros are compiled separately from regular crates. The macro code can be squeezed a bit, but more or less this is it.

Yeah, I tried using conditional compilation with target_feature = "i" + target_feature = "e", but it doesn't look like base ISA are available.

If target_feature = "i" and target_feature = "e" were available, we could use conditional compilation to define a core_interrupt_inner the four variants we care about, i.e. riscv32e, riscv32i, riscv64e, riscv64i. Then we could have the single core_interrupt macro function wrapping the unsafe inner functions.

I can look at the rustc/cargo source, and see what it takes to add base ISA as conditional compilation attribute. It may make more sense for the base ISA stuff to go under the instruction_set attribute (currently only has ARM in the docs).

The changes here LGTM.

Edit: something like this is what I was thinking: rv32e-compat-asm...rmsyn:riscv:rv32e-compat-asm

@romancardenas
Copy link
Contributor Author

I tried something like that, but I found out that the procedural macros are compiled for the host, not the target. Thus, the cfg(target_arch = "riscv32") does not work unless your host machine is a RV32

@rmsyn
Copy link
Contributor

rmsyn commented Nov 23, 2024

I tried something like that, but I found out that the procedural macros are compiled for the host, not the target. Thus, the cfg(target_arch = "riscv32") does not work unless your host machine is a RV32

What about replacing them with the cfg! macro, which is evaluated at run-time instead of build-time?

Something like: rmsyn/riscv@rust-embedded:riscv:rv32e-compat-asm...rv32e-compat-asm

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