introduce loop info

This commit is contained in:
Asuka 2022-05-27 03:01:26 +08:00
parent 25498bc283
commit 5a4b08868f
19 changed files with 1054 additions and 143 deletions

View file

@ -456,7 +456,7 @@ BOOST_concept(VertexIndexGraph, (Graph))
ignore_unused_variable_warning(x);
// This is relaxed
renumber_vertex_indices(g);
// renumber_vertex_indices(g);
const_constraints(g);
}

View file

@ -0,0 +1,372 @@
// (C) Copyright 2007-2009 Andrew Sutton
//
// Use, modification and distribution are subject to the
// Boost Software License, Version 1.0 (See accompanying file
// LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_GRAPH_CYCLE_HPP
#define BOOST_GRAPH_CYCLE_HPP
#include <vector>
#include <boost/config.hpp>
#include <boost/graph/graph_concepts.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/properties.hpp>
#include <boost/concept/assert.hpp>
#include <boost/concept/detail/concept_def.hpp>
namespace boost
{
namespace concepts
{
BOOST_concept(CycleVisitor, (Visitor)(Path)(Graph))
{
BOOST_CONCEPT_USAGE(CycleVisitor) { vis.cycle(p, g); }
private:
Visitor vis;
Graph g;
Path p;
};
} /* namespace concepts */
using concepts::CycleVisitorConcept;
} /* namespace boost */
#include <boost/concept/detail/concept_undef.hpp>
namespace boost
{
// The implementation of this algorithm is a reproduction of the Teirnan
// approach for directed graphs: bibtex follows
//
// @article{362819,
// author = {James C. Tiernan},
// title = {An efficient search algorithm to find the elementary
// circuits of a graph}, journal = {Commun. ACM}, volume = {13}, number
// = {12}, year = {1970}, issn = {0001-0782}, pages = {722--726}, doi =
// {http://doi.acm.org/10.1145/362814.362819},
// publisher = {ACM Press},
// address = {New York, NY, USA},
// }
//
// It should be pointed out that the author does not provide a complete analysis
// for either time or space. This is in part, due to the fact that it's a fairly
// input sensitive problem related to the density and construction of the graph,
// not just its size.
//
// I've also taken some liberties with the interpretation of the algorithm -
// I've basically modernized it to use real data structures (no more arrays and
// matrices). Oh... and there's explicit control structures - not just gotos.
//
// The problem is definitely NP-complete, an unbounded implementation of this
// will probably run for quite a while on a large graph. The conclusions
// of this paper also reference a Paton algorithm for undirected graphs as being
// much more efficient (apparently based on spanning trees). Although not
// implemented, it can be found here:
//
// @article{363232,
// author = {Keith Paton},
// title = {An algorithm for finding a fundamental set of cycles of a
// graph}, journal = {Commun. ACM}, volume = {12}, number = {9}, year =
// {1969}, issn = {0001-0782}, pages = {514--518}, doi =
// {http://doi.acm.org/10.1145/363219.363232},
// publisher = {ACM Press},
// address = {New York, NY, USA},
// }
/**
* The default cycle visitor provides an empty visit function for cycle
* visitors.
*/
struct cycle_visitor
{
template < typename Path, typename Graph >
inline void cycle(const Path& p, const Graph& g)
{
}
};
/**
* The min_max_cycle_visitor simultaneously records the minimum and maximum
* cycles in a graph.
*/
struct min_max_cycle_visitor
{
min_max_cycle_visitor(std::size_t& min_, std::size_t& max_)
: minimum(min_), maximum(max_)
{
}
template < typename Path, typename Graph >
inline void cycle(const Path& p, const Graph& g)
{
BOOST_USING_STD_MIN();
BOOST_USING_STD_MAX();
std::size_t len = p.size();
minimum = min BOOST_PREVENT_MACRO_SUBSTITUTION(minimum, len);
maximum = max BOOST_PREVENT_MACRO_SUBSTITUTION(maximum, len);
}
std::size_t& minimum;
std::size_t& maximum;
};
inline min_max_cycle_visitor find_min_max_cycle(
std::size_t& min_, std::size_t& max_)
{
return min_max_cycle_visitor(min_, max_);
}
namespace detail
{
template < typename Graph, typename Path >
inline bool is_vertex_in_path(const Graph&,
typename graph_traits< Graph >::vertex_descriptor v, const Path& p)
{
return (std::find(p.begin(), p.end(), v) != p.end());
}
template < typename Graph, typename ClosedMatrix >
inline bool is_path_closed(const Graph& g,
typename graph_traits< Graph >::vertex_descriptor u,
typename graph_traits< Graph >::vertex_descriptor v,
const ClosedMatrix& closed)
{
// the path from u to v is closed if v can be found in the list
// of closed vertices associated with u.
typedef typename ClosedMatrix::const_reference Row;
Row r = closed[get(vertex_index, g, u)];
if (find(r.begin(), r.end(), v) != r.end())
{
return true;
}
return false;
}
template < typename Graph, typename Path, typename ClosedMatrix >
inline bool can_extend_path(const Graph& g,
typename graph_traits< Graph >::edge_descriptor e, const Path& p,
const ClosedMatrix& m)
{
BOOST_CONCEPT_ASSERT((IncidenceGraphConcept< Graph >));
BOOST_CONCEPT_ASSERT((VertexIndexGraphConcept< Graph >));
typedef typename graph_traits< Graph >::vertex_descriptor Vertex;
// get the vertices in question
Vertex u = source(e, g), v = target(e, g);
// conditions for allowing a traversal along this edge are:
// 1. the index of v must be greater than that at which the
// path is rooted (p.front()).
// 2. the vertex v cannot already be in the path
// 3. the vertex v cannot be closed to the vertex u
bool indices
= get(vertex_index, g, p.front()) < get(vertex_index, g, v);
bool path = !is_vertex_in_path(g, v, p);
bool closed = !is_path_closed(g, u, v, m);
return indices && path && closed;
}
template < typename Graph, typename Path >
inline bool can_wrap_path(const Graph& g, const Path& p)
{
BOOST_CONCEPT_ASSERT((IncidenceGraphConcept< Graph >));
typedef typename graph_traits< Graph >::vertex_descriptor Vertex;
typedef typename graph_traits< Graph >::out_edge_iterator OutIterator;
// iterate over the out-edges of the back, looking for the
// front of the path. also, we can't travel along the same
// edge that we did on the way here, but we don't quite have the
// stringent requirements that we do in can_extend_path().
Vertex u = p.back(), v = p.front();
OutIterator i, end;
for (boost::tie(i, end) = out_edges(u, g); i != end; ++i)
{
if ((target(*i, g) == v))
{
return true;
}
}
return false;
}
template < typename Graph, typename Path, typename ClosedMatrix >
inline typename graph_traits< Graph >::vertex_descriptor extend_path(
const Graph& g, Path& p, ClosedMatrix& closed)
{
BOOST_CONCEPT_ASSERT((IncidenceGraphConcept< Graph >));
typedef typename graph_traits< Graph >::vertex_descriptor Vertex;
typedef typename graph_traits< Graph >::out_edge_iterator OutIterator;
// get the current vertex
Vertex u = p.back();
Vertex ret = graph_traits< Graph >::null_vertex();
// AdjacencyIterator i, end;
OutIterator i, end;
for (boost::tie(i, end) = out_edges(u, g); i != end; ++i)
{
Vertex v = target(*i, g);
// if we can actually extend along this edge,
// then that's what we want to do
if (can_extend_path(g, *i, p, closed))
{
p.push_back(v); // add the vertex to the path
ret = v;
break;
}
}
return ret;
}
template < typename Graph, typename Path, typename ClosedMatrix >
inline bool exhaust_paths(const Graph& g, Path& p, ClosedMatrix& closed)
{
BOOST_CONCEPT_ASSERT((GraphConcept< Graph >));
typedef typename graph_traits< Graph >::vertex_descriptor Vertex;
// if there's more than one vertex in the path, this closes
// of some possible routes and returns true. otherwise, if there's
// only one vertex left, the vertex has been used up
if (p.size() > 1)
{
// get the last and second to last vertices, popping the last
// vertex off the path
Vertex last, prev;
last = p.back();
p.pop_back();
prev = p.back();
// reset the closure for the last vertex of the path and
// indicate that the last vertex in p is now closed to
// the next-to-last vertex in p
closed[get(vertex_index, g, last)].clear();
closed[get(vertex_index, g, prev)].push_back(last);
return true;
}
else
{
return false;
}
}
template < typename Graph, typename Visitor >
inline void all_cycles_from_vertex(const Graph& g,
typename graph_traits< Graph >::vertex_descriptor v, Visitor vis,
std::size_t minlen, std::size_t maxlen)
{
BOOST_CONCEPT_ASSERT((VertexListGraphConcept< Graph >));
typedef typename graph_traits< Graph >::vertex_descriptor Vertex;
typedef std::vector< Vertex > Path;
BOOST_CONCEPT_ASSERT((CycleVisitorConcept< Visitor, Path, Graph >));
typedef std::vector< Vertex > VertexList;
typedef std::vector< VertexList > ClosedMatrix;
Path p;
ClosedMatrix closed(num_vertices(g), VertexList());
Vertex null = graph_traits< Graph >::null_vertex();
// each path investigation starts at the ith vertex
p.push_back(v);
while (1)
{
// extend the path until we've reached the end or the
// maxlen-sized cycle
Vertex j = null;
while (((j = detail::extend_path(g, p, closed)) != null)
&& (p.size() < maxlen))
; // empty loop
// if we're done extending the path and there's an edge
// connecting the back to the front, then we should have
// a cycle.
if (detail::can_wrap_path(g, p) && p.size() >= minlen)
{
vis.cycle(p, g);
}
if (!detail::exhaust_paths(g, p, closed))
{
break;
}
}
}
// Select the minimum allowable length of a cycle based on the directedness
// of the graph - 2 for directed, 3 for undirected.
template < typename D > struct min_cycles
{
enum
{
value = 2
};
};
template <> struct min_cycles< undirected_tag >
{
enum
{
value = 3
};
};
} /* namespace detail */
template < typename Graph, typename Visitor >
inline void tiernan_all_cycles(
const Graph& g, Visitor vis, std::size_t minlen, std::size_t maxlen)
{
BOOST_CONCEPT_ASSERT((VertexListGraphConcept< Graph >));
typedef typename graph_traits< Graph >::vertex_iterator VertexIterator;
VertexIterator i, end;
for (boost::tie(i, end) = vertices(g); i != end; ++i)
{
detail::all_cycles_from_vertex(g, *i, vis, minlen, maxlen);
}
}
template < typename Graph, typename Visitor >
inline void tiernan_all_cycles(const Graph& g, Visitor vis, std::size_t maxlen)
{
typedef typename graph_traits< Graph >::directed_category Dir;
tiernan_all_cycles(g, vis, detail::min_cycles< Dir >::value, maxlen);
}
template < typename Graph, typename Visitor >
inline void tiernan_all_cycles(const Graph& g, Visitor vis)
{
typedef typename graph_traits< Graph >::directed_category Dir;
tiernan_all_cycles(g, vis, detail::min_cycles< Dir >::value,
(std::numeric_limits< std::size_t >::max)());
}
template < typename Graph >
inline std::pair< std::size_t, std::size_t > tiernan_girth_and_circumference(
const Graph& g)
{
std::size_t min_ = (std::numeric_limits< std::size_t >::max)(), max_ = 0;
tiernan_all_cycles(g, find_min_max_cycle(min_, max_));
// if this is the case, the graph is acyclic...
if (max_ == 0)
max_ = min_;
return std::make_pair(min_, max_);
}
template < typename Graph > inline std::size_t tiernan_girth(const Graph& g)
{
return tiernan_girth_and_circumference(g).first;
}
template < typename Graph >
inline std::size_t tiernan_circumference(const Graph& g)
{
return tiernan_girth_and_circumference(g).second;
}
} /* namespace boost */
#endif

View file

@ -63,7 +63,7 @@
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<LanguageStandard>stdcpp17</LanguageStandard>
<LanguageStandard>stdcpp20</LanguageStandard>
<LanguageStandard_C>stdc11</LanguageStandard_C>
<AdditionalOptions>-Wno-microsoft-include %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@ -93,7 +93,7 @@
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<LanguageStandard>stdcpp17</LanguageStandard>
<LanguageStandard>stdcpp20</LanguageStandard>
<LanguageStandard_C>stdc11</LanguageStandard_C>
<AdditionalOptions>-Wno-microsoft-include %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>

View file

@ -60,11 +60,12 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<LanguageStandard>stdcpp20</LanguageStandard>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<BufferSecurityCheck>false</BufferSecurityCheck>
<AdditionalOptions>-Wno-deprecated-declarations -Wno-ignored-pragmas %(AdditionalOptions)</AdditionalOptions>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<LanguageStandard_C>Default</LanguageStandard_C>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@ -79,11 +80,12 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<LanguageStandard>stdcpp20</LanguageStandard>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<BufferSecurityCheck>false</BufferSecurityCheck>
<AdditionalOptions>-Wno-deprecated-declarations -Wno-ignored-pragmas %(AdditionalOptions)</AdditionalOptions>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<LanguageStandard_C>Default</LanguageStandard_C>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>

View file

@ -13,9 +13,9 @@
// clang 12.0.0:
// error : invalid token at start of a preprocessor expression
//#if (not +0) && !defined(XBYAK_NO_OP_NAMES) // trick to detect whether 'not' is operator or not
// #define XBYAK_NO_OP_NAMES
//#endif
#if (not +0) && !defined(XBYAK_NO_OP_NAMES) // trick to detect whether 'not' is operator or not
#define XBYAK_NO_OP_NAMES
#endif
#include <stdio.h> // for debug print
#include <assert.h>

View file

@ -36,6 +36,7 @@
<ClInclude Include="Graphics\Gcn\GcnInstruction.h" />
<ClInclude Include="Graphics\Gcn\GcnInstructionIterator.h" />
<ClInclude Include="Graphics\Gcn\GcnInstructionUtil.h" />
<ClInclude Include="Graphics\Gcn\GcnLoopInfo.h" />
<ClInclude Include="Graphics\Gcn\GcnModInfo.h" />
<ClInclude Include="Graphics\Gcn\GcnShaderMeta.h" />
<ClInclude Include="Graphics\Gcn\GcnModule.h" />
@ -316,6 +317,7 @@
<ClCompile Include="Graphics\Gcn\GcnInstruction.cpp" />
<ClCompile Include="Graphics\Gcn\GcnInstructionIterator.cpp" />
<ClCompile Include="Graphics\Gcn\GcnInstructionUtil.cpp" />
<ClCompile Include="Graphics\Gcn\GcnLoopInfo.cpp" />
<ClCompile Include="Graphics\Gcn\GcnModule.cpp" />
<ClCompile Include="Graphics\Gcn\GcnProgramInfo.cpp" />
<ClCompile Include="Graphics\Gcn\GcnStackifier.cpp" />
@ -594,13 +596,13 @@
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<IncludePath>$(ProjectDir);$(ProjectDir)Emulator;$(ProjectDir)Algorithm;$(ProjectDir)Common;$(ProjectDir)Platform;$(ProjectDir)Util;$(ProjectDir)Graphics;$(ProjectDir)SceModules;$(SolutionDir)3rdParty;$(SolutionDir)3rdParty\zydis\include;$(SolutionDir)3rdParty\zydis\dependencies\zycore\include;$(SolutionDir)3rdParty\boost;$(IncludePath)</IncludePath>
<IncludePath>$(ProjectDir);$(ProjectDir)Emulator;$(ProjectDir)Algorithm;$(ProjectDir)Common;$(ProjectDir)Platform;$(ProjectDir)Util;$(ProjectDir)Util\Object;$(ProjectDir)Graphics;$(ProjectDir)SceModules;$(SolutionDir)3rdParty;$(SolutionDir)3rdParty\zydis\include;$(SolutionDir)3rdParty\zydis\dependencies\zycore\include;$(SolutionDir)3rdParty\boost;$(IncludePath)</IncludePath>
<LibraryPath>$(LibraryPath)</LibraryPath>
<EnableClangTidyCodeAnalysis>
</EnableClangTidyCodeAnalysis>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IncludePath>$(ProjectDir);$(ProjectDir)Emulator;$(ProjectDir)Algorithm;$(ProjectDir)Common;$(ProjectDir)Platform;$(ProjectDir)Util;$(ProjectDir)Graphics;$(ProjectDir)SceModules;$(SolutionDir)3rdParty;$(SolutionDir)3rdParty\zydis\include;$(SolutionDir)3rdParty\zydis\dependencies\zycore\include;$(SolutionDir)3rdParty\boost;$(IncludePath)</IncludePath>
<IncludePath>$(ProjectDir);$(ProjectDir)Emulator;$(ProjectDir)Algorithm;$(ProjectDir)Common;$(ProjectDir)Platform;$(ProjectDir)Util;$(ProjectDir)Util\Object;$(ProjectDir)Graphics;$(ProjectDir)SceModules;$(SolutionDir)3rdParty;$(SolutionDir)3rdParty\zydis\include;$(SolutionDir)3rdParty\zydis\dependencies\zycore\include;$(SolutionDir)3rdParty\boost;$(IncludePath)</IncludePath>
<LibraryPath>$(LibraryPath)</LibraryPath>
<EnableClangTidyCodeAnalysis>
</EnableClangTidyCodeAnalysis>
@ -617,13 +619,14 @@
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<LanguageStandard>stdcpplatest</LanguageStandard>
<BufferSecurityCheck>false</BufferSecurityCheck>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<AdditionalIncludeDirectories>$(VULKAN_SDK)\Include;$(SolutionDir)GPCS4;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>GPCS4_DEBUG;__PTW32_STATIC_LIB;_CRT_SECURE_NO_WARNINGS;FMT_HEADER_ONLY;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<ControlFlowGuard>false</ControlFlowGuard>
<LanguageStandard_C>stdc17</LanguageStandard_C>
</ClCompile>
<Link>
<GenerateDebugInformation>DebugFull</GenerateDebugInformation>
@ -640,13 +643,14 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<LanguageStandard>stdcpplatest</LanguageStandard>
<BufferSecurityCheck>false</BufferSecurityCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalIncludeDirectories>$(VULKAN_SDK)\Include;$(SolutionDir)GPCS4;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>__PTW32_STATIC_LIB;_CRT_SECURE_NO_WARNINGS;FMT_HEADER_ONLY;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<ControlFlowGuard>false</ControlFlowGuard>
<LanguageStandard_C>stdc17</LanguageStandard_C>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>

View file

@ -949,6 +949,9 @@
<ClInclude Include="Util\Object\UtilStructBank.h">
<Filter>Source Files\Util\Object</Filter>
</ClInclude>
<ClInclude Include="Graphics\Gcn\GcnLoopInfo.h">
<Filter>Source Files\Graphics\Gcn</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Loader\EbootObject.cpp">
@ -1671,6 +1674,9 @@
<ClCompile Include="Util\Object\UtilStructBank.cpp">
<Filter>Source Files\Util\Object</Filter>
</ClCompile>
<ClCompile Include="Graphics\Gcn\GcnLoopInfo.cpp">
<Filter>Source Files\Graphics\Gcn</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Text Include="Emulator\TLSStub.asm">

View file

@ -72,6 +72,7 @@ namespace sce::gcn
{
GcnBasicBlock block;
block.pcBegin = m_programCounter;
block.pcEnd = m_programCounter + ins.length; // in case there is only one instruction in this block
block.insList.push_back(ins);
currentVtx = boost::add_vertex(block, m_cfg);
}
@ -97,7 +98,7 @@ namespace sce::gcn
{
uint32_t branchInstPc = basicBlock.pcEnd - lastInst.length;
uint32_t targetPc = calculateBranchTarget(branchInstPc, lastInst);
auto successor = findVertex(basicBlock.pcEnd);
auto successor = findVertex(targetPc);
LOG_ASSERT(successor.has_value(), "can't find successor for unconditional branch.");
boost::add_edge(vtx, successor.value(), m_cfg);
}

View file

@ -19,10 +19,16 @@ namespace sce::gcn
typedef boost::adjacency_list<
boost::hash_setS, boost::vecS,
boost::bidirectionalS,
boost::directedS,
GcnBasicBlock>
GcnControlFlowGraph;
typedef GcnControlFlowGraph::vertex_descriptor
GcnCfgVertex;
typedef GcnControlFlowGraph::edge_descriptor
GcnCfgEdge;
typedef boost::graph_traits<
GcnControlFlowGraph>::adjacency_iterator
GcnAdjacencyIterator;

View file

@ -53,7 +53,7 @@ namespace sce::gcn
{
auto sopp = gcnInstructionAs<GcnShaderInstSOPP>(ins);
uint32_t target = static_cast<uint32_t>(static_cast<int32_t>(pc) +
static_cast<int32_t>(sopp.control.simm << 2) + 4);
static_cast<int16_t>(sopp.control.simm << 2) + 4);
return target;
}

View file

@ -0,0 +1,131 @@
#include "GcnLoopInfo.h"
#include <boost/graph/tiernan_all_cycles.hpp>
namespace sce::gcn
{
GcnLoop::GcnLoop(const std::vector<GcnCfgVertex>& path):
m_vertices(path)
{
}
GcnLoop::~GcnLoop()
{
}
//////////////////////////////////////////////////////////////////////////
GcnLoopInfo::LoopVisitor::LoopVisitor(std::vector<GcnLoop>& loops) :
m_loops(loops)
{
}
template <typename Path, typename Graph>
void GcnLoopInfo::LoopVisitor::cycle(const Path& path, const Graph& g) const
{
// path is of type std::vector<vertex_descriptor>
m_loops.emplace_back(path);
}
GcnLoopInfo::GcnLoopInfo(GcnControlFlowGraph& cfg) :
m_cfg(cfg)
{
detectLoops();
detectLoopDomination();
detectVertexMaping();
}
GcnLoopInfo::~GcnLoopInfo()
{
}
GcnLoop* GcnLoopInfo::getLoop(GcnCfgVertex vertex) const
{
GcnLoop* loop = nullptr;
auto iter = m_vertexMap.find(vertex);
if (iter != m_vertexMap.end())
{
loop = iter->second;
}
return loop;
}
bool GcnLoopInfo::isLoopHeader(GcnCfgVertex vtx) const
{
auto loop = getLoop(vtx);
return (loop != nullptr) &&
(loop->getHeader() == vtx);
}
void GcnLoopInfo::detectLoops()
{
LoopVisitor visitor(m_loops);
boost::tiernan_all_cycles(m_cfg, visitor);
}
void GcnLoopInfo::detectLoopDomination()
{
for (auto& loop : m_loops)
{
const auto& loopHeader = loop.m_vertices.front();
for (auto& anotherloop : m_loops)
{
if (loop == anotherloop)
{
continue;
}
// If a loop's header is a vertex of an another loop,
// then this loop is the child of that loop.
auto iter = std::find(
anotherloop.m_vertices.begin(),
anotherloop.m_vertices.end(),
loopHeader);
if (iter != anotherloop.m_vertices.end())
{
loop.m_parent = &anotherloop;
anotherloop.m_child = &loop;
break;
}
}
}
}
void GcnLoopInfo::detectVertexMaping()
{
for (const auto& vertex : boost::make_iterator_range(boost::vertices(m_cfg)))
{
auto loop = findLoop(vertex);
m_vertexMap.insert(std::make_pair(vertex, loop));
}
}
GcnLoop* GcnLoopInfo::findLoop(GcnCfgVertex vtx)
{
GcnLoop* innerMostLoop = nullptr;
for (auto& loop : m_loops)
{
if (!loop.contains(vtx))
{
continue;
}
innerMostLoop = findInnerLoop(&loop, vtx);
break;
}
return innerMostLoop;
}
GcnLoop* GcnLoopInfo::findInnerLoop(GcnLoop* loop, GcnCfgVertex vtx)
{
GcnLoop* result = nullptr;
if (loop->m_child == nullptr || !loop->m_child->contains(vtx))
{
result = loop;
}
else
{
result = findInnerLoop(loop->m_child, vtx);
}
return result;
}
} // namespace sce::gcn

View file

@ -0,0 +1,89 @@
#pragma once
#include "GcnCommon.h"
#include "GcnControlFlowGraph.h"
#include <unordered_map>
namespace sce::gcn
{
/**
* \brief Represents a loop in CFG
*/
class GcnLoop
{
friend class GcnLoopInfo;
public:
GcnLoop(
const std::vector<GcnCfgVertex>& path);
~GcnLoop();
bool contains(GcnCfgVertex vtx) const
{
return std::find(
m_vertices.begin(), m_vertices.end(), vtx) !=
m_vertices.end();
}
GcnCfgVertex getHeader() const
{
return m_vertices.front();
}
bool operator==(const GcnLoop& other) const
{
return m_vertices == other.m_vertices;
}
private:
std::vector<GcnCfgVertex> m_vertices;
GcnLoop* m_parent = nullptr;
GcnLoop* m_child = nullptr;
};
/**
* \brief Loop collection of the CFG
*/
class GcnLoopInfo
{
class LoopVisitor
{
public:
LoopVisitor(std::vector<GcnLoop>& loops);
template <typename Path, typename Graph>
void cycle(const Path& path, const Graph& g) const;
private:
std::vector<GcnLoop>& m_loops;
};
public:
GcnLoopInfo(GcnControlFlowGraph& cfg);
~GcnLoopInfo();
/**
* \brief Get loop for vertex
*
* Return the inner most loop that vertex lives in. If a vertex is in no
* loop (for example the entry node), null is returned.
*/
GcnLoop* getLoop(GcnCfgVertex vertex) const;
bool isLoopHeader(GcnCfgVertex vtx) const;
private:
void detectLoops();
void detectLoopDomination();
void detectVertexMaping();
GcnLoop* findLoop(GcnCfgVertex vtx);
GcnLoop* findInnerLoop(GcnLoop* loop, GcnCfgVertex vtx);
private:
GcnControlFlowGraph& m_cfg;
std::vector<GcnLoop> m_loops;
// Mapping of vertex to the inner most loop they occur in
std::unordered_map<GcnCfgVertex, GcnLoop*> m_vertexMap;
};
} // namespace sce::gcn

View file

@ -0,0 +1,18 @@
#include "GcnStackifier.h"
namespace sce::gcn
{
//////////////////////////////////////////////////////////////////////////
GcnStackifier::GcnStackifier(GcnControlFlowGraph& cfg):
m_cfg(cfg)
{
}
GcnStackifier::~GcnStackifier()
{
}
} // namespace sce::gcn

View file

@ -0,0 +1,24 @@
#pragma once
#include "GcnCommon.h"
#include "GcnTokenList.h"
#include "GcnLoopInfo.h"
#include <vector>
namespace sce::gcn
{
class GcnStackifier
{
public:
GcnStackifier(GcnControlFlowGraph& cfg);
~GcnStackifier();
private:
GcnControlFlowGraph& m_cfg;
GcnTokenList m_tokens;
};
} // namespace sce::gcn

View file

@ -0,0 +1,36 @@
#include "GcnTokenList.h"
namespace sce::gcn
{
GcnToken::GcnToken(GcnTokenKind kind, GcnCfgVertex vertex, GcnToken* match) :
m_kind(kind),
m_vertex(vertex),
m_match(match)
{
}
GcnToken::~GcnToken()
{
}
////////////////////////////////////////////////////////////
GcnTokenFactory::GcnTokenFactory(util::ObjectBank<GcnToken>& tokenPool):
m_pool(tokenPool)
{
}
GcnTokenFactory::~GcnTokenFactory()
{
}
////////////////////////////////////////////////////////////
GcnTokenList::GcnTokenList()
{
}
GcnTokenList::~GcnTokenList()
{
}
} // namespace sce::gcn

View file

@ -0,0 +1,217 @@
#pragma once
#include "GcnCommon.h"
#include "GcnControlFlowGraph.h"
#include "UtilObjectBank.h"
#include <list>
namespace sce::gcn
{
enum class GcnTokenKind : uint32_t
{
Invalid = 0,
Code = 1 << 0,
Loop = 1 << 1,
Block = 1 << 2,
If = 1 << 3,
IfNot = 1 << 4,
Else = 1 << 5,
Branch = 1 << 6,
End = 1 << 7,
Condition = 1 << 8,
};
class GcnToken
{
friend class GcnTokenFactory;
public:
GcnToken(GcnTokenKind kind,
GcnCfgVertex vertex,
GcnToken* match);
~GcnToken();
private:
GcnTokenKind m_kind;
GcnCfgVertex m_vertex;
// A related token, for example,
// the match of a If token is End
GcnToken* m_match;
};
class GcnTokenFactory
{
public:
GcnTokenFactory(
util::ObjectBank<GcnToken>& tokenPool);
~GcnTokenFactory();
GcnToken* createCode(GcnCfgVertex vertex)
{
return m_pool.allocate(GcnTokenKind::Code, vertex, nullptr);
}
GcnToken* createLoop()
{
return m_pool.allocate(
GcnTokenKind::Loop, GcnControlFlowGraph::null_vertex(), nullptr);
}
GcnToken* createLoopEnd(GcnToken* loopBegin)
{
assert(loopBegin && loopBegin->m_kind == GcnTokenKind::Loop);
auto loopEnd = m_pool.allocate(
GcnTokenKind::End, GcnControlFlowGraph::null_vertex(), loopBegin);
loopBegin->m_match = loopEnd;
return loopEnd;
}
GcnToken* createBlock()
{
return m_pool.allocate(
GcnTokenKind::Block, GcnControlFlowGraph::null_vertex(), nullptr);
}
GcnToken* createBlockEnd(GcnToken* blockBegin)
{
assert(blockBegin && blockBegin->m_kind == GcnTokenKind::Block);
auto blockEnd = m_pool.allocate(
GcnTokenKind::End, GcnControlFlowGraph::null_vertex(), blockBegin);
blockBegin->m_match = blockEnd;
return blockEnd;
}
GcnToken* createIf(GcnCfgVertex condition)
{
return m_pool.allocate(GcnTokenKind::If, condition, nullptr);
}
GcnToken* createIfNot(GcnCfgVertex condition)
{
return m_pool.allocate(GcnTokenKind::IfNot, condition, nullptr);
}
GcnToken* createElse(GcnToken* tokenIf)
{
assert(!tokenIf || tokenIf->m_kind == GcnTokenKind::If);
auto tokenElse = m_pool.allocate(
GcnTokenKind::Else, GcnControlFlowGraph::null_vertex(), nullptr);
if (tokenIf)
{
tokenIf->m_match = tokenElse;
}
return tokenElse;
}
GcnToken* createIfEnd(GcnToken* tokenIf, GcnToken* tokenElse)
{
assert(!tokenIf || tokenIf->m_kind == GcnTokenKind::If);
assert(!tokenElse || tokenElse->m_kind == GcnTokenKind::Else);
assert(tokenIf || tokenElse);
auto tokenEnd = m_pool.allocate(
GcnTokenKind::End, GcnControlFlowGraph::null_vertex(), tokenIf);
if (tokenElse)
{
tokenElse->m_match = tokenEnd;
}
else
{
tokenIf->m_match = tokenEnd;
}
return tokenEnd;
}
GcnToken* createBranch(GcnToken* target)
{
return m_pool.allocate(
GcnTokenKind::Branch, GcnControlFlowGraph::null_vertex(), target);
}
GcnToken* createCondition(GcnCfgVertex condition)
{
return m_pool.allocate(GcnTokenKind::Condition, condition, nullptr);
}
private:
util::ObjectBank<GcnToken>& m_pool;
};
class GcnTokenList
{
typedef std::list<GcnToken*> TokenListType;
public:
GcnTokenList();
~GcnTokenList();
public:
/// Token iterators...
typedef TokenListType::iterator iterator;
typedef TokenListType::const_iterator const_iterator;
typedef TokenListType::reverse_iterator reverse_iterator;
typedef TokenListType::const_reverse_iterator const_reverse_iterator;
/// Token iterator methods
// clang-format off
inline iterator begin() { return m_list.begin(); }
inline const_iterator begin() const { return m_list.begin(); }
inline iterator end () { return m_list.end(); }
inline const_iterator end () const { return m_list.end(); }
inline reverse_iterator rbegin() { return m_list.rbegin(); }
inline const_reverse_iterator rbegin() const { return m_list.rbegin(); }
inline reverse_iterator rend () { return m_list.rend(); }
inline const_reverse_iterator rend () const { return m_list.rend(); }
inline size_t size() const { return m_list.size(); }
inline bool empty() const { return m_list.empty(); }
inline const GcnToken &front() const { return *m_list.front(); }
inline GcnToken &front() { return *m_list.front(); }
inline const GcnToken &back() const { return *m_list.back(); }
inline GcnToken &back() { return *m_list.back(); }
// clang-format on
void append(GcnToken* token)
{
m_list.push_back(token);
}
iterator insert(iterator where, GcnToken* token)
{
return m_list.insert(where, token);
}
iterator insertAfter(iterator where, GcnToken* token)
{
iterator iter = {};
if (empty())
{
iter = m_list.insert(begin(), token);
}
else
{
iter = m_list.insert(++where, token);
}
return iter;
}
void moveAfter(iterator where, iterator first, iterator last)
{
return m_list.splice(std::next(where), m_list, first, last);
}
void erase(GcnToken* token)
{
std::erase(m_list, token);
}
private:
std::list<GcnToken*> m_list;
};
} // namespace sce::gcn

View file

@ -1,120 +1,102 @@
#pragma once
#include "UtilStructBank.h"
namespace util
{
// These work just like StructBanks but are for C++ objects that need their
// constructors and destructors called.
constexpr uint32_t DEFAULT_OBJECT_CACHE_SIZE = 32;
class DummyThingForConstructor
{
public:
};
class NullCS
{
public:
void Enter()
void enter()
{
}
void Leave()
void leave()
{
}
};
template <class T, class CS = NullCS>
class ObjectBank : public BaseObjectBank
class ObjectBank
{
public:
ObjectBank();
ObjectBank(uint32_t cacheSize, uint32_t preAllocate = 0);
virtual ~ObjectBank();
ObjectBank()
{
m_bank = sbCreate();
sbInit(m_bank, sizeof(T), DEFAULT_OBJECT_CACHE_SIZE);
}
void init(uint32_t cacheSize, uint32_t preAllocate = 0);
ObjectBank(uint32_t cacheSize, uint32_t preAllocate = 0)
{
m_bank = sbCreate();
sbInitEx(m_bank, sizeof(T), cacheSize, preAllocate);
}
void term();
~ObjectBank()
{
sbTerm(&m_bank);
sbDestroy(m_bank);
}
// Set the cache size (in numbers of objects).
// Default is DEFAULT_OBJECT_CACHE_SIZE.
void setCacheSize(uint32_t size);
void init(uint32_t cacheSize, uint32_t preAllocate = 0)
{
m_cs.enter();
sbTerm(m_bank);
sbInitEx(m_bank, sizeof(T), cacheSize, preAllocate);
m_cs.leave();
}
T* allocate();
void free(T* pObj);
void term()
{
m_cs.enter();
sbTerm(m_bank);
sbInit(m_bank, sizeof(T), DEFAULT_OBJECT_CACHE_SIZE);
m_cs.leave();
}
/**
* \brief Set page granularity
*
* Set number of objects for each real memory allocation (malloc)
* Default is DEFAULT_OBJECT_CACHE_SIZE.
*/
void setPageGranularity(uint32_t size)
{
m_cs.enter();
m_bank->m_cacheSize = size;
m_cs.leave();
}
template <typename... Args>
//typename std::enable_if<std::is_constructible_v<T, Args...>, T*>::type
requires std::is_constructible_v<T, Args...>
T* allocate(Args&&... args)
{
T* object = nullptr;
m_cs.enter();
object = (T*)sbAllocate(m_bank);
if (object)
{
std::construct_at(object, std::forward<Args>(args)...);
}
m_cs.leave();
return object;
}
void free(T* pObj)
{
m_cs.enter();
std::destroy_at(pObj);
sbFree(m_bank, object);
m_cs.leave();
}
public:
StructBank m_bank;
CS m_cs;
StructBank* m_bank = nullptr;
CS m_cs;
};
template <class T, class CS>
inline ObjectBank<T, CS>::ObjectBank()
{
sbInit(&m_bank, sizeof(T), DEFAULT_OBJECT_CACHE_SIZE);
}
template <class T, class CS>
inline ObjectBank<T, CS>::ObjectBank(uint32_t cacheSize, uint32_t preAllocate)
{
sbInitEx(&m_bank, sizeof(T), cacheSize, preAllocate);
}
template <class T, class CS>
inline ObjectBank<T, CS>::~ObjectBank()
{
sbTerm(&m_bank);
}
template <class T, class CS>
inline void ObjectBank<T, CS>::init(uint32_t cacheSize, uint32_t preAllocate)
{
m_cs.Enter();
sbTerm(&m_bank);
sbInitEx(&m_bank, sizeof(T), cacheSize, preAllocate);
m_cs.Leave();
}
template <class T, class CS>
inline void ObjectBank<T, CS>::term()
{
m_cs.Enter();
sbTerm(&m_bank);
sbInit(&m_bank, sizeof(T), DEFAULT_OBJECT_CACHE_SIZE);
m_cs.Leave();
}
template <class T, class CS>
inline void ObjectBank<T, CS>::setCacheSize(uint32_t size)
{
m_cs.Enter();
m_bank.m_cacheSize = size;
m_cs.Leave();
}
template <class T, class CS>
inline T* ObjectBank<T, CS>::allocate()
{
T* object;
m_cs.Enter();
object = (T*)sbAllocate(&m_Bank);
if (object)
{
::new ((DummyThingForConstructor*)0, object) T;
}
m_cs.Leave();
return object;
}
template <class T, class CS>
inline void ObjectBank<T, CS>::free(T* object)
{
m_cs.Enter();
object->~T();
sbFree(&m_bank, object);
m_cs.Leave();
}
} // namespace util

View file

@ -6,8 +6,59 @@ LOG_CHANNEL(Util.StructBank);
namespace util
{
struct StructLink
{
struct StructLink* m_slNext;
};
struct StructBankPage
{
struct StructBankPage* m_next;
unsigned long m_objects; // How many objects are in this page?
uint32_t m_data[1];
};
struct StructBank
{
// Struct size.
unsigned long m_structSize;
// The actual size we allocate with (aligned to 4 bytes and with 4 bytes
// extra for the StructLink).
unsigned long m_alignedStructSize;
// How many structs per page.
unsigned long m_cacheSize;
// How many pages have we allocated?
unsigned long m_numPages;
// Total number of objects this StructBank has allocated.
unsigned long m_numTotalObjects;
// The first page.
StructBankPage* m_pageHead;
// The free list.
StructLink* m_freeListHead;
};
bool sbAllocateNewStructPage(StructBank* pBank, uint32_t nAllocations);
StructBank* sbCreate()
{
return (StructBank*)calloc(1, sizeof(StructBank));
}
void sbDestroy(StructBank* pBank)
{
if (pBank)
{
sbTerm(pBank);
}
free(pBank);
}
void sbInit(StructBank* pBank, uint32_t structSize, uint32_t cacheSize)
{
pBank->m_structSize = structSize;
@ -137,7 +188,9 @@ namespace util
}
// Allocate a new page.
pPage = (StructBankPage*)malloc((pBank->m_alignedStructSize * nAllocations) + (sizeof(StructBankPage) - sizeof(uint32_t)));
pPage = (StructBankPage*)malloc(
(pBank->m_alignedStructSize * nAllocations) +
(sizeof(StructBankPage) - sizeof(uint32_t)));
if (!pPage)
{
break;

View file

@ -5,43 +5,13 @@
namespace util
{
struct StructBank;
struct StructLink
{
struct StructLink* m_slNext;
};
// Create an empty bank object
StructBank* sbCreate();
struct StructBankPage
{
struct StructBankPage* m_next;
unsigned long m_objects; // How many objects are in this page?
uint32_t m_data[1];
};
struct StructBank
{
// Struct size.
unsigned long m_structSize;
// The actual size we allocate with (aligned to 4 bytes and with 4 bytes
// extra for the StructLink).
unsigned long m_alignedStructSize;
// How many structs per page.
unsigned long m_cacheSize;
// How many pages have we allocated?
unsigned long m_numPages;
// Total number of objects this StructBank has allocated.
unsigned long m_numTotalObjects;
// The first page.
StructBankPage* m_pageHead;
// The free list.
StructLink* m_freeListHead;
};
// Destroy the bank created by sbCreate
void sbDestroy(StructBank* pBank);
// Initialize.. pass in the size of your structure and how much to cache.
void sbInit(StructBank* pBank, uint32_t structSize, uint32_t cacheSize);