diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index b74c18b03ad6766b72e4fdefb936b1cf9a91f4f5..0e37d4043d0c0ad4c0865afc2865d6a498e6feeb 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6989,6 +6989,7 @@ extern void copy_linkage			(tree, tree);
 extern tree get_guard				(tree);
 extern tree get_guard_cond			(tree, bool);
 extern tree set_guard				(tree);
+extern bool var_needs_tls_wrapper		(tree);
 extern tree maybe_get_tls_wrapper_call		(tree);
 extern void mark_needed				(tree);
 extern bool decl_needed_p			(tree);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index f5785339c5a9927895d738aa3de875576d7d801b..20b980f68c83633a7f523920ed827cb2ebc5cc7e 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -8706,6 +8706,18 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
 	  if (!decl_maybe_constant_destruction (decl, type))
 	    TREE_READONLY (decl) = 0;
 	}
+      else if (VAR_P (decl)
+	       && CP_DECL_THREAD_LOCAL_P (decl)
+	       && (!DECL_EXTERNAL (decl) || flag_extern_tls_init)
+	       && (was_readonly || TREE_READONLY (decl))
+	       && var_needs_tls_wrapper (decl))
+	{
+	  /* TLS variables need dynamic initialization by the TLS wrapper
+	     function, we don't want to hoist accesses to it before the
+	     wrapper.  */
+	  was_readonly = 0;
+	  TREE_READONLY (decl) = 0;
+	}
 
       make_rtl_for_nonlocal_decl (decl, init, asmspec);
 
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 387e24542cd727aa4cd2abc7629325bad5c77398..0002d21beefd305386739008d9fe779394629eb3 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -3623,7 +3623,7 @@ var_defined_without_dynamic_init (tree var)
 /* Returns true iff VAR is a variable that needs uses to be
    wrapped for possible dynamic initialization.  */
 
-static bool
+bool
 var_needs_tls_wrapper (tree var)
 {
   return (!error_operand_p (var)
diff --git a/gcc/testsuite/g++.dg/tls/thread_local13-aux.cc b/gcc/testsuite/g++.dg/tls/thread_local13-aux.cc
new file mode 100644
index 0000000000000000000000000000000000000000..691f308cff1ad94841519def4acd535ca9628ebe
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tls/thread_local13-aux.cc
@@ -0,0 +1,35 @@
+// PR c++/109164
+
+struct S { virtual void foo (); int s; };
+extern bool baz ();
+
+void
+S::foo ()
+{
+  if (s != 42)
+    __builtin_abort ();
+}
+
+S s;
+
+S &
+qux ()
+{
+  s.s = 42;
+  return s;
+}
+
+thread_local S &t = qux ();
+
+bool
+bar ()
+{
+  return false;
+}
+
+int
+main ()
+{
+  if (baz ())
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/g++.dg/tls/thread_local13.C b/gcc/testsuite/g++.dg/tls/thread_local13.C
new file mode 100644
index 0000000000000000000000000000000000000000..b424b9f8acfebcfd5200965dbe69478126c28563
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tls/thread_local13.C
@@ -0,0 +1,21 @@
+// PR c++/109164
+// { dg-do run { target c++11 } }
+// { dg-options "-O2" }
+// { dg-add-options tls }
+// { dg-require-effective-target tls_runtime }
+// { dg-additional-sources "thread_local13-aux.cc" }
+
+struct S { virtual void foo (); int s; };
+extern thread_local S &t;
+bool bar ();
+
+bool
+baz ()
+{
+  while (1)
+    {
+      t.foo ();
+      if (!bar ())
+        return false;
+    }
+}
diff --git a/gcc/testsuite/g++.dg/tls/thread_local14-aux.cc b/gcc/testsuite/g++.dg/tls/thread_local14-aux.cc
new file mode 100644
index 0000000000000000000000000000000000000000..e8f2a243fc14382940d236d9cc1250d02b0a6504
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tls/thread_local14-aux.cc
@@ -0,0 +1,26 @@
+// PR c++/109164
+
+extern bool baz ();
+
+int
+qux ()
+{
+  return 42;
+}
+
+extern thread_local const int t = qux ();
+
+bool
+bar (int x)
+{
+  if (x != 42)
+    __builtin_abort ();
+  return false;
+}
+
+int
+main ()
+{
+  if (baz ())
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/g++.dg/tls/thread_local14.C b/gcc/testsuite/g++.dg/tls/thread_local14.C
new file mode 100644
index 0000000000000000000000000000000000000000..a7402d0d6317991b51f167a0669e154d60edcd90
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tls/thread_local14.C
@@ -0,0 +1,19 @@
+// PR c++/109164
+// { dg-do run { target c++11 } }
+// { dg-options "-O2" }
+// { dg-add-options tls }
+// { dg-require-effective-target tls_runtime }
+// { dg-additional-sources "thread_local14-aux.cc" }
+
+extern thread_local const int t;
+bool bar (int);
+
+bool
+baz ()
+{
+  while (1)
+    {
+      if (!bar (t))
+        return false;
+    }
+}