Provide a function to create an ELF image in a format GDB expects.
Outside of ELF platforms this image doesn't make much sense, and in
MacOS a Mach-O memory image is required: see
https://chromium.googlesource.com/v8/v8.git/+/refs/heads/main/src/diagnostics/gdb-jit.cc#1802
Since GDB requires active runtime addresses for the code, copying the
generated code into the image will not help. Instead, `build_gdb_image`
writes the runtime addresses of the code into a NOBITS `.text` section.
The new JIT::GDB namespace enables registering JITted objects into GDB
dynamically.
Its clients just have to ensure the memory they give to
`register_into_gdb` is in a format that GDB can understand, either by
generating an object file in memory with debug info + symbols or by
registering a custom debug info parser.
None of these are implemented by this API; it only implements the
registering part and lets the client to choose the data format.
GDB JIT Interface:
https://sourceware.org/gdb/current/onlinedocs/gdb.html/JIT-Interface.html#JIT-Interface
Things to take into account from v8's docs, some of which we may
improve: https://v8.dev/docs/gdb-jit#known-limitations
Now that x86-specific Assembler will be compiled on every architecture
we can't rely on void* being the right width.
It also fixes compilation on targets which have void*
be different length from u64 (WASM in particular).
This is in preparation for making LibJIT support multiple architectures.
Assembler will now be typedefed to the specific assembler
for a particular architecture.
Additionally, there's now JIT_ARCH_SUPPORTED which is defined on
architectures which LibJIT supports.
This makes JS::JIT::Compiler less architecture-specific
and unifies aligning the stack into a single operation,
where previously we were doing it separately for preserved registers
and for stack arguments.
Flip the order from save-registers,enter and leave,restore-registers
to enter,save-register and restore-registers,leave.
This way the return address is next to the saved frame pointer like
unwinding routines expect.
This makes the code a bit more readable and in conjunction with the
ModRM helper should prevent some operand ordering bugs.
This also includes one incidental bugfix:
`sign_extend_32_to_64_bits`, was not setting the `REX.R` bit when
appropriate,
And one size obvious optimization:
We may now elide the REX prefix on `xor eax, eax` as storing to a 32 bit
register clears the upper 32 bit of said register, which is wanted here.
This also widens the argument coverage of some helpers, to allow
memory offsets, this also consolidates the displacement size choosing.
This also stops us from some out argument ordering bugs, as we now just
need to look up the correct calling convention and call the correct
function.
Since all conditional instructions use a certain number of bits to
encode the condition type (from my observation of `Jcc`, `SETcc` and
`CMOVcc`), let's abuse that to deduplicate some code!
This adds a `Condition` enum that defines the type of condition we are
jumping based on, whose underlying values are the values that must be
encoded to trigger each condition.
The `test` instruction will have the same result as `cmp` when
comparing to zero, so let's always emit that code. This has no effect
until the following commit.
Instead of JIT::Assembler making the decision for everyone and forcing
out every caller-saved register in the ABI onto the stack, we now leave
that decision to users of JIT::Assembler.
Instead of adjusting the stack pointer before/after making native calls,
just make sure we come out of enter() with the stack pointer aligned
for making calls.
This is strictly a code size reduction. :^)
This replaces the existing sized immediate operands with a unified
immediate operand that leaves the size handling to the assembler,
instead of the user.
This has 2 benefits:
1. The user doesn't need to know which specific operand size the
instruction expects when using it
2. The assembler automatically chooses the minimal operand size that
fits the given value, resulting in smaller code size without any
additional effort from the user. While the change is small, it still
has a noticeable effect on performance (since it increases the I$ hit
rate), resulting in 5% speedup on kraken a-star.
If a mov instruction is meant to be patchable, we don't want to rewrite
it as a xor, since that removes the slot where we'd patch in the right
value later.
Also, make sure to set both size bits in the REX prefix for xoring a
register with itself.