[msl-writer] Use entry point interface canonicalization transform

This replaces the entry point IO component of the MSL sanitizing
transform, and completes support for the new entry point IO syntax.

Struct emission in the MSL writer is updated to use the correct
attributes depending on the pipeline stage usage.

Fixed: tint:510
Change-Id: I7ace33568c5a6e1bbf5c90f67b920579ba14be78
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/46520
Auto-Submit: James Price <jrprice@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: James Price <jrprice@google.com>
This commit is contained in:
James Price
2021-03-31 18:42:12 +00:00
committed by Commit Bot service account
parent 3f985b984d
commit dc8f08a782
8 changed files with 227 additions and 373 deletions

View File

@@ -14,13 +14,9 @@
#include "src/transform/msl.h"
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>
#include "src/program_builder.h"
#include "src/semantic/variable.h"
namespace tint {
namespace transform {
@@ -270,134 +266,9 @@ Transform::Output Msl::Run(const Program* in, const DataMap&) {
ProgramBuilder out;
CloneContext ctx(&out, in);
RenameReservedKeywords(&ctx, kReservedKeywords);
HandleEntryPointIOTypes(ctx);
ctx.Clone();
return Output{Program(std::move(out))};
}
void Msl::HandleEntryPointIOTypes(CloneContext& ctx) const {
// Collect location-decorated entry point parameters into a struct.
// Insert function-scope const declarations to replace those parameters.
//
// Before:
// ```
// [[stage(fragment)]]
// fn frag_main([[builtin(frag_coord)]] coord : vec4<f32>,
// [[location(1)]] loc1 : f32,
// [[location(2)]] loc2 : vec4<u32>) -> void {
// var col : f32 = (coord.x * loc1);
// }
// ```
//
// After:
// ```
// struct frag_main_in {
// [[location(1)]] loc1 : f32;
// [[location(2)]] loc2 : vec4<u32>
// };
// [[stage(fragment)]]
// fn frag_main([[builtin(frag_coord)]] coord : vec4<f32>,
// in : frag_main_in) -> void {
// const loc1 : f32 = in.loc1;
// const loc2 : vec4<u32> = in.loc2;
// var col : f32 = (coord.x * loc1);
// }
// ```
for (auto* func : ctx.src->AST().Functions()) {
if (!func->IsEntryPoint()) {
continue;
}
// Find location-decorated parameters and build a struct to hold them.
ast::StructMemberList struct_members;
std::unordered_set<ast::Variable*> builtins;
for (auto* param : func->params()) {
// TODO(jrprice): Handle structs (collate members into a single struct).
if (param->decorations().size() != 1) {
TINT_ICE(ctx.dst->Diagnostics()) << "Unsupported entry point parameter";
}
auto* deco = param->decorations()[0];
if (deco->Is<ast::BuiltinDecoration>()) {
// Keep any builtin-decorated parameters unchanged.
builtins.insert(param);
continue;
} else if (auto* loc = deco->As<ast::LocationDecoration>()) {
// Create a struct member with the location decoration.
std::string name = ctx.src->Symbols().NameFor(param->symbol());
auto* type = ctx.Clone(ctx.src->Sem().Get(param)->Type());
struct_members.push_back(
ctx.dst->Member(name, type, ast::DecorationList{ctx.Clone(loc)}));
} else {
TINT_ICE(ctx.dst->Diagnostics())
<< "Unsupported entry point parameter decoration";
}
}
if (struct_members.empty()) {
// Nothing to do.
continue;
}
ast::VariableList new_parameters;
ast::StatementList new_body;
// Create a struct type to hold all of the user-defined input parameters.
auto* in_struct = ctx.dst->create<type::Struct>(
ctx.dst->Symbols().New(),
ctx.dst->create<ast::Struct>(struct_members, ast::DecorationList{}));
ctx.InsertBefore(func, in_struct);
// Create a new function parameter using this struct type.
auto struct_param_symbol = ctx.dst->Symbols().New();
auto* struct_param =
ctx.dst->Var(struct_param_symbol, in_struct, ast::StorageClass::kNone);
new_parameters.push_back(struct_param);
// Replace the original parameters with function-scope constants.
for (auto* param : func->params()) {
if (builtins.count(param)) {
// Keep any builtin-decorated parameters unchanged.
new_parameters.push_back(ctx.Clone(param));
continue;
}
auto name = ctx.src->Symbols().NameFor(param->symbol());
// Create a function-scope const to replace the parameter.
// Initialize it with the value extracted from the struct parameter.
auto func_const_symbol = ctx.dst->Symbols().Register(name);
auto* type = ctx.Clone(ctx.src->Sem().Get(param)->Type());
auto* constructor = ctx.dst->MemberAccessor(struct_param_symbol, name);
auto* func_const = ctx.dst->Const(func_const_symbol, type, constructor);
new_body.push_back(ctx.dst->WrapInStatement(func_const));
// Replace all uses of the function parameter with the function const.
for (auto* user : ctx.src->Sem().Get(param)->Users()) {
ctx.Replace<ast::Expression>(user->Declaration(),
ctx.dst->Expr(func_const_symbol));
}
}
// Copy over the rest of the function body unchanged.
for (auto* stmt : func->body()->list()) {
new_body.push_back(ctx.Clone(stmt));
}
// Rewrite the function header with the new parameters.
auto* new_func = ctx.dst->create<ast::Function>(
func->source(), ctx.Clone(func->symbol()), new_parameters,
ctx.Clone(func->return_type()),
ctx.dst->create<ast::BlockStatement>(new_body),
ctx.Clone(func->decorations()),
ctx.Clone(func->return_type_decorations()));
ctx.Replace(func, new_func);
}
}
} // namespace transform
} // namespace tint

View File

@@ -34,10 +34,6 @@ class Msl : public Transform {
/// @param data optional extra transform-specific input data
/// @returns the transformation result
Output Run(const Program* program, const DataMap& data = {}) override;
private:
/// Hoist location-decorated entry point parameters out to struct members.
void HandleEntryPointIOTypes(CloneContext& ctx) const;
};
} // namespace transform

View File

@@ -326,109 +326,6 @@ INSTANTIATE_TEST_SUITE_P(MslReservedKeywordTest,
"vec",
"vertex"));
using MslEntryPointIOTest = TransformTest;
TEST_F(MslEntryPointIOTest, HandleEntryPointIOTypes_Parameters) {
auto* src = R"(
[[stage(fragment)]]
fn frag_main([[builtin(frag_coord)]] coord : vec4<f32>,
[[location(1)]] loc1 : f32,
[[location(2)]] loc2 : vec4<u32>) -> void {
var col : f32 = (coord.x * loc1);
}
)";
auto* expect = R"(
struct tint_symbol_3 {
[[location(1)]]
loc1 : f32;
[[location(2)]]
loc2 : vec4<u32>;
};
[[stage(fragment)]]
fn frag_main(tint_symbol_4 : tint_symbol_3, [[builtin(frag_coord)]] coord : vec4<f32>) -> void {
const loc1 : f32 = tint_symbol_4.loc1;
const loc2 : vec4<u32> = tint_symbol_4.loc2;
var col : f32 = (coord.x * loc1);
}
)";
auto got = Run<Msl>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(MslEntryPointIOTest, HandleEntryPointIOTypes_OnlyBuiltinParameters) {
// Expect no change.
auto* src = R"(
[[stage(fragment)]]
fn frag_main([[builtin(frag_coord)]] coord : vec4<f32>) -> void {
}
)";
auto got = Run<Msl>(src);
EXPECT_EQ(src, str(got));
}
TEST_F(MslEntryPointIOTest, HandleEntryPointIOTypes_Parameter_TypeAlias) {
auto* src = R"(
type myf32 = f32;
[[stage(fragment)]]
fn frag_main([[location(1)]] loc1 : myf32) -> void {
}
)";
auto* expect = R"(
type myf32 = f32;
struct tint_symbol_3 {
[[location(1)]]
loc1 : myf32;
};
[[stage(fragment)]]
fn frag_main(tint_symbol_4 : tint_symbol_3) -> void {
const loc1 : myf32 = tint_symbol_4.loc1;
}
)";
auto got = Run<Msl>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(MslEntryPointIOTest, HandleEntryPointIOTypes_Parameters_EmptyBody) {
auto* src = R"(
[[stage(fragment)]]
fn frag_main([[builtin(frag_coord)]] coord : vec4<f32>,
[[location(1)]] loc1 : f32,
[[location(2)]] loc2 : vec4<u32>) -> void {
}
)";
auto* expect = R"(
struct tint_symbol_3 {
[[location(1)]]
loc1 : f32;
[[location(2)]]
loc2 : vec4<u32>;
};
[[stage(fragment)]]
fn frag_main(tint_symbol_4 : tint_symbol_3, [[builtin(frag_coord)]] coord : vec4<f32>) -> void {
const loc1 : f32 = tint_symbol_4.loc1;
const loc2 : vec4<u32> = tint_symbol_4.loc2;
}
)";
auto got = Run<Msl>(src);
EXPECT_EQ(expect, str(got));
}
} // namespace
} // namespace transform
} // namespace tint