mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2025-02-23 13:21:43 +08:00
On x86 machines with xmm register, and with recent versions of systemtap (and gcc?), it can occur that stap probe arguments will be placed into xmm registers. I notice this happening on a current Fedora Rawhide install with the following package versions installed: $ rpm -q glibc systemtap gcc glibc-2.35.9000-10.fc37.x86_64 systemtap-4.7~pre16468670g9f253544-1.fc37.x86_64 gcc-12.0.1-0.12.fc37.x86_64 If I check the probe data in libc, I see this: $ readelf -n /lib64/libc.so.6 ... stapsdt 0x0000004d NT_STAPSDT (SystemTap probe descriptors) Provider: libc Name: pthread_start Location: 0x0000000000090ac3, Base: 0x00000000001c65c4, Semaphore: 0x0000000000000000 Arguments: 8@%xmm1 8@1600(%rbx) 8@1608(%rbx) stapsdt 0x00000050 NT_STAPSDT (SystemTap probe descriptors) Provider: libc Name: pthread_create Location: 0x00000000000912f1, Base: 0x00000000001c65c4, Semaphore: 0x0000000000000000 Arguments: 8@%xmm1 8@%r13 8@8(%rsp) 8@16(%rsp) ... Notice that for both of these probes, the first argument is a uint64_t stored in the xmm1 register. Unfortunately, if I try to use this probe within GDB, then I can't view the first argument. Here's an example session: $ gdb $(which gdb) (gdb) start ... (gdb) info probes stap libc pthread_create ... (gdb) break *0x00007ffff729e2f1 # Use address of probe. (gdb) continue ... (gdb) p $_probe_arg0 Invalid cast. What's going wrong? If I re-run my session, but this time use 'set debug stap-expression 1', this is what I see: (gdb) set debug stap-expression 1 (gdb) p $_probe_arg0 Operation: UNOP_CAST Operation: OP_REGISTER String: xmm1 Type: uint64_t Operation: UNOP_CAST Operation: OP_REGISTER String: r13 Type: uint64_t Operation: UNOP_CAST Operation: UNOP_IND Operation: UNOP_CAST Operation: BINOP_ADD Operation: OP_LONG Type: long Constant: 0x0000000000000008 Operation: OP_REGISTER String: rsp Type: uint64_t * Type: uint64_t Operation: UNOP_CAST Operation: UNOP_IND Operation: UNOP_CAST Operation: BINOP_ADD Operation: OP_LONG Type: long Constant: 0x0000000000000010 Operation: OP_REGISTER String: rsp Type: uint64_t * Type: uint64_t Invalid cast. (gdb) The important bit is this: Operation: UNOP_CAST Operation: OP_REGISTER String: xmm1 Type: uint64_t Which is where we cast the xmm1 register to uint64_t. And the final piece of the puzzle is: (gdb) ptype $xmm1 type = union vec128 { v8bf16 v8_bfloat16; v4f v4_float; v2d v2_double; v16i8 v16_int8; v8i16 v8_int16; v4i32 v4_int32; v2i64 v2_int64; uint128_t uint128; } So, we are attempting to cast a union type to a scalar type, which is not supporting in C/C++, and as a consequence GDB's expression evaluator throws an error when we attempt to do this. The first approach I considered for solving this problem was to try and make use of gdbarch_stap_adjust_register. We already have a gdbarch method (gdbarch_stap_adjust_register) that allows us to tweak the name of the register that we access. Currently only x86 architectures use this to transform things like ax to eax in some cases. I wondered, what if we change gdbarch_stap_adjust_register to do more than just change the register names? What if this method instead became gdbarch_stap_read_register. This new method would return a operation_up, and would take the register name, and the type we are trying to read from the register, and return the operation that actually reads the register. The default implementation of this method would just use user_reg_map_name_to_regnum, and then create a register_operation, like we already do in stap_parse_register_operand. But, for x86 architectures this method would fist possibly adjust the register name, then do the default action to read the register. Finally, for x86 this method would spot when we were accessing an xmm register, and, based on the type being pulled from the register, would extract the correct field from the union. The benefit of this approach is that it would work with the expression types that GDB currently supports. The draw back would be that this approach would not be very generic. We'd need code to handle each sub-field size with an xmm register. If other architectures started using vector registers for probe arguments, those architectures would have to create their own gdbarch_stap_read_register method. And finally, the type of the xmm registers comes from the type defined in the target description, there's a risk that GDB might end up hard-coding the names of type sub-fields, then if a target uses a different target description, with different field names for xmm registers, the stap probes would stop working. And so, based on all the above draw backs, I rejected this first approach. My second plan involves adding a new expression type to GDB called unop_extract_operation. This new expression takes a value and a type, during evaluation the value contents are fetched, and then a new value is extracted from the value contents (based on type). This is similar to the following C expression: result_value = *((output_type *) &input_value); Obviously we can't actually build this expression in this case, as the input_value is in a register, but hopefully the above makes it clearer what I'm trying to do. The benefit of the new expression approach is that this code can be shared across all architectures, and it doesn't care about sub-field names within the union type. The draw-backs that I see are potential future problems if arguments are not stored within the least significant bytes of the register. However if/when that becomes an issue we can adapt the gdbarch_stap_read_register approach to allow architectures to control how a value is extracted. For testing, I've extended the existing gdb.base/stap-probe.exp test to include a function that tries to force an argument into an xmm register. Obviously, that will only work on a x86 target, so I've guarded the new function with an appropriate GCC define. In the exp script we use readelf to check if the probe exists, and is using the xmm register. If the probe doesn't exist then the associated tests are skipped. If the probe exists, put isn't using the xmm register (which will depend on systemtap/gcc versions), then again, the tests are skipped. Otherwise, we can run the test. I think the cost of running readelf is pretty low, so I don't feel too bad making all the non-xmm targets running this step. I found that on a Fedora 35 install, with these packages installed, I was able to run this test and have the probe argument be placed in an xmm register: $ rpm -q systemtap gcc glibc systemtap-4.6-4.fc35.x86_64 gcc-11.2.1-9.fc35.x86_64 glibc-2.34-7.fc35.x86_64 Finally, as this patch adds a new operation type, then I need to consider how to generate an agent expression for the new operation type. I have kicked the can down the road a bit on this. In the function stap_parse_register_operand, I only create a unop_extract_operation in the case where the register type is non-scalar, this means that in most cases I don't need to worry about generating an agent expression at all. In the xmm register case, when an unop_extract_operation will be created, I have sketched out how the agent expression could be handled, however, this code is currently not reached. When we try to generate the agent expression to place the xmm register on the stack, GDB hits this error: (gdb) trace -probe-stap test:xmmreg Tracepoint 1 at 0x401166 (gdb) actions Enter actions for tracepoint 1, one per line. End with a line saying just "end". >collect $_probe_arg0 Value not scalar: cannot be an rvalue. This is because GDB doesn't currently support placing non-scalar types on the agent expression evaluation stack. Solving this is clearly related to the original problem, but feels a bit like a second problem. I'd like to get feedback on whether my approach to solving the original problem is acceptable or not before I start looking at how to handle xmm registers within agent expressions.
2207 lines
58 KiB
C++
2207 lines
58 KiB
C++
/* Definitions for expressions in GDB
|
|
|
|
Copyright (C) 2020-2022 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;
|
|
|
|
extern void gen_expr_binop (struct expression *exp,
|
|
enum exp_opcode op,
|
|
expr::operation *lhs, expr::operation *rhs,
|
|
struct agent_expr *ax, struct axs_value *value);
|
|
extern void gen_expr_structop (struct expression *exp,
|
|
enum exp_opcode op,
|
|
expr::operation *lhs,
|
|
const char *name,
|
|
struct agent_expr *ax, struct axs_value *value);
|
|
extern void gen_expr_unop (struct expression *exp,
|
|
enum exp_opcode op,
|
|
expr::operation *lhs,
|
|
struct agent_expr *ax, struct axs_value *value);
|
|
|
|
extern struct value *eval_op_scope (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside,
|
|
struct type *type, const char *string);
|
|
extern struct value *eval_op_var_msym_value (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside,
|
|
bool outermost_p,
|
|
bound_minimal_symbol msymbol);
|
|
extern struct value *eval_op_var_entry_value (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside, symbol *sym);
|
|
extern struct value *eval_op_func_static_var (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside,
|
|
value *func, const char *var);
|
|
extern struct value *eval_op_register (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside, const char *name);
|
|
extern struct value *eval_op_ternop (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside,
|
|
struct value *array, struct value *low,
|
|
struct value *upper);
|
|
extern struct value *eval_op_structop_struct (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside,
|
|
struct value *arg1,
|
|
const char *string);
|
|
extern struct value *eval_op_structop_ptr (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside,
|
|
struct value *arg1,
|
|
const char *string);
|
|
extern struct value *eval_op_member (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside,
|
|
struct value *arg1, struct value *arg2);
|
|
extern struct value *eval_op_add (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside,
|
|
struct value *arg1, struct value *arg2);
|
|
extern struct value *eval_op_sub (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside,
|
|
struct value *arg1, struct value *arg2);
|
|
extern struct value *eval_op_binary (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside, enum exp_opcode op,
|
|
struct value *arg1, struct value *arg2);
|
|
extern struct value *eval_op_subscript (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside, enum exp_opcode op,
|
|
struct value *arg1,
|
|
struct value *arg2);
|
|
extern struct value *eval_op_equal (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside, enum exp_opcode op,
|
|
struct value *arg1,
|
|
struct value *arg2);
|
|
extern struct value *eval_op_notequal (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside, enum exp_opcode op,
|
|
struct value *arg1,
|
|
struct value *arg2);
|
|
extern struct value *eval_op_less (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside, enum exp_opcode op,
|
|
struct value *arg1,
|
|
struct value *arg2);
|
|
extern struct value *eval_op_gtr (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside, enum exp_opcode op,
|
|
struct value *arg1,
|
|
struct value *arg2);
|
|
extern struct value *eval_op_geq (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside, enum exp_opcode op,
|
|
struct value *arg1,
|
|
struct value *arg2);
|
|
extern struct value *eval_op_leq (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside, enum exp_opcode op,
|
|
struct value *arg1,
|
|
struct value *arg2);
|
|
extern struct value *eval_op_repeat (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside, enum exp_opcode op,
|
|
struct value *arg1,
|
|
struct value *arg2);
|
|
extern struct value *eval_op_plus (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside, enum exp_opcode op,
|
|
struct value *arg1);
|
|
extern struct value *eval_op_neg (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside, enum exp_opcode op,
|
|
struct value *arg1);
|
|
extern struct value *eval_op_complement (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside,
|
|
enum exp_opcode op,
|
|
struct value *arg1);
|
|
extern struct value *eval_op_lognot (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside,
|
|
enum exp_opcode op,
|
|
struct value *arg1);
|
|
extern struct value *eval_op_preinc (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside,
|
|
enum exp_opcode op,
|
|
struct value *arg1);
|
|
extern struct value *eval_op_predec (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside,
|
|
enum exp_opcode op,
|
|
struct value *arg1);
|
|
extern struct value *eval_op_postinc (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside,
|
|
enum exp_opcode op,
|
|
struct value *arg1);
|
|
extern struct value *eval_op_postdec (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside,
|
|
enum exp_opcode op,
|
|
struct value *arg1);
|
|
extern struct value *eval_op_ind (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside,
|
|
struct value *arg1);
|
|
extern struct value *eval_op_type (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside, struct type *type);
|
|
extern struct value *eval_op_alignof (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside,
|
|
struct value *arg1);
|
|
extern struct value *eval_op_memval (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside,
|
|
struct value *arg1, struct type *type);
|
|
extern struct value *eval_binop_assign_modify (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside,
|
|
enum exp_opcode op,
|
|
struct value *arg1,
|
|
struct value *arg2);
|
|
|
|
namespace expr
|
|
{
|
|
|
|
class ada_component;
|
|
|
|
/* 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 (const block_symbol &sym, struct objfile *objfile)
|
|
{
|
|
return (check_objfile (sym.symbol, objfile)
|
|
|| check_objfile (sym.block, objfile));
|
|
}
|
|
|
|
static inline bool
|
|
check_objfile (bound_minimal_symbol minsym, struct objfile *objfile)
|
|
{
|
|
return check_objfile (minsym.objfile, objfile);
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
extern bool check_objfile (const std::unique_ptr<ada_component> &comp,
|
|
struct objfile *objfile);
|
|
|
|
static inline void
|
|
dump_for_expression (struct ui_file *stream, int depth,
|
|
const operation_up &op)
|
|
{
|
|
op->dump (stream, depth);
|
|
}
|
|
|
|
extern void dump_for_expression (struct ui_file *stream, int depth,
|
|
enum exp_opcode op);
|
|
extern void dump_for_expression (struct ui_file *stream, int depth,
|
|
const std::string &str);
|
|
extern void dump_for_expression (struct ui_file *stream, int depth,
|
|
struct type *type);
|
|
extern void dump_for_expression (struct ui_file *stream, int depth,
|
|
CORE_ADDR addr);
|
|
extern void dump_for_expression (struct ui_file *stream, int depth,
|
|
internalvar *ivar);
|
|
extern void dump_for_expression (struct ui_file *stream, int depth,
|
|
symbol *sym);
|
|
extern void dump_for_expression (struct ui_file *stream, int depth,
|
|
const block_symbol &sym);
|
|
extern void dump_for_expression (struct ui_file *stream, int depth,
|
|
bound_minimal_symbol msym);
|
|
extern void dump_for_expression (struct ui_file *stream, int depth,
|
|
const block *bl);
|
|
extern void dump_for_expression (struct ui_file *stream, int depth,
|
|
type_instance_flags flags);
|
|
extern void dump_for_expression (struct ui_file *stream, int depth,
|
|
enum c_string_type_values flags);
|
|
extern void dump_for_expression (struct ui_file *stream, int depth,
|
|
enum range_flag flags);
|
|
extern void dump_for_expression (struct ui_file *stream, int depth,
|
|
const std::unique_ptr<ada_component> &comp);
|
|
|
|
template<typename T>
|
|
void
|
|
dump_for_expression (struct ui_file *stream, int depth,
|
|
const std::vector<T> &vals)
|
|
{
|
|
fprintf_filtered (stream, _("%*sVector:\n"), depth, "");
|
|
for (auto &item : vals)
|
|
dump_for_expression (stream, depth + 1, item);
|
|
}
|
|
|
|
template<typename X, typename Y>
|
|
void
|
|
dump_for_expression (struct ui_file *stream, int depth,
|
|
const std::pair<X, Y> &vals)
|
|
{
|
|
dump_for_expression (stream, depth, vals.first);
|
|
dump_for_expression (stream, depth, vals.second);
|
|
}
|
|
|
|
/* 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
|
|
{
|
|
dump_for_expression (stream, depth, opcode ());
|
|
do_dump<0, Arg...> (stream, depth + 1, 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
|
|
{
|
|
dump_for_expression (stream, depth, std::get<I> (value));
|
|
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 (bound_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 (ULONGEST cst)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static inline bool
|
|
check_constant (struct symbol *sym)
|
|
{
|
|
enum address_class sc = sym->aclass ();
|
|
return (sc == LOC_BLOCK
|
|
|| sc == LOC_CONST
|
|
|| sc == LOC_CONST_BYTES
|
|
|| sc == LOC_LABEL);
|
|
}
|
|
|
|
static inline bool
|
|
check_constant (const block_symbol &sym)
|
|
{
|
|
/* We know the block is constant, so we only need to check the
|
|
symbol. */
|
|
return check_constant (sym.symbol);
|
|
}
|
|
|
|
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);
|
|
}
|
|
};
|
|
|
|
/* A floating-point constant. The constant is encoded in the target
|
|
format. */
|
|
|
|
typedef std::array<gdb_byte, 16> float_data;
|
|
|
|
/* An operation that holds a floating-point constant of a given
|
|
type.
|
|
|
|
This does not need the facilities provided by
|
|
tuple_holding_operation, so it does not use it. */
|
|
class float_const_operation
|
|
: public operation
|
|
{
|
|
public:
|
|
|
|
float_const_operation (struct type *type, float_data data)
|
|
: m_type (type),
|
|
m_data (data)
|
|
{
|
|
}
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
return value_from_contents (m_type, m_data.data ());
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return OP_FLOAT; }
|
|
|
|
bool constant_p () const override
|
|
{ return true; }
|
|
|
|
void dump (struct ui_file *stream, int depth) const override;
|
|
|
|
private:
|
|
|
|
struct type *m_type;
|
|
float_data m_data;
|
|
};
|
|
|
|
class scope_operation
|
|
: public maybe_constant_operation<struct type *, std::string>
|
|
{
|
|
public:
|
|
|
|
using maybe_constant_operation::maybe_constant_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
return eval_op_scope (expect_type, exp, noside,
|
|
std::get<0> (m_storage),
|
|
std::get<1> (m_storage).c_str ());
|
|
}
|
|
|
|
value *evaluate_for_address (struct expression *exp,
|
|
enum noside noside) override;
|
|
|
|
value *evaluate_funcall (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside,
|
|
const std::vector<operation_up> &args) override;
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return OP_SCOPE; }
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override;
|
|
};
|
|
|
|
/* Compute the value of a variable. */
|
|
class var_value_operation
|
|
: public maybe_constant_operation<block_symbol>
|
|
{
|
|
public:
|
|
|
|
using maybe_constant_operation::maybe_constant_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override;
|
|
|
|
value *evaluate_with_coercion (struct expression *exp,
|
|
enum noside noside) override;
|
|
|
|
value *evaluate_for_sizeof (struct expression *exp, enum noside noside)
|
|
override;
|
|
|
|
value *evaluate_for_cast (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override;
|
|
|
|
value *evaluate_for_address (struct expression *exp, enum noside noside)
|
|
override;
|
|
|
|
value *evaluate_funcall (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside,
|
|
const std::vector<operation_up> &args) override;
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return OP_VAR_VALUE; }
|
|
|
|
/* Return the symbol referenced by this object. */
|
|
symbol *get_symbol () const
|
|
{
|
|
return std::get<0> (m_storage).symbol;
|
|
}
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override;
|
|
};
|
|
|
|
class long_const_operation
|
|
: public tuple_holding_operation<struct type *, LONGEST>
|
|
{
|
|
public:
|
|
|
|
using tuple_holding_operation::tuple_holding_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
return value_from_longest (std::get<0> (m_storage),
|
|
std::get<1> (m_storage));
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return OP_LONG; }
|
|
|
|
bool constant_p () const override
|
|
{ return true; }
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override;
|
|
};
|
|
|
|
class var_msym_value_operation
|
|
: public maybe_constant_operation<bound_minimal_symbol>
|
|
{
|
|
public:
|
|
|
|
using maybe_constant_operation::maybe_constant_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
return eval_op_var_msym_value (expect_type, exp, noside, m_outermost,
|
|
std::get<0> (m_storage));
|
|
}
|
|
|
|
value *evaluate_for_sizeof (struct expression *exp, enum noside noside)
|
|
override;
|
|
|
|
value *evaluate_for_address (struct expression *exp, enum noside noside)
|
|
override;
|
|
|
|
value *evaluate_for_cast (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override;
|
|
|
|
value *evaluate_funcall (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside,
|
|
const std::vector<operation_up> &args) override
|
|
{
|
|
const char *name = std::get<0> (m_storage).minsym->print_name ();
|
|
return operation::evaluate_funcall (expect_type, exp, noside, name, args);
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return OP_VAR_MSYM_VALUE; }
|
|
|
|
void set_outermost () override
|
|
{
|
|
m_outermost = true;
|
|
}
|
|
|
|
protected:
|
|
|
|
/* True if this is the outermost operation in the expression. */
|
|
bool m_outermost = false;
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override;
|
|
};
|
|
|
|
class var_entry_value_operation
|
|
: public tuple_holding_operation<symbol *>
|
|
{
|
|
public:
|
|
|
|
using tuple_holding_operation::tuple_holding_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
return eval_op_var_entry_value (expect_type, exp, noside,
|
|
std::get<0> (m_storage));
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return OP_VAR_ENTRY_VALUE; }
|
|
};
|
|
|
|
class func_static_var_operation
|
|
: public maybe_constant_operation<operation_up, std::string>
|
|
{
|
|
public:
|
|
|
|
using maybe_constant_operation::maybe_constant_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
value *func = std::get<0> (m_storage)->evaluate (nullptr, exp, noside);
|
|
return eval_op_func_static_var (expect_type, exp, noside, func,
|
|
std::get<1> (m_storage).c_str ());
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return OP_FUNC_STATIC_VAR; }
|
|
};
|
|
|
|
class last_operation
|
|
: public tuple_holding_operation<int>
|
|
{
|
|
public:
|
|
|
|
using tuple_holding_operation::tuple_holding_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
return access_value_history (std::get<0> (m_storage));
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return OP_LAST; }
|
|
};
|
|
|
|
class register_operation
|
|
: public tuple_holding_operation<std::string>
|
|
{
|
|
public:
|
|
|
|
using tuple_holding_operation::tuple_holding_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
return eval_op_register (expect_type, exp, noside,
|
|
std::get<0> (m_storage).c_str ());
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return OP_REGISTER; }
|
|
|
|
/* Return the name of the register. */
|
|
const char *get_name () const
|
|
{
|
|
return std::get<0> (m_storage).c_str ();
|
|
}
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override;
|
|
};
|
|
|
|
class bool_operation
|
|
: public tuple_holding_operation<bool>
|
|
{
|
|
public:
|
|
|
|
using tuple_holding_operation::tuple_holding_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
struct type *type = language_bool_type (exp->language_defn, exp->gdbarch);
|
|
return value_from_longest (type, std::get<0> (m_storage));
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return OP_BOOL; }
|
|
|
|
bool constant_p () const override
|
|
{ return true; }
|
|
};
|
|
|
|
class internalvar_operation
|
|
: public tuple_holding_operation<internalvar *>
|
|
{
|
|
public:
|
|
|
|
using tuple_holding_operation::tuple_holding_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
return value_of_internalvar (exp->gdbarch,
|
|
std::get<0> (m_storage));
|
|
}
|
|
|
|
internalvar *get_internalvar () const
|
|
{
|
|
return std::get<0> (m_storage);
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return OP_INTERNALVAR; }
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override;
|
|
};
|
|
|
|
class string_operation
|
|
: public tuple_holding_operation<std::string>
|
|
{
|
|
public:
|
|
|
|
using tuple_holding_operation::tuple_holding_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override;
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return OP_STRING; }
|
|
};
|
|
|
|
class ternop_slice_operation
|
|
: public maybe_constant_operation<operation_up, operation_up, operation_up>
|
|
{
|
|
public:
|
|
|
|
using maybe_constant_operation::maybe_constant_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
struct value *array
|
|
= std::get<0> (m_storage)->evaluate (nullptr, exp, noside);
|
|
struct value *low
|
|
= std::get<1> (m_storage)->evaluate (nullptr, exp, noside);
|
|
struct value *upper
|
|
= std::get<2> (m_storage)->evaluate (nullptr, exp, noside);
|
|
return eval_op_ternop (expect_type, exp, noside, array, low, upper);
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return TERNOP_SLICE; }
|
|
};
|
|
|
|
class ternop_cond_operation
|
|
: public maybe_constant_operation<operation_up, operation_up, operation_up>
|
|
{
|
|
public:
|
|
|
|
using maybe_constant_operation::maybe_constant_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
struct value *val
|
|
= std::get<0> (m_storage)->evaluate (nullptr, exp, noside);
|
|
|
|
if (value_logical_not (val))
|
|
return std::get<2> (m_storage)->evaluate (nullptr, exp, noside);
|
|
return std::get<1> (m_storage)->evaluate (nullptr, exp, noside);
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return TERNOP_COND; }
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override;
|
|
};
|
|
|
|
class complex_operation
|
|
: public maybe_constant_operation<operation_up, operation_up, struct type *>
|
|
{
|
|
public:
|
|
|
|
using maybe_constant_operation::maybe_constant_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
value *real = std::get<0> (m_storage)->evaluate (nullptr, exp, noside);
|
|
value *imag = std::get<1> (m_storage)->evaluate (nullptr, exp, noside);
|
|
return value_literal_complex (real, imag,
|
|
std::get<2> (m_storage));
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return OP_COMPLEX; }
|
|
};
|
|
|
|
class structop_base_operation
|
|
: public tuple_holding_operation<operation_up, std::string>
|
|
{
|
|
public:
|
|
|
|
/* Used for completion. Return the field name. */
|
|
const std::string &get_string () const
|
|
{
|
|
return std::get<1> (m_storage);
|
|
}
|
|
|
|
/* Used for completion. Evaluate the LHS for type. */
|
|
value *evaluate_lhs (struct expression *exp)
|
|
{
|
|
return std::get<0> (m_storage)->evaluate (nullptr, exp,
|
|
EVAL_AVOID_SIDE_EFFECTS);
|
|
}
|
|
|
|
value *evaluate_funcall (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside,
|
|
const std::vector<operation_up> &args) override;
|
|
|
|
protected:
|
|
|
|
using tuple_holding_operation::tuple_holding_operation;
|
|
};
|
|
|
|
class structop_operation
|
|
: public structop_base_operation
|
|
{
|
|
public:
|
|
|
|
using structop_base_operation::structop_base_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
value *val =std::get<0> (m_storage)->evaluate (nullptr, exp, noside);
|
|
return eval_op_structop_struct (expect_type, exp, noside, val,
|
|
std::get<1> (m_storage).c_str ());
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return STRUCTOP_STRUCT; }
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override
|
|
{
|
|
gen_expr_structop (exp, STRUCTOP_STRUCT,
|
|
std::get<0> (this->m_storage).get (),
|
|
std::get<1> (this->m_storage).c_str (),
|
|
ax, value);
|
|
}
|
|
};
|
|
|
|
class structop_ptr_operation
|
|
: public structop_base_operation
|
|
{
|
|
public:
|
|
|
|
using structop_base_operation::structop_base_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
value *val = std::get<0> (m_storage)->evaluate (nullptr, exp, noside);
|
|
return eval_op_structop_ptr (expect_type, exp, noside, val,
|
|
std::get<1> (m_storage).c_str ());
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return STRUCTOP_PTR; }
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override
|
|
{
|
|
gen_expr_structop (exp, STRUCTOP_PTR,
|
|
std::get<0> (this->m_storage).get (),
|
|
std::get<1> (this->m_storage).c_str (),
|
|
ax, value);
|
|
}
|
|
};
|
|
|
|
class structop_member_base
|
|
: public tuple_holding_operation<operation_up, operation_up>
|
|
{
|
|
public:
|
|
|
|
using tuple_holding_operation::tuple_holding_operation;
|
|
|
|
value *evaluate_funcall (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside,
|
|
const std::vector<operation_up> &args) override;
|
|
};
|
|
|
|
class structop_member_operation
|
|
: public structop_member_base
|
|
{
|
|
public:
|
|
|
|
using structop_member_base::structop_member_base;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
value *lhs
|
|
= std::get<0> (m_storage)->evaluate_for_address (exp, noside);
|
|
value *rhs
|
|
= std::get<1> (m_storage)->evaluate (nullptr, exp, noside);
|
|
return eval_op_member (expect_type, exp, noside, lhs, rhs);
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return STRUCTOP_MEMBER; }
|
|
};
|
|
|
|
class structop_mptr_operation
|
|
: public structop_member_base
|
|
{
|
|
public:
|
|
|
|
using structop_member_base::structop_member_base;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
value *lhs
|
|
= std::get<0> (m_storage)->evaluate (nullptr, exp, noside);
|
|
value *rhs
|
|
= std::get<1> (m_storage)->evaluate (nullptr, exp, noside);
|
|
return eval_op_member (expect_type, exp, noside, lhs, rhs);
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return STRUCTOP_MPTR; }
|
|
};
|
|
|
|
class concat_operation
|
|
: public maybe_constant_operation<operation_up, operation_up>
|
|
{
|
|
public:
|
|
|
|
using maybe_constant_operation::maybe_constant_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
value *lhs
|
|
= std::get<0> (m_storage)->evaluate_with_coercion (exp, noside);
|
|
value *rhs
|
|
= std::get<1> (m_storage)->evaluate_with_coercion (exp, noside);
|
|
return value_concat (lhs, rhs);
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return BINOP_CONCAT; }
|
|
};
|
|
|
|
class add_operation
|
|
: public maybe_constant_operation<operation_up, operation_up>
|
|
{
|
|
public:
|
|
|
|
using maybe_constant_operation::maybe_constant_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
value *lhs
|
|
= std::get<0> (m_storage)->evaluate_with_coercion (exp, noside);
|
|
value *rhs
|
|
= std::get<1> (m_storage)->evaluate_with_coercion (exp, noside);
|
|
return eval_op_add (expect_type, exp, noside, lhs, rhs);
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return BINOP_ADD; }
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override
|
|
{
|
|
gen_expr_binop (exp, BINOP_ADD,
|
|
std::get<0> (this->m_storage).get (),
|
|
std::get<1> (this->m_storage).get (),
|
|
ax, value);
|
|
}
|
|
};
|
|
|
|
class sub_operation
|
|
: public maybe_constant_operation<operation_up, operation_up>
|
|
{
|
|
public:
|
|
|
|
using maybe_constant_operation::maybe_constant_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
value *lhs
|
|
= std::get<0> (m_storage)->evaluate_with_coercion (exp, noside);
|
|
value *rhs
|
|
= std::get<1> (m_storage)->evaluate_with_coercion (exp, noside);
|
|
return eval_op_sub (expect_type, exp, noside, lhs, rhs);
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return BINOP_SUB; }
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override
|
|
{
|
|
gen_expr_binop (exp, BINOP_SUB,
|
|
std::get<0> (this->m_storage).get (),
|
|
std::get<1> (this->m_storage).get (),
|
|
ax, value);
|
|
}
|
|
};
|
|
|
|
typedef struct value *binary_ftype (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside, enum exp_opcode op,
|
|
struct value *arg1, struct value *arg2);
|
|
|
|
template<enum exp_opcode OP, binary_ftype FUNC>
|
|
class binop_operation
|
|
: public maybe_constant_operation<operation_up, operation_up>
|
|
{
|
|
public:
|
|
|
|
using maybe_constant_operation::maybe_constant_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
value *lhs
|
|
= std::get<0> (m_storage)->evaluate (nullptr, exp, noside);
|
|
value *rhs
|
|
= std::get<1> (m_storage)->evaluate (nullptr, exp, noside);
|
|
return FUNC (expect_type, exp, noside, OP, lhs, rhs);
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return OP; }
|
|
};
|
|
|
|
template<enum exp_opcode OP, binary_ftype FUNC>
|
|
class usual_ax_binop_operation
|
|
: public binop_operation<OP, FUNC>
|
|
{
|
|
public:
|
|
|
|
using binop_operation<OP, FUNC>::binop_operation;
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override
|
|
{
|
|
gen_expr_binop (exp, OP,
|
|
std::get<0> (this->m_storage).get (),
|
|
std::get<1> (this->m_storage).get (),
|
|
ax, value);
|
|
}
|
|
};
|
|
|
|
using exp_operation = binop_operation<BINOP_EXP, eval_op_binary>;
|
|
using intdiv_operation = binop_operation<BINOP_INTDIV, eval_op_binary>;
|
|
using mod_operation = binop_operation<BINOP_MOD, eval_op_binary>;
|
|
|
|
using mul_operation = usual_ax_binop_operation<BINOP_MUL, eval_op_binary>;
|
|
using div_operation = usual_ax_binop_operation<BINOP_DIV, eval_op_binary>;
|
|
using rem_operation = usual_ax_binop_operation<BINOP_REM, eval_op_binary>;
|
|
using lsh_operation = usual_ax_binop_operation<BINOP_LSH, eval_op_binary>;
|
|
using rsh_operation = usual_ax_binop_operation<BINOP_RSH, eval_op_binary>;
|
|
using bitwise_and_operation
|
|
= usual_ax_binop_operation<BINOP_BITWISE_AND, eval_op_binary>;
|
|
using bitwise_ior_operation
|
|
= usual_ax_binop_operation<BINOP_BITWISE_IOR, eval_op_binary>;
|
|
using bitwise_xor_operation
|
|
= usual_ax_binop_operation<BINOP_BITWISE_XOR, eval_op_binary>;
|
|
|
|
class subscript_operation
|
|
: public usual_ax_binop_operation<BINOP_SUBSCRIPT, eval_op_subscript>
|
|
{
|
|
public:
|
|
using usual_ax_binop_operation<BINOP_SUBSCRIPT,
|
|
eval_op_subscript>::usual_ax_binop_operation;
|
|
|
|
value *evaluate_for_sizeof (struct expression *exp,
|
|
enum noside noside) override;
|
|
};
|
|
|
|
/* Implementation of comparison operations. */
|
|
template<enum exp_opcode OP, binary_ftype FUNC>
|
|
class comparison_operation
|
|
: public usual_ax_binop_operation<OP, FUNC>
|
|
{
|
|
public:
|
|
|
|
using usual_ax_binop_operation<OP, FUNC>::usual_ax_binop_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
value *lhs
|
|
= std::get<0> (this->m_storage)->evaluate (nullptr, exp, noside);
|
|
value *rhs
|
|
= std::get<1> (this->m_storage)->evaluate (value_type (lhs), exp,
|
|
noside);
|
|
return FUNC (expect_type, exp, noside, OP, lhs, rhs);
|
|
}
|
|
};
|
|
|
|
class equal_operation
|
|
: public comparison_operation<BINOP_EQUAL, eval_op_equal>
|
|
{
|
|
public:
|
|
|
|
using comparison_operation::comparison_operation;
|
|
|
|
operation *get_lhs () const
|
|
{
|
|
return std::get<0> (m_storage).get ();
|
|
}
|
|
|
|
operation *get_rhs () const
|
|
{
|
|
return std::get<1> (m_storage).get ();
|
|
}
|
|
};
|
|
|
|
using notequal_operation
|
|
= comparison_operation<BINOP_NOTEQUAL, eval_op_notequal>;
|
|
using less_operation = comparison_operation<BINOP_LESS, eval_op_less>;
|
|
using gtr_operation = comparison_operation<BINOP_GTR, eval_op_gtr>;
|
|
using geq_operation = comparison_operation<BINOP_GEQ, eval_op_geq>;
|
|
using leq_operation = comparison_operation<BINOP_LEQ, eval_op_leq>;
|
|
|
|
/* Implement the GDB '@' repeat operator. */
|
|
class repeat_operation
|
|
: public binop_operation<BINOP_REPEAT, eval_op_repeat>
|
|
{
|
|
using binop_operation<BINOP_REPEAT, eval_op_repeat>::binop_operation;
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override;
|
|
};
|
|
|
|
/* C-style comma operator. */
|
|
class comma_operation
|
|
: public maybe_constant_operation<operation_up, operation_up>
|
|
{
|
|
public:
|
|
|
|
using maybe_constant_operation::maybe_constant_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
/* The left-hand-side is only evaluated for side effects, so don't
|
|
bother in other modes. */
|
|
if (noside == EVAL_NORMAL)
|
|
std::get<0> (m_storage)->evaluate (nullptr, exp, noside);
|
|
return std::get<1> (m_storage)->evaluate (nullptr, exp, noside);
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return BINOP_COMMA; }
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override;
|
|
};
|
|
|
|
typedef struct value *unary_ftype (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside, enum exp_opcode op,
|
|
struct value *arg1);
|
|
|
|
/* Base class for unary operations. */
|
|
template<enum exp_opcode OP, unary_ftype FUNC>
|
|
class unop_operation
|
|
: public maybe_constant_operation<operation_up>
|
|
{
|
|
public:
|
|
|
|
using maybe_constant_operation::maybe_constant_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
value *val = std::get<0> (m_storage)->evaluate (nullptr, exp, noside);
|
|
return FUNC (expect_type, exp, noside, OP, val);
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return OP; }
|
|
};
|
|
|
|
/* Unary operations that can also be turned into agent expressions in
|
|
the "usual" way. */
|
|
template<enum exp_opcode OP, unary_ftype FUNC>
|
|
class usual_ax_unop_operation
|
|
: public unop_operation<OP, FUNC>
|
|
{
|
|
using unop_operation<OP, FUNC>::unop_operation;
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override
|
|
{
|
|
gen_expr_unop (exp, OP,
|
|
std::get<0> (this->m_storage).get (),
|
|
ax, value);
|
|
}
|
|
};
|
|
|
|
using unary_plus_operation = usual_ax_unop_operation<UNOP_PLUS, eval_op_plus>;
|
|
using unary_neg_operation = usual_ax_unop_operation<UNOP_NEG, eval_op_neg>;
|
|
using unary_complement_operation
|
|
= usual_ax_unop_operation<UNOP_COMPLEMENT, eval_op_complement>;
|
|
using unary_logical_not_operation
|
|
= usual_ax_unop_operation<UNOP_LOGICAL_NOT, eval_op_lognot>;
|
|
|
|
/* Handle pre- and post- increment and -decrement. */
|
|
template<enum exp_opcode OP, unary_ftype FUNC>
|
|
class unop_incr_operation
|
|
: public tuple_holding_operation<operation_up>
|
|
{
|
|
public:
|
|
|
|
using tuple_holding_operation::tuple_holding_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
value *val = std::get<0> (m_storage)->evaluate (expect_type, exp, noside);
|
|
return FUNC (expect_type, exp, noside, OP, val);
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return OP; }
|
|
};
|
|
|
|
using preinc_operation
|
|
= unop_incr_operation<UNOP_PREINCREMENT, eval_op_preinc>;
|
|
using predec_operation
|
|
= unop_incr_operation<UNOP_PREDECREMENT, eval_op_predec>;
|
|
using postinc_operation
|
|
= unop_incr_operation<UNOP_POSTINCREMENT, eval_op_postinc>;
|
|
using postdec_operation
|
|
= unop_incr_operation<UNOP_POSTDECREMENT, eval_op_postdec>;
|
|
|
|
/* Base class for implementations of UNOP_IND. */
|
|
class unop_ind_base_operation
|
|
: public tuple_holding_operation<operation_up>
|
|
{
|
|
public:
|
|
|
|
using tuple_holding_operation::tuple_holding_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
if (expect_type != nullptr && expect_type->code () == TYPE_CODE_PTR)
|
|
expect_type = TYPE_TARGET_TYPE (check_typedef (expect_type));
|
|
value *val = std::get<0> (m_storage)->evaluate (expect_type, exp, noside);
|
|
return eval_op_ind (expect_type, exp, noside, val);
|
|
}
|
|
|
|
value *evaluate_for_address (struct expression *exp,
|
|
enum noside noside) override;
|
|
|
|
value *evaluate_for_sizeof (struct expression *exp,
|
|
enum noside noside) override;
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return UNOP_IND; }
|
|
};
|
|
|
|
/* Ordinary UNOP_IND implementation. */
|
|
class unop_ind_operation
|
|
: public unop_ind_base_operation
|
|
{
|
|
public:
|
|
|
|
using unop_ind_base_operation::unop_ind_base_operation;
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override
|
|
{
|
|
gen_expr_unop (exp, UNOP_IND,
|
|
std::get<0> (this->m_storage).get (),
|
|
ax, value);
|
|
}
|
|
};
|
|
|
|
/* Implement OP_TYPE. */
|
|
class type_operation
|
|
: public tuple_holding_operation<struct type *>
|
|
{
|
|
public:
|
|
|
|
using tuple_holding_operation::tuple_holding_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
return eval_op_type (expect_type, exp, noside, std::get<0> (m_storage));
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return OP_TYPE; }
|
|
|
|
bool constant_p () const override
|
|
{ return true; }
|
|
};
|
|
|
|
/* Implement the "typeof" operation. */
|
|
class typeof_operation
|
|
: public maybe_constant_operation<operation_up>
|
|
{
|
|
public:
|
|
|
|
using maybe_constant_operation::maybe_constant_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
if (noside == EVAL_AVOID_SIDE_EFFECTS)
|
|
return std::get<0> (m_storage)->evaluate (nullptr, exp,
|
|
EVAL_AVOID_SIDE_EFFECTS);
|
|
else
|
|
error (_("Attempt to use a type as an expression"));
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return OP_TYPEOF; }
|
|
};
|
|
|
|
/* Implement 'decltype'. */
|
|
class decltype_operation
|
|
: public maybe_constant_operation<operation_up>
|
|
{
|
|
public:
|
|
|
|
using maybe_constant_operation::maybe_constant_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
if (noside == EVAL_AVOID_SIDE_EFFECTS)
|
|
{
|
|
value *result
|
|
= std::get<0> (m_storage)->evaluate (nullptr, exp,
|
|
EVAL_AVOID_SIDE_EFFECTS);
|
|
enum exp_opcode sub_op = std::get<0> (m_storage)->opcode ();
|
|
if (sub_op == BINOP_SUBSCRIPT
|
|
|| sub_op == STRUCTOP_MEMBER
|
|
|| sub_op == STRUCTOP_MPTR
|
|
|| sub_op == UNOP_IND
|
|
|| sub_op == STRUCTOP_STRUCT
|
|
|| sub_op == STRUCTOP_PTR
|
|
|| sub_op == OP_SCOPE)
|
|
{
|
|
struct type *type = value_type (result);
|
|
|
|
if (!TYPE_IS_REFERENCE (type))
|
|
{
|
|
type = lookup_lvalue_reference_type (type);
|
|
result = allocate_value (type);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
else
|
|
error (_("Attempt to use a type as an expression"));
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return OP_DECLTYPE; }
|
|
};
|
|
|
|
/* Implement 'typeid'. */
|
|
class typeid_operation
|
|
: public tuple_holding_operation<operation_up>
|
|
{
|
|
public:
|
|
|
|
using tuple_holding_operation::tuple_holding_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
enum exp_opcode sub_op = std::get<0> (m_storage)->opcode ();
|
|
enum noside sub_noside
|
|
= ((sub_op == OP_TYPE || sub_op == OP_DECLTYPE || sub_op == OP_TYPEOF)
|
|
? EVAL_AVOID_SIDE_EFFECTS
|
|
: noside);
|
|
|
|
value *result = std::get<0> (m_storage)->evaluate (nullptr, exp,
|
|
sub_noside);
|
|
if (noside != EVAL_NORMAL)
|
|
return allocate_value (cplus_typeid_type (exp->gdbarch));
|
|
return cplus_typeid (result);
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return OP_TYPEID; }
|
|
};
|
|
|
|
/* Implement the address-of operation. */
|
|
class unop_addr_operation
|
|
: public maybe_constant_operation<operation_up>
|
|
{
|
|
public:
|
|
|
|
using maybe_constant_operation::maybe_constant_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
/* C++: check for and handle pointer to members. */
|
|
return std::get<0> (m_storage)->evaluate_for_address (exp, noside);
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return UNOP_ADDR; }
|
|
|
|
/* Return the subexpression. */
|
|
const operation_up &get_expression () const
|
|
{
|
|
return std::get<0> (m_storage);
|
|
}
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override
|
|
{
|
|
gen_expr_unop (exp, UNOP_ADDR,
|
|
std::get<0> (this->m_storage).get (),
|
|
ax, value);
|
|
}
|
|
};
|
|
|
|
/* Implement 'sizeof'. */
|
|
class unop_sizeof_operation
|
|
: public maybe_constant_operation<operation_up>
|
|
{
|
|
public:
|
|
|
|
using maybe_constant_operation::maybe_constant_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
return std::get<0> (m_storage)->evaluate_for_sizeof (exp, noside);
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return UNOP_SIZEOF; }
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override;
|
|
};
|
|
|
|
/* Implement 'alignof'. */
|
|
class unop_alignof_operation
|
|
: public maybe_constant_operation<operation_up>
|
|
{
|
|
public:
|
|
|
|
using maybe_constant_operation::maybe_constant_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
value *val = std::get<0> (m_storage)->evaluate (nullptr, exp,
|
|
EVAL_AVOID_SIDE_EFFECTS);
|
|
return eval_op_alignof (expect_type, exp, noside, val);
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return UNOP_ALIGNOF; }
|
|
};
|
|
|
|
/* Implement UNOP_MEMVAL. */
|
|
class unop_memval_operation
|
|
: public tuple_holding_operation<operation_up, struct type *>
|
|
{
|
|
public:
|
|
|
|
using tuple_holding_operation::tuple_holding_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
value *val = std::get<0> (m_storage)->evaluate (expect_type, exp, noside);
|
|
return eval_op_memval (expect_type, exp, noside, val,
|
|
std::get<1> (m_storage));
|
|
}
|
|
|
|
value *evaluate_for_sizeof (struct expression *exp,
|
|
enum noside noside) override;
|
|
|
|
value *evaluate_for_address (struct expression *exp,
|
|
enum noside noside) override;
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return UNOP_MEMVAL; }
|
|
|
|
/* Return the type referenced by this object. */
|
|
struct type *get_type () const
|
|
{
|
|
return std::get<1> (m_storage);
|
|
}
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override;
|
|
};
|
|
|
|
/* Implement UNOP_MEMVAL_TYPE. */
|
|
class unop_memval_type_operation
|
|
: public tuple_holding_operation<operation_up, operation_up>
|
|
{
|
|
public:
|
|
|
|
using tuple_holding_operation::tuple_holding_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
value *typeval
|
|
= std::get<0> (m_storage)->evaluate (expect_type, exp,
|
|
EVAL_AVOID_SIDE_EFFECTS);
|
|
struct type *type = value_type (typeval);
|
|
value *val = std::get<1> (m_storage)->evaluate (expect_type, exp, noside);
|
|
return eval_op_memval (expect_type, exp, noside, val, type);
|
|
}
|
|
|
|
value *evaluate_for_sizeof (struct expression *exp,
|
|
enum noside noside) override;
|
|
|
|
value *evaluate_for_address (struct expression *exp,
|
|
enum noside noside) override;
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return UNOP_MEMVAL_TYPE; }
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override;
|
|
};
|
|
|
|
/* Implement the 'this' expression. */
|
|
class op_this_operation
|
|
: public tuple_holding_operation<>
|
|
{
|
|
public:
|
|
|
|
using tuple_holding_operation::tuple_holding_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
return value_of_this (exp->language_defn);
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return OP_THIS; }
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override;
|
|
};
|
|
|
|
/* Implement the "type instance" operation. */
|
|
class type_instance_operation
|
|
: public tuple_holding_operation<type_instance_flags, std::vector<type *>,
|
|
operation_up>
|
|
{
|
|
public:
|
|
|
|
using tuple_holding_operation::tuple_holding_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override;
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return TYPE_INSTANCE; }
|
|
};
|
|
|
|
/* The assignment operator. */
|
|
class assign_operation
|
|
: public tuple_holding_operation<operation_up, operation_up>
|
|
{
|
|
public:
|
|
|
|
using tuple_holding_operation::tuple_holding_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
value *lhs = std::get<0> (m_storage)->evaluate (nullptr, exp, noside);
|
|
/* Special-case assignments where the left-hand-side is a
|
|
convenience variable -- in these, don't bother setting an
|
|
expected type. This avoids a weird case where re-assigning a
|
|
string or array to an internal variable could error with "Too
|
|
many array elements". */
|
|
struct type *xtype = (VALUE_LVAL (lhs) == lval_internalvar
|
|
? nullptr
|
|
: value_type (lhs));
|
|
value *rhs = std::get<1> (m_storage)->evaluate (xtype, exp, noside);
|
|
|
|
if (noside == EVAL_AVOID_SIDE_EFFECTS)
|
|
return lhs;
|
|
if (binop_user_defined_p (BINOP_ASSIGN, lhs, rhs))
|
|
return value_x_binop (lhs, rhs, BINOP_ASSIGN, OP_NULL, noside);
|
|
else
|
|
return value_assign (lhs, rhs);
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return BINOP_ASSIGN; }
|
|
|
|
/* Return the left-hand-side of the assignment. */
|
|
operation *get_lhs () const
|
|
{
|
|
return std::get<0> (m_storage).get ();
|
|
}
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override;
|
|
};
|
|
|
|
/* Assignment with modification, like "+=". */
|
|
class assign_modify_operation
|
|
: public tuple_holding_operation<exp_opcode, operation_up, operation_up>
|
|
{
|
|
public:
|
|
|
|
using tuple_holding_operation::tuple_holding_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
value *lhs = std::get<1> (m_storage)->evaluate (nullptr, exp, noside);
|
|
value *rhs = std::get<2> (m_storage)->evaluate (expect_type, exp, noside);
|
|
return eval_binop_assign_modify (expect_type, exp, noside,
|
|
std::get<0> (m_storage), lhs, rhs);
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return BINOP_ASSIGN_MODIFY; }
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override;
|
|
};
|
|
|
|
/* Not a cast! Extract a value of a given type from the contents of a
|
|
value. The new value is extracted from the least significant bytes
|
|
of the old value. The new value's type must be no bigger than the
|
|
old values type. */
|
|
class unop_extract_operation
|
|
: public maybe_constant_operation<operation_up, struct type *>
|
|
{
|
|
public:
|
|
|
|
using maybe_constant_operation::maybe_constant_operation;
|
|
|
|
value *evaluate (struct type *expect_type, struct expression *exp,
|
|
enum noside noside) override;
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return UNOP_EXTRACT; }
|
|
|
|
/* Return the type referenced by this object. */
|
|
struct type *get_type () const
|
|
{
|
|
return std::get<1> (m_storage);
|
|
}
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type) override;
|
|
};
|
|
|
|
/* A type cast. */
|
|
class unop_cast_operation
|
|
: public maybe_constant_operation<operation_up, struct type *>
|
|
{
|
|
public:
|
|
|
|
using maybe_constant_operation::maybe_constant_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
return std::get<0> (m_storage)->evaluate_for_cast (std::get<1> (m_storage),
|
|
exp, noside);
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return UNOP_CAST; }
|
|
|
|
/* Return the type referenced by this object. */
|
|
struct type *get_type () const
|
|
{
|
|
return std::get<1> (m_storage);
|
|
}
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override;
|
|
};
|
|
|
|
/* A cast, but the type comes from an expression, not a "struct
|
|
type". */
|
|
class unop_cast_type_operation
|
|
: public maybe_constant_operation<operation_up, operation_up>
|
|
{
|
|
public:
|
|
|
|
using maybe_constant_operation::maybe_constant_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
value *val = std::get<0> (m_storage)->evaluate (nullptr, exp,
|
|
EVAL_AVOID_SIDE_EFFECTS);
|
|
return std::get<1> (m_storage)->evaluate_for_cast (value_type (val),
|
|
exp, noside);
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return UNOP_CAST_TYPE; }
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override;
|
|
};
|
|
|
|
typedef value *cxx_cast_ftype (struct type *, value *);
|
|
|
|
/* This implements dynamic_cast and reinterpret_cast. static_cast and
|
|
const_cast are handled by the ordinary case operations. */
|
|
template<exp_opcode OP, cxx_cast_ftype FUNC>
|
|
class cxx_cast_operation
|
|
: public maybe_constant_operation<operation_up, operation_up>
|
|
{
|
|
public:
|
|
|
|
using maybe_constant_operation::maybe_constant_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
value *val = std::get<0> (m_storage)->evaluate (nullptr, exp,
|
|
EVAL_AVOID_SIDE_EFFECTS);
|
|
struct type *type = value_type (val);
|
|
value *rhs = std::get<1> (m_storage)->evaluate (type, exp, noside);
|
|
return FUNC (type, rhs);
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return OP; }
|
|
};
|
|
|
|
using dynamic_cast_operation = cxx_cast_operation<UNOP_DYNAMIC_CAST,
|
|
value_dynamic_cast>;
|
|
using reinterpret_cast_operation = cxx_cast_operation<UNOP_REINTERPRET_CAST,
|
|
value_reinterpret_cast>;
|
|
|
|
/* Multi-dimensional subscripting. */
|
|
class multi_subscript_operation
|
|
: public tuple_holding_operation<operation_up, std::vector<operation_up>>
|
|
{
|
|
public:
|
|
|
|
using tuple_holding_operation::tuple_holding_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override;
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return MULTI_SUBSCRIPT; }
|
|
};
|
|
|
|
/* The "&&" operator. */
|
|
class logical_and_operation
|
|
: public maybe_constant_operation<operation_up, operation_up>
|
|
{
|
|
public:
|
|
|
|
using maybe_constant_operation::maybe_constant_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override;
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return BINOP_LOGICAL_AND; }
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override;
|
|
};
|
|
|
|
/* The "||" operator. */
|
|
class logical_or_operation
|
|
: public maybe_constant_operation<operation_up, operation_up>
|
|
{
|
|
public:
|
|
|
|
using maybe_constant_operation::maybe_constant_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override;
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return BINOP_LOGICAL_OR; }
|
|
|
|
protected:
|
|
|
|
void do_generate_ax (struct expression *exp,
|
|
struct agent_expr *ax,
|
|
struct axs_value *value,
|
|
struct type *cast_type)
|
|
override;
|
|
};
|
|
|
|
/* This class implements ADL (aka Koenig) function calls for C++. It
|
|
holds the name of the function to call, the block in which the
|
|
lookup should be done, and a vector of arguments. */
|
|
class adl_func_operation
|
|
: public tuple_holding_operation<std::string, const block *,
|
|
std::vector<operation_up>>
|
|
{
|
|
public:
|
|
|
|
using tuple_holding_operation::tuple_holding_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override;
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return OP_ADL_FUNC; }
|
|
};
|
|
|
|
/* The OP_ARRAY operation. */
|
|
class array_operation
|
|
: public tuple_holding_operation<int, int, std::vector<operation_up>>
|
|
{
|
|
public:
|
|
|
|
using tuple_holding_operation::tuple_holding_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override;
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return OP_ARRAY; }
|
|
|
|
private:
|
|
|
|
struct value *evaluate_struct_tuple (struct value *struct_val,
|
|
struct expression *exp,
|
|
enum noside noside, int nargs);
|
|
};
|
|
|
|
/* A function call. This holds the callee operation and the
|
|
arguments. */
|
|
class funcall_operation
|
|
: public tuple_holding_operation<operation_up, std::vector<operation_up>>
|
|
{
|
|
public:
|
|
|
|
using tuple_holding_operation::tuple_holding_operation;
|
|
|
|
value *evaluate (struct type *expect_type,
|
|
struct expression *exp,
|
|
enum noside noside) override
|
|
{
|
|
return std::get<0> (m_storage)->evaluate_funcall (expect_type, exp, noside,
|
|
std::get<1> (m_storage));
|
|
}
|
|
|
|
enum exp_opcode opcode () const override
|
|
{ return OP_FUNCALL; }
|
|
};
|
|
|
|
} /* namespace expr */
|
|
|
|
#endif /* EXPOP_H */
|