This patch adds a second movk pattern that models the instruction
as a "normal" and/ior operation rather than an insertion.  It fixes
the third insv_1.c failure in PR87763, which was a regression from
GCC 8.

Tested on aarch64-linux-gnu & pushed.

Richard


2020-02-04  Richard Sandiford  <richard.sandif...@arm.com>

gcc/
        PR target/87763
        * config/aarch64/aarch64-protos.h (aarch64_movk_shift): Declare.
        * config/aarch64/aarch64.c (aarch64_movk_shift): New function.
        * config/aarch64/aarch64.md (aarch64_movk<mode>): New pattern.

gcc/testsuite/
        PR target/87763
        * gcc.target/aarch64/movk_2.c: New test.
---
 gcc/config/aarch64/aarch64-protos.h       |  1 +
 gcc/config/aarch64/aarch64.c              | 24 +++++++
 gcc/config/aarch64/aarch64.md             | 17 +++++
 gcc/testsuite/gcc.target/aarch64/movk_2.c | 78 +++++++++++++++++++++++
 4 files changed, 120 insertions(+)
 create mode 100644 gcc/testsuite/gcc.target/aarch64/movk_2.c

diff --git a/gcc/config/aarch64/aarch64-protos.h 
b/gcc/config/aarch64/aarch64-protos.h
index 24cc65a383a..d29975a8921 100644
--- a/gcc/config/aarch64/aarch64-protos.h
+++ b/gcc/config/aarch64/aarch64-protos.h
@@ -560,6 +560,7 @@ bool aarch64_sve_float_mul_immediate_p (rtx);
 bool aarch64_split_dimode_const_store (rtx, rtx);
 bool aarch64_symbolic_address_p (rtx);
 bool aarch64_uimm12_shift (HOST_WIDE_INT);
+int aarch64_movk_shift (const wide_int_ref &, const wide_int_ref &);
 bool aarch64_use_return_insn_p (void);
 const char *aarch64_output_casesi (rtx *);
 
diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index 6581e4cb075..6a1b4099af1 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -7895,6 +7895,30 @@ aarch64_movw_imm (HOST_WIDE_INT val, scalar_int_mode 
mode)
          || (val & (((HOST_WIDE_INT) 0xffff) << 16)) == val);
 }
 
+/* Test whether:
+
+     X = (X & AND_VAL) | IOR_VAL;
+
+   can be implemented using:
+
+     MOVK X, #(IOR_VAL >> shift), LSL #shift
+
+   Return the shift if so, otherwise return -1.  */
+int
+aarch64_movk_shift (const wide_int_ref &and_val,
+                   const wide_int_ref &ior_val)
+{
+  unsigned int precision = and_val.get_precision ();
+  unsigned HOST_WIDE_INT mask = 0xffff;
+  for (unsigned int shift = 0; shift < precision; shift += 16)
+    {
+      if (and_val == ~mask && (ior_val & mask) == ior_val)
+       return shift;
+      mask <<= 16;
+    }
+  return -1;
+}
+
 /* VAL is a value with the inner mode of MODE.  Replicate it to fill a
    64-bit (DImode) integer.  */
 
diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md
index 90eebce85c0..9c1f17d0f85 100644
--- a/gcc/config/aarch64/aarch64.md
+++ b/gcc/config/aarch64/aarch64.md
@@ -1282,6 +1282,23 @@ (define_insn "insv_imm<mode>"
   [(set_attr "type" "mov_imm")]
 )
 
+;; Match MOVK as a normal AND and IOR operation.
+(define_insn "aarch64_movk<mode>"
+  [(set (match_operand:GPI 0 "register_operand" "=r")
+       (ior:GPI (and:GPI (match_operand:GPI 1 "register_operand" "0")
+                         (match_operand:GPI 2 "const_int_operand"))
+                (match_operand:GPI 3 "const_int_operand")))]
+  "aarch64_movk_shift (rtx_mode_t (operands[2], <MODE>mode),
+                      rtx_mode_t (operands[3], <MODE>mode)) >= 0"
+  {
+    int shift = aarch64_movk_shift (rtx_mode_t (operands[2], <MODE>mode),
+                                   rtx_mode_t (operands[3], <MODE>mode));
+    operands[2] = gen_int_mode (UINTVAL (operands[3]) >> shift, SImode);
+    operands[3] = gen_int_mode (shift, SImode);
+    return "movk\\t%<w>0, #%X2, lsl %3";
+  }
+)
+
 (define_expand "movti"
   [(set (match_operand:TI 0 "nonimmediate_operand")
        (match_operand:TI 1 "general_operand"))]
diff --git a/gcc/testsuite/gcc.target/aarch64/movk_2.c 
b/gcc/testsuite/gcc.target/aarch64/movk_2.c
new file mode 100644
index 00000000000..a0477ad5d42
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/movk_2.c
@@ -0,0 +1,78 @@
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include <stdint.h>
+
+#define H3 ((uint64_t) 0xffff << 48)
+#define H2 ((uint64_t) 0xffff << 32)
+#define H1 ((uint64_t) 0xffff << 16)
+#define H0 ((uint64_t) 0xffff)
+
+/*
+** f1:
+**     mov     w0, w1
+**     movk    w0, #0x9876(?:, lsl #?0)?
+**     ret
+*/
+uint32_t
+f1 (uint32_t dummy, uint32_t x)
+{
+  return (x & 0xffff0000) | 0x9876;
+}
+
+/*
+** f2:
+**     movk    w0, #0x1234, lsl #?16
+**     ret
+*/
+uint32_t
+f2 (uint32_t x)
+{
+  return (x & 0xffff) | 0x12340000;
+}
+
+/*
+** g1:
+**     movk    x0, #0x1234, lsl #?0
+**     ret
+*/
+uint64_t
+g1 (uint64_t x)
+{
+  return (x & (H3 | H2 | H1)) | 0x1234;
+}
+
+/*
+** g2:
+**     movk    x0, #0x900e, lsl #?16
+**     ret
+*/
+uint64_t
+g2 (uint64_t x)
+{
+  return (x & (H3 | H2 | H0)) | ((uint64_t) 0x900e << 16);
+}
+
+/*
+** g3:
+**     movk    x0, #0xee33, lsl #?32
+**     ret
+*/
+uint64_t
+g3 (uint64_t x)
+{
+  return (x & (H3 | H1 | H0)) | ((uint64_t) 0xee33 << 32);
+}
+
+/*
+** g4:
+**     mov     x0, x1
+**     movk    x0, #0x7654, lsl #?48
+**     ret
+*/
+uint64_t
+g4 (uint64_t dummy, uint64_t x)
+{
+  return (x & (H2 | H1 | H0)) | ((uint64_t) 0x7654 << 48);
+}

Reply via email to