What is the behaviour of shl and shr for non register sized operands?

后端 未结 3 1706
广开言路
广开言路 2020-12-17 17:52

This question is inspired by my attempts to answer another question: Converting decimal/integer to binary - how and why it works the way it does?

The only documentat

3条回答
  •  -上瘾入骨i
    2020-12-17 18:13

    What's happening behind the scenes is actually quite interesting.

    Given the following Delphi app:

    program BitwiseShift;
    var
      u8: Byte;
    begin
      //all in one go
      u8 := $ff;
      Writeln((u8 shl 7) shr 7);   
      // expects: 1 actual: 255
    
      //step by step
      u8 := $ff;
      u8:= u8 shl 7;
      u8:= u8 shr 7;
      WriteLn(u8);  
      // expects: 1 actual: 1
    end.
    

    The following assembly is produced (in XE2)

    BitwiseShift.dpr.10: Writeln((u8 shl 7) shr 7);
    004060D3 33D2             xor edx,edx
    004060D5 8A1594AB4000     mov dl,[$0040ab94]
    004060DB C1E207           shl edx,$07
    004060DE C1EA07           shr edx,$07
    004060E1 A114784000       mov eax,[$00407814]  <<--- The result is NOT a byte!!
    004060E6 E895D6FFFF       call @Write0Long
    004060EB E864D9FFFF       call @WriteLn
    004060F0 E8A7CCFFFF       call @_IOTest
    BitwiseShift.dpr.13: u8 := $ff;
    004060F5 C60594AB4000FF   mov byte ptr [$0040ab94],$ff
    BitwiseShift.dpr.14: u8:= u8 shl 7;
    004060FC C02594AB400007   shl byte ptr [$0040ab94],$07
    BitwiseShift.dpr.15: u8:= u8 shr 7;
    00406103 33C0             xor eax,eax
    00406105 A094AB4000       mov al,[$0040ab94]
    0040610A C1E807           shr eax,$07
    0040610D A294AB4000       mov [$0040ab94],al
    BitwiseShift.dpr.16: WriteLn(u8);
    00406112 33D2             xor edx,edx
    00406114 8A1594AB4000     mov dl,[$0040ab94]
    0040611A A114784000       mov eax,[$00407814]
    0040611F E85CD6FFFF       call @Write0Long
    00406124 E82BD9FFFF       call @WriteLn
    00406129 E86ECCFFFF       call @_IOTest
    

    The rule as far as I can make out is:

    Rule

    The narrowness of the shift being performed (8/16/32 bits) depends on the size of the result of the shift, not the size of variables used in the shift. In the original case you do not reserve a variable to hold the result and thus Delphi chooses a default (integer) for you.

    How to get the expected result
    In my altered case the result is byte sized and hence the data gets chopped to that size.

    If you alter your case to force the use of bytes, your original expectations are met:

    Writeln(byte(byte(u8 shl 7) shr 7));
    // expects: 1 actual: 1
    
    Project24.dpr.19: Writeln(byte(byte(u8 shl 7) shr 7));
    00406135 8A1594AB4000     mov dl,[$0040ab94]
    0040613B C1E207           shl edx,$07
    0040613E 81E2FF000000     and edx,$000000ff
    00406144 C1EA07           shr edx,$07
    00406147 81E2FF000000     and edx,$000000ff
    0040614D A114784000       mov eax,[$00407814]
    00406152 E829D6FFFF       call @Write0Long
    00406157 E8F8D8FFFF       call @WriteLn
    0040615C E83BCCFFFF       call @_IOTest
    

提交回复
热议问题