diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 91dfcd71a4b213de59d5153ec53e664eac3c900c..2cc61d68cf35f031f4c3b1bd68c21c281d91e74b 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,22 @@
+2020-01-27  Claudiu Zissulescu  <claziss@synopsys.com>
+	    Petro Karashchenko  <petro.karashchenko@ring.com>
+
+	* config/arc/arc.c (arc_is_uncached_mem_p): Check struct
+	attributes if needed.
+	(prepare_move_operands): Generate special unspec instruction for
+	direct access.
+	(arc_isuncached_mem_p): Propagate uncached attribute to each
+	structure member.
+	* config/arc/arc.md (VUNSPEC_ARC_LDDI): Define.
+	(VUNSPEC_ARC_STDI): Likewise.
+	(ALLI): New mode iterator.
+	(mALLI): New mode attribute.
+	(lddi): New instruction pattern.
+	(stdi): Likewise.
+	(stdidi_split): Split instruction for architectures which are not
+	supporting ll64 option.
+	(lddidi_split): Likewise.
+
 2020-01-27  Richard Sandiford  <richard.sandiford@arm.com>
 
 	PR rtl-optimization/92989
diff --git a/gcc/config/arc/arc.c b/gcc/config/arc/arc.c
index 22475f2732e6a4eb2604b89587a05fb3e104d3e8..e1a865f02e699a8ed477e842ef4480d5f1e26d10 100644
--- a/gcc/config/arc/arc.c
+++ b/gcc/config/arc/arc.c
@@ -9091,49 +9091,71 @@ arc_get_aux_arg (rtx pat, int *auxr)
 bool
 prepare_move_operands (rtx *operands, machine_mode mode)
 {
-  /* First handle aux attribute.  */
-  if (mode == SImode
-      && (MEM_P (operands[0]) || MEM_P (operands[1])))
+  if ((MEM_P (operands[0]) || MEM_P (operands[1]))
+      && SCALAR_INT_MODE_P (mode))
     {
-      rtx tmp;
-      int auxr = 0;
-      if (MEM_P (operands[0]) && arc_is_aux_reg_p (operands[0]))
+      /* First handle aux attribute.  */
+      if (mode == SImode)
 	{
-	  /* Save operation.  */
-	  if (arc_get_aux_arg (operands[0], &auxr))
+	  rtx tmp;
+	  int auxr = 0;
+	  if (MEM_P (operands[0]) && arc_is_aux_reg_p (operands[0]))
 	    {
-	      tmp = gen_reg_rtx (SImode);
-	      emit_move_insn (tmp, GEN_INT (auxr));
+	      /* Save operation.  */
+	      if (arc_get_aux_arg (operands[0], &auxr))
+		{
+		  tmp = gen_reg_rtx (SImode);
+		  emit_move_insn (tmp, GEN_INT (auxr));
+		}
+	      else
+		tmp = XEXP (operands[0], 0);
+
+	      operands[1] = force_reg (SImode, operands[1]);
+	      emit_insn (gen_rtx_UNSPEC_VOLATILE
+			 (VOIDmode, gen_rtvec (2, operands[1], tmp),
+			  VUNSPEC_ARC_SR));
+	      return true;
 	    }
-	  else
+	  if (MEM_P (operands[1]) && arc_is_aux_reg_p (operands[1]))
 	    {
-	      tmp = XEXP (operands[0], 0);
+	      if (arc_get_aux_arg (operands[1], &auxr))
+		{
+		  tmp = gen_reg_rtx (SImode);
+		  emit_move_insn (tmp, GEN_INT (auxr));
+		}
+	      else
+		{
+		  tmp = XEXP (operands[1], 0);
+		  gcc_assert (GET_CODE (tmp) == SYMBOL_REF);
+		}
+	      /* Load operation.  */
+	      gcc_assert (REG_P (operands[0]));
+	      emit_insn (gen_rtx_SET (operands[0],
+				      gen_rtx_UNSPEC_VOLATILE
+				      (SImode, gen_rtvec (1, tmp),
+				       VUNSPEC_ARC_LR)));
+	      return true;
 	    }
-
-	  operands[1] = force_reg (SImode, operands[1]);
+	}
+      /* Second, we check for the uncached.  */
+      if (arc_is_uncached_mem_p (operands[0]))
+	{
+	  if (!REG_P (operands[1]))
+	    operands[1] = force_reg (mode, operands[1]);
 	  emit_insn (gen_rtx_UNSPEC_VOLATILE
-		     (VOIDmode, gen_rtvec (2, operands[1], tmp),
-		      VUNSPEC_ARC_SR));
+		     (VOIDmode, gen_rtvec (2, operands[0], operands[1]),
+		      VUNSPEC_ARC_STDI));
 	  return true;
 	}
-      if (MEM_P (operands[1]) && arc_is_aux_reg_p (operands[1]))
+      if (arc_is_uncached_mem_p (operands[1]))
 	{
-	  if (arc_get_aux_arg (operands[1], &auxr))
-	    {
-	      tmp = gen_reg_rtx (SImode);
-	      emit_move_insn (tmp, GEN_INT (auxr));
-	    }
-	  else
-	    {
-	      tmp = XEXP (operands[1], 0);
-	      gcc_assert (GET_CODE (tmp) == SYMBOL_REF);
-	    }
-	  /* Load operation.  */
-	  gcc_assert (REG_P (operands[0]));
-	  emit_insn (gen_rtx_SET (operands[0],
-				  gen_rtx_UNSPEC_VOLATILE
-				  (SImode, gen_rtvec (1, tmp),
-				   VUNSPEC_ARC_LR)));
+	  if (MEM_P (operands[0]))
+	    operands[0] = force_reg (mode, operands[0]);
+	  emit_insn (gen_rtx_SET
+		     (operands[0],
+		      gen_rtx_UNSPEC_VOLATILE
+		      (mode, gen_rtvec (1, operands[1]),
+		       VUNSPEC_ARC_LDDI)));
 	  return true;
 	}
     }
@@ -11162,24 +11184,40 @@ arc_is_uncached_mem_p (rtx pat)
     return false;
 
   /* Get the attributes.  */
-  if (TREE_CODE (addr) == MEM_REF)
+  if (TREE_CODE (addr) == MEM_REF
+      || TREE_CODE (addr) == VAR_DECL)
     {
       attrs = TYPE_ATTRIBUTES (TREE_TYPE (addr));
       if (lookup_attribute ("uncached", attrs))
 	return true;
-
+    }
+  if (TREE_CODE (addr) == MEM_REF)
+    {
       attrs = TYPE_ATTRIBUTES (TREE_TYPE (TREE_OPERAND (addr, 0)));
       if (lookup_attribute ("uncached", attrs))
 	return true;
-    }
-
-  /* For COMPONENT_REF, use the FIELD_DECL from tree operand 1.  */
-  if (TREE_CODE (addr) == COMPONENT_REF)
-    {
       attrs = TYPE_ATTRIBUTES (TREE_TYPE (TREE_OPERAND (addr, 1)));
       if (lookup_attribute ("uncached", attrs))
 	return true;
     }
+
+  /* Check the definitions of the structs.  */
+  while (handled_component_p (addr))
+    {
+      if (TREE_CODE (addr) == COMPONENT_REF)
+	{
+	  attrs = TYPE_ATTRIBUTES (TREE_TYPE (addr));
+	  if (lookup_attribute ("uncached", attrs))
+	    return true;
+	  attrs = TYPE_ATTRIBUTES (TREE_TYPE (TREE_OPERAND (addr, 0)));
+	  if (lookup_attribute ("uncached", attrs))
+	    return true;
+	  attrs = TYPE_ATTRIBUTES (TREE_TYPE (TREE_OPERAND (addr, 1)));
+	  if (lookup_attribute ("uncached", attrs))
+	    return true;
+	}
+      addr = TREE_OPERAND (addr, 0);
+    }
   return false;
 }
 
diff --git a/gcc/config/arc/arc.md b/gcc/config/arc/arc.md
index cf7aa8d83c989ec9a3060543c1bff6eab0efbf07..46cb254ed28e805f62ee764cb9bf3814bf923fb8 100644
--- a/gcc/config/arc/arc.md
+++ b/gcc/config/arc/arc.md
@@ -164,6 +164,8 @@
   VUNSPEC_ARC_BLOCKAGE
   VUNSPEC_ARC_EH_RETURN
   VUNSPEC_ARC_ARC600_RTIE
+  VUNSPEC_ARC_LDDI
+  VUNSPEC_ARC_STDI
   ])
 
 (define_constants
@@ -4682,6 +4684,64 @@ core_3, archs4x, archs4xd, archs4xd_slow"
   [(set_attr "length" "8,4,8,4")
    (set_attr "type" "sr,sr,sr,sr")])
 
+(define_mode_iterator ALLI [QI HI SI (DI "TARGET_LL64")])
+(define_mode_attr mALLI [(QI "b") (HI "%_") (SI "") (DI "d")])
+
+(define_insn "lddi<mode>"
+  [(set (match_operand:ALLI 0 "register_operand" "=r")
+	(unspec_volatile:ALLI [(match_operand:ALLI 1 "memory_operand" "m")]
+			      VUNSPEC_ARC_LDDI))]
+  ""
+  "ld<mALLI>%U1.di\\t%0,%1"
+  [(set_attr "type" "load")])
+
+(define_insn "stdi<mode>"
+  [(unspec_volatile [(match_operand:ALLI 0 "memory_operand"    "m,m,Usc")
+		     (match_operand:ALLI 1 "nonmemory_operand" "r,Cm3,i")]
+		    VUNSPEC_ARC_STDI)]
+  ""
+  "st<mALLI>%U0.di\\t%1,%0"
+  [(set_attr "length" "*,*,8")
+   (set_attr "type" "store")])
+
+(define_insn_and_split "*stdidi_split"
+  [(unspec_volatile [(match_operand:DI 0 "memory_operand"   "m")
+		     (match_operand:DI 1 "register_operand" "r")]
+		    VUNSPEC_ARC_STDI)]
+  "!TARGET_LL64"
+  "#"
+  "&& reload_completed"
+  [(unspec_volatile:SI [(match_dup 2) (match_dup 3)] VUNSPEC_ARC_STDI)
+   (unspec_volatile:SI [(match_dup 4) (match_dup 5)] VUNSPEC_ARC_STDI)]
+  "
+  {
+   operands[3] = gen_lowpart (SImode, operands[1]);
+   operands[5] = gen_highpart_mode (SImode, DImode, operands[1]);
+   operands[2] = gen_lowpart (SImode, operands[0]);
+   operands[4] = gen_highpart (SImode, operands[0]);
+  }
+  "
+  )
+
+(define_insn_and_split "*lddidi_split"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+	(unspec_volatile:DI [(match_operand:DI 1 "memory_operand" "m")]
+			    VUNSPEC_ARC_LDDI))]
+  "!TARGET_LL64"
+  "#"
+  "&& reload_completed"
+  [(set (match_dup 2) (unspec_volatile:SI [(match_dup 3)] VUNSPEC_ARC_LDDI))
+   (set (match_dup 4) (unspec_volatile:SI [(match_dup 5)] VUNSPEC_ARC_LDDI))]
+  "
+  {
+   operands[3] = gen_lowpart (SImode, operands[1]);
+   operands[5] = gen_highpart (SImode, operands[1]);
+   operands[2] = gen_lowpart (SImode, operands[0]);
+   operands[4] = gen_highpart (SImode, operands[0]);
+  }
+  "
+  )
+
 (define_insn "trap_s"
   [(unspec_volatile [(match_operand:SI 0 "immediate_operand" "L,Cal")]
 		   VUNSPEC_ARC_TRAP_S)]
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 16ddef07516a4d0579efcfb90137f499a3506d2f..991934272e06c7c9c1680f975988c40c46b2df55 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,14 @@
+2020-01-27  Claudiu Zissulescu  <claziss@synopsys.com>
+	    Petro Karashchenko  <petro.karashchenko@ring.com>
+
+	* gcc.target/arc/uncached-3.c: New test.
+	* gcc.target/arc/uncached-4.c: Likewise.
+	* gcc.target/arc/uncached-5.c: Likewise.
+	* gcc.target/arc/uncached-6.c: Likewise.
+	* gcc.target/arc/uncached-7.c: Likewise.
+	* gcc.target/arc/uncached-8.c: Likewise.
+	* gcc.target/arc/arc.exp (ll64): New predicate.
+
 2020-01-27  Richard Sandiford  <richard.sandiford@arm.com>
 
 	* gcc.dg/torture/pr93170.c: New test.
diff --git a/gcc/testsuite/gcc.target/arc/arc.exp b/gcc/testsuite/gcc.target/arc/arc.exp
index 8d1844edd22da4676644f6e2d08ebec5004e077b..501d4589c53c622d2af5092b7c28bdac9b462893 100644
--- a/gcc/testsuite/gcc.target/arc/arc.exp
+++ b/gcc/testsuite/gcc.target/arc/arc.exp
@@ -122,6 +122,15 @@ proc check_effective_target_dpfp { } {
     }]
 }
 
+# Return 1 if this is a compiler supporting LL64 option.
+proc check_effective_target_ll64 { } {
+    return [check_no_compiler_messages ll64 assembly {
+	#if !defined(__ARC_LL64__)
+	#error No ARC LL64
+	#endif
+    }]
+}
+
 # If a testcase doesn't have special options, use these.
 global DEFAULT_CFLAGS
 if ![info exists DEFAULT_CFLAGS] then {
diff --git a/gcc/testsuite/gcc.target/arc/uncached-1.c b/gcc/testsuite/gcc.target/arc/uncached-1.c
index 7a6bade81c4b0fc1aee9514065cdd8bf9447990d..fa5ecb7b7d395921306e548523b74ac7aa7164fe 100644
--- a/gcc/testsuite/gcc.target/arc/uncached-1.c
+++ b/gcc/testsuite/gcc.target/arc/uncached-1.c
@@ -8,4 +8,4 @@ int get_stat (void)
   return *status;
 }
 
-/* { dg-final { scan-assembler-times "ld\.di" 1 } } */
+/* { dg-final { scan-assembler-times "ld\.di" 2 } } */
diff --git a/gcc/testsuite/gcc.target/arc/uncached-2.c b/gcc/testsuite/gcc.target/arc/uncached-2.c
index 89eed326e01a41015ddc73995c524e81f7bc9a59..9d6bfbbb50e5c55097f5082fe63e19abc96b9c75 100644
--- a/gcc/testsuite/gcc.target/arc/uncached-2.c
+++ b/gcc/testsuite/gcc.target/arc/uncached-2.c
@@ -6,4 +6,4 @@ void clkgen_switch(unsigned int base, unsigned int offset, int val)
     (volatile unsigned int __attribute__ ((uncached)) *) (base + offset);
   *dest = val;
 }
-/* { dg-final { scan-assembler-times "st\.di" 1 } } */
+/* { dg-final { scan-assembler-times "st\.di" 2 } } */
diff --git a/gcc/testsuite/gcc.target/arc/uncached-3.c b/gcc/testsuite/gcc.target/arc/uncached-3.c
new file mode 100644
index 0000000000000000000000000000000000000000..f2a317b2816280d2f009cbe0b9da6b0bb0462c87
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arc/uncached-3.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+
+typedef volatile struct {
+    int a;
+    char *b;
+} __attribute__((uncached)) my_type_t;
+
+my_type_t x;
+
+void foo (my_type_t *p)
+{
+    p->a = 10;
+    p->b = 0;
+}
+
+void bar (void)
+{
+    x.a = 10;
+    x.b = 0;
+}
+
+/* { dg-final { scan-assembler-times "st\.di" 4 } } */
diff --git a/gcc/testsuite/gcc.target/arc/uncached-4.c b/gcc/testsuite/gcc.target/arc/uncached-4.c
new file mode 100644
index 0000000000000000000000000000000000000000..fecb16648b8e7a864137bb9950bf7ae45cee4493
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arc/uncached-4.c
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+
+#include <stddef.h>
+
+typedef enum
+{
+ e1,
+ e2
+} my_enum_t;
+
+typedef struct
+{
+  int a;
+  int *p;
+} my_struct_t;
+
+typedef volatile struct
+{
+  my_enum_t a;
+  my_struct_t b;
+} __attribute__((uncached)) my_type_t;
+
+my_type_t x;
+
+void foo (my_type_t *p)
+{
+  p->a = e2;
+  p->b.a = 10;
+  p->b.p = NULL;
+  *p->b.p = 10;
+}
+
+void bar (void)
+{
+  x.a = e2;
+  x.b.a = 10;
+  x.b.p = NULL;
+  *x.b.p = 10;
+}
+
+/* { dg-final { scan-assembler-times "st\.di" 6 } } */
+/* { dg-final { scan-assembler-times "ld\.di" 2 } } */
diff --git a/gcc/testsuite/gcc.target/arc/uncached-5.c b/gcc/testsuite/gcc.target/arc/uncached-5.c
new file mode 100644
index 0000000000000000000000000000000000000000..4fe0464fdde32b0bef0d626c1e3d2d5dadde588f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arc/uncached-5.c
@@ -0,0 +1,29 @@
+/* { dg-options "-O1" } */
+/* { dg-do compile } */
+
+#define RegWrSI(a,v)  (*(volatile __attribute__((uncached)) int *)(a)=(v))
+#define RegWrQI(a,v)  (*(volatile __attribute__((uncached)) char *)(a)=(v))
+#define RegWrHI(a,v)  (*(volatile __attribute__((uncached)) short *)(a)=(v))
+#define RegWrDI(a,v)  (*(volatile __attribute__((uncached)) long long *)(a)=(v))
+
+void foo (int arg, void *p)
+{
+  RegWrDI (p  , arg);
+  RegWrHI (p++, arg);
+  RegWrSI (p++, arg);
+  RegWrQI (p++, arg);
+}
+
+void bar (void)
+{
+  RegWrQI (0x40000, 1);
+  RegWrHI (0x40010, 2);
+  RegWrSI (0x40020, 4);
+  RegWrDI (0x40040, 8);
+}
+
+/* { dg-final { scan-assembler-times "stb\.di" 2 } } */
+/* { dg-final { scan-assembler-times "st\[hw\]\.di" 2 } } */
+/* { dg-final { scan-assembler-times "std\.di" 2 { target { ll64 } } } } */
+/* { dg-final { scan-assembler-times "st\.di" 2 { target { ll64 } } } } */
+/* { dg-final { scan-assembler-times "st\.di" 6 { target { ! { ll64 } } } } } */
diff --git a/gcc/testsuite/gcc.target/arc/uncached-6.c b/gcc/testsuite/gcc.target/arc/uncached-6.c
new file mode 100644
index 0000000000000000000000000000000000000000..581a9eccb3b9db9c1763489cc8852ea4450680fb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arc/uncached-6.c
@@ -0,0 +1,35 @@
+/* { dg-options "-O1" } */
+/* { dg-do compile } */
+
+#define RegRdSI(v,a) ((v) = *(volatile __attribute__((uncached)) int *)(a))
+#define RegRdQI(v,a) ((v) = *(volatile __attribute__((uncached)) char *)(a))
+#define RegRdHI(v,a) ((v) = *(volatile __attribute__((uncached)) short *)(a))
+#define RegRdDI(v,a) \
+  ((v) = *(volatile __attribute__((uncached)) long long *)(a))
+
+char a0;
+short a1;
+int a2;
+long long a3;
+
+void foox (void *p)
+{
+  RegRdQI (a0, p++);
+  RegRdHI (a1, p++);
+  RegRdSI (a2, p++);
+  RegRdDI (a3, p  );
+}
+
+void barx (int arg)
+{
+  RegRdQI (a0, 0x40000);
+  RegRdHI (a1, 0x40010);
+  RegRdSI (a2, 0x40020);
+  RegRdDI (a3, 0x40040);
+}
+
+/* { dg-final { scan-assembler-times "ldb\.di" 2 } } */
+/* { dg-final { scan-assembler-times "ld\[hw\]\.di" 2 } } */
+/* { dg-final { scan-assembler-times "ldd\.di" 2 { target { ll64 } } } } */
+/* { dg-final { scan-assembler-times "ld\.di" 2 { target { ll64 } } } } */
+/* { dg-final { scan-assembler-times "ld\.di" 6 { target { ! { ll64 } } } } } */
diff --git a/gcc/testsuite/gcc.target/arc/uncached-7.c b/gcc/testsuite/gcc.target/arc/uncached-7.c
new file mode 100644
index 0000000000000000000000000000000000000000..4001b8bd821a3523ec191aefd58777be38961b4e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arc/uncached-7.c
@@ -0,0 +1,11 @@
+/* { dg-options "-O1" } */
+/* { dg-do compile } */
+
+volatile __attribute__((uncached)) int s[20];
+
+void s_acc(void)
+{
+    s[10] = 15;
+}
+
+/* { dg-final { scan-assembler-times "st\.di" 1 } } */
diff --git a/gcc/testsuite/gcc.target/arc/uncached-8.c b/gcc/testsuite/gcc.target/arc/uncached-8.c
new file mode 100644
index 0000000000000000000000000000000000000000..060229b11df43d944e41a2590e5606aabcbfb180
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arc/uncached-8.c
@@ -0,0 +1,33 @@
+/* { dg-do compile } */
+
+#include <stddef.h>
+
+typedef struct
+{
+  int a;
+} my_structB_t;
+
+typedef struct
+{
+  my_structB_t b;
+} __attribute__((uncached))  my_structA_t;
+
+typedef volatile struct
+{
+  my_structA_t c;
+} my_type_t;
+
+my_type_t x;
+
+void foo (my_type_t *p)
+{
+  p->c.b.a = 10;
+}
+
+void bar (void)
+{
+  x.c.b.a = 10;
+}
+
+/* { dg-final { scan-assembler-times "st\.di" 1 } } */
+/* { dg-final { scan-assembler-times "st\.as\.di" 1 } } */