x86 / x64 and the crazy conditional moves based on flags

Started by
9 comments, last by BloodOrange1981 9 years, 1 month ago

Evening!

I've been brushing up on some x64 for SIMD and SSE at the assembly level and I was wondering about conditional statements that execute on the carry flag, the parity flag etc

I understand that a conditional move based on equality "maps" to c or c++ code like

if(x != y){ cmp x,y

a = 24 movne a, 24

}

but what about operations based on register flags like the carry flag? I understand the use of the zero flag for loops etc but instructions like cmovc and cmovp have me puzzled how they can be used practically

Advertisement

I'm not sure if x86/64 CPUs actually use them this way, but if I remember correctly, some old microcontroller architectures I once coded on used the carry flag for > and < comparisons of unsigned integers. They subtracted one value from the other, and depending on which one was larger it would result in the carry flag being set or not, thereby giving you the result of the comparison. So you could do something like


sub A,B
jc label1   //jc = jump if carry is set
jmp label2

label1:
//B was greater than A!

label2:
//A was greater than or equal to B!

Could very well still be this way on modern architectures.

Not all CPU instructions have a corresponding command in C++.

Many instructions don't correspond at all. The CPU instruction for NOP, for example, doesn't exist in C++. CPU instructions for binary coded decimal don't have a parallel in C++. Hardware IO instructions like IN and OUT don't exist in C++. Cache control instructions are found on most modern processors and would be a great addition, but doesn't exist in C++.

Thankfully the inline assembly wrapper and the ability to link to external modules written in assembly allow C++ code to use the instructions.

Sometimes a few of these instructions get added as compiler-specific extensions and intrinsics. The SIMD instructions are a great example. They often trail several years behind the introduction of the processors. In some ways that is good, since it can take several years before new processor functions get out to everyone.

Basically you just need to recognize that the languages are different, and some concepts available in one language are not present in another language.

The compiler and optimizer might take advantage of the instructions if it knows about them, but other times the CPU instructions can be completely unused by a compiler. Other times the compiler may make use of instructions in unexpected ways, since people who write compiler optimizers can find all kinds of tricky tricks, using alternate instructions that are faster ore more efficient than the direct route. As an example, you'll never see a good x86 compiler knowingly move a zero into a register, they'll use "xor rax, rax" rather than "mov rax, 0". So there's that too.

You specifically call out the parity flag and carry flag. Since these date back to antiquity tongue.png the compiler writers know all about them, and have either found ways to exploit them for performance or ways to avoid them for performance. The ones you mentioned date back to the early 1990s, if there is a way to exploit them you can be certain the optimizer writers have explored them. Either way, used or not, they are well known.

The ones you mentioned date back to the early 1990s


Make that early 1980s.
My C64's 6510 (6502 variant) had them. At least the carry flag but not sure about parity.
The title is x86. The conditional move instructions were added in the Pentium Pro line. So 1995.
if(x != y){ cmp x,y
a = 24 movne a, 24
}

Are you sure you were reading about x86/x64? Because MOVNE is an ARM instruction...

if(x != y){ cmp x,y
a = 24 movne a, 24
}


Are you sure you were reading about x86/x64? Because MOVNE is an ARM instruction...


Well, x86's is (F)CMOVNE|Z, but we all still knew what he meant...


I understand that a conditional move based on equality "maps" to c or c++ code like

,,,

but what about operations based on register flags like the carry flag? I understand the use of the zero flag for loops etc but instructions like cmovc and cmovp have me puzzled how they can be used practically

You try to reason about assembler at the wrong abstraction level; machine instructions have a direct and simple correspondence with high level variables, arithmetic, loops etc. only in easy cases, and optimizations (like using conditional move instructions rather than jumps and plain moves) tend to depart from the easy cases.

Omae Wa Mou Shindeiru

There are also some assembly instructions which are redundant and only exist to make intentions behind handwritten assembly code a little more clear.

For example, jc translates to the same machine code as jb and jnae, but one tells the reader you test the flag while the others tell about testing the comparison of two numbers.

Evening!

I've been brushing up on some x64 for SIMD and SSE at the assembly level and I was wondering about conditional statements that execute on the carry flag, the parity flag etc

I understand that a conditional move based on equality "maps" to c or c++ code like

if(x != y){ cmp x,y

a = 24 movne a, 24

}

but what about operations based on register flags like the carry flag? I understand the use of the zero flag for loops etc but instructions like cmovc and cmovp have me puzzled how they can be used practically

To figure this out, you should consider what kinds of operations affect those other flags, what those flags mean, and what language constructs they map to.

For example, how does that MOVNE know whether or not to move? It checks the zero flag - CMP works by subtracting one operand form the other, setting CPU flags based on the result, and then discarding the result. So if x equals y, then y-x equals 0 and the CMP sets the Zero flag, but if x and y are not equal, then y-x is not 0 and the Zero flag is cleared. Then MOVNE checks the zero flag, and only moves 24 into a if the Zero flag is clear.

So to answer "how would you use the carry flag with a conditional move?" I ask you: what does the carry flag mean?

This topic is closed to new replies.

Advertisement