Bug 1878375 - Synchronize vendored Rust libraries with mozilla-central. r=ikey
mozilla-central: fb4318779187f4b4724ae2f56e91608e550520e5 comm-central: feaf93e78cbd012302a60597e48abb15588ba2d8 Differential Revision: https://phabricator.services.mozilla.com/D222920 --HG-- extra : amend_source : fc41d896aceb0d80c46fbd6904958dfea682fa2a
This commit is contained in:
Родитель
c7cec536fe
Коммит
4544d2435b
|
@ -21,9 +21,9 @@ git = "https://github.com/franziskuskiefer/cose-rust"
|
|||
rev = "43c22248d136c8b38fe42ea709d08da6355cf04b"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source."git+https://github.com/gfx-rs/wgpu?rev=c8beade1877251c494036fc3661b04ec6aad63a9"]
|
||||
[source."git+https://github.com/gfx-rs/wgpu?rev=3fda684eb9e69c78b16312a3e927e3ea82e853d1"]
|
||||
git = "https://github.com/gfx-rs/wgpu"
|
||||
rev = "c8beade1877251c494036fc3661b04ec6aad63a9"
|
||||
rev = "3fda684eb9e69c78b16312a3e927e3ea82e853d1"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source."git+https://github.com/hsivonen/any_all_workaround?rev=7fb1b7034c9f172aade21ee1c8554e8d8a48af80"]
|
||||
|
|
|
@ -2873,6 +2873,7 @@ name = "kvstore"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"atomic_refcell",
|
||||
"chrono",
|
||||
"crossbeam-utils",
|
||||
"cstr",
|
||||
"futures",
|
||||
|
@ -3498,7 +3499,7 @@ checksum = "a2983372caf4480544083767bf2d27defafe32af49ab4df3a0b7fc90793a3664"
|
|||
[[package]]
|
||||
name = "naga"
|
||||
version = "22.0.0"
|
||||
source = "git+https://github.com/gfx-rs/wgpu?rev=c8beade1877251c494036fc3661b04ec6aad63a9#c8beade1877251c494036fc3661b04ec6aad63a9"
|
||||
source = "git+https://github.com/gfx-rs/wgpu?rev=3fda684eb9e69c78b16312a3e927e3ea82e853d1#3fda684eb9e69c78b16312a3e927e3ea82e853d1"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bit-set",
|
||||
|
@ -5811,7 +5812,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "wgpu-core"
|
||||
version = "22.0.0"
|
||||
source = "git+https://github.com/gfx-rs/wgpu?rev=c8beade1877251c494036fc3661b04ec6aad63a9#c8beade1877251c494036fc3661b04ec6aad63a9"
|
||||
source = "git+https://github.com/gfx-rs/wgpu?rev=3fda684eb9e69c78b16312a3e927e3ea82e853d1#3fda684eb9e69c78b16312a3e927e3ea82e853d1"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bit-vec",
|
||||
|
@ -5836,7 +5837,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "wgpu-hal"
|
||||
version = "22.0.0"
|
||||
source = "git+https://github.com/gfx-rs/wgpu?rev=c8beade1877251c494036fc3661b04ec6aad63a9#c8beade1877251c494036fc3661b04ec6aad63a9"
|
||||
source = "git+https://github.com/gfx-rs/wgpu?rev=3fda684eb9e69c78b16312a3e927e3ea82e853d1#3fda684eb9e69c78b16312a3e927e3ea82e853d1"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"arrayvec",
|
||||
|
@ -5875,7 +5876,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "wgpu-types"
|
||||
version = "22.0.0"
|
||||
source = "git+https://github.com/gfx-rs/wgpu?rev=c8beade1877251c494036fc3661b04ec6aad63a9#c8beade1877251c494036fc3661b04ec6aad63a9"
|
||||
source = "git+https://github.com/gfx-rs/wgpu?rev=3fda684eb9e69c78b16312a3e927e3ea82e853d1#3fda684eb9e69c78b16312a3e927e3ea82e853d1"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"js-sys",
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"mc_workspace_toml": "b04bca0c48214c9d01b1fc5fb74e1973ad108bbf73bd629d0ac2bf79d1e7dcc750b09fc9d17694f785984f837af61a645328d1eb37b105ebc313e1d382358630", "mc_gkrust_toml": "65d421024f2f4b6dde159c173c8d8a7ceac53d31079cd87c73872365e50bfbceff11d5a529c2b0e922b08527fc8d533278efef06bbe1476bcf5acd9be467612b", "mc_hack_toml": "d76ce395d170433ad163ba7bd0d83a02fdc6c831287315a099d070348e2eb00f8f08da1d9a5fe135925865457a635954c216229ef0abd178a03f0922ebbc890e", "mc_cargo_lock": "eac022e9e2f26ad236ddfb7819b18f079e578de40d0b48c987858fa14e3f4574694c77cd3773f5a56cf01a69750a972e5ca837709b23ee514af9ae170f4dffed"}
|
||||
{"mc_workspace_toml": "b04bca0c48214c9d01b1fc5fb74e1973ad108bbf73bd629d0ac2bf79d1e7dcc750b09fc9d17694f785984f837af61a645328d1eb37b105ebc313e1d382358630", "mc_gkrust_toml": "65d421024f2f4b6dde159c173c8d8a7ceac53d31079cd87c73872365e50bfbceff11d5a529c2b0e922b08527fc8d533278efef06bbe1476bcf5acd9be467612b", "mc_hack_toml": "d76ce395d170433ad163ba7bd0d83a02fdc6c831287315a099d070348e2eb00f8f08da1d9a5fe135925865457a635954c216229ef0abd178a03f0922ebbc890e", "mc_cargo_lock": "7f1b21ad1289d7b03ee8343b45b2cf429995a5401533c0d64e6a3bf0a8baf4eb3a2e9b95e4e9a433ecb595aa2a3a531fa6c31a65f054a2d7f9d5d8a690e75e4e"}
|
|
@ -16,8 +16,8 @@ harness = false
|
|||
[dependencies]
|
||||
mozilla-central-workspace-hack = { version = "0.1", features = ['gkrust'], optional = true }
|
||||
gkrust-shared = { version = "0.1.0", path = "../../../toolkit/library/rust/shared" }
|
||||
sys_tray = { version = "0.1.0", path = "../sys_tray" }
|
||||
ews_xpcom = { version = "0.1.0", path = "../ews_xpcom" }
|
||||
sys_tray = { version = "0.1.0", path = "../sys_tray" }
|
||||
aa-stroke = { git = "https://github.com/FirefoxGraphics/aa-stroke", rev = "d94278ed9c7020f50232689a26d1277eb0eb74d2" }
|
||||
app_services_logger = { path = "../../../services/common/app_services_logger" }
|
||||
audio_thread_priority = { version = "0.32" }
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -376,6 +376,11 @@ pub struct Writer<W> {
|
|||
/// Set of (struct type, struct field index) denoting which fields require
|
||||
/// padding inserted **before** them (i.e. between fields at index - 1 and index)
|
||||
struct_member_pads: FastHashSet<(Handle<crate::Type>, u32)>,
|
||||
|
||||
/// Name of the loop reachability macro.
|
||||
///
|
||||
/// See `emit_loop_reachable_macro` for details.
|
||||
loop_reachable_macro_name: String,
|
||||
}
|
||||
|
||||
impl crate::Scalar {
|
||||
|
@ -665,6 +670,7 @@ impl<W: Write> Writer<W> {
|
|||
#[cfg(test)]
|
||||
put_block_stack_pointers: Default::default(),
|
||||
struct_member_pads: FastHashSet::default(),
|
||||
loop_reachable_macro_name: String::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -675,6 +681,125 @@ impl<W: Write> Writer<W> {
|
|||
self.out
|
||||
}
|
||||
|
||||
/// Define a macro to invoke before loops, to defeat MSL infinite loop
|
||||
/// reasoning.
|
||||
///
|
||||
/// If we haven't done so already, emit the definition of a preprocessor
|
||||
/// macro to be invoked before each loop in the generated MSL, to ensure
|
||||
/// that the MSL compiler's optimizations do not remove bounds checks.
|
||||
///
|
||||
/// Only the first call to this function for a given module actually causes
|
||||
/// the macro definition to be written. Subsequent loops can simply use the
|
||||
/// prior macro definition, since macros aren't block-scoped.
|
||||
///
|
||||
/// # What is this trying to solve?
|
||||
///
|
||||
/// In Metal Shading Language, an infinite loop has undefined behavior.
|
||||
/// (This rule is inherited from C++14.) This means that, if the MSL
|
||||
/// compiler determines that a given loop will never exit, it may assume
|
||||
/// that it is never reached. It may thus assume that any conditions
|
||||
/// sufficient to cause the loop to be reached must be false. Like many
|
||||
/// optimizing compilers, MSL uses this kind of analysis to establish limits
|
||||
/// on the range of values variables involved in those conditions might
|
||||
/// hold.
|
||||
///
|
||||
/// For example, suppose the MSL compiler sees the code:
|
||||
///
|
||||
/// ```ignore
|
||||
/// if (i >= 10) {
|
||||
/// while (true) { }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// It will recognize that the `while` loop will never terminate, conclude
|
||||
/// that it must be unreachable, and thus infer that, if this code is
|
||||
/// reached, then `i < 10` at that point.
|
||||
///
|
||||
/// Now suppose that, at some point where `i` has the same value as above,
|
||||
/// the compiler sees the code:
|
||||
///
|
||||
/// ```ignore
|
||||
/// if (i < 10) {
|
||||
/// a[i] = 1;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Because the compiler is confident that `i < 10`, it will make the
|
||||
/// assignment to `a[i]` unconditional, rewriting this code as, simply:
|
||||
///
|
||||
/// ```ignore
|
||||
/// a[i] = 1;
|
||||
/// ```
|
||||
///
|
||||
/// If that `if` condition was injected by Naga to implement a bounds check,
|
||||
/// the MSL compiler's optimizations could allow out-of-bounds array
|
||||
/// accesses to occur.
|
||||
///
|
||||
/// Naga cannot feasibly anticipate whether the MSL compiler will determine
|
||||
/// that a loop is infinite, so an attacker could craft a Naga module
|
||||
/// containing an infinite loop protected by conditions that cause the Metal
|
||||
/// compiler to remove bounds checks that Naga injected elsewhere in the
|
||||
/// function.
|
||||
///
|
||||
/// This rewrite could occur even if the conditional assignment appears
|
||||
/// *before* the `while` loop, as long as `i < 10` by the time the loop is
|
||||
/// reached. This would allow the attacker to save the results of
|
||||
/// unauthorized reads somewhere accessible before entering the infinite
|
||||
/// loop. But even worse, the MSL compiler has been observed to simply
|
||||
/// delete the infinite loop entirely, so that even code dominated by the
|
||||
/// loop becomes reachable. This would make the attack even more flexible,
|
||||
/// since shaders that would appear to never terminate would actually exit
|
||||
/// nicely, after having stolen data from elsewhere in the GPU address
|
||||
/// space.
|
||||
///
|
||||
/// Ideally, Naga would prevent UB entirely via some means that persuades
|
||||
/// the MSL compiler that no loop Naga generates is infinite. One approach
|
||||
/// would be to add inline assembly to each loop that is annotated as
|
||||
/// potentially branching out of the loop, but which in fact generates no
|
||||
/// instructions. Unfortunately, inline assembly is not handled correctly by
|
||||
/// some Metal device drivers. Further experimentation hasn't produced a
|
||||
/// satisfactory approach.
|
||||
///
|
||||
/// Instead, we accept that the MSL compiler may determine that some loops
|
||||
/// are infinite, and focus instead on preventing the range analysis from
|
||||
/// being affected. We transform *every* loop into something like this:
|
||||
///
|
||||
/// ```ignore
|
||||
/// if (volatile bool unpredictable = true; unpredictable)
|
||||
/// while (true) { }
|
||||
/// ```
|
||||
///
|
||||
/// Since the `volatile` qualifier prevents the compiler from assuming that
|
||||
/// the `if` condition is true, it cannot be sure the infinite loop is
|
||||
/// reached, and thus it cannot assume the entire structure is unreachable.
|
||||
/// This prevents the range analysis impact described above.
|
||||
///
|
||||
/// Unfortunately, what makes this a kludge, not a hack, is that this
|
||||
/// solution leaves the GPU executing a pointless conditional branch, at
|
||||
/// runtime, before each loop. There's no part of the system that has a
|
||||
/// global enough view to be sure that `unpredictable` is true, and remove
|
||||
/// it from the code.
|
||||
///
|
||||
/// To make our output a bit more legible, we pull the condition out into a
|
||||
/// preprocessor macro defined at the top of the module.
|
||||
fn emit_loop_reachable_macro(&mut self) -> BackendResult {
|
||||
if !self.loop_reachable_macro_name.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.loop_reachable_macro_name = self.namer.call("LOOP_IS_REACHABLE");
|
||||
let loop_reachable_volatile_name = self.namer.call("unpredictable_jump_over_loop");
|
||||
writeln!(
|
||||
self.out,
|
||||
"#define {} if (volatile bool {} = true; {})",
|
||||
self.loop_reachable_macro_name,
|
||||
loop_reachable_volatile_name,
|
||||
loop_reachable_volatile_name,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn put_call_parameters(
|
||||
&mut self,
|
||||
parameters: impl Iterator<Item = Handle<crate::Expression>>,
|
||||
|
@ -2924,10 +3049,15 @@ impl<W: Write> Writer<W> {
|
|||
ref continuing,
|
||||
break_if,
|
||||
} => {
|
||||
self.emit_loop_reachable_macro()?;
|
||||
if !continuing.is_empty() || break_if.is_some() {
|
||||
let gate_name = self.namer.call("loop_init");
|
||||
writeln!(self.out, "{level}bool {gate_name} = true;")?;
|
||||
writeln!(self.out, "{level}while(true) {{")?;
|
||||
writeln!(
|
||||
self.out,
|
||||
"{level}{} while(true) {{",
|
||||
self.loop_reachable_macro_name,
|
||||
)?;
|
||||
let lif = level.next();
|
||||
let lcontinuing = lif.next();
|
||||
writeln!(self.out, "{lif}if (!{gate_name}) {{")?;
|
||||
|
@ -2942,7 +3072,11 @@ impl<W: Write> Writer<W> {
|
|||
writeln!(self.out, "{lif}}}")?;
|
||||
writeln!(self.out, "{lif}{gate_name} = false;")?;
|
||||
} else {
|
||||
writeln!(self.out, "{level}while(true) {{")?;
|
||||
writeln!(
|
||||
self.out,
|
||||
"{level}{} while(true) {{",
|
||||
self.loop_reachable_macro_name,
|
||||
)?;
|
||||
}
|
||||
self.put_block(level.next(), body, context)?;
|
||||
writeln!(self.out, "{level}}}")?;
|
||||
|
@ -3379,6 +3513,7 @@ impl<W: Write> Writer<W> {
|
|||
&[CLAMPED_LOD_LOAD_PREFIX],
|
||||
&mut self.names,
|
||||
);
|
||||
self.loop_reachable_macro_name.clear();
|
||||
self.struct_member_pads.clear();
|
||||
|
||||
writeln!(
|
||||
|
|
|
@ -16,7 +16,7 @@ mod selection;
|
|||
mod subgroup;
|
||||
mod writer;
|
||||
|
||||
pub use spirv::Capability;
|
||||
pub use spirv::{Capability, SourceLanguage};
|
||||
|
||||
use crate::arena::{Handle, HandleVec};
|
||||
use crate::proc::{BoundsCheckPolicies, TypeResolution};
|
||||
|
@ -89,6 +89,7 @@ impl IdGenerator {
|
|||
pub struct DebugInfo<'a> {
|
||||
pub source_code: &'a str,
|
||||
pub file_name: &'a std::path::Path,
|
||||
pub language: SourceLanguage,
|
||||
}
|
||||
|
||||
/// A SPIR-V block to which we are still adding instructions.
|
||||
|
|
|
@ -1967,7 +1967,7 @@ impl Writer {
|
|||
source_file_id,
|
||||
});
|
||||
self.debugs.append(&mut Instruction::source_auto_continued(
|
||||
spirv::SourceLanguage::Unknown,
|
||||
debug_info.language,
|
||||
0,
|
||||
&debug_info_inner,
|
||||
));
|
||||
|
|
|
@ -44,12 +44,8 @@ pub enum Error {
|
|||
MultiMemberStruct,
|
||||
#[error("encountered unsupported global initializer in an atomic variable")]
|
||||
GlobalInitUnsupported,
|
||||
}
|
||||
|
||||
impl From<Error> for crate::front::spv::Error {
|
||||
fn from(source: Error) -> Self {
|
||||
crate::front::spv::Error::AtomicUpgradeError(source)
|
||||
}
|
||||
#[error("expected to find a global variable")]
|
||||
GlobalVariableMissing,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
|
|
|
@ -159,3 +159,9 @@ impl Error {
|
|||
String::from_utf8(writer.into_inner()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<atomic_upgrade::Error> for Error {
|
||||
fn from(source: atomic_upgrade::Error) -> Self {
|
||||
crate::front::spv::Error::AtomicUpgradeError(source)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -565,11 +565,15 @@ impl<'a> BlockContext<'a> {
|
|||
/// Descend into the expression with the given handle, locating a contained
|
||||
/// global variable.
|
||||
///
|
||||
/// If the expression doesn't actually refer to something in a global
|
||||
/// variable, we can't upgrade its type in a way that Naga validation would
|
||||
/// pass, so reject the input instead.
|
||||
///
|
||||
/// This is used to track atomic upgrades.
|
||||
fn get_contained_global_variable(
|
||||
&self,
|
||||
mut handle: Handle<crate::Expression>,
|
||||
) -> Option<Handle<crate::GlobalVariable>> {
|
||||
) -> Result<Handle<crate::GlobalVariable>, Error> {
|
||||
log::debug!("\t\tlocating global variable in {handle:?}");
|
||||
loop {
|
||||
match self.expressions[handle] {
|
||||
|
@ -583,14 +587,16 @@ impl<'a> BlockContext<'a> {
|
|||
}
|
||||
crate::Expression::GlobalVariable(h) => {
|
||||
log::debug!("\t\t found {h:?}");
|
||||
return Some(h);
|
||||
return Ok(h);
|
||||
}
|
||||
_ => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
Err(Error::AtomicUpgradeError(
|
||||
crate::front::atomic_upgrade::Error::GlobalVariableMissing,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1323,6 +1329,109 @@ impl<I: Iterator<Item = u32>> Frontend<I> {
|
|||
))
|
||||
}
|
||||
|
||||
/// Return the Naga [`Expression`] for `pointer_id`, and its referent [`Type`].
|
||||
///
|
||||
/// Return a [`Handle`] for a Naga [`Expression`] that holds the value of
|
||||
/// the SPIR-V instruction `pointer_id`, along with the [`Type`] to which it
|
||||
/// is a pointer.
|
||||
///
|
||||
/// This may entail spilling `pointer_id`'s value to a temporary:
|
||||
/// see [`get_expr_handle`]'s documentation.
|
||||
///
|
||||
/// [`Expression`]: crate::Expression
|
||||
/// [`Type`]: crate::Type
|
||||
/// [`Handle`]: crate::Handle
|
||||
/// [`get_expr_handle`]: Frontend::get_expr_handle
|
||||
fn get_exp_and_base_ty_handles(
|
||||
&self,
|
||||
pointer_id: spirv::Word,
|
||||
ctx: &mut BlockContext,
|
||||
emitter: &mut crate::proc::Emitter,
|
||||
block: &mut crate::Block,
|
||||
body_idx: usize,
|
||||
) -> Result<(Handle<crate::Expression>, Handle<crate::Type>), Error> {
|
||||
log::trace!("\t\t\tlooking up pointer expr {:?}", pointer_id);
|
||||
let p_lexp_handle;
|
||||
let p_lexp_ty_id;
|
||||
{
|
||||
let lexp = self.lookup_expression.lookup(pointer_id)?;
|
||||
p_lexp_handle = self.get_expr_handle(pointer_id, lexp, ctx, emitter, block, body_idx);
|
||||
p_lexp_ty_id = lexp.type_id;
|
||||
};
|
||||
|
||||
log::trace!("\t\t\tlooking up pointer type {pointer_id:?}");
|
||||
let p_ty = self.lookup_type.lookup(p_lexp_ty_id)?;
|
||||
let p_ty_base_id = p_ty.base_id.ok_or(Error::InvalidAccessType(p_lexp_ty_id))?;
|
||||
|
||||
log::trace!("\t\t\tlooking up pointer base type {p_ty_base_id:?} of {p_ty:?}");
|
||||
let p_base_ty = self.lookup_type.lookup(p_ty_base_id)?;
|
||||
|
||||
Ok((p_lexp_handle, p_base_ty.handle))
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn parse_atomic_expr_with_value(
|
||||
&mut self,
|
||||
inst: Instruction,
|
||||
emitter: &mut crate::proc::Emitter,
|
||||
ctx: &mut BlockContext,
|
||||
block: &mut crate::Block,
|
||||
block_id: spirv::Word,
|
||||
body_idx: usize,
|
||||
atomic_function: crate::AtomicFunction,
|
||||
) -> Result<(), Error> {
|
||||
inst.expect(7)?;
|
||||
let start = self.data_offset;
|
||||
let result_type_id = self.next()?;
|
||||
let result_id = self.next()?;
|
||||
let pointer_id = self.next()?;
|
||||
let _scope_id = self.next()?;
|
||||
let _memory_semantics_id = self.next()?;
|
||||
let value_id = self.next()?;
|
||||
let span = self.span_from_with_op(start);
|
||||
|
||||
let (p_lexp_handle, p_base_ty_handle) =
|
||||
self.get_exp_and_base_ty_handles(pointer_id, ctx, emitter, block, body_idx)?;
|
||||
|
||||
log::trace!("\t\t\tlooking up value expr {value_id:?}");
|
||||
let v_lexp_handle = self.lookup_expression.lookup(value_id)?.handle;
|
||||
|
||||
block.extend(emitter.finish(ctx.expressions));
|
||||
// Create an expression for our result
|
||||
let r_lexp_handle = {
|
||||
let expr = crate::Expression::AtomicResult {
|
||||
ty: p_base_ty_handle,
|
||||
comparison: false,
|
||||
};
|
||||
let handle = ctx.expressions.append(expr, span);
|
||||
self.lookup_expression.insert(
|
||||
result_id,
|
||||
LookupExpression {
|
||||
handle,
|
||||
type_id: result_type_id,
|
||||
block_id,
|
||||
},
|
||||
);
|
||||
handle
|
||||
};
|
||||
emitter.start(ctx.expressions);
|
||||
|
||||
// Create a statement for the op itself
|
||||
let stmt = crate::Statement::Atomic {
|
||||
pointer: p_lexp_handle,
|
||||
fun: atomic_function,
|
||||
value: v_lexp_handle,
|
||||
result: Some(r_lexp_handle),
|
||||
};
|
||||
block.push(stmt, span);
|
||||
|
||||
// Store any associated global variables so we can upgrade their types later
|
||||
self.upgrade_atomics
|
||||
.insert(ctx.get_contained_global_variable(p_lexp_handle)?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add the next SPIR-V block's contents to `block_ctx`.
|
||||
///
|
||||
/// Except for the function's entry block, `block_id` should be the label of
|
||||
|
@ -3985,35 +4094,91 @@ impl<I: Iterator<Item = u32>> Frontend<I> {
|
|||
);
|
||||
emitter.start(ctx.expressions);
|
||||
}
|
||||
Op::AtomicIIncrement => {
|
||||
Op::AtomicLoad => {
|
||||
inst.expect(6)?;
|
||||
let start = self.data_offset;
|
||||
let span = self.span_from_with_op(start);
|
||||
let result_type_id = self.next()?;
|
||||
let result_id = self.next()?;
|
||||
let pointer_id = self.next()?;
|
||||
let _scope_id = self.next()?;
|
||||
let _memory_semantics_id = self.next()?;
|
||||
let span = self.span_from_with_op(start);
|
||||
|
||||
log::trace!("\t\t\tlooking up expr {:?}", pointer_id);
|
||||
let (p_lexp_handle, p_lexp_ty_id) = {
|
||||
let lexp = self.lookup_expression.lookup(pointer_id)?;
|
||||
let handle = get_expr_handle!(pointer_id, &lexp);
|
||||
(handle, lexp.type_id)
|
||||
let p_lexp_handle =
|
||||
get_expr_handle!(pointer_id, self.lookup_expression.lookup(pointer_id)?);
|
||||
|
||||
// Create an expression for our result
|
||||
let expr = crate::Expression::Load {
|
||||
pointer: p_lexp_handle,
|
||||
};
|
||||
let handle = ctx.expressions.append(expr, span);
|
||||
self.lookup_expression.insert(
|
||||
result_id,
|
||||
LookupExpression {
|
||||
handle,
|
||||
type_id: result_type_id,
|
||||
block_id,
|
||||
},
|
||||
);
|
||||
|
||||
log::trace!("\t\t\tlooking up type {pointer_id:?}");
|
||||
let p_ty = self.lookup_type.lookup(p_lexp_ty_id)?;
|
||||
let p_ty_base_id =
|
||||
p_ty.base_id.ok_or(Error::InvalidAccessType(p_lexp_ty_id))?;
|
||||
// Store any associated global variables so we can upgrade their types later
|
||||
self.upgrade_atomics
|
||||
.insert(ctx.get_contained_global_variable(p_lexp_handle)?);
|
||||
}
|
||||
Op::AtomicStore => {
|
||||
inst.expect(5)?;
|
||||
let start = self.data_offset;
|
||||
let pointer_id = self.next()?;
|
||||
let _scope_id = self.next()?;
|
||||
let _memory_semantics_id = self.next()?;
|
||||
let value_id = self.next()?;
|
||||
let span = self.span_from_with_op(start);
|
||||
|
||||
log::trace!("\t\t\tlooking up base type {p_ty_base_id:?} of {p_ty:?}");
|
||||
let p_base_ty = self.lookup_type.lookup(p_ty_base_id)?;
|
||||
log::trace!("\t\t\tlooking up pointer expr {:?}", pointer_id);
|
||||
let p_lexp_handle =
|
||||
get_expr_handle!(pointer_id, self.lookup_expression.lookup(pointer_id)?);
|
||||
|
||||
log::trace!("\t\t\tlooking up value expr {:?}", pointer_id);
|
||||
let v_lexp_handle =
|
||||
get_expr_handle!(value_id, self.lookup_expression.lookup(value_id)?);
|
||||
|
||||
block.extend(emitter.finish(ctx.expressions));
|
||||
// Create a statement for the op itself
|
||||
let stmt = crate::Statement::Store {
|
||||
pointer: p_lexp_handle,
|
||||
value: v_lexp_handle,
|
||||
};
|
||||
block.push(stmt, span);
|
||||
emitter.start(ctx.expressions);
|
||||
|
||||
// Store any associated global variables so we can upgrade their types later
|
||||
self.upgrade_atomics
|
||||
.insert(ctx.get_contained_global_variable(p_lexp_handle)?);
|
||||
}
|
||||
Op::AtomicIIncrement | Op::AtomicIDecrement => {
|
||||
inst.expect(6)?;
|
||||
let start = self.data_offset;
|
||||
let result_type_id = self.next()?;
|
||||
let result_id = self.next()?;
|
||||
let pointer_id = self.next()?;
|
||||
let _scope_id = self.next()?;
|
||||
let _memory_semantics_id = self.next()?;
|
||||
let span = self.span_from_with_op(start);
|
||||
|
||||
let (p_exp_h, p_base_ty_h) = self.get_exp_and_base_ty_handles(
|
||||
pointer_id,
|
||||
ctx,
|
||||
&mut emitter,
|
||||
&mut block,
|
||||
body_idx,
|
||||
)?;
|
||||
|
||||
block.extend(emitter.finish(ctx.expressions));
|
||||
// Create an expression for our result
|
||||
let r_lexp_handle = {
|
||||
let expr = crate::Expression::AtomicResult {
|
||||
ty: p_base_ty.handle,
|
||||
ty: p_base_ty_h,
|
||||
comparison: false,
|
||||
};
|
||||
let handle = ctx.expressions.append(expr, span);
|
||||
|
@ -4027,22 +4192,26 @@ impl<I: Iterator<Item = u32>> Frontend<I> {
|
|||
);
|
||||
handle
|
||||
};
|
||||
emitter.start(ctx.expressions);
|
||||
|
||||
// Create a literal "1" since WGSL lacks an increment operation
|
||||
// Create a literal "1" to use as our value
|
||||
let one_lexp_handle = make_index_literal(
|
||||
ctx,
|
||||
1,
|
||||
&mut block,
|
||||
&mut emitter,
|
||||
p_base_ty.handle,
|
||||
p_lexp_ty_id,
|
||||
p_base_ty_h,
|
||||
result_type_id,
|
||||
span,
|
||||
)?;
|
||||
|
||||
// Create a statement for the op itself
|
||||
let stmt = crate::Statement::Atomic {
|
||||
pointer: p_lexp_handle,
|
||||
fun: crate::AtomicFunction::Add,
|
||||
pointer: p_exp_h,
|
||||
fun: match inst.op {
|
||||
Op::AtomicIIncrement => crate::AtomicFunction::Add,
|
||||
_ => crate::AtomicFunction::Subtract,
|
||||
},
|
||||
value: one_lexp_handle,
|
||||
result: Some(r_lexp_handle),
|
||||
};
|
||||
|
@ -4050,8 +4219,38 @@ impl<I: Iterator<Item = u32>> Frontend<I> {
|
|||
|
||||
// Store any associated global variables so we can upgrade their types later
|
||||
self.upgrade_atomics
|
||||
.extend(ctx.get_contained_global_variable(p_lexp_handle));
|
||||
.insert(ctx.get_contained_global_variable(p_exp_h)?);
|
||||
}
|
||||
Op::AtomicExchange
|
||||
| Op::AtomicIAdd
|
||||
| Op::AtomicISub
|
||||
| Op::AtomicSMin
|
||||
| Op::AtomicUMin
|
||||
| Op::AtomicSMax
|
||||
| Op::AtomicUMax
|
||||
| Op::AtomicAnd
|
||||
| Op::AtomicOr
|
||||
| Op::AtomicXor => self.parse_atomic_expr_with_value(
|
||||
inst,
|
||||
&mut emitter,
|
||||
ctx,
|
||||
&mut block,
|
||||
block_id,
|
||||
body_idx,
|
||||
match inst.op {
|
||||
Op::AtomicExchange => crate::AtomicFunction::Exchange { compare: None },
|
||||
Op::AtomicIAdd => crate::AtomicFunction::Add,
|
||||
Op::AtomicISub => crate::AtomicFunction::Subtract,
|
||||
Op::AtomicSMin => crate::AtomicFunction::Min,
|
||||
Op::AtomicUMin => crate::AtomicFunction::Min,
|
||||
Op::AtomicSMax => crate::AtomicFunction::Max,
|
||||
Op::AtomicUMax => crate::AtomicFunction::Max,
|
||||
Op::AtomicAnd => crate::AtomicFunction::And,
|
||||
Op::AtomicOr => crate::AtomicFunction::InclusiveOr,
|
||||
_ => crate::AtomicFunction::ExclusiveOr,
|
||||
},
|
||||
)?,
|
||||
|
||||
_ => {
|
||||
return Err(Error::UnsupportedInstruction(self.state, inst.op));
|
||||
}
|
||||
|
@ -5709,33 +5908,48 @@ mod test {
|
|||
];
|
||||
let _ = super::parse_u8_slice(&bin, &Default::default()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "wgsl-in", wgsl_out))]
|
||||
#[test]
|
||||
fn atomic_i_inc() {
|
||||
#[cfg(all(test, feature = "wgsl-in", wgsl_out))]
|
||||
mod test_atomic {
|
||||
fn atomic_test(bytes: &[u8]) {
|
||||
let _ = env_logger::builder().is_test(true).try_init();
|
||||
let bytes = include_bytes!("../../../tests/in/spv/atomic_i_increment.spv");
|
||||
let m = super::parse_u8_slice(bytes, &Default::default()).unwrap();
|
||||
let mut validator = crate::valid::Validator::new(
|
||||
let m = crate::front::spv::parse_u8_slice(bytes, &Default::default()).unwrap();
|
||||
|
||||
let mut wgsl = String::new();
|
||||
let mut should_panic = false;
|
||||
|
||||
for vflags in [
|
||||
crate::valid::ValidationFlags::all(),
|
||||
crate::valid::ValidationFlags::empty(),
|
||||
Default::default(),
|
||||
);
|
||||
let info = match validator.validate(&m) {
|
||||
Err(e) => {
|
||||
log::error!("{}", e.emit_to_string(""));
|
||||
return;
|
||||
}
|
||||
Ok(i) => i,
|
||||
};
|
||||
let wgsl =
|
||||
crate::back::wgsl::write_string(&m, &info, crate::back::wgsl::WriterFlags::empty())
|
||||
.unwrap();
|
||||
log::info!("atomic_i_increment:\n{wgsl}");
|
||||
] {
|
||||
let mut validator = crate::valid::Validator::new(vflags, Default::default());
|
||||
match validator.validate(&m) {
|
||||
Err(e) => {
|
||||
log::error!("SPIR-V validation {}", e.emit_to_string(""));
|
||||
should_panic = true;
|
||||
}
|
||||
Ok(i) => {
|
||||
wgsl = crate::back::wgsl::write_string(
|
||||
&m,
|
||||
&i,
|
||||
crate::back::wgsl::WriterFlags::empty(),
|
||||
)
|
||||
.unwrap();
|
||||
log::info!("wgsl-out:\n{wgsl}");
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if should_panic {
|
||||
panic!("validation error");
|
||||
}
|
||||
|
||||
let m = match crate::front::wgsl::parse_str(&wgsl) {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
log::error!("{}", e.emit_to_string(&wgsl));
|
||||
log::error!("round trip WGSL validation {}", e.emit_to_string(&wgsl));
|
||||
panic!("invalid module");
|
||||
}
|
||||
};
|
||||
|
@ -5746,4 +5960,35 @@ mod test {
|
|||
panic!("invalid generated wgsl");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn atomic_i_inc() {
|
||||
atomic_test(include_bytes!(
|
||||
"../../../tests/in/spv/atomic_i_increment.spv"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn atomic_load_and_store() {
|
||||
atomic_test(include_bytes!(
|
||||
"../../../tests/in/spv/atomic_load_and_store.spv"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn atomic_exchange() {
|
||||
atomic_test(include_bytes!("../../../tests/in/spv/atomic_exchange.spv"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn atomic_i_decrement() {
|
||||
atomic_test(include_bytes!(
|
||||
"../../../tests/in/spv/atomic_i_decrement.spv"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn atomic_i_add_and_sub() {
|
||||
atomic_test(include_bytes!("../../../tests/in/spv/atomic_i_add_sub.spv"));
|
||||
}
|
||||
}
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -84,7 +84,7 @@ path = "../naga"
|
|||
version = "1.19.0"
|
||||
|
||||
[dependencies.parking_lot]
|
||||
version = ">=0.11, <0.13"
|
||||
version = "0.12.1"
|
||||
|
||||
[dependencies.profiling]
|
||||
version = "1"
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -71,7 +71,7 @@ version = "0.7"
|
|||
version = "2.6"
|
||||
|
||||
[dependencies.glow]
|
||||
version = "0.14.0"
|
||||
version = "0.14.1"
|
||||
optional = true
|
||||
|
||||
[dependencies.log]
|
||||
|
@ -85,7 +85,7 @@ path = "../naga"
|
|||
version = "1.19.0"
|
||||
|
||||
[dependencies.parking_lot]
|
||||
version = ">=0.11, <0.13"
|
||||
version = "0.12.1"
|
||||
|
||||
[dependencies.profiling]
|
||||
version = "1"
|
||||
|
|
|
@ -769,6 +769,7 @@ impl super::Device {
|
|||
temp_options.debug_info = Some(naga::back::spv::DebugInfo {
|
||||
source_code: &debug.source_code,
|
||||
file_name: debug.file_name.as_ref().as_ref(),
|
||||
language: naga::back::spv::SourceLanguage::WGSL,
|
||||
})
|
||||
}
|
||||
if !stage.zero_initialize_workgroup_memory {
|
||||
|
@ -1742,6 +1743,7 @@ impl crate::Device for super::Device {
|
|||
.map(|d| naga::back::spv::DebugInfo {
|
||||
source_code: d.source_code.as_ref(),
|
||||
file_name: d.file_name.as_ref().as_ref(),
|
||||
language: naga::back::spv::SourceLanguage::WGSL,
|
||||
});
|
||||
if !desc.runtime_checks {
|
||||
naga_options.bounds_check_policies = naga::proc::BoundsCheckPolicies {
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"32a781ba9b4fb68f6854c80c3d307bdd720de34bc8fe17aef73cf8f821f18098","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","src/assertions.rs":"d22aa7ddb95d3ae129ff9848fd45913bab41bd354b6f1c1f6c38a86f9d1abbc6","src/counters.rs":"599e9c033c4f8fcc95113bf730191d80f51b3276b3ee8fd03bbc1c27d24bf76d","src/lib.rs":"e4c3a736e22002ea6d5c4e0aceeb1b5cdf269b742fbfa06b88e369b5c90b20c0","src/math.rs":"4d03039736dd6926feb139bc68734cb59df34ede310427bbf059e5c925e0af3b"},"package":null}
|
||||
{"files":{"Cargo.toml":"32a781ba9b4fb68f6854c80c3d307bdd720de34bc8fe17aef73cf8f821f18098","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","src/assertions.rs":"d22aa7ddb95d3ae129ff9848fd45913bab41bd354b6f1c1f6c38a86f9d1abbc6","src/counters.rs":"599e9c033c4f8fcc95113bf730191d80f51b3276b3ee8fd03bbc1c27d24bf76d","src/lib.rs":"bf21e776108de35061b72b95cd865bc435a3096567b00381555299c009c8c05b","src/math.rs":"4d03039736dd6926feb139bc68734cb59df34ede310427bbf059e5c925e0af3b"},"package":null}
|
|
@ -568,7 +568,7 @@ bitflags::bitflags! {
|
|||
/// may also create uniform arrays of storage textures.
|
||||
///
|
||||
/// ex.
|
||||
/// - `var textures: array<texture_storage_2d<f32, write>, 10>` (WGSL)
|
||||
/// - `var textures: array<texture_storage_2d<r32float, write>, 10>` (WGSL)
|
||||
/// - `uniform image2D textures[10]` (GLSL)
|
||||
///
|
||||
/// This capability allows them to exist and to be indexed by dynamically uniform
|
||||
|
@ -1959,12 +1959,13 @@ impl TextureViewDimension {
|
|||
|
||||
/// Alpha blend factor.
|
||||
///
|
||||
/// Alpha blending is very complicated: see the OpenGL or Vulkan spec for more information.
|
||||
///
|
||||
/// Corresponds to [WebGPU `GPUBlendFactor`](
|
||||
/// https://gpuweb.github.io/gpuweb/#enumdef-gpublendfactor).
|
||||
/// Values using S1 requires [`Features::DUAL_SOURCE_BLENDING`] and can only be
|
||||
/// used with the first render target.
|
||||
/// https://gpuweb.github.io/gpuweb/#enumdef-gpublendfactor). Values using `Src1`
|
||||
/// require [`Features::DUAL_SOURCE_BLENDING`] and can only be used with the first
|
||||
/// render target.
|
||||
///
|
||||
/// For further details on how the blend factors are applied, see the analogous
|
||||
/// functionality in OpenGL: <https://www.khronos.org/opengl/wiki/Blending#Blending_Parameters>.
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
|
@ -2024,10 +2025,11 @@ impl BlendFactor {
|
|||
|
||||
/// Alpha blend operation.
|
||||
///
|
||||
/// Alpha blending is very complicated: see the OpenGL or Vulkan spec for more information.
|
||||
///
|
||||
/// Corresponds to [WebGPU `GPUBlendOperation`](
|
||||
/// https://gpuweb.github.io/gpuweb/#enumdef-gpublendoperation).
|
||||
///
|
||||
/// For further details on how the blend operations are applied, see
|
||||
/// the analogous functionality in OpenGL: <https://www.khronos.org/opengl/wiki/Blending#Blend_Equations>.
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
|
@ -2102,8 +2104,6 @@ impl Default for BlendComponent {
|
|||
/// Describe the blend state of a render pipeline,
|
||||
/// within [`ColorTargetState`].
|
||||
///
|
||||
/// See the OpenGL or Vulkan spec for more information.
|
||||
///
|
||||
/// Corresponds to [WebGPU `GPUBlendState`](
|
||||
/// https://gpuweb.github.io/gpuweb/#dictdef-gpublendstate).
|
||||
#[repr(C)]
|
||||
|
@ -6543,7 +6543,7 @@ pub enum StorageTextureAccess {
|
|||
/// Example WGSL syntax:
|
||||
/// ```rust,ignore
|
||||
/// @group(0) @binding(0)
|
||||
/// var my_storage_image: texture_storage_2d<f32, write>;
|
||||
/// var my_storage_image: texture_storage_2d<r32float, write>;
|
||||
/// ```
|
||||
///
|
||||
/// Example GLSL syntax:
|
||||
|
@ -6560,7 +6560,7 @@ pub enum StorageTextureAccess {
|
|||
/// Example WGSL syntax:
|
||||
/// ```rust,ignore
|
||||
/// @group(0) @binding(0)
|
||||
/// var my_storage_image: texture_storage_2d<f32, read>;
|
||||
/// var my_storage_image: texture_storage_2d<r32float, read>;
|
||||
/// ```
|
||||
///
|
||||
/// Example GLSL syntax:
|
||||
|
@ -6577,7 +6577,7 @@ pub enum StorageTextureAccess {
|
|||
/// Example WGSL syntax:
|
||||
/// ```rust,ignore
|
||||
/// @group(0) @binding(0)
|
||||
/// var my_storage_image: texture_storage_2d<f32, read_write>;
|
||||
/// var my_storage_image: texture_storage_2d<r32float, read_write>;
|
||||
/// ```
|
||||
///
|
||||
/// Example GLSL syntax:
|
||||
|
@ -6701,8 +6701,8 @@ pub enum BindingType {
|
|||
/// Dimension of the texture view that is going to be sampled.
|
||||
view_dimension: TextureViewDimension,
|
||||
/// True if the texture has a sample count greater than 1. If this is true,
|
||||
/// the texture must be read from shaders with `texture1DMS`, `texture2DMS`, or `texture3DMS`,
|
||||
/// depending on `dimension`.
|
||||
/// the texture must be declared as `texture_multisampled_2d` or
|
||||
/// `texture_depth_multisampled_2d` in the shader, and read using `textureLoad`.
|
||||
multisampled: bool,
|
||||
},
|
||||
/// A storage texture.
|
||||
|
@ -6710,15 +6710,16 @@ pub enum BindingType {
|
|||
/// Example WGSL syntax:
|
||||
/// ```rust,ignore
|
||||
/// @group(0) @binding(0)
|
||||
/// var my_storage_image: texture_storage_2d<f32, write>;
|
||||
/// var my_storage_image: texture_storage_2d<r32float, write>;
|
||||
/// ```
|
||||
///
|
||||
/// Example GLSL syntax:
|
||||
/// ```cpp,ignore
|
||||
/// layout(set=0, binding=0, r32f) writeonly uniform image2D myStorageImage;
|
||||
/// ```
|
||||
/// Note that the texture format must be specified in the shader as well.
|
||||
/// A list of valid formats can be found in the specification here: <https://www.khronos.org/registry/OpenGL/specs/gl/GLSLangSpec.4.60.html#layout-qualifiers>
|
||||
/// Note that the texture format must be specified in the shader, along with the
|
||||
/// access mode. For WGSL, the format must be one of the enumerants in the list
|
||||
/// of [storage texel formats](https://gpuweb.github.io/gpuweb/wgsl/#storage-texel-formats).
|
||||
///
|
||||
/// Corresponds to [WebGPU `GPUStorageTextureBindingLayout`](
|
||||
/// https://gpuweb.github.io/gpuweb/#dictdef-gpustoragetexturebindinglayout).
|
||||
|
|
Загрузка…
Ссылка в новой задаче