Instead of assuming only small immediates are available for AND,
consult the backend in order to decide between SHL/SHR and SHR/AND.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
include/tcg/tcg.h | 2 ++
tcg/tcg-op.c | 30 ++++++++++++++----------------
tcg/tcg.c | 23 +++++++++++++++++++----
3 files changed, 35 insertions(+), 20 deletions(-)
diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h
index a6d9aa50d47..6ca5bf7f67b 100644
--- a/include/tcg/tcg.h
+++ b/include/tcg/tcg.h
@@ -795,6 +795,8 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags);
*/
bool tcg_op_deposit_valid(TCGType type, unsigned ofs, unsigned len);
+bool tcg_op_imm_match(TCGOpcode op, TCGType type, tcg_target_ulong imm);
+
void tcg_gen_call0(void *func, TCGHelperInfo *, TCGTemp *ret);
void tcg_gen_call1(void *func, TCGHelperInfo *, TCGTemp *ret, TCGTemp *);
void tcg_gen_call2(void *func, TCGHelperInfo *, TCGTemp *ret,
diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c
index ab7b409be61..ccf66382623 100644
--- a/tcg/tcg-op.c
+++ b/tcg/tcg-op.c
@@ -995,18 +995,17 @@ void tcg_gen_extract_i32(TCGv_i32 ret, TCGv_i32 arg,
return;
}
- /* ??? Ideally we'd know what values are available for immediate AND.
- Assume that 8 bits are available, plus the special case of 16,
- so that we get ext8u, ext16u. */
- switch (len) {
- case 1 ... 8: case 16:
+ /*
+ * Use TCG_TARGET_extract_valid to check for 8- and 16-bit extension
+ * opcodes, which tcg_gen_andi_i32 can produce.
+ */
+ if (TCG_TARGET_extract_valid(TCG_TYPE_I32, 0, len) ||
+ tcg_op_imm_match(INDEX_op_and, TCG_TYPE_I32, (1u << len) - 1)) {
tcg_gen_shri_i32(ret, arg, ofs);
tcg_gen_andi_i32(ret, ret, (1u << len) - 1);
- break;
- default:
+ } else {
tcg_gen_shli_i32(ret, arg, 32 - len - ofs);
tcg_gen_shri_i32(ret, ret, 32 - len);
- break;
}
}
@@ -2690,18 +2689,17 @@ void tcg_gen_extract_i64(TCGv_i64 ret, TCGv_i64 arg,
return;
}
- /* ??? Ideally we'd know what values are available for immediate AND.
- Assume that 8 bits are available, plus the special cases of 16 and 32,
- so that we get ext8u, ext16u, and ext32u. */
- switch (len) {
- case 1 ... 8: case 16: case 32:
+ /*
+ * Use TCG_TARGET_extract_valid to check for 8-, 16- and 32-bit extension
+ * opcodes, which tcg_gen_andi_i64 can produce.
+ */
+ if (TCG_TARGET_extract_valid(TCG_TYPE_I64, 0, len) ||
+ tcg_op_imm_match(INDEX_op_and, TCG_TYPE_I64, (1ull << len) - 1)) {
tcg_gen_shri_i64(ret, arg, ofs);
tcg_gen_andi_i64(ret, ret, (1ull << len) - 1);
- break;
- default:
+ } else {
tcg_gen_shli_i64(ret, arg, 64 - len - ofs);
tcg_gen_shri_i64(ret, ret, 64 - len);
- break;
}
}
diff --git a/tcg/tcg.c b/tcg/tcg.c
index fbf09f5c826..79ca49154a2 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -3509,11 +3509,8 @@ static void process_constraint_sets(void)
}
}
-static const TCGArgConstraint *opcode_args_ct(const TCGOp *op)
+static const TCGArgConstraint *op_args_ct(TCGOpcode opc, TCGType type, unsigned flags)
{
- TCGOpcode opc = op->opc;
- TCGType type = TCGOP_TYPE(op);
- unsigned flags = TCGOP_FLAGS(op);
const TCGOpDef *def = &tcg_op_defs[opc];
const TCGOutOp *outop = all_outop[opc];
TCGConstraintSetIndex con_set;
@@ -3540,6 +3537,24 @@ static const TCGArgConstraint *opcode_args_ct(const TCGOp *op)
return all_cts[con_set];
}
+static const TCGArgConstraint *opcode_args_ct(const TCGOp *op)
+{
+ return op_args_ct(op->opc, TCGOP_TYPE(op), TCGOP_FLAGS(op));
+}
+
+/* Return true if the backend can efficiently handle IMM as an immediate
+ for OP. */
+bool tcg_op_imm_match(TCGOpcode opc, TCGType type, tcg_target_ulong imm)
+{
+ const TCGOpDef * const def = &tcg_op_defs[opc];
+ const TCGArgConstraint *args_ct = op_args_ct(opc, type, 0);
+
+ tcg_debug_assert(def->nb_iargs == 2);
+ return tcg_target_const_match(
+ imm, args_ct[def->nb_oargs + 2].ct,
+ type, 0, 0);
+}
+
static void remove_label_use(TCGOp *op, int idx)
{
TCGLabel *label = arg_label(op->args[idx]);
--
2.52.0
On 1/15/26 13:54, Paolo Bonzini wrote:
> diff --git a/tcg/tcg.c b/tcg/tcg.c
> index fbf09f5c826..79ca49154a2 100644
> --- a/tcg/tcg.c
> +++ b/tcg/tcg.c
> @@ -3509,11 +3509,8 @@ static void process_constraint_sets(void)
> }
> }
>
> -static const TCGArgConstraint *opcode_args_ct(const TCGOp *op)
> +static const TCGArgConstraint *op_args_ct(TCGOpcode opc, TCGType type, unsigned flags)
> {
> - TCGOpcode opc = op->opc;
> - TCGType type = TCGOP_TYPE(op);
> - unsigned flags = TCGOP_FLAGS(op);
> const TCGOpDef *def = &tcg_op_defs[opc];
> const TCGOutOp *outop = all_outop[opc];
> TCGConstraintSetIndex con_set;
> @@ -3540,6 +3537,24 @@ static const TCGArgConstraint *opcode_args_ct(const TCGOp *op)
> return all_cts[con_set];
> }
>
> +static const TCGArgConstraint *opcode_args_ct(const TCGOp *op)
> +{
> + return op_args_ct(op->opc, TCGOP_TYPE(op), TCGOP_FLAGS(op));
> +}
> +
> +/* Return true if the backend can efficiently handle IMM as an immediate
> + for OP. */
> +bool tcg_op_imm_match(TCGOpcode opc, TCGType type, tcg_target_ulong imm)
> +{
> + const TCGOpDef * const def = &tcg_op_defs[opc];
> + const TCGArgConstraint *args_ct = op_args_ct(opc, type, 0);
> +
> + tcg_debug_assert(def->nb_iargs == 2);
> + return tcg_target_const_match(
> + imm, args_ct[def->nb_oargs + 2].ct,
> + type, 0, 0);
> +}
> +
> static void remove_label_use(TCGOp *op, int idx)
> {
> TCGLabel *label = arg_label(op->args[idx]);
Functionally this looks good, but there are a couple of coding standards
issues raised by scripts/checkpatch.pl: The definition of op_args_ct is
over 80 characters, and your block comment for tcg_op_imm_match should
use /* and */ on separate lines. If those are fixed I'm happy to approve it.
Jim
On Fri, Jan 16, 2026 at 12:38 PM Jim MacArthur <jim.macarthur@linaro.org> wrote:
>
> On 1/15/26 13:54, Paolo Bonzini wrote:
>
> diff --git a/tcg/tcg.c b/tcg/tcg.c
> index fbf09f5c826..79ca49154a2 100644
> --- a/tcg/tcg.c
> +++ b/tcg/tcg.c
> @@ -3509,11 +3509,8 @@ static void process_constraint_sets(void)
> }
> }
>
> -static const TCGArgConstraint *opcode_args_ct(const TCGOp *op)
> +static const TCGArgConstraint *op_args_ct(TCGOpcode opc, TCGType type, unsigned flags)
> {
> - TCGOpcode opc = op->opc;
> - TCGType type = TCGOP_TYPE(op);
> - unsigned flags = TCGOP_FLAGS(op);
> const TCGOpDef *def = &tcg_op_defs[opc];
> const TCGOutOp *outop = all_outop[opc];
> TCGConstraintSetIndex con_set;
> @@ -3540,6 +3537,24 @@ static const TCGArgConstraint *opcode_args_ct(const TCGOp *op)
> return all_cts[con_set];
> }
>
> +static const TCGArgConstraint *opcode_args_ct(const TCGOp *op)
> +{
> + return op_args_ct(op->opc, TCGOP_TYPE(op), TCGOP_FLAGS(op));
> +}
> +
> +/* Return true if the backend can efficiently handle IMM as an immediate
> + for OP. */
> +bool tcg_op_imm_match(TCGOpcode opc, TCGType type, tcg_target_ulong imm)
> +{
> + const TCGOpDef * const def = &tcg_op_defs[opc];
> + const TCGArgConstraint *args_ct = op_args_ct(opc, type, 0);
> +
> + tcg_debug_assert(def->nb_iargs == 2);
> + return tcg_target_const_match(
> + imm, args_ct[def->nb_oargs + 2].ct,
> + type, 0, 0);
> +}
> +
> static void remove_label_use(TCGOp *op, int idx)
> {
> TCGLabel *label = arg_label(op->args[idx]);
>
> Functionally this looks good, but there are a couple of coding standards issues raised by scripts/checkpatch.pl: The definition of op_args_ct is over 80 characters, and your block comment for tcg_op_imm_match should use /* and */ on separate lines. If those are fixed I'm happy to approve it.
tcg/tcg.c has several comments with that style, especially at the head
of functions (see temp_sync, temp_free_or_dead). I'll fix the 80
characters.
Paolo
© 2016 - 2026 Red Hat, Inc.