我正在编写一个具有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之外,我还必须为每个支持的cpu架构编写程序集,而且我只能特别流利地使用其中一个。
这是合理的我能做的最好的,还是有更好的方法来设置这个int56类?
或者,有什么方法可以确保我能够使用位域来做我真正需要的事情吗?
提前致谢