md_reader: Support (symbol_ref { code }) in insn attributes.

When md_reader is reading insn attributes from md files, it accepts
expressions like  (symbol_ref { code })  and  (symbol_ref "{ code }")
as valid expressions.  However, when generating insn-attrtab.cc, it
would print the {} block as an expression, which is not valid C++ syntax.
   The conclusion is that to date, no backend is using that syntax, and
hence we can assign a new semantic to such {} blocks:

|  The value of a (symbol_ref { code }) expression is the value returned
|  by the {} code block (when taken as the body of a C++ function).

No changes to the md scanner are required; all what's needed is to print
the code from {} blocks in such a way that it works as the body of a C++
function / lambda expression.  To that end, md_reader::fprint_c_condition()
now issues code as follows to insn-attrtab.cc:

   return [&]() -> attr_desc.cxx_type { code } ();

where the capture provides code access to local variables like "insn".
Without this patch, the issued code was basically  return { code };

gcc/
        * doc/md.texi (Attribute Expressions) <symbol_ref>: Document it.
        * genattrtab.cc (write_attr_value) [SYMBOL_REF]: Pass down
        attr->cxx_type to rtx_reader_ptr->fprint_c_condition().
        (write_test_expr) [MATCH_TEST]: Pass down "bool" as cxx_type
        to rtx_reader_ptr->fprint_c_condition().
        * genconditions.cc (write_one_condition): Same.
        * genrecog.cc (print_test) [rtx_test::C_TEST]: Same.
        * read-md.cc (md_reader::fprint_c_condition): Add cxx_type
        argument and pass it down to recursive invocations.
        When cond starts with a '{', then use cond as the body of a C++
        lambda expression that's evaluated in place.
        (md_reader::print_c_condition): Add cxx_type argument.
        * read-md.h (md_reader::fprint_c_condition)
        (md_reader::print_c_condition): Add cxx_type argument.

diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi
index a9259112251..4d18385a1d5 100644
--- a/gcc/doc/md.texi
+++ b/gcc/doc/md.texi
@@ -10277,6 +10277,57 @@ so there is no need to explicitly convert the expression into a boolean
 (match_test "(x & 2) != 0")
 @end smallexample
 
+@cindex @code{symbol_ref} and attributes
+@item (symbol_ref "@var{quoted-c-expr}")
+
+Specifies the value of the attribute sub-expression as a C++ expression,
+where the surrounding quotes are not part of the expression.
+Similar to @code{match_test}, variables @var{insn}, @var{operands[]}
+and @var{which_alternative} are available.  Moreover, code and mode
+attributes can be used to compose the resulting C++ expression.
+Here is an example for the case where the according insn
+has exactly one mode iterator:
+
+@smallexample
+(set (attr "length")
+     (symbol_ref "1 + GET_MODE_SIZE (<MODE>mode)"))
+@end smallexample
+
+See @ref{Mode Iterators} and @ref{Code Iterators}.
+
+@item  (symbol_ref "@{ @var{quoted-c-code} @}")
+@itemx (symbol_ref @{ @var{c-code} @})
+
+The value of this subexpression is determined by running the specfied block
+of C++ code which returns the desired value.  The braces are part of the
+code, whereas the quotes in the quoted form are not.
+
+This variant of @code{symbol_ref} allows for more complex code than
+just a single C++ expression, like for example:
+
+@smallexample
+(set (attr "length")
+     (symbol_ref
+      @{
+        int len;
+        some_function (insn, <CODE>, <MODE>mode, & len);
+        return len;
+      @}))
+@end smallexample
+
+for an insn that has one code iterator and one mode iterator.
+Again, variables @var{insn}, @var{operands[]} and @var{which_alternative}
+can be used.  The unquoted form only supports a subset of C++,
+for example no C comments are supported, and strings that contain
+characters like @samp{@}} are problematic and may need to be escaped
+as @samp{\@}}.
+
+The return type is @code{int} for the @var{length} attribute, and
+@code{enum attr_@var{name}} for an insn attribute named @var{name}.
+The types and available enum values can be looked up in
+@file{$builddir/gcc/insn-attr-common.h}.
+
+
 @cindex @code{le} and attributes
 @cindex @code{leu} and attributes
 @cindex @code{lt} and attributes
diff --git a/gcc/genattrtab.cc b/gcc/genattrtab.cc
index 2a51549ddd4..63ddef84469 100644
--- a/gcc/genattrtab.cc
+++ b/gcc/genattrtab.cc
@@ -3707,7 +3707,7 @@ write_test_expr (FILE *outf, rtx exp, unsigned int attrs_cached, int flags,
       break;
 
     case MATCH_TEST:
-      rtx_reader_ptr->fprint_c_condition (outf, XSTR (exp, 0));
+      rtx_reader_ptr->fprint_c_condition (outf, XSTR (exp, 0), "bool");
       if (flags & FLG_BITWISE)
 	fprintf (outf, " != 0");
       break;
@@ -4385,7 +4385,8 @@ write_attr_value (FILE *outf, class attr_desc *attr, rtx value)
       break;
 
     case SYMBOL_REF:
-      rtx_reader_ptr->fprint_c_condition (outf, XSTR (value, 0));
+      rtx_reader_ptr->fprint_c_condition (outf, XSTR (value, 0),
+					  attr->cxx_type);
       break;
 
     case ATTR:
diff --git a/gcc/genconditions.cc b/gcc/genconditions.cc
index 13963dc3ff4..8a55f0f1033 100644
--- a/gcc/genconditions.cc
+++ b/gcc/genconditions.cc
@@ -141,9 +141,9 @@ write_one_condition (void **slot, void * ARG_UNUSED (dummy))
     }
 
   fputs ("\",\n    __builtin_constant_p ", stdout);
-  rtx_reader_ptr->print_c_condition (test->expr);
+  rtx_reader_ptr->print_c_condition (test->expr, "$bool");
   fputs ("\n    ? (int) ", stdout);
-  rtx_reader_ptr->print_c_condition (test->expr);
+  rtx_reader_ptr->print_c_condition (test->expr, "$bool");
   fputs ("\n    : -1 },\n", stdout);
   return 1;
 }
diff --git a/gcc/genrecog.cc b/gcc/genrecog.cc
index ba09ec3b600..c597958a487 100644
--- a/gcc/genrecog.cc
+++ b/gcc/genrecog.cc
@@ -4762,7 +4762,7 @@ print_test (output_state *os, const rtx_test &test, bool is_param,
       gcc_assert (!is_param && value == 1);
       if (invert_p)
 	printf ("!");
-      rtx_reader_ptr->print_c_condition (test.u.string);
+      rtx_reader_ptr->print_c_condition (test.u.string, "bool");
       break;
 
     case rtx_test::ACCEPT:
diff --git a/gcc/read-md.cc b/gcc/read-md.cc
index 93d1ea43781..2e04fffc45d 100644
--- a/gcc/read-md.cc
+++ b/gcc/read-md.cc
@@ -170,31 +170,54 @@ md_reader::join_c_conditions (const char *cond1, const char *cond2)
    directive for COND if its original file position is known.  */
 
 void
-md_reader::fprint_c_condition (FILE *outf, const char *cond)
+md_reader::fprint_c_condition (FILE *outf, const char *cond,
+			       const char *cxx_type)
 {
   const char **halves = (const char **) htab_find (m_joined_conditions, &cond);
   if (halves != 0)
     {
       fprintf (outf, "(");
-      fprint_c_condition (outf, halves[1]);
+      fprint_c_condition (outf, halves[1], cxx_type);
       fprintf (outf, " && ");
-      fprint_c_condition (outf, halves[2]);
+      fprint_c_condition (outf, halves[2], cxx_type);
       fprintf (outf, ")");
     }
   else
     {
       fputc ('\n', outf);
       fprint_md_ptr_loc (outf, cond);
-      fprintf (outf, "(%s)", cond);
+      if (cond[0] == '{')
+	{
+	  /* Resulting C++ code is like "( [&]() -> <cxx_type> <cond> () )"
+	     where COND is actually the body of a function, including the
+	     outer {}'s.  Print this as a lambda that's evaluated in place.
+	     The capture-all is required to have access to variables
+	     like "insn".  Objects like "operands[]" and "which_alternative"
+	     are accessible since they are global or captured.
+	     When the expression is not evaluated locally (not in a function),
+	     then CXX_TYPE starts with a '$' to indicate that no capture
+	     should be performed.  */
+	  bool no_capture = cxx_type && cxx_type[0] == '$';
+	  if (cxx_type && no_capture)
+	    fprintf (outf, "( []() -> %s %s () )", 1 + cxx_type, cond);
+	  else if (cxx_type)
+	    fprintf (outf, "( [&]() -> %s %s () )", cxx_type, cond);
+	  else
+	    fprintf (outf, "( [&]() /* unknown return type */ %s () )", cond);
+	}
+      else
+	{
+	  fprintf (outf, "(%s)", cond);
+	}
     }
 }
 
 /* Special fprint_c_condition for writing to STDOUT.  */
 
 void
-md_reader::print_c_condition (const char *cond)
+md_reader::print_c_condition (const char *cond, const char *cxx_type)
 {
-  fprint_c_condition (stdout, cond);
+  fprint_c_condition (stdout, cond, cxx_type);
 }
 
 /* A vfprintf-like function for reporting an error against line LINENO
diff --git a/gcc/read-md.h b/gcc/read-md.h
index 9703551a8fd..b6ee68647ec 100644
--- a/gcc/read-md.h
+++ b/gcc/read-md.h
@@ -204,8 +204,9 @@ class md_reader
   void handle_enum (file_location loc, bool md_p);
 
   const char *join_c_conditions (const char *cond1, const char *cond2);
-  void fprint_c_condition (FILE *outf, const char *cond);
-  void print_c_condition (const char *cond);
+  void fprint_c_condition (FILE *outf, const char *cond,
+			   const char *cxx_type = nullptr);
+  void print_c_condition (const char *cond, const char *cxx_type = nullptr);
 
   /* Defined in read-rtl.cc.  */
   const char *apply_iterator_to_string (const char *string);
