From ad3a68e9b9b01fbdc09854fb7fd655db652c482f Mon Sep 17 00:00:00 2001 From: Keith Seitz Date: Fri, 10 Aug 2018 11:01:24 -0700 Subject: [PATCH] Use policies for code generation 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 | 10 + gdb/compile/compile-c-support.c | 429 ++++++++++++++++++++------------ 2 files changed, 274 insertions(+), 165 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index d6f002d12d..e046b64b5c 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,13 @@ +2018-08-10 Keith Seitz + + * 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 * compile/compile-internal.h (compile_instance::~compile_instance): diff --git a/gdb/compile/compile-c-support.c b/gdb/compile/compile-c-support.c index d77c7d9625..e8993aa5eb 100644 --- a/gdb/compile/compile-c-support.c +++ b/gdb/compile/compile-c-support.c @@ -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: - /* is needed for a memcpy call below. */ - fputs_unfiltered ("#include \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,13 +260,267 @@ 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. */ + +struct c_push_user_expression +{ + void push_user_expression (struct ui_file *buf) + { + fputs_unfiltered ("#pragma GCC user_expression\n", buf); + } +}; + +/* C-language policy to emit a pop user expression pragma into BUF. + For C, this is a nop. */ + +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. */ + +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: + /* is needed for a memcpy call below. */ + fputs_unfiltered ("#include \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.")); + } + } +}; + +/* 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. */ + +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; + + case COMPILE_I_RAW_SCOPE: + break; + + default: + gdb_assert_not_reached (_("Unknown compiler scope reached.")); + } + } +}; + +/* C-language policy to emit the user code snippet INPUT into BUF based on the + scope TYPE. */ + +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); + } +}; + +/* A host class representing a compile program. + + 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. + + AddInputPolicy adds the actual user code. */ + +template +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 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); + } + + 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 c_compile_program; + +/* The la_compute_program method for C. */ std::string c_compute_program (compile_instance *inst, @@ -340,98 +529,8 @@ c_compute_program (compile_instance *inst, const struct block *expr_block, CORE_ADDR expr_pc) { - compile_c_instance *context - = static_cast (inst); + compile_c_instance *c_inst = static_cast (inst); + c_compile_program program (c_inst, gdbarch); - string_file buf; - string_file var_stream; - - write_macro_definitions (expr_block, expr_pc, &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; - - /* 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 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); - - gdb_assert (mode != NULL); - buf.printf ("typedef int" - " __attribute__ ((__mode__(__%s__)))" - " __gdb_int_%s;\n", - mode, mode); - } - - generate_register_struct (&buf, gdbarch, registers_used.get ()); - } - - add_code_header (inst->scope (), &buf); - - 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"); - } - - /* 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"); - - buf.puts ("#line 1 \"gdb command line\"\n"); - - 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; - } - - buf.puts ("\n"); - - /* For larger user expressions the automatic semicolons may be - confusing. */ - if (strchr (input, '\n') == NULL) - buf.puts (";\n"); - - if (inst->scope () != COMPILE_I_RAW_SCOPE) - buf.puts ("}\n"); - - add_code_footer (inst->scope (), &buf); - return std::move (buf.string ()); + return program.compute (input, expr_block, expr_pc); }