-
Notifications
You must be signed in to change notification settings - Fork 444
Add support for RVV 1.0 #542
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -99,6 +99,113 @@ fn is_wasm32() -> bool { | |
| target_components()[0] == "wasm32" | ||
| } | ||
|
|
||
| fn is_riscv64() -> bool { | ||
| target_components()[0].starts_with("riscv64") | ||
| } | ||
|
|
||
| fn is_cross_compiling() -> bool { | ||
| let host = env::var("HOST").unwrap(); | ||
| let target = env::var("TARGET").unwrap(); | ||
| host != target | ||
| } | ||
|
|
||
| fn test_rvv_runtime_support() -> bool { | ||
| use std::fs; | ||
| use std::io::Write; | ||
| use std::process::Command; | ||
|
|
||
| let out_dir = env::var("OUT_DIR").unwrap(); | ||
| let test_c = format!("{}/test_rvv.c", out_dir); | ||
| let test_bin = format!("{}/test_rvv", out_dir); | ||
|
|
||
| let test_code = b" | ||
| #include <riscv_vector.h> | ||
| #include <stdint.h> | ||
|
|
||
| int main() { | ||
| size_t vl = __riscv_vsetvlmax_e32m1(); | ||
| if (vl == 0) return 1; | ||
|
|
||
| vuint32m1_t v32_a = __riscv_vmv_v_x_u32m1(42, vl); | ||
| vuint32m1_t v32_b = __riscv_vmv_v_x_u32m1(17, vl); | ||
| vuint32m1_t v32_sum = __riscv_vadd_vv_u32m1(v32_a, v32_b, vl); | ||
| vuint32m1_t v32_xor = __riscv_vxor_vv_u32m1(v32_a, v32_b, vl); | ||
| vuint32m1_t v32_or = __riscv_vor_vv_u32m1(v32_a, v32_b, vl); | ||
| vuint32m1_t v32_srl = __riscv_vsrl_vx_u32m1(v32_a, 4, vl); | ||
| vuint32m1_t v32_sll = __riscv_vsll_vx_u32m1(v32_a, 4, vl); | ||
|
|
||
| uint32_t data32[16] = {0}; | ||
| vuint32m1_t v32_loaded = __riscv_vle32_v_u32m1(data32, vl); | ||
| __riscv_vsse32_v_u32m1(data32, sizeof(uint32_t) * 2, v32_sum, vl); | ||
|
|
||
| uint64_t data64[16] = {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60}; | ||
| vuint64m2_t v64 = __riscv_vle64_v_u64m2(data64, vl); | ||
| vuint64m2_t v64_add = __riscv_vadd_vx_u64m2(v64, 100, vl); | ||
|
|
||
| uint32_t src_data[16] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160}; | ||
| vuint32m1_t v32_indexed = __riscv_vluxei64_v_u32m1((const uint32_t *)src_data, v64, vl); | ||
|
|
||
| uint32_t result_sum[16] = {0}; | ||
| uint32_t result_indexed[16] = {0}; | ||
| __riscv_vse32_v_u32m1(result_sum, v32_sum, vl); | ||
| __riscv_vse32_v_u32m1(result_indexed, v32_indexed, vl); | ||
|
|
||
| if (result_sum[0] != 59) return 2; | ||
| if (result_indexed[0] != 10) return 3; | ||
| if (vl > 1 && result_indexed[1] != 20) return 4; | ||
|
|
||
| return 0; | ||
| } | ||
| "; | ||
|
|
||
| let mut f = match fs::File::create(&test_c) { | ||
| Ok(f) => f, | ||
| Err(e) => { | ||
| println!("cargo:warning=Failed to create RVV test file: {}", e); | ||
| return false; | ||
| } | ||
| }; | ||
|
|
||
| if let Err(e) = f.write_all(test_code) { | ||
| println!("cargo:warning=Failed to write RVV test file: {}", e); | ||
| return false; | ||
| } | ||
|
|
||
| drop(f); | ||
|
|
||
| let mut build = cc::Build::new(); | ||
| build.flag("-march=rv64gcv"); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is certainly testing something, but it is testing Even though it is not possible to check for vector extension even in Nightly Rust right now, this will serve as a piece of documentation for things actually used in the code. Intrinsics above are already good, but actual extensions will be even better. |
||
| let compiler = build.get_compiler(); | ||
|
|
||
| let mut cmd = compiler.to_command(); | ||
| cmd.arg(&test_c).arg("-o").arg(&test_bin); | ||
|
|
||
| if !cmd.status().map(|s| s.success()).unwrap_or(false) { | ||
| return false; | ||
| } | ||
|
|
||
| match Command::new(&test_bin).output() { | ||
| Ok(output) => output.status.success(), | ||
| Err(_) => false, | ||
| } | ||
| } | ||
|
|
||
| fn is_rvv() -> bool { | ||
| if defined("CARGO_FEATURE_RVV") { | ||
| return true; | ||
| } | ||
|
|
||
| if is_pure() { | ||
| return false; | ||
| } | ||
|
|
||
| if !is_cross_compiling() && is_riscv64() && is_little_endian() { | ||
| return test_rvv_runtime_support(); | ||
| } | ||
|
|
||
| false | ||
| } | ||
|
Comment on lines
+102
to
+207
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is very limited, I'd be nicer to detect the availability of extensions rather than parsing target triple for just a few known good values. I have a custom target that is called The fact that override exists helps, but it'd be much nicer if feature detection just worked out of the box. |
||
|
|
||
| fn endianness() -> String { | ||
| let endianness = env::var("CARGO_CFG_TARGET_ENDIAN").unwrap(); | ||
| assert!(endianness == "little" || endianness == "big"); | ||
|
|
@@ -300,6 +407,14 @@ fn build_wasm32_simd() { | |
| println!("cargo:rustc-cfg=blake3_wasm32_simd"); | ||
| } | ||
|
|
||
| fn build_rvv_c_intrinsics() { | ||
| let mut build = new_build(); | ||
| build.file("c/blake3_rvv.c"); | ||
| build.flag("-march=rv64gcv"); | ||
| build.define("BLAKE3_USE_RVV", "1"); | ||
| build.compile("blake3_rvv"); | ||
| } | ||
|
|
||
| fn main() -> Result<(), Box<dyn std::error::Error>> { | ||
| // As of Rust 1.80, unrecognized config names are warnings. Give Cargo all of our config names. | ||
| let all_cfgs = [ | ||
|
|
@@ -312,6 +427,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> { | |
| "blake3_avx512_ffi", | ||
| "blake3_neon", | ||
| "blake3_wasm32_simd", | ||
| "blake3_rvv", | ||
| ]; | ||
| for cfg_name in all_cfgs { | ||
| // TODO: Switch this whole file to the new :: syntax when our MSRV reaches 1.77. | ||
|
|
@@ -327,6 +443,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> { | |
| panic!("It doesn't make sense to enable both \"no_neon\" and \"neon\"."); | ||
| } | ||
|
|
||
| if is_pure() && is_rvv() { | ||
| panic!("It doesn't make sense to enable both \"pure\" and \"rvv\"."); | ||
| } | ||
|
|
||
| if is_x86_64() || is_x86_32() { | ||
| let support = c_compiler_support(); | ||
| if is_x86_32() || should_prefer_intrinsics() || is_pure() || support == NoCompiler { | ||
|
|
@@ -361,6 +481,16 @@ fn main() -> Result<(), Box<dyn std::error::Error>> { | |
| build_wasm32_simd(); | ||
| } | ||
|
|
||
| if is_rvv() && is_big_endian() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay. |
||
| panic!("The RVV implementation doesn't support big-endian RISC-V.") | ||
| } | ||
|
|
||
| // Enable RVV if explicitly requested via feature flag | ||
| if is_riscv64() && is_rvv() { | ||
| println!("cargo:rustc-cfg=blake3_rvv"); | ||
| build_rvv_c_intrinsics(); | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it should always build RVV support unless
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately, although RISC-V does provide an x86
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see. And That said, you could still conditionally check for features in OS-specific way when compiled for the OS. |
||
|
|
||
| // The `cc` crate doesn't automatically emit rerun-if directives for the | ||
| // environment variables it supports, in particular for $CC. We expect to | ||
| // do a lot of benchmarking across different compilers, so we explicitly | ||
|
|
||
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.
This is a single intrinsic. Does it cover all extensions used by this implementation or maybe more of them need to be added?
Uh oh!
There was an error while loading. Please reload this page.
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.
This time all functions used in the arch specific code are tested.