[PATCH 13/29] tcg/i386: Support TCG_COND_TST{EQ,NE}

Richard Henderson posted 94 patches 1 year, 1 month ago
Maintainers: Richard Henderson <richard.henderson@linaro.org>, Paolo Bonzini <pbonzini@redhat.com>, Laurent Vivier <laurent@vivier.eu>, Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>, Artyom Tarasenko <atar4qemu@gmail.com>, WANG Xuerui <git@xen0n.name>, "Philippe Mathieu-Daudé" <philmd@linaro.org>, Aurelien Jarno <aurelien@aurel32.net>, Huacai Chen <chenhuacai@kernel.org>, Jiaxun Yang <jiaxun.yang@flygoat.com>, Aleksandar Rikalo <aleksandar.rikalo@syrmia.com>, Palmer Dabbelt <palmer@dabbelt.com>, Alistair Francis <Alistair.Francis@wdc.com>, Stefan Weil <sw@weilnetz.de>
There is a newer version of this series
[PATCH 13/29] tcg/i386: Support TCG_COND_TST{EQ,NE}
Posted by Richard Henderson 1 year, 1 month ago
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 tcg/i386/tcg-target.c.inc | 43 +++++++++++++++++++++++++++------------
 1 file changed, 30 insertions(+), 13 deletions(-)

diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc
index f4f456a2c0..0d97864174 100644
--- a/tcg/i386/tcg-target.c.inc
+++ b/tcg/i386/tcg-target.c.inc
@@ -504,6 +504,8 @@ static const uint8_t tcg_cond_to_jcc[] = {
     [TCG_COND_GEU] = JCC_JAE,
     [TCG_COND_LEU] = JCC_JBE,
     [TCG_COND_GTU] = JCC_JA,
+    [TCG_COND_TSTEQ] = JCC_JE,
+    [TCG_COND_TSTNE] = JCC_JNE,
 };
 
 #if TCG_TARGET_REG_BITS == 64
@@ -1419,12 +1421,14 @@ static void tcg_out_jxx(TCGContext *s, int opc, TCGLabel *l, bool small)
 }
 
 /* Test register R vs immediate bits I, setting Z flag for EQ/NE. */
-static void __attribute__((unused))
-tcg_out_testi(TCGContext *s, TCGReg r, uint32_t i, int rexw)
+static void tcg_out_testi(TCGContext *s, TCGReg r, uint32_t i, int rexw)
 {
     if (i <= 0xff && (TCG_TARGET_REG_BITS == 64 || r < 4)) {
         tcg_out_modrm(s, OPC_GRP3_Eb | P_REXB_RM, EXT3_TESTi, r);
         tcg_out8(s, i);
+    } else if ((i & ~0xff00) == 0 && r < 4) {
+        tcg_out_modrm(s, OPC_GRP3_Eb, EXT3_TESTi, r);
+        tcg_out8(s, i >> 8);
     } else {
         tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_TESTi, r);
         tcg_out32(s, i);
@@ -1434,15 +1438,25 @@ tcg_out_testi(TCGContext *s, TCGReg r, uint32_t i, int rexw)
 static int tcg_out_cmp(TCGContext *s, TCGCond cond, TCGArg arg1,
                        TCGArg arg2, int const_arg2, int rexw)
 {
-    if (const_arg2) {
-        if (arg2 == 0) {
-            /* test r, r */
+    if (is_tst_cond(cond)) {
+        if (!const_arg2) {
+            tcg_out_modrm(s, OPC_TESTL + rexw, arg1, arg2);
+        } else {
+            if (!rexw) {
+                arg2 = (uint32_t)arg2;
+            } else if ((arg2 >> 31 >> 1) == 0) {
+                rexw = 0;
+            }
+            tcg_out_testi(s, arg1, arg2, rexw);
+        }
+    } else {
+        if (!const_arg2) {
+            tgen_arithr(s, ARITH_CMP + rexw, arg1, arg2);
+        } else if (arg2 == 0) {
             tcg_out_modrm(s, OPC_TESTL + rexw, arg1, arg1);
         } else {
             tgen_arithi(s, ARITH_CMP + rexw, arg1, arg2, 0);
         }
-    } else {
-        tgen_arithr(s, ARITH_CMP + rexw, arg1, arg2);
     }
     return tcg_cond_to_jcc[cond];
 }
@@ -1461,18 +1475,21 @@ static void tcg_out_brcond2(TCGContext *s, const TCGArg *args,
 {
     TCGLabel *label_next = gen_new_label();
     TCGLabel *label_this = arg_label(args[5]);
+    TCGCond cond = args[4];
 
-    switch(args[4]) {
+    switch (cond) {
     case TCG_COND_EQ:
-        tcg_out_brcond(s, 0, TCG_COND_NE, args[0], args[2], const_args[2],
-                       label_next, 1);
-        tcg_out_brcond(s, 0, TCG_COND_EQ, args[1], args[3], const_args[3],
+    case TCG_COND_TSTEQ:
+        tcg_out_brcond(s, 0, tcg_invert_cond(cond),
+                       args[0], args[2], const_args[2], label_next, 1);
+        tcg_out_brcond(s, 0, cond, args[1], args[3], const_args[3],
                        label_this, small);
         break;
     case TCG_COND_NE:
-        tcg_out_brcond(s, 0, TCG_COND_NE, args[0], args[2], const_args[2],
+    case TCG_COND_TSTNE:
+        tcg_out_brcond(s, 0, cond, args[0], args[2], const_args[2],
                        label_this, small);
-        tcg_out_brcond(s, 0, TCG_COND_NE, args[1], args[3], const_args[3],
+        tcg_out_brcond(s, 0, cond, args[1], args[3], const_args[3],
                        label_this, small);
         break;
     case TCG_COND_LT:
-- 
2.34.1
Re: [PATCH 13/29] tcg/i386: Support TCG_COND_TST{EQ,NE}
Posted by Paolo Bonzini 1 year, 1 month ago
On 10/26/23 02:14, Richard Henderson wrote:
> 
> +    } else if ((i & ~0xff00) == 0 && r < 4) {
> +        tcg_out_modrm(s, OPC_GRP3_Eb, EXT3_TESTi, r);

Should be "r + 4".

Paolo

> +        tcg_out8(s, i >> 8);
>      } else {
Re: [PATCH 13/29] tcg/i386: Support TCG_COND_TST{EQ,NE}
Posted by Paolo Bonzini 1 year, 1 month ago
On 10/26/23 02:14, Richard Henderson wrote:
> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
> ---

Also, a TST{EQ,NE} with a one-bit immediate argument can be changed to:

- a TEST reg, reg + js/jns (or sets/setns, or cmovs/cmovns) when testing 
bits 7, 15 or 31

- a BT reg, imm + jc/jnc (or setc/setnc, or cmovc/cmovnc) when testing 
other bits in the 8..63 range.

I will take a look at using this to get rid of the mask field in 
CCPrepare, but I would not mind if someone else took a look at these 
code generation optimizations in tcg/i386.

Paolo

>   tcg/i386/tcg-target.c.inc | 43 +++++++++++++++++++++++++++------------
>   1 file changed, 30 insertions(+), 13 deletions(-)
> 
> diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc
> index f4f456a2c0..0d97864174 100644
> --- a/tcg/i386/tcg-target.c.inc
> +++ b/tcg/i386/tcg-target.c.inc
> @@ -504,6 +504,8 @@ static const uint8_t tcg_cond_to_jcc[] = {
>       [TCG_COND_GEU] = JCC_JAE,
>       [TCG_COND_LEU] = JCC_JBE,
>       [TCG_COND_GTU] = JCC_JA,
> +    [TCG_COND_TSTEQ] = JCC_JE,
> +    [TCG_COND_TSTNE] = JCC_JNE,
>   };
>   
>   #if TCG_TARGET_REG_BITS == 64
> @@ -1419,12 +1421,14 @@ static void tcg_out_jxx(TCGContext *s, int opc, TCGLabel *l, bool small)
>   }
>   
>   /* Test register R vs immediate bits I, setting Z flag for EQ/NE. */
> -static void __attribute__((unused))
> -tcg_out_testi(TCGContext *s, TCGReg r, uint32_t i, int rexw)
> +static void tcg_out_testi(TCGContext *s, TCGReg r, uint32_t i, int rexw)
>   {
>       if (i <= 0xff && (TCG_TARGET_REG_BITS == 64 || r < 4)) {
>           tcg_out_modrm(s, OPC_GRP3_Eb | P_REXB_RM, EXT3_TESTi, r);
>           tcg_out8(s, i);
> +    } else if ((i & ~0xff00) == 0 && r < 4) {
> +        tcg_out_modrm(s, OPC_GRP3_Eb, EXT3_TESTi, r);
> +        tcg_out8(s, i >> 8);
>       } else {
>           tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_TESTi, r);
>           tcg_out32(s, i);
> @@ -1434,15 +1438,25 @@ tcg_out_testi(TCGContext *s, TCGReg r, uint32_t i, int rexw)
>   static int tcg_out_cmp(TCGContext *s, TCGCond cond, TCGArg arg1,
>                          TCGArg arg2, int const_arg2, int rexw)
>   {
> -    if (const_arg2) {
> -        if (arg2 == 0) {
> -            /* test r, r */
> +    if (is_tst_cond(cond)) {
> +        if (!const_arg2) {
> +            tcg_out_modrm(s, OPC_TESTL + rexw, arg1, arg2);
> +        } else {
> +            if (!rexw) {
> +                arg2 = (uint32_t)arg2;
> +            } else if ((arg2 >> 31 >> 1) == 0) {
> +                rexw = 0;
> +            }
> +            tcg_out_testi(s, arg1, arg2, rexw);
> +        }
> +    } else {
> +        if (!const_arg2) {
> +            tgen_arithr(s, ARITH_CMP + rexw, arg1, arg2);
> +        } else if (arg2 == 0) {
>               tcg_out_modrm(s, OPC_TESTL + rexw, arg1, arg1);
>           } else {
>               tgen_arithi(s, ARITH_CMP + rexw, arg1, arg2, 0);
>           }
> -    } else {
> -        tgen_arithr(s, ARITH_CMP + rexw, arg1, arg2);
>       }
>       return tcg_cond_to_jcc[cond];
>   }
> @@ -1461,18 +1475,21 @@ static void tcg_out_brcond2(TCGContext *s, const TCGArg *args,
>   {
>       TCGLabel *label_next = gen_new_label();
>       TCGLabel *label_this = arg_label(args[5]);
> +    TCGCond cond = args[4];
>   
> -    switch(args[4]) {
> +    switch (cond) {
>       case TCG_COND_EQ:
> -        tcg_out_brcond(s, 0, TCG_COND_NE, args[0], args[2], const_args[2],
> -                       label_next, 1);
> -        tcg_out_brcond(s, 0, TCG_COND_EQ, args[1], args[3], const_args[3],
> +    case TCG_COND_TSTEQ:
> +        tcg_out_brcond(s, 0, tcg_invert_cond(cond),
> +                       args[0], args[2], const_args[2], label_next, 1);
> +        tcg_out_brcond(s, 0, cond, args[1], args[3], const_args[3],
>                          label_this, small);
>           break;
>       case TCG_COND_NE:
> -        tcg_out_brcond(s, 0, TCG_COND_NE, args[0], args[2], const_args[2],
> +    case TCG_COND_TSTNE:
> +        tcg_out_brcond(s, 0, cond, args[0], args[2], const_args[2],
>                          label_this, small);
> -        tcg_out_brcond(s, 0, TCG_COND_NE, args[1], args[3], const_args[3],
> +        tcg_out_brcond(s, 0, cond, args[1], args[3], const_args[3],
>                          label_this, small);
>           break;
>       case TCG_COND_LT:
Re: [PATCH 13/29] tcg/i386: Support TCG_COND_TST{EQ,NE}
Posted by Richard Henderson 1 year, 1 month ago
On 10/26/23 04:29, Paolo Bonzini wrote:
> On 10/26/23 02:14, Richard Henderson wrote:
>> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
>> ---
> 
> Also, a TST{EQ,NE} with a one-bit immediate argument can be changed to:
> 
> - a TEST reg, reg + js/jns (or sets/setns, or cmovs/cmovns) when testing bits 7, 15 or 31
> 
> - a BT reg, imm + jc/jnc (or setc/setnc, or cmovc/cmovnc) when testing other bits in the 
> 8..63 range.
> 
> I will take a look at using this to get rid of the mask field in CCPrepare, but I would 
> not mind if someone else took a look at these code generation optimizations in tcg/i386.

I thought about that while working on this series, and is part of the reason why 
tcg_out_cmp now returns a JCC_* value rather than having the caller look it up.

I thought I'd start simpler before adding these optimizations.


r~