[wgsl-in, spv-out] Allow dynamic indexing of arrays by value.

Bring https://github.com/gfx-rs/naga/pull/723 back from the dead.

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
Co-authored-by: Dzmitry Malyshau <kvarkus@gmail.com>
Co-authored-by: Jim Blandy <jimb@red-bean.com>
This commit is contained in:
sagudev 2024-09-01 16:12:06 +02:00 committed by Jim Blandy
parent 71b4f361e2
commit 2d82054ae4
21 changed files with 837 additions and 173 deletions

View File

@ -343,6 +343,32 @@ impl<'w> BlockContext<'w> {
load_id load_id
} }
crate::TypeInner::Array {
base: ty_element, ..
} => {
let index_id = self.cached[index];
let base_id = self.cached[base];
let base_ty = match self.fun_info[base].ty {
TypeResolution::Handle(handle) => handle,
TypeResolution::Value(_) => {
return Err(Error::Validation(
"Array types should always be in the arena",
))
}
};
let (id, variable) = self.writer.promote_access_expression_to_variable(
&self.ir_module.types,
result_type_id,
base_id,
base_ty,
index_id,
ty_element,
block,
)?;
self.function.internal_variables.push(variable);
id
}
// wgpu#4337: Support `crate::TypeInner::Matrix`
ref other => { ref other => {
log::error!( log::error!(
"Unable to access base {:?} of type {:?}", "Unable to access base {:?} of type {:?}",
@ -350,7 +376,7 @@ impl<'w> BlockContext<'w> {
other other
); );
return Err(Error::Validation( return Err(Error::Validation(
"only vectors may be dynamically indexed by value", "only vectors and arrays may be dynamically indexed by value",
)); ));
} }
} }

View File

@ -144,6 +144,7 @@ struct Function {
signature: Option<Instruction>, signature: Option<Instruction>,
parameters: Vec<FunctionArgument>, parameters: Vec<FunctionArgument>,
variables: crate::FastHashMap<Handle<crate::LocalVariable>, LocalVariable>, variables: crate::FastHashMap<Handle<crate::LocalVariable>, LocalVariable>,
internal_variables: Vec<LocalVariable>,
blocks: Vec<TerminatedBlock>, blocks: Vec<TerminatedBlock>,
entry_point_context: Option<EntryPointContext>, entry_point_context: Option<EntryPointContext>,
} }

View File

@ -32,6 +32,9 @@ impl Function {
for local_var in self.variables.values() { for local_var in self.variables.values() {
local_var.instruction.to_words(sink); local_var.instruction.to_words(sink);
} }
for internal_var in self.internal_variables.iter() {
internal_var.instruction.to_words(sink);
}
} }
for instruction in block.body.iter() { for instruction in block.body.iter() {
instruction.to_words(sink); instruction.to_words(sink);
@ -135,6 +138,56 @@ impl Writer {
self.capabilities_used.insert(spirv::Capability::Shader); self.capabilities_used.insert(spirv::Capability::Shader);
} }
#[allow(clippy::too_many_arguments)]
pub(super) fn promote_access_expression_to_variable(
&mut self,
ir_types: &UniqueArena<crate::Type>,
result_type_id: Word,
container_id: Word,
container_ty: Handle<crate::Type>,
index_id: Word,
element_ty: Handle<crate::Type>,
block: &mut Block,
) -> Result<(Word, LocalVariable), Error> {
let pointer_type_id =
self.get_pointer_id(ir_types, container_ty, spirv::StorageClass::Function)?;
let variable = {
let id = self.id_gen.next();
LocalVariable {
id,
instruction: Instruction::variable(
pointer_type_id,
id,
spirv::StorageClass::Function,
None,
),
}
};
block
.body
.push(Instruction::store(variable.id, container_id, None));
let element_pointer_id = self.id_gen.next();
let element_pointer_type_id =
self.get_pointer_id(ir_types, element_ty, spirv::StorageClass::Function)?;
block.body.push(Instruction::access_chain(
element_pointer_type_id,
element_pointer_id,
variable.id,
&[index_id],
));
let id = self.id_gen.next();
block.body.push(Instruction::load(
result_type_id,
id,
element_pointer_id,
None,
));
Ok((id, variable))
}
/// Indicate that the code requires any one of the listed capabilities. /// Indicate that the code requires any one of the listed capabilities.
/// ///
/// If nothing in `capabilities` appears in the available capabilities /// If nothing in `capabilities` appears in the available capabilities

View File

@ -1402,21 +1402,20 @@ pub enum Expression {
/// ## Dynamic indexing restrictions /// ## Dynamic indexing restrictions
/// ///
/// To accommodate restrictions in some of the shader languages that Naga /// To accommodate restrictions in some of the shader languages that Naga
/// targets, it is not permitted to subscript a matrix or array with a /// targets, it is not permitted to subscript a matrix with a dynamically
/// dynamically computed index unless that matrix or array appears behind a /// computed index unless that matrix appears behind a pointer. In other
/// pointer. In other words, if the inner type of `base` is [`Array`] or /// words, if the inner type of `base` is [`Matrix`], then `index` must be a
/// [`Matrix`], then `index` must be a constant. But if the type of `base` /// constant. But if the type of `base` is a [`Pointer`] to an matrix, then
/// is a [`Pointer`] to an array or matrix or a [`ValuePointer`] with a /// the index may be any expression of integer type.
/// `size`, then the index may be any expression of integer type.
/// ///
/// You can use the [`Expression::is_dynamic_index`] method to determine /// You can use the [`Expression::is_dynamic_index`] method to determine
/// whether a given index expression requires matrix or array base operands /// whether a given index expression requires matrix base operands to be
/// to be behind a pointer. /// behind a pointer.
/// ///
/// (It would be simpler to always require the use of `AccessIndex` when /// (It would be simpler to always require the use of `AccessIndex` when
/// subscripting arrays and matrices that are not behind pointers, but to /// subscripting matrices that are not behind pointers, but to accommodate
/// accommodate existing front ends, Naga also permits `Access`, with a /// existing front ends, Naga also permits `Access`, with a restricted
/// restricted `index`.) /// `index`.)
/// ///
/// [`Vector`]: TypeInner::Vector /// [`Vector`]: TypeInner::Vector
/// [`Matrix`]: TypeInner::Matrix /// [`Matrix`]: TypeInner::Matrix

View File

@ -521,12 +521,12 @@ impl crate::Expression {
} }
} }
/// Return true if this expression is a dynamic array index, for [`Access`]. /// Return true if this expression is a dynamic array/vector/matrix index,
/// for [`Access`].
/// ///
/// This method returns true if this expression is a dynamically computed /// This method returns true if this expression is a dynamically computed
/// index, and as such can only be used to index matrices and arrays when /// index, and as such can only be used to index matrices when they appear
/// they appear behind a pointer. See the documentation for [`Access`] for /// behind a pointer. See the documentation for [`Access`] for details.
/// details.
/// ///
/// Note, this does not check the _type_ of the given expression. It's up to /// Note, this does not check the _type_ of the given expression. It's up to
/// the caller to establish that the `Access` expression is well-typed /// the caller to establish that the `Access` expression is well-typed

View File

@ -92,6 +92,13 @@ pub enum TypeResolution {
/// available in the associated arena. However, the `TypeInner` itself may /// available in the associated arena. However, the `TypeInner` itself may
/// contain `Handle<Type>` values referring to types from the arena. /// contain `Handle<Type>` values referring to types from the arena.
/// ///
/// The inner type must only be one of the following variants:
/// - TypeInner::Pointer
/// - TypeInner::ValuePointer
/// - TypeInner::Matrix (generated by matrix multiplication)
/// - TypeInner::Vector
/// - TypeInner::Scalar
///
/// [`TypeInner`]: crate::TypeInner /// [`TypeInner`]: crate::TypeInner
Value(crate::TypeInner), Value(crate::TypeInner),
} }

View File

@ -240,9 +240,10 @@ impl super::Validator {
let base_type = &resolver[base]; let base_type = &resolver[base];
// See the documentation for `Expression::Access`. // See the documentation for `Expression::Access`.
let dynamic_indexing_restricted = match *base_type { let dynamic_indexing_restricted = match *base_type {
Ti::Vector { .. } => false, Ti::Matrix { .. } => true,
Ti::Matrix { .. } | Ti::Array { .. } => true, Ti::Vector { .. }
Ti::Pointer { .. } | Ti::Array { .. }
| Ti::Pointer { .. }
| Ti::ValuePointer { size: Some(_), .. } | Ti::ValuePointer { size: Some(_), .. }
| Ti::BindingArray { .. } => false, | Ti::BindingArray { .. } => false,
ref other => { ref other => {

View File

@ -167,3 +167,14 @@ fn assign_through_ptr() {
var arr = array<vec4<f32>, 2>(vec4(6.0), vec4(7.0)); var arr = array<vec4<f32>, 2>(vec4(6.0), vec4(7.0));
assign_array_through_ptr_fn(&arr); assign_array_through_ptr_fn(&arr);
} }
@vertex
fn foo(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4<f32> {
let arr = array<i32, 5>(1, 2, 3, 4, 5);
let value = arr[vi];
return vec4<f32>(vec4<i32>(value));
}
fn array_by_value(a: array<i32, 5>, i: i32) -> i32 {
return a[i];
}

View File

@ -2735,6 +2735,54 @@
sampling: [], sampling: [],
dual_source_blending: false, dual_source_blending: false,
), ),
(
flags: ("EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS"),
available_stages: ("VERTEX | FRAGMENT | COMPUTE"),
uniformity: (
non_uniform_result: Some(0),
requirements: (""),
),
may_kill: false,
sampling_set: [],
global_uses: [
(""),
(""),
(""),
(""),
(""),
],
expressions: [
(
uniformity: (
non_uniform_result: Some(0),
requirements: (""),
),
ref_count: 1,
assignable_global: None,
ty: Handle(25),
),
(
uniformity: (
non_uniform_result: Some(1),
requirements: (""),
),
ref_count: 1,
assignable_global: None,
ty: Handle(2),
),
(
uniformity: (
non_uniform_result: Some(0),
requirements: (""),
),
ref_count: 1,
assignable_global: None,
ty: Handle(2),
),
],
sampling: [],
dual_source_blending: false,
),
], ],
entry_points: [ entry_points: [
( (
@ -3981,6 +4029,144 @@
sampling: [], sampling: [],
dual_source_blending: false, dual_source_blending: false,
), ),
(
flags: ("EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS"),
available_stages: ("VERTEX | FRAGMENT | COMPUTE"),
uniformity: (
non_uniform_result: Some(0),
requirements: (""),
),
may_kill: false,
sampling_set: [],
global_uses: [
(""),
(""),
(""),
(""),
(""),
],
expressions: [
(
uniformity: (
non_uniform_result: Some(0),
requirements: (""),
),
ref_count: 1,
assignable_global: None,
ty: Handle(0),
),
(
uniformity: (
non_uniform_result: None,
requirements: (""),
),
ref_count: 1,
assignable_global: None,
ty: Value(Scalar((
kind: Sint,
width: 4,
))),
),
(
uniformity: (
non_uniform_result: None,
requirements: (""),
),
ref_count: 1,
assignable_global: None,
ty: Value(Scalar((
kind: Sint,
width: 4,
))),
),
(
uniformity: (
non_uniform_result: None,
requirements: (""),
),
ref_count: 1,
assignable_global: None,
ty: Value(Scalar((
kind: Sint,
width: 4,
))),
),
(
uniformity: (
non_uniform_result: None,
requirements: (""),
),
ref_count: 1,
assignable_global: None,
ty: Value(Scalar((
kind: Sint,
width: 4,
))),
),
(
uniformity: (
non_uniform_result: None,
requirements: (""),
),
ref_count: 1,
assignable_global: None,
ty: Value(Scalar((
kind: Sint,
width: 4,
))),
),
(
uniformity: (
non_uniform_result: None,
requirements: (""),
),
ref_count: 1,
assignable_global: None,
ty: Handle(25),
),
(
uniformity: (
non_uniform_result: Some(0),
requirements: (""),
),
ref_count: 1,
assignable_global: None,
ty: Handle(2),
),
(
uniformity: (
non_uniform_result: Some(0),
requirements: (""),
),
ref_count: 1,
assignable_global: None,
ty: Value(Vector(
size: Quad,
scalar: (
kind: Sint,
width: 4,
),
)),
),
(
uniformity: (
non_uniform_result: Some(0),
requirements: (""),
),
ref_count: 1,
assignable_global: None,
ty: Value(Vector(
size: Quad,
scalar: (
kind: Float,
width: 4,
),
)),
),
],
sampling: [],
dual_source_blending: false,
),
], ],
const_expression_types: [ const_expression_types: [
Value(Scalar(( Value(Scalar((

View File

@ -20,8 +20,8 @@ struct MatCx2InArray {
mat4x2 am[2]; mat4x2 am[2];
}; };
float read_from_private(inout float foo_1) { float read_from_private(inout float foo_2) {
float _e1 = foo_1; float _e1 = foo_2;
return _e1; return _e1;
} }
@ -34,11 +34,15 @@ void assign_through_ptr_fn(inout uint p) {
return; return;
} }
void assign_array_through_ptr_fn(inout vec4 foo_2[2]) { void assign_array_through_ptr_fn(inout vec4 foo_3[2]) {
foo_2 = vec4[2](vec4(1.0), vec4(2.0)); foo_3 = vec4[2](vec4(1.0), vec4(2.0));
return; return;
} }
int array_by_value(int a_1[5], int i) {
return a_1[i];
}
void main() { void main() {
uint val = 33u; uint val = 33u;
vec4 arr[2] = vec4[2](vec4(6.0), vec4(7.0)); vec4 arr[2] = vec4[2](vec4(6.0), vec4(7.0));

View File

@ -0,0 +1,52 @@
#version 310 es
precision highp float;
precision highp int;
struct GlobalConst {
uint a;
uvec3 b;
int c;
};
struct AlignedWrapper {
int value;
};
struct Baz {
mat3x2 m;
};
struct MatCx2InArray {
mat4x2 am[2];
};
float read_from_private(inout float foo_2) {
float _e1 = foo_2;
return _e1;
}
float test_arr_as_arg(float a[5][10]) {
return a[4][9];
}
void assign_through_ptr_fn(inout uint p) {
p = 42u;
return;
}
void assign_array_through_ptr_fn(inout vec4 foo_3[2]) {
foo_3 = vec4[2](vec4(1.0), vec4(2.0));
return;
}
int array_by_value(int a_1[5], int i) {
return a_1[i];
}
void main() {
uint vi_1 = uint(gl_VertexID);
int arr_1[5] = int[5](1, 2, 3, 4, 5);
int value = arr_1[vi_1];
gl_Position = vec4(ivec4(value));
gl_Position.yz = vec2(-gl_Position.y, gl_Position.z * 2.0 - gl_Position.w);
return;
}

View File

@ -30,8 +30,8 @@ layout(std430) buffer type_13_block_1Fragment { ivec2 _group_0_binding_2_fs; };
layout(location = 0) out vec4 _fs2p_location0; layout(location = 0) out vec4 _fs2p_location0;
float read_from_private(inout float foo_1) { float read_from_private(inout float foo_2) {
float _e1 = foo_1; float _e1 = foo_2;
return _e1; return _e1;
} }
@ -44,11 +44,15 @@ void assign_through_ptr_fn(inout uint p) {
return; return;
} }
void assign_array_through_ptr_fn(inout vec4 foo_2[2]) { void assign_array_through_ptr_fn(inout vec4 foo_3[2]) {
foo_2 = vec4[2](vec4(1.0), vec4(2.0)); foo_3 = vec4[2](vec4(1.0), vec4(2.0));
return; return;
} }
int array_by_value(int a_1[5], int i) {
return a_1[i];
}
void main() { void main() {
_group_0_binding_0_fs._matrix[1][2] = 1.0; _group_0_binding_0_fs._matrix[1][2] = 1.0;
_group_0_binding_0_fs._matrix = mat4x3(vec3(0.0), vec3(1.0), vec3(2.0), vec3(3.0)); _group_0_binding_0_fs._matrix = mat4x3(vec3(0.0), vec3(1.0), vec3(2.0), vec3(3.0));

View File

@ -103,8 +103,8 @@ void test_matrix_within_array_within_struct_accesses() {
return; return;
} }
float read_from_private(inout float foo_1) { float read_from_private(inout float foo_2) {
float _e1 = foo_1; float _e1 = foo_2;
return _e1; return _e1;
} }
@ -117,11 +117,15 @@ void assign_through_ptr_fn(inout uint p) {
return; return;
} }
void assign_array_through_ptr_fn(inout vec4 foo_2[2]) { void assign_array_through_ptr_fn(inout vec4 foo_3[2]) {
foo_2 = vec4[2](vec4(1.0), vec4(2.0)); foo_3 = vec4[2](vec4(1.0), vec4(2.0));
return; return;
} }
int array_by_value(int a_1[5], int i) {
return a_1[i];
}
void main() { void main() {
uint vi = uint(gl_VertexID); uint vi = uint(gl_VertexID);
float foo = 0.0; float foo = 0.0;
@ -133,10 +137,10 @@ void main() {
mat4x3 _matrix = _group_0_binding_0_vs._matrix; mat4x3 _matrix = _group_0_binding_0_vs._matrix;
uvec2 arr_1[2] = _group_0_binding_0_vs.arr; uvec2 arr_1[2] = _group_0_binding_0_vs.arr;
float b = _group_0_binding_0_vs._matrix[3u][0]; float b = _group_0_binding_0_vs._matrix[3u][0];
int a_1 = _group_0_binding_0_vs.data[(uint(_group_0_binding_0_vs.data.length()) - 2u)].value; int a_2 = _group_0_binding_0_vs.data[(uint(_group_0_binding_0_vs.data.length()) - 2u)].value;
ivec2 c = _group_0_binding_2_vs; ivec2 c = _group_0_binding_2_vs;
float _e33 = read_from_private(foo); float _e33 = read_from_private(foo);
c2_ = int[5](a_1, int(b), 3, 4, 5); c2_ = int[5](a_2, int(b), 3, 4, 5);
c2_[(vi + 1u)] = 42; c2_[(vi + 1u)] = 42;
int value = c2_[vi]; int value = c2_[vi];
float _e47 = test_arr_as_arg(float[5][10](float[10](0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), float[10](0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), float[10](0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), float[10](0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), float[10](0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0))); float _e47 = test_arr_as_arg(float[5][10](float[10](0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), float[10](0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), float[10](0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), float[10](0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), float[10](0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)));

View File

@ -201,9 +201,9 @@ void test_matrix_within_array_within_struct_accesses()
return; return;
} }
float read_from_private(inout float foo_1) float read_from_private(inout float foo_2)
{ {
float _e1 = foo_1; float _e1 = foo_2;
return _e1; return _e1;
} }
@ -224,12 +224,17 @@ ret_Constructarray2_float4_ Constructarray2_float4_(float4 arg0, float4 arg1) {
return ret; return ret;
} }
void assign_array_through_ptr_fn(inout float4 foo_2[2]) void assign_array_through_ptr_fn(inout float4 foo_3[2])
{ {
foo_2 = Constructarray2_float4_((1.0).xxxx, (2.0).xxxx); foo_3 = Constructarray2_float4_((1.0).xxxx, (2.0).xxxx);
return; return;
} }
int array_by_value(int a_1[5], int i)
{
return a_1[i];
}
typedef int ret_Constructarray5_int_[5]; typedef int ret_Constructarray5_int_[5];
ret_Constructarray5_int_ Constructarray5_int_(int arg0, int arg1, int arg2, int arg3, int arg4) { ret_Constructarray5_int_ Constructarray5_int_(int arg0, int arg1, int arg2, int arg3, int arg4) {
int ret[5] = { arg0, arg1, arg2, arg3, arg4 }; int ret[5] = { arg0, arg1, arg2, arg3, arg4 };
@ -266,10 +271,10 @@ float4 foo_vert(uint vi : SV_VertexID) : SV_Position
float4x3 _matrix = float4x3(asfloat(bar.Load3(0+0)), asfloat(bar.Load3(0+16)), asfloat(bar.Load3(0+32)), asfloat(bar.Load3(0+48))); float4x3 _matrix = float4x3(asfloat(bar.Load3(0+0)), asfloat(bar.Load3(0+16)), asfloat(bar.Load3(0+32)), asfloat(bar.Load3(0+48)));
uint2 arr_1[2] = Constructarray2_uint2_(asuint(bar.Load2(144+0)), asuint(bar.Load2(144+8))); uint2 arr_1[2] = Constructarray2_uint2_(asuint(bar.Load2(144+0)), asuint(bar.Load2(144+8)));
float b = asfloat(bar.Load(0+3u*16+0)); float b = asfloat(bar.Load(0+3u*16+0));
int a_1 = asint(bar.Load(0+(((NagaBufferLengthRW(bar) - 160) / 8) - 2u)*8+160)); int a_2 = asint(bar.Load(0+(((NagaBufferLengthRW(bar) - 160) / 8) - 2u)*8+160));
int2 c = asint(qux.Load2(0)); int2 c = asint(qux.Load2(0));
const float _e33 = read_from_private(foo); const float _e33 = read_from_private(foo);
c2_ = Constructarray5_int_(a_1, int(b), 3, 4, 5); c2_ = Constructarray5_int_(a_2, int(b), 3, 4, 5);
c2_[(vi + 1u)] = 42; c2_[(vi + 1u)] = 42;
int value = c2_[vi]; int value = c2_[vi];
const float _e47 = test_arr_as_arg(ZeroValuearray5_array10_float__()); const float _e47 = test_arr_as_arg(ZeroValuearray5_array10_float__());
@ -310,3 +315,10 @@ void assign_through_ptr()
assign_array_through_ptr_fn(arr); assign_array_through_ptr_fn(arr);
return; return;
} }
float4 foo_1(uint vi_1 : SV_VertexID) : SV_Position
{
int arr_2[5] = Constructarray5_int_(1, 2, 3, 4, 5);
int value_1 = arr_2[vi_1];
return float4((value_1).xxxx);
}

View File

@ -4,6 +4,10 @@
entry_point:"foo_vert", entry_point:"foo_vert",
target_profile:"vs_5_1", target_profile:"vs_5_1",
), ),
(
entry_point:"foo_1",
target_profile:"vs_5_1",
),
], ],
fragment:[ fragment:[
( (

View File

@ -1655,6 +1655,47 @@
), ),
], ],
), ),
(
name: Some("array_by_value"),
arguments: [
(
name: Some("a"),
ty: 25,
binding: None,
),
(
name: Some("i"),
ty: 2,
binding: None,
),
],
result: Some((
ty: 2,
binding: None,
)),
local_variables: [],
expressions: [
FunctionArgument(0),
FunctionArgument(1),
Access(
base: 0,
index: 1,
),
],
named_expressions: {
0: "a",
1: "i",
},
body: [
Emit((
start: 2,
end: 3,
)),
Return(
value: Some(2),
),
],
),
], ],
entry_points: [ entry_points: [
( (
@ -2230,5 +2271,81 @@
], ],
), ),
), ),
(
name: "foo",
stage: Vertex,
early_depth_test: None,
workgroup_size: (0, 0, 0),
function: (
name: Some("foo"),
arguments: [
(
name: Some("vi"),
ty: 0,
binding: Some(BuiltIn(VertexIndex)),
),
],
result: Some((
ty: 24,
binding: Some(BuiltIn(Position(
invariant: false,
))),
)),
local_variables: [],
expressions: [
FunctionArgument(0),
Literal(I32(1)),
Literal(I32(2)),
Literal(I32(3)),
Literal(I32(4)),
Literal(I32(5)),
Compose(
ty: 25,
components: [
1,
2,
3,
4,
5,
],
),
Access(
base: 6,
index: 0,
),
Splat(
size: Quad,
value: 7,
),
As(
expr: 8,
kind: Float,
convert: Some(4),
),
],
named_expressions: {
0: "vi",
6: "arr",
7: "value",
},
body: [
Emit((
start: 6,
end: 7,
)),
Emit((
start: 7,
end: 8,
)),
Emit((
start: 8,
end: 10,
)),
Return(
value: Some(9),
),
],
),
),
], ],
) )

View File

@ -1655,6 +1655,47 @@
), ),
], ],
), ),
(
name: Some("array_by_value"),
arguments: [
(
name: Some("a"),
ty: 25,
binding: None,
),
(
name: Some("i"),
ty: 2,
binding: None,
),
],
result: Some((
ty: 2,
binding: None,
)),
local_variables: [],
expressions: [
FunctionArgument(0),
FunctionArgument(1),
Access(
base: 0,
index: 1,
),
],
named_expressions: {
0: "a",
1: "i",
},
body: [
Emit((
start: 2,
end: 3,
)),
Return(
value: Some(2),
),
],
),
], ],
entry_points: [ entry_points: [
( (
@ -2230,5 +2271,81 @@
], ],
), ),
), ),
(
name: "foo",
stage: Vertex,
early_depth_test: None,
workgroup_size: (0, 0, 0),
function: (
name: Some("foo"),
arguments: [
(
name: Some("vi"),
ty: 0,
binding: Some(BuiltIn(VertexIndex)),
),
],
result: Some((
ty: 24,
binding: Some(BuiltIn(Position(
invariant: false,
))),
)),
local_variables: [],
expressions: [
FunctionArgument(0),
Literal(I32(1)),
Literal(I32(2)),
Literal(I32(3)),
Literal(I32(4)),
Literal(I32(5)),
Compose(
ty: 25,
components: [
1,
2,
3,
4,
5,
],
),
Access(
base: 6,
index: 0,
),
Splat(
size: Quad,
value: 7,
),
As(
expr: 8,
kind: Float,
convert: Some(4),
),
],
named_expressions: {
0: "vi",
6: "arr",
7: "value",
},
body: [
Emit((
start: 6,
end: 7,
)),
Emit((
start: 7,
end: 8,
)),
Emit((
start: 8,
end: 10,
)),
Return(
value: Some(9),
),
],
),
),
], ],
) )

View File

@ -133,9 +133,9 @@ void test_matrix_within_array_within_struct_accesses(
} }
float read_from_private( float read_from_private(
thread float& foo_1 thread float& foo_2
) { ) {
float _e1 = foo_1; float _e1 = foo_2;
return _e1; return _e1;
} }
@ -153,12 +153,19 @@ void assign_through_ptr_fn(
} }
void assign_array_through_ptr_fn( void assign_array_through_ptr_fn(
thread type_22& foo_2 thread type_22& foo_3
) { ) {
foo_2 = type_22 {metal::float4(1.0), metal::float4(2.0)}; foo_3 = type_22 {metal::float4(1.0), metal::float4(2.0)};
return; return;
} }
int array_by_value(
type_20 a_1,
int i
) {
return a_1.inner[i];
}
struct foo_vertInput { struct foo_vertInput {
}; };
struct foo_vertOutput { struct foo_vertOutput {
@ -181,10 +188,10 @@ vertex foo_vertOutput foo_vert(
metal::float4x3 _matrix = bar._matrix; metal::float4x3 _matrix = bar._matrix;
type_10 arr_1 = bar.arr; type_10 arr_1 = bar.arr;
float b = bar._matrix[3u].x; float b = bar._matrix[3u].x;
int a_1 = bar.data[(1 + (_buffer_sizes.size1 - 160 - 8) / 8) - 2u].value; int a_2 = bar.data[(1 + (_buffer_sizes.size1 - 160 - 8) / 8) - 2u].value;
metal::int2 c = qux; metal::int2 c = qux;
float _e33 = read_from_private(foo); float _e33 = read_from_private(foo);
c2_ = type_20 {a_1, static_cast<int>(b), 3, 4, 5}; c2_ = type_20 {a_2, static_cast<int>(b), 3, 4, 5};
c2_.inner[vi + 1u] = 42; c2_.inner[vi + 1u] = 42;
int value = c2_.inner[vi]; int value = c2_.inner[vi];
float _e47 = test_arr_as_arg(type_18 {}); float _e47 = test_arr_as_arg(type_18 {});
@ -217,3 +224,17 @@ kernel void assign_through_ptr(
assign_array_through_ptr_fn(arr); assign_array_through_ptr_fn(arr);
return; return;
} }
struct foo_1Input {
};
struct foo_1Output {
metal::float4 member_3 [[position]];
};
vertex foo_1Output foo_1(
uint vi_1 [[vertex_id]]
) {
type_20 arr_2 = type_20 {1, 2, 3, 4, 5};
int value_1 = arr_2.inner[vi_1];
return foo_1Output { static_cast<metal::float4>(metal::int4(value_1)) };
}

View File

@ -1,16 +1,17 @@
; SPIR-V ; SPIR-V
; Version: 1.1 ; Version: 1.1
; Generator: rspirv ; Generator: rspirv
; Bound: 301 ; Bound: 323
OpCapability Shader OpCapability Shader
OpExtension "SPV_KHR_storage_buffer_storage_class" OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450" %1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450 OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %219 "foo_vert" %214 %217 OpEntryPoint Vertex %229 "foo_vert" %224 %227
OpEntryPoint Fragment %273 "foo_frag" %272 OpEntryPoint Fragment %282 "foo_frag" %281
OpEntryPoint GLCompute %291 "assign_through_ptr" OpEntryPoint GLCompute %300 "assign_through_ptr"
OpExecutionMode %273 OriginUpperLeft OpEntryPoint Vertex %314 "foo" %311 %313
OpExecutionMode %291 LocalSize 1 1 1 OpExecutionMode %282 OriginUpperLeft
OpExecutionMode %300 LocalSize 1 1 1
OpMemberName %6 0 "a" OpMemberName %6 0 "a"
OpMemberName %6 1 "b" OpMemberName %6 1 "b"
OpMemberName %6 2 "c" OpMemberName %6 2 "c"
@ -47,14 +48,19 @@ OpName %200 "p"
OpName %201 "assign_through_ptr_fn" OpName %201 "assign_through_ptr_fn"
OpName %206 "foo" OpName %206 "foo"
OpName %207 "assign_array_through_ptr_fn" OpName %207 "assign_array_through_ptr_fn"
OpName %214 "vi" OpName %214 "a"
OpName %219 "foo_vert" OpName %215 "i"
OpName %231 "foo" OpName %216 "array_by_value"
OpName %232 "c2" OpName %224 "vi"
OpName %273 "foo_frag" OpName %229 "foo_vert"
OpName %291 "assign_through_ptr" OpName %241 "foo"
OpName %296 "val" OpName %242 "c2"
OpName %297 "arr" OpName %282 "foo_frag"
OpName %300 "assign_through_ptr"
OpName %305 "val"
OpName %306 "arr"
OpName %311 "vi"
OpName %314 "foo"
OpMemberDecorate %6 0 Offset 0 OpMemberDecorate %6 0 Offset 0
OpMemberDecorate %6 1 Offset 16 OpMemberDecorate %6 1 Offset 16
OpMemberDecorate %6 2 Offset 28 OpMemberDecorate %6 2 Offset 28
@ -99,9 +105,11 @@ OpDecorate %50 DescriptorSet 0
OpDecorate %50 Binding 3 OpDecorate %50 Binding 3
OpDecorate %51 Block OpDecorate %51 Block
OpMemberDecorate %51 0 Offset 0 OpMemberDecorate %51 0 Offset 0
OpDecorate %214 BuiltIn VertexIndex OpDecorate %224 BuiltIn VertexIndex
OpDecorate %217 BuiltIn Position OpDecorate %227 BuiltIn Position
OpDecorate %272 Location 0 OpDecorate %281 Location 0
OpDecorate %311 BuiltIn VertexIndex
OpDecorate %313 BuiltIn Position
%2 = OpTypeVoid %2 = OpTypeVoid
%3 = OpTypeInt 32 0 %3 = OpTypeInt 32 0
%4 = OpTypeVector %3 3 %4 = OpTypeVector %3 3
@ -209,44 +217,49 @@ OpDecorate %272 Location 0
%209 = OpConstantComposite %31 %59 %59 %59 %59 %209 = OpConstantComposite %31 %59 %59 %59 %59
%210 = OpConstantComposite %31 %61 %61 %61 %61 %210 = OpConstantComposite %31 %61 %61 %61 %61
%211 = OpConstantComposite %34 %209 %210 %211 = OpConstantComposite %34 %209 %210
%215 = OpTypePointer Input %3 %217 = OpTypeFunction %5 %32 %5
%214 = OpVariable %215 Input %219 = OpTypePointer Function %32
%218 = OpTypePointer Output %31 %225 = OpTypePointer Input %3
%217 = OpVariable %218 Output %224 = OpVariable %225 Input
%221 = OpTypePointer StorageBuffer %23 %228 = OpTypePointer Output %31
%224 = OpConstant %8 0.0 %227 = OpVariable %228 Output
%225 = OpConstant %3 3 %231 = OpTypePointer StorageBuffer %23
%226 = OpConstant %5 3 %234 = OpConstant %8 0.0
%227 = OpConstant %5 4 %235 = OpConstant %3 3
%228 = OpConstant %5 5 %236 = OpConstant %5 3
%229 = OpConstant %5 42 %237 = OpConstant %5 4
%230 = OpConstantNull %29 %238 = OpConstant %5 5
%233 = OpTypePointer Function %32 %239 = OpConstant %5 42
%234 = OpConstantNull %32 %240 = OpConstantNull %29
%239 = OpTypePointer StorageBuffer %9 %243 = OpConstantNull %32
%242 = OpTypePointer StorageBuffer %18 %248 = OpTypePointer StorageBuffer %9
%243 = OpConstant %3 4 %251 = OpTypePointer StorageBuffer %18
%246 = OpTypePointer StorageBuffer %10 %252 = OpConstant %3 4
%247 = OpTypePointer StorageBuffer %8 %255 = OpTypePointer StorageBuffer %10
%250 = OpTypePointer StorageBuffer %19 %256 = OpTypePointer StorageBuffer %8
%253 = OpTypePointer StorageBuffer %7 %259 = OpTypePointer StorageBuffer %19
%254 = OpTypePointer StorageBuffer %5 %262 = OpTypePointer StorageBuffer %7
%266 = OpTypeVector %5 4 %263 = OpTypePointer StorageBuffer %5
%272 = OpVariable %218 Output %275 = OpTypeVector %5 4
%275 = OpConstantComposite %10 %224 %224 %224 %281 = OpVariable %228 Output
%276 = OpConstantComposite %10 %59 %59 %59 %284 = OpConstantComposite %10 %234 %234 %234
%277 = OpConstantComposite %10 %61 %61 %61 %285 = OpConstantComposite %10 %59 %59 %59
%278 = OpConstantComposite %10 %63 %63 %63 %286 = OpConstantComposite %10 %61 %61 %61
%279 = OpConstantComposite %9 %275 %276 %277 %278 %287 = OpConstantComposite %10 %63 %63 %63
%280 = OpConstantComposite %17 %36 %36 %288 = OpConstantComposite %9 %284 %285 %286 %287
%281 = OpConstantComposite %17 %99 %99 %289 = OpConstantComposite %17 %36 %36
%282 = OpConstantComposite %18 %280 %281 %290 = OpConstantComposite %17 %99 %99
%283 = OpConstantNull %23 %291 = OpConstantComposite %18 %289 %290
%284 = OpConstantComposite %31 %224 %224 %224 %224 %292 = OpConstantNull %23
%292 = OpConstant %3 33 %293 = OpConstantComposite %31 %234 %234 %234 %234
%293 = OpConstantComposite %31 %67 %67 %67 %67 %301 = OpConstant %3 33
%294 = OpConstantComposite %31 %137 %137 %137 %137 %302 = OpConstantComposite %31 %67 %67 %67 %67
%295 = OpConstantComposite %34 %293 %294 %303 = OpConstantComposite %31 %137 %137 %137 %137
%304 = OpConstantComposite %34 %302 %303
%311 = OpVariable %225 Input
%313 = OpVariable %228 Output
%315 = OpConstant %5 2
%316 = OpConstantComposite %32 %58 %315 %236 %237 %238
%54 = OpFunction %2 None %55 %54 = OpFunction %2 None %55
%53 = OpLabel %53 = OpLabel
%82 = OpVariable %83 Function %58 %82 = OpVariable %83 Function %58
@ -390,72 +403,98 @@ OpBranch %212
OpStore %206 %211 OpStore %206 %211
OpReturn OpReturn
OpFunctionEnd OpFunctionEnd
%219 = OpFunction %2 None %55 %216 = OpFunction %5 None %217
%214 = OpFunctionParameter %32
%215 = OpFunctionParameter %5
%213 = OpLabel %213 = OpLabel
%231 = OpVariable %27 Function %224 %220 = OpVariable %219 Function
%232 = OpVariable %233 Function %234 OpBranch %218
%216 = OpLoad %3 %214 %218 = OpLabel
%220 = OpAccessChain %56 %44 %36 OpStore %220 %214
%222 = OpAccessChain %221 %47 %36 %221 = OpAccessChain %83 %220 %215
%223 = OpAccessChain %131 %50 %36 %222 = OpLoad %5 %221
OpBranch %235 OpReturnValue %222
%235 = OpLabel OpFunctionEnd
%236 = OpLoad %8 %231 %229 = OpFunction %2 None %55
OpStore %231 %59 %223 = OpLabel
%237 = OpFunctionCall %2 %54 %241 = OpVariable %27 Function %234
%238 = OpFunctionCall %2 %130 %242 = OpVariable %219 Function %243
%240 = OpAccessChain %239 %42 %36 %226 = OpLoad %3 %224
%241 = OpLoad %9 %240 %230 = OpAccessChain %56 %44 %36
%244 = OpAccessChain %242 %42 %243 %232 = OpAccessChain %231 %47 %36
%245 = OpLoad %18 %244 %233 = OpAccessChain %131 %50 %36
%248 = OpAccessChain %247 %42 %36 %225 %36 OpBranch %244
%249 = OpLoad %8 %248 %244 = OpLabel
%251 = OpArrayLength %3 %42 5 %245 = OpLoad %8 %241
%252 = OpISub %3 %251 %14 OpStore %241 %59
%255 = OpAccessChain %254 %42 %30 %252 %36 %246 = OpFunctionCall %2 %54
%256 = OpLoad %5 %255 %247 = OpFunctionCall %2 %130
%257 = OpLoad %23 %222 %249 = OpAccessChain %248 %42 %36
%258 = OpFunctionCall %8 %188 %231 %250 = OpLoad %9 %249
%259 = OpConvertFToS %5 %249 %253 = OpAccessChain %251 %42 %252
%260 = OpCompositeConstruct %32 %256 %259 %226 %227 %228 %254 = OpLoad %18 %253
OpStore %232 %260 %257 = OpAccessChain %256 %42 %36 %235 %36
%261 = OpIAdd %3 %216 %99 %258 = OpLoad %8 %257
%262 = OpAccessChain %83 %232 %261 %260 = OpArrayLength %3 %42 5
OpStore %262 %229 %261 = OpISub %3 %260 %14
%263 = OpAccessChain %83 %232 %216 %264 = OpAccessChain %263 %42 %30 %261 %36
%264 = OpLoad %5 %263 %265 = OpLoad %5 %264
%265 = OpFunctionCall %8 %194 %230 %266 = OpLoad %23 %232
%267 = OpCompositeConstruct %266 %264 %264 %264 %264 %267 = OpFunctionCall %8 %188 %241
%268 = OpConvertSToF %31 %267 %268 = OpConvertFToS %5 %258
%269 = OpMatrixTimesVector %10 %241 %268 %269 = OpCompositeConstruct %32 %265 %268 %236 %237 %238
%270 = OpCompositeConstruct %31 %269 %61 OpStore %242 %269
OpStore %217 %270 %270 = OpIAdd %3 %226 %99
%271 = OpAccessChain %83 %242 %270
OpStore %271 %239
%272 = OpAccessChain %83 %242 %226
%273 = OpLoad %5 %272
%274 = OpFunctionCall %8 %194 %240
%276 = OpCompositeConstruct %275 %273 %273 %273 %273
%277 = OpConvertSToF %31 %276
%278 = OpMatrixTimesVector %10 %250 %277
%279 = OpCompositeConstruct %31 %278 %61
OpStore %227 %279
OpReturn OpReturn
OpFunctionEnd OpFunctionEnd
%273 = OpFunction %2 None %55 %282 = OpFunction %2 None %55
%271 = OpLabel %280 = OpLabel
%274 = OpAccessChain %221 %47 %36 %283 = OpAccessChain %231 %47 %36
OpBranch %285 OpBranch %294
%285 = OpLabel %294 = OpLabel
%286 = OpAccessChain %247 %42 %36 %99 %14 %295 = OpAccessChain %256 %42 %36 %99 %14
OpStore %286 %59 OpStore %295 %59
%287 = OpAccessChain %239 %42 %36 %296 = OpAccessChain %248 %42 %36
OpStore %287 %279 OpStore %296 %288
%288 = OpAccessChain %242 %42 %243 %297 = OpAccessChain %251 %42 %252
OpStore %288 %282 OpStore %297 %291
%289 = OpAccessChain %254 %42 %30 %99 %36 %298 = OpAccessChain %263 %42 %30 %99 %36
OpStore %289 %58 OpStore %298 %58
OpStore %274 %283 OpStore %283 %292
OpStore %272 %284 OpStore %281 %293
OpReturn OpReturn
OpFunctionEnd OpFunctionEnd
%291 = OpFunction %2 None %55 %300 = OpFunction %2 None %55
%290 = OpLabel %299 = OpLabel
%296 = OpVariable %33 Function %292 %305 = OpVariable %33 Function %301
%297 = OpVariable %35 Function %295 %306 = OpVariable %35 Function %304
OpBranch %298 OpBranch %307
%298 = OpLabel %307 = OpLabel
%299 = OpFunctionCall %2 %201 %296 %308 = OpFunctionCall %2 %201 %305
%300 = OpFunctionCall %2 %207 %297 %309 = OpFunctionCall %2 %207 %306
OpReturn
OpFunctionEnd
%314 = OpFunction %2 None %55
%310 = OpLabel
%318 = OpVariable %219 Function
%312 = OpLoad %3 %311
OpBranch %317
%317 = OpLabel
OpStore %318 %316
%319 = OpAccessChain %83 %318 %312
%320 = OpLoad %5 %319
%321 = OpCompositeConstruct %275 %320 %320 %320 %320
%322 = OpConvertSToF %31 %321
OpStore %313 %322
OpReturn OpReturn
OpFunctionEnd OpFunctionEnd

View File

@ -107,8 +107,8 @@ fn test_matrix_within_array_within_struct_accesses() {
return; return;
} }
fn read_from_private(foo_1: ptr<function, f32>) -> f32 { fn read_from_private(foo_2: ptr<function, f32>) -> f32 {
let _e1 = (*foo_1); let _e1 = (*foo_2);
return _e1; return _e1;
} }
@ -121,11 +121,15 @@ fn assign_through_ptr_fn(p: ptr<function, u32>) {
return; return;
} }
fn assign_array_through_ptr_fn(foo_2: ptr<function, array<vec4<f32>, 2>>) { fn assign_array_through_ptr_fn(foo_3: ptr<function, array<vec4<f32>, 2>>) {
(*foo_2) = array<vec4<f32>, 2>(vec4(1f), vec4(2f)); (*foo_3) = array<vec4<f32>, 2>(vec4(1f), vec4(2f));
return; return;
} }
fn array_by_value(a_1: array<i32, 5>, i: i32) -> i32 {
return a_1[i];
}
@vertex @vertex
fn foo_vert(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4<f32> { fn foo_vert(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4<f32> {
var foo: f32 = 0f; var foo: f32 = 0f;
@ -138,11 +142,11 @@ fn foo_vert(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4<f32> {
let _matrix = bar._matrix; let _matrix = bar._matrix;
let arr_1 = bar.arr; let arr_1 = bar.arr;
let b = bar._matrix[3u][0]; let b = bar._matrix[3u][0];
let a_1 = bar.data[(arrayLength((&bar.data)) - 2u)].value; let a_2 = bar.data[(arrayLength((&bar.data)) - 2u)].value;
let c = qux; let c = qux;
let data_pointer = (&bar.data[0].value); let data_pointer = (&bar.data[0].value);
let _e33 = read_from_private((&foo)); let _e33 = read_from_private((&foo));
c2_ = array<i32, 5>(a_1, i32(b), 3i, 4i, 5i); c2_ = array<i32, 5>(a_2, i32(b), 3i, 4i, 5i);
c2_[(vi + 1u)] = 42i; c2_[(vi + 1u)] = 42i;
let value = c2_[vi]; let value = c2_[vi];
let _e47 = test_arr_as_arg(array<array<f32, 10>, 5>()); let _e47 = test_arr_as_arg(array<array<f32, 10>, 5>());
@ -168,3 +172,10 @@ fn assign_through_ptr() {
assign_array_through_ptr_fn((&arr)); assign_array_through_ptr_fn((&arr));
return; return;
} }
@vertex
fn foo_1(@builtin(vertex_index) vi_1: u32) -> @builtin(position) vec4<f32> {
const arr_2 = array<i32, 5>(1i, 2i, 3i, 4i, 5i);
let value_1 = arr_2[vi_1];
return vec4<f32>(vec4(value_1));
}

View File

@ -1359,11 +1359,6 @@ fn missing_bindings2() {
#[test] #[test]
fn invalid_access() { fn invalid_access() {
check_validation! { check_validation! {
"
fn array_by_value(a: array<i32, 5>, i: i32) -> i32 {
return a[i];
}
",
" "
fn matrix_by_value(m: mat4x4<f32>, i: i32) -> vec4<f32> { fn matrix_by_value(m: mat4x4<f32>, i: i32) -> vec4<f32> {
return m[i]; return m[i];