spirv-reader: vertex_index always has u32 store-type

Fixed: tint:483
Change-Id: Ie26941ab751425dfbc0924ea21bee32dc0f92527
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/40623
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:
David Neto 2021-02-08 15:02:49 +00:00 committed by Commit Bot service account
parent 3c568f188d
commit 6d1687fb01
4 changed files with 409 additions and 4 deletions

View File

@ -2035,6 +2035,11 @@ TypedExpression FunctionEmitter::MakeExpression(uint32_t id) {
Fail() << "unhandled use of a pointer to the SampleId builtin, with ID: " Fail() << "unhandled use of a pointer to the SampleId builtin, with ID: "
<< id; << id;
return {}; return {};
case SkipReason::kVertexIndexBuiltinPointer:
Fail()
<< "unhandled use of a pointer to the VertexIndex builtin, with ID: "
<< id;
return {};
case SkipReason::kSampleMaskInBuiltinPointer: case SkipReason::kSampleMaskInBuiltinPointer:
Fail() Fail()
<< "unhandled use of a pointer to the SampleMask builtin, with ID: " << "unhandled use of a pointer to the SampleMask builtin, with ID: "
@ -3036,14 +3041,19 @@ bool FunctionEmitter::EmitStatement(const spvtools::opt::Instruction& inst) {
// Memory accesses must be issued in SPIR-V program order. // Memory accesses must be issued in SPIR-V program order.
// So represent a load by a new const definition. // So represent a load by a new const definition.
const auto ptr_id = inst.GetSingleWordInOperand(0); const auto ptr_id = inst.GetSingleWordInOperand(0);
switch (GetSkipReason(ptr_id)) { const auto skip_reason = GetSkipReason(ptr_id);
switch (skip_reason) {
case SkipReason::kPointSizeBuiltinPointer: case SkipReason::kPointSizeBuiltinPointer:
GetDefInfo(inst.result_id())->skip = GetDefInfo(inst.result_id())->skip =
SkipReason::kPointSizeBuiltinValue; SkipReason::kPointSizeBuiltinValue;
return true; return true;
case SkipReason::kSampleIdBuiltinPointer: { case SkipReason::kSampleIdBuiltinPointer:
case SkipReason::kVertexIndexBuiltinPointer: {
// The SPIR-V variable is i32, but WGSL requires u32. // The SPIR-V variable is i32, but WGSL requires u32.
auto var_id = parser_impl_.IdForSpecialBuiltIn(SpvBuiltInSampleId); auto var_id = parser_impl_.IdForSpecialBuiltIn(
(skip_reason == SkipReason::kSampleIdBuiltinPointer)
? SpvBuiltInSampleId
: SpvBuiltInVertexIndex);
auto name = namer_.Name(var_id); auto name = namer_.Name(var_id);
ast::Expression* id_expr = create<ast::IdentifierExpression>( ast::Expression* id_expr = create<ast::IdentifierExpression>(
Source{}, builder_.Symbols().Register(name)); Source{}, builder_.Symbols().Register(name));
@ -3712,6 +3722,9 @@ bool FunctionEmitter::RegisterSpecialBuiltInVariables() {
case SpvBuiltInSampleId: case SpvBuiltInSampleId:
def->skip = SkipReason::kSampleIdBuiltinPointer; def->skip = SkipReason::kSampleIdBuiltinPointer;
break; break;
case SpvBuiltInVertexIndex:
def->skip = SkipReason::kVertexIndexBuiltinPointer;
break;
case SpvBuiltInSampleMask: { case SpvBuiltInSampleMask: {
// Distinguish between input and output variable. // Distinguish between input and output variable.
const auto storage_class = const auto storage_class =

View File

@ -229,6 +229,10 @@ enum class SkipReason {
/// variable. Don't generate its address. /// variable. Don't generate its address.
kSampleIdBuiltinPointer, kSampleIdBuiltinPointer,
/// `kVertexIndexBuiltinPointer`: the value is a pointer to the VertexIndex
/// builtin variable. Don't generate its address.
kVertexIndexBuiltinPointer,
/// `kSampleMaskInBuiltinPointer`: the value is a pointer to the SampleMaskIn /// `kSampleMaskInBuiltinPointer`: the value is a pointer to the SampleMaskIn
/// builtin input variable. Don't generate its address. /// builtin input variable. Don't generate its address.
kSampleMaskInBuiltinPointer, kSampleMaskInBuiltinPointer,
@ -344,6 +348,9 @@ inline std::ostream& operator<<(std::ostream& o, const DefInfo& di) {
case SkipReason::kSampleIdBuiltinPointer: case SkipReason::kSampleIdBuiltinPointer:
o << " skip:sampleid_pointer"; o << " skip:sampleid_pointer";
break; break;
case SkipReason::kVertexIndexBuiltinPointer:
o << " skip:vertexindex_pointer";
break;
case SkipReason::kSampleMaskInBuiltinPointer: case SkipReason::kSampleMaskInBuiltinPointer:
o << " skip:samplemaskin_pointer"; o << " skip:samplemaskin_pointer";
break; break;

View File

@ -1293,7 +1293,8 @@ ast::Variable* ParserImpl::MakeVariable(
special_builtins_[id] = spv_builtin; special_builtins_[id] = spv_builtin;
return nullptr; return nullptr;
case SpvBuiltInSampleId: case SpvBuiltInSampleId:
// The SPIR-V variable might is likely to be signed (because GLSL case SpvBuiltInVertexIndex:
// The SPIR-V variable is likely to be signed (because GLSL
// requires signed), but WGSL requires unsigned. Handle specially // requires signed), but WGSL requires unsigned. Handle specially
// so we always perform the conversion at load and store. // so we always perform the conversion at load and store.
if (auto* forced_type = unsigned_type_for_[type]) { if (auto* forced_type = unsigned_type_for_[type]) {

View File

@ -192,6 +192,10 @@ TEST_F(SpvModuleScopeVarParserTest, PrivateVar) {
} }
TEST_F(SpvModuleScopeVarParserTest, BuiltinVertexIndex) { TEST_F(SpvModuleScopeVarParserTest, BuiltinVertexIndex) {
// This is the simple case for the vertex_index builtin,
// where the SPIR-V uses the same store type as in WGSL.
// See later for tests where the SPIR-V store type is signed
// integer, as in GLSL.
auto p = parser(test::Assemble(R"( auto p = parser(test::Assemble(R"(
OpDecorate %52 BuiltIn VertexIndex OpDecorate %52 BuiltIn VertexIndex
%uint = OpTypeInt 32 0 %uint = OpTypeInt 32 0
@ -2965,6 +2969,386 @@ TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_I32_AccessChain) {
<< module_str; << module_str;
} }
// Returns the start of a shader for testing VertexIndex,
// parameterized by store type of %int or %uint
std::string VertexIndexPreamble(std::string store_type) {
return R"(
OpCapability Shader
OpMemoryModel Logical Simple
OpEntryPoint Vertex %main "main" %1
OpDecorate %1 BuiltIn VertexIndex
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%float = OpTypeFloat 32
%uint = OpTypeInt 32 0
%int = OpTypeInt 32 1
%ptr_ty = OpTypePointer Input )" +
store_type + R"(
%1 = OpVariable %ptr_ty Input
)";
}
TEST_F(SpvModuleScopeVarParserTest, VertexIndex_I32_Load_Direct) {
const std::string assembly = VertexIndexPreamble("%int") + R"(
%main = OpFunction %void None %voidfn
%entry = OpLabel
%2 = OpLoad %int %1
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
EXPECT_TRUE(p->error().empty());
const auto module_str = p->program().to_str();
// Correct declaration
EXPECT_THAT(module_str, HasSubstr(R"(
Variable{
Decorations{
BuiltinDecoration{vertex_index}
}
x_1
in
__u32
})"));
// Correct body
EXPECT_THAT(module_str, HasSubstr(R"(
VariableDeclStatement{
VariableConst{
x_2
none
__i32
{
TypeConstructor[not set]{
__i32
Identifier[not set]{x_1}
}
}
}
})"))
<< module_str;
}
TEST_F(SpvModuleScopeVarParserTest, VertexIndex_I32_Load_CopyObject) {
const std::string assembly = VertexIndexPreamble("%int") + R"(
%main = OpFunction %void None %voidfn
%entry = OpLabel
%copy_ptr = OpCopyObject %ptr_ty %1
%2 = OpLoad %int %copy_ptr
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
EXPECT_TRUE(p->error().empty());
const auto module_str = p->program().to_str();
// Correct declaration
EXPECT_THAT(module_str, HasSubstr(R"(
Variable{
Decorations{
BuiltinDecoration{vertex_index}
}
x_1
in
__u32
})"));
// Correct body
EXPECT_THAT(module_str, HasSubstr(R"(
VariableDeclStatement{
VariableConst{
x_2
none
__i32
{
TypeConstructor[not set]{
__i32
Identifier[not set]{x_1}
}
}
}
})"))
<< module_str;
}
TEST_F(SpvModuleScopeVarParserTest, VertexIndex_I32_Load_AccessChain) {
const std::string assembly = VertexIndexPreamble("%int") + R"(
%main = OpFunction %void None %voidfn
%entry = OpLabel
%copy_ptr = OpAccessChain %ptr_ty %1
%2 = OpLoad %int %copy_ptr
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
EXPECT_TRUE(p->error().empty());
const auto module_str = p->program().to_str();
// Correct declaration
EXPECT_THAT(module_str, HasSubstr(R"(
Variable{
Decorations{
BuiltinDecoration{vertex_index}
}
x_1
in
__u32
})"));
// Correct body
EXPECT_THAT(module_str, HasSubstr(R"(
VariableDeclStatement{
VariableConst{
x_2
none
__i32
{
TypeConstructor[not set]{
__i32
Identifier[not set]{x_1}
}
}
}
})"))
<< module_str;
}
TEST_F(SpvModuleScopeVarParserTest, VertexIndex_I32_FunctParam) {
const std::string assembly = VertexIndexPreamble("%int") + R"(
%helper_ty = OpTypeFunction %int %ptr_ty
%helper = OpFunction %int None %helper_ty
%param = OpFunctionParameter %ptr_ty
%helper_entry = OpLabel
%3 = OpLoad %int %param
OpReturnValue %3
OpFunctionEnd
%main = OpFunction %void None %voidfn
%entry = OpLabel
%result = OpFunctionCall %int %helper %1
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
// TODO(dneto): We can handle this if we make a shadow variable and mutate
// the parameter type.
ASSERT_FALSE(p->BuildAndParseInternalModule());
EXPECT_THAT(
p->error(),
HasSubstr(
"unhandled use of a pointer to the VertexIndex builtin, with ID: 1"));
}
TEST_F(SpvModuleScopeVarParserTest, VertexIndex_U32_Load_Direct) {
const std::string assembly = VertexIndexPreamble("%uint") + R"(
%main = OpFunction %void None %voidfn
%entry = OpLabel
%2 = OpLoad %uint %1
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
EXPECT_TRUE(p->error().empty());
const auto module_str = p->program().to_str();
// Correct declaration
EXPECT_THAT(module_str, HasSubstr(R"(
Variable{
Decorations{
BuiltinDecoration{vertex_index}
}
x_1
in
__u32
})"));
// Correct body
EXPECT_THAT(module_str, HasSubstr(R"(
VariableDeclStatement{
VariableConst{
x_2
none
__u32
{
Identifier[not set]{x_1}
}
}
})"))
<< module_str;
}
TEST_F(SpvModuleScopeVarParserTest, VertexIndex_U32_Load_CopyObject) {
const std::string assembly = VertexIndexPreamble("%uint") + R"(
%main = OpFunction %void None %voidfn
%entry = OpLabel
%copy_ptr = OpCopyObject %ptr_ty %1
%2 = OpLoad %uint %copy_ptr
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
EXPECT_TRUE(p->error().empty());
const auto module_str = p->program().to_str();
// Correct declaration
EXPECT_THAT(module_str, HasSubstr(R"(
Variable{
Decorations{
BuiltinDecoration{vertex_index}
}
x_1
in
__u32
})"));
// Correct body
EXPECT_THAT(module_str, HasSubstr(R"(
VariableDeclStatement{
VariableConst{
x_11
none
__ptr_in__u32
{
Identifier[not set]{x_1}
}
}
}
VariableDeclStatement{
VariableConst{
x_2
none
__u32
{
Identifier[not set]{x_11}
}
}
})"))
<< module_str;
}
TEST_F(SpvModuleScopeVarParserTest, VertexIndex_U32_Load_AccessChain) {
const std::string assembly = VertexIndexPreamble("%uint") + R"(
%main = OpFunction %void None %voidfn
%entry = OpLabel
%copy_ptr = OpAccessChain %ptr_ty %1
%2 = OpLoad %uint %copy_ptr
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
EXPECT_TRUE(p->error().empty());
const auto module_str = p->program().to_str();
// Correct declaration
EXPECT_THAT(module_str, HasSubstr(R"(
Variable{
Decorations{
BuiltinDecoration{vertex_index}
}
x_1
in
__u32
})"));
// Correct body
EXPECT_THAT(module_str, HasSubstr(R"(
VariableDeclStatement{
VariableConst{
x_2
none
__u32
{
Identifier[not set]{x_1}
}
}
})"))
<< module_str;
}
TEST_F(SpvModuleScopeVarParserTest, VertexIndex_U32_FunctParam) {
const std::string assembly = VertexIndexPreamble("%uint") + R"(
%helper_ty = OpTypeFunction %uint %ptr_ty
%helper = OpFunction %uint None %helper_ty
%param = OpFunctionParameter %ptr_ty
%helper_entry = OpLabel
%3 = OpLoad %uint %param
OpReturnValue %3
OpFunctionEnd
%main = OpFunction %void None %voidfn
%entry = OpLabel
%result = OpFunctionCall %uint %helper %1
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
// TODO(dneto): We can handle this if we make a shadow variable and mutate
// the parameter type.
ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
EXPECT_TRUE(p->error().empty());
const auto module_str = p->program().to_str();
// Correct declaration
EXPECT_THAT(module_str, HasSubstr(R"(
Variable{
Decorations{
BuiltinDecoration{vertex_index}
}
x_1
in
__u32
})"));
// Correct bodies
EXPECT_THAT(module_str, HasSubstr(R"(
Function x_11 -> __u32
(
VariableConst{
x_12
none
__ptr_in__u32
}
)
{
VariableDeclStatement{
VariableConst{
x_3
none
__u32
{
Identifier[not set]{x_12}
}
}
}
Return{
{
Identifier[not set]{x_3}
}
}
}
Function main -> __void
StageDecoration{vertex}
()
{
VariableDeclStatement{
VariableConst{
x_15
none
__u32
{
Call[not set]{
Identifier[not set]{x_11}
(
Identifier[not set]{x_1}
)
}
}
}
}
Return{}
}
})")) << module_str;
}
// TODO(dneto): Test passing pointer to SampleMask as function parameter, // TODO(dneto): Test passing pointer to SampleMask as function parameter,
// both input case and output case. // both input case and output case.