diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt index f26c138d4d..25212605c7 100644 --- a/src/tint/CMakeLists.txt +++ b/src/tint/CMakeLists.txt @@ -666,6 +666,8 @@ if(${TINT_BUILD_IR}) ir/loop.h ir/module.cc ir/module.h + ir/register.cc + ir/register.h ir/switch.cc ir/switch.h ir/terminator.cc @@ -1330,6 +1332,7 @@ if(TINT_BUILD_TESTS) if (${TINT_BUILD_IR}) list(APPEND TINT_TEST_SRCS ir/builder_impl_test.cc + ir/register_test.cc ir/test_helper.h ) endif() diff --git a/src/tint/ir/register.cc b/src/tint/ir/register.cc new file mode 100644 index 0000000000..815cb02d80 --- /dev/null +++ b/src/tint/ir/register.cc @@ -0,0 +1,64 @@ +// Copyright 2022 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/tint/ir/register.h" + +namespace tint::ir { + +Register::Register(Id id) : kind_(Kind::kTemp), data_(id) {} + +Register::Register(f32 f) : kind_(Kind::kF32), data_(f) {} + +Register::Register(f16 f) : kind_(Kind::kF16), data_(f) {} + +Register::Register(u32 u) : kind_(Kind::kU32), data_(u) {} + +Register::Register(i32 i) : kind_(Kind::kI32), data_(i) {} + +Register::Register(bool b) : kind_(Kind::kBool), data_(b) {} + +Register::Register(Symbol s, Id id) : kind_(Kind::kVar), data_(VarData{s, id}) {} + +Register::~Register() = default; + +Register::Register(const Register& o) = default; + +Register::Register(Register&& o) = default; + +Register& Register::operator=(const Register& o) = default; + +Register& Register::operator=(Register&& o) = default; + +std::string Register::AsString() const { + switch (kind_) { + case Kind::kTemp: + return "%" + std::to_string(AsId()); + case Kind::kF32: + return std::to_string(AsF32().value); + case Kind::kF16: + return std::to_string(AsF16().value); + case Kind::kI32: + return std::to_string(AsI32().value); + case Kind::kU32: + return std::to_string(AsU32().value); + // TODO(dsinclair): Emit the symbol instead of v + case Kind::kVar: + return "%v" + std::to_string(AsVarData().id); + case Kind::kBool: + return AsBool() ? "true" : "false"; + } + return "unknown register"; +} + +} // namespace tint::ir diff --git a/src/tint/ir/register.h b/src/tint/ir/register.h new file mode 100644 index 0000000000..dd56aa9488 --- /dev/null +++ b/src/tint/ir/register.h @@ -0,0 +1,161 @@ +// Copyright 2022 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SRC_TINT_IR_REGISTER_H_ +#define SRC_TINT_IR_REGISTER_H_ + +#include +#include + +#include "src/tint/number.h" +#include "src/tint/symbol.h" + +namespace tint::ir { + +/// Register in the IR. The register can be one of several types these include, but aren't limited +/// to, `f32`, `u32`, `temp`, `var`. The type of the register determines the type of data stored +/// in the register. +class Register { + public: + /// A register id. + using Id = uint32_t; + + /// Stores data for a given variable. There will be multiple `VarData` entries for a given `id`. + /// The `id` acts like a generation number (although they aren't sequential, they are + /// increasing). As the variable is stored too a new register will be created and the the `id` + /// will be incremented. + struct VarData { + /// The symbol for the variable + Symbol sym; + /// The id for the variable. + Id id; + // TODO(dsinclair): Should var type data be stored here along side the variable info? + }; + + /// Constructor + /// @param id the id for the register + explicit Register(Id id); + + /// Constructor + /// @param s the symbol for the register + /// @param id the id for the register + Register(Symbol s, Id id); + + /// Constructor + /// @param b the `bool` value to store in the register + explicit Register(bool b); + + /// Constructor + /// @param f the `f32` value to store in the register + explicit Register(f32 f); + + /// Constructor + /// @param f the `f16` value to store in the register + explicit Register(f16 f); + + /// Constructor + /// @param u the `u32` value to store in the register + explicit Register(u32 u); + + /// Constructor + /// @param i the `i32` value to store in the register + explicit Register(i32 i); + + /// Destructor + ~Register(); + + /// Copy constructor + /// @param o the register to copy from + Register(const Register& o); + /// Move constructor + /// @param o the register to move from + Register(Register&& o); + + /// Copy assign + /// @param o the register to copy from + /// @returns this + Register& operator=(const Register& o); + /// Move assign + /// @param o the register to move from + /// @returns this + Register& operator=(Register&& o); + + /// @returns true if this is a temporary register + bool IsTemp() const { return kind_ == Kind::kTemp; } + /// @returns true if this is a f32 register + bool IsF32() const { return kind_ == Kind::kF32; } + /// @returns true if this is a f16 register + bool IsF16() const { return kind_ == Kind::kF16; } + /// @returns true if this is an i32 register + bool IsI32() const { return kind_ == Kind::kI32; } + /// @returns true if this is a u32 register + bool IsU32() const { return kind_ == Kind::kU32; } + /// @returns true if this is a var register + bool IsVar() const { return kind_ == Kind::kVar; } + /// @returns true if this is a bool register + bool IsBool() const { return kind_ == Kind::kBool; } + + /// @returns the register data as a `f32`. + /// @note, must only be called if `IsF32()` is true + f32 AsF32() const { return std::get(data_); } + /// @returns the register data as a `f16`. + /// @note, must only be called if `IsF16()` is true + f16 AsF16() const { return std::get(data_); } + /// @returns the register data as an `i32`. + /// @note, must only be called if `IsI32()` is true + i32 AsI32() const { return std::get(data_); } + /// @returns the register data as a `u32`. + /// @note, must only be called if `IsU32()` is true + u32 AsU32() const { return std::get(data_); } + /// @returns the register data as an `Id`. + /// @note, must only be called if `IsTemp()` is true + Id AsId() const { return std::get(data_); } + /// @returns the register data as a `VarData` structure. + /// @note, must only be called if `IsVar()` is true + VarData AsVarData() const { return std::get(data_); } + /// @returns the register data as a `bool`. + /// @note, must only be called if `IsBool()` is true + bool AsBool() const { return std::get(data_); } + + /// @returns the string representation of the register + std::string AsString() const; + + private: + /// The type of the register + enum class Kind { + /// A temporary allocated register + kTemp, + /// A f32 register + kF32, + /// A f16 register + kF16, + /// An i32 register + kI32, + /// A u32 register + kU32, + /// A variable register + kVar, + /// A boolean register + kBool, + }; + + /// The type of data stored in this register + Kind kind_; + /// The data stored in the register + std::variant data_; +}; + +} // namespace tint::ir + +#endif // SRC_TINT_IR_REGISTER_H_ diff --git a/src/tint/ir/register_test.cc b/src/tint/ir/register_test.cc new file mode 100644 index 0000000000..a19a773cc9 --- /dev/null +++ b/src/tint/ir/register_test.cc @@ -0,0 +1,136 @@ +// Copyright 2022 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/tint/ir/test_helper.h" + +#include "src/tint/ir/register.h" + +namespace tint::ir { +namespace { + +using namespace tint::number_suffixes; // NOLINT + +using IR_RegisterTest = TestHelper; + +TEST_F(IR_RegisterTest, f32) { + Register r(1.2_f); + EXPECT_EQ(1.2_f, r.AsF32()); + EXPECT_EQ("1.200000", r.AsString()); + + EXPECT_TRUE(r.IsF32()); + EXPECT_FALSE(r.IsF16()); + EXPECT_FALSE(r.IsI32()); + EXPECT_FALSE(r.IsU32()); + EXPECT_FALSE(r.IsTemp()); + EXPECT_FALSE(r.IsVar()); + EXPECT_FALSE(r.IsBool()); +} + +TEST_F(IR_RegisterTest, f16) { + Register r(1.1_h); + EXPECT_EQ(1.1_h, r.AsF16()); + EXPECT_EQ("1.099609", r.AsString()); + + EXPECT_FALSE(r.IsF32()); + EXPECT_TRUE(r.IsF16()); + EXPECT_FALSE(r.IsI32()); + EXPECT_FALSE(r.IsU32()); + EXPECT_FALSE(r.IsTemp()); + EXPECT_FALSE(r.IsVar()); + EXPECT_FALSE(r.IsBool()); +} + +TEST_F(IR_RegisterTest, i32) { + Register r(1_i); + EXPECT_EQ(1_i, r.AsI32()); + EXPECT_EQ("1", r.AsString()); + + EXPECT_FALSE(r.IsF32()); + EXPECT_FALSE(r.IsF16()); + EXPECT_TRUE(r.IsI32()); + EXPECT_FALSE(r.IsU32()); + EXPECT_FALSE(r.IsTemp()); + EXPECT_FALSE(r.IsVar()); + EXPECT_FALSE(r.IsBool()); +} + +TEST_F(IR_RegisterTest, u32) { + Register r(2_u); + EXPECT_EQ(2_u, r.AsU32()); + EXPECT_EQ("2", r.AsString()); + + EXPECT_FALSE(r.IsF32()); + EXPECT_FALSE(r.IsF16()); + EXPECT_FALSE(r.IsI32()); + EXPECT_TRUE(r.IsU32()); + EXPECT_FALSE(r.IsTemp()); + EXPECT_FALSE(r.IsVar()); + EXPECT_FALSE(r.IsBool()); +} + +TEST_F(IR_RegisterTest, id) { + Register r(Register::Id(4)); + EXPECT_EQ(4u, r.AsId()); + EXPECT_EQ("%4", r.AsString()); + + EXPECT_FALSE(r.IsF32()); + EXPECT_FALSE(r.IsF16()); + EXPECT_FALSE(r.IsI32()); + EXPECT_FALSE(r.IsU32()); + EXPECT_TRUE(r.IsTemp()); + EXPECT_FALSE(r.IsVar()); + EXPECT_FALSE(r.IsBool()); +} + +TEST_F(IR_RegisterTest, bool) { + Register r(false); + EXPECT_FALSE(r.AsBool()); + EXPECT_EQ("false", r.AsString()); + + r = Register(true); + EXPECT_TRUE(r.AsBool()); + EXPECT_EQ("true", r.AsString()); + + EXPECT_FALSE(r.IsF32()); + EXPECT_FALSE(r.IsF16()); + EXPECT_FALSE(r.IsI32()); + EXPECT_FALSE(r.IsU32()); + EXPECT_FALSE(r.IsTemp()); + EXPECT_FALSE(r.IsVar()); + EXPECT_TRUE(r.IsBool()); +} + +TEST_F(IR_RegisterTest, var) { + Symbol s; + Register r(s, 2); + EXPECT_EQ(2u, r.AsVarData().id); + EXPECT_EQ(s, r.AsVarData().sym); + EXPECT_EQ("%v2", r.AsString()); + + r = Register(s, 4); + EXPECT_EQ(4u, r.AsVarData().id); + EXPECT_EQ(s, r.AsVarData().sym); + EXPECT_EQ("%v4", r.AsString()); + + EXPECT_FALSE(r.IsF32()); + EXPECT_FALSE(r.IsF16()); + EXPECT_FALSE(r.IsI32()); + EXPECT_FALSE(r.IsU32()); + EXPECT_FALSE(r.IsTemp()); + EXPECT_TRUE(r.IsVar()); + EXPECT_FALSE(r.IsBool()); +} + +} // namespace +} // namespace tint::ir