649 lines
13 KiB
C++
649 lines
13 KiB
C++
/*
|
|
* Copyright 2018-2019 Arm Limited
|
|
*
|
|
* 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 "spirv_cross_parsed_ir.hpp"
|
|
#include <algorithm>
|
|
#include <assert.h>
|
|
|
|
using namespace std;
|
|
using namespace spv;
|
|
|
|
namespace spirv_cross
|
|
{
|
|
void ParsedIR::set_id_bounds(uint32_t bounds)
|
|
{
|
|
ids.resize(bounds);
|
|
block_meta.resize(bounds);
|
|
}
|
|
|
|
static string ensure_valid_identifier(const string &name, bool member)
|
|
{
|
|
// Functions in glslangValidator are mangled with name(<mangled> stuff.
|
|
// Normally, we would never see '(' in any legal identifiers, so just strip them out.
|
|
auto str = name.substr(0, name.find('('));
|
|
|
|
for (uint32_t i = 0; i < str.size(); i++)
|
|
{
|
|
auto &c = str[i];
|
|
|
|
if (member)
|
|
{
|
|
// _m<num> variables are reserved by the internal implementation,
|
|
// otherwise, make sure the name is a valid identifier.
|
|
if (i == 0)
|
|
c = isalpha(c) ? c : '_';
|
|
else if (i == 2 && str[0] == '_' && str[1] == 'm')
|
|
c = isalpha(c) ? c : '_';
|
|
else
|
|
c = isalnum(c) ? c : '_';
|
|
}
|
|
else
|
|
{
|
|
// _<num> variables are reserved by the internal implementation,
|
|
// otherwise, make sure the name is a valid identifier.
|
|
if (i == 0 || (str[0] == '_' && i == 1))
|
|
c = isalpha(c) ? c : '_';
|
|
else
|
|
c = isalnum(c) ? c : '_';
|
|
}
|
|
}
|
|
return str;
|
|
}
|
|
|
|
const string &ParsedIR::get_name(uint32_t id) const
|
|
{
|
|
auto *m = find_meta(id);
|
|
if (m)
|
|
return m->decoration.alias;
|
|
else
|
|
return empty_string;
|
|
}
|
|
|
|
const string &ParsedIR::get_member_name(uint32_t id, uint32_t index) const
|
|
{
|
|
auto *m = find_meta(id);
|
|
if (m)
|
|
{
|
|
if (index >= m->members.size())
|
|
return empty_string;
|
|
return m->members[index].alias;
|
|
}
|
|
else
|
|
return empty_string;
|
|
}
|
|
|
|
void ParsedIR::set_name(uint32_t id, const string &name)
|
|
{
|
|
auto &str = meta[id].decoration.alias;
|
|
str.clear();
|
|
|
|
if (name.empty())
|
|
return;
|
|
|
|
// Reserved for temporaries.
|
|
if (name[0] == '_' && name.size() >= 2 && isdigit(name[1]))
|
|
return;
|
|
|
|
str = ensure_valid_identifier(name, false);
|
|
}
|
|
|
|
void ParsedIR::set_member_name(uint32_t id, uint32_t index, const string &name)
|
|
{
|
|
meta[id].members.resize(max(meta[id].members.size(), size_t(index) + 1));
|
|
|
|
auto &str = meta[id].members[index].alias;
|
|
str.clear();
|
|
if (name.empty())
|
|
return;
|
|
|
|
// Reserved for unnamed members.
|
|
if (name[0] == '_' && name.size() >= 3 && name[1] == 'm' && isdigit(name[2]))
|
|
return;
|
|
|
|
str = ensure_valid_identifier(name, true);
|
|
}
|
|
|
|
void ParsedIR::set_decoration_string(uint32_t id, Decoration decoration, const string &argument)
|
|
{
|
|
auto &dec = meta[id].decoration;
|
|
dec.decoration_flags.set(decoration);
|
|
|
|
switch (decoration)
|
|
{
|
|
case DecorationHlslSemanticGOOGLE:
|
|
dec.hlsl_semantic = argument;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ParsedIR::set_decoration(uint32_t id, Decoration decoration, uint32_t argument)
|
|
{
|
|
auto &dec = meta[id].decoration;
|
|
dec.decoration_flags.set(decoration);
|
|
|
|
switch (decoration)
|
|
{
|
|
case DecorationBuiltIn:
|
|
dec.builtin = true;
|
|
dec.builtin_type = static_cast<BuiltIn>(argument);
|
|
break;
|
|
|
|
case DecorationLocation:
|
|
dec.location = argument;
|
|
break;
|
|
|
|
case DecorationComponent:
|
|
dec.component = argument;
|
|
break;
|
|
|
|
case DecorationOffset:
|
|
dec.offset = argument;
|
|
break;
|
|
|
|
case DecorationArrayStride:
|
|
dec.array_stride = argument;
|
|
break;
|
|
|
|
case DecorationMatrixStride:
|
|
dec.matrix_stride = argument;
|
|
break;
|
|
|
|
case DecorationBinding:
|
|
dec.binding = argument;
|
|
break;
|
|
|
|
case DecorationDescriptorSet:
|
|
dec.set = argument;
|
|
break;
|
|
|
|
case DecorationInputAttachmentIndex:
|
|
dec.input_attachment = argument;
|
|
break;
|
|
|
|
case DecorationSpecId:
|
|
dec.spec_id = argument;
|
|
break;
|
|
|
|
case DecorationIndex:
|
|
dec.index = argument;
|
|
break;
|
|
|
|
case DecorationHlslCounterBufferGOOGLE:
|
|
meta[id].hlsl_magic_counter_buffer = argument;
|
|
meta[argument].hlsl_is_magic_counter_buffer = true;
|
|
break;
|
|
|
|
case DecorationFPRoundingMode:
|
|
dec.fp_rounding_mode = static_cast<FPRoundingMode>(argument);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ParsedIR::set_member_decoration(uint32_t id, uint32_t index, Decoration decoration, uint32_t argument)
|
|
{
|
|
meta[id].members.resize(max(meta[id].members.size(), size_t(index) + 1));
|
|
auto &dec = meta[id].members[index];
|
|
dec.decoration_flags.set(decoration);
|
|
|
|
switch (decoration)
|
|
{
|
|
case DecorationBuiltIn:
|
|
dec.builtin = true;
|
|
dec.builtin_type = static_cast<BuiltIn>(argument);
|
|
break;
|
|
|
|
case DecorationLocation:
|
|
dec.location = argument;
|
|
break;
|
|
|
|
case DecorationComponent:
|
|
dec.component = argument;
|
|
break;
|
|
|
|
case DecorationBinding:
|
|
dec.binding = argument;
|
|
break;
|
|
|
|
case DecorationOffset:
|
|
dec.offset = argument;
|
|
break;
|
|
|
|
case DecorationSpecId:
|
|
dec.spec_id = argument;
|
|
break;
|
|
|
|
case DecorationMatrixStride:
|
|
dec.matrix_stride = argument;
|
|
break;
|
|
|
|
case DecorationIndex:
|
|
dec.index = argument;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Recursively marks any constants referenced by the specified constant instruction as being used
|
|
// as an array length. The id must be a constant instruction (SPIRConstant or SPIRConstantOp).
|
|
void ParsedIR::mark_used_as_array_length(uint32_t id)
|
|
{
|
|
switch (ids[id].get_type())
|
|
{
|
|
case TypeConstant:
|
|
get<SPIRConstant>(id).is_used_as_array_length = true;
|
|
break;
|
|
|
|
case TypeConstantOp:
|
|
{
|
|
auto &cop = get<SPIRConstantOp>(id);
|
|
for (uint32_t arg_id : cop.arguments)
|
|
mark_used_as_array_length(arg_id);
|
|
break;
|
|
}
|
|
|
|
case TypeUndef:
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
Bitset ParsedIR::get_buffer_block_flags(const SPIRVariable &var) const
|
|
{
|
|
auto &type = get<SPIRType>(var.basetype);
|
|
assert(type.basetype == SPIRType::Struct);
|
|
|
|
// Some flags like non-writable, non-readable are actually found
|
|
// as member decorations. If all members have a decoration set, propagate
|
|
// the decoration up as a regular variable decoration.
|
|
Bitset base_flags;
|
|
auto *m = find_meta(var.self);
|
|
if (m)
|
|
base_flags = m->decoration.decoration_flags;
|
|
|
|
if (type.member_types.empty())
|
|
return base_flags;
|
|
|
|
Bitset all_members_flags = get_member_decoration_bitset(type.self, 0);
|
|
for (uint32_t i = 1; i < uint32_t(type.member_types.size()); i++)
|
|
all_members_flags.merge_and(get_member_decoration_bitset(type.self, i));
|
|
|
|
base_flags.merge_or(all_members_flags);
|
|
return base_flags;
|
|
}
|
|
|
|
const Bitset &ParsedIR::get_member_decoration_bitset(uint32_t id, uint32_t index) const
|
|
{
|
|
auto *m = find_meta(id);
|
|
if (m)
|
|
{
|
|
if (index >= m->members.size())
|
|
return cleared_bitset;
|
|
return m->members[index].decoration_flags;
|
|
}
|
|
else
|
|
return cleared_bitset;
|
|
}
|
|
|
|
bool ParsedIR::has_decoration(uint32_t id, Decoration decoration) const
|
|
{
|
|
return get_decoration_bitset(id).get(decoration);
|
|
}
|
|
|
|
uint32_t ParsedIR::get_decoration(uint32_t id, Decoration decoration) const
|
|
{
|
|
auto *m = find_meta(id);
|
|
if (!m)
|
|
return 0;
|
|
|
|
auto &dec = m->decoration;
|
|
if (!dec.decoration_flags.get(decoration))
|
|
return 0;
|
|
|
|
switch (decoration)
|
|
{
|
|
case DecorationBuiltIn:
|
|
return dec.builtin_type;
|
|
case DecorationLocation:
|
|
return dec.location;
|
|
case DecorationComponent:
|
|
return dec.component;
|
|
case DecorationOffset:
|
|
return dec.offset;
|
|
case DecorationBinding:
|
|
return dec.binding;
|
|
case DecorationDescriptorSet:
|
|
return dec.set;
|
|
case DecorationInputAttachmentIndex:
|
|
return dec.input_attachment;
|
|
case DecorationSpecId:
|
|
return dec.spec_id;
|
|
case DecorationArrayStride:
|
|
return dec.array_stride;
|
|
case DecorationMatrixStride:
|
|
return dec.matrix_stride;
|
|
case DecorationIndex:
|
|
return dec.index;
|
|
case DecorationFPRoundingMode:
|
|
return dec.fp_rounding_mode;
|
|
default:
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
const string &ParsedIR::get_decoration_string(uint32_t id, Decoration decoration) const
|
|
{
|
|
auto *m = find_meta(id);
|
|
if (!m)
|
|
return empty_string;
|
|
|
|
auto &dec = m->decoration;
|
|
|
|
if (!dec.decoration_flags.get(decoration))
|
|
return empty_string;
|
|
|
|
switch (decoration)
|
|
{
|
|
case DecorationHlslSemanticGOOGLE:
|
|
return dec.hlsl_semantic;
|
|
|
|
default:
|
|
return empty_string;
|
|
}
|
|
}
|
|
|
|
void ParsedIR::unset_decoration(uint32_t id, Decoration decoration)
|
|
{
|
|
auto &dec = meta[id].decoration;
|
|
dec.decoration_flags.clear(decoration);
|
|
switch (decoration)
|
|
{
|
|
case DecorationBuiltIn:
|
|
dec.builtin = false;
|
|
break;
|
|
|
|
case DecorationLocation:
|
|
dec.location = 0;
|
|
break;
|
|
|
|
case DecorationComponent:
|
|
dec.component = 0;
|
|
break;
|
|
|
|
case DecorationOffset:
|
|
dec.offset = 0;
|
|
break;
|
|
|
|
case DecorationBinding:
|
|
dec.binding = 0;
|
|
break;
|
|
|
|
case DecorationDescriptorSet:
|
|
dec.set = 0;
|
|
break;
|
|
|
|
case DecorationInputAttachmentIndex:
|
|
dec.input_attachment = 0;
|
|
break;
|
|
|
|
case DecorationSpecId:
|
|
dec.spec_id = 0;
|
|
break;
|
|
|
|
case DecorationHlslSemanticGOOGLE:
|
|
dec.hlsl_semantic.clear();
|
|
break;
|
|
|
|
case DecorationFPRoundingMode:
|
|
dec.fp_rounding_mode = FPRoundingModeMax;
|
|
break;
|
|
|
|
case DecorationHlslCounterBufferGOOGLE:
|
|
{
|
|
auto &counter = meta[id].hlsl_magic_counter_buffer;
|
|
if (counter)
|
|
{
|
|
meta[counter].hlsl_is_magic_counter_buffer = false;
|
|
counter = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool ParsedIR::has_member_decoration(uint32_t id, uint32_t index, Decoration decoration) const
|
|
{
|
|
return get_member_decoration_bitset(id, index).get(decoration);
|
|
}
|
|
|
|
uint32_t ParsedIR::get_member_decoration(uint32_t id, uint32_t index, Decoration decoration) const
|
|
{
|
|
auto *m = find_meta(id);
|
|
if (!m)
|
|
return 0;
|
|
|
|
if (index >= m->members.size())
|
|
return 0;
|
|
|
|
auto &dec = m->members[index];
|
|
if (!dec.decoration_flags.get(decoration))
|
|
return 0;
|
|
|
|
switch (decoration)
|
|
{
|
|
case DecorationBuiltIn:
|
|
return dec.builtin_type;
|
|
case DecorationLocation:
|
|
return dec.location;
|
|
case DecorationComponent:
|
|
return dec.component;
|
|
case DecorationBinding:
|
|
return dec.binding;
|
|
case DecorationOffset:
|
|
return dec.offset;
|
|
case DecorationSpecId:
|
|
return dec.spec_id;
|
|
case DecorationIndex:
|
|
return dec.index;
|
|
default:
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
const Bitset &ParsedIR::get_decoration_bitset(uint32_t id) const
|
|
{
|
|
auto *m = find_meta(id);
|
|
if (m)
|
|
{
|
|
auto &dec = m->decoration;
|
|
return dec.decoration_flags;
|
|
}
|
|
else
|
|
return cleared_bitset;
|
|
}
|
|
|
|
void ParsedIR::set_member_decoration_string(uint32_t id, uint32_t index, Decoration decoration, const string &argument)
|
|
{
|
|
meta[id].members.resize(max(meta[id].members.size(), size_t(index) + 1));
|
|
auto &dec = meta[id].members[index];
|
|
dec.decoration_flags.set(decoration);
|
|
|
|
switch (decoration)
|
|
{
|
|
case DecorationHlslSemanticGOOGLE:
|
|
dec.hlsl_semantic = argument;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
const string &ParsedIR::get_member_decoration_string(uint32_t id, uint32_t index, Decoration decoration) const
|
|
{
|
|
auto *m = find_meta(id);
|
|
if (m)
|
|
{
|
|
if (!has_member_decoration(id, index, decoration))
|
|
return empty_string;
|
|
|
|
auto &dec = m->members[index];
|
|
|
|
switch (decoration)
|
|
{
|
|
case DecorationHlslSemanticGOOGLE:
|
|
return dec.hlsl_semantic;
|
|
|
|
default:
|
|
return empty_string;
|
|
}
|
|
}
|
|
else
|
|
return empty_string;
|
|
}
|
|
|
|
void ParsedIR::unset_member_decoration(uint32_t id, uint32_t index, Decoration decoration)
|
|
{
|
|
auto &m = meta[id];
|
|
if (index >= m.members.size())
|
|
return;
|
|
|
|
auto &dec = m.members[index];
|
|
|
|
dec.decoration_flags.clear(decoration);
|
|
switch (decoration)
|
|
{
|
|
case DecorationBuiltIn:
|
|
dec.builtin = false;
|
|
break;
|
|
|
|
case DecorationLocation:
|
|
dec.location = 0;
|
|
break;
|
|
|
|
case DecorationComponent:
|
|
dec.component = 0;
|
|
break;
|
|
|
|
case DecorationOffset:
|
|
dec.offset = 0;
|
|
break;
|
|
|
|
case DecorationSpecId:
|
|
dec.spec_id = 0;
|
|
break;
|
|
|
|
case DecorationHlslSemanticGOOGLE:
|
|
dec.hlsl_semantic.clear();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint32_t ParsedIR::increase_bound_by(uint32_t incr_amount)
|
|
{
|
|
auto curr_bound = ids.size();
|
|
auto new_bound = curr_bound + incr_amount;
|
|
ids.resize(new_bound);
|
|
block_meta.resize(new_bound);
|
|
return uint32_t(curr_bound);
|
|
}
|
|
|
|
void ParsedIR::remove_typed_id(Types type, uint32_t id)
|
|
{
|
|
auto &type_ids = ids_for_type[type];
|
|
type_ids.erase(remove(begin(type_ids), end(type_ids), id), end(type_ids));
|
|
}
|
|
|
|
void ParsedIR::reset_all_of_type(Types type)
|
|
{
|
|
for (auto &id : ids_for_type[type])
|
|
if (ids[id].get_type() == type)
|
|
ids[id].reset();
|
|
|
|
ids_for_type[type].clear();
|
|
}
|
|
|
|
void ParsedIR::add_typed_id(Types type, uint32_t id)
|
|
{
|
|
if (loop_iteration_depth)
|
|
SPIRV_CROSS_THROW("Cannot add typed ID while looping over it.");
|
|
|
|
switch (type)
|
|
{
|
|
case TypeConstant:
|
|
ids_for_constant_or_variable.push_back(id);
|
|
ids_for_constant_or_type.push_back(id);
|
|
break;
|
|
|
|
case TypeVariable:
|
|
ids_for_constant_or_variable.push_back(id);
|
|
break;
|
|
|
|
case TypeType:
|
|
case TypeConstantOp:
|
|
ids_for_constant_or_type.push_back(id);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (ids[id].empty())
|
|
{
|
|
ids_for_type[type].push_back(id);
|
|
}
|
|
else if (ids[id].get_type() != type)
|
|
{
|
|
remove_typed_id(ids[id].get_type(), id);
|
|
ids_for_type[type].push_back(id);
|
|
}
|
|
}
|
|
|
|
const Meta *ParsedIR::find_meta(uint32_t id) const
|
|
{
|
|
auto itr = meta.find(id);
|
|
if (itr != end(meta))
|
|
return &itr->second;
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
Meta *ParsedIR::find_meta(uint32_t id)
|
|
{
|
|
auto itr = meta.find(id);
|
|
if (itr != end(meta))
|
|
return &itr->second;
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
} // namespace spirv_cross
|