@samitouri / QOSamiQemu / commits / 90aa356edc

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>

Richard Henderson committed Aug 11, 2026 at 15:06 UTC 90aa356edcf1d7c1d4453269a0a8b0e667aa964f
12 files changed +192 -8
docs/devel/tcg-ops.rst
+12
@@ -317,6 +317,18 @@ Arithmetic
317 pass 0 to *nh* to make a simple zero-extension of *nl*,
318 so overflow should never occur.
319
320 + * - smax *t0*, *t1*, *t2*
321 +
322 + umax *t0*, *t1*, *t2*
323 +
324 + - | *t0* = MAX(*t1*, *t2*), for signed and unsigned integers.
325 +
326 + * - smin *t0*, *t1*, *t2*
327 +
328 + umin *t0*, *t1*, *t2*
329 +
330 + - | *t0* = MIN(*t1*, *t2*), for signed and unsigned integers.
331 +
332 Logical
333 -------
334
include/tcg/tcg-opc.h
+4
@@ -89,11 +89,15 @@ DEF(setcond, 1, 2, 1, TCG_OPF_INT)
89 DEF(sextract, 1, 1, 2, TCG_OPF_INT)
90 DEF(shl, 1, 2, 0, TCG_OPF_INT)
91 DEF(shr, 1, 2, 0, TCG_OPF_INT)
92 +DEF(smax, 1, 2, 0, TCG_OPF_INT)
93 +DEF(smin, 1, 2, 0, TCG_OPF_INT)
94 DEF(st8, 0, 2, 1, TCG_OPF_INT)
95 DEF(st16, 0, 2, 1, TCG_OPF_INT)
96 DEF(st32, 0, 2, 1, TCG_OPF_INT)
97 DEF(st, 0, 2, 1, TCG_OPF_INT)
98 DEF(sub, 1, 2, 0, TCG_OPF_INT)
99 +DEF(umax, 1, 2, 0, TCG_OPF_INT)
100 +DEF(umin, 1, 2, 0, TCG_OPF_INT)
101 DEF(xor, 1, 2, 0, TCG_OPF_INT)
102
103 DEF(addco, 1, 2, 0, TCG_OPF_INT | TCG_OPF_CARRY_OUT)
tcg/aarch64/tcg-target.c.inc
+16
@@ -2592,6 +2592,22 @@ static void tcg_out_set_borrow(TCGContext *s)
2592 TCG_REG_XZR, TCG_REG_XZR, TCG_REG_XZR);
2593 }
2594
2595 +static const TCGOutOpBinary outop_smax = {
2596 + .base.static_constraint = C_NotImplemented,
2597 +};
2598 +
2599 +static const TCGOutOpBinary outop_smin = {
2600 + .base.static_constraint = C_NotImplemented,
2601 +};
2602 +
2603 +static const TCGOutOpBinary outop_umax = {
2604 + .base.static_constraint = C_NotImplemented,
2605 +};
2606 +
2607 +static const TCGOutOpBinary outop_umin = {
2608 + .base.static_constraint = C_NotImplemented,
2609 +};
2610 +
2611 static void tgen_xor(TCGContext *s, TCGType type,
2612 TCGReg a0, TCGReg a1, TCGReg a2)
2613 {
tcg/loongarch64/tcg-target.c.inc
+16
@@ -1804,6 +1804,22 @@ static void tcg_out_set_borrow(TCGContext *s)
1804 g_assert_not_reached();
1805 }
1806
1807 +static const TCGOutOpBinary outop_smax = {
1808 + .base.static_constraint = C_NotImplemented,
1809 +};
1810 +
1811 +static const TCGOutOpBinary outop_smin = {
1812 + .base.static_constraint = C_NotImplemented,
1813 +};
1814 +
1815 +static const TCGOutOpBinary outop_umax = {
1816 + .base.static_constraint = C_NotImplemented,
1817 +};
1818 +
1819 +static const TCGOutOpBinary outop_umin = {
1820 + .base.static_constraint = C_NotImplemented,
1821 +};
1822 +
1823 static void tgen_xor(TCGContext *s, TCGType type,
1824 TCGReg a0, TCGReg a1, TCGReg a2)
1825 {
tcg/ppc64/tcg-target.c.inc
+16
@@ -3281,6 +3281,22 @@ static void tcg_out_set_borrow(TCGContext *s)
3281 tcg_out32(s, ADDIC | TAI(TCG_REG_R0, TCG_REG_R0, 0));
3282 }
3283
3284 +static const TCGOutOpBinary outop_smax = {
3285 + .base.static_constraint = C_NotImplemented,
3286 +};
3287 +
3288 +static const TCGOutOpBinary outop_smin = {
3289 + .base.static_constraint = C_NotImplemented,
3290 +};
3291 +
3292 +static const TCGOutOpBinary outop_umax = {
3293 + .base.static_constraint = C_NotImplemented,
3294 +};
3295 +
3296 +static const TCGOutOpBinary outop_umin = {
3297 + .base.static_constraint = C_NotImplemented,
3298 +};
3299 +
3300 static void tgen_xor(TCGContext *s, TCGType type,
3301 TCGReg a0, TCGReg a1, TCGReg a2)
3302 {
tcg/riscv64/tcg-target.c.inc
+16
@@ -2404,6 +2404,22 @@ static void tcg_out_set_borrow(TCGContext *s)
2404 g_assert_not_reached();
2405 }
2406
2407 +static const TCGOutOpBinary outop_smax = {
2408 + .base.static_constraint = C_NotImplemented,
2409 +};
2410 +
2411 +static const TCGOutOpBinary outop_smin = {
2412 + .base.static_constraint = C_NotImplemented,
2413 +};
2414 +
2415 +static const TCGOutOpBinary outop_umax = {
2416 + .base.static_constraint = C_NotImplemented,
2417 +};
2418 +
2419 +static const TCGOutOpBinary outop_umin = {
2420 + .base.static_constraint = C_NotImplemented,
2421 +};
2422 +
2423 static void tgen_xor(TCGContext *s, TCGType type,
2424 TCGReg a0, TCGReg a1, TCGReg a2)
2425 {
tcg/s390x/tcg-target.c.inc
+16
@@ -2950,6 +2950,22 @@ static void tcg_out_set_borrow(TCGContext *s)
2950 tcg_out_insn(s, RR, CLR, TCG_REG_R0, TCG_REG_R0); /* cc = 0 */
2951 }
2952
2953 +static const TCGOutOpBinary outop_smax = {
2954 + .base.static_constraint = C_NotImplemented,
2955 +};
2956 +
2957 +static const TCGOutOpBinary outop_smin = {
2958 + .base.static_constraint = C_NotImplemented,
2959 +};
2960 +
2961 +static const TCGOutOpBinary outop_umax = {
2962 + .base.static_constraint = C_NotImplemented,
2963 +};
2964 +
2965 +static const TCGOutOpBinary outop_umin = {
2966 + .base.static_constraint = C_NotImplemented,
2967 +};
2968 +
2969 static void tgen_xor(TCGContext *s, TCGType type,
2970 TCGReg a0, TCGReg a1, TCGReg a2)
2971 {
tcg/sparc64/tcg-target.c.inc
+16
@@ -1917,6 +1917,22 @@ static void tcg_out_set_borrow(TCGContext *s)
1917 tcg_out_set_carry(s); /* borrow == carry */
1918 }
1919
1920 +static const TCGOutOpBinary outop_smax = {
1921 + .base.static_constraint = C_NotImplemented,
1922 +};
1923 +
1924 +static const TCGOutOpBinary outop_smin = {
1925 + .base.static_constraint = C_NotImplemented,
1926 +};
1927 +
1928 +static const TCGOutOpBinary outop_umax = {
1929 + .base.static_constraint = C_NotImplemented,
1930 +};
1931 +
1932 +static const TCGOutOpBinary outop_umin = {
1933 + .base.static_constraint = C_NotImplemented,
1934 +};
1935 +
1936 static void tgen_xor(TCGContext *s, TCGType type,
1937 TCGReg a0, TCGReg a1, TCGReg a2)
1938 {
tcg/tcg-op.c
+40 -8
@@ -1294,22 +1294,38 @@ void tcg_gen_revbit32_i32(TCGv_i32 ret, TCGv_i32 arg)
1294
1295 void tcg_gen_smin_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b)
1296 {
1297 - tcg_gen_movcond_i32(TCG_COND_LT, ret, a, b, a, b);
1297 + if (tcg_op_supported(INDEX_op_smin, TCG_TYPE_I32, 0)) {
1298 + tcg_gen_op3_i32(INDEX_op_smin, ret, a, b);
1299 + } else {
1300 + tcg_gen_movcond_i32(TCG_COND_LT, ret, a, b, a, b);
1301 + }
1302 }
1303
1304 void tcg_gen_umin_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b)
1305 {
1302 - tcg_gen_movcond_i32(TCG_COND_LTU, ret, a, b, a, b);
1306 + if (tcg_op_supported(INDEX_op_umin, TCG_TYPE_I32, 0)) {
1307 + tcg_gen_op3_i32(INDEX_op_umin, ret, a, b);
1308 + } else {
1309 + tcg_gen_movcond_i32(TCG_COND_LTU, ret, a, b, a, b);
1310 + }
1311 }
1312
1313 void tcg_gen_smax_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b)
1314 {
1307 - tcg_gen_movcond_i32(TCG_COND_LT, ret, a, b, b, a);
1315 + if (tcg_op_supported(INDEX_op_smax, TCG_TYPE_I32, 0)) {
1316 + tcg_gen_op3_i32(INDEX_op_smax, ret, a, b);
1317 + } else {
1318 + tcg_gen_movcond_i32(TCG_COND_LT, ret, a, b, b, a);
1319 + }
1320 }
1321
1322 void tcg_gen_umax_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b)
1323 {
1312 - tcg_gen_movcond_i32(TCG_COND_LTU, ret, a, b, b, a);
1324 + if (tcg_op_supported(INDEX_op_umax, TCG_TYPE_I32, 0)) {
1325 + tcg_gen_op3_i32(INDEX_op_umax, ret, a, b);
1326 + } else {
1327 + tcg_gen_movcond_i32(TCG_COND_LTU, ret, a, b, b, a);
1328 + }
1329 }
1330
1331 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)
2489
2490 void tcg_gen_smin_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b)
2491 {
2476 - tcg_gen_movcond_i64(TCG_COND_LT, ret, a, b, a, b);
2492 + if (tcg_op_supported(INDEX_op_smin, TCG_TYPE_I64, 0)) {
2493 + tcg_gen_op3_i64(INDEX_op_smin, ret, a, b);
2494 + } else {
2495 + tcg_gen_movcond_i64(TCG_COND_LT, ret, a, b, a, b);
2496 + }
2497 }
2498
2499 void tcg_gen_umin_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b)
2500 {
2481 - tcg_gen_movcond_i64(TCG_COND_LTU, ret, a, b, a, b);
2501 + if (tcg_op_supported(INDEX_op_umin, TCG_TYPE_I64, 0)) {
2502 + tcg_gen_op3_i64(INDEX_op_umin, ret, a, b);
2503 + } else {
2504 + tcg_gen_movcond_i64(TCG_COND_LTU, ret, a, b, a, b);
2505 + }
2506 }
2507
2508 void tcg_gen_smax_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b)
2509 {
2486 - tcg_gen_movcond_i64(TCG_COND_LT, ret, a, b, b, a);
2510 + if (tcg_op_supported(INDEX_op_smax, TCG_TYPE_I64, 0)) {
2511 + tcg_gen_op3_i64(INDEX_op_smax, ret, a, b);
2512 + } else {
2513 + tcg_gen_movcond_i64(TCG_COND_LT, ret, a, b, b, a);
2514 + }
2515 }
2516
2517 void tcg_gen_umax_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b)
2518 {
2491 - tcg_gen_movcond_i64(TCG_COND_LTU, ret, a, b, b, a);
2519 + if (tcg_op_supported(INDEX_op_umax, TCG_TYPE_I64, 0)) {
2520 + tcg_gen_op3_i64(INDEX_op_umax, ret, a, b);
2521 + } else {
2522 + tcg_gen_movcond_i64(TCG_COND_LTU, ret, a, b, b, a);
2523 + }
2524 }
2525
2526 void tcg_gen_abs_i64(TCGv_i64 ret, TCGv_i64 a)
tcg/tcg.c
+8
@@ -1211,6 +1211,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = {
1211 OUTOP(INDEX_op_sextract, TCGOutOpExtract, outop_sextract),
1212 OUTOP(INDEX_op_shl, TCGOutOpBinary, outop_shl),
1213 OUTOP(INDEX_op_shr, TCGOutOpBinary, outop_shr),
1214 + OUTOP(INDEX_op_smax, TCGOutOpBinary, outop_smax),
1215 + OUTOP(INDEX_op_smin, TCGOutOpBinary, outop_smin),
1216 OUTOP(INDEX_op_st, TCGOutOpStore, outop_st),
1217 OUTOP(INDEX_op_st8, TCGOutOpStore, outop_st8),
1218 OUTOP(INDEX_op_st16, TCGOutOpStore, outop_st16),
@@ -1220,6 +1222,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = {
1222 OUTOP(INDEX_op_subbo, TCGOutOpAddSubCarry, outop_subbo),
1223 /* subb1o is implemented with set_borrow + subbio */
1224 OUTOP(INDEX_op_subb1o, TCGOutOpAddSubCarry, outop_subbio),
1225 + OUTOP(INDEX_op_umax, TCGOutOpBinary, outop_umax),
1226 + OUTOP(INDEX_op_umin, TCGOutOpBinary, outop_umin),
1227 OUTOP(INDEX_op_xor, TCGOutOpBinary, outop_xor),
1228
1229 [INDEX_op_goto_ptr] = &outop_goto_ptr,
@@ -5518,6 +5522,10 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op)
5522 case INDEX_op_sar:
5523 case INDEX_op_shl:
5524 case INDEX_op_shr:
5525 + case INDEX_op_smax:
5526 + case INDEX_op_smin:
5527 + case INDEX_op_umax:
5528 + case INDEX_op_umin:
5529 case INDEX_op_xor:
5530 {
5531 const TCGOutOpBinary *out =
tcg/tci/tcg-target.c.inc
+16
@@ -894,6 +894,22 @@ static void tcg_out_set_borrow(TCGContext *s)
894 tcg_out_op_v(s, INDEX_op_tci_setcarry); /* borrow == carry */
895 }
896
897 +static const TCGOutOpBinary outop_smax = {
898 + .base.static_constraint = C_NotImplemented,
899 +};
900 +
901 +static const TCGOutOpBinary outop_smin = {
902 + .base.static_constraint = C_NotImplemented,
903 +};
904 +
905 +static const TCGOutOpBinary outop_umax = {
906 + .base.static_constraint = C_NotImplemented,
907 +};
908 +
909 +static const TCGOutOpBinary outop_umin = {
910 + .base.static_constraint = C_NotImplemented,
911 +};
912 +
913 static void tgen_xor(TCGContext *s, TCGType type,
914 TCGReg a0, TCGReg a1, TCGReg a2)
915 {
tcg/x86_64/tcg-target.c.inc
+16
@@ -2977,6 +2977,22 @@ static void tcg_out_set_borrow(TCGContext *s)
2977 tcg_out8(s, OPC_STC);
2978 }
2979
2980 +static const TCGOutOpBinary outop_smax = {
2981 + .base.static_constraint = C_NotImplemented,
2982 +};
2983 +
2984 +static const TCGOutOpBinary outop_smin = {
2985 + .base.static_constraint = C_NotImplemented,
2986 +};
2987 +
2988 +static const TCGOutOpBinary outop_umax = {
2989 + .base.static_constraint = C_NotImplemented,
2990 +};
2991 +
2992 +static const TCGOutOpBinary outop_umin = {
2993 + .base.static_constraint = C_NotImplemented,
2994 +};
2995 +
2996 static void tgen_xor(TCGContext *s, TCGType type,
2997 TCGReg a0, TCGReg a1, TCGReg a2)
2998 {