2017-11-30 22:03:06 +00:00
|
|
|
// Copyright (c) 2017 Google Inc.
|
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
|
2018-08-03 19:06:09 +00:00
|
|
|
#include "source/opt/scalar_replacement_pass.h"
|
2017-11-30 22:03:06 +00:00
|
|
|
|
2018-08-03 19:06:09 +00:00
|
|
|
#include <algorithm>
|
2017-11-30 22:03:06 +00:00
|
|
|
#include <queue>
|
2017-12-08 20:33:19 +00:00
|
|
|
#include <tuple>
|
2018-08-03 19:06:09 +00:00
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
#include "source/extensions.h"
|
|
|
|
#include "source/opt/reflect.h"
|
|
|
|
#include "source/opt/types.h"
|
2018-08-14 16:44:54 +00:00
|
|
|
#include "source/util/make_unique.h"
|
2017-11-30 22:03:06 +00:00
|
|
|
|
|
|
|
namespace spvtools {
|
|
|
|
namespace opt {
|
2022-11-17 18:02:50 +00:00
|
|
|
namespace {
|
|
|
|
constexpr uint32_t kDebugValueOperandValueIndex = 5;
|
|
|
|
constexpr uint32_t kDebugValueOperandExpressionIndex = 6;
|
|
|
|
constexpr uint32_t kDebugDeclareOperandVariableIndex = 5;
|
|
|
|
} // namespace
|
2017-11-30 22:03:06 +00:00
|
|
|
|
2018-07-12 13:08:45 +00:00
|
|
|
Pass::Status ScalarReplacementPass::Process() {
|
2017-11-30 22:03:06 +00:00
|
|
|
Status status = Status::SuccessWithoutChange;
|
|
|
|
for (auto& f : *get_module()) {
|
2021-10-28 15:54:37 +00:00
|
|
|
if (f.IsDeclaration()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-11-30 22:03:06 +00:00
|
|
|
Status functionStatus = ProcessFunction(&f);
|
|
|
|
if (functionStatus == Status::Failure)
|
|
|
|
return functionStatus;
|
|
|
|
else if (functionStatus == Status::SuccessWithChange)
|
|
|
|
status = functionStatus;
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
Pass::Status ScalarReplacementPass::ProcessFunction(Function* function) {
|
|
|
|
std::queue<Instruction*> worklist;
|
|
|
|
BasicBlock& entry = *function->begin();
|
2017-11-30 22:03:06 +00:00
|
|
|
for (auto iter = entry.begin(); iter != entry.end(); ++iter) {
|
|
|
|
// Function storage class OpVariables must appear as the first instructions
|
|
|
|
// of the entry block.
|
2022-11-04 21:27:10 +00:00
|
|
|
if (iter->opcode() != spv::Op::OpVariable) break;
|
2017-11-30 22:03:06 +00:00
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
Instruction* varInst = &*iter;
|
2017-11-30 22:03:06 +00:00
|
|
|
if (CanReplaceVariable(varInst)) {
|
|
|
|
worklist.push(varInst);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Status status = Status::SuccessWithoutChange;
|
|
|
|
while (!worklist.empty()) {
|
2018-07-12 19:14:43 +00:00
|
|
|
Instruction* varInst = worklist.front();
|
2017-11-30 22:03:06 +00:00
|
|
|
worklist.pop();
|
|
|
|
|
2019-07-26 16:33:40 +00:00
|
|
|
Status var_status = ReplaceVariable(varInst, &worklist);
|
|
|
|
if (var_status == Status::Failure)
|
|
|
|
return var_status;
|
|
|
|
else if (var_status == Status::SuccessWithChange)
|
|
|
|
status = var_status;
|
2017-11-30 22:03:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2019-07-26 16:33:40 +00:00
|
|
|
Pass::Status ScalarReplacementPass::ReplaceVariable(
|
2018-07-12 19:14:43 +00:00
|
|
|
Instruction* inst, std::queue<Instruction*>* worklist) {
|
|
|
|
std::vector<Instruction*> replacements;
|
2019-05-15 13:29:28 +00:00
|
|
|
if (!CreateReplacementVariables(inst, &replacements)) {
|
2019-07-26 16:33:40 +00:00
|
|
|
return Status::Failure;
|
2019-05-15 13:29:28 +00:00
|
|
|
}
|
2017-11-30 22:03:06 +00:00
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
std::vector<Instruction*> dead;
|
2019-08-21 17:12:42 +00:00
|
|
|
bool replaced_all_uses = get_def_use_mgr()->WhileEachUser(
|
|
|
|
inst, [this, &replacements, &dead](Instruction* user) {
|
2021-07-21 16:04:38 +00:00
|
|
|
if (user->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) {
|
2020-07-27 17:02:25 +00:00
|
|
|
if (ReplaceWholeDebugDeclare(user, replacements)) {
|
|
|
|
dead.push_back(user);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2021-07-21 16:04:38 +00:00
|
|
|
if (user->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) {
|
2020-07-27 17:02:25 +00:00
|
|
|
if (ReplaceWholeDebugValue(user, replacements)) {
|
|
|
|
dead.push_back(user);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2019-08-21 17:12:42 +00:00
|
|
|
if (!IsAnnotationInst(user->opcode())) {
|
|
|
|
switch (user->opcode()) {
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpLoad:
|
2019-08-21 17:12:42 +00:00
|
|
|
if (ReplaceWholeLoad(user, replacements)) {
|
|
|
|
dead.push_back(user);
|
|
|
|
} else {
|
|
|
|
return false;
|
2018-01-12 20:05:53 +00:00
|
|
|
}
|
2019-08-21 17:12:42 +00:00
|
|
|
break;
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpStore:
|
2019-08-21 17:12:42 +00:00
|
|
|
if (ReplaceWholeStore(user, replacements)) {
|
|
|
|
dead.push_back(user);
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpAccessChain:
|
|
|
|
case spv::Op::OpInBoundsAccessChain:
|
2019-08-21 17:12:42 +00:00
|
|
|
if (ReplaceAccessChain(user, replacements))
|
|
|
|
dead.push_back(user);
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
break;
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpName:
|
|
|
|
case spv::Op::OpMemberName:
|
2019-08-21 17:12:42 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(false && "Unexpected opcode");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
|
|
|
if (replaced_all_uses) {
|
2019-07-26 16:33:40 +00:00
|
|
|
dead.push_back(inst);
|
2019-08-21 17:12:42 +00:00
|
|
|
} else {
|
|
|
|
return Status::Failure;
|
|
|
|
}
|
2019-07-26 16:33:40 +00:00
|
|
|
|
|
|
|
// If there are no dead instructions to clean up, return with no changes.
|
|
|
|
if (dead.empty()) return Status::SuccessWithoutChange;
|
2017-11-30 22:03:06 +00:00
|
|
|
|
|
|
|
// Clean up some dead code.
|
|
|
|
while (!dead.empty()) {
|
2018-07-12 19:14:43 +00:00
|
|
|
Instruction* toKill = dead.back();
|
2017-11-30 22:03:06 +00:00
|
|
|
dead.pop_back();
|
|
|
|
context()->KillInst(toKill);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Attempt to further scalarize.
|
|
|
|
for (auto var : replacements) {
|
2022-11-04 21:27:10 +00:00
|
|
|
if (var->opcode() == spv::Op::OpVariable) {
|
2018-05-11 21:02:57 +00:00
|
|
|
if (get_def_use_mgr()->NumUsers(var) == 0) {
|
|
|
|
context()->KillInst(var);
|
|
|
|
} else if (CanReplaceVariable(var)) {
|
|
|
|
worklist->push(var);
|
|
|
|
}
|
2017-11-30 22:03:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-26 16:33:40 +00:00
|
|
|
return Status::SuccessWithChange;
|
2017-11-30 22:03:06 +00:00
|
|
|
}
|
|
|
|
|
2020-07-27 17:02:25 +00:00
|
|
|
bool ScalarReplacementPass::ReplaceWholeDebugDeclare(
|
|
|
|
Instruction* dbg_decl, const std::vector<Instruction*>& replacements) {
|
|
|
|
// Insert Deref operation to the front of the operation list of |dbg_decl|.
|
|
|
|
Instruction* dbg_expr = context()->get_def_use_mgr()->GetDef(
|
|
|
|
dbg_decl->GetSingleWordOperand(kDebugValueOperandExpressionIndex));
|
|
|
|
auto* deref_expr =
|
|
|
|
context()->get_debug_info_mgr()->DerefDebugExpression(dbg_expr);
|
|
|
|
|
|
|
|
// Add DebugValue instruction with Indexes operand and Deref operation.
|
|
|
|
int32_t idx = 0;
|
|
|
|
for (const auto* var : replacements) {
|
2021-09-15 18:38:53 +00:00
|
|
|
Instruction* insert_before = var->NextNode();
|
2022-11-04 21:27:10 +00:00
|
|
|
while (insert_before->opcode() == spv::Op::OpVariable)
|
2021-09-15 18:38:53 +00:00
|
|
|
insert_before = insert_before->NextNode();
|
|
|
|
assert(insert_before != nullptr && "unexpected end of list");
|
2020-07-27 17:02:25 +00:00
|
|
|
Instruction* added_dbg_value =
|
2020-11-13 17:06:38 +00:00
|
|
|
context()->get_debug_info_mgr()->AddDebugValueForDecl(
|
|
|
|
dbg_decl, /*value_id=*/var->result_id(),
|
2021-09-15 18:38:53 +00:00
|
|
|
/*insert_before=*/insert_before, /*scope_and_line=*/dbg_decl);
|
2021-01-28 17:57:35 +00:00
|
|
|
|
2020-07-27 17:02:25 +00:00
|
|
|
if (added_dbg_value == nullptr) return false;
|
2020-11-13 17:06:38 +00:00
|
|
|
added_dbg_value->AddOperand(
|
|
|
|
{SPV_OPERAND_TYPE_ID,
|
2022-12-06 14:00:10 +00:00
|
|
|
{context()->get_constant_mgr()->GetSIntConstId(idx)}});
|
2020-11-13 17:06:38 +00:00
|
|
|
added_dbg_value->SetOperand(kDebugValueOperandExpressionIndex,
|
|
|
|
{deref_expr->result_id()});
|
|
|
|
if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) {
|
|
|
|
context()->get_def_use_mgr()->AnalyzeInstUse(added_dbg_value);
|
|
|
|
}
|
2020-07-27 17:02:25 +00:00
|
|
|
++idx;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ScalarReplacementPass::ReplaceWholeDebugValue(
|
|
|
|
Instruction* dbg_value, const std::vector<Instruction*>& replacements) {
|
|
|
|
int32_t idx = 0;
|
|
|
|
BasicBlock* block = context()->get_instr_block(dbg_value);
|
|
|
|
for (auto var : replacements) {
|
|
|
|
// Clone the DebugValue.
|
|
|
|
std::unique_ptr<Instruction> new_dbg_value(dbg_value->Clone(context()));
|
|
|
|
uint32_t new_id = TakeNextId();
|
|
|
|
if (new_id == 0) return false;
|
|
|
|
new_dbg_value->SetResultId(new_id);
|
|
|
|
// Update 'Value' operand to the |replacements|.
|
|
|
|
new_dbg_value->SetOperand(kDebugValueOperandValueIndex, {var->result_id()});
|
|
|
|
// Append 'Indexes' operand.
|
|
|
|
new_dbg_value->AddOperand(
|
|
|
|
{SPV_OPERAND_TYPE_ID,
|
2022-12-06 14:00:10 +00:00
|
|
|
{context()->get_constant_mgr()->GetSIntConstId(idx)}});
|
2020-07-27 17:02:25 +00:00
|
|
|
// Insert the new DebugValue to the basic block.
|
|
|
|
auto* added_instr = dbg_value->InsertBefore(std::move(new_dbg_value));
|
|
|
|
get_def_use_mgr()->AnalyzeInstDefUse(added_instr);
|
|
|
|
context()->set_instr_block(added_instr, block);
|
|
|
|
++idx;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-08-21 17:12:42 +00:00
|
|
|
bool ScalarReplacementPass::ReplaceWholeLoad(
|
2018-07-12 19:14:43 +00:00
|
|
|
Instruction* load, const std::vector<Instruction*>& replacements) {
|
2017-11-30 22:03:06 +00:00
|
|
|
// Replaces the load of the entire composite with a load from each replacement
|
|
|
|
// variable followed by a composite construction.
|
2018-07-12 19:14:43 +00:00
|
|
|
BasicBlock* block = context()->get_instr_block(load);
|
|
|
|
std::vector<Instruction*> loads;
|
2017-11-30 22:03:06 +00:00
|
|
|
loads.reserve(replacements.size());
|
2018-07-12 19:14:43 +00:00
|
|
|
BasicBlock::iterator where(load);
|
2017-11-30 22:03:06 +00:00
|
|
|
for (auto var : replacements) {
|
|
|
|
// Create a load of each replacement variable.
|
2022-11-04 21:27:10 +00:00
|
|
|
if (var->opcode() != spv::Op::OpVariable) {
|
2018-05-11 21:02:57 +00:00
|
|
|
loads.push_back(var);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
Instruction* type = GetStorageType(var);
|
2017-11-30 22:03:06 +00:00
|
|
|
uint32_t loadId = TakeNextId();
|
2019-08-21 17:12:42 +00:00
|
|
|
if (loadId == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
2018-07-12 19:14:43 +00:00
|
|
|
std::unique_ptr<Instruction> newLoad(
|
2022-11-04 21:27:10 +00:00
|
|
|
new Instruction(context(), spv::Op::OpLoad, type->result_id(), loadId,
|
2018-07-12 19:14:43 +00:00
|
|
|
std::initializer_list<Operand>{
|
|
|
|
{SPV_OPERAND_TYPE_ID, {var->result_id()}}}));
|
2017-11-30 22:03:06 +00:00
|
|
|
// Copy memory access attributes which start at index 1. Index 0 is the
|
|
|
|
// pointer to load.
|
|
|
|
for (uint32_t i = 1; i < load->NumInOperands(); ++i) {
|
2018-07-12 19:14:43 +00:00
|
|
|
Operand copy(load->GetInOperand(i));
|
2017-11-30 22:03:06 +00:00
|
|
|
newLoad->AddOperand(std::move(copy));
|
|
|
|
}
|
|
|
|
where = where.InsertBefore(std::move(newLoad));
|
|
|
|
get_def_use_mgr()->AnalyzeInstDefUse(&*where);
|
|
|
|
context()->set_instr_block(&*where, block);
|
2020-07-27 17:02:25 +00:00
|
|
|
where->UpdateDebugInfoFrom(load);
|
2017-11-30 22:03:06 +00:00
|
|
|
loads.push_back(&*where);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Construct a new composite.
|
|
|
|
uint32_t compositeId = TakeNextId();
|
2019-08-21 17:12:42 +00:00
|
|
|
if (compositeId == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-11-30 22:03:06 +00:00
|
|
|
where = load;
|
2022-11-04 21:27:10 +00:00
|
|
|
std::unique_ptr<Instruction> compositeConstruct(
|
|
|
|
new Instruction(context(), spv::Op::OpCompositeConstruct, load->type_id(),
|
|
|
|
compositeId, {}));
|
2017-11-30 22:03:06 +00:00
|
|
|
for (auto l : loads) {
|
2018-07-12 19:14:43 +00:00
|
|
|
Operand op(SPV_OPERAND_TYPE_ID,
|
|
|
|
std::initializer_list<uint32_t>{l->result_id()});
|
2017-11-30 22:03:06 +00:00
|
|
|
compositeConstruct->AddOperand(std::move(op));
|
|
|
|
}
|
|
|
|
where = where.InsertBefore(std::move(compositeConstruct));
|
|
|
|
get_def_use_mgr()->AnalyzeInstDefUse(&*where);
|
2020-07-27 17:02:25 +00:00
|
|
|
where->UpdateDebugInfoFrom(load);
|
2017-11-30 22:03:06 +00:00
|
|
|
context()->set_instr_block(&*where, block);
|
|
|
|
context()->ReplaceAllUsesWith(load->result_id(), compositeId);
|
2019-08-21 17:12:42 +00:00
|
|
|
return true;
|
2017-11-30 22:03:06 +00:00
|
|
|
}
|
|
|
|
|
2019-08-21 17:12:42 +00:00
|
|
|
bool ScalarReplacementPass::ReplaceWholeStore(
|
2018-07-12 19:14:43 +00:00
|
|
|
Instruction* store, const std::vector<Instruction*>& replacements) {
|
2017-11-30 22:03:06 +00:00
|
|
|
// Replaces a store to the whole composite with a series of extract and stores
|
|
|
|
// to each element.
|
|
|
|
uint32_t storeInput = store->GetSingleWordInOperand(1u);
|
2018-07-12 19:14:43 +00:00
|
|
|
BasicBlock* block = context()->get_instr_block(store);
|
|
|
|
BasicBlock::iterator where(store);
|
2017-11-30 22:03:06 +00:00
|
|
|
uint32_t elementIndex = 0;
|
|
|
|
for (auto var : replacements) {
|
|
|
|
// Create the extract.
|
2022-11-04 21:27:10 +00:00
|
|
|
if (var->opcode() != spv::Op::OpVariable) {
|
2018-05-11 21:02:57 +00:00
|
|
|
elementIndex++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
Instruction* type = GetStorageType(var);
|
2017-11-30 22:03:06 +00:00
|
|
|
uint32_t extractId = TakeNextId();
|
2019-08-21 17:12:42 +00:00
|
|
|
if (extractId == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
2018-07-12 19:14:43 +00:00
|
|
|
std::unique_ptr<Instruction> extract(new Instruction(
|
2022-11-04 21:27:10 +00:00
|
|
|
context(), spv::Op::OpCompositeExtract, type->result_id(), extractId,
|
2018-07-12 19:14:43 +00:00
|
|
|
std::initializer_list<Operand>{
|
2017-11-30 22:03:06 +00:00
|
|
|
{SPV_OPERAND_TYPE_ID, {storeInput}},
|
|
|
|
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {elementIndex++}}}));
|
|
|
|
auto iter = where.InsertBefore(std::move(extract));
|
2020-07-27 17:02:25 +00:00
|
|
|
iter->UpdateDebugInfoFrom(store);
|
2017-11-30 22:03:06 +00:00
|
|
|
get_def_use_mgr()->AnalyzeInstDefUse(&*iter);
|
|
|
|
context()->set_instr_block(&*iter, block);
|
|
|
|
|
|
|
|
// Create the store.
|
2018-07-12 19:14:43 +00:00
|
|
|
std::unique_ptr<Instruction> newStore(
|
2022-11-04 21:27:10 +00:00
|
|
|
new Instruction(context(), spv::Op::OpStore, 0, 0,
|
2018-07-12 19:14:43 +00:00
|
|
|
std::initializer_list<Operand>{
|
|
|
|
{SPV_OPERAND_TYPE_ID, {var->result_id()}},
|
|
|
|
{SPV_OPERAND_TYPE_ID, {extractId}}}));
|
2017-11-30 22:03:06 +00:00
|
|
|
// Copy memory access attributes which start at index 2. Index 0 is the
|
|
|
|
// pointer and index 1 is the data.
|
|
|
|
for (uint32_t i = 2; i < store->NumInOperands(); ++i) {
|
2018-07-12 19:14:43 +00:00
|
|
|
Operand copy(store->GetInOperand(i));
|
2017-11-30 22:03:06 +00:00
|
|
|
newStore->AddOperand(std::move(copy));
|
|
|
|
}
|
|
|
|
iter = where.InsertBefore(std::move(newStore));
|
2020-07-27 17:02:25 +00:00
|
|
|
iter->UpdateDebugInfoFrom(store);
|
2017-11-30 22:03:06 +00:00
|
|
|
get_def_use_mgr()->AnalyzeInstDefUse(&*iter);
|
|
|
|
context()->set_instr_block(&*iter, block);
|
|
|
|
}
|
2019-08-21 17:12:42 +00:00
|
|
|
return true;
|
2017-11-30 22:03:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ScalarReplacementPass::ReplaceAccessChain(
|
2018-07-12 19:14:43 +00:00
|
|
|
Instruction* chain, const std::vector<Instruction*>& replacements) {
|
2017-11-30 22:03:06 +00:00
|
|
|
// Replaces the access chain with either another access chain (with one fewer
|
|
|
|
// indexes) or a direct use of the replacement variable.
|
|
|
|
uint32_t indexId = chain->GetSingleWordInOperand(1u);
|
2018-07-12 19:14:43 +00:00
|
|
|
const Instruction* index = get_def_use_mgr()->GetDef(indexId);
|
2019-07-31 19:39:33 +00:00
|
|
|
int64_t indexValue = context()
|
|
|
|
->get_constant_mgr()
|
|
|
|
->GetConstantFromInst(index)
|
|
|
|
->GetSignExtendedValue();
|
|
|
|
if (indexValue < 0 ||
|
|
|
|
indexValue >= static_cast<int64_t>(replacements.size())) {
|
2019-07-26 16:33:40 +00:00
|
|
|
// Out of bounds access, this is illegal IR. Notice that OpAccessChain
|
|
|
|
// indexing is 0-based, so we should also reject index == size-of-array.
|
2017-11-30 22:03:06 +00:00
|
|
|
return false;
|
|
|
|
} else {
|
2019-04-26 16:52:23 +00:00
|
|
|
const Instruction* var = replacements[static_cast<size_t>(indexValue)];
|
2017-11-30 22:03:06 +00:00
|
|
|
if (chain->NumInOperands() > 2) {
|
|
|
|
// Replace input access chain with another access chain.
|
2018-07-12 19:14:43 +00:00
|
|
|
BasicBlock::iterator chainIter(chain);
|
2017-11-30 22:03:06 +00:00
|
|
|
uint32_t replacementId = TakeNextId();
|
2019-08-21 17:12:42 +00:00
|
|
|
if (replacementId == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
2018-07-12 19:14:43 +00:00
|
|
|
std::unique_ptr<Instruction> replacementChain(new Instruction(
|
2017-11-30 22:03:06 +00:00
|
|
|
context(), chain->opcode(), chain->type_id(), replacementId,
|
2018-07-12 19:14:43 +00:00
|
|
|
std::initializer_list<Operand>{
|
2017-11-30 22:03:06 +00:00
|
|
|
{SPV_OPERAND_TYPE_ID, {var->result_id()}}}));
|
|
|
|
// Add the remaining indexes.
|
|
|
|
for (uint32_t i = 2; i < chain->NumInOperands(); ++i) {
|
2018-07-12 19:14:43 +00:00
|
|
|
Operand copy(chain->GetInOperand(i));
|
2017-11-30 22:03:06 +00:00
|
|
|
replacementChain->AddOperand(std::move(copy));
|
|
|
|
}
|
2020-07-27 17:02:25 +00:00
|
|
|
replacementChain->UpdateDebugInfoFrom(chain);
|
2017-11-30 22:03:06 +00:00
|
|
|
auto iter = chainIter.InsertBefore(std::move(replacementChain));
|
|
|
|
get_def_use_mgr()->AnalyzeInstDefUse(&*iter);
|
|
|
|
context()->set_instr_block(&*iter, context()->get_instr_block(chain));
|
|
|
|
context()->ReplaceAllUsesWith(chain->result_id(), replacementId);
|
|
|
|
} else {
|
|
|
|
// Replace with a use of the variable.
|
|
|
|
context()->ReplaceAllUsesWith(chain->result_id(), var->result_id());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-05-15 13:29:28 +00:00
|
|
|
bool ScalarReplacementPass::CreateReplacementVariables(
|
2018-07-12 19:14:43 +00:00
|
|
|
Instruction* inst, std::vector<Instruction*>* replacements) {
|
|
|
|
Instruction* type = GetStorageType(inst);
|
2018-05-11 21:02:57 +00:00
|
|
|
|
2019-07-31 19:39:33 +00:00
|
|
|
std::unique_ptr<std::unordered_set<int64_t>> components_used =
|
2018-05-11 21:02:57 +00:00
|
|
|
GetUsedComponents(inst);
|
|
|
|
|
2017-11-30 22:03:06 +00:00
|
|
|
uint32_t elem = 0;
|
|
|
|
switch (type->opcode()) {
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpTypeStruct:
|
2018-05-11 21:02:57 +00:00
|
|
|
type->ForEachInOperand(
|
|
|
|
[this, inst, &elem, replacements, &components_used](uint32_t* id) {
|
|
|
|
if (!components_used || components_used->count(elem)) {
|
|
|
|
CreateVariable(*id, inst, elem, replacements);
|
|
|
|
} else {
|
2022-02-03 15:51:15 +00:00
|
|
|
replacements->push_back(GetUndef(*id));
|
2018-05-11 21:02:57 +00:00
|
|
|
}
|
|
|
|
elem++;
|
|
|
|
});
|
2017-11-30 22:03:06 +00:00
|
|
|
break;
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpTypeArray:
|
2017-11-30 22:03:06 +00:00
|
|
|
for (uint32_t i = 0; i != GetArrayLength(type); ++i) {
|
2018-05-11 21:02:57 +00:00
|
|
|
if (!components_used || components_used->count(i)) {
|
|
|
|
CreateVariable(type->GetSingleWordInOperand(0u), inst, i,
|
|
|
|
replacements);
|
|
|
|
} else {
|
2022-02-03 15:51:15 +00:00
|
|
|
uint32_t element_type_id = type->GetSingleWordInOperand(0);
|
|
|
|
replacements->push_back(GetUndef(element_type_id));
|
2018-05-11 21:02:57 +00:00
|
|
|
}
|
2017-11-30 22:03:06 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpTypeMatrix:
|
|
|
|
case spv::Op::OpTypeVector:
|
2017-11-30 22:03:06 +00:00
|
|
|
for (uint32_t i = 0; i != GetNumElements(type); ++i) {
|
|
|
|
CreateVariable(type->GetSingleWordInOperand(0u), inst, i, replacements);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
assert(false && "Unexpected type.");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
TransferAnnotations(inst, replacements);
|
2019-05-15 13:29:28 +00:00
|
|
|
return std::find(replacements->begin(), replacements->end(), nullptr) ==
|
|
|
|
replacements->end();
|
2017-11-30 22:03:06 +00:00
|
|
|
}
|
|
|
|
|
2022-02-03 15:51:15 +00:00
|
|
|
Instruction* ScalarReplacementPass::GetUndef(uint32_t type_id) {
|
|
|
|
return get_def_use_mgr()->GetDef(Type2Undef(type_id));
|
|
|
|
}
|
|
|
|
|
2017-11-30 22:03:06 +00:00
|
|
|
void ScalarReplacementPass::TransferAnnotations(
|
2018-07-12 19:14:43 +00:00
|
|
|
const Instruction* source, std::vector<Instruction*>* replacements) {
|
2017-11-30 22:03:06 +00:00
|
|
|
// Only transfer invariant and restrict decorations on the variable. There are
|
|
|
|
// no type or member decorations that are necessary to transfer.
|
|
|
|
for (auto inst :
|
|
|
|
get_decoration_mgr()->GetDecorationsFor(source->result_id(), false)) {
|
2022-11-04 21:27:10 +00:00
|
|
|
assert(inst->opcode() == spv::Op::OpDecorate);
|
|
|
|
auto decoration = spv::Decoration(inst->GetSingleWordInOperand(1u));
|
|
|
|
if (decoration == spv::Decoration::Invariant ||
|
|
|
|
decoration == spv::Decoration::Restrict) {
|
2017-11-30 22:03:06 +00:00
|
|
|
for (auto var : *replacements) {
|
2019-08-16 17:15:17 +00:00
|
|
|
if (var == nullptr) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-11-04 21:27:10 +00:00
|
|
|
std::unique_ptr<Instruction> annotation(new Instruction(
|
|
|
|
context(), spv::Op::OpDecorate, 0, 0,
|
|
|
|
std::initializer_list<Operand>{
|
|
|
|
{SPV_OPERAND_TYPE_ID, {var->result_id()}},
|
|
|
|
{SPV_OPERAND_TYPE_DECORATION, {uint32_t(decoration)}}}));
|
2017-11-30 22:03:06 +00:00
|
|
|
for (uint32_t i = 2; i < inst->NumInOperands(); ++i) {
|
2018-07-12 19:14:43 +00:00
|
|
|
Operand copy(inst->GetInOperand(i));
|
2017-11-30 22:03:06 +00:00
|
|
|
annotation->AddOperand(std::move(copy));
|
|
|
|
}
|
|
|
|
context()->AddAnnotationInst(std::move(annotation));
|
|
|
|
get_def_use_mgr()->AnalyzeInstUse(&*--context()->annotation_end());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScalarReplacementPass::CreateVariable(
|
2023-05-08 13:39:14 +00:00
|
|
|
uint32_t type_id, Instruction* var_inst, uint32_t index,
|
2018-07-12 19:14:43 +00:00
|
|
|
std::vector<Instruction*>* replacements) {
|
2023-05-08 13:39:14 +00:00
|
|
|
uint32_t ptr_id = GetOrCreatePointerType(type_id);
|
2017-11-30 22:03:06 +00:00
|
|
|
uint32_t id = TakeNextId();
|
2019-08-16 17:15:17 +00:00
|
|
|
|
|
|
|
if (id == 0) {
|
|
|
|
replacements->push_back(nullptr);
|
|
|
|
}
|
|
|
|
|
2022-11-04 21:27:10 +00:00
|
|
|
std::unique_ptr<Instruction> variable(
|
2023-05-08 13:39:14 +00:00
|
|
|
new Instruction(context(), spv::Op::OpVariable, ptr_id, id,
|
2022-11-04 21:27:10 +00:00
|
|
|
std::initializer_list<Operand>{
|
|
|
|
{SPV_OPERAND_TYPE_STORAGE_CLASS,
|
|
|
|
{uint32_t(spv::StorageClass::Function)}}}));
|
2017-11-30 22:03:06 +00:00
|
|
|
|
2023-05-08 13:39:14 +00:00
|
|
|
BasicBlock* block = context()->get_instr_block(var_inst);
|
2017-11-30 22:03:06 +00:00
|
|
|
block->begin().InsertBefore(std::move(variable));
|
2018-07-12 19:14:43 +00:00
|
|
|
Instruction* inst = &*block->begin();
|
2017-11-30 22:03:06 +00:00
|
|
|
|
|
|
|
// If varInst was initialized, make sure to initialize its replacement.
|
2023-05-08 13:39:14 +00:00
|
|
|
GetOrCreateInitialValue(var_inst, index, inst);
|
2017-11-30 22:03:06 +00:00
|
|
|
get_def_use_mgr()->AnalyzeInstDefUse(inst);
|
|
|
|
context()->set_instr_block(inst, block);
|
|
|
|
|
2023-05-08 13:39:14 +00:00
|
|
|
CopyDecorationsToVariable(var_inst, inst, index);
|
|
|
|
inst->UpdateDebugInfoFrom(var_inst);
|
2020-07-27 17:02:25 +00:00
|
|
|
|
2017-11-30 22:03:06 +00:00
|
|
|
replacements->push_back(inst);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t ScalarReplacementPass::GetOrCreatePointerType(uint32_t id) {
|
|
|
|
auto iter = pointee_to_pointer_.find(id);
|
|
|
|
if (iter != pointee_to_pointer_.end()) return iter->second;
|
|
|
|
|
2023-05-08 13:39:14 +00:00
|
|
|
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
|
|
|
uint32_t ptr_type_id =
|
|
|
|
type_mgr->FindPointerToType(id, spv::StorageClass::Function);
|
|
|
|
pointee_to_pointer_[id] = ptr_type_id;
|
|
|
|
return ptr_type_id;
|
2017-11-30 22:03:06 +00:00
|
|
|
}
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
void ScalarReplacementPass::GetOrCreateInitialValue(Instruction* source,
|
2017-11-30 22:03:06 +00:00
|
|
|
uint32_t index,
|
2018-07-12 19:14:43 +00:00
|
|
|
Instruction* newVar) {
|
2022-11-04 21:27:10 +00:00
|
|
|
assert(source->opcode() == spv::Op::OpVariable);
|
2017-11-30 22:03:06 +00:00
|
|
|
if (source->NumInOperands() < 2) return;
|
|
|
|
|
|
|
|
uint32_t initId = source->GetSingleWordInOperand(1u);
|
|
|
|
uint32_t storageId = GetStorageType(newVar)->result_id();
|
2018-07-12 19:14:43 +00:00
|
|
|
Instruction* init = get_def_use_mgr()->GetDef(initId);
|
2017-11-30 22:03:06 +00:00
|
|
|
uint32_t newInitId = 0;
|
|
|
|
// TODO(dnovillo): Refactor this with constant propagation.
|
2022-11-04 21:27:10 +00:00
|
|
|
if (init->opcode() == spv::Op::OpConstantNull) {
|
2017-11-30 22:03:06 +00:00
|
|
|
// Initialize to appropriate NULL.
|
|
|
|
auto iter = type_to_null_.find(storageId);
|
|
|
|
if (iter == type_to_null_.end()) {
|
|
|
|
newInitId = TakeNextId();
|
|
|
|
type_to_null_[storageId] = newInitId;
|
2018-07-12 19:14:43 +00:00
|
|
|
context()->AddGlobalValue(
|
2022-11-04 21:27:10 +00:00
|
|
|
MakeUnique<Instruction>(context(), spv::Op::OpConstantNull, storageId,
|
2018-07-12 19:14:43 +00:00
|
|
|
newInitId, std::initializer_list<Operand>{}));
|
|
|
|
Instruction* newNull = &*--context()->types_values_end();
|
2017-11-30 22:03:06 +00:00
|
|
|
get_def_use_mgr()->AnalyzeInstDefUse(newNull);
|
|
|
|
} else {
|
|
|
|
newInitId = iter->second;
|
|
|
|
}
|
2018-07-12 19:14:43 +00:00
|
|
|
} else if (IsSpecConstantInst(init->opcode())) {
|
2017-11-30 22:03:06 +00:00
|
|
|
// Create a new constant extract.
|
|
|
|
newInitId = TakeNextId();
|
2018-07-12 19:14:43 +00:00
|
|
|
context()->AddGlobalValue(MakeUnique<Instruction>(
|
2022-11-04 21:27:10 +00:00
|
|
|
context(), spv::Op::OpSpecConstantOp, storageId, newInitId,
|
2018-07-12 19:14:43 +00:00
|
|
|
std::initializer_list<Operand>{
|
2022-11-04 21:27:10 +00:00
|
|
|
{SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER,
|
|
|
|
{uint32_t(spv::Op::OpCompositeExtract)}},
|
2017-11-30 22:03:06 +00:00
|
|
|
{SPV_OPERAND_TYPE_ID, {init->result_id()}},
|
|
|
|
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {index}}}));
|
2018-07-12 19:14:43 +00:00
|
|
|
Instruction* newSpecConst = &*--context()->types_values_end();
|
2017-11-30 22:03:06 +00:00
|
|
|
get_def_use_mgr()->AnalyzeInstDefUse(newSpecConst);
|
2022-11-04 21:27:10 +00:00
|
|
|
} else if (init->opcode() == spv::Op::OpConstantComposite) {
|
2017-11-30 22:03:06 +00:00
|
|
|
// Get the appropriate index constant.
|
|
|
|
newInitId = init->GetSingleWordInOperand(index);
|
2018-07-12 19:14:43 +00:00
|
|
|
Instruction* element = get_def_use_mgr()->GetDef(newInitId);
|
2022-11-04 21:27:10 +00:00
|
|
|
if (element->opcode() == spv::Op::OpUndef) {
|
2017-11-30 22:03:06 +00:00
|
|
|
// Undef is not a valid initializer for a variable.
|
|
|
|
newInitId = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newInitId != 0) {
|
|
|
|
newVar->AddOperand({SPV_OPERAND_TYPE_ID, {newInitId}});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-26 16:52:23 +00:00
|
|
|
uint64_t ScalarReplacementPass::GetArrayLength(
|
2018-07-12 19:14:43 +00:00
|
|
|
const Instruction* arrayType) const {
|
2022-11-04 21:27:10 +00:00
|
|
|
assert(arrayType->opcode() == spv::Op::OpTypeArray);
|
2018-07-12 19:14:43 +00:00
|
|
|
const Instruction* length =
|
2017-11-30 22:03:06 +00:00
|
|
|
get_def_use_mgr()->GetDef(arrayType->GetSingleWordInOperand(1u));
|
2019-07-31 19:39:33 +00:00
|
|
|
return context()
|
|
|
|
->get_constant_mgr()
|
|
|
|
->GetConstantFromInst(length)
|
|
|
|
->GetZeroExtendedValue();
|
2017-11-30 22:03:06 +00:00
|
|
|
}
|
|
|
|
|
2019-04-26 16:52:23 +00:00
|
|
|
uint64_t ScalarReplacementPass::GetNumElements(const Instruction* type) const {
|
2022-11-04 21:27:10 +00:00
|
|
|
assert(type->opcode() == spv::Op::OpTypeVector ||
|
|
|
|
type->opcode() == spv::Op::OpTypeMatrix);
|
2018-07-12 19:14:43 +00:00
|
|
|
const Operand& op = type->GetInOperand(1u);
|
2017-11-30 22:03:06 +00:00
|
|
|
assert(op.words.size() <= 2);
|
2019-04-26 16:52:23 +00:00
|
|
|
uint64_t len = 0;
|
|
|
|
for (size_t i = 0; i != op.words.size(); ++i) {
|
|
|
|
len |= (static_cast<uint64_t>(op.words[i]) << (32ull * i));
|
2017-11-30 22:03:06 +00:00
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2018-10-04 13:00:24 +00:00
|
|
|
bool ScalarReplacementPass::IsSpecConstant(uint32_t id) const {
|
|
|
|
const Instruction* inst = get_def_use_mgr()->GetDef(id);
|
|
|
|
assert(inst);
|
|
|
|
return spvOpcodeIsSpecConstant(inst->opcode());
|
|
|
|
}
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
Instruction* ScalarReplacementPass::GetStorageType(
|
|
|
|
const Instruction* inst) const {
|
2022-11-04 21:27:10 +00:00
|
|
|
assert(inst->opcode() == spv::Op::OpVariable);
|
2017-11-30 22:03:06 +00:00
|
|
|
|
|
|
|
uint32_t ptrTypeId = inst->type_id();
|
|
|
|
uint32_t typeId =
|
|
|
|
get_def_use_mgr()->GetDef(ptrTypeId)->GetSingleWordInOperand(1u);
|
|
|
|
return get_def_use_mgr()->GetDef(typeId);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ScalarReplacementPass::CanReplaceVariable(
|
2018-07-12 19:14:43 +00:00
|
|
|
const Instruction* varInst) const {
|
2022-11-04 21:27:10 +00:00
|
|
|
assert(varInst->opcode() == spv::Op::OpVariable);
|
2017-11-30 22:03:06 +00:00
|
|
|
|
|
|
|
// Can only replace function scope variables.
|
2022-11-04 21:27:10 +00:00
|
|
|
if (spv::StorageClass(varInst->GetSingleWordInOperand(0u)) !=
|
|
|
|
spv::StorageClass::Function) {
|
2017-11-30 22:03:06 +00:00
|
|
|
return false;
|
2019-08-07 16:17:26 +00:00
|
|
|
}
|
2017-11-30 22:03:06 +00:00
|
|
|
|
2019-08-07 16:17:26 +00:00
|
|
|
if (!CheckTypeAnnotations(get_def_use_mgr()->GetDef(varInst->type_id()))) {
|
2017-11-30 22:03:06 +00:00
|
|
|
return false;
|
2019-08-07 16:17:26 +00:00
|
|
|
}
|
2017-11-30 22:03:06 +00:00
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
const Instruction* typeInst = GetStorageType(varInst);
|
2019-08-07 16:17:26 +00:00
|
|
|
if (!CheckType(typeInst)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!CheckAnnotations(varInst)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!CheckUses(varInst)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2017-11-30 22:03:06 +00:00
|
|
|
}
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
bool ScalarReplacementPass::CheckType(const Instruction* typeInst) const {
|
2019-08-07 16:17:26 +00:00
|
|
|
if (!CheckTypeAnnotations(typeInst)) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-11-30 22:03:06 +00:00
|
|
|
|
|
|
|
switch (typeInst->opcode()) {
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpTypeStruct:
|
2017-11-30 22:03:06 +00:00
|
|
|
// Don't bother with empty structs or very large structs.
|
|
|
|
if (typeInst->NumInOperands() == 0 ||
|
2019-08-07 16:17:26 +00:00
|
|
|
IsLargerThanSizeLimit(typeInst->NumInOperands())) {
|
2017-11-30 22:03:06 +00:00
|
|
|
return false;
|
2019-08-07 16:17:26 +00:00
|
|
|
}
|
2017-11-30 22:03:06 +00:00
|
|
|
return true;
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpTypeArray:
|
2018-10-04 13:00:24 +00:00
|
|
|
if (IsSpecConstant(typeInst->GetSingleWordInOperand(1u))) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (IsLargerThanSizeLimit(GetArrayLength(typeInst))) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-11-30 22:03:06 +00:00
|
|
|
return true;
|
2018-04-16 13:58:00 +00:00
|
|
|
// TODO(alanbaker): Develop some heuristics for when this should be
|
|
|
|
// re-enabled.
|
|
|
|
//// Specifically including matrix and vector in an attempt to reduce the
|
|
|
|
//// number of vector registers required.
|
2022-11-04 21:27:10 +00:00
|
|
|
// case spv::Op::OpTypeMatrix:
|
|
|
|
// case spv::Op::OpTypeVector:
|
2018-04-16 13:58:00 +00:00
|
|
|
// if (IsLargerThanSizeLimit(GetNumElements(typeInst))) return false;
|
|
|
|
// return true;
|
2017-11-30 22:03:06 +00:00
|
|
|
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpTypeRuntimeArray:
|
2017-11-30 22:03:06 +00:00
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ScalarReplacementPass::CheckTypeAnnotations(
|
2018-07-12 19:14:43 +00:00
|
|
|
const Instruction* typeInst) const {
|
2017-11-30 22:03:06 +00:00
|
|
|
for (auto inst :
|
|
|
|
get_decoration_mgr()->GetDecorationsFor(typeInst->result_id(), false)) {
|
|
|
|
uint32_t decoration;
|
2022-11-04 21:27:10 +00:00
|
|
|
if (inst->opcode() == spv::Op::OpDecorate) {
|
2017-11-30 22:03:06 +00:00
|
|
|
decoration = inst->GetSingleWordInOperand(1u);
|
|
|
|
} else {
|
2022-11-04 21:27:10 +00:00
|
|
|
assert(inst->opcode() == spv::Op::OpMemberDecorate);
|
2017-11-30 22:03:06 +00:00
|
|
|
decoration = inst->GetSingleWordInOperand(2u);
|
|
|
|
}
|
|
|
|
|
2022-11-04 21:27:10 +00:00
|
|
|
switch (spv::Decoration(decoration)) {
|
|
|
|
case spv::Decoration::RowMajor:
|
|
|
|
case spv::Decoration::ColMajor:
|
|
|
|
case spv::Decoration::ArrayStride:
|
|
|
|
case spv::Decoration::MatrixStride:
|
|
|
|
case spv::Decoration::CPacked:
|
|
|
|
case spv::Decoration::Invariant:
|
|
|
|
case spv::Decoration::Restrict:
|
|
|
|
case spv::Decoration::Offset:
|
|
|
|
case spv::Decoration::Alignment:
|
|
|
|
case spv::Decoration::AlignmentId:
|
|
|
|
case spv::Decoration::MaxByteOffset:
|
|
|
|
case spv::Decoration::RelaxedPrecision:
|
2023-05-08 13:39:14 +00:00
|
|
|
case spv::Decoration::AliasedPointer:
|
|
|
|
case spv::Decoration::RestrictPointer:
|
2017-11-30 22:03:06 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
bool ScalarReplacementPass::CheckAnnotations(const Instruction* varInst) const {
|
2017-11-30 22:03:06 +00:00
|
|
|
for (auto inst :
|
|
|
|
get_decoration_mgr()->GetDecorationsFor(varInst->result_id(), false)) {
|
2022-11-04 21:27:10 +00:00
|
|
|
assert(inst->opcode() == spv::Op::OpDecorate);
|
|
|
|
auto decoration = spv::Decoration(inst->GetSingleWordInOperand(1u));
|
2017-11-30 22:03:06 +00:00
|
|
|
switch (decoration) {
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Decoration::Invariant:
|
|
|
|
case spv::Decoration::Restrict:
|
|
|
|
case spv::Decoration::Alignment:
|
|
|
|
case spv::Decoration::AlignmentId:
|
|
|
|
case spv::Decoration::MaxByteOffset:
|
2023-05-08 13:39:14 +00:00
|
|
|
case spv::Decoration::AliasedPointer:
|
|
|
|
case spv::Decoration::RestrictPointer:
|
2017-11-30 22:03:06 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
bool ScalarReplacementPass::CheckUses(const Instruction* inst) const {
|
2017-11-30 22:03:06 +00:00
|
|
|
VariableStats stats = {0, 0};
|
|
|
|
bool ok = CheckUses(inst, &stats);
|
|
|
|
|
2018-02-07 02:54:58 +00:00
|
|
|
// TODO(alanbaker/greg-lunarg): Add some meaningful heuristics about when
|
|
|
|
// SRoA is costly, such as when the structure has many (unaccessed?)
|
|
|
|
// members.
|
2017-11-30 22:03:06 +00:00
|
|
|
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
bool ScalarReplacementPass::CheckUses(const Instruction* inst,
|
2017-11-30 22:03:06 +00:00
|
|
|
VariableStats* stats) const {
|
2019-08-21 17:12:42 +00:00
|
|
|
uint64_t max_legal_index = GetMaxLegalIndex(inst);
|
|
|
|
|
2017-11-30 22:03:06 +00:00
|
|
|
bool ok = true;
|
2019-08-21 17:12:42 +00:00
|
|
|
get_def_use_mgr()->ForEachUse(inst, [this, max_legal_index, stats, &ok](
|
|
|
|
const Instruction* user,
|
|
|
|
uint32_t index) {
|
2021-07-21 16:04:38 +00:00
|
|
|
if (user->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare ||
|
|
|
|
user->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) {
|
2020-07-27 17:02:25 +00:00
|
|
|
// TODO: include num_partial_accesses if it uses Fragment operation or
|
|
|
|
// DebugValue has Indexes operand.
|
|
|
|
stats->num_full_accesses++;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-08-21 17:12:42 +00:00
|
|
|
// Annotations are check as a group separately.
|
|
|
|
if (!IsAnnotationInst(user->opcode())) {
|
|
|
|
switch (user->opcode()) {
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpAccessChain:
|
|
|
|
case spv::Op::OpInBoundsAccessChain:
|
2019-08-21 17:12:42 +00:00
|
|
|
if (index == 2u && user->NumInOperands() > 1) {
|
|
|
|
uint32_t id = user->GetSingleWordInOperand(1u);
|
|
|
|
const Instruction* opInst = get_def_use_mgr()->GetDef(id);
|
|
|
|
const auto* constant =
|
|
|
|
context()->get_constant_mgr()->GetConstantFromInst(opInst);
|
|
|
|
if (!constant) {
|
2017-11-30 22:03:06 +00:00
|
|
|
ok = false;
|
2019-08-21 17:12:42 +00:00
|
|
|
} else if (constant->GetZeroExtendedValue() >= max_legal_index) {
|
|
|
|
ok = false;
|
|
|
|
} else {
|
|
|
|
if (!CheckUsesRelaxed(user)) ok = false;
|
|
|
|
}
|
|
|
|
stats->num_partial_accesses++;
|
|
|
|
} else {
|
|
|
|
ok = false;
|
2017-11-30 22:03:06 +00:00
|
|
|
}
|
2019-08-21 17:12:42 +00:00
|
|
|
break;
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpLoad:
|
2019-08-21 17:12:42 +00:00
|
|
|
if (!CheckLoad(user, index)) ok = false;
|
|
|
|
stats->num_full_accesses++;
|
|
|
|
break;
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpStore:
|
2019-08-21 17:12:42 +00:00
|
|
|
if (!CheckStore(user, index)) ok = false;
|
|
|
|
stats->num_full_accesses++;
|
|
|
|
break;
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpName:
|
|
|
|
case spv::Op::OpMemberName:
|
2019-08-21 17:12:42 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ok = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2017-11-30 22:03:06 +00:00
|
|
|
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
bool ScalarReplacementPass::CheckUsesRelaxed(const Instruction* inst) const {
|
2017-11-30 22:03:06 +00:00
|
|
|
bool ok = true;
|
|
|
|
get_def_use_mgr()->ForEachUse(
|
2018-07-12 19:14:43 +00:00
|
|
|
inst, [this, &ok](const Instruction* user, uint32_t index) {
|
2017-11-30 22:03:06 +00:00
|
|
|
switch (user->opcode()) {
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpAccessChain:
|
|
|
|
case spv::Op::OpInBoundsAccessChain:
|
2017-11-30 22:03:06 +00:00
|
|
|
if (index != 2u) {
|
|
|
|
ok = false;
|
|
|
|
} else {
|
|
|
|
if (!CheckUsesRelaxed(user)) ok = false;
|
|
|
|
}
|
|
|
|
break;
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpLoad:
|
2017-11-30 22:03:06 +00:00
|
|
|
if (!CheckLoad(user, index)) ok = false;
|
|
|
|
break;
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpStore:
|
2017-11-30 22:03:06 +00:00
|
|
|
if (!CheckStore(user, index)) ok = false;
|
|
|
|
break;
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpImageTexelPointer:
|
2021-03-16 20:40:51 +00:00
|
|
|
if (!CheckImageTexelPointer(index)) ok = false;
|
|
|
|
break;
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpExtInst:
|
2021-09-24 14:56:08 +00:00
|
|
|
if (user->GetCommonDebugOpcode() != CommonDebugInfoDebugDeclare ||
|
|
|
|
!CheckDebugDeclare(index))
|
|
|
|
ok = false;
|
|
|
|
break;
|
2017-11-30 22:03:06 +00:00
|
|
|
default:
|
|
|
|
ok = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2021-03-16 20:40:51 +00:00
|
|
|
bool ScalarReplacementPass::CheckImageTexelPointer(uint32_t index) const {
|
|
|
|
return index == 2u;
|
|
|
|
}
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
bool ScalarReplacementPass::CheckLoad(const Instruction* inst,
|
2017-11-30 22:03:06 +00:00
|
|
|
uint32_t index) const {
|
|
|
|
if (index != 2u) return false;
|
|
|
|
if (inst->NumInOperands() >= 2 &&
|
2022-11-04 21:27:10 +00:00
|
|
|
inst->GetSingleWordInOperand(1u) &
|
|
|
|
uint32_t(spv::MemoryAccessMask::Volatile))
|
2017-11-30 22:03:06 +00:00
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
bool ScalarReplacementPass::CheckStore(const Instruction* inst,
|
2017-11-30 22:03:06 +00:00
|
|
|
uint32_t index) const {
|
|
|
|
if (index != 0u) return false;
|
|
|
|
if (inst->NumInOperands() >= 3 &&
|
2022-11-04 21:27:10 +00:00
|
|
|
inst->GetSingleWordInOperand(2u) &
|
|
|
|
uint32_t(spv::MemoryAccessMask::Volatile))
|
2017-11-30 22:03:06 +00:00
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
2021-09-24 14:56:08 +00:00
|
|
|
|
|
|
|
bool ScalarReplacementPass::CheckDebugDeclare(uint32_t index) const {
|
|
|
|
if (index != kDebugDeclareOperandVariableIndex) return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-04-26 16:52:23 +00:00
|
|
|
bool ScalarReplacementPass::IsLargerThanSizeLimit(uint64_t length) const {
|
2018-04-16 13:58:00 +00:00
|
|
|
if (max_num_elements_ == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return length > max_num_elements_;
|
|
|
|
}
|
2017-11-30 22:03:06 +00:00
|
|
|
|
2019-07-31 19:39:33 +00:00
|
|
|
std::unique_ptr<std::unordered_set<int64_t>>
|
2018-07-12 19:14:43 +00:00
|
|
|
ScalarReplacementPass::GetUsedComponents(Instruction* inst) {
|
2019-07-31 19:39:33 +00:00
|
|
|
std::unique_ptr<std::unordered_set<int64_t>> result(
|
|
|
|
new std::unordered_set<int64_t>());
|
2018-05-11 21:02:57 +00:00
|
|
|
|
|
|
|
analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
|
|
|
|
|
|
|
|
def_use_mgr->WhileEachUser(inst, [&result, def_use_mgr,
|
2018-07-12 19:14:43 +00:00
|
|
|
this](Instruction* use) {
|
2018-05-11 21:02:57 +00:00
|
|
|
switch (use->opcode()) {
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpLoad: {
|
2018-05-11 21:02:57 +00:00
|
|
|
// Look for extract from the load.
|
|
|
|
std::vector<uint32_t> t;
|
2018-07-12 19:14:43 +00:00
|
|
|
if (def_use_mgr->WhileEachUser(use, [&t](Instruction* use2) {
|
2022-11-04 21:27:10 +00:00
|
|
|
if (use2->opcode() != spv::Op::OpCompositeExtract ||
|
2019-09-24 20:19:31 +00:00
|
|
|
use2->NumInOperands() <= 1) {
|
2018-05-11 21:02:57 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
t.push_back(use2->GetSingleWordInOperand(1));
|
|
|
|
return true;
|
|
|
|
})) {
|
|
|
|
result->insert(t.begin(), t.end());
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
result.reset(nullptr);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpName:
|
|
|
|
case spv::Op::OpMemberName:
|
|
|
|
case spv::Op::OpStore:
|
2018-12-19 17:07:29 +00:00
|
|
|
// No components are used.
|
2018-05-11 21:02:57 +00:00
|
|
|
return true;
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpAccessChain:
|
|
|
|
case spv::Op::OpInBoundsAccessChain: {
|
2018-05-11 21:02:57 +00:00
|
|
|
// Add the first index it if is a constant.
|
|
|
|
// TODO: Could be improved by checking if the address is used in a load.
|
|
|
|
analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
|
|
|
|
uint32_t index_id = use->GetSingleWordInOperand(1);
|
|
|
|
const analysis::Constant* index_const =
|
|
|
|
const_mgr->FindDeclaredConstant(index_id);
|
|
|
|
if (index_const) {
|
2019-07-31 19:39:33 +00:00
|
|
|
result->insert(index_const->GetSignExtendedValue());
|
|
|
|
return true;
|
2018-05-11 21:02:57 +00:00
|
|
|
} else {
|
|
|
|
// Could be any element. Assuming all are used.
|
|
|
|
result.reset(nullptr);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
// We do not know what is happening. Have to assume the worst.
|
|
|
|
result.reset(nullptr);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2019-08-21 17:12:42 +00:00
|
|
|
uint64_t ScalarReplacementPass::GetMaxLegalIndex(
|
|
|
|
const Instruction* var_inst) const {
|
2022-11-04 21:27:10 +00:00
|
|
|
assert(var_inst->opcode() == spv::Op::OpVariable &&
|
2019-08-21 17:12:42 +00:00
|
|
|
"|var_inst| must be a variable instruction.");
|
|
|
|
Instruction* type = GetStorageType(var_inst);
|
|
|
|
switch (type->opcode()) {
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpTypeStruct:
|
2019-08-21 17:12:42 +00:00
|
|
|
return type->NumInOperands();
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpTypeArray:
|
2019-08-21 17:12:42 +00:00
|
|
|
return GetArrayLength(type);
|
2022-11-04 21:27:10 +00:00
|
|
|
case spv::Op::OpTypeMatrix:
|
|
|
|
case spv::Op::OpTypeVector:
|
2019-08-21 17:12:42 +00:00
|
|
|
return GetNumElements(type);
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-05-08 13:39:14 +00:00
|
|
|
void ScalarReplacementPass::CopyDecorationsToVariable(Instruction* from,
|
|
|
|
Instruction* to,
|
|
|
|
uint32_t member_index) {
|
|
|
|
CopyPointerDecorationsToVariable(from, to);
|
|
|
|
CopyNecessaryMemberDecorationsToVariable(from, to, member_index);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScalarReplacementPass::CopyPointerDecorationsToVariable(Instruction* from,
|
|
|
|
Instruction* to) {
|
|
|
|
// The RestrictPointer and AliasedPointer decorations are copied to all
|
|
|
|
// members even if the new variable does not contain a pointer. It does
|
|
|
|
// not hurt to do so.
|
|
|
|
for (auto dec_inst :
|
|
|
|
get_decoration_mgr()->GetDecorationsFor(from->result_id(), false)) {
|
|
|
|
uint32_t decoration;
|
|
|
|
decoration = dec_inst->GetSingleWordInOperand(1u);
|
|
|
|
switch (spv::Decoration(decoration)) {
|
|
|
|
case spv::Decoration::AliasedPointer:
|
|
|
|
case spv::Decoration::RestrictPointer: {
|
|
|
|
std::unique_ptr<Instruction> new_dec_inst(dec_inst->Clone(context()));
|
|
|
|
new_dec_inst->SetInOperand(0, {to->result_id()});
|
|
|
|
context()->AddAnnotationInst(std::move(new_dec_inst));
|
|
|
|
} break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScalarReplacementPass::CopyNecessaryMemberDecorationsToVariable(
|
|
|
|
Instruction* from, Instruction* to, uint32_t member_index) {
|
|
|
|
Instruction* type_inst = GetStorageType(from);
|
|
|
|
for (auto dec_inst :
|
|
|
|
get_decoration_mgr()->GetDecorationsFor(type_inst->result_id(), false)) {
|
|
|
|
uint32_t decoration;
|
|
|
|
if (dec_inst->opcode() == spv::Op::OpMemberDecorate) {
|
|
|
|
if (dec_inst->GetSingleWordInOperand(1) != member_index) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
decoration = dec_inst->GetSingleWordInOperand(2u);
|
|
|
|
switch (spv::Decoration(decoration)) {
|
|
|
|
case spv::Decoration::ArrayStride:
|
|
|
|
case spv::Decoration::Alignment:
|
|
|
|
case spv::Decoration::AlignmentId:
|
|
|
|
case spv::Decoration::MaxByteOffset:
|
|
|
|
case spv::Decoration::MaxByteOffsetId:
|
|
|
|
case spv::Decoration::RelaxedPrecision: {
|
|
|
|
std::unique_ptr<Instruction> new_dec_inst(
|
|
|
|
new Instruction(context(), spv::Op::OpDecorate, 0, 0, {}));
|
|
|
|
new_dec_inst->AddOperand(
|
|
|
|
Operand(SPV_OPERAND_TYPE_ID, {to->result_id()}));
|
|
|
|
for (uint32_t i = 2; i < dec_inst->NumInOperandWords(); ++i) {
|
|
|
|
new_dec_inst->AddOperand(Operand(dec_inst->GetInOperand(i)));
|
|
|
|
}
|
|
|
|
context()->AddAnnotationInst(std::move(new_dec_inst));
|
|
|
|
} break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-30 22:03:06 +00:00
|
|
|
} // namespace opt
|
|
|
|
} // namespace spvtools
|