| 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 | |