Import Tint changes from Dawn
Changes:
- a1c6c504cb67019e25f05939b6730aedb1540fe6 [ir] Check operands exist before adding/removing by dan sinclair <dsinclair@chromium.org>
- eac9ac23dee9491cfb62f90d18d6ff77346da2e6 [tint][ir][ToProgram] Test compound assignments by Ben Clayton <bclayton@google.com>
- e692fa7b6e76099a94974529a691a32672fe24bf [tint][ir][transform] Fix MergeReturn test by Ben Clayton <bclayton@google.com>
- b274678f4a243670cd5615ad801f647cf5aea283 [tint][ir] Rename Branch to Terminator by Ben Clayton <bclayton@google.com>
- 0fde633f706b6ce7f8dc40c5fd8c8111fbdc95de [tint][ir] Add comments for exit instructions by Ben Clayton <bclayton@google.com>
- 293e8ae22d0391c3568e1765d5a0117976a9e041 [tint][ir] Add implicit false-block 'undef' value comment by Ben Clayton <bclayton@google.com>
- 8c36b7035dadafe5409c2738ca8734d00150f3c5 [tint][ir] Reformat Block & ControlInstruction disassembly by Ben Clayton <bclayton@google.com>
- eb059e04284052f024287330a97aeaddb6dacf24 [tint][utils] Enable TINT_ASSERT_ITERATORS_NOT_INVALIDATED by Ben Clayton <bclayton@google.com>
- e3feea76729d52c050908e5f8588ee0b964112ff [tint][ir][to_program] Implement short circuit by Ben Clayton <bclayton@google.com>
- f8f852430c6b00a256686e249c03b596386bc8ee [tint][ir] Refactor ToProgram by Ben Clayton <bclayton@google.com>
GitOrigin-RevId: a1c6c504cb67019e25f05939b6730aedb1540fe6
Change-Id: Id8686e57f9ed9544cf50a398c65e2e6ad8a300ed
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/138500
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 1d71177..ae14e47 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -1234,8 +1234,6 @@
"ir/block.h",
"ir/block_param.cc",
"ir/block_param.h",
- "ir/branch.cc",
- "ir/branch.h",
"ir/break_if.cc",
"ir/break_if.h",
"ir/builder.cc",
@@ -1297,6 +1295,8 @@
"ir/switch.h",
"ir/swizzle.cc",
"ir/swizzle.h",
+ "ir/terminator.cc",
+ "ir/terminator.h",
"ir/transform/transform.cc",
"ir/transform/transform.h",
"ir/unary.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 5c0bf6f..0b4cd81 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -734,8 +734,6 @@
ir/block.h
ir/block_param.cc
ir/block_param.h
- ir/branch.cc
- ir/branch.h
ir/break_if.cc
ir/break_if.h
ir/builder.cc
@@ -799,6 +797,8 @@
ir/switch.h
ir/swizzle.cc
ir/swizzle.h
+ ir/terminator.cc
+ ir/terminator.h
ir/to_program.cc
ir/to_program.h
ir/unary.cc
diff --git a/src/tint/ir/block.h b/src/tint/ir/block.h
index 60f6b31..8c9e06a 100644
--- a/src/tint/ir/block.h
+++ b/src/tint/ir/block.h
@@ -17,8 +17,8 @@
#include <utility>
-#include "src/tint/ir/branch.h"
#include "src/tint/ir/instruction.h"
+#include "src/tint/ir/terminator.h"
#include "src/tint/utils/vector.h"
// Forward declarations
@@ -29,25 +29,24 @@
namespace tint::ir {
/// A block of statements. The instructions in the block are a linear list of instructions to
-/// execute. The block will branch at the end. The only blocks which do not branch are the end
-/// blocks of functions.
+/// execute. The block will terminate with a Terminator instruction at the end.
class Block : public utils::Castable<Block> {
public:
/// Constructor
Block();
~Block() override;
- /// @returns true if this is block has a branch target set
- bool HasBranchTarget() {
- return instructions_.last != nullptr && instructions_.last->Is<ir::Branch>();
+ /// @returns true if this is block has a terminator instruction
+ bool HasTerminator() {
+ return instructions_.last != nullptr && instructions_.last->Is<ir::Terminator>();
}
- /// @return the node this block branches to or nullptr if the block doesn't branch
- ir::Branch* Branch() {
- if (!HasBranchTarget()) {
+ /// @return the terminator instruction for this block
+ ir::Terminator* Terminator() {
+ if (!HasTerminator()) {
return nullptr;
}
- return instructions_.last->As<ir::Branch>();
+ return instructions_.last->As<ir::Terminator>();
}
/// @returns the instructions in the block
diff --git a/src/tint/ir/block_test.cc b/src/tint/ir/block_test.cc
index e9523c5..9f5fbfa 100644
--- a/src/tint/ir/block_test.cc
+++ b/src/tint/ir/block_test.cc
@@ -22,65 +22,65 @@
using namespace tint::number_suffixes; // NOLINT
using IR_BlockTest = IRTestHelper;
-TEST_F(IR_BlockTest, HasBranchTarget_Empty) {
+TEST_F(IR_BlockTest, HasTerminator_Empty) {
auto* blk = b.Block();
- EXPECT_FALSE(blk->HasBranchTarget());
+ EXPECT_FALSE(blk->HasTerminator());
}
-TEST_F(IR_BlockTest, HasBranchTarget_NoBranch) {
+TEST_F(IR_BlockTest, HasTerminator_None) {
auto* blk = b.Block();
blk->Append(b.Add(mod.Types().i32(), 1_u, 2_u));
- EXPECT_FALSE(blk->HasBranchTarget());
+ EXPECT_FALSE(blk->HasTerminator());
}
-TEST_F(IR_BlockTest, HasBranchTarget_BreakIf) {
+TEST_F(IR_BlockTest, HasTerminator_BreakIf) {
auto* blk = b.Block();
auto* loop = b.Loop();
blk->Append(b.BreakIf(true, loop));
- EXPECT_TRUE(blk->HasBranchTarget());
+ EXPECT_TRUE(blk->HasTerminator());
}
-TEST_F(IR_BlockTest, HasBranchTarget_Continue) {
+TEST_F(IR_BlockTest, HasTerminator_Continue) {
auto* blk = b.Block();
auto* loop = b.Loop();
blk->Append(b.Continue(loop));
- EXPECT_TRUE(blk->HasBranchTarget());
+ EXPECT_TRUE(blk->HasTerminator());
}
-TEST_F(IR_BlockTest, HasBranchTarget_ExitIf) {
+TEST_F(IR_BlockTest, HasTerminator_ExitIf) {
auto* blk = b.Block();
auto* if_ = b.If(true);
blk->Append(b.ExitIf(if_));
- EXPECT_TRUE(blk->HasBranchTarget());
+ EXPECT_TRUE(blk->HasTerminator());
}
-TEST_F(IR_BlockTest, HasBranchTarget_ExitLoop) {
+TEST_F(IR_BlockTest, HasTerminator_ExitLoop) {
auto* blk = b.Block();
auto* loop = b.Loop();
blk->Append(b.ExitLoop(loop));
- EXPECT_TRUE(blk->HasBranchTarget());
+ EXPECT_TRUE(blk->HasTerminator());
}
-TEST_F(IR_BlockTest, HasBranchTarget_ExitSwitch) {
+TEST_F(IR_BlockTest, HasTerminator_ExitSwitch) {
auto* blk = b.Block();
auto* s = b.Switch(1_u);
blk->Append(b.ExitSwitch(s));
- EXPECT_TRUE(blk->HasBranchTarget());
+ EXPECT_TRUE(blk->HasTerminator());
}
-TEST_F(IR_BlockTest, HasBranchTarget_NextIteration) {
+TEST_F(IR_BlockTest, HasTerminator_NextIteration) {
auto* blk = b.Block();
auto* loop = b.Loop();
blk->Append(b.NextIteration(loop));
- EXPECT_TRUE(blk->HasBranchTarget());
+ EXPECT_TRUE(blk->HasTerminator());
}
-TEST_F(IR_BlockTest, HasBranchTarget_Return) {
+TEST_F(IR_BlockTest, HasTerminator_Return) {
auto* f = b.Function("myFunc", mod.Types().void_());
auto* blk = b.Block();
blk->Append(b.Return(f));
- EXPECT_TRUE(blk->HasBranchTarget());
+ EXPECT_TRUE(blk->HasTerminator());
}
TEST_F(IR_BlockTest, Append) {
diff --git a/src/tint/ir/break_if.h b/src/tint/ir/break_if.h
index 59046b2..7078bc4 100644
--- a/src/tint/ir/break_if.h
+++ b/src/tint/ir/break_if.h
@@ -15,7 +15,7 @@
#ifndef SRC_TINT_IR_BREAK_IF_H_
#define SRC_TINT_IR_BREAK_IF_H_
-#include "src/tint/ir/branch.h"
+#include "src/tint/ir/terminator.h"
#include "src/tint/ir/value.h"
#include "src/tint/utils/castable.h"
@@ -27,7 +27,7 @@
namespace tint::ir {
/// A break-if iteration instruction.
-class BreakIf : public utils::Castable<BreakIf, Branch> {
+class BreakIf : public utils::Castable<BreakIf, Terminator> {
public:
/// The offset in Operands() for the condition
static constexpr size_t kConditionOperandOffset = 0;
@@ -38,11 +38,11 @@
/// Constructor
/// @param condition the break condition
/// @param loop the loop containing the break-if
- /// @param args the branch arguments
+ /// @param args the MultiInBlock arguments
BreakIf(Value* condition, ir::Loop* loop, utils::VectorRef<Value*> args = utils::Empty);
~BreakIf() override;
- /// @returns the branch arguments
+ /// @returns the MultiInBlock arguments
utils::Slice<Value* const> Args() override {
return operands_.Slice().Offset(kArgsOperandOffset);
}
diff --git a/src/tint/ir/builder.h b/src/tint/ir/builder.h
index 9a2c817..8db4098 100644
--- a/src/tint/ir/builder.h
+++ b/src/tint/ir/builder.h
@@ -109,48 +109,48 @@
/// @returns a new multi-in block
ir::MultiInBlock* MultiInBlock();
- /// Creates a function flow node
+ /// Creates a function instruction
/// @param name the function name
/// @param return_type the function return type
/// @param stage the function stage
/// @param wg_size the workgroup_size
- /// @returns the flow node
+ /// @returns the instruction
ir::Function* Function(std::string_view name,
const type::Type* return_type,
Function::PipelineStage stage = Function::PipelineStage::kUndefined,
std::optional<std::array<uint32_t, 3>> wg_size = {});
- /// Creates an if flow node
+ /// Creates an if instruction
/// @param condition the if condition
- /// @returns the flow node
+ /// @returns the instruction
template <typename T>
ir::If* If(T&& condition) {
return Append(
ir.instructions.Create<ir::If>(Value(std::forward<T>(condition)), Block(), Block()));
}
- /// Creates a loop flow node
- /// @returns the flow node
+ /// Creates a loop instruction
+ /// @returns the instruction
ir::Loop* Loop();
- /// Creates a switch flow node
+ /// Creates a switch instruction
/// @param condition the switch condition
- /// @returns the flow node
+ /// @returns the instruction
template <typename T>
ir::Switch* Switch(T&& condition) {
return Append(ir.instructions.Create<ir::Switch>(Value(std::forward<T>(condition))));
}
- /// Creates a case flow node for the given case branch.
+ /// Creates a case for the switch @p s with the given selectors
/// @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
+ /// @returns the start block for the case instruction
ir::Block* Case(ir::Switch* s, utils::VectorRef<Switch::CaseSelector> selectors);
- /// Creates a case flow node for the given case branch.
+ /// Creates a case for the switch @p s with the given selectors
/// @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
+ /// @returns the start block for the case instruction
ir::Block* Case(ir::Switch* s, std::initializer_list<Switch::CaseSelector> selectors);
/// Creates a new ir::Constant
@@ -563,7 +563,7 @@
/// Creates a loop next iteration instruction
/// @param loop the loop being iterated
- /// @param args the branch arguments
+ /// @param args the arguments for the target MultiInBlock
/// @returns the instruction
template <typename... ARGS>
ir::NextIteration* NextIteration(ir::Loop* loop, ARGS&&... args) {
@@ -574,7 +574,7 @@
/// Creates a loop break-if instruction
/// @param condition the break condition
/// @param loop the loop being iterated
- /// @param args the branch arguments
+ /// @param args the arguments for the target MultiInBlock
/// @returns the instruction
template <typename CONDITION, typename... ARGS>
ir::BreakIf* BreakIf(CONDITION&& condition, ir::Loop* loop, ARGS&&... args) {
@@ -584,7 +584,7 @@
/// Creates a continue instruction
/// @param loop the loop being continued
- /// @param args the branch arguments
+ /// @param args the arguments for the target MultiInBlock
/// @returns the instruction
template <typename... ARGS>
ir::Continue* Continue(ir::Loop* loop, ARGS&&... args) {
@@ -594,7 +594,7 @@
/// Creates an exit switch instruction
/// @param sw the switch being exited
- /// @param args the branch arguments
+ /// @param args the arguments for the target MultiInBlock
/// @returns the instruction
template <typename... ARGS>
ir::ExitSwitch* ExitSwitch(ir::Switch* sw, ARGS&&... args) {
@@ -604,7 +604,7 @@
/// Creates an exit loop instruction
/// @param loop the loop being exited
- /// @param args the branch arguments
+ /// @param args the arguments for the target MultiInBlock
/// @returns the instruction
template <typename... ARGS>
ir::ExitLoop* ExitLoop(ir::Loop* loop, ARGS&&... args) {
@@ -614,7 +614,7 @@
/// Creates an exit if instruction
/// @param i the if being exited
- /// @param args the branch arguments
+ /// @param args the arguments for the target MultiInBlock
/// @returns the instruction
template <typename... ARGS>
ir::ExitIf* ExitIf(ir::If* i, ARGS&&... args) {
@@ -623,10 +623,10 @@
/// Creates an exit instruction for the given control instruction
/// @param inst the control instruction being exited
- /// @param args the branch arguments
+ /// @param args the arguments for the target MultiInBlock
/// @returns the exit instruction, or nullptr if the control instruction is not supported.
template <typename... ARGS>
- ir::Branch* Exit(ir::ControlInstruction* inst, ARGS&&... args) {
+ ir::Exit* Exit(ir::ControlInstruction* inst, ARGS&&... args) {
return tint::Switch(
inst, //
[&](ir::If* i) { return ExitIf(i, std::forward<ARGS>(args)...); },
diff --git a/src/tint/ir/continue.h b/src/tint/ir/continue.h
index 6beda97..2f3367c 100644
--- a/src/tint/ir/continue.h
+++ b/src/tint/ir/continue.h
@@ -15,7 +15,7 @@
#ifndef SRC_TINT_IR_CONTINUE_H_
#define SRC_TINT_IR_CONTINUE_H_
-#include "src/tint/ir/branch.h"
+#include "src/tint/ir/terminator.h"
#include "src/tint/utils/castable.h"
// Forward declarations
@@ -26,14 +26,14 @@
namespace tint::ir {
/// A continue instruction.
-class Continue : public utils::Castable<Continue, Branch> {
+class Continue : public utils::Castable<Continue, Terminator> {
public:
/// The base offset in Operands() for the args
static constexpr size_t kArgsOperandOffset = 0;
/// Constructor
/// @param loop the loop owning the continue block
- /// @param args the branch arguments
+ /// @param args the arguments for the MultiInBlock
explicit Continue(ir::Loop* loop, utils::VectorRef<Value*> args = utils::Empty);
~Continue() override;
diff --git a/src/tint/ir/control_instruction.h b/src/tint/ir/control_instruction.h
index ebe5825..701e032 100644
--- a/src/tint/ir/control_instruction.h
+++ b/src/tint/ir/control_instruction.h
@@ -17,7 +17,6 @@
#include <utility>
-#include "src/tint/ir/branch.h"
#include "src/tint/ir/operand_instruction.h"
// Forward declarations
@@ -28,7 +27,7 @@
namespace tint::ir {
-/// Base class of instructions that perform branches to two or more blocks, owned by the
+/// Base class of instructions that perform control flow to two or more blocks, owned by the
/// ControlInstruction.
class ControlInstruction : public utils::Castable<ControlInstruction, OperandInstruction<1, 1>> {
public:
@@ -60,7 +59,7 @@
SetResults(utils::Vector{std::forward<ARGS>(values)...});
}
- /// @return All the exit branches for the flow control instruction
+ /// @return All the exits for the flow control instruction
const utils::Hashset<Exit*, 2>& Exits() const { return exits_; }
/// Adds the exit to the flow control instruction
diff --git a/src/tint/ir/disassembler.cc b/src/tint/ir/disassembler.cc
index 27598f0..629146a 100644
--- a/src/tint/ir/disassembler.cc
+++ b/src/tint/ir/disassembler.cc
@@ -99,6 +99,23 @@
});
}
+std::string_view Disassembler::NameOf(If* inst) {
+ TINT_ASSERT(IR, inst);
+ return if_names_.GetOrCreate(inst, [&] { return "if_" + std::to_string(if_names_.Count()); });
+}
+
+std::string_view Disassembler::NameOf(Loop* inst) {
+ TINT_ASSERT(IR, inst);
+ return loop_names_.GetOrCreate(inst,
+ [&] { return "loop_" + std::to_string(loop_names_.Count()); });
+}
+
+std::string_view Disassembler::NameOf(Switch* inst) {
+ TINT_ASSERT(IR, inst);
+ return switch_names_.GetOrCreate(
+ inst, [&] { return "switch_" + std::to_string(switch_names_.Count()); });
+}
+
Source::Location Disassembler::MakeCurrentLocation() {
return Source::Location{current_output_line_, out_.tellp() - current_output_start_pos_ + 1};
}
@@ -111,9 +128,7 @@
}
if (mod_.root_block) {
- Indent() << "# Root block";
- EmitLine();
- EmitBlock(mod_.root_block);
+ EmitBlock(mod_.root_block, "root");
EmitLine();
}
@@ -123,7 +138,7 @@
return out_.str();
}
-void Disassembler::EmitBlock(Block* blk) {
+void Disassembler::EmitBlock(Block* blk, std::string_view comment /* = "" */) {
Indent();
SourceMarker sm(this);
@@ -138,6 +153,10 @@
sm.Store(blk);
out_ << " {";
+ if (!comment.empty()) {
+ out_ << " # " << comment;
+ }
+
EmitLine();
{
ScopedIndent si(indent_size_);
@@ -468,7 +487,7 @@
}
EmitLine();
},
- [&](Branch* b) { EmitBranch(b); },
+ [&](Terminator* b) { EmitTerminator(b); },
[&](Default) { out_ << "Unknown instruction: " << inst->TypeInfo().name; });
}
@@ -515,24 +534,29 @@
out_ << "]";
sm.Store(i);
+ out_ << " { # " << NameOf(i);
EmitLine();
if (has_true) {
ScopedIndent si(indent_size_);
- Indent() << "# True block";
- EmitLine();
-
- EmitBlock(i->True());
- EmitLine();
+ EmitBlock(i->True(), "true");
}
if (has_false) {
ScopedIndent si(indent_size_);
- Indent() << "# False block";
- EmitLine();
-
- EmitBlock(i->False());
+ EmitBlock(i->False(), "false");
+ } else if (i->HasResults()) {
+ ScopedIndent si(indent_size_);
+ Indent();
+ out_ << "# implicit false block: exit_if undef";
+ for (size_t v = 1; v < i->Results().Length(); v++) {
+ out_ << ", undef";
+ }
EmitLine();
}
+
+ Indent();
+ out_ << "}";
+ EmitLine();
}
void Disassembler::EmitLoop(Loop* l) {
@@ -551,31 +575,27 @@
out_ << "loop [" << utils::Join(parts, ", ") << "]";
sm.Store(l);
+ out_ << " { # " << NameOf(l);
EmitLine();
if (!l->Initializer()->IsEmpty()) {
ScopedIndent si(indent_size_);
- Indent() << "# Initializer block";
- EmitLine();
- EmitBlock(l->Initializer());
- EmitLine();
+ EmitBlock(l->Initializer(), "initializer");
}
if (!l->Body()->IsEmpty()) {
ScopedIndent si(indent_size_);
- Indent() << "# Body block";
- EmitLine();
- EmitBlock(l->Body());
- EmitLine();
+ EmitBlock(l->Body(), "body");
}
if (!l->Continuing()->IsEmpty()) {
ScopedIndent si(indent_size_);
- Indent() << "# Continuing block";
- EmitLine();
- EmitBlock(l->Continuing());
- EmitLine();
+ EmitBlock(l->Continuing(), "continuing");
}
+
+ Indent();
+ out_ << "}";
+ EmitLine();
}
void Disassembler::EmitSwitch(Switch* s) {
@@ -600,20 +620,20 @@
}
out_ << ", %b" << IdOf(c.Block()) << ")";
}
- out_ << "]";
+ out_ << "] { # " << NameOf(s);
EmitLine();
for (auto& c : s->Cases()) {
ScopedIndent si(indent_size_);
- Indent() << "# Case block";
- EmitLine();
-
- EmitBlock(c.Block());
- EmitLine();
+ EmitBlock(c.Block(), "case");
}
+
+ Indent();
+ out_ << "}";
+ EmitLine();
}
-void Disassembler::EmitBranch(Branch* b) {
+void Disassembler::EmitTerminator(Terminator* b) {
SourceMarker sm(this);
tint::Switch(
b, //
@@ -630,7 +650,7 @@
out_ << " %b" << IdOf(bi->Loop()->Body());
},
[&](Unreachable*) { out_ << "unreachable"; },
- [&](Default) { out_ << "Unknown branch " << b->TypeInfo().name; });
+ [&](Default) { out_ << "unknown terminator " << b->TypeInfo().name; });
if (!b->Args().IsEmpty()) {
out_ << " ";
@@ -638,6 +658,12 @@
}
sm.Store(b);
+ tint::Switch(
+ b, //
+ [&](ir::ExitIf* e) { out_ << " # " << NameOf(e->If()); }, //
+ [&](ir::ExitSwitch* e) { out_ << " # " << NameOf(e->Switch()); }, //
+ [&](ir::ExitLoop* e) { out_ << " # " << NameOf(e->Loop()); } //
+ );
EmitLine();
}
diff --git a/src/tint/ir/disassembler.h b/src/tint/ir/disassembler.h
index 71773f1..940f7f6 100644
--- a/src/tint/ir/disassembler.h
+++ b/src/tint/ir/disassembler.h
@@ -108,8 +108,11 @@
size_t IdOf(Block* blk);
std::string_view IdOf(Value* node);
+ std::string_view NameOf(If* inst);
+ std::string_view NameOf(Loop* inst);
+ std::string_view NameOf(Switch* inst);
- void EmitBlock(Block* blk);
+ void EmitBlock(Block* blk, std::string_view comment = "");
void EmitFunction(Function* func);
void EmitParamAttributes(FunctionParam* p);
void EmitReturnAttributes(Function* func);
@@ -123,7 +126,7 @@
void EmitArgs(Call* call);
void EmitBinary(Binary* b);
void EmitUnary(Unary* b);
- void EmitBranch(Branch* b);
+ void EmitTerminator(Terminator* b);
void EmitSwitch(Switch* s);
void EmitLoop(Loop* l);
void EmitIf(If* i);
@@ -148,6 +151,9 @@
utils::Hashmap<Block*, Source, 8> block_to_src_;
utils::Hashmap<Instruction*, Source, 8> instruction_to_src_;
utils::Hashmap<Usage, Source, 8, Usage::Hasher> operand_to_src_;
+ utils::Hashmap<If*, std::string, 8> if_names_;
+ utils::Hashmap<Loop*, std::string, 8> loop_names_;
+ utils::Hashmap<Switch*, std::string, 8> switch_names_;
};
} // namespace tint::ir
diff --git a/src/tint/ir/exit.h b/src/tint/ir/exit.h
index f9734fb..b8b8db1 100644
--- a/src/tint/ir/exit.h
+++ b/src/tint/ir/exit.h
@@ -15,7 +15,7 @@
#ifndef SRC_TINT_IR_EXIT_H_
#define SRC_TINT_IR_EXIT_H_
-#include "src/tint/ir/branch.h"
+#include "src/tint/ir/terminator.h"
// Forward declarations
namespace tint::ir {
@@ -25,7 +25,7 @@
namespace tint::ir {
/// The base class for all exit terminators.
-class Exit : public utils::Castable<Exit, Branch> {
+class Exit : public utils::Castable<Exit, Terminator> {
public:
~Exit() override;
diff --git a/src/tint/ir/exit_if.h b/src/tint/ir/exit_if.h
index e3cf9e7..0b89d0a 100644
--- a/src/tint/ir/exit_if.h
+++ b/src/tint/ir/exit_if.h
@@ -33,7 +33,7 @@
/// Constructor
/// @param i the if being exited
- /// @param args the branch arguments
+ /// @param args the target MultiInBlock arguments
explicit ExitIf(ir::If* i, utils::VectorRef<Value*> args = utils::Empty);
~ExitIf() override;
diff --git a/src/tint/ir/exit_loop.h b/src/tint/ir/exit_loop.h
index 253655e..3931287 100644
--- a/src/tint/ir/exit_loop.h
+++ b/src/tint/ir/exit_loop.h
@@ -33,7 +33,7 @@
/// Constructor
/// @param loop the loop being exited
- /// @param args the branch arguments
+ /// @param args the target MultiInBlock arguments
explicit ExitLoop(ir::Loop* loop, utils::VectorRef<Value*> args = utils::Empty);
~ExitLoop() override;
diff --git a/src/tint/ir/exit_switch.h b/src/tint/ir/exit_switch.h
index 9567a59..6f32890 100644
--- a/src/tint/ir/exit_switch.h
+++ b/src/tint/ir/exit_switch.h
@@ -33,7 +33,7 @@
/// Constructor
/// @param sw the switch being exited
- /// @param args the branch arguments
+ /// @param args the target MultiInBlock arguments
explicit ExitSwitch(ir::Switch* sw, utils::VectorRef<Value*> args = utils::Empty);
~ExitSwitch() override;
diff --git a/src/tint/ir/from_program.cc b/src/tint/ir/from_program.cc
index c831d83..3ff8811 100644
--- a/src/tint/ir/from_program.cc
+++ b/src/tint/ir/from_program.cc
@@ -172,13 +172,13 @@
diagnostics_.add_error(tint::diag::System::IR, err, s);
}
- bool NeedBranch() { return current_block_ && !current_block_->HasBranchTarget(); }
+ bool NeedTerminator() { return current_block_ && !current_block_->HasTerminator(); }
- void SetBranch(Branch* br) {
+ void SetTerminator(Terminator* terminator) {
TINT_ASSERT(IR, current_block_);
- TINT_ASSERT(IR, !current_block_->HasBranchTarget());
+ TINT_ASSERT(IR, !current_block_->HasTerminator());
- current_block_->Append(br);
+ current_block_->Append(terminator);
current_block_ = nullptr;
}
@@ -211,7 +211,7 @@
// Folded away and doesn't appear in the IR.
},
[&](const ast::Variable* var) {
- // Setup the current flow node to be the root block for the module. The builder
+ // Setup the current block to be the root block for the module. The builder
// will handle creating it if it doesn't exist already.
TINT_SCOPED_ASSIGNMENT(current_block_, builder_.RootBlock());
EmitVariable(var);
@@ -420,10 +420,9 @@
TINT_SCOPED_ASSIGNMENT(current_block_, ir_func->StartTarget());
EmitBlock(ast_func->body);
- // If the branch target has already been set then a `return` was called. Only set in
- // the case where `return` wasn't called.
- if (NeedBranch()) {
- SetBranch(builder_.Return(current_function_));
+ // Add a terminator if one was not already created.
+ if (NeedTerminator()) {
+ SetTerminator(builder_.Return(current_function_));
}
TINT_ASSERT(IR, control_stack_.IsEmpty());
@@ -593,9 +592,9 @@
scopes_.Push();
TINT_DEFER(scopes_.Pop());
- // Note, this doesn't need to emit a Block as the current block flow node should be
- // sufficient as the blocks all get flattened. Each flow control node will inject the
- // basic blocks it requires.
+ // Note, this doesn't need to emit a Block as the current block should be sufficient as the
+ // blocks all get flattened. Each flow control node will inject the basic blocks it
+ // requires.
EmitStatements(block->statements);
}
@@ -615,9 +614,9 @@
TINT_SCOPED_ASSIGNMENT(current_block_, if_inst->True());
EmitBlock(stmt->body);
- // If the true block did not branch, then emit an exit_if
- if (NeedBranch()) {
- SetBranch(builder_.ExitIf(if_inst));
+ // If the true block did not terminate, then emit an exit_if
+ if (NeedTerminator()) {
+ SetTerminator(builder_.ExitIf(if_inst));
}
}
@@ -625,9 +624,9 @@
TINT_SCOPED_ASSIGNMENT(current_block_, if_inst->False());
EmitStatement(stmt->else_statement);
- // If the false block did not branch, then emit an exit_if
- if (NeedBranch()) {
- SetBranch(builder_.ExitIf(if_inst));
+ // If the false block did not terminate, then emit an exit_if
+ if (NeedTerminator()) {
+ SetTerminator(builder_.ExitIf(if_inst));
}
}
}
@@ -650,8 +649,8 @@
EmitStatements(stmt->body->statements);
// The current block didn't `break`, `return` or `continue`, go to the continuing block.
- if (NeedBranch()) {
- SetBranch(builder_.Continue(loop_inst));
+ if (NeedTerminator()) {
+ SetTerminator(builder_.Continue(loop_inst));
}
}
@@ -660,9 +659,9 @@
if (stmt->continuing) {
EmitBlock(stmt->continuing);
}
- // Branch back to the start block if the continue target didn't branch out already
- if (NeedBranch()) {
- SetBranch(builder_.NextIteration(loop_inst));
+ // Branch back to the start block if the continue target didn't terminate already
+ if (NeedTerminator()) {
+ SetTerminator(builder_.NextIteration(loop_inst));
}
}
}
@@ -676,7 +675,7 @@
// Continue is always empty, just go back to the start
{
TINT_SCOPED_ASSIGNMENT(current_block_, loop_inst->Continuing());
- SetBranch(builder_.NextIteration(loop_inst));
+ SetTerminator(builder_.NextIteration(loop_inst));
}
{
@@ -694,16 +693,16 @@
{
TINT_SCOPED_ASSIGNMENT(current_block_, if_inst->True());
- SetBranch(builder_.ExitIf(if_inst));
+ SetTerminator(builder_.ExitIf(if_inst));
}
{
TINT_SCOPED_ASSIGNMENT(current_block_, if_inst->False());
- SetBranch(builder_.ExitLoop(loop_inst));
+ SetTerminator(builder_.ExitLoop(loop_inst));
}
- if (NeedBranch()) {
- SetBranch(builder_.Continue(loop_inst));
+ if (NeedTerminator()) {
+ SetTerminator(builder_.Continue(loop_inst));
}
}
}
@@ -724,8 +723,8 @@
// Emit the for initializer before branching to the loop body
EmitStatement(stmt->initializer);
- if (NeedBranch()) {
- SetBranch(builder_.NextIteration(loop_inst));
+ if (NeedTerminator()) {
+ SetTerminator(builder_.NextIteration(loop_inst));
}
}
@@ -744,24 +743,24 @@
{
TINT_SCOPED_ASSIGNMENT(current_block_, if_inst->True());
- SetBranch(builder_.ExitIf(if_inst));
+ SetTerminator(builder_.ExitIf(if_inst));
}
{
TINT_SCOPED_ASSIGNMENT(current_block_, if_inst->False());
- SetBranch(builder_.ExitLoop(loop_inst));
+ SetTerminator(builder_.ExitLoop(loop_inst));
}
}
EmitBlock(stmt->body);
- if (NeedBranch()) {
- SetBranch(builder_.Continue(loop_inst));
+ if (NeedTerminator()) {
+ SetTerminator(builder_.Continue(loop_inst));
}
if (stmt->continuing) {
TINT_SCOPED_ASSIGNMENT(current_block_, loop_inst->Continuing());
EmitStatement(stmt->continuing);
- SetBranch(builder_.NextIteration(loop_inst));
+ SetTerminator(builder_.NextIteration(loop_inst));
}
}
@@ -790,8 +789,8 @@
TINT_SCOPED_ASSIGNMENT(current_block_, builder_.Case(switch_inst, selectors));
EmitBlock(c->Body()->Declaration());
- if (NeedBranch()) {
- SetBranch(builder_.ExitSwitch(switch_inst));
+ if (NeedTerminator()) {
+ SetTerminator(builder_.ExitSwitch(switch_inst));
}
}
}
@@ -806,9 +805,9 @@
ret_value = ret.Get();
}
if (ret_value) {
- SetBranch(builder_.Return(current_function_, ret_value));
+ SetTerminator(builder_.Return(current_function_, ret_value));
} else {
- SetBranch(builder_.Return(current_function_));
+ SetTerminator(builder_.Return(current_function_));
}
}
@@ -817,9 +816,9 @@
TINT_ASSERT(IR, current_control);
if (auto* c = current_control->As<Loop>()) {
- SetBranch(builder_.ExitLoop(c));
+ SetTerminator(builder_.ExitLoop(c));
} else if (auto* s = current_control->As<Switch>()) {
- SetBranch(builder_.ExitSwitch(s));
+ SetTerminator(builder_.ExitSwitch(s));
} else {
TINT_UNREACHABLE(IR, diagnostics_);
}
@@ -830,7 +829,7 @@
TINT_ASSERT(IR, current_control);
if (auto* c = current_control->As<Loop>()) {
- SetBranch(builder_.Continue(c));
+ SetTerminator(builder_.Continue(c));
} else {
TINT_UNREACHABLE(IR, diagnostics_);
}
@@ -853,7 +852,7 @@
if (!cond) {
return;
}
- SetBranch(builder_.BreakIf(cond.Get(), current_control->As<ir::Loop>()));
+ SetTerminator(builder_.BreakIf(cond.Get(), current_control->As<ir::Loop>()));
}
struct AccessorInfo {
@@ -1174,16 +1173,16 @@
// if (lhs) {
// res = rhs;
// } else {
- // res = lhs;
+ // res = false;
// }
{
TINT_SCOPED_ASSIGNMENT(current_block_, if_inst->True());
auto rhs = EmitExpression(expr->rhs);
- SetBranch(builder_.ExitIf(if_inst, utils::Vector{rhs.Get()}));
+ SetTerminator(builder_.ExitIf(if_inst, rhs.Get()));
}
{
TINT_SCOPED_ASSIGNMENT(current_block_, if_inst->False());
- SetBranch(builder_.ExitIf(if_inst, utils::Vector{lhs.Get()}));
+ SetTerminator(builder_.ExitIf(if_inst, builder_.Constant(false)));
}
} else {
// res = lhs || rhs;
@@ -1191,18 +1190,18 @@
// transform into:
//
// if (lhs) {
- // res = lhs;
+ // res = true;
// } else {
// res = rhs;
// }
{
TINT_SCOPED_ASSIGNMENT(current_block_, if_inst->True());
- SetBranch(builder_.ExitIf(if_inst, utils::Vector{lhs.Get()}));
+ SetTerminator(builder_.ExitIf(if_inst, builder_.Constant(true)));
}
{
TINT_SCOPED_ASSIGNMENT(current_block_, if_inst->False());
auto rhs = EmitExpression(expr->rhs);
- SetBranch(builder_.ExitIf(if_inst, utils::Vector{rhs.Get()}));
+ SetTerminator(builder_.ExitIf(if_inst, rhs.Get()));
}
}
diff --git a/src/tint/ir/from_program_binary_test.cc b/src/tint/ir/from_program_binary_test.cc
index 86570e4..9763942 100644
--- a/src/tint/ir/from_program_binary_test.cc
+++ b/src/tint/ir/from_program_binary_test.cc
@@ -56,8 +56,7 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- EXPECT_EQ(Disassemble(m.Get()), R"(# Root block
-%b1 = block {
+ EXPECT_EQ(Disassemble(m.Get()), R"(%b1 = block { # root
%v1:ptr<private, u32, read_write> = var
}
@@ -80,8 +79,7 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- EXPECT_EQ(Disassemble(m.Get()), R"(# Root block
-%b1 = block {
+ EXPECT_EQ(Disassemble(m.Get()), R"(%b1 = block { # root
%v1:ptr<private, u32, read_write> = var
}
@@ -127,8 +125,7 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- EXPECT_EQ(Disassemble(m.Get()), R"(# Root block
-%b1 = block {
+ EXPECT_EQ(Disassemble(m.Get()), R"(%b1 = block { # root
%v1:ptr<private, i32, read_write> = var
}
@@ -151,8 +148,7 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- EXPECT_EQ(Disassemble(m.Get()), R"(# Root block
-%b1 = block {
+ EXPECT_EQ(Disassemble(m.Get()), R"(%b1 = block { # root
%v1:ptr<private, u32, read_write> = var
}
@@ -198,8 +194,7 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- EXPECT_EQ(Disassemble(m.Get()), R"(# Root block
-%b1 = block {
+ EXPECT_EQ(Disassemble(m.Get()), R"(%b1 = block { # root
%v1:ptr<private, u32, read_write> = var
}
@@ -245,8 +240,7 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- EXPECT_EQ(Disassemble(m.Get()), R"(# Root block
-%b1 = block {
+ EXPECT_EQ(Disassemble(m.Get()), R"(%b1 = block { # root
%v1:ptr<private, u32, read_write> = var
}
@@ -292,8 +286,7 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- EXPECT_EQ(Disassemble(m.Get()), R"(# Root block
-%b1 = block {
+ EXPECT_EQ(Disassemble(m.Get()), R"(%b1 = block { # root
%v1:ptr<private, u32, read_write> = var
}
@@ -339,8 +332,7 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- EXPECT_EQ(Disassemble(m.Get()), R"(# Root block
-%b1 = block {
+ EXPECT_EQ(Disassemble(m.Get()), R"(%b1 = block { # root
%v1:ptr<private, bool, read_write> = var
}
@@ -386,8 +378,7 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- EXPECT_EQ(Disassemble(m.Get()), R"(# Root block
-%b1 = block {
+ EXPECT_EQ(Disassemble(m.Get()), R"(%b1 = block { # root
%v1:ptr<private, bool, read_write> = var
}
@@ -433,8 +424,7 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- EXPECT_EQ(Disassemble(m.Get()), R"(# Root block
-%b1 = block {
+ EXPECT_EQ(Disassemble(m.Get()), R"(%b1 = block { # root
%v1:ptr<private, u32, read_write> = var
}
@@ -466,23 +456,19 @@
%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b2 {
%b2 = block {
%3:bool = call %my_func
- %logical_and:bool = if %3 [t: %b3, f: %b4]
- # True block
- %b3 = block {
- exit_if false
+ %logical_and:bool = if %3 [t: %b3, f: %b4] { # if_1
+ %b3 = block { # true
+ exit_if false # if_1
}
-
- # False block
- %b4 = block {
- exit_if %3
+ %b4 = block { # false
+ exit_if false # if_1
}
-
- if %logical_and [t: %b5]
- # True block
- %b5 = block {
- exit_if
+ }
+ if %logical_and [t: %b5] { # if_2
+ %b5 = block { # true
+ exit_if # if_2
}
-
+ }
ret
}
}
@@ -506,23 +492,19 @@
%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b2 {
%b2 = block {
%3:bool = call %my_func
- %logical_or:bool = if %3 [t: %b3, f: %b4]
- # True block
- %b3 = block {
- exit_if %3
+ %logical_or:bool = if %3 [t: %b3, f: %b4] { # if_1
+ %b3 = block { # true
+ exit_if true # if_1
}
-
- # False block
- %b4 = block {
- exit_if true
+ %b4 = block { # false
+ exit_if true # if_1
}
-
- if %logical_or [t: %b5]
- # True block
- %b5 = block {
- exit_if
+ }
+ if %logical_or [t: %b5] { # if_2
+ %b5 = block { # true
+ exit_if # if_2
}
-
+ }
ret
}
}
@@ -698,8 +680,7 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- EXPECT_EQ(Disassemble(m.Get()), R"(# Root block
-%b1 = block {
+ EXPECT_EQ(Disassemble(m.Get()), R"(%b1 = block { # root
%v1:ptr<private, u32, read_write> = var
}
@@ -745,8 +726,7 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- EXPECT_EQ(Disassemble(m.Get()), R"(# Root block
-%b1 = block {
+ EXPECT_EQ(Disassemble(m.Get()), R"(%b1 = block { # root
%v1:ptr<private, u32, read_write> = var
}
@@ -780,22 +760,19 @@
%b2 = block {
%3:f32 = call %my_func
%4:bool = lt %3, 2.0f
- %tint_symbol:bool = if %4 [t: %b3, f: %b4]
- # True block
- %b3 = block {
+ %tint_symbol:bool = if %4 [t: %b3, f: %b4] { # if_1
+ %b3 = block { # true
%6:f32 = call %my_func
%7:f32 = call %my_func
%8:f32 = mul 2.29999995231628417969f, %7
%9:f32 = div %6, %8
%10:bool = gt 2.5f, %9
- exit_if %10
+ exit_if %10 # if_1
}
-
- # False block
- %b4 = block {
- exit_if %4
+ %b4 = block { # false
+ exit_if false # if_1
}
-
+ }
ret
}
}
diff --git a/src/tint/ir/from_program_builtin_test.cc b/src/tint/ir/from_program_builtin_test.cc
index 262c8bd..296ec79 100644
--- a/src/tint/ir/from_program_builtin_test.cc
+++ b/src/tint/ir/from_program_builtin_test.cc
@@ -33,8 +33,7 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- EXPECT_EQ(Disassemble(m.Get()), R"(# Root block
-%b1 = block {
+ EXPECT_EQ(Disassemble(m.Get()), R"(%b1 = block { # root
%i:ptr<private, f32, read_write> = var, 1.0f
}
diff --git a/src/tint/ir/from_program_call_test.cc b/src/tint/ir/from_program_call_test.cc
index 1148f56..3807e85 100644
--- a/src/tint/ir/from_program_call_test.cc
+++ b/src/tint/ir/from_program_call_test.cc
@@ -99,8 +99,7 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- EXPECT_EQ(Disassemble(m.Get()), R"(# Root block
-%b1 = block {
+ EXPECT_EQ(Disassemble(m.Get()), R"(%b1 = block { # root
%i:ptr<private, i32, read_write> = var, 1i
}
@@ -121,8 +120,7 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- EXPECT_EQ(Disassemble(m.Get()), R"(# Root block
-%b1 = block {
+ EXPECT_EQ(Disassemble(m.Get()), R"(%b1 = block { # root
%i:ptr<private, vec3<f32>, read_write> = var, vec3<f32>(0.0f)
}
@@ -137,8 +135,7 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- EXPECT_EQ(Disassemble(m.Get()), R"(# Root block
-%b1 = block {
+ EXPECT_EQ(Disassemble(m.Get()), R"(%b1 = block { # root
%i:ptr<private, f32, read_write> = var, 1.0f
}
diff --git a/src/tint/ir/from_program_store_test.cc b/src/tint/ir/from_program_store_test.cc
index c58cd47..31e9792 100644
--- a/src/tint/ir/from_program_store_test.cc
+++ b/src/tint/ir/from_program_store_test.cc
@@ -34,8 +34,7 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- EXPECT_EQ(Disassemble(m.Get()), R"(# Root block
-%b1 = block {
+ EXPECT_EQ(Disassemble(m.Get()), R"(%b1 = block { # root
%a:ptr<private, u32, read_write> = var
}
diff --git a/src/tint/ir/from_program_test.cc b/src/tint/ir/from_program_test.cc
index 1dd8f24..81738d6 100644
--- a/src/tint/ir/from_program_test.cc
+++ b/src/tint/ir/from_program_test.cc
@@ -26,9 +26,9 @@
namespace tint::ir {
namespace {
-/// Looks for the flow node with the given type T.
-/// If no flow node is found, then nullptr is returned.
-/// If multiple flow nodes are found with the type T, then an error is raised and the first is
+/// Looks for the instruction with the given type T.
+/// If no instruction is found, then nullptr is returned.
+/// If multiple instructions are found with the type T, then an error is raised and the first is
/// returned.
template <typename T>
T* FindSingleInstruction(Module& mod) {
@@ -141,17 +141,14 @@
EXPECT_EQ(Disassemble(m),
R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
%b1 = block {
- if true [t: %b2, f: %b3]
- # True block
- %b2 = block {
- exit_if
+ if true [t: %b2, f: %b3] { # if_1
+ %b2 = block { # true
+ exit_if # if_1
}
-
- # False block
- %b3 = block {
- exit_if
+ %b3 = block { # false
+ exit_if # if_1
}
-
+ }
ret
}
}
@@ -172,12 +169,11 @@
EXPECT_EQ(Disassemble(m),
R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
%b1 = block {
- if true [t: %b2]
- # True block
- %b2 = block {
+ if true [t: %b2] { # if_1
+ %b2 = block { # true
ret
}
-
+ }
ret
}
}
@@ -198,17 +194,14 @@
EXPECT_EQ(Disassemble(m),
R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
%b1 = block {
- if true [t: %b2, f: %b3]
- # True block
- %b2 = block {
- exit_if
+ if true [t: %b2, f: %b3] { # if_1
+ %b2 = block { # true
+ exit_if # if_1
}
-
- # False block
- %b3 = block {
+ %b3 = block { # false
ret
}
-
+ }
ret
}
}
@@ -229,17 +222,14 @@
EXPECT_EQ(Disassemble(m),
R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
%b1 = block {
- if true [t: %b2, f: %b3]
- # True block
- %b2 = block {
+ if true [t: %b2, f: %b3] { # if_1
+ %b2 = block { # true
ret
}
-
- # False block
- %b3 = block {
+ %b3 = block { # false
ret
}
-
+ }
ret
}
}
@@ -259,23 +249,19 @@
EXPECT_EQ(Disassemble(m),
R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
%b1 = block {
- if true [t: %b2]
- # True block
- %b2 = block {
- loop [b: %b3, c: %b4]
- # Body block
- %b3 = block {
- exit_loop
+ if true [t: %b2] { # if_1
+ %b2 = block { # true
+ loop [b: %b3, c: %b4] { # loop_1
+ %b3 = block { # body
+ exit_loop # loop_1
}
-
- # Continuing block
- %b4 = block {
+ %b4 = block { # continuing
next_iteration %b3
}
-
- exit_if
+ }
+ exit_if # if_1
}
-
+ }
ret
}
}
@@ -300,17 +286,14 @@
EXPECT_EQ(Disassemble(m),
R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
%b1 = block {
- loop [b: %b2, c: %b3]
- # Body block
- %b2 = block {
- exit_loop
+ loop [b: %b2, c: %b3] { # loop_1
+ %b2 = block { # body
+ exit_loop # loop_1
}
-
- # Continuing block
- %b3 = block {
+ %b3 = block { # continuing
next_iteration %b2
}
-
+ }
ret
}
}
@@ -336,23 +319,19 @@
EXPECT_EQ(Disassemble(m),
R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
%b1 = block {
- loop [b: %b2, c: %b3]
- # Body block
- %b2 = block {
- if true [t: %b4]
- # True block
- %b4 = block {
- exit_loop
+ loop [b: %b2, c: %b3] { # loop_1
+ %b2 = block { # body
+ if true [t: %b4] { # if_1
+ %b4 = block { # true
+ exit_loop # loop_1
}
-
+ }
continue %b3
}
-
- # Continuing block
- %b3 = block {
+ %b3 = block { # continuing
next_iteration %b2
}
-
+ }
ret
}
}
@@ -378,17 +357,14 @@
EXPECT_EQ(Disassemble(m),
R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
%b1 = block {
- loop [b: %b2, c: %b3]
- # Body block
- %b2 = block {
+ loop [b: %b2, c: %b3] { # loop_1
+ %b2 = block { # body
continue %b3
}
-
- # Continuing block
- %b3 = block {
+ %b3 = block { # continuing
break_if true %b2
}
-
+ }
ret
}
}
@@ -408,17 +384,14 @@
EXPECT_EQ(Disassemble(m),
R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
%b1 = block {
- loop [b: %b2, c: %b3]
- # Body block
- %b2 = block {
+ loop [b: %b2, c: %b3] { # loop_1
+ %b2 = block { # body
continue %b3
}
-
- # Continuing block
- %b3 = block {
+ %b3 = block { # continuing
break_if true %b2
}
-
+ }
ret
}
}
@@ -444,23 +417,19 @@
EXPECT_EQ(Disassemble(m),
R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
%b1 = block {
- loop [b: %b2, c: %b3]
- # Body block
- %b2 = block {
- if true [t: %b4]
- # True block
- %b4 = block {
+ loop [b: %b2, c: %b3] { # loop_1
+ %b2 = block { # body
+ if true [t: %b4] { # if_1
+ %b4 = block { # true
ret
}
-
+ }
continue %b3
}
-
- # Continuing block
- %b3 = block {
+ %b3 = block { # continuing
next_iteration %b2
}
-
+ }
ret
}
}
@@ -485,17 +454,14 @@
EXPECT_EQ(Disassemble(m),
R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
%b1 = block {
- loop [b: %b2, c: %b3]
- # Body block
- %b2 = block {
+ loop [b: %b2, c: %b3] { # loop_1
+ %b2 = block { # body
ret
}
-
- # Continuing block
- %b3 = block {
+ %b3 = block { # continuing
next_iteration %b2
}
-
+ }
ret
}
}
@@ -529,23 +495,19 @@
EXPECT_EQ(Disassemble(m),
R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
%b1 = block {
- loop [b: %b2, c: %b3]
- # Body block
- %b2 = block {
+ loop [b: %b2, c: %b3] { # loop_1
+ %b2 = block { # body
ret
}
-
- # Continuing block
- %b3 = block {
+ %b3 = block { # continuing
break_if true %b2
}
-
- if true [t: %b4]
- # True block
- %b4 = block {
+ }
+ if true [t: %b4] { # if_1
+ %b4 = block { # true
ret
}
-
+ }
ret
}
}
@@ -571,28 +533,22 @@
EXPECT_EQ(Disassemble(m),
R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
%b1 = block {
- loop [b: %b2, c: %b3]
- # Body block
- %b2 = block {
- if true [t: %b4, f: %b5]
- # True block
- %b4 = block {
- exit_loop
+ loop [b: %b2, c: %b3] { # loop_1
+ %b2 = block { # body
+ if true [t: %b4, f: %b5] { # if_1
+ %b4 = block { # true
+ exit_loop # loop_1
}
-
- # False block
- %b5 = block {
- exit_loop
+ %b5 = block { # false
+ exit_loop # loop_1
}
-
+ }
continue %b3
}
-
- # Continuing block
- %b3 = block {
+ %b3 = block { # continuing
next_iteration %b2
}
-
+ }
ret
}
}
@@ -619,68 +575,53 @@
EXPECT_EQ(Disassemble(m.Get()),
R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
%b1 = block {
- loop [b: %b2, c: %b3]
- # Body block
- %b2 = block {
- loop [b: %b4, c: %b5]
- # Body block
- %b4 = block {
- if true [t: %b6]
- # True block
- %b6 = block {
- exit_loop
+ loop [b: %b2, c: %b3] { # loop_1
+ %b2 = block { # body
+ loop [b: %b4, c: %b5] { # loop_2
+ %b4 = block { # body
+ if true [t: %b6] { # if_1
+ %b6 = block { # true
+ exit_loop # loop_2
}
-
- if true [t: %b7]
- # True block
- %b7 = block {
+ }
+ if true [t: %b7] { # if_2
+ %b7 = block { # true
continue %b5
}
-
+ }
continue %b5
}
-
- # Continuing block
- %b5 = block {
- loop [b: %b8, c: %b9]
- # Body block
- %b8 = block {
- exit_loop
+ %b5 = block { # continuing
+ loop [b: %b8, c: %b9] { # loop_3
+ %b8 = block { # body
+ exit_loop # loop_3
}
-
- # Continuing block
- %b9 = block {
+ %b9 = block { # continuing
next_iteration %b8
}
-
- loop [b: %b10, c: %b11]
- # Body block
- %b10 = block {
+ }
+ loop [b: %b10, c: %b11] { # loop_4
+ %b10 = block { # body
continue %b11
}
-
- # Continuing block
- %b11 = block {
+ %b11 = block { # continuing
break_if true %b10
}
-
+ }
next_iteration %b4
}
-
- if true [t: %b12]
- # True block
- %b12 = block {
- exit_loop
+ }
+ if true [t: %b12] { # if_3
+ %b12 = block { # true
+ exit_loop # loop_1
}
-
+ }
continue %b3
}
-
- # Continuing block
- %b3 = block {
+ %b3 = block { # continuing
next_iteration %b2
}
-
+ }
ret
}
}
@@ -705,28 +646,22 @@
EXPECT_EQ(Disassemble(m),
R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
%b1 = block {
- loop [b: %b2, c: %b3]
- # Body block
- %b2 = block {
- if false [t: %b4, f: %b5]
- # True block
- %b4 = block {
- exit_if
+ loop [b: %b2, c: %b3] { # loop_1
+ %b2 = block { # body
+ if false [t: %b4, f: %b5] { # if_1
+ %b4 = block { # true
+ exit_if # if_1
}
-
- # False block
- %b5 = block {
- exit_loop
+ %b5 = block { # false
+ exit_loop # loop_1
}
-
+ }
continue %b3
}
-
- # Continuing block
- %b3 = block {
+ %b3 = block { # continuing
next_iteration %b2
}
-
+ }
ret
}
}
@@ -751,28 +686,22 @@
EXPECT_EQ(Disassemble(m),
R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
%b1 = block {
- loop [b: %b2, c: %b3]
- # Body block
- %b2 = block {
- if true [t: %b4, f: %b5]
- # True block
- %b4 = block {
- exit_if
+ loop [b: %b2, c: %b3] { # loop_1
+ %b2 = block { # body
+ if true [t: %b4, f: %b5] { # if_1
+ %b4 = block { # true
+ exit_if # if_1
}
-
- # False block
- %b5 = block {
- exit_loop
+ %b5 = block { # false
+ exit_loop # loop_1
}
-
+ }
continue %b3
}
-
- # Continuing block
- %b3 = block {
+ %b3 = block { # continuing
next_iteration %b2
}
-
+ }
ret
}
}
@@ -828,18 +757,15 @@
EXPECT_EQ(Disassemble(m),
R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
%b1 = block {
- loop [i: %b2, b: %b3]
- # Initializer block
- %b2 = block {
+ loop [i: %b2, b: %b3] { # loop_1
+ %b2 = block { # initializer
%i:ptr<function, i32, read_write> = var
next_iteration %b3
}
-
- # Body block
- %b3 = block {
- exit_loop
+ %b3 = block { # body
+ exit_loop # loop_1
}
-
+ }
ret
}
}
@@ -864,12 +790,11 @@
EXPECT_EQ(Disassemble(m),
R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
%b1 = block {
- loop [b: %b2]
- # Body block
- %b2 = block {
- exit_loop
+ loop [b: %b2] { # loop_1
+ %b2 = block { # body
+ exit_loop # loop_1
}
-
+ }
ret
}
}
@@ -887,11 +812,11 @@
ASSERT_TRUE(res) << (!res ? res.Failure() : "");
auto m = res.Move();
- auto* flow = FindSingleInstruction<ir::Switch>(m);
+ auto* swtch = FindSingleInstruction<ir::Switch>(m);
ASSERT_EQ(1u, m.functions.Length());
- auto cases = flow->Cases();
+ auto cases = swtch->Cases();
ASSERT_EQ(3u, cases.Length());
ASSERT_EQ(1u, cases[0].selectors.Length());
@@ -910,22 +835,17 @@
EXPECT_EQ(Disassemble(m),
R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
%b1 = block {
- switch 1i [c: (0i, %b2), c: (1i, %b3), c: (default, %b4)]
- # Case block
- %b2 = block {
- exit_switch
+ switch 1i [c: (0i, %b2), c: (1i, %b3), c: (default, %b4)] { # switch_1
+ %b2 = block { # case
+ exit_switch # switch_1
}
-
- # Case block
- %b3 = block {
- exit_switch
+ %b3 = block { # case
+ exit_switch # switch_1
}
-
- # Case block
- %b4 = block {
- exit_switch
+ %b4 = block { # case
+ exit_switch # switch_1
}
-
+ }
ret
}
}
@@ -944,11 +864,11 @@
ASSERT_TRUE(res) << (!res ? res.Failure() : "");
auto m = res.Move();
- auto* flow = FindSingleInstruction<ir::Switch>(m);
+ auto* swtch = FindSingleInstruction<ir::Switch>(m);
ASSERT_EQ(1u, m.functions.Length());
- auto cases = flow->Cases();
+ auto cases = swtch->Cases();
ASSERT_EQ(1u, cases.Length());
ASSERT_EQ(3u, cases[0].selectors.Length());
ASSERT_TRUE(cases[0].selectors[0].val->Value()->Is<constant::Scalar<tint::i32>>());
@@ -964,12 +884,11 @@
EXPECT_EQ(Disassemble(m),
R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
%b1 = block {
- switch 1i [c: (0i 1i default, %b2)]
- # Case block
- %b2 = block {
- exit_switch
+ switch 1i [c: (0i 1i default, %b2)] { # switch_1
+ %b2 = block { # case
+ exit_switch # switch_1
}
-
+ }
ret
}
}
@@ -984,11 +903,11 @@
ASSERT_TRUE(res) << (!res ? res.Failure() : "");
auto m = res.Move();
- auto* flow = FindSingleInstruction<ir::Switch>(m);
+ auto* swtch = FindSingleInstruction<ir::Switch>(m);
ASSERT_EQ(1u, m.functions.Length());
- auto cases = flow->Cases();
+ auto cases = swtch->Cases();
ASSERT_EQ(1u, cases.Length());
ASSERT_EQ(1u, cases[0].selectors.Length());
EXPECT_TRUE(cases[0].selectors[0].IsDefault());
@@ -996,12 +915,11 @@
EXPECT_EQ(Disassemble(m),
R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
%b1 = block {
- switch 1i [c: (default, %b2)]
- # Case block
- %b2 = block {
- exit_switch
+ switch 1i [c: (default, %b2)] { # switch_1
+ %b2 = block { # case
+ exit_switch # switch_1
}
-
+ }
ret
}
}
@@ -1018,11 +936,11 @@
ASSERT_TRUE(res) << (!res ? res.Failure() : "");
auto m = res.Move();
- auto* flow = FindSingleInstruction<ir::Switch>(m);
+ auto* swtch = FindSingleInstruction<ir::Switch>(m);
ASSERT_EQ(1u, m.functions.Length());
- auto cases = flow->Cases();
+ auto cases = swtch->Cases();
ASSERT_EQ(2u, cases.Length());
ASSERT_EQ(1u, cases[0].selectors.Length());
ASSERT_TRUE(cases[0].selectors[0].val->Value()->Is<constant::Scalar<tint::i32>>());
@@ -1037,17 +955,14 @@
EXPECT_EQ(Disassemble(m),
R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
%b1 = block {
- switch 1i [c: (0i, %b2), c: (default, %b3)]
- # Case block
- %b2 = block {
- exit_switch
+ switch 1i [c: (0i, %b2), c: (default, %b3)] { # switch_1
+ %b2 = block { # case
+ exit_switch # switch_1
}
-
- # Case block
- %b3 = block {
- exit_switch
+ %b3 = block { # case
+ exit_switch # switch_1
}
-
+ }
ret
}
}
@@ -1066,11 +981,11 @@
auto m = res.Move();
- auto* flow = FindSingleInstruction<ir::Switch>(m);
+ auto* swtch = FindSingleInstruction<ir::Switch>(m);
ASSERT_EQ(1u, m.functions.Length());
- auto cases = flow->Cases();
+ auto cases = swtch->Cases();
ASSERT_EQ(2u, cases.Length());
ASSERT_EQ(1u, cases[0].selectors.Length());
ASSERT_TRUE(cases[0].selectors[0].val->Value()->Is<constant::Scalar<tint::i32>>());
@@ -1083,17 +998,14 @@
EXPECT_EQ(Disassemble(m),
R"(%test_function = @compute @workgroup_size(1, 1, 1) func():void -> %b1 {
%b1 = block {
- switch 1i [c: (0i, %b2), c: (default, %b3)]
- # Case block
- %b2 = block {
+ switch 1i [c: (0i, %b2), c: (default, %b3)] { # switch_1
+ %b2 = block { # case
ret
}
-
- # Case block
- %b3 = block {
+ %b3 = block { # case
ret
}
-
+ }
ret
}
}
diff --git a/src/tint/ir/from_program_unary_test.cc b/src/tint/ir/from_program_unary_test.cc
index 2a66d1c..976863e 100644
--- a/src/tint/ir/from_program_unary_test.cc
+++ b/src/tint/ir/from_program_unary_test.cc
@@ -103,8 +103,7 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- EXPECT_EQ(Disassemble(m.Get()), R"(# Root block
-%b1 = block {
+ EXPECT_EQ(Disassemble(m.Get()), R"(%b1 = block { # root
%v2:ptr<private, i32, read_write> = var
}
@@ -127,8 +126,7 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- EXPECT_EQ(Disassemble(m.Get()), R"(# Root block
-%b1 = block {
+ EXPECT_EQ(Disassemble(m.Get()), R"(%b1 = block { # root
%v3:ptr<private, i32, read_write> = var
}
diff --git a/src/tint/ir/from_program_var_test.cc b/src/tint/ir/from_program_var_test.cc
index aedb0cb..ce75297 100644
--- a/src/tint/ir/from_program_var_test.cc
+++ b/src/tint/ir/from_program_var_test.cc
@@ -31,8 +31,7 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- EXPECT_EQ(Disassemble(m.Get()), R"(# Root block
-%b1 = block {
+ EXPECT_EQ(Disassemble(m.Get()), R"(%b1 = block { # root
%a:ptr<private, u32, read_write> = var
}
@@ -46,8 +45,7 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- EXPECT_EQ(Disassemble(m.Get()), R"(# Root block
-%b1 = block {
+ EXPECT_EQ(Disassemble(m.Get()), R"(%b1 = block { # root
%a:ptr<private, u32, read_write> = var, 2u
}
@@ -61,8 +59,7 @@
auto m = Build();
ASSERT_TRUE(m) << (!m ? m.Failure() : "");
- EXPECT_EQ(Disassemble(m.Get()), R"(# Root block
-%b1 = block {
+ EXPECT_EQ(Disassemble(m.Get()), R"(%b1 = block { # root
%a:ptr<storage, u32, read> = var @binding_point(2, 3)
}
diff --git a/src/tint/ir/if.h b/src/tint/ir/if.h
index 6b87463..216b54b 100644
--- a/src/tint/ir/if.h
+++ b/src/tint/ir/if.h
@@ -58,10 +58,10 @@
/// @returns the if condition
Value* Condition() { return operands_[kConditionOperandOffset]; }
- /// @returns the true branch block
+ /// @returns the true block
ir::Block* True() { return true_; }
- /// @returns the false branch block
+ /// @returns the false block
ir::Block* False() { return false_; }
private:
diff --git a/src/tint/ir/loop.cc b/src/tint/ir/loop.cc
index 4b931b4..a09699d 100644
--- a/src/tint/ir/loop.cc
+++ b/src/tint/ir/loop.cc
@@ -54,7 +54,7 @@
}
bool Loop::HasInitializer() {
- return initializer_->HasBranchTarget();
+ return initializer_->HasTerminator();
}
} // namespace tint::ir
diff --git a/src/tint/ir/loop.h b/src/tint/ir/loop.h
index f5e2ea4..1a00ac3 100644
--- a/src/tint/ir/loop.h
+++ b/src/tint/ir/loop.h
@@ -45,7 +45,7 @@
/// ┃ ▼ ┃
/// ┃ ┌─────────────────┐ ┃ BreakIf(false)
/// ExitLoop ┃ │ Continuing │━━┛
-/// │ (optional) │
+/// ┃ │ (optional) │
/// ┃ └─────────────────┘
/// ┃ ┃
/// ┃ ┃ BreakIf(true)
diff --git a/src/tint/ir/multi_in_block.cc b/src/tint/ir/multi_in_block.cc
index 94e7f59..53286ba 100644
--- a/src/tint/ir/multi_in_block.cc
+++ b/src/tint/ir/multi_in_block.cc
@@ -32,7 +32,7 @@
params_ = std::move(params);
}
-void MultiInBlock::AddInboundSiblingBranch(ir::Branch* node) {
+void MultiInBlock::AddInboundSiblingBranch(ir::Terminator* node) {
TINT_ASSERT(IR, node != nullptr);
if (node) {
diff --git a/src/tint/ir/multi_in_block.h b/src/tint/ir/multi_in_block.h
index e0d807f..c70bb9c 100644
--- a/src/tint/ir/multi_in_block.h
+++ b/src/tint/ir/multi_in_block.h
@@ -47,17 +47,17 @@
const utils::Vector<BlockParam*, 2>& Params() { return params_; }
/// @returns branches made to this block by sibling blocks
- const utils::VectorRef<ir::Branch*> InboundSiblingBranches() {
+ const utils::VectorRef<ir::Terminator*> InboundSiblingBranches() {
return inbound_sibling_branches_;
}
/// Adds the given branch to the list of branches made to this block by sibling blocks
/// @param branch the branch to add
- void AddInboundSiblingBranch(ir::Branch* branch);
+ void AddInboundSiblingBranch(ir::Terminator* branch);
private:
utils::Vector<BlockParam*, 2> params_;
- utils::Vector<ir::Branch*, 2> inbound_sibling_branches_;
+ utils::Vector<ir::Terminator*, 2> inbound_sibling_branches_;
};
} // namespace tint::ir
diff --git a/src/tint/ir/next_iteration.h b/src/tint/ir/next_iteration.h
index 6b01634..4783d53 100644
--- a/src/tint/ir/next_iteration.h
+++ b/src/tint/ir/next_iteration.h
@@ -15,7 +15,7 @@
#ifndef SRC_TINT_IR_NEXT_ITERATION_H_
#define SRC_TINT_IR_NEXT_ITERATION_H_
-#include "src/tint/ir/branch.h"
+#include "src/tint/ir/terminator.h"
#include "src/tint/utils/castable.h"
// Forward declarations
@@ -26,14 +26,14 @@
namespace tint::ir {
/// A next iteration instruction.
-class NextIteration : public utils::Castable<NextIteration, Branch> {
+class NextIteration : public utils::Castable<NextIteration, Terminator> {
public:
/// The base offset in Operands() for the args
static constexpr size_t kArgsOperandOffset = 0;
/// Constructor
/// @param loop the loop being iterated
- /// @param args the branch arguments
+ /// @param args the arguments for the MultiInBlock
explicit NextIteration(ir::Loop* loop, utils::VectorRef<Value*> args = utils::Empty);
~NextIteration() override;
diff --git a/src/tint/ir/operand_instruction.h b/src/tint/ir/operand_instruction.h
index 82957c0..040bfc9 100644
--- a/src/tint/ir/operand_instruction.h
+++ b/src/tint/ir/operand_instruction.h
@@ -57,13 +57,18 @@
ClearOperands();
operands_ = std::move(operands);
for (size_t i = 0; i < operands_.Length(); i++) {
- operands_[i]->AddUsage({this, static_cast<uint32_t>(i)});
+ if (operands_[i]) {
+ operands_[i]->AddUsage({this, static_cast<uint32_t>(i)});
+ }
}
}
/// Removes all operands from the instruction
void ClearOperands() {
for (uint32_t i = 0; i < operands_.Length(); i++) {
+ if (!operands_[i]) {
+ continue;
+ }
operands_[i]->RemoveUsage({this, i});
}
operands_.Clear();
diff --git a/src/tint/ir/operand_instruction_test.cc b/src/tint/ir/operand_instruction_test.cc
index 9d05a07..03c0435 100644
--- a/src/tint/ir/operand_instruction_test.cc
+++ b/src/tint/ir/operand_instruction_test.cc
@@ -18,7 +18,8 @@
namespace tint::ir {
namespace {
-using namespace tint::number_suffixes; // NOLINT
+using namespace tint::number_suffixes; // NOLINT
+using namespace tint::builtin::fluent_types; // NOLINT
using IR_OperandInstructionTest = IRTestHelper;
@@ -41,5 +42,24 @@
EXPECT_FALSE(inst->Result()->Alive());
}
+TEST_F(IR_OperandInstructionTest, ClearOperands_WithNullOperand) {
+ auto* block = b.Block();
+ // The var initializer is a nullptr
+ auto* inst = b.Var(ty.ptr<private_, f32>());
+ block->Append(inst);
+
+ inst->Destroy();
+ EXPECT_EQ(inst->Block(), nullptr);
+ EXPECT_FALSE(inst->Result()->Alive());
+}
+
+TEST_F(IR_OperandInstructionTest, SetOperands_WithNullOperand) {
+ auto* inst = b.Var(ty.ptr<private_, f32>());
+ utils::Vector<Value*, 1> ops;
+ ops.Push(nullptr);
+
+ inst->SetOperands(ops);
+}
+
} // namespace
} // namespace tint::ir
diff --git a/src/tint/ir/return.h b/src/tint/ir/return.h
index 403a215..14f1863 100644
--- a/src/tint/ir/return.h
+++ b/src/tint/ir/return.h
@@ -15,7 +15,7 @@
#ifndef SRC_TINT_IR_RETURN_H_
#define SRC_TINT_IR_RETURN_H_
-#include "src/tint/ir/branch.h"
+#include "src/tint/ir/terminator.h"
#include "src/tint/utils/castable.h"
// Forward declarations
@@ -26,7 +26,7 @@
namespace tint::ir {
/// A return instruction.
-class Return : public utils::Castable<Return, Branch> {
+class Return : public utils::Castable<Return, Terminator> {
public:
/// The offset in Operands() for the function being returned
static constexpr size_t kFunctionOperandOffset = 0;
@@ -57,7 +57,7 @@
/// @param val the new return value
void SetValue(ir::Value* val) { SetOperand(kArgOperandOffset, val); }
- /// @returns the branch arguments
+ /// @returns the return arguments
utils::Slice<ir::Value* const> Args() override {
return operands_.Slice().Offset(kArgOperandOffset);
}
diff --git a/src/tint/ir/branch.cc b/src/tint/ir/terminator.cc
similarity index 84%
rename from src/tint/ir/branch.cc
rename to src/tint/ir/terminator.cc
index a7ea495..ce76977 100644
--- a/src/tint/ir/branch.cc
+++ b/src/tint/ir/terminator.cc
@@ -12,14 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "src/tint/ir/branch.h"
+#include "src/tint/ir/terminator.h"
#include <utility>
-TINT_INSTANTIATE_TYPEINFO(tint::ir::Branch);
+TINT_INSTANTIATE_TYPEINFO(tint::ir::Terminator);
namespace tint::ir {
-Branch::~Branch() = default;
+Terminator::~Terminator() = default;
} // namespace tint::ir
diff --git a/src/tint/ir/branch.h b/src/tint/ir/terminator.h
similarity index 74%
rename from src/tint/ir/branch.h
rename to src/tint/ir/terminator.h
index 1069529..5698100 100644
--- a/src/tint/ir/branch.h
+++ b/src/tint/ir/terminator.h
@@ -12,8 +12,8 @@
// 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_
+#ifndef SRC_TINT_IR_TERMINATOR_H_
+#define SRC_TINT_IR_TERMINATOR_H_
#include "src/tint/ir/operand_instruction.h"
#include "src/tint/ir/value.h"
@@ -26,15 +26,15 @@
namespace tint::ir {
-/// A branch instruction.
-class Branch : public utils::Castable<Branch, OperandInstruction<1, 0>> {
+/// The base class of all instructions that terminate a block.
+class Terminator : public utils::Castable<Terminator, OperandInstruction<1, 0>> {
public:
- ~Branch() override;
+ ~Terminator() override;
- /// @returns the branch arguments
+ /// @returns the terminator arguments
virtual utils::Slice<Value* const> Args() { return operands_.Slice(); }
};
} // namespace tint::ir
-#endif // SRC_TINT_IR_BRANCH_H_
+#endif // SRC_TINT_IR_TERMINATOR_H_
diff --git a/src/tint/ir/to_program.cc b/src/tint/ir/to_program.cc
index a286702..495f608 100644
--- a/src/tint/ir/to_program.cc
+++ b/src/tint/ir/to_program.cc
@@ -15,8 +15,10 @@
#include "src/tint/ir/to_program.h"
#include <string>
+#include <tuple>
#include <utility>
+#include "src/tint/ir/access.h"
#include "src/tint/ir/binary.h"
#include "src/tint/ir/block.h"
#include "src/tint/ir/call.h"
@@ -46,6 +48,7 @@
#include "src/tint/type/texture.h"
#include "src/tint/utils/hashmap.h"
#include "src/tint/utils/predicates.h"
+#include "src/tint/utils/reverse.h"
#include "src/tint/utils/scoped_assignment.h"
#include "src/tint/utils/transform.h"
#include "src/tint/utils/vector.h"
@@ -65,6 +68,10 @@
namespace {
+/// Empty struct used as a sentinel value to indicate that an ast::Value has been consumed by its
+/// single place of usage. Attempting to use this value a second time should result in an ICE.
+struct ConsumedValue {};
+
class State {
public:
explicit State(Module& m) : mod(m) {}
@@ -85,15 +92,25 @@
/// The target ProgramBuilder
ProgramBuilder b;
- /// A hashmap of value to symbol used in the emitted AST
- utils::Hashmap<Value*, Symbol, 32> value_names_;
+ using ValueBinding = std::variant<Symbol, const ast::Expression*, ConsumedValue>;
+
+ /// A hashmap of value to one of:
+ /// * Symbol - Name of 'let' (non-inlinable value), 'var' or parameter.
+ /// * ast::Expression* - single use, inlined expression.
+ /// * ConsumedValue - a special value used to indicate that the value has already been
+ /// consumed.
+ utils::Hashmap<Value*, ValueBinding, 32> bindings_;
/// The nesting depth of the currently generated AST
- /// 0 is module scope
- /// 1 is root-level function scope
+ /// 0 is module scope
+ /// 1 is root-level function scope
/// 2+ is within control flow
uint32_t nesting_depth_ = 0;
+ using StatementList = utils::Vector<const ast::Statement*,
+ decltype(ast::BlockStatement::statements)::static_length>;
+ StatementList* statements_ = nullptr;
+
/// The current switch case block
ir::Block* current_switch_case_ = nullptr;
@@ -103,12 +120,12 @@
// TODO(crbug.com/tint/1915): Properly implement this when we've fleshed out Function
static constexpr size_t N = decltype(ast::Function::params)::static_length;
auto params = utils::Transform<N>(fn->Params(), [&](FunctionParam* param) {
- auto name = AssignNameTo(param);
+ auto name = BindName(param);
auto ty = Type(param->Type());
return b.Param(name, ty);
});
- auto name = AssignNameTo(fn);
+ auto name = BindName(fn);
auto ret_ty = Type(fn->ReturnType());
auto* body = Block(fn->StartTarget());
utils::Vector<const ast::Attribute*, 1> attrs{};
@@ -118,175 +135,129 @@
}
const ast::BlockStatement* Block(ir::Block* block) {
- static constexpr size_t N = decltype(ast::BlockStatement::statements)::static_length;
- utils::Vector<const ast::Statement*, N> stmts;
-
// TODO(crbug.com/tint/1902): Handle block arguments.
+ return b.Block(Statements(block));
+ }
- for (auto* inst : *block) {
- if (auto stmt = Stmt(inst)) {
- stmts.Push(stmt);
+ StatementList Statements(ir::Block* block) {
+ StatementList stmts;
+ if (block) {
+ TINT_SCOPED_ASSIGNMENT(statements_, &stmts);
+ for (auto* inst : *block) {
+ Instruction(inst);
}
}
-
- return b.Block(std::move(stmts));
+ return stmts;
}
- ////////////////////////////////////////////////////////////////////////////////////////////////
- // Statements
- //
- // Statement methods may return nullptr, in the case of instructions that do not map to an AST
- // statement, or in the case of an error. These should simply be ignored.
- ////////////////////////////////////////////////////////////////////////////////////////////////
+ void Append(const ast::Statement* inst) { statements_->Push(inst); }
- /// @param inst the ir::Instruction
- /// @return an ast::Statement from @p inst, or nullptr if there was an error
- const ast::Statement* Stmt(ir::Instruction* inst) {
- return tint::Switch(
- inst, //
- [&](ir::Call* i) { return CallStmt(i); }, //
- [&](ir::Var* i) { return Var(i); }, //
- [&](ir::Load*) { return nullptr; }, //
- [&](ir::Store* i) { return Store(i); }, //
- [&](ir::If* if_) { return If(if_); }, //
- [&](ir::Switch* switch_) { return Switch(switch_); }, //
- [&](ir::Return* ret) { return Return(ret); }, //
- [&](ir::ExitSwitch* e) { return ExitSwitch(e); }, //
- [&](ir::ExitIf*) { return nullptr; }, //
- [&](ir::Instruction* i) { return ValueStmt(i->Result()); }, //
- [&](Default) {
- UNHANDLED_CASE(inst);
- return nullptr;
- });
+ void Instruction(ir::Instruction* inst) {
+ tint::Switch(
+ inst, //
+ [&](ir::Binary* u) { Binary(u); }, //
+ [&](ir::Call* i) { Call(i); }, //
+ [&](ir::ExitIf*) {}, //
+ [&](ir::ExitSwitch* i) { ExitSwitch(i); }, //
+ [&](ir::If* i) { If(i); }, //
+ [&](ir::Load* l) { Load(l); }, //
+ [&](ir::Return* i) { Return(i); }, //
+ [&](ir::Store* i) { Store(i); }, //
+ [&](ir::Switch* i) { Switch(i); }, //
+ [&](ir::Unary* u) { Unary(u); }, //
+ [&](ir::Var* i) { Var(i); }, //
+ [&](Default) { UNHANDLED_CASE(inst); });
}
- /// @param i the ir::If
- /// @return an ast::IfStatement from @p i, or nullptr if there was an error
- const ast::IfStatement* If(ir::If* i) {
+ void If(ir::If* if_) {
SCOPED_NESTING();
- auto* cond = Expr(i->Condition());
- auto* t = Block(i->True());
- if (TINT_UNLIKELY(!t)) {
- return nullptr;
+
+ auto true_stmts = Statements(if_->True());
+ auto false_stmts = Statements(if_->False());
+ if (IsShortCircuit(if_, true_stmts, false_stmts)) {
+ return;
}
- if (auto* false_blk = i->False(); false_blk && !false_blk->IsEmpty()) {
- bool maybe_elseif = (false_blk->Length() == 1) ||
- (false_blk->Length() == 2 && false_blk->Back()->Is<ir::Branch>());
- if (maybe_elseif) {
- if (auto* else_if = false_blk->Front()->As<ir::If>()) {
- auto* f = If(else_if);
- if (!f) {
- return nullptr;
- }
- return b.If(cond, t, b.Else(f));
+ auto* cond = Expr(if_->Condition());
+ auto* true_block = b.Block(std::move(true_stmts));
+
+ switch (false_stmts.Length()) {
+ case 0:
+ Append(b.If(cond, true_block));
+ return;
+ case 1:
+ if (auto* else_if = false_stmts.Front()->As<ast::IfStatement>()) {
+ Append(b.If(cond, true_block, b.Else(else_if)));
+ return;
}
- }
-
- auto* f = Block(i->False());
- if (!f) {
- return nullptr;
- }
- return b.If(cond, t, b.Else(f));
+ break;
}
- return b.If(cond, t);
+ auto* false_block = b.Block(std::move(false_stmts));
+ Append(b.If(cond, true_block, b.Else(false_block)));
}
- /// @param s the ir::Switch
- /// @return an ast::SwitchStatement from @p s, or nullptr if there was an error
- const ast::SwitchStatement* Switch(ir::Switch* s) {
+ void Switch(ir::Switch* s) {
SCOPED_NESTING();
auto* cond = Expr(s->Condition());
- if (!cond) {
- return nullptr;
- }
- auto cases =
- utils::Transform(s->Cases(), //
- [&](ir::Switch::Case c) -> const tint::ast::CaseStatement* {
- SCOPED_NESTING();
+ auto cases = utils::Transform(
+ s->Cases(), //
+ [&](ir::Switch::Case c) -> const tint::ast::CaseStatement* {
+ SCOPED_NESTING();
- const ast::BlockStatement* body = nullptr;
- {
- TINT_SCOPED_ASSIGNMENT(current_switch_case_, c.Block());
- body = Block(c.Block());
- }
- if (!body) {
- return nullptr;
- }
+ const ast::BlockStatement* body = nullptr;
+ {
+ TINT_SCOPED_ASSIGNMENT(current_switch_case_, c.Block());
+ body = Block(c.Block());
+ }
- auto selectors = utils::Transform(
- c.selectors, //
- [&](ir::Switch::CaseSelector cs) -> const ast::CaseSelector* {
- if (cs.IsDefault()) {
- return b.DefaultCaseSelector();
- }
- auto* expr = Expr(cs.val);
- if (!expr) {
- return nullptr;
- }
- return b.CaseSelector(expr);
- });
- if (selectors.Any(utils::IsNull)) {
- return nullptr;
- }
+ auto selectors = utils::Transform(c.selectors, //
+ [&](ir::Switch::CaseSelector cs) {
+ return cs.IsDefault()
+ ? b.DefaultCaseSelector()
+ : b.CaseSelector(Expr(cs.val));
+ });
+ return b.Case(std::move(selectors), body);
+ });
- return b.Case(std::move(selectors), body);
- });
- if (cases.Any(utils::IsNull)) {
- return nullptr;
- }
-
- return b.Switch(cond, std::move(cases));
+ Append(b.Switch(cond, std::move(cases)));
}
- const ast::BreakStatement* ExitSwitch(const ir::ExitSwitch* e) {
- if (current_switch_case_ && current_switch_case_->Branch() == e) {
- return nullptr; // No need to emit
+ void ExitSwitch(const ir::ExitSwitch* e) {
+ if (current_switch_case_ && current_switch_case_->Terminator() == e) {
+ return; // No need to emit
}
- return b.Break();
+ Append(b.Break());
}
- /// @param ret the ir::Return
- /// @return an ast::ReturnStatement from @p ret, or nullptr if there was an error
- const ast::ReturnStatement* Return(ir::Return* ret) {
+ void Return(ir::Return* ret) {
if (ret->Args().IsEmpty()) {
// Return has no arguments.
// If this block is nested withing some control flow, then we must
// emit a 'return' statement, otherwise we've just naturally reached
// the end of the function where the 'return' is redundant.
if (nesting_depth_ > 1) {
- return b.Return();
+ Append(b.Return());
}
- return nullptr;
+ return;
}
// Return has arguments - this is the return value.
if (ret->Args().Length() != 1) {
TINT_ICE(IR, b.Diagnostics())
<< "expected 1 value for return, got " << ret->Args().Length();
- return b.Return();
+ return;
}
- auto* val = Expr(ret->Args().Front());
- if (TINT_UNLIKELY(!val)) {
- return b.Return();
- }
-
- return b.Return(val);
+ Append(b.Return(Expr(ret->Args().Front())));
}
- /// @param call the ir::Call
- /// @return an ast::CallStatement from @p call, or nullptr if there was an error
- const ast::CallStatement* CallStmt(ir::Call* call) { return b.CallStmt(Call(call)); }
-
- /// @param var the ir::Var
- /// @return an ast::VariableDeclStatement from @p var
- const ast::VariableDeclStatement* Var(ir::Var* var) {
- Symbol name = AssignNameTo(var);
- auto* ptr = var->Result()->Type()->As<type::Pointer>();
+ void Var(ir::Var* var) {
+ auto* val = var->Result();
+ Symbol name = BindName(val);
+ auto* ptr = As<type::Pointer>(val->Type());
auto ty = Type(ptr->StoreType());
const ast::Expression* init = nullptr;
if (var->Initializer()) {
@@ -294,104 +265,163 @@
}
switch (ptr->AddressSpace()) {
case builtin::AddressSpace::kFunction:
- return b.Decl(b.Var(name, ty, init));
+ Append(b.Decl(b.Var(name, ty, init)));
+ return;
case builtin::AddressSpace::kStorage:
- return b.Decl(b.Var(name, ty, init, ptr->Access(), ptr->AddressSpace()));
+ Append(b.Decl(b.Var(name, ty, init, ptr->Access(), ptr->AddressSpace())));
+ return;
default:
- return b.Decl(b.Var(name, ty, init, ptr->AddressSpace()));
+ Append(b.Decl(b.Var(name, ty, init, ptr->AddressSpace())));
+ return;
}
}
- /// @param store the ir::Store
- /// @return an ast::AssignmentStatement from @p call
- const ast::AssignmentStatement* Store(ir::Store* store) {
- auto* expr = Expr(store->From());
- return b.Assign(AssignNameTo(store->To()), expr);
+ void Store(ir::Store* store) {
+ auto* dst = Expr(store->To());
+ auto* src = Expr(store->From());
+ Append(b.Assign(dst, src));
}
- /// @param val the ir::Value
- /// @return an ast::Statement from @p val, or nullptr if the value does not produce a statement.
- const ast::Statement* ValueStmt(ir::Value* val) {
- // As we're visiting this value's declaration it shouldn't already have a name reserved.
- TINT_ASSERT(IR, !value_names_.Contains(val));
-
- // Determine whether the value should be placed into a let, or inlined in its single place
- // of usage. Currently a value is inlined if it has a single usage and is unnamed.
- // TODO(crbug.com/tint/1902): This logic needs to check that the sequence of side -
- // effecting expressions is not changed by inlining the expression. This needs fixing.
- bool create_let = val->Usages().Count() > 1 || mod.NameOf(val).IsValid();
- if (create_let) {
- auto* init = Expr(val); // Must come before giving the value a name
- auto name = AssignNameTo(val);
- return b.Decl(b.Let(name, init));
- }
- return nullptr; // Value will be inlined at its place of usage.
- }
-
- ////////////////////////////////////////////////////////////////////////////////////////////////
- // Expressions
- //
- // The the case of an error:
- // * The expression generating methods must return a non-null ast expression pointer, which may
- // not be semantically legal, but is enough to populate the AST.
- // * A diagnostic error must be added to the ast::ProgramBuilder.
- // This prevents littering the ToProgram logic with expensive error checking code.
- ////////////////////////////////////////////////////////////////////////////////////////////////
-
- /// @param val the value
- /// @returns the ast::Expression from the values source instruction
- const ast::Expression* Expr(ir::Value* val) {
- if (auto name = value_names_.Get(val)) {
- return b.Expr(name.value());
- }
-
- return tint::Switch(
- val, //
- [&](ir::Constant* c) { return ConstExpr(c); }, //
- [&](ir::InstructionResult* r) { return Expr(r->Source()); }, //
- [&](Default) -> const ast::Expression* {
- UNHANDLED_CASE(val);
- return b.Expr("<error>");
- });
- }
-
- /// @param val the ir::Expression
- /// @return an ast::Expression from @p val.
- /// @note May be a semantically-invalid placeholder expression on error.
- const ast::Expression* Expr(ir::Instruction* val) {
- if (auto name = value_names_.Get(val->Result())) {
- return b.Expr(name.value());
- }
-
- return tint::Switch(
- val, //
- [&](ir::Load* l) { return LoadExpr(l); }, //
- [&](ir::Unary* u) { return UnaryExpr(u); }, //
- [&](ir::Binary* u) { return BinaryExpr(u); }, //
- [&](Default) {
- UNHANDLED_CASE(val);
- return b.Expr("<error>");
- });
- }
-
- /// @param call the ir::Call
- /// @return an ast::CallExpression from @p call.
- /// @note May be a semantically-invalid placeholder expression on error.
- const ast::CallExpression* Call(ir::Call* call) {
+ void Call(ir::Call* call) {
auto args = utils::Transform<2>(call->Args(), [&](ir::Value* arg) { return Expr(arg); });
- return tint::Switch(
+ tint::Switch(
call, //
- [&](ir::UserCall* c) { return b.Call(AssignNameTo(c->Func()), std::move(args)); },
- [&](Default) {
- UNHANDLED_CASE(call);
- return b.Call("<error>");
+ [&](ir::UserCall* c) {
+ auto* expr = b.Call(BindName(c->Func()), std::move(args));
+ if (!call->HasResults() || call->Result()->Usages().IsEmpty()) {
+ Append(b.CallStmt(expr));
+ return;
+ }
+ Bind(c->Result(), expr);
+ },
+ [&](Default) { UNHANDLED_CASE(call); });
+ }
+
+ void Load(ir::Load* l) { Bind(l->Result(), Expr(l->From())); }
+
+ void Unary(ir::Unary* u) {
+ const ast::Expression* expr = nullptr;
+ switch (u->Kind()) {
+ case ir::Unary::Kind::kComplement:
+ expr = b.Complement(Expr(u->Val()));
+ break;
+ case ir::Unary::Kind::kNegation:
+ expr = b.Negation(Expr(u->Val()));
+ break;
+ }
+ Bind(u->Result(), expr);
+ }
+
+ void Binary(ir::Binary* e) {
+ if (e->Kind() == ir::Binary::Kind::kEqual) {
+ auto* rhs = e->RHS()->As<ir::Constant>();
+ if (rhs && rhs->Type()->Is<type::Bool>() && rhs->Value()->ValueAs<bool>() == false) {
+ // expr == false
+ Bind(e->Result(), b.Not(Expr(e->LHS())));
+ return;
+ }
+ }
+ auto* lhs = Expr(e->LHS());
+ auto* rhs = Expr(e->RHS());
+ const ast::Expression* expr = nullptr;
+ switch (e->Kind()) {
+ case ir::Binary::Kind::kAdd:
+ expr = b.Add(lhs, rhs);
+ break;
+ case ir::Binary::Kind::kSubtract:
+ expr = b.Sub(lhs, rhs);
+ break;
+ case ir::Binary::Kind::kMultiply:
+ expr = b.Mul(lhs, rhs);
+ break;
+ case ir::Binary::Kind::kDivide:
+ expr = b.Div(lhs, rhs);
+ break;
+ case ir::Binary::Kind::kModulo:
+ expr = b.Mod(lhs, rhs);
+ break;
+ case ir::Binary::Kind::kAnd:
+ expr = b.And(lhs, rhs);
+ break;
+ case ir::Binary::Kind::kOr:
+ expr = b.Or(lhs, rhs);
+ break;
+ case ir::Binary::Kind::kXor:
+ expr = b.Xor(lhs, rhs);
+ break;
+ case ir::Binary::Kind::kEqual:
+ expr = b.Equal(lhs, rhs);
+ break;
+ case ir::Binary::Kind::kNotEqual:
+ expr = b.NotEqual(lhs, rhs);
+ break;
+ case ir::Binary::Kind::kLessThan:
+ expr = b.LessThan(lhs, rhs);
+ break;
+ case ir::Binary::Kind::kGreaterThan:
+ expr = b.GreaterThan(lhs, rhs);
+ break;
+ case ir::Binary::Kind::kLessThanEqual:
+ expr = b.LessThanEqual(lhs, rhs);
+ break;
+ case ir::Binary::Kind::kGreaterThanEqual:
+ expr = b.GreaterThanEqual(lhs, rhs);
+ break;
+ case ir::Binary::Kind::kShiftLeft:
+ expr = b.Shl(lhs, rhs);
+ break;
+ case ir::Binary::Kind::kShiftRight:
+ expr = b.Shr(lhs, rhs);
+ break;
+ }
+ Bind(e->Result(), expr);
+ }
+
+ TINT_BEGIN_DISABLE_WARNING(UNREACHABLE_CODE);
+
+ const ast::Expression* Expr(ir::Value* value) {
+ return tint::Switch(
+ value, //
+ [&](ir::Constant* c) { return Constant(c); }, //
+ [&](Default) -> const ast::Expression* {
+ auto lookup = bindings_.Find(value);
+ if (TINT_UNLIKELY(!lookup)) {
+ TINT_ICE(IR, b.Diagnostics())
+ << "Expr(" << (value ? value->TypeInfo().name : "null")
+ << ") value has no expression";
+ return b.Expr("<error>");
+ }
+ return std::visit(
+ [&](auto&& got) -> const ast::Expression* {
+ using T = std::decay_t<decltype(got)>;
+
+ if constexpr (std::is_same_v<T, Symbol>) {
+ return b.Expr(got); // var, let or parameter.
+ }
+
+ if constexpr (std::is_same_v<T, const ast::Expression*>) {
+ // Single use (inlined) expression.
+ // Mark the bindings_ map entry as consumed.
+ *lookup = ConsumedValue{};
+ return got;
+ }
+
+ if constexpr (std::is_same_v<T, ConsumedValue>) {
+ TINT_ICE(IR, b.Diagnostics()) << "Expr(" << value->TypeInfo().name
+ << ") called twice on the same value";
+ } else {
+ TINT_ICE(IR, b.Diagnostics())
+ << "Expr(" << value->TypeInfo().name << ") has unhandled value";
+ }
+ return b.Expr("<error>");
+ },
+ *lookup);
});
}
- /// @param c the ir::Constant
- /// @return an ast::Expression from @p c.
- /// @note May be a semantically-invalid placeholder expression on error.
- const ast::Expression* ConstExpr(ir::Constant* c) {
+ TINT_END_DISABLE_WARNING(UNREACHABLE_CODE);
+
+ const ast::Expression* Constant(ir::Constant* c) {
return tint::Switch(
c->Type(), //
[&](const type::I32*) { return b.Expr(c->Value()->ValueAs<i32>()); },
@@ -405,74 +435,6 @@
});
}
- /// @param l the ir::Load
- /// @return an ast::Expression from @p l.
- /// @note May be a semantically-invalid placeholder expression on error.
- const ast::Expression* LoadExpr(ir::Load* l) { return Expr(l->From()); }
-
- /// @param u the ir::Unary
- /// @return an ast::UnaryOpExpression from @p u.
- /// @note May be a semantically-invalid placeholder expression on error.
- const ast::Expression* UnaryExpr(ir::Unary* u) {
- switch (u->Kind()) {
- case ir::Unary::Kind::kComplement:
- return b.Complement(Expr(u->Val()));
- case ir::Unary::Kind::kNegation:
- return b.Negation(Expr(u->Val()));
- }
- return b.Expr("<error>");
- }
-
- /// @param e the ir::Binary
- /// @return an ast::BinaryOpExpression from @p e.
- /// @note May be a semantically-invalid placeholder expression on error.
- const ast::Expression* BinaryExpr(ir::Binary* e) {
- if (e->Kind() == ir::Binary::Kind::kEqual) {
- auto* rhs = e->RHS()->As<ir::Constant>();
- if (rhs && rhs->Type()->Is<type::Bool>() && rhs->Value()->ValueAs<bool>() == false) {
- // expr == false
- return b.Not(Expr(e->LHS()));
- }
- }
- auto* lhs = Expr(e->LHS());
- auto* rhs = Expr(e->RHS());
- switch (e->Kind()) {
- case ir::Binary::Kind::kAdd:
- return b.Add(lhs, rhs);
- case ir::Binary::Kind::kSubtract:
- return b.Sub(lhs, rhs);
- case ir::Binary::Kind::kMultiply:
- return b.Mul(lhs, rhs);
- case ir::Binary::Kind::kDivide:
- return b.Div(lhs, rhs);
- case ir::Binary::Kind::kModulo:
- return b.Mod(lhs, rhs);
- case ir::Binary::Kind::kAnd:
- return b.And(lhs, rhs);
- case ir::Binary::Kind::kOr:
- return b.Or(lhs, rhs);
- case ir::Binary::Kind::kXor:
- return b.Xor(lhs, rhs);
- case ir::Binary::Kind::kEqual:
- return b.Equal(lhs, rhs);
- case ir::Binary::Kind::kNotEqual:
- return b.NotEqual(lhs, rhs);
- case ir::Binary::Kind::kLessThan:
- return b.LessThan(lhs, rhs);
- case ir::Binary::Kind::kGreaterThan:
- return b.GreaterThan(lhs, rhs);
- case ir::Binary::Kind::kLessThanEqual:
- return b.LessThanEqual(lhs, rhs);
- case ir::Binary::Kind::kGreaterThanEqual:
- return b.GreaterThanEqual(lhs, rhs);
- case ir::Binary::Kind::kShiftLeft:
- return b.Shl(lhs, rhs);
- case ir::Binary::Kind::kShiftRight:
- return b.Shr(lhs, rhs);
- }
- return b.Expr("<error>");
- }
-
////////////////////////////////////////////////////////////////////////////////////////////////
// Types
//
@@ -563,25 +525,124 @@
}
////////////////////////////////////////////////////////////////////////////////////////////////
- // Helpers
+ // Bindings
////////////////////////////////////////////////////////////////////////////////////////////////
- /// Creates and returns a new, unique name for the instructions result value, or returns the
- /// previously created name. Must not be called with a multi-result instruction.
- /// @return the instruction values name
- Symbol AssignNameTo(Instruction* inst) { return AssignNameTo(inst->Result()); }
-
/// Creates and returns a new, unique name for the given value, or returns the previously
/// created name.
/// @return the value's name
- Symbol AssignNameTo(Value* value) {
+ Symbol BindName(Value* value, std::string_view suggested = {}) {
TINT_ASSERT(IR, value);
- return value_names_.GetOrCreate(value, [&] {
- if (auto sym = mod.NameOf(value)) {
- return b.Symbols().New(sym.Name());
+ auto& existing = bindings_.GetOrCreate(value, [&] {
+ if (!suggested.empty()) {
+ return b.Symbols().New(suggested);
}
- return b.Symbols().New("v" + std::to_string(value_names_.Count()));
+ if (auto sym = mod.NameOf(value)) {
+ return b.Symbols().New(sym.NameView());
+ }
+ return b.Symbols().New("v");
});
+ if (auto* name = std::get_if<Symbol>(&existing); TINT_LIKELY(name)) {
+ return *name;
+ }
+
+ TINT_ICE(IR, b.Diagnostics()) << "BindName(" << value->TypeInfo().name
+ << ") called on value that has non-name binding";
+ return {};
+ }
+
+ template <typename T>
+ void Bind(ir::Value* value, const T* expr) {
+ TINT_ASSERT(IR, value);
+ if (CanInline(value)) {
+ // Value will be inlined at its place of usage.
+ bool added = bindings_.Add(value, expr);
+ if (TINT_UNLIKELY(!added)) {
+ TINT_ICE(IR, b.Diagnostics())
+ << "Bind(" << value->TypeInfo().name << ") called twice for same node";
+ }
+ } else {
+ Append(b.Decl(b.Let(BindName(value), expr)));
+ }
+ }
+
+ /// @returns true if the if the value can be inlined into its single place
+ /// of usage. Currently a value is inlined if it has a single usage and is unnamed.
+ /// TODO(crbug.com/tint/1902): This logic needs to check that the sequence of side-effecting
+ /// expressions is not changed by inlining the expression. This needs fixing.
+ bool CanInline(Value* val) { return val->Usages().Count() == 1 && !mod.NameOf(val).IsValid(); }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ // Helpers
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ bool IsShortCircuit(ir::If* i,
+ const StatementList& true_stmts,
+ const StatementList& false_stmts) {
+ if (!i->HasResults()) {
+ return false;
+ }
+ auto* result = i->Result();
+ if (!result->Type()->Is<type::Bool>()) {
+ return false; // Wrong result type
+ }
+ if (i->Exits().Count() != 2) {
+ return false; // Doesn't have two exits
+ }
+ if (!true_stmts.IsEmpty() || !false_stmts.IsEmpty()) {
+ return false; // True or False blocks contain statements
+ }
+
+ auto* cond = i->Condition();
+ auto* true_val = i->True()->Back()->Operands().Front();
+ auto* false_val = i->False()->Back()->Operands().Front();
+ if (IsConstant(false_val, false)) {
+ // %res = if %cond {
+ // block { # true
+ // exit_if %true_val;
+ // }
+ // block { # false
+ // exit_if false;
+ // }
+ // }
+ //
+ // transform into:
+ //
+ // res = cond && true_val;
+ //
+ auto* lhs = Expr(cond);
+ auto* rhs = Expr(true_val);
+ Bind(result, b.LogicalAnd(lhs, rhs));
+ return true;
+ }
+ if (IsConstant(true_val, true)) {
+ // %res = if %cond {
+ // block { # true
+ // exit_if true;
+ // }
+ // block { # false
+ // exit_if %false_val;
+ // }
+ // }
+ //
+ // transform into:
+ //
+ // res = cond || false_val;
+ //
+ auto* lhs = Expr(cond);
+ auto* rhs = Expr(false_val);
+ Bind(result, b.LogicalOr(lhs, rhs));
+ return true;
+ }
+ return false;
+ }
+
+ bool IsConstant(ir::Value* val, bool value) {
+ if (auto* c = val->As<ir::Constant>()) {
+ if (c->Type()->Is<type::Bool>()) {
+ return c->Value()->ValueAs<bool>() == value;
+ }
+ }
+ return false;
}
};
diff --git a/src/tint/ir/to_program_roundtrip_test.cc b/src/tint/ir/to_program_roundtrip_test.cc
index 587d999..2d91267 100644
--- a/src/tint/ir/to_program_roundtrip_test.cc
+++ b/src/tint/ir/to_program_roundtrip_test.cc
@@ -39,12 +39,14 @@
auto ir_module = FromProgram(&input_program);
ASSERT_TRUE(ir_module);
+ tint::ir::Disassembler d{ir_module.Get()};
+ auto disassembly = d.Disassemble();
+
auto output_program = ToProgram(ir_module.Get());
if (!output_program.IsValid()) {
- tint::ir::Disassembler d{ir_module.Get()};
FAIL() << output_program.Diagnostics().str() << std::endl //
<< "IR:" << std::endl //
- << d.Disassemble() << std::endl //
+ << disassembly << std::endl //
<< "AST:" << std::endl //
<< Program::printer(&output_program) << std::endl;
}
@@ -56,10 +58,7 @@
auto expected = expected_wgsl.empty() ? input : utils::TrimSpace(expected_wgsl);
auto got = utils::TrimSpace(output.wgsl);
- if (expected != got) {
- tint::ir::Disassembler d{ir_module.Get()};
- EXPECT_EQ(expected, got) << "IR:" << std::endl << d.Disassemble();
- }
+ EXPECT_EQ(expected, got) << "IR:" << std::endl << disassembly;
}
void Test(std::string_view wgsl) { Test(wgsl, wgsl); }
@@ -266,8 +265,7 @@
// Short-circuiting binary ops
////////////////////////////////////////////////////////////////////////////////
-// TODO(crbug.com/tint/1902): Pattern detect this
-TEST_F(IRToProgramRoundtripTest, DISABLED_BinaryOp_LogicalAnd) {
+TEST_F(IRToProgramRoundtripTest, BinaryOp_LogicalAnd_Param_2) {
Test(R"(
fn f(a : bool, b : bool) -> bool {
return (a && b);
@@ -275,11 +273,334 @@
)");
}
-// TODO(crbug.com/tint/1902): Pattern detect this
-TEST_F(IRToProgramRoundtripTest, DISABLED_BinaryOp_LogicalOr) {
+TEST_F(IRToProgramRoundtripTest, BinaryOp_LogicalAnd_Param_3_ab_c) {
+ Test(R"(
+fn f(a : bool, b : bool, c : bool) -> bool {
+ return ((a && b) && c);
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, BinaryOp_LogicalAnd_Param_3_a_bc) {
+ Test(R"(
+fn f(a : bool, b : bool, c : bool) -> bool {
+ return ((a && b) && c);
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, BinaryOp_LogicalAnd_Let_2) {
Test(R"(
fn f(a : bool, b : bool) -> bool {
- return (a && b);
+ let l = (a && b);
+ return l;
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, BinaryOp_LogicalAnd_Let_3_ab_c) {
+ Test(R"(
+fn f(a : bool, b : bool, c : bool) -> bool {
+ let l = ((a && b) && c);
+ return l;
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, BinaryOp_LogicalAnd_Let_3_a_bc) {
+ Test(R"(
+fn f(a : bool, b : bool, c : bool) -> bool {
+ let l = (a && (b && c));
+ return l;
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, BinaryOp_LogicalAnd_Call_2) {
+ Test(R"(
+fn a() -> bool {
+ return true;
+}
+
+fn b() -> bool {
+ return true;
+}
+
+fn f() -> bool {
+ return (a() && b());
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, BinaryOp_LogicalAnd_Call_3_ab_c) {
+ Test(R"(
+fn a() -> bool {
+ return true;
+}
+
+fn b() -> bool {
+ return true;
+}
+
+fn c() -> bool {
+ return true;
+}
+
+fn f() -> bool {
+ return ((a() && b()) && c());
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, BinaryOp_LogicalAnd_Call_3_a_bc) {
+ Test(R"(
+fn a() -> bool {
+ return true;
+}
+
+fn b() -> bool {
+ return true;
+}
+
+fn c() -> bool {
+ return true;
+}
+
+fn f() -> bool {
+ return (a() && (b() && c()));
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, BinaryOp_LogicalOr_Param_2) {
+ Test(R"(
+fn f(a : bool, b : bool) -> bool {
+ return (a || b);
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, BinaryOp_LogicalOr_Param_3_ab_c) {
+ Test(R"(
+fn f(a : bool, b : bool, c : bool) -> bool {
+ return ((a || b) || c);
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, BinaryOp_LogicalOr_Param_3_a_bc) {
+ Test(R"(
+fn f(a : bool, b : bool, c : bool) -> bool {
+ return (a || (b || c));
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, BinaryOp_LogicalOr_Let_2) {
+ Test(R"(
+fn f(a : bool, b : bool) -> bool {
+ let l = (a || b);
+ return l;
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, BinaryOp_LogicalOr_Let_3_ab_c) {
+ Test(R"(
+fn f(a : bool, b : bool, c : bool) -> bool {
+ let l = ((a || b) || c);
+ return l;
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, BinaryOp_LogicalOr_Let_3_a_bc) {
+ Test(R"(
+fn f(a : bool, b : bool, c : bool) -> bool {
+ let l = (a || (b || c));
+ return l;
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, BinaryOp_LogicalOr_Call_2) {
+ Test(R"(
+fn a() -> bool {
+ return true;
+}
+
+fn b() -> bool {
+ return true;
+}
+
+fn f() -> bool {
+ return (a() || b());
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, BinaryOp_LogicalOr_Call_3_ab_c) {
+ Test(R"(
+fn a() -> bool {
+ return true;
+}
+
+fn b() -> bool {
+ return true;
+}
+
+fn c() -> bool {
+ return true;
+}
+
+fn f() -> bool {
+ return ((a() || b()) || c());
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, BinaryOp_LogicalOr_Call_3_a_bc) {
+ Test(R"(
+fn a() -> bool {
+ return true;
+}
+
+fn b() -> bool {
+ return true;
+}
+
+fn c() -> bool {
+ return true;
+}
+
+fn f() -> bool {
+ return (a() || (b() || c()));
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, BinaryOp_LogicalMixed) {
+ Test(R"(
+fn b() -> bool {
+ return true;
+}
+
+fn d() -> bool {
+ return true;
+}
+
+fn f(a : bool, c : bool) -> bool {
+ let l = ((a || b()) && (c || d()));
+ return l;
+}
+)");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Compound assignment
+////////////////////////////////////////////////////////////////////////////////
+
+TEST_F(IRToProgramRoundtripTest, CompoundAssign_Increment) {
+ Test(R"(
+fn f() {
+ var v : i32;
+ v++;
+}
+)",
+ R"(
+fn f() {
+ var v : i32;
+ v = (v + 1i);
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, CompoundAssign_Decrement) {
+ Test(R"(
+fn f() {
+ var v : i32;
+ v++;
+}
+)",
+ R"(
+fn f() {
+ var v : i32;
+ v = (v + 1i);
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, CompoundAssign_Add) {
+ Test(R"(
+fn f() {
+ var v : i32;
+ v += 8i;
+}
+)",
+ R"(
+fn f() {
+ var v : i32;
+ v = (v + 8i);
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, CompoundAssign_Subtract) {
+ Test(R"(
+fn f() {
+ var v : i32;
+ v -= 8i;
+}
+)",
+ R"(
+fn f() {
+ var v : i32;
+ v = (v - 8i);
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, CompoundAssign_Multiply) {
+ Test(R"(
+fn f() {
+ var v : i32;
+ v *= 8i;
+}
+)",
+ R"(
+fn f() {
+ var v : i32;
+ v = (v * 8i);
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, CompoundAssign_Divide) {
+ Test(R"(
+fn f() {
+ var v : i32;
+ v /= 8i;
+}
+)",
+ R"(
+fn f() {
+ var v : i32;
+ v = (v / 8i);
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, CompoundAssign_Xor) {
+ Test(R"(
+fn f() {
+ var v : i32;
+ v ^= 8i;
+}
+)",
+ R"(
+fn f() {
+ var v : i32;
+ v = (v ^ 8i);
}
)");
}
@@ -450,6 +771,26 @@
)");
}
+TEST_F(IRToProgramRoundtripTest, If_Else_Chain) {
+ Test(R"(
+fn x(i : i32) -> bool {
+ return true;
+}
+
+fn f(a : bool, b : bool, c : bool, d : bool) {
+ if (a) {
+ x(0i);
+ } else if (b) {
+ x(1i);
+ } else if (c) {
+ x(2i);
+ } else {
+ x(3i);
+ }
+}
+)");
+}
+
////////////////////////////////////////////////////////////////////////////////
// Switch
////////////////////////////////////////////////////////////////////////////////
diff --git a/src/tint/ir/transform/block_decorated_structs_test.cc b/src/tint/ir/transform/block_decorated_structs_test.cc
index 92e3922..2211f99 100644
--- a/src/tint/ir/transform/block_decorated_structs_test.cc
+++ b/src/tint/ir/transform/block_decorated_structs_test.cc
@@ -64,8 +64,7 @@
tint_symbol:i32 @offset(0)
}
-# Root block
-%b1 = block {
+%b1 = block { # root
%1:ptr<uniform, tint_symbol_1, read_write> = var @binding_point(0, 0)
}
@@ -98,8 +97,7 @@
tint_symbol:i32 @offset(0)
}
-# Root block
-%b1 = block {
+%b1 = block { # root
%1:ptr<storage, tint_symbol_1, read_write> = var @binding_point(0, 0)
}
@@ -136,8 +134,7 @@
tint_symbol:array<i32> @offset(0)
}
-# Root block
-%b1 = block {
+%b1 = block { # root
%1:ptr<storage, tint_symbol_1, read_write> = var @binding_point(0, 0)
}
@@ -191,8 +188,7 @@
arr:array<i32> @offset(4)
}
-# Root block
-%b1 = block {
+%b1 = block { # root
%1:ptr<storage, tint_symbol, read_write> = var @binding_point(0, 0)
}
@@ -240,8 +236,7 @@
tint_symbol:MyStruct @offset(0)
}
-# Root block
-%b1 = block {
+%b1 = block { # root
%1:ptr<storage, tint_symbol_1, read_write> = var @binding_point(0, 0)
%2:ptr<private, MyStruct, read_write> = var
}
@@ -293,8 +288,7 @@
tint_symbol_4:i32 @offset(0)
}
-# Root block
-%b1 = block {
+%b1 = block { # root
%1:ptr<storage, tint_symbol_1, read_write> = var @binding_point(0, 0)
%2:ptr<storage, tint_symbol_3, read_write> = var @binding_point(0, 1)
%3:ptr<storage, tint_symbol_5, read_write> = var @binding_point(0, 2)
diff --git a/src/tint/ir/transform/merge_return.cc b/src/tint/ir/transform/merge_return.cc
index 0b95a3a..4817d04 100644
--- a/src/tint/ir/transform/merge_return.cc
+++ b/src/tint/ir/transform/merge_return.cc
@@ -91,7 +91,7 @@
}
// Look to see if the function ends with a return
- fn_return = tint::As<Return>(fn->StartTarget()->Branch());
+ fn_return = tint::As<Return>(fn->StartTarget()->Terminator());
// Process the function's block.
// This will traverse into control instructions that hold returns, and apply the necessary
@@ -139,7 +139,7 @@
if (inst->Is<Unreachable>()) {
// Unreachable can become reachable once returns are turned into exits.
- // As this is the terminator for the branch, simply stop processing the
+ // As this is the terminator for the block, simply stop processing the
// instructions. A appropriate terminator will be created for this block below.
inst->Remove();
break;
@@ -170,8 +170,8 @@
// new_value_with_type returns a new RuntimeValue with the same type as 'v'
auto new_value_with_type = [&](Value* v) { return b.InstructionResult(v->Type()); };
- if (inner_if->True()->HasBranchTarget()) {
- if (auto* exit_if = inner_if->True()->Branch()->As<ExitIf>()) {
+ if (inner_if->True()->HasTerminator()) {
+ if (auto* exit_if = inner_if->True()->Terminator()->As<ExitIf>()) {
// Ensure the associated 'if' is updated.
exit_if->SetIf(inner_if);
@@ -190,7 +190,7 @@
// Loop over the 'if' instructions, starting with the inner-most, and add any missing
// terminating instructions to the blocks holding the 'if'.
for (auto* i = inner_if; i; i = tint::As<If>(i->Block()->Parent())) {
- if (!i->Block()->HasBranchTarget()) {
+ if (!i->Block()->HasTerminator()) {
// Append the exit instruction to the block holding the 'if'.
utils::Vector<InstructionResult*, 8> exit_args = i->Results();
if (!i->HasResults()) {
diff --git a/src/tint/ir/transform/merge_return_test.cc b/src/tint/ir/transform/merge_return_test.cc
index cf2a2cc..bf68eae 100644
--- a/src/tint/ir/transform/merge_return_test.cc
+++ b/src/tint/ir/transform/merge_return_test.cc
@@ -73,19 +73,16 @@
auto* src = R"(
%foo = func(%2:i32):i32 -> %b1 {
%b1 = block {
- %3:i32 = if %4 [t: %b2, f: %b3]
- # True block
- %b2 = block {
+ %3:i32 = if %4 [t: %b2, f: %b3] { # if_1
+ %b2 = block { # true
%5:i32 = add %2, 1i
- exit_if %5
+ exit_if %5 # if_1
}
-
- # False block
- %b3 = block {
+ %b3 = block { # false
%6:i32 = add %2, 2i
- exit_if %6
+ exit_if %6 # if_1
}
-
+ }
ret %3
}
}
@@ -125,26 +122,23 @@
auto* src = R"(
%foo = func(%2:i32):i32 -> %b1 {
%b1 = block {
- switch %2 [c: (default, %b2)]
- # Case block
- %b2 = block {
- exit_switch
+ switch %2 [c: (default, %b2)] { # switch_1
+ %b2 = block { # case
+ exit_switch # switch_1
}
-
- loop []
- %3:i32 = if %4 [t: %b3, f: %b4]
- # True block
- %b3 = block {
+ }
+ loop [] { # loop_1
+ }
+ %3:i32 = if %4 [t: %b3, f: %b4] { # if_1
+ %b3 = block { # true
%5:i32 = add %2, 1i
- exit_if %5
+ exit_if %5 # if_1
}
-
- # False block
- %b4 = block {
+ %b4 = block { # false
%6:i32 = add %2, 2i
- exit_if %6
+ exit_if %6 # if_1
}
-
+ }
ret %3
}
}
@@ -177,17 +171,14 @@
auto* src = R"(
%foo = func(%2:bool):void -> %b1 {
%b1 = block {
- if %2 [t: %b2, f: %b3]
- # True block
- %b2 = block {
+ if %2 [t: %b2, f: %b3] { # if_1
+ %b2 = block { # true
ret
}
-
- # False block
- %b3 = block {
- exit_if
+ %b3 = block { # false
+ exit_if # if_1
}
-
+ }
ret
}
}
@@ -197,17 +188,14 @@
auto* expect = R"(
%foo = func(%2:bool):void -> %b1 {
%b1 = block {
- if %2 [t: %b2, f: %b3]
- # True block
- %b2 = block {
- exit_if
+ if %2 [t: %b2, f: %b3] { # if_1
+ %b2 = block { # true
+ exit_if # if_1
}
-
- # False block
- %b3 = block {
- exit_if
+ %b3 = block { # false
+ exit_if # if_1
}
-
+ }
ret
}
}
@@ -239,17 +227,14 @@
auto* src = R"(
%foo = func(%2:bool):void -> %b1 {
%b1 = block {
- if %2 [t: %b2, f: %b3]
- # True block
- %b2 = block {
+ if %2 [t: %b2, f: %b3] { # if_1
+ %b2 = block { # true
ret
}
-
- # False block
- %b3 = block {
- exit_if
+ %b3 = block { # false
+ exit_if # if_1
}
-
+ }
ret
}
}
@@ -259,17 +244,14 @@
auto* expect = R"(
%foo = func(%2:bool):void -> %b1 {
%b1 = block {
- if %2 [t: %b2, f: %b3]
- # True block
- %b2 = block {
- exit_if
+ if %2 [t: %b2, f: %b3] { # if_1
+ %b2 = block { # true
+ exit_if # if_1
}
-
- # False block
- %b3 = block {
- exit_if
+ %b3 = block { # false
+ exit_if # if_1
}
-
+ }
ret
}
}
@@ -299,17 +281,14 @@
auto* src = R"(
%foo = func(%2:bool):i32 -> %b1 {
%b1 = block {
- if %2 [t: %b2, f: %b3]
- # True block
- %b2 = block {
+ if %2 [t: %b2, f: %b3] { # if_1
+ %b2 = block { # true
ret 1i
}
-
- # False block
- %b3 = block {
- exit_if
+ %b3 = block { # false
+ exit_if # if_1
}
-
+ }
ret 2i
}
}
@@ -321,27 +300,23 @@
%b1 = block {
%return_value:ptr<function, i32, read_write> = var
%continue_execution:ptr<function, bool, read_write> = var, true
- if %2 [t: %b2, f: %b3]
- # True block
- %b2 = block {
+ if %2 [t: %b2, f: %b3] { # if_1
+ %b2 = block { # true
store %continue_execution, false
store %return_value, 1i
- exit_if
+ exit_if # if_1
}
-
- # False block
- %b3 = block {
- exit_if
+ %b3 = block { # false
+ exit_if # if_1
}
-
+ }
%5:bool = load %continue_execution
- if %5 [t: %b4]
- # True block
- %b4 = block {
+ if %5 [t: %b4] { # if_2
+ %b4 = block { # true
store %return_value, 2i
- exit_if
+ exit_if # if_2
}
-
+ }
%6:i32 = load %return_value
ret %6
}
@@ -373,17 +348,14 @@
auto* src = R"(
%foo = func(%2:bool):i32 -> %b1 {
%b1 = block {
- %3:i32 = if %2 [t: %b2, f: %b3]
- # True block
- %b2 = block {
+ %3:i32 = if %2 [t: %b2, f: %b3] { # if_1
+ %b2 = block { # true
ret 1i
}
-
- # False block
- %b3 = block {
- exit_if 2i
+ %b3 = block { # false
+ exit_if 2i # if_1
}
-
+ }
ret %3
}
}
@@ -395,27 +367,23 @@
%b1 = block {
%return_value:ptr<function, i32, read_write> = var
%continue_execution:ptr<function, bool, read_write> = var, true
- %5:i32 = if %2 [t: %b2, f: %b3]
- # True block
- %b2 = block {
+ %5:i32 = if %2 [t: %b2, f: %b3] { # if_1
+ %b2 = block { # true
store %continue_execution, false
store %return_value, 1i
- exit_if undef
+ exit_if undef # if_1
}
-
- # False block
- %b3 = block {
- exit_if 2i
+ %b3 = block { # false
+ exit_if 2i # if_1
}
-
+ }
%6:bool = load %continue_execution
- if %6 [t: %b4]
- # True block
- %b4 = block {
+ if %6 [t: %b4] { # if_2
+ %b4 = block { # true
store %return_value, %5
- exit_if
+ exit_if # if_2
}
-
+ }
%7:i32 = load %return_value
ret %7
}
@@ -440,24 +408,21 @@
auto tb = b.With(ifelse->True());
tb.Return(func, 1_i);
auto fb = b.With(ifelse->False());
- fb.ExitIf(ifelse, b.Constant(8_i));
+ fb.ExitIf(ifelse, nullptr);
sb.Return(func, ifelse->Result(0));
auto* src = R"(
%foo = func(%2:bool):i32 -> %b1 {
%b1 = block {
- %3:i32 = if %2 [t: %b2, f: %b3]
- # True block
- %b2 = block {
+ %3:i32 = if %2 [t: %b2, f: %b3] { # if_1
+ %b2 = block { # true
ret 1i
}
-
- # False block
- %b3 = block {
- exit_if 8i
+ %b3 = block { # false
+ exit_if undef # if_1
}
-
+ }
ret %3
}
}
@@ -469,27 +434,23 @@
%b1 = block {
%return_value:ptr<function, i32, read_write> = var
%continue_execution:ptr<function, bool, read_write> = var, true
- %5:i32 = if %2 [t: %b2, f: %b3]
- # True block
- %b2 = block {
+ %5:i32 = if %2 [t: %b2, f: %b3] { # if_1
+ %b2 = block { # true
store %continue_execution, false
store %return_value, 1i
- exit_if undef
+ exit_if undef # if_1
}
-
- # False block
- %b3 = block {
- exit_if 8i
+ %b3 = block { # false
+ exit_if undef # if_1
}
-
+ }
%6:bool = load %continue_execution
- if %6 [t: %b4]
- # True block
- %b4 = block {
+ if %6 [t: %b4] { # if_2
+ %b4 = block { # true
store %return_value, %5
- exit_if
+ exit_if # if_2
}
-
+ }
%7:i32 = load %return_value
ret %7
}
@@ -520,17 +481,14 @@
auto* src = R"(
%foo = func(%2:bool):void -> %b1 {
%b1 = block {
- if %2 [t: %b2, f: %b3]
- # True block
- %b2 = block {
+ if %2 [t: %b2, f: %b3] { # if_1
+ %b2 = block { # true
ret
}
-
- # False block
- %b3 = block {
+ %b3 = block { # false
ret
}
-
+ }
unreachable
}
}
@@ -541,17 +499,14 @@
auto* expect = R"(
%foo = func(%2:bool):void -> %b1 {
%b1 = block {
- if %2 [t: %b2, f: %b3]
- # True block
- %b2 = block {
- exit_if
+ if %2 [t: %b2, f: %b3] { # if_1
+ %b2 = block { # true
+ exit_if # if_1
}
-
- # False block
- %b3 = block {
- exit_if
+ %b3 = block { # false
+ exit_if # if_1
}
-
+ }
ret
}
}
@@ -583,24 +538,20 @@
sb.Return(func);
auto* src = R"(
-# Root block
-%b1 = block {
+%b1 = block { # root
%1:ptr<private, i32, read_write> = var
}
%foo = func(%3:bool):void -> %b2 {
%b2 = block {
- if %3 [t: %b3, f: %b4]
- # True block
- %b3 = block {
+ if %3 [t: %b3, f: %b4] { # if_1
+ %b3 = block { # true
ret
}
-
- # False block
- %b4 = block {
- exit_if
+ %b4 = block { # false
+ exit_if # if_1
}
-
+ }
store %1, 42i
ret
}
@@ -610,34 +561,29 @@
EXPECT_EQ(src, str());
auto* expect = R"(
-# Root block
-%b1 = block {
+%b1 = block { # root
%1:ptr<private, i32, read_write> = var
}
%foo = func(%3:bool):void -> %b2 {
%b2 = block {
%continue_execution:ptr<function, bool, read_write> = var, true
- if %3 [t: %b3, f: %b4]
- # True block
- %b3 = block {
+ if %3 [t: %b3, f: %b4] { # if_1
+ %b3 = block { # true
store %continue_execution, false
- exit_if
+ exit_if # if_1
}
-
- # False block
- %b4 = block {
- exit_if
+ %b4 = block { # false
+ exit_if # if_1
}
-
+ }
%5:bool = load %continue_execution
- if %5 [t: %b5]
- # True block
- %b5 = block {
+ if %5 [t: %b5] { # if_2
+ %b5 = block { # true
store %1, 42i
- exit_if
+ exit_if # if_2
}
-
+ }
ret
}
}
@@ -671,24 +617,20 @@
fb.ExitIf(ifelse);
auto* src = R"(
-# Root block
-%b1 = block {
+%b1 = block { # root
%1:ptr<private, i32, read_write> = var
}
%foo = func(%3:bool):void -> %b2 {
%b2 = block {
- if %3 [t: %b3, f: %b4]
- # True block
- %b3 = block {
+ if %3 [t: %b3, f: %b4] { # if_1
+ %b3 = block { # true
ret
}
-
- # False block
- %b4 = block {
- exit_if
+ %b4 = block { # false
+ exit_if # if_1
}
-
+ }
store %1, 42i
ret
}
@@ -698,34 +640,29 @@
EXPECT_EQ(src, str());
auto* expect = R"(
-# Root block
-%b1 = block {
+%b1 = block { # root
%1:ptr<private, i32, read_write> = var
}
%foo = func(%3:bool):void -> %b2 {
%b2 = block {
%continue_execution:ptr<function, bool, read_write> = var, true
- if %3 [t: %b3, f: %b4]
- # True block
- %b3 = block {
+ if %3 [t: %b3, f: %b4] { # if_1
+ %b3 = block { # true
store %continue_execution, false
- exit_if
+ exit_if # if_1
}
-
- # False block
- %b4 = block {
- exit_if
+ %b4 = block { # false
+ exit_if # if_1
}
-
+ }
%5:bool = load %continue_execution
- if %5 [t: %b5]
- # True block
- %b5 = block {
+ if %5 [t: %b5] { # if_2
+ %b5 = block { # true
store %1, 42i
- exit_if
+ exit_if # if_2
}
-
+ }
ret
}
}
@@ -778,48 +715,38 @@
middle_true.Return(func, 2_i);
auto* src = R"(
-# Root block
-%b1 = block {
+%b1 = block { # root
%1:ptr<private, i32, read_write> = var
}
%foo = func(%condA:bool, %condB:bool, %condC:bool):i32 -> %b2 {
%b2 = block {
- if %condA [t: %b3, f: %b4]
- # True block
- %b3 = block {
+ if %condA [t: %b3, f: %b4] { # if_1
+ %b3 = block { # true
ret 3i
}
-
- # False block
- %b4 = block {
- if %condB [t: %b5, f: %b6]
- # True block
- %b5 = block {
- if %condC [t: %b7, f: %b8]
- # True block
- %b7 = block {
+ %b4 = block { # false
+ if %condB [t: %b5, f: %b6] { # if_2
+ %b5 = block { # true
+ if %condC [t: %b7, f: %b8] { # if_3
+ %b7 = block { # true
ret 1i
}
-
- # False block
- %b8 = block {
- exit_if
+ %b8 = block { # false
+ exit_if # if_3
}
-
+ }
store %1, 1i
ret 2i
}
-
- # False block
- %b6 = block {
- exit_if
+ %b6 = block { # false
+ exit_if # if_2
}
-
+ }
store %1, 2i
- exit_if
+ exit_if # if_1
}
-
+ }
store %1, 3i
%6:i32 = add 5i, 6i
ret %6
@@ -830,8 +757,7 @@
EXPECT_EQ(src, str());
auto* expect = R"(
-# Root block
-%b1 = block {
+%b1 = block { # root
%1:ptr<private, i32, read_write> = var
}
@@ -839,71 +765,59 @@
%b2 = block {
%return_value:ptr<function, i32, read_write> = var
%continue_execution:ptr<function, bool, read_write> = var, true
- if %condA [t: %b3, f: %b4]
- # True block
- %b3 = block {
+ if %condA [t: %b3, f: %b4] { # if_1
+ %b3 = block { # true
store %continue_execution, false
store %return_value, 3i
- exit_if
+ exit_if # if_1
}
-
- # False block
- %b4 = block {
- if %condB [t: %b5, f: %b6]
- # True block
- %b5 = block {
- if %condC [t: %b7, f: %b8]
- # True block
- %b7 = block {
+ %b4 = block { # false
+ if %condB [t: %b5, f: %b6] { # if_2
+ %b5 = block { # true
+ if %condC [t: %b7, f: %b8] { # if_3
+ %b7 = block { # true
store %continue_execution, false
store %return_value, 1i
- exit_if
+ exit_if # if_3
}
-
- # False block
- %b8 = block {
- exit_if
+ %b8 = block { # false
+ exit_if # if_3
}
-
+ }
%8:bool = load %continue_execution
- if %8 [t: %b9]
- # True block
- %b9 = block {
+ if %8 [t: %b9] { # if_4
+ %b9 = block { # true
store %1, 1i
store %continue_execution, false
store %return_value, 2i
- exit_if
+ exit_if # if_4
}
-
- exit_if
+ }
+ exit_if # if_2
}
-
- # False block
- %b6 = block {
- exit_if
+ %b6 = block { # false
+ exit_if # if_2
}
-
+ }
%9:bool = load %continue_execution
- if %9 [t: %b10]
- # True block
- %b10 = block {
+ if %9 [t: %b10] { # if_5
+ %b10 = block { # true
store %1, 2i
- exit_if
+ exit_if # if_5
}
-
- exit_if
+ }
+ exit_if # if_1
}
-
+ }
%10:bool = load %continue_execution
- if %10 [t: %b11]
- # True block
- %b11 = block {
+ if %10 [t: %b11] { # if_6
+ %b11 = block { # true
store %1, 3i
%11:i32 = add 5i, 6i
store %return_value, %11
- exit_if
+ exit_if # if_6
}
-
+ }
%12:i32 = load %return_value
ret %12
}
@@ -954,46 +868,36 @@
middle_true.ExitIf(ifelse_middle);
auto* src = R"(
-# Root block
-%b1 = block {
+%b1 = block { # root
%1:ptr<private, i32, read_write> = var
}
%foo = func(%condA:bool, %condB:bool, %condC:bool):i32 -> %b2 {
%b2 = block {
- if %condA [t: %b3, f: %b4]
- # True block
- %b3 = block {
+ if %condA [t: %b3, f: %b4] { # if_1
+ %b3 = block { # true
ret 3i
}
-
- # False block
- %b4 = block {
- if %condB [t: %b5, f: %b6]
- # True block
- %b5 = block {
- if %condC [t: %b7, f: %b8]
- # True block
- %b7 = block {
+ %b4 = block { # false
+ if %condB [t: %b5, f: %b6] { # if_2
+ %b5 = block { # true
+ if %condC [t: %b7, f: %b8] { # if_3
+ %b7 = block { # true
ret 1i
}
-
- # False block
- %b8 = block {
- exit_if
+ %b8 = block { # false
+ exit_if # if_3
}
-
- exit_if
+ }
+ exit_if # if_2
}
-
- # False block
- %b6 = block {
- exit_if
+ %b6 = block { # false
+ exit_if # if_2
}
-
- exit_if
+ }
+ exit_if # if_1
}
-
+ }
ret 3i
}
}
@@ -1002,8 +906,7 @@
EXPECT_EQ(src, str());
auto* expect = R"(
-# Root block
-%b1 = block {
+%b1 = block { # root
%1:ptr<private, i32, read_write> = var
}
@@ -1011,51 +914,41 @@
%b2 = block {
%return_value:ptr<function, i32, read_write> = var
%continue_execution:ptr<function, bool, read_write> = var, true
- if %condA [t: %b3, f: %b4]
- # True block
- %b3 = block {
+ if %condA [t: %b3, f: %b4] { # if_1
+ %b3 = block { # true
store %continue_execution, false
store %return_value, 3i
- exit_if
+ exit_if # if_1
}
-
- # False block
- %b4 = block {
- if %condB [t: %b5, f: %b6]
- # True block
- %b5 = block {
- if %condC [t: %b7, f: %b8]
- # True block
- %b7 = block {
+ %b4 = block { # false
+ if %condB [t: %b5, f: %b6] { # if_2
+ %b5 = block { # true
+ if %condC [t: %b7, f: %b8] { # if_3
+ %b7 = block { # true
store %continue_execution, false
store %return_value, 1i
- exit_if
+ exit_if # if_3
}
-
- # False block
- %b8 = block {
- exit_if
+ %b8 = block { # false
+ exit_if # if_3
}
-
- exit_if
+ }
+ exit_if # if_2
}
-
- # False block
- %b6 = block {
- exit_if
+ %b6 = block { # false
+ exit_if # if_2
}
-
- exit_if
+ }
+ exit_if # if_1
}
-
+ }
%8:bool = load %continue_execution
- if %8 [t: %b9]
- # True block
- %b9 = block {
+ if %8 [t: %b9] { # if_4
+ %b9 = block { # true
store %return_value, 3i
- exit_if
+ exit_if # if_4
}
-
+ }
%9:i32 = load %return_value
ret %9
}
@@ -1108,49 +1001,39 @@
middle_true.ExitIf(ifelse_middle, middle_true.Add(ty.i32(), 42_i, 1_i));
auto* src = R"(
-# Root block
-%b1 = block {
+%b1 = block { # root
%1:ptr<private, i32, read_write> = var
}
%foo = func(%condA:bool, %condB:bool, %condC:bool):i32 -> %b2 {
%b2 = block {
- %6:i32 = if %condA [t: %b3, f: %b4]
- # True block
- %b3 = block {
+ %6:i32 = if %condA [t: %b3, f: %b4] { # if_1
+ %b3 = block { # true
ret 3i
}
-
- # False block
- %b4 = block {
- %7:i32 = if %condB [t: %b5, f: %b6]
- # True block
- %b5 = block {
- if %condC [t: %b7, f: %b8]
- # True block
- %b7 = block {
+ %b4 = block { # false
+ %7:i32 = if %condB [t: %b5, f: %b6] { # if_2
+ %b5 = block { # true
+ if %condC [t: %b7, f: %b8] { # if_3
+ %b7 = block { # true
ret 1i
}
-
- # False block
- %b8 = block {
- exit_if
+ %b8 = block { # false
+ exit_if # if_3
}
-
+ }
%8:i32 = add 42i, 1i
- exit_if %8
+ exit_if %8 # if_2
}
-
- # False block
- %b6 = block {
+ %b6 = block { # false
%9:i32 = add 43i, 2i
- exit_if %9
+ exit_if %9 # if_2
}
-
+ }
%10:i32 = add %7, 1i
- exit_if %10
+ exit_if %10 # if_1
}
-
+ }
%11:i32 = add %6, 1i
ret %11
}
@@ -1160,8 +1043,7 @@
EXPECT_EQ(src, str());
auto* expect = R"(
-# Root block
-%b1 = block {
+%b1 = block { # root
%1:ptr<private, i32, read_write> = var
}
@@ -1169,69 +1051,59 @@
%b2 = block {
%return_value:ptr<function, i32, read_write> = var
%continue_execution:ptr<function, bool, read_write> = var, true
- %8:i32 = if %condA [t: %b3, f: %b4]
- # True block
- %b3 = block {
+ %8:i32 = if %condA [t: %b3, f: %b4] { # if_1
+ %b3 = block { # true
store %continue_execution, false
store %return_value, 3i
- exit_if undef
+ exit_if undef # if_1
}
-
- # False block
- %b4 = block {
- %9:i32 = if %condB [t: %b5, f: %b6]
- # True block
- %b5 = block {
- if %condC [t: %b7, f: %b8]
- # True block
- %b7 = block {
+ %b4 = block { # false
+ %9:i32 = if %condB [t: %b5, f: %b6] { # if_2
+ %b5 = block { # true
+ if %condC [t: %b7, f: %b8] { # if_3
+ %b7 = block { # true
store %continue_execution, false
store %return_value, 1i
- exit_if
+ exit_if # if_3
}
-
- # False block
- %b8 = block {
- exit_if
+ %b8 = block { # false
+ exit_if # if_3
}
-
+ }
%10:bool = load %continue_execution
- %11:i32 = if %10 [t: %b9]
- # True block
- %b9 = block {
+ %11:i32 = if %10 [t: %b9] { # if_4
+ %b9 = block { # true
%12:i32 = add 42i, 1i
- exit_if %12
+ exit_if %12 # if_4
}
-
- exit_if %11
+ # implicit false block: exit_if undef
+ }
+ exit_if %11 # if_2
}
-
- # False block
- %b6 = block {
+ %b6 = block { # false
%13:i32 = add 43i, 2i
- exit_if %13
+ exit_if %13 # if_2
}
-
+ }
%14:bool = load %continue_execution
- %15:i32 = if %14 [t: %b10]
- # True block
- %b10 = block {
+ %15:i32 = if %14 [t: %b10] { # if_5
+ %b10 = block { # true
%16:i32 = add %9, 1i
- exit_if %16
+ exit_if %16 # if_5
}
-
- exit_if %15
+ # implicit false block: exit_if undef
+ }
+ exit_if %15 # if_1
}
-
+ }
%17:bool = load %continue_execution
- if %17 [t: %b11]
- # True block
- %b11 = block {
+ if %17 [t: %b11] { # if_6
+ %b11 = block { # true
%18:i32 = add %8, 1i
store %return_value, %18
- exit_if
+ exit_if # if_6
}
-
+ }
%19:i32 = load %return_value
ret %19
}
@@ -1257,12 +1129,11 @@
auto* src = R"(
%foo = func():i32 -> %b1 {
%b1 = block {
- loop [b: %b2]
- # Body block
- %b2 = block {
+ loop [b: %b2] { # loop_1
+ %b2 = block { # body
ret 42i
}
-
+ }
unreachable
}
}
@@ -1273,13 +1144,12 @@
%foo = func():i32 -> %b1 {
%b1 = block {
%return_value:ptr<function, i32, read_write> = var
- loop [b: %b2]
- # Body block
- %b2 = block {
+ loop [b: %b2] { # loop_1
+ %b2 = block { # body
store %return_value, 42i
- exit_loop
+ exit_loop # loop_1
}
-
+ }
%3:i32 = load %return_value
ret %3
}
@@ -1322,37 +1192,30 @@
sb.Return(func, 43_i);
auto* src = R"(
-# Root block
-%b1 = block {
+%b1 = block { # root
%1:ptr<private, i32, read_write> = var
}
%foo = func(%3:bool):i32 -> %b2 {
%b2 = block {
- loop [b: %b3, c: %b4]
- # Body block
- %b3 = block {
- if %3 [t: %b5, f: %b6]
- # True block
- %b5 = block {
+ loop [b: %b3, c: %b4] { # loop_1
+ %b3 = block { # body
+ if %3 [t: %b5, f: %b6] { # if_1
+ %b5 = block { # true
ret 42i
}
-
- # False block
- %b6 = block {
- exit_if
+ %b6 = block { # false
+ exit_if # if_1
}
-
+ }
store %1, 2i
continue %b4
}
-
- # Continuing block
- %b4 = block {
+ %b4 = block { # continuing
store %1, 1i
break_if true %b3
}
-
+ }
store %1, 3i
ret 43i
}
@@ -1361,8 +1224,7 @@
EXPECT_EQ(src, str());
auto* expect = R"(
-# Root block
-%b1 = block {
+%b1 = block { # root
%1:ptr<private, i32, read_write> = var
}
@@ -1370,48 +1232,40 @@
%b2 = block {
%return_value:ptr<function, i32, read_write> = var
%continue_execution:ptr<function, bool, read_write> = var, true
- loop [b: %b3, c: %b4]
- # Body block
- %b3 = block {
- if %3 [t: %b5, f: %b6]
- # True block
- %b5 = block {
+ loop [b: %b3, c: %b4] { # loop_1
+ %b3 = block { # body
+ if %3 [t: %b5, f: %b6] { # if_1
+ %b5 = block { # true
store %continue_execution, false
store %return_value, 42i
- exit_if
+ exit_if # if_1
}
-
- # False block
- %b6 = block {
- exit_if
+ %b6 = block { # false
+ exit_if # if_1
}
-
+ }
%6:bool = load %continue_execution
- if %6 [t: %b7]
- # True block
- %b7 = block {
+ if %6 [t: %b7] { # if_2
+ %b7 = block { # true
store %1, 2i
continue %b4
}
-
- exit_loop
+ }
+ exit_loop # loop_1
}
-
- # Continuing block
- %b4 = block {
+ %b4 = block { # continuing
store %1, 1i
break_if true %b3
}
-
+ }
%7:bool = load %continue_execution
- if %7 [t: %b8]
- # True block
- %b8 = block {
+ if %7 [t: %b8] { # if_3
+ %b8 = block { # true
store %1, 3i
store %return_value, 43i
- exit_if
+ exit_if # if_3
}
-
+ }
%8:i32 = load %return_value
ret %8
}
@@ -1453,37 +1307,30 @@
sb.Unreachable();
auto* src = R"(
-# Root block
-%b1 = block {
+%b1 = block { # root
%1:ptr<private, i32, read_write> = var
}
%foo = func(%3:bool):i32 -> %b2 {
%b2 = block {
- loop [b: %b3, c: %b4]
- # Body block
- %b3 = block {
- if %3 [t: %b5, f: %b6]
- # True block
- %b5 = block {
+ loop [b: %b3, c: %b4] { # loop_1
+ %b3 = block { # body
+ if %3 [t: %b5, f: %b6] { # if_1
+ %b5 = block { # true
ret 42i
}
-
- # False block
- %b6 = block {
- exit_if
+ %b6 = block { # false
+ exit_if # if_1
}
-
+ }
store %1, 2i
continue %b4
}
-
- # Continuing block
- %b4 = block {
+ %b4 = block { # continuing
store %1, 1i
next_iteration %b3
}
-
+ }
unreachable
}
}
@@ -1491,8 +1338,7 @@
EXPECT_EQ(src, str());
auto* expect = R"(
-# Root block
-%b1 = block {
+%b1 = block { # root
%1:ptr<private, i32, read_write> = var
}
@@ -1500,39 +1346,32 @@
%b2 = block {
%return_value:ptr<function, i32, read_write> = var
%continue_execution:ptr<function, bool, read_write> = var, true
- loop [b: %b3, c: %b4]
- # Body block
- %b3 = block {
- if %3 [t: %b5, f: %b6]
- # True block
- %b5 = block {
+ loop [b: %b3, c: %b4] { # loop_1
+ %b3 = block { # body
+ if %3 [t: %b5, f: %b6] { # if_1
+ %b5 = block { # true
store %continue_execution, false
store %return_value, 42i
- exit_if
+ exit_if # if_1
}
-
- # False block
- %b6 = block {
- exit_if
+ %b6 = block { # false
+ exit_if # if_1
}
-
+ }
%6:bool = load %continue_execution
- if %6 [t: %b7]
- # True block
- %b7 = block {
+ if %6 [t: %b7] { # if_2
+ %b7 = block { # true
store %1, 2i
continue %b4
}
-
- exit_loop
+ }
+ exit_loop # loop_1
}
-
- # Continuing block
- %b4 = block {
+ %b4 = block { # continuing
store %1, 1i
next_iteration %b3
}
-
+ }
%7:i32 = load %return_value
ret %7
}
@@ -1576,37 +1415,30 @@
sb.Return(func, loop->Result(0));
auto* src = R"(
-# Root block
-%b1 = block {
+%b1 = block { # root
%1:ptr<private, i32, read_write> = var
}
%foo = func(%3:bool):i32 -> %b2 {
%b2 = block {
- loop [b: %b3, c: %b4]
- # Body block
- %b3 = block {
- if %3 [t: %b5, f: %b6]
- # True block
- %b5 = block {
+ loop [b: %b3, c: %b4] { # loop_1
+ %b3 = block { # body
+ if %3 [t: %b5, f: %b6] { # if_1
+ %b5 = block { # true
ret 42i
}
-
- # False block
- %b6 = block {
- exit_if
+ %b6 = block { # false
+ exit_if # if_1
}
-
+ }
store %1, 2i
continue %b4
}
-
- # Continuing block
- %b4 = block {
+ %b4 = block { # continuing
store %1, 1i
break_if true %b3 4i
}
-
+ }
store %1, 3i
ret %4
}
@@ -1615,8 +1447,7 @@
EXPECT_EQ(src, str());
auto* expect = R"(
-# Root block
-%b1 = block {
+%b1 = block { # root
%1:ptr<private, i32, read_write> = var
}
@@ -1624,48 +1455,40 @@
%b2 = block {
%return_value:ptr<function, i32, read_write> = var
%continue_execution:ptr<function, bool, read_write> = var, true
- loop [b: %b3, c: %b4]
- # Body block
- %b3 = block {
- if %3 [t: %b5, f: %b6]
- # True block
- %b5 = block {
+ loop [b: %b3, c: %b4] { # loop_1
+ %b3 = block { # body
+ if %3 [t: %b5, f: %b6] { # if_1
+ %b5 = block { # true
store %continue_execution, false
store %return_value, 42i
- exit_if
+ exit_if # if_1
}
-
- # False block
- %b6 = block {
- exit_if
+ %b6 = block { # false
+ exit_if # if_1
}
-
+ }
%6:bool = load %continue_execution
- if %6 [t: %b7]
- # True block
- %b7 = block {
+ if %6 [t: %b7] { # if_2
+ %b7 = block { # true
store %1, 2i
continue %b4
}
-
- exit_loop
+ }
+ exit_loop # loop_1
}
-
- # Continuing block
- %b4 = block {
+ %b4 = block { # continuing
store %1, 1i
break_if true %b3 4i
}
-
+ }
%7:bool = load %continue_execution
- if %7 [t: %b8]
- # True block
- %b8 = block {
+ if %7 [t: %b8] { # if_3
+ %b8 = block { # true
store %1, 3i
store %return_value, %8
- exit_if
+ exit_if # if_3
}
-
+ }
%9:i32 = load %return_value
ret %9
}
@@ -1696,17 +1519,14 @@
auto* src = R"(
%foo = func(%2:i32):i32 -> %b1 {
%b1 = block {
- switch %2 [c: (1i, %b2), c: (default, %b3)]
- # Case block
- %b2 = block {
+ switch %2 [c: (1i, %b2), c: (default, %b3)] { # switch_1
+ %b2 = block { # case
ret 42i
}
-
- # Case block
- %b3 = block {
- exit_switch
+ %b3 = block { # case
+ exit_switch # switch_1
}
-
+ }
ret 0i
}
}
@@ -1718,27 +1538,23 @@
%b1 = block {
%return_value:ptr<function, i32, read_write> = var
%continue_execution:ptr<function, bool, read_write> = var, true
- switch %2 [c: (1i, %b2), c: (default, %b3)]
- # Case block
- %b2 = block {
+ switch %2 [c: (1i, %b2), c: (default, %b3)] { # switch_1
+ %b2 = block { # case
store %continue_execution, false
store %return_value, 42i
- exit_switch
+ exit_switch # switch_1
}
-
- # Case block
- %b3 = block {
- exit_switch
+ %b3 = block { # case
+ exit_switch # switch_1
}
-
+ }
%5:bool = load %continue_execution
- if %5 [t: %b4]
- # True block
- %b4 = block {
+ if %5 [t: %b4] { # if_1
+ %b4 = block { # true
store %return_value, 0i
- exit_if
+ exit_if # if_1
}
-
+ }
%6:i32 = load %return_value
ret %6
}
@@ -1779,36 +1595,29 @@
sb.Return(func, 0_i);
auto* src = R"(
-# Root block
-%b1 = block {
+%b1 = block { # root
%1:ptr<private, i32, read_write> = var
}
%foo = func(%3:i32):i32 -> %b2 {
%b2 = block {
- switch %3 [c: (1i, %b3), c: (default, %b4)]
- # Case block
- %b3 = block {
- if %3 [t: %b5, f: %b6]
- # True block
- %b5 = block {
+ switch %3 [c: (1i, %b3), c: (default, %b4)] { # switch_1
+ %b3 = block { # case
+ if %3 [t: %b5, f: %b6] { # if_1
+ %b5 = block { # true
ret 42i
}
-
- # False block
- %b6 = block {
- exit_if
+ %b6 = block { # false
+ exit_if # if_1
}
-
+ }
store %1, 2i
- exit_switch
+ exit_switch # switch_1
}
-
- # Case block
- %b4 = block {
- exit_switch
+ %b4 = block { # case
+ exit_switch # switch_1
}
-
+ }
ret 0i
}
}
@@ -1816,8 +1625,7 @@
EXPECT_EQ(src, str());
auto* expect = R"(
-# Root block
-%b1 = block {
+%b1 = block { # root
%1:ptr<private, i32, read_write> = var
}
@@ -1825,46 +1633,38 @@
%b2 = block {
%return_value:ptr<function, i32, read_write> = var
%continue_execution:ptr<function, bool, read_write> = var, true
- switch %3 [c: (1i, %b3), c: (default, %b4)]
- # Case block
- %b3 = block {
- if %3 [t: %b5, f: %b6]
- # True block
- %b5 = block {
+ switch %3 [c: (1i, %b3), c: (default, %b4)] { # switch_1
+ %b3 = block { # case
+ if %3 [t: %b5, f: %b6] { # if_1
+ %b5 = block { # true
store %continue_execution, false
store %return_value, 42i
- exit_if
+ exit_if # if_1
}
-
- # False block
- %b6 = block {
- exit_if
+ %b6 = block { # false
+ exit_if # if_1
}
-
+ }
%6:bool = load %continue_execution
- if %6 [t: %b7]
- # True block
- %b7 = block {
+ if %6 [t: %b7] { # if_2
+ %b7 = block { # true
store %1, 2i
- exit_switch
+ exit_switch # switch_1
}
-
- exit_switch
+ }
+ exit_switch # switch_1
}
-
- # Case block
- %b4 = block {
- exit_switch
+ %b4 = block { # case
+ exit_switch # switch_1
}
-
+ }
%7:bool = load %continue_execution
- if %7 [t: %b8]
- # True block
- %b8 = block {
+ if %7 [t: %b8] { # if_3
+ %b8 = block { # true
store %return_value, 0i
- exit_if
+ exit_if # if_3
}
-
+ }
%8:i32 = load %return_value
ret %8
}
@@ -1900,27 +1700,20 @@
auto* src = R"(
%foo = func(%2:i32):i32 -> %b1 {
%b1 = block {
- switch %2 [c: (1i, %b2), c: (2i, %b3), c: (3i, %b4), c: (default, %b5)]
- # Case block
- %b2 = block {
+ switch %2 [c: (1i, %b2), c: (2i, %b3), c: (3i, %b4), c: (default, %b5)] { # switch_1
+ %b2 = block { # case
ret 42i
}
-
- # Case block
- %b3 = block {
+ %b3 = block { # case
ret 99i
}
-
- # Case block
- %b4 = block {
- exit_switch 1i
+ %b4 = block { # case
+ exit_switch 1i # switch_1
}
-
- # Case block
- %b5 = block {
- exit_switch 0i
+ %b5 = block { # case
+ exit_switch 0i # switch_1
}
-
+ }
ret %3
}
}
@@ -1932,39 +1725,31 @@
%b1 = block {
%return_value:ptr<function, i32, read_write> = var
%continue_execution:ptr<function, bool, read_write> = var, true
- switch %2 [c: (1i, %b2), c: (2i, %b3), c: (3i, %b4), c: (default, %b5)]
- # Case block
- %b2 = block {
+ switch %2 [c: (1i, %b2), c: (2i, %b3), c: (3i, %b4), c: (default, %b5)] { # switch_1
+ %b2 = block { # case
store %continue_execution, false
store %return_value, 42i
- exit_switch undef
+ exit_switch undef # switch_1
}
-
- # Case block
- %b3 = block {
+ %b3 = block { # case
store %continue_execution, false
store %return_value, 99i
- exit_switch undef
+ exit_switch undef # switch_1
}
-
- # Case block
- %b4 = block {
- exit_switch 1i
+ %b4 = block { # case
+ exit_switch 1i # switch_1
}
-
- # Case block
- %b5 = block {
- exit_switch 0i
+ %b5 = block { # case
+ exit_switch 0i # switch_1
}
-
+ }
%5:bool = load %continue_execution
- if %5 [t: %b6]
- # True block
- %b6 = block {
+ if %5 [t: %b6] { # if_1
+ %b6 = block { # true
store %return_value, %6
- exit_if
+ exit_if # if_1
}
-
+ }
%7:i32 = load %return_value
ret %7
}
@@ -1995,18 +1780,16 @@
auto* src = R"(
%foo = func():void -> %b1 {
%b1 = block {
- loop [b: %b2]
- # Body block
- %b2 = block {
- if true [t: %b3]
- # True block
- %b3 = block {
+ loop [b: %b2] { # loop_1
+ %b2 = block { # body
+ if true [t: %b3] { # if_1
+ %b3 = block { # true
ret
}
-
+ }
continue %b4
}
-
+ }
unreachable
}
}
@@ -2017,26 +1800,23 @@
%foo = func():void -> %b1 {
%b1 = block {
%continue_execution:ptr<function, bool, read_write> = var, true
- loop [b: %b2]
- # Body block
- %b2 = block {
- if true [t: %b3]
- # True block
- %b3 = block {
+ loop [b: %b2] { # loop_1
+ %b2 = block { # body
+ if true [t: %b3] { # if_1
+ %b3 = block { # true
store %continue_execution, false
- exit_if
+ exit_if # if_1
}
-
+ }
%3:bool = load %continue_execution
- if %3 [t: %b4]
- # True block
- %b4 = block {
+ if %3 [t: %b4] { # if_2
+ %b4 = block { # true
continue %b5
}
-
- exit_loop
+ }
+ exit_loop # loop_1
}
-
+ }
ret
}
}
@@ -2065,18 +1845,16 @@
auto* src = R"(
%foo = func():i32 -> %b1 {
%b1 = block {
- if true [t: %b2]
- # True block
- %b2 = block {
- if true [t: %b3]
- # True block
- %b3 = block {
+ if true [t: %b2] { # if_1
+ %b2 = block { # true
+ if true [t: %b3] { # if_2
+ %b3 = block { # true
ret 1i
}
-
+ }
ret 2i
}
-
+ }
ret 3i
}
}
@@ -2088,37 +1866,33 @@
%b1 = block {
%return_value:ptr<function, i32, read_write> = var
%continue_execution:ptr<function, bool, read_write> = var, true
- if true [t: %b2]
- # True block
- %b2 = block {
- if true [t: %b3]
- # True block
- %b3 = block {
+ if true [t: %b2] { # if_1
+ %b2 = block { # true
+ if true [t: %b3] { # if_2
+ %b3 = block { # true
store %continue_execution, false
store %return_value, 1i
- exit_if
+ exit_if # if_2
}
-
+ }
%4:bool = load %continue_execution
- if %4 [t: %b4]
- # True block
- %b4 = block {
+ if %4 [t: %b4] { # if_3
+ %b4 = block { # true
store %continue_execution, false
store %return_value, 2i
- exit_if
+ exit_if # if_3
}
-
- exit_if
+ }
+ exit_if # if_1
}
-
+ }
%5:bool = load %continue_execution
- if %5 [t: %b5]
- # True block
- %b5 = block {
+ if %5 [t: %b5] { # if_4
+ %b5 = block { # true
store %return_value, 3i
- exit_if
+ exit_if # if_4
}
-
+ }
%6:i32 = load %return_value
ret %6
}
diff --git a/src/tint/ir/unreachable.h b/src/tint/ir/unreachable.h
index 51fdc24..507d6cb 100644
--- a/src/tint/ir/unreachable.h
+++ b/src/tint/ir/unreachable.h
@@ -15,12 +15,12 @@
#ifndef SRC_TINT_IR_UNREACHABLE_H_
#define SRC_TINT_IR_UNREACHABLE_H_
-#include "src/tint/ir/branch.h"
+#include "src/tint/ir/terminator.h"
namespace tint::ir {
/// An unreachable instruction in the IR.
-class Unreachable : public utils::Castable<Unreachable, Branch> {
+class Unreachable : public utils::Castable<Unreachable, Terminator> {
public:
~Unreachable() override;
};
diff --git a/src/tint/ir/validate.cc b/src/tint/ir/validate.cc
index 7d4a15c..6687111 100644
--- a/src/tint/ir/validate.cc
+++ b/src/tint/ir/validate.cc
@@ -166,13 +166,13 @@
void CheckBlock(Block* blk) {
TINT_SCOPED_ASSIGNMENT(current_block_, blk);
- if (!blk->HasBranchTarget()) {
- AddError(blk, "block: does not end in a branch");
+ if (!blk->HasTerminator()) {
+ AddError(blk, "block: does not end in a terminator instruction");
}
for (auto* inst : *blk) {
- if (inst->Is<ir::Branch>() && inst != blk->Branch()) {
- AddError(inst, "block: branch which isn't the final instruction");
+ if (inst->Is<ir::Terminator>() && inst != blk->Terminator()) {
+ AddError(inst, "block: terminator which isn't the final instruction");
continue;
}
@@ -182,19 +182,19 @@
void CheckInstruction(Instruction* inst) {
tint::Switch(
- inst, //
- [&](Access* a) { CheckAccess(a); }, //
- [&](Binary*) {}, //
- [&](Branch* b) { CheckBranch(b); }, //
- [&](Call* c) { CheckCall(c); }, //
- [&](If* if_) { CheckIf(if_); }, //
- [&](Load*) {}, //
- [&](Loop*) {}, //
- [&](Store*) {}, //
- [&](Switch*) {}, //
- [&](Swizzle*) {}, //
- [&](Unary*) {}, //
- [&](Var*) {}, //
+ inst, //
+ [&](Access* a) { CheckAccess(a); }, //
+ [&](Binary*) {}, //
+ [&](Call* c) { CheckCall(c); }, //
+ [&](If* if_) { CheckIf(if_); }, //
+ [&](Load*) {}, //
+ [&](Loop*) {}, //
+ [&](Store*) {}, //
+ [&](Switch*) {}, //
+ [&](Swizzle*) {}, //
+ [&](Terminator* b) { CheckTerminator(b); }, //
+ [&](Unary*) {}, //
+ [&](Var*) {}, //
[&](Default) {
AddError(std::string("missing validation of: ") + inst->TypeInfo().name);
});
@@ -282,7 +282,7 @@
}
}
- void CheckBranch(ir::Branch* b) {
+ void CheckTerminator(ir::Terminator* b) {
tint::Switch(
b, //
[&](ir::BreakIf*) {}, //
@@ -298,7 +298,7 @@
},
[&](ir::Unreachable*) {}, //
[&](Default) {
- AddError(std::string("missing validation of branch: ") + b->TypeInfo().name);
+ AddError(std::string("missing validation of terminator: ") + b->TypeInfo().name);
});
}
diff --git a/src/tint/ir/validate_test.cc b/src/tint/ir/validate_test.cc
index c05674a..080625c 100644
--- a/src/tint/ir/validate_test.cc
+++ b/src/tint/ir/validate_test.cc
@@ -46,23 +46,21 @@
auto res = ir::Validate(mod);
ASSERT_FALSE(res);
- EXPECT_EQ(res.Failure().str(), R"(:3:3 error: root block: invalid instruction: tint::ir::Loop
- loop [b: %b2]
+ EXPECT_EQ(res.Failure().str(), R"(:2:3 error: root block: invalid instruction: tint::ir::Loop
+ loop [b: %b2] { # loop_1
^^^^^^^^^^^^^
-:2:1 note: In block
-%b1 = block {
+:1:1 note: In block
+%b1 = block { # root
^^^^^^^^^^^
note: # Disassembly
-# Root block
-%b1 = block {
- loop [b: %b2]
- # Body block
- %b2 = block {
+%b1 = block { # root
+ loop [b: %b2] { # loop_1
+ %b2 = block { # body
continue %b3
}
-
+ }
}
)");
@@ -79,13 +77,13 @@
EXPECT_TRUE(res) << res.Failure().str();
}
-TEST_F(IR_ValidateTest, Block_NoBranchAtEnd) {
+TEST_F(IR_ValidateTest, Block_NoTerminator) {
auto* f = b.Function("my_func", ty.void_());
mod.functions.Push(f);
auto res = ir::Validate(mod);
ASSERT_FALSE(res);
- EXPECT_EQ(res.Failure().str(), R"(:2:3 error: block: does not end in a branch
+ EXPECT_EQ(res.Failure().str(), R"(:2:3 error: block: does not end in a terminator instruction
%b1 = block {
^^^^^^^^^^^
@@ -463,7 +461,7 @@
)");
}
-TEST_F(IR_ValidateTest, Block_BranchInMiddle) {
+TEST_F(IR_ValidateTest, Block_TerminatorInMiddle) {
auto* f = b.Function("my_func", ty.void_());
mod.functions.Push(f);
@@ -473,7 +471,8 @@
auto res = ir::Validate(mod);
ASSERT_FALSE(res);
- EXPECT_EQ(res.Failure().str(), R"(:3:5 error: block: branch which isn't the final instruction
+ EXPECT_EQ(res.Failure().str(),
+ R"(:3:5 error: block: terminator which isn't the final instruction
ret
^^^
@@ -505,7 +504,7 @@
auto res = ir::Validate(mod);
ASSERT_FALSE(res);
EXPECT_EQ(res.Failure().str(), R"(:3:8 error: if: condition must be a `bool` type
- if 1i [t: %b2, f: %b3]
+ if 1i [t: %b2, f: %b3] { # if_1
^^
:2:3 note: In block
@@ -515,17 +514,14 @@
note: # Disassembly
%my_func = func():void -> %b1 {
%b1 = block {
- if 1i [t: %b2, f: %b3]
- # True block
- %b2 = block {
+ if 1i [t: %b2, f: %b3] { # if_1
+ %b2 = block { # true
ret
}
-
- # False block
- %b3 = block {
+ %b3 = block { # false
ret
}
-
+ }
ret
}
}
diff --git a/src/tint/utils/hashmap_base.h b/src/tint/utils/hashmap_base.h
index 73cb0d7..d2aaf1d 100644
--- a/src/tint/utils/hashmap_base.h
+++ b/src/tint/utils/hashmap_base.h
@@ -25,9 +25,7 @@
#include "src/tint/utils/hash.h"
#include "src/tint/utils/vector.h"
-#ifndef NDEBUG
#define TINT_ASSERT_ITERATORS_NOT_INVALIDATED
-#endif
namespace tint::utils {
diff --git a/src/tint/writer/spirv/ir/generator_impl_ir.cc b/src/tint/writer/spirv/ir/generator_impl_ir.cc
index 1a821da..fc52fe7 100644
--- a/src/tint/writer/spirv/ir/generator_impl_ir.cc
+++ b/src/tint/writer/spirv/ir/generator_impl_ir.cc
@@ -39,6 +39,7 @@
#include "src/tint/ir/return.h"
#include "src/tint/ir/store.h"
#include "src/tint/ir/switch.h"
+#include "src/tint/ir/terminator.h"
#include "src/tint/ir/transform/add_empty_entry_point.h"
#include "src/tint/ir/transform/block_decorated_structs.h"
#include "src/tint/ir/transform/merge_return.h"
@@ -479,7 +480,7 @@
[&](ir::UserCall* c) { EmitUserCall(c); }, //
[&](ir::Var* v) { EmitVar(v); }, //
[&](ir::If* i) { EmitIf(i); }, //
- [&](ir::Branch* b) { EmitBranch(b); }, //
+ [&](ir::Terminator* t) { EmitTerminator(t); }, //
[&](Default) {
TINT_ICE(Writer, diagnostics_)
<< "unimplemented instruction: " << inst->TypeInfo().name;
@@ -492,14 +493,14 @@
}
}
-void GeneratorImplIr::EmitBranch(ir::Branch* b) {
+void GeneratorImplIr::EmitTerminator(ir::Terminator* t) {
tint::Switch( //
- b, //
+ t, //
[&](ir::Return*) {
- if (!b->Args().IsEmpty()) {
- TINT_ASSERT(Writer, b->Args().Length() == 1u);
+ if (!t->Args().IsEmpty()) {
+ TINT_ASSERT(Writer, t->Args().Length() == 1u);
OperandList operands;
- operands.push_back(Value(b->Args()[0]));
+ operands.push_back(Value(t->Args()[0]));
current_function_.push_inst(spv::Op::OpReturnValue, operands);
} else {
current_function_.push_inst(spv::Op::OpReturn, {});
@@ -528,7 +529,7 @@
[&](ir::Unreachable*) { current_function_.push_inst(spv::Op::OpUnreachable, {}); },
[&](Default) {
- TINT_ICE(Writer, diagnostics_) << "unimplemented branch: " << b->TypeInfo().name;
+ TINT_ICE(Writer, diagnostics_) << "unimplemented branch: " << t->TypeInfo().name;
});
}
@@ -547,11 +548,11 @@
uint32_t true_label = merge_label;
uint32_t false_label = merge_label;
if (true_block->Length() > 1 || i->HasResults() ||
- (true_block->HasBranchTarget() && !true_block->Branch()->Is<ir::ExitIf>())) {
+ (true_block->HasTerminator() && !true_block->Terminator()->Is<ir::ExitIf>())) {
true_label = Label(true_block);
}
if (false_block->Length() > 1 || i->HasResults() ||
- (false_block->HasBranchTarget() && !false_block->Branch()->Is<ir::ExitIf>())) {
+ (false_block->HasTerminator() && !false_block->Terminator()->Is<ir::ExitIf>())) {
false_label = Label(false_block);
}
@@ -837,7 +838,7 @@
EmitBlockInstructions(loop->Body());
// Emit the loop continuing block.
- if (loop->Continuing()->HasBranchTarget()) {
+ if (loop->Continuing()->HasTerminator()) {
EmitBlock(loop->Continuing());
} else {
// We still need to emit a continuing block with a back-edge, even if it is unreachable.
diff --git a/src/tint/writer/spirv/ir/generator_impl_ir.h b/src/tint/writer/spirv/ir/generator_impl_ir.h
index 1a2ec8e..357c732 100644
--- a/src/tint/writer/spirv/ir/generator_impl_ir.h
+++ b/src/tint/writer/spirv/ir/generator_impl_ir.h
@@ -33,7 +33,6 @@
class Binary;
class Block;
class BlockParam;
-class Branch;
class BuiltinCall;
class Construct;
class ControlInstruction;
@@ -48,6 +47,7 @@
class MultiInBlock;
class Store;
class Switch;
+class Terminator;
class UserCall;
class Value;
class Var;
@@ -184,9 +184,9 @@
/// @param var the var instruction to emit
void EmitVar(ir::Var* var);
- /// Emit a branch instruction.
- /// @param b the branch instruction to emit
- void EmitBranch(ir::Branch* b);
+ /// Emit a terminator instruction.
+ /// @param term the terminator instruction to emit
+ void EmitTerminator(ir::Terminator* term);
/// Emit the OpPhis for the given flow control instruction.
/// @param inst the flow control instruction