diff mbox

[4/6,ARM] Implement support for ACLE Coprocessor LDC and STC intrinsics

Message ID 586FA729.1000609@arm.com
State New
Headers show

Commit Message

Andre Vieira (lists) Jan. 6, 2017, 2:18 p.m. UTC
On 05/01/17 11:02, Kyrill Tkachov wrote:
> Hi Andre,
> 
> On 09/11/16 10:12, Andre Vieira (lists) wrote:
>> Hi,
>>
>> This patch implements support for the ARM ACLE Coprocessor LDC and STC
>> intrinsics. See below a table mapping the intrinsics to their respective
>> instructions:
>>
>> +----------------------------------------------------+--------------------------------------+
>>
>> | Intrinsic signature                                | Instruction
>> pattern                  |
>> +----------------------------------------------------+--------------------------------------+
>>
>> |void __arm_ldc(coproc, CRd, const void* p)          |LDC coproc, CRd,
>> [...]                |
>> +----------------------------------------------------+--------------------------------------+
>>
>> |void __arm_ldcl(coproc, CRd, const void* p)         |LDCL coproc, CRd,
>> [...]               |
>> +----------------------------------------------------+--------------------------------------+
>>
>> |void __arm_ldc2(coproc, CRd, const void* p)         |LDC2 coproc, CRd,
>> [...]               |
>> +----------------------------------------------------+--------------------------------------+
>>
>> |void __arm_ldc2l(coproc, CRd, const void* p)        |LDC2L coproc, CRd,
>> [...]              |
>> +----------------------------------------------------+--------------------------------------+
>>
>> |void __arm_stc(coproc, CRd, void* p)                |STC coproc, CRd,
>> [...]                |
>> +----------------------------------------------------+--------------------------------------+
>>
>> |void __arm_stcl(coproc, CRd, void* p)               |STCL coproc, CRd,
>> [...]               |
>> +----------------------------------------------------+--------------------------------------+
>>
>> |void __arm_stc2(coproc, CRd, void* p)               |STC2 coproc, CRd,
>> [...]               |
>> +----------------------------------------------------+--------------------------------------+
>>
>> |void __arm_stc2l(coproc, CRd, void* p)              |STC2L coproc, CRd,
>> [...]              |
>> +----------------------------------------------------+--------------------------------------+
>>
>> Note that any untyped variable in the intrinsic signature is required to
>> be a compiler-time constant and has the type 'unsigned int'.  We do some
>> boundary checks for coproc:[0-15], CR*:[0-31]. If either of these
>> requirements are not met a diagnostic is issued.
>>
>>
>> Is this ok for trunk?
> 
> I have a few comments below...
> 
>> Regards,
>> Andre
>>
>> gcc/ChangeLog:
>> 2016-11-09  Andre Vieira  <andre.simoesdiasvieira@arm.com>
>>
>>    * config/arm/arm.md (*ldcstc): New.
>>    (<ldcstc>): New.
>>    * config/arm/arm.c (arm_coproc_builtin_available): Add
>>    support for ldc,ldcl,stc,stcl,ldc2,ldc2l,stc2 and stc2l.
>>    (arm_coproc_ldc_stc_legitimate_address): New.
>>    * config/arm/arm-builtins.c (arm_type_qualifiers): Add
>>    'qualifier_const_pointer'.
>>    (LDC_QUALIFIERS): Define to...
>>    (arm_ldc_qualifiers): ... this. New.
>>    (STC_QUALIFIERS): Define to...
>>    (arm_stc_qualifiers): ... this. New.
>>    * config/arm/arm-protos.h
>>    (arm_coproc_ldc_stc_legitimate_address): New.
>>    * config/arm/arm_acle.h (__arm_ldc, __arm_ldcl, __arm_stc,
>>    __arm_stcl, __arm_ldc2, __arm_ldc2l, __arm_stc2, __arm_stc2l): New.
>>    * config/arm/arm_acle_builtins.def (ldc, ldc2, ldcl, ldc2l, stc,
>>    stc2, stcl, stc2l): New.
>>    * config/arm/constraints.md (Uz): New.
>>    * config/arm/iterators.md (LDCSTCI, ldcstc, LDCSTC): New.
>>    * config/arm/unspecs.md (VUNSPEC_LDC, VUNSPEC_LDC2, VUNSPEC_LDCL,
>>    VUNSPEC_LDC2L, VUNSPEC_STC, VUNSPEC_STC2, VUNSPEC_STCL,
>>    VUNSPEC_STC2L): New.
>>
>> gcc/testsuite/ChangeLog:
>>
>> 2016-11-09  Andre Vieira  <andre.simoesdiasvieira@arm.com>
>>
>>    * gcc.target/arm/acle/ldc: New.
>>    * gcc.target/arm/acle/ldc2: New.
>>    * gcc.target/arm/acle/ldcl: New.
>>    * gcc.target/arm/acle/ldc2l: New.
>>    * gcc.target/arm/acle/stc: New.
>>    * gcc.target/arm/acle/stc2: New.
>>    * gcc.target/arm/acle/stcl: New.
>>    * gcc.target/arm/acle/stc2l: New.
> 
> +  /* const T * foo */
> +  qualifier_const_pointer = 0x6,
> 
> Nit: full stop at end of comment.
> 
> +
> +/* This function returns true if OP is a valid memory operand for the
> ldc and
> +   stc coprocessor instructions and false otherwise.  */
> +
> +bool arm_coproc_ldc_stc_legitimate_address (rtx op)
> +{
> 
> type and function name should be on separate lines.
> 
> +  int range;
> +  /* Has to be a memory operand.  */
> +  if (!MEM_P (op))
> +    return false;
> 
> +      /* Within the range of [-1020,1020].  */
> +      if (range < -1020 || range > 1020)
> +        return false;
> 
> Use !IN_RANGE (-1020, 1020), also make 'range' a HOST_WIDE_INT (that is
> what INTVAL returns).
> 
> --- a/gcc/config/arm/arm.md
> +++ b/gcc/config/arm/arm.md
> @@ -11858,6 +11858,25 @@
>    [(set_attr "length" "4")
>     (set_attr "type" "coproc")])
>  
> +(define_insn "*ldcstc"
> +  [(unspec_volatile [(match_operand:SI 0 "immediate_operand")
> +             (match_operand:SI 1 "immediate_operand")
> +             (match_operand:SI 2 "memory_operand" "Uz")] LDCSTCI)]
> +  "arm_coproc_builtin_available (VUNSPEC_<LDCSTC>)"
> 
> You're missing constraints for operands 1 and 2? (just the general 'n'
> constraint should do it since you do bounds checking in arm_const_bounds)
> Also, the ldc reads memory, whereas stc writes to it, so at least the
> stc variants will need
> a '=' in their constraint string to tell LRA that the memory is written
> to. For this I think it's better to split ldc* and stc* into separate
> define_insns.
> 
> +(define_memory_constraint "Uz"
> + "@internal
> +  A memory access that is accessible"
> + (and (match_code "mem")
> +      (match_test "arm_coproc_ldc_stc_legitimate_address (op)")))
> 
> That description doesn't make much sense. Did you mean "A memory access
> suitable as an LDC/STC operand" ?
> 
Hi,

Reworked according to comments and rebased it.

Is this ok for trunk?

Regards,
Andre

gcc/ChangeLog:
2017-01-xx  Andre Vieira  <andre.simoesdiasvieira@arm.com>

  * config/arm/arm.md (*ldc): New.
  (*stc): New.
  (<ldc>): New.
  (<stc>): New.
  * config/arm/arm.c (arm_coproc_builtin_available): Add
  support for ldc,ldcl,stc,stcl,ldc2,ldc2l,stc2 and stc2l.
  (arm_coproc_ldc_stc_legitimate_address): New.
  * config/arm/arm-builtins.c (arm_type_qualifiers): Add
  'qualifier_const_pointer'.
  (LDC_QUALIFIERS): Define to...
  (arm_ldc_qualifiers): ... this. New.
  (STC_QUALIFIERS): Define to...
  (arm_stc_qualifiers): ... this. New.
  * config/arm/arm-protos.h
  (arm_coproc_ldc_stc_legitimate_address): New.
  * config/arm/arm_acle.h (__arm_ldc, __arm_ldcl, __arm_stc,
  __arm_stcl, __arm_ldc2, __arm_ldc2l, __arm_stc2, __arm_stc2l): New.
  * config/arm/arm_acle_builtins.def (ldc, ldc2, ldcl, ldc2l, stc,
  stc2, stcl, stc2l): New.
  * config/arm/constraints.md (Uz): New.
  * config/arm/iterators.md (LDCI, STCI, ldc, stc, LDC STC): New.
  * config/arm/unspecs.md (VUNSPEC_LDC, VUNSPEC_LDC2, VUNSPEC_LDCL,
  VUNSPEC_LDC2L, VUNSPEC_STC, VUNSPEC_STC2, VUNSPEC_STCL,
  VUNSPEC_STC2L): New.

gcc/testsuite/ChangeLog:

2017-01-xx  Andre Vieira  <andre.simoesdiasvieira@arm.com>

  * gcc.target/arm/acle/ldc: New.
  * gcc.target/arm/acle/ldc2: New.
  * gcc.target/arm/acle/ldcl: New.
  * gcc.target/arm/acle/ldc2l: New.
  * gcc.target/arm/acle/stc: New.
  * gcc.target/arm/acle/stc2: New.
  * gcc.target/arm/acle/stcl: New.
  * gcc.target/arm/acle/stc2l: New.

Comments

Kyrill Tkachov Jan. 6, 2017, 3:14 p.m. UTC | #1
On 06/01/17 14:18, Andre Vieira (lists) wrote:
> On 05/01/17 11:02, Kyrill Tkachov wrote:
>> Hi Andre,
>>
>> On 09/11/16 10:12, Andre Vieira (lists) wrote:
>>> Hi,
>>>
>>> This patch implements support for the ARM ACLE Coprocessor LDC and STC
>>> intrinsics. See below a table mapping the intrinsics to their respective
>>> instructions:
>>>
>>> +----------------------------------------------------+--------------------------------------+
>>>
>>> | Intrinsic signature                                | Instruction
>>> pattern                  |
>>> +----------------------------------------------------+--------------------------------------+
>>>
>>> |void __arm_ldc(coproc, CRd, const void* p)          |LDC coproc, CRd,
>>> [...]                |
>>> +----------------------------------------------------+--------------------------------------+
>>>
>>> |void __arm_ldcl(coproc, CRd, const void* p)         |LDCL coproc, CRd,
>>> [...]               |
>>> +----------------------------------------------------+--------------------------------------+
>>>
>>> |void __arm_ldc2(coproc, CRd, const void* p)         |LDC2 coproc, CRd,
>>> [...]               |
>>> +----------------------------------------------------+--------------------------------------+
>>>
>>> |void __arm_ldc2l(coproc, CRd, const void* p)        |LDC2L coproc, CRd,
>>> [...]              |
>>> +----------------------------------------------------+--------------------------------------+
>>>
>>> |void __arm_stc(coproc, CRd, void* p)                |STC coproc, CRd,
>>> [...]                |
>>> +----------------------------------------------------+--------------------------------------+
>>>
>>> |void __arm_stcl(coproc, CRd, void* p)               |STCL coproc, CRd,
>>> [...]               |
>>> +----------------------------------------------------+--------------------------------------+
>>>
>>> |void __arm_stc2(coproc, CRd, void* p)               |STC2 coproc, CRd,
>>> [...]               |
>>> +----------------------------------------------------+--------------------------------------+
>>>
>>> |void __arm_stc2l(coproc, CRd, void* p)              |STC2L coproc, CRd,
>>> [...]              |
>>> +----------------------------------------------------+--------------------------------------+
>>>
>>> Note that any untyped variable in the intrinsic signature is required to
>>> be a compiler-time constant and has the type 'unsigned int'.  We do some
>>> boundary checks for coproc:[0-15], CR*:[0-31]. If either of these
>>> requirements are not met a diagnostic is issued.
>>>
>>>
>>> Is this ok for trunk?
>> I have a few comments below...
>>
>>> Regards,
>>> Andre
>>>
>>> gcc/ChangeLog:
>>> 2016-11-09  Andre Vieira  <andre.simoesdiasvieira@arm.com>
>>>
>>>     * config/arm/arm.md (*ldcstc): New.
>>>     (<ldcstc>): New.
>>>     * config/arm/arm.c (arm_coproc_builtin_available): Add
>>>     support for ldc,ldcl,stc,stcl,ldc2,ldc2l,stc2 and stc2l.
>>>     (arm_coproc_ldc_stc_legitimate_address): New.
>>>     * config/arm/arm-builtins.c (arm_type_qualifiers): Add
>>>     'qualifier_const_pointer'.
>>>     (LDC_QUALIFIERS): Define to...
>>>     (arm_ldc_qualifiers): ... this. New.
>>>     (STC_QUALIFIERS): Define to...
>>>     (arm_stc_qualifiers): ... this. New.
>>>     * config/arm/arm-protos.h
>>>     (arm_coproc_ldc_stc_legitimate_address): New.
>>>     * config/arm/arm_acle.h (__arm_ldc, __arm_ldcl, __arm_stc,
>>>     __arm_stcl, __arm_ldc2, __arm_ldc2l, __arm_stc2, __arm_stc2l): New.
>>>     * config/arm/arm_acle_builtins.def (ldc, ldc2, ldcl, ldc2l, stc,
>>>     stc2, stcl, stc2l): New.
>>>     * config/arm/constraints.md (Uz): New.
>>>     * config/arm/iterators.md (LDCSTCI, ldcstc, LDCSTC): New.
>>>     * config/arm/unspecs.md (VUNSPEC_LDC, VUNSPEC_LDC2, VUNSPEC_LDCL,
>>>     VUNSPEC_LDC2L, VUNSPEC_STC, VUNSPEC_STC2, VUNSPEC_STCL,
>>>     VUNSPEC_STC2L): New.
>>>
>>> gcc/testsuite/ChangeLog:
>>>
>>> 2016-11-09  Andre Vieira  <andre.simoesdiasvieira@arm.com>
>>>
>>>     * gcc.target/arm/acle/ldc: New.
>>>     * gcc.target/arm/acle/ldc2: New.
>>>     * gcc.target/arm/acle/ldcl: New.
>>>     * gcc.target/arm/acle/ldc2l: New.
>>>     * gcc.target/arm/acle/stc: New.
>>>     * gcc.target/arm/acle/stc2: New.
>>>     * gcc.target/arm/acle/stcl: New.
>>>     * gcc.target/arm/acle/stc2l: New.
>> +  /* const T * foo */
>> +  qualifier_const_pointer = 0x6,
>>
>> Nit: full stop at end of comment.
>>
>> +
>> +/* This function returns true if OP is a valid memory operand for the
>> ldc and
>> +   stc coprocessor instructions and false otherwise.  */
>> +
>> +bool arm_coproc_ldc_stc_legitimate_address (rtx op)
>> +{
>>
>> type and function name should be on separate lines.
>>
>> +  int range;
>> +  /* Has to be a memory operand.  */
>> +  if (!MEM_P (op))
>> +    return false;
>>
>> +      /* Within the range of [-1020,1020].  */
>> +      if (range < -1020 || range > 1020)
>> +        return false;
>>
>> Use !IN_RANGE (-1020, 1020), also make 'range' a HOST_WIDE_INT (that is
>> what INTVAL returns).
>>
>> --- a/gcc/config/arm/arm.md
>> +++ b/gcc/config/arm/arm.md
>> @@ -11858,6 +11858,25 @@
>>     [(set_attr "length" "4")
>>      (set_attr "type" "coproc")])
>>   
>> +(define_insn "*ldcstc"
>> +  [(unspec_volatile [(match_operand:SI 0 "immediate_operand")
>> +             (match_operand:SI 1 "immediate_operand")
>> +             (match_operand:SI 2 "memory_operand" "Uz")] LDCSTCI)]
>> +  "arm_coproc_builtin_available (VUNSPEC_<LDCSTC>)"
>>
>> You're missing constraints for operands 1 and 2? (just the general 'n'
>> constraint should do it since you do bounds checking in arm_const_bounds)
>> Also, the ldc reads memory, whereas stc writes to it, so at least the
>> stc variants will need
>> a '=' in their constraint string to tell LRA that the memory is written
>> to. For this I think it's better to split ldc* and stc* into separate
>> define_insns.
>>
>> +(define_memory_constraint "Uz"
>> + "@internal
>> +  A memory access that is accessible"
>> + (and (match_code "mem")
>> +      (match_test "arm_coproc_ldc_stc_legitimate_address (op)")))
>>
>> That description doesn't make much sense. Did you mean "A memory access
>> suitable as an LDC/STC operand" ?
>>
> Hi,
>
> Reworked according to comments and rebased it.
>
> Is this ok for trunk?

Ok.
Thanks,
Kyrill

> Regards,
> Andre
>
> gcc/ChangeLog:
> 2017-01-xx  Andre Vieira  <andre.simoesdiasvieira@arm.com>
>
>    * config/arm/arm.md (*ldc): New.
>    (*stc): New.
>    (<ldc>): New.
>    (<stc>): New.
>    * config/arm/arm.c (arm_coproc_builtin_available): Add
>    support for ldc,ldcl,stc,stcl,ldc2,ldc2l,stc2 and stc2l.
>    (arm_coproc_ldc_stc_legitimate_address): New.
>    * config/arm/arm-builtins.c (arm_type_qualifiers): Add
>    'qualifier_const_pointer'.
>    (LDC_QUALIFIERS): Define to...
>    (arm_ldc_qualifiers): ... this. New.
>    (STC_QUALIFIERS): Define to...
>    (arm_stc_qualifiers): ... this. New.
>    * config/arm/arm-protos.h
>    (arm_coproc_ldc_stc_legitimate_address): New.
>    * config/arm/arm_acle.h (__arm_ldc, __arm_ldcl, __arm_stc,
>    __arm_stcl, __arm_ldc2, __arm_ldc2l, __arm_stc2, __arm_stc2l): New.
>    * config/arm/arm_acle_builtins.def (ldc, ldc2, ldcl, ldc2l, stc,
>    stc2, stcl, stc2l): New.
>    * config/arm/constraints.md (Uz): New.
>    * config/arm/iterators.md (LDCI, STCI, ldc, stc, LDC STC): New.
>    * config/arm/unspecs.md (VUNSPEC_LDC, VUNSPEC_LDC2, VUNSPEC_LDCL,
>    VUNSPEC_LDC2L, VUNSPEC_STC, VUNSPEC_STC2, VUNSPEC_STCL,
>    VUNSPEC_STC2L): New.
>
> gcc/testsuite/ChangeLog:
>
> 2017-01-xx  Andre Vieira  <andre.simoesdiasvieira@arm.com>
>
>    * gcc.target/arm/acle/ldc: New.
>    * gcc.target/arm/acle/ldc2: New.
>    * gcc.target/arm/acle/ldcl: New.
>    * gcc.target/arm/acle/ldc2l: New.
>    * gcc.target/arm/acle/stc: New.
>    * gcc.target/arm/acle/stc2: New.
>    * gcc.target/arm/acle/stcl: New.
>    * gcc.target/arm/acle/stc2l: New.
diff mbox

Patch

diff --git a/gcc/config/arm/arm-builtins.c b/gcc/config/arm/arm-builtins.c
index 2bb9e22bb8cf7ae2d8a5698e970af4845016d93c..103ae762f7c2de62f445a5f48b80222ac4f37767 100644
--- a/gcc/config/arm/arm-builtins.c
+++ b/gcc/config/arm/arm-builtins.c
@@ -51,6 +51,8 @@  enum arm_type_qualifiers
   qualifier_const = 0x2, /* 1 << 1  */
   /* T *foo.  */
   qualifier_pointer = 0x4, /* 1 << 2  */
+  /* const T * foo.  */
+  qualifier_const_pointer = 0x6,
   /* Used when expanding arguments if an operand could
      be an immediate.  */
   qualifier_immediate = 0x8, /* 1 << 3  */
@@ -178,6 +180,23 @@  arm_cdp_qualifiers[SIMD_MAX_BUILTIN_ARGS]
       qualifier_unsigned_immediate };
 #define CDP_QUALIFIERS \
   (arm_cdp_qualifiers)
+
+/* void (unsigned immediate, unsigned immediate,  const void *).  */
+static enum arm_type_qualifiers
+arm_ldc_qualifiers[SIMD_MAX_BUILTIN_ARGS]
+  = { qualifier_void, qualifier_unsigned_immediate,
+      qualifier_unsigned_immediate, qualifier_const_pointer };
+#define LDC_QUALIFIERS \
+  (arm_ldc_qualifiers)
+
+/* void (unsigned immediate, unsigned immediate,  void *).  */
+static enum arm_type_qualifiers
+arm_stc_qualifiers[SIMD_MAX_BUILTIN_ARGS]
+  = { qualifier_void, qualifier_unsigned_immediate,
+      qualifier_unsigned_immediate, qualifier_pointer };
+#define STC_QUALIFIERS \
+  (arm_stc_qualifiers)
+
 /* The first argument (return type) of a store should be void type,
    which we represent with qualifier_void.  Their first operand will be
    a DImode pointer to the location to store to, so we must use
diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h
index 4d6a3ed3d47952728c3c4c1a8bd5ec0b9274bb16..1b1623917273b7f7d1708b2429fc6bdd0c1613ab 100644
--- a/gcc/config/arm/arm-protos.h
+++ b/gcc/config/arm/arm-protos.h
@@ -177,6 +177,7 @@  extern void arm_split_compare_and_swap (rtx op[]);
 extern void arm_split_atomic_op (enum rtx_code, rtx, rtx, rtx, rtx, rtx, rtx);
 extern rtx arm_load_tp (rtx);
 extern bool arm_coproc_builtin_available (enum unspecv);
+extern bool arm_coproc_ldc_stc_legitimate_address (rtx);
 
 #if defined TREE_CODE
 extern void arm_init_cumulative_args (CUMULATIVE_ARGS *, tree, rtx, tree);
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index 64599981961d80c5493a88f30743b98a138ca932..5e098461ec71d16768c9e6cde4e8cc5303c3eb59 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -30904,10 +30904,18 @@  arm_coproc_builtin_available (enum unspecv builtin)
   switch (builtin)
     {
       case VUNSPEC_CDP:
+      case VUNSPEC_LDC:
+      case VUNSPEC_LDCL:
+      case VUNSPEC_STC:
+      case VUNSPEC_STCL:
 	if (arm_arch4)
 	  return true;
 	break;
       case VUNSPEC_CDP2:
+      case VUNSPEC_LDC2:
+      case VUNSPEC_LDC2L:
+      case VUNSPEC_STC2:
+      case VUNSPEC_STC2L:
 	/* Only present in ARMv5*, ARMv6 (but not ARMv6-M), ARMv7* and
 	   ARMv8-{A,M}.  */
 	if (arm_arch5)
@@ -30918,4 +30926,55 @@  arm_coproc_builtin_available (enum unspecv builtin)
     }
   return false;
 }
+
+/* This function returns true if OP is a valid memory operand for the ldc and
+   stc coprocessor instructions and false otherwise.  */
+
+bool
+arm_coproc_ldc_stc_legitimate_address (rtx op)
+{
+  HOST_WIDE_INT range;
+  /* Has to be a memory operand.  */
+  if (!MEM_P (op))
+    return false;
+
+  op = XEXP (op, 0);
+
+  /* We accept registers.  */
+  if (REG_P (op))
+    return true;
+
+  switch GET_CODE (op)
+    {
+      case PLUS:
+	{
+	  /* Or registers with an offset.  */
+	  if (!REG_P (XEXP (op, 0)))
+	    return false;
+
+	  op = XEXP (op, 1);
+
+	  /* The offset must be an immediate though.  */
+	  if (!CONST_INT_P (op))
+	    return false;
+
+	  range = INTVAL (op);
+
+	  /* Within the range of [-1020,1020].  */
+	  if (!IN_RANGE (range, -1020, 1020))
+	    return false;
+
+	  /* And a multiple of 4.  */
+	  return (range % 4) == 0;
+	}
+      case PRE_INC:
+      case POST_INC:
+      case PRE_DEC:
+      case POST_DEC:
+	return REG_P (XEXP (op, 0));
+      default:
+	gcc_unreachable ();
+    }
+  return false;
+}
 #include "gt-arm.h"
diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md
index b5325013c2179c06e0079f35a5c5bd0ae9388d4c..799a9cae3707202ce45ae72fcbeda97dccf62529 100644
--- a/gcc/config/arm/arm.md
+++ b/gcc/config/arm/arm.md
@@ -11939,6 +11939,44 @@ 
   [(set_attr "length" "4")
    (set_attr "type" "coproc")])
 
+(define_insn "*ldc"
+  [(unspec_volatile [(match_operand:SI 0 "immediate_operand" "n")
+		     (match_operand:SI 1 "immediate_operand" "n")
+		     (match_operand:SI 2 "memory_operand" "Uz")] LDCI)]
+  "arm_coproc_builtin_available (VUNSPEC_<LDC>)"
+{
+  arm_const_bounds (operands[0], 0, 16);
+  arm_const_bounds (operands[1], 0, (1 << 5));
+  return "<ldc>\\tp%c0, CR%c1, %2";
+}
+  [(set_attr "length" "4")
+   (set_attr "type" "coproc")])
+
+(define_insn "*stc"
+  [(unspec_volatile [(match_operand:SI 0 "immediate_operand" "n")
+		     (match_operand:SI 1 "immediate_operand" "n")
+		     (match_operand:SI 2 "memory_operand" "=Uz")] STCI)]
+  "arm_coproc_builtin_available (VUNSPEC_<STC>)"
+{
+  arm_const_bounds (operands[0], 0, 16);
+  arm_const_bounds (operands[1], 0, (1 << 5));
+  return "<stc>\\tp%c0, CR%c1, %2";
+}
+  [(set_attr "length" "4")
+   (set_attr "type" "coproc")])
+
+(define_expand "<ldc>"
+  [(unspec_volatile [(match_operand:SI 0 "immediate_operand")
+		     (match_operand:SI 1 "immediate_operand")
+		     (mem:SI (match_operand:SI 2 "s_register_operand"))] LDCI)]
+  "arm_coproc_builtin_available (VUNSPEC_<LDC>)")
+
+(define_expand "<stc>"
+  [(unspec_volatile [(match_operand:SI 0 "immediate_operand")
+		     (match_operand:SI 1 "immediate_operand")
+		     (mem:SI (match_operand:SI 2 "s_register_operand"))] STCI)]
+  "arm_coproc_builtin_available (VUNSPEC_<STC>)")
+
 ;; Vector bits common to IWMMXT and Neon
 (include "vec-common.md")
 ;; Load the Intel Wireless Multimedia Extension patterns
diff --git a/gcc/config/arm/arm_acle.h b/gcc/config/arm/arm_acle.h
index 08add2b7ac79f487dea92477d39b9db886a3f027..1c7a44d9087b4c4b9e46669deb833f585564d053 100644
--- a/gcc/config/arm/arm_acle.h
+++ b/gcc/config/arm/arm_acle.h
@@ -41,6 +41,33 @@  __arm_cdp (const unsigned int __coproc, const unsigned int __opc1,
   return __builtin_arm_cdp (__coproc, __opc1, __CRd, __CRn, __CRm, __opc2);
 }
 
+__extension__ static __inline void __attribute__ ((__always_inline__))
+__arm_ldc (const unsigned int __coproc, const unsigned int __CRd,
+	   const void * __p)
+{
+  return __builtin_arm_ldc (__coproc, __CRd, __p);
+}
+
+__extension__ static __inline void __attribute__ ((__always_inline__))
+__arm_ldcl (const unsigned int __coproc, const unsigned int __CRd,
+	    const void * __p)
+{
+  return __builtin_arm_ldcl (__coproc, __CRd, __p);
+}
+
+__extension__ static __inline void __attribute__ ((__always_inline__))
+__arm_stc (const unsigned int __coproc, const unsigned int __CRd,
+	   void * __p)
+{
+  return __builtin_arm_stc (__coproc, __CRd, __p);
+}
+
+__extension__ static __inline void __attribute__ ((__always_inline__))
+__arm_stcl (const unsigned int __coproc, const unsigned int __CRd,
+	    void * __p)
+{
+  return __builtin_arm_stcl (__coproc, __CRd, __p);
+}
 #if __ARM_ARCH >= 5
 __extension__ static __inline void __attribute__ ((__always_inline__))
 __arm_cdp2 (const unsigned int __coproc, const unsigned int __opc1,
@@ -49,6 +76,34 @@  __arm_cdp2 (const unsigned int __coproc, const unsigned int __opc1,
 {
   return __builtin_arm_cdp2 (__coproc, __opc1, __CRd, __CRn, __CRm, __opc2);
 }
+
+__extension__ static __inline void __attribute__ ((__always_inline__))
+__arm_ldc2 (const unsigned int __coproc, const unsigned int __CRd,
+	    const void * __p)
+{
+  return __builtin_arm_ldc2 (__coproc, __CRd, __p);
+}
+
+__extension__ static __inline void __attribute__ ((__always_inline__))
+__arm_ldc2l (const unsigned int __coproc, const unsigned int __CRd,
+	     const void * __p)
+{
+  return __builtin_arm_ldc2l (__coproc, __CRd, __p);
+}
+
+__extension__ static __inline void __attribute__ ((__always_inline__))
+__arm_stc2 (const unsigned int __coproc, const unsigned int __CRd,
+	    void * __p)
+{
+  return __builtin_arm_stc2 (__coproc, __CRd, __p);
+}
+
+__extension__ static __inline void __attribute__ ((__always_inline__))
+__arm_stc2l (const unsigned int __coproc, const unsigned int __CRd,
+	     void * __p)
+{
+  return __builtin_arm_stc2l (__coproc, __CRd, __p);
+}
 #endif /*  __ARM_ARCH >= 5.  */
 #endif /* (!__thumb__ || __thumb2__) &&  __ARM_ARCH >= 4.  */
 
diff --git a/gcc/config/arm/arm_acle_builtins.def b/gcc/config/arm/arm_acle_builtins.def
index 03b5bf88ef2632bceedba1e64c0f83bc50337364..eb6168d98a897280aef2954372613487ca256fb0 100644
--- a/gcc/config/arm/arm_acle_builtins.def
+++ b/gcc/config/arm/arm_acle_builtins.def
@@ -26,3 +26,11 @@  VAR1 (UBINOP, crc32ch, si)
 VAR1 (UBINOP, crc32cw, si)
 VAR1 (CDP, cdp, void)
 VAR1 (CDP, cdp2, void)
+VAR1 (LDC, ldc, void)
+VAR1 (LDC, ldc2, void)
+VAR1 (LDC, ldcl, void)
+VAR1 (LDC, ldc2l, void)
+VAR1 (STC, stc, void)
+VAR1 (STC, stc2, void)
+VAR1 (STC, stcl, void)
+VAR1 (STC, stc2l, void)
diff --git a/gcc/config/arm/constraints.md b/gcc/config/arm/constraints.md
index ceea46ae9fa31fbab855e8250a410d3e6d8177bd..0bd87dd32af1de9a0be58640ec94e83a76ce092c 100644
--- a/gcc/config/arm/constraints.md
+++ b/gcc/config/arm/constraints.md
@@ -447,6 +447,12 @@ 
  (match_code "symbol_ref")
 )
 
+(define_memory_constraint "Uz"
+ "@internal
+  A memory access that is accessible as an LDC/STC operand"
+ (and (match_code "mem")
+      (match_test "arm_coproc_ldc_stc_legitimate_address (op)")))
+
 ;; We used to have constraint letters for S and R in ARM state, but
 ;; all uses of these now appear to have been removed.
 
diff --git a/gcc/config/arm/iterators.md b/gcc/config/arm/iterators.md
index 86d6aa70e5766bc42a4209f14e929942ee63b773..ec74868a9b99c3a4bb379b82d3a77057ce238f03 100644
--- a/gcc/config/arm/iterators.md
+++ b/gcc/config/arm/iterators.md
@@ -948,3 +948,19 @@ 
 (define_int_iterator CDPI [VUNSPEC_CDP VUNSPEC_CDP2])
 (define_int_attr cdp [(VUNSPEC_CDP "cdp") (VUNSPEC_CDP2 "cdp2")])
 (define_int_attr CDP [(VUNSPEC_CDP "CDP") (VUNSPEC_CDP2 "CDP2")])
+
+;; An iterator for the LDC coprocessor instruction
+(define_int_iterator LDCI [VUNSPEC_LDC VUNSPEC_LDC2
+			   VUNSPEC_LDCL VUNSPEC_LDC2L])
+(define_int_attr ldc [(VUNSPEC_LDC "ldc") (VUNSPEC_LDC2 "ldc2")
+		      (VUNSPEC_LDCL "ldcl") (VUNSPEC_LDC2L "ldc2l")])
+(define_int_attr LDC [(VUNSPEC_LDC "LDC") (VUNSPEC_LDC2 "LDC2")
+		      (VUNSPEC_LDCL "LDCL") (VUNSPEC_LDC2L "LDC2L")])
+
+;; An iterator for the STC coprocessor instructions
+(define_int_iterator STCI [VUNSPEC_STC VUNSPEC_STC2
+			   VUNSPEC_STCL VUNSPEC_STC2L])
+(define_int_attr stc [(VUNSPEC_STC "stc") (VUNSPEC_STC2 "stc2")
+		      (VUNSPEC_STCL "stcl") (VUNSPEC_STC2L "stc2l")])
+(define_int_attr STC [(VUNSPEC_STC "STC") (VUNSPEC_STC2 "STC2")
+		      (VUNSPEC_STCL "STCL") (VUNSPEC_STC2L "STC2L")])
diff --git a/gcc/config/arm/unspecs.md b/gcc/config/arm/unspecs.md
index 01dd700a0af8043ce40ada939f9b0c34d846eded..2cc5eb8e1dbeafd156567c8e507e39d8057a99f1 100644
--- a/gcc/config/arm/unspecs.md
+++ b/gcc/config/arm/unspecs.md
@@ -152,6 +152,14 @@ 
   VUNSPEC_PROBE_STACK_RANGE ; Represent stack range probing.
   VUNSPEC_CDP		; Represent the coprocessor cdp instruction.
   VUNSPEC_CDP2		; Represent the coprocessor cdp2 instruction.
+  VUNSPEC_LDC		; Represent the coprocessor ldc instruction.
+  VUNSPEC_LDC2		; Represent the coprocessor ldc2 instruction.
+  VUNSPEC_LDCL		; Represent the coprocessor ldcl instruction.
+  VUNSPEC_LDC2L		; Represent the coprocessor ldc2l instruction.
+  VUNSPEC_STC		; Represent the coprocessor stc instruction.
+  VUNSPEC_STC2		; Represent the coprocessor stc2 instruction.
+  VUNSPEC_STCL		; Represent the coprocessor stcl instruction.
+  VUNSPEC_STC2L		; Represent the coprocessor stc2l instruction.
 ])
 
 ;; Enumerators for NEON unspecs.
diff --git a/gcc/testsuite/gcc.target/arm/acle/ldc.c b/gcc/testsuite/gcc.target/arm/acle/ldc.c
new file mode 100644
index 0000000000000000000000000000000000000000..f45f25d8c973e6aff715fcf17fbd9d2fc1ac2edf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/acle/ldc.c
@@ -0,0 +1,18 @@ 
+/* Test the ldc ACLE intrinsic.  */
+
+/* { dg-do assemble } */
+/* { dg-options "-save-temps" } */
+/* { dg-require-effective-target arm_coproc1_ok } */
+
+#include "arm_acle.h"
+
+extern void * p;
+
+void test_ldc (void)
+{
+  __arm_ldc (10, 1, p + 4);
+  __arm_ldc (11, 1, p + 1024);
+}
+
+/* { dg-final { scan-assembler "ldc\tp10, CR1, \[r\[0-9\]+" } } */
+/* { dg-final { scan-assembler "ldc\tp11, CR1, \[r\[0-9\]+\]\n" } } */
diff --git a/gcc/testsuite/gcc.target/arm/acle/ldc2.c b/gcc/testsuite/gcc.target/arm/acle/ldc2.c
new file mode 100644
index 0000000000000000000000000000000000000000..433bf8a12043c3876e8c961d86b64365faa4b30f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/acle/ldc2.c
@@ -0,0 +1,18 @@ 
+/* Test the ldc2 ACLE intrinsic.  */
+
+/* { dg-do assemble } */
+/* { dg-options "-save-temps" } */
+/* { dg-require-effective-target arm_coproc2_ok } */
+
+#include "arm_acle.h"
+
+extern void * p;
+
+void test_ldc2 (void)
+{
+  __arm_ldc2 (10, 1, p - 120);
+  __arm_ldc2 (11, 1, p - 122);
+}
+
+/* { dg-final { scan-assembler "ldc2\tp10, CR1, \[r\[0-9\]+" } } */
+/* { dg-final { scan-assembler "ldc2\tp11, CR1, \[r\[0-9\]+\]\n" } } */
diff --git a/gcc/testsuite/gcc.target/arm/acle/ldc2l.c b/gcc/testsuite/gcc.target/arm/acle/ldc2l.c
new file mode 100644
index 0000000000000000000000000000000000000000..88c8aa447656d64c0890101005708675c8f02b38
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/acle/ldc2l.c
@@ -0,0 +1,18 @@ 
+/* Test the ldc2l ACLE intrinsic.  */
+
+/* { dg-do assemble } */
+/* { dg-options "-save-temps" } */
+/* { dg-require-effective-target arm_coproc2_ok } */
+
+#include "arm_acle.h"
+
+extern void * p;
+
+void test_ldc2l (void)
+{
+  __arm_ldc2l (10, 1, p - 120);
+  __arm_ldc2l (11, 1, p - 122);
+}
+
+/* { dg-final { scan-assembler "ldc2l\tp10, CR1, \[r\[0-9\]+" } } */
+/* { dg-final { scan-assembler "ldc2l\tp11, CR1, \[r\[0-9\]+\]\n" } } */
diff --git a/gcc/testsuite/gcc.target/arm/acle/ldcl.c b/gcc/testsuite/gcc.target/arm/acle/ldcl.c
new file mode 100644
index 0000000000000000000000000000000000000000..72a97f1d7b7d1a1aea99af638240ad50b708e137
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/acle/ldcl.c
@@ -0,0 +1,18 @@ 
+/* Test the ldcl ACLE intrinsic.  */
+
+/* { dg-do assemble } */
+/* { dg-options "-save-temps" } */
+/* { dg-require-effective-target arm_coproc1_ok } */
+
+#include "arm_acle.h"
+
+extern void * p;
+
+void test_ldcl (void)
+{
+  __arm_ldcl (10, 1, p + 4);
+  __arm_ldcl (11, 1, p + 1024);
+}
+
+/* { dg-final { scan-assembler "ldcl\tp10, CR1, \[r\[0-9\]+" } } */
+/* { dg-final { scan-assembler "ldcl\tp11, CR1, \[r\[0-9\]+\]\n" } } */
diff --git a/gcc/testsuite/gcc.target/arm/acle/stc.c b/gcc/testsuite/gcc.target/arm/acle/stc.c
new file mode 100644
index 0000000000000000000000000000000000000000..7c6e04fe0fe5cd63a259566c34d976657ae03509
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/acle/stc.c
@@ -0,0 +1,18 @@ 
+/* Test the stc ACLE intrinsic.  */
+
+/* { dg-do assemble } */
+/* { dg-options "-save-temps" } */
+/* { dg-require-effective-target arm_coproc1_ok } */
+
+#include "arm_acle.h"
+
+extern void * p;
+
+void test_stc (void)
+{
+  __arm_stc (10, 1, p + 4);
+  __arm_stc (11, 1, p + 1024);
+}
+
+/* { dg-final { scan-assembler "stc\tp10, CR1, \[r\[0-9\]+" } } */
+/* { dg-final { scan-assembler "stc\tp11, CR1, \[r\[0-9\]+\]\n" } } */
diff --git a/gcc/testsuite/gcc.target/arm/acle/stc2.c b/gcc/testsuite/gcc.target/arm/acle/stc2.c
new file mode 100644
index 0000000000000000000000000000000000000000..1578f7b113676c9d5692c7e5b48b910f091ca29c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/acle/stc2.c
@@ -0,0 +1,18 @@ 
+/* Test the stc2 ACLE intrinsic.  */
+
+/* { dg-do assemble } */
+/* { dg-options "-save-temps" } */
+/* { dg-require-effective-target arm_coproc2_ok } */
+
+#include "arm_acle.h"
+
+extern void * p;
+
+void test_stc2 (void)
+{
+  __arm_stc2 (10, 1, p - 120);
+  __arm_stc2 (11, 1, p - 122);
+}
+
+/* { dg-final { scan-assembler "stc2\tp10, CR1, \[r\[0-9\]+" } } */
+/* { dg-final { scan-assembler "stc2\tp11, CR1, \[r\[0-9\]+\]\n" } } */
diff --git a/gcc/testsuite/gcc.target/arm/acle/stc2l.c b/gcc/testsuite/gcc.target/arm/acle/stc2l.c
new file mode 100644
index 0000000000000000000000000000000000000000..7adbd60d48ac3f5a0f77dce98a3d3ba931ec58e3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/acle/stc2l.c
@@ -0,0 +1,18 @@ 
+/* Test the stc2l ACLE intrinsic.  */
+
+/* { dg-do assemble } */
+/* { dg-options "-save-temps" } */
+/* { dg-require-effective-target arm_coproc2_ok } */
+
+#include "arm_acle.h"
+
+extern void * p;
+
+void test_stc2l (void)
+{
+  __arm_stc2l (10, 1, p - 120);
+  __arm_stc2l (11, 1, p - 122);
+}
+
+/* { dg-final { scan-assembler "stc2l\tp10, CR1, \[r\[0-9\]+" } } */
+/* { dg-final { scan-assembler "stc2l\tp11, CR1, \[r\[0-9\]+\]\n" } } */
diff --git a/gcc/testsuite/gcc.target/arm/acle/stcl.c b/gcc/testsuite/gcc.target/arm/acle/stcl.c
new file mode 100644
index 0000000000000000000000000000000000000000..2fd5edd02d7e83efdcfaa2203ef1be9f612e414e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/acle/stcl.c
@@ -0,0 +1,18 @@ 
+/* Test the stcl ACLE intrinsic.  */
+
+/* { dg-do assemble } */
+/* { dg-options "-save-temps" } */
+/* { dg-require-effective-target arm_coproc1_ok } */
+
+#include "arm_acle.h"
+
+extern void * p;
+
+void test_stcl (void)
+{
+  __arm_stcl (14, 10, p + 4);
+  __arm_stcl (10, 10, p + 1024);
+}
+
+/* { dg-final { scan-assembler "stcl\tp14, CR10, \[r\[0-9\]+" } } */
+/* { dg-final { scan-assembler "stcl\tp10, CR10, \[r\[0-9\]+\]\n" } } */