1 // Copyright (c) 2018 The Khronos Group Inc. 2 // Copyright (c) 2018 Valve Corporation 3 // Copyright (c) 2018 LunarG Inc. 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 #include "source/opt/process_lines_pass.h" 18 19 #include <set> 20 #include <unordered_set> 21 #include <vector> 22 23 namespace { 24 25 // Input Operand Indices 26 static const int kSpvLineFileInIdx = 0; 27 static const int kSpvLineLineInIdx = 1; 28 static const int kSpvLineColInIdx = 2; 29 30 } // anonymous namespace 31 32 namespace spvtools { 33 namespace opt { 34 35 Pass::Status ProcessLinesPass::Process() { 36 bool modified = ProcessLines(); 37 return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); 38 } 39 40 bool ProcessLinesPass::ProcessLines() { 41 bool modified = false; 42 uint32_t file_id = 0; 43 uint32_t line = 0; 44 uint32_t col = 0; 45 // Process types, globals, constants 46 for (Instruction& inst : get_module()->types_values()) 47 modified |= line_process_func_(&inst, &file_id, &line, &col); 48 // Process functions 49 for (Function& function : *get_module()) { 50 modified |= line_process_func_(&function.DefInst(), &file_id, &line, &col); 51 function.ForEachParam( 52 [this, &modified, &file_id, &line, &col](Instruction* param) { 53 modified |= line_process_func_(param, &file_id, &line, &col); 54 }); 55 for (BasicBlock& block : function) { 56 modified |= 57 line_process_func_(block.GetLabelInst(), &file_id, &line, &col); 58 for (Instruction& inst : block) { 59 modified |= line_process_func_(&inst, &file_id, &line, &col); 60 // Don't process terminal instruction if preceeded by merge 61 if (inst.opcode() == SpvOpSelectionMerge || 62 inst.opcode() == SpvOpLoopMerge) 63 break; 64 } 65 // Nullify line info after each block. 66 file_id = 0; 67 } 68 modified |= line_process_func_(function.EndInst(), &file_id, &line, &col); 69 } 70 return modified; 71 } 72 73 bool ProcessLinesPass::PropagateLine(Instruction* inst, uint32_t* file_id, 74 uint32_t* line, uint32_t* col) { 75 bool modified = false; 76 // only the last debug instruction needs to be considered 77 auto line_itr = inst->dbg_line_insts().rbegin(); 78 // if no line instructions, propagate previous info 79 if (line_itr == inst->dbg_line_insts().rend()) { 80 // if no current line info, add OpNoLine, else OpLine 81 if (*file_id == 0) 82 inst->dbg_line_insts().push_back(Instruction(context(), SpvOpNoLine)); 83 else 84 inst->dbg_line_insts().push_back(Instruction( 85 context(), SpvOpLine, 0, 0, 86 {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {*file_id}}, 87 {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {*line}}, 88 {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {*col}}})); 89 modified = true; 90 } else { 91 // else pre-existing line instruction, so update source line info 92 if (line_itr->opcode() == SpvOpNoLine) { 93 *file_id = 0; 94 } else { 95 assert(line_itr->opcode() == SpvOpLine && "unexpected debug inst"); 96 *file_id = line_itr->GetSingleWordInOperand(kSpvLineFileInIdx); 97 *line = line_itr->GetSingleWordInOperand(kSpvLineLineInIdx); 98 *col = line_itr->GetSingleWordInOperand(kSpvLineColInIdx); 99 } 100 } 101 return modified; 102 } 103 104 bool ProcessLinesPass::EliminateDeadLines(Instruction* inst, uint32_t* file_id, 105 uint32_t* line, uint32_t* col) { 106 // If no debug line instructions, return without modifying lines 107 if (inst->dbg_line_insts().empty()) return false; 108 // Only the last debug instruction needs to be considered; delete all others 109 bool modified = inst->dbg_line_insts().size() > 1; 110 Instruction last_inst = inst->dbg_line_insts().back(); 111 inst->dbg_line_insts().clear(); 112 // If last line is OpNoLine 113 if (last_inst.opcode() == SpvOpNoLine) { 114 // If no propagated line info, throw away redundant OpNoLine 115 if (*file_id == 0) { 116 modified = true; 117 // Else replace OpNoLine and propagate no line info 118 } else { 119 inst->dbg_line_insts().push_back(last_inst); 120 *file_id = 0; 121 } 122 } else { 123 // Else last line is OpLine 124 assert(last_inst.opcode() == SpvOpLine && "unexpected debug inst"); 125 // If propagated info matches last line, throw away last line 126 if (*file_id == last_inst.GetSingleWordInOperand(kSpvLineFileInIdx) && 127 *line == last_inst.GetSingleWordInOperand(kSpvLineLineInIdx) && 128 *col == last_inst.GetSingleWordInOperand(kSpvLineColInIdx)) { 129 modified = true; 130 } else { 131 // Else replace last line and propagate line info 132 *file_id = last_inst.GetSingleWordInOperand(kSpvLineFileInIdx); 133 *line = last_inst.GetSingleWordInOperand(kSpvLineLineInIdx); 134 *col = last_inst.GetSingleWordInOperand(kSpvLineColInIdx); 135 inst->dbg_line_insts().push_back(last_inst); 136 } 137 } 138 return modified; 139 } 140 141 ProcessLinesPass::ProcessLinesPass(uint32_t func_id) { 142 if (func_id == kLinesPropagateLines) { 143 line_process_func_ = [this](Instruction* inst, uint32_t* file_id, 144 uint32_t* line, uint32_t* col) { 145 return PropagateLine(inst, file_id, line, col); 146 }; 147 } else { 148 assert(func_id == kLinesEliminateDeadLines && "unknown Lines param"); 149 line_process_func_ = [this](Instruction* inst, uint32_t* file_id, 150 uint32_t* line, uint32_t* col) { 151 return EliminateDeadLines(inst, file_id, line, col); 152 }; 153 } 154 } 155 156 } // namespace opt 157 } // namespace spvtools 158