[PATCH 000/203] Refactor expressions

Tom Tromey
2021-01-01T21:47:31Z

I have long thought that struct expression is the worst data structure
in GDB. In fact, I've sometimes told people it is the worst data
structure I've encountered in my career. I've wanted to rewrite it
for many years, and this year I finally found a workable plan and the
motivation to do so. This series is the result.

The new approach is what I consider to be the most obvious data
structure to use here: a base class represents a single node of an
expression tree, and concrete subclasses implement the various
operations. Each object maintains its own storage. In the end,
'struct expression' is just a simple wrapper around the root node of a
tree. The weird things in the current code -- the strange way storage
is handled, and the need to "rotate" the expression after creation --
are gone.

The series is, of course, extremely long. This is partly due to my
desire to split it up so that each patch is reasonably understandable,
but also partly because it is just unavoidable.

Conceptually it can be divided into multiple sub-series. I didn't
submit these separately, though, as they don't really make sense
independently. I've already submitted any patch that was truly
separate. (These aren't all in yet, though, and this series depends
on them.)

Here are the sub-series:

1. Split functions out from the evaluators.

Each evaluator function is fairly long, and mixes knowledge about
the structure of the expression (how the exp_elements are laid out)
with knowledge of the semantics of the operation.

This part of the series, which is about 70 patches, splits out the
semantics of most operations into their own functions. These
functions can be then be reused -- which is helpful for ensuring
that changes don't sneak in.

In this part of the series, sometimes the newly-introduced
functions have extraneous arguments. This was done partly so that
some could be reused by generic code in part 2; but also partly
because it was just simpler to write patches by rote.

2. Introduce 'class operation' and its subclasses.

This sub-series is around 100 patches. It introduces the new base
class for an expression operation, and then proceeds to add
operations for every necessary opcode. In some cases multiple such
operations are needed -- for example when multiple languages
implement an opcode in different ways.

Some discussion of the design & tradeoffs appear in the "Introduce
class operation" patch.

3. Convert existing code to use operations.

This is a short sub-series of around 10 patches. Each parser is
converted separately, as are DTrace and SystemTap probes

4. Delete obsolete code.

The final 20 patches or so are concerned with removing all the code
that is now obsolete, and with doing some minor tidying of the
result.

The overall approach here is to replace the current data structure --
but only the data structure. I didn't generally try to clean up
things that seemed a bit weird.

I also didn't try to add new features, like async evaluation. After
starting the project, I felt that trying to combine this refactoring
with another, different one would be too difficult.

This is all on the branch t/expression-rewrite-incr in my github.

Regression tested on x86-64 Fedora 32. I also built it on PPC Linux
to ensure that the ppc-linux-nat.c changes would build.

Tom