blob: dec52d1da28fec61afea9150100d60b53731106d [file] [log] [blame]
// Copyright (C) 2016 The Android Open Source Project
//
// 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 "SpvManager.h"
#include <assert.h>
namespace spvmanager {
#define PRINT_NAME "print"
#define LABEL_PRINT_NAME "label"
#define PRINT_PARAM_NAME "value"
#define WIDTH 32
#define RESULT_VEC_SIZE 4
#define COORDINATE_SIZE 2
#define FIRST_CHAIN_INDEX_OPERAND 3
/***********************************************************************
**************************** public ***********************************
***********************************************************************/
/**
* Changes spv handled by module to prepare debug instructions.
* Firstly, changes all non-build-in names to avoid versions incompatybility.
* Secondly, for every input creates output variable to show value of each input.
* Finally, insert print functions and call instructions to appropriate print
* after each 'store' inst.
**/
void SpvManager::makeSpvDebuggable() {
declareDebugVariables();
declarePrints();
insertPrintCallsIntoFunctions();
}
/**
* Adds output variable for each input.
* Assigns value of input value to appropriate output in the beginning of main function.
* Output variable has the same name as input with added prefix (option 'outs_pref').
**/
void SpvManager::addOutputForInputs(std::string outs_pref) {
Variable out_var;
curr_block_insts.clear();
std::unordered_set<uint32_t> names = name_mgr->getNamedIds();
for (auto& id : names) {
Instruction* def_inst = def_use_mgr->GetDef(id);
if (isInputVariable(*def_inst)) {
std::string in_name = name_mgr->getStrName(id);
out_var.name = outs_pref + in_name;
const Type* type = getPointeeIfPointer(def_inst->GetSingleWordOperand(0));
out_var.type_id = type_mgr->GetId(type);
addGlobalVariable(spv::StorageClassOutput, &out_var);
// instructions stored in curr_block_insts
uint32_t ref_id =
collectInstWithResult(SpvOpLoad, {{def_inst->GetSingleWordOperand(1)}}, out_var.type_id);
collectInstWithoutResult(SpvOpStore, {{out_var.ref_id}, {ref_id}});
}
}
// add curr_block_insts to first block in main
module->begin()->begin()->PrependInstructions(curr_block_insts);
curr_block_insts.clear();
}
/**
* Changes all declared names with adding name_pref.
**/
void SpvManager::mapDeclarationNames(std::string name_pref) {
std::unordered_set<uint32_t> builtIns;
for (auto& annotations_it : module->annotations())
if (isBuiltInDecoration(annotations_it)) builtIns.insert(annotations_it.GetSingleWordOperand(0));
namemanager::name_map::iterator it;
for (it = name_mgr->begin(); it != name_mgr->end(); it++) {
std::string name = name_mgr->getStrName(it->first);
if (name != "main") // special word
if (name_mgr->isDeprecatedBuiltInName(name) || builtIns.find(it->first.first) == builtIns.end()) {
std::string out_name = name_pref + name;
name_mgr->setIfName(it->first, out_name);
}
}
}
/**
* Return binary currently handled by module
**/
std::vector<unsigned int> SpvManager::getSpvBinary() {
std::vector<unsigned int> res;
module->ToBinary(&res, false);
return res;
}
/**
* Returns set of debug instructions searching through the module.
**/
debug_instructions_t* SpvManager::getDebugInstructions() {
debug_instructions_t* result = new debug_instructions_t{};
std::vector<instruction_t> insts_v;
module->ForEachInst(
std::bind(&SpvManager::appendDebugInstruction, this, &insts_v, std::placeholders::_1), true);
result->insts_num = insts_v.size();
result->insts = new instruction_t[result->insts_num];
for (int i = 0; i < result->insts_num; i++) {
result->insts[i] = insts_v[i];
}
return result;
}
/***********************************************************************
**************************** private **********************************
***********************************************************************/
std::vector<spvtools::ir::Operand> SpvManager::makeOperands(
spv_opcode_desc& op_desc, std::initializer_list<std::initializer_list<uint32_t>>& words,
const char* literal_string) {
std::vector<spvtools::ir::Operand> operands;
std::initializer_list<std::initializer_list<uint32_t>>::iterator it = words.begin();
for (int i = 0; i < op_desc->numTypes; i++) {
spv_operand_type_t operand_type = op_desc->operandTypes[i];
switch (operand_type) {
case SPV_OPERAND_TYPE_TYPE_ID:
case SPV_OPERAND_TYPE_RESULT_ID:
break;
case SPV_OPERAND_TYPE_LITERAL_STRING: {
assert(literal_string && "makeOperands: SPV_OPERAND_TYPE_LITERAL_STRING is missing.");
operands.emplace_back(operand_type, std::move(makeVector(literal_string)));
break;
}
case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING: {
if (literal_string) operands.emplace_back(operand_type, std::move(makeVector(literal_string)));
break;
}
default: {
assert((it != words.end() || spvOperandIsOptional(operand_type) ||
spvOperandIsVariable(operand_type)) &&
"makeOperands: too few operands to make vector of operands.");
if (it != words.end()) {
operands.emplace_back(operand_type, std::move(makeVector(*it)));
it++;
}
break;
}
}
}
assert((it == words.end() || it->size() == 0) &&
"makeOperands: too many operands to make vector of operands.");
return operands;
}
std::unique_ptr<Instruction> SpvManager::makeInstruction(
SpvOp_ op, uint32_t type_id, uint32_t result_id,
std::initializer_list<std::initializer_list<uint32_t>> words, const char* literal_string) {
spv_opcode_desc op_desc;
spv_result_t res;
std::unique_ptr<Instruction> inst;
res = grammar->lookupOpcode(op, &op_desc);
assert(res == SPV_SUCCESS && "makeInstruction: cannot find opcode description");
if (res == SPV_SUCCESS) {
inst = spvtools::MakeUnique<Instruction>(op, type_id, result_id,
makeOperands(op_desc, words, literal_string));
def_use_mgr->AnalyzeInstDefUse(inst.get());
}
return inst;
}
/**
* Returns pointer to created BasicBlock (body of appropriate print function).
**/
std::unique_ptr<BasicBlock> SpvManager::makeBasicBlock(uint32_t label_id, Function* parent,
std::vector<std::unique_ptr<Instruction>>& body) {
auto label_inst = makeInstruction(SpvOpLabel, 0, label_id, {{}});
std::unique_ptr<BasicBlock> bb = spvtools::MakeUnique<BasicBlock>(std::move(label_inst));
bb->SetInstructions(std::move(body));
bb->SetParent(parent);
return bb;
}
/**
* Functions that create Instructions and add them directly to the module.
**/
uint32_t SpvManager::addName(const char* name) {
uint32_t ref_id = getUnique();
auto inst = makeInstruction(SpvOpName, 0, 0, {{ref_id}}, name);
name_mgr->addName(inst.get());
module->AddDebugInst(std::move(inst));
return ref_id;
}
/**
* OpConstant has SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER operand.
* This operand is a literal number whose format and size are determined by a previous operand
* in this instruction. It's a signed integer, an unsigned integer, or a floating point number,
* so can occupy multiple SPIR-V words
**/
uint32_t SpvManager::addConstant(uint32_t type_id, std::initializer_list<uint32_t> num) {
uint32_t result_id = getUnique();
auto inst = makeInstruction(SpvOpConstant, type_id, result_id, {num});
module->AddGlobalValue(std::move(inst));
return result_id;
}
uint32_t SpvManager::addTypeInst(SpvOp_ op, std::initializer_list<std::initializer_list<uint32_t>> words,
uint32_t type_id) {
uint32_t result_id = getUnique();
auto inst = makeInstruction(op, type_id, result_id, words);
type_mgr->GetRecordIfTypeDefinition(*inst);
module->AddType(std::move(inst));
return result_id;
}
void SpvManager::addVariable(uint32_t type_id, uint32_t ref_id, spv::StorageClass storage_class) {
auto inst = makeInstruction(SpvOpVariable, type_id, ref_id, {{storage_class}});
if (storage_class == spv::StorageClassFunction)
curr_block_insts.emplace_back(std::move(inst));
else
module->AddGlobalValue(std::move(inst));
}
void SpvManager::addGlobalVariable(spv::StorageClass storage_class, Variable* const var) {
if (!var->name.empty() && var->type_id) {
var->ref_id = addName(var->name.c_str());
uint32_t ptr_id = addTypeInst(SpvOpTypePointer, {{storage_class}, {var->type_id}});
addVariable(ptr_id, var->ref_id, storage_class);
}
}
/**
* Adds function (with exactly one parameter) to the module
**/
uint32_t SpvManager::addFunction(const char* name, uint32_t result_type_id, uint32_t param_type) {
uint32_t name_id = addName(name);
uint32_t param_type_id = addTypeInst(SpvOpTypePointer, {{spv::StorageClassFunction}, {param_type}});
uint32_t fun_type_id = addTypeInst(SpvOpTypeFunction, {{globals.void_id}, {param_type_id}});
auto fun_inst = makeInstruction(SpvOpFunction, result_type_id, name_id,
{{spv::FunctionControlMaskNone}, {fun_type_id}});
std::unique_ptr<Function> fun = spvtools::MakeUnique<Function>(std::move(fun_inst));
uint32_t param_name_id = addName(PRINT_PARAM_NAME);
auto param_inst = makeInstruction(SpvOpFunctionParameter, param_type_id, param_name_id, {{}});
fun->AddParameter(std::move(param_inst));
name_type param(param_name_id, param_type);
curr_block_insts.clear();
if (param_type == globals.result.type_id) {
uint32_t true_label_id = getUnique();
uint32_t false_label_id = getUnique();
// condition block
uint32_t load_id =
collectInstWithResult(SpvOpLoad, {{globals.curr_step.ref_id}}, globals.curr_step.type_id);
uint32_t sub_id =
collectInstWithResult(SpvOpISub, {{load_id}, {getConstId(1)}}, globals.curr_step.type_id);
collectInstWithoutResult(SpvOpStore, {{globals.curr_step.ref_id}, {sub_id}});
collectCondition(true_label_id, false_label_id);
fun->AddBasicBlock(std::move(makeBasicBlock(getUnique(), fun.get(), curr_block_insts)));
curr_block_insts.clear();
// true block
load_id = collectInstWithResult(SpvOpLoad, {{param.first}}, globals.result.type_id);
collectInstWithoutResult(SpvOpStore, {{globals.result.ref_id}, {load_id}});
collectInstWithoutResult(SpvOpBranch, {{false_label_id}});
fun->AddBasicBlock(std::move(makeBasicBlock(true_label_id, fun.get(), curr_block_insts)));
curr_block_insts.clear();
// after-if-statement block
collectInstWithoutResult(SpvOpReturn);
fun->AddBasicBlock(std::move(makeBasicBlock(false_label_id, fun.get(), curr_block_insts)));
curr_block_insts.clear();
} else {
// call-another-print block
const Type* arg_type = type_mgr->GetType(param.second);
uint32_t type_to_convert = getTypeToConvert(arg_type);
collectPrintCall(param, type_to_convert);
collectInstWithoutResult(SpvOpReturn);
fun->AddBasicBlock(std::move(makeBasicBlock(getUnique(), fun.get(), curr_block_insts)));
curr_block_insts.clear();
}
auto inst_end = makeInstruction(SpvOpFunctionEnd, 0, 0, {{}});
fun->SetFunctionEnd(std::move(inst_end));
module->AddFunction(std::move(fun));
return name_id;
}
/**
* Functions that create Instructions and add them to currently constructed block instructions.
**/
uint32_t SpvManager::collectInstWithResult(SpvOp_ op,
std::initializer_list<std::initializer_list<uint32_t>> data,
uint32_t type_id) {
uint32_t result_id = getUnique();
auto inst = makeInstruction(op, type_id, result_id, data);
curr_block_insts.emplace_back(std::move(inst));
return result_id;
}
void SpvManager::collectInstWithoutResult(SpvOp_ op,
std::initializer_list<std::initializer_list<uint32_t>> data,
uint32_t type_id) {
auto inst = makeInstruction(op, 0, type_id, data);
curr_block_insts.emplace_back(std::move(inst));
}
uint32_t SpvManager::collectCompositeConstruct(
std::initializer_list<std::initializer_list<uint32_t>> data, uint32_t type_id) {
const spvtools::opt::analysis::Vector* vec = type_mgr->GetType(type_id)->AsVector();
assert((vec != nullptr && vec->element_count() == data.begin()->size()) &&
"collectCompositeConstruct: wrong data size to construct vector");
return collectInstWithResult(SpvOpCompositeConstruct, data, type_id);
}
void SpvManager::collectCondition(uint32_t true_label_id, uint32_t false_label_id) {
uint32_t load_id =
collectInstWithResult(SpvOpLoad, {{globals.curr_step.ref_id}}, globals.curr_step.type_id);
uint32_t bool_id = addTypeInst(SpvOpTypeBool, {{}});
uint32_t cond_id = collectInstWithResult(SpvOpIEqual, {{load_id}, {getConstId(0)}}, bool_id);
collectInstWithoutResult(SpvOpSelectionMerge, {{false_label_id}, {SpvSelectionControlMaskNone}});
collectInstWithoutResult(SpvOpBranchConditional, {{cond_id}, {true_label_id}, {false_label_id}});
}
/**
* Converts given type 'from' to 'to_type' type and returns 'ref_id' to new converted element.
* Type conversion only for:
* bool, float, sint --> uint
* uint, vec --> uvec4
**/
uint32_t SpvManager::collectTypeConversion(name_type from, uint32_t to_type) {
const Type* from_type = type_mgr->GetType(from.second);
uint32_t ref_id = 0;
if (from_type->AsBool()) {
ref_id =
collectInstWithResult(SpvOpSelect, {{from.first}, {getConstId(1)}, {getConstId(0)}}, to_type);
} else if (from_type->AsFloat()) {
ref_id = collectInstWithResult(SpvOpBitcast, {{from.first}}, to_type);
} else if (from_type->AsInteger()) {
if (from_type->AsInteger()->isSigned()) {
ref_id = collectInstWithResult(SpvOpBitcast, {{from.first}}, to_type);
} else {
ref_id = collectCompositeConstruct({{from.first, getConstId(0), getConstId(0), getConstId(0)}},
to_type);
}
} else if (from_type->AsVector()) {
uint32_t elem_type_id = type_mgr->GetId(from_type->AsVector()->element_type());
uint32_t elem_count = from_type->AsVector()->element_count();
std::vector<uint32_t> components(RESULT_VEC_SIZE);
for (uint32_t i = 0; i < RESULT_VEC_SIZE; i++) {
if (i < elem_count) {
uint32_t elem_id =
collectInstWithResult(SpvOpAccessChain, {{from.first}, {getConstId(i)}}, elem_type_id);
components[i] =
collectTypeConversion(std::make_pair(elem_id, elem_type_id), globals.uint_type_id);
} else {
components[i] = getConstId(0);
}
}
assert(RESULT_VEC_SIZE == 4 && "collectTypeConversion: assumption that resulting vec size is 4.");
ref_id = collectCompositeConstruct({{components[0], components[1], components[2], components[3]}},
to_type);
}
assert(ref_id != 0 &&
"collectTypeConversion: conversion only from types: bool, float, sint, uint, vec.");
return ref_id;
}
/**
* Collects FunctionCall instruction with appropriate fun_id and arg.
* If fun_param_type_id is given then argument type should be different then fun_param_type_id.
* By default fun_param_type_id = 0 and that means that conversion shouldn't be needed.
**/
void SpvManager::collectPrintCall(name_type arg, uint32_t fun_param_type_id) {
uint32_t fun_id;
uint32_t arg_name_id;
if (fun_param_type_id && fun_param_type_id != arg.second) {
fun_id = getPrintFunction(fun_param_type_id);
uint32_t source = collectInstWithResult(SpvOpLoad, {{arg.first}}, arg.second);
arg_name_id = collectTypeConversion(std::make_pair(source, arg.second), fun_param_type_id);
} else {
fun_id = getPrintFunction(arg.second);
arg_name_id = arg.first;
}
collectInstWithResult(SpvOpFunctionCall, {{fun_id}, {arg_name_id}}, globals.void_id);
}
void SpvManager::collectPrintChain(name_type arg) {
const Type* arg_type = type_mgr->GetType(arg.second);
if (isConvertedType(arg_type)) {
collectPrintCall(arg);
return;
}
uint32_t print_elem_type;
uint32_t elem_id;
uint32_t elem_count;
if (arg_type->AsMatrix()) {
const spvtools::opt::analysis::Matrix* matrix = arg_type->AsMatrix();
print_elem_type = type_mgr->GetId(matrix->element_type());
elem_count = matrix->element_count();
for (uint32_t i = 0; i < elem_count; i++) {
elem_id = collectInstWithResult(SpvOpAccessChain, {{arg.first}, {getConstId(i)}}, print_elem_type);
collectPrintChain(std::make_pair(elem_id, print_elem_type));
}
}
if (arg_type->AsStruct()) {
const std::vector<Type*>& element_types = arg_type->AsStruct()->element_types();
elem_count = element_types.size();
for (uint32_t i = 0; i < elem_count; i++) {
print_elem_type = type_mgr->GetId(element_types[i]);
elem_id = collectInstWithResult(SpvOpAccessChain, {{arg.first}, {getConstId(i)}}, print_elem_type);
collectPrintChain(std::make_pair(elem_id, print_elem_type));
}
}
if (arg_type->AsArray()) {
print_elem_type = type_mgr->GetId(arg_type->AsArray()->element_type());
elem_count = getArrayLength(arg_type);
for (uint32_t i = 0; i < elem_count; i++) {
elem_id = collectInstWithResult(SpvOpAccessChain, {{arg.first}, {getConstId(i)}}, print_elem_type);
collectPrintChain(std::make_pair(elem_id, print_elem_type));
}
}
}
/**
* Declares global debug variables: result, sampler, coordinate, curr_step.
**/
void SpvManager::declareDebugVariables() {
globals.uint_type_id = addTypeInst(SpvOpTypeInt, {{WIDTH}, {0}});
globals.result.type_id = addTypeInst(SpvOpTypeVector, {{globals.uint_type_id}, {RESULT_VEC_SIZE}});
addGlobalVariable(spv::StorageClassOutput, &globals.result);
uint32_t float_type_id = addTypeInst(SpvOpTypeFloat, {{WIDTH}});
globals.coordinate.type_id = addTypeInst(SpvOpTypeVector, {{float_type_id}, {COORDINATE_SIZE}});
addGlobalVariable(spv::StorageClassInput, &globals.coordinate);
uint32_t image_type = addTypeInst(
SpvOpTypeImage, {{globals.uint_type_id}, {SpvDim2D}, {0}, {0}, {0}, {1}, {SpvImageFormatUnknown}});
uint32_t sampler_type = addTypeInst(SpvOpTypeSampledImage, {{image_type}});
globals.sampler.type_id = sampler_type;
addGlobalVariable(spv::StorageClassUniformConstant, &globals.sampler);
globals.curr_step.type_id = globals.uint_type_id;
addGlobalVariable(spv::StorageClassPrivate, &globals.curr_step);
}
/**
* Adds print functions (declarations) to module.
* For every different type that type_mgr holds prepares print function with this parameter type.
**/
void SpvManager::declarePrints() {
globals.void_id = addTypeInst(SpvOpTypeVoid, {{}});
// declares those two functions first, because other print functions call them
insertPrintDeclaration(globals.result.type_id);
insertPrintDeclaration(globals.uint_type_id);
// special 'print' for labels printing
globals.label_print_id = addFunction(LABEL_PRINT_NAME, globals.void_id, globals.uint_type_id);
// makes a copy, because iterating through all types insertPrintDeclaration adds new types
const TypeManager::TypeToIdMap all_types(type_mgr->type_to_ids());
for (TypeManager::TypeToIdMap::const_iterator it = all_types.cbegin(); it != all_types.cend(); it++)
insertPrintDeclaration(it->second);
}
/**
* Variable 'curr_step' is stored in Sampler2D.
* Retrieves that data and assign to 'curr_step'.
**/
void SpvManager::setStepVariable() {
uint32_t sampler_id =
collectInstWithResult(SpvOpLoad, {{globals.sampler.ref_id}}, globals.sampler.type_id);
uint32_t coor_id =
collectInstWithResult(SpvOpLoad, {{globals.coordinate.ref_id}}, globals.coordinate.type_id);
uint32_t float_type_id = addTypeInst(SpvOpTypeFloat, {{WIDTH}});
uint32_t vec = addTypeInst(SpvOpTypeVector, {{float_type_id}, {4}});
uint32_t texture_res =
collectInstWithResult(SpvOpImageSampleImplicitLod, {{sampler_id}, {coor_id}}, vec);
uint32_t float_val =
collectInstWithResult(SpvOpAccessChain, {{texture_res}, {getConstId(0)}}, float_type_id);
collectInstWithoutResult(SpvOpStore, {{globals.curr_step.ref_id}, {float_val}});
}
/**
* Traverses function blocks to insert print function calls.
**/
void SpvManager::insertPrintCallsIntoFunctions() {
// main function is first, add insts to the first block
setStepVariable();
for (Module::iterator it = module->begin(); it != module->end(); it++) {
if (isDebugFunction(*it)) continue;
for (Function::iterator fun_it = it->begin(); fun_it != it->end(); fun_it++)
insertPrintCallsIntoBlock(*fun_it);
}
}
void SpvManager::moveCollectedBlockInsts(BasicBlock::iterator& it) {
for (auto& i : curr_block_insts) {
it = it.InsertBefore(std::move(i));
it++;
}
curr_block_insts.clear();
}
/**
* Inserts FunctionCall instruction after Store instructions.
**/
void SpvManager::insertPrintCallsIntoBlock(BasicBlock& bb) {
BasicBlock::iterator it = bb.begin();
uint32_t label_id = bb.GetLabelId();
// print label id to indicate current BasicBlock
assert(globals.label_print_id != 0 &&
"insertPrintCallsIntoBlock: label_print_id has to be bigger then zero.");
collectInstWithResult(SpvOpFunctionCall, {{globals.label_print_id}, {getConstId(label_id)}},
globals.void_id);
moveCollectedBlockInsts(it);
while (it != bb.end()) {
if (it->opcode() == SpvOpStore && !isArgStoreInst(it, bb.end())) {
uint32_t pointer = it->GetSingleWordOperand(0);
Instruction* pointer_def = def_use_mgr->GetDef(pointer);
const Type* pointee_type = getPointeeIfPointer(pointer_def->type_id());
assert(pointee_type != nullptr && "insertPrintCallsIntoBlock: not recognized pointee type.");
if (pointee_type) {
uint32_t opcode = pointer_def->opcode();
if (opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain) {
uint32_t offset_id;
for (uint32_t i = FIRST_CHAIN_INDEX_OPERAND; i < pointer_def->NumOperands(); i++) {
offset_id = pointer_def->GetSingleWordOperand(i);
collectPrintCall(std::make_pair(offset_id, getVariableTypeId(offset_id)));
}
}
uint32_t pointee_type_id = type_mgr->GetId(pointee_type);
collectPrintChain(std::make_pair(pointer, pointee_type_id));
}
}
it++;
moveCollectedBlockInsts(it);
}
}
/**
* Extends typeid_to_printid by inserting new pair <type_id, fun_id>,
* where fun_id is function made for type_id type.
* If function for this type already exists, return fun_id.
* Attention! addFunction uses curr_block_insts vector to collect function body.
**/
uint32_t SpvManager::insertPrintDeclaration(uint32_t type_id) {
const Type* type = type_mgr->GetType(type_id);
if (isConvertedType(type)) {
for (map_uint::iterator it = typeid_to_printid.begin(); it != typeid_to_printid.end(); ++it) {
if (type->IsSame(type_mgr->GetType(it->first))) {
return it->second;
}
}
if (type->AsVector() && type_id != globals.result.type_id) {
uint32_t elem_type_id = type_mgr->GetId(type->AsVector()->element_type());
insertPrintDeclaration(elem_type_id);
}
typeid_to_printid[type_id] = addFunction(PRINT_NAME, globals.void_id, type_id);
return typeid_to_printid[type_id];
}
return 0;
}
uint32_t SpvManager::getVariableTypeId(uint32_t var_id) {
Instruction* var_inst = def_use_mgr->GetDef(var_id);
uint32_t type_id = var_inst->type_id();
assert(type_id != 0 && "getVariableTypeId: variable type not found.");
return type_id;
}
/**
* Returns type_id of element which we want to print for given type.
**/
uint32_t SpvManager::getTypeToConvert(const Type* type) {
uint32_t res_id = 0;
if ((type->AsInteger() && !type->AsInteger()->isSigned()) || type->AsVector()) {
res_id = globals.result.type_id;
} else if (type->AsBool() || type->AsInteger() || type->AsFloat()) {
res_id = globals.uint_type_id;
}
assert(res_id != 0 && "getTypeToConvert: type id to convert not found.");
return res_id;
}
/**
* Determines length of array if given type represents array.
**/
uint32_t SpvManager::getArrayLength(const Type* type) {
uint32_t length = 0;
if (type->AsArray()) {
const spvtools::opt::analysis::Array* array = type->AsArray();
Instruction* const_inst = def_use_mgr->GetDef(array->length_id());
assert((const_inst->opcode() == SpvOpConstant && const_inst->NumOperands() == 3) &&
"getArrayLength: array length must come from constant instruction.");
length = const_inst->GetSingleWordOperand(2);
}
assert(length != 0 && "getArrayLength: array length must be at least 1.");
return length;
}
/**
* Returns pointer to pointee_type if given id defines Pointer type.
**/
const Type* SpvManager::getPointeeIfPointer(uint32_t id) {
const Type* type = type_mgr->GetType(id);
if (type) {
const spvtools::opt::analysis::Pointer* pointer = type->AsPointer();
if (pointer) {
return pointer->pointee_type();
}
}
return nullptr;
}
/**
* Returns id to function declaration with the same parameter type
* as given type_id.
**/
uint32_t SpvManager::getPrintFunction(uint32_t type_id) {
uint32_t fun_id = 0;
map_uint::iterator it = typeid_to_printid.find(type_id);
if (it != typeid_to_printid.end()) {
fun_id = it->second;
} else {
const Type* type = type_mgr->GetType(type_id);
it = typeid_to_printid.begin();
while (it != typeid_to_printid.end()) {
if (type->IsSame(type_mgr->GetType(it->first))) {
fun_id = it->second;
break;
}
it++;
}
}
assert(fun_id != 0 && "getPrintFunction: function id must be bigger then 0.");
return fun_id;
}
bool SpvManager::isConvertedType(const Type* type) {
return (type->AsBool() || type->AsInteger() || type->AsFloat() || type->AsVector());
}
/**
* Checks if given function is print function added for debugging.
**/
bool SpvManager::isDebugFunction(Function& f) {
std::string name = name_mgr->getStrName(f.GetNameId());
return name == PRINT_NAME || name == LABEL_PRINT_NAME;
}
/**
* Checks if given inst is Store instruction and
* stores variable on function argument.
**/
bool SpvManager::isArgStoreInst(BasicBlock::iterator bb_curr, BasicBlock::iterator bb_end) {
if (bb_curr->NumOperands() >= 2) {
uint32_t dest_id = bb_curr->GetSingleWordOperand(0);
while (bb_curr->opcode() == SpvOpStore && bb_curr != bb_end) {
bb_curr++;
}
if (bb_curr != bb_end)
if (bb_curr->opcode() == SpvOpFunctionCall && bb_curr->NumOperands() >= 4)
for (auto i = 3; i < bb_curr->NumOperands(); i++)
if (dest_id == bb_curr->GetSingleWordOperand(i)) return true;
}
return false;
}
/**
* Checks if given instruction is decoration of built in variable.
**/
bool SpvManager::isBuiltInDecoration(const Instruction& inst) const {
spv_opcode_desc op_decoration_desc;
if (grammar->lookupOpcode(inst.opcode(), &op_decoration_desc) == SPV_SUCCESS) {
switch (inst.opcode()) {
case SpvOpDecorate:
if (op_decoration_desc->numTypes >= 2 && inst.GetSingleWordOperand(1) == SpvDecorationBuiltIn)
return true;
case SpvOpMemberDecorate:
if (op_decoration_desc->numTypes >= 3 && inst.GetSingleWordOperand(2) == SpvDecorationBuiltIn)
return true;
default:
break;
}
}
return false;
}
/**
* Checks if instuction represents some input variable.
**/
bool SpvManager::isInputVariable(const Instruction& inst) const {
return (inst.opcode() == SpvOpVariable && inst.NumOperands() >= 3 &&
inst.GetSingleWordOperand(2) == spv::StorageClassInput);
}
/**
* Returns id to const instruction with a given value.
* Adds new constant instruction if not added before.
**/
uint32_t SpvManager::getConstId(uint32_t val) {
if (consts.find(val) == consts.end()) {
consts[val] = addConstant(globals.uint_type_id, {val});
}
return consts[val];
}
/**
* Returns first free unique id.
**/
uint32_t SpvManager::getUnique() {
uint32_t res;
res = module->GetIdBound();
module->SetIdBound(res + 1);
return res;
}
/**
* Checks if instruction gives useful information in debugging process.
**/
bool SpvManager::isDebugInstruction(Instruction* inst) {
if (spvtools::ir::IsTypeInst(inst->opcode())) {
return true;
}
switch (inst->opcode()) {
case SpvOpName:
case SpvOpMemberName:
case SpvOpLine:
case SpvOpVariable:
case SpvOpLabel:
case SpvOpAccessChain:
case SpvOpInBoundsAccessChain:
return true;
case SpvOpFunctionCall: {
uint32_t ref_id = inst->GetSingleWordOperand(2);
Instruction* def_inst = def_use_mgr->GetDef(ref_id);
if (def_inst->opcode() == SpvOpFunction) {
std::string name = name_mgr->getStrName(def_inst->result_id());
return name == PRINT_NAME || name == LABEL_PRINT_NAME;
}
return false;
}
default:
return false;
}
}
/**
* Adds instruction to 'debugs' vector if it is usuful in debugging process.
* If instruction is a Name or MemberName instruction remember name as a char array.
**/
void SpvManager::appendDebugInstruction(std::vector<instruction_t>* debugs, Instruction* inst) {
SpvOp_ opcode = inst->opcode();
if (isDebugInstruction(inst)) {
instruction_t i{};
i.id = inst->result_id();
i.opcode = opcode;
if (opcode == SpvOpName || opcode == SpvOpMemberName) {
i.words_num = inst->NumOperands() - 1;
std::string str_name;
if (opcode == SpvOpName)
str_name = name_mgr->getStrName(inst->GetSingleWordOperand(0));
else
str_name = name_mgr->getStrName(
namemanager::id_offset(inst->GetSingleWordOperand(0), inst->GetSingleWordOperand(1)));
i.name = new char[str_name.length() + 1];
std::strcpy(i.name, str_name.c_str());
} else {
i.words_num = inst->NumOperands();
}
// copy operands words
i.words = new uint32_t[i.words_num];
for (int op = 0; op < i.words_num; op++) i.words[op] = inst->GetSingleWordOperand(op);
debugs->emplace_back(std::move(i));
}
}
} // namespace spvmanager