void LocalAccessChainConvertPass::GenAccessChainStoreReplacement( const Instruction* ptrInst, uint32_t valId, std::vector<std::unique_ptr<Instruction>>* newInsts) { // Build and append load of variable in ptrInst uint32_t varId; uint32_t varPteTypeId; const uint32_t ldResultId = BuildAndAppendVarLoad(ptrInst, &varId, &varPteTypeId, newInsts); context()->get_decoration_mgr()->CloneDecorations( varId, ldResultId, {SpvDecorationRelaxedPrecision}); // Build and append Insert const uint32_t insResultId = TakeNextId(); std::vector<Operand> ins_in_opnds = { {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {valId}}, {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ldResultId}}}; AppendConstantOperands(ptrInst, &ins_in_opnds); BuildAndAppendInst(SpvOpCompositeInsert, varPteTypeId, insResultId, ins_in_opnds, newInsts); context()->get_decoration_mgr()->CloneDecorations( varId, insResultId, {SpvDecorationRelaxedPrecision}); // Build and append Store BuildAndAppendInst(SpvOpStore, 0, 0, {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {varId}}, {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {insResultId}}}, newInsts); }
void CommonUniformElimPass::GenACLoadRepl(const ir::Instruction* ptrInst, std::vector<std::unique_ptr<ir::Instruction>>* newInsts, uint32_t* resultId) { // Build and append Load const uint32_t ldResultId = TakeNextId(); const uint32_t varId = ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx); const ir::Instruction* varInst = def_use_mgr_->GetDef(varId); assert(varInst->opcode() == SpvOpVariable); const uint32_t varPteTypeId = GetPointeeTypeId(varInst); std::vector<ir::Operand> load_in_operands; load_in_operands.push_back( ir::Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, std::initializer_list<uint32_t>{varId})); std::unique_ptr<ir::Instruction> newLoad(new ir::Instruction(SpvOpLoad, varPteTypeId, ldResultId, load_in_operands)); def_use_mgr_->AnalyzeInstDefUse(&*newLoad); newInsts->emplace_back(std::move(newLoad)); // Build and append Extract const uint32_t extResultId = TakeNextId(); const uint32_t ptrPteTypeId = GetPointeeTypeId(ptrInst); std::vector<ir::Operand> ext_in_opnds; ext_in_opnds.push_back( ir::Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, std::initializer_list<uint32_t>{ldResultId})); uint32_t iidIdx = 0; ptrInst->ForEachInId([&iidIdx, &ext_in_opnds, this](const uint32_t *iid) { if (iidIdx > 0) { const ir::Instruction* cInst = def_use_mgr_->GetDef(*iid); uint32_t val = cInst->GetSingleWordInOperand(kConstantValueInIdx); ext_in_opnds.push_back( ir::Operand(spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, std::initializer_list<uint32_t>{val})); } ++iidIdx; }); std::unique_ptr<ir::Instruction> newExt(new ir::Instruction( SpvOpCompositeExtract, ptrPteTypeId, extResultId, ext_in_opnds)); def_use_mgr_->AnalyzeInstDefUse(&*newExt); newInsts->emplace_back(std::move(newExt)); *resultId = extResultId; }
uint32_t LocalAccessChainConvertPass::BuildAndAppendVarLoad( const Instruction* ptrInst, uint32_t* varId, uint32_t* varPteTypeId, std::vector<std::unique_ptr<Instruction>>* newInsts) { const uint32_t ldResultId = TakeNextId(); *varId = ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx); const Instruction* varInst = get_def_use_mgr()->GetDef(*varId); assert(varInst->opcode() == SpvOpVariable); *varPteTypeId = GetPointeeTypeId(varInst); BuildAndAppendInst(SpvOpLoad, *varPteTypeId, ldResultId, {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {*varId}}}, newInsts); return ldResultId; }
bool CommonUniformElimPass::CommonExtractElimination(ir::Function* func) { // Find all composite ids with duplicate extracts. for (auto bi = func->begin(); bi != func->end(); ++bi) { for (auto ii = bi->begin(); ii != bi->end(); ++ii) { if (ii->opcode() != SpvOpCompositeExtract) continue; // TODO(greg-lunarg): Support multiple indices if (ii->NumInOperands() > 2) continue; if (HasUnsupportedDecorates(ii->result_id())) continue; uint32_t compId = ii->GetSingleWordInOperand(kExtractCompositeIdInIdx); uint32_t idx = ii->GetSingleWordInOperand(kExtractIdx0InIdx); comp2idx2inst_[compId][idx].push_back(&*ii); } } // For all defs of ids with duplicate extracts, insert new extracts // after def, and replace and delete old extracts bool modified = false; for (auto bi = func->begin(); bi != func->end(); ++bi) { for (auto ii = bi->begin(); ii != bi->end(); ++ii) { const auto cItr = comp2idx2inst_.find(ii->result_id()); if (cItr == comp2idx2inst_.end()) continue; for (auto idxItr : cItr->second) { if (idxItr.second.size() < 2) continue; uint32_t replId = TakeNextId(); std::unique_ptr<ir::Instruction> newExtract(new ir::Instruction(*idxItr.second.front())); newExtract->SetResultId(replId); def_use_mgr_->AnalyzeInstDefUse(&*newExtract); ++ii; ii = ii.InsertBefore(std::move(newExtract)); for (auto instItr : idxItr.second) { uint32_t resId = instItr->result_id(); KillNamesAndDecorates(resId); (void)def_use_mgr_->ReplaceAllUsesWith(resId, replId); def_use_mgr_->KillInst(instItr); } modified = true; } } } return modified; }
uint32_t LocalAccessChainConvertPass::GenAccessChainLoadReplacement( const ir::Instruction* ptrInst, std::vector<std::unique_ptr<ir::Instruction>>* newInsts) { // Build and append load of variable in ptrInst uint32_t varId; uint32_t varPteTypeId; const uint32_t ldResultId = BuildAndAppendVarLoad(ptrInst, &varId, &varPteTypeId, newInsts); // Build and append Extract const uint32_t extResultId = TakeNextId(); const uint32_t ptrPteTypeId = GetPointeeTypeId(ptrInst); std::vector<ir::Operand> ext_in_opnds = {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ldResultId}}}; AppendConstantOperands(ptrInst, &ext_in_opnds); BuildAndAppendInst(SpvOpCompositeExtract, ptrPteTypeId, extResultId, ext_in_opnds, newInsts); return extResultId; }
bool CommonUniformElimPass::CommonUniformLoadElimination(ir::Function* func) { // Process all blocks in structured order. This is just one way (the // simplest?) to keep track of the most recent block outside of control // flow, used to copy common instructions, guaranteed to dominate all // following load sites. std::list<ir::BasicBlock*> structuredOrder; ComputeStructuredOrder(func, &structuredOrder); uniform2load_id_.clear(); bool modified = false; // Find insertion point in first block to copy non-dominating loads. auto insertItr = func->begin()->begin(); while (insertItr->opcode() == SpvOpVariable || insertItr->opcode() == SpvOpNop) ++insertItr; uint32_t mergeBlockId = 0; for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) { ir::BasicBlock* bp = *bi; // Check if we are exiting outermost control construct. If so, remember // new load insertion point. Trying to keep register pressure down. if (mergeBlockId == bp->id()) { mergeBlockId = 0; insertItr = bp->begin(); } for (auto ii = bp->begin(); ii != bp->end(); ++ii) { if (ii->opcode() != SpvOpLoad) continue; uint32_t varId; ir::Instruction* ptrInst = GetPtr(&*ii, &varId); if (ptrInst->opcode() != SpvOpVariable) continue; if (!IsUniformVar(varId)) continue; if (IsSamplerOrImageVar(varId)) continue; if (HasUnsupportedDecorates(ii->result_id())) continue; uint32_t replId; const auto uItr = uniform2load_id_.find(varId); if (uItr != uniform2load_id_.end()) { replId = uItr->second; } else { if (mergeBlockId == 0) { // Load is in dominating block; just remember it uniform2load_id_[varId] = ii->result_id(); continue; } else { // Copy load into most recent dominating block and remember it replId = TakeNextId(); std::unique_ptr<ir::Instruction> newLoad(new ir::Instruction(SpvOpLoad, ii->type_id(), replId, {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {varId}}})); def_use_mgr_->AnalyzeInstDefUse(&*newLoad); insertItr = insertItr.InsertBefore(std::move(newLoad)); ++insertItr; uniform2load_id_[varId] = replId; } } ReplaceAndDeleteLoad(&*ii, replId, ptrInst); modified = true; } // If we are outside of any control construct and entering one, remember // the id of the merge block if (mergeBlockId == 0) { uint32_t dummy; mergeBlockId = MergeBlockIdIfAny(*bp, &dummy); } } return modified; }