blob: ad0d3f325d8f30a8324e9763f20e8ac451e6a2e9 [file] [log] [blame]
// 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/spirv/reader/lower/vector_element_pointer.h"
#include <utility>
#include "src/tint/lang/core/ir/builder.h"
#include "src/tint/lang/core/ir/module.h"
#include "src/tint/lang/core/ir/validator.h"
namespace tint::spirv::reader::lower {
namespace {
using namespace tint::core::fluent_types; // NOLINT
/// PIMPL state for the transform.
struct State {
/// The IR module.
core::ir::Module& ir;
/// The IR builder.
core::ir::Builder b{ir};
/// The type manager.
core::type::Manager& ty{ir.Types()};
/// Access is an access instruction and the type of the vector that it produces a pointer to.
struct Access {
/// The access instruction.
core::ir::Access* inst;
/// The vector type being accessed.
const core::type::Type* type;
};
/// Process the module.
void Process() {
// Find the access instructions that need to be replaced.
Vector<Access, 8> worklist;
for (auto* inst : ir.instructions.Objects()) {
if (!inst->Alive()) {
continue;
}
if (auto* access = inst->As<core::ir::Access>()) {
auto* source_ty = access->Object()->Type();
if (!source_ty->Is<core::type::Pointer>()) {
continue;
}
source_ty = source_ty->UnwrapPtr();
// Step through the indices of the access instruction to check for vector types.
for (auto* idx : access->Indices()) {
if (source_ty->Is<core::type::Vector>()) {
// Found an access that is indexing into a vector pointer.
worklist.Push(Access{access, source_ty});
break;
}
// Update the current source type based on the next index.
if (auto* constant = idx->As<core::ir::Constant>()) {
auto i = constant->Value()->ValueAs<u32>();
source_ty = source_ty->Element(i);
} else {
source_ty = source_ty->Elements().type;
}
}
}
}
// Replace the access instructions that we found.
for (const auto& access : worklist) {
ReplaceAccess(access);
}
}
/// Replace an access instruction with {load,store}_vector_element instructions.
/// @param access the access instruction to replace
void ReplaceAccess(const Access& access) {
auto* object = access.inst->Object();
if (access.inst->Indices().Length() > 1) {
// Create a new access instruction that stops at the vector pointer.
Vector<core::ir::Value*, 8> partial_indices{access.inst->Indices()};
partial_indices.Pop();
auto addrspace = object->Type()->As<core::type::Pointer>()->AddressSpace();
auto* access_to_vec = b.Access(ty.ptr(addrspace, access.type), object, partial_indices);
access_to_vec->InsertBefore(access.inst);
object = access_to_vec->Result(0);
}
// Replace all uses of the original access instruction.
auto* index = access.inst->Indices().Back();
ReplaceAccessUses(access.inst, object, index);
// Destroy the original access instruction.
access.inst->Destroy();
}
/// Replace all uses of an access instruction with {load,store}_vector_element instructions.
/// @param access the access instruction to replace
/// @param object the pointer-to-vector source object
/// @param index the index of the vector element
void ReplaceAccessUses(core::ir::Access* access,
core::ir::Value* object,
core::ir::Value* index) {
Vector<core::ir::Instruction*, 4> to_destroy;
access->Result(0)->ForEachUse([&](core::ir::Usage use) {
Switch(
use.instruction,
[&](core::ir::Load* load) {
auto* lve = b.LoadVectorElement(object, index);
lve->InsertBefore(load);
load->Result(0)->ReplaceAllUsesWith(lve->Result(0));
to_destroy.Push(load);
},
[&](core::ir::Store* store) {
auto* sve = b.StoreVectorElement(object, index, store->From());
sve->InsertBefore(store);
to_destroy.Push(store);
},
[&](core::ir::Access* noop_access) {
TINT_ASSERT(noop_access->Indices().IsEmpty());
ReplaceAccessUses(noop_access, object, index);
to_destroy.Push(noop_access);
},
TINT_ICE_ON_NO_MATCH);
});
// Clean up old instructions.
for (auto* inst : to_destroy) {
inst->Destroy();
}
}
};
} // namespace
Result<SuccessType> VectorElementPointer(core::ir::Module& ir) {
auto result = ValidateAndDumpIfNeeded(ir, "VectorElementPointer transform",
EnumSet<core::ir::Capability>{
core::ir::Capability::kAllowVectorElementPointer,
});
if (result != Success) {
return result.Failure();
}
State{ir}.Process();
return Success;
}
} // namespace tint::spirv::reader::lower