diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c
index e6158f243c00676dbfd3c1a578966f1a0b6b4b42..08c425007a1b1366a85fd16dde33550b3937a40b 100644
--- a/gcc/emit-rtl.c
+++ b/gcc/emit-rtl.c
@@ -1261,7 +1261,13 @@ rtx
 gen_rtx_REG_offset (rtx reg, machine_mode mode, unsigned int regno,
 		    poly_int64 offset)
 {
-  rtx new_rtx = gen_rtx_REG (mode, regno);
+  /* Use gen_raw_REG rather than gen_rtx_REG, because otherwise we'd
+     overwrite REG_ATTRS (and in the callers often ORIGINAL_REGNO too)
+     of the shared REG rtxes like stack_pointer_rtx etc.  This should
+     happen only for SUBREGs from DEBUG_INSNs, RA should ensure
+     multi-word registers don't overlap the special registers like
+     stack pointer.  */
+  rtx new_rtx = gen_raw_REG (mode, regno);
 
   update_reg_offset (new_rtx, reg, offset);
   return new_rtx;
diff --git a/gcc/testsuite/gcc.dg/pr103808.c b/gcc/testsuite/gcc.dg/pr103808.c
new file mode 100644
index 0000000000000000000000000000000000000000..51fc460a900873ed5a59e80668f04f0b4ae20451
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr103808.c
@@ -0,0 +1,23 @@
+/* PR debug/103808 */
+/* { dg-do compile { target int128 } } */
+/* { dg-options "-fcompare-debug -O2 -ftrapv" } */
+
+void
+foo (__int128 x, int y)
+{
+  for (;;)
+    {
+      __int128 a, b;
+
+      x |= !!y;
+      a = x + 1;
+      b = y ? ++y : ++x;
+      y = a < b;
+      asm ("" : "+r" (y));
+      if (x >> 2)
+        y *= 2;
+
+      if (y == b)
+        __builtin_unreachable ();
+    }
+}