3 ms·
I personally would be interested in a tour of "rep movsb", as I consider it one of the interesting (and enduring) features of x86. I figure this'll take you pre
by raphlinus 6y ago
I personally would be interested in a tour of "rep movsb", as I consider it one of the interesting (and enduring) features of x86. I figure this'll take you pretty deep into microcode, especially the register-dependent sequencing.
- messe 6y agoI've done my fair share of 8086 programming (mostly bootloader stuff and some DOS coding for fun), this is (along with the cmpsb, scasb, stosb, insb, lodsb, outsb instructions) probably the most CISCy instruction(s) the x86 has, especially when you consider that the rep prefix can use the ZF flag (repe, repne) to decide whether to repeat as well as the value in CX. I'd love to see a write-up on it.
- beagle3 6y agoThe 8088 (maybe 8086 too) had a bug where an interrupt during a “rep movsb” would return to the rep, so any prefixes were lost. You had to do e.g. “rep cs: movsb” instead of “cs: rep movsb” because the latter would switch to ds: on return from interrupt. Fun times.
- zwegner 6y agoThat would indeed be a pretty good topic to hear about. An interesting story related to rep movsb was how the original Pentium-based Larrabee supported gather/scatter, as described in this slide deck from Tom Forsyth: http://tomforsyth1000.github.io/papers/LRBNI%20origins%20v4%20full%20fat.pdf http://tomforsyth1000.github.io/papers/LRBNI%20origins%20v4%... (gather/scatter info on slides 48-61, important background about virtual memory/pipeline in slides 21-28) A summary: The original Pentium had a very simple pipeline that could only access one cache line per instruction, and had to determine if there was a page fault early in the pipeline. This presented a problem for the Larrabee design, which needed to support gathers/scatters (which are vectorized loads/stores, and can read/write up to 16 different cache lines in one instruction). Additionally, the Pentium microcode system was quite slow, and gather/scatter performance was very important for many workloads. To solve this, they looked at how rep movsb (which can read/write an arbitrary number of cache lines) works: as it executes, rep movsb modifies the cx, si, and di registers in place (or their 32/64-bit counterparts). This side effect actually helps keep the implementation simple, because if an interrupt occurs in the middle of the instruction, the registers keep all the state needed to continue executing after control returns to the instruction--it just keeps copying from where it left off. So gathers/scatters used the mask registers from the Larrabee vector instructions to keep state: a bit in the mask indicates that a vector lane still needs to be read/written. The instruction is then run in a manual loop (not a microcoded rep prefix) until all bits in the mask are cleared. Interestingly, this implementation can be faster than gather/scatter in a modern AVX-512 out-of-order core: whenever multiple vector lanes point to addresses in the same cache line, those loads/stores can all execute at once; in contrast, in the big cores gather/scatter are split into one load/store micro-op for each lane, that each execute independently. And the masking isn't taken into account before the uop split, so gathering with only a single address unmasked can still take more than 16 uops (see https://mobile.twitter.com/trav_downs/status/1223333226258759680 https://mobile.twitter.com/trav_downs/status/122333322625875...)
- monocasa 6y agoYou see similar constructs in about any system where the system both touches multiple addresses in a single instruction, and can take an interrupt somewhere in the middle of that instruction (whether that be an exception generated by the instruction, or just to keep external interrupt latency down). The lowly Cortex-M0 exposes the progress of load/store multiple instructions in architectural state so they can be restarted where they left off after an interrupt for example. They even do this with the multiplier too, so if you have the slow but tiny 32 cycle iterative multiplier in your design, you can still get single cycle interrupt latency. The M68ks had a halfway mechanism where it would just barf up partially documented internal microcode state onto the stack in a chip version specific manner on exceptions in the middle of instructions so you didn't restart the full instruction. Probably the grossest thing about that architecture.
- Taniwha 6y agoNot quite: The 68000 didn't do this, but couldn't restart a page fault. The 68010 didn't either (but could restart a page fault). The 68020 and 030 did do this horrible thing - doing Unix recursive signals was pretty hard if not impossible. And you couldn't copy this stuff to the user stack because it wasn't documented and so therefore you couldn't validate it when you pulled it back into the kernel. The 68040 was sane again (and I presume subsequent 68ks) Really this is part of the CISC vs. RISC thing - RISC instructions tend to have 1 side effect only, either they run to completion, or not at all, but CISC instructions can have multiple side effects - consider the infamous pdp11 instruction "mov -(pc), -(pc)" 3 side effects - 68k instructions are more complex multiple memory indirects, many possible faults, all that crud on the stack represents half done stuff