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