6

There's three basic shifts:

  • Logical left shift: ignores sign bit altogether.
  • Logical right shift: clears sign bit if shift > 0.
  • Arithmetic left shift: same as logical left shift.
  • Arithmetic right shift: retains top bit.

Intel has both sal and shl, but they map to the same opcode and most other architectures seem to only have three mnemonics in the first place.

You could recast the grid in terms of sign-preserving vs not:

  • Logical + arithmetic left shift: not sign-preserving.
  • Logical right shift: not sign-preserving.
  • Arithmetic right shift: sign-preserving.

But this gives rise to a new possibility, a left shift that is sign-preserving. This would have the behavior of retaining the sign bit, but shifting the rest as normal.

And now I have a couple questions:

  1. Is this of any real-world use?
  2. Does any architecture implement this?

Edit: To clarify, what I mean by "sign-preserving" is this, for the example of a left shift of 1 and a bit width of 8:

Not sign-preserving:
S A B C D E F G
 / / / / / / /
A B C D E F G 0

Sign-preserving:
S A B C D E F G
|  / / / / / /
S B C D E F G 0

Hope this helps!


Footnote 1: Fun fact: x86 has a 2nd undocumented opcode for left shifts, e.g. d0 /4 and d0 /6 are both left-shift by 1 of a byte. Sandpile even claims that one is SHL and one is SAL, but the current Intel x86 manuals only document the /4 encoding for both SAL and SHL.

It's unclear if original 8086 was actually designed to use separate opcodes for the 2 names for the same operation, or if the alias opcode has always been undocumented. Whatever the history was, modern x86 no longer documents separate opcodes. (8 instructions share the same leading byte with the /r field distinguishing them: ROL/ROR, RCL/RCR, SHL/SHR, and SAL/SAR. Sticking some other non-shift instruction into the /6 encoding would have cost transistors, and making it fault would probably also have cost more transistors, so it makes sense that original 8086 was designed with a redundant encoding for left shifting, the only question is if/how it was documented historically.)

But this is totally tangential to the actual question: x86's SAL is not the sign-bit-preserving shift being asked about.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Claudia
  • 1,197
  • 15
  • 30
  • To clarify: for your *hypothetical* "arithmetic left shift", if we shift 0xC0 (11000000b) (on an 8-bit CPU) by one bit - what result do you expect? And what about 0x80 (10000000b)? – tum_ Jun 04 '19 at 08:32
  • 2
    x86 does *not* have separate *opcodes* for left shifts. The SAL and SHL *mnemonics* are synonyms for the same opcode. e.g. `D0 /4` for 8-bit SAL/SHL by 1. https://www.felixcloutier.com/x86/sal:sar:shl:shr. (With SSE vector shifts, and BMI2 flagless integer shifts, they no longer even defined an SALX mnemonic.) – Peter Cordes Jun 04 '19 at 08:45
  • The interest of shifts is that they realize a useful operation. Left shifts do a multiply by a power of 2 (if no overflow). Arithmetic and logic right shifts always do a divide by a power of two respectively for signed and unsigned. I do not see what useful operation would be done by your shift. – Alain Merigot Jun 04 '19 at 08:46
  • @tum_ Both would return 0x80 (1000_0000b) for my hypothetical sign-preserving left shift. From a hardware implementation standpoint, it'd pass through the sign bit unmodified and just perform a logical left shift of all but the sign bit, but that's just how it'd be implemented. – Claudia Jun 04 '19 at 08:46
  • 1
    @PeterCordes [Not from what I found](http://sandpile.org/x86/opc_grp.htm). I'll update the question to cite this. – Claudia Jun 04 '19 at 08:48
  • @AlainMerigot: on a 2's complement machine, it would give you a left shift that overflowed to `INT_MIN` instead of 0 when you shift out all the bits. Seems plausibly useful to me; saturating instructions have their uses. – Peter Cordes Jun 04 '19 at 08:48
  • 1
    _"Does any architecture implement this?"_ Depends on whether you're willing to count arithmetic left shift with saturation (e.g. `0x4000000 SHAS 1` would be 0x7FFFFFFF instead of 0x80000000). Several architectures have those, e.g. Infineon's TriCore and Analog Devices' Blackfin. – Michael Jun 04 '19 at 08:58
  • 1
    @IsiahMeadows: `d0 /6` is an undocumented opcode alias for `d0 /4`. Sandpile.org is incorrect in saying one is SAL and the other is SHL; Intel's current vol.2 reference manual [(pdf)](https://software.intel.com/sites/default/files/managed/a4/60/325383-sdm-vol-2abcd.pdf) doesn't document a `/6` in the ModRM for any of the `c0/c1/d0/etc` bytes that start a shift/rotate instruction encoding. Was that different historically? Because currently both SHL and SAL use a `/4` in the `/r` field. – Peter Cordes Jun 04 '19 at 08:59
  • Oh okay. I'll correct it then. – Claudia Jun 04 '19 at 09:01
  • @Michael This isn't asking about saturation, just sign preservation. – Claudia Jun 04 '19 at 09:02
  • Also, GAS, NASM, and YASM all encode both SAL and `SHL eax, 1` as `d1 e0`. I haven't checked other assemblers, but I'd consider it a bug if any chose the `/6` encoding. GNU binutils `objdump -d` does disassemble `d1 f0` as `shl eax,1` though, so tools do apparently know about this other encoding. – Peter Cordes Jun 04 '19 at 09:03
  • 1
    Maybe if can be useful to do a multiply by power of two on signed-absolute value representations. In which case, you also need a "right shift logical but preserve MSB" instruction to implement division. – Alain Merigot Jun 04 '19 at 09:07
  • _"This isn't asking about saturation, just sign preservation"_. Well, saturation is a means of sign preservation. – Michael Jun 04 '19 at 09:07
  • I edited it to clarify what I meant by "sign preservation", using a little bit of ASCII art. – Claudia Jun 04 '19 at 09:10
  • 2
    Michael knows the difference between what you're asking about vs. the `SHAS 1` instruction, but they're somewhat related. And now that it's pointed out, it's pretty clear that saturating to INT_MAX for positive is likely more useful than `0` in cases where saturating negative inputs to INT_MIN is also useful. Maybe there's some use-case for your idea, but I don't see one outside of maybe manipulating a value with a bitfield or flag at the top. (Maybe for manipulating an IEEE floating point bit pattern, which does use sign/magnitude? But as Alain says, you'd also want a bitfield right-shift) – Peter Cordes Jun 04 '19 at 09:24
  • Re: the `/6` encoding: [Why left shift instruction has two names in x86-64 ISA?](https://stackoverflow.com/q/70348306) is a new Q&A about that. Likely `/6` was never intended to be used, given the way Stephen Morse describes it in his book. Probably never officially documented in Intel manuals, either. – Peter Cordes Dec 14 '21 at 21:40

1 Answers1

2

The Intel 80960 shli instruction behaves this way. The description says, “if the bits shifted out are not the same as the sign bit, an overflow fault is generated. If overflow occurs, the sign of the result is the same as the sign of the src operand.” (The overflow fault can be masked. Even if it is not masked, the destination operand is written with the result.)

prl
  • 11,716
  • 2
  • 13
  • 31