Я пишу виртуальную машину с 8-битными кодами операций, а один из общих типов инструкций имеет 8-битный код операции, за которым в памяти следует 56-битный целочисленный операнд.
Первоначально,Я собирался реализовать эту инструкцию следующим образом:
struct machine_op {
std::uint64_t opcode:8
std::int64_t operand:56;
};
Однако я не слышал конца комментариев о том, что использование таких битовых полей не будет переносимым, и, в частности, оно не будет гарантировать, что поле операнда будетна самом деле находиться в нужном месте в памяти (т. е. первый байт памяти структуры).
То, что я тогда подумал сделать, это создать 56-битный целочисленный класс и изменить приведенную выше структуру в соответствии сследующее:
struct machine_op {
char opcode;
int56 operand;
};
Это, в силу того, что является стандартной структурой размещения, гарантирует, что адрес операнда находится сразу после кода операции, если int56 был написан так, что не имел никакогоограничения на выравнивание.
И для этого у меня определен следующий класс, цель которого заключается в том, чтобы инкапсулировать какигнорируемое 56-битное целое число, которое не имеет ограничений на выравнивание:
class int56 {
public:
struct data {
unsigned char byte1;
unsigned char byte2;
unsigned char byte3;
unsigned char byte4;
unsigned char byte5;
unsigned char byte6;
unsigned char byte7;
};
struct big_endian {
char sign;
data value;
inline big_endian() = default;
inline constexpr big_endian(const data &v) : sign(v.byte1 & 0x80 ? 0xff : 0), value(v)
{
}
};
struct little_endian {
data value;
char sign;
inline little_endian() = default;
inline constexpr little_endian(const data &v) : value(v), sign(v.byte7 & 0x80 ? 0xff : 0)
{
}
};
union aligner {
std::int64_t value;
big_endian big;
little_endian little;
};
inline int56() = default;
inline constexpr int56(std::int64_t x) :
#if BYTE_ORDER == LITTLE_ENDIAN
value((aligner{x}).little.value)
#else
value((aligner{x}).big.value)
#endif
{
}
inline constexpr operator std::int64_t() const
{
#if BYTE_ORDER == LITTLE_ENDIAN
return (aligner{.little = little_endian(value)}).value;
#else
return (aligner{.big = big_endian(value)}).value;
#endif
}
private:
data value;
};
Эта реализация будет работать только для машин с прямым или обратным порядком байтов, однако ... другие битовые представления целых чисел потерпят неудачу.Несмотря на это, я могу жить с этим ограничением, но даже при использовании флагов компилятора с высокой оптимизацией я обнаружил, что доступ к этому классу значительно снижает производительность по сравнению с использованием битовых полей.
Кстати, я всегда будуцелевые машины с как минимум 64-битной архитектурой, поэтому нет проблем с доступом к данным шириной 64 бита.Я просто не могу прибегнуть к ручной сборке, потому что в дополнение к тому, что эти функции больше не являются constexpr, мне пришлось бы писать ассемблер для каждой поддерживаемой архитектуры процессора, и я только в совершенстве владею одной из них.
Поэтомуразумно лучшее, что я могу сделать, или есть лучший способ настроить этот класс int56?
Или есть ли способ, которым я могу быть уверен, что смогу использовать битовые поля для того, что мне действительно нужно?
Заранее спасибо