Import Tint changes from Dawn
Changes:
- 5e8ee8581d802b08c189858eadbad6de8a3090fc [ir] Handle some unexpected ast nodes. by dan sinclair <dsinclair@chromium.org>
- 97b98619e818b2cd8896ca4b2b15e3cb902bdcd4 [ir] Convert tests to use disassembler. by dan sinclair <dsinclair@chromium.org>
- e3992f240821992499bb5ce7ffe9e5fdcee7392f [ir] Cleanup disassembler output by dan sinclair <dsinclair@chromium.org>
- a7644371384f47db8b2486087260523cec3a4c82 [ir] Allow branching arguments. by dan sinclair <dsinclair@chromium.org>
- 1d04cf841c5d628fe0fc19e5626fbf95b519bf55 Enable the 1D -> 2D texture transform in GLSL writer. by Stephen White <senorblanco@chromium.org>
- fb8a6dbb5d15d208d03f25af94cf41892c27ed22 Implement a Texture1D -> Texture2D transform. by Stephen White <senorblanco@chromium.org>
- 4a62d91a9e45ef7ca376c9ac9c9eb67a28ef55b2 [ir] Remove program in ir tests. by dan sinclair <dsinclair@chromium.org>
- 8bcb4e9e77db4f69a2b55926e58f64c39415f3d0 [ir] Remove ast pointers. by dan sinclair <dsinclair@chromium.org>
- 0258f276b50d708bcc4bf3ce7550252b9c6bb1aa Convert CaseSelector to IR. by dan sinclair <dsinclair@chromium.org>
- 19bc66a3754ab5b3e2b6dd0627edad0d14601329 [ir] Cleanup some comments and un-needed code. by dan sinclair <dsinclair@chromium.org>
GitOrigin-RevId: 5e8ee8581d802b08c189858eadbad6de8a3090fc
Change-Id: I2c0de371e95d1d19f660c13d8fe2c4d0fd8bda8e
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/116372
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index f57a2d7..f3fd25b 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -535,6 +535,8 @@
"transform/std140.h",
"transform/substitute_override.cc",
"transform/substitute_override.h",
+ "transform/texture_1d_to_2d.cc",
+ "transform/texture_1d_to_2d.h",
"transform/transform.cc",
"transform/transform.h",
"transform/truncate_interstage_variables.cc",
@@ -1328,6 +1330,7 @@
"transform/std140_test.cc",
"transform/substitute_override_test.cc",
"transform/test_helper.h",
+ "transform/texture_1d_to_2d_test.cc",
"transform/transform_test.cc",
"transform/truncate_interstage_variables_test.cc",
"transform/unshadow_test.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index f35aa57..2f7bda7 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -441,6 +441,8 @@
transform/std140.h
transform/substitute_override.cc
transform/substitute_override.h
+ transform/texture_1d_to_2d.cc
+ transform/texture_1d_to_2d.h
transform/transform.cc
transform/transform.h
transform/truncate_interstage_variables.cc
@@ -1261,6 +1263,7 @@
transform/std140_test.cc
transform/substitute_override_test.cc
transform/test_helper.h
+ transform/texture_1d_to_2d_test.cc
transform/truncate_interstage_variables_test.cc
transform/unshadow_test.cc
transform/var_for_dynamic_index_test.cc
diff --git a/src/tint/ir/binary_test.cc b/src/tint/ir/binary_test.cc
index f234879..68341ad 100644
--- a/src/tint/ir/binary_test.cc
+++ b/src/tint/ir/binary_test.cc
@@ -48,7 +48,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
std::stringstream str;
- instr->ToString(str, program->Symbols());
+ instr->ToString(str, b.builder.ir.symbols);
EXPECT_EQ(str.str(), "%42 (i32) = 4 & 2");
}
@@ -75,7 +75,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
std::stringstream str;
- instr->ToString(str, program->Symbols());
+ instr->ToString(str, b.builder.ir.symbols);
EXPECT_EQ(str.str(), "%42 (i32) = 4 | 2");
}
@@ -102,7 +102,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
std::stringstream str;
- instr->ToString(str, program->Symbols());
+ instr->ToString(str, b.builder.ir.symbols);
EXPECT_EQ(str.str(), "%42 (i32) = 4 ^ 2");
}
@@ -129,7 +129,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
std::stringstream str;
- instr->ToString(str, program->Symbols());
+ instr->ToString(str, b.builder.ir.symbols);
EXPECT_EQ(str.str(), "%42 (bool) = 4 && 2");
}
@@ -156,7 +156,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
std::stringstream str;
- instr->ToString(str, program->Symbols());
+ instr->ToString(str, b.builder.ir.symbols);
EXPECT_EQ(str.str(), "%42 (bool) = 4 || 2");
}
@@ -183,7 +183,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
std::stringstream str;
- instr->ToString(str, program->Symbols());
+ instr->ToString(str, b.builder.ir.symbols);
EXPECT_EQ(str.str(), "%42 (bool) = 4 == 2");
}
@@ -210,7 +210,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
std::stringstream str;
- instr->ToString(str, program->Symbols());
+ instr->ToString(str, b.builder.ir.symbols);
EXPECT_EQ(str.str(), "%42 (bool) = 4 != 2");
}
@@ -237,7 +237,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
std::stringstream str;
- instr->ToString(str, program->Symbols());
+ instr->ToString(str, b.builder.ir.symbols);
EXPECT_EQ(str.str(), "%42 (bool) = 4 < 2");
}
@@ -264,7 +264,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
std::stringstream str;
- instr->ToString(str, program->Symbols());
+ instr->ToString(str, b.builder.ir.symbols);
EXPECT_EQ(str.str(), "%42 (bool) = 4 > 2");
}
@@ -291,7 +291,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
std::stringstream str;
- instr->ToString(str, program->Symbols());
+ instr->ToString(str, b.builder.ir.symbols);
EXPECT_EQ(str.str(), "%42 (bool) = 4 <= 2");
}
@@ -318,7 +318,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
std::stringstream str;
- instr->ToString(str, program->Symbols());
+ instr->ToString(str, b.builder.ir.symbols);
EXPECT_EQ(str.str(), "%42 (bool) = 4 >= 2");
}
@@ -345,7 +345,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
std::stringstream str;
- instr->ToString(str, program->Symbols());
+ instr->ToString(str, b.builder.ir.symbols);
EXPECT_EQ(str.str(), "%42 (i32) = 4 << 2");
}
@@ -372,7 +372,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
std::stringstream str;
- instr->ToString(str, program->Symbols());
+ instr->ToString(str, b.builder.ir.symbols);
EXPECT_EQ(str.str(), "%42 (i32) = 4 >> 2");
}
@@ -399,7 +399,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
std::stringstream str;
- instr->ToString(str, program->Symbols());
+ instr->ToString(str, b.builder.ir.symbols);
EXPECT_EQ(str.str(), "%42 (i32) = 4 + 2");
}
@@ -426,7 +426,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
std::stringstream str;
- instr->ToString(str, program->Symbols());
+ instr->ToString(str, b.builder.ir.symbols);
EXPECT_EQ(str.str(), "%42 (i32) = 4 - 2");
}
@@ -453,7 +453,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
std::stringstream str;
- instr->ToString(str, program->Symbols());
+ instr->ToString(str, b.builder.ir.symbols);
EXPECT_EQ(str.str(), "%42 (i32) = 4 * 2");
}
@@ -480,7 +480,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
std::stringstream str;
- instr->ToString(str, program->Symbols());
+ instr->ToString(str, b.builder.ir.symbols);
EXPECT_EQ(str.str(), "%42 (i32) = 4 / 2");
}
@@ -507,7 +507,7 @@
EXPECT_EQ(2_i, rhs->As<constant::Scalar<i32>>()->ValueAs<i32>());
std::stringstream str;
- instr->ToString(str, program->Symbols());
+ instr->ToString(str, b.builder.ir.symbols);
EXPECT_EQ(str.str(), "%42 (i32) = 4 % 2");
}
diff --git a/src/tint/ir/bitcast_test.cc b/src/tint/ir/bitcast_test.cc
index 91c5c4b..a6924df 100644
--- a/src/tint/ir/bitcast_test.cc
+++ b/src/tint/ir/bitcast_test.cc
@@ -41,7 +41,7 @@
EXPECT_EQ(4_i, val->As<constant::Scalar<i32>>()->ValueAs<i32>());
std::stringstream str;
- instr->ToString(str, program->Symbols());
+ instr->ToString(str, b.builder.ir.symbols);
EXPECT_EQ(str.str(), "%42 (i32) = bitcast(4)");
}
diff --git a/src/tint/ir/block.h b/src/tint/ir/block.h
index ac9028b..4258167 100644
--- a/src/tint/ir/block.h
+++ b/src/tint/ir/block.h
@@ -15,6 +15,7 @@
#ifndef SRC_TINT_IR_BLOCK_H_
#define SRC_TINT_IR_BLOCK_H_
+#include "src/tint/ir/branch.h"
#include "src/tint/ir/flow_node.h"
#include "src/tint/ir/instruction.h"
#include "src/tint/utils/vector.h"
@@ -30,8 +31,12 @@
Block();
~Block() override;
+ /// @returns true if this is a dead block. This can happen in the case like a loop merge block
+ /// which is never reached.
+ bool IsDead() const { return branch.target == nullptr; }
+
/// The node this block branches too.
- const FlowNode* branch_target = nullptr;
+ Branch branch = {};
/// The instructions in the block
utils::Vector<const Instruction*, 16> instructions;
diff --git a/src/tint/ir/branch.h b/src/tint/ir/branch.h
new file mode 100644
index 0000000..4fc8d7c
--- /dev/null
+++ b/src/tint/ir/branch.h
@@ -0,0 +1,36 @@
+// Copyright 2023 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_TINT_IR_BRANCH_H_
+#define SRC_TINT_IR_BRANCH_H_
+
+#include "src/tint/ir/flow_node.h"
+#include "src/tint/ir/value.h"
+
+namespace tint::ir {
+
+/// A information on a branch to another block
+struct Branch {
+ /// The block being branched too.
+ FlowNode* target = nullptr;
+
+ /// The arguments provided for that branch. These arguments could be the
+ /// return value in the case of a branch to the terminator, or they could
+ /// be the basic block arguments passed into the block.
+ utils::Vector<Value*, 2> args;
+};
+
+} // namespace tint::ir
+
+#endif // SRC_TINT_IR_BRANCH_H_
diff --git a/src/tint/ir/builder.cc b/src/tint/ir/builder.cc
index e65c2fc..1452dc4 100644
--- a/src/tint/ir/builder.cc
+++ b/src/tint/ir/builder.cc
@@ -17,11 +17,10 @@
#include <utility>
#include "src/tint/ir/builder_impl.h"
-#include "src/tint/program.h"
namespace tint::ir {
-Builder::Builder(const Program* prog) : ir(prog) {}
+Builder::Builder() {}
Builder::Builder(Module&& mod) : ir(std::move(mod)) {}
@@ -35,8 +34,8 @@
return ir.flow_nodes.Create<Terminator>();
}
-Function* Builder::CreateFunction(const ast::Function* ast_func) {
- auto* ir_func = ir.flow_nodes.Create<Function>(ast_func);
+Function* Builder::CreateFunction() {
+ auto* ir_func = ir.flow_nodes.Create<Function>();
ir_func->start_target = CreateBlock();
ir_func->end_target = CreateTerminator();
@@ -46,50 +45,51 @@
return ir_func;
}
-If* Builder::CreateIf(const ast::Statement* stmt) {
- auto* ir_if = ir.flow_nodes.Create<If>(stmt);
- ir_if->true_target = CreateBlock();
- ir_if->false_target = CreateBlock();
- ir_if->merge_target = CreateBlock();
+If* Builder::CreateIf() {
+ auto* ir_if = ir.flow_nodes.Create<If>();
+ ir_if->true_.target = CreateBlock();
+ ir_if->false_.target = CreateBlock();
+ ir_if->merge.target = CreateBlock();
// An if always branches to both the true and false block.
- ir_if->true_target->inbound_branches.Push(ir_if);
- ir_if->false_target->inbound_branches.Push(ir_if);
+ ir_if->true_.target->inbound_branches.Push(ir_if);
+ ir_if->false_.target->inbound_branches.Push(ir_if);
return ir_if;
}
-Loop* Builder::CreateLoop(const ast::Statement* stmt) {
- auto* ir_loop = ir.flow_nodes.Create<Loop>(stmt);
- ir_loop->start_target = CreateBlock();
- ir_loop->continuing_target = CreateBlock();
- ir_loop->merge_target = CreateBlock();
+Loop* Builder::CreateLoop() {
+ auto* ir_loop = ir.flow_nodes.Create<Loop>();
+ ir_loop->start.target = CreateBlock();
+ ir_loop->continuing.target = CreateBlock();
+ ir_loop->merge.target = CreateBlock();
// A loop always branches to the start block.
- ir_loop->start_target->inbound_branches.Push(ir_loop);
+ ir_loop->start.target->inbound_branches.Push(ir_loop);
return ir_loop;
}
-Switch* Builder::CreateSwitch(const ast::SwitchStatement* stmt) {
- auto* ir_switch = ir.flow_nodes.Create<Switch>(stmt);
- ir_switch->merge_target = CreateBlock();
+Switch* Builder::CreateSwitch() {
+ auto* ir_switch = ir.flow_nodes.Create<Switch>();
+ ir_switch->merge.target = CreateBlock();
return ir_switch;
}
-Block* Builder::CreateCase(Switch* s, utils::VectorRef<const ast::CaseSelector*> selectors) {
- s->cases.Push(Switch::Case{selectors, CreateBlock()});
+Block* Builder::CreateCase(Switch* s, utils::VectorRef<Switch::CaseSelector> selectors) {
+ s->cases.Push(Switch::Case{selectors, {CreateBlock(), utils::Empty}});
- Block* b = s->cases.Back().start_target;
+ Block* b = s->cases.Back().start.target->As<Block>();
// Switch branches into the case block
b->inbound_branches.Push(s);
return b;
}
-void Builder::Branch(Block* from, FlowNode* to) {
+void Builder::Branch(Block* from, FlowNode* to, utils::VectorRef<Value*> args) {
TINT_ASSERT(IR, from);
TINT_ASSERT(IR, to);
- from->branch_target = to;
+ from->branch.target = to;
+ from->branch.args = args;
to->inbound_branches.Push(from);
}
diff --git a/src/tint/ir/builder.h b/src/tint/ir/builder.h
index ad9e2e1..147e4c4 100644
--- a/src/tint/ir/builder.h
+++ b/src/tint/ir/builder.h
@@ -35,22 +35,13 @@
#include "src/tint/type/i32.h"
#include "src/tint/type/u32.h"
-// Forward Declarations
-namespace tint {
-class Program;
-} // namespace tint
-namespace tint::ast {
-class CaseSelector;
-} // namespace tint::ast
-
namespace tint::ir {
-/// Builds an ir::Module from a given Program
+/// Builds an ir::Module
class Builder {
public:
/// Constructor
- /// @param prog the program this ir is associated with
- explicit Builder(const Program* prog);
+ Builder();
/// Constructor
/// @param mod the ir::Module to wrap with this builder
explicit Builder(Module&& mod);
@@ -63,36 +54,33 @@
/// @returns a new terminator flow node
Terminator* CreateTerminator();
- /// Creates a function flow node for the given ast::Function
- /// @param func the ast::Function
+ /// Creates a function flow node
/// @returns the flow node
- Function* CreateFunction(const ast::Function* func);
+ Function* CreateFunction();
- /// Creates an if flow node for the given ast::IfStatement or ast::BreakIfStatement
- /// @param stmt the ast::IfStatement or ast::BreakIfStatement
+ /// Creates an if flow node
/// @returns the flow node
- If* CreateIf(const ast::Statement* stmt);
+ If* CreateIf();
- /// Creates a loop flow node for the given ast loop, while or for statement
- /// @param stmt the ast loop, while or for statement
+ /// Creates a loop flow node
/// @returns the flow node
- Loop* CreateLoop(const ast::Statement* stmt);
+ Loop* CreateLoop();
- /// Creates a switch flow node for the given ast::SwitchStatement
- /// @param stmt the ast::SwitchStatment
+ /// Creates a switch flow node
/// @returns the flow node
- Switch* CreateSwitch(const ast::SwitchStatement* stmt);
+ Switch* CreateSwitch();
/// Creates a case flow node for the given case branch.
/// @param s the switch to create the case into
/// @param selectors the case selectors for the case statement
/// @returns the start block for the case flow node
- Block* CreateCase(Switch* s, utils::VectorRef<const ast::CaseSelector*> selectors);
+ Block* CreateCase(Switch* s, utils::VectorRef<Switch::CaseSelector> selectors);
/// Branches the given block to the given flow node.
/// @param from the block to branch from
/// @param to the node to branch too
- void Branch(Block* from, FlowNode* to);
+ /// @param args arguments to the branch
+ void Branch(Block* from, FlowNode* to, utils::VectorRef<Value*> args);
/// Creates a constant::Value
/// @param args the arguments
diff --git a/src/tint/ir/builder_impl.cc b/src/tint/ir/builder_impl.cc
index 7c4c63f1..0bcaf24 100644
--- a/src/tint/ir/builder_impl.cc
+++ b/src/tint/ir/builder_impl.cc
@@ -25,13 +25,18 @@
#include "src/tint/ast/float_literal_expression.h"
#include "src/tint/ast/for_loop_statement.h"
#include "src/tint/ast/function.h"
+#include "src/tint/ast/id_attribute.h"
#include "src/tint/ast/if_statement.h"
#include "src/tint/ast/int_literal_expression.h"
#include "src/tint/ast/literal_expression.h"
#include "src/tint/ast/loop_statement.h"
+#include "src/tint/ast/override.h"
#include "src/tint/ast/return_statement.h"
#include "src/tint/ast/statement.h"
#include "src/tint/ast/static_assert.h"
+#include "src/tint/ast/struct.h"
+#include "src/tint/ast/struct_member_align_attribute.h"
+#include "src/tint/ast/struct_member_size_attribute.h"
#include "src/tint/ast/switch_statement.h"
#include "src/tint/ast/variable_decl_statement.h"
#include "src/tint/ast/while_statement.h"
@@ -44,6 +49,8 @@
#include "src/tint/program.h"
#include "src/tint/sem/expression.h"
#include "src/tint/sem/module.h"
+#include "src/tint/sem/switch_statement.h"
+#include "src/tint/type/void.h"
namespace tint::ir {
namespace {
@@ -63,7 +70,7 @@
};
bool IsBranched(const Block* b) {
- return b->branch_target != nullptr;
+ return b->branch.target != nullptr;
}
bool IsConnected(const FlowNode* b) {
@@ -84,18 +91,18 @@
} // namespace
BuilderImpl::BuilderImpl(const Program* program)
- : builder(program),
+ : program_(program),
clone_ctx_{
type::CloneContext{{&program->Symbols()}, {&builder.ir.symbols, &builder.ir.types}},
{&builder.ir.constants}} {}
BuilderImpl::~BuilderImpl() = default;
-void BuilderImpl::BranchTo(FlowNode* node) {
+void BuilderImpl::BranchTo(FlowNode* node, utils::VectorRef<Value*> args) {
TINT_ASSERT(IR, current_flow_block);
TINT_ASSERT(IR, !IsBranched(current_flow_block));
- builder.Branch(current_flow_block, node);
+ builder.Branch(current_flow_block, node, args);
current_flow_block = nullptr;
}
@@ -121,20 +128,33 @@
return nullptr;
}
+Symbol BuilderImpl::CloneSymbol(Symbol sym) const {
+ return clone_ctx_.type_ctx.dst.st->Register(clone_ctx_.type_ctx.src.st->NameFor(sym));
+}
+
ResultType BuilderImpl::Build() {
- auto* sem = builder.ir.program->Sem().Module();
+ auto* sem = program_->Sem().Module();
for (auto* decl : sem->DependencyOrderedDeclarations()) {
bool ok = tint::Switch(
decl, //
- // [&](const ast::Struct* str) { },
+ [&](const ast::Struct*) {
+ // Will be encoded into the `type::Struct` when used. We will then hoist all
+ // used structs up to module scope when converting IR.
+ return true;
+ },
[&](const ast::Alias*) {
// Folded away and doesn't appear in the IR.
return true;
},
- // [&](const ast::Variable* var) { },
+ // [&](const ast::Variable* var) {
+ // TODO(dsinclair): Implement
+ // },
[&](const ast::Function* func) { return EmitFunction(func); },
- // [&](const ast::Enable*) { },
+ // [&](const ast::Enable*) {
+ // TODO(dsinclair): Implement? I think these need to be passed along so further stages
+ // know what is enabled.
+ // },
[&](const ast::StaticAssert*) {
// Evaluated by the resolver, drop from the IR.
return true;
@@ -154,10 +174,11 @@
}
bool BuilderImpl::EmitFunction(const ast::Function* ast_func) {
- // The flow stack should have been emptied when the previous function finshed building.
+ // The flow stack should have been emptied when the previous function finished building.
TINT_ASSERT(IR, flow_stack.IsEmpty());
- auto* ir_func = builder.CreateFunction(ast_func);
+ auto* ir_func = builder.CreateFunction();
+ ir_func->name = CloneSymbol(ast_func->symbol);
current_function_ = ir_func;
builder.ir.functions.Push(ir_func);
@@ -175,6 +196,10 @@
return false;
}
+ // TODO(dsinclair): Store return type and attributes
+ // TODO(dsinclair): Store parameters
+ // TODO(dsinclair): Store attributes
+
// If the branch target has already been set then a `return` was called. Only set in the
// case where `return` wasn't called.
BranchToIfNeeded(current_function_->end_target);
@@ -205,14 +230,22 @@
bool BuilderImpl::EmitStatement(const ast::Statement* stmt) {
return tint::Switch(
stmt,
- // [&](const ast::AssignmentStatement* a) { },
+ // [&](const ast::AssignmentStatement* a) {
+ // TODO(dsinclair): Implement
+ // },
[&](const ast::BlockStatement* b) { return EmitBlock(b); },
[&](const ast::BreakStatement* b) { return EmitBreak(b); },
[&](const ast::BreakIfStatement* b) { return EmitBreakIf(b); },
- // [&](const ast::CallStatement* c) { },
- // [&](const ast::CompoundAssignmentStatement* c) { },
+ // [&](const ast::CallStatement* c) {
+ // TODO(dsinclair): Implement
+ // },
+ // [&](const ast::CompoundAssignmentStatement* c) {
+ // TODO(dsinclair): Implement
+ // },
[&](const ast::ContinueStatement* c) { return EmitContinue(c); },
- // [&](const ast::DiscardStatement* d) { },
+ // [&](const ast::DiscardStatement* d) {
+ // TODO(dsinclair): Implement
+ // },
[&](const ast::IfStatement* i) { return EmitIf(i); },
[&](const ast::LoopStatement* l) { return EmitLoop(l); },
[&](const ast::ForLoopStatement* l) { return EmitForLoop(l); },
@@ -227,6 +260,8 @@
diagnostics_.add_warning(
tint::diag::System::IR,
"unknown statement type: " + std::string(stmt->TypeInfo().name), stmt->source);
+ // TODO(dsinclair): This should return `false`, switch back when all
+ // the cases are handled.
return true;
});
}
@@ -239,9 +274,9 @@
}
bool BuilderImpl::EmitIf(const ast::IfStatement* stmt) {
- auto* if_node = builder.CreateIf(stmt);
+ auto* if_node = builder.CreateIf();
- // Emit the if condition into the end of the preceeding block
+ // Emit the if condition into the end of the preceding block
auto reg = EmitExpression(stmt->condition);
if (!reg) {
return false;
@@ -255,34 +290,34 @@
{
FlowStackScope scope(this, if_node);
- current_flow_block = if_node->true_target;
+ current_flow_block = if_node->true_.target->As<Block>();
if (!EmitStatement(stmt->body)) {
return false;
}
// If the true branch did not execute control flow, then go to the merge target
- BranchToIfNeeded(if_node->merge_target);
+ BranchToIfNeeded(if_node->merge.target);
- current_flow_block = if_node->false_target;
+ current_flow_block = if_node->false_.target->As<Block>();
if (stmt->else_statement && !EmitStatement(stmt->else_statement)) {
return false;
}
// If the false branch did not execute control flow, then go to the merge target
- BranchToIfNeeded(if_node->merge_target);
+ BranchToIfNeeded(if_node->merge.target);
}
current_flow_block = nullptr;
// If both branches went somewhere, then they both returned, continued or broke. So,
// there is no need for the if merge-block and there is nothing to branch to the merge
// block anyway.
- if (IsConnected(if_node->merge_target)) {
- current_flow_block = if_node->merge_target;
+ if (IsConnected(if_node->merge.target)) {
+ current_flow_block = if_node->merge.target->As<Block>();
}
return true;
}
bool BuilderImpl::EmitLoop(const ast::LoopStatement* stmt) {
- auto* loop_node = builder.CreateLoop(stmt);
+ auto* loop_node = builder.CreateLoop();
BranchTo(loop_node);
@@ -291,15 +326,15 @@
{
FlowStackScope scope(this, loop_node);
- current_flow_block = loop_node->start_target;
+ current_flow_block = loop_node->start.target->As<Block>();
if (!EmitStatement(stmt->body)) {
return false;
}
// The current block didn't `break`, `return` or `continue`, go to the continuing block.
- BranchToIfNeeded(loop_node->continuing_target);
+ BranchToIfNeeded(loop_node->continuing.target);
- current_flow_block = loop_node->continuing_target;
+ current_flow_block = loop_node->continuing.target->As<Block>();
if (stmt->continuing) {
if (!EmitStatement(stmt->continuing)) {
return false;
@@ -307,22 +342,24 @@
}
// Branch back to the start node if the continue target didn't branch out already
- BranchToIfNeeded(loop_node->start_target);
+ BranchToIfNeeded(loop_node->start.target);
}
// The loop merge can get disconnected if the loop returns directly, or the continuing target
// branches, eventually, to the merge, but nothing branched to the continuing target.
- current_flow_block = loop_node->merge_target;
- if (!IsConnected(loop_node->merge_target)) {
+ current_flow_block = loop_node->merge.target->As<Block>();
+ if (!IsConnected(loop_node->merge.target)) {
current_flow_block = nullptr;
}
return true;
}
bool BuilderImpl::EmitWhile(const ast::WhileStatement* stmt) {
- auto* loop_node = builder.CreateLoop(stmt);
+ auto* loop_node = builder.CreateLoop();
// Continue is always empty, just go back to the start
- builder.Branch(loop_node->continuing_target, loop_node->start_target);
+ TINT_ASSERT(IR, loop_node->continuing.target->Is<Block>());
+ builder.Branch(loop_node->continuing.target->As<Block>(), loop_node->start.target,
+ utils::Empty);
BranchTo(loop_node);
@@ -331,7 +368,7 @@
{
FlowStackScope scope(this, loop_node);
- current_flow_block = loop_node->start_target;
+ current_flow_block = loop_node->start.target->As<Block>();
// Emit the while condition into the start target of the loop
auto reg = EmitExpression(stmt->condition);
@@ -339,30 +376,35 @@
return false;
}
- // Create an if (cond) {} else {break;} control flow
- auto* if_node = builder.CreateIf(nullptr);
- builder.Branch(if_node->true_target, if_node->merge_target);
- builder.Branch(if_node->false_target, loop_node->merge_target);
+ // Create an `if (cond) {} else {break;}` control flow
+ auto* if_node = builder.CreateIf();
+ TINT_ASSERT(IR, if_node->true_.target->Is<Block>());
+ builder.Branch(if_node->true_.target->As<Block>(), if_node->merge.target, utils::Empty);
+
+ TINT_ASSERT(IR, if_node->false_.target->Is<Block>());
+ builder.Branch(if_node->false_.target->As<Block>(), loop_node->merge.target, utils::Empty);
if_node->condition = reg.Get();
BranchTo(if_node);
- current_flow_block = if_node->merge_target;
+ current_flow_block = if_node->merge.target->As<Block>();
if (!EmitStatement(stmt->body)) {
return false;
}
- BranchToIfNeeded(loop_node->continuing_target);
+ BranchToIfNeeded(loop_node->continuing.target);
}
// The while loop always has a path to the merge target as the break statement comes before
// anything inside the loop.
- current_flow_block = loop_node->merge_target;
+ current_flow_block = loop_node->merge.target->As<Block>();
return true;
}
bool BuilderImpl::EmitForLoop(const ast::ForLoopStatement* stmt) {
- auto* loop_node = builder.CreateLoop(stmt);
- builder.Branch(loop_node->continuing_target, loop_node->start_target);
+ auto* loop_node = builder.CreateLoop();
+ TINT_ASSERT(IR, loop_node->continuing.target->Is<Block>());
+ builder.Branch(loop_node->continuing.target->As<Block>(), loop_node->start.target,
+ utils::Empty);
if (stmt->initializer) {
// Emit the for initializer before branching to the loop
@@ -378,7 +420,7 @@
{
FlowStackScope scope(this, loop_node);
- current_flow_block = loop_node->start_target;
+ current_flow_block = loop_node->start.target->As<Block>();
if (stmt->condition) {
// Emit the condition into the target target of the loop
@@ -387,24 +429,28 @@
return false;
}
- // Create an if (cond) {} else {break;} control flow
- auto* if_node = builder.CreateIf(nullptr);
- builder.Branch(if_node->true_target, if_node->merge_target);
- builder.Branch(if_node->false_target, loop_node->merge_target);
+ // Create an `if (cond) {} else {break;}` control flow
+ auto* if_node = builder.CreateIf();
+ TINT_ASSERT(IR, if_node->true_.target->Is<Block>());
+ builder.Branch(if_node->true_.target->As<Block>(), if_node->merge.target, utils::Empty);
+
+ TINT_ASSERT(IR, if_node->false_.target->Is<Block>());
+ builder.Branch(if_node->false_.target->As<Block>(), loop_node->merge.target,
+ utils::Empty);
if_node->condition = reg.Get();
BranchTo(if_node);
- current_flow_block = if_node->merge_target;
+ current_flow_block = if_node->merge.target->As<Block>();
}
if (!EmitStatement(stmt->body)) {
return false;
}
- BranchToIfNeeded(loop_node->continuing_target);
+ BranchToIfNeeded(loop_node->continuing.target);
if (stmt->continuing) {
- current_flow_block = loop_node->continuing_target;
+ current_flow_block = loop_node->continuing.target->As<Block>();
if (!EmitStatement(stmt->continuing)) {
return false;
}
@@ -412,14 +458,14 @@
}
// The while loop always has a path to the merge target as the break statement comes before
// anything inside the loop.
- current_flow_block = loop_node->merge_target;
+ current_flow_block = loop_node->merge.target->As<Block>();
return true;
}
bool BuilderImpl::EmitSwitch(const ast::SwitchStatement* stmt) {
- auto* switch_node = builder.CreateSwitch(stmt);
+ auto* switch_node = builder.CreateSwitch();
- // Emit the condition into the preceeding block
+ // Emit the condition into the preceding block
auto reg = EmitExpression(stmt->condition);
if (!reg) {
return false;
@@ -433,27 +479,44 @@
{
FlowStackScope scope(this, switch_node);
- for (const auto* c : stmt->body) {
- current_flow_block = builder.CreateCase(switch_node, c->selectors);
- if (!EmitStatement(c->body)) {
+ const auto* sem = program_->Sem().Get(stmt);
+ for (const auto* c : sem->Cases()) {
+ utils::Vector<Switch::CaseSelector, 4> selectors;
+ for (const auto* selector : c->Selectors()) {
+ if (selector->IsDefault()) {
+ selectors.Push({nullptr});
+ } else {
+ selectors.Push({builder.Constant(selector->Value()->Clone(clone_ctx_))});
+ }
+ }
+
+ current_flow_block = builder.CreateCase(switch_node, selectors);
+ if (!EmitStatement(c->Body()->Declaration())) {
return false;
}
- BranchToIfNeeded(switch_node->merge_target);
+ BranchToIfNeeded(switch_node->merge.target);
}
}
current_flow_block = nullptr;
- if (IsConnected(switch_node->merge_target)) {
- current_flow_block = switch_node->merge_target;
+ if (IsConnected(switch_node->merge.target)) {
+ current_flow_block = switch_node->merge.target->As<Block>();
}
return true;
}
-bool BuilderImpl::EmitReturn(const ast::ReturnStatement*) {
- // TODO(dsinclair): Emit the return value ....
+bool BuilderImpl::EmitReturn(const ast::ReturnStatement* stmt) {
+ utils::Vector<Value*, 1> ret_value;
+ if (stmt->value) {
+ auto ret = EmitExpression(stmt->value);
+ if (!ret) {
+ return false;
+ }
+ ret_value.Push(ret.Get());
+ }
- BranchTo(current_function_->end_target);
+ BranchTo(current_function_->end_target, std::move(ret_value));
return true;
}
@@ -462,9 +525,9 @@
TINT_ASSERT(IR, current_control);
if (auto* c = current_control->As<Loop>()) {
- BranchTo(c->merge_target);
+ BranchTo(c->merge.target);
} else if (auto* s = current_control->As<Switch>()) {
- BranchTo(s->merge_target);
+ BranchTo(s->merge.target);
} else {
TINT_UNREACHABLE(IR, diagnostics_);
return false;
@@ -478,7 +541,7 @@
TINT_ASSERT(IR, current_control);
if (auto* c = current_control->As<Loop>()) {
- BranchTo(c->continuing_target);
+ BranchTo(c->continuing.target);
} else {
TINT_UNREACHABLE(IR, diagnostics_);
}
@@ -487,9 +550,9 @@
}
bool BuilderImpl::EmitBreakIf(const ast::BreakIfStatement* stmt) {
- auto* if_node = builder.CreateIf(stmt);
+ auto* if_node = builder.CreateIf();
- // Emit the break-if condition into the end of the preceeding block
+ // Emit the break-if condition into the end of the preceding block
auto reg = EmitExpression(stmt->condition);
if (!reg) {
return false;
@@ -506,18 +569,17 @@
auto* loop = current_control->As<Loop>();
- current_flow_block = if_node->true_target;
- BranchTo(loop->merge_target);
+ current_flow_block = if_node->true_.target->As<Block>();
+ BranchTo(loop->merge.target);
- current_flow_block = if_node->false_target;
- BranchTo(if_node->merge_target);
+ current_flow_block = if_node->false_.target->As<Block>();
+ BranchTo(if_node->merge.target);
- current_flow_block = if_node->merge_target;
+ current_flow_block = if_node->merge.target->As<Block>();
// The `break-if` has to be the last item in the continuing block. The false branch of the
// `break-if` will always take us back to the start of the loop.
- // break then we go back to the start of the loop.
- BranchTo(loop->start_target);
+ BranchTo(loop->start.target);
return true;
}
@@ -525,35 +587,65 @@
utils::Result<Value*> BuilderImpl::EmitExpression(const ast::Expression* expr) {
return tint::Switch(
expr,
- // [&](const ast::IndexAccessorExpression* a) { return EmitIndexAccessor(a); },
+ // [&](const ast::IndexAccessorExpression* a) {
+ // TODO(dsinclair): Implement
+ // },
[&](const ast::BinaryExpression* b) { return EmitBinary(b); },
[&](const ast::BitcastExpression* b) { return EmitBitcast(b); },
- // [&](const ast::CallExpression* c) { return EmitCall(c); },
- // [&](const ast::IdentifierExpression* i) { return EmitIdentifier(i); },
+ // [&](const ast::CallExpression* c) {
+ // TODO(dsinclair): Implement
+ // },
+ // [&](const ast::IdentifierExpression* i) {
+ // TODO(dsinclair): Implement
+ // },
[&](const ast::LiteralExpression* l) { return EmitLiteral(l); },
- // [&](const ast::MemberAccessorExpression* m) { return EmitMemberAccessor(m); },
- // [&](const ast::PhonyExpression*) { return true; },
- // [&](const ast::UnaryOpExpression* u) { return EmitUnaryOp(u); },
+ // [&](const ast::MemberAccessorExpression* m) {
+ // TODO(dsinclair): Implement
+ // },
+ // [&](const ast::PhonyExpression*) {
+ // TODO(dsinclair): Implement. The call may have side effects so has to be made.
+ // },
+ // [&](const ast::UnaryOpExpression* u) {
+ // TODO(dsinclair): Implement
+ // },
[&](Default) {
diagnostics_.add_warning(
tint::diag::System::IR,
"unknown expression type: " + std::string(expr->TypeInfo().name), expr->source);
- return utils::Failure;
+ // TODO(dsinclair): This should return utils::Failure; Switch back
+ // once all the above cases are handled.
+ auto* v = builder.ir.types.Get<type::Void>();
+ return builder.Temp(v);
});
}
bool BuilderImpl::EmitVariable(const ast::Variable* var) {
return tint::Switch( //
var,
- // [&](const ast::Var* var) {},
- // [&](const ast::Let*) {},
- // [&](const ast::Override*) { },
- // [&](const ast::Const* c) { },
+ // [&](const ast::Var* var) {
+ // TODO(dsinclair): Implement
+ // },
+ // [&](const ast::Let*) {
+ // TODO(dsinclair): Implement
+ // },
+ [&](const ast::Override*) {
+ diagnostics_.add_warning(tint::diag::System::IR,
+ "found an `Override` variable. The SubstituteOverrides "
+ "transform must be run before converting to IR",
+ var->source);
+ return false;
+ },
+ // [&](const ast::Const* c) {
+ // TODO(dsinclair): Implement
+ // },
[&](Default) {
diagnostics_.add_warning(tint::diag::System::IR,
"unknown variable: " + std::string(var->TypeInfo().name),
var->source);
- return false;
+
+ // TODO(dsinclair): This should return `false`, switch back when all
+ // the cases are handled.
+ return true;
});
}
@@ -568,7 +660,7 @@
return utils::Failure;
}
- auto* sem = builder.ir.program->Sem().Get(expr);
+ auto* sem = program_->Sem().Get(expr);
auto* ty = sem->Type()->Clone(clone_ctx_.type_ctx);
Binary* instr = nullptr;
@@ -642,7 +734,7 @@
return utils::Failure;
}
- auto* sem = builder.ir.program->Sem().Get(expr);
+ auto* sem = program_->Sem().Get(expr);
auto* ty = sem->Type()->Clone(clone_ctx_.type_ctx);
auto* instr = builder.Bitcast(ty, val.Get());
@@ -651,7 +743,7 @@
}
utils::Result<Value*> BuilderImpl::EmitLiteral(const ast::LiteralExpression* lit) {
- auto* sem = builder.ir.program->Sem().Get(lit);
+ auto* sem = program_->Sem().Get(lit);
if (!sem) {
diagnostics_.add_error(
tint::diag::System::IR,
@@ -671,45 +763,6 @@
return builder.Constant(cv);
}
-bool BuilderImpl::EmitType(const ast::Type* ty) {
- return tint::Switch(
- ty,
- // [&](const ast::Array* ary) { },
- // [&](const ast::Bool* b) { },
- // [&](const ast::F32* f) { },
- // [&](const ast::F16* f) { },
- // [&](const ast::I32* i) { },
- // [&](const ast::U32* u) { },
- // [&](const ast::Vector* v) { },
- // [&](const ast::Matrix* mat) { },
- // [&](const ast::Pointer* ptr) { },'
- // [&](const ast::Atomic* a) { },
- // [&](const ast::Sampler* s) { },
- // [&](const ast::ExternalTexture* t) { },
- // [&](const ast::Texture* t) {
- // return tint::Switch(
- // t,
- // [&](const ast::DepthTexture*) { },
- // [&](const ast::DepthMultisampledTexture*) { },
- // [&](const ast::SampledTexture*) { },
- // [&](const ast::MultisampledTexture*) { },
- // [&](const ast::StorageTexture*) { },
- // [&](Default) {
- // diagnostics_.add_warning(tint::diag::System::IR,
- // "unknown texture: " + std::string(t->TypeInfo().name), t->source);
- // return false;
- // });
- // },
- // [&](const ast::Void* v) { },
- // [&](const ast::TypeName* tn) { },
- [&](Default) {
- diagnostics_.add_warning(tint::diag::System::IR,
- "unknown type: " + std::string(ty->TypeInfo().name),
- ty->source);
- return false;
- });
-}
-
bool BuilderImpl::EmitAttributes(utils::VectorRef<const ast::Attribute*> attrs) {
for (auto* attr : attrs) {
if (!EmitAttribute(attr)) {
@@ -722,19 +775,53 @@
bool BuilderImpl::EmitAttribute(const ast::Attribute* attr) {
return tint::Switch( //
attr,
- // [&](const ast::WorkgroupAttribute* wg) {},
- // [&](const ast::StageAttribute* s) {},
- // [&](const ast::BindingAttribute* b) {},
- // [&](const ast::GroupAttribute* g) {},
- // [&](const ast::LocationAttribute* l) {},
- // [&](const ast::BuiltinAttribute* b) {},
- // [&](const ast::InterpolateAttribute* i) {},
- // [&](const ast::InvariantAttribute* i) {},
- // [&](const ast::IdAttribute* i) {},
- // [&](const ast::StructMemberSizeAttribute* s) {},
- // [&](const ast::StructMemberAlignAttribute* a) {},
- // [&](const ast::StrideAttribute* s) {}
- // [&](const ast::InternalAttribute *i) {},
+ // [&](const ast::WorkgroupAttribute* wg) {
+ // TODO(dsinclair): Implement
+ // },
+ // [&](const ast::StageAttribute* s) {
+ // TODO(dsinclair): Implement
+ // },
+ // [&](const ast::BindingAttribute* b) {
+ // TODO(dsinclair): Implement
+ // },
+ // [&](const ast::GroupAttribute* g) {
+ // TODO(dsinclair): Implement
+ // },
+ // [&](const ast::LocationAttribute* l) {
+ // TODO(dsinclair): Implement
+ // },
+ // [&](const ast::BuiltinAttribute* b) {
+ // TODO(dsinclair): Implement
+ // },
+ // [&](const ast::InterpolateAttribute* i) {
+ // TODO(dsinclair): Implement
+ // },
+ // [&](const ast::InvariantAttribute* i) {
+ // TODO(dsinclair): Implement
+ // },
+ [&](const ast::IdAttribute*) {
+ diagnostics_.add_warning(tint::diag::System::IR,
+ "found an `Id` attribute. The SubstituteOverrides transform "
+ "must be run before converting to IR",
+ attr->source);
+ return false;
+ },
+ [&](const ast::StructMemberSizeAttribute*) {
+ TINT_ICE(IR, diagnostics_)
+ << "StructMemberSizeAttribute encountered during IR conversion";
+ return false;
+ },
+ [&](const ast::StructMemberAlignAttribute*) {
+ TINT_ICE(IR, diagnostics_)
+ << "StructMemberAlignAttribute encountered during IR conversion";
+ return false;
+ },
+ // [&](const ast::StrideAttribute* s) {
+ // TODO(dsinclair): Implement
+ // },
+ // [&](const ast::InternalAttribute *i) {
+ // TODO(dsinclair): Implement
+ // },
[&](Default) {
diagnostics_.add_warning(tint::diag::System::IR,
"unknown attribute: " + std::string(attr->TypeInfo().name),
diff --git a/src/tint/ir/builder_impl.h b/src/tint/ir/builder_impl.h
index 6c858ac..c086677 100644
--- a/src/tint/ir/builder_impl.h
+++ b/src/tint/ir/builder_impl.h
@@ -32,20 +32,26 @@
class Program;
} // namespace tint
namespace tint::ast {
+class Attribute;
class BinaryExpression;
class BitcastExpression;
class BlockStatement;
class BreakIfStatement;
class BreakStatement;
class ContinueStatement;
+class Expression;
class ForLoopStatement;
class Function;
class IfStatement;
class LoopStatement;
class LiteralExpression;
+class Node;
class ReturnStatement;
class Statement;
+class SwitchStatement;
+class Type;
class WhileStatement;
+class Variable;
} // namespace tint::ast
namespace tint::ir {
class Block;
@@ -164,11 +170,6 @@
/// @returns true if successful, false otherwise
utils::Result<Value*> EmitLiteral(const ast::LiteralExpression* lit);
- /// Emits a type
- /// @param ty the type to emit
- /// @returns true if successful, false otherwise
- bool EmitType(const ast::Type* ty);
-
/// Emits a set of attributes
/// @param attrs the attributes to emit
/// @returns true if successful, false otherwise
@@ -201,11 +202,15 @@
private:
enum class ControlFlags { kNone, kExcludeSwitch };
- void BranchTo(ir::FlowNode* node);
+ void BranchTo(ir::FlowNode* node, utils::VectorRef<Value*> args = {});
void BranchToIfNeeded(ir::FlowNode* node);
FlowNode* FindEnclosingControl(ControlFlags flags);
+ const Program* program_ = nullptr;
+
+ Symbol CloneSymbol(Symbol sym) const;
+
diag::List diagnostics_;
Function* current_function_ = nullptr;
diff --git a/src/tint/ir/builder_impl_test.cc b/src/tint/ir/builder_impl_test.cc
index ced23f6..9dc908d 100644
--- a/src/tint/ir/builder_impl_test.cc
+++ b/src/tint/ir/builder_impl_test.cc
@@ -16,6 +16,7 @@
#include "src/tint/ast/case_selector.h"
#include "src/tint/ast/int_literal_expression.h"
+#include "src/tint/constant/scalar.h"
namespace tint::ir {
namespace {
@@ -25,35 +26,34 @@
using IR_BuilderImplTest = TestHelper;
TEST_F(IR_BuilderImplTest, Func) {
- // func -> start -> end
-
Func("f", utils::Empty, ty.void_(), utils::Empty);
- auto& b = CreateBuilder();
-
- auto r = b.Build();
- ASSERT_TRUE(r) << b.error();
+ auto r = Build();
+ ASSERT_TRUE(r) << Error();
auto m = r.Move();
ASSERT_EQ(0u, m.entry_points.Length());
ASSERT_EQ(1u, m.functions.Length());
auto* f = m.functions[0];
- EXPECT_NE(f->start_target, nullptr);
- EXPECT_NE(f->end_target, nullptr);
+ ASSERT_NE(f->start_target, nullptr);
+ ASSERT_NE(f->end_target, nullptr);
EXPECT_EQ(1u, f->start_target->inbound_branches.Length());
EXPECT_EQ(1u, f->end_target->inbound_branches.Length());
- EXPECT_EQ(f->start_target->branch_target, f->end_target);
+ EXPECT_EQ(Disassemble(m), R"(%bb0 = Function f
+ %bb1 = Block
+ Return ()
+FunctionEnd
+
+)");
}
TEST_F(IR_BuilderImplTest, EntryPoint) {
Func("f", utils::Empty, ty.void_(), utils::Empty,
utils::Vector{Stage(ast::PipelineStage::kFragment)});
- auto& b = CreateBuilder();
-
- auto r = b.Build();
- ASSERT_TRUE(r) << b.error();
+ auto r = Build();
+ ASSERT_TRUE(r) << Error();
auto m = r.Move();
ASSERT_EQ(1u, m.entry_points.Length());
@@ -61,687 +61,718 @@
}
TEST_F(IR_BuilderImplTest, IfStatement) {
- // func -> start -> if -> true block
- // -> false block
- //
- // [true block] -> if merge
- // [false block] -> if merge
- // [if merge] -> func end
- //
auto* ast_if = If(true, Block(), Else(Block()));
WrapInFunction(ast_if);
- auto& b = CreateBuilder();
- auto r = b.Build();
- ASSERT_TRUE(r) << b.error();
+ auto r = Build();
+ ASSERT_TRUE(r) << Error();
auto m = r.Move();
- auto* ir_if = b.FlowNodeForAstNode(ast_if);
+ auto* ir_if = FlowNodeForAstNode(ast_if);
ASSERT_NE(ir_if, nullptr);
EXPECT_TRUE(ir_if->Is<ir::If>());
auto* flow = ir_if->As<ir::If>();
- ASSERT_NE(flow->true_target, nullptr);
- ASSERT_NE(flow->false_target, nullptr);
- ASSERT_NE(flow->merge_target, nullptr);
+ ASSERT_NE(flow->true_.target, nullptr);
+ ASSERT_NE(flow->false_.target, nullptr);
+ ASSERT_NE(flow->merge.target, nullptr);
ASSERT_EQ(1u, m.functions.Length());
auto* func = m.functions[0];
EXPECT_EQ(1u, flow->inbound_branches.Length());
- EXPECT_EQ(1u, flow->true_target->inbound_branches.Length());
- EXPECT_EQ(1u, flow->false_target->inbound_branches.Length());
- EXPECT_EQ(2u, flow->merge_target->inbound_branches.Length());
+ EXPECT_EQ(1u, flow->true_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, flow->false_.target->inbound_branches.Length());
+ EXPECT_EQ(2u, flow->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
- EXPECT_EQ(func->start_target->branch_target, flow);
- EXPECT_EQ(flow->true_target->branch_target, flow->merge_target);
- EXPECT_EQ(flow->false_target->branch_target, flow->merge_target);
- EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+ EXPECT_EQ(Disassemble(m), R"(%bb0 = Function test_function
+ %bb1 = Block
+ BranchTo %bb2 ()
- // Check condition
- ASSERT_TRUE(flow->condition->Is<Constant>());
- auto* instr = flow->condition->As<Constant>()->value;
- ASSERT_TRUE(instr->Is<constant::Scalar<bool>>());
- EXPECT_TRUE(instr->As<constant::Scalar<bool>>()->ValueAs<bool>());
+ %bb2 = if (true)
+ # true branch
+ %bb3 = Block
+ BranchTo %bb4 ()
+
+ # false branch
+ %bb5 = Block
+ BranchTo %bb4 ()
+
+ # if merge
+ %bb4 = Block
+ Return ()
+FunctionEnd
+
+)");
}
TEST_F(IR_BuilderImplTest, IfStatement_TrueReturns) {
- // func -> start -> if -> true block
- // -> false block
- //
- // [true block] -> func end
- // [false block] -> if merge
- // [if merge] -> func end
- //
auto* ast_if = If(true, Block(Return()));
WrapInFunction(ast_if);
- auto& b = CreateBuilder();
- auto r = b.Build();
- ASSERT_TRUE(r) << b.error();
+ auto r = Build();
+ ASSERT_TRUE(r) << Error();
auto m = r.Move();
- auto* ir_if = b.FlowNodeForAstNode(ast_if);
+ auto* ir_if = FlowNodeForAstNode(ast_if);
ASSERT_NE(ir_if, nullptr);
EXPECT_TRUE(ir_if->Is<ir::If>());
auto* flow = ir_if->As<ir::If>();
- ASSERT_NE(flow->true_target, nullptr);
- ASSERT_NE(flow->false_target, nullptr);
- ASSERT_NE(flow->merge_target, nullptr);
+ ASSERT_NE(flow->true_.target, nullptr);
+ ASSERT_NE(flow->false_.target, nullptr);
+ ASSERT_NE(flow->merge.target, nullptr);
ASSERT_EQ(1u, m.functions.Length());
auto* func = m.functions[0];
EXPECT_EQ(1u, flow->inbound_branches.Length());
- EXPECT_EQ(1u, flow->true_target->inbound_branches.Length());
- EXPECT_EQ(1u, flow->false_target->inbound_branches.Length());
- EXPECT_EQ(1u, flow->merge_target->inbound_branches.Length());
+ EXPECT_EQ(1u, flow->true_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, flow->false_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
EXPECT_EQ(2u, func->end_target->inbound_branches.Length());
- EXPECT_EQ(func->start_target->branch_target, flow);
- EXPECT_EQ(flow->true_target->branch_target, func->end_target);
- EXPECT_EQ(flow->false_target->branch_target, flow->merge_target);
- EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+ EXPECT_EQ(Disassemble(m), R"(%bb0 = Function test_function
+ %bb1 = Block
+ BranchTo %bb2 ()
+
+ %bb2 = if (true)
+ # true branch
+ %bb3 = Block
+ Return ()
+ # false branch
+ %bb4 = Block
+ BranchTo %bb5 ()
+
+ # if merge
+ %bb5 = Block
+ Return ()
+FunctionEnd
+
+)");
}
TEST_F(IR_BuilderImplTest, IfStatement_FalseReturns) {
- // func -> start -> if -> true block
- // -> false block
- //
- // [true block] -> if merge
- // [false block] -> func end
- // [if merge] -> func end
- //
auto* ast_if = If(true, Block(), Else(Block(Return())));
WrapInFunction(ast_if);
- auto& b = CreateBuilder();
- auto r = b.Build();
- ASSERT_TRUE(r) << b.error();
+ auto r = Build();
+ ASSERT_TRUE(r) << Error();
auto m = r.Move();
- auto* ir_if = b.FlowNodeForAstNode(ast_if);
+ auto* ir_if = FlowNodeForAstNode(ast_if);
ASSERT_NE(ir_if, nullptr);
EXPECT_TRUE(ir_if->Is<ir::If>());
auto* flow = ir_if->As<ir::If>();
- ASSERT_NE(flow->true_target, nullptr);
- ASSERT_NE(flow->false_target, nullptr);
- ASSERT_NE(flow->merge_target, nullptr);
+ ASSERT_NE(flow->true_.target, nullptr);
+ ASSERT_NE(flow->false_.target, nullptr);
+ ASSERT_NE(flow->merge.target, nullptr);
ASSERT_EQ(1u, m.functions.Length());
auto* func = m.functions[0];
EXPECT_EQ(1u, flow->inbound_branches.Length());
- EXPECT_EQ(1u, flow->true_target->inbound_branches.Length());
- EXPECT_EQ(1u, flow->false_target->inbound_branches.Length());
- EXPECT_EQ(1u, flow->merge_target->inbound_branches.Length());
+ EXPECT_EQ(1u, flow->true_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, flow->false_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
EXPECT_EQ(2u, func->end_target->inbound_branches.Length());
- EXPECT_EQ(func->start_target->branch_target, flow);
- EXPECT_EQ(flow->true_target->branch_target, flow->merge_target);
- EXPECT_EQ(flow->false_target->branch_target, func->end_target);
- EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+ EXPECT_EQ(Disassemble(m), R"(%bb0 = Function test_function
+ %bb1 = Block
+ BranchTo %bb2 ()
+
+ %bb2 = if (true)
+ # true branch
+ %bb3 = Block
+ BranchTo %bb4 ()
+
+ # false branch
+ %bb5 = Block
+ Return ()
+ # if merge
+ %bb4 = Block
+ Return ()
+FunctionEnd
+
+)");
}
TEST_F(IR_BuilderImplTest, IfStatement_BothReturn) {
- // func -> start -> if -> true block
- // -> false block
- //
- // [true block] -> func end
- // [false block] -> func end
- // [if merge] -> nullptr
- //
auto* ast_if = If(true, Block(Return()), Else(Block(Return())));
WrapInFunction(ast_if);
- auto& b = CreateBuilder();
- auto r = b.Build();
- ASSERT_TRUE(r) << b.error();
+ auto r = Build();
+ ASSERT_TRUE(r) << Error();
auto m = r.Move();
- auto* ir_if = b.FlowNodeForAstNode(ast_if);
+ auto* ir_if = FlowNodeForAstNode(ast_if);
ASSERT_NE(ir_if, nullptr);
EXPECT_TRUE(ir_if->Is<ir::If>());
auto* flow = ir_if->As<ir::If>();
- ASSERT_NE(flow->true_target, nullptr);
- ASSERT_NE(flow->false_target, nullptr);
- ASSERT_NE(flow->merge_target, nullptr);
+ ASSERT_NE(flow->true_.target, nullptr);
+ ASSERT_NE(flow->false_.target, nullptr);
+ ASSERT_NE(flow->merge.target, nullptr);
ASSERT_EQ(1u, m.functions.Length());
auto* func = m.functions[0];
EXPECT_EQ(1u, flow->inbound_branches.Length());
- EXPECT_EQ(1u, flow->true_target->inbound_branches.Length());
- EXPECT_EQ(1u, flow->false_target->inbound_branches.Length());
- EXPECT_EQ(0u, flow->merge_target->inbound_branches.Length());
+ EXPECT_EQ(1u, flow->true_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, flow->false_.target->inbound_branches.Length());
+ EXPECT_EQ(0u, flow->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
EXPECT_EQ(2u, func->end_target->inbound_branches.Length());
- EXPECT_EQ(func->start_target->branch_target, flow);
- EXPECT_EQ(flow->true_target->branch_target, func->end_target);
- EXPECT_EQ(flow->false_target->branch_target, func->end_target);
+ EXPECT_EQ(Disassemble(m), R"(%bb0 = Function test_function
+ %bb1 = Block
+ BranchTo %bb2 ()
+
+ %bb2 = if (true)
+ # true branch
+ %bb3 = Block
+ Return ()
+ # false branch
+ %bb4 = Block
+ Return ()
+FunctionEnd
+
+)");
}
TEST_F(IR_BuilderImplTest, IfStatement_JumpChainToMerge) {
- // if (true) {
- // loop {
- // break;
- // }
- // }
- //
- // func -> start -> if true
- // -> if false
- //
- // [if true] -> loop
- // [if false] -> if merge
- // [if merge] -> func end
- // [loop] -> loop start
- // [loop start] -> loop merge
- // [loop continuing] -> loop start
- // [loop merge] -> if merge
- //
auto* ast_loop = Loop(Block(Break()));
auto* ast_if = If(true, Block(ast_loop));
WrapInFunction(ast_if);
- auto& b = CreateBuilder();
- auto r = b.Build();
- ASSERT_TRUE(r) << b.error();
+ auto r = Build();
+ ASSERT_TRUE(r) << Error();
auto m = r.Move();
- auto* ir_if = b.FlowNodeForAstNode(ast_if);
+ auto* ir_if = FlowNodeForAstNode(ast_if);
ASSERT_NE(ir_if, nullptr);
EXPECT_TRUE(ir_if->Is<ir::If>());
auto* if_flow = ir_if->As<ir::If>();
- ASSERT_NE(if_flow->true_target, nullptr);
- ASSERT_NE(if_flow->false_target, nullptr);
- ASSERT_NE(if_flow->merge_target, nullptr);
+ ASSERT_NE(if_flow->true_.target, nullptr);
+ ASSERT_NE(if_flow->false_.target, nullptr);
+ ASSERT_NE(if_flow->merge.target, nullptr);
- auto* ir_loop = b.FlowNodeForAstNode(ast_loop);
+ auto* ir_loop = FlowNodeForAstNode(ast_loop);
ASSERT_NE(ir_loop, nullptr);
EXPECT_TRUE(ir_loop->Is<ir::Loop>());
auto* loop_flow = ir_loop->As<ir::Loop>();
- ASSERT_NE(loop_flow->start_target, nullptr);
- ASSERT_NE(loop_flow->continuing_target, nullptr);
- ASSERT_NE(loop_flow->merge_target, nullptr);
+ ASSERT_NE(loop_flow->start.target, nullptr);
+ ASSERT_NE(loop_flow->continuing.target, nullptr);
+ ASSERT_NE(loop_flow->merge.target, nullptr);
- ASSERT_EQ(1u, m.functions.Length());
- auto* func = m.functions[0];
+ EXPECT_EQ(Disassemble(m), R"(%bb0 = Function test_function
+ %bb1 = Block
+ BranchTo %bb2 ()
- EXPECT_EQ(func->start_target->branch_target, if_flow);
- EXPECT_EQ(if_flow->true_target->branch_target, loop_flow);
- EXPECT_EQ(loop_flow->start_target->branch_target, loop_flow->merge_target);
- EXPECT_EQ(loop_flow->merge_target->branch_target, if_flow->merge_target);
- EXPECT_EQ(loop_flow->continuing_target->branch_target, loop_flow->start_target);
- EXPECT_EQ(if_flow->false_target->branch_target, if_flow->merge_target);
- EXPECT_EQ(if_flow->merge_target->branch_target, func->end_target);
+ %bb2 = if (true)
+ # true branch
+ %bb3 = Block
+ BranchTo %bb4 ()
+
+ %bb4 = loop
+ # loop start
+ %bb5 = Block
+ BranchTo %bb6 ()
+
+ # loop continuing
+ %bb7 = Block
+ BranchTo %bb5 ()
+
+ # loop merge
+ %bb6 = Block
+ BranchTo %bb8 ()
+
+ # false branch
+ %bb9 = Block
+ BranchTo %bb8 ()
+
+ # if merge
+ %bb8 = Block
+ Return ()
+FunctionEnd
+
+)");
}
TEST_F(IR_BuilderImplTest, Loop_WithBreak) {
- // func -> start -> loop -> loop start -> loop merge -> func end
- //
- // [continuing] -> loop start
- //
auto* ast_loop = Loop(Block(Break()));
WrapInFunction(ast_loop);
- auto& b = CreateBuilder();
- auto r = b.Build();
- ASSERT_TRUE(r) << b.error();
+ auto r = Build();
+ ASSERT_TRUE(r) << Error();
auto m = r.Move();
- auto* ir_loop = b.FlowNodeForAstNode(ast_loop);
+ auto* ir_loop = FlowNodeForAstNode(ast_loop);
ASSERT_NE(ir_loop, nullptr);
EXPECT_TRUE(ir_loop->Is<ir::Loop>());
auto* flow = ir_loop->As<ir::Loop>();
- ASSERT_NE(flow->start_target, nullptr);
- ASSERT_NE(flow->continuing_target, nullptr);
- ASSERT_NE(flow->merge_target, nullptr);
+ ASSERT_NE(flow->start.target, nullptr);
+ ASSERT_NE(flow->continuing.target, nullptr);
+ ASSERT_NE(flow->merge.target, nullptr);
ASSERT_EQ(1u, m.functions.Length());
auto* func = m.functions[0];
EXPECT_EQ(1u, flow->inbound_branches.Length());
- EXPECT_EQ(2u, flow->start_target->inbound_branches.Length());
- EXPECT_EQ(0u, flow->continuing_target->inbound_branches.Length());
- EXPECT_EQ(1u, flow->merge_target->inbound_branches.Length());
+ EXPECT_EQ(2u, flow->start.target->inbound_branches.Length());
+ EXPECT_EQ(0u, flow->continuing.target->inbound_branches.Length());
+ EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
- EXPECT_EQ(func->start_target->branch_target, flow);
- EXPECT_EQ(flow->start_target->branch_target, flow->merge_target);
- EXPECT_EQ(flow->continuing_target->branch_target, flow->start_target);
- EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+ EXPECT_EQ(Disassemble(m), R"(%bb0 = Function test_function
+ %bb1 = Block
+ BranchTo %bb2 ()
+
+ %bb2 = loop
+ # loop start
+ %bb3 = Block
+ BranchTo %bb4 ()
+
+ # loop continuing
+ %bb5 = Block
+ BranchTo %bb3 ()
+
+ # loop merge
+ %bb4 = Block
+ Return ()
+FunctionEnd
+
+)");
}
TEST_F(IR_BuilderImplTest, Loop_WithContinue) {
- // func -> start -> loop -> loop start -> if -> true block
- // -> false block
- //
- // [if true] -> loop merge
- // [if false] -> if merge
- // [if merge] -> loop continuing
- // [loop continuing] -> loop start
- // [loop merge] -> func end
- //
auto* ast_if = If(true, Block(Break()));
auto* ast_loop = Loop(Block(ast_if, Continue()));
WrapInFunction(ast_loop);
- auto& b = CreateBuilder();
- auto r = b.Build();
- ASSERT_TRUE(r) << b.error();
+ auto r = Build();
+ ASSERT_TRUE(r) << Error();
auto m = r.Move();
- auto* ir_loop = b.FlowNodeForAstNode(ast_loop);
+ auto* ir_loop = FlowNodeForAstNode(ast_loop);
ASSERT_NE(ir_loop, nullptr);
EXPECT_TRUE(ir_loop->Is<ir::Loop>());
auto* loop_flow = ir_loop->As<ir::Loop>();
- ASSERT_NE(loop_flow->start_target, nullptr);
- ASSERT_NE(loop_flow->continuing_target, nullptr);
- ASSERT_NE(loop_flow->merge_target, nullptr);
+ ASSERT_NE(loop_flow->start.target, nullptr);
+ ASSERT_NE(loop_flow->continuing.target, nullptr);
+ ASSERT_NE(loop_flow->merge.target, nullptr);
- auto* ir_if = b.FlowNodeForAstNode(ast_if);
+ auto* ir_if = FlowNodeForAstNode(ast_if);
ASSERT_NE(ir_if, nullptr);
ASSERT_TRUE(ir_if->Is<ir::If>());
auto* if_flow = ir_if->As<ir::If>();
- ASSERT_NE(if_flow->true_target, nullptr);
- ASSERT_NE(if_flow->false_target, nullptr);
- ASSERT_NE(if_flow->merge_target, nullptr);
+ ASSERT_NE(if_flow->true_.target, nullptr);
+ ASSERT_NE(if_flow->false_.target, nullptr);
+ ASSERT_NE(if_flow->merge.target, nullptr);
ASSERT_EQ(1u, m.functions.Length());
auto* func = m.functions[0];
EXPECT_EQ(1u, loop_flow->inbound_branches.Length());
- EXPECT_EQ(2u, loop_flow->start_target->inbound_branches.Length());
- EXPECT_EQ(1u, loop_flow->continuing_target->inbound_branches.Length());
- EXPECT_EQ(1u, loop_flow->merge_target->inbound_branches.Length());
+ EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length());
+ EXPECT_EQ(1u, loop_flow->continuing.target->inbound_branches.Length());
+ EXPECT_EQ(1u, loop_flow->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, if_flow->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow->true_target->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow->false_target->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow->merge_target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow->true_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow->false_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
- EXPECT_EQ(func->start_target->branch_target, loop_flow);
- EXPECT_EQ(loop_flow->start_target->branch_target, if_flow);
- EXPECT_EQ(if_flow->true_target->branch_target, loop_flow->merge_target);
- EXPECT_EQ(if_flow->false_target->branch_target, if_flow->merge_target);
- EXPECT_EQ(loop_flow->continuing_target->branch_target, loop_flow->start_target);
- EXPECT_EQ(loop_flow->merge_target->branch_target, func->end_target);
+ EXPECT_EQ(Disassemble(m), R"(%bb0 = Function test_function
+ %bb1 = Block
+ BranchTo %bb2 ()
+
+ %bb2 = loop
+ # loop start
+ %bb3 = Block
+ BranchTo %bb4 ()
+
+ %bb4 = if (true)
+ # true branch
+ %bb5 = Block
+ BranchTo %bb6 ()
+
+ # false branch
+ %bb7 = Block
+ BranchTo %bb8 ()
+
+ # if merge
+ %bb8 = Block
+ BranchTo %bb9 ()
+
+ # loop continuing
+ %bb9 = Block
+ BranchTo %bb3 ()
+
+ # loop merge
+ %bb6 = Block
+ Return ()
+FunctionEnd
+
+)");
}
TEST_F(IR_BuilderImplTest, Loop_WithContinuing_BreakIf) {
- // func -> start -> loop -> loop start -> continuing
- //
- // [loop continuing] -> if -> true branch
- // -> false branch
- // [if true] -> loop merge
- // [if false] -> if merge
- // [if merge] -> loop start
- // [loop merge] -> func end
- //
auto* ast_break_if = BreakIf(true);
auto* ast_loop = Loop(Block(), Block(ast_break_if));
WrapInFunction(ast_loop);
- auto& b = CreateBuilder();
- auto r = b.Build();
- ASSERT_TRUE(r) << b.error();
+ auto r = Build();
+ ASSERT_TRUE(r) << Error();
auto m = r.Move();
- auto* ir_loop = b.FlowNodeForAstNode(ast_loop);
+ auto* ir_loop = FlowNodeForAstNode(ast_loop);
ASSERT_NE(ir_loop, nullptr);
EXPECT_TRUE(ir_loop->Is<ir::Loop>());
auto* loop_flow = ir_loop->As<ir::Loop>();
- ASSERT_NE(loop_flow->start_target, nullptr);
- ASSERT_NE(loop_flow->continuing_target, nullptr);
- ASSERT_NE(loop_flow->merge_target, nullptr);
+ ASSERT_NE(loop_flow->start.target, nullptr);
+ ASSERT_NE(loop_flow->continuing.target, nullptr);
+ ASSERT_NE(loop_flow->merge.target, nullptr);
- auto* ir_break_if = b.FlowNodeForAstNode(ast_break_if);
+ auto* ir_break_if = FlowNodeForAstNode(ast_break_if);
ASSERT_NE(ir_break_if, nullptr);
ASSERT_TRUE(ir_break_if->Is<ir::If>());
auto* break_if_flow = ir_break_if->As<ir::If>();
- ASSERT_NE(break_if_flow->true_target, nullptr);
- ASSERT_NE(break_if_flow->false_target, nullptr);
- ASSERT_NE(break_if_flow->merge_target, nullptr);
+ ASSERT_NE(break_if_flow->true_.target, nullptr);
+ ASSERT_NE(break_if_flow->false_.target, nullptr);
+ ASSERT_NE(break_if_flow->merge.target, nullptr);
ASSERT_EQ(1u, m.functions.Length());
auto* func = m.functions[0];
EXPECT_EQ(1u, loop_flow->inbound_branches.Length());
- EXPECT_EQ(2u, loop_flow->start_target->inbound_branches.Length());
- EXPECT_EQ(1u, loop_flow->continuing_target->inbound_branches.Length());
- EXPECT_EQ(1u, loop_flow->merge_target->inbound_branches.Length());
+ EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length());
+ EXPECT_EQ(1u, loop_flow->continuing.target->inbound_branches.Length());
+ EXPECT_EQ(1u, loop_flow->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, break_if_flow->inbound_branches.Length());
- EXPECT_EQ(1u, break_if_flow->true_target->inbound_branches.Length());
- EXPECT_EQ(1u, break_if_flow->false_target->inbound_branches.Length());
- EXPECT_EQ(1u, break_if_flow->merge_target->inbound_branches.Length());
+ EXPECT_EQ(1u, break_if_flow->true_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, break_if_flow->false_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, break_if_flow->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
- EXPECT_EQ(func->start_target->branch_target, loop_flow);
- EXPECT_EQ(loop_flow->start_target->branch_target, loop_flow->continuing_target);
- EXPECT_EQ(loop_flow->continuing_target->branch_target, break_if_flow);
- EXPECT_EQ(break_if_flow->true_target->branch_target, loop_flow->merge_target);
- EXPECT_EQ(break_if_flow->false_target->branch_target, break_if_flow->merge_target);
- EXPECT_EQ(break_if_flow->merge_target->branch_target, loop_flow->start_target);
- EXPECT_EQ(loop_flow->merge_target->branch_target, func->end_target);
+ EXPECT_EQ(Disassemble(m), R"(%bb0 = Function test_function
+ %bb1 = Block
+ BranchTo %bb2 ()
+
+ %bb2 = loop
+ # loop start
+ %bb3 = Block
+ BranchTo %bb4 ()
+
+ # loop continuing
+ %bb4 = Block
+ BranchTo %bb5 ()
+
+ %bb5 = if (true)
+ # true branch
+ %bb6 = Block
+ BranchTo %bb7 ()
+
+ # false branch
+ %bb8 = Block
+ BranchTo %bb9 ()
+
+ # if merge
+ %bb9 = Block
+ BranchTo %bb3 ()
+
+ # loop merge
+ %bb7 = Block
+ Return ()
+FunctionEnd
+
+)");
}
TEST_F(IR_BuilderImplTest, Loop_WithReturn) {
- // func -> start -> loop -> loop start -> if -> true block
- // -> false block
- //
- // [if true] -> func end
- // [if false] -> if merge
- // [if merge] -> loop continuing
- // [loop continuing] -> loop start
- // [loop merge] -> nullptr
- //
auto* ast_if = If(true, Block(Return()));
auto* ast_loop = Loop(Block(ast_if, Continue()));
WrapInFunction(ast_loop);
- auto& b = CreateBuilder();
- auto r = b.Build();
- ASSERT_TRUE(r) << b.error();
+ auto r = Build();
+ ASSERT_TRUE(r) << Error();
auto m = r.Move();
- auto* ir_loop = b.FlowNodeForAstNode(ast_loop);
+ auto* ir_loop = FlowNodeForAstNode(ast_loop);
ASSERT_NE(ir_loop, nullptr);
EXPECT_TRUE(ir_loop->Is<ir::Loop>());
auto* loop_flow = ir_loop->As<ir::Loop>();
- ASSERT_NE(loop_flow->start_target, nullptr);
- ASSERT_NE(loop_flow->continuing_target, nullptr);
- ASSERT_NE(loop_flow->merge_target, nullptr);
+ ASSERT_NE(loop_flow->start.target, nullptr);
+ ASSERT_NE(loop_flow->continuing.target, nullptr);
+ ASSERT_NE(loop_flow->merge.target, nullptr);
- auto* ir_if = b.FlowNodeForAstNode(ast_if);
+ auto* ir_if = FlowNodeForAstNode(ast_if);
ASSERT_NE(ir_if, nullptr);
ASSERT_TRUE(ir_if->Is<ir::If>());
auto* if_flow = ir_if->As<ir::If>();
- ASSERT_NE(if_flow->true_target, nullptr);
- ASSERT_NE(if_flow->false_target, nullptr);
- ASSERT_NE(if_flow->merge_target, nullptr);
+ ASSERT_NE(if_flow->true_.target, nullptr);
+ ASSERT_NE(if_flow->false_.target, nullptr);
+ ASSERT_NE(if_flow->merge.target, nullptr);
ASSERT_EQ(1u, m.functions.Length());
auto* func = m.functions[0];
EXPECT_EQ(1u, loop_flow->inbound_branches.Length());
- EXPECT_EQ(2u, loop_flow->start_target->inbound_branches.Length());
- EXPECT_EQ(1u, loop_flow->continuing_target->inbound_branches.Length());
- EXPECT_EQ(0u, loop_flow->merge_target->inbound_branches.Length());
+ EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length());
+ EXPECT_EQ(1u, loop_flow->continuing.target->inbound_branches.Length());
+ EXPECT_EQ(0u, loop_flow->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, if_flow->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow->true_target->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow->false_target->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow->merge_target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow->true_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow->false_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
- EXPECT_EQ(loop_flow->start_target->branch_target, if_flow);
- EXPECT_EQ(if_flow->true_target->branch_target, func->end_target);
- EXPECT_EQ(if_flow->false_target->branch_target, if_flow->merge_target);
- EXPECT_EQ(loop_flow->continuing_target->branch_target, loop_flow->start_target);
+ EXPECT_EQ(Disassemble(m), R"(%bb0 = Function test_function
+ %bb1 = Block
+ BranchTo %bb2 ()
- EXPECT_EQ(func->start_target->branch_target, ir_loop);
- EXPECT_EQ(loop_flow->merge_target->branch_target, nullptr);
+ %bb2 = loop
+ # loop start
+ %bb3 = Block
+ BranchTo %bb4 ()
- // Check condition
- ASSERT_TRUE(if_flow->condition->Is<Constant>());
- auto* instr = if_flow->condition->As<Constant>()->value;
- ASSERT_TRUE(instr->Is<constant::Scalar<bool>>());
- EXPECT_TRUE(instr->As<constant::Scalar<bool>>()->ValueAs<bool>());
+ %bb4 = if (true)
+ # true branch
+ %bb5 = Block
+ Return ()
+ # false branch
+ %bb6 = Block
+ BranchTo %bb7 ()
+
+ # if merge
+ %bb7 = Block
+ BranchTo %bb8 ()
+
+ # loop continuing
+ %bb8 = Block
+ BranchTo %bb3 ()
+
+ # loop merge
+ # Dead
+FunctionEnd
+
+)");
}
TEST_F(IR_BuilderImplTest, Loop_WithOnlyReturn) {
- // {
- // loop {
- // return;
- // continue;
- // }
- // if true { return; }
- // }
- //
- // func -> start -> loop -> loop start -> return -> func end
- //
- // [loop continuing] -> loop start
- // [loop merge] -> nullptr
- //
- // Note, the continue; is here is a dead call, so we won't emit a branch to the continuing block
- // so the inbound_branches will be zero for continuing.
- //
- // The `if` after the `loop` is also eliminated as there is no control-flow path reaching the
- // block.
auto* ast_loop = Loop(Block(Return(), Continue()));
WrapInFunction(ast_loop, If(true, Block(Return())));
- auto& b = CreateBuilder();
- auto r = b.Build();
- ASSERT_TRUE(r) << b.error();
+ auto r = Build();
+ ASSERT_TRUE(r) << Error();
auto m = r.Move();
- auto* ir_loop = b.FlowNodeForAstNode(ast_loop);
+ auto* ir_loop = FlowNodeForAstNode(ast_loop);
ASSERT_NE(ir_loop, nullptr);
EXPECT_TRUE(ir_loop->Is<ir::Loop>());
auto* loop_flow = ir_loop->As<ir::Loop>();
- ASSERT_NE(loop_flow->start_target, nullptr);
- ASSERT_NE(loop_flow->continuing_target, nullptr);
- ASSERT_NE(loop_flow->merge_target, nullptr);
+ ASSERT_NE(loop_flow->start.target, nullptr);
+ ASSERT_NE(loop_flow->continuing.target, nullptr);
+ ASSERT_NE(loop_flow->merge.target, nullptr);
ASSERT_EQ(1u, m.functions.Length());
auto* func = m.functions[0];
EXPECT_EQ(1u, loop_flow->inbound_branches.Length());
- EXPECT_EQ(2u, loop_flow->start_target->inbound_branches.Length());
- EXPECT_EQ(0u, loop_flow->continuing_target->inbound_branches.Length());
- EXPECT_EQ(0u, loop_flow->merge_target->inbound_branches.Length());
+ EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length());
+ EXPECT_EQ(0u, loop_flow->continuing.target->inbound_branches.Length());
+ EXPECT_EQ(0u, loop_flow->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
- EXPECT_EQ(loop_flow->start_target->branch_target, func->end_target);
- EXPECT_EQ(loop_flow->continuing_target->branch_target, loop_flow->start_target);
+ EXPECT_EQ(Disassemble(m), R"(%bb0 = Function test_function
+ %bb1 = Block
+ BranchTo %bb2 ()
- EXPECT_EQ(func->start_target->branch_target, ir_loop);
+ %bb2 = loop
+ # loop start
+ %bb3 = Block
+ Return ()
+ # loop continuing
+ %bb4 = Block
+ BranchTo %bb3 ()
+
+ # loop merge
+ # Dead
+FunctionEnd
+
+)");
}
TEST_F(IR_BuilderImplTest, Loop_WithOnlyReturn_ContinuingBreakIf) {
- // {
- // loop {
- // return;
- // continuing {
- // break if true;
- // }
- // }
- // if (true) { return; }
- // }
- //
- // func -> start -> loop -> loop start -> return -> func end
- //
- // [loop continuing] -> break if true
- // -> break if false
- // [break if true] -> loop merge
- // [break if false] -> if merge
- // [break if merge] -> loop start
- // [loop merge] -> nullptr
- //
- // In this case, the continuing block is dead code, but we don't really know that when parsing
- // so we end up with a branch into the loop merge target. The loop merge can tell it's dead code
- // so we can drop the if ater the loop.
+ // Note, even though there is code in the loop merge (specifically, the
+ // `ast_if` below), it doesn't get emitted as there is no way to reach the
+ // loop merge due to the loop itself doing a `return`. This is why the
+ // loop merge gets marked as Dead and the `ast_if` doesn't appear.
auto* ast_break_if = BreakIf(true);
auto* ast_loop = Loop(Block(Return()), Block(ast_break_if));
auto* ast_if = If(true, Block(Return()));
WrapInFunction(Block(ast_loop, ast_if));
- auto& b = CreateBuilder();
- auto r = b.Build();
- ASSERT_TRUE(r) << b.error();
+ auto r = Build();
+ ASSERT_TRUE(r) << Error();
auto m = r.Move();
- auto* ir_loop = b.FlowNodeForAstNode(ast_loop);
+ auto* ir_loop = FlowNodeForAstNode(ast_loop);
ASSERT_NE(ir_loop, nullptr);
EXPECT_TRUE(ir_loop->Is<ir::Loop>());
auto* loop_flow = ir_loop->As<ir::Loop>();
- ASSERT_NE(loop_flow->start_target, nullptr);
- ASSERT_NE(loop_flow->continuing_target, nullptr);
- ASSERT_NE(loop_flow->merge_target, nullptr);
+ ASSERT_NE(loop_flow->start.target, nullptr);
+ ASSERT_NE(loop_flow->continuing.target, nullptr);
+ ASSERT_NE(loop_flow->merge.target, nullptr);
- auto* ir_if = b.FlowNodeForAstNode(ast_if);
+ auto* ir_if = FlowNodeForAstNode(ast_if);
EXPECT_EQ(ir_if, nullptr);
- auto* ir_break_if = b.FlowNodeForAstNode(ast_break_if);
+ auto* ir_break_if = FlowNodeForAstNode(ast_break_if);
ASSERT_NE(ir_break_if, nullptr);
EXPECT_TRUE(ir_break_if->Is<ir::If>());
auto* break_if_flow = ir_break_if->As<ir::If>();
- ASSERT_NE(break_if_flow->true_target, nullptr);
- ASSERT_NE(break_if_flow->false_target, nullptr);
- ASSERT_NE(break_if_flow->merge_target, nullptr);
+ ASSERT_NE(break_if_flow->true_.target, nullptr);
+ ASSERT_NE(break_if_flow->false_.target, nullptr);
+ ASSERT_NE(break_if_flow->merge.target, nullptr);
ASSERT_EQ(1u, m.functions.Length());
auto* func = m.functions[0];
EXPECT_EQ(1u, loop_flow->inbound_branches.Length());
- EXPECT_EQ(2u, loop_flow->start_target->inbound_branches.Length());
- EXPECT_EQ(0u, loop_flow->continuing_target->inbound_branches.Length());
- EXPECT_EQ(1u, loop_flow->merge_target->inbound_branches.Length());
+ EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length());
+ EXPECT_EQ(0u, loop_flow->continuing.target->inbound_branches.Length());
+ EXPECT_EQ(1u, loop_flow->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
// This is 1 because only the loop branch happens. The subsequent if return is dead code.
EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
- EXPECT_EQ(loop_flow->start_target->branch_target, func->end_target);
- EXPECT_EQ(loop_flow->continuing_target->branch_target, break_if_flow);
+ EXPECT_EQ(Disassemble(m), R"(%bb0 = Function test_function
+ %bb1 = Block
+ BranchTo %bb2 ()
- EXPECT_EQ(func->start_target->branch_target, ir_loop);
+ %bb2 = loop
+ # loop start
+ %bb3 = Block
+ Return ()
+ # loop continuing
+ %bb4 = Block
+ BranchTo %bb5 ()
+
+ %bb5 = if (true)
+ # true branch
+ %bb6 = Block
+ BranchTo %bb7 ()
+
+ # false branch
+ %bb8 = Block
+ BranchTo %bb9 ()
+
+ # if merge
+ %bb9 = Block
+ BranchTo %bb3 ()
+
+ # loop merge
+ # Dead
+FunctionEnd
+
+)");
}
TEST_F(IR_BuilderImplTest, Loop_WithIf_BothBranchesBreak) {
- // func -> start -> loop -> loop start -> if -> true branch
- // -> false branch
- //
- // [if true] -> loop merge
- // [if false] -> loop merge
- // [if merge] -> nullptr
- // [loop continuing] -> loop start
- // [loop merge] -> func end
- //
auto* ast_if = If(true, Block(Break()), Else(Block(Break())));
auto* ast_loop = Loop(Block(ast_if, Continue()));
WrapInFunction(ast_loop);
- auto& b = CreateBuilder();
- auto r = b.Build();
- ASSERT_TRUE(r) << b.error();
+ auto r = Build();
+ ASSERT_TRUE(r) << Error();
auto m = r.Move();
- auto* ir_loop = b.FlowNodeForAstNode(ast_loop);
+ auto* ir_loop = FlowNodeForAstNode(ast_loop);
ASSERT_NE(ir_loop, nullptr);
EXPECT_TRUE(ir_loop->Is<ir::Loop>());
auto* loop_flow = ir_loop->As<ir::Loop>();
- ASSERT_NE(loop_flow->start_target, nullptr);
- ASSERT_NE(loop_flow->continuing_target, nullptr);
- ASSERT_NE(loop_flow->merge_target, nullptr);
+ ASSERT_NE(loop_flow->start.target, nullptr);
+ ASSERT_NE(loop_flow->continuing.target, nullptr);
+ ASSERT_NE(loop_flow->merge.target, nullptr);
- auto* ir_if = b.FlowNodeForAstNode(ast_if);
+ auto* ir_if = FlowNodeForAstNode(ast_if);
ASSERT_NE(ir_if, nullptr);
ASSERT_TRUE(ir_if->Is<ir::If>());
auto* if_flow = ir_if->As<ir::If>();
- ASSERT_NE(if_flow->true_target, nullptr);
- ASSERT_NE(if_flow->false_target, nullptr);
- ASSERT_NE(if_flow->merge_target, nullptr);
+ ASSERT_NE(if_flow->true_.target, nullptr);
+ ASSERT_NE(if_flow->false_.target, nullptr);
+ ASSERT_NE(if_flow->merge.target, nullptr);
ASSERT_EQ(1u, m.functions.Length());
auto* func = m.functions[0];
EXPECT_EQ(1u, loop_flow->inbound_branches.Length());
- EXPECT_EQ(2u, loop_flow->start_target->inbound_branches.Length());
- EXPECT_EQ(0u, loop_flow->continuing_target->inbound_branches.Length());
- EXPECT_EQ(2u, loop_flow->merge_target->inbound_branches.Length());
+ EXPECT_EQ(2u, loop_flow->start.target->inbound_branches.Length());
+ EXPECT_EQ(0u, loop_flow->continuing.target->inbound_branches.Length());
+ EXPECT_EQ(2u, loop_flow->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, if_flow->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow->true_target->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow->false_target->inbound_branches.Length());
- EXPECT_EQ(0u, if_flow->merge_target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow->true_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow->false_.target->inbound_branches.Length());
+ EXPECT_EQ(0u, if_flow->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
- // Note, the `continue` is dead code because both if branches go out of loop, so it just gets
- // dropped.
+ EXPECT_EQ(Disassemble(m), R"(%bb0 = Function test_function
+ %bb1 = Block
+ BranchTo %bb2 ()
- EXPECT_EQ(func->start_target->branch_target, loop_flow);
- EXPECT_EQ(loop_flow->start_target->branch_target, if_flow);
- EXPECT_EQ(if_flow->true_target->branch_target, loop_flow->merge_target);
- EXPECT_EQ(if_flow->false_target->branch_target, loop_flow->merge_target);
- EXPECT_EQ(loop_flow->continuing_target->branch_target, loop_flow->start_target);
- EXPECT_EQ(loop_flow->merge_target->branch_target, func->end_target);
+ %bb2 = loop
+ # loop start
+ %bb3 = Block
+ BranchTo %bb4 ()
+
+ %bb4 = if (true)
+ # true branch
+ %bb5 = Block
+ BranchTo %bb6 ()
+
+ # false branch
+ %bb7 = Block
+ BranchTo %bb6 ()
+
+ # loop continuing
+ %bb8 = Block
+ BranchTo %bb3 ()
+
+ # loop merge
+ %bb6 = Block
+ Return ()
+FunctionEnd
+
+)");
}
TEST_F(IR_BuilderImplTest, Loop_Nested) {
- // loop { // loop_a
- // loop { // loop_b
- // if (true) { break; } // if_a
- // if (true) { continue; } // if_b
- // continuing {
- // loop { // loop_c
- // break;
- // }
- //
- // loop { // loop_d
- // continuing {
- // break if (true); // if_c
- // }
- // }
- // }
- // }
- // if (true) { break; } // if_d
- // }
- //
- // func -> start -> loop_a -> loop_a start
- //
- // [loop_a start] -> loop_b
- // [loop_b start] -> if_a
- // [if_a true] -> loop_b merge
- // [if_a false] -> if_a merge
- // [if_a merge] -> if_b
- // [if_b true] -> loop_b continuing
- // [if_b false] -> if_b merge
- // [if_b merge] -> loop_b continug
- // [loop_b continuing] -> loop_c
- // [loop_c start] -> loop_c merge
- // [loop_c continuing] -> loop_c start
- // [loop_c merge] -> loop_d
- // [loop_d start] -> loop_d continuing
- // [loop_d continuing] -> if_c
- // [if_c true] -> loop_d merge
- // [if_c false] -> if_c merge
- // [if c merge] -> loop_d start
- // [loop_d merge] -> loop_b start
- // [loop_b merge] -> if_d
- // [if_d true] -> loop_a merge
- // [if_d false] -> if_d merge
- // [if_d merge] -> loop_a continuing
- // [loop_a continuing] -> loop_a start
- // [loop_a merge] -> func end
- //
-
auto* ast_if_a = If(true, Block(Break()));
auto* ast_if_b = If(true, Block(Continue()));
auto* ast_if_c = BreakIf(true);
@@ -754,265 +785,362 @@
auto* ast_loop_a = Loop(Block(ast_loop_b, ast_if_d));
WrapInFunction(ast_loop_a);
- auto& b = CreateBuilder();
- auto r = b.Build();
- ASSERT_TRUE(r) << b.error();
+ auto r = Build();
+ ASSERT_TRUE(r) << Error();
auto m = r.Move();
- auto* ir_loop_a = b.FlowNodeForAstNode(ast_loop_a);
+ auto* ir_loop_a = FlowNodeForAstNode(ast_loop_a);
ASSERT_NE(ir_loop_a, nullptr);
EXPECT_TRUE(ir_loop_a->Is<ir::Loop>());
auto* loop_flow_a = ir_loop_a->As<ir::Loop>();
- ASSERT_NE(loop_flow_a->start_target, nullptr);
- ASSERT_NE(loop_flow_a->continuing_target, nullptr);
- ASSERT_NE(loop_flow_a->merge_target, nullptr);
+ ASSERT_NE(loop_flow_a->start.target, nullptr);
+ ASSERT_NE(loop_flow_a->continuing.target, nullptr);
+ ASSERT_NE(loop_flow_a->merge.target, nullptr);
- auto* ir_loop_b = b.FlowNodeForAstNode(ast_loop_b);
+ auto* ir_loop_b = FlowNodeForAstNode(ast_loop_b);
ASSERT_NE(ir_loop_b, nullptr);
EXPECT_TRUE(ir_loop_b->Is<ir::Loop>());
auto* loop_flow_b = ir_loop_b->As<ir::Loop>();
- ASSERT_NE(loop_flow_b->start_target, nullptr);
- ASSERT_NE(loop_flow_b->continuing_target, nullptr);
- ASSERT_NE(loop_flow_b->merge_target, nullptr);
+ ASSERT_NE(loop_flow_b->start.target, nullptr);
+ ASSERT_NE(loop_flow_b->continuing.target, nullptr);
+ ASSERT_NE(loop_flow_b->merge.target, nullptr);
- auto* ir_loop_c = b.FlowNodeForAstNode(ast_loop_c);
+ auto* ir_loop_c = FlowNodeForAstNode(ast_loop_c);
ASSERT_NE(ir_loop_c, nullptr);
EXPECT_TRUE(ir_loop_c->Is<ir::Loop>());
auto* loop_flow_c = ir_loop_c->As<ir::Loop>();
- ASSERT_NE(loop_flow_c->start_target, nullptr);
- ASSERT_NE(loop_flow_c->continuing_target, nullptr);
- ASSERT_NE(loop_flow_c->merge_target, nullptr);
+ ASSERT_NE(loop_flow_c->start.target, nullptr);
+ ASSERT_NE(loop_flow_c->continuing.target, nullptr);
+ ASSERT_NE(loop_flow_c->merge.target, nullptr);
- auto* ir_loop_d = b.FlowNodeForAstNode(ast_loop_d);
+ auto* ir_loop_d = FlowNodeForAstNode(ast_loop_d);
ASSERT_NE(ir_loop_d, nullptr);
EXPECT_TRUE(ir_loop_d->Is<ir::Loop>());
auto* loop_flow_d = ir_loop_d->As<ir::Loop>();
- ASSERT_NE(loop_flow_d->start_target, nullptr);
- ASSERT_NE(loop_flow_d->continuing_target, nullptr);
- ASSERT_NE(loop_flow_d->merge_target, nullptr);
+ ASSERT_NE(loop_flow_d->start.target, nullptr);
+ ASSERT_NE(loop_flow_d->continuing.target, nullptr);
+ ASSERT_NE(loop_flow_d->merge.target, nullptr);
- auto* ir_if_a = b.FlowNodeForAstNode(ast_if_a);
+ auto* ir_if_a = FlowNodeForAstNode(ast_if_a);
ASSERT_NE(ir_if_a, nullptr);
EXPECT_TRUE(ir_if_a->Is<ir::If>());
auto* if_flow_a = ir_if_a->As<ir::If>();
- ASSERT_NE(if_flow_a->true_target, nullptr);
- ASSERT_NE(if_flow_a->false_target, nullptr);
- ASSERT_NE(if_flow_a->merge_target, nullptr);
+ ASSERT_NE(if_flow_a->true_.target, nullptr);
+ ASSERT_NE(if_flow_a->false_.target, nullptr);
+ ASSERT_NE(if_flow_a->merge.target, nullptr);
- auto* ir_if_b = b.FlowNodeForAstNode(ast_if_b);
+ auto* ir_if_b = FlowNodeForAstNode(ast_if_b);
ASSERT_NE(ir_if_b, nullptr);
EXPECT_TRUE(ir_if_b->Is<ir::If>());
auto* if_flow_b = ir_if_b->As<ir::If>();
- ASSERT_NE(if_flow_b->true_target, nullptr);
- ASSERT_NE(if_flow_b->false_target, nullptr);
- ASSERT_NE(if_flow_b->merge_target, nullptr);
+ ASSERT_NE(if_flow_b->true_.target, nullptr);
+ ASSERT_NE(if_flow_b->false_.target, nullptr);
+ ASSERT_NE(if_flow_b->merge.target, nullptr);
- auto* ir_if_c = b.FlowNodeForAstNode(ast_if_c);
+ auto* ir_if_c = FlowNodeForAstNode(ast_if_c);
ASSERT_NE(ir_if_c, nullptr);
EXPECT_TRUE(ir_if_c->Is<ir::If>());
auto* if_flow_c = ir_if_c->As<ir::If>();
- ASSERT_NE(if_flow_c->true_target, nullptr);
- ASSERT_NE(if_flow_c->false_target, nullptr);
- ASSERT_NE(if_flow_c->merge_target, nullptr);
+ ASSERT_NE(if_flow_c->true_.target, nullptr);
+ ASSERT_NE(if_flow_c->false_.target, nullptr);
+ ASSERT_NE(if_flow_c->merge.target, nullptr);
- auto* ir_if_d = b.FlowNodeForAstNode(ast_if_d);
+ auto* ir_if_d = FlowNodeForAstNode(ast_if_d);
ASSERT_NE(ir_if_d, nullptr);
EXPECT_TRUE(ir_if_d->Is<ir::If>());
auto* if_flow_d = ir_if_d->As<ir::If>();
- ASSERT_NE(if_flow_d->true_target, nullptr);
- ASSERT_NE(if_flow_d->false_target, nullptr);
- ASSERT_NE(if_flow_d->merge_target, nullptr);
+ ASSERT_NE(if_flow_d->true_.target, nullptr);
+ ASSERT_NE(if_flow_d->false_.target, nullptr);
+ ASSERT_NE(if_flow_d->merge.target, nullptr);
ASSERT_EQ(1u, m.functions.Length());
auto* func = m.functions[0];
EXPECT_EQ(1u, loop_flow_a->inbound_branches.Length());
- EXPECT_EQ(2u, loop_flow_a->start_target->inbound_branches.Length());
- EXPECT_EQ(1u, loop_flow_a->continuing_target->inbound_branches.Length());
- EXPECT_EQ(1u, loop_flow_a->merge_target->inbound_branches.Length());
+ EXPECT_EQ(2u, loop_flow_a->start.target->inbound_branches.Length());
+ EXPECT_EQ(1u, loop_flow_a->continuing.target->inbound_branches.Length());
+ EXPECT_EQ(1u, loop_flow_a->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, loop_flow_b->inbound_branches.Length());
- EXPECT_EQ(2u, loop_flow_b->start_target->inbound_branches.Length());
- EXPECT_EQ(2u, loop_flow_b->continuing_target->inbound_branches.Length());
- EXPECT_EQ(1u, loop_flow_b->merge_target->inbound_branches.Length());
+ EXPECT_EQ(2u, loop_flow_b->start.target->inbound_branches.Length());
+ EXPECT_EQ(2u, loop_flow_b->continuing.target->inbound_branches.Length());
+ EXPECT_EQ(1u, loop_flow_b->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, loop_flow_c->inbound_branches.Length());
- EXPECT_EQ(2u, loop_flow_c->start_target->inbound_branches.Length());
- EXPECT_EQ(0u, loop_flow_c->continuing_target->inbound_branches.Length());
- EXPECT_EQ(1u, loop_flow_c->merge_target->inbound_branches.Length());
+ EXPECT_EQ(2u, loop_flow_c->start.target->inbound_branches.Length());
+ EXPECT_EQ(0u, loop_flow_c->continuing.target->inbound_branches.Length());
+ EXPECT_EQ(1u, loop_flow_c->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, loop_flow_d->inbound_branches.Length());
- EXPECT_EQ(2u, loop_flow_d->start_target->inbound_branches.Length());
- EXPECT_EQ(1u, loop_flow_d->continuing_target->inbound_branches.Length());
- EXPECT_EQ(1u, loop_flow_d->merge_target->inbound_branches.Length());
+ EXPECT_EQ(2u, loop_flow_d->start.target->inbound_branches.Length());
+ EXPECT_EQ(1u, loop_flow_d->continuing.target->inbound_branches.Length());
+ EXPECT_EQ(1u, loop_flow_d->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, if_flow_a->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow_a->true_target->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow_a->false_target->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow_a->merge_target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow_a->true_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow_a->false_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow_a->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, if_flow_b->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow_b->true_target->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow_b->false_target->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow_b->merge_target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow_b->true_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow_b->false_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow_b->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, if_flow_c->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow_c->true_target->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow_c->false_target->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow_c->merge_target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow_c->true_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow_c->false_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow_c->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, if_flow_d->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow_d->true_target->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow_d->false_target->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow_d->merge_target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow_d->true_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow_d->false_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow_d->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, func->start_target->inbound_branches.Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
- EXPECT_EQ(func->start_target->branch_target, loop_flow_a);
- EXPECT_EQ(loop_flow_a->start_target->branch_target, loop_flow_b);
- EXPECT_EQ(loop_flow_b->start_target->branch_target, if_flow_a);
- EXPECT_EQ(if_flow_a->true_target->branch_target, loop_flow_b->merge_target);
- EXPECT_EQ(if_flow_a->false_target->branch_target, if_flow_a->merge_target);
- EXPECT_EQ(if_flow_a->merge_target->branch_target, if_flow_b);
- EXPECT_EQ(if_flow_b->true_target->branch_target, loop_flow_b->continuing_target);
- EXPECT_EQ(if_flow_b->false_target->branch_target, if_flow_b->merge_target);
- EXPECT_EQ(if_flow_b->merge_target->branch_target, loop_flow_b->continuing_target);
- EXPECT_EQ(loop_flow_b->continuing_target->branch_target, loop_flow_c);
- EXPECT_EQ(loop_flow_c->start_target->branch_target, loop_flow_c->merge_target);
- EXPECT_EQ(loop_flow_c->continuing_target->branch_target, loop_flow_c->start_target);
- EXPECT_EQ(loop_flow_c->merge_target->branch_target, loop_flow_d);
- EXPECT_EQ(loop_flow_d->start_target->branch_target, loop_flow_d->continuing_target);
- EXPECT_EQ(loop_flow_d->continuing_target->branch_target, if_flow_c);
- EXPECT_EQ(if_flow_c->true_target->branch_target, loop_flow_d->merge_target);
- EXPECT_EQ(if_flow_c->false_target->branch_target, if_flow_c->merge_target);
- EXPECT_EQ(if_flow_c->merge_target->branch_target, loop_flow_d->start_target);
- EXPECT_EQ(loop_flow_d->merge_target->branch_target, loop_flow_b->start_target);
- EXPECT_EQ(loop_flow_b->merge_target->branch_target, if_flow_d);
- EXPECT_EQ(if_flow_d->true_target->branch_target, loop_flow_a->merge_target);
- EXPECT_EQ(if_flow_d->false_target->branch_target, if_flow_d->merge_target);
- EXPECT_EQ(if_flow_d->merge_target->branch_target, loop_flow_a->continuing_target);
- EXPECT_EQ(loop_flow_a->continuing_target->branch_target, loop_flow_a->start_target);
- EXPECT_EQ(loop_flow_a->merge_target->branch_target, func->end_target);
+ EXPECT_EQ(Disassemble(m), R"(%bb0 = Function test_function
+ %bb1 = Block
+ BranchTo %bb2 ()
+
+ %bb2 = loop
+ # loop start
+ %bb3 = Block
+ BranchTo %bb4 ()
+
+ %bb4 = loop
+ # loop start
+ %bb5 = Block
+ BranchTo %bb6 ()
+
+ %bb6 = if (true)
+ # true branch
+ %bb7 = Block
+ BranchTo %bb8 ()
+
+ # false branch
+ %bb9 = Block
+ BranchTo %bb10 ()
+
+ # if merge
+ %bb10 = Block
+ BranchTo %bb11 ()
+
+ %bb11 = if (true)
+ # true branch
+ %bb12 = Block
+ BranchTo %bb13 ()
+
+ # false branch
+ %bb14 = Block
+ BranchTo %bb15 ()
+
+ # if merge
+ %bb15 = Block
+ BranchTo %bb13 ()
+
+ # loop continuing
+ %bb13 = Block
+ BranchTo %bb16 ()
+
+ %bb16 = loop
+ # loop start
+ %bb17 = Block
+ BranchTo %bb18 ()
+
+ # loop continuing
+ %bb19 = Block
+ BranchTo %bb17 ()
+
+ # loop merge
+ %bb18 = Block
+ BranchTo %bb20 ()
+
+ %bb20 = loop
+ # loop start
+ %bb21 = Block
+ BranchTo %bb22 ()
+
+ # loop continuing
+ %bb22 = Block
+ BranchTo %bb23 ()
+
+ %bb23 = if (true)
+ # true branch
+ %bb24 = Block
+ BranchTo %bb25 ()
+
+ # false branch
+ %bb26 = Block
+ BranchTo %bb27 ()
+
+ # if merge
+ %bb27 = Block
+ BranchTo %bb21 ()
+
+ # loop merge
+ %bb25 = Block
+ BranchTo %bb5 ()
+
+ # loop merge
+ %bb8 = Block
+ BranchTo %bb28 ()
+
+ %bb28 = if (true)
+ # true branch
+ %bb29 = Block
+ BranchTo %bb30 ()
+
+ # false branch
+ %bb31 = Block
+ BranchTo %bb32 ()
+
+ # if merge
+ %bb32 = Block
+ BranchTo %bb33 ()
+
+ # loop continuing
+ %bb33 = Block
+ BranchTo %bb3 ()
+
+ # loop merge
+ %bb30 = Block
+ Return ()
+FunctionEnd
+
+)");
}
TEST_F(IR_BuilderImplTest, While) {
- // {
- // while false {
- // }
- // }
- //
- // func -> while -> loop_start -> if true
- // -> if false
- //
- // [if true] -> if merge
- // [if false] -> while merge
- // [if merge] -> loop continuing
- // [loop continuing] -> loop start
- // [while merge] -> func end
- //
auto* ast_while = While(false, Block());
WrapInFunction(ast_while);
- auto& b = CreateBuilder();
- auto r = b.Build();
- ASSERT_TRUE(r) << b.error();
+ auto r = Build();
+ ASSERT_TRUE(r) << Error();
auto m = r.Move();
- auto* ir_while = b.FlowNodeForAstNode(ast_while);
+ auto* ir_while = FlowNodeForAstNode(ast_while);
ASSERT_NE(ir_while, nullptr);
ASSERT_TRUE(ir_while->Is<ir::Loop>());
auto* flow = ir_while->As<ir::Loop>();
- ASSERT_NE(flow->start_target, nullptr);
- ASSERT_NE(flow->continuing_target, nullptr);
- ASSERT_NE(flow->merge_target, nullptr);
+ ASSERT_NE(flow->start.target, nullptr);
+ ASSERT_NE(flow->continuing.target, nullptr);
+ ASSERT_NE(flow->merge.target, nullptr);
- ASSERT_NE(flow->start_target->branch_target, nullptr);
- ASSERT_TRUE(flow->start_target->branch_target->Is<ir::If>());
- auto* if_flow = flow->start_target->branch_target->As<ir::If>();
- ASSERT_NE(if_flow->true_target, nullptr);
- ASSERT_NE(if_flow->false_target, nullptr);
- ASSERT_NE(if_flow->merge_target, nullptr);
+ ASSERT_NE(flow->start.target->As<ir::Block>()->branch.target, nullptr);
+ ASSERT_TRUE(flow->start.target->As<ir::Block>()->branch.target->Is<ir::If>());
+ auto* if_flow = flow->start.target->As<ir::Block>()->branch.target->As<ir::If>();
+ ASSERT_NE(if_flow->true_.target, nullptr);
+ ASSERT_NE(if_flow->false_.target, nullptr);
+ ASSERT_NE(if_flow->merge.target, nullptr);
ASSERT_EQ(1u, m.functions.Length());
auto* func = m.functions[0];
EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
EXPECT_EQ(1u, flow->inbound_branches.Length());
- EXPECT_EQ(2u, flow->start_target->inbound_branches.Length());
- EXPECT_EQ(1u, flow->continuing_target->inbound_branches.Length());
- EXPECT_EQ(1u, flow->merge_target->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow->true_target->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow->false_target->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow->merge_target->inbound_branches.Length());
+ EXPECT_EQ(2u, flow->start.target->inbound_branches.Length());
+ EXPECT_EQ(1u, flow->continuing.target->inbound_branches.Length());
+ EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow->true_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow->false_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow->merge.target->inbound_branches.Length());
- EXPECT_EQ(func->start_target->branch_target, flow);
- EXPECT_EQ(flow->start_target->branch_target, if_flow);
- EXPECT_EQ(if_flow->true_target->branch_target, if_flow->merge_target);
- EXPECT_EQ(if_flow->false_target->branch_target, flow->merge_target);
- EXPECT_EQ(if_flow->merge_target->branch_target, flow->continuing_target);
- EXPECT_EQ(flow->continuing_target->branch_target, flow->start_target);
- EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+ EXPECT_EQ(Disassemble(m), R"(%bb0 = Function test_function
+ %bb1 = Block
+ BranchTo %bb2 ()
- // Check condition
- ASSERT_TRUE(if_flow->condition->Is<Constant>());
- auto* instr = if_flow->condition->As<Constant>()->value;
- ASSERT_TRUE(instr->Is<constant::Scalar<bool>>());
- EXPECT_FALSE(instr->As<constant::Scalar<bool>>()->ValueAs<bool>());
+ %bb2 = loop
+ # loop start
+ %bb3 = Block
+ BranchTo %bb4 ()
+
+ %bb4 = if (false)
+ # true branch
+ %bb5 = Block
+ BranchTo %bb6 ()
+
+ # false branch
+ %bb7 = Block
+ BranchTo %bb8 ()
+
+ # if merge
+ %bb6 = Block
+ BranchTo %bb9 ()
+
+ # loop continuing
+ %bb9 = Block
+ BranchTo %bb3 ()
+
+ # loop merge
+ %bb8 = Block
+ Return ()
+FunctionEnd
+
+)");
}
TEST_F(IR_BuilderImplTest, While_Return) {
- // {
- // while true {
- // return;
- // }
- // }
- //
- // func -> while -> if true
- // if false
- //
- // [if true] -> if merge
- // [if false] -> while merge
- // [if merge] -> func end
- // [while merge] -> func end
- //
auto* ast_while = While(true, Block(Return()));
WrapInFunction(ast_while);
- auto& b = CreateBuilder();
- auto r = b.Build();
- ASSERT_TRUE(r) << b.error();
+ auto r = Build();
+ ASSERT_TRUE(r) << Error();
auto m = r.Move();
- auto* ir_while = b.FlowNodeForAstNode(ast_while);
+ auto* ir_while = FlowNodeForAstNode(ast_while);
ASSERT_NE(ir_while, nullptr);
ASSERT_TRUE(ir_while->Is<ir::Loop>());
auto* flow = ir_while->As<ir::Loop>();
- ASSERT_NE(flow->start_target, nullptr);
- ASSERT_NE(flow->continuing_target, nullptr);
- ASSERT_NE(flow->merge_target, nullptr);
+ ASSERT_NE(flow->start.target, nullptr);
+ ASSERT_NE(flow->continuing.target, nullptr);
+ ASSERT_NE(flow->merge.target, nullptr);
- ASSERT_NE(flow->start_target->branch_target, nullptr);
- ASSERT_TRUE(flow->start_target->branch_target->Is<ir::If>());
- auto* if_flow = flow->start_target->branch_target->As<ir::If>();
- ASSERT_NE(if_flow->true_target, nullptr);
- ASSERT_NE(if_flow->false_target, nullptr);
- ASSERT_NE(if_flow->merge_target, nullptr);
+ ASSERT_NE(flow->start.target->As<ir::Block>()->branch.target, nullptr);
+ ASSERT_TRUE(flow->start.target->As<ir::Block>()->branch.target->Is<ir::If>());
+ auto* if_flow = flow->start.target->As<ir::Block>()->branch.target->As<ir::If>();
+ ASSERT_NE(if_flow->true_.target, nullptr);
+ ASSERT_NE(if_flow->false_.target, nullptr);
+ ASSERT_NE(if_flow->merge.target, nullptr);
ASSERT_EQ(1u, m.functions.Length());
auto* func = m.functions[0];
EXPECT_EQ(2u, func->end_target->inbound_branches.Length());
EXPECT_EQ(1u, flow->inbound_branches.Length());
- EXPECT_EQ(2u, flow->start_target->inbound_branches.Length());
- EXPECT_EQ(0u, flow->continuing_target->inbound_branches.Length());
- EXPECT_EQ(1u, flow->merge_target->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow->true_target->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow->false_target->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow->merge_target->inbound_branches.Length());
+ EXPECT_EQ(2u, flow->start.target->inbound_branches.Length());
+ EXPECT_EQ(0u, flow->continuing.target->inbound_branches.Length());
+ EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow->true_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow->false_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow->merge.target->inbound_branches.Length());
- EXPECT_EQ(func->start_target->branch_target, flow);
- EXPECT_EQ(flow->start_target->branch_target, if_flow);
- EXPECT_EQ(if_flow->true_target->branch_target, if_flow->merge_target);
- EXPECT_EQ(if_flow->false_target->branch_target, flow->merge_target);
- EXPECT_EQ(if_flow->merge_target->branch_target, func->end_target);
- EXPECT_EQ(flow->continuing_target->branch_target, flow->start_target);
- EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+ EXPECT_EQ(Disassemble(m), R"(%bb0 = Function test_function
+ %bb1 = Block
+ BranchTo %bb2 ()
+
+ %bb2 = loop
+ # loop start
+ %bb3 = Block
+ BranchTo %bb4 ()
+
+ %bb4 = if (true)
+ # true branch
+ %bb5 = Block
+ BranchTo %bb6 ()
+
+ # false branch
+ %bb7 = Block
+ BranchTo %bb8 ()
+
+ # if merge
+ %bb6 = Block
+ Return ()
+ # loop continuing
+ %bb9 = Block
+ BranchTo %bb3 ()
+
+ # loop merge
+ %bb8 = Block
+ Return ()
+FunctionEnd
+
+)");
}
// TODO(dsinclair): Enable when variable declarations and increment are supported
@@ -1031,314 +1159,311 @@
//
auto* ast_for = For(Decl(Var("i", ty.i32())), LessThan("i", 10_a), Increment("i"), Block());
WrapInFunction(ast_for);
- auto& b = CreateBuilder();
- auto r = b.Build();
- ASSERT_TRUE(r) << b.error();
+ auto r = Build();
+ ASSERT_TRUE(r) << Error();
auto m = r.Move();
- auto* ir_for = b.FlowNodeForAstNode(ast_for);
+ auto* ir_for = FlowNodeForAstNode(ast_for);
ASSERT_NE(ir_for, nullptr);
ASSERT_TRUE(ir_for->Is<ir::Loop>());
auto* flow = ir_for->As<ir::Loop>();
- ASSERT_NE(flow->start_target, nullptr);
- ASSERT_NE(flow->continuing_target, nullptr);
- ASSERT_NE(flow->merge_target, nullptr);
+ ASSERT_NE(flow->start.target, nullptr);
+ ASSERT_NE(flow->continuing.target, nullptr);
+ ASSERT_NE(flow->merge.target, nullptr);
- ASSERT_NE(flow->start_target->branch_target, nullptr);
- ASSERT_TRUE(flow->start_target->branch_target->Is<ir::If>());
- auto* if_flow = flow->start_target->branch_target->As<ir::If>();
- ASSERT_NE(if_flow->true_target, nullptr);
- ASSERT_NE(if_flow->false_target, nullptr);
- ASSERT_NE(if_flow->merge_target, nullptr);
+ ASSERT_NE(flow->start.target->As<ir::Block>()->branch.target, nullptr);
+ ASSERT_TRUE(flow->start.target->As<ir::Block>()->branch.target->Is<ir::If>());
+ auto* if_flow = flow->start.target->As<ir::Block>()->branch.target->As<ir::If>();
+ ASSERT_NE(if_flow->true_.target, nullptr);
+ ASSERT_NE(if_flow->false_.target, nullptr);
+ ASSERT_NE(if_flow->merge.target, nullptr);
ASSERT_EQ(1u, m.functions.Length());
auto* func = m.functions[0];
EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
EXPECT_EQ(1u, flow->inbound_branches.Length());
- EXPECT_EQ(2u, flow->start_target->inbound_branches.Length());
- EXPECT_EQ(1u, flow->continuing_target->inbound_branches.Length());
- EXPECT_EQ(1u, flow->merge_target->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow->true_target->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow->false_target->inbound_branches.Length());
- EXPECT_EQ(1u, if_flow->merge_target->inbound_branches.Length());
+ EXPECT_EQ(2u, flow->start.target->inbound_branches.Length());
+ EXPECT_EQ(1u, flow->continuing.target->inbound_branches.Length());
+ EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow->true_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow->false_.target->inbound_branches.Length());
+ EXPECT_EQ(1u, if_flow->merge.target->inbound_branches.Length());
- EXPECT_EQ(func->start_target->branch_target, flow);
- EXPECT_EQ(flow->start_target->branch_target, if_flow);
- EXPECT_EQ(if_flow->true_target->branch_target, if_flow->merge_target);
- EXPECT_EQ(if_flow->false_target->branch_target, flow->merge_target);
- EXPECT_EQ(if_flow->merge_target->branch_target, flow->continuing_target);
- EXPECT_EQ(flow->continuing_target->branch_target, flow->start_target);
- EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
-
- // Check condition
- ASSERT_TRUE(if_flow->condition->Is<Constant>());
- auto* instr = if_flow->condition->As<Constant>()->value;
- ASSERT_TRUE(instr->Is<constant::Scalar<bool>>());
- EXPECT_FALSE(instr->As<constant::Scalar<bool>>()->ValueAs<bool>());
+ EXPECT_EQ(Disassemble(m), R"()");
}
TEST_F(IR_BuilderImplTest, For_NoInitCondOrContinuing) {
- // for (;;) {
- // break;
- // }
- //
- // func -> loop -> loop start -> loop merge -> func end
- //
auto* ast_for = For(nullptr, nullptr, nullptr, Block(Break()));
WrapInFunction(ast_for);
- auto& b = CreateBuilder();
- auto r = b.Build();
- ASSERT_TRUE(r) << b.error();
+ auto r = Build();
+ ASSERT_TRUE(r) << Error();
auto m = r.Move();
- auto* ir_for = b.FlowNodeForAstNode(ast_for);
+ auto* ir_for = FlowNodeForAstNode(ast_for);
ASSERT_NE(ir_for, nullptr);
ASSERT_TRUE(ir_for->Is<ir::Loop>());
auto* flow = ir_for->As<ir::Loop>();
- ASSERT_NE(flow->start_target, nullptr);
- ASSERT_NE(flow->continuing_target, nullptr);
- ASSERT_NE(flow->merge_target, nullptr);
+ ASSERT_NE(flow->start.target, nullptr);
+ ASSERT_NE(flow->continuing.target, nullptr);
+ ASSERT_NE(flow->merge.target, nullptr);
ASSERT_EQ(1u, m.functions.Length());
auto* func = m.functions[0];
EXPECT_EQ(1u, flow->inbound_branches.Length());
- EXPECT_EQ(2u, flow->start_target->inbound_branches.Length());
- EXPECT_EQ(0u, flow->continuing_target->inbound_branches.Length());
- EXPECT_EQ(1u, flow->merge_target->inbound_branches.Length());
+ EXPECT_EQ(2u, flow->start.target->inbound_branches.Length());
+ EXPECT_EQ(0u, flow->continuing.target->inbound_branches.Length());
+ EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
- EXPECT_EQ(flow->start_target->branch_target, flow->merge_target);
- EXPECT_EQ(flow->continuing_target->branch_target, flow->start_target);
- EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+ EXPECT_EQ(Disassemble(m), R"(%bb0 = Function test_function
+ %bb1 = Block
+ BranchTo %bb2 ()
+
+ %bb2 = loop
+ # loop start
+ %bb3 = Block
+ BranchTo %bb4 ()
+
+ # loop continuing
+ %bb5 = Block
+ BranchTo %bb3 ()
+
+ # loop merge
+ %bb4 = Block
+ Return ()
+FunctionEnd
+
+)");
}
TEST_F(IR_BuilderImplTest, Switch) {
- // func -> switch -> case 1
- // -> case 2
- // -> default
- //
- // [case 1] -> switch merge
- // [case 2] -> switch merge
- // [default] -> switch merge
- // [switch merge] -> func end
- //
auto* ast_switch = Switch(
1_i, utils::Vector{Case(utils::Vector{CaseSelector(0_i)}, Block()),
Case(utils::Vector{CaseSelector(1_i)}, Block()), DefaultCase(Block())});
WrapInFunction(ast_switch);
- auto& b = CreateBuilder();
- auto r = b.Build();
- ASSERT_TRUE(r) << b.error();
+ auto r = Build();
+ ASSERT_TRUE(r) << Error();
auto m = r.Move();
- auto* ir_switch = b.FlowNodeForAstNode(ast_switch);
+ auto* ir_switch = FlowNodeForAstNode(ast_switch);
ASSERT_NE(ir_switch, nullptr);
ASSERT_TRUE(ir_switch->Is<ir::Switch>());
auto* flow = ir_switch->As<ir::Switch>();
- ASSERT_NE(flow->merge_target, nullptr);
+ ASSERT_NE(flow->merge.target, nullptr);
ASSERT_EQ(3u, flow->cases.Length());
ASSERT_EQ(1u, m.functions.Length());
auto* func = m.functions[0];
ASSERT_EQ(1u, flow->cases[0].selectors.Length());
- ASSERT_TRUE(flow->cases[0].selectors[0]->expr->Is<ast::IntLiteralExpression>());
- EXPECT_EQ(0_i, flow->cases[0].selectors[0]->expr->As<ast::IntLiteralExpression>()->value);
+ ASSERT_TRUE(flow->cases[0].selectors[0].val->value->Is<constant::Scalar<tint::i32>>());
+ EXPECT_EQ(0_i,
+ flow->cases[0].selectors[0].val->value->As<constant::Scalar<tint::i32>>()->ValueOf());
ASSERT_EQ(1u, flow->cases[1].selectors.Length());
- ASSERT_TRUE(flow->cases[1].selectors[0]->expr->Is<ast::IntLiteralExpression>());
- EXPECT_EQ(1_i, flow->cases[1].selectors[0]->expr->As<ast::IntLiteralExpression>()->value);
+ ASSERT_TRUE(flow->cases[1].selectors[0].val->value->Is<constant::Scalar<tint::i32>>());
+ EXPECT_EQ(1_i,
+ flow->cases[1].selectors[0].val->value->As<constant::Scalar<tint::i32>>()->ValueOf());
ASSERT_EQ(1u, flow->cases[2].selectors.Length());
- EXPECT_TRUE(flow->cases[2].selectors[0]->IsDefault());
+ EXPECT_TRUE(flow->cases[2].selectors[0].IsDefault());
EXPECT_EQ(1u, flow->inbound_branches.Length());
- EXPECT_EQ(1u, flow->cases[0].start_target->inbound_branches.Length());
- EXPECT_EQ(1u, flow->cases[1].start_target->inbound_branches.Length());
- EXPECT_EQ(1u, flow->cases[2].start_target->inbound_branches.Length());
- EXPECT_EQ(3u, flow->merge_target->inbound_branches.Length());
+ EXPECT_EQ(1u, flow->cases[0].start.target->inbound_branches.Length());
+ EXPECT_EQ(1u, flow->cases[1].start.target->inbound_branches.Length());
+ EXPECT_EQ(1u, flow->cases[2].start.target->inbound_branches.Length());
+ EXPECT_EQ(3u, flow->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
- EXPECT_EQ(func->start_target->branch_target, ir_switch);
- EXPECT_EQ(flow->cases[0].start_target->branch_target, flow->merge_target);
- EXPECT_EQ(flow->cases[1].start_target->branch_target, flow->merge_target);
- EXPECT_EQ(flow->cases[2].start_target->branch_target, flow->merge_target);
- EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+ EXPECT_EQ(Disassemble(m), R"(%bb0 = Function test_function
+ %bb1 = Block
+ BranchTo %bb2 ()
- // Check condition
- ASSERT_TRUE(flow->condition->Is<Constant>());
- auto* instr = flow->condition->As<Constant>()->value;
- ASSERT_TRUE(instr->Is<constant::Scalar<i32>>());
- EXPECT_EQ(1_i, instr->As<constant::Scalar<i32>>()->ValueAs<i32>());
+ %bb2 = Switch (1)
+ # Case 0
+ %bb3 = Block
+ BranchTo %bb4 ()
+
+ # Case 1
+ %bb5 = Block
+ BranchTo %bb4 ()
+
+ # Case default
+ %bb6 = Block
+ BranchTo %bb4 ()
+
+ # Switch Merge
+ %bb4 = Block
+ Return ()
+FunctionEnd
+
+)");
}
TEST_F(IR_BuilderImplTest, Switch_OnlyDefault) {
- // func -> switch -> default -> switch merge -> func end
- //
auto* ast_switch = Switch(1_i, utils::Vector{DefaultCase(Block())});
-
WrapInFunction(ast_switch);
- auto& b = CreateBuilder();
- auto r = b.Build();
- ASSERT_TRUE(r) << b.error();
+ auto r = Build();
+ ASSERT_TRUE(r) << Error();
auto m = r.Move();
- auto* ir_switch = b.FlowNodeForAstNode(ast_switch);
+ auto* ir_switch = FlowNodeForAstNode(ast_switch);
ASSERT_NE(ir_switch, nullptr);
ASSERT_TRUE(ir_switch->Is<ir::Switch>());
auto* flow = ir_switch->As<ir::Switch>();
- ASSERT_NE(flow->merge_target, nullptr);
+ ASSERT_NE(flow->merge.target, nullptr);
ASSERT_EQ(1u, flow->cases.Length());
ASSERT_EQ(1u, m.functions.Length());
auto* func = m.functions[0];
ASSERT_EQ(1u, flow->cases[0].selectors.Length());
- EXPECT_TRUE(flow->cases[0].selectors[0]->IsDefault());
+ EXPECT_TRUE(flow->cases[0].selectors[0].IsDefault());
EXPECT_EQ(1u, flow->inbound_branches.Length());
- EXPECT_EQ(1u, flow->cases[0].start_target->inbound_branches.Length());
- EXPECT_EQ(1u, flow->merge_target->inbound_branches.Length());
+ EXPECT_EQ(1u, flow->cases[0].start.target->inbound_branches.Length());
+ EXPECT_EQ(1u, flow->merge.target->inbound_branches.Length());
EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
- EXPECT_EQ(func->start_target->branch_target, ir_switch);
- EXPECT_EQ(flow->cases[0].start_target->branch_target, flow->merge_target);
- EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+ EXPECT_EQ(Disassemble(m), R"(%bb0 = Function test_function
+ %bb1 = Block
+ BranchTo %bb2 ()
+
+ %bb2 = Switch (1)
+ # Case default
+ %bb3 = Block
+ BranchTo %bb4 ()
+
+ # Switch Merge
+ %bb4 = Block
+ Return ()
+FunctionEnd
+
+)");
}
TEST_F(IR_BuilderImplTest, Switch_WithBreak) {
- // {
- // switch(1) {
- // case 0: {
- // break;
- // if true { return;} // Dead code
- // }
- // default: {}
- // }
- // }
- //
- // func -> switch -> case 1
- // -> default
- //
- // [case 1] -> switch merge
- // [default] -> switch merge
- // [switch merge] -> func end
auto* ast_switch = Switch(1_i, utils::Vector{Case(utils::Vector{CaseSelector(0_i)},
Block(Break(), If(true, Block(Return())))),
DefaultCase(Block())});
-
WrapInFunction(ast_switch);
- auto& b = CreateBuilder();
- auto r = b.Build();
- ASSERT_TRUE(r) << b.error();
+ auto r = Build();
+ ASSERT_TRUE(r) << Error();
auto m = r.Move();
- auto* ir_switch = b.FlowNodeForAstNode(ast_switch);
+ auto* ir_switch = FlowNodeForAstNode(ast_switch);
ASSERT_NE(ir_switch, nullptr);
ASSERT_TRUE(ir_switch->Is<ir::Switch>());
auto* flow = ir_switch->As<ir::Switch>();
- ASSERT_NE(flow->merge_target, nullptr);
+ ASSERT_NE(flow->merge.target, nullptr);
ASSERT_EQ(2u, flow->cases.Length());
ASSERT_EQ(1u, m.functions.Length());
auto* func = m.functions[0];
ASSERT_EQ(1u, flow->cases[0].selectors.Length());
- ASSERT_TRUE(flow->cases[0].selectors[0]->expr->Is<ast::IntLiteralExpression>());
- EXPECT_EQ(0_i, flow->cases[0].selectors[0]->expr->As<ast::IntLiteralExpression>()->value);
+ ASSERT_TRUE(flow->cases[0].selectors[0].val->value->Is<constant::Scalar<tint::i32>>());
+ EXPECT_EQ(0_i,
+ flow->cases[0].selectors[0].val->value->As<constant::Scalar<tint::i32>>()->ValueOf());
ASSERT_EQ(1u, flow->cases[1].selectors.Length());
- EXPECT_TRUE(flow->cases[1].selectors[0]->IsDefault());
+ EXPECT_TRUE(flow->cases[1].selectors[0].IsDefault());
EXPECT_EQ(1u, flow->inbound_branches.Length());
- EXPECT_EQ(1u, flow->cases[0].start_target->inbound_branches.Length());
- EXPECT_EQ(1u, flow->cases[1].start_target->inbound_branches.Length());
- EXPECT_EQ(2u, flow->merge_target->inbound_branches.Length());
+ EXPECT_EQ(1u, flow->cases[0].start.target->inbound_branches.Length());
+ EXPECT_EQ(1u, flow->cases[1].start.target->inbound_branches.Length());
+ EXPECT_EQ(2u, flow->merge.target->inbound_branches.Length());
// This is 1 because the if is dead-code eliminated and the return doesn't happen.
EXPECT_EQ(1u, func->end_target->inbound_branches.Length());
- EXPECT_EQ(func->start_target->branch_target, ir_switch);
- EXPECT_EQ(flow->cases[0].start_target->branch_target, flow->merge_target);
- EXPECT_EQ(flow->cases[1].start_target->branch_target, flow->merge_target);
- EXPECT_EQ(flow->merge_target->branch_target, func->end_target);
+ EXPECT_EQ(Disassemble(m), R"(%bb0 = Function test_function
+ %bb1 = Block
+ BranchTo %bb2 ()
+
+ %bb2 = Switch (1)
+ # Case 0
+ %bb3 = Block
+ BranchTo %bb4 ()
+
+ # Case default
+ %bb5 = Block
+ BranchTo %bb4 ()
+
+ # Switch Merge
+ %bb4 = Block
+ Return ()
+FunctionEnd
+
+)");
}
TEST_F(IR_BuilderImplTest, Switch_AllReturn) {
- // {
- // switch(1) {
- // case 0: {
- // return;
- // }
- // default: {
- // return;
- // }
- // }
- // if true { return; } // Dead code
- // }
- //
- // func -> switch -> case 1
- // -> default
- //
- // [case 1] -> func end
- // [default] -> func end
- // [switch merge] -> nullptr
- //
auto* ast_switch =
Switch(1_i, utils::Vector{Case(utils::Vector{CaseSelector(0_i)}, Block(Return())),
DefaultCase(Block(Return()))});
-
auto* ast_if = If(true, Block(Return()));
-
WrapInFunction(ast_switch, ast_if);
- auto& b = CreateBuilder();
- auto r = b.Build();
- ASSERT_TRUE(r) << b.error();
+ auto r = Build();
+ ASSERT_TRUE(r) << Error();
auto m = r.Move();
- ASSERT_EQ(b.FlowNodeForAstNode(ast_if), nullptr);
+ ASSERT_EQ(FlowNodeForAstNode(ast_if), nullptr);
- auto* ir_switch = b.FlowNodeForAstNode(ast_switch);
+ auto* ir_switch = FlowNodeForAstNode(ast_switch);
ASSERT_NE(ir_switch, nullptr);
ASSERT_TRUE(ir_switch->Is<ir::Switch>());
auto* flow = ir_switch->As<ir::Switch>();
- ASSERT_NE(flow->merge_target, nullptr);
+ ASSERT_NE(flow->merge.target, nullptr);
ASSERT_EQ(2u, flow->cases.Length());
ASSERT_EQ(1u, m.functions.Length());
auto* func = m.functions[0];
ASSERT_EQ(1u, flow->cases[0].selectors.Length());
- ASSERT_TRUE(flow->cases[0].selectors[0]->expr->Is<ast::IntLiteralExpression>());
- EXPECT_EQ(0_i, flow->cases[0].selectors[0]->expr->As<ast::IntLiteralExpression>()->value);
+ ASSERT_TRUE(flow->cases[0].selectors[0].val->value->Is<constant::Scalar<tint::i32>>());
+ EXPECT_EQ(0_i,
+ flow->cases[0].selectors[0].val->value->As<constant::Scalar<tint::i32>>()->ValueOf());
ASSERT_EQ(1u, flow->cases[1].selectors.Length());
- EXPECT_TRUE(flow->cases[1].selectors[0]->IsDefault());
+ EXPECT_TRUE(flow->cases[1].selectors[0].IsDefault());
EXPECT_EQ(1u, flow->inbound_branches.Length());
- EXPECT_EQ(1u, flow->cases[0].start_target->inbound_branches.Length());
- EXPECT_EQ(1u, flow->cases[1].start_target->inbound_branches.Length());
- EXPECT_EQ(0u, flow->merge_target->inbound_branches.Length());
+ EXPECT_EQ(1u, flow->cases[0].start.target->inbound_branches.Length());
+ EXPECT_EQ(1u, flow->cases[1].start.target->inbound_branches.Length());
+ EXPECT_EQ(0u, flow->merge.target->inbound_branches.Length());
EXPECT_EQ(2u, func->end_target->inbound_branches.Length());
- EXPECT_EQ(func->start_target->branch_target, ir_switch);
- EXPECT_EQ(flow->cases[0].start_target->branch_target, func->end_target);
- EXPECT_EQ(flow->cases[1].start_target->branch_target, func->end_target);
- EXPECT_EQ(flow->merge_target->branch_target, nullptr);
+ EXPECT_EQ(Disassemble(m), R"(%bb0 = Function test_function
+ %bb1 = Block
+ BranchTo %bb2 ()
+
+ %bb2 = Switch (1)
+ # Case 0
+ %bb3 = Block
+ Return ()
+ # Case default
+ %bb4 = Block
+ Return ()
+ # Switch Merge
+ # Dead
+FunctionEnd
+
+)");
}
TEST_F(IR_BuilderImplTest, EmitLiteral_Bool_True) {
@@ -1436,7 +1561,7 @@
ASSERT_TRUE(r) << b.error();
Disassembler d(b.builder.ir);
- d.EmitBlockInstructions(b.current_flow_block);
+ d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1 (u32) = 3 + 4
)");
}
@@ -1451,7 +1576,7 @@
ASSERT_TRUE(r) << b.error();
Disassembler d(b.builder.ir);
- d.EmitBlockInstructions(b.current_flow_block);
+ d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1 (u32) = 3 - 4
)");
}
@@ -1466,7 +1591,7 @@
ASSERT_TRUE(r) << b.error();
Disassembler d(b.builder.ir);
- d.EmitBlockInstructions(b.current_flow_block);
+ d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1 (u32) = 3 * 4
)");
}
@@ -1481,7 +1606,7 @@
ASSERT_TRUE(r) << b.error();
Disassembler d(b.builder.ir);
- d.EmitBlockInstructions(b.current_flow_block);
+ d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1 (u32) = 3 / 4
)");
}
@@ -1496,7 +1621,7 @@
ASSERT_TRUE(r) << b.error();
Disassembler d(b.builder.ir);
- d.EmitBlockInstructions(b.current_flow_block);
+ d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1 (u32) = 3 % 4
)");
}
@@ -1511,7 +1636,7 @@
ASSERT_TRUE(r) << b.error();
Disassembler d(b.builder.ir);
- d.EmitBlockInstructions(b.current_flow_block);
+ d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1 (u32) = 3 & 4
)");
}
@@ -1526,7 +1651,7 @@
ASSERT_TRUE(r) << b.error();
Disassembler d(b.builder.ir);
- d.EmitBlockInstructions(b.current_flow_block);
+ d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1 (u32) = 3 | 4
)");
}
@@ -1541,7 +1666,7 @@
ASSERT_TRUE(r) << b.error();
Disassembler d(b.builder.ir);
- d.EmitBlockInstructions(b.current_flow_block);
+ d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1 (u32) = 3 ^ 4
)");
}
@@ -1556,7 +1681,7 @@
ASSERT_TRUE(r) << b.error();
Disassembler d(b.builder.ir);
- d.EmitBlockInstructions(b.current_flow_block);
+ d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1 (bool) = true && false
)");
}
@@ -1571,7 +1696,7 @@
ASSERT_TRUE(r) << b.error();
Disassembler d(b.builder.ir);
- d.EmitBlockInstructions(b.current_flow_block);
+ d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1 (bool) = false || true
)");
}
@@ -1586,7 +1711,7 @@
ASSERT_TRUE(r) << b.error();
Disassembler d(b.builder.ir);
- d.EmitBlockInstructions(b.current_flow_block);
+ d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1 (bool) = 3 == 4
)");
}
@@ -1601,7 +1726,7 @@
ASSERT_TRUE(r) << b.error();
Disassembler d(b.builder.ir);
- d.EmitBlockInstructions(b.current_flow_block);
+ d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1 (bool) = 3 != 4
)");
}
@@ -1616,7 +1741,7 @@
ASSERT_TRUE(r) << b.error();
Disassembler d(b.builder.ir);
- d.EmitBlockInstructions(b.current_flow_block);
+ d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1 (bool) = 3 < 4
)");
}
@@ -1631,7 +1756,7 @@
ASSERT_TRUE(r) << b.error();
Disassembler d(b.builder.ir);
- d.EmitBlockInstructions(b.current_flow_block);
+ d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1 (bool) = 3 > 4
)");
}
@@ -1646,7 +1771,7 @@
ASSERT_TRUE(r) << b.error();
Disassembler d(b.builder.ir);
- d.EmitBlockInstructions(b.current_flow_block);
+ d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1 (bool) = 3 <= 4
)");
}
@@ -1661,7 +1786,7 @@
ASSERT_TRUE(r) << b.error();
Disassembler d(b.builder.ir);
- d.EmitBlockInstructions(b.current_flow_block);
+ d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1 (bool) = 3 >= 4
)");
}
@@ -1676,7 +1801,7 @@
ASSERT_TRUE(r) << b.error();
Disassembler d(b.builder.ir);
- d.EmitBlockInstructions(b.current_flow_block);
+ d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1 (u32) = 3 << 4
)");
}
@@ -1691,7 +1816,7 @@
ASSERT_TRUE(r) << b.error();
Disassembler d(b.builder.ir);
- d.EmitBlockInstructions(b.current_flow_block);
+ d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1 (u32) = 3 >> 4
)");
}
@@ -1707,7 +1832,7 @@
ASSERT_TRUE(r) << b.error();
Disassembler d(b.builder.ir);
- d.EmitBlockInstructions(b.current_flow_block);
+ d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1 (u32) = 3 >> 4
%2 (u32) = %1 (u32) + 9
%3 (bool) = 1 < %2 (u32)
@@ -1728,9 +1853,10 @@
ASSERT_TRUE(r) << b.error();
Disassembler d(b.builder.ir);
- d.EmitBlockInstructions(b.current_flow_block);
+ d.EmitBlockInstructions(b.current_flow_block->As<ir::Block>());
EXPECT_EQ(d.AsString(), R"(%1 (f32) = bitcast(3)
)");
}
+
} // namespace
} // namespace tint::ir
diff --git a/src/tint/ir/constant_test.cc b/src/tint/ir/constant_test.cc
index 87eec65..c6fe2e4 100644
--- a/src/tint/ir/constant_test.cc
+++ b/src/tint/ir/constant_test.cc
@@ -32,7 +32,7 @@
auto* c = b.builder.Constant(1.2_f);
EXPECT_EQ(1.2_f, c->value->As<constant::Scalar<f32>>()->ValueAs<f32>());
- c->ToString(str, program->Symbols());
+ c->ToString(str, b.builder.ir.symbols);
EXPECT_EQ("1.2", str.str());
EXPECT_TRUE(c->value->Is<constant::Scalar<f32>>());
@@ -50,7 +50,7 @@
auto* c = b.builder.Constant(1.1_h);
EXPECT_EQ(1.1_h, c->value->As<constant::Scalar<f16>>()->ValueAs<f16>());
- c->ToString(str, program->Symbols());
+ c->ToString(str, b.builder.ir.symbols);
EXPECT_EQ("1.09961", str.str());
EXPECT_FALSE(c->value->Is<constant::Scalar<f32>>());
@@ -68,7 +68,7 @@
auto* c = b.builder.Constant(1_i);
EXPECT_EQ(1_i, c->value->As<constant::Scalar<i32>>()->ValueAs<i32>());
- c->ToString(str, program->Symbols());
+ c->ToString(str, b.builder.ir.symbols);
EXPECT_EQ("1", str.str());
EXPECT_FALSE(c->value->Is<constant::Scalar<f32>>());
@@ -86,7 +86,7 @@
auto* c = b.builder.Constant(2_u);
EXPECT_EQ(2_u, c->value->As<constant::Scalar<u32>>()->ValueAs<u32>());
- c->ToString(str, program->Symbols());
+ c->ToString(str, b.builder.ir.symbols);
EXPECT_EQ("2", str.str());
EXPECT_FALSE(c->value->Is<constant::Scalar<f32>>());
@@ -104,14 +104,14 @@
auto* c = b.builder.Constant(false);
EXPECT_FALSE(c->value->As<constant::Scalar<bool>>()->ValueAs<bool>());
- c->ToString(str, program->Symbols());
+ c->ToString(str, b.builder.ir.symbols);
EXPECT_EQ("false", str.str());
str.str("");
c = b.builder.Constant(true);
EXPECT_TRUE(c->value->As<constant::Scalar<bool>>()->ValueAs<bool>());
- c->ToString(str, program->Symbols());
+ c->ToString(str, b.builder.ir.symbols);
EXPECT_EQ("true", str.str());
EXPECT_FALSE(c->value->Is<constant::Scalar<f32>>());
diff --git a/src/tint/ir/debug.cc b/src/tint/ir/debug.cc
index be83111..617b207 100644
--- a/src/tint/ir/debug.cc
+++ b/src/tint/ir/debug.cc
@@ -23,7 +23,6 @@
#include "src/tint/ir/loop.h"
#include "src/tint/ir/switch.h"
#include "src/tint/ir/terminator.h"
-#include "src/tint/program.h"
namespace tint::ir {
@@ -60,24 +59,24 @@
if (node_to_name.count(b) == 0) {
out << name_for(b) << R"( [label="block"])" << std::endl;
}
- out << name_for(b) << " -> " << name_for(b->branch_target);
+ out << name_for(b) << " -> " << name_for(b->branch.target);
// Dashed lines to merge blocks
- if (merge_nodes.count(b->branch_target) != 0) {
+ if (merge_nodes.count(b->branch.target) != 0) {
out << " [style=dashed]";
}
out << std::endl;
- Graph(b->branch_target);
+ Graph(b->branch.target);
},
[&](const ir::Switch* s) {
out << name_for(s) << R"( [label="switch"])" << std::endl;
- out << name_for(s->merge_target) << R"( [label="switch merge"])" << std::endl;
- merge_nodes.insert(s->merge_target);
+ out << name_for(s->merge.target) << R"( [label="switch merge"])" << std::endl;
+ merge_nodes.insert(s->merge.target);
size_t i = 0;
for (const auto& c : s->cases) {
- out << name_for(c.start_target)
+ out << name_for(c.start.target)
<< R"( [label="case )" + std::to_string(i++) + R"("])" << std::endl;
}
out << name_for(s) << " -> {";
@@ -85,56 +84,56 @@
if (&c != &(s->cases[0])) {
out << ", ";
}
- out << name_for(c.start_target);
+ out << name_for(c.start.target);
}
out << "}" << std::endl;
for (const auto& c : s->cases) {
- Graph(c.start_target);
+ Graph(c.start.target);
}
- Graph(s->merge_target);
+ Graph(s->merge.target);
},
[&](const ir::If* i) {
out << name_for(i) << R"( [label="if"])" << std::endl;
- out << name_for(i->true_target) << R"( [label="true"])" << std::endl;
- out << name_for(i->false_target) << R"( [label="false"])" << std::endl;
- out << name_for(i->merge_target) << R"( [label="if merge"])" << std::endl;
- merge_nodes.insert(i->merge_target);
+ out << name_for(i->true_.target) << R"( [label="true"])" << std::endl;
+ out << name_for(i->false_.target) << R"( [label="false"])" << std::endl;
+ out << name_for(i->merge.target) << R"( [label="if merge"])" << std::endl;
+ merge_nodes.insert(i->merge.target);
out << name_for(i) << " -> {";
- out << name_for(i->true_target) << ", " << name_for(i->false_target);
+ out << name_for(i->true_.target) << ", " << name_for(i->false_.target);
out << "}" << std::endl;
// Subgraph if true/false branches so they draw on the same line
out << "subgraph sub_" << name_for(i) << " {" << std::endl;
out << R"(rank="same")" << std::endl;
- out << name_for(i->true_target) << std::endl;
- out << name_for(i->false_target) << std::endl;
+ out << name_for(i->true_.target) << std::endl;
+ out << name_for(i->false_.target) << std::endl;
out << "}" << std::endl;
- Graph(i->true_target);
- Graph(i->false_target);
- Graph(i->merge_target);
+ Graph(i->true_.target);
+ Graph(i->false_.target);
+ Graph(i->merge.target);
},
[&](const ir::Loop* l) {
out << name_for(l) << R"( [label="loop"])" << std::endl;
- out << name_for(l->start_target) << R"( [label="start"])" << std::endl;
- out << name_for(l->continuing_target) << R"( [label="continuing"])" << std::endl;
- out << name_for(l->merge_target) << R"( [label="loop merge"])" << std::endl;
- merge_nodes.insert(l->merge_target);
+ out << name_for(l->start.target) << R"( [label="start"])" << std::endl;
+ out << name_for(l->continuing.target) << R"( [label="continuing"])" << std::endl;
+ out << name_for(l->merge.target) << R"( [label="loop merge"])" << std::endl;
+ merge_nodes.insert(l->merge.target);
// Subgraph the continuing and merge so they get drawn on the same line
out << "subgraph sub_" << name_for(l) << " {" << std::endl;
out << R"(rank="same")" << std::endl;
- out << name_for(l->continuing_target) << std::endl;
- out << name_for(l->merge_target) << std::endl;
+ out << name_for(l->continuing.target) << std::endl;
+ out << name_for(l->merge.target) << std::endl;
out << "}" << std::endl;
- out << name_for(l) << " -> " << name_for(l->start_target) << std::endl;
+ out << name_for(l) << " -> " << name_for(l->start.target) << std::endl;
- Graph(l->start_target);
- Graph(l->continuing_target);
- Graph(l->merge_target);
+ Graph(l->start.target);
+ Graph(l->continuing.target);
+ Graph(l->merge.target);
},
[&](const ir::Terminator*) {
// Already done
@@ -145,8 +144,7 @@
for (const auto* func : mod->functions) {
// Cluster each function to label and draw a box around it.
out << "subgraph cluster_" << name_for(func) << " {" << std::endl;
- out << R"(label=")" << mod->program->Symbols().NameFor(func->source->symbol) << R"(")"
- << std::endl;
+ out << R"(label=")" << mod->symbols.NameFor(func->name) << R"(")" << std::endl;
out << name_for(func->start_target) << R"( [label="start"])" << std::endl;
out << name_for(func->end_target) << R"( [label="end"])" << std::endl;
Graph(func->start_target);
diff --git a/src/tint/ir/disassembler.cc b/src/tint/ir/disassembler.cc
index 37f294c..4665c80 100644
--- a/src/tint/ir/disassembler.cc
+++ b/src/tint/ir/disassembler.cc
@@ -19,7 +19,6 @@
#include "src/tint/ir/loop.h"
#include "src/tint/ir/switch.h"
#include "src/tint/ir/terminator.h"
-#include "src/tint/program.h"
namespace tint::ir {
namespace {
@@ -63,10 +62,23 @@
void Disassembler::EmitBlockInstructions(const Block* b) {
for (const auto* instr : b->instructions) {
- instr->ToString(out_, mod_.program->Symbols()) << std::endl;
+ Indent();
+ instr->ToString(out_, mod_.symbols) << std::endl;
}
}
+size_t Disassembler::GetIdForNode(const FlowNode* node) {
+ TINT_ASSERT(IR, node);
+
+ auto it = flow_node_to_id_.find(node);
+ if (it != flow_node_to_id_.end()) {
+ return it->second;
+ }
+ size_t id = next_node_id_++;
+ flow_node_to_id_[node] = id;
+ return id;
+}
+
void Disassembler::Walk(const FlowNode* node) {
if ((visited_.count(node) > 0) || (stop_nodes_.count(node) > 0)) {
return;
@@ -76,7 +88,8 @@
tint::Switch(
node,
[&](const ir::Function* f) {
- Indent() << "Function" << std::endl;
+ Indent() << "%bb" << GetIdForNode(f) << " = Function " << mod_.symbols.NameFor(f->name)
+ << std::endl;
{
ScopedIndent func_indent(&indent_size_);
@@ -86,62 +99,106 @@
Walk(f->end_target);
},
[&](const ir::Block* b) {
- Indent() << "Block" << std::endl;
+ // If this block is dead, nothing to do
+ if (b->IsDead()) {
+ Indent() << "# Dead" << std::endl;
+ return;
+ }
+
+ Indent() << "%bb" << GetIdForNode(b) << " = Block" << std::endl;
EmitBlockInstructions(b);
- Walk(b->branch_target);
+
+ if (b->branch.target->Is<Terminator>()) {
+ Indent() << "Return";
+ } else {
+ Indent() << "BranchTo "
+ << "%bb" << GetIdForNode(b->branch.target);
+ }
+ out_ << " (";
+ for (const auto* v : b->branch.args) {
+ if (v != b->branch.args.Front()) {
+ out_ << ", ";
+ }
+ v->ToString(out_, mod_.symbols);
+ }
+ out_ << ")" << std::endl;
+
+ if (!b->branch.target->Is<Terminator>()) {
+ out_ << std::endl;
+ }
+
+ Walk(b->branch.target);
},
[&](const ir::Switch* s) {
- Indent() << "Switch (" << s->condition << ")" << std::endl;
+ Indent() << "%bb" << GetIdForNode(s) << " = Switch (";
+ s->condition->ToString(out_, mod_.symbols);
+ out_ << ")" << std::endl;
{
ScopedIndent switch_indent(&indent_size_);
- ScopedStopNode scope(&stop_nodes_, s->merge_target);
+ ScopedStopNode scope(&stop_nodes_, s->merge.target);
for (const auto& c : s->cases) {
- Indent() << "Case" << std::endl;
- ScopedIndent case_indent(&indent_size_);
- Walk(c.start_target);
+ Indent() << "# Case ";
+ for (const auto& selector : c.selectors) {
+ if (&selector != &c.selectors.Front()) {
+ out_ << " ";
+ }
+
+ if (selector.IsDefault()) {
+ out_ << "default";
+ } else {
+ selector.val->ToString(out_, mod_.symbols);
+ }
+ }
+ out_ << std::endl;
+ Walk(c.start.target);
}
}
- Indent() << "Switch Merge" << std::endl;
- Walk(s->merge_target);
+ Indent() << "# Switch Merge" << std::endl;
+ Walk(s->merge.target);
},
[&](const ir::If* i) {
- Indent() << "if (" << i->condition << ")" << std::endl;
+ Indent() << "%bb" << GetIdForNode(i) << " = if (";
+ i->condition->ToString(out_, mod_.symbols);
+ out_ << ")" << std::endl;
+
{
ScopedIndent if_indent(&indent_size_);
- ScopedStopNode scope(&stop_nodes_, i->merge_target);
+ ScopedStopNode scope(&stop_nodes_, i->merge.target);
- Indent() << "true branch" << std::endl;
- Walk(i->true_target);
+ Indent() << "# true branch" << std::endl;
+ Walk(i->true_.target);
- Indent() << "false branch" << std::endl;
- Walk(i->false_target);
+ Indent() << "# false branch" << std::endl;
+ Walk(i->false_.target);
}
- Indent() << "if merge" << std::endl;
- Walk(i->merge_target);
+ if (!i->merge.target->IsDisconnected()) {
+ Indent() << "# if merge" << std::endl;
+ Walk(i->merge.target);
+ }
},
[&](const ir::Loop* l) {
- Indent() << "loop" << std::endl;
+ Indent() << "%bb" << GetIdForNode(l) << " = loop" << std::endl;
{
- ScopedStopNode loop_scope(&stop_nodes_, l->merge_target);
+ ScopedStopNode loop_scope(&stop_nodes_, l->merge.target);
ScopedIndent loop_indent(&indent_size_);
{
- ScopedStopNode inner_scope(&stop_nodes_, l->continuing_target);
- Indent() << "loop start" << std::endl;
- Walk(l->start_target);
+ ScopedStopNode inner_scope(&stop_nodes_, l->continuing.target);
+ Indent() << "# loop start" << std::endl;
+ Walk(l->start.target);
}
- Indent() << "loop continuing" << std::endl;
- ScopedIndent continuing_indent(&indent_size_);
- Walk(l->continuing_target);
+ Indent() << "# loop continuing" << std::endl;
+ Walk(l->continuing.target);
}
- Indent() << "loop merge" << std::endl;
- Walk(l->merge_target);
+ Indent() << "# loop merge" << std::endl;
+ Walk(l->merge.target);
},
- [&](const ir::Terminator*) { Indent() << "Function end" << std::endl; });
+ [&](const ir::Terminator*) { Indent() << "FunctionEnd" << std::endl
+ << std::endl; });
}
std::string Disassembler::Disassemble() {
diff --git a/src/tint/ir/disassembler.h b/src/tint/ir/disassembler.h
index 46b4ffd..03cbea1 100644
--- a/src/tint/ir/disassembler.h
+++ b/src/tint/ir/disassembler.h
@@ -17,6 +17,7 @@
#include <sstream>
#include <string>
+#include <unordered_map>
#include <unordered_set>
#include "src/tint/ir/flow_node.h"
@@ -46,11 +47,14 @@
private:
std::ostream& Indent();
void Walk(const FlowNode* node);
+ size_t GetIdForNode(const FlowNode* node);
const Module& mod_;
std::stringstream out_;
std::unordered_set<const FlowNode*> visited_;
std::unordered_set<const FlowNode*> stop_nodes_;
+ std::unordered_map<const FlowNode*, size_t> flow_node_to_id_;
+ size_t next_node_id_ = 0;
uint32_t indent_size_ = 0;
};
diff --git a/src/tint/ir/flow_node.h b/src/tint/ir/flow_node.h
index 2b5ec39..019a8a0 100644
--- a/src/tint/ir/flow_node.h
+++ b/src/tint/ir/flow_node.h
@@ -32,6 +32,9 @@
/// - Node is a continue target outside control flow (loop that returns)
utils::Vector<FlowNode*, 2> inbound_branches;
+ /// @returns true if this node has no inbound branches
+ bool IsDisconnected() const { return inbound_branches.IsEmpty(); }
+
protected:
/// Constructor
FlowNode();
diff --git a/src/tint/ir/function.cc b/src/tint/ir/function.cc
index f74550e..167664a 100644
--- a/src/tint/ir/function.cc
+++ b/src/tint/ir/function.cc
@@ -18,7 +18,7 @@
namespace tint::ir {
-Function::Function(const ast::Function* f) : Base(), source(f) {}
+Function::Function() : Base() {}
Function::~Function() = default;
diff --git a/src/tint/ir/function.h b/src/tint/ir/function.h
index c63aec0..c6032ba 100644
--- a/src/tint/ir/function.h
+++ b/src/tint/ir/function.h
@@ -15,8 +15,8 @@
#ifndef SRC_TINT_IR_FUNCTION_H_
#define SRC_TINT_IR_FUNCTION_H_
-#include "src/tint/ast/function.h"
#include "src/tint/ir/flow_node.h"
+#include "src/tint/symbol.h"
// Forward declarations
namespace tint::ir {
@@ -30,12 +30,11 @@
class Function : public Castable<Function, FlowNode> {
public:
/// Constructor
- /// @param func the ast::Function to create from
- explicit Function(const ast::Function* func);
+ Function();
~Function() override;
- /// The ast function this ir function is created from.
- const ast::Function* source;
+ /// The function name
+ Symbol name;
/// The start target is the first block in a function.
Block* start_target = nullptr;
diff --git a/src/tint/ir/if.cc b/src/tint/ir/if.cc
index df017d2..22a9078 100644
--- a/src/tint/ir/if.cc
+++ b/src/tint/ir/if.cc
@@ -18,7 +18,7 @@
namespace tint::ir {
-If::If(const ast::Statement* stmt) : Base(), source(stmt) {}
+If::If() : Base() {}
If::~If() = default;
diff --git a/src/tint/ir/if.h b/src/tint/ir/if.h
index 905f311..823700e 100644
--- a/src/tint/ir/if.h
+++ b/src/tint/ir/if.h
@@ -15,7 +15,7 @@
#ifndef SRC_TINT_IR_IF_H_
#define SRC_TINT_IR_IF_H_
-#include "src/tint/ast/if_statement.h"
+#include "src/tint/ir/branch.h"
#include "src/tint/ir/flow_node.h"
#include "src/tint/ir/value.h"
@@ -30,20 +30,16 @@
class If : public Castable<If, FlowNode> {
public:
/// Constructor
- /// @param stmt the ast::IfStatement or ast::BreakIfStatement
- explicit If(const ast::Statement* stmt);
+ If();
~If() override;
- /// The ast::IfStatement or ast::BreakIfStatement source for this flow node.
- const ast::Statement* source;
-
/// The true branch block
- Block* true_target = nullptr;
+ Branch true_ = {};
/// The false branch block
- Block* false_target = nullptr;
- /// An block to reconvert the true/false barnches. The block always exists, but there maybe no
+ Branch false_ = {};
+ /// An block to converge the true/false branches. The block always exists, but there maybe no
/// branches into it. (e.g. if both branches `return`)
- Block* merge_target = nullptr;
+ Branch merge = {};
/// Value holding the condition result
const Value* condition = nullptr;
};
diff --git a/src/tint/ir/loop.cc b/src/tint/ir/loop.cc
index 46ac5a1..9a1af45 100644
--- a/src/tint/ir/loop.cc
+++ b/src/tint/ir/loop.cc
@@ -14,17 +14,11 @@
#include "src/tint/ir/loop.h"
-#include "src/tint/ast/for_loop_statement.h"
-#include "src/tint/ast/loop_statement.h"
-#include "src/tint/ast/while_statement.h"
-
TINT_INSTANTIATE_TYPEINFO(tint::ir::Loop);
namespace tint::ir {
-Loop::Loop(const ast::Statement* s) : Base(), source(s) {
- TINT_ASSERT(IR, (s->IsAnyOf<ast::LoopStatement, ast::WhileStatement, ast::ForLoopStatement>()));
-}
+Loop::Loop() : Base() {}
Loop::~Loop() = default;
diff --git a/src/tint/ir/loop.h b/src/tint/ir/loop.h
index 52d24fb..2b026e6 100644
--- a/src/tint/ir/loop.h
+++ b/src/tint/ir/loop.h
@@ -15,8 +15,8 @@
#ifndef SRC_TINT_IR_LOOP_H_
#define SRC_TINT_IR_LOOP_H_
-#include "src/tint/ast/statement.h"
#include "src/tint/ir/block.h"
+#include "src/tint/ir/branch.h"
#include "src/tint/ir/flow_node.h"
namespace tint::ir {
@@ -25,21 +25,17 @@
class Loop : public Castable<Loop, FlowNode> {
public:
/// Constructor
- /// @param stmt the loop, while or for statement.
- explicit Loop(const ast::Statement* stmt);
+ Loop();
~Loop() override;
- /// The ast loop, while or for statement this ir loop is created from.
- const ast::Statement* source;
-
/// The start block is the first block in a loop.
- Block* start_target = nullptr;
+ Branch start = {};
/// The continue target of the block.
- Block* continuing_target = nullptr;
+ Branch continuing = {};
/// The loop merge target. If the `loop` does a `return` then this block may not actually
/// end up in the control flow. We need it if the loop does a `break` we know where to break
/// too.
- Block* merge_target = nullptr;
+ Branch merge = {};
};
} // namespace tint::ir
diff --git a/src/tint/ir/module.cc b/src/tint/ir/module.cc
index 0f9be09..bf05e08 100644
--- a/src/tint/ir/module.cc
+++ b/src/tint/ir/module.cc
@@ -34,7 +34,7 @@
return Result{r.Move()};
}
-Module::Module(const Program* prog) : program(prog) {}
+Module::Module() = default;
Module::Module(Module&&) = default;
diff --git a/src/tint/ir/module.h b/src/tint/ir/module.h
index 5e7a9dd..5cfc883 100644
--- a/src/tint/ir/module.h
+++ b/src/tint/ir/module.h
@@ -52,8 +52,7 @@
static Result FromProgram(const Program* program);
/// Constructor
- /// @param program the program this module was constructed from
- explicit Module(const Program* program);
+ Module();
/// Move constructor
/// @param o the module to move from
Module(Module&& o);
@@ -89,9 +88,6 @@
/// List of indexes into the functions list for the entry points
utils::Vector<Function*, 8> entry_points;
- /// The source ast::Program this module was constucted from
- const Program* program;
-
/// The type manager for the module
type::Manager types;
diff --git a/src/tint/ir/switch.cc b/src/tint/ir/switch.cc
index 23b7fbb..9ad6d30 100644
--- a/src/tint/ir/switch.cc
+++ b/src/tint/ir/switch.cc
@@ -18,7 +18,7 @@
namespace tint::ir {
-Switch::Switch(const ast::SwitchStatement* stmt) : Base(), source(stmt) {}
+Switch::Switch() : Base() {}
Switch::~Switch() = default;
diff --git a/src/tint/ir/switch.h b/src/tint/ir/switch.h
index 7fff0a0..6ba1d4c 100644
--- a/src/tint/ir/switch.h
+++ b/src/tint/ir/switch.h
@@ -16,38 +16,39 @@
#define SRC_TINT_IR_SWITCH_H_
#include "src/tint/ir/block.h"
+#include "src/tint/ir/branch.h"
+#include "src/tint/ir/constant.h"
#include "src/tint/ir/flow_node.h"
#include "src/tint/ir/value.h"
-// Forward declarations
-namespace tint::ast {
-class CaseSelector;
-class SwitchStatement;
-} // namespace tint::ast
-
namespace tint::ir {
/// Flow node representing a switch statement
class Switch : public Castable<Switch, FlowNode> {
public:
+ /// A case selector
+ struct CaseSelector {
+ /// @returns true if this is a default selector
+ bool IsDefault() const { return val == nullptr; }
+
+ /// The selector value, or nullptr if this is the default selector
+ Constant* val = nullptr;
+ };
+
/// A case label in the struct
struct Case {
/// The case selector for this node
- utils::Vector<const ast::CaseSelector*, 4> selectors;
+ utils::Vector<CaseSelector, 4> selectors;
/// The start block for the case block.
- Block* start_target;
+ Branch start = {};
};
/// Constructor
- /// @param stmt the originating ast switch statement
- explicit Switch(const ast::SwitchStatement* stmt);
+ Switch();
~Switch() override;
- /// The originating switch statment in the AST
- const ast::SwitchStatement* source;
-
/// The switch merge target
- Block* merge_target;
+ Branch merge = {};
/// The switch case statements
utils::Vector<Case, 4> cases;
diff --git a/src/tint/ir/temp_test.cc b/src/tint/ir/temp_test.cc
index 81a4992..ddb0124 100644
--- a/src/tint/ir/temp_test.cc
+++ b/src/tint/ir/temp_test.cc
@@ -33,7 +33,7 @@
auto* val = b.builder.Temp(b.builder.ir.types.Get<type::I32>());
EXPECT_EQ(4u, val->AsId());
- val->ToString(str, program->Symbols());
+ val->ToString(str, b.builder.ir.symbols);
EXPECT_EQ("%4 (i32)", str.str());
}
diff --git a/src/tint/ir/test_helper.h b/src/tint/ir/test_helper.h
index e3d66cf..9fb54cc 100644
--- a/src/tint/ir/test_helper.h
+++ b/src/tint/ir/test_helper.h
@@ -16,6 +16,7 @@
#define SRC_TINT_IR_TEST_HELPER_H_
#include <memory>
+#include <string>
#include <utility>
#include "gtest/gtest.h"
@@ -44,9 +45,9 @@
}
diag::Formatter formatter;
- program = std::make_unique<Program>(std::move(*this));
- [&]() { ASSERT_TRUE(program->IsValid()) << formatter.format(program->Diagnostics()); }();
- gen_ = std::make_unique<BuilderImpl>(program.get());
+ program_ = std::make_unique<Program>(std::move(*this));
+ [&]() { ASSERT_TRUE(program_->IsValid()) << formatter.format(program_->Diagnostics()); }();
+ gen_ = std::make_unique<BuilderImpl>(program_.get());
return *gen_;
}
@@ -63,17 +64,52 @@
/// is initialized with an empty block.
/// @returns the BuilderImpl for testing.
BuilderImpl& CreateEmptyBuilder() {
- program = std::make_unique<Program>();
- gen_ = std::make_unique<BuilderImpl>(program.get());
+ program_ = std::make_unique<Program>();
+ gen_ = std::make_unique<BuilderImpl>(program_.get());
gen_->current_flow_block = gen_->builder.CreateBlock();
return *gen_;
}
- /// The program built with a call to Build()
- std::unique_ptr<Program> program;
+ /// Build the module, cleaning up the program before returning.
+ /// @returns the generated module
+ utils::Result<Module> Build() {
+ auto& b = CreateBuilder();
+ auto m = b.Build();
+
+ // Store the error away in case we need it
+ error_ = b.error();
+
+ // Explicitly remove program to guard against pointers back to ast. Note, this does mean the
+ // BuilderImpl is pointing to an invalid program. We keep the BuilderImpl around because we
+ // need to be able to map from ast pointers to flow nodes in tests.
+ program_ = nullptr;
+ return m;
+ }
+
+ /// @param node the ast node to lookup
+ /// @returns the IR flow node for the given ast node.
+ const ir::FlowNode* FlowNodeForAstNode(const ast::Node* node) const {
+ return gen_->FlowNodeForAstNode(node);
+ }
+
+ /// @param mod the module
+ /// @returns the disassembly string of the module
+ std::string Disassemble(Module& mod) const {
+ Disassembler d(mod);
+ return d.Disassemble();
+ }
+
+ /// @returns the error generated during build, if any
+ std::string Error() const { return error_; }
private:
std::unique_ptr<BuilderImpl> gen_;
+
+ /// The program built with a call to Build()
+ std::unique_ptr<Program> program_;
+
+ /// Error generated when calling `Build`
+ std::string error_;
};
using TestHelper = TestHelperBase<testing::Test>;
diff --git a/src/tint/transform/texture_1d_to_2d.cc b/src/tint/transform/texture_1d_to_2d.cc
new file mode 100644
index 0000000..0e19d02
--- /dev/null
+++ b/src/tint/transform/texture_1d_to_2d.cc
@@ -0,0 +1,186 @@
+// Copyright 2022 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/tint/transform/texture_1d_to_2d.h"
+
+#include <utility>
+
+#include "src/tint/program_builder.h"
+#include "src/tint/sem/function.h"
+#include "src/tint/sem/statement.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::transform::Texture1DTo2D);
+
+using namespace tint::number_suffixes; // NOLINT
+
+namespace tint::transform {
+
+namespace {
+
+bool ShouldRun(const Program* program) {
+ for (auto* fn : program->AST().Functions()) {
+ if (auto* sem_fn = program->Sem().Get(fn)) {
+ for (auto* builtin : sem_fn->DirectlyCalledBuiltins()) {
+ const auto& signature = builtin->Signature();
+ auto texture = signature.Parameter(sem::ParameterUsage::kTexture);
+ if (texture) {
+ auto* tex = texture->Type()->As<type::Texture>();
+ if (tex->dim() == ast::TextureDimension::k1d) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ for (auto* var : program->AST().GlobalVariables()) {
+ if (Switch(
+ program->Sem().Get(var->type),
+ [&](const type::SampledTexture* tex) {
+ return tex->dim() == ast::TextureDimension::k1d;
+ },
+ [&](const type::StorageTexture* storage_tex) {
+ return storage_tex->dim() == ast::TextureDimension::k1d;
+ })) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace
+
+/// PIMPL state for the transform
+struct Texture1DTo2D::State {
+ /// The source program
+ const Program* const src;
+ /// The target program builder
+ ProgramBuilder b;
+ /// The clone context
+ CloneContext ctx = {&b, src, /* auto_clone_symbols */ true};
+
+ /// Constructor
+ /// @param program the source program
+ explicit State(const Program* program) : src(program) {}
+
+ /// Runs the transform
+ /// @returns the new program or SkipTransform if the transform is not required
+ ApplyResult Run() {
+ auto& sem = src->Sem();
+
+ if (!ShouldRun(ctx.src)) {
+ return SkipTransform;
+ }
+
+ auto create_var = [&](const ast::Variable* v, ast::Type* type) -> const ast::Variable* {
+ if (v->As<ast::Parameter>()) {
+ return ctx.dst->Param(ctx.Clone(v->symbol), type, ctx.Clone(v->attributes));
+ } else {
+ return ctx.dst->Var(ctx.Clone(v->symbol), type, ctx.Clone(v->attributes));
+ }
+ };
+
+ ctx.ReplaceAll([&](const ast::Variable* v) -> const ast::Variable* {
+ const ast::Variable* r = Switch(
+ sem.Get(v->type),
+ [&](const type::SampledTexture* tex) -> const ast::Variable* {
+ if (tex->dim() == ast::TextureDimension::k1d) {
+ auto* type = ctx.dst->create<ast::SampledTexture>(
+ ast::TextureDimension::k2d, CreateASTTypeFor(ctx, tex->type()));
+ return create_var(v, type);
+ } else {
+ return nullptr;
+ }
+ },
+ [&](const type::StorageTexture* storage_tex) -> const ast::Variable* {
+ if (storage_tex->dim() == ast::TextureDimension::k1d) {
+ auto* type = ctx.dst->create<ast::StorageTexture>(
+ ast::TextureDimension::k2d, storage_tex->texel_format(),
+ CreateASTTypeFor(ctx, storage_tex->type()), storage_tex->access());
+ return create_var(v, type);
+ } else {
+ return nullptr;
+ }
+ },
+ [](Default) { return nullptr; });
+ return r;
+ });
+
+ ctx.ReplaceAll([&](const ast::CallExpression* c) -> const ast::Expression* {
+ auto* call = sem.Get(c)->UnwrapMaterialize()->As<sem::Call>();
+ if (!call) {
+ return nullptr;
+ }
+ auto* builtin = call->Target()->As<sem::Builtin>();
+ if (!builtin) {
+ return nullptr;
+ }
+ const auto& signature = builtin->Signature();
+ auto texture = signature.Parameter(sem::ParameterUsage::kTexture);
+ auto* tex = texture->Type()->As<type::Texture>();
+ if (tex->dim() != ast::TextureDimension::k1d) {
+ return nullptr;
+ }
+
+ if (builtin->Type() == sem::BuiltinType::kTextureDimensions) {
+ // If this textureDimensions() call is in a CallStatement, we can leave it
+ // unmodified since the return value will be dropped on the floor anyway.
+ if (call->Stmt()->Declaration()->Is<ast::CallStatement>()) {
+ return nullptr;
+ }
+ auto* new_call = ctx.CloneWithoutTransform(c);
+ return ctx.dst->MemberAccessor(new_call, "x");
+ }
+
+ auto coords_index = signature.IndexOf(sem::ParameterUsage::kCoords);
+ if (coords_index == -1) {
+ return nullptr;
+ }
+
+ utils::Vector<const ast::Expression*, 8> args;
+ int index = 0;
+ for (auto* arg : c->args) {
+ if (index == coords_index) {
+ auto* ctype = call->Arguments()[static_cast<size_t>(coords_index)]->Type();
+ auto* coords = c->args[static_cast<size_t>(coords_index)];
+
+ const ast::LiteralExpression* half = nullptr;
+ if (ctype->is_integer_scalar()) {
+ half = ctx.dst->Expr(0_a);
+ } else {
+ half = ctx.dst->Expr(0.5_a);
+ }
+ args.Push(
+ ctx.dst->vec(CreateASTTypeFor(ctx, ctype), 2u, ctx.Clone(coords), half));
+ } else {
+ args.Push(ctx.Clone(arg));
+ }
+ index++;
+ }
+ return ctx.dst->Call(ctx.Clone(c->target.name), args);
+ });
+
+ ctx.Clone();
+ return Program(std::move(b));
+ }
+};
+
+Texture1DTo2D::Texture1DTo2D() = default;
+
+Texture1DTo2D::~Texture1DTo2D() = default;
+
+Transform::ApplyResult Texture1DTo2D::Apply(const Program* src, const DataMap&, DataMap&) const {
+ return State(src).Run();
+}
+
+} // namespace tint::transform
diff --git a/src/tint/transform/texture_1d_to_2d.h b/src/tint/transform/texture_1d_to_2d.h
new file mode 100644
index 0000000..9999821
--- /dev/null
+++ b/src/tint/transform/texture_1d_to_2d.h
@@ -0,0 +1,43 @@
+// Copyright 2022 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_TINT_TRANSFORM_TEXTURE_1D_TO_2D_H_
+#define SRC_TINT_TRANSFORM_TEXTURE_1D_TO_2D_H_
+
+#include "src/tint/transform/transform.h"
+
+namespace tint::transform {
+
+/// This transform converts all 1D texture types and accesses to 2D.
+/// This is required for GLSL ES, which does not support 1D textures.
+class Texture1DTo2D final : public Castable<Texture1DTo2D, Transform> {
+ public:
+ /// Constructor
+ Texture1DTo2D();
+
+ /// Destructor
+ ~Texture1DTo2D() override;
+
+ /// @copydoc Transform::Apply
+ ApplyResult Apply(const Program* program,
+ const DataMap& inputs,
+ DataMap& outputs) const override;
+
+ private:
+ struct State;
+};
+
+} // namespace tint::transform
+
+#endif // SRC_TINT_TRANSFORM_TEXTURE_1D_TO_2D_H_
diff --git a/src/tint/transform/texture_1d_to_2d_test.cc b/src/tint/transform/texture_1d_to_2d_test.cc
new file mode 100644
index 0000000..83a12bd
--- /dev/null
+++ b/src/tint/transform/texture_1d_to_2d_test.cc
@@ -0,0 +1,279 @@
+// Copyright 2022 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/tint/transform/texture_1d_to_2d.h"
+
+// #include <memory>
+// #include <utility>
+
+#include "src/tint/transform/test_helper.h"
+
+namespace tint::transform {
+namespace {
+
+using Texture1DTo2DTest = TransformTest;
+
+TEST_F(Texture1DTo2DTest, EmptyModule) {
+ auto* src = "";
+
+ DataMap data;
+ EXPECT_FALSE(ShouldRun<Texture1DTo2D>(src, data));
+}
+
+TEST_F(Texture1DTo2DTest, Global1DDecl) {
+ auto* src = R"(
+@group(0) @binding(0) var t : texture_1d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+)";
+ auto* expect = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+)";
+
+ DataMap data;
+ auto got = Run<Texture1DTo2D>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Texture1DTo2DTest, Global1DDeclAndSample) {
+ auto* src = R"(
+@group(0) @binding(0) var t : texture_1d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+fn main() -> vec4<f32> {
+ return textureSample(t, s, 0.5);
+}
+)";
+ auto* expect = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+fn main() -> vec4<f32> {
+ return textureSample(t, s, vec2<f32>(0.5, 0.5));
+}
+)";
+
+ DataMap data;
+ auto got = Run<Texture1DTo2D>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Texture1DTo2DTest, Global1DDeclAndLoad) {
+ auto* src = R"(
+@group(0) @binding(0) var t : texture_1d<f32>;
+
+fn main() -> vec4<f32> {
+ return textureLoad(t, 1, 0);
+}
+)";
+ auto* expect = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+fn main() -> vec4<f32> {
+ return textureLoad(t, vec2<i32>(1, 0), 0);
+}
+)";
+
+ DataMap data;
+ auto got = Run<Texture1DTo2D>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Texture1DTo2DTest, Global1DDeclAndTextureDimensions) {
+ auto* src = R"(
+@group(0) @binding(0) var t : texture_1d<f32>;
+
+fn main() -> u32 {
+ return textureDimensions(t);
+}
+)";
+ auto* expect = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+fn main() -> u32 {
+ return textureDimensions(t).x;
+}
+)";
+
+ DataMap data;
+ auto got = Run<Texture1DTo2D>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Texture1DTo2DTest, Global1DDeclAndTextureNumLevels) {
+ auto* src = R"(
+@group(0) @binding(0) var t : texture_1d<f32>;
+
+fn main() -> u32 {
+ return textureNumLevels(t);
+}
+)";
+ auto* expect = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+fn main() -> u32 {
+ return textureNumLevels(t);
+}
+)";
+
+ DataMap data;
+ auto got = Run<Texture1DTo2D>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Texture1DTo2DTest, Global1DDeclAndTextureDimensionsInCallStmt) {
+ auto* src = R"(
+@group(0) @binding(0) var t : texture_1d<f32>;
+
+fn main() {
+ textureDimensions(t);
+}
+)";
+ auto* expect = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+fn main() {
+ textureDimensions(t);
+}
+)";
+
+ DataMap data;
+ auto got = Run<Texture1DTo2D>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Texture1DTo2DTest, GlobalStorage1DDecl) {
+ auto* src = R"(
+@group(0) @binding(0) var t : texture_storage_1d<r32float, write>;
+)";
+ auto* expect = R"(
+@group(0) @binding(0) var t : texture_storage_2d<r32float, write>;
+)";
+
+ DataMap data;
+ auto got = Run<Texture1DTo2D>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Texture1DTo2DTest, Global2DDeclAndSample) {
+ auto* src = R"(
+@group(0) @binding(0) var t : texture_2d<f32>;
+
+@group(0) @binding(1) var s : sampler;
+
+fn main() -> vec4<f32> {
+ return textureSample(t, s, vec2<f32>(0.5, 1.5));
+}
+)";
+
+ DataMap data;
+ EXPECT_FALSE(ShouldRun<Texture1DTo2D>(src, data));
+}
+
+TEST_F(Texture1DTo2DTest, PrivateIntNoop) {
+ auto* src = R"(
+var<private> i : i32;
+)";
+
+ DataMap data;
+ EXPECT_FALSE(ShouldRun<Texture1DTo2D>(src, data));
+}
+
+TEST_F(Texture1DTo2DTest, GlobalMatrixNoop) {
+ auto* src = R"(
+@group(0) @binding(0) var<uniform> m : mat2x2<f32>;
+)";
+
+ DataMap data;
+ EXPECT_FALSE(ShouldRun<Texture1DTo2D>(src, data));
+}
+
+TEST_F(Texture1DTo2DTest, Texture1DFuncParam) {
+ auto* src = R"(
+@group(0) @binding(0) var tex : texture_1d<f32>;
+
+@group(0) @binding(1) var samp : sampler;
+
+fn f(t : texture_1d<f32>, s : sampler) -> vec4<f32> {
+ return textureSample(t, s, 0.7);
+}
+
+fn main() -> vec4<f32> {
+ return f(tex, samp);
+}
+)";
+ auto* expect = R"(
+@group(0) @binding(0) var tex : texture_2d<f32>;
+
+@group(0) @binding(1) var samp : sampler;
+
+fn f(t : texture_2d<f32>, s : sampler) -> vec4<f32> {
+ return textureSample(t, s, vec2<f32>(0.7, 0.5));
+}
+
+fn main() -> vec4<f32> {
+ return f(tex, samp);
+}
+)";
+
+ DataMap data;
+ auto got = Run<Texture1DTo2D>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Texture1DTo2DTest, TextureStorage1DFuncParam) {
+ auto* src = R"(
+@group(0) @binding(0) var tex : texture_storage_1d<rgba8unorm, write>;
+
+fn f(t : texture_storage_1d<rgba8unorm, write>) {
+ textureStore(t, 3, vec4<f32>(42.0, 21.0, 84.0, 10.5));
+}
+
+fn main() {
+ f(tex);
+}
+)";
+
+ auto* expect = R"(
+@group(0) @binding(0) var tex : texture_storage_2d<rgba8unorm, write>;
+
+fn f(t : texture_storage_2d<rgba8unorm, write>) {
+ textureStore(t, vec2<i32>(3, 0), vec4<f32>(42.0, 21.0, 84.0, 10.5));
+}
+
+fn main() {
+ f(tex);
+}
+)";
+
+ DataMap data;
+ auto got = Run<Texture1DTo2D>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+} // namespace
+} // namespace tint::transform
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index 8450be3..bc52f15 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -60,6 +60,7 @@
#include "src/tint/transform/simplify_pointers.h"
#include "src/tint/transform/single_entry_point.h"
#include "src/tint/transform/std140.h"
+#include "src/tint/transform/texture_1d_to_2d.h"
#include "src/tint/transform/unshadow.h"
#include "src/tint/transform/zero_init_workgroup_memory.h"
#include "src/tint/type/array.h"
@@ -253,6 +254,8 @@
// Std140 must come after PromoteSideEffectsToDecl and before SimplifyPointers.
manager.Add<transform::Std140>();
+ manager.Add<transform::Texture1DTo2D>();
+
manager.Add<transform::SimplifyPointers>();
data.Add<transform::CanonicalizeEntryPointIO::Config>(
diff --git a/src/tint/writer/glsl/generator_impl_builtin_texture_test.cc b/src/tint/writer/glsl/generator_impl_builtin_texture_test.cc
index 8fc9844..cb5a4c8 100644
--- a/src/tint/writer/glsl/generator_impl_builtin_texture_test.cc
+++ b/src/tint/writer/glsl/generator_impl_builtin_texture_test.cc
@@ -117,7 +117,7 @@
case ValidTextureOverload::kNumSamplesMultisampled2d:
return {"textureSamples"};
case ValidTextureOverload::kSample1dF32:
- return R"(texture(tint_symbol_sampler, 1.0f);)";
+ return R"(texture(tint_symbol_sampler, vec2(1.0f, 0.5f));)";
case ValidTextureOverload::kSample2dF32:
return R"(texture(tint_symbol_sampler, vec2(1.0f, 2.0f));)";
case ValidTextureOverload::kSample2dOffsetF32:
@@ -231,10 +231,10 @@
case ValidTextureOverload::kSampleCompareLevelDepthCubeArrayF32:
return R"(texture(tint_symbol_sampler, vec4(1.0f, 2.0f, 3.0f, float(4)), 5.0f);)";
case ValidTextureOverload::kLoad1dLevelF32:
- return R"(texelFetch(tint_symbol_2, int(1u), int(3u));)";
+ return R"(texelFetch(tint_symbol_2, ivec2(uvec2(1u, 0u)), int(3u));)";
case ValidTextureOverload::kLoad1dLevelU32:
case ValidTextureOverload::kLoad1dLevelI32:
- return R"(texelFetch(tint_symbol_2, 1, 3);)";
+ return R"(texelFetch(tint_symbol_2, ivec2(1, 0), 3);)";
case ValidTextureOverload::kLoad2dLevelU32:
return R"(texelFetch(tint_symbol_2, ivec2(1, 2), 3);)";
case ValidTextureOverload::kLoad2dLevelF32:
@@ -260,7 +260,7 @@
case ValidTextureOverload::kLoadDepthMultisampled2dF32:
return R"(texelFetch(tint_symbol_2, ivec2(uvec2(1u, 2u)), int(3u)).x;)";
case ValidTextureOverload::kStoreWO1dRgba32float:
- return R"(imageStore(tint_symbol, 1, vec4(2.0f, 3.0f, 4.0f, 5.0f));)";
+ return R"(imageStore(tint_symbol, ivec2(1, 0), vec4(2.0f, 3.0f, 4.0f, 5.0f));)";
case ValidTextureOverload::kStoreWO2dRgba32float:
return R"(imageStore(tint_symbol, ivec2(1, 2), vec4(3.0f, 4.0f, 5.0f, 6.0f));)";
case ValidTextureOverload::kStoreWO2dArrayRgba32float: