From b6311789bb4868bd8176d792d4bccfb83406383f Mon Sep 17 00:00:00 2001 From: XAMPPRocky <4464295+XAMPPRocky@users.noreply.github.com> Date: Mon, 12 Apr 2021 13:19:28 +0200 Subject: [PATCH] Add new handling for SPIR-V targets (#559) * Add new handling for SPIR-V targets * Ensure target is always three components --- .github/workflows/test.sh | 2 +- crates/rustc_codegen_spirv/src/abi.rs | 4 +- .../src/builder/ext_inst.rs | 6 +- .../src/builder/intrinsics.rs | 44 +++--- .../rustc_codegen_spirv/src/builder_spirv.rs | 29 ++-- .../src/codegen_cx/constant.rs | 4 +- .../rustc_codegen_spirv/src/codegen_cx/mod.rs | 41 ++--- crates/rustc_codegen_spirv/src/lib.rs | 49 +----- crates/rustc_codegen_spirv/src/spirv_type.rs | 6 +- crates/rustc_codegen_spirv/src/symbols.rs | 20 --- crates/rustc_codegen_spirv/src/target.rs | 149 ++++++++++++++++++ crates/spirv-builder/src/lib.rs | 55 ++----- crates/spirv-builder/src/test/basic.rs | 10 +- crates/spirv-builder/src/test/mod.rs | 2 +- docs/src/platform-support.md | 39 +++++ docs/src/writing-shader-crates.md | 15 +- examples/multibuilder/src/main.rs | 3 +- examples/runners/ash/src/main.rs | 11 +- examples/runners/wgpu/build.rs | 4 +- tests/src/main.rs | 2 +- 20 files changed, 285 insertions(+), 210 deletions(-) create mode 100644 crates/rustc_codegen_spirv/src/target.rs diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 668ff88be2..ff66ff20b1 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -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 --target-env unknown,vulkan1.1,spv1.3 +cargo compiletest --target-env vulkan1.1,spv1.3 diff --git a/crates/rustc_codegen_spirv/src/abi.rs b/crates/rustc_codegen_spirv/src/abi.rs index 61e522da31..8da069d8d3 100644 --- a/crates/rustc_codegen_spirv/src/abi.rs +++ b/crates/rustc_codegen_spirv/src/abi.rs @@ -415,7 +415,7 @@ fn trans_scalar<'tcx>( match scalar.value { Primitive::Int(width, mut signedness) => { - if cx.kernel_mode { + if cx.target.is_kernel() { signedness = false; } SpirvType::Integer(width.size().bits() as u32, signedness).def(span, cx) @@ -644,7 +644,7 @@ pub fn auto_struct_layout<'tcx>( fn trans_struct<'tcx>(cx: &CodegenCx<'tcx>, span: Span, ty: TyAndLayout<'tcx>) -> Word { if let TyKind::Foreign(_) = ty.ty.kind() { // "An unsized FFI type that is opaque to Rust", `extern type A;` (currently unstable) - if cx.kernel_mode { + if cx.target.is_kernel() { // TODO: This should use the name of the struct as the name. However, names are not stable across crates, // e.g. core::fmt::Opaque in one crate and fmt::Opaque in core. return SpirvType::Opaque { diff --git a/crates/rustc_codegen_spirv/src/builder/ext_inst.rs b/crates/rustc_codegen_spirv/src/builder/ext_inst.rs index ef847a2a71..26611fd5e6 100644 --- a/crates/rustc_codegen_spirv/src/builder/ext_inst.rs +++ b/crates/rustc_codegen_spirv/src/builder/ext_inst.rs @@ -17,7 +17,7 @@ pub struct ExtInst { impl ExtInst { pub fn import_glsl<'a, 'tcx>(&mut self, bx: &Builder<'a, 'tcx>) -> Word { - assert!(!bx.kernel_mode); + assert!(!bx.target.is_kernel()); match self.glsl { Some(id) => id, None => { @@ -29,7 +29,7 @@ impl ExtInst { } pub fn import_opencl<'a, 'tcx>(&mut self, bx: &Builder<'a, 'tcx>) -> Word { - assert!(bx.kernel_mode); + assert!(bx.target.is_kernel()); match self.opencl { Some(id) => id, None => { @@ -42,7 +42,7 @@ impl ExtInst { pub fn import_integer_functions_2_intel<'tcx>(&mut self, cx: &CodegenCx<'tcx>) { if !self.integer_functions_2_intel { - assert!(!cx.kernel_mode); + assert!(!cx.target.is_kernel()); self.integer_functions_2_intel = true; cx.emit_global() .extension("SPV_INTEL_shader_integer_functions2"); diff --git a/crates/rustc_codegen_spirv/src/builder/intrinsics.rs b/crates/rustc_codegen_spirv/src/builder/intrinsics.rs index 6a181d81a1..e7ea98b587 100644 --- a/crates/rustc_codegen_spirv/src/builder/intrinsics.rs +++ b/crates/rustc_codegen_spirv/src/builder/intrinsics.rs @@ -154,14 +154,14 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> { // TODO: Configure these to be ocl vs. gl ext instructions, etc. sym::sqrtf32 | sym::sqrtf64 => { - if self.kernel_mode { + if self.target.is_kernel() { self.cl_op(CLOp::sqrt, ret_ty, [args[0].immediate()]) } else { self.gl_op(GLOp::Sqrt, ret_ty, [args[0].immediate()]) } } sym::powif32 | sym::powif64 => { - if self.kernel_mode { + if self.target.is_kernel() { self.cl_op( CLOp::pown, ret_ty, @@ -173,21 +173,21 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> { } } sym::sinf32 | sym::sinf64 => { - if self.kernel_mode { + if self.target.is_kernel() { self.cl_op(CLOp::sin, ret_ty, [args[0].immediate()]) } else { self.gl_op(GLOp::Sin, ret_ty, [args[0].immediate()]) } } sym::cosf32 | sym::cosf64 => { - if self.kernel_mode { + if self.target.is_kernel() { self.cl_op(CLOp::cos, ret_ty, [args[0].immediate()]) } else { self.gl_op(GLOp::Cos, ret_ty, [args[0].immediate()]) } } sym::powf32 | sym::powf64 => { - if self.kernel_mode { + if self.target.is_kernel() { self.cl_op( CLOp::pow, ret_ty, @@ -202,35 +202,35 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> { } } sym::expf32 | sym::expf64 => { - if self.kernel_mode { + if self.target.is_kernel() { self.cl_op(CLOp::exp, ret_ty, [args[0].immediate()]) } else { self.gl_op(GLOp::Exp, ret_ty, [args[0].immediate()]) } } sym::exp2f32 | sym::exp2f64 => { - if self.kernel_mode { + if self.target.is_kernel() { self.cl_op(CLOp::exp2, ret_ty, [args[0].immediate()]) } else { self.gl_op(GLOp::Exp2, ret_ty, [args[0].immediate()]) } } sym::logf32 | sym::logf64 => { - if self.kernel_mode { + if self.target.is_kernel() { self.cl_op(CLOp::log, ret_ty, [args[0].immediate()]) } else { self.gl_op(GLOp::Log, ret_ty, [args[0].immediate()]) } } sym::log2f32 | sym::log2f64 => { - if self.kernel_mode { + if self.target.is_kernel() { self.cl_op(CLOp::log2, ret_ty, [args[0].immediate()]) } else { self.gl_op(GLOp::Log2, ret_ty, [args[0].immediate()]) } } sym::log10f32 | sym::log10f64 => { - if self.kernel_mode { + if self.target.is_kernel() { self.cl_op(CLOp::log10, ret_ty, [args[0].immediate()]) } else { // spir-v glsl doesn't have log10, so, @@ -241,7 +241,7 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> { } } sym::fmaf32 | sym::fmaf64 => { - if self.kernel_mode { + if self.target.is_kernel() { self.cl_op( CLOp::fma, ret_ty, @@ -264,14 +264,14 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> { } } sym::fabsf32 | sym::fabsf64 => { - if self.kernel_mode { + if self.target.is_kernel() { self.cl_op(CLOp::fabs, ret_ty, [args[0].immediate()]) } else { self.gl_op(GLOp::FAbs, ret_ty, [args[0].immediate()]) } } sym::minnumf32 | sym::minnumf64 => { - if self.kernel_mode { + if self.target.is_kernel() { self.cl_op( CLOp::fmin, ret_ty, @@ -286,7 +286,7 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> { } } sym::maxnumf32 | sym::maxnumf64 => { - if self.kernel_mode { + if self.target.is_kernel() { self.cl_op( CLOp::fmax, ret_ty, @@ -301,7 +301,7 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> { } } sym::copysignf32 | sym::copysignf64 => { - if self.kernel_mode { + if self.target.is_kernel() { self.cl_op( CLOp::copysign, ret_ty, @@ -314,21 +314,21 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> { } } sym::floorf32 | sym::floorf64 => { - if self.kernel_mode { + if self.target.is_kernel() { self.cl_op(CLOp::floor, ret_ty, [args[0].immediate()]) } else { self.gl_op(GLOp::Floor, ret_ty, [args[0].immediate()]) } } sym::ceilf32 | sym::ceilf64 => { - if self.kernel_mode { + if self.target.is_kernel() { self.cl_op(CLOp::ceil, ret_ty, [args[0].immediate()]) } else { self.gl_op(GLOp::Ceil, ret_ty, [args[0].immediate()]) } } sym::truncf32 | sym::truncf64 => { - if self.kernel_mode { + if self.target.is_kernel() { self.cl_op(CLOp::trunc, ret_ty, [args[0].immediate()]) } else { self.gl_op(GLOp::Trunc, ret_ty, [args[0].immediate()]) @@ -336,14 +336,14 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> { } // TODO: Correctness of all these rounds sym::rintf32 | sym::rintf64 => { - if self.kernel_mode { + if self.target.is_kernel() { self.cl_op(CLOp::rint, ret_ty, [args[0].immediate()]) } else { self.gl_op(GLOp::Round, ret_ty, [args[0].immediate()]) } } sym::nearbyintf32 | sym::nearbyintf64 | sym::roundf32 | sym::roundf64 => { - if self.kernel_mode { + if self.target.is_kernel() { self.cl_op(CLOp::round, ret_ty, [args[0].immediate()]) } else { self.gl_op(GLOp::Round, ret_ty, [args[0].immediate()]) @@ -359,7 +359,7 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> { // TODO: Do we want to manually implement these instead of using intel instructions? sym::ctlz | sym::ctlz_nonzero => { - if self.kernel_mode { + if self.target.is_kernel() { self.cl_op(CLOp::clz, ret_ty, [args[0].immediate()]) } else { self.ext_inst @@ -376,7 +376,7 @@ impl<'a, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'tcx> { } } sym::cttz | sym::cttz_nonzero => { - if self.kernel_mode { + if self.target.is_kernel() { self.cl_op(CLOp::ctz, ret_ty, [args[0].immediate()]) } else { self.ext_inst diff --git a/crates/rustc_codegen_spirv/src/builder_spirv.rs b/crates/rustc_codegen_spirv/src/builder_spirv.rs index bdc2a10e5d..326273f00e 100644 --- a/crates/rustc_codegen_spirv/src/builder_spirv.rs +++ b/crates/rustc_codegen_spirv/src/builder_spirv.rs @@ -1,6 +1,7 @@ use crate::builder; use crate::codegen_cx::CodegenCx; use crate::spirv_type::SpirvType; +use crate::target::SpirvTarget; use rspirv::dr::{Block, Builder, Module, Operand}; use rspirv::spirv::{AddressingModel, Capability, MemoryModel, Op, StorageClass, Word}; use rspirv::{binary::Assemble, binary::Disassemble}; @@ -302,17 +303,14 @@ pub struct BuilderSpirv { } impl BuilderSpirv { - pub fn new( - version: Option<(u8, u8)>, - memory_model: Option, - kernel_mode: bool, - ) -> Self { + pub fn new(target: &SpirvTarget) -> Self { + let version = target.spirv_version(); + let memory_model = target.memory_model(); + let mut builder = Builder::new(); - // Default to spir-v 1.3 - let version = version.unwrap_or((1, 3)); builder.set_version(version.0, version.1); - let memory_model = memory_model.unwrap_or(MemoryModel::Vulkan); - if kernel_mode { + + if target.is_kernel() { builder.capability(Capability::Kernel); } else { builder.capability(Capability::Shader); @@ -327,18 +325,23 @@ impl BuilderSpirv { builder.extension("SPV_KHR_variable_pointers"); } } + // The linker will always be ran on this module builder.capability(Capability::Linkage); builder.capability(Capability::Int8); builder.capability(Capability::Int16); builder.capability(Capability::Int64); builder.capability(Capability::Float64); - if kernel_mode { + + let addressing_model = if target.is_kernel() { builder.capability(Capability::Addresses); - builder.memory_model(AddressingModel::Physical32, MemoryModel::OpenCL); + AddressingModel::Physical32 } else { - builder.memory_model(AddressingModel::Logical, memory_model); - } + AddressingModel::Logical + }; + + builder.memory_model(addressing_model, memory_model); + Self { builder: RefCell::new(builder), const_to_id: Default::default(), diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/constant.rs b/crates/rustc_codegen_spirv/src/codegen_cx/constant.rs index 64e5cdb7b6..e9f9df1283 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/constant.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/constant.rs @@ -25,7 +25,7 @@ impl<'tcx> CodegenCx<'tcx> { } pub fn constant_i32(&self, span: Span, val: i32) -> SpirvValue { - let ty = SpirvType::Integer(32, !self.kernel_mode).def(span, self); + let ty = SpirvType::Integer(32, !self.target.is_kernel()).def(span, self); self.builder.def_constant(ty, SpirvConst::U32(val as u32)) } @@ -219,7 +219,7 @@ impl<'tcx> ConstMethods<'tcx> for CodegenCx<'tcx> { Primitive::Int(int_size, int_signedness) => match self.lookup_type(ty) { SpirvType::Integer(width, spirv_signedness) => { assert_eq!(width as u64, int_size.size().bits()); - if !self.kernel_mode { + if !self.target.is_kernel() { assert_eq!(spirv_signedness, int_signedness); } self.constant_int(ty, data as u64) diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs b/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs index 5492366d50..4734236e01 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs @@ -10,8 +10,10 @@ use crate::decorations::{ }; use crate::spirv_type::{SpirvType, SpirvTypePrinter, TypeCache}; use crate::symbols::Symbols; +use crate::target::SpirvTarget; + use rspirv::dr::{Module, Operand}; -use rspirv::spirv::{AddressingModel, Decoration, LinkageType, MemoryModel, Word}; +use rspirv::spirv::{AddressingModel, Decoration, LinkageType, Word}; use rustc_codegen_ssa::mir::debuginfo::{FunctionDebugContext, VariableKind}; use rustc_codegen_ssa::traits::{ AsmMethods, BackendTypes, CoverageInfoMethods, DebugInfoMethods, MiscMethods, @@ -55,7 +57,6 @@ pub struct CodegenCx<'tcx> { /// get `LoopControl::UNROLL` applied to all of their loops' `OpLoopMerge` /// instructions, during structuralization. unroll_loops_decorations: RefCell>, - pub kernel_mode: bool, /// Cache of all the builtin symbols we need pub sym: Rc, pub instruction_table: InstructionTable, @@ -71,44 +72,24 @@ pub struct CodegenCx<'tcx> { pub i8_i16_atomics_allowed: bool, pub codegen_args: CodegenArgs, + + /// Information about the SPIR-V target. + pub target: SpirvTarget, } impl<'tcx> CodegenCx<'tcx> { pub fn new(tcx: TyCtxt<'tcx>, codegen_unit: &'tcx CodegenUnit<'tcx>) -> Self { let sym = Symbols::get(); - let mut spirv_version = None; - let mut memory_model = None; - let mut kernel_mode = false; for &feature in &tcx.sess.target_features { - if feature == sym.kernel { - kernel_mode = true; - } else if feature == sym.spirv10 { - spirv_version = Some((1, 0)); - } else if feature == sym.spirv11 { - spirv_version = Some((1, 1)); - } else if feature == sym.spirv12 { - spirv_version = Some((1, 2)); - } else if feature == sym.spirv13 { - spirv_version = Some((1, 3)); - } else if feature == sym.spirv14 { - spirv_version = Some((1, 4)); - } else if feature == sym.spirv15 { - spirv_version = Some((1, 5)); - } else if feature == sym.simple { - memory_model = Some(MemoryModel::Simple); - } else if feature == sym.vulkan { - memory_model = Some(MemoryModel::Vulkan); - } else if feature == sym.glsl450 { - memory_model = Some(MemoryModel::GLSL450); - } else { - tcx.sess.err(&format!("Unknown feature {}", feature)); - } + tcx.sess.err(&format!("Unknown feature {}", feature)); } let codegen_args = CodegenArgs::from_session(tcx.sess); + let target = tcx.sess.target.llvm_target.parse().unwrap(); + Self { tcx, codegen_unit, - builder: BuilderSpirv::new(spirv_version, memory_model, kernel_mode), + builder: BuilderSpirv::new(&target), instances: Default::default(), function_parameter_values: Default::default(), type_cache: Default::default(), @@ -116,7 +97,7 @@ impl<'tcx> CodegenCx<'tcx> { ext_inst: Default::default(), zombie_decorations: Default::default(), unroll_loops_decorations: Default::default(), - kernel_mode, + target, sym, instruction_table: InstructionTable::new(), libm_intrinsics: Default::default(), diff --git a/crates/rustc_codegen_spirv/src/lib.rs b/crates/rustc_codegen_spirv/src/lib.rs index 858cc9aab1..6f2ee5e844 100644 --- a/crates/rustc_codegen_spirv/src/lib.rs +++ b/crates/rustc_codegen_spirv/src/lib.rs @@ -116,6 +116,7 @@ mod linker; mod spirv_type; mod spirv_type_constraints; mod symbols; +mod target; use builder::Builder; use codegen_cx::{CodegenArgs, CodegenCx, ModuleOutputType}; @@ -146,7 +147,7 @@ use rustc_session::config::{self, OptLevel, OutputFilenames, OutputType}; use rustc_session::Session; use rustc_span::symbol::{sym, Symbol}; use rustc_target::spec::abi::Abi; -use rustc_target::spec::{LinkerFlavor, PanicStrategy, Target, TargetOptions, TargetTriple}; +use rustc_target::spec::{Target, TargetTriple}; use std::any::Any; use std::env; use std::fs::{create_dir_all, File}; @@ -211,34 +212,6 @@ fn is_blocklisted_fn<'tcx>( false } -fn target_options(env: Option) -> Target { - Target { - llvm_target: "no-llvm".to_string(), - pointer_width: 32, - data_layout: "e-m:e-p:32:32:32-i64:64-n8:16:32:64".to_string(), - arch: "spirv".to_string(), - options: TargetOptions { - simd_types_indirect: false, - allows_weak_linkage: false, - crt_static_allows_dylibs: true, - dll_prefix: "".to_string(), - dll_suffix: ".spv".to_string(), - dynamic_linking: true, - emit_debug_gdb_scripts: false, - 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() - }, - } -} - // TODO: Should this store Vec or Module? struct SpirvModuleBuffer(Vec); @@ -294,20 +267,10 @@ impl CodegenBackend for SpirvCodegenBackend { fn target_override(&self, opts: &config::Options) -> Option { match opts.target_triple { - 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))), - Err(_) if env == "unknown" => Some(target_options(None)), - Err(_) => None, - } - } + TargetTriple::TargetTriple(ref target) => target + .parse::() + .map(|target| target.rustc_target()) + .ok(), TargetTriple::TargetPath(_) => None, } } diff --git a/crates/rustc_codegen_spirv/src/spirv_type.rs b/crates/rustc_codegen_spirv/src/spirv_type.rs index ef00e916d0..9a7e1d42b8 100644 --- a/crates/rustc_codegen_spirv/src/spirv_type.rs +++ b/crates/rustc_codegen_spirv/src/spirv_type.rs @@ -147,7 +147,7 @@ impl SpirvType { let id = emit.id(); let result = emit.type_struct_id(Some(id), field_types.iter().cloned()); // The struct size is only used in our own sizeof_in_bits() (used in e.g. ArrayStride decoration) - if !cx.kernel_mode { + if !cx.target.is_kernel() { // TODO: kernel mode can't do this?? for (index, offset) in field_offsets.iter().copied().enumerate() { emit.member_decorate( @@ -177,7 +177,7 @@ impl SpirvType { .expect("Element of sized array must be sized") .bytes(); let result = cx.emit_global().type_array(element, count.def_cx(cx)); - if !cx.kernel_mode { + if !cx.target.is_kernel() { // TODO: kernel mode can't do this?? cx.emit_global().decorate( result, @@ -200,7 +200,7 @@ impl SpirvType { Decoration::ArrayStride, iter::once(Operand::LiteralInt32(element_size as u32)), ); - if cx.kernel_mode { + if cx.target.is_kernel() { cx.zombie_with_span(result, def_span, "RuntimeArray in kernel mode"); } result diff --git a/crates/rustc_codegen_spirv/src/symbols.rs b/crates/rustc_codegen_spirv/src/symbols.rs index daaeacdb42..47e1f2f6fa 100644 --- a/crates/rustc_codegen_spirv/src/symbols.rs +++ b/crates/rustc_codegen_spirv/src/symbols.rs @@ -20,16 +20,6 @@ pub struct Symbols { pub spirv_std: Symbol, pub libm: Symbol, pub num_traits: Symbol, - pub kernel: Symbol, - pub simple: Symbol, - pub vulkan: Symbol, - pub glsl450: Symbol, - pub spirv10: Symbol, - pub spirv11: Symbol, - pub spirv12: Symbol, - pub spirv13: Symbol, - pub spirv14: Symbol, - pub spirv15: Symbol, pub entry_point_name: Symbol, descriptor_set: Symbol, binding: Symbol, @@ -376,16 +366,6 @@ impl Symbols { spirv_std: Symbol::intern("spirv_std"), libm: Symbol::intern("libm"), num_traits: Symbol::intern("num_traits"), - kernel: Symbol::intern("kernel"), - simple: Symbol::intern("simple"), - vulkan: Symbol::intern("vulkan"), - glsl450: Symbol::intern("glsl450"), - spirv10: Symbol::intern("spirv1.0"), - spirv11: Symbol::intern("spirv1.1"), - spirv12: Symbol::intern("spirv1.2"), - spirv13: Symbol::intern("spirv1.3"), - spirv14: Symbol::intern("spirv1.4"), - spirv15: Symbol::intern("spirv1.5"), descriptor_set: Symbol::intern("descriptor_set"), binding: Symbol::intern("binding"), image_type: Symbol::intern("image_type"), diff --git a/crates/rustc_codegen_spirv/src/target.rs b/crates/rustc_codegen_spirv/src/target.rs new file mode 100644 index 0000000000..2172fc52c4 --- /dev/null +++ b/crates/rustc_codegen_spirv/src/target.rs @@ -0,0 +1,149 @@ +use rspirv::spirv::MemoryModel; +use rustc_target::spec::{LinkerFlavor, PanicStrategy, Target, TargetOptions}; +use spirv_tools::TargetEnv; + +const ARCH: &str = "spirv"; + +pub struct SpirvTarget { + env: TargetEnv, + vendor: String, +} + +impl SpirvTarget { + pub fn is_kernel(&self) -> bool { + self.memory_model() == MemoryModel::OpenCL + } + + pub fn memory_model(&self) -> MemoryModel { + match self.env { + TargetEnv::Universal_1_0 + | TargetEnv::Universal_1_1 + | TargetEnv::Universal_1_2 + | TargetEnv::Universal_1_3 + | TargetEnv::Universal_1_4 + | TargetEnv::Universal_1_5 => MemoryModel::Simple, + + TargetEnv::OpenGL_4_0 + | TargetEnv::OpenGL_4_1 + | TargetEnv::OpenGL_4_2 + | TargetEnv::OpenGL_4_3 + | TargetEnv::OpenGL_4_5 => MemoryModel::GLSL450, + + TargetEnv::OpenCL_2_1 + | TargetEnv::OpenCL_2_2 + | TargetEnv::OpenCL_1_2 + | TargetEnv::OpenCLEmbedded_1_2 + | TargetEnv::OpenCL_2_0 + | TargetEnv::OpenCLEmbedded_2_0 + | TargetEnv::OpenCLEmbedded_2_1 + | TargetEnv::OpenCLEmbedded_2_2 => MemoryModel::OpenCL, + + TargetEnv::Vulkan_1_0 + | TargetEnv::Vulkan_1_1 + | TargetEnv::WebGPU_0 + | TargetEnv::Vulkan_1_1_Spirv_1_4 + | TargetEnv::Vulkan_1_2 => MemoryModel::Vulkan, + } + } + + pub fn spirv_version(&self) -> (u8, u8) { + #[allow(clippy::match_same_arms)] + match self.env { + TargetEnv::Universal_1_0 => (1, 0), + TargetEnv::Universal_1_1 => (1, 1), + TargetEnv::Universal_1_2 => (1, 2), + TargetEnv::Universal_1_3 => (1, 3), + TargetEnv::Universal_1_4 => (1, 4), + TargetEnv::Universal_1_5 => (1, 5), + + TargetEnv::OpenGL_4_0 => (1, 0), + TargetEnv::OpenGL_4_1 => (1, 0), + TargetEnv::OpenGL_4_2 => (1, 0), + TargetEnv::OpenGL_4_3 => (1, 0), + TargetEnv::OpenGL_4_5 => (1, 3), + + TargetEnv::OpenCL_1_2 => (1, 0), + TargetEnv::OpenCL_2_0 => (1, 0), + TargetEnv::OpenCL_2_1 => (1, 0), + TargetEnv::OpenCL_2_2 => (1, 2), + TargetEnv::OpenCLEmbedded_1_2 => (1, 0), + TargetEnv::OpenCLEmbedded_2_0 => (1, 0), + TargetEnv::OpenCLEmbedded_2_1 => (1, 0), + TargetEnv::OpenCLEmbedded_2_2 => (1, 2), + + TargetEnv::Vulkan_1_0 => (1, 0), + TargetEnv::Vulkan_1_1 => (1, 3), + TargetEnv::WebGPU_0 => (1, 3), + TargetEnv::Vulkan_1_1_Spirv_1_4 => (1, 4), + TargetEnv::Vulkan_1_2 => (1, 5), + } + } + + pub fn rustc_target(&self) -> Target { + Target { + llvm_target: self.to_string(), + pointer_width: 32, + data_layout: "e-m:e-p:32:32:32-i64:64-n8:16:32:64".to_string(), + arch: String::from(ARCH), + options: TargetOptions { + simd_types_indirect: false, + allows_weak_linkage: false, + crt_static_allows_dylibs: true, + dll_prefix: "".to_string(), + dll_suffix: ".spv".to_string(), + dynamic_linking: true, + emit_debug_gdb_scripts: false, + linker_flavor: LinkerFlavor::Ld, + panic_strategy: PanicStrategy::Abort, + os: "unknown".to_string(), + env: self.env.to_string(), + vendor: self.vendor.clone(), + // TODO: Investigate if main_needs_argc_argv is useful (for building exes) + main_needs_argc_argv: false, + ..Default::default() + }, + } + } +} + +impl std::str::FromStr for SpirvTarget { + type Err = InvalidTarget; + + fn from_str(target: &str) -> Result { + let mut iter = target.split('-'); + let error = || InvalidTarget(target.into()); + + if iter.next().map_or(true, |arch| arch != ARCH) { + return Err(error()); + } + + let vendor = iter.next().map(From::from).ok_or_else(error)?; + + let env = iter + .next() + .and_then(|env| env.parse().ok()) + .ok_or_else(error)?; + + if iter.next().is_some() { + return Err(error()); + } + + Ok(Self { env, vendor }) + } +} + +impl std::fmt::Display for SpirvTarget { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}-{}-{}", ARCH, self.vendor, self.env) + } +} + +#[derive(Debug)] +pub struct InvalidTarget(String); + +impl std::error::Error for InvalidTarget {} +impl std::fmt::Display for InvalidTarget { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Invalid target `{}`.", self.0) + } +} diff --git a/crates/spirv-builder/src/lib.rs b/crates/spirv-builder/src/lib.rs index 38f10cc62b..cccb0e8feb 100644 --- a/crates/spirv-builder/src/lib.rs +++ b/crates/spirv-builder/src/lib.rs @@ -106,17 +106,16 @@ pub struct SpirvBuilder { path_to_crate: PathBuf, print_metadata: bool, release: bool, - spirv_version: Option<(u8, u8)>, - memory_model: Option, + target: String, } + impl SpirvBuilder { - pub fn new(path_to_crate: impl AsRef) -> Self { + pub fn new(path_to_crate: impl AsRef, target: impl Into) -> Self { Self { path_to_crate: path_to_crate.as_ref().to_owned(), print_metadata: true, release: true, - spirv_version: None, - memory_model: None, + target: target.into(), } } @@ -132,18 +131,6 @@ impl SpirvBuilder { self } - /// Sets the SPIR-V binary version to use. Defaults to v1.3. - pub fn spirv_version(mut self, major: u8, minor: u8) -> Self { - self.spirv_version = Some((major, minor)); - self - } - - /// Sets the SPIR-V memory model. Defaults to Vulkan. - pub fn memory_model(mut self, memory_model: MemoryModel) -> Self { - self.memory_model = Some(memory_model); - self - } - /// Builds the module. Returns the path to the built spir-v file. If `print_metadata` is true, /// you usually don't have to inspect the path, as the environment variable will already be /// set. @@ -212,35 +199,13 @@ fn invoke_rustc(builder: &SpirvBuilder, multimodule: bool) -> Result "+simple", - MemoryModel::Vulkan => "+vulkan", - MemoryModel::GLSL450 => "+glsl450", - } - .to_string(), - ); - } - let feature_flag = if target_features.is_empty() { - String::new() - } else { - format!(" -C target-feature={}", target_features.join(",")) - }; - let llvm_args = if multimodule { - " -C llvm-args=--module-output=multiple" - } else { - "" - }; + let llvm_args = multimodule + .then(|| " -C llvm-args=--module-output=multiple") + .unwrap_or_default(); + let rustflags = format!( - "-Z codegen-backend={} -Z symbol-mangling-version=v0{}{}", + "-Z codegen-backend={} -Z symbol-mangling-version=v0{}", rustc_codegen_spirv.display(), - feature_flag, llvm_args, ); let mut cargo = Command::new("cargo"); @@ -250,7 +215,7 @@ fn invoke_rustc(builder: &SpirvBuilder, multimodule: bool) -> Result Result> { fn build(src: &str) -> PathBuf { let project = setup(src).expect("Failed to set up project"); - crate::SpirvBuilder::new(&project) + crate::SpirvBuilder::new(&project, "spirv-unknown-spv1.3") .print_metadata(false) .release(false) .build() diff --git a/docs/src/platform-support.md b/docs/src/platform-support.md index fcb01d102b..785e452ecd 100644 --- a/docs/src/platform-support.md +++ b/docs/src/platform-support.md @@ -25,6 +25,45 @@ The `rust-gpu` project currently supports a limited number of platforms and grap | WGPU | 0.6 | Primary | Uses a translation layer to Metal/DX12 | OpenGL | ??? | Tertiary | +### SPIR-V Targets + +- `spirv-unknown-spv1.0` +- `spirv-unknown-spv1.1` +- `spirv-unknown-spv1.2` +- `spirv-unknown-spv1.3` +- `spirv-unknown-spv1.4` +- `spirv-unknown-spv1.5` + +### Vulkan Targets + +- `spirv-unknown-vulkan1.0` +- `spirv-unknown-vulkan1.1` +- `spirv-unknown-vulkan1.1spv1.4` +- `spirv-unknown-vulkan1.2` + +### WebGPU Targets + +- `spirv-unknown-webgpu0` + +### OpenGL Targets + +- `spirv-unknown-opengl4.0` +- `spirv-unknown-opengl4.1` +- `spirv-unknown-opengl4.2` +- `spirv-unknown-opengl4.3` +- `spirv-unknown-opengl4.5` + +### OpenCL Targets + +- `spirv-unknown-opencl1.2` +- `spirv-unknown-opencl1.2embedded` +- `spirv-unknown-opencl2.0` +- `spirv-unknown-opencl2.0embedded` +- `spirv-unknown-opencl2.1` +- `spirv-unknown-opencl2.1embedded` +- `spirv-unknown-opencl2.2` +- `spirv-unknown-opencl2.2embedded` + ## GPU Currently we don't have specific generations of GPUs for support, as long they support Vulkan 1.1+ with the latest officially installed drivers it should be able build and run the examples. You can check your Vulkan version using the [`vulkaninfo`] command from the `vulkan-sdk`. diff --git a/docs/src/writing-shader-crates.md b/docs/src/writing-shader-crates.md index 6e6344c959..3ea6d6908c 100644 --- a/docs/src/writing-shader-crates.md +++ b/docs/src/writing-shader-crates.md @@ -15,7 +15,7 @@ You can now test out and try building shaders with rust-gpu from the browser! and run shaders on the web. - [Shader Playground] A playground for building and checking the output of shader code similar to godbolt or play.rust-lang.org. - + [SHADERed]: https://shadered.org/template [shader playground]: http://shader-playground.timjones.io/9d744d5893beb6a8f129fda50ad4aeeb @@ -70,15 +70,14 @@ Now you should have a `librustc_codegen_spirv` dynamic library available in `target/release`. You'll need to keep this somewhere stable that you can reference from your shader project. -Now we need to add our `.cargo/config` file. This tells cargo to build for the -`spirv-unknown-unknown` target, and provides a path to the codegen backend for -that target. We have to also provide `-Zbuild-std` as the -`spirv-unknown-unknown` sysroot is not currently available in the -default installation. +Now we need to add our `.cargo/config` file. This is a configuration file that +tells cargo how to build for SPIR-V. You need provide the target you're +compiling for (see [platform support](./platform-support.md)) and provide a path to your built `rustc_codegen_spirv` dynamic +library. We have to also provide `-Zbuild-std`. ```toml [build] -target = "spirv-unknown-unknown" +target = "spirv-unknown-spv1.3" rustflags = [ "-Zcodegen-backend=", "-Zsymbol-mangling-version=v0" @@ -89,7 +88,7 @@ build-std=["core"] build-std-features=["compiler-builtins-mem"] ``` -Now we can build our crate with cargo as normal. +Now we can build our crate with cargo as normal. ```bash cargo build ``` diff --git a/examples/multibuilder/src/main.rs b/examples/multibuilder/src/main.rs index 9b3bd5e67e..589149f3b8 100644 --- a/examples/multibuilder/src/main.rs +++ b/examples/multibuilder/src/main.rs @@ -1,9 +1,8 @@ use spirv_builder::SpirvBuilder; fn main() { - let result = SpirvBuilder::new("../shaders/sky-shader") + let result = SpirvBuilder::new("../shaders/sky-shader", "spirv-unknown-spv1.3") .print_metadata(false) - .spirv_version(1, 0) .build_multimodule() .unwrap(); println!("{:#?}", result); diff --git a/examples/runners/ash/src/main.rs b/examples/runners/ash/src/main.rs index 0cad25287a..7b8561869d 100644 --- a/examples/runners/ash/src/main.rs +++ b/examples/runners/ash/src/main.rs @@ -171,10 +171,13 @@ pub fn main() { } pub fn compile_shaders() -> Vec { - let spv_paths: Vec = vec![SpirvBuilder::new("examples/shaders/sky-shader") - .print_metadata(false) - .build() - .unwrap()]; + let spv_paths: Vec = + vec![ + SpirvBuilder::new("examples/shaders/sky-shader", "spirv-unknown-vulkan1.1") + .print_metadata(false) + .build() + .unwrap(), + ]; let mut spv_files = Vec::::with_capacity(spv_paths.len()); for path in spv_paths.iter() { spv_files.push(SpvFile { diff --git a/examples/runners/wgpu/build.rs b/examples/runners/wgpu/build.rs index 0e18815223..da88425a60 100644 --- a/examples/runners/wgpu/build.rs +++ b/examples/runners/wgpu/build.rs @@ -2,9 +2,7 @@ use spirv_builder::SpirvBuilder; use std::error::Error; fn build_shader(path_to_create: &str) -> Result<(), Box> { - SpirvBuilder::new(path_to_create) - .spirv_version(1, 0) - .build()?; + SpirvBuilder::new(path_to_create, "spirv-unknown-vulkan1.0").build()?; Ok(()) } diff --git a/tests/src/main.rs b/tests/src/main.rs index a412a79cd5..dd0b0ef618 100644 --- a/tests/src/main.rs +++ b/tests/src/main.rs @@ -30,7 +30,7 @@ impl Opt { pub fn environments(&self) -> Vec { match &self.target_env { Some(env) => env.split(',').map(String::from).collect(), - None => vec!["unknown".into()], + None => vec!["spv1.3".into()], } } }