[spirv-reader] Handle old-style storage buffers
Old way: - struct decorated with BufferBlock - Uniform storage class New way - struct decorated with Block - StorageBuffer storage class Also fixes the result type for an access chain. Bug: tint:99 Change-Id: I2324ba94bb19b369d206313de798bdfec6099fe0 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/24605 Reviewed-by: dan sinclair <dsinclair@chromium.org>
This commit is contained in:
parent
13904a612d
commit
6982c22eee
|
@ -38,7 +38,7 @@ ast::PipelineStage EnumConverter::ToPipelineStage(SpvExecutionModel model) {
|
||||||
return ast::PipelineStage::kNone;
|
return ast::PipelineStage::kNone;
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::StorageClass EnumConverter::ToStorageClass(SpvStorageClass sc) {
|
ast::StorageClass EnumConverter::ToStorageClass(const SpvStorageClass sc) {
|
||||||
switch (sc) {
|
switch (sc) {
|
||||||
case SpvStorageClassInput:
|
case SpvStorageClassInput:
|
||||||
return ast::StorageClass::kInput;
|
return ast::StorageClass::kInput;
|
||||||
|
|
|
@ -44,7 +44,7 @@ class EnumConverter {
|
||||||
/// On failure, logs an error and returns kNone
|
/// On failure, logs an error and returns kNone
|
||||||
/// @param sc the SPIR-V storage class
|
/// @param sc the SPIR-V storage class
|
||||||
/// @returns a Tint AST storage class
|
/// @returns a Tint AST storage class
|
||||||
ast::StorageClass ToStorageClass(SpvStorageClass sc);
|
ast::StorageClass ToStorageClass(const SpvStorageClass sc);
|
||||||
|
|
||||||
/// Converts a SPIR-V Builtin value a Tint Builtin.
|
/// Converts a SPIR-V Builtin value a Tint Builtin.
|
||||||
/// On failure, logs an error and returns kNone
|
/// On failure, logs an error and returns kNone
|
||||||
|
|
|
@ -631,7 +631,10 @@ bool FunctionEmitter::EmitBody() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
RegisterValuesNeedingNamedOrHoistedDefinition();
|
if (!RegisterLocallyDefinedValues()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FindValuesNeedingNamedOrHoistedDefinition();
|
||||||
|
|
||||||
if (!EmitFunctionVariables()) {
|
if (!EmitFunctionVariables()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -2419,10 +2422,10 @@ bool FunctionEmitter::EmitStatementsInBasicBlock(const BlockInfo& block_info,
|
||||||
for (auto id : sorted_by_index(block_info.hoisted_ids)) {
|
for (auto id : sorted_by_index(block_info.hoisted_ids)) {
|
||||||
const auto* def_inst = def_use_mgr_->GetDef(id);
|
const auto* def_inst = def_use_mgr_->GetDef(id);
|
||||||
assert(def_inst);
|
assert(def_inst);
|
||||||
AddStatement(
|
auto* ast_type =
|
||||||
std::make_unique<ast::VariableDeclStatement>(parser_impl_.MakeVariable(
|
RemapStorageClass(parser_impl_.ConvertType(def_inst->type_id()), id);
|
||||||
id, ast::StorageClass::kFunction,
|
AddStatement(std::make_unique<ast::VariableDeclStatement>(
|
||||||
parser_impl_.ConvertType(def_inst->type_id()))));
|
parser_impl_.MakeVariable(id, ast::StorageClass::kFunction, ast_type)));
|
||||||
// Save this as an already-named value.
|
// Save this as an already-named value.
|
||||||
identifier_values_.insert(id);
|
identifier_values_.insert(id);
|
||||||
}
|
}
|
||||||
|
@ -2580,12 +2583,14 @@ bool FunctionEmitter::EmitStatement(const spvtools::opt::Instruction& inst) {
|
||||||
expr.type = expr.type->AsPointer()->type();
|
expr.type = expr.type->AsPointer()->type();
|
||||||
return EmitConstDefOrWriteToHoistedVar(inst, std::move(expr));
|
return EmitConstDefOrWriteToHoistedVar(inst, std::move(expr));
|
||||||
}
|
}
|
||||||
case SpvOpCopyObject:
|
case SpvOpCopyObject: {
|
||||||
// Arguably, OpCopyObject is purely combinatorial. On the other hand,
|
// Arguably, OpCopyObject is purely combinatorial. On the other hand,
|
||||||
// it exists to make a new name for something. So we choose to make
|
// it exists to make a new name for something. So we choose to make
|
||||||
// a new named constant definition.
|
// a new named constant definition.
|
||||||
return EmitConstDefOrWriteToHoistedVar(
|
auto expr = MakeExpression(inst.GetSingleWordInOperand(0));
|
||||||
inst, MakeExpression(inst.GetSingleWordInOperand(0)));
|
expr.type = RemapStorageClass(expr.type, result_id);
|
||||||
|
return EmitConstDefOrWriteToHoistedVar(inst, std::move(expr));
|
||||||
|
}
|
||||||
case SpvOpPhi: {
|
case SpvOpPhi: {
|
||||||
// Emit a read from the associated state variable.
|
// Emit a read from the associated state variable.
|
||||||
auto expr = TypedExpression(
|
auto expr = TypedExpression(
|
||||||
|
@ -2754,7 +2759,6 @@ TypedExpression FunctionEmitter::MakeAccessChain(
|
||||||
// ever-deeper nested indexing expressions. Start off with an expression
|
// ever-deeper nested indexing expressions. Start off with an expression
|
||||||
// for the base, and then bury that inside nested indexing expressions.
|
// for the base, and then bury that inside nested indexing expressions.
|
||||||
TypedExpression current_expr(MakeOperand(inst, 0));
|
TypedExpression current_expr(MakeOperand(inst, 0));
|
||||||
|
|
||||||
const auto constants = constant_mgr_->GetOperandConstants(&inst);
|
const auto constants = constant_mgr_->GetOperandConstants(&inst);
|
||||||
static const char* swizzles[] = {"x", "y", "z", "w"};
|
static const char* swizzles[] = {"x", "y", "z", "w"};
|
||||||
|
|
||||||
|
@ -2803,9 +2807,10 @@ TypedExpression FunctionEmitter::MakeAccessChain(
|
||||||
// Skip past the member index that gets us to Position.
|
// Skip past the member index that gets us to Position.
|
||||||
first_index = first_index + 1;
|
first_index = first_index + 1;
|
||||||
// Replace the gl_PerVertex reference with the gl_Position reference
|
// Replace the gl_PerVertex reference with the gl_Position reference
|
||||||
|
ptr_ty_id = builtin_position_info.member_pointer_type_id;
|
||||||
current_expr.expr =
|
current_expr.expr =
|
||||||
std::make_unique<ast::IdentifierExpression>(namer_.Name(base_id));
|
std::make_unique<ast::IdentifierExpression>(namer_.Name(base_id));
|
||||||
ptr_ty_id = builtin_position_info.member_pointer_type_id;
|
current_expr.type = parser_impl_.ConvertType(ptr_ty_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2815,6 +2820,7 @@ TypedExpression FunctionEmitter::MakeAccessChain(
|
||||||
<< " base pointer is not of pointer type";
|
<< " base pointer is not of pointer type";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
SpvStorageClass storage_class = ptr_type->AsPointer()->storage_class();
|
||||||
const auto* pointee_type = ptr_type->AsPointer()->pointee_type();
|
const auto* pointee_type = ptr_type->AsPointer()->pointee_type();
|
||||||
for (uint32_t index = first_index; index < num_in_operands; ++index) {
|
for (uint32_t index = first_index; index < num_in_operands; ++index) {
|
||||||
const auto* index_const =
|
const auto* index_const =
|
||||||
|
@ -2904,9 +2910,13 @@ TypedExpression FunctionEmitter::MakeAccessChain(
|
||||||
<< type_mgr_->GetId(pointee_type) << " " << pointee_type->str();
|
<< type_mgr_->GetId(pointee_type) << " " << pointee_type->str();
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
current_expr.reset(TypedExpression(
|
const auto pointee_type_id = type_mgr_->GetId(pointee_type);
|
||||||
parser_impl_.ConvertType(type_mgr_->GetId(pointee_type)),
|
const auto pointer_type_id =
|
||||||
std::move(next_expr)));
|
type_mgr_->FindPointerToType(pointee_type_id, storage_class);
|
||||||
|
auto* ast_pointer_type = parser_impl_.ConvertType(pointer_type_id);
|
||||||
|
assert(ast_pointer_type);
|
||||||
|
assert(ast_pointer_type->IsPointer);
|
||||||
|
current_expr.reset(TypedExpression(ast_pointer_type, std::move(next_expr)));
|
||||||
}
|
}
|
||||||
return current_expr;
|
return current_expr;
|
||||||
}
|
}
|
||||||
|
@ -3074,7 +3084,7 @@ TypedExpression FunctionEmitter::MakeVectorShuffle(
|
||||||
result_type, std::move(values))};
|
result_type, std::move(values))};
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionEmitter::RegisterValuesNeedingNamedOrHoistedDefinition() {
|
bool FunctionEmitter::RegisterLocallyDefinedValues() {
|
||||||
// Create a DefInfo for each value definition in this function.
|
// Create a DefInfo for each value definition in this function.
|
||||||
size_t index = 0;
|
size_t index = 0;
|
||||||
for (auto block_id : block_order_) {
|
for (auto block_id : block_order_) {
|
||||||
|
@ -3087,9 +3097,72 @@ void FunctionEmitter::RegisterValuesNeedingNamedOrHoistedDefinition() {
|
||||||
}
|
}
|
||||||
def_info_[result_id] = std::make_unique<DefInfo>(inst, block_pos, index);
|
def_info_[result_id] = std::make_unique<DefInfo>(inst, block_pos, index);
|
||||||
index++;
|
index++;
|
||||||
|
|
||||||
|
// Determine storage class for pointer values. Do this in order because
|
||||||
|
// we might rely on the storage class for a previously-visited definition.
|
||||||
|
// Logical pointers can't be transmitted through OpPhi, so remaining
|
||||||
|
// pointer definitions are SSA values, and their definitions must be
|
||||||
|
// visited before their uses.
|
||||||
|
auto& storage_class = def_info_[result_id]->storage_class;
|
||||||
|
const auto* type = type_mgr_->GetType(inst.type_id());
|
||||||
|
if (type && type->AsPointer()) {
|
||||||
|
const auto* ast_type = parser_impl_.ConvertType(inst.type_id());
|
||||||
|
if (ast_type && ast_type->AsPointer()) {
|
||||||
|
storage_class = ast_type->AsPointer()->storage_class();
|
||||||
}
|
}
|
||||||
|
switch (inst.opcode()) {
|
||||||
|
case SpvOpUndef:
|
||||||
|
case SpvOpVariable:
|
||||||
|
// Keep the default decision based on the result type.
|
||||||
|
break;
|
||||||
|
case SpvOpAccessChain:
|
||||||
|
case SpvOpCopyObject:
|
||||||
|
// Inherit from the first operand. We need this so we can pick up
|
||||||
|
// a remapped storage buffer.
|
||||||
|
storage_class =
|
||||||
|
GetStorageClassForPointerValue(inst.GetSingleWordInOperand(0));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return Fail() << "pointer defined in function from unknown opcode: "
|
||||||
|
<< inst.PrettyPrint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ast::StorageClass FunctionEmitter::GetStorageClassForPointerValue(uint32_t id) {
|
||||||
|
auto where = def_info_.find(id);
|
||||||
|
if (where != def_info_.end()) {
|
||||||
|
return where->second.get()->storage_class;
|
||||||
|
}
|
||||||
|
const auto type_id = def_use_mgr_->GetDef(id)->type_id();
|
||||||
|
if (type_id) {
|
||||||
|
auto* ast_type = parser_impl_.ConvertType(type_id);
|
||||||
|
if (ast_type && ast_type->IsPointer()) {
|
||||||
|
return ast_type->AsPointer()->storage_class();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ast::StorageClass::kNone;
|
||||||
|
}
|
||||||
|
|
||||||
|
ast::type::Type* FunctionEmitter::RemapStorageClass(ast::type::Type* type,
|
||||||
|
uint32_t result_id) {
|
||||||
|
if (type->IsPointer()) {
|
||||||
|
// Remap an old-style storage buffer pointer to a new-style storage
|
||||||
|
// buffer pointer.
|
||||||
|
const auto* ast_ptr_type = type->AsPointer();
|
||||||
|
const auto sc = GetStorageClassForPointerValue(result_id);
|
||||||
|
if (ast_ptr_type->storage_class() != sc) {
|
||||||
|
return parser_impl_.context().type_mgr().Get(
|
||||||
|
std::make_unique<ast::type::PointerType>(ast_ptr_type->type(), sc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FunctionEmitter::FindValuesNeedingNamedOrHoistedDefinition() {
|
||||||
// Mark vector operands of OpVectorShuffle as needing a named definition,
|
// Mark vector operands of OpVectorShuffle as needing a named definition,
|
||||||
// but only if they are defined in this function as well.
|
// but only if they are defined in this function as well.
|
||||||
for (auto& id_def_info_pair : def_info_) {
|
for (auto& id_def_info_pair : def_info_) {
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include "src/ast/expression.h"
|
#include "src/ast/expression.h"
|
||||||
#include "src/ast/module.h"
|
#include "src/ast/module.h"
|
||||||
#include "src/ast/statement.h"
|
#include "src/ast/statement.h"
|
||||||
|
#include "src/ast/storage_class.h"
|
||||||
#include "src/reader/spirv/construct.h"
|
#include "src/reader/spirv/construct.h"
|
||||||
#include "src/reader/spirv/fail_stream.h"
|
#include "src/reader/spirv/fail_stream.h"
|
||||||
#include "src/reader/spirv/namer.h"
|
#include "src/reader/spirv/namer.h"
|
||||||
|
@ -192,7 +193,7 @@ inline std::ostream& operator<<(std::ostream& o, const BlockInfo& bi) {
|
||||||
|
|
||||||
/// Bookkeeping info for a SPIR-V ID defined in the function.
|
/// Bookkeeping info for a SPIR-V ID defined in the function.
|
||||||
/// This will be valid for result IDs for:
|
/// This will be valid for result IDs for:
|
||||||
/// - instructions that are not OpLabel, OpVariable, and OpFunctionParameter
|
/// - instructions that are not OpLabel, and not OpFunctionParameter
|
||||||
/// - are defined in a basic block visited in the block-order for the function.
|
/// - are defined in a basic block visited in the block-order for the function.
|
||||||
struct DefInfo {
|
struct DefInfo {
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
|
@ -243,8 +244,15 @@ struct DefInfo {
|
||||||
|
|
||||||
/// If the definition is an OpPhi, then |phi_var| is the name of the
|
/// If the definition is an OpPhi, then |phi_var| is the name of the
|
||||||
/// variable that stores the value carried from parent basic blocks into
|
/// variable that stores the value carried from parent basic blocks into
|
||||||
// the basic block containing the OpPhi. Otherwise this is the empty string.
|
/// the basic block containing the OpPhi. Otherwise this is the empty string.
|
||||||
std::string phi_var;
|
std::string phi_var;
|
||||||
|
|
||||||
|
/// The storage class to use for this value, if it is of pointer type.
|
||||||
|
/// This is required to carry a stroage class override from a storage
|
||||||
|
/// buffer expressed in the old style (with Uniform storage class)
|
||||||
|
/// that needs to be remapped to StorageBuffer storage class.
|
||||||
|
/// This is kNone for non-pointers.
|
||||||
|
ast::StorageClass storage_class = ast::StorageClass::kNone;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::ostream& operator<<(std::ostream& o, const DefInfo& di) {
|
inline std::ostream& operator<<(std::ostream& o, const DefInfo& di) {
|
||||||
|
@ -254,8 +262,11 @@ inline std::ostream& operator<<(std::ostream& o, const DefInfo& di) {
|
||||||
<< " last_use_pos: " << di.last_use_pos << " requires_named_const_def: "
|
<< " last_use_pos: " << di.last_use_pos << " requires_named_const_def: "
|
||||||
<< (di.requires_named_const_def ? "true" : "false")
|
<< (di.requires_named_const_def ? "true" : "false")
|
||||||
<< " requires_hoisted_def: " << (di.requires_hoisted_def ? "true" : "false")
|
<< " requires_hoisted_def: " << (di.requires_hoisted_def ? "true" : "false")
|
||||||
<< " phi_var: '" << di.phi_var << "'"
|
<< " phi_var: '" << di.phi_var << "'";
|
||||||
<< "}";
|
if (di.storage_class != ast::StorageClass::kNone) {
|
||||||
|
o << " sc:" << int(di.storage_class);
|
||||||
|
}
|
||||||
|
o << "}";
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -367,7 +378,24 @@ class FunctionEmitter {
|
||||||
/// @returns false if bad nesting has been detected.
|
/// @returns false if bad nesting has been detected.
|
||||||
bool FindIfSelectionInternalHeaders();
|
bool FindIfSelectionInternalHeaders();
|
||||||
|
|
||||||
/// Record the SPIR-V IDs of non-constants that should get a 'const'
|
/// Creates a DefInfo record for each locally defined SPIR-V ID.
|
||||||
|
/// Populates the |def_info_| mapping with basic results.
|
||||||
|
/// @returns false on failure
|
||||||
|
bool RegisterLocallyDefinedValues();
|
||||||
|
|
||||||
|
/// Returns the Tint storage class for the given SPIR-V ID that is a
|
||||||
|
/// pointer value.
|
||||||
|
/// @returns the storage class
|
||||||
|
ast::StorageClass GetStorageClassForPointerValue(uint32_t id);
|
||||||
|
|
||||||
|
/// Remaps the storage class for the type of a locally-defined value,
|
||||||
|
/// if necessary. If it's not a pointer type, or if its storage class
|
||||||
|
/// already matches, then the result is a copy of the |type| argument.
|
||||||
|
/// @param type the AST type
|
||||||
|
/// @param result_id the SPIR-V ID for the locally defined value
|
||||||
|
ast::type::Type* RemapStorageClass(ast::type::Type* type, uint32_t result_id);
|
||||||
|
|
||||||
|
/// Marks locally defined values when they should get a 'const'
|
||||||
/// definition in WGSL, or a 'var' definition at an outer scope.
|
/// definition in WGSL, or a 'var' definition at an outer scope.
|
||||||
/// This occurs in several cases:
|
/// This occurs in several cases:
|
||||||
/// - When a SPIR-V instruction might use the dynamically computed value
|
/// - When a SPIR-V instruction might use the dynamically computed value
|
||||||
|
@ -382,8 +410,8 @@ class FunctionEmitter {
|
||||||
/// - When a definition is in a construct that does not enclose all the
|
/// - When a definition is in a construct that does not enclose all the
|
||||||
/// uses. In this case the definition's |requires_hoisted_def| property
|
/// uses. In this case the definition's |requires_hoisted_def| property
|
||||||
/// is set to true.
|
/// is set to true.
|
||||||
/// Populates the |def_info_| mapping.
|
/// Updates the |def_info_| mapping.
|
||||||
void RegisterValuesNeedingNamedOrHoistedDefinition();
|
void FindValuesNeedingNamedOrHoistedDefinition();
|
||||||
|
|
||||||
/// Emits declarations of function variables.
|
/// Emits declarations of function variables.
|
||||||
/// @returns false if emission failed.
|
/// @returns false if emission failed.
|
||||||
|
|
|
@ -287,10 +287,10 @@ TEST_F(SpvParserTest_CompositeExtract, Vector_IndexTooBigError) {
|
||||||
TEST_F(SpvParserTest_CompositeExtract, Matrix) {
|
TEST_F(SpvParserTest_CompositeExtract, Matrix) {
|
||||||
const auto assembly = Preamble() + R"(
|
const auto assembly = Preamble() + R"(
|
||||||
%ptr = OpTypePointer Function %m3v2float
|
%ptr = OpTypePointer Function %m3v2float
|
||||||
%var = OpVariable %ptr Function
|
|
||||||
|
|
||||||
%100 = OpFunction %void None %voidfn
|
%100 = OpFunction %void None %voidfn
|
||||||
%entry = OpLabel
|
%entry = OpLabel
|
||||||
|
%var = OpVariable %ptr Function
|
||||||
%1 = OpLoad %m3v2float %var
|
%1 = OpLoad %m3v2float %var
|
||||||
%2 = OpCompositeExtract %v2float %1 2
|
%2 = OpCompositeExtract %v2float %1 2
|
||||||
OpReturn
|
OpReturn
|
||||||
|
@ -318,10 +318,10 @@ TEST_F(SpvParserTest_CompositeExtract, Matrix) {
|
||||||
TEST_F(SpvParserTest_CompositeExtract, Matrix_IndexTooBigError) {
|
TEST_F(SpvParserTest_CompositeExtract, Matrix_IndexTooBigError) {
|
||||||
const auto assembly = Preamble() + R"(
|
const auto assembly = Preamble() + R"(
|
||||||
%ptr = OpTypePointer Function %m3v2float
|
%ptr = OpTypePointer Function %m3v2float
|
||||||
%var = OpVariable %ptr Function
|
|
||||||
|
|
||||||
%100 = OpFunction %void None %voidfn
|
%100 = OpFunction %void None %voidfn
|
||||||
%entry = OpLabel
|
%entry = OpLabel
|
||||||
|
%var = OpVariable %ptr Function
|
||||||
%1 = OpLoad %m3v2float %var
|
%1 = OpLoad %m3v2float %var
|
||||||
%2 = OpCompositeExtract %v2float %1 3
|
%2 = OpCompositeExtract %v2float %1 3
|
||||||
OpReturn
|
OpReturn
|
||||||
|
@ -338,10 +338,10 @@ TEST_F(SpvParserTest_CompositeExtract, Matrix_IndexTooBigError) {
|
||||||
TEST_F(SpvParserTest_CompositeExtract, Matrix_Vector) {
|
TEST_F(SpvParserTest_CompositeExtract, Matrix_Vector) {
|
||||||
const auto assembly = Preamble() + R"(
|
const auto assembly = Preamble() + R"(
|
||||||
%ptr = OpTypePointer Function %m3v2float
|
%ptr = OpTypePointer Function %m3v2float
|
||||||
%var = OpVariable %ptr Function
|
|
||||||
|
|
||||||
%100 = OpFunction %void None %voidfn
|
%100 = OpFunction %void None %voidfn
|
||||||
%entry = OpLabel
|
%entry = OpLabel
|
||||||
|
%var = OpVariable %ptr Function
|
||||||
%1 = OpLoad %m3v2float %var
|
%1 = OpLoad %m3v2float %var
|
||||||
%2 = OpCompositeExtract %float %1 2 1
|
%2 = OpCompositeExtract %float %1 2 1
|
||||||
OpReturn
|
OpReturn
|
||||||
|
@ -372,10 +372,10 @@ TEST_F(SpvParserTest_CompositeExtract, Matrix_Vector) {
|
||||||
TEST_F(SpvParserTest_CompositeExtract, Array) {
|
TEST_F(SpvParserTest_CompositeExtract, Array) {
|
||||||
const auto assembly = Preamble() + R"(
|
const auto assembly = Preamble() + R"(
|
||||||
%ptr = OpTypePointer Function %a_u_5
|
%ptr = OpTypePointer Function %a_u_5
|
||||||
%var = OpVariable %ptr Function
|
|
||||||
|
|
||||||
%100 = OpFunction %void None %voidfn
|
%100 = OpFunction %void None %voidfn
|
||||||
%entry = OpLabel
|
%entry = OpLabel
|
||||||
|
%var = OpVariable %ptr Function
|
||||||
%1 = OpLoad %a_u_5 %var
|
%1 = OpLoad %a_u_5 %var
|
||||||
%2 = OpCompositeExtract %uint %1 3
|
%2 = OpCompositeExtract %uint %1 3
|
||||||
OpReturn
|
OpReturn
|
||||||
|
@ -404,10 +404,10 @@ TEST_F(SpvParserTest_CompositeExtract, RuntimeArray_IsError) {
|
||||||
const auto assembly = Preamble() + R"(
|
const auto assembly = Preamble() + R"(
|
||||||
%rtarr = OpTypeRuntimeArray %uint
|
%rtarr = OpTypeRuntimeArray %uint
|
||||||
%ptr = OpTypePointer Function %rtarr
|
%ptr = OpTypePointer Function %rtarr
|
||||||
%var = OpVariable %ptr Function
|
|
||||||
|
|
||||||
%100 = OpFunction %void None %voidfn
|
%100 = OpFunction %void None %voidfn
|
||||||
%entry = OpLabel
|
%entry = OpLabel
|
||||||
|
%var = OpVariable %ptr Function
|
||||||
%1 = OpLoad %rtarr %var
|
%1 = OpLoad %rtarr %var
|
||||||
%2 = OpCompositeExtract %uint %1 3
|
%2 = OpCompositeExtract %uint %1 3
|
||||||
OpReturn
|
OpReturn
|
||||||
|
@ -423,10 +423,10 @@ TEST_F(SpvParserTest_CompositeExtract, RuntimeArray_IsError) {
|
||||||
TEST_F(SpvParserTest_CompositeExtract, Struct) {
|
TEST_F(SpvParserTest_CompositeExtract, Struct) {
|
||||||
const auto assembly = Preamble() + R"(
|
const auto assembly = Preamble() + R"(
|
||||||
%ptr = OpTypePointer Function %s_v2f_u_i
|
%ptr = OpTypePointer Function %s_v2f_u_i
|
||||||
%var = OpVariable %ptr Function
|
|
||||||
|
|
||||||
%100 = OpFunction %void None %voidfn
|
%100 = OpFunction %void None %voidfn
|
||||||
%entry = OpLabel
|
%entry = OpLabel
|
||||||
|
%var = OpVariable %ptr Function
|
||||||
%1 = OpLoad %s_v2f_u_i %var
|
%1 = OpLoad %s_v2f_u_i %var
|
||||||
%2 = OpCompositeExtract %int %1 2
|
%2 = OpCompositeExtract %int %1 2
|
||||||
OpReturn
|
OpReturn
|
||||||
|
@ -454,10 +454,10 @@ TEST_F(SpvParserTest_CompositeExtract, Struct) {
|
||||||
TEST_F(SpvParserTest_CompositeExtract, Struct_IndexTooBigError) {
|
TEST_F(SpvParserTest_CompositeExtract, Struct_IndexTooBigError) {
|
||||||
const auto assembly = Preamble() + R"(
|
const auto assembly = Preamble() + R"(
|
||||||
%ptr = OpTypePointer Function %s_v2f_u_i
|
%ptr = OpTypePointer Function %s_v2f_u_i
|
||||||
%var = OpVariable %ptr Function
|
|
||||||
|
|
||||||
%100 = OpFunction %void None %voidfn
|
%100 = OpFunction %void None %voidfn
|
||||||
%entry = OpLabel
|
%entry = OpLabel
|
||||||
|
%var = OpVariable %ptr Function
|
||||||
%1 = OpLoad %s_v2f_u_i %var
|
%1 = OpLoad %s_v2f_u_i %var
|
||||||
%2 = OpCompositeExtract %int %1 40
|
%2 = OpCompositeExtract %int %1 40
|
||||||
OpReturn
|
OpReturn
|
||||||
|
@ -476,10 +476,10 @@ TEST_F(SpvParserTest_CompositeExtract, Struct_Array_Matrix_Vector) {
|
||||||
%a_mat = OpTypeArray %m3v2float %uint_3
|
%a_mat = OpTypeArray %m3v2float %uint_3
|
||||||
%s = OpTypeStruct %uint %a_mat
|
%s = OpTypeStruct %uint %a_mat
|
||||||
%ptr = OpTypePointer Function %s
|
%ptr = OpTypePointer Function %s
|
||||||
%var = OpVariable %ptr Function
|
|
||||||
|
|
||||||
%100 = OpFunction %void None %voidfn
|
%100 = OpFunction %void None %voidfn
|
||||||
%entry = OpLabel
|
%entry = OpLabel
|
||||||
|
%var = OpVariable %ptr Function
|
||||||
%1 = OpLoad %s %var
|
%1 = OpLoad %s %var
|
||||||
%2 = OpCompositeExtract %float %1 1 2 0 1
|
%2 = OpCompositeExtract %float %1 1 2 0 1
|
||||||
OpReturn
|
OpReturn
|
||||||
|
@ -553,10 +553,10 @@ VariableDeclStatement{
|
||||||
TEST_F(SpvParserTest_CopyObject, Pointer) {
|
TEST_F(SpvParserTest_CopyObject, Pointer) {
|
||||||
const auto assembly = Preamble() + R"(
|
const auto assembly = Preamble() + R"(
|
||||||
%ptr = OpTypePointer Function %uint
|
%ptr = OpTypePointer Function %uint
|
||||||
%10 = OpVariable %ptr Function
|
|
||||||
|
|
||||||
%100 = OpFunction %void None %voidfn
|
%100 = OpFunction %void None %voidfn
|
||||||
%entry = OpLabel
|
%entry = OpLabel
|
||||||
|
%10 = OpVariable %ptr Function
|
||||||
%1 = OpCopyObject %ptr %10
|
%1 = OpCopyObject %ptr %10
|
||||||
%2 = OpCopyObject %ptr %1
|
%2 = OpCopyObject %ptr %1
|
||||||
OpReturn
|
OpReturn
|
||||||
|
|
|
@ -708,6 +708,245 @@ TEST_F(SpvParserTest, EmitStatement_AccessChain_InvalidPointeeType) {
|
||||||
HasSubstr("Access chain with unknown pointee type %60 void"));
|
HasSubstr("Access chain with unknown pointee type %60 void"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string OldStorageBufferPreamble() {
|
||||||
|
return R"(
|
||||||
|
OpName %myvar "myvar"
|
||||||
|
|
||||||
|
OpDecorate %struct BufferBlock
|
||||||
|
OpMemberDecorate %struct 0 Offset 0
|
||||||
|
OpMemberDecorate %struct 1 Offset 4
|
||||||
|
OpDecorate %arr ArrayStride 4
|
||||||
|
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%voidfn = OpTypeFunction %void
|
||||||
|
%uint = OpTypeInt 32 0
|
||||||
|
|
||||||
|
%uint_0 = OpConstant %uint 0
|
||||||
|
%uint_1 = OpConstant %uint 1
|
||||||
|
|
||||||
|
%arr = OpTypeRuntimeArray %uint
|
||||||
|
%struct = OpTypeStruct %uint %arr
|
||||||
|
%ptr_struct = OpTypePointer Uniform %struct
|
||||||
|
%ptr_uint = OpTypePointer Uniform %uint
|
||||||
|
|
||||||
|
%myvar = OpVariable %ptr_struct Uniform
|
||||||
|
)";
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest, RemapStorageBuffer_TypesAndVarDeclarations) {
|
||||||
|
// Enusure we get the right module-scope declaration. This tests translation
|
||||||
|
// of the structure type, arrays of the structure, pointers to them, and
|
||||||
|
// OpVariable of these.
|
||||||
|
const auto assembly = OldStorageBufferPreamble();
|
||||||
|
auto* p = parser(test::Assemble(assembly));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
|
||||||
|
<< assembly << p->error();
|
||||||
|
const auto module_str = p->module().to_str();
|
||||||
|
EXPECT_THAT(module_str, HasSubstr(R"(
|
||||||
|
Variable{
|
||||||
|
myvar
|
||||||
|
storage_buffer
|
||||||
|
__alias_S__struct_S
|
||||||
|
}
|
||||||
|
RTArr -> __array__u32_stride_4
|
||||||
|
S -> __struct_S)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest,
|
||||||
|
RemapStorageBuffer_ThroughAccessChain_NonCascaded) {
|
||||||
|
const auto assembly = OldStorageBufferPreamble() + R"(
|
||||||
|
%100 = OpFunction %void None %voidfn
|
||||||
|
%entry = OpLabel
|
||||||
|
|
||||||
|
; the scalar element
|
||||||
|
%1 = OpAccessChain %ptr_uint %myvar %uint_0
|
||||||
|
OpStore %1 %uint_0
|
||||||
|
|
||||||
|
; element in the runtime array
|
||||||
|
%2 = OpAccessChain %ptr_uint %myvar %uint_1 %uint_1
|
||||||
|
OpStore %2 %uint_0
|
||||||
|
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
auto* p = parser(test::Assemble(assembly));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModule()) << assembly << p->error();
|
||||||
|
FunctionEmitter fe(p, *spirv_function(100));
|
||||||
|
EXPECT_TRUE(fe.EmitBody()) << p->error();
|
||||||
|
EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(Assignment{
|
||||||
|
MemberAccessor{
|
||||||
|
Identifier{myvar}
|
||||||
|
Identifier{field0}
|
||||||
|
}
|
||||||
|
ScalarConstructor{0}
|
||||||
|
}
|
||||||
|
Assignment{
|
||||||
|
ArrayAccessor{
|
||||||
|
MemberAccessor{
|
||||||
|
Identifier{myvar}
|
||||||
|
Identifier{field1}
|
||||||
|
}
|
||||||
|
ScalarConstructor{1}
|
||||||
|
}
|
||||||
|
ScalarConstructor{0}
|
||||||
|
})")) << ToString(fe.ast_body())
|
||||||
|
<< p->error();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest, RemapStorageBuffer_ThroughAccessChain_Cascaded) {
|
||||||
|
const auto assembly = OldStorageBufferPreamble() + R"(
|
||||||
|
%ptr_rtarr = OpTypePointer Uniform %arr
|
||||||
|
%100 = OpFunction %void None %voidfn
|
||||||
|
%entry = OpLabel
|
||||||
|
|
||||||
|
; get the runtime array
|
||||||
|
%1 = OpAccessChain %ptr_rtarr %myvar %uint_1
|
||||||
|
; now an element in it
|
||||||
|
%2 = OpAccessChain %ptr_uint %1 %uint_1
|
||||||
|
OpStore %2 %uint_0
|
||||||
|
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
auto* p = parser(test::Assemble(assembly));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModule()) << assembly << p->error();
|
||||||
|
FunctionEmitter fe(p, *spirv_function(100));
|
||||||
|
EXPECT_TRUE(fe.EmitBody()) << p->error();
|
||||||
|
EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(Assignment{
|
||||||
|
ArrayAccessor{
|
||||||
|
MemberAccessor{
|
||||||
|
Identifier{myvar}
|
||||||
|
Identifier{field1}
|
||||||
|
}
|
||||||
|
ScalarConstructor{1}
|
||||||
|
}
|
||||||
|
ScalarConstructor{0}
|
||||||
|
})")) << ToString(fe.ast_body())
|
||||||
|
<< p->error();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest,
|
||||||
|
RemapStorageBuffer_ThroughCopyObject_WithoutHoisting) {
|
||||||
|
// Generates a const declaration directly.
|
||||||
|
// We have to do a bunch of storage class tracking for locally
|
||||||
|
// defined values in order to get the right pointer-to-storage-buffer
|
||||||
|
// value type for the const declration.
|
||||||
|
const auto assembly = OldStorageBufferPreamble() + R"(
|
||||||
|
%100 = OpFunction %void None %voidfn
|
||||||
|
%entry = OpLabel
|
||||||
|
|
||||||
|
%1 = OpAccessChain %ptr_uint %myvar %uint_1 %uint_1
|
||||||
|
%2 = OpCopyObject %ptr_uint %1
|
||||||
|
OpStore %2 %uint_0
|
||||||
|
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
auto* p = parser(test::Assemble(assembly));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModule()) << assembly << p->error();
|
||||||
|
FunctionEmitter fe(p, *spirv_function(100));
|
||||||
|
EXPECT_TRUE(fe.EmitBody()) << p->error();
|
||||||
|
EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"(VariableDeclStatement{
|
||||||
|
Variable{
|
||||||
|
x_2
|
||||||
|
none
|
||||||
|
__ptr_storage_buffer__u32
|
||||||
|
{
|
||||||
|
ArrayAccessor{
|
||||||
|
MemberAccessor{
|
||||||
|
Identifier{myvar}
|
||||||
|
Identifier{field1}
|
||||||
|
}
|
||||||
|
ScalarConstructor{1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assignment{
|
||||||
|
Identifier{x_2}
|
||||||
|
ScalarConstructor{0}
|
||||||
|
})")) << ToString(fe.ast_body())
|
||||||
|
<< p->error();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest, RemapStorageBuffer_ThroughCopyObject_WithHoisting) {
|
||||||
|
// Like the previous test, but the declaration for the copy-object
|
||||||
|
// has its declaration hoisted.
|
||||||
|
const auto assembly = OldStorageBufferPreamble() + R"(
|
||||||
|
%bool = OpTypeBool
|
||||||
|
%cond = OpConstantTrue %bool
|
||||||
|
|
||||||
|
%100 = OpFunction %void None %voidfn
|
||||||
|
|
||||||
|
%entry = OpLabel
|
||||||
|
OpSelectionMerge %99 None
|
||||||
|
OpBranchConditional %cond %20 %30
|
||||||
|
|
||||||
|
%20 = OpLabel
|
||||||
|
%1 = OpAccessChain %ptr_uint %myvar %uint_1 %uint_1
|
||||||
|
; this definintion dominates the use in %99
|
||||||
|
%2 = OpCopyObject %ptr_uint %1
|
||||||
|
OpBranch %99
|
||||||
|
|
||||||
|
%30 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
|
||||||
|
%99 = OpLabel
|
||||||
|
OpStore %2 %uint_0
|
||||||
|
OpReturn
|
||||||
|
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
auto* p = parser(test::Assemble(assembly));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModule()) << assembly << p->error();
|
||||||
|
FunctionEmitter fe(p, *spirv_function(100));
|
||||||
|
EXPECT_TRUE(fe.EmitBody()) << p->error();
|
||||||
|
EXPECT_THAT(ToString(fe.ast_body()), Eq(R"(VariableDeclStatement{
|
||||||
|
Variable{
|
||||||
|
x_2
|
||||||
|
function
|
||||||
|
__ptr_storage_buffer__u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
If{
|
||||||
|
(
|
||||||
|
ScalarConstructor{true}
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Assignment{
|
||||||
|
Identifier{x_2}
|
||||||
|
ArrayAccessor{
|
||||||
|
MemberAccessor{
|
||||||
|
Identifier{myvar}
|
||||||
|
Identifier{field1}
|
||||||
|
}
|
||||||
|
ScalarConstructor{1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Else{
|
||||||
|
{
|
||||||
|
Return{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assignment{
|
||||||
|
Identifier{x_2}
|
||||||
|
ScalarConstructor{0}
|
||||||
|
}
|
||||||
|
Return{}
|
||||||
|
)")) << ToString(fe.ast_body())
|
||||||
|
<< p->error();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest, DISABLED_RemapStorageBuffer_ThroughFunctionCall) {
|
||||||
|
// TODO(dneto): Blocked on OpFunctionCall support.
|
||||||
|
// We might need this for passing pointers into atomic builtins.
|
||||||
|
}
|
||||||
|
TEST_F(SpvParserTest, DISABLED_RemapStorageBuffer_ThroughFunctionParameter) {
|
||||||
|
// TODO(dneto): Blocked on OpFunctionCall support.
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace spirv
|
} // namespace spirv
|
||||||
} // namespace reader
|
} // namespace reader
|
||||||
|
|
|
@ -65,6 +65,7 @@
|
||||||
#include "src/ast/variable.h"
|
#include "src/ast/variable.h"
|
||||||
#include "src/ast/variable_decl_statement.h"
|
#include "src/ast/variable_decl_statement.h"
|
||||||
#include "src/ast/variable_decoration.h"
|
#include "src/ast/variable_decoration.h"
|
||||||
|
#include "src/reader/spirv/enum_converter.h"
|
||||||
#include "src/reader/spirv/function.h"
|
#include "src/reader/spirv/function.h"
|
||||||
#include "src/type_manager.h"
|
#include "src/type_manager.h"
|
||||||
|
|
||||||
|
@ -612,7 +613,8 @@ ast::type::Type* ParserImpl::ConvertType(
|
||||||
|
|
||||||
ast::type::Type* ParserImpl::ConvertType(
|
ast::type::Type* ParserImpl::ConvertType(
|
||||||
const spvtools::opt::analysis::Array* arr_ty) {
|
const spvtools::opt::analysis::Array* arr_ty) {
|
||||||
auto* ast_elem_ty = ConvertType(type_mgr_->GetId(arr_ty->element_type()));
|
const auto elem_type_id = type_mgr_->GetId(arr_ty->element_type());
|
||||||
|
auto* ast_elem_ty = ConvertType(elem_type_id);
|
||||||
if (ast_elem_ty == nullptr) {
|
if (ast_elem_ty == nullptr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -648,6 +650,9 @@ ast::type::Type* ParserImpl::ConvertType(
|
||||||
if (!ApplyArrayDecorations(arr_ty, ast_type.get())) {
|
if (!ApplyArrayDecorations(arr_ty, ast_type.get())) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
if (remap_buffer_block_type_.count(elem_type_id)) {
|
||||||
|
remap_buffer_block_type_.insert(type_mgr_->GetId(arr_ty));
|
||||||
|
}
|
||||||
return ctx_.type_mgr().Get(std::move(ast_type));
|
return ctx_.type_mgr().Get(std::move(ast_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -684,9 +689,17 @@ ast::type::Type* ParserImpl::ConvertType(
|
||||||
// Compute the struct decoration.
|
// Compute the struct decoration.
|
||||||
auto struct_decorations = this->GetDecorationsFor(type_id);
|
auto struct_decorations = this->GetDecorationsFor(type_id);
|
||||||
auto ast_struct_decoration = ast::StructDecoration::kNone;
|
auto ast_struct_decoration = ast::StructDecoration::kNone;
|
||||||
if (struct_decorations.size() == 1 &&
|
if (struct_decorations.size() == 1) {
|
||||||
struct_decorations[0][0] == SpvDecorationBlock) {
|
const auto decoration = struct_decorations[0][0];
|
||||||
|
if (decoration == SpvDecorationBlock) {
|
||||||
ast_struct_decoration = ast::StructDecoration::kBlock;
|
ast_struct_decoration = ast::StructDecoration::kBlock;
|
||||||
|
} else if (decoration == SpvDecorationBufferBlock) {
|
||||||
|
ast_struct_decoration = ast::StructDecoration::kBlock;
|
||||||
|
remap_buffer_block_type_.insert(type_id);
|
||||||
|
} else {
|
||||||
|
Fail() << "struct with ID " << type_id
|
||||||
|
<< " has unrecognized decoration: " << int(decoration);
|
||||||
|
}
|
||||||
} else if (struct_decorations.size() > 1) {
|
} else if (struct_decorations.size() > 1) {
|
||||||
Fail() << "can't handle a struct with more than one decoration: struct "
|
Fail() << "can't handle a struct with more than one decoration: struct "
|
||||||
<< type_id << " has " << struct_decorations.size();
|
<< type_id << " has " << struct_decorations.size();
|
||||||
|
@ -751,26 +764,28 @@ ast::type::Type* ParserImpl::ConvertType(
|
||||||
// Set the struct name before registering it.
|
// Set the struct name before registering it.
|
||||||
namer_.SuggestSanitizedName(type_id, "S");
|
namer_.SuggestSanitizedName(type_id, "S");
|
||||||
ast_struct_type->set_name(namer_.GetName(type_id));
|
ast_struct_type->set_name(namer_.GetName(type_id));
|
||||||
return ctx_.type_mgr().Get(std::move(ast_struct_type));
|
auto* result = ctx_.type_mgr().Get(std::move(ast_struct_type));
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::type::Type* ParserImpl::ConvertType(
|
ast::type::Type* ParserImpl::ConvertType(
|
||||||
uint32_t type_id,
|
uint32_t type_id,
|
||||||
const spvtools::opt::analysis::Pointer*) {
|
const spvtools::opt::analysis::Pointer*) {
|
||||||
const auto* inst = def_use_mgr_->GetDef(type_id);
|
const auto* inst = def_use_mgr_->GetDef(type_id);
|
||||||
const auto pointee_ty_id = inst->GetSingleWordInOperand(1);
|
const auto pointee_type_id = inst->GetSingleWordInOperand(1);
|
||||||
const auto storage_class = SpvStorageClass(inst->GetSingleWordInOperand(0));
|
const auto storage_class = SpvStorageClass(inst->GetSingleWordInOperand(0));
|
||||||
if (pointee_ty_id == builtin_position_.struct_type_id) {
|
if (pointee_type_id == builtin_position_.struct_type_id) {
|
||||||
builtin_position_.pointer_type_id = type_id;
|
builtin_position_.pointer_type_id = type_id;
|
||||||
builtin_position_.storage_class = storage_class;
|
builtin_position_.storage_class = storage_class;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto* ast_elem_ty = ConvertType(pointee_ty_id);
|
auto* ast_elem_ty = ConvertType(pointee_type_id);
|
||||||
if (ast_elem_ty == nullptr) {
|
if (ast_elem_ty == nullptr) {
|
||||||
Fail() << "SPIR-V pointer type with ID " << type_id
|
Fail() << "SPIR-V pointer type with ID " << type_id
|
||||||
<< " has invalid pointee type " << pointee_ty_id;
|
<< " has invalid pointee type " << pointee_type_id;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ast_storage_class = enum_converter_.ToStorageClass(storage_class);
|
auto ast_storage_class = enum_converter_.ToStorageClass(storage_class);
|
||||||
if (ast_storage_class == ast::StorageClass::kNone) {
|
if (ast_storage_class == ast::StorageClass::kNone) {
|
||||||
Fail() << "SPIR-V pointer type with ID " << type_id
|
Fail() << "SPIR-V pointer type with ID " << type_id
|
||||||
|
@ -778,6 +793,11 @@ ast::type::Type* ParserImpl::ConvertType(
|
||||||
<< static_cast<uint32_t>(storage_class);
|
<< static_cast<uint32_t>(storage_class);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
if (ast_storage_class == ast::StorageClass::kUniform &&
|
||||||
|
remap_buffer_block_type_.count(pointee_type_id)) {
|
||||||
|
ast_storage_class = ast::StorageClass::kStorageBuffer;
|
||||||
|
remap_buffer_block_type_.insert(type_id);
|
||||||
|
}
|
||||||
return ctx_.type_mgr().Get(
|
return ctx_.type_mgr().Get(
|
||||||
std::make_unique<ast::type::PointerType>(ast_elem_ty, ast_storage_class));
|
std::make_unique<ast::type::PointerType>(ast_elem_ty, ast_storage_class));
|
||||||
}
|
}
|
||||||
|
@ -854,7 +874,8 @@ bool ParserImpl::EmitModuleScopeVariables() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto& var = type_or_value;
|
const auto& var = type_or_value;
|
||||||
const auto spirv_storage_class = var.GetSingleWordInOperand(0);
|
const auto spirv_storage_class =
|
||||||
|
SpvStorageClass(var.GetSingleWordInOperand(0));
|
||||||
|
|
||||||
uint32_t type_id = var.type_id();
|
uint32_t type_id = var.type_id();
|
||||||
if ((type_id == builtin_position_.pointer_type_id) &&
|
if ((type_id == builtin_position_.pointer_type_id) &&
|
||||||
|
@ -864,9 +885,21 @@ bool ParserImpl::EmitModuleScopeVariables() {
|
||||||
builtin_position_.per_vertex_var_id = var.result_id();
|
builtin_position_.per_vertex_var_id = var.result_id();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
switch (enum_converter_.ToStorageClass(spirv_storage_class)) {
|
||||||
auto ast_storage_class = enum_converter_.ToStorageClass(
|
case ast::StorageClass::kInput:
|
||||||
static_cast<SpvStorageClass>(spirv_storage_class));
|
case ast::StorageClass::kOutput:
|
||||||
|
case ast::StorageClass::kUniform:
|
||||||
|
case ast::StorageClass::kUniformConstant:
|
||||||
|
case ast::StorageClass::kStorageBuffer:
|
||||||
|
case ast::StorageClass::kImage:
|
||||||
|
case ast::StorageClass::kWorkgroup:
|
||||||
|
case ast::StorageClass::kPrivate:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return Fail() << "invalid SPIR-V storage class "
|
||||||
|
<< int(spirv_storage_class)
|
||||||
|
<< " for module scope variable: " << var.PrettyPrint();
|
||||||
|
}
|
||||||
if (!success_) {
|
if (!success_) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -881,6 +914,7 @@ bool ParserImpl::EmitModuleScopeVariables() {
|
||||||
<< " has non-pointer type " << var.type_id();
|
<< " has non-pointer type " << var.type_id();
|
||||||
}
|
}
|
||||||
auto* ast_store_type = ast_type->AsPointer()->type();
|
auto* ast_store_type = ast_type->AsPointer()->type();
|
||||||
|
auto ast_storage_class = ast_type->AsPointer()->storage_class();
|
||||||
auto ast_var =
|
auto ast_var =
|
||||||
MakeVariable(var.result_id(), ast_storage_class, ast_store_type);
|
MakeVariable(var.result_id(), ast_storage_class, ast_store_type);
|
||||||
if (var.NumInOperands() > 1) {
|
if (var.NumInOperands() > 1) {
|
||||||
|
|
|
@ -89,6 +89,11 @@ class ParserImpl : Reader {
|
||||||
/// @returns true if the parse was successful, false otherwise.
|
/// @returns true if the parse was successful, false otherwise.
|
||||||
bool Parse() override;
|
bool Parse() override;
|
||||||
|
|
||||||
|
/// @returns the Tint context.
|
||||||
|
Context& context() {
|
||||||
|
return ctx_; // Inherited from Reader
|
||||||
|
}
|
||||||
|
|
||||||
/// @returns the module. The module in the parser will be reset after this.
|
/// @returns the module. The module in the parser will be reset after this.
|
||||||
ast::Module module() override;
|
ast::Module module() override;
|
||||||
|
|
||||||
|
@ -439,6 +444,16 @@ class ParserImpl : Reader {
|
||||||
// [[position]] var<in> gl_Position : vec4<f32>;
|
// [[position]] var<in> gl_Position : vec4<f32>;
|
||||||
// The builtin variable was detected if and only if the struct_id is non-zero.
|
// The builtin variable was detected if and only if the struct_id is non-zero.
|
||||||
BuiltInPositionInfo builtin_position_;
|
BuiltInPositionInfo builtin_position_;
|
||||||
|
|
||||||
|
// SPIR-V type IDs that are either:
|
||||||
|
// - a struct type decorated by BufferBlock
|
||||||
|
// - an array, runtime array containing one of these
|
||||||
|
// - a pointer type to one of these
|
||||||
|
// These are the types "enclosing" a buffer block with the old style
|
||||||
|
// representation: using Uniform storage class and BufferBlock decoration
|
||||||
|
// on the struct. The new style is to use the StorageBuffer storage class
|
||||||
|
// and Block decoration.
|
||||||
|
std::unordered_set<uint32_t> remap_buffer_block_type_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace spirv
|
} // namespace spirv
|
||||||
|
|
|
@ -70,7 +70,7 @@ TEST_F(SpvParserTest, ModuleScopeVar_NoVar) {
|
||||||
EXPECT_THAT(module_ast, Not(HasSubstr("Variable")));
|
EXPECT_THAT(module_ast, Not(HasSubstr("Variable")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SpvParserTest, ModuleScopeVar_BadStorageClass) {
|
TEST_F(SpvParserTest, ModuleScopeVar_BadStorageClass_NotAWebGPUStorageClass) {
|
||||||
auto* p = parser(test::Assemble(R"(
|
auto* p = parser(test::Assemble(R"(
|
||||||
%float = OpTypeFloat 32
|
%float = OpTypeFloat 32
|
||||||
%ptr = OpTypePointer CrossWorkgroup %float
|
%ptr = OpTypePointer CrossWorkgroup %float
|
||||||
|
@ -84,6 +84,22 @@ TEST_F(SpvParserTest, ModuleScopeVar_BadStorageClass) {
|
||||||
EXPECT_THAT(p->error(), HasSubstr("unknown SPIR-V storage class: 5"));
|
EXPECT_THAT(p->error(), HasSubstr("unknown SPIR-V storage class: 5"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest, ModuleScopeVar_BadStorageClass_Function) {
|
||||||
|
auto* p = parser(test::Assemble(R"(
|
||||||
|
%float = OpTypeFloat 32
|
||||||
|
%ptr = OpTypePointer Function %float
|
||||||
|
%52 = OpVariable %ptr Function
|
||||||
|
)"));
|
||||||
|
EXPECT_TRUE(p->BuildInternalModule());
|
||||||
|
// Normally we should run ParserImpl::RegisterTypes before emitting
|
||||||
|
// variables. But defensive coding in EmitModuleScopeVariables lets
|
||||||
|
// us catch this error.
|
||||||
|
EXPECT_FALSE(p->EmitModuleScopeVariables()) << p->error();
|
||||||
|
EXPECT_THAT(p->error(),
|
||||||
|
HasSubstr("invalid SPIR-V storage class 7 for module scope "
|
||||||
|
"variable: %52 = OpVariable %2 Function"));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(SpvParserTest, ModuleScopeVar_BadPointerType) {
|
TEST_F(SpvParserTest, ModuleScopeVar_BadPointerType) {
|
||||||
auto* p = parser(test::Assemble(R"(
|
auto* p = parser(test::Assemble(R"(
|
||||||
%float = OpTypeFloat 32
|
%float = OpTypeFloat 32
|
||||||
|
|
Loading…
Reference in New Issue