diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c
index a1133c7..385ee1a 100644
--- a/gcc/c-family/c-format.c
+++ b/gcc/c-family/c-format.c
@@ -60,6 +60,7 @@ struct function_format_info
 /* Initialized in init_dynamic_diag_info.  */
 static GTY(()) tree local_tree_type_node;
 static GTY(()) tree local_gimple_ptr_node;
+static GTY(()) tree local_cgraph_node_ptr_node;
 static GTY(()) tree locus;
 
 static bool decode_format_attr (tree, function_format_info *, int);
@@ -803,6 +804,9 @@ static const format_char_info gcc_dump_printf_char_table[] =
   /* E and G require a "gimple *" argument at runtime.  */
   { "EG",   1, STD_C89, { T89_G,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "\"",   NULL },
 
+  /* C requires a "cgraph_node *" argument at runtime.  */
+  { "C",   1, STD_C89, { T_CGRAPH_NODE,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "\"",   NULL },
+
   /* T requires a "tree" at runtime.  */
   { "T",   1, STD_C89, { T89_T,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "\"",   NULL },
 
@@ -3879,6 +3883,33 @@ init_dynamic_gfc_info (void)
     }
 }
 
+/* Lookup the type named NAME and return a pointer-to-NAME type if found.
+   Otherwise, return void_type_node if NAME has not been used yet, or NULL_TREE if
+   NAME is not a type (issuing an error).  */
+
+static tree
+get_pointer_to_named_type (const char *name)
+{
+  tree result;
+  if ((result = maybe_get_identifier (name)))
+    {
+      result = identifier_global_value (result);
+      if (result)
+	{
+	  if (TREE_CODE (result) != TYPE_DECL)
+	    {
+	      error ("%qs is not defined as a type", name);
+	      result = NULL_TREE;
+	    }
+	  else
+	    result = TREE_TYPE (result);
+	}
+    }
+  else
+    result = void_type_node;
+  return result;
+}
+
 /* Determine the types of "tree" and "location_t" in the code being
    compiled for use in GCC's diagnostic custom format attributes.  You
    must have set dynamic_format_types before calling this function.  */
@@ -3932,25 +3963,12 @@ init_dynamic_diag_info (void)
   /* Similar to the above but for gimple*.  */
   if (!local_gimple_ptr_node
       || local_gimple_ptr_node == void_type_node)
-    {
-      if ((local_gimple_ptr_node = maybe_get_identifier ("gimple")))
-	{
-	  local_gimple_ptr_node
-	    = identifier_global_value (local_gimple_ptr_node);
-	  if (local_gimple_ptr_node)
-	    {
-	      if (TREE_CODE (local_gimple_ptr_node) != TYPE_DECL)
-		{
-		  error ("%<gimple%> is not defined as a type");
-		  local_gimple_ptr_node = 0;
-		}
-	      else
-		local_gimple_ptr_node = TREE_TYPE (local_gimple_ptr_node);
-	    }
-	}
-      else
-	local_gimple_ptr_node = void_type_node;
-    }
+    local_gimple_ptr_node = get_pointer_to_named_type ("gimple");
+
+  /* Similar to the above but for cgraph_node*.  */
+  if (!local_cgraph_node_ptr_node
+      || local_cgraph_node_ptr_node == void_type_node)
+    local_cgraph_node_ptr_node = get_pointer_to_named_type ("cgraph_node");
 
   static tree hwi;
 
diff --git a/gcc/c-family/c-format.h b/gcc/c-family/c-format.h
index d984d10..eabb4f0 100644
--- a/gcc/c-family/c-format.h
+++ b/gcc/c-family/c-format.h
@@ -299,6 +299,7 @@ struct format_kind_info
 #define T99_UC	{ STD_C99, NULL, T_UC }
 #define T_V	&void_type_node
 #define T89_G   { STD_C89, NULL, &local_gimple_ptr_node }
+#define T_CGRAPH_NODE   { STD_C89, NULL, &local_cgraph_node_ptr_node }
 #define T89_T   { STD_C89, NULL, &local_tree_type_node }
 #define T89_V	{ STD_C89, NULL, T_V }
 #define T_W	&wchar_type_node
diff --git a/gcc/dump-context.h b/gcc/dump-context.h
index 3a45f23..ace139c 100644
--- a/gcc/dump-context.h
+++ b/gcc/dump-context.h
@@ -251,6 +251,14 @@ verify_item (const location &loc,
 		 (EXPECTED_LOCATION), (EXPECTED_TEXT));	    \
   SELFTEST_END_STMT
 
+/* Verify that ITEM is a symtab node, with the expected values.  */
+
+#define ASSERT_IS_SYMTAB_NODE(ITEM, EXPECTED_LOCATION, EXPECTED_TEXT) \
+  SELFTEST_BEGIN_STMT						    \
+    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_SYMTAB_NODE, \
+		 (EXPECTED_LOCATION), (EXPECTED_TEXT));	    \
+  SELFTEST_END_STMT
+
 } // namespace selftest
 
 #endif /* CHECKING_P */
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index 0b140ff..09c2490 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -748,6 +748,18 @@ dump_context::dump_generic_expr_loc (dump_flags_t dump_kind,
   dump_generic_expr (dump_kind, extra_dump_flags, t);
 }
 
+/* Make an item for the given dump call.  */
+
+static optinfo_item *
+make_item_for_dump_symtab_node (symtab_node *node)
+{
+  location_t loc = DECL_SOURCE_LOCATION (node->decl);
+  optinfo_item *item
+    = new optinfo_item (OPTINFO_ITEM_KIND_SYMTAB_NODE, loc,
+			xstrdup (node->dump_name ()));
+  return item;
+}
+
 /* dump_pretty_printer's ctor.  */
 
 dump_pretty_printer::dump_pretty_printer (dump_context *context,
@@ -881,6 +893,8 @@ dump_pretty_printer::format_decoder_cb (pretty_printer *pp, text_info *text,
    Supported format codes (in addition to the standard pretty_printer ones)
    are:
 
+   %C: cgraph_node *:
+       Equivalent to: dump_symtab_node (MSG_*, node)
    %E: gimple *:
        Equivalent to: dump_gimple_expr (MSG_*, TDF_SLIM, stmt, 0)
    %G: gimple *:
@@ -888,7 +902,9 @@ dump_pretty_printer::format_decoder_cb (pretty_printer *pp, text_info *text,
    %T: tree:
        Equivalent to: dump_generic_expr (MSG_*, arg, TDF_SLIM).
 
-   FIXME: add symtab_node?
+   TODO: add a format code that can handle (symtab_node*) *and* both
+   subclasses (presumably means teaching -Wformat about non-virtual
+   subclasses).
 
    These format codes build optinfo_item instances, thus capturing metadata
    about the arguments being dumped, as well as the textual output.  */
@@ -901,6 +917,16 @@ dump_pretty_printer::decode_format (text_info *text, const char *spec,
      for later use (to capture metadata, rather than plain text).  */
   switch (*spec)
     {
+    case 'C':
+      {
+	cgraph_node *node = va_arg (*text->args_ptr, cgraph_node *);
+
+	/* Make an item for the node, and stash it.  */
+	optinfo_item *item = make_item_for_dump_symtab_node (node);
+	stash_item (buffer_ptr, item);
+	return true;
+      }
+
     case 'E':
       {
 	gimple *stmt = va_arg (*text->args_ptr, gimple *);
@@ -1023,18 +1049,6 @@ dump_context::dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
     delete item;
 }
 
-/* Make an item for the given dump call.  */
-
-static optinfo_item *
-make_item_for_dump_symtab_node (symtab_node *node)
-{
-  location_t loc = DECL_SOURCE_LOCATION (node->decl);
-  optinfo_item *item
-    = new optinfo_item (OPTINFO_ITEM_KIND_SYMTAB_NODE, loc,
-			xstrdup (node->dump_name ()));
-  return item;
-}
-
 /* Output the name of NODE on appropriate dump streams.  */
 
 void
@@ -2067,18 +2081,26 @@ test_capture_of_dump_calls (const line_table_case &case_)
   linemap_add (line_table, LC_ENTER, false, "test.txt", 0);
   linemap_line_start (line_table, 5, 100);
   linemap_add (line_table, LC_LEAVE, false, NULL, 0);
-  location_t where = linemap_position_for_column (line_table, 10);
-  if (where > LINE_MAP_MAX_LOCATION_WITH_COLS)
+  location_t decl_loc = linemap_position_for_column (line_table, 8);
+  location_t stmt_loc = linemap_position_for_column (line_table, 10);
+  if (stmt_loc > LINE_MAP_MAX_LOCATION_WITH_COLS)
     return;
 
-  dump_location_t loc = dump_location_t::from_location_t (where);
+  dump_location_t loc = dump_location_t::from_location_t (stmt_loc);
 
   gimple *stmt = gimple_build_return (NULL);
-  gimple_set_location (stmt, where);
+  gimple_set_location (stmt, stmt_loc);
 
-  tree test_decl = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+  tree test_decl = build_decl (decl_loc, FUNCTION_DECL,
 			       get_identifier ("test_decl"),
-			       integer_type_node);
+			       build_function_type_list (void_type_node,
+							 NULL_TREE));
+
+  symbol_table_test tmp_symtab;
+
+  cgraph_node *node = cgraph_node::get_create (test_decl);
+  gcc_assert (node);
+
   /* Run all tests twice, with and then without optinfo enabled, to ensure
      that immediate destinations vs optinfo-based destinations both
      work, independently of each other, with no leaks.  */
@@ -2135,7 +2157,7 @@ test_capture_of_dump_calls (const line_table_case &case_)
 	    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
 	    ASSERT_EQ (info->num_items (), 2);
 	    ASSERT_IS_TEXT (info->get_item (0), "gimple: ");
-	    ASSERT_IS_GIMPLE (info->get_item (1), where, "return;");
+	    ASSERT_IS_GIMPLE (info->get_item (1), stmt_loc, "return;");
 	  }
       }
 
@@ -2153,7 +2175,25 @@ test_capture_of_dump_calls (const line_table_case &case_)
 	    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
 	    ASSERT_EQ (info->num_items (), 2);
 	    ASSERT_IS_TEXT (info->get_item (0), "gimple: ");
-	    ASSERT_IS_GIMPLE (info->get_item (1), where, "return;\n");
+	    ASSERT_IS_GIMPLE (info->get_item (1), stmt_loc, "return;\n");
+	  }
+      }
+
+      /* Test of dump_printf with %C.  */
+      {
+	temp_dump_context tmp (with_optinfo, true,
+			       MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING);
+	dump_printf (MSG_NOTE, "node: %C", node);
+
+	ASSERT_DUMPED_TEXT_EQ (tmp, "node: test_decl/0");
+	if (with_optinfo)
+	  {
+	    optinfo *info = tmp.get_pending_optinfo ();
+	    ASSERT_TRUE (info != NULL);
+	    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
+	    ASSERT_EQ (info->num_items (), 2);
+	    ASSERT_IS_TEXT (info->get_item (0), "node: ");
+	    ASSERT_IS_SYMTAB_NODE (info->get_item (1), decl_loc, "test_decl/0");
 	  }
       }
 
@@ -2184,8 +2224,8 @@ test_capture_of_dump_calls (const line_table_case &case_)
 	    ASSERT_IS_TEXT (info->get_item (2), " and ");
 	    ASSERT_IS_TREE (info->get_item (3), UNKNOWN_LOCATION, "test_decl");
 	    ASSERT_IS_TEXT (info->get_item (4), " 42 consecutive ");
-	    ASSERT_IS_GIMPLE (info->get_item (5), where, "return;");
-	    ASSERT_IS_GIMPLE (info->get_item (6), where, "return;");
+	    ASSERT_IS_GIMPLE (info->get_item (5), stmt_loc, "return;");
+	    ASSERT_IS_GIMPLE (info->get_item (6), stmt_loc, "return;");
 	    ASSERT_IS_TEXT (info->get_item (7), " after\n");
 	  }
       }
@@ -2202,7 +2242,7 @@ test_capture_of_dump_calls (const line_table_case &case_)
 	  {
 	    optinfo *info = tmp.get_pending_optinfo ();
 	    ASSERT_TRUE (info != NULL);
-	    ASSERT_EQ (info->get_location_t (), where);
+	    ASSERT_EQ (info->get_location_t (), stmt_loc);
 	    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
 	    ASSERT_EQ (info->num_items (), 2);
 	    ASSERT_IS_TEXT (info->get_item (0), "test of tree: ");
@@ -2221,7 +2261,7 @@ test_capture_of_dump_calls (const line_table_case &case_)
 	  {
 	    optinfo *info = tmp.get_pending_optinfo ();
 	    ASSERT_TRUE (info != NULL);
-	    ASSERT_EQ (info->get_location_t (), where);
+	    ASSERT_EQ (info->get_location_t (), stmt_loc);
 	    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
 	    ASSERT_EQ (info->num_items (), 1);
 	    ASSERT_IS_TREE (info->get_item (0), UNKNOWN_LOCATION, "1");
@@ -2242,7 +2282,7 @@ test_capture_of_dump_calls (const line_table_case &case_)
 	      optinfo *info = tmp.get_pending_optinfo ();
 	      ASSERT_TRUE (info != NULL);
 	      ASSERT_EQ (info->num_items (), 1);
-	      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;\n");
+	      ASSERT_IS_GIMPLE (info->get_item (0), stmt_loc, "return;\n");
 	    }
 	}
 
@@ -2258,7 +2298,7 @@ test_capture_of_dump_calls (const line_table_case &case_)
 	      optinfo *info = tmp.get_pending_optinfo ();
 	      ASSERT_TRUE (info != NULL);
 	      ASSERT_EQ (info->num_items (), 1);
-	      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;\n");
+	      ASSERT_IS_GIMPLE (info->get_item (0), stmt_loc, "return;\n");
 	    }
 	}
 
@@ -2274,7 +2314,7 @@ test_capture_of_dump_calls (const line_table_case &case_)
 	      optinfo *info = tmp.get_pending_optinfo ();
 	      ASSERT_TRUE (info != NULL);
 	      ASSERT_EQ (info->num_items (), 1);
-	      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;");
+	      ASSERT_IS_GIMPLE (info->get_item (0), stmt_loc, "return;");
 	    }
 	}
 
@@ -2290,11 +2330,28 @@ test_capture_of_dump_calls (const line_table_case &case_)
 	      optinfo *info = tmp.get_pending_optinfo ();
 	      ASSERT_TRUE (info != NULL);
 	      ASSERT_EQ (info->num_items (), 1);
-	      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;");
+	      ASSERT_IS_GIMPLE (info->get_item (0), stmt_loc, "return;");
 	    }
 	}
       }
 
+      /* symtab_node.  */
+      {
+	temp_dump_context tmp (with_optinfo, true,
+			       MSG_ALL_KINDS | MSG_PRIORITY_USER_FACING);
+	dump_symtab_node (MSG_NOTE, node);
+
+	ASSERT_DUMPED_TEXT_EQ (tmp, "test_decl/0");
+	if (with_optinfo)
+	  {
+	    optinfo *info = tmp.get_pending_optinfo ();
+	    ASSERT_TRUE (info != NULL);
+	    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
+	    ASSERT_EQ (info->num_items (), 1);
+	    ASSERT_IS_SYMTAB_NODE (info->get_item (0), decl_loc, "test_decl/0");
+	  }
+      }
+
       /* poly_int.  */
       {
 	temp_dump_context tmp (with_optinfo, true,
diff --git a/gcc/testsuite/gcc.dg/format/gcc_diag-10.c b/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
index 2f6a002..97a1993 100644
--- a/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
+++ b/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
@@ -20,7 +20,7 @@ typedef union tree_node *tree;
 typedef struct gimple gimple;
 
 /* Likewise for gimple.  */
-typedef struct gimple gimple;
+typedef struct cgraph_node cgraph_node;
 
 #define FORMAT(kind) __attribute__ ((format (__gcc_## kind ##__, 1, 2)))
 
@@ -162,7 +162,7 @@ void test_cxxdiag (tree t, gimple *gc)
   cxxdiag ("%<%X%>", t);
 }
 
-void test_dump (tree t, gimple *stmt)
+void test_dump (tree t, gimple *stmt, cgraph_node *node)
 {
   dump ("%<");   /* { dg-warning "unterminated quoting directive" } */
   dump ("%>");   /* { dg-warning "unmatched quoting directive " } */
@@ -182,4 +182,5 @@ void test_dump (tree t, gimple *stmt)
   dump ("%E", stmt);
   dump ("%T", t);
   dump ("%G", stmt);
+  dump ("%C", node);
 }
