Skip to content

Commit

Permalink
tcg/i386: Rely on undefined/undocumented behaviour of BSF/BSR
Browse files Browse the repository at this point in the history
The ISA manual documents the output is undefined if the input was zero.

However, we document in target-i386 that the behavior of real silicon
is to preserve the contents of the output register.  We also mention
that there are real applications that depend on this.  That this is
baked into silicon is mentioned as a potential cause for some false
sharing behaviour wrt lzcnt/tzcnt.

Taking advantage of this allows us to save 2 insns in the normal case,
and 4 insns for i686 emulating a 64-bit clz.

Signed-off-by: Richard Henderson <rth@twiddle.net>
  • Loading branch information
rth7680 committed Jan 10, 2017
1 parent bbf25f9 commit 4ac7691
Showing 1 changed file with 22 additions and 13 deletions.
35 changes: 22 additions & 13 deletions tcg/i386/tcg-target.inc.c
Expand Up @@ -1146,9 +1146,12 @@ static void tcg_out_ctz(TCGContext *s, int rexw, TCGReg dest, TCGReg arg1,
tcg_debug_assert(arg2 == (rexw ? 64 : 32));
tcg_out_modrm(s, OPC_TZCNT + rexw, dest, arg1);
} else {
tcg_debug_assert(dest != arg2);
/* ??? The manual says that the output is undefined when the
input is zero, but real hardware leaves it unchanged. As
noted in target-i386/translate.c, real programs depend on
this -- now we are one more of those. */
tcg_debug_assert(dest == arg2);
tcg_out_modrm(s, OPC_BSF + rexw, dest, arg1);
tcg_out_cmov(s, TCG_COND_EQ, rexw, dest, arg2);
}
}

Expand All @@ -1161,20 +1164,26 @@ static void tcg_out_clz(TCGContext *s, int rexw, TCGReg dest, TCGReg arg1,
tcg_debug_assert(arg2 == (rexw ? 64 : 32));
} else {
tcg_debug_assert(dest != arg2);
/* LZCNT sets C if the input was zero. */
tcg_out_cmov(s, TCG_COND_LTU, rexw, dest, arg2);
}
} else {
tcg_debug_assert(!const_a2);
tcg_debug_assert(dest != arg1);
tcg_debug_assert(dest != arg2);
TCGType type = rexw ? TCG_TYPE_I64: TCG_TYPE_I32;
TCGArg rev = rexw ? 63 : 31;

/* Recall that the output of BSR is the index not the count. */
/* Recall that the output of BSR is the index not the count.
Therefore we must adjust the result by ^ (SIZE-1). In some
cases below, we prefer an extra XOR to a JMP. */
/* ??? See the comment in tcg_out_ctz re BSF. */
if (const_a2) {
tcg_debug_assert(dest != arg1);
tcg_out_movi(s, type, dest, arg2 ^ rev);
} else {
tcg_debug_assert(dest == arg2);
tgen_arithi(s, ARITH_XOR + rexw, dest, rev, 0);
}
tcg_out_modrm(s, OPC_BSR + rexw, dest, arg1);
tgen_arithi(s, ARITH_XOR + rexw, dest, rexw ? 63 : 31, 0);

/* Since we have destroyed the flags from BSR, we have to re-test. */
tcg_out_cmp(s, arg1, 0, 1, rexw);
tcg_out_cmov(s, TCG_COND_EQ, rexw, dest, arg2);
tgen_arithi(s, ARITH_XOR + rexw, dest, rev, 0);
}
}

Expand Down Expand Up @@ -2443,7 +2452,7 @@ static const TCGTargetOpDef *tcg_target_op_def(TCGOpcode op)
case INDEX_op_ctz_i64:
{
static const TCGTargetOpDef ctz[2] = {
{ .args_ct_str = { "&r", "r", "r" } },
{ .args_ct_str = { "r", "r", "0" } },
{ .args_ct_str = { "&r", "r", "rW" } },
};
return &ctz[have_bmi1];
Expand All @@ -2452,7 +2461,7 @@ static const TCGTargetOpDef *tcg_target_op_def(TCGOpcode op)
case INDEX_op_clz_i64:
{
static const TCGTargetOpDef clz[2] = {
{ .args_ct_str = { "&r", "r", "r" } },
{ .args_ct_str = { "&r", "r", "0i" } },
{ .args_ct_str = { "&r", "r", "rW" } },
};
return &clz[have_lzcnt];
Expand Down

0 comments on commit 4ac7691

Please sign in to comment.