| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 |
- // Copyright (c) 2021 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.
- #include <cassert>
- #include <sstream>
- #include <string>
- #include "source/diagnostic.h"
- #include "source/lint/divergence_analysis.h"
- #include "source/lint/lints.h"
- #include "source/opt/basic_block.h"
- #include "source/opt/cfg.h"
- #include "source/opt/control_dependence.h"
- #include "source/opt/def_use_manager.h"
- #include "source/opt/dominator_analysis.h"
- #include "source/opt/instruction.h"
- #include "source/opt/ir_context.h"
- #include "spirv-tools/libspirv.h"
- namespace spvtools {
- namespace lint {
- namespace lints {
- namespace {
- // Returns the %name[id], where `name` is the first name associated with the
- // given id, or just %id if one is not found.
- std::string GetFriendlyName(opt::IRContext* context, uint32_t id) {
- auto names = context->GetNames(id);
- std::stringstream ss;
- ss << "%";
- if (names.empty()) {
- ss << id;
- } else {
- opt::Instruction* inst_name = names.begin()->second;
- if (inst_name->opcode() == spv::Op::OpName) {
- ss << names.begin()->second->GetInOperand(0).AsString();
- ss << "[" << id << "]";
- } else {
- ss << id;
- }
- }
- return ss.str();
- }
- bool InstructionHasDerivative(const opt::Instruction& inst) {
- static const spv::Op derivative_opcodes[] = {
- // Implicit derivatives.
- spv::Op::OpImageSampleImplicitLod,
- spv::Op::OpImageSampleDrefImplicitLod,
- spv::Op::OpImageSampleProjImplicitLod,
- spv::Op::OpImageSampleProjDrefImplicitLod,
- spv::Op::OpImageSparseSampleImplicitLod,
- spv::Op::OpImageSparseSampleDrefImplicitLod,
- spv::Op::OpImageSparseSampleProjImplicitLod,
- spv::Op::OpImageSparseSampleProjDrefImplicitLod,
- // Explicit derivatives.
- spv::Op::OpDPdx,
- spv::Op::OpDPdy,
- spv::Op::OpFwidth,
- spv::Op::OpDPdxFine,
- spv::Op::OpDPdyFine,
- spv::Op::OpFwidthFine,
- spv::Op::OpDPdxCoarse,
- spv::Op::OpDPdyCoarse,
- spv::Op::OpFwidthCoarse,
- };
- return std::find(std::begin(derivative_opcodes), std::end(derivative_opcodes),
- inst.opcode()) != std::end(derivative_opcodes);
- }
- spvtools::DiagnosticStream Warn(opt::IRContext* context,
- opt::Instruction* inst) {
- if (inst == nullptr) {
- return DiagnosticStream({0, 0, 0}, context->consumer(), "", SPV_WARNING);
- } else {
- // TODO(kuhar): Use line numbers based on debug info.
- return DiagnosticStream(
- {0, 0, 0}, context->consumer(),
- inst->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES),
- SPV_WARNING);
- }
- }
- void PrintDivergenceFlow(opt::IRContext* context, DivergenceAnalysis div,
- uint32_t id) {
- opt::analysis::DefUseManager* def_use = context->get_def_use_mgr();
- opt::CFG* cfg = context->cfg();
- while (id != 0) {
- bool is_block = def_use->GetDef(id)->opcode() == spv::Op::OpLabel;
- if (is_block) {
- Warn(context, nullptr)
- << "block " << GetFriendlyName(context, id) << " is divergent";
- uint32_t source = div.GetDivergenceSource(id);
- // Skip intermediate blocks.
- while (source != 0 &&
- def_use->GetDef(source)->opcode() == spv::Op::OpLabel) {
- id = source;
- source = div.GetDivergenceSource(id);
- }
- if (source == 0) break;
- spvtools::opt::Instruction* branch =
- cfg->block(div.GetDivergenceDependenceSource(id))->terminator();
- Warn(context, branch)
- << "because it depends on a conditional branch on divergent value "
- << GetFriendlyName(context, source) << "";
- id = source;
- } else {
- Warn(context, nullptr)
- << "value " << GetFriendlyName(context, id) << " is divergent";
- uint32_t source = div.GetDivergenceSource(id);
- opt::Instruction* def = def_use->GetDef(id);
- opt::Instruction* source_def =
- source == 0 ? nullptr : def_use->GetDef(source);
- // First print data -> data dependencies.
- while (source != 0 && source_def->opcode() != spv::Op::OpLabel) {
- Warn(context, def_use->GetDef(id))
- << "because " << GetFriendlyName(context, id) << " uses value "
- << GetFriendlyName(context, source)
- << "in its definition, which is divergent";
- id = source;
- def = source_def;
- source = div.GetDivergenceSource(id);
- source_def = def_use->GetDef(source);
- }
- if (source == 0) {
- Warn(context, def) << "because it has a divergent definition";
- break;
- }
- Warn(context, def) << "because it is conditionally set in block "
- << GetFriendlyName(context, source);
- id = source;
- }
- }
- }
- } // namespace
- bool CheckDivergentDerivatives(opt::IRContext* context) {
- DivergenceAnalysis div(*context);
- for (opt::Function& func : *context->module()) {
- div.Run(&func);
- for (const opt::BasicBlock& bb : func) {
- for (const opt::Instruction& inst : bb) {
- if (InstructionHasDerivative(inst) &&
- div.GetDivergenceLevel(bb.id()) >
- DivergenceAnalysis::DivergenceLevel::kPartiallyUniform) {
- Warn(context, nullptr)
- << "derivative with divergent control flow"
- << " located in block " << GetFriendlyName(context, bb.id());
- PrintDivergenceFlow(context, div, bb.id());
- }
- }
- }
- }
- return true;
- }
- } // namespace lints
- } // namespace lint
- } // namespace spvtools
|