sub rdx,rax
sbb rcx,rcx
and rcx,rdx
add rax,rcx
See explanation
This snippet finds the minimum between the two values stored in
rax
and rdx
and stores the result in rcx
.
This is a branchless implementation, and as such it offers performance
benefits on architectures with prefetching and branch prediction
(like modern x86 CPUs). SSE introduces dedicated instructions, see
the section "Unsigned integer compares" in the
SSE: mind the gap
post.
sub rdx,rax
subtracts the value contained in rax
from the value
contained in rdx
, and stores the result in rdx
. It also sets CF
(carry flag) to 1 if there was carry, or in other words if rax
is
greater than rdx
.
sbb rcx,rcx
sets all the bits of rcx
to the value of CF
after
the preceding instruction. In other words it is set to either all
zeros, or all ones. We have seen how sbb
does it in the previous
riddle.
and rcx,rdx
sets rcx
to 0 if CF
was zero, or to the current
value of rdx
if CF
was 1, using rcx
as a mask, with the value
that was set at the previous step.
Remember that at this stage rdx
contains the difference between
the original values of rdx
and rax
, this will be relevant in the
next instruction.
add rax,rcx
sums rax
and rcx
and sets the result into rax
.
At this point rcx
contains either the initial value of rax
, or
the value of rdx
as seen above, so adding it up yields either the
initial value of rax
, or the initial value of rdx
This was the theory, but let's also see the practice with two examples.
Assume that the initial value of rax
is 3, and the initial value of
rdx
is 5. As said above this snippet computes the minimum of the two
values, and puts it in eax
.
- step 1:
sub rdx,rax
. This setsrdx
tordx - rax
= 5 - 3 = 2, and the carry flagCF
is set to 0. Now we haverax = 3
andrdx = 2
- step 2:
sbb rcx,rcx
. This setsrcx
to 0, becauseCF
is 0 - step 3:
and rcx,rdx
. This usesrcx
as a mask forrdx
, in fact leavingrcx
set to 0 - step 4:
add rax,rcx
. This setsrax
to the sum of the two operands, i.e. 3 + 0 = 3, that is the original value ofrax
.
Let's see the second example. We assume that rax
is set to 5 and rdx
is
set to 3. This will trigger the opposite behaviour of the previous example,
so we expect that the final value in rax
will be the initial value of
rdx
, 3.
- step 1:
sub rdx,rax
. This setsrdx
tordx - rax
= 3 - 5 = -2, and the carry flagCF
is set to 1. Now we haverdx = -2
(or 0xfffffffffffffffe), andrax = 5
- step 2:
sbb rcx,rcx
. This setsrcx
to 0xffffffffffffffff, becauseCF
is 1 - step 3:
and rcx,rdx
. This usesrcx
as a mask forrdx
, in fact settingrcx
to the value ofrdx
, that is 3 - step 4:
add rax,rcx
. This setsrax
to the sum of the two operands, i.e. 5 + (-2) = 3, that is the original value ofrdx
.