-
Notifications
You must be signed in to change notification settings - Fork 747
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
Initial support for building RISC-V runtimes targeting PolkaVM #3179
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work!
@@ -21,13 +21,16 @@ sp-api-proc-macro = { path = "proc-macro", default-features = false } | |||
sp-core = { path = "../core", default-features = false } | |||
sp-std = { path = "../std", default-features = false } | |||
sp-runtime = { path = "../runtime", default-features = false } | |||
sp-runtime-interface = { path = "../runtime-interface", default-features = false } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should directly import polkavm here. No need to pull in the entire sp-runtime-interface
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem here is that this needs access to the polkavm_abi
module we've defined in sp-runtime-interface
, so just directly including the PolkaVM crates is not sufficient.
So I can think of three options here:
- Define the
polkavm_abi
module insp-runtime-interface
and depend on it here. - Define the
polkavm_abi
insp-wasm-interface
(which actually isn't WASM specific, since PolkaVM support also uses what's defined in there because it reuses parts of the existing machinery to avoid having to rewrite everything, and thewasmtime
feature for that crate is optional anyway; a better name for that crate would be, uh,sp-runtime-interface
, but that's already taken, so maybe merging these two crates would be the way to go :P) - Define the
polkavm_abi
in a new crate, say,sp-polkavm-interface
, and depend on it here. (But that seems like an overkill to me to add a new crate for ~10 lines of code, and also because the stuff insp-wasm-interface
is also used it'd be weird to have WASM only usesp-wasm-interface
but PolkaVM use bothsp-polkavm-interface
andsp-wasm-interface
.)
What I would suggest we do here is:
- Leave the
polkavm_abi
definition insp-runtime-interface
and depend on it here. - (In another PR) Remove the
sp-wasm-interface
crate and merge it withsp-runtime-interface
. Then refactorsp-runtime-interface
so that it's more modularized and you don't need to always pull in the whole thing.
Does this sound good to you? (Alternatively maybe you have a better idea?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh okay, yeah then fine to leave it the way you have done it right now.
#[polkavm_derive::polkavm_define_abi(allow_extra_input_registers)] | ||
pub mod polkavm_abi {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does that do? :D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a magic macro. (;
Basically, it defines the trait machinery required to automatically convert types on an FFI boundary. It essentially generates this:
pub mod polkavm_abi {
pub trait IntoHost {
type Regs;
type Destructor;
fn into_host(value: Self) -> (Self::Regs, Self::Destructor);
}
pub trait FromHost {
type Regs;
fn from_host(value: Self::Regs) -> Self;
}
#[doc(hidden)]
pub mod private {
/* ...more stuff... */
}
}
The idea here is that you can define how the types are marshaled through the FFI boundary in one place, and then when you use the polkavm_import
or polkavm_export
macro you can give it a path to this module, and it will use the IntoHost
and FromHost
traits defined within to do the conversions. For example, something like this:
#[polkavm_derive::polkavm_define_abi]
pub mod my_abi {}
impl self::my_abi::FromHost for FancyType {
type Regs = (u32, u32,); // It needs two registers to be marshaled through the FFI boundary.
fn from_host(value: Self::Regs) -> Self { ... }
}
#[polkavm_derive::polkavm_import(abi = my_abi)]
extern {
fn hostcall() -> FancyType;
}
fn get_fancy() {
let fancy = unsafe { hostcall(); }
}
This is somewhat similar to the traits we already have in Substrate for converting into/from WASM FFI types, except it's part of the upstream instead of being Substrate-specific, and you can have multiple ABIs defined (e.g. you could have one for Substrate runtimes and another for smart contracts, and they won't conflict with each other).
); | ||
}, | ||
RuntimeTarget::Riscv => { | ||
rustflags.push_str("-C target-feature=+lui-addi-fusion -C relocation-model=pie -C link-arg=--emit-relocs -C link-arg=--unique "); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was hitting duplicate symbol name when implementing PolkaVM support for ink!. Essentially the call
export was having problems. Is the --unique
a solution for that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, the --unique
just allows LLD to emit multiple sections, which allows our linker to better optimize the blob in some cases.
Your problems with duplicate symbols are probably fixed in the newest version of the PolkaVM derive crate. With each successive version I've been coming up with less hacky ways to do imports/exports, and the way they're implemented right now manages to exclusively use rustc
to generate the symbol names for us so there should not be any dupes anymore.
Co-authored-by: Bastian Köcher <git@kchr.de>
Co-authored-by: Bastian Köcher <git@kchr.de>
Co-authored-by: Bastian Köcher <git@kchr.de>
- .docker-env | ||
- .common-refs | ||
script: | ||
- SUBSTRATE_RUNTIME_TARGET=riscv cargo check -p minimal-runtime |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Btw why does it only run check and not build?
I think there are possible errors that only arise on build
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The builder runs as part of the build.rs
. It can not detect if you are running cargo check
and thus, it will always compile. So, everything works here as expected.
…ytech#3179) This PR adds initial support for building RISC-V runtimes targeting PolkaVM. - Setting the `SUBSTRATE_RUNTIME_TARGET=riscv` environment variable will now build a RISC-V runtime instead of a WASM runtime. - This only adds support for *building* runtimes; running them will need a PolkaVM-based executor, which I will add in a future PR. - Only building the minimal runtime is supported (building the Polkadot runtime doesn't work *yet* due to one of the dependencies). - The builder now sets a `substrate_runtime` cfg flag when building the runtimes, with the idea being that instead of doing `#[cfg(not(feature = "std"))]` or `#[cfg(target_arch = "wasm32")]` to detect that we're building a runtime you'll do `#[cfg(substrate_runtime)]`. (Switching the whole codebase to use this will be done in a future PR; I deliberately didn't do this here to keep this PR minimal and reviewable.) - Further renaming of things (e.g. types, environment variables and proc macro attributes having "wasm" in their name) to be target-agnostic will also be done in a future refactoring PR (while keeping backwards compatibility where it makes sense; I don't intend to break anyone's workflow or create unnecessary churn). - This PR also fixes two bugs in the `wasm-builder` crate: * The `RUSTC` environment variable is now removed when invoking the compiler. This prevents the toolchain version from being overridden when called from a `build.rs` script. * When parsing the `rustup toolchain list` output the `(default)` is now properly stripped and not treated as part of the version. - I've also added a minimal CI job that makes sure this doesn't break in the future. (cc @paritytech/ci) cc @athei ------ Also, just a fun little tidbit: quickly comparing the size of the built runtimes it seems that the PolkaVM runtime is slightly smaller than the WASM one. (`production` build, with the `names` section substracted from the WASM's size to keep things fair, since for the PolkaVM runtime we're currently stripping out everything) - `.wasm`: 625505 bytes - `.wasm` (after wasm-opt -O3): 563205 bytes - `.wasm` (after wasm-opt -Os): 562987 bytes - `.wasm` (after wasm-opt -Oz): 536852 bytes - `.polkavm`: ~~580338 bytes~~ 550476 bytes (after enabling extra target features; I'll add those in another PR once we have an executor working) --------- Co-authored-by: Bastian Köcher <git@kchr.de>
This PR adds initial support for building RISC-V runtimes targeting PolkaVM.
SUBSTRATE_RUNTIME_TARGET=riscv
environment variable will now build a RISC-V runtime instead of a WASM runtime.substrate_runtime
cfg flag when building the runtimes, with the idea being that instead of doing#[cfg(not(feature = "std"))]
or#[cfg(target_arch = "wasm32")]
to detect that we're building a runtime you'll do#[cfg(substrate_runtime)]
. (Switching the whole codebase to use this will be done in a future PR; I deliberately didn't do this here to keep this PR minimal and reviewable.)wasm-builder
crate:RUSTC
environment variable is now removed when invoking the compiler. This prevents the toolchain version from being overridden when called from abuild.rs
script.rustup toolchain list
output the(default)
is now properly stripped and not treated as part of the version.cc @athei
Also, just a fun little tidbit: quickly comparing the size of the built runtimes it seems that the PolkaVM runtime is slightly smaller than the WASM one. (
production
build, with thenames
section substracted from the WASM's size to keep things fair, since for the PolkaVM runtime we're currently stripping out everything).wasm
: 625505 bytes.wasm
(after wasm-opt -O3): 563205 bytes.wasm
(after wasm-opt -Os): 562987 bytes.wasm
(after wasm-opt -Oz): 536852 bytes.polkavm
:580338 bytes550476 bytes (after enabling extra target features; I'll add those in another PR once we have an executor working)