public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 00/43 V2] Allow location description on the DWARF stack
@ 2021-03-01 14:45 Zoran Zaric
  2021-03-01 14:45 ` [PATCH 01/43] Replace the symbol needs evaluator with a parser Zoran Zaric
                   ` (42 more replies)
  0 siblings, 43 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:45 UTC (permalink / raw)
  To: gdb-patches

Based on gdb master: 8255cf421c513747b7e189f932ca6e5b450e0d46

The idea of this patch series is to cleanup the design of the DWARF
expression evaluator (dwarf_expr_context class) and allow a future
extensions of that evaluator needed for the upcoming vendor specific
DWARF extensions.

Main motivation behind this series of patches is AMD’s effort to
improve DWARF support for heavily optimized code, but more
specifically, optimized code for SIMD and SIMT architectures.

These patches are part of the AMD’s DWARF standard extensions that
can be found at:

https://llvm.org/docs/AMDGPUDwarfExtensionsForHeterogeneousDebugging.html

While trying to support optimized code for these architectures, we
found couple of restriction imposed by the version 5 of the standard:

- CFI describes restoring callee saved registers that are spilled.
  Currently CFI only allows a location description that is a register,
  memory address, or implicit location description. AMDGPU optimized
  code may spill scalar registers into portions of vector registers.
  This requires extending CFI to allow any location description.
 
- Optimized code may need to describe a variable that resides in pieces
  that are in different kinds of storage which may include parts that
  are in different kinds of memory address spaces. DWARF has the
  concept of segment addresses. However, the segment cannot be
  specified within a DWARF expression, which is only able to specify
  the offset portion of a segment address. The segment index is only
  provided by the entity that specifies the DWARF expression.
  Therefore, the segment index is a property that can only be put on
  complete objects, such as a variable. Another problem is with DWARF
  DW_OP_xderef* operations, which allow a value to be converted into
  an address of a specified address space which is then read. But it
  provides no way to create a memory location description for an
  address in the non-default address space.
 
- DW_OP_breg* treats the register as containing an address in the
  default address space. It is required to be able to specify the
  address space of the register value.
 
- There is really limited support for bit offsets of location
  description. This issue was already found by others who worked
  on packed array support for ADA language. The array could be packed
  in such a way that their start is not byte alligned. There is no way
  to describe this in DWARF, so gdb implementation in required for the
  packed array to be copied to the byte alligned addresses and then
  passed that buffer in the expression evaluator. This approach is
  restrictive for more complex scenarios.
 
All these restriction are caused by the fact that DWARF stack can only
hold values which size is not bigger then the size of the DWARF generic
type. This means that intermediate result of any DWARF operation needs
to fit in such a small representation.

DWARF Version 5 does not allow location descriptions to be entries on
the DWARF stack. They can only be the final result of the evaluation of
a DWARF expression. However, by allowing a location description to be a
first-class entry on the DWARF stack it becomes possible to compose
expressions containing both values and location descriptions naturally.
It allows objects to be located in any kind of memory address space,
in registers, be implicit values, be undefined, or a composite of any
of these.

This approach unifies the location description operations with general
operations. This in turn allows addition of new DWARF operations which
can push a memory location description with any offset and in any kind
of memory address space.

The number of registers and the cost of memory operations is much
higher for some architectures than a typical CPU. The compiler attempts
to optimize whole variables and arrays into registers. Currently DWARF
only allows DW_OP_push_object_address and related operations to work
with a global memory location. With this change, push object address
mechanism can be modified to work with any location description. This
would also removed the need for the passed in buffer mechanism that ADA
and GO languages currently use.

The proposed implementation in this patch series is completely backward
compatible with the DWARF 5 standard.

Although the patch series is designed as a whole, the first sixteen
patches could be viewed as a standalone subset that introduces a
welcome cleanup of the DWARF expression evaluator module.

Zoran Zaric (43):
  Replace the symbol needs evaluator with a parser
  Cleanup of the dwarf_expr_context constructor
  Move frame context info to dwarf_expr_context
  Remove get_frame_cfa from dwarf_expr_context
  Move compilation unit info to dwarf_expr_context
  Move dwarf_call to dwarf_expr_context
  Move get_object_address to dwarf_expr_context
  Move read_mem to dwarf_expr_context
  Move push_dwarf_reg_entry_value to expr.c
  Inline get_reg_value method of dwarf_expr_context
  Remove empty frame and full evaluators
  Merge evaluate_for_locexpr_baton evaluator
  Move piece_closure and its support to expr.c
  Make value_copy also copy the stack data member
  Make DWARF evaluator return a single struct value
  Simplify dwarf_expr_context class interface
  Add as_lval argument to expression evaluator
  Add new register access interface to expr.c
  Add new memory access interface to expr.c
  Add new classes that model DWARF stack element
  Add to_location method to DWARF entry classes
  Add to_value method to DWARF entry classes
  Add read method to location description classes
  Add write method to location description classes
  Add deref method to location description classes
  Add read_from_gdb_value method to dwarf_location
  Add write_to_gdb_value method to dwarf_location
  Add is_implicit_ptr_at method to dwarf_location
  Add indirect_implicit_ptr to dwarf_location class
  Add new computed struct value callback interface
  Add to_gdb_value method to DWARF entry class
  Change DWARF stack to use new dwarf_entry classes
  Remove old computed struct value callbacks
  Comments cleanup between expr.h and expr.c
  Remove dwarf_expr_context from expr.h interface
  Move read_addr_from_reg function to frame.c
  Add frame info check to DW_OP_reg operations
  Remove DWARF expression composition check
  Change back the symbol needs to use the evaluator
  Add support for any location description in CFI
  Add DWARF operations for byte and bit offset
  Add support for DW_OP_LLVM_undefined operation
  Add support for nested composite locations

 gdb/ada-lang.c                                |    2 +-
 gdb/compile/compile-loc2c.c                   |   11 +
 gdb/dwarf2/expr.c                             | 3419 +++++++++++++++--
 gdb/dwarf2/expr.h                             |  275 +-
 gdb/dwarf2/frame.c                            |  163 +-
 gdb/dwarf2/loc.c                              | 1271 +-----
 gdb/dwarf2/loc.h                              |   34 +-
 gdb/f-lang.c                                  |    4 +-
 gdb/findvar.c                                 |    4 +-
 gdb/frame.c                                   |   37 +-
 gdb/testsuite/gdb.dwarf2/dw2-llvm-offset.exp  |  328 ++
 .../gdb.dwarf2/dw2-llvm-piece-end.exp         |  192 +
 .../gdb.dwarf2/dw2-llvm-undefined.exp         |  144 +
 gdb/testsuite/gdb.dwarf2/symbol_needs_eval.c  |   25 +
 .../gdb.dwarf2/symbol_needs_eval_fail.exp     |  108 +
 .../gdb.dwarf2/symbol_needs_eval_timeout.exp  |  127 +
 .../amd64-py-framefilter-invalidarg.S         |    1 -
 gdb/testsuite/lib/dwarf.exp                   |    8 +
 gdb/valops.c                                  |  129 +-
 gdb/value.c                                   |   69 +-
 gdb/value.h                                   |    4 +-
 include/dwarf2.def                            |    6 +
 22 files changed, 4329 insertions(+), 2032 deletions(-)
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-llvm-offset.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-llvm-piece-end.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-llvm-undefined.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/symbol_needs_eval.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/symbol_needs_eval_fail.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/symbol_needs_eval_timeout.exp

-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 01/43] Replace the symbol needs evaluator with a parser
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
@ 2021-03-01 14:45 ` Zoran Zaric
  2021-04-27  1:20   ` Simon Marchi
  2021-03-01 14:45 ` [PATCH 02/43] Cleanup of the dwarf_expr_context constructor Zoran Zaric
                   ` (41 subsequent siblings)
  42 siblings, 1 reply; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:45 UTC (permalink / raw)
  To: gdb-patches

This patch addresses a design problem with the symbol_needs_eval_context
class. It exposes the problem by introducing two new testsuite test
cases.

To explain the issue, I first need to explain the dwarf_expr_context
class that the symbol_needs_eval_context class derives from.

The intention behind the dwarf_expr_context class is to commonize the
DWARF expression evaluation mechanism for different evaluation
contexts. Currently in gdb, the evaluation context can contain some or
all of the following information: architecture, object file, frame and
compilation unit.

Depending on the information needed to evaluate a given expression,
there are currently three distinct DWARF expression evaluators:

 - Frame: designed to evaluate an expression in the context of a call
   frame information (dwarf_expr_executor class). This evaluator doesn’t
   need a compilation unit information.

 - Location description: designed to evaluate an expression in the
   context of a source level information (dwarf_evaluate_loc_desc
   class). This evaluator expects all information needed for the
   evaluation of the given expression to be present.

 - Symbol needs: designed to answer a question about the parts of the
   context information required to evaluate a DWARF expression behind a
   given symbol (symbol_needs_eval_context class). This evaluator
   doesn’t need a frame information.

The functional difference between the symbol needs evaluator and the
others is that this evaluator is not meant to interact with the actual
target. Instead, it is supposed to check which parts of the context
information are needed for the given DWARF expression to be evaluated by
the location description evaluator.

The idea is to take advantage of the existing dwarf_expr_context
evaluation mechanism and to fake all required interactions with the
actual target, by returning back dummy values. The evaluation result is
returned as one of three possible values, based on operations found in a
given expression:

- SYMBOL_NEEDS_NONE,
- SYMBOL_NEEDS_REGISTERS and
- SYMBOL_NEEDS_FRAME.

The problem here is that faking results of target interactions can yield
an incorrect evaluation result.

For example, if we have a conditional DWARF expression, where the
condition depends on a value read from an actual target, and the true
branch of the condition requires a frame information to be evaluated,
while the false branch doesn’t, fake target reads could conclude that a
frame information is not needed, where in fact it is. This wrong
information would then cause the expression to be actually evaluated (by
the location description evaluator) with a missing frame information.
This would then crash the debugger.

The gdb.dwarf2/symbol_needs_eval_fail.exp test introduces this
scenario, with the following DWARF expression:

                   DW_OP_addr $some_variable
                   DW_OP_deref
                   DW_OP_bra 4  # conditional jump to DW_OP_bregx
                   DW_OP_lit0
                   DW_OP_skip 3  # jump to DW_OP_stack_value
                   DW_OP_bregx $dwarf_regnum 0
                   DW_OP_stack_value

This expression describes a case where some variable dictates the
location of another variable. Depending on a value of some_variable, the
variable whose location is described by this expression is either read
from a register or it is defined as a constant value 0. In both cases,
the value will be returned as an implicit location description on the
DWARF stack.

Currently, when the symbol needs evaluator fakes a memory read from the
address behind the some_variable variable, the constant value 0 is used
as the value of the variable A, and the check returns the
SYMBOL_NEEDS_NONE result.

This is clearly a wrong result and it causes the debugger to crash.

The scenario might sound strange to some people, but it comes from a
SIMD/SIMT architecture where $some_variable is an execution mask.  In
any case, it is a valid DWARF expression, and GDB shouldn't crash while
evaluating it. Also, a similar example could be made based on a
condition of the frame base value, where if that value is concluded to
be 0, the variable location could be defaulted to a TLS based memory
address.

The gdb.dwarf2/symbol_needs_eval_timeout.exp test introduces a second
scenario. This scenario is a bit more abstract due to the DWARF
assembler lacking the CFI support, but it exposes a different
manifestation of the same problem. Like in the previous scenario, the
DWARF expression used in the test is valid:

                       DW_OP_lit1
                       DW_OP_addr $some_variable
                       DW_OP_deref
                       DW_OP_skip 4  # jump to DW_OP_fbreg
                       DW_OP_drop
                       DW_OP_fbreg 0
                       DW_OP_dup
                       DW_OP_lit0
                       DW_OP_eq
                       DW_OP_bra -9  # conditional jump to DW_OP_drop
                       DW_OP_stack_value

Similarly to the previous scenario, the location of a variable A is an
implicit location description with a constant value that depends on a
value held by a global variable. The difference from the previous case
is that DWARF expression contains a loop instead of just one branch. The
end condition of that loop depends on the expectation that a frame base
value is never zero. Currently, the act of faking the target reads will
cause the symbol needs evaluator to get stuck in an infinite loop.

Somebody could argue that we could change the fake reads to return
something else, but that would only hide the real problem.

The general impression seems to be that the desired design is to have
one class that deals with parsing of the DWARF expression, while there
are virtual methods that deal with specifics of some operations.

Using an evaluator mechanism here doesn’t seem to be correct, because
the act of evaluation relies on accessing the data from the actual
target with the possibility of skipping the evaluation of some parts of
the expression.

To better explain the proposed solution for the issue, I first need to
explain a couple more details behind the current design:

There are multiple places in gdb that handle DWARF expression parsing
for different purposes. Some are in charge of converting the expression
to some other internal representation (decode_location_expression,
disassemble_dwarf_expression and dwarf2_compile_expr_to_ax), some are
analysing the expression for specific information
(compute_stack_depth_worker) and some are in charge of evaluating the
expression in a given context (dwarf_expr_context::execute_stack_op
and decode_locdesc).

The problem is that all those functions have a similar (large) switch
statement that handles each DWARF expression operation. The result of
this is a code duplication and harder maintenance.

As a step into the right direction to solve this problem (at least for
the purpose of a DWARF expression evaluation) the expression parsing was
commonized inside of an evaluator base class (dwarf_expr_context). This
makes sense for all derived classes, except for the symbol needs
evaluator (symbol_needs_eval_context) class.

As described previously the problem with this evaluator is that if the
evaluator is not allowed to access the actual target, it is not really
evaluating.

Instead, the desired function of a symbol needs evaluator seems to fall
more into expression analysis category. This means that a more natural
fit for this evaluator is to be a symbol needs analysis, similar to the
existing compute_stack_depth_worker analysis.

Another problem is that using a heavyweight mechanism of an evaluator
to do an expression analysis seems to be an unneeded overhead. It also
requires a more complicated design of the parent class to support fake
target reads.

The reality is that the whole symbol_needs_eval_context class can be
replaced with a lightweight recursive analysis function, that will give
more correct result without compromising the design of the
dwarf_expr_context class.

The downside of this approach is adding of one more similar switch
statement, but at least this way the new symbol needs analysis will be
a lightweight mechnism and it will provide a correct result for any
given DWARF expression.

A more desired long term design would be to have one class that deals
with parsing of the DWARF expression, while there would be a virtual
methods that deal with specifics of some DWARF operations. Then that
class would be used as a base for all DWARF expression parsing mentioned
at the beginning.

This however, requires a far bigger changes that are out of the scope
of this patch series.

The new analysis requires the DWARF location description for the
argc argument of the niam function to change in the assembly file
gdb.python/amd64-py-framefilter-invalidarg.S. Originally, expression
ended with a 0 value byte, which was never reached by the symbol needs
evaluator, because it was detecting a stack underflow when evaluating
the operation before. The new approach does not simulate a DWARF
stack anymore, so the 0 value byte needs to be removed because it
makes the DWARF expression invalid.

Some concerns were raised that a linear scan of the expression byte
stream would have issues if a DWARF producer would try to hide some
non DWARF related data after a control flow operation. Although the
testsuite doesn't show this case, it is a valid concern, so one of
the later patches in this series will address it by switching back to
the then redesigned DWARF expression evaluator.

gdb/ChangeLog:

        * dwarf2/loc.c (class symbol_needs_eval_context): Remove.
        (dwarf2_get_symbol_read_needs): New function.
        (dwarf2_loc_desc_get_symbol_read_needs): Remove.
        (locexpr_get_symbol_read_needs): Use
        dwarf2_get_symbol_read_needs.

gdb/testsuite/ChangeLog:

        * gdb.python/amd64-py-framefilter-invalidarg.S : Update argc
          DWARF location expression.
        * lib/dwarf.exp (_location): Handle DW_OP_fbreg.
        * gdb.dwarf2/symbol_needs_eval.c: New file.
        * gdb.dwarf2/symbol_needs_eval_fail.exp: New file.
        * gdb.dwarf2/symbol_needs_eval_timeout.exp: New file.
---
 gdb/dwarf2/loc.c                              | 490 ++++++++++++++----
 gdb/testsuite/gdb.dwarf2/symbol_needs_eval.c  |  25 +
 .../gdb.dwarf2/symbol_needs_eval_fail.exp     | 108 ++++
 .../gdb.dwarf2/symbol_needs_eval_timeout.exp  | 127 +++++
 .../amd64-py-framefilter-invalidarg.S         |   1 -
 gdb/testsuite/lib/dwarf.exp                   |   4 +
 6 files changed, 639 insertions(+), 116 deletions(-)
 create mode 100644 gdb/testsuite/gdb.dwarf2/symbol_needs_eval.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/symbol_needs_eval_fail.exp
 create mode 100644 gdb/testsuite/gdb.dwarf2/symbol_needs_eval_timeout.exp

diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c
index aec50da4b6d..8dd9e589f5f 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -2733,151 +2733,405 @@ dwarf2_compile_property_to_c (string_file *stream,
 			     data, data + size, per_cu, per_objfile);
 }
 
-\f
-/* Helper functions and baton for dwarf2_loc_desc_get_symbol_read_needs.  */
+/* Compute the correct symbol_needs_kind value for the location
+   expression in EXPR.  */
 
-class symbol_needs_eval_context : public dwarf_expr_context
+static enum symbol_needs_kind
+dwarf2_get_symbol_read_needs (gdb::array_view<const gdb_byte> expr,
+			     dwarf2_per_cu_data *per_cu,
+			     dwarf2_per_objfile *per_objfile,
+			     bfd_endian byte_order,
+			     int addr_size,
+			     int ref_addr_size,
+			     int depth = 0)
 {
-public:
-  symbol_needs_eval_context (dwarf2_per_objfile *per_objfile)
-    : dwarf_expr_context (per_objfile)
-  {}
+  const gdb_byte *expr_end = expr.data () + expr.size ();
+  const gdb_byte *op_ptr = expr.data ();
+  enum symbol_needs_kind symbol_needs = SYMBOL_NEEDS_NONE;
+  const int max_depth = 256;
 
-  enum symbol_needs_kind needs;
-  struct dwarf2_per_cu_data *per_cu;
+  if (depth > max_depth)
+    error (_("DWARF-2 expression error: Loop detected (%d)."), depth);
 
-  /* Reads from registers do require a frame.  */
-  CORE_ADDR read_addr_from_reg (int regnum) override
-  {
-    needs = SYMBOL_NEEDS_FRAME;
-    return 1;
-  }
+  depth++;
 
-  /* "get_reg_value" callback: Reads from registers do require a
-     frame.  */
+  while (op_ptr < expr_end)
+    {
+      enum dwarf_location_atom op
+	= (enum dwarf_location_atom) *op_ptr++;
+      uint64_t uoffset;
+      int64_t offset;
 
-  struct value *get_reg_value (struct type *type, int regnum) override
-  {
-    needs = SYMBOL_NEEDS_FRAME;
-    return value_zero (type, not_lval);
-  }
+      /* The DWARF expression might have a bug causing an infinite
+	 loop.  In that case, quitting is the only way out.  */
+      QUIT;
 
-  /* Reads from memory do not require a frame.  */
-  void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t len) override
-  {
-    memset (buf, 0, len);
-  }
+      switch (op)
+	{
+	case DW_OP_lit0:
+	case DW_OP_lit1:
+	case DW_OP_lit2:
+	case DW_OP_lit3:
+	case DW_OP_lit4:
+	case DW_OP_lit5:
+	case DW_OP_lit6:
+	case DW_OP_lit7:
+	case DW_OP_lit8:
+	case DW_OP_lit9:
+	case DW_OP_lit10:
+	case DW_OP_lit11:
+	case DW_OP_lit12:
+	case DW_OP_lit13:
+	case DW_OP_lit14:
+	case DW_OP_lit15:
+	case DW_OP_lit16:
+	case DW_OP_lit17:
+	case DW_OP_lit18:
+	case DW_OP_lit19:
+	case DW_OP_lit20:
+	case DW_OP_lit21:
+	case DW_OP_lit22:
+	case DW_OP_lit23:
+	case DW_OP_lit24:
+	case DW_OP_lit25:
+	case DW_OP_lit26:
+	case DW_OP_lit27:
+	case DW_OP_lit28:
+	case DW_OP_lit29:
+	case DW_OP_lit30:
+	case DW_OP_lit31:
+	case DW_OP_stack_value:
+	case DW_OP_dup:
+	case DW_OP_drop:
+	case DW_OP_swap:
+	case DW_OP_over:
+	case DW_OP_rot:
+	case DW_OP_deref:
+	case DW_OP_abs:
+	case DW_OP_neg:
+	case DW_OP_not:
+	case DW_OP_and:
+	case DW_OP_div:
+	case DW_OP_minus:
+	case DW_OP_mod:
+	case DW_OP_mul:
+	case DW_OP_or:
+	case DW_OP_plus:
+	case DW_OP_shl:
+	case DW_OP_shr:
+	case DW_OP_shra:
+	case DW_OP_xor:
+	case DW_OP_le:
+	case DW_OP_ge:
+	case DW_OP_eq:
+	case DW_OP_lt:
+	case DW_OP_gt:
+	case DW_OP_ne:
+	case DW_OP_GNU_push_tls_address:
+	case DW_OP_nop:
+	case DW_OP_GNU_uninit:
+	case DW_OP_push_object_address:
+	  break;
 
-  /* Frame-relative accesses do require a frame.  */
-  void get_frame_base (const gdb_byte **start, size_t *length) override
-  {
-    static gdb_byte lit0 = DW_OP_lit0;
+	case DW_OP_form_tls_address:
+	  if (symbol_needs <= SYMBOL_NEEDS_REGISTERS)
+	    symbol_needs = SYMBOL_NEEDS_REGISTERS;
+	  break;
 
-    *start = &lit0;
-    *length = 1;
+	case DW_OP_convert:
+	case DW_OP_GNU_convert:
+	case DW_OP_reinterpret:
+	case DW_OP_GNU_reinterpret:
+	case DW_OP_addrx:
+	case DW_OP_GNU_addr_index:
+	case DW_OP_GNU_const_index:
+	case DW_OP_constu:
+	case DW_OP_plus_uconst:
+	case DW_OP_piece:
+	  op_ptr = safe_read_uleb128 (op_ptr, expr_end, &uoffset);
+	  break;
 
-    needs = SYMBOL_NEEDS_FRAME;
-  }
+	case DW_OP_consts:
+	  op_ptr = safe_read_sleb128 (op_ptr, expr_end, &offset);
+	  break;
 
-  /* CFA accesses require a frame.  */
-  CORE_ADDR get_frame_cfa () override
-  {
-    needs = SYMBOL_NEEDS_FRAME;
-    return 1;
-  }
+	case DW_OP_bit_piece:
+	  op_ptr = safe_read_uleb128 (op_ptr, expr_end, &uoffset);
+	  op_ptr = safe_read_uleb128 (op_ptr, expr_end, &uoffset);
+	  break;
 
-  CORE_ADDR get_frame_pc () override
-  {
-    needs = SYMBOL_NEEDS_FRAME;
-    return 1;
-  }
+	case DW_OP_deref_type:
+	case DW_OP_GNU_deref_type:
+	  op_ptr++;
+	  op_ptr = safe_read_uleb128 (op_ptr, expr_end, &uoffset);
+	  break;
 
-  /* Thread-local accesses require registers, but not a frame.  */
-  CORE_ADDR get_tls_address (CORE_ADDR offset) override
-  {
-    if (needs <= SYMBOL_NEEDS_REGISTERS)
-      needs = SYMBOL_NEEDS_REGISTERS;
-    return 1;
-  }
+	case DW_OP_addr:
+	  op_ptr += addr_size;
+	  break;
 
-  /* Helper interface of per_cu_dwarf_call for
-     dwarf2_loc_desc_get_symbol_read_needs.  */
+	case DW_OP_const1u:
+	case DW_OP_const1s:
+	  op_ptr += 1;
+	  break;
 
-  void dwarf_call (cu_offset die_offset) override
-  {
-    per_cu_dwarf_call (this, die_offset, per_cu, per_objfile);
-  }
+	case DW_OP_const2u:
+	case DW_OP_const2s:
+	  op_ptr += 2;
+	  break;
 
-  /* Helper interface of sect_variable_value for
-     dwarf2_loc_desc_get_symbol_read_needs.  */
+	case DW_OP_const4s:
+	case DW_OP_const4u:
+	  op_ptr += 4;
+	  break;
 
-  struct value *dwarf_variable_value (sect_offset sect_off) override
-  {
-    return sect_variable_value (this, sect_off, per_cu, per_objfile);
-  }
+	case DW_OP_const8s:
+	case DW_OP_const8u:
+	  op_ptr += 8;
+	  break;
 
-  /* DW_OP_entry_value accesses require a caller, therefore a
-     frame.  */
+	case DW_OP_reg0:
+	case DW_OP_reg1:
+	case DW_OP_reg2:
+	case DW_OP_reg3:
+	case DW_OP_reg4:
+	case DW_OP_reg5:
+	case DW_OP_reg6:
+	case DW_OP_reg7:
+	case DW_OP_reg8:
+	case DW_OP_reg9:
+	case DW_OP_reg10:
+	case DW_OP_reg11:
+	case DW_OP_reg12:
+	case DW_OP_reg13:
+	case DW_OP_reg14:
+	case DW_OP_reg15:
+	case DW_OP_reg16:
+	case DW_OP_reg17:
+	case DW_OP_reg18:
+	case DW_OP_reg19:
+	case DW_OP_reg20:
+	case DW_OP_reg21:
+	case DW_OP_reg22:
+	case DW_OP_reg23:
+	case DW_OP_reg24:
+	case DW_OP_reg25:
+	case DW_OP_reg26:
+	case DW_OP_reg27:
+	case DW_OP_reg28:
+	case DW_OP_reg29:
+	case DW_OP_reg30:
+	case DW_OP_reg31:
+	case DW_OP_regx:
+	case DW_OP_breg0:
+	case DW_OP_breg1:
+	case DW_OP_breg2:
+	case DW_OP_breg3:
+	case DW_OP_breg4:
+	case DW_OP_breg5:
+	case DW_OP_breg6:
+	case DW_OP_breg7:
+	case DW_OP_breg8:
+	case DW_OP_breg9:
+	case DW_OP_breg10:
+	case DW_OP_breg11:
+	case DW_OP_breg12:
+	case DW_OP_breg13:
+	case DW_OP_breg14:
+	case DW_OP_breg15:
+	case DW_OP_breg16:
+	case DW_OP_breg17:
+	case DW_OP_breg18:
+	case DW_OP_breg19:
+	case DW_OP_breg20:
+	case DW_OP_breg21:
+	case DW_OP_breg22:
+	case DW_OP_breg23:
+	case DW_OP_breg24:
+	case DW_OP_breg25:
+	case DW_OP_breg26:
+	case DW_OP_breg27:
+	case DW_OP_breg28:
+	case DW_OP_breg29:
+	case DW_OP_breg30:
+	case DW_OP_breg31:
+	case DW_OP_bregx:
+	case DW_OP_fbreg:
+	case DW_OP_call_frame_cfa:
+	case DW_OP_entry_value:
+	case DW_OP_GNU_entry_value:
+	case DW_OP_GNU_parameter_ref:
+	case DW_OP_regval_type:
+	case DW_OP_GNU_regval_type:
+	  symbol_needs = SYMBOL_NEEDS_FRAME;
+	  break;
 
-  void push_dwarf_reg_entry_value (enum call_site_parameter_kind kind,
-				   union call_site_parameter_u kind_u,
-				   int deref_size) override
-  {
-    needs = SYMBOL_NEEDS_FRAME;
+	case DW_OP_implicit_value:
+	  op_ptr = safe_read_uleb128 (op_ptr, expr_end, &uoffset);
+	  op_ptr += uoffset;
+	  break;
 
-    /* The expression may require some stub values on DWARF stack.  */
-    push_address (0, 0);
-  }
+	case DW_OP_implicit_pointer:
+	case DW_OP_GNU_implicit_pointer:
+	  op_ptr += ref_addr_size;
+	  op_ptr = safe_read_sleb128 (op_ptr, expr_end, &offset);
+	  break;
 
-  /* DW_OP_addrx and DW_OP_GNU_addr_index doesn't require a frame.  */
+	case DW_OP_deref_size:
+	case DW_OP_pick:
+	  op_ptr++;
+	  break;
 
-  CORE_ADDR get_addr_index (unsigned int index) override
-  {
-    /* Nothing to do.  */
-    return 1;
-  }
+	case DW_OP_skip:
+	  op_ptr += 2;
+	  break;
 
-  /* DW_OP_push_object_address has a frame already passed through.  */
+	case DW_OP_bra:
+	  op_ptr += 2;
+	  break;
 
-  CORE_ADDR get_object_address () override
-  {
-    /* Nothing to do.  */
-    return 1;
-  }
-};
+	case DW_OP_call2:
+	  {
+	    cu_offset cu_off
+	      = (cu_offset) extract_unsigned_integer (op_ptr, 2, byte_order);
+	    op_ptr += 2;
 
-/* Compute the correct symbol_needs_kind value for the location
-   expression at DATA (length SIZE).  */
+	    auto get_frame_pc = [&symbol_needs] ()
+	      {
+		symbol_needs = SYMBOL_NEEDS_FRAME;
+		return 0;
+	      };
 
-static enum symbol_needs_kind
-dwarf2_loc_desc_get_symbol_read_needs (const gdb_byte *data, size_t size,
-				       dwarf2_per_cu_data *per_cu,
-				       dwarf2_per_objfile *per_objfile)
-{
-  scoped_value_mark free_values;
+	    struct dwarf2_locexpr_baton baton
+	      = dwarf2_fetch_die_loc_cu_off (cu_off, per_cu,
+					     per_objfile,
+					     get_frame_pc);
 
-  symbol_needs_eval_context ctx (per_objfile);
+	    /* If SYMBOL_NEEDS_FRAME is returned from the previous call,
+	       we dont have to check the baton content.  */
+	    if (symbol_needs != SYMBOL_NEEDS_FRAME)
+	      {
+		gdbarch *arch = baton.per_objfile->objfile->arch ();
+		gdb::array_view<const gdb_byte> sub_expr (baton.data,
+							  baton.size);
+		symbol_needs
+		  = dwarf2_get_symbol_read_needs (sub_expr,
+						  baton.per_cu,
+						  baton.per_objfile,
+						  gdbarch_byte_order (arch),
+						  baton.per_cu->addr_size (),
+						  baton.per_cu->ref_addr_size (),
+						  depth);
+	      }
+	    break;
+	  }
 
-  ctx.needs = SYMBOL_NEEDS_NONE;
-  ctx.per_cu = per_cu;
-  ctx.gdbarch = per_objfile->objfile->arch ();
-  ctx.addr_size = per_cu->addr_size ();
-  ctx.ref_addr_size = per_cu->ref_addr_size ();
+	case DW_OP_call4:
+	  {
+	    cu_offset cu_off
+	      = (cu_offset) extract_unsigned_integer (op_ptr, 4, byte_order);
+	    op_ptr += 4;
 
-  ctx.eval (data, size);
+	    auto get_frame_pc = [&symbol_needs] ()
+	      {
+		symbol_needs = SYMBOL_NEEDS_FRAME;
+		return 0;
+	      };
 
-  bool in_reg = ctx.location == DWARF_VALUE_REGISTER;
+	    struct dwarf2_locexpr_baton baton
+	      = dwarf2_fetch_die_loc_cu_off (cu_off, per_cu,
+					     per_objfile,
+					     get_frame_pc);
 
-  /* If the location has several pieces, and any of them are in
-     registers, then we will need a frame to fetch them from.  */
-  for (dwarf_expr_piece &p : ctx.pieces)
-    if (p.location == DWARF_VALUE_REGISTER)
-      in_reg = true;
+	    /* If SYMBOL_NEEDS_FRAME is returned from the previous call,
+	       we dont have to check the baton content.  */
+	    if (symbol_needs != SYMBOL_NEEDS_FRAME)
+	      {
+		gdbarch *arch = baton.per_objfile->objfile->arch ();
+		gdb::array_view<const gdb_byte> sub_expr (baton.data,
+							  baton.size);
+		symbol_needs
+		  = dwarf2_get_symbol_read_needs (sub_expr,
+						  baton.per_cu,
+						  baton.per_objfile,
+						  gdbarch_byte_order (arch),
+						  baton.per_cu->addr_size (),
+						  baton.per_cu->ref_addr_size (),
+						  depth);
+	      }
+	    break;
+	  }
 
-  if (in_reg)
-    ctx.needs = SYMBOL_NEEDS_FRAME;
+	case DW_OP_GNU_variable_value:
+	  {
+	    sect_offset sect_off
+	      = (sect_offset) extract_unsigned_integer (op_ptr,
+							ref_addr_size,
+							byte_order);
+	    op_ptr += ref_addr_size;
+
+	    struct type *die_type
+	      = dwarf2_fetch_die_type_sect_off (sect_off, per_cu,
+						per_objfile);
+
+	    if (die_type == NULL)
+	      error (_("Bad DW_OP_GNU_variable_value DIE."));
+
+	    /* Note: Things still work when the following test is
+	       removed.  This test and error is here to conform to the
+	       proposed specification.  */
+	    if (die_type->code () != TYPE_CODE_INT
+	       && die_type->code () != TYPE_CODE_PTR)
+	      error (_("Type of DW_OP_GNU_variable_value DIE must be "
+		       "an integer or pointer."));
+
+	    auto get_frame_pc = [&symbol_needs] ()
+	      {
+		symbol_needs = SYMBOL_NEEDS_FRAME;
+		return 0;
+	      };
 
-  return ctx.needs;
+	    struct dwarf2_locexpr_baton baton
+	      = dwarf2_fetch_die_loc_sect_off (sect_off, per_cu,
+					       per_objfile,
+					       get_frame_pc, true);
+
+	    /* If SYMBOL_NEEDS_FRAME is returned from the previous call,
+	       we dont have to check the baton content.  */
+	    if (symbol_needs != SYMBOL_NEEDS_FRAME)
+	      {
+		gdbarch *arch = baton.per_objfile->objfile->arch ();
+		gdb::array_view<const gdb_byte> sub_expr (baton.data,
+							  baton.size);
+		symbol_needs
+		  = dwarf2_get_symbol_read_needs (sub_expr,
+						  baton.per_cu,
+						  baton.per_objfile,
+						  gdbarch_byte_order (arch),
+						  baton.per_cu->addr_size (),
+						  baton.per_cu->ref_addr_size (),
+						  depth);
+	      }
+	    break;
+	  }
+
+	case DW_OP_const_type:
+	case DW_OP_GNU_const_type:
+	  op_ptr = safe_read_uleb128 (op_ptr, expr_end, &uoffset);
+	  offset = *op_ptr++;
+	  op_ptr += offset;
+	  break;
+
+	default:
+	  error (_("Unhandled DWARF expression opcode 0x%x"), op);
+	}
+      /* If it is known that a frame information is
+	 needed we can stop parsing the expression.  */
+      if (symbol_needs == SYMBOL_NEEDS_FRAME)
+	break;
+    }
+
+  return symbol_needs;
 }
 
 /* A helper function that throws an unimplemented error mentioning a
@@ -3719,9 +3973,15 @@ locexpr_get_symbol_read_needs (struct symbol *symbol)
   struct dwarf2_locexpr_baton *dlbaton
     = (struct dwarf2_locexpr_baton *) SYMBOL_LOCATION_BATON (symbol);
 
-  return dwarf2_loc_desc_get_symbol_read_needs (dlbaton->data, dlbaton->size,
-						dlbaton->per_cu,
-						dlbaton->per_objfile);
+  gdbarch *arch = dlbaton->per_objfile->objfile->arch ();
+  gdb::array_view<const gdb_byte> expr (dlbaton->data, dlbaton->size);
+
+  return dwarf2_get_symbol_read_needs (expr,
+				       dlbaton->per_cu,
+				       dlbaton->per_objfile,
+				       gdbarch_byte_order (arch),
+				       dlbaton->per_cu->addr_size (),
+				       dlbaton->per_cu->ref_addr_size ());
 }
 
 /* Return true if DATA points to the end of a piece.  END is one past
diff --git a/gdb/testsuite/gdb.dwarf2/symbol_needs_eval.c b/gdb/testsuite/gdb.dwarf2/symbol_needs_eval.c
new file mode 100644
index 00000000000..9740944a73c
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/symbol_needs_eval.c
@@ -0,0 +1,25 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2017-2020 Free Software Foundation, Inc.
+
+   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/>.  */
+
+int exec_mask = 1;
+
+int
+main (void)
+{
+  asm volatile ("main_label: .globl main_label");
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.dwarf2/symbol_needs_eval_fail.exp b/gdb/testsuite/gdb.dwarf2/symbol_needs_eval_fail.exp
new file mode 100644
index 00000000000..00a57228fa4
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/symbol_needs_eval_fail.exp
@@ -0,0 +1,108 @@
+# Copyright 2017-2020 Free Software Foundation, Inc.
+
+# 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/>.
+
+# Test the symbol needs check mechanism if it assumes that faking
+# reads from a target is a safe thing to do.
+#
+# In particular, the test uses a relative branch DWARF operation to
+# hide a register read. If the target reads are indeed faked, the
+# result returned will be wrong.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# Choose suitable integer registers for the test.
+
+set dwarf_regnum 0
+
+if { [is_aarch64_target] } {
+    set regname x0
+} elseif { [is_aarch32_target]
+	   || [istarget "s390*-*-*" ]
+	   || [istarget "powerpc*-*-*"]
+	   || [istarget "rs6000*-*-aix*"] } {
+    set regname r0
+} elseif { [is_x86_like_target] } {
+    set regname eax
+} elseif { [is_amd64_regs_target] } {
+    set regname rax
+} else {
+    verbose "Skipping ${gdb_test_file_name}."
+    return
+}
+
+standard_testfile symbol_needs_eval.c ${gdb_test_file_name}-dw.S
+
+# Make some DWARF for the test.
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global dwarf_regnum regname
+
+    set exec_mask_var [gdb_target_symbol exec_mask]
+
+    cu {} {
+	DW_TAG_compile_unit {
+	    {DW_AT_name symbol_needs_eval.c}
+	    {DW_AT_comp_dir /tmp}
+	} {
+	    declare_labels int_type_label
+
+	    # define int type
+	    int_type_label: DW_TAG_base_type {
+		{DW_AT_name "int"}
+		{DW_AT_encoding @DW_ATE_signed}
+		{DW_AT_byte_size 4 DW_FORM_sdata}
+	    }
+
+	    # define artificial variable a
+	    DW_TAG_variable {
+		{DW_AT_name a}
+		{DW_AT_type :$int_type_label}
+		{DW_AT_location {
+		    DW_OP_addr $exec_mask_var
+		    DW_OP_deref
+		    DW_OP_bra 4 # conditional jump to DW_OP_bregx
+		    DW_OP_lit0
+		    DW_OP_skip 3 # jump to DW_OP_stack_value
+		    DW_OP_bregx $dwarf_regnum 0
+		    DW_OP_stack_value
+		} SPECIAL_expr}
+		{external 1 flag}
+	    }
+	}
+    }
+}
+
+if { [prepare_for_testing ${testfile}.exp ${testfile} \
+     [list $srcfile $asm_file] {nodebug}] } {
+    return -1
+}
+
+# The variable's location expression requires a frame,
+# so an error should be reported.
+gdb_test "print/d a" "No frame selected." "variable a can't be printed"
+
+if ![runto_main] {
+    return -1
+}
+
+gdb_test_no_output "set var \$$regname = 2" "init reg to 2"
+
+gdb_test "print/d a" " = 2" "a == 2"
diff --git a/gdb/testsuite/gdb.dwarf2/symbol_needs_eval_timeout.exp b/gdb/testsuite/gdb.dwarf2/symbol_needs_eval_timeout.exp
new file mode 100644
index 00000000000..52dfb136fbc
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/symbol_needs_eval_timeout.exp
@@ -0,0 +1,127 @@
+# Copyright 2017-2020 Free Software Foundation, Inc.
+
+# 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/>.
+
+# Test the symbol needs check mechanism if it assumes that faking
+# reads from a target is a safe thing to do.
+#
+# In particular, the test uses a relative branch DWARF operation to
+# potentially cause an infinite loop, if the target reads are indeed
+# faked.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# Choose suitable integer registers for the test.
+
+set dwarf_regnum 0
+
+if { [is_aarch64_target] } {
+    set regname x0
+} elseif { [is_aarch32_target]
+	   || [istarget "s390*-*-*" ]
+	   || [istarget "powerpc*-*-*"]
+	   || [istarget "rs6000*-*-aix*"] } {
+    set regname r0
+} elseif { [is_x86_like_target] } {
+    set regname eax
+} elseif { [is_amd64_regs_target] } {
+    set regname rax
+} else {
+    verbose "Skipping ${gdb_test_file_name}."
+    return
+}
+
+standard_testfile symbol_needs_eval.c ${gdb_test_file_name}-dw.S
+
+# Make some DWARF for the test.
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global dwarf_regnum regname
+
+    set exec_mask_var [gdb_target_symbol exec_mask]
+
+    cu {} {
+	DW_TAG_compile_unit {
+	    {DW_AT_name symbol_needs_eval.c}
+	    {DW_AT_comp_dir /tmp}
+	} {
+	    declare_labels int_type_label
+
+	    # define int type
+	    int_type_label: DW_TAG_base_type {
+		{DW_AT_name "int"}
+		{DW_AT_encoding @DW_ATE_signed}
+		{DW_AT_byte_size 4 DW_FORM_sdata}
+	    }
+
+	    # add info for variable exec_mask
+	    DW_TAG_variable {
+		{DW_AT_name exec_mask}
+		{DW_AT_type :$int_type_label}
+		{DW_AT_location {
+		    DW_OP_addr $exec_mask_var
+		} SPECIAL_expr}
+		{external 1 flag}
+	    }
+
+	    # add info for subprogram main
+	    DW_TAG_subprogram {
+		{MACRO_AT_func { main }}
+		{DW_AT_frame_base {
+		    DW_OP_regx $dwarf_regnum
+		} SPECIAL_expr}
+	    } {
+		# define artificial variable a
+		DW_TAG_variable {
+		    {DW_AT_name a}
+		    {DW_AT_type :$int_type_label}
+		    {DW_AT_location {
+			DW_OP_lit1
+			DW_OP_addr $exec_mask_var
+			DW_OP_deref
+			DW_OP_skip 4 # jump to DW_OP_fbreg
+			DW_OP_drop
+			DW_OP_fbreg 0
+			DW_OP_dup
+			DW_OP_lit0
+			DW_OP_eq
+			DW_OP_bra -9 # conditional jump to DW_OP_drop
+			DW_OP_stack_value
+		    } SPECIAL_expr}
+		    {external 1 flag}
+		}
+	    }
+	}
+    }
+}
+
+if { [prepare_for_testing ${testfile}.exp ${testfile} \
+	  [list $srcfile $asm_file] {nodebug}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+gdb_test_no_output "set var \$$regname = 2" "init reg to 2"
+gdb_test_no_output "set var exec_mask = 0" "init exec_mask to 0"
+
+gdb_test "print/d a" " = 2" "a == 2"
diff --git a/gdb/testsuite/gdb.python/amd64-py-framefilter-invalidarg.S b/gdb/testsuite/gdb.python/amd64-py-framefilter-invalidarg.S
index 0adc69fdad6..c8cb9e5aca8 100644
--- a/gdb/testsuite/gdb.python/amd64-py-framefilter-invalidarg.S
+++ b/gdb/testsuite/gdb.python/amd64-py-framefilter-invalidarg.S
@@ -102,7 +102,6 @@ die4e:
 	.uleb128 1f - 2f	# DW_AT_location
 2:
 	.byte	0x13	# DW_OP_drop
-	.quad 0
 1:
 #endif
 die5c:
diff --git a/gdb/testsuite/lib/dwarf.exp b/gdb/testsuite/lib/dwarf.exp
index c1c07be0b98..a913cd48d67 100644
--- a/gdb/testsuite/lib/dwarf.exp
+++ b/gdb/testsuite/lib/dwarf.exp
@@ -1041,6 +1041,10 @@ namespace eval Dwarf {
 		    _op .sleb128 [lindex $line 2]
 		}
 
+		DW_OP_fbreg {
+		    _op .sleb128 [lindex $line 1]
+		}
+
 		default {
 		    if {[llength $line] > 1} {
 			error "Unimplemented: operands in location for $opcode"
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 02/43] Cleanup of the dwarf_expr_context constructor
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
  2021-03-01 14:45 ` [PATCH 01/43] Replace the symbol needs evaluator with a parser Zoran Zaric
@ 2021-03-01 14:45 ` Zoran Zaric
  2021-04-27  1:23   ` Simon Marchi
  2021-03-01 14:45 ` [PATCH 03/43] Move frame context info to dwarf_expr_context Zoran Zaric
                   ` (40 subsequent siblings)
  42 siblings, 1 reply; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:45 UTC (permalink / raw)
  To: gdb-patches

Move the initial values for dwarf_expr_context class data members
to the class declaration in expr.h.

gdb/ChangeLog:

        * dwarf2/expr.c (dwarf_expr_context::dwarf_expr_context):
        Remove initial data members values.
        * dwarf2/expr.h (dwarf_expr_context): Add initial values
        to the class data members.
---
 gdb/dwarf2/expr.c | 11 +----------
 gdb/dwarf2/expr.h | 16 ++++++++--------
 2 files changed, 9 insertions(+), 18 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 107b9cdbd5d..aa166b22d9c 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -90,16 +90,7 @@ dwarf_expr_context::address_type () const
 /* Create a new context for the expression evaluator.  */
 
 dwarf_expr_context::dwarf_expr_context (dwarf2_per_objfile *per_objfile)
-: gdbarch (NULL),
-  addr_size (0),
-  ref_addr_size (0),
-  recursion_depth (0),
-  max_recursion_depth (0x100),
-  location (DWARF_VALUE_MEMORY),
-  len (0),
-  data (NULL),
-  initialized (0),
-  per_objfile (per_objfile)
+: per_objfile (per_objfile)
 {
 }
 
diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
index b28a0775602..fc31be31a4d 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -132,32 +132,32 @@ struct dwarf_expr_context
   std::vector<dwarf_stack_value> stack;
 
   /* Target architecture to use for address operations.  */
-  struct gdbarch *gdbarch;
+  struct gdbarch *gdbarch = nullptr;
 
   /* Target address size in bytes.  */
-  int addr_size;
+  int addr_size = 0;
 
   /* DW_FORM_ref_addr size in bytes.  If -1 DWARF is executed from a frame
      context and operations depending on DW_FORM_ref_addr are not allowed.  */
-  int ref_addr_size;
+  int ref_addr_size = 0;
 
   /* The current depth of dwarf expression recursion, via DW_OP_call*,
      DW_OP_fbreg, DW_OP_push_object_address, etc., and the maximum
      depth we'll tolerate before raising an error.  */
-  int recursion_depth, max_recursion_depth;
+  int recursion_depth = 0, max_recursion_depth = 0x100;
 
   /* Location of the value.  */
-  enum dwarf_value_location location;
+  enum dwarf_value_location location = DWARF_VALUE_MEMORY;
 
   /* For DWARF_VALUE_LITERAL, the current literal value's length and
      data.  For DWARF_VALUE_IMPLICIT_POINTER, LEN is the offset of the
      target DIE of sect_offset kind.  */
-  ULONGEST len;
-  const gdb_byte *data;
+  ULONGEST len = 0;
+  const gdb_byte *data = nullptr;
 
   /* Initialization status of variable: Non-zero if variable has been
      initialized; zero otherwise.  */
-  int initialized;
+  int initialized = 0;
 
   /* A vector of pieces.
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 03/43] Move frame context info to dwarf_expr_context
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
  2021-03-01 14:45 ` [PATCH 01/43] Replace the symbol needs evaluator with a parser Zoran Zaric
  2021-03-01 14:45 ` [PATCH 02/43] Cleanup of the dwarf_expr_context constructor Zoran Zaric
@ 2021-03-01 14:45 ` Zoran Zaric
  2021-04-27  2:19   ` Simon Marchi
  2021-03-01 14:45 ` [PATCH 04/43] Remove get_frame_cfa from dwarf_expr_context Zoran Zaric
                   ` (39 subsequent siblings)
  42 siblings, 1 reply; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:45 UTC (permalink / raw)
  To: gdb-patches

From: Zoran Zaric <zoran.zaric@amd.com>

Following 15 patches in this patch series is cleaning up the design of
the DWARF expression evaluator (dwarf_expr_context) to make future
extensions of that evaluator easier and cleaner to implement.

There are three subclasses of the dwarf_expr_context class
(dwarf_expr_executor, dwarf_evaluate_loc_desc and
evaluate_for_locexpr_baton). Here is a short description of each class:

- dwarf_expr_executor is evaluating a DWARF expression in a context
  of a Call Frame Information. The overridden methods of this subclass
  report an error if a specific DWARF operation, represented by that
  method, is not allowed in a CFI context. The source code of this
  subclass lacks the support for composite as well as implicit pointer
  location description.

- dwarf_evaluate_loc_desc can evaluate any expression with no
  restrictions. All of the methods that this subclass overrides are
  actually doing what they are intended to do. This subclass contains
  a full support for all location description types.

- evaluate_for_locexpr_baton subclass is a specialization of the
  dwarf_evaluate_loc_desc subclass and it’s function is to add
  support for passed in buffers. This seems to be a way to go around
  the fact that DWARF standard lacks a bit offset support for memory
  location descriptions as well as using any location description for
  the push object address functionality.

It all comes down to this question: what is a function of a DWARF
expression evaluator?

Is it to evaluate the expression in a given context or to check the
correctness of that expression in that context?

Currently, the only reason why there is a dwarf_expr_executor subclass
is to report an invalid DWARF expression in a context of a CFI, but is
that what the evaluator is supposed to do considering that the evaluator
is not tied to a given DWARF version?

There are more and more vendor and GNU extensions that are not part of
the DWARF standard, so is it that impossible to expect that some of the
extensions could actually lift the previously imposed restrictions of
the CFI context? Not to mention that every new DWARF version is lifting
some restrictions anyway.

The thing that makes more sense for an evaluator to do, is to take the
context of an evaluation and checks the requirements of every operation
evaluated against that context. With this approach, the evaluator would
report an error only if parts of the context, necessary for the
evaluation, are missing.

If this approach is taken, then the unification of the
dwarf_evaluate_loc_desc, dwarf_expr_executor and dwarf_expr_context
is the next logical step. This makes a design of the DWARF expression
evaluator cleaner and allows more flexibility when supporting future
vendor and GNU extensions.

Additional benefit here is that now all evaluators have access to all
location description types, which means that a vendor extended CFI
rules could support composite location description as well. This also
means that a new evaluator interface can be changed to return a single
struct value (that describes the result of the evaluation) instead of
a caller poking around the dwarf_expr_context internal data for answers
(like it is done currently).

This patch starts the merging proccess by moving the frame context
information and support from dwarf_expr_executor and
dwarf_evaluate_loc_desc to dwarf_expr_context evaluator. The idea
is to report an error when a given operation requires a frame
information to be resolved, if that information is not present.

gdb/ChangeLog:

	* dwarf2/expr.c (ensure_have_frame): New function.
	(read_addr_from_reg): Add from frame.c.
	(dwarf_expr_context::dwarf_expr_context): Add frame info to
	dwarf_expr_context.
	(dwarf_expr_context::read_addr_from_reg): Remove.
	(dwarf_expr_context::get_reg_value): Move from
	dwarf_evaluate_loc_desc.
	(dwarf_expr_context::get_frame_base): Move from
	dwarf_evaluate_loc_desc.
	(dwarf_expr_context::execute_stack_op): Call frame context info
	check. Remove use of read_addr_from_reg method.
	* dwarf2/expr.h (struct dwarf_expr_context): Add frame info
	member, read_addr_from_reg, get_reg_value and get_frame_base
	declaration.
	(read_addr_from_reg): Move to expr.c.
	* dwarf2/frame.c (read_addr_from_reg): Move to
	dwarf_expr_context.
	(dwarf_expr_executor::read_addr_from_reg): Remove.
	(dwarf_expr_executor::get_frame_base): Remove.
	(dwarf_expr_executor::get_reg_value): Remove.
	(execute_stack_op): Use read_addr_from_reg function instead of
	read_addr_from_reg method.
	* dwarf2/loc.c (dwarf_evaluate_loc_desc::get_frame_base): Move
	to dwarf_expr_context.
	(dwarf_evaluate_loc_desc::get_reg_value): Move to
	dwarf_expr_context.
	(dwarf_evaluate_loc_desc::read_addr_from_reg): Remove.
	(dwarf2_locexpr_baton_eval):Use read_addr_from_reg function
	instead of read_addr_from_reg method.
---
 gdb/dwarf2/expr.c  | 77 ++++++++++++++++++++++++++++++++++++++++++++--
 gdb/dwarf2/expr.h  | 31 ++++++++++---------
 gdb/dwarf2/frame.c | 36 ++--------------------
 gdb/dwarf2/loc.c   | 52 +------------------------------
 4 files changed, 94 insertions(+), 102 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index aa166b22d9c..845080a73a3 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -20,6 +20,7 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
+#include "block.h"
 #include "symtab.h"
 #include "gdbtypes.h"
 #include "value.h"
@@ -56,6 +57,31 @@ dwarf_gdbarch_types_init (struct gdbarch *gdbarch)
   return types;
 }
 
+/* Ensure that a FRAME is defined, throw an exception otherwise.
+
+   Throwing NOT_AVAILABLE_ERROR error so that a client can chose
+   to react differently if the evaluation ended because there
+   was a missing context information.  */
+
+static void
+ensure_have_frame (struct frame_info *frame, const char *op_name)
+{
+  if (frame == nullptr)
+    throw_error (NOT_AVAILABLE_ERROR,
+		 _("%s evaluation requires a frame."), op_name);
+}
+
+/* See expr.h.  */
+
+CORE_ADDR
+read_addr_from_reg (struct frame_info *frame, int reg)
+{
+  struct gdbarch *gdbarch = get_frame_arch (frame);
+  int regnum = dwarf_reg_to_regnum_or_error (gdbarch, reg);
+
+  return address_from_register (regnum, frame);
+}
+
 /* Return the type used for DWARF operations where the type is
    unspecified in the DWARF spec.  Only certain sizes are
    supported.  */
@@ -133,6 +159,47 @@ dwarf_expr_context::fetch (int n)
   return stack[stack.size () - (1 + n)].value;
 }
 
+/* See expr.h.  */
+
+struct value *
+dwarf_expr_context::get_reg_value (struct type *type, int reg)
+{
+  ensure_have_frame (frame, "DW_OP_regval_type");
+
+  struct gdbarch *gdbarch = get_frame_arch (frame);
+  int regnum = dwarf_reg_to_regnum_or_error (gdbarch, reg);
+
+  return value_from_register (type, regnum, frame);
+}
+
+/* See expr.h.  */
+
+void
+dwarf_expr_context::get_frame_base (const gdb_byte **start,
+				    size_t * length)
+{
+  ensure_have_frame (frame, "DW_OP_fbreg");
+
+  const struct block *bl = get_frame_block (frame, NULL);
+
+  if (bl == NULL)
+    error (_("frame address is not available."));
+
+  /* Use block_linkage_function, which returns a real (not inlined)
+     function, instead of get_frame_function, which may return an
+     inlined function.  */
+  struct symbol *framefunc = block_linkage_function (bl);
+
+  /* If we found a frame-relative symbol then it was certainly within
+     some function associated with a frame. If we can't find the frame,
+     something has gone wrong.  */
+  gdb_assert (framefunc != NULL);
+
+  func_get_frame_base_dwarf_block (framefunc,
+				   get_frame_address_in_block (frame),
+				   start, length);
+}
+
 /* Require that TYPE be an integral type; throw an exception if not.  */
 
 static void
@@ -821,7 +888,9 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	case DW_OP_breg31:
 	  {
 	    op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset);
-	    result = this->read_addr_from_reg (op - DW_OP_breg0);
+	    ensure_have_frame (this->frame, "DW_OP_breg");
+
+	    result = read_addr_from_reg (this->frame, op - DW_OP_breg0);
 	    result += offset;
 	    result_val = value_from_ulongest (address_type, result);
 	  }
@@ -830,7 +899,9 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	  {
 	    op_ptr = safe_read_uleb128 (op_ptr, op_end, &reg);
 	    op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset);
-	    result = this->read_addr_from_reg (reg);
+	    ensure_have_frame (this->frame, "DW_OP_bregx");
+
+	    result = read_addr_from_reg (this->frame, reg);
 	    result += offset;
 	    result_val = value_from_ulongest (address_type, result);
 	  }
@@ -857,7 +928,7 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	    if (this->location == DWARF_VALUE_MEMORY)
 	      result = fetch_address (0);
 	    else if (this->location == DWARF_VALUE_REGISTER)
-	      result = this->read_addr_from_reg (value_as_long (fetch (0)));
+	      result = read_addr_from_reg (this->frame, value_as_long (fetch (0)));
 	    else
 	      error (_("Not implemented: computing frame "
 		       "base using explicit value operator"));
diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
index fc31be31a4d..cc2463336e7 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -186,24 +186,12 @@ struct dwarf_expr_context
   /* We evaluate the expression in the context of this objfile.  */
   dwarf2_per_objfile *per_objfile;
 
-  /* Return the value of register number REGNUM (a DWARF register number),
-     read as an address.  */
-  virtual CORE_ADDR read_addr_from_reg (int regnum) = 0;
-
-  /* Return a value of type TYPE, stored in register number REGNUM
-     of the frame associated to the given BATON.
-
-     REGNUM is a DWARF register number.  */
-  virtual struct value *get_reg_value (struct type *type, int regnum) = 0;
+  /* Frame information used for the evaluation.  */
+  struct frame_info *frame = nullptr;
 
   /* Read LENGTH bytes at ADDR into BUF.  */
   virtual void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t length) = 0;
 
-  /* Return the location expression for the frame base attribute, in
-     START and LENGTH.  The result must be live until the current
-     expression evaluation is complete.  */
-  virtual void get_frame_base (const gdb_byte **start, size_t *length) = 0;
-
   /* Return the CFA for the frame.  */
   virtual CORE_ADDR get_frame_cfa () = 0;
 
@@ -259,8 +247,23 @@ struct dwarf_expr_context
   void add_piece (ULONGEST size, ULONGEST offset);
   void execute_stack_op (const gdb_byte *op_ptr, const gdb_byte *op_end);
   void pop ();
+
+  /* Return a value of type TYPE, stored in register number REGNUM
+     of the frame associated to the given BATON.
+
+     REGNUM is a DWARF register number.  */
+  struct value *get_reg_value (struct type *type, int regnum);
+
+  /* Return the location expression for the frame base attribute, in
+     START and LENGTH.  The result must be live until the current
+     expression evaluation is complete.  */
+  void get_frame_base (const gdb_byte **start, size_t *length);
 };
 
+/* Return the value of register number REG (a DWARF register number),
+   read as an address in a given FRAME.  */
+CORE_ADDR read_addr_from_reg (struct frame_info *, int);
+
 void dwarf_expr_require_composition (const gdb_byte *, const gdb_byte *,
 				     const char *);
 
diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
index 4d62b197d3a..c4200fc2311 100644
--- a/gdb/dwarf2/frame.c
+++ b/gdb/dwarf2/frame.c
@@ -191,18 +191,6 @@ dwarf2_frame_state::dwarf2_frame_state (CORE_ADDR pc_, struct dwarf2_cie *cie)
     retaddr_column (cie->return_address_register)
 {
 }
-\f
-
-/* Helper functions for execute_stack_op.  */
-
-static CORE_ADDR
-read_addr_from_reg (struct frame_info *this_frame, int reg)
-{
-  struct gdbarch *gdbarch = get_frame_arch (this_frame);
-  int regnum = dwarf_reg_to_regnum_or_error (gdbarch, reg);
-
-  return address_from_register (regnum, this_frame);
-}
 
 /* Execute the required actions for both the DW_CFA_restore and
 DW_CFA_restore_extended instructions.  */
@@ -243,31 +231,11 @@ class dwarf_expr_executor : public dwarf_expr_context
     : dwarf_expr_context (per_objfile)
   {}
 
-  struct frame_info *this_frame;
-
-  CORE_ADDR read_addr_from_reg (int reg) override
-  {
-    return ::read_addr_from_reg (this_frame, reg);
-  }
-
-  struct value *get_reg_value (struct type *type, int reg) override
-  {
-    struct gdbarch *gdbarch = get_frame_arch (this_frame);
-    int regnum = dwarf_reg_to_regnum_or_error (gdbarch, reg);
-
-    return value_from_register (type, regnum, this_frame);
-  }
-
   void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t len) override
   {
     read_memory (addr, buf, len);
   }
 
-  void get_frame_base (const gdb_byte **start, size_t *length) override
-  {
-    invalid ("DW_OP_fbreg");
-  }
-
   void push_dwarf_reg_entry_value (enum call_site_parameter_kind kind,
 				   union call_site_parameter_u kind_u,
 				   int deref_size) override
@@ -323,7 +291,7 @@ execute_stack_op (const gdb_byte *exp, ULONGEST len, int addr_size,
   dwarf_expr_executor ctx (per_objfile);
   scoped_value_mark free_values;
 
-  ctx.this_frame = this_frame;
+  ctx.frame = this_frame;
   ctx.gdbarch = get_frame_arch (this_frame);
   ctx.addr_size = addr_size;
   ctx.ref_addr_size = -1;
@@ -334,7 +302,7 @@ execute_stack_op (const gdb_byte *exp, ULONGEST len, int addr_size,
   if (ctx.location == DWARF_VALUE_MEMORY)
     result = ctx.fetch_address (0);
   else if (ctx.location == DWARF_VALUE_REGISTER)
-    result = ctx.read_addr_from_reg (value_as_long (ctx.fetch (0)));
+    result = read_addr_from_reg (this_frame, value_as_long (ctx.fetch (0)));
   else
     {
       /* This is actually invalid DWARF, but if we ever do run across
diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c
index 8dd9e589f5f..f1caf3469b2 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -654,7 +654,6 @@ class dwarf_evaluate_loc_desc : public dwarf_expr_context
     : dwarf_expr_context (per_objfile)
   {}
 
-  struct frame_info *frame;
   struct dwarf2_per_cu_data *per_cu;
   CORE_ADDR obj_address;
 
@@ -780,61 +779,12 @@ class dwarf_evaluate_loc_desc : public dwarf_expr_context
     this->eval (data_src, size);
   }
 
-  /* Using the frame specified in BATON, find the location expression
-     describing the frame base.  Return a pointer to it in START and
-     its length in LENGTH.  */
-  void get_frame_base (const gdb_byte **start, size_t * length) override
-  {
-    /* FIXME: cagney/2003-03-26: This code should be using
-       get_frame_base_address(), and then implement a dwarf2 specific
-       this_base method.  */
-    struct symbol *framefunc;
-    const struct block *bl = get_frame_block (frame, NULL);
-
-    if (bl == NULL)
-      error (_("frame address is not available."));
-
-    /* Use block_linkage_function, which returns a real (not inlined)
-       function, instead of get_frame_function, which may return an
-       inlined function.  */
-    framefunc = block_linkage_function (bl);
-
-    /* If we found a frame-relative symbol then it was certainly within
-       some function associated with a frame. If we can't find the frame,
-       something has gone wrong.  */
-    gdb_assert (framefunc != NULL);
-
-    func_get_frame_base_dwarf_block (framefunc,
-				     get_frame_address_in_block (frame),
-				     start, length);
-  }
-
   /* Read memory at ADDR (length LEN) into BUF.  */
 
   void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t len) override
   {
     read_memory (addr, buf, len);
   }
-
-  /* Using the frame specified in BATON, return the value of register
-     REGNUM, treated as a pointer.  */
-  CORE_ADDR read_addr_from_reg (int dwarf_regnum) override
-  {
-    struct gdbarch *gdbarch = get_frame_arch (frame);
-    int regnum = dwarf_reg_to_regnum_or_error (gdbarch, dwarf_regnum);
-
-    return address_from_register (regnum, frame);
-  }
-
-  /* Implement "get_reg_value" callback.  */
-
-  struct value *get_reg_value (struct type *type, int dwarf_regnum) override
-  {
-    struct gdbarch *gdbarch = get_frame_arch (frame);
-    int regnum = dwarf_reg_to_regnum_or_error (gdbarch, dwarf_regnum);
-
-    return value_from_register (type, regnum, frame);
-  }
 };
 
 /* See dwarf2loc.h.  */
@@ -2555,7 +2505,7 @@ dwarf2_locexpr_baton_eval (const struct dwarf2_locexpr_baton *dlbaton,
     case DWARF_VALUE_MEMORY:
       *valp = ctx.fetch_address (0);
       if (ctx.location == DWARF_VALUE_REGISTER)
-	*valp = ctx.read_addr_from_reg (*valp);
+	*valp = read_addr_from_reg (frame, *valp);
       return 1;
     case DWARF_VALUE_LITERAL:
       *valp = extract_signed_integer (ctx.data, ctx.len,
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 04/43] Remove get_frame_cfa from dwarf_expr_context
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (2 preceding siblings ...)
  2021-03-01 14:45 ` [PATCH 03/43] Move frame context info to dwarf_expr_context Zoran Zaric
@ 2021-03-01 14:45 ` Zoran Zaric
  2021-03-01 14:45 ` [PATCH 05/43] Move compilation unit info to dwarf_expr_context Zoran Zaric
                   ` (38 subsequent siblings)
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:45 UTC (permalink / raw)
  To: gdb-patches

Following the idea of merging the evaluators, the get_frame_cfa method
can be moved from dwarf_expr_executor and dwarf_evaluate_loc_desc
classes to their base class dwarf_expr_context. Once this is done,
it becomes apparent that the method is only called once and it can be
inlined.

It is also necessary to check if the frame context information was
provided before the DW_OP_call_frame_cfa operation is executed.

gdb/ChangeLog:

	* dwarf2/expr.c (dwarf_expr_context::get_frame_cfa): Remove
	method.
	(dwarf_expr_context::execute_stack_op): Call frame context info
	check for DW_OP_call_frame_cfa. Remove use of get_frame_cfa.
	* dwarf2/expr.h (dwarf_expr_context::get_frame_cfa): Remove
	method.
	* dwarf2/frame.c (dwarf_expr_context::get_frame_cfa): Remove
	method.
	* dwarf2/loc.c (dwarf_expr_context::get_frame_cfa): Remove
	method.
---
 gdb/dwarf2/expr.c  | 5 ++++-
 gdb/dwarf2/expr.h  | 3 ---
 gdb/dwarf2/frame.c | 5 -----
 gdb/dwarf2/loc.c   | 8 --------
 4 files changed, 4 insertions(+), 17 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 845080a73a3..6b5cba6b7b4 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -29,6 +29,7 @@
 #include "dwarf2/expr.h"
 #include "dwarf2/loc.h"
 #include "dwarf2/read.h"
+#include "frame.h"
 #include "gdbsupport/underlying.h"
 #include "gdbarch.h"
 
@@ -1220,7 +1221,9 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	  break;
 
 	case DW_OP_call_frame_cfa:
-	  result = this->get_frame_cfa ();
+	  ensure_have_frame (this->frame, "DW_OP_call_frame_cfa");
+
+	  result = dwarf2_frame_cfa (this->frame);
 	  result_val = value_from_ulongest (address_type, result);
 	  in_stack_memory = true;
 	  break;
diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
index cc2463336e7..6ba8efb3cc6 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -192,9 +192,6 @@ struct dwarf_expr_context
   /* Read LENGTH bytes at ADDR into BUF.  */
   virtual void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t length) = 0;
 
-  /* Return the CFA for the frame.  */
-  virtual CORE_ADDR get_frame_cfa () = 0;
-
   /* Return the PC for the frame.  */
   virtual CORE_ADDR get_frame_pc ()
   {
diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
index c4200fc2311..682f2881556 100644
--- a/gdb/dwarf2/frame.c
+++ b/gdb/dwarf2/frame.c
@@ -248,11 +248,6 @@ class dwarf_expr_executor : public dwarf_expr_context
     invalid ("DW_OP_push_object_address");
   }
 
-  CORE_ADDR get_frame_cfa () override
-  {
-    invalid ("DW_OP_call_frame_cfa");
-  }
-
   CORE_ADDR get_tls_address (CORE_ADDR offset) override
   {
     invalid ("DW_OP_form_tls_address");
diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c
index f1caf3469b2..2ad262dc802 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -657,14 +657,6 @@ class dwarf_evaluate_loc_desc : public dwarf_expr_context
   struct dwarf2_per_cu_data *per_cu;
   CORE_ADDR obj_address;
 
-  /* Helper function for dwarf2_evaluate_loc_desc.  Computes the CFA for
-     the frame in BATON.  */
-
-  CORE_ADDR get_frame_cfa () override
-  {
-    return dwarf2_frame_cfa (frame);
-  }
-
   /* Helper function for dwarf2_evaluate_loc_desc.  Computes the PC for
      the frame in BATON.  */
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 05/43] Move compilation unit info to dwarf_expr_context
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (3 preceding siblings ...)
  2021-03-01 14:45 ` [PATCH 04/43] Remove get_frame_cfa from dwarf_expr_context Zoran Zaric
@ 2021-03-01 14:45 ` Zoran Zaric
  2021-04-27  2:58   ` Simon Marchi
  2021-03-01 14:45 ` [PATCH 06/43] Move dwarf_call " Zoran Zaric
                   ` (37 subsequent siblings)
  42 siblings, 1 reply; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:45 UTC (permalink / raw)
  To: gdb-patches

From: Zoran Zaric <zoran.zaric@amd.com>

This patch moves the compilation unit context information and support
from dwarf_expr_executor and dwarf_evaluate_loc_desc to
dwarf_expr_context evaluator. The idea is to report an error when a
given operation requires a compilation unit information to be resolved,
which is not available.

gdb/ChangeLog:

	* dwarf2/expr.c (ensure_have_per_cu): New function.
	(dwarf_expr_context::dwarf_expr_context): Add compilation unit
	context information.
	(dwarf_expr_context::get_base_type): Move from
	dwarf_evaluate_loc_desc.
	(dwarf_expr_context::get_addr_index): Remove method.
	(dwarf_expr_context::dwarf_variable_value): Remove method.
	(dwarf_expr_context::execute_stack_op): Call compilation unit
	context info check. Inline get_addr_index and
	dwarf_variable_value methods.
	* dwarf2/expr.h (struct dwarf_expr_context): Add compilation
	context info. Remove get_addr_index and dwarf_variable_value.
	* dwarf2/frame.c (dwarf_expr_executor::get_addr_index): Remove
	method.
	(dwarf_expr_executor::dwarf_variable_value): Remove method.
	* dwarf2/loc.c (sect_variable_value): Expose function.
	(dwarf_evaluate_loc_desc::get_addr_index): Remove method.
	(dwarf_evaluate_loc_desc::dwarf_variable_value): Remove method.
	(class dwarf_evaluate_loc_desc): Move compilation unit context
	information to dwarf_expr_context class.
	* dwarf2/loc.h (sect_variable_value): Expose function.
---
 gdb/dwarf2/expr.c  | 55 +++++++++++++++++++++++++++++++++++++++-------
 gdb/dwarf2/expr.h  | 26 ++++++++--------------
 gdb/dwarf2/frame.c | 10 ---------
 gdb/dwarf2/loc.c   | 35 +++--------------------------
 gdb/dwarf2/loc.h   |  8 +++++++
 5 files changed, 67 insertions(+), 67 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 6b5cba6b7b4..5a0a4a4299c 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -72,6 +72,20 @@ ensure_have_frame (struct frame_info *frame, const char *op_name)
 		 _("%s evaluation requires a frame."), op_name);
 }
 
+/* Ensure that a PER_CU is defined and throw an exception otherwise.
+
+   Throwing NOT_AVAILABLE_ERROR error so that a client can chose
+   to react differently if the evaluation ended because there
+   was a missing context information.  */
+
+static void
+ensure_have_per_cu (struct dwarf2_per_cu_data *per_cu, const char* op_name)
+{
+  if (per_cu == nullptr)
+    throw_error (NOT_AVAILABLE_ERROR,
+		 _("%s evaluation requires a compilation unit."), op_name);
+}
+
 /* See expr.h.  */
 
 CORE_ADDR
@@ -201,6 +215,25 @@ dwarf_expr_context::get_frame_base (const gdb_byte **start,
 				   start, length);
 }
 
+/* See expr.h.  */
+
+struct type *
+dwarf_expr_context::get_base_type (cu_offset die_cu_off, int size)
+{
+  if (per_cu == nullptr)
+    return builtin_type (this->gdbarch)->builtin_int;
+
+  struct type *result = dwarf2_get_die_type (die_cu_off, per_cu, per_objfile);
+
+  if (result == NULL)
+    error (_("Could not find type for DW_OP_const_type"));
+
+  if (size != 0 && TYPE_LENGTH (result) != size)
+    error (_("DW_OP_const_type has different sizes for type and data"));
+
+  return result;
+}
+
 /* Require that TYPE be an integral type; throw an exception if not.  */
 
 static void
@@ -697,14 +730,20 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 
 	case DW_OP_addrx:
 	case DW_OP_GNU_addr_index:
+	  ensure_have_per_cu (this->per_cu, "DW_OP_addrx");
+
 	  op_ptr = safe_read_uleb128 (op_ptr, op_end, &uoffset);
-	  result = this->get_addr_index (uoffset);
+	  result = dwarf2_read_addr_index (this->per_cu, this->per_objfile,
+					   uoffset);
 	  result += this->per_objfile->objfile->text_section_offset ();
 	  result_val = value_from_ulongest (address_type, result);
 	  break;
 	case DW_OP_GNU_const_index:
+	  ensure_have_per_cu (per_cu, "DW_OP_GNU_const_index");
+
 	  op_ptr = safe_read_uleb128 (op_ptr, op_end, &uoffset);
-	  result = this->get_addr_index (uoffset);
+	  result = dwarf2_read_addr_index (this->per_cu, this->per_objfile,
+					   uoffset);
 	  result_val = value_from_ulongest (address_type, result);
 	  break;
 
@@ -834,10 +873,7 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	case DW_OP_GNU_implicit_pointer:
 	  {
 	    int64_t len;
-
-	    if (this->ref_addr_size == -1)
-	      error (_("DWARF-2 expression error: DW_OP_implicit_pointer "
-		       "is not allowed in frame context"));
+	    ensure_have_per_cu (per_cu, "DW_OP_implicit_pointer");
 
 	    /* The referred-to DIE of sect_offset kind.  */
 	    this->len = extract_unsigned_integer (op_ptr, this->ref_addr_size,
@@ -1330,13 +1366,16 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 
 	case DW_OP_GNU_variable_value:
 	  {
+	    ensure_have_per_cu (per_cu, "DW_OP_GNU_variable_value");
+
 	    sect_offset sect_off
 	      = (sect_offset) extract_unsigned_integer (op_ptr,
 							this->ref_addr_size,
 							byte_order);
 	    op_ptr += this->ref_addr_size;
-	    result_val = value_cast (address_type,
-				     this->dwarf_variable_value (sect_off));
+	    result_val = sect_variable_value (sect_off, this->per_cu,
+					      this->per_objfile);
+	    result_val = value_cast (address_type, result_val);
 	  }
 	  break;
 	
diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
index 6ba8efb3cc6..48dd37150db 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -189,6 +189,9 @@ struct dwarf_expr_context
   /* Frame information used for the evaluation.  */
   struct frame_info *frame = nullptr;
 
+  /* Compilation unit used for the evaluation.  */
+  struct dwarf2_per_cu_data *per_cu = nullptr;
+
   /* Read LENGTH bytes at ADDR into BUF.  */
   virtual void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t length) = 0;
 
@@ -208,19 +211,6 @@ struct dwarf_expr_context
      subroutine.  */
   virtual void dwarf_call (cu_offset die_cu_off) = 0;
 
-  /* Execute "variable value" operation on the DIE at SECT_OFF.  */
-  virtual struct value *dwarf_variable_value (sect_offset sect_off) = 0;
-
-  /* Return the base type given by the indicated DIE at DIE_CU_OFF.
-     This can throw an exception if the DIE is invalid or does not
-     represent a base type.  SIZE is non-zero if this function should
-     verify that the resulting type has the correct size.  */
-  virtual struct type *get_base_type (cu_offset die_cu_off, int size)
-  {
-    /* Anything will do.  */
-    return builtin_type (this->gdbarch)->builtin_int;
-  }
-
   /* Push on DWARF stack an entry evaluated for DW_TAG_call_site's
      parameter matching KIND and KIND_U at the caller of specified BATON.
      If DEREF_SIZE is not -1 then use DW_AT_call_data_value instead of
@@ -229,10 +219,6 @@ struct dwarf_expr_context
 					   union call_site_parameter_u kind_u,
 					   int deref_size) = 0;
 
-  /* Return the address indexed by DW_OP_addrx or DW_OP_GNU_addr_index.
-     This can throw an exception if the index is out of range.  */
-  virtual CORE_ADDR get_addr_index (unsigned int index) = 0;
-
   /* Return the `object address' for DW_OP_push_object_address.  */
   virtual CORE_ADDR get_object_address () = 0;
 
@@ -255,6 +241,12 @@ struct dwarf_expr_context
      START and LENGTH.  The result must be live until the current
      expression evaluation is complete.  */
   void get_frame_base (const gdb_byte **start, size_t *length);
+
+  /* Return the base type given by the indicated DIE at DIE_CU_OFF.
+     This can throw an exception if the DIE is invalid or does not
+     represent a base type.  SIZE is non-zero if this function should
+     verify that the resulting type has the correct size.  */
+  struct type *get_base_type (cu_offset die_cu_off, int size);
 };
 
 /* Return the value of register number REG (a DWARF register number),
diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
index 682f2881556..69a3e424c6e 100644
--- a/gdb/dwarf2/frame.c
+++ b/gdb/dwarf2/frame.c
@@ -258,16 +258,6 @@ class dwarf_expr_executor : public dwarf_expr_context
     invalid ("DW_OP_call*");
   }
 
-  struct value *dwarf_variable_value (sect_offset sect_off) override
-  {
-    invalid ("DW_OP_GNU_variable_value");
-  }
-
-  CORE_ADDR get_addr_index (unsigned int index) override
-  {
-    invalid ("DW_OP_addrx or DW_OP_GNU_addr_index");
-  }
-
  private:
 
   void invalid (const char *op) ATTRIBUTE_NORETURN
diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c
index 2ad262dc802..1281a8a3d34 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -620,12 +620,10 @@ per_cu_dwarf_call (struct dwarf_expr_context *ctx, cu_offset die_offset,
   ctx->eval (block.data, block.size);
 }
 
-/* Given context CTX, section offset SECT_OFF, and compilation unit
-   data PER_CU, execute the "variable value" operation on the DIE
-   found at SECT_OFF.  */
+/* See loc.h.  */
 
-static struct value *
-sect_variable_value (struct dwarf_expr_context *ctx, sect_offset sect_off,
+struct value *
+sect_variable_value (sect_offset sect_off,
 		     dwarf2_per_cu_data *per_cu,
 		     dwarf2_per_objfile *per_objfile)
 {
@@ -654,7 +652,6 @@ class dwarf_evaluate_loc_desc : public dwarf_expr_context
     : dwarf_expr_context (per_objfile)
   {}
 
-  struct dwarf2_per_cu_data *per_cu;
   CORE_ADDR obj_address;
 
   /* Helper function for dwarf2_evaluate_loc_desc.  Computes the PC for
@@ -680,32 +677,6 @@ class dwarf_evaluate_loc_desc : public dwarf_expr_context
     per_cu_dwarf_call (this, die_offset, per_cu, per_objfile);
   }
 
-  /* Helper interface of sect_variable_value for
-     dwarf2_evaluate_loc_desc.  */
-
-  struct value *dwarf_variable_value (sect_offset sect_off) override
-  {
-    return sect_variable_value (this, sect_off, per_cu, per_objfile);
-  }
-
-  struct type *get_base_type (cu_offset die_offset, int size) override
-  {
-    struct type *result = dwarf2_get_die_type (die_offset, per_cu, per_objfile);
-    if (result == NULL)
-      error (_("Could not find type for DW_OP_const_type"));
-    if (size != 0 && TYPE_LENGTH (result) != size)
-      error (_("DW_OP_const_type has different sizes for type and data"));
-    return result;
-  }
-
-  /* Callback function for dwarf2_evaluate_loc_desc.
-     Fetch the address indexed by DW_OP_addrx or DW_OP_GNU_addr_index.  */
-
-  CORE_ADDR get_addr_index (unsigned int index) override
-  {
-    return dwarf2_read_addr_index (per_cu, per_objfile, index);
-  }
-
   /* Callback function for get_object_address. Return the address of the VLA
      object.  */
 
diff --git a/gdb/dwarf2/loc.h b/gdb/dwarf2/loc.h
index 2943baf91d2..a729b370e2e 100644
--- a/gdb/dwarf2/loc.h
+++ b/gdb/dwarf2/loc.h
@@ -53,6 +53,14 @@ extern void func_get_frame_base_dwarf_block (struct symbol *framefunc,
 					     const gdb_byte **start,
 					     size_t *length);
 
+/* Given section offset SECT_OFF, and compilation unit data
+   PER_CU, execute the "variable value" operation on the DIE
+   found at SECT_OFF.  */
+
+struct value *sect_variable_value (sect_offset sect_off,
+				   dwarf2_per_cu_data *per_cu,
+				   dwarf2_per_objfile *per_objfile);
+
 /* Evaluate a location description, starting at DATA and with length
    SIZE, to find the current location of variable of TYPE in the context
    of FRAME.  */
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 06/43] Move dwarf_call to dwarf_expr_context
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (4 preceding siblings ...)
  2021-03-01 14:45 ` [PATCH 05/43] Move compilation unit info to dwarf_expr_context Zoran Zaric
@ 2021-03-01 14:45 ` Zoran Zaric
  2021-03-01 14:45 ` [PATCH 07/43] Move get_object_address " Zoran Zaric
                   ` (36 subsequent siblings)
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:45 UTC (permalink / raw)
  To: gdb-patches

From: Zoran Zaric <zoran.zaric@amd.com>

Following the idea of merging the evaluators, the dwarf_call and
get_frame_pc method can be moved from dwarf_expr_executor and
dwarf_evaluate_loc_desc classes to their base class dwarf_expr_context.
Once this is done, the get_frame_pc can be replace with lambda
function.

gdb/ChangeLog:

	* dwarf2/expr.c (dwarf_expr_context::dwarf_call): Move from
	dwarf_evaluate_loc_desc.
	(dwarf_expr_context::get_frame_pc): Replace with lambda.
	* dwarf2/expr.h (dwarf_expr_context::get_frame_pc): Remove
	method.
	* dwarf2/frame.c (dwarf_expr_executor::dwarf_call): Remove
	method.
	(dwarf_expr_executor::get_frame_pc): Remove method.
	* dwarf2/loc.c (dwarf_evaluate_loc_desc::get_frame_pc): Remove
	method.
	(dwarf_evaluate_loc_desc::dwarf_call): Move to
	dwarf_expr_context.
	(per_cu_dwarf_call): Inline function.
---
 gdb/dwarf2/expr.c  | 28 +++++++++++++++++++++++++++-
 gdb/dwarf2/expr.h  | 22 ++++++----------------
 gdb/dwarf2/frame.c | 10 ----------
 gdb/dwarf2/loc.c   | 43 -------------------------------------------
 4 files changed, 33 insertions(+), 70 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 5a0a4a4299c..9b2c7485720 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -234,6 +234,31 @@ dwarf_expr_context::get_base_type (cu_offset die_cu_off, int size)
   return result;
 }
 
+/* See expr.h.  */
+
+void
+dwarf_expr_context::dwarf_call (cu_offset die_cu_off)
+{
+  ensure_have_per_cu (per_cu, "DW_OP_call");
+
+  struct frame_info *frame = this->frame;
+
+  auto get_pc_from_frame = [frame] ()
+    {
+      ensure_have_frame (frame, "DW_OP_call");
+      return get_frame_address_in_block (frame);
+    };
+
+  struct dwarf2_locexpr_baton block
+    = dwarf2_fetch_die_loc_cu_off (die_cu_off, per_cu, per_objfile,
+				   get_pc_from_frame);
+
+  /* DW_OP_call_ref is currently not supported.  */
+  gdb_assert (block.per_cu == per_cu);
+
+  this->eval (block.data, block.size);
+}
+
 /* Require that TYPE be an integral type; throw an exception if not.  */
 
 static void
@@ -1276,7 +1301,8 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	  returned.  */
 	  result = value_as_long (fetch (0));
 	  pop ();
-	  result = this->get_tls_address (result);
+	  result = target_translate_tls_address (this->per_objfile->objfile,
+						 result);
 	  result_val = value_from_ulongest (address_type, result);
 	  break;
 
diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
index 48dd37150db..9510dfe33d4 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -195,22 +195,6 @@ struct dwarf_expr_context
   /* Read LENGTH bytes at ADDR into BUF.  */
   virtual void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t length) = 0;
 
-  /* Return the PC for the frame.  */
-  virtual CORE_ADDR get_frame_pc ()
-  {
-    error (_("%s is invalid in this context"), "DW_OP_implicit_pointer");
-  }
-
-  /* Return the thread-local storage address for
-     DW_OP_GNU_push_tls_address or DW_OP_form_tls_address.  */
-  virtual CORE_ADDR get_tls_address (CORE_ADDR offset) = 0;
-
-  /* Execute DW_AT_location expression for the DWARF expression
-     subroutine in the DIE at DIE_CU_OFF in the CU.  Do not touch
-     STACK while it being passed to and returned from the called DWARF
-     subroutine.  */
-  virtual void dwarf_call (cu_offset die_cu_off) = 0;
-
   /* Push on DWARF stack an entry evaluated for DW_TAG_call_site's
      parameter matching KIND and KIND_U at the caller of specified BATON.
      If DEREF_SIZE is not -1 then use DW_AT_call_data_value instead of
@@ -247,6 +231,12 @@ struct dwarf_expr_context
      represent a base type.  SIZE is non-zero if this function should
      verify that the resulting type has the correct size.  */
   struct type *get_base_type (cu_offset die_cu_off, int size);
+
+  /* Execute DW_AT_location expression for the DWARF expression
+     subroutine in the DIE at DIE_CU_OFF in the CU.  Do not touch
+     STACK while it being passed to and returned from the called DWARF
+     subroutine.  */
+  void dwarf_call (cu_offset die_cu_off);
 };
 
 /* Return the value of register number REG (a DWARF register number),
diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
index 69a3e424c6e..f9f78f472cb 100644
--- a/gdb/dwarf2/frame.c
+++ b/gdb/dwarf2/frame.c
@@ -248,16 +248,6 @@ class dwarf_expr_executor : public dwarf_expr_context
     invalid ("DW_OP_push_object_address");
   }
 
-  CORE_ADDR get_tls_address (CORE_ADDR offset) override
-  {
-    invalid ("DW_OP_form_tls_address");
-  }
-
-  void dwarf_call (cu_offset die_offset) override
-  {
-    invalid ("DW_OP_call*");
-  }
-
  private:
 
   void invalid (const char *op) ATTRIBUTE_NORETURN
diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c
index 1281a8a3d34..6b0fd4c8a02 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -600,26 +600,6 @@ func_get_frame_base_dwarf_block (struct symbol *framefunc, CORE_ADDR pc,
 	   framefunc->natural_name ());
 }
 
-static void
-per_cu_dwarf_call (struct dwarf_expr_context *ctx, cu_offset die_offset,
-		   dwarf2_per_cu_data *per_cu, dwarf2_per_objfile *per_objfile)
-{
-  struct dwarf2_locexpr_baton block;
-
-  auto get_frame_pc_from_ctx = [ctx] ()
-    {
-      return ctx->get_frame_pc ();
-    };
-
-  block = dwarf2_fetch_die_loc_cu_off (die_offset, per_cu, per_objfile,
-				       get_frame_pc_from_ctx);
-
-  /* DW_OP_call_ref is currently not supported.  */
-  gdb_assert (block.per_cu == per_cu);
-
-  ctx->eval (block.data, block.size);
-}
-
 /* See loc.h.  */
 
 struct value *
@@ -654,29 +634,6 @@ class dwarf_evaluate_loc_desc : public dwarf_expr_context
 
   CORE_ADDR obj_address;
 
-  /* Helper function for dwarf2_evaluate_loc_desc.  Computes the PC for
-     the frame in BATON.  */
-
-  CORE_ADDR get_frame_pc () override
-  {
-    return get_frame_address_in_block (frame);
-  }
-
-  /* Using the objfile specified in BATON, find the address for the
-     current thread's thread-local storage with offset OFFSET.  */
-  CORE_ADDR get_tls_address (CORE_ADDR offset) override
-  {
-    return target_translate_tls_address (per_objfile->objfile, offset);
-  }
-
-  /* Helper interface of per_cu_dwarf_call for
-     dwarf2_evaluate_loc_desc.  */
-
-  void dwarf_call (cu_offset die_offset) override
-  {
-    per_cu_dwarf_call (this, die_offset, per_cu, per_objfile);
-  }
-
   /* Callback function for get_object_address. Return the address of the VLA
      object.  */
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 07/43] Move get_object_address to dwarf_expr_context
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (5 preceding siblings ...)
  2021-03-01 14:45 ` [PATCH 06/43] Move dwarf_call " Zoran Zaric
@ 2021-03-01 14:45 ` Zoran Zaric
  2021-04-27  3:12   ` Simon Marchi
  2021-03-01 14:45 ` [PATCH 08/43] Move read_mem " Zoran Zaric
                   ` (35 subsequent siblings)
  42 siblings, 1 reply; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:45 UTC (permalink / raw)
  To: gdb-patches

Following the idea of merging the evaluators, the get_object_address
and can be moved from dwarf_expr_executor and dwarf_evaluate_loc_desc
classes to their base class dwarf_expr_context.

gdb/ChangeLog:

	* dwarf2/expr.c (dwarf_expr_context::get_object_address): Move
	from dwarf_evaluate_loc_desc.
	(class dwarf_expr_context): Add object address member to
	dwarf_expr_context.
	* dwarf2/expr.h (dwarf_expr_context::get_frame_pc): Remove
	method.
	* dwarf2/frame.c (dwarf_expr_executor::get_object_address):
	Remove method.
	* dwarf2/loc.c (dwarf_evaluate_loc_desc::get_object_address):
	move to dwarf_expr_context.
	(class dwarf_evaluate_loc_desc): Move object address member to
	dwarf_expr_context.
---
 gdb/dwarf2/expr.h  | 10 +++++++++-
 gdb/dwarf2/frame.c |  5 -----
 gdb/dwarf2/loc.c   | 12 ------------
 3 files changed, 9 insertions(+), 18 deletions(-)

diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
index 9510dfe33d4..b25b2315dd9 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -192,6 +192,9 @@ struct dwarf_expr_context
   /* Compilation unit used for the evaluation.  */
   struct dwarf2_per_cu_data *per_cu = nullptr;
 
+  /* Object address used for the evaluation.  */
+  CORE_ADDR obj_address = 0;
+
   /* Read LENGTH bytes at ADDR into BUF.  */
   virtual void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t length) = 0;
 
@@ -204,7 +207,12 @@ struct dwarf_expr_context
 					   int deref_size) = 0;
 
   /* Return the `object address' for DW_OP_push_object_address.  */
-  virtual CORE_ADDR get_object_address () = 0;
+  virtual CORE_ADDR get_object_address ()
+  {
+    if (obj_address == 0)
+      error (_("Location address is not set."));
+    return obj_address;
+  }
 
 private:
 
diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
index f9f78f472cb..b3f0888e239 100644
--- a/gdb/dwarf2/frame.c
+++ b/gdb/dwarf2/frame.c
@@ -243,11 +243,6 @@ class dwarf_expr_executor : public dwarf_expr_context
     invalid ("DW_OP_entry_value");
   }
 
-  CORE_ADDR get_object_address () override
-  {
-    invalid ("DW_OP_push_object_address");
-  }
-
  private:
 
   void invalid (const char *op) ATTRIBUTE_NORETURN
diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c
index 6b0fd4c8a02..c716c3a7858 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -632,18 +632,6 @@ class dwarf_evaluate_loc_desc : public dwarf_expr_context
     : dwarf_expr_context (per_objfile)
   {}
 
-  CORE_ADDR obj_address;
-
-  /* Callback function for get_object_address. Return the address of the VLA
-     object.  */
-
-  CORE_ADDR get_object_address () override
-  {
-    if (obj_address == 0)
-      error (_("Location address is not set."));
-    return obj_address;
-  }
-
   /* Execute DWARF block of call_site_parameter which matches KIND and
      KIND_U.  Choose DEREF_SIZE value of that parameter.  Search
      caller of this objects's frame.
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 08/43] Move read_mem to dwarf_expr_context
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (6 preceding siblings ...)
  2021-03-01 14:45 ` [PATCH 07/43] Move get_object_address " Zoran Zaric
@ 2021-03-01 14:45 ` Zoran Zaric
  2021-03-01 14:45 ` [PATCH 09/43] Move push_dwarf_reg_entry_value to expr.c Zoran Zaric
                   ` (34 subsequent siblings)
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:45 UTC (permalink / raw)
  To: gdb-patches

From: Zoran Zaric <zoran.zaric@amd.com>

Following the idea of merging the evaluators, the read_mem method can
be moved from dwarf_expr_executor and dwarf_evaluate_loc_desc classes
to their base class dwarf_expr_context.

gdb/ChangeLog:

	* dwarf2/expr.c (dwarf_expr_context::read_mem): Move from
	dwarf_evaluate_loc_desc.
	* dwarf2/frame.c (dwarf_expr_executor::read_mem): Remove
	method.
	* dwarf2/loc.c (dwarf_evaluate_loc_desc::read_mem): Move to
	dwarf_expr_context.
---
 gdb/dwarf2/expr.c  | 9 +++++++++
 gdb/dwarf2/expr.h  | 2 +-
 gdb/dwarf2/frame.c | 5 -----
 gdb/dwarf2/loc.c   | 7 -------
 4 files changed, 10 insertions(+), 13 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 9b2c7485720..872de16cd70 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -259,6 +259,15 @@ dwarf_expr_context::dwarf_call (cu_offset die_cu_off)
   this->eval (block.data, block.size);
 }
 
+/* See expr.h.  */
+
+void
+dwarf_expr_context::read_mem (gdb_byte *buf, CORE_ADDR addr,
+			      size_t length)
+{
+  read_memory (addr, buf, length);
+}
+
 /* Require that TYPE be an integral type; throw an exception if not.  */
 
 static void
diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
index b25b2315dd9..713bdd94f27 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -196,7 +196,7 @@ struct dwarf_expr_context
   CORE_ADDR obj_address = 0;
 
   /* Read LENGTH bytes at ADDR into BUF.  */
-  virtual void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t length) = 0;
+  virtual void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t length);
 
   /* Push on DWARF stack an entry evaluated for DW_TAG_call_site's
      parameter matching KIND and KIND_U at the caller of specified BATON.
diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
index b3f0888e239..ce95a811abd 100644
--- a/gdb/dwarf2/frame.c
+++ b/gdb/dwarf2/frame.c
@@ -231,11 +231,6 @@ class dwarf_expr_executor : public dwarf_expr_context
     : dwarf_expr_context (per_objfile)
   {}
 
-  void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t len) override
-  {
-    read_memory (addr, buf, len);
-  }
-
   void push_dwarf_reg_entry_value (enum call_site_parameter_kind kind,
 				   union call_site_parameter_u kind_u,
 				   int deref_size) override
diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c
index c716c3a7858..9c4872436d7 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -686,13 +686,6 @@ class dwarf_evaluate_loc_desc : public dwarf_expr_context
 
     this->eval (data_src, size);
   }
-
-  /* Read memory at ADDR (length LEN) into BUF.  */
-
-  void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t len) override
-  {
-    read_memory (addr, buf, len);
-  }
 };
 
 /* See dwarf2loc.h.  */
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 09/43] Move push_dwarf_reg_entry_value to expr.c
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (7 preceding siblings ...)
  2021-03-01 14:45 ` [PATCH 08/43] Move read_mem " Zoran Zaric
@ 2021-03-01 14:45 ` Zoran Zaric
  2021-04-27  3:56   ` Simon Marchi
  2021-03-01 14:45 ` [PATCH 10/43] Inline get_reg_value method of dwarf_expr_context Zoran Zaric
                   ` (33 subsequent siblings)
  42 siblings, 1 reply; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:45 UTC (permalink / raw)
  To: gdb-patches

From: Zoran Zaric <zoran.zaric@amd.com>

Following the idea of merging the evaluators, the
push_dwarf_reg_entry_value method can be moved from
dwarf_expr_executor and dwarf_evaluate_loc_desc classes
to their base class dwarf_expr_context.

gdb/ChangeLog:

	* dwarf2/expr.c
        (dwarf_expr_context::push_dwarf_reg_entry_value): Move from
	dwarf_evaluate_loc_desc.
	* dwarf2/frame.c
	(dwarf_expr_executor::push_dwarf_reg_entry_value): Remove
	method.
	* dwarf2/loc.c (dwarf_expr_reg_to_entry_parameter): Expose
	function.
	(dwarf_evaluate_loc_desc::push_dwarf_reg_entry_value): Move to
	dwarf_expr_context.
	* dwarf2/loc.h (dwarf_expr_reg_to_entry_parameter): Expose
	function.
---
 gdb/dwarf2/expr.c  | 51 +++++++++++++++++++++++++++++++++
 gdb/dwarf2/expr.h  | 16 +++++------
 gdb/dwarf2/frame.c |  7 -----
 gdb/dwarf2/loc.c   | 70 ++--------------------------------------------
 gdb/dwarf2/loc.h   | 14 ++++++++++
 5 files changed, 75 insertions(+), 83 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 872de16cd70..0b7f3c90728 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -268,6 +268,57 @@ dwarf_expr_context::read_mem (gdb_byte *buf, CORE_ADDR addr,
   read_memory (addr, buf, length);
 }
 
+/* See expr.h.  */
+
+void
+dwarf_expr_context::push_dwarf_reg_entry_value
+		      (enum call_site_parameter_kind kind,
+		       union call_site_parameter_u kind_u,
+		       int deref_size)
+{
+  ensure_have_per_cu (per_cu, "DW_OP_entry_value");
+  ensure_have_frame (frame, "DW_OP_entry_value");
+
+  dwarf2_per_cu_data *caller_per_cu;
+  dwarf2_per_objfile *caller_per_objfile;
+  struct frame_info *caller_frame = get_prev_frame (frame);
+  struct call_site_parameter *parameter
+    = dwarf_expr_reg_to_entry_parameter (frame, kind, kind_u,
+					 &caller_per_cu,
+					 &caller_per_objfile);
+  const gdb_byte *data_src
+    = deref_size == -1 ? parameter->value : parameter->data_value;
+  size_t size
+    = deref_size == -1 ? parameter->value_size : parameter->data_value_size;
+
+  /* DEREF_SIZE size is not verified here.  */
+  if (data_src == NULL)
+    throw_error (NO_ENTRY_VALUE_ERROR,
+		 _("Cannot resolve DW_AT_call_data_value"));
+
+  /* We are about to evaluate an expression in the context of the caller
+     of the current frame.  This evaluation context may be different from
+     the current (callee's) context), so temporarily set the caller's context.
+
+     It is possible for the caller to be from a different objfile from the
+     callee if the call is made through a function pointer.  */
+  scoped_restore save_frame = make_scoped_restore (&this->frame,
+						   caller_frame);
+  scoped_restore save_per_cu = make_scoped_restore (&this->per_cu,
+						    caller_per_cu);
+  scoped_restore save_obj_addr = make_scoped_restore (&this->obj_address,
+						      (CORE_ADDR) 0);
+  scoped_restore save_per_objfile = make_scoped_restore (&this->per_objfile,
+							 caller_per_objfile);
+
+  scoped_restore save_arch = make_scoped_restore (&this->gdbarch);
+  this->gdbarch = this->per_objfile->objfile->arch ();
+  scoped_restore save_addr_size = make_scoped_restore (&this->addr_size);
+  this->addr_size = this->per_cu->addr_size ();
+
+  this->eval (data_src, size);
+}
+
 /* Require that TYPE be an integral type; throw an exception if not.  */
 
 static void
diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
index 713bdd94f27..09fec0b871e 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -198,14 +198,6 @@ struct dwarf_expr_context
   /* Read LENGTH bytes at ADDR into BUF.  */
   virtual void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t length);
 
-  /* Push on DWARF stack an entry evaluated for DW_TAG_call_site's
-     parameter matching KIND and KIND_U at the caller of specified BATON.
-     If DEREF_SIZE is not -1 then use DW_AT_call_data_value instead of
-     DW_AT_call_value.  */
-  virtual void push_dwarf_reg_entry_value (enum call_site_parameter_kind kind,
-					   union call_site_parameter_u kind_u,
-					   int deref_size) = 0;
-
   /* Return the `object address' for DW_OP_push_object_address.  */
   virtual CORE_ADDR get_object_address ()
   {
@@ -245,6 +237,14 @@ struct dwarf_expr_context
      STACK while it being passed to and returned from the called DWARF
      subroutine.  */
   void dwarf_call (cu_offset die_cu_off);
+
+  /* Push on DWARF stack an entry evaluated for DW_TAG_call_site's
+     parameter matching KIND and KIND_U at the caller of specified BATON.
+     If DEREF_SIZE is not -1 then use DW_AT_call_data_value instead of
+     DW_AT_call_value.  */
+  void push_dwarf_reg_entry_value (enum call_site_parameter_kind kind,
+				   union call_site_parameter_u kind_u,
+				   int deref_size);
 };
 
 /* Return the value of register number REG (a DWARF register number),
diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
index ce95a811abd..abbd24b8ed0 100644
--- a/gdb/dwarf2/frame.c
+++ b/gdb/dwarf2/frame.c
@@ -231,13 +231,6 @@ class dwarf_expr_executor : public dwarf_expr_context
     : dwarf_expr_context (per_objfile)
   {}
 
-  void push_dwarf_reg_entry_value (enum call_site_parameter_kind kind,
-				   union call_site_parameter_u kind_u,
-				   int deref_size) override
-  {
-    invalid ("DW_OP_entry_value");
-  }
-
  private:
 
   void invalid (const char *op) ATTRIBUTE_NORETURN
diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c
index 9c4872436d7..1779241d9dc 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -52,13 +52,6 @@ static struct value *dwarf2_evaluate_loc_desc_full
    size_t size, dwarf2_per_cu_data *per_cu, dwarf2_per_objfile *per_objfile,
    struct type *subobj_type, LONGEST subobj_byte_offset);
 
-static struct call_site_parameter *dwarf_expr_reg_to_entry_parameter
-    (struct frame_info *frame,
-     enum call_site_parameter_kind kind,
-     union call_site_parameter_u kind_u,
-     dwarf2_per_cu_data **per_cu_return,
-     dwarf2_per_objfile **per_objfile_return);
-
 static struct value *indirect_synthetic_pointer
   (sect_offset die, LONGEST byte_offset,
    dwarf2_per_cu_data *per_cu,
@@ -631,61 +624,6 @@ class dwarf_evaluate_loc_desc : public dwarf_expr_context
   dwarf_evaluate_loc_desc (dwarf2_per_objfile *per_objfile)
     : dwarf_expr_context (per_objfile)
   {}
-
-  /* Execute DWARF block of call_site_parameter which matches KIND and
-     KIND_U.  Choose DEREF_SIZE value of that parameter.  Search
-     caller of this objects's frame.
-
-     The caller can be from a different CU - per_cu_dwarf_call
-     implementation can be more simple as it does not support cross-CU
-     DWARF executions.  */
-
-  void push_dwarf_reg_entry_value (enum call_site_parameter_kind kind,
-				   union call_site_parameter_u kind_u,
-				   int deref_size) override
-  {
-    struct frame_info *caller_frame;
-    dwarf2_per_cu_data *caller_per_cu;
-    dwarf2_per_objfile *caller_per_objfile;
-    struct call_site_parameter *parameter;
-    const gdb_byte *data_src;
-    size_t size;
-
-    caller_frame = get_prev_frame (frame);
-
-    parameter = dwarf_expr_reg_to_entry_parameter (frame, kind, kind_u,
-						   &caller_per_cu,
-						   &caller_per_objfile);
-    data_src = deref_size == -1 ? parameter->value : parameter->data_value;
-    size = deref_size == -1 ? parameter->value_size : parameter->data_value_size;
-
-    /* DEREF_SIZE size is not verified here.  */
-    if (data_src == NULL)
-      throw_error (NO_ENTRY_VALUE_ERROR,
-		   _("Cannot resolve DW_AT_call_data_value"));
-
-    /* We are about to evaluate an expression in the context of the caller
-       of the current frame.  This evaluation context may be different from
-       the current (callee's) context), so temporarily set the caller's context.
-
-       It is possible for the caller to be from a different objfile from the
-       callee if the call is made through a function pointer.  */
-    scoped_restore save_frame = make_scoped_restore (&this->frame,
-						     caller_frame);
-    scoped_restore save_per_cu = make_scoped_restore (&this->per_cu,
-						      caller_per_cu);
-    scoped_restore save_obj_addr = make_scoped_restore (&this->obj_address,
-							(CORE_ADDR) 0);
-    scoped_restore save_per_objfile = make_scoped_restore (&this->per_objfile,
-							   caller_per_objfile);
-
-    scoped_restore save_arch = make_scoped_restore (&this->gdbarch);
-    this->gdbarch = this->per_objfile->objfile->arch ();
-    scoped_restore save_addr_size = make_scoped_restore (&this->addr_size);
-    this->addr_size = this->per_cu->addr_size ();
-
-    this->eval (data_src, size);
-  }
 };
 
 /* See dwarf2loc.h.  */
@@ -1166,13 +1104,9 @@ call_site_parameter_matches (struct call_site_parameter *parameter,
   return 0;
 }
 
-/* Fetch call_site_parameter from caller matching KIND and KIND_U.
-   FRAME is for callee.
-
-   Function always returns non-NULL, it throws NO_ENTRY_VALUE_ERROR
-   otherwise.  */
+/* See loc.h.  */
 
-static struct call_site_parameter *
+struct call_site_parameter *
 dwarf_expr_reg_to_entry_parameter (struct frame_info *frame,
 				   enum call_site_parameter_kind kind,
 				   union call_site_parameter_u kind_u,
diff --git a/gdb/dwarf2/loc.h b/gdb/dwarf2/loc.h
index a729b370e2e..baee63ffccb 100644
--- a/gdb/dwarf2/loc.h
+++ b/gdb/dwarf2/loc.h
@@ -61,6 +61,20 @@ struct value *sect_variable_value (sect_offset sect_off,
 				   dwarf2_per_cu_data *per_cu,
 				   dwarf2_per_objfile *per_objfile);
 
+/* Fetch call_site_parameter from caller matching KIND and KIND_U.
+   FRAME is for callee.
+
+   Function always returns non-NULL, it throws NO_ENTRY_VALUE_ERROR
+   otherwise.  */
+
+struct call_site_parameter *dwarf_expr_reg_to_entry_parameter
+    (struct frame_info *frame,
+     enum call_site_parameter_kind kind,
+     union call_site_parameter_u kind_u,
+     dwarf2_per_cu_data **per_cu_return,
+     dwarf2_per_objfile **per_objfile_return);
+
+
 /* Evaluate a location description, starting at DATA and with length
    SIZE, to find the current location of variable of TYPE in the context
    of FRAME.  */
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 10/43] Inline get_reg_value method of dwarf_expr_context
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (8 preceding siblings ...)
  2021-03-01 14:45 ` [PATCH 09/43] Move push_dwarf_reg_entry_value to expr.c Zoran Zaric
@ 2021-03-01 14:45 ` Zoran Zaric
  2021-03-01 14:45 ` [PATCH 11/43] Remove empty frame and full evaluators Zoran Zaric
                   ` (32 subsequent siblings)
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:45 UTC (permalink / raw)
  To: gdb-patches

The get_reg_value method is a small function that is only called once,
so it can be inlined to simplify the dwarf_expr_context class.

gdb/ChangeLog:

	* dwarf2/expr.c (dwarf_expr_context::get_reg_value): Remove
	method.
	(dwarf_expr_context::execute_stack_op): Inline get_reg_value
	method.
	* dwarf2/expr.h (dwarf_expr_context::get_reg_value): Remove
	method.
---
 gdb/dwarf2/expr.c | 24 +++++++-----------------
 gdb/dwarf2/expr.h |  6 ------
 2 files changed, 7 insertions(+), 23 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 0b7f3c90728..1212be9c984 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -176,19 +176,6 @@ dwarf_expr_context::fetch (int n)
 
 /* See expr.h.  */
 
-struct value *
-dwarf_expr_context::get_reg_value (struct type *type, int reg)
-{
-  ensure_have_frame (frame, "DW_OP_regval_type");
-
-  struct gdbarch *gdbarch = get_frame_arch (frame);
-  int regnum = dwarf_reg_to_regnum_or_error (gdbarch, reg);
-
-  return value_from_register (type, regnum, frame);
-}
-
-/* See expr.h.  */
-
 void
 dwarf_expr_context::get_frame_base (const gdb_byte **start,
 				    size_t * length)
@@ -1539,14 +1526,17 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	case DW_OP_regval_type:
 	case DW_OP_GNU_regval_type:
 	  {
-	    struct type *type;
-
 	    op_ptr = safe_read_uleb128 (op_ptr, op_end, &reg);
 	    op_ptr = safe_read_uleb128 (op_ptr, op_end, &uoffset);
 	    cu_offset type_die_cu_off = (cu_offset) uoffset;
 
-	    type = get_base_type (type_die_cu_off, 0);
-	    result_val = this->get_reg_value (type, reg);
+	    ensure_have_frame (this->frame, "DW_OP_regval_type");
+
+	    struct type *type = get_base_type (type_die_cu_off, 0);
+	    int regnum
+	      = dwarf_reg_to_regnum_or_error (get_frame_arch (this->frame),
+					      reg);
+	    result_val = value_from_register (type, regnum, this->frame);
 	  }
 	  break;
 
diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
index 09fec0b871e..3faedebfb83 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -215,12 +215,6 @@ struct dwarf_expr_context
   void execute_stack_op (const gdb_byte *op_ptr, const gdb_byte *op_end);
   void pop ();
 
-  /* Return a value of type TYPE, stored in register number REGNUM
-     of the frame associated to the given BATON.
-
-     REGNUM is a DWARF register number.  */
-  struct value *get_reg_value (struct type *type, int regnum);
-
   /* Return the location expression for the frame base attribute, in
      START and LENGTH.  The result must be live until the current
      expression evaluation is complete.  */
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 11/43] Remove empty frame and full evaluators
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (9 preceding siblings ...)
  2021-03-01 14:45 ` [PATCH 10/43] Inline get_reg_value method of dwarf_expr_context Zoran Zaric
@ 2021-03-01 14:45 ` Zoran Zaric
  2021-03-01 14:45 ` [PATCH 12/43] Merge evaluate_for_locexpr_baton evaluator Zoran Zaric
                   ` (31 subsequent siblings)
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:45 UTC (permalink / raw)
  To: gdb-patches

From: Zoran Zaric <zoran.zaric@amd.com>

There are no virtual methods that require different specialization in
dwarf_expr_context class. This means that derived classes
dwarf_expr_executor and dwarf_evaluate_loc_desc are not needed any
more.

As a result of this, the  evaluate_for_locexpr_baton class base class
is now the dwarf_expr_context class.

There might be a need for a better class hierarchy when we know more
about the direction of the future DWARF versions and gdb extensions,
but that is out of the scope of this patch series.

gdb/ChangeLog:

	* dwarf2/frame.c (class dwarf_expr_executor): Remove class.
	(execute_stack_op): Instantiate dwarf_expr_context instead of
	dwarf_evaluate_loc_desc class.
	* dwarf2/loc.c (class dwarf_evaluate_loc_desc): Remove class.
	(dwarf2_evaluate_loc_desc_full): Instantiate dwarf_expr_context
	instead of dwarf_evaluate_loc_desc class.
	(struct evaluate_for_locexpr_baton): Derive from
	dwarf_expr_context.
---
 gdb/dwarf2/frame.c | 18 +-----------------
 gdb/dwarf2/loc.c   | 16 ++++------------
 2 files changed, 5 insertions(+), 29 deletions(-)

diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
index abbd24b8ed0..8faadeb3469 100644
--- a/gdb/dwarf2/frame.c
+++ b/gdb/dwarf2/frame.c
@@ -223,22 +223,6 @@ register %s (#%d) at %s"),
     }
 }
 
-class dwarf_expr_executor : public dwarf_expr_context
-{
-public:
-
-  dwarf_expr_executor (dwarf2_per_objfile *per_objfile)
-    : dwarf_expr_context (per_objfile)
-  {}
-
- private:
-
-  void invalid (const char *op) ATTRIBUTE_NORETURN
-  {
-    error (_("%s is invalid in this context"), op);
-  }
-};
-
 static CORE_ADDR
 execute_stack_op (const gdb_byte *exp, ULONGEST len, int addr_size,
 		  struct frame_info *this_frame, CORE_ADDR initial,
@@ -246,7 +230,7 @@ execute_stack_op (const gdb_byte *exp, ULONGEST len, int addr_size,
 {
   CORE_ADDR result;
 
-  dwarf_expr_executor ctx (per_objfile);
+  dwarf_expr_context ctx (per_objfile);
   scoped_value_mark free_values;
 
   ctx.frame = this_frame;
diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c
index 1779241d9dc..bd3f7d4d46a 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -618,14 +618,6 @@ sect_variable_value (sect_offset sect_off,
 				     type, true);
 }
 
-class dwarf_evaluate_loc_desc : public dwarf_expr_context
-{
-public:
-  dwarf_evaluate_loc_desc (dwarf2_per_objfile *per_objfile)
-    : dwarf_expr_context (per_objfile)
-  {}
-};
-
 /* See dwarf2loc.h.  */
 
 unsigned int entry_values_debug = 0;
@@ -2029,7 +2021,7 @@ dwarf2_evaluate_loc_desc_full (struct type *type, struct frame_info *frame,
   if (size == 0)
     return allocate_optimized_out_value (subobj_type);
 
-  dwarf_evaluate_loc_desc ctx (per_objfile);
+  dwarf_expr_context ctx (per_objfile);
   ctx.frame = frame;
   ctx.per_cu = per_cu;
   ctx.obj_address = 0;
@@ -2229,16 +2221,16 @@ dwarf2_evaluate_loc_desc (struct type *type, struct frame_info *frame,
 					per_objfile, NULL, 0);
 }
 
-/* A specialization of dwarf_evaluate_loc_desc that is used by
+/* A specialization of dwarf_expr_context that is used by
    dwarf2_locexpr_baton_eval.  This subclass exists to handle the case
    where a caller of dwarf2_locexpr_baton_eval passes in some data,
    but with the address being 0.  In this situation, we arrange for
    memory reads to come from the passed-in buffer.  */
 
-struct evaluate_for_locexpr_baton : public dwarf_evaluate_loc_desc
+struct evaluate_for_locexpr_baton : public dwarf_expr_context
 {
   evaluate_for_locexpr_baton (dwarf2_per_objfile *per_objfile)
-    : dwarf_evaluate_loc_desc (per_objfile)
+    : dwarf_expr_context (per_objfile)
   {}
 
   /* The data that was passed in.  */
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 12/43] Merge evaluate_for_locexpr_baton evaluator
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (10 preceding siblings ...)
  2021-03-01 14:45 ` [PATCH 11/43] Remove empty frame and full evaluators Zoran Zaric
@ 2021-03-01 14:45 ` Zoran Zaric
  2021-04-28  1:33   ` Simon Marchi
  2021-03-01 14:45 ` [PATCH 13/43] Move piece_closure and its support to expr.c Zoran Zaric
                   ` (30 subsequent siblings)
  42 siblings, 1 reply; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:45 UTC (permalink / raw)
  To: gdb-patches

From: Zoran Zaric <zoran.zaric@amd.com>

The evaluate_for_locexpr_baton is the last derived class from the
dwarf_expr_context class. It's purpose is to support the passed in
buffer functionality.

Although, it is not really necessary to merge this class with it's
base class, doing that simplifies new expression evaluator design.

Considering that this functionality is going around the DWARF standard,
it is also reasonable to expect that with a new evaluator design and
extending the push object address functionality to accept any location
description, there will be no need to support passed in buffers.

Alternatively, it would also makes sense to abstract the interaction
between the evaluator and a given resource in the near future. The
passed in buffer would then be a specialization of that abstraction.

gdb/ChangeLog:

	* dwarf2/expr.c (dwarf_expr_context::read_mem): Merge with
	evaluate_for_locexpr_baton implementation.
	* dwarf2/loc.c (class evaluate_for_locexpr_baton): Remove
	class.
	(evaluate_for_locexpr_baton::read_mem): Move to
	dwarf_expr_context.
	(dwarf2_locexpr_baton_eval): Instantiate dwarf_expr_context
	instead of evaluate_for_locexpr_baton class.
---
 gdb/dwarf2/expr.c | 19 +++++++++++++++++--
 gdb/dwarf2/expr.h | 13 +++----------
 gdb/dwarf2/loc.c  | 45 ++-------------------------------------------
 3 files changed, 22 insertions(+), 55 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 1212be9c984..fe6d072ac8f 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -252,6 +252,18 @@ void
 dwarf_expr_context::read_mem (gdb_byte *buf, CORE_ADDR addr,
 			      size_t length)
 {
+  if (length == 0)
+    return;
+
+  /* Prefer the passed-in memory, if it exists.  */
+  CORE_ADDR offset = addr - obj_address;
+
+  if (offset < data_view.size () && offset + length <= data_view.size ())
+    {
+      memcpy (buf, data_view.data (), length);
+      return;
+    }
+
   read_memory (addr, buf, length);
 }
 
@@ -1576,8 +1588,11 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 
 	case DW_OP_push_object_address:
 	  /* Return the address of the object we are currently observing.  */
-	  result = this->get_object_address ();
-	  result_val = value_from_ulongest (address_type, result);
+	  if (this->data_view.data () == nullptr
+	      && this->obj_address == 0)
+	    error (_("Location address is not set."));
+
+	  result_val = value_from_ulongest (address_type, this->obj_address);
 	  break;
 
 	default:
diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
index 3faedebfb83..af14f6ee0c2 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -195,16 +195,8 @@ struct dwarf_expr_context
   /* Object address used for the evaluation.  */
   CORE_ADDR obj_address = 0;
 
-  /* Read LENGTH bytes at ADDR into BUF.  */
-  virtual void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t length);
-
-  /* Return the `object address' for DW_OP_push_object_address.  */
-  virtual CORE_ADDR get_object_address ()
-  {
-    if (obj_address == 0)
-      error (_("Location address is not set."));
-    return obj_address;
-  }
+  /* The data that was passed in.  */
+  gdb::array_view<const gdb_byte> data_view;
 
 private:
 
@@ -239,6 +231,7 @@ struct dwarf_expr_context
   void push_dwarf_reg_entry_value (enum call_site_parameter_kind kind,
 				   union call_site_parameter_u kind_u,
 				   int deref_size);
+  void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t length);
 };
 
 /* Return the value of register number REG (a DWARF register number),
diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c
index bd3f7d4d46a..d02fb1cd792 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -2221,45 +2221,6 @@ dwarf2_evaluate_loc_desc (struct type *type, struct frame_info *frame,
 					per_objfile, NULL, 0);
 }
 
-/* A specialization of dwarf_expr_context that is used by
-   dwarf2_locexpr_baton_eval.  This subclass exists to handle the case
-   where a caller of dwarf2_locexpr_baton_eval passes in some data,
-   but with the address being 0.  In this situation, we arrange for
-   memory reads to come from the passed-in buffer.  */
-
-struct evaluate_for_locexpr_baton : public dwarf_expr_context
-{
-  evaluate_for_locexpr_baton (dwarf2_per_objfile *per_objfile)
-    : dwarf_expr_context (per_objfile)
-  {}
-
-  /* The data that was passed in.  */
-  gdb::array_view<const gdb_byte> data_view;
-
-  CORE_ADDR get_object_address () override
-  {
-    if (data_view.data () == nullptr && obj_address == 0)
-      error (_("Location address is not set."));
-    return obj_address;
-  }
-
-  void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t len) override
-  {
-    if (len == 0)
-      return;
-
-    /* Prefer the passed-in memory, if it exists.  */
-    CORE_ADDR offset = addr - obj_address;
-    if (offset < data_view.size () && offset + len <= data_view.size ())
-      {
-	memcpy (buf, data_view.data (), len);
-	return;
-      }
-
-    read_memory (addr, buf, len);
-  }
-};
-
 /* Evaluates a dwarf expression and stores the result in VAL,
    expecting that the dwarf expression only produces a single
    CORE_ADDR.  FRAME is the frame in which the expression is
@@ -2283,13 +2244,11 @@ dwarf2_locexpr_baton_eval (const struct dwarf2_locexpr_baton *dlbaton,
     return 0;
 
   dwarf2_per_objfile *per_objfile = dlbaton->per_objfile;
-  evaluate_for_locexpr_baton ctx (per_objfile);
+  dwarf_expr_context ctx (per_objfile);
 
   ctx.frame = frame;
   ctx.per_cu = dlbaton->per_cu;
-  if (addr_stack == nullptr)
-    ctx.obj_address = 0;
-  else
+  if (addr_stack != nullptr)
     {
       ctx.obj_address = addr_stack->addr;
       ctx.data_view = addr_stack->valaddr;
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 13/43] Move piece_closure and its support to expr.c
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (11 preceding siblings ...)
  2021-03-01 14:45 ` [PATCH 12/43] Merge evaluate_for_locexpr_baton evaluator Zoran Zaric
@ 2021-03-01 14:45 ` Zoran Zaric
  2021-04-28  1:56   ` Simon Marchi
  2021-03-01 14:45 ` [PATCH 14/43] Make value_copy also copy the stack data member Zoran Zaric
                   ` (29 subsequent siblings)
  42 siblings, 1 reply; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:45 UTC (permalink / raw)
  To: gdb-patches

From: Zoran Zaric <zoran.zaric@amd.com>

Following 5 patches series is trying to clean up the interface of the
DWARF expression evaluator class (dwarf_expr_context).

After merging all expression evaluators into one class, the next
logical step is to make a clean user interface for that class. To do
that, we first need to address the issue of class users writing and
reading the internal data of the class directly.

Fixing the case of writing is simple, it makes sense for an evaluator
instance to be per architecture basis. Currently, the best separation
seems to be per object file, so having that data (dwarf2_per_objfile)
as a constructor argument makes sense. It also makes sense to get the
address size from that object file, but unfortunately that interface
does not exist at the moment.

Luckily, address size information is already available to the users
through other means. As a result, the address size also needs to be a
class constructor argument, at least until a better interface for
acquiring that information from an object file is implemented.

The rest of the user written data comes down to a context of an
evaluated expression (compilation unit context, frame context and
passed in buffer context) and a source type information that a result
of evaluating expression is representing. So, it makes sense for all of
these to be arguments of an evaluation method.

To address the problem of reading the dwarf_expr_context class
internal data, we first need to understand why it is implemented that
way?

This is actualy a question of which existing class can be used to
represent both values and a location descriptions and why it is not
used currently?

The answer is in a struct value class/structure, but the problem is
that before the evaluators were merged, only one evaluator had an
infrastructure to resolve composite and implicit pointer location
descriptions.

After the merge, we are now able to use the struct value to represent
any result of the expression evaluation. It also makes sense to move
all infrastructure for those location descriptions to the expr.c file
considering that that is the only place using that infrastructure.

What we are left with in the end is a clean public interface of the
dwarf_expr_context class containing:

- constructor,
- destructor,
- push_address method and
- eval_exp method.

The idea with this particular patch is to move piece_closure structure
and the interface that handles it (lval_funcs) to expr.c file.

While implicit pointer location descriptions are still not useful in
the CFI context (of the AMD’s DWARF standard extensions), the composite
location descriptions are certainly necessary to describe a results of
specific compiler optimizations.

Considering that a piece_closure structure is used to represent both,
there was no benefit in splitting them.

gdb/ChangeLog:

	* dwarf2/expr.c (struct piece_closure): Add from loc.c.
	(allocate_piece_closure): Add from loc.c.
	(bits_to_bytes): Add from loc.c.
	(rw_pieced_value): Add from loc.c.
	(read_pieced_value): Add from loc.c.
	(write_pieced_value): Add from loc.c.
	(check_pieced_synthetic_pointer): Add from loc.c.
	(indirect_pieced_value): Add from loc.c.
	(coerce_pieced_ref): Add from loc.c.
	(copy_pieced_value_closure): Add from loc.c.
	(free_pieced_value_closure): Add from loc.c.
	(sect_variable_value): Add from loc.c.
	* dwarf2/loc.c (sect_variable_value): Move to expr.c.
	(struct piece_closure): Move to expr.c.
	(allocate_piece_closure): Move to expr.c.
	(bits_to_bytes): Move to expr.c.
	(rw_pieced_value): Move to expr.c.
	(read_pieced_value): Move to expr.c.
	(write_pieced_value): Move to expr.c.
	(check_pieced_synthetic_pointer): Move to expr.c.
	(indirect_pieced_value): Move to expr.c.
	(coerce_pieced_ref): Move to expr.c.
	(copy_pieced_value_closure): Move to expr.c.
	(free_pieced_value_closure): Move to expr.c.
---
 gdb/dwarf2/expr.c | 568 ++++++++++++++++++++++++++++++++++++++++++++
 gdb/dwarf2/expr.h |  12 +
 gdb/dwarf2/loc.c  | 584 +---------------------------------------------
 gdb/dwarf2/loc.h  |  17 +-
 4 files changed, 592 insertions(+), 589 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index fe6d072ac8f..486521b5e04 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -86,6 +86,15 @@ ensure_have_per_cu (struct dwarf2_per_cu_data *per_cu, const char* op_name)
 		 _("%s evaluation requires a compilation unit."), op_name);
 }
 
+/* Return the number of bytes overlapping a contiguous chunk of N_BITS
+   bits whose first bit is located at bit offset START.  */
+
+static size_t
+bits_to_bytes (ULONGEST start, ULONGEST n_bits)
+{
+  return (start % HOST_CHAR_BIT + n_bits + HOST_CHAR_BIT - 1) / HOST_CHAR_BIT;
+}
+
 /* See expr.h.  */
 
 CORE_ADDR
@@ -97,6 +106,565 @@ read_addr_from_reg (struct frame_info *frame, int reg)
   return address_from_register (regnum, frame);
 }
 
+struct piece_closure
+{
+  /* Reference count.  */
+  int refc = 0;
+
+  /* The objfile from which this closure's expression came.  */
+  dwarf2_per_objfile *per_objfile = nullptr;
+
+  /* The CU from which this closure's expression came.  */
+  struct dwarf2_per_cu_data *per_cu = NULL;
+
+  /* The pieces describing this variable.  */
+  std::vector<dwarf_expr_piece> pieces;
+
+  /* Frame ID of frame to which a register value is relative, used
+     only by DWARF_VALUE_REGISTER.  */
+  struct frame_id frame_id;
+};
+
+/* See expr.h.  */
+
+struct piece_closure *
+allocate_piece_closure (dwarf2_per_cu_data *per_cu,
+			dwarf2_per_objfile *per_objfile,
+			std::vector<dwarf_expr_piece> &&pieces,
+			struct frame_info *frame)
+{
+  struct piece_closure *c = new piece_closure;
+
+  c->refc = 1;
+  /* We must capture this here due to sharing of DWARF state.  */
+  c->per_objfile = per_objfile;
+  c->per_cu = per_cu;
+  c->pieces = std::move (pieces);
+  if (frame == NULL)
+    c->frame_id = null_frame_id;
+  else
+    c->frame_id = get_frame_id (frame);
+
+  for (dwarf_expr_piece &piece : c->pieces)
+    if (piece.location == DWARF_VALUE_STACK)
+      value_incref (piece.v.value);
+
+  return c;
+}
+
+/* Read or write a pieced value V.  If FROM != NULL, operate in "write
+   mode": copy FROM into the pieces comprising V.  If FROM == NULL,
+   operate in "read mode": fetch the contents of the (lazy) value V by
+   composing it from its pieces.  */
+
+static void
+rw_pieced_value (struct value *v, struct value *from)
+{
+  int i;
+  LONGEST offset = 0, max_offset;
+  ULONGEST bits_to_skip;
+  gdb_byte *v_contents;
+  const gdb_byte *from_contents;
+  struct piece_closure *c
+    = (struct piece_closure *) value_computed_closure (v);
+  gdb::byte_vector buffer;
+  bool bits_big_endian = type_byte_order (value_type (v)) == BFD_ENDIAN_BIG;
+
+  if (from != NULL)
+    {
+      from_contents = value_contents (from);
+      v_contents = NULL;
+    }
+  else
+    {
+      if (value_type (v) != value_enclosing_type (v))
+	internal_error (__FILE__, __LINE__,
+			_("Should not be able to create a lazy value with "
+			  "an enclosing type"));
+      v_contents = value_contents_raw (v);
+      from_contents = NULL;
+    }
+
+  bits_to_skip = 8 * value_offset (v);
+  if (value_bitsize (v))
+    {
+      bits_to_skip += (8 * value_offset (value_parent (v))
+		       + value_bitpos (v));
+      if (from != NULL
+	  && (type_byte_order (value_type (from))
+	      == BFD_ENDIAN_BIG))
+	{
+	  /* Use the least significant bits of FROM.  */
+	  max_offset = 8 * TYPE_LENGTH (value_type (from));
+	  offset = max_offset - value_bitsize (v);
+	}
+      else
+	max_offset = value_bitsize (v);
+    }
+  else
+    max_offset = 8 * TYPE_LENGTH (value_type (v));
+
+  /* Advance to the first non-skipped piece.  */
+  for (i = 0; i < c->pieces.size () && bits_to_skip >= c->pieces[i].size; i++)
+    bits_to_skip -= c->pieces[i].size;
+
+  for (; i < c->pieces.size () && offset < max_offset; i++)
+    {
+      struct dwarf_expr_piece *p = &c->pieces[i];
+      size_t this_size_bits, this_size;
+
+      this_size_bits = p->size - bits_to_skip;
+      if (this_size_bits > max_offset - offset)
+	this_size_bits = max_offset - offset;
+
+      switch (p->location)
+	{
+	case DWARF_VALUE_REGISTER:
+	  {
+	    struct frame_info *frame = frame_find_by_id (c->frame_id);
+	    struct gdbarch *arch = get_frame_arch (frame);
+	    int gdb_regnum = dwarf_reg_to_regnum_or_error (arch, p->v.regno);
+	    ULONGEST reg_bits = 8 * register_size (arch, gdb_regnum);
+	    int optim, unavail;
+
+	    if (gdbarch_byte_order (arch) == BFD_ENDIAN_BIG
+		&& p->offset + p->size < reg_bits)
+	      {
+		/* Big-endian, and we want less than full size.  */
+		bits_to_skip += reg_bits - (p->offset + p->size);
+	      }
+	    else
+	      bits_to_skip += p->offset;
+
+	    this_size = bits_to_bytes (bits_to_skip, this_size_bits);
+	    buffer.resize (this_size);
+
+	    if (from == NULL)
+	      {
+		/* Read mode.  */
+		if (!get_frame_register_bytes (frame, gdb_regnum,
+					       bits_to_skip / 8,
+					       buffer, &optim, &unavail))
+		  {
+		    if (optim)
+		      mark_value_bits_optimized_out (v, offset,
+						     this_size_bits);
+		    if (unavail)
+		      mark_value_bits_unavailable (v, offset,
+						   this_size_bits);
+		    break;
+		  }
+
+		copy_bitwise (v_contents, offset,
+			      buffer.data (), bits_to_skip % 8,
+			      this_size_bits, bits_big_endian);
+	      }
+	    else
+	      {
+		/* Write mode.  */
+		if (bits_to_skip % 8 != 0 || this_size_bits % 8 != 0)
+		  {
+		    /* Data is copied non-byte-aligned into the register.
+		       Need some bits from original register value.  */
+		    get_frame_register_bytes (frame, gdb_regnum,
+					      bits_to_skip / 8,
+					      buffer, &optim, &unavail);
+		    if (optim)
+		      throw_error (OPTIMIZED_OUT_ERROR,
+				   _("Can't do read-modify-write to "
+				     "update bitfield; containing word "
+				     "has been optimized out"));
+		    if (unavail)
+		      throw_error (NOT_AVAILABLE_ERROR,
+				   _("Can't do read-modify-write to "
+				     "update bitfield; containing word "
+				     "is unavailable"));
+		  }
+
+		copy_bitwise (buffer.data (), bits_to_skip % 8,
+			      from_contents, offset,
+			      this_size_bits, bits_big_endian);
+		put_frame_register_bytes (frame, gdb_regnum,
+					  bits_to_skip / 8,
+					  buffer);
+	      }
+	  }
+	  break;
+
+	case DWARF_VALUE_MEMORY:
+	  {
+	    bits_to_skip += p->offset;
+
+	    CORE_ADDR start_addr = p->v.mem.addr + bits_to_skip / 8;
+
+	    if (bits_to_skip % 8 == 0 && this_size_bits % 8 == 0
+		&& offset % 8 == 0)
+	      {
+		/* Everything is byte-aligned; no buffer needed.  */
+		if (from != NULL)
+		  write_memory_with_notification (start_addr,
+						  (from_contents
+						   + offset / 8),
+						  this_size_bits / 8);
+		else
+		  read_value_memory (v, offset,
+				     p->v.mem.in_stack_memory,
+				     p->v.mem.addr + bits_to_skip / 8,
+				     v_contents + offset / 8,
+				     this_size_bits / 8);
+		break;
+	      }
+
+	    this_size = bits_to_bytes (bits_to_skip, this_size_bits);
+	    buffer.resize (this_size);
+
+	    if (from == NULL)
+	      {
+		/* Read mode.  */
+		read_value_memory (v, offset,
+				   p->v.mem.in_stack_memory,
+				   p->v.mem.addr + bits_to_skip / 8,
+				   buffer.data (), this_size);
+		copy_bitwise (v_contents, offset,
+			      buffer.data (), bits_to_skip % 8,
+			      this_size_bits, bits_big_endian);
+	      }
+	    else
+	      {
+		/* Write mode.  */
+		if (bits_to_skip % 8 != 0 || this_size_bits % 8 != 0)
+		  {
+		    if (this_size <= 8)
+		      {
+			/* Perform a single read for small sizes.  */
+			read_memory (start_addr, buffer.data (),
+				     this_size);
+		      }
+		    else
+		      {
+			/* Only the first and last bytes can possibly have
+			   any bits reused.  */
+			read_memory (start_addr, buffer.data (), 1);
+			read_memory (start_addr + this_size - 1,
+				     &buffer[this_size - 1], 1);
+		      }
+		  }
+
+		copy_bitwise (buffer.data (), bits_to_skip % 8,
+			      from_contents, offset,
+			      this_size_bits, bits_big_endian);
+		write_memory_with_notification (start_addr,
+						buffer.data (),
+						this_size);
+	      }
+	  }
+	  break;
+
+	case DWARF_VALUE_STACK:
+	  {
+	    if (from != NULL)
+	      {
+		mark_value_bits_optimized_out (v, offset, this_size_bits);
+		break;
+	      }
+
+	    gdbarch *objfile_gdbarch = c->per_objfile->objfile->arch ();
+	    ULONGEST stack_value_size_bits
+	      = 8 * TYPE_LENGTH (value_type (p->v.value));
+
+	    /* Use zeroes if piece reaches beyond stack value.  */
+	    if (p->offset + p->size > stack_value_size_bits)
+	      break;
+
+	    /* Piece is anchored at least significant bit end.  */
+	    if (gdbarch_byte_order (objfile_gdbarch) == BFD_ENDIAN_BIG)
+	      bits_to_skip += stack_value_size_bits - p->offset - p->size;
+	    else
+	      bits_to_skip += p->offset;
+
+	    copy_bitwise (v_contents, offset,
+			  value_contents_all (p->v.value),
+			  bits_to_skip,
+			  this_size_bits, bits_big_endian);
+	  }
+	  break;
+
+	case DWARF_VALUE_LITERAL:
+	  {
+	    if (from != NULL)
+	      {
+		mark_value_bits_optimized_out (v, offset, this_size_bits);
+		break;
+	      }
+
+	    ULONGEST literal_size_bits = 8 * p->v.literal.length;
+	    size_t n = this_size_bits;
+
+	    /* Cut off at the end of the implicit value.  */
+	    bits_to_skip += p->offset;
+	    if (bits_to_skip >= literal_size_bits)
+	      break;
+	    if (n > literal_size_bits - bits_to_skip)
+	      n = literal_size_bits - bits_to_skip;
+
+	    copy_bitwise (v_contents, offset,
+			  p->v.literal.data, bits_to_skip,
+			  n, bits_big_endian);
+	  }
+	  break;
+
+	case DWARF_VALUE_IMPLICIT_POINTER:
+	    if (from != NULL)
+	      {
+		mark_value_bits_optimized_out (v, offset, this_size_bits);
+		break;
+	      }
+
+	  /* These bits show up as zeros -- but do not cause the value to
+	     be considered optimized-out.  */
+	  break;
+
+	case DWARF_VALUE_OPTIMIZED_OUT:
+	  mark_value_bits_optimized_out (v, offset, this_size_bits);
+	  break;
+
+	default:
+	  internal_error (__FILE__, __LINE__, _("invalid location type"));
+	}
+
+      offset += this_size_bits;
+      bits_to_skip = 0;
+    }
+}
+
+static void
+read_pieced_value (struct value *v)
+{
+  rw_pieced_value (v, NULL);
+}
+
+static void
+write_pieced_value (struct value *to, struct value *from)
+{
+  rw_pieced_value (to, from);
+}
+
+/* An implementation of an lval_funcs method to see whether a value is
+   a synthetic pointer.  */
+
+static int
+check_pieced_synthetic_pointer (const struct value *value, LONGEST bit_offset,
+				int bit_length)
+{
+  struct piece_closure *c
+    = (struct piece_closure *) value_computed_closure (value);
+  int i;
+
+  bit_offset += 8 * value_offset (value);
+  if (value_bitsize (value))
+    bit_offset += value_bitpos (value);
+
+  for (i = 0; i < c->pieces.size () && bit_length > 0; i++)
+    {
+      struct dwarf_expr_piece *p = &c->pieces[i];
+      size_t this_size_bits = p->size;
+
+      if (bit_offset > 0)
+	{
+	  if (bit_offset >= this_size_bits)
+	    {
+	      bit_offset -= this_size_bits;
+	      continue;
+	    }
+
+	  bit_length -= this_size_bits - bit_offset;
+	  bit_offset = 0;
+	}
+      else
+	bit_length -= this_size_bits;
+
+      if (p->location != DWARF_VALUE_IMPLICIT_POINTER)
+	return 0;
+    }
+
+  return 1;
+}
+
+/* An implementation of an lval_funcs method to indirect through a
+   pointer.  This handles the synthetic pointer case when needed.  */
+
+static struct value *
+indirect_pieced_value (struct value *value)
+{
+  struct piece_closure *c
+    = (struct piece_closure *) value_computed_closure (value);
+  struct type *type;
+  struct frame_info *frame;
+  int i, bit_length;
+  LONGEST bit_offset;
+  struct dwarf_expr_piece *piece = NULL;
+  LONGEST byte_offset;
+  enum bfd_endian byte_order;
+
+  type = check_typedef (value_type (value));
+  if (type->code () != TYPE_CODE_PTR)
+    return NULL;
+
+  bit_length = 8 * TYPE_LENGTH (type);
+  bit_offset = 8 * value_offset (value);
+  if (value_bitsize (value))
+    bit_offset += value_bitpos (value);
+
+  for (i = 0; i < c->pieces.size () && bit_length > 0; i++)
+    {
+      struct dwarf_expr_piece *p = &c->pieces[i];
+      size_t this_size_bits = p->size;
+
+      if (bit_offset > 0)
+	{
+	  if (bit_offset >= this_size_bits)
+	    {
+	      bit_offset -= this_size_bits;
+	      continue;
+	    }
+
+	  bit_length -= this_size_bits - bit_offset;
+	  bit_offset = 0;
+	}
+      else
+	bit_length -= this_size_bits;
+
+      if (p->location != DWARF_VALUE_IMPLICIT_POINTER)
+	return NULL;
+
+      if (bit_length != 0)
+	error (_("Invalid use of DW_OP_implicit_pointer"));
+
+      piece = p;
+      break;
+    }
+
+  gdb_assert (piece != NULL && c->per_cu != nullptr);
+  frame = get_selected_frame (_("No frame selected."));
+
+  /* This is an offset requested by GDB, such as value subscripts.
+     However, due to how synthetic pointers are implemented, this is
+     always presented to us as a pointer type.  This means we have to
+     sign-extend it manually as appropriate.  Use raw
+     extract_signed_integer directly rather than value_as_address and
+     sign extend afterwards on architectures that would need it
+     (mostly everywhere except MIPS, which has signed addresses) as
+     the later would go through gdbarch_pointer_to_address and thus
+     return a CORE_ADDR with high bits set on architectures that
+     encode address spaces and other things in CORE_ADDR.  */
+  byte_order = gdbarch_byte_order (get_frame_arch (frame));
+  byte_offset = extract_signed_integer (value_contents (value),
+					TYPE_LENGTH (type), byte_order);
+  byte_offset += piece->v.ptr.offset;
+
+  return indirect_synthetic_pointer (piece->v.ptr.die_sect_off,
+				     byte_offset, c->per_cu,
+				     c->per_objfile, frame, type);
+}
+
+/* Implementation of the coerce_ref method of lval_funcs for synthetic C++
+   references.  */
+
+static struct value *
+coerce_pieced_ref (const struct value *value)
+{
+  struct type *type = check_typedef (value_type (value));
+
+  if (value_bits_synthetic_pointer (value, value_embedded_offset (value),
+				    TARGET_CHAR_BIT * TYPE_LENGTH (type)))
+    {
+      const struct piece_closure *closure
+	= (struct piece_closure *) value_computed_closure (value);
+      struct frame_info *frame
+	= get_selected_frame (_("No frame selected."));
+
+      /* gdb represents synthetic pointers as pieced values with a single
+	 piece.  */
+      gdb_assert (closure != NULL);
+      gdb_assert (closure->pieces.size () == 1);
+
+      return indirect_synthetic_pointer
+	(closure->pieces[0].v.ptr.die_sect_off,
+	 closure->pieces[0].v.ptr.offset,
+	 closure->per_cu, closure->per_objfile, frame, type);
+    }
+  else
+    {
+      /* Else: not a synthetic reference; do nothing.  */
+      return NULL;
+    }
+}
+
+static void *
+copy_pieced_value_closure (const struct value *v)
+{
+  struct piece_closure *c
+    = (struct piece_closure *) value_computed_closure (v);
+
+  ++c->refc;
+  return c;
+}
+
+static void
+free_pieced_value_closure (struct value *v)
+{
+  struct piece_closure *c
+    = (struct piece_closure *) value_computed_closure (v);
+
+  --c->refc;
+  if (c->refc == 0)
+    {
+      for (dwarf_expr_piece &p : c->pieces)
+	if (p.location == DWARF_VALUE_STACK)
+	  value_decref (p.v.value);
+
+      delete c;
+    }
+}
+
+/* Functions for accessing a variable described by DW_OP_piece.  */
+const struct lval_funcs pieced_value_funcs = {
+  read_pieced_value,
+  write_pieced_value,
+  indirect_pieced_value,
+  coerce_pieced_ref,
+  check_pieced_synthetic_pointer,
+  copy_pieced_value_closure,
+  free_pieced_value_closure
+};
+
+/* Given context CTX, section offset SECT_OFF, and compilation unit
+   data PER_CU, execute the "variable value" operation on the DIE
+   found at SECT_OFF.  */
+
+static struct value *
+sect_variable_value (sect_offset sect_off,
+		     dwarf2_per_cu_data *per_cu,
+		     dwarf2_per_objfile *per_objfile)
+{
+  struct type *die_type
+    = dwarf2_fetch_die_type_sect_off (sect_off, per_cu, per_objfile);
+
+  if (die_type == NULL)
+    error (_("Bad DW_OP_GNU_variable_value DIE."));
+
+  /* Note: Things still work when the following test is removed.  This
+     test and error is here to conform to the proposed specification.  */
+  if (die_type->code () != TYPE_CODE_INT
+      && die_type->code () != TYPE_CODE_PTR)
+    error (_("Type of DW_OP_GNU_variable_value DIE must be an integer or pointer."));
+
+  struct type *type = lookup_pointer_type (die_type);
+  struct frame_info *frame = get_selected_frame (_("No frame selected."));
+  return indirect_synthetic_pointer (sect_off, 0, per_cu, per_objfile, frame,
+				     type, true);
+}
+
 /* Return the type used for DWARF operations where the type is
    unspecified in the DWARF spec.  Only certain sizes are
    supported.  */
diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
index af14f6ee0c2..3ac19e5ba0b 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -26,6 +26,7 @@
 #include "gdbtypes.h"
 
 struct dwarf2_per_objfile;
+struct piece_closure;
 
 /* The location of a value.  */
 enum dwarf_value_location
@@ -300,4 +301,15 @@ extern const gdb_byte *safe_read_sleb128 (const gdb_byte *buf,
 extern const gdb_byte *safe_skip_leb128 (const gdb_byte *buf,
 					 const gdb_byte *buf_end);
 
+extern const struct lval_funcs pieced_value_funcs;
+
+/* Allocate a closure for a value formed from separately-described
+   PIECES.  */
+
+struct piece_closure *allocate_piece_closure
+			(dwarf2_per_cu_data *per_cu,
+			 dwarf2_per_objfile *per_objfile,
+			 std::vector<dwarf_expr_piece> &&pieces,
+			 struct frame_info *frame);
+
 #endif /* dwarf2expr.h */
diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c
index d02fb1cd792..92ff3111a5a 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -52,13 +52,6 @@ static struct value *dwarf2_evaluate_loc_desc_full
    size_t size, dwarf2_per_cu_data *per_cu, dwarf2_per_objfile *per_objfile,
    struct type *subobj_type, LONGEST subobj_byte_offset);
 
-static struct value *indirect_synthetic_pointer
-  (sect_offset die, LONGEST byte_offset,
-   dwarf2_per_cu_data *per_cu,
-   dwarf2_per_objfile *per_objfile,
-   struct frame_info *frame,
-   struct type *type, bool resolve_abstract_p = false);
-
 /* Until these have formal names, we define these here.
    ref: http://gcc.gnu.org/wiki/DebugFission
    Each entry in .debug_loc.dwo begins with a byte that describes the entry,
@@ -96,8 +89,7 @@ enum debug_loc_kind
   DEBUG_LOC_INVALID_ENTRY = -2
 };
 
-/* Helper function which throws an error if a synthetic pointer is
-   invalid.  */
+/* See loc.h.  */
 
 static void
 invalid_synthetic_pointer (void)
@@ -593,31 +585,6 @@ func_get_frame_base_dwarf_block (struct symbol *framefunc, CORE_ADDR pc,
 	   framefunc->natural_name ());
 }
 
-/* See loc.h.  */
-
-struct value *
-sect_variable_value (sect_offset sect_off,
-		     dwarf2_per_cu_data *per_cu,
-		     dwarf2_per_objfile *per_objfile)
-{
-  struct type *die_type
-    = dwarf2_fetch_die_type_sect_off (sect_off, per_cu, per_objfile);
-
-  if (die_type == NULL)
-    error (_("Bad DW_OP_GNU_variable_value DIE."));
-
-  /* Note: Things still work when the following test is removed.  This
-     test and error is here to conform to the proposed specification.  */
-  if (die_type->code () != TYPE_CODE_INT
-      && die_type->code () != TYPE_CODE_PTR)
-    error (_("Type of DW_OP_GNU_variable_value DIE must be an integer or pointer."));
-
-  struct type *type = lookup_pointer_type (die_type);
-  struct frame_info *frame = get_selected_frame (_("No frame selected."));
-  return indirect_synthetic_pointer (sect_off, 0, per_cu, per_objfile, frame,
-				     type, true);
-}
-
 /* See dwarf2loc.h.  */
 
 unsigned int entry_values_debug = 0;
@@ -1377,403 +1344,6 @@ value_of_dwarf_block_entry (struct type *type, struct frame_info *frame,
 		 "only for single DW_OP_reg* or for DW_OP_fbreg(*)"));
 }
 
-struct piece_closure
-{
-  /* Reference count.  */
-  int refc = 0;
-
-  /* The objfile from which this closure's expression came.  */
-  dwarf2_per_objfile *per_objfile = nullptr;
-
-  /* The CU from which this closure's expression came.  */
-  struct dwarf2_per_cu_data *per_cu = NULL;
-
-  /* The pieces describing this variable.  */
-  std::vector<dwarf_expr_piece> pieces;
-
-  /* Frame ID of frame to which a register value is relative, used
-     only by DWARF_VALUE_REGISTER.  */
-  struct frame_id frame_id;
-};
-
-/* Allocate a closure for a value formed from separately-described
-   PIECES.  */
-
-static struct piece_closure *
-allocate_piece_closure (dwarf2_per_cu_data *per_cu,
-			dwarf2_per_objfile *per_objfile,
-			std::vector<dwarf_expr_piece> &&pieces,
-			struct frame_info *frame)
-{
-  struct piece_closure *c = new piece_closure;
-
-  c->refc = 1;
-  /* We must capture this here due to sharing of DWARF state.  */
-  c->per_objfile = per_objfile;
-  c->per_cu = per_cu;
-  c->pieces = std::move (pieces);
-  if (frame == NULL)
-    c->frame_id = null_frame_id;
-  else
-    c->frame_id = get_frame_id (frame);
-
-  for (dwarf_expr_piece &piece : c->pieces)
-    if (piece.location == DWARF_VALUE_STACK)
-      value_incref (piece.v.value);
-
-  return c;
-}
-
-/* Return the number of bytes overlapping a contiguous chunk of N_BITS
-   bits whose first bit is located at bit offset START.  */
-
-static size_t
-bits_to_bytes (ULONGEST start, ULONGEST n_bits)
-{
-  return (start % 8 + n_bits + 7) / 8;
-}
-
-/* Read or write a pieced value V.  If FROM != NULL, operate in "write
-   mode": copy FROM into the pieces comprising V.  If FROM == NULL,
-   operate in "read mode": fetch the contents of the (lazy) value V by
-   composing it from its pieces.  */
-
-static void
-rw_pieced_value (struct value *v, struct value *from)
-{
-  int i;
-  LONGEST offset = 0, max_offset;
-  ULONGEST bits_to_skip;
-  gdb_byte *v_contents;
-  const gdb_byte *from_contents;
-  struct piece_closure *c
-    = (struct piece_closure *) value_computed_closure (v);
-  gdb::byte_vector buffer;
-  bool bits_big_endian = type_byte_order (value_type (v)) == BFD_ENDIAN_BIG;
-
-  if (from != NULL)
-    {
-      from_contents = value_contents (from);
-      v_contents = NULL;
-    }
-  else
-    {
-      if (value_type (v) != value_enclosing_type (v))
-	internal_error (__FILE__, __LINE__,
-			_("Should not be able to create a lazy value with "
-			  "an enclosing type"));
-      v_contents = value_contents_raw (v);
-      from_contents = NULL;
-    }
-
-  bits_to_skip = 8 * value_offset (v);
-  if (value_bitsize (v))
-    {
-      bits_to_skip += (8 * value_offset (value_parent (v))
-		       + value_bitpos (v));
-      if (from != NULL
-	  && (type_byte_order (value_type (from))
-	      == BFD_ENDIAN_BIG))
-	{
-	  /* Use the least significant bits of FROM.  */
-	  max_offset = 8 * TYPE_LENGTH (value_type (from));
-	  offset = max_offset - value_bitsize (v);
-	}
-      else
-	max_offset = value_bitsize (v);
-    }
-  else
-    max_offset = 8 * TYPE_LENGTH (value_type (v));
-
-  /* Advance to the first non-skipped piece.  */
-  for (i = 0; i < c->pieces.size () && bits_to_skip >= c->pieces[i].size; i++)
-    bits_to_skip -= c->pieces[i].size;
-
-  for (; i < c->pieces.size () && offset < max_offset; i++)
-    {
-      struct dwarf_expr_piece *p = &c->pieces[i];
-      size_t this_size_bits, this_size;
-
-      this_size_bits = p->size - bits_to_skip;
-      if (this_size_bits > max_offset - offset)
-	this_size_bits = max_offset - offset;
-
-      switch (p->location)
-	{
-	case DWARF_VALUE_REGISTER:
-	  {
-	    struct frame_info *frame = frame_find_by_id (c->frame_id);
-	    struct gdbarch *arch = get_frame_arch (frame);
-	    int gdb_regnum = dwarf_reg_to_regnum_or_error (arch, p->v.regno);
-	    ULONGEST reg_bits = 8 * register_size (arch, gdb_regnum);
-	    int optim, unavail;
-
-	    if (gdbarch_byte_order (arch) == BFD_ENDIAN_BIG
-		&& p->offset + p->size < reg_bits)
-	      {
-		/* Big-endian, and we want less than full size.  */
-		bits_to_skip += reg_bits - (p->offset + p->size);
-	      }
-	    else
-	      bits_to_skip += p->offset;
-
-	    this_size = bits_to_bytes (bits_to_skip, this_size_bits);
-	    buffer.resize (this_size);
-
-	    if (from == NULL)
-	      {
-		/* Read mode.  */
-		if (!get_frame_register_bytes (frame, gdb_regnum,
-					       bits_to_skip / 8,
-					       buffer,
-					       &optim, &unavail))
-		  {
-		    if (optim)
-		      mark_value_bits_optimized_out (v, offset,
-						     this_size_bits);
-		    if (unavail)
-		      mark_value_bits_unavailable (v, offset,
-						   this_size_bits);
-		    break;
-		  }
-
-		copy_bitwise (v_contents, offset,
-			      buffer.data (), bits_to_skip % 8,
-			      this_size_bits, bits_big_endian);
-	      }
-	    else
-	      {
-		/* Write mode.  */
-		if (bits_to_skip % 8 != 0 || this_size_bits % 8 != 0)
-		  {
-		    /* Data is copied non-byte-aligned into the register.
-		       Need some bits from original register value.  */
-		    get_frame_register_bytes (frame, gdb_regnum,
-					      bits_to_skip / 8,
-					      buffer,
-					      &optim, &unavail);
-		    if (optim)
-		      throw_error (OPTIMIZED_OUT_ERROR,
-				   _("Can't do read-modify-write to "
-				     "update bitfield; containing word "
-				     "has been optimized out"));
-		    if (unavail)
-		      throw_error (NOT_AVAILABLE_ERROR,
-				   _("Can't do read-modify-write to "
-				     "update bitfield; containing word "
-				     "is unavailable"));
-		  }
-
-		copy_bitwise (buffer.data (), bits_to_skip % 8,
-			      from_contents, offset,
-			      this_size_bits, bits_big_endian);
-		put_frame_register_bytes (frame, gdb_regnum,
-					  bits_to_skip / 8,
-					  buffer);
-	      }
-	  }
-	  break;
-
-	case DWARF_VALUE_MEMORY:
-	  {
-	    bits_to_skip += p->offset;
-
-	    CORE_ADDR start_addr = p->v.mem.addr + bits_to_skip / 8;
-
-	    if (bits_to_skip % 8 == 0 && this_size_bits % 8 == 0
-		&& offset % 8 == 0)
-	      {
-		/* Everything is byte-aligned; no buffer needed.  */
-		if (from != NULL)
-		  write_memory_with_notification (start_addr,
-						  (from_contents
-						   + offset / 8),
-						  this_size_bits / 8);
-		else
-		  read_value_memory (v, offset,
-				     p->v.mem.in_stack_memory,
-				     p->v.mem.addr + bits_to_skip / 8,
-				     v_contents + offset / 8,
-				     this_size_bits / 8);
-		break;
-	      }
-
-	    this_size = bits_to_bytes (bits_to_skip, this_size_bits);
-	    buffer.resize (this_size);
-
-	    if (from == NULL)
-	      {
-		/* Read mode.  */
-		read_value_memory (v, offset,
-				   p->v.mem.in_stack_memory,
-				   p->v.mem.addr + bits_to_skip / 8,
-				   buffer.data (), this_size);
-		copy_bitwise (v_contents, offset,
-			      buffer.data (), bits_to_skip % 8,
-			      this_size_bits, bits_big_endian);
-	      }
-	    else
-	      {
-		/* Write mode.  */
-		if (bits_to_skip % 8 != 0 || this_size_bits % 8 != 0)
-		  {
-		    if (this_size <= 8)
-		      {
-			/* Perform a single read for small sizes.  */
-			read_memory (start_addr, buffer.data (),
-				     this_size);
-		      }
-		    else
-		      {
-			/* Only the first and last bytes can possibly have
-			   any bits reused.  */
-			read_memory (start_addr, buffer.data (), 1);
-			read_memory (start_addr + this_size - 1,
-				     &buffer[this_size - 1], 1);
-		      }
-		  }
-
-		copy_bitwise (buffer.data (), bits_to_skip % 8,
-			      from_contents, offset,
-			      this_size_bits, bits_big_endian);
-		write_memory_with_notification (start_addr,
-						buffer.data (),
-						this_size);
-	      }
-	  }
-	  break;
-
-	case DWARF_VALUE_STACK:
-	  {
-	    if (from != NULL)
-	      {
-		mark_value_bits_optimized_out (v, offset, this_size_bits);
-		break;
-	      }
-
-	    gdbarch *objfile_gdbarch = c->per_objfile->objfile->arch ();
-	    ULONGEST stack_value_size_bits
-	      = 8 * TYPE_LENGTH (value_type (p->v.value));
-
-	    /* Use zeroes if piece reaches beyond stack value.  */
-	    if (p->offset + p->size > stack_value_size_bits)
-	      break;
-
-	    /* Piece is anchored at least significant bit end.  */
-	    if (gdbarch_byte_order (objfile_gdbarch) == BFD_ENDIAN_BIG)
-	      bits_to_skip += stack_value_size_bits - p->offset - p->size;
-	    else
-	      bits_to_skip += p->offset;
-
-	    copy_bitwise (v_contents, offset,
-			  value_contents_all (p->v.value),
-			  bits_to_skip,
-			  this_size_bits, bits_big_endian);
-	  }
-	  break;
-
-	case DWARF_VALUE_LITERAL:
-	  {
-	    if (from != NULL)
-	      {
-		mark_value_bits_optimized_out (v, offset, this_size_bits);
-		break;
-	      }
-
-	    ULONGEST literal_size_bits = 8 * p->v.literal.length;
-	    size_t n = this_size_bits;
-
-	    /* Cut off at the end of the implicit value.  */
-	    bits_to_skip += p->offset;
-	    if (bits_to_skip >= literal_size_bits)
-	      break;
-	    if (n > literal_size_bits - bits_to_skip)
-	      n = literal_size_bits - bits_to_skip;
-
-	    copy_bitwise (v_contents, offset,
-			  p->v.literal.data, bits_to_skip,
-			  n, bits_big_endian);
-	  }
-	  break;
-
-	case DWARF_VALUE_IMPLICIT_POINTER:
-	    if (from != NULL)
-	      {
-		mark_value_bits_optimized_out (v, offset, this_size_bits);
-		break;
-	      }
-
-	  /* These bits show up as zeros -- but do not cause the value to
-	     be considered optimized-out.  */
-	  break;
-
-	case DWARF_VALUE_OPTIMIZED_OUT:
-	  mark_value_bits_optimized_out (v, offset, this_size_bits);
-	  break;
-
-	default:
-	  internal_error (__FILE__, __LINE__, _("invalid location type"));
-	}
-
-      offset += this_size_bits;
-      bits_to_skip = 0;
-    }
-}
-
-
-static void
-read_pieced_value (struct value *v)
-{
-  rw_pieced_value (v, NULL);
-}
-
-static void
-write_pieced_value (struct value *to, struct value *from)
-{
-  rw_pieced_value (to, from);
-}
-
-/* An implementation of an lval_funcs method to see whether a value is
-   a synthetic pointer.  */
-
-static int
-check_pieced_synthetic_pointer (const struct value *value, LONGEST bit_offset,
-				int bit_length)
-{
-  struct piece_closure *c
-    = (struct piece_closure *) value_computed_closure (value);
-  int i;
-
-  bit_offset += 8 * value_offset (value);
-  if (value_bitsize (value))
-    bit_offset += value_bitpos (value);
-
-  for (i = 0; i < c->pieces.size () && bit_length > 0; i++)
-    {
-      struct dwarf_expr_piece *p = &c->pieces[i];
-      size_t this_size_bits = p->size;
-
-      if (bit_offset > 0)
-	{
-	  if (bit_offset >= this_size_bits)
-	    {
-	      bit_offset -= this_size_bits;
-	      continue;
-	    }
-
-	  bit_length -= this_size_bits - bit_offset;
-	  bit_offset = 0;
-	}
-      else
-	bit_length -= this_size_bits;
-
-      if (p->location != DWARF_VALUE_IMPLICIT_POINTER)
-	return 0;
-    }
-
-  return 1;
-}
-
 /* Fetch a DW_AT_const_value through a synthetic pointer.  */
 
 static struct value *
@@ -1807,9 +1377,9 @@ fetch_const_value_from_synthetic_pointer (sect_offset die, LONGEST byte_offset,
   return result;
 }
 
-/* Fetch the value pointed to by a synthetic pointer.  */
+/* See loc.h.  */
 
-static struct value *
+struct value *
 indirect_synthetic_pointer (sect_offset die, LONGEST byte_offset,
 			    dwarf2_per_cu_data *per_cu,
 			    dwarf2_per_objfile *per_objfile,
@@ -1846,154 +1416,6 @@ indirect_synthetic_pointer (sect_offset die, LONGEST byte_offset,
 						     per_objfile, type);
 }
 
-/* An implementation of an lval_funcs method to indirect through a
-   pointer.  This handles the synthetic pointer case when needed.  */
-
-static struct value *
-indirect_pieced_value (struct value *value)
-{
-  struct piece_closure *c
-    = (struct piece_closure *) value_computed_closure (value);
-  struct type *type;
-  struct frame_info *frame;
-  int i, bit_length;
-  LONGEST bit_offset;
-  struct dwarf_expr_piece *piece = NULL;
-  LONGEST byte_offset;
-  enum bfd_endian byte_order;
-
-  type = check_typedef (value_type (value));
-  if (type->code () != TYPE_CODE_PTR)
-    return NULL;
-
-  bit_length = 8 * TYPE_LENGTH (type);
-  bit_offset = 8 * value_offset (value);
-  if (value_bitsize (value))
-    bit_offset += value_bitpos (value);
-
-  for (i = 0; i < c->pieces.size () && bit_length > 0; i++)
-    {
-      struct dwarf_expr_piece *p = &c->pieces[i];
-      size_t this_size_bits = p->size;
-
-      if (bit_offset > 0)
-	{
-	  if (bit_offset >= this_size_bits)
-	    {
-	      bit_offset -= this_size_bits;
-	      continue;
-	    }
-
-	  bit_length -= this_size_bits - bit_offset;
-	  bit_offset = 0;
-	}
-      else
-	bit_length -= this_size_bits;
-
-      if (p->location != DWARF_VALUE_IMPLICIT_POINTER)
-	return NULL;
-
-      if (bit_length != 0)
-	error (_("Invalid use of DW_OP_implicit_pointer"));
-
-      piece = p;
-      break;
-    }
-
-  gdb_assert (piece != NULL);
-  frame = get_selected_frame (_("No frame selected."));
-
-  /* This is an offset requested by GDB, such as value subscripts.
-     However, due to how synthetic pointers are implemented, this is
-     always presented to us as a pointer type.  This means we have to
-     sign-extend it manually as appropriate.  Use raw
-     extract_signed_integer directly rather than value_as_address and
-     sign extend afterwards on architectures that would need it
-     (mostly everywhere except MIPS, which has signed addresses) as
-     the later would go through gdbarch_pointer_to_address and thus
-     return a CORE_ADDR with high bits set on architectures that
-     encode address spaces and other things in CORE_ADDR.  */
-  byte_order = gdbarch_byte_order (get_frame_arch (frame));
-  byte_offset = extract_signed_integer (value_contents (value),
-					TYPE_LENGTH (type), byte_order);
-  byte_offset += piece->v.ptr.offset;
-
-  return indirect_synthetic_pointer (piece->v.ptr.die_sect_off,
-				     byte_offset, c->per_cu,
-				     c->per_objfile, frame, type);
-}
-
-/* Implementation of the coerce_ref method of lval_funcs for synthetic C++
-   references.  */
-
-static struct value *
-coerce_pieced_ref (const struct value *value)
-{
-  struct type *type = check_typedef (value_type (value));
-
-  if (value_bits_synthetic_pointer (value, value_embedded_offset (value),
-				    TARGET_CHAR_BIT * TYPE_LENGTH (type)))
-    {
-      const struct piece_closure *closure
-	= (struct piece_closure *) value_computed_closure (value);
-      struct frame_info *frame
-	= get_selected_frame (_("No frame selected."));
-
-      /* gdb represents synthetic pointers as pieced values with a single
-	 piece.  */
-      gdb_assert (closure != NULL);
-      gdb_assert (closure->pieces.size () == 1);
-
-      return indirect_synthetic_pointer
-	(closure->pieces[0].v.ptr.die_sect_off,
-	 closure->pieces[0].v.ptr.offset,
-	 closure->per_cu, closure->per_objfile, frame, type);
-    }
-  else
-    {
-      /* Else: not a synthetic reference; do nothing.  */
-      return NULL;
-    }
-}
-
-static void *
-copy_pieced_value_closure (const struct value *v)
-{
-  struct piece_closure *c
-    = (struct piece_closure *) value_computed_closure (v);
-  
-  ++c->refc;
-  return c;
-}
-
-static void
-free_pieced_value_closure (struct value *v)
-{
-  struct piece_closure *c
-    = (struct piece_closure *) value_computed_closure (v);
-
-  --c->refc;
-  if (c->refc == 0)
-    {
-      for (dwarf_expr_piece &p : c->pieces)
-	if (p.location == DWARF_VALUE_STACK)
-	  value_decref (p.v.value);
-
-      delete c;
-    }
-}
-
-/* Functions for accessing a variable described by DW_OP_piece.  */
-static const struct lval_funcs pieced_value_funcs = {
-  read_pieced_value,
-  write_pieced_value,
-  indirect_pieced_value,
-  coerce_pieced_ref,
-  check_pieced_synthetic_pointer,
-  copy_pieced_value_closure,
-  free_pieced_value_closure
-};
-
 /* Evaluate a location description, starting at DATA and with length
    SIZE, to find the current location of variable of TYPE in the
    context of FRAME.  If SUBOBJ_TYPE is non-NULL, return instead the
diff --git a/gdb/dwarf2/loc.h b/gdb/dwarf2/loc.h
index baee63ffccb..33388ef88ee 100644
--- a/gdb/dwarf2/loc.h
+++ b/gdb/dwarf2/loc.h
@@ -53,14 +53,6 @@ extern void func_get_frame_base_dwarf_block (struct symbol *framefunc,
 					     const gdb_byte **start,
 					     size_t *length);
 
-/* Given section offset SECT_OFF, and compilation unit data
-   PER_CU, execute the "variable value" operation on the DIE
-   found at SECT_OFF.  */
-
-struct value *sect_variable_value (sect_offset sect_off,
-				   dwarf2_per_cu_data *per_cu,
-				   dwarf2_per_objfile *per_objfile);
-
 /* Fetch call_site_parameter from caller matching KIND and KIND_U.
    FRAME is for callee.
 
@@ -286,4 +278,13 @@ extern int dwarf_reg_to_regnum (struct gdbarch *arch, int dwarf_reg);
 extern int dwarf_reg_to_regnum_or_error (struct gdbarch *arch,
 					 ULONGEST dwarf_reg);
 
+/* Fetch the value pointed to by a synthetic pointer.  */
+
+extern struct value *indirect_synthetic_pointer
+    (sect_offset die, LONGEST byte_offset,
+     dwarf2_per_cu_data *per_cu,
+     dwarf2_per_objfile *per_objfile,
+     struct frame_info *frame,
+     struct type *type, bool resolve_abstract_p = false);
+
 #endif /* dwarf2loc.h */
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 14/43] Make value_copy also copy the stack data member
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (12 preceding siblings ...)
  2021-03-01 14:45 ` [PATCH 13/43] Move piece_closure and its support to expr.c Zoran Zaric
@ 2021-03-01 14:45 ` Zoran Zaric
  2021-04-28  2:01   ` Simon Marchi
  2021-03-01 14:45 ` [PATCH 15/43] Make DWARF evaluator return a single struct value Zoran Zaric
                   ` (28 subsequent siblings)
  42 siblings, 1 reply; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:45 UTC (permalink / raw)
  To: gdb-patches

Fixing a bug where the value_copy function did not copy the stack data
member of the struct value. This is needed for the next patch where the
DWARF expression evaluator is changed to return a single struct value
object.

        * value.c (value_copy): Change to also copy the stack data
         member.
---
 gdb/value.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/gdb/value.c b/gdb/value.c
index bddf9a47923..9f3914a5bc5 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -1690,6 +1690,7 @@ value_copy (struct value *arg)
   val->embedded_offset = value_embedded_offset (arg);
   val->pointed_to_offset = arg->pointed_to_offset;
   val->modifiable = arg->modifiable;
+  val->stack = arg->stack;
   if (!value_lazy (val))
     {
       memcpy (value_contents_all_raw (val), value_contents_all_raw (arg),
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 15/43] Make DWARF evaluator return a single struct value
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (13 preceding siblings ...)
  2021-03-01 14:45 ` [PATCH 14/43] Make value_copy also copy the stack data member Zoran Zaric
@ 2021-03-01 14:45 ` Zoran Zaric
  2021-04-28  2:21   ` Simon Marchi
  2021-03-01 14:45 ` [PATCH 16/43] Simplify dwarf_expr_context class interface Zoran Zaric
                   ` (27 subsequent siblings)
  42 siblings, 1 reply; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:45 UTC (permalink / raw)
  To: gdb-patches

From: Zoran Zaric <zoran.zaric@amd.com>

The patch is addressing the issue of class users writing and reading
the internal data of the dwarf_expr_context class.

At this point, all conditions are met for the DWARF evaluator to return
an evaluation result in a form of a single struct value object.

gdb/ChangeLog:

	* dwarf2/expr.c (pieced_value_funcs): Chenge to static
	function.
	(allocate_piece_closure): Change to static function.
	(dwarf_expr_context::fetch_result): New function.
	* dwarf2/expr.h (struct piece_closure): Remove declaration.
	(struct dwarf_expr_context): fetch_result new declaration.
	fetch, fetch_address and fetch_in_stack_memory members move
	to private.
	(allocate_piece_closure): Remove.
	* dwarf2/frame.c (execute_stack_op): Change to use
	fetch_result.
	* dwarf2/loc.c (dwarf2_evaluate_loc_desc_full): Change to use
	fetch_result.
	(dwarf2_locexpr_baton_eval): Change to use fetch_result.
        * dwarf2/loc.h (invalid_synthetic_pointer): Expose function.
---
 gdb/dwarf2/expr.c  | 163 ++++++++++++++++++++++++++++++++++++-
 gdb/dwarf2/expr.h  |  25 +++---
 gdb/dwarf2/frame.c |  19 ++---
 gdb/dwarf2/loc.c   | 195 ++++++---------------------------------------
 gdb/dwarf2/loc.h   |   5 ++
 5 files changed, 207 insertions(+), 200 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 486521b5e04..b2248681899 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -125,9 +125,10 @@ struct piece_closure
   struct frame_id frame_id;
 };
 
-/* See expr.h.  */
+/* Allocate a closure for a value formed from separately-described
+   PIECES.  */
 
-struct piece_closure *
+static struct piece_closure *
 allocate_piece_closure (dwarf2_per_cu_data *per_cu,
 			dwarf2_per_objfile *per_objfile,
 			std::vector<dwarf_expr_piece> &&pieces,
@@ -628,7 +629,7 @@ free_pieced_value_closure (struct value *v)
 }
 
 /* Functions for accessing a variable described by DW_OP_piece.  */
-const struct lval_funcs pieced_value_funcs = {
+static const struct lval_funcs pieced_value_funcs = {
   read_pieced_value,
   write_pieced_value,
   indirect_pieced_value,
@@ -886,6 +887,162 @@ dwarf_expr_context::push_dwarf_reg_entry_value
   this->eval (data_src, size);
 }
 
+/* See expr.h.  */
+
+struct value *
+dwarf_expr_context::fetch_result (struct type *type,
+				  struct type *subobj_type,
+				  LONGEST subobj_offset)
+{
+  struct value *retval = nullptr;
+
+  if (type == nullptr)
+    type = address_type ();
+
+  if (subobj_type == nullptr)
+    subobj_type = type;
+
+  if (this->pieces.size () > 0)
+    {
+      struct piece_closure *c;
+      ULONGEST bit_size = 0;
+
+      for (dwarf_expr_piece &piece : this->pieces)
+	bit_size += piece.size;
+      /* Complain if the expression is larger than the size of the
+	 outer type.  */
+      if (bit_size > 8 * TYPE_LENGTH (type))
+	invalid_synthetic_pointer ();
+
+      c = allocate_piece_closure (this->per_cu, this->per_objfile,
+				  std::move (this->pieces), this->frame);
+      retval = allocate_computed_value (subobj_type,
+					&pieced_value_funcs, c);
+      set_value_offset (retval, subobj_offset);
+    }
+  else
+    {
+      switch (this->location)
+	{
+	case DWARF_VALUE_REGISTER:
+	  {
+	    int dwarf_regnum
+	      = longest_to_int (value_as_long (this->fetch (0)));
+	    int gdb_regnum = dwarf_reg_to_regnum_or_error (this->gdbarch,
+							   dwarf_regnum);
+
+	    if (subobj_offset != 0)
+	      error (_("cannot use offset on synthetic pointer to register"));
+
+	    gdb_assert (this->frame != NULL);
+
+	    retval = value_from_register (subobj_type, gdb_regnum,
+					  this->frame);
+	    if (value_optimized_out (retval))
+	      {
+		struct value *tmp;
+
+		/* This means the register has undefined value / was
+		   not saved.  As we're computing the location of some
+		   variable etc. in the program, not a value for
+		   inspecting a register ($pc, $sp, etc.), return a
+		   generic optimized out value instead, so that we show
+		   <optimized out> instead of <not saved>.  */
+		tmp = allocate_value (subobj_type);
+		value_contents_copy (tmp, 0, retval, 0,
+				     TYPE_LENGTH (subobj_type));
+		retval = tmp;
+	      }
+	  }
+	  break;
+
+	case DWARF_VALUE_MEMORY:
+	  {
+	    struct type *ptr_type;
+	    CORE_ADDR address = this->fetch_address (0);
+	    bool in_stack_memory = this->fetch_in_stack_memory (0);
+
+	    /* DW_OP_deref_size (and possibly other operations too) may
+	       create a pointer instead of an address.  Ideally, the
+	       pointer to address conversion would be performed as part
+	       of those operations, but the type of the object to
+	       which the address refers is not known at the time of
+	       the operation.  Therefore, we do the conversion here
+	       since the type is readily available.  */
+
+	    switch (subobj_type->code ())
+	      {
+		case TYPE_CODE_FUNC:
+		case TYPE_CODE_METHOD:
+		  ptr_type = builtin_type (this->gdbarch)->builtin_func_ptr;
+		  break;
+		default:
+		  ptr_type = builtin_type (this->gdbarch)->builtin_data_ptr;
+		  break;
+	      }
+	    address = value_as_address (value_from_pointer (ptr_type, address));
+
+	    retval = value_at_lazy (subobj_type,
+				    address + subobj_offset);
+	    if (in_stack_memory)
+	      set_value_stack (retval, 1);
+	  }
+	  break;
+
+	case DWARF_VALUE_STACK:
+	  {
+	    struct value *value = this->fetch (0);
+	    size_t n = TYPE_LENGTH (value_type (value));
+	    size_t len = TYPE_LENGTH (subobj_type);
+	    size_t max = TYPE_LENGTH (type);
+
+	    if (subobj_offset + len > max)
+	      invalid_synthetic_pointer ();
+
+	    retval = allocate_value (subobj_type);
+
+	    /* The given offset is relative to the actual object.  */
+	    if (gdbarch_byte_order (this->gdbarch) == BFD_ENDIAN_BIG)
+	      subobj_offset += n - max;
+
+	    memcpy (value_contents_raw (retval),
+		    value_contents_all (value) + subobj_offset, len);
+	  }
+	  break;
+
+	case DWARF_VALUE_LITERAL:
+	  {
+	    bfd_byte *contents;
+	    size_t n = TYPE_LENGTH (subobj_type);
+
+	    if (subobj_offset + n > this->len)
+	      invalid_synthetic_pointer ();
+
+	    retval = allocate_value (subobj_type);
+	    contents = value_contents_raw (retval);
+	    memcpy (contents, this->data + subobj_offset, n);
+	  }
+	  break;
+
+	case DWARF_VALUE_OPTIMIZED_OUT:
+	  retval = allocate_optimized_out_value (subobj_type);
+	  break;
+
+	  /* DWARF_VALUE_IMPLICIT_POINTER was converted to a pieced
+	     operation by execute_stack_op.  */
+	case DWARF_VALUE_IMPLICIT_POINTER:
+	  /* DWARF_VALUE_OPTIMIZED_OUT can't occur in this context --
+	     it can only be encountered when making a piece.  */
+	default:
+	  internal_error (__FILE__, __LINE__, _("invalid location type"));
+	}
+    }
+
+  set_value_initialized (retval, this->initialized);
+
+  return retval;
+}
+
 /* Require that TYPE be an integral type; throw an exception if not.  */
 
 static void
diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
index 3ac19e5ba0b..a0ac21f2ed1 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -26,7 +26,6 @@
 #include "gdbtypes.h"
 
 struct dwarf2_per_objfile;
-struct piece_closure;
 
 /* The location of a value.  */
 enum dwarf_value_location
@@ -125,9 +124,13 @@ struct dwarf_expr_context
 
   void push_address (CORE_ADDR value, bool in_stack_memory);
   void eval (const gdb_byte *addr, size_t len);
-  struct value *fetch (int n);
-  CORE_ADDR fetch_address (int n);
-  bool fetch_in_stack_memory (int n);
+
+  /* Fetch the result of the expression evaluation in a form of
+     a struct value, where TYPE, SUBOBJ_TYPE and SUBOBJ_OFFSET
+     describe the source level representation of that result.  */
+  struct value *fetch_result (struct type *type = nullptr,
+			      struct type *subobj_type = nullptr,
+			      LONGEST subobj_offset = 0);
 
   /* The stack of values.  */
   std::vector<dwarf_stack_value> stack;
@@ -207,6 +210,9 @@ struct dwarf_expr_context
   void add_piece (ULONGEST size, ULONGEST offset);
   void execute_stack_op (const gdb_byte *op_ptr, const gdb_byte *op_end);
   void pop ();
+  struct value *fetch (int n);
+  CORE_ADDR fetch_address (int n);
+  bool fetch_in_stack_memory (int n);
 
   /* Return the location expression for the frame base attribute, in
      START and LENGTH.  The result must be live until the current
@@ -301,15 +307,4 @@ extern const gdb_byte *safe_read_sleb128 (const gdb_byte *buf,
 extern const gdb_byte *safe_skip_leb128 (const gdb_byte *buf,
 					 const gdb_byte *buf_end);
 
-extern const struct lval_funcs pieced_value_funcs;
-
-/* Allocate a closure for a value formed from separately-described
-   PIECES.  */
-
-struct piece_closure *allocate_piece_closure
-			(dwarf2_per_cu_data *per_cu,
-			 dwarf2_per_objfile *per_objfile,
-			 std::vector<dwarf_expr_piece> &&pieces,
-			 struct frame_info *frame);
-
 #endif /* dwarf2expr.h */
diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
index 8faadeb3469..21daecec61c 100644
--- a/gdb/dwarf2/frame.c
+++ b/gdb/dwarf2/frame.c
@@ -228,8 +228,6 @@ execute_stack_op (const gdb_byte *exp, ULONGEST len, int addr_size,
 		  struct frame_info *this_frame, CORE_ADDR initial,
 		  int initial_in_stack_memory, dwarf2_per_objfile *per_objfile)
 {
-  CORE_ADDR result;
-
   dwarf_expr_context ctx (per_objfile);
   scoped_value_mark free_values;
 
@@ -241,18 +239,13 @@ execute_stack_op (const gdb_byte *exp, ULONGEST len, int addr_size,
   ctx.push_address (initial, initial_in_stack_memory);
   ctx.eval (exp, len);
 
-  if (ctx.location == DWARF_VALUE_MEMORY)
-    result = ctx.fetch_address (0);
-  else if (ctx.location == DWARF_VALUE_REGISTER)
-    result = read_addr_from_reg (this_frame, value_as_long (ctx.fetch (0)));
+  CORE_ADDR result;
+  struct value *result_val = ctx.fetch_result ();
+
+  if (VALUE_LVAL (result_val) == lval_memory)
+    result = value_address (result_val);
   else
-    {
-      /* This is actually invalid DWARF, but if we ever do run across
-	 it somehow, we might as well support it.  So, instead, report
-	 it as unimplemented.  */
-      error (_("\
-Not implemented: computing unwound register using explicit value operator"));
-    }
+    result = value_as_address (result_val);
 
   return result;
 }
diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c
index 92ff3111a5a..e2d6b32717b 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -91,7 +91,7 @@ enum debug_loc_kind
 
 /* See loc.h.  */
 
-static void
+void
 invalid_synthetic_pointer (void)
 {
   error (_("access outside bounds of object "
@@ -1457,6 +1457,8 @@ dwarf2_evaluate_loc_desc_full (struct type *type, struct frame_info *frame,
   try
     {
       ctx.eval (data, size);
+      retval = ctx.fetch_result (type, subobj_type,
+				 subobj_byte_offset);
     }
   catch (const gdb_exception_error &ex)
     {
@@ -1479,155 +1481,15 @@ dwarf2_evaluate_loc_desc_full (struct type *type, struct frame_info *frame,
 	throw;
     }
 
-  if (ctx.pieces.size () > 0)
-    {
-      struct piece_closure *c;
-      ULONGEST bit_size = 0;
-
-      for (dwarf_expr_piece &piece : ctx.pieces)
-	bit_size += piece.size;
-      /* Complain if the expression is larger than the size of the
-	 outer type.  */
-      if (bit_size > 8 * TYPE_LENGTH (type))
-	invalid_synthetic_pointer ();
-
-      c = allocate_piece_closure (per_cu, per_objfile, std::move (ctx.pieces),
-				  frame);
-      /* We must clean up the value chain after creating the piece
-	 closure but before allocating the result.  */
-      free_values.free_to_mark ();
-      retval = allocate_computed_value (subobj_type,
-					&pieced_value_funcs, c);
-      set_value_offset (retval, subobj_byte_offset);
-    }
-  else
-    {
-      switch (ctx.location)
-	{
-	case DWARF_VALUE_REGISTER:
-	  {
-	    struct gdbarch *arch = get_frame_arch (frame);
-	    int dwarf_regnum
-	      = longest_to_int (value_as_long (ctx.fetch (0)));
-	    int gdb_regnum = dwarf_reg_to_regnum_or_error (arch, dwarf_regnum);
-
-	    if (subobj_byte_offset != 0)
-	      error (_("cannot use offset on synthetic pointer to register"));
-	    free_values.free_to_mark ();
-	    retval = value_from_register (subobj_type, gdb_regnum, frame);
-	    if (value_optimized_out (retval))
-	      {
-		struct value *tmp;
-
-		/* This means the register has undefined value / was
-		   not saved.  As we're computing the location of some
-		   variable etc. in the program, not a value for
-		   inspecting a register ($pc, $sp, etc.), return a
-		   generic optimized out value instead, so that we show
-		   <optimized out> instead of <not saved>.  */
-		tmp = allocate_value (subobj_type);
-		value_contents_copy (tmp, 0, retval, 0,
-				     TYPE_LENGTH (subobj_type));
-		retval = tmp;
-	      }
-	  }
-	  break;
-
-	case DWARF_VALUE_MEMORY:
-	  {
-	    struct type *ptr_type;
-	    CORE_ADDR address = ctx.fetch_address (0);
-	    bool in_stack_memory = ctx.fetch_in_stack_memory (0);
-
-	    /* DW_OP_deref_size (and possibly other operations too) may
-	       create a pointer instead of an address.  Ideally, the
-	       pointer to address conversion would be performed as part
-	       of those operations, but the type of the object to
-	       which the address refers is not known at the time of
-	       the operation.  Therefore, we do the conversion here
-	       since the type is readily available.  */
-
-	    switch (subobj_type->code ())
-	      {
-		case TYPE_CODE_FUNC:
-		case TYPE_CODE_METHOD:
-		  ptr_type = builtin_type (ctx.gdbarch)->builtin_func_ptr;
-		  break;
-		default:
-		  ptr_type = builtin_type (ctx.gdbarch)->builtin_data_ptr;
-		  break;
-	      }
-	    address = value_as_address (value_from_pointer (ptr_type, address));
-
-	    free_values.free_to_mark ();
-	    retval = value_at_lazy (subobj_type,
-				    address + subobj_byte_offset);
-	    if (in_stack_memory)
-	      set_value_stack (retval, 1);
-	  }
-	  break;
-
-	case DWARF_VALUE_STACK:
-	  {
-	    struct value *value = ctx.fetch (0);
-	    size_t n = TYPE_LENGTH (value_type (value));
-	    size_t len = TYPE_LENGTH (subobj_type);
-	    size_t max = TYPE_LENGTH (type);
-	    gdbarch *objfile_gdbarch = per_objfile->objfile->arch ();
-
-	    if (subobj_byte_offset + len > max)
-	      invalid_synthetic_pointer ();
-
-	    /* Preserve VALUE because we are going to free values back
-	       to the mark, but we still need the value contents
-	       below.  */
-	    value_ref_ptr value_holder = value_ref_ptr::new_reference (value);
-	    free_values.free_to_mark ();
-
-	    retval = allocate_value (subobj_type);
-
-	    /* The given offset is relative to the actual object.  */
-	    if (gdbarch_byte_order (objfile_gdbarch) == BFD_ENDIAN_BIG)
-	      subobj_byte_offset += n - max;
-
-	    memcpy (value_contents_raw (retval),
-		    value_contents_all (value) + subobj_byte_offset, len);
-	  }
-	  break;
-
-	case DWARF_VALUE_LITERAL:
-	  {
-	    bfd_byte *contents;
-	    size_t n = TYPE_LENGTH (subobj_type);
-
-	    if (subobj_byte_offset + n > ctx.len)
-	      invalid_synthetic_pointer ();
-
-	    free_values.free_to_mark ();
-	    retval = allocate_value (subobj_type);
-	    contents = value_contents_raw (retval);
-	    memcpy (contents, ctx.data + subobj_byte_offset, n);
-	  }
-	  break;
-
-	case DWARF_VALUE_OPTIMIZED_OUT:
-	  free_values.free_to_mark ();
-	  retval = allocate_optimized_out_value (subobj_type);
-	  break;
-
-	  /* DWARF_VALUE_IMPLICIT_POINTER was converted to a pieced
-	     operation by execute_stack_op.  */
-	case DWARF_VALUE_IMPLICIT_POINTER:
-	  /* DWARF_VALUE_OPTIMIZED_OUT can't occur in this context --
-	     it can only be encountered when making a piece.  */
-	default:
-	  internal_error (__FILE__, __LINE__, _("invalid location type"));
-	}
-    }
-
-  set_value_initialized (retval, ctx.initialized);
+  /* We need to clean up all the values that are not needed any more.
+     The problem with a value_ref_ptr class is that it disconnects the
+     RETVAL from the value garbage collection, so we need to make
+     a copy of that value on the stack to keep everything consistent.
+     The value_ref_ptr will clean up after itself at the end of this block.  */
+  value_ref_ptr value_holder = value_ref_ptr::new_reference (retval);
+  free_values.free_to_mark ();
 
-  return retval;
+  return value_copy(retval);
 }
 
 /* The exported interface to dwarf2_evaluate_loc_desc_full; it always
@@ -1668,6 +1530,9 @@ dwarf2_locexpr_baton_eval (const struct dwarf2_locexpr_baton *dlbaton,
   dwarf2_per_objfile *per_objfile = dlbaton->per_objfile;
   dwarf_expr_context ctx (per_objfile);
 
+  struct value *result;
+  scoped_value_mark free_values;
+
   ctx.frame = frame;
   ctx.per_cu = dlbaton->per_cu;
   if (addr_stack != nullptr)
@@ -1686,6 +1551,7 @@ dwarf2_locexpr_baton_eval (const struct dwarf2_locexpr_baton *dlbaton,
   try
     {
       ctx.eval (dlbaton->data, dlbaton->size);
+      result = ctx.fetch_result ();
     }
   catch (const gdb_exception_error &ex)
     {
@@ -1703,29 +1569,20 @@ dwarf2_locexpr_baton_eval (const struct dwarf2_locexpr_baton *dlbaton,
 	throw;
     }
 
-  switch (ctx.location)
+  if (value_optimized_out (result))
+    return 0;
+
+  if (VALUE_LVAL (result) == lval_memory)
+    *valp = value_address (result);
+  else
     {
-    case DWARF_VALUE_STACK:
-      *is_reference = false;
-      /* FALLTHROUGH */
-
-    case DWARF_VALUE_REGISTER:
-    case DWARF_VALUE_MEMORY:
-      *valp = ctx.fetch_address (0);
-      if (ctx.location == DWARF_VALUE_REGISTER)
-	*valp = read_addr_from_reg (frame, *valp);
-      return 1;
-    case DWARF_VALUE_LITERAL:
-      *valp = extract_signed_integer (ctx.data, ctx.len,
-				      gdbarch_byte_order (ctx.gdbarch));
-      return 1;
-      /* Unsupported dwarf values.  */
-    case DWARF_VALUE_OPTIMIZED_OUT:
-    case DWARF_VALUE_IMPLICIT_POINTER:
-      break;
+      if (VALUE_LVAL (result) == not_lval)
+	*is_reference = false;
+
+      *valp = value_as_address (result);
     }
 
-  return 0;
+  return 1;
 }
 
 /* See dwarf2loc.h.  */
diff --git a/gdb/dwarf2/loc.h b/gdb/dwarf2/loc.h
index 33388ef88ee..02c686e5592 100644
--- a/gdb/dwarf2/loc.h
+++ b/gdb/dwarf2/loc.h
@@ -278,6 +278,11 @@ extern int dwarf_reg_to_regnum (struct gdbarch *arch, int dwarf_reg);
 extern int dwarf_reg_to_regnum_or_error (struct gdbarch *arch,
 					 ULONGEST dwarf_reg);
 
+/* Helper function which throws an error if a synthetic pointer is
+   invalid.  */
+
+extern void invalid_synthetic_pointer ();
+
 /* Fetch the value pointed to by a synthetic pointer.  */
 
 extern struct value *indirect_synthetic_pointer
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 16/43] Simplify dwarf_expr_context class interface
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (14 preceding siblings ...)
  2021-03-01 14:45 ` [PATCH 15/43] Make DWARF evaluator return a single struct value Zoran Zaric
@ 2021-03-01 14:45 ` Zoran Zaric
  2021-04-28  2:45   ` Simon Marchi
  2021-03-01 14:45 ` [PATCH 17/43] Add as_lval argument to expression evaluator Zoran Zaric
                   ` (26 subsequent siblings)
  42 siblings, 1 reply; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:45 UTC (permalink / raw)
  To: gdb-patches

From: Zoran Zaric <zoran.zaric@amd.com>

Idea of this patch is to get a clean and simple public interface for
the dwarf_expr_context class, looking like:

- constructor,
- destructor,
- push_address method and
- evaluate method.

Where constructor should only ever require a target architecture
information. This information is held in per object file
(dwarf2_per_objfile) structure, so it makes sense to keep that
structure as a constructor argument. It also makes sense to get the
address size from that structure, but unfortunately that interface
doesn’t exist at the moment, so the dwarf_expr_context class user
needs to provide that information.

The push_address method is used to push a CORE_ADDR as a value on
top of the DWARF stack before the evaluation. This method can be
later changed to push any struct value object on the stack.

The evaluate method is the method that evaluates a DWARF expression
and provides the evaluation result, in a form of a single struct
value object that describes a location. To do this, the method requires
a context of the evaluation, as well as expected result type
information. If the type information is not provided, the DWARF generic
type will be used instead.

gdb/ChangeLog:

	* dwarf2/expr.c (dwarf_expr_context::dwarf_expr_context): Add
	address size argument.
	(dwarf_expr_context::read_mem): Change to use property_addr_info
	structure.
	(dwarf_expr_context::evaluate): New function.
	(dwarf_expr_context::execute_stack_op): Change to use
	property_addr_info structure.
	* dwarf2/expr.h (struct dwarf_expr_context): New evaluate
	declaration. Change eval and fetch_result method to private.
	* dwarf2/frame.c (execute_stack_op): Change to call evaluate
	method.
	* dwarf2/loc.c (dwarf2_evaluate_loc_desc_full): Change to call
	evaluate method.
	(dwarf2_locexpr_baton_eval): Change to call evaluate method.
---
 gdb/dwarf2/expr.c  | 49 ++++++++++++++++++++++++++++++++++++----------
 gdb/dwarf2/expr.h  | 44 +++++++++++++++++++++++++++--------------
 gdb/dwarf2/frame.c | 18 ++++-------------
 gdb/dwarf2/loc.c   | 41 ++++++++++++--------------------------
 4 files changed, 85 insertions(+), 67 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index b2248681899..1645f477d74 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -699,8 +699,11 @@ dwarf_expr_context::address_type () const
 
 /* Create a new context for the expression evaluator.  */
 
-dwarf_expr_context::dwarf_expr_context (dwarf2_per_objfile *per_objfile)
-: per_objfile (per_objfile)
+dwarf_expr_context::dwarf_expr_context (dwarf2_per_objfile *per_objfile,
+					int addr_size)
+: gdbarch (per_objfile->objfile->arch ()),
+  addr_size (addr_size),
+  per_objfile (per_objfile)
 {
 }
 
@@ -825,13 +828,17 @@ dwarf_expr_context::read_mem (gdb_byte *buf, CORE_ADDR addr,
     return;
 
   /* Prefer the passed-in memory, if it exists.  */
-  CORE_ADDR offset = addr - obj_address;
+  if (addr_info != nullptr)
+    {
+      CORE_ADDR offset = addr - addr_info->addr;
 
-  if (offset < data_view.size () && offset + length <= data_view.size ())
+      if (offset < addr_info->valaddr.size ()
+	  && offset + length <= addr_info->valaddr.size ())
     {
-      memcpy (buf, data_view.data (), length);
+      memcpy (buf, addr_info->valaddr.data (), length);
       return;
     }
+    }
 
   read_memory (addr, buf, length);
 }
@@ -874,8 +881,8 @@ dwarf_expr_context::push_dwarf_reg_entry_value
 						   caller_frame);
   scoped_restore save_per_cu = make_scoped_restore (&this->per_cu,
 						    caller_per_cu);
-  scoped_restore save_obj_addr = make_scoped_restore (&this->obj_address,
-						      (CORE_ADDR) 0);
+  scoped_restore save_addr_info = make_scoped_restore (&this->addr_info,
+						       nullptr);
   scoped_restore save_per_objfile = make_scoped_restore (&this->per_objfile,
 							 caller_per_objfile);
 
@@ -1043,6 +1050,28 @@ dwarf_expr_context::fetch_result (struct type *type,
   return retval;
 }
 
+/* See expr.h.  */
+
+struct value *
+dwarf_expr_context::evaluate (const gdb_byte *addr, size_t len,
+			      struct dwarf2_per_cu_data *per_cu,
+			      struct frame_info *frame,
+			      const struct property_addr_info *addr_info,
+			      struct type *type,
+			      struct type *subobj_type,
+			      LONGEST subobj_offset)
+{
+  this->per_cu = per_cu;
+  this->frame = frame;
+  this->addr_info = addr_info;
+
+  if (per_cu != nullptr)
+    this->ref_addr_size = per_cu->ref_addr_size ();
+
+  eval (addr, len);
+  return fetch_result (type, subobj_type, subobj_offset);
+}
+
 /* Require that TYPE be an integral type; throw an exception if not.  */
 
 static void
@@ -2313,11 +2342,11 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 
 	case DW_OP_push_object_address:
 	  /* Return the address of the object we are currently observing.  */
-	  if (this->data_view.data () == nullptr
-	      && this->obj_address == 0)
+	  if (addr_info == nullptr)
 	    error (_("Location address is not set."));
 
-	  result_val = value_from_ulongest (address_type, this->obj_address);
+	  result_val
+	    = value_from_ulongest (address_type, this->addr_info->addr);
 	  break;
 
 	default:
diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
index a0ac21f2ed1..d1374068732 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -119,19 +119,30 @@ struct dwarf_stack_value
    its current state and its callbacks.  */
 struct dwarf_expr_context
 {
-  dwarf_expr_context (dwarf2_per_objfile *per_objfile);
+  /* We should ever only pass in the PER_OBJFILE, while the ADDR_SIZE
+     information should be retrievable from there.  The PER_OBJFILE
+     contains a pointer to the PER_BFD information anyway and the
+     address size information must be the same for the whole BFD.  */
+  dwarf_expr_context (struct dwarf2_per_objfile *per_objfile,
+		      int addr_size);
   virtual ~dwarf_expr_context () = default;
 
   void push_address (CORE_ADDR value, bool in_stack_memory);
-  void eval (const gdb_byte *addr, size_t len);
 
-  /* Fetch the result of the expression evaluation in a form of
-     a struct value, where TYPE, SUBOBJ_TYPE and SUBOBJ_OFFSET
-     describe the source level representation of that result.  */
-  struct value *fetch_result (struct type *type = nullptr,
-			      struct type *subobj_type = nullptr,
-			      LONGEST subobj_offset = 0);
+  /* Evaluate the expression at ADDR (LEN bytes long) in a given PER_CU
+     FRAME context.  Where TYPE, SUBOBJ_TYPE and SUBOBJ_OFFSET describe
+     expected struct value representation of the evaluation result.
+     The ADDR_INFO property can be specified to override the range of
+     memory addresses with the passed in buffer.  */
+  struct value *evaluate (const gdb_byte *addr, size_t len,
+			  struct dwarf2_per_cu_data *per_cu,
+			  struct frame_info *frame,
+			  const struct property_addr_info *addr_info = nullptr,
+			  struct type *type = nullptr,
+			  struct type *subobj_type = nullptr,
+			  LONGEST subobj_offset = 0);
 
+private:
   /* The stack of values.  */
   std::vector<dwarf_stack_value> stack;
 
@@ -196,14 +207,10 @@ struct dwarf_expr_context
   /* Compilation unit used for the evaluation.  */
   struct dwarf2_per_cu_data *per_cu = nullptr;
 
-  /* Object address used for the evaluation.  */
-  CORE_ADDR obj_address = 0;
-
-  /* The data that was passed in.  */
-  gdb::array_view<const gdb_byte> data_view;
-
-private:
+  /* Property address info used for the evaluation.  */
+  const struct property_addr_info *addr_info = nullptr;
 
+  void eval (const gdb_byte *addr, size_t len);
   struct type *address_type () const;
   void push (struct value *value, bool in_stack_memory);
   bool stack_empty_p () const;
@@ -214,6 +221,13 @@ struct dwarf_expr_context
   CORE_ADDR fetch_address (int n);
   bool fetch_in_stack_memory (int n);
 
+  /* Fetch the result of the expression evaluation in a form of
+     a struct value, where TYPE, SUBOBJ_TYPE and SUBOBJ_OFFSET
+     describe the source level representation of that result.  */
+  struct value *fetch_result (struct type *type,
+			      struct type *subobj_type,
+			      LONGEST subobj_offset);
+
   /* Return the location expression for the frame base attribute, in
      START and LENGTH.  The result must be live until the current
      expression evaluation is complete.  */
diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
index 21daecec61c..07667c3dee5 100644
--- a/gdb/dwarf2/frame.c
+++ b/gdb/dwarf2/frame.c
@@ -228,26 +228,16 @@ execute_stack_op (const gdb_byte *exp, ULONGEST len, int addr_size,
 		  struct frame_info *this_frame, CORE_ADDR initial,
 		  int initial_in_stack_memory, dwarf2_per_objfile *per_objfile)
 {
-  dwarf_expr_context ctx (per_objfile);
+  dwarf_expr_context ctx (per_objfile, addr_size);
   scoped_value_mark free_values;
 
-  ctx.frame = this_frame;
-  ctx.gdbarch = get_frame_arch (this_frame);
-  ctx.addr_size = addr_size;
-  ctx.ref_addr_size = -1;
-
   ctx.push_address (initial, initial_in_stack_memory);
-  ctx.eval (exp, len);
-
-  CORE_ADDR result;
-  struct value *result_val = ctx.fetch_result ();
+  struct value *result_val = ctx.evaluate (exp, len, nullptr, this_frame);
 
   if (VALUE_LVAL (result_val) == lval_memory)
-    result = value_address (result_val);
+    return value_address (result_val);
   else
-    result = value_as_address (result_val);
-
-  return result;
+    return value_as_address (result_val);
 }
 \f
 
diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c
index e2d6b32717b..55445021ad4 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -1430,8 +1430,6 @@ dwarf2_evaluate_loc_desc_full (struct type *type, struct frame_info *frame,
 			       struct type *subobj_type,
 			       LONGEST subobj_byte_offset)
 {
-  struct value *retval;
-
   if (subobj_type == NULL)
     {
       subobj_type = type;
@@ -1443,22 +1441,15 @@ dwarf2_evaluate_loc_desc_full (struct type *type, struct frame_info *frame,
   if (size == 0)
     return allocate_optimized_out_value (subobj_type);
 
-  dwarf_expr_context ctx (per_objfile);
-  ctx.frame = frame;
-  ctx.per_cu = per_cu;
-  ctx.obj_address = 0;
+  dwarf_expr_context ctx (per_objfile, per_cu->addr_size ());
 
+  struct value *retval;
   scoped_value_mark free_values;
 
-  ctx.gdbarch = per_objfile->objfile->arch ();
-  ctx.addr_size = per_cu->addr_size ();
-  ctx.ref_addr_size = per_cu->ref_addr_size ();
-
   try
     {
-      ctx.eval (data, size);
-      retval = ctx.fetch_result (type, subobj_type,
-				 subobj_byte_offset);
+      retval = ctx.evaluate (data, size, per_cu, frame, nullptr, type,
+			     subobj_type, subobj_byte_offset);
     }
   catch (const gdb_exception_error &ex)
     {
@@ -1528,30 +1519,24 @@ dwarf2_locexpr_baton_eval (const struct dwarf2_locexpr_baton *dlbaton,
     return 0;
 
   dwarf2_per_objfile *per_objfile = dlbaton->per_objfile;
-  dwarf_expr_context ctx (per_objfile);
+  struct dwarf2_per_cu_data *per_cu = dlbaton->per_cu;
+  dwarf_expr_context ctx (per_objfile, per_cu->addr_size ());
 
   struct value *result;
   scoped_value_mark free_values;
 
-  ctx.frame = frame;
-  ctx.per_cu = dlbaton->per_cu;
-  if (addr_stack != nullptr)
+  if (push_initial_value)
     {
-      ctx.obj_address = addr_stack->addr;
-      ctx.data_view = addr_stack->valaddr;
+      if (addr_stack != nullptr)
+	ctx.push_address (addr_stack->addr, false);
+      else
+	ctx.push_address (0, false);
     }
 
-  ctx.gdbarch = per_objfile->objfile->arch ();
-  ctx.addr_size = dlbaton->per_cu->addr_size ();
-  ctx.ref_addr_size = dlbaton->per_cu->ref_addr_size ();
-
-  if (push_initial_value)
-    ctx.push_address (ctx.obj_address, false);
-
   try
     {
-      ctx.eval (dlbaton->data, dlbaton->size);
-      result = ctx.fetch_result ();
+      result = ctx.evaluate (dlbaton->data, dlbaton->size,
+			     per_cu, frame, addr_stack);
     }
   catch (const gdb_exception_error &ex)
     {
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 17/43] Add as_lval argument to expression evaluator
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (15 preceding siblings ...)
  2021-03-01 14:45 ` [PATCH 16/43] Simplify dwarf_expr_context class interface Zoran Zaric
@ 2021-03-01 14:45 ` Zoran Zaric
  2021-04-28  3:04   ` Simon Marchi
  2021-04-28  3:30   ` Simon Marchi
  2021-03-01 14:45 ` [PATCH 18/43] Add new register access interface to expr.c Zoran Zaric
                   ` (25 subsequent siblings)
  42 siblings, 2 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:45 UTC (permalink / raw)
  To: gdb-patches

There are cases where the result of the expression evaluation is
expected to be in a form of a value and not location description.

One place that has this requirement is dwarf_entry_parameter_to_value
function, but more are expected in the future. Until now, this
requirement was fulfilled by extending the evaluated expression with
a DW_OP_stack_value operation at the end.

New implementation, introduces a new evaluation argument instead.

	* dwarf2/expr.c (dwarf_expr_context::fetch_result): Add as_lval
	argument.
	(dwarf_expr_context::eval_exp): Add as_lval argument.
	* dwarf2/expr.h (struct dwarf_expr_context): Add as_lval
	argument to fetch_result and eval_exp methods.
	* dwarf2/frame.c (execute_stack_op): Add as_lval argument.
	* dwarf2/loc.c (dwarf_entry_parameter_to_value): Remove
	DWARF expression extension.
	(dwarf2_evaluate_loc_desc_full): Add as_lval argument support.
	(dwarf2_evaluate_loc_desc): Add as_lval argument support.
	(dwarf2_locexpr_baton_eval): Add as_lval argument support.
---
 gdb/dwarf2/expr.c  | 12 +++++++++---
 gdb/dwarf2/expr.h  | 19 ++++++++++++-------
 gdb/dwarf2/frame.c |  2 +-
 gdb/dwarf2/loc.c   | 27 ++++++++++-----------------
 gdb/dwarf2/loc.h   |  6 ++++--
 5 files changed, 36 insertions(+), 30 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 1645f477d74..c50bb3c8d90 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -899,7 +899,8 @@ dwarf_expr_context::push_dwarf_reg_entry_value
 struct value *
 dwarf_expr_context::fetch_result (struct type *type,
 				  struct type *subobj_type,
-				  LONGEST subobj_offset)
+				  LONGEST subobj_offset,
+				  bool as_lval)
 {
   struct value *retval = nullptr;
 
@@ -929,6 +930,11 @@ dwarf_expr_context::fetch_result (struct type *type,
     }
   else
     {
+      /* If AS_LVAL is false, means that the implicit conversion
+	 from a location description to value is expected.  */
+      if (as_lval == false)
+	this->location = DWARF_VALUE_STACK;
+
       switch (this->location)
 	{
 	case DWARF_VALUE_REGISTER:
@@ -1053,7 +1059,7 @@ dwarf_expr_context::fetch_result (struct type *type,
 /* See expr.h.  */
 
 struct value *
-dwarf_expr_context::evaluate (const gdb_byte *addr, size_t len,
+dwarf_expr_context::evaluate (const gdb_byte *addr, size_t len, bool as_lval,
 			      struct dwarf2_per_cu_data *per_cu,
 			      struct frame_info *frame,
 			      const struct property_addr_info *addr_info,
@@ -1069,7 +1075,7 @@ dwarf_expr_context::evaluate (const gdb_byte *addr, size_t len,
     this->ref_addr_size = per_cu->ref_addr_size ();
 
   eval (addr, len);
-  return fetch_result (type, subobj_type, subobj_offset);
+  return fetch_result (type, subobj_type, subobj_offset, as_lval);
 }
 
 /* Require that TYPE be an integral type; throw an exception if not.  */
diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
index d1374068732..3f519e81849 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -130,11 +130,13 @@ struct dwarf_expr_context
   void push_address (CORE_ADDR value, bool in_stack_memory);
 
   /* Evaluate the expression at ADDR (LEN bytes long) in a given PER_CU
-     FRAME context.  Where TYPE, SUBOBJ_TYPE and SUBOBJ_OFFSET describe
-     expected struct value representation of the evaluation result.
-     The ADDR_INFO property can be specified to override the range of
-     memory addresses with the passed in buffer.  */
-  struct value *evaluate (const gdb_byte *addr, size_t len,
+     FRAME context.  AS_LVAL defines if the returned struct value is
+     expected to be a value or a location description.  Where TYPE,
+     SUBOBJ_TYPE and SUBOBJ_OFFSET describe expected struct value
+     representation of the evaluation result.  The ADDR_INFO property
+     can be specified to override the range of memory addresses with
+     the passed in buffer.  */
+  struct value *evaluate (const gdb_byte *addr, size_t len, bool as_lval,
 			  struct dwarf2_per_cu_data *per_cu,
 			  struct frame_info *frame,
 			  const struct property_addr_info *addr_info = nullptr,
@@ -223,10 +225,13 @@ struct dwarf_expr_context
 
   /* Fetch the result of the expression evaluation in a form of
      a struct value, where TYPE, SUBOBJ_TYPE and SUBOBJ_OFFSET
-     describe the source level representation of that result.  */
+     describe the source level representation of that result.
+     AS_LVAL defines if the fetched struct value is expected to
+     be a value or a location description.  */
   struct value *fetch_result (struct type *type,
 			      struct type *subobj_type,
-			      LONGEST subobj_offset);
+			      LONGEST subobj_offset,
+			      bool as_lval);
 
   /* Return the location expression for the frame base attribute, in
      START and LENGTH.  The result must be live until the current
diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
index 07667c3dee5..51d308fd94f 100644
--- a/gdb/dwarf2/frame.c
+++ b/gdb/dwarf2/frame.c
@@ -232,7 +232,7 @@ execute_stack_op (const gdb_byte *exp, ULONGEST len, int addr_size,
   scoped_value_mark free_values;
 
   ctx.push_address (initial, initial_in_stack_memory);
-  struct value *result_val = ctx.evaluate (exp, len, nullptr, this_frame);
+  struct value *result_val = ctx.evaluate (exp, len, true, nullptr, this_frame);
 
   if (VALUE_LVAL (result_val) == lval_memory)
     return value_address (result_val);
diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c
index 55445021ad4..c53413a942f 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -50,7 +50,7 @@
 static struct value *dwarf2_evaluate_loc_desc_full
   (struct type *type, struct frame_info *frame, const gdb_byte *data,
    size_t size, dwarf2_per_cu_data *per_cu, dwarf2_per_objfile *per_objfile,
-   struct type *subobj_type, LONGEST subobj_byte_offset);
+   struct type *subobj_type, LONGEST subobj_byte_offset, bool as_lval = true);
 
 /* Until these have formal names, we define these here.
    ref: http://gcc.gnu.org/wiki/DebugFission
@@ -1183,7 +1183,6 @@ dwarf_entry_parameter_to_value (struct call_site_parameter *parameter,
 				dwarf2_per_objfile *per_objfile)
 {
   const gdb_byte *data_src;
-  gdb_byte *data;
   size_t size;
 
   data_src = deref_size == -1 ? parameter->value : parameter->data_value;
@@ -1194,15 +1193,8 @@ dwarf_entry_parameter_to_value (struct call_site_parameter *parameter,
     throw_error (NO_ENTRY_VALUE_ERROR,
 		 _("Cannot resolve DW_AT_call_data_value"));
 
-  /* DW_AT_call_value is a DWARF expression, not a DWARF
-     location.  Postprocessing of DWARF_VALUE_MEMORY would lose the type from
-     DWARF block.  */
-  data = (gdb_byte *) alloca (size + 1);
-  memcpy (data, data_src, size);
-  data[size] = DW_OP_stack_value;
-
-  return dwarf2_evaluate_loc_desc (type, caller_frame, data, size + 1, per_cu,
-				   per_objfile);
+  return dwarf2_evaluate_loc_desc (type, caller_frame, data_src, size, per_cu,
+				   per_objfile, false);
 }
 
 /* VALUE must be of type lval_computed with entry_data_value_funcs.  Perform
@@ -1428,7 +1420,8 @@ dwarf2_evaluate_loc_desc_full (struct type *type, struct frame_info *frame,
 			       dwarf2_per_cu_data *per_cu,
 			       dwarf2_per_objfile *per_objfile,
 			       struct type *subobj_type,
-			       LONGEST subobj_byte_offset)
+			       LONGEST subobj_byte_offset,
+			       bool as_lval)
 {
   if (subobj_type == NULL)
     {
@@ -1448,8 +1441,8 @@ dwarf2_evaluate_loc_desc_full (struct type *type, struct frame_info *frame,
 
   try
     {
-      retval = ctx.evaluate (data, size, per_cu, frame, nullptr, type,
-			     subobj_type, subobj_byte_offset);
+      retval = ctx.evaluate (data, size, as_lval, per_cu, frame, nullptr,
+			     type, subobj_type, subobj_byte_offset);
     }
   catch (const gdb_exception_error &ex)
     {
@@ -1490,10 +1483,10 @@ struct value *
 dwarf2_evaluate_loc_desc (struct type *type, struct frame_info *frame,
 			  const gdb_byte *data, size_t size,
 			  dwarf2_per_cu_data *per_cu,
-			  dwarf2_per_objfile *per_objfile)
+			  dwarf2_per_objfile *per_objfile, bool as_lval)
 {
   return dwarf2_evaluate_loc_desc_full (type, frame, data, size, per_cu,
-					per_objfile, NULL, 0);
+					per_objfile, NULL, 0, as_lval);
 }
 
 /* Evaluates a dwarf expression and stores the result in VAL,
@@ -1536,7 +1529,7 @@ dwarf2_locexpr_baton_eval (const struct dwarf2_locexpr_baton *dlbaton,
   try
     {
       result = ctx.evaluate (dlbaton->data, dlbaton->size,
-			     per_cu, frame, addr_stack);
+			     true, per_cu, frame, addr_stack);
     }
   catch (const gdb_exception_error &ex)
     {
diff --git a/gdb/dwarf2/loc.h b/gdb/dwarf2/loc.h
index 02c686e5592..c5cacce1ceb 100644
--- a/gdb/dwarf2/loc.h
+++ b/gdb/dwarf2/loc.h
@@ -69,14 +69,16 @@ struct call_site_parameter *dwarf_expr_reg_to_entry_parameter
 
 /* Evaluate a location description, starting at DATA and with length
    SIZE, to find the current location of variable of TYPE in the context
-   of FRAME.  */
+   of FRAME.  AS_LVAL defines if the resulting struct value is expected to
+   be a value or a location description.  */
 
 struct value *dwarf2_evaluate_loc_desc (struct type *type,
 					struct frame_info *frame,
 					const gdb_byte *data,
 					size_t size,
 					dwarf2_per_cu_data *per_cu,
-					dwarf2_per_objfile *per_objfile);
+					dwarf2_per_objfile *per_objfile,
+					bool as_lval = true);
 
 /* A chain of addresses that might be needed to resolve a dynamic
    property.  */
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 18/43] Add new register access interface to expr.c
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (16 preceding siblings ...)
  2021-03-01 14:45 ` [PATCH 17/43] Add as_lval argument to expression evaluator Zoran Zaric
@ 2021-03-01 14:45 ` Zoran Zaric
  2021-03-08 23:52   ` Lancelot SIX
  2021-04-28  3:25   ` Simon Marchi
  2021-03-01 14:45 ` [PATCH 19/43] Add new memory " Zoran Zaric
                   ` (24 subsequent siblings)
  42 siblings, 2 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:45 UTC (permalink / raw)
  To: gdb-patches

DWARF expression evaluator is currently using get_frame_register_bytes
and put_frame_register_bytes interface for register access.

The problem with evaluator using this interface is that it allows a
bleed out register access. This means that if the caller specifies a
larger amount of data then the size of a specified register, the
operation will continue accessing the neighboring registers until a
full amount of data has been reached.

DWARF specification does not define this behavior, so a new simplified
register access interface is needed instead.

	* dwarf2/expr.c (read_from_register): New function.
	(write_to_register): New function.
	(rw_pieced_value): Now calls the read_from_register and
	write_to_register functions.
---
 gdb/dwarf2/expr.c | 128 ++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 106 insertions(+), 22 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index c50bb3c8d90..5a1fd5b941f 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -106,6 +106,96 @@ read_addr_from_reg (struct frame_info *frame, int reg)
   return address_from_register (regnum, frame);
 }
 
+/* Read register REGNUM's contents in a given FRAME context.
+
+   The data read is offsetted by OFFSET, and the number of bytes read
+   is defined by LENGTH.  The data is then copied into the
+   caller-managed buffer BUF.
+
+   If the register is optimized out or unavailable for the given
+   FRAME, the OPTIMIZED and UNAVAILABLE outputs are set
+   accordingly  */
+
+static void
+read_from_register (struct frame_info *frame, int regnum,
+		    CORE_ADDR offset, gdb::array_view<gdb_byte> buf,
+		    int *optimized, int *unavailable)
+{
+  struct gdbarch *gdbarch = get_frame_arch (frame);
+  int regsize = register_size (gdbarch, regnum);
+  int numregs = gdbarch_num_cooked_regs (gdbarch);
+  int length = buf.size ();
+
+  /* If a register is wholly inside the OFFSET, skip it.  */
+  if (frame == NULL || !regsize
+      || offset + length > regsize || numregs < regnum)
+    {
+      *optimized = 0;
+      *unavailable = 1;
+      return;
+    }
+
+  gdb::byte_vector temp_buf (regsize);
+  enum lval_type lval;
+  CORE_ADDR address;
+  int realnum;
+
+  frame_register (frame, regnum, optimized, unavailable,
+		  &lval, &address, &realnum, temp_buf.data ());
+
+  if (!*optimized && !*unavailable)
+     memcpy (buf.data (), (char *) temp_buf.data () + offset, length);
+
+  return;
+}
+
+/* Write register REGNUM's contents in a given FRAME context.
+
+   The data written is offsetted by OFFSET, and the number of bytes
+   written is defined by LENGTH.  The data is copied from
+   caller-managed buffer BUF.
+
+   If the register is optimized out or unavailable for the given
+   FRAME, the OPTIMIZED and UNAVAILABLE outputs are set
+   accordingly. */
+
+static void
+write_to_register (struct frame_info *frame, int regnum,
+		   CORE_ADDR offset, gdb::array_view<gdb_byte> buf,
+		   int *optimized, int *unavailable)
+{
+  struct gdbarch *gdbarch = get_frame_arch (frame);
+  int regsize = register_size (gdbarch, regnum);
+  int numregs = gdbarch_num_cooked_regs (gdbarch);
+  int length = buf.size ();
+
+  /* If a register is wholly inside of OFFSET, skip it.  */
+  if (frame == NULL || !regsize
+     || offset + length > regsize || numregs < regnum)
+    {
+      *optimized = 0;
+      *unavailable = 1;
+      return;
+    }
+
+  gdb::byte_vector temp_buf (regsize);
+  enum lval_type lval;
+  CORE_ADDR address;
+  int realnum;
+
+  frame_register (frame, regnum, optimized, unavailable,
+		  &lval, &address, &realnum, temp_buf.data ());
+
+  if (!*optimized && !*unavailable)
+    {
+      memcpy ((char *) temp_buf.data () + offset, buf.data (), length);
+
+      put_frame_register (frame, regnum, temp_buf.data ());
+    }
+
+  return;
+}
+
 struct piece_closure
 {
   /* Reference count.  */
@@ -243,22 +333,18 @@ rw_pieced_value (struct value *v, struct value *from)
 	    if (from == NULL)
 	      {
 		/* Read mode.  */
-		if (!get_frame_register_bytes (frame, gdb_regnum,
-					       bits_to_skip / 8,
-					       buffer, &optim, &unavail))
-		  {
-		    if (optim)
-		      mark_value_bits_optimized_out (v, offset,
-						     this_size_bits);
-		    if (unavail)
-		      mark_value_bits_unavailable (v, offset,
-						   this_size_bits);
-		    break;
-		  }
-
-		copy_bitwise (v_contents, offset,
-			      buffer.data (), bits_to_skip % 8,
-			      this_size_bits, bits_big_endian);
+		read_from_register (frame, gdb_regnum, bits_to_skip / 8,
+				    buffer, &optim, &unavail);
+
+		if (optim)
+		  mark_value_bits_optimized_out (v, offset, this_size_bits);
+		if (unavail)
+		  mark_value_bits_unavailable (v, offset, this_size_bits);
+		/* Only copy data if valid.  */
+		if (!optim && !unavail)
+		  copy_bitwise (v_contents, offset,
+				buffer.data (), bits_to_skip % 8,
+				this_size_bits, bits_big_endian);
 	      }
 	    else
 	      {
@@ -267,9 +353,8 @@ rw_pieced_value (struct value *v, struct value *from)
 		  {
 		    /* Data is copied non-byte-aligned into the register.
 		       Need some bits from original register value.  */
-		    get_frame_register_bytes (frame, gdb_regnum,
-					      bits_to_skip / 8,
-					      buffer, &optim, &unavail);
+		    read_from_register (frame, gdb_regnum, bits_to_skip / 8,
+					buffer, &optim, &unavail);
 		    if (optim)
 		      throw_error (OPTIMIZED_OUT_ERROR,
 				   _("Can't do read-modify-write to "
@@ -285,9 +370,8 @@ rw_pieced_value (struct value *v, struct value *from)
 		copy_bitwise (buffer.data (), bits_to_skip % 8,
 			      from_contents, offset,
 			      this_size_bits, bits_big_endian);
-		put_frame_register_bytes (frame, gdb_regnum,
-					  bits_to_skip / 8,
-					  buffer);
+		write_to_register (frame, gdb_regnum, bits_to_skip / 8,
+				   buffer, &optim, &unavail);
 	      }
 	  }
 	  break;
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 19/43] Add new memory access interface to expr.c
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (17 preceding siblings ...)
  2021-03-01 14:45 ` [PATCH 18/43] Add new register access interface to expr.c Zoran Zaric
@ 2021-03-01 14:45 ` Zoran Zaric
  2021-04-30 21:24   ` Simon Marchi
  2021-03-01 14:45 ` [PATCH 20/43] Add new classes that model DWARF stack element Zoran Zaric
                   ` (23 subsequent siblings)
  42 siblings, 1 reply; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:45 UTC (permalink / raw)
  To: gdb-patches

DWARF expression evaluator is currently using a few different
interfaces for memory access: write_memory_with_notification,
read_value_memory, read_memory.

They all seem incosistent, while some of them even need a struct
value typed argument to be present.

This patch is simplifying that interface by replacing it with two new
low level functions: read_from_memory and write_to_memory.

The advantage of this new interface is that it behaves in the same way
as the register access interface from the previous patch. Both of these
have the same error returning policy, which will be usefull for the
following patches.

	* dwarf2/expr.c (xfer_memory):  New function.
	(read_from_memory): New function.
	(write_to_memory): New function.
	(rw_pieced_value): Now calls the read_from_memory and
	write_to_memory functions.
---
 gdb/dwarf2/expr.c | 187 +++++++++++++++++++++++++++++++++++-----------
 1 file changed, 144 insertions(+), 43 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 5a1fd5b941f..1abece7d173 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -32,6 +32,8 @@
 #include "frame.h"
 #include "gdbsupport/underlying.h"
 #include "gdbarch.h"
+#include "inferior.h"
+#include "observable.h"
 
 /* Cookie for gdbarch data.  */
 
@@ -196,6 +198,85 @@ write_to_register (struct frame_info *frame, int regnum,
   return;
 }
 
+/* Helper for read_from_memory and write_to_memory.  */
+
+static void
+xfer_memory (CORE_ADDR address, gdb_byte *readbuf,
+	     const gdb_byte *writebuf,
+	     size_t length, bool stack, int *unavailable)
+{
+  *unavailable = 0;
+
+  enum target_object object
+    = stack ? TARGET_OBJECT_STACK_MEMORY : TARGET_OBJECT_MEMORY;
+
+  ULONGEST xfered_total = 0;
+
+  while (xfered_total < length)
+    {
+      ULONGEST xfered_partial;
+
+      enum target_xfer_status status
+	= target_xfer_partial (current_top_target (), object, NULL,
+			       (readbuf != nullptr
+				? readbuf + xfered_total
+				: nullptr),
+			       (writebuf != nullptr
+				? writebuf + xfered_total
+				: nullptr),
+			       address + xfered_total, length - xfered_total,
+			       &xfered_partial);
+
+      if (status == TARGET_XFER_OK)
+	{
+	  xfered_total += xfered_partial;
+	  QUIT;
+	}
+      else if (status == TARGET_XFER_UNAVAILABLE)
+	{
+	  *unavailable = 1;
+	  return;
+	}
+      else if (status == TARGET_XFER_EOF)
+	memory_error (TARGET_XFER_E_IO, address + xfered_total);
+      else
+	memory_error (status, address + xfered_total);
+    }
+}
+
+/* Read LENGTH bytes of memory contents starting at ADDRESS.
+
+   The data read is copied to a caller-managed buffer BUF.  STACK
+   indicates whether the memory range specified belongs to a stack
+   memory region.
+
+   If the memory is unavailable, the UNAVAILABLE output is set.  */
+
+static void
+read_from_memory (CORE_ADDR address, gdb_byte *buffer,
+		  size_t length, bool stack, int *unavailable)
+{
+  xfer_memory (address, buffer, nullptr, length, stack, unavailable);
+}
+
+/* Write LENGTH bytes of memory contents starting at ADDRESS.
+
+   The data written is copied from a caller-managed buffer buf.  STACK
+   indicates whether the memory range specified belongs to a stack
+   memory region.
+
+   If the memory is unavailable, the UNAVAILABLE output is set.  */
+
+static void
+write_to_memory (CORE_ADDR address, const gdb_byte *buffer,
+		 size_t length, bool stack, int *unavailable)
+{
+  xfer_memory (address, nullptr, buffer, length, stack, unavailable);
+
+  gdb::observers::memory_changed.notify (current_inferior (), address,
+					 length, buffer);
+}
+
 struct piece_closure
 {
   /* Reference count.  */
@@ -381,66 +462,86 @@ rw_pieced_value (struct value *v, struct value *from)
 	    bits_to_skip += p->offset;
 
 	    CORE_ADDR start_addr = p->v.mem.addr + bits_to_skip / 8;
+	    bool in_stack_memory = p->v.mem.in_stack_memory;
+	    int unavail = 0;
 
 	    if (bits_to_skip % 8 == 0 && this_size_bits % 8 == 0
 		&& offset % 8 == 0)
 	      {
 		/* Everything is byte-aligned; no buffer needed.  */
 		if (from != NULL)
-		  write_memory_with_notification (start_addr,
-						  (from_contents
-						   + offset / 8),
-						  this_size_bits / 8);
+		  write_to_memory (start_addr, (from_contents + offset / 8),
+				   this_size_bits / 8, in_stack_memory,
+				   &unavail);
 		else
-		  read_value_memory (v, offset,
-				     p->v.mem.in_stack_memory,
-				     p->v.mem.addr + bits_to_skip / 8,
-				     v_contents + offset / 8,
-				     this_size_bits / 8);
-		break;
-	      }
-
-	    this_size = bits_to_bytes (bits_to_skip, this_size_bits);
-	    buffer.resize (this_size);
-
-	    if (from == NULL)
-	      {
-		/* Read mode.  */
-		read_value_memory (v, offset,
-				   p->v.mem.in_stack_memory,
-				   p->v.mem.addr + bits_to_skip / 8,
-				   buffer.data (), this_size);
-		copy_bitwise (v_contents, offset,
-			      buffer.data (), bits_to_skip % 8,
-			      this_size_bits, bits_big_endian);
+		  read_from_memory (start_addr, (v_contents + offset / 8),
+				    this_size_bits / 8, in_stack_memory,
+				    &unavail);
 	      }
 	    else
 	      {
-		/* Write mode.  */
-		if (bits_to_skip % 8 != 0 || this_size_bits % 8 != 0)
+		this_size = bits_to_bytes (bits_to_skip, this_size_bits);
+		buffer.resize (this_size);
+
+		if (from == NULL)
 		  {
-		    if (this_size <= 8)
+		    /* Read mode.  */
+		    read_from_memory (start_addr, buffer.data (),
+				      this_size, in_stack_memory,
+				      &unavail);
+		    if (!unavail)
+		      copy_bitwise (v_contents, offset,
+				    buffer.data (), bits_to_skip % 8,
+				    this_size_bits, bits_big_endian);
+		  }
+		else
+		  {
+		    /* Write mode.  */
+		    if (bits_to_skip % 8 != 0 || this_size_bits % 8 != 0)
 		      {
-			/* Perform a single read for small sizes.  */
-			read_memory (start_addr, buffer.data (),
-				     this_size);
+			if (this_size <= 8)
+			  {
+			    /* Perform a single read for small sizes.  */
+			    read_from_memory (start_addr, buffer.data (),
+					      this_size, in_stack_memory,
+					      &unavail);
+			  }
+			else
+			  {
+			    /* Only the first and last bytes can possibly have
+			       any bits reused.  */
+			    read_from_memory (start_addr, buffer.data (),
+					      1, in_stack_memory,
+					      &unavail);
+			    if (!unavail)
+			      read_from_memory (start_addr + this_size - 1,
+						&buffer[this_size - 1], 1,
+						in_stack_memory, &unavail);
+			  }
 		      }
-		    else
+
+		    if (!unavail)
 		      {
-			/* Only the first and last bytes can possibly have
-			   any bits reused.  */
-			read_memory (start_addr, buffer.data (), 1);
-			read_memory (start_addr + this_size - 1,
-				     &buffer[this_size - 1], 1);
+			copy_bitwise (buffer.data (), bits_to_skip % 8,
+				      from_contents, offset,
+				      this_size_bits, bits_big_endian);
+			write_to_memory (start_addr, buffer.data (),
+					 this_size, in_stack_memory,
+					 &unavail);
 		      }
 		  }
+	      }
 
-		copy_bitwise (buffer.data (), bits_to_skip % 8,
-			      from_contents, offset,
-			      this_size_bits, bits_big_endian);
-		write_memory_with_notification (start_addr,
-						buffer.data (),
-						this_size);
+	    if (unavail)
+	      {
+		if (from == NULL)
+		  mark_value_bits_unavailable (v, (offset + bits_to_skip % 8),
+					       this_size_bits);
+		else
+		  throw_error (NOT_AVAILABLE_ERROR,
+			       _("Can't do read-modify-write to "
+				 "update bitfield; containing word "
+				 "is unavailable"));
 	      }
 	  }
 	  break;
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 20/43] Add new classes that model DWARF stack element
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (18 preceding siblings ...)
  2021-03-01 14:45 ` [PATCH 19/43] Add new memory " Zoran Zaric
@ 2021-03-01 14:45 ` Zoran Zaric
  2021-03-01 14:45 ` [PATCH 21/43] Add to_location method to DWARF entry classes Zoran Zaric
                   ` (22 subsequent siblings)
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:45 UTC (permalink / raw)
  To: gdb-patches

The rest of the patch series addresses the issues described in a
"Motivation" section of the AMD’s DWARF standard extensions that
can be found at:

https://llvm.org/docs/AMDGPUDwarfExtensionsForHeterogeneousDebugging.html

The document describes a couple of main issues found when using the
current DWARF 5 version to describe optimized code for SIMD and SIMT
architectures.

Without going much into details described in the document, the main
point is that DWARF version 5 does not allow a proper support for
address spaces and it does not allow a location description to be part
of a DWARF expression, unless it is a leaf node of that expression.

Both issues can be solved in a clean way by introducing a new set of
classes that describe all entry types which can be placed on a DWARF
stack, while keeping a full backward compatibility with the previous
standard version. These entry types can now be either a typed value
or any location description.

Currently, the result of an expression evaluation is kept in a separate
data structure, while with the new approach, it will be always found as
a top element of the DWARF stack. The reason why this approach is
backward compatible is because in version 5, a location description
is only allowed to be a leaf node of the expression or a composite
piece.

Question here is, why do we need a new set of classes and why not just
use the struct value instead?

As it stands, there are couple of issues with using the struct value to
describe a DWARF stack element:

 - It is not designed to represent a DWARF location description
specifically, instead it behaves more like unified debug information
format that represents an actual target resource. One example of this
is accessing data of a struct value register location description,
where if the amount of data accessed is larger then the register,
results in accessing more then one register. In DWARF this is not a
valid behavior and locations that span more then one register should be
described as a composite location description.

- There is a tight coupling between struct value and it’s type
information, regardless if the data represented is describing a value
(not_lval) or a location description. While the type information
dictates how the data is accessed for a struct value case, in DWARF,
location description doesn’t have a type so data access is not bound by
it.

- DWARF values only support much simpler base types, while struct value
can be linked to any type. Admittedly, new classes are still using the
same struct value infrastructure for a value based operations at the
moment, but that is planned to change in the near future.

- struct value register location description requires a frame id
information which makes them unsuitable for CFA expression evaluation.

So, there seems to be a lack of separation of concerns in the design
of a struct value infrastructure, while the new classes are handling
one specific purpose and are completely encapsulated in the expr.c.

Additional benefit of this design is that it makes a step in a
right direction for being able to evaluate DWARF expressions on a
gdbserver side in the near future, which sounds like a desirable thing.

It is also worth mentioning that this new location description
representation is based on a bit granularity (the bit_suboffset class
member) even though the DWARF standard has a very limited support for
it (mostly used for DW_OP_bit_piece operation).

By allowing any location description to define a bit sub-offset of the
location, we are able to give more options for supporting of new
concepts (like the existing packed arrays in Ada language).

In this patch, a new set of classes that describe a DWARF stack element
are added. The new classes are:

- Value - describes a numerical value with a DWARF base type.
- Location description - describes a DWARF location description.
  - Undefined location - describes a location that is not defined.
  - Memory location - describes a location in memory.
  - Register location - describes a register location in a frame
    context.
  - Implicit location - describes a location that implicitly holds a
    value that it describes.
  - Implicit pointer - describes a concept of an implicit pointer to
    a source variable.
  - Composite location - describes a location that is composed from
    pieces of other location descriptions.

For now, these classes are just defined, and they are planned to be
used by the following patches.

gdb/ChangeLog:

	* dwarf2/expr.c (class dwarf_entry): New class.
	(class dwarf_value): New class.
	(class dwarf_location): New class.
	(class dwarf_undefined): New class.
	(class dwarf_memory): New class.
	(class dwarf_register): New class.
	(class dwarf_implicit): New class.
	(class dwarf_implicit_pointer): New class.
	(class dwarf_composite): New class.
	* value.c (pack_unsigned_long): Expose function.
	* value.h (pack_unsigned_long): Expose function.
---
 gdb/dwarf2/expr.c | 264 ++++++++++++++++++++++++++++++++++++++++++++++
 gdb/value.c       |   2 +-
 gdb/value.h       |   2 +
 3 files changed, 267 insertions(+), 1 deletion(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 1abece7d173..5d47dc83aaa 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -277,6 +277,270 @@ write_to_memory (CORE_ADDR address, const gdb_byte *buffer,
 					 length, buffer);
 }
 
+/* Base class that describes entries found on a DWARF expression
+   evaluation stack.  */
+
+class dwarf_entry : public std::enable_shared_from_this<dwarf_entry>
+{
+public:
+  /* Not expected to be called on it's own.  */
+  dwarf_entry () = default;
+
+  virtual ~dwarf_entry () = 0;
+};
+
+dwarf_entry::~dwarf_entry () = default;
+
+/* Location description entry found on a DWARF expression evaluation
+   stack.
+
+   Types of locations descirbed can be: register location, memory
+   location, implicit location, implicit pointer location, undefined
+   location and composite location (composed out of any of the location
+   types including another composite location).  */
+
+class dwarf_location : public dwarf_entry
+{
+public:
+  /* Not expected to be called on it's own.  */
+  dwarf_location (struct gdbarch *arch, LONGEST offset = 0,
+		  LONGEST bit_suboffset = 0)
+    : m_arch (arch), m_initialised (true)
+  {
+    m_offset = offset;
+    m_offset += bit_suboffset / HOST_CHAR_BIT;
+    m_bit_suboffset = bit_suboffset % HOST_CHAR_BIT;
+  }
+
+  virtual ~dwarf_location () = default;
+
+  /* Add bit offset to the location description.  */
+  void add_bit_offset (LONGEST bit_offset)
+  {
+    LONGEST bit_total_offset = m_bit_suboffset + bit_offset;
+
+    m_offset += bit_total_offset / HOST_CHAR_BIT;
+    m_bit_suboffset = bit_total_offset % HOST_CHAR_BIT;
+  };
+
+  void set_initialised (bool initialised)
+  {
+    m_initialised = initialised;
+  };
+
+protected:
+  /* Architecture of the location.  */
+  struct gdbarch *m_arch;
+
+  /* Byte offset into the location.  */
+  LONGEST m_offset;
+
+  /* Bit suboffset of the last byte.  */
+  LONGEST m_bit_suboffset;
+
+  /* Whether the location is initialized.  Used for non-standard
+     DW_OP_GNU_uninit operation.  */
+  bool m_initialised;
+};
+
+/* Value entry found on a DWARF expression evaluation stack.  */
+
+class dwarf_value : public dwarf_entry
+{
+public:
+  dwarf_value (const gdb_byte *contents, struct type *type)
+  {
+    size_t type_len = TYPE_LENGTH (type);
+    m_contents.reset ((gdb_byte *) xzalloc (type_len));
+
+    memcpy (m_contents.get (), contents, type_len);
+    m_type = type;
+  }
+
+  dwarf_value (ULONGEST value, struct type *type)
+  {
+    m_contents.reset ((gdb_byte *) xzalloc (TYPE_LENGTH (type)));
+
+    pack_unsigned_long (m_contents.get (), type, value);
+    m_type = type;
+  }
+
+  dwarf_value (LONGEST value, struct type *type)
+  {
+    m_contents.reset ((gdb_byte *) xzalloc (TYPE_LENGTH (type)));
+
+    pack_long (m_contents.get (), type, value);
+    m_type = type;
+  }
+
+  const gdb_byte* get_contents () const
+  {
+    return m_contents.get ();
+  }
+
+  struct type* get_type () const
+  {
+    return m_type;
+  }
+
+  LONGEST to_long () const
+  {
+    return unpack_long (m_type, m_contents.get ());
+  }
+
+private:
+  /* Value contents as a stream of bytes in target byte order.  */
+  gdb::unique_xmalloc_ptr<gdb_byte> m_contents;
+
+  /* Type of the value held by the entry.  */
+  struct type *m_type;
+};
+
+/* Undefined location description entry.  This is a special location
+   description type that describes the location description that is
+   not known.  */
+
+class dwarf_undefined : public dwarf_location
+{
+public:
+  dwarf_undefined (struct gdbarch *arch, LONGEST offset = 0,
+		   LONGEST bit_suboffset = 0)
+    : dwarf_location (arch, offset, bit_suboffset)
+  {}
+};
+
+class dwarf_memory : public dwarf_location
+{
+public:
+  dwarf_memory (struct gdbarch *arch, LONGEST offset,
+	        LONGEST bit_suboffset = 0, bool stack = false)
+    : dwarf_location (arch, offset, bit_suboffset),
+      m_stack (stack)
+  {}
+
+  void set_stack (bool stack)
+  {
+    m_stack = stack;
+  };
+
+private:
+  /* True if the location belongs to a stack memory region.  */
+  bool m_stack;
+};
+
+/* Register location description entry.  */
+
+class dwarf_register : public dwarf_location
+{
+public:
+  dwarf_register (struct gdbarch *arch, unsigned int regnum,
+		  LONGEST offset = 0, LONGEST bit_suboffset = 0)
+    : dwarf_location (arch, offset, bit_suboffset),
+      m_regnum (regnum)
+  {}
+
+private:
+  /* DWARF register number.  */
+  unsigned int m_regnum;
+};
+
+/* Implicit location description entry.  Describes a location
+   description not found on the target but instead saved in a
+   gdb-allocated buffer.  */
+
+class dwarf_implicit : public dwarf_location
+{
+public:
+
+  dwarf_implicit (struct gdbarch *arch, const gdb_byte *contents,
+		  size_t size, enum bfd_endian byte_order)
+    : dwarf_location (arch)
+  {
+    m_contents.reset ((gdb_byte *) xzalloc (size));
+
+    memcpy (m_contents.get (), contents, size);
+    m_size = size;
+    m_byte_order = byte_order;
+  }
+
+private:
+  /* Implicit location contents as a stream of bytes in target byte-order.  */
+  gdb::unique_xmalloc_ptr<gdb_byte> m_contents;
+
+  /* Contents byte stream size.  */
+  size_t m_size;
+
+  /* Contents original byte order.  */
+  enum bfd_endian m_byte_order;
+};
+
+/* Implicit pointer location description entry.  */
+
+class dwarf_implicit_pointer : public dwarf_location
+{
+public:
+  dwarf_implicit_pointer (struct gdbarch *arch,
+			  dwarf2_per_objfile *per_objfile,
+			  struct dwarf2_per_cu_data *per_cu,
+			  int addr_size, sect_offset die_offset,
+			  LONGEST offset, LONGEST bit_suboffset = 0)
+    : dwarf_location (arch, offset, bit_suboffset),
+      m_per_objfile (per_objfile), m_per_cu (per_cu),
+      m_addr_size (addr_size), m_die_offset (die_offset)
+  {}
+
+private:
+  /* Per object file data of the implicit pointer.  */
+  dwarf2_per_objfile *m_per_objfile;
+
+  /* Compilation unit context of the implicit pointer.  */
+  struct dwarf2_per_cu_data *m_per_cu;
+
+  /* Address size for the evaluation.  */
+  int m_addr_size;
+
+  /* DWARF die offset pointed by the implicit pointer.  */
+  sect_offset m_die_offset;
+};
+
+/* Composite location description entry.  */
+
+class dwarf_composite : public dwarf_location
+{
+public:
+  dwarf_composite (struct gdbarch *arch, struct dwarf2_per_cu_data *per_cu,
+		   LONGEST offset = 0, LONGEST bit_suboffset = 0)
+    : dwarf_location (arch, offset, bit_suboffset), m_per_cu (per_cu)
+  {}
+
+  void add_piece (std::shared_ptr<dwarf_location> location, ULONGEST bit_size)
+  {
+    gdb_assert (location != nullptr);
+    m_pieces.emplace_back (location, bit_size);
+  }
+
+private:
+  /* Composite piece that contains a piece location
+     description and it's size.  */
+  class piece
+  {
+  public:
+    piece (std::shared_ptr<dwarf_location> location, ULONGEST size)
+    : m_location (location),
+      m_size (size)
+    {}
+
+    std::shared_ptr<dwarf_location> m_location;
+    ULONGEST m_size;
+  };
+
+  /* Compilation unit context of the pointer.  */
+  struct dwarf2_per_cu_data *m_per_cu;
+
+  /* Vector of composite pieces.  */
+  std::vector<struct piece> m_pieces;
+};
+
 struct piece_closure
 {
   /* Reference count.  */
diff --git a/gdb/value.c b/gdb/value.c
index 9f3914a5bc5..52f55f0bb97 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -3436,7 +3436,7 @@ pack_long (gdb_byte *buf, struct type *type, LONGEST num)
 
 /* Pack NUM into BUF using a target format of TYPE.  */
 
-static void
+void
 pack_unsigned_long (gdb_byte *buf, struct type *type, ULONGEST num)
 {
   LONGEST len;
diff --git a/gdb/value.h b/gdb/value.h
index 60a831c38c4..cbb60e2f073 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -678,6 +678,8 @@ extern struct value *value_field_bitfield (struct type *type, int fieldno,
 					   const struct value *val);
 
 extern void pack_long (gdb_byte *buf, struct type *type, LONGEST num);
+extern void pack_unsigned_long (gdb_byte *buf, struct type *type,
+				ULONGEST num);
 
 extern struct value *value_from_longest (struct type *type, LONGEST num);
 extern struct value *value_from_ulongest (struct type *type, ULONGEST num);
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 21/43] Add to_location method to DWARF entry classes
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (19 preceding siblings ...)
  2021-03-01 14:45 ` [PATCH 20/43] Add new classes that model DWARF stack element Zoran Zaric
@ 2021-03-01 14:45 ` Zoran Zaric
  2021-03-01 14:45 ` [PATCH 22/43] Add to_value " Zoran Zaric
                   ` (21 subsequent siblings)
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:45 UTC (permalink / raw)
  To: gdb-patches

DWARF standard already contains an implicit conversion between location
description and a DWARF value. In the DWARF 5 version, the only place
where this can happen is at the very end of the evaluation where a
client decides if the result is expected to be in a form of a value or
a location description (as_lval argument of the new evaluation method).

By allowing any location description to be on the DWARF stack, these
implicit conversions are expected on per operation basis which means
that the new DWARF entry classes need to have a support for those.

This patch adds a conversion method from any DWARF entry object into a
location description object.

gdb/ChangeLog:

        * dwarf2/expr.c (class dwarf_entry::to_location): New method.
        (class dwarf_value::to_location): New method.
        (class dwarf_location::to_location): New method.
---
 gdb/dwarf2/expr.c | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 5d47dc83aaa..d0c0efd2d31 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -277,6 +277,9 @@ write_to_memory (CORE_ADDR address, const gdb_byte *buffer,
 					 length, buffer);
 }
 
+class dwarf_location;
+class dwarf_memory;
+
 /* Base class that describes entries found on a DWARF expression
    evaluation stack.  */
 
@@ -287,6 +290,11 @@ class dwarf_entry : public std::enable_shared_from_this<dwarf_entry>
   dwarf_entry () = default;
 
   virtual ~dwarf_entry () = 0;
+
+  /* Convert DWARF entry into a DWARF location description.  ARCH
+     defines an architecture of the location described.   */
+  virtual std::shared_ptr<dwarf_location> to_location
+    (struct gdbarch *arch) = 0;
 };
 
 dwarf_entry::~dwarf_entry () = default;
@@ -328,6 +336,15 @@ class dwarf_location : public dwarf_entry
     m_initialised = initialised;
   };
 
+  /* Convert DWARF entry into a DWARF location description.  If the
+     entry is already a location description, it will be returned as a
+     result and no conversion will be applied to it.  ARCH defines an
+     architecture of the location described.  */
+  std::shared_ptr<dwarf_location> to_location (struct gdbarch *arch) override
+  {
+    return std::dynamic_pointer_cast<dwarf_location> (shared_from_this ());
+  }
+
 protected:
   /* Architecture of the location.  */
   struct gdbarch *m_arch;
@@ -388,6 +405,10 @@ class dwarf_value : public dwarf_entry
     return unpack_long (m_type, m_contents.get ());
   }
 
+  /* Convert DWARF value into a DWARF memory location description.
+     ARCH defines an architecture of the location described.  */
+  std::shared_ptr<dwarf_location> to_location (struct gdbarch *arch) override;
+
 private:
   /* Value contents as a stream of bytes in target byte order.  */
   gdb::unique_xmalloc_ptr<gdb_byte> m_contents;
@@ -396,6 +417,21 @@ class dwarf_value : public dwarf_entry
   struct type *m_type;
 };
 
+std::shared_ptr<dwarf_location>
+dwarf_value::to_location (struct gdbarch *arch)
+{
+  LONGEST offset;
+
+  if (gdbarch_integer_to_address_p (arch))
+    offset = gdbarch_integer_to_address (arch, m_type, m_contents.get ());
+  else
+    offset = extract_unsigned_integer (m_contents.get (), TYPE_LENGTH (m_type),
+				       type_byte_order (m_type));
+
+  auto memory = std::make_shared<dwarf_memory> (arch, offset);
+  return std::dynamic_pointer_cast<dwarf_location> (memory);
+}
+
 /* Undefined location description entry.  This is a special location
    description type that describes the location description that is
    not known.  */
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 22/43] Add to_value method to DWARF entry classes
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (20 preceding siblings ...)
  2021-03-01 14:45 ` [PATCH 21/43] Add to_location method to DWARF entry classes Zoran Zaric
@ 2021-03-01 14:45 ` Zoran Zaric
  2021-03-01 14:46 ` [PATCH 23/43] Add read method to location description classes Zoran Zaric
                   ` (20 subsequent siblings)
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:45 UTC (permalink / raw)
  To: gdb-patches

Following the idea from the last patch this patch is adding another
conversion method from any DWARF entry object into a DWARF value
object.

Currently, we only know how to convert from a memory location
description into a value, but it is resonable to expect a set of target
hooks that would let the target decide on how to do other conversions
in the future.

gdb/ChangeLog:

        * dwarf2/expr.c (dwarf_entry::to_value): New method.
        (dwarf_location::to_value): New method.
        (dwarf_memory::to_value): New method.
        (ill_formed_expression): New function.
---
 gdb/dwarf2/expr.c | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index d0c0efd2d31..69be36fcf2d 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -97,6 +97,14 @@ bits_to_bytes (ULONGEST start, ULONGEST n_bits)
   return (start % HOST_CHAR_BIT + n_bits + HOST_CHAR_BIT - 1) / HOST_CHAR_BIT;
 }
 
+/* Throw an exception about the invalid DWARF expression.  */
+
+static void
+ill_formed_expression ()
+{
+  error (_("Ill-formed DWARF expression"));
+}
+
 /* See expr.h.  */
 
 CORE_ADDR
@@ -279,6 +287,7 @@ write_to_memory (CORE_ADDR address, const gdb_byte *buffer,
 
 class dwarf_location;
 class dwarf_memory;
+class dwarf_value;
 
 /* Base class that describes entries found on a DWARF expression
    evaluation stack.  */
@@ -295,6 +304,11 @@ class dwarf_entry : public std::enable_shared_from_this<dwarf_entry>
      defines an architecture of the location described.   */
   virtual std::shared_ptr<dwarf_location> to_location
     (struct gdbarch *arch) = 0;
+
+  /* Convert DWARF entry into a DWARF value.  TYPE defines a
+     desired type of the returned DWARF value if it already
+     doesnt have one.  */
+  virtual std::shared_ptr<dwarf_value> to_value (struct type *type) = 0;
 };
 
 dwarf_entry::~dwarf_entry () = default;
@@ -345,6 +359,16 @@ class dwarf_location : public dwarf_entry
     return std::dynamic_pointer_cast<dwarf_location> (shared_from_this ());
   }
 
+  /* Convert DWARF entry into a DWARF value.  If the conversion
+     from that location description kind to a value is not supported
+     the result is an empty pointer.  TYPE defines a desired type of
+     the returned DWARF value if it already doesnt have one.  */
+  virtual std::shared_ptr<dwarf_value> to_value (struct type *type) override
+  {
+    ill_formed_expression ();
+    return std::shared_ptr<dwarf_value> (nullptr);
+  }
+
 protected:
   /* Architecture of the location.  */
   struct gdbarch *m_arch;
@@ -409,6 +433,14 @@ class dwarf_value : public dwarf_entry
      ARCH defines an architecture of the location described.  */
   std::shared_ptr<dwarf_location> to_location (struct gdbarch *arch) override;
 
+  /* Convert DWARF entry into a DWARF value.  If the entry
+     is already a value, it is just returned and the TYPE type
+     information is ignored.  */
+  std::shared_ptr<dwarf_value> to_value (struct type *type) override
+  {
+    return std::dynamic_pointer_cast<dwarf_value> (shared_from_this ());
+  }
+
 private:
   /* Value contents as a stream of bytes in target byte order.  */
   gdb::unique_xmalloc_ptr<gdb_byte> m_contents;
@@ -459,11 +491,19 @@ class dwarf_memory : public dwarf_location
     m_stack = stack;
   };
 
+  std::shared_ptr<dwarf_value> to_value (struct type *type) override;
+
 private:
   /* True if the location belongs to a stack memory region.  */
   bool m_stack;
 };
 
+std::shared_ptr<dwarf_value>
+dwarf_memory::to_value (struct type *type)
+{
+  return std::make_shared<dwarf_value> (m_offset, type);
+}
+
 /* Register location description entry.  */
 
 class dwarf_register : public dwarf_location
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 23/43] Add read method to location description classes
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (21 preceding siblings ...)
  2021-03-01 14:45 ` [PATCH 22/43] Add to_value " Zoran Zaric
@ 2021-03-01 14:46 ` Zoran Zaric
  2021-03-01 14:46 ` [PATCH 24/43] Add write " Zoran Zaric
                   ` (19 subsequent siblings)
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:46 UTC (permalink / raw)
  To: gdb-patches

After adding the interface for register and memory location access, a
new method for reading from any location derived from a dwarf_location
class, can now be defined.

In the case of implicit pointer location description the existing
indirect_synthetic_pointer interface requiers a type of the pointer
to be specified, so for a generic read interface the only type that
makes sense is the DWARF generic type. This means that the existing
address_type method of a dwarf_expr_context class needs to be exposed
outside of that class.

gdb/ChangeLog:

        * dwarf2/expr.c (dwarf_location::read): New method.
        (dwarf_undefined::read): New method.
        (dwarf_memory::read): New method.
        (dwarf_register::read): New method.
        (dwarf_implicit::read): New method.
        (dwarf_implicit_pointer::read): New method.
        (dwarf_composite::read): New method.
        (dwarf_expr_context::address_type): Change to use the new
        address_type function.
        (address_type): New function.
---
 gdb/dwarf2/expr.c | 305 ++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 283 insertions(+), 22 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 69be36fcf2d..35b2efa6cb5 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -285,6 +285,37 @@ write_to_memory (CORE_ADDR address, const gdb_byte *buffer,
 					 length, buffer);
 }
 
+/* Return the type used for DWARF operations where the type is
+   generic in the DWARF spec, the ARCH is a target architecture.
+   of the type and ADDR_SIZE is expected size of the address.
+   Only certain sizes are supported.  */
+
+static struct type *
+address_type (struct gdbarch *gdbarch, int addr_size)
+{
+  struct dwarf_gdbarch_types *types
+    = (struct dwarf_gdbarch_types *) gdbarch_data (gdbarch,
+						   dwarf_arch_cookie);
+  int ndx;
+
+  if (addr_size == 2)
+    ndx = 0;
+  else if (addr_size == 4)
+    ndx = 1;
+  else if (addr_size == 8)
+    ndx = 2;
+  else
+    error (_("Unsupported address size in DWARF expressions: %d bits"),
+	   8 * addr_size);
+
+  if (types->dw_types[ndx] == NULL)
+    types->dw_types[ndx]
+      = arch_integer_type (gdbarch, HOST_CHAR_BIT * addr_size,
+			   0, "<signed DWARF address type>");
+
+  return types->dw_types[ndx];
+}
+
 class dwarf_location;
 class dwarf_memory;
 class dwarf_value;
@@ -369,6 +400,27 @@ class dwarf_location : public dwarf_entry
     return std::shared_ptr<dwarf_value> (nullptr);
   }
 
+  /* Read contents from the descripbed location.
+
+     The read operation is performed in the context of a FRAME.
+     BIT_SIZE is the number of bits to read.  The data read is copied
+     to the caller-managed buffer BUF.  BIG_ENDIAN defines the
+     endianness of the target.  BITS_TO_SKIP is a bit offset into the
+     location and BUF_BIT_OFFSET is buffer BUF's bit offset.
+     LOCATION_BIT_LIMIT is a maximum number of bits that location can
+     hold, where value zero signifies that there is no such
+     restriction.
+
+     Note that some location types can be read without a FRAME context.
+
+     If the location is optimized out or unavailable, the OPTIMIZED and
+     UNAVAILABLE outputs are set accordingly.  */
+  virtual void read (struct frame_info *frame, gdb_byte *buf,
+		     int buf_bit_offset, size_t bit_size,
+		     LONGEST bits_to_skip, size_t location_bit_limit,
+		     bool big_endian, int *optimized,
+		     int *unavailable) const = 0;
+
 protected:
   /* Architecture of the location.  */
   struct gdbarch *m_arch;
@@ -475,6 +527,14 @@ class dwarf_undefined : public dwarf_location
 		   LONGEST bit_suboffset = 0)
     : dwarf_location (arch, offset, bit_suboffset)
   {}
+
+  void read (struct frame_info *frame, gdb_byte *buf, int buf_bit_offset,
+	     size_t bit_size, LONGEST bits_to_skip, size_t location_bit_limit,
+	     bool big_endian, int *optimized, int *unavailable) const override
+  {
+    *unavailable = 0;
+    *optimized = 1;
+  }
 };
 
 class dwarf_memory : public dwarf_location
@@ -493,6 +553,11 @@ class dwarf_memory : public dwarf_location
 
   std::shared_ptr<dwarf_value> to_value (struct type *type) override;
 
+  void read (struct frame_info *frame, gdb_byte *buf, int buf_bit_offset,
+	     size_t bit_size, LONGEST bits_to_skip,
+	     size_t location_bit_limit, bool big_endian,
+	     int *optimized, int *unavailable) const override;
+
 private:
   /* True if the location belongs to a stack memory region.  */
   bool m_stack;
@@ -504,6 +569,46 @@ dwarf_memory::to_value (struct type *type)
   return std::make_shared<dwarf_value> (m_offset, type);
 }
 
+void
+dwarf_memory::read (struct frame_info *frame, gdb_byte *buf,
+		    int buf_bit_offset, size_t bit_size,
+		    LONGEST bits_to_skip, size_t location_bit_limit,
+		    bool big_endian, int *optimized, int *unavailable) const
+{
+  LONGEST total_bits_to_skip = bits_to_skip;
+  CORE_ADDR start_address
+    = m_offset + (m_bit_suboffset + total_bits_to_skip) / HOST_CHAR_BIT;
+  gdb::byte_vector temp_buf;
+
+  *optimized = 0;
+  total_bits_to_skip += m_bit_suboffset;
+
+  if (total_bits_to_skip % HOST_CHAR_BIT == 0
+      && bit_size % HOST_CHAR_BIT == 0
+      && buf_bit_offset % HOST_CHAR_BIT == 0)
+    {
+      /* Everything is byte-aligned, no buffer needed.  */
+      read_from_memory (start_address,
+			buf + buf_bit_offset / HOST_CHAR_BIT,
+			bit_size / HOST_CHAR_BIT, m_stack, unavailable);
+    }
+  else
+    {
+      LONGEST this_size = bits_to_bytes (total_bits_to_skip, bit_size);
+      temp_buf.resize (this_size);
+
+      /* Can only read from memory on byte granularity so an
+	 additional buffer is required.  */
+      read_from_memory (start_address, temp_buf.data (), this_size,
+			m_stack, unavailable);
+
+      if (!*unavailable)
+	copy_bitwise (buf, buf_bit_offset, temp_buf.data (),
+		      total_bits_to_skip % HOST_CHAR_BIT,
+		      bit_size, big_endian);
+    }
+}
+
 /* Register location description entry.  */
 
 class dwarf_register : public dwarf_location
@@ -515,11 +620,55 @@ class dwarf_register : public dwarf_location
       m_regnum (regnum)
   {}
 
+  void read (struct frame_info *frame, gdb_byte *buf, int buf_bit_offset,
+	     size_t bit_size, LONGEST bits_to_skip, size_t location_bit_limit,
+	     bool big_endian, int *optimized, int *unavailable) const override;
+
 private:
   /* DWARF register number.  */
   unsigned int m_regnum;
 };
 
+void
+dwarf_register::read (struct frame_info *frame, gdb_byte *buf,
+		      int buf_bit_offset, size_t bit_size,
+		      LONGEST bits_to_skip, size_t location_bit_limit,
+		      bool big_endian, int *optimized, int *unavailable) const
+{
+  LONGEST total_bits_to_skip = bits_to_skip;
+  size_t read_bit_limit = location_bit_limit;
+  int reg = dwarf_reg_to_regnum_or_error (m_arch, m_regnum);
+  ULONGEST reg_bits = HOST_CHAR_BIT * register_size (m_arch, reg);
+  gdb::byte_vector temp_buf;
+
+  if (big_endian)
+    {
+      if (!read_bit_limit || reg_bits <= read_bit_limit)
+	read_bit_limit = bit_size;
+
+      total_bits_to_skip += reg_bits - (m_offset * HOST_CHAR_BIT
+					+ m_bit_suboffset + read_bit_limit);
+    }
+  else
+    total_bits_to_skip += m_offset * HOST_CHAR_BIT + m_bit_suboffset;
+
+  LONGEST this_size = bits_to_bytes (total_bits_to_skip, bit_size);
+  temp_buf.resize (this_size);
+
+  if (frame == NULL)
+    internal_error (__FILE__, __LINE__, _("invalid frame information"));
+
+  /* Can only read from a register on byte granularity so an
+     additional buffer is required.  */
+  read_from_register (frame, reg, total_bits_to_skip / HOST_CHAR_BIT,
+		      temp_buf, optimized, unavailable);
+
+  /* Only copy data if valid.  */
+  if (!*optimized && !*unavailable)
+    copy_bitwise (buf, buf_bit_offset, temp_buf.data (),
+		  total_bits_to_skip % HOST_CHAR_BIT, bit_size, big_endian);
+}
+
 /* Implicit location description entry.  Describes a location
    description not found on the target but instead saved in a
    gdb-allocated buffer.  */
@@ -539,6 +688,10 @@ class dwarf_implicit : public dwarf_location
     m_byte_order = byte_order;
   }
 
+  void read (struct frame_info *frame, gdb_byte *buf, int buf_bit_offset,
+	     size_t bit_size, LONGEST bits_to_skip, size_t location_bit_limit,
+	     bool big_endian, int *optimized, int *unavailable) const override;
+
 private:
   /* Implicit location contents as a stream of bytes in target byte-order.  */
   gdb::unique_xmalloc_ptr<gdb_byte> m_contents;
@@ -550,6 +703,45 @@ class dwarf_implicit : public dwarf_location
   enum bfd_endian m_byte_order;
 };
 
+void
+dwarf_implicit::read (struct frame_info *frame, gdb_byte *buf,
+		      int buf_bit_offset, size_t bit_size,
+		      LONGEST bits_to_skip, size_t location_bit_limit,
+		      bool big_endian, int *optimized, int *unavailable) const
+{
+  ULONGEST implicit_bit_size = HOST_CHAR_BIT * m_size;
+  LONGEST total_bits_to_skip = bits_to_skip;
+  size_t read_bit_limit = location_bit_limit;
+
+  *optimized = 0;
+  *unavailable = 0;
+
+  /* Cut off at the end of the implicit value.  */
+  if (m_byte_order == BFD_ENDIAN_BIG)
+    {
+      if (!read_bit_limit || read_bit_limit > implicit_bit_size)
+	read_bit_limit = bit_size;
+
+      total_bits_to_skip
+	+= implicit_bit_size - (m_offset * HOST_CHAR_BIT
+			       + m_bit_suboffset + read_bit_limit);
+    }
+  else
+    total_bits_to_skip += m_offset * HOST_CHAR_BIT + m_bit_suboffset;
+
+  if (total_bits_to_skip >= implicit_bit_size)
+    {
+      (*unavailable) = 1;
+      return;
+    }
+
+  if (bit_size > implicit_bit_size - total_bits_to_skip)
+    bit_size = implicit_bit_size - total_bits_to_skip;
+
+  copy_bitwise (buf, buf_bit_offset, m_contents.get (),
+		total_bits_to_skip, bit_size, big_endian);
+}
+
 /* Implicit pointer location description entry.  */
 
 class dwarf_implicit_pointer : public dwarf_location
@@ -565,6 +757,10 @@ class dwarf_implicit_pointer : public dwarf_location
       m_addr_size (addr_size), m_die_offset (die_offset)
   {}
 
+  void read (struct frame_info *frame, gdb_byte *buf, int buf_bit_offset,
+	     size_t bit_size, LONGEST bits_to_skip, size_t location_bit_limit,
+	     bool big_endian, int *optimized, int *unavailable) const override;
+
 private:
   /* Per object file data of the implicit pointer.  */
   dwarf2_per_objfile *m_per_objfile;
@@ -579,6 +775,44 @@ class dwarf_implicit_pointer : public dwarf_location
   sect_offset m_die_offset;
 };
 
+void
+dwarf_implicit_pointer::read (struct frame_info *frame, gdb_byte *buf,
+			      int buf_bit_offset, size_t bit_size,
+                              LONGEST bits_to_skip, size_t location_bit_limit,
+			      bool big_endian, int *optimized,
+			      int *unavailable) const
+{
+  struct frame_info *actual_frame = frame;
+  LONGEST total_bits_to_skip = bits_to_skip + m_bit_suboffset;
+
+  if (actual_frame == nullptr)
+    actual_frame = get_selected_frame (_("No frame selected."));
+
+  struct type *type
+    = address_type (get_frame_arch (actual_frame), m_addr_size);
+
+  struct value *value
+    = indirect_synthetic_pointer (m_die_offset, m_offset, m_per_cu,
+				  m_per_objfile, actual_frame, type);
+
+  gdb_byte *value_contents
+    = value_contents_raw (value) + total_bits_to_skip / HOST_CHAR_BIT;
+
+  if (total_bits_to_skip % HOST_CHAR_BIT == 0
+      && bit_size % HOST_CHAR_BIT == 0
+      && buf_bit_offset % HOST_CHAR_BIT == 0)
+    {
+      memcpy (buf + buf_bit_offset / HOST_CHAR_BIT,
+	      value_contents, bit_size / HOST_CHAR_BIT);
+    }
+  else
+    {
+      copy_bitwise (buf, buf_bit_offset, value_contents,
+		    total_bits_to_skip % HOST_CHAR_BIT,
+		    bit_size, big_endian);
+    }
+}
+
 /* Composite location description entry.  */
 
 class dwarf_composite : public dwarf_location
@@ -595,6 +829,10 @@ class dwarf_composite : public dwarf_location
     m_pieces.emplace_back (location, bit_size);
   }
 
+  void read (struct frame_info *frame, gdb_byte *buf, int buf_bit_offset,
+	     size_t bit_size, LONGEST bits_to_skip, size_t location_bit_limit,
+	     bool big_endian, int *optimized, int *unavailable) const override;
+
 private:
   /* Composite piece that contains a piece location
      description and it's size.  */
@@ -617,6 +855,50 @@ class dwarf_composite : public dwarf_location
   std::vector<struct piece> m_pieces;
 };
 
+void
+dwarf_composite::read (struct frame_info *frame, gdb_byte *buf,
+		       int buf_bit_offset, size_t bit_size,
+		       LONGEST bits_to_skip, size_t location_bit_limit,
+		       bool big_endian, int *optimized, int *unavailable) const
+{
+  unsigned int pieces_num = m_pieces.size ();
+  LONGEST total_bits_to_skip = bits_to_skip;
+  unsigned int i;
+
+  total_bits_to_skip += m_offset * HOST_CHAR_BIT + m_bit_suboffset;
+
+  /* Skip pieces covered by the read offset.  */
+  for (i = 0; i < pieces_num; i++)
+    {
+      LONGEST piece_bit_size = m_pieces[i].m_size;
+
+      if (total_bits_to_skip < piece_bit_size)
+        break;
+
+      total_bits_to_skip -= piece_bit_size;
+    }
+
+  for (; i < pieces_num; i++)
+    {
+      LONGEST piece_bit_size = m_pieces[i].m_size;
+      LONGEST actual_bit_size = piece_bit_size;
+
+      if (actual_bit_size > bit_size)
+        actual_bit_size = bit_size;
+
+      m_pieces[i].m_location->read (frame, buf, buf_bit_offset,
+				    actual_bit_size, total_bits_to_skip,
+				    piece_bit_size, big_endian,
+				    optimized, unavailable);
+
+      if (bit_size == actual_bit_size || *optimized || *unavailable)
+	break;
+
+      buf_bit_offset += actual_bit_size;
+      bit_size -= actual_bit_size;
+    }
+}
+
 struct piece_closure
 {
   /* Reference count.  */
@@ -1198,28 +1480,7 @@ sect_variable_value (sect_offset sect_off,
 struct type *
 dwarf_expr_context::address_type () const
 {
-  struct dwarf_gdbarch_types *types
-    = (struct dwarf_gdbarch_types *) gdbarch_data (this->gdbarch,
-						   dwarf_arch_cookie);
-  int ndx;
-
-  if (this->addr_size == 2)
-    ndx = 0;
-  else if (this->addr_size == 4)
-    ndx = 1;
-  else if (this->addr_size == 8)
-    ndx = 2;
-  else
-    error (_("Unsupported address size in DWARF expressions: %d bits"),
-	   8 * this->addr_size);
-
-  if (types->dw_types[ndx] == NULL)
-    types->dw_types[ndx]
-      = arch_integer_type (this->gdbarch,
-			   8 * this->addr_size,
-			   0, "<signed DWARF address type>");
-
-  return types->dw_types[ndx];
+  return ::address_type (this->gdbarch, this->addr_size);
 }
 
 /* Create a new context for the expression evaluator.  */
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 24/43] Add write method to location description classes
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (22 preceding siblings ...)
  2021-03-01 14:46 ` [PATCH 23/43] Add read method to location description classes Zoran Zaric
@ 2021-03-01 14:46 ` Zoran Zaric
  2021-03-01 14:46 ` [PATCH 25/43] Add deref " Zoran Zaric
                   ` (18 subsequent siblings)
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:46 UTC (permalink / raw)
  To: gdb-patches

After adding the interface for reading from the location, it also
makes sense to add the interface for writing.

To be clear, DWARF standard doesn't have a concept of writting to a
location, but because of the way how callback functions are used to
interact with the opeque implementation of the computed struct value
objects, the choice was to either use the existing DWARF entry classes
or to invent another way of representing the complexity behind those
computed objects.

Adding a write method seems to be a simpler option of the two.

gdb/ChangeLog:

        * dwarf2/expr.c (dwarf_location::write): New method.
        (dwarf_undefined::write): New method.
        (dwarf_memory::write): New method.
        (dwarf_register::write): New method.
        (dwarf_implicit::write): New method.
        (dwarf_implicit_pointer::write): New method.
        (dwarf_composite::write): New method.
---
 gdb/dwarf2/expr.c | 211 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 211 insertions(+)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 35b2efa6cb5..ef7adb1f959 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -421,6 +421,28 @@ class dwarf_location : public dwarf_entry
 		     bool big_endian, int *optimized,
 		     int *unavailable) const = 0;
 
+  /* Write contents to a described location.
+
+     The write operation is performed in the context of a FRAME.
+     BIT_SIZE is the number of bits written.  The data written is
+     copied from the caller-managed BUF buffer.  BIG_ENDIAN defines an
+     endianness of the target.  BITS_TO_SKIP is a bit offset into the
+     location and BUF_BIT_OFFSET is buffer BUF's bit offset.
+     LOCATION_BIT_LIMIT is a maximum number of bits that location can
+     hold, where value zero signifies that there is no such
+     restriction.
+
+     Note that some location types can be written without a FRAME
+     context.
+
+     If the location is optimized out or unavailable, the OPTIMIZED and
+     UNAVAILABLE outputs are set.  */
+  virtual void write (struct frame_info *frame, const gdb_byte *buf,
+		      int buf_bit_offset, size_t bit_size,
+		      LONGEST bits_to_skip, size_t location_bit_limit,
+		      bool big_endian, int *optimized,
+		      int *unavailable) const = 0;
+
 protected:
   /* Architecture of the location.  */
   struct gdbarch *m_arch;
@@ -535,6 +557,15 @@ class dwarf_undefined : public dwarf_location
     *unavailable = 0;
     *optimized = 1;
   }
+
+  void write (struct frame_info *frame, const gdb_byte *buf,
+	      int buf_bit_offset, size_t bit_size, LONGEST bits_to_skip,
+	      size_t location_bit_limit, bool big_endian,
+	      int *optimized, int *unavailable) const override
+  {
+    *unavailable = 0;
+    *optimized = 1;
+  }
 };
 
 class dwarf_memory : public dwarf_location
@@ -558,6 +589,11 @@ class dwarf_memory : public dwarf_location
 	     size_t location_bit_limit, bool big_endian,
 	     int *optimized, int *unavailable) const override;
 
+  void write (struct frame_info *frame, const gdb_byte *buf,
+	      int buf_bit_offset, size_t bit_size, LONGEST bits_to_skip,
+	      size_t location_bit_limit, bool big_endian,
+	      int *optimized, int *unavailable) const override;
+
 private:
   /* True if the location belongs to a stack memory region.  */
   bool m_stack;
@@ -609,6 +645,62 @@ dwarf_memory::read (struct frame_info *frame, gdb_byte *buf,
     }
 }
 
+void
+dwarf_memory::write (struct frame_info *frame, const gdb_byte *buf,
+		     int buf_bit_offset, size_t bit_size,
+		     LONGEST bits_to_skip, size_t location_bit_limit,
+		     bool big_endian, int *optimized, int *unavailable) const
+{
+  LONGEST total_bits_to_skip = bits_to_skip;
+  CORE_ADDR start_address
+    = m_offset + (m_bit_suboffset + total_bits_to_skip) / HOST_CHAR_BIT;
+  gdb::byte_vector temp_buf;
+
+  total_bits_to_skip += m_bit_suboffset;
+  *optimized = 0;
+
+  if (total_bits_to_skip % HOST_CHAR_BIT == 0
+      && bit_size % HOST_CHAR_BIT == 0
+      && buf_bit_offset % HOST_CHAR_BIT == 0)
+    {
+      /* Everything is byte-aligned; no buffer needed.  */
+      write_to_memory (start_address, buf + buf_bit_offset / HOST_CHAR_BIT,
+		       bit_size / HOST_CHAR_BIT, m_stack, unavailable);
+    }
+  else
+    {
+      LONGEST this_size = bits_to_bytes (total_bits_to_skip, bit_size);
+      temp_buf.resize (this_size);
+
+      if (total_bits_to_skip % HOST_CHAR_BIT != 0
+	  || bit_size % HOST_CHAR_BIT != 0)
+	{
+	  if (this_size <= HOST_CHAR_BIT)
+	    /* Perform a single read for small sizes.  */
+	    read_from_memory (start_address, temp_buf.data (),
+			      this_size, m_stack, unavailable);
+	  else
+	    {
+	      /* Only the first and last bytes can possibly have
+		 any bits reused.  */
+	      read_from_memory (start_address, temp_buf.data (),
+				1, m_stack, unavailable);
+
+	      if (!*unavailable)
+		read_from_memory (start_address + this_size - 1,
+				  &temp_buf[this_size - 1], 1,
+				  m_stack, unavailable);
+	    }
+	}
+
+      copy_bitwise (temp_buf.data (), total_bits_to_skip % HOST_CHAR_BIT,
+		    buf, buf_bit_offset, bit_size, big_endian);
+
+      write_to_memory (start_address, temp_buf.data (), this_size,
+		       m_stack, unavailable);
+    }
+}
+
 /* Register location description entry.  */
 
 class dwarf_register : public dwarf_location
@@ -624,6 +716,11 @@ class dwarf_register : public dwarf_location
 	     size_t bit_size, LONGEST bits_to_skip, size_t location_bit_limit,
 	     bool big_endian, int *optimized, int *unavailable) const override;
 
+  void write (struct frame_info *frame, const gdb_byte *buf,
+	      int buf_bit_offset, size_t bit_size, LONGEST bits_to_skip,
+	      size_t location_bit_limit, bool big_endian,
+	      int *optimized, int *unavailable) const override;
+
 private:
   /* DWARF register number.  */
   unsigned int m_regnum;
@@ -669,6 +766,52 @@ dwarf_register::read (struct frame_info *frame, gdb_byte *buf,
 		  total_bits_to_skip % HOST_CHAR_BIT, bit_size, big_endian);
 }
 
+void
+dwarf_register::write (struct frame_info *frame, const gdb_byte *buf,
+		       int buf_bit_offset, size_t bit_size,
+		       LONGEST bits_to_skip, size_t location_bit_limit,
+		       bool big_endian, int *optimized, int *unavailable) const
+{
+  LONGEST total_bits_to_skip = bits_to_skip;
+  size_t write_bit_limit = location_bit_limit;
+  int gdb_regnum = dwarf_reg_to_regnum_or_error (m_arch, m_regnum);
+  ULONGEST reg_bits = HOST_CHAR_BIT * register_size (m_arch, gdb_regnum);
+  gdb::byte_vector temp_buf;
+
+  if (frame == NULL)
+    internal_error (__FILE__, __LINE__, _("invalid frame information"));
+
+  if (big_endian)
+    {
+      if (!write_bit_limit || reg_bits <= write_bit_limit)
+	write_bit_limit = bit_size;
+
+      total_bits_to_skip += reg_bits - (m_offset * HOST_CHAR_BIT
+					+ m_bit_suboffset + write_bit_limit);
+    }
+  else
+    total_bits_to_skip += m_offset * HOST_CHAR_BIT + m_bit_suboffset;
+
+  LONGEST this_size = bits_to_bytes (total_bits_to_skip, bit_size);
+  temp_buf.resize (this_size);
+
+  if (total_bits_to_skip % HOST_CHAR_BIT != 0
+      || bit_size % HOST_CHAR_BIT != 0)
+    {
+      /* Contents is copied non-byte-aligned into the register.
+         Need some bits from original register value.  */
+      read_from_register (frame, gdb_regnum,
+			  total_bits_to_skip / HOST_CHAR_BIT,
+			  temp_buf, optimized, unavailable);
+    }
+
+  copy_bitwise (temp_buf.data (), total_bits_to_skip % HOST_CHAR_BIT, buf,
+		buf_bit_offset, bit_size, big_endian);
+
+  write_to_register (frame, gdb_regnum, total_bits_to_skip / HOST_CHAR_BIT,
+		     temp_buf, optimized, unavailable);
+}
+
 /* Implicit location description entry.  Describes a location
    description not found on the target but instead saved in a
    gdb-allocated buffer.  */
@@ -692,6 +835,15 @@ class dwarf_implicit : public dwarf_location
 	     size_t bit_size, LONGEST bits_to_skip, size_t location_bit_limit,
 	     bool big_endian, int *optimized, int *unavailable) const override;
 
+  void write (struct frame_info *frame, const gdb_byte *buf,
+	      int buf_bit_offset, size_t bit_size,
+	      LONGEST bits_to_skip, size_t location_bit_limit,
+	      bool big_endian, int* optimized, int* unavailable) const override
+  {
+    *optimized = 1;
+    *unavailable = 0;
+  }
+
 private:
   /* Implicit location contents as a stream of bytes in target byte-order.  */
   gdb::unique_xmalloc_ptr<gdb_byte> m_contents;
@@ -761,6 +913,15 @@ class dwarf_implicit_pointer : public dwarf_location
 	     size_t bit_size, LONGEST bits_to_skip, size_t location_bit_limit,
 	     bool big_endian, int *optimized, int *unavailable) const override;
 
+  void write (struct frame_info *frame, const gdb_byte *buf,
+	      int buf_bit_offset, size_t bit_size, LONGEST bits_to_skip,
+	      size_t location_bit_limit, bool big_endian,
+	      int* optimized, int* unavailable) const override
+  {
+    *optimized = 1;
+    *unavailable = 0;
+  }
+
 private:
   /* Per object file data of the implicit pointer.  */
   dwarf2_per_objfile *m_per_objfile;
@@ -833,6 +994,11 @@ class dwarf_composite : public dwarf_location
 	     size_t bit_size, LONGEST bits_to_skip, size_t location_bit_limit,
 	     bool big_endian, int *optimized, int *unavailable) const override;
 
+  void write (struct frame_info *frame, const gdb_byte *buf,
+	      int buf_bit_offset, size_t bit_size, LONGEST bits_to_skip,
+	      size_t location_bit_limit, bool big_endian,
+	      int *optimized, int *unavailable) const override;
+
 private:
   /* Composite piece that contains a piece location
      description and it's size.  */
@@ -899,6 +1065,51 @@ dwarf_composite::read (struct frame_info *frame, gdb_byte *buf,
     }
 }
 
+void
+dwarf_composite::write (struct frame_info *frame, const gdb_byte *buf,
+			int buf_bit_offset, size_t bit_size,
+			LONGEST bits_to_skip, size_t location_bit_limit,
+			bool big_endian, int *optimized,
+			int *unavailable) const
+{
+  LONGEST total_bits_to_skip = bits_to_skip;
+  unsigned int pieces_num = m_pieces.size ();
+  unsigned int i;
+
+  total_bits_to_skip += m_offset * HOST_CHAR_BIT + m_bit_suboffset;
+
+  /* Skip pieces covered by the write offset.  */
+  for (i = 0; i < pieces_num; i++)
+    {
+      LONGEST piece_bit_size = m_pieces[i].m_size;
+
+      if (total_bits_to_skip < piece_bit_size)
+	break;
+
+      total_bits_to_skip -= piece_bit_size;
+    }
+
+  for (; i < pieces_num; i++)
+    {
+      LONGEST piece_bit_size = m_pieces[i].m_size;
+      LONGEST actual_bit_size = piece_bit_size;
+
+      if (actual_bit_size > bit_size)
+        actual_bit_size = bit_size;
+
+      m_pieces[i].m_location->write (frame, buf, buf_bit_offset,
+				     actual_bit_size, total_bits_to_skip,
+				     piece_bit_size, big_endian,
+				     optimized, unavailable);
+
+      if (bit_size == actual_bit_size || *optimized || *unavailable)
+	break;
+
+      buf_bit_offset += actual_bit_size;
+      bit_size -= actual_bit_size;
+    }
+}
+
 struct piece_closure
 {
   /* Reference count.  */
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 25/43] Add deref method to location description classes
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (23 preceding siblings ...)
  2021-03-01 14:46 ` [PATCH 24/43] Add write " Zoran Zaric
@ 2021-03-01 14:46 ` Zoran Zaric
  2021-03-01 14:46 ` [PATCH 26/43] Add read_from_gdb_value method to dwarf_location Zoran Zaric
                   ` (17 subsequent siblings)
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:46 UTC (permalink / raw)
  To: gdb-patches

Looking at the DWARF standard, the concept of reading from a location
seems to be too low level. What the standard actually describes is a
concept of dereferencing where the type of the operation result can be
specified in advance.

This can be seen in the definition of the DW_OP_derefX family of
expression operations, but it is also happening implicitly in the case
of DW_OP_fbreg, DW_OP_regval_type and DW_OP_bregX family of operations.

Currently, the DW_OP_derefX operations will take the value from the
DWARF expression stack and implicitly convert it to a memory location
description (in reality treat it as a memory address for a given
target) and apply the dereference operation to it. When we allow any
location description on a DWARF expression stack, these operations need
to work in the same way.

The conclusion here is that we need an universal method that model the
dereference operation for any class derived from a location description
class.

It is worth mentioning that because of how the passed in buffers are
currently being implemented, we needed a specialisation for the deref
method of the dwarf_memory class to support them.

gdb/ChangeLog:

        * dwarf2/expr.c (dwarf_location::deref): New method.
        (dwarf_memory::deref): New method.
---
 gdb/dwarf2/expr.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 119 insertions(+)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index ef7adb1f959..ff9c88087d8 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -443,6 +443,18 @@ class dwarf_location : public dwarf_entry
 		      bool big_endian, int *optimized,
 		      int *unavailable) const = 0;
 
+  /* Apply dereference operation on the DWARF location description.
+     Operation returns a DWARF value of a given TYPE type while FRAME
+     contains a frame context information of the location.  ADDR_INFO
+     (if present) describes a passed in memory buffer if a regular
+     memory read is not desired for certain address range.  If the SIZE
+     is specified, it must be equal or smaller then the TYPE type size.
+     If SIZE is smaller then the type size, the value will be zero
+     extended to the difference.  */
+  virtual std::shared_ptr<dwarf_value> deref
+    (struct frame_info *frame, const struct property_addr_info *addr_info,
+     struct type *type, size_t size = 0) const;
+
 protected:
   /* Architecture of the location.  */
   struct gdbarch *m_arch;
@@ -458,6 +470,43 @@ class dwarf_location : public dwarf_entry
   bool m_initialised;
 };
 
+std::shared_ptr<dwarf_value>
+dwarf_location::deref (struct frame_info *frame,
+		       const struct property_addr_info *addr_info,
+		       struct type *type, size_t size) const
+{
+  bool big_endian = type_byte_order (type) == BFD_ENDIAN_BIG;
+  size_t actual_size = size != 0 ? size : TYPE_LENGTH (type);
+
+  if (actual_size > TYPE_LENGTH (type))
+    ill_formed_expression ();
+
+    /* If the size of the object read from memory is different
+     from the type length, we need to zero-extend it.  */
+  gdb::byte_vector read_buf (TYPE_LENGTH (type), 0);
+  gdb_byte *buf_ptr = read_buf.data ();
+  int optimized, unavailable;
+
+  if (big_endian)
+    buf_ptr += TYPE_LENGTH (type) - actual_size;
+
+  this->read (frame, buf_ptr, 0, actual_size * HOST_CHAR_BIT,
+	      0, 0, big_endian, &optimized, &unavailable);
+
+  if (optimized)
+    throw_error (OPTIMIZED_OUT_ERROR,
+		 _("Can't do read-modify-write to "
+		   "update bitfield; containing word "
+		   "has been optimized out"));
+  if (unavailable)
+    throw_error (NOT_AVAILABLE_ERROR,
+		 _("Can't dereference "
+		   "update bitfield; containing word "
+		   "is unavailable"));
+
+  return std::make_shared<dwarf_value> (read_buf.data (), type);
+}
+
 /* Value entry found on a DWARF expression evaluation stack.  */
 
 class dwarf_value : public dwarf_entry
@@ -594,6 +643,10 @@ class dwarf_memory : public dwarf_location
 	      size_t location_bit_limit, bool big_endian,
 	      int *optimized, int *unavailable) const override;
 
+  std::shared_ptr<dwarf_value> deref
+    (struct frame_info *frame, const struct property_addr_info *addr_info,
+     struct type *type, size_t size = 0) const override;
+
 private:
   /* True if the location belongs to a stack memory region.  */
   bool m_stack;
@@ -701,6 +754,72 @@ dwarf_memory::write (struct frame_info *frame, const gdb_byte *buf,
     }
 }
 
+std::shared_ptr<dwarf_value>
+dwarf_memory::deref (struct frame_info *frame,
+		     const struct property_addr_info *addr_info,
+		     struct type *type, size_t size) const
+{
+  bool big_endian = type_byte_order (type) == BFD_ENDIAN_BIG;
+  size_t actual_size = size != 0 ? size : TYPE_LENGTH (type);
+
+  if (actual_size > TYPE_LENGTH (type))
+    ill_formed_expression ();
+
+  gdb::byte_vector read_buf (TYPE_LENGTH (type), 0);
+  size_t size_in_bits = actual_size * HOST_CHAR_BIT;
+  gdb_byte *buf_ptr = read_buf.data ();
+  bool passed_in_buf = false;
+
+  if (big_endian)
+    buf_ptr += TYPE_LENGTH (type) - actual_size;
+
+  /* Covers the case where we have a passed in memory that is not
+     part of the target and requires for the location description
+     to address it instead of addressing the actual target
+     memory.  */
+  LONGEST this_size = bits_to_bytes (m_bit_suboffset, size_in_bits);
+
+  /* We shouldn't have a case where we read from a passed in
+     memory and the same memory being marked as stack. */
+  if (!m_stack && this_size && addr_info != nullptr)
+    {
+      CORE_ADDR offset = (CORE_ADDR) m_offset - addr_info->addr;
+      /* Using second buffer here because the copy_bitwise
+	 doesn't support in place copy.  */
+      gdb::byte_vector temp_buf (this_size);
+
+      if (offset < addr_info->valaddr.size ()
+	  && offset + this_size <= addr_info->valaddr.size ())
+	{
+	  memcpy (temp_buf.data (), addr_info->valaddr.data (), this_size);
+	  copy_bitwise (buf_ptr, 0, temp_buf.data (),
+			m_bit_suboffset, size_in_bits, big_endian);
+	  passed_in_buf = true;
+	}
+    }
+
+  if (!passed_in_buf)
+    {
+      int optimized, unavailable;
+
+      this->read (frame, buf_ptr, 0, size_in_bits, 0, 0,
+		  big_endian, &optimized, &unavailable);
+
+      if (optimized)
+	throw_error (OPTIMIZED_OUT_ERROR,
+		     _("Can't do read-modify-write to "
+		     "update bitfield; containing word "
+		     "has been optimized out"));
+      if (unavailable)
+	throw_error (NOT_AVAILABLE_ERROR,
+		     _("Can't dereference "
+		     "update bitfield; containing word "
+		     "is unavailable"));
+    }
+
+  return std::make_shared<dwarf_value> (read_buf.data (), type);
+}
+
 /* Register location description entry.  */
 
 class dwarf_register : public dwarf_location
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 26/43] Add read_from_gdb_value method to dwarf_location
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (24 preceding siblings ...)
  2021-03-01 14:46 ` [PATCH 25/43] Add deref " Zoran Zaric
@ 2021-03-01 14:46 ` Zoran Zaric
  2021-03-01 14:46 ` [PATCH 27/43] Add write_to_gdb_value " Zoran Zaric
                   ` (16 subsequent siblings)
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:46 UTC (permalink / raw)
  To: gdb-patches

The next 4 patches are addressing the expectations of the existing
function calback interface of the computed struct value objects.

As mentioned in the previous patches the location description and the
interaction with that location are opaque to the struct value object,
but currently that interaction is influenced by the data contained
inside of that object and outside of the location description class.

Also, the struct value evaluation involves more then just writing or
reading the object contents buffer, in certain cases it is also
expected to throw an exception or mark different parts of the object
with additional information (optimized out bitmap for example).

As a result, reading the data from a struct value object and writing
that data into the location described, can be different then just
generic writing the data from a buffer (dwarf_location write method).

To make this distinction clear a new read_from_gdb_value method is
added to classes that derive from location description class.

gdb/ChangeLog:

        * dwarf2/expr.c (dwarf_location::read_from_gdb_value):
        New method.
        (dwarf_composite::read_from_gdb_value): New method.
---
 gdb/dwarf2/expr.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 102 insertions(+)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index ff9c88087d8..10386cca858 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -455,6 +455,23 @@ class dwarf_location : public dwarf_entry
     (struct frame_info *frame, const struct property_addr_info *addr_info,
      struct type *type, size_t size = 0) const;
 
+/* Read data from the VALUE contents to the location specified by the
+   location description.
+
+   The read operation is performed in the context of a FRAME.  BIT_SIZE
+   is the number of bits to read.  VALUE_BIT_OFFSET is a bit offset
+   into a VALUE content and BITS_TO_SKIP is a bit offset into the
+   location.  LOCATION_BIT_LIMIT is a maximum number of bits that
+   location can hold, where value zero signifies that there is no such
+   restriction.
+
+   Note that some location types can be read without a FRAME context.  */
+  virtual void read_from_gdb_value (struct frame_info *frame,
+				    struct value *value,
+				    int value_bit_offset,
+				    LONGEST bits_to_skip, size_t bit_size,
+				    size_t location_bit_limit);
+
 protected:
   /* Architecture of the location.  */
   struct gdbarch *m_arch;
@@ -507,6 +524,32 @@ dwarf_location::deref (struct frame_info *frame,
   return std::make_shared<dwarf_value> (read_buf.data (), type);
 }
 
+void
+dwarf_location::read_from_gdb_value (struct frame_info *frame,
+				     struct value *value,
+				     int value_bit_offset,
+				     LONGEST bits_to_skip, size_t bit_size,
+				     size_t location_bit_limit)
+{
+  int optimized, unavailable;
+  bool big_endian = type_byte_order (value_type (value)) == BFD_ENDIAN_BIG;
+
+  this->write (frame, value_contents (value), value_bit_offset,
+	       bit_size, bits_to_skip, location_bit_limit,
+	       big_endian, &optimized, &unavailable);
+
+  if (optimized)
+    throw_error (OPTIMIZED_OUT_ERROR,
+		 _("Can't do read-modify-write to "
+		   "update bitfield; containing word "
+		   "has been optimized out"));
+  if (unavailable)
+    throw_error (NOT_AVAILABLE_ERROR,
+		 _("Can't do read-modify-write to "
+		   "update bitfield; containing word "
+		   "is unavailable"));
+}
+
 /* Value entry found on a DWARF expression evaluation stack.  */
 
 class dwarf_value : public dwarf_entry
@@ -1041,6 +1084,16 @@ class dwarf_implicit_pointer : public dwarf_location
     *unavailable = 0;
   }
 
+  /* Reading from and writing to an implicit pointer is not meaningful,
+     so we just skip them here.  */
+  void read_from_gdb_value (struct frame_info *frame,
+			    struct value *value, int value_bit_offset,
+			    LONGEST bits_to_skip, size_t bit_size,
+			    size_t location_bit_limit) override
+  {
+    mark_value_bits_optimized_out (value, bits_to_skip, bit_size);
+  }
+
 private:
   /* Per object file data of the implicit pointer.  */
   dwarf2_per_objfile *m_per_objfile;
@@ -1118,6 +1171,11 @@ class dwarf_composite : public dwarf_location
 	      size_t location_bit_limit, bool big_endian,
 	      int *optimized, int *unavailable) const override;
 
+  void read_from_gdb_value (struct frame_info *frame,
+			    struct value *value, int value_bit_offset,
+			    LONGEST bits_to_skip, size_t bit_size,
+			    size_t location_bit_limit) override;
+
 private:
   /* Composite piece that contains a piece location
      description and it's size.  */
@@ -1229,6 +1287,50 @@ dwarf_composite::write (struct frame_info *frame, const gdb_byte *buf,
     }
 }
 
+void
+dwarf_composite::read_from_gdb_value (struct frame_info *frame,
+				      struct value *value,
+				      int value_bit_offset,
+				      LONGEST bits_to_skip, size_t bit_size,
+				      size_t location_bit_limit)
+{
+  ULONGEST total_bits_to_skip
+    = bits_to_skip + HOST_CHAR_BIT * m_offset + m_bit_suboffset;
+  ULONGEST remaining_bit_size = bit_size;
+  ULONGEST bit_offset = value_bit_offset;
+  unsigned int pieces_num = m_pieces.size ();
+  unsigned int i;
+
+  /* Advance to the first non-skipped piece.  */
+  for (i = 0; i < pieces_num; i++)
+    {
+      ULONGEST piece_bit_size = m_pieces[i].m_size;
+
+      if (total_bits_to_skip < piece_bit_size)
+	break;
+
+      total_bits_to_skip -= piece_bit_size;
+    }
+
+  for (; i < pieces_num; i++)
+    {
+      auto location = m_pieces[i].m_location;
+      ULONGEST piece_bit_size = m_pieces[i].m_size;
+      size_t this_bit_size = piece_bit_size - total_bits_to_skip;
+
+      if (this_bit_size > remaining_bit_size)
+	this_bit_size = remaining_bit_size;
+
+      location->read_from_gdb_value (frame, value, bit_offset,
+				     total_bits_to_skip, this_bit_size,
+				     piece_bit_size);
+
+      bit_offset += this_bit_size;
+      remaining_bit_size -= this_bit_size;
+      total_bits_to_skip = 0;
+    }
+}
+
 struct piece_closure
 {
   /* Reference count.  */
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 27/43] Add write_to_gdb_value method to dwarf_location
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (25 preceding siblings ...)
  2021-03-01 14:46 ` [PATCH 26/43] Add read_from_gdb_value method to dwarf_location Zoran Zaric
@ 2021-03-01 14:46 ` Zoran Zaric
  2021-03-01 14:46 ` [PATCH 28/43] Add is_implicit_ptr_at " Zoran Zaric
                   ` (15 subsequent siblings)
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:46 UTC (permalink / raw)
  To: gdb-patches

Similar story behind the previous read_from_gdb_value method applies
to the new write_to_gdb_value.

In the same way, reading the data from a location described and writing
that data to a struct value object, can be different from just generic
read the data from a buffer (location description read method).

To make this distinction clear, a new write_to_gdb_value method is
added to classes that derive from location description class.

gdb/ChangeLog:

        * dwarf2/expr.c (dwarf_location::write_to_gdb_value):
        New method.
        (dwarf_composite::write_to_gdb_value): New method.
---
 gdb/dwarf2/expr.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 91 insertions(+)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 10386cca858..7e5ba5d636b 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -472,6 +472,23 @@ class dwarf_location : public dwarf_entry
 				    LONGEST bits_to_skip, size_t bit_size,
 				    size_t location_bit_limit);
 
+/* Write data to the VALUE contents from the location specified by the
+   location description.
+
+   The write operation is performed in the context of a FRAME.
+   BIT_SIZE is the number of bits to read.  VALUE_BIT_OFFSET is a bit
+   offset into a VALUE content and BITS_TO_SKIP is a bit offset into
+   the location.  LOCATION_BIT_LIMIT is a maximum number of bits that
+   location can hold, where value zero signifies that there is no such
+   restriction.
+
+   Note that some location types can be read without a FRAME context.  */
+  virtual void write_to_gdb_value (struct frame_info *frame,
+				   struct value *value,
+				   int value_bit_offset,
+				   LONGEST bits_to_skip, size_t bit_size,
+				   size_t location_bit_limit);
+
 protected:
   /* Architecture of the location.  */
   struct gdbarch *m_arch;
@@ -550,6 +567,26 @@ dwarf_location::read_from_gdb_value (struct frame_info *frame,
 		   "is unavailable"));
 }
 
+void
+dwarf_location::write_to_gdb_value (struct frame_info *frame,
+				    struct value *value,
+				    int value_bit_offset,
+				    LONGEST bits_to_skip, size_t bit_size,
+				    size_t location_bit_limit)
+{
+  int optimized, unavailable;
+  bool big_endian = type_byte_order (value_type (value)) == BFD_ENDIAN_BIG;
+
+  this->read (frame, value_contents_raw (value), value_bit_offset,
+	      bit_size, bits_to_skip, location_bit_limit,
+	      big_endian, &optimized, &unavailable);
+
+  if (optimized)
+    mark_value_bits_optimized_out (value, value_bit_offset, bit_size);
+  if (unavailable)
+    mark_value_bits_unavailable (value, value_bit_offset, bit_size);
+}
+
 /* Value entry found on a DWARF expression evaluation stack.  */
 
 class dwarf_value : public dwarf_entry
@@ -1094,6 +1131,12 @@ class dwarf_implicit_pointer : public dwarf_location
     mark_value_bits_optimized_out (value, bits_to_skip, bit_size);
   }
 
+  void write_to_gdb_value (struct frame_info *frame,
+			   struct value *value, int value_bit_offset,
+			   LONGEST bits_to_skip, size_t bit_size,
+			   size_t location_bit_limit) override
+  {}
+
 private:
   /* Per object file data of the implicit pointer.  */
   dwarf2_per_objfile *m_per_objfile;
@@ -1176,6 +1219,11 @@ class dwarf_composite : public dwarf_location
 			    LONGEST bits_to_skip, size_t bit_size,
 			    size_t location_bit_limit) override;
 
+  void write_to_gdb_value (struct frame_info *frame,
+			   struct value *value, int value_bit_offset,
+			   LONGEST bits_to_skip, size_t bit_size,
+			   size_t location_bit_limit) override;
+
 private:
   /* Composite piece that contains a piece location
      description and it's size.  */
@@ -1331,6 +1379,49 @@ dwarf_composite::read_from_gdb_value (struct frame_info *frame,
     }
 }
 
+void
+dwarf_composite::write_to_gdb_value (struct frame_info *frame,
+				     struct value *value, int value_bit_offset,
+				     LONGEST bits_to_skip, size_t bit_size,
+				     size_t location_bit_limit)
+{
+  ULONGEST total_bits_to_skip
+    = bits_to_skip + HOST_CHAR_BIT * m_offset + m_bit_suboffset;
+  ULONGEST remaining_bit_size = bit_size;
+  ULONGEST bit_offset = value_bit_offset;
+  unsigned int pieces_num = m_pieces.size ();
+  unsigned int i;
+
+  /* Advance to the first non-skipped piece.  */
+  for (i = 0; i < pieces_num; i++)
+    {
+      ULONGEST piece_bit_size = m_pieces[i].m_size;
+
+      if (total_bits_to_skip < piece_bit_size)
+	break;
+
+      total_bits_to_skip -= piece_bit_size;
+    }
+
+  for (; i < pieces_num; i++)
+    {
+      auto location = m_pieces[i].m_location;
+      ULONGEST piece_bit_size = m_pieces[i].m_size;
+      size_t this_bit_size = piece_bit_size - total_bits_to_skip;
+
+      if (this_bit_size > remaining_bit_size)
+	this_bit_size = remaining_bit_size;
+
+      location->write_to_gdb_value (frame, value, bit_offset,
+				    total_bits_to_skip, this_bit_size,
+				    piece_bit_size);
+
+      bit_offset += this_bit_size;
+      remaining_bit_size -= this_bit_size;
+      total_bits_to_skip = 0;
+    }
+}
+
 struct piece_closure
 {
   /* Reference count.  */
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 28/43] Add is_implicit_ptr_at method to dwarf_location
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (26 preceding siblings ...)
  2021-03-01 14:46 ` [PATCH 27/43] Add write_to_gdb_value " Zoran Zaric
@ 2021-03-01 14:46 ` Zoran Zaric
  2021-03-01 14:46 ` [PATCH 29/43] Add indirect_implicit_ptr to dwarf_location class Zoran Zaric
                   ` (14 subsequent siblings)
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:46 UTC (permalink / raw)
  To: gdb-patches

Another expectation of the existing function callback interface of the
computed struct value is to check if a specific part (on a given offset
of a given length) of an underlying location description is an implicit
pointer location description.

To satisfy this expectation a new is_implicit_ptr_at has been added.

gdb/ChangeLog:

        * dwarf2/expr.c (dwarf_location::is_implicit_ptr_at):
        New method.
        (dwarf_implicit_pointer::is_implicit_ptr_at): New method.
        (dwarf_composite::is_implicit_ptr_at): New method.
---
 gdb/dwarf2/expr.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 7e5ba5d636b..5de75f570aa 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -489,6 +489,14 @@ class dwarf_location : public dwarf_entry
 				   LONGEST bits_to_skip, size_t bit_size,
 				   size_t location_bit_limit);
 
+  /* Check if a given DWARF location description contains an implicit
+     pointer location description of a BIT_LENGTH size on a given
+     BIT_OFFSET offset.  */
+  virtual bool is_implicit_ptr_at (LONGEST bit_offset, int bit_length) const
+  {
+     return false;
+  }
+
 protected:
   /* Architecture of the location.  */
   struct gdbarch *m_arch;
@@ -1137,6 +1145,11 @@ class dwarf_implicit_pointer : public dwarf_location
 			   size_t location_bit_limit) override
   {}
 
+  bool is_implicit_ptr_at (LONGEST bit_offset, int bit_length) const override
+  {
+     return true;
+  }
+
 private:
   /* Per object file data of the implicit pointer.  */
   dwarf2_per_objfile *m_per_objfile;
@@ -1224,6 +1237,8 @@ class dwarf_composite : public dwarf_location
 			   LONGEST bits_to_skip, size_t bit_size,
 			   size_t location_bit_limit) override;
 
+  bool is_implicit_ptr_at (LONGEST bit_offset, int bit_length) const override;
+
 private:
   /* Composite piece that contains a piece location
      description and it's size.  */
@@ -1422,6 +1437,42 @@ dwarf_composite::write_to_gdb_value (struct frame_info *frame,
     }
 }
 
+bool
+dwarf_composite::is_implicit_ptr_at (LONGEST bit_offset, int bit_length) const
+{
+  /* Advance to the first non-skipped piece.  */
+  unsigned int pieces_num = m_pieces.size ();
+  LONGEST total_bit_offset = bit_offset;
+  LONGEST total_bit_length = bit_length;
+
+  total_bit_offset += HOST_CHAR_BIT * m_offset + m_bit_suboffset;
+
+  for (unsigned int i = 0; i < pieces_num && total_bit_length != 0; i++)
+    {
+      ULONGEST read_bit_length = m_pieces[i].m_size;
+
+      if (total_bit_offset >= read_bit_length)
+	{
+	  total_bit_offset -= read_bit_length;
+	  continue;
+	}
+
+      read_bit_length -= total_bit_offset;
+
+      if (total_bit_length < read_bit_length)
+	read_bit_length = total_bit_length;
+
+      if (!m_pieces[i].m_location->is_implicit_ptr_at (total_bit_offset,
+						       read_bit_length))
+	return false;
+
+      total_bit_offset = 0;
+      total_bit_length -= read_bit_length;
+    }
+
+    return true;
+}
+
 struct piece_closure
 {
   /* Reference count.  */
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 29/43] Add indirect_implicit_ptr to dwarf_location class
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (27 preceding siblings ...)
  2021-03-01 14:46 ` [PATCH 28/43] Add is_implicit_ptr_at " Zoran Zaric
@ 2021-03-01 14:46 ` Zoran Zaric
  2021-03-01 14:46 ` [PATCH 30/43] Add new computed struct value callback interface Zoran Zaric
                   ` (13 subsequent siblings)
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:46 UTC (permalink / raw)
  To: gdb-patches

Similarly to the is_implicit_ptr_at method, the existing function
callback interface of the computed struct value, requiers a way to
apply indirection to an implicit pointer on a given offset of a given
length of an underlying location description.

This is different then reading from a struct value object (previously
described write_to_gdb_value method) in a way that the result of this
operation is expected to be a struct value of a pointed source level
variable instead of reading the value of that variable.

In the same way this is also different operation then the deref method
because the deref returns a read value of a given type from that
location description.

gdb/ChangeLog:

        * dwarf2/expr.c (dwarf_location::indirect_implicit_ptr):
        New method.
        (dwarf_implicit_pointer::indirect_implicit_ptr): New method.
        (dwarf_composite::indirect_implicit_ptr): New method.
---
 gdb/dwarf2/expr.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 5de75f570aa..2550c59dc27 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -497,6 +497,25 @@ class dwarf_location : public dwarf_entry
      return false;
   }
 
+  /* Recursive indirecting of the implicit pointer location description
+     if that location is or encapsulates an implicit pointer.  The
+     operation is performed in a given FRAME context, using the TYPE as
+     the type of the pointer.  Where POINTER_OFFSET is an offset
+     applied to that implicit pointer location description before the
+     operation. BIT_OFFSET is a bit offset applied to the location and
+     BIT_LENGTH is a bit length of the read.
+
+     Indirecting is only performed on the implicit pointer location
+     description parts of the location.  */
+  virtual struct value *indirect_implicit_ptr (struct frame_info *frame,
+					       struct type *type,
+					       LONGEST pointer_offset = 0,
+					       LONGEST bit_offset = 0,
+					       int bit_length = 0) const
+  {
+    return nullptr;
+  }
+
 protected:
   /* Architecture of the location.  */
   struct gdbarch *m_arch;
@@ -1150,6 +1169,12 @@ class dwarf_implicit_pointer : public dwarf_location
      return true;
   }
 
+  struct value *indirect_implicit_ptr (struct frame_info *frame,
+				       struct type *type,
+				       LONGEST pointer_offset = 0,
+				       LONGEST bit_offset = 0,
+				       int bit_length = 0) const override;
+
 private:
   /* Per object file data of the implicit pointer.  */
   dwarf2_per_objfile *m_per_objfile;
@@ -1202,6 +1227,17 @@ dwarf_implicit_pointer::read (struct frame_info *frame, gdb_byte *buf,
     }
 }
 
+struct value *
+dwarf_implicit_pointer::indirect_implicit_ptr (struct frame_info *frame,
+					       struct type *type,
+					       LONGEST pointer_offset,
+					       LONGEST bit_offset,
+					       int bit_length) const
+{
+  return indirect_synthetic_pointer (m_die_offset, m_offset + pointer_offset,
+				     m_per_cu, m_per_objfile, frame, type);
+}
+
 /* Composite location description entry.  */
 
 class dwarf_composite : public dwarf_location
@@ -1239,6 +1275,12 @@ class dwarf_composite : public dwarf_location
 
   bool is_implicit_ptr_at (LONGEST bit_offset, int bit_length) const override;
 
+  struct value *indirect_implicit_ptr (struct frame_info *frame,
+				       struct type *type,
+				       LONGEST pointer_offset = 0,
+				       LONGEST bit_offset = 0,
+				       int bit_length = 0) const override;
+
 private:
   /* Composite piece that contains a piece location
      description and it's size.  */
@@ -1473,6 +1515,42 @@ dwarf_composite::is_implicit_ptr_at (LONGEST bit_offset, int bit_length) const
     return true;
 }
 
+struct value *
+dwarf_composite::indirect_implicit_ptr (struct frame_info *frame,
+					struct type *type,
+					LONGEST pointer_offset,
+					LONGEST bit_offset,
+					int bit_length) const
+{
+  /* Advance to the first non-skipped piece.  */
+  unsigned int pieces_num = m_pieces.size ();
+  LONGEST total_bit_offset = HOST_CHAR_BIT * m_offset
+			     + m_bit_suboffset + bit_offset;
+
+  for (unsigned int i = 0; i < pieces_num; i++)
+    {
+      ULONGEST read_bit_length = m_pieces[i].m_size;
+
+      if (total_bit_offset >= read_bit_length)
+	{
+	  total_bit_offset -= read_bit_length;
+	  continue;
+	}
+
+      read_bit_length -= total_bit_offset;
+
+      if (bit_length < read_bit_length)
+	read_bit_length = bit_length;
+
+      return m_pieces[i].m_location->indirect_implicit_ptr (frame, type,
+							    pointer_offset,
+							    total_bit_offset,
+							    read_bit_length);
+    }
+
+  return nullptr;
+}
+
 struct piece_closure
 {
   /* Reference count.  */
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 30/43] Add new computed struct value callback interface
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (28 preceding siblings ...)
  2021-03-01 14:46 ` [PATCH 29/43] Add indirect_implicit_ptr to dwarf_location class Zoran Zaric
@ 2021-03-01 14:46 ` Zoran Zaric
  2021-03-01 14:46 ` [PATCH 31/43] Add to_gdb_value method to DWARF entry class Zoran Zaric
                   ` (12 subsequent siblings)
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:46 UTC (permalink / raw)
  To: gdb-patches

At this point all support is there to add a new callback interface
for the computed struct value infrastructure.

Original callback interface (piece closure) is going to be removed as
soon as the switch to the new DWARF entry classes is done in the next
few patches.

gdb/ChangeLog:

        * dwarf2/expr.c (class computed_closure): New class.
        (closure_value_funcs): New closure callback structure.
        (copy_value_closure): New function.
        (free_value_closure): New function.
        (rw_closure_value): New function.
        (check_synthetic_pointer): New function.
        (write_closure_value): New function.
        (read_closure_value): New function.
        (indirect_closure_value): New function.
        (coerce_closure_ref): New function.
---
 gdb/dwarf2/expr.c | 265 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 265 insertions(+)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 2550c59dc27..ec78a698e52 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -320,6 +320,91 @@ class dwarf_location;
 class dwarf_memory;
 class dwarf_value;
 
+/* Closure callback functions.  */
+
+static void *
+copy_value_closure (const struct value *v);
+
+static void
+free_value_closure (struct value *v);
+
+static void
+rw_closure_value (struct value *v, struct value *from);
+
+static int
+check_synthetic_pointer (const struct value *value, LONGEST bit_offset,
+			 int bit_length);
+
+static void
+write_closure_value (struct value *to, struct value *from);
+
+static void
+read_closure_value (struct value *v);
+
+static struct value *
+indirect_closure_value (struct value *value);
+
+static struct value *
+coerce_closure_ref (const struct value *value);
+
+/* Functions for accessing a variable described by DW_OP_piece,
+   DW_OP_bit_piece or DW_OP_implicit_pointer.  */
+
+static const struct lval_funcs closure_value_funcs = {
+  read_closure_value,
+  write_closure_value,
+  indirect_closure_value,
+  coerce_closure_ref,
+  check_synthetic_pointer,
+  copy_value_closure,
+  free_value_closure
+};
+
+/* Closure class that encapsulates a DWARF location description and a
+   frame information used when that location description was created.
+   Used for lval_computed value abstraction.  */
+
+class computed_closure : public refcounted_object
+{
+public:
+  computed_closure (std::shared_ptr<dwarf_location> location,
+		    struct frame_id frame_id)
+    : m_location (location), m_frame_id (frame_id)
+  {}
+
+  computed_closure (std::shared_ptr<dwarf_location> location,
+		    struct frame_info *frame)
+    : m_location (location), m_frame (frame)
+  {}
+
+  const std::shared_ptr<dwarf_location> get_location () const
+  {
+    return m_location;
+  }
+
+  struct frame_id get_frame_id () const
+  {
+    return m_frame_id;
+  }
+
+  struct frame_info *get_frame () const
+  {
+    return m_frame;
+  }
+
+private:
+  /* Entry that this class encloses.  */
+  std::shared_ptr<dwarf_location> m_location;
+
+  /* Frame ID context of the closure.  */
+  struct frame_id m_frame_id;
+
+  /* In the case of frame expression evaluator the frame_id
+     is not safe to use because the frame itself is being built.
+     Only in these cases we set and use frame info directly.  */
+  struct frame_info *m_frame = NULL;
+};
+
 /* Base class that describes entries found on a DWARF expression
    evaluation stack.  */
 
@@ -1551,6 +1636,186 @@ dwarf_composite::indirect_implicit_ptr (struct frame_info *frame,
   return nullptr;
 }
 
+static void *
+copy_value_closure (const struct value *v)
+{
+  computed_closure *closure = ((computed_closure*) value_computed_closure (v));
+
+  if (closure == nullptr)
+    internal_error (__FILE__, __LINE__, _("invalid closure type"));
+
+  closure->incref ();
+  return closure;
+}
+
+static void
+free_value_closure (struct value *v)
+{
+  computed_closure *closure = ((computed_closure*) value_computed_closure (v));
+
+  if (closure == nullptr)
+    internal_error (__FILE__, __LINE__, _("invalid closure type"));
+
+  closure->decref ();
+
+  if (closure->refcount () == 0)
+    delete closure;
+}
+
+/* Read or write a closure value V.  If FROM != NULL, operate in "write
+   mode": copy FROM into the closure comprising V.  If FROM == NULL,
+   operate in "read mode": fetch the contents of the (lazy) value V by
+   composing it from its closure.  */
+
+static void
+rw_closure_value (struct value *v, struct value *from)
+{
+  LONGEST bit_offset = 0, max_bit_size;
+  computed_closure *closure = (computed_closure*) value_computed_closure (v);
+  bool big_endian = type_byte_order (value_type (v)) == BFD_ENDIAN_BIG;
+  auto location = closure->get_location ();
+
+  if (from == NULL)
+    {
+      if (value_type (v) != value_enclosing_type (v))
+        internal_error (__FILE__, __LINE__,
+			_("Should not be able to create a lazy value with "
+			  "an enclosing type"));
+    }
+
+  ULONGEST bits_to_skip = HOST_CHAR_BIT * value_offset (v);
+
+  /* If there are bits that don't complete a byte, count them in.  */
+  if (value_bitsize (v))
+    {
+      bits_to_skip += HOST_CHAR_BIT * value_offset (value_parent (v))
+		       + value_bitpos (v);
+      if (from != NULL && big_endian)
+	{
+	  /* Use the least significant bits of FROM.  */
+	  max_bit_size = HOST_CHAR_BIT * TYPE_LENGTH (value_type (from));
+	  bit_offset = max_bit_size - value_bitsize (v);
+	}
+      else
+	max_bit_size = value_bitsize (v);
+    }
+  else
+    max_bit_size = HOST_CHAR_BIT * TYPE_LENGTH (value_type (v));
+
+  struct frame_info *frame = closure->get_frame ();
+
+  if (frame == NULL)
+    frame = frame_find_by_id (closure->get_frame_id ());
+
+  if (from == NULL)
+    {
+      location->write_to_gdb_value (frame, v, bit_offset, bits_to_skip,
+				    max_bit_size - bit_offset, 0);
+    }
+  else
+    {
+      location->read_from_gdb_value (frame, from, bit_offset, bits_to_skip,
+				     max_bit_size - bit_offset, 0);
+    }
+}
+
+static void
+read_closure_value (struct value *v)
+{
+  rw_closure_value (v, NULL);
+}
+
+static void
+write_closure_value (struct value *to, struct value *from)
+{
+  rw_closure_value (to, from);
+}
+
+/* An implementation of an lval_funcs method to see whether a value is
+   a synthetic pointer.  */
+
+static int
+check_synthetic_pointer (const struct value *value, LONGEST bit_offset,
+			 int bit_length)
+{
+  LONGEST total_bit_offset = bit_offset + HOST_CHAR_BIT * value_offset (value);
+
+  if (value_bitsize (value))
+    total_bit_offset += value_bitpos (value);
+
+  computed_closure *closure
+    = (computed_closure *) value_computed_closure (value);
+
+  return closure->get_location ()->is_implicit_ptr_at (total_bit_offset,
+						       bit_length);
+}
+
+/* An implementation of an lval_funcs method to indirect through a
+   pointer.  This handles the synthetic pointer case when needed.  */
+
+static struct value *
+indirect_closure_value (struct value *value)
+{
+  computed_closure *closure
+    = (computed_closure *) value_computed_closure (value);
+
+  struct type *type = check_typedef (value_type (value));
+  if (type->code () != TYPE_CODE_PTR)
+    return NULL;
+
+  LONGEST bit_length = HOST_CHAR_BIT * TYPE_LENGTH (type);
+  LONGEST bit_offset = HOST_CHAR_BIT * value_offset (value);
+
+  if (value_bitsize (value))
+    bit_offset += value_bitpos (value);
+
+  struct frame_info *frame = get_selected_frame (_("No frame selected."));
+
+  /* This is an offset requested by GDB, such as value subscripts.
+     However, due to how synthetic pointers are implemented, this is
+     always presented to us as a pointer type.  This means we have to
+     sign-extend it manually as appropriate.  Use raw
+     extract_signed_integer directly rather than value_as_address and
+     sign extend afterwards on architectures that would need it
+     (mostly everywhere except MIPS, which has signed addresses) as
+     the later would go through gdbarch_pointer_to_address and thus
+     return a CORE_ADDR with high bits set on architectures that
+     encode address spaces and other things in CORE_ADDR.  */
+  enum bfd_endian byte_order = gdbarch_byte_order (get_frame_arch (frame));
+  LONGEST pointer_offset
+    = extract_signed_integer (value_contents (value),
+			      TYPE_LENGTH (type), byte_order);
+
+  return closure->get_location ()->indirect_implicit_ptr (frame, type,
+							  pointer_offset,
+							  bit_offset, bit_length);
+}
+
+/* Implementation of the coerce_ref method of lval_funcs for synthetic C++
+   references.  */
+
+static struct value *
+coerce_closure_ref (const struct value *value)
+{
+  struct type *type = check_typedef (value_type (value));
+
+  if (value_bits_synthetic_pointer (value, value_embedded_offset (value),
+				    TARGET_CHAR_BIT * TYPE_LENGTH (type)))
+    {
+      computed_closure *closure
+	= (computed_closure *) value_computed_closure (value);
+      struct frame_info *frame
+	= get_selected_frame (_("No frame selected."));
+
+      return closure->get_location ()->indirect_implicit_ptr (frame, type);
+    }
+  else
+    {
+      /* Else: not a synthetic reference; do nothing.  */
+      return NULL;
+    }
+}
+
 struct piece_closure
 {
   /* Reference count.  */
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 31/43] Add to_gdb_value method to DWARF entry class
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (29 preceding siblings ...)
  2021-03-01 14:46 ` [PATCH 30/43] Add new computed struct value callback interface Zoran Zaric
@ 2021-03-01 14:46 ` Zoran Zaric
  2021-03-01 14:46 ` [PATCH 32/43] Change DWARF stack to use new dwarf_entry classes Zoran Zaric
                   ` (11 subsequent siblings)
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:46 UTC (permalink / raw)
  To: gdb-patches

The result of the DWARF expression evaluation is expected to be in a
format of a struct value object. This means that a new to_gdb_value
method is needed for all classes that derive from dwarf_entry class.

In the case of the dwarf_value class, it is usefull to have an even
more simplified version of this method (convert_to_gdb_value) that
does not require the evaluation frame context or any subobj
information. This method will be used even more in the following patch.

gdb/ChangeLog:

        * dwarf2/expr.c (dwarf_entry::to_gdb_value): New method.
        (dwarf_location::to_gdb_value): New method.
        (dwarf_value::convert_to_gdb_value): New method.
        (dwarf_value::to_gdb_value): New method.
        (dwarf_undefined::to_gdb_value): New method.
        (dwarf_memory::to_gdb_value): New method.
        (dwarf_register::to_gdb_value): New method.
        (dwarf_implicit::to_gdb_value): New method.
        (dwarf_implicit_pointer::to_gdb_value): New method.
        (dwarf_composite::to_gdb_value): New method.
---
 gdb/dwarf2/expr.c | 239 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 239 insertions(+)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index ec78a698e52..a2bdee95725 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -425,6 +425,16 @@ class dwarf_entry : public std::enable_shared_from_this<dwarf_entry>
      desired type of the returned DWARF value if it already
      doesnt have one.  */
   virtual std::shared_ptr<dwarf_value> to_value (struct type *type) = 0;
+
+  /* Convert DWARF entry to the matching struct value representation
+     of the given TYPE type in a given FRAME. SUBOBJ_TYPE information
+     if specified, will be used for more precise description of the
+     source variable type information.  Where SUBOBJ_OFFSET defines an
+     offset into the DWARF entry contents.  */
+  virtual struct value *to_gdb_value (struct frame_info *frame,
+				      struct type *type,
+				      struct type *subobj_type,
+				      LONGEST subobj_offset) const = 0;
 };
 
 dwarf_entry::~dwarf_entry () = default;
@@ -744,6 +754,12 @@ class dwarf_value : public dwarf_entry
     return unpack_long (m_type, m_contents.get ());
   }
 
+  /* Convert DWARF value to the matching struct value representation
+     of the given TYPE type.  Where offset defines an offset into the
+     DWARF value contents.  */
+  struct value *convert_to_gdb_value (struct type *type,
+				      LONGEST offset = 0) const;
+
   /* Convert DWARF value into a DWARF memory location description.
      ARCH defines an architecture of the location described.  */
   std::shared_ptr<dwarf_location> to_location (struct gdbarch *arch) override;
@@ -756,6 +772,10 @@ class dwarf_value : public dwarf_entry
     return std::dynamic_pointer_cast<dwarf_value> (shared_from_this ());
   }
 
+  struct value *to_gdb_value (struct frame_info *frame, struct type *type,
+			      struct type *subobj_type,
+			      LONGEST subobj_offset) const override;
+
 private:
   /* Value contents as a stream of bytes in target byte order.  */
   gdb::unique_xmalloc_ptr<gdb_byte> m_contents;
@@ -764,6 +784,20 @@ class dwarf_value : public dwarf_entry
   struct type *m_type;
 };
 
+struct value *
+dwarf_value::convert_to_gdb_value (struct type *type, LONGEST offset) const
+{
+  size_t type_len = TYPE_LENGTH (type);
+
+  if (offset + type_len > TYPE_LENGTH (m_type))
+    invalid_synthetic_pointer ();
+
+  struct value *retval = allocate_value (type);
+  memcpy (value_contents_raw (retval),
+	  m_contents.get () + offset, type_len);
+  return retval;
+}
+
 std::shared_ptr<dwarf_location>
 dwarf_value::to_location (struct gdbarch *arch)
 {
@@ -779,6 +813,17 @@ dwarf_value::to_location (struct gdbarch *arch)
   return std::dynamic_pointer_cast<dwarf_location> (memory);
 }
 
+struct value *
+dwarf_value::to_gdb_value (struct frame_info *frame, struct type *type,
+			   struct type *subobj_type,
+			   LONGEST subobj_offset) const
+{
+  if (subobj_type == nullptr)
+    subobj_type = type;
+
+  return convert_to_gdb_value (subobj_type, subobj_offset);
+}
+
 /* Undefined location description entry.  This is a special location
    description type that describes the location description that is
    not known.  */
@@ -807,6 +852,20 @@ class dwarf_undefined : public dwarf_location
     *unavailable = 0;
     *optimized = 1;
   }
+
+  struct value *to_gdb_value (struct frame_info *frame, struct type *type,
+			      struct type *subobj_type,
+			      LONGEST subobj_offset) const override
+  {
+    struct value *retval = allocate_value (subobj_type);
+
+    if (subobj_type == nullptr)
+      subobj_type = type;
+
+    mark_value_bytes_optimized_out (retval, subobj_offset,
+				    TYPE_LENGTH (subobj_type));
+    return retval;
+  }
 };
 
 class dwarf_memory : public dwarf_location
@@ -839,6 +898,10 @@ class dwarf_memory : public dwarf_location
     (struct frame_info *frame, const struct property_addr_info *addr_info,
      struct type *type, size_t size = 0) const override;
 
+  struct value *to_gdb_value (struct frame_info *frame, struct type *type,
+			      struct type *subobj_type,
+			      LONGEST subobj_offset) const override;
+
 private:
   /* True if the location belongs to a stack memory region.  */
   bool m_stack;
@@ -1012,6 +1075,27 @@ dwarf_memory::deref (struct frame_info *frame,
   return std::make_shared<dwarf_value> (read_buf.data (), type);
 }
 
+struct value *
+dwarf_memory::to_gdb_value (struct frame_info *frame, struct type *type,
+			    struct type *subobj_type,
+			    LONGEST subobj_offset) const
+{
+  if (subobj_type == nullptr)
+    subobj_type = type;
+
+  struct type *ptr_type = builtin_type (m_arch)->builtin_data_ptr;
+  CORE_ADDR address = m_offset;
+
+  if (subobj_type->code () == TYPE_CODE_FUNC
+      || subobj_type->code () == TYPE_CODE_METHOD)
+    ptr_type = builtin_type (m_arch)->builtin_func_ptr;
+
+  address = value_as_address (value_from_pointer (ptr_type, address));
+  struct value *retval = value_at_lazy (subobj_type, address + subobj_offset);
+  set_value_stack (retval, m_stack);
+  return retval;
+}
+
 /* Register location description entry.  */
 
 class dwarf_register : public dwarf_location
@@ -1032,6 +1116,10 @@ class dwarf_register : public dwarf_location
 	      size_t location_bit_limit, bool big_endian,
 	      int *optimized, int *unavailable) const override;
 
+  struct value *to_gdb_value (struct frame_info *frame, struct type *type,
+			      struct type *subobj_type,
+			      LONGEST subobj_offset) const override;
+
 private:
   /* DWARF register number.  */
   unsigned int m_regnum;
@@ -1123,6 +1211,50 @@ dwarf_register::write (struct frame_info *frame, const gdb_byte *buf,
 		     temp_buf, optimized, unavailable);
 }
 
+struct value *
+dwarf_register::to_gdb_value (struct frame_info *frame, struct type *type,
+			      struct type *subobj_type,
+			      LONGEST subobj_offset) const
+{
+  int gdb_regnum = dwarf_reg_to_regnum_or_error (m_arch, m_regnum);
+
+  if (subobj_type == nullptr)
+    subobj_type = type;
+
+  if (frame == NULL)
+    internal_error (__FILE__, __LINE__, _("invalid frame information"));
+
+  /* Construct the value.  */
+  struct value *retval
+    = gdbarch_value_from_register (m_arch, type,
+				   gdb_regnum, get_frame_id (frame));
+  LONGEST retval_offset = value_offset (retval);
+
+  if (type_byte_order (type) == BFD_ENDIAN_BIG
+      && TYPE_LENGTH (type) + m_offset < retval_offset)
+    /* Big-endian, and we want less than full size.  */
+    set_value_offset (retval, retval_offset - m_offset);
+  else
+    set_value_offset (retval, retval_offset + m_offset);
+
+  /* Get the data.  */
+  read_frame_register_value (retval, frame);
+
+  if (value_optimized_out (retval))
+    {
+      /* This means the register has undefined value / was not saved.
+	 As we're computing the location of some variable etc. in the
+	 program, not a value for inspecting a register ($pc, $sp, etc.),
+	 return a generic optimized out value instead, so that we show
+	 <optimized out> instead of <not saved>.  */
+      struct value *temp = allocate_value (subobj_type);
+      value_contents_copy (temp, 0, retval, 0, TYPE_LENGTH (subobj_type));
+      retval = temp;
+    }
+
+  return retval;
+}
+
 /* Implicit location description entry.  Describes a location
    description not found on the target but instead saved in a
    gdb-allocated buffer.  */
@@ -1155,6 +1287,10 @@ class dwarf_implicit : public dwarf_location
     *unavailable = 0;
   }
 
+  struct value *to_gdb_value (struct frame_info *frame, struct type *type,
+			      struct type *subobj_type,
+			      LONGEST subobj_offset) const override;
+
 private:
   /* Implicit location contents as a stream of bytes in target byte-order.  */
   gdb::unique_xmalloc_ptr<gdb_byte> m_contents;
@@ -1205,6 +1341,36 @@ dwarf_implicit::read (struct frame_info *frame, gdb_byte *buf,
 		total_bits_to_skip, bit_size, big_endian);
 }
 
+struct value *
+dwarf_implicit::to_gdb_value (struct frame_info *frame, struct type *type,
+			      struct type *subobj_type,
+			      LONGEST subobj_offset) const
+{
+  if (subobj_type == nullptr)
+    subobj_type = type;
+
+  size_t subtype_len = TYPE_LENGTH (subobj_type);
+  size_t type_len = TYPE_LENGTH (type);
+
+  /* To be compatible with expected error output of the existing
+     tests, the invalid synthetic pointer is not reported for
+     DW_OP_implicit_value operation.  */
+  if (subobj_offset + subtype_len > type_len
+      && m_byte_order != BFD_ENDIAN_UNKNOWN)
+    invalid_synthetic_pointer ();
+
+  struct value *retval = allocate_value (subobj_type);
+
+  /* The given offset is relative to the actual object.  */
+  if (m_byte_order == BFD_ENDIAN_BIG)
+    subobj_offset += m_size - type_len;
+
+  memcpy ((void *)value_contents_raw (retval),
+	  (void *)(m_contents.get () + subobj_offset), subtype_len);
+
+  return retval;
+}
+
 /* Implicit pointer location description entry.  */
 
 class dwarf_implicit_pointer : public dwarf_location
@@ -1260,6 +1426,10 @@ class dwarf_implicit_pointer : public dwarf_location
 				       LONGEST bit_offset = 0,
 				       int bit_length = 0) const override;
 
+  struct value *to_gdb_value (struct frame_info *frame, struct type *type,
+			      struct type *subobj_type,
+			      LONGEST subobj_offset) const override;
+
 private:
   /* Per object file data of the implicit pointer.  */
   dwarf2_per_objfile *m_per_objfile;
@@ -1323,6 +1493,32 @@ dwarf_implicit_pointer::indirect_implicit_ptr (struct frame_info *frame,
 				     m_per_cu, m_per_objfile, frame, type);
 }
 
+struct value *
+dwarf_implicit_pointer::to_gdb_value (struct frame_info *frame,
+				      struct type *type,
+				      struct type *subobj_type,
+				      LONGEST subobj_offset) const
+{
+  if (subobj_type == nullptr)
+    subobj_type = type;
+
+  /* Complain if the expression is larger than the size of the
+     outer type.  */
+  if (m_addr_size > HOST_CHAR_BIT * TYPE_LENGTH (type))
+    invalid_synthetic_pointer ();
+
+  computed_closure *closure
+    = new computed_closure (std::make_shared<dwarf_implicit_pointer> (*this),
+			    get_frame_id (frame));
+  closure->incref ();
+
+  struct value *retval
+    = allocate_computed_value (subobj_type, &closure_value_funcs, closure);
+  set_value_offset (retval, subobj_offset);
+
+  return retval;
+}
+
 /* Composite location description entry.  */
 
 class dwarf_composite : public dwarf_location
@@ -1366,6 +1562,10 @@ class dwarf_composite : public dwarf_location
 				       LONGEST bit_offset = 0,
 				       int bit_length = 0) const override;
 
+  struct value *to_gdb_value (struct frame_info *frame, struct type *type,
+			      struct type *subobj_type,
+			      LONGEST subobj_offset) const override;
+
 private:
   /* Composite piece that contains a piece location
      description and it's size.  */
@@ -1636,6 +1836,45 @@ dwarf_composite::indirect_implicit_ptr (struct frame_info *frame,
   return nullptr;
 }
 
+struct value *
+dwarf_composite::to_gdb_value (struct frame_info *frame, struct type *type,
+			       struct type *subobj_type,
+			       LONGEST subobj_offset) const
+{
+  size_t pieces_num = m_pieces.size ();
+  ULONGEST bit_size = 0;
+
+  if (subobj_type == nullptr)
+    subobj_type = type;
+
+  for (unsigned int i = 0; i < pieces_num; i++)
+    bit_size += m_pieces[i].m_size;
+
+  /* Complain if the expression is larger than the size of the
+     outer type.  */
+  if (bit_size > HOST_CHAR_BIT * TYPE_LENGTH (type))
+    invalid_synthetic_pointer ();
+
+  computed_closure *closure;
+
+  /* If compilation unit information is not available
+     we are in a CFI context.  */
+  if (m_per_cu == NULL)
+    closure = new computed_closure (std::make_shared<dwarf_composite> (*this),
+				    frame);
+  else
+    closure = new computed_closure (std::make_shared<dwarf_composite> (*this),
+				    get_frame_id (frame));
+
+  closure->incref ();
+
+  struct value *retval
+    = allocate_computed_value (subobj_type, &closure_value_funcs, closure);
+  set_value_offset (retval, subobj_offset);
+
+  return retval;
+}
+
 static void *
 copy_value_closure (const struct value *v)
 {
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 32/43] Change DWARF stack to use new dwarf_entry classes
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (30 preceding siblings ...)
  2021-03-01 14:46 ` [PATCH 31/43] Add to_gdb_value method to DWARF entry class Zoran Zaric
@ 2021-03-01 14:46 ` Zoran Zaric
  2021-03-01 14:46 ` [PATCH 33/43] Remove old computed struct value callbacks Zoran Zaric
                   ` (10 subsequent siblings)
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:46 UTC (permalink / raw)
  To: gdb-patches

To replace existing DWARF stack element (dwarf_stack_value) with
dwarf_entry class based objects, a support for conversion between
struct value and the new classes is needed. The reason for this is
that dwarf_entry based classes are not designed to be visible outside
the expr.c file. This makes the DWARF expression evaluator more self
contained. This can be beneficial if there is ever a need to have a
DWARF support in gdbserver.

In the previous patch the conversion between the DWARF entry classes to
the struct value has been already added in a form of a to_gdb_value
method.

The interface that is still missing is to convert from struct value to
the DWARF entry classes. New static function gdb_value_to_dwarf_entry
has been added for this purpose.

We also need a way to perform DWARF arithmetic and logical operations
on DWARF values and for this a new set of static functions
(dwarf_value_X) has been provided.

Currently the existing struct value operations are used under the
hood of these functions to avoid the code duplication. Vector types
are planned to be promoted to base types in the future anyway which
means that the operations subset needed is just going to grow.

Now, everything is ready so that the DWARF stack element can easily be
swapped out.

gdb/ChangeLog:

        * dwarf2/expr.c (gdb_value_to_dwarf_entry): New function.
        (dwarf_value_cast_op): New function.
        (dwarf_value_complement_op): New function.
        (dwarf_value_negation_op): New function.
        (dwarf_value_binary_op): New function.
        (dwarf_value_less_op): New function.
        (dwarf_value_equal_op): New function.
        (allocate_piece_closure): Remove unused function.
        (dwarf_expr_context::push): Change to use dwarf_entry based
        classes.
        (dwarf_expr_context::push_address): Change to use dwarf_entry
        based classes.
        (dwarf_expr_context::fetch): Change to use dwarf_entry based
        classes.
        (dwarf_expr_context::read_mem): Remove method.
        (dwarf_expr_context::fetch_result): Change to use dwarf_entry
        based classes.
        (dwarf_expr_context::fetch_address): Change to use dwarf_entry
        based classes.
        (dwarf_expr_context::fetch_in_stack_memory): Remove method.
        (dwarf_expr_context::add_piece): Change to use dwarf_entry based
        classes.
        (dwarf_expr_context::execute_stack_op): Change to use dwarf_entry
        based classes.
        * dwarf2/expr.h (class dwarf_entry): New declaration.
        (struct dwarf_stack_value): Remove structure.
        (struct dwarf_expr_context): Change to use dwarf_entry based.
---
 gdb/dwarf2/expr.c | 978 ++++++++++++++++++++++------------------------
 gdb/dwarf2/expr.h |  70 +---
 2 files changed, 466 insertions(+), 582 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index a2bdee95725..d51366007ab 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -1875,6 +1875,92 @@ dwarf_composite::to_gdb_value (struct frame_info *frame, struct type *type,
   return retval;
 }
 
+/* Set of functions that perform different arithmetic operations
+   on a given DWARF value arguments.
+
+   Currently the existing struct value operations are used under the
+   hood to avoid the code duplication.  Vector types are planned to be
+   promoted to base types in the future anyway which means that the
+   operations subset needed is just going to grow anyway.  */
+
+/* Compare two DWARF value's ARG1 and ARG2 for equality in a context
+   of a value entry comparison.  */
+
+static bool
+dwarf_value_equal_op (std::shared_ptr<const dwarf_value> arg1,
+		      std::shared_ptr<const dwarf_value> arg2)
+{
+  struct value *arg1_value = arg1->convert_to_gdb_value (arg1->get_type ());
+  struct value *arg2_value = arg2->convert_to_gdb_value (arg2->get_type ());
+
+  return value_equal (arg1_value, arg2_value);
+}
+
+/* Compare if DWARF value ARG1 is lesser then DWARF value ARG2 in a
+   context of a value entry comparison.   */
+
+static bool
+dwarf_value_less_op (std::shared_ptr<const dwarf_value> arg1,
+		     std::shared_ptr<const dwarf_value> arg2)
+{
+  struct value *arg1_value = arg1->convert_to_gdb_value (arg1->get_type ());
+  struct value *arg2_value = arg2->convert_to_gdb_value (arg2->get_type ());
+
+  return value_less (arg1_value, arg2_value);
+}
+
+/* Apply binary operation OP on given ARG1 and ARG2 arguments
+   and return a new value entry containing the result of that
+   operation.  */
+
+static std::shared_ptr<dwarf_value>
+dwarf_value_binary_op (std::shared_ptr<const dwarf_value> arg1,
+		       std::shared_ptr<const dwarf_value> arg2,
+		       enum exp_opcode op)
+{
+  struct value *arg1_value = arg1->convert_to_gdb_value (arg1->get_type ());
+  struct value *arg2_value = arg2->convert_to_gdb_value (arg2->get_type ());
+  struct value *result = value_binop (arg1_value, arg2_value, op);
+
+  return std::make_shared<dwarf_value> (value_contents_raw (result),
+				        value_type (result));
+}
+
+/* Apply a negation operation on ARG and return a new value entry
+   containing the result of that operation.  */
+
+static std::shared_ptr<dwarf_value>
+dwarf_value_negation_op (std::shared_ptr<const dwarf_value> arg)
+{
+  struct value *result
+    = value_neg (arg->convert_to_gdb_value (arg->get_type ()));
+  return std::make_shared<dwarf_value> (value_contents_raw (result),
+					value_type (result));
+}
+
+/* Apply a complement operation on ARG and return a new value entry
+   containing the result of that operation.  */
+
+static std::shared_ptr<dwarf_value>
+dwarf_value_complement_op (std::shared_ptr<const dwarf_value> arg)
+{
+  struct value *result
+    = value_complement (arg->convert_to_gdb_value (arg->get_type ()));
+  return std::make_shared<dwarf_value> (value_contents_raw (result),
+					value_type (result));
+}
+
+/* Apply a cast operation on ARG and return a new value entry
+   containing the result of that operation.  */
+
+static std::shared_ptr<dwarf_value>
+dwarf_value_cast_op (std::shared_ptr<const dwarf_value> arg, struct type *type)
+{
+  struct value *result
+    = value_cast (type, arg->convert_to_gdb_value (arg->get_type ()));
+  return std::make_shared<dwarf_value> (value_contents_raw (result), type);
+}
+
 static void *
 copy_value_closure (const struct value *v)
 {
@@ -2055,6 +2141,59 @@ coerce_closure_ref (const struct value *value)
     }
 }
 
+/* Convert struct value VALUE to the matching DWARF entry
+   representation.  ARCH describes an architecture of the new
+   entry.  */
+
+static std::shared_ptr<dwarf_entry>
+gdb_value_to_dwarf_entry (struct gdbarch *arch, struct value *value)
+{
+  struct type *type = value_type (value);
+
+  if (value_optimized_out (value))
+    return std::make_shared<dwarf_undefined> (arch);
+
+  LONGEST offset = value_offset (value);
+
+  switch (value_lval_const (value))
+    {
+      /* We can only convert struct value to a location because
+	 we can't distinguish between the implicit value and
+	 not_lval.  */
+    case not_lval:
+      {
+	gdb_byte *contents_start = value_contents_raw (value) + offset;
+
+	return std::make_shared<dwarf_implicit> (arch, contents_start,
+						 TYPE_LENGTH (type),
+						 type_byte_order (type));
+      }
+    case lval_memory:
+      return std::make_shared<dwarf_memory> (arch,
+					     value_address (value) + offset,
+					     0, value_stack (value));
+    case lval_register:
+      return std::make_shared<dwarf_register> (arch, VALUE_REGNUM (value),
+					       false, offset);
+    case lval_computed:
+      {
+	/* Dwarf entry is enclosed by the closure anyway so we just
+	   need to unwrap it here.  */
+	computed_closure *closure
+	  = ((computed_closure *) value_computed_closure (value));
+	auto location = closure->get_location ();
+
+	if (location == nullptr)
+	  internal_error (__FILE__, __LINE__, _("invalid closure type"));
+
+	location->add_bit_offset (offset * HOST_CHAR_BIT);
+	return location;
+      }
+    default:
+      internal_error (__FILE__, __LINE__, _("invalid location type"));
+  }
+}
+
 struct piece_closure
 {
   /* Reference count.  */
@@ -2074,34 +2213,6 @@ struct piece_closure
   struct frame_id frame_id;
 };
 
-/* Allocate a closure for a value formed from separately-described
-   PIECES.  */
-
-static struct piece_closure *
-allocate_piece_closure (dwarf2_per_cu_data *per_cu,
-			dwarf2_per_objfile *per_objfile,
-			std::vector<dwarf_expr_piece> &&pieces,
-			struct frame_info *frame)
-{
-  struct piece_closure *c = new piece_closure;
-
-  c->refc = 1;
-  /* We must capture this here due to sharing of DWARF state.  */
-  c->per_objfile = per_objfile;
-  c->per_cu = per_cu;
-  c->pieces = std::move (pieces);
-  if (frame == NULL)
-    c->frame_id = null_frame_id;
-  else
-    c->frame_id = get_frame_id (frame);
-
-  for (dwarf_expr_piece &piece : c->pieces)
-    if (piece.location == DWARF_VALUE_STACK)
-      value_incref (piece.v.value);
-
-  return c;
-}
-
 /* Read or write a pieced value V.  If FROM != NULL, operate in "write
    mode": copy FROM into the pieces comprising V.  If FROM == NULL,
    operate in "read mode": fetch the contents of the (lazy) value V by
@@ -2649,22 +2760,24 @@ dwarf_expr_context::dwarf_expr_context (dwarf2_per_objfile *per_objfile,
 {
 }
 
-/* Push VALUE onto the stack.  */
+/* Push ENTRY onto the stack.  */
 
 void
-dwarf_expr_context::push (struct value *value, bool in_stack_memory)
+dwarf_expr_context::push (std::shared_ptr<dwarf_entry> entry)
 {
-  stack.emplace_back (value, in_stack_memory);
+  stack.emplace_back (entry);
 }
 
-/* Push VALUE onto the stack.  */
+/* Push ADDR onto the stack.  */
 
 void
-dwarf_expr_context::push_address (CORE_ADDR value, bool in_stack_memory)
+dwarf_expr_context::push_address (CORE_ADDR addr, bool in_stack_memory)
 {
-  push (value_from_ulongest (address_type (), value), in_stack_memory);
+  stack.emplace_back (std::make_shared<dwarf_memory> (this->gdbarch, addr,
+						      0, in_stack_memory));
 }
 
+
 /* Pop the top item off of the stack.  */
 
 void
@@ -2678,14 +2791,14 @@ dwarf_expr_context::pop ()
 
 /* Retrieve the N'th item on the stack.  */
 
-struct value *
+std::shared_ptr<dwarf_entry>
 dwarf_expr_context::fetch (int n)
 {
   if (stack.size () <= n)
      error (_("Asked for position %d of stack, "
 	      "stack only has %zu elements on it."),
 	    n, stack.size ());
-  return stack[stack.size () - (1 + n)].value;
+  return stack[stack.size () - (1 + n)];
 }
 
 /* See expr.h.  */
@@ -2762,31 +2875,6 @@ dwarf_expr_context::dwarf_call (cu_offset die_cu_off)
 
 /* See expr.h.  */
 
-void
-dwarf_expr_context::read_mem (gdb_byte *buf, CORE_ADDR addr,
-			      size_t length)
-{
-  if (length == 0)
-    return;
-
-  /* Prefer the passed-in memory, if it exists.  */
-  if (addr_info != nullptr)
-    {
-      CORE_ADDR offset = addr - addr_info->addr;
-
-      if (offset < addr_info->valaddr.size ()
-	  && offset + length <= addr_info->valaddr.size ())
-    {
-      memcpy (buf, addr_info->valaddr.data (), length);
-      return;
-    }
-    }
-
-  read_memory (addr, buf, length);
-}
-
-/* See expr.h.  */
-
 void
 dwarf_expr_context::push_dwarf_reg_entry_value
 		      (enum call_site_parameter_kind kind,
@@ -2844,158 +2932,20 @@ dwarf_expr_context::fetch_result (struct type *type,
 				  LONGEST subobj_offset,
 				  bool as_lval)
 {
-  struct value *retval = nullptr;
-
   if (type == nullptr)
     type = address_type ();
 
   if (subobj_type == nullptr)
     subobj_type = type;
 
-  if (this->pieces.size () > 0)
-    {
-      struct piece_closure *c;
-      ULONGEST bit_size = 0;
-
-      for (dwarf_expr_piece &piece : this->pieces)
-	bit_size += piece.size;
-      /* Complain if the expression is larger than the size of the
-	 outer type.  */
-      if (bit_size > 8 * TYPE_LENGTH (type))
-	invalid_synthetic_pointer ();
-
-      c = allocate_piece_closure (this->per_cu, this->per_objfile,
-				  std::move (this->pieces), this->frame);
-      retval = allocate_computed_value (subobj_type,
-					&pieced_value_funcs, c);
-      set_value_offset (retval, subobj_offset);
-    }
-  else
-    {
-      /* If AS_LVAL is false, means that the implicit conversion
-	 from a location description to value is expected.  */
-      if (as_lval == false)
-	this->location = DWARF_VALUE_STACK;
-
-      switch (this->location)
-	{
-	case DWARF_VALUE_REGISTER:
-	  {
-	    int dwarf_regnum
-	      = longest_to_int (value_as_long (this->fetch (0)));
-	    int gdb_regnum = dwarf_reg_to_regnum_or_error (this->gdbarch,
-							   dwarf_regnum);
-
-	    if (subobj_offset != 0)
-	      error (_("cannot use offset on synthetic pointer to register"));
-
-	    gdb_assert (this->frame != NULL);
-
-	    retval = value_from_register (subobj_type, gdb_regnum,
-					  this->frame);
-	    if (value_optimized_out (retval))
-	      {
-		struct value *tmp;
-
-		/* This means the register has undefined value / was
-		   not saved.  As we're computing the location of some
-		   variable etc. in the program, not a value for
-		   inspecting a register ($pc, $sp, etc.), return a
-		   generic optimized out value instead, so that we show
-		   <optimized out> instead of <not saved>.  */
-		tmp = allocate_value (subobj_type);
-		value_contents_copy (tmp, 0, retval, 0,
-				     TYPE_LENGTH (subobj_type));
-		retval = tmp;
-	      }
-	  }
-	  break;
-
-	case DWARF_VALUE_MEMORY:
-	  {
-	    struct type *ptr_type;
-	    CORE_ADDR address = this->fetch_address (0);
-	    bool in_stack_memory = this->fetch_in_stack_memory (0);
-
-	    /* DW_OP_deref_size (and possibly other operations too) may
-	       create a pointer instead of an address.  Ideally, the
-	       pointer to address conversion would be performed as part
-	       of those operations, but the type of the object to
-	       which the address refers is not known at the time of
-	       the operation.  Therefore, we do the conversion here
-	       since the type is readily available.  */
-
-	    switch (subobj_type->code ())
-	      {
-		case TYPE_CODE_FUNC:
-		case TYPE_CODE_METHOD:
-		  ptr_type = builtin_type (this->gdbarch)->builtin_func_ptr;
-		  break;
-		default:
-		  ptr_type = builtin_type (this->gdbarch)->builtin_data_ptr;
-		  break;
-	      }
-	    address = value_as_address (value_from_pointer (ptr_type, address));
-
-	    retval = value_at_lazy (subobj_type,
-				    address + subobj_offset);
-	    if (in_stack_memory)
-	      set_value_stack (retval, 1);
-	  }
-	  break;
-
-	case DWARF_VALUE_STACK:
-	  {
-	    struct value *value = this->fetch (0);
-	    size_t n = TYPE_LENGTH (value_type (value));
-	    size_t len = TYPE_LENGTH (subobj_type);
-	    size_t max = TYPE_LENGTH (type);
-
-	    if (subobj_offset + len > max)
-	      invalid_synthetic_pointer ();
-
-	    retval = allocate_value (subobj_type);
-
-	    /* The given offset is relative to the actual object.  */
-	    if (gdbarch_byte_order (this->gdbarch) == BFD_ENDIAN_BIG)
-	      subobj_offset += n - max;
-
-	    memcpy (value_contents_raw (retval),
-		    value_contents_all (value) + subobj_offset, len);
-	  }
-	  break;
-
-	case DWARF_VALUE_LITERAL:
-	  {
-	    bfd_byte *contents;
-	    size_t n = TYPE_LENGTH (subobj_type);
+  auto entry = fetch (0);
 
-	    if (subobj_offset + n > this->len)
-	      invalid_synthetic_pointer ();
-
-	    retval = allocate_value (subobj_type);
-	    contents = value_contents_raw (retval);
-	    memcpy (contents, this->data + subobj_offset, n);
-	  }
-	  break;
-
-	case DWARF_VALUE_OPTIMIZED_OUT:
-	  retval = allocate_optimized_out_value (subobj_type);
-	  break;
-
-	  /* DWARF_VALUE_IMPLICIT_POINTER was converted to a pieced
-	     operation by execute_stack_op.  */
-	case DWARF_VALUE_IMPLICIT_POINTER:
-	  /* DWARF_VALUE_OPTIMIZED_OUT can't occur in this context --
-	     it can only be encountered when making a piece.  */
-	default:
-	  internal_error (__FILE__, __LINE__, _("invalid location type"));
-	}
-    }
-
-  set_value_initialized (retval, this->initialized);
+  if (!as_lval)
+    entry = entry->to_value (address_type ());
+  else
+    entry = entry->to_location (this->gdbarch);
 
-  return retval;
+  return entry->to_gdb_value (this->frame, type, subobj_type, subobj_offset);
 }
 
 /* See expr.h.  */
@@ -3075,51 +3025,6 @@ get_signed_type (struct gdbarch *gdbarch, struct type *type)
     }
 }
 
-/* Retrieve the N'th item on the stack, converted to an address.  */
-
-CORE_ADDR
-dwarf_expr_context::fetch_address (int n)
-{
-  struct value *result_val = fetch (n);
-  enum bfd_endian byte_order = gdbarch_byte_order (this->gdbarch);
-  ULONGEST result;
-
-  dwarf_require_integral (value_type (result_val));
-  result = extract_unsigned_integer (value_contents (result_val),
-				     TYPE_LENGTH (value_type (result_val)),
-				     byte_order);
-
-  /* For most architectures, calling extract_unsigned_integer() alone
-     is sufficient for extracting an address.  However, some
-     architectures (e.g. MIPS) use signed addresses and using
-     extract_unsigned_integer() will not produce a correct
-     result.  Make sure we invoke gdbarch_integer_to_address()
-     for those architectures which require it.  */
-  if (gdbarch_integer_to_address_p (this->gdbarch))
-    {
-      gdb_byte *buf = (gdb_byte *) alloca (this->addr_size);
-      struct type *int_type = get_unsigned_type (this->gdbarch,
-						 value_type (result_val));
-
-      store_unsigned_integer (buf, this->addr_size, byte_order, result);
-      return gdbarch_integer_to_address (this->gdbarch, int_type, buf);
-    }
-
-  return (CORE_ADDR) result;
-}
-
-/* Retrieve the in_stack_memory flag of the N'th item on the stack.  */
-
-bool
-dwarf_expr_context::fetch_in_stack_memory (int n)
-{
-  if (stack.size () <= n)
-     error (_("Asked for position %d of stack, "
-	      "stack only has %zu elements on it."),
-	    n, stack.size ());
-  return stack[stack.size () - (1 + n)].in_stack_memory;
-}
-
 /* Return true if the expression stack is empty.  */
 
 bool
@@ -3128,49 +3033,42 @@ dwarf_expr_context::stack_empty_p () const
   return stack.empty ();
 }
 
-/* Add a new piece to the dwarf_expr_context's piece list.  */
-void
-dwarf_expr_context::add_piece (ULONGEST size, ULONGEST offset)
-{
-  this->pieces.emplace_back ();
-  dwarf_expr_piece &p = this->pieces.back ();
+/* Add a new piece to the composite on top of the stack.  */
 
-  p.location = this->location;
-  p.size = size;
-  p.offset = offset;
+std::shared_ptr<dwarf_entry>
+dwarf_expr_context::add_piece (ULONGEST bit_size, ULONGEST bit_offset)
+{
+  std::shared_ptr<dwarf_location> piece;
+  std::shared_ptr<dwarf_composite> composite;
 
-  if (p.location == DWARF_VALUE_LITERAL)
-    {
-      p.v.literal.data = this->data;
-      p.v.literal.length = this->len;
-    }
-  else if (stack_empty_p ())
-    {
-      p.location = DWARF_VALUE_OPTIMIZED_OUT;
-      /* Also reset the context's location, for our callers.  This is
-	 a somewhat strange approach, but this lets us avoid setting
-	 the location to DWARF_VALUE_MEMORY in all the individual
-	 cases in the evaluator.  */
-      this->location = DWARF_VALUE_OPTIMIZED_OUT;
-    }
-  else if (p.location == DWARF_VALUE_MEMORY)
-    {
-      p.v.mem.addr = fetch_address (0);
-      p.v.mem.in_stack_memory = fetch_in_stack_memory (0);
-    }
-  else if (p.location == DWARF_VALUE_IMPLICIT_POINTER)
+  if (!stack_empty_p ()
+      && std::dynamic_pointer_cast<dwarf_composite> (fetch (0)) == nullptr)
     {
-      p.v.ptr.die_sect_off = (sect_offset) this->len;
-      p.v.ptr.offset = value_as_long (fetch (0));
+      piece = fetch (0)->to_location (this->gdbarch);
+      pop ();
     }
-  else if (p.location == DWARF_VALUE_REGISTER)
-    p.v.regno = value_as_long (fetch (0));
+  else
+    piece = std::make_shared<dwarf_undefined> (this->gdbarch);
+
+  piece->add_bit_offset (bit_offset);
+
+  /* If stack is empty then it is a start of a new composite.  In the
+     future this will check if the composite is finished or not.  */
+  if (stack_empty_p ()
+      || std::dynamic_pointer_cast<dwarf_composite> (fetch (0)) == nullptr)
+    composite = std::make_shared<dwarf_composite> (this->gdbarch,
+						   this->per_cu);
   else
     {
-      p.v.value = fetch (0);
+      composite = std::dynamic_pointer_cast<dwarf_composite> (fetch (0));
+      pop ();
     }
+
+  composite->add_piece (piece, bit_size);
+  return composite;
 }
 
+
 /* Evaluate the expression at ADDR (LEN bytes long).  */
 
 void
@@ -3217,7 +3115,6 @@ safe_skip_leb128 (const gdb_byte *buf, const gdb_byte *buf_end)
     error (_("DWARF expression error: ran off end of buffer reading leb128 value"));
   return buf;
 }
-\f
 
 /* Check that the current operator is either at the end of an
    expression, or that it is followed by a composition operator or by
@@ -3436,9 +3333,6 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
      CU.  */
   struct type *address_type = this->address_type ();
 
-  this->location = DWARF_VALUE_MEMORY;
-  this->initialized = 1;  /* Default is initialized.  */
-
   if (this->recursion_depth > this->max_recursion_depth)
     error (_("DWARF-2 expression error: Loop detected (%d)."),
 	   this->recursion_depth);
@@ -3448,16 +3342,9 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
     {
       enum dwarf_location_atom op = (enum dwarf_location_atom) *op_ptr++;
       ULONGEST result;
-      /* Assume the value is not in stack memory.
-	 Code that knows otherwise sets this to true.
-	 Some arithmetic on stack addresses can probably be assumed to still
-	 be a stack address, but we skip this complication for now.
-	 This is just an optimization, so it's always ok to punt
-	 and leave this as false.  */
-      bool in_stack_memory = false;
       uint64_t uoffset, reg;
       int64_t offset;
-      struct value *result_val = NULL;
+      std::shared_ptr<dwarf_entry> result_entry = nullptr;
 
       /* The DWARF expression might have a bug causing an infinite
 	 loop.  In that case, quitting is the only way out.  */
@@ -3498,7 +3385,7 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	case DW_OP_lit30:
 	case DW_OP_lit31:
 	  result = op - DW_OP_lit0;
-	  result_val = value_from_ulongest (address_type, result);
+	  result_entry = std::make_shared<dwarf_value> (result, address_type);
 	  break;
 
 	case DW_OP_addr:
@@ -3510,8 +3397,15 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	     index, not an address.  We don't support things like
 	     branching between the address and the TLS op.  */
 	  if (op_ptr >= op_end || *op_ptr != DW_OP_GNU_push_tls_address)
-	    result += this->per_objfile->objfile->text_section_offset ();
-	  result_val = value_from_ulongest (address_type, result);
+	    {
+	      result += this->per_objfile->objfile->text_section_offset ();
+	      result_entry
+		= std::make_shared<dwarf_memory> (this->gdbarch, result);
+	    }
+	  else
+	    /* This is a special case where the value is expected to be
+	       created instead of memory location.  */
+	    result_entry = std::make_shared<dwarf_value> (result, address_type);
 	  break;
 
 	case DW_OP_addrx:
@@ -3522,7 +3416,8 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	  result = dwarf2_read_addr_index (this->per_cu, this->per_objfile,
 					   uoffset);
 	  result += this->per_objfile->objfile->text_section_offset ();
-	  result_val = value_from_ulongest (address_type, result);
+	  result_entry
+	    = std::make_shared<dwarf_memory> (this->gdbarch, result);
 	  break;
 	case DW_OP_GNU_const_index:
 	  ensure_have_per_cu (per_cu, "DW_OP_GNU_const_index");
@@ -3530,58 +3425,58 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	  op_ptr = safe_read_uleb128 (op_ptr, op_end, &uoffset);
 	  result = dwarf2_read_addr_index (this->per_cu, this->per_objfile,
 					   uoffset);
-	  result_val = value_from_ulongest (address_type, result);
+	  result_entry = std::make_shared<dwarf_value> (result, address_type);
 	  break;
 
 	case DW_OP_const1u:
 	  result = extract_unsigned_integer (op_ptr, 1, byte_order);
-	  result_val = value_from_ulongest (address_type, result);
+	  result_entry = std::make_shared<dwarf_value> (result, address_type);
 	  op_ptr += 1;
 	  break;
 	case DW_OP_const1s:
 	  result = extract_signed_integer (op_ptr, 1, byte_order);
-	  result_val = value_from_ulongest (address_type, result);
+	  result_entry = std::make_shared<dwarf_value> (result, address_type);
 	  op_ptr += 1;
 	  break;
 	case DW_OP_const2u:
 	  result = extract_unsigned_integer (op_ptr, 2, byte_order);
-	  result_val = value_from_ulongest (address_type, result);
+	  result_entry = std::make_shared<dwarf_value> (result, address_type);
 	  op_ptr += 2;
 	  break;
 	case DW_OP_const2s:
 	  result = extract_signed_integer (op_ptr, 2, byte_order);
-	  result_val = value_from_ulongest (address_type, result);
+	  result_entry = std::make_shared<dwarf_value> (result, address_type);
 	  op_ptr += 2;
 	  break;
 	case DW_OP_const4u:
 	  result = extract_unsigned_integer (op_ptr, 4, byte_order);
-	  result_val = value_from_ulongest (address_type, result);
+	  result_entry = std::make_shared<dwarf_value> (result, address_type);
 	  op_ptr += 4;
 	  break;
 	case DW_OP_const4s:
 	  result = extract_signed_integer (op_ptr, 4, byte_order);
-	  result_val = value_from_ulongest (address_type, result);
+	  result_entry = std::make_shared<dwarf_value> (result, address_type);
 	  op_ptr += 4;
 	  break;
 	case DW_OP_const8u:
 	  result = extract_unsigned_integer (op_ptr, 8, byte_order);
-	  result_val = value_from_ulongest (address_type, result);
+	  result_entry = std::make_shared<dwarf_value> (result, address_type);
 	  op_ptr += 8;
 	  break;
 	case DW_OP_const8s:
 	  result = extract_signed_integer (op_ptr, 8, byte_order);
-	  result_val = value_from_ulongest (address_type, result);
+	  result_entry = std::make_shared<dwarf_value> (result, address_type);
 	  op_ptr += 8;
 	  break;
 	case DW_OP_constu:
 	  op_ptr = safe_read_uleb128 (op_ptr, op_end, &uoffset);
 	  result = uoffset;
-	  result_val = value_from_ulongest (address_type, result);
+	  result_entry = std::make_shared<dwarf_value> (result, address_type);
 	  break;
 	case DW_OP_consts:
 	  op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset);
 	  result = offset;
-	  result_val = value_from_ulongest (address_type, result);
+	  result_entry = std::make_shared<dwarf_value> (result, address_type);
 	  break;
 
 	/* The DW_OP_reg operations are required to occur alone in
@@ -3621,8 +3516,8 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	  dwarf_expr_require_composition (op_ptr, op_end, "DW_OP_reg");
 
 	  result = op - DW_OP_reg0;
-	  result_val = value_from_ulongest (address_type, result);
-	  this->location = DWARF_VALUE_REGISTER;
+	  result_entry
+	    = std::make_shared<dwarf_register> (this->gdbarch, result);
 	  break;
 
 	case DW_OP_regx:
@@ -3630,8 +3525,7 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	  dwarf_expr_require_composition (op_ptr, op_end, "DW_OP_regx");
 
 	  result = reg;
-	  result_val = value_from_ulongest (address_type, result);
-	  this->location = DWARF_VALUE_REGISTER;
+	  result_entry = std::make_shared<dwarf_register> (this->gdbarch, reg);
 	  break;
 
 	case DW_OP_implicit_value:
@@ -3641,19 +3535,32 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	    op_ptr = safe_read_uleb128 (op_ptr, op_end, &len);
 	    if (op_ptr + len > op_end)
 	      error (_("DW_OP_implicit_value: too few bytes available."));
-	    this->len = len;
-	    this->data = op_ptr;
-	    this->location = DWARF_VALUE_LITERAL;
+	    result_entry
+	      = std::make_shared<dwarf_implicit> (this->gdbarch, op_ptr, len,
+						  BFD_ENDIAN_UNKNOWN);
 	    op_ptr += len;
 	    dwarf_expr_require_composition (op_ptr, op_end,
 					    "DW_OP_implicit_value");
 	  }
-	  goto no_push;
+	  break;
 
 	case DW_OP_stack_value:
-	  this->location = DWARF_VALUE_STACK;
-	  dwarf_expr_require_composition (op_ptr, op_end, "DW_OP_stack_value");
-	  goto no_push;
+	  {
+	    auto value = fetch (0)->to_value (address_type);
+	    pop ();
+
+	    struct type* type = value->get_type ();
+
+	    result_entry
+	      = std::make_shared<dwarf_implicit> (this->gdbarch,
+						  value->get_contents (),
+						  TYPE_LENGTH (type),
+						  type_byte_order (type));
+
+	    dwarf_expr_require_composition (op_ptr, op_end,
+					    "DW_OP_stack_value");
+	  }
+	  break;
 
 	case DW_OP_implicit_pointer:
 	case DW_OP_GNU_implicit_pointer:
@@ -3662,16 +3569,20 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	    ensure_have_per_cu (per_cu, "DW_OP_implicit_pointer");
 
 	    /* The referred-to DIE of sect_offset kind.  */
-	    this->len = extract_unsigned_integer (op_ptr, this->ref_addr_size,
-						 byte_order);
+	    sect_offset die_offset
+	      = (sect_offset) extract_unsigned_integer (op_ptr,
+							this->ref_addr_size,
+							byte_order);
 	    op_ptr += this->ref_addr_size;
 
 	    /* The byte offset into the data.  */
 	    op_ptr = safe_read_sleb128 (op_ptr, op_end, &len);
-	    result = (ULONGEST) len;
-	    result_val = value_from_ulongest (address_type, result);
-
-	    this->location = DWARF_VALUE_IMPLICIT_POINTER;
+	    result_entry
+	      = std::make_shared<dwarf_implicit_pointer> (this->gdbarch,
+							  this->per_objfile,
+							  this->per_cu,
+							  this->addr_size,
+							  die_offset, len);
 	    dwarf_expr_require_composition (op_ptr, op_end,
 					    "DW_OP_implicit_pointer");
 	  }
@@ -3713,9 +3624,18 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	    op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset);
 	    ensure_have_frame (this->frame, "DW_OP_breg");
 
-	    result = read_addr_from_reg (this->frame, op - DW_OP_breg0);
-	    result += offset;
-	    result_val = value_from_ulongest (address_type, result);
+	    reg = op - DW_OP_breg0;
+
+	    int regnum = dwarf_reg_to_regnum_or_error (this->gdbarch, reg);
+	    ULONGEST reg_size = register_size (this->gdbarch, regnum);
+	    std::shared_ptr<dwarf_location> location
+	      = std::make_shared<dwarf_register> (this->gdbarch, reg);
+	    result_entry = location->deref (frame, this->addr_info,
+					    address_type, reg_size);
+
+	    location = result_entry->to_location (this->gdbarch);
+	    location->add_bit_offset (offset * HOST_CHAR_BIT);
+	    result_entry = location;
 	  }
 	  break;
 	case DW_OP_bregx:
@@ -3724,51 +3644,64 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	    op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset);
 	    ensure_have_frame (this->frame, "DW_OP_bregx");
 
-	    result = read_addr_from_reg (this->frame, reg);
-	    result += offset;
-	    result_val = value_from_ulongest (address_type, result);
+	    int regnum = dwarf_reg_to_regnum_or_error (this->gdbarch, reg);
+	    ULONGEST reg_size = register_size (this->gdbarch, regnum);
+	    std::shared_ptr<dwarf_location> location
+	      = std::make_shared<dwarf_register> (this->gdbarch, reg);
+	    result_entry = location->deref (frame, this->addr_info,
+					    address_type, reg_size);
+
+	    location = result_entry->to_location (this->gdbarch);
+	    location->add_bit_offset (offset * HOST_CHAR_BIT);
+	    result_entry = location;
 	  }
 	  break;
 	case DW_OP_fbreg:
 	  {
-	    const gdb_byte *datastart;
-	    size_t datalen;
-
 	    op_ptr = safe_read_sleb128 (op_ptr, op_end, &offset);
 
 	    /* Rather than create a whole new context, we simply
 	       backup the current stack locally and install a new empty stack,
 	       then reset it afterwards, effectively erasing whatever the
 	       recursive call put there.  */
-	    std::vector<dwarf_stack_value> saved_stack = std::move (stack);
+	    std::vector<std::shared_ptr<dwarf_entry>> saved_stack
+	      = std::move (stack);
 	    stack.clear ();
 
-	    /* FIXME: cagney/2003-03-26: This code should be using
-	       get_frame_base_address(), and then implement a dwarf2
-	       specific this_base method.  */
+	    const gdb_byte *datastart;
+	    size_t datalen;
+
 	    this->get_frame_base (&datastart, &datalen);
 	    eval (datastart, datalen);
-	    if (this->location == DWARF_VALUE_MEMORY)
-	      result = fetch_address (0);
-	    else if (this->location == DWARF_VALUE_REGISTER)
-	      result = read_addr_from_reg (this->frame, value_as_long (fetch (0)));
-	    else
-	      error (_("Not implemented: computing frame "
-		       "base using explicit value operator"));
-	    result = result + offset;
-	    result_val = value_from_ulongest (address_type, result);
-	    in_stack_memory = true;
+	    result_entry = fetch (0);
+
+	    auto registr
+	      = std::dynamic_pointer_cast<dwarf_register> (result_entry);
+
+	    if (registr != nullptr)
+	      result_entry
+		= registr->deref (frame, this->addr_info, address_type);
+
+	    result_entry = result_entry->to_location (this->gdbarch);
+	    auto memory
+	      = std::dynamic_pointer_cast<dwarf_memory> (result_entry);
+
+	    /* If we get anything else then memory location here,
+	       the DWARF standard defines the expression as ill formed.  */
+	    if (memory == nullptr)
+	      ill_formed_expression ();
+
+	    memory->add_bit_offset (offset * HOST_CHAR_BIT);
+	    memory->set_stack (true);
+	    result_entry = memory;
 
 	    /* Restore the content of the original stack.  */
 	    stack = std::move (saved_stack);
-
-	    this->location = DWARF_VALUE_MEMORY;
 	  }
 	  break;
 
 	case DW_OP_dup:
-	  result_val = fetch (0);
-	  in_stack_memory = fetch_in_stack_memory (0);
+	  result_entry = fetch (0);
 	  break;
 
 	case DW_OP_drop:
@@ -3777,8 +3710,7 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 
 	case DW_OP_pick:
 	  offset = *op_ptr++;
-	  result_val = fetch (offset);
-	  in_stack_memory = fetch_in_stack_memory (offset);
+	  result_entry = fetch (offset);
 	  break;
 	  
 	case DW_OP_swap:
@@ -3788,15 +3720,14 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 			"DW_OP_swap.  Need 2, have %zu."),
 		      stack.size ());
 
-	    dwarf_stack_value &t1 = stack[stack.size () - 1];
-	    dwarf_stack_value &t2 = stack[stack.size () - 2];
-	    std::swap (t1, t2);
+	    auto temp = stack[stack.size () - 1];
+	    stack[stack.size () - 1] = stack[stack.size () - 2];
+	    stack[stack.size () - 2] = temp;
 	    goto no_push;
 	  }
 
 	case DW_OP_over:
-	  result_val = fetch (1);
-	  in_stack_memory = fetch_in_stack_memory (1);
+	  result_entry = fetch (1);
 	  break;
 
 	case DW_OP_rot:
@@ -3806,7 +3737,7 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 			"DW_OP_rot.  Need 3, have %zu."),
 		      stack.size ());
 
-	    dwarf_stack_value temp = stack[stack.size () - 1];
+	    auto temp = stack[stack.size () - 1];
 	    stack[stack.size () - 1] = stack[stack.size () - 2];
 	    stack[stack.size () - 2] = stack[stack.size () - 3];
 	    stack[stack.size () - 3] = temp;
@@ -3819,38 +3750,22 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	case DW_OP_GNU_deref_type:
 	  {
 	    int addr_size = (op == DW_OP_deref ? this->addr_size : *op_ptr++);
-	    gdb_byte *buf = (gdb_byte *) alloca (addr_size);
-	    CORE_ADDR addr = fetch_address (0);
-	    struct type *type;
-
-	    pop ();
+	    struct type *type = address_type;
 
 	    if (op == DW_OP_deref_type || op == DW_OP_GNU_deref_type)
 	      {
 		op_ptr = safe_read_uleb128 (op_ptr, op_end, &uoffset);
 		cu_offset type_die_cu_off = (cu_offset) uoffset;
 		type = get_base_type (type_die_cu_off, 0);
-	      }
-	    else
-	      type = address_type;
-
-	    this->read_mem (buf, addr, addr_size);
-
-	    /* If the size of the object read from memory is different
-	       from the type length, we need to zero-extend it.  */
-	    if (TYPE_LENGTH (type) != addr_size)
-	      {
-		ULONGEST datum =
-		  extract_unsigned_integer (buf, addr_size, byte_order);
-
-		buf = (gdb_byte *) alloca (TYPE_LENGTH (type));
-		store_unsigned_integer (buf, TYPE_LENGTH (type),
-					byte_order, datum);
+		addr_size = TYPE_LENGTH (type);
 	      }
 
-	    result_val = value_from_contents_and_address (type, buf, addr);
-	    break;
+	    auto location = fetch (0)->to_location (this->gdbarch);
+	    result_entry = location->deref (frame, this->addr_info,
+					    type, addr_size);
+	    pop ();
 	  }
+	  break;
 
 	case DW_OP_abs:
 	case DW_OP_neg:
@@ -3858,31 +3773,37 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	case DW_OP_plus_uconst:
 	  {
 	    /* Unary operations.  */
-	    result_val = fetch (0);
+	    auto arg
+	      = fetch (0)->to_value (address_type);
 	    pop ();
 
 	    switch (op)
 	      {
 	      case DW_OP_abs:
-		if (value_less (result_val,
-				value_zero (value_type (result_val), not_lval)))
-		  result_val = value_neg (result_val);
+		{
+		  struct value *arg_value
+		    = arg->convert_to_gdb_value (arg->get_type ());
+
+		  if (value_less (arg_value,
+				  value_zero (arg->get_type (), not_lval)))
+		    arg = dwarf_value_negation_op (arg);
+		}
 		break;
 	      case DW_OP_neg:
-		result_val = value_neg (result_val);
+		arg = dwarf_value_negation_op (arg);
 		break;
 	      case DW_OP_not:
-		dwarf_require_integral (value_type (result_val));
-		result_val = value_complement (result_val);
+		dwarf_require_integral (arg->get_type ());
+		arg = dwarf_value_complement_op (arg);
 		break;
 	      case DW_OP_plus_uconst:
-		dwarf_require_integral (value_type (result_val));
-		result = value_as_long (result_val);
+		dwarf_require_integral (arg->get_type ());
 		op_ptr = safe_read_uleb128 (op_ptr, op_end, &reg);
-		result += reg;
-		result_val = value_from_ulongest (address_type, result);
+		result = arg->to_long () + reg;
+		arg = std::make_shared<dwarf_value> (result, address_type);
 		break;
 	      }
+	    result_entry = arg;
 	  }
 	  break;
 
@@ -3905,34 +3826,37 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	case DW_OP_ne:
 	  {
 	    /* Binary operations.  */
-	    struct value *first, *second;
-
-	    second = fetch (0);
+	    auto arg2 = fetch (0)->to_value (address_type);
 	    pop ();
 
-	    first = fetch (0);
+	    auto arg1 = fetch (0)->to_value (address_type);
 	    pop ();
 
-	    if (! base_types_equal_p (value_type (first), value_type (second)))
+	    if (! base_types_equal_p (arg1->get_type (), arg2->get_type ()))
 	      error (_("Incompatible types on DWARF stack"));
 
+	    std::shared_ptr<dwarf_value> op_result;
+
 	    switch (op)
 	      {
 	      case DW_OP_and:
-		dwarf_require_integral (value_type (first));
-		dwarf_require_integral (value_type (second));
-		result_val = value_binop (first, second, BINOP_BITWISE_AND);
+		dwarf_require_integral (arg1->get_type ());
+		dwarf_require_integral (arg2->get_type ());
+		op_result = dwarf_value_binary_op (arg1, arg2,
+						   BINOP_BITWISE_AND);
 		break;
 	      case DW_OP_div:
-		result_val = value_binop (first, second, BINOP_DIV);
+		op_result
+		  = dwarf_value_binary_op (arg1, arg2, BINOP_DIV);
 		break;
 	      case DW_OP_minus:
-		result_val = value_binop (first, second, BINOP_SUB);
+		op_result
+		  = dwarf_value_binary_op (arg1, arg2, BINOP_SUB);
 		break;
 	      case DW_OP_mod:
 		{
 		  int cast_back = 0;
-		  struct type *orig_type = value_type (first);
+		  struct type *orig_type = arg1->get_type ();
 
 		  /* We have to special-case "old-style" untyped values
 		     -- these must have mod computed using unsigned
@@ -3943,102 +3867,117 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 			= get_unsigned_type (this->gdbarch, orig_type);
 
 		      cast_back = 1;
-		      first = value_cast (utype, first);
-		      second = value_cast (utype, second);
+		      arg1 = dwarf_value_cast_op (arg1, utype);
+		      arg2 = dwarf_value_cast_op (arg2, utype);
 		    }
 		  /* Note that value_binop doesn't handle float or
 		     decimal float here.  This seems unimportant.  */
-		  result_val = value_binop (first, second, BINOP_MOD);
+		  op_result = dwarf_value_binary_op (arg1, arg2, BINOP_MOD);
 		  if (cast_back)
-		    result_val = value_cast (orig_type, result_val);
+		    op_result = dwarf_value_cast_op (op_result, orig_type);
 		}
 		break;
 	      case DW_OP_mul:
-		result_val = value_binop (first, second, BINOP_MUL);
+		op_result
+		  = dwarf_value_binary_op (arg1, arg2, BINOP_MUL);
 		break;
 	      case DW_OP_or:
-		dwarf_require_integral (value_type (first));
-		dwarf_require_integral (value_type (second));
-		result_val = value_binop (first, second, BINOP_BITWISE_IOR);
+		dwarf_require_integral (arg1->get_type ());
+		dwarf_require_integral (arg2->get_type ());
+		op_result
+		  = dwarf_value_binary_op (arg1, arg2, BINOP_BITWISE_IOR);
 		break;
 	      case DW_OP_plus:
-		result_val = value_binop (first, second, BINOP_ADD);
+		op_result
+		  = dwarf_value_binary_op (arg1, arg2, BINOP_ADD);
 		break;
 	      case DW_OP_shl:
-		dwarf_require_integral (value_type (first));
-		dwarf_require_integral (value_type (second));
-		result_val = value_binop (first, second, BINOP_LSH);
+		dwarf_require_integral (arg1->get_type ());
+		dwarf_require_integral (arg2->get_type ());
+		op_result
+		  = dwarf_value_binary_op (arg1, arg2, BINOP_LSH);
 		break;
 	      case DW_OP_shr:
-		dwarf_require_integral (value_type (first));
-		dwarf_require_integral (value_type (second));
-		if (!value_type (first)->is_unsigned ())
+		dwarf_require_integral (arg1->get_type ());
+		dwarf_require_integral (arg2->get_type ());
+		if (!arg1->get_type ()->is_unsigned ())
 		  {
 		    struct type *utype
-		      = get_unsigned_type (this->gdbarch, value_type (first));
+		      = get_unsigned_type (this->gdbarch, arg1->get_type ());
 
-		    first = value_cast (utype, first);
+		    arg1 = dwarf_value_cast_op (arg1, utype);
 		  }
 
-		result_val = value_binop (first, second, BINOP_RSH);
+		op_result
+		  = dwarf_value_binary_op (arg1, arg2, BINOP_RSH);
 		/* Make sure we wind up with the same type we started
 		   with.  */
-		if (value_type (result_val) != value_type (second))
-		  result_val = value_cast (value_type (second), result_val);
+		if (op_result->get_type () != arg2->get_type ())
+		  op_result
+		    = dwarf_value_cast_op (op_result, arg2->get_type ());
 		break;
 	      case DW_OP_shra:
-		dwarf_require_integral (value_type (first));
-		dwarf_require_integral (value_type (second));
-		if (value_type (first)->is_unsigned ())
+		dwarf_require_integral (arg1->get_type ());
+		dwarf_require_integral (arg2->get_type ());
+		if (arg1->get_type ()->is_unsigned ())
 		  {
 		    struct type *stype
-		      = get_signed_type (this->gdbarch, value_type (first));
+		      = get_signed_type (this->gdbarch, arg1->get_type ());
 
-		    first = value_cast (stype, first);
+		    arg1 = dwarf_value_cast_op (arg1, stype);
 		  }
 
-		result_val = value_binop (first, second, BINOP_RSH);
-		/* Make sure we wind up with the same type we started
-		   with.  */
-		if (value_type (result_val) != value_type (second))
-		  result_val = value_cast (value_type (second), result_val);
+		op_result
+		  = dwarf_value_binary_op (arg1, arg2, BINOP_RSH);
+		/* Make sure we wind up with the same type we started  with.  */
+		if (op_result->get_type () != arg2->get_type ())
+		  op_result
+		    = dwarf_value_cast_op (op_result, arg2->get_type ());
 		break;
 	      case DW_OP_xor:
-		dwarf_require_integral (value_type (first));
-		dwarf_require_integral (value_type (second));
-		result_val = value_binop (first, second, BINOP_BITWISE_XOR);
+		dwarf_require_integral (arg1->get_type ());
+		dwarf_require_integral (arg2->get_type ());
+		op_result
+		  = dwarf_value_binary_op (arg1, arg2, BINOP_BITWISE_XOR);
 		break;
 	      case DW_OP_le:
 		/* A <= B is !(B < A).  */
-		result = ! value_less (second, first);
-		result_val = value_from_ulongest (address_type, result);
+		result = ! dwarf_value_less_op (arg2, arg1);
+		op_result
+		  = std::make_shared<dwarf_value> (result, address_type);
 		break;
 	      case DW_OP_ge:
 		/* A >= B is !(A < B).  */
-		result = ! value_less (first, second);
-		result_val = value_from_ulongest (address_type, result);
+		result = ! dwarf_value_less_op (arg1, arg2);
+		op_result
+		  = std::make_shared<dwarf_value> (result, address_type);
 		break;
 	      case DW_OP_eq:
-		result = value_equal (first, second);
-		result_val = value_from_ulongest (address_type, result);
+		result = dwarf_value_equal_op (arg1, arg2);
+		op_result
+		  = std::make_shared<dwarf_value> (result, address_type);
 		break;
 	      case DW_OP_lt:
-		result = value_less (first, second);
-		result_val = value_from_ulongest (address_type, result);
+		result = dwarf_value_less_op (arg1, arg2);
+		op_result
+		  = std::make_shared<dwarf_value> (result, address_type);
 		break;
 	      case DW_OP_gt:
 		/* A > B is B < A.  */
-		result = value_less (second, first);
-		result_val = value_from_ulongest (address_type, result);
+		result = dwarf_value_less_op (arg2, arg1);
+		op_result
+		  = std::make_shared<dwarf_value> (result, address_type);
 		break;
 	      case DW_OP_ne:
-		result = ! value_equal (first, second);
-		result_val = value_from_ulongest (address_type, result);
+		result = ! dwarf_value_equal_op (arg1, arg2);
+		op_result
+		  = std::make_shared<dwarf_value> (result, address_type);
 		break;
 	      default:
 		internal_error (__FILE__, __LINE__,
 				_("Can't be reached."));
 	      }
+	    result_entry = op_result;
 	  }
 	  break;
 
@@ -4046,8 +3985,8 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	  ensure_have_frame (this->frame, "DW_OP_call_frame_cfa");
 
 	  result = dwarf2_frame_cfa (this->frame);
-	  result_val = value_from_ulongest (address_type, result);
-	  in_stack_memory = true;
+	  result_entry
+	    = std::make_shared<dwarf_memory> (this->gdbarch, result, 0, true);
 	  break;
 
 	case DW_OP_GNU_push_tls_address:
@@ -4060,11 +3999,12 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	  control block at which the variable is located.  Nothing
 	  should follow this operator, so the top of stack would be
 	  returned.  */
-	  result = value_as_long (fetch (0));
+	  result = fetch (0)->to_value (address_type)->to_long ();
 	  pop ();
 	  result = target_translate_tls_address (this->per_objfile->objfile,
 						 result);
-	  result_val = value_from_ulongest (address_type, result);
+	  result_entry
+	    = std::make_shared<dwarf_memory> (this->gdbarch, result);
 	  break;
 
 	case DW_OP_skip:
@@ -4075,13 +4015,12 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 
 	case DW_OP_bra:
 	  {
-	    struct value *val;
+	    auto dwarf_value = fetch (0)->to_value (address_type);
 
 	    offset = extract_signed_integer (op_ptr, 2, byte_order);
 	    op_ptr += 2;
-	    val = fetch (0);
-	    dwarf_require_integral (value_type (val));
-	    if (value_as_long (val) != 0)
+	    dwarf_require_integral (dwarf_value->get_type ());
+	    if (dwarf_value->to_long () != 0)
 	      op_ptr += offset;
 	    pop ();
 	  }
@@ -4096,16 +4035,9 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 
 	    /* Record the piece.  */
 	    op_ptr = safe_read_uleb128 (op_ptr, op_end, &size);
-	    add_piece (8 * size, 0);
-
-	    /* Pop off the address/regnum, and reset the location
-	       type.  */
-	    if (this->location != DWARF_VALUE_LITERAL
-		&& this->location != DWARF_VALUE_OPTIMIZED_OUT)
-	      pop ();
-	    this->location = DWARF_VALUE_MEMORY;
+	    result_entry = add_piece (HOST_CHAR_BIT * size, 0);
 	  }
-	  goto no_push;
+	  break;
 
 	case DW_OP_bit_piece:
 	  {
@@ -4114,23 +4046,24 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	    /* Record the piece.  */
 	    op_ptr = safe_read_uleb128 (op_ptr, op_end, &size);
 	    op_ptr = safe_read_uleb128 (op_ptr, op_end, &uleb_offset);
-	    add_piece (size, uleb_offset);
-
-	    /* Pop off the address/regnum, and reset the location
-	       type.  */
-	    if (this->location != DWARF_VALUE_LITERAL
-		&& this->location != DWARF_VALUE_OPTIMIZED_OUT)
-	      pop ();
-	    this->location = DWARF_VALUE_MEMORY;
+	    result_entry = add_piece (size, uleb_offset);
 	  }
-	  goto no_push;
+	  break;
 
 	case DW_OP_GNU_uninit:
-	  if (op_ptr != op_end)
-	    error (_("DWARF-2 expression error: DW_OP_GNU_uninit must always "
-		   "be the very last op."));
+	  {
+	    if (op_ptr != op_end)
+	      error (_("DWARF-2 expression error: DW_OP_GNU_uninit must always "
+		     "be the very last op."));
 
-	  this->initialized = 0;
+	    auto location = std::dynamic_pointer_cast<dwarf_location> (fetch (0));
+
+	    if (location == nullptr)
+	      ill_formed_expression ();
+
+	    location->set_initialised (false);
+	    result_entry = location;
+	  }
 	  goto no_push;
 
 	case DW_OP_call2:
@@ -4160,9 +4093,22 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 							this->ref_addr_size,
 							byte_order);
 	    op_ptr += this->ref_addr_size;
-	    result_val = sect_variable_value (sect_off, this->per_cu,
-					      this->per_objfile);
-	    result_val = value_cast (address_type, result_val);
+	    struct value *value
+	      = sect_variable_value (sect_off, per_cu, per_objfile);
+	    value = value_cast (address_type, value);
+
+	    result_entry
+	      = gdb_value_to_dwarf_entry (this->gdbarch, value);
+
+	    auto undefined
+	      = std::dynamic_pointer_cast<dwarf_undefined> (result_entry);
+
+	    if (undefined != nullptr)
+	      error_value_optimized_out ();
+
+	    auto location = result_entry->to_location (this->gdbarch);
+	    result_entry = location->deref (frame, this->addr_info,
+					    address_type);
 	  }
 	  break;
 	
@@ -4221,19 +4167,15 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	case DW_OP_const_type:
 	case DW_OP_GNU_const_type:
 	  {
-	    int n;
-	    const gdb_byte *data;
-	    struct type *type;
-
 	    op_ptr = safe_read_uleb128 (op_ptr, op_end, &uoffset);
 	    cu_offset type_die_cu_off = (cu_offset) uoffset;
 
-	    n = *op_ptr++;
-	    data = op_ptr;
+	    int n = *op_ptr++;
+	    const gdb_byte *data = op_ptr;
 	    op_ptr += n;
 
-	    type = get_base_type (type_die_cu_off, n);
-	    result_val = value_from_contents (type, data);
+	    struct type *type = get_base_type (type_die_cu_off, n);
+	    result_entry = std::make_shared<dwarf_value> (data, type);
 	  }
 	  break;
 
@@ -4245,12 +4187,11 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	    cu_offset type_die_cu_off = (cu_offset) uoffset;
 
 	    ensure_have_frame (this->frame, "DW_OP_regval_type");
-
 	    struct type *type = get_base_type (type_die_cu_off, 0);
-	    int regnum
-	      = dwarf_reg_to_regnum_or_error (get_frame_arch (this->frame),
-					      reg);
-	    result_val = value_from_register (type, regnum, this->frame);
+
+	    auto registr
+	      = std::make_shared<dwarf_register> (this->gdbarch, reg);
+	    result_entry = registr->deref (frame, this->addr_info, type);
 	  }
 	  break;
 
@@ -4259,42 +4200,45 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	case DW_OP_reinterpret:
 	case DW_OP_GNU_reinterpret:
 	  {
-	    struct type *type;
+	    std::shared_ptr<dwarf_value> value
+	      = fetch (0)->to_value (address_type);
+
+	    pop ();
 
 	    op_ptr = safe_read_uleb128 (op_ptr, op_end, &uoffset);
 	    cu_offset type_die_cu_off = (cu_offset) uoffset;
 
+	    struct type *type;
+
 	    if (to_underlying (type_die_cu_off) == 0)
 	      type = address_type;
 	    else
 	      type = get_base_type (type_die_cu_off, 0);
 
-	    result_val = fetch (0);
-	    pop ();
-
 	    if (op == DW_OP_convert || op == DW_OP_GNU_convert)
-	      result_val = value_cast (type, result_val);
-	    else if (type == value_type (result_val))
+	      value = dwarf_value_cast_op (value, type);
+	    else if (type == value->get_type ())
 	      {
 		/* Nothing.  */
 	      }
 	    else if (TYPE_LENGTH (type)
-		     != TYPE_LENGTH (value_type (result_val)))
+		     != TYPE_LENGTH (value->get_type ()))
 	      error (_("DW_OP_reinterpret has wrong size"));
 	    else
-	      result_val
-		= value_from_contents (type,
-				       value_contents_all (result_val));
+	      value
+		= std::make_shared<dwarf_value> (value->get_contents (), type);
+	    result_entry = value;
 	  }
 	  break;
 
 	case DW_OP_push_object_address:
-	  /* Return the address of the object we are currently observing.  */
 	  if (addr_info == nullptr)
 	    error (_("Location address is not set."));
 
-	  result_val
-	    = value_from_ulongest (address_type, this->addr_info->addr);
+	  /* Return the address of the object we are currently observing.  */
+	  result_entry
+	    = std::make_shared<dwarf_memory> (this->gdbarch,
+					      this->addr_info->addr);
 	  break;
 
 	default:
@@ -4302,18 +4246,12 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	}
 
       /* Most things push a result value.  */
-      gdb_assert (result_val != NULL);
-      push (result_val, in_stack_memory);
+      gdb_assert (result_entry != NULL);
+      push (result_entry);
     no_push:
       ;
     }
 
-  /* To simplify our main caller, if the result is an implicit
-     pointer, then make a pieced value.  This is ok because we can't
-     have implicit pointers in contexts where pieces are invalid.  */
-  if (this->location == DWARF_VALUE_IMPLICIT_POINTER)
-    add_piece (8 * this->addr_size, 0);
-
   this->recursion_depth--;
   gdb_assert (this->recursion_depth >= 0);
 }
diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
index 3f519e81849..2d24f4402db 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -25,6 +25,7 @@
 #include "leb128.h"
 #include "gdbtypes.h"
 
+class dwarf_entry;
 struct dwarf2_per_objfile;
 
 /* The location of a value.  */
@@ -98,22 +99,6 @@ struct dwarf_expr_piece
   ULONGEST offset;
 };
 
-/* The dwarf expression stack.  */
-
-struct dwarf_stack_value
-{
-  dwarf_stack_value (struct value *value_, int in_stack_memory_)
-  : value (value_), in_stack_memory (in_stack_memory_)
-  {}
-
-  struct value *value;
-
-  /* True if the piece is in memory and is known to be on the program's stack.
-     It is always ok to set this to zero.  This is used, for example, to
-     optimize memory access from the target.  It can vastly speed up backtraces
-     on long latency connections when "set stack-cache on".  */
-  bool in_stack_memory;
-};
 
 /* The expression evaluator works with a dwarf_expr_context, describing
    its current state and its callbacks.  */
@@ -127,7 +112,7 @@ struct dwarf_expr_context
 		      int addr_size);
   virtual ~dwarf_expr_context () = default;
 
-  void push_address (CORE_ADDR value, bool in_stack_memory);
+  void push_address (CORE_ADDR addr, bool in_stack_memory);
 
   /* Evaluate the expression at ADDR (LEN bytes long) in a given PER_CU
      FRAME context.  AS_LVAL defines if the returned struct value is
@@ -145,8 +130,8 @@ struct dwarf_expr_context
 			  LONGEST subobj_offset = 0);
 
 private:
-  /* The stack of values.  */
-  std::vector<dwarf_stack_value> stack;
+  /* The stack of DWARF entries.  */
+  std::vector<std::shared_ptr<dwarf_entry>> stack;
 
   /* Target architecture to use for address operations.  */
   struct gdbarch *gdbarch = nullptr;
@@ -163,43 +148,6 @@ struct dwarf_expr_context
      depth we'll tolerate before raising an error.  */
   int recursion_depth = 0, max_recursion_depth = 0x100;
 
-  /* Location of the value.  */
-  enum dwarf_value_location location = DWARF_VALUE_MEMORY;
-
-  /* For DWARF_VALUE_LITERAL, the current literal value's length and
-     data.  For DWARF_VALUE_IMPLICIT_POINTER, LEN is the offset of the
-     target DIE of sect_offset kind.  */
-  ULONGEST len = 0;
-  const gdb_byte *data = nullptr;
-
-  /* Initialization status of variable: Non-zero if variable has been
-     initialized; zero otherwise.  */
-  int initialized = 0;
-
-  /* A vector of pieces.
-
-     Each time DW_OP_piece is executed, we add a new element to the
-     end of this array, recording the current top of the stack, the
-     current location, and the size given as the operand to
-     DW_OP_piece.  We then pop the top value from the stack, reset the
-     location, and resume evaluation.
-
-     The Dwarf spec doesn't say whether DW_OP_piece pops the top value
-     from the stack.  We do, ensuring that clients of this interface
-     expecting to see a value left on the top of the stack (say, code
-     evaluating frame base expressions or CFA's specified with
-     DW_CFA_def_cfa_expression) will get an error if the expression
-     actually marks all the values it computes as pieces.
-
-     If an expression never uses DW_OP_piece, num_pieces will be zero.
-     (It would be nice to present these cases as expressions yielding
-     a single piece, so that callers need not distinguish between the
-     no-DW_OP_piece and one-DW_OP_piece cases.  But expressions with
-     no DW_OP_piece operations have no value to place in a piece's
-     'size' field; the size comes from the surrounding data.  So the
-     two cases need to be handled separately.)  */
-  std::vector<dwarf_expr_piece> pieces;
-
   /* We evaluate the expression in the context of this objfile.  */
   dwarf2_per_objfile *per_objfile;
 
@@ -214,14 +162,13 @@ struct dwarf_expr_context
 
   void eval (const gdb_byte *addr, size_t len);
   struct type *address_type () const;
-  void push (struct value *value, bool in_stack_memory);
+  void push (std::shared_ptr<dwarf_entry> value);
   bool stack_empty_p () const;
-  void add_piece (ULONGEST size, ULONGEST offset);
+  std::shared_ptr<dwarf_entry> add_piece (ULONGEST bit_size,
+					  ULONGEST bit_offset);
   void execute_stack_op (const gdb_byte *op_ptr, const gdb_byte *op_end);
   void pop ();
-  struct value *fetch (int n);
-  CORE_ADDR fetch_address (int n);
-  bool fetch_in_stack_memory (int n);
+  std::shared_ptr<dwarf_entry> fetch (int n);
 
   /* Fetch the result of the expression evaluation in a form of
      a struct value, where TYPE, SUBOBJ_TYPE and SUBOBJ_OFFSET
@@ -257,7 +204,6 @@ struct dwarf_expr_context
   void push_dwarf_reg_entry_value (enum call_site_parameter_kind kind,
 				   union call_site_parameter_u kind_u,
 				   int deref_size);
-  void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t length);
 };
 
 /* Return the value of register number REG (a DWARF register number),
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 33/43] Remove old computed struct value callbacks
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (31 preceding siblings ...)
  2021-03-01 14:46 ` [PATCH 32/43] Change DWARF stack to use new dwarf_entry classes Zoran Zaric
@ 2021-03-01 14:46 ` Zoran Zaric
  2021-03-01 14:46 ` [PATCH 34/43] Comments cleanup between expr.h and expr.c Zoran Zaric
                   ` (9 subsequent siblings)
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:46 UTC (permalink / raw)
  To: gdb-patches

After changing the DWARF stack to use the new DWARF entry based
classes, the previous computed struct value callback
infrastructure is not used anymore and can be removed.

gdb/ChangeLog:

        * dwarf2/expr.c (struct piece_closure): Remove structure.
        (rw_pieced_value): Remove unused function.
        (read_pieced_value): Remove unused function.
        (write_pieced_value): Remove unused function.
        (check_pieced_synthetic_pointer): Remove unused function.
        (indirect_pieced_value): Remove unused function.
        (coerce_pieced_ref): Remove unused function.
        (copy_pieced_value_closure): Remove unused function.
        (free_pieced_value_closure): Remove unused function.
        * dwarf2/expr.h (class dwarf_entry): New declaration.
        (struct dwarf_expr_piece): Remove structure.
        (enum dwarf_value_location): Remove enumeration.
---
 gdb/dwarf2/expr.c | 519 ----------------------------------------------
 gdb/dwarf2/expr.h |  72 -------
 2 files changed, 591 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index d51366007ab..e45a7755ec5 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -2194,525 +2194,6 @@ gdb_value_to_dwarf_entry (struct gdbarch *arch, struct value *value)
   }
 }
 
-struct piece_closure
-{
-  /* Reference count.  */
-  int refc = 0;
-
-  /* The objfile from which this closure's expression came.  */
-  dwarf2_per_objfile *per_objfile = nullptr;
-
-  /* The CU from which this closure's expression came.  */
-  struct dwarf2_per_cu_data *per_cu = NULL;
-
-  /* The pieces describing this variable.  */
-  std::vector<dwarf_expr_piece> pieces;
-
-  /* Frame ID of frame to which a register value is relative, used
-     only by DWARF_VALUE_REGISTER.  */
-  struct frame_id frame_id;
-};
-
-/* Read or write a pieced value V.  If FROM != NULL, operate in "write
-   mode": copy FROM into the pieces comprising V.  If FROM == NULL,
-   operate in "read mode": fetch the contents of the (lazy) value V by
-   composing it from its pieces.  */
-
-static void
-rw_pieced_value (struct value *v, struct value *from)
-{
-  int i;
-  LONGEST offset = 0, max_offset;
-  ULONGEST bits_to_skip;
-  gdb_byte *v_contents;
-  const gdb_byte *from_contents;
-  struct piece_closure *c
-    = (struct piece_closure *) value_computed_closure (v);
-  gdb::byte_vector buffer;
-  bool bits_big_endian = type_byte_order (value_type (v)) == BFD_ENDIAN_BIG;
-
-  if (from != NULL)
-    {
-      from_contents = value_contents (from);
-      v_contents = NULL;
-    }
-  else
-    {
-      if (value_type (v) != value_enclosing_type (v))
-	internal_error (__FILE__, __LINE__,
-			_("Should not be able to create a lazy value with "
-			  "an enclosing type"));
-      v_contents = value_contents_raw (v);
-      from_contents = NULL;
-    }
-
-  bits_to_skip = 8 * value_offset (v);
-  if (value_bitsize (v))
-    {
-      bits_to_skip += (8 * value_offset (value_parent (v))
-		       + value_bitpos (v));
-      if (from != NULL
-	  && (type_byte_order (value_type (from))
-	      == BFD_ENDIAN_BIG))
-	{
-	  /* Use the least significant bits of FROM.  */
-	  max_offset = 8 * TYPE_LENGTH (value_type (from));
-	  offset = max_offset - value_bitsize (v);
-	}
-      else
-	max_offset = value_bitsize (v);
-    }
-  else
-    max_offset = 8 * TYPE_LENGTH (value_type (v));
-
-  /* Advance to the first non-skipped piece.  */
-  for (i = 0; i < c->pieces.size () && bits_to_skip >= c->pieces[i].size; i++)
-    bits_to_skip -= c->pieces[i].size;
-
-  for (; i < c->pieces.size () && offset < max_offset; i++)
-    {
-      struct dwarf_expr_piece *p = &c->pieces[i];
-      size_t this_size_bits, this_size;
-
-      this_size_bits = p->size - bits_to_skip;
-      if (this_size_bits > max_offset - offset)
-	this_size_bits = max_offset - offset;
-
-      switch (p->location)
-	{
-	case DWARF_VALUE_REGISTER:
-	  {
-	    struct frame_info *frame = frame_find_by_id (c->frame_id);
-	    struct gdbarch *arch = get_frame_arch (frame);
-	    int gdb_regnum = dwarf_reg_to_regnum_or_error (arch, p->v.regno);
-	    ULONGEST reg_bits = 8 * register_size (arch, gdb_regnum);
-	    int optim, unavail;
-
-	    if (gdbarch_byte_order (arch) == BFD_ENDIAN_BIG
-		&& p->offset + p->size < reg_bits)
-	      {
-		/* Big-endian, and we want less than full size.  */
-		bits_to_skip += reg_bits - (p->offset + p->size);
-	      }
-	    else
-	      bits_to_skip += p->offset;
-
-	    this_size = bits_to_bytes (bits_to_skip, this_size_bits);
-	    buffer.resize (this_size);
-
-	    if (from == NULL)
-	      {
-		/* Read mode.  */
-		read_from_register (frame, gdb_regnum, bits_to_skip / 8,
-				    buffer, &optim, &unavail);
-
-		if (optim)
-		  mark_value_bits_optimized_out (v, offset, this_size_bits);
-		if (unavail)
-		  mark_value_bits_unavailable (v, offset, this_size_bits);
-		/* Only copy data if valid.  */
-		if (!optim && !unavail)
-		  copy_bitwise (v_contents, offset,
-				buffer.data (), bits_to_skip % 8,
-				this_size_bits, bits_big_endian);
-	      }
-	    else
-	      {
-		/* Write mode.  */
-		if (bits_to_skip % 8 != 0 || this_size_bits % 8 != 0)
-		  {
-		    /* Data is copied non-byte-aligned into the register.
-		       Need some bits from original register value.  */
-		    read_from_register (frame, gdb_regnum, bits_to_skip / 8,
-					buffer, &optim, &unavail);
-		    if (optim)
-		      throw_error (OPTIMIZED_OUT_ERROR,
-				   _("Can't do read-modify-write to "
-				     "update bitfield; containing word "
-				     "has been optimized out"));
-		    if (unavail)
-		      throw_error (NOT_AVAILABLE_ERROR,
-				   _("Can't do read-modify-write to "
-				     "update bitfield; containing word "
-				     "is unavailable"));
-		  }
-
-		copy_bitwise (buffer.data (), bits_to_skip % 8,
-			      from_contents, offset,
-			      this_size_bits, bits_big_endian);
-		write_to_register (frame, gdb_regnum, bits_to_skip / 8,
-				   buffer, &optim, &unavail);
-	      }
-	  }
-	  break;
-
-	case DWARF_VALUE_MEMORY:
-	  {
-	    bits_to_skip += p->offset;
-
-	    CORE_ADDR start_addr = p->v.mem.addr + bits_to_skip / 8;
-	    bool in_stack_memory = p->v.mem.in_stack_memory;
-	    int unavail = 0;
-
-	    if (bits_to_skip % 8 == 0 && this_size_bits % 8 == 0
-		&& offset % 8 == 0)
-	      {
-		/* Everything is byte-aligned; no buffer needed.  */
-		if (from != NULL)
-		  write_to_memory (start_addr, (from_contents + offset / 8),
-				   this_size_bits / 8, in_stack_memory,
-				   &unavail);
-		else
-		  read_from_memory (start_addr, (v_contents + offset / 8),
-				    this_size_bits / 8, in_stack_memory,
-				    &unavail);
-	      }
-	    else
-	      {
-		this_size = bits_to_bytes (bits_to_skip, this_size_bits);
-		buffer.resize (this_size);
-
-		if (from == NULL)
-		  {
-		    /* Read mode.  */
-		    read_from_memory (start_addr, buffer.data (),
-				      this_size, in_stack_memory,
-				      &unavail);
-		    if (!unavail)
-		      copy_bitwise (v_contents, offset,
-				    buffer.data (), bits_to_skip % 8,
-				    this_size_bits, bits_big_endian);
-		  }
-		else
-		  {
-		    /* Write mode.  */
-		    if (bits_to_skip % 8 != 0 || this_size_bits % 8 != 0)
-		      {
-			if (this_size <= 8)
-			  {
-			    /* Perform a single read for small sizes.  */
-			    read_from_memory (start_addr, buffer.data (),
-					      this_size, in_stack_memory,
-					      &unavail);
-			  }
-			else
-			  {
-			    /* Only the first and last bytes can possibly have
-			       any bits reused.  */
-			    read_from_memory (start_addr, buffer.data (),
-					      1, in_stack_memory,
-					      &unavail);
-			    if (!unavail)
-			      read_from_memory (start_addr + this_size - 1,
-						&buffer[this_size - 1], 1,
-						in_stack_memory, &unavail);
-			  }
-		      }
-
-		    if (!unavail)
-		      {
-			copy_bitwise (buffer.data (), bits_to_skip % 8,
-				      from_contents, offset,
-				      this_size_bits, bits_big_endian);
-			write_to_memory (start_addr, buffer.data (),
-					 this_size, in_stack_memory,
-					 &unavail);
-		      }
-		  }
-	      }
-
-	    if (unavail)
-	      {
-		if (from == NULL)
-		  mark_value_bits_unavailable (v, (offset + bits_to_skip % 8),
-					       this_size_bits);
-		else
-		  throw_error (NOT_AVAILABLE_ERROR,
-			       _("Can't do read-modify-write to "
-				 "update bitfield; containing word "
-				 "is unavailable"));
-	      }
-	  }
-	  break;
-
-	case DWARF_VALUE_STACK:
-	  {
-	    if (from != NULL)
-	      {
-		mark_value_bits_optimized_out (v, offset, this_size_bits);
-		break;
-	      }
-
-	    gdbarch *objfile_gdbarch = c->per_objfile->objfile->arch ();
-	    ULONGEST stack_value_size_bits
-	      = 8 * TYPE_LENGTH (value_type (p->v.value));
-
-	    /* Use zeroes if piece reaches beyond stack value.  */
-	    if (p->offset + p->size > stack_value_size_bits)
-	      break;
-
-	    /* Piece is anchored at least significant bit end.  */
-	    if (gdbarch_byte_order (objfile_gdbarch) == BFD_ENDIAN_BIG)
-	      bits_to_skip += stack_value_size_bits - p->offset - p->size;
-	    else
-	      bits_to_skip += p->offset;
-
-	    copy_bitwise (v_contents, offset,
-			  value_contents_all (p->v.value),
-			  bits_to_skip,
-			  this_size_bits, bits_big_endian);
-	  }
-	  break;
-
-	case DWARF_VALUE_LITERAL:
-	  {
-	    if (from != NULL)
-	      {
-		mark_value_bits_optimized_out (v, offset, this_size_bits);
-		break;
-	      }
-
-	    ULONGEST literal_size_bits = 8 * p->v.literal.length;
-	    size_t n = this_size_bits;
-
-	    /* Cut off at the end of the implicit value.  */
-	    bits_to_skip += p->offset;
-	    if (bits_to_skip >= literal_size_bits)
-	      break;
-	    if (n > literal_size_bits - bits_to_skip)
-	      n = literal_size_bits - bits_to_skip;
-
-	    copy_bitwise (v_contents, offset,
-			  p->v.literal.data, bits_to_skip,
-			  n, bits_big_endian);
-	  }
-	  break;
-
-	case DWARF_VALUE_IMPLICIT_POINTER:
-	    if (from != NULL)
-	      {
-		mark_value_bits_optimized_out (v, offset, this_size_bits);
-		break;
-	      }
-
-	  /* These bits show up as zeros -- but do not cause the value to
-	     be considered optimized-out.  */
-	  break;
-
-	case DWARF_VALUE_OPTIMIZED_OUT:
-	  mark_value_bits_optimized_out (v, offset, this_size_bits);
-	  break;
-
-	default:
-	  internal_error (__FILE__, __LINE__, _("invalid location type"));
-	}
-
-      offset += this_size_bits;
-      bits_to_skip = 0;
-    }
-}
-
-static void
-read_pieced_value (struct value *v)
-{
-  rw_pieced_value (v, NULL);
-}
-
-static void
-write_pieced_value (struct value *to, struct value *from)
-{
-  rw_pieced_value (to, from);
-}
-
-/* An implementation of an lval_funcs method to see whether a value is
-   a synthetic pointer.  */
-
-static int
-check_pieced_synthetic_pointer (const struct value *value, LONGEST bit_offset,
-				int bit_length)
-{
-  struct piece_closure *c
-    = (struct piece_closure *) value_computed_closure (value);
-  int i;
-
-  bit_offset += 8 * value_offset (value);
-  if (value_bitsize (value))
-    bit_offset += value_bitpos (value);
-
-  for (i = 0; i < c->pieces.size () && bit_length > 0; i++)
-    {
-      struct dwarf_expr_piece *p = &c->pieces[i];
-      size_t this_size_bits = p->size;
-
-      if (bit_offset > 0)
-	{
-	  if (bit_offset >= this_size_bits)
-	    {
-	      bit_offset -= this_size_bits;
-	      continue;
-	    }
-
-	  bit_length -= this_size_bits - bit_offset;
-	  bit_offset = 0;
-	}
-      else
-	bit_length -= this_size_bits;
-
-      if (p->location != DWARF_VALUE_IMPLICIT_POINTER)
-	return 0;
-    }
-
-  return 1;
-}
-
-/* An implementation of an lval_funcs method to indirect through a
-   pointer.  This handles the synthetic pointer case when needed.  */
-
-static struct value *
-indirect_pieced_value (struct value *value)
-{
-  struct piece_closure *c
-    = (struct piece_closure *) value_computed_closure (value);
-  struct type *type;
-  struct frame_info *frame;
-  int i, bit_length;
-  LONGEST bit_offset;
-  struct dwarf_expr_piece *piece = NULL;
-  LONGEST byte_offset;
-  enum bfd_endian byte_order;
-
-  type = check_typedef (value_type (value));
-  if (type->code () != TYPE_CODE_PTR)
-    return NULL;
-
-  bit_length = 8 * TYPE_LENGTH (type);
-  bit_offset = 8 * value_offset (value);
-  if (value_bitsize (value))
-    bit_offset += value_bitpos (value);
-
-  for (i = 0; i < c->pieces.size () && bit_length > 0; i++)
-    {
-      struct dwarf_expr_piece *p = &c->pieces[i];
-      size_t this_size_bits = p->size;
-
-      if (bit_offset > 0)
-	{
-	  if (bit_offset >= this_size_bits)
-	    {
-	      bit_offset -= this_size_bits;
-	      continue;
-	    }
-
-	  bit_length -= this_size_bits - bit_offset;
-	  bit_offset = 0;
-	}
-      else
-	bit_length -= this_size_bits;
-
-      if (p->location != DWARF_VALUE_IMPLICIT_POINTER)
-	return NULL;
-
-      if (bit_length != 0)
-	error (_("Invalid use of DW_OP_implicit_pointer"));
-
-      piece = p;
-      break;
-    }
-
-  gdb_assert (piece != NULL && c->per_cu != nullptr);
-  frame = get_selected_frame (_("No frame selected."));
-
-  /* This is an offset requested by GDB, such as value subscripts.
-     However, due to how synthetic pointers are implemented, this is
-     always presented to us as a pointer type.  This means we have to
-     sign-extend it manually as appropriate.  Use raw
-     extract_signed_integer directly rather than value_as_address and
-     sign extend afterwards on architectures that would need it
-     (mostly everywhere except MIPS, which has signed addresses) as
-     the later would go through gdbarch_pointer_to_address and thus
-     return a CORE_ADDR with high bits set on architectures that
-     encode address spaces and other things in CORE_ADDR.  */
-  byte_order = gdbarch_byte_order (get_frame_arch (frame));
-  byte_offset = extract_signed_integer (value_contents (value),
-					TYPE_LENGTH (type), byte_order);
-  byte_offset += piece->v.ptr.offset;
-
-  return indirect_synthetic_pointer (piece->v.ptr.die_sect_off,
-				     byte_offset, c->per_cu,
-				     c->per_objfile, frame, type);
-}
-
-/* Implementation of the coerce_ref method of lval_funcs for synthetic C++
-   references.  */
-
-static struct value *
-coerce_pieced_ref (const struct value *value)
-{
-  struct type *type = check_typedef (value_type (value));
-
-  if (value_bits_synthetic_pointer (value, value_embedded_offset (value),
-				    TARGET_CHAR_BIT * TYPE_LENGTH (type)))
-    {
-      const struct piece_closure *closure
-	= (struct piece_closure *) value_computed_closure (value);
-      struct frame_info *frame
-	= get_selected_frame (_("No frame selected."));
-
-      /* gdb represents synthetic pointers as pieced values with a single
-	 piece.  */
-      gdb_assert (closure != NULL);
-      gdb_assert (closure->pieces.size () == 1);
-
-      return indirect_synthetic_pointer
-	(closure->pieces[0].v.ptr.die_sect_off,
-	 closure->pieces[0].v.ptr.offset,
-	 closure->per_cu, closure->per_objfile, frame, type);
-    }
-  else
-    {
-      /* Else: not a synthetic reference; do nothing.  */
-      return NULL;
-    }
-}
-
-static void *
-copy_pieced_value_closure (const struct value *v)
-{
-  struct piece_closure *c
-    = (struct piece_closure *) value_computed_closure (v);
-
-  ++c->refc;
-  return c;
-}
-
-static void
-free_pieced_value_closure (struct value *v)
-{
-  struct piece_closure *c
-    = (struct piece_closure *) value_computed_closure (v);
-
-  --c->refc;
-  if (c->refc == 0)
-    {
-      for (dwarf_expr_piece &p : c->pieces)
-	if (p.location == DWARF_VALUE_STACK)
-	  value_decref (p.v.value);
-
-      delete c;
-    }
-}
-
-/* Functions for accessing a variable described by DW_OP_piece.  */
-static const struct lval_funcs pieced_value_funcs = {
-  read_pieced_value,
-  write_pieced_value,
-  indirect_pieced_value,
-  coerce_pieced_ref,
-  check_pieced_synthetic_pointer,
-  copy_pieced_value_closure,
-  free_pieced_value_closure
-};
-
 /* Given context CTX, section offset SECT_OFF, and compilation unit
    data PER_CU, execute the "variable value" operation on the DIE
    found at SECT_OFF.  */
diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
index 2d24f4402db..3c404d8f83c 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -28,78 +28,6 @@
 class dwarf_entry;
 struct dwarf2_per_objfile;
 
-/* The location of a value.  */
-enum dwarf_value_location
-{
-  /* The piece is in memory.
-     The value on the dwarf stack is its address.  */
-  DWARF_VALUE_MEMORY,
-
-  /* The piece is in a register.
-     The value on the dwarf stack is the register number.  */
-  DWARF_VALUE_REGISTER,
-
-  /* The piece is on the dwarf stack.  */
-  DWARF_VALUE_STACK,
-
-  /* The piece is a literal.  */
-  DWARF_VALUE_LITERAL,
-
-  /* The piece was optimized out.  */
-  DWARF_VALUE_OPTIMIZED_OUT,
-
-  /* The piece is an implicit pointer.  */
-  DWARF_VALUE_IMPLICIT_POINTER
-};
-
-/* A piece of an object, as recorded by DW_OP_piece or DW_OP_bit_piece.  */
-struct dwarf_expr_piece
-{
-  enum dwarf_value_location location;
-
-  union
-  {
-    struct
-    {
-      /* This piece's address, for DWARF_VALUE_MEMORY pieces.  */
-      CORE_ADDR addr;
-      /* Non-zero if the piece is known to be in memory and on
-	 the program's stack.  */
-      bool in_stack_memory;
-    } mem;
-
-    /* The piece's register number, for DWARF_VALUE_REGISTER pieces.  */
-    int regno;
-
-    /* The piece's literal value, for DWARF_VALUE_STACK pieces.  */
-    struct value *value;
-
-    struct
-    {
-      /* A pointer to the data making up this piece,
-	 for DWARF_VALUE_LITERAL pieces.  */
-      const gdb_byte *data;
-      /* The length of the available data.  */
-      ULONGEST length;
-    } literal;
-
-    /* Used for DWARF_VALUE_IMPLICIT_POINTER.  */
-    struct
-    {
-      /* The referent DIE from DW_OP_implicit_pointer.  */
-      sect_offset die_sect_off;
-      /* The byte offset into the resulting data.  */
-      LONGEST offset;
-    } ptr;
-  } v;
-
-  /* The length of the piece, in bits.  */
-  ULONGEST size;
-  /* The piece offset, in bits.  */
-  ULONGEST offset;
-};
-
-
 /* The expression evaluator works with a dwarf_expr_context, describing
    its current state and its callbacks.  */
 struct dwarf_expr_context
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 34/43] Comments cleanup between expr.h and expr.c
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (32 preceding siblings ...)
  2021-03-01 14:46 ` [PATCH 33/43] Remove old computed struct value callbacks Zoran Zaric
@ 2021-03-01 14:46 ` Zoran Zaric
  2021-03-01 14:46 ` [PATCH 35/43] Remove dwarf_expr_context from expr.h interface Zoran Zaric
                   ` (8 subsequent siblings)
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:46 UTC (permalink / raw)
  To: gdb-patches

This is just a cosmetic cleanup of the expr.h and expr.c documentation.

gdb/ChangeLog:

        * dwarf2/expr.c: Comments cleanup.
        * dwarf2/expr.h: Comments cleanup.
---
 gdb/dwarf2/expr.c | 42 ++++++++++++++++------------------------
 gdb/dwarf2/expr.h | 49 +++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 64 insertions(+), 27 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index e45a7755ec5..aa28d632442 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -2241,7 +2241,7 @@ dwarf_expr_context::dwarf_expr_context (dwarf2_per_objfile *per_objfile,
 {
 }
 
-/* Push ENTRY onto the stack.  */
+/* See expr.h.  */
 
 void
 dwarf_expr_context::push (std::shared_ptr<dwarf_entry> entry)
@@ -2249,7 +2249,7 @@ dwarf_expr_context::push (std::shared_ptr<dwarf_entry> entry)
   stack.emplace_back (entry);
 }
 
-/* Push ADDR onto the stack.  */
+/* See expr.h.  */
 
 void
 dwarf_expr_context::push_address (CORE_ADDR addr, bool in_stack_memory)
@@ -2259,7 +2259,7 @@ dwarf_expr_context::push_address (CORE_ADDR addr, bool in_stack_memory)
 }
 
 
-/* Pop the top item off of the stack.  */
+/* See expr.h.  */
 
 void
 dwarf_expr_context::pop ()
@@ -2270,7 +2270,7 @@ dwarf_expr_context::pop ()
   stack.pop_back ();
 }
 
-/* Retrieve the N'th item on the stack.  */
+/* See expr.h.  */
 
 std::shared_ptr<dwarf_entry>
 dwarf_expr_context::fetch (int n)
@@ -2506,7 +2506,7 @@ get_signed_type (struct gdbarch *gdbarch, struct type *type)
     }
 }
 
-/* Return true if the expression stack is empty.  */
+/* See expr.h.  */
 
 bool
 dwarf_expr_context::stack_empty_p () const
@@ -2514,7 +2514,7 @@ dwarf_expr_context::stack_empty_p () const
   return stack.empty ();
 }
 
-/* Add a new piece to the composite on top of the stack.  */
+/* See expr.h.  */
 
 std::shared_ptr<dwarf_entry>
 dwarf_expr_context::add_piece (ULONGEST bit_size, ULONGEST bit_offset)
@@ -2550,7 +2550,7 @@ dwarf_expr_context::add_piece (ULONGEST bit_size, ULONGEST bit_offset)
 }
 
 
-/* Evaluate the expression at ADDR (LEN bytes long).  */
+/* See expr.h.  */
 
 void
 dwarf_expr_context::eval (const gdb_byte *addr, size_t len)
@@ -2564,7 +2564,7 @@ dwarf_expr_context::eval (const gdb_byte *addr, size_t len)
   gdb_assert (this->recursion_depth == old_recursion_depth);
 }
 
-/* Helper to read a uleb128 value or throw an error.  */
+/* See expr.h.  */
 
 const gdb_byte *
 safe_read_uleb128 (const gdb_byte *buf, const gdb_byte *buf_end,
@@ -2576,7 +2576,7 @@ safe_read_uleb128 (const gdb_byte *buf, const gdb_byte *buf_end,
   return buf;
 }
 
-/* Helper to read a sleb128 value or throw an error.  */
+/* See expr.h.  */
 
 const gdb_byte *
 safe_read_sleb128 (const gdb_byte *buf, const gdb_byte *buf_end,
@@ -2588,6 +2588,8 @@ safe_read_sleb128 (const gdb_byte *buf, const gdb_byte *buf_end,
   return buf;
 }
 
+/* See expr.h.  */
+
 const gdb_byte *
 safe_skip_leb128 (const gdb_byte *buf, const gdb_byte *buf_end)
 {
@@ -2597,9 +2599,7 @@ safe_skip_leb128 (const gdb_byte *buf, const gdb_byte *buf_end)
   return buf;
 }
 
-/* Check that the current operator is either at the end of an
-   expression, or that it is followed by a composition operator or by
-   DW_OP_GNU_uninit (which should terminate the expression).  */
+/* See expr.h.  */
 
 void
 dwarf_expr_require_composition (const gdb_byte *op_ptr, const gdb_byte *op_end,
@@ -2627,8 +2627,7 @@ base_types_equal_p (struct type *t1, struct type *t2)
   return TYPE_LENGTH (t1) == TYPE_LENGTH (t2);
 }
 
-/* If <BUF..BUF_END] contains DW_FORM_block* with single DW_OP_reg* return the
-   DWARF register number.  Otherwise return -1.  */
+/* See expr.h.  */
 
 int
 dwarf_block_to_dwarf_reg (const gdb_byte *buf, const gdb_byte *buf_end)
@@ -2668,10 +2667,7 @@ dwarf_block_to_dwarf_reg (const gdb_byte *buf, const gdb_byte *buf_end)
   return dwarf_reg;
 }
 
-/* If <BUF..BUF_END] contains DW_FORM_block* with just DW_OP_breg*(0) and
-   DW_OP_deref* return the DWARF register number.  Otherwise return -1.
-   DEREF_SIZE_RETURN contains -1 for DW_OP_deref; otherwise it contains the
-   size from DW_OP_deref_size.  */
+/* See expr.h.  */
 
 int
 dwarf_block_to_dwarf_reg_deref (const gdb_byte *buf, const gdb_byte *buf_end,
@@ -2729,8 +2725,7 @@ dwarf_block_to_dwarf_reg_deref (const gdb_byte *buf, const gdb_byte *buf_end,
   return dwarf_reg;
 }
 
-/* If <BUF..BUF_END] contains DW_FORM_block* with single DW_OP_fbreg(X) fill
-   in FB_OFFSET_RETURN with the X offset and return 1.  Otherwise return 0.  */
+/* See expr.h.  */
 
 int
 dwarf_block_to_fb_offset (const gdb_byte *buf, const gdb_byte *buf_end,
@@ -2755,9 +2750,7 @@ dwarf_block_to_fb_offset (const gdb_byte *buf, const gdb_byte *buf_end,
   return 1;
 }
 
-/* If <BUF..BUF_END] contains DW_FORM_block* with single DW_OP_bregSP(X) fill
-   in SP_OFFSET_RETURN with the X offset and return 1.  Otherwise return 0.
-   The matched SP register number depends on GDBARCH.  */
+/* See expr.h.  */
 
 int
 dwarf_block_to_sp_offset (struct gdbarch *gdbarch, const gdb_byte *buf,
@@ -2797,8 +2790,7 @@ dwarf_block_to_sp_offset (struct gdbarch *gdbarch, const gdb_byte *buf,
   return 1;
 }
 
-/* The engine for the expression evaluator.  Using the context in this
-   object, evaluate the expression between OP_PTR and OP_END.  */
+/* See expr.h.  */
 
 void
 dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
index 3c404d8f83c..5170ac2df43 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -32,7 +32,9 @@ struct dwarf2_per_objfile;
    its current state and its callbacks.  */
 struct dwarf_expr_context
 {
-  /* We should ever only pass in the PER_OBJFILE, while the ADDR_SIZE
+  /* Create a new context for the expression evaluator.
+
+     We should ever only pass in the PER_OBJFILE and the ADDR_SIZE
      information should be retrievable from there.  The PER_OBJFILE
      contains a pointer to the PER_BFD information anyway and the
      address size information must be the same for the whole BFD.  */
@@ -40,6 +42,7 @@ struct dwarf_expr_context
 		      int addr_size);
   virtual ~dwarf_expr_context () = default;
 
+  /* Push ADDR onto the stack.  */
   void push_address (CORE_ADDR addr, bool in_stack_memory);
 
   /* Evaluate the expression at ADDR (LEN bytes long) in a given PER_CU
@@ -88,14 +91,39 @@ struct dwarf_expr_context
   /* Property address info used for the evaluation.  */
   const struct property_addr_info *addr_info = nullptr;
 
+  /* Evaluate the expression at ADDR (LEN bytes long).  */
   void eval (const gdb_byte *addr, size_t len);
+
+  /* Return the type used for DWARF operations where the type is
+     unspecified in the DWARF spec.  Only certain sizes are
+     supported.  */
   struct type *address_type () const;
-  void push (std::shared_ptr<dwarf_entry> value);
+
+  /* Push ENTRY onto the stack.  */
+  void push (std::shared_ptr<dwarf_entry> entry);
+
+  /* Return true if the expression stack is empty.  */
   bool stack_empty_p () const;
+
+  /* Pop a top element of the stack and add as a composite piece
+     with an BIT_OFFSET offset and of a BIT_SIZE size.
+
+     If the following top element of the stack is a composite
+     location description, the piece will be added to it.  Otherwise
+     a new composite location description will be created and
+     the piece will be added to that composite.  */
   std::shared_ptr<dwarf_entry> add_piece (ULONGEST bit_size,
 					  ULONGEST bit_offset);
+
+  /* The engine for the expression evaluator.  Using the context in
+      this object, evaluate the expression between OP_PTR and
+      OP_END.  */
   void execute_stack_op (const gdb_byte *op_ptr, const gdb_byte *op_end);
+
+  /* Pop the top item off of the stack.  */
   void pop ();
+
+  /* Retrieve the N'th item on the stack.  */
   std::shared_ptr<dwarf_entry> fetch (int n);
 
   /* Fetch the result of the expression evaluation in a form of
@@ -138,18 +166,32 @@ struct dwarf_expr_context
    read as an address in a given FRAME.  */
 CORE_ADDR read_addr_from_reg (struct frame_info *, int);
 
+/* Check that the current operator is either at the end of an
+   expression, or that it is followed by a composition operator or by
+   DW_OP_GNU_uninit (which should terminate the expression).  */
 void dwarf_expr_require_composition (const gdb_byte *, const gdb_byte *,
 				     const char *);
 
+/* If <BUF..BUF_END] contains DW_FORM_block* with single DW_OP_reg* return the
+   DWARF register number.  Otherwise return -1.  */
 int dwarf_block_to_dwarf_reg (const gdb_byte *buf, const gdb_byte *buf_end);
 
+/* If <BUF..BUF_END] contains DW_FORM_block* with just DW_OP_breg*(0) and
+   DW_OP_deref* return the DWARF register number.  Otherwise return -1.
+   DEREF_SIZE_RETURN contains -1 for DW_OP_deref; otherwise it contains the
+   size from DW_OP_deref_size.  */
 int dwarf_block_to_dwarf_reg_deref (const gdb_byte *buf,
 				    const gdb_byte *buf_end,
 				    CORE_ADDR *deref_size_return);
 
+/* If <BUF..BUF_END] contains DW_FORM_block* with single DW_OP_fbreg(X) fill
+   in FB_OFFSET_RETURN with the X offset and return 1.  Otherwise return 0.  */
 int dwarf_block_to_fb_offset (const gdb_byte *buf, const gdb_byte *buf_end,
 			      CORE_ADDR *fb_offset_return);
 
+/* If <BUF..BUF_END] contains DW_FORM_block* with single DW_OP_bregSP(X) fill
+   in SP_OFFSET_RETURN with the X offset and return 1.  Otherwise return 0.
+   The matched SP register number depends on GDBARCH.  */
 int dwarf_block_to_sp_offset (struct gdbarch *gdbarch, const gdb_byte *buf,
 			      const gdb_byte *buf_end,
 			      CORE_ADDR *sp_offset_return);
@@ -189,14 +231,17 @@ gdb_skip_leb128 (const gdb_byte *buf, const gdb_byte *buf_end)
   return buf + bytes_read;
 }
 
+/* Helper to read a uleb128 value or throw an error.  */
 extern const gdb_byte *safe_read_uleb128 (const gdb_byte *buf,
 					  const gdb_byte *buf_end,
 					  uint64_t *r);
 
+/* Helper to read a uleb128 value or throw an error.  */
 extern const gdb_byte *safe_read_sleb128 (const gdb_byte *buf,
 					  const gdb_byte *buf_end,
 					  int64_t *r);
 
+/* Helper to skip a leb128 value or throw an error.  */
 extern const gdb_byte *safe_skip_leb128 (const gdb_byte *buf,
 					 const gdb_byte *buf_end);
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 35/43] Remove dwarf_expr_context from expr.h interface
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (33 preceding siblings ...)
  2021-03-01 14:46 ` [PATCH 34/43] Comments cleanup between expr.h and expr.c Zoran Zaric
@ 2021-03-01 14:46 ` Zoran Zaric
  2021-03-01 14:46 ` [PATCH 36/43] Move read_addr_from_reg function to frame.c Zoran Zaric
                   ` (7 subsequent siblings)
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:46 UTC (permalink / raw)
  To: gdb-patches

After the switch to the new evaluator implementation, it is now
possible to completely remove the dwarf_expr_context class from the
expr.h interface and encapsulate it inside the expr.c file.

The new interface consists of a new function called dwarf2_evaluate
that takes a DWARF expression stream, initial DWARF stack elements (in
a form of a vector of a struct value objects), evaluation context and
expected result type information. Function returns an evaluation result
in a form of a struct value object.

Currently, there is ever only one initial stack element provided to the
evaluator and that element is always a memory address, so having a
vector of struct value object might seems like an overkill.

In reality this new flexibility allows implementation of a new DWARF
attribute extensions that could provide any number of initial stack
elements to describe any location description or value.

gdb/ChangeLog:

	* dwarf2/expr.c (dwarf2_evaluate): New function.
	(struct dwarf_expr_context): Move from expr.h.
	(dwarf_expr_context::push_address): Remove function.
	* dwarf2/expr.h (struct dwarf_expr_context): Move to expr.c.
        (address_type): Expose function.
	* dwarf2/frame.c (execute_stack_op): Now calls dwarf2_evaluate.
	* dwarf2/loc.c (dwarf2_evaluate_loc_desc_full): Now calls
	dwarf2_evaluate.
	(dwarf2_locexpr_baton_eval): Now calls dwarf2_evaluate.
---
 gdb/dwarf2/expr.c  | 199 +++++++++++++++++++++++++++++++++++----------
 gdb/dwarf2/expr.h  | 157 ++++++-----------------------------
 gdb/dwarf2/frame.c |  14 +++-
 gdb/dwarf2/loc.c   |  23 ++++--
 4 files changed, 205 insertions(+), 188 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index aa28d632442..7a091e74ce5 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -285,12 +285,9 @@ write_to_memory (CORE_ADDR address, const gdb_byte *buffer,
 					 length, buffer);
 }
 
-/* Return the type used for DWARF operations where the type is
-   generic in the DWARF spec, the ARCH is a target architecture.
-   of the type and ADDR_SIZE is expected size of the address.
-   Only certain sizes are supported.  */
 
-static struct type *
+
+struct type *
 address_type (struct gdbarch *gdbarch, int addr_size)
 {
   struct dwarf_gdbarch_types *types
@@ -2221,6 +2218,137 @@ sect_variable_value (sect_offset sect_off,
 				     type, true);
 }
 
+/* The expression evaluator works with a dwarf_expr_context, describing
+   its current state and its callbacks.  */
+struct dwarf_expr_context
+{
+  /* Create a new context for the expression evaluator.
+
+     We should ever only pass in the PER_OBJFILE and the ADDR_SIZE
+     information should be retrievable from there.  The PER_OBJFILE
+     contains a pointer to the PER_BFD information anyway and the
+     address size information must be the same for the whole BFD.   */
+  dwarf_expr_context (struct dwarf2_per_objfile *per_objfile,
+		      int addr_size);
+
+  /* Evaluate the expression at ADDR (LEN bytes long) in a given PER_CU
+     FRAME context.  INIT_VALUES vector contains values that are
+     expected to be pushed on a DWARF expression stack before the
+     evaluation.  AS_LVAL defines if the returned struct value is
+     expected to be a value or a location description.  Where TYPE,
+     SUBOBJ_TYPE and SUBOBJ_OFFSET describe expected struct value
+     representation of the evaluation result.  The ADDR_INFO property
+     can be specified to override the range of memory addresses with
+     the passed in buffer.  */
+  struct value *evaluate (const gdb_byte *addr, size_t len, bool as_lval,
+			  struct dwarf2_per_cu_data *per_cu,
+			  struct frame_info *frame,
+			  std::vector<struct value *> *init_values,
+			  const struct property_addr_info *addr_info,
+			  struct type *type, struct type *subobj_type,
+			  LONGEST subobj_offset);
+
+private:
+  /* The stack of DWARF entries.  */
+  std::vector<std::shared_ptr<dwarf_entry>> stack;
+
+  /* Target architecture to use for address operations.  */
+  struct gdbarch *gdbarch;
+
+  /* Target address size in bytes.  */
+  int addr_size;
+
+  /* DW_FORM_ref_addr size in bytes.  If -1 DWARF is executed
+     from a frame context and operations depending on DW_FORM_ref_addr
+     are not allowed.  */
+  int ref_addr_size = 0;
+
+  /* The current depth of dwarf expression recursion, via DW_OP_call*,
+     DW_OP_fbreg, DW_OP_push_object_address, etc., and the maximum
+     depth we'll tolerate before raising an error.  */
+  int recursion_depth = 0, max_recursion_depth = 0x100;
+
+  /* We evaluate the expression in the context of this objfile.  */
+  dwarf2_per_objfile *per_objfile;
+
+  /* Frame information used for the evaluation.  */
+  struct frame_info *frame = nullptr;
+
+  /* Compilation unit used for the evaluation.  */
+  struct dwarf2_per_cu_data *per_cu = nullptr;
+
+  /* Property address info used for the evaluation.  */
+  const struct property_addr_info *addr_info = nullptr;
+
+  /* Evaluate the expression at ADDR (LEN bytes long).  */
+  void eval (const gdb_byte *addr, size_t len);
+
+  /* Return the type used for DWARF operations where the type is
+     unspecified in the DWARF spec.  Only certain sizes are
+     supported.  */
+  struct type *address_type () const;
+
+  /* Push ENTRY onto the stack.  */
+  void push (std::shared_ptr<dwarf_entry> value);
+
+  /* Return true if the expression stack is empty.  */
+  bool stack_empty_p () const;
+
+  /* Pop a top element of the stack and add as a composite piece.
+
+     If the fallowing top element of the stack is a composite
+     location description, the piece will be added to it.  Otherwise
+     a new composite location description will be created and
+     the piece will be added to that composite.  */
+  std::shared_ptr<dwarf_entry> add_piece (ULONGEST bit_size,
+					  ULONGEST bit_offset);
+
+  /* The engine for the expression evaluator.  Using the context in this
+     object, evaluate the expression between OP_PTR and OP_END.  */
+  void execute_stack_op (const gdb_byte *op_ptr, const gdb_byte *op_end);
+
+  /* Pop the top item off of the stack.  */
+  void pop ();
+
+  /* Retrieve the N'th item on the stack.  */
+  std::shared_ptr<dwarf_entry> fetch (int n);
+
+  /* Fetch the result of the expression evaluation in a form of
+     a struct value, where TYPE, SUBOBJ_TYPE and SUBOBJ_OFFSET
+     describe the source level representation of that result.
+     AS_LVAL defines if the fetched struct value is expected to
+     be a value or a location description.  */
+  struct value *fetch_result (struct type *type,
+			      struct type *subobj_type,
+			      LONGEST subobj_offset,
+			      bool as_lval);
+
+  /* Return the location expression for the frame base attribute, in
+     START and LENGTH.  The result must be live until the current
+     expression evaluation is complete.  */
+  void get_frame_base (const gdb_byte **start, size_t *length);
+
+  /* Return the base type given by the indicated DIE at DIE_CU_OFF.
+     This can throw an exception if the DIE is invalid or does not
+     represent a base type.  SIZE is non-zero if this function should
+     verify that the resulting type has the correct size.  */
+  struct type *get_base_type (cu_offset die_cu_off, int size);
+
+  /* Execute DW_AT_location expression for the DWARF expression
+     subroutine in the DIE at DIE_CU_OFF in the CU.  Do not touch
+     STACK while it being passed to and returned from the called DWARF
+     subroutine.  */
+  void dwarf_call (cu_offset die_cu_off);
+
+  /* Push on DWARF stack an entry evaluated for DW_TAG_call_site's
+     parameter matching KIND and KIND_U at the caller of specified
+     BATON. If DEREF_SIZE is not -1 then use DW_AT_call_data_value
+     instead of DW_AT_call_value.  */
+  void push_dwarf_reg_entry_value (enum call_site_parameter_kind kind,
+				   union call_site_parameter_u kind_u,
+				   int deref_size);
+};
+
 /* Return the type used for DWARF operations where the type is
    unspecified in the DWARF spec.  Only certain sizes are
    supported.  */
@@ -2241,26 +2369,12 @@ dwarf_expr_context::dwarf_expr_context (dwarf2_per_objfile *per_objfile,
 {
 }
 
-/* See expr.h.  */
-
 void
 dwarf_expr_context::push (std::shared_ptr<dwarf_entry> entry)
 {
   stack.emplace_back (entry);
 }
 
-/* See expr.h.  */
-
-void
-dwarf_expr_context::push_address (CORE_ADDR addr, bool in_stack_memory)
-{
-  stack.emplace_back (std::make_shared<dwarf_memory> (this->gdbarch, addr,
-						      0, in_stack_memory));
-}
-
-
-/* See expr.h.  */
-
 void
 dwarf_expr_context::pop ()
 {
@@ -2270,8 +2384,6 @@ dwarf_expr_context::pop ()
   stack.pop_back ();
 }
 
-/* See expr.h.  */
-
 std::shared_ptr<dwarf_entry>
 dwarf_expr_context::fetch (int n)
 {
@@ -2282,8 +2394,6 @@ dwarf_expr_context::fetch (int n)
   return stack[stack.size () - (1 + n)];
 }
 
-/* See expr.h.  */
-
 void
 dwarf_expr_context::get_frame_base (const gdb_byte **start,
 				    size_t * length)
@@ -2310,8 +2420,6 @@ dwarf_expr_context::get_frame_base (const gdb_byte **start,
 				   start, length);
 }
 
-/* See expr.h.  */
-
 struct type *
 dwarf_expr_context::get_base_type (cu_offset die_cu_off, int size)
 {
@@ -2329,8 +2437,6 @@ dwarf_expr_context::get_base_type (cu_offset die_cu_off, int size)
   return result;
 }
 
-/* See expr.h.  */
-
 void
 dwarf_expr_context::dwarf_call (cu_offset die_cu_off)
 {
@@ -2354,8 +2460,6 @@ dwarf_expr_context::dwarf_call (cu_offset die_cu_off)
   this->eval (block.data, block.size);
 }
 
-/* See expr.h.  */
-
 void
 dwarf_expr_context::push_dwarf_reg_entry_value
 		      (enum call_site_parameter_kind kind,
@@ -2405,8 +2509,6 @@ dwarf_expr_context::push_dwarf_reg_entry_value
   this->eval (data_src, size);
 }
 
-/* See expr.h.  */
-
 struct value *
 dwarf_expr_context::fetch_result (struct type *type,
 				  struct type *subobj_type,
@@ -2429,12 +2531,11 @@ dwarf_expr_context::fetch_result (struct type *type,
   return entry->to_gdb_value (this->frame, type, subobj_type, subobj_offset);
 }
 
-/* See expr.h.  */
-
 struct value *
 dwarf_expr_context::evaluate (const gdb_byte *addr, size_t len, bool as_lval,
 			      struct dwarf2_per_cu_data *per_cu,
 			      struct frame_info *frame,
+			      std::vector<struct value *> *init_values,
 			      const struct property_addr_info *addr_info,
 			      struct type *type,
 			      struct type *subobj_type,
@@ -2447,6 +2548,10 @@ dwarf_expr_context::evaluate (const gdb_byte *addr, size_t len, bool as_lval,
   if (per_cu != nullptr)
     this->ref_addr_size = per_cu->ref_addr_size ();
 
+  if (init_values != nullptr)
+    for (unsigned int i = 0; i < init_values->size (); i++)
+      push (gdb_value_to_dwarf_entry (this->gdbarch, (*init_values)[i]));
+
   eval (addr, len);
   return fetch_result (type, subobj_type, subobj_offset, as_lval);
 }
@@ -2506,16 +2611,12 @@ get_signed_type (struct gdbarch *gdbarch, struct type *type)
     }
 }
 
-/* See expr.h.  */
-
 bool
 dwarf_expr_context::stack_empty_p () const
 {
   return stack.empty ();
 }
 
-/* See expr.h.  */
-
 std::shared_ptr<dwarf_entry>
 dwarf_expr_context::add_piece (ULONGEST bit_size, ULONGEST bit_offset)
 {
@@ -2549,9 +2650,6 @@ dwarf_expr_context::add_piece (ULONGEST bit_size, ULONGEST bit_offset)
   return composite;
 }
 
-
-/* See expr.h.  */
-
 void
 dwarf_expr_context::eval (const gdb_byte *addr, size_t len)
 {
@@ -2790,8 +2888,6 @@ dwarf_block_to_sp_offset (struct gdbarch *gdbarch, const gdb_byte *buf,
   return 1;
 }
 
-/* See expr.h.  */
-
 void
 dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 				      const gdb_byte *op_end)
@@ -3729,6 +3825,25 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
   gdb_assert (this->recursion_depth >= 0);
 }
 
+/* See expr.h.  */
+
+struct value *
+dwarf2_evaluate (const gdb_byte *addr, size_t len, bool as_lval,
+		 struct dwarf2_per_objfile *per_objfile,
+		 struct dwarf2_per_cu_data *per_cu,
+		 struct frame_info *frame, int addr_size,
+		 std::vector<struct value *> *init_values,
+		 const struct property_addr_info *addr_info,
+		 struct type *type, struct type *subobj_type,
+		 LONGEST subobj_offset)
+{
+  dwarf_expr_context ctx (per_objfile, addr_size);
+
+  return ctx.evaluate (addr, len, as_lval, per_cu,
+		       frame, init_values, addr_info,
+		       type, subobj_type, subobj_offset);
+}
+
 void _initialize_dwarf2expr ();
 void
 _initialize_dwarf2expr ()
diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
index 5170ac2df43..132f924014f 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -25,142 +25,31 @@
 #include "leb128.h"
 #include "gdbtypes.h"
 
-class dwarf_entry;
 struct dwarf2_per_objfile;
 
-/* The expression evaluator works with a dwarf_expr_context, describing
-   its current state and its callbacks.  */
-struct dwarf_expr_context
-{
-  /* Create a new context for the expression evaluator.
-
-     We should ever only pass in the PER_OBJFILE and the ADDR_SIZE
-     information should be retrievable from there.  The PER_OBJFILE
-     contains a pointer to the PER_BFD information anyway and the
-     address size information must be the same for the whole BFD.  */
-  dwarf_expr_context (struct dwarf2_per_objfile *per_objfile,
-		      int addr_size);
-  virtual ~dwarf_expr_context () = default;
-
-  /* Push ADDR onto the stack.  */
-  void push_address (CORE_ADDR addr, bool in_stack_memory);
-
-  /* Evaluate the expression at ADDR (LEN bytes long) in a given PER_CU
-     FRAME context.  AS_LVAL defines if the returned struct value is
-     expected to be a value or a location description.  Where TYPE,
-     SUBOBJ_TYPE and SUBOBJ_OFFSET describe expected struct value
-     representation of the evaluation result.  The ADDR_INFO property
-     can be specified to override the range of memory addresses with
-     the passed in buffer.  */
-  struct value *evaluate (const gdb_byte *addr, size_t len, bool as_lval,
-			  struct dwarf2_per_cu_data *per_cu,
-			  struct frame_info *frame,
-			  const struct property_addr_info *addr_info = nullptr,
-			  struct type *type = nullptr,
-			  struct type *subobj_type = nullptr,
-			  LONGEST subobj_offset = 0);
-
-private:
-  /* The stack of DWARF entries.  */
-  std::vector<std::shared_ptr<dwarf_entry>> stack;
-
-  /* Target architecture to use for address operations.  */
-  struct gdbarch *gdbarch = nullptr;
-
-  /* Target address size in bytes.  */
-  int addr_size = 0;
-
-  /* DW_FORM_ref_addr size in bytes.  If -1 DWARF is executed from a frame
-     context and operations depending on DW_FORM_ref_addr are not allowed.  */
-  int ref_addr_size = 0;
-
-  /* The current depth of dwarf expression recursion, via DW_OP_call*,
-     DW_OP_fbreg, DW_OP_push_object_address, etc., and the maximum
-     depth we'll tolerate before raising an error.  */
-  int recursion_depth = 0, max_recursion_depth = 0x100;
-
-  /* We evaluate the expression in the context of this objfile.  */
-  dwarf2_per_objfile *per_objfile;
-
-  /* Frame information used for the evaluation.  */
-  struct frame_info *frame = nullptr;
-
-  /* Compilation unit used for the evaluation.  */
-  struct dwarf2_per_cu_data *per_cu = nullptr;
-
-  /* Property address info used for the evaluation.  */
-  const struct property_addr_info *addr_info = nullptr;
-
-  /* Evaluate the expression at ADDR (LEN bytes long).  */
-  void eval (const gdb_byte *addr, size_t len);
-
-  /* Return the type used for DWARF operations where the type is
-     unspecified in the DWARF spec.  Only certain sizes are
-     supported.  */
-  struct type *address_type () const;
-
-  /* Push ENTRY onto the stack.  */
-  void push (std::shared_ptr<dwarf_entry> entry);
-
-  /* Return true if the expression stack is empty.  */
-  bool stack_empty_p () const;
-
-  /* Pop a top element of the stack and add as a composite piece
-     with an BIT_OFFSET offset and of a BIT_SIZE size.
-
-     If the following top element of the stack is a composite
-     location description, the piece will be added to it.  Otherwise
-     a new composite location description will be created and
-     the piece will be added to that composite.  */
-  std::shared_ptr<dwarf_entry> add_piece (ULONGEST bit_size,
-					  ULONGEST bit_offset);
-
-  /* The engine for the expression evaluator.  Using the context in
-      this object, evaluate the expression between OP_PTR and
-      OP_END.  */
-  void execute_stack_op (const gdb_byte *op_ptr, const gdb_byte *op_end);
-
-  /* Pop the top item off of the stack.  */
-  void pop ();
-
-  /* Retrieve the N'th item on the stack.  */
-  std::shared_ptr<dwarf_entry> fetch (int n);
-
-  /* Fetch the result of the expression evaluation in a form of
-     a struct value, where TYPE, SUBOBJ_TYPE and SUBOBJ_OFFSET
-     describe the source level representation of that result.
-     AS_LVAL defines if the fetched struct value is expected to
-     be a value or a location description.  */
-  struct value *fetch_result (struct type *type,
-			      struct type *subobj_type,
-			      LONGEST subobj_offset,
-			      bool as_lval);
-
-  /* Return the location expression for the frame base attribute, in
-     START and LENGTH.  The result must be live until the current
-     expression evaluation is complete.  */
-  void get_frame_base (const gdb_byte **start, size_t *length);
-
-  /* Return the base type given by the indicated DIE at DIE_CU_OFF.
-     This can throw an exception if the DIE is invalid or does not
-     represent a base type.  SIZE is non-zero if this function should
-     verify that the resulting type has the correct size.  */
-  struct type *get_base_type (cu_offset die_cu_off, int size);
-
-  /* Execute DW_AT_location expression for the DWARF expression
-     subroutine in the DIE at DIE_CU_OFF in the CU.  Do not touch
-     STACK while it being passed to and returned from the called DWARF
-     subroutine.  */
-  void dwarf_call (cu_offset die_cu_off);
-
-  /* Push on DWARF stack an entry evaluated for DW_TAG_call_site's
-     parameter matching KIND and KIND_U at the caller of specified BATON.
-     If DEREF_SIZE is not -1 then use DW_AT_call_data_value instead of
-     DW_AT_call_value.  */
-  void push_dwarf_reg_entry_value (enum call_site_parameter_kind kind,
-				   union call_site_parameter_u kind_u,
-				   int deref_size);
-};
+/* Evaluate the expression at ADDR (LEN bytes long) in a given PER_CU
+   FRAME context.  The PER_OBJFILE contains a pointer to the PER_BFD
+   information.  ADDR_SIZE defines a size of the DWARF generic type.
+   INIT_VALUES vector contains values that are expected to be pushed
+   on a DWARF expression stack before the evaluation.  AS_LVAL defines
+   if the returned struct value is expected to be a value or a location
+   description.  Where TYPE, SUBOBJ_TYPE and SUBOBJ_OFFSET describe
+   expected struct value representation of the evaluation result.  The
+   ADDR_INFO property can be specified to override the range of memory
+   addresses with the passed in buffer.  */
+struct value *dwarf2_evaluate (const gdb_byte *addr, size_t len, bool as_lval,
+			       struct dwarf2_per_objfile *per_objfile,
+			       struct dwarf2_per_cu_data *per_cu,
+			       struct frame_info *frame, int addr_size,
+			       std::vector<struct value *> *init_values,
+			       const struct property_addr_info *addr_info,
+			       struct type *type = nullptr,
+			       struct type *subobj_type = nullptr,
+			       LONGEST subobj_offset = 0);
+
+/* Return the address type used of the GDBARCH architecture and
+   ADDR_SIZE is expected size of the type.  */
+struct type *address_type (struct gdbarch *gdbarch, int addr_size);
 
 /* Return the value of register number REG (a DWARF register number),
    read as an address in a given FRAME.  */
diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
index 51d308fd94f..97bd0d35319 100644
--- a/gdb/dwarf2/frame.c
+++ b/gdb/dwarf2/frame.c
@@ -228,11 +228,19 @@ execute_stack_op (const gdb_byte *exp, ULONGEST len, int addr_size,
 		  struct frame_info *this_frame, CORE_ADDR initial,
 		  int initial_in_stack_memory, dwarf2_per_objfile *per_objfile)
 {
-  dwarf_expr_context ctx (per_objfile, addr_size);
   scoped_value_mark free_values;
+  struct type *type = address_type (per_objfile->objfile->arch (),
+				    addr_size);
 
-  ctx.push_address (initial, initial_in_stack_memory);
-  struct value *result_val = ctx.evaluate (exp, len, true, nullptr, this_frame);
+  struct value *init_value = value_at_lazy (type, initial);
+  std::vector<struct value *> init_values;
+
+  set_value_stack (init_value, initial_in_stack_memory);
+  init_values.push_back (init_value);
+
+  struct value *result_val
+    = dwarf2_evaluate (exp, len, true, per_objfile, nullptr,
+		       this_frame, addr_size, &init_values, nullptr);
 
   if (VALUE_LVAL (result_val) == lval_memory)
     return value_address (result_val);
diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c
index c53413a942f..7dfe28c3e46 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -1434,15 +1434,15 @@ dwarf2_evaluate_loc_desc_full (struct type *type, struct frame_info *frame,
   if (size == 0)
     return allocate_optimized_out_value (subobj_type);
 
-  dwarf_expr_context ctx (per_objfile, per_cu->addr_size ());
-
   struct value *retval;
   scoped_value_mark free_values;
 
   try
     {
-      retval = ctx.evaluate (data, size, as_lval, per_cu, frame, nullptr,
-			     type, subobj_type, subobj_byte_offset);
+      retval
+	= dwarf2_evaluate (data, size, as_lval, per_objfile, per_cu,
+			   frame, per_cu->addr_size (), nullptr, nullptr,
+			   type, subobj_type, subobj_byte_offset);
     }
   catch (const gdb_exception_error &ex)
     {
@@ -1513,23 +1513,28 @@ dwarf2_locexpr_baton_eval (const struct dwarf2_locexpr_baton *dlbaton,
 
   dwarf2_per_objfile *per_objfile = dlbaton->per_objfile;
   struct dwarf2_per_cu_data *per_cu = dlbaton->per_cu;
-  dwarf_expr_context ctx (per_objfile, per_cu->addr_size ());
 
   struct value *result;
   scoped_value_mark free_values;
+  std::vector<struct value *> init_values;
 
   if (push_initial_value)
     {
+      struct type *type = address_type (per_objfile->objfile->arch (),
+					per_cu->addr_size ());
+
       if (addr_stack != nullptr)
-	ctx.push_address (addr_stack->addr, false);
+	init_values.push_back (value_at_lazy (type, addr_stack->addr));
       else
-	ctx.push_address (0, false);
+	init_values.push_back (value_at_lazy (type, 0));
     }
 
   try
     {
-      result = ctx.evaluate (dlbaton->data, dlbaton->size,
-			     true, per_cu, frame, addr_stack);
+      result
+	= dwarf2_evaluate (dlbaton->data, dlbaton->size, true, per_objfile,
+			   per_cu, frame, per_cu->addr_size (), &init_values,
+			   addr_stack);
     }
   catch (const gdb_exception_error &ex)
     {
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 36/43] Move read_addr_from_reg function to frame.c
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (34 preceding siblings ...)
  2021-03-01 14:46 ` [PATCH 35/43] Remove dwarf_expr_context from expr.h interface Zoran Zaric
@ 2021-03-01 14:46 ` Zoran Zaric
  2021-03-01 14:46 ` [PATCH 37/43] Add frame info check to DW_OP_reg operations Zoran Zaric
                   ` (6 subsequent siblings)
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:46 UTC (permalink / raw)
  To: gdb-patches

read_addr_from_reg function is now only called from frame.c file, this
means that the function can safely be moved there.

gdb/ChangeLog:

	* dwarf2/expr.c (read_addr_from_reg): Move function to frame.c.
	* dwarf2/expr.h (read_addr_from_reg): Remove function.
	* dwarf2/frame.c (read_addr_from_reg): Add function from
	expr.c.
---
 gdb/dwarf2/expr.c  | 11 -----------
 gdb/dwarf2/expr.h  |  4 ----
 gdb/dwarf2/frame.c | 12 ++++++++++++
 3 files changed, 12 insertions(+), 15 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 7a091e74ce5..b5f408fead1 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -105,17 +105,6 @@ ill_formed_expression ()
   error (_("Ill-formed DWARF expression"));
 }
 
-/* See expr.h.  */
-
-CORE_ADDR
-read_addr_from_reg (struct frame_info *frame, int reg)
-{
-  struct gdbarch *gdbarch = get_frame_arch (frame);
-  int regnum = dwarf_reg_to_regnum_or_error (gdbarch, reg);
-
-  return address_from_register (regnum, frame);
-}
-
 /* Read register REGNUM's contents in a given FRAME context.
 
    The data read is offsetted by OFFSET, and the number of bytes read
diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
index 132f924014f..b64bffc58c1 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -51,10 +51,6 @@ struct value *dwarf2_evaluate (const gdb_byte *addr, size_t len, bool as_lval,
    ADDR_SIZE is expected size of the type.  */
 struct type *address_type (struct gdbarch *gdbarch, int addr_size);
 
-/* Return the value of register number REG (a DWARF register number),
-   read as an address in a given FRAME.  */
-CORE_ADDR read_addr_from_reg (struct frame_info *, int);
-
 /* Check that the current operator is either at the end of an
    expression, or that it is followed by a composition operator or by
    DW_OP_GNU_uninit (which should terminate the expression).  */
diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
index 97bd0d35319..46cbf7016c2 100644
--- a/gdb/dwarf2/frame.c
+++ b/gdb/dwarf2/frame.c
@@ -192,6 +192,18 @@ dwarf2_frame_state::dwarf2_frame_state (CORE_ADDR pc_, struct dwarf2_cie *cie)
 {
 }
 
+/* Return the value of register number REG (a DWARF register number),
+   read as an address in a given FRAME.  */
+
+static CORE_ADDR
+read_addr_from_reg (struct frame_info *frame, int reg)
+{
+  struct gdbarch *gdbarch = get_frame_arch (frame);
+  int regnum = dwarf_reg_to_regnum_or_error (gdbarch, reg);
+
+  return address_from_register (regnum, frame);
+}
+
 /* Execute the required actions for both the DW_CFA_restore and
 DW_CFA_restore_extended instructions.  */
 static void
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 37/43] Add frame info check to DW_OP_reg operations
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (35 preceding siblings ...)
  2021-03-01 14:46 ` [PATCH 36/43] Move read_addr_from_reg function to frame.c Zoran Zaric
@ 2021-03-01 14:46 ` Zoran Zaric
  2021-03-01 14:46 ` [PATCH 38/43] Remove DWARF expression composition check Zoran Zaric
                   ` (5 subsequent siblings)
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:46 UTC (permalink / raw)
  To: gdb-patches

After enabling location description to be on a DWARF stack, it is now
needed to check the frame context information validity when creating a
register location description.

gdb/ChangeLog:

	* dwarf2/expr.c (dwarf_expr_context::execute_stack_op): Add
	check_frame_info call for DW_OP_reg operations.
---
 gdb/dwarf2/expr.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index b5f408fead1..977ac64dd68 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -3072,6 +3072,7 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	case DW_OP_reg30:
 	case DW_OP_reg31:
 	  dwarf_expr_require_composition (op_ptr, op_end, "DW_OP_reg");
+	  ensure_have_frame (this->frame, "DW_OP_reg");
 
 	  result = op - DW_OP_reg0;
 	  result_entry
@@ -3081,6 +3082,7 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	case DW_OP_regx:
 	  op_ptr = safe_read_uleb128 (op_ptr, op_end, &reg);
 	  dwarf_expr_require_composition (op_ptr, op_end, "DW_OP_regx");
+	  ensure_have_frame (this->frame, "DW_OP_regx");
 
 	  result = reg;
 	  result_entry = std::make_shared<dwarf_register> (this->gdbarch, reg);
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 38/43] Remove DWARF expression composition check
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (36 preceding siblings ...)
  2021-03-01 14:46 ` [PATCH 37/43] Add frame info check to DW_OP_reg operations Zoran Zaric
@ 2021-03-01 14:46 ` Zoran Zaric
  2021-03-01 14:46 ` [PATCH 39/43] Change back the symbol needs to use the evaluator Zoran Zaric
                   ` (4 subsequent siblings)
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:46 UTC (permalink / raw)
  To: gdb-patches

The dwarf_expr_require_composition function reports an error if the
last operation is not a leaf node of the DWARF expression. This was
previously used to prevent location description operations to be used
freely in the DWARF expression.

With the new approach, all operations are treated the same and
everything is composable, so there is no need for the previous
restrictions in the expression evaluator.

gdb/ChangeLog:

	* dwarf2/expr.c (dwarf_expr_context::execute_stack_op): Remove
	the use of dwarf_expr_require_composition.
---
 gdb/dwarf2/expr.c | 9 ---------
 1 file changed, 9 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 977ac64dd68..871f52acfd8 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -3071,7 +3071,6 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	case DW_OP_reg29:
 	case DW_OP_reg30:
 	case DW_OP_reg31:
-	  dwarf_expr_require_composition (op_ptr, op_end, "DW_OP_reg");
 	  ensure_have_frame (this->frame, "DW_OP_reg");
 
 	  result = op - DW_OP_reg0;
@@ -3081,7 +3080,6 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 
 	case DW_OP_regx:
 	  op_ptr = safe_read_uleb128 (op_ptr, op_end, &reg);
-	  dwarf_expr_require_composition (op_ptr, op_end, "DW_OP_regx");
 	  ensure_have_frame (this->frame, "DW_OP_regx");
 
 	  result = reg;
@@ -3099,8 +3097,6 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	      = std::make_shared<dwarf_implicit> (this->gdbarch, op_ptr, len,
 						  BFD_ENDIAN_UNKNOWN);
 	    op_ptr += len;
-	    dwarf_expr_require_composition (op_ptr, op_end,
-					    "DW_OP_implicit_value");
 	  }
 	  break;
 
@@ -3116,9 +3112,6 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 						  value->get_contents (),
 						  TYPE_LENGTH (type),
 						  type_byte_order (type));
-
-	    dwarf_expr_require_composition (op_ptr, op_end,
-					    "DW_OP_stack_value");
 	  }
 	  break;
 
@@ -3143,8 +3136,6 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 							  this->per_cu,
 							  this->addr_size,
 							  die_offset, len);
-	    dwarf_expr_require_composition (op_ptr, op_end,
-					    "DW_OP_implicit_pointer");
 	  }
 	  break;
 
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 39/43] Change back the symbol needs to use the evaluator
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (37 preceding siblings ...)
  2021-03-01 14:46 ` [PATCH 38/43] Remove DWARF expression composition check Zoran Zaric
@ 2021-03-01 14:46 ` Zoran Zaric
  2021-03-01 14:46 ` [PATCH 40/43] Add support for any location description in CFI Zoran Zaric
                   ` (3 subsequent siblings)
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:46 UTC (permalink / raw)
  To: gdb-patches

With the new design of the DWARF expression evaluator, it is now
possible to again use the evaluator for the symbol needs check.

This should address the concerns mentioned in the first patch of this
series.

gdb/ChangeLog:

        * dwarf2/loc.c (dwarf2_get_symbol_read_needs): Remove function.
        (dwarf2_loc_desc_get_symbol_read_needs): New function.
        (locexpr_get_symbol_read_needs): Change to use
        dwarf2_loc_desc_get_symbol_read_needs function.
---
 gdb/dwarf2/loc.c | 415 ++---------------------------------------------
 1 file changed, 18 insertions(+), 397 deletions(-)

diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c
index 7dfe28c3e46..3ea5d67d517 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -1735,403 +1735,28 @@ dwarf2_compile_property_to_c (string_file *stream,
    expression in EXPR.  */
 
 static enum symbol_needs_kind
-dwarf2_get_symbol_read_needs (gdb::array_view<const gdb_byte> expr,
-			     dwarf2_per_cu_data *per_cu,
-			     dwarf2_per_objfile *per_objfile,
-			     bfd_endian byte_order,
-			     int addr_size,
-			     int ref_addr_size,
-			     int depth = 0)
+dwarf2_loc_desc_get_symbol_read_needs (gdb::array_view<const gdb_byte> expr,
+				       dwarf2_per_cu_data *per_cu,
+				       dwarf2_per_objfile *per_objfile,
+				       struct type *type)
 {
-  const gdb_byte *expr_end = expr.data () + expr.size ();
-  const gdb_byte *op_ptr = expr.data ();
-  enum symbol_needs_kind symbol_needs = SYMBOL_NEEDS_NONE;
-  const int max_depth = 256;
-
-  if (depth > max_depth)
-    error (_("DWARF-2 expression error: Loop detected (%d)."), depth);
-
-  depth++;
+  scoped_value_mark free_values;
 
-  while (op_ptr < expr_end)
+  try
     {
-      enum dwarf_location_atom op
-	= (enum dwarf_location_atom) *op_ptr++;
-      uint64_t uoffset;
-      int64_t offset;
-
-      /* The DWARF expression might have a bug causing an infinite
-	 loop.  In that case, quitting is the only way out.  */
-      QUIT;
-
-      switch (op)
-	{
-	case DW_OP_lit0:
-	case DW_OP_lit1:
-	case DW_OP_lit2:
-	case DW_OP_lit3:
-	case DW_OP_lit4:
-	case DW_OP_lit5:
-	case DW_OP_lit6:
-	case DW_OP_lit7:
-	case DW_OP_lit8:
-	case DW_OP_lit9:
-	case DW_OP_lit10:
-	case DW_OP_lit11:
-	case DW_OP_lit12:
-	case DW_OP_lit13:
-	case DW_OP_lit14:
-	case DW_OP_lit15:
-	case DW_OP_lit16:
-	case DW_OP_lit17:
-	case DW_OP_lit18:
-	case DW_OP_lit19:
-	case DW_OP_lit20:
-	case DW_OP_lit21:
-	case DW_OP_lit22:
-	case DW_OP_lit23:
-	case DW_OP_lit24:
-	case DW_OP_lit25:
-	case DW_OP_lit26:
-	case DW_OP_lit27:
-	case DW_OP_lit28:
-	case DW_OP_lit29:
-	case DW_OP_lit30:
-	case DW_OP_lit31:
-	case DW_OP_stack_value:
-	case DW_OP_dup:
-	case DW_OP_drop:
-	case DW_OP_swap:
-	case DW_OP_over:
-	case DW_OP_rot:
-	case DW_OP_deref:
-	case DW_OP_abs:
-	case DW_OP_neg:
-	case DW_OP_not:
-	case DW_OP_and:
-	case DW_OP_div:
-	case DW_OP_minus:
-	case DW_OP_mod:
-	case DW_OP_mul:
-	case DW_OP_or:
-	case DW_OP_plus:
-	case DW_OP_shl:
-	case DW_OP_shr:
-	case DW_OP_shra:
-	case DW_OP_xor:
-	case DW_OP_le:
-	case DW_OP_ge:
-	case DW_OP_eq:
-	case DW_OP_lt:
-	case DW_OP_gt:
-	case DW_OP_ne:
-	case DW_OP_GNU_push_tls_address:
-	case DW_OP_nop:
-	case DW_OP_GNU_uninit:
-	case DW_OP_push_object_address:
-	  break;
-
-	case DW_OP_form_tls_address:
-	  if (symbol_needs <= SYMBOL_NEEDS_REGISTERS)
-	    symbol_needs = SYMBOL_NEEDS_REGISTERS;
-	  break;
-
-	case DW_OP_convert:
-	case DW_OP_GNU_convert:
-	case DW_OP_reinterpret:
-	case DW_OP_GNU_reinterpret:
-	case DW_OP_addrx:
-	case DW_OP_GNU_addr_index:
-	case DW_OP_GNU_const_index:
-	case DW_OP_constu:
-	case DW_OP_plus_uconst:
-	case DW_OP_piece:
-	  op_ptr = safe_read_uleb128 (op_ptr, expr_end, &uoffset);
-	  break;
-
-	case DW_OP_consts:
-	  op_ptr = safe_read_sleb128 (op_ptr, expr_end, &offset);
-	  break;
-
-	case DW_OP_bit_piece:
-	  op_ptr = safe_read_uleb128 (op_ptr, expr_end, &uoffset);
-	  op_ptr = safe_read_uleb128 (op_ptr, expr_end, &uoffset);
-	  break;
-
-	case DW_OP_deref_type:
-	case DW_OP_GNU_deref_type:
-	  op_ptr++;
-	  op_ptr = safe_read_uleb128 (op_ptr, expr_end, &uoffset);
-	  break;
-
-	case DW_OP_addr:
-	  op_ptr += addr_size;
-	  break;
-
-	case DW_OP_const1u:
-	case DW_OP_const1s:
-	  op_ptr += 1;
-	  break;
-
-	case DW_OP_const2u:
-	case DW_OP_const2s:
-	  op_ptr += 2;
-	  break;
-
-	case DW_OP_const4s:
-	case DW_OP_const4u:
-	  op_ptr += 4;
-	  break;
-
-	case DW_OP_const8s:
-	case DW_OP_const8u:
-	  op_ptr += 8;
-	  break;
-
-	case DW_OP_reg0:
-	case DW_OP_reg1:
-	case DW_OP_reg2:
-	case DW_OP_reg3:
-	case DW_OP_reg4:
-	case DW_OP_reg5:
-	case DW_OP_reg6:
-	case DW_OP_reg7:
-	case DW_OP_reg8:
-	case DW_OP_reg9:
-	case DW_OP_reg10:
-	case DW_OP_reg11:
-	case DW_OP_reg12:
-	case DW_OP_reg13:
-	case DW_OP_reg14:
-	case DW_OP_reg15:
-	case DW_OP_reg16:
-	case DW_OP_reg17:
-	case DW_OP_reg18:
-	case DW_OP_reg19:
-	case DW_OP_reg20:
-	case DW_OP_reg21:
-	case DW_OP_reg22:
-	case DW_OP_reg23:
-	case DW_OP_reg24:
-	case DW_OP_reg25:
-	case DW_OP_reg26:
-	case DW_OP_reg27:
-	case DW_OP_reg28:
-	case DW_OP_reg29:
-	case DW_OP_reg30:
-	case DW_OP_reg31:
-	case DW_OP_regx:
-	case DW_OP_breg0:
-	case DW_OP_breg1:
-	case DW_OP_breg2:
-	case DW_OP_breg3:
-	case DW_OP_breg4:
-	case DW_OP_breg5:
-	case DW_OP_breg6:
-	case DW_OP_breg7:
-	case DW_OP_breg8:
-	case DW_OP_breg9:
-	case DW_OP_breg10:
-	case DW_OP_breg11:
-	case DW_OP_breg12:
-	case DW_OP_breg13:
-	case DW_OP_breg14:
-	case DW_OP_breg15:
-	case DW_OP_breg16:
-	case DW_OP_breg17:
-	case DW_OP_breg18:
-	case DW_OP_breg19:
-	case DW_OP_breg20:
-	case DW_OP_breg21:
-	case DW_OP_breg22:
-	case DW_OP_breg23:
-	case DW_OP_breg24:
-	case DW_OP_breg25:
-	case DW_OP_breg26:
-	case DW_OP_breg27:
-	case DW_OP_breg28:
-	case DW_OP_breg29:
-	case DW_OP_breg30:
-	case DW_OP_breg31:
-	case DW_OP_bregx:
-	case DW_OP_fbreg:
-	case DW_OP_call_frame_cfa:
-	case DW_OP_entry_value:
-	case DW_OP_GNU_entry_value:
-	case DW_OP_GNU_parameter_ref:
-	case DW_OP_regval_type:
-	case DW_OP_GNU_regval_type:
-	  symbol_needs = SYMBOL_NEEDS_FRAME;
-	  break;
-
-	case DW_OP_implicit_value:
-	  op_ptr = safe_read_uleb128 (op_ptr, expr_end, &uoffset);
-	  op_ptr += uoffset;
-	  break;
-
-	case DW_OP_implicit_pointer:
-	case DW_OP_GNU_implicit_pointer:
-	  op_ptr += ref_addr_size;
-	  op_ptr = safe_read_sleb128 (op_ptr, expr_end, &offset);
-	  break;
-
-	case DW_OP_deref_size:
-	case DW_OP_pick:
-	  op_ptr++;
-	  break;
-
-	case DW_OP_skip:
-	  op_ptr += 2;
-	  break;
-
-	case DW_OP_bra:
-	  op_ptr += 2;
-	  break;
-
-	case DW_OP_call2:
-	  {
-	    cu_offset cu_off
-	      = (cu_offset) extract_unsigned_integer (op_ptr, 2, byte_order);
-	    op_ptr += 2;
-
-	    auto get_frame_pc = [&symbol_needs] ()
-	      {
-		symbol_needs = SYMBOL_NEEDS_FRAME;
-		return 0;
-	      };
-
-	    struct dwarf2_locexpr_baton baton
-	      = dwarf2_fetch_die_loc_cu_off (cu_off, per_cu,
-					     per_objfile,
-					     get_frame_pc);
-
-	    /* If SYMBOL_NEEDS_FRAME is returned from the previous call,
-	       we dont have to check the baton content.  */
-	    if (symbol_needs != SYMBOL_NEEDS_FRAME)
-	      {
-		gdbarch *arch = baton.per_objfile->objfile->arch ();
-		gdb::array_view<const gdb_byte> sub_expr (baton.data,
-							  baton.size);
-		symbol_needs
-		  = dwarf2_get_symbol_read_needs (sub_expr,
-						  baton.per_cu,
-						  baton.per_objfile,
-						  gdbarch_byte_order (arch),
-						  baton.per_cu->addr_size (),
-						  baton.per_cu->ref_addr_size (),
-						  depth);
-	      }
-	    break;
-	  }
-
-	case DW_OP_call4:
-	  {
-	    cu_offset cu_off
-	      = (cu_offset) extract_unsigned_integer (op_ptr, 4, byte_order);
-	    op_ptr += 4;
-
-	    auto get_frame_pc = [&symbol_needs] ()
-	      {
-		symbol_needs = SYMBOL_NEEDS_FRAME;
-		return 0;
-	      };
-
-	    struct dwarf2_locexpr_baton baton
-	      = dwarf2_fetch_die_loc_cu_off (cu_off, per_cu,
-					     per_objfile,
-					     get_frame_pc);
-
-	    /* If SYMBOL_NEEDS_FRAME is returned from the previous call,
-	       we dont have to check the baton content.  */
-	    if (symbol_needs != SYMBOL_NEEDS_FRAME)
-	      {
-		gdbarch *arch = baton.per_objfile->objfile->arch ();
-		gdb::array_view<const gdb_byte> sub_expr (baton.data,
-							  baton.size);
-		symbol_needs
-		  = dwarf2_get_symbol_read_needs (sub_expr,
-						  baton.per_cu,
-						  baton.per_objfile,
-						  gdbarch_byte_order (arch),
-						  baton.per_cu->addr_size (),
-						  baton.per_cu->ref_addr_size (),
-						  depth);
-	      }
-	    break;
-	  }
-
-	case DW_OP_GNU_variable_value:
-	  {
-	    sect_offset sect_off
-	      = (sect_offset) extract_unsigned_integer (op_ptr,
-							ref_addr_size,
-							byte_order);
-	    op_ptr += ref_addr_size;
-
-	    struct type *die_type
-	      = dwarf2_fetch_die_type_sect_off (sect_off, per_cu,
-						per_objfile);
-
-	    if (die_type == NULL)
-	      error (_("Bad DW_OP_GNU_variable_value DIE."));
-
-	    /* Note: Things still work when the following test is
-	       removed.  This test and error is here to conform to the
-	       proposed specification.  */
-	    if (die_type->code () != TYPE_CODE_INT
-	       && die_type->code () != TYPE_CODE_PTR)
-	      error (_("Type of DW_OP_GNU_variable_value DIE must be "
-		       "an integer or pointer."));
-
-	    auto get_frame_pc = [&symbol_needs] ()
-	      {
-		symbol_needs = SYMBOL_NEEDS_FRAME;
-		return 0;
-	      };
-
-	    struct dwarf2_locexpr_baton baton
-	      = dwarf2_fetch_die_loc_sect_off (sect_off, per_cu,
-					       per_objfile,
-					       get_frame_pc, true);
-
-	    /* If SYMBOL_NEEDS_FRAME is returned from the previous call,
-	       we dont have to check the baton content.  */
-	    if (symbol_needs != SYMBOL_NEEDS_FRAME)
-	      {
-		gdbarch *arch = baton.per_objfile->objfile->arch ();
-		gdb::array_view<const gdb_byte> sub_expr (baton.data,
-							  baton.size);
-		symbol_needs
-		  = dwarf2_get_symbol_read_needs (sub_expr,
-						  baton.per_cu,
-						  baton.per_objfile,
-						  gdbarch_byte_order (arch),
-						  baton.per_cu->addr_size (),
-						  baton.per_cu->ref_addr_size (),
-						  depth);
-	      }
-	    break;
-	  }
-
-	case DW_OP_const_type:
-	case DW_OP_GNU_const_type:
-	  op_ptr = safe_read_uleb128 (op_ptr, expr_end, &uoffset);
-	  offset = *op_ptr++;
-	  op_ptr += offset;
-	  break;
-
-	default:
-	  error (_("Unhandled DWARF expression opcode 0x%x"), op);
-	}
-      /* If it is known that a frame information is
-	 needed we can stop parsing the expression.  */
-      if (symbol_needs == SYMBOL_NEEDS_FRAME)
-	break;
+      dwarf2_evaluate (expr.data (), expr.size (), true, per_objfile, per_cu,
+		       nullptr, per_cu->addr_size (), nullptr, nullptr, type);
+    }
+  catch (const gdb_exception_error &ex)
+    {
+      if (ex.error == NOT_AVAILABLE_ERROR)
+       return SYMBOL_NEEDS_FRAME;
     }
 
-  return symbol_needs;
+  return SYMBOL_NEEDS_NONE;
 }
 
+
 /* A helper function that throws an unimplemented error mentioning a
    given DWARF operator.  */
 
@@ -2971,15 +2596,11 @@ locexpr_get_symbol_read_needs (struct symbol *symbol)
   struct dwarf2_locexpr_baton *dlbaton
     = (struct dwarf2_locexpr_baton *) SYMBOL_LOCATION_BATON (symbol);
 
-  gdbarch *arch = dlbaton->per_objfile->objfile->arch ();
   gdb::array_view<const gdb_byte> expr (dlbaton->data, dlbaton->size);
 
-  return dwarf2_get_symbol_read_needs (expr,
-				       dlbaton->per_cu,
-				       dlbaton->per_objfile,
-				       gdbarch_byte_order (arch),
-				       dlbaton->per_cu->addr_size (),
-				       dlbaton->per_cu->ref_addr_size ());
+  return dwarf2_loc_desc_get_symbol_read_needs (expr, dlbaton->per_cu,
+						dlbaton->per_objfile,
+						SYMBOL_TYPE (symbol));
 }
 
 /* Return true if DATA points to the end of a piece.  END is one past
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 40/43] Add support for any location description in CFI
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (38 preceding siblings ...)
  2021-03-01 14:46 ` [PATCH 39/43] Change back the symbol needs to use the evaluator Zoran Zaric
@ 2021-03-01 14:46 ` Zoran Zaric
  2021-03-01 14:46 ` [PATCH 41/43] Add DWARF operations for byte and bit offset Zoran Zaric
                   ` (2 subsequent siblings)
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:46 UTC (permalink / raw)
  To: gdb-patches

One of the main benefits of allowing location description to be on the
DWARF stack is that now CFI expression based register rules can be
defined using a location description operations. This allows a register
of one frame to be saved in any location, including any composite
location.

To fully support this feature, the execute_stack_op function in
dwarf2/frame.c needs to return a single struct value object instead of
just an address.

Function put_frame_register_bytes also needs to change to support any
location description.

This support is a one of the key features to truly support optimized
code.

gdb/ChangeLog:

	* dwarf2/frame.c (execute_stack_op): Change to return a struct
	value object.
	(dwarf2_frame_cache): Change to call new execute_stack_op
	definition.
	(dwarf2_frame_prev_register): Change to call new execute_stack_op
	definition.
	* frame.c (put_frame_register_bytes): Add support for writing to
	composite location description.
---
 gdb/dwarf2/frame.c | 54 ++++++++++++++++++++++++++--------------------
 gdb/frame.c        | 37 +++++++++++++++++++++++++------
 2 files changed, 62 insertions(+), 29 deletions(-)

diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
index 46cbf7016c2..88f118d6374 100644
--- a/gdb/dwarf2/frame.c
+++ b/gdb/dwarf2/frame.c
@@ -235,16 +235,17 @@ register %s (#%d) at %s"),
     }
 }
 
-static CORE_ADDR
+static value *
 execute_stack_op (const gdb_byte *exp, ULONGEST len, int addr_size,
 		  struct frame_info *this_frame, CORE_ADDR initial,
-		  int initial_in_stack_memory, dwarf2_per_objfile *per_objfile)
+		  int initial_in_stack_memory, dwarf2_per_objfile *per_objfile,
+		  struct type* type = nullptr, bool as_lval = true)
 {
   scoped_value_mark free_values;
-  struct type *type = address_type (per_objfile->objfile->arch (),
-				    addr_size);
+  struct type *init_type = address_type (per_objfile->objfile->arch (),
+					 addr_size);
 
-  struct value *init_value = value_at_lazy (type, initial);
+  struct value *init_value = value_at_lazy (init_type, initial);
   std::vector<struct value *> init_values;
 
   set_value_stack (init_value, initial_in_stack_memory);
@@ -254,10 +255,15 @@ execute_stack_op (const gdb_byte *exp, ULONGEST len, int addr_size,
     = dwarf2_evaluate (exp, len, true, per_objfile, nullptr,
 		       this_frame, addr_size, &init_values, nullptr);
 
-  if (VALUE_LVAL (result_val) == lval_memory)
-    return value_address (result_val);
-  else
-    return value_as_address (result_val);
+  /* We need to clean up all the values that are not needed any more.
+     The problem with a value_ref_ptr class is that it disconnects the
+     RETVAL from the value garbage collection, so we need to make
+     a copy of that value on the stack to keep everything consistent.
+     The value_ref_ptr will clean up after itself at the end of this block.  */
+  value_ref_ptr value_holder = value_ref_ptr::new_reference (result_val);
+  free_values.free_to_mark ();
+
+  return value_copy(result_val);
 }
 \f
 
@@ -988,10 +994,14 @@ dwarf2_frame_cache (struct frame_info *this_frame, void **this_cache)
 	  break;
 
 	case CFA_EXP:
-	  cache->cfa =
-	    execute_stack_op (fs.regs.cfa_exp, fs.regs.cfa_exp_len,
-			      cache->addr_size, this_frame, 0, 0,
-			      cache->per_objfile);
+	  {
+	    struct value *value
+	      = execute_stack_op (fs.regs.cfa_exp, fs.regs.cfa_exp_len,
+				  cache->addr_size, this_frame, 0, 0,
+				  cache->per_objfile);
+	    cache->cfa = value_address (value);
+	  }
+
 	  break;
 
 	default:
@@ -1189,24 +1199,22 @@ dwarf2_frame_prev_register (struct frame_info *this_frame, void **this_cache,
       return frame_unwind_got_register (this_frame, regnum, realnum);
 
     case DWARF2_FRAME_REG_SAVED_EXP:
-      addr = execute_stack_op (cache->reg[regnum].loc.exp.start,
+      return execute_stack_op (cache->reg[regnum].loc.exp.start,
 			       cache->reg[regnum].loc.exp.len,
-			       cache->addr_size,
-			       this_frame, cache->cfa, 1,
-			       cache->per_objfile);
-      return frame_unwind_got_memory (this_frame, regnum, addr);
+			       cache->addr_size, this_frame,
+			       cache->cfa, 1, cache->per_objfile,
+			       register_type (gdbarch, regnum));
 
     case DWARF2_FRAME_REG_SAVED_VAL_OFFSET:
       addr = cache->cfa + cache->reg[regnum].loc.offset;
       return frame_unwind_got_constant (this_frame, regnum, addr);
 
     case DWARF2_FRAME_REG_SAVED_VAL_EXP:
-      addr = execute_stack_op (cache->reg[regnum].loc.exp.start,
+      return execute_stack_op (cache->reg[regnum].loc.exp.start,
 			       cache->reg[regnum].loc.exp.len,
-			       cache->addr_size,
-			       this_frame, cache->cfa, 1,
-			       cache->per_objfile);
-      return frame_unwind_got_constant (this_frame, regnum, addr);
+			       cache->addr_size, this_frame,
+			       cache->cfa, 1, cache->per_objfile,
+			       register_type (gdbarch, regnum), false);
 
     case DWARF2_FRAME_REG_UNSPECIFIED:
       /* GCC, in its infinite wisdom decided to not provide unwind
diff --git a/gdb/frame.c b/gdb/frame.c
index 4578b1acab3..c0d749094ac 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1574,26 +1574,51 @@ put_frame_register_bytes (struct frame_info *frame, int regnum,
     {
       int curr_len = register_size (gdbarch, regnum) - offset;
 
+      struct value *value = frame_unwind_register_value (frame->next,
+							 regnum);
+
       if (curr_len > len)
 	curr_len = len;
 
       const gdb_byte *myaddr = buffer.data ();
-      if (curr_len == register_size (gdbarch, regnum))
+
+      /*  Compute value is a special new case.  The problem is that
+	  the computed callback mechanism only supports a struct
+	  value arguments, so we need to make one.  */
+      if (value != NULL && VALUE_LVAL (value) == lval_computed)
+	{
+	  struct value *from_value;
+	  const struct lval_funcs *funcs = value_computed_funcs (value);
+	  struct type * reg_type = register_type (gdbarch, regnum);
+
+	  if (funcs->write == NULL)
+	    error (_("Attempt to assign to an unmodifiable value."));
+
+	  from_value = allocate_value (reg_type);
+	  memcpy (value_contents_raw (from_value), myaddr,
+		  TYPE_LENGTH (reg_type));
+
+	  set_value_offset (value, offset);
+
+	  funcs->write (value, from_value);
+	  release_value (from_value);
+	}
+      else if (curr_len == register_size (gdbarch, regnum))
 	{
 	  put_frame_register (frame, regnum, myaddr);
 	}
       else
 	{
-	  struct value *value = frame_unwind_register_value (frame->next,
-							     regnum);
 	  gdb_assert (value != NULL);
 
-	  memcpy ((char *) value_contents_writeable (value) + offset, myaddr,
-		  curr_len);
+	  memcpy ((char *) value_contents_writeable (value) + offset,
+		  myaddr, curr_len);
 	  put_frame_register (frame, regnum, value_contents_raw (value));
-	  release_value (value);
 	}
 
+      if (value != NULL)
+	release_value (value);
+
       myaddr += curr_len;
       len -= curr_len;
       offset = 0;
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 41/43] Add DWARF operations for byte and bit offset
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (39 preceding siblings ...)
  2021-03-01 14:46 ` [PATCH 40/43] Add support for any location description in CFI Zoran Zaric
@ 2021-03-01 14:46 ` Zoran Zaric
  2021-03-01 14:46 ` [PATCH 42/43] Add support for DW_OP_LLVM_undefined operation Zoran Zaric
  2021-03-01 14:46 ` [PATCH 43/43] Add support for nested composite locations Zoran Zaric
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:46 UTC (permalink / raw)
  To: gdb-patches

Currently in DWARF, there are only two ways to specify an offset for a
location description.

For a memory location description, the location description can be
first converted to a DWARF value, after which an arithmetic operation
can be applied to it. This however, only works while there are no
address spaces involved, that are not mapped to a general address space
(CORE_ADDR). Another limitation is that there is no way to specify a
bit offset to that location description.

Second way of specifying an offset to a location description is more
universal and involves wrapping that location description in a
composite piece, where piece itself has a bit/byte offset defined. The
problem with this approach is that both DW_OP_piece and DW_OP_bit_piece
define an offset as a DWARF operation operand, which means that an
offset needs to be a constant value encoded into the DWARF expression.

By adding three new operations (DW_OP_LLVM_offset,
DW_OP_LLVM_offset_constu and DW_OP_LLVM_bit_offset) these restrictions
are now lifted.

Detailed descriptions of these new operations can be found here:

https://llvm.org/docs/AMDGPUDwarfExtensionsForHeterogeneousDebugging.html

The same document also explores an idea of extending the
DW_OP_push_object_address operation to allow pushing any location
description on the DWARF stack. This together with the new bit/byte
offset operations, generalizes DWARF to work with bit fields and could
replace the odd passed-in buffer mechanics in a more elegant way.

There seem to be a difference in views on what the big endian machine
register byte ordering should be. On one hand, some would expect for
a register to behave in the same way as memory, but on another, there
seems to be an existing implementation for (IBM big endian based
machines) which seems to be viewing registers differently, depending
if the register location description is part of a composite piece or
not. More on this topic can be found here:

https://sourceware.org/legacy-ml/gdb-patches/2017-04/msg00177.html

Unfortunately, the gdb current implementation favors the second option,
which feels like a target specific implementation.

Because of this, I’ve decided to not favor a specific implementation
in the added test for new DWARF operations (dw2-llvm-offset.exp), so
the test is restricted to only run on little endian platforms.

gdb/ChangeLog:

	* ada-lang.c (coerce_unspec_val_to_type): Add source bit offset
	argument to the value_contents_copy call.
	* compile/compile-loc2c.c (compute_stack_depth_worker): Add new
	DWARF operations support.
	* dwarf2/expr.c (dwarf_register::to_gdb_value): Add bit offset
	support.
        (dwarf_register::to_gdb_value): Add bit offset support.
	(dwarf_register::to_gdb_value): Add source bit
	offset argument to the value_contents_copy call.
	(dwarf_expr_context::execute_stack_op): Add new DWARF
	operations support.
	* findvar.c (read_frame_register_value): Add source bit offset
	argument to the value_contents_copy call.
	* valops.c (read_value_memory): Add bit offset support.
	(value_assign): Add bit offset support.
	(value_repeat): Add bit offset support.
	(value_array): Add source bit offset argument to the
	value_contents_copy call.
	(value_slice): Add source bit offset argument to the
	value_contents_copy call.
	* value.c (value_contents_copy_raw): Add source bit offset
	support.
	(value_contents_copy): Add source bit offset argument to
	value_contents_copy_raw call.
	(value_primitive_field): Add source bit offset argument to the
	value_contents_copy call.
	(value_from_component): Add source bit offset argument to the
	value_contents_copy call.
	(value_fetch_lazy_memory): Add bit offset argument to the
	read_value_memory call.
	(value_fetch_lazy_register): Add source bit offset argument to
	the value_contents_copy call.
	* value.h (value_contents_copy): Add source bit offset
	argument.

include/ChangeLog:

	* dwarf2.def (DW_OP_DUP): New DWARF operations enumeration.

gdb/testsuite/ChangeLog:

	* lib/dwarf.exp: Add support for new DW_OP_LLVM_offset_constu
	DWARF operation.
	* gdb.dwarf2/dw2-llvm-offset.exp: New test.
---
 gdb/ada-lang.c                               |   2 +-
 gdb/compile/compile-loc2c.c                  |   6 +
 gdb/dwarf2/expr.c                            |  48 ++-
 gdb/f-lang.c                                 |   4 +-
 gdb/findvar.c                                |   4 +-
 gdb/testsuite/gdb.dwarf2/dw2-llvm-offset.exp | 328 +++++++++++++++++++
 gdb/testsuite/lib/dwarf.exp                  |   4 +
 gdb/valops.c                                 | 129 +++++---
 gdb/value.c                                  |  66 ++--
 gdb/value.h                                  |   2 +-
 include/dwarf2.def                           |   4 +
 11 files changed, 520 insertions(+), 77 deletions(-)
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-llvm-offset.exp

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 419347f776d..197d6ba8dcb 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -611,7 +611,7 @@ coerce_unspec_val_to_type (struct value *val, struct type *type)
       else
 	{
 	  result = allocate_value (type);
-	  value_contents_copy (result, 0, val, 0, TYPE_LENGTH (type));
+	  value_contents_copy (result, 0, val, 0, 0, TYPE_LENGTH (type));
 	}
       set_value_component_location (result, val);
       set_value_bitsize (result, value_bitsize (val));
diff --git a/gdb/compile/compile-loc2c.c b/gdb/compile/compile-loc2c.c
index ef819799eb4..58558fa7d22 100644
--- a/gdb/compile/compile-loc2c.c
+++ b/gdb/compile/compile-loc2c.c
@@ -356,6 +356,12 @@ compute_stack_depth_worker (int start, int *need_tempvar,
 	  (*info)[offset].label = 1;
 	  break;
 
+	case DW_OP_LLVM_offset:
+	case DW_OP_LLVM_bit_offset:
+	  --stack_depth;
+	  break;
+
+	case DW_OP_LLVM_offset_constu:
 	case DW_OP_nop:
 	  break;
 
diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 871f52acfd8..19e5f5e93b5 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -1079,6 +1079,7 @@ dwarf_memory::to_gdb_value (struct frame_info *frame, struct type *type,
   address = value_as_address (value_from_pointer (ptr_type, address));
   struct value *retval = value_at_lazy (subobj_type, address + subobj_offset);
   set_value_stack (retval, m_stack);
+  set_value_bitpos (retval, m_bit_suboffset);
   return retval;
 }
 
@@ -1223,6 +1224,8 @@ dwarf_register::to_gdb_value (struct frame_info *frame, struct type *type,
   else
     set_value_offset (retval, retval_offset + m_offset);
 
+  set_value_bitpos (retval, m_bit_suboffset);
+
   /* Get the data.  */
   read_frame_register_value (retval, frame);
 
@@ -1234,7 +1237,7 @@ dwarf_register::to_gdb_value (struct frame_info *frame, struct type *type,
 	 return a generic optimized out value instead, so that we show
 	 <optimized out> instead of <not saved>.  */
       struct value *temp = allocate_value (subobj_type);
-      value_contents_copy (temp, 0, retval, 0, TYPE_LENGTH (subobj_type));
+      value_contents_copy (temp, 0, retval, 0, 0, TYPE_LENGTH (subobj_type));
       retval = temp;
     }
 
@@ -3792,6 +3795,49 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 					      this->addr_info->addr);
 	  break;
 
+	case DW_OP_LLVM_offset:
+	  {
+	    auto value = fetch (0)->to_value (address_type);
+	    pop ();
+
+	    dwarf_require_integral (value->get_type ());
+
+	    auto location = fetch (0)->to_location (this->gdbarch);
+	    pop ();
+
+	    location->add_bit_offset (value->to_long () * HOST_CHAR_BIT);
+	    result_entry = location;
+	  }
+	  break;
+
+	case DW_OP_LLVM_offset_constu:
+	  {
+	    op_ptr = safe_read_uleb128 (op_ptr, op_end, &uoffset);
+	    result = uoffset;
+
+	    auto location = fetch (0)->to_location (this->gdbarch);
+	    pop ();
+
+	    location->add_bit_offset (result * HOST_CHAR_BIT);
+	    result_entry = location;
+	  }
+	  break;
+
+	case DW_OP_LLVM_bit_offset:
+	  {
+	    auto value = fetch (0)->to_value (address_type);
+	    pop ();
+
+	    dwarf_require_integral (value->get_type ());
+
+	    auto location = fetch (0)->to_location (this->gdbarch);
+	    pop ();
+
+	    location->add_bit_offset (value->to_long ());
+	    result_entry = location;
+	  }
+	  break;
+
 	default:
 	  error (_("Unhandled dwarf expression opcode 0x%x"), op);
 	}
diff --git a/gdb/f-lang.c b/gdb/f-lang.c
index 01de51837f6..161398e0c5b 100644
--- a/gdb/f-lang.c
+++ b/gdb/f-lang.c
@@ -171,7 +171,7 @@ fortran_bounds_all_dims (bool lbound_p,
       gdb_assert (dst_offset + TYPE_LENGTH (value_type (v))
 		  <= TYPE_LENGTH (value_type (result)));
       gdb_assert (TYPE_LENGTH (value_type (v)) == elm_len);
-      value_contents_copy (result, dst_offset, v, 0, elm_len);
+      value_contents_copy (result, dst_offset, v, 0, 0, elm_len);
 
       /* Peel another dimension of the array.  */
       array_type = TYPE_TARGET_TYPE (array_type);
@@ -299,7 +299,7 @@ class fortran_array_repacker_base_impl
      available offset.  */
   void copy_element_to_dest (struct value *elt)
   {
-    value_contents_copy (m_dest, m_dest_offset, elt, 0,
+    value_contents_copy (m_dest, m_dest_offset, elt, 0, 0,
 			 TYPE_LENGTH (value_type (elt)));
     m_dest_offset += TYPE_LENGTH (value_type (elt));
   }
diff --git a/gdb/findvar.c b/gdb/findvar.c
index fcd97191c14..b7e8ded4fb6 100644
--- a/gdb/findvar.c
+++ b/gdb/findvar.c
@@ -869,6 +869,7 @@ read_frame_register_value (struct value *value, struct frame_info *frame)
   struct gdbarch *gdbarch = get_frame_arch (frame);
   LONGEST offset = 0;
   LONGEST reg_offset = value_offset (value);
+  LONGEST bit_offset = value_bitpos (value);
   int regnum = VALUE_REGNUM (value);
   int len = type_length_units (check_typedef (value_type (value)));
 
@@ -892,7 +893,8 @@ read_frame_register_value (struct value *value, struct frame_info *frame)
       if (reg_len > len)
 	reg_len = len;
 
-      value_contents_copy (value, offset, regval, reg_offset, reg_len);
+      value_contents_copy (value, offset, regval, reg_offset,
+			   bit_offset, reg_len);
 
       offset += reg_len;
       len -= reg_len;
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-llvm-offset.exp b/gdb/testsuite/gdb.dwarf2/dw2-llvm-offset.exp
new file mode 100644
index 00000000000..afbd123a16f
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-llvm-offset.exp
@@ -0,0 +1,328 @@
+# Copyright 2017-2020 Free Software Foundation, Inc.
+
+# 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/>.
+
+# Test DWARF operation that allow adding byte and bit offset to any
+# location description.
+#
+# In particular, the test uses memory and register location
+# descriptions (both as standalone and parts of the composite
+# location), and applies different byte and bit offsets to them.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# Choose suitable integer registers for the test.
+
+set dwarf_regnum 0
+
+if { [is_aarch64_target] } {
+    set regname x0
+} elseif { [is_aarch32_target]
+	   || [istarget "s390*-*-*" ]
+	   || [istarget "powerpc*-*-*"]
+	   || [istarget "rs6000*-*-aix*"] } {
+    set regname r0
+} elseif { [is_x86_like_target] } {
+    set regname eax
+} elseif { [is_amd64_regs_target] } {
+    set regname rax
+} else {
+    verbose "Skipping ${gdb_test_file_name}."
+    return
+}
+
+standard_testfile var-access.c ${gdb_test_file_name}-dw.S
+
+# Make some DWARF for the test.
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global dwarf_regnum regname srcdir subdir srcfile
+    set buf_var [gdb_target_symbol buf]
+
+    set main_result [function_range main ${srcdir}/${subdir}/${srcfile}]
+    set main_start [lindex $main_result 0]
+    set main_length [lindex $main_result 1]
+
+    cu {} {
+	DW_TAG_compile_unit {
+	    {DW_AT_name var-access.c}
+	    {DW_AT_comp_dir /tmp}
+	} {
+	    declare_labels char_type_label int_type_label
+	    declare_labels array_size_4_type_label array_size_8_type_label
+
+	    # define char type.
+	    char_type_label: DW_TAG_base_type {
+		{DW_AT_name "char"}
+		{DW_AT_encoding @DW_ATE_signed}
+		{DW_AT_byte_size 1 DW_FORM_sdata}
+	    }
+
+	    # define int type.
+	    int_type_label: DW_TAG_base_type {
+		{DW_AT_name "int"}
+		{DW_AT_encoding @DW_ATE_signed}
+		{DW_AT_byte_size 4 DW_FORM_sdata}
+	    }
+
+	    # define 4 byte size array type.
+	    array_size_4_type_label: DW_TAG_array_type {
+		{DW_AT_type :$char_type_label}
+	    } {
+		DW_TAG_subrange_type {
+		    {DW_AT_type :$int_type_label}
+		    {DW_AT_upper_bound 3 DW_FORM_udata}
+		}
+	    }
+
+	    # define 8 byte size array type.
+	    array_size_8_type_label: DW_TAG_array_type {
+		{DW_AT_type :$char_type_label}
+	    } {
+		DW_TAG_subrange_type {
+		    {DW_AT_type :$int_type_label}
+		    {DW_AT_upper_bound 7 DW_FORM_udata}
+		}
+	    }
+
+	    DW_TAG_subprogram {
+		{DW_AT_name main}
+		{DW_AT_low_pc $main_start addr}
+		{DW_AT_high_pc $main_length data8}
+	    } {
+		# define original buf variable.
+		DW_TAG_variable {
+		    {DW_AT_name buf}
+		    {DW_AT_type :$array_size_4_type_label}
+		    {DW_AT_location {
+			DW_OP_addr $buf_var
+		    } SPECIAL_expr}
+		}
+
+		# defined a variable located in
+		# a third byte of the buf variable.
+		DW_TAG_variable {
+		    {DW_AT_name buf_byte_3}
+		    {DW_AT_type :$char_type_label}
+		    {DW_AT_location {
+			DW_OP_addr $buf_var
+			DW_OP_LLVM_offset_constu 2
+		    } SPECIAL_expr}
+		    {external 1 flag}
+		}
+
+		# defined a variable located in a second byte
+		# of the buf variable with a bit offset of one.
+		DW_TAG_variable {
+		    {DW_AT_name buf_byte_2_bit_1}
+		    {DW_AT_type :$char_type_label}
+		    {DW_AT_location {
+			DW_OP_addr $buf_var
+			DW_OP_lit9
+			DW_OP_LLVM_bit_offset
+		    } SPECIAL_expr}
+		    {external 1 flag}
+		}
+
+		# defined a variable located in a
+		# third byte of the REGNAME register.
+		DW_TAG_variable {
+		    {DW_AT_name reg_byte_3}
+		    {DW_AT_type :$char_type_label}
+		    {DW_AT_location {
+			DW_OP_regx $dwarf_regnum
+			DW_OP_lit2
+			DW_OP_LLVM_offset
+		    } SPECIAL_expr}
+		    {external 1 flag}
+		}
+
+		# defined a variable located in a second byte of
+		# the REGNAME register with a bit offset of one.
+		DW_TAG_variable {
+		    {DW_AT_name reg_byte_2_bit_1}
+		    {DW_AT_type :$char_type_label}
+		    {DW_AT_location {
+			DW_OP_regx $dwarf_regnum
+			DW_OP_lit1
+			DW_OP_LLVM_offset
+			DW_OP_lit1
+			DW_OP_LLVM_bit_offset
+		    } SPECIAL_expr}
+		    {external 1 flag}
+		}
+
+		# Define an array variable spread in different
+		# pieces of buf variable and REGNAME register.
+		DW_TAG_variable {
+		    {DW_AT_name mix_array}
+		    {DW_AT_type :$array_size_8_type_label}
+		    {DW_AT_location {
+
+			# a byte piece located in a
+			# fourth byte of the buf variable.
+			DW_OP_addr $buf_var
+			DW_OP_LLVM_offset_constu 3
+			DW_OP_piece 0x1
+
+			# a byte piece located in a
+			# third byte of the buf variable.
+			DW_OP_addr $buf_var
+			DW_OP_lit2
+			DW_OP_LLVM_offset
+			DW_OP_piece 0x1
+
+			# a byte piece located in a second byte of
+			# the buf variable with a bit offset of one.
+			DW_OP_addr $buf_var
+			DW_OP_lit1
+			DW_OP_LLVM_offset
+			DW_OP_lit1
+			DW_OP_LLVM_bit_offset
+			DW_OP_piece 0x1
+
+			# a four bit piece located in a first byte
+			# of the buf variable with a bit offset of one.
+			DW_OP_addr $buf_var
+			DW_OP_LLVM_offset_constu 0
+			DW_OP_bit_piece 0x4 0x1
+
+			# a four bit piece located in a first byte of
+			# the buf variable with a bit offset of eight.
+			DW_OP_addr $buf_var
+			DW_OP_lit1
+			DW_OP_LLVM_bit_offset
+			DW_OP_LLVM_offset_constu 0
+			DW_OP_bit_piece 0x4 0x7
+
+			# a byte piece located in a fourth
+			# byte of the REGNAME register.
+			DW_OP_regx $dwarf_regnum
+			DW_OP_LLVM_offset_constu 3
+			DW_OP_piece 0x1
+
+			# a byte piece located in a third
+			# byte of the REGNAME register.
+			DW_OP_regx $dwarf_regnum
+			DW_OP_lit2
+			DW_OP_LLVM_offset
+			DW_OP_piece 0x1
+
+			# a byte piece located in a second byte of the
+			# REGNAME register with a bit offset of one.
+			DW_OP_regx $dwarf_regnum
+			DW_OP_lit1
+			DW_OP_LLVM_offset
+			DW_OP_lit1
+			DW_OP_LLVM_bit_offset
+			DW_OP_piece 0x1
+
+			# a four bit piece located in a first byte of
+			# the REGNAME register with a bit offset of one.
+			DW_OP_regx $dwarf_regnum
+			DW_OP_LLVM_offset_constu 0
+			DW_OP_bit_piece 0x4 0x1
+
+			# a four bit piece located in a first byte of the
+			# REGNAME register with a bit offset of eight.
+			DW_OP_regx $dwarf_regnum
+			DW_OP_lit1
+			DW_OP_LLVM_bit_offset
+			DW_OP_LLVM_offset_constu 0
+			DW_OP_bit_piece 0x4 0x7
+
+		    } SPECIAL_expr}
+		    {external 1 flag}
+		}
+	    }
+	}
+    }
+}
+
+if { [prepare_for_testing ${testfile}.exp ${testfile} \
+     [list $srcfile $asm_file] {nodebug}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+# Determine byte order.
+set endian [get_endianness]
+
+if { $endian != "little" } then {
+    verbose "Skipping ${gdb_test_file_name}."
+    return
+}
+
+gdb_test_no_output "set var \$$regname = 0x04030201" "init reg"
+gdb_test_no_output "set var *\(\(unsigned int *\) buf\) = 0x04030201" \
+		   "init buf"
+
+gdb_test "print/x buf_byte_3" " = 0x3" "buf_byte_3 == 0x3"
+gdb_test "print/x buf_byte_2_bit_1" " = 0x81" \
+	 "print buf_byte_2_bit_1"
+gdb_test "print/x reg_byte_3" " = 0x3" "reg_byte_3 == 0x3"
+gdb_test "print/x reg_byte_2_bit_1" " = 0x81" \
+	 "print reg_byte_2_bit_1"
+
+gdb_test_no_output "set var buf_byte_3 = 0x4" "init buf_byte_3 to 0x4"
+gdb_test "print/x buf_byte_3" " = 0x4" "buf_byte_3 == 0x4"
+
+gdb_test_no_output "set var buf_byte_2_bit_1 = 0x4" \
+		   "init buf_byte_2_bit_1 to 0x4"
+gdb_test "print/x buf_byte_2_bit_1" " = 0x4" "buf_byte_2_bit_1 == 0x4"
+
+gdb_test "print/x buf" " = \\{0x1, 0x8, 0x4, 0x4\\}" "buf print"
+
+gdb_test_no_output "set var reg_byte_3 = 0x4" "init reg_byte_3 to 0x4"
+gdb_test "print/x reg_byte_3" " = 0x4" "reg_byte_3 == 0x4"
+
+gdb_test_no_output "set var reg_byte_2_bit_1 = 0x4" \
+		   "init reg_byte_2_bit_1 to 0x4"
+gdb_test "print/x reg_byte_2_bit_1" " = 0x4" "reg_byte_2_bit_1 == 0x4"
+
+gdb_test "print/x \$$regname" " = 0x4040801" "\$$regname print"
+
+gdb_test_no_output "set var \$$regname = 0x04030201" "reset reg"
+gdb_test_no_output "set var *\(\(unsigned int *\) buf\) = 0x04030201" \
+		   "reset buf"
+
+gdb_test "print/x mix_array" \
+	 " = \\{0x4, 0x3, 0x81, 0x20, 0x4, 0x3, 0x81, 0x20\\}" \
+	 "mix_array print"
+
+gdb_test_no_output "set var mix_array\[1\] = 0x4" \
+		   "set mix_array second byte"
+gdb_test_no_output "set var mix_array\[2\] = 0x4" \
+		   "set mix_array third byte"
+gdb_test_no_output "set var mix_array\[5\] = 0x4" \
+		   "set mix_array fifth byte"
+gdb_test_no_output "set var mix_array\[6\] = 0x4" \
+		   "set mix_array sixth byte"
+
+gdb_test "print/x mix_array" \
+	 " = \\{0x4, 0x4, 0x4, 0x80, 0x4, 0x4, 0x4, 0x80\\}" \
+	 "mix_array second print"
+
+gdb_test "print/x buf" " = \\{0x1, 0x8, 0x4, 0x4\\}" "buf second print"
+
+gdb_test "print/x \$$regname" " = 0x4040801" "\$$regname second print"
diff --git a/gdb/testsuite/lib/dwarf.exp b/gdb/testsuite/lib/dwarf.exp
index a913cd48d67..cdb1c025ddc 100644
--- a/gdb/testsuite/lib/dwarf.exp
+++ b/gdb/testsuite/lib/dwarf.exp
@@ -1045,6 +1045,10 @@ namespace eval Dwarf {
 		    _op .sleb128 [lindex $line 1]
 		}
 
+		DW_OP_LLVM_offset_constu {
+		    _op .uleb128 [lindex $line 1]
+		}
+
 		default {
 		    if {[llength $line] > 1} {
 			error "Unimplemented: operands in location for $opcode"
diff --git a/gdb/valops.c b/gdb/valops.c
index fec821ad932..9e96af191a3 100644
--- a/gdb/valops.c
+++ b/gdb/valops.c
@@ -1031,20 +1031,31 @@ read_value_memory (struct value *val, LONGEST bit_offset,
   ULONGEST xfered_total = 0;
   struct gdbarch *arch = get_value_arch (val);
   int unit_size = gdbarch_addressable_memory_unit_size (arch);
+  bool big_endian = type_byte_order (value_type (val)) == BFD_ENDIAN_BIG;
   enum target_object object;
+  size_t extended_length
+    = length + (bit_offset + HOST_CHAR_BIT - 1) / HOST_CHAR_BIT;
+  gdb_byte *buffer_ptr = buffer;
+  gdb::byte_vector temp_buffer;
+
+  if (bit_offset)
+    {
+      temp_buffer.resize (extended_length);
+      buffer_ptr = temp_buffer.data ();
+    }
 
   object = stack ? TARGET_OBJECT_STACK_MEMORY : TARGET_OBJECT_MEMORY;
 
-  while (xfered_total < length)
+  while (xfered_total < extended_length)
     {
       enum target_xfer_status status;
       ULONGEST xfered_partial;
 
       status = target_xfer_partial (current_top_target (),
 				    object, NULL,
-				    buffer + xfered_total * unit_size, NULL,
+				    buffer_ptr + xfered_total * unit_size, NULL,
 				    memaddr + xfered_total,
-				    length - xfered_total,
+				    extended_length - xfered_total,
 				    &xfered_partial);
 
       if (status == TARGET_XFER_OK)
@@ -1061,6 +1072,10 @@ read_value_memory (struct value *val, LONGEST bit_offset,
       xfered_total += xfered_partial;
       QUIT;
     }
+
+  if (bit_offset)
+    copy_bitwise (buffer, 0, temp_buffer.data (),
+		  bit_offset, length * HOST_CHAR_BIT, big_endian);
 }
 
 /* Store the contents of FROMVAL into the location of TOVAL.
@@ -1132,7 +1147,7 @@ value_assign (struct value *toval, struct value *fromval)
 	const gdb_byte *dest_buffer;
 	CORE_ADDR changed_addr;
 	int changed_len;
-	gdb_byte buffer[sizeof (LONGEST)];
+	gdb::byte_vector buffer;
 
 	if (value_bitsize (toval))
 	  {
@@ -1158,10 +1173,25 @@ value_assign (struct value *toval, struct value *fromval)
 		       "don't fit in a %d bit word."),
 		     (int) sizeof (LONGEST) * HOST_CHAR_BIT);
 
-	    read_memory (changed_addr, buffer, changed_len);
-	    modify_field (type, buffer, value_as_long (fromval),
+	    buffer.resize (changed_len);
+
+	    read_memory (changed_addr, buffer.data (), changed_len);
+	    modify_field (type, buffer.data (), value_as_long (fromval),
 			  value_bitpos (toval), value_bitsize (toval));
-	    dest_buffer = buffer;
+	    dest_buffer = buffer.data ();
+	  }
+	else if (value_bitpos (toval))
+	  {
+	    int bitpos = value_bitpos (toval);
+	    bool big_endian = type_byte_order (type) == BFD_ENDIAN_BIG;
+	    changed_addr = value_address (toval);
+	    changed_len = TYPE_LENGTH (type)
+			  + (bitpos + HOST_CHAR_BIT - 1) / HOST_CHAR_BIT;
+	    buffer.resize (changed_len);
+	    read_memory (changed_addr, buffer.data (), changed_len);
+	    copy_bitwise (buffer.data (), bitpos, value_contents (fromval),
+			  0, TYPE_LENGTH (type) * HOST_CHAR_BIT, big_endian);
+	    dest_buffer = buffer.data();
 	  }
 	else
 	  {
@@ -1176,10 +1206,6 @@ value_assign (struct value *toval, struct value *fromval)
 
     case lval_register:
       {
-	struct frame_info *frame;
-	struct gdbarch *gdbarch;
-	int value_reg;
-
 	/* Figure out which frame this is in currently.
 	
 	   We use VALUE_FRAME_ID for obtaining the value's frame id instead of
@@ -1187,36 +1213,47 @@ value_assign (struct value *toval, struct value *fromval)
 	   put_frame_register_bytes() below.  That function will (eventually)
 	   perform the necessary unwind operation by first obtaining the next
 	   frame.  */
-	frame = frame_find_by_id (VALUE_FRAME_ID (toval));
-
-	value_reg = VALUE_REGNUM (toval);
+	struct frame_info *frame = frame_find_by_id (VALUE_FRAME_ID (toval));
 
 	if (!frame)
 	  error (_("Value being assigned to is no longer active."));
 
-	gdbarch = get_frame_arch (frame);
+	struct gdbarch *gdbarch = get_frame_arch (frame);
+	int value_reg = VALUE_REGNUM (toval);
+	LONGEST bitpos = value_bitpos (toval);
+	LONGEST bitsize = value_bitsize (toval);
+	LONGEST offset = value_offset (toval);
 
-	if (value_bitsize (toval))
+	if (bitpos || bitsize)
 	  {
-	    struct value *parent = value_parent (toval);
-	    LONGEST offset = value_offset (parent) + value_offset (toval);
-	    size_t changed_len;
-	    gdb_byte buffer[sizeof (LONGEST)];
-	    int optim, unavail;
+	    int changed_len;
+	    bool big_endian = type_byte_order (type) == BFD_ENDIAN_BIG;
 
-	    changed_len = (value_bitpos (toval)
-			   + value_bitsize (toval)
-			   + HOST_CHAR_BIT - 1)
-			  / HOST_CHAR_BIT;
+	    if (bitsize)
+	      {
+		offset += value_offset (value_parent (toval));
 
-	    if (changed_len > sizeof (LONGEST))
-	      error (_("Can't handle bitfields which "
-		       "don't fit in a %d bit word."),
-		     (int) sizeof (LONGEST) * HOST_CHAR_BIT);
+		changed_len = (bitpos + bitsize + HOST_CHAR_BIT - 1)
+			      / HOST_CHAR_BIT;
+
+		if (changed_len > (int) sizeof (LONGEST))
+		  error (_("Can't handle bitfields which "
+			   "don't fit in a %d bit word."),
+			   (int) sizeof (LONGEST) * HOST_CHAR_BIT);
+	      }
+	    else
+	      {
+		changed_len = TYPE_LENGTH (type)
+			      + (bitpos + HOST_CHAR_BIT - 1) / HOST_CHAR_BIT;
+
+		bitsize = TYPE_LENGTH (type) * HOST_CHAR_BIT;
+	      }
+
+	    gdb::byte_vector buffer (changed_len);
+	    int optim, unavail;
 
 	    if (!get_frame_register_bytes (frame, value_reg, offset,
-					   {buffer, changed_len},
-					   &optim, &unavail))
+					   buffer, &optim, &unavail))
 	      {
 		if (optim)
 		  throw_error (OPTIMIZED_OUT_ERROR,
@@ -1226,11 +1263,10 @@ value_assign (struct value *toval, struct value *fromval)
 			       _("value is not available"));
 	      }
 
-	    modify_field (type, buffer, value_as_long (fromval),
-			  value_bitpos (toval), value_bitsize (toval));
+	    copy_bitwise (buffer.data (), bitpos, value_contents (fromval),
+			  0, bitsize, big_endian);
 
-	    put_frame_register_bytes (frame, value_reg, offset,
-				      {buffer, changed_len});
+	    put_frame_register_bytes (frame, value_reg, offset, buffer);
 	  }
 	else
 	  {
@@ -1250,8 +1286,7 @@ value_assign (struct value *toval, struct value *fromval)
 		  = gdb::make_array_view (value_contents (fromval),
 					  TYPE_LENGTH (type));
 		put_frame_register_bytes (frame, value_reg,
-					  value_offset (toval),
-					  contents);
+					  offset, contents);
 	      }
 	  }
 
@@ -1352,21 +1387,22 @@ value_assign (struct value *toval, struct value *fromval)
 struct value *
 value_repeat (struct value *arg1, int count)
 {
-  struct value *val;
-
   if (VALUE_LVAL (arg1) != lval_memory)
     error (_("Only values in memory can be extended with '@'."));
   if (count < 1)
     error (_("Invalid number %d of repetitions."), count);
 
-  val = allocate_repeat_value (value_enclosing_type (arg1), count);
+  struct value *val
+    = allocate_repeat_value (value_enclosing_type (arg1), count);
 
   VALUE_LVAL (val) = lval_memory;
   set_value_address (val, value_address (arg1));
+  set_value_bitpos (val, value_bitpos (arg1));
+  struct type *enclosing_type = value_enclosing_type (val);
 
-  read_value_memory (val, 0, value_stack (val), value_address (val),
-		     value_contents_all_raw (val),
-		     type_length_units (value_enclosing_type (val)));
+  read_value_memory (val, value_bitpos (val), value_stack (val),
+		     value_address (val), value_contents_all_raw (val),
+		     type_length_units (enclosing_type));
 
   return val;
 }
@@ -1715,7 +1751,7 @@ value_array (int lowbound, int highbound, struct value **elemvec)
     {
       val = allocate_value (arraytype);
       for (idx = 0; idx < nelem; idx++)
-	value_contents_copy (val, idx * typelength, elemvec[idx], 0,
+	value_contents_copy (val, idx * typelength, elemvec[idx], 0, 0,
 			     typelength);
       return val;
     }
@@ -1725,7 +1761,8 @@ value_array (int lowbound, int highbound, struct value **elemvec)
 
   val = allocate_value (arraytype);
   for (idx = 0; idx < nelem; idx++)
-    value_contents_copy (val, idx * typelength, elemvec[idx], 0, typelength);
+    value_contents_copy (val, idx * typelength, elemvec[idx], 0, 0,
+			 typelength);
   return val;
 }
 
@@ -3992,7 +4029,7 @@ value_slice (struct value *array, int lowbound, int length)
     else
       {
 	slice = allocate_value (slice_type);
-	value_contents_copy (slice, 0, array, offset,
+	value_contents_copy (slice, 0, array, offset, 0,
 			     type_length_units (slice_type));
       }
 
diff --git a/gdb/value.c b/gdb/value.c
index 52f55f0bb97..437e4df3e01 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -1306,9 +1306,10 @@ value_ranges_copy_adjusted (struct value *dst, int dst_bit_offset,
 
 static void
 value_contents_copy_raw (struct value *dst, LONGEST dst_offset,
-			 struct value *src, LONGEST src_offset, LONGEST length)
+			 struct value *src, LONGEST src_offset,
+			 LONGEST src_bit_offset, LONGEST length)
 {
-  LONGEST src_bit_offset, dst_bit_offset, bit_length;
+  LONGEST src_total_bit_offset, dst_total_bit_offset, bit_length;
   struct gdbarch *arch = get_value_arch (src);
   int unit_size = gdbarch_addressable_memory_unit_size (arch);
 
@@ -1327,17 +1328,29 @@ value_contents_copy_raw (struct value *dst, LONGEST dst_offset,
 					     TARGET_CHAR_BIT * length));
 
   /* Copy the data.  */
-  memcpy (value_contents_all_raw (dst) + dst_offset * unit_size,
-	  value_contents_all_raw (src) + src_offset * unit_size,
-	  length * unit_size);
+  bit_length = length * unit_size * HOST_CHAR_BIT;
+
+  if (src_bit_offset)
+    {
+      bool big_endian = type_byte_order (value_type (dst)) == BFD_ENDIAN_BIG;
+
+      copy_bitwise (value_contents_all_raw (dst) + dst_offset * unit_size, 0,
+		    value_contents_all_raw (src) + src_offset * unit_size,
+		    src_bit_offset, bit_length, big_endian);
+    }
+  else
+    memcpy (value_contents_all_raw (dst) + dst_offset * unit_size,
+	    value_contents_all_raw (src) + src_offset * unit_size,
+	    length * unit_size);
 
   /* Copy the meta-data, adjusted.  */
-  src_bit_offset = src_offset * unit_size * HOST_CHAR_BIT;
-  dst_bit_offset = dst_offset * unit_size * HOST_CHAR_BIT;
-  bit_length = length * unit_size * HOST_CHAR_BIT;
+  src_total_bit_offset = src_offset * unit_size * HOST_CHAR_BIT
+			 + src_bit_offset;
+  dst_total_bit_offset = dst_offset * unit_size * HOST_CHAR_BIT;
+
 
-  value_ranges_copy_adjusted (dst, dst_bit_offset,
-			      src, src_bit_offset,
+  value_ranges_copy_adjusted (dst, dst_total_bit_offset,
+			      src, src_total_bit_offset,
 			      bit_length);
 }
 
@@ -1353,12 +1366,14 @@ value_contents_copy_raw (struct value *dst, LONGEST dst_offset,
 
 void
 value_contents_copy (struct value *dst, LONGEST dst_offset,
-		     struct value *src, LONGEST src_offset, LONGEST length)
+		     struct value *src, LONGEST src_offset,
+		     LONGEST src_bit_offset, LONGEST length)
 {
   if (src->lazy)
     value_fetch_lazy (src);
 
-  value_contents_copy_raw (dst, dst_offset, src, src_offset, length);
+  value_contents_copy_raw (dst, dst_offset, src, src_offset,
+			   src_bit_offset, length);
 }
 
 int
@@ -3050,7 +3065,7 @@ value_primitive_field (struct value *arg1, LONGEST offset,
       else
 	{
 	  v = allocate_value (value_enclosing_type (arg1));
-	  value_contents_copy_raw (v, 0, arg1, 0,
+	  value_contents_copy_raw (v, 0, arg1, 0, 0,
 				   TYPE_LENGTH (value_enclosing_type (arg1)));
 	}
       v->type = type;
@@ -3085,7 +3100,7 @@ value_primitive_field (struct value *arg1, LONGEST offset,
 	  v = allocate_value (type);
 	  value_contents_copy_raw (v, value_embedded_offset (v),
 				   arg1, value_embedded_offset (arg1) + offset,
-				   type_length_units (type));
+				   0, type_length_units (type));
 	}
       v->offset = (value_offset (arg1) + offset
 		   + value_embedded_offset (arg1));
@@ -3679,7 +3694,7 @@ value_from_component (struct value *whole, struct type *type, LONGEST offset)
       v = allocate_value (type);
       value_contents_copy (v, value_embedded_offset (v),
 			   whole, value_embedded_offset (whole) + offset,
-			   type_length_units (type));
+			   0, type_length_units (type));
     }
   v->offset = value_offset (whole) + offset + value_embedded_offset (whole);
   set_value_component_location (v, whole);
@@ -3862,9 +3877,9 @@ value_fetch_lazy_memory (struct value *val)
   struct type *type = check_typedef (value_enclosing_type (val));
 
   if (TYPE_LENGTH (type))
-      read_value_memory (val, 0, value_stack (val),
-			 addr, value_contents_all_raw (val),
-			 type_length_units (type));
+    read_value_memory (val, value_bitpos (val), value_stack (val),
+		       addr, value_contents_all_raw (val),
+		       type_length_units (type));
 }
 
 /* Helper for value_fetch_lazy when the value is in a register.  */
@@ -3877,10 +3892,6 @@ value_fetch_lazy_register (struct value *val)
   struct type *type = check_typedef (value_type (val));
   struct value *new_val = val, *mark = value_mark ();
 
-  /* Offsets are not supported here; lazy register values must
-     refer to the entire register.  */
-  gdb_assert (value_offset (val) == 0);
-
   while (VALUE_LVAL (new_val) == lval_register && value_lazy (new_val))
     {
       struct frame_id next_frame_id = VALUE_NEXT_FRAME_ID (new_val);
@@ -3923,6 +3934,11 @@ value_fetch_lazy_register (struct value *val)
 			_("infinite loop while fetching a register"));
     }
 
+  /* Check if NEW_VALUE is big enough to cover
+     the expected VAL type with an offset.  */
+  gdb_assert ((TYPE_LENGTH (type) + value_offset (val))
+	      <= TYPE_LENGTH (value_type (new_val)));
+
   /* If it's still lazy (for instance, a saved register on the
      stack), fetch it.  */
   if (value_lazy (new_val))
@@ -3931,9 +3947,9 @@ value_fetch_lazy_register (struct value *val)
   /* Copy the contents and the unavailability/optimized-out
      meta-data from NEW_VAL to VAL.  */
   set_value_lazy (val, 0);
-  value_contents_copy (val, value_embedded_offset (val),
-		       new_val, value_embedded_offset (new_val),
-		       type_length_units (type));
+  value_contents_copy (val, value_embedded_offset (val), new_val,
+		       value_embedded_offset (new_val) + value_offset (val),
+		       value_bitpos (val), type_length_units (type));
 
   if (frame_debug)
     {
diff --git a/gdb/value.h b/gdb/value.h
index cbb60e2f073..240041b4406 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -740,7 +740,7 @@ extern struct value *allocate_value (struct type *type);
 extern struct value *allocate_value_lazy (struct type *type);
 extern void value_contents_copy (struct value *dst, LONGEST dst_offset,
 				 struct value *src, LONGEST src_offset,
-				 LONGEST length);
+				 LONGEST src_bit_offset, LONGEST length);
 
 extern struct value *allocate_repeat_value (struct type *type, int count);
 
diff --git a/include/dwarf2.def b/include/dwarf2.def
index 1ae6e1df298..5e622695d60 100644
--- a/include/dwarf2.def
+++ b/include/dwarf2.def
@@ -704,6 +704,10 @@ DW_OP (DW_OP_PGI_omp_thread_num, 0xf8)
    to 0 except explicitly documented for one action.  Please refer AArch64 DWARF
    ABI documentation for details.  */
 DW_OP (DW_OP_AARCH64_operation, 0xea)
+/* LLVM extensions for heterogeneous targets */
+DW_OP_DUP (DW_OP_LLVM_offset, 0xe3)
+DW_OP_DUP (DW_OP_LLVM_offset_constu, 0xe4)
+DW_OP_DUP (DW_OP_LLVM_bit_offset, 0xe5)
 DW_END_OP
 
 DW_FIRST_ATE (DW_ATE_void, 0x0)
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 42/43] Add support for DW_OP_LLVM_undefined operation
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (40 preceding siblings ...)
  2021-03-01 14:46 ` [PATCH 41/43] Add DWARF operations for byte and bit offset Zoran Zaric
@ 2021-03-01 14:46 ` Zoran Zaric
  2021-03-01 14:46 ` [PATCH 43/43] Add support for nested composite locations Zoran Zaric
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:46 UTC (permalink / raw)
  To: gdb-patches

For the DW_OP_piece and DW_OP_bit_piece operations, in the DWARF 5
standard, it is stated that if the location description (of that piece)
is empty, then the piece is describing an undefined location
description.

The act of allowing any location description to be placed on a DWARF
stack means that now a new operations can be defined which could pop
more then one location description from a DWARF stack.

This means that the old rule is not really applicable any more and a
new operation that explicitly pushes an undefined location description
on the DWARF stack is needed.

This new rule however is fully backward compatibility as described
in the document found on:

https://llvm.org/docs/AMDGPUDwarfExtensionsForHeterogeneousDebugging.html

Under the new definitions for the DW_OP_piece and DW_OP_bit_piece
operations.

gdb/ChangeLog:

	* compile/compile-loc2c.c (compute_stack_depth_worker): Add
	support for new DW_OP_LLVM_undefined operations.
	* dwarf2/expr.c (dwarf_expr_context::execute_stack_op): Add
	support for new DW_OP_LLVM_undefined operations.

include/ChangeLog:

	* dwarf2.def (DW_OP): New DW_OP_LLVM_undefined operations
	enumeration.

gdb/testsuite/ChangeLog:

	* gdb.dwarf2/dw2-llvm-undefined.exp: New test.
---
 gdb/compile/compile-loc2c.c                   |   4 +
 gdb/dwarf2/expr.c                             |   4 +
 .../gdb.dwarf2/dw2-llvm-undefined.exp         | 144 ++++++++++++++++++
 include/dwarf2.def                            |   1 +
 4 files changed, 153 insertions(+)
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-llvm-undefined.exp

diff --git a/gdb/compile/compile-loc2c.c b/gdb/compile/compile-loc2c.c
index 58558fa7d22..057d68823f9 100644
--- a/gdb/compile/compile-loc2c.c
+++ b/gdb/compile/compile-loc2c.c
@@ -361,6 +361,10 @@ compute_stack_depth_worker (int start, int *need_tempvar,
 	  --stack_depth;
 	  break;
 
+	case DW_OP_LLVM_undefined:
+	  ++stack_depth;
+	  break;
+
 	case DW_OP_LLVM_offset_constu:
 	case DW_OP_nop:
 	  break;
diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 19e5f5e93b5..ec7837e1935 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -3838,6 +3838,10 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	  }
 	  break;
 
+	case DW_OP_LLVM_undefined:
+	  result_entry = std::make_shared<dwarf_undefined> (this->gdbarch);
+	  break;
+
 	default:
 	  error (_("Unhandled dwarf expression opcode 0x%x"), op);
 	}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-llvm-undefined.exp b/gdb/testsuite/gdb.dwarf2/dw2-llvm-undefined.exp
new file mode 100644
index 00000000000..be677a1ddce
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-llvm-undefined.exp
@@ -0,0 +1,144 @@
+# Copyright 2017-2020 Free Software Foundation, Inc.
+
+# 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/>.
+
+# Test the new DW_OP_LLVM_undefined operation.
+#
+# The test uses a composite location description, where some pieces
+# of that location description are undefined location description.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# Choose suitable integer registers for the test.
+
+set dwarf_regnum 0
+
+if { [is_aarch64_target] } {
+    set regname x0
+} elseif { [is_aarch32_target]
+	   || [istarget "s390*-*-*" ]
+	   || [istarget "powerpc*-*-*"]
+	   || [istarget "rs6000*-*-aix*"] } {
+    set regname r0
+} elseif { [is_x86_like_target] } {
+    set regname eax
+} elseif { [is_amd64_regs_target] } {
+    set regname rax
+} else {
+    verbose "Skipping ${gdb_test_file_name}."
+    return
+}
+
+standard_testfile var-access.c ${gdb_test_file_name}-dw.S
+
+# Make some DWARF for the test.
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global dwarf_regnum regname srcdir subdir srcfile
+    set buf_src [gdb_target_symbol buf]
+
+    set main_result [function_range main ${srcdir}/${subdir}/${srcfile}]
+    set main_start [lindex $main_result 0]
+    set main_length [lindex $main_result 1]
+
+    cu {} {
+	DW_TAG_compile_unit {
+	    {DW_AT_name var-access.c}
+	    {DW_AT_comp_dir /tmp}
+	} {
+	    declare_labels int_type_label char_type_label array_type_label
+
+	    # define char type
+	    char_type_label: DW_TAG_base_type {
+		{DW_AT_name "char"}
+		{DW_AT_encoding @DW_ATE_signed}
+		{DW_AT_byte_size 1 DW_FORM_sdata}
+	    }
+
+	    int_type_label: DW_TAG_base_type {
+		{DW_AT_name "int"}
+		{DW_AT_encoding @DW_ATE_signed}
+		{DW_AT_byte_size 4 DW_FORM_sdata}
+	    }
+
+	    array_type_label: DW_TAG_array_type {
+		{DW_AT_type :$char_type_label}
+	    } {
+		DW_TAG_subrange_type {
+		    {DW_AT_type :$int_type_label}
+		    {DW_AT_upper_bound 5 DW_FORM_udata}
+		}
+	    }
+
+	    DW_TAG_subprogram {
+		{DW_AT_name main}
+		{DW_AT_low_pc $main_start addr}
+		{DW_AT_high_pc $main_length data8}
+	    } {
+
+		# Array spread in different pieces of which some are
+		# undefined (1st and sixth bytes) and some are in a
+		# REGNAME register.
+		DW_TAG_variable {
+		    {DW_AT_name var_array}
+		    {DW_AT_type :$array_type_label}
+		    {DW_AT_location {
+			DW_OP_LLVM_undefined
+			DW_OP_piece 0x1
+			DW_OP_regx $dwarf_regnum
+			DW_OP_piece 0x4
+			DW_OP_LLVM_undefined
+			DW_OP_piece 0x1
+		    } SPECIAL_expr}
+		}
+
+		DW_TAG_variable {
+		    {DW_AT_name var_int}
+		    {DW_AT_type :$int_type_label}
+		    {DW_AT_location {
+			DW_OP_LLVM_undefined
+		    } SPECIAL_expr}
+		}
+	    }
+	}
+    }
+}
+
+if { [prepare_for_testing ${testfile}.exp ${testfile} \
+     [list $srcfile $asm_file] {nodebug}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+gdb_test_no_output "set var \$$regname = 0x04030201" "init reg"
+
+# Determine byte order.
+set endian [get_endianness]
+
+switch $endian {
+	little {set val "<optimized out>, 0x1, 0x2, 0x3, 0x4, <optimized out>"}
+	big {set val "<optimized out>, 0x4, 0x3, 0x2, 0x1, <optimized out>"}
+}
+
+gdb_test "print/x var_array" " = \\{${val}\\}" "var_array print"
+gdb_test "print/x var_int" " = <optimized out>" "var_int print"
diff --git a/include/dwarf2.def b/include/dwarf2.def
index 5e622695d60..1b626362ec4 100644
--- a/include/dwarf2.def
+++ b/include/dwarf2.def
@@ -708,6 +708,7 @@ DW_OP (DW_OP_AARCH64_operation, 0xea)
 DW_OP_DUP (DW_OP_LLVM_offset, 0xe3)
 DW_OP_DUP (DW_OP_LLVM_offset_constu, 0xe4)
 DW_OP_DUP (DW_OP_LLVM_bit_offset, 0xe5)
+DW_OP (DW_OP_LLVM_undefined, 0xe7)
 DW_END_OP
 
 DW_FIRST_ATE (DW_ATE_void, 0x0)
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* [PATCH 43/43] Add support for nested composite locations
  2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
                   ` (41 preceding siblings ...)
  2021-03-01 14:46 ` [PATCH 42/43] Add support for DW_OP_LLVM_undefined operation Zoran Zaric
@ 2021-03-01 14:46 ` Zoran Zaric
  42 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-03-01 14:46 UTC (permalink / raw)
  To: gdb-patches

After allowing a location description to be placed on a DWARF stack,
in an effort to achieve a full composability of the DWARF expression,
it is necessary to enable forming of a nested composite location
descriptions.

To be able do this, a new operation DW_OP_LLVM_piece_end needs to be
introduced, along with some additional rules on the way how the
composite location description is formed using the existing DW_OP_piece
and DW_OP_bit_piece operations. These new rules are fully compatible
with the composite forming rules from the DWARF 5 standard.

More details on the new operation and added rules can be found here:

https://llvm.org/docs/AMDGPUDwarfExtensionsForHeterogeneousDebugging.html

The dwarf_composite also needed to be modified to make a distinction
between completed composite locationd description and not completed
one.

This also mean that some DWARF expression operations can duplicate a
composite location description that is not completed and end up with
more then one different composite location description on the stack.
To be able to do this, classes that derive from a DWARF entry class
need to have a clone method.

gdb/ChangeLog:

        * compile/compile-loc2c.c (compute_stack_depth_worker): Add
        new DW_OP_LLVM_piece_end operation support.
        * dwarf2/expr.c (dwarf_composite::m_completed): New data
        member.
        (dwarf_entry::dwarf_entry): New copy constructor.
        (dwarf_location::dwarf_location): New copy constructor.
        (dwarf_value::dwarf_value): New copy constructor.
        (dwarf_undefined::dwarf_undefined): New copy constructor.
        (dwarf_memory::dwarf_memory): New copy constructor.
        (dwarf_register::dwarf_register): New copy constructor.
        (dwarf_implicit::dwarf_implicit): New method.
        (dwarf_implicit_pointer::dwarf_implicit_pointer): New copy
        constructor.
        (dwarf_composite::dwarf_composite): New copy constructor.
        (dwarf_entry::clone): New method.
        (dwarf_location::clone): New method.
        (dwarf_value::clone): New method.
        (dwarf_undefined::clone): New method.
        (dwarf_memory::clone): New method.
        (dwarf_register::clone): New method.
        (dwarf_implicit::clone): New method.
        (dwarf_implicit_pointer::clone): New method.
        (dwarf_composite::clone): New method.
        (dwarf_composite::is_completed): New method.
        (dwarf_composite::set_completed): New method.
        (dwarf_expr_context::add_piece): Use new composite forming
        rules.
        (dwarf_expr_context::execute_stack_op): Add new
        DW_OP_LLVM_piece_end operation support.

    include/ChangeLog:

        * dwarf2.def (DW_OP_DUP): Add new DW_OP_LLVM_piece_end
        enumeration.

    gdb/testsuite/ChangeLog:

        * gdb.dwarf2/dw2-llvm-piece-end.exp: New test.
---
 gdb/compile/compile-loc2c.c                   |   1 +
 gdb/dwarf2/expr.c                             | 206 ++++++++++++++++--
 .../gdb.dwarf2/dw2-llvm-piece-end.exp         | 192 ++++++++++++++++
 include/dwarf2.def                            |   1 +
 4 files changed, 384 insertions(+), 16 deletions(-)
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-llvm-piece-end.exp

diff --git a/gdb/compile/compile-loc2c.c b/gdb/compile/compile-loc2c.c
index 057d68823f9..86a8fb9e354 100644
--- a/gdb/compile/compile-loc2c.c
+++ b/gdb/compile/compile-loc2c.c
@@ -365,6 +365,7 @@ compute_stack_depth_worker (int start, int *need_tempvar,
 	  ++stack_depth;
 	  break;
 
+	case DW_OP_LLVM_piece_end:
 	case DW_OP_LLVM_offset_constu:
 	case DW_OP_nop:
 	  break;
diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index ec7837e1935..8b80d1fd09f 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -402,6 +402,9 @@ class dwarf_entry : public std::enable_shared_from_this<dwarf_entry>
 
   virtual ~dwarf_entry () = 0;
 
+  /* Clones entry.  */
+  virtual std::shared_ptr<dwarf_entry> clone () const = 0;
+
   /* Convert DWARF entry into a DWARF location description.  ARCH
      defines an architecture of the location described.   */
   virtual std::shared_ptr<dwarf_location> to_location
@@ -446,6 +449,13 @@ class dwarf_location : public dwarf_entry
     m_bit_suboffset = bit_suboffset % HOST_CHAR_BIT;
   }
 
+  dwarf_location (const dwarf_location &location)
+    : m_arch (location.m_arch),
+      m_offset (location.m_offset),
+      m_bit_suboffset (location.m_bit_suboffset),
+      m_initialised (location.m_initialised)
+  {}
+
   virtual ~dwarf_location () = default;
 
   /* Add bit offset to the location description.  */
@@ -725,6 +735,22 @@ class dwarf_value : public dwarf_entry
     m_type = type;
   }
 
+  dwarf_value (const dwarf_value &value)
+  {
+    struct type *type = value.m_type;
+    size_t type_len = TYPE_LENGTH (type);
+
+    m_contents.reset ((gdb_byte *) xzalloc (type_len));
+
+    memcpy (m_contents.get (), value.m_contents.get (), type_len);
+    m_type = type;
+  }
+
+  std::shared_ptr<dwarf_entry> clone () const override
+  {
+    return std::make_shared<dwarf_value> (*this);
+  }
+
   const gdb_byte* get_contents () const
   {
     return m_contents.get ();
@@ -822,6 +848,15 @@ class dwarf_undefined : public dwarf_location
     : dwarf_location (arch, offset, bit_suboffset)
   {}
 
+  dwarf_undefined (const dwarf_undefined &undefined)
+    : dwarf_location (undefined)
+  {}
+
+  std::shared_ptr<dwarf_entry> clone () const override
+  {
+    return std::make_shared<dwarf_undefined> (*this);
+  }
+
   void read (struct frame_info *frame, gdb_byte *buf, int buf_bit_offset,
 	     size_t bit_size, LONGEST bits_to_skip, size_t location_bit_limit,
 	     bool big_endian, int *optimized, int *unavailable) const override
@@ -863,6 +898,16 @@ class dwarf_memory : public dwarf_location
       m_stack (stack)
   {}
 
+  dwarf_memory (const dwarf_memory &memory)
+    : dwarf_location (memory),
+      m_stack (memory.m_stack)
+  {}
+
+  std::shared_ptr<dwarf_entry> clone () const override
+  {
+    return std::make_shared<dwarf_memory> (*this);
+  }
+
   void set_stack (bool stack)
   {
     m_stack = stack;
@@ -1094,6 +1139,16 @@ class dwarf_register : public dwarf_location
       m_regnum (regnum)
   {}
 
+  dwarf_register (const dwarf_register &registr)
+    : dwarf_location (registr),
+      m_regnum (registr.m_regnum)
+  {}
+
+  std::shared_ptr<dwarf_entry> clone () const override
+  {
+    return std::make_shared<dwarf_register> (*this);
+  }
+
   void read (struct frame_info *frame, gdb_byte *buf, int buf_bit_offset,
 	     size_t bit_size, LONGEST bits_to_skip, size_t location_bit_limit,
 	     bool big_endian, int *optimized, int *unavailable) const override;
@@ -1263,6 +1318,22 @@ class dwarf_implicit : public dwarf_location
     m_byte_order = byte_order;
   }
 
+  dwarf_implicit (const dwarf_implicit &implicit)
+    : dwarf_location (implicit)
+  {
+    size_t size = implicit.m_size;
+    m_contents.reset ((gdb_byte *) xzalloc (size));
+
+    memcpy (m_contents.get (), implicit.m_contents.get (), size);
+    m_size = size;
+    m_byte_order = implicit.m_byte_order;
+  }
+
+  std::shared_ptr<dwarf_entry> clone () const override
+  {
+    return std::make_shared<dwarf_implicit> (*this);
+  }
+
   void read (struct frame_info *frame, gdb_byte *buf, int buf_bit_offset,
 	     size_t bit_size, LONGEST bits_to_skip, size_t location_bit_limit,
 	     bool big_endian, int *optimized, int *unavailable) const override;
@@ -1375,6 +1446,19 @@ class dwarf_implicit_pointer : public dwarf_location
       m_addr_size (addr_size), m_die_offset (die_offset)
   {}
 
+  dwarf_implicit_pointer (const dwarf_implicit_pointer &implicit_ptr)
+    : dwarf_location (implicit_ptr),
+      m_per_objfile (implicit_ptr.m_per_objfile),
+      m_per_cu (implicit_ptr.m_per_cu),
+      m_addr_size (implicit_ptr.m_addr_size),
+      m_die_offset (implicit_ptr.m_die_offset)
+  {}
+
+  std::shared_ptr<dwarf_entry> clone () const override
+  {
+    return std::make_shared<dwarf_implicit_pointer> (*this);
+  }
+
   void read (struct frame_info *frame, gdb_byte *buf, int buf_bit_offset,
 	     size_t bit_size, LONGEST bits_to_skip, size_t location_bit_limit,
 	     bool big_endian, int *optimized, int *unavailable) const override;
@@ -1518,12 +1602,39 @@ class dwarf_composite : public dwarf_location
     : dwarf_location (arch, offset, bit_suboffset), m_per_cu (per_cu)
   {}
 
+  dwarf_composite (const dwarf_composite &composite)
+    : dwarf_location (composite), m_per_cu (composite.m_per_cu)
+  {
+    /* We do a shallow copy of the pieces because they are not
+       expected to be modified after they are already formed.  */
+    for (unsigned int i = 0; i < composite.m_pieces.size (); i++)
+      m_pieces.emplace_back (composite.m_pieces[i].m_location,
+			     composite.m_pieces[i].m_size);
+
+    m_completed = composite.m_completed;
+  }
+
+  std::shared_ptr<dwarf_entry> clone () const override
+  {
+    return std::make_shared<dwarf_composite> (*this);
+  }
+
   void add_piece (std::shared_ptr<dwarf_location> location, ULONGEST bit_size)
   {
     gdb_assert (location != nullptr);
     m_pieces.emplace_back (location, bit_size);
   }
 
+  void set_completed (bool completed)
+  {
+    m_completed = completed;
+  };
+
+  bool is_completed () const
+  {
+    return m_completed;
+  };
+
   void read (struct frame_info *frame, gdb_byte *buf, int buf_bit_offset,
 	     size_t bit_size, LONGEST bits_to_skip, size_t location_bit_limit,
 	     bool big_endian, int *optimized, int *unavailable) const override;
@@ -1575,6 +1686,9 @@ class dwarf_composite : public dwarf_location
 
   /* Vector of composite pieces.  */
   std::vector<struct piece> m_pieces;
+
+  /* True if location description is completed.  */
+  bool m_completed = false;
 };
 
 void
@@ -1587,6 +1701,9 @@ dwarf_composite::read (struct frame_info *frame, gdb_byte *buf,
   LONGEST total_bits_to_skip = bits_to_skip;
   unsigned int i;
 
+  if (!m_completed)
+    ill_formed_expression ();
+
   total_bits_to_skip += m_offset * HOST_CHAR_BIT + m_bit_suboffset;
 
   /* Skip pieces covered by the read offset.  */
@@ -1632,6 +1749,9 @@ dwarf_composite::write (struct frame_info *frame, const gdb_byte *buf,
   unsigned int pieces_num = m_pieces.size ();
   unsigned int i;
 
+  if (!m_completed)
+    ill_formed_expression ();
+
   total_bits_to_skip += m_offset * HOST_CHAR_BIT + m_bit_suboffset;
 
   /* Skip pieces covered by the write offset.  */
@@ -2287,11 +2407,34 @@ struct dwarf_expr_context
   bool stack_empty_p () const;
 
   /* Pop a top element of the stack and add as a composite piece.
-
-     If the fallowing top element of the stack is a composite
-     location description, the piece will be added to it.  Otherwise
-     a new composite location description will be created and
-     the piece will be added to that composite.  */
+     The action is based on the context:
+
+      - If the stack is empty, then an incomplete composite location
+	description (comprised of one undefined location description),
+	is pushed on the stack.
+
+      - Otherwise, if the top stack entry is an incomplete composite
+	location description, then it is updated to append a new piece
+	comprised of one undefined location description.  The
+	incomplete composite location description is then left on the
+	stack.
+
+      - Otherwise, if the top stack entry is a location description or
+	can be converted to one, it is popped. Then:
+
+	 - If the top stack entry (after popping) is a location
+	   description comprised of one incomplete composite location
+	   description, then it is updated to append a new piece
+	   specified by the previously popped location description.
+	   The incomplete composite location description is then left
+	   on the stack.
+
+	 - Otherwise, a new location description comprised of one
+	   incomplete composite location description, with a new piece
+	   specified by the previously popped location description, is
+	   pushed on the stack.
+
+      - Otherwise, the DWARF expression is ill-formed  */
   std::shared_ptr<dwarf_entry> add_piece (ULONGEST bit_size,
 					  ULONGEST bit_offset);
 
@@ -2615,19 +2758,28 @@ dwarf_expr_context::add_piece (ULONGEST bit_size, ULONGEST bit_offset)
   std::shared_ptr<dwarf_location> piece;
   std::shared_ptr<dwarf_composite> composite;
 
-  if (!stack_empty_p ()
-      && std::dynamic_pointer_cast<dwarf_composite> (fetch (0)) == nullptr)
+  if (stack_empty_p ())
+    piece = std::make_shared<dwarf_undefined> (this->gdbarch);
+  else
     {
       piece = fetch (0)->to_location (this->gdbarch);
-      pop ();
+
+      if (auto old_composite
+	    = std::dynamic_pointer_cast<dwarf_composite> (piece))
+	{
+	  if (!old_composite->is_completed ())
+	    piece = std::make_shared<dwarf_undefined> (this->gdbarch);
+	}
+      else if (std::dynamic_pointer_cast<dwarf_undefined> (piece) != nullptr)
+	pop ();
     }
-  else
-    piece = std::make_shared<dwarf_undefined> (this->gdbarch);
 
-  piece->add_bit_offset (bit_offset);
+  if (std::dynamic_pointer_cast<dwarf_undefined> (piece) == nullptr)
+    {
+      piece->add_bit_offset (bit_offset);
+      pop ();
+    }
 
-  /* If stack is empty then it is a start of a new composite.  In the
-     future this will check if the composite is finished or not.  */
   if (stack_empty_p ()
       || std::dynamic_pointer_cast<dwarf_composite> (fetch (0)) == nullptr)
     composite = std::make_shared<dwarf_composite> (this->gdbarch,
@@ -2635,7 +2787,12 @@ dwarf_expr_context::add_piece (ULONGEST bit_size, ULONGEST bit_offset)
   else
     {
       composite = std::dynamic_pointer_cast<dwarf_composite> (fetch (0));
-      pop ();
+
+      if (composite->is_completed ())
+	composite = std::make_shared<dwarf_composite> (this->gdbarch,
+						       this->per_cu);
+      else
+	pop ();
     }
 
   composite->add_piece (piece, bit_size);
@@ -3255,7 +3412,7 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	  break;
 
 	case DW_OP_dup:
-	  result_entry = fetch (0);
+	  result_entry = fetch (0)->clone ();
 	  break;
 
 	case DW_OP_drop:
@@ -3281,7 +3438,7 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	  }
 
 	case DW_OP_over:
-	  result_entry = fetch (1);
+	  result_entry = fetch (1)->clone ();
 	  break;
 
 	case DW_OP_rot:
@@ -3842,6 +3999,23 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	  result_entry = std::make_shared<dwarf_undefined> (this->gdbarch);
 	  break;
 
+	case DW_OP_LLVM_piece_end:
+	  {
+	    auto entry = fetch (0);
+
+	    auto composite
+	      = std::dynamic_pointer_cast<dwarf_composite> (entry);
+
+	    if (composite == nullptr)
+	      ill_formed_expression ();
+
+	    if (composite->is_completed ())
+	      ill_formed_expression ();
+
+	    composite->set_completed (true);
+	    goto no_push;
+	  }
+
 	default:
 	  error (_("Unhandled dwarf expression opcode 0x%x"), op);
 	}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-llvm-piece-end.exp b/gdb/testsuite/gdb.dwarf2/dw2-llvm-piece-end.exp
new file mode 100644
index 00000000000..8a8d0d8ec17
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-llvm-piece-end.exp
@@ -0,0 +1,192 @@
+# Copyright (C) 2017-2021 Free Software Foundation, Inc.
+# Copyright (C) 2020-2021 Advanced Micro Devices, Inc. All rights reserved.
+
+# 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/>.
+
+# Test the nested composition location description by using the new
+# DW_OP_LLVM_piece_end operation.
+#
+# The test uses three nested levels of composite location descriptions
+# to define a location of an array.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+# Choose suitable integer registers for the test.
+
+set dwarf_regnum 0
+
+if { [is_aarch64_target] } {
+    set regname x0
+} elseif { [is_aarch32_target]
+	   || [istarget "s390*-*-*" ]
+	   || [istarget "powerpc*-*-*"]
+	   || [istarget "rs6000*-*-aix*"] } {
+    set regname r0
+} elseif { [is_x86_like_target] } {
+    set regname eax
+} elseif { [is_amd64_regs_target] } {
+    set regname rax
+} else {
+    verbose "Skipping ${gdb_test_file_name}."
+    return
+}
+
+standard_testfile var-access.c ${gdb_test_file_name}-dw.S
+
+# Make some DWARF for the test.
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global dwarf_regnum regname srcdir subdir srcfile
+    set buf_src [gdb_target_symbol buf]
+
+    set main_result [function_range main ${srcdir}/${subdir}/${srcfile}]
+    set main_start [lindex $main_result 0]
+    set main_length [lindex $main_result 1]
+
+    cu {} {
+	DW_TAG_compile_unit {
+	    {DW_AT_name var-access.c}
+	    {DW_AT_comp_dir /tmp}
+	} {
+	    declare_labels array_type_label int_type_label char_type_label
+
+	    # define char type
+	    char_type_label: DW_TAG_base_type {
+		{DW_AT_name "char"}
+		{DW_AT_encoding @DW_ATE_signed}
+		{DW_AT_byte_size 1 DW_FORM_sdata}
+	    }
+
+	    int_type_label: DW_TAG_base_type {
+		{DW_AT_name "int"}
+		{DW_AT_encoding @DW_ATE_signed}
+		{DW_AT_byte_size 4 DW_FORM_sdata}
+	    }
+
+	    array_type_label: DW_TAG_array_type {
+		{DW_AT_type :$char_type_label}
+	    } {
+		DW_TAG_subrange_type {
+		    {DW_AT_type :$int_type_label}
+		    {DW_AT_upper_bound 7 DW_FORM_udata}
+		}
+	    }
+
+	    DW_TAG_subprogram {
+		{DW_AT_name main}
+		{DW_AT_low_pc $main_start addr}
+		{DW_AT_high_pc $main_length data8}
+	    } {
+		# Array spread in different pieces, of which some are
+		# undefined (1st and sixth bytes) and some are either
+		# in buf variable or REGNAME register.
+		#
+		# Location consists of three nested composite levels:
+		# - Third level consists of a composite location
+		# descriptions which hold a single simple location
+		# description each.
+		# - Second level consist of two more composite location
+		# descriptions that hold two of the third level
+		# composite location descriptions.
+		# - First level holds two of the second level composite
+		# location descriptions.
+
+		DW_TAG_variable {
+		    {DW_AT_name var_array}
+		    {DW_AT_type :$array_type_label}
+		    {DW_AT_location {
+			# First level composite start
+			# Second level first composite start
+			# Third level first composite start
+			DW_OP_addr $buf_src
+			DW_OP_piece 0x2
+			DW_OP_LLVM_piece_end
+			# Third level first composite end
+
+			# Third level second composite start
+			DW_OP_LLVM_undefined
+			DW_OP_piece 0x1
+			DW_OP_LLVM_piece_end
+			# Third level second composite end
+
+			DW_OP_piece 0x1
+			DW_OP_swap
+			DW_OP_piece 0x2
+			DW_OP_LLVM_piece_end
+			# Second level first composite end
+
+			# Second level second composite start
+			# Third level third composite start
+			DW_OP_regx $dwarf_regnum
+			DW_OP_piece 0x4
+			DW_OP_LLVM_piece_end
+			# Third level third composite end
+
+			# Third level fourth composite start
+			DW_OP_LLVM_undefined
+			DW_OP_piece 0x1
+			DW_OP_LLVM_piece_end
+			# Third level fourth composite end
+
+			DW_OP_piece 0x1
+			DW_OP_swap
+			DW_OP_piece 0x4
+			DW_OP_LLVM_piece_end
+			# Second level second composite end
+
+			DW_OP_piece 0x5
+			DW_OP_swap
+			DW_OP_piece 0x3
+			DW_OP_LLVM_piece_end
+			# First level composite end
+
+		    } SPECIAL_expr}
+		}
+	    }
+	}
+    }
+}
+
+if { [prepare_for_testing ${testfile}.exp ${testfile} \
+     [list $srcfile $asm_file] {nodebug}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+gdb_test_no_output "set var \$$regname = 0x4030201" "init reg"
+
+# Determine byte order.
+set endian [get_endianness]
+set optimized "<optimized out>"
+
+switch $endian {
+    little {
+	set val "$optimized, 0x1, 0x2, 0x3, 0x4, $optimized, 0x0, 0x1"
+    }
+    big {
+	set val "$optimized, 0x4, 0x3, 0x2, 0x1, $optimized, 0x0, 0x1"
+    }
+}
+
+gdb_test "print/x var_array" " = \\{${val}\\}" "var_array print"
+
diff --git a/include/dwarf2.def b/include/dwarf2.def
index 1b626362ec4..c6669221041 100644
--- a/include/dwarf2.def
+++ b/include/dwarf2.def
@@ -709,6 +709,7 @@ DW_OP_DUP (DW_OP_LLVM_offset, 0xe3)
 DW_OP_DUP (DW_OP_LLVM_offset_constu, 0xe4)
 DW_OP_DUP (DW_OP_LLVM_bit_offset, 0xe5)
 DW_OP (DW_OP_LLVM_undefined, 0xe7)
+DW_OP_DUP (DW_OP_LLVM_piece_end, 0xea)
 DW_END_OP
 
 DW_FIRST_ATE (DW_ATE_void, 0x0)
-- 
2.17.1


^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 18/43] Add new register access interface to expr.c
  2021-03-01 14:45 ` [PATCH 18/43] Add new register access interface to expr.c Zoran Zaric
@ 2021-03-08 23:52   ` Lancelot SIX
  2021-04-28  3:25   ` Simon Marchi
  1 sibling, 0 replies; 86+ messages in thread
From: Lancelot SIX @ 2021-03-08 23:52 UTC (permalink / raw)
  To: Zoran Zaric; +Cc: gdb-patches

Hi,

I have minor style related comments above.

Le Mon, Mar 01, 2021 at 02:45:55PM +0000, Zoran Zaric via Gdb-patches a écrit :
> DWARF expression evaluator is currently using get_frame_register_bytes
> and put_frame_register_bytes interface for register access.
> 
> The problem with evaluator using this interface is that it allows a
> bleed out register access. This means that if the caller specifies a
> larger amount of data then the size of a specified register, the
> operation will continue accessing the neighboring registers until a
> full amount of data has been reached.
> 
> DWARF specification does not define this behavior, so a new simplified
> register access interface is needed instead.
> 
> 	* dwarf2/expr.c (read_from_register): New function.
> 	(write_to_register): New function.
> 	(rw_pieced_value): Now calls the read_from_register and
> 	write_to_register functions.
> ---
>  gdb/dwarf2/expr.c | 128 ++++++++++++++++++++++++++++++++++++++--------
>  1 file changed, 106 insertions(+), 22 deletions(-)
> 
> diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
> index c50bb3c8d90..5a1fd5b941f 100644
> --- a/gdb/dwarf2/expr.c
> +++ b/gdb/dwarf2/expr.c
> @@ -106,6 +106,96 @@ read_addr_from_reg (struct frame_info *frame, int reg)
>    return address_from_register (regnum, frame);
>  }
>  
> +/* Read register REGNUM's contents in a given FRAME context.
> +
> +   The data read is offsetted by OFFSET, and the number of bytes read
> +   is defined by LENGTH.  The data is then copied into the
> +   caller-managed buffer BUF.
> +
> +   If the register is optimized out or unavailable for the given
> +   FRAME, the OPTIMIZED and UNAVAILABLE outputs are set
> +   accordingly  */
> +
> +static void
> +read_from_register (struct frame_info *frame, int regnum,
> +		    CORE_ADDR offset, gdb::array_view<gdb_byte> buf,
> +		    int *optimized, int *unavailable)
> +{
> +  struct gdbarch *gdbarch = get_frame_arch (frame);
> +  int regsize = register_size (gdbarch, regnum);
> +  int numregs = gdbarch_num_cooked_regs (gdbarch);
> +  int length = buf.size ();
> +
> +  /* If a register is wholly inside the OFFSET, skip it.  */
> +  if (frame == NULL || !regsize

Gdb coding standard asks for explicit comparison of numbers
(https://sourceware.org/gdb/wiki/Internals%20GDB-C-Coding-Standards#Comparison_With_NULL_And_Zero).

!regsize should be regsize == 0.

I guess NULL could also be changed to nullptr.

> +      || offset + length > regsize || numregs < regnum)
> +    {
> +      *optimized = 0;
> +      *unavailable = 1;
> +      return;
> +    }
> +
> +  gdb::byte_vector temp_buf (regsize);
> +  enum lval_type lval;
> +  CORE_ADDR address;
> +  int realnum;
> +
> +  frame_register (frame, regnum, optimized, unavailable,
> +		  &lval, &address, &realnum, temp_buf.data ());
> +
> +  if (!*optimized && !*unavailable)
> +     memcpy (buf.data (), (char *) temp_buf.data () + offset, length);
> +
> +  return;

I changes nothing, but I find it odd to have a return; as last
statement of a void returning function.

> +}
> +
> +/* Write register REGNUM's contents in a given FRAME context.
> +
> +   The data written is offsetted by OFFSET, and the number of bytes
> +   written is defined by LENGTH.  The data is copied from
> +   caller-managed buffer BUF.
> +
> +   If the register is optimized out or unavailable for the given
> +   FRAME, the OPTIMIZED and UNAVAILABLE outputs are set
> +   accordingly. */
> +
> +static void
> +write_to_register (struct frame_info *frame, int regnum,
> +		   CORE_ADDR offset, gdb::array_view<gdb_byte> buf,
> +		   int *optimized, int *unavailable)
> +{
> +  struct gdbarch *gdbarch = get_frame_arch (frame);
> +  int regsize = register_size (gdbarch, regnum);
> +  int numregs = gdbarch_num_cooked_regs (gdbarch);
> +  int length = buf.size ();
> +
> +  /* If a register is wholly inside of OFFSET, skip it.  */
> +  if (frame == NULL || !regsize

Same remark for regsize.

Thanks,
Lancelot.

> +     || offset + length > regsize || numregs < regnum)
> +    {
> +      *optimized = 0;
> +      *unavailable = 1;
> +      return;
> +    }
> +
> +  gdb::byte_vector temp_buf (regsize);
> +  enum lval_type lval;
> +  CORE_ADDR address;
> +  int realnum;
> +
> +  frame_register (frame, regnum, optimized, unavailable,
> +		  &lval, &address, &realnum, temp_buf.data ());
> +
> +  if (!*optimized && !*unavailable)
> +    {
> +      memcpy ((chr *) temp_buf.data () + offset, buf.data (), length);
> +
> +      put_frame_register (frame, regnum, temp_buf.data ());
> +    }
> +
> +  return;
> +}
> +
>  struct piece_closure
>  {
>    /* Reference count.  */
> @@ -243,22 +333,18 @@ rw_pieced_value (struct value *v, struct value *from)
>  	    if (from == NULL)
>  	      {
>  		/* Read mode.  */
> -		if (!get_frame_register_bytes (frame, gdb_regnum,
> -					       bits_to_skip / 8,
> -					       buffer, &optim, &unavail))
> -		  {
> -		    if (optim)
> -		      mark_value_bits_optimized_out (v, offset,
> -						     this_size_bits);
> -		    if (unavail)
> -		      mark_value_bits_unavailable (v, offset,
> -						   this_size_bits);
> -		    break;
> -		  }
> -
> -		copy_bitwise (v_contents, offset,
> -			      buffer.data (), bits_to_skip % 8,
> -			      this_size_bits, bits_big_endian);
> +		read_from_register (frame, gdb_regnum, bits_to_skip / 8,
> +				    buffer, &optim, &unavail);
> +
> +		if (optim)
> +		  mark_value_bits_optimized_out (v, offset, this_size_bits);
> +		if (unavail)
> +		  mark_value_bits_unavailable (v, offset, this_size_bits);
> +		/* Only copy data if valid.  */
> +		if (!optim && !unavail)
> +		  copy_bitwise (v_contents, offset,
> +				buffer.data (), bits_to_skip % 8,
> +				this_size_bits, bits_big_endian);
>  	      }
>  	    else
>  	      {
> @@ -267,9 +353,8 @@ rw_pieced_value (struct value *v, struct value *from)
>  		  {
>  		    /* Data is copied non-byte-aligned into the register.
>  		       Need some bits from original register value.  */
> -		    get_frame_register_bytes (frame, gdb_regnum,
> -					      bits_to_skip / 8,
> -					      buffer, &optim, &unavail);
> +		    read_from_register (frame, gdb_regnum, bits_to_skip / 8,
> +					buffer, &optim, &unavail);
>  		    if (optim)
>  		      throw_error (OPTIMIZED_OUT_ERROR,
>  				   _("Can't do read-modify-write to "
> @@ -285,9 +370,8 @@ rw_pieced_value (struct value *v, struct value *from)
>  		copy_bitwise (buffer.data (), bits_to_skip % 8,
>  			      from_contents, offset,
>  			      this_size_bits, bits_big_endian);
> -		put_frame_register_bytes (frame, gdb_regnum,
> -					  bits_to_skip / 8,
> -					  buffer);
> +		write_to_register (frame, gdb_regnum, bits_to_skip / 8,
> +				   buffer, &optim, &unavail);
>  	      }
>  	  }
>  	  break;
> -- 
> 2.17.1
> 

-- 
Lancelot SIX

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 01/43] Replace the symbol needs evaluator with a parser
  2021-03-01 14:45 ` [PATCH 01/43] Replace the symbol needs evaluator with a parser Zoran Zaric
@ 2021-04-27  1:20   ` Simon Marchi
  2021-04-28 10:17     ` Zoran Zaric
  0 siblings, 1 reply; 86+ messages in thread
From: Simon Marchi @ 2021-04-27  1:20 UTC (permalink / raw)
  To: Zoran Zaric, gdb-patches



On 2021-03-01 9:45 a.m., Zoran Zaric via Gdb-patches wrote:
> This patch addresses a design problem with the symbol_needs_eval_context
> class. It exposes the problem by introducing two new testsuite test
> cases.
> 
> To explain the issue, I first need to explain the dwarf_expr_context
> class that the symbol_needs_eval_context class derives from.
> 
> The intention behind the dwarf_expr_context class is to commonize the
> DWARF expression evaluation mechanism for different evaluation
> contexts. Currently in gdb, the evaluation context can contain some or
> all of the following information: architecture, object file, frame and
> compilation unit.
> 
> Depending on the information needed to evaluate a given expression,
> there are currently three distinct DWARF expression evaluators:
> 
>  - Frame: designed to evaluate an expression in the context of a call
>    frame information (dwarf_expr_executor class). This evaluator doesn’t
>    need a compilation unit information.
> 
>  - Location description: designed to evaluate an expression in the
>    context of a source level information (dwarf_evaluate_loc_desc
>    class). This evaluator expects all information needed for the
>    evaluation of the given expression to be present.
> 
>  - Symbol needs: designed to answer a question about the parts of the
>    context information required to evaluate a DWARF expression behind a
>    given symbol (symbol_needs_eval_context class). This evaluator
>    doesn’t need a frame information.
> 
> The functional difference between the symbol needs evaluator and the
> others is that this evaluator is not meant to interact with the actual
> target. Instead, it is supposed to check which parts of the context
> information are needed for the given DWARF expression to be evaluated by
> the location description evaluator.
> 
> The idea is to take advantage of the existing dwarf_expr_context
> evaluation mechanism and to fake all required interactions with the
> actual target, by returning back dummy values. The evaluation result is
> returned as one of three possible values, based on operations found in a
> given expression:
> 
> - SYMBOL_NEEDS_NONE,
> - SYMBOL_NEEDS_REGISTERS and
> - SYMBOL_NEEDS_FRAME.
> 
> The problem here is that faking results of target interactions can yield
> an incorrect evaluation result.
> 
> For example, if we have a conditional DWARF expression, where the
> condition depends on a value read from an actual target, and the true
> branch of the condition requires a frame information to be evaluated,
> while the false branch doesn’t, fake target reads could conclude that a
> frame information is not needed, where in fact it is. This wrong
> information would then cause the expression to be actually evaluated (by
> the location description evaluator) with a missing frame information.
> This would then crash the debugger.
> 
> The gdb.dwarf2/symbol_needs_eval_fail.exp test introduces this
> scenario, with the following DWARF expression:
> 
>                    DW_OP_addr $some_variable
>                    DW_OP_deref
>                    DW_OP_bra 4  # conditional jump to DW_OP_bregx
>                    DW_OP_lit0
>                    DW_OP_skip 3  # jump to DW_OP_stack_value
>                    DW_OP_bregx $dwarf_regnum 0
>                    DW_OP_stack_value
> 
> This expression describes a case where some variable dictates the
> location of another variable. Depending on a value of some_variable, the
> variable whose location is described by this expression is either read
> from a register or it is defined as a constant value 0. In both cases,
> the value will be returned as an implicit location description on the
> DWARF stack.
> 
> Currently, when the symbol needs evaluator fakes a memory read from the
> address behind the some_variable variable, the constant value 0 is used
> as the value of the variable A, and the check returns the
> SYMBOL_NEEDS_NONE result.
> 
> This is clearly a wrong result and it causes the debugger to crash.
> 
> The scenario might sound strange to some people, but it comes from a
> SIMD/SIMT architecture where $some_variable is an execution mask.  In
> any case, it is a valid DWARF expression, and GDB shouldn't crash while
> evaluating it. Also, a similar example could be made based on a
> condition of the frame base value, where if that value is concluded to
> be 0, the variable location could be defaulted to a TLS based memory
> address.
> 
> The gdb.dwarf2/symbol_needs_eval_timeout.exp test introduces a second
> scenario. This scenario is a bit more abstract due to the DWARF
> assembler lacking the CFI support, but it exposes a different
> manifestation of the same problem. Like in the previous scenario, the
> DWARF expression used in the test is valid:
> 
>                        DW_OP_lit1
>                        DW_OP_addr $some_variable
>                        DW_OP_deref
>                        DW_OP_skip 4  # jump to DW_OP_fbreg
>                        DW_OP_drop
>                        DW_OP_fbreg 0
>                        DW_OP_dup
>                        DW_OP_lit0
>                        DW_OP_eq
>                        DW_OP_bra -9  # conditional jump to DW_OP_drop
>                        DW_OP_stack_value
> 
> Similarly to the previous scenario, the location of a variable A is an
> implicit location description with a constant value that depends on a
> value held by a global variable. The difference from the previous case
> is that DWARF expression contains a loop instead of just one branch. The
> end condition of that loop depends on the expectation that a frame base
> value is never zero. Currently, the act of faking the target reads will
> cause the symbol needs evaluator to get stuck in an infinite loop.
> 
> Somebody could argue that we could change the fake reads to return
> something else, but that would only hide the real problem.
> 
> The general impression seems to be that the desired design is to have
> one class that deals with parsing of the DWARF expression, while there
> are virtual methods that deal with specifics of some operations.
> 
> Using an evaluator mechanism here doesn’t seem to be correct, because
> the act of evaluation relies on accessing the data from the actual
> target with the possibility of skipping the evaluation of some parts of
> the expression.
> 
> To better explain the proposed solution for the issue, I first need to
> explain a couple more details behind the current design:
> 
> There are multiple places in gdb that handle DWARF expression parsing
> for different purposes. Some are in charge of converting the expression
> to some other internal representation (decode_location_expression,
> disassemble_dwarf_expression and dwarf2_compile_expr_to_ax), some are
> analysing the expression for specific information
> (compute_stack_depth_worker) and some are in charge of evaluating the
> expression in a given context (dwarf_expr_context::execute_stack_op
> and decode_locdesc).
> 
> The problem is that all those functions have a similar (large) switch
> statement that handles each DWARF expression operation. The result of
> this is a code duplication and harder maintenance.
> 
> As a step into the right direction to solve this problem (at least for
> the purpose of a DWARF expression evaluation) the expression parsing was
> commonized inside of an evaluator base class (dwarf_expr_context). This
> makes sense for all derived classes, except for the symbol needs
> evaluator (symbol_needs_eval_context) class.
> 
> As described previously the problem with this evaluator is that if the
> evaluator is not allowed to access the actual target, it is not really
> evaluating.
> 
> Instead, the desired function of a symbol needs evaluator seems to fall
> more into expression analysis category. This means that a more natural
> fit for this evaluator is to be a symbol needs analysis, similar to the
> existing compute_stack_depth_worker analysis.
> 
> Another problem is that using a heavyweight mechanism of an evaluator
> to do an expression analysis seems to be an unneeded overhead. It also
> requires a more complicated design of the parent class to support fake
> target reads.
> 
> The reality is that the whole symbol_needs_eval_context class can be
> replaced with a lightweight recursive analysis function, that will give
> more correct result without compromising the design of the
> dwarf_expr_context class.
> 
> The downside of this approach is adding of one more similar switch
> statement, but at least this way the new symbol needs analysis will be
> a lightweight mechnism and it will provide a correct result for any
> given DWARF expression.
> 
> A more desired long term design would be to have one class that deals
> with parsing of the DWARF expression, while there would be a virtual
> methods that deal with specifics of some DWARF operations. Then that
> class would be used as a base for all DWARF expression parsing mentioned
> at the beginning.
> 
> This however, requires a far bigger changes that are out of the scope
> of this patch series.
> 
> The new analysis requires the DWARF location description for the
> argc argument of the niam function to change in the assembly file
> gdb.python/amd64-py-framefilter-invalidarg.S. Originally, expression
> ended with a 0 value byte, which was never reached by the symbol needs
> evaluator, because it was detecting a stack underflow when evaluating
> the operation before. The new approach does not simulate a DWARF
> stack anymore, so the 0 value byte needs to be removed because it
> makes the DWARF expression invalid.
> 
> Some concerns were raised that a linear scan of the expression byte
> stream would have issues if a DWARF producer would try to hide some
> non DWARF related data after a control flow operation. Although the
> testsuite doesn't show this case, it is a valid concern, so one of
> the later patches in this series will address it by switching back to
> the then redesigned DWARF expression evaluator.

While re-reading your exchange with Tom, I was under the impression that
traversing the "control flow graph" of the expression, visiting each
node only once, would be a good solution.  It would avoid the infinite
loop problem, the "two branches" problem, and even the corner cases
where you have garbage in the middle of the expression, or if the
expression jumps in the middle of an instruction to re-use the operand
of an instruction as an instruction.

Patch 39 changes this back to an evaluator, so I'm not sure if this is
what you implemented or something else, I'll see when I get there.

> -class symbol_needs_eval_context : public dwarf_expr_context
> +static enum symbol_needs_kind
> +dwarf2_get_symbol_read_needs (gdb::array_view<const gdb_byte> expr,
> +			     dwarf2_per_cu_data *per_cu,
> +			     dwarf2_per_objfile *per_objfile,
> +			     bfd_endian byte_order,
> +			     int addr_size,
> +			     int ref_addr_size,
> +			     int depth = 0)

The wrapped lines above are missing one column of indent.

Simon

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 02/43] Cleanup of the dwarf_expr_context constructor
  2021-03-01 14:45 ` [PATCH 02/43] Cleanup of the dwarf_expr_context constructor Zoran Zaric
@ 2021-04-27  1:23   ` Simon Marchi
  2021-04-28 10:19     ` Zoran Zaric
  0 siblings, 1 reply; 86+ messages in thread
From: Simon Marchi @ 2021-04-27  1:23 UTC (permalink / raw)
  To: Zoran Zaric, gdb-patches

On 2021-03-01 9:45 a.m., Zoran Zaric via Gdb-patches wrote:
> Move the initial values for dwarf_expr_context class data members
> to the class declaration in expr.h.
> 
> gdb/ChangeLog:
> 
>         * dwarf2/expr.c (dwarf_expr_context::dwarf_expr_context):
>         Remove initial data members values.
>         * dwarf2/expr.h (dwarf_expr_context): Add initial values
>         to the class data members.

LGTM.  I suggest you push this right away, it's a good cleanup and it
will be one less patch to carry.

Simon

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 03/43] Move frame context info to dwarf_expr_context
  2021-03-01 14:45 ` [PATCH 03/43] Move frame context info to dwarf_expr_context Zoran Zaric
@ 2021-04-27  2:19   ` Simon Marchi
  2021-04-28 10:51     ` Zoran Zaric
  0 siblings, 1 reply; 86+ messages in thread
From: Simon Marchi @ 2021-04-27  2:19 UTC (permalink / raw)
  To: Zoran Zaric, gdb-patches

Hi Zoran,

This patch was already ok'ed by Tom, but I have some more suggestions
below.  Nothing that compromises the general idea of the patch though.

On 2021-03-01 9:45 a.m., Zoran Zaric via Gdb-patches wrote:
> From: Zoran Zaric <zoran.zaric@amd.com>
> 
> Following 15 patches in this patch series is cleaning up the design of
> the DWARF expression evaluator (dwarf_expr_context) to make future
> extensions of that evaluator easier and cleaner to implement.
> 
> There are three subclasses of the dwarf_expr_context class
> (dwarf_expr_executor, dwarf_evaluate_loc_desc and
> evaluate_for_locexpr_baton). Here is a short description of each class:
> 
> - dwarf_expr_executor is evaluating a DWARF expression in a context
>   of a Call Frame Information. The overridden methods of this subclass
>   report an error if a specific DWARF operation, represented by that
>   method, is not allowed in a CFI context. The source code of this
>   subclass lacks the support for composite as well as implicit pointer
>   location description.
> 
> - dwarf_evaluate_loc_desc can evaluate any expression with no
>   restrictions. All of the methods that this subclass overrides are
>   actually doing what they are intended to do. This subclass contains
>   a full support for all location description types.
> 
> - evaluate_for_locexpr_baton subclass is a specialization of the
>   dwarf_evaluate_loc_desc subclass and it’s function is to add
>   support for passed in buffers. This seems to be a way to go around
>   the fact that DWARF standard lacks a bit offset support for memory
>   location descriptions as well as using any location description for
>   the push object address functionality.
> 
> It all comes down to this question: what is a function of a DWARF
> expression evaluator?
> 
> Is it to evaluate the expression in a given context or to check the
> correctness of that expression in that context?
> 
> Currently, the only reason why there is a dwarf_expr_executor subclass
> is to report an invalid DWARF expression in a context of a CFI, but is
> that what the evaluator is supposed to do considering that the evaluator
> is not tied to a given DWARF version?
> 
> There are more and more vendor and GNU extensions that are not part of
> the DWARF standard, so is it that impossible to expect that some of the
> extensions could actually lift the previously imposed restrictions of
> the CFI context? Not to mention that every new DWARF version is lifting
> some restrictions anyway.
> 
> The thing that makes more sense for an evaluator to do, is to take the
> context of an evaluation and checks the requirements of every operation
> evaluated against that context. With this approach, the evaluator would
> report an error only if parts of the context, necessary for the
> evaluation, are missing.
> 
> If this approach is taken, then the unification of the
> dwarf_evaluate_loc_desc, dwarf_expr_executor and dwarf_expr_context
> is the next logical step. This makes a design of the DWARF expression
> evaluator cleaner and allows more flexibility when supporting future
> vendor and GNU extensions.
> 
> Additional benefit here is that now all evaluators have access to all
> location description types, which means that a vendor extended CFI
> rules could support composite location description as well. This also
> means that a new evaluator interface can be changed to return a single
> struct value (that describes the result of the evaluation) instead of
> a caller poking around the dwarf_expr_context internal data for answers
> (like it is done currently).
> 
> This patch starts the merging proccess by moving the frame context

proccess -> process

> @@ -56,6 +57,31 @@ dwarf_gdbarch_types_init (struct gdbarch *gdbarch)
>    return types;
>  }
>  
> +/* Ensure that a FRAME is defined, throw an exception otherwise.
> +
> +   Throwing NOT_AVAILABLE_ERROR error so that a client can chose
> +   to react differently if the evaluation ended because there
> +   was a missing context information.  */
> +
> +static void
> +ensure_have_frame (struct frame_info *frame, const char *op_name)
> +{
> +  if (frame == nullptr)
> +    throw_error (NOT_AVAILABLE_ERROR,
> +		 _("%s evaluation requires a frame."), op_name);
> +}

I don't remember if we discussed about that or not, so I'll ask:
"available" in GDB terminology usually refers to the tracing
functionality.  While tracing, you can choose which registers and what
part of the memory to collect when hitting a tracepoint.  During later
analysis, if you try for example to print the value of a variable that
requires some information (register or memory) that you didn't collect,
we'll say that this register or memory (and therefore the variable's
value) is not available / unavailable.

It is also used with the "record" command / concept, where some things
are recorded during execution to be able to step backwards in time,
that's pretty much the same as tracing.  If you step backwards in time
and try to access an information that wasn't recorded and an exception
is thrown as a result, it will be NOT_AVAILABLE_ERROR.

So I am a bit worried that by using NOT_AVAILABLE_ERROR to say "to
evaluate this expression, you need a frame, but there is no frame in the
current context", we are overloading this exception type / return code.

I can imagine that I could be inspecting a trace, I try to print a
variable whose location expression needs to be evaluated.  Evaluating
the location expression requires accessing some memory or register that
wasn't recorded in my trace, and that results in throwing
NOT_AVAILABLE_ERROR.  That would be a different error than trying to
evaluate an expression that requires a frame in a context that doesn't
have a frame.

So it seems to me like a new exception type would be desirable.  Maybe
INSUFFICIENT_CONTEXT_ERROR?

On an unrelated topic, things like ensure_have_frame always worry me
because I'm scared I'll forget to call it where necessary.  An
alternative could be to make sure we get the frame through a getter that
throws if frame == nullptr.

Basically, a tiny class (could be internal to dwarf_expr_context):

struct frame_safe
{
  frame_safe (frame_info *frame)
    : m_frame (frame)
  {}

  frame_info *frame (const char *op)
  {
    if (m_frame == nullptr)
      ... throw ...

    return m_frame;
  }

private:
  frame_info *m_frame;
};

dwarf_expr_context would have a `frame_safe m_frame` field and a
`frame (const char *op)` getter that just does
`return m_frame->frame (op)`.

This way, it's impossible to get the frame and forgetting an
ensure_have_frame call.

> @@ -857,7 +928,7 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
>  	    if (this->location == DWARF_VALUE_MEMORY)
>  	      result = fetch_address (0);
>  	    else if (this->location == DWARF_VALUE_REGISTER)
> -	      result = this->read_addr_from_reg (value_as_long (fetch (0)));
> +	      result = read_addr_from_reg (this->frame, value_as_long (fetch (0)));

This line is slightly too long.

> @@ -259,8 +247,23 @@ struct dwarf_expr_context
>    void add_piece (ULONGEST size, ULONGEST offset);
>    void execute_stack_op (const gdb_byte *op_ptr, const gdb_byte *op_end);
>    void pop ();
> +
> +  /* Return a value of type TYPE, stored in register number REGNUM
> +     of the frame associated to the given BATON.

"the given BATON" is a bit confusing, I'm not sure which BATON this
refers to.  Could you try to improve this comment?

> +
> +     REGNUM is a DWARF register number.  */
> +  struct value *get_reg_value (struct type *type, int regnum);
> +
> +  /* Return the location expression for the frame base attribute, in
> +     START and LENGTH.  The result must be live until the current
> +     expression evaluation is complete.  */
> +  void get_frame_base (const gdb_byte **start, size_t *length);
>  };
>  
> +/* Return the value of register number REG (a DWARF register number),
> +   read as an address in a given FRAME.  */
> +CORE_ADDR read_addr_from_reg (struct frame_info *, int);

Can you please add the names to the parameters?  It's a bit confusing
otherwise, since the comment refers to them.

Simon

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 05/43] Move compilation unit info to dwarf_expr_context
  2021-03-01 14:45 ` [PATCH 05/43] Move compilation unit info to dwarf_expr_context Zoran Zaric
@ 2021-04-27  2:58   ` Simon Marchi
  2021-04-28 11:28     ` Zoran Zaric
  0 siblings, 1 reply; 86+ messages in thread
From: Simon Marchi @ 2021-04-27  2:58 UTC (permalink / raw)
  To: Zoran Zaric, gdb-patches

On 2021-03-01 9:45 a.m., Zoran Zaric via Gdb-patches wrote:
> From: Zoran Zaric <zoran.zaric@amd.com>
> 
> This patch moves the compilation unit context information and support
> from dwarf_expr_executor and dwarf_evaluate_loc_desc to
> dwarf_expr_context evaluator. The idea is to report an error when a
> given operation requires a compilation unit information to be resolved,
> which is not available.
> 
> gdb/ChangeLog:
> 
> 	* dwarf2/expr.c (ensure_have_per_cu): New function.
> 	(dwarf_expr_context::dwarf_expr_context): Add compilation unit
> 	context information.
> 	(dwarf_expr_context::get_base_type): Move from
> 	dwarf_evaluate_loc_desc.
> 	(dwarf_expr_context::get_addr_index): Remove method.
> 	(dwarf_expr_context::dwarf_variable_value): Remove method.
> 	(dwarf_expr_context::execute_stack_op): Call compilation unit
> 	context info check. Inline get_addr_index and
> 	dwarf_variable_value methods.
> 	* dwarf2/expr.h (struct dwarf_expr_context): Add compilation
> 	context info. Remove get_addr_index and dwarf_variable_value.
> 	* dwarf2/frame.c (dwarf_expr_executor::get_addr_index): Remove
> 	method.
> 	(dwarf_expr_executor::dwarf_variable_value): Remove method.
> 	* dwarf2/loc.c (sect_variable_value): Expose function.
> 	(dwarf_evaluate_loc_desc::get_addr_index): Remove method.
> 	(dwarf_evaluate_loc_desc::dwarf_variable_value): Remove method.
> 	(class dwarf_evaluate_loc_desc): Move compilation unit context
> 	information to dwarf_expr_context class.
> 	* dwarf2/loc.h (sect_variable_value): Expose function.
> ---
>  gdb/dwarf2/expr.c  | 55 +++++++++++++++++++++++++++++++++++++++-------
>  gdb/dwarf2/expr.h  | 26 ++++++++--------------
>  gdb/dwarf2/frame.c | 10 ---------
>  gdb/dwarf2/loc.c   | 35 +++--------------------------
>  gdb/dwarf2/loc.h   |  8 +++++++
>  5 files changed, 67 insertions(+), 67 deletions(-)
> 
> diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
> index 6b5cba6b7b4..5a0a4a4299c 100644
> --- a/gdb/dwarf2/expr.c
> +++ b/gdb/dwarf2/expr.c
> @@ -72,6 +72,20 @@ ensure_have_frame (struct frame_info *frame, const char *op_name)
>  		 _("%s evaluation requires a frame."), op_name);
>  }
>  
> +/* Ensure that a PER_CU is defined and throw an exception otherwise.
> +
> +   Throwing NOT_AVAILABLE_ERROR error so that a client can chose
> +   to react differently if the evaluation ended because there
> +   was a missing context information.  */
> +
> +static void
> +ensure_have_per_cu (struct dwarf2_per_cu_data *per_cu, const char* op_name)
> +{
> +  if (per_cu == nullptr)
> +    throw_error (NOT_AVAILABLE_ERROR,
> +		 _("%s evaluation requires a compilation unit."), op_name);
> +}

Same comment as earlier about NOT_AVAILABLE_ERROR, and about wrapping
per_cu to force accessing it through a getter that throws if it is
nullptr (whatever we end up doing for the frame should be done here).

> +
>  /* See expr.h.  */
>  
>  CORE_ADDR
> @@ -201,6 +215,25 @@ dwarf_expr_context::get_frame_base (const gdb_byte **start,
>  				   start, length);
>  }
>  
> +/* See expr.h.  */
> +
> +struct type *
> +dwarf_expr_context::get_base_type (cu_offset die_cu_off, int size)
> +{
> +  if (per_cu == nullptr)
> +    return builtin_type (this->gdbarch)->builtin_int;

Ok, so this seems to be a case where we need to check if per_cu is
nullptr or not.  The getter that throws won't work here, but there could
be an `has_per_cu ()` method on the side.

> +
> +  struct type *result = dwarf2_get_die_type (die_cu_off, per_cu, per_objfile);
> +
> +  if (result == NULL)
> +    error (_("Could not find type for DW_OP_const_type"));

Might as well change this NULL for nullptr.

> +
> +  if (size != 0 && TYPE_LENGTH (result) != size)
> +    error (_("DW_OP_const_type has different sizes for type and data"));
> +
> +  return result;
> +}

I find get_base_type a bit awkward.  Do you think you could split it in
two:

 - get_base_type
 - get_base_type_check_size

... where get_base_type_check_size uses get_base_type?  The callers that
don't need the size checking wouldn't need to pass 0.

I also find it awkward that get_base_type hardcodes DW_OP_const_type in
its error message, given that it's used in the context of many other
operators.  In the end we understand that DW_OP_const_type is the only
case that passes size != 0, so it's the only case where that error can
possibly be thrown.  But if you could pass the op name as a parameter,
like you did for the ensure_have_per_cu function, I think it would make
things better.

Or, since that size checking is only used when handling
DW_OP_const_type, we could in-line that code in the caller and not
bother with having get_base_type_check_size.

> @@ -834,10 +873,7 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
>  	case DW_OP_GNU_implicit_pointer:
>  	  {
>  	    int64_t len;
> -
> -	    if (this->ref_addr_size == -1)
> -	      error (_("DWARF-2 expression error: DW_OP_implicit_pointer "
> -		       "is not allowed in frame context"));
> +	    ensure_have_per_cu (per_cu, "DW_OP_implicit_pointer");
>  
>  	    /* The referred-to DIE of sect_offset kind.  */
>  	    this->len = extract_unsigned_integer (op_ptr, this->ref_addr_size,

This case would be a bit weird with my suggestion of per_cu getter that
throws if per_cu is nullptr, because nothing actually uses per_cu here.
But, I think that in the end we could get rid of the
`this->ref_addr_size` field, and use `this->per_cu ()->ref_addr_size
()`.

Simon

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 07/43] Move get_object_address to dwarf_expr_context
  2021-03-01 14:45 ` [PATCH 07/43] Move get_object_address " Zoran Zaric
@ 2021-04-27  3:12   ` Simon Marchi
  2021-04-28 11:34     ` Zoran Zaric
  0 siblings, 1 reply; 86+ messages in thread
From: Simon Marchi @ 2021-04-27  3:12 UTC (permalink / raw)
  To: Zoran Zaric, gdb-patches

> @@ -204,7 +207,12 @@ struct dwarf_expr_context
>  					   int deref_size) = 0;
>  
>    /* Return the `object address' for DW_OP_push_object_address.  */
> -  virtual CORE_ADDR get_object_address () = 0;
> +  virtual CORE_ADDR get_object_address ()
> +  {
> +    if (obj_address == 0)
> +      error (_("Location address is not set."));
> +    return obj_address;

Instead of having obj_address == 0 as a special case, I'd suggest making
it a gdb::optional maybe.  To make sure that nobody accesses it
directly, other than through the getter, you could even apply the same
pattern of wrapping it that I suggested for the frame and per_cu.

Should the error thrown here be of the same type as the error thrown for
if the frame or per_cu is not there (NOT_AVAILABLE_ERROR currently, but
perhaps subject to change)?

Simon

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 09/43] Move push_dwarf_reg_entry_value to expr.c
  2021-03-01 14:45 ` [PATCH 09/43] Move push_dwarf_reg_entry_value to expr.c Zoran Zaric
@ 2021-04-27  3:56   ` Simon Marchi
  2021-04-28 11:36     ` Zoran Zaric
  0 siblings, 1 reply; 86+ messages in thread
From: Simon Marchi @ 2021-04-27  3:56 UTC (permalink / raw)
  To: Zoran Zaric, gdb-patches

> diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
> index 872de16cd70..0b7f3c90728 100644
> --- a/gdb/dwarf2/expr.c
> +++ b/gdb/dwarf2/expr.c
> @@ -268,6 +268,57 @@ dwarf_expr_context::read_mem (gdb_byte *buf, CORE_ADDR addr,
>    read_memory (addr, buf, length);
>  }
>  
> +/* See expr.h.  */
> +
> +void
> +dwarf_expr_context::push_dwarf_reg_entry_value
> +		      (enum call_site_parameter_kind kind,
> +		       union call_site_parameter_u kind_u,
> +		       int deref_size)

When the parameter list doesn't fit in the right of the name, we
typically to:

void
dwarf_expr_context::push_dwarf_reg_entry_value
  (enum call_site_parameter_kind kind,
   union call_site_parameter_u kind_u,
   int deref_size)o

If we need to wrap, we might as well start completely on the left, that
leaves more space :).

Simon

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 12/43] Merge evaluate_for_locexpr_baton evaluator
  2021-03-01 14:45 ` [PATCH 12/43] Merge evaluate_for_locexpr_baton evaluator Zoran Zaric
@ 2021-04-28  1:33   ` Simon Marchi
  2021-04-28 11:39     ` Zoran Zaric
  0 siblings, 1 reply; 86+ messages in thread
From: Simon Marchi @ 2021-04-28  1:33 UTC (permalink / raw)
  To: Zoran Zaric, gdb-patches



On 2021-03-01 9:45 a.m., Zoran Zaric via Gdb-patches wrote:
> From: Zoran Zaric <zoran.zaric@amd.com>
> 
> The evaluate_for_locexpr_baton is the last derived class from the
> dwarf_expr_context class. It's purpose is to support the passed in
> buffer functionality.
> 
> Although, it is not really necessary to merge this class with it's
> base class, doing that simplifies new expression evaluator design.
> 
> Considering that this functionality is going around the DWARF standard,
> it is also reasonable to expect that with a new evaluator design and
> extending the push object address functionality to accept any location
> description, there will be no need to support passed in buffers.
> 
> Alternatively, it would also makes sense to abstract the interaction
> between the evaluator and a given resource in the near future. The
> passed in buffer would then be a specialization of that abstraction.
> 
> gdb/ChangeLog:
> 
> 	* dwarf2/expr.c (dwarf_expr_context::read_mem): Merge with
> 	evaluate_for_locexpr_baton implementation.
> 	* dwarf2/loc.c (class evaluate_for_locexpr_baton): Remove
> 	class.
> 	(evaluate_for_locexpr_baton::read_mem): Move to
> 	dwarf_expr_context.
> 	(dwarf2_locexpr_baton_eval): Instantiate dwarf_expr_context
> 	instead of evaluate_for_locexpr_baton class.
> ---
>  gdb/dwarf2/expr.c | 19 +++++++++++++++++--
>  gdb/dwarf2/expr.h | 13 +++----------
>  gdb/dwarf2/loc.c  | 45 ++-------------------------------------------
>  3 files changed, 22 insertions(+), 55 deletions(-)
> 
> diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
> index 1212be9c984..fe6d072ac8f 100644
> --- a/gdb/dwarf2/expr.c
> +++ b/gdb/dwarf2/expr.c
> @@ -252,6 +252,18 @@ void
>  dwarf_expr_context::read_mem (gdb_byte *buf, CORE_ADDR addr,
>  			      size_t length)
>  {
> +  if (length == 0)
> +    return;

I'm really curious about when `length == 0` happens, but I see it was
there previously, so this ok.  But if you know the answer, I want to
know.

> @@ -239,6 +231,7 @@ struct dwarf_expr_context
>    void push_dwarf_reg_entry_value (enum call_site_parameter_kind kind,
>  				   union call_site_parameter_u kind_u,
>  				   int deref_size);
> +  void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t length);

You could keep the comment that was there previously over the read_mem
method signature.

Simon

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 13/43] Move piece_closure and its support to expr.c
  2021-03-01 14:45 ` [PATCH 13/43] Move piece_closure and its support to expr.c Zoran Zaric
@ 2021-04-28  1:56   ` Simon Marchi
  2021-04-28 11:40     ` Zoran Zaric
  0 siblings, 1 reply; 86+ messages in thread
From: Simon Marchi @ 2021-04-28  1:56 UTC (permalink / raw)
  To: Zoran Zaric, gdb-patches

On 2021-03-01 9:45 a.m., Zoran Zaric via Gdb-patches wrote:
> @@ -300,4 +301,15 @@ extern const gdb_byte *safe_read_sleb128 (const gdb_byte *buf,
>  extern const gdb_byte *safe_skip_leb128 (const gdb_byte *buf,
>  					 const gdb_byte *buf_end);
>  
> +extern const struct lval_funcs pieced_value_funcs;
> +
> +/* Allocate a closure for a value formed from separately-described
> +   PIECES.  */
> +
> +struct piece_closure *allocate_piece_closure
> +			(dwarf2_per_cu_data *per_cu,
> +			 dwarf2_per_objfile *per_objfile,
> +			 std::vector<dwarf_expr_piece> &&pieces,
> +			 struct frame_info *frame);

Indent with two columns:

struct piece_closure *allocate_piece_closure
  (dwarf2_per_cu_data *per_cu,
   dwarf2_per_objfile *per_objfile,
   std::vector<dwarf_expr_piece> &&pieces,
   struct frame_info *frame);

> @@ -96,8 +89,7 @@ enum debug_loc_kind
>    DEBUG_LOC_INVALID_ENTRY = -2
>  };
>  
> -/* Helper function which throws an error if a synthetic pointer is
> -   invalid.  */
> +/* See loc.h.  */
>  
>  static void
>  invalid_synthetic_pointer (void)

I don't think you wanted to change this comment.

> @@ -286,4 +278,13 @@ extern int dwarf_reg_to_regnum (struct gdbarch *arch, int dwarf_reg);
>  extern int dwarf_reg_to_regnum_or_error (struct gdbarch *arch,
>  					 ULONGEST dwarf_reg);
>  
> +/* Fetch the value pointed to by a synthetic pointer.  */
> +
> +extern struct value *indirect_synthetic_pointer
> +    (sect_offset die, LONGEST byte_offset,
> +     dwarf2_per_cu_data *per_cu,
> +     dwarf2_per_objfile *per_objfile,
> +     struct frame_info *frame,
> +     struct type *type, bool resolve_abstract_p = false);

This would typically have two columns of indent:

extern struct value *indirect_synthetic_pointer
  (sect_offset die, LONGEST byte_offset,
   dwarf2_per_cu_data *per_cu,
   dwarf2_per_objfile *per_objfile,
   struct frame_info *frame,
   struct type *type, bool resolve_abstract_p = false);

Although I found one instance in this file of a declaration formatted
like you did, it can be confusing.

Simon

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 14/43] Make value_copy also copy the stack data member
  2021-03-01 14:45 ` [PATCH 14/43] Make value_copy also copy the stack data member Zoran Zaric
@ 2021-04-28  2:01   ` Simon Marchi
  2021-04-28 11:43     ` Zoran Zaric
  0 siblings, 1 reply; 86+ messages in thread
From: Simon Marchi @ 2021-04-28  2:01 UTC (permalink / raw)
  To: Zoran Zaric, gdb-patches

On 2021-03-01 9:45 a.m., Zoran Zaric via Gdb-patches wrote:
> Fixing a bug where the value_copy function did not copy the stack data
> member of the struct value. This is needed for the next patch where the
> DWARF expression evaluator is changed to return a single struct value
> object.
> 
>         * value.c (value_copy): Change to also copy the stack data
>          member.

`member` should be aligned with the asterisk.

> ---
>  gdb/value.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/gdb/value.c b/gdb/value.c
> index bddf9a47923..9f3914a5bc5 100644
> --- a/gdb/value.c
> +++ b/gdb/value.c
> @@ -1690,6 +1690,7 @@ value_copy (struct value *arg)
>    val->embedded_offset = value_embedded_offset (arg);
>    val->pointed_to_offset = arg->pointed_to_offset;
>    val->modifiable = arg->modifiable;
> +  val->stack = arg->stack;
>    if (!value_lazy (val))
>      {
>        memcpy (value_contents_all_raw (val), value_contents_all_raw (arg),
> 

LGTM.  Although it would be good to check if there are other leftovers.
For example it seems like the `initialized` field should be copied as
well?

Simon

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 15/43] Make DWARF evaluator return a single struct value
  2021-03-01 14:45 ` [PATCH 15/43] Make DWARF evaluator return a single struct value Zoran Zaric
@ 2021-04-28  2:21   ` Simon Marchi
  2021-04-28 11:47     ` Zoran Zaric
  0 siblings, 1 reply; 86+ messages in thread
From: Simon Marchi @ 2021-04-28  2:21 UTC (permalink / raw)
  To: Zoran Zaric, gdb-patches

> @@ -886,6 +887,162 @@ dwarf_expr_context::push_dwarf_reg_entry_value
>    this->eval (data_src, size);
>  }
>  
> +/* See expr.h.  */
> +
> +struct value *
> +dwarf_expr_context::fetch_result (struct type *type,
> +				  struct type *subobj_type,
> +				  LONGEST subobj_offset)
> +{
> +  struct value *retval = nullptr;
> +
> +  if (type == nullptr)
> +    type = address_type ();
> +
> +  if (subobj_type == nullptr)
> +    subobj_type = type;
> +
> +  if (this->pieces.size () > 0)
> +    {
> +      struct piece_closure *c;

You can move this to where it's first initialized (and drop the struct
keyword).

> +      ULONGEST bit_size = 0;
> +
> +      for (dwarf_expr_piece &piece : this->pieces)
> +	bit_size += piece.size;
> +      /* Complain if the expression is larger than the size of the
> +	 outer type.  */
> +      if (bit_size > 8 * TYPE_LENGTH (type))
> +	invalid_synthetic_pointer ();
> +
> +      c = allocate_piece_closure (this->per_cu, this->per_objfile,
> +				  std::move (this->pieces), this->frame);
> +      retval = allocate_computed_value (subobj_type,
> +					&pieced_value_funcs, c);
> +      set_value_offset (retval, subobj_offset);
> +    }
> +  else
> +    {
> +      switch (this->location)
> +	{
> +	case DWARF_VALUE_REGISTER:
> +	  {
> +	    int dwarf_regnum
> +	      = longest_to_int (value_as_long (this->fetch (0)));
> +	    int gdb_regnum = dwarf_reg_to_regnum_or_error (this->gdbarch,
> +							   dwarf_regnum);
> +
> +	    if (subobj_offset != 0)
> +	      error (_("cannot use offset on synthetic pointer to register"));
> +
> +	    gdb_assert (this->frame != NULL);
> +
> +	    retval = value_from_register (subobj_type, gdb_regnum,
> +					  this->frame);
> +	    if (value_optimized_out (retval))
> +	      {
> +		struct value *tmp;
> +
> +		/* This means the register has undefined value / was
> +		   not saved.  As we're computing the location of some
> +		   variable etc. in the program, not a value for
> +		   inspecting a register ($pc, $sp, etc.), return a
> +		   generic optimized out value instead, so that we show
> +		   <optimized out> instead of <not saved>.  */
> +		tmp = allocate_value (subobj_type);
> +		value_contents_copy (tmp, 0, retval, 0,
> +				     TYPE_LENGTH (subobj_type));
> +		retval = tmp;
> +	      }
> +	  }
> +	  break;
> +
> +	case DWARF_VALUE_MEMORY:
> +	  {
> +	    struct type *ptr_type;
> +	    CORE_ADDR address = this->fetch_address (0);
> +	    bool in_stack_memory = this->fetch_in_stack_memory (0);
> +
> +	    /* DW_OP_deref_size (and possibly other operations too) may
> +	       create a pointer instead of an address.  Ideally, the
> +	       pointer to address conversion would be performed as part
> +	       of those operations, but the type of the object to
> +	       which the address refers is not known at the time of
> +	       the operation.  Therefore, we do the conversion here
> +	       since the type is readily available.  */
> +
> +	    switch (subobj_type->code ())
> +	      {
> +		case TYPE_CODE_FUNC:
> +		case TYPE_CODE_METHOD:
> +		  ptr_type = builtin_type (this->gdbarch)->builtin_func_ptr;
> +		  break;
> +		default:
> +		  ptr_type = builtin_type (this->gdbarch)->builtin_data_ptr;
> +		  break;
> +	      }
> +	    address = value_as_address (value_from_pointer (ptr_type, address));
> +
> +	    retval = value_at_lazy (subobj_type,
> +				    address + subobj_offset);
> +	    if (in_stack_memory)
> +	      set_value_stack (retval, 1);
> +	  }
> +	  break;
> +
> +	case DWARF_VALUE_STACK:
> +	  {
> +	    struct value *value = this->fetch (0);
> +	    size_t n = TYPE_LENGTH (value_type (value));
> +	    size_t len = TYPE_LENGTH (subobj_type);
> +	    size_t max = TYPE_LENGTH (type);
> +
> +	    if (subobj_offset + len > max)
> +	      invalid_synthetic_pointer ();
> +
> +	    retval = allocate_value (subobj_type);
> +
> +	    /* The given offset is relative to the actual object.  */
> +	    if (gdbarch_byte_order (this->gdbarch) == BFD_ENDIAN_BIG)
> +	      subobj_offset += n - max;
> +
> +	    memcpy (value_contents_raw (retval),
> +		    value_contents_all (value) + subobj_offset, len);
> +	  }
> +	  break;
> +
> +	case DWARF_VALUE_LITERAL:
> +	  {
> +	    bfd_byte *contents;

Same here, declare where initialized.

> +	    size_t n = TYPE_LENGTH (subobj_type);
> +
> +	    if (subobj_offset + n > this->len)
> +	      invalid_synthetic_pointer ();
> +
> +	    retval = allocate_value (subobj_type);
> +	    contents = value_contents_raw (retval);
> +	    memcpy (contents, this->data + subobj_offset, n);
> +	  }
> +	  break;
> +
> +	case DWARF_VALUE_OPTIMIZED_OUT:
> +	  retval = allocate_optimized_out_value (subobj_type);
> +	  break;
> +
> +	  /* DWARF_VALUE_IMPLICIT_POINTER was converted to a pieced
> +	     operation by execute_stack_op.  */
> +	case DWARF_VALUE_IMPLICIT_POINTER:
> +	  /* DWARF_VALUE_OPTIMIZED_OUT can't occur in this context --
> +	     it can only be encountered when making a piece.  */
> +	default:
> +	  internal_error (__FILE__, __LINE__, _("invalid location type"));

You could change this to use gdb_assert_not_reached.

> @@ -1479,155 +1481,15 @@ dwarf2_evaluate_loc_desc_full (struct type *type, struct frame_info *frame,
>  	throw;
>      }
>  
> -  if (ctx.pieces.size () > 0)
> -    {
> -      struct piece_closure *c;
> -      ULONGEST bit_size = 0;
> -
> -      for (dwarf_expr_piece &piece : ctx.pieces)
> -	bit_size += piece.size;
> -      /* Complain if the expression is larger than the size of the
> -	 outer type.  */
> -      if (bit_size > 8 * TYPE_LENGTH (type))
> -	invalid_synthetic_pointer ();
> -
> -      c = allocate_piece_closure (per_cu, per_objfile, std::move (ctx.pieces),
> -				  frame);
> -      /* We must clean up the value chain after creating the piece
> -	 closure but before allocating the result.  */
> -      free_values.free_to_mark ();
> -      retval = allocate_computed_value (subobj_type,
> -					&pieced_value_funcs, c);
> -      set_value_offset (retval, subobj_byte_offset);
> -    }
> -  else
> -    {
> -      switch (ctx.location)
> -	{
> -	case DWARF_VALUE_REGISTER:
> -	  {
> -	    struct gdbarch *arch = get_frame_arch (frame);
> -	    int dwarf_regnum
> -	      = longest_to_int (value_as_long (ctx.fetch (0)));
> -	    int gdb_regnum = dwarf_reg_to_regnum_or_error (arch, dwarf_regnum);
> -
> -	    if (subobj_byte_offset != 0)
> -	      error (_("cannot use offset on synthetic pointer to register"));
> -	    free_values.free_to_mark ();
> -	    retval = value_from_register (subobj_type, gdb_regnum, frame);
> -	    if (value_optimized_out (retval))
> -	      {
> -		struct value *tmp;
> -
> -		/* This means the register has undefined value / was
> -		   not saved.  As we're computing the location of some
> -		   variable etc. in the program, not a value for
> -		   inspecting a register ($pc, $sp, etc.), return a
> -		   generic optimized out value instead, so that we show
> -		   <optimized out> instead of <not saved>.  */
> -		tmp = allocate_value (subobj_type);
> -		value_contents_copy (tmp, 0, retval, 0,
> -				     TYPE_LENGTH (subobj_type));
> -		retval = tmp;
> -	      }
> -	  }
> -	  break;
> -
> -	case DWARF_VALUE_MEMORY:
> -	  {
> -	    struct type *ptr_type;
> -	    CORE_ADDR address = ctx.fetch_address (0);
> -	    bool in_stack_memory = ctx.fetch_in_stack_memory (0);
> -
> -	    /* DW_OP_deref_size (and possibly other operations too) may
> -	       create a pointer instead of an address.  Ideally, the
> -	       pointer to address conversion would be performed as part
> -	       of those operations, but the type of the object to
> -	       which the address refers is not known at the time of
> -	       the operation.  Therefore, we do the conversion here
> -	       since the type is readily available.  */
> -
> -	    switch (subobj_type->code ())
> -	      {
> -		case TYPE_CODE_FUNC:
> -		case TYPE_CODE_METHOD:
> -		  ptr_type = builtin_type (ctx.gdbarch)->builtin_func_ptr;
> -		  break;
> -		default:
> -		  ptr_type = builtin_type (ctx.gdbarch)->builtin_data_ptr;
> -		  break;
> -	      }
> -	    address = value_as_address (value_from_pointer (ptr_type, address));
> -
> -	    free_values.free_to_mark ();
> -	    retval = value_at_lazy (subobj_type,
> -				    address + subobj_byte_offset);
> -	    if (in_stack_memory)
> -	      set_value_stack (retval, 1);
> -	  }
> -	  break;
> -
> -	case DWARF_VALUE_STACK:
> -	  {
> -	    struct value *value = ctx.fetch (0);
> -	    size_t n = TYPE_LENGTH (value_type (value));
> -	    size_t len = TYPE_LENGTH (subobj_type);
> -	    size_t max = TYPE_LENGTH (type);
> -	    gdbarch *objfile_gdbarch = per_objfile->objfile->arch ();
> -
> -	    if (subobj_byte_offset + len > max)
> -	      invalid_synthetic_pointer ();
> -
> -	    /* Preserve VALUE because we are going to free values back
> -	       to the mark, but we still need the value contents
> -	       below.  */
> -	    value_ref_ptr value_holder = value_ref_ptr::new_reference (value);
> -	    free_values.free_to_mark ();
> -
> -	    retval = allocate_value (subobj_type);
> -
> -	    /* The given offset is relative to the actual object.  */
> -	    if (gdbarch_byte_order (objfile_gdbarch) == BFD_ENDIAN_BIG)
> -	      subobj_byte_offset += n - max;
> -
> -	    memcpy (value_contents_raw (retval),
> -		    value_contents_all (value) + subobj_byte_offset, len);
> -	  }
> -	  break;
> -
> -	case DWARF_VALUE_LITERAL:
> -	  {
> -	    bfd_byte *contents;
> -	    size_t n = TYPE_LENGTH (subobj_type);
> -
> -	    if (subobj_byte_offset + n > ctx.len)
> -	      invalid_synthetic_pointer ();
> -
> -	    free_values.free_to_mark ();
> -	    retval = allocate_value (subobj_type);
> -	    contents = value_contents_raw (retval);
> -	    memcpy (contents, ctx.data + subobj_byte_offset, n);
> -	  }
> -	  break;
> -
> -	case DWARF_VALUE_OPTIMIZED_OUT:
> -	  free_values.free_to_mark ();
> -	  retval = allocate_optimized_out_value (subobj_type);
> -	  break;
> -
> -	  /* DWARF_VALUE_IMPLICIT_POINTER was converted to a pieced
> -	     operation by execute_stack_op.  */
> -	case DWARF_VALUE_IMPLICIT_POINTER:
> -	  /* DWARF_VALUE_OPTIMIZED_OUT can't occur in this context --
> -	     it can only be encountered when making a piece.  */
> -	default:
> -	  internal_error (__FILE__, __LINE__, _("invalid location type"));
> -	}
> -    }
> -
> -  set_value_initialized (retval, ctx.initialized);
> +  /* We need to clean up all the values that are not needed any more.
> +     The problem with a value_ref_ptr class is that it disconnects the
> +     RETVAL from the value garbage collection, so we need to make
> +     a copy of that value on the stack to keep everything consistent.
> +     The value_ref_ptr will clean up after itself at the end of this block.  */
> +  value_ref_ptr value_holder = value_ref_ptr::new_reference (retval);
> +  free_values.free_to_mark ();
>  
> -  return retval;
> +  return value_copy(retval);

Missing space.

Simon

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 16/43] Simplify dwarf_expr_context class interface
  2021-03-01 14:45 ` [PATCH 16/43] Simplify dwarf_expr_context class interface Zoran Zaric
@ 2021-04-28  2:45   ` Simon Marchi
  2021-04-28 13:15     ` Zoran Zaric
  0 siblings, 1 reply; 86+ messages in thread
From: Simon Marchi @ 2021-04-28  2:45 UTC (permalink / raw)
  To: Zoran Zaric, gdb-patches



On 2021-03-01 9:45 a.m., Zoran Zaric via Gdb-patches wrote:
> From: Zoran Zaric <zoran.zaric@amd.com>
> 
> Idea of this patch is to get a clean and simple public interface for
> the dwarf_expr_context class, looking like:
> 
> - constructor,
> - destructor,
> - push_address method and
> - evaluate method.
> 
> Where constructor should only ever require a target architecture
> information. This information is held in per object file
> (dwarf2_per_objfile) structure, so it makes sense to keep that
> structure as a constructor argument. It also makes sense to get the
> address size from that structure, but unfortunately that interface
> doesn’t exist at the moment, so the dwarf_expr_context class user
> needs to provide that information.
> 
> The push_address method is used to push a CORE_ADDR as a value on
> top of the DWARF stack before the evaluation. This method can be
> later changed to push any struct value object on the stack.
> 
> The evaluate method is the method that evaluates a DWARF expression
> and provides the evaluation result, in a form of a single struct
> value object that describes a location. To do this, the method requires
> a context of the evaluation, as well as expected result type
> information. If the type information is not provided, the DWARF generic
> type will be used instead.
> 
> gdb/ChangeLog:
> 
> 	* dwarf2/expr.c (dwarf_expr_context::dwarf_expr_context): Add
> 	address size argument.
> 	(dwarf_expr_context::read_mem): Change to use property_addr_info
> 	structure.
> 	(dwarf_expr_context::evaluate): New function.
> 	(dwarf_expr_context::execute_stack_op): Change to use
> 	property_addr_info structure.
> 	* dwarf2/expr.h (struct dwarf_expr_context): New evaluate
> 	declaration. Change eval and fetch_result method to private.
> 	* dwarf2/frame.c (execute_stack_op): Change to call evaluate
> 	method.
> 	* dwarf2/loc.c (dwarf2_evaluate_loc_desc_full): Change to call
> 	evaluate method.
> 	(dwarf2_locexpr_baton_eval): Change to call evaluate method.
> ---
>  gdb/dwarf2/expr.c  | 49 ++++++++++++++++++++++++++++++++++++----------
>  gdb/dwarf2/expr.h  | 44 +++++++++++++++++++++++++++--------------
>  gdb/dwarf2/frame.c | 18 ++++-------------
>  gdb/dwarf2/loc.c   | 41 ++++++++++++--------------------------
>  4 files changed, 85 insertions(+), 67 deletions(-)
> 
> diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
> index b2248681899..1645f477d74 100644
> --- a/gdb/dwarf2/expr.c
> +++ b/gdb/dwarf2/expr.c
> @@ -699,8 +699,11 @@ dwarf_expr_context::address_type () const
>  
>  /* Create a new context for the expression evaluator.  */
>  
> -dwarf_expr_context::dwarf_expr_context (dwarf2_per_objfile *per_objfile)
> -: per_objfile (per_objfile)
> +dwarf_expr_context::dwarf_expr_context (dwarf2_per_objfile *per_objfile,
> +					int addr_size)
> +: gdbarch (per_objfile->objfile->arch ()),

If gdbarch is always obtained from the objfile, we could maybe avoid
storing it.

> +  addr_size (addr_size),
> +  per_objfile (per_objfile)
>  {
>  }
>  
> @@ -825,13 +828,17 @@ dwarf_expr_context::read_mem (gdb_byte *buf, CORE_ADDR addr,
>      return;
>  
>    /* Prefer the passed-in memory, if it exists.  */
> -  CORE_ADDR offset = addr - obj_address;
> +  if (addr_info != nullptr)
> +    {
> +      CORE_ADDR offset = addr - addr_info->addr;
>  
> -  if (offset < data_view.size () && offset + length <= data_view.size ())
> +      if (offset < addr_info->valaddr.size ()
> +	  && offset + length <= addr_info->valaddr.size ())
>      {
> -      memcpy (buf, data_view.data (), length);
> +      memcpy (buf, addr_info->valaddr.data (), length);

Not related to this patch, but: I find it odd that the field `valaddr`
contains the object's value.  Why is it called like that?

>        return;
>      }
> +    }

The indentation here is not right.

>  
>    read_memory (addr, buf, length);
>  }
> @@ -874,8 +881,8 @@ dwarf_expr_context::push_dwarf_reg_entry_value
>  						   caller_frame);
>    scoped_restore save_per_cu = make_scoped_restore (&this->per_cu,
>  						    caller_per_cu);
> -  scoped_restore save_obj_addr = make_scoped_restore (&this->obj_address,
> -						      (CORE_ADDR) 0);
> +  scoped_restore save_addr_info = make_scoped_restore (&this->addr_info,
> +						       nullptr);
>    scoped_restore save_per_objfile = make_scoped_restore (&this->per_objfile,
>  							 caller_per_objfile);
>  
> @@ -1043,6 +1050,28 @@ dwarf_expr_context::fetch_result (struct type *type,
>    return retval;
>  }
>  
> +/* See expr.h.  */
> +
> +struct value *
> +dwarf_expr_context::evaluate (const gdb_byte *addr, size_t len,
> +			      struct dwarf2_per_cu_data *per_cu,
> +			      struct frame_info *frame,
> +			      const struct property_addr_info *addr_info,
> +			      struct type *type,
> +			      struct type *subobj_type,
> +			      LONGEST subobj_offset)
> +{
> +  this->per_cu = per_cu;
> +  this->frame = frame;
> +  this->addr_info = addr_info;
> +
> +  if (per_cu != nullptr)
> +    this->ref_addr_size = per_cu->ref_addr_size ();

As mentioned in a previous message, I have the feeling that we could get
rid of the ref_addr_size field and just call per_cu->ref_addr_size when
we need it.  It could always be a follow-up cleanup.

> +
> +  eval (addr, len);
> +  return fetch_result (type, subobj_type, subobj_offset);

This is just me, not an officiel style rule, when I like to use `this->`
when accessing fields and methods, to make it clear what they are (not
global variables or free functions).  Although for fields prefixed with
`m_`, I don't, because it's already obvious by the naming.

> diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
> index a0ac21f2ed1..d1374068732 100644
> --- a/gdb/dwarf2/expr.h
> +++ b/gdb/dwarf2/expr.h
> @@ -119,19 +119,30 @@ struct dwarf_stack_value
>     its current state and its callbacks.  */
>  struct dwarf_expr_context
>  {
> -  dwarf_expr_context (dwarf2_per_objfile *per_objfile);
> +  /* We should ever only pass in the PER_OBJFILE, while the ADDR_SIZE
> +     information should be retrievable from there.  The PER_OBJFILE
> +     contains a pointer to the PER_BFD information anyway and the
> +     address size information must be the same for the whole BFD.  */
> +  dwarf_expr_context (struct dwarf2_per_objfile *per_objfile,
> +		      int addr_size);

I don't really understand the comment.  What do you mean by "We should
ever only pass..."?  In general, I don't find that the comment helps me
understand what the parameters are and do.  Try to change it to be more
concrete and straight to the point.

>    virtual ~dwarf_expr_context () = default;
>  
>    void push_address (CORE_ADDR value, bool in_stack_memory);
> -  void eval (const gdb_byte *addr, size_t len);
>  
> -  /* Fetch the result of the expression evaluation in a form of
> -     a struct value, where TYPE, SUBOBJ_TYPE and SUBOBJ_OFFSET
> -     describe the source level representation of that result.  */
> -  struct value *fetch_result (struct type *type = nullptr,
> -			      struct type *subobj_type = nullptr,
> -			      LONGEST subobj_offset = 0);
> +  /* Evaluate the expression at ADDR (LEN bytes long) in a given PER_CU
> +     FRAME context.  Where TYPE, SUBOBJ_TYPE and SUBOBJ_OFFSET describe
> +     expected struct value representation of the evaluation result.
> +     The ADDR_INFO property can be specified to override the range of
> +     memory addresses with the passed in buffer.  */

It sounds like you are missing a word between PER_CU and FRAME.

The sentence starting with "Where" sounds strange, I'd remove the
"Where" to make it a simple statement.

It sounds like you are missing a "the" between "describe" and
"expected".

> +  struct value *evaluate (const gdb_byte *addr, size_t len,
> +			  struct dwarf2_per_cu_data *per_cu,
> +			  struct frame_info *frame,
> +			  const struct property_addr_info *addr_info = nullptr,
> +			  struct type *type = nullptr,
> +			  struct type *subobj_type = nullptr,
> +			  LONGEST subobj_offset = 0);

Note to self that ADDR + LEN would be good candidates to become a
gdb::array_view later.

>  
> +private:
>    /* The stack of values.  */
>    std::vector<dwarf_stack_value> stack;

If all these fields become private, it would be good to rename them to
add the `m_` prefix.  It could be done at the end (if not already done
later in the series), to avoid the conflicts.

Simon

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 17/43] Add as_lval argument to expression evaluator
  2021-03-01 14:45 ` [PATCH 17/43] Add as_lval argument to expression evaluator Zoran Zaric
@ 2021-04-28  3:04   ` Simon Marchi
  2021-04-28 13:16     ` Zoran Zaric
  2021-04-28  3:30   ` Simon Marchi
  1 sibling, 1 reply; 86+ messages in thread
From: Simon Marchi @ 2021-04-28  3:04 UTC (permalink / raw)
  To: Zoran Zaric, gdb-patches

> @@ -929,6 +930,11 @@ dwarf_expr_context::fetch_result (struct type *type,
>      }
>    else
>      {
> +      /* If AS_LVAL is false, means that the implicit conversion
> +	 from a location description to value is expected.  */
> +      if (as_lval == false)

For booleans: !as_lval

> diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
> index d1374068732..3f519e81849 100644
> --- a/gdb/dwarf2/expr.h
> +++ b/gdb/dwarf2/expr.h
> @@ -130,11 +130,13 @@ struct dwarf_expr_context
>    void push_address (CORE_ADDR value, bool in_stack_memory);
>  
>    /* Evaluate the expression at ADDR (LEN bytes long) in a given PER_CU
> -     FRAME context.  Where TYPE, SUBOBJ_TYPE and SUBOBJ_OFFSET describe
> -     expected struct value representation of the evaluation result.
> -     The ADDR_INFO property can be specified to override the range of
> -     memory addresses with the passed in buffer.  */
> -  struct value *evaluate (const gdb_byte *addr, size_t len,
> +     FRAME context.  AS_LVAL defines if the returned struct value is
> +     expected to be a value or a location description.  Where TYPE,
> +     SUBOBJ_TYPE and SUBOBJ_OFFSET describe expected struct value
> +     representation of the evaluation result.  The ADDR_INFO property
> +     can be specified to override the range of memory addresses with
> +     the passed in buffer.  */

Especially when the comment starts getting long and there are many
parameters, I think it helps readability to space it out a bit.  Start
with one sentence by itself that gives the high level description of
what the method does.  Then one paragraph per parameter (or group of
parameters, if some are logically connected).  So, something like:

  /* Evaluate the expression at ADDR (LEN bytes long) in a given PER_CU
     and FRAME context.

     AS_LVAL defines if the returned struct value is expected to be a
     value or a location description.

     TYPE, SUBOBJ_TYPE, SUBOBJ_OFFSET describe the expected struct value
     representation of the evaluation result.

     ...

It would also be good to clarify for AS_LVAL which of true and false
mean "a value" and "a location description".  It's not clear to me.

Simon

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 18/43] Add new register access interface to expr.c
  2021-03-01 14:45 ` [PATCH 18/43] Add new register access interface to expr.c Zoran Zaric
  2021-03-08 23:52   ` Lancelot SIX
@ 2021-04-28  3:25   ` Simon Marchi
  2021-04-28 13:29     ` Zoran Zaric
  1 sibling, 1 reply; 86+ messages in thread
From: Simon Marchi @ 2021-04-28  3:25 UTC (permalink / raw)
  To: Zoran Zaric, gdb-patches

On 2021-03-01 9:45 a.m., Zoran Zaric via Gdb-patches wrote:
> DWARF expression evaluator is currently using get_frame_register_bytes
> and put_frame_register_bytes interface for register access.
> 
> The problem with evaluator using this interface is that it allows a
> bleed out register access. This means that if the caller specifies a
> larger amount of data then the size of a specified register, the
> operation will continue accessing the neighboring registers until a
> full amount of data has been reached.
> 
> DWARF specification does not define this behavior, so a new simplified
> register access interface is needed instead.
> 
> 	* dwarf2/expr.c (read_from_register): New function.
> 	(write_to_register): New function.
> 	(rw_pieced_value): Now calls the read_from_register and
> 	write_to_register functions.
> ---
>  gdb/dwarf2/expr.c | 128 ++++++++++++++++++++++++++++++++++++++--------
>  1 file changed, 106 insertions(+), 22 deletions(-)
> 
> diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
> index c50bb3c8d90..5a1fd5b941f 100644
> --- a/gdb/dwarf2/expr.c
> +++ b/gdb/dwarf2/expr.c
> @@ -106,6 +106,96 @@ read_addr_from_reg (struct frame_info *frame, int reg)
>    return address_from_register (regnum, frame);
>  }
>  
> +/* Read register REGNUM's contents in a given FRAME context.
> +
> +   The data read is offsetted by OFFSET, and the number of bytes read
> +   is defined by LENGTH.  The data is then copied into the
> +   caller-managed buffer BUF.
> +
> +   If the register is optimized out or unavailable for the given
> +   FRAME, the OPTIMIZED and UNAVAILABLE outputs are set
> +   accordingly  */
> +
> +static void
> +read_from_register (struct frame_info *frame, int regnum,
> +		    CORE_ADDR offset, gdb::array_view<gdb_byte> buf,
> +		    int *optimized, int *unavailable)
> +{
> +  struct gdbarch *gdbarch = get_frame_arch (frame);
> +  int regsize = register_size (gdbarch, regnum);
> +  int numregs = gdbarch_num_cooked_regs (gdbarch);
> +  int length = buf.size ();
> +
> +  /* If a register is wholly inside the OFFSET, skip it.  */
> +  if (frame == NULL || !regsize
> +      || offset + length > regsize || numregs < regnum)

The last line is missing one column of indent.

Can `frame` really be NULL here?  Given that where write_to_register is
used, we have:

    struct frame_info *frame = frame_find_by_id (c->frame_id);
    struct gdbarch *arch = get_frame_arch (frame);

If frame was NULL, it would segfault in get_frame_arch.

Can regsize really be 0?

I don't understand the code and how it relates to the comment.  What
does it mean for a register to be inside an offset?  The expression
`offset + length > regsize` checks that the end of the portion we want
to read is beyond the end of the register.  But there could be a part of
the portion we want to read that is within the register.  The code might
be correct, but the comment needs to express the intention more clearly.

Is `numregs < regnum` really useful?  When would you encounter that?

Simon

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 17/43] Add as_lval argument to expression evaluator
  2021-03-01 14:45 ` [PATCH 17/43] Add as_lval argument to expression evaluator Zoran Zaric
  2021-04-28  3:04   ` Simon Marchi
@ 2021-04-28  3:30   ` Simon Marchi
  1 sibling, 0 replies; 86+ messages in thread
From: Simon Marchi @ 2021-04-28  3:30 UTC (permalink / raw)
  To: Zoran Zaric, gdb-patches, Tom Tromey

Hi Tom,

Up to here (patch 17), I think that Zoran's patches are a good cleanup
of the DWARF expression evaluation system that we could merge regardless
of what follows, do you agree?  From your review of the previous version
of the series, that's what I understood but I want to make sure.

It would make things a bit easier if we finished addressing the comments
on these first 17 patches and got them merged without waiting for the
whole series to be reviewed.

Simon

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 01/43] Replace the symbol needs evaluator with a parser
  2021-04-27  1:20   ` Simon Marchi
@ 2021-04-28 10:17     ` Zoran Zaric
  2021-04-28 14:08       ` Simon Marchi
  0 siblings, 1 reply; 86+ messages in thread
From: Zoran Zaric @ 2021-04-28 10:17 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches


>> Some concerns were raised that a linear scan of the expression byte
>> stream would have issues if a DWARF producer would try to hide some
>> non DWARF related data after a control flow operation. Although the
>> testsuite doesn't show this case, it is a valid concern, so one of
>> the later patches in this series will address it by switching back to
>> the then redesigned DWARF expression evaluator.
> 
> While re-reading your exchange with Tom, I was under the impression that
> traversing the "control flow graph" of the expression, visiting each
> node only once, would be a good solution.  It would avoid the infinite
> loop problem, the "two branches" problem, and even the corner cases
> where you have garbage in the middle of the expression, or if the
> expression jumps in the middle of an instruction to re-use the operand
> of an instruction as an instruction.
> 
> Patch 39 changes this back to an evaluator, so I'm not sure if this is
> what you implemented or something else, I'll see when I get there.

There is no concept of a "control flow graph" in the  existing gdb 
evaluator, there is just a byte stream that gdb either fully traverse 
and evaluates or it "fakes" the target access.

The solution that Tom talked about was just an idea that he used in some 
other tool and would need to be implemented from scratch.

The patch 39 switches the evaluator design to throwing the missing 
context exception idea which re-enables using the same evaluator for 
both purposes.

Having a separate control flow representation is still a good idea for 
the future because it removes the need for byte stream parsing code 
duplication that is present in couple of places.

> 
>> -class symbol_needs_eval_context : public dwarf_expr_context
>> +static enum symbol_needs_kind
>> +dwarf2_get_symbol_read_needs (gdb::array_view<const gdb_byte> expr,
>> +                          dwarf2_per_cu_data *per_cu,
>> +                          dwarf2_per_objfile *per_objfile,
>> +                          bfd_endian byte_order,
>> +                          int addr_size,
>> +                          int ref_addr_size,
>> +                          int depth = 0)
> 
> The wrapped lines above are missing one column of indent.
> 
> Simon
>

Thanks, I will fix this in the next iteration.

Zoran


^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 02/43] Cleanup of the dwarf_expr_context constructor
  2021-04-27  1:23   ` Simon Marchi
@ 2021-04-28 10:19     ` Zoran Zaric
  0 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-04-28 10:19 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches


> 
> On 2021-03-01 9:45 a.m., Zoran Zaric via Gdb-patches wrote:
>> Move the initial values for dwarf_expr_context class data members
>> to the class declaration in expr.h.
>>
>> gdb/ChangeLog:
>>
>>          * dwarf2/expr.c (dwarf_expr_context::dwarf_expr_context):
>>          Remove initial data members values.
>>          * dwarf2/expr.h (dwarf_expr_context): Add initial values
>>          to the class data members.
> 
> LGTM.  I suggest you push this right away, it's a good cleanup and it
> will be one less patch to carry.
> 
> Simon
> 

Fair point.

Zoran

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 03/43] Move frame context info to dwarf_expr_context
  2021-04-27  2:19   ` Simon Marchi
@ 2021-04-28 10:51     ` Zoran Zaric
  2021-04-28 14:14       ` Simon Marchi
  0 siblings, 1 reply; 86+ messages in thread
From: Zoran Zaric @ 2021-04-28 10:51 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches



On 4/27/21 3:19 AM, Simon Marchi wrote:
>>
>> This patch starts the merging proccess by moving the frame context
> 
> proccess -> process

Thanks, I will fix it in the next iteration.

> 
>> @@ -56,6 +57,31 @@ dwarf_gdbarch_types_init (struct gdbarch *gdbarch)
>>     return types;
>>   }
>>
>> +/* Ensure that a FRAME is defined, throw an exception otherwise.
>> +
>> +   Throwing NOT_AVAILABLE_ERROR error so that a client can chose
>> +   to react differently if the evaluation ended because there
>> +   was a missing context information.  */
>> +
>> +static void
>> +ensure_have_frame (struct frame_info *frame, const char *op_name)
>> +{
>> +  if (frame == nullptr)
>> +    throw_error (NOT_AVAILABLE_ERROR,
>> +              _("%s evaluation requires a frame."), op_name);
>> +}
> 
> I don't remember if we discussed about that or not, so I'll ask:
> "available" in GDB terminology usually refers to the tracing
> functionality.  While tracing, you can choose which registers and what
> part of the memory to collect when hitting a tracepoint.  During later
> analysis, if you try for example to print the value of a variable that
> requires some information (register or memory) that you didn't collect,
> we'll say that this register or memory (and therefore the variable's
> value) is not available / unavailable.
> 
> It is also used with the "record" command / concept, where some things
> are recorded during execution to be able to step backwards in time,
> that's pretty much the same as tracing.  If you step backwards in time
> and try to access an information that wasn't recorded and an exception
> is thrown as a result, it will be NOT_AVAILABLE_ERROR.
> 
> So I am a bit worried that by using NOT_AVAILABLE_ERROR to say "to
> evaluate this expression, you need a frame, but there is no frame in the
> current context", we are overloading this exception type / return code.
> 
> I can imagine that I could be inspecting a trace, I try to print a
> variable whose location expression needs to be evaluated.  Evaluating
> the location expression requires accessing some memory or register that
> wasn't recorded in my trace, and that results in throwing
> NOT_AVAILABLE_ERROR.  That would be a different error than trying to
> evaluate an expression that requires a frame in a context that doesn't
> have a frame.
> 
> So it seems to me like a new exception type would be desirable.  Maybe
> INSUFFICIENT_CONTEXT_ERROR?

We did discuss this and at the time the feeling was that we want to 
avoid adding new exception types, then I realized that the GENERIC_ERROR 
error doesn't work after all so I chose the error that made the most 
sense and was already expected by the evaluator callers for other scenarios.

I wouldn't be against adding a new exception type if the feeling is that 
it is justifiable. Actually, I would prefer it to make the solution 
cleaner.

> 
> On an unrelated topic, things like ensure_have_frame always worry me
> because I'm scared I'll forget to call it where necessary.  An
> alternative could be to make sure we get the frame through a getter that
> throws if frame == nullptr.
> 
> Basically, a tiny class (could be internal to dwarf_expr_context):
> 
> struct frame_safe
> {
>    frame_safe (frame_info *frame)
>      : m_frame (frame)
>    {}
> 
>    frame_info *frame (const char *op)
>    {
>      if (m_frame == nullptr)
>        ... throw ...
> 
>      return m_frame;
>    }
> 
> private:
>    frame_info *m_frame;
> };
> 
> dwarf_expr_context would have a `frame_safe m_frame` field and a
> `frame (const char *op)` getter that just does
> `return m_frame->frame (op)`.
> 
> This way, it's impossible to get the frame and forgetting an
> ensure_have_frame call.

True, but then you end up with even more "switch" statements in 
different parts of the gdb that one needs to update every time they add 
a new operation and this is one of the problems with the current design 
as is. This would force us to add two new places (one for frame and one 
for compilation unit) and there will be more in the future when we add a 
concept of a thread to the context.

At least with the current implementation the implementer of the case 
statement that process that operation needs to know which part of the 
context has to be present for that operation, which is clearly defined 
in the DWARF standard as part of that operations definition (or at least 
it will be after our extensions).

Another problem with the new class approach is that it makes the 
information about what each operation needs convoluted and hidden which 
in my mind adds even bigger chance of someone forgetting to add their 
operation to the query calls.

I don't think that there is a good way to avoid this problem. People 
that work on the evaluator changes need to pay close attention to what 
that operation requires.

> 
>> @@ -857,7 +928,7 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
>>            if (this->location == DWARF_VALUE_MEMORY)
>>              result = fetch_address (0);
>>            else if (this->location == DWARF_VALUE_REGISTER)
>> -           result = this->read_addr_from_reg (value_as_long (fetch (0)));
>> +           result = read_addr_from_reg (this->frame, value_as_long (fetch (0)));
> 
> This line is slightly too long.

Thanks, I will fix it in the next iteration.

> 
>> @@ -259,8 +247,23 @@ struct dwarf_expr_context
>>     void add_piece (ULONGEST size, ULONGEST offset);
>>     void execute_stack_op (const gdb_byte *op_ptr, const gdb_byte *op_end);
>>     void pop ();
>> +
>> +  /* Return a value of type TYPE, stored in register number REGNUM
>> +     of the frame associated to the given BATON.
> 
> "the given BATON" is a bit confusing, I'm not sure which BATON this
> refers to.  Could you try to improve this comment?


Thanks, I will fix it in the next iteration.

> 
>> +
>> +     REGNUM is a DWARF register number.  */
>> +  struct value *get_reg_value (struct type *type, int regnum);
>> +
>> +  /* Return the location expression for the frame base attribute, in
>> +     START and LENGTH.  The result must be live until the current
>> +     expression evaluation is complete.  */
>> +  void get_frame_base (const gdb_byte **start, size_t *length);
>>   };
>>
>> +/* Return the value of register number REG (a DWARF register number),
>> +   read as an address in a given FRAME.  */
>> +CORE_ADDR read_addr_from_reg (struct frame_info *, int);
> 
> Can you please add the names to the parameters?  It's a bit confusing
> otherwise, since the comment refers to them.


Thanks, I will fix it in the next iteration.

Zoran

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 05/43] Move compilation unit info to dwarf_expr_context
  2021-04-27  2:58   ` Simon Marchi
@ 2021-04-28 11:28     ` Zoran Zaric
  0 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-04-28 11:28 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches


>> +static void
>> +ensure_have_per_cu (struct dwarf2_per_cu_data *per_cu, const char* op_name)
>> +{
>> +  if (per_cu == nullptr)
>> +    throw_error (NOT_AVAILABLE_ERROR,
>> +              _("%s evaluation requires a compilation unit."), op_name);
>> +}
> 
> Same comment as earlier about NOT_AVAILABLE_ERROR, and about wrapping
> per_cu to force accessing it through a getter that throws if it is
> nullptr (whatever we end up doing for the frame should be done here).

Same comment as for the frame side. I am not convinced that this is a 
better approach for the reasons mentioned in my previous response.

> 
>> +
>>   /* See expr.h.  */
>>
>>   CORE_ADDR
>> @@ -201,6 +215,25 @@ dwarf_expr_context::get_frame_base (const gdb_byte **start,
>>                                   start, length);
>>   }
>>
>> +/* See expr.h.  */
>> +
>> +struct type *
>> +dwarf_expr_context::get_base_type (cu_offset die_cu_off, int size)
>> +{
>> +  if (per_cu == nullptr)
>> +    return builtin_type (this->gdbarch)->builtin_int;
> 
> Ok, so this seems to be a case where we need to check if per_cu is
> nullptr or not.  The getter that throws won't work here, but there could
> be an `has_per_cu ()` method on the side.

As I said, this approach can make things convoluted and difficult for 
someone to extend in the future. How are they supposed to know if they 
should use has_per_cu method or the getter method?

The ensure_* functions are not part of the class interface so they are 
only used where the standard requires them to be used. All other places 
just use the class member in the current design.

> 
>> +
>> +  struct type *result = dwarf2_get_die_type (die_cu_off, per_cu, per_objfile);
>> +
>> +  if (result == NULL)
>> +    error (_("Could not find type for DW_OP_const_type"));
> 
> Might as well change this NULL for nullptr.

Thanks, I will change it in the next iteration.
> 
>> +
>> +  if (size != 0 && TYPE_LENGTH (result) != size)
>> +    error (_("DW_OP_const_type has different sizes for type and data"));
>> +
>> +  return result;
>> +}
> 
> I find get_base_type a bit awkward.  Do you think you could split it in
> two:
> 
>   - get_base_type
>   - get_base_type_check_size
> 
> ... where get_base_type_check_size uses get_base_type?  The callers that
> don't need the size checking wouldn't need to pass 0.
> 
> I also find it awkward that get_base_type hardcodes DW_OP_const_type in
> its error message, given that it's used in the context of many other
> operators.  In the end we understand that DW_OP_const_type is the only
> case that passes size != 0, so it's the only case where that error can
> possibly be thrown.  But if you could pass the op name as a parameter,
> like you did for the ensure_have_per_cu function, I think it would make
> things better.
> 
> Or, since that size checking is only used when handling
> DW_OP_const_type, we could in-line that code in the caller and not
> bother with having get_base_type_check_size.

To be honest, this is not part of my implementation. I just copied it 
from the loc.c file and inlined one existing functions into another 
during the merging process.

Considering that it is mostly used by the GNU specific operations 
(therefore orthogonal to my implementation), and that there is no 
official spec that defines those operations, I am reluctant to change 
that part of the code.

It was already there previously so it shouldn't be a problem to keep it 
as is after my patch series.

> 
>> @@ -834,10 +873,7 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
>>        case DW_OP_GNU_implicit_pointer:
>>          {
>>            int64_t len;
>> -
>> -         if (this->ref_addr_size == -1)
>> -           error (_("DWARF-2 expression error: DW_OP_implicit_pointer "
>> -                    "is not allowed in frame context"));
>> +         ensure_have_per_cu (per_cu, "DW_OP_implicit_pointer");
>>
>>            /* The referred-to DIE of sect_offset kind.  */
>>            this->len = extract_unsigned_integer (op_ptr, this->ref_addr_size,
> 
> This case would be a bit weird with my suggestion of per_cu getter that
> throws if per_cu is nullptr, because nothing actually uses per_cu here.
> But, I think that in the end we could get rid of the
> `this->ref_addr_size` field, and use `this->per_cu ()->ref_addr_size
> ()`.
> 
> Simon
> 

Yes, this would be one benefit of the class approach. But nothing really 
prevent us to always use the per_cu object, considering that all the 
places that use the ref_addr_size information called the 
ensure_have_per_cu function before that.

There are many places in gdb that currently use the same logic for the 
validity of the passed in frame information. Some don't even do that 
much, but that is an issue for some other discussion.

I am still not convinced that this is a better way to go. Both 
approaches have downsides I guess...

Zoran

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 07/43] Move get_object_address to dwarf_expr_context
  2021-04-27  3:12   ` Simon Marchi
@ 2021-04-28 11:34     ` Zoran Zaric
  0 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-04-28 11:34 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches



On 4/27/21 4:12 AM, Simon Marchi wrote:
> [CAUTION: External Email]
> 
>> @@ -204,7 +207,12 @@ struct dwarf_expr_context
>>                                           int deref_size) = 0;
>>
>>     /* Return the `object address' for DW_OP_push_object_address.  */
>> -  virtual CORE_ADDR get_object_address () = 0;
>> +  virtual CORE_ADDR get_object_address ()
>> +  {
>> +    if (obj_address == 0)
>> +      error (_("Location address is not set."));
>> +    return obj_address;
> 
> Instead of having obj_address == 0 as a special case, I'd suggest making
> it a gdb::optional maybe.  To make sure that nobody accesses it
> directly, other than through the getter, you could even apply the same
> pattern of wrapping it that I suggested for the frame and per_cu.
> 
> Should the error thrown here be of the same type as the error thrown for
> if the frame or per_cu is not there (NOT_AVAILABLE_ERROR currently, but
> perhaps subject to change)?
> 
> Simon
> 

This method was just copied from the previous implementation and inlined 
in the later patches.

Zoran

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 09/43] Move push_dwarf_reg_entry_value to expr.c
  2021-04-27  3:56   ` Simon Marchi
@ 2021-04-28 11:36     ` Zoran Zaric
  0 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-04-28 11:36 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches



On 4/27/21 4:56 AM, Simon Marchi wrote:
> [CAUTION: External Email]
> 
>> diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
>> index 872de16cd70..0b7f3c90728 100644
>> --- a/gdb/dwarf2/expr.c
>> +++ b/gdb/dwarf2/expr.c
>> @@ -268,6 +268,57 @@ dwarf_expr_context::read_mem (gdb_byte *buf, CORE_ADDR addr,
>>     read_memory (addr, buf, length);
>>   }
>>
>> +/* See expr.h.  */
>> +
>> +void
>> +dwarf_expr_context::push_dwarf_reg_entry_value
>> +                   (enum call_site_parameter_kind kind,
>> +                    union call_site_parameter_u kind_u,
>> +                    int deref_size)
> 
> When the parameter list doesn't fit in the right of the name, we
> typically to:
> 
> void
> dwarf_expr_context::push_dwarf_reg_entry_value
>    (enum call_site_parameter_kind kind,
>     union call_site_parameter_u kind_u,
>     int deref_size)o
> 
> If we need to wrap, we might as well start completely on the left, that
> leaves more space :).
> 
> Simon
> 

Thank you, I realized this way after I've created these patches :)

I will change that in the next iteration and check for other similar 
places as well.

Zoran

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 12/43] Merge evaluate_for_locexpr_baton evaluator
  2021-04-28  1:33   ` Simon Marchi
@ 2021-04-28 11:39     ` Zoran Zaric
  0 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-04-28 11:39 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches



On 4/28/21 2:33 AM, Simon Marchi wrote:
> 
> On 2021-03-01 9:45 a.m., Zoran Zaric via Gdb-patches wrote:
>> From: Zoran Zaric <zoran.zaric@amd.com>
>>
>> The evaluate_for_locexpr_baton is the last derived class from the
>> dwarf_expr_context class. It's purpose is to support the passed in
>> buffer functionality.
>>
>> Although, it is not really necessary to merge this class with it's
>> base class, doing that simplifies new expression evaluator design.
>>
>> Considering that this functionality is going around the DWARF standard,
>> it is also reasonable to expect that with a new evaluator design and
>> extending the push object address functionality to accept any location
>> description, there will be no need to support passed in buffers.
>>
>> Alternatively, it would also makes sense to abstract the interaction
>> between the evaluator and a given resource in the near future. The
>> passed in buffer would then be a specialization of that abstraction.
>>
>> gdb/ChangeLog:
>>
>>        * dwarf2/expr.c (dwarf_expr_context::read_mem): Merge with
>>        evaluate_for_locexpr_baton implementation.
>>        * dwarf2/loc.c (class evaluate_for_locexpr_baton): Remove
>>        class.
>>        (evaluate_for_locexpr_baton::read_mem): Move to
>>        dwarf_expr_context.
>>        (dwarf2_locexpr_baton_eval): Instantiate dwarf_expr_context
>>        instead of evaluate_for_locexpr_baton class.
>> ---
>>   gdb/dwarf2/expr.c | 19 +++++++++++++++++--
>>   gdb/dwarf2/expr.h | 13 +++----------
>>   gdb/dwarf2/loc.c  | 45 ++-------------------------------------------
>>   3 files changed, 22 insertions(+), 55 deletions(-)
>>
>> diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
>> index 1212be9c984..fe6d072ac8f 100644
>> --- a/gdb/dwarf2/expr.c
>> +++ b/gdb/dwarf2/expr.c
>> @@ -252,6 +252,18 @@ void
>>   dwarf_expr_context::read_mem (gdb_byte *buf, CORE_ADDR addr,
>>                              size_t length)
>>   {
>> +  if (length == 0)
>> +    return;
> 
> I'm really curious about when `length == 0` happens, but I see it was
> there previously, so this ok.  But if you know the answer, I want to
> know.

I am curious about that as well. I might remove it to check if something 
fails at one point.

> 
>> @@ -239,6 +231,7 @@ struct dwarf_expr_context
>>     void push_dwarf_reg_entry_value (enum call_site_parameter_kind kind,
>>                                   union call_site_parameter_u kind_u,
>>                                   int deref_size);
>> +  void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t length);
> 
> You could keep the comment that was there previously over the read_mem
> method signature.
> 
> Simon
> 

Thanks, I was sure that I've fixed that already...

Zoran

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 13/43] Move piece_closure and its support to expr.c
  2021-04-28  1:56   ` Simon Marchi
@ 2021-04-28 11:40     ` Zoran Zaric
  0 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-04-28 11:40 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches



On 4/28/21 2:56 AM, Simon Marchi wrote:
> [CAUTION: External Email]
> 
> On 2021-03-01 9:45 a.m., Zoran Zaric via Gdb-patches wrote:
>> @@ -300,4 +301,15 @@ extern const gdb_byte *safe_read_sleb128 (const gdb_byte *buf,
>>   extern const gdb_byte *safe_skip_leb128 (const gdb_byte *buf,
>>                                         const gdb_byte *buf_end);
>>
>> +extern const struct lval_funcs pieced_value_funcs;
>> +
>> +/* Allocate a closure for a value formed from separately-described
>> +   PIECES.  */
>> +
>> +struct piece_closure *allocate_piece_closure
>> +                     (dwarf2_per_cu_data *per_cu,
>> +                      dwarf2_per_objfile *per_objfile,
>> +                      std::vector<dwarf_expr_piece> &&pieces,
>> +                      struct frame_info *frame);
> 
> Indent with two columns:
> 
> struct piece_closure *allocate_piece_closure
>    (dwarf2_per_cu_data *per_cu,
>     dwarf2_per_objfile *per_objfile,
>     std::vector<dwarf_expr_piece> &&pieces,
>     struct frame_info *frame);

Thanks, I will fix it in the next iteration.
> 
>> @@ -96,8 +89,7 @@ enum debug_loc_kind
>>     DEBUG_LOC_INVALID_ENTRY = -2
>>   };
>>
>> -/* Helper function which throws an error if a synthetic pointer is
>> -   invalid.  */
>> +/* See loc.h.  */
>>
>>   static void
>>   invalid_synthetic_pointer (void)
> 
> I don't think you wanted to change this comment.
>


Thanks, I will fix it in the next iteration.

>> @@ -286,4 +278,13 @@ extern int dwarf_reg_to_regnum (struct gdbarch *arch, int dwarf_reg);
>>   extern int dwarf_reg_to_regnum_or_error (struct gdbarch *arch,
>>                                         ULONGEST dwarf_reg);
>>
>> +/* Fetch the value pointed to by a synthetic pointer.  */
>> +
>> +extern struct value *indirect_synthetic_pointer
>> +    (sect_offset die, LONGEST byte_offset,
>> +     dwarf2_per_cu_data *per_cu,
>> +     dwarf2_per_objfile *per_objfile,
>> +     struct frame_info *frame,
>> +     struct type *type, bool resolve_abstract_p = false);
> 
> This would typically have two columns of indent:
> 
> extern struct value *indirect_synthetic_pointer
>    (sect_offset die, LONGEST byte_offset,
>     dwarf2_per_cu_data *per_cu,
>     dwarf2_per_objfile *per_objfile,
>     struct frame_info *frame,
>     struct type *type, bool resolve_abstract_p = false);
> 
> Although I found one instance in this file of a declaration formatted
> like you did, it can be confusing.
> 
> Simon
> 

Thanks, I will fix it in the next iteration.

Zoran

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 14/43] Make value_copy also copy the stack data member
  2021-04-28  2:01   ` Simon Marchi
@ 2021-04-28 11:43     ` Zoran Zaric
  0 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-04-28 11:43 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches



On 4/28/21 3:01 AM, Simon Marchi wrote:
> [CAUTION: External Email]
> 
> On 2021-03-01 9:45 a.m., Zoran Zaric via Gdb-patches wrote:
>> Fixing a bug where the value_copy function did not copy the stack data
>> member of the struct value. This is needed for the next patch where the
>> DWARF expression evaluator is changed to return a single struct value
>> object.
>>
>>          * value.c (value_copy): Change to also copy the stack data
>>           member.
> 
> `member` should be aligned with the asterisk.

Thanks, I will fix it in the next iteration.

> 
>> ---
>>   gdb/value.c | 1 +
>>   1 file changed, 1 insertion(+)
>>
>> diff --git a/gdb/value.c b/gdb/value.c
>> index bddf9a47923..9f3914a5bc5 100644
>> --- a/gdb/value.c
>> +++ b/gdb/value.c
>> @@ -1690,6 +1690,7 @@ value_copy (struct value *arg)
>>     val->embedded_offset = value_embedded_offset (arg);
>>     val->pointed_to_offset = arg->pointed_to_offset;
>>     val->modifiable = arg->modifiable;
>> +  val->stack = arg->stack;
>>     if (!value_lazy (val))
>>       {
>>         memcpy (value_contents_all_raw (val), value_contents_all_raw (arg),
>>
> 
> LGTM.  Although it would be good to check if there are other leftovers.
> For example it seems like the `initialized` field should be copied as
> well?
> 
> Simon
> 

Yeah, I need to add the initialized there too and run the test to see 
what happens.

I am confused how this wasn't noticed before though. There might be more 
  information that is missing.

Zoran

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 15/43] Make DWARF evaluator return a single struct value
  2021-04-28  2:21   ` Simon Marchi
@ 2021-04-28 11:47     ` Zoran Zaric
  2021-04-28 14:24       ` Simon Marchi
  0 siblings, 1 reply; 86+ messages in thread
From: Zoran Zaric @ 2021-04-28 11:47 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches



On 4/28/21 3:21 AM, Simon Marchi wrote:
> [CAUTION: External Email]
> 
>> @@ -886,6 +887,162 @@ dwarf_expr_context::push_dwarf_reg_entry_value
>>     this->eval (data_src, size);
>>   }
>>
>> +/* See expr.h.  */
>> +
>> +struct value *
>> +dwarf_expr_context::fetch_result (struct type *type,
>> +                               struct type *subobj_type,
>> +                               LONGEST subobj_offset)
>> +{
>> +  struct value *retval = nullptr;
>> +
>> +  if (type == nullptr)
>> +    type = address_type ();
>> +
>> +  if (subobj_type == nullptr)
>> +    subobj_type = type;
>> +
>> +  if (this->pieces.size () > 0)
>> +    {
>> +      struct piece_closure *c;
> 
> You can move this to where it's first initialized (and drop the struct
> keyword).
> 
>> +      ULONGEST bit_size = 0;
>> +
>> +      for (dwarf_expr_piece &piece : this->pieces)
>> +     bit_size += piece.size;
>> +      /* Complain if the expression is larger than the size of the
>> +      outer type.  */
>> +      if (bit_size > 8 * TYPE_LENGTH (type))
>> +     invalid_synthetic_pointer ();
>> +
>> +      c = allocate_piece_closure (this->per_cu, this->per_objfile,
>> +                               std::move (this->pieces), this->frame);
>> +      retval = allocate_computed_value (subobj_type,
>> +                                     &pieced_value_funcs, c);
>> +      set_value_offset (retval, subobj_offset);
>> +    }
>> +  else
>> +    {
>> +      switch (this->location)
>> +     {
>> +     case DWARF_VALUE_REGISTER:
>> +       {
>> +         int dwarf_regnum
>> +           = longest_to_int (value_as_long (this->fetch (0)));
>> +         int gdb_regnum = dwarf_reg_to_regnum_or_error (this->gdbarch,
>> +                                                        dwarf_regnum);
>> +
>> +         if (subobj_offset != 0)
>> +           error (_("cannot use offset on synthetic pointer to register"));
>> +
>> +         gdb_assert (this->frame != NULL);
>> +
>> +         retval = value_from_register (subobj_type, gdb_regnum,
>> +                                       this->frame);
>> +         if (value_optimized_out (retval))
>> +           {
>> +             struct value *tmp;
>> +
>> +             /* This means the register has undefined value / was
>> +                not saved.  As we're computing the location of some
>> +                variable etc. in the program, not a value for
>> +                inspecting a register ($pc, $sp, etc.), return a
>> +                generic optimized out value instead, so that we show
>> +                <optimized out> instead of <not saved>.  */
>> +             tmp = allocate_value (subobj_type);
>> +             value_contents_copy (tmp, 0, retval, 0,
>> +                                  TYPE_LENGTH (subobj_type));
>> +             retval = tmp;
>> +           }
>> +       }
>> +       break;
>> +
>> +     case DWARF_VALUE_MEMORY:
>> +       {
>> +         struct type *ptr_type;
>> +         CORE_ADDR address = this->fetch_address (0);
>> +         bool in_stack_memory = this->fetch_in_stack_memory (0);
>> +
>> +         /* DW_OP_deref_size (and possibly other operations too) may
>> +            create a pointer instead of an address.  Ideally, the
>> +            pointer to address conversion would be performed as part
>> +            of those operations, but the type of the object to
>> +            which the address refers is not known at the time of
>> +            the operation.  Therefore, we do the conversion here
>> +            since the type is readily available.  */
>> +
>> +         switch (subobj_type->code ())
>> +           {
>> +             case TYPE_CODE_FUNC:
>> +             case TYPE_CODE_METHOD:
>> +               ptr_type = builtin_type (this->gdbarch)->builtin_func_ptr;
>> +               break;
>> +             default:
>> +               ptr_type = builtin_type (this->gdbarch)->builtin_data_ptr;
>> +               break;
>> +           }
>> +         address = value_as_address (value_from_pointer (ptr_type, address));
>> +
>> +         retval = value_at_lazy (subobj_type,
>> +                                 address + subobj_offset);
>> +         if (in_stack_memory)
>> +           set_value_stack (retval, 1);
>> +       }
>> +       break;
>> +
>> +     case DWARF_VALUE_STACK:
>> +       {
>> +         struct value *value = this->fetch (0);
>> +         size_t n = TYPE_LENGTH (value_type (value));
>> +         size_t len = TYPE_LENGTH (subobj_type);
>> +         size_t max = TYPE_LENGTH (type);
>> +
>> +         if (subobj_offset + len > max)
>> +           invalid_synthetic_pointer ();
>> +
>> +         retval = allocate_value (subobj_type);
>> +
>> +         /* The given offset is relative to the actual object.  */
>> +         if (gdbarch_byte_order (this->gdbarch) == BFD_ENDIAN_BIG)
>> +           subobj_offset += n - max;
>> +
>> +         memcpy (value_contents_raw (retval),
>> +                 value_contents_all (value) + subobj_offset, len);
>> +       }
>> +       break;
>> +
>> +     case DWARF_VALUE_LITERAL:
>> +       {
>> +         bfd_byte *contents;
> 
> Same here, declare where initialized.
> 
>> +         size_t n = TYPE_LENGTH (subobj_type);
>> +
>> +         if (subobj_offset + n > this->len)
>> +           invalid_synthetic_pointer ();
>> +
>> +         retval = allocate_value (subobj_type);
>> +         contents = value_contents_raw (retval);
>> +         memcpy (contents, this->data + subobj_offset, n);
>> +       }
>> +       break;
>> +
>> +     case DWARF_VALUE_OPTIMIZED_OUT:
>> +       retval = allocate_optimized_out_value (subobj_type);
>> +       break;
>> +
>> +       /* DWARF_VALUE_IMPLICIT_POINTER was converted to a pieced
>> +          operation by execute_stack_op.  */
>> +     case DWARF_VALUE_IMPLICIT_POINTER:
>> +       /* DWARF_VALUE_OPTIMIZED_OUT can't occur in this context --
>> +          it can only be encountered when making a piece.  */
>> +     default:
>> +       internal_error (__FILE__, __LINE__, _("invalid location type"));
> 
> You could change this to use gdb_assert_not_reached.
> 
>> @@ -1479,155 +1481,15 @@ dwarf2_evaluate_loc_desc_full (struct type *type, struct frame_info *frame,
>>        throw;
>>       }
>>
>> -  if (ctx.pieces.size () > 0)
>> -    {
>> -      struct piece_closure *c;
>> -      ULONGEST bit_size = 0;
>> -
>> -      for (dwarf_expr_piece &piece : ctx.pieces)
>> -     bit_size += piece.size;
>> -      /* Complain if the expression is larger than the size of the
>> -      outer type.  */
>> -      if (bit_size > 8 * TYPE_LENGTH (type))
>> -     invalid_synthetic_pointer ();
>> -
>> -      c = allocate_piece_closure (per_cu, per_objfile, std::move (ctx.pieces),
>> -                               frame);
>> -      /* We must clean up the value chain after creating the piece
>> -      closure but before allocating the result.  */
>> -      free_values.free_to_mark ();
>> -      retval = allocate_computed_value (subobj_type,
>> -                                     &pieced_value_funcs, c);
>> -      set_value_offset (retval, subobj_byte_offset);
>> -    }
>> -  else
>> -    {
>> -      switch (ctx.location)
>> -     {
>> -     case DWARF_VALUE_REGISTER:
>> -       {
>> -         struct gdbarch *arch = get_frame_arch (frame);
>> -         int dwarf_regnum
>> -           = longest_to_int (value_as_long (ctx.fetch (0)));
>> -         int gdb_regnum = dwarf_reg_to_regnum_or_error (arch, dwarf_regnum);
>> -
>> -         if (subobj_byte_offset != 0)
>> -           error (_("cannot use offset on synthetic pointer to register"));
>> -         free_values.free_to_mark ();
>> -         retval = value_from_register (subobj_type, gdb_regnum, frame);
>> -         if (value_optimized_out (retval))
>> -           {
>> -             struct value *tmp;
>> -
>> -             /* This means the register has undefined value / was
>> -                not saved.  As we're computing the location of some
>> -                variable etc. in the program, not a value for
>> -                inspecting a register ($pc, $sp, etc.), return a
>> -                generic optimized out value instead, so that we show
>> -                <optimized out> instead of <not saved>.  */
>> -             tmp = allocate_value (subobj_type);
>> -             value_contents_copy (tmp, 0, retval, 0,
>> -                                  TYPE_LENGTH (subobj_type));
>> -             retval = tmp;
>> -           }
>> -       }
>> -       break;
>> -
>> -     case DWARF_VALUE_MEMORY:
>> -       {
>> -         struct type *ptr_type;
>> -         CORE_ADDR address = ctx.fetch_address (0);
>> -         bool in_stack_memory = ctx.fetch_in_stack_memory (0);
>> -
>> -         /* DW_OP_deref_size (and possibly other operations too) may
>> -            create a pointer instead of an address.  Ideally, the
>> -            pointer to address conversion would be performed as part
>> -            of those operations, but the type of the object to
>> -            which the address refers is not known at the time of
>> -            the operation.  Therefore, we do the conversion here
>> -            since the type is readily available.  */
>> -
>> -         switch (subobj_type->code ())
>> -           {
>> -             case TYPE_CODE_FUNC:
>> -             case TYPE_CODE_METHOD:
>> -               ptr_type = builtin_type (ctx.gdbarch)->builtin_func_ptr;
>> -               break;
>> -             default:
>> -               ptr_type = builtin_type (ctx.gdbarch)->builtin_data_ptr;
>> -               break;
>> -           }
>> -         address = value_as_address (value_from_pointer (ptr_type, address));
>> -
>> -         free_values.free_to_mark ();
>> -         retval = value_at_lazy (subobj_type,
>> -                                 address + subobj_byte_offset);
>> -         if (in_stack_memory)
>> -           set_value_stack (retval, 1);
>> -       }
>> -       break;
>> -
>> -     case DWARF_VALUE_STACK:
>> -       {
>> -         struct value *value = ctx.fetch (0);
>> -         size_t n = TYPE_LENGTH (value_type (value));
>> -         size_t len = TYPE_LENGTH (subobj_type);
>> -         size_t max = TYPE_LENGTH (type);
>> -         gdbarch *objfile_gdbarch = per_objfile->objfile->arch ();
>> -
>> -         if (subobj_byte_offset + len > max)
>> -           invalid_synthetic_pointer ();
>> -
>> -         /* Preserve VALUE because we are going to free values back
>> -            to the mark, but we still need the value contents
>> -            below.  */
>> -         value_ref_ptr value_holder = value_ref_ptr::new_reference (value);
>> -         free_values.free_to_mark ();
>> -
>> -         retval = allocate_value (subobj_type);
>> -
>> -         /* The given offset is relative to the actual object.  */
>> -         if (gdbarch_byte_order (objfile_gdbarch) == BFD_ENDIAN_BIG)
>> -           subobj_byte_offset += n - max;
>> -
>> -         memcpy (value_contents_raw (retval),
>> -                 value_contents_all (value) + subobj_byte_offset, len);
>> -       }
>> -       break;
>> -
>> -     case DWARF_VALUE_LITERAL:
>> -       {
>> -         bfd_byte *contents;
>> -         size_t n = TYPE_LENGTH (subobj_type);
>> -
>> -         if (subobj_byte_offset + n > ctx.len)
>> -           invalid_synthetic_pointer ();
>> -
>> -         free_values.free_to_mark ();
>> -         retval = allocate_value (subobj_type);
>> -         contents = value_contents_raw (retval);
>> -         memcpy (contents, ctx.data + subobj_byte_offset, n);
>> -       }
>> -       break;
>> -
>> -     case DWARF_VALUE_OPTIMIZED_OUT:
>> -       free_values.free_to_mark ();
>> -       retval = allocate_optimized_out_value (subobj_type);
>> -       break;
>> -
>> -       /* DWARF_VALUE_IMPLICIT_POINTER was converted to a pieced
>> -          operation by execute_stack_op.  */
>> -     case DWARF_VALUE_IMPLICIT_POINTER:
>> -       /* DWARF_VALUE_OPTIMIZED_OUT can't occur in this context --
>> -          it can only be encountered when making a piece.  */
>> -     default:
>> -       internal_error (__FILE__, __LINE__, _("invalid location type"));
>> -     }
>> -    }

I've just copied this whole block from the loc.c. Everything is replaced 
with a new implementation in the later patch.

Do you still feel that I need to clean it up here or it doesn't really 
matter considering the later patches?

>> -
>> -  set_value_initialized (retval, ctx.initialized);
>> +  /* We need to clean up all the values that are not needed any more.
>> +     The problem with a value_ref_ptr class is that it disconnects the
>> +     RETVAL from the value garbage collection, so we need to make
>> +     a copy of that value on the stack to keep everything consistent.
>> +     The value_ref_ptr will clean up after itself at the end of this block.  */
>> +  value_ref_ptr value_holder = value_ref_ptr::new_reference (retval);
>> +  free_values.free_to_mark ();
>>
>> -  return retval;
>> +  return value_copy(retval);
> 
> Missing space.
> 
> Simon
> 

Thanks, I will fix it in the next iteration.

Zoran

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 16/43] Simplify dwarf_expr_context class interface
  2021-04-28  2:45   ` Simon Marchi
@ 2021-04-28 13:15     ` Zoran Zaric
  2021-04-28 14:41       ` Simon Marchi
  2021-04-29 15:49       ` Simon Marchi
  0 siblings, 2 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-04-28 13:15 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches


>>   /* Create a new context for the expression evaluator.  */
>>
>> -dwarf_expr_context::dwarf_expr_context (dwarf2_per_objfile *per_objfile)
>> -: per_objfile (per_objfile)
>> +dwarf_expr_context::dwarf_expr_context (dwarf2_per_objfile *per_objfile,
>> +                                     int addr_size)
>> +: gdbarch (per_objfile->objfile->arch ()),
> 
> If gdbarch is always obtained from the objfile, we could maybe avoid
> storing it.

Sure, but it is used so many times (often as a few times per operation) 
so we currently cut off two indirection per each use.

I think having one pointer as a class member that cuts through all that 
is worth it.

> 
>> +  addr_size (addr_size),
>> +  per_objfile (per_objfile)
>>   {
>>   }
>>
>> @@ -825,13 +828,17 @@ dwarf_expr_context::read_mem (gdb_byte *buf, CORE_ADDR addr,
>>       return;
>>
>>     /* Prefer the passed-in memory, if it exists.  */
>> -  CORE_ADDR offset = addr - obj_address;
>> +  if (addr_info != nullptr)
>> +    {
>> +      CORE_ADDR offset = addr - addr_info->addr;
>>
>> -  if (offset < data_view.size () && offset + length <= data_view.size ())
>> +      if (offset < addr_info->valaddr.size ()
>> +       && offset + length <= addr_info->valaddr.size ())
>>       {
>> -      memcpy (buf, data_view.data (), length);
>> +      memcpy (buf, addr_info->valaddr.data (), length);
> 
> Not related to this patch, but: I find it odd that the field `valaddr`
> contains the object's value.  Why is it called like that?

My understanding is that this is because it often (although not always) 
represents a pointer to a struct value contents of some object in cases 
where the DWARF spec is not descriptive enough to support the actual case.

This is something that could be replaced by our extensions in the future 
but it would probably require a compiler change.

> 
>>         return;
>>       }
>> +    }
> 
> The indentation here is not right.
> 
>>
>>     read_memory (addr, buf, length);
>>   }
>> @@ -874,8 +881,8 @@ dwarf_expr_context::push_dwarf_reg_entry_value
>>                                                   caller_frame);
>>     scoped_restore save_per_cu = make_scoped_restore (&this->per_cu,
>>                                                    caller_per_cu);
>> -  scoped_restore save_obj_addr = make_scoped_restore (&this->obj_address,
>> -                                                   (CORE_ADDR) 0);
>> +  scoped_restore save_addr_info = make_scoped_restore (&this->addr_info,
>> +                                                    nullptr);
>>     scoped_restore save_per_objfile = make_scoped_restore (&this->per_objfile,
>>                                                         caller_per_objfile);
>>
>> @@ -1043,6 +1050,28 @@ dwarf_expr_context::fetch_result (struct type *type,
>>     return retval;
>>   }
>>
>> +/* See expr.h.  */
>> +
>> +struct value *
>> +dwarf_expr_context::evaluate (const gdb_byte *addr, size_t len,
>> +                           struct dwarf2_per_cu_data *per_cu,
>> +                           struct frame_info *frame,
>> +                           const struct property_addr_info *addr_info,
>> +                           struct type *type,
>> +                           struct type *subobj_type,
>> +                           LONGEST subobj_offset)
>> +{
>> +  this->per_cu = per_cu;
>> +  this->frame = frame;
>> +  this->addr_info = addr_info;
>> +
>> +  if (per_cu != nullptr)
>> +    this->ref_addr_size = per_cu->ref_addr_size ();
> 
> As mentioned in a previous message, I have the feeling that we could get
> rid of the ref_addr_size field and just call per_cu->ref_addr_size when
> we need it.  It could always be a follow-up cleanup.

Agreed.

> 
>> +
>> +  eval (addr, len);
>> +  return fetch_result (type, subobj_type, subobj_offset);
> 
> This is just me, not an officiel style rule, when I like to use `this->`
> when accessing fields and methods, to make it clear what they are (not
> global variables or free functions).  Although for fields prefixed with
> `m_`, I don't, because it's already obvious by the naming.

I was wondering about this because the old code was inconsistent. I will 
probably do like a systematic change for all places in one of the new 
patches.

> 
>> diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
>> index a0ac21f2ed1..d1374068732 100644
>> --- a/gdb/dwarf2/expr.h
>> +++ b/gdb/dwarf2/expr.h
>> @@ -119,19 +119,30 @@ struct dwarf_stack_value
>>      its current state and its callbacks.  */
>>   struct dwarf_expr_context
>>   {
>> -  dwarf_expr_context (dwarf2_per_objfile *per_objfile);
>> +  /* We should ever only pass in the PER_OBJFILE, while the ADDR_SIZE
>> +     information should be retrievable from there.  The PER_OBJFILE
>> +     contains a pointer to the PER_BFD information anyway and the
>> +     address size information must be the same for the whole BFD.  */
>> +  dwarf_expr_context (struct dwarf2_per_objfile *per_objfile,
>> +                   int addr_size);
> 
> I don't really understand the comment.  What do you mean by "We should
> ever only pass..."?  In general, I don't find that the comment helps me
> understand what the parameters are and do.  Try to change it to be more
> concrete and straight to the point.

This was more me venting about that particular design choice because the 
reality is that an address size has to be consistent with what is 
defined in the original object file but there is no interface to always 
get it in a clean way in gdb.

It makes sense for me to remove that comment altogether in the next 
iteration and thing about a future change that would clean it up.

> 
>>     virtual ~dwarf_expr_context () = default;
>>
>>     void push_address (CORE_ADDR value, bool in_stack_memory);
>> -  void eval (const gdb_byte *addr, size_t len);
>>
>> -  /* Fetch the result of the expression evaluation in a form of
>> -     a struct value, where TYPE, SUBOBJ_TYPE and SUBOBJ_OFFSET
>> -     describe the source level representation of that result.  */
>> -  struct value *fetch_result (struct type *type = nullptr,
>> -                           struct type *subobj_type = nullptr,
>> -                           LONGEST subobj_offset = 0);
>> +  /* Evaluate the expression at ADDR (LEN bytes long) in a given PER_CU
>> +     FRAME context.  Where TYPE, SUBOBJ_TYPE and SUBOBJ_OFFSET describe
>> +     expected struct value representation of the evaluation result.
>> +     The ADDR_INFO property can be specified to override the range of
>> +     memory addresses with the passed in buffer.  */
> 
> It sounds like you are missing a word between PER_CU and FRAME.
> 
> The sentence starting with "Where" sounds strange, I'd remove the
> "Where" to make it a simple statement.
> 
> It sounds like you are missing a "the" between "describe" and
> "expected".

Thank you, I will change it in the next iteration.

> 
>> +  struct value *evaluate (const gdb_byte *addr, size_t len,
>> +                       struct dwarf2_per_cu_data *per_cu,
>> +                       struct frame_info *frame,
>> +                       const struct property_addr_info *addr_info = nullptr,
>> +                       struct type *type = nullptr,
>> +                       struct type *subobj_type = nullptr,
>> +                       LONGEST subobj_offset = 0);
> 
> Note to self that ADDR + LEN would be good candidates to become a
> gdb::array_view later.
> 

Agreed.

>>
>> +private:
>>     /* The stack of values.  */
>>     std::vector<dwarf_stack_value> stack;
> 
> If all these fields become private, it would be good to rename them to
> add the `m_` prefix.  It could be done at the end (if not already done
> later in the series), to avoid the conflicts.
> 
> Simon
> 

I didn't do it, but I will in the next iteration.

Zoran

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 17/43] Add as_lval argument to expression evaluator
  2021-04-28  3:04   ` Simon Marchi
@ 2021-04-28 13:16     ` Zoran Zaric
  0 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-04-28 13:16 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches



On 4/28/21 4:04 AM, Simon Marchi wrote:
> [CAUTION: External Email]
> 
>> @@ -929,6 +930,11 @@ dwarf_expr_context::fetch_result (struct type *type,
>>       }
>>     else
>>       {
>> +      /* If AS_LVAL is false, means that the implicit conversion
>> +      from a location description to value is expected.  */
>> +      if (as_lval == false)
> 
> For booleans: !as_lval
> 
>> diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
>> index d1374068732..3f519e81849 100644
>> --- a/gdb/dwarf2/expr.h
>> +++ b/gdb/dwarf2/expr.h
>> @@ -130,11 +130,13 @@ struct dwarf_expr_context
>>     void push_address (CORE_ADDR value, bool in_stack_memory);
>>
>>     /* Evaluate the expression at ADDR (LEN bytes long) in a given PER_CU
>> -     FRAME context.  Where TYPE, SUBOBJ_TYPE and SUBOBJ_OFFSET describe
>> -     expected struct value representation of the evaluation result.
>> -     The ADDR_INFO property can be specified to override the range of
>> -     memory addresses with the passed in buffer.  */
>> -  struct value *evaluate (const gdb_byte *addr, size_t len,
>> +     FRAME context.  AS_LVAL defines if the returned struct value is
>> +     expected to be a value or a location description.  Where TYPE,
>> +     SUBOBJ_TYPE and SUBOBJ_OFFSET describe expected struct value
>> +     representation of the evaluation result.  The ADDR_INFO property
>> +     can be specified to override the range of memory addresses with
>> +     the passed in buffer.  */
> 
> Especially when the comment starts getting long and there are many
> parameters, I think it helps readability to space it out a bit.  Start
> with one sentence by itself that gives the high level description of
> what the method does.  Then one paragraph per parameter (or group of
> parameters, if some are logically connected).  So, something like:
> 
>    /* Evaluate the expression at ADDR (LEN bytes long) in a given PER_CU
>       and FRAME context.
> 
>       AS_LVAL defines if the returned struct value is expected to be a
>       value or a location description.
> 
>       TYPE, SUBOBJ_TYPE, SUBOBJ_OFFSET describe the expected struct value
>       representation of the evaluation result.
> 
>       ...
> 
> It would also be good to clarify for AS_LVAL which of true and false
> mean "a value" and "a location description".  It's not clear to me.
> 
> Simon
> 

Agreed, I will change it in the next iteration.

Zoran

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 18/43] Add new register access interface to expr.c
  2021-04-28  3:25   ` Simon Marchi
@ 2021-04-28 13:29     ` Zoran Zaric
  2021-04-28 14:48       ` Simon Marchi
  0 siblings, 1 reply; 86+ messages in thread
From: Zoran Zaric @ 2021-04-28 13:29 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches


> 
> On 2021-03-01 9:45 a.m., Zoran Zaric via Gdb-patches wrote:
>> DWARF expression evaluator is currently using get_frame_register_bytes
>> and put_frame_register_bytes interface for register access.
>>
>> The problem with evaluator using this interface is that it allows a
>> bleed out register access. This means that if the caller specifies a
>> larger amount of data then the size of a specified register, the
>> operation will continue accessing the neighboring registers until a
>> full amount of data has been reached.
>>
>> DWARF specification does not define this behavior, so a new simplified
>> register access interface is needed instead.
>>
>>        * dwarf2/expr.c (read_from_register): New function.
>>        (write_to_register): New function.
>>        (rw_pieced_value): Now calls the read_from_register and
>>        write_to_register functions.
>> ---
>>   gdb/dwarf2/expr.c | 128 ++++++++++++++++++++++++++++++++++++++--------
>>   1 file changed, 106 insertions(+), 22 deletions(-)
>>
>> diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
>> index c50bb3c8d90..5a1fd5b941f 100644
>> --- a/gdb/dwarf2/expr.c
>> +++ b/gdb/dwarf2/expr.c
>> @@ -106,6 +106,96 @@ read_addr_from_reg (struct frame_info *frame, int reg)
>>     return address_from_register (regnum, frame);
>>   }
>>
>> +/* Read register REGNUM's contents in a given FRAME context.
>> +
>> +   The data read is offsetted by OFFSET, and the number of bytes read
>> +   is defined by LENGTH.  The data is then copied into the
>> +   caller-managed buffer BUF.
>> +
>> +   If the register is optimized out or unavailable for the given
>> +   FRAME, the OPTIMIZED and UNAVAILABLE outputs are set
>> +   accordingly  */
>> +
>> +static void
>> +read_from_register (struct frame_info *frame, int regnum,
>> +                 CORE_ADDR offset, gdb::array_view<gdb_byte> buf,
>> +                 int *optimized, int *unavailable)
>> +{
>> +  struct gdbarch *gdbarch = get_frame_arch (frame);
>> +  int regsize = register_size (gdbarch, regnum);
>> +  int numregs = gdbarch_num_cooked_regs (gdbarch);
>> +  int length = buf.size ();
>> +
>> +  /* If a register is wholly inside the OFFSET, skip it.  */
>> +  if (frame == NULL || !regsize
>> +      || offset + length > regsize || numregs < regnum)
> 
> The last line is missing one column of indent.

Thanks, I will fix it in the next iteration.

> 
> Can `frame` really be NULL here?  Given that where write_to_register is
> used, we have:
> 
>      struct frame_info *frame = frame_find_by_id (c->frame_id);
>      struct gdbarch *arch = get_frame_arch (frame);
> 
> If frame was NULL, it would segfault in get_frame_arch.

This is a good point. I will remove that in the next iteration.

> 
> Can regsize really be 0?

This was part of the previously used API so my guess is that it could :)

I didn't want to presume to many things if the previous code handled them.

> 
> I don't understand the code and how it relates to the comment.  What
> does it mean for a register to be inside an offset?  The expression
> `offset + length > regsize` checks that the end of the portion we want
> to read is beyond the end of the register.  But there could be a part of
> the portion we want to read that is within the register.  The code might
> be correct, but the comment needs to express the intention more clearly.

This is not allowed in DWARF. The whole length needs to be contained in 
a register. If only some parts of it are then the location needs to be a 
composite and that is not covered with this low level interface.

I should probably explicitly say that in the comment in the next iteration

> 
> Is `numregs < regnum` really useful?  When would you encounter that?
> 
> Simon
>

This check was part of the previous interface as well. Maybe the 
interface was design to be conservative or maybe there was an use case 
where compiler generated something that resulted in an invalid regnum?

Better safe then sorry I guess but, I can remove it if it doesn't make 
sense.

Zoran

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 01/43] Replace the symbol needs evaluator with a parser
  2021-04-28 10:17     ` Zoran Zaric
@ 2021-04-28 14:08       ` Simon Marchi
  2021-04-28 15:02         ` Zoran Zaric
  2021-04-28 15:31         ` Zoran Zaric
  0 siblings, 2 replies; 86+ messages in thread
From: Simon Marchi @ 2021-04-28 14:08 UTC (permalink / raw)
  To: Zoran Zaric, gdb-patches

On 2021-04-28 6:17 a.m., Zoran Zaric wrote:
> 
>>> Some concerns were raised that a linear scan of the expression byte
>>> stream would have issues if a DWARF producer would try to hide some
>>> non DWARF related data after a control flow operation. Although the
>>> testsuite doesn't show this case, it is a valid concern, so one of
>>> the later patches in this series will address it by switching back to
>>> the then redesigned DWARF expression evaluator.
>>
>> While re-reading your exchange with Tom, I was under the impression that
>> traversing the "control flow graph" of the expression, visiting each
>> node only once, would be a good solution.  It would avoid the infinite
>> loop problem, the "two branches" problem, and even the corner cases
>> where you have garbage in the middle of the expression, or if the
>> expression jumps in the middle of an instruction to re-use the operand
>> of an instruction as an instruction.
>>
>> Patch 39 changes this back to an evaluator, so I'm not sure if this is
>> what you implemented or something else, I'll see when I get there.
> 
> There is no concept of a "control flow graph" in the  existing gdb evaluator, there is just a byte stream that gdb either fully traverse and evaluates or it "fakes" the target access.

When I meant "control flow graph", I didn't mean to have a proper
control flow graph representation with classes and all (unless we think
it would be useful), but just navigate it conceptually.

You maintain a queue of "to visit" instructions and a set of "already
visited" instructions.  You by pushing the first instruction in the
queue and work until the queue is empty.  For all instructions but
conditional jumps, you push the instruction after it in the queue.  For
conditional jumps, you also push the jump target in the queue.  You
maintain the "already visited" set on the side to avoid visiting the
same instruction twice, in case there are loops.

> 
> The solution that Tom talked about was just an idea that he used in some other tool and would need to be implemented from scratch.
> 
> The patch 39 switches the evaluator design to throwing the missing context exception idea which re-enables using the same evaluator for both purposes.

I'll see and understand better when I get there.  But from what I
understood of past dicussions, using the evaluator may give wrong
results, because one branch may require only registers while another
branch may require a frame (that's the point of your patch).  So I don't
really understand how going back to using an evaluator would work.

Although that depends on the semantic we want symbol_needs to have:

 - find what's needed to evaluate the expression in the current context,
   only in the branch that would be taken right now?
 - find what's needed to evaluate the expression in any possible
   context, considering all branches?

Simon

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 03/43] Move frame context info to dwarf_expr_context
  2021-04-28 10:51     ` Zoran Zaric
@ 2021-04-28 14:14       ` Simon Marchi
  2021-04-28 15:55         ` Zoran Zaric
  0 siblings, 1 reply; 86+ messages in thread
From: Simon Marchi @ 2021-04-28 14:14 UTC (permalink / raw)
  To: Zoran Zaric, gdb-patches

>> On an unrelated topic, things like ensure_have_frame always worry me
>> because I'm scared I'll forget to call it where necessary.  An
>> alternative could be to make sure we get the frame through a getter that
>> throws if frame == nullptr.
>>
>> Basically, a tiny class (could be internal to dwarf_expr_context):
>>
>> struct frame_safe
>> {
>>    frame_safe (frame_info *frame)
>>      : m_frame (frame)
>>    {}
>>
>>    frame_info *frame (const char *op)
>>    {
>>      if (m_frame == nullptr)
>>        ... throw ...
>>
>>      return m_frame;
>>    }
>>
>> private:
>>    frame_info *m_frame;
>> };
>>
>> dwarf_expr_context would have a `frame_safe m_frame` field and a
>> `frame (const char *op)` getter that just does
>> `return m_frame->frame (op)`.
>>
>> This way, it's impossible to get the frame and forgetting an
>> ensure_have_frame call.
> 
> True, but then you end up with even more "switch" statements in different parts of the gdb that one needs to update every time they add a new operation and this is one of the problems with the current design as is.

I don't understand what you mean, why it would mean adding more switch
statements.  In practice, it would just mean changing `this->frame` to
`this->frame ()`.

> This would force us to add two new places (one for frame and one for compilation unit) and there will be more in the future when we add a concept of a thread to the context.
> 
> At least with the current implementation the implementer of the case statement that process that operation needs to know which part of the context has to be present for that operation, which is clearly defined in the DWARF standard as part of that operations definition (or at least it will be after our extensions).
> 
> Another problem with the new class approach is that it makes the information about what each operation needs convoluted and hidden which in my mind adds even bigger chance of someone forgetting to add their operation to the query calls.

What do you mean by "add their operation to the query calls"?

I don't think see how it helps to make it explicit which operation needs
which part of the context.  If an operation needs a frame, it will call
`this->frame ()`  which will throw if the context does not have a
frame.

Simon

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 15/43] Make DWARF evaluator return a single struct value
  2021-04-28 11:47     ` Zoran Zaric
@ 2021-04-28 14:24       ` Simon Marchi
  0 siblings, 0 replies; 86+ messages in thread
From: Simon Marchi @ 2021-04-28 14:24 UTC (permalink / raw)
  To: Zoran Zaric, gdb-patches

> I've just copied this whole block from the loc.c. Everything is replaced with a new implementation in the later patch.
> 
> Do you still feel that I need to clean it up here or it doesn't really matter considering the later patches?

Up to you.  Keep in mind that I give comments that make sense by looking
at the individual patches, without much context of what's coming after.
For a change of this size, it would be too difficult for me to make
comments considering the patchset as a whole, it just wouldn't fit in my
head.  So if some code gets removed down the road and doing the change
isn't really useful, it's ok to say that and ignore the comment.

Simon

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 16/43] Simplify dwarf_expr_context class interface
  2021-04-28 13:15     ` Zoran Zaric
@ 2021-04-28 14:41       ` Simon Marchi
  2021-04-28 15:39         ` Zoran Zaric
  2021-04-29 15:49       ` Simon Marchi
  1 sibling, 1 reply; 86+ messages in thread
From: Simon Marchi @ 2021-04-28 14:41 UTC (permalink / raw)
  To: Zoran Zaric, gdb-patches

>>> diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
>>> index a0ac21f2ed1..d1374068732 100644
>>> --- a/gdb/dwarf2/expr.h
>>> +++ b/gdb/dwarf2/expr.h
>>> @@ -119,19 +119,30 @@ struct dwarf_stack_value
>>>      its current state and its callbacks.  */
>>>   struct dwarf_expr_context
>>>   {
>>> -  dwarf_expr_context (dwarf2_per_objfile *per_objfile);
>>> +  /* We should ever only pass in the PER_OBJFILE, while the ADDR_SIZE
>>> +     information should be retrievable from there.  The PER_OBJFILE
>>> +     contains a pointer to the PER_BFD information anyway and the
>>> +     address size information must be the same for the whole BFD.  */
>>> +  dwarf_expr_context (struct dwarf2_per_objfile *per_objfile,
>>> +                   int addr_size);
>>
>> I don't really understand the comment.  What do you mean by "We should
>> ever only pass..."?  In general, I don't find that the comment helps me
>> understand what the parameters are and do.  Try to change it to be more
>> concrete and straight to the point.
> 
> This was more me venting about that particular design choice

Haha, it indeed did feel this way.

> because the reality is that an address size has to be consistent with what is defined in the original object file but there is no interface to always get it in a clean way in gdb.

In this case, it looks like it's because an expression can come from a
DIE attribute, directly linked to a CU.  So the address size comes from
that CU's header.  And at other times, the expression comes from a frame
description entry (FDE) / common information entry (CIE), so the address
size comes from that CIE's header.  So I don't see any other easy way than
making the caller provide it.

> It makes sense for me to remove that comment altogether in the next iteration and thing about a future change that would clean it up.

Ok.  Or, just make a simple comment.  You could say that ADDRESS_SIZE is
the address size coming from the CU's header or the CIE's header,
depending on the context.

Anyway, this is all perhaps subject to change if I understand correctly.
In the end do you still expose a class, or just one function, to
evaluate a DWARF expression?

Simon

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 18/43] Add new register access interface to expr.c
  2021-04-28 13:29     ` Zoran Zaric
@ 2021-04-28 14:48       ` Simon Marchi
  2021-04-28 15:42         ` Zoran Zaric
  0 siblings, 1 reply; 86+ messages in thread
From: Simon Marchi @ 2021-04-28 14:48 UTC (permalink / raw)
  To: Zoran Zaric, gdb-patches

>> Can regsize really be 0?
> 
> This was part of the previously used API so my guess is that it could :)
> 
> I didn't want to presume to many things if the previous code handled them.

Ok, it's fine to keep it then, to avoid breaking some potential existing
use case (we could try to remove it, but as a separate work).

I ask these things because if things are not supposed to happen, it's
better that we gdb_assert them, rather than handle them like this.
Otherwise, when somebody comes around to read that code later, they will
think that regsize == 0 (or frame == NULL in the other case) is a
possibility, so that makes their mental model unnecessarily more
complex.  It can also cover some bugs which would be exposed by a failed
assertion.

>> I don't understand the code and how it relates to the comment.  What
>> does it mean for a register to be inside an offset?  The expression
>> `offset + length > regsize` checks that the end of the portion we want
>> to read is beyond the end of the register.  But there could be a part of
>> the portion we want to read that is within the register.  The code might
>> be correct, but the comment needs to express the intention more clearly.
> 
> This is not allowed in DWARF. The whole length needs to be contained in a register. If only some parts of it are then the location needs to be a composite and that is not covered with this low level interface.
> 
> I should probably explicitly say that in the comment in the next iteration

Ok, the existing comment makes it sound like "if what we want to read is
_wholly_ outside of the register, we can return early".  So just an
optimization things.  So yeah I think the comments needs to be clearer.

>> Is `numregs < regnum` really useful?  When would you encounter that?
>>
>> Simon
>>
> 
> This check was part of the previous interface as well. Maybe the interface was design to be conservative or maybe there was an use case where compiler generated something that resulted in an invalid regnum?
> 
> Better safe then sorry I guess but, I can remove it if it doesn't make sense.

If it was already there (I didn't find it though), then it's ok to keep
it.

Simon


^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 01/43] Replace the symbol needs evaluator with a parser
  2021-04-28 14:08       ` Simon Marchi
@ 2021-04-28 15:02         ` Zoran Zaric
  2021-04-28 15:31         ` Zoran Zaric
  1 sibling, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-04-28 15:02 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches


>>>> Some concerns were raised that a linear scan of the expression byte
>>>> stream would have issues if a DWARF producer would try to hide some
>>>> non DWARF related data after a control flow operation. Although the
>>>> testsuite doesn't show this case, it is a valid concern, so one of
>>>> the later patches in this series will address it by switching back to
>>>> the then redesigned DWARF expression evaluator.
>>>
>>> While re-reading your exchange with Tom, I was under the impression that
>>> traversing the "control flow graph" of the expression, visiting each
>>> node only once, would be a good solution.  It would avoid the infinite
>>> loop problem, the "two branches" problem, and even the corner cases
>>> where you have garbage in the middle of the expression, or if the
>>> expression jumps in the middle of an instruction to re-use the operand
>>> of an instruction as an instruction.
>>>
>>> Patch 39 changes this back to an evaluator, so I'm not sure if this is
>>> what you implemented or something else, I'll see when I get there.
>>
>> There is no concept of a "control flow graph" in the  existing gdb evaluator, there is just a byte stream that gdb either fully traverse and evaluates or it "fakes" the target access.
> 
> When I meant "control flow graph", I didn't mean to have a proper
> control flow graph representation with classes and all (unless we think
> it would be useful), but just navigate it conceptually.
> 
> You maintain a queue of "to visit" instructions and a set of "already
> visited" instructions.  You by pushing the first instruction in the
> queue and work until the queue is empty.  For all instructions but
> conditional jumps, you push the instruction after it in the queue.  For
> conditional jumps, you also push the jump target in the queue.  You
> maintain the "already visited" set on the side to avoid visiting the
> same instruction twice, in case there are loops.

Right, this would probably work, but the only problem it is solving is 
the garbage issue, and considering that the later patch in the series 
removes this temporary solution anyway. I don't think it is worth the 
time in re-implementing it.

> 
>>
>> The solution that Tom talked about was just an idea that he used in some other tool and would need to be implemented from scratch.
>>
>> The patch 39 switches the evaluator design to throwing the missing context exception idea which re-enables using the same evaluator for both purposes.
> 
> I'll see and understand better when I get there.  But from what I
> understood of past dicussions, using the evaluator may give wrong
> results, because one branch may require only registers while another
> branch may require a frame (that's the point of your patch).  So I don't
> really understand how going back to using an evaluator would work.

You can't really have a register without a frame. That doesn't really 
make sense in any of the existing targets. Even the struct value 
describing the register location always needs to have a frame defined.

The question that symbol needs is answering is if the symbol needs a 
frame or not. And this is why catching an exception thrown by the 
evaluator when the frame was needed but missing does work.

> 
> Although that depends on the semantic we want symbol_needs to have:
> 
>   - find what's needed to evaluate the expression in the current context,
>     only in the branch that would be taken right now?
>   - find what's needed to evaluate the expression in any possible
>     context, considering all branches?
> 
> Simon
> 

Symbol needs is always called regarding the current context, I didn't 
see anything that proves otherwise.

Zoran

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 01/43] Replace the symbol needs evaluator with a parser
  2021-04-28 14:08       ` Simon Marchi
  2021-04-28 15:02         ` Zoran Zaric
@ 2021-04-28 15:31         ` Zoran Zaric
  1 sibling, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-04-28 15:31 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches


> Although that depends on the semantic we want symbol_needs to have:
> 
>   - find what's needed to evaluate the expression in the current context,
>     only in the branch that would be taken right now?
>   - find what's needed to evaluate the expression in any possible
>     context, considering all branches?
> 
> Simon
> 

After thinking a bit more about it and speaking with Simon offline, I 
realized that there is a bit more complexity here and I am not sure what 
is the right answer to this question.

In the first case, if we decide to split the patchset in two, where 
first part would be the general cleanup and the second would be the 
evaluator redesign, it would make sense to go with the control flow idea 
so that we have a more robust implementation at the end of first part.

For the second case, the control flow idea would be the only way to go, 
but that would mean that it never goes back to the evaluator mechanism.

I am not sure how this mechanism is expected to work and considering 
that the previous implementation didn't cover any of these two cases 
properly, I am not sure what to do here.

The control flow implementation sounds easy to implement, but is that 
what is expected? It also forces one more big switch statement that 
parses the expression to be present.

Zoran

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 16/43] Simplify dwarf_expr_context class interface
  2021-04-28 14:41       ` Simon Marchi
@ 2021-04-28 15:39         ` Zoran Zaric
  2021-04-28 19:19           ` Simon Marchi
  0 siblings, 1 reply; 86+ messages in thread
From: Zoran Zaric @ 2021-04-28 15:39 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches


>>
>> This was more me venting about that particular design choice
> 
> Haha, it indeed did feel this way.
> 
>> because the reality is that an address size has to be consistent with what is defined in the original object file but there is no interface to always get it in a clean way in gdb.
> 
> In this case, it looks like it's because an expression can come from a
> DIE attribute, directly linked to a CU.  So the address size comes from
> that CU's header.  And at other times, the expression comes from a frame
> description entry (FDE) / common information entry (CIE), so the address
> size comes from that CIE's header.  So I don't see any other easy way than
> making the caller provide it.
> 
>> It makes sense for me to remove that comment altogether in the next iteration and thing about a future change that would clean it up.
> 
> Ok.  Or, just make a simple comment.  You could say that ADDRESS_SIZE is
> the address size coming from the CU's header or the CIE's header,
> depending on the context.

Right, but all these sources come from a single object file and by the 
standard they all need to be consistent, otherwise things couldn't 
really work.

> 
> Anyway, this is all perhaps subject to change if I understand correctly.
> In the end do you still expose a class, or just one function, to
> evaluate a DWARF expression?
> 
> Simon
> 

Right, and ideally, because the per_objfile always has to be present, it 
would make sense that it also contains the address size information that 
should get it from any header that is present in that objfile and if 
there are more then one, they should all be consistent.

Does this makes sense?

Zoran

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 18/43] Add new register access interface to expr.c
  2021-04-28 14:48       ` Simon Marchi
@ 2021-04-28 15:42         ` Zoran Zaric
  0 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-04-28 15:42 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches



On 4/28/21 3:48 PM, Simon Marchi wrote:
> [CAUTION: External Email]
> 
>>> Can regsize really be 0?
>>
>> This was part of the previously used API so my guess is that it could :)
>>
>> I didn't want to presume to many things if the previous code handled them.
> 
> Ok, it's fine to keep it then, to avoid breaking some potential existing
> use case (we could try to remove it, but as a separate work).
> 
> I ask these things because if things are not supposed to happen, it's
> better that we gdb_assert them, rather than handle them like this.
> Otherwise, when somebody comes around to read that code later, they will
> think that regsize == 0 (or frame == NULL in the other case) is a
> possibility, so that makes their mental model unnecessarily more
> complex.  It can also cover some bugs which would be exposed by a failed
> assertion.

Agreed. I can go back to this and re-test it as part of a separate patch.

> 
>>> I don't understand the code and how it relates to the comment.  What
>>> does it mean for a register to be inside an offset?  The expression
>>> `offset + length > regsize` checks that the end of the portion we want
>>> to read is beyond the end of the register.  But there could be a part of
>>> the portion we want to read that is within the register.  The code might
>>> be correct, but the comment needs to express the intention more clearly.
>>
>> This is not allowed in DWARF. The whole length needs to be contained in a register. If only some parts of it are then the location needs to be a composite and that is not covered with this low level interface.
>>
>> I should probably explicitly say that in the comment in the next iteration
> 
> Ok, the existing comment makes it sound like "if what we want to read is
> _wholly_ outside of the register, we can return early".  So just an
> optimization things.  So yeah I think the comments needs to be clearer.

I will change it in the next iteration.

> 
>>> Is `numregs < regnum` really useful?  When would you encounter that?
>>>
>>> Simon
>>>
>>
>> This check was part of the previous interface as well. Maybe the interface was design to be conservative or maybe there was an use case where compiler generated something that resulted in an invalid regnum?
>>
>> Better safe then sorry I guess but, I can remove it if it doesn't make sense.
> 
> If it was already there (I didn't find it though), then it's ok to keep
> it.
> 
> Simon
> 

Let me reconfirm this to be safe.

Zoran

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 03/43] Move frame context info to dwarf_expr_context
  2021-04-28 14:14       ` Simon Marchi
@ 2021-04-28 15:55         ` Zoran Zaric
  0 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-04-28 15:55 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches


>>> dwarf_expr_context would have a `frame_safe m_frame` field and a
>>> `frame (const char *op)` getter that just does
>>> `return m_frame->frame (op)`.
>>>
>>> This way, it's impossible to get the frame and forgetting an
>>> ensure_have_frame call.
>>
>> True, but then you end up with even more "switch" statements in different parts of the gdb that one needs to update every time they add a new operation and this is one of the problems with the current design as is.
> 
> I don't understand what you mean, why it would mean adding more switch
> statements.  In practice, it would just mean changing `this->frame` to
> `this->frame ()`.
> 
>> This would force us to add two new places (one for frame and one for compilation unit) and there will be more in the future when we add a concept of a thread to the context.
>>
>> At least with the current implementation the implementer of the case statement that process that operation needs to know which part of the context has to be present for that operation, which is clearly defined in the DWARF standard as part of that operations definition (or at least it will be after our extensions).
>>
>> Another problem with the new class approach is that it makes the information about what each operation needs convoluted and hidden which in my mind adds even bigger chance of someone forgetting to add their operation to the query calls.
> 
> What do you mean by "add their operation to the query calls"?
> 
> I don't think see how it helps to make it explicit which operation needs
> which part of the context.  If an operation needs a frame, it will call
> `this->frame ()`  which will throw if the context does not have a
> frame.
> 
> Simon
> 

I looked even deeper in this issue and the problem is that both frame 
and compilation unit information are valid to be nullptr in some cases, 
but for certain operations they need to be present and if they are not 
the evaluator needs to bail out.

There seems to be a couple of reason why they can be nullptr:

- some preexisting API will call the get_selected_frame (_("No frame 
selected.")) if the frame is nullptr.

- evaluator can call another evaluator recursively that might also not 
need the frame or compilation unit information or may bailout it they 
are needed but not present.

- the composite and implicit pointer location description carry the 
context information outside the evaluator which might never be used but 
if they are the exception will be thrown.

There might be some other complications, but these seem to be the main ones.

Zoran

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 16/43] Simplify dwarf_expr_context class interface
  2021-04-28 15:39         ` Zoran Zaric
@ 2021-04-28 19:19           ` Simon Marchi
  0 siblings, 0 replies; 86+ messages in thread
From: Simon Marchi @ 2021-04-28 19:19 UTC (permalink / raw)
  To: Zoran Zaric, gdb-patches

On 2021-04-28 11:39 a.m., Zoran Zaric wrote:> 
>>>
>>> This was more me venting about that particular design choice
>>
>> Haha, it indeed did feel this way.
>>
>>> because the reality is that an address size has to be consistent with what is defined in the original object file but there is no interface to always get it in a clean way in gdb.
>>
>> In this case, it looks like it's because an expression can come from a
>> DIE attribute, directly linked to a CU.  So the address size comes from
>> that CU's header.  And at other times, the expression comes from a frame
>> description entry (FDE) / common information entry (CIE), so the address
>> size comes from that CIE's header.  So I don't see any other easy way than
>> making the caller provide it.
>>
>>> It makes sense for me to remove that comment altogether in the next iteration and thing about a future change that would clean it up.
>>
>> Ok.  Or, just make a simple comment.  You could say that ADDRESS_SIZE is
>> the address size coming from the CU's header or the CIE's header,
>> depending on the context.
> 
> Right, but all these sources come from a single object file and by the standard they all need to be consistent, otherwise things couldn't really work.
> 
>>
>> Anyway, this is all perhaps subject to change if I understand correctly.
>> In the end do you still expose a class, or just one function, to
>> evaluate a DWARF expression?
>>
>> Simon
>>
> 
> Right, and ideally, because the per_objfile always has to be present, it would make sense that it also contains the address size information that should get it from any header that is present in that objfile and if there are more then one, they should all be consistent.
> 
> Does this makes sense?

Yeah, I suppose.  In DWARF5, it says that about the address_size field
of the CIE header:

    If a compilation unit exists for this frame, its address size must
    match the address size here.

In any case, It's not a big problem for now I think to pass in the
address size.

Simon

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 16/43] Simplify dwarf_expr_context class interface
  2021-04-28 13:15     ` Zoran Zaric
  2021-04-28 14:41       ` Simon Marchi
@ 2021-04-29 15:49       ` Simon Marchi
  2021-04-29 15:55         ` Zoran Zaric
  1 sibling, 1 reply; 86+ messages in thread
From: Simon Marchi @ 2021-04-29 15:49 UTC (permalink / raw)
  To: Zoran Zaric, gdb-patches



On 2021-04-28 9:15 a.m., Zoran Zaric wrote:
> 
>>>   /* Create a new context for the expression evaluator.  */
>>>
>>> -dwarf_expr_context::dwarf_expr_context (dwarf2_per_objfile *per_objfile)
>>> -: per_objfile (per_objfile)
>>> +dwarf_expr_context::dwarf_expr_context (dwarf2_per_objfile *per_objfile,
>>> +                                     int addr_size)
>>> +: gdbarch (per_objfile->objfile->arch ()),
>>
>> If gdbarch is always obtained from the objfile, we could maybe avoid
>> storing it.
> 
> Sure, but it is used so many times (often as a few times per operation) so we currently cut off two indirection per each use.
> 
> I think having one pointer as a class member that cuts through all that is worth it.
But there's no need to store it in a member of the class AFAIK, it
could be fetched once at the top of the "evaluate" method.

Simon

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 16/43] Simplify dwarf_expr_context class interface
  2021-04-29 15:49       ` Simon Marchi
@ 2021-04-29 15:55         ` Zoran Zaric
  0 siblings, 0 replies; 86+ messages in thread
From: Zoran Zaric @ 2021-04-29 15:55 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches



On 4/29/21 4:49 PM, Simon Marchi wrote:
> [CAUTION: External Email]
> 
> On 2021-04-28 9:15 a.m., Zoran Zaric wrote:
>>
>>>>    /* Create a new context for the expression evaluator.  */
>>>>
>>>> -dwarf_expr_context::dwarf_expr_context (dwarf2_per_objfile *per_objfile)
>>>> -: per_objfile (per_objfile)
>>>> +dwarf_expr_context::dwarf_expr_context (dwarf2_per_objfile *per_objfile,
>>>> +                                     int addr_size)
>>>> +: gdbarch (per_objfile->objfile->arch ()),
>>>
>>> If gdbarch is always obtained from the objfile, we could maybe avoid
>>> storing it.
>>
>> Sure, but it is used so many times (often as a few times per operation) so we currently cut off two indirection per each use.
>>
>> I think having one pointer as a class member that cuts through all that is worth it.
> But there's no need to store it in a member of the class AFAIK, it
> could be fetched once at the top of the "evaluate" method.
> 
> Simon
> 

Well it is used by other methods as well, but maybe those are not called 
too many times so I could grab it on top of each one.

I don't mind doing that way really.

Zoran

^ permalink raw reply	[flat|nested] 86+ messages in thread

* Re: [PATCH 19/43] Add new memory access interface to expr.c
  2021-03-01 14:45 ` [PATCH 19/43] Add new memory " Zoran Zaric
@ 2021-04-30 21:24   ` Simon Marchi
  0 siblings, 0 replies; 86+ messages in thread
From: Simon Marchi @ 2021-04-30 21:24 UTC (permalink / raw)
  To: Zoran Zaric, gdb-patches

On 2021-03-01 9:45 a.m., Zoran Zaric via Gdb-patches wrote:
> DWARF expression evaluator is currently using a few different
> interfaces for memory access: write_memory_with_notification,
> read_value_memory, read_memory.
> 
> They all seem incosistent, while some of them even need a struct
> value typed argument to be present.
> 
> This patch is simplifying that interface by replacing it with two new
> low level functions: read_from_memory and write_to_memory.
> 
> The advantage of this new interface is that it behaves in the same way
> as the register access interface from the previous patch. Both of these
> have the same error returning policy, which will be usefull for the
> following patches.
> 
> 	* dwarf2/expr.c (xfer_memory):  New function.
> 	(read_from_memory): New function.
> 	(write_to_memory): New function.
> 	(rw_pieced_value): Now calls the read_from_memory and
> 	write_to_memory functions.
> ---
>  gdb/dwarf2/expr.c | 187 +++++++++++++++++++++++++++++++++++-----------
>  1 file changed, 144 insertions(+), 43 deletions(-)
> 
> diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
> index 5a1fd5b941f..1abece7d173 100644
> --- a/gdb/dwarf2/expr.c
> +++ b/gdb/dwarf2/expr.c
> @@ -32,6 +32,8 @@
>  #include "frame.h"
>  #include "gdbsupport/underlying.h"
>  #include "gdbarch.h"
> +#include "inferior.h"
> +#include "observable.h"
>  
>  /* Cookie for gdbarch data.  */
>  
> @@ -196,6 +198,85 @@ write_to_register (struct frame_info *frame, int regnum,
>    return;
>  }
>  
> +/* Helper for read_from_memory and write_to_memory.  */
> +
> +static void
> +xfer_memory (CORE_ADDR address, gdb_byte *readbuf,
> +	     const gdb_byte *writebuf,
> +	     size_t length, bool stack, int *unavailable)

unavailable should probably be bool (end-to-end).

> +{
> +  *unavailable = 0;
> +
> +  enum target_object object
> +    = stack ? TARGET_OBJECT_STACK_MEMORY : TARGET_OBJECT_MEMORY;
> +
> +  ULONGEST xfered_total = 0;
> +
> +  while (xfered_total < length)
> +    {
> +      ULONGEST xfered_partial;
> +
> +      enum target_xfer_status status
> +	= target_xfer_partial (current_top_target (), object, NULL,
> +			       (readbuf != nullptr
> +				? readbuf + xfered_total
> +				: nullptr),
> +			       (writebuf != nullptr
> +				? writebuf + xfered_total
> +				: nullptr),
> +			       address + xfered_total, length - xfered_total,
> +			       &xfered_partial);
> +
> +      if (status == TARGET_XFER_OK)
> +	{
> +	  xfered_total += xfered_partial;
> +	  QUIT;
> +	}
> +      else if (status == TARGET_XFER_UNAVAILABLE)
> +	{
> +	  *unavailable = 1;
> +	  return;
> +	}
> +      else if (status == TARGET_XFER_EOF)
> +	memory_error (TARGET_XFER_E_IO, address + xfered_total);
> +      else
> +	memory_error (status, address + xfered_total);
> +    }
> +}
> +
> +/* Read LENGTH bytes of memory contents starting at ADDRESS.
> +
> +   The data read is copied to a caller-managed buffer BUF.  STACK

BUF -> BUFFER.

> +   indicates whether the memory range specified belongs to a stack
> +   memory region.
> +
> +   If the memory is unavailable, the UNAVAILABLE output is set.  */
> +
> +static void
> +read_from_memory (CORE_ADDR address, gdb_byte *buffer,
> +		  size_t length, bool stack, int *unavailable)
> +{
> +  xfer_memory (address, buffer, nullptr, length, stack, unavailable);
> +}
> +
> +/* Write LENGTH bytes of memory contents starting at ADDRESS.
> +
> +   The data written is copied from a caller-managed buffer buf.  STACK

buf -> BUFFER

> +   indicates whether the memory range specified belongs to a stack
> +   memory region.
> +
> +   If the memory is unavailable, the UNAVAILABLE output is set.  */
> +
> +static void
> +write_to_memory (CORE_ADDR address, const gdb_byte *buffer,
> +		 size_t length, bool stack, int *unavailable)
> +{
> +  xfer_memory (address, nullptr, buffer, length, stack, unavailable);
> +
> +  gdb::observers::memory_changed.notify (current_inferior (), address,
> +					 length, buffer);
> +}
> +
>  struct piece_closure
>  {
>    /* Reference count.  */
> @@ -381,66 +462,86 @@ rw_pieced_value (struct value *v, struct value *from)
>  	    bits_to_skip += p->offset;
>  
>  	    CORE_ADDR start_addr = p->v.mem.addr + bits_to_skip / 8;
> +	    bool in_stack_memory = p->v.mem.in_stack_memory;
> +	    int unavail = 0;
>  
>  	    if (bits_to_skip % 8 == 0 && this_size_bits % 8 == 0
>  		&& offset % 8 == 0)
>  	      {
>  		/* Everything is byte-aligned; no buffer needed.  */
>  		if (from != NULL)
> -		  write_memory_with_notification (start_addr,
> -						  (from_contents
> -						   + offset / 8),
> -						  this_size_bits / 8);
> +		  write_to_memory (start_addr, (from_contents + offset / 8),
> +				   this_size_bits / 8, in_stack_memory,
> +				   &unavail);
>  		else
> -		  read_value_memory (v, offset,
> -				     p->v.mem.in_stack_memory,
> -				     p->v.mem.addr + bits_to_skip / 8,
> -				     v_contents + offset / 8,
> -				     this_size_bits / 8);
> -		break;
> -	      }
> -
> -	    this_size = bits_to_bytes (bits_to_skip, this_size_bits);
> -	    buffer.resize (this_size);
> -
> -	    if (from == NULL)
> -	      {
> -		/* Read mode.  */
> -		read_value_memory (v, offset,
> -				   p->v.mem.in_stack_memory,
> -				   p->v.mem.addr + bits_to_skip / 8,
> -				   buffer.data (), this_size);
> -		copy_bitwise (v_contents, offset,
> -			      buffer.data (), bits_to_skip % 8,
> -			      this_size_bits, bits_big_endian);
> +		  read_from_memory (start_addr, (v_contents + offset / 8),
> +				    this_size_bits / 8, in_stack_memory,
> +				    &unavail);
>  	      }
>  	    else
>  	      {
> -		/* Write mode.  */
> -		if (bits_to_skip % 8 != 0 || this_size_bits % 8 != 0)
> +		this_size = bits_to_bytes (bits_to_skip, this_size_bits);
> +		buffer.resize (this_size);
> +
> +		if (from == NULL)
>  		  {
> -		    if (this_size <= 8)
> +		    /* Read mode.  */
> +		    read_from_memory (start_addr, buffer.data (),
> +				      this_size, in_stack_memory,
> +				      &unavail);
> +		    if (!unavail)
> +		      copy_bitwise (v_contents, offset,
> +				    buffer.data (), bits_to_skip % 8,
> +				    this_size_bits, bits_big_endian);
> +		  }
> +		else
> +		  {
> +		    /* Write mode.  */
> +		    if (bits_to_skip % 8 != 0 || this_size_bits % 8 != 0)
>  		      {
> -			/* Perform a single read for small sizes.  */
> -			read_memory (start_addr, buffer.data (),
> -				     this_size);
> +			if (this_size <= 8)
> +			  {
> +			    /* Perform a single read for small sizes.  */
> +			    read_from_memory (start_addr, buffer.data (),
> +					      this_size, in_stack_memory,
> +					      &unavail);
> +			  }
> +			else
> +			  {
> +			    /* Only the first and last bytes can possibly have
> +			       any bits reused.  */
> +			    read_from_memory (start_addr, buffer.data (),
> +					      1, in_stack_memory,
> +					      &unavail);
> +			    if (!unavail)
> +			      read_from_memory (start_addr + this_size - 1,
> +						&buffer[this_size - 1], 1,
> +						in_stack_memory, &unavail);
> +			  }
>  		      }
> -		    else
> +
> +		    if (!unavail)
>  		      {
> -			/* Only the first and last bytes can possibly have
> -			   any bits reused.  */
> -			read_memory (start_addr, buffer.data (), 1);
> -			read_memory (start_addr + this_size - 1,
> -				     &buffer[this_size - 1], 1);
> +			copy_bitwise (buffer.data (), bits_to_skip % 8,
> +				      from_contents, offset,
> +				      this_size_bits, bits_big_endian);
> +			write_to_memory (start_addr, buffer.data (),
> +					 this_size, in_stack_memory,
> +					 &unavail);
>  		      }
>  		  }
> +	      }
>  
> -		copy_bitwise (buffer.data (), bits_to_skip % 8,
> -			      from_contents, offset,
> -			      this_size_bits, bits_big_endian);
> -		write_memory_with_notification (start_addr,
> -						buffer.data (),
> -						this_size);
> +	    if (unavail)
> +	      {
> +		if (from == NULL)
> +		  mark_value_bits_unavailable (v, (offset + bits_to_skip % 8),
> +					       this_size_bits);
> +		else
> +		  throw_error (NOT_AVAILABLE_ERROR,
> +			       _("Can't do read-modify-write to "
> +				 "update bitfield; containing word "
> +				 "is unavailable"));

Did you get inspiration from existing code for this unavailable byte
error handling?  Were you able to test it?

Simon

^ permalink raw reply	[flat|nested] 86+ messages in thread

end of thread, other threads:[~2021-04-30 21:24 UTC | newest]

Thread overview: 86+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-01 14:45 [PATCH 00/43 V2] Allow location description on the DWARF stack Zoran Zaric
2021-03-01 14:45 ` [PATCH 01/43] Replace the symbol needs evaluator with a parser Zoran Zaric
2021-04-27  1:20   ` Simon Marchi
2021-04-28 10:17     ` Zoran Zaric
2021-04-28 14:08       ` Simon Marchi
2021-04-28 15:02         ` Zoran Zaric
2021-04-28 15:31         ` Zoran Zaric
2021-03-01 14:45 ` [PATCH 02/43] Cleanup of the dwarf_expr_context constructor Zoran Zaric
2021-04-27  1:23   ` Simon Marchi
2021-04-28 10:19     ` Zoran Zaric
2021-03-01 14:45 ` [PATCH 03/43] Move frame context info to dwarf_expr_context Zoran Zaric
2021-04-27  2:19   ` Simon Marchi
2021-04-28 10:51     ` Zoran Zaric
2021-04-28 14:14       ` Simon Marchi
2021-04-28 15:55         ` Zoran Zaric
2021-03-01 14:45 ` [PATCH 04/43] Remove get_frame_cfa from dwarf_expr_context Zoran Zaric
2021-03-01 14:45 ` [PATCH 05/43] Move compilation unit info to dwarf_expr_context Zoran Zaric
2021-04-27  2:58   ` Simon Marchi
2021-04-28 11:28     ` Zoran Zaric
2021-03-01 14:45 ` [PATCH 06/43] Move dwarf_call " Zoran Zaric
2021-03-01 14:45 ` [PATCH 07/43] Move get_object_address " Zoran Zaric
2021-04-27  3:12   ` Simon Marchi
2021-04-28 11:34     ` Zoran Zaric
2021-03-01 14:45 ` [PATCH 08/43] Move read_mem " Zoran Zaric
2021-03-01 14:45 ` [PATCH 09/43] Move push_dwarf_reg_entry_value to expr.c Zoran Zaric
2021-04-27  3:56   ` Simon Marchi
2021-04-28 11:36     ` Zoran Zaric
2021-03-01 14:45 ` [PATCH 10/43] Inline get_reg_value method of dwarf_expr_context Zoran Zaric
2021-03-01 14:45 ` [PATCH 11/43] Remove empty frame and full evaluators Zoran Zaric
2021-03-01 14:45 ` [PATCH 12/43] Merge evaluate_for_locexpr_baton evaluator Zoran Zaric
2021-04-28  1:33   ` Simon Marchi
2021-04-28 11:39     ` Zoran Zaric
2021-03-01 14:45 ` [PATCH 13/43] Move piece_closure and its support to expr.c Zoran Zaric
2021-04-28  1:56   ` Simon Marchi
2021-04-28 11:40     ` Zoran Zaric
2021-03-01 14:45 ` [PATCH 14/43] Make value_copy also copy the stack data member Zoran Zaric
2021-04-28  2:01   ` Simon Marchi
2021-04-28 11:43     ` Zoran Zaric
2021-03-01 14:45 ` [PATCH 15/43] Make DWARF evaluator return a single struct value Zoran Zaric
2021-04-28  2:21   ` Simon Marchi
2021-04-28 11:47     ` Zoran Zaric
2021-04-28 14:24       ` Simon Marchi
2021-03-01 14:45 ` [PATCH 16/43] Simplify dwarf_expr_context class interface Zoran Zaric
2021-04-28  2:45   ` Simon Marchi
2021-04-28 13:15     ` Zoran Zaric
2021-04-28 14:41       ` Simon Marchi
2021-04-28 15:39         ` Zoran Zaric
2021-04-28 19:19           ` Simon Marchi
2021-04-29 15:49       ` Simon Marchi
2021-04-29 15:55         ` Zoran Zaric
2021-03-01 14:45 ` [PATCH 17/43] Add as_lval argument to expression evaluator Zoran Zaric
2021-04-28  3:04   ` Simon Marchi
2021-04-28 13:16     ` Zoran Zaric
2021-04-28  3:30   ` Simon Marchi
2021-03-01 14:45 ` [PATCH 18/43] Add new register access interface to expr.c Zoran Zaric
2021-03-08 23:52   ` Lancelot SIX
2021-04-28  3:25   ` Simon Marchi
2021-04-28 13:29     ` Zoran Zaric
2021-04-28 14:48       ` Simon Marchi
2021-04-28 15:42         ` Zoran Zaric
2021-03-01 14:45 ` [PATCH 19/43] Add new memory " Zoran Zaric
2021-04-30 21:24   ` Simon Marchi
2021-03-01 14:45 ` [PATCH 20/43] Add new classes that model DWARF stack element Zoran Zaric
2021-03-01 14:45 ` [PATCH 21/43] Add to_location method to DWARF entry classes Zoran Zaric
2021-03-01 14:45 ` [PATCH 22/43] Add to_value " Zoran Zaric
2021-03-01 14:46 ` [PATCH 23/43] Add read method to location description classes Zoran Zaric
2021-03-01 14:46 ` [PATCH 24/43] Add write " Zoran Zaric
2021-03-01 14:46 ` [PATCH 25/43] Add deref " Zoran Zaric
2021-03-01 14:46 ` [PATCH 26/43] Add read_from_gdb_value method to dwarf_location Zoran Zaric
2021-03-01 14:46 ` [PATCH 27/43] Add write_to_gdb_value " Zoran Zaric
2021-03-01 14:46 ` [PATCH 28/43] Add is_implicit_ptr_at " Zoran Zaric
2021-03-01 14:46 ` [PATCH 29/43] Add indirect_implicit_ptr to dwarf_location class Zoran Zaric
2021-03-01 14:46 ` [PATCH 30/43] Add new computed struct value callback interface Zoran Zaric
2021-03-01 14:46 ` [PATCH 31/43] Add to_gdb_value method to DWARF entry class Zoran Zaric
2021-03-01 14:46 ` [PATCH 32/43] Change DWARF stack to use new dwarf_entry classes Zoran Zaric
2021-03-01 14:46 ` [PATCH 33/43] Remove old computed struct value callbacks Zoran Zaric
2021-03-01 14:46 ` [PATCH 34/43] Comments cleanup between expr.h and expr.c Zoran Zaric
2021-03-01 14:46 ` [PATCH 35/43] Remove dwarf_expr_context from expr.h interface Zoran Zaric
2021-03-01 14:46 ` [PATCH 36/43] Move read_addr_from_reg function to frame.c Zoran Zaric
2021-03-01 14:46 ` [PATCH 37/43] Add frame info check to DW_OP_reg operations Zoran Zaric
2021-03-01 14:46 ` [PATCH 38/43] Remove DWARF expression composition check Zoran Zaric
2021-03-01 14:46 ` [PATCH 39/43] Change back the symbol needs to use the evaluator Zoran Zaric
2021-03-01 14:46 ` [PATCH 40/43] Add support for any location description in CFI Zoran Zaric
2021-03-01 14:46 ` [PATCH 41/43] Add DWARF operations for byte and bit offset Zoran Zaric
2021-03-01 14:46 ` [PATCH 42/43] Add support for DW_OP_LLVM_undefined operation Zoran Zaric
2021-03-01 14:46 ` [PATCH 43/43] Add support for nested composite locations Zoran Zaric

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).