mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-12-08 13:14:56 +00:00
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:
committed by
Tint LUCI CQ
parent
1934f59427
commit
42220ba1b2
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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"()";
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
108
src/transform/promote_initializers_to_const_var.cc
Normal file
108
src/transform/promote_initializers_to_const_var.cc
Normal 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
|
||||
44
src/transform/promote_initializers_to_const_var.h
Normal file
44
src/transform/promote_initializers_to_const_var.h
Normal 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_
|
||||
291
src/transform/promote_initializers_to_const_var_test.cc
Normal file
291
src/transform/promote_initializers_to_const_var_test.cc
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
)");
|
||||
|
||||
Reference in New Issue
Block a user