public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 00/30] Allow location description on the DWARF stack
@ 2020-12-07 19:00 Zoran Zaric
  2020-12-07 19:00 ` [PATCH 01/30] Replace the symbol needs evaluator with a parser Zoran Zaric
                   ` (30 more replies)
  0 siblings, 31 replies; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

Based on gdb master: 13f11b0b61ca2620611b08eeaece0ce62c862f4b

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 fifteen
patches could be viewed as a standalone subset that introduces a
welcome cleanup of the DWARF expression evaluator module.

The patch series is based on a patch that is already in the review
process, so it had to be included in this series as well. The review
for that patch can be found here:

https://sourceware.org/pipermail/gdb-patches/2020-November/173638.html

But I don't mind it being discussed as a part of this patch series.

Zoran Zaric (30):
  Replace the symbol needs evaluator with a parser
  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 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 new location description access interface
  Add dwarf_entry factory class to expr.c
  Change DWARF stack to use new dwarf_entry classes
  Remove dwarf_expr_context from expr.h interface
  Rename and update the piece_closure structure
  Move read_addr_from_reg function to frame.c
  Add frame info check to DW_OP_reg operations
  Remove DWARF expression composition check
  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                             | 3406 +++++++++++++++--
 gdb/dwarf2/expr.h                             |  275 +-
 gdb/dwarf2/frame.c                            |  163 +-
 gdb/dwarf2/loc.c                              | 1885 +++------
 gdb/dwarf2/loc.h                              |   34 +-
 gdb/f-lang.c                                  |    2 +-
 gdb/findvar.c                                 |    4 +-
 gdb/frame.c                                   |   36 +-
 gdb/testsuite/gdb.dwarf2/dw2-llvm-offset.exp  |  328 ++
 .../gdb.dwarf2/dw2-llvm-piece-end.exp         |  191 +
 .../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.python/py-framefilter-invalidarg.exp  |    2 +-
 gdb/testsuite/lib/dwarf.exp                   |    8 +
 gdb/valops.c                                  |  123 +-
 gdb/value.c                                   |   68 +-
 gdb/value.h                                   |    6 +-
 include/dwarf2.def                            |    6 +
 23 files changed, 4841 insertions(+), 2114 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] 75+ messages in thread

* [PATCH 01/30] Replace the symbol needs evaluator with a parser
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2021-01-21 21:16   ` Tom Tromey
  2020-12-07 19:00 ` [PATCH 02/30] Move frame context info to dwarf_expr_context Zoran Zaric
                   ` (29 subsequent siblings)
  30 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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.

To support the new implementation, I have also added two new self tests,
found in a file dwarf2/loc.c (named symbol_needs_cond_nonzero and
symbol_needs_cond_zero), which expose a similar problem to the one
described in the first testsuite test case. The difference is that the
global some_variable is replaced with a use of a
DW_OP_push_object_address operation. The idea is for the symbol needs
evaluator to return the SYMBOL_NEEDS_FRAME value, regardless of the
evaluation of that operation.

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.

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.
        (symbol_needs_cond_zero): New function.
        (symbol_needs_cond_nonzero): New function.
        (_initialize_dwarf2loc): Register selftests.

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                              | 568 ++++++++++++++----
 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.python/py-framefilter-invalidarg.exp  |   2 +-
 gdb/testsuite/lib/dwarf.exp                   |   4 +
 7 files changed, 718 insertions(+), 117 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 fea49c340f..d5da8772be 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;
+
+	case DW_OP_deref_size:
+	case DW_OP_pick:
+	  op_ptr++;
+	  break;
 
-  /* DW_OP_addrx and DW_OP_GNU_addr_index doesn't require a frame.  */
+	case DW_OP_skip:
+	  op_ptr += 2;
+	  break;
 
-  CORE_ADDR get_addr_index (unsigned int index) override
-  {
-    /* Nothing to do.  */
-    return 1;
-  }
+	case DW_OP_bra:
+	  op_ptr += 2;
+	  break;
 
-  /* DW_OP_push_object_address has a frame already passed through.  */
+	case DW_OP_call2:
+	  {
+	    cu_offset cu_off
+	      = (cu_offset) extract_unsigned_integer (op_ptr, 2, byte_order);
+	    op_ptr += 2;
 
-  CORE_ADDR get_object_address () override
-  {
-    /* Nothing to do.  */
-    return 1;
-  }
-};
+	    auto get_frame_pc = [&symbol_needs] ()
+	      {
+		symbol_needs = SYMBOL_NEEDS_FRAME;
+		return 0;
+	      };
 
-/* Compute the correct symbol_needs_kind value for the location
-   expression at DATA (length SIZE).  */
+	    struct dwarf2_locexpr_baton baton
+	      = dwarf2_fetch_die_loc_cu_off (cu_off, per_cu,
+					     per_objfile,
+					     get_frame_pc);
 
-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;
+	    /* 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;
+	  }
 
-  symbol_needs_eval_context ctx (per_objfile);
+	case DW_OP_call4:
+	  {
+	    cu_offset cu_off
+	      = (cu_offset) extract_unsigned_integer (op_ptr, 4, byte_order);
+	    op_ptr += 4;
 
-  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 ();
+	    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;
+	  }
 
-  ctx.eval (data, size);
+	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;
+	      };
 
-  bool in_reg = ctx.location == DWARF_VALUE_REGISTER;
+	    struct dwarf2_locexpr_baton baton
+	      = dwarf2_fetch_die_loc_sect_off (sect_off, per_cu,
+					       per_objfile,
+					       get_frame_pc, true);
 
-  /* 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_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 ctx.needs;
+  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
@@ -4739,6 +4999,77 @@ const struct symbol_computed_ops dwarf2_loclist_funcs = {
   loclist_generate_c_location
 };
 
+#if GDB_SELF_TEST
+
+namespace selftests {
+
+/* Unit test for the symbol needs check mechanism.
+
+   This mechanism is not expected to access the target. This
+   means that a conditional expressions that depends on a target
+   access needs to be evaluated in full.
+
+   This test is testing if the zero resulting branch is ignored.  */
+
+static void
+symbol_needs_cond_zero ()
+{
+  const gdb_byte dwarf_expr[] {
+    0x97,           /* DW_OP_push_object_address */
+    0x28, 0x5, 0x0, /* DW_OP_bra 5 */
+    0x70, 0x0,      /* DW_OP_breg0 0 */
+    0x2f, 0x1, 0x0, /* DW_OP_skip 1 */
+    0x30,           /* DW_OP_lit0 */
+    0x9f,           /* DW_OP_stack_value */
+  };
+
+  symbol_needs_kind symbol_needs
+    = dwarf2_get_symbol_read_needs (dwarf_expr,
+				    NULL /* per_objfile */,
+				    NULL /* per_cu */,
+				    BFD_ENDIAN_LITTLE,
+				    4 /* addr_size */,
+				    4 /* ref_addr_size */);
+
+  SELF_CHECK (symbol_needs == SYMBOL_NEEDS_FRAME);
+}
+
+/* Unit test for the symbol needs check mechanism.
+
+   This mechanism is not expected to access the target. This
+   means that a conditional expressions that depends on a target
+   access needs to be evaluated in full.
+
+   This test is testing if the nonzero resulting branch is ignored.  */
+
+static void
+symbol_needs_cond_nonzero ()
+{
+  const gdb_byte dwarf_expr[] {
+    0x97,           /* DW_OP_push_object_address */
+    0x30,           /* DW_OP_lit0 */
+    0x29,           /* DW_OP_eq */
+    0x28, 0x5, 0x0, /* DW_OP_bra 5 */
+    0x70, 0x0,      /* DW_OP_breg0 0 */
+    0x2f, 0x1, 0x0, /* DW_OP_skip 1 */
+    0x30,           /* DW_OP_lit0 */
+    0x9f,           /* DW_OP_stack_value */
+  };
+
+  symbol_needs_kind symbol_needs
+    = dwarf2_get_symbol_read_needs (dwarf_expr,
+				    NULL /* per_objfile */,
+				    NULL /* per_cu */,
+				    BFD_ENDIAN_LITTLE,
+				    4 /* addr_size */,
+				    4 /* ref_addr_size */);
+
+  SELF_CHECK (symbol_needs == SYMBOL_NEEDS_FRAME);
+}
+
+} // namespace selftests
+#endif /* GDB_SELF_TEST */
+
 void _initialize_dwarf2loc ();
 void
 _initialize_dwarf2loc ()
@@ -4767,4 +5098,11 @@ conversational style, when possible."),
 			   show_dwarf_always_disassemble,
 			   &set_dwarf_cmdlist,
 			   &show_dwarf_cmdlist);
+
+#if GDB_SELF_TEST
+  selftests::register_test ("symbol_needs_cond_zero",
+			    selftests::symbol_needs_cond_zero);
+  selftests::register_test ("symbol_needs_cond_nonzero",
+			    selftests::symbol_needs_cond_nonzero);
+#endif
 }
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 0000000000..9740944a73
--- /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 0000000000..00a57228fa
--- /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 0000000000..52dfb136fb
--- /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 9c2ffb6e60..b1ea42cb10 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/gdb.python/py-framefilter-invalidarg.exp b/gdb/testsuite/gdb.python/py-framefilter-invalidarg.exp
index beb66ab82d..143f35f8e0 100644
--- a/gdb/testsuite/gdb.python/py-framefilter-invalidarg.exp
+++ b/gdb/testsuite/gdb.python/py-framefilter-invalidarg.exp
@@ -63,4 +63,4 @@ set remote_python_file [gdb_remote_download host \
 			    ${srcdir}/${subdir}/${testfile}.py]
 gdb_test_no_output "source ${remote_python_file}" "load python file"
 
-gdb_test "bt" "niam \\(argc=<error reading variable: dwarf expression stack underflow>, argv=0x\[0-9a-f\]+\\) at py-framefilter-invalidarg.c:\[0-9\]+" "bt full with filters"
+gdb_test "bt" "niam \\(argc=<error reading variable: dwarf expression stack underflow>, argv=0x\[0-9a-f\]+\\) at py-framefilter-invalidarg.c:\[0-9\]+" "bt full with filters"
\ No newline at end of file
diff --git a/gdb/testsuite/lib/dwarf.exp b/gdb/testsuite/lib/dwarf.exp
index ecd438b205..223e0b15be 100644
--- a/gdb/testsuite/lib/dwarf.exp
+++ b/gdb/testsuite/lib/dwarf.exp
@@ -1018,6 +1018,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] 75+ messages in thread

* [PATCH 02/30] Move frame context info to dwarf_expr_context
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
  2020-12-07 19:00 ` [PATCH 01/30] Replace the symbol needs evaluator with a parser Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2021-01-21 21:23   ` Tom Tromey
  2020-12-07 19:00 ` [PATCH 03/30] Remove get_frame_cfa from dwarf_expr_context Zoran Zaric
                   ` (28 subsequent siblings)
  30 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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  | 76 +++++++++++++++++++++++++++++++++++++++++++---
 gdb/dwarf2/expr.h  | 31 ++++++++++---------
 gdb/dwarf2/frame.c | 36 ++--------------------
 gdb/dwarf2/loc.c   | 52 +------------------------------
 4 files changed, 92 insertions(+), 103 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index eaeb76f9c6..91d0b99640 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,27 @@ dwarf_gdbarch_types_init (struct gdbarch *gdbarch)
   return types;
 }
 
+/* Ensure that a FRAME is defined, throw an exception otherwise.  */
+
+static void
+ensure_have_frame (struct frame_info *frame, const char *op_name)
+{
+  if (frame == nullptr)
+    throw_error (GENERIC_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.  */
@@ -99,7 +121,8 @@ dwarf_expr_context::dwarf_expr_context (dwarf2_per_objfile *per_objfile)
   len (0),
   data (NULL),
   initialized (0),
-  per_objfile (per_objfile)
+  per_objfile (per_objfile),
+  frame (nullptr)
 {
 }
 
@@ -142,6 +165,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
@@ -830,7 +894,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);
 	  }
@@ -839,7 +905,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);
 	  }
@@ -866,7 +934,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 fd9c2bb624..140d3c36db 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;
 
   /* 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 7df3ccfe46..ce1f9e2763 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 d5da8772be..8913051e4e 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] 75+ messages in thread

* [PATCH 03/30] Remove get_frame_cfa from dwarf_expr_context
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
  2020-12-07 19:00 ` [PATCH 01/30] Replace the symbol needs evaluator with a parser Zoran Zaric
  2020-12-07 19:00 ` [PATCH 02/30] Move frame context info to dwarf_expr_context Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2021-01-21 21:23   ` Tom Tromey
  2020-12-07 19:00 ` [PATCH 04/30] Move compilation unit info to dwarf_expr_context Zoran Zaric
                   ` (27 subsequent siblings)
  30 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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 91d0b99640..6ba8b90c58 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"
 
@@ -1226,7 +1227,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 140d3c36db..5abb870f88 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 ce1f9e2763..4cc0ffe3c5 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 8913051e4e..ecf1020b3b 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] 75+ messages in thread

* [PATCH 04/30] Move compilation unit info to dwarf_expr_context
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (2 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 03/30] Remove get_frame_cfa from dwarf_expr_context Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2021-01-21 21:28   ` Tom Tromey
  2020-12-07 19:00 ` [PATCH 05/30] Move dwarf_call " Zoran Zaric
                   ` (26 subsequent siblings)
  30 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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  | 54 ++++++++++++++++++++++++++++++++++++++--------
 gdb/dwarf2/expr.h  | 26 ++++++++--------------
 gdb/dwarf2/frame.c | 10 ---------
 gdb/dwarf2/loc.c   | 35 +++---------------------------
 gdb/dwarf2/loc.h   |  8 +++++++
 5 files changed, 65 insertions(+), 68 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 6ba8b90c58..29f5f977ac 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -68,6 +68,16 @@ 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.  */
+
+static void
+ensure_have_per_cu (struct dwarf2_per_cu_data *per_cu, const char* op_name)
+{
+  if (per_cu == nullptr)
+    throw_error (GENERIC_ERROR,
+		 _("%s evaluation requires a compilation unit."), op_name);
+}
+
 /* See expr.h.  */
 
 CORE_ADDR
@@ -123,7 +133,8 @@ dwarf_expr_context::dwarf_expr_context (dwarf2_per_objfile *per_objfile)
   data (NULL),
   initialized (0),
   per_objfile (per_objfile),
-  frame (nullptr)
+  frame (nullptr),
+  per_cu (nullptr)
 {
 }
 
@@ -207,6 +218,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
@@ -703,14 +733,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;
 
@@ -840,10 +876,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,
@@ -1336,13 +1369,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 5abb870f88..e17cd99c25 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;
 
+  /* Compilation unit used for the evaluation.  */
+  struct dwarf2_per_cu_data *per_cu;
+
   /* 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 4cc0ffe3c5..c93ec5d5a6 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 ecf1020b3b..21e43e399c 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 907455b72c..85700ea99d 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] 75+ messages in thread

* [PATCH 05/30] Move dwarf_call to dwarf_expr_context
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (3 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 04/30] Move compilation unit info to dwarf_expr_context Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2021-01-21 21:30   ` Tom Tromey
  2020-12-07 19:00 ` [PATCH 06/30] Move get_object_address " Zoran Zaric
                   ` (25 subsequent siblings)
  30 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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 29f5f977ac..a5144b692d 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -237,6 +237,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
@@ -1279,7 +1304,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 e17cd99c25..eb765a28a2 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 c93ec5d5a6..af5302bd98 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 21e43e399c..5a2b1b2851 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] 75+ messages in thread

* [PATCH 06/30] Move get_object_address to dwarf_expr_context
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (4 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 05/30] Move dwarf_call " Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2021-01-21 21:31   ` Tom Tromey
  2020-12-07 19:00 ` [PATCH 07/30] Move read_mem " Zoran Zaric
                   ` (24 subsequent siblings)
  30 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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.c  |  3 ++-
 gdb/dwarf2/expr.h  | 10 +++++++++-
 gdb/dwarf2/frame.c |  5 -----
 gdb/dwarf2/loc.c   | 12 ------------
 4 files changed, 11 insertions(+), 19 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index a5144b692d..e6a9e877e4 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -134,7 +134,8 @@ dwarf_expr_context::dwarf_expr_context (dwarf2_per_objfile *per_objfile)
   initialized (0),
   per_objfile (per_objfile),
   frame (nullptr),
-  per_cu (nullptr)
+  per_cu (nullptr),
+  obj_address (0)
 {
 }
 
diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
index eb765a28a2..e3e8bf8ae5 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;
 
+  /* Object address used for the evaluation.  */
+  CORE_ADDR obj_address;
+
   /* 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 af5302bd98..96bfdc1452 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 5a2b1b2851..c1f5457e1d 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] 75+ messages in thread

* [PATCH 07/30] Move read_mem to dwarf_expr_context
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (5 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 06/30] Move get_object_address " Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2021-01-21 21:34   ` Tom Tromey
  2020-12-07 19:00 ` [PATCH 08/30] Move push_dwarf_reg_entry_value to expr.c Zoran Zaric
                   ` (23 subsequent siblings)
  30 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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 e6a9e877e4..a2aa9435a2 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -263,6 +263,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 e3e8bf8ae5..3c40dde13d 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -196,7 +196,7 @@ struct dwarf_expr_context
   CORE_ADDR obj_address;
 
   /* 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 96bfdc1452..46f3f5145d 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 c1f5457e1d..679852c81b 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] 75+ messages in thread

* [PATCH 08/30] Move push_dwarf_reg_entry_value to expr.c
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (6 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 07/30] Move read_mem " Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2021-01-21 21:35   ` Tom Tromey
  2020-12-07 19:00 ` [PATCH 09/30] Inline get_reg_value method of dwarf_expr_context Zoran Zaric
                   ` (22 subsequent siblings)
  30 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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 a2aa9435a2..33237e1f27 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -272,6 +272,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 3c40dde13d..71c10ebe3b 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 46f3f5145d..3eb2aa9991 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 679852c81b..2750907c9e 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 85700ea99d..068c75af93 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] 75+ messages in thread

* [PATCH 09/30] Inline get_reg_value method of dwarf_expr_context
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (7 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 08/30] Move push_dwarf_reg_entry_value to expr.c Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2021-01-21 21:36   ` Tom Tromey
  2020-12-07 19:00 ` [PATCH 10/30] Remove empty frame and full evaluators Zoran Zaric
                   ` (21 subsequent siblings)
  30 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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 33237e1f27..a7bc084755 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -180,19 +180,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)
@@ -1543,14 +1530,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 71c10ebe3b..a04a67c0f8 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] 75+ messages in thread

* [PATCH 10/30] Remove empty frame and full evaluators
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (8 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 09/30] Inline get_reg_value method of dwarf_expr_context Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2021-01-21 21:37   ` Tom Tromey
  2020-12-07 19:00 ` [PATCH 11/30] Merge evaluate_for_locexpr_baton evaluator Zoran Zaric
                   ` (20 subsequent siblings)
  30 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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 3eb2aa9991..9081ec0c92 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 2750907c9e..2e353f1b31 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] 75+ messages in thread

* [PATCH 11/30] Merge evaluate_for_locexpr_baton evaluator
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (9 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 10/30] Remove empty frame and full evaluators Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2021-02-08 21:21   ` Tom Tromey
  2020-12-07 19:00 ` [PATCH 12/30] Move piece_closure and its support to expr.c Zoran Zaric
                   ` (19 subsequent siblings)
  30 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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 a7bc084755..9662f83d2e 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -256,6 +256,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);
 }
 
@@ -1580,8 +1592,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 a04a67c0f8..2fb65822bd 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;
 
-  /* 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 2e353f1b31..b9fe5c2f81 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] 75+ messages in thread

* [PATCH 12/30] Move piece_closure and its support to expr.c
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (10 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 11/30] Merge evaluate_for_locexpr_baton evaluator Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2021-02-08 21:32   ` Tom Tromey
  2020-12-07 19:00 ` [PATCH 13/30] Make DWARF evaluator return a single struct value Zoran Zaric
                   ` (18 subsequent siblings)
  30 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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.
	* dwarf2/loc.h (invalid_synthetic_pointer): Expose function.
---
 gdb/dwarf2/expr.c | 570 ++++++++++++++++++++++++++++++++++++++++++++
 gdb/dwarf2/expr.h |  12 +
 gdb/dwarf2/loc.c  | 586 +---------------------------------------------
 gdb/dwarf2/loc.h  |  22 +-
 4 files changed, 600 insertions(+), 590 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 9662f83d2e..675f044e6f 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -78,6 +78,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
@@ -89,6 +98,567 @@ 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,
+					       this_size, buffer.data (),
+					       &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,
+					      this_size, buffer.data (),
+					      &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,
+					  this_size, buffer.data ());
+	      }
+	  }
+	  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 2fb65822bd..1dee2249a0 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 b9fe5c2f81..2f75b380a2 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,10 +89,9 @@ 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
+void
 invalid_synthetic_pointer (void)
 {
   error (_("access outside bounds of object "
@@ -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,
-					       this_size, buffer.data (),
-					       &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,
-					      this_size, buffer.data (),
-					      &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,
-					  this_size, buffer.data ());
-	      }
-	  }
-	  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 068c75af93..1de3604194 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,18 @@ 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 (void);
+
+/* 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] 75+ messages in thread

* [PATCH 13/30] Make DWARF evaluator return a single struct value
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (11 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 12/30] Move piece_closure and its support to expr.c Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2021-02-08 21:35   ` Tom Tromey
  2020-12-07 19:00 ` [PATCH 14/30] Simplify dwarf_expr_context class interface Zoran Zaric
                   ` (17 subsequent siblings)
  30 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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.
---
 gdb/dwarf2/expr.c  | 163 +++++++++++++++++++++++++++++++++++++-
 gdb/dwarf2/expr.h  |  25 +++---
 gdb/dwarf2/frame.c |  19 ++---
 gdb/dwarf2/loc.c   | 193 ++++++---------------------------------------
 4 files changed, 201 insertions(+), 199 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 675f044e6f..7f8e8134a4 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -117,9 +117,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,
@@ -622,7 +623,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,
@@ -892,6 +893,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 1dee2249a0..b7605ffd28 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 9081ec0c92..039990dea3 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 2f75b380a2..90ab2077c6 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -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;
+  /* 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 ();
 
-	    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);
-
-  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.  */
-- 
2.17.1


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

* [PATCH 14/30] Simplify dwarf_expr_context class interface
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (12 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 13/30] Make DWARF evaluator return a single struct value Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2021-02-08 21:38   ` Tom Tromey
  2020-12-07 19:00 ` [PATCH 15/30] Add as_lval argument to expression evaluator Zoran Zaric
                   ` (16 subsequent siblings)
  30 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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
- eval_exp 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 eval_exp 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::eval_exp): New function.
	(dwarf_expr_context::execute_stack_op): Change to use
	property_addr_info structure.
	* dwarf2/expr.h (struct dwarf_expr_context): New eval_exp
	declaration. Change eval and fetch_result method to private.
	* dwarf2/frame.c (execute_stack_op): Change to call eval_exp
	method.
	* dwarf2/loc.c (dwarf2_evaluate_loc_desc_full): Change to call
	eval_exp method.
	(dwarf2_locexpr_baton_eval): Change to call eval_exp method.
---
 gdb/dwarf2/expr.c  | 51 +++++++++++++++++++++++++++++++++++-----------
 gdb/dwarf2/expr.h  | 44 +++++++++++++++++++++++++--------------
 gdb/dwarf2/frame.c | 18 ++++------------
 gdb/dwarf2/loc.c   | 41 ++++++++++++-------------------------
 4 files changed, 85 insertions(+), 69 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 7f8e8134a4..5e99a98e1e 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -693,9 +693,10 @@ 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),
+dwarf_expr_context::dwarf_expr_context (dwarf2_per_objfile *per_objfile,
+					int addr_size)
+: gdbarch (per_objfile->objfile->arch ()),
+  addr_size (addr_size),
   ref_addr_size (0),
   recursion_depth (0),
   max_recursion_depth (0x100),
@@ -706,7 +707,7 @@ dwarf_expr_context::dwarf_expr_context (dwarf2_per_objfile *per_objfile)
   per_objfile (per_objfile),
   frame (nullptr),
   per_cu (nullptr),
-  obj_address (0)
+  addr_info (nullptr)
 {
 }
 
@@ -831,13 +832,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);
 }
@@ -880,8 +885,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);
 
@@ -1049,6 +1054,28 @@ dwarf_expr_context::fetch_result (struct type *type,
   return retval;
 }
 
+/* See expr.h.  */
+
+struct value *
+dwarf_expr_context::eval_exp (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
@@ -2319,11 +2346,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 b7605ffd28..69a043da50 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 *eval_exp (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;
 
-  /* Object address used for the evaluation.  */
-  CORE_ADDR obj_address;
-
-  /* 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;
 
+  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 039990dea3..228c48f387 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.eval_exp (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 90ab2077c6..fdf8385933 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.eval_exp (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.eval_exp (dlbaton->data, dlbaton->size,
+			     per_cu, frame, addr_stack);
     }
   catch (const gdb_exception_error &ex)
     {
-- 
2.17.1


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

* [PATCH 15/30] Add as_lval argument to expression evaluator
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (13 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 14/30] Simplify dwarf_expr_context class interface Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2021-02-08 21:41   ` Tom Tromey
  2020-12-07 19:00 ` [PATCH 16/30] Add new register access interface to expr.c Zoran Zaric
                   ` (15 subsequent siblings)
  30 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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 5e99a98e1e..b1283e70df 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -903,7 +903,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;
 
@@ -933,6 +934,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:
@@ -1057,7 +1063,7 @@ dwarf_expr_context::fetch_result (struct type *type,
 /* See expr.h.  */
 
 struct value *
-dwarf_expr_context::eval_exp (const gdb_byte *addr, size_t len,
+dwarf_expr_context::eval_exp (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,
@@ -1073,7 +1079,7 @@ dwarf_expr_context::eval_exp (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 69a043da50..1c06308f99 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 *eval_exp (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 *eval_exp (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 228c48f387..4037653106 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.eval_exp (exp, len, nullptr, this_frame);
+  struct value *result_val = ctx.eval_exp (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 fdf8385933..1a23e0b2da 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.eval_exp (data, size, per_cu, frame, nullptr, type,
-			     subobj_type, subobj_byte_offset);
+      retval = ctx.eval_exp (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.eval_exp (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 1de3604194..c14ba8febd 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] 75+ messages in thread

* [PATCH 16/30] Add new register access interface to expr.c
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (14 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 15/30] Add as_lval argument to expression evaluator Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2021-02-09 19:37   ` Tom Tromey
  2020-12-07 19:00 ` [PATCH 17/30] Add new memory " Zoran Zaric
                   ` (14 subsequent siblings)
  30 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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 | 131 +++++++++++++++++++++++++++++++++++++---------
 1 file changed, 107 insertions(+), 24 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index b1283e70df..0493bd926e 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -98,6 +98,94 @@ 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, int length, 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);
+
+  /* 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, (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, int length, 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);
+
+  /* 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, length);
+
+      put_frame_register (frame, regnum, temp_buf.data ());
+    }
+
+  return;
+}
+
 struct piece_closure
 {
   /* Reference count.  */
@@ -235,23 +323,19 @@ 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,
-					       this_size, buffer.data (),
-					       &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,
+				    this_size, buffer.data (),
+				    &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
 	      {
@@ -260,10 +344,9 @@ 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,
-					      this_size, buffer.data (),
-					      &optim, &unavail);
+		    read_from_register (frame, gdb_regnum, bits_to_skip / 8,
+					this_size, buffer.data (),
+					&optim, &unavail);
 		    if (optim)
 		      throw_error (OPTIMIZED_OUT_ERROR,
 				   _("Can't do read-modify-write to "
@@ -279,9 +362,9 @@ 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,
-					  this_size, buffer.data ());
+		write_to_register (frame, gdb_regnum, bits_to_skip / 8,
+				   this_size, buffer.data (),
+				   &optim, &unavail);
 	      }
 	  }
 	  break;
-- 
2.17.1


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

* [PATCH 17/30] Add new memory access interface to expr.c
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (15 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 16/30] Add new register access interface to expr.c Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2021-02-09 19:45   ` Tom Tromey
  2020-12-07 19:00 ` [PATCH 18/30] Add new classes that model DWARF stack element Zoran Zaric
                   ` (13 subsequent siblings)
  30 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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_from_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 0493bd926e..0c9ea51e5b 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.  */
 
@@ -186,6 +188,85 @@ write_to_register (struct frame_info *frame, int regnum,
   return;
 }
 
+/* Helper for read_from_memory and write_from_memory.  */
+
+static void
+xfer_from_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_from_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_from_memory (address, nullptr, buffer, length, stack, unavailable);
+
+  gdb::observers::memory_changed.notify (current_inferior (), address,
+					 length, buffer);
+}
+
 struct piece_closure
 {
   /* Reference count.  */
@@ -374,66 +455,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] 75+ messages in thread

* [PATCH 18/30] Add new classes that model DWARF stack element
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (16 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 17/30] Add new memory " Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2021-02-08 21:54   ` Tom Tromey
  2020-12-07 19:00 ` [PATCH 19/30] Add new location description access interface Zoran Zaric
                   ` (12 subsequent siblings)
  30 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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 | 346 ++++++++++++++++++++++++++++++++++++++++++++++
 gdb/value.c       |   2 +-
 gdb/value.h       |   2 +
 3 files changed, 349 insertions(+), 1 deletion(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 0c9ea51e5b..c0bb06fc4c 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -267,6 +267,352 @@ 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 refcounted_object
+{
+public:
+  /* Not expected to be called on it's own.  */
+  dwarf_entry () = default;
+
+  virtual ~dwarf_entry () = 0;
+};
+
+dwarf_entry::~dwarf_entry () = default;
+
+/* 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;
+  }
+
+  virtual ~dwarf_value () = default;
+
+  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;
+};
+
+/* 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 (made 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 (LONGEST offset = 0, LONGEST bit_suboffset = 0)
+    : 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;
+
+  LONGEST get_offset () const
+  {
+    return m_offset;
+  };
+
+  LONGEST get_bit_suboffset () const
+  {
+    return m_bit_suboffset;
+  };
+
+  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;
+  };
+
+  bool is_initialised () const
+  {
+    return m_initialised;
+  };
+
+private:
+  /* 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;
+};
+
+/* 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 (LONGEST offset = 0, LONGEST bit_suboffset = 0)
+    : dwarf_location (offset, bit_suboffset)
+  {}
+};
+
+class dwarf_memory : public dwarf_location
+{
+public:
+  dwarf_memory (LONGEST offset, LONGEST bit_suboffset = 0,
+		bool stack = false)
+    : dwarf_location (offset, bit_suboffset),
+      m_stack (stack)
+  {}
+
+  bool in_stack () const
+  {
+    return m_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 (unsigned int regnum,
+		  LONGEST offset = 0, LONGEST bit_suboffset = 0)
+    : dwarf_location (offset, bit_suboffset),
+      m_regnum (regnum)
+  {}
+
+  unsigned int get_regnum () const
+  {
+    return m_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 (const gdb_byte* contents, size_t size,
+		  enum bfd_endian byte_order)
+  {
+    m_contents.reset ((gdb_byte *) xzalloc (size));
+
+    memcpy (m_contents.get (), contents, size);
+    m_size = size;
+    m_byte_order = byte_order;
+  }
+
+  const gdb_byte* get_contents () const
+  {
+    return m_contents.get ();
+  }
+
+  size_t get_size () const
+  {
+    return m_size;
+  }
+
+  size_t get_byte_order () const
+  {
+    return m_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 (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 (offset, bit_suboffset),
+      m_per_objfile (per_objfile), m_per_cu (per_cu),
+      m_addr_size (addr_size), m_die_offset (die_offset)
+  {}
+
+  dwarf2_per_objfile *get_per_objfile () const
+  {
+    return m_per_objfile;
+  }
+
+  dwarf2_per_cu_data *get_per_cu () const
+  {
+    return m_per_cu;
+  }
+
+  int get_addr_size () const
+  {
+    return m_addr_size;
+  }
+
+  sect_offset get_die_offset () const
+  {
+    return m_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 (LONGEST offset = 0, LONGEST bit_suboffset = 0)
+    : dwarf_location (offset, bit_suboffset)
+  {}
+
+  /* A composite location gets detached from its factory object for
+     the purpose of lval_computed resolution, which means that it
+     needs to take care of garbage collecting its pieces.  */
+  ~dwarf_composite () override
+  {
+    for (unsigned int i = 0; i < m_pieces.size (); i++)
+      {
+	dwarf_location* location = m_pieces[i].m_location;
+
+	location->decref ();
+
+	if (location->refcount () == 0)
+	  delete location;
+      }
+  }
+
+  void add_piece (dwarf_location* location, ULONGEST bit_size)
+  {
+    gdb_assert (location != nullptr);
+    location->incref ();
+    m_pieces.emplace_back (location, bit_size);
+  }
+
+  const dwarf_location* get_piece_at (unsigned int index) const
+  {
+    gdb_assert (index < m_pieces.size ());
+    return m_pieces[index].m_location;
+  }
+
+  ULONGEST get_bit_size_at (unsigned int index) const
+  {
+    gdb_assert (index < m_pieces.size ());
+    return m_pieces[index].m_size;
+  }
+
+  size_t get_pieces_num () const
+  {
+    return m_pieces.size ();
+  }
+
+private:
+  /* Composite piece that contains a piece location
+     description and it's size.  */
+  class piece
+  {
+  public:
+    piece (dwarf_location *location, ULONGEST size)
+    : m_location (location),
+      m_size (size)
+    {}
+
+    dwarf_location *m_location;
+    ULONGEST m_size;
+  };
+
+  /* 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 eba5bce6d2..5f52c2d566 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -3401,7 +3401,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 d026c8ed95..2eaba57487 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -677,6 +677,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] 75+ messages in thread

* [PATCH 19/30] Add new location description access interface
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (17 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 18/30] Add new classes that model DWARF stack element Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2021-02-08 21:46   ` Tom Tromey
  2020-12-07 19:00 ` [PATCH 20/30] Add dwarf_entry factory class to expr.c Zoran Zaric
                   ` (11 subsequent siblings)
  30 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

After adding interface for register and memory location access, a new
top level interface for accessing any location, described by the
dwarf_location class based objects, can now be defined.

Also, the address_type method is now needed to be outside of the
dwarf_stack_value class to allow creation of the DWARF generic type
independently of that class.

	* dwarf2/expr.c (read_from_location): New function.
	(write_to_location): New function.
	(address_type): New function.
	* dwarf2/expr.h (address_type): Exposed function.
---
 gdb/dwarf2/expr.c | 427 ++++++++++++++++++++++++++++++++++++++++++++--
 gdb/dwarf2/expr.h |   4 +
 2 files changed, 420 insertions(+), 11 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index c0bb06fc4c..fea82f8a87 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -613,6 +613,404 @@ class dwarf_composite : public dwarf_location
   std::vector<struct piece> m_pieces;
 };
 
+/* Read contents from the location specified by the DWARF location
+   description entry LOCATION.
+
+   The read operation is performed in the context of 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
+   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.  */
+
+static void
+read_from_location (const dwarf_location *location, struct frame_info *frame,
+		    LONGEST bits_to_skip, gdb_byte *buf, int buf_bit_offset,
+		    size_t bit_size, size_t location_bit_limit,
+		    bool big_endian, int* optimized, int* unavailable)
+{
+  LONGEST offset = location->get_offset ();
+  LONGEST bit_suboffset = location->get_bit_suboffset ();
+  LONGEST total_bits_to_skip = bits_to_skip;
+  size_t read_bit_limit = location_bit_limit;
+  gdb::byte_vector temp_buf;
+
+  /* Reads from undefined locations are always marked as optimized
+     out.  */
+  if (dynamic_cast<const dwarf_undefined *> (location) != nullptr)
+    {
+      (*unavailable) = 0;
+      (*optimized) = 1;
+    }
+  else if (auto register_entry
+	     = dynamic_cast<const dwarf_register *> (location))
+    {
+      struct gdbarch *arch = get_frame_arch (frame);
+      int reg = dwarf_reg_to_regnum_or_error (arch,
+					      register_entry->get_regnum ());
+      ULONGEST reg_bits = HOST_CHAR_BIT * register_size (arch, reg);
+
+      if (big_endian)
+	{
+	  if (!read_bit_limit || reg_bits <= read_bit_limit)
+	    read_bit_limit = bit_size;
+
+	  total_bits_to_skip
+	    += reg_bits - (offset * HOST_CHAR_BIT
+			   + bit_suboffset + read_bit_limit);
+	}
+      else
+	total_bits_to_skip += offset * HOST_CHAR_BIT + bit_suboffset;
+
+      LONGEST this_size = bits_to_bytes (total_bits_to_skip, bit_size);
+      temp_buf.resize (this_size);
+
+      /* 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,
+			  this_size, temp_buf.data (), 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);
+    }
+  else if (auto memory_entry = dynamic_cast<const dwarf_memory *> (location))
+    {
+      CORE_ADDR start_address
+	= offset + (bit_suboffset + total_bits_to_skip) / HOST_CHAR_BIT;
+
+      (*optimized) = 0;
+      total_bits_to_skip += 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,
+			    memory_entry->in_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,
+			    memory_entry->in_stack (), unavailable);
+
+	  if (!(*unavailable))
+	    copy_bitwise (buf, buf_bit_offset, temp_buf.data (),
+			  total_bits_to_skip % HOST_CHAR_BIT,
+			  bit_size, big_endian);
+	}
+    }
+  else if (auto implicit_entry
+	     = dynamic_cast<const dwarf_implicit *> (location))
+    {
+      ULONGEST literal_bit_size = HOST_CHAR_BIT * implicit_entry->get_size ();
+
+      (*optimized) = 0;
+      (*unavailable) = 0;
+
+      /* Cut off at the end of the implicit value.  */
+      if (implicit_entry->get_byte_order() == BFD_ENDIAN_BIG)
+	{
+	  if (!read_bit_limit || read_bit_limit > literal_bit_size)
+	    read_bit_limit = bit_size;
+
+	  total_bits_to_skip
+	    += literal_bit_size - (offset * HOST_CHAR_BIT
+				   + bit_suboffset + read_bit_limit);
+	}
+      else
+	total_bits_to_skip += offset * HOST_CHAR_BIT + bit_suboffset;
+
+      if (total_bits_to_skip >= literal_bit_size)
+	{
+	  (*unavailable) = 1;
+	  return;
+	}
+
+      if (bit_size > literal_bit_size - total_bits_to_skip)
+	bit_size = literal_bit_size - total_bits_to_skip;
+
+      copy_bitwise (buf, buf_bit_offset, implicit_entry->get_contents (),
+		    total_bits_to_skip, bit_size, big_endian);
+    }
+  else if (auto pointer_entry
+	     = dynamic_cast<const dwarf_implicit_pointer *> (location))
+    {
+      struct frame_info *read_frame = frame;
+
+      if (read_frame == nullptr)
+	read_frame = get_selected_frame (_("No frame selected."));
+
+      struct type *type = address_type (get_frame_arch (read_frame),
+					pointer_entry->get_addr_size ());
+
+      struct value *value
+	= indirect_synthetic_pointer (pointer_entry->get_die_offset (),
+				      pointer_entry->get_offset (),
+				      pointer_entry->get_per_cu (),
+				      pointer_entry->get_per_objfile (),
+				      read_frame, type);
+
+      total_bits_to_skip += bit_suboffset;
+
+      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);
+	}
+    }
+  else if (auto composite_entry
+	     = dynamic_cast<const dwarf_composite *> (location))
+    {
+      unsigned int pieces_num = composite_entry->get_pieces_num ();
+      unsigned int i;
+
+      total_bits_to_skip += offset * HOST_CHAR_BIT + bit_suboffset;
+
+      /* Skip pieces covered by the read offset.  */
+      for (i = 0; i < pieces_num; i++)
+	{
+	  LONGEST piece_bit_size = composite_entry->get_bit_size_at (i);
+
+	  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 = composite_entry->get_bit_size_at (i);
+	  LONGEST actual_bit_size = piece_bit_size;
+
+	  if (actual_bit_size > bit_size)
+	    actual_bit_size = bit_size;
+
+	  read_from_location (composite_entry->get_piece_at (i), frame,
+			      total_bits_to_skip, buf, buf_bit_offset,
+			      actual_bit_size, 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;
+	}
+    }
+  else
+    internal_error (__FILE__, __LINE__, _("invalid location type"));
+}
+
+/* Write contents to a location specified by the DWARF location
+   description entry LOCATION.
+
+   The write operation is performed in the context of 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
+   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.  */
+
+static void
+write_to_location (const dwarf_location *location, struct frame_info *frame,
+		   LONGEST bits_to_skip, const gdb_byte *buf,
+		   int buf_bit_offset, size_t bit_size,
+		   size_t location_bit_limit, bool big_endian,
+		   int* optimized, int* unavailable)
+{
+  LONGEST offset = location->get_offset();
+  LONGEST bit_suboffset = location->get_bit_suboffset();
+  LONGEST total_bits_to_skip = bits_to_skip;
+  size_t write_bit_limit = location_bit_limit;
+  gdb::byte_vector temp_buf;
+
+  /* Writes to undefined locations are always marked as optimized
+     out.  */
+  if (dynamic_cast<const dwarf_undefined *> (location) != nullptr)
+    {
+      (*unavailable) = 0;
+      (*optimized) = 1;
+    }
+  else if (auto register_entry
+	     = dynamic_cast<const dwarf_register *> (location))
+    {
+      struct gdbarch *arch = get_frame_arch (frame);
+      int gdb_regnum
+	= dwarf_reg_to_regnum_or_error (arch, register_entry->get_regnum ());
+      ULONGEST reg_bits = HOST_CHAR_BIT * register_size (arch, gdb_regnum);
+
+      if (big_endian)
+	{
+	  if (!write_bit_limit || reg_bits <= write_bit_limit)
+	    write_bit_limit = bit_size;
+
+	  total_bits_to_skip
+	    += reg_bits - (offset * HOST_CHAR_BIT
+			   + bit_suboffset + write_bit_limit);
+	}
+      else
+	total_bits_to_skip += offset * HOST_CHAR_BIT + 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,
+			      this_size, temp_buf.data (), 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,
+			 this_size, temp_buf.data (), optimized, unavailable);
+    }
+  else if (auto memory_entry = dynamic_cast<const dwarf_memory *> (location))
+    {
+      CORE_ADDR start_address
+	= offset + (bit_suboffset + total_bits_to_skip) / HOST_CHAR_BIT;
+
+      total_bits_to_skip += 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, memory_entry->in_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, memory_entry->in_stack (),
+				  unavailable);
+	      else
+		{
+		  /* Only the first and last bytes can possibly have
+		     any bits reused.  */
+		  read_from_memory (start_address, temp_buf.data (),
+				    1, memory_entry->in_stack (), unavailable);
+
+		  if (!(*unavailable))
+		    read_from_memory (start_address + this_size - 1,
+				      &temp_buf[this_size - 1], 1,
+				      memory_entry->in_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,
+			   memory_entry->in_stack (), unavailable);
+	}
+    }
+  else if (dynamic_cast<const dwarf_implicit *> (location) != nullptr)
+    {
+      (*optimized) = 1;
+      (*unavailable) = 0;
+    }
+  else if (dynamic_cast<const dwarf_implicit_pointer *> (location) != nullptr)
+    {
+      (*optimized) = 1;
+      (*unavailable) = 0;
+    }
+  else if (auto composite_entry
+	     = dynamic_cast<const dwarf_composite *> (location))
+    {
+      unsigned int pieces_num = composite_entry->get_pieces_num ();
+      unsigned int i;
+
+      total_bits_to_skip += offset * HOST_CHAR_BIT + bit_suboffset;
+
+      /* Skip pieces covered by the write offset.  */
+      for (i = 0; i < pieces_num; i++)
+	{
+	  LONGEST piece_bit_size = composite_entry->get_bit_size_at (i);
+
+	  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 = composite_entry->get_bit_size_at (i);
+	  LONGEST actual_bit_size = piece_bit_size;
+
+	  if (actual_bit_size > bit_size)
+	    actual_bit_size = bit_size;
+
+	  write_to_location (composite_entry->get_piece_at (i), frame,
+			     total_bits_to_skip, buf, buf_bit_offset,
+			     actual_bit_size, 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;
+	}
+    }
+  else
+    internal_error (__FILE__, __LINE__, _("invalid location type"));
+}
+
 struct piece_closure
 {
   /* Reference count.  */
@@ -1190,37 +1588,44 @@ sect_variable_value (sect_offset sect_off,
 				     type, true);
 }
 
-/* Return the type used for DWARF operations where the type is
-   unspecified in the DWARF spec.  Only certain sizes are
-   supported.  */
+/* See expr.h.  */
 
 struct type *
-dwarf_expr_context::address_type () const
+address_type (struct gdbarch *gdbarch, int addr_size)
 {
   struct dwarf_gdbarch_types *types
-    = (struct dwarf_gdbarch_types *) gdbarch_data (this->gdbarch,
+    = (struct dwarf_gdbarch_types *) gdbarch_data (gdbarch,
 						   dwarf_arch_cookie);
   int ndx;
 
-  if (this->addr_size == 2)
+  if (addr_size == 2)
     ndx = 0;
-  else if (this->addr_size == 4)
+  else if (addr_size == 4)
     ndx = 1;
-  else if (this->addr_size == 8)
+  else if (addr_size == 8)
     ndx = 2;
   else
     error (_("Unsupported address size in DWARF expressions: %d bits"),
-	   8 * this->addr_size);
+	   8 * addr_size);
 
   if (types->dw_types[ndx] == NULL)
     types->dw_types[ndx]
-      = arch_integer_type (this->gdbarch,
-			   8 * this->addr_size,
+      = arch_integer_type (gdbarch, HOST_CHAR_BIT * addr_size,
 			   0, "<signed DWARF address type>");
 
   return types->dw_types[ndx];
 }
 
+/* Return the type used for DWARF operations where the type is
+   unspecified in the DWARF spec.  Only certain sizes are
+   supported.  */
+
+struct type *
+dwarf_expr_context::address_type () const
+{
+  return ::address_type (this->gdbarch, this->addr_size);
+}
+
 /* Create a new context for the expression evaluator.  */
 
 dwarf_expr_context::dwarf_expr_context (dwarf2_per_objfile *per_objfile,
diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
index 1c06308f99..739731daf8 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -260,6 +260,10 @@ struct dwarf_expr_context
   void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t length);
 };
 
+/* 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.  */
 CORE_ADDR read_addr_from_reg (struct frame_info *, int);
-- 
2.17.1


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

* [PATCH 20/30] Add dwarf_entry factory class to expr.c
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (18 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 19/30] Add new location description access interface Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2021-02-09 19:54   ` Tom Tromey
  2020-12-07 19:00 ` [PATCH 21/30] Change DWARF stack to use new dwarf_entry classes Zoran Zaric
                   ` (10 subsequent siblings)
  30 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

This patch introduces a new class in charge of creating, tracking and
destroying dwarf_entry class based objects.

gdb/ChangeLog:

	* dwarf2/expr.c (ill_formed_expression): New function.
	(value_to_gdb_value): New function.
	(class dwarf_entry_factory): New class.
	(dwarf_entry_factory::~dwarf_entry_factory): New method.
	(dwarf_entry_factory::record_entry): New method.
	(dwarf_entry_factory::create_value): New method.
	(dwarf_entry_factory::create_undefined): New method.
	(dwarf_entry_factory::create_memory): New method.
	(dwarf_entry_factory::create_register): New method.
	(dwarf_entry_factory::create_implicit): New method.
	(dwarf_entry_factory::create_implicit_pointer): New method.
	(dwarf_entry_factory::create_composite): New method.
	(dwarf_entry_factory::entry_to_location): New method.
	(dwarf_entry_factory::entry_to_value): New method.
	(dwarf_entry_factory::value_binary_op): New method.
	(dwarf_entry_factory::value_negation_op): New method.
	(dwarf_entry_factory::value_complement_op): New method.
	(dwarf_entry_factory::value_cast_op): New method.
---
 gdb/dwarf2/expr.c | 316 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 316 insertions(+)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index fea82f8a87..f188240d8b 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -89,6 +89,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
@@ -1011,6 +1019,314 @@ write_to_location (const dwarf_location *location, struct frame_info *frame,
     internal_error (__FILE__, __LINE__, _("invalid location type"));
 }
 
+/* Convert a value entry to the matching struct value representation
+   of a given TYPE.  OFFSET defines the offset into the value
+   contents.
+
+   We only need to support dwarf_value to gdb struct value conversion
+   here so that we can utilize the existing unary and binary operations
+   on struct value's.
+
+   We could implement them for the dwarf_value's but that would lead
+   to code duplication with no real gain at the moment.  */
+
+static struct value *
+value_to_gdb_value (const dwarf_value *value, struct type *type,
+		    LONGEST offset = 0)
+{
+  size_t type_len = TYPE_LENGTH (type);
+
+  if (offset + type_len > TYPE_LENGTH (value->get_type ()))
+    invalid_synthetic_pointer ();
+
+  struct value *retval = allocate_value (type);
+  memcpy (value_contents_raw (retval),
+	  value->get_contents () + offset, type_len);
+
+  return retval;
+}
+
+/* Factory class for creation and lifetime management of all DWARF
+   entries found on a DWARF evaluation stack.  */
+
+class dwarf_entry_factory
+{
+public:
+  dwarf_entry_factory () = default;
+  ~dwarf_entry_factory ();
+
+  /* Create a value entry of a given TYPE and copy a type size number of
+     bytes from the CONTENTS byte stream to the entry.  */
+  dwarf_value *create_value (const gdb_byte* contents, struct type *type);
+
+  /* Creates a value entry of a TYPE type and copies the NUM
+     value to it's contents byte stream.  */
+  dwarf_value *create_value (ULONGEST num, struct type *type);
+
+  /* Create a value entry of TYPE type and copy the NUM value to its
+     contents byte stream.  */
+  dwarf_value *create_value (LONGEST num, struct type *type);
+
+  /* Create an undefined location description entry.  */
+  dwarf_undefined *create_undefined ();
+
+  /* Create a memory location description entry.  */
+  dwarf_memory *create_memory (LONGEST offset, LONGEST bit_suboffset = 0,
+			       bool stack = false);
+
+  /* Create a register location description entry.  */
+  dwarf_register *create_register (unsigned int regnum, LONGEST offset = 0,
+				   LONGEST bit_suboffset = 0);
+
+  /* Create an implicit location description entry and copy SIZE
+     number of bytes from the CONTENTS byte stream to the location.
+     BYTE_ORDER holds the byte order of the location described.  */
+  dwarf_implicit *create_implicit (const gdb_byte *content, size_t size,
+				   enum bfd_endian byte_order);
+
+  /* Create an implicit pointer location description entry.  */
+  dwarf_implicit_pointer *create_implicit_pointer
+    (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);
+
+  /* Create a composite location description entry.  */
+  dwarf_composite *create_composite (LONGEST offset = 0,
+				     LONGEST bit_suboffset = 0);
+
+  /* Convert an entry to a location description entry. If the entry
+     is a location description entry a dynamic cast is applied.
+
+     In a case of a value entry, the value is implicitly
+     converted to a memory location description entry.  */
+  dwarf_location *entry_to_location (dwarf_entry *entry);
+
+  /* Convert an entry to a value entry.  If the entry is a value entry
+     a dynamic cast is applied.
+
+     A location description entry is implicitly converted to a value
+     entry of DEFAULT_TYPE type.
+     Note that only 'memory location description entry' to 'value
+     entry' conversion is currently supported. */
+  dwarf_value *entry_to_value (dwarf_entry *entry, struct type *default_type);
+
+  /* Execute OP operation between ARG1 and ARG2 and return a new value
+     entry containing the result of that operation.  */
+  dwarf_value *value_binary_op (const dwarf_value *arg1,
+				const dwarf_value *arg2, enum exp_opcode op);
+
+  /* Execute a negation operation on ARG and return a new value entry
+     containing the result of that operation.  */
+  dwarf_value *value_negation_op (const dwarf_value *arg);
+
+  /* Execute a complement operation on ARG and return a new value
+     entry containing the result of that operation.  */
+  dwarf_value *value_complement_op (const dwarf_value *arg);
+
+  /* Execute a cast operation on ARG and return a new value entry
+     containing the result of that operation.  */
+  dwarf_value *value_cast_op (const dwarf_value *arg, struct type *type);
+
+private:
+  /* Record entry for garbage collection.  */
+  void record_entry (dwarf_entry *entry);
+
+  /* List of all entries created by the factory.  */
+  std::vector<dwarf_entry *> m_dwarf_entries;
+};
+
+dwarf_entry_factory::~dwarf_entry_factory ()
+{
+  for (unsigned int i = 0; i < m_dwarf_entries.size (); i++)
+    {
+      dwarf_entry* entry = m_dwarf_entries[i];
+
+      entry->decref ();
+
+      if (entry->refcount () == 0)
+	delete entry;
+    }
+}
+
+void
+dwarf_entry_factory::record_entry (dwarf_entry *entry)
+{
+  entry->incref ();
+  m_dwarf_entries.push_back (entry);
+}
+
+dwarf_value *
+dwarf_entry_factory::create_value (const gdb_byte* content, struct type *type)
+{
+  dwarf_value *value = new dwarf_value (content, type);
+  record_entry (value);
+  return value;
+}
+
+dwarf_value *
+dwarf_entry_factory::create_value (ULONGEST num, struct type *type)
+{
+  dwarf_value *value = new dwarf_value (num, type);
+  record_entry (value);
+  return value;
+}
+
+dwarf_value *
+dwarf_entry_factory::create_value (LONGEST num, struct type *type)
+{
+  dwarf_value *value = new dwarf_value (num, type);
+  record_entry (value);
+  return value;
+}
+
+dwarf_undefined *
+dwarf_entry_factory::create_undefined ()
+{
+  dwarf_undefined *undefined_entry = new dwarf_undefined ();
+  record_entry (undefined_entry);
+  return undefined_entry;
+}
+
+dwarf_memory *
+dwarf_entry_factory::create_memory (LONGEST offset, LONGEST bit_suboffset,
+				    bool stack)
+{
+  dwarf_memory *memory_entry
+    = new dwarf_memory (offset, bit_suboffset, stack);
+  record_entry (memory_entry);
+  return memory_entry;
+}
+
+dwarf_register *
+dwarf_entry_factory::create_register (unsigned int regnum, LONGEST offset,
+				      LONGEST bit_suboffset)
+{
+  dwarf_register *register_entry
+    = new dwarf_register (regnum, offset, bit_suboffset);
+  record_entry (register_entry);
+  return register_entry;
+}
+
+dwarf_implicit *
+dwarf_entry_factory::create_implicit (const gdb_byte* content, size_t size,
+				      enum bfd_endian byte_order)
+{
+  dwarf_implicit *implicit_entry
+    = new dwarf_implicit (content, size, byte_order);
+  record_entry (implicit_entry);
+  return implicit_entry;
+}
+
+dwarf_implicit_pointer *
+dwarf_entry_factory::create_implicit_pointer
+  (dwarf2_per_objfile *per_objfile, struct dwarf2_per_cu_data *per_cu,
+   int addr_size, sect_offset die_offset, LONGEST offset,
+   LONGEST bit_suboffset)
+{
+  dwarf_implicit_pointer *implicit_pointer_entry
+    = new dwarf_implicit_pointer (per_objfile, per_cu, addr_size,
+				  die_offset, offset, bit_suboffset);
+  record_entry (implicit_pointer_entry);
+  return implicit_pointer_entry;
+}
+
+dwarf_composite *
+dwarf_entry_factory::create_composite (LONGEST offset, LONGEST bit_suboffset)
+{
+  dwarf_composite *composite_entry
+    = new dwarf_composite (offset, bit_suboffset);
+  record_entry (composite_entry);
+  return composite_entry;
+}
+
+dwarf_location *
+dwarf_entry_factory::entry_to_location (dwarf_entry *entry)
+{
+  /* If the given entry is already a location,
+     just send it back to the caller.  */
+  if (auto location = dynamic_cast<dwarf_location *> (entry))
+    return location;
+
+  auto value = dynamic_cast<dwarf_value *> (entry);
+  gdb_assert (value != nullptr);
+
+  struct type *type = value->get_type ();
+  struct gdbarch *gdbarch = get_type_arch (type);
+  LONGEST offset;
+
+  if (gdbarch_integer_to_address_p (gdbarch))
+    offset = gdbarch_integer_to_address (gdbarch, type,
+					 value->get_contents ());
+
+  offset = extract_unsigned_integer (value->get_contents (),
+				     TYPE_LENGTH (type),
+				     type_byte_order (type));
+
+  return create_memory (offset);
+}
+
+dwarf_value *
+dwarf_entry_factory::entry_to_value (dwarf_entry *entry,
+				     struct type *default_type)
+{
+  /* If the given entry is already a value,
+     just send it back to the caller.  */
+  if (auto value = dynamic_cast<dwarf_value *> (entry))
+    return value;
+
+  auto location = dynamic_cast<dwarf_location *> (entry);
+  gdb_assert (location != nullptr);
+
+  /* We only support memory location to value conversion at this point.
+     It is hard to define how would that conversion work for other
+     location description types.  */
+  if (dynamic_cast<dwarf_memory *> (location) == nullptr)
+    ill_formed_expression ();
+
+  return create_value (location->get_offset (), default_type);
+}
+
+/* We use the existing struct value operations to avoid code
+   duplication.  Vector types are planned to be promoted to base types
+   in the future anyway which means that the subset we actually need
+   from these operations is just going to grow anyway.  */
+
+dwarf_value *
+dwarf_entry_factory::value_binary_op (const dwarf_value *arg1,
+				      const dwarf_value *arg2,
+				      enum exp_opcode op)
+{
+  struct value *arg1_value = value_to_gdb_value (arg1, arg1->get_type ());
+  struct value *arg2_value = value_to_gdb_value (arg2, arg2->get_type ());
+  struct value *result = value_binop (arg1_value, arg2_value, op);
+
+  return create_value (value_contents_raw (result), value_type (result));
+}
+
+dwarf_value *
+dwarf_entry_factory::value_negation_op (const dwarf_value *arg)
+{
+  struct value *result
+    = value_neg (value_to_gdb_value (arg, arg->get_type ()));
+  return create_value (value_contents_raw (result), value_type (result));
+}
+
+dwarf_value *
+dwarf_entry_factory::value_complement_op (const dwarf_value *arg)
+{
+  struct value *result
+    = value_complement (value_to_gdb_value (arg, arg->get_type ()));
+  return create_value (value_contents_raw (result), value_type (result));
+}
+
+dwarf_value *
+dwarf_entry_factory::value_cast_op (const dwarf_value *arg, struct type *type)
+{
+  struct value *result
+    = value_cast (type, value_to_gdb_value (arg, arg->get_type ()));
+  return create_value (value_contents_raw (result), type);
+}
+
 struct piece_closure
 {
   /* Reference count.  */
-- 
2.17.1


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

* [PATCH 21/30] Change DWARF stack to use new dwarf_entry classes
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (19 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 20/30] Add dwarf_entry factory class to expr.c Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2021-02-09 20:11   ` Tom Tromey
  2020-12-07 19:00 ` [PATCH 22/30] Remove dwarf_expr_context from expr.h interface Zoran Zaric
                   ` (9 subsequent siblings)
  30 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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.

Once the conversion support is added, the current DWARF stack element
can easily be swapped out.

gdb/ChangeLog:

	* dwarf2/expr.c (dwarf_value_equal_op): New function.
	(dwarf_value_less_op): New function.
	(struct piece_closure): Change to use dwarf_entry based
	classes.
	(allocate_piece_closure): Change to use dwarf_entry based
	classes.
	(rw_pieced_value): Change to use dwarf_entry based classes.
	(check_pieced_synthetic_pointer): Change to use dwarf_entry
	based classes.
	(check_synthetic_pointer_location): New function.
	(indirect_pieced_value): Change to use dwarf_entry based
	classes.
	(indirect_from_location): New function.
	(coerce_pieced_ref): Change to use dwarf_entry based classes.
	(free_pieced_value_closure): Change to use dwarf_entry based
	classes.
	(dwarf_expr_context::~dwarf_expr_context): Instantiate
	dwarf_entry_factory object.
	(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::dwarf_entry_deref): New method.
	(dwarf_expr_context::gdb_value_to_dwarf_entry): New method.
	(dwarf_expr_context::dwarf_entry_to_gdb_value): New method.
	(dwarf_expr_context::fetch_address): Change to use dwarf_entry
	based classes.
	(dwarf_expr_context::fetch_in_stack_memory): Change to use
	dwarf_entry based classes.
	(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.
	(class dwarf_entry_factory): New declaration.
	(enum dwarf_value_location): Remove enumeration.
	(struct dwarf_expr_piece): Remove structure.
	(struct dwarf_stack_value): Remove structure.
	(struct dwarf_expr_context): Change to use dwarf_entry based
	classes. Add dwarf_entry_factory object.
---
 gdb/dwarf2/expr.c | 1673 ++++++++++++++++++++++-----------------------
 gdb/dwarf2/expr.h |  227 +++---
 2 files changed, 900 insertions(+), 1000 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index f188240d8b..aefbeb3dad 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -1046,6 +1046,30 @@ value_to_gdb_value (const dwarf_value *value, struct type *type,
   return retval;
 }
 
+/* Compare two DWARF value's ARG1 and ARG2 for equality in a context
+   of a value entry comparison.  */
+
+static bool
+dwarf_value_equal_op (dwarf_value *arg1, dwarf_value *arg2)
+{
+  struct value *arg1_value = value_to_gdb_value (arg1, arg1->get_type ());
+  struct value *arg2_value = value_to_gdb_value (arg2, 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 (dwarf_value *arg1, dwarf_value *arg2)
+{
+  struct value *arg1_value = value_to_gdb_value (arg1, arg1->get_type ());
+  struct value *arg2_value = value_to_gdb_value (arg2, arg2->get_type ());
+
+  return value_less (arg1_value, arg2_value);
+}
+
 /* Factory class for creation and lifetime management of all DWARF
    entries found on a DWARF evaluation stack.  */
 
@@ -1338,8 +1362,8 @@ struct piece_closure
   /* 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;
+  /* Location description of this variable.  */
+  dwarf_location *location;
 
   /* Frame ID of frame to which a register value is relative, used
      only by DWARF_VALUE_REGISTER.  */
@@ -1352,7 +1376,7 @@ 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,
+			dwarf_location *location,
 			struct frame_info *frame)
 {
   struct piece_closure *c = new piece_closure;
@@ -1361,16 +1385,13 @@ allocate_piece_closure (dwarf2_per_cu_data *per_cu,
   /* 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);
-
+  location->incref ();
+  c->location = location;
   return c;
 }
 
@@ -1382,310 +1403,189 @@ allocate_piece_closure (dwarf2_per_cu_data *per_cu,
 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
+  LONGEST bit_offset = 0, max_bit_offset;
+  struct piece_closure *closure
     = (struct piece_closure *) value_computed_closure (v);
-  gdb::byte_vector buffer;
-  bool bits_big_endian = type_byte_order (value_type (v)) == BFD_ENDIAN_BIG;
+  bool big_endian = type_byte_order (value_type (v)) == BFD_ENDIAN_BIG;
+  dwarf_entry *entry = closure->location;
+
+  /* Only expect implicit pointer and composite location
+     description here.  */
+  if (entry == nullptr
+      || (dynamic_cast<dwarf_implicit_pointer *> (entry) == nullptr
+          && dynamic_cast<dwarf_composite *> (entry) == nullptr))
+    internal_error (__FILE__, __LINE__, _("invalid location type"));
 
-  if (from != NULL)
-    {
-      from_contents = value_contents (from);
-      v_contents = NULL;
-    }
-  else
+  if (from == NULL)
     {
       if (value_type (v) != value_enclosing_type (v))
-	internal_error (__FILE__, __LINE__,
+        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);
+  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 += (8 * value_offset (value_parent (v))
+      bits_to_skip += (HOST_CHAR_BIT * value_offset (value_parent (v))
 		       + value_bitpos (v));
-      if (from != NULL
-	  && (type_byte_order (value_type (from))
-	      == BFD_ENDIAN_BIG))
+      if (from != NULL && big_endian)
 	{
 	  /* Use the least significant bits of FROM.  */
-	  max_offset = 8 * TYPE_LENGTH (value_type (from));
-	  offset = max_offset - value_bitsize (v);
+	  max_bit_offset = HOST_CHAR_BIT * TYPE_LENGTH (value_type (from));
+	  bit_offset = max_bit_offset - value_bitsize (v);
 	}
       else
-	max_offset = value_bitsize (v);
+	max_bit_offset = value_bitsize (v);
     }
   else
-    max_offset = 8 * TYPE_LENGTH (value_type (v));
+    max_bit_offset = HOST_CHAR_BIT * 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++)
+  /* Reading from and writing to an implicit pointer is not meaningful,
+     so we just skip them here.  */
+  if (dynamic_cast<dwarf_implicit_pointer *> (entry) != nullptr)
     {
-      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;
+      if (from != NULL)
+	mark_value_bits_optimized_out (v, bits_to_skip,
+				       HOST_CHAR_BIT
+				       * TYPE_LENGTH (value_type (v)));
+      return;
+    }
 
-      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;
+  struct frame_info *frame = frame_find_by_id (closure->frame_id);
 
-	    this_size = bits_to_bytes (bits_to_skip, this_size_bits);
-	    buffer.resize (this_size);
+  dwarf_composite *composite_entry = dynamic_cast<dwarf_composite *> (entry);
 
-	    if (from == NULL)
-	      {
-		/* Read mode.  */
-		read_from_register (frame, gdb_regnum, bits_to_skip / 8,
-				    this_size, buffer.data (),
-				    &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,
-					this_size, buffer.data (),
-					&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"));
-		  }
+  /* Advance to the first non-skipped piece.  */
+  unsigned int i;
+  unsigned int pieces_num = composite_entry->get_pieces_num ();
 
-		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,
-				   this_size, buffer.data (),
-				   &optim, &unavail);
-	      }
-	  }
-	  break;
+  for (i = 0; i < pieces_num; i++)
+    {
+      ULONGEST bit_size = composite_entry->get_bit_size_at (i);
 
-	case DWARF_VALUE_MEMORY:
-	  {
-	    bits_to_skip += p->offset;
+      if (bits_to_skip < bit_size)
+	break;
 
-	    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;
+      bits_to_skip -= bit_size;
+    }
 
-	    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);
+  for (; i < pieces_num && bit_offset < max_bit_offset; i++)
+    {
+      const dwarf_location *location = composite_entry->get_piece_at (i);
+      ULONGEST bit_size = composite_entry->get_bit_size_at (i);
+      size_t this_bit_size = bit_size - bits_to_skip;
+      int optimized, unavailable;
 
-		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 (this_bit_size > max_bit_offset - bit_offset)
+	this_bit_size = max_bit_offset - bit_offset;
 
-	    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;
+      if (from == NULL)
+	{
+	  /* Implicit pointers are handled later.  */
+	  if (dynamic_cast<const dwarf_implicit_pointer *>
+		(location) == nullptr)
+	    {
+	      read_from_location (location, frame, bits_to_skip,
+				  value_contents_raw (v), bit_offset,
+				  this_bit_size, bit_size, big_endian,
+				  &optimized, &unavailable);
+
+	      if (optimized)
+		mark_value_bits_optimized_out (v, bit_offset, this_bit_size);
+	      if (unavailable)
+		mark_value_bits_unavailable (v, bit_offset, this_bit_size);
+	    }
+	}
+      else
+	{
+	  write_to_location (location, frame, bits_to_skip,
+			     value_contents (from), bit_offset,
+			     this_bit_size, bit_size, 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"));
+    }
 
-	case DWARF_VALUE_STACK:
-	  {
-	    if (from != NULL)
-	      {
-		mark_value_bits_optimized_out (v, offset, this_size_bits);
-		break;
-	      }
+      bit_offset += this_bit_size;
+      bits_to_skip = 0;
+    }
+}
 
-	    gdbarch *objfile_gdbarch = c->per_objfile->objfile->arch ();
-	    ULONGEST stack_value_size_bits
-	      = 8 * TYPE_LENGTH (value_type (p->v.value));
+static void
+read_pieced_value (struct value *v)
+{
+  rw_pieced_value (v, NULL);
+}
 
-	    /* Use zeroes if piece reaches beyond stack value.  */
-	    if (p->offset + p->size > stack_value_size_bits)
-	      break;
+static void
+write_pieced_value (struct value *to, struct value *from)
+{
+  rw_pieced_value (to, from);
+}
 
-	    /* 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;
+/* Check if a given location contains an implicit pointer
+   location description of a BIT_LENGTH size on a given
+   BIT_OFFSET offset.  */
 
-	    copy_bitwise (v_contents, offset,
-			  value_contents_all (p->v.value),
-			  bits_to_skip,
-			  this_size_bits, bits_big_endian);
-	  }
-	  break;
+static int
+check_synthetic_pointer_location (const dwarf_location *location,
+				  LONGEST bit_offset, int bit_length)
+{
+  if (dynamic_cast<const dwarf_implicit_pointer *> (location) != nullptr)
+    return 1;
 
-	case DWARF_VALUE_LITERAL:
-	  {
-	    if (from != NULL)
-	      {
-		mark_value_bits_optimized_out (v, offset, this_size_bits);
-		break;
-	      }
+  if (auto composite_entry
+	= dynamic_cast<const dwarf_composite *> (location))
+  {
+    /* Advance to the first non-skipped piece.  */
+    unsigned int pieces_num = composite_entry->get_pieces_num ();
+    LONGEST total_bit_offset = bit_offset;
+    LONGEST total_bit_length = bit_length;
 
-	    ULONGEST literal_size_bits = 8 * p->v.literal.length;
-	    size_t n = this_size_bits;
+    total_bit_offset += HOST_CHAR_BIT * composite_entry->get_offset ()
+			+ composite_entry->get_bit_suboffset ();
 
-	    /* 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;
+    for (unsigned int i = 0; i < pieces_num && total_bit_length != 0; i++)
+      {
+	ULONGEST read_bit_length = composite_entry->get_bit_size_at (i);
 
-	    copy_bitwise (v_contents, offset,
-			  p->v.literal.data, bits_to_skip,
-			  n, bits_big_endian);
+	if (total_bit_offset >= read_bit_length)
+	  {
+	     total_bit_offset -= read_bit_length;
+	     continue;
 	  }
-	  break;
 
-	case DWARF_VALUE_IMPLICIT_POINTER:
-	    if (from != NULL)
-	      {
-		mark_value_bits_optimized_out (v, offset, this_size_bits);
-		break;
-	      }
+	read_bit_length -= total_bit_offset;
 
-	  /* These bits show up as zeros -- but do not cause the value to
-	     be considered optimized-out.  */
-	  break;
+	if (total_bit_length < read_bit_length)
+	  read_bit_length = total_bit_length;
 
-	case DWARF_VALUE_OPTIMIZED_OUT:
-	  mark_value_bits_optimized_out (v, offset, this_size_bits);
-	  break;
+	const dwarf_location *piece = composite_entry->get_piece_at (i);
 
-	default:
-	  internal_error (__FILE__, __LINE__, _("invalid location type"));
-	}
+	if (!check_synthetic_pointer_location (piece, total_bit_offset,
+					       read_bit_length))
+	  return 0;
 
-      offset += this_size_bits;
-      bits_to_skip = 0;
-    }
-}
+	total_bit_offset = 0;
+	total_bit_length -= read_bit_length;
+      }
 
-static void
-read_pieced_value (struct value *v)
-{
-  rw_pieced_value (v, NULL);
-}
+    return 1;
+  }
 
-static void
-write_pieced_value (struct value *to, struct value *from)
-{
-  rw_pieced_value (to, from);
+  return 0;
 }
 
 /* An implementation of an lval_funcs method to see whether a value is
@@ -1695,38 +1595,85 @@ 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;
+  LONGEST total_bit_offset = bit_offset + HOST_CHAR_BIT * value_offset (value);
 
-  bit_offset += 8 * value_offset (value);
   if (value_bitsize (value))
-    bit_offset += value_bitpos (value);
+    total_bit_offset += value_bitpos (value);
+
+  struct piece_closure *closure
+    = (struct piece_closure *) value_computed_closure (value);
+  dwarf_location *location = closure->location;
+
+  if (location == nullptr)
+    return 0;
+
+  return check_synthetic_pointer_location (location, total_bit_offset,
+					   bit_length);
+}
 
-  for (i = 0; i < c->pieces.size () && bit_length > 0; i++)
+/* Recursive indirect read from a given LOCATION location description
+   in a given FRAME context, using the TYPE as the type of the pointer.
+   Where INDIRECT_OFFSET is an offset applied to the implicit pointer
+   location description before the indirect read. BIT_OFFSET is an bit
+   offset applied to the LOCATION before the indirection and BIT_LENGTH
+   is a bit length of the LOCATION to be used for the read.
+
+   Read is only performed on the implicit pointer location
+   description parts of the LOCATION.  */
+
+static struct value *
+indirect_from_location (const dwarf_location *location,
+			struct frame_info *frame,
+			struct type *type, LONGEST indirect_offset,
+			LONGEST bit_offset, int bit_length)
+{
+  if (auto pointer_entry
+	= dynamic_cast<const dwarf_implicit_pointer *> (location))
+    {
+      gdb_assert (pointer_entry->get_per_cu () != nullptr);
+
+      LONGEST total_indirect_offset
+	= indirect_offset + pointer_entry->get_offset ();
+
+      return indirect_synthetic_pointer (pointer_entry->get_die_offset (),
+					 total_indirect_offset,
+					 pointer_entry->get_per_cu (),
+					 pointer_entry->get_per_objfile (),
+					 frame, type);
+    }
+  else if (auto composite_entry
+	     = dynamic_cast<const dwarf_composite *> (location))
     {
-      struct dwarf_expr_piece *p = &c->pieces[i];
-      size_t this_size_bits = p->size;
+      /* Advance to the first non-skipped piece.  */
+      unsigned int pieces_num = composite_entry->get_pieces_num ();
+      LONGEST total_bit_offset = bit_offset;
 
-      if (bit_offset > 0)
+      total_bit_offset += HOST_CHAR_BIT * composite_entry->get_offset ()
+			  + composite_entry->get_bit_suboffset ();
+
+      for (unsigned int i = 0; i < pieces_num; i++)
 	{
-	  if (bit_offset >= this_size_bits)
+	  ULONGEST read_bit_length = composite_entry->get_bit_size_at (i);
+
+	  if (total_bit_offset >= read_bit_length)
 	    {
-	      bit_offset -= this_size_bits;
+	      total_bit_offset -= read_bit_length;
 	      continue;
 	    }
 
-	  bit_length -= this_size_bits - bit_offset;
-	  bit_offset = 0;
-	}
-      else
-	bit_length -= this_size_bits;
+	  read_bit_length -= total_bit_offset;
 
-      if (p->location != DWARF_VALUE_IMPLICIT_POINTER)
-	return 0;
+	  if (bit_length < read_bit_length)
+	    read_bit_length = bit_length;
+
+	  const dwarf_location *piece = composite_entry->get_piece_at (i);
+
+	  return indirect_from_location (piece, frame, type, indirect_offset,
+					 total_bit_offset, read_bit_length);
+	}
     }
 
-  return 1;
+  return NULL;
 }
 
 /* An implementation of an lval_funcs method to indirect through a
@@ -1735,56 +1682,26 @@ check_pieced_synthetic_pointer (const struct value *value, LONGEST bit_offset,
 static struct value *
 indirect_pieced_value (struct value *value)
 {
-  struct piece_closure *c
+  struct piece_closure *closure
     = (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));
+
+  struct type *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);
+  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);
 
-  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"));
+  dwarf_location *location = closure->location;
 
-      piece = p;
-      break;
-    }
+  /* Only location descriptions are meaningful here.  */
+  if (location == nullptr)
+    return NULL;
 
-  gdb_assert (piece != NULL && c->per_cu != nullptr);
-  frame = get_selected_frame (_("No frame selected."));
+  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
@@ -1796,14 +1713,13 @@ indirect_pieced_value (struct value *value)
      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;
+  enum bfd_endian byte_order = gdbarch_byte_order (get_frame_arch (frame));
+  LONGEST indirect_offset
+    = extract_signed_integer (value_contents (value),
+			      TYPE_LENGTH (type), byte_order);
 
-  return indirect_synthetic_pointer (piece->v.ptr.die_sect_off,
-				     byte_offset, c->per_cu,
-				     c->per_objfile, frame, type);
+  return indirect_from_location (location, frame, type, indirect_offset,
+				 bit_offset, bit_length);
 }
 
 /* Implementation of the coerce_ref method of lval_funcs for synthetic C++
@@ -1817,20 +1733,23 @@ coerce_pieced_ref (const struct value *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 *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);
+      auto pointer_entry
+	= dynamic_cast<dwarf_implicit_pointer *> (closure->location);
 
-      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);
+      /* Only implicit pointer location description is meaningful here.  */
+      if (pointer_entry == nullptr)
+	return NULL;
+
+      return indirect_synthetic_pointer (pointer_entry->get_die_offset (),
+					 pointer_entry->get_offset (),
+					 closure->per_cu,
+					 closure->per_objfile,
+					 frame, type);
     }
   else
     {
@@ -1858,10 +1777,7 @@ free_pieced_value_closure (struct value *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);
-
+      c->location->decref ();
       delete c;
     }
 }
@@ -1951,34 +1867,38 @@ dwarf_expr_context::dwarf_expr_context (dwarf2_per_objfile *per_objfile,
   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),
   frame (nullptr),
   per_cu (nullptr),
   addr_info (nullptr)
 {
+  entry_factory = new dwarf_entry_factory ();
 }
 
-/* Push VALUE onto the stack.  */
+/* See expr.h.  */
+
+dwarf_expr_context::~dwarf_expr_context ()
+{
+  delete entry_factory;
+}
+
+/* See expr.h.  */
 
 void
-dwarf_expr_context::push (struct value *value, bool in_stack_memory)
+dwarf_expr_context::push (dwarf_entry *entry)
 {
-  stack.emplace_back (value, in_stack_memory);
+  stack.emplace_back (entry);
 }
 
-/* Push VALUE onto the stack.  */
+/* See expr.h.  */
 
 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 (entry_factory->create_memory (addr, 0, in_stack_memory));
 }
 
-/* Pop the top item off of the stack.  */
+/* See expr.h.  */
 
 void
 dwarf_expr_context::pop ()
@@ -1989,16 +1909,16 @@ dwarf_expr_context::pop ()
   stack.pop_back ();
 }
 
-/* Retrieve the N'th item on the stack.  */
+/* See expr.h.  */
 
-struct value *
+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.  */
@@ -2075,31 +1995,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,
@@ -2157,180 +2052,284 @@ 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)
+  dwarf_entry *entry = fetch (0);
+
+  if (!as_lval)
+    entry = entry_factory->entry_to_value (entry, address_type ());
+   else
+    entry = entry_factory->entry_to_location (entry);
+
+  return dwarf_entry_to_gdb_value (entry, type, subobj_type, subobj_offset);
+}
+
+/* See expr.h.  */
+
+struct value *
+dwarf_expr_context::eval_exp (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,
+			      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, as_lval);
+}
+
+/* See expr.h.  */
+
+dwarf_entry *
+dwarf_expr_context::dwarf_entry_deref (dwarf_entry *entry,
+				       struct type *type, size_t size)
+{
+  bool big_endian = type_byte_order (type) == BFD_ENDIAN_BIG;
+  dwarf_location *location = entry_factory->entry_to_location (entry);
+  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);
+  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;
+
+  if (auto memory_entry = dynamic_cast<dwarf_memory *> (location))
     {
-      struct piece_closure *c;
-      ULONGEST bit_size = 0;
+      /* 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.  */
+      CORE_ADDR start_address = memory_entry->get_offset ();
+      LONGEST bit_suboffset = location->get_bit_suboffset ();
+      LONGEST this_size = bits_to_bytes (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 (!memory_entry->in_stack () && this_size && addr_info != nullptr)
+	{
+	  CORE_ADDR offset = start_address - addr_info->addr;
 
-      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 ();
+	  /* Using second buffer here because the copy_bitwise
+	     doesn't support in place copy.  */
+	  gdb::byte_vector temp_buf (this_size);
 
-      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);
+	  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 (),
+			    bit_suboffset, size_in_bits,
+			    big_endian);
+	      passed_in_buf = true;
+	    }
+	}
     }
-  else
+
+  if (!passed_in_buf)
     {
-      /* 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;
+      int optimized, unavailable;
+
+      read_from_location (location, this->frame, 0, buf_ptr, 0, size_in_bits,
+			  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"));
+    }
 
-      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);
+  return entry_factory->create_value (read_buf.data (), type);
+}
 
-	    if (subobj_offset != 0)
-	      error (_("cannot use offset on synthetic pointer to register"));
+/* See expr.h.  */
 
-	    gdb_assert (this->frame != NULL);
+dwarf_entry *
+dwarf_expr_context::gdb_value_to_dwarf_entry (struct value *value)
+{
+  if (value_optimized_out (value))
+    return entry_factory->create_undefined ();
 
-	    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;
+  LONGEST offset = value_offset (value);
 
-	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));
+  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;
+	struct type *type = value_type (value);
 
-	    retval = value_at_lazy (subobj_type,
-				    address + subobj_offset);
-	    if (in_stack_memory)
-	      set_value_stack (retval, 1);
-	  }
-	  break;
+	return entry_factory->create_implicit (contents_start,
+					       TYPE_LENGTH (type),
+					       type_byte_order (type));
+      }
+    case lval_memory:
+      return entry_factory->create_memory (value_address (value) + offset,
+					   0, value_stack (value));
+    case lval_register:
+      return entry_factory->create_register (VALUE_REGNUM (value), offset);
+    case lval_computed:
+      {
+	/* Dwarf entry is enclosed by the closure anyway so we just
+	   need to unwrap it here.  */
+	struct piece_closure *closure
+	  = (struct piece_closure *) value_computed_closure (value);
+	auto location = dynamic_cast<dwarf_location *> (closure->location);
 
-	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 (location == nullptr)
+	  internal_error (__FILE__, __LINE__, _("invalid closure type"));
 
-	    if (subobj_offset + len > max)
-	      invalid_synthetic_pointer ();
+	location->add_bit_offset (offset * HOST_CHAR_BIT);
+	return location;
+      }
+    default:
+      internal_error (__FILE__, __LINE__, _("invalid location type"));
+  }
+}
 
-	    retval = allocate_value (subobj_type);
+/* See expr.h.  */
 
-	    /* The given offset is relative to the actual object.  */
-	    if (gdbarch_byte_order (this->gdbarch) == BFD_ENDIAN_BIG)
-	      subobj_offset += n - max;
+struct value *
+dwarf_expr_context::dwarf_entry_to_gdb_value (dwarf_entry *entry,
+					      struct type *type,
+					      struct type *subobj_type,
+					      LONGEST subobj_offset)
+{
+  struct gdbarch *gdbarch = get_type_arch (type);
+  struct value *retval = nullptr;
 
-	    memcpy (value_contents_raw (retval),
-		    value_contents_all (value) + subobj_offset, len);
-	  }
-	  break;
+  if (subobj_type == nullptr)
+    subobj_type = type;
 
-	case DWARF_VALUE_LITERAL:
-	  {
-	    bfd_byte *contents;
-	    size_t n = TYPE_LENGTH (subobj_type);
+  if (auto value = dynamic_cast<dwarf_value *> (entry))
+    retval = value_to_gdb_value (value, subobj_type, subobj_offset);
+  else if (dynamic_cast<dwarf_undefined *> (entry) != nullptr)
+    {
+      retval = allocate_value (subobj_type);
+      mark_value_bytes_optimized_out (retval, subobj_offset,
+				      TYPE_LENGTH (subobj_type));
+    }
+  else if (auto memory_entry = dynamic_cast<dwarf_memory *> (entry))
+    {
+      struct type *ptr_type = builtin_type (gdbarch)->builtin_data_ptr;
+      CORE_ADDR address = memory_entry->get_offset ();
 
-	    if (subobj_offset + n > this->len)
-	      invalid_synthetic_pointer ();
+      if (subobj_type->code () == TYPE_CODE_FUNC
+	  || subobj_type->code () == TYPE_CODE_METHOD)
+	ptr_type = builtin_type (gdbarch)->builtin_func_ptr;
 
-	    retval = allocate_value (subobj_type);
-	    contents = value_contents_raw (retval);
-	    memcpy (contents, this->data + subobj_offset, n);
-	  }
-	  break;
+      address = value_as_address (value_from_pointer (ptr_type, address));
+      retval = value_at_lazy (subobj_type, address + subobj_offset);
+      set_value_stack (retval, memory_entry->in_stack ());
+    }
+  else if (auto register_entry = dynamic_cast<dwarf_register *> (entry))
+    {
+      unsigned int regnum = register_entry->get_regnum ();
+      int gdb_regnum = dwarf_reg_to_regnum_or_error (gdbarch, regnum);
 
-	case DWARF_VALUE_OPTIMIZED_OUT:
-	  retval = allocate_optimized_out_value (subobj_type);
-	  break;
+      /* Construct the value.  */
+      retval = gdbarch_value_from_register (gdbarch, type,
+					    gdb_regnum, get_frame_id (frame));
 
-	  /* 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"));
+      /* 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;
 	}
     }
+  else if (auto implicit_entry = dynamic_cast<dwarf_implicit *> (entry))
+    {
+      size_t subtype_len = TYPE_LENGTH (subobj_type);
+      size_t type_len = TYPE_LENGTH (type);
 
-  set_value_initialized (retval, this->initialized);
+      /* 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)
+	  && implicit_entry->get_byte_order() != BFD_ENDIAN_UNKNOWN)
+	  invalid_synthetic_pointer ();
 
-  return retval;
-}
+      retval = allocate_value (subobj_type);
 
-/* See expr.h.  */
+      /* The given offset is relative to the actual object.  */
+      if (implicit_entry->get_byte_order() == BFD_ENDIAN_BIG)
+	subobj_offset += implicit_entry->get_size () - type_len;
 
-struct value *
-dwarf_expr_context::eval_exp (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,
-			      struct type *type,
-			      struct type *subobj_type,
-			      LONGEST subobj_offset)
-{
-  this->per_cu = per_cu;
-  this->frame = frame;
-  this->addr_info = addr_info;
+      memcpy ((void *)value_contents_raw (retval),
+	      (void *)(implicit_entry->get_contents () + subobj_offset),
+	      subtype_len);
+    }
+  else if (auto implicit_pointer_entry
+	    = dynamic_cast<dwarf_implicit_pointer *> (entry))
+    {
+      struct piece_closure *closure
+	= allocate_piece_closure (this->per_cu, this->per_objfile,
+				  implicit_pointer_entry, this->frame);
 
-  if (per_cu != nullptr)
-    this->ref_addr_size = per_cu->ref_addr_size ();
+      retval
+	= allocate_computed_value (subobj_type, &pieced_value_funcs, closure);
+      set_value_offset (retval, subobj_offset);
+    }
+  else if (auto composite_entry = dynamic_cast<dwarf_composite *> (entry))
+    {
+      size_t pieces_num = composite_entry->get_pieces_num ();
+      ULONGEST bit_size = 0;
 
-  eval (addr, len);
-  return fetch_result (type, subobj_type, subobj_offset, as_lval);
+      for (unsigned int i = 0; i < pieces_num; i++)
+	bit_size += composite_entry->get_bit_size_at (i);
+
+      /* 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 ();
+
+      struct piece_closure *closure
+	= allocate_piece_closure (this->per_cu, this->per_objfile,
+				  composite_entry, this->frame);
+
+      retval
+	= allocate_computed_value (subobj_type, &pieced_value_funcs, closure);
+      set_value_offset (retval, subobj_offset);
+  }
+
+  return retval;
 }
 
 /* Require that TYPE be an integral type; throw an exception if not.  */
@@ -2388,52 +2387,7 @@ 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.  */
+/* See expr.h.  */
 
 bool
 dwarf_expr_context::stack_empty_p () const
@@ -2441,50 +2395,41 @@ 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 ();
+/* See expr.h.  */
 
-  p.location = this->location;
-  p.size = size;
-  p.offset = offset;
+dwarf_entry *
+dwarf_expr_context::add_piece (ULONGEST bit_size, ULONGEST bit_offset)
+{
+  dwarf_location *piece_entry;
+  dwarf_composite *composite_entry;
 
-  if (p.location == DWARF_VALUE_LITERAL)
-    {
-      p.v.literal.data = this->data;
-      p.v.literal.length = this->len;
-    }
-  else if (stack_empty_p ())
+  if (!stack_empty_p ()
+      && dynamic_cast<dwarf_composite *> (fetch (0)) == nullptr)
     {
-      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;
+      piece_entry = entry_factory->entry_to_location (fetch (0));
+      pop ();
     }
-  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)
-    {
-      p.v.ptr.die_sect_off = (sect_offset) this->len;
-      p.v.ptr.offset = value_as_long (fetch (0));
-    }
-  else if (p.location == DWARF_VALUE_REGISTER)
-    p.v.regno = value_as_long (fetch (0));
+  else
+    piece_entry = entry_factory->create_undefined ();
+
+  piece_entry->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 ()
+      || dynamic_cast<dwarf_composite *> (fetch (0)) == nullptr)
+    composite_entry = entry_factory->create_composite ();
   else
     {
-      p.v.value = fetch (0);
+      composite_entry = dynamic_cast<dwarf_composite *> (fetch (0));
+      pop ();
     }
+
+  composite_entry->add_piece (piece_entry, bit_size);
+  return composite_entry;
 }
 
-/* Evaluate the expression at ADDR (LEN bytes long).  */
+/* See expr.h.  */
 
 void
 dwarf_expr_context::eval (const gdb_byte *addr, size_t len)
@@ -2498,7 +2443,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,
@@ -2510,7 +2455,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,
@@ -2530,11 +2475,8 @@ 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
-   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,
@@ -2562,8 +2504,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)
@@ -2603,10 +2544,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,
@@ -2664,8 +2602,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,
@@ -2690,9 +2627,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,
@@ -2732,8 +2667,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,
@@ -2749,9 +2683,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);
@@ -2761,16 +2692,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;
+      struct dwarf_entry *result_entry = NULL;
 
       /* The DWARF expression might have a bug causing an infinite
 	 loop.  In that case, quitting is the only way out.  */
@@ -2811,7 +2735,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 = entry_factory->create_value (result, address_type);
 	  break;
 
 	case DW_OP_addr:
@@ -2823,8 +2747,14 @@ 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 = entry_factory->create_memory (result);
+	    }
+	  else
+	    /* This is a special case where the value is expected to be
+	       created instead of memory location.  */
+	    result_entry = entry_factory->create_value (result, address_type);
 	  break;
 
 	case DW_OP_addrx:
@@ -2835,7 +2765,7 @@ 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 = entry_factory->create_memory (result);
 	  break;
 	case DW_OP_GNU_const_index:
 	  ensure_have_per_cu (per_cu, "DW_OP_GNU_const_index");
@@ -2843,58 +2773,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 = entry_factory->create_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 = entry_factory->create_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 = entry_factory->create_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 = entry_factory->create_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 = entry_factory->create_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 = entry_factory->create_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 = entry_factory->create_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 = entry_factory->create_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 = entry_factory->create_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 = entry_factory->create_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 = entry_factory->create_value (result, address_type);
 	  break;
 
 	/* The DW_OP_reg operations are required to occur alone in
@@ -2934,8 +2864,7 @@ 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 = entry_factory->create_register (result);
 	  break;
 
 	case DW_OP_regx:
@@ -2943,8 +2872,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 = entry_factory->create_register (reg);
 	  break;
 
 	case DW_OP_implicit_value:
@@ -2954,19 +2882,30 @@ 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 = entry_factory->create_implicit (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;
+	  {
+	    dwarf_value *value
+	      = entry_factory->entry_to_value (fetch (0), address_type);
+	    pop ();
+
+	    struct type* type = value->get_type ();
+
+	    result_entry
+	      = entry_factory->create_implicit (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:
@@ -2975,16 +2914,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);
+	    result_entry
+	      = entry_factory->create_implicit_pointer (this->per_objfile,
+							this->per_cu,
+							this->addr_size,
+							die_offset, len);
 
-	    this->location = DWARF_VALUE_IMPLICIT_POINTER;
 	    dwarf_expr_require_composition (op_ptr, op_end,
 					    "DW_OP_implicit_pointer");
 	  }
@@ -3026,9 +2969,16 @@ 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);
+	    dwarf_location *location = entry_factory->create_register (reg);
+	    result_entry
+	      = dwarf_entry_deref (location, address_type, reg_size);
+	    location = entry_factory->entry_to_location (result_entry);
+	    location->add_bit_offset (offset * HOST_CHAR_BIT);
+	    result_entry = location;
 	  }
 	  break;
 	case DW_OP_bregx:
@@ -3037,51 +2987,56 @@ 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);
+	    dwarf_location *location = entry_factory->create_register (reg);
+	    result_entry
+	      = dwarf_entry_deref (location, address_type, reg_size);
+	    location = entry_factory->entry_to_location (result_entry);
+	    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<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);
+
+	    if (dynamic_cast<dwarf_register *> (result_entry) != nullptr)
+	      result_entry = dwarf_entry_deref (result_entry, address_type);
+
+	    result_entry = entry_factory->entry_to_location (result_entry);
+	    auto memory_entry = dynamic_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_entry == nullptr)
+	      ill_formed_expression ();
+
+	    memory_entry->add_bit_offset (offset * HOST_CHAR_BIT);
+	    memory_entry->set_stack (true);
+	    result_entry = memory_entry;
 
 	    /* 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:
@@ -3090,8 +3045,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:
@@ -3101,15 +3055,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);
+	    dwarf_entry *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:
@@ -3119,7 +3072,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];
+	    dwarf_entry *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;
@@ -3132,38 +3085,20 @@ 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;
+	    result_entry = dwarf_entry_deref (fetch (0), type, addr_size);
+	    pop ();
 	  }
+	  break;
 
 	case DW_OP_abs:
 	case DW_OP_neg:
@@ -3171,31 +3106,37 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	case DW_OP_plus_uconst:
 	  {
 	    /* Unary operations.  */
-	    result_val = fetch (0);
+	    dwarf_value *arg
+	      = entry_factory->entry_to_value (fetch (0), 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
+		    = value_to_gdb_value (arg, arg->get_type ());
+
+		  if (value_less (arg_value,
+				  value_zero (arg->get_type (), not_lval)))
+		    arg = entry_factory->value_negation_op (arg);
+		}
 		break;
 	      case DW_OP_neg:
-		result_val = value_neg (result_val);
+		arg = entry_factory->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 = entry_factory->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 = entry_factory->create_value (result, address_type);
 		break;
 	      }
+	    result_entry = arg;
 	  }
 	  break;
 
@@ -3218,34 +3159,39 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	case DW_OP_ne:
 	  {
 	    /* Binary operations.  */
-	    struct value *first, *second;
-
-	    second = fetch (0);
+	    dwarf_value *arg2
+	      = entry_factory->entry_to_value (fetch (0), address_type);
 	    pop ();
 
-	    first = fetch (0);
+	    dwarf_value *arg1
+	      = entry_factory->entry_to_value (fetch (0), 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"));
 
+	    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 = entry_factory->value_binary_op (arg1, arg2,
+							    BINOP_BITWISE_AND);
 		break;
 	      case DW_OP_div:
-		result_val = value_binop (first, second, BINOP_DIV);
+		op_result
+		  = entry_factory->value_binary_op (arg1, arg2, BINOP_DIV);
 		break;
 	      case DW_OP_minus:
-		result_val = value_binop (first, second, BINOP_SUB);
+		op_result
+		  = entry_factory->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
@@ -3256,102 +3202,113 @@ 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 = entry_factory->value_cast_op (arg1, utype);
+		      arg2 = entry_factory->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
+		    = entry_factory->value_binary_op (arg1, arg2, BINOP_MOD);
 		  if (cast_back)
-		    result_val = value_cast (orig_type, result_val);
+		    op_result
+		      = entry_factory->value_cast_op (op_result, orig_type);
 		}
 		break;
 	      case DW_OP_mul:
-		result_val = value_binop (first, second, BINOP_MUL);
+		op_result
+		  = entry_factory->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 = entry_factory->value_binary_op (arg1, arg2,
+							    BINOP_BITWISE_IOR);
 		break;
 	      case DW_OP_plus:
-		result_val = value_binop (first, second, BINOP_ADD);
+		op_result
+		  = entry_factory->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
+		  = entry_factory->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 = entry_factory->value_cast_op (arg1, utype);
 		  }
 
-		result_val = value_binop (first, second, BINOP_RSH);
+		op_result
+		  = entry_factory->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 = entry_factory->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 = entry_factory->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
+		  = entry_factory->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 = entry_factory->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 = entry_factory->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 = entry_factory->create_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 = entry_factory->create_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 = entry_factory->create_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 = entry_factory->create_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 = entry_factory->create_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 = entry_factory->create_value (result, address_type);
 		break;
 	      default:
 		internal_error (__FILE__, __LINE__,
 				_("Can't be reached."));
 	      }
+	    result_entry = op_result;
 	  }
 	  break;
 
@@ -3359,8 +3316,7 @@ 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 = entry_factory->create_memory (result, 0, true);
 	  break;
 
 	case DW_OP_GNU_push_tls_address:
@@ -3373,11 +3329,13 @@ 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
+	    = entry_factory->entry_to_value (fetch (0),
+					     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 = entry_factory->create_memory (result);
 	  break;
 
 	case DW_OP_skip:
@@ -3388,13 +3346,13 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 
 	case DW_OP_bra:
 	  {
-	    struct value *val;
+	    dwarf_value *dwarf_value
+	      = entry_factory->entry_to_value (fetch (0), 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 ();
 	  }
@@ -3409,16 +3367,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:
 	  {
@@ -3427,23 +3378,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 = dynamic_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:
@@ -3473,9 +3425,16 @@ 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 (value);
+
+	    if (dynamic_cast<dwarf_undefined *> (result_entry) != nullptr)
+	      error_value_optimized_out ();
+	    else
+	      result_entry = dwarf_entry_deref (result_entry, address_type);
 	  }
 	  break;
 	
@@ -3483,7 +3442,6 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	case DW_OP_GNU_entry_value:
 	  {
 	    uint64_t len;
-	    CORE_ADDR deref_size;
 	    union call_site_parameter_u kind_u;
 
 	    op_ptr = safe_read_uleb128 (op_ptr, op_end, &len);
@@ -3500,6 +3458,8 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 		goto no_push;
 	      }
 
+	    CORE_ADDR deref_size;
+
 	    kind_u.dwarf_reg = dwarf_block_to_dwarf_reg_deref (op_ptr,
 							       op_ptr + len,
 							       &deref_size);
@@ -3534,19 +3494,16 @@ 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
+	      = entry_factory->create_value (data, type);
 	  }
 	  break;
 
@@ -3558,12 +3515,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);
+
+	    dwarf_register *register_descr
+	      = entry_factory->create_register (reg);
+	    result_entry = dwarf_entry_deref (register_descr, type);
 	  }
 	  break;
 
@@ -3572,42 +3528,45 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	case DW_OP_reinterpret:
 	case DW_OP_GNU_reinterpret:
 	  {
-	    struct type *type;
+	    dwarf_value *dwarf_value
+	      = entry_factory->entry_to_value (fetch (0), 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))
+	      dwarf_value = entry_factory->value_cast_op (dwarf_value, type);
+	    else if (type == dwarf_value->get_type ())
 	      {
 		/* Nothing.  */
 	      }
 	    else if (TYPE_LENGTH (type)
-		     != TYPE_LENGTH (value_type (result_val)))
+		     != TYPE_LENGTH (dwarf_value->get_type ()))
 	      error (_("DW_OP_reinterpret has wrong size"));
 	    else
-	      result_val
-		= value_from_contents (type,
-				       value_contents_all (result_val));
+	      dwarf_value
+		= entry_factory->create_value (dwarf_value->get_contents (),
+					       type);
+	    result_entry = dwarf_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
+	    = entry_factory->create_memory (this->addr_info->addr);
 	  break;
 
 	default:
@@ -3615,18 +3574,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 739731daf8..19c28754bb 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -25,109 +25,28 @@
 #include "leb128.h"
 #include "gdbtypes.h"
 
+class dwarf_entry;
+class dwarf_entry_factory;
 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 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.  */
 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.  */
   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);
+  /* Destroy dwarf entry factory object.  */
+  virtual ~dwarf_expr_context ();
+
+  /* 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
@@ -146,7 +65,7 @@ struct dwarf_expr_context
 
 private:
   /* The stack of values.  */
-  std::vector<dwarf_stack_value> stack;
+  std::vector<dwarf_entry *> stack;
 
   /* Target architecture to use for address operations.  */
   struct gdbarch *gdbarch;
@@ -154,8 +73,9 @@ struct dwarf_expr_context
   /* 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.  */
+  /* 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;
 
   /* The current depth of dwarf expression recursion, via DW_OP_call*,
@@ -163,43 +83,6 @@ struct dwarf_expr_context
      depth we'll tolerate before raising an error.  */
   int recursion_depth, max_recursion_depth;
 
-  /* Location of the value.  */
-  enum dwarf_value_location location;
-
-  /* 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;
-
-  /* Initialization status of variable: Non-zero if variable has been
-     initialized; zero otherwise.  */
-  int initialized;
-
-  /* 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;
 
@@ -212,16 +95,40 @@ struct dwarf_expr_context
   /* Property address info used for the evaluation.  */
   const struct property_addr_info *addr_info;
 
+  /* Factory in charge of the dwarf entry's life cycle.  */
+  dwarf_entry_factory *entry_factory;
+
+  /* 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 (struct value *value, bool in_stack_memory);
+
+  /* Push ENTRY onto the stack.  */
+  void push (dwarf_entry *value);
+
+  /* Return true if the expression stack is empty.  */
   bool stack_empty_p () const;
-  void add_piece (ULONGEST size, ULONGEST offset);
+
+  /* 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.  */
+  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 ();
-  struct value *fetch (int n);
-  CORE_ADDR fetch_address (int n);
-  bool fetch_in_stack_memory (int n);
+
+  /* Retrieve the N'th item on the stack.  */
+  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
@@ -251,13 +158,36 @@ struct dwarf_expr_context
   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.  */
+     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);
-  void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t length);
+
+  /* Apply dereference operation on the DWARF ENTRY.  In the case of a
+     value entry, the entry will be implicitly converted to the
+     appropriate location description before the operation is applied.
+     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.  */
+  dwarf_entry* dwarf_entry_deref (dwarf_entry *entry, struct type *type,
+				  size_t size = 0);
+
+  /* Convert struct value to the matching DWARF entry representation.
+     Used for non-standard DW_OP_GNU_variable_value operation
+     support.  */
+  dwarf_entry *gdb_value_to_dwarf_entry (struct value *value);
+
+  /* Convert DWARF entry to the matching struct value representation
+     of the given TYPE type. 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.  */
+  struct value *dwarf_entry_to_gdb_value (dwarf_entry *entry,
+					  struct type *type,
+					  struct type *subobj_type = nullptr,
+					  LONGEST subobj_offset = 0);
 };
 
 /* Return the address type used of the GDBARCH architecture and
@@ -268,18 +198,32 @@ struct type *address_type (struct gdbarch *gdbarch, int addr_size);
    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);
@@ -319,14 +263,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 sleb128 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] 75+ messages in thread

* [PATCH 22/30] Remove dwarf_expr_context from expr.h interface
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (20 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 21/30] Change DWARF stack to use new dwarf_entry classes Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2020-12-07 19:00 ` [PATCH 23/30] Rename and update the piece_closure structure Zoran Zaric
                   ` (8 subsequent siblings)
  30 siblings, 0 replies; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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_eval_exp
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_eval_exp): 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.
	* dwarf2/frame.c (execute_stack_op): Now calls dwarf2_eval_exp.
	* dwarf2/loc.c (dwarf2_evaluate_loc_desc_full): Now calls
	dwarf2_eval_exp.
	(dwarf2_locexpr_baton_eval): Now calls dwarf2_eval_exp.
---
 gdb/dwarf2/expr.c  | 226 ++++++++++++++++++++++++++++++++++++---------
 gdb/dwarf2/expr.h  | 181 ++++--------------------------------
 gdb/dwarf2/frame.c |  14 ++-
 gdb/dwarf2/loc.c   |  23 +++--
 4 files changed, 227 insertions(+), 217 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index aefbeb3dad..9ed0ad8baf 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -1820,7 +1820,165 @@ sect_variable_value (sect_offset sect_off,
 				     type, true);
 }
 
-/* See expr.h.  */
+/* 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);
+
+  /* Destroy dwarf entry factory object.  */
+  virtual ~dwarf_expr_context ();
+
+  /* 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 *eval_exp (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 values.  */
+  std::vector<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;
+
+  /* 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;
+
+  /* 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;
+
+  /* Compilation unit used for the evaluation.  */
+  struct dwarf2_per_cu_data *per_cu;
+
+  /* Property address info used for the evaluation.  */
+  const struct property_addr_info *addr_info;
+
+  /* Factory in charge of the dwarf entry's life cycle.  */
+  dwarf_entry_factory *entry_factory;
+
+  /* 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 (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.  */
+  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.  */
+  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);
+
+  /* Apply dereference operation on the DWARF ENTRY.  In the case of a
+     value entry, the entry will be implicitly converted to the
+     appropriate location description before the operation is applied.
+     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.  */
+  dwarf_entry* dwarf_entry_deref (dwarf_entry *entry, struct type *type,
+				  size_t size = 0);
+
+  /* Convert struct value to the matching DWARF entry representation.
+     Used for non-standard DW_OP_GNU_variable_value operation
+     support.  */
+  dwarf_entry *gdb_value_to_dwarf_entry (struct value *value);
+
+  /* Convert DWARF entry to the matching struct value representation
+     of the given TYPE type. 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.  */
+  struct value *dwarf_entry_to_gdb_value (dwarf_entry *entry,
+					  struct type *type,
+					  struct type *subobj_type = nullptr,
+					  LONGEST subobj_offset = 0);
+};
 
 struct type *
 address_type (struct gdbarch *gdbarch, int addr_size)
@@ -1875,31 +2033,17 @@ dwarf_expr_context::dwarf_expr_context (dwarf2_per_objfile *per_objfile,
   entry_factory = new dwarf_entry_factory ();
 }
 
-/* See expr.h.  */
-
 dwarf_expr_context::~dwarf_expr_context ()
 {
   delete entry_factory;
 }
 
-/* See expr.h.  */
-
 void
 dwarf_expr_context::push (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 (entry_factory->create_memory (addr, 0, in_stack_memory));
-}
-
-/* See expr.h.  */
-
 void
 dwarf_expr_context::pop ()
 {
@@ -1909,8 +2053,6 @@ dwarf_expr_context::pop ()
   stack.pop_back ();
 }
 
-/* See expr.h.  */
-
 dwarf_entry *
 dwarf_expr_context::fetch (int n)
 {
@@ -1921,8 +2063,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)
@@ -1949,8 +2089,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)
 {
@@ -1968,8 +2106,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)
 {
@@ -1993,8 +2129,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,
@@ -2044,8 +2178,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,
@@ -2068,12 +2200,11 @@ dwarf_expr_context::fetch_result (struct type *type,
   return dwarf_entry_to_gdb_value (entry, type, subobj_type, subobj_offset);
 }
 
-/* See expr.h.  */
-
 struct value *
 dwarf_expr_context::eval_exp (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,
@@ -2086,12 +2217,14 @@ dwarf_expr_context::eval_exp (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 ((*init_values)[i]));
+
   eval (addr, len);
   return fetch_result (type, subobj_type, subobj_offset, as_lval);
 }
 
-/* See expr.h.  */
-
 dwarf_entry *
 dwarf_expr_context::dwarf_entry_deref (dwarf_entry *entry,
 				       struct type *type, size_t size)
@@ -2167,8 +2300,6 @@ dwarf_expr_context::dwarf_entry_deref (dwarf_entry *entry,
   return entry_factory->create_value (read_buf.data (), type);
 }
 
-/* See expr.h.  */
-
 dwarf_entry *
 dwarf_expr_context::gdb_value_to_dwarf_entry (struct value *value)
 {
@@ -2215,8 +2346,6 @@ dwarf_expr_context::gdb_value_to_dwarf_entry (struct value *value)
   }
 }
 
-/* See expr.h.  */
-
 struct value *
 dwarf_expr_context::dwarf_entry_to_gdb_value (dwarf_entry *entry,
 					      struct type *type,
@@ -2387,16 +2516,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.  */
-
 dwarf_entry *
 dwarf_expr_context::add_piece (ULONGEST bit_size, ULONGEST bit_offset)
 {
@@ -2429,8 +2554,6 @@ dwarf_expr_context::add_piece (ULONGEST bit_size, ULONGEST bit_offset)
   return composite_entry;
 }
 
-/* See expr.h.  */
-
 void
 dwarf_expr_context::eval (const gdb_byte *addr, size_t len)
 {
@@ -2667,8 +2790,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)
@@ -3584,6 +3705,25 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
   gdb_assert (this->recursion_depth >= 0);
 }
 
+/* See expr.h.  */
+
+struct value *
+dwarf2_eval_exp (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.eval_exp (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 19c28754bb..5fb4b0857e 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -25,170 +25,27 @@
 #include "leb128.h"
 #include "gdbtypes.h"
 
-class dwarf_entry;
-class dwarf_entry_factory;
 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);
-
-  /* Destroy dwarf entry factory object.  */
-  virtual ~dwarf_expr_context ();
-
-  /* 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 *eval_exp (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 values.  */
-  std::vector<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;
-
-  /* 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;
-
-  /* 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;
-
-  /* Compilation unit used for the evaluation.  */
-  struct dwarf2_per_cu_data *per_cu;
-
-  /* Property address info used for the evaluation.  */
-  const struct property_addr_info *addr_info;
-
-  /* Factory in charge of the dwarf entry's life cycle.  */
-  dwarf_entry_factory *entry_factory;
-
-  /* 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 (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.  */
-  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.  */
-  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);
-
-  /* Apply dereference operation on the DWARF ENTRY.  In the case of a
-     value entry, the entry will be implicitly converted to the
-     appropriate location description before the operation is applied.
-     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.  */
-  dwarf_entry* dwarf_entry_deref (dwarf_entry *entry, struct type *type,
-				  size_t size = 0);
-
-  /* Convert struct value to the matching DWARF entry representation.
-     Used for non-standard DW_OP_GNU_variable_value operation
-     support.  */
-  dwarf_entry *gdb_value_to_dwarf_entry (struct value *value);
-
-  /* Convert DWARF entry to the matching struct value representation
-     of the given TYPE type. 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.  */
-  struct value *dwarf_entry_to_gdb_value (dwarf_entry *entry,
-					  struct type *type,
-					  struct type *subobj_type = nullptr,
-					  LONGEST subobj_offset = 0);
-};
+/* 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_eval_exp (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.  */
diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
index 4037653106..dd4eca9aac 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.eval_exp (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_eval_exp (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 1a23e0b2da..d2aa1eddc9 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.eval_exp (data, size, as_lval, per_cu, frame, nullptr,
-			     type, subobj_type, subobj_byte_offset);
+      retval
+	= dwarf2_eval_exp (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.eval_exp (dlbaton->data, dlbaton->size,
-			     true, per_cu, frame, addr_stack);
+      result
+	= dwarf2_eval_exp (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] 75+ messages in thread

* [PATCH 23/30] Rename and update the piece_closure structure
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (21 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 22/30] Remove dwarf_expr_context from expr.h interface Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2020-12-07 19:00 ` [PATCH 24/30] Move read_addr_from_reg function to frame.c Zoran Zaric
                   ` (7 subsequent siblings)
  30 siblings, 0 replies; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

Class that describes a computed_lval closure needs to be update to fit
better with the new dwarf_entry set of classes. This also means that a
pieced_value_funcs interface with that closure, needs to be renamed and
updated accordingly.

Considering that a closure is designed to describe a computed location
description, it makes sense to rename piece_closure to a
computed_closure.

gdb/ChangeLog:

	* dwarf2/expr.c (struct piece_closure): Change to
	computed_closure class.
	(allocate_piece_closure): Remove function.
	(rw_pieced_value): Rename to rw_closure_value and change to use
	computed_closure class.
	(read_pieced_value): Rename to read_closure_value and change to
	use computed_closure class.
	(write_pieced_value): Rename to write_closure_value and change
	to use computed_closure class.
	(check_pieced_synthetic_pointer): Rename to
	check_synthetic_pointer and change to use computed_closure
	class.
	(indirect_pieced_value): Rename to indirect_closure_value and
	change to use computed_closure class.
	(coerce_pieced_ref): Rename to coerce_closure_ref and change
	to use computed_closure class.
	(copy_pieced_value_closure): Rename to copy_value_closure and
	change to use computed_closure class.
	(free_pieced_value_closure): Rename to free_value_closure and
	change to use computed_closure class.
	(dwarf_expr_context::gdb_value_to_dwarf_entry): Change to use
	computed_closure class.
	(dwarf_expr_context::dwarf_entry_to_gdb_value): Change to use
	computed_closure class.
---
 gdb/dwarf2/expr.c | 206 +++++++++++++++++++++++-----------------------
 1 file changed, 103 insertions(+), 103 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 9ed0ad8baf..a259f5d5d5 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -1351,77 +1351,68 @@ dwarf_entry_factory::value_cast_op (const dwarf_value *arg, struct type *type)
   return create_value (value_contents_raw (result), type);
 }
 
-struct piece_closure
+/* Closure class that encapsulates a location description and
+   a context in which that description is created.  Used for
+   lval_computed value abstraction.  */
+class computed_closure : public refcounted_object
 {
-  /* 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;
+public:
+  computed_closure (dwarf_entry *entry, struct frame_id frame_id) :
+		    m_entry (entry), m_frame_id (frame_id)
+		    {entry->incref ();}
 
-  /* Location description of this variable.  */
-  dwarf_location *location;
+  virtual ~computed_closure ()
+  {
+    m_entry->decref ();
 
-  /* Frame ID of frame to which a register value is relative, used
-     only by DWARF_VALUE_REGISTER.  */
-  struct frame_id frame_id;
-};
+    if (m_entry->refcount () == 0)
+      delete m_entry;
+  }
 
-/* Allocate a closure for a value formed from separately-described
-   PIECES.  */
+  dwarf_entry *get_entry () const
+  {
+    return m_entry;
+  }
 
-static struct piece_closure *
-allocate_piece_closure (dwarf2_per_cu_data *per_cu,
-			dwarf2_per_objfile *per_objfile,
-			dwarf_location *location,
-			struct frame_info *frame)
-{
-  struct piece_closure *c = new piece_closure;
+  struct frame_id get_frame_id () const
+  {
+    return m_frame_id;
+  }
 
-  c->refc = 1;
-  /* We must capture this here due to sharing of DWARF state.  */
-  c->per_objfile = per_objfile;
-  c->per_cu = per_cu;
-  if (frame == NULL)
-    c->frame_id = null_frame_id;
-  else
-    c->frame_id = get_frame_id (frame);
+private:
+  /* Entry that this class encloses.  */
+  dwarf_entry *m_entry;
 
-  location->incref ();
-  c->location = location;
-  return c;
-}
+  /* Frame ID context of the closure.  */
+  struct frame_id m_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,
+/* 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 pieces.  */
+   composing it from its closure.  */
 
 static void
-rw_pieced_value (struct value *v, struct value *from)
+rw_closure_value (struct value *v, struct value *from)
 {
   LONGEST bit_offset = 0, max_bit_offset;
-  struct piece_closure *closure
-    = (struct piece_closure *) value_computed_closure (v);
+  computed_closure *closure = ((computed_closure*) value_computed_closure (v));
   bool big_endian = type_byte_order (value_type (v)) == BFD_ENDIAN_BIG;
-  dwarf_entry *entry = closure->location;
+  dwarf_entry *entry = closure->get_entry ();
 
   /* Only expect implicit pointer and composite location
      description here.  */
   if (entry == nullptr
       || (dynamic_cast<dwarf_implicit_pointer *> (entry) == nullptr
-          && dynamic_cast<dwarf_composite *> (entry) == nullptr))
+	  && dynamic_cast<dwarf_composite *> (entry) == nullptr))
     internal_error (__FILE__, __LINE__, _("invalid location type"));
 
   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"));
+	  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);
@@ -1454,7 +1445,7 @@ rw_pieced_value (struct value *v, struct value *from)
       return;
     }
 
-  struct frame_info *frame = frame_find_by_id (closure->frame_id);
+  struct frame_info *frame = frame_find_by_id (closure->get_frame_id ());
 
   dwarf_composite *composite_entry = dynamic_cast<dwarf_composite *> (entry);
 
@@ -1524,15 +1515,15 @@ rw_pieced_value (struct value *v, struct value *from)
 }
 
 static void
-read_pieced_value (struct value *v)
+read_closure_value (struct value *v)
 {
-  rw_pieced_value (v, NULL);
+  rw_closure_value (v, NULL);
 }
 
 static void
-write_pieced_value (struct value *to, struct value *from)
+write_closure_value (struct value *to, struct value *from)
 {
-  rw_pieced_value (to, from);
+  rw_closure_value (to, from);
 }
 
 /* Check if a given location contains an implicit pointer
@@ -1592,17 +1583,18 @@ check_synthetic_pointer_location (const dwarf_location *location,
    a synthetic pointer.  */
 
 static int
-check_pieced_synthetic_pointer (const struct value *value, LONGEST bit_offset,
-				int bit_length)
+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);
 
-  struct piece_closure *closure
-    = (struct piece_closure *) value_computed_closure (value);
-  dwarf_location *location = closure->location;
+  computed_closure *closure
+    = (computed_closure *) value_computed_closure (value);
+  auto location
+    = dynamic_cast<dwarf_location *> (closure->get_entry ());
 
   if (location == nullptr)
     return 0;
@@ -1680,10 +1672,10 @@ indirect_from_location (const dwarf_location *location,
    pointer.  This handles the synthetic pointer case when needed.  */
 
 static struct value *
-indirect_pieced_value (struct value *value)
+indirect_closure_value (struct value *value)
 {
-  struct piece_closure *closure
-    = (struct piece_closure *) value_computed_closure (value);
+  computed_closure *closure
+    = (computed_closure *) value_computed_closure (value);
 
   struct type *type = check_typedef (value_type (value));
   if (type->code () != TYPE_CODE_PTR)
@@ -1695,7 +1687,8 @@ indirect_pieced_value (struct value *value)
   if (value_bitsize (value))
     bit_offset += value_bitpos (value);
 
-  dwarf_location *location = closure->location;
+  auto location
+    = dynamic_cast<dwarf_location *> (closure->get_entry ());
 
   /* Only location descriptions are meaningful here.  */
   if (location == nullptr)
@@ -1726,20 +1719,20 @@ indirect_pieced_value (struct value *value)
    references.  */
 
 static struct value *
-coerce_pieced_ref (const struct value *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)))
     {
-      struct piece_closure *closure
-	= (struct piece_closure *) value_computed_closure (value);
+      computed_closure *closure
+	= (computed_closure *) value_computed_closure (value);
       struct frame_info *frame
 	= get_selected_frame (_("No frame selected."));
 
       auto pointer_entry
-	= dynamic_cast<dwarf_implicit_pointer *> (closure->location);
+	= dynamic_cast<dwarf_implicit_pointer *> (closure->get_entry ());
 
       /* Only implicit pointer location description is meaningful here.  */
       if (pointer_entry == nullptr)
@@ -1747,8 +1740,8 @@ coerce_pieced_ref (const struct value *value)
 
       return indirect_synthetic_pointer (pointer_entry->get_die_offset (),
 					 pointer_entry->get_offset (),
-					 closure->per_cu,
-					 closure->per_objfile,
+					 pointer_entry->get_per_cu (),
+					 pointer_entry->get_per_objfile (),
 					 frame, type);
     }
   else
@@ -1759,38 +1752,41 @@ coerce_pieced_ref (const struct value *value)
 }
 
 static void *
-copy_pieced_value_closure (const struct value *v)
+copy_value_closure (const struct value *v)
 {
-  struct piece_closure *c
-    = (struct piece_closure *) value_computed_closure (v);
+  computed_closure *closure = ((computed_closure*) value_computed_closure (v));
 
-  ++c->refc;
-  return c;
+  if (closure == nullptr)
+    internal_error (__FILE__, __LINE__, _("invalid closure type"));
+
+  closure->incref ();
+  return closure;
 }
 
 static void
-free_pieced_value_closure (struct value *v)
+free_value_closure (struct value *v)
 {
-  struct piece_closure *c
-    = (struct piece_closure *) value_computed_closure (v);
+  computed_closure *closure = ((computed_closure*) value_computed_closure (v));
 
-  --c->refc;
-  if (c->refc == 0)
-    {
-      c->location->decref ();
-      delete c;
-    }
+  if (closure == nullptr)
+    internal_error (__FILE__, __LINE__, _("invalid closure type"));
+
+  closure->decref ();
+
+  if (closure->refcount () == 0)
+    delete closure;
 }
 
-/* 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
+/* 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
 };
 
 /* Given context CTX, section offset SECT_OFF, and compilation unit
@@ -2331,9 +2327,9 @@ dwarf_expr_context::gdb_value_to_dwarf_entry (struct value *value)
       {
 	/* Dwarf entry is enclosed by the closure anyway so we just
 	   need to unwrap it here.  */
-	struct piece_closure *closure
-	  = (struct piece_closure *) value_computed_closure (value);
-	auto location = dynamic_cast<dwarf_location *> (closure->location);
+	computed_closure *closure
+	  = ((computed_closure *) value_computed_closure (value));
+	auto location = dynamic_cast<dwarf_location *> (closure->get_entry ());
 
 	if (location == nullptr)
 	  internal_error (__FILE__, __LINE__, _("invalid closure type"));
@@ -2425,15 +2421,19 @@ dwarf_expr_context::dwarf_entry_to_gdb_value (dwarf_entry *entry,
 	      (void *)(implicit_entry->get_contents () + subobj_offset),
 	      subtype_len);
     }
-  else if (auto implicit_pointer_entry
-	    = dynamic_cast<dwarf_implicit_pointer *> (entry))
+  else if (dynamic_cast<dwarf_implicit_pointer *> (entry) != nullptr)
     {
-      struct piece_closure *closure
-	= allocate_piece_closure (this->per_cu, this->per_objfile,
-				  implicit_pointer_entry, this->frame);
+      /* Complain if the expression is larger than the size of the
+	 outer type.  */
+      if (this->addr_size > HOST_CHAR_BIT * TYPE_LENGTH (type))
+	invalid_synthetic_pointer ();
+
+      computed_closure *closure
+	= new computed_closure (entry, get_frame_id (frame));
+      closure->incref ();
 
       retval
-	= allocate_computed_value (subobj_type, &pieced_value_funcs, closure);
+	= allocate_computed_value (subobj_type, &closure_value_funcs, closure);
       set_value_offset (retval, subobj_offset);
     }
   else if (auto composite_entry = dynamic_cast<dwarf_composite *> (entry))
@@ -2449,12 +2449,12 @@ dwarf_expr_context::dwarf_entry_to_gdb_value (dwarf_entry *entry,
       if (bit_size > HOST_CHAR_BIT * TYPE_LENGTH (type))
 	invalid_synthetic_pointer ();
 
-      struct piece_closure *closure
-	= allocate_piece_closure (this->per_cu, this->per_objfile,
-				  composite_entry, this->frame);
+      computed_closure *closure
+	= new computed_closure (entry, get_frame_id (frame));
+      closure->incref ();
 
       retval
-	= allocate_computed_value (subobj_type, &pieced_value_funcs, closure);
+	= allocate_computed_value (subobj_type, &closure_value_funcs, closure);
       set_value_offset (retval, subobj_offset);
   }
 
-- 
2.17.1


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

* [PATCH 24/30] Move read_addr_from_reg function to frame.c
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (22 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 23/30] Rename and update the piece_closure structure Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2020-12-07 19:00 ` [PATCH 25/30] Add frame info check to DW_OP_reg operations Zoran Zaric
                   ` (6 subsequent siblings)
  30 siblings, 0 replies; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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 a259f5d5d5..9f2c77197a 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -97,17 +97,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 5fb4b0857e..63fe789939 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -51,10 +51,6 @@ struct value *dwarf2_eval_exp (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 dd4eca9aac..1469621133 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] 75+ messages in thread

* [PATCH 25/30] Add frame info check to DW_OP_reg operations
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (23 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 24/30] Move read_addr_from_reg function to frame.c Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2020-12-07 19:00 ` [PATCH 26/30] Remove DWARF expression composition check Zoran Zaric
                   ` (5 subsequent siblings)
  30 siblings, 0 replies; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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 9f2c77197a..ee3732a6d9 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -2972,6 +2972,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 = entry_factory->create_register (result);
@@ -2980,6 +2981,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 = entry_factory->create_register (reg);
-- 
2.17.1


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

* [PATCH 26/30] Remove DWARF expression composition check
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (24 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 25/30] Add frame info check to DW_OP_reg operations Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2020-12-07 19:00 ` [PATCH 27/30] Add support for any location description in CFI Zoran Zaric
                   ` (4 subsequent siblings)
  30 siblings, 0 replies; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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, everything all operations are treated the same
and everything is composable, so there is no need for the previous
restriction 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 ee3732a6d9..1f07502059 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -2971,7 +2971,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;
@@ -2980,7 +2979,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;
@@ -2997,8 +2995,6 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	    result_entry = entry_factory->create_implicit (op_ptr, len,
 							   BFD_ENDIAN_UNKNOWN);
 	    op_ptr += len;
-	    dwarf_expr_require_composition (op_ptr, op_end,
-					    "DW_OP_implicit_value");
 	  }
 	  break;
 
@@ -3014,8 +3010,6 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	      = entry_factory->create_implicit (value->get_contents (),
 						TYPE_LENGTH (type),
 						type_byte_order (type));
-
-	    dwarf_expr_require_composition (op_ptr, op_end, "DW_OP_stack_value");
 	  }
 	  break;
 
@@ -3039,9 +3033,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] 75+ messages in thread

* [PATCH 27/30] Add support for any location description in CFI
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (25 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 26/30] Remove DWARF expression composition check Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2020-12-07 19:00 ` [PATCH 28/30] Add DWARF operations for byte and bit offset Zoran Zaric
                   ` (3 subsequent siblings)
  30 siblings, 0 replies; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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        | 36 +++++++++++++++++++++++++------
 2 files changed, 61 insertions(+), 29 deletions(-)

diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
index 1469621133..e83031d1a5 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_eval_exp (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 4618da6c81..0068bf8455 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1567,25 +1567,49 @@ 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;
 
-      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] 75+ messages in thread

* [PATCH 28/30] Add DWARF operations for byte and bit offset
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (26 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 27/30] Add support for any location description in CFI Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2020-12-07 19:00 ` [PATCH 29/30] Add support for DW_OP_LLVM_undefined operation Zoran Zaric
                   ` (2 subsequent siblings)
  30 siblings, 0 replies; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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_raw call.
	* compile/compile-loc2c.c (compute_stack_depth_worker): Add new
	DWARF operations support.
	* dwarf2/expr.c (rw_closure_value): Add bit offset support.
	(dwarf_expr_context::dwarf_entry_to_gdb_value): Add source bit
	offset argument to the value_contents_copy_raw call.
	(dwarf_expr_context::execute_stack_op): Add new DWARF
	operations support.
	* dwarf2/loc.c (dwarf2_get_symbol_read_needs): Add new DWARF
 	operations support.
	* findvar.c (read_frame_register_value): Add source bit offset
	argument to the value_contents_copy_raw 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_raw call.
	(value_slice): Add source bit offset argument to the
	value_contents_copy_raw 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_raw call.
	(value_from_component): Add source bit offset argument to the
	value_contents_copy_raw 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                            |  68 +++-
 gdb/dwarf2/loc.c                             |   3 +
 gdb/f-lang.c                                 |   2 +-
 gdb/findvar.c                                |   4 +-
 gdb/testsuite/gdb.dwarf2/dw2-llvm-offset.exp | 328 +++++++++++++++++++
 gdb/testsuite/lib/dwarf.exp                  |   4 +
 gdb/valops.c                                 | 123 ++++---
 gdb/value.c                                  |  66 ++--
 gdb/value.h                                  |   4 +-
 include/dwarf2.def                           |   4 +
 12 files changed, 540 insertions(+), 74 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 906671155d..5c068a6f07 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -606,7 +606,7 @@ coerce_unspec_val_to_type (struct value *val, struct type *type)
       else
 	{
 	  result = allocate_value (type);
-	  value_contents_copy_raw (result, 0, val, 0, TYPE_LENGTH (type));
+	  value_contents_copy_raw (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 2fd1810759..037b6433bc 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 1f07502059..dfe34f877a 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -1442,6 +1442,9 @@ rw_closure_value (struct value *v, struct value *from)
   unsigned int i;
   unsigned int pieces_num = composite_entry->get_pieces_num ();
 
+  bits_to_skip += HOST_CHAR_BIT * composite_entry->get_offset ()
+		  + composite_entry->get_bit_suboffset ();
+
   for (i = 0; i < pieces_num; i++)
     {
       ULONGEST bit_size = composite_entry->get_bit_size_at (i);
@@ -2363,6 +2366,7 @@ dwarf_expr_context::dwarf_entry_to_gdb_value (dwarf_entry *entry,
       address = value_as_address (value_from_pointer (ptr_type, address));
       retval = value_at_lazy (subobj_type, address + subobj_offset);
       set_value_stack (retval, memory_entry->in_stack ());
+      set_value_bitpos (retval, memory_entry->get_bit_suboffset ());
     }
   else if (auto register_entry = dynamic_cast<dwarf_register *> (entry))
     {
@@ -2373,6 +2377,18 @@ dwarf_expr_context::dwarf_entry_to_gdb_value (dwarf_entry *entry,
       retval = gdbarch_value_from_register (gdbarch, type,
 					    gdb_regnum, get_frame_id (frame));
 
+      LONGEST reg_offset = register_entry->get_offset ();
+      LONGEST retval_offset = value_offset (retval);
+
+      if (type_byte_order (type) == BFD_ENDIAN_BIG
+	  && (TYPE_LENGTH (type) + reg_offset) < retval_offset)
+	/* Big-endian, and we want less than full size.  */
+	set_value_offset (retval, retval_offset - reg_offset);
+      else
+	set_value_offset (retval, retval_offset + reg_offset);
+
+      set_value_bitpos (retval, register_entry->get_bit_suboffset ());
+
       /* Get the data.  */
       read_frame_register_value (retval, frame);
 
@@ -2384,7 +2400,8 @@ dwarf_expr_context::dwarf_entry_to_gdb_value (dwarf_entry *entry,
 	     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;
 	}
     }
@@ -3672,6 +3689,55 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	    = entry_factory->create_memory (this->addr_info->addr);
 	  break;
 
+	case DW_OP_LLVM_offset:
+	  {
+	    dwarf_value *offset_value
+	      = entry_factory->entry_to_value (fetch (0), address_type);
+	    pop ();
+
+	    dwarf_require_integral (offset_value->get_type ());
+
+	    dwarf_location *location
+	      = entry_factory->entry_to_location (fetch (0));
+	    pop ();
+
+	    location->add_bit_offset
+	      (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;
+
+	    dwarf_location *location
+	      = entry_factory->entry_to_location (fetch (0));
+	    pop ();
+
+	    location->add_bit_offset (result * HOST_CHAR_BIT);
+	    result_entry = location;
+	  }
+	  break;
+
+	case DW_OP_LLVM_bit_offset:
+	  {
+	    dwarf_value *offset_value
+	      = entry_factory->entry_to_value (fetch (0), address_type);
+	    pop ();
+
+	    dwarf_require_integral (offset_value->get_type ());
+
+	    dwarf_location *location
+	      = entry_factory->entry_to_location (fetch (0));
+	    pop ();
+
+	    location->add_bit_offset (offset_value->to_long ());
+	    result_entry = location;
+	  }
+	  break;
+
 	default:
 	  error (_("Unhandled dwarf expression opcode 0x%x"), op);
 	}
diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c
index d2aa1eddc9..fe2174ab14 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -1829,6 +1829,8 @@ dwarf2_get_symbol_read_needs (gdb::array_view<const gdb_byte> expr,
 	case DW_OP_nop:
 	case DW_OP_GNU_uninit:
 	case DW_OP_push_object_address:
+	case DW_OP_LLVM_offset:
+	case DW_OP_LLVM_bit_offset:
 	  break;
 
 	case DW_OP_form_tls_address:
@@ -1846,6 +1848,7 @@ dwarf2_get_symbol_read_needs (gdb::array_view<const gdb_byte> expr,
 	case DW_OP_constu:
 	case DW_OP_plus_uconst:
 	case DW_OP_piece:
+	case DW_OP_LLVM_offset_constu:
 	  op_ptr = safe_read_uleb128 (op_ptr, expr_end, &uoffset);
 	  break;
 
diff --git a/gdb/f-lang.c b/gdb/f-lang.c
index 6771758bac..5121515665 100644
--- a/gdb/f-lang.c
+++ b/gdb/f-lang.c
@@ -196,7 +196,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 95d0ff03a2..9fefe1e822 100644
--- a/gdb/findvar.c
+++ b/gdb/findvar.c
@@ -871,6 +871,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)));
 
@@ -894,7 +895,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 0000000000..afbd123a16
--- /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 223e0b15be..b29e8917c0 100644
--- a/gdb/testsuite/lib/dwarf.exp
+++ b/gdb/testsuite/lib/dwarf.exp
@@ -1022,6 +1022,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 4d0e002b20..057c5968f2 100644
--- a/gdb/valops.c
+++ b/gdb/valops.c
@@ -1018,20 +1018,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)
@@ -1048,6 +1059,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.
@@ -1119,7 +1134,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))
 	  {
@@ -1145,10 +1160,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
 	  {
@@ -1163,10 +1193,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
@@ -1174,35 +1200,45 @@ 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);
 	    int changed_len;
-	    gdb_byte buffer[sizeof (LONGEST)];
-	    int optim, unavail;
+	    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)
+	      {
+		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);
+		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,
-					   changed_len, buffer,
+					   changed_len, buffer.data (),
 					   &optim, &unavail))
 	      {
 		if (optim)
@@ -1213,11 +1249,11 @@ 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,
-				      changed_len, buffer);
+				      changed_len, buffer.data ());
 	  }
 	else
 	  {
@@ -1234,8 +1270,7 @@ value_assign (struct value *toval, struct value *fromval)
 	    else
 	      {
 		put_frame_register_bytes (frame, value_reg,
-					  value_offset (toval),
-					  TYPE_LENGTH (type),
+					  offset, TYPE_LENGTH (type),
 					  value_contents (fromval));
 	      }
 	  }
@@ -1337,21 +1372,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;
 }
@@ -1700,7 +1736,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;
     }
@@ -1710,7 +1746,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;
 }
 
@@ -3977,7 +4014,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 5f52c2d566..60054d3b34 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -1306,9 +1306,10 @@ value_ranges_copy_adjusted (struct value *dst, int dst_bit_offset,
 
 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
@@ -3015,7 +3030,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;
@@ -3050,7 +3065,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));
@@ -3644,7 +3659,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);
@@ -3827,9 +3842,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.  */
@@ -3842,10 +3857,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);
@@ -3888,6 +3899,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))
@@ -3896,9 +3912,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 2eaba57487..d1c8ced67c 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -739,10 +739,10 @@ 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 void value_contents_copy_raw (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 13825a3eef..4880a2268f 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] 75+ messages in thread

* [PATCH 29/30] Add support for DW_OP_LLVM_undefined operation
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (27 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 28/30] Add DWARF operations for byte and bit offset Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2020-12-07 19:00 ` [PATCH 30/30] Add support for nested composite locations Zoran Zaric
  2020-12-08 14:48 ` [PATCH 00/30] Allow location description on the DWARF stack Metzger, Markus T
  30 siblings, 0 replies; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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.
	* dwarf2/loc.c (dwarf2_get_symbol_read_needs): 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/loc.c                              |   1 +
 .../gdb.dwarf2/dw2-llvm-undefined.exp         | 144 ++++++++++++++++++
 include/dwarf2.def                            |   1 +
 5 files changed, 154 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 037b6433bc..e9698a758e 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 dfe34f877a..a65d18d492 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -3738,6 +3738,10 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	  }
 	  break;
 
+	case DW_OP_LLVM_undefined:
+	  result_entry = entry_factory->create_undefined ();
+	  break;
+
 	default:
 	  error (_("Unhandled dwarf expression opcode 0x%x"), op);
 	}
diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c
index fe2174ab14..1a903ce123 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -1831,6 +1831,7 @@ dwarf2_get_symbol_read_needs (gdb::array_view<const gdb_byte> expr,
 	case DW_OP_push_object_address:
 	case DW_OP_LLVM_offset:
 	case DW_OP_LLVM_bit_offset:
+	case DW_OP_LLVM_undefined:
 	  break;
 
 	case DW_OP_form_tls_address:
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 0000000000..be677a1ddc
--- /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 4880a2268f..b58560296d 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] 75+ messages in thread

* [PATCH 30/30] Add support for nested composite locations
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (28 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 29/30] Add support for DW_OP_LLVM_undefined operation Zoran Zaric
@ 2020-12-07 19:00 ` Zoran Zaric
  2020-12-08 14:48 ` [PATCH 00/30] Allow location description on the DWARF stack Metzger, Markus T
  30 siblings, 0 replies; 75+ messages in thread
From: Zoran Zaric @ 2020-12-07 19:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zoran Zaric

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

gdb/ChangeLog:

	* compile/compile-loc2c.c (compute_stack_depth_worker): Add new
	DW_OP_LLVM_piece_end operation support.
	* dwarf2/expr.c (class dwarf_value): Add copy constructor.
	(class dwarf_location): Add copy constructor.
	(class dwarf_undefined): Add copy constructor.
	(class dwarf_memory): Add copy constructor.
	(class dwarf_register): Add copy constructor.
	(class dwarf_implicit): Add copy constructor.
	(class dwarf_implicit_pointer): Add copy constructor.
	(class dwarf_composite): Add copy constructor.
	(read_from_location): Add composite completed check.
	(write_to_location): Add composite completed check.
	(read_value_contents_from_location): New function.
	(dwarf_entry_factory::copy_entry): New method.
	(rw_closure_value): Now calls read_value_contents_from_location
	function.
	(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.
	* dwarf2/loc.c (dwarf2_get_symbol_read_needs): 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                             | 305 ++++++++++++++++--
 gdb/dwarf2/loc.c                              |   1 +
 .../gdb.dwarf2/dw2-llvm-piece-end.exp         | 191 +++++++++++
 include/dwarf2.def                            |   1 +
 5 files changed, 469 insertions(+), 30 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 e9698a758e..fd28f2cd4e 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 a65d18d492..647bf8810a 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -308,6 +308,17 @@ 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;
+  }
+
   virtual ~dwarf_value () = default;
 
   const gdb_byte* get_contents () const
@@ -352,6 +363,12 @@ class dwarf_location : public dwarf_entry
     m_bit_suboffset = bit_suboffset % HOST_CHAR_BIT;
   }
 
+  dwarf_location (const dwarf_location &location)
+    : m_offset (location.m_offset),
+      m_bit_suboffset (location.m_bit_suboffset),
+      m_initialised (location.m_initialised)
+  {}
+
   virtual ~dwarf_location () = default;
 
   LONGEST get_offset () const
@@ -404,6 +421,11 @@ class dwarf_undefined : public dwarf_location
   dwarf_undefined (LONGEST offset = 0, LONGEST bit_suboffset = 0)
     : dwarf_location (offset, bit_suboffset)
   {}
+
+  dwarf_undefined (const dwarf_undefined &undefined_entry)
+    : dwarf_location (undefined_entry)
+  {}
+
 };
 
 class dwarf_memory : public dwarf_location
@@ -415,6 +437,11 @@ class dwarf_memory : public dwarf_location
       m_stack (stack)
   {}
 
+  dwarf_memory (const dwarf_memory &memory_entry)
+    : dwarf_location (memory_entry),
+      m_stack (memory_entry.m_stack)
+  {}
+
   bool in_stack () const
   {
     return m_stack;
@@ -440,6 +467,11 @@ class dwarf_register : public dwarf_location
       m_regnum (regnum)
   {}
 
+  dwarf_register (const dwarf_register &register_entry)
+    : dwarf_location (register_entry),
+      m_regnum (register_entry.m_regnum)
+  {}
+
   unsigned int get_regnum () const
   {
     return m_regnum;
@@ -468,6 +500,17 @@ class dwarf_implicit : public dwarf_location
     m_byte_order = byte_order;
   }
 
+  dwarf_implicit (const dwarf_implicit &implicit_entry)
+    : dwarf_location (implicit_entry)
+  {
+    size_t size = implicit_entry.m_size;
+    m_contents.reset ((gdb_byte *) xzalloc (size));
+
+    memcpy (m_contents.get (), implicit_entry.m_contents.get (), size);
+    m_size = size;
+    m_byte_order = implicit_entry.m_byte_order;
+  }
+
   const gdb_byte* get_contents () const
   {
     return m_contents.get ();
@@ -508,6 +551,14 @@ 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_entry)
+    : dwarf_location (implicit_ptr_entry),
+      m_per_objfile (implicit_ptr_entry.m_per_objfile),
+      m_per_cu (implicit_ptr_entry.m_per_cu),
+      m_addr_size (implicit_ptr_entry.m_addr_size),
+      m_die_offset (implicit_ptr_entry.m_die_offset)
+  {}
+
   dwarf2_per_objfile *get_per_objfile () const
   {
     return m_per_objfile;
@@ -551,6 +602,22 @@ class dwarf_composite : public dwarf_location
     : dwarf_location (offset, bit_suboffset)
   {}
 
+  dwarf_composite (const dwarf_composite &composite_entry)
+    : dwarf_location (composite_entry)
+  {
+    /* 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_entry.m_pieces.size (); i++)
+      {
+	dwarf_location* location = composite_entry.m_pieces[i].m_location;
+
+	location->incref ();
+	m_pieces.emplace_back (location, composite_entry.m_pieces[i].m_size);
+      }
+
+    m_completed = composite_entry.m_completed;
+  }
+
   /* A composite location gets detached from its factory object for
      the purpose of lval_computed resolution, which means that it
      needs to take care of garbage collecting its pieces.  */
@@ -591,6 +658,16 @@ class dwarf_composite : public dwarf_location
     return m_pieces.size ();
   }
 
+  void set_completed (bool completed)
+  {
+    m_completed = completed;
+  };
+
+  bool is_completed () const
+  {
+    return m_completed;
+  };
+
 private:
   /* Composite piece that contains a piece location
      description and it's size.  */
@@ -608,6 +685,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;
 };
 
 /* Read contents from the location specified by the DWARF location
@@ -789,6 +869,9 @@ read_from_location (const dwarf_location *location, struct frame_info *frame,
       unsigned int pieces_num = composite_entry->get_pieces_num ();
       unsigned int i;
 
+      if (!composite_entry->is_completed ())
+	ill_formed_expression ();
+
       total_bits_to_skip += offset * HOST_CHAR_BIT + bit_suboffset;
 
       /* Skip pieces covered by the read offset.  */
@@ -971,6 +1054,9 @@ write_to_location (const dwarf_location *location, struct frame_info *frame,
       unsigned int pieces_num = composite_entry->get_pieces_num ();
       unsigned int i;
 
+      if (!composite_entry->is_completed ())
+	ill_formed_expression ();
+
       total_bits_to_skip += offset * HOST_CHAR_BIT + bit_suboffset;
 
       /* Skip pieces covered by the write offset.  */
@@ -1008,6 +1094,92 @@ write_to_location (const dwarf_location *location, struct frame_info *frame,
     internal_error (__FILE__, __LINE__, _("invalid location type"));
 }
 
+/* Read value contents from the location specified by the DWARF
+   location description entry LOCATION.
+
+   The read operation is performed in the context of FRAME.  BIT_SIZE
+   is the number of bits to read.  The data read is copied to the
+   caller-managed buffer BUF.  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.  */
+
+static void
+read_value_contents_from_location (struct value * value,
+				   const dwarf_location *location,
+				   struct frame_info *frame,
+				   LONGEST bits_to_skip,
+				   int value_bit_offset, size_t bit_size,
+				   size_t location_bit_limit)
+{
+  /* Implicit pointers are handled later.  */
+  if (dynamic_cast<const dwarf_implicit_pointer *> (location) != nullptr)
+    return;
+
+  auto composite_entry = dynamic_cast<const dwarf_composite *> (location);
+
+  if (composite_entry == nullptr)
+    {
+      int optimized, unavailable;
+      bool big_endian = type_byte_order (value_type (value)) == BFD_ENDIAN_BIG;
+
+      read_from_location (location, frame, bits_to_skip,
+			  value_contents_raw (value),
+			  value_bit_offset, bit_size, 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);
+
+      return;
+    }
+
+  if (!composite_entry->is_completed ())
+    ill_formed_expression ();
+
+  unsigned int pieces_num = composite_entry->get_pieces_num ();
+  unsigned int i;
+
+  LONGEST total_bits_to_skip = bits_to_skip
+			       + composite_entry->get_offset () * HOST_CHAR_BIT
+			       + composite_entry->get_bit_suboffset ();
+
+  /* Skip pieces covered by the read offset.  */
+  for (i = 0; i < pieces_num; i++)
+    {
+      LONGEST piece_bit_size = composite_entry->get_bit_size_at (i);
+
+      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 = composite_entry->get_bit_size_at (i);
+      const dwarf_location *piece = composite_entry->get_piece_at (i);
+
+      if (piece_bit_size > bit_size)
+	piece_bit_size = bit_size;
+
+      read_value_contents_from_location (value, piece, frame,
+					 total_bits_to_skip,
+					 value_bit_offset, piece_bit_size,
+					 piece_bit_size);
+
+      if (bit_size == piece_bit_size)
+	break;
+
+      value_bit_offset += piece_bit_size;
+      bit_size -= piece_bit_size;
+    }
+}
+
 /* Convert a value entry to the matching struct value representation
    of a given TYPE.  OFFSET defines the offset into the value
    contents.
@@ -1107,6 +1279,9 @@ class dwarf_entry_factory
   dwarf_composite *create_composite (LONGEST offset = 0,
 				     LONGEST bit_suboffset = 0);
 
+  /* Create a deep copy of the DWARF ENTRY.  */
+  dwarf_entry *copy_entry (dwarf_entry *entry);
+
   /* Convert an entry to a location description entry. If the entry
      is a location description entry a dynamic cast is applied.
 
@@ -1252,6 +1427,33 @@ dwarf_entry_factory::create_composite (LONGEST offset, LONGEST bit_suboffset)
   return composite_entry;
 }
 
+dwarf_entry *
+dwarf_entry_factory::copy_entry (dwarf_entry *entry)
+{
+  dwarf_entry *entry_copy;
+
+  if (auto value = dynamic_cast<dwarf_value *> (entry))
+    entry_copy = new dwarf_value (*value);
+  else if (auto undefined = dynamic_cast<dwarf_undefined *> (entry))
+    entry_copy = new dwarf_undefined (*undefined);
+  else if (auto memory = dynamic_cast<dwarf_memory *> (entry))
+    entry_copy = new dwarf_memory (*memory);
+  else if (auto reg = dynamic_cast<dwarf_register *> (entry))
+    entry_copy = new dwarf_register (*reg);
+  else if (auto implicit = dynamic_cast<dwarf_implicit *> (entry))
+    entry_copy = new dwarf_implicit (*implicit);
+  else if (auto implicit_pointer
+	    = dynamic_cast<dwarf_implicit_pointer *> (entry))
+    entry_copy = new dwarf_implicit_pointer (*implicit_pointer);
+  else if (auto composite = dynamic_cast<dwarf_composite *> (entry))
+    entry_copy = new dwarf_composite (*composite);
+  else
+    internal_error (__FILE__, __LINE__, _("invalid DWARF entry to copy."));
+
+  record_entry (entry_copy);
+  return entry_copy;
+}
+
 dwarf_location *
 dwarf_entry_factory::entry_to_location (dwarf_entry *entry)
 {
@@ -1460,30 +1662,19 @@ rw_closure_value (struct value *v, struct value *from)
       const dwarf_location *location = composite_entry->get_piece_at (i);
       ULONGEST bit_size = composite_entry->get_bit_size_at (i);
       size_t this_bit_size = bit_size - bits_to_skip;
-      int optimized, unavailable;
 
       if (this_bit_size > max_bit_offset - bit_offset)
 	this_bit_size = max_bit_offset - bit_offset;
 
       if (from == NULL)
 	{
-	  /* Implicit pointers are handled later.  */
-	  if (dynamic_cast<const dwarf_implicit_pointer *>
-		(location) == nullptr)
-	    {
-	      read_from_location (location, frame, bits_to_skip,
-				  value_contents_raw (v), bit_offset,
-				  this_bit_size, bit_size, big_endian,
-				  &optimized, &unavailable);
-
-	      if (optimized)
-		mark_value_bits_optimized_out (v, bit_offset, this_bit_size);
-	      if (unavailable)
-		mark_value_bits_unavailable (v, bit_offset, this_bit_size);
-	    }
+	  read_value_contents_from_location (v, location, frame, bits_to_skip,
+					     bit_offset, this_bit_size, bit_size);
 	}
       else
 	{
+	  int optimized, unavailable;
+
 	  write_to_location (location, frame, bits_to_skip,
 			     value_contents (from), bit_offset,
 			     this_bit_size, bit_size, big_endian,
@@ -1892,10 +2083,34 @@ struct dwarf_expr_context
 
   /* 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  */
   dwarf_entry *add_piece (ULONGEST bit_size, ULONGEST bit_offset);
 
   /* The engine for the expression evaluator.  Using the context in this
@@ -2534,26 +2749,39 @@ dwarf_expr_context::add_piece (ULONGEST bit_size, ULONGEST bit_offset)
   dwarf_location *piece_entry;
   dwarf_composite *composite_entry;
 
-  if (!stack_empty_p ()
-      && dynamic_cast<dwarf_composite *> (fetch (0)) == nullptr)
+  if (stack_empty_p ())
+    piece_entry = entry_factory->create_undefined ();
+  else
     {
       piece_entry = entry_factory->entry_to_location (fetch (0));
-      pop ();
+
+      if (auto old_composite_entry
+	    = dynamic_cast<dwarf_composite *> (piece_entry))
+	{
+	  if (!old_composite_entry->is_completed ())
+	    piece_entry = entry_factory->create_undefined ();
+	}
+      else if (dynamic_cast<dwarf_undefined *> (piece_entry) != nullptr)
+	pop ();
     }
-  else
-    piece_entry = entry_factory->create_undefined ();
 
-  piece_entry->add_bit_offset (bit_offset);
+  if (dynamic_cast<dwarf_undefined *> (piece_entry) == nullptr)
+    {
+      piece_entry->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 ()
       || dynamic_cast<dwarf_composite *> (fetch (0)) == nullptr)
     composite_entry = entry_factory->create_composite ();
   else
     {
       composite_entry = dynamic_cast<dwarf_composite *> (fetch (0));
-      pop ();
+
+      if (composite_entry->is_completed ())
+	composite_entry = entry_factory->create_composite ();
+      else
+	pop ();
     }
 
   composite_entry->add_piece (piece_entry, bit_size);
@@ -3156,7 +3384,7 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	  break;
 
 	case DW_OP_dup:
-	  result_entry = fetch (0);
+	  result_entry = entry_factory->copy_entry (fetch (0));
 	  break;
 
 	case DW_OP_drop:
@@ -3182,7 +3410,7 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	  }
 
 	case DW_OP_over:
-	  result_entry = fetch (1);
+	  result_entry = entry_factory->copy_entry (fetch (1));
 	  break;
 
 	case DW_OP_rot:
@@ -3742,6 +3970,23 @@ dwarf_expr_context::execute_stack_op (const gdb_byte *op_ptr,
 	  result_entry = entry_factory->create_undefined ();
 	  break;
 
+	case DW_OP_LLVM_piece_end:
+	  {
+	    dwarf_entry *entry = fetch (0);
+
+	    dwarf_composite *composite_entry
+	      = dynamic_cast<dwarf_composite *> (entry);
+
+	    if (composite_entry == nullptr)
+	      ill_formed_expression ();
+
+	    if (composite_entry->is_completed ())
+	      ill_formed_expression ();
+
+	    composite_entry->set_completed (true);
+	    goto no_push;
+	  }
+
 	default:
 	  error (_("Unhandled dwarf expression opcode 0x%x"), op);
 	}
diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c
index 1a903ce123..e98d6fb39d 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -1832,6 +1832,7 @@ dwarf2_get_symbol_read_needs (gdb::array_view<const gdb_byte> expr,
 	case DW_OP_LLVM_offset:
 	case DW_OP_LLVM_bit_offset:
 	case DW_OP_LLVM_undefined:
+	case DW_OP_LLVM_piece_end:
 	  break;
 
 	case DW_OP_form_tls_address:
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 0000000000..3da739ea72
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-llvm-piece-end.exp
@@ -0,0 +1,191 @@
+# 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 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 b58560296d..fa6c20a9ef 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] 75+ messages in thread

* RE: [PATCH 00/30] Allow location description on the DWARF stack
  2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
                   ` (29 preceding siblings ...)
  2020-12-07 19:00 ` [PATCH 30/30] Add support for nested composite locations Zoran Zaric
@ 2020-12-08 14:48 ` Metzger, Markus T
  2020-12-08 16:17   ` Simon Marchi
  2020-12-09  0:30   ` Tye, Tony
  30 siblings, 2 replies; 75+ messages in thread
From: Metzger, Markus T @ 2020-12-08 14:48 UTC (permalink / raw)
  To: Zoran Zaric; +Cc: gdb-patches

Hello Zoran,

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

Wouldn't this change the semantics of existing operators?

DW_OP_piece, for example, does not push the value onto the DWARF stack
today; also, it terminates a DWARF sub-expression, which is no longer the
case with your proposal.

I like this extension (referring to location descriptions and allowing pieces on
the stack; I have not read the full proposal) as it addresses a problem we're
also facing.  But I'm not sure about the proper ordering of things.

Wouldn't this make GDB non-compliant?  Is there some opt-in so GDB can
recognize producers that are aware of this?

Do you have these patches on a user branch?

regards,
Markus.

> -----Original Message-----
> From: Gdb-patches <gdb-patches-bounces@sourceware.org> On Behalf Of Zoran
> Zaric via Gdb-patches
> Sent: Montag, 7. Dezember 2020 20:00
> To: gdb-patches@sourceware.org
> Cc: Zoran Zaric <Zoran.Zaric@amd.com>
> Subject: [PATCH 00/30] Allow location description on the DWARF stack
> 
> Based on gdb master: 13f11b0b61ca2620611b08eeaece0ce62c862f4b
> 
> 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 fifteen
> patches could be viewed as a standalone subset that introduces a
> welcome cleanup of the DWARF expression evaluator module.
> 
> The patch series is based on a patch that is already in the review
> process, so it had to be included in this series as well. The review
> for that patch can be found here:
> 
> https://sourceware.org/pipermail/gdb-patches/2020-November/173638.html
> 
> But I don't mind it being discussed as a part of this patch series.
> 
> Zoran Zaric (30):
>   Replace the symbol needs evaluator with a parser
>   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 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 new location description access interface
>   Add dwarf_entry factory class to expr.c
>   Change DWARF stack to use new dwarf_entry classes
>   Remove dwarf_expr_context from expr.h interface
>   Rename and update the piece_closure structure
>   Move read_addr_from_reg function to frame.c
>   Add frame info check to DW_OP_reg operations
>   Remove DWARF expression composition check
>   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                             | 3406 +++++++++++++++--
>  gdb/dwarf2/expr.h                             |  275 +-
>  gdb/dwarf2/frame.c                            |  163 +-
>  gdb/dwarf2/loc.c                              | 1885 +++------
>  gdb/dwarf2/loc.h                              |   34 +-
>  gdb/f-lang.c                                  |    2 +-
>  gdb/findvar.c                                 |    4 +-
>  gdb/frame.c                                   |   36 +-
>  gdb/testsuite/gdb.dwarf2/dw2-llvm-offset.exp  |  328 ++
>  .../gdb.dwarf2/dw2-llvm-piece-end.exp         |  191 +
>  .../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.python/py-framefilter-invalidarg.exp  |    2 +-
>  gdb/testsuite/lib/dwarf.exp                   |    8 +
>  gdb/valops.c                                  |  123 +-
>  gdb/value.c                                   |   68 +-
>  gdb/value.h                                   |    6 +-
>  include/dwarf2.def                            |    6 +
>  23 files changed, 4841 insertions(+), 2114 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

Intel Deutschland GmbH
Registered Address: Am Campeon 10-12, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Christin Eisenschmid, Gary Kershaw
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

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

* Re: [PATCH 00/30] Allow location description on the DWARF stack
  2020-12-08 14:48 ` [PATCH 00/30] Allow location description on the DWARF stack Metzger, Markus T
@ 2020-12-08 16:17   ` Simon Marchi
  2020-12-09  0:30   ` Tye, Tony
  1 sibling, 0 replies; 75+ messages in thread
From: Simon Marchi @ 2020-12-08 16:17 UTC (permalink / raw)
  To: Metzger, Markus T, Zoran Zaric; +Cc: gdb-patches

On 2020-12-08 9:48 a.m., Metzger, Markus T via Gdb-patches wrote:
> Do you have these patches on a user branch?

I don't think Zoran has push access to the repo yet, so I pushed this user branch for him:

users/zoran/allow-location-description-on-dwarf-stack
https://sourceware.org/git/?p=binutils-gdb.git;a=shortlog;h=refs/heads/users/zoran/allow-location-description-on-dwarf-stack

Simon

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

* RE: [PATCH 00/30] Allow location description on the DWARF stack
  2020-12-08 14:48 ` [PATCH 00/30] Allow location description on the DWARF stack Metzger, Markus T
  2020-12-08 16:17   ` Simon Marchi
@ 2020-12-09  0:30   ` Tye, Tony
  1 sibling, 0 replies; 75+ messages in thread
From: Tye, Tony @ 2020-12-09  0:30 UTC (permalink / raw)
  To: Metzger, Markus T, Zaric, Zoran (Zare); +Cc: gdb-patches

Hi Markus,
Care was taken to define things in a backwards compatible way so that the existing semantics of DWARF 5 are not changed. All existing operators still behave the same way when given the same arguments. This allows all the existing gdb tests to continue to pass with these changes. So it is optional whether a producer uses the extra capabilities of the extension. Effectively the extension is relaxing restrictions in DWARF 5 to allow more expressions to become legal. It is not changing the meaning of any existing expressions.

The extension suggests using the existing augmentation fields to indicate if it is being used, however there are other extensions supported by gdb that do not have any indication besides using new operators.

It would be good to know if this helps address the issues you have been encountering.

Thanks,
-Tony

-----Original Message-----
From: Gdb-patches <gdb-patches-bounces@sourceware.org> On Behalf Of Metzger, Markus T via Gdb-patches
Sent: Tuesday, December 8, 2020 9:48 AM
To: Zaric, Zoran (Zare) <Zoran.Zaric@amd.com>
Cc: gdb-patches@sourceware.org
Subject: RE: [PATCH 00/30] Allow location description on the DWARF stack

[CAUTION: External Email]

Hello Zoran,

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

Wouldn't this change the semantics of existing operators?

DW_OP_piece, for example, does not push the value onto the DWARF stack today; also, it terminates a DWARF sub-expression, which is no longer the case with your proposal.

I like this extension (referring to location descriptions and allowing pieces on the stack; I have not read the full proposal) as it addresses a problem we're also facing.  But I'm not sure about the proper ordering of things.

Wouldn't this make GDB non-compliant?  Is there some opt-in so GDB can recognize producers that are aware of this?

Do you have these patches on a user branch?

regards,
Markus.

> -----Original Message-----
> From: Gdb-patches <gdb-patches-bounces@sourceware.org> On Behalf Of 
> Zoran Zaric via Gdb-patches
> Sent: Montag, 7. Dezember 2020 20:00
> To: gdb-patches@sourceware.org
> Cc: Zoran Zaric <Zoran.Zaric@amd.com>
> Subject: [PATCH 00/30] Allow location description on the DWARF stack
>
> Based on gdb master: 13f11b0b61ca2620611b08eeaece0ce62c862f4b
>
> 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://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fllvm
> .org%2Fdocs%2FAMDGPUDwarfExtensionsForHeterogeneousDebugging.html&amp;
> data=04%7C01%7Ctony.tye%40amd.com%7Cdc391fc1b8c64df8009408d89b8855c1%7
> C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637430357142209676%7CUnkno
> wn%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiL
> CJXVCI6Mn0%3D%7C1000&amp;sdata=UmXqMslVerr06k4NgA83e6vJf63dfxl3VFzipev
> UA%2BQ%3D&amp;reserved=0
>
> 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 fifteen 
> patches could be viewed as a standalone subset that introduces a 
> welcome cleanup of the DWARF expression evaluator module.
>
> The patch series is based on a patch that is already in the review 
> process, so it had to be included in this series as well. The review 
> for that patch can be found here:
>
> https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Fsour
> ceware.org%2Fpipermail%2Fgdb-patches%2F2020-November%2F173638.html&amp
> ;data=04%7C01%7Ctony.tye%40amd.com%7Cdc391fc1b8c64df8009408d89b8855c1%
> 7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637430357142209676%7CUnkn
> own%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwi
> LCJXVCI6Mn0%3D%7C1000&amp;sdata=m6SzVi7zrym1UCNJRwOuDYAfNr0xww9beANmvr
> mb%2B0s%3D&amp;reserved=0
>
> But I don't mind it being discussed as a part of this patch series.
>
> Zoran Zaric (30):
>   Replace the symbol needs evaluator with a parser
>   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 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 new location description access interface
>   Add dwarf_entry factory class to expr.c
>   Change DWARF stack to use new dwarf_entry classes
>   Remove dwarf_expr_context from expr.h interface
>   Rename and update the piece_closure structure
>   Move read_addr_from_reg function to frame.c
>   Add frame info check to DW_OP_reg operations
>   Remove DWARF expression composition check
>   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                             | 3406 +++++++++++++++--
>  gdb/dwarf2/expr.h                             |  275 +-
>  gdb/dwarf2/frame.c                            |  163 +-
>  gdb/dwarf2/loc.c                              | 1885 +++------
>  gdb/dwarf2/loc.h                              |   34 +-
>  gdb/f-lang.c                                  |    2 +-
>  gdb/findvar.c                                 |    4 +-
>  gdb/frame.c                                   |   36 +-
>  gdb/testsuite/gdb.dwarf2/dw2-llvm-offset.exp  |  328 ++
>  .../gdb.dwarf2/dw2-llvm-piece-end.exp         |  191 +
>  .../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.python/py-framefilter-invalidarg.exp  |    2 +-
>  gdb/testsuite/lib/dwarf.exp                   |    8 +
>  gdb/valops.c                                  |  123 +-
>  gdb/value.c                                   |   68 +-
>  gdb/value.h                                   |    6 +-
>  include/dwarf2.def                            |    6 +
>  23 files changed, 4841 insertions(+), 2114 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

Intel Deutschland GmbH
Registered Address: Am Campeon 10-12, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, https://nam11.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.intel.de%2F&amp;data=04%7C01%7Ctony.tye%40amd.com%7Cdc391fc1b8c64df8009408d89b8855c1%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C637430357142209676%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&amp;sdata=uO9stvNjdJ61MIzPiJydQdL0wzxq5UtfmMeuz1QSQAs%3D&amp;reserved=0
Managing Directors: Christin Eisenschmid, Gary Kershaw Chairperson of the Supervisory Board: Nicole Lau Registered Office: Munich Commercial Register: Amtsgericht Muenchen HRB 186928

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

* Re: [PATCH 01/30] Replace the symbol needs evaluator with a parser
  2020-12-07 19:00 ` [PATCH 01/30] Replace the symbol needs evaluator with a parser Zoran Zaric
@ 2021-01-21 21:16   ` Tom Tromey
  2021-01-21 21:48     ` Zoran Zaric
  2021-02-23 14:15     ` Zoran Zaric
  0 siblings, 2 replies; 75+ messages in thread
From: Tom Tromey @ 2021-01-21 21:16 UTC (permalink / raw)
  To: Zoran Zaric via Gdb-patches

>>>>> "Zoran" == Zoran Zaric via Gdb-patches <gdb-patches@sourceware.org> writes:

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

Zoran> +  while (op_ptr < expr_end)
Zoran> +    {
Zoran> +      enum dwarf_location_atom op
Zoran> +	= (enum dwarf_location_atom) *op_ptr++;
Zoran> +      uint64_t uoffset;
Zoran> +      int64_t offset;

IIUC this is the same as the earlier patch, that marches through the
bytecode linearly.  I think I sent comments on the earlier submission.

Tom

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

* Re: [PATCH 02/30] Move frame context info to dwarf_expr_context
  2020-12-07 19:00 ` [PATCH 02/30] Move frame context info to dwarf_expr_context Zoran Zaric
@ 2021-01-21 21:23   ` Tom Tromey
  0 siblings, 0 replies; 75+ messages in thread
From: Tom Tromey @ 2021-01-21 21:23 UTC (permalink / raw)
  To: Zoran Zaric via Gdb-patches

>>>>> "Zoran" == Zoran Zaric via Gdb-patches <gdb-patches@sourceware.org> writes:

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

Zoran> gdb/ChangeLog:

Zoran> 	* dwarf2/expr.c (ensure_have_frame): New function.
Zoran> 	(read_addr_from_reg): Add from frame.c.
[...]

Thanks, this looks ok to me.

Tom

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

* Re: [PATCH 03/30] Remove get_frame_cfa from dwarf_expr_context
  2020-12-07 19:00 ` [PATCH 03/30] Remove get_frame_cfa from dwarf_expr_context Zoran Zaric
@ 2021-01-21 21:23   ` Tom Tromey
  0 siblings, 0 replies; 75+ messages in thread
From: Tom Tromey @ 2021-01-21 21:23 UTC (permalink / raw)
  To: Zoran Zaric via Gdb-patches

>>>>> "Zoran" == Zoran Zaric via Gdb-patches <gdb-patches@sourceware.org> writes:

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

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

Zoran> gdb/ChangeLog:

Zoran> 	* dwarf2/expr.c (dwarf_expr_context::get_frame_cfa): Remove
Zoran> 	method.
Zoran> 	(dwarf_expr_context::execute_stack_op): Call frame context info
Zoran> 	check for DW_OP_call_frame_cfa. Remove use of get_frame_cfa.
Zoran> 	* dwarf2/expr.h (dwarf_expr_context::get_frame_cfa): Remove
Zoran> 	method.
Zoran> 	* dwarf2/frame.c (dwarf_expr_context::get_frame_cfa): Remove
Zoran> 	method.
Zoran> 	* dwarf2/loc.c (dwarf_expr_context::get_frame_cfa): Remove
Zoran> 	method.

Thank you for the patch.
This is ok.

Tom

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

* Re: [PATCH 04/30] Move compilation unit info to dwarf_expr_context
  2020-12-07 19:00 ` [PATCH 04/30] Move compilation unit info to dwarf_expr_context Zoran Zaric
@ 2021-01-21 21:28   ` Tom Tromey
  2021-02-23 14:21     ` Zoran Zaric
  0 siblings, 1 reply; 75+ messages in thread
From: Tom Tromey @ 2021-01-21 21:28 UTC (permalink / raw)
  To: Zoran Zaric via Gdb-patches

>>>>> "Zoran" == Zoran Zaric via Gdb-patches <gdb-patches@sourceware.org> writes:

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

Thank you for the patch.

Zoran> +/* Ensure that a PER_CU is defined and throw an exception otherwise.  */
Zoran> +
Zoran> +static void
Zoran> +ensure_have_per_cu (struct dwarf2_per_cu_data *per_cu, const char* op_name)
Zoran> +{
Zoran> +  if (per_cu == nullptr)
Zoran> +    throw_error (GENERIC_ERROR,
Zoran> +		 _("%s evaluation requires a compilation unit."), op_name);

One thing I didn't think to ask about in an earlier patch is -- why
throw_error with GENERIC_ERROR here?  Unless there's a specific reason,
in gdb it's normal to use plain 'error' instead.

Other than that, this seems fine.

Tom

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

* Re: [PATCH 05/30] Move dwarf_call to dwarf_expr_context
  2020-12-07 19:00 ` [PATCH 05/30] Move dwarf_call " Zoran Zaric
@ 2021-01-21 21:30   ` Tom Tromey
  0 siblings, 0 replies; 75+ messages in thread
From: Tom Tromey @ 2021-01-21 21:30 UTC (permalink / raw)
  To: Zoran Zaric via Gdb-patches

>>>>> "Zoran" == Zoran Zaric via Gdb-patches <gdb-patches@sourceware.org> writes:

Zoran> From: Zoran Zaric <zoran.zaric@amd.com>
Zoran> Following the idea of merging the evaluators, the dwarf_call and
Zoran> get_frame_pc method can be moved from dwarf_expr_executor and
Zoran> dwarf_evaluate_loc_desc classes to their base class dwarf_expr_context.
Zoran> Once this is done, the get_frame_pc can be replace with lambda
Zoran> function.

Zoran> gdb/ChangeLog:

Zoran> 	* dwarf2/expr.c (dwarf_expr_context::dwarf_call): Move from
Zoran> 	dwarf_evaluate_loc_desc.
Zoran> 	(dwarf_expr_context::get_frame_pc): Replace with lambda.
Zoran> 	* dwarf2/expr.h (dwarf_expr_context::get_frame_pc): Remove
Zoran> 	method.
Zoran> 	* dwarf2/frame.c (dwarf_expr_executor::dwarf_call): Remove
Zoran> 	method.
Zoran> 	(dwarf_expr_executor::get_frame_pc): Remove method.
Zoran> 	* dwarf2/loc.c (dwarf_evaluate_loc_desc::get_frame_pc): Remove
Zoran> 	method.
Zoran> 	(dwarf_evaluate_loc_desc::dwarf_call): Move to
Zoran> 	dwarf_expr_context.
Zoran> 	(per_cu_dwarf_call): Inline function.

Thank you.  This is ok.

Tom

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

* Re: [PATCH 06/30] Move get_object_address to dwarf_expr_context
  2020-12-07 19:00 ` [PATCH 06/30] Move get_object_address " Zoran Zaric
@ 2021-01-21 21:31   ` Tom Tromey
  2021-02-23 14:33     ` Zoran Zaric
  0 siblings, 1 reply; 75+ messages in thread
From: Tom Tromey @ 2021-01-21 21:31 UTC (permalink / raw)
  To: Zoran Zaric via Gdb-patches

>>>>> "Zoran" == Zoran Zaric via Gdb-patches <gdb-patches@sourceware.org> writes:

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

Zoran> gdb/ChangeLog:

Zoran> 	* dwarf2/expr.c (dwarf_expr_context::get_object_address): Move
Zoran> 	from dwarf_evaluate_loc_desc.
Zoran> 	(class dwarf_expr_context): Add object address member to
Zoran> 	dwarf_expr_context.
Zoran> 	* dwarf2/expr.h (dwarf_expr_context::get_frame_pc): Remove
Zoran> 	method.
Zoran> 	* dwarf2/frame.c (dwarf_expr_executor::get_object_address):
Zoran> 	Remove method.
Zoran> 	* dwarf2/loc.c (dwarf_evaluate_loc_desc::get_object_address):
Zoran> 	move to dwarf_expr_context.
Zoran> 	(class dwarf_evaluate_loc_desc): Move object address member to
Zoran> 	dwarf_expr_context.

Thank you.

Zoran> +  per_cu (nullptr),
Zoran> +  obj_address (0)

Another thing I forgot earlier is that gdb is usually using inline
initialization for things like this.
 
Zoran> +  /* Object address used for the evaluation.  */
Zoran> +  CORE_ADDR obj_address;

... so this could just be " = 0".
This could be applied in the other patches as well.

Otherwise this looks good to me.

Tom

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

* Re: [PATCH 07/30] Move read_mem to dwarf_expr_context
  2020-12-07 19:00 ` [PATCH 07/30] Move read_mem " Zoran Zaric
@ 2021-01-21 21:34   ` Tom Tromey
  0 siblings, 0 replies; 75+ messages in thread
From: Tom Tromey @ 2021-01-21 21:34 UTC (permalink / raw)
  To: Zoran Zaric via Gdb-patches

>>>>> "Zoran" == Zoran Zaric via Gdb-patches <gdb-patches@sourceware.org> writes:

Zoran> gdb/ChangeLog:

Zoran> 	* dwarf2/expr.c (dwarf_expr_context::read_mem): Move from
Zoran> 	dwarf_evaluate_loc_desc.
Zoran> 	* dwarf2/frame.c (dwarf_expr_executor::read_mem): Remove
Zoran> 	method.
Zoran> 	* dwarf2/loc.c (dwarf_evaluate_loc_desc::read_mem): Move to
Zoran> 	dwarf_expr_context.

Thank you.

I think the idea here is that there's still another override of read_mem?
This isn't obvious from the patch since of course it doesn't touch that
override.

Tom

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

* Re: [PATCH 08/30] Move push_dwarf_reg_entry_value to expr.c
  2020-12-07 19:00 ` [PATCH 08/30] Move push_dwarf_reg_entry_value to expr.c Zoran Zaric
@ 2021-01-21 21:35   ` Tom Tromey
  0 siblings, 0 replies; 75+ messages in thread
From: Tom Tromey @ 2021-01-21 21:35 UTC (permalink / raw)
  To: Zoran Zaric via Gdb-patches

>>>>> "Zoran" == Zoran Zaric via Gdb-patches <gdb-patches@sourceware.org> writes:

Zoran> From: Zoran Zaric <zoran.zaric@amd.com>
Zoran> Following the idea of merging the evaluators, the
Zoran> push_dwarf_reg_entry_value method can be moved from
Zoran> dwarf_expr_executor and dwarf_evaluate_loc_desc classes
Zoran> to their base class dwarf_expr_context.

Zoran> gdb/ChangeLog:

Zoran> 	* dwarf2/expr.c
Zoran>         (dwarf_expr_context::push_dwarf_reg_entry_value): Move from
Zoran> 	dwarf_evaluate_loc_desc.
Zoran> 	* dwarf2/frame.c
Zoran> 	(dwarf_expr_executor::push_dwarf_reg_entry_value): Remove
Zoran> 	method.
Zoran> 	* dwarf2/loc.c (dwarf_expr_reg_to_entry_parameter): Expose
Zoran> 	function.
Zoran> 	(dwarf_evaluate_loc_desc::push_dwarf_reg_entry_value): Move to
Zoran> 	dwarf_expr_context.
Zoran> 	* dwarf2/loc.h (dwarf_expr_reg_to_entry_parameter): Expose
Zoran> 	function.

Thank you, this is ok.

Tom

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

* Re: [PATCH 09/30] Inline get_reg_value method of dwarf_expr_context
  2020-12-07 19:00 ` [PATCH 09/30] Inline get_reg_value method of dwarf_expr_context Zoran Zaric
@ 2021-01-21 21:36   ` Tom Tromey
  0 siblings, 0 replies; 75+ messages in thread
From: Tom Tromey @ 2021-01-21 21:36 UTC (permalink / raw)
  To: Zoran Zaric via Gdb-patches

>>>>> "Zoran" == Zoran Zaric via Gdb-patches <gdb-patches@sourceware.org> writes:

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

Zoran> gdb/ChangeLog:

Zoran> 	* dwarf2/expr.c (dwarf_expr_context::get_reg_value): Remove
Zoran> 	method.
Zoran> 	(dwarf_expr_context::execute_stack_op): Inline get_reg_value
Zoran> 	method.
Zoran> 	* dwarf2/expr.h (dwarf_expr_context::get_reg_value): Remove
Zoran> 	method.

Looks good, thank you.

Tom

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

* Re: [PATCH 10/30] Remove empty frame and full evaluators
  2020-12-07 19:00 ` [PATCH 10/30] Remove empty frame and full evaluators Zoran Zaric
@ 2021-01-21 21:37   ` Tom Tromey
  0 siblings, 0 replies; 75+ messages in thread
From: Tom Tromey @ 2021-01-21 21:37 UTC (permalink / raw)
  To: Zoran Zaric via Gdb-patches

>>>>> "Zoran" == Zoran Zaric via Gdb-patches <gdb-patches@sourceware.org> writes:

Zoran> gdb/ChangeLog:

Zoran> 	* dwarf2/frame.c (class dwarf_expr_executor): Remove class.
Zoran> 	(execute_stack_op): Instantiate dwarf_expr_context instead of
Zoran> 	dwarf_evaluate_loc_desc class.
Zoran> 	* dwarf2/loc.c (class dwarf_evaluate_loc_desc): Remove class.
Zoran> 	(dwarf2_evaluate_loc_desc_full): Instantiate dwarf_expr_context
Zoran> 	instead of dwarf_evaluate_loc_desc class.
Zoran> 	(struct evaluate_for_locexpr_baton): Derive from
Zoran> 	dwarf_expr_context.

Looks good.  Thanks for doing this, it's a nice cleanup.

Tom

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

* Re: [PATCH 01/30] Replace the symbol needs evaluator with a parser
  2021-01-21 21:16   ` Tom Tromey
@ 2021-01-21 21:48     ` Zoran Zaric
  2021-02-23 14:15     ` Zoran Zaric
  1 sibling, 0 replies; 75+ messages in thread
From: Zoran Zaric @ 2021-01-21 21:48 UTC (permalink / raw)
  To: Tom Tromey, Zoran Zaric via Gdb-patches

> Zoran> +  while (op_ptr < expr_end)
> Zoran> +    {
> Zoran> +      enum dwarf_location_atom op
> Zoran> +        = (enum dwarf_location_atom) *op_ptr++;
> Zoran> +      uint64_t uoffset;
> Zoran> +      int64_t offset;
>
> IIUC this is the same as the earlier patch, that marches through the
> bytecode linearly.  I think I sent comments on the earlier submission.
>
> Tom

Thank you for looking at my patches Tom.

I realize that they require a fairly bit of effort considering the area 
in gdb that they are addressing.

We ended the previous discussion thread on an agreement that this is not 
the best solution for the case when producer "hides" some information 
inside of the DWARF expression. I am not sure if this is even a reality 
with the existing compilers, but it is a valid concern.

At this stage it is hard to solve that issue, but at least we would 
solve the issue of a valid DWARF expression breaking the symbol needs 
evaluator.

Would you be OK for this symbol needs change to go in as is and then 
solve the other issue when the evaluator redesign is accepted?

With the new evaluator design, it should be a straightforward thing to 
get rid of this symbol needs expression parser.

Zoran


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

* Re: [PATCH 11/30] Merge evaluate_for_locexpr_baton evaluator
  2020-12-07 19:00 ` [PATCH 11/30] Merge evaluate_for_locexpr_baton evaluator Zoran Zaric
@ 2021-02-08 21:21   ` Tom Tromey
  0 siblings, 0 replies; 75+ messages in thread
From: Tom Tromey @ 2021-02-08 21:21 UTC (permalink / raw)
  To: Zoran Zaric via Gdb-patches

>>>>> "Zoran" == Zoran Zaric via Gdb-patches <gdb-patches@sourceware.org> writes:

Zoran> gdb/ChangeLog:

Zoran> 	* dwarf2/expr.c (dwarf_expr_context::read_mem): Merge with
Zoran> 	evaluate_for_locexpr_baton implementation.
Zoran> 	* dwarf2/loc.c (class evaluate_for_locexpr_baton): Remove
Zoran> 	class.
Zoran> 	(evaluate_for_locexpr_baton::read_mem): Move to
Zoran> 	dwarf_expr_context.
Zoran> 	(dwarf2_locexpr_baton_eval): Instantiate dwarf_expr_context
Zoran> 	instead of evaluate_for_locexpr_baton class.

Thank you.  This is ok.

Tom

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

* Re: [PATCH 12/30] Move piece_closure and its support to expr.c
  2020-12-07 19:00 ` [PATCH 12/30] Move piece_closure and its support to expr.c Zoran Zaric
@ 2021-02-08 21:32   ` Tom Tromey
  2021-02-09 14:53     ` Zoran Zaric
  0 siblings, 1 reply; 75+ messages in thread
From: Tom Tromey @ 2021-02-08 21:32 UTC (permalink / raw)
  To: Zoran Zaric via Gdb-patches

>>>>> "Zoran" == Zoran Zaric via Gdb-patches <gdb-patches@sourceware.org> writes:

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

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

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

Seems reasonable.

Zoran> 	* dwarf2/loc.h (invalid_synthetic_pointer): Expose function.

I didn't understand the need for this change.
This function doesn't seem to be referenced elsewhere in the patch.

Maybe it's needed for some subsequent change?

Tom

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

* Re: [PATCH 13/30] Make DWARF evaluator return a single struct value
  2020-12-07 19:00 ` [PATCH 13/30] Make DWARF evaluator return a single struct value Zoran Zaric
@ 2021-02-08 21:35   ` Tom Tromey
  2021-02-09 14:55     ` Zoran Zaric
  0 siblings, 1 reply; 75+ messages in thread
From: Tom Tromey @ 2021-02-08 21:35 UTC (permalink / raw)
  To: Zoran Zaric via Gdb-patches

>>>>> "Zoran" == Zoran Zaric via Gdb-patches <gdb-patches@sourceware.org> writes:

Zoran> From: Zoran Zaric <zoran.zaric@amd.com>
Zoran> The patch is addressing the issue of class users writing and reading
Zoran> the internal data of the dwarf_expr_context class.

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

Zoran> gdb/ChangeLog:

Zoran> 	* dwarf2/expr.c (pieced_value_funcs): Chenge to static
Zoran> 	function.
Zoran> 	(allocate_piece_closure): Change to static function.
Zoran> 	(dwarf_expr_context::fetch_result): New function.
Zoran> 	* dwarf2/expr.h (struct piece_closure): Remove declaration.
Zoran> 	(struct dwarf_expr_context): fetch_result new declaration.
Zoran> 	fetch, fetch_address and fetch_in_stack_memory members move
Zoran> 	to private.
Zoran> 	(allocate_piece_closure): Remove.
Zoran> 	* dwarf2/frame.c (execute_stack_op): Change to use
Zoran> 	fetch_result.
Zoran> 	* dwarf2/loc.c (dwarf2_evaluate_loc_desc_full): Change to use
Zoran> 	fetch_result.
Zoran> 	(dwarf2_locexpr_baton_eval): Change to use fetch_result.

Thanks, this looks good.
I guess this is where invalid_synthetic_pointer is used.
I don't mind if you just leave the previous patch as-is.

Tom

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

* Re: [PATCH 14/30] Simplify dwarf_expr_context class interface
  2020-12-07 19:00 ` [PATCH 14/30] Simplify dwarf_expr_context class interface Zoran Zaric
@ 2021-02-08 21:38   ` Tom Tromey
  2021-02-09 14:56     ` Zoran Zaric
  2021-02-23 14:38     ` Zoran Zaric
  0 siblings, 2 replies; 75+ messages in thread
From: Tom Tromey @ 2021-02-08 21:38 UTC (permalink / raw)
  To: Zoran Zaric via Gdb-patches

>>>>> "Zoran" == Zoran Zaric via Gdb-patches <gdb-patches@sourceware.org> writes:

Zoran> The eval_exp method is the method that evaluates a DWARF expression
Zoran> and provides the evaluation result, in a form of a single struct
Zoran> value object that describes a location.

How about just calling this method "evaluate"?
Same length, but no abbreviations.

Otherwise this looks good to me.

Tom

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

* Re: [PATCH 15/30] Add as_lval argument to expression evaluator
  2020-12-07 19:00 ` [PATCH 15/30] Add as_lval argument to expression evaluator Zoran Zaric
@ 2021-02-08 21:41   ` Tom Tromey
  2021-02-09 15:25     ` Zoran Zaric
  0 siblings, 1 reply; 75+ messages in thread
From: Tom Tromey @ 2021-02-08 21:41 UTC (permalink / raw)
  To: Zoran Zaric via Gdb-patches

>>>>> "Zoran" == Zoran Zaric via Gdb-patches <gdb-patches@sourceware.org> writes:

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

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

Zoran> New implementation, introduces a new evaluation argument instead.

I like this idea.
While reading this, though, I was wondering if the new parameter ought
to be given to the dwarf_expr_context constructor, rather than passed to
the evaluation method.

Tom

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

* Re: [PATCH 19/30] Add new location description access interface
  2020-12-07 19:00 ` [PATCH 19/30] Add new location description access interface Zoran Zaric
@ 2021-02-08 21:46   ` Tom Tromey
  2021-02-09 16:00     ` Zoran Zaric
  0 siblings, 1 reply; 75+ messages in thread
From: Tom Tromey @ 2021-02-08 21:46 UTC (permalink / raw)
  To: Zoran Zaric via Gdb-patches

>>>>> "Zoran" == Zoran Zaric via Gdb-patches <gdb-patches@sourceware.org> writes:

Zoran> After adding interface for register and memory location access, a new
Zoran> top level interface for accessing any location, described by the
Zoran> dwarf_location class based objects, can now be defined.

Zoran> Also, the address_type method is now needed to be outside of the
Zoran> dwarf_stack_value class to allow creation of the DWARF generic type
Zoran> independently of that class.

Zoran> 	* dwarf2/expr.c (read_from_location): New function.
Zoran> 	(write_to_location): New function.
Zoran> 	(address_type): New function.
Zoran> 	* dwarf2/expr.h (address_type): Exposed function.

Zoran> +  if (dynamic_cast<const dwarf_undefined *> (location) != nullptr)

Zoran> +  else if (auto register_entry
Zoran> +	     = dynamic_cast<const dwarf_register *> (location))

Zoran> +  else if (auto memory_entry = dynamic_cast<const dwarf_memory *> (location))

I didn't read this patch too closely, but this kind of type-case should
only be done when a virtual function is unsuitable.

Tom

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

* Re: [PATCH 18/30] Add new classes that model DWARF stack element
  2020-12-07 19:00 ` [PATCH 18/30] Add new classes that model DWARF stack element Zoran Zaric
@ 2021-02-08 21:54   ` Tom Tromey
  2021-02-09 17:34     ` Zoran Zaric
  0 siblings, 1 reply; 75+ messages in thread
From: Tom Tromey @ 2021-02-08 21:54 UTC (permalink / raw)
  To: Zoran Zaric via Gdb-patches

>>>>> "Zoran" == Zoran Zaric via Gdb-patches <gdb-patches@sourceware.org> writes:

Zoran> +class dwarf_entry : public refcounted_object

I guess reference counting is needed due to how dwarf_composite works.
I suppose it's impossible to create a reference loop.  But I wonder if
ownership transfer is more correct and reference counting is not needed.

Zoran> +class dwarf_implicit : public dwarf_location
Zoran> +{
Zoran> +public:
Zoran> +
Zoran> +  dwarf_implicit (const gdb_byte* contents, size_t size,
Zoran> +		  enum bfd_endian byte_order)
Zoran> +  {
Zoran> +    m_contents.reset ((gdb_byte *) xzalloc (size));

This could just be a std::vector<gdb_byte> and m_size could be removed.

Zoran> +class dwarf_composite : public dwarf_location
Zoran> +{
Zoran> +
Zoran> +  /* A composite location gets detached from its factory object for
Zoran> +     the purpose of lval_computed resolution, which means that it
Zoran> +     needs to take care of garbage collecting its pieces.  */
Zoran> +  ~dwarf_composite () override
Zoran> +  {
Zoran> +    for (unsigned int i = 0; i < m_pieces.size (); i++)
Zoran> +      {
Zoran> +	dwarf_location* location = m_pieces[i].m_location;
Zoran> +
Zoran> +	location->decref ();
Zoran> +
Zoran> +	if (location->refcount () == 0)
Zoran> +	  delete location;

It's better to use the reference-counting smart pointer abstraction so
that manual management is not needed.

Zoran> +  void add_piece (dwarf_location* location, ULONGEST bit_size)

"dwarf_location *location" - this little typo appears in a few spots.
Perhaps this should take a ref-pointer&& (I forget exactly how the type
is spelled, you'll probably want a typedef anyway) to indicate transfer,
or a "const .. &" and then use new_reference.

Zoran> +  class piece
Zoran> +  {
Zoran> +  public:
Zoran> +    piece (dwarf_location *location, ULONGEST size)
Zoran> +    : m_location (location),
Zoran> +      m_size (size)
Zoran> +    {}
Zoran> +
Zoran> +    dwarf_location *m_location;

This could have the smart pointer type.

Tom

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

* Re: [PATCH 12/30] Move piece_closure and its support to expr.c
  2021-02-08 21:32   ` Tom Tromey
@ 2021-02-09 14:53     ` Zoran Zaric
  0 siblings, 0 replies; 75+ messages in thread
From: Zoran Zaric @ 2021-02-09 14:53 UTC (permalink / raw)
  To: Tom Tromey, Zoran Zaric via Gdb-patches


> 
> Zoran>  * dwarf2/loc.h (invalid_synthetic_pointer): Expose function.
> 
> I didn't understand the need for this change.
> This function doesn't seem to be referenced elsewhere in the patch.
> 
> Maybe it's needed for some subsequent change?
> 
> Tom
> 

Thank you Tom.

Somehow I missed that and it is used in the later patches.

I will make sure to move it to appropriate patch in the next version.

Zoran

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

* Re: [PATCH 13/30] Make DWARF evaluator return a single struct value
  2021-02-08 21:35   ` Tom Tromey
@ 2021-02-09 14:55     ` Zoran Zaric
  2021-02-09 17:13       ` Tom Tromey
  0 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2021-02-09 14:55 UTC (permalink / raw)
  To: Tom Tromey, Zoran Zaric via Gdb-patches


> 
> Zoran> From: Zoran Zaric <zoran.zaric@amd.com>
> Zoran> The patch is addressing the issue of class users writing and reading
> Zoran> the internal data of the dwarf_expr_context class.
> 
> Zoran> At this point, all conditions are met for the DWARF evaluator to return
> Zoran> an evaluation result in a form of a single struct value object.
> 
> Zoran> gdb/ChangeLog:
> 
> Zoran>  * dwarf2/expr.c (pieced_value_funcs): Chenge to static
> Zoran>  function.
> Zoran>  (allocate_piece_closure): Change to static function.
> Zoran>  (dwarf_expr_context::fetch_result): New function.
> Zoran>  * dwarf2/expr.h (struct piece_closure): Remove declaration.
> Zoran>  (struct dwarf_expr_context): fetch_result new declaration.
> Zoran>  fetch, fetch_address and fetch_in_stack_memory members move
> Zoran>  to private.
> Zoran>  (allocate_piece_closure): Remove.
> Zoran>  * dwarf2/frame.c (execute_stack_op): Change to use
> Zoran>  fetch_result.
> Zoran>  * dwarf2/loc.c (dwarf2_evaluate_loc_desc_full): Change to use
> Zoran>  fetch_result.
> Zoran>  (dwarf2_locexpr_baton_eval): Change to use fetch_result.
> 
> Thanks, this looks good.
> I guess this is where invalid_synthetic_pointer is used.
> I don't mind if you just leave the previous patch as-is.
> 
> Tom
> 

Thank you.

I was trying really hard to not have these silly patch split mistakes, 
but somehow there is always a few that slip through.

Zoran

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

* Re: [PATCH 14/30] Simplify dwarf_expr_context class interface
  2021-02-08 21:38   ` Tom Tromey
@ 2021-02-09 14:56     ` Zoran Zaric
  2021-02-23 14:38     ` Zoran Zaric
  1 sibling, 0 replies; 75+ messages in thread
From: Zoran Zaric @ 2021-02-09 14:56 UTC (permalink / raw)
  To: Tom Tromey, Zoran Zaric via Gdb-patches



On 2/8/21 9:38 PM, Tom Tromey wrote:
> [CAUTION: External Email]
> 
>>>>>> "Zoran" == Zoran Zaric via Gdb-patches <gdb-patches@sourceware.org> writes:
> 
> Zoran> The eval_exp method is the method that evaluates a DWARF expression
> Zoran> and provides the evaluation result, in a form of a single struct
> Zoran> value object that describes a location.
> 
> How about just calling this method "evaluate"?
> Same length, but no abbreviations.
> 
> Otherwise this looks good to me.
> 
> Tom
> 

Makes sense.

I will change it in the next version.

Zoran

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

* Re: [PATCH 15/30] Add as_lval argument to expression evaluator
  2021-02-08 21:41   ` Tom Tromey
@ 2021-02-09 15:25     ` Zoran Zaric
  2021-02-09 20:33       ` Tom Tromey
  0 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2021-02-09 15:25 UTC (permalink / raw)
  To: Tom Tromey, Zoran Zaric via Gdb-patches


> 
> Zoran> There are cases where the result of the expression evaluation is
> Zoran> expected to be in a form of a value and not location description.
> 
> Zoran> One place that has this requirement is dwarf_entry_parameter_to_value
> Zoran> function, but more are expected in the future. Until now, this
> Zoran> requirement was fulfilled by extending the evaluated expression with
> Zoran> a DW_OP_stack_value operation at the end.
> 
> Zoran> New implementation, introduces a new evaluation argument instead.
> 
> I like this idea.
> While reading this, though, I was wondering if the new parameter ought
> to be given to the dwarf_expr_context constructor, rather than passed to
> the evaluation method.
> 
> Tom
> 

Thank you Tom.

Originally, I had an idea to have one persistent evaluator object per 
architecture that could be called to evaluate a given expression at any 
time.

In this scenario the as_lval information needs to be an evaluation call 
argument because it could change from evaluation to evaluation.

People didn't seem to like the idea of persistent evaluator so I guess 
many more arguments (if not all) could be just moved to the constructor.

In the later patches, the whole dwarf_expr_context class is hidden in 
the expr.c file and we end up with just one dwarf2_eval_exp function 
(which can be renamed to dwarf2_evaluate) that internally creates the 
evaluator object and calls the evaluation.

In that sense, we can move most of the evaluation arguments to be a 
constructor arguments and just leave the expression stream and result 
types as evaluation method arguments.

If people really feel that there is no need for multiple calls to the 
same evaluator object in the future, I will be happy to change it in the 
next patch series.

Zoran

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

* Re: [PATCH 19/30] Add new location description access interface
  2021-02-08 21:46   ` Tom Tromey
@ 2021-02-09 16:00     ` Zoran Zaric
  2021-02-09 17:30       ` Zoran Zaric
  0 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2021-02-09 16:00 UTC (permalink / raw)
  To: Tom Tromey, Zoran Zaric via Gdb-patches


> 
> Zoran> +  if (dynamic_cast<const dwarf_undefined *> (location) != nullptr)
> 
> Zoran> +  else if (auto register_entry
> Zoran> +             = dynamic_cast<const dwarf_register *> (location))
> 
> Zoran> +  else if (auto memory_entry = dynamic_cast<const dwarf_memory *> (location))
> 
> I didn't read this patch too closely, but this kind of type-case should
> only be done when a virtual function is unsuitable.
> 
> Tom
> 
Thank you Tom for the review.

To be honest, I am not sure how to do this with virtual functions, 
considering that the information held by the different location 
descriptions categories are so very different.

The only way how I can see this being implemented if I encapsulate all 
the specifics of dealing with the different location descriptions 
(reading, writing, conversions between them and the composite/implicit 
pointer infrastructure) inside of that class hierarchy and change the 
dwarf_entry class to have a bunch of virtual functions that only make 
sense for one location description category.

The reason why I didn't implement it that way in the first place is 
because I was following the existing model of how the struct value 
handling was implemented, where instead of dynamic casting there is an 
enumeration check to figure out what is described by an object.

Don't get me wrong, I am not really against that change, but it would 
require a bit serious rework of the implementation.

So if you really feel that this is a deal breaker, I could give another 
crack at it in the next version.

Zoran





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

* Re: [PATCH 13/30] Make DWARF evaluator return a single struct value
  2021-02-09 14:55     ` Zoran Zaric
@ 2021-02-09 17:13       ` Tom Tromey
  0 siblings, 0 replies; 75+ messages in thread
From: Tom Tromey @ 2021-02-09 17:13 UTC (permalink / raw)
  To: Zoran Zaric; +Cc: Tom Tromey, Zoran Zaric via Gdb-patches

Zoran> I was trying really hard to not have these silly patch split mistakes,
Zoran> but somehow there is always a few that slip through.

Yeah, understood.  It's a pain to manage, especially since it also means
tweaking the ChangeLog.  You don't have to bother fixing it in this
case, it seems fine.  Normally it's only a real problem if it causes
some kind of regression, since that makes bisecting harder.

Tom

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

* Re: [PATCH 19/30] Add new location description access interface
  2021-02-09 16:00     ` Zoran Zaric
@ 2021-02-09 17:30       ` Zoran Zaric
  2021-02-23 14:49         ` Zoran Zaric
  0 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2021-02-09 17:30 UTC (permalink / raw)
  To: Tom Tromey, Zoran Zaric via Gdb-patches



>>
> Thank you Tom for the review.
> 
> To be honest, I am not sure how to do this with virtual functions, 
> considering that the information held by the different location 
> descriptions categories are so very different.
> 
> The only way how I can see this being implemented if I encapsulate all 
> the specifics of dealing with the different location descriptions 
> (reading, writing, conversions between them and the composite/implicit 
> pointer infrastructure) inside of that class hierarchy and change the 
> dwarf_entry class to have a bunch of virtual functions that only make 
> sense for one location description category.
> 
> The reason why I didn't implement it that way in the first place is 
> because I was following the existing model of how the struct value 
> handling was implemented, where instead of dynamic casting there is an 
> enumeration check to figure out what is described by an object.
> 
> Don't get me wrong, I am not really against that change, but it would 
> require a bit serious rework of the implementation.
> 
> So if you really feel that this is a deal breaker, I could give another 
> crack at it in the next version.
> 
> Zoran
> 

After rereading my response, I realized that I probably need to explain 
my thought process a bit more.

In the original code there were large switch cases that dealt with the 
evaluation result (aka location description) based on a result 
enumeration. This was done in two places:

- at the evaluator caller side and
- composite/implicit pointer callback functions.

I first moved those pieces to be at the expr.c side and then when I 
switched to the new classes, I have replaced the big switch statement, 
with the if/else statements that used the dynamic cast to cast to the 
appropriate class instead to referring to the enumeration to know which 
data members are valid or not.

So in a sense, the ugly code was already there. Not to mention that the 
struct value infrastructure is based on the same model.

Originally, I had an enumeration that would be checked inside of the 
dwarf_entry class and return an object of the correct class but that was 
functionally the same as having the dynamic cast so people advised me to 
just use the dynamic cast because it was already used in gdb.

So Tom, when you say that the type-case looks bad, do you mean just the 
fact that I am using the dynamic cast or the problem is more general 
because the dwarf_entry class is supposed to encapsulate all that logic?

For the dynamic cast issue, I can always switch back to the enumeration 
methodology that I've described and is similar to what was there before.

In the case of a potentially better design of the dwarf_entry classes, I 
couldn't agree more. I was trying to first mimic the previous mechanism 
so that people would find it easier to understand and accept.

The proper encapsulation was planned for a later time, but to be frank 
the struct value requires the same type of remake which is not a modest 
task in either case.

Hope this makes more sense,
Zoran

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

* Re: [PATCH 18/30] Add new classes that model DWARF stack element
  2021-02-08 21:54   ` Tom Tromey
@ 2021-02-09 17:34     ` Zoran Zaric
  2021-02-09 20:36       ` Tom Tromey
  0 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2021-02-09 17:34 UTC (permalink / raw)
  To: Tom Tromey, Zoran Zaric via Gdb-patches

Thank you Tom for the review.

> 
> Zoran> +class dwarf_entry : public refcounted_object
> 
> I guess reference counting is needed due to how dwarf_composite works.
> I suppose it's impossible to create a reference loop.  But I wonder if
> ownership transfer is more correct and reference counting is not needed.
Right, I can look into this for the next version.

> 
> Zoran> +class dwarf_implicit : public dwarf_location
> Zoran> +{
> Zoran> +public:
> Zoran> +
> Zoran> +  dwarf_implicit (const gdb_byte* contents, size_t size,
> Zoran> +                  enum bfd_endian byte_order)
> Zoran> +  {
> Zoran> +    m_contents.reset ((gdb_byte *) xzalloc (size));
> 
> This could just be a std::vector<gdb_byte> and m_size could be removed.

Agreed.

> 
> Zoran> +class dwarf_composite : public dwarf_location
> Zoran> +{
> Zoran> +
> Zoran> +  /* A composite location gets detached from its factory object for
> Zoran> +     the purpose of lval_computed resolution, which means that it
> Zoran> +     needs to take care of garbage collecting its pieces.  */
> Zoran> +  ~dwarf_composite () override
> Zoran> +  {
> Zoran> +    for (unsigned int i = 0; i < m_pieces.size (); i++)
> Zoran> +      {
> Zoran> +        dwarf_location* location = m_pieces[i].m_location;
> Zoran> +
> Zoran> +        location->decref ();
> Zoran> +
> Zoran> +        if (location->refcount () == 0)
> Zoran> +          delete location;
> 
> It's better to use the reference-counting smart pointer abstraction so
> that manual management is not needed.

Agreed.

> 
> Zoran> +  void add_piece (dwarf_location* location, ULONGEST bit_size)
> 
> "dwarf_location *location" - this little typo appears in a few spots.
> Perhaps this should take a ref-pointer&& (I forget exactly how the type
> is spelled, you'll probably want a typedef anyway) to indicate transfer,
> or a "const .. &" and then use new_reference.

I wasn't sure about using the smart pointers in the code, but I can look 
into it for the next version.

> 
> Zoran> +  class piece
> Zoran> +  {
> Zoran> +  public:
> Zoran> +    piece (dwarf_location *location, ULONGEST size)
> Zoran> +    : m_location (location),
> Zoran> +      m_size (size)
> Zoran> +    {}
> Zoran> +
> Zoran> +    dwarf_location *m_location;
> 
> This could have the smart pointer type.
> 
> Tom
> 

Same comment as before.

Zoran

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

* Re: [PATCH 16/30] Add new register access interface to expr.c
  2020-12-07 19:00 ` [PATCH 16/30] Add new register access interface to expr.c Zoran Zaric
@ 2021-02-09 19:37   ` Tom Tromey
  0 siblings, 0 replies; 75+ messages in thread
From: Tom Tromey @ 2021-02-09 19:37 UTC (permalink / raw)
  To: Zoran Zaric via Gdb-patches

>>>>> "Zoran" == Zoran Zaric via Gdb-patches <gdb-patches@sourceware.org> writes:

Zoran> 	* dwarf2/expr.c (read_from_register): New function.
Zoran> 	(write_to_register): New function.
Zoran> 	(rw_pieced_value): Now calls the read_from_register and
Zoran> 	write_to_register functions.

Zoran> +      (*optimized) = 0;
Zoran> +      (*unavailable) = 1;

I think all the spots referring to these are over-parenthesized.
The parens here can be removed.

Zoran> +  if (!(*optimized) && !(*unavailable))

Same here & elsewhere.

Tom

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

* Re: [PATCH 17/30] Add new memory access interface to expr.c
  2020-12-07 19:00 ` [PATCH 17/30] Add new memory " Zoran Zaric
@ 2021-02-09 19:45   ` Tom Tromey
  2021-02-23 15:35     ` Zoran Zaric
  0 siblings, 1 reply; 75+ messages in thread
From: Tom Tromey @ 2021-02-09 19:45 UTC (permalink / raw)
  To: Zoran Zaric via Gdb-patches

>>>>> "Zoran" == Zoran Zaric via Gdb-patches <gdb-patches@sourceware.org> writes:

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

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

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

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

Is there any way to avoid adding more functions like this?
Like could target_read_memory be used instead?

Zoran> +static void
Zoran> +xfer_from_memory (CORE_ADDR address, gdb_byte *readbuf,

This is a bit misnamed, since "from" implies reading, but really this
does both reading and writing.

Zoran> +static void
Zoran> +write_to_memory (CORE_ADDR address, const gdb_byte *buffer,
Zoran> +		 size_t length, bool stack, int *unavailable)
Zoran> +{
Zoran> +  xfer_from_memory (address, nullptr, buffer, length, stack, unavailable);
Zoran> +
Zoran> +  gdb::observers::memory_changed.notify (current_inferior (), address,
Zoran> +					 length, buffer);

This notifies the observer even if the write fails.
I do wonder, though, why some writes notify the observer and some do
not... looking e.g., at corefile.c:write_memory_unsigned_integer.

Tom

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

* Re: [PATCH 20/30] Add dwarf_entry factory class to expr.c
  2020-12-07 19:00 ` [PATCH 20/30] Add dwarf_entry factory class to expr.c Zoran Zaric
@ 2021-02-09 19:54   ` Tom Tromey
  0 siblings, 0 replies; 75+ messages in thread
From: Tom Tromey @ 2021-02-09 19:54 UTC (permalink / raw)
  To: Zoran Zaric via Gdb-patches

>>>>> "Zoran" == Zoran Zaric via Gdb-patches <gdb-patches@sourceware.org> writes:

Zoran> This patch introduces a new class in charge of creating, tracking and
Zoran> destroying dwarf_entry class based objects.

Zoran> +/* Factory class for creation and lifetime management of all DWARF
Zoran> +   entries found on a DWARF evaluation stack.  */
Zoran> +
Zoran> +class dwarf_entry_factory
Zoran> +{

I wonder why a stateful class is needed for this.
Like, why do dwarf entries require reference counting at all?
And, in this patch, they are pushed on this vector:

Zoran> +  /* List of all entries created by the factory.  */
Zoran> +  std::vector<dwarf_entry *> m_dwarf_entries;

... but nothing ever retrieves them, leading me to wonder why it's
needed.

As with earlier patches, this should probably use a smart pointer rather
than passing around bare pointers and doing manual management.
Automatically handling this kind of thing is one of the major benefits
of C++.

Zoran> +dwarf_location *
Zoran> +dwarf_entry_factory::entry_to_location (dwarf_entry *entry)
Zoran> +{
Zoran> +  /* If the given entry is already a location,
Zoran> +     just send it back to the caller.  */
Zoran> +  if (auto location = dynamic_cast<dwarf_location *> (entry))
Zoran> +    return location;
Zoran> +
Zoran> +  auto value = dynamic_cast<dwarf_value *> (entry);
Zoran> +  gdb_assert (value != nullptr);

Another situation where instead of a type-case, virtual methods are
probably preferred.

Zoran> +dwarf_value *
Zoran> +dwarf_entry_factory::entry_to_value (dwarf_entry *entry,
Zoran> +				     struct type *default_type)
Zoran> +{

Here too.

Tom

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

* Re: [PATCH 21/30] Change DWARF stack to use new dwarf_entry classes
  2020-12-07 19:00 ` [PATCH 21/30] Change DWARF stack to use new dwarf_entry classes Zoran Zaric
@ 2021-02-09 20:11   ` Tom Tromey
  0 siblings, 0 replies; 75+ messages in thread
From: Tom Tromey @ 2021-02-09 20:11 UTC (permalink / raw)
  To: Zoran Zaric via Gdb-patches

>>>>> "Zoran" == Zoran Zaric via Gdb-patches <gdb-patches@sourceware.org> writes:

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

Zoran> Once the conversion support is added, the current DWARF stack element
Zoran> can easily be swapped out.

Thank you.
This is a rather large patch, but a lot of it seems to be pretty
routine.

I had some minor notes, basically the same kind of thing as in other
patches.

Zoran> -  /* The pieces describing this variable.  */
Zoran> -  std::vector<dwarf_expr_piece> pieces;
Zoran> +  /* Location description of this variable.  */
Zoran> +  dwarf_location *location;
 
This sort of change replaces automatic management by manual management,
which ought to be avoided.

Zoran> +  /* Only expect implicit pointer and composite location
Zoran> +     description here.  */
Zoran> +  if (entry == nullptr
Zoran> +      || (dynamic_cast<dwarf_implicit_pointer *> (entry) == nullptr
Zoran> +          && dynamic_cast<dwarf_composite *> (entry) == nullptr))
Zoran> +    internal_error (__FILE__, __LINE__, _("invalid location type"));

I wonder about virtual methods here, though this is a borderline case
for me.  Probably instead of internal_error this should use gdb_assert
though.

Zoran> +  /* Reading from and writing to an implicit pointer is not meaningful,
Zoran> +     so we just skip them here.  */
Zoran> +  if (dynamic_cast<dwarf_implicit_pointer *> (entry) != nullptr)

dynamic_cast should generally be avoided if reasonably possible.
I realize it isn't always, I suspect I'm the #1 adder of dynamic_cast to
the code.  But still it's worth a try.

Zoran> +static void
Zoran> +read_pieced_value (struct value *v)
Zoran> +{
Zoran> +  rw_pieced_value (v, NULL);
Zoran> +}

Zoran> +static void
Zoran> +write_pieced_value (struct value *to, struct value *from)
Zoran> +{
Zoran> +  rw_pieced_value (to, from);
Zoran> +}
 
These both seems like perhaps some other function could just be renamed.

Zoran>  {
Zoran> +  entry_factory = new dwarf_entry_factory ();
Zoran>  }

See the previous patch; but if the factory must have state, why not just
make it a member?


Zoran> +/* Check that the current operator is either at the end of an
Zoran> +   expression, or that it is followed by a composition operator or by
Zoran> +   DW_OP_GNU_uninit (which should terminate the expression).  */
Zoran>  void dwarf_expr_require_composition (const gdb_byte *, const gdb_byte *,
Zoran>  				     const char *);
 
I like these comment additions but they seem unrelated.
They could just be a separate patch.

Tom

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

* Re: [PATCH 15/30] Add as_lval argument to expression evaluator
  2021-02-09 15:25     ` Zoran Zaric
@ 2021-02-09 20:33       ` Tom Tromey
  0 siblings, 0 replies; 75+ messages in thread
From: Tom Tromey @ 2021-02-09 20:33 UTC (permalink / raw)
  To: Zoran Zaric via Gdb-patches; +Cc: Tom Tromey, Zoran Zaric

>>>>> "Zoran" == Zoran Zaric via Gdb-patches <gdb-patches@sourceware.org> writes:

>> While reading this, though, I was wondering if the new parameter ought
>> to be given to the dwarf_expr_context constructor, rather than passed to
>> the evaluation method.

[...]
Zoran> People didn't seem to like the idea of persistent evaluator so I guess
Zoran> many more arguments (if not all) could be just moved to the
Zoran> constructor.

Zoran> In the later patches, the whole dwarf_expr_context class is hidden in
Zoran> the expr.c file and we end up with just one dwarf2_eval_exp function 
Zoran> (which can be renamed to dwarf2_evaluate) that internally creates the
Zoran> evaluator object and calls the evaluation.

Zoran> In that sense, we can move most of the evaluation arguments to be a
Zoran> constructor arguments and just leave the expression stream and result 
Zoran> types as evaluation method arguments.

Zoran> If people really feel that there is no need for multiple calls to the
Zoran> same evaluator object in the future, I will be happy to change it in
Zoran> the next patch series.

If it's all hidden, then I think it's fine either way, because the main
interface is going to be something else anyway.  So you might as well
just leave it however you've got it now.

Tom

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

* Re: [PATCH 18/30] Add new classes that model DWARF stack element
  2021-02-09 17:34     ` Zoran Zaric
@ 2021-02-09 20:36       ` Tom Tromey
  2021-02-09 21:07         ` Tom Tromey
  0 siblings, 1 reply; 75+ messages in thread
From: Tom Tromey @ 2021-02-09 20:36 UTC (permalink / raw)
  To: Zoran Zaric via Gdb-patches; +Cc: Tom Tromey, Zoran Zaric

>> Perhaps this should take a ref-pointer&& (I forget exactly how the type
>> is spelled, you'll probably want a typedef anyway) to indicate transfer,
>> or a "const .. &" and then use new_reference.

This advice of mine is a little wrong; if you pass in a "const ptr &",
then vec.push_back will automatically handle the incref.  No need for a
new_reference call.

Zoran> I wasn't sure about using the smart pointers in the code, but I can
Zoran> look into it for the next version.

See gdbsupport/gdb_ref_ptr.h.  In gdb it's normal to use a typedef for
these, e.g. from gdb_bfd.h:

    /* A gdb::ref_ptr that has been specialized for BFD objects.  */
    typedef gdb::ref_ptr<struct bfd, gdb_bfd_ref_policy> gdb_bfd_ref_ptr;

There's a refcounted-object policy class already.

It would still be good to understand why reference counting is needed
here though.  That part still doesn't make sense to me.

Tom

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

* Re: [PATCH 18/30] Add new classes that model DWARF stack element
  2021-02-09 20:36       ` Tom Tromey
@ 2021-02-09 21:07         ` Tom Tromey
  2021-02-09 21:26           ` Zoran Zaric
  0 siblings, 1 reply; 75+ messages in thread
From: Tom Tromey @ 2021-02-09 21:07 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Zoran Zaric via Gdb-patches, Zoran Zaric

>>>>> "Tom" == Tom Tromey <tom@tromey.com> writes:

Tom> There's a refcounted-object policy class already.

... which, weirdly to me at least, does not call delete.

Maybe you should just be using shared_ptr anyway.
Though to reiterate:

Tom> It would still be good to understand why reference counting is needed
Tom> here though.  That part still doesn't make sense to me.

thanks,
Tom

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

* Re: [PATCH 18/30] Add new classes that model DWARF stack element
  2021-02-09 21:07         ` Tom Tromey
@ 2021-02-09 21:26           ` Zoran Zaric
  2021-02-23 14:57             ` Zoran Zaric
  0 siblings, 1 reply; 75+ messages in thread
From: Zoran Zaric @ 2021-02-09 21:26 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Zoran Zaric via Gdb-patches


> 
>>>>>> "Tom" == Tom Tromey <tom@tromey.com> writes:
> 
> Tom> There's a refcounted-object policy class already.
> 
> ... which, weirdly to me at least, does not call delete.

Right, so this was one of the reasons why I added the factory class.

Another reason was to separate the dwarf_entry handling and conversions 
from the original evaluator class so that the class is not cluttered.

> 
> Maybe you should just be using shared_ptr anyway.

I really need to make another pass and make everything work with 
shared_ptr. The reason why I didn't do that in the first place was 
because I didn't notice smart pointers being used a lot around the code 
and even less so in the code that I was replacing.

> Though to reiterate:
> 
> Tom> It would still be good to understand why reference counting is needed
> Tom> here though.  That part still doesn't make sense to me.
> 
> thanks,
> Tom
> 

The reference counting is there because in the extensions that we are 
proposing there is a concept of nested composites and composites that 
are not completed.

Both of those can be duplicated with the DW_OP_DUP and similar operators.

This is the same reason why the closure now contains a location 
description instead of a vector of pieces like it did before.

When you add those two things together, now you need to reference count 
because the struct value (in lval_compute case) can also reference a 
location description as well as you can have multiple copies of the same 
location description on the stack.

Maybe this will make more sense in the later patches then this one.

Smart pointers have a reference counting built in so if I switch to 
those, that would probably sort out most of these issues.

Zoran

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

* Re: [PATCH 01/30] Replace the symbol needs evaluator with a parser
  2021-01-21 21:16   ` Tom Tromey
  2021-01-21 21:48     ` Zoran Zaric
@ 2021-02-23 14:15     ` Zoran Zaric
  1 sibling, 0 replies; 75+ messages in thread
From: Zoran Zaric @ 2021-02-23 14:15 UTC (permalink / raw)
  To: Tom Tromey, Zoran Zaric via Gdb-patches

>>>>>> "Zoran" == Zoran Zaric via Gdb-patches <gdb-patches@sourceware.org> writes:
> 
> Zoran> This patch addresses a design problem with the symbol_needs_eval_context
> Zoran> class. It exposes the problem by introducing two new testsuite test
> Zoran> cases.
> 
> Zoran> +  while (op_ptr < expr_end)
> Zoran> +    {
> Zoran> +      enum dwarf_location_atom op
> Zoran> +        = (enum dwarf_location_atom) *op_ptr++;
> Zoran> +      uint64_t uoffset;
> Zoran> +      int64_t offset;
> 
> IIUC this is the same as the earlier patch, that marches through the
> bytecode linearly.  I think I sent comments on the earlier submission.

Hi Tom,

I am doing a bit of a rework on my patch set and now in one of the later 
patches (when I the evaluator redesign is complete) the symbol needs 
parser is removed and a new evaluator is used instead.

This should address the concerns you have about the linear scan.

Zoran

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

* Re: [PATCH 04/30] Move compilation unit info to dwarf_expr_context
  2021-01-21 21:28   ` Tom Tromey
@ 2021-02-23 14:21     ` Zoran Zaric
  0 siblings, 0 replies; 75+ messages in thread
From: Zoran Zaric @ 2021-02-23 14:21 UTC (permalink / raw)
  To: Tom Tromey, Zoran Zaric via Gdb-patches

> 
> Zoran> +/* Ensure that a PER_CU is defined and throw an exception otherwise.  */
> Zoran> +
> Zoran> +static void
> Zoran> +ensure_have_per_cu (struct dwarf2_per_cu_data *per_cu, const char* op_name)
> Zoran> +{
> Zoran> +  if (per_cu == nullptr)
> Zoran> +    throw_error (GENERIC_ERROR,
> Zoran> +                 _("%s evaluation requires a compilation unit."), op_name);
> 
> One thing I didn't think to ask about in an earlier patch is -- why
> throw_error with GENERIC_ERROR here?  Unless there's a specific reason,
> in gdb it's normal to use plain 'error' instead.
> 
> Other than that, this seems fine.
> 
> Tom
> 

Hi Tom,

In the new patch series I will be using the NOT_AVAILABLE_ERROR 
exception because the evaluator client needs to distinguish the regular 
error and the missing context error case so that the new symbol needs 
evaluator replacement can behave the same as before.

I couldn't find a more appropriate existing error then the 
NOT_AVAILABLE_ERROR which kinda fits the purpose, but maybe I should 
define a new CONTEXT_MISSING_ERROR or something.

What do you think?

Zoran

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

* Re: [PATCH 06/30] Move get_object_address to dwarf_expr_context
  2021-01-21 21:31   ` Tom Tromey
@ 2021-02-23 14:33     ` Zoran Zaric
  0 siblings, 0 replies; 75+ messages in thread
From: Zoran Zaric @ 2021-02-23 14:33 UTC (permalink / raw)
  To: Tom Tromey, Zoran Zaric via Gdb-patches


> 
> Zoran> +  per_cu (nullptr),
> Zoran> +  obj_address (0)
> 
> Another thing I forgot earlier is that gdb is usually using inline
> initialization for things like this.
> 
> Zoran> +  /* Object address used for the evaluation.  */
> Zoran> +  CORE_ADDR obj_address;
> 
> ... so this could just be " = 0".
> This could be applied in the other patches as well.
> 
> Otherwise this looks good to me.
> 
> Tom
> 

Will be fixed in the next patch set version.

Zoran

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

* Re: [PATCH 14/30] Simplify dwarf_expr_context class interface
  2021-02-08 21:38   ` Tom Tromey
  2021-02-09 14:56     ` Zoran Zaric
@ 2021-02-23 14:38     ` Zoran Zaric
  1 sibling, 0 replies; 75+ messages in thread
From: Zoran Zaric @ 2021-02-23 14:38 UTC (permalink / raw)
  To: Tom Tromey, Zoran Zaric via Gdb-patches

> 
> Zoran> The eval_exp method is the method that evaluates a DWARF expression
> Zoran> and provides the evaluation result, in a form of a single struct
> Zoran> value object that describes a location.
> 
> How about just calling this method "evaluate"?
> Same length, but no abbreviations.
> 
> Otherwise this looks good to me.
> 
> Tom
> 

Will be changed in the next patch series.

Zoran

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

* Re: [PATCH 19/30] Add new location description access interface
  2021-02-09 17:30       ` Zoran Zaric
@ 2021-02-23 14:49         ` Zoran Zaric
  0 siblings, 0 replies; 75+ messages in thread
From: Zoran Zaric @ 2021-02-23 14:49 UTC (permalink / raw)
  To: Tom Tromey, Zoran Zaric via Gdb-patches


>> Thank you Tom for the review.
>>
>> To be honest, I am not sure how to do this with virtual functions, 
>> considering that the information held by the different location 
>> descriptions categories are so very different.
>>
>> The only way how I can see this being implemented if I encapsulate all 
>> the specifics of dealing with the different location descriptions 
>> (reading, writing, conversions between them and the composite/implicit 
>> pointer infrastructure) inside of that class hierarchy and change the 
>> dwarf_entry class to have a bunch of virtual functions that only make 
>> sense for one location description category.
>>
>> The reason why I didn't implement it that way in the first place is 
>> because I was following the existing model of how the struct value 
>> handling was implemented, where instead of dynamic casting there is an 
>> enumeration check to figure out what is described by an object.
>>
>> Don't get me wrong, I am not really against that change, but it would 
>> require a bit serious rework of the implementation.
>>
>> So if you really feel that this is a deal breaker, I could give 
>> another crack at it in the next version.
>>
>> Zoran
>>
> 
> After rereading my response, I realized that I probably need to explain 
> my thought process a bit more.
> 
> In the original code there were large switch cases that dealt with the 
> evaluation result (aka location description) based on a result 
> enumeration. This was done in two places:
> 
> - at the evaluator caller side and
> - composite/implicit pointer callback functions.
> 
> I first moved those pieces to be at the expr.c side and then when I 
> switched to the new classes, I have replaced the big switch statement, 
> with the if/else statements that used the dynamic cast to cast to the 
> appropriate class instead to referring to the enumeration to know which 
> data members are valid or not.
> 
> So in a sense, the ugly code was already there. Not to mention that the 
> struct value infrastructure is based on the same model.
> 
> Originally, I had an enumeration that would be checked inside of the 
> dwarf_entry class and return an object of the correct class but that was 
> functionally the same as having the dynamic cast so people advised me to 
> just use the dynamic cast because it was already used in gdb.
> 
> So Tom, when you say that the type-case looks bad, do you mean just the 
> fact that I am using the dynamic cast or the problem is more general 
> because the dwarf_entry class is supposed to encapsulate all that logic?
> 
> For the dynamic cast issue, I can always switch back to the enumeration 
> methodology that I've described and is similar to what was there before.
> 
> In the case of a potentially better design of the dwarf_entry classes, I 
> couldn't agree more. I was trying to first mimic the previous mechanism 
> so that people would find it easier to understand and accept.
> 
> The proper encapsulation was planned for a later time, but to be frank 
> the struct value requires the same type of remake which is not a modest 
> task in either case.
> 
> Hope this makes more sense,
> Zoran

Hi Tom,

I've invested a lot of time during the last two weeks and managed to 
change the design to be more C++ based and now most of the dynamic 
casting has been replaced with virtual functions which made the code a 
lot more elegant.

Unfortunately, there are still a few places left that use the casting 
but those are limited to the evaluator parts that model the logic behind 
how some of the DWARF operations are defined in the standard, which 
don't really make sense to be a DWARF entry class virtual functions.

This will all make more sense when I submit the new version of my patch 
series in a few days.

Zoran



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

* Re: [PATCH 18/30] Add new classes that model DWARF stack element
  2021-02-09 21:26           ` Zoran Zaric
@ 2021-02-23 14:57             ` Zoran Zaric
  0 siblings, 0 replies; 75+ messages in thread
From: Zoran Zaric @ 2021-02-23 14:57 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Zoran Zaric via Gdb-patches


> 
>>
>> Maybe you should just be using shared_ptr anyway.
> 
> I really need to make another pass and make everything work with 
> shared_ptr. The reason why I didn't do that in the first place was 
> because I didn't notice smart pointers being used a lot around the code 
> and even less so in the code that I was replacing.

In the new patch set, the implementation is now completely changed to 
use shared pointers.

> 
>> Though to reiterate:
>>
>> Tom> It would still be good to understand why reference counting is 
>> needed
>> Tom> here though.  That part still doesn't make sense to me.
>>
>> thanks,
>> Tom
>>
> 
> The reference counting is there because in the extensions that we are 
> proposing there is a concept of nested composites and composites that 
> are not completed.
> 

After the shared pointers switch, the only part that still uses the 
reference counting is the closure class for the computed struct value 
object support.

Unfortunately, this cant be changed without a bigger change of how those 
computed struct value callbacks work and I feel that is outside of the 
scope of this patch set.

Zoran

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

* Re: [PATCH 17/30] Add new memory access interface to expr.c
  2021-02-09 19:45   ` Tom Tromey
@ 2021-02-23 15:35     ` Zoran Zaric
  0 siblings, 0 replies; 75+ messages in thread
From: Zoran Zaric @ 2021-02-23 15:35 UTC (permalink / raw)
  To: Tom Tromey, Zoran Zaric via Gdb-patches


> 
> Zoran> DWARF expression evaluator is currently using a few different
> Zoran> interfaces for memory access: write_memory_with_notification,
> Zoran> read_value_memory, read_memory.
> 
> Zoran> They all seem incosistent, while some of them even need a struct
> Zoran> value typed argument to be present.
> 
> Zoran> This patch is simplifying that interface by replacing it with two new
> Zoran> low level functions: read_from_memory and write_to_memory.
> 
> Zoran> The advantage of this new interface is that it behaves in the same way
> Zoran> as the register access interface from the previous patch. Both of these
> Zoran> have the same error returning policy, which will be usefull for the
> Zoran> following patches.
> 
> Is there any way to avoid adding more functions like this?
> Like could target_read_memory be used instead?

Unfortunately not, The target_read_memory doesn't have a way to target 
the TARGET_OBJECT_STACK for example and if used will break some of the 
existing gdb tests that involve caching.

I am open to suggestions but I couldn't find an appropriate replacement 
to the previously used read_value_memory which is tied to the struct 
value infrastructure.

> 
> Zoran> +static void
> Zoran> +xfer_from_memory (CORE_ADDR address, gdb_byte *readbuf,
> 
> This is a bit misnamed, since "from" implies reading, but really this
> does both reading and writing.
> 
> Zoran> +static void
> Zoran> +write_to_memory (CORE_ADDR address, const gdb_byte *buffer,
> Zoran> +                 size_t length, bool stack, int *unavailable)
> Zoran> +{
> Zoran> +  xfer_from_memory (address, nullptr, buffer, length, stack, unavailable);
> Zoran> +
> Zoran> +  gdb::observers::memory_changed.notify (current_inferior (), address,
> Zoran> +                                         length, buffer);
> 
> This notifies the observer even if the write fails.
> I do wonder, though, why some writes notify the observer and some do
> not... looking e.g., at corefile.c:write_memory_unsigned_integer.
> 
> Tom
> 

This is something that still puzzles me to be honest.

I was trying not to break the existing algorithm because I didn't have 
the time to get into the details behind the write notify logic.

I did have a version where I didn't do any notification in the evaluator 
and I didn't see any of the test failing, but that doesn't really prove 
anything.

Zoran

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

end of thread, other threads:[~2021-02-23 15:35 UTC | newest]

Thread overview: 75+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-07 19:00 [PATCH 00/30] Allow location description on the DWARF stack Zoran Zaric
2020-12-07 19:00 ` [PATCH 01/30] Replace the symbol needs evaluator with a parser Zoran Zaric
2021-01-21 21:16   ` Tom Tromey
2021-01-21 21:48     ` Zoran Zaric
2021-02-23 14:15     ` Zoran Zaric
2020-12-07 19:00 ` [PATCH 02/30] Move frame context info to dwarf_expr_context Zoran Zaric
2021-01-21 21:23   ` Tom Tromey
2020-12-07 19:00 ` [PATCH 03/30] Remove get_frame_cfa from dwarf_expr_context Zoran Zaric
2021-01-21 21:23   ` Tom Tromey
2020-12-07 19:00 ` [PATCH 04/30] Move compilation unit info to dwarf_expr_context Zoran Zaric
2021-01-21 21:28   ` Tom Tromey
2021-02-23 14:21     ` Zoran Zaric
2020-12-07 19:00 ` [PATCH 05/30] Move dwarf_call " Zoran Zaric
2021-01-21 21:30   ` Tom Tromey
2020-12-07 19:00 ` [PATCH 06/30] Move get_object_address " Zoran Zaric
2021-01-21 21:31   ` Tom Tromey
2021-02-23 14:33     ` Zoran Zaric
2020-12-07 19:00 ` [PATCH 07/30] Move read_mem " Zoran Zaric
2021-01-21 21:34   ` Tom Tromey
2020-12-07 19:00 ` [PATCH 08/30] Move push_dwarf_reg_entry_value to expr.c Zoran Zaric
2021-01-21 21:35   ` Tom Tromey
2020-12-07 19:00 ` [PATCH 09/30] Inline get_reg_value method of dwarf_expr_context Zoran Zaric
2021-01-21 21:36   ` Tom Tromey
2020-12-07 19:00 ` [PATCH 10/30] Remove empty frame and full evaluators Zoran Zaric
2021-01-21 21:37   ` Tom Tromey
2020-12-07 19:00 ` [PATCH 11/30] Merge evaluate_for_locexpr_baton evaluator Zoran Zaric
2021-02-08 21:21   ` Tom Tromey
2020-12-07 19:00 ` [PATCH 12/30] Move piece_closure and its support to expr.c Zoran Zaric
2021-02-08 21:32   ` Tom Tromey
2021-02-09 14:53     ` Zoran Zaric
2020-12-07 19:00 ` [PATCH 13/30] Make DWARF evaluator return a single struct value Zoran Zaric
2021-02-08 21:35   ` Tom Tromey
2021-02-09 14:55     ` Zoran Zaric
2021-02-09 17:13       ` Tom Tromey
2020-12-07 19:00 ` [PATCH 14/30] Simplify dwarf_expr_context class interface Zoran Zaric
2021-02-08 21:38   ` Tom Tromey
2021-02-09 14:56     ` Zoran Zaric
2021-02-23 14:38     ` Zoran Zaric
2020-12-07 19:00 ` [PATCH 15/30] Add as_lval argument to expression evaluator Zoran Zaric
2021-02-08 21:41   ` Tom Tromey
2021-02-09 15:25     ` Zoran Zaric
2021-02-09 20:33       ` Tom Tromey
2020-12-07 19:00 ` [PATCH 16/30] Add new register access interface to expr.c Zoran Zaric
2021-02-09 19:37   ` Tom Tromey
2020-12-07 19:00 ` [PATCH 17/30] Add new memory " Zoran Zaric
2021-02-09 19:45   ` Tom Tromey
2021-02-23 15:35     ` Zoran Zaric
2020-12-07 19:00 ` [PATCH 18/30] Add new classes that model DWARF stack element Zoran Zaric
2021-02-08 21:54   ` Tom Tromey
2021-02-09 17:34     ` Zoran Zaric
2021-02-09 20:36       ` Tom Tromey
2021-02-09 21:07         ` Tom Tromey
2021-02-09 21:26           ` Zoran Zaric
2021-02-23 14:57             ` Zoran Zaric
2020-12-07 19:00 ` [PATCH 19/30] Add new location description access interface Zoran Zaric
2021-02-08 21:46   ` Tom Tromey
2021-02-09 16:00     ` Zoran Zaric
2021-02-09 17:30       ` Zoran Zaric
2021-02-23 14:49         ` Zoran Zaric
2020-12-07 19:00 ` [PATCH 20/30] Add dwarf_entry factory class to expr.c Zoran Zaric
2021-02-09 19:54   ` Tom Tromey
2020-12-07 19:00 ` [PATCH 21/30] Change DWARF stack to use new dwarf_entry classes Zoran Zaric
2021-02-09 20:11   ` Tom Tromey
2020-12-07 19:00 ` [PATCH 22/30] Remove dwarf_expr_context from expr.h interface Zoran Zaric
2020-12-07 19:00 ` [PATCH 23/30] Rename and update the piece_closure structure Zoran Zaric
2020-12-07 19:00 ` [PATCH 24/30] Move read_addr_from_reg function to frame.c Zoran Zaric
2020-12-07 19:00 ` [PATCH 25/30] Add frame info check to DW_OP_reg operations Zoran Zaric
2020-12-07 19:00 ` [PATCH 26/30] Remove DWARF expression composition check Zoran Zaric
2020-12-07 19:00 ` [PATCH 27/30] Add support for any location description in CFI Zoran Zaric
2020-12-07 19:00 ` [PATCH 28/30] Add DWARF operations for byte and bit offset Zoran Zaric
2020-12-07 19:00 ` [PATCH 29/30] Add support for DW_OP_LLVM_undefined operation Zoran Zaric
2020-12-07 19:00 ` [PATCH 30/30] Add support for nested composite locations Zoran Zaric
2020-12-08 14:48 ` [PATCH 00/30] Allow location description on the DWARF stack Metzger, Markus T
2020-12-08 16:17   ` Simon Marchi
2020-12-09  0:30   ` Tye, Tony

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).