[spirv-writer] Add null initializers where needed.

The WGSL spec requires initializers for Output, Private and Function
variables. This CL adds initializers where needed.

Bug: tint:75
Change-Id: Id97f85a67ead2ffc41d6bdd1b71bf7034b04502a
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/20980
Reviewed-by: David Neto <dneto@google.com>
This commit is contained in:
dan sinclair 2020-05-05 14:21:19 +00:00
parent 5b43c58f02
commit 2287f33424
23 changed files with 511 additions and 269 deletions

View File

@ -268,6 +268,8 @@ source_set("libtint_core_src") {
"src/ast/node.h", "src/ast/node.h",
"src/ast/nop_statement.cc", "src/ast/nop_statement.cc",
"src/ast/nop_statement.h", "src/ast/nop_statement.h",
"src/ast/null_literal.cc",
"src/ast/null_literal.h",
"src/ast/pipeline_stage.cc", "src/ast/pipeline_stage.cc",
"src/ast/pipeline_stage.h", "src/ast/pipeline_stage.h",
"src/ast/return_statement.cc", "src/ast/return_statement.cc",
@ -598,6 +600,7 @@ source_set("tint_unittests_core_src") {
"src/ast/member_accessor_expression_test.cc", "src/ast/member_accessor_expression_test.cc",
"src/ast/module_test.cc", "src/ast/module_test.cc",
"src/ast/nop_statement_test.cc", "src/ast/nop_statement_test.cc",
"src/ast/null_literal_test.cc",
"src/ast/return_statement_test.cc", "src/ast/return_statement_test.cc",
"src/ast/scalar_constructor_expression_test.cc", "src/ast/scalar_constructor_expression_test.cc",
"src/ast/set_decoration_test.cc", "src/ast/set_decoration_test.cc",

View File

@ -105,6 +105,8 @@ set(TINT_LIB_SRCS
ast/node.h ast/node.h
ast/nop_statement.cc ast/nop_statement.cc
ast/nop_statement.h ast/nop_statement.h
ast/null_literal.cc
ast/null_literal.h
ast/pipeline_stage.cc ast/pipeline_stage.cc
ast/pipeline_stage.h ast/pipeline_stage.h
ast/return_statement.cc ast/return_statement.cc
@ -283,6 +285,7 @@ set(TINT_TEST_SRCS
ast/member_accessor_expression_test.cc ast/member_accessor_expression_test.cc
ast/module_test.cc ast/module_test.cc
ast/nop_statement_test.cc ast/nop_statement_test.cc
ast/null_literal_test.cc
ast/binary_expression_test.cc ast/binary_expression_test.cc
ast/return_statement_test.cc ast/return_statement_test.cc
ast/scalar_constructor_expression_test.cc ast/scalar_constructor_expression_test.cc

View File

@ -46,6 +46,7 @@ TEST_F(BoolLiteralTest, Is) {
EXPECT_FALSE(b.IsInt()); EXPECT_FALSE(b.IsInt());
EXPECT_FALSE(b.IsFloat()); EXPECT_FALSE(b.IsFloat());
EXPECT_FALSE(b.IsUint()); EXPECT_FALSE(b.IsUint());
EXPECT_FALSE(b.IsNull());
} }
TEST_F(BoolLiteralTest, ToStr) { TEST_F(BoolLiteralTest, ToStr) {

View File

@ -37,6 +37,7 @@ TEST_F(FloatLiteralTest, Is) {
EXPECT_FALSE(f.IsInt()); EXPECT_FALSE(f.IsInt());
EXPECT_TRUE(f.IsFloat()); EXPECT_TRUE(f.IsFloat());
EXPECT_FALSE(f.IsUint()); EXPECT_FALSE(f.IsUint());
EXPECT_FALSE(f.IsNull());
} }
TEST_F(FloatLiteralTest, ToStr) { TEST_F(FloatLiteralTest, ToStr) {

View File

@ -38,6 +38,7 @@ TEST_F(IntLiteralTest, Is) {
EXPECT_TRUE(i.IsInt()); EXPECT_TRUE(i.IsInt());
EXPECT_FALSE(i.IsFloat()); EXPECT_FALSE(i.IsFloat());
EXPECT_FALSE(i.IsUint()); EXPECT_FALSE(i.IsUint());
EXPECT_FALSE(i.IsNull());
} }
TEST_F(IntLiteralTest, ToStr) { TEST_F(IntLiteralTest, ToStr) {

View File

@ -19,6 +19,7 @@
#include "src/ast/bool_literal.h" #include "src/ast/bool_literal.h"
#include "src/ast/float_literal.h" #include "src/ast/float_literal.h"
#include "src/ast/int_literal.h" #include "src/ast/int_literal.h"
#include "src/ast/null_literal.h"
#include "src/ast/uint_literal.h" #include "src/ast/uint_literal.h"
namespace tint { namespace tint {
@ -40,6 +41,10 @@ bool Literal::IsInt() const {
return false; return false;
} }
bool Literal::IsNull() const {
return false;
}
bool Literal::IsUint() const { bool Literal::IsUint() const {
return false; return false;
} }
@ -59,6 +64,11 @@ IntLiteral* Literal::AsInt() {
return static_cast<IntLiteral*>(this); return static_cast<IntLiteral*>(this);
} }
NullLiteral* Literal::AsNull() {
assert(IsNull());
return static_cast<NullLiteral*>(this);
}
UintLiteral* Literal::AsUint() { UintLiteral* Literal::AsUint() {
assert(IsUint()); assert(IsUint());
return static_cast<UintLiteral*>(this); return static_cast<UintLiteral*>(this);

View File

@ -25,6 +25,7 @@ namespace ast {
class BoolLiteral; class BoolLiteral;
class FloatLiteral; class FloatLiteral;
class IntLiteral; class IntLiteral;
class NullLiteral;
class UintLiteral; class UintLiteral;
/// Base class for a literal value /// Base class for a literal value
@ -38,6 +39,8 @@ class Literal {
virtual bool IsFloat() const; virtual bool IsFloat() const;
/// @returns true if this is a signed int literal /// @returns true if this is a signed int literal
virtual bool IsInt() const; virtual bool IsInt() const;
/// @returns true if this is a null literal
virtual bool IsNull() const;
/// @returns true if this is a unsigned int literal /// @returns true if this is a unsigned int literal
virtual bool IsUint() const; virtual bool IsUint() const;
@ -47,6 +50,8 @@ class Literal {
FloatLiteral* AsFloat(); FloatLiteral* AsFloat();
/// @returns the literal as a int literal /// @returns the literal as a int literal
IntLiteral* AsInt(); IntLiteral* AsInt();
/// @returns the literal as a null literal
NullLiteral* AsNull();
/// @returns the literal as a unsigned int literal /// @returns the literal as a unsigned int literal
UintLiteral* AsUint(); UintLiteral* AsUint();

37
src/ast/null_literal.cc Normal file
View File

@ -0,0 +1,37 @@
// 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/ast/null_literal.h"
namespace tint {
namespace ast {
NullLiteral::NullLiteral(ast::type::Type* type) : Literal(type) {}
NullLiteral::~NullLiteral() = default;
bool NullLiteral::IsNull() const {
return true;
}
std::string NullLiteral::to_str() const {
return "null " + type()->type_name();
}
std::string NullLiteral::name() const {
return "__null" + type()->type_name();
}
} // namespace ast
} // namespace tint

46
src/ast/null_literal.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_AST_NULL_LITERAL_H_
#define SRC_AST_NULL_LITERAL_H_
#include <string>
#include "src/ast/literal.h"
namespace tint {
namespace ast {
/// A null literal
class NullLiteral : public Literal {
public:
/// Constructor
/// @param type the type
NullLiteral(ast::type::Type* type);
~NullLiteral() override;
/// @returns true if this is a null literal
bool IsNull() const override;
/// @returns the name for this literal. This name is unique to this value.
std::string name() const override;
/// @returns the literal as a string
std::string to_str() const override;
};
} // namespace ast
} // namespace tint
#endif // SRC_AST_NULL_LITERAL_H_

View File

@ -0,0 +1,51 @@
// 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/ast/null_literal.h"
#include "gtest/gtest.h"
#include "src/ast/type/i32_type.h"
namespace tint {
namespace ast {
namespace {
using NullLiteralTest = testing::Test;
TEST_F(NullLiteralTest, Is) {
ast::type::I32Type i32;
NullLiteral i{&i32};
EXPECT_FALSE(i.IsBool());
EXPECT_FALSE(i.IsInt());
EXPECT_FALSE(i.IsFloat());
EXPECT_FALSE(i.IsUint());
EXPECT_TRUE(i.IsNull());
}
TEST_F(NullLiteralTest, ToStr) {
ast::type::I32Type i32;
NullLiteral i{&i32};
EXPECT_EQ(i.to_str(), "null __i32");
}
TEST_F(NullLiteralTest, Name_I32) {
ast::type::I32Type i32;
NullLiteral i{&i32};
EXPECT_EQ("__null__i32", i.name());
}
} // namespace
} // namespace ast
} // namespace tint

View File

@ -37,6 +37,7 @@ TEST_F(UintLiteralTest, Is) {
EXPECT_FALSE(u.IsInt()); EXPECT_FALSE(u.IsInt());
EXPECT_FALSE(u.IsFloat()); EXPECT_FALSE(u.IsFloat());
EXPECT_TRUE(u.IsUint()); EXPECT_TRUE(u.IsUint());
EXPECT_FALSE(u.IsNull());
} }
TEST_F(UintLiteralTest, ToStr) { TEST_F(UintLiteralTest, ToStr) {

View File

@ -36,6 +36,7 @@
#include "src/ast/location_decoration.h" #include "src/ast/location_decoration.h"
#include "src/ast/loop_statement.h" #include "src/ast/loop_statement.h"
#include "src/ast/member_accessor_expression.h" #include "src/ast/member_accessor_expression.h"
#include "src/ast/null_literal.h"
#include "src/ast/return_statement.h" #include "src/ast/return_statement.h"
#include "src/ast/scalar_constructor_expression.h" #include "src/ast/scalar_constructor_expression.h"
#include "src/ast/set_decoration.h" #include "src/ast/set_decoration.h"
@ -430,9 +431,15 @@ bool Builder::GenerateFunctionVariable(ast::Variable* var) {
// TODO(dsinclair) We could detect if the constructor is fully const and emit // TODO(dsinclair) We could detect if the constructor is fully const and emit
// an initializer value for the variable instead of doing the OpLoad. // an initializer value for the variable instead of doing the OpLoad.
ast::NullLiteral nl(var->type()->UnwrapPtrIfNeeded());
auto null_id = GenerateLiteralIfNeeded(&nl);
if (null_id == 0) {
return 0;
}
push_function_var({Operand::Int(type_id), result,
Operand::Int(ConvertStorageClass(sc)),
Operand::Int(null_id)});
push_function_var(
{Operand::Int(type_id), result, Operand::Int(ConvertStorageClass(sc))});
if (var->has_constructor()) { if (var->has_constructor()) {
init_id = GenerateLoadIfNeeded(var->constructor()->result_type(), init_id); init_id = GenerateLoadIfNeeded(var->constructor()->result_type(), init_id);
GenerateStore(var_id, init_id); GenerateStore(var_id, init_id);
@ -493,6 +500,19 @@ bool Builder::GenerateGlobalVariable(ast::Variable* var) {
Operand::Int(ConvertStorageClass(sc))}; Operand::Int(ConvertStorageClass(sc))};
if (var->has_constructor()) { if (var->has_constructor()) {
ops.push_back(Operand::Int(init_id)); ops.push_back(Operand::Int(init_id));
} else {
// If we don't have a constructor and we're an Output or Private variable
// then WGSL requires an initializer.
if (var->storage_class() == ast::StorageClass::kPrivate ||
var->storage_class() == ast::StorageClass::kNone ||
var->storage_class() == ast::StorageClass::kOutput) {
ast::NullLiteral nl(var->type()->UnwrapPtrIfNeeded());
init_id = GenerateLiteralIfNeeded(&nl);
if (init_id == 0) {
return 0;
}
ops.push_back(Operand::Int(init_id));
}
} }
push_type(spv::Op::OpVariable, std::move(ops)); push_type(spv::Op::OpVariable, std::move(ops));
@ -965,6 +985,8 @@ uint32_t Builder::GenerateLiteralIfNeeded(ast::Literal* lit) {
} else if (lit->IsFloat()) { } else if (lit->IsFloat()) {
push_type(spv::Op::OpConstant, {Operand::Int(type_id), result, push_type(spv::Op::OpConstant, {Operand::Int(type_id), result,
Operand::Float(lit->AsFloat()->value())}); Operand::Float(lit->AsFloat()->value())});
} else if (lit->IsNull()) {
push_type(spv::Op::OpConstantNull, {Operand::Int(type_id), result});
} else { } else {
error_ = "unknown literal type"; error_ = "unknown literal type";
return 0; return 0;

View File

@ -68,20 +68,21 @@ TEST_F(BuilderTest, ArrayAccessor) {
b.push_function(Function{}); b.push_function(Function{});
ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error(); ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error();
EXPECT_EQ(b.GenerateAccessorExpression(&expr), 8u); EXPECT_EQ(b.GenerateAccessorExpression(&expr), 9u);
EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
%3 = OpTypeVector %4 3 %3 = OpTypeVector %4 3
%2 = OpTypePointer Function %3 %2 = OpTypePointer Function %3
%5 = OpTypeInt 32 1 %5 = OpConstantNull %3
%6 = OpConstant %5 1 %6 = OpTypeInt 32 1
%7 = OpTypePointer Function %4 %7 = OpConstant %6 1
%8 = OpTypePointer Function %4
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
R"(%1 = OpVariable %2 Function R"(%1 = OpVariable %2 Function %5
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%8 = OpAccessChain %7 %1 %6 R"(%9 = OpAccessChain %8 %1 %7
)"); )");
} }
@ -114,22 +115,24 @@ TEST_F(BuilderTest, Accessor_Array_LoadIndex) {
ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error(); ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error();
ASSERT_TRUE(b.GenerateFunctionVariable(&idx)) << b.error(); ASSERT_TRUE(b.GenerateFunctionVariable(&idx)) << b.error();
EXPECT_EQ(b.GenerateAccessorExpression(&expr), 10u); EXPECT_EQ(b.GenerateAccessorExpression(&expr), 12u);
EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
%3 = OpTypeVector %4 3 %3 = OpTypeVector %4 3
%2 = OpTypePointer Function %3 %2 = OpTypePointer Function %3
%7 = OpTypeInt 32 1 %5 = OpConstantNull %3
%6 = OpTypePointer Function %7 %8 = OpTypeInt 32 1
%9 = OpTypePointer Function %4 %7 = OpTypePointer Function %8
%9 = OpConstantNull %8
%11 = OpTypePointer Function %4
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
R"(%1 = OpVariable %2 Function R"(%1 = OpVariable %2 Function %5
%5 = OpVariable %6 Function %6 = OpVariable %7 Function %9
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%8 = OpLoad %7 %5 R"(%10 = OpLoad %8 %6
%10 = OpAccessChain %9 %1 %8 %12 = OpAccessChain %11 %1 %10
)"); )");
} }
@ -163,22 +166,23 @@ TEST_F(BuilderTest, ArrayAccessor_Dynamic) {
b.push_function(Function{}); b.push_function(Function{});
ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error(); ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error();
EXPECT_EQ(b.GenerateAccessorExpression(&expr), 10u); EXPECT_EQ(b.GenerateAccessorExpression(&expr), 11u);
EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
%3 = OpTypeVector %4 3 %3 = OpTypeVector %4 3
%2 = OpTypePointer Function %3 %2 = OpTypePointer Function %3
%5 = OpTypeInt 32 1 %5 = OpConstantNull %3
%6 = OpConstant %5 1 %6 = OpTypeInt 32 1
%7 = OpConstant %5 2 %7 = OpConstant %6 1
%9 = OpTypePointer Function %4 %8 = OpConstant %6 2
%10 = OpTypePointer Function %4
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
R"(%1 = OpVariable %2 Function R"(%1 = OpVariable %2 Function %5
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%8 = OpIAdd %5 %6 %7 R"(%9 = OpIAdd %6 %7 %8
%10 = OpAccessChain %9 %1 %8 %11 = OpAccessChain %10 %1 %9
)"); )");
} }
@ -211,7 +215,7 @@ TEST_F(BuilderTest, ArrayAccessor_MultiLevel) {
b.push_function(Function{}); b.push_function(Function{});
ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error(); ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error();
EXPECT_EQ(b.GenerateAccessorExpression(&expr), 12u); EXPECT_EQ(b.GenerateAccessorExpression(&expr), 13u);
EXPECT_EQ(DumpInstructions(b.types()), R"(%5 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%5 = OpTypeFloat 32
%4 = OpTypeVector %5 3 %4 = OpTypeVector %5 3
@ -219,16 +223,17 @@ TEST_F(BuilderTest, ArrayAccessor_MultiLevel) {
%7 = OpConstant %6 4 %7 = OpConstant %6 4
%3 = OpTypeArray %4 %7 %3 = OpTypeArray %4 %7
%2 = OpTypePointer Function %3 %2 = OpTypePointer Function %3
%8 = OpTypeInt 32 1 %8 = OpConstantNull %3
%9 = OpConstant %8 3 %9 = OpTypeInt 32 1
%10 = OpConstant %8 2 %10 = OpConstant %9 3
%11 = OpTypePointer Function %5 %11 = OpConstant %9 2
%12 = OpTypePointer Function %5
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
R"(%1 = OpVariable %2 Function R"(%1 = OpVariable %2 Function %8
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%12 = OpAccessChain %11 %1 %9 %10 R"(%13 = OpAccessChain %12 %1 %10 %11
)"); )");
} }
@ -259,7 +264,7 @@ TEST_F(BuilderTest, Accessor_ArrayWithSwizzle) {
Builder b(&mod); Builder b(&mod);
b.push_function(Function{}); b.push_function(Function{});
ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error(); ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error();
EXPECT_EQ(b.GenerateAccessorExpression(&expr), 14u); EXPECT_EQ(b.GenerateAccessorExpression(&expr), 15u);
EXPECT_EQ(DumpInstructions(b.types()), R"(%5 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%5 = OpTypeFloat 32
%4 = OpTypeVector %5 3 %4 = OpTypeVector %5 3
@ -267,18 +272,19 @@ TEST_F(BuilderTest, Accessor_ArrayWithSwizzle) {
%7 = OpConstant %6 4 %7 = OpConstant %6 4
%3 = OpTypeArray %4 %7 %3 = OpTypeArray %4 %7
%2 = OpTypePointer Function %3 %2 = OpTypePointer Function %3
%8 = OpTypeInt 32 1 %8 = OpConstantNull %3
%9 = OpConstant %8 2 %9 = OpTypeInt 32 1
%10 = OpTypePointer Function %4 %10 = OpConstant %9 2
%12 = OpTypeVector %5 2 %11 = OpTypePointer Function %4
%13 = OpTypeVector %5 2
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
R"(%1 = OpVariable %2 Function R"(%1 = OpVariable %2 Function %8
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%11 = OpAccessChain %10 %1 %9 R"(%12 = OpAccessChain %11 %1 %10
%13 = OpLoad %4 %11 %14 = OpLoad %4 %12
%14 = OpVectorShuffle %12 %13 %13 0 1 %15 = OpVectorShuffle %13 %14 %14 0 1
)"); )");
} }
@ -320,20 +326,21 @@ TEST_F(BuilderTest, MemberAccessor) {
b.push_function(Function{}); b.push_function(Function{});
ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error(); ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error();
EXPECT_EQ(b.GenerateAccessorExpression(&expr), 8u); EXPECT_EQ(b.GenerateAccessorExpression(&expr), 9u);
EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
%3 = OpTypeStruct %4 %4 %3 = OpTypeStruct %4 %4
%2 = OpTypePointer Function %3 %2 = OpTypePointer Function %3
%5 = OpTypeInt 32 0 %5 = OpConstantNull %3
%6 = OpConstant %5 1 %6 = OpTypeInt 32 0
%7 = OpTypePointer Function %4 %7 = OpConstant %6 1
%8 = OpTypePointer Function %4
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
R"(%1 = OpVariable %2 Function R"(%1 = OpVariable %2 Function %5
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%8 = OpAccessChain %7 %1 %6 R"(%9 = OpAccessChain %8 %1 %7
)"); )");
} }
@ -385,21 +392,22 @@ TEST_F(BuilderTest, MemberAccessor_Nested) {
b.push_function(Function{}); b.push_function(Function{});
ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error(); ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error();
EXPECT_EQ(b.GenerateAccessorExpression(&expr), 9u); EXPECT_EQ(b.GenerateAccessorExpression(&expr), 10u);
EXPECT_EQ(DumpInstructions(b.types()), R"(%5 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%5 = OpTypeFloat 32
%4 = OpTypeStruct %5 %5 %4 = OpTypeStruct %5 %5
%3 = OpTypeStruct %4 %3 = OpTypeStruct %4
%2 = OpTypePointer Function %3 %2 = OpTypePointer Function %3
%6 = OpTypeInt 32 0 %6 = OpConstantNull %3
%7 = OpConstant %6 0 %7 = OpTypeInt 32 0
%8 = OpTypePointer Function %5 %8 = OpConstant %7 0
%9 = OpTypePointer Function %5
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
R"(%1 = OpVariable %2 Function R"(%1 = OpVariable %2 Function %6
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%9 = OpAccessChain %8 %1 %7 %7 R"(%10 = OpAccessChain %9 %1 %8 %8
)"); )");
} }
@ -454,21 +462,22 @@ TEST_F(BuilderTest, MemberAccessor_Nested_WithAlias) {
b.push_function(Function{}); b.push_function(Function{});
ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error(); ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error();
EXPECT_EQ(b.GenerateAccessorExpression(&expr), 9u); EXPECT_EQ(b.GenerateAccessorExpression(&expr), 10u);
EXPECT_EQ(DumpInstructions(b.types()), R"(%5 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%5 = OpTypeFloat 32
%4 = OpTypeStruct %5 %5 %4 = OpTypeStruct %5 %5
%3 = OpTypeStruct %4 %3 = OpTypeStruct %4
%2 = OpTypePointer Function %3 %2 = OpTypePointer Function %3
%6 = OpTypeInt 32 0 %6 = OpConstantNull %3
%7 = OpConstant %6 0 %7 = OpTypeInt 32 0
%8 = OpTypePointer Function %5 %8 = OpConstant %7 0
%9 = OpTypePointer Function %5
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
R"(%1 = OpVariable %2 Function R"(%1 = OpVariable %2 Function %6
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%9 = OpAccessChain %8 %1 %7 %7 R"(%10 = OpAccessChain %9 %1 %8 %8
)"); )");
} }
@ -532,17 +541,18 @@ TEST_F(BuilderTest, MemberAccessor_Nested_Assignment_LHS) {
%4 = OpTypeStruct %5 %5 %4 = OpTypeStruct %5 %5
%3 = OpTypeStruct %4 %3 = OpTypeStruct %4
%2 = OpTypePointer Function %3 %2 = OpTypePointer Function %3
%6 = OpTypeInt 32 0 %6 = OpConstantNull %3
%7 = OpConstant %6 0 %7 = OpTypeInt 32 0
%8 = OpTypePointer Function %5 %8 = OpConstant %7 0
%10 = OpConstant %5 2 %9 = OpTypePointer Function %5
%11 = OpConstant %5 2
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
R"(%1 = OpVariable %2 Function R"(%1 = OpVariable %2 Function %6
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%9 = OpAccessChain %8 %1 %7 %7 R"(%10 = OpAccessChain %9 %1 %8 %8
OpStore %9 %10 OpStore %10 %11
)"); )");
} }
@ -608,18 +618,20 @@ TEST_F(BuilderTest, MemberAccessor_Nested_Assignment_RHS) {
%4 = OpTypeStruct %5 %5 %4 = OpTypeStruct %5 %5
%3 = OpTypeStruct %4 %3 = OpTypeStruct %4
%2 = OpTypePointer Function %3 %2 = OpTypePointer Function %3
%7 = OpTypePointer Function %5 %6 = OpConstantNull %3
%8 = OpTypeInt 32 0 %8 = OpTypePointer Function %5
%9 = OpConstant %8 0 %9 = OpConstantNull %5
%10 = OpTypeInt 32 0
%11 = OpConstant %10 0
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
R"(%1 = OpVariable %2 Function R"(%1 = OpVariable %2 Function %6
%6 = OpVariable %7 Function %7 = OpVariable %8 Function %9
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%10 = OpAccessChain %7 %1 %9 %9 R"(%12 = OpAccessChain %8 %1 %11 %11
%11 = OpLoad %5 %10 %13 = OpLoad %5 %12
OpStore %6 %11 OpStore %7 %13
)"); )");
} }
@ -645,20 +657,21 @@ TEST_F(BuilderTest, MemberAccessor_Swizzle_Single) {
b.push_function(Function{}); b.push_function(Function{});
ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error(); ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error();
EXPECT_EQ(b.GenerateAccessorExpression(&expr), 8u); EXPECT_EQ(b.GenerateAccessorExpression(&expr), 9u);
EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
%3 = OpTypeVector %4 3 %3 = OpTypeVector %4 3
%2 = OpTypePointer Function %3 %2 = OpTypePointer Function %3
%5 = OpTypeInt 32 0 %5 = OpConstantNull %3
%6 = OpConstant %5 1 %6 = OpTypeInt 32 0
%7 = OpTypePointer Function %4 %7 = OpConstant %6 1
%8 = OpTypePointer Function %4
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
R"(%1 = OpVariable %2 Function R"(%1 = OpVariable %2 Function %5
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%8 = OpAccessChain %7 %1 %6 R"(%9 = OpAccessChain %8 %1 %7
)"); )");
} }
@ -684,19 +697,20 @@ TEST_F(BuilderTest, MemberAccessor_Swizzle_MultipleNames) {
b.push_function(Function{}); b.push_function(Function{});
ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error(); ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error();
EXPECT_EQ(b.GenerateAccessorExpression(&expr), 7u); EXPECT_EQ(b.GenerateAccessorExpression(&expr), 8u);
EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
%3 = OpTypeVector %4 3 %3 = OpTypeVector %4 3
%2 = OpTypePointer Function %3 %2 = OpTypePointer Function %3
%5 = OpTypeVector %4 2 %5 = OpConstantNull %3
%6 = OpTypeVector %4 2
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
R"(%1 = OpVariable %2 Function R"(%1 = OpVariable %2 Function %5
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%6 = OpLoad %3 %1 R"(%7 = OpLoad %3 %1
%7 = OpVectorShuffle %5 %6 %6 1 0 %8 = OpVectorShuffle %6 %7 %7 1 0
)"); )");
} }
@ -724,20 +738,21 @@ TEST_F(BuilderTest, MemberAccessor_Swizzle_of_Swizzle) {
b.push_function(Function{}); b.push_function(Function{});
ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error(); ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error();
EXPECT_EQ(b.GenerateAccessorExpression(&expr), 8u); EXPECT_EQ(b.GenerateAccessorExpression(&expr), 9u);
EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
%3 = OpTypeVector %4 3 %3 = OpTypeVector %4 3
%2 = OpTypePointer Function %3 %2 = OpTypePointer Function %3
%7 = OpTypeVector %4 2 %5 = OpConstantNull %3
%8 = OpTypeVector %4 2
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
R"(%1 = OpVariable %2 Function R"(%1 = OpVariable %2 Function %5
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%5 = OpLoad %3 %1 R"(%6 = OpLoad %3 %1
%6 = OpVectorShuffle %3 %5 %5 1 0 2 %7 = OpVectorShuffle %3 %6 %6 1 0 2
%8 = OpVectorShuffle %7 %6 %6 0 2 %9 = OpVectorShuffle %8 %7 %7 0 2
)"); )");
} }
@ -765,19 +780,20 @@ TEST_F(BuilderTest, MemberAccessor_Member_of_Swizzle) {
b.push_function(Function{}); b.push_function(Function{});
ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error(); ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error();
EXPECT_EQ(b.GenerateAccessorExpression(&expr), 7u); EXPECT_EQ(b.GenerateAccessorExpression(&expr), 8u);
EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
%3 = OpTypeVector %4 3 %3 = OpTypeVector %4 3
%2 = OpTypePointer Function %3 %2 = OpTypePointer Function %3
%5 = OpConstantNull %3
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
R"(%1 = OpVariable %2 Function R"(%1 = OpVariable %2 Function %5
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%5 = OpLoad %3 %1 R"(%6 = OpLoad %3 %1
%6 = OpVectorShuffle %3 %5 %5 1 0 2 %7 = OpVectorShuffle %3 %6 %6 1 0 2
%7 = OpCompositeExtract %4 %6 0 %8 = OpCompositeExtract %4 %7 0
)"); )");
} }
@ -807,21 +823,22 @@ TEST_F(BuilderTest, MemberAccessor_Array_of_Swizzle) {
b.push_function(Function{}); b.push_function(Function{});
ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error(); ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error();
EXPECT_EQ(b.GenerateAccessorExpression(&expr), 9u); EXPECT_EQ(b.GenerateAccessorExpression(&expr), 10u);
EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
%3 = OpTypeVector %4 3 %3 = OpTypeVector %4 3
%2 = OpTypePointer Function %3 %2 = OpTypePointer Function %3
%7 = OpTypeInt 32 1 %5 = OpConstantNull %3
%8 = OpConstant %7 1 %8 = OpTypeInt 32 1
%9 = OpConstant %8 1
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
R"(%1 = OpVariable %2 Function R"(%1 = OpVariable %2 Function %5
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%5 = OpLoad %3 %1 R"(%6 = OpLoad %3 %1
%6 = OpVectorShuffle %3 %5 %5 1 0 2 %7 = OpVectorShuffle %3 %6 %6 1 0 2
%9 = OpVectorExtractDynamic %4 %6 %8 %10 = OpVectorExtractDynamic %4 %7 %9
)"); )");
} }
@ -897,7 +914,7 @@ TEST_F(BuilderTest, Accessor_Mixed_ArrayAndMember) {
b.push_function(Function{}); b.push_function(Function{});
ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error(); ASSERT_TRUE(b.GenerateFunctionVariable(&var)) << b.error();
EXPECT_EQ(b.GenerateAccessorExpression(&expr), 21u); EXPECT_EQ(b.GenerateAccessorExpression(&expr), 22u);
EXPECT_EQ(DumpInstructions(b.types()), R"(%9 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%9 = OpTypeFloat 32
%8 = OpTypeVector %9 3 %8 = OpTypeVector %9 3
@ -910,20 +927,21 @@ TEST_F(BuilderTest, Accessor_Mixed_ArrayAndMember) {
%12 = OpConstant %10 2 %12 = OpConstant %10 2
%3 = OpTypeArray %4 %12 %3 = OpTypeArray %4 %12
%2 = OpTypePointer Function %3 %2 = OpTypePointer Function %3
%13 = OpTypeInt 32 1 %13 = OpConstantNull %3
%14 = OpConstant %13 0 %14 = OpTypeInt 32 1
%15 = OpConstant %10 0 %15 = OpConstant %14 0
%16 = OpConstant %13 2 %16 = OpConstant %10 0
%17 = OpTypePointer Function %8 %17 = OpConstant %14 2
%19 = OpTypeVector %9 2 %18 = OpTypePointer Function %8
%20 = OpTypeVector %9 2
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
R"(%1 = OpVariable %2 Function R"(%1 = OpVariable %2 Function %13
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%18 = OpAccessChain %17 %1 %14 %15 %16 %15 %15 R"(%19 = OpAccessChain %18 %1 %15 %16 %17 %16 %16
%20 = OpLoad %8 %18 %21 = OpLoad %8 %19
%21 = OpVectorShuffle %19 %20 %20 1 0 %22 = OpVectorShuffle %20 %21 %21 1 0
)"); )");
} }

View File

@ -69,11 +69,12 @@ TEST_F(BuilderTest, Assign_Var) {
EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
%2 = OpTypePointer Output %3 %2 = OpTypePointer Output %3
%1 = OpVariable %2 Output %4 = OpConstantNull %3
%4 = OpConstant %3 1 %1 = OpVariable %2 Output %4
%5 = OpConstant %3 1
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %1 %4 EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %1 %5
)"); )");
} }
@ -180,13 +181,14 @@ TEST_F(BuilderTest, Assign_Vector) {
EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
%3 = OpTypeVector %4 3 %3 = OpTypeVector %4 3
%2 = OpTypePointer Output %3 %2 = OpTypePointer Output %3
%1 = OpVariable %2 Output %5 = OpConstantNull %3
%5 = OpConstant %4 1 %1 = OpVariable %2 Output %5
%6 = OpConstant %4 3 %6 = OpConstant %4 1
%7 = OpConstantComposite %3 %5 %5 %6 %7 = OpConstant %4 3
%8 = OpConstantComposite %3 %6 %6 %7
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %1 %7 EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %1 %8
)"); )");
} }
@ -224,16 +226,17 @@ TEST_F(BuilderTest, Assign_Vector_MemberByName) {
EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
%3 = OpTypeVector %4 3 %3 = OpTypeVector %4 3
%2 = OpTypePointer Output %3 %2 = OpTypePointer Output %3
%1 = OpVariable %2 Output %5 = OpConstantNull %3
%5 = OpTypeInt 32 0 %1 = OpVariable %2 Output %5
%6 = OpConstant %5 1 %6 = OpTypeInt 32 0
%7 = OpTypePointer Output %4 %7 = OpConstant %6 1
%9 = OpConstant %4 1 %8 = OpTypePointer Output %4
%10 = OpConstant %4 1
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%8 = OpAccessChain %7 %1 %6 R"(%9 = OpAccessChain %8 %1 %7
OpStore %8 %9 OpStore %9 %10
)"); )");
} }
@ -273,16 +276,17 @@ TEST_F(BuilderTest, Assign_Vector_MemberByIndex) {
EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
%3 = OpTypeVector %4 3 %3 = OpTypeVector %4 3
%2 = OpTypePointer Output %3 %2 = OpTypePointer Output %3
%1 = OpVariable %2 Output %5 = OpConstantNull %3
%5 = OpTypeInt 32 1 %1 = OpVariable %2 Output %5
%6 = OpConstant %5 1 %6 = OpTypeInt 32 1
%7 = OpTypePointer Output %4 %7 = OpConstant %6 1
%9 = OpConstant %4 1 %8 = OpTypePointer Output %4
%10 = OpConstant %4 1
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%8 = OpAccessChain %7 %1 %6 R"(%9 = OpAccessChain %8 %1 %7
OpStore %8 %9 OpStore %9 %10
)"); )");
} }

View File

@ -107,19 +107,20 @@ TEST_F(BuilderTest, Call_GLSLMethod_WithLoad) {
ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error(); ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error();
ASSERT_TRUE(b.GenerateFunction(&func)) << b.error(); ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
EXPECT_EQ(b.GenerateCallExpression(&expr), 9u) << b.error(); EXPECT_EQ(b.GenerateCallExpression(&expr), 10u) << b.error();
EXPECT_EQ(DumpBuilder(b), R"(%1 = OpExtInstImport "GLSL.std.450" EXPECT_EQ(DumpBuilder(b), R"(%1 = OpExtInstImport "GLSL.std.450"
OpName %2 "ident" OpName %2 "ident"
OpName %7 "a_func" OpName %8 "a_func"
%4 = OpTypeFloat 32 %4 = OpTypeFloat 32
%3 = OpTypePointer Private %4 %3 = OpTypePointer Private %4
%2 = OpVariable %3 Private %5 = OpConstantNull %4
%6 = OpTypeVoid %2 = OpVariable %3 Private %5
%5 = OpTypeFunction %6 %7 = OpTypeVoid
%7 = OpFunction %6 None %5 %6 = OpTypeFunction %7
%8 = OpLabel %8 = OpFunction %7 None %6
%10 = OpLoad %4 %2 %9 = OpLabel
%9 = OpExtInst %4 %1 Round %10 %11 = OpLoad %4 %2
%10 = OpExtInst %4 %1 Round %11
OpFunctionEnd OpFunctionEnd
)"); )");
} }

View File

@ -86,16 +86,17 @@ TEST_F(BuilderTest, Cast_WithLoad) {
Builder b(&mod); Builder b(&mod);
b.push_function(Function{}); b.push_function(Function{});
ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error(); ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error();
EXPECT_EQ(b.GenerateCastExpression(&cast), 4u) << b.error(); EXPECT_EQ(b.GenerateCastExpression(&cast), 5u) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1 EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1
%2 = OpTypePointer Private %3 %2 = OpTypePointer Private %3
%1 = OpVariable %2 Private %4 = OpConstantNull %3
%5 = OpTypeFloat 32 %1 = OpVariable %2 Private %4
%6 = OpTypeFloat 32
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%6 = OpLoad %3 %1 R"(%7 = OpLoad %3 %1
%4 = OpConvertSToF %5 %6 %5 = OpConvertSToF %6 %7
)"); )");
} }

View File

@ -106,21 +106,22 @@ TEST_F(BuilderTest, Constructor_Type_NonConstructorParam) {
b.push_function(Function{}); b.push_function(Function{});
ASSERT_TRUE(b.GenerateFunctionVariable(var.get())) << b.error(); ASSERT_TRUE(b.GenerateFunctionVariable(var.get())) << b.error();
EXPECT_EQ(b.GenerateConstructorExpression(&t, false), 7u); EXPECT_EQ(b.GenerateConstructorExpression(&t, false), 8u);
ASSERT_FALSE(b.has_error()) << b.error(); ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
%2 = OpTypePointer Function %3 %2 = OpTypePointer Function %3
%4 = OpTypeVector %3 2 %4 = OpConstantNull %3
%5 = OpConstant %3 1 %5 = OpTypeVector %3 2
%6 = OpConstant %3 1
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
R"(%1 = OpVariable %2 Function R"(%1 = OpVariable %2 Function %4
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%6 = OpLoad %3 %1 R"(%7 = OpLoad %3 %1
%7 = OpCompositeConstruct %4 %5 %6 %8 = OpCompositeConstruct %5 %6 %7
)"); )");
} }
@ -151,24 +152,25 @@ TEST_F(BuilderTest, Constructor_Type_NonConstVector) {
b.push_function(Function{}); b.push_function(Function{});
ASSERT_TRUE(b.GenerateFunctionVariable(var.get())) << b.error(); ASSERT_TRUE(b.GenerateFunctionVariable(var.get())) << b.error();
EXPECT_EQ(b.GenerateConstructorExpression(&t, false), 10u); EXPECT_EQ(b.GenerateConstructorExpression(&t, false), 11u);
ASSERT_FALSE(b.has_error()) << b.error(); ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
%3 = OpTypeVector %4 2 %3 = OpTypeVector %4 2
%2 = OpTypePointer Function %3 %2 = OpTypePointer Function %3
%5 = OpTypeVector %4 4 %5 = OpConstantNull %3
%6 = OpConstant %4 1 %6 = OpTypeVector %4 4
%7 = OpConstant %4 1
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
R"(%1 = OpVariable %2 Function R"(%1 = OpVariable %2 Function %5
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%7 = OpLoad %3 %1 R"(%8 = OpLoad %3 %1
%8 = OpCompositeExtract %4 %7 0 %9 = OpCompositeExtract %4 %8 0
%9 = OpCompositeExtract %4 %7 1 %10 = OpCompositeExtract %4 %8 1
%10 = OpCompositeConstruct %5 %6 %6 %8 %9 %11 = OpCompositeConstruct %6 %7 %7 %9 %10
)"); )");
} }

View File

@ -129,15 +129,16 @@ TEST_F(BuilderTest, EntryPoint_WithInterfaceIds) {
ASSERT_TRUE(b.GenerateEntryPoint(&ep)); ASSERT_TRUE(b.GenerateEntryPoint(&ep));
EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %1 "my_in" EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %1 "my_in"
OpName %4 "my_out" OpName %4 "my_out"
OpName %6 "my_wg" OpName %7 "my_wg"
)"); )");
EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
%2 = OpTypePointer Input %3 %2 = OpTypePointer Input %3
%1 = OpVariable %2 Input %1 = OpVariable %2 Input
%5 = OpTypePointer Output %3 %5 = OpTypePointer Output %3
%4 = OpVariable %5 Output %6 = OpConstantNull %3
%7 = OpTypePointer Workgroup %3 %4 = OpVariable %5 Output %6
%6 = OpVariable %7 Workgroup %8 = OpTypePointer Workgroup %3
%7 = OpVariable %8 Workgroup
)"); )");
EXPECT_EQ(DumpInstructions(b.preamble()), EXPECT_EQ(DumpInstructions(b.preamble()),
R"(OpEntryPoint Vertex %3 "main" %1 %4 R"(OpEntryPoint Vertex %3 "main" %1 %4

View File

@ -54,10 +54,12 @@ TEST_F(BuilderTest, FunctionVar_NoStorageClass) {
)"); )");
EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
%2 = OpTypePointer Function %3 %2 = OpTypePointer Function %3
%4 = OpConstantNull %3
)"); )");
const auto& func = b.functions()[0]; const auto& func = b.functions()[0];
EXPECT_EQ(DumpInstructions(func.variables()), R"(%1 = OpVariable %2 Function EXPECT_EQ(DumpInstructions(func.variables()),
R"(%1 = OpVariable %2 Function %4
)"); )");
} }
@ -99,9 +101,10 @@ TEST_F(BuilderTest, FunctionVar_WithConstantConstructor) {
%4 = OpConstant %2 3 %4 = OpConstant %2 3
%5 = OpConstantComposite %1 %3 %3 %4 %5 = OpConstantComposite %1 %3 %3 %4
%7 = OpTypePointer Function %2 %7 = OpTypePointer Function %2
%8 = OpConstantNull %2
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
R"(%6 = OpVariable %7 Function R"(%6 = OpVariable %7 Function %8
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %6 %5 EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %6 %5
)"); )");
@ -147,9 +150,10 @@ TEST_F(BuilderTest, FunctionVar_WithNonConstantConstructor) {
%3 = OpConstant %2 1 %3 = OpConstant %2 1
%4 = OpConstant %2 3 %4 = OpConstant %2 3
%8 = OpTypePointer Function %1 %8 = OpTypePointer Function %1
%9 = OpConstantNull %1
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), EXPECT_EQ(DumpInstructions(b.functions()[0].variables()),
R"(%7 = OpVariable %8 Function R"(%7 = OpVariable %8 Function %9
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%5 = OpFAdd %2 %4 %4 R"(%5 = OpFAdd %2 %4 %4

View File

@ -52,7 +52,8 @@ TEST_F(BuilderTest, GlobalVar_NoStorageClass) {
)"); )");
EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
%2 = OpTypePointer Private %3 %2 = OpTypePointer Private %3
%1 = OpVariable %2 Private %4 = OpConstantNull %3
%1 = OpVariable %2 Private %4
)"); )");
} }
@ -67,7 +68,23 @@ TEST_F(BuilderTest, GlobalVar_WithStorageClass) {
)"); )");
EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
%2 = OpTypePointer Output %3 %2 = OpTypePointer Output %3
%1 = OpVariable %2 Output %4 = OpConstantNull %3
%1 = OpVariable %2 Output %4
)");
}
TEST_F(BuilderTest, GlobalVar_WithStorageClass_Input) {
ast::type::F32Type f32;
ast::Variable v("var", ast::StorageClass::kInput, &f32);
ast::Module mod;
Builder b(&mod);
EXPECT_TRUE(b.GenerateGlobalVariable(&v)) << b.error();
EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %1 "var"
)");
EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
%2 = OpTypePointer Input %3
%1 = OpVariable %2 Input
)"); )");
} }
@ -167,7 +184,8 @@ TEST_F(BuilderTest, GlobalVar_WithLocation) {
)"); )");
EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
%2 = OpTypePointer Output %3 %2 = OpTypePointer Output %3
%1 = OpVariable %2 Output %4 = OpConstantNull %3
%1 = OpVariable %2 Output %4
)"); )");
} }
@ -192,7 +210,8 @@ OpDecorate %1 DescriptorSet 3
)"); )");
EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
%2 = OpTypePointer Output %3 %2 = OpTypePointer Output %3
%1 = OpVariable %2 Output %4 = OpConstantNull %3
%1 = OpVariable %2 Output %4
)"); )");
} }
@ -216,7 +235,8 @@ TEST_F(BuilderTest, GlobalVar_WithBuiltin) {
)"); )");
EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
%2 = OpTypePointer Output %3 %2 = OpTypePointer Output %3
%1 = OpVariable %2 Output %4 = OpConstantNull %3
%1 = OpVariable %2 Output %4
)"); )");
} }

View File

@ -96,7 +96,8 @@ TEST_F(BuilderTest, IdentifierExpression_GlobalVar) {
)"); )");
EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
%2 = OpTypePointer Output %3 %2 = OpTypePointer Output %3
%1 = OpVariable %2 Output %4 = OpConstantNull %3
%1 = OpVariable %2 Output %4
)"); )");
ast::IdentifierExpression expr("var"); ast::IdentifierExpression expr("var");
@ -161,10 +162,12 @@ TEST_F(BuilderTest, IdentifierExpression_FunctionVar) {
)"); )");
EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32 EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
%2 = OpTypePointer Function %3 %2 = OpTypePointer Function %3
%4 = OpConstantNull %3
)"); )");
const auto& func = b.functions()[0]; const auto& func = b.functions()[0];
EXPECT_EQ(DumpInstructions(func.variables()), R"(%1 = OpVariable %2 Function EXPECT_EQ(DumpInstructions(func.variables()),
R"(%1 = OpVariable %2 Function %4
)"); )");
ast::IdentifierExpression expr("var"); ast::IdentifierExpression expr("var");
@ -195,15 +198,16 @@ TEST_F(BuilderTest, IdentifierExpression_Load) {
b.push_function(Function{}); b.push_function(Function{});
ASSERT_TRUE(b.GenerateGlobalVariable(&var)) << b.error(); ASSERT_TRUE(b.GenerateGlobalVariable(&var)) << b.error();
EXPECT_EQ(b.GenerateBinaryExpression(&expr), 6u) << b.error(); EXPECT_EQ(b.GenerateBinaryExpression(&expr), 7u) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1 EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1
%2 = OpTypePointer Private %3 %2 = OpTypePointer Private %3
%1 = OpVariable %2 Private %4 = OpConstantNull %3
%1 = OpVariable %2 Private %4
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%4 = OpLoad %3 %1 R"(%5 = OpLoad %3 %1
%5 = OpLoad %3 %1 %6 = OpLoad %3 %1
%6 = OpIAdd %3 %4 %5 %7 = OpIAdd %3 %5 %6
)"); )");
} }

View File

@ -107,18 +107,19 @@ TEST_F(BuilderTest, If_WithStatements) {
EXPECT_TRUE(b.GenerateIfStatement(&expr)) << b.error(); EXPECT_TRUE(b.GenerateIfStatement(&expr)) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1 EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1
%2 = OpTypePointer Private %3 %2 = OpTypePointer Private %3
%1 = OpVariable %2 Private %4 = OpConstantNull %3
%4 = OpTypeBool %1 = OpVariable %2 Private %4
%5 = OpConstantTrue %4 %5 = OpTypeBool
%8 = OpConstant %3 2 %6 = OpConstantTrue %5
%9 = OpConstant %3 2
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(OpSelectionMerge %6 None R"(OpSelectionMerge %7 None
OpBranchConditional %5 %7 %6 OpBranchConditional %6 %8 %7
%8 = OpLabel
OpStore %1 %9
OpBranch %7
%7 = OpLabel %7 = OpLabel
OpStore %1 %8
OpBranch %6
%6 = OpLabel
)"); )");
} }
@ -170,22 +171,23 @@ TEST_F(BuilderTest, If_WithElse) {
EXPECT_TRUE(b.GenerateIfStatement(&expr)) << b.error(); EXPECT_TRUE(b.GenerateIfStatement(&expr)) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1 EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1
%2 = OpTypePointer Private %3 %2 = OpTypePointer Private %3
%1 = OpVariable %2 Private %4 = OpConstantNull %3
%4 = OpTypeBool %1 = OpVariable %2 Private %4
%5 = OpConstantTrue %4 %5 = OpTypeBool
%9 = OpConstant %3 2 %6 = OpConstantTrue %5
%10 = OpConstant %3 3 %10 = OpConstant %3 2
%11 = OpConstant %3 3
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(OpSelectionMerge %6 None R"(OpSelectionMerge %7 None
OpBranchConditional %5 %7 %8 OpBranchConditional %6 %8 %9
%7 = OpLabel
OpStore %1 %9
OpBranch %6
%8 = OpLabel %8 = OpLabel
OpStore %1 %10 OpStore %1 %10
OpBranch %6 OpBranch %7
%6 = OpLabel %9 = OpLabel
OpStore %1 %11
OpBranch %7
%7 = OpLabel
)"); )");
} }
@ -240,27 +242,28 @@ TEST_F(BuilderTest, If_WithElseIf) {
EXPECT_TRUE(b.GenerateIfStatement(&expr)) << b.error(); EXPECT_TRUE(b.GenerateIfStatement(&expr)) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1 EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1
%2 = OpTypePointer Private %3 %2 = OpTypePointer Private %3
%1 = OpVariable %2 Private %4 = OpConstantNull %3
%4 = OpTypeBool %1 = OpVariable %2 Private %4
%5 = OpConstantTrue %4 %5 = OpTypeBool
%9 = OpConstant %3 2 %6 = OpConstantTrue %5
%12 = OpConstant %3 3 %10 = OpConstant %3 2
%13 = OpConstant %3 3
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(OpSelectionMerge %6 None R"(OpSelectionMerge %7 None
OpBranchConditional %5 %7 %8 OpBranchConditional %6 %8 %9
%7 = OpLabel
OpStore %1 %9
OpBranch %6
%8 = OpLabel %8 = OpLabel
OpSelectionMerge %10 None OpStore %1 %10
OpBranchConditional %5 %11 %10 OpBranch %7
%9 = OpLabel
OpSelectionMerge %11 None
OpBranchConditional %6 %12 %11
%12 = OpLabel
OpStore %1 %13
OpBranch %11
%11 = OpLabel %11 = OpLabel
OpStore %1 %12 OpBranch %7
OpBranch %10 %7 = OpLabel
%10 = OpLabel
OpBranch %6
%6 = OpLabel
)"); )");
} }
@ -334,41 +337,42 @@ TEST_F(BuilderTest, If_WithMultiple) {
EXPECT_TRUE(b.GenerateIfStatement(&expr)) << b.error(); EXPECT_TRUE(b.GenerateIfStatement(&expr)) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1 EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1
%2 = OpTypePointer Private %3 %2 = OpTypePointer Private %3
%1 = OpVariable %2 Private %4 = OpConstantNull %3
%4 = OpTypeBool %1 = OpVariable %2 Private %4
%5 = OpConstantTrue %4 %5 = OpTypeBool
%9 = OpConstant %3 2 %6 = OpConstantTrue %5
%13 = OpConstant %3 3 %10 = OpConstant %3 2
%14 = OpConstantFalse %4 %14 = OpConstant %3 3
%18 = OpConstant %3 4 %15 = OpConstantFalse %5
%19 = OpConstant %3 5 %19 = OpConstant %3 4
%20 = OpConstant %3 5
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(OpSelectionMerge %6 None R"(OpSelectionMerge %7 None
OpBranchConditional %5 %7 %8 OpBranchConditional %6 %8 %9
%7 = OpLabel
OpStore %1 %9
OpBranch %6
%8 = OpLabel %8 = OpLabel
OpSelectionMerge %10 None OpStore %1 %10
OpBranchConditional %5 %11 %12 OpBranch %7
%11 = OpLabel %9 = OpLabel
OpStore %1 %13 OpSelectionMerge %11 None
OpBranch %10 OpBranchConditional %6 %12 %13
%12 = OpLabel %12 = OpLabel
OpSelectionMerge %15 None OpStore %1 %14
OpBranchConditional %14 %16 %17 OpBranch %11
%16 = OpLabel %13 = OpLabel
OpStore %1 %18 OpSelectionMerge %16 None
OpBranch %15 OpBranchConditional %15 %17 %18
%17 = OpLabel %17 = OpLabel
OpStore %1 %19 OpStore %1 %19
OpBranch %15 OpBranch %16
%15 = OpLabel %18 = OpLabel
OpBranch %10 OpStore %1 %20
%10 = OpLabel OpBranch %16
OpBranch %6 %16 = OpLabel
%6 = OpLabel OpBranch %11
%11 = OpLabel
OpBranch %7
%7 = OpLabel
)"); )");
} }

View File

@ -94,20 +94,21 @@ TEST_F(BuilderTest, Loop_WithoutContinuing) {
EXPECT_TRUE(b.GenerateLoopStatement(&expr)) << b.error(); EXPECT_TRUE(b.GenerateLoopStatement(&expr)) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1 EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1
%2 = OpTypePointer Private %3 %2 = OpTypePointer Private %3
%1 = OpVariable %2 Private %4 = OpConstantNull %3
%8 = OpConstant %3 2 %1 = OpVariable %2 Private %4
%9 = OpConstant %3 2
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(OpBranch %4 R"(OpBranch %5
%4 = OpLabel %5 = OpLabel
OpLoopMerge %5 %6 None OpLoopMerge %6 %7 None
OpBranch %8
%8 = OpLabel
OpStore %1 %9
OpBranch %7 OpBranch %7
%7 = OpLabel %7 = OpLabel
OpStore %1 %8 OpBranch %5
OpBranch %6
%6 = OpLabel %6 = OpLabel
OpBranch %4
%5 = OpLabel
)"); )");
} }
@ -149,22 +150,23 @@ TEST_F(BuilderTest, Loop_WithContinuing) {
EXPECT_TRUE(b.GenerateLoopStatement(&expr)) << b.error(); EXPECT_TRUE(b.GenerateLoopStatement(&expr)) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1 EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1
%2 = OpTypePointer Private %3 %2 = OpTypePointer Private %3
%1 = OpVariable %2 Private %4 = OpConstantNull %3
%8 = OpConstant %3 2 %1 = OpVariable %2 Private %4
%9 = OpConstant %3 3 %9 = OpConstant %3 2
%10 = OpConstant %3 3
)"); )");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(OpBranch %4 R"(OpBranch %5
%4 = OpLabel %5 = OpLabel
OpLoopMerge %5 %6 None OpLoopMerge %6 %7 None
OpBranch %8
%8 = OpLabel
OpStore %1 %9
OpBranch %7 OpBranch %7
%7 = OpLabel %7 = OpLabel
OpStore %1 %8 OpStore %1 %10
OpBranch %6 OpBranch %5
%6 = OpLabel %6 = OpLabel
OpStore %1 %9
OpBranch %4
%5 = OpLabel
)"); )");
} }