Есть ли эффективный способ сделать 56-битное целое число? - PullRequest
0 голосов
/ 10 июня 2018

Я пишу виртуальную машину с 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?

Или есть ли способ, которым я могу быть уверен, что смогу использовать битовые поля для того, что мне действительно нужно?

Заранее спасибо

...