| // Copyright 2020 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/wgsl/helpers/append_vector.h" |
| |
| #include <utility> |
| #include <vector> |
| |
| #include "src/tint/lang/wgsl/sem/call.h" |
| #include "src/tint/lang/wgsl/sem/value_constructor.h" |
| #include "src/tint/lang/wgsl/sem/value_conversion.h" |
| #include "src/tint/lang/wgsl/sem/value_expression.h" |
| #include "src/tint/utils/containers/transform.h" |
| #include "src/tint/utils/rtti/switch.h" |
| |
| using namespace tint::core::number_suffixes; // NOLINT |
| |
| namespace tint::wgsl { |
| namespace { |
| |
| struct VectorConstructorInfo { |
| const sem::Call* call = nullptr; |
| const sem::ValueConstructor* ctor = nullptr; |
| operator bool() const { return call != nullptr; } |
| }; |
| VectorConstructorInfo AsVectorConstructor(const sem::ValueExpression* expr) { |
| if (auto* call = expr->As<sem::Call>()) { |
| if (auto* ctor = call->Target()->As<sem::ValueConstructor>()) { |
| if (ctor->ReturnType()->Is<core::type::Vector>()) { |
| return {call, ctor}; |
| } |
| } |
| } |
| return {}; |
| } |
| |
| const sem::ValueExpression* Zero(ProgramBuilder& b, |
| const core::type::Type* ty, |
| const sem::Statement* stmt) { |
| const ast::Expression* expr = nullptr; |
| if (ty->Is<core::type::I32>()) { |
| expr = b.Expr(0_i); |
| } else if (ty->Is<core::type::U32>()) { |
| expr = b.Expr(0_u); |
| } else if (ty->Is<core::type::F32>()) { |
| expr = b.Expr(0_f); |
| } else if (ty->Is<core::type::Bool>()) { |
| expr = b.Expr(false); |
| } else { |
| TINT_UNREACHABLE() << "unsupported vector element type: " << ty->TypeInfo().name; |
| return nullptr; |
| } |
| auto* sem = b.create<sem::ValueExpression>(expr, ty, core::EvaluationStage::kRuntime, stmt, |
| /* constant_value */ nullptr, |
| /* has_side_effects */ false); |
| b.Sem().Add(expr, sem); |
| return sem; |
| } |
| |
| } // namespace |
| |
| const sem::Call* AppendVector(ProgramBuilder* b, |
| const ast::Expression* vector_ast, |
| const ast::Expression* scalar_ast) { |
| uint32_t packed_size; |
| const core::type::Type* packed_el_sem_ty; |
| auto* vector_sem = b->Sem().GetVal(vector_ast); |
| auto* scalar_sem = b->Sem().GetVal(scalar_ast); |
| auto* vector_ty = vector_sem->Type()->UnwrapRef(); |
| if (auto* vec = vector_ty->As<core::type::Vector>()) { |
| packed_size = vec->Width() + 1; |
| packed_el_sem_ty = vec->type(); |
| } else { |
| packed_size = 2; |
| packed_el_sem_ty = vector_ty; |
| } |
| |
| auto packed_el_ast_ty = Switch( |
| packed_el_sem_ty, // |
| [&](const core::type::I32*) { return b->ty.i32(); }, |
| [&](const core::type::U32*) { return b->ty.u32(); }, |
| [&](const core::type::F32*) { return b->ty.f32(); }, |
| [&](const core::type::Bool*) { return b->ty.bool_(); }, // |
| TINT_ICE_ON_NO_MATCH); |
| |
| auto* statement = vector_sem->Stmt(); |
| |
| auto packed_ast_ty = b->ty.vec(packed_el_ast_ty, packed_size); |
| auto* packed_sem_ty = b->create<core::type::Vector>(packed_el_sem_ty, packed_size); |
| |
| // If the coordinates are already passed in a vector constructor, with only |
| // scalar components supplied, extract the elements into the new vector |
| // instead of nesting a vector-in-vector. |
| // If the coordinates are a zero-constructor of the vector, then expand that |
| // to scalar zeros. |
| // The other cases for a nested vector constructor are when it is used |
| // to convert a vector of a different type, e.g. vec2<i32>(vec2<u32>()). |
| // In that case, preserve the original argument, or you'll get a type error. |
| |
| Vector<const sem::ValueExpression*, 4> packed; |
| if (auto vc = AsVectorConstructor(vector_sem)) { |
| const auto num_supplied = vc.call->Arguments().Length(); |
| if (num_supplied == 0) { |
| // Zero-value vector constructor. Populate with zeros |
| for (uint32_t i = 0; i < packed_size - 1; i++) { |
| auto* zero = Zero(*b, packed_el_sem_ty, statement); |
| packed.Push(zero); |
| } |
| } else if (num_supplied + 1 == packed_size) { |
| // All vector components were supplied as scalars. Pass them through. |
| packed = vc.call->Arguments(); |
| } |
| } |
| if (packed.IsEmpty()) { |
| // The special cases didn't occur. Use the vector argument as-is. |
| packed.Push(vector_sem); |
| } |
| |
| if (packed_el_sem_ty != scalar_sem->Type()->UnwrapRef()) { |
| // Cast scalar to the vector element type |
| auto* scalar_cast_ast = b->Call(packed_el_ast_ty, scalar_ast); |
| auto* param = b->create<sem::Parameter>(nullptr, 0u, scalar_sem->Type()->UnwrapRef()); |
| auto* scalar_cast_target = b->create<sem::ValueConversion>(packed_el_sem_ty, param, |
| core::EvaluationStage::kRuntime); |
| auto* scalar_cast_sem = b->create<sem::Call>( |
| scalar_cast_ast, scalar_cast_target, core::EvaluationStage::kRuntime, |
| Vector<const sem::ValueExpression*, 1>{scalar_sem}, statement, |
| /* constant_value */ nullptr, /* has_side_effects */ false); |
| b->Sem().Add(scalar_cast_ast, scalar_cast_sem); |
| packed.Push(scalar_cast_sem); |
| } else { |
| packed.Push(scalar_sem); |
| } |
| |
| auto* ctor_ast = |
| b->Call(packed_ast_ty, tint::Transform(packed, [&](const sem::ValueExpression* expr) { |
| return expr->Declaration(); |
| })); |
| auto* ctor_target = b->create<sem::ValueConstructor>( |
| packed_sem_ty, |
| tint::Transform(packed, |
| [&](const tint::sem::ValueExpression* arg, size_t i) { |
| return b->create<sem::Parameter>(nullptr, static_cast<uint32_t>(i), |
| arg->Type()->UnwrapRef()); |
| }), |
| core::EvaluationStage::kRuntime); |
| auto* ctor_sem = b->create<sem::Call>(ctor_ast, ctor_target, core::EvaluationStage::kRuntime, |
| std::move(packed), statement, |
| /* constant_value */ nullptr, |
| /* has_side_effects */ false); |
| b->Sem().Add(ctor_ast, ctor_sem); |
| return ctor_sem; |
| } |
| |
| } // namespace tint::wgsl |