|  | // Copyright 2020 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/ast/transform/transform.h" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <string> | 
|  |  | 
|  | #include "src/tint/builtin/builtin.h" | 
|  | #include "src/tint/program_builder.h" | 
|  | #include "src/tint/sem/block_statement.h" | 
|  | #include "src/tint/sem/for_loop_statement.h" | 
|  | #include "src/tint/sem/variable.h" | 
|  | #include "src/tint/type/atomic.h" | 
|  | #include "src/tint/type/depth_multisampled_texture.h" | 
|  | #include "src/tint/type/reference.h" | 
|  | #include "src/tint/type/sampler.h" | 
|  |  | 
|  | TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::Transform); | 
|  |  | 
|  | namespace tint::ast::transform { | 
|  |  | 
|  | Output::Output() = default; | 
|  | Output::Output(Program&& p) : program(std::move(p)) {} | 
|  | Transform::Transform() = default; | 
|  | Transform::~Transform() = default; | 
|  |  | 
|  | Output Transform::Run(const Program* src, const DataMap& data /* = {} */) const { | 
|  | Output output; | 
|  | if (auto program = Apply(src, data, output.data)) { | 
|  | output.program = std::move(program.value()); | 
|  | } else { | 
|  | ProgramBuilder b; | 
|  | CloneContext ctx{&b, src, /* auto_clone_symbols */ true}; | 
|  | ctx.Clone(); | 
|  | output.program = Program(std::move(b)); | 
|  | } | 
|  | return output; | 
|  | } | 
|  |  | 
|  | void Transform::RemoveStatement(CloneContext& ctx, const Statement* stmt) { | 
|  | auto* sem = ctx.src->Sem().Get(stmt); | 
|  | if (auto* block = tint::As<sem::BlockStatement>(sem->Parent())) { | 
|  | ctx.Remove(block->Declaration()->statements, stmt); | 
|  | return; | 
|  | } | 
|  | if (TINT_LIKELY(tint::Is<sem::ForLoopStatement>(sem->Parent()))) { | 
|  | ctx.Replace(stmt, static_cast<Expression*>(nullptr)); | 
|  | return; | 
|  | } | 
|  | TINT_ICE(Transform, ctx.dst->Diagnostics()) | 
|  | << "unable to remove statement from parent of type " << sem->TypeInfo().name; | 
|  | } | 
|  |  | 
|  | Type Transform::CreateASTTypeFor(CloneContext& ctx, const type::Type* ty) { | 
|  | if (ty->Is<type::Void>()) { | 
|  | return Type{}; | 
|  | } | 
|  | if (ty->Is<type::I32>()) { | 
|  | return ctx.dst->ty.i32(); | 
|  | } | 
|  | if (ty->Is<type::U32>()) { | 
|  | return ctx.dst->ty.u32(); | 
|  | } | 
|  | if (ty->Is<type::F16>()) { | 
|  | return ctx.dst->ty.f16(); | 
|  | } | 
|  | if (ty->Is<type::F32>()) { | 
|  | return ctx.dst->ty.f32(); | 
|  | } | 
|  | if (ty->Is<type::Bool>()) { | 
|  | return ctx.dst->ty.bool_(); | 
|  | } | 
|  | if (auto* m = ty->As<type::Matrix>()) { | 
|  | auto el = CreateASTTypeFor(ctx, m->type()); | 
|  | return ctx.dst->ty.mat(el, m->columns(), m->rows()); | 
|  | } | 
|  | if (auto* v = ty->As<type::Vector>()) { | 
|  | auto el = CreateASTTypeFor(ctx, v->type()); | 
|  | if (v->Packed()) { | 
|  | TINT_ASSERT(Transform, v->Width() == 3u); | 
|  | return ctx.dst->ty(builtin::Builtin::kPackedVec3, el); | 
|  | } else { | 
|  | return ctx.dst->ty.vec(el, v->Width()); | 
|  | } | 
|  | } | 
|  | if (auto* a = ty->As<type::Array>()) { | 
|  | auto el = CreateASTTypeFor(ctx, a->ElemType()); | 
|  | utils::Vector<const Attribute*, 1> attrs; | 
|  | if (!a->IsStrideImplicit()) { | 
|  | attrs.Push(ctx.dst->create<StrideAttribute>(a->Stride())); | 
|  | } | 
|  | if (a->Count()->Is<type::RuntimeArrayCount>()) { | 
|  | return ctx.dst->ty.array(el, std::move(attrs)); | 
|  | } | 
|  | if (auto* override = a->Count()->As<sem::NamedOverrideArrayCount>()) { | 
|  | auto* count = ctx.Clone(override->variable->Declaration()); | 
|  | return ctx.dst->ty.array(el, count, std::move(attrs)); | 
|  | } | 
|  | if (auto* override = a->Count()->As<sem::UnnamedOverrideArrayCount>()) { | 
|  | // If the array count is an unnamed (complex) override expression, then its not safe to | 
|  | // redeclare this type as we'd end up with two types that would not compare equal. | 
|  | // See crbug.com/tint/1764. | 
|  | // Look for a type alias for this array. | 
|  | for (auto* type_decl : ctx.src->AST().TypeDecls()) { | 
|  | if (auto* alias = type_decl->As<Alias>()) { | 
|  | if (ty == ctx.src->Sem().Get(alias)) { | 
|  | // Alias found. Use the alias name to ensure types compare equal. | 
|  | return ctx.dst->ty(ctx.Clone(alias->name->symbol)); | 
|  | } | 
|  | } | 
|  | } | 
|  | // Array is not aliased. Rebuild the array. | 
|  | auto* count = ctx.Clone(override->expr->Declaration()); | 
|  | return ctx.dst->ty.array(el, count, std::move(attrs)); | 
|  | } | 
|  | auto count = a->ConstantCount(); | 
|  | if (TINT_UNLIKELY(!count)) { | 
|  | TINT_ICE(Transform, ctx.dst->Diagnostics()) << type::Array::kErrExpectedConstantCount; | 
|  | return ctx.dst->ty.array(el, u32(1), std::move(attrs)); | 
|  | } | 
|  | return ctx.dst->ty.array(el, u32(count.value()), std::move(attrs)); | 
|  | } | 
|  | if (auto* s = ty->As<type::Struct>()) { | 
|  | return ctx.dst->ty(ctx.Clone(s->Name())); | 
|  | } | 
|  | if (auto* s = ty->As<type::Reference>()) { | 
|  | return CreateASTTypeFor(ctx, s->StoreType()); | 
|  | } | 
|  | if (auto* a = ty->As<type::Atomic>()) { | 
|  | return ctx.dst->ty.atomic(CreateASTTypeFor(ctx, a->Type())); | 
|  | } | 
|  | if (auto* t = ty->As<type::DepthTexture>()) { | 
|  | return ctx.dst->ty.depth_texture(t->dim()); | 
|  | } | 
|  | if (auto* t = ty->As<type::DepthMultisampledTexture>()) { | 
|  | return ctx.dst->ty.depth_multisampled_texture(t->dim()); | 
|  | } | 
|  | if (ty->Is<type::ExternalTexture>()) { | 
|  | return ctx.dst->ty.external_texture(); | 
|  | } | 
|  | if (auto* t = ty->As<type::MultisampledTexture>()) { | 
|  | return ctx.dst->ty.multisampled_texture(t->dim(), CreateASTTypeFor(ctx, t->type())); | 
|  | } | 
|  | if (auto* t = ty->As<type::SampledTexture>()) { | 
|  | return ctx.dst->ty.sampled_texture(t->dim(), CreateASTTypeFor(ctx, t->type())); | 
|  | } | 
|  | if (auto* t = ty->As<type::StorageTexture>()) { | 
|  | return ctx.dst->ty.storage_texture(t->dim(), t->texel_format(), t->access()); | 
|  | } | 
|  | if (auto* s = ty->As<type::Sampler>()) { | 
|  | return ctx.dst->ty.sampler(s->kind()); | 
|  | } | 
|  | if (auto* p = ty->As<type::Pointer>()) { | 
|  | // Note: type::Pointer always has an inferred access, but WGSL only allows an explicit | 
|  | // access in the 'storage' address space. | 
|  | auto address_space = p->AddressSpace(); | 
|  | auto access = address_space == builtin::AddressSpace::kStorage | 
|  | ? p->Access() | 
|  | : builtin::Access::kUndefined; | 
|  | return ctx.dst->ty.pointer(CreateASTTypeFor(ctx, p->StoreType()), address_space, access); | 
|  | } | 
|  | TINT_UNREACHABLE(Transform, ctx.dst->Diagnostics()) | 
|  | << "Unhandled type: " << ty->TypeInfo().name; | 
|  | return Type{}; | 
|  | } | 
|  |  | 
|  | }  // namespace tint::ast::transform |