mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-18 14:11:03 +00:00
34be23373b
* Handle more cases in dead member elim - Rewrite composite insert and extract operations on SpecConstnatOp. - Leaves assert for Access chain instructions, which are only allowed for kernels. - Other operations do not require any extra code will no longer cause an assert. Fixes #3284. Fixes #3282.
692 lines
23 KiB
C++
692 lines
23 KiB
C++
// Copyright (c) 2019 Google LLC
|
|
//
|
|
// 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 "source/opt/eliminate_dead_members_pass.h"
|
|
|
|
#include "ir_builder.h"
|
|
#include "source/opt/ir_context.h"
|
|
|
|
namespace {
|
|
const uint32_t kRemovedMember = 0xFFFFFFFF;
|
|
const uint32_t kSpecConstOpOpcodeIdx = 0;
|
|
}
|
|
|
|
namespace spvtools {
|
|
namespace opt {
|
|
|
|
Pass::Status EliminateDeadMembersPass::Process() {
|
|
if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
|
|
return Status::SuccessWithoutChange;
|
|
|
|
FindLiveMembers();
|
|
if (RemoveDeadMembers()) {
|
|
return Status::SuccessWithChange;
|
|
}
|
|
return Status::SuccessWithoutChange;
|
|
}
|
|
|
|
void EliminateDeadMembersPass::FindLiveMembers() {
|
|
// Until we have implemented the rewritting of OpSpecConsantOp instructions,
|
|
// we have to mark them as fully used just to be safe.
|
|
for (auto& inst : get_module()->types_values()) {
|
|
if (inst.opcode() == SpvOpSpecConstantOp) {
|
|
switch (inst.GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) {
|
|
case SpvOpCompositeExtract:
|
|
MarkMembersAsLiveForExtract(&inst);
|
|
break;
|
|
case SpvOpCompositeInsert:
|
|
// Nothing specific to do.
|
|
break;
|
|
case SpvOpAccessChain:
|
|
case SpvOpInBoundsAccessChain:
|
|
case SpvOpPtrAccessChain:
|
|
case SpvOpInBoundsPtrAccessChain:
|
|
assert(false && "Not implemented yet.");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else if (inst.opcode() == SpvOpVariable) {
|
|
switch (inst.GetSingleWordInOperand(0)) {
|
|
case SpvStorageClassInput:
|
|
case SpvStorageClassOutput:
|
|
MarkPointeeTypeAsFullUsed(inst.type_id());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const Function& func : *get_module()) {
|
|
FindLiveMembers(func);
|
|
}
|
|
}
|
|
|
|
void EliminateDeadMembersPass::FindLiveMembers(const Function& function) {
|
|
function.ForEachInst(
|
|
[this](const Instruction* inst) { FindLiveMembers(inst); });
|
|
}
|
|
|
|
void EliminateDeadMembersPass::FindLiveMembers(const Instruction* inst) {
|
|
switch (inst->opcode()) {
|
|
case SpvOpStore:
|
|
MarkMembersAsLiveForStore(inst);
|
|
break;
|
|
case SpvOpCopyMemory:
|
|
case SpvOpCopyMemorySized:
|
|
MarkMembersAsLiveForCopyMemory(inst);
|
|
break;
|
|
case SpvOpCompositeExtract:
|
|
MarkMembersAsLiveForExtract(inst);
|
|
break;
|
|
case SpvOpAccessChain:
|
|
case SpvOpInBoundsAccessChain:
|
|
case SpvOpPtrAccessChain:
|
|
case SpvOpInBoundsPtrAccessChain:
|
|
MarkMembersAsLiveForAccessChain(inst);
|
|
break;
|
|
case SpvOpReturnValue:
|
|
// This should be an issue only if we are returning from the entry point.
|
|
// However, for now I will keep it more conservative because functions are
|
|
// often inlined leaving only the entry points.
|
|
MarkOperandTypeAsFullyUsed(inst, 0);
|
|
break;
|
|
case SpvOpArrayLength:
|
|
MarkMembersAsLiveForArrayLength(inst);
|
|
break;
|
|
case SpvOpLoad:
|
|
case SpvOpCompositeInsert:
|
|
case SpvOpCompositeConstruct:
|
|
break;
|
|
default:
|
|
// This path is here for safety. All instructions that can reference
|
|
// structs in a function body should be handled above. However, this will
|
|
// keep the pass valid, but not optimal, as new instructions get added
|
|
// or if something was missed.
|
|
MarkStructOperandsAsFullyUsed(inst);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void EliminateDeadMembersPass::MarkMembersAsLiveForStore(
|
|
const Instruction* inst) {
|
|
// We should only have to mark the members as live if the store is to
|
|
// memory that is read outside of the shader. Other passes can remove all
|
|
// store to memory that is not visible outside of the shader, so we do not
|
|
// complicate the code for now.
|
|
assert(inst->opcode() == SpvOpStore);
|
|
uint32_t object_id = inst->GetSingleWordInOperand(1);
|
|
Instruction* object_inst = context()->get_def_use_mgr()->GetDef(object_id);
|
|
uint32_t object_type_id = object_inst->type_id();
|
|
MarkTypeAsFullyUsed(object_type_id);
|
|
}
|
|
|
|
void EliminateDeadMembersPass::MarkTypeAsFullyUsed(uint32_t type_id) {
|
|
Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
|
|
assert(type_inst != nullptr);
|
|
if (type_inst->opcode() != SpvOpTypeStruct) {
|
|
return;
|
|
}
|
|
|
|
// Mark every member of the current struct as used.
|
|
for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) {
|
|
used_members_[type_id].insert(i);
|
|
}
|
|
|
|
// Mark any sub struct as fully used.
|
|
for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) {
|
|
MarkTypeAsFullyUsed(type_inst->GetSingleWordInOperand(i));
|
|
}
|
|
}
|
|
|
|
void EliminateDeadMembersPass::MarkPointeeTypeAsFullUsed(uint32_t ptr_type_id) {
|
|
Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id);
|
|
assert(ptr_type_inst->opcode() == SpvOpTypePointer);
|
|
MarkTypeAsFullyUsed(ptr_type_inst->GetSingleWordInOperand(1));
|
|
}
|
|
|
|
void EliminateDeadMembersPass::MarkMembersAsLiveForCopyMemory(
|
|
const Instruction* inst) {
|
|
uint32_t target_id = inst->GetSingleWordInOperand(0);
|
|
Instruction* target_inst = get_def_use_mgr()->GetDef(target_id);
|
|
uint32_t pointer_type_id = target_inst->type_id();
|
|
Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id);
|
|
uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1);
|
|
MarkTypeAsFullyUsed(type_id);
|
|
}
|
|
|
|
void EliminateDeadMembersPass::MarkMembersAsLiveForExtract(
|
|
const Instruction* inst) {
|
|
assert(inst->opcode() == SpvOpCompositeExtract ||
|
|
(inst->opcode() == SpvOpSpecConstantOp &&
|
|
inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx) ==
|
|
SpvOpCompositeExtract));
|
|
|
|
uint32_t first_operand = (inst->opcode() == SpvOpSpecConstantOp ? 1 : 0);
|
|
uint32_t composite_id = inst->GetSingleWordInOperand(first_operand);
|
|
Instruction* composite_inst = get_def_use_mgr()->GetDef(composite_id);
|
|
uint32_t type_id = composite_inst->type_id();
|
|
|
|
for (uint32_t i = first_operand + 1; i < inst->NumInOperands(); ++i) {
|
|
Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
|
|
uint32_t member_idx = inst->GetSingleWordInOperand(i);
|
|
switch (type_inst->opcode()) {
|
|
case SpvOpTypeStruct:
|
|
used_members_[type_id].insert(member_idx);
|
|
type_id = type_inst->GetSingleWordInOperand(member_idx);
|
|
break;
|
|
case SpvOpTypeArray:
|
|
case SpvOpTypeRuntimeArray:
|
|
case SpvOpTypeVector:
|
|
case SpvOpTypeMatrix:
|
|
type_id = type_inst->GetSingleWordInOperand(0);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void EliminateDeadMembersPass::MarkMembersAsLiveForAccessChain(
|
|
const Instruction* inst) {
|
|
assert(inst->opcode() == SpvOpAccessChain ||
|
|
inst->opcode() == SpvOpInBoundsAccessChain ||
|
|
inst->opcode() == SpvOpPtrAccessChain ||
|
|
inst->opcode() == SpvOpInBoundsPtrAccessChain);
|
|
|
|
uint32_t pointer_id = inst->GetSingleWordInOperand(0);
|
|
Instruction* pointer_inst = get_def_use_mgr()->GetDef(pointer_id);
|
|
uint32_t pointer_type_id = pointer_inst->type_id();
|
|
Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id);
|
|
uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1);
|
|
|
|
analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
|
|
|
|
// For a pointer access chain, we need to skip the |element| index. It is not
|
|
// a reference to the member of a struct, and it does not change the type.
|
|
uint32_t i = (inst->opcode() == SpvOpAccessChain ||
|
|
inst->opcode() == SpvOpInBoundsAccessChain
|
|
? 1
|
|
: 2);
|
|
for (; i < inst->NumInOperands(); ++i) {
|
|
Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
|
|
switch (type_inst->opcode()) {
|
|
case SpvOpTypeStruct: {
|
|
const analysis::IntConstant* member_idx =
|
|
const_mgr->FindDeclaredConstant(inst->GetSingleWordInOperand(i))
|
|
->AsIntConstant();
|
|
assert(member_idx);
|
|
if (member_idx->type()->AsInteger()->width() == 32) {
|
|
used_members_[type_id].insert(member_idx->GetU32());
|
|
type_id = type_inst->GetSingleWordInOperand(member_idx->GetU32());
|
|
} else {
|
|
used_members_[type_id].insert(
|
|
static_cast<uint32_t>(member_idx->GetU64()));
|
|
type_id = type_inst->GetSingleWordInOperand(
|
|
static_cast<uint32_t>(member_idx->GetU64()));
|
|
}
|
|
} break;
|
|
case SpvOpTypeArray:
|
|
case SpvOpTypeRuntimeArray:
|
|
case SpvOpTypeVector:
|
|
case SpvOpTypeMatrix:
|
|
type_id = type_inst->GetSingleWordInOperand(0);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void EliminateDeadMembersPass::MarkOperandTypeAsFullyUsed(
|
|
const Instruction* inst, uint32_t in_idx) {
|
|
uint32_t op_id = inst->GetSingleWordInOperand(in_idx);
|
|
Instruction* op_inst = get_def_use_mgr()->GetDef(op_id);
|
|
MarkTypeAsFullyUsed(op_inst->type_id());
|
|
}
|
|
|
|
void EliminateDeadMembersPass::MarkMembersAsLiveForArrayLength(
|
|
const Instruction* inst) {
|
|
assert(inst->opcode() == SpvOpArrayLength);
|
|
uint32_t object_id = inst->GetSingleWordInOperand(0);
|
|
Instruction* object_inst = get_def_use_mgr()->GetDef(object_id);
|
|
uint32_t pointer_type_id = object_inst->type_id();
|
|
Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id);
|
|
uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1);
|
|
used_members_[type_id].insert(inst->GetSingleWordInOperand(1));
|
|
}
|
|
|
|
bool EliminateDeadMembersPass::RemoveDeadMembers() {
|
|
bool modified = false;
|
|
|
|
// First update all of the OpTypeStruct instructions.
|
|
get_module()->ForEachInst([&modified, this](Instruction* inst) {
|
|
switch (inst->opcode()) {
|
|
case SpvOpTypeStruct:
|
|
modified |= UpdateOpTypeStruct(inst);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
});
|
|
|
|
// Now update all of the instructions that reference the OpTypeStructs.
|
|
get_module()->ForEachInst([&modified, this](Instruction* inst) {
|
|
switch (inst->opcode()) {
|
|
case SpvOpMemberName:
|
|
modified |= UpdateOpMemberNameOrDecorate(inst);
|
|
break;
|
|
case SpvOpMemberDecorate:
|
|
modified |= UpdateOpMemberNameOrDecorate(inst);
|
|
break;
|
|
case SpvOpGroupMemberDecorate:
|
|
modified |= UpdateOpGroupMemberDecorate(inst);
|
|
break;
|
|
case SpvOpSpecConstantComposite:
|
|
case SpvOpConstantComposite:
|
|
case SpvOpCompositeConstruct:
|
|
modified |= UpdateConstantComposite(inst);
|
|
break;
|
|
case SpvOpAccessChain:
|
|
case SpvOpInBoundsAccessChain:
|
|
case SpvOpPtrAccessChain:
|
|
case SpvOpInBoundsPtrAccessChain:
|
|
modified |= UpdateAccessChain(inst);
|
|
break;
|
|
case SpvOpCompositeExtract:
|
|
modified |= UpdateCompsiteExtract(inst);
|
|
break;
|
|
case SpvOpCompositeInsert:
|
|
modified |= UpdateCompositeInsert(inst);
|
|
break;
|
|
case SpvOpArrayLength:
|
|
modified |= UpdateOpArrayLength(inst);
|
|
break;
|
|
case SpvOpSpecConstantOp:
|
|
switch (inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) {
|
|
case SpvOpCompositeExtract:
|
|
modified |= UpdateCompsiteExtract(inst);
|
|
break;
|
|
case SpvOpCompositeInsert:
|
|
modified |= UpdateCompositeInsert(inst);
|
|
break;
|
|
case SpvOpAccessChain:
|
|
case SpvOpInBoundsAccessChain:
|
|
case SpvOpPtrAccessChain:
|
|
case SpvOpInBoundsPtrAccessChain:
|
|
assert(false && "Not implemented yet.");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
});
|
|
return modified;
|
|
}
|
|
|
|
bool EliminateDeadMembersPass::UpdateOpTypeStruct(Instruction* inst) {
|
|
assert(inst->opcode() == SpvOpTypeStruct);
|
|
|
|
const auto& live_members = used_members_[inst->result_id()];
|
|
if (live_members.size() == inst->NumInOperands()) {
|
|
return false;
|
|
}
|
|
|
|
Instruction::OperandList new_operands;
|
|
for (uint32_t idx : live_members) {
|
|
new_operands.emplace_back(inst->GetInOperand(idx));
|
|
}
|
|
|
|
inst->SetInOperands(std::move(new_operands));
|
|
context()->UpdateDefUse(inst);
|
|
return true;
|
|
}
|
|
|
|
bool EliminateDeadMembersPass::UpdateOpMemberNameOrDecorate(Instruction* inst) {
|
|
assert(inst->opcode() == SpvOpMemberName ||
|
|
inst->opcode() == SpvOpMemberDecorate);
|
|
|
|
uint32_t type_id = inst->GetSingleWordInOperand(0);
|
|
auto live_members = used_members_.find(type_id);
|
|
if (live_members == used_members_.end()) {
|
|
return false;
|
|
}
|
|
|
|
uint32_t orig_member_idx = inst->GetSingleWordInOperand(1);
|
|
uint32_t new_member_idx = GetNewMemberIndex(type_id, orig_member_idx);
|
|
|
|
if (new_member_idx == kRemovedMember) {
|
|
context()->KillInst(inst);
|
|
return true;
|
|
}
|
|
|
|
if (new_member_idx == orig_member_idx) {
|
|
return false;
|
|
}
|
|
|
|
inst->SetInOperand(1, {new_member_idx});
|
|
return true;
|
|
}
|
|
|
|
bool EliminateDeadMembersPass::UpdateOpGroupMemberDecorate(Instruction* inst) {
|
|
assert(inst->opcode() == SpvOpGroupMemberDecorate);
|
|
|
|
bool modified = false;
|
|
|
|
Instruction::OperandList new_operands;
|
|
new_operands.emplace_back(inst->GetInOperand(0));
|
|
for (uint32_t i = 1; i < inst->NumInOperands(); i += 2) {
|
|
uint32_t type_id = inst->GetSingleWordInOperand(i);
|
|
uint32_t member_idx = inst->GetSingleWordInOperand(i + 1);
|
|
uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx);
|
|
|
|
if (new_member_idx == kRemovedMember) {
|
|
modified = true;
|
|
continue;
|
|
}
|
|
|
|
new_operands.emplace_back(inst->GetOperand(i));
|
|
if (new_member_idx != member_idx) {
|
|
new_operands.emplace_back(
|
|
Operand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {new_member_idx}}));
|
|
modified = true;
|
|
} else {
|
|
new_operands.emplace_back(inst->GetOperand(i + 1));
|
|
}
|
|
}
|
|
|
|
if (!modified) {
|
|
return false;
|
|
}
|
|
|
|
if (new_operands.size() == 1) {
|
|
context()->KillInst(inst);
|
|
return true;
|
|
}
|
|
|
|
inst->SetInOperands(std::move(new_operands));
|
|
context()->UpdateDefUse(inst);
|
|
return true;
|
|
}
|
|
|
|
bool EliminateDeadMembersPass::UpdateConstantComposite(Instruction* inst) {
|
|
assert(inst->opcode() == SpvOpSpecConstantComposite ||
|
|
inst->opcode() == SpvOpConstantComposite ||
|
|
inst->opcode() == SpvOpCompositeConstruct);
|
|
uint32_t type_id = inst->type_id();
|
|
|
|
bool modified = false;
|
|
Instruction::OperandList new_operands;
|
|
for (uint32_t i = 0; i < inst->NumInOperands(); ++i) {
|
|
uint32_t new_idx = GetNewMemberIndex(type_id, i);
|
|
if (new_idx == kRemovedMember) {
|
|
modified = true;
|
|
} else {
|
|
new_operands.emplace_back(inst->GetInOperand(i));
|
|
}
|
|
}
|
|
inst->SetInOperands(std::move(new_operands));
|
|
context()->UpdateDefUse(inst);
|
|
return modified;
|
|
}
|
|
|
|
bool EliminateDeadMembersPass::UpdateAccessChain(Instruction* inst) {
|
|
assert(inst->opcode() == SpvOpAccessChain ||
|
|
inst->opcode() == SpvOpInBoundsAccessChain ||
|
|
inst->opcode() == SpvOpPtrAccessChain ||
|
|
inst->opcode() == SpvOpInBoundsPtrAccessChain);
|
|
|
|
uint32_t pointer_id = inst->GetSingleWordInOperand(0);
|
|
Instruction* pointer_inst = get_def_use_mgr()->GetDef(pointer_id);
|
|
uint32_t pointer_type_id = pointer_inst->type_id();
|
|
Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id);
|
|
uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1);
|
|
|
|
analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
|
|
Instruction::OperandList new_operands;
|
|
bool modified = false;
|
|
new_operands.emplace_back(inst->GetInOperand(0));
|
|
|
|
// For pointer access chains we want to copy the element operand.
|
|
if (inst->opcode() == SpvOpPtrAccessChain ||
|
|
inst->opcode() == SpvOpInBoundsPtrAccessChain) {
|
|
new_operands.emplace_back(inst->GetInOperand(1));
|
|
}
|
|
|
|
for (uint32_t i = static_cast<uint32_t>(new_operands.size());
|
|
i < inst->NumInOperands(); ++i) {
|
|
Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
|
|
switch (type_inst->opcode()) {
|
|
case SpvOpTypeStruct: {
|
|
const analysis::IntConstant* member_idx =
|
|
const_mgr->FindDeclaredConstant(inst->GetSingleWordInOperand(i))
|
|
->AsIntConstant();
|
|
assert(member_idx);
|
|
uint32_t orig_member_idx;
|
|
if (member_idx->type()->AsInteger()->width() == 32) {
|
|
orig_member_idx = member_idx->GetU32();
|
|
} else {
|
|
orig_member_idx = static_cast<uint32_t>(member_idx->GetU64());
|
|
}
|
|
uint32_t new_member_idx = GetNewMemberIndex(type_id, orig_member_idx);
|
|
assert(new_member_idx != kRemovedMember);
|
|
if (orig_member_idx != new_member_idx) {
|
|
InstructionBuilder ir_builder(
|
|
context(), inst,
|
|
IRContext::kAnalysisDefUse |
|
|
IRContext::kAnalysisInstrToBlockMapping);
|
|
uint32_t const_id =
|
|
ir_builder.GetUintConstant(new_member_idx)->result_id();
|
|
new_operands.emplace_back(Operand({SPV_OPERAND_TYPE_ID, {const_id}}));
|
|
modified = true;
|
|
} else {
|
|
new_operands.emplace_back(inst->GetInOperand(i));
|
|
}
|
|
// The type will have already been rewritten, so use the new member
|
|
// index.
|
|
type_id = type_inst->GetSingleWordInOperand(new_member_idx);
|
|
} break;
|
|
case SpvOpTypeArray:
|
|
case SpvOpTypeRuntimeArray:
|
|
case SpvOpTypeVector:
|
|
case SpvOpTypeMatrix:
|
|
new_operands.emplace_back(inst->GetInOperand(i));
|
|
type_id = type_inst->GetSingleWordInOperand(0);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!modified) {
|
|
return false;
|
|
}
|
|
inst->SetInOperands(std::move(new_operands));
|
|
context()->UpdateDefUse(inst);
|
|
return true;
|
|
}
|
|
|
|
uint32_t EliminateDeadMembersPass::GetNewMemberIndex(uint32_t type_id,
|
|
uint32_t member_idx) {
|
|
auto live_members = used_members_.find(type_id);
|
|
if (live_members == used_members_.end()) {
|
|
return member_idx;
|
|
}
|
|
|
|
auto current_member = live_members->second.find(member_idx);
|
|
if (current_member == live_members->second.end()) {
|
|
return kRemovedMember;
|
|
}
|
|
|
|
return static_cast<uint32_t>(
|
|
std::distance(live_members->second.begin(), current_member));
|
|
}
|
|
|
|
bool EliminateDeadMembersPass::UpdateCompsiteExtract(Instruction* inst) {
|
|
assert(inst->opcode() == SpvOpCompositeExtract ||
|
|
(inst->opcode() == SpvOpSpecConstantOp &&
|
|
inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx) ==
|
|
SpvOpCompositeExtract));
|
|
|
|
uint32_t first_operand = 0;
|
|
if (inst->opcode() == SpvOpSpecConstantOp) {
|
|
first_operand = 1;
|
|
}
|
|
uint32_t object_id = inst->GetSingleWordInOperand(first_operand);
|
|
Instruction* object_inst = get_def_use_mgr()->GetDef(object_id);
|
|
uint32_t type_id = object_inst->type_id();
|
|
|
|
Instruction::OperandList new_operands;
|
|
bool modified = false;
|
|
for (uint32_t i = 0; i < first_operand + 1; i++) {
|
|
new_operands.emplace_back(inst->GetInOperand(i));
|
|
}
|
|
for (uint32_t i = first_operand + 1; i < inst->NumInOperands(); ++i) {
|
|
uint32_t member_idx = inst->GetSingleWordInOperand(i);
|
|
uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx);
|
|
assert(new_member_idx != kRemovedMember);
|
|
if (member_idx != new_member_idx) {
|
|
modified = true;
|
|
}
|
|
new_operands.emplace_back(
|
|
Operand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {new_member_idx}}));
|
|
|
|
Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
|
|
switch (type_inst->opcode()) {
|
|
case SpvOpTypeStruct:
|
|
// The type will have already been rewriten, so use the new member
|
|
// index.
|
|
type_id = type_inst->GetSingleWordInOperand(new_member_idx);
|
|
break;
|
|
case SpvOpTypeArray:
|
|
case SpvOpTypeRuntimeArray:
|
|
case SpvOpTypeVector:
|
|
case SpvOpTypeMatrix:
|
|
type_id = type_inst->GetSingleWordInOperand(0);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
if (!modified) {
|
|
return false;
|
|
}
|
|
inst->SetInOperands(std::move(new_operands));
|
|
context()->UpdateDefUse(inst);
|
|
return true;
|
|
}
|
|
|
|
bool EliminateDeadMembersPass::UpdateCompositeInsert(Instruction* inst) {
|
|
assert(inst->opcode() == SpvOpCompositeInsert ||
|
|
(inst->opcode() == SpvOpSpecConstantOp &&
|
|
inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx) ==
|
|
SpvOpCompositeInsert));
|
|
|
|
uint32_t first_operand = 0;
|
|
if (inst->opcode() == SpvOpSpecConstantOp) {
|
|
first_operand = 1;
|
|
}
|
|
|
|
uint32_t composite_id = inst->GetSingleWordInOperand(first_operand + 1);
|
|
Instruction* composite_inst = get_def_use_mgr()->GetDef(composite_id);
|
|
uint32_t type_id = composite_inst->type_id();
|
|
|
|
Instruction::OperandList new_operands;
|
|
bool modified = false;
|
|
|
|
for (uint32_t i = 0; i < first_operand + 2; ++i) {
|
|
new_operands.emplace_back(inst->GetInOperand(i));
|
|
}
|
|
for (uint32_t i = first_operand + 2; i < inst->NumInOperands(); ++i) {
|
|
uint32_t member_idx = inst->GetSingleWordInOperand(i);
|
|
uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx);
|
|
if (new_member_idx == kRemovedMember) {
|
|
context()->KillInst(inst);
|
|
return true;
|
|
}
|
|
|
|
if (member_idx != new_member_idx) {
|
|
modified = true;
|
|
}
|
|
new_operands.emplace_back(
|
|
Operand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {new_member_idx}}));
|
|
|
|
Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
|
|
switch (type_inst->opcode()) {
|
|
case SpvOpTypeStruct:
|
|
// The type will have already been rewritten, so use the new member
|
|
// index.
|
|
type_id = type_inst->GetSingleWordInOperand(new_member_idx);
|
|
break;
|
|
case SpvOpTypeArray:
|
|
case SpvOpTypeRuntimeArray:
|
|
case SpvOpTypeVector:
|
|
case SpvOpTypeMatrix:
|
|
type_id = type_inst->GetSingleWordInOperand(0);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
if (!modified) {
|
|
return false;
|
|
}
|
|
inst->SetInOperands(std::move(new_operands));
|
|
context()->UpdateDefUse(inst);
|
|
return true;
|
|
}
|
|
|
|
bool EliminateDeadMembersPass::UpdateOpArrayLength(Instruction* inst) {
|
|
uint32_t struct_id = inst->GetSingleWordInOperand(0);
|
|
Instruction* struct_inst = get_def_use_mgr()->GetDef(struct_id);
|
|
uint32_t pointer_type_id = struct_inst->type_id();
|
|
Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id);
|
|
uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1);
|
|
|
|
uint32_t member_idx = inst->GetSingleWordInOperand(1);
|
|
uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx);
|
|
assert(new_member_idx != kRemovedMember);
|
|
|
|
if (member_idx == new_member_idx) {
|
|
return false;
|
|
}
|
|
|
|
inst->SetInOperand(1, {new_member_idx});
|
|
context()->UpdateDefUse(inst);
|
|
return true;
|
|
}
|
|
|
|
void EliminateDeadMembersPass::MarkStructOperandsAsFullyUsed(
|
|
const Instruction* inst) {
|
|
if (inst->type_id() != 0) {
|
|
MarkTypeAsFullyUsed(inst->type_id());
|
|
}
|
|
|
|
inst->ForEachInId([this](const uint32_t* id) {
|
|
Instruction* instruction = get_def_use_mgr()->GetDef(*id);
|
|
if (instruction->type_id() != 0) {
|
|
MarkTypeAsFullyUsed(instruction->type_id());
|
|
}
|
|
});
|
|
}
|
|
} // namespace opt
|
|
} // namespace spvtools
|