Skip to content

Commit

Permalink
Add support for multiple target environments
Browse files Browse the repository at this point in the history
  • Loading branch information
XAMPPRocky committed Mar 26, 2021
1 parent 908487a commit 94ae624
Show file tree
Hide file tree
Showing 13 changed files with 196 additions and 108 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,4 @@ cargo_test_no_features examples/runners/cpu
cargo_test_no_features examples/shaders/sky-shader
cargo_test_no_features examples/shaders/simplest-shader

cargo compiletest
cargo compiletest --target-env unknown,vulkan1.1,spv1.3
26 changes: 19 additions & 7 deletions crates/rustc_codegen_spirv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ fn is_blocklisted_fn<'tcx>(
false
}

fn target_options() -> Target {
fn target_options(env: Option<spirv_tools::TargetEnv>) -> Target {
Target {
llvm_target: "no-llvm".to_string(),
pointer_width: 32,
Expand All @@ -222,6 +222,10 @@ fn target_options() -> Target {
linker_flavor: LinkerFlavor::Ld,
panic_strategy: PanicStrategy::Abort,
os: "unknown".to_string(),
env: env
.as_ref()
.map(ToString::to_string)
.unwrap_or_else(|| "unknown".to_string()),
// TODO: Investigate if main_needs_argc_argv is useful (for building exes)
main_needs_argc_argv: false,
..Default::default()
Expand Down Expand Up @@ -284,12 +288,20 @@ impl CodegenBackend for SpirvCodegenBackend {

fn target_override(&self, opts: &config::Options) -> Option<Target> {
match opts.target_triple {
TargetTriple::TargetTriple(ref target_triple) => match &**target_triple {
// TODO: Do we want to match *everything* here, since, well, we only support one thing? that will allow
// folks to not specify --target spirv-unknown-unknown on the commandline.
"spirv-unknown-unknown" => Some(target_options()),
_ => None,
},
TargetTriple::TargetTriple(ref target_triple) => {
const ARCH_VENDOR: &str = "spirv-unknown-";
if !target_triple.starts_with(ARCH_VENDOR) {
return None;
}

let env = &target_triple[ARCH_VENDOR.len()..];

match env.parse() {
Ok(env) => Some(target_options(Some(env))),
_ if env == "unknown" => Some(target_options(None)),
_ => None,
}
}
TargetTriple::TargetPath(_) => None,
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/rustc_codegen_spirv/src/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ fn do_spirv_opt(sess: &Session, spv_binary: Vec<u32>, filename: &Path) -> Vec<u3
fn do_spirv_val(sess: &Session, spv_binary: &[u32], filename: &Path) {
use spirv_tools::val::{self, Validator};

let validator = val::create(None);
let validator = val::create(sess.target.options.env.parse().ok());

if let Err(e) = validator.validate(spv_binary, None) {
let mut err = sess.struct_err(&e.to_string());
Expand Down
11 changes: 11 additions & 0 deletions docs/src/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,16 @@ cargo compiletest arch/u image
The above command will only test `ui/arch/u_*.rs` and `ui/image/*.rs`, and skip
everything else. You can also add `--bless` to update expected outputs, as well.

### Testing Different Environments

You can test against multiple different SPIR-V environments with the
`--target-env` flag. By default it is set to `unknown`.

```bash
cargo compiletest --target-env=vulkan1.1
# You can also provide multiple values to test multiple environments
cargo compiletest --target-env=vulkan1.1,spv.1.3
```

[`compiletest`]: https://github.com/laumann/compiletest-rs
[rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/tests/intro.html
201 changes: 117 additions & 84 deletions tests/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,22 @@ struct Opt {
/// Only run tests that match these filters
#[structopt(name = "FILTER")]
filters: Vec<String>,

/// The environment to compile to the SPIR-V tests.
#[structopt(long)]
target_env: Option<String>,
}

impl Opt {
pub fn environments(&self) -> Vec<String> {
match &self.target_env {
Some(env) => env.split(",").map(String::from).collect(),
None => vec!["unknown".into()],
}
}
}

const TARGET: &str = "spirv-unknown-unknown";
const TARGET_PREFIX: &str = "spirv-unknown-";

#[derive(Copy, Clone)]
enum DepKind {
Expand All @@ -38,18 +51,18 @@ impl DepKind {
}
}

fn target_dir_suffix(self) -> &'static str {
fn target_dir_suffix(self, target: &str) -> String {
match self {
Self::SpirvLib => "spirv-unknown-unknown/debug/deps",
Self::ProcMacro => "debug/deps",
Self::SpirvLib => format!("{}/debug/deps", target),
Self::ProcMacro => "debug/deps".into(),
}
}
}

fn main() {
let opt = Opt::from_args();

let tests_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
let tests_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let workspace_root = tests_dir.parent().unwrap();
let original_target_dir = workspace_root.join("target");
let deps_target_dir = original_target_dir.join("compiletest-deps");
Expand All @@ -58,90 +71,99 @@ fn main() {
// Pull in rustc_codegen_spirv as a dynamic library in the same way
// spirv-builder does.
let codegen_backend_path = find_rustc_codegen_spirv();
let libs = build_deps(&deps_target_dir, &codegen_backend_path);

run_mode(
"ui",
let runner = Runner {
opt,
tests_dir,
compiletest_build_dir,
&deps_target_dir,
&codegen_backend_path,
&libs,
);
deps_target_dir,
codegen_backend_path,
};

runner.run_mode("ui");
}

// FIXME(eddyb) a bunch of these functions could be nicer if they were methods.

/// Runs the given `mode` on the directory that matches that name, using the
/// backend provided by `codegen_backend_path`.
fn run_mode(
mode: &'static str,
struct Runner {
opt: Opt,
tests_dir: &Path,
tests_dir: PathBuf,
compiletest_build_dir: PathBuf,
deps_target_dir: &Path,
codegen_backend_path: &Path,
libs: &TestDeps,
) {
let mut config = compiletest::Config::default();

/// RUSTFLAGS passed to all test files.
fn test_rustc_flags(
codegen_backend_path: &Path,
deps: &TestDeps,
indirect_deps_dirs: &[&Path],
) -> String {
[
&*rust_flags(codegen_backend_path),
&*indirect_deps_dirs
.iter()
.map(|dir| format!("-L dependency={}", dir.display()))
.fold(String::new(), |a, b| b + " " + &a),
"--edition 2018",
&*format!("--extern noprelude:core={}", deps.core.display()),
&*format!(
"--extern noprelude:compiler_builtins={}",
deps.compiler_builtins.display()
),
&*format!(
"--extern spirv_std_macros={}",
deps.spirv_std_macros.display()
),
&*format!("--extern spirv_std={}", deps.spirv_std.display()),
&*format!("--extern glam={}", deps.glam.display()),
"--crate-type dylib",
"-Zunstable-options",
"-Zcrate-attr=no_std",
"-Zcrate-attr=feature(register_attr,asm)",
"-Zcrate-attr=register_attr(spirv)",
]
.join(" ")
}

let flags = test_rustc_flags(
codegen_backend_path,
libs,
&[
&deps_target_dir.join(DepKind::SpirvLib.target_dir_suffix()),
&deps_target_dir.join(DepKind::ProcMacro.target_dir_suffix()),
],
);
deps_target_dir: PathBuf,
codegen_backend_path: PathBuf,
}

config.target_rustcflags = Some(flags);
config.mode = mode.parse().expect("Invalid mode");
config.target = String::from(TARGET);
config.src_base = tests_dir.join(mode);
config.build_base = compiletest_build_dir;
config.bless = opt.bless;
config.filters = opt.filters;
config.clean_rmeta();
impl Runner {
/// Runs the given `mode` on the directory that matches that name, using the
/// backend provided by `codegen_backend_path`.
fn run_mode(&self, mode: &'static str) {
/// RUSTFLAGS passed to all test files.
fn test_rustc_flags(
codegen_backend_path: &Path,
deps: &TestDeps,
indirect_deps_dirs: &[&Path],
) -> String {
[
&*rust_flags(codegen_backend_path),
&*indirect_deps_dirs
.iter()
.map(|dir| format!("-L dependency={}", dir.display()))
.fold(String::new(), |a, b| b + " " + &a),
"--edition 2018",
&*format!("--extern noprelude:core={}", deps.core.display()),
&*format!(
"--extern noprelude:compiler_builtins={}",
deps.compiler_builtins.display()
),
&*format!(
"--extern spirv_std_macros={}",
deps.spirv_std_macros.display()
),
&*format!("--extern spirv_std={}", deps.spirv_std.display()),
&*format!("--extern glam={}", deps.glam.display()),
"--crate-type dylib",
"-Zunstable-options",
"-Zcrate-attr=no_std",
"-Zcrate-attr=feature(register_attr,asm)",
"-Zcrate-attr=register_attr(spirv)",
]
.join(" ")
}

compiletest::run_tests(&config);
for env in self.opt.environments() {
let target = format!("{}{}", TARGET_PREFIX, env);
let mut config = compiletest::Config::default();
let libs = build_deps(&self.deps_target_dir, &self.codegen_backend_path, &target);

let flags = test_rustc_flags(
&self.codegen_backend_path,
&libs,
&[
&self
.deps_target_dir
.join(DepKind::SpirvLib.target_dir_suffix(&target)),
&self
.deps_target_dir
.join(DepKind::ProcMacro.target_dir_suffix(&target)),
],
);

config.target_rustcflags = Some(flags);
config.mode = mode.parse().expect("Invalid mode");
config.target = target;
config.src_base = self.tests_dir.join(mode);
config.build_base = self.compiletest_build_dir.clone();
config.bless = self.opt.bless;
config.filters = self.opt.filters.clone();
config.clean_rmeta();

compiletest::run_tests(&config);
}
}
}

/// Runs the processes needed to build `spirv-std` & other deps.
fn build_deps(deps_target_dir: &Path, codegen_backend_path: &Path) -> TestDeps {
fn build_deps(deps_target_dir: &Path, codegen_backend_path: &Path, target: &str) -> TestDeps {
// HACK(eddyb) this is only needed until we enable `resolver = "2"`, as the
// old ("1") resolver has a bug where it picks up extra features based on the
// current directory (and so we always set the working dir as a workaround).
Expand All @@ -154,7 +176,7 @@ fn build_deps(deps_target_dir: &Path, codegen_backend_path: &Path) -> TestDeps {
"-p",
"compiletests-deps-helper",
"-Zbuild-std=core",
&*format!("--target={}", TARGET),
&*format!("--target={}", target),
])
.arg("--target-dir")
.arg(deps_target_dir)
Expand All @@ -166,13 +188,23 @@ fn build_deps(deps_target_dir: &Path, codegen_backend_path: &Path) -> TestDeps {
.and_then(map_status_to_result)
.unwrap();

let compiler_builtins =
find_lib(deps_target_dir, "compiler_builtins", DepKind::SpirvLib).unwrap();
let core = find_lib(deps_target_dir, "core", DepKind::SpirvLib).unwrap();
let spirv_std = find_lib(deps_target_dir, "spirv_std", DepKind::SpirvLib).unwrap();
let glam = find_lib(deps_target_dir, "glam", DepKind::SpirvLib).unwrap();
let spirv_std_macros =
find_lib(deps_target_dir, "spirv_std_macros", DepKind::ProcMacro).unwrap();
let compiler_builtins = find_lib(
deps_target_dir,
"compiler_builtins",
DepKind::SpirvLib,
target,
)
.unwrap();
let core = find_lib(deps_target_dir, "core", DepKind::SpirvLib, target).unwrap();
let spirv_std = find_lib(deps_target_dir, "spirv_std", DepKind::SpirvLib, target).unwrap();
let glam = find_lib(deps_target_dir, "glam", DepKind::SpirvLib, target).unwrap();
let spirv_std_macros = find_lib(
deps_target_dir,
"spirv_std_macros",
DepKind::ProcMacro,
target,
)
.unwrap();

if [
&compiler_builtins,
Expand All @@ -185,7 +217,7 @@ fn build_deps(deps_target_dir: &Path, codegen_backend_path: &Path) -> TestDeps {
.any(|o| o.is_none())
{
clean_deps(deps_target_dir);
build_deps(deps_target_dir, codegen_backend_path)
build_deps(deps_target_dir, codegen_backend_path, target)
} else {
TestDeps {
core: core.unwrap(),
Expand Down Expand Up @@ -215,12 +247,13 @@ fn find_lib(
deps_target_dir: &Path,
base: impl AsRef<Path>,
dep_kind: DepKind,
target: &str,
) -> Result<Option<PathBuf>> {
let base = base.as_ref();
let (expected_prefix, expected_extension) = dep_kind.prefix_and_extension();
let expected_name = format!("{}{}", expected_prefix, base.display());

let dir = deps_target_dir.join(dep_kind.target_dir_suffix());
let dir = deps_target_dir.join(dep_kind.target_dir_suffix(target));

let paths = std::fs::read_dir(dir)?
.filter_map(Result::ok)
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/image/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use spirv_std::{arch, Image2d};

#[spirv(fragment)]
pub fn main(#[spirv(uniform_constant)] image: &Image2d, output: &mut glam::Vec4) {
pub fn main(#[spirv(uniform_constant, descriptor_set = 0, binding = 0)] image: &Image2d, output: &mut glam::Vec4) {
let texel = image.fetch(glam::IVec2::new(0, 1));
*output = texel;
}
29 changes: 29 additions & 0 deletions tests/ui/image/issue_527.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use glam::*;

#[spirv(block)]
pub struct PointsBuffer {
points: [UVec2; 100],
}

#[spirv(compute(threads(1)))]
pub fn main_cs(
#[spirv(global_invocation_id)]
id: UVec3,
#[spirv(storage_buffer, descriptor_set = 0, binding = 0)]
points_buffer: &mut PointsBuffer,
#[spirv(uniform_constant, descriptor_set = 1, binding = 1)]
image: &spirv_std::StorageImage2d,
) {
unsafe { asm!("OpCapability StorageImageWriteWithoutFormat") };
let position = id.xy();
for i in 0..100usize {
let p0 = &points_buffer.points[i];
let p1 = &points_buffer.points[i + 1];
if p0.x == position.x && p1.y == position.y {
unsafe {
image.write(position, vec2(1.0, 0.0));
};
}
}
}

Loading

0 comments on commit 94ae624

Please sign in to comment.