diff options
author | Tom Tromey <tom@tromey.com> | 2021-03-08 07:27:57 -0700 |
---|---|---|
committer | Tom Tromey <tom@tromey.com> | 2021-03-08 07:28:13 -0700 |
commit | e2803273a078950d0895de245cdc5375f362a8c5 (patch) | |
tree | 5831732b28dc39d8afa6d009089f501fe6b0c57c | |
parent | e18c58f290609dcfe8d7df450bb88b1adf44337a (diff) | |
download | binutils-e2803273a078950d0895de245cdc5375f362a8c5.zip binutils-e2803273a078950d0895de245cdc5375f362a8c5.tar.gz binutils-e2803273a078950d0895de245cdc5375f362a8c5.tar.bz2 |
Introduce class operation
This patch introduces class operation, the new base class for all
expression operations.
In the new approach, an operation is simply a class that presents a
certain interface. Operations own their operands, and management is
done via unique_ptr.
The operation interface is largely ad hoc, based on the evolution of
expression handling in GDB. Parts (for example,
evaluate_with_coercion) are probably redundant; however I took this
approach to try to avoid mixing different kinds of refactorings.
In some specific situations, rather than add a generic method across
the entire operation class hierarchy, I chose instead to use
dynamic_cast and specialized methods on certain concrete subclasses.
This will appear in some subsequent patches.
One goal of this work is to avoid the kinds of easy-to-make errors
that affected the old implementation. To this end, some helper
subclasses are also added here. These helpers automate the
implementation of the 'dump', 'uses_objfile', and 'constant_p'
methods. Nearly every concrete operation that is subsequently added
will use these facilities. (Note that the 'dump' implementation is
only outlined here, the body appears in the next patch.)
gdb/ChangeLog
2021-03-08 Tom Tromey <tom@tromey.com>
* expression.h (expr::operation): New class.
(expr::make_operation): New function.
(expr::operation_up): New typedef.
* expop.h: New file.
* eval.c (operation::evaluate_for_cast)
(operation::evaluate_for_address, operation::evaluate_for_sizeof):
New methods.
* ax-gdb.c (operation::generate_ax): New method.
-rw-r--r-- | gdb/ChangeLog | 11 | ||||
-rw-r--r-- | gdb/ax-gdb.c | 26 | ||||
-rw-r--r-- | gdb/eval.c | 36 | ||||
-rw-r--r-- | gdb/expop.h | 315 | ||||
-rw-r--r-- | gdb/expression.h | 99 |
5 files changed, 487 insertions, 0 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 70a93ed..abf16c0 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,16 @@ 2021-03-08 Tom Tromey <tom@tromey.com> + * expression.h (expr::operation): New class. + (expr::make_operation): New function. + (expr::operation_up): New typedef. + * expop.h: New file. + * eval.c (operation::evaluate_for_cast) + (operation::evaluate_for_address, operation::evaluate_for_sizeof): + New methods. + * ax-gdb.c (operation::generate_ax): New method. + +2021-03-08 Tom Tromey <tom@tromey.com> + * ax-gdb.c (gen_expr_binop_rest): Remove "pc" parameter. (gen_expr_binop_rest): New overload. diff --git a/gdb/ax-gdb.c b/gdb/ax-gdb.c index e18e968..728b21d 100644 --- a/gdb/ax-gdb.c +++ b/gdb/ax-gdb.c @@ -2270,6 +2270,32 @@ gen_expr (struct expression *exp, union exp_element **pc, } } +namespace expr +{ + +void +operation::generate_ax (struct expression *exp, + struct agent_expr *ax, + struct axs_value *value, + struct type *cast_type) +{ + if (constant_p ()) + { + struct value *v = evaluate (nullptr, exp, EVAL_AVOID_SIDE_EFFECTS); + ax_const_l (ax, value_as_long (v)); + value->kind = axs_rvalue; + value->type = check_typedef (value_type (v)); + } + else + { + do_generate_ax (exp, ax, value, cast_type); + if (cast_type != nullptr) + gen_cast (ax, value, cast_type); + } +} + +} + /* This handles the middle-to-right-side of code generation for binary expressions, which is shared between regular binary operations and assign-modify (+= and friends) expressions. */ @@ -40,6 +40,7 @@ #include "objfiles.h" #include "typeprint.h" #include <ctype.h> +#include "expop.h" /* Prototypes for local functions. */ @@ -3267,6 +3268,29 @@ evaluate_subexp_for_address (struct expression *exp, int *pos, } } +namespace expr +{ + +value * +operation::evaluate_for_cast (struct type *expect_type, + struct expression *exp, + enum noside noside) +{ + value *val = evaluate (expect_type, exp, noside); + if (noside == EVAL_SKIP) + return eval_skip_value (exp); + return value_cast (expect_type, val); +} + +value * +operation::evaluate_for_address (struct expression *exp, enum noside noside) +{ + value *val = evaluate (nullptr, exp, noside); + return evaluate_subexp_for_address_base (exp, noside, val); +} + +} + /* Evaluate like `evaluate_subexp' except coercing arrays to pointers. When used in contexts where arrays will be coerced anyway, this is equivalent to `evaluate_subexp' but much faster because it avoids @@ -3455,6 +3479,18 @@ evaluate_subexp_for_sizeof (struct expression *exp, int *pos, return evaluate_subexp_for_sizeof_base (exp, type); } +namespace expr +{ + +value * +operation::evaluate_for_sizeof (struct expression *exp, enum noside noside) +{ + value *val = evaluate (nullptr, exp, EVAL_AVOID_SIDE_EFFECTS); + return evaluate_subexp_for_sizeof_base (exp, value_type (val)); +} + +} + /* Evaluate a subexpression of EXP, at index *POS, and return a value for that subexpression cast to TO_TYPE. Advance *POS over the subexpression. */ diff --git a/gdb/expop.h b/gdb/expop.h new file mode 100644 index 0000000..861e3c2 --- /dev/null +++ b/gdb/expop.h @@ -0,0 +1,315 @@ +/* Definitions for expressions in GDB + + Copyright (C) 2020 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef EXPOP_H +#define EXPOP_H + +#include "block.h" +#include "c-lang.h" +#include "cp-abi.h" +#include "expression.h" +#include "objfiles.h" +#include "gdbsupport/traits.h" +#include "gdbsupport/enum-flags.h" + +struct agent_expr; +struct axs_value; + +namespace expr +{ + +/* The check_objfile overloads are used to check whether a particular + component of some operation references an objfile. The passed-in + objfile will never be a debug objfile. */ + +/* See if EXP_OBJFILE matches OBJFILE. */ +static inline bool +check_objfile (struct objfile *exp_objfile, struct objfile *objfile) +{ + if (exp_objfile->separate_debug_objfile_backlink) + exp_objfile = exp_objfile->separate_debug_objfile_backlink; + return exp_objfile == objfile; +} + +static inline bool +check_objfile (struct type *type, struct objfile *objfile) +{ + struct objfile *ty_objfile = type->objfile_owner (); + if (ty_objfile != nullptr) + return check_objfile (ty_objfile, objfile); + return false; +} + +static inline bool +check_objfile (struct symbol *sym, struct objfile *objfile) +{ + return check_objfile (symbol_objfile (sym), objfile); +} + +static inline bool +check_objfile (const struct block *block, struct objfile *objfile) +{ + return check_objfile (block_objfile (block), objfile); +} + +static inline bool +check_objfile (minimal_symbol *minsym, struct objfile *objfile) +{ + /* This may seem strange but minsyms are only used with an objfile + as well. */ + return false; +} + +static inline bool +check_objfile (internalvar *ivar, struct objfile *objfile) +{ + return false; +} + +static inline bool +check_objfile (const std::string &str, struct objfile *objfile) +{ + return false; +} + +static inline bool +check_objfile (const operation_up &op, struct objfile *objfile) +{ + return op->uses_objfile (objfile); +} + +static inline bool +check_objfile (enum exp_opcode val, struct objfile *objfile) +{ + return false; +} + +static inline bool +check_objfile (ULONGEST val, struct objfile *objfile) +{ + return false; +} + +template<typename T> +static inline bool +check_objfile (enum_flags<T> val, struct objfile *objfile) +{ + return false; +} + +template<typename T> +static inline bool +check_objfile (const std::vector<T> &collection, struct objfile *objfile) +{ + for (const auto &item : collection) + { + if (check_objfile (item, objfile)) + return true; + } + return false; +} + +template<typename S, typename T> +static inline bool +check_objfile (const std::pair<S, T> &item, struct objfile *objfile) +{ + return (check_objfile (item.first, objfile) + || check_objfile (item.second, objfile)); +} + +/* Base class for most concrete operations. This class holds data, + specified via template parameters, and supplies generic + implementations of the 'dump' and 'uses_objfile' methods. */ +template<typename... Arg> +class tuple_holding_operation : public operation +{ +public: + + explicit tuple_holding_operation (Arg... args) + : m_storage (std::forward<Arg> (args)...) + { + } + + DISABLE_COPY_AND_ASSIGN (tuple_holding_operation); + + bool uses_objfile (struct objfile *objfile) const override + { + return do_check_objfile<0, Arg...> (objfile, m_storage); + } + + void dump (struct ui_file *stream, int depth) const override + { + do_dump<0, Arg...> (stream, depth, m_storage); + } + +protected: + + /* Storage for the data. */ + std::tuple<Arg...> m_storage; + +private: + + /* do_dump does the work of dumping the data. */ + template<int I, typename... T> + typename std::enable_if<I == sizeof... (T), void>::type + do_dump (struct ui_file *stream, int depth, const std::tuple<T...> &value) + const + { + } + + template<int I, typename... T> + typename std::enable_if<I < sizeof... (T), void>::type + do_dump (struct ui_file *stream, int depth, const std::tuple<T...> &value) + const + { + do_dump<I + 1, T...> (stream, depth, value); + } + + /* do_check_objfile does the work of checking whether this object + refers to OBJFILE. */ + template<int I, typename... T> + typename std::enable_if<I == sizeof... (T), bool>::type + do_check_objfile (struct objfile *objfile, const std::tuple<T...> &value) + const + { + return false; + } + + template<int I, typename... T> + typename std::enable_if<I < sizeof... (T), bool>::type + do_check_objfile (struct objfile *objfile, const std::tuple<T...> &value) + const + { + if (check_objfile (std::get<I> (value), objfile)) + return true; + return do_check_objfile<I + 1, T...> (objfile, value); + } +}; + +/* The check_constant overloads are used to decide whether a given + concrete operation is a constant. This is done by checking the + operands. */ + +static inline bool +check_constant (const operation_up &item) +{ + return item->constant_p (); +} + +static inline bool +check_constant (struct minimal_symbol *msym) +{ + return false; +} + +static inline bool +check_constant (struct type *type) +{ + return true; +} + +static inline bool +check_constant (const struct block *block) +{ + return true; +} + +static inline bool +check_constant (const std::string &str) +{ + return true; +} + +static inline bool +check_constant (struct objfile *objfile) +{ + return true; +} + +static inline bool +check_constant (ULONGEST cst) +{ + return true; +} + +static inline bool +check_constant (struct symbol *sym) +{ + enum address_class sc = SYMBOL_CLASS (sym); + return (sc == LOC_BLOCK + || sc == LOC_CONST + || sc == LOC_CONST_BYTES + || sc == LOC_LABEL); +} + +template<typename T> +static inline bool +check_constant (const std::vector<T> &collection) +{ + for (const auto &item : collection) + if (!check_constant (item)) + return false; + return true; +} + +template<typename S, typename T> +static inline bool +check_constant (const std::pair<S, T> &item) +{ + return check_constant (item.first) && check_constant (item.second); +} + +/* Base class for concrete operations. This class supplies an + implementation of 'constant_p' that works by checking the + operands. */ +template<typename... Arg> +class maybe_constant_operation + : public tuple_holding_operation<Arg...> +{ +public: + + using tuple_holding_operation<Arg...>::tuple_holding_operation; + + bool constant_p () const override + { + return do_check_constant<0, Arg...> (this->m_storage); + } + +private: + + template<int I, typename... T> + typename std::enable_if<I == sizeof... (T), bool>::type + do_check_constant (const std::tuple<T...> &value) const + { + return true; + } + + template<int I, typename... T> + typename std::enable_if<I < sizeof... (T), bool>::type + do_check_constant (const std::tuple<T...> &value) const + { + if (!check_constant (std::get<I> (value))) + return false; + return do_check_constant<I + 1, T...> (value); + } +}; + +} /* namespace expr */ + +#endif /* EXPOP_H */ diff --git a/gdb/expression.h b/gdb/expression.h index 397a0af..24a0eb3 100644 --- a/gdb/expression.h +++ b/gdb/expression.h @@ -89,6 +89,105 @@ enum noside does in many situations. */ }; +struct expression; +struct agent_expr; +struct axs_value; +struct type; +struct ui_file; + +namespace expr +{ + +class operation; +typedef std::unique_ptr<operation> operation_up; + +/* Base class for an operation. An operation is a single component of + an expression. */ + +class operation +{ +protected: + + operation () = default; + DISABLE_COPY_AND_ASSIGN (operation); + +public: + + virtual ~operation () = default; + + /* Evaluate this operation. */ + virtual value *evaluate (struct type *expect_type, + struct expression *exp, + enum noside noside) = 0; + + /* Evaluate this operation in a context where C-like coercion is + needed. */ + virtual value *evaluate_with_coercion (struct expression *exp, + enum noside noside) + { + return evaluate (nullptr, exp, noside); + } + + /* Evaluate this expression in the context of a cast to + EXPECT_TYPE. */ + virtual value *evaluate_for_cast (struct type *expect_type, + struct expression *exp, + enum noside noside); + + /* Evaluate this expression in the context of a sizeof + operation. */ + virtual value *evaluate_for_sizeof (struct expression *exp, + enum noside noside); + + /* Evaluate this expression in the context of an address-of + operation. Must return the address. */ + virtual value *evaluate_for_address (struct expression *exp, + enum noside noside); + + /* True if this is a constant expression. */ + virtual bool constant_p () const + { return false; } + + /* Return true if this operation uses OBJFILE (and will become + dangling when OBJFILE is unloaded), otherwise return false. + OBJFILE must not be a separate debug info file. */ + virtual bool uses_objfile (struct objfile *objfile) const + { return false; } + + /* Generate agent expression bytecodes for this operation. */ + void generate_ax (struct expression *exp, struct agent_expr *ax, + struct axs_value *value, + struct type *cast_type = nullptr); + + /* Return the opcode that is implemented by this operation. */ + virtual enum exp_opcode opcode () const = 0; + + /* Print this operation to STREAM. */ + virtual void dump (struct ui_file *stream, int depth) const = 0; + +protected: + + /* Called by generate_ax to do the work for this particular + operation. */ + virtual void do_generate_ax (struct expression *exp, + struct agent_expr *ax, + struct axs_value *value, + struct type *cast_type) + { + error (_("Cannot translate to agent expression")); + } +}; + +/* A helper function for creating an operation_up, given a type. */ +template<typename T, typename... Arg> +operation_up +make_operation (Arg... args) +{ + return operation_up (new T (std::forward<Arg> (args)...)); +} + +} + union exp_element { enum exp_opcode opcode; |