"char *" с необычным размером слова памяти (архитектура Knuth MIX)

Оригинальная архитектура MIX содержит 6-битные байты, а память адресуется как 31-битные слова (5 байтов и бит знака). В качестве мысленного упражнения мне интересно, как язык C может функционировать в этой среде, учитывая:

  • char имеет не менее 8 бит (приложение E спецификации C99).
  • В разделе 8.2 раздела "6.2.2.3" ( "Указатели" ) говорится: "Когда указатель на объект преобразуется в указатель на тип символа, результат указывает на младший адресный байт объекта. Последовательные приращения результат, вплоть до размера объекта, выводит указатели на оставшиеся байты объекта". Моя интерпретация этого требования заключается в том, что он поддерживает "memcpy (& dst_obj, & src_obj, sizeof (src_obj))".

Подходы, о которых я могу думать:

  • Сделайте char 31 бит, поэтому косвенное обращение через "char *" - это простой доступ к памяти. Но это делает строки расточительными (и означает, что они не совместимы с POSIX, поскольку, по-видимому, требуется 8-битные символы).
  • Упакуйте три 8-битных символа в одно слово, с 7 игнорируемыми битами: "char *" может состоять из слова и индекса char внутри него. Однако это, похоже, нарушает 6.3.2.3, то есть memcpy() обязательно пропускает игнорируемые биты (которые, вероятно, имеют смысл для реального типа объекта)
  • Полностью упаковать символы в слова, например. четвертый 8 бит char будет иметь 7 бит в слове 0 и один бит в слове 1. Однако это, по-видимому, требует, чтобы все объекты имели размер в 8-битных символах, например. "uint31_t" не может быть объявлен в соответствии с длиной слова, так как это снова имеет проблему memcpy().

Таким образом, похоже, что первый (расточительный) вариант использования 31-битных символов со всеми объектами, размер которых равен кратному char - правильно ли я читаю его таким образом?

Ответы

Ответ 1

Я согласен с тем, что C на архитектуре MIX может быть болью для реализации, и, хотя он и не является юристом по языку, мне кажется, что вы правы, чтобы указать свой подход 1. как единственный соответствующий.

Во всяком случае, космические отходы для струн - это наименьшая из ваших проблем: вы можете обойти это, обратившись к решению, более старому, чем C: каждый char представляет несколько букв. Для архитектуры MIX вы могли бы, например, разработать 7-битное кодирование и упаковать 4 буквы в каждый char:

char hi[4];
hi[0] = 'hell';
hi[1] = 'o, w';
hi[2] = 'orld';
hi[3] = '\0';

printf("%s", hi);

// Whoops, we forgot the exclamation mark
putchar('!\n');

Эта реализация кажется странной, но, согласно Википедии, она использовалась в первой программе Hello World. Я взглянул на Стандарт и не нашел ничего, что помешало бы ему, даже в C11. Особенно в разделе 6.4.4.4 допускается кодирование буквенных символов и строк конкретными способами реализации.

EDIT:

Это не помогает в случае других трудностей, главным из которых является невозможность использовать большую часть вашей машины возможных инструкций, поскольку вы не можете адресовать одиночные байты с родными типами C. Однако вы можете использовать битовые поля следующим образом:

typedef struct _bytes {
    unsigned int sign  : 1;
    unsigned int byte1 : 6; // EDIT: bitfields must be 
    unsigned int byte2 : 6; // declared as ints in standard C
    unsigned int byte3 : 6;
    unsigned int byte4 : 6;
    unsigned int byte5 : 6;
} bytes;

typedef union _native_type {
    char as_word;
    int as_int; // int = char; useful for standard library functions, etc.
    bytes as_bytes;
} native_type;

Обратите внимание, что в С++ из-за предложения в правилах строгого сглаживания вы должны быть осторожны, чтобы всегда обращаться к элементу char между доступом к int и одним к bytes, так как этот фрагмент:

native_type a, b;
a.as_int = 0xC11BABE;
b.as_bytes.byte4 = a.as_bytes.byte4; // Whoops

приведет к поведению undefined: подробнее см. здесь.

Ответ 2

Самый практичный подход, вероятно, заключался бы в том, чтобы сделать int равным 30 битам и иметь char равным 10 или 15. Использование десяти бит для char позволило бы упаковать текст ASCII более плотно, но увеличит стоимость индексации в массивы char из-за требуемого разделения на три. Хранение текста в Юникоде может быть довольно эффективным с 10- или 15-байтным char. С 15-байтовым char около 30720 кодовых точек будет занимать 15 бит, а остаток займет 30. С 10-байтовым char 128 кодовых точек будут принимать 10 бит, а 65408 - 20, а остальные будут принимать 30.

Чтобы уменьшить стоимость деления на 3, может оказаться полезным, чтобы каждый char* содержал два слова; один будет идентифицировать слово, содержащее символ, а другой будет идентифицировать смещение в символах с самого начала этого слова. Добавление постоянного смещения к указателю, который, как было известно, нормирован, может использовать код, например:

p += 5; // Becomes...
if (p.offset) { p.offset=2; p.base+=1; }
else { p.offset--; p.base+=2; }

Неплохо, но это позволит избежать необходимости "деления".