Commit 54313fbf by Nicolas Capens Committed by Nicolas Capens

Report back the Subzero optimizer results

This change adds a callback mechanism to report how many instructions of certain types are left after the optimization passes have been run. This enables unit tests to check that the desired optimization actually took place, instead of just checking correct execution results. Bug: b/180665600 Change-Id: I3916d327138516a0a0778be2b3fdd5b000fc9bdb Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/52989 Presubmit-Ready: Nicolas Capens <nicolascapens@google.com> Tested-by: 's avatarNicolas Capens <nicolascapens@google.com> Reviewed-by: 's avatarAntonio Maiorano <amaiorano@google.com>
parent bc1a66c7
...@@ -1781,6 +1781,12 @@ Value *Nucleus::createConstantString(const char *v) ...@@ -1781,6 +1781,12 @@ Value *Nucleus::createConstantString(const char *v)
return V(ptr); return V(ptr);
} }
void Nucleus::setOptimizerCallback(OptimizerCallback *callback)
{
// The LLVM backend does not produce optimizer reports.
(void)callback;
}
Type *Void::type() Type *Void::type()
{ {
return T(llvm::Type::getVoidTy(*jit->context)); return T(llvm::Type::getVoidTy(*jit->context));
......
...@@ -333,6 +333,20 @@ public: ...@@ -333,6 +333,20 @@ public:
static Type *getContainedType(Type *vectorType); static Type *getContainedType(Type *vectorType);
static Type *getPointerType(Type *elementType); static Type *getPointerType(Type *elementType);
static Type *getPrintfStorageType(Type *valueType); static Type *getPrintfStorageType(Type *valueType);
// Diagnostic utilities
struct OptimizerReport
{
int allocas = 0;
int loads = 0;
int stores = 0;
};
using OptimizerCallback = void(const OptimizerReport *report);
// Sets the callback to be used by the next optimizer invocation (during acquireRoutine),
// for reporting stats about the resulting IR code. For testing only.
static void setOptimizerCallback(OptimizerCallback *callback);
}; };
} // namespace rr } // namespace rr
......
...@@ -24,6 +24,11 @@ namespace { ...@@ -24,6 +24,11 @@ namespace {
class Optimizer class Optimizer
{ {
public: public:
Optimizer(rr::Nucleus::OptimizerReport *report)
: report(report)
{
}
void run(Ice::Cfg *function); void run(Ice::Cfg *function);
private: private:
...@@ -46,6 +51,8 @@ private: ...@@ -46,6 +51,8 @@ private:
static std::size_t storeSize(const Ice::Inst *instruction); static std::size_t storeSize(const Ice::Inst *instruction);
static bool loadTypeMatchesStore(const Ice::Inst *load, const Ice::Inst *store); static bool loadTypeMatchesStore(const Ice::Inst *load, const Ice::Inst *store);
void collectDiagnostics();
Ice::Cfg *function; Ice::Cfg *function;
Ice::GlobalContext *context; Ice::GlobalContext *context;
...@@ -88,6 +95,8 @@ private: ...@@ -88,6 +95,8 @@ private:
bool hasLoadStoreInsts(Ice::CfgNode *node) const; bool hasLoadStoreInsts(Ice::CfgNode *node) const;
std::vector<Ice::Operand *> operandsWithUses; std::vector<Ice::Operand *> operandsWithUses;
rr::Nucleus::OptimizerReport *report = nullptr;
}; };
void Optimizer::run(Ice::Cfg *function) void Optimizer::run(Ice::Cfg *function)
...@@ -115,6 +124,8 @@ void Optimizer::run(Ice::Cfg *function) ...@@ -115,6 +124,8 @@ void Optimizer::run(Ice::Cfg *function)
setUses(operand, nullptr); setUses(operand, nullptr);
} }
operandsWithUses.clear(); operandsWithUses.clear();
collectDiagnostics();
} }
// Eliminates allocas which store the address of other allocas. // Eliminates allocas which store the address of other allocas.
...@@ -719,6 +730,38 @@ bool Optimizer::loadTypeMatchesStore(const Ice::Inst *load, const Ice::Inst *sto ...@@ -719,6 +730,38 @@ bool Optimizer::loadTypeMatchesStore(const Ice::Inst *load, const Ice::Inst *sto
return true; return true;
} }
void Optimizer::collectDiagnostics()
{
if(report)
{
*report = {};
for(auto *basicBlock : function->getNodes())
{
for(auto &inst : basicBlock->getInsts())
{
if(inst.isDeleted())
{
continue;
}
if(llvm::isa<Ice::InstAlloca>(inst))
{
report->allocas++;
}
else if(isLoad(inst))
{
report->loads++;
}
else if(isStore(inst))
{
report->stores++;
}
}
}
}
}
Optimizer::Uses *Optimizer::getUses(Ice::Operand *operand) Optimizer::Uses *Optimizer::getUses(Ice::Operand *operand)
{ {
Optimizer::Uses *uses = (Optimizer::Uses *)operand->Ice::Operand::getExternalData(); Optimizer::Uses *uses = (Optimizer::Uses *)operand->Ice::Operand::getExternalData();
...@@ -846,9 +889,9 @@ void Optimizer::Uses::erase(Ice::Inst *instruction) ...@@ -846,9 +889,9 @@ void Optimizer::Uses::erase(Ice::Inst *instruction)
namespace rr { namespace rr {
void optimize(Ice::Cfg *function) void optimize(Ice::Cfg *function, Nucleus::OptimizerReport *report)
{ {
Optimizer optimizer; Optimizer optimizer(report);
optimizer.run(function); optimizer.run(function);
} }
......
...@@ -15,11 +15,13 @@ ...@@ -15,11 +15,13 @@
#ifndef rr_Optimizer_hpp #ifndef rr_Optimizer_hpp
#define rr_Optimizer_hpp #define rr_Optimizer_hpp
#include "Nucleus.hpp"
#include "src/IceCfg.h" #include "src/IceCfg.h"
namespace rr { namespace rr {
void optimize(Ice::Cfg *function); void optimize(Ice::Cfg *function, Nucleus::OptimizerReport *report = nullptr);
} // namespace rr } // namespace rr
......
...@@ -241,6 +241,9 @@ marl::Scheduler &getOrCreateScheduler() ...@@ -241,6 +241,9 @@ marl::Scheduler &getOrCreateScheduler()
return *scheduler; return *scheduler;
} }
rr::Nucleus::OptimizerCallback *optimizerCallback = nullptr;
} // Anonymous namespace } // Anonymous namespace
namespace { namespace {
...@@ -1008,7 +1011,17 @@ static std::shared_ptr<Routine> acquireRoutine(Ice::Cfg *const (&functions)[Coun ...@@ -1008,7 +1011,17 @@ static std::shared_ptr<Routine> acquireRoutine(Ice::Cfg *const (&functions)[Coun
currFunc->setFunctionName(Ice::GlobalString::createWithString(::context, names[i])); currFunc->setFunctionName(Ice::GlobalString::createWithString(::context, names[i]));
rr::optimize(currFunc); if(::optimizerCallback)
{
Nucleus::OptimizerReport report;
rr::optimize(currFunc, &report);
::optimizerCallback(&report);
::optimizerCallback = nullptr;
}
else
{
rr::optimize(currFunc);
}
currFunc->computeInOutEdges(); currFunc->computeInOutEdges();
ASSERT_MSG(!currFunc->hasError(), "%s", currFunc->getError().c_str()); ASSERT_MSG(!currFunc->hasError(), "%s", currFunc->getError().c_str());
...@@ -2186,6 +2199,11 @@ Value *Nucleus::createConstantString(const char *v) ...@@ -2186,6 +2199,11 @@ Value *Nucleus::createConstantString(const char *v)
return V(IceConstantData(v, strlen(v) + 1)); return V(IceConstantData(v, strlen(v) + 1));
} }
void Nucleus::setOptimizerCallback(OptimizerCallback *callback)
{
::optimizerCallback = callback;
}
Type *Void::type() Type *Void::type()
{ {
return T(Ice::IceType_void); return T(Ice::IceType_void);
......
...@@ -395,7 +395,6 @@ TEST(ReactorUnitTests, ConstantPointer) ...@@ -395,7 +395,6 @@ TEST(ReactorUnitTests, ConstantPointer)
// This test excercises the Optimizer::eliminateLoadsFollowingSingleStore() optimization pass. // This test excercises the Optimizer::eliminateLoadsFollowingSingleStore() optimization pass.
// The three load operations for `y` should get eliminated. // The three load operations for `y` should get eliminated.
// TODO(b/180665600): Check that the optimization took place.
TEST(ReactorUnitTests, EliminateLoadsFollowingSingleStore) TEST(ReactorUnitTests, EliminateLoadsFollowingSingleStore)
{ {
FunctionT<int(int)> function; FunctionT<int(int)> function;
...@@ -415,6 +414,12 @@ TEST(ReactorUnitTests, EliminateLoadsFollowingSingleStore) ...@@ -415,6 +414,12 @@ TEST(ReactorUnitTests, EliminateLoadsFollowingSingleStore)
Return(z); Return(z);
} }
Nucleus::setOptimizerCallback([](const Nucleus::OptimizerReport *report) {
EXPECT_EQ(report->allocas, 2);
EXPECT_EQ(report->loads, 2);
EXPECT_EQ(report->stores, 2);
});
auto routine = function(testName().c_str()); auto routine = function(testName().c_str());
int result = routine(11); int result = routine(11);
...@@ -423,7 +428,6 @@ TEST(ReactorUnitTests, EliminateLoadsFollowingSingleStore) ...@@ -423,7 +428,6 @@ TEST(ReactorUnitTests, EliminateLoadsFollowingSingleStore)
// This test excercises the Optimizer::propagateAlloca() optimization pass. // This test excercises the Optimizer::propagateAlloca() optimization pass.
// The pointer variable should not get stored to / loaded from memory. // The pointer variable should not get stored to / loaded from memory.
// TODO(b/180665600): Check that the optimization took place.
TEST(ReactorUnitTests, PropagateAlloca) TEST(ReactorUnitTests, PropagateAlloca)
{ {
FunctionT<int(int)> function; FunctionT<int(int)> function;
...@@ -443,6 +447,12 @@ TEST(ReactorUnitTests, PropagateAlloca) ...@@ -443,6 +447,12 @@ TEST(ReactorUnitTests, PropagateAlloca)
Return(Int(*p)); // TODO(b/179694472): Support Return(*p) Return(Int(*p)); // TODO(b/179694472): Support Return(*p)
} }
Nucleus::setOptimizerCallback([](const Nucleus::OptimizerReport *report) {
EXPECT_EQ(report->allocas, 1);
EXPECT_EQ(report->loads, 1);
EXPECT_EQ(report->stores, 1);
});
auto routine = function(testName().c_str()); auto routine = function(testName().c_str());
int result = routine(true); int result = routine(true);
......
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