Commit 90aa356edc for qemu.org
commit 90aa356edcf1d7c1d4453269a0a8b0e667aa964f
Author: Richard Henderson <richard.henderson@linaro.org>
Date: Tue Aug 11 15:06:36 2026 -0700
tcg: Add integer min/max opcodes
We already have these for vectors; replicate for integers.
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst
index f2e9255dd9..88d48a1f99 100644
--- a/docs/devel/tcg-ops.rst
+++ b/docs/devel/tcg-ops.rst
@@ -317,6 +317,18 @@ Arithmetic
pass 0 to *nh* to make a simple zero-extension of *nl*,
so overflow should never occur.
+ * - smax *t0*, *t1*, *t2*
+
+ umax *t0*, *t1*, *t2*
+
+ - | *t0* = MAX(*t1*, *t2*), for signed and unsigned integers.
+
+ * - smin *t0*, *t1*, *t2*
+
+ umin *t0*, *t1*, *t2*
+
+ - | *t0* = MIN(*t1*, *t2*), for signed and unsigned integers.
+
Logical
-------
diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h
index 13c7f17f76..f3a81d5d7f 100644
--- a/include/tcg/tcg-opc.h
+++ b/include/tcg/tcg-opc.h
@@ -89,11 +89,15 @@ DEF(setcond, 1, 2, 1, TCG_OPF_INT)
DEF(sextract, 1, 1, 2, TCG_OPF_INT)
DEF(shl, 1, 2, 0, TCG_OPF_INT)
DEF(shr, 1, 2, 0, TCG_OPF_INT)
+DEF(smax, 1, 2, 0, TCG_OPF_INT)
+DEF(smin, 1, 2, 0, TCG_OPF_INT)
DEF(st8, 0, 2, 1, TCG_OPF_INT)
DEF(st16, 0, 2, 1, TCG_OPF_INT)
DEF(st32, 0, 2, 1, TCG_OPF_INT)
DEF(st, 0, 2, 1, TCG_OPF_INT)
DEF(sub, 1, 2, 0, TCG_OPF_INT)
+DEF(umax, 1, 2, 0, TCG_OPF_INT)
+DEF(umin, 1, 2, 0, TCG_OPF_INT)
DEF(xor, 1, 2, 0, TCG_OPF_INT)
DEF(addco, 1, 2, 0, TCG_OPF_INT | TCG_OPF_CARRY_OUT)
diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc
index 80995403e4..f5c185bbf4 100644
--- a/tcg/aarch64/tcg-target.c.inc
+++ b/tcg/aarch64/tcg-target.c.inc
@@ -2592,6 +2592,22 @@ static void tcg_out_set_borrow(TCGContext *s)
TCG_REG_XZR, TCG_REG_XZR, TCG_REG_XZR);
}
+static const TCGOutOpBinary outop_smax = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static const TCGOutOpBinary outop_smin = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static const TCGOutOpBinary outop_umax = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static const TCGOutOpBinary outop_umin = {
+ .base.static_constraint = C_NotImplemented,
+};
+
static void tgen_xor(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc
index 7d89e80886..f65496a040 100644
--- a/tcg/loongarch64/tcg-target.c.inc
+++ b/tcg/loongarch64/tcg-target.c.inc
@@ -1804,6 +1804,22 @@ static void tcg_out_set_borrow(TCGContext *s)
g_assert_not_reached();
}
+static const TCGOutOpBinary outop_smax = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static const TCGOutOpBinary outop_smin = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static const TCGOutOpBinary outop_umax = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static const TCGOutOpBinary outop_umin = {
+ .base.static_constraint = C_NotImplemented,
+};
+
static void tgen_xor(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
diff --git a/tcg/ppc64/tcg-target.c.inc b/tcg/ppc64/tcg-target.c.inc
index 07dff67e84..cd1c234367 100644
--- a/tcg/ppc64/tcg-target.c.inc
+++ b/tcg/ppc64/tcg-target.c.inc
@@ -3281,6 +3281,22 @@ static void tcg_out_set_borrow(TCGContext *s)
tcg_out32(s, ADDIC | TAI(TCG_REG_R0, TCG_REG_R0, 0));
}
+static const TCGOutOpBinary outop_smax = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static const TCGOutOpBinary outop_smin = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static const TCGOutOpBinary outop_umax = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static const TCGOutOpBinary outop_umin = {
+ .base.static_constraint = C_NotImplemented,
+};
+
static void tgen_xor(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
diff --git a/tcg/riscv64/tcg-target.c.inc b/tcg/riscv64/tcg-target.c.inc
index 8fd32644fe..723c21b3da 100644
--- a/tcg/riscv64/tcg-target.c.inc
+++ b/tcg/riscv64/tcg-target.c.inc
@@ -2404,6 +2404,22 @@ static void tcg_out_set_borrow(TCGContext *s)
g_assert_not_reached();
}
+static const TCGOutOpBinary outop_smax = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static const TCGOutOpBinary outop_smin = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static const TCGOutOpBinary outop_umax = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static const TCGOutOpBinary outop_umin = {
+ .base.static_constraint = C_NotImplemented,
+};
+
static void tgen_xor(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc
index c481745c3f..4d1a779c47 100644
--- a/tcg/s390x/tcg-target.c.inc
+++ b/tcg/s390x/tcg-target.c.inc
@@ -2950,6 +2950,22 @@ static void tcg_out_set_borrow(TCGContext *s)
tcg_out_insn(s, RR, CLR, TCG_REG_R0, TCG_REG_R0); /* cc = 0 */
}
+static const TCGOutOpBinary outop_smax = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static const TCGOutOpBinary outop_smin = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static const TCGOutOpBinary outop_umax = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static const TCGOutOpBinary outop_umin = {
+ .base.static_constraint = C_NotImplemented,
+};
+
static void tgen_xor(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc
index d6ed9d3362..35cd14a5b6 100644
--- a/tcg/sparc64/tcg-target.c.inc
+++ b/tcg/sparc64/tcg-target.c.inc
@@ -1917,6 +1917,22 @@ static void tcg_out_set_borrow(TCGContext *s)
tcg_out_set_carry(s); /* borrow == carry */
}
+static const TCGOutOpBinary outop_smax = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static const TCGOutOpBinary outop_smin = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static const TCGOutOpBinary outop_umax = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static const TCGOutOpBinary outop_umin = {
+ .base.static_constraint = C_NotImplemented,
+};
+
static void tgen_xor(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c
index c302a484cd..e28944cf72 100644
--- a/tcg/tcg-op.c
+++ b/tcg/tcg-op.c
@@ -1294,22 +1294,38 @@ void tcg_gen_revbit32_i32(TCGv_i32 ret, TCGv_i32 arg)
void tcg_gen_smin_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b)
{
- tcg_gen_movcond_i32(TCG_COND_LT, ret, a, b, a, b);
+ if (tcg_op_supported(INDEX_op_smin, TCG_TYPE_I32, 0)) {
+ tcg_gen_op3_i32(INDEX_op_smin, ret, a, b);
+ } else {
+ tcg_gen_movcond_i32(TCG_COND_LT, ret, a, b, a, b);
+ }
}
void tcg_gen_umin_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b)
{
- tcg_gen_movcond_i32(TCG_COND_LTU, ret, a, b, a, b);
+ if (tcg_op_supported(INDEX_op_umin, TCG_TYPE_I32, 0)) {
+ tcg_gen_op3_i32(INDEX_op_umin, ret, a, b);
+ } else {
+ tcg_gen_movcond_i32(TCG_COND_LTU, ret, a, b, a, b);
+ }
}
void tcg_gen_smax_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b)
{
- tcg_gen_movcond_i32(TCG_COND_LT, ret, a, b, b, a);
+ if (tcg_op_supported(INDEX_op_smax, TCG_TYPE_I32, 0)) {
+ tcg_gen_op3_i32(INDEX_op_smax, ret, a, b);
+ } else {
+ tcg_gen_movcond_i32(TCG_COND_LT, ret, a, b, b, a);
+ }
}
void tcg_gen_umax_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b)
{
- tcg_gen_movcond_i32(TCG_COND_LTU, ret, a, b, b, a);
+ if (tcg_op_supported(INDEX_op_umax, TCG_TYPE_I32, 0)) {
+ tcg_gen_op3_i32(INDEX_op_umax, ret, a, b);
+ } else {
+ tcg_gen_movcond_i32(TCG_COND_LTU, ret, a, b, b, a);
+ }
}
void tcg_gen_abs_i32(TCGv_i32 ret, TCGv_i32 a)
@@ -2473,22 +2489,38 @@ void tcg_gen_mulsu2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2)
void tcg_gen_smin_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b)
{
- tcg_gen_movcond_i64(TCG_COND_LT, ret, a, b, a, b);
+ if (tcg_op_supported(INDEX_op_smin, TCG_TYPE_I64, 0)) {
+ tcg_gen_op3_i64(INDEX_op_smin, ret, a, b);
+ } else {
+ tcg_gen_movcond_i64(TCG_COND_LT, ret, a, b, a, b);
+ }
}
void tcg_gen_umin_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b)
{
- tcg_gen_movcond_i64(TCG_COND_LTU, ret, a, b, a, b);
+ if (tcg_op_supported(INDEX_op_umin, TCG_TYPE_I64, 0)) {
+ tcg_gen_op3_i64(INDEX_op_umin, ret, a, b);
+ } else {
+ tcg_gen_movcond_i64(TCG_COND_LTU, ret, a, b, a, b);
+ }
}
void tcg_gen_smax_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b)
{
- tcg_gen_movcond_i64(TCG_COND_LT, ret, a, b, b, a);
+ if (tcg_op_supported(INDEX_op_smax, TCG_TYPE_I64, 0)) {
+ tcg_gen_op3_i64(INDEX_op_smax, ret, a, b);
+ } else {
+ tcg_gen_movcond_i64(TCG_COND_LT, ret, a, b, b, a);
+ }
}
void tcg_gen_umax_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b)
{
- tcg_gen_movcond_i64(TCG_COND_LTU, ret, a, b, b, a);
+ if (tcg_op_supported(INDEX_op_umax, TCG_TYPE_I64, 0)) {
+ tcg_gen_op3_i64(INDEX_op_umax, ret, a, b);
+ } else {
+ tcg_gen_movcond_i64(TCG_COND_LTU, ret, a, b, b, a);
+ }
}
void tcg_gen_abs_i64(TCGv_i64 ret, TCGv_i64 a)
diff --git a/tcg/tcg.c b/tcg/tcg.c
index 8a324ce885..489df0e738 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -1211,6 +1211,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = {
OUTOP(INDEX_op_sextract, TCGOutOpExtract, outop_sextract),
OUTOP(INDEX_op_shl, TCGOutOpBinary, outop_shl),
OUTOP(INDEX_op_shr, TCGOutOpBinary, outop_shr),
+ OUTOP(INDEX_op_smax, TCGOutOpBinary, outop_smax),
+ OUTOP(INDEX_op_smin, TCGOutOpBinary, outop_smin),
OUTOP(INDEX_op_st, TCGOutOpStore, outop_st),
OUTOP(INDEX_op_st8, TCGOutOpStore, outop_st8),
OUTOP(INDEX_op_st16, TCGOutOpStore, outop_st16),
@@ -1220,6 +1222,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = {
OUTOP(INDEX_op_subbo, TCGOutOpAddSubCarry, outop_subbo),
/* subb1o is implemented with set_borrow + subbio */
OUTOP(INDEX_op_subb1o, TCGOutOpAddSubCarry, outop_subbio),
+ OUTOP(INDEX_op_umax, TCGOutOpBinary, outop_umax),
+ OUTOP(INDEX_op_umin, TCGOutOpBinary, outop_umin),
OUTOP(INDEX_op_xor, TCGOutOpBinary, outop_xor),
[INDEX_op_goto_ptr] = &outop_goto_ptr,
@@ -5518,6 +5522,10 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op)
case INDEX_op_sar:
case INDEX_op_shl:
case INDEX_op_shr:
+ case INDEX_op_smax:
+ case INDEX_op_smin:
+ case INDEX_op_umax:
+ case INDEX_op_umin:
case INDEX_op_xor:
{
const TCGOutOpBinary *out =
diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc
index 1b61668517..4cd1c1431c 100644
--- a/tcg/tci/tcg-target.c.inc
+++ b/tcg/tci/tcg-target.c.inc
@@ -894,6 +894,22 @@ static void tcg_out_set_borrow(TCGContext *s)
tcg_out_op_v(s, INDEX_op_tci_setcarry); /* borrow == carry */
}
+static const TCGOutOpBinary outop_smax = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static const TCGOutOpBinary outop_smin = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static const TCGOutOpBinary outop_umax = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static const TCGOutOpBinary outop_umin = {
+ .base.static_constraint = C_NotImplemented,
+};
+
static void tgen_xor(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{
diff --git a/tcg/x86_64/tcg-target.c.inc b/tcg/x86_64/tcg-target.c.inc
index 37acba9045..2c8f1f3e58 100644
--- a/tcg/x86_64/tcg-target.c.inc
+++ b/tcg/x86_64/tcg-target.c.inc
@@ -2977,6 +2977,22 @@ static void tcg_out_set_borrow(TCGContext *s)
tcg_out8(s, OPC_STC);
}
+static const TCGOutOpBinary outop_smax = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static const TCGOutOpBinary outop_smin = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static const TCGOutOpBinary outop_umax = {
+ .base.static_constraint = C_NotImplemented,
+};
+
+static const TCGOutOpBinary outop_umin = {
+ .base.static_constraint = C_NotImplemented,
+};
+
static void tgen_xor(TCGContext *s, TCGType type,
TCGReg a0, TCGReg a1, TCGReg a2)
{