Some of the design decisions apparent above are arguable.
First, note that you don't need different bytecodes for different operand sizes. You can generate code without knowing how big the stack elements actually are on the target. If the target only supports 32-bit ints, and you don't send any 64-bit bytecodes, everything just works. The observation here is that the MIPS and the Alpha have only fixed-size registers, and you can still get C's semantics even though most instructions only operate on full-sized words. You just need to make sure everything is properly sign-extended at the right times. So there is no need for 32- and 64-bit variants of the bytecodes. Just implement everything using the largest size you support.
GDB should certainly check to see what sizes the target supports, so the
user can get an error earlier, rather than later. But this information
is not necessary for correctness.
log_not, and swap the order of the operands, yielding all four asymmetrical comparison operators. For example,
(x <= y)is
! (x > y), which is
! (y < x).
log_not is equivalent to
const8 0 equal; it's used in half
the relational operators.
ext n is equivalent to
rsh_signed, where s is the size of the stack elements;
refm and reg bytecodes when the value
should be signed. See the next bulleted item.
zero_ext n is equivalent to
log_and; it's used whenever we push the value of a register, because we
can't assume the upper bits of the register aren't garbage.
refoperators, and we need the
extbytecode anyway for accessing bitfields.
refoperators again, and
ref32is only one byte longer.
refn operators have to support unaligned fetches?
In particular, structure bitfields may be several bytes long, but follow no alignment rules; members of packed structures are not necessarily aligned either.
In general, there are many cases where unaligned references occur in
correct C code, either at the programmer's explicit request, or at the
compiler's discretion. Thus, it is simpler to make the GDB agent
bytecodes work correctly in all circumstances than to make GDB guess in
each case whether the compiler did the usual thing.
Suppose we have multiple branch ops with different offset sizes. As I generate code left-to-right, all my jumps are forward jumps (there are no loops in expressions), so I never know the target when I emit the jump opcode. Thus, I have to either always assume the largest offset size, or do jump relaxation on the code after I generate it, which seems like a big waste of time.
I can imagine a reasonable expression being longer than 256 bytes. I can't imagine one being longer than 64k. Thus, we need 16-bit offsets. This kind of reasoning is so bogus, but relaxation is pathetic.
The other approach would be to generate code right-to-left. Then I'd
always know my offset size. That might be fun.
regbytecode take a 16-bit register number?
x->y->z, the agent must record the values of
x->yas well as the value of
tracebytecodes make the interpreter less general?
tracebytecodes, they don't get in its way.
trace_quickconsume its arguments the way everything else does?
trace_quickis a kludge to save space; it only exists so we needn't write
tracebefore every memory reference. Therefore, it's okay for it not to consume its arguments; it's meant for a specific context in which we know exactly what it should do with the stack. If we're going to have a kludge, it should be an effective kludge.
tracein those cases.
Whatever we decide to do with
trace16, we should at least leave
opcode 0x30 reserved, to remain compatible with the customer who added