diff --git a/gcc/builtins.cc b/gcc/builtins.cc
index 4c6c2939053..669936ef7b6 100644
--- a/gcc/builtins.cc
+++ b/gcc/builtins.cc
@@ -5569,7 +5569,7 @@ expand_builtin_fork_or_exec (tree fn, tree exp, rtx target, int ignore)
   tree call;
 
   /* If we are not profiling, just call the function.  */
-  if (!profile_arc_flag)
+  if (!profile_arc_flag && !profile_condition_flag)
     return NULL_RTX;
 
   /* Otherwise call the wrapper.  This should be equivalent for the rest of
diff --git a/gcc/common.opt b/gcc/common.opt
index 8b6513de47c..2afe4384d9d 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -861,6 +861,10 @@ Wcoverage-invalid-line-number
 Common Var(warn_coverage_invalid_linenum) Init(1) Warning
 Warn in case a function ends earlier than it begins due to an invalid linenum macros.
 
+Wcoverage-too-many-conditions
+Common Var(warn_too_many_conditions) Init(1) Warning
+Warn when a conditional has too many terms and coverage gives up.
+
 Wmissing-profile
 Common Var(warn_missing_profile) Init(1) Warning
 Warn in case profiles in -fprofile-use do not exist.
@@ -2290,6 +2294,10 @@ fprofile-arcs
 Common Var(profile_arc_flag)
 Insert arc-based program profiling code.
 
+fprofile-conditions
+Common Var(profile_condition_flag)
+Insert condition coverage profiling code.
+
 fprofile-dir=
 Common Joined RejectNegative Var(profile_data_prefix)
 Set the top-level directory for storing the profile data.
diff --git a/gcc/doc/gcov.texi b/gcc/doc/gcov.texi
index fc39da0f02d..a85b7f09769 100644
--- a/gcc/doc/gcov.texi
+++ b/gcc/doc/gcov.texi
@@ -122,6 +122,7 @@ gcov [@option{-v}|@option{--version}] [@option{-h}|@option{--help}]
      [@option{-a}|@option{--all-blocks}]
      [@option{-b}|@option{--branch-probabilities}]
      [@option{-c}|@option{--branch-counts}]
+     [@option{-g}|@option{--conditions}]
      [@option{-d}|@option{--display-progress}]
      [@option{-f}|@option{--function-summaries}]
      [@option{-j}|@option{--json-format}]
@@ -167,6 +168,13 @@ be shown, unless the @option{-u} option is given.
 Write branch frequencies as the number of branches taken, rather than
 the percentage of branches taken.
 
+@item -g
+@itemx --conditions
+Write condition coverage to the output file, and write condition summary info
+to the standard output.  This option allows you to see if the conditions in
+your program at least once had an independent effect on the outcome of the
+boolean expression (modified condition/decision coverage).
+
 @item -d
 @itemx --display-progress
 Display the progress on the standard output.
@@ -291,6 +299,7 @@ Each @var{line} has the following form:
 @{
   "branches": ["$branch"],
   "count": 2,
+  "conditions": ["$condition"],
   "line_number": 15,
   "unexecuted_block": false,
   "function_name": "foo",
@@ -339,6 +348,33 @@ Fields of the @var{branch} element have following semantics:
 @var{throw}: true when the branch is an exceptional branch
 @end itemize
 
+Each @var{condition} element have the following semantics:
+
+@smallexample
+@{
+  "count": 4,
+  "covered": 2,
+  "not_covered_false": [],
+  "not_covered_true": [0, 1],
+@}
+@end smallexample
+
+Fields of the @var{condition} element have following semantics:
+
+@itemize @bullet
+@item
+@var{count}: number of conditions in this expression
+
+@item
+@var{covered}: number of covered conditions in this expression
+
+@item
+@var{not_covered_true}: terms, by index, not seen as true in this expression
+
+@item
+@var{not_covered_false}: terms, by index, not seen as false in this expression
+@end itemize
+
 @item -H
 @itemx --human-readable
 Write counts in human readable format (like 24.6k).
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index e3f2e82cde5..0e24e43ca42 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -594,6 +594,7 @@ Objective-C and Objective-C++ Dialects}.
 @item Program Instrumentation Options
 @xref{Instrumentation Options,,Program Instrumentation Options}.
 @gccoptlist{-p  -pg  -fprofile-arcs  --coverage  -ftest-coverage @gol
+-fprofile-conditions @gol
 -fprofile-abs-path @gol
 -fprofile-dir=@var{path}  -fprofile-generate  -fprofile-generate=@var{path} @gol
 -fprofile-info-section  -fprofile-info-section=@var{name} @gol
@@ -6020,6 +6021,13 @@ error.  @option{-Wno-coverage-invalid-line-number} can be used to disable the
 warning or @option{-Wno-error=coverage-invalid-line-number} can be used to
 disable the error.
 
+@item -Wno-coverage-too-many-conditions
+@opindex Wno-coverage-too-many-conditions
+@opindex Wcoverage-too-many-conditions
+Warn in case a condition have too many terms and GCC gives up coverage.
+Coverage is given up when there are more terms in the conditional than there
+are bits in a @code{gcov_type_unsigned}.  This warning is enabled by default.
+
 @item -Wno-cpp @r{(C, Objective-C, C++, Objective-C++ and Fortran only)}
 @opindex Wno-cpp
 @opindex Wcpp
@@ -15271,6 +15279,13 @@ E.g. @code{gcc a.c b.c -o binary} would generate @file{binary-a.gcda} and
 
 @xref{Cross-profiling}.
 
+@item -fprofile-conditions
+@opindex fprofile-conditions
+Add code so that program conditions are instrumented.  During execution the
+program records what terms in a conditional contributes to a decision.  The
+data may be used to verify that all terms in a booleans are tested and have an
+effect on the outcome of a condition.
+
 @cindex @command{gcov}
 @item --coverage
 @opindex coverage
@@ -15331,6 +15346,10 @@ executed.  When an arc is the only exit or only entrance to a block, the
 instrumentation code can be added to the block; otherwise, a new basic
 block must be created to hold the instrumentation code.
 
+With @option{-fprofile-conditions}, for each conditional in your program GCC
+creates a bitset and records the boolean values that have an independent effect
+on the outcome of that expression.
+
 @need 2000
 @item -ftest-coverage
 @opindex ftest-coverage
diff --git a/gcc/gcc.cc b/gcc/gcc.cc
index a4d863ca457..5a25a47b7d0 100644
--- a/gcc/gcc.cc
+++ b/gcc/gcc.cc
@@ -1165,7 +1165,7 @@ proper position among the other output files.  */
 	%:include(libgomp.spec)%(link_gomp)}\
     %{fgnu-tm:%:include(libitm.spec)%(link_itm)}\
     %(mflib) " STACK_SPLIT_SPEC "\
-    %{fprofile-arcs|fprofile-generate*|coverage:-lgcov} " SANITIZER_SPEC " \
+    %{fprofile-arcs|fprofile-conditions|fprofile-generate*|coverage:-lgcov} " SANITIZER_SPEC " \
     %{!nostdlib:%{!r:%{!nodefaultlibs:%(link_ssp) %(link_gcc_c_sequence)}}}\
     %{!nostdlib:%{!r:%{!nostartfiles:%E}}} %{T*}  \n%(post_link) }}}}}}"
 #endif
diff --git a/gcc/gcov-counter.def b/gcc/gcov-counter.def
index 6d2182bd3db..96563a59a45 100644
--- a/gcc/gcov-counter.def
+++ b/gcc/gcov-counter.def
@@ -49,3 +49,6 @@ DEF_GCOV_COUNTER(GCOV_COUNTER_IOR, "ior", _ior)
 
 /* Time profile collecting first run of a function */
 DEF_GCOV_COUNTER(GCOV_TIME_PROFILER, "time_profiler", _time_profile)
+
+/* Conditions.  The counter is interpreted as a bit-set.  */
+DEF_GCOV_COUNTER(GCOV_COUNTER_CONDS, "conditions", _ior)
diff --git a/gcc/gcov-io.h b/gcc/gcov-io.h
index 99ce7dbccc8..c84f58b99ac 100644
--- a/gcc/gcov-io.h
+++ b/gcc/gcov-io.h
@@ -253,6 +253,9 @@ typedef uint64_t gcov_type_unsigned;
 #define GCOV_TAG_ARCS		 ((gcov_unsigned_t)0x01430000)
 #define GCOV_TAG_ARCS_LENGTH(NUM)  (1 + (NUM) * 2 * GCOV_WORD_SIZE)
 #define GCOV_TAG_ARCS_NUM(LENGTH)  (((LENGTH / GCOV_WORD_SIZE) - 1) / 2)
+#define GCOV_TAG_CONDS             ((gcov_unsigned_t)0x01470000)
+#define GCOV_TAG_CONDS_LENGTH(NUM) ((NUM) * 2 * GCOV_WORD_SIZE)
+#define GCOV_TAG_CONDS_NUM(LENGTH) (((LENGTH) / GCOV_WORD_SIZE) / 2)
 #define GCOV_TAG_LINES		 ((gcov_unsigned_t)0x01450000)
 #define GCOV_TAG_COUNTER_BASE 	 ((gcov_unsigned_t)0x01a10000)
 #define GCOV_TAG_COUNTER_LENGTH(NUM) ((NUM) * 2 * GCOV_WORD_SIZE)
diff --git a/gcc/gcov.cc b/gcc/gcov.cc
index 27be5ff0911..4db40c18ae7 100644
--- a/gcc/gcov.cc
+++ b/gcc/gcov.cc
@@ -79,6 +79,7 @@ using namespace std;
 class function_info;
 class block_info;
 class source_info;
+class condition_info;
 
 /* Describes an arc between two basic blocks.  */
 
@@ -132,6 +133,28 @@ public:
   vector<unsigned> lines;
 };
 
+class condition_info
+{
+public:
+  condition_info ();
+
+  int popcount () const;
+
+  gcov_type_unsigned truev;
+  gcov_type_unsigned falsev;
+
+  unsigned n_terms;
+};
+
+condition_info::condition_info (): truev (0), falsev (0), n_terms (0)
+{
+}
+
+int condition_info::popcount () const
+{
+    return __builtin_popcountll (truev) + __builtin_popcountll (falsev);
+}
+
 /* Describes a basic block. Contains lists of arcs to successor and
    predecessor blocks.  */
 
@@ -165,6 +188,8 @@ public:
   /* Block is a landing pad for longjmp or throw.  */
   unsigned is_nonlocal_return : 1;
 
+  condition_info conditions;
+
   vector<block_location_info> locations;
 
   struct
@@ -275,6 +300,8 @@ public:
   vector<block_info> blocks;
   unsigned blocks_executed;
 
+  vector<condition_info*> conditions;
+
   /* Raw arc coverage counts.  */
   vector<gcov_type> counts;
 
@@ -351,6 +378,9 @@ struct coverage_info
   int branches_executed;
   int branches_taken;
 
+  int conditions;
+  int conditions_covered;
+
   int calls;
   int calls_executed;
 
@@ -550,6 +580,10 @@ static int multiple_files = 0;
 
 static int flag_branches = 0;
 
+/* Output conditions (modified condition/decision coverage) */
+
+static int flag_conditions = 0;
+
 /* Show unconditional branches too.  */
 static int flag_unconditional = 0;
 
@@ -656,6 +690,7 @@ static int read_count_file (void);
 static void solve_flow_graph (function_info *);
 static void find_exception_blocks (function_info *);
 static void add_branch_counts (coverage_info *, const arc_info *);
+static void add_condition_counts (coverage_info *, const block_info *);
 static void add_line_counts (coverage_info *, function_info *);
 static void executed_summary (unsigned, unsigned);
 static void function_summary (const coverage_info *);
@@ -664,6 +699,7 @@ static const char *format_gcov (gcov_type, gcov_type, int);
 static void accumulate_line_counts (source_info *);
 static void output_gcov_file (const char *, source_info *);
 static int output_branch_count (FILE *, int, const arc_info *);
+static void output_conditions (FILE *, const block_info *);
 static void output_lines (FILE *, const source_info *);
 static string make_gcov_file_name (const char *, const char *);
 static char *mangle_name (const char *);
@@ -928,6 +964,7 @@ print_usage (int error_p)
   fnotice (file, "  -b, --branch-probabilities      Include branch probabilities in output\n");
   fnotice (file, "  -c, --branch-counts             Output counts of branches taken\n\
                                     rather than percentages\n");
+  fnotice (file, "  -g, --conditions                Include condition coverage in output (MC/DC)\n");
   fnotice (file, "  -d, --display-progress          Display progress information\n");
   fnotice (file, "  -D, --debug			    Display debugging dumps\n");
   fnotice (file, "  -f, --function-summaries        Output summaries for each function\n");
@@ -980,6 +1017,7 @@ static const struct option options[] =
   { "all-blocks",           no_argument,       NULL, 'a' },
   { "branch-probabilities", no_argument,       NULL, 'b' },
   { "branch-counts",        no_argument,       NULL, 'c' },
+  { "conditions",           no_argument,       NULL, 'g' },
   { "json-format",	    no_argument,       NULL, 'j' },
   { "human-readable",	    no_argument,       NULL, 'H' },
   { "no-output",            no_argument,       NULL, 'n' },
@@ -1008,7 +1046,7 @@ process_args (int argc, char **argv)
 {
   int opt;
 
-  const char *opts = "abcdDfhHijklmno:pqrs:tuvwx";
+  const char *opts = "abcdDfghHijklmno:pqrs:tuvwx";
   while ((opt = getopt_long (argc, argv, opts, options, NULL)) != -1)
     {
       switch (opt)
@@ -1025,6 +1063,9 @@ process_args (int argc, char **argv)
 	case 'f':
 	  flag_function_summary = 1;
 	  break;
+	case 'g':
+	  flag_conditions = 1;
+	  break;
 	case 'h':
 	  print_usage (false);
 	  /* print_usage will exit.  */
@@ -1132,6 +1173,45 @@ output_intermediate_json_line (json::array *object,
 	  }
       }
 
+  json::array *conditions = new json::array ();
+  lineo->set ("conditions", conditions);
+  if (flag_conditions)
+  {
+    vector<block_info *>::const_iterator it;
+    for (it = line->blocks.begin (); it != line->blocks.end (); it++)
+      {
+	const condition_info& info = (*it)->conditions;
+	if (info.n_terms == 0)
+	    continue;
+
+	const int count = 2 * info.n_terms;
+	const int covered = info.popcount ();
+
+	json::object *cond = new json::object ();
+	cond->set ("count",   new json::integer_number (count));
+	cond->set ("covered", new json::integer_number (covered));
+
+	json::array *mtrue = new json::array ();
+	json::array *mfalse = new json::array ();
+	cond->set ("not_covered_true",  mtrue);
+	cond->set ("not_covered_false", mfalse);
+
+	if (count != covered)
+	  {
+	    for (unsigned i = 0; i < info.n_terms; i++)
+	      {
+		gcov_type_unsigned index = 1;
+		index <<= i;
+		if (!(index & info.truev))
+		    mtrue->append (new json::integer_number (i));
+		if (!(index & info.falsev))
+		    mfalse->append (new json::integer_number (i));
+	      }
+	  }
+	conditions->append (cond);
+      }
+  }
+
   object->append (lineo);
 }
 
@@ -1956,6 +2036,28 @@ read_graph_file (void)
 		  }
 	    }
 	}
+      else if (fn && tag == GCOV_TAG_CONDS)
+	{
+	  unsigned num_dests = GCOV_TAG_CONDS_NUM (length);
+
+	  if (!fn->conditions.empty ())
+	    fnotice (stderr, "%s:already seen conditions for '%s'\n",
+		     bbg_file_name, fn->get_name ());
+	  else
+	    fn->conditions.resize (num_dests);
+
+	  for (unsigned i = 0; i < num_dests; ++i)
+	    {
+	      unsigned idx = gcov_read_unsigned ();
+
+	      if (idx >= fn->blocks.size ())
+		goto corrupt;
+
+	      condition_info *info = &fn->blocks[idx].conditions;
+	      info->n_terms = gcov_read_unsigned ();
+	      fn->conditions[i] = info;
+	    }
+	}
       else if (fn && tag == GCOV_TAG_LINES)
 	{
 	  unsigned blockno = gcov_read_unsigned ();
@@ -2086,11 +2188,26 @@ read_count_file (void)
 	      goto cleanup;
 	    }
 	}
-      else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_ARCS) && fn)
+      else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_CONDS) && fn)
 	{
+	  length = abs (read_length);
+	    if (length != GCOV_TAG_COUNTER_LENGTH (2 * fn->conditions.size ()))
+		goto mismatch;
+
+	    if (read_length > 0)
+	      {
+		for (ix = 0; ix != fn->conditions.size (); ix++)
+		{
+		    fn->conditions[ix]->truev  |= gcov_read_counter ();
+		    fn->conditions[ix]->falsev |= gcov_read_counter ();
+		}
+	      }
+	}
+      else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_ARCS) && fn)
+      {
 	  length = abs (read_length);
 	  if (length != GCOV_TAG_COUNTER_LENGTH (fn->counts.size ()))
-	    goto mismatch;
+	      goto mismatch;
 
 	  if (read_length > 0)
 	    for (ix = 0; ix != fn->counts.size (); ix++)
@@ -2430,6 +2547,13 @@ add_branch_counts (coverage_info *coverage, const arc_info *arc)
     }
 }
 
+static void
+add_condition_counts (coverage_info *coverage, const block_info *block)
+{
+  coverage->conditions += 2 * block->conditions.n_terms;
+  coverage->conditions_covered += block->conditions.popcount ();
+}
+
 /* Format COUNT, if flag_human_readable_numbers is set, return it human
    readable format.  */
 
@@ -2533,7 +2657,14 @@ file_summary (const coverage_info *coverage)
 		 coverage->calls);
       else
 	fnotice (stdout, "No calls\n");
+
     }
+
+  if (flag_conditions)
+      fnotice (stdout, "Conditions covered:%s of %d\n",
+	       format_gcov (coverage->conditions_covered,
+			    coverage->conditions, 2),
+	       coverage->conditions);
 }
 
 /* Canonicalize the filename NAME by canonicalizing directory
@@ -2760,6 +2891,12 @@ static void accumulate_line_info (line_info *line, source_info *src,
 	 it != line->branches.end (); it++)
       add_branch_counts (&src->coverage, *it);
 
+  if (add_coverage)
+    for (vector<block_info *>::iterator it = line->blocks.begin ();
+	 it != line->blocks.end (); it++)
+      add_condition_counts (&src->coverage, *it);
+
+
   if (!line->blocks.empty ())
     {
       /* The user expects the line count to be the number of times
@@ -2861,6 +2998,31 @@ accumulate_line_counts (source_info *src)
       }
 }
 
+static void
+output_conditions (FILE *gcov_file, const block_info *binfo)
+{
+    const condition_info& info = binfo->conditions;
+    if (info.n_terms == 0)
+	return;
+
+    const int expected = 2 * info.n_terms;
+    const int got = info.popcount ();
+
+    fnotice (gcov_file, "conditions covered %d/%d\n", got, expected);
+    if (expected == got)
+	return;
+
+    for (unsigned i = 0; i < info.n_terms; i++)
+    {
+	gcov_type_unsigned index = 1;
+	index <<= i;
+	if (!(index & info.truev))
+	    fnotice (gcov_file, "condition %2u not covered (true)\n", i);
+	if (!(index & info.falsev))
+	    fnotice (gcov_file, "condition %2u not covered (false)\n", i);
+    }
+}
+
 /* Output information about ARC number IX.  Returns nonzero if
    anything is output.  */
 
@@ -3071,16 +3233,29 @@ output_line_details (FILE *f, const line_info *line, unsigned line_num)
 	  if (flag_branches)
 	    for (arc = (*it)->succ; arc; arc = arc->succ_next)
 	      jx += output_branch_count (f, jx, arc);
+
+	  if (flag_conditions)
+	      output_conditions (f, *it);
 	}
     }
-  else if (flag_branches)
+  else
     {
-      int ix;
+      if (flag_branches)
+	{
+	  int ix;
+
+	  ix = 0;
+	  for (vector<arc_info *>::const_iterator it = line->branches.begin ();
+		  it != line->branches.end (); it++)
+	      ix += output_branch_count (f, ix, (*it));
+	}
 
-      ix = 0;
-      for (vector<arc_info *>::const_iterator it = line->branches.begin ();
-	   it != line->branches.end (); it++)
-	ix += output_branch_count (f, ix, (*it));
+      if (flag_conditions)
+	{
+	  for (vector<block_info *>::const_iterator it = line->blocks.begin ();
+	       it != line->blocks.end (); it++)
+	      output_conditions (f, *it);
+	}
     }
 }
 
diff --git a/gcc/ipa-inline.cc b/gcc/ipa-inline.cc
index f8bb072c422..b826dcd580c 100644
--- a/gcc/ipa-inline.cc
+++ b/gcc/ipa-inline.cc
@@ -646,7 +646,7 @@ can_early_inline_edge_p (struct cgraph_edge *e)
 			 "  edge not inlinable: not in SSA form\n");
       return false;
     }
-  else if (profile_arc_flag
+  else if ((profile_arc_flag || profile_condition_flag)
 	   && ((lookup_attribute ("no_profile_instrument_function",
 				 DECL_ATTRIBUTES (caller->decl)) == NULL_TREE)
 	       != (lookup_attribute ("no_profile_instrument_function",
diff --git a/gcc/ipa-split.cc b/gcc/ipa-split.cc
index 60021bad13c..9890518e11f 100644
--- a/gcc/ipa-split.cc
+++ b/gcc/ipa-split.cc
@@ -1929,7 +1929,8 @@ pass_split_functions::gate (function *)
   /* When doing profile feedback, we want to execute the pass after profiling
      is read.  So disable one in early optimization.  */
   return (flag_partial_inlining
-	  && !profile_arc_flag && !flag_branch_probabilities);
+      && !profile_arc_flag && !flag_branch_probabilities
+      && !profile_condition_flag);
 }
 
 } // anon namespace
diff --git a/gcc/passes.cc b/gcc/passes.cc
index 36e5b4ac45f..ee099188844 100644
--- a/gcc/passes.cc
+++ b/gcc/passes.cc
@@ -352,7 +352,8 @@ finish_optimization_passes (void)
   gcc::dump_manager *dumps = m_ctxt->get_dumps ();
 
   timevar_push (TV_DUMP);
-  if (profile_arc_flag || flag_test_coverage || flag_branch_probabilities)
+  if (profile_arc_flag || profile_condition_flag || flag_test_coverage
+          || flag_branch_probabilities)
     {
       dumps->dump_start (pass_profile_1->static_pass_number, NULL);
       end_branch_prob ();
diff --git a/gcc/profile.cc b/gcc/profile.cc
index a67cce5b199..b51fabbb59b 100644
--- a/gcc/profile.cc
+++ b/gcc/profile.cc
@@ -69,6 +69,9 @@ along with GCC; see the file COPYING3.  If not see
 
 #include "profile.h"
 
+int find_conditions (basic_block, basic_block, basic_block*, int*, int);
+int instrument_decisions (basic_block*, int, int);
+
 /* Map from BBs/edges to gcov counters.  */
 vec<gcov_type> bb_gcov_counts;
 hash_map<edge,gcov_type> *edge_gcov_counts;
@@ -100,6 +103,7 @@ static int total_num_passes;
 static int total_num_times_called;
 static int total_hist_br_prob[20];
 static int total_num_branches;
+static int total_num_conds;
 
 /* Forward declarations.  */
 static void find_spanning_tree (struct edge_list *);
@@ -1155,6 +1159,12 @@ read_thunk_profile (struct cgraph_node *node)
    the flow graph that are needed to reconstruct the dynamic behavior of the
    flow graph.  This data is written to the gcno file for gcov.
 
+   When FLAG_PROFILE_CONDITIONS is nonzero, this functions instruments the
+   edges in the control flow graph to track what conditions are evaluated to in
+   order to determine what conditions are covered and have an independent
+   effect on the outcome (modified condition/decision coverage).  This data is
+   written to the gcno file for gcov.
+
    When FLAG_BRANCH_PROBABILITIES is nonzero, this function reads auxiliary
    information from the gcda file containing edge count information from
    previous executions of the function being compiled.  In this case, the
@@ -1173,6 +1183,7 @@ branch_prob (bool thunk)
   struct edge_list *el;
   histogram_values values = histogram_values ();
   unsigned cfg_checksum, lineno_checksum;
+  bool output_to_file;
 
   total_num_times_called++;
 
@@ -1381,7 +1392,7 @@ branch_prob (bool thunk)
 
   /* Compute two different checksums. Note that we want to compute
      the checksum in only once place, since it depends on the shape
-     of the control flow which can change during 
+     of the control flow which can change during
      various transformations.  */
   if (thunk)
     {
@@ -1397,10 +1408,18 @@ branch_prob (bool thunk)
 
   /* Write the data from which gcov can reconstruct the basic block
      graph and function line numbers (the gcno file).  */
+  output_to_file = false;
   if (coverage_begin_function (lineno_checksum, cfg_checksum))
     {
       gcov_position_t offset;
 
+      /* The condition coverage needs a deeper analysis to identify expressions
+       * of conditions, which means it is not yet ready to write to the gcno
+       * file.  It will write its entries later, but needs to know if it do it
+       * in the first place, which is controlled by the return value of
+       * coverage_begin_function. */
+      output_to_file = true;
+
       /* Basic block flags */
       offset = gcov_write_tag (GCOV_TAG_BLOCKS);
       gcov_write_unsigned (n_basic_blocks_for_fn (cfun));
@@ -1512,29 +1531,67 @@ branch_prob (bool thunk)
 
   remove_fake_edges ();
 
+  if (profile_condition_flag || profile_arc_flag)
+      gimple_init_gcov_profiler ();
+
+  if (profile_condition_flag)
+    {
+      basic_block entry = ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb;
+      basic_block exit  = EXIT_BLOCK_PTR_FOR_FN (cfun);
+
+      // find_conditions () expect memory up front, see that function for
+      // details
+      const int max_blocks = 5 * n_basic_blocks_for_fn (cfun);
+      auto_vec<basic_block> blocks (max_blocks);
+      blocks.quick_grow (max_blocks);
+
+      const int max_sizes = n_basic_blocks_for_fn (cfun) + 1;
+      auto_vec<int> sizes (max_sizes);
+      sizes.quick_grow (max_sizes);
+
+      int nconds = find_conditions
+	  (entry, exit, blocks.address (), sizes.address (), max_blocks);
+      total_num_conds += nconds;
+
+      if (nconds > 0
+	  && coverage_counter_alloc (GCOV_COUNTER_CONDS, 2 * nconds)
+	  && output_to_file)
+	{
+	  gcov_position_t offset = gcov_write_tag (GCOV_TAG_CONDS);
+	  for (int i = 0; i < nconds; ++i)
+	    {
+	      int idx = sizes[i];
+	      int len = sizes[i + 1] - idx;
+	      basic_block *itr = blocks.address () + idx;
+	      int terms = instrument_decisions (itr, len, i);
+	      gcov_write_unsigned (blocks[idx]->index);
+	      gcov_write_unsigned (terms);
+	    }
+	  gcov_write_length (offset);
+	}
+    }
+
   /* For each edge not on the spanning tree, add counting code.  */
   if (profile_arc_flag
       && coverage_counter_alloc (GCOV_COUNTER_ARCS, num_instrumented))
     {
       unsigned n_instrumented;
 
-      gimple_init_gcov_profiler ();
-
       n_instrumented = instrument_edges (el);
 
       gcc_assert (n_instrumented == num_instrumented);
 
       if (flag_profile_values)
 	instrument_values (values);
-
-      /* Commit changes done by instrumentation.  */
-      gsi_commit_edge_inserts ();
     }
 
   free_aux_for_edges ();
 
   values.release ();
   free_edge_list (el);
+  /* Commit changes done by instrumentation.  */
+  gsi_commit_edge_inserts ();
+
   coverage_end_function (lineno_checksum, cfg_checksum);
   if (flag_branch_probabilities
       && (profile_status_for_fn (cfun) == PROFILE_READ))
@@ -1664,6 +1721,7 @@ init_branch_prob (void)
   total_num_passes = 0;
   total_num_times_called = 0;
   total_num_branches = 0;
+  total_num_conds = 0;
   for (i = 0; i < 20; i++)
     total_hist_br_prob[i] = 0;
 }
@@ -1703,5 +1761,7 @@ end_branch_prob (void)
 		     (total_hist_br_prob[i] + total_hist_br_prob[19-i]) * 100
 		     / total_num_branches, 5*i, 5*i+5);
 	}
+      fprintf (dump_file, "Total number of conditions: %d\n",
+	       total_num_conds);
     }
 }
diff --git a/gcc/testsuite/g++.dg/gcov/gcov-18.C b/gcc/testsuite/g++.dg/gcov/gcov-18.C
new file mode 100644
index 00000000000..9ac14dfeff6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gcov/gcov-18.C
@@ -0,0 +1,227 @@
+/* { dg-options "--coverage -fprofile-conditions -std=c++11" } */
+/* { dg-do run { target native } } */
+
+#include <vector>
+#include <stdexcept>
+
+class nontrivial_destructor
+{
+public:
+    explicit nontrivial_destructor (int v) : val (v) {}
+    ~nontrivial_destructor () {}
+
+    explicit operator bool() const { return bool(val); }
+
+    int val;
+};
+
+int identity (int x) { return x; }
+int throws (int) { throw std::runtime_error("exception"); }
+
+int throw_if (int x)
+{
+    if (x) /* conditions(1/2) true(0) */
+        throw std::runtime_error("exception");
+    return x;
+}
+
+/* used for side effects to insert nodes in conditional bodies etc. */
+int x = 0;
+
+/* conditionals work in the presence of non-trivial destructors */
+void mcdc001a (int a)
+{
+    nontrivial_destructor v (a);
+
+    if (v.val > 0) /* conditions(2/2) */
+        x = v.val;
+    else
+        x = -v.val;
+}
+
+/* non-trivial destructor in-loop temporary */
+nontrivial_destructor
+mcdc002a (int a, int b)
+{
+    for (int i = 0; i < a; i++) /* conditions(2/2) */
+    {
+        nontrivial_destructor tmp (a);
+        if (tmp.val % b) /* conditions(2/2) */
+            return nontrivial_destructor (0);
+        x += i;
+    } /* conditions(suppress) */
+      /* conditions(end) */
+
+    return nontrivial_destructor (a * b);
+}
+
+/* conditional in constructor */
+void mcdc003a (int a)
+{
+    class C
+    {
+    public:
+        explicit C (int e) : v (e) {
+            if (e) /* conditions(1/2) false(0) */
+                v = x - e;
+        }
+        int v;
+    };
+
+    C c (a);
+    if (c.v > 2) /* conditions(1/2) true(0) */
+        x = c.v + a;
+}
+
+/* conditional in destructor */
+void mcdc004a (int a)
+{
+    class C
+    {
+    public:
+        explicit C (int e) : v (e) {}
+        ~C () {
+            if (v) /* conditions(2/2) */
+                x = 2 * v;
+        }
+        int v;
+    };
+
+    C c (a);
+    x = 1; // arbitrary action between ctor+dtor
+}
+
+/* conditional in try */
+void mcdc005a (int a)
+{
+    try
+    {
+        if (a) /* conditions(1/2) true(0) */
+            x = 2 * identity (a);
+        else
+            x = 1;
+    }
+    catch (...)
+    {
+        x = 0;
+    }
+}
+
+/* conditional in catch */
+void mcdc006a (int a) {
+    try
+    {
+        throws (a);
+    }
+    catch (std::exception&)
+    {
+        if (a) /* conditions(1/2) false(0) */
+            x = identity (a);
+        else
+            x = 0;
+    }
+}
+
+void mcdc006b (int a)
+{
+    if (a) /* conditions(1/2) true(0) */
+        throws (a);
+    else
+        x = 1;
+}
+
+void mcdc006c (int a) try
+{
+    throws (a);
+}
+catch (...) {
+    if (a) /* conditions(2/2) */
+        x = 5;
+}
+
+/* temporary with destructor as term */
+void mcdc007a (int a, int b)
+{
+    x = a && nontrivial_destructor (b); /* conditions(3/4) false(1) destructor() */
+}
+
+void mcdc007b (int a, int b)
+{
+    if (a || throw_if (b)) /* conditions(3/4) true(1) destructor() */
+        x = -1;
+    else
+        x = 1;
+}
+
+void mcdc007c (int a, int b)
+{
+    if (throw_if (a) || throw_if (b)) /* conditions(2/4) true(0 1) destructor() */
+        x = -1;
+    else
+        x = 1;
+}
+
+/* destructor with delete */
+void mcdc008a (int a)
+{
+    class C
+    {
+    public:
+        int size = 5;
+        int* ptr = nullptr;
+
+        explicit C (int v) : size(v + 5), ptr (new int[size]) /* conditions(suppress) */
+                                                              /* conditions(end) */
+        {
+            for (int i = 0; i < size; i++) /* conditions(2/2) */
+                ptr[i] = i + 1;
+        }
+        ~C()
+        {
+            if (ptr) /* conditions(1/2) false(0) */
+                delete ptr; /* conditions(suppress) */
+                            /* conditions(end) */
+        }
+    };
+
+    C c (a);
+    if (c.ptr[a + 1]) /* conditions(1/2) false(0) */
+        x = a;
+}
+
+int
+main (void)
+{
+    mcdc001a (0);
+    mcdc001a (1);
+
+    mcdc002a (1, 1);
+    mcdc002a (1, 2);
+
+    mcdc003a (1);
+
+    mcdc004a (0);
+    mcdc004a (1);
+
+    mcdc005a (0);
+
+    mcdc006a (1);
+
+    mcdc006b (0);
+
+    mcdc006c (0);
+    mcdc006c (1);
+
+    mcdc007a (0, 0);
+    mcdc007a (1, 1);
+
+    mcdc007b (0, 0);
+    mcdc007b (1, 0);
+
+    mcdc007c (0, 0);
+
+    mcdc008a (1);
+
+}
+
+/* { dg-final { run-gcov conditions { --conditions gcov-18.C } } } */
diff --git a/gcc/testsuite/gcc.misc-tests/gcov-19.c b/gcc/testsuite/gcc.misc-tests/gcov-19.c
new file mode 100644
index 00000000000..c1ea8acee7d
--- /dev/null
+++ b/gcc/testsuite/gcc.misc-tests/gcov-19.c
@@ -0,0 +1,735 @@
+/* { dg-options "-fprofile-conditions -ftest-coverage" } */
+/* { dg-do run { target native } } */
+
+/* some side effect to stop branches from being pruned */
+int x = 0;
+
+/* || works */
+void mcdc001a (int a, int b)
+{
+    if (a || b) /* conditions(1/4) true(0) false(0 1) */
+		/* conditions(end) */
+	x = 1;
+    else
+	x = 2;
+}
+
+void mcdc001b (int a, int b)
+{
+    if (a || b) /* conditions(3/4) true(0) false() */
+		/* conditions(end) */
+	x = 1;
+    else
+	x = 2;
+}
+
+void mcdc001c (int a, int b)
+{
+    if (a || b) /* conditions(4/4) */
+	x = 1;
+    else
+	x = 2;
+}
+
+void mcdc001d (int a, int b, int c)
+{
+    if (a || b || c) /* conditions(3/6) false(1 2) true(2) */
+		     /* conditions(end) */
+	x = 1;
+}
+
+/* && works */
+void mcdc002a (int a, int b)
+{
+    if (a && b) /* conditions(1/4) true(0 1) false(0) */
+		/* conditions(end) */
+	x = 1;
+    else
+	x = 2;
+}
+
+void mcdc002b (int a, int b)
+{
+    if (a && b) /* conditions(3/4) false(0) */
+		/* conditions(end) */
+	x = 1;
+    else
+	x = 2;
+}
+
+void mcdc002c (int a, int b)
+{
+    if (a && b) /* conditions(4/4) */
+	x = 1;
+    else
+	x = 2;
+}
+
+void mcdc002d (int a, int b, int c)
+{
+    /*
+     * This is an odd case, and falls victim to trying to detect nested ifs.
+     *
+     * if (a) if (b) if (c) with no else is equivalent to if (a && b && c) and
+     * the CFGs are identical *unless* the else nodes are generated too. In the
+     * && expression, all false edges should go to the same else, but in the
+     * nested-if case they go to different elses.
+     *
+     * This can be surprising, and bad for MC/DC because non-independent
+     * conditionals masked by terms further-right can not be detected. If an
+     * else node is generated, this expression becomes a 3-term decision again.
+     */
+    if (a && b && c) /* conditions(suppress) conditions(4/6) false(0 2) */
+	/* conditions(end) */
+	x = 1;
+}
+
+/* negation works */
+void mcdc003a (int a, int b)
+{
+    if (!a || !b) /* conditions(2/4) false(0 1) */
+		  /* conditions(end) */
+	x = 1;
+    else
+	x = 2;
+}
+
+/* single conditionals with and without else */
+void mcdc004a (int a)
+{
+    if (a) /* conditions(1/2) true(0) */
+	   /* conditions(end) */
+	x = 1;
+    else
+	x = 2;
+}
+
+void mcdc004b (int a)
+{
+    if (a) /* conditions(2/2) */
+	x = 1;
+    else
+	x = 2;
+}
+
+void mcdc004c (int a)
+{
+    if (a) /* conditions(1/2) false(0) */
+	   /* conditions(end) */
+	x = 1;
+}
+
+void mcdc004d (int a, int b, int c) {
+    if (a)  /* conditions(2/2) */
+    {
+	if (b || c) /* conditions(1/4) true(1) false(0 1) */
+	    x = a + b + c;
+    }
+}
+
+/* mixing && and || works */
+void mcdc005a (int a, int b, int c)
+{
+    if ((a && b) || c) /* conditions(1/6) true(0 1) false(0 1 2) */
+		       /* conditions(end) */
+	x = 1;
+    else
+	x = 2;
+}
+
+void mcdc005b (int a, int b, int c, int d)
+{
+    if ((a && (b || c)) && d) /* conditions(4/8) true(1 2 3) false(0) */
+			      /* conditions(end) */
+	x = 1;
+    else
+	x = 2;
+}
+
+/* nested conditionals */
+void mcdc006a (int a, int b, int c, int d, int e)
+{
+    if (a) /* conditions(2/2) */
+    {
+	if (b && c) /* conditions(3/4) false(1) */
+		    /* conditions(end) */
+	    x = 1;
+	else
+	    x = 2;
+    }
+    else
+    {
+	if (c || d) /* conditions(2/4) true(0 1) */
+		    /* conditions(end) */
+	    x = 3;
+	else
+	    x = 4;
+    }
+}
+
+void mcdc006b (int a, int b, int c)
+{
+    if (a) /* conditions(2/2) */
+    {
+	if (b) /*conditions(2/2) */
+	{
+	    if (c) /* conditions(2/2) */
+	    {
+		x = a + b + c;
+	    }
+	}
+    }
+}
+
+/* else/if */
+void mcdc007a (int a, int b, int c, int d)
+{
+    if (a) /* conditions(2/2) */
+    {
+	if (b) /* conditions(1/2) true(0) */
+	       /* conditions(end) */
+	    x = 1;
+	else
+	    x = 2;
+    }
+    else if (c) /* conditions(2/2) */
+    {
+	if (d) /* conditions(1/2) true(0) */
+	       /* conditions(end) */
+	    x = 3;
+    }
+}
+
+void mcdc007b (int a, int b, int c)
+{
+    /* similar to if (a || b || c) x = 1 */
+    if (a) /* conditions(2/2) */
+	goto then;
+    else if (b) /* conditions(2/2) */
+	goto then;
+    else if (c) /* conditions(1/2) true(0) */
+	/* conditions(end) */
+	goto then;
+
+    return;
+
+then:
+    x = 1;
+}
+
+/* while loop */
+void mcdc008a (int a)
+{
+    while (a < 10) /* conditions(2/2) */
+	x = a++;
+}
+
+void mcdc008b (int a)
+{
+    while (a > 10) /* conditions(1/2) true(0) */
+		   /* conditions(end) */
+	x = a--;
+}
+
+void mcdc008c (int a)
+{
+    // should work, even with no body
+    while (a) /* conditions(2/2) */
+	break;
+}
+
+void mcdc008d (int a, int b, int c, int d)
+{
+    /* multi-term loop conditional */
+    while ((a && (b || c)) && d) /* conditions(8/8) */
+	a = b = c = d = 0;
+}
+
+void mcdc009a (int a, int b)
+{
+    while (a > 0 && b > 0) /* conditions(3/4) false(1) */
+			   /* conditions(end) */
+	x = a--;
+}
+
+/* for loop */
+void mcdc010a(int a, int b)
+{
+    for (int i = 0; i < b; i++) /* conditions(2/2) */
+    {
+	if (a < b) /* conditions(2/2) */
+	    x = 1;
+	else
+	    x = a += 2;
+    }
+}
+
+int always (int) { return 1; }
+
+/* no-condition infinite loops */
+void mcdc010b (int a)
+{
+    for (;;)
+    {
+	if (always(a)) /* conditions(1/2) false(0) */
+		       /* conditions(end) */
+	{
+	    x = a;
+	    break;
+	}
+	x += a + 1;
+    }
+}
+
+/* conditionals without control flow constructs work */
+void mcdc011a (int a, int b, int c)
+{
+    x = (a && b) || c; /* conditions(5/6) false(1) */
+		       /* conditions(end) */
+}
+
+/* sequential expressions are handled independently */
+void mcdc012a (int a, int b, int c) {
+    if (a || b) /* conditions(3/4) true(0) */
+	/* conditions(end) */
+	x = 1;
+    else
+	x = 2;
+
+    if (c) /* conditions(2/2) */
+	x = 1;
+}
+
+/*
+ * cannot ever satisfy MC/DC, even with all input combinations, because not all
+ * variables independently affect the decision
+ */
+void mcdc013a (int a, int /* b */, int c)
+{
+    /*
+     * Specification: (a && b) || c
+     *
+     * But the expression was implemented wrong. This has branch coverage, but
+     * not MC/DC
+     */
+    if ((a && !c) || c) /* conditions(5/6) false(1) */
+	/* conditions(end) */
+	x = 1;
+    else
+	x = 2;
+}
+
+void mcdc014a ()
+{
+    int conds[64] = { 0 };
+    /* conditions(64/128) true(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63) */
+    x = conds[ 0] || conds[ 1] || conds[ 2] || conds[ 3] || conds[ 4] ||
+	conds[ 5] || conds[ 6] || conds[ 7] || conds[ 8] || conds[ 9] ||
+	conds[10] || conds[11] || conds[12] || conds[13] || conds[14] ||
+	conds[15] || conds[16] || conds[17] || conds[18] || conds[19] ||
+	conds[20] || conds[21] || conds[22] || conds[23] || conds[24] ||
+	conds[25] || conds[26] || conds[27] || conds[28] || conds[29] ||
+	conds[30] || conds[31] || conds[32] || conds[33] || conds[34] ||
+	conds[35] || conds[36] || conds[37] || conds[38] || conds[39] ||
+	conds[40] || conds[41] || conds[42] || conds[43] || conds[44] ||
+	conds[45] || conds[46] || conds[47] || conds[48] || conds[49] ||
+	conds[50] || conds[51] || conds[52] || conds[53] || conds[54] ||
+	conds[55] || conds[56] || conds[57] || conds[58] || conds[59] ||
+	conds[60] || conds[61] || conds[62] || conds[63]
+	;  /* conditions(end) */
+}
+
+/* early returns */
+void mcdc015a (int a, int b)
+{
+    if (a) /* conditions(2/2) */
+	return;
+
+    if (b) /* conditions(1/2) true(0) */
+	   /* conditions(end) */
+	x = 1;
+}
+
+void mcdc015b (int a, int b)
+{
+    for (int i = 5; i > a; i--) /* conditions(2/2) */
+    {
+	if (i == b) /* conditions(2/2) */
+	    return;
+	x = i;
+    }
+}
+
+void mcdc015c (int a, int b)
+{
+    for (int i = 5; i > a; i--) /* conditions(2/2) */
+    {
+	if (i == b) /* conditions(2/2) */
+	{
+	    x = 0;
+	    return;
+	}
+	else
+	{
+	    x = 1;
+	    return;
+	}
+
+	x = i;
+    }
+}
+
+
+/* check nested loops */
+void mcdc016a (int a, int b)
+{
+    for (int i = 0; i < a; i++) /* conditions(2/2) */
+	for (int k = 0; k < b; k++) /* conditions(2/2) */
+	    x = i + k;
+}
+
+void mcdc016b (int a, int b)
+{
+    for (int i = 0; i < a; i++) /* conditions(2/2) */
+    {
+	if (a > 5) /* conditions(2/2) */
+	    break;
+
+	for (int k = 0; k < b; k++) /* conditions(2/2) */
+	    x = i + k;
+    }
+}
+
+void mcdc016c (int a, int b)
+{
+    for (int i = 0; i < a; i++) /* conditions(2/2) */
+    {
+	if (a > 5) /* conditions(1/2) true(0) */
+		   /* conditions(end) */
+	    return;
+
+	for (int k = 0; k < b; k++) /* conditions(2/2) */
+	    x = i + k;
+    }
+}
+
+void mcdc016d (int a, int b)
+{
+    for (int i = 0; i < a; i++) /* conditions(2/2) */
+    {
+	for (int k = 0; k < 5; k++) /* conditions(2/2) */
+	{
+	    if (b > 5) /* conditions(1/2) true(0) */
+		       /* conditions(end) */
+		return;
+	    x = i + k;
+	}
+
+    }
+}
+
+/* do-while loops */
+void mcdc017a (int a)
+{
+    do {
+	a--;
+    } while (a > 0); /* conditions(2/2) */
+}
+
+void noop () {}
+
+void mcdc017b (int a, int b)
+{
+    do {
+	/*
+	 * This call is important; it can add more nodes to the body in the
+	 * CFG, which has changes how close exits and breaks are to the loop
+	 * conditional.
+	 */
+	noop ();
+	a--;
+	if (b) /* conditions(2/2) */
+	    break;
+
+    } while (a > 0); /* conditions(2/2) */
+}
+
+void mcdc017c (int a, int b)
+{
+    int left = 0;
+    int right = 0;
+    int n = a + b;
+    do {
+	if (a) /* conditions(1/2) false(0) */
+	       /* conditions(end) */
+	{
+	    left = a > left ? b : left; /* conditions(2/2) */
+	}
+	if (b) /* conditions(1/2) false(0) */
+	{
+	    right = b > right ? a : right; /* conditions(2/2) */
+	}
+    } while (n-- > 0); /* conditions(2/2) */
+}
+
+int id  (int x) { return  x; }
+int inv (int x) { return !x; }
+
+/* collection of odd cases lifted-and-adapted from real-world code */
+int mcdc018a (int a, int b, int c, int d, int e, int f, int g, int len)
+{
+    int n;
+    /* adapted from zlib/gz_read */
+    do
+    {
+	n = -1;
+	if (n > len) /* conditions(2/2) */
+	    n = len;
+
+	if (b) /* conditions(2/2) */
+	{
+	    if (b < 5) /* conditions(2/2) */
+		x = 1;
+	    noop();
+	}
+	else if (c && d) /* conditions(3/4) false(1) */
+	{
+	    x = 2;
+	    break;
+	}
+	else if (e || f) /* conditions(2/4) false(0 1) */
+			 /* conditions(end) */
+	{
+	    if (id(g)) /* conditions(2/2) */
+		return 0;
+	    continue;
+	}
+    } while (a-- > 0); /* conditions(2/2) */
+
+    return 1;
+}
+
+void mcdc018b (int a, int b, int c) {
+    int n;
+    while (a) /* conditions(2/2) */
+    {
+	/*
+	 * else block does not make a difference for the problem, but ensures
+	 * loop termination
+	 */
+	if (b) /* conditions(2/2) */
+	    n = c ? 0 : 0; // does not show up in CFG (embedded in the block)
+	else
+	    n = 0;
+	a = n;
+    }
+}
+
+/* too many conditions, coverage gives up */
+void mcdc019a ()
+{
+    int conds[65] = { 0 };
+    #pragma GCC diagnostic push
+    #pragma GCC diagnostic ignored "-Wcoverage-too-many-conditions"
+    x = conds[ 0] || conds[ 1] || conds[ 2] || conds[ 3] || conds[ 4] ||
+	conds[ 5] || conds[ 6] || conds[ 7] || conds[ 8] || conds[ 9] ||
+	conds[10] || conds[11] || conds[12] || conds[13] || conds[14] ||
+	conds[15] || conds[16] || conds[17] || conds[18] || conds[19] ||
+	conds[20] || conds[21] || conds[22] || conds[23] || conds[24] ||
+	conds[25] || conds[26] || conds[27] || conds[28] || conds[29] ||
+	conds[30] || conds[31] || conds[32] || conds[33] || conds[34] ||
+	conds[35] || conds[36] || conds[37] || conds[38] || conds[39] ||
+	conds[40] || conds[41] || conds[42] || conds[43] || conds[44] ||
+	conds[45] || conds[46] || conds[47] || conds[48] || conds[49] ||
+	conds[50] || conds[51] || conds[52] || conds[53] || conds[54] ||
+	conds[55] || conds[56] || conds[57] || conds[58] || conds[59] ||
+	conds[60] || conds[61] || conds[62] || conds[63] || conds[64]
+	;
+    #pragma GCC diagnostic pop
+}
+
+/* ternary */
+void mcdc020a (int a)
+{
+    // special case, this can be reduced to:
+    // _1 = argc != 0;
+    // e = (int) _1;
+    x = a ? 1 : 0;
+
+    // changing to different int makes branch
+    x = a ? 2 : 1; /* conditions(2/2) */
+}
+
+void mcdc020b (int a, int b)
+{
+    x = (a || b) ? 1 : 0; /* conditions(3/4) true(1) */
+}
+
+void mcdc020c (int a, int b)
+{
+    x = a ? 0
+	: b ? 1 /* conditions(2/2) */
+	: 2;    /* conditions(1/2) false(0) */
+		/* conditions(end) */
+}
+
+/* infinite loop (no exit-edge), this should not be called, but it should
+   compile fine */
+void mcdc021a ()
+{
+    while (1)
+	;
+}
+
+/* test with functions as conditionals */
+
+int main ()
+{
+    mcdc001a (0, 1);
+
+    mcdc001b (0, 1);
+    mcdc001b (0, 0);
+
+    mcdc001c (0, 1);
+    mcdc001c (0, 0);
+    mcdc001c (1, 1);
+
+    mcdc001d (1, 1, 1);
+    mcdc001d (0, 1, 0);
+
+    mcdc002a (1, 0);
+
+    mcdc002b (1, 0);
+    mcdc002b (1, 1);
+
+    mcdc002c (0, 0);
+    mcdc002c (1, 1);
+    mcdc002c (1, 0);
+
+    mcdc002d (1, 1, 1);
+    mcdc002d (1, 0, 0);
+
+    mcdc003a (0, 0);
+    mcdc003a (1, 0);
+
+    mcdc004a (0);
+    mcdc004b (0);
+    mcdc004b (1);
+    mcdc004c (1);
+
+    mcdc004d (0, 0, 0);
+    mcdc004d (1, 1, 1);
+
+    mcdc005a (1, 0, 1);
+
+    mcdc005b (1, 1, 0, 0);
+    mcdc005b (1, 0, 0, 0);
+
+    mcdc006a (0, 0, 0, 0, 0);
+    mcdc006a (1, 0, 0, 0, 0);
+    mcdc006a (1, 1, 1, 0, 0);
+
+    mcdc006b (0, 0, 0);
+    mcdc006b (1, 0, 0);
+    mcdc006b (1, 1, 0);
+    mcdc006b (1, 1, 1);
+
+    mcdc007a (0, 0, 0, 0);
+    mcdc007a (1, 0, 0, 0);
+    mcdc007a (0, 0, 1, 0);
+
+    mcdc007b (0, 0, 0);
+    mcdc007b (0, 1, 1);
+    mcdc007b (1, 0, 1);
+
+    mcdc008a (0);
+
+    mcdc008b (0);
+
+    mcdc008c (0);
+    mcdc008c (1);
+
+    mcdc008d (0, 0, 0, 0);
+    mcdc008d (1, 0, 0, 0);
+    mcdc008d (1, 0, 1, 0);
+    mcdc008d (1, 0, 1, 1);
+    mcdc008d (1, 1, 1, 1);
+
+    mcdc009a (0, 0);
+    mcdc009a (1, 1);
+
+    mcdc010a (0, 0);
+    mcdc010a (0, 9);
+    mcdc010a (2, 1);
+
+    mcdc010b (1);
+
+    mcdc011a (0, 0, 0);
+    mcdc011a (1, 1, 0);
+    mcdc011a (1, 0, 1);
+
+    mcdc012a (0, 0, 0);
+    mcdc012a (0, 1, 1);
+
+    mcdc013a (0, 0, 0);
+    mcdc013a (0, 0, 1);
+    mcdc013a (0, 1, 0);
+    mcdc013a (0, 1, 1);
+    mcdc013a (1, 0, 0);
+    mcdc013a (1, 0, 1);
+    mcdc013a (1, 1, 0);
+    mcdc013a (1, 1, 1);
+
+    mcdc014a ();
+
+    mcdc015a (0, 0);
+    mcdc015a (1, 0);
+
+    mcdc015b (0, 0);
+    mcdc015b (0, 1);
+    mcdc015b (6, 1);
+
+    mcdc015c (0, 0);
+    mcdc015c (0, 5);
+    mcdc015c (6, 1);
+
+    mcdc016a (5, 5);
+
+    mcdc016b (5, 5);
+    mcdc016b (6, 5);
+
+    mcdc016c (5, 5);
+
+    mcdc016d (1, 0);
+
+    mcdc017a (0);
+    mcdc017a (2);
+
+    mcdc017b (2, 0);
+    mcdc017b (0, 1);
+
+    mcdc017c (1, 1);
+
+    mcdc018a (0, 0, 1, 1, 0, 0, 0, 0);
+    mcdc018a (0, 1, 0, 0, 0, 0, 1, -2);
+    mcdc018a (0, 6, 0, 0, 0, 0, 1, -2);
+    mcdc018a (0, 6, 0, 0, 0, 0, 1, -2);
+    mcdc018a (0, 0, 0, 1, 0, 1, 1, 0);
+    mcdc018a (1, 0, 0, 0, 1, 1, 0, 0);
+
+    mcdc018b (1, 0, 0);
+    mcdc018b (1, 1, 0);
+
+    mcdc019a ();
+
+    mcdc020a (0);
+    mcdc020a (1);
+
+    mcdc020b (0, 0);
+    mcdc020b (1, 0);
+
+    mcdc020c (0, 1);
+    mcdc020c (1, 1);
+}
+
+/* { dg-final { run-gcov conditions { --conditions gcov-19.c } } } */
diff --git a/gcc/testsuite/gcc.misc-tests/gcov-20.c b/gcc/testsuite/gcc.misc-tests/gcov-20.c
new file mode 100644
index 00000000000..56dd6cf89bb
--- /dev/null
+++ b/gcc/testsuite/gcc.misc-tests/gcov-20.c
@@ -0,0 +1,21 @@
+/* { dg-options "-fprofile-conditions -ftest-coverage -fprofile-update=atomic" } */
+/* { dg-do run { target native } } */
+
+/* some side effect to stop branches from being pruned */
+int x = 0;
+
+void conditions_atomic001(int a, int b)
+{
+  if (a || b) /* conditions(1/4) true(0) false(0 1) */
+	      /* conditions(end) */
+      x = 1;
+  else
+      x = 2;
+}
+
+int main ()
+{
+    conditions_atomic001 (0, 1);
+}
+
+/* { dg-final { run-gcov conditions { --conditions gcov-20.c } } } */
diff --git a/gcc/testsuite/lib/gcov.exp b/gcc/testsuite/lib/gcov.exp
index 9d5b2cdb86b..88f28d26bb6 100644
--- a/gcc/testsuite/lib/gcov.exp
+++ b/gcc/testsuite/lib/gcov.exp
@@ -174,6 +174,180 @@ proc verify-branches { testname testcase file } {
     return $failed
 }
 
+#
+# verify-conditions -- check that conditions are checked as expected
+#
+# TESTNAME is the name of the test, including unique flags.
+# TESTCASE is the name of the test file.
+# FILE is the name of the gcov output file.
+#
+# Checks are based on comments in the source file. Condition coverage comes
+# with with two types of output, a summary and a list of the uncovered
+# conditions. Both must be checked to pass the test
+#
+# To check for conditions, add a comment the line of a conditional:
+# /* conditions(n/m) true(0 1) false(1) */
+#
+# where n/m are the covered and total conditions in the expression. The true()
+# and false() take the indices expected *not* covered.
+#
+# This means that all coverage statements should have been seen:
+# /* conditions(end) */
+#
+# If all conditions are covered i.e. n == m, then conditions(end) can be
+# omitted. If either true() or false() are empty they can be omitted too.
+#
+# C++ can insert conditionals in the CFG that are not present in source code.
+# These must be manually suppressed since unexpected and unhandled conditions
+# are an error (to help combat regressions). Output can be suppressed with
+# conditions(suppress) and conditions(end). suppress should usually be on a
+# closing brace.
+#
+# Some expressions, when using unnamed temporaries as operands, will have
+# destructors in expressions. The coverage of the destructor will be reported
+# on the same line as the expression itself, but suppress() would also swallow
+# the expected tested-for messages. To handle these, use the destructor() [1]
+# which will suppress everything from and including the second "conditions
+# covered".
+#
+# [1] it is important that the destructor() is *on the same line* as the
+#     conditions(m/n)
+proc verify-conditions { testname testcase file } {
+    set failed 0
+    set suppress 0
+    set destructor 0
+    set shouldt ""
+    set shouldf ""
+    set shouldall ""
+    set fd [open $file r]
+    set n 0
+    set keywords {"end" "suppress"}
+    while {[gets $fd line] >= 0} {
+        regexp "^\[^:\]+: *(\[0-9\]+):" "$line" all n
+        set prefix "$testname line $n"
+
+        if {![regexp "condition" $line]} {
+            continue
+        }
+
+        # Missing coverage for both true and false will cause a failure, but
+        # only count it once for the report.
+        set ok 1
+        if [regexp {conditions *\(([0-9a-z/]+)\)} "$line" all e] {
+            # *Very* coarse sanity check: conditions() should either be a
+            # keyword or n/m, anything else means a buggy test case. end is
+            # optional for cases where all conditions are covered, since it
+            # only expects a single line of output.
+            if {([lsearch -exact $keywords $e] >= 0 || [regexp {\d+/\d+} "$e"]) == 0} {
+                fail "$prefix: expected conditions (n/m), (suppress) or (end); was ($e)"
+                incr failed
+                continue
+            }
+
+            # Any keyword means a new context. Set the error flag if not all
+            # expected output has been seen, and reset the state.
+
+            if {[llength $shouldt] != 0} {
+                fail "$prefix: expected uncovered (true) for terms $shouldt"
+                set ok 0
+            }
+
+            if {[llength $shouldf] != 0} {
+                fail "$prefix: expected uncovered (false) for terms $shouldf"
+                set ok 0
+            }
+
+            if {$shouldall ne ""} {
+                fail "$prefix: coverage summary not found; expected $shouldall"
+                set ok 0
+            }
+
+            set suppress 0
+            set destructor 0
+            set shouldt ""
+            set shouldf ""
+            set shouldall ""
+            set newt ""
+            set newf ""
+
+            if [regexp {destructor\(\)} "$line"] {
+                set destructor 1
+            }
+
+            if [regexp {(\d+)/(\d+)} "$e" all i k] {
+                regexp {true\(([0-9 ]+)\)}  "$line" all newt
+                regexp {false\(([0-9 ]+)\)} "$line" all newf
+
+                # Sanity check - if the true() and false() vectors should have
+                # m-n elements to cover all uncovered conditions. Because of
+                # masking it can sometimes be surprising what terms are
+                # independent, so this makes for more robust test at the cost
+                # of being slightly more annoying to write.
+                set nterms [expr [llength $newt] + [llength $newf]]
+                set nexpected [expr {$k - $i}]
+                if {$nterms != $nexpected} {
+                    fail "$prefix: expected $nexpected uncovered terms; got $nterms"
+                    set ok 0
+                }
+                set shouldall $e
+                set shouldt $newt
+                set shouldf $newf
+            } elseif {$e == "end"} {
+                # no-op - state has already been reset, and errors flagged
+            } elseif {$e == "suppress"} {
+                set suppress 1
+            } else {
+                # this should be unreachable, 
+                fail "$prefix: unhandled control ($e), should be unreachable"
+                set ok 0
+            }
+        } elseif {$suppress == 1} {
+            # ignore everything in a suppress block. C++ especially can insert
+            # conditionals in exceptions and destructors which would otherwise
+            # be considered unhandled.
+            continue
+        } elseif [regexp {condition +(\d+) not covered \(true\)} "$line" all cond] {
+            set i [lsearch $shouldt $cond]
+            if {$i != -1} {
+                set shouldt [lreplace $shouldt $i $i]
+            } else {
+                fail "$testname line $n: unexpected uncovered term $cond (true)"
+                set ok 0
+            }
+        } elseif [regexp {condition +(\d+) not covered \(false\)} "$line" all cond] {
+            set i [lsearch $shouldf $cond]
+            if {$i != -1} {
+                set shouldf [lreplace $shouldf $i $i]
+            } else {
+                fail "$testname line $n: unexpected uncovered term $cond (false)"
+                set ok 0
+            }
+        } elseif [regexp {conditions covered (\d+/\d+)} "$line" all cond] {
+            # the destructor-generated "conditions covered" lines will be
+            # written after all expression-related output. Handle these by
+            # turning on suppression if the destructor-suppression is
+            # requested.
+            if {$shouldall == "" && $destructor == 1} {
+                set suppress 1
+                continue
+            }
+
+            if {$cond == $shouldall} {
+                set shouldall ""
+            } else {
+                fail "$testname line $n: unexpected summary $cond"
+                set ok 0
+            }
+        }
+
+        if {$ok != 1} {
+            incr failed
+        }
+    }
+    close $fd
+    return $failed
+}
+
 #
 # verify-calls -- check that call return percentages are as expected
 #
@@ -321,6 +495,7 @@ proc run-gcov { args } {
     set gcov_args ""
     set gcov_verify_calls 0
     set gcov_verify_branches 0
+    set gcov_verify_conditions 0
     set gcov_verify_lines 1
     set gcov_verify_intermediate 0
     set gcov_remove_gcda 0
@@ -331,10 +506,13 @@ proc run-gcov { args } {
 	  set gcov_verify_calls 1
 	} elseif { $a == "branches" } {
 	  set gcov_verify_branches 1
+	} elseif { $a == "conditions" } {
+	  set gcov_verify_conditions 1
 	} elseif { $a == "intermediate" } {
 	  set gcov_verify_intermediate 1
 	  set gcov_verify_calls 0
 	  set gcov_verify_branches 0
+	  set gcov_verify_conditions 0
 	  set gcov_verify_lines 0
 	} elseif { $a == "remove-gcda" } {
 	  set gcov_remove_gcda 1
@@ -404,6 +582,11 @@ proc run-gcov { args } {
     } else {
 	set bfailed 0
     }
+    if { $gcov_verify_conditions } {
+	set cdfailed [verify-conditions $testname $testcase $testcase.gcov]
+    } else {
+	set cdfailed 0
+    }
     if { $gcov_verify_calls } {
 	set cfailed [verify-calls $testname $testcase $testcase.gcov]
     } else {
@@ -418,12 +601,12 @@ proc run-gcov { args } {
 
     # Report whether the gcov test passed or failed.  If there were
     # multiple failures then the message is a summary.
-    set tfailed [expr $lfailed + $bfailed + $cfailed + $ifailed]
+    set tfailed [expr $lfailed + $bfailed + $cdfailed + $cfailed + $ifailed]
     if { $xfailed } {
 	setup_xfail "*-*-*"
     }
     if { $tfailed > 0 } {
-	fail "$testname gcov: $lfailed failures in line counts, $bfailed in branch percentages, $cfailed in return percentages, $ifailed in intermediate format"
+	fail "$testname gcov: $lfailed failures in line counts, $bfailed in branch percentages, $cdfailed in condition/decision, $cfailed in return percentages, $ifailed in intermediate format"
 	if { $xfailed } {
 	    clean-gcov $testcase
 	}
diff --git a/gcc/tree-profile.cc b/gcc/tree-profile.cc
index 6d40401f86f..d737e45cd62 100644
--- a/gcc/tree-profile.cc
+++ b/gcc/tree-profile.cc
@@ -24,6 +24,7 @@ along with GCC; see the file COPYING3.  If not see
 /* Generate basic block profile instrumentation and auxiliary files.
    Tree-based version.  See profile.cc for overview.  */
 
+#define INCLUDE_ALGORITHM
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
@@ -58,6 +59,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "alloc-pool.h"
 #include "symbol-summary.h"
 #include "symtab-thunks.h"
+#include "cfganal.h"
+#include "cfgloop.h"
 
 static GTY(()) tree gcov_type_node;
 static GTY(()) tree tree_interval_profiler_fn;
@@ -73,6 +76,879 @@ static GTY(()) tree ic_tuple_var;
 static GTY(()) tree ic_tuple_counters_field;
 static GTY(()) tree ic_tuple_callee_field;
 
+namespace
+{
+
+struct conds_ctx
+{
+    /* Output arrays allocated by the caller.  */
+    basic_block *blocks;
+    int *sizes;
+
+    /* The size of the blocks buffer.  This is just bug protection,
+       the caller should have allocated enough memory for blocks to never get
+       this many elements.
+       */
+    int maxsize;
+
+    /* Number of expressions found - this value is the number of entries in the
+       gcov output and the parameter to gcov_counter_alloc ().
+       */
+    int exprs;
+
+    /* Bitmap of the processed blocks - bit n set means basic_block->index has
+       been processed as a first-in-expression block.  This effectively stops
+       loop edges from being taken and subgraphs re-processed.
+       */
+    auto_sbitmap seen;
+
+    /* Pre-allocate bitmaps for per-function book keeping.  This is pure
+       instance reuse and the bitmaps carries no data between function calls.
+       */
+    auto_sbitmap expr;
+    auto_sbitmap reachable;
+
+    explicit conds_ctx (unsigned size) noexcept (true) : maxsize (0), exprs (0),
+    seen (size), expr (size), reachable (size)
+    {
+	bitmap_clear (seen);
+    }
+
+    void commit (basic_block top, int nblocks) noexcept (true)
+    {
+	blocks  += nblocks;
+	*sizes  += nblocks;
+	maxsize -= nblocks;
+
+	exprs++;
+	sizes++;
+	*sizes = 0;
+
+	bitmap_set_bit (seen, top->index);
+    }
+};
+
+/* Only instrument terms with fewer than number of bits in a (wide) gcov
+   integer, which is probably 64.  The algorithm itself does not impose this
+   limitation, but it makes for a simpler implementation.
+
+   * Allocating the output data structure (coverage_counter_alloc ()) can
+     assume pairs of gcov_type_unsigned and not use a separate length field.
+   * A pair gcov_type_unsigned can be used as accumulators.
+   * Updating accumulators is can use the bitwise operations |=, &= and not
+     custom operators that work for arbitrary-sized bit-sets.
+
+   Most real-world code should be unaffected by this, but it is possible
+   (especially for generated code) to exceed this limit.
+ */
+#define CONDITIONS_MAX_TERMS (sizeof (gcov_type_unsigned) * BITS_PER_UNIT)
+
+int
+index_of (const_basic_block needle, const const_basic_block *blocks, int size)
+{
+    for (int i = 0; i < size; i++)
+    {
+	if (blocks[i] == needle)
+	    return i;
+    }
+    return -1;
+}
+
+bool
+index_lt (const basic_block x, const basic_block y)
+{
+    return x->index < y->index;
+}
+
+/* Special cases of the single_*_p and single_*_edge functions in basic-block.h
+   that don't consider exception handling or other complex edges.  This helps
+   create a view of the CFG with only normal edges - if a basic block has both
+   an outgoing fallthrough and exceptional edge [1], it should be considered a
+   single-successor.
+
+   [1] if this is not possible, these functions can be removed and replaced by
+       their basic-block.h cousins.
+ */
+bool
+single (const vec<edge, va_gc> *edges)
+{
+    int n = EDGE_COUNT (edges);
+    if (n == 0)
+	return false;
+
+    for (edge e : edges)
+	if (e->flags & EDGE_COMPLEX)
+	    n -= 1;
+
+    return n == 1;
+}
+
+edge
+single_edge (const vec<edge, va_gc> *edges)
+{
+    for (edge e : edges)
+    {
+	if (e->flags & EDGE_COMPLEX)
+	    continue;
+	return e;
+    }
+    gcc_unreachable ();
+}
+
+/* Sometimes, for example with function calls and C++ destructors the CFG gets
+   extra nodes that are essentially single-entry-single-exit in the middle of
+   boolean expressions.  For example:
+
+      x || can_throw (y)
+
+               A
+              /|
+             / |
+            B  |
+            |  |
+            C  |
+           / \ |
+          /   \|
+         F     T
+
+   Without the extra node inserted by the function + exception it becomes a
+   proper 2-term graph, not 2 single-term graphs.
+
+               A
+              /|
+             C |
+            / \|
+           F   T
+
+   contract_edge ignores the series of intermediate nodes and makes a virtual
+   edge A -> C, without having to construct a new simplified CFG explicitly.
+
+   Such an expression cannot correctly be repreted as two 1-term expressions,
+   as it would break the condition masking.
+ */
+edge
+contract_edge (edge e, sbitmap expr)
+{
+    while (true)
+    {
+	basic_block src  = e->src;
+	basic_block dest = e->dest;
+
+	if (!single (dest->succs))
+	    return e;
+	if (!single (dest->preds))
+	    return e;
+	if (!single (src->preds))
+	    return e;
+
+	edge succe = single_edge (dest->succs);
+	if (!single (succe->dest->preds))
+	    return e;
+
+	bitmap_set_bit (expr, dest->index);
+	e = succe;
+    }
+}
+
+edge
+contract_edge_up (edge e, sbitmap expr)
+{
+    while (true)
+    {
+	basic_block src  = e->src;
+	basic_block dest = e->dest;
+
+	if (!single (dest->succs))
+	    return e;
+	if (!single (dest->preds))
+	    return e;
+	if (!single (src->preds))
+	    return e;
+
+	if (expr) bitmap_set_bit (expr, src->index);
+	e = single_pred_edge (src);
+    }
+}
+
+bool
+is_conditional_p (const basic_block b)
+{
+    if (single_succ_p (b))
+        return false;
+
+    unsigned t = 0;
+    unsigned f = 0;
+    for (edge e : b->succs)
+    {
+        t |= (e->flags & EDGE_TRUE_VALUE);
+        f |= (e->flags & EDGE_FALSE_VALUE);
+    }
+    return t && f;
+}
+
+/* The first block in the output will always be the source block of the edge
+   that will apply the masking operation, with the remaining blocks effectively
+   unordered.
+ */
+int
+find_conditions_masked_by (
+    basic_block block,
+    const sbitmap expr,
+    const unsigned *flag,
+    basic_block *out,
+    int maxsize)
+{
+    int n = 0;
+    for (edge e : block->preds)
+    {
+	/* Skip any predecessor not in the expression - there might be such an
+	   edge to the enclosing expression or in the presence of loops, but
+	   masking cannot happen outside the expression itself. */
+	if (!bitmap_bit_p (expr, e->src->index))
+	    continue;
+
+	e = contract_edge_up (e, NULL);
+	if (e->flags & flag[0])
+	    out[n++] = e->src;
+    }
+
+    if (n > 1)
+    {
+	basic_block *top = std::max_element (out, out + n, index_lt);
+	std::iter_swap (top, out);
+    }
+
+    for (int pos = 0; pos < n; pos++)
+    {
+	for (edge e : out[pos]->preds)
+	{
+	    if (!bitmap_bit_p (expr, e->src->index))
+		continue;
+	    if (index_of (e->src, out, n) != -1)
+		continue;
+
+	    e = contract_edge_up (e, NULL);
+	    if (e->flags & flag[1])
+		out[n++] = e->src;
+
+	    gcc_assert (n < maxsize);
+	}
+    }
+
+    return n;
+}
+
+/* Scan the blocks that make up an expression and look for conditions that
+   would mask other conditions.  For a deeper discussion on masking, see
+   Whalen, Heimdahl, De Silva "Efficient Test Coverage Measurement for MC/DC".
+   Masking is best illustrated with an example:
+
+   A || B.  If B is true then A will does not independently affect the decision.
+   In a way, this is "reverse" short circuiting, and always work on the
+   right-hand side of expressions.  * || true is always true and * && false is
+   always false - the left-hand-side does not affect the outcome, and their
+   values should not contribute to modifidied condition/decision coverage.
+
+   A || B interpreted as a decision diagram becomes:
+
+    A
+   t|\f
+    | \
+    |  B
+    |t/ \f
+    |/   \
+    T     F
+
+   The algorithm looks for triangles like ATB.  Masking right-hand sides happen
+   when a block has a pair of incoming edges of the same boolean value, and
+   there is an edge connecting the two predecessors with the *opposite* boolean
+   value.  The masking block is B, and the masked block is A.  In this
+   particular example:
+
+   Masking can affect "outside" its own subexpression; in A || (B && C) if C is
+   false, B is masked.  However, if (B && C) is true, A gets masked.  B && C
+   can be determined from evaluating C since !B would short-circuit, so a true
+   C would mask A.
+
+    A
+   t|\f
+    | \
+    |  \
+    |   \
+    |    B
+    |  t/ \f
+    |  C   |
+    |t/ \f |
+    |/   \ |
+    T     F
+
+   Notice how BFC forms a triangle.  Expressions masked by an edge are
+   determined by:
+   * Go to the predecessor with truth value b (if it exists).
+   * Follow the path of ancestors with taking only !b edges, recording every
+     node from here on.
+
+   For the case where C can mask A, the path goes C [true]-> B -> [false] A, so
+   C [true] masks A.
+
+   The mask is output as a bit-set stored in a gcov_type_unsigned.  The
+   bit-sets are output in pairs, one for each decision (the outcome of the
+   boolean expression, or which arc to take in the CFG).
+ */
+void
+find_subexpr_masks (
+    const basic_block *blocks,
+    int nblocks,
+    gcov_type_unsigned *masks)
+{
+    const unsigned flags[] = {
+	EDGE_TRUE_VALUE,
+	EDGE_FALSE_VALUE,
+	EDGE_TRUE_VALUE,
+    };
+
+    basic_block path[CONDITIONS_MAX_TERMS];
+    auto_sbitmap expr (n_basic_blocks_for_fn (cfun));
+    bitmap_clear (expr);
+    for (int i = 0; i < nblocks; i++)
+	bitmap_set_bit (expr, blocks[i]->index);
+
+    for (int i = 0; i < nblocks; i++)
+    {
+	basic_block block = blocks[i];
+	if (single_pred_p (block))
+	    continue;
+
+	for (int k = 0; k < 2; k++)
+	{
+	    const int n = find_conditions_masked_by
+		(block, expr, flags + k, path, CONDITIONS_MAX_TERMS);
+
+	    if (n < 2)
+		continue;
+
+	    const int m = 2*index_of (path[0], blocks, nblocks) + k;
+	    for (int i = 1; i < n; i++)
+	    {
+		const int index = index_of (path[i], blocks, nblocks);
+		masks[m] |= gcov_type_unsigned (1) << index;
+	    }
+	}
+    }
+}
+
+int
+collect_reachable_conditionals (
+    basic_block pre,
+    basic_block post,
+    basic_block *out,
+    int maxsize,
+    sbitmap expr)
+{
+    gcc_assert (maxsize > 0);
+
+    basic_block loop = pre->loop_father->header;
+    int n = 0;
+    out[n++] = pre;
+    bitmap_set_bit (expr, pre->index);
+
+    for (int pos = 0; pos < n; pos++)
+    {
+	basic_block block = out[pos];
+
+	for (edge e : block->succs)
+	{
+	    basic_block dest = contract_edge (e, expr)->dest;
+
+	    /* Skip loop edges, as they go outside the expression.  */
+	    if (dest == loop)
+		continue;
+	    if (dest == post)
+		continue;
+	    if (!is_conditional_p (dest))
+		continue;
+	    /* Already-seen, don't re-add.  */
+	    if (bitmap_bit_p (expr, dest->index))
+		continue;
+
+	    bitmap_set_bit (expr, dest->index);
+	    out[n++] = dest;
+	    if (n == maxsize)
+		return n;
+	}
+    }
+
+    return n;
+}
+
+int
+collect_neighbors (basic_block *blocks, int nblocks, sbitmap reachable)
+{
+    int n = 0;
+    basic_block *exits = blocks + nblocks;
+    for (int i = 0; i < nblocks; i++)
+    {
+	for (edge e : blocks[i]->succs)
+	{
+	    if (bitmap_bit_p (reachable, e->dest->index))
+		continue;
+
+	    bitmap_set_bit (reachable, e->dest->index);
+	    exits[n++] = e->dest;
+	}
+    }
+
+    return n;
+}
+
+/* Find and isolate the first expression between two dominators.
+
+   Either block of a conditional could have more decisions and loops, so
+   isolate the first decision by set-intersecting all paths from the
+   post-dominator to the entry block.
+
+   The function returns the number of blocks from n that make up the leading
+   expression in prefix order (i.e. the order expected by the instrumenting
+   code).  When this function returns 0 there are no decisions between pre and
+   post, and this segment of the CFG can safely be skipped.
+
+   The post nodes can have predecessors that do not belong to this subgraph,
+   which are skipped.  This is expected, for example when there is a
+   conditional in the else-block of a larger expression:
+
+   if (A)
+   {
+      if (B) {}
+   }
+   else
+   {
+      if (C) {} else {}
+   }
+
+             A
+          t / \ f
+           /   \
+          B     C
+         /\    / \
+        /  \  T   F
+       T    \  \ /
+        \   |   o
+         \  |  /
+          \ | /
+           \|/
+            E
+
+   Processing [B, E) which looks like:
+
+      B
+     /|
+    / |
+   T  |
+    \ /
+     E ----> o // predecessor outside [B, e)
+ */
+
+/* Do a (upwards) search for reachable nodes and mark them in the reachable
+   set, making sure not to take loop edges.  dfs_enumerate_from () won't work as
+   the filter function needs information from the edge. */
+void
+find_reachable (
+    sbitmap reachable,
+    basic_block pre,
+    basic_block post,
+    basic_block *stack)
+{
+    stack[0] = pre;
+    bitmap_set_bit (reachable, pre->index);
+    bitmap_set_bit (reachable, post->index);
+    for (int n = 0; n >= 0; n--)
+    {
+	for (edge e : stack[n]->preds)
+	{
+	    if (bitmap_bit_p (reachable, e->src->index))
+		continue;
+
+	    /* Ignore any loop edges.  */
+	    if (dominated_by_p (CDI_DOMINATORS, e->src, e->dest))
+		continue;
+
+	    basic_block src = contract_edge_up (e, reachable)->src;
+	    bitmap_set_bit (reachable, src->index);
+	    stack[n++] = src;
+	}
+    }
+}
+
+int
+find_first_conditional (conds_ctx &ctx, basic_block pre, basic_block post)
+{
+    basic_block *blocks = ctx.blocks;
+    sbitmap expr = ctx.expr;
+    sbitmap reachable = ctx.reachable;
+    bitmap_clear (expr);
+    bitmap_clear (reachable);
+    blocks[0] = pre;
+
+    /* If there is a direct edge to the post dominator then this cannot only be
+       a single-term conditional *unless* it is a loop (in which case the
+       to-post edge is the loop exit edge).
+     */
+    const bool dowhile = !loop_exits_from_bb_p (pre->loop_father, pre);
+    const bool isloop = bb_loop_header_p (pre) && !dowhile;
+    if (find_edge (pre, post) && !isloop)
+	return 1;
+
+    const int nblocks = collect_reachable_conditionals
+	(pre, post, blocks, ctx.maxsize, expr);
+    if (nblocks == 1)
+	return nblocks;
+
+    bitmap_copy (reachable, expr);
+    const int nexits = collect_neighbors (blocks, nblocks, reachable);
+    if (nexits == 2)
+	return nblocks;
+
+    /* Find reachable nodes from the neighbors.  */
+    basic_block *exits = blocks + nblocks;
+    for (int i = 0; i < nexits; i++)
+    {
+	for (edge e : exits[i]->preds)
+	{
+	    if (!dominated_by_p (CDI_DOMINATORS, e->src, pre))
+		continue;
+
+	    bitmap_clear (reachable);
+	    find_reachable (reachable, e->src, pre, exits + nexits);
+	    for (edge f : exits[i]->preds)
+		bitmap_set_bit (reachable, f->src->index);
+	    bitmap_and (expr, expr, reachable);
+	}
+    }
+
+    int k = 0;
+    for (int i = 0; i < nblocks; i++)
+	if (bitmap_bit_p (expr, blocks[i]->index))
+	    blocks[k++] = blocks[i];
+    return k;
+}
+
+void
+emit_bitwise_op (edge e, tree lhs, tree op1, tree_code op, tree op2,
+		 int atomic = 0)
+{
+    tree tmp;
+    gassign *read;
+    gassign *bitw;
+    gimple  *write;
+
+    /* Insert lhs = op1 <bit-op> op2.
+
+       The operands are assumed to be local accumulators or already SSA'd (and
+       not referencing globals) and don't need atomic reads anymore.
+     */
+    tmp   = make_temp_ssa_name (gcov_type_node, NULL, "__conditions_tmp");
+    read  = gimple_build_assign (tmp, op1);
+    tmp   = make_temp_ssa_name (gcov_type_node, NULL, "__conditions_tmp");
+    bitw  = gimple_build_assign (tmp, op, gimple_assign_lhs (read), op2);
+
+    if (atomic)
+    {
+	tree relaxed = build_int_cst (integer_type_node, MEMMODEL_RELAXED);
+	tree store = builtin_decl_explicit (
+		TYPE_PRECISION (gcov_type_node) > 32
+		? BUILT_IN_ATOMIC_STORE_8
+		: BUILT_IN_ATOMIC_STORE_4);
+
+	// __atomic_store_8 (&lhs, (op1 | op2), __ATOMIC_RELAXED)
+	write = gimple_build_call
+	    (store, 3, build_addr (lhs), gimple_assign_lhs (bitw), relaxed);
+    }
+    else
+    {
+	write = gimple_build_assign (lhs, gimple_assign_lhs (bitw));
+    }
+
+    gsi_insert_on_edge (e, read);
+    gsi_insert_on_edge (e, bitw);
+    gsi_insert_on_edge (e, write);
+}
+
+/* Walk the CFG and collect conditionals.
+
+   1. Collect all nodes reachable from the root node through (contracted) paths
+      of true/false edges.
+   2. Collect the neighbors of the reachable node (set).
+   3. From every node in the neighborhood, walk the up the CFG and mark every
+      reachable node.  Only the nodes reachable from *every* node in the
+      neighborhood are a part of the first expression.
+   4. Record the expression plus the two successors of the last (highest-index)
+      node in the expression, i.e. the last term.
+   5. Repeat using the two successors as new root nodes.
+
+   It is not guaranteed to find nodes in the order of the expression, i.e. it
+   might find (a || b) && c as [a c b], so the output is sorted by
+   basic_block->index.
+
+   Steps 2 and 3 are necessary to distinguish chained conditionals from
+   multi-term conditionals, e.g. to separate
+
+       if (a)
+       {
+           if (b)
+               work ();
+       }
+       if (a && b)
+           work ();
+ */
+void
+collect_conditions (conds_ctx& ctx, basic_block entry, basic_block exit)
+{
+    basic_block pre;
+    basic_block post;
+    for (pre = entry ;; pre = post)
+    {
+	if (pre == exit)
+	    break;
+	if (bitmap_bit_p (ctx.seen, pre->index))
+	    break;
+
+	bitmap_set_bit (ctx.seen, pre->index);
+	post = get_immediate_dominator (CDI_POST_DOMINATORS, pre);
+	if (!is_conditional_p (pre))
+	{
+	    for (edge e : pre->succs)
+		collect_conditions (ctx, e->dest, post);
+	    continue;
+	}
+
+	int nterms = find_first_conditional (ctx, pre, post);
+	std::sort (ctx.blocks, ctx.blocks + nterms, index_lt);
+	basic_block last = ctx.blocks[nterms - 1];
+	if (size_t (nterms) <= CONDITIONS_MAX_TERMS)
+	{
+	    for (edge e : last->succs)
+		if (e->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE))
+		    ctx.blocks[nterms++] = e->dest;
+	    ctx.commit (pre, nterms);
+	}
+	else
+	{
+	    location_t loc = gimple_location (gsi_stmt (gsi_last_bb (pre)));
+	    warning_at
+		(loc, OPT_Wcoverage_too_many_conditions,
+		 "Too many conditions (found %d); giving up coverage", nterms);
+	}
+
+	for (edge e : last->succs)
+	    collect_conditions (ctx, e->dest, post);
+    }
+}
+
+}
+
+/* Condition coverage (MC/DC)
+
+   Algorithm
+   ---------
+   Whalen, Heimdahl, De Silva in "Efficient Test Coverage Measurement for
+   MC/DC" describe an algorithm for modified condition/decision coverage based
+   on AST analysis.  This algorithm analyses the control flow graph, but the
+   accumulation and recording of (partial) results is inspired by their work.
+   The individual phases are described in more detail closer to the
+   implementation.
+
+   The CFG is broken up into segments between dominators.  This isn't strictly
+   necessary, but since boolean expressions cannot cross dominators it makes
+   for a nice way to reduce work.
+
+   The coverage only considers the positions, not the symbols, in a
+   conditional, e.g. !A || (!B && A) is a 3-term conditional even though A
+   appears twice.  Subexpressions have no effect on term ordering:
+   (a && (b || (c && d)) || e) comes out as [a b c d e].
+
+   The output for gcov is a vector of pairs of unsigned integers, interpreted
+   as bit-sets, where the bit index corresponds to the index of the condition
+   in the expression.
+
+   Implementation and interface
+   ----------------------------
+   Two public functions - find_conditions and instrument_decisions.
+
+   find_conditions outputs two arrays, blocks and sizes.  The sizes describes
+   the ranges of blocks that make up every conditional, in a [first, last)
+   fashion, i.e. begin = blocks[sizes[n]], end = blocks[sizes[n+1]] for
+   expression n.  The function returns the number of expressions.
+
+   The coverage is designed to get most of its memory needs met by the caller,
+   and heavily uses the end of the blocks array for buffer.  This is both for
+   performance (no resizes, amortized cost of allocation) and
+   ease-of-implementation.  This makes the caller responsible for allocating
+   large enough arrays.
+
+   blocks:
+    Every permanent conditions add 2 blocks (the true & false dest blocks), and
+    assuming a worst case of one-block-per-expr just storing the output needs
+    3*n_basic_blocks_for_fn ().  Additionally, the searches might need to buffer
+    the full graph between entry and exit [1]. In total that means
+    5*n_basic_blocks_for_fn () should should be plenty, and the savings for
+    reducing this number is probably not worth the risk.
+   sizes:
+    sizes gets one entry per expression plus initial, so
+    1+n_basic_blocks_for_fn () is sufficient.
+
+   instrument_decisions uses the information provided by find_conditions to
+   inject code onto edges in the CFG.  Every instrumented function gets local
+   accumulators zero'd on function entry, which on function exit are flushed to
+   the global accumulators (created by coverage_counter_alloc ()).
+
+   [1] In truth, the set of nodes that could be buffered gets smaller as the
+       algorithm walks the CFG, but assuming just using node-count comes at
+       little run-time cost and is guaranteed to be sufficient.
+ */
+int
+find_conditions (
+    basic_block entry,
+    basic_block exit,
+    basic_block *blocks,
+    int *sizes,
+    int maxsize)
+{
+    record_loop_exits ();
+    bool free_dom = false;
+    bool free_post_dom = false;
+    if (!dom_info_available_p (CDI_POST_DOMINATORS))
+    {
+	calculate_dominance_info (CDI_POST_DOMINATORS);
+	free_post_dom = true;
+    }
+
+    if (!dom_info_available_p (CDI_DOMINATORS))
+    {
+	calculate_dominance_info (CDI_DOMINATORS);
+	free_dom = true;
+    }
+
+    conds_ctx ctx (maxsize);
+    ctx.blocks = blocks;
+    ctx.sizes = sizes + 1;
+    ctx.maxsize = maxsize;
+    sizes[0] = sizes[1] = 0;
+    collect_conditions (ctx, entry, exit);
+
+    /* Partial sum.  */
+    for (int i = 0; i < ctx.exprs; ++i)
+	sizes[i + 1] += sizes[i];
+
+    if (free_post_dom)
+	free_dominance_info (CDI_POST_DOMINATORS);
+    if (free_dom)
+	free_dominance_info (CDI_DOMINATORS);
+
+    return ctx.exprs;
+}
+
+int instrument_decisions (basic_block *blocks, int nblocks, int condno)
+{
+    /* Insert function-local accumulators per decision.  */
+    tree accu[2] = {
+	build_decl
+	    (UNKNOWN_LOCATION, VAR_DECL,
+	     get_identifier ("__conditions_accu_true"), gcov_type_node),
+	build_decl
+	    (UNKNOWN_LOCATION, VAR_DECL,
+	     get_identifier ("__conditions_accu_false"), gcov_type_node),
+    };
+    for (tree acc : accu)
+    {
+	tree zero = build_int_cst (gcov_type_node, 0);
+	for (edge e : ENTRY_BLOCK_PTR_FOR_FN (cfun)->succs)
+	    gsi_insert_on_edge (e, gimple_build_assign (acc, zero));
+    }
+
+    auto_vec<gcov_type_unsigned, 32> masks (nblocks * 2);
+    masks.quick_grow_cleared (nblocks * 2);
+    find_subexpr_masks (blocks, nblocks, masks.address ());
+
+    /* The true/false target blocks are included in the nblocks set, but
+       their outgoing edges should not be instrumented.
+     */
+    gcc_assert (nblocks > 2);
+
+    /* Add instructions for updating the function-local accumulators.  */
+    for (int i = 0; i < nblocks - 2; i++)
+    {
+	for (edge e : blocks[i]->succs)
+	{
+	    if (!(e->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE)))
+		continue;
+
+	    /* accu |= expr[i] */
+	    const int t = !!(e->flags & EDGE_FALSE_VALUE);
+	    tree rhs = build_int_cst (gcov_type_node, 1ULL << i);
+	    emit_bitwise_op (e, accu[t], accu[t], BIT_IOR_EXPR, rhs);
+
+	    if (masks[2*i + t] == 0)
+		continue;
+
+	    /* accu &= mask[i] */
+	    tree mask = build_int_cst (gcov_type_node, ~masks[2*i + t]);
+	    for (int j = 0; j < 2; j++)
+		emit_bitwise_op (e, accu[j], accu[j], BIT_AND_EXPR, mask);
+	}
+    }
+
+    /* Add instructions for updating the global accumulators.  */
+    basic_block exit = EXIT_BLOCK_PTR_FOR_FN (cfun);
+    for (edge e : exit->preds)
+    {
+	for (int k = 0; k < 2; k++)
+	{
+	    tree ref = tree_coverage_counter_ref
+		(GCOV_COUNTER_CONDS, 2*condno + k);
+
+	    tree tmp = make_temp_ssa_name
+		(gcov_type_node, NULL, "__conditions_tmp");
+
+	    const bool atomic = flag_profile_update == PROFILE_UPDATE_ATOMIC;
+	    if (atomic)
+	    {
+		tree relaxed = build_int_cst
+		    (integer_type_node, MEMMODEL_RELAXED);
+		tree atomic_load = builtin_decl_explicit
+		    (TYPE_PRECISION (gcov_type_node) > 32
+		     ? BUILT_IN_ATOMIC_LOAD_8
+		     : BUILT_IN_ATOMIC_LOAD_4);
+
+		tree lhs_type = TREE_TYPE (TREE_TYPE (atomic_load));
+		tree lhs = make_temp_ssa_name
+		    (lhs_type, NULL, "__conditions_load_lhs");
+
+		gcall *load = gimple_build_call
+		    (atomic_load, 2, build_addr (ref), relaxed);
+		gimple_set_lhs (load, lhs);
+		gassign *assign = gimple_build_assign
+		    (tmp, NOP_EXPR, gimple_call_lhs (load));
+
+		gsi_insert_on_edge (e, load);
+		gsi_insert_on_edge (e, assign);
+	    }
+	    else
+	    {
+		gassign *read = gimple_build_assign (tmp, ref);
+		tmp = gimple_assign_lhs (read);
+		gsi_insert_on_edge (e, read);
+	    }
+
+	    ref = unshare_expr (ref);
+	    emit_bitwise_op (e, ref, accu[k], BIT_IOR_EXPR, tmp, atomic);
+	}
+    }
+
+    return nblocks - 2;
+}
+
+#undef CONDITIONS_MAX_TERMS
+
 /* Do initialization work for the edge profiler.  */
 
 /* Add code:
@@ -758,7 +1634,7 @@ tree_profiling (void)
 	  thunk = true;
 	  /* When generate profile, expand thunk to gimple so it can be
 	     instrumented same way as other functions.  */
-	  if (profile_arc_flag)
+	  if (profile_arc_flag || profile_condition_flag)
 	    expand_thunk (node, false, true);
 	  /* Read cgraph profile but keep function as thunk at profile-use
 	     time.  */
@@ -803,7 +1679,7 @@ tree_profiling (void)
   release_profile_file_filtering ();
 
   /* Drop pure/const flags from instrumented functions.  */
-  if (profile_arc_flag || flag_test_coverage)
+  if (profile_arc_flag || profile_condition_flag || flag_test_coverage)
     FOR_EACH_DEFINED_FUNCTION (node)
       {
 	if (!gimple_has_body_p (node->decl)
@@ -897,7 +1773,7 @@ pass_ipa_tree_profile::gate (function *)
      disabled.  */
   return (!in_lto_p && !flag_auto_profile
 	  && (flag_branch_probabilities || flag_test_coverage
-	      || profile_arc_flag));
+	      || profile_arc_flag || profile_condition_flag));
 }
 
 } // anon namespace
