writer/msl: Hoist array and struct initializers

Pull the HLSL transformation out to a standalone transform that can be
used by both HLSL and MSL.

The new E2E tests do not yet pass for MSL because they produce array
assignments, which will be addressed in the next patch.

Fixed: tint:826
Change-Id: Idc27c81ad45e3d4ab96d82663927d2fc1384618e
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/52842
Auto-Submit: James Price <jrprice@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
James Price 2021-06-01 12:08:20 +00:00 committed by Tint LUCI CQ
parent 1934f59427
commit 42220ba1b2
24 changed files with 941 additions and 345 deletions

View File

@ -521,6 +521,8 @@ libtint_source_set("libtint_core_all_src") {
"transform/inline_pointer_lets.h",
"transform/manager.cc",
"transform/manager.h",
"transform/promote_initializers_to_const_var.cc",
"transform/promote_initializers_to_const_var.h",
"transform/renamer.cc",
"transform/renamer.h",
"transform/simplify.cc",

View File

@ -284,6 +284,8 @@ set(TINT_LIB_SRCS
transform/inline_pointer_lets.h
transform/manager.cc
transform/manager.h
transform/promote_initializers_to_const_var.cc
transform/promote_initializers_to_const_var.h
transform/renamer.cc
transform/renamer.h
transform/simplify.cc
@ -815,6 +817,7 @@ if(${TINT_BUILD_TESTS})
transform/external_texture_transform_test.cc
transform/first_index_offset_test.cc
transform/inline_pointer_lets_test.cc
transform/promote_initializers_to_const_var_test.cc
transform/renamer_test.cc
transform/simplify_test.cc
transform/single_entry_point_test.cc

View File

@ -16,19 +16,14 @@
#include <utility>
#include "src/ast/stage_decoration.h"
#include "src/ast/variable_decl_statement.h"
#include "src/program_builder.h"
#include "src/sem/block_statement.h"
#include "src/sem/expression.h"
#include "src/sem/statement.h"
#include "src/sem/variable.h"
#include "src/transform/calculate_array_length.h"
#include "src/transform/canonicalize_entry_point_io.h"
#include "src/transform/decompose_storage_access.h"
#include "src/transform/external_texture_transform.h"
#include "src/transform/inline_pointer_lets.h"
#include "src/transform/manager.h"
#include "src/transform/promote_initializers_to_const_var.h"
#include "src/transform/simplify.h"
namespace tint {
@ -50,6 +45,7 @@ Output Hlsl::Run(const Program* in, const DataMap& data) {
manager.Add<DecomposeStorageAccess>();
manager.Add<CalculateArrayLength>();
manager.Add<ExternalTextureTransform>();
manager.Add<PromoteInitializersToConstVar>();
auto out = manager.Run(in, data);
if (!out.program.IsValid()) {
return out;
@ -57,81 +53,11 @@ Output Hlsl::Run(const Program* in, const DataMap& data) {
ProgramBuilder builder;
CloneContext ctx(&builder, &out.program);
PromoteInitializersToConstVar(ctx);
AddEmptyEntryPoint(ctx);
ctx.Clone();
return Output{Program(std::move(builder))};
}
void Hlsl::PromoteInitializersToConstVar(CloneContext& ctx) const {
// Scan the AST nodes for array and structure initializers which
// need to be promoted to their own constant declaration.
// Note: Correct handling of nested expressions is guaranteed due to the
// depth-first traversal of the ast::Node::Clone() methods:
//
// The inner-most initializers are traversed first, and they are hoisted
// to const variables declared just above the statement of use. The outer
// initializer will then be hoisted, inserting themselves between the
// inner declaration and the statement of use. This pattern applies correctly
// to any nested depth.
//
// Depth-first traversal of the AST is guaranteed because AST nodes are fully
// immutable and require their children to be constructed first so their
// pointer can be passed to the parent's constructor.
for (auto* src_node : ctx.src->ASTNodes().Objects()) {
if (auto* src_init = src_node->As<ast::TypeConstructorExpression>()) {
auto* src_sem_expr = ctx.src->Sem().Get(src_init);
if (!src_sem_expr) {
TINT_ICE(ctx.dst->Diagnostics())
<< "ast::TypeConstructorExpression has no semantic expression node";
continue;
}
auto* src_sem_stmt = src_sem_expr->Stmt();
if (!src_sem_stmt) {
// Expression is outside of a statement. This usually means the
// expression is part of a global (module-scope) constant declaration.
// These must be constexpr, and so cannot contain the type of
// expressions that must be sanitized.
continue;
}
auto* src_stmt = src_sem_stmt->Declaration();
if (auto* src_var_decl = src_stmt->As<ast::VariableDeclStatement>()) {
if (src_var_decl->variable()->constructor() == src_init) {
// This statement is just a variable declaration with the initializer
// as the constructor value. This is what we're attempting to
// transform to, and so ignore.
continue;
}
}
auto* src_ty = src_sem_expr->Type();
if (src_ty->IsAnyOf<sem::Array, sem::Struct>()) {
// Create a new symbol for the constant
auto dst_symbol = ctx.dst->Sym();
// Clone the type
auto* dst_ty = ctx.Clone(src_init->type());
// Clone the initializer
auto* dst_init = ctx.Clone(src_init);
// Construct the constant that holds the hoisted initializer
auto* dst_var = ctx.dst->Const(dst_symbol, dst_ty, dst_init);
// Construct the variable declaration statement
auto* dst_var_decl = ctx.dst->Decl(dst_var);
// Construct the identifier for referencing the constant
auto* dst_ident = ctx.dst->Expr(dst_symbol);
// Insert the constant before the usage
ctx.InsertBefore(src_sem_stmt->Block()->Declaration()->statements(),
src_stmt, dst_var_decl);
// Replace the inlined initializer with a reference to the constant
ctx.Replace(src_init, dst_ident);
}
}
}
}
void Hlsl::AddEmptyEntryPoint(CloneContext& ctx) const {
for (auto* func : ctx.src->AST().Functions()) {
if (func->IsEntryPoint()) {

View File

@ -40,10 +40,6 @@ class Hlsl : public Transform {
Output Run(const Program* program, const DataMap& data = {}) override;
private:
/// Hoists the array and structure initializers to a constant variable,
/// declared just before the statement of usage. See crbug.com/tint/406 for
/// more details
void PromoteInitializersToConstVar(CloneContext& ctx) const;
/// Add an empty shader entry point if none exist in the module.
void AddEmptyEntryPoint(CloneContext& ctx) const;
};

View File

@ -22,261 +22,6 @@ namespace {
using HlslTest = TransformTest;
TEST_F(HlslTest, PromoteArrayInitializerToConstVar_Basic) {
auto* src = R"(
[[stage(compute)]]
fn main() {
var f0 : f32 = 1.0;
var f1 : f32 = 2.0;
var f2 : f32 = 3.0;
var f3 : f32 = 4.0;
var i : f32 = array<f32, 4>(f0, f1, f2, f3)[2];
}
)";
auto* expect = R"(
[[stage(compute)]]
fn main() {
var f0 : f32 = 1.0;
var f1 : f32 = 2.0;
var f2 : f32 = 3.0;
var f3 : f32 = 4.0;
let tint_symbol : array<f32, 4> = array<f32, 4>(f0, f1, f2, f3);
var i : f32 = tint_symbol[2];
}
)";
auto got = Run<Hlsl>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(HlslTest, PromoteStructureInitializerToConstVar_Basic) {
auto* src = R"(
struct S {
a : i32;
b : f32;
c : vec3<f32>;
};
[[stage(compute)]]
fn main() {
var x : f32 = S(1, 2.0, vec3<f32>()).b;
}
)";
auto* expect = R"(
struct S {
a : i32;
b : f32;
c : vec3<f32>;
};
[[stage(compute)]]
fn main() {
let tint_symbol : S = S(1, 2.0, vec3<f32>());
var x : f32 = tint_symbol.b;
}
)";
auto got = Run<Hlsl>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(HlslTest, PromoteArrayInitializerToConstVar_ArrayInArray) {
auto* src = R"(
[[stage(compute)]]
fn main() {
var i : f32 = array<array<f32, 2>, 2>(array<f32, 2>(1.0, 2.0), array<f32, 2>(3.0, 4.0))[0][1];
}
)";
auto* expect = R"(
[[stage(compute)]]
fn main() {
let tint_symbol : array<f32, 2> = array<f32, 2>(1.0, 2.0);
let tint_symbol_1 : array<f32, 2> = array<f32, 2>(3.0, 4.0);
let tint_symbol_2 : array<array<f32, 2>, 2> = array<array<f32, 2>, 2>(tint_symbol, tint_symbol_1);
var i : f32 = tint_symbol_2[0][1];
}
)";
auto got = Run<Hlsl>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(HlslTest, PromoteStructureInitializerToConstVar_Nested) {
auto* src = R"(
struct S1 {
a : i32;
};
struct S2 {
a : i32;
b : S1;
c : i32;
};
struct S3 {
a : S2;
};
[[stage(compute)]]
fn main() {
var x : i32 = S3(S2(1, S1(2), 3)).a.b.a;
}
)";
auto* expect = R"(
struct S1 {
a : i32;
};
struct S2 {
a : i32;
b : S1;
c : i32;
};
struct S3 {
a : S2;
};
[[stage(compute)]]
fn main() {
let tint_symbol : S1 = S1(2);
let tint_symbol_1 : S2 = S2(1, tint_symbol, 3);
let tint_symbol_2 : S3 = S3(tint_symbol_1);
var x : i32 = tint_symbol_2.a.b.a;
}
)";
auto got = Run<Hlsl>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(HlslTest, PromoteInitializerToConstVar_Mixed) {
auto* src = R"(
struct S1 {
a : i32;
};
struct S2 {
a : array<S1, 3>;
};
[[stage(compute)]]
fn main() {
var x : i32 = S2(array<S1, 3>(S1(1), S1(2), S1(3))).a[1].a;
}
)";
auto* expect = R"(
struct S1 {
a : i32;
};
struct S2 {
a : array<S1, 3>;
};
[[stage(compute)]]
fn main() {
let tint_symbol : S1 = S1(1);
let tint_symbol_1 : S1 = S1(2);
let tint_symbol_2 : S1 = S1(3);
let tint_symbol_3 : array<S1, 3> = array<S1, 3>(tint_symbol, tint_symbol_1, tint_symbol_2);
let tint_symbol_4 : S2 = S2(tint_symbol_3);
var x : i32 = tint_symbol_4.a[1].a;
}
)";
auto got = Run<Hlsl>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(HlslTest, PromoteInitializerToConstVar_NoChangeOnVarDecl) {
auto* src = R"(
struct S {
a : i32;
b : f32;
c : i32;
};
[[stage(compute)]]
fn main() {
var local_arr : array<f32, 4> = array<f32, 4>(0.0, 1.0, 2.0, 3.0);
var local_str : S = S(1, 2.0, 3);
}
let module_arr : array<f32, 4> = array<f32, 4>(0.0, 1.0, 2.0, 3.0);
let module_str : S = S(1, 2.0, 3);
)";
auto* expect = src;
auto got = Run<Hlsl>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(HlslTest, PromoteArrayInitializerToConstVar_Bug406) {
// See crbug.com/tint/406
auto* src = R"(
[[block]]
struct Uniforms {
transform : mat2x2<f32>;
};
[[group(0), binding(0)]] var<uniform> ubo : Uniforms;
[[builtin(vertex_index)]] var<in> vertex_index : u32;
[[builtin(position)]] var<out> position : vec4<f32>;
[[stage(vertex)]]
fn main() {
let transform : mat2x2<f32> = ubo.transform;
var coord : vec2<f32> = array<vec2<f32>, 3>(
vec2<f32>(-1.0, 1.0),
vec2<f32>( 1.0, 1.0),
vec2<f32>(-1.0, -1.0)
)[vertex_index];
position = vec4<f32>(transform * coord, 0.0, 1.0);
}
)";
auto* expect = R"(
[[block]]
struct Uniforms {
transform : mat2x2<f32>;
};
[[group(0), binding(0)]] var<uniform> ubo : Uniforms;
[[builtin(vertex_index)]] var<in> vertex_index : u32;
[[builtin(position)]] var<out> position : vec4<f32>;
[[stage(vertex)]]
fn main() {
let transform : mat2x2<f32> = ubo.transform;
let tint_symbol : array<vec2<f32>, 3> = array<vec2<f32>, 3>(vec2<f32>(-1.0, 1.0), vec2<f32>(1.0, 1.0), vec2<f32>(-1.0, -1.0));
var coord : vec2<f32> = tint_symbol[vertex_index];
position = vec4<f32>((transform * coord), 0.0, 1.0);
}
)";
auto got = Run<Hlsl>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(HlslTest, AddEmptyEntryPoint) {
auto* src = R"()";

View File

@ -27,6 +27,7 @@
#include "src/transform/canonicalize_entry_point_io.h"
#include "src/transform/external_texture_transform.h"
#include "src/transform/manager.h"
#include "src/transform/promote_initializers_to_const_var.h"
namespace tint {
namespace transform {
@ -38,6 +39,7 @@ Output Msl::Run(const Program* in, const DataMap& data) {
Manager manager;
manager.Add<CanonicalizeEntryPointIO>();
manager.Add<ExternalTextureTransform>();
manager.Add<PromoteInitializersToConstVar>();
auto out = manager.Run(in, data);
if (!out.program.IsValid()) {
return out;

View File

@ -0,0 +1,108 @@
// Copyright 2021 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/transform/promote_initializers_to_const_var.h"
#include <utility>
#include "src/program_builder.h"
#include "src/sem/block_statement.h"
#include "src/sem/expression.h"
#include "src/sem/statement.h"
namespace tint {
namespace transform {
PromoteInitializersToConstVar::PromoteInitializersToConstVar() = default;
PromoteInitializersToConstVar::~PromoteInitializersToConstVar() = default;
Output PromoteInitializersToConstVar::Run(const Program* in, const DataMap&) {
ProgramBuilder out;
CloneContext ctx(&out, in);
// Scan the AST nodes for array and structure initializers which
// need to be promoted to their own constant declaration.
// Note: Correct handling of nested expressions is guaranteed due to the
// depth-first traversal of the ast::Node::Clone() methods:
//
// The inner-most initializers are traversed first, and they are hoisted
// to const variables declared just above the statement of use. The outer
// initializer will then be hoisted, inserting themselves between the
// inner declaration and the statement of use. This pattern applies correctly
// to any nested depth.
//
// Depth-first traversal of the AST is guaranteed because AST nodes are fully
// immutable and require their children to be constructed first so their
// pointer can be passed to the parent's constructor.
for (auto* src_node : ctx.src->ASTNodes().Objects()) {
if (auto* src_init = src_node->As<ast::TypeConstructorExpression>()) {
auto* src_sem_expr = ctx.src->Sem().Get(src_init);
if (!src_sem_expr) {
TINT_ICE(ctx.dst->Diagnostics())
<< "ast::TypeConstructorExpression has no semantic expression node";
continue;
}
auto* src_sem_stmt = src_sem_expr->Stmt();
if (!src_sem_stmt) {
// Expression is outside of a statement. This usually means the
// expression is part of a global (module-scope) constant declaration.
// These must be constexpr, and so cannot contain the type of
// expressions that must be sanitized.
continue;
}
auto* src_stmt = src_sem_stmt->Declaration();
if (auto* src_var_decl = src_stmt->As<ast::VariableDeclStatement>()) {
if (src_var_decl->variable()->constructor() == src_init) {
// This statement is just a variable declaration with the initializer
// as the constructor value. This is what we're attempting to
// transform to, and so ignore.
continue;
}
}
auto* src_ty = src_sem_expr->Type();
if (src_ty->IsAnyOf<sem::Array, sem::Struct>()) {
// Create a new symbol for the constant
auto dst_symbol = ctx.dst->Sym();
// Clone the type
auto* dst_ty = ctx.Clone(src_init->type());
// Clone the initializer
auto* dst_init = ctx.Clone(src_init);
// Construct the constant that holds the hoisted initializer
auto* dst_var = ctx.dst->Const(dst_symbol, dst_ty, dst_init);
// Construct the variable declaration statement
auto* dst_var_decl = ctx.dst->Decl(dst_var);
// Construct the identifier for referencing the constant
auto* dst_ident = ctx.dst->Expr(dst_symbol);
// Insert the constant before the usage
ctx.InsertBefore(src_sem_stmt->Block()->Declaration()->statements(),
src_stmt, dst_var_decl);
// Replace the inlined initializer with a reference to the constant
ctx.Replace(src_init, dst_ident);
}
}
}
ctx.Clone();
return Output(Program(std::move(out)));
}
} // namespace transform
} // namespace tint

View File

@ -0,0 +1,44 @@
// Copyright 2021 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_TRANSFORM_PROMOTE_INITIALIZERS_TO_CONST_VAR_H_
#define SRC_TRANSFORM_PROMOTE_INITIALIZERS_TO_CONST_VAR_H_
#include "src/transform/transform.h"
namespace tint {
namespace transform {
/// A transform that hoists the array and structure initializers to a constant
/// variable, declared just before the statement of usage. See
/// crbug.com/tint/406 for more details.
class PromoteInitializersToConstVar : public Transform {
public:
/// Constructor
PromoteInitializersToConstVar();
/// Destructor
~PromoteInitializersToConstVar() override;
/// Runs the transform on `program`, returning the transformation result.
/// @param program the source program to transform
/// @param data optional extra transform-specific input data
/// @returns the transformation result
Output Run(const Program* program, const DataMap& data = {}) override;
};
} // namespace transform
} // namespace tint
#endif // SRC_TRANSFORM_PROMOTE_INITIALIZERS_TO_CONST_VAR_H_

View File

@ -0,0 +1,291 @@
// Copyright 2021 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/transform/promote_initializers_to_const_var.h"
#include "src/transform/test_helper.h"
namespace tint {
namespace transform {
namespace {
using PromoteInitializersToConstVarTest = TransformTest;
TEST_F(PromoteInitializersToConstVarTest, BasicArray) {
auto* src = R"(
[[stage(compute)]]
fn main() {
var f0 : f32 = 1.0;
var f1 : f32 = 2.0;
var f2 : f32 = 3.0;
var f3 : f32 = 4.0;
var i : f32 = array<f32, 4>(f0, f1, f2, f3)[2];
}
)";
auto* expect = R"(
[[stage(compute)]]
fn main() {
var f0 : f32 = 1.0;
var f1 : f32 = 2.0;
var f2 : f32 = 3.0;
var f3 : f32 = 4.0;
let tint_symbol : array<f32, 4> = array<f32, 4>(f0, f1, f2, f3);
var i : f32 = tint_symbol[2];
}
)";
auto got = Run<PromoteInitializersToConstVar>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(PromoteInitializersToConstVarTest, BasicStruct) {
auto* src = R"(
struct S {
a : i32;
b : f32;
c : vec3<f32>;
};
[[stage(compute)]]
fn main() {
var x : f32 = S(1, 2.0, vec3<f32>()).b;
}
)";
auto* expect = R"(
struct S {
a : i32;
b : f32;
c : vec3<f32>;
};
[[stage(compute)]]
fn main() {
let tint_symbol : S = S(1, 2.0, vec3<f32>());
var x : f32 = tint_symbol.b;
}
)";
auto got = Run<PromoteInitializersToConstVar>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(PromoteInitializersToConstVarTest, ArrayInArrayArray) {
auto* src = R"(
[[stage(compute)]]
fn main() {
var i : f32 = array<array<f32, 2>, 2>(array<f32, 2>(1.0, 2.0), array<f32, 2>(3.0, 4.0))[0][1];
}
)";
auto* expect = R"(
[[stage(compute)]]
fn main() {
let tint_symbol : array<f32, 2> = array<f32, 2>(1.0, 2.0);
let tint_symbol_1 : array<f32, 2> = array<f32, 2>(3.0, 4.0);
let tint_symbol_2 : array<array<f32, 2>, 2> = array<array<f32, 2>, 2>(tint_symbol, tint_symbol_1);
var i : f32 = tint_symbol_2[0][1];
}
)";
auto got = Run<PromoteInitializersToConstVar>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(PromoteInitializersToConstVarTest, StructNested) {
auto* src = R"(
struct S1 {
a : i32;
};
struct S2 {
a : i32;
b : S1;
c : i32;
};
struct S3 {
a : S2;
};
[[stage(compute)]]
fn main() {
var x : i32 = S3(S2(1, S1(2), 3)).a.b.a;
}
)";
auto* expect = R"(
struct S1 {
a : i32;
};
struct S2 {
a : i32;
b : S1;
c : i32;
};
struct S3 {
a : S2;
};
[[stage(compute)]]
fn main() {
let tint_symbol : S1 = S1(2);
let tint_symbol_1 : S2 = S2(1, tint_symbol, 3);
let tint_symbol_2 : S3 = S3(tint_symbol_1);
var x : i32 = tint_symbol_2.a.b.a;
}
)";
auto got = Run<PromoteInitializersToConstVar>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(PromoteInitializersToConstVarTest, Mixed) {
auto* src = R"(
struct S1 {
a : i32;
};
struct S2 {
a : array<S1, 3>;
};
[[stage(compute)]]
fn main() {
var x : i32 = S2(array<S1, 3>(S1(1), S1(2), S1(3))).a[1].a;
}
)";
auto* expect = R"(
struct S1 {
a : i32;
};
struct S2 {
a : array<S1, 3>;
};
[[stage(compute)]]
fn main() {
let tint_symbol : S1 = S1(1);
let tint_symbol_1 : S1 = S1(2);
let tint_symbol_2 : S1 = S1(3);
let tint_symbol_3 : array<S1, 3> = array<S1, 3>(tint_symbol, tint_symbol_1, tint_symbol_2);
let tint_symbol_4 : S2 = S2(tint_symbol_3);
var x : i32 = tint_symbol_4.a[1].a;
}
)";
auto got = Run<PromoteInitializersToConstVar>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(PromoteInitializersToConstVarTest, NoChangeOnVarDecl) {
auto* src = R"(
struct S {
a : i32;
b : f32;
c : i32;
};
[[stage(compute)]]
fn main() {
var local_arr : array<f32, 4> = array<f32, 4>(0.0, 1.0, 2.0, 3.0);
var local_str : S = S(1, 2.0, 3);
}
let module_arr : array<f32, 4> = array<f32, 4>(0.0, 1.0, 2.0, 3.0);
let module_str : S = S(1, 2.0, 3);
)";
auto* expect = src;
auto got = Run<PromoteInitializersToConstVar>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(PromoteInitializersToConstVarTest, Bug406Array) {
// See crbug.com/tint/406
auto* src = R"(
[[block]]
struct Uniforms {
transform : mat2x2<f32>;
};
[[group(0), binding(0)]] var<uniform> ubo : Uniforms;
[[builtin(vertex_index)]] var<in> vertex_index : u32;
[[builtin(position)]] var<out> position : vec4<f32>;
[[stage(vertex)]]
fn main() {
let transform : mat2x2<f32> = ubo.transform;
var coord : vec2<f32> = array<vec2<f32>, 3>(
vec2<f32>(-1.0, 1.0),
vec2<f32>( 1.0, 1.0),
vec2<f32>(-1.0, -1.0)
)[vertex_index];
position = vec4<f32>(transform * coord, 0.0, 1.0);
}
)";
auto* expect = R"(
[[block]]
struct Uniforms {
transform : mat2x2<f32>;
};
[[group(0), binding(0)]] var<uniform> ubo : Uniforms;
[[builtin(vertex_index)]] var<in> vertex_index : u32;
[[builtin(position)]] var<out> position : vec4<f32>;
[[stage(vertex)]]
fn main() {
let transform : mat2x2<f32> = ubo.transform;
let tint_symbol : array<vec2<f32>, 3> = array<vec2<f32>, 3>(vec2<f32>(-1.0, 1.0), vec2<f32>(1.0, 1.0), vec2<f32>(-1.0, -1.0));
var coord : vec2<f32> = tint_symbol[vertex_index];
position = vec4<f32>((transform * coord), 0.0, 1.0);
}
)";
auto got = Run<PromoteInitializersToConstVar>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(PromoteInitializersToConstVarTest, EmptyModule) {
auto* src = "";
auto* expect = "";
auto got = Run<PromoteInitializersToConstVar>(src);
EXPECT_EQ(expect, str(got));
}
} // namespace
} // namespace transform
} // namespace tint

View File

@ -113,7 +113,8 @@ struct tint_symbol_2 {
fragment tint_symbol_2 frag_main(tint_symbol_1 tint_symbol [[stage_in]]) {
float const foo = tint_symbol.foo;
return {foo};
tint_symbol_2 const tint_symbol_3 = {foo};
return tint_symbol_3;
}
)");
@ -147,7 +148,8 @@ struct tint_symbol_2 {
fragment tint_symbol_2 frag_main(tint_symbol_1 tint_symbol [[stage_in]]) {
float4 const coord = tint_symbol.coord;
return {coord.x};
tint_symbol_2 const tint_symbol_3 = {coord.x};
return tint_symbol_3;
}
)");
@ -215,7 +217,8 @@ struct tint_symbol_3 {
vertex tint_symbol vert_main() {
Interface const tint_symbol_1 = {0.5f, 0.25f, float4()};
return {tint_symbol_1.col1, tint_symbol_1.col2, tint_symbol_1.pos};
tint_symbol const tint_symbol_4 = {tint_symbol_1.col1, tint_symbol_1.col2, tint_symbol_1.pos};
return tint_symbol_4;
}
fragment void frag_main(tint_symbol_3 tint_symbol_2 [[stage_in]]) {
@ -279,17 +282,20 @@ struct tint_symbol_2 {
};
VertexOutput foo(float x) {
return {float4(x, x, x, 1.0f)};
VertexOutput const tint_symbol_4 = {float4(x, x, x, 1.0f)};
return tint_symbol_4;
}
vertex tint_symbol vert_main1() {
VertexOutput const tint_symbol_1 = {foo(0.5f)};
return {tint_symbol_1.pos};
tint_symbol const tint_symbol_5 = {tint_symbol_1.pos};
return tint_symbol_5;
}
vertex tint_symbol_2 vert_main2() {
VertexOutput const tint_symbol_3 = {foo(0.25f)};
return {tint_symbol_3.pos};
tint_symbol_2 const tint_symbol_6 = {tint_symbol_3.pos};
return tint_symbol_6;
}
)");

View File

@ -303,6 +303,7 @@ tint_unittests_source_set("tint_unittests_core_src") {
"../src/transform/external_texture_transform_test.cc",
"../src/transform/first_index_offset_test.cc",
"../src/transform/inline_pointer_lets_test.cc",
"../src/transform/promote_initializers_to_const_var_test.cc",
"../src/transform/renamer_test.cc",
"../src/transform/simplify_test.cc",
"../src/transform/single_entry_point_test.cc",

View File

@ -0,0 +1,48 @@
[[stage(compute)]]
fn main() {
let x : i32 = 42;
// Test basic usage.
let empty : array<i32, 4> = array<i32, 4>();
let nonempty : array<i32, 4> = array<i32, 4>(1, 2, 3, 4);
let nonempty_with_expr : array<i32, 4> =
array<i32, 4>(1, x, x + 1, nonempty[3]);
// Test nested arrays.
let nested_empty : array<array<array<i32, 4>, 3>, 2> =
array<array<array<i32, 4>, 3>, 2>();
let nested_nonempty : array<array<array<i32, 4>, 3>, 2> =
array<array<array<i32, 4>, 3>, 2>(
array<array<i32, 4>, 3>(
array<i32, 4>(1, 2, 3, 4),
array<i32, 4>(5, 6, 7, 8),
array<i32, 4>(9, 10, 11, 12)),
array<array<i32, 4>, 3>(
array<i32, 4>(13, 14, 15, 16),
array<i32, 4>(17, 18, 19, 20),
array<i32, 4>(21, 22, 23, 24)));
let nested_nonempty_with_expr : array<array<array<i32, 4>, 3>, 2> =
array<array<array<i32, 4>, 3>, 2>(
array<array<i32, 4>, 3>(
array<i32, 4>(1, 2, x, x + 1),
array<i32, 4>(5, 6, nonempty[2], nonempty[3] + 1),
nonempty),
nested_nonempty[1]);
// Test use of constructors as sub-expressions.
let subexpr_empty : i32 = array<i32, 4>()[1];
let subexpr_nonempty : i32 = array<i32, 4>(1, 2, 3, 4)[2];
let subexpr_nonempty_with_expr : i32 =
array<i32, 4>(1, x, x + 1, nonempty[3])[2];
let subexpr_nested_empty : array<i32, 4> = array<array<i32, 4>, 2>()[1];
let subexpr_nested_nonempty : array<i32, 4> =
array<array<i32, 4>, 2>(
array<i32, 4>(1, 2, 3, 4),
array<i32, 4>(5, 6, 7, 8)
)[1];
let subexpr_nested_nonempty_with_expr : array<i32, 4> =
array<array<i32, 4>, 2>(
array<i32, 4>(1, x, x + 1, nonempty[3]),
nested_nonempty[1][2],
)[1];
}

View File

@ -0,0 +1,38 @@
[numthreads(1, 1, 1)]
void main() {
const int x = 42;
const int empty[4] = {0, 0, 0, 0};
const int nonempty[4] = {1, 2, 3, 4};
const int nonempty_with_expr[4] = {1, x, (x + 1), nonempty[3]};
const int nested_empty[2][3][4] = {{{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}};
const int tint_symbol[4] = {1, 2, 3, 4};
const int tint_symbol_1[4] = {5, 6, 7, 8};
const int tint_symbol_2[4] = {9, 10, 11, 12};
const int tint_symbol_3[3][4] = {tint_symbol, tint_symbol_1, tint_symbol_2};
const int tint_symbol_4[4] = {13, 14, 15, 16};
const int tint_symbol_5[4] = {17, 18, 19, 20};
const int tint_symbol_6[4] = {21, 22, 23, 24};
const int tint_symbol_7[3][4] = {tint_symbol_4, tint_symbol_5, tint_symbol_6};
const int nested_nonempty[2][3][4] = {tint_symbol_3, tint_symbol_7};
const int tint_symbol_8[4] = {1, 2, x, (x + 1)};
const int tint_symbol_9[4] = {5, 6, nonempty[2], (nonempty[3] + 1)};
const int tint_symbol_10[3][4] = {tint_symbol_8, tint_symbol_9, nonempty};
const int nested_nonempty_with_expr[2][3][4] = {tint_symbol_10, nested_nonempty[1]};
const int tint_symbol_11[4] = {0, 0, 0, 0};
const int subexpr_empty = tint_symbol_11[1];
const int tint_symbol_12[4] = {1, 2, 3, 4};
const int subexpr_nonempty = tint_symbol_12[2];
const int tint_symbol_13[4] = {1, x, (x + 1), nonempty[3]};
const int subexpr_nonempty_with_expr = tint_symbol_13[2];
const int tint_symbol_14[2][4] = {{0, 0, 0, 0}, {0, 0, 0, 0}};
const int subexpr_nested_empty[4] = tint_symbol_14[1];
const int tint_symbol_15[4] = {1, 2, 3, 4};
const int tint_symbol_16[4] = {5, 6, 7, 8};
const int tint_symbol_17[2][4] = {tint_symbol_15, tint_symbol_16};
const int subexpr_nested_nonempty[4] = tint_symbol_17[1];
const int tint_symbol_18[4] = {1, x, (x + 1), nonempty[3]};
const int tint_symbol_19[2][4] = {tint_symbol_18, nested_nonempty[1][2]};
const int subexpr_nested_nonempty_with_expr[4] = tint_symbol_19[1];
return;
}

View File

@ -0,0 +1 @@
SKIP: crbug.com/tint/814

View File

@ -0,0 +1,94 @@
; SPIR-V
; Version: 1.3
; Generator: Google Tint Compiler; 0
; Bound: 79
; Schema: 0
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpName %main "main"
OpDecorate %_arr_int_uint_4 ArrayStride 4
OpDecorate %_arr__arr_int_uint_4_uint_3 ArrayStride 16
OpDecorate %_arr__arr__arr_int_uint_4_uint_3_uint_2 ArrayStride 48
OpDecorate %_arr__arr_int_uint_4_uint_2 ArrayStride 16
%void = OpTypeVoid
%1 = OpTypeFunction %void
%int = OpTypeInt 32 1
%int_42 = OpConstant %int 42
%uint = OpTypeInt 32 0
%uint_4 = OpConstant %uint 4
%_arr_int_uint_4 = OpTypeArray %int %uint_4
%10 = OpConstantNull %_arr_int_uint_4
%int_1 = OpConstant %int 1
%int_2 = OpConstant %int 2
%int_3 = OpConstant %int 3
%int_4 = OpConstant %int 4
%15 = OpConstantComposite %_arr_int_uint_4 %int_1 %int_2 %int_3 %int_4
%uint_3 = OpConstant %uint 3
%_arr__arr_int_uint_4_uint_3 = OpTypeArray %_arr_int_uint_4 %uint_3
%uint_2 = OpConstant %uint 2
%_arr__arr__arr_int_uint_4_uint_3_uint_2 = OpTypeArray %_arr__arr_int_uint_4_uint_3 %uint_2
%23 = OpConstantNull %_arr__arr__arr_int_uint_4_uint_3_uint_2
%int_5 = OpConstant %int 5
%int_6 = OpConstant %int 6
%int_7 = OpConstant %int 7
%int_8 = OpConstant %int 8
%28 = OpConstantComposite %_arr_int_uint_4 %int_5 %int_6 %int_7 %int_8
%int_9 = OpConstant %int 9
%int_10 = OpConstant %int 10
%int_11 = OpConstant %int 11
%int_12 = OpConstant %int 12
%33 = OpConstantComposite %_arr_int_uint_4 %int_9 %int_10 %int_11 %int_12
%34 = OpConstantComposite %_arr__arr_int_uint_4_uint_3 %15 %28 %33
%int_13 = OpConstant %int 13
%int_14 = OpConstant %int 14
%int_15 = OpConstant %int 15
%int_16 = OpConstant %int 16
%39 = OpConstantComposite %_arr_int_uint_4 %int_13 %int_14 %int_15 %int_16
%int_17 = OpConstant %int 17
%int_18 = OpConstant %int 18
%int_19 = OpConstant %int 19
%int_20 = OpConstant %int 20
%44 = OpConstantComposite %_arr_int_uint_4 %int_17 %int_18 %int_19 %int_20
%int_21 = OpConstant %int 21
%int_22 = OpConstant %int 22
%int_23 = OpConstant %int 23
%int_24 = OpConstant %int 24
%49 = OpConstantComposite %_arr_int_uint_4 %int_21 %int_22 %int_23 %int_24
%50 = OpConstantComposite %_arr__arr_int_uint_4_uint_3 %39 %44 %49
%51 = OpConstantComposite %_arr__arr__arr_int_uint_4_uint_3_uint_2 %34 %50
%_arr__arr_int_uint_4_uint_2 = OpTypeArray %_arr_int_uint_4 %uint_2
%68 = OpConstantNull %_arr__arr_int_uint_4_uint_2
%70 = OpConstantComposite %_arr__arr_int_uint_4_uint_2 %15 %28
%main = OpFunction %void None %1
%4 = OpLabel
%16 = OpIAdd %int %int_42 %int_1
%17 = OpCompositeExtract %int %15 3
%18 = OpCompositeConstruct %_arr_int_uint_4 %int_1 %int_42 %16 %17
%52 = OpIAdd %int %int_42 %int_1
%53 = OpCompositeConstruct %_arr_int_uint_4 %int_1 %int_2 %int_42 %52
%54 = OpCompositeExtract %int %15 2
%55 = OpCompositeExtract %int %15 3
%56 = OpIAdd %int %55 %int_1
%57 = OpCompositeConstruct %_arr_int_uint_4 %int_5 %int_6 %54 %56
%58 = OpCompositeConstruct %_arr__arr_int_uint_4_uint_3 %53 %57 %15
%59 = OpCompositeExtract %_arr__arr_int_uint_4_uint_3 %51 1
%60 = OpCompositeConstruct %_arr__arr__arr_int_uint_4_uint_3_uint_2 %58 %59
%61 = OpCompositeExtract %int %10 1
%62 = OpCompositeExtract %int %15 2
%63 = OpIAdd %int %int_42 %int_1
%64 = OpCompositeExtract %int %15 3
%65 = OpCompositeConstruct %_arr_int_uint_4 %int_1 %int_42 %63 %64
%66 = OpCompositeExtract %int %65 2
%69 = OpCompositeExtract %_arr_int_uint_4 %68 1
%71 = OpCompositeExtract %_arr_int_uint_4 %70 1
%72 = OpIAdd %int %int_42 %int_1
%73 = OpCompositeExtract %int %15 3
%74 = OpCompositeConstruct %_arr_int_uint_4 %int_1 %int_42 %72 %73
%75 = OpCompositeExtract %_arr__arr_int_uint_4_uint_3 %51 1
%76 = OpCompositeExtract %_arr_int_uint_4 %75 2
%77 = OpCompositeConstruct %_arr__arr_int_uint_4_uint_2 %74 %76
%78 = OpCompositeExtract %_arr_int_uint_4 %77 1
OpReturn
OpFunctionEnd

View File

@ -0,0 +1,16 @@
[[stage(compute)]]
fn main() {
let x : i32 = 42;
let empty : array<i32, 4> = array<i32, 4>();
let nonempty : array<i32, 4> = array<i32, 4>(1, 2, 3, 4);
let nonempty_with_expr : array<i32, 4> = array<i32, 4>(1, x, (x + 1), nonempty[3]);
let nested_empty : array<array<array<i32, 4>, 3>, 2> = array<array<array<i32, 4>, 3>, 2>();
let nested_nonempty : array<array<array<i32, 4>, 3>, 2> = array<array<array<i32, 4>, 3>, 2>(array<array<i32, 4>, 3>(array<i32, 4>(1, 2, 3, 4), array<i32, 4>(5, 6, 7, 8), array<i32, 4>(9, 10, 11, 12)), array<array<i32, 4>, 3>(array<i32, 4>(13, 14, 15, 16), array<i32, 4>(17, 18, 19, 20), array<i32, 4>(21, 22, 23, 24)));
let nested_nonempty_with_expr : array<array<array<i32, 4>, 3>, 2> = array<array<array<i32, 4>, 3>, 2>(array<array<i32, 4>, 3>(array<i32, 4>(1, 2, x, (x + 1)), array<i32, 4>(5, 6, nonempty[2], (nonempty[3] + 1)), nonempty), nested_nonempty[1]);
let subexpr_empty : i32 = array<i32, 4>()[1];
let subexpr_nonempty : i32 = array<i32, 4>(1, 2, 3, 4)[2];
let subexpr_nonempty_with_expr : i32 = array<i32, 4>(1, x, (x + 1), nonempty[3])[2];
let subexpr_nested_empty : array<i32, 4> = array<array<i32, 4>, 2>()[1];
let subexpr_nested_nonempty : array<i32, 4> = array<array<i32, 4>, 2>(array<i32, 4>(1, 2, 3, 4), array<i32, 4>(5, 6, 7, 8))[1];
let subexpr_nested_nonempty_with_expr : array<i32, 4> = array<array<i32, 4>, 2>(array<i32, 4>(1, x, (x + 1), nonempty[3]), nested_nonempty[1][2])[1];
}

View File

@ -81,6 +81,7 @@ void matrix_matrix_f32() {
}
fragment tint_symbol_1 tint_symbol() {
return {float4(0.0f, 0.0f, 0.0f, 0.0f)};
tint_symbol_1 const tint_symbol_2 = {float4(0.0f, 0.0f, 0.0f, 0.0f)};
return tint_symbol_2;
}

View File

@ -32,11 +32,13 @@ vertex tint_symbol_2 vtx_main(tint_symbol_1 tint_symbol [[stage_in]], constant U
VertexOutput output = {};
output.Position = (uniforms.modelViewProjectionMatrix * input.cur_position);
output.vtxFragColor = input.color;
return {output.vtxFragColor, output.Position};
tint_symbol_2 const tint_symbol_6 = {output.vtxFragColor, output.Position};
return tint_symbol_6;
}
fragment tint_symbol_5 frag_main(tint_symbol_4 tint_symbol_3 [[stage_in]]) {
float4 const fragColor = tint_symbol_3.fragColor;
return {fragColor};
tint_symbol_5 const tint_symbol_7 = {fragColor};
return tint_symbol_7;
}

View File

@ -11,6 +11,7 @@ void bar() {
fragment tint_symbol_1 tint_symbol() {
float2 a = float2();
bar();
return {float4(0.400000006f, 0.400000006f, 0.800000012f, 1.0f)};
tint_symbol_1 const tint_symbol_2 = {float4(0.400000006f, 0.400000006f, 0.800000012f, 1.0f)};
return tint_symbol_2;
}

View File

@ -0,0 +1,59 @@
struct S1 {
a : i32;
b : i32;
c : i32;
d : i32;
};
struct S2 {
e : i32;
f : S1;
};
struct S3 {
g : i32;
h : S1;
i : S2;
};
struct T {
a : array<i32, 2>;
};
[[stage(compute)]]
fn main() {
let x : i32 = 42;
// Test basic usage.
let empty : S1 = S1();
let nonempty : S1 = S1(1, 2, 3, 4);
let nonempty_with_expr : S1 = S1(1, x, x + 1, nonempty.d);
// Test nested structs.
let nested_empty : S3 = S3();
let nested_nonempty : S3 = S3(1, S1(2, 3, 4, 5), S2(6, S1(7, 8, 9, 10)));
let nested_nonempty_with_expr : S3 =
S3(1, S1(2, x, x + 1, nested_nonempty.i.f.d), S2(6, nonempty));
// Test use of constructors as sub-expressions.
let subexpr_empty : i32 = S1().a;
let subexpr_nonempty : i32 = S1(1, 2, 3, 4).b;
let subexpr_nonempty_with_expr : i32 = S1(1, x, x + 1, nonempty.d).c;
let subexpr_nested_empty : S1 = S2().f;
let subexpr_nested_nonempty : S1 = S2(1, S1(2, 3, 4, 5)).f;
let subexpr_nested_nonempty_with_expr : S1 =
S2(1, S1(2, x, x + 1, nested_nonempty.i.f.d)).f;
// Test arrays of structs containing arrays.
let aosoa_empty : array<T, 2> = array<T, 2>();
let aosoa_nonempty : array<T, 2> =
array<T, 2>(
T(array<i32, 2>(1, 2)),
T(array<i32, 2>(3, 4)),
);
let aosoa_nonempty_with_expr : array<T, 2> =
array<T, 2>(
T(array<i32, 2>(1, aosoa_nonempty[0].a[0] + 1)),
aosoa_nonempty[1],
);
}

View File

@ -0,0 +1,59 @@
struct S1 {
int a;
int b;
int c;
int d;
};
struct S2 {
int e;
S1 f;
};
struct S3 {
int g;
S1 h;
S2 i;
};
struct T {
int a[2];
};
[numthreads(1, 1, 1)]
void main() {
const int x = 42;
const S1 empty = {0, 0, 0, 0};
const S1 nonempty = {1, 2, 3, 4};
const S1 nonempty_with_expr = {1, x, (x + 1), nonempty.d};
const S3 nested_empty = {0, {0, 0, 0, 0}, {0, {0, 0, 0, 0}}};
const S1 tint_symbol = {2, 3, 4, 5};
const S1 tint_symbol_1 = {7, 8, 9, 10};
const S2 tint_symbol_2 = {6, tint_symbol_1};
const S3 nested_nonempty = {1, tint_symbol, tint_symbol_2};
const S1 tint_symbol_3 = {2, x, (x + 1), nested_nonempty.i.f.d};
const S2 tint_symbol_4 = {6, nonempty};
const S3 nested_nonempty_with_expr = {1, tint_symbol_3, tint_symbol_4};
const S1 tint_symbol_5 = {0, 0, 0, 0};
const int subexpr_empty = tint_symbol_5.a;
const S1 tint_symbol_6 = {1, 2, 3, 4};
const int subexpr_nonempty = tint_symbol_6.b;
const S1 tint_symbol_7 = {1, x, (x + 1), nonempty.d};
const int subexpr_nonempty_with_expr = tint_symbol_7.c;
const S2 tint_symbol_8 = {0, {0, 0, 0, 0}};
const S1 subexpr_nested_empty = tint_symbol_8.f;
const S1 tint_symbol_9 = {2, 3, 4, 5};
const S2 tint_symbol_10 = {1, tint_symbol_9};
const S1 subexpr_nested_nonempty = tint_symbol_10.f;
const S1 tint_symbol_11 = {2, x, (x + 1), nested_nonempty.i.f.d};
const S2 tint_symbol_12 = {1, tint_symbol_11};
const S1 subexpr_nested_nonempty_with_expr = tint_symbol_12.f;
const T aosoa_empty[2] = {{{0, 0}}, {{0, 0}}};
const int tint_symbol_13[2] = {1, 2};
const T tint_symbol_14 = {tint_symbol_13};
const int tint_symbol_15[2] = {3, 4};
const T tint_symbol_16 = {tint_symbol_15};
const T aosoa_nonempty[2] = {tint_symbol_14, tint_symbol_16};
const int tint_symbol_17[2] = {1, (aosoa_nonempty[0].a[0] + 1)};
const T tint_symbol_18 = {tint_symbol_17};
const T aosoa_nonempty_with_expr[2] = {tint_symbol_18, aosoa_nonempty[1]};
return;
}

View File

@ -0,0 +1 @@
SKIP: crbug.com/tint/814

View File

@ -0,0 +1,111 @@
; SPIR-V
; Version: 1.3
; Generator: Google Tint Compiler; 0
; Bound: 74
; Schema: 0
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpName %main "main"
OpName %S1 "S1"
OpMemberName %S1 0 "a"
OpMemberName %S1 1 "b"
OpMemberName %S1 2 "c"
OpMemberName %S1 3 "d"
OpName %S3 "S3"
OpMemberName %S3 0 "g"
OpMemberName %S3 1 "h"
OpMemberName %S3 2 "i"
OpName %S2 "S2"
OpMemberName %S2 0 "e"
OpMemberName %S2 1 "f"
OpName %T "T"
OpMemberName %T 0 "a"
OpMemberDecorate %S1 0 Offset 0
OpMemberDecorate %S1 1 Offset 4
OpMemberDecorate %S1 2 Offset 8
OpMemberDecorate %S1 3 Offset 12
OpMemberDecorate %S3 0 Offset 0
OpMemberDecorate %S3 1 Offset 4
OpMemberDecorate %S3 2 Offset 20
OpMemberDecorate %S2 0 Offset 0
OpMemberDecorate %S2 1 Offset 4
OpMemberDecorate %T 0 Offset 0
OpDecorate %_arr_int_uint_2 ArrayStride 4
OpDecorate %_arr_T_uint_2 ArrayStride 8
%void = OpTypeVoid
%1 = OpTypeFunction %void
%int = OpTypeInt 32 1
%int_42 = OpConstant %int 42
%S1 = OpTypeStruct %int %int %int %int
%8 = OpConstantNull %S1
%int_1 = OpConstant %int 1
%int_2 = OpConstant %int 2
%int_3 = OpConstant %int 3
%int_4 = OpConstant %int 4
%13 = OpConstantComposite %S1 %int_1 %int_2 %int_3 %int_4
%S2 = OpTypeStruct %int %S1
%S3 = OpTypeStruct %int %S1 %S2
%19 = OpConstantNull %S3
%int_5 = OpConstant %int 5
%21 = OpConstantComposite %S1 %int_2 %int_3 %int_4 %int_5
%int_6 = OpConstant %int 6
%int_7 = OpConstant %int 7
%int_8 = OpConstant %int 8
%int_9 = OpConstant %int 9
%int_10 = OpConstant %int 10
%27 = OpConstantComposite %S1 %int_7 %int_8 %int_9 %int_10
%28 = OpConstantComposite %S2 %int_6 %27
%29 = OpConstantComposite %S3 %int_1 %21 %28
%43 = OpConstantNull %S2
%45 = OpConstantComposite %S2 %int_1 %21
%uint = OpTypeInt 32 0
%uint_2 = OpConstant %uint 2
%_arr_int_uint_2 = OpTypeArray %int %uint_2
%T = OpTypeStruct %_arr_int_uint_2
%_arr_T_uint_2 = OpTypeArray %T %uint_2
%59 = OpConstantNull %_arr_T_uint_2
%60 = OpConstantComposite %_arr_int_uint_2 %int_1 %int_2
%61 = OpConstantComposite %T %60
%62 = OpConstantComposite %_arr_int_uint_2 %int_3 %int_4
%63 = OpConstantComposite %T %62
%64 = OpConstantComposite %_arr_T_uint_2 %61 %63
%int_0 = OpConstant %int 0
%main = OpFunction %void None %1
%4 = OpLabel
%14 = OpIAdd %int %int_42 %int_1
%15 = OpCompositeExtract %int %13 3
%16 = OpCompositeConstruct %S1 %int_1 %int_42 %14 %15
%30 = OpIAdd %int %int_42 %int_1
%31 = OpCompositeExtract %S2 %29 2
%32 = OpCompositeExtract %S1 %31 1
%33 = OpCompositeExtract %int %32 3
%34 = OpCompositeConstruct %S1 %int_2 %int_42 %30 %33
%35 = OpCompositeConstruct %S2 %int_6 %13
%36 = OpCompositeConstruct %S3 %int_1 %34 %35
%37 = OpCompositeExtract %int %8 0
%38 = OpCompositeExtract %int %13 1
%39 = OpIAdd %int %int_42 %int_1
%40 = OpCompositeExtract %int %13 3
%41 = OpCompositeConstruct %S1 %int_1 %int_42 %39 %40
%42 = OpCompositeExtract %int %41 2
%44 = OpCompositeExtract %S1 %43 1
%46 = OpCompositeExtract %S1 %45 1
%47 = OpIAdd %int %int_42 %int_1
%48 = OpCompositeExtract %S2 %29 2
%49 = OpCompositeExtract %S1 %48 1
%50 = OpCompositeExtract %int %49 3
%51 = OpCompositeConstruct %S1 %int_2 %int_42 %47 %50
%52 = OpCompositeConstruct %S2 %int_1 %51
%53 = OpCompositeExtract %S1 %52 1
%66 = OpCompositeExtract %T %64 0
%67 = OpCompositeExtract %_arr_int_uint_2 %66 0
%68 = OpCompositeExtract %int %67 0
%69 = OpIAdd %int %68 %int_1
%70 = OpCompositeConstruct %_arr_int_uint_2 %int_1 %69
%71 = OpCompositeConstruct %T %70
%72 = OpCompositeExtract %T %64 1
%73 = OpCompositeConstruct %_arr_T_uint_2 %71 %72
OpReturn
OpFunctionEnd

View File

@ -0,0 +1,41 @@
struct S1 {
a : i32;
b : i32;
c : i32;
d : i32;
};
struct S2 {
e : i32;
f : S1;
};
struct S3 {
g : i32;
h : S1;
i : S2;
};
struct T {
a : array<i32, 2>;
};
[[stage(compute)]]
fn main() {
let x : i32 = 42;
let empty : S1 = S1();
let nonempty : S1 = S1(1, 2, 3, 4);
let nonempty_with_expr : S1 = S1(1, x, (x + 1), nonempty.d);
let nested_empty : S3 = S3();
let nested_nonempty : S3 = S3(1, S1(2, 3, 4, 5), S2(6, S1(7, 8, 9, 10)));
let nested_nonempty_with_expr : S3 = S3(1, S1(2, x, (x + 1), nested_nonempty.i.f.d), S2(6, nonempty));
let subexpr_empty : i32 = S1().a;
let subexpr_nonempty : i32 = S1(1, 2, 3, 4).b;
let subexpr_nonempty_with_expr : i32 = S1(1, x, (x + 1), nonempty.d).c;
let subexpr_nested_empty : S1 = S2().f;
let subexpr_nested_nonempty : S1 = S2(1, S1(2, 3, 4, 5)).f;
let subexpr_nested_nonempty_with_expr : S1 = S2(1, S1(2, x, (x + 1), nested_nonempty.i.f.d)).f;
let aosoa_empty : array<T, 2> = array<T, 2>();
let aosoa_nonempty : array<T, 2> = array<T, 2>(T(array<i32, 2>(1, 2)), T(array<i32, 2>(3, 4)));
let aosoa_nonempty_with_expr : array<T, 2> = array<T, 2>(T(array<i32, 2>(1, (aosoa_nonempty[0].a[0] + 1))), aosoa_nonempty[1]);
}