Merge pull request #380 from delroth/faster-mmiointerface
MMIO: Lazy-initialize read/write handlers since most are not actually used
This commit is contained in:
commit
3161cdfa8e
|
@ -224,8 +224,8 @@ ReadHandlingMethod<T>* ReadToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_
|
||||||
{
|
{
|
||||||
typedef typename SmallerAccessSize<T>::value ST;
|
typedef typename SmallerAccessSize<T>::value ST;
|
||||||
|
|
||||||
const ReadHandler<ST>* high_part;
|
ReadHandler<ST>* high_part;
|
||||||
const ReadHandler<ST>* low_part;
|
ReadHandler<ST>* low_part;
|
||||||
mmio->GetHandlerForRead(high_part_addr, &high_part);
|
mmio->GetHandlerForRead(high_part_addr, &high_part);
|
||||||
mmio->GetHandlerForRead(low_part_addr, &low_part);
|
mmio->GetHandlerForRead(low_part_addr, &low_part);
|
||||||
|
|
||||||
|
@ -241,8 +241,8 @@ WriteHandlingMethod<T>* WriteToSmaller(Mapping* mmio, u32 high_part_addr, u32 lo
|
||||||
{
|
{
|
||||||
typedef typename SmallerAccessSize<T>::value ST;
|
typedef typename SmallerAccessSize<T>::value ST;
|
||||||
|
|
||||||
const WriteHandler<ST>* high_part;
|
WriteHandler<ST>* high_part;
|
||||||
const WriteHandler<ST>* low_part;
|
WriteHandler<ST>* low_part;
|
||||||
mmio->GetHandlerForWrite(high_part_addr, &high_part);
|
mmio->GetHandlerForWrite(high_part_addr, &high_part);
|
||||||
mmio->GetHandlerForWrite(low_part_addr, &low_part);
|
mmio->GetHandlerForWrite(low_part_addr, &low_part);
|
||||||
|
|
||||||
|
@ -258,7 +258,7 @@ ReadHandlingMethod<T>* ReadToLarger(Mapping* mmio, u32 larger_addr, u32 shift)
|
||||||
{
|
{
|
||||||
typedef typename LargerAccessSize<T>::value LT;
|
typedef typename LargerAccessSize<T>::value LT;
|
||||||
|
|
||||||
const ReadHandler<LT>* large;
|
ReadHandler<LT>* large;
|
||||||
mmio->GetHandlerForRead(larger_addr, &large);
|
mmio->GetHandlerForRead(larger_addr, &large);
|
||||||
|
|
||||||
// TODO(delroth): optimize
|
// TODO(delroth): optimize
|
||||||
|
@ -271,9 +271,8 @@ ReadHandlingMethod<T>* ReadToLarger(Mapping* mmio, u32 larger_addr, u32 shift)
|
||||||
// redundant code between these two classes but trying to abstract it away
|
// redundant code between these two classes but trying to abstract it away
|
||||||
// brings more trouble than it fixes.
|
// brings more trouble than it fixes.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
ReadHandler<T>::ReadHandler() : m_Method(nullptr)
|
ReadHandler<T>::ReadHandler()
|
||||||
{
|
{
|
||||||
ResetMethod(InvalidRead<T>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -289,8 +288,11 @@ ReadHandler<T>::~ReadHandler()
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void ReadHandler<T>::Visit(ReadHandlingMethodVisitor<T>& visitor) const
|
void ReadHandler<T>::Visit(ReadHandlingMethodVisitor<T>& visitor)
|
||||||
{
|
{
|
||||||
|
if (!m_Method)
|
||||||
|
InitializeInvalid();
|
||||||
|
|
||||||
m_Method->AcceptReadVisitor(visitor);
|
m_Method->AcceptReadVisitor(visitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,9 +327,8 @@ void ReadHandler<T>::ResetMethod(ReadHandlingMethod<T>* method)
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
WriteHandler<T>::WriteHandler() : m_Method(nullptr)
|
WriteHandler<T>::WriteHandler()
|
||||||
{
|
{
|
||||||
ResetMethod(InvalidWrite<T>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -343,8 +344,11 @@ WriteHandler<T>::~WriteHandler()
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void WriteHandler<T>::Visit(WriteHandlingMethodVisitor<T>& visitor) const
|
void WriteHandler<T>::Visit(WriteHandlingMethodVisitor<T>& visitor)
|
||||||
{
|
{
|
||||||
|
if (!m_Method)
|
||||||
|
InitializeInvalid();
|
||||||
|
|
||||||
m_Method->AcceptWriteVisitor(visitor);
|
m_Method->AcceptWriteVisitor(visitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ public:
|
||||||
// Note that for reads we cannot simply return the read value because C++
|
// Note that for reads we cannot simply return the read value because C++
|
||||||
// allows overloading only with parameter types, not return types.
|
// allows overloading only with parameter types, not return types.
|
||||||
#define READ_FUNC(Size) \
|
#define READ_FUNC(Size) \
|
||||||
void Read(u32 addr, u##Size* val) const \
|
void Read(u32 addr, u##Size* val) \
|
||||||
{ \
|
{ \
|
||||||
u32 id = UniqueID(addr) / sizeof (u##Size); \
|
u32 id = UniqueID(addr) / sizeof (u##Size); \
|
||||||
*val = m_Read##Size##Handlers[id].Read(addr); \
|
*val = m_Read##Size##Handlers[id].Read(addr); \
|
||||||
|
@ -114,7 +114,7 @@ public:
|
||||||
#undef READ_FUNC
|
#undef READ_FUNC
|
||||||
|
|
||||||
#define WRITE_FUNC(Size) \
|
#define WRITE_FUNC(Size) \
|
||||||
void Write(u32 addr, u##Size val) const \
|
void Write(u32 addr, u##Size val) \
|
||||||
{ \
|
{ \
|
||||||
u32 id = UniqueID(addr) / sizeof (u##Size); \
|
u32 id = UniqueID(addr) / sizeof (u##Size); \
|
||||||
m_Write##Size##Handlers[id].Write(addr, val); \
|
m_Write##Size##Handlers[id].Write(addr, val); \
|
||||||
|
@ -133,11 +133,11 @@ public:
|
||||||
// value. This second variant is needed because C++ doesn't do overloads
|
// value. This second variant is needed because C++ doesn't do overloads
|
||||||
// based on return type but only based on argument types.
|
// based on return type but only based on argument types.
|
||||||
#define GET_HANDLERS_FUNC(Type, Size) \
|
#define GET_HANDLERS_FUNC(Type, Size) \
|
||||||
const Type##Handler<u##Size>& GetHandlerFor##Type##Size(u32 addr) const \
|
Type##Handler<u##Size>& GetHandlerFor##Type##Size(u32 addr) \
|
||||||
{ \
|
{ \
|
||||||
return m_##Type##Size##Handlers[UniqueID(addr) / sizeof (u##Size)]; \
|
return m_##Type##Size##Handlers[UniqueID(addr) / sizeof (u##Size)]; \
|
||||||
} \
|
} \
|
||||||
void GetHandlerFor##Type(u32 addr, const Type##Handler<u##Size>** h) const \
|
void GetHandlerFor##Type(u32 addr, Type##Handler<u##Size>** h) \
|
||||||
{ \
|
{ \
|
||||||
*h = &GetHandlerFor##Type##Size(addr); \
|
*h = &GetHandlerFor##Type##Size(addr); \
|
||||||
}
|
}
|
||||||
|
@ -147,8 +147,8 @@ public:
|
||||||
|
|
||||||
// Dummy 64 bits variants of these functions. While 64 bits MMIO access is
|
// Dummy 64 bits variants of these functions. While 64 bits MMIO access is
|
||||||
// not supported, we need these in order to make the code compile.
|
// not supported, we need these in order to make the code compile.
|
||||||
void Read(u32 addr, u64* val) const { _dbg_assert_(MEMMAP, 0); }
|
void Read(u32 addr, u64* val) { _dbg_assert_(MEMMAP, 0); }
|
||||||
void Write(u32 addr, u64 val) const { _dbg_assert_(MEMMAP, 0); }
|
void Write(u32 addr, u64 val) { _dbg_assert_(MEMMAP, 0); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// These arrays contain the handlers for each MMIO access type: read/write
|
// These arrays contain the handlers for each MMIO access type: read/write
|
||||||
|
|
|
@ -115,10 +115,16 @@ public:
|
||||||
~ReadHandler();
|
~ReadHandler();
|
||||||
|
|
||||||
// Entry point for read handling method visitors.
|
// Entry point for read handling method visitors.
|
||||||
void Visit(ReadHandlingMethodVisitor<T>& visitor) const;
|
void Visit(ReadHandlingMethodVisitor<T>& visitor);
|
||||||
|
|
||||||
T Read(u32 addr) const
|
T Read(u32 addr)
|
||||||
{
|
{
|
||||||
|
// Check if the handler has already been initialized. For real
|
||||||
|
// handlers, this will always be the case, so this branch should be
|
||||||
|
// easily predictable.
|
||||||
|
if (!m_Method)
|
||||||
|
InitializeInvalid();
|
||||||
|
|
||||||
return m_ReadFunc(addr);
|
return m_ReadFunc(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,6 +133,13 @@ public:
|
||||||
void ResetMethod(ReadHandlingMethod<T>* method);
|
void ResetMethod(ReadHandlingMethod<T>* method);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Initialize this handler to an invalid handler. Done lazily to avoid
|
||||||
|
// useless initialization of thousands of unused handler objects.
|
||||||
|
void InitializeInvalid()
|
||||||
|
{
|
||||||
|
ResetMethod(InvalidRead<T>());
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<ReadHandlingMethod<T>> m_Method;
|
std::unique_ptr<ReadHandlingMethod<T>> m_Method;
|
||||||
std::function<T(u32)> m_ReadFunc;
|
std::function<T(u32)> m_ReadFunc;
|
||||||
};
|
};
|
||||||
|
@ -142,10 +155,16 @@ public:
|
||||||
~WriteHandler();
|
~WriteHandler();
|
||||||
|
|
||||||
// Entry point for write handling method visitors.
|
// Entry point for write handling method visitors.
|
||||||
void Visit(WriteHandlingMethodVisitor<T>& visitor) const;
|
void Visit(WriteHandlingMethodVisitor<T>& visitor);
|
||||||
|
|
||||||
void Write(u32 addr, T val) const
|
void Write(u32 addr, T val)
|
||||||
{
|
{
|
||||||
|
// Check if the handler has already been initialized. For real
|
||||||
|
// handlers, this will always be the case, so this branch should be
|
||||||
|
// easily predictable.
|
||||||
|
if (!m_Method)
|
||||||
|
InitializeInvalid();
|
||||||
|
|
||||||
m_WriteFunc(addr, val);
|
m_WriteFunc(addr, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,6 +174,13 @@ public:
|
||||||
void ResetMethod(WriteHandlingMethod<T>* method);
|
void ResetMethod(WriteHandlingMethod<T>* method);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Initialize this handler to an invalid handler. Done lazily to avoid
|
||||||
|
// useless initialization of thousands of unused handler objects.
|
||||||
|
void InitializeInvalid()
|
||||||
|
{
|
||||||
|
ResetMethod(InvalidWrite<T>());
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<WriteHandlingMethod<T>> m_Method;
|
std::unique_ptr<WriteHandlingMethod<T>> m_Method;
|
||||||
std::function<void(u32, T)> m_WriteFunc;
|
std::function<void(u32, T)> m_WriteFunc;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue