Push the compiler into the optimization

It looks like copying the RegisterRec into a local variable (as it has been here before)
seems to confuse the compiler which then refuses to optimize the calls.
With this simple tweak the code is actually 8B shorter than before (while retaining the saved ~170B of RAM)
pull/287/head
D.R.racer 2023-08-04 11:45:09 +02:00
parent 347b6fd391
commit 442ad9f1ef
1 changed files with 65 additions and 35 deletions

View File

@ -168,17 +168,39 @@
*/ */
struct RegisterFlags { struct RegisterFlags {
uint8_t writable : 1; struct A {
uint8_t rwfuncs : 1; // 1: register needs special read and write functions uint8_t size : 2; // 0: 1 bit, 1: 1 byte, 2: 2 bytes - keeping size as the lowest 2 bits avoids costly shifts when accessing them
uint8_t size : 2; // 0: 1 bit, 1: 1 byte, 2: 2 bytes uint8_t writable : 1;
constexpr RegisterFlags(bool writable, uint8_t size) uint8_t rwfuncs : 1; // 1: register needs special read and write functions
: writable(writable) constexpr A(uint8_t size, bool writable)
, rwfuncs(0) : size(size)
, size(size) {} , writable(writable)
constexpr RegisterFlags(bool writable, bool rwfuncs, uint8_t size) , rwfuncs(0) {}
: writable(writable) constexpr A(uint8_t size, bool writable, bool rwfuncs)
, rwfuncs(rwfuncs) : size(size)
, size(size) {} , writable(writable)
, rwfuncs(rwfuncs) {}
};
union U {
A bits;
uint8_t b;
constexpr U(uint8_t size, bool writable)
: bits(size, writable) {}
constexpr U(uint8_t size, bool writable, bool rwfuncs)
: bits(size, writable, rwfuncs) {}
constexpr U(uint8_t b)
: b(b) {}
} u;
constexpr RegisterFlags(uint8_t size, bool writable)
: u(size, writable) {}
constexpr RegisterFlags(uint8_t size, bool writable, bool rwfuncs)
: u(size, writable, rwfuncs) {}
constexpr RegisterFlags(uint8_t b)
: u(b) {}
constexpr bool Writable() const { return u.bits.writable; }
constexpr bool RWFuncs() const { return u.bits.rwfuncs; }
constexpr uint8_t Size() const { return u.bits.size; }
}; };
using TReadFunc = uint16_t (*)(); using TReadFunc = uint16_t (*)();
@ -209,21 +231,21 @@ struct RegisterRec {
template <typename T> template <typename T>
constexpr RegisterRec(bool writable, T *address) constexpr RegisterRec(bool writable, T *address)
: flags(RegisterFlags(writable, sizeof(T))) : flags(RegisterFlags(sizeof(T), writable))
, A1((void *)address) , A1((void *)address)
, A2((void *)nullptr) {} , A2((void *)nullptr) {}
constexpr RegisterRec(const TReadFunc &readFunc, uint8_t bytes) constexpr RegisterRec(const TReadFunc &readFunc, uint8_t bytes)
: flags(RegisterFlags(false, true, bytes)) : flags(RegisterFlags(bytes, false, true))
, A1(readFunc) , A1(readFunc)
, A2((void *)nullptr) {} , A2((void *)nullptr) {}
constexpr RegisterRec(const TReadFunc &readFunc, const TWriteFunc &writeFunc, uint8_t bytes) constexpr RegisterRec(const TReadFunc &readFunc, const TWriteFunc &writeFunc, uint8_t bytes)
: flags(RegisterFlags(true, true, bytes)) : flags(RegisterFlags(bytes, true, true))
, A1(readFunc) , A1(readFunc)
, A2(writeFunc) {} , A2(writeFunc) {}
constexpr RegisterRec() constexpr RegisterRec()
: flags(RegisterFlags(false, false, 1)) : flags(RegisterFlags(1, false, false))
, A1((void *)&dummyZero) , A1((void *)&dummyZero)
, A2((void *)nullptr) {} , A2((void *)nullptr) {}
}; };
@ -413,30 +435,35 @@ bool ReadRegister(uint8_t address, uint16_t &value) {
// Get pointer to register at address // Get pointer to register at address
#ifndef UNITTEST #ifndef UNITTEST
RegisterRec reg = *static_cast<RegisterRec *>(pgm_read_ptr(registers + address)); // beware - abusing the knowledge of RegisterRec memory layout to do lpm_reads
const uint8_t *addr = reinterpret_cast<const uint8_t *>(registers + address);
const RegisterFlags rf(hal::progmem::read_byte(addr));
#else #else
RegisterRec reg = registers[address]; const RegisterRec *reg = registers + address;
const RegisterFlags rf = reg->flags;
#endif #endif
if (!reg.flags.rwfuncs) { if (!rf.RWFuncs()) {
switch (reg.flags.size) { const uint16_t *varAddr = reinterpret_cast<const uint16_t *>(addr + 1);
switch (rf.Size()) {
case 0: case 0:
case 1: case 1:
value = *static_cast<uint8_t *>(reg.A1.addr); value = *reinterpret_cast<const uint8_t *>(hal::progmem::read_word(varAddr));
break; break;
case 2: case 2:
value = *static_cast<uint16_t *>(reg.A1.addr); value = *reinterpret_cast<const uint16_t *>(hal::progmem::read_word(varAddr));
break; break;
default: default:
return false; return false;
} }
return true; return true;
} else { } else {
switch (reg.flags.size) { switch (rf.Size()) {
case 0: case 0:
case 1: case 1:
case 2: case 2: {
value = reg.A1.readFunc(); const TReadFunc readFunc = reinterpret_cast<const TReadFunc>(pgm_read_word(addr + 1));
break; value = readFunc();
} break;
default: default:
return false; return false;
} }
@ -450,33 +477,36 @@ bool WriteRegister(uint8_t address, uint16_t value) {
} }
#ifndef UNITTEST #ifndef UNITTEST
RegisterRec reg = *static_cast<RegisterRec *>(pgm_read_ptr(registers + address)); const uint8_t *addr = reinterpret_cast<const uint8_t *>(registers + address);
const RegisterFlags rf(hal::progmem::read_byte(addr));
#else #else
RegisterRec reg = registers[address]; RegisterRec reg = registers[address];
#endif #endif
if (!reg.flags.writable) { if (!rf.Writable()) {
return false; return false;
} }
if (!reg.flags.rwfuncs) { if (!rf.RWFuncs()) {
switch (reg.flags.size) { const uint16_t *varAddr = reinterpret_cast<const uint16_t *>(addr + 3);
switch (rf.Size()) {
case 0: case 0:
case 1: case 1:
*static_cast<uint8_t *>(reg.A1.addr) = value; *reinterpret_cast<uint8_t *>(hal::progmem::read_word(varAddr)) = value;
break; break;
case 2: case 2:
*static_cast<uint16_t *>(reg.A1.addr) = value; *reinterpret_cast<uint16_t *>(hal::progmem::read_word(varAddr)) = value;
break; break;
default: default:
return false; return false;
} }
return true; return true;
} else { } else {
switch (reg.flags.size) { switch (rf.Size()) {
case 0: case 0:
case 1: case 1:
case 2: case 2: {
reg.A2.writeFunc(value); const TWriteFunc writeFunc = reinterpret_cast<const TWriteFunc>(pgm_read_word(addr + 3));
break; writeFunc(value);
} break;
default: default:
return false; return false;
} }