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
{