-
Notifications
You must be signed in to change notification settings - Fork 2.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Opt] Global variable optimizations #857
Changes from all commits
e4df53d
ce68dab
7075833
4274db3
69c7783
428b8f1
8ce63cb
5837637
fc14b95
81f4c4c
17fec6a
3e380ac
90d586d
3ec767e
9daa183
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,67 @@ IRBuilder ¤t_ast_builder() { | |
return context->builder(); | ||
} | ||
|
||
bool maybe_same_address(Stmt *var1, Stmt *var2) { | ||
// Return true when two statements might be the same address; | ||
// false when two statements cannot be the same address. | ||
|
||
// If both stmts are allocas, they have the same address iff var1 == var2. | ||
// If only one of them is an alloca, they can never share the same address. | ||
if (var1 == var2) | ||
return true; | ||
if (var1->is<AllocaStmt>() || var2->is<AllocaStmt>()) | ||
yuanming-hu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return false; | ||
|
||
// If both statements are global temps, they have the same address iff they | ||
// have the same offset. If only one of them is a global temp, they can never | ||
// share the same address. | ||
if (var1->is<GlobalTemporaryStmt>() || var2->is<GlobalTemporaryStmt>()) { | ||
if (!var1->is<GlobalTemporaryStmt>() || !var2->is<GlobalTemporaryStmt>()) | ||
return false; | ||
return var1->as<GlobalTemporaryStmt>()->offset == | ||
var2->as<GlobalTemporaryStmt>()->offset; | ||
} | ||
|
||
// If both statements are GlobalPtrStmts or GetChStmts, we can check by | ||
// SNode::id. | ||
TI_ASSERT(var1->width() == 1); | ||
TI_ASSERT(var2->width() == 1); | ||
auto get_snode_id = [](Stmt *s) { | ||
if (auto ptr = s->cast<GlobalPtrStmt>()) | ||
return ptr->snodes[0]->id; | ||
else if (auto get_child = s->cast<GetChStmt>()) | ||
return get_child->output_snode->id; | ||
else | ||
return -1; | ||
}; | ||
int snode1 = get_snode_id(var1); | ||
int snode2 = get_snode_id(var2); | ||
if (snode1 != -1 && snode2 != -1 && snode1 != snode2) | ||
return false; | ||
|
||
// GlobalPtrStmts with guaranteed different indices cannot share the same | ||
// address. | ||
if (var1->is<GlobalPtrStmt>() && var2->is<GlobalPtrStmt>()) { | ||
auto ptr1 = var1->as<GlobalPtrStmt>(); | ||
auto ptr2 = var2->as<GlobalPtrStmt>(); | ||
for (int i = 0; i < (int)ptr1->indices.size(); i++) { | ||
if (!irpass::analysis::same_statements(ptr1->indices[i], | ||
ptr2->indices[i])) { | ||
if (ptr1->indices[i]->is<ConstStmt>() && | ||
ptr2->indices[i]->is<ConstStmt>()) { | ||
// different constants | ||
return false; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm confused here: shouldn't we just return false here whenever two statements are not the same? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For here, however, we want to check if two statements are possible to be the same. For example, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now I understand. Thanks. Let's briefly document: return true when two statements might be the same address; false when two statements cannot be the same address. |
||
} | ||
} | ||
return true; | ||
Comment on lines
+64
to
+74
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's do that in a future PR. |
||
} | ||
|
||
// In other cases (probably after lower_access), we don't know if the two | ||
// statements share the same address. | ||
return true; | ||
} | ||
|
||
std::string VectorType::pointer_suffix() const { | ||
if (is_pointer()) { | ||
return "*"; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,7 @@ TLANG_NAMESPACE_BEGIN | |
|
||
std::unique_ptr<std::unordered_set<AtomicOpStmt *>> StateMachine::used_atomics; | ||
|
||
StateMachine::StateMachine(Stmt *var) | ||
StateMachine::StateMachine(Stmt *var, bool zero_initialized) | ||
: var(var), | ||
stored(never), | ||
stored_in_this_if_or_loop(never), | ||
|
@@ -16,8 +16,8 @@ StateMachine::StateMachine(Stmt *var) | |
last_atomic(nullptr), | ||
last_atomic_eliminable(false), | ||
maybe_loaded_before_first_definite_store_in_this_if_or_loop(false) { | ||
TI_ASSERT(var->is<AllocaStmt>() || var->is<GlobalTemporaryStmt>() || | ||
var->is<GlobalPtrStmt>()); | ||
if (!zero_initialized) | ||
stored = stored_in_this_if_or_loop = maybe; | ||
} | ||
|
||
bool StateMachine::same_data(Stmt *store_stmt1, Stmt *store_stmt2) { | ||
|
@@ -117,12 +117,18 @@ void StateMachine::store(Stmt *store_stmt) { | |
} | ||
|
||
void StateMachine::load(Stmt *load_stmt) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's briefly document the behavior when |
||
TI_ASSERT(load_stmt->is<LocalLoadStmt>() || load_stmt->is<GlobalLoadStmt>()); | ||
// The load_stmt == nullptr case is only for an offloaded range_for loading | ||
// global temps via begin_offset and end_offset. | ||
if (load_stmt) | ||
TI_ASSERT(load_stmt->is<LocalLoadStmt>() || | ||
load_stmt->is<GlobalLoadStmt>()); | ||
if (stored_in_this_if_or_loop != definitely) | ||
maybe_loaded_before_first_definite_store_in_this_if_or_loop = true; | ||
loaded = loaded_in_this_if_or_loop = definitely; | ||
last_store_eliminable = false; | ||
last_atomic_eliminable = false; | ||
if (!load_stmt) | ||
return; | ||
|
||
if (stored == never) { | ||
auto zero = load_stmt->insert_after_me(Stmt::make<ConstStmt>( | ||
|
@@ -423,4 +429,8 @@ void StateMachine::finalize() { | |
} | ||
} | ||
|
||
Stmt *StateMachine::get_var() const { | ||
return var; | ||
} | ||
|
||
TLANG_NAMESPACE_END |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -494,13 +494,30 @@ class BasicBlockSimplify : public IRVisitor { | |
// no store to the var? | ||
bool has_store = false; | ||
for (int j = i + 1; j < current_stmt_id; j++) { | ||
if (block->statements[j] | ||
->is_container_statement()) { // no if, while, etc.. | ||
has_store = true; | ||
break; | ||
if (!advanced_optimization) { | ||
if (block->statements[j] | ||
->is_container_statement()) { // no if, while, etc.. | ||
has_store = true; | ||
break; | ||
} | ||
if (block->statements[j]->is<GlobalStoreStmt>()) { | ||
has_store = true; | ||
} | ||
continue; | ||
} | ||
if (block->statements[j]->is<GlobalStoreStmt>()) { | ||
if (!irpass::analysis::gather_statements( | ||
block->statements[j].get(), | ||
[&](Stmt *s) { | ||
if (auto store = s->cast<GlobalStoreStmt>()) | ||
return maybe_same_address(store->ptr, stmt->ptr); | ||
else if (auto atomic = s->cast<AtomicOpStmt>()) | ||
return maybe_same_address(atomic->dest, stmt->ptr); | ||
else | ||
return false; | ||
}) | ||
.empty()) { | ||
has_store = true; | ||
break; | ||
} | ||
} | ||
if (!has_store) { | ||
|
@@ -1141,12 +1158,11 @@ void simplify(IRNode *root, Kernel *kernel) { | |
|
||
void full_simplify(IRNode *root, const CompileConfig &config, Kernel *kernel) { | ||
constant_fold(root); | ||
if (advanced_optimization) | ||
if (advanced_optimization) { | ||
alg_simp(root, config); | ||
if (advanced_optimization) | ||
die(root); | ||
whole_kernel_cse(root); | ||
if (advanced_optimization) | ||
variable_optimization(root); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interestingly, avoid doing some variable optimizations leads to final optimization on some tests. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, now you need something like https://ieeexplore.ieee.org/document/1611550 |
||
} | ||
die(root); | ||
simplify(root, kernel); | ||
die(root); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe_same_global_address
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I included the
Alloca
case (i.e. this function can check if two allocas are the same by directly checking ifvar1 == var2
) although it is not used in that way.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If both
Stmt*
are allocas, they have the same address iffvar1 == var2
.If only one of them is an alloca, they can never share the same address.
If both of them are global temps, they have the same address iff they have the same offset. (Is checking ret_type unnecessary?)
If only one of them is a global temp, they can never share the same address.
If both of them are global ptrs (or
GetChStmt
s), we can check bySNode::id
.In other cases (probably after
lower_access
), we don't know if the two statements share the same address.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A well-formed IR should not have cases with different ret_type but it's good to be safe here.
The logic here sounds good. Maybe we should copy-paste these as comments to the code :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just realized that
offset
is a sufficient condition of "might be the same address".There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. In case you are interested, there's the LLVM's design of alias analysis: https://llvm.org/docs/AliasAnalysis.html