spirv-reader: support OpCompositeInsert
This generates intermediate variable to stuff the component into, then a constant definition to evaluate the result for later use. Bug: tint:3 Change-Id: If2e6bb24e2b1e621c3602509eb3237c40f53897b Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/41360 Auto-Submit: David Neto <dneto@google.com> Commit-Queue: dan sinclair <dsinclair@chromium.org> Reviewed-by: dan sinclair <dsinclair@chromium.org>
This commit is contained in:
parent
c0f30195a0
commit
0c7f97626f
|
@ -3135,6 +3135,10 @@ bool FunctionEmitter::EmitStatement(const spvtools::opt::Instruction& inst) {
|
||||||
// Synthesize a vector insertion in its own statements.
|
// Synthesize a vector insertion in its own statements.
|
||||||
return MakeVectorInsertDynamic(inst);
|
return MakeVectorInsertDynamic(inst);
|
||||||
|
|
||||||
|
case SpvOpCompositeInsert:
|
||||||
|
// Synthesize a composite insertion in its own statements.
|
||||||
|
return MakeCompositeInsert(inst);
|
||||||
|
|
||||||
case SpvOpFunctionCall:
|
case SpvOpFunctionCall:
|
||||||
return EmitFunctionCall(inst);
|
return EmitFunctionCall(inst);
|
||||||
|
|
||||||
|
@ -3307,7 +3311,6 @@ TypedExpression FunctionEmitter::MaybeEmitCombinatorialValue(
|
||||||
// OpGenericCastToPtrExplicit // Not in Vulkan
|
// OpGenericCastToPtrExplicit // Not in Vulkan
|
||||||
//
|
//
|
||||||
// OpArrayLength
|
// OpArrayLength
|
||||||
// OpCompositeInsert
|
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -3577,35 +3580,68 @@ TypedExpression FunctionEmitter::MakeCompositeExtract(
|
||||||
// This is structurally similar to creating an access chain, but
|
// This is structurally similar to creating an access chain, but
|
||||||
// the SPIR-V instruction has literal indices instead of IDs for indices.
|
// the SPIR-V instruction has literal indices instead of IDs for indices.
|
||||||
|
|
||||||
// A SPIR-V composite extract is a single instruction with multiple
|
auto composite_index = 0;
|
||||||
// literal indices walking down into composites. The Tint AST represents
|
auto first_index_position = 1;
|
||||||
// this as ever-deeper nested indexing expressions. Start off with an
|
TypedExpression current_expr(MakeOperand(inst, composite_index));
|
||||||
// expression for the composite, and then bury that inside nested indexing
|
const auto composite_id = inst.GetSingleWordInOperand(composite_index);
|
||||||
// expressions.
|
auto current_type_id = def_use_mgr_->GetDef(composite_id)->type_id();
|
||||||
auto source = GetSourceForInst(inst);
|
|
||||||
TypedExpression current_expr(MakeOperand(inst, 0));
|
|
||||||
|
|
||||||
auto make_index = [this, source](uint32_t literal) {
|
return MakeCompositeValueDecomposition(inst, current_expr, current_type_id,
|
||||||
|
first_index_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
TypedExpression FunctionEmitter::MakeCompositeValueDecomposition(
|
||||||
|
const spvtools::opt::Instruction& inst,
|
||||||
|
TypedExpression composite,
|
||||||
|
uint32_t composite_type_id,
|
||||||
|
int index_start) {
|
||||||
|
// This is structurally similar to creating an access chain, but
|
||||||
|
// the SPIR-V instruction has literal indices instead of IDs for indices.
|
||||||
|
|
||||||
|
// A SPIR-V composite extract is a single instruction with multiple
|
||||||
|
// literal indices walking down into composites.
|
||||||
|
// A SPIR-V composite insert is similar but also tells you what component
|
||||||
|
// to inject. This function is respnosible for the the walking-into part
|
||||||
|
// of composite-insert.
|
||||||
|
//
|
||||||
|
// The Tint AST represents this as ever-deeper nested indexing expressions.
|
||||||
|
// Start off with an expression for the composite, and then bury that inside
|
||||||
|
// nested indexing expressions.
|
||||||
|
|
||||||
|
auto current_expr = composite;
|
||||||
|
auto current_type_id = composite_type_id;
|
||||||
|
|
||||||
|
auto make_index = [this](uint32_t literal) {
|
||||||
return create<ast::ScalarConstructorExpression>(
|
return create<ast::ScalarConstructorExpression>(
|
||||||
source, create<ast::UintLiteral>(source, u32_, literal));
|
Source{}, create<ast::UintLiteral>(Source{}, u32_, literal));
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto composite = inst.GetSingleWordInOperand(0);
|
// Build up a nested expression for the decomposition by walking down the type
|
||||||
auto current_type_id = def_use_mgr_->GetDef(composite)->type_id();
|
|
||||||
// Build up a nested expression for the access chain by walking down the type
|
|
||||||
// hierarchy, maintaining |current_type_id| as the SPIR-V ID of the type of
|
// hierarchy, maintaining |current_type_id| as the SPIR-V ID of the type of
|
||||||
// the object pointed to after processing the previous indices.
|
// the object pointed to after processing the previous indices.
|
||||||
const auto num_in_operands = inst.NumInOperands();
|
const auto num_in_operands = inst.NumInOperands();
|
||||||
for (uint32_t index = 1; index < num_in_operands; ++index) {
|
for (uint32_t index = index_start; index < num_in_operands; ++index) {
|
||||||
const uint32_t index_val = inst.GetSingleWordInOperand(index);
|
const uint32_t index_val = inst.GetSingleWordInOperand(index);
|
||||||
|
|
||||||
const auto* current_type_inst = def_use_mgr_->GetDef(current_type_id);
|
const auto* current_type_inst = def_use_mgr_->GetDef(current_type_id);
|
||||||
if (!current_type_inst) {
|
if (!current_type_inst) {
|
||||||
Fail() << "composite type %" << current_type_id
|
Fail() << "composite type %" << current_type_id
|
||||||
<< " is invalid after following " << (index - 1)
|
<< " is invalid after following " << (index - index_start)
|
||||||
<< " indices: " << inst.PrettyPrint();
|
<< " indices: " << inst.PrettyPrint();
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
const char* operation_name = nullptr;
|
||||||
|
switch (inst.opcode()) {
|
||||||
|
case SpvOpCompositeExtract:
|
||||||
|
operation_name = "OpCompositeExtract";
|
||||||
|
break;
|
||||||
|
case SpvOpCompositeInsert:
|
||||||
|
operation_name = "OpCompositeInsert";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Fail() << "internal error: unhandled " << inst.PrettyPrint();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
ast::Expression* next_expr = nullptr;
|
ast::Expression* next_expr = nullptr;
|
||||||
switch (current_type_inst->opcode()) {
|
switch (current_type_inst->opcode()) {
|
||||||
case SpvOpTypeVector: {
|
case SpvOpTypeVector: {
|
||||||
|
@ -3613,8 +3649,9 @@ TypedExpression FunctionEmitter::MakeCompositeExtract(
|
||||||
// like "foo.z", which is more idiomatic than "foo[2]".
|
// like "foo.z", which is more idiomatic than "foo[2]".
|
||||||
const auto num_elems = current_type_inst->GetSingleWordInOperand(1);
|
const auto num_elems = current_type_inst->GetSingleWordInOperand(1);
|
||||||
if (num_elems <= index_val) {
|
if (num_elems <= index_val) {
|
||||||
Fail() << "CompositeExtract %" << inst.result_id() << " index value "
|
Fail() << operation_name << " %" << inst.result_id()
|
||||||
<< index_val << " is out of bounds for vector of " << num_elems
|
<< " index value " << index_val
|
||||||
|
<< " is out of bounds for vector of " << num_elems
|
||||||
<< " elements";
|
<< " elements";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -3632,8 +3669,9 @@ TypedExpression FunctionEmitter::MakeCompositeExtract(
|
||||||
// Check bounds
|
// Check bounds
|
||||||
const auto num_elems = current_type_inst->GetSingleWordInOperand(1);
|
const auto num_elems = current_type_inst->GetSingleWordInOperand(1);
|
||||||
if (num_elems <= index_val) {
|
if (num_elems <= index_val) {
|
||||||
Fail() << "CompositeExtract %" << inst.result_id() << " index value "
|
Fail() << operation_name << " %" << inst.result_id()
|
||||||
<< index_val << " is out of bounds for matrix of " << num_elems
|
<< " index value " << index_val
|
||||||
|
<< " is out of bounds for matrix of " << num_elems
|
||||||
<< " elements";
|
<< " elements";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -3657,14 +3695,16 @@ TypedExpression FunctionEmitter::MakeCompositeExtract(
|
||||||
current_type_id = current_type_inst->GetSingleWordInOperand(0);
|
current_type_id = current_type_inst->GetSingleWordInOperand(0);
|
||||||
break;
|
break;
|
||||||
case SpvOpTypeRuntimeArray:
|
case SpvOpTypeRuntimeArray:
|
||||||
Fail() << "can't do OpCompositeExtract on a runtime array";
|
Fail() << "can't do " << operation_name
|
||||||
|
<< " on a runtime array: " << inst.PrettyPrint();
|
||||||
return {};
|
return {};
|
||||||
case SpvOpTypeStruct: {
|
case SpvOpTypeStruct: {
|
||||||
const auto num_members = current_type_inst->NumInOperands();
|
const auto num_members = current_type_inst->NumInOperands();
|
||||||
if (num_members <= index_val) {
|
if (num_members <= index_val) {
|
||||||
Fail() << "CompositeExtract %" << inst.result_id() << " index value "
|
Fail() << operation_name << " %" << inst.result_id()
|
||||||
<< index_val << " is out of bounds for structure %"
|
<< " index value " << index_val
|
||||||
<< current_type_id << " having " << num_members << " members";
|
<< " is out of bounds for structure %" << current_type_id
|
||||||
|
<< " having " << num_members << " members";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
auto name = namer_.GetMemberName(current_type_id, uint32_t(index_val));
|
auto name = namer_.GetMemberName(current_type_id, uint32_t(index_val));
|
||||||
|
@ -3677,8 +3717,8 @@ TypedExpression FunctionEmitter::MakeCompositeExtract(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
Fail() << "CompositeExtract with bad type %" << current_type_id << ": "
|
Fail() << operation_name << " with bad type %" << current_type_id
|
||||||
<< current_type_inst->PrettyPrint();
|
<< ": " << current_type_inst->PrettyPrint();
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
current_expr =
|
current_expr =
|
||||||
|
@ -4909,6 +4949,59 @@ bool FunctionEmitter::MakeVectorInsertDynamic(
|
||||||
{ast_type, create<ast::IdentifierExpression>(registered_temp_name)});
|
{ast_type, create<ast::IdentifierExpression>(registered_temp_name)});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FunctionEmitter::MakeCompositeInsert(
|
||||||
|
const spvtools::opt::Instruction& inst) {
|
||||||
|
// For
|
||||||
|
// %result = OpCompositeInsert %type %object %composite 1 2 3 ...
|
||||||
|
// generate statements like this:
|
||||||
|
//
|
||||||
|
// var temp : type = composite;
|
||||||
|
// temp[index].x = object;
|
||||||
|
// const result : type = temp;
|
||||||
|
//
|
||||||
|
// Then use result everywhere the original SPIR-V id is used. Using a const
|
||||||
|
// like this avoids constantly reloading the value many times.
|
||||||
|
//
|
||||||
|
// This technique is a combination of:
|
||||||
|
// - making a temporary variable and constant declaration, like what we do
|
||||||
|
// for VectorInsertDynamic, and
|
||||||
|
// - building up an access-chain like access like for CompositeExtract, but
|
||||||
|
// on the left-hand side of the assignment.
|
||||||
|
|
||||||
|
auto* ast_type = parser_impl_.ConvertType(inst.type_id());
|
||||||
|
auto component = MakeOperand(inst, 0);
|
||||||
|
auto src_composite = MakeOperand(inst, 1);
|
||||||
|
|
||||||
|
// Synthesize the temporary variable.
|
||||||
|
// It doesn't correspond to a SPIR-V ID, so we don't use the ordinary
|
||||||
|
// API in parser_impl_.
|
||||||
|
auto result_name = namer_.Name(inst.result_id());
|
||||||
|
auto temp_name = namer_.MakeDerivedName(result_name);
|
||||||
|
auto registered_temp_name = builder_.Symbols().Register(temp_name);
|
||||||
|
|
||||||
|
auto* temp_var = create<ast::Variable>(
|
||||||
|
Source{}, registered_temp_name, ast::StorageClass::kFunction, ast_type,
|
||||||
|
false, src_composite.expr, ast::VariableDecorationList{});
|
||||||
|
AddStatement(create<ast::VariableDeclStatement>(Source{}, temp_var));
|
||||||
|
|
||||||
|
TypedExpression seed_expr{ast_type, create<ast::IdentifierExpression>(
|
||||||
|
Source{}, registered_temp_name)};
|
||||||
|
|
||||||
|
// The left-hand side of the assignment *looks* like a decomposition.
|
||||||
|
TypedExpression lhs =
|
||||||
|
MakeCompositeValueDecomposition(inst, seed_expr, inst.type_id(), 2);
|
||||||
|
if (!lhs.expr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AddStatement(
|
||||||
|
create<ast::AssignmentStatement>(Source{}, lhs.expr, component.expr));
|
||||||
|
|
||||||
|
return EmitConstDefinition(
|
||||||
|
inst,
|
||||||
|
{ast_type, create<ast::IdentifierExpression>(registered_temp_name)});
|
||||||
|
}
|
||||||
|
|
||||||
FunctionEmitter::FunctionDeclaration::FunctionDeclaration() = default;
|
FunctionEmitter::FunctionDeclaration::FunctionDeclaration() = default;
|
||||||
FunctionEmitter::FunctionDeclaration::~FunctionDeclaration() = default;
|
FunctionEmitter::FunctionDeclaration::~FunctionDeclaration() = default;
|
||||||
|
|
||||||
|
|
|
@ -729,6 +729,21 @@ class FunctionEmitter {
|
||||||
/// @returns an AST expression for the instruction, or nullptr.
|
/// @returns an AST expression for the instruction, or nullptr.
|
||||||
TypedExpression MakeCompositeExtract(const spvtools::opt::Instruction& inst);
|
TypedExpression MakeCompositeExtract(const spvtools::opt::Instruction& inst);
|
||||||
|
|
||||||
|
/// Creates an expression for indexing into a composite value. The literal
|
||||||
|
/// indices that step into the value start at instruction input operand
|
||||||
|
/// `start_index` and run to the end of the instruction.
|
||||||
|
/// @param inst the original instruction
|
||||||
|
/// @param composite the typed expression for the composite
|
||||||
|
/// @param composite_type_id the SPIR-V type ID for the composite
|
||||||
|
/// @param index_start the index of the first operand in `inst` that is an
|
||||||
|
/// index into the composite type
|
||||||
|
/// @returns an AST expression for the decomposed composite, or {} on error
|
||||||
|
TypedExpression MakeCompositeValueDecomposition(
|
||||||
|
const spvtools::opt::Instruction& inst,
|
||||||
|
TypedExpression composite,
|
||||||
|
uint32_t composite_type_id,
|
||||||
|
int index_start);
|
||||||
|
|
||||||
/// Creates an expression for OpVectorShuffle
|
/// Creates an expression for OpVectorShuffle
|
||||||
/// @param inst an OpVectorShuffle instruction.
|
/// @param inst an OpVectorShuffle instruction.
|
||||||
/// @returns an AST expression for the instruction, or nullptr.
|
/// @returns an AST expression for the instruction, or nullptr.
|
||||||
|
@ -911,6 +926,12 @@ class FunctionEmitter {
|
||||||
/// @returns an expression
|
/// @returns an expression
|
||||||
bool MakeVectorInsertDynamic(const spvtools::opt::Instruction& inst);
|
bool MakeVectorInsertDynamic(const spvtools::opt::Instruction& inst);
|
||||||
|
|
||||||
|
/// Generates statements for a SPIR-V OpComposite instruction.
|
||||||
|
/// Registers a const declaration for the result.
|
||||||
|
/// @param inst the SPIR-V instruction
|
||||||
|
/// @returns an expression
|
||||||
|
bool MakeCompositeInsert(const spvtools::opt::Instruction& inst);
|
||||||
|
|
||||||
/// Get the SPIR-V instruction for the image memory object declaration for
|
/// Get the SPIR-V instruction for the image memory object declaration for
|
||||||
/// the image operand to the given instruction.
|
/// the image operand to the given instruction.
|
||||||
/// @param inst the SPIR-V instruction
|
/// @param inst the SPIR-V instruction
|
||||||
|
|
|
@ -287,7 +287,7 @@ TEST_F(SpvParserTest_CompositeExtract, Vector_IndexTooBigError) {
|
||||||
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
|
||||||
FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
|
FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
|
||||||
EXPECT_FALSE(fe.EmitBody());
|
EXPECT_FALSE(fe.EmitBody());
|
||||||
EXPECT_THAT(p->error(), Eq("CompositeExtract %1 index value 900 is out of "
|
EXPECT_THAT(p->error(), Eq("OpCompositeExtract %1 index value 900 is out of "
|
||||||
"bounds for vector of 2 elements"));
|
"bounds for vector of 2 elements"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,7 +338,7 @@ TEST_F(SpvParserTest_CompositeExtract, Matrix_IndexTooBigError) {
|
||||||
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
|
||||||
FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
|
FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
|
||||||
EXPECT_FALSE(fe.EmitBody()) << p->error();
|
EXPECT_FALSE(fe.EmitBody()) << p->error();
|
||||||
EXPECT_THAT(p->error(), Eq("CompositeExtract %2 index value 3 is out of "
|
EXPECT_THAT(p->error(), Eq("OpCompositeExtract %2 index value 3 is out of "
|
||||||
"bounds for matrix of 3 elements"));
|
"bounds for matrix of 3 elements"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,7 +424,8 @@ TEST_F(SpvParserTest_CompositeExtract, RuntimeArray_IsError) {
|
||||||
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
|
||||||
FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
|
FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
|
||||||
EXPECT_FALSE(fe.EmitBody()) << p->error();
|
EXPECT_FALSE(fe.EmitBody()) << p->error();
|
||||||
EXPECT_THAT(p->error(), Eq("can't do OpCompositeExtract on a runtime array"));
|
EXPECT_THAT(p->error(),
|
||||||
|
HasSubstr("can't do OpCompositeExtract on a runtime array: "));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SpvParserTest_CompositeExtract, Struct) {
|
TEST_F(SpvParserTest_CompositeExtract, Struct) {
|
||||||
|
@ -530,7 +531,7 @@ TEST_F(SpvParserTest_CompositeExtract, Struct_IndexTooBigError) {
|
||||||
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
|
||||||
FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
|
FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
|
||||||
EXPECT_FALSE(fe.EmitBody());
|
EXPECT_FALSE(fe.EmitBody());
|
||||||
EXPECT_THAT(p->error(), Eq("CompositeExtract %2 index value 40 is out of "
|
EXPECT_THAT(p->error(), Eq("OpCompositeExtract %2 index value 40 is out of "
|
||||||
"bounds for structure %26 having 3 members"));
|
"bounds for structure %26 having 3 members"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -576,6 +577,465 @@ TEST_F(SpvParserTest_CompositeExtract, Struct_Array_Matrix_Vector) {
|
||||||
<< ToString(p->builder(), fe.ast_body());
|
<< ToString(p->builder(), fe.ast_body());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using SpvParserTest_CompositeInsert = SpvParserTest;
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest_CompositeInsert, Vector) {
|
||||||
|
const auto assembly = Preamble() + R"(
|
||||||
|
%100 = OpFunction %void None %voidfn
|
||||||
|
%entry = OpLabel
|
||||||
|
%1 = OpCompositeInsert %v2float %float_70 %v2float_50_60 1
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
auto p = parser(test::Assemble(assembly));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
|
||||||
|
FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
|
||||||
|
EXPECT_TRUE(fe.EmitBody()) << p->error();
|
||||||
|
auto body_str = ToString(p->builder(), fe.ast_body());
|
||||||
|
EXPECT_THAT(body_str, HasSubstr(R"(VariableDeclStatement{
|
||||||
|
Variable{
|
||||||
|
x_1_1
|
||||||
|
function
|
||||||
|
__vec_2__f32
|
||||||
|
{
|
||||||
|
TypeConstructor[not set]{
|
||||||
|
__vec_2__f32
|
||||||
|
ScalarConstructor[not set]{50.000000}
|
||||||
|
ScalarConstructor[not set]{60.000000}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assignment{
|
||||||
|
MemberAccessor[not set]{
|
||||||
|
Identifier[not set]{x_1_1}
|
||||||
|
Identifier[not set]{y}
|
||||||
|
}
|
||||||
|
ScalarConstructor[not set]{70.000000}
|
||||||
|
}
|
||||||
|
VariableDeclStatement{
|
||||||
|
VariableConst{
|
||||||
|
x_1
|
||||||
|
none
|
||||||
|
__vec_2__f32
|
||||||
|
{
|
||||||
|
Identifier[not set]{x_1_1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})")) << body_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest_CompositeInsert, Vector_IndexTooBigError) {
|
||||||
|
const auto assembly = Preamble() + R"(
|
||||||
|
%100 = OpFunction %void None %voidfn
|
||||||
|
%entry = OpLabel
|
||||||
|
%1 = OpCompositeInsert %v2float %float_70 %v2float_50_60 900
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
auto p = parser(test::Assemble(assembly));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
|
||||||
|
FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
|
||||||
|
EXPECT_FALSE(fe.EmitBody());
|
||||||
|
EXPECT_THAT(p->error(), Eq("OpCompositeInsert %1 index value 900 is out of "
|
||||||
|
"bounds for vector of 2 elements"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest_CompositeInsert, Matrix) {
|
||||||
|
const auto assembly = Preamble() + R"(
|
||||||
|
%ptr = OpTypePointer Function %m3v2float
|
||||||
|
|
||||||
|
%100 = OpFunction %void None %voidfn
|
||||||
|
%entry = OpLabel
|
||||||
|
%var = OpVariable %ptr Function
|
||||||
|
%1 = OpLoad %m3v2float %var
|
||||||
|
%2 = OpCompositeInsert %m3v2float %v2float_50_60 %1 2
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
auto p = parser(test::Assemble(assembly));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
|
||||||
|
FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
|
||||||
|
EXPECT_TRUE(fe.EmitBody()) << p->error();
|
||||||
|
auto body_str = ToString(p->builder(), fe.ast_body());
|
||||||
|
EXPECT_THAT(body_str, HasSubstr(R"(VariableDeclStatement{
|
||||||
|
Variable{
|
||||||
|
x_2_1
|
||||||
|
function
|
||||||
|
__mat_2_3__f32
|
||||||
|
{
|
||||||
|
Identifier[not set]{x_1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assignment{
|
||||||
|
ArrayAccessor[not set]{
|
||||||
|
Identifier[not set]{x_2_1}
|
||||||
|
ScalarConstructor[not set]{2}
|
||||||
|
}
|
||||||
|
TypeConstructor[not set]{
|
||||||
|
__vec_2__f32
|
||||||
|
ScalarConstructor[not set]{50.000000}
|
||||||
|
ScalarConstructor[not set]{60.000000}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VariableDeclStatement{
|
||||||
|
VariableConst{
|
||||||
|
x_2
|
||||||
|
none
|
||||||
|
__mat_2_3__f32
|
||||||
|
{
|
||||||
|
Identifier[not set]{x_2_1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})")) << body_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest_CompositeInsert, Matrix_IndexTooBigError) {
|
||||||
|
const auto assembly = Preamble() + R"(
|
||||||
|
%ptr = OpTypePointer Function %m3v2float
|
||||||
|
|
||||||
|
%100 = OpFunction %void None %voidfn
|
||||||
|
%entry = OpLabel
|
||||||
|
%var = OpVariable %ptr Function
|
||||||
|
%1 = OpLoad %m3v2float %var
|
||||||
|
%2 = OpCompositeInsert %m3v2float %v2float_50_60 %1 3
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
auto p = parser(test::Assemble(assembly));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
|
||||||
|
FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
|
||||||
|
EXPECT_FALSE(fe.EmitBody()) << p->error();
|
||||||
|
EXPECT_THAT(p->error(), Eq("OpCompositeInsert %2 index value 3 is out of "
|
||||||
|
"bounds for matrix of 3 elements"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest_CompositeInsert, Matrix_Vector) {
|
||||||
|
const auto assembly = Preamble() + R"(
|
||||||
|
%ptr = OpTypePointer Function %m3v2float
|
||||||
|
|
||||||
|
%100 = OpFunction %void None %voidfn
|
||||||
|
%entry = OpLabel
|
||||||
|
%var = OpVariable %ptr Function
|
||||||
|
%1 = OpLoad %m3v2float %var
|
||||||
|
%2 = OpCompositeInsert %m3v2float %v2float_50_60 %1 2
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
auto p = parser(test::Assemble(assembly));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
|
||||||
|
FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
|
||||||
|
EXPECT_TRUE(fe.EmitBody()) << p->error();
|
||||||
|
auto body_str = ToString(p->builder(), fe.ast_body());
|
||||||
|
EXPECT_THAT(body_str, HasSubstr(R"(VariableDeclStatement{
|
||||||
|
Variable{
|
||||||
|
x_2_1
|
||||||
|
function
|
||||||
|
__mat_2_3__f32
|
||||||
|
{
|
||||||
|
Identifier[not set]{x_1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assignment{
|
||||||
|
ArrayAccessor[not set]{
|
||||||
|
Identifier[not set]{x_2_1}
|
||||||
|
ScalarConstructor[not set]{2}
|
||||||
|
}
|
||||||
|
TypeConstructor[not set]{
|
||||||
|
__vec_2__f32
|
||||||
|
ScalarConstructor[not set]{50.000000}
|
||||||
|
ScalarConstructor[not set]{60.000000}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VariableDeclStatement{
|
||||||
|
VariableConst{
|
||||||
|
x_2
|
||||||
|
none
|
||||||
|
__mat_2_3__f32
|
||||||
|
{
|
||||||
|
Identifier[not set]{x_2_1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})")) << body_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest_CompositeInsert, Array) {
|
||||||
|
const auto assembly = Preamble() + R"(
|
||||||
|
%ptr = OpTypePointer Function %a_u_5
|
||||||
|
|
||||||
|
%100 = OpFunction %void None %voidfn
|
||||||
|
%entry = OpLabel
|
||||||
|
%var = OpVariable %ptr Function
|
||||||
|
%1 = OpLoad %a_u_5 %var
|
||||||
|
%2 = OpCompositeInsert %a_u_5 %uint_20 %1 3
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
auto p = parser(test::Assemble(assembly));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
|
||||||
|
FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
|
||||||
|
EXPECT_TRUE(fe.EmitBody()) << p->error();
|
||||||
|
auto body_str = ToString(p->builder(), fe.ast_body());
|
||||||
|
EXPECT_THAT(body_str, HasSubstr(R"(VariableDeclStatement{
|
||||||
|
Variable{
|
||||||
|
x_2_1
|
||||||
|
function
|
||||||
|
__array__u32_5
|
||||||
|
{
|
||||||
|
Identifier[not set]{x_1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assignment{
|
||||||
|
ArrayAccessor[not set]{
|
||||||
|
Identifier[not set]{x_2_1}
|
||||||
|
ScalarConstructor[not set]{3}
|
||||||
|
}
|
||||||
|
ScalarConstructor[not set]{20}
|
||||||
|
}
|
||||||
|
VariableDeclStatement{
|
||||||
|
VariableConst{
|
||||||
|
x_2
|
||||||
|
none
|
||||||
|
__array__u32_5
|
||||||
|
{
|
||||||
|
Identifier[not set]{x_2_1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})")) << body_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest_CompositeInsert, RuntimeArray_IsError) {
|
||||||
|
const auto assembly = Preamble() + R"(
|
||||||
|
%rtarr = OpTypeRuntimeArray %uint
|
||||||
|
%ptr = OpTypePointer Function %rtarr
|
||||||
|
|
||||||
|
%100 = OpFunction %void None %voidfn
|
||||||
|
%entry = OpLabel
|
||||||
|
%var = OpVariable %ptr Function
|
||||||
|
%1 = OpLoad %rtarr %var
|
||||||
|
%2 = OpCompositeInsert %rtarr %uint_20 %1 3
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
auto p = parser(test::Assemble(assembly));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
|
||||||
|
FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
|
||||||
|
EXPECT_FALSE(fe.EmitBody()) << p->error();
|
||||||
|
EXPECT_THAT(p->error(),
|
||||||
|
HasSubstr("can't do OpCompositeInsert on a runtime array: "));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest_CompositeInsert, Struct) {
|
||||||
|
const auto assembly = Preamble() + R"(
|
||||||
|
%ptr = OpTypePointer Function %s_v2f_u_i
|
||||||
|
|
||||||
|
%100 = OpFunction %void None %voidfn
|
||||||
|
%entry = OpLabel
|
||||||
|
%var = OpVariable %ptr Function
|
||||||
|
%1 = OpLoad %s_v2f_u_i %var
|
||||||
|
%2 = OpCompositeInsert %s_v2f_u_i %int_30 %1 2
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
auto p = parser(test::Assemble(assembly));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
|
||||||
|
FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
|
||||||
|
EXPECT_TRUE(fe.EmitBody()) << p->error();
|
||||||
|
auto body_str = ToString(p->builder(), fe.ast_body());
|
||||||
|
EXPECT_THAT(body_str, HasSubstr(R"(VariableDeclStatement{
|
||||||
|
Variable{
|
||||||
|
x_2_1
|
||||||
|
function
|
||||||
|
__struct_S
|
||||||
|
{
|
||||||
|
Identifier[not set]{x_1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assignment{
|
||||||
|
MemberAccessor[not set]{
|
||||||
|
Identifier[not set]{x_2_1}
|
||||||
|
Identifier[not set]{field2}
|
||||||
|
}
|
||||||
|
ScalarConstructor[not set]{30}
|
||||||
|
}
|
||||||
|
VariableDeclStatement{
|
||||||
|
VariableConst{
|
||||||
|
x_2
|
||||||
|
none
|
||||||
|
__struct_S
|
||||||
|
{
|
||||||
|
Identifier[not set]{x_2_1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})")) << body_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest_CompositeInsert, Struct_DifferOnlyInMemberName) {
|
||||||
|
const auto assembly =
|
||||||
|
R"(
|
||||||
|
OpMemberName %s0 0 "algo"
|
||||||
|
OpMemberName %s1 0 "rithm"
|
||||||
|
)" + Preamble() +
|
||||||
|
R"(
|
||||||
|
%s0 = OpTypeStruct %uint
|
||||||
|
%s1 = OpTypeStruct %uint
|
||||||
|
%ptr0 = OpTypePointer Function %s0
|
||||||
|
%ptr1 = OpTypePointer Function %s1
|
||||||
|
|
||||||
|
%100 = OpFunction %void None %voidfn
|
||||||
|
%entry = OpLabel
|
||||||
|
%var0 = OpVariable %ptr0 Function
|
||||||
|
%var1 = OpVariable %ptr1 Function
|
||||||
|
%1 = OpLoad %s0 %var0
|
||||||
|
%2 = OpCompositeInsert %s0 %uint_10 %1 0
|
||||||
|
%3 = OpLoad %s1 %var1
|
||||||
|
%4 = OpCompositeInsert %s1 %uint_10 %3 0
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
auto p = parser(test::Assemble(assembly));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
|
||||||
|
FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
|
||||||
|
EXPECT_TRUE(fe.EmitBody()) << p->error();
|
||||||
|
auto body_str = ToString(p->builder(), fe.ast_body());
|
||||||
|
EXPECT_THAT(body_str, HasSubstr(R"(VariableDeclStatement{
|
||||||
|
Variable{
|
||||||
|
x_2_1
|
||||||
|
function
|
||||||
|
__struct_S_1
|
||||||
|
{
|
||||||
|
Identifier[not set]{x_1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assignment{
|
||||||
|
MemberAccessor[not set]{
|
||||||
|
Identifier[not set]{x_2_1}
|
||||||
|
Identifier[not set]{algo}
|
||||||
|
}
|
||||||
|
ScalarConstructor[not set]{10}
|
||||||
|
}
|
||||||
|
VariableDeclStatement{
|
||||||
|
VariableConst{
|
||||||
|
x_2
|
||||||
|
none
|
||||||
|
__struct_S_1
|
||||||
|
{
|
||||||
|
Identifier[not set]{x_2_1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)")) << body_str;
|
||||||
|
EXPECT_THAT(body_str, HasSubstr(R"(VariableDeclStatement{
|
||||||
|
Variable{
|
||||||
|
x_4_1
|
||||||
|
function
|
||||||
|
__struct_S_2
|
||||||
|
{
|
||||||
|
Identifier[not set]{x_3}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assignment{
|
||||||
|
MemberAccessor[not set]{
|
||||||
|
Identifier[not set]{x_4_1}
|
||||||
|
Identifier[not set]{rithm}
|
||||||
|
}
|
||||||
|
ScalarConstructor[not set]{10}
|
||||||
|
}
|
||||||
|
VariableDeclStatement{
|
||||||
|
VariableConst{
|
||||||
|
x_4
|
||||||
|
none
|
||||||
|
__struct_S_2
|
||||||
|
{
|
||||||
|
Identifier[not set]{x_4_1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})")) << body_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest_CompositeInsert, Struct_IndexTooBigError) {
|
||||||
|
const auto assembly = Preamble() + R"(
|
||||||
|
%ptr = OpTypePointer Function %s_v2f_u_i
|
||||||
|
|
||||||
|
%100 = OpFunction %void None %voidfn
|
||||||
|
%entry = OpLabel
|
||||||
|
%var = OpVariable %ptr Function
|
||||||
|
%1 = OpLoad %s_v2f_u_i %var
|
||||||
|
%2 = OpCompositeInsert %s_v2f_u_i %uint_10 %1 40
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
auto p = parser(test::Assemble(assembly));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
|
||||||
|
FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
|
||||||
|
EXPECT_FALSE(fe.EmitBody());
|
||||||
|
EXPECT_THAT(p->error(), Eq("OpCompositeInsert %2 index value 40 is out of "
|
||||||
|
"bounds for structure %26 having 3 members"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest_CompositeInsert, Struct_Array_Matrix_Vector) {
|
||||||
|
const auto assembly = Preamble() + R"(
|
||||||
|
%a_mat = OpTypeArray %m3v2float %uint_3
|
||||||
|
%s = OpTypeStruct %uint %a_mat
|
||||||
|
%ptr = OpTypePointer Function %s
|
||||||
|
|
||||||
|
%100 = OpFunction %void None %voidfn
|
||||||
|
%entry = OpLabel
|
||||||
|
%var = OpVariable %ptr Function
|
||||||
|
%1 = OpLoad %s %var
|
||||||
|
%2 = OpCompositeInsert %s %float_70 %1 1 2 0 1
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
auto p = parser(test::Assemble(assembly));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
|
||||||
|
FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100));
|
||||||
|
EXPECT_TRUE(fe.EmitBody()) << p->error();
|
||||||
|
auto body_str = ToString(p->builder(), fe.ast_body());
|
||||||
|
EXPECT_THAT(body_str, HasSubstr(R"(VariableDeclStatement{
|
||||||
|
Variable{
|
||||||
|
x_2_1
|
||||||
|
function
|
||||||
|
__struct_S_1
|
||||||
|
{
|
||||||
|
Identifier[not set]{x_1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assignment{
|
||||||
|
MemberAccessor[not set]{
|
||||||
|
ArrayAccessor[not set]{
|
||||||
|
ArrayAccessor[not set]{
|
||||||
|
MemberAccessor[not set]{
|
||||||
|
Identifier[not set]{x_2_1}
|
||||||
|
Identifier[not set]{field1}
|
||||||
|
}
|
||||||
|
ScalarConstructor[not set]{2}
|
||||||
|
}
|
||||||
|
ScalarConstructor[not set]{0}
|
||||||
|
}
|
||||||
|
Identifier[not set]{y}
|
||||||
|
}
|
||||||
|
ScalarConstructor[not set]{70.000000}
|
||||||
|
}
|
||||||
|
VariableDeclStatement{
|
||||||
|
VariableConst{
|
||||||
|
x_2
|
||||||
|
none
|
||||||
|
__struct_S_1
|
||||||
|
{
|
||||||
|
Identifier[not set]{x_2_1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})")) << body_str;
|
||||||
|
}
|
||||||
|
|
||||||
using SpvParserTest_CopyObject = SpvParserTest;
|
using SpvParserTest_CopyObject = SpvParserTest;
|
||||||
|
|
||||||
TEST_F(SpvParserTest_CopyObject, Scalar) {
|
TEST_F(SpvParserTest_CopyObject, Scalar) {
|
||||||
|
|
Loading…
Reference in New Issue