diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index a4344d67f44..71d326ff98c 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -222,6 +222,7 @@ libgcov-merge-tool.o-warn = -Wno-error
 gimple-match.o-warn = -Wno-unused
 generic-match.o-warn = -Wno-unused
 dfp.o-warn = -Wno-strict-aliasing
+insn-emit.o-warn = -Wno-unreachable-code-return
 
 # All warnings have to be shut off in stage1 if the compiler used then
 # isn't gcc; configure determines that.  WARN_CFLAGS will be either
diff --git a/gcc/common.opt b/gcc/common.opt
index 755e1a233b7..486ea36c8e5 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -806,8 +806,12 @@ Common Var(warn_maybe_uninitialized) Warning EnabledBy(Wuninitialized)
 Warn about maybe uninitialized automatic variables.
 
 Wunreachable-code
-Common Ignore Warning
-Does nothing. Preserved for backward compatibility.
+Common Var(warn_unreachable_code) Warning
+Catch all for -Wunreachable-code-*.
+
+Wunreachable-code-return
+Common Var(warn_unreachable_code_return) Warning EnabledBy(Wunreachable-code || Wextra)
+Warn about unreachable statements after a return.
 
 Wunused
 Common Var(warn_unused) Init(0) Warning
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
index c1691c3e073..55e03abba43 100644
--- a/gcc/cp/cp-gimplify.c
+++ b/gcc/cp/cp-gimplify.c
@@ -167,9 +167,11 @@ genericize_if_stmt (tree *stmt_p)
      the then_ block regardless of whether else_ has side-effects or not.  */
   if (IF_STMT_CONSTEVAL_P (stmt))
     stmt = else_;
-  else if (integer_nonzerop (cond) && !TREE_SIDE_EFFECTS (else_))
+  else if (!warn_unreachable_code_return
+	   && integer_nonzerop (cond) && !TREE_SIDE_EFFECTS (else_))
     stmt = then_;
-  else if (integer_zerop (cond) && !TREE_SIDE_EFFECTS (then_))
+  else if (!warn_unreachable_code_return
+	   && integer_zerop (cond) && !TREE_SIDE_EFFECTS (then_))
     stmt = else_;
   else
     stmt = build3 (COND_EXPR, void_type_node, cond, then_, else_);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 56f80775ca0..cb81a794e5b 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -17573,7 +17573,14 @@ finish_function (bool inline_p)
     {
       /* Make it so that `main' always returns 0 by default.  */
       if (DECL_MAIN_P (current_function_decl))
-	finish_return_stmt (integer_zero_node);
+	{
+	  /* Hack.  We don't want the middle-end to warn that this return
+	     is unreachable, so we mark its location as special.  */
+	  auto saved_il = input_location;
+	  input_location = BUILTINS_LOCATION;
+	  finish_return_stmt (integer_zero_node);
+	  input_location = saved_il;
+	}
 
       if (use_eh_spec_block (current_function_decl))
 	finish_eh_spec_block (TYPE_RAISES_EXCEPTIONS
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 3bddfbaae6a..7d7f2e7fddc 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -400,6 +400,7 @@ Objective-C and Objective-C++ Dialects}.
 -Wsystem-headers  -Wtautological-compare  -Wtrampolines  -Wtrigraphs @gol
 -Wtsan -Wtype-limits  -Wundef @gol
 -Wuninitialized  -Wunknown-pragmas @gol
+-Wunreachable-code -Wunreachable-code-return @gol
 -Wunsuffixed-float-constants  -Wunused @gol
 -Wunused-but-set-parameter  -Wunused-but-set-variable @gol
 -Wunused-const-variable  -Wunused-const-variable=@var{n} @gol
@@ -5721,6 +5722,7 @@ name is still supported, but the newer name is more descriptive.)
 -Wredundant-move @r{(only for C++)}  @gol
 -Wtype-limits  @gol
 -Wuninitialized  @gol
+-Wunreachable-code-return @gol
 -Wshift-negative-value @r{(in C++03 and in C99 and newer)}  @gol
 -Wunused-parameter @r{(only with} @option{-Wunused} @r{or} @option{-Wall}@r{)} @gol
 -Wunused-but-set-parameter @r{(only with} @option{-Wunused} @r{or} @option{-Wall}@r{)}}
@@ -6865,6 +6867,17 @@ This warning is enabled by default for C and C++ programs.
 Warn when @code{__sync_fetch_and_nand} and @code{__sync_nand_and_fetch}
 built-in functions are used.  These functions changed semantics in GCC 4.4.
 
+@item -Wunreachable-code
+@opindex Wunreachable-code
+@opindex Wno-unreachable-code
+Warn about unreachable code.  Enables @option{-Wunreachable-code-return}.
+
+@item -Wunreachable-code-return
+@opindex Wunreachable-code-return
+@opindex Wno-unreachable-code-return
+Warn about unreachable code after a return stmt.  Enabled by
+@option{-Wunreachable-code} and @option{-Wextra}.
+
 @item -Wunused-but-set-parameter
 @opindex Wunused-but-set-parameter
 @opindex Wno-unused-but-set-parameter
diff --git a/gcc/gimple-low.c b/gcc/gimple-low.c
index 7e39c22df44..f0eb82f72f6 100644
--- a/gcc/gimple-low.c
+++ b/gcc/gimple-low.c
@@ -33,6 +33,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "predict.h"
 #include "gimple-predict.h"
 #include "gimple-fold.h"
+#include "diagnostic.h"
 
 /* The differences between High GIMPLE and Low GIMPLE are the
    following:
@@ -56,6 +57,22 @@ struct return_statements_t
 };
 typedef struct return_statements_t return_statements_t;
 
+/* Helper tracking the reason a previous stmt cannot fallthru.  */
+struct cft_reason
+{
+  enum reason { CAN_FALLTHRU = false, UNKNOWN = true, RETURN };
+  cft_reason () : m_reason (CAN_FALLTHRU) {}
+  cft_reason (bool b) : m_reason (b ? UNKNOWN : CAN_FALLTHRU) {}
+  cft_reason (reason r) : m_reason (r) {}
+  operator bool () const { return m_reason != CAN_FALLTHRU; }
+  cft_reason& operator|= (const cft_reason& other)
+    {
+      if (other.m_reason != m_reason && (bool)other)
+	m_reason = UNKNOWN;
+      return *this;
+    }
+  reason m_reason;
+};
 
 struct lower_data
 {
@@ -67,7 +84,7 @@ struct lower_data
   vec<return_statements_t> return_statements;
 
   /* True if the current statement cannot fall through.  */
-  bool cannot_fallthru;
+  cft_reason cannot_fallthru;
 };
 
 static void lower_stmt (gimple_stmt_iterator *, struct lower_data *);
@@ -84,7 +101,7 @@ static void lower_builtin_posix_memalign (gimple_stmt_iterator *);
 static unsigned int
 lower_function_body (void)
 {
-  struct lower_data data;
+  struct lower_data data = {};
   gimple_seq body = gimple_body (current_function_decl);
   gimple_seq lowered_body;
   gimple_stmt_iterator i;
@@ -96,7 +113,6 @@ lower_function_body (void)
   gcc_assert (gimple_seq_first (body) == gimple_seq_last (body)
 	      && gimple_code (gimple_seq_first_stmt (body)) == GIMPLE_BIND);
 
-  memset (&data, 0, sizeof (data));
   data.block = DECL_INITIAL (current_function_decl);
   BLOCK_SUBBLOCKS (data.block) = NULL_TREE;
   BLOCK_CHAIN (data.block) = NULL_TREE;
@@ -249,6 +265,12 @@ lower_stmt (gimple_stmt_iterator *gsi, struct lower_data *data)
 
   gimple_set_block (stmt, data->block);
 
+  if (data->cannot_fallthru.m_reason == cft_reason::RETURN
+      && gimple_code (stmt) != GIMPLE_LABEL
+      && LOCATION_LOCUS (gimple_location (stmt)) > BUILTINS_LOCATION)
+    warning_at (gimple_location (stmt), OPT_Wunreachable_code_return,
+		"statement after return is not reachable");
+
   switch (gimple_code (stmt))
     {
     case GIMPLE_BIND:
@@ -272,7 +294,7 @@ lower_stmt (gimple_stmt_iterator *gsi, struct lower_data *data)
       else
 	{
 	  lower_gimple_return (gsi, data);
-	  data->cannot_fallthru = true;
+	  data->cannot_fallthru = cft_reason::RETURN;
 	}
       return;
 
diff --git a/gcc/testsuite/c-c++-common/Wunreachable-code-return-1.c b/gcc/testsuite/c-c++-common/Wunreachable-code-return-1.c
new file mode 100644
index 00000000000..17a35939182
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wunreachable-code-return-1.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-options "-Wunreachable-code-return" } */
+
+void baz();
+void foo ()
+{
+  return;
+  baz (); /* { dg-warning "statement after return is not reachable" } */
+}
