diff --git a/src/tint/lang/core/ir/BUILD.bazel b/src/tint/lang/core/ir/BUILD.bazel index 99c054a4dbe..81ee19e1160 100644 --- a/src/tint/lang/core/ir/BUILD.bazel +++ b/src/tint/lang/core/ir/BUILD.bazel @@ -180,6 +180,7 @@ cc_library( "block_param_test.cc", "block_test.cc", "break_if_test.cc", + "builder_test.cc", "constant_test.cc", "construct_test.cc", "continue_test.cc", diff --git a/src/tint/lang/core/ir/BUILD.cmake b/src/tint/lang/core/ir/BUILD.cmake index 7b9e40f5f2b..e918b7cacd7 100644 --- a/src/tint/lang/core/ir/BUILD.cmake +++ b/src/tint/lang/core/ir/BUILD.cmake @@ -181,6 +181,7 @@ tint_add_target(tint_lang_core_ir_test test lang/core/ir/block_param_test.cc lang/core/ir/block_test.cc lang/core/ir/break_if_test.cc + lang/core/ir/builder_test.cc lang/core/ir/constant_test.cc lang/core/ir/construct_test.cc lang/core/ir/continue_test.cc diff --git a/src/tint/lang/core/ir/BUILD.gn b/src/tint/lang/core/ir/BUILD.gn index 85e557334c3..0d065771e7e 100644 --- a/src/tint/lang/core/ir/BUILD.gn +++ b/src/tint/lang/core/ir/BUILD.gn @@ -180,6 +180,7 @@ if (tint_build_unittests) { "block_param_test.cc", "block_test.cc", "break_if_test.cc", + "builder_test.cc", "constant_test.cc", "construct_test.cc", "continue_test.cc", diff --git a/src/tint/lang/core/ir/builder.h b/src/tint/lang/core/ir/builder.h index d2c98ce8797..9e963c08b07 100644 --- a/src/tint/lang/core/ir/builder.h +++ b/src/tint/lang/core/ir/builder.h @@ -136,13 +136,17 @@ class Builder { /// @param i the instruction to insert void operator()(ir::Instruction* i) { block->Append(i); } }; - /// Insertion point method that inserts the instruction after #after + /// Insertion point method that inserts the instruction after #after and updates the + /// insertion point to be after the inserted instruction. struct InsertAfter { /// The instruction to insert new instructions after ir::Instruction* after = nullptr; /// The insertion point function /// @param i the instruction to insert - void operator()(ir::Instruction* i) { i->InsertAfter(after); } + void operator()(ir::Instruction* i) { + i->InsertAfter(after); + after = i; + } }; /// Insertion point method that inserts the instruction before #before struct InsertBefore { diff --git a/src/tint/lang/core/ir/builder_test.cc b/src/tint/lang/core/ir/builder_test.cc new file mode 100644 index 00000000000..91bb796425b --- /dev/null +++ b/src/tint/lang/core/ir/builder_test.cc @@ -0,0 +1,257 @@ +// Copyright 2024 The Dawn & Tint Authors +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "src/tint/lang/core/ir/builder.h" + +#include "gmock/gmock.h" +#include "src/tint/lang/core/ir/ir_helper_test.h" + +namespace tint::core::ir { +namespace { + +using namespace tint::core::number_suffixes; // NOLINT +using IR_BuilderTest = IRTestHelper; + +TEST_F(IR_BuilderTest, Append) { + auto* func = b.Function("foo", ty.void_()); + b.Append(func->Block(), [&] { + b.Let("a", 1_u); + b.Let("b", 2_u); + b.Let("c", 3_u); + }); + b.Append(func->Block(), [&] { + b.Let("d", 4_u); + b.Let("e", 5_u); + b.Let("f", 6_u); + }); + EXPECT_EQ(str(), R"( +%foo = func():void { + $B1: { + %a:u32 = let 1u + %b:u32 = let 2u + %c:u32 = let 3u + %d:u32 = let 4u + %e:u32 = let 5u + %f:u32 = let 6u + } +} +)"); +} + +TEST_F(IR_BuilderTest, InsertBefore) { + auto* func = b.Function("foo", ty.void_()); + Instruction* ip = nullptr; + b.Append(func->Block(), [&] { + b.Let("a", 1_u); + ip = b.Let("b", 2_u); + b.Let("c", 3_u); + }); + b.InsertBefore(ip, [&] { + b.Let("d", 4_u); + b.Let("e", 5_u); + b.Let("f", 6_u); + }); + EXPECT_EQ(str(), R"( +%foo = func():void { + $B1: { + %a:u32 = let 1u + %d:u32 = let 4u + %e:u32 = let 5u + %f:u32 = let 6u + %b:u32 = let 2u + %c:u32 = let 3u + } +} +)"); +} + +TEST_F(IR_BuilderTest, InsertAfter) { + auto* func = b.Function("foo", ty.void_()); + Instruction* ip = nullptr; + b.Append(func->Block(), [&] { + b.Let("a", 1_u); + ip = b.Let("b", 2_u); + b.Let("c", 3_u); + }); + b.InsertAfter(ip, [&] { + b.Let("d", 4_u); + b.Let("e", 5_u); + b.Let("f", 6_u); + }); + EXPECT_EQ(str(), R"( +%foo = func():void { + $B1: { + %a:u32 = let 1u + %b:u32 = let 2u + %d:u32 = let 4u + %e:u32 = let 5u + %f:u32 = let 6u + %c:u32 = let 3u + } +} +)"); +} + +TEST_F(IR_BuilderTest, InsertAfter_InstructionResult) { + auto* func = b.Function("foo", ty.void_()); + Instruction* ip = nullptr; + b.Append(func->Block(), [&] { + b.Let("a", 1_u); + ip = b.Let("b", 2_u); + b.Let("c", 3_u); + }); + b.InsertAfter(ip->Result(0), [&] { + b.Let("d", 4_u); + b.Let("e", 5_u); + b.Let("f", 6_u); + }); + EXPECT_EQ(str(), R"( +%foo = func():void { + $B1: { + %a:u32 = let 1u + %b:u32 = let 2u + %d:u32 = let 4u + %e:u32 = let 5u + %f:u32 = let 6u + %c:u32 = let 3u + } +} +)"); +} + +TEST_F(IR_BuilderTest, InsertAfter_Param) { + auto* func = b.Function("foo", ty.void_()); + auto* param = b.FunctionParam("param", ty.u32()); + func->SetParams({param}); + b.Append(func->Block(), [&] { + b.Let("a", 1_u); + b.Let("b", 2_u); + b.Let("c", 3_u); + }); + b.InsertAfter(param, [&] { + b.Let("d", 4_u); + b.Let("e", 5_u); + b.Let("f", 6_u); + }); + EXPECT_EQ(str(), R"( +%foo = func(%param:u32):void { + $B1: { + %d:u32 = let 4u + %e:u32 = let 5u + %f:u32 = let 6u + %a:u32 = let 1u + %b:u32 = let 2u + %c:u32 = let 3u + } +} +)"); +} + +TEST_F(IR_BuilderTest, InsertAfter_Param_EmptyFunction) { + auto* func = b.Function("foo", ty.void_()); + auto* param = b.FunctionParam("param", ty.u32()); + func->SetParams({param}); + b.InsertAfter(param, [&] { + b.Let("a", 1_u); + b.Let("b", 2_u); + b.Let("c", 3_u); + }); + EXPECT_EQ(str(), R"( +%foo = func(%param:u32):void { + $B1: { + %a:u32 = let 1u + %b:u32 = let 2u + %c:u32 = let 3u + } +} +)"); +} + +TEST_F(IR_BuilderTest, InsertAfter_BlockParam) { + auto* func = b.Function("foo", ty.void_()); + b.Append(func->Block(), [&] { + auto* loop = b.Loop(); + auto* param = b.BlockParam("param", ty.u32()); + loop->Body()->SetParams({param}); + b.Append(loop->Body(), [&] { + b.Let("a", 1_u); + b.Let("b", 2_u); + b.Let("c", 3_u); + }); + b.InsertAfter(param, [&] { + b.Let("d", 4_u); + b.Let("e", 5_u); + b.Let("f", 6_u); + }); + }); + EXPECT_EQ(str(), R"( +%foo = func():void { + $B1: { + loop [b: $B2] { # loop_1 + $B2 (%param:u32): { # body + %d:u32 = let 4u + %e:u32 = let 5u + %f:u32 = let 6u + %a:u32 = let 1u + %b:u32 = let 2u + %c:u32 = let 3u + } + } + } +} +)"); +} + +TEST_F(IR_BuilderTest, InsertAfter_BlockParam_EmptyBlock) { + auto* func = b.Function("foo", ty.void_()); + b.Append(func->Block(), [&] { + auto* loop = b.Loop(); + auto* param = b.BlockParam("param", ty.u32()); + loop->Body()->SetParams({param}); + b.InsertAfter(param, [&] { + b.Let("a", 1_u); + b.Let("b", 2_u); + b.Let("c", 3_u); + }); + }); + EXPECT_EQ(str(), R"( +%foo = func():void { + $B1: { + loop [b: $B2] { # loop_1 + $B2 (%param:u32): { # body + %a:u32 = let 1u + %b:u32 = let 2u + %c:u32 = let 3u + } + } + } +} +)"); +} + +} // namespace +} // namespace tint::core::ir diff --git a/src/tint/lang/core/ir/ir_helper_test.h b/src/tint/lang/core/ir/ir_helper_test.h index d5898756624..b97757bea64 100644 --- a/src/tint/lang/core/ir/ir_helper_test.h +++ b/src/tint/lang/core/ir/ir_helper_test.h @@ -28,9 +28,12 @@ #ifndef SRC_TINT_LANG_CORE_IR_IR_HELPER_TEST_H_ #define SRC_TINT_LANG_CORE_IR_IR_HELPER_TEST_H_ +#include + #include "gtest/gtest.h" #include "src/tint/lang/core/ir/builder.h" #include "src/tint/lang/core/ir/clone_context.h" +#include "src/tint/lang/core/ir/disassembler.h" #include "src/tint/lang/core/ir/module.h" namespace tint::core::ir { @@ -51,6 +54,9 @@ class IRTestHelperBase : public BASE { /// CloneContext CloneContext clone_ctx{mod}; + + /// @returns the module as a disassembled string + std::string str() { return "\n" + ir::Disassembler(mod).Plain(); } }; using IRTestHelper = IRTestHelperBase;