| // Copyright 2023 The Tint Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "src/tint/resolver/builtin_structs.h" |
| |
| #include <algorithm> |
| #include <string> |
| #include <utility> |
| |
| #include "src/tint/program_builder.h" |
| #include "src/tint/switch.h" |
| #include "src/tint/type/abstract_float.h" |
| #include "src/tint/type/abstract_int.h" |
| #include "src/tint/type/vector.h" |
| |
| namespace tint::resolver { |
| |
| namespace { |
| |
| struct NameAndType { |
| std::string_view name; |
| const type::Type* type; |
| }; |
| |
| sem::Struct* BuildStruct(ProgramBuilder& b, |
| builtin::Builtin name, |
| std::initializer_list<NameAndType> member_names_and_types) { |
| uint32_t offset = 0; |
| uint32_t max_align = 0; |
| utils::Vector<const sem::StructMember*, 4> members; |
| for (auto& m : member_names_and_types) { |
| uint32_t align = std::max<uint32_t>(m.type->Align(), 1); |
| uint32_t size = m.type->Size(); |
| offset = utils::RoundUp(align, offset); |
| max_align = std::max(max_align, align); |
| members.Push(b.create<sem::StructMember>( |
| /* declaration */ nullptr, |
| /* source */ Source{}, |
| /* name */ b.Sym(m.name), |
| /* type */ m.type, |
| /* index */ static_cast<uint32_t>(members.Length()), |
| /* offset */ offset, |
| /* align */ align, |
| /* size */ size, |
| /* attributes */ type::StructMemberAttributes{})); |
| offset += size; |
| } |
| uint32_t size_without_padding = offset; |
| uint32_t size_with_padding = utils::RoundUp(max_align, offset); |
| return b.create<sem::Struct>( |
| /* declaration */ nullptr, |
| /* source */ Source{}, |
| /* name */ b.Sym(name), |
| /* members */ std::move(members), |
| /* align */ max_align, |
| /* size */ size_with_padding, |
| /* size_no_padding */ size_without_padding); |
| } |
| } // namespace |
| |
| constexpr std::array kModfVecF32Names{ |
| builtin::Builtin::kModfResultVec2F32, |
| builtin::Builtin::kModfResultVec3F32, |
| builtin::Builtin::kModfResultVec4F32, |
| }; |
| constexpr std::array kModfVecF16Names{ |
| builtin::Builtin::kModfResultVec2F16, |
| builtin::Builtin::kModfResultVec3F16, |
| builtin::Builtin::kModfResultVec4F16, |
| }; |
| constexpr std::array kModfVecAbstractNames{ |
| builtin::Builtin::kModfResultVec2Abstract, |
| builtin::Builtin::kModfResultVec3Abstract, |
| builtin::Builtin::kModfResultVec4Abstract, |
| }; |
| |
| sem::Struct* CreateModfResult(ProgramBuilder& b, const type::Type* ty) { |
| return Switch( |
| ty, |
| [&](const type::F32*) { |
| return BuildStruct(b, builtin::Builtin::kModfResultF32, {{"fract", ty}, {"whole", ty}}); |
| }, // |
| [&](const type::F16*) { |
| return BuildStruct(b, builtin::Builtin::kModfResultF16, {{"fract", ty}, {"whole", ty}}); |
| }, |
| [&](const type::AbstractFloat*) { |
| auto* abstract = BuildStruct(b, builtin::Builtin::kModfResultAbstract, |
| {{"fract", ty}, {"whole", ty}}); |
| auto* f32 = b.create<type::F32>(); |
| auto* f16 = b.create<type::F16>(); |
| abstract->SetConcreteTypes(utils::Vector{ |
| BuildStruct(b, builtin::Builtin::kModfResultF32, {{"fract", f32}, {"whole", f32}}), |
| BuildStruct(b, builtin::Builtin::kModfResultF16, {{"fract", f16}, {"whole", f16}}), |
| }); |
| return abstract; |
| }, |
| [&](const type::Vector* vec) { |
| auto width = vec->Width(); |
| return Switch( |
| vec->type(), // |
| [&](const type::F32*) { |
| return BuildStruct(b, kModfVecF32Names[width - 2], |
| {{"fract", vec}, {"whole", vec}}); |
| }, |
| [&](const type::F16*) { |
| return BuildStruct(b, kModfVecF16Names[width - 2], |
| {{"fract", vec}, {"whole", vec}}); |
| }, |
| [&](const type::AbstractFloat*) { |
| auto* vec_f32 = b.create<type::Vector>(b.create<type::F32>(), width); |
| auto* vec_f16 = b.create<type::Vector>(b.create<type::F16>(), width); |
| auto* abstract = BuildStruct(b, kModfVecAbstractNames[width - 2], |
| {{"fract", vec}, {"whole", vec}}); |
| abstract->SetConcreteTypes(utils::Vector{ |
| BuildStruct(b, kModfVecF32Names[width - 2], |
| {{"fract", vec_f32}, {"whole", vec_f32}}), |
| BuildStruct(b, kModfVecF16Names[width - 2], |
| {{"fract", vec_f16}, {"whole", vec_f16}}), |
| }); |
| return abstract; |
| }, |
| [&](Default) { |
| TINT_ICE(Resolver, b.Diagnostics()) |
| << "unhandled modf type: " << b.FriendlyName(ty); |
| return nullptr; |
| }); |
| }, |
| [&](Default) { |
| TINT_ICE(Resolver, b.Diagnostics()) << "unhandled modf type: " << b.FriendlyName(ty); |
| return nullptr; |
| }); |
| } |
| |
| constexpr std::array kFrexpVecF32Names{ |
| builtin::Builtin::kFrexpResultVec2F32, |
| builtin::Builtin::kFrexpResultVec3F32, |
| builtin::Builtin::kFrexpResultVec4F32, |
| }; |
| constexpr std::array kFrexpVecF16Names{ |
| builtin::Builtin::kFrexpResultVec2F16, |
| builtin::Builtin::kFrexpResultVec3F16, |
| builtin::Builtin::kFrexpResultVec4F16, |
| }; |
| constexpr std::array kFrexpVecAbstractNames{ |
| builtin::Builtin::kFrexpResultVec2Abstract, |
| builtin::Builtin::kFrexpResultVec3Abstract, |
| builtin::Builtin::kFrexpResultVec4Abstract, |
| }; |
| sem::Struct* CreateFrexpResult(ProgramBuilder& b, const type::Type* ty) { |
| return Switch( |
| ty, // |
| [&](const type::F32*) { |
| auto* i32 = b.create<type::I32>(); |
| return BuildStruct(b, builtin::Builtin::kFrexpResultF32, {{"fract", ty}, {"exp", i32}}); |
| }, |
| [&](const type::F16*) { |
| auto* i32 = b.create<type::I32>(); |
| return BuildStruct(b, builtin::Builtin::kFrexpResultF16, {{"fract", ty}, {"exp", i32}}); |
| }, |
| [&](const type::AbstractFloat*) { |
| auto* f32 = b.create<type::F32>(); |
| auto* f16 = b.create<type::F16>(); |
| auto* i32 = b.create<type::I32>(); |
| auto* ai = b.create<type::AbstractInt>(); |
| auto* abstract = BuildStruct(b, builtin::Builtin::kFrexpResultAbstract, |
| {{"fract", ty}, {"exp", ai}}); |
| abstract->SetConcreteTypes(utils::Vector{ |
| BuildStruct(b, builtin::Builtin::kFrexpResultF32, {{"fract", f32}, {"exp", i32}}), |
| BuildStruct(b, builtin::Builtin::kFrexpResultF16, {{"fract", f16}, {"exp", i32}}), |
| }); |
| return abstract; |
| }, |
| [&](const type::Vector* vec) { |
| auto width = vec->Width(); |
| return Switch( |
| vec->type(), // |
| [&](const type::F32*) { |
| auto* vec_i32 = b.create<type::Vector>(b.create<type::I32>(), width); |
| return BuildStruct(b, kFrexpVecF32Names[width - 2], |
| {{"fract", ty}, {"exp", vec_i32}}); |
| }, |
| [&](const type::F16*) { |
| auto* vec_i32 = b.create<type::Vector>(b.create<type::I32>(), width); |
| return BuildStruct(b, kFrexpVecF16Names[width - 2], |
| {{"fract", ty}, {"exp", vec_i32}}); |
| }, |
| [&](const type::AbstractFloat*) { |
| auto* vec_f32 = b.create<type::Vector>(b.create<type::F32>(), width); |
| auto* vec_f16 = b.create<type::Vector>(b.create<type::F16>(), width); |
| auto* vec_i32 = b.create<type::Vector>(b.create<type::I32>(), width); |
| auto* vec_ai = b.create<type::Vector>(b.create<type::AbstractInt>(), width); |
| auto* abstract = BuildStruct(b, kFrexpVecAbstractNames[width - 2], |
| {{"fract", ty}, {"exp", vec_ai}}); |
| abstract->SetConcreteTypes(utils::Vector{ |
| BuildStruct(b, kFrexpVecF32Names[width - 2], |
| {{"fract", vec_f32}, {"exp", vec_i32}}), |
| BuildStruct(b, kFrexpVecF16Names[width - 2], |
| {{"fract", vec_f16}, {"exp", vec_i32}}), |
| }); |
| return abstract; |
| }, |
| [&](Default) { |
| TINT_ICE(Resolver, b.Diagnostics()) |
| << "unhandled frexp type: " << b.FriendlyName(ty); |
| return nullptr; |
| }); |
| }, |
| [&](Default) { |
| TINT_ICE(Resolver, b.Diagnostics()) << "unhandled frexp type: " << b.FriendlyName(ty); |
| return nullptr; |
| }); |
| } |
| |
| sem::Struct* CreateAtomicCompareExchangeResult(ProgramBuilder& b, const type::Type* ty) { |
| return Switch( |
| ty, // |
| [&](const type::I32*) { |
| return BuildStruct(b, builtin::Builtin::kAtomicCompareExchangeResultI32, |
| {{"old_value", ty}, {"exchanged", b.create<type::Bool>()}}); |
| }, |
| [&](const type::U32*) { |
| return BuildStruct(b, builtin::Builtin::kAtomicCompareExchangeResultU32, |
| {{"old_value", ty}, {"exchanged", b.create<type::Bool>()}}); |
| }, |
| [&](Default) { |
| TINT_ICE(Resolver, b.Diagnostics()) |
| << "unhandled atomic_compare_exchange type: " << b.FriendlyName(ty); |
| return nullptr; |
| }); |
| } |
| |
| } // namespace tint::resolver |