Elide single basic block variable materialization
Variables which are only used within a single basic block and don't
have their address taken, don't require allocating stack memory.
Instead they can track the latest rvalue that has been assigned to them.
Allocating stack memory is necessary for variables that are assigned to
in divergent basic blocks since there is no single rvalue to track after
the merge point (unless we inserted phi instructions, but that is
outside of the scope of this change).
Because Reactor can't look forward to check whether a variable will be
used in divergent basic blocks, we keep the set of variables that may
not have been materialized yet, meaning they have no stack address yet.
When a branch is encountered, they are all materialized.
Variables not yet materialized at a Return() are 'killed' to prevent
materializing afterwards, which would cause the terminator ret
instruction to not be the last instruction of the basic block.
Note that this change creates a dependency of the Nucleus
implementation on Variable, which is currently defined in a higher
layer in Reactor.hpp. Since Variable doesn't depend on anything else
in Reactor.hpp it could be made part of Nucleus, or an in-between
layer could be added.
Bug b/129356087
Change-Id: Ie8c09e34c8befb9787a5c0bca7a20e770bcbf8d3
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/27928
Tested-by: Nicolas Capens <nicolascapens@google.com>
Presubmit-Ready: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/Reactor/LLVMReactor.cpp b/src/Reactor/LLVMReactor.cpp
index 624af83..55ca049 100644
--- a/src/Reactor/LLVMReactor.cpp
+++ b/src/Reactor/LLVMReactor.cpp
@@ -1052,6 +1052,9 @@
void Nucleus::setInsertBlock(BasicBlock *basicBlock)
{
// assert(::builder->GetInsertBlock()->back().isTerminator());
+
+ Variable::materializeAll();
+
::builder->SetInsertPoint(B(basicBlock));
}
@@ -1090,21 +1093,35 @@
void Nucleus::createRetVoid()
{
+ // Code generated after this point is unreachable, so any variables
+ // being read can safely return an undefined value. We have to avoid
+ // materializing variables after the terminator ret instruction.
+ Variable::killUnmaterialized();
+
::builder->CreateRetVoid();
}
void Nucleus::createRet(Value *v)
{
+ // Code generated after this point is unreachable, so any variables
+ // being read can safely return an undefined value. We have to avoid
+ // materializing variables after the terminator ret instruction.
+ Variable::killUnmaterialized();
+
::builder->CreateRet(V(v));
}
void Nucleus::createBr(BasicBlock *dest)
{
+ Variable::materializeAll();
+
::builder->CreateBr(B(dest));
}
void Nucleus::createCondBr(Value *cond, BasicBlock *ifTrue, BasicBlock *ifFalse)
{
+ Variable::materializeAll();
+
::builder->CreateCondBr(V(cond), B(ifTrue), B(ifFalse));
}