[msl-writer] Add namer class.

This CL adds a namer class to prevent collisions with builtin names in
MSL. The MSL generator has been updated to use the namer anywhere that
names are emitted.

Bug: tint:8
Change-Id: I820f226a7286be1d5b0d613bd0fa41b68cb9f8ba
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/24184
Reviewed-by: David Neto <dneto@google.com>
This commit is contained in:
dan sinclair 2020-07-02 20:53:45 +00:00 committed by dan sinclair
parent 53b3283c6a
commit 6002a3345f
11 changed files with 776 additions and 13 deletions

View File

@ -487,7 +487,9 @@ source_set("libtint_msl_writer_src") {
"src/writer/msl/generator.cc",
"src/writer/msl/generator.h",
"src/writer/msl/generator_impl.cc",
"src/wrtier/msl/generator_impl.h",
"src/writer/msl/generator_impl.h",
"src/writer/msl/namer.cc",
"src/writer/msl/namer.h",
]
configs += [ ":tint_common_config" ]
@ -905,6 +907,7 @@ source_set("tint_unittests_msl_writer_src") {
"src/writer/msl/generator_impl_test.cc",
"src/writer/msl/generator_impl_type_test.cc",
"src/writer/msl/generator_impl_unary_op_test.cc",
"src/writer/msl/namer_test.cc",
]
configs += [

View File

@ -255,6 +255,8 @@ if(${TINT_BUILD_MSL_WRITER})
writer/msl/generator.h
writer/msl/generator_impl.cc
writer/msl/generator_impl.h
writer/msl/namer.cc
writer/msl/namer.h
)
endif()
@ -513,6 +515,7 @@ if(${TINT_BUILD_MSL_WRITER})
writer/msl/generator_impl_test.cc
writer/msl/generator_impl_type_test.cc
writer/msl/generator_impl_unary_op_test.cc
writer/msl/namer_test.cc
)
endif()

View File

@ -95,7 +95,7 @@ bool GeneratorImpl::EmitAliasType(const ast::type::AliasType* alias) {
if (!EmitType(alias->type(), "")) {
return false;
}
out_ << " " << alias->name() << ";" << std::endl;
out_ << " " << namer_.NameFor(alias->name()) << ";" << std::endl;
return true;
}
@ -432,7 +432,7 @@ bool GeneratorImpl::EmitFunction(ast::Function* func) {
return false;
}
out_ << " " << name << "(";
out_ << " " << namer_.NameFor(name) << "(";
bool first = true;
for (const auto& v : func->params()) {
@ -461,13 +461,16 @@ bool GeneratorImpl::EmitIdentifier(ast::IdentifierExpression* expr) {
error_ = "Identifier paths not handled yet.";
return false;
}
out_ << ident->name();
out_ << namer_.NameFor(ident->name());
return true;
}
bool GeneratorImpl::EmitLoop(ast::LoopStatement* stmt) {
loop_emission_counter_++;
std::string guard = namer_.NameFor("tint_msl_is_first_" +
std::to_string(loop_emission_counter_));
if (stmt->has_continuing()) {
make_indent();
@ -476,8 +479,7 @@ bool GeneratorImpl::EmitLoop(ast::LoopStatement* stmt) {
increment_indent();
make_indent();
out_ << "bool tint_msl_is_first_" << loop_emission_counter_ << " = true;"
<< std::endl;
out_ << "bool " << guard << " = true;" << std::endl;
}
make_indent();
@ -486,15 +488,14 @@ bool GeneratorImpl::EmitLoop(ast::LoopStatement* stmt) {
if (stmt->has_continuing()) {
make_indent();
out_ << "if (!tint_msl_is_first_" << loop_emission_counter_ << ")";
out_ << "if (!" << guard << ")";
if (!EmitStatementBlockAndNewline(stmt->continuing())) {
return false;
}
make_indent();
out_ << "tint_msl_is_first_" << loop_emission_counter_ << " = false;"
<< std::endl;
out_ << guard << " = false;" << std::endl;
out_ << std::endl;
}
@ -676,7 +677,7 @@ bool GeneratorImpl::EmitSwitch(ast::SwitchStatement* stmt) {
bool GeneratorImpl::EmitType(ast::type::Type* type, const std::string& name) {
if (type->IsAlias()) {
auto* alias = type->AsAlias();
out_ << alias->name();
out_ << namer_.NameFor(alias->name());
} else if (type->IsArray()) {
auto* ary = type->AsArray();
@ -684,7 +685,7 @@ bool GeneratorImpl::EmitType(ast::type::Type* type, const std::string& name) {
return false;
}
if (!name.empty()) {
out_ << " " << name;
out_ << " " << namer_.NameFor(name);
}
out_ << "[";
if (ary->IsRuntimeArray()) {
@ -732,7 +733,7 @@ bool GeneratorImpl::EmitType(ast::type::Type* type, const std::string& name) {
}
// Array member name will be output with the type
if (!mem->type()->IsArray()) {
out_ << " " << mem->name();
out_ << " " << namer_.NameFor(mem->name());
}
out_ << ";" << std::endl;
}

View File

@ -22,6 +22,7 @@
#include "src/ast/module.h"
#include "src/ast/scalar_constructor_expression.h"
#include "src/ast/type_constructor_expression.h"
#include "src/writer/msl/namer.h"
#include "src/writer/text_generator.h"
namespace tint {
@ -158,6 +159,7 @@ class GeneratorImpl : public TextGenerator {
bool EmitUnaryOp(ast::UnaryOpExpression* expr);
private:
Namer namer_;
const ast::Module* module_ = nullptr;
uint32_t loop_emission_counter_ = 0;
};

View File

@ -39,6 +39,16 @@ TEST_F(MslGeneratorImplTest, EmitAliasType_F32) {
)");
}
TEST_F(MslGeneratorImplTest, EmitAliasType_NameCollision) {
ast::type::F32Type f32;
ast::type::AliasType alias("float", &f32);
GeneratorImpl g;
ASSERT_TRUE(g.EmitAliasType(&alias)) << g.error();
EXPECT_EQ(g.result(), R"(typedef float float_tint_0;
)");
}
TEST_F(MslGeneratorImplTest, EmitAliasType_Struct) {
ast::type::I32Type i32;
ast::type::F32Type f32;

View File

@ -54,6 +54,30 @@ TEST_F(MslGeneratorImplTest, Emit_Function) {
)");
}
TEST_F(MslGeneratorImplTest, Emit_Function_Name_Collision) {
ast::type::VoidType void_type;
auto func =
std::make_unique<ast::Function>("main", ast::VariableList{}, &void_type);
ast::StatementList body;
body.push_back(std::make_unique<ast::ReturnStatement>());
func->set_body(std::move(body));
ast::Module m;
m.AddFunction(std::move(func));
GeneratorImpl g;
g.increment_indent();
ASSERT_TRUE(g.Generate(m)) << g.error();
EXPECT_EQ(g.result(), R"( void main_tint_0() {
return;
}
)");
}
TEST_F(MslGeneratorImplTest, Emit_Function_WithParams) {
ast::type::F32Type f32;
ast::type::I32Type i32;
@ -109,6 +133,26 @@ TEST_F(MslGeneratorImplTest, Emit_Function_EntryPoint_NoName) {
TEST_F(MslGeneratorImplTest, Emit_Function_EntryPoint_WithName) {
ast::type::VoidType void_type;
auto func = std::make_unique<ast::Function>("comp_main", ast::VariableList{},
&void_type);
auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kCompute,
"my_main", "comp_main");
ast::Module m;
m.AddFunction(std::move(func));
m.AddEntryPoint(std::move(ep));
GeneratorImpl g;
ASSERT_TRUE(g.Generate(m)) << g.error();
EXPECT_EQ(g.result(), R"(kernel void my_main() {
}
)");
}
TEST_F(MslGeneratorImplTest, Emit_Function_EntryPoint_WithNameCollision) {
ast::type::VoidType void_type;
auto func = std::make_unique<ast::Function>("comp_main", ast::VariableList{},
&void_type);
auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kCompute,
@ -120,7 +164,7 @@ TEST_F(MslGeneratorImplTest, Emit_Function_EntryPoint_WithName) {
GeneratorImpl g;
ASSERT_TRUE(g.Generate(m)) << g.error();
EXPECT_EQ(g.result(), R"(kernel void main() {
EXPECT_EQ(g.result(), R"(kernel void main_tint_0() {
}
)");

View File

@ -39,6 +39,14 @@ TEST_F(MslGeneratorImplTest, EmitIdentifierExpression_Single) {
EXPECT_EQ(g.result(), "foo");
}
TEST_F(MslGeneratorImplTest, EmitIdentifierExpression_Single_WithCollision) {
ast::IdentifierExpression i("virtual");
GeneratorImpl g;
ASSERT_TRUE(g.EmitExpression(&i)) << g.error();
EXPECT_EQ(g.result(), "virtual_tint_0");
}
// TODO(dsinclair): Handle import names
TEST_F(MslGeneratorImplTest, DISABLED_EmitIdentifierExpression_MultipleNames) {
ast::IdentifierExpression i({"std", "glsl", "init"});

View File

@ -46,6 +46,15 @@ TEST_F(MslGeneratorImplTest, EmitType_Alias) {
EXPECT_EQ(g.result(), "alias");
}
TEST_F(MslGeneratorImplTest, EmitType_Alias_NameCollision) {
ast::type::F32Type f32;
ast::type::AliasType alias("bool", &f32);
GeneratorImpl g;
ASSERT_TRUE(g.EmitType(&alias, "")) << g.error();
EXPECT_EQ(g.result(), "bool_tint_0");
}
TEST_F(MslGeneratorImplTest, EmitType_Array) {
ast::type::BoolType b;
ast::type::ArrayType a(&b, 4);
@ -55,6 +64,15 @@ TEST_F(MslGeneratorImplTest, EmitType_Array) {
EXPECT_EQ(g.result(), "bool ary[4]");
}
TEST_F(MslGeneratorImplTest, EmitType_Array_NameCollision) {
ast::type::BoolType b;
ast::type::ArrayType a(&b, 4);
GeneratorImpl g;
ASSERT_TRUE(g.EmitType(&a, "bool")) << g.error();
EXPECT_EQ(g.result(), "bool bool_tint_0[4]");
}
TEST_F(MslGeneratorImplTest, EmitType_Array_WithoutName) {
ast::type::BoolType b;
ast::type::ArrayType a(&b, 4);
@ -73,6 +91,15 @@ TEST_F(MslGeneratorImplTest, EmitType_RuntimeArray) {
EXPECT_EQ(g.result(), "bool ary[1]");
}
TEST_F(MslGeneratorImplTest, EmitType_RuntimeArray_NameCollision) {
ast::type::BoolType b;
ast::type::ArrayType a(&b);
GeneratorImpl g;
ASSERT_TRUE(g.EmitType(&a, "discard_fragment")) << g.error();
EXPECT_EQ(g.result(), "bool discard_fragment_tint_0[1]");
}
TEST_F(MslGeneratorImplTest, EmitType_Bool) {
ast::type::BoolType b;
@ -143,6 +170,31 @@ TEST_F(MslGeneratorImplTest, DISABLED_EmitType_Struct) {
})");
}
TEST_F(MslGeneratorImplTest, EmitType_Struct_NameCollision) {
ast::type::I32Type i32;
ast::type::F32Type f32;
ast::StructMemberList members;
members.push_back(std::make_unique<ast::StructMember>(
"main", &i32, ast::StructMemberDecorationList{}));
ast::StructMemberDecorationList b_deco;
members.push_back(
std::make_unique<ast::StructMember>("float", &f32, std::move(b_deco)));
auto str = std::make_unique<ast::Struct>();
str->set_members(std::move(members));
ast::type::StructType s(std::move(str));
GeneratorImpl g;
ASSERT_TRUE(g.EmitType(&s, "")) << g.error();
EXPECT_EQ(g.result(), R"(struct {
int main_tint_0;
float float_tint_0;
})");
}
// TODO(dsinclair): How to translate [[block]]
TEST_F(MslGeneratorImplTest, DISABLED_EmitType_Struct_WithDecoration) {
ast::type::I32Type i32;

299
src/writer/msl/namer.cc Normal file
View File

@ -0,0 +1,299 @@
// Copyright 2020 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/writer/msl/namer.h"
namespace tint {
namespace writer {
namespace msl {
namespace {
const char* kNames[] = {"access",
"alignas",
"alignof",
"and",
"and_eq",
"array",
"array_ref",
"as_type",
"asm",
"atomic",
"atomic_bool",
"atomic_int",
"atomic_uint",
"auto",
"bitand",
"bitor",
"bool",
"bool2",
"bool3",
"bool4",
"break",
"buffer",
"case",
"catch",
"char",
"char16_t",
"char2",
"char3",
"char32_t",
"char4",
"class",
"compl",
"const",
"const_cast",
"const_reference",
"constant",
"constexpr",
"continue",
"decltype",
"default",
"delete",
"depth2d",
"depth2d_array",
"depth2d_ms",
"depth2d_ms_array",
"depthcube",
"depthcube_array",
"device",
"discard_fragment",
"do",
"double",
"dynamic_cast",
"else",
"enum",
"explicit",
"extern",
"extern",
"false",
"final",
"float",
"float2",
"float2x2",
"float2x3",
"float2x4",
"float3",
"float3x2",
"float3x3",
"float3x4",
"float4",
"float4x2",
"float4x3",
"float4x4",
"for",
"fragment",
"friend",
"goto",
"half",
"half2",
"half2x2",
"half2x3",
"half2x4",
"half3",
"half3x2",
"half3x3",
"half3x4",
"half4",
"half4x2",
"half4x3",
"half4x4",
"if",
"imageblock",
"inline",
"inline",
"int",
"int16_t",
"int2",
"int3",
"int32_t",
"int4",
"int64_t",
"int8_t",
"kernel",
"long",
"long2",
"long3",
"long4",
"main",
"metal",
"mutable"
"mutable",
"namespace",
"new",
"noexcept"
"not",
"not_eq",
"nullptr",
"operator",
"or",
"or_eq",
"override",
"packed_bool2",
"packed_bool3",
"packed_bool4",
"packed_char2",
"packed_char3",
"packed_char4",
"packed_float2",
"packed_float3",
"packed_float4",
"packed_half2",
"packed_half3",
"packed_half4",
"packed_int2",
"packed_int3",
"packed_int4",
"packed_short2",
"packed_short3",
"packed_short4",
"packed_uchar2",
"packed_uchar3",
"packed_uchar4",
"packed_uint2",
"packed_uint3",
"packed_uint4",
"packed_ushort2",
"packed_ushort3",
"packed_ushort4",
"patch_control_point",
"private",
"protected",
"ptrdiff_t",
"public",
"r16snorm",
"r16unorm",
"r8unorm",
"reference",
"register",
"reinterpret_cast",
"return",
"rg11b10f",
"rg16snorm",
"rg16unorm",
"rg8snorm",
"rg8unorm",
"rgb10a2",
"rgb9e5",
"rgba16snorm",
"rgba16unorm",
"rgba8snorm",
"rgba8unorm",
"sampler",
"short",
"short2",
"short3",
"short4",
"signed",
"size_t",
"sizeof",
"srgba8unorm",
"static",
"static_assert",
"static_cast",
"struct",
"switch",
"template",
"texture",
"texture1d",
"texture1d_array",
"texture2d",
"texture2d_array",
"texture2d_ms",
"texture2d_ms_array",
"texture3d",
"texture_buffer",
"texturecube",
"texturecube_array",
"this",
"thread",
"thread_local",
"threadgroup",
"threadgroup_imageblock",
"throw",
"true",
"try",
"typedef",
"typeid",
"typename",
"uchar",
"uchar2",
"uchar3",
"uchar4",
"uint",
"uint16_t",
"uint2",
"uint3",
"uint32_t",
"uint4",
"uint64_t",
"uint8_t",
"ulong2",
"ulong3",
"ulong4",
"uniform",
"union",
"unsigned",
"ushort",
"ushort2",
"ushort3",
"ushort4",
"using",
"vec",
"vertex",
"virtual",
"virtual",
"void",
"volatile",
"wchar_t",
"while",
"xor",
"xor_eq"};
} // namespace
Namer::Namer() = default;
Namer::~Namer() = default;
std::string Namer::NameFor(const std::string& name) {
// If it's in the name make we can just return it. There are no shadow names
// in WGSL so this has to be unique in the WGSL names, and we've already
// checked the name collisions with MSL.
auto it = name_map_.find(name);
if (it != name_map_.end()) {
return it->second;
}
std::string ret_name = name;
if (std::binary_search(std::begin(kNames), std::end(kNames), ret_name)) {
uint32_t i = 0;
// Make sure there wasn't already a tint variable with the new name we've
// now created.
while (true) {
ret_name = name + "_tint_" + std::to_string(i);
it = name_map_.find(ret_name);
if (it == name_map_.end()) {
break;
}
i++;
}
}
name_map_[name] = ret_name;
return ret_name;
}
} // namespace msl
} // namespace writer
} // namespace tint

46
src/writer/msl/namer.h Normal file
View File

@ -0,0 +1,46 @@
// Copyright 2020 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_WRITER_MSL_NAMER_H_
#define SRC_WRITER_MSL_NAMER_H_
#include <string>
#include <unordered_map>
namespace tint {
namespace writer {
namespace msl {
/// Remaps maps names to avoid reserved words and collisions for MSL.
class Namer {
public:
/// Constructor
Namer();
~Namer();
/// Returns a sanitized version of |name|
/// @param name the name to sanitize
/// @returns the sanitized version of |name|
std::string NameFor(const std::string& name);
private:
/// Map of original name to new name. The two names may be the same.
std::unordered_map<std::string, std::string> name_map_;
};
} // namespace msl
} // namespace writer
} // namespace tint
#endif // SRC_WRITER_MSL_NAMER_H_

View File

@ -0,0 +1,295 @@
// Copyright 2020 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/writer/msl/namer.h"
#include "gtest/gtest.h"
namespace tint {
namespace writer {
namespace msl {
namespace {
using MslNamerTest = testing::Test;
TEST_F(MslNamerTest, ReturnsName) {
Namer n;
EXPECT_EQ("my_name", n.NameFor("my_name"));
EXPECT_EQ("my_name", n.NameFor("my_name"));
}
TEST_F(MslNamerTest, HandlesConflictWithRenamedReservedWord) {
Namer n;
EXPECT_EQ("float_tint_0", n.NameFor("float_tint_0"));
EXPECT_EQ("float_tint_1", n.NameFor("float"));
EXPECT_EQ("float_tint_0", n.NameFor("float_tint_0"));
}
using MslReservedNameTest = testing::TestWithParam<std::string>;
TEST_P(MslReservedNameTest, Emit) {
auto name = GetParam();
Namer n;
EXPECT_EQ(name + "_tint_0", n.NameFor(name));
}
INSTANTIATE_TEST_SUITE_P(MslNamerTest,
MslReservedNameTest,
testing::Values(
// c++14 spec
"alignas",
"alignof",
"and",
"and_eq",
"asm",
"auto",
"bitand",
"bitor",
"bool",
"break",
"case",
"catch",
"char",
"char16_t",
"char32_t",
"class",
"compl",
"const",
"const_cast",
"constexpr",
"continue",
"decltype",
"default",
"delete",
"do",
"double",
"dynamic_cast",
"else",
"enum",
"explicit",
"extern",
"extern",
"false",
"final",
"float",
"for",
"friend",
"goto",
"if",
"inline",
"inline",
"int",
"long",
"mutable"
"mutable",
"namespace",
"new",
"noexcept"
"not",
"not_eq",
"nullptr",
"operator",
"or",
"or_eq",
"override",
"private",
"protected",
"public",
"register",
"reinterpret_cast",
"return",
"short",
"signed",
"sizeof",
"static",
"static_assert",
"static_cast",
"struct",
"switch",
"template",
"this",
"thread_local",
"throw",
"true",
"try",
"typedef",
"typeid",
"typename",
"union",
"unsigned",
"using",
"virtual",
"virtual",
"void",
"volatile",
"wchar_t",
"while",
"xor",
"xor_eq",
// Metal Spec
"access",
"array",
"array_ref",
"as_type",
"atomic",
"atomic_bool",
"atomic_int",
"atomic_uint",
"bool2",
"bool3",
"bool4",
"buffer",
"char2",
"char3",
"char4",
"const_reference",
"constant",
"depth2d",
"depth2d_array",
"depth2d_ms",
"depth2d_ms_array",
"depthcube",
"depthcube_array",
"device",
"discard_fragment",
"float2",
"float2x2",
"float2x3",
"float2x4",
"float3",
"float3x2",
"float3x3",
"float3x4",
"float4",
"float4x2",
"float4x3",
"float4x4",
"fragment",
"half",
"half2",
"half2x2",
"half2x3",
"half2x4",
"half3",
"half3x2",
"half3x3",
"half3x4",
"half4",
"half4x2",
"half4x3",
"half4x4",
"imageblock",
"int16_t",
"int2",
"int3",
"int32_t",
"int4",
"int64_t",
"int8_t",
"kernel",
"long2",
"long3",
"long4",
"main", // No functions called main
"metal", // The namespace
"packed_bool2",
"packed_bool3",
"packed_bool4",
"packed_char2",
"packed_char3",
"packed_char4",
"packed_float2",
"packed_float3",
"packed_float4",
"packed_half2",
"packed_half3",
"packed_half4",
"packed_int2",
"packed_int3",
"packed_int4",
"packed_short2",
"packed_short3",
"packed_short4",
"packed_uchar2",
"packed_uchar3",
"packed_uchar4",
"packed_uint2",
"packed_uint3",
"packed_uint4",
"packed_ushort2",
"packed_ushort3",
"packed_ushort4",
"patch_control_point",
"ptrdiff_t",
"r16snorm",
"r16unorm",
"r8unorm",
"reference",
"rg11b10f",
"rg16snorm",
"rg16unorm",
"rg8snorm",
"rg8unorm",
"rgb10a2",
"rgb9e5",
"rgba16snorm",
"rgba16unorm",
"rgba8snorm",
"rgba8unorm",
"sampler",
"short2",
"short3",
"short4",
"size_t",
"srgba8unorm",
"texture",
"texture1d",
"texture1d_array",
"texture2d",
"texture2d_array",
"texture2d_ms",
"texture2d_ms_array",
"texture3d",
"texture_buffer",
"texturecube",
"texturecube_array",
"thread",
"threadgroup",
"threadgroup_imageblock",
"uchar",
"uchar2",
"uchar3",
"uchar4",
"uint",
"uint16_t",
"uint2",
"uint3",
"uint32_t",
"uint4",
"uint64_t",
"uint8_t",
"ulong2",
"ulong3",
"ulong4",
"uniform",
"ushort",
"ushort2",
"ushort3",
"ushort4",
"vec",
"vertex"));
} // namespace
} // namespace msl
} // namespace writer
} // namespace tint