Подозрительное поведение shl

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

Подозрительное поведение shl

Сообщение Дож » 18.04.2019 07:38:19

Код:
Код: Выделить всё
  function One: Int32;
  begin
    One := 1;
  end;
var
  U: UInt64;
begin
  U := One shl 63;
  Writeln(BinStr(U, 64));
end.

Компилируется без варнингов, результат на i386:
Код: Выделить всё
1111111111111111111111111111111110000000000000000000000000000000

Результат на ARMv6:
Код: Выделить всё
1111111111111111111111111111111110000000000000000000000000000000


Если включить RANGECHECKS, то код выше падает в ран-тайме, но вот если заменить 63 на 62, то почему-то выполняется и тоже выдаёт удивительный результат:
Код: Выделить всё
0000000000000000000000000000000001000000000000000000000000000000


Я вот как-то слышал такое, что паскаль -- это строгий типизированный язык для людей, с понятными и естественными операторами, без мутных неявных преобразований. Наверное, что я что-то не понимаю в том, как происходит сдвиг битов? Может ли мне кто-нибудь рассказать как в fpc в точности работает shl?
Аватара пользователя
Дож
энтузиаст
 
Сообщения: 798
Зарегистрирован: 12.10.2008 16:14:47

Re: Подозрительное поведение shl

Сообщение Снег Север » 18.04.2019 07:55:17

Я только одного понять не могу, какое отношение к собственно Паскалю имеет ассемблерная битовая операция, которая, конечно же, машиннозависима?
Аватара пользователя
Снег Север
долгожитель
 
Сообщения: 2110
Зарегистрирован: 27.11.2007 16:14:47

Re: Подозрительное поведение shl

Сообщение Дож » 18.04.2019 08:05:34

Так я же не на ассемблере писал, а на паскале. В нём, внезапно, есть оператор shl.

Если написать на ассемблере
Код: Выделить всё
{$ASMMODE Intel}
  function One: Int32;
  begin
    One := 1;
  end;
var
  U: UInt64;
begin
  asm
    call One
    shl eax, 63
    mov DWORD [U], eax
    mov DWORD [U+4], 0
  end;
  Writeln(BinStr(U, 64));
end.

То машиннозависимая инструкция shl на i386 выдаёт
Код: Выделить всё
0000000000000000000000000000000010000000000000000000000000000000

Этот результат всё равно отличается от того, что выдают программы в первом посте.

Так как работает оператор shl в паскале?
Аватара пользователя
Дож
энтузиаст
 
Сообщения: 798
Зарегистрирован: 12.10.2008 16:14:47

Re: Подозрительное поведение shl

Сообщение SSerge » 18.04.2019 09:03:42

Дож писал(а):Так как работает оператор shl в паскале?


Судя по примерчикам, вами приведенным, работает он корректно только над аргументами до 16 разрядов.
А что бы не посмотреть ассемблерный код, в который компилятор это сгенерировал? Не окажутся ли в нем операции с 16-битовыми регистрами?

Добавлено спустя 13 минут 31 секунду:
Кстати, минуточку...
вы двигаете временную переменную in32 на 63 разряда, доводя ее до внутреннего изображения отрицательного числа и затем преобразуете результат в unsigned int64. Во всяком случае, это ничуть не эквивалентно вашему аасемблерному коду.
SSerge
энтузиаст
 
Сообщения: 866
Зарегистрирован: 12.01.2012 05:34:14
Откуда: Барнаул

Re: Подозрительное поведение shl

Сообщение Дож » 18.04.2019 09:32:38

А что бы не посмотреть ассемблерный код, в который компилятор это сгенерировал?

Пожалуйста:
Код: Выделить всё
# Var U located in register edi:esi
...
# [8] U := One shl 63;
   call   P$PROGRAM_$$_ONE$$LONGINT
   movl   %eax,%esi
   shll   $31,%esi
   movl   %esi,%edi
   sarl   $31,%edi

Код: Выделить всё
# [8] U := One shl 63;
  blx P$PROGRAM_$$_ONE$$LONGINT
  lsl r0,r0,#31
  asr r2,r0,#31
  ldr r1,.Lj9
  str r0,[r1]
  ldr r0,.Lj10
  str r2,[r0]
...
.Lj9:
  .long U_$P$PROGRAM_$$_U
.Lj10:
  .long U_$P$PROGRAM_$$_U+4
Аватара пользователя
Дож
энтузиаст
 
Сообщения: 798
Зарегистрирован: 12.10.2008 16:14:47

Re: Подозрительное поведение shl

Сообщение iskander » 18.04.2019 09:49:01

X86_64, немного изменил пример(результат не изменился):
Код: Выделить всё
function One: Integer; inline;
begin
  Result := 1;
end;

var
  U: UInt64;
begin
  U := One shl 63;
  Writeln(BinStr(U, 64));
end. 

asm:
Код: Выделить всё
main:
@@c3:
; Temps allocated between rsp+32 and rsp+288
; [12] begin
      push   rbx
      push   rsi
      lea   rsp,qword ptr [rsp-296]
@@c5:
; Var U located in register rax
      call   fpc_initializeunits
; Var U located in register rsi
; [13] U := One shl 63;
      mov   rsi,-2147483648 //<--------------
; [16] Writeln(BinStr(U, 64));
      call   fpc_get_output
      mov   rbx,rax
      mov   rdx,rsi
; Var U located in register rdx
      mov   r8d,64
      lea   rcx,qword ptr [rsp+32]
      call   SYSTEM_$$_BINSTR$QWORD$BYTE$$SHORTSTRING
      lea   r8,qword ptr [rsp+32]
      mov   rdx,rbx
      xor   ecx,ecx
      call   fpc_write_text_shortstr
      call   fpc_iocheck
      mov   rcx,rbx
      call   fpc_writeln_end
      call   fpc_iocheck
; [17] ReadLn;
      call   fpc_get_input
      mov   rcx,rax
      call   fpc_readln_end
      call   fpc_iocheck
; [18] end.
      call   fpc_do_exit
      nop
      lea   rsp,qword ptr [rsp+296]
      pop   rsi
      pop   rbx
      ret

Кажется надо стучаться в багтрекер.
iskander
постоялец
 
Сообщения: 180
Зарегистрирован: 08.01.2012 18:43:34

Re: Подозрительное поведение shl

Сообщение Kemet » 18.04.2019 09:53:59

Так ты сдвигаешь 32битное число, а в интеле количество сдвигов в таком случае обрезается до 5 бит - вот что там в результате получится из 63? 31 - максимально возможное количество сдвигов для 32 битного числа.
Kemet
постоялец
 
Сообщения: 241
Зарегистрирован: 10.02.2010 19:28:32
Откуда: Временно оккупированная территория

Re: Подозрительное поведение shl

Сообщение SSerge » 18.04.2019 10:03:12

Код: Выделить всё
program shltest;

function One: Int32;
  begin
    One := 1;
  end;

function Two: UInt64;
begin
  Two:=1;
end;

var
  U: UInt64;
  D: UInt64;
begin
  U := One shl 63;
  Writeln(BinStr(U, 64));
  D := 1;
  U := D shl 63;
  Writeln(BinStr(U, 64));
  U := Two shl 63;
  Writeln(BinStr(U, 64));
end.   


Результат:
Код: Выделить всё
./shltest
1111111111111111111111111111111110000000000000000000000000000000
1000000000000000000000000000000000000000000000000000000000000000
1000000000000000000000000000000000000000000000000000000000000000


...Наглядно видим, что глючь не в shl, а в преобразованиях разных типов целого

Добавлено спустя 10 минут 57 секунд:
Еще красочнее:

Код: Выделить всё
program shltest;

function One: Int32;
  begin
    One := 1;
  end;

function Two: UInt64;
begin
  Two:=1;
end;

var
  U: UInt64;
  D: UInt64;
  C: Int32;
begin
  U := One shl 63;
  Writeln(BinStr(U, 64));
  D := 1;
  U := D shl 63;
  Writeln(BinStr(U, 64));
  U := Two shl 63;
  Writeln(BinStr(U, 64));
  C:=1;
  C := C shl 64;
  U:=C;
  Writeln(BinStr(U, 64));
  Writeln(BinStr(C, 32));
end.     


Код: Выделить всё
./shltest
1111111111111111111111111111111110000000000000000000000000000000
1000000000000000000000000000000000000000000000000000000000000000
1000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000001
00000000000000000000000000000001

SSerge
энтузиаст
 
Сообщения: 866
Зарегистрирован: 12.01.2012 05:34:14
Откуда: Барнаул

Re: Подозрительное поведение shl

Сообщение iskander » 18.04.2019 10:17:02

Да, действительно, One shl 63 -> One shl (63 mod 32) = -2147483648;
А %1111111111111111111111111111111110000000000000000000000000000000 это оно и есть. :oops:
iskander
постоялец
 
Сообщения: 180
Зарегистрирован: 08.01.2012 18:43:34

Re: Подозрительное поведение shl

Сообщение Дож » 18.04.2019 10:42:02

Так что же это получается: паскаль абсолютно всё делает правильно? Вот эти mod 32 -- так и должны сдвигаться биты, один я про это не знал?

Кто не компилируя может предугадать что выдаст код
Код: Выделить всё
var
  U: UInt64;
begin
  U := LongInt(1) shl 63;
  Writeln(BinStr(U, 64));
end.

1000000000000000000000000000000000000000000000000000000000000000
или
1111111111111111111111111111111110000000000000000000000000000000
?
Аватара пользователя
Дож
энтузиаст
 
Сообщения: 798
Зарегистрирован: 12.10.2008 16:14:47

Re: Подозрительное поведение shl

Сообщение iskander » 18.04.2019 10:52:45

Дож писал(а):Вот эти mod 32 -- так и должны сдвигаться биты

Во всяком случае так было когда-то у Делфи в доке.
Дож писал(а):Кто не компилируя может предугадать что выдаст код

Если первый вариант, тогда все-таки в багтрекер.

PS
RAD Studio 10.3 Rio писал(а):Note that the value of y is interpreted modulo the size of the type of x.
Thus for example, if x is an integer, x shl 40 is interpreted as x shl 8 because an integer is 32 bits and 40 mod 32 is 8.
Последний раз редактировалось iskander 18.04.2019 10:56:57, всего редактировалось 1 раз.
iskander
постоялец
 
Сообщения: 180
Зарегистрирован: 08.01.2012 18:43:34

Re: Подозрительное поведение shl

Сообщение Kemet » 18.04.2019 10:54:57

вроде как 1000000000000000000000000000000000000000000000000000000000000000, потому что константное выражение, а оно, вроде как, считается в 64 бит всегда
Kemet
постоялец
 
Сообщения: 241
Зарегистрирован: 10.02.2010 19:28:32
Откуда: Временно оккупированная территория

Re: Подозрительное поведение shl

Сообщение iskander » 18.04.2019 10:59:06

Kemet писал(а):оно, вроде как, считается в 64 бит всегда

А это где-нибудь документировано?
iskander
постоялец
 
Сообщения: 180
Зарегистрирован: 08.01.2012 18:43:34

Re: Подозрительное поведение shl

Сообщение Дож » 18.04.2019 11:00:00

вроде как 1000000000000000000000000000000000000000000000000000000000000000, потому что константное выражение, а оно, вроде как, считается в 64 бит всегда

Поздравляю, вы угадали!

Теперь поиграемся со вторым операндом, вынесем его в функцию
Код: Выделить всё
  function SixtyThree: UInt64;
  begin
    SixtyThree := 63;
  end;
var
  U: UInt64;
begin
  U := 1 shl SixtyThree;
  Writeln(BinStr(U, 64));
end.

Здесь уже нет ни 32-битной арифметики, ни знаков. Что же выдаст программа в этот раз?
Аватара пользователя
Дож
энтузиаст
 
Сообщения: 798
Зарегистрирован: 12.10.2008 16:14:47

Re: Подозрительное поведение shl

Сообщение Kemet » 18.04.2019 11:02:49

Ну так выражение не константное, и вероятно результат будет зависеть от битности системы.

Добавлено спустя 3 минуты 29 секунд:
iskander писал(а):
Kemet писал(а):оно, вроде как, считается в 64 бит всегда

А это где-нибудь документировано?
Без понятия, я уж давно не использую ни дельфи ни Фрипаскаль/Лазарус, просто захожу по привычке почитать интересные темы))) Но подобная тема уже была несколько лет назад. И там говорилось, что константные выражения считаются в 64 бит всегда. Вроде Сергей Горелкин отвечал.
Kemet
постоялец
 
Сообщения: 241
Зарегистрирован: 10.02.2010 19:28:32
Откуда: Временно оккупированная территория

След.

Вернуться в Free Pascal Compiler

Кто сейчас на конференции

Сейчас этот форум просматривают: Google Adsense [Bot] и гости: 4

Рейтинг@Mail.ru