Commit 0192d15d by Nicolas Capens Committed by Nicolas Capens

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/+/27928Tested-by: 's avatarNicolas Capens <nicolascapens@google.com> Presubmit-Ready: Nicolas Capens <nicolascapens@google.com> Reviewed-by: 's avatarBen Clayton <bclayton@google.com>
parent 5da8d8de
...@@ -1052,6 +1052,9 @@ namespace rr ...@@ -1052,6 +1052,9 @@ namespace rr
void Nucleus::setInsertBlock(BasicBlock *basicBlock) void Nucleus::setInsertBlock(BasicBlock *basicBlock)
{ {
// assert(::builder->GetInsertBlock()->back().isTerminator()); // assert(::builder->GetInsertBlock()->back().isTerminator());
Variable::materializeAll();
::builder->SetInsertPoint(B(basicBlock)); ::builder->SetInsertPoint(B(basicBlock));
} }
...@@ -1090,21 +1093,35 @@ namespace rr ...@@ -1090,21 +1093,35 @@ namespace rr
void Nucleus::createRetVoid() 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(); ::builder->CreateRetVoid();
} }
void Nucleus::createRet(Value *v) 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)); ::builder->CreateRet(V(v));
} }
void Nucleus::createBr(BasicBlock *dest) void Nucleus::createBr(BasicBlock *dest)
{ {
Variable::materializeAll();
::builder->CreateBr(B(dest)); ::builder->CreateBr(B(dest));
} }
void Nucleus::createCondBr(Value *cond, BasicBlock *ifTrue, BasicBlock *ifFalse) void Nucleus::createCondBr(Value *cond, BasicBlock *ifTrue, BasicBlock *ifFalse)
{ {
Variable::materializeAll();
::builder->CreateCondBr(V(cond), B(ifTrue), B(ifFalse)); ::builder->CreateCondBr(V(cond), B(ifTrue), B(ifFalse));
} }
......
...@@ -14,8 +14,46 @@ ...@@ -14,8 +14,46 @@
#include "Reactor.hpp" #include "Reactor.hpp"
// Define REACTOR_MATERIALIZE_LVALUES_ON_DEFINITION to non-zero to ensure all
// variables have a stack location obtained throuch alloca().
#ifndef REACTOR_MATERIALIZE_LVALUES_ON_DEFINITION
#define REACTOR_MATERIALIZE_LVALUES_ON_DEFINITION 0
#endif
namespace rr namespace rr
{ {
// Set of variables that do not have a stack location yet.
std::unordered_set<Variable*> Variable::unmaterializedVariables;
Variable::Variable(Type *type, int arraySize) : type(type), arraySize(arraySize)
{
#if REACTOR_MATERIALIZE_LVALUES_ON_DEFINITION
materialize();
#else
unmaterializedVariables.emplace(this);
#endif
}
Variable::~Variable()
{
unmaterializedVariables.erase(this);
}
void Variable::materializeAll()
{
for(auto *var : unmaterializedVariables)
{
var->materialize();
}
unmaterializedVariables.clear();
}
void Variable::killUnmaterialized()
{
unmaterializedVariables.clear();
}
static Value *createSwizzle4(Value *val, unsigned char select) static Value *createSwizzle4(Value *val, unsigned char select)
{ {
int swizzle[4] = int swizzle[4] =
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <string> #include <string>
#include <tuple> #include <tuple>
#include <unordered_set>
#undef Bool // b/127920555 #undef Bool // b/127920555
...@@ -81,16 +82,37 @@ namespace rr ...@@ -81,16 +82,37 @@ namespace rr
class Variable class Variable
{ {
friend class Nucleus;
friend class PrintValue; friend class PrintValue;
Variable() = delete;
Variable &operator=(const Variable&) = delete; Variable &operator=(const Variable&) = delete;
public: public:
Variable() = default; void materialize() const;
Variable(const Variable&) = default;
Value *loadValue() const;
Value *storeValue(Value *value) const;
Value *getBaseAddress() const;
Value *getElementPointer(Value *index, bool unsignedIndex) const;
protected: protected:
Value *address; Variable(Type *type, int arraySize);
Variable(const Variable&) = default;
~Variable();
private:
static void materializeAll();
static void killUnmaterialized();
static std::unordered_set<Variable*> unmaterializedVariables;
Type *const type;
const int arraySize;
mutable Value *rvalue = nullptr;
mutable Value *address = nullptr;
}; };
template<class T> template<class T>
...@@ -105,10 +127,6 @@ namespace rr ...@@ -105,10 +127,6 @@ namespace rr
{ {
return false; return false;
} }
Value *loadValue() const;
Value *storeValue(Value *value) const;
Value *getAddress(Value *index, bool unsignedIndex) const;
}; };
template<class T> template<class T>
...@@ -2320,33 +2338,68 @@ namespace rr ...@@ -2320,33 +2338,68 @@ namespace rr
namespace rr namespace rr
{ {
template<class T> template<class T>
LValue<T>::LValue(int arraySize) LValue<T>::LValue(int arraySize) : Variable(T::getType(), arraySize)
{ {
address = Nucleus::allocateStackVariable(T::getType(), arraySize);
} }
template<class T> inline void Variable::materialize() const
Value *LValue<T>::loadValue() const {
if(!address)
{
address = Nucleus::allocateStackVariable(type, arraySize);
if(rvalue)
{
storeValue(rvalue);
rvalue = nullptr;
}
}
}
inline Value *Variable::loadValue() const
{
if(rvalue)
{ {
return Nucleus::createLoad(address, T::getType(), false, 0); return rvalue;
} }
template<class T> if(!address)
Value *LValue<T>::storeValue(Value *value) const
{ {
return Nucleus::createStore(value, address, T::getType(), false, 0); // TODO: Return undef instead.
materialize();
} }
template<class T> return Nucleus::createLoad(address, type, false, 0);
Value *LValue<T>::getAddress(Value *index, bool unsignedIndex) const }
inline Value *Variable::storeValue(Value *value) const
{
if(address)
{
return Nucleus::createStore(value, address, type, false, 0);
}
rvalue = value;
return value;
}
inline Value *Variable::getBaseAddress() const
{
materialize();
return address;
}
inline Value *Variable::getElementPointer(Value *index, bool unsignedIndex) const
{ {
return Nucleus::createGEP(address, T::getType(), index, unsignedIndex); return Nucleus::createGEP(getBaseAddress(), type, index, unsignedIndex);
} }
template<class T> template<class T>
RValue<Pointer<T>> LValue<T>::operator&() RValue<Pointer<T>> LValue<T>::operator&()
{ {
return RValue<Pointer<T>>(address); return RValue<Pointer<T>>(getBaseAddress());
} }
template<class T> template<class T>
...@@ -2680,7 +2733,7 @@ namespace rr ...@@ -2680,7 +2733,7 @@ namespace rr
template<class T, int S> template<class T, int S>
Reference<T> Array<T, S>::operator[](int index) Reference<T> Array<T, S>::operator[](int index)
{ {
Value *element = LValue<T>::getAddress(Nucleus::createConstantInt(index), false); Value *element = LValue<T>::getElementPointer(Nucleus::createConstantInt(index), false);
return Reference<T>(element); return Reference<T>(element);
} }
...@@ -2688,7 +2741,7 @@ namespace rr ...@@ -2688,7 +2741,7 @@ namespace rr
template<class T, int S> template<class T, int S>
Reference<T> Array<T, S>::operator[](unsigned int index) Reference<T> Array<T, S>::operator[](unsigned int index)
{ {
Value *element = LValue<T>::getAddress(Nucleus::createConstantInt(index), true); Value *element = LValue<T>::getElementPointer(Nucleus::createConstantInt(index), true);
return Reference<T>(element); return Reference<T>(element);
} }
...@@ -2696,7 +2749,7 @@ namespace rr ...@@ -2696,7 +2749,7 @@ namespace rr
template<class T, int S> template<class T, int S>
Reference<T> Array<T, S>::operator[](RValue<Int> index) Reference<T> Array<T, S>::operator[](RValue<Int> index)
{ {
Value *element = LValue<T>::getAddress(index.value, false); Value *element = LValue<T>::getElementPointer(index.value, false);
return Reference<T>(element); return Reference<T>(element);
} }
...@@ -2704,7 +2757,7 @@ namespace rr ...@@ -2704,7 +2757,7 @@ namespace rr
template<class T, int S> template<class T, int S>
Reference<T> Array<T, S>::operator[](RValue<UInt> index) Reference<T> Array<T, S>::operator[](RValue<UInt> index)
{ {
Value *element = LValue<T>::getAddress(index.value, true); Value *element = LValue<T>::getElementPointer(index.value, true);
return Reference<T>(element); return Reference<T>(element);
} }
......
...@@ -112,6 +112,65 @@ TEST(ReactorUnitTests, Uninitialized) ...@@ -112,6 +112,65 @@ TEST(ReactorUnitTests, Uninitialized)
delete routine; delete routine;
} }
TEST(ReactorUnitTests, Unreachable)
{
Routine *routine = nullptr;
{
Function<Int(Int)> function;
{
Int a = function.Arg<0>();
Int z = 4;
Return(a + z);
// Code beyond this point is unreachable but should not cause any
// compilation issues.
z += a;
}
routine = function("one");
if(routine)
{
int (*callable)(int) = (int(*)(int))routine->getEntry();
int result = callable(16);
EXPECT_EQ(result, 20);
}
}
delete routine;
}
TEST(ReactorUnitTests, VariableAddress)
{
Routine *routine = nullptr;
{
Function<Int(Int)> function;
{
Int a = function.Arg<0>();
Int z = 0;
Pointer<Int> p = &z;
*p = 4;
Return(a + z);
}
routine = function("one");
if(routine)
{
int (*callable)(int) = (int(*)(int))routine->getEntry();
int result = callable(16);
EXPECT_EQ(result, 20);
}
}
delete routine;
}
TEST(ReactorUnitTests, SubVectorLoadStore) TEST(ReactorUnitTests, SubVectorLoadStore)
{ {
Routine *routine = nullptr; Routine *routine = nullptr;
......
...@@ -649,6 +649,9 @@ namespace rr ...@@ -649,6 +649,9 @@ namespace rr
void Nucleus::setInsertBlock(BasicBlock *basicBlock) void Nucleus::setInsertBlock(BasicBlock *basicBlock)
{ {
// assert(::basicBlock->getInsts().back().getTerminatorEdges().size() >= 0 && "Previous basic block must have a terminator"); // assert(::basicBlock->getInsts().back().getTerminatorEdges().size() >= 0 && "Previous basic block must have a terminator");
Variable::materializeAll();
::basicBlock = basicBlock; ::basicBlock = basicBlock;
} }
...@@ -676,24 +679,38 @@ namespace rr ...@@ -676,24 +679,38 @@ namespace rr
void Nucleus::createRetVoid() 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();
Ice::InstRet *ret = Ice::InstRet::create(::function); Ice::InstRet *ret = Ice::InstRet::create(::function);
::basicBlock->appendInst(ret); ::basicBlock->appendInst(ret);
} }
void Nucleus::createRet(Value *v) 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();
Ice::InstRet *ret = Ice::InstRet::create(::function, v); Ice::InstRet *ret = Ice::InstRet::create(::function, v);
::basicBlock->appendInst(ret); ::basicBlock->appendInst(ret);
} }
void Nucleus::createBr(BasicBlock *dest) void Nucleus::createBr(BasicBlock *dest)
{ {
Variable::materializeAll();
auto br = Ice::InstBr::create(::function, dest); auto br = Ice::InstBr::create(::function, dest);
::basicBlock->appendInst(br); ::basicBlock->appendInst(br);
} }
void Nucleus::createCondBr(Value *cond, BasicBlock *ifTrue, BasicBlock *ifFalse) void Nucleus::createCondBr(Value *cond, BasicBlock *ifTrue, BasicBlock *ifFalse)
{ {
Variable::materializeAll();
auto br = Ice::InstBr::create(::function, cond, ifTrue, ifFalse); auto br = Ice::InstBr::create(::function, cond, ifTrue, ifFalse);
::basicBlock->appendInst(br); ::basicBlock->appendInst(br);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment