Use policies for code generation
authorKeith Seitz <keiths@redhat.com>
Fri, 10 Aug 2018 18:01:24 +0000 (11:01 -0700)
committerKeith Seitz <keiths@redhat.com>
Fri, 10 Aug 2018 18:14:25 +0000 (11:14 -0700)
This patch changes code generation procedures add_code_header,
add_code_footer, and several other language-specific code generation
functions into policies.

gdb/ChangeLog:
        * compile/compile-c-support.c (add_code_header, add_code_footer):
        Move into policy class.
        (c_push_user_expression, pop_user_expression_nop)
        (c_add_code_header, c_add_code_footer, c_add_input): New policy class.
        (compile_program): New host class.
        (c_compile_program): New typedef.
        (c_compute_porgram): Use c_compile_program.

gdb/ChangeLog
gdb/compile/compile-c-support.c

index d6f002d12da835017d467f6330fab60dd1c04a23..e046b64b5c60f3d706bdf88a54130713f666c69c 100644 (file)
@@ -1,3 +1,13 @@
+2018-08-10  Keith Seitz  <keiths@redhat.com>
+
+       * compile/compile-c-support.c (add_code_header, add_code_footer):
+       Move into policy class.
+       (c_push_user_expression, pop_user_expression_nop)
+       (c_add_code_header, c_add_code_footer, c_add_input): New policy class.
+       (compile_program): New host class.
+       (c_compile_program): New typedef.
+       (c_compute_porgram): Use c_compile_program.
+
 2018-08-10  Keith Seitz  <keiths@redhat.com>
 
        * compile/compile-internal.h (compile_instance::~compile_instance):
index d77c7d962577c3f831f35965f4293ff0b611be13..e8993aa5eb2d17454f9e0f8201040959aae178d0 100644 (file)
@@ -180,71 +180,6 @@ write_macro_definitions (const struct block *block, CORE_ADDR pc,
     }
 }
 
-/* Helper function to construct a header scope for a block of code.
-   Takes a scope argument which selects the correct header to
-   insert into BUF.  */
-
-static void
-add_code_header (enum compile_i_scope_types type, struct ui_file *buf)
-{
-  switch (type)
-    {
-    case COMPILE_I_SIMPLE_SCOPE:
-      fputs_unfiltered ("void "
-                       GCC_FE_WRAPPER_FUNCTION
-                       " (struct "
-                       COMPILE_I_SIMPLE_REGISTER_STRUCT_TAG
-                       " *"
-                       COMPILE_I_SIMPLE_REGISTER_ARG_NAME
-                       ") {\n",
-                       buf);
-      break;
-    case COMPILE_I_PRINT_ADDRESS_SCOPE:
-    case COMPILE_I_PRINT_VALUE_SCOPE:
-      /* <string.h> is needed for a memcpy call below.  */
-      fputs_unfiltered ("#include <string.h>\n"
-                       "void "
-                       GCC_FE_WRAPPER_FUNCTION
-                       " (struct "
-                       COMPILE_I_SIMPLE_REGISTER_STRUCT_TAG
-                       " *"
-                       COMPILE_I_SIMPLE_REGISTER_ARG_NAME
-                       ", "
-                       COMPILE_I_PRINT_OUT_ARG_TYPE
-                       " "
-                       COMPILE_I_PRINT_OUT_ARG
-                       ") {\n",
-                       buf);
-      break;
-
-    case COMPILE_I_RAW_SCOPE:
-      break;
-    default:
-      gdb_assert_not_reached (_("Unknown compiler scope reached."));
-    }
-}
-
-/* Helper function to construct a footer scope for a block of code.
-   Takes a scope argument which selects the correct footer to
-   insert into BUF.  */
-
-static void
-add_code_footer (enum compile_i_scope_types type, struct ui_file *buf)
-{
-  switch (type)
-    {
-    case COMPILE_I_SIMPLE_SCOPE:
-    case COMPILE_I_PRINT_ADDRESS_SCOPE:
-    case COMPILE_I_PRINT_VALUE_SCOPE:
-      fputs_unfiltered ("}\n", buf);
-      break;
-    case COMPILE_I_RAW_SCOPE:
-      break;
-    default:
-      gdb_assert_not_reached (_("Unknown compiler scope reached."));
-    }
-}
-
 /* Generate a structure holding all the registers used by the function
    we're generating.  */
 
@@ -325,113 +260,277 @@ generate_register_struct (struct ui_file *stream, struct gdbarch *gdbarch,
   fputs_unfiltered ("};\n\n", stream);
 }
 
-/* Take the source code provided by the user with the 'compile'
-   command, and compute the additional wrapping, macro, variable and
-   register operations needed.  INPUT is the source code derived from
-   the 'compile' command, GDBARCH is the architecture to use when
-   computing above, EXPR_BLOCK denotes the block relevant contextually
-   to the inferior when the expression was created, and EXPR_PC
-   indicates the value of $PC.  */
+/* C-language policy to emit a push user expression pragma into BUF.  */
 
-std::string
-c_compute_program (compile_instance *inst,
-                  const char *input,
-                  struct gdbarch *gdbarch,
-                  const struct block *expr_block,
-                  CORE_ADDR expr_pc)
+struct c_push_user_expression
 {
-  compile_c_instance *context
-    = static_cast<compile_c_instance *> (inst);
+  void push_user_expression (struct ui_file *buf)
+  {
+    fputs_unfiltered ("#pragma GCC user_expression\n", buf);
+  }
+};
 
-  string_file buf;
-  string_file var_stream;
+/* C-language policy to emit a pop user expression pragma into BUF.
+   For C, this is a nop.  */
 
-  write_macro_definitions (expr_block, expr_pc, &buf);
+struct pop_user_expression_nop
+{
+  void pop_user_expression (struct ui_file *buf)
+  {
+    /* Nothing to do.  */
+  }
+};
+
+/* C-language policy to construct a code header for a block of code.
+   Takes a scope TYPE argument which selects the correct header to
+   insert into BUF.  */
 
-  /* Do not generate local variable information for "raw"
-     compilations.  In this case we aren't emitting our own function
-     and the user's code may only refer to globals.  */
-  if (inst->scope () != COMPILE_I_RAW_SCOPE)
-    {
-      int i;
+struct c_add_code_header
+{
+  void add_code_header (enum compile_i_scope_types type, struct ui_file *buf)
+  {
+    switch (type)
+      {
+      case COMPILE_I_SIMPLE_SCOPE:
+       fputs_unfiltered ("void "
+                         GCC_FE_WRAPPER_FUNCTION
+                         " (struct "
+                         COMPILE_I_SIMPLE_REGISTER_STRUCT_TAG
+                         " *"
+                         COMPILE_I_SIMPLE_REGISTER_ARG_NAME
+                         ") {\n",
+                         buf);
+       break;
+
+      case COMPILE_I_PRINT_ADDRESS_SCOPE:
+      case COMPILE_I_PRINT_VALUE_SCOPE:
+       /* <string.h> is needed for a memcpy call below.  */
+       fputs_unfiltered ("#include <string.h>\n"
+                         "void "
+                         GCC_FE_WRAPPER_FUNCTION
+                         " (struct "
+                         COMPILE_I_SIMPLE_REGISTER_STRUCT_TAG
+                         " *"
+                         COMPILE_I_SIMPLE_REGISTER_ARG_NAME
+                         ", "
+                         COMPILE_I_PRINT_OUT_ARG_TYPE
+                         " "
+                         COMPILE_I_PRINT_OUT_ARG
+                         ") {\n",
+                         buf);
+       break;
+
+      case COMPILE_I_RAW_SCOPE:
+       break;
+
+      default:
+       gdb_assert_not_reached (_("Unknown compiler scope reached."));
+      }
+  }
+};
 
-      /* Generate the code to compute variable locations, but do it
-        before generating the function header, so we can define the
-        register struct before the function body.  This requires a
-        temporary stream.  */
-      gdb::unique_xmalloc_ptr<unsigned char> registers_used
-       = generate_c_for_variable_locations (context, var_stream, gdbarch,
-                                            expr_block, expr_pc);
-
-      buf.puts ("typedef unsigned int"
-               " __attribute__ ((__mode__(__pointer__)))"
-               " __gdb_uintptr;\n");
-      buf.puts ("typedef int"
-               " __attribute__ ((__mode__(__pointer__)))"
-               " __gdb_intptr;\n");
-
-      /* Iterate all log2 sizes in bytes supported by c_get_mode_for_size.  */
-      for (i = 0; i < 4; ++i)
-       {
-         const char *mode = c_get_mode_for_size (1 << i);
+/* C-language policy to construct a code footer for a block of code.
+   Takes a scope TYPE which selects the correct footer to insert into BUF.  */
 
-         gdb_assert (mode != NULL);
-         buf.printf ("typedef int"
-                     " __attribute__ ((__mode__(__%s__)))"
-                     " __gdb_int_%s;\n",
-                     mode, mode);
-       }
+struct c_add_code_footer
+{
+  void add_code_footer (enum compile_i_scope_types type, struct ui_file *buf)
+  {
+    switch (type)
+      {
+      case COMPILE_I_SIMPLE_SCOPE:
+      case COMPILE_I_PRINT_ADDRESS_SCOPE:
+      case COMPILE_I_PRINT_VALUE_SCOPE:
+       fputs_unfiltered ("}\n", buf);
+       break;
 
-      generate_register_struct (&buf, gdbarch, registers_used.get ());
-    }
+      case COMPILE_I_RAW_SCOPE:
+       break;
 
-  add_code_header (inst->scope (), &buf);
+      default:
+       gdb_assert_not_reached (_("Unknown compiler scope reached."));
+      }
+  }
+};
 
-  if (inst->scope () == COMPILE_I_SIMPLE_SCOPE
-      || inst->scope () == COMPILE_I_PRINT_ADDRESS_SCOPE
-      || inst->scope () == COMPILE_I_PRINT_VALUE_SCOPE)
-    {
-      buf.write (var_stream.c_str (), var_stream.size ());
-      buf.puts ("#pragma GCC user_expression\n");
-    }
+/* C-language policy to emit the user code snippet INPUT into BUF based on the
+   scope TYPE.  */
 
-  /* The user expression has to be in its own scope, so that "extern"
-     works properly.  Otherwise gcc thinks that the "extern"
-     declaration is in the same scope as the declaration provided by
-     gdb.  */
-  if (inst->scope () != COMPILE_I_RAW_SCOPE)
-    buf.puts ("{\n");
+struct c_add_input
+{
+  void add_input (enum compile_i_scope_types type, const char *input,
+                 struct ui_file *buf)
+  {
+    switch (type)
+      {
+      case COMPILE_I_PRINT_ADDRESS_SCOPE:
+      case COMPILE_I_PRINT_VALUE_SCOPE:
+       fprintf_unfiltered (buf,
+                           "__auto_type " COMPILE_I_EXPR_VAL " = %s;\n"
+                           "typeof (%s) *" COMPILE_I_EXPR_PTR_TYPE ";\n"
+                           "memcpy (" COMPILE_I_PRINT_OUT_ARG ", %s"
+                           COMPILE_I_EXPR_VAL ",\n"
+                           "sizeof (*" COMPILE_I_EXPR_PTR_TYPE "));\n"
+                           , input, input,
+                           (type == COMPILE_I_PRINT_ADDRESS_SCOPE
+                            ? "&" : ""));
+       break;
+
+      default:
+       fputs_unfiltered (input, buf);
+       break;
+      }
+    fputs_unfiltered ("\n", buf);
+  }
+};
 
-  buf.puts ("#line 1 \"gdb command line\"\n");
+/* A host class representing a compile program.
 
-  switch (inst->scope ())
-    {
-    case COMPILE_I_PRINT_ADDRESS_SCOPE:
-    case COMPILE_I_PRINT_VALUE_SCOPE:
-      buf.printf (
-"__auto_type " COMPILE_I_EXPR_VAL " = %s;\n"
-"typeof (%s) *" COMPILE_I_EXPR_PTR_TYPE ";\n"
-"memcpy (" COMPILE_I_PRINT_OUT_ARG ", %s" COMPILE_I_EXPR_VAL ",\n"
-        "sizeof (*" COMPILE_I_EXPR_PTR_TYPE "));\n"
-                         , input, input,
-                         (inst->scope () == COMPILE_I_PRINT_ADDRESS_SCOPE
-                          ? "&" : ""));
-      break;
-    default:
-      buf.puts (input);
-      break;
-    }
+   CompileInstanceType is the type of the compile_instance for the
+   language.
+
+   PushUserExpressionPolicy and PopUserExpressionPolicy are used to
+   push and pop user expression pragmas to the compile plug-in.
+
+   AddCodeHeaderPolicy and AddCodeFooterPolicy are used to add the appropriate
+   code header and footer, respectively.
 
-  buf.puts ("\n");
+   AddInputPolicy adds the actual user code.  */
 
-  /* For larger user expressions the automatic semicolons may be
-     confusing.  */
-  if (strchr (input, '\n') == NULL)
-    buf.puts (";\n");
+template <class CompileInstanceType, class PushUserExpressionPolicy,
+         class PopUserExpressionPolicy, class AddCodeHeaderPolicy,
+         class AddCodeFooterPolicy, class AddInputPolicy>
+class compile_program
+  : private PushUserExpressionPolicy, private PopUserExpressionPolicy,
+    private AddCodeHeaderPolicy, private AddCodeFooterPolicy,
+    private AddInputPolicy
+{
+public:
+
+  /* Construct a compile_program using the compiler instance INST
+     using the architecture given by GDBARCH.  */
+  compile_program (CompileInstanceType *inst, struct gdbarch *gdbarch)
+    : m_instance (inst), m_arch (gdbarch)
+  {
+  }
+
+  /* Take the source code provided by the user with the 'compile'
+     command and compute the additional wrapping, macro, variable and
+     register operations needed.  INPUT is the source code derived from
+     the 'compile' command, EXPR_BLOCK denotes the block relevant contextually
+     to the inferior when the expression was created, and EXPR_PC
+     indicates the value of $PC.
+
+     Returns the text of the program to compile.  */
+  std::string compute (const char *input, const struct block *expr_block,
+                      CORE_ADDR expr_pc)
+  {
+    string_file var_stream;
+    string_file buf;
+
+    /* Do not generate local variable information for "raw"
+       compilations.  In this case we aren't emitting our own function
+       and the user's code may only refer to globals.  */
+    if (m_instance->scope () != COMPILE_I_RAW_SCOPE)
+      {
+       /* Generate the code to compute variable locations, but do it
+          before generating the function header, so we can define the
+          register struct before the function body.  This requires a
+          temporary stream.  */
+       gdb::unique_xmalloc_ptr<unsigned char> registers_used
+         = generate_c_for_variable_locations (m_instance, var_stream, m_arch,
+                                              expr_block, expr_pc);
+
+       buf.puts ("typedef unsigned int"
+                 " __attribute__ ((__mode__(__pointer__)))"
+                 " __gdb_uintptr;\n");
+       buf.puts ("typedef int"
+                 " __attribute__ ((__mode__(__pointer__)))"
+                 " __gdb_intptr;\n");
+
+       /* Iterate all log2 sizes in bytes supported by c_get_mode_for_size.  */
+       for (int i = 0; i < 4; ++i)
+         {
+           const char *mode = c_get_mode_for_size (1 << i);
+
+           gdb_assert (mode != NULL);
+           buf.printf ("typedef int"
+                       " __attribute__ ((__mode__(__%s__)))"
+                       " __gdb_int_%s;\n",
+                       mode, mode);
+         }
+
+       generate_register_struct (&buf, m_arch, registers_used.get ());
+      }
+
+    AddCodeHeaderPolicy::add_code_header (m_instance->scope (), &buf);
+
+    if (m_instance->scope () == COMPILE_I_SIMPLE_SCOPE
+       || m_instance->scope () == COMPILE_I_PRINT_ADDRESS_SCOPE
+       || m_instance->scope () == COMPILE_I_PRINT_VALUE_SCOPE)
+      {
+       buf.write (var_stream.c_str (), var_stream.size ());
+       PushUserExpressionPolicy::push_user_expression (&buf);
+      }
 
-  if (inst->scope () != COMPILE_I_RAW_SCOPE)
-    buf.puts ("}\n");
+    write_macro_definitions (expr_block, expr_pc, &buf);
+
+    /* The user expression has to be in its own scope, so that "extern"
+       works properly.  Otherwise gcc thinks that the "extern"
+       declaration is in the same scope as the declaration provided by
+       gdb.  */
+    if (m_instance->scope () != COMPILE_I_RAW_SCOPE)
+      buf.puts ("{\n");
+
+    buf.puts ("#line 1 \"gdb command line\"\n");
+
+    AddInputPolicy::add_input (m_instance->scope (), input, &buf);
+
+    /* For larger user expressions the automatic semicolons may be
+       confusing.  */
+    if (strchr (input, '\n') == NULL)
+      buf.puts (";\n");
+
+    if (m_instance->scope () != COMPILE_I_RAW_SCOPE)
+      buf.puts ("}\n");
+
+    if (m_instance->scope () == COMPILE_I_SIMPLE_SCOPE
+       || m_instance->scope () == COMPILE_I_PRINT_ADDRESS_SCOPE
+       || m_instance->scope () == COMPILE_I_PRINT_VALUE_SCOPE)
+      PopUserExpressionPolicy::pop_user_expression (&buf);
+
+    AddCodeFooterPolicy::add_code_footer (m_instance->scope (), &buf);
+    return buf.string ();
+  }
+
+private:
+
+  /* The compile instance to be used for compilation and
+     type-conversion.  */
+  CompileInstanceType *m_instance;
+
+  /* The architecture to be used.  */
+  struct gdbarch *m_arch;
+};
+
+/* Type used for C program computations.  */
+
+typedef compile_program<compile_c_instance,
+                       c_push_user_expression, pop_user_expression_nop,
+                       c_add_code_header, c_add_code_footer,
+                       c_add_input> c_compile_program;
+
+/* The la_compute_program method for C.  */
+
+std::string
+c_compute_program (compile_instance *inst,
+                  const char *input,
+                  struct gdbarch *gdbarch,
+                  const struct block *expr_block,
+                  CORE_ADDR expr_pc)
+{
+  compile_c_instance *c_inst = static_cast<compile_c_instance *> (inst);
+  c_compile_program program (c_inst, gdbarch);
 
-  add_code_footer (inst->scope (), &buf);
-  return std::move (buf.string ());
+  return program.compute (input, expr_block, expr_pc);
 }