Add determination of entrypoint callees.

This Cl updates the type determiner to annotate each function with the
name of any entry points which call into the given function. This will
allow determining in the backends if we need to duplicate the function
due to differing entry point parameter requirements.

Bug: tint:8
Change-Id: Icd7c4ccab72dd6eabcf0abaf1159319949c4ecf5
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/24760
Reviewed-by: David Neto <dneto@google.com>
This commit is contained in:
dan sinclair 2020-07-14 19:45:47 +00:00
parent 8604eb4495
commit 16890b9ce8
6 changed files with 162 additions and 0 deletions

View File

@ -51,6 +51,15 @@ void Function::add_referenced_module_variable(Variable* var) {
referenced_module_vars_.push_back(var);
}
void Function::add_ancestor_entry_point(const std::string& ep) {
for (const auto& point : ancestor_entry_points_) {
if (point == ep) {
return;
}
}
ancestor_entry_points_.push_back(ep);
}
bool Function::IsValid() const {
for (const auto& param : params_) {
if (param == nullptr || !param->IsValid())

View File

@ -77,6 +77,14 @@ class Function : public Node {
return referenced_module_vars_;
}
/// Adds an ancestor entry point
/// @param ep the entry point ancestor
void add_ancestor_entry_point(const std::string& ep);
/// @returns the ancestor entry points
const std::vector<std::string>& ancestor_entry_points() const {
return ancestor_entry_points_;
}
/// Sets the return type of the function
/// @param type the return type
void set_return_type(type::Type* type) { return_type_ = type; }
@ -108,6 +116,7 @@ class Function : public Node {
type::Type* return_type_ = nullptr;
StatementList body_;
std::vector<Variable*> referenced_module_vars_;
std::vector<std::string> ancestor_entry_points_;
};
/// A list of unique functions

View File

@ -16,6 +16,7 @@
#include "gtest/gtest.h"
#include "src/ast/kill_statement.h"
#include "src/ast/pipeline_stage.h"
#include "src/ast/type/f32_type.h"
#include "src/ast/type/i32_type.h"
#include "src/ast/type/void_type.h"
@ -77,6 +78,19 @@ TEST_F(FunctionTest, AddDuplicateReferencedVariables) {
EXPECT_EQ(f.referenced_module_variables()[1], &v2);
}
TEST_F(FunctionTest, AddDuplicateEntryPoints) {
ast::type::VoidType void_type;
Function f("func", VariableList{}, &void_type);
f.add_ancestor_entry_point("main");
ASSERT_EQ(1u, f.ancestor_entry_points().size());
EXPECT_EQ("main", f.ancestor_entry_points()[0]);
f.add_ancestor_entry_point("main");
ASSERT_EQ(1u, f.ancestor_entry_points().size());
EXPECT_EQ("main", f.ancestor_entry_points()[0]);
}
TEST_F(FunctionTest, IsValid) {
type::VoidType void_type;
type::I32Type i32;

View File

@ -188,9 +188,27 @@ bool TypeDeterminer::Determine() {
if (!DetermineFunctions(mod_->functions())) {
return false;
}
// Walk over the caller to callee information and update functions with which
// entry points call those functions.
for (const auto& ep : mod_->entry_points()) {
for (const auto& callee : caller_to_callee_[ep->function_name()]) {
set_entry_points(callee, ep->name());
}
}
return true;
}
void TypeDeterminer::set_entry_points(const std::string& fn_name,
const std::string& ep_name) {
name_to_function_[fn_name]->add_ancestor_entry_point(ep_name);
for (const auto& callee : caller_to_callee_[fn_name]) {
set_entry_points(callee, ep_name);
}
}
bool TypeDeterminer::DetermineFunctions(const ast::FunctionList& funcs) {
for (const auto& func : funcs) {
if (!DetermineFunction(func.get())) {
@ -457,6 +475,10 @@ bool TypeDeterminer::DetermineCall(ast::CallExpression* expr) {
imp->AddMethodId(ident->name(), ext_id);
expr->func()->set_result_type(result_type);
} else {
if (current_function_) {
caller_to_callee_[current_function_->name()].push_back(ident->name());
}
// An identifier with a single name is a function call, not an import
// lookup which we can handle with the regular identifier lookup.
if (!DetermineResultType(ident)) {

View File

@ -105,6 +105,7 @@ class TypeDeterminer {
private:
void set_error(const Source& src, const std::string& msg);
void set_referenced_from_function_if_needed(ast::Variable* var);
void set_entry_points(const std::string& fn_name, const std::string& ep_name);
bool DetermineArrayAccessor(ast::ArrayAccessorExpression* expr);
bool DetermineAs(ast::AsExpression* expr);
@ -123,6 +124,9 @@ class TypeDeterminer {
ScopeStack<ast::Variable*> variable_stack_;
std::unordered_map<std::string, ast::Function*> name_to_function_;
ast::Function* current_function_ = nullptr;
// Map from caller functions to callee functions.
std::unordered_map<std::string, std::vector<std::string>> caller_to_callee_;
};
} // namespace tint

View File

@ -3746,5 +3746,109 @@ INSTANTIATE_TEST_SUITE_P(TypeDeterminerTest,
testing::Values(GLSLData{"sclamp", GLSLstd450SClamp},
GLSLData{"uclamp", GLSLstd450UClamp}));
TEST_F(TypeDeterminerTest, Function_EntryPoints) {
ast::type::F32Type f32;
// fn b() {}
// fn c() { b(); }
// fn a() { c(); }
// fn ep_1() { a(); b(); }
// fn ep_2() { c();}
//
// c -> {ep_1, ep_2}
// a -> {ep_1}
// b -> {ep_1, ep_2}
// ep_1 -> {}
// ep_2 -> {}
ast::VariableList params;
auto func_b = std::make_unique<ast::Function>("b", std::move(params), &f32);
auto* func_b_ptr = func_b.get();
ast::StatementList body;
func_b->set_body(std::move(body));
auto func_c = std::make_unique<ast::Function>("c", std::move(params), &f32);
auto* func_c_ptr = func_c.get();
body.push_back(std::make_unique<ast::AssignmentStatement>(
std::make_unique<ast::IdentifierExpression>("second"),
std::make_unique<ast::CallExpression>(
std::make_unique<ast::IdentifierExpression>("b"),
ast::ExpressionList{})));
func_c->set_body(std::move(body));
auto func_a = std::make_unique<ast::Function>("a", std::move(params), &f32);
auto* func_a_ptr = func_a.get();
body.push_back(std::make_unique<ast::AssignmentStatement>(
std::make_unique<ast::IdentifierExpression>("first"),
std::make_unique<ast::CallExpression>(
std::make_unique<ast::IdentifierExpression>("c"),
ast::ExpressionList{})));
func_a->set_body(std::move(body));
auto ep_1_func =
std::make_unique<ast::Function>("ep_1_func", std::move(params), &f32);
auto* ep_1_func_ptr = ep_1_func.get();
body.push_back(std::make_unique<ast::AssignmentStatement>(
std::make_unique<ast::IdentifierExpression>("call_a"),
std::make_unique<ast::CallExpression>(
std::make_unique<ast::IdentifierExpression>("a"),
ast::ExpressionList{})));
body.push_back(std::make_unique<ast::AssignmentStatement>(
std::make_unique<ast::IdentifierExpression>("call_b"),
std::make_unique<ast::CallExpression>(
std::make_unique<ast::IdentifierExpression>("b"),
ast::ExpressionList{})));
ep_1_func->set_body(std::move(body));
auto ep_2_func =
std::make_unique<ast::Function>("ep_2_func", std::move(params), &f32);
auto* ep_2_func_ptr = ep_2_func.get();
body.push_back(std::make_unique<ast::AssignmentStatement>(
std::make_unique<ast::IdentifierExpression>("call_c"),
std::make_unique<ast::CallExpression>(
std::make_unique<ast::IdentifierExpression>("c"),
ast::ExpressionList{})));
ep_2_func->set_body(std::move(body));
auto ep_1 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kVertex,
"ep_1", "ep_1_func");
auto ep_2 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kVertex,
"ep_2", "ep_2_func");
mod()->AddFunction(std::move(func_b));
mod()->AddFunction(std::move(func_c));
mod()->AddFunction(std::move(func_a));
mod()->AddFunction(std::move(ep_1_func));
mod()->AddFunction(std::move(ep_2_func));
mod()->AddEntryPoint(std::move(ep_1));
mod()->AddEntryPoint(std::move(ep_2));
// Register the functions and calculate the callers
ASSERT_TRUE(td()->Determine()) << td()->error();
const auto& b_eps = func_b_ptr->ancestor_entry_points();
ASSERT_EQ(2u, b_eps.size());
EXPECT_EQ("ep_1", b_eps[0]);
EXPECT_EQ("ep_2", b_eps[1]);
const auto& a_eps = func_a_ptr->ancestor_entry_points();
ASSERT_EQ(1u, a_eps.size());
EXPECT_EQ("ep_1", a_eps[0]);
const auto& c_eps = func_c_ptr->ancestor_entry_points();
ASSERT_EQ(2u, c_eps.size());
EXPECT_EQ("ep_1", c_eps[0]);
EXPECT_EQ("ep_2", c_eps[1]);
EXPECT_TRUE(ep_1_func_ptr->ancestor_entry_points().empty());
EXPECT_TRUE(ep_2_func_ptr->ancestor_entry_points().empty());
}
} // namespace
} // namespace tint