В основном все сводится к тому, как x86 обрабатывает коды операций арифметического сдвига: он использует только нижние 5 битов счетчика сдвигов. См., Например, руководство по программированию 80386 . В C / C ++ это технически неопределенное поведение - сдвигать бит более чем на 31 бит (для 32-битного целого), придерживаясь философии C: «вы не платите за то, что вам не нужно». Из раздела 6.5.7, параграфа 3 стандарта C99:
Целочисленные продвижения выполняются для каждого из операндов. Тип результата - тип повышенного левого операнда. Если значение правого операнда отрицательно или
больше или равно ширине повышенного левого операнда, поведение не определено.
Это позволяет компиляторам пропускать односменную инструкцию на x86 для смен. 64-битные сдвиги не могут быть выполнены в одной инструкции на x86. Они используют инструкции SHLD / SHRD и некоторую дополнительную логику. На x86_64 64-битные сдвиги могут быть выполнены в одной инструкции.
Например, gcc 3.4.4 выдает следующую сборку для 64-разрядного сдвига влево на произвольную величину (скомпилировано с -O3 -fomit-frame-pointer
):
uint64_t lshift(uint64_t x, int r)
{
return x << r;
}
_lshift:
movl 12(%esp), %ecx
movl 4(%esp), %eax
movl 8(%esp), %edx
shldl %cl,%eax, %edx
sall %cl, %eax
testb $32, %cl
je L5
movl %eax, %edx
xorl %eax, %eax
L5:
ret
Теперь я не очень знаком с C #, но, полагаю, у него схожая философия - спроектировать язык, чтобы он мог быть реализован максимально эффективно. Указывая, что операции сдвига используют только младшие 5/6 битов счетчика сдвигов, это позволяет компилятору JIT компилировать сдвиги как можно более оптимально. 32-битные сдвиги, а также 64-битные сдвиги в 64-битных системах могут компилировать JIT в один код операции.
Если бы C # был перенесен на платформу, которая по-разному реагировала на собственные коды операций сдвига, то это фактически повлекло бы за собой дополнительное снижение производительности - JIT-компилятор должен был бы обеспечить соблюдение стандарта, поэтому он должен был бы добавить дополнительная логика, позволяющая убедиться, что использовались только младшие 5/6 битов числа сдвигов.