spirv-reader: Register a name for an entry point inner implementation
It's tricky when mulitple entry points share the same implementation
function.
Bug: tint:508
Change-Id: Ie3084d4af8d6eb3aa9f0d25babb8ebfaed7b2fae
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/48902
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: David Neto <dneto@google.com>
Auto-Submit: David Neto <dneto@google.com>
Reviewed-by: James Price <jrprice@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/reader/spirv/entry_point_info.cc b/src/reader/spirv/entry_point_info.cc
index 61f1586..44242a5 100644
--- a/src/reader/spirv/entry_point_info.cc
+++ b/src/reader/spirv/entry_point_info.cc
@@ -22,10 +22,14 @@
EntryPointInfo::EntryPointInfo(std::string the_name,
ast::PipelineStage the_stage,
+ bool the_owns_inner_implementation,
+ std::string the_inner_name,
std::vector<uint32_t>&& the_inputs,
std::vector<uint32_t>&& the_outputs)
: name(the_name),
stage(the_stage),
+ owns_inner_implementation(the_owns_inner_implementation),
+ inner_name(std::move(the_inner_name)),
inputs(std::move(the_inputs)),
outputs(std::move(the_outputs)) {}
diff --git a/src/reader/spirv/entry_point_info.h b/src/reader/spirv/entry_point_info.h
index 8cb11f3..ee819cc 100644
--- a/src/reader/spirv/entry_point_info.h
+++ b/src/reader/spirv/entry_point_info.h
@@ -26,25 +26,39 @@
/// Entry point information for a function
struct EntryPointInfo {
- // Constructor.
- // @param the_name the name of the entry point
- // @param the_stage the pipeline stage
- // @param the_inputs list of IDs for Input variables used by the shader
- // @param the_outputs list of IDs for Output variables used by the shader
+ /// Constructor.
+ /// @param the_name the name of the entry point
+ /// @param the_stage the pipeline stage
+ /// @param the_inputs list of IDs for Input variables used by the shader
+ /// @param the_outputs list of IDs for Output variables used by the shader
EntryPointInfo(std::string the_name,
ast::PipelineStage the_stage,
+ bool the_owns_inner_implementation,
+ std::string the_inner_name,
std::vector<uint32_t>&& the_inputs,
std::vector<uint32_t>&& the_outputs);
- // Copy constructor
- // @param other the other entry point info to be built from
+ /// Copy constructor
+ /// @param other the other entry point info to be built from
EntryPointInfo(const EntryPointInfo& other);
- // Destructor
+ /// Destructor
~EntryPointInfo();
- /// The entry point name
+ /// The entry point name.
+ /// In the WGSL output, this function will have pipeline inputs and outputs
+ /// as parameters. This function will store them into Private variables,
+ /// and then call the "inner" function, named by the next memeber.
+ /// Then outputs are copied from the private variables to the return value.
std::string name;
/// The entry point stage
ast::PipelineStage stage = ast::PipelineStage::kNone;
+ /// True when this entry point is responsible for generating the
+ /// inner implementation function. False when this is the second entry
+ /// point encountered for the same function in SPIR-V. It's unusual, but
+ /// possible for the same function to be the implementation for multiple
+ /// entry points.
+ bool owns_inner_implementation;
+ /// The name of the inner implementation function of the entry point.
+ std::string inner_name;
/// IDs of pipeline input variables, sorted and without duplicates.
std::vector<uint32_t> inputs;
/// IDs of pipeline output variables, sorted and without duplicates.
diff --git a/src/reader/spirv/parser_impl.cc b/src/reader/spirv/parser_impl.cc
index 32b32b0..559136d 100644
--- a/src/reader/spirv/parser_impl.cc
+++ b/src/reader/spirv/parser_impl.cc
@@ -712,8 +712,27 @@
module_->entry_points()) {
const auto stage = SpvExecutionModel(entry_point.GetSingleWordInOperand(0));
const uint32_t function_id = entry_point.GetSingleWordInOperand(1);
+
const std::string ep_name = entry_point.GetOperand(2).AsString();
+ bool owns_inner_implementation = false;
+ std::string inner_implementation_name;
+
+ if (hlsl_style_pipeline_io_) {
+ auto where = function_to_ep_info_.find(function_id);
+ if (where == function_to_ep_info_.end()) {
+ // If this is the first entry point to have function_id as its
+ // implementation, then this entry point is responsible for generating
+ // the inner implementation.
+ owns_inner_implementation = true;
+ inner_implementation_name = namer_.MakeDerivedName(ep_name);
+ } else {
+ // Reuse the inner implementation owned by the first entry point.
+ inner_implementation_name = where->second[0].inner_name;
+ }
+ }
+ TINT_ASSERT(ep_name != inner_implementation_name);
+
tint::UniqueVector<uint32_t> inputs;
tint::UniqueVector<uint32_t> outputs;
for (unsigned iarg = 3; iarg < entry_point.NumInOperands(); iarg++) {
@@ -739,6 +758,7 @@
function_to_ep_info_[function_id].emplace_back(
ep_name, enum_converter_.ToPipelineStage(stage),
+ owns_inner_implementation, inner_implementation_name,
std::move(sorted_inputs), std::move(sorted_outputs));
}
// The enum conversion could have failed, so return the existing status value.
diff --git a/src/reader/spirv/parser_impl_user_name_test.cc b/src/reader/spirv/parser_impl_user_name_test.cc
index 99ffbcc..f69be61 100644
--- a/src/reader/spirv/parser_impl_user_name_test.cc
+++ b/src/reader/spirv/parser_impl_user_name_test.cc
@@ -100,7 +100,7 @@
EXPECT_THAT(p->namer().GetMemberName(3, 2), Eq("field2"));
}
-TEST_F(SpvParserTest, EntryPointNamesAlwaysTakePrecedence) {
+TEST_F(SpvParserTest, EntryPointNames_AlwaysTakePrecedence) {
const std::string assembly = R"(
OpCapability Shader
OpMemoryModel Logical Simple
@@ -126,7 +126,7 @@
EXPECT_TRUE(p->BuildAndParseInternalModule());
// The first entry point grabs the best name, "main"
EXPECT_THAT(p->namer().Name(100), Eq("main"));
- // The OpName on %1 is overriden becuase the second entry point
+ // The OpName on %1 is overriden because the second entry point
// has grabbed "main_1" first.
EXPECT_THAT(p->namer().Name(1), Eq("main_1_1"));
@@ -136,6 +136,48 @@
EXPECT_EQ(ep_info[1].name, "main_1");
}
+TEST_F(SpvParserTest, EntryPointNames_DistinctFromInnerNames) {
+ const std::string assembly = R"(
+ OpCapability Shader
+ OpMemoryModel Logical Simple
+ OpEntryPoint GLCompute %100 "main"
+ OpEntryPoint Fragment %100 "main_1"
+
+ ; attempt to grab the "main_1" that would be the derived name
+ ; for the second entry point.
+ OpName %1 "main_1"
+
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+ %uint = OpTypeInt 32 0
+ %uint_0 = OpConstant %uint 0
+
+ %100 = OpFunction %void None %voidfn
+ %100_entry = OpLabel
+ %1 = OpCopyObject %uint %uint_0
+ OpReturn
+ OpFunctionEnd
+)";
+ auto p = parser(test::Assemble(assembly));
+
+ // TODO(crbug.com/tint/508): Remove this when switchover complete.
+ p->SetHLSLStylePipelineIO();
+
+ EXPECT_TRUE(p->BuildAndParseInternalModule());
+ // The first entry point grabs the best name, "main"
+ EXPECT_THAT(p->namer().Name(100), Eq("main"));
+ EXPECT_THAT(p->namer().Name(1), Eq("main_1_1"));
+
+ const auto ep_info = p->GetEntryPointInfo(100);
+ ASSERT_EQ(2u, ep_info.size());
+ EXPECT_EQ(ep_info[0].name, "main");
+ EXPECT_EQ(ep_info[0].inner_name, "main_2");
+ // The second entry point retains its name...
+ EXPECT_EQ(ep_info[1].name, "main_1");
+ // ...but will use the same implementation function.
+ EXPECT_EQ(ep_info[1].inner_name, "main_2");
+}
+
} // namespace
} // namespace spirv
} // namespace reader