Since this pass already tracks bit permutations, we can extend it to
find bit reversing operations and emit the appropriate builtin.
gcc/ChangeLog:
* gimple-ssa-store-merging.cc (enum bitperm_operation):
Enumerate possible bit permutation operations.
(struct bswap_stat): Extend support to bitreverse.
(sym_rotate_right): New function.
(verify_symbolic_number_p): Update comment.
(perform_symbolic_merge): Likewise.
(find_bswap_or_nop_finalize): Rename to ...
(find_bitperm_or_nop_finalize): Generate bitreverse pattern too.
(is_bswap_or_nop_p): Rename to ...
(is_bitperm_or_nop_p): Recognize bitreverse as well, and handle
any cases with 8 or more bits (after masking).
(find_bswap_or_nop): Rename to ...
(find_bitperm_or_nop): Add bitreverse support by extending the
search depth limit, as well as support bitreverse rotations.
(bswap_replace): Rename to ...
(bitperm_replace): Support bitreverse by properly handling
operation types (like nop or bswap).
(maybe_optimize_vector_constructor): Likewise.
(pass_optimize_bswap::execute): Handle bitreverse builtins
and emit them when needed. Handle stats too.
(imm_store_chain_info::try_coalesce_bswap): Minor adjustments,
ignore bitreverse for this codepath.
(imm_store_chain_info::output_merged_store): Likewise.
* optabs-query.cc (can_open_code_p): Handle smaller or bigger
bitreverse operations similarly to bswap.
gcc/testsuite/ChangeLog:
* lib/target-supports.exp (check_effective_target_bitreverse):
New.
* gcc.dg/optimize-bitrevdi-1.c: New test.
* gcc.dg/optimize-bitrevdi-2.c: New test.
* gcc.dg/optimize-bitrevsi-1.c: New test.
* gcc.dg/optimize-bitrevsi-2.c: New test.
* gcc.dg/optimize-bitrevsi-3.c: New test.
* gcc.dg/optimize-bitrevsi-4.c: New test.
Signed-off-by: David Guillen Fandos <[email protected]>
---
gcc/gimple-ssa-store-merging.cc | 385 +++++++++++++--------
gcc/optabs-query.cc | 3 +-
gcc/testsuite/gcc.dg/optimize-bitrevdi-1.c | 21 ++
gcc/testsuite/gcc.dg/optimize-bitrevdi-2.c | 21 ++
gcc/testsuite/gcc.dg/optimize-bitrevsi-1.c | 20 ++
gcc/testsuite/gcc.dg/optimize-bitrevsi-2.c | 20 ++
gcc/testsuite/gcc.dg/optimize-bitrevsi-3.c | 21 ++
gcc/testsuite/gcc.dg/optimize-bitrevsi-4.c | 44 +++
gcc/testsuite/lib/target-supports.exp | 10 +
9 files changed, 408 insertions(+), 137 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/optimize-bitrevdi-1.c
create mode 100644 gcc/testsuite/gcc.dg/optimize-bitrevdi-2.c
create mode 100644 gcc/testsuite/gcc.dg/optimize-bitrevsi-1.c
create mode 100644 gcc/testsuite/gcc.dg/optimize-bitrevsi-2.c
create mode 100644 gcc/testsuite/gcc.dg/optimize-bitrevsi-3.c
create mode 100644 gcc/testsuite/gcc.dg/optimize-bitrevsi-4.c
diff --git a/gcc/gimple-ssa-store-merging.cc b/gcc/gimple-ssa-store-merging.cc
index b749c3c51e9..53671f32b24 100644
--- a/gcc/gimple-ssa-store-merging.cc
+++ b/gcc/gimple-ssa-store-merging.cc
@@ -181,17 +181,30 @@
namespace {
+enum bitperm_operation {
+ BITOP_NOP, /* No operation (bits/bytes are not permutated in any way). */
+ BITOP_BSWAP, /* Bytes are swapped. */
+ BITOP_BITREV, /* Bits are reversed. */
+ BITOP_LAST
+};
+
+static const char * const bitperm_names[] = {
+ "nop",
+ "bswap",
+ "bitreverse"
+};
+
struct bswap_stat
{
- /* Number of hand-written 16-bit nop / bswaps found. */
+ /* Number of hand-written 16-bit nop / bswaps / bitrev found. */
int found_16bit;
- /* Number of hand-written 32-bit nop / bswaps found. */
+ /* Number of hand-written 32-bit nop / bswaps / bitrev found. */
int found_32bit;
- /* Number of hand-written 64-bit nop / bswaps found. */
+ /* Number of hand-written 64-bit nop / bswaps / bitrev found. */
int found_64bit;
-} nop_stats, bswap_stats;
+} op_stats[BITOP_LAST];
/* A symbolic number structure is used to detect bit permutation and selection
patterns of a source. To achieve that, its array N contains a series of
@@ -248,6 +261,19 @@ sym_shift_right (uint8_t *n, unsigned int amount, uint8_t
fillvalue)
n[i] = i + amount < MAX_SYM_BITS ? n[amount + i] : fillvalue;
}
+/* Perform a right rotation on a symbolic_number by amount bits. */
+
+static void
+sym_rotate_right (uint8_t *dst, const uint8_t *src, unsigned int amount,
+ unsigned int size)
+{
+ for (unsigned int i = 0; i < MAX_SYM_BITS; i++)
+ if (i < size)
+ dst[i] = src[(i + amount) % size];
+ else
+ dst[i] = 0;
+}
+
static bool
sym_is_range_zero (const uint8_t *n, unsigned int start, unsigned int count)
{
@@ -325,7 +351,7 @@ verify_symbolic_number_p (struct symbolic_number *n, gimple
*stmt)
return true;
}
-/* Initialize the symbolic number N for the bswap pass from the base element
+/* Initialize the symbolic number N for the bitperm pass from the base element
SRC manipulated by the bitwise OR expression. */
bool
@@ -453,7 +479,7 @@ perform_symbolic_merge (gimple *source_stmt1, struct
symbolic_number *n1,
&& TREE_CODE (TREE_OPERAND (rhs2, 0)) == SSA_NAME)
rhs2 = TREE_OPERAND (rhs2, 0);
- /* Sources are different, cancel bswap if they are not memory location with
+ /* Sources are different, cancel bitperm if they are not memory location with
the same base (array, structure, ...). */
if (rhs1 != rhs2)
{
@@ -917,12 +943,13 @@ find_bswap_or_nop_2 (gimple *stmt, struct symbolic_number
*n, int limit)
return ins_stmt;
}
-/* Helper for find_bswap_or_nop and try_coalesce_bswap to compute
- *CMPXCHG, *CMPNOP and adjust *N. */
+/* Helper for find_bitperm_or_nop and try_coalesce_bswap to compute
+ *CMPXCHG, *CMPREV, *CMPNOP and adjust *N. */
void
-find_bswap_or_nop_finalize (struct symbolic_number *n, uint8_t *cmpxchg,
- uint8_t *cmpnop, bool *cast64_to_32)
+find_bitperm_or_nop_finalize (struct symbolic_number *n, uint8_t *cmpxchg,
+ uint8_t *cmprev, uint8_t *cmpnop,
+ bool *cast64_to_32)
{
unsigned i, rsize;
@@ -932,9 +959,11 @@ find_bswap_or_nop_finalize (struct symbolic_number *n,
uint8_t *cmpxchg,
for (i = 0; i < MAX_SYM_BITS; i++)
cmpxchg[i] = ((MAX_SYM_BITS / BITS_PER_UNIT - i / BITS_PER_UNIT - 1)
* BITS_PER_UNIT + i % BITS_PER_UNIT + 1);
-
+ for (i = 0; i < MAX_SYM_BITS; i++)
+ cmprev[i] = MAX_SYM_BITS - i;
for (i = 0; i < MAX_SYM_BITS; i++)
cmpnop[i] = i + 1;
+
*cast64_to_32 = false;
/* Find real size of result (highest non-zero byte). */
@@ -955,8 +984,9 @@ find_bswap_or_nop_finalize (struct symbolic_number *n,
uint8_t *cmpxchg,
&& n->range == 4
&& int_size_in_bytes (TREE_TYPE (n->src)) == 8)
{
- /* If all bytes in n->n are either 0 or in [5..8] range, this
- might be a candidate for (unsigned) __builtin_bswap64 (src).
+ /* If all bits in n->n are either 0 or in [33..64] range, this
+ might be a candidate for (unsigned) __builtin_bswap64 (src) or
+ (unsigned) __builtin_bitreverse64 (src).
It is not worth it for (unsigned short) __builtin_bswap64 (src)
or (unsigned short) __builtin_bswap32 (src). */
*cast64_to_32 = true;
@@ -970,9 +1000,12 @@ find_bswap_or_nop_finalize (struct symbolic_number *n,
uint8_t *cmpxchg,
}
if (*cast64_to_32)
for (i = n->range * BITS_PER_UNIT; i < MAX_SYM_BITS; i++)
- cmpxchg[i] = 0;
+ cmpxchg[i] = cmprev[i] = 0;
else
- sym_shift_right (cmpxchg, MAX_SYM_BITS - n->range * BITS_PER_UNIT, 0);
+ {
+ sym_shift_right (cmpxchg, MAX_SYM_BITS - n->range * BITS_PER_UNIT, 0);
+ sym_shift_right (cmprev, MAX_SYM_BITS - n->range * BITS_PER_UNIT, 0);
+ }
for (i = n->range * BITS_PER_UNIT; i < MAX_SYM_BITS; i++)
cmpnop[i] = 0;
@@ -986,11 +1019,12 @@ find_bswap_or_nop_finalize (struct symbolic_number *n,
uint8_t *cmpxchg,
{
sym_shift_right (cmpnop, (n->range - rsize) * BITS_PER_UNIT, 0);
for (i = rsize * BITS_PER_UNIT; i < MAX_SYM_BITS; i++)
- cmpxchg[i] = 0;
+ cmpxchg[i] = cmprev[i] = 0;
}
else
{
sym_shift_right (cmpxchg, (n->range - rsize) * BITS_PER_UNIT, 0);
+ sym_shift_right (cmprev, (n->range - rsize) * BITS_PER_UNIT, 0);
for (i = rsize * BITS_PER_UNIT; i < MAX_SYM_BITS; i++)
cmpnop[i] = 0;
}
@@ -1002,39 +1036,48 @@ find_bswap_or_nop_finalize (struct symbolic_number *n,
uint8_t *cmpxchg,
n->range *= BITS_PER_UNIT;
}
-/* Helper function for find_bswap_or_nop,
- Return true if N is a swap or nop with MASK. */
+/* Helper function for find_bitperm_or_nop,
+ Return true if N is a swap/rev/nop with MASK. */
static bool
-is_bswap_or_nop_p (const uint8_t *n, const uint8_t *cmpxchg,
- const uint8_t *cmpnop, uint64_t* mask,
- bool* bswap)
+is_bitperm_or_nop_p (const uint8_t *n, const uint8_t *cmpxchg,
+ const uint8_t *cmprev, const uint8_t *cmpnop,
+ uint64_t* mask, enum bitperm_operation* bitop)
{
*mask = ~(uint64_t) 0;
if (memcmp (n, cmpnop, MAX_SYM_BITS) == 0)
- *bswap = false;
+ *bitop = BITOP_NOP;
else if (memcmp (n, cmpxchg, MAX_SYM_BITS) == 0)
- *bswap = true;
+ *bitop = BITOP_BSWAP;
+ else if (memcmp (n, cmprev, MAX_SYM_BITS) == 0)
+ *bitop = BITOP_BITREV;
else
{
- int set = 0;
+ int set = 0, setxchg = 0, setrev = 0;
for (unsigned int i = 0; i < MAX_SYM_BITS; i++)
if (n[i] == 0)
*mask &= ~((uint64_t) 1 << i);
- else if (n[i] == cmpxchg[i])
- set++;
else
- return false;
+ {
+ set++;
+ if (n[i] == cmpxchg[i])
+ setxchg++;
+ if (n[i] == cmprev[i])
+ setrev++;
+ }
- if (set < 2 * BITS_PER_UNIT)
+ if (set == setxchg && set >= 2 * BITS_PER_UNIT)
+ *bitop = BITOP_BSWAP;
+ else if (set == setrev && set >= BITS_PER_UNIT)
+ *bitop = BITOP_BITREV;
+ else
return false;
- *bswap = true;
}
return true;
}
-/* Check if STMT completes a bswap implementation or a read in a given
- endianness consisting of ORs, SHIFTs and ANDs and sets *BSWAP
+/* Check if STMT completes a bit permutation or a read in a given
+ endianness consisting of ORs, SHIFTs and ANDs and sets *BITOP
accordingly. It also sets N to represent the kind of operations
performed: size of the resulting expression and whether it works on
a memory source, and if so alias-set and vuse. At last, the
@@ -1042,8 +1085,9 @@ is_bswap_or_nop_p (const uint8_t *n, const uint8_t
*cmpxchg,
expression. */
gimple *
-find_bswap_or_nop (gimple *stmt, struct symbolic_number *n, bool *bswap,
- bool *cast64_to_32, uint64_t *mask, uint64_t* l_rotate)
+find_bitperm_or_nop (gimple *stmt, struct symbolic_number *n,
+ enum bitperm_operation* bitop, bool *cast64_to_32,
+ uint64_t *mask, uint64_t* l_rotate)
{
tree type_size = TYPE_SIZE_UNIT (TREE_TYPE (gimple_get_lhs (stmt)));
if (!tree_fits_uhwi_p (type_size))
@@ -1054,15 +1098,15 @@ find_bswap_or_nop (gimple *stmt, struct symbolic_number
*n, bool *bswap,
increase that number by 2 * (log2(n) + 1) here in order to also
cover signed -> unsigned conversions of the src operand as can be seen
in libgcc, and for initial shift/and operation of the src operand. */
- int limit = tree_to_uhwi (type_size);
- limit += 2 * (1 + (int) ceil_log2 ((unsigned HOST_WIDE_INT) limit));
+ int numbits = tree_to_uhwi (type_size) * BITS_PER_UNIT;
+ int limit = 2 + 3 * (1 + (int) ceil_log2 ((unsigned HOST_WIDE_INT) numbits));
gimple *ins_stmt = find_bswap_or_nop_2 (stmt, n, limit);
if (!ins_stmt)
return NULL;
- uint8_t cmpxchg[MAX_SYM_BITS], cmpnop[MAX_SYM_BITS];
+ uint8_t cmpxchg[MAX_SYM_BITS], cmprev[MAX_SYM_BITS], cmpnop[MAX_SYM_BITS];
uint64_t orig_range = n->range * BITS_PER_UNIT;
- find_bswap_or_nop_finalize (n, cmpxchg, cmpnop, cast64_to_32);
+ find_bitperm_or_nop_finalize (n, cmpxchg, cmprev, cmpnop, cast64_to_32);
/* A complete byte swap should make the symbolic number to start with
the largest digit in the highest order byte. Unchanged symbolic
@@ -1070,9 +1114,9 @@ find_bswap_or_nop (gimple *stmt, struct symbolic_number
*n, bool *bswap,
*l_rotate = 0;
uint8_t tmp_n[MAX_SYM_BITS];
memcpy (tmp_n, n->n, MAX_SYM_BITS);
- if (!is_bswap_or_nop_p (tmp_n, cmpxchg, cmpnop, mask, bswap))
+ if (!is_bitperm_or_nop_p (tmp_n, cmpxchg, cmprev, cmpnop, mask, bitop))
{
- /* Try bswap + lrotate. */
+ /* Try bswap/bitreverse + lrotate. */
/* TODO, handle cast64_to_32 and big/litte_endian memory
source when rsize < range. */
if (n->range == orig_range
@@ -1084,37 +1128,55 @@ find_bswap_or_nop (gimple *stmt, struct symbolic_number
*n, bool *bswap,
|| (orig_range == 64
&& optab_handler (rotl_optab, DImode) != CODE_FOR_nothing)))
{
- unsigned int count = 0;
+ unsigned int swap_rot = 0, rev_rot = 0;
uint8_t tmp2_n[MAX_SYM_BITS];
/* Inspecting the lowest bit value, determine the rotation amount.
Handles some bits/bytes being zero (i.e. 0x203040506070800). */
for (unsigned int i = 0; i < orig_range; i++)
{
- unsigned int v, j;
if (tmp_n[i] == 0)
continue;
if (tmp_n[i] - 1u >= orig_range)
return NULL;
- v = tmp_n[i] - 1;
- j = (orig_range / BITS_PER_UNIT - 1 - v / BITS_PER_UNIT)
- * BITS_PER_UNIT + v % BITS_PER_UNIT;
- count = (i + orig_range - j) % orig_range;
+ /* Calculate the expected poistions of the LSB for swap/rev.
+ Then calculate the amount of rotation to get them there. */
+ unsigned int v = tmp_n[i] - 1;
+ unsigned int pswap = (orig_range / BITS_PER_UNIT - 1
+ - v / BITS_PER_UNIT) * BITS_PER_UNIT
+ + v % BITS_PER_UNIT;
+ unsigned int prev = orig_range - 1 - v;
+
+ swap_rot = (i + orig_range - pswap) % orig_range;
+ rev_rot = (i + orig_range - prev) % orig_range;
break;
}
- if (!count)
- return NULL;
-
- memcpy (tmp2_n, tmp_n, MAX_SYM_BITS);
- for (unsigned int i = 0; i < orig_range; i++)
- tmp2_n[i] = tmp_n[(i + count) % orig_range];
+ if (swap_rot)
+ {
+ sym_rotate_right (tmp2_n, tmp_n, swap_rot, orig_range);
+ if (is_bitperm_or_nop_p (tmp2_n, cmpxchg, cmprev, cmpnop, mask,
+ bitop))
+ {
+ *l_rotate = swap_rot;
+ gcc_assert (*bitop != BITOP_NOP);
+ return ins_stmt;
+ }
+ }
+ if (rev_rot)
+ {
+ sym_rotate_right (tmp2_n, tmp_n, rev_rot, orig_range);
+ if (is_bitperm_or_nop_p (tmp2_n, cmpxchg, cmprev, cmpnop, mask,
+ bitop))
+ {
+ *l_rotate = rev_rot;
+ gcc_assert (*bitop != BITOP_NOP);
+ return ins_stmt;
+ }
+ }
- if (!is_bswap_or_nop_p (tmp2_n, cmpxchg, cmpnop, mask, bswap))
- return NULL;
- *l_rotate = count;
- gcc_assert (*bswap);
+ return NULL;
}
else
return NULL;
@@ -1210,12 +1272,12 @@ bswap_view_convert (gimple_stmt_iterator *gsi, tree
type, tree val,
same VUSE which can lead to gsi_stmt (GSI) changing of basic block. */
tree
-bswap_replace (gimple_stmt_iterator gsi, gimple *ins_stmt, tree fndecl,
- tree bswap_type, tree load_type, struct symbolic_number *n,
- bool bswap, uint64_t mask, uint64_t l_rotate)
+bitperm_replace (gimple_stmt_iterator gsi, gimple *ins_stmt, tree fndecl,
+ tree bperm_type, tree load_type, struct symbolic_number *n,
+ enum bitperm_operation op, uint64_t mask, uint64_t l_rotate)
{
tree src, tmp, tgt = NULL_TREE;
- gimple *bswap_stmt, *mask_stmt = NULL, *rotl_stmt = NULL;
+ gimple *bitperm_stmt, *mask_stmt = NULL, *rotl_stmt = NULL;
tree_code conv_code = NOP_EXPR;
gimple *cur_stmt = gsi_stmt (gsi);
@@ -1296,16 +1358,16 @@ bswap_replace (gimple_stmt_iterator gsi, gimple
*ins_stmt, tree fndecl,
val_expr = fold_build2 (MEM_REF, aligned_load_type, addr_tmp,
load_offset_ptr);
- if (!bswap)
+ if (op == BITOP_NOP)
{
if (n->range == 16)
- nop_stats.found_16bit++;
+ op_stats[BITOP_NOP].found_16bit++;
else if (n->range == 32)
- nop_stats.found_32bit++;
+ op_stats[BITOP_NOP].found_32bit++;
else
{
gcc_assert (n->range == 64);
- nop_stats.found_64bit++;
+ op_stats[BITOP_NOP].found_64bit++;
}
/* Convert the result of load if necessary. */
@@ -1354,7 +1416,7 @@ bswap_replace (gimple_stmt_iterator gsi, gimple
*ins_stmt, tree fndecl,
}
src = val_tmp;
}
- else if (!bswap)
+ else if (op == BITOP_NOP)
{
gimple *g = NULL;
if (tgt && !useless_type_conversion_p (TREE_TYPE (tgt), TREE_TYPE (src)))
@@ -1370,13 +1432,13 @@ bswap_replace (gimple_stmt_iterator gsi, gimple
*ins_stmt, tree fndecl,
else
tgt = src;
if (n->range == 16)
- nop_stats.found_16bit++;
+ op_stats[BITOP_NOP].found_16bit++;
else if (n->range == 32)
- nop_stats.found_32bit++;
+ op_stats[BITOP_NOP].found_32bit++;
else
{
gcc_assert (n->range == 64);
- nop_stats.found_64bit++;
+ op_stats[BITOP_NOP].found_64bit++;
}
if (dump_file)
{
@@ -1399,23 +1461,23 @@ bswap_replace (gimple_stmt_iterator gsi, gimple
*ins_stmt, tree fndecl,
src = TREE_OPERAND (src, 0);
if (n->range == 16)
- bswap_stats.found_16bit++;
+ op_stats[op].found_16bit++;
else if (n->range == 32)
- bswap_stats.found_32bit++;
+ op_stats[op].found_32bit++;
else
{
gcc_assert (n->range == 64);
- bswap_stats.found_64bit++;
+ op_stats[op].found_64bit++;
}
tmp = src;
/* Convert the src expression if necessary. */
- if (!useless_type_conversion_p (TREE_TYPE (tmp), bswap_type))
+ if (!useless_type_conversion_p (TREE_TYPE (tmp), bperm_type))
{
gimple *convert_stmt;
- tmp = make_temp_ssa_name (bswap_type, NULL, "bswapsrc");
+ tmp = make_temp_ssa_name (bperm_type, NULL, "bswapsrc");
convert_stmt = gimple_build_assign (tmp, NOP_EXPR, src);
gsi_insert_before (&gsi, convert_stmt, GSI_SAME_STMT);
}
@@ -1424,42 +1486,42 @@ bswap_replace (gimple_stmt_iterator gsi, gimple
*ins_stmt, tree fndecl,
are considered as rotation of 2N bit values by N bits is generally not
equivalent to a bswap. Consider for instance 0x01020304 r>> 16 which
gives 0x03040102 while a bswap for that value is 0x04030201. */
- if (bswap && n->range == 16)
+ if (op == BITOP_BSWAP && n->range == 16)
{
tree count = build_int_cst (integer_type_node, BITS_PER_UNIT);
- src = fold_build2 (LROTATE_EXPR, bswap_type, tmp, count);
- bswap_stmt = gimple_build_assign (NULL, src);
+ src = fold_build2 (LROTATE_EXPR, bperm_type, tmp, count);
+ bitperm_stmt = gimple_build_assign (NULL, src);
}
else
- bswap_stmt = gimple_build_call (fndecl, 1, tmp);
+ bitperm_stmt = gimple_build_call (fndecl, 1, tmp);
if (tgt == NULL_TREE)
- tgt = make_ssa_name (bswap_type);
+ tgt = make_ssa_name (bperm_type);
tmp = tgt;
if (mask != ~(uint64_t) 0)
{
- tree m = build_int_cst (bswap_type, mask);
- tmp = make_temp_ssa_name (bswap_type, NULL, "bswapdst");
- gimple_set_lhs (bswap_stmt, tmp);
+ tree m = build_int_cst (bperm_type, mask);
+ tmp = make_temp_ssa_name (bperm_type, NULL, "bswapdst");
+ gimple_set_lhs (bitperm_stmt, tmp);
mask_stmt = gimple_build_assign (tgt, BIT_AND_EXPR, tmp, m);
tmp = tgt;
}
if (l_rotate)
{
- tree m = build_int_cst (bswap_type, l_rotate);
- tmp = make_temp_ssa_name (bswap_type, NULL,
+ tree m = build_int_cst (bperm_type, l_rotate);
+ tmp = make_temp_ssa_name (bperm_type, NULL,
mask_stmt ? "bswapmaskdst" : "bswapdst");
- gimple_set_lhs (mask_stmt ? mask_stmt : bswap_stmt, tmp);
+ gimple_set_lhs (mask_stmt ? mask_stmt : bitperm_stmt, tmp);
rotl_stmt = gimple_build_assign (tgt, LROTATE_EXPR, tmp, m);
tmp = tgt;
}
/* Convert the result if necessary. */
- if (!useless_type_conversion_p (TREE_TYPE (tgt), bswap_type))
+ if (!useless_type_conversion_p (TREE_TYPE (tgt), bperm_type))
{
- tmp = make_temp_ssa_name (bswap_type, NULL, "bswapdst");
+ tmp = make_temp_ssa_name (bperm_type, NULL, "bswapdst");
tree atmp = tmp;
gimple_stmt_iterator gsi2 = gsi;
if (conv_code == VIEW_CONVERT_EXPR)
@@ -1469,12 +1531,12 @@ bswap_replace (gimple_stmt_iterator gsi, gimple
*ins_stmt, tree fndecl,
}
gimple_set_lhs (rotl_stmt ? rotl_stmt
- : mask_stmt ? mask_stmt : bswap_stmt, tmp);
+ : mask_stmt ? mask_stmt : bitperm_stmt, tmp);
if (dump_file)
{
- fprintf (dump_file, "%d bit bswap implementation found at: ",
- (int) n->range);
+ fprintf (dump_file, "%d bit %s implementation found at: ",
+ (int) n->range, bitperm_names[op]);
if (cur_stmt)
print_gimple_stmt (dump_file, cur_stmt, 0);
else
@@ -1490,12 +1552,12 @@ bswap_replace (gimple_stmt_iterator gsi, gimple
*ins_stmt, tree fndecl,
gsi_insert_after (&gsi, rotl_stmt, GSI_SAME_STMT);
if (mask_stmt)
gsi_insert_after (&gsi, mask_stmt, GSI_SAME_STMT);
- gsi_insert_after (&gsi, bswap_stmt, GSI_SAME_STMT);
+ gsi_insert_after (&gsi, bitperm_stmt, GSI_SAME_STMT);
gsi_remove (&gsi, true);
}
else
{
- gsi_insert_before (&gsi, bswap_stmt, GSI_SAME_STMT);
+ gsi_insert_before (&gsi, bitperm_stmt, GSI_SAME_STMT);
if (mask_stmt)
gsi_insert_before (&gsi, mask_stmt, GSI_SAME_STMT);
if (rotl_stmt)
@@ -1514,7 +1576,6 @@ maybe_optimize_vector_constructor (gimple *cur_stmt)
{
tree fndecl = NULL_TREE, bswap_type = NULL_TREE, load_type;
struct symbolic_number n;
- bool bswap;
gcc_assert (is_gimple_assign (cur_stmt));
switch (gimple_assign_rhs_code (cur_stmt))
@@ -1564,24 +1625,25 @@ maybe_optimize_vector_constructor (gimple *cur_stmt)
return false;
}
+ enum bitperm_operation bitop;
bool cast64_to_32;
uint64_t mask, l_rotate;
- gimple *ins_stmt = find_bswap_or_nop (cur_stmt, &n, &bswap,
- &cast64_to_32, &mask, &l_rotate);
+ gimple *ins_stmt = find_bitperm_or_nop (cur_stmt, &n, &bitop,
+ &cast64_to_32, &mask, &l_rotate);
if (!ins_stmt
|| n.range != (unsigned HOST_WIDE_INT) sz
|| cast64_to_32
+ || bitop == BITOP_BITREV
|| mask != ~(uint64_t) 0)
return false;
- if (bswap && !fndecl && n.range != 16)
+ if (bitop == BITOP_BSWAP && !fndecl && n.range != 16)
return false;
- memset (&nop_stats, 0, sizeof (nop_stats));
- memset (&bswap_stats, 0, sizeof (bswap_stats));
- return bswap_replace (gsi_for_stmt (cur_stmt), ins_stmt, fndecl,
- bswap_type, load_type, &n, bswap, mask,
- l_rotate) != NULL_TREE;
+ memset (&op_stats[0], 0, sizeof (op_stats));
+ return bitperm_replace (gsi_for_stmt (cur_stmt), ins_stmt, fndecl,
+ bswap_type, load_type, &n, bitop, mask,
+ l_rotate) != NULL_TREE;
}
/* Find manual byte swap implementations as well as load in a given
@@ -1594,14 +1656,24 @@ pass_optimize_bswap::execute (function *fun)
{
basic_block bb;
bool bswap32_p, bswap64_p;
+ bool bitrev16_p, bitrev32_p, bitrev64_p;
bool changed = false;
tree bswap32_type = NULL_TREE, bswap64_type = NULL_TREE;
+ tree bitrev16_type = NULL_TREE;
+ tree bitrev32_type = NULL_TREE, bitrev64_type = NULL_TREE;
bswap32_p = (builtin_decl_explicit_p (BUILT_IN_BSWAP32)
&& can_open_code_p (bswap_optab, SImode));
bswap64_p = (builtin_decl_explicit_p (BUILT_IN_BSWAP64)
&& can_open_code_p (bswap_optab, DImode));
+ bitrev16_p = (builtin_decl_explicit_p (BUILT_IN_BITREVERSE16)
+ && can_open_code_p (bitreverse_optab, HImode));
+ bitrev32_p = (builtin_decl_explicit_p (BUILT_IN_BITREVERSE32)
+ && can_open_code_p (bitreverse_optab, SImode));
+ bitrev64_p = (builtin_decl_explicit_p (BUILT_IN_BITREVERSE64)
+ && can_open_code_p (bitreverse_optab, DImode));
+
/* Determine the argument type of the builtins. The code later on
assumes that the return and argument type are the same. */
if (bswap32_p)
@@ -1616,30 +1688,40 @@ pass_optimize_bswap::execute (function *fun)
bswap64_type = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (fndecl)));
}
- memset (&nop_stats, 0, sizeof (nop_stats));
- memset (&bswap_stats, 0, sizeof (bswap_stats));
+ if (bitrev16_p)
+ bitrev16_type = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (
+ builtin_decl_explicit (BUILT_IN_BITREVERSE16))));
+
+ if (bitrev32_p)
+ bitrev32_type = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (
+ builtin_decl_explicit (BUILT_IN_BITREVERSE32))));
+
+ if (bitrev64_p)
+ bitrev64_type = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (
+ builtin_decl_explicit (BUILT_IN_BITREVERSE64))));
+
+
+ memset (&op_stats[0], 0, sizeof (op_stats));
calculate_dominance_info (CDI_DOMINATORS);
FOR_EACH_BB_FN (bb, fun)
{
gimple_stmt_iterator gsi;
- /* We do a reverse scan for bswap patterns to make sure we get the
- widest match. As bswap pattern matching doesn't handle previously
- inserted smaller bswap replacements as sub-patterns, the wider
+ /* We do a reverse scan for bitperm patterns to make sure we get the
+ widest match. As bitperm pattern matching doesn't handle previously
+ inserted smaller bitperm replacements as sub-patterns, the wider
variant wouldn't be detected. */
for (gsi = gsi_last_bb (bb); !gsi_end_p (gsi);)
{
gimple *ins_stmt, *cur_stmt = gsi_stmt (gsi);
- tree fndecl = NULL_TREE, bswap_type = NULL_TREE, load_type;
+ tree fndecl = NULL_TREE, bitperm_type = NULL_TREE, load_type;
enum tree_code code;
struct symbolic_number n;
- bool bswap, cast64_to_32;
- uint64_t mask, l_rotate;
/* This gsi_prev (&gsi) is not part of the for loop because cur_stmt
- might be moved to a different basic block by bswap_replace and gsi
- must not points to it if that's the case. Moving the gsi_prev
+ might be moved to a different basic block by bitperm_replace and
+ gsi must not points to it if that's the case. Moving the gsi_prev
there make sure that gsi points to the statement previous to
cur_stmt while still making sure that all statements are
considered in this basic block. */
@@ -1675,8 +1757,11 @@ pass_optimize_bswap::execute (function *fun)
continue;
}
- ins_stmt = find_bswap_or_nop (cur_stmt, &n, &bswap,
- &cast64_to_32, &mask, &l_rotate);
+ enum bitperm_operation bitop;
+ uint64_t mask, l_rotate;
+ bool cast64_to_32;
+ ins_stmt = find_bitperm_or_nop (cur_stmt, &n, &bitop,
+ &cast64_to_32, &mask, &l_rotate);
if (!ins_stmt)
continue;
@@ -1684,53 +1769,80 @@ pass_optimize_bswap::execute (function *fun)
switch (n.range)
{
case 16:
- /* Already in canonical form, nothing to do. */
- if (code == LROTATE_EXPR || code == RROTATE_EXPR)
- continue;
- load_type = bswap_type = uint16_type_node;
+ load_type = bitperm_type = uint16_type_node;
+ if (bitop == BITOP_BSWAP)
+ {
+ /* Already in canonical form, nothing to do. */
+ if (code == LROTATE_EXPR || code == RROTATE_EXPR)
+ continue;
+ }
+ else if (bitop == BITOP_BITREV && bitrev16_p)
+ {
+ fndecl = builtin_decl_explicit (BUILT_IN_BITREVERSE16);
+ bitperm_type = bitrev16_type;
+ }
break;
case 32:
- load_type = uint32_type_node;
- if (bswap32_p)
+ load_type = bitperm_type = uint32_type_node;
+ if (bitop == BITOP_BSWAP && bswap32_p)
{
fndecl = builtin_decl_explicit (BUILT_IN_BSWAP32);
- bswap_type = bswap32_type;
+ bitperm_type = bswap32_type;
+ }
+ else if (bitop == BITOP_BITREV && bitrev32_p)
+ {
+ fndecl = builtin_decl_explicit (BUILT_IN_BITREVERSE32);
+ bitperm_type = bitrev32_type;
}
break;
case 64:
- load_type = uint64_type_node;
- if (bswap64_p)
+ load_type = bitperm_type = uint64_type_node;
+ if (bitop == BITOP_BSWAP && bswap64_p)
{
fndecl = builtin_decl_explicit (BUILT_IN_BSWAP64);
- bswap_type = bswap64_type;
+ bitperm_type = bswap64_type;
+ }
+ else if (bitop == BITOP_BITREV && bitrev64_p)
+ {
+ fndecl = builtin_decl_explicit (BUILT_IN_BITREVERSE64);
+ bitperm_type = bitrev64_type;
}
break;
default:
continue;
}
- if (bswap && !fndecl && n.range != 16)
+ /* Bail if we don't have a proper function to call. */
+ if (!fndecl
+ && ((bitop == BITOP_BSWAP && n.range != 16)
+ || bitop == BITOP_BITREV))
continue;
- if (bswap_replace (gsi_for_stmt (cur_stmt), ins_stmt, fndecl,
- bswap_type, load_type, &n, bswap, mask,
- l_rotate))
+ if (bitperm_replace (gsi_for_stmt (cur_stmt), ins_stmt, fndecl,
+ bitperm_type, load_type, &n, bitop, mask,
+ l_rotate))
changed = true;
}
}
statistics_counter_event (fun, "16-bit nop implementations found",
- nop_stats.found_16bit);
+ op_stats[BITOP_NOP].found_16bit);
statistics_counter_event (fun, "32-bit nop implementations found",
- nop_stats.found_32bit);
+ op_stats[BITOP_NOP].found_32bit);
statistics_counter_event (fun, "64-bit nop implementations found",
- nop_stats.found_64bit);
+ op_stats[BITOP_NOP].found_64bit);
statistics_counter_event (fun, "16-bit bswap implementations found",
- bswap_stats.found_16bit);
+ op_stats[BITOP_BSWAP].found_16bit);
statistics_counter_event (fun, "32-bit bswap implementations found",
- bswap_stats.found_32bit);
+ op_stats[BITOP_BSWAP].found_32bit);
statistics_counter_event (fun, "64-bit bswap implementations found",
- bswap_stats.found_64bit);
+ op_stats[BITOP_BSWAP].found_64bit);
+ statistics_counter_event (fun, "16-bit bitreverse implementations found",
+ op_stats[BITOP_BITREV].found_16bit);
+ statistics_counter_event (fun, "32-bit bitreverse implementations found",
+ op_stats[BITOP_BITREV].found_32bit);
+ statistics_counter_event (fun, "64-bit bitreverse implementations found",
+ op_stats[BITOP_BITREV].found_64bit);
return (changed ? TODO_update_ssa : 0);
}
@@ -3059,9 +3171,9 @@ imm_store_chain_info::try_coalesce_bswap
(merged_store_group *merged_store,
}
}
- uint8_t cmpxchg[MAX_SYM_BITS], cmpnop[MAX_SYM_BITS];
+ uint8_t cmpxchg[MAX_SYM_BITS], cmprev[MAX_SYM_BITS], cmpnop[MAX_SYM_BITS];
bool cast64_to_32;
- find_bswap_or_nop_finalize (&n, cmpxchg, cmpnop, &cast64_to_32);
+ find_bitperm_or_nop_finalize (&n, cmpxchg, cmprev, cmpnop, &cast64_to_32);
/* A complete byte swap should make the symbolic number to start with
the largest digit in the highest order byte. Unchanged symbolic
@@ -4424,9 +4536,10 @@ imm_store_chain_info::output_merged_store
(merged_store_group *group)
/* Update vuse in case it has changed by output_merged_stores. */
n->vuse = gimple_vuse (ins_stmt);
}
- bswap_res = bswap_replace (gsi_start (seq), ins_stmt, fndecl,
- bswap_type, load_type, n, bswap,
- ~(uint64_t) 0, 0);
+ bswap_res = bitperm_replace (gsi_start (seq), ins_stmt, fndecl,
+ bswap_type, load_type, n,
+ bswap ? BITOP_BSWAP : BITOP_NOP,
+ ~(uint64_t) 0, 0);
gcc_assert (bswap_res);
}
diff --git a/gcc/optabs-query.cc b/gcc/optabs-query.cc
index da0d37cf5df..7509be5f85f 100644
--- a/gcc/optabs-query.cc
+++ b/gcc/optabs-query.cc
@@ -882,7 +882,8 @@ can_open_code_p (optab op, machine_mode mode)
return true;
scalar_int_mode int_mode;
- if (op == bswap_optab && is_a<scalar_int_mode> (mode, &int_mode))
+ if ((op == bswap_optab || op == bitreverse_optab)
+ && is_a<scalar_int_mode> (mode, &int_mode))
{
/* widen_bswap_or_bitreverse can implement smaller bswaps using
wider bswaps and a shift. */
diff --git a/gcc/testsuite/gcc.dg/optimize-bitrevdi-1.c
b/gcc/testsuite/gcc.dg/optimize-bitrevdi-1.c
new file mode 100644
index 00000000000..75a9ab53e32
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/optimize-bitrevdi-1.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target bitreverse } */
+/* { dg-require-effective-target stdint_types } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* Test a common implementation of bitreverse, from Hacker's Delight. */
+
+#include <stdint.h>
+
+uint64_t
+bitrdi(uint64_t x) {
+ x = (x & 0x5555555555555555ULL) << 1 | (x & 0xAAAAAAAAAAAAAAAAULL) >> 1;
+ x = (x & 0x3333333333333333ULL) << 2 | (x & 0xCCCCCCCCCCCCCCCCULL) >> 2;
+ x = (x & 0x0F0F0F0F0F0F0F0FULL) << 4 | (x & 0xF0F0F0F0F0F0F0F0ULL) >> 4;
+ x = (x & 0x00FF00FF00FF00FFULL) << 8 | (x & 0xFF00FF00FF00FF00ULL) >> 8;
+ x = (x & 0x0000FFFF0000FFFFULL) << 16 | (x & 0xFFFF0000FFFF0000ULL) >> 16;
+ x = (x & 0x00000000FFFFFFFFULL) << 32 | (x & 0xFFFFFFFF00000000ULL) >> 32;
+ return x;
+}
+
+/* { dg-final { scan-tree-dump-times "= __builtin_bitreverse64 \\\(" 1
"optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/optimize-bitrevdi-2.c
b/gcc/testsuite/gcc.dg/optimize-bitrevdi-2.c
new file mode 100644
index 00000000000..239d93db3d3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/optimize-bitrevdi-2.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target bitreverse } */
+/* { dg-require-effective-target stdint_types } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* Test 64 to 32 bit casting as a recognized pattern. */
+
+#include <stdint.h>
+
+uint32_t
+bitrdi(uint64_t x) {
+ x >>= 32;
+ x = (x & 0x55555555) << 1 | (x & 0xAAAAAAAA) >> 1;
+ x = (x & 0x33333333) << 2 | (x & 0xCCCCCCCC) >> 2;
+ x = (x & 0x0F0F0F0F) << 4 | (x & 0xF0F0F0F0) >> 4;
+ x = (x & 0x00FF00FF) << 8 | (x & 0xFF00FF00) >> 8;
+ x = (x & 0x0000FFFF) << 16 | (x & 0xFFFF0000) >> 16;
+ return x;
+}
+
+/* { dg-final { scan-tree-dump-times "= __builtin_bitreverse64 \\\(" 1
"optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/optimize-bitrevsi-1.c
b/gcc/testsuite/gcc.dg/optimize-bitrevsi-1.c
new file mode 100644
index 00000000000..bbfd14e8b39
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/optimize-bitrevsi-1.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target bitreverse } */
+/* { dg-require-effective-target stdint_types } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* Test a common implementation of bitreverse, from Hacker's Delight. */
+
+#include <stdint.h>
+
+uint32_t
+bitr1(uint32_t x) {
+ x = (x & 0x55555555) << 1 | (x & 0xAAAAAAAA) >> 1;
+ x = (x & 0x33333333) << 2 | (x & 0xCCCCCCCC) >> 2;
+ x = (x & 0x0F0F0F0F) << 4 | (x & 0xF0F0F0F0) >> 4;
+ x = (x & 0x00FF00FF) << 8 | (x & 0xFF00FF00) >> 8;
+ x = (x & 0x0000FFFF) << 16 | (x & 0xFFFF0000) >> 16;
+ return x;
+}
+
+/* { dg-final { scan-tree-dump-times "= __builtin_bitreverse32 \\\(" 1
"optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/optimize-bitrevsi-2.c
b/gcc/testsuite/gcc.dg/optimize-bitrevsi-2.c
new file mode 100644
index 00000000000..696dc367d29
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/optimize-bitrevsi-2.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target bitreverse } */
+/* { dg-require-effective-target stdint_types } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* Performs bit reverse and a 1-right rotation simultaneously. */
+
+#include <stdint.h>
+
+uint32_t
+bitr1(uint32_t x) {
+ x = (x & 0x55555555) | (x & 0x2AAAAAAA) << 2 | (x >> 30) & 2;
+ x = (x & 0x33333333) << 2 | (x & 0xCCCCCCCC) >> 2;
+ x = (x & 0x0F0F0F0F) << 4 | (x & 0xF0F0F0F0) >> 4;
+ x = (x & 0x00FF00FF) << 8 | (x & 0xFF00FF00) >> 8;
+ x = (x & 0x0000FFFF) << 16 | (x & 0xFFFF0000) >> 16;
+ return x;
+}
+
+/* { dg-final { scan-tree-dump-times "= __builtin_bitreverse32 \\\(" 1
"optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/optimize-bitrevsi-3.c
b/gcc/testsuite/gcc.dg/optimize-bitrevsi-3.c
new file mode 100644
index 00000000000..56b8710f15e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/optimize-bitrevsi-3.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target bitreverse } */
+/* { dg-require-effective-target stdint_types } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* Performs bit reverse, a 1-bit rotation, and also clears some bits. */
+
+#include <stdint.h>
+
+uint32_t
+bitr1(uint32_t x) {
+ x = (x & 0x55555555) << 1 | (x & 0xA2AAAAAA) >> 1; /* -bit27 -> out 3 */
+ x = (x & 0x33333333) << 2 | (x & 0xCC4CCCCC) >> 2; /* -bit23 -> out 8 */
+ x = (x & 0x0F0F0B0F) << 4 | (x & 0xF0F0F0F0) >> 4; /* -bit10 -> out 21 */
+ x = (x & 0x00FF00FB) << 8 | (x & 0xFF00FF00) >> 8; /* -bit2 -> out 25 */
+ x = x << 15 | x >> 17;
+ /* Bits 3, 8, 21 and 25 are clear. */
+ return x;
+}
+
+/* { dg-final { scan-tree-dump-times "= __builtin_bitreverse32 \\\(" 1
"optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/optimize-bitrevsi-4.c
b/gcc/testsuite/gcc.dg/optimize-bitrevsi-4.c
new file mode 100644
index 00000000000..269c14f4e7b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/optimize-bitrevsi-4.c
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target bitreverse } */
+/* { dg-require-effective-target stdint_types } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* A variant similar to the "canonical" bit exchange scheme, with bswap. */
+
+#include <stdint.h>
+
+uint32_t
+rev_variant1(uint32_t n) {
+ n = ((n & 0x55555555) << 1) | ((n >> 1) & 0x55555555);
+ n = ((n & 0x33333333) << 2) | ((n >> 2) & 0x33333333);
+ n = ((n & 0x0F0F0F0F) << 4) | ((n >> 4) & 0x0F0F0F0F);
+ n = (n << 24) | ((n & 0xFF00) << 8) | ((n >> 8) & 0xFF00) | (n >> 24);
+ return n;
+}
+
+/* A couple of variants from Hacker's Delight. */
+
+inline uint32_t
+rot32(uint32_t x, uint32_t amount) {
+ return (x << amount) | (x >> (32 - amount));
+}
+
+uint32_t
+rev12(uint32_t n) {
+ n = rot32(n & 0x00FF00FF, 16) | (n & ~0x00FF00FF);
+ n = rot32(n & 0x0F0F0F0F, 8) | (n & ~0x0F0F0F0F);
+ n = rot32(n & 0x33333333, 4) | (n & ~0x33333333);
+ n = rot32(n & 0x55555555, 2) | (n & ~0x55555555);
+ return rot32(n, 1);
+}
+
+/* Reverses 27 bits only. Emitted with rotation + masking. */
+uint32_t
+rev13(uint32_t n) {
+ n = (n & 0x000001FF) << 18 | (n & 0x0003FE00) | (n >> 18) & 0x000001FF;
+ n = (n & 0x001C0E07) << 6 | (n & 0x00E07038) | (n >> 6) & 0x001C0E07;
+ n = (n & 0x01249249) << 2 | (n & 0x02492492) | (n >> 2) & 0x01249249;
+ return n;
+}
+
+/* { dg-final { scan-tree-dump-times "= __builtin_bitreverse32 \\\(" 3
"optimized" } } */
diff --git a/gcc/testsuite/lib/target-supports.exp
b/gcc/testsuite/lib/target-supports.exp
index a29d3bd89f5..87acc8162a9 100644
--- a/gcc/testsuite/lib/target-supports.exp
+++ b/gcc/testsuite/lib/target-supports.exp
@@ -10778,6 +10778,16 @@ proc check_effective_target_bswap { } {
} ""]) }}]
}
+# Return 1 if the target supports bitreverse instructions.
+
+proc check_effective_target_bitreverse { } {
+ return [check_cached_effective_target bitreverse {
+ expr { [istarget aarch64*-*-*]
+ || [istarget cris-*-*]
+ || [istarget loongarch*-*-*]
+ }}]
+}
+
# Return 1 if the target supports atomic operations on "int" and "long".
proc check_effective_target_sync_int_long { } {
--
2.55.0