Hello,

Fortran tests have been failing on SPU since libgfortran now assumes the
atomic builtins are always available.  On the SPU, execution is always
single-threaded, so we have not provided atomic builtins so far.  As
suggested by Ian here:
https://gcc.gnu.org/ml/gcc-patches/2015-08/msg01818.html
this patch adds a trivial implementation of the atomic builtins on SPU.

Tested on spu-elf, committed to mainline.

Bye,
Ulrich


ChangeLog:

        * config/spu/spu-protos.h (spu_expand_atomic_op): Add prototype.
        * config/spu/spu.c (spu_expand_atomic_op): New function.
        * config/spu/spu.md (AINT): New mode iterator.
        (ATOMIC): New code iterator.
        (atomic_name, atomic_pred): New code predicates.
        ("atomic_load<mode>", "atomic_store<mode>"): New expanders.
        ("atomic_compare_and_swap<mode>", "atomic_exchange<mode>"): Likewise.
        (""atomic_<atomic_name><mode>", "atomic_fetch_<atomic_name><mode>",
        "atomic_<atomic_name>_fetch<mode>"): Likewise.

testsuite/ChangeLog:

        * lib/target-supports.exp (check_effective_target_sync_int_128):
        Return 1 on spu-*-* targets.
        (check_effective_target_sync_int_128_runtime): Likewise.
        (check_effective_target_sync_long_long): Likewise.
        (check_effective_target_sync_long_long_runtime): Likewise.
        (check_effective_target_sync_int_long): Likewise.
        (check_effective_target_sync_char_short): Likewise.


Index: gcc/testsuite/lib/target-supports.exp
===================================================================
*** gcc/testsuite/lib/target-supports.exp       (revision 228013)
--- gcc/testsuite/lib/target-supports.exp       (working copy)
*************** proc check_effective_target_sync_int_128
*** 5085,5090 ****
--- 5085,5092 ----
      if { ([istarget x86_64-*-*] || [istarget i?86-*-*])
         && ![is-effective-target ia32] } {
        return 1
+     } elseif { [istarget spu-*-*] } {
+       return 1
      } else {
        return 0
      }
*************** proc check_effective_target_sync_int_128
*** 5108,5113 ****
--- 5110,5117 ----
                }
            } ""
        }]
+     } elseif { [istarget spu-*-*] } {
+       return 1
      } else {
        return 0
      }
*************** proc check_effective_target_sync_long_lo
*** 5122,5128 ****
         || [istarget aarch64*-*-*]
         || [istarget arm*-*-*]
         || [istarget alpha*-*-*]
!        || ([istarget sparc*-*-*] && [check_effective_target_lp64]) } {
        return 1
      } else {
        return 0
--- 5126,5133 ----
         || [istarget aarch64*-*-*]
         || [istarget arm*-*-*]
         || [istarget alpha*-*-*]
!        || ([istarget sparc*-*-*] && [check_effective_target_lp64])
!        || [istarget spu-*-*] } {
        return 1
      } else {
        return 0
*************** proc check_effective_target_sync_long_lo
*** 5172,5177 ****
--- 5177,5184 ----
                 && [check_effective_target_lp64]
                 && [check_effective_target_ultrasparc_hw]) } {
        return 1
+     } elseif { [istarget spu-*-*] } {
+       return 1
      } elseif { [istarget powerpc*-*-*] && [check_effective_target_lp64] } {
        return 1
      } else {
*************** proc check_effective_target_sync_int_lon
*** 5285,5290 ****
--- 5292,5298 ----
             || [istarget powerpc*-*-*]
             || [istarget crisv32-*-*] || [istarget cris-*-*]
             || ([istarget sparc*-*-*] && [check_effective_target_sparc_v9])
+            || [istarget spu-*-*]
             || [check_effective_target_mips_llsc] } {
             set et_sync_int_long_saved 1
          }
*************** proc check_effective_target_sync_char_sh
*** 5315,5320 ****
--- 5323,5329 ----
             || [istarget powerpc*-*-*]
             || [istarget crisv32-*-*] || [istarget cris-*-*]
             || ([istarget sparc*-*-*] && [check_effective_target_sparc_v9])
+            || [istarget spu-*-*]
             || [check_effective_target_mips_llsc] } {
             set et_sync_char_short_saved 1
          }
Index: gcc/config/spu/spu-protos.h
===================================================================
*** gcc/config/spu/spu-protos.h (revision 228013)
--- gcc/config/spu/spu-protos.h (working copy)
*************** extern void spu_builtin_promote (rtx ops
*** 76,81 ****
--- 76,83 ----
  extern void spu_expand_sign_extend (rtx ops[]);
  extern void spu_expand_vector_init (rtx target, rtx vals);
  extern rtx spu_legitimize_reload_address (rtx, machine_mode, int, int);
+ extern void spu_expand_atomic_op (enum rtx_code code, rtx mem, rtx val,
+                                 rtx orig_before, rtx orig_after);
  #endif /* RTX_CODE  */
  
  extern void spu_init_expanders (void);
Index: gcc/config/spu/spu.c
===================================================================
*** gcc/config/spu/spu.c        (revision 228013)
--- gcc/config/spu/spu.c        (working copy)
*************** spu_canonicalize_comparison (int *code, 
*** 7121,7126 ****
--- 7121,7161 ----
        *code = (int)swap_condition ((enum rtx_code)*code);
      }
  }
+ 
+ /* Expand an atomic fetch-and-operate pattern.  CODE is the binary operation
+    to perform.  MEM is the memory on which to operate.  VAL is the second
+    operand of the binary operator.  BEFORE and AFTER are optional locations to
+    return the value of MEM either before of after the operation.  */
+ void
+ spu_expand_atomic_op (enum rtx_code code, rtx mem, rtx val,
+                     rtx orig_before, rtx orig_after)
+ {
+   machine_mode mode = GET_MODE (mem);
+   rtx before = orig_before, after = orig_after;
+ 
+   if (before == NULL_RTX)
+     before = gen_reg_rtx (mode);
+ 
+   emit_move_insn (before, mem);
+ 
+   if (code == MULT)  /* NAND operation */
+     {
+       rtx x = expand_simple_binop (mode, AND, before, val,
+                                  NULL_RTX, 1, OPTAB_LIB_WIDEN);
+       after = expand_simple_unop (mode, NOT, x, after, 1);
+     }
+   else
+     {
+       after = expand_simple_binop (mode, code, before, val,
+                                  after, 1, OPTAB_LIB_WIDEN);
+     }
+ 
+   emit_move_insn (mem, after);
+ 
+   if (orig_after && after != orig_after)
+     emit_move_insn (orig_after, after);
+ }
+ 
  
  /*  Table of machine attributes.  */
  static const struct attribute_spec spu_attribute_table[] =
Index: gcc/config/spu/spu.md
===================================================================
*** gcc/config/spu/spu.md       (revision 228013)
--- gcc/config/spu/spu.md       (working copy)
*************** DONE;
*** 5097,5099 ****
--- 5097,5246 ----
     (set_attr "type" "multi1")]
  )
  
+ ; Atomic operations
+ ;
+ ; SPU execution is always single-threaded, so there is no need for real
+ ; atomic operations.  We provide the atomic primitives anyway so that
+ ; code expecting the builtins to be present (like libgfortran) will work.
+ 
+ ;; Types that we should provide atomic instructions for.
+ (define_mode_iterator AINT [QI HI SI DI TI])
+ 
+ (define_code_iterator ATOMIC [plus minus ior xor and mult])
+ (define_code_attr atomic_name
+   [(plus "add") (minus "sub")
+    (ior "or") (xor "xor") (and "and") (mult "nand")])
+ (define_code_attr atomic_pred
+   [(plus "spu_arith_operand") (minus "spu_reg_operand")
+    (ior "spu_logical_operand") (xor "spu_logical_operand")
+    (and "spu_logical_operand") (mult "spu_logical_operand")])
+ 
+ (define_expand "atomic_load<mode>"
+   [(set (match_operand:AINT 0 "spu_reg_operand" "")           ;; output
+       (match_operand:AINT 1 "memory_operand" ""))             ;; memory
+    (use (match_operand:SI 2 "const_int_operand" ""))]         ;; model
+   ""
+ {
+   if (MEM_ADDR_SPACE (operands[1]))
+     FAIL;
+ 
+   emit_move_insn (operands[0], operands[1]);
+   DONE;
+ })
+ 
+ (define_expand "atomic_store<mode>"
+   [(set (match_operand:AINT 0 "memory_operand" "")            ;; memory
+       (match_operand:AINT 1 "spu_reg_operand" ""))            ;; input
+    (use (match_operand:SI 2 "const_int_operand" ""))]         ;; model
+   ""
+ {
+   if (MEM_ADDR_SPACE (operands[0]))
+     FAIL;
+ 
+   emit_move_insn (operands[0], operands[1]);
+   DONE;
+ })
+ 
+ (define_expand "atomic_compare_and_swap<mode>"
+   [(match_operand:SI 0 "spu_reg_operand" "")          ;; bool out
+    (match_operand:AINT 1 "spu_reg_operand" "")                ;; val out
+    (match_operand:AINT 2 "memory_operand" "")         ;; memory
+    (match_operand:AINT 3 "spu_nonmem_operand" "")     ;; expected
+    (match_operand:AINT 4 "spu_nonmem_operand" "")     ;; desired
+    (match_operand:SI 5 "const_int_operand" "")                ;; is_weak
+    (match_operand:SI 6 "const_int_operand" "")                ;; model succ
+    (match_operand:SI 7 "const_int_operand" "")]               ;; model fail
+   ""
+ {
+   rtx boolval, retval, label;
+ 
+   if (MEM_ADDR_SPACE (operands[2]))
+     FAIL;
+ 
+   boolval = gen_reg_rtx (SImode);
+   retval = gen_reg_rtx (<MODE>mode);
+   label = gen_label_rtx ();
+ 
+   emit_move_insn (retval, operands[2]);
+   emit_move_insn (boolval, const0_rtx);
+ 
+   emit_cmp_and_jump_insns (retval, operands[3], NE, NULL_RTX,
+                            <MODE>mode, 1, label);
+ 
+   emit_move_insn (operands[2], operands[4]);
+   emit_move_insn (boolval, const1_rtx);
+ 
+   emit_label (label);
+ 
+   emit_move_insn (operands[0], boolval);
+   emit_move_insn (operands[1], retval);
+   DONE;
+ })
+ 
+ (define_expand "atomic_exchange<mode>"
+   [(match_operand:AINT 0 "spu_reg_operand" "")                ;; output
+    (match_operand:AINT 1 "memory_operand" "")         ;; memory
+    (match_operand:AINT 2 "spu_nonmem_operand" "")     ;; input
+    (match_operand:SI 3 "const_int_operand" "")]               ;; model
+   ""
+ {
+   rtx retval;
+ 
+   if (MEM_ADDR_SPACE (operands[1]))
+     FAIL;
+ 
+   retval = gen_reg_rtx (<MODE>mode);
+ 
+   emit_move_insn (retval, operands[1]);
+   emit_move_insn (operands[1], operands[2]);
+   emit_move_insn (operands[0], retval);
+   DONE;
+ })
+ 
+ (define_expand "atomic_<atomic_name><mode>"
+   [(ATOMIC:AINT
+      (match_operand:AINT 0 "memory_operand" "")               ;; memory
+      (match_operand:AINT 1 "<atomic_pred>" ""))               ;; operand
+    (match_operand:SI 2 "const_int_operand" "")]               ;; model
+   ""
+ {
+   if (MEM_ADDR_SPACE (operands[0]))
+     FAIL;
+ 
+   spu_expand_atomic_op (<CODE>, operands[0], operands[1],
+                       NULL_RTX, NULL_RTX);
+   DONE;
+ })
+ 
+ (define_expand "atomic_fetch_<atomic_name><mode>"
+   [(match_operand:AINT 0 "spu_reg_operand" "")                ;; output
+    (ATOMIC:AINT
+      (match_operand:AINT 1 "memory_operand" "")               ;; memory
+      (match_operand:AINT 2 "<atomic_pred>" ""))               ;; operand
+    (match_operand:SI 3 "const_int_operand" "")]               ;; model
+   ""
+ { 
+   if (MEM_ADDR_SPACE (operands[1]))
+     FAIL;
+ 
+   spu_expand_atomic_op (<CODE>, operands[1], operands[2],
+                       operands[0], NULL_RTX);
+   DONE;
+ })
+ 
+ (define_expand "atomic_<atomic_name>_fetch<mode>"
+   [(match_operand:AINT 0 "spu_reg_operand" "")                ;; output
+    (ATOMIC:AINT
+      (match_operand:AINT 1 "memory_operand" "")               ;; memory
+      (match_operand:AINT 2 "<atomic_pred>" ""))               ;; operand
+    (match_operand:SI 3 "const_int_operand" "")]               ;; model
+   ""
+ {
+   if (MEM_ADDR_SPACE (operands[1]))
+     FAIL;
+ 
+   spu_expand_atomic_op (<CODE>, operands[1], operands[2],
+                       NULL_RTX, operands[0]);
+   DONE;
+ })
+ 
-- 
  Dr. Ulrich Weigand
  GNU/Linux compilers and toolchain
  ulrich.weig...@de.ibm.com

Reply via email to