Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wgsl-in] Constructor improvements #1790

Merged
merged 4 commits into from
Mar 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
649 changes: 649 additions & 0 deletions src/front/wgsl/construction.rs

Large diffs are not rendered by default.

277 changes: 124 additions & 153 deletions src/front/wgsl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Frontend for [WGSL][wgsl] (WebGPU Shading Language).
[wgsl]: https://gpuweb.github.io/gpuweb/wgsl.html
*/

mod construction;
mod conv;
mod lexer;
mod number_literals;
Expand Down Expand Up @@ -124,6 +125,7 @@ pub enum BadFloatError {
#[derive(Clone, Debug)]
pub enum Error<'a> {
Unexpected(TokenSpan<'a>, ExpectedToken<'a>),
UnexpectedComponents(Span),
BadU32(Span, BadIntError),
BadI32(Span, BadIntError),
/// A negative signed integer literal where both signed and unsigned,
Expand All @@ -148,6 +150,7 @@ pub enum Error<'a> {
InvalidResolve(ResolveError),
InvalidForInitializer(Span),
InvalidGatherComponent(Span, i32),
InvalidConstructorComponentType(Span, i32),
ReservedIdentifierPrefix(Span),
UnknownAddressSpace(Span),
UnknownAttribute(Span),
Expand All @@ -162,6 +165,8 @@ pub enum Error<'a> {
ZeroSizeOrAlign(Span),
InconsistentBinding(Span),
UnknownLocalFunction(Span),
TypeNotConstructible(Span),
TypeNotInferrable(Span),
InitializationTypeMismatch(Span, String),
MissingType(Span),
MissingAttribute(&'static str, Span),
Expand Down Expand Up @@ -253,6 +258,11 @@ impl<'a> Error<'a> {
notes: vec![],
}
},
Error::UnexpectedComponents(ref bad_span) => ParseError {
message: "unexpected components".to_string(),
labels: vec![(bad_span.clone(), "unexpected components".into())],
notes: vec![],
},
Error::BadU32(ref bad_span, ref err) => ParseError {
message: format!(
"expected unsigned integer literal, found `{}`",
Expand Down Expand Up @@ -355,6 +365,11 @@ impl<'a> Error<'a> {
labels: vec![(bad_span.clone(), "invalid component".into())],
notes: vec![],
},
Error::InvalidConstructorComponentType(ref bad_span, component) => ParseError {
message: format!("invalid type for constructor component at index [{}]", component),
labels: vec![(bad_span.clone(), "invalid component type".into())],
notes: vec![],
},
Error::ReservedIdentifierPrefix(ref bad_span) => ParseError {
message: format!("Identifier starts with a reserved prefix: '{}'", &source[bad_span.clone()]),
labels: vec![(bad_span.clone(), "invalid identifier".into())],
Expand Down Expand Up @@ -415,6 +430,16 @@ impl<'a> Error<'a> {
labels: vec![(span.clone(), "unknown local function".into())],
notes: vec![],
},
Error::TypeNotConstructible(ref span) => ParseError {
message: format!("type `{}` is not constructible", &source[span.clone()]),
labels: vec![(span.clone(), "type is not constructible".into())],
notes: vec![],
},
Error::TypeNotInferrable(ref span) => ParseError {
message: "type can't be inferred".to_string(),
labels: vec![(span.clone(), "type can't be inferred".into())],
notes: vec![],
},
Error::InitializationTypeMismatch(ref name_span, ref expected_ty) => ParseError {
message: format!("the type of `{}` is expected to be `{}`", &source[name_span.clone()], expected_ty),
labels: vec![(name_span.clone(), format!("definition of `{}`", &source[name_span.clone()]).into())],
Expand Down Expand Up @@ -969,6 +994,98 @@ impl<'a> ExpressionContext<'a, '_, '_> {
expr.handle
}
}

/// Creates a zero value constant of type `ty`
///
/// Returns `None` if the given `ty` is not a constructible type
fn create_zero_value_constant(
&mut self,
ty: Handle<crate::Type>,
) -> Option<Handle<crate::Constant>> {
let inner = match self.types[ty].inner {
crate::TypeInner::Scalar { kind, width } => {
let value = match kind {
crate::ScalarKind::Sint => crate::ScalarValue::Sint(0),
crate::ScalarKind::Uint => crate::ScalarValue::Uint(0),
crate::ScalarKind::Float => crate::ScalarValue::Float(0.),
crate::ScalarKind::Bool => crate::ScalarValue::Bool(false),
};
crate::ConstantInner::Scalar { width, value }
}
crate::TypeInner::Vector { size, kind, width } => {
let scalar_ty = self.types.insert(
crate::Type {
name: None,
inner: crate::TypeInner::Scalar { width, kind },
},
Default::default(),
);
let component = self.create_zero_value_constant(scalar_ty);
crate::ConstantInner::Composite {
ty,
components: (0..size as u8).map(|_| component).collect::<Option<_>>()?,
}
}
crate::TypeInner::Matrix {
columns,
rows,
width,
} => {
let vec_ty = self.types.insert(
crate::Type {
name: None,
inner: crate::TypeInner::Vector {
width,
kind: crate::ScalarKind::Float,
size: rows,
},
},
Default::default(),
);
let component = self.create_zero_value_constant(vec_ty);
crate::ConstantInner::Composite {
ty,
components: (0..columns as u8)
.map(|_| component)
.collect::<Option<_>>()?,
}
}
crate::TypeInner::Array {
base,
size: crate::ArraySize::Constant(size),
..
} => {
let component = self.create_zero_value_constant(base);
crate::ConstantInner::Composite {
ty,
components: (0..self.constants[size].to_array_length().unwrap())
.map(|_| component)
.collect::<Option<_>>()?,
}
}
crate::TypeInner::Struct { ref members, .. } => {
let members = members.clone();
crate::ConstantInner::Composite {
ty,
components: members
.iter()
.map(|member| self.create_zero_value_constant(member.ty))
.collect::<Option<_>>()?,
}
}
_ => return None,
};

let constant = self.constants.fetch_or_append(
crate::Constant {
name: None,
specialization: None,
inner,
},
crate::Span::default(),
);
Some(constant)
}
}

/// A Naga [`Expression`] handle, with WGSL type information.
Expand Down Expand Up @@ -2044,158 +2161,6 @@ impl Parser {
}))
}

/// Expects [`Scope::PrimaryExpr`] scope on top; if returning Some(_), pops it.
fn parse_construction<'a>(
&mut self,
lexer: &mut Lexer<'a>,
type_name: &'a str,
mut ctx: ExpressionContext<'a, '_, '_>,
) -> Result<Option<Handle<crate::Expression>>, Error<'a>> {
assert_eq!(
self.scopes.last().map(|&(ref scope, _)| scope.clone()),
Some(Scope::PrimaryExpr)
);
let ty_resolution = match self.lookup_type.get(type_name) {
Some(&handle) => TypeResolution::Handle(handle),
None => match self.parse_type_decl_impl(
lexer,
TypeAttributes::default(),
type_name,
ctx.types,
ctx.constants,
)? {
Some(inner) => TypeResolution::Value(inner),
None => return Ok(None),
},
};

let mut components = Vec::new();
let (last_component, arguments_span) = lexer.capture_span(|lexer| {
lexer.open_arguments()?;
let mut last_component = self.parse_general_expression(lexer, ctx.reborrow())?;

while lexer.next_argument()? {
components.push(last_component);
last_component = self.parse_general_expression(lexer, ctx.reborrow())?;
}

Ok(last_component)
})?;

// We can't use the `TypeInner` returned by this because
// `resolve_type` borrows context mutably.
// Use it to insert into the right maps,
// and then grab it again immutably.
ctx.resolve_type(last_component)?;

let expr = if components.is_empty()
&& ty_resolution.inner_with(ctx.types).scalar_kind().is_some()
{
match (
ty_resolution.inner_with(ctx.types),
ctx.typifier.get(last_component, ctx.types),
) {
(
&crate::TypeInner::Vector {
size, kind, width, ..
},
&crate::TypeInner::Scalar {
kind: arg_kind,
width: arg_width,
..
},
) if arg_kind == kind && arg_width == width => crate::Expression::Splat {
size,
value: last_component,
},
(
&crate::TypeInner::Scalar { kind, width, .. },
&crate::TypeInner::Scalar { .. },
)
| (
&crate::TypeInner::Vector { kind, width, .. },
&crate::TypeInner::Vector { .. },
) => crate::Expression::As {
expr: last_component,
kind,
convert: Some(width),
},
(&crate::TypeInner::Matrix { width, .. }, &crate::TypeInner::Matrix { .. }) => {
crate::Expression::As {
expr: last_component,
kind: crate::ScalarKind::Float,
convert: Some(width),
}
}
(to_type, from_type) => {
return Err(Error::BadTypeCast {
span: arguments_span,
from_type: from_type.to_wgsl(ctx.types, ctx.constants),
to_type: to_type.to_wgsl(ctx.types, ctx.constants),
});
}
}
} else {
components.push(last_component);
let mut compose_components = Vec::new();

if let (
&crate::TypeInner::Matrix {
rows,
width,
columns,
},
&crate::TypeInner::Scalar {
kind: crate::ScalarKind::Float,
..
},
) = (
ty_resolution.inner_with(ctx.types),
ctx.typifier.get(last_component, ctx.types),
) {
let vec_ty = ctx.types.insert(
crate::Type {
name: None,
inner: crate::TypeInner::Vector {
width,
kind: crate::ScalarKind::Float,
size: rows,
},
},
Default::default(),
);

compose_components.reserve(columns as usize);
for vec_components in components.chunks(rows as usize) {
let handle = ctx.expressions.append(
crate::Expression::Compose {
ty: vec_ty,
components: Vec::from(vec_components),
},
crate::Span::default(),
);
compose_components.push(handle);
}
} else {
compose_components = components;
}

let ty = match ty_resolution {
TypeResolution::Handle(handle) => handle,
TypeResolution::Value(inner) => ctx
.types
.insert(crate::Type { name: None, inner }, Default::default()),
};
crate::Expression::Compose {
ty,
components: compose_components,
}
};

let span = NagaSpan::from(self.pop_scope(lexer));
Ok(Some(ctx.expressions.append(expr, span)))
}

fn parse_const_expression_impl<'a>(
&mut self,
first_token_span: TokenSpan<'a>,
Expand Down Expand Up @@ -2325,7 +2290,13 @@ impl Parser {
TypedExpression::non_reference(expr)
} else {
let _ = lexer.next();
if let Some(expr) = self.parse_construction(lexer, word, ctx.reborrow())? {
if let Some(expr) = construction::parse_construction(
self,
lexer,
word,
span.clone(),
ctx.reborrow(),
)? {
TypedExpression::non_reference(expr)
} else {
return Err(Error::UnknownIdent(span, word));
Expand Down
15 changes: 15 additions & 0 deletions tests/in/operators.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,21 @@ fn constructors() -> f32 {
0.0, 0.0, 0.0, 1.0,
);

// zero value constructors
var _ = bool();
var _ = i32();
var _ = u32();
var _ = f32();
var _ = vec2<u32>();
var _ = mat2x2<f32>();
var _ = array<Foo, 3>();
var _ = Foo();

// constructors that infer their type from their parameters
var _ = vec2(0u);
var _ = mat2x2(vec2(0.), vec2(0.));
var _ = array(0, 1, 2, 3);

return foo.a.x;
}

Expand Down
18 changes: 16 additions & 2 deletions tests/out/glsl/operators.main.Compute.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,25 @@ vec3 bool_cast(vec3 x) {

float constructors() {
Foo foo = Foo(vec4(0.0), 0);
bool unnamed = false;
int unnamed_1 = 0;
uint unnamed_2 = 0u;
float unnamed_3 = 0.0;
uvec2 unnamed_4 = uvec2(0u, 0u);
mat2x2 unnamed_5 = mat2x2(vec2(0.0, 0.0), vec2(0.0, 0.0));
Foo unnamed_6[3] = Foo[3](Foo(vec4(0.0, 0.0, 0.0, 0.0), 0), Foo(vec4(0.0, 0.0, 0.0, 0.0), 0), Foo(vec4(0.0, 0.0, 0.0, 0.0), 0));
Foo unnamed_7 = Foo(vec4(0.0, 0.0, 0.0, 0.0), 0);
uvec2 unnamed_8 = uvec2(0u);
mat2x2 unnamed_9 = mat2x2(0.0);
int unnamed_10[4] = int[4](0, 0, 0, 0);
foo = Foo(vec4(1.0), 1);
mat2x2 mat2comp = mat2x2(vec2(1.0, 0.0), vec2(0.0, 1.0));
mat4x4 mat4comp = mat4x4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));
float _e39 = foo.a.x;
return _e39;
unnamed_8 = uvec2(0u);
unnamed_9 = mat2x2(vec2(0.0), vec2(0.0));
unnamed_10 = int[4](0, 1, 2, 3);
float _e70 = foo.a.x;
return _e70;
}

void modulo() {
Expand Down
Loading