2020-04-27 19:18:55 +00:00
|
|
|
// Copyright (c) 2020 Google LLC
|
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
|
|
|
|
#ifndef SOURCE_OPT_DEBUG_INFO_MANAGER_H_
|
|
|
|
#define SOURCE_OPT_DEBUG_INFO_MANAGER_H_
|
|
|
|
|
2022-09-13 14:41:07 +00:00
|
|
|
#include <set>
|
2020-04-27 19:18:55 +00:00
|
|
|
#include <unordered_map>
|
2020-06-25 19:48:26 +00:00
|
|
|
#include <unordered_set>
|
2020-04-27 19:18:55 +00:00
|
|
|
|
|
|
|
#include "source/opt/instruction.h"
|
|
|
|
#include "source/opt/module.h"
|
|
|
|
|
|
|
|
namespace spvtools {
|
|
|
|
namespace opt {
|
|
|
|
namespace analysis {
|
|
|
|
|
2020-05-21 17:09:43 +00:00
|
|
|
// When an instruction of a callee function is inlined to its caller function,
|
|
|
|
// we need the line and the scope information of the function call instruction
|
|
|
|
// to generate DebugInlinedAt. This class keeps the data. For multiple inlining
|
|
|
|
// of a single instruction, we have to create multiple DebugInlinedAt
|
|
|
|
// instructions as a chain. This class keeps the information of the generated
|
|
|
|
// DebugInlinedAt chains to reduce the number of chains.
|
|
|
|
class DebugInlinedAtContext {
|
|
|
|
public:
|
|
|
|
explicit DebugInlinedAtContext(Instruction* call_inst)
|
|
|
|
: call_inst_line_(call_inst->dbg_line_inst()),
|
|
|
|
call_inst_scope_(call_inst->GetDebugScope()) {}
|
|
|
|
|
|
|
|
const Instruction* GetLineOfCallInstruction() { return call_inst_line_; }
|
|
|
|
const DebugScope& GetScopeOfCallInstruction() { return call_inst_scope_; }
|
|
|
|
// Puts the DebugInlinedAt chain that is generated for the callee instruction
|
|
|
|
// whose DebugInlinedAt of DebugScope is |callee_instr_inlined_at| into
|
|
|
|
// |callee_inlined_at2chain_|.
|
|
|
|
void SetDebugInlinedAtChain(uint32_t callee_instr_inlined_at,
|
|
|
|
uint32_t chain_head_id) {
|
|
|
|
callee_inlined_at2chain_[callee_instr_inlined_at] = chain_head_id;
|
|
|
|
}
|
|
|
|
// Gets the DebugInlinedAt chain from |callee_inlined_at2chain_|.
|
|
|
|
uint32_t GetDebugInlinedAtChain(uint32_t callee_instr_inlined_at) {
|
|
|
|
auto chain_itr = callee_inlined_at2chain_.find(callee_instr_inlined_at);
|
|
|
|
if (chain_itr != callee_inlined_at2chain_.end()) return chain_itr->second;
|
|
|
|
return kNoInlinedAt;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
// The line information of the function call instruction that will be
|
|
|
|
// replaced by the callee function.
|
|
|
|
const Instruction* call_inst_line_;
|
|
|
|
|
|
|
|
// The scope information of the function call instruction that will be
|
|
|
|
// replaced by the callee function.
|
|
|
|
const DebugScope call_inst_scope_;
|
|
|
|
|
|
|
|
// Map from DebugInlinedAt ids of callee to head ids of new generated
|
|
|
|
// DebugInlinedAt chain.
|
|
|
|
std::unordered_map<uint32_t, uint32_t> callee_inlined_at2chain_;
|
|
|
|
};
|
|
|
|
|
2021-08-10 13:31:17 +00:00
|
|
|
// A class for analyzing, managing, and creating OpenCL.DebugInfo.100 and
|
2021-09-15 18:38:53 +00:00
|
|
|
// NonSemantic.Shader.DebugInfo.100 extension instructions.
|
2020-04-27 19:18:55 +00:00
|
|
|
class DebugInfoManager {
|
|
|
|
public:
|
|
|
|
// Constructs a debug information manager from the given |context|.
|
|
|
|
DebugInfoManager(IRContext* context);
|
|
|
|
|
|
|
|
DebugInfoManager(const DebugInfoManager&) = delete;
|
|
|
|
DebugInfoManager(DebugInfoManager&&) = delete;
|
|
|
|
DebugInfoManager& operator=(const DebugInfoManager&) = delete;
|
|
|
|
DebugInfoManager& operator=(DebugInfoManager&&) = delete;
|
|
|
|
|
|
|
|
friend bool operator==(const DebugInfoManager&, const DebugInfoManager&);
|
|
|
|
friend bool operator!=(const DebugInfoManager& lhs,
|
|
|
|
const DebugInfoManager& rhs) {
|
|
|
|
return !(lhs == rhs);
|
|
|
|
}
|
|
|
|
|
2021-08-10 13:31:17 +00:00
|
|
|
// Analyzes DebugInfo instruction |dbg_inst|.
|
2020-04-27 19:18:55 +00:00
|
|
|
void AnalyzeDebugInst(Instruction* dbg_inst);
|
|
|
|
|
|
|
|
// Creates new DebugInlinedAt and returns its id. Its line operand is the
|
|
|
|
// line number of |line| if |line| is not nullptr. Otherwise, its line operand
|
|
|
|
// is the line number of lexical scope of |scope|. Its Scope and Inlined
|
|
|
|
// operands are Scope and Inlined of |scope|.
|
|
|
|
uint32_t CreateDebugInlinedAt(const Instruction* line,
|
|
|
|
const DebugScope& scope);
|
|
|
|
|
2020-07-06 17:48:12 +00:00
|
|
|
// Clones DebugExpress instruction |dbg_expr| and add Deref Operation
|
|
|
|
// in the front of the Operation list of |dbg_expr|.
|
|
|
|
Instruction* DerefDebugExpression(Instruction* dbg_expr);
|
|
|
|
|
2020-04-27 19:18:55 +00:00
|
|
|
// Returns a DebugInfoNone instruction.
|
|
|
|
Instruction* GetDebugInfoNone();
|
|
|
|
|
|
|
|
// Returns DebugInlinedAt whose id is |dbg_inlined_at_id|. If it does not
|
|
|
|
// exist or it is not a DebugInlinedAt instruction, return nullptr.
|
|
|
|
Instruction* GetDebugInlinedAt(uint32_t dbg_inlined_at_id);
|
|
|
|
|
|
|
|
// Returns DebugFunction whose Function operand is |fn_id|. If it does not
|
|
|
|
// exist, return nullptr.
|
|
|
|
Instruction* GetDebugFunction(uint32_t fn_id) {
|
|
|
|
auto dbg_fn_it = fn_id_to_dbg_fn_.find(fn_id);
|
|
|
|
return dbg_fn_it == fn_id_to_dbg_fn_.end() ? nullptr : dbg_fn_it->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clones DebugInlinedAt whose id is |clone_inlined_at_id|. If
|
|
|
|
// |clone_inlined_at_id| is not an id of DebugInlinedAt, returns nullptr.
|
|
|
|
// If |insert_before| is given, inserts the new DebugInlinedAt before it.
|
|
|
|
// Otherwise, inserts the new DebugInlinedAt into the debug instruction
|
|
|
|
// section of the module.
|
|
|
|
Instruction* CloneDebugInlinedAt(uint32_t clone_inlined_at_id,
|
|
|
|
Instruction* insert_before = nullptr);
|
|
|
|
|
2020-05-21 17:09:43 +00:00
|
|
|
// Returns the debug scope corresponding to an inlining instruction in the
|
|
|
|
// scope |callee_instr_scope| into |inlined_at_ctx|. Generates all new
|
|
|
|
// debug instructions needed to represent the scope.
|
|
|
|
DebugScope BuildDebugScope(const DebugScope& callee_instr_scope,
|
|
|
|
DebugInlinedAtContext* inlined_at_ctx);
|
|
|
|
|
|
|
|
// Returns DebugInlinedAt corresponding to inlining an instruction, which
|
|
|
|
// was inlined at |callee_inlined_at|, into |inlined_at_ctx|. Generates all
|
|
|
|
// new debug instructions needed to represent the DebugInlinedAt.
|
|
|
|
uint32_t BuildDebugInlinedAtChain(uint32_t callee_inlined_at,
|
|
|
|
DebugInlinedAtContext* inlined_at_ctx);
|
|
|
|
|
2020-07-30 16:18:06 +00:00
|
|
|
// Returns true if there is a debug declaration instruction whose
|
|
|
|
// 'Local Variable' operand is |variable_id|.
|
|
|
|
bool IsVariableDebugDeclared(uint32_t variable_id);
|
2020-07-09 21:21:39 +00:00
|
|
|
|
2020-07-30 16:18:06 +00:00
|
|
|
// Kills all debug declaration instructions with Deref whose 'Local Variable'
|
Add DebugValue for invisible store in single_store_elim (#4002)
The front-end language compiler would simply emit DebugDeclare for
a variable when it is declared, which is effective through the variable's
scope. Since DebugDeclare only maps an OpVariable to a local variable,
the information can be removed when an optimization pass uses the
loaded value of the variable. DebugValue can be used to specify the
value of a variable. For each value update or phi instruction of a variable,
we can add DebugValue to help debugger inspect the variable at any
point of the program execution.
For example,
float a = 3;
... (complicated cfg) ...
foo(a); // <-- variable inspection: debugger can find DebugValue of `float a` in the nearest dominant
For the code with complicated CFG e.g., for-loop, if-statement, we
need help of ssa-rewrite to analyze the effective value of each variable
in each basic block.
If the value update of the variable happens only once and it dominates
all its uses, local-single-store-elim pass conducts the same value update
with ssa-rewrite and we have to let it add DebugValue for the value assignment.
One main issue is that we have to add DebugValue only when the value
update of a variable is visible to DebugDeclare. For example,
```
{ // scope1
%stack = OpVariable %ptr_int %int_3
{ // scope2
DebugDeclare %foo %stack <-- local variable "foo" in high-level language source code is declared as OpVariable "%stack"
// add DebugValue "foo = 3"
...
Store %stack %int_7 <-- foo = 7, add DebugValue "foo = 7"
...
// debugger can inspect the value of "foo"
}
Store %stack %int_11 <-- out of "scope2" i.e., scope of "foo". DO NOT add DebugValue "foo = 11"
}
```
However, the initalization of a variable is an exception.
For example, an argument passing of an inlined function must be done out of
the function's scope, but we must add a DebugValue for it.
```
// in HLSL
bar(float arg) { ... }
...
float foo = 3;
bar(foo);
// in SPIR-V
%arg = OpVariable
OpStore %arg %foo <-- Argument passing. Out of "float arg" scope, but we must add DebugValue for "float arg"
... body of function bar(float arg) ...
```
This PR handles the except case in local-single-store-elim pass. It adds
DebugValue for a store that is considered as an initialization.
The same exception handling code for ssa-rewrite is done by this commit: df4198e50eaa705298167d3c0b3cb01ffd28a5ff.
2020-11-04 18:43:59 +00:00
|
|
|
// operand is |variable_id|. Returns whether it kills an instruction or not.
|
|
|
|
bool KillDebugDeclares(uint32_t variable_id);
|
2020-07-10 19:17:14 +00:00
|
|
|
|
2020-06-19 18:57:43 +00:00
|
|
|
// Generates a DebugValue instruction with value |value_id| for every local
|
|
|
|
// variable that is in the scope of |scope_and_line| and whose memory is
|
|
|
|
// |variable_id| and inserts it after the instruction |insert_pos|.
|
2022-09-20 21:27:23 +00:00
|
|
|
// Returns whether a DebugValue is added or not.
|
|
|
|
bool AddDebugValueForVariable(Instruction* scope_and_line,
|
|
|
|
uint32_t variable_id, uint32_t value_id,
|
|
|
|
Instruction* insert_pos);
|
2020-07-27 17:02:25 +00:00
|
|
|
|
2020-11-13 17:06:38 +00:00
|
|
|
// Creates a DebugValue for DebugDeclare |dbg_decl| and inserts it before
|
2021-01-28 17:57:35 +00:00
|
|
|
// |insert_before|. The new DebugValue has the same line and scope as
|
|
|
|
// |scope_and_line|, or no scope and line information if |scope_and_line|
|
|
|
|
// is nullptr. The new DebugValue has the same operands as DebugDeclare
|
|
|
|
// but it uses |value_id| for the value. Returns the created DebugValue,
|
|
|
|
// or nullptr if fails to create one.
|
2020-11-13 17:06:38 +00:00
|
|
|
Instruction* AddDebugValueForDecl(Instruction* dbg_decl, uint32_t value_id,
|
2021-01-28 17:57:35 +00:00
|
|
|
Instruction* insert_before,
|
|
|
|
Instruction* scope_and_line);
|
2020-10-27 19:10:08 +00:00
|
|
|
|
2020-06-19 18:57:43 +00:00
|
|
|
// Erases |instr| from data structures of this class.
|
|
|
|
void ClearDebugInfo(Instruction* instr);
|
|
|
|
|
2021-08-10 13:31:17 +00:00
|
|
|
// Return the opcode for the Vulkan DebugOperation inst
|
|
|
|
uint32_t GetVulkanDebugOperation(Instruction* inst);
|
|
|
|
|
2020-07-21 20:10:09 +00:00
|
|
|
// Returns the id of Value operand if |inst| is DebugValue who has Deref
|
|
|
|
// operation and its Value operand is a result id of OpVariable with
|
|
|
|
// Function storage class. Otherwise, returns 0.
|
|
|
|
uint32_t GetVariableIdOfDebugValueUsedForDeclare(Instruction* inst);
|
|
|
|
|
2020-07-27 13:27:47 +00:00
|
|
|
// Converts DebugGlobalVariable |dbg_global_var| to a DebugLocalVariable and
|
|
|
|
// creates a DebugDeclare mapping the new DebugLocalVariable to |local_var|.
|
|
|
|
void ConvertDebugGlobalToLocalVariable(Instruction* dbg_global_var,
|
|
|
|
Instruction* local_var);
|
|
|
|
|
2020-07-30 16:18:06 +00:00
|
|
|
// Returns true if |instr| is a debug declaration instruction.
|
|
|
|
bool IsDebugDeclare(Instruction* instr);
|
|
|
|
|
2020-08-31 14:05:38 +00:00
|
|
|
// Replace all uses of |before| id that is an operand of a DebugScope with
|
|
|
|
// |after| id if those uses (instruction) return true for |predicate|.
|
|
|
|
void ReplaceAllUsesInDebugScopeWithPredicate(
|
|
|
|
uint32_t before, uint32_t after,
|
|
|
|
const std::function<bool(Instruction*)>& predicate);
|
|
|
|
|
|
|
|
// Removes uses of DebugScope |inst| from |scope_id_to_users_| or uses of
|
|
|
|
// DebugInlinedAt |inst| from |inlinedat_id_to_users_|.
|
|
|
|
void ClearDebugScopeAndInlinedAtUses(Instruction* inst);
|
|
|
|
|
2020-04-27 19:18:55 +00:00
|
|
|
private:
|
|
|
|
IRContext* context() { return context_; }
|
|
|
|
|
2021-08-10 13:31:17 +00:00
|
|
|
// Analyzes DebugInfo instructions in the given |module| and
|
2020-04-27 19:18:55 +00:00
|
|
|
// populates data structures in this class.
|
|
|
|
void AnalyzeDebugInsts(Module& module);
|
|
|
|
|
2021-08-10 13:31:17 +00:00
|
|
|
// Get the DebugInfo ExtInstImport Id, or 0 if no DebugInfo is available.
|
|
|
|
uint32_t GetDbgSetImportId();
|
|
|
|
|
2020-04-27 19:18:55 +00:00
|
|
|
// Returns the debug instruction whose id is |id|. Returns |nullptr| if one
|
|
|
|
// does not exists.
|
|
|
|
Instruction* GetDbgInst(uint32_t id);
|
|
|
|
|
2020-07-06 17:48:12 +00:00
|
|
|
// Returns a DebugOperation instruction with OpCode Deref.
|
|
|
|
Instruction* GetDebugOperationWithDeref();
|
|
|
|
|
2020-04-27 19:18:55 +00:00
|
|
|
// Registers the debug instruction |inst| into |id_to_dbg_inst_| using id of
|
|
|
|
// |inst| as a key.
|
|
|
|
void RegisterDbgInst(Instruction* inst);
|
|
|
|
|
|
|
|
// Register the DebugFunction instruction |inst|. The function referenced
|
|
|
|
// in |inst| must not already be registered.
|
|
|
|
void RegisterDbgFunction(Instruction* inst);
|
|
|
|
|
2020-06-25 19:48:26 +00:00
|
|
|
// Register the DebugDeclare or DebugValue with Deref operation
|
|
|
|
// |dbg_declare| into |var_id_to_dbg_decl_| using OpVariable id
|
|
|
|
// |var_id| as a key.
|
2020-06-19 18:57:43 +00:00
|
|
|
void RegisterDbgDeclare(uint32_t var_id, Instruction* dbg_declare);
|
|
|
|
|
|
|
|
// Returns a DebugExpression instruction without Operation operands.
|
|
|
|
Instruction* GetEmptyDebugExpression();
|
|
|
|
|
|
|
|
// Returns true if a scope |ancestor| is |scope| or an ancestor scope
|
|
|
|
// of |scope|.
|
|
|
|
bool IsAncestorOfScope(uint32_t scope, uint32_t ancestor);
|
|
|
|
|
2020-10-27 19:10:08 +00:00
|
|
|
// Returns true if the declaration of a local variable |dbg_declare|
|
|
|
|
// is visible in the scope of an instruction |instr_scope_id|.
|
|
|
|
bool IsDeclareVisibleToInstr(Instruction* dbg_declare, Instruction* scope);
|
2020-06-19 18:57:43 +00:00
|
|
|
|
|
|
|
// Returns the parent scope of the scope |child_scope|.
|
|
|
|
uint32_t GetParentScope(uint32_t child_scope);
|
|
|
|
|
2020-04-27 19:18:55 +00:00
|
|
|
IRContext* context_;
|
|
|
|
|
2021-08-10 13:31:17 +00:00
|
|
|
// Mapping from ids of DebugInfo extension instructions.
|
2020-04-27 19:18:55 +00:00
|
|
|
// to their Instruction instances.
|
|
|
|
std::unordered_map<uint32_t, Instruction*> id_to_dbg_inst_;
|
|
|
|
|
|
|
|
// Mapping from function's ids to DebugFunction instructions whose
|
|
|
|
// operand is the function.
|
|
|
|
std::unordered_map<uint32_t, Instruction*> fn_id_to_dbg_fn_;
|
|
|
|
|
2022-09-13 14:41:07 +00:00
|
|
|
// Orders Instruction* for use in associative containers (i.e. less than
|
|
|
|
// ordering). Unique Id is used.
|
|
|
|
typedef Instruction* InstPtr;
|
|
|
|
struct InstPtrLess {
|
|
|
|
bool operator()(const InstPtr& lhs, const InstPtr& rhs) const {
|
|
|
|
return lhs->unique_id() < rhs->unique_id();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-06-25 19:48:26 +00:00
|
|
|
// Mapping from variable or value ids to DebugDeclare or DebugValue
|
|
|
|
// instructions whose operand is the variable or value.
|
2022-09-13 14:41:07 +00:00
|
|
|
std::unordered_map<uint32_t, std::set<InstPtr, InstPtrLess>>
|
2020-06-25 19:48:26 +00:00
|
|
|
var_id_to_dbg_decl_;
|
2020-06-19 18:57:43 +00:00
|
|
|
|
2020-08-31 14:05:38 +00:00
|
|
|
// Mapping from DebugScope ids to users.
|
|
|
|
std::unordered_map<uint32_t, std::unordered_set<Instruction*>>
|
|
|
|
scope_id_to_users_;
|
|
|
|
|
|
|
|
// Mapping from DebugInlinedAt ids to users.
|
|
|
|
std::unordered_map<uint32_t, std::unordered_set<Instruction*>>
|
|
|
|
inlinedat_id_to_users_;
|
|
|
|
|
2020-07-06 17:48:12 +00:00
|
|
|
// DebugOperation whose OpCode is OpenCLDebugInfo100Deref.
|
|
|
|
Instruction* deref_operation_;
|
|
|
|
|
2020-04-27 19:18:55 +00:00
|
|
|
// DebugInfoNone instruction. We need only a single DebugInfoNone.
|
|
|
|
// To reuse the existing one, we keep it using this member variable.
|
|
|
|
Instruction* debug_info_none_inst_;
|
2020-06-19 18:57:43 +00:00
|
|
|
|
|
|
|
// DebugExpression instruction without Operation operands. We need only
|
|
|
|
// a single DebugExpression without Operation operands. To reuse the
|
|
|
|
// existing one, we keep it using this member variable.
|
|
|
|
Instruction* empty_debug_expr_inst_;
|
2020-04-27 19:18:55 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace analysis
|
|
|
|
} // namespace opt
|
|
|
|
} // namespace spvtools
|
|
|
|
|
|
|
|
#endif // SOURCE_OPT_DEBUG_INFO_MANAGER_H_
|