x86's REP STOS instruction is a memset (with element size!) with the
ability to choose a direction of execution.
Additionally it has a feature where if it faults part-way through the
copy, an application can catch the fault and continue afterwards to know
how many bytes got copied.
RCX is the counter which decrements for each element, and RDI is the
memory pointer. On fault these will reflect the last location that was
attempted to be written. FEX doesn't support this behaviour which makes
our lives easier.
Without supporting that feature, this turns in to a directional memset
by element size. Let's remove all the multiple blocks and just emit a
single IR operation to improve performance of the JIT.
Our generated code here was terrible, the IR was terrible, multiblock is
always slow with RA. Just a general overall improvement.
With profiling pressure-vessel this change deletes the hottest block that
appeared in the trace. This instruction is very commonly used for
memsetting a region to zero so it should be quite fast.
We can also optimize REP MOVS in the future with a Memcpy IR operation
in a similar fashion.
Additionally in the future these can be optimized to use ARM's new MOPS
instructions since the most common case is memset by byte. Which is when
we should expose the "Fast REP STOS" CPUID bit. Both setp/setm/sete and
cpyfp/cpyfm/cpyfe match `REP STOS` and `REP MOVS` respectively.
This operation directly matches what the x86 STOS instruction does
without supporting its faulting behaviour.
STOS faulting behaviour is that RCX and RDI get updated to the last word
written. Which is something that FEX hasn't ever supported.
Like with the vector registers, we can remove all implicit conversion
operators except the ones that convert down to the base PRegister class.
With this, all of the registers are now adequately constrained, so we
shouldn't have any wonky implicit conversions happening anymore.
Ensures that we're always explicit about the size of a register when
using APIs that enforce it.
The only implicit conversions we keep are conversions that convert down
to Register, but not anything that converts up the hierarchy or across
it.
A few vector instructions that take register lists often require
vector registers within the list to be sequential in the form of an
increasing list modulo the register file size.
For example:
v1, v2, v3, v4
v31, v0, v1, v2
both fit these requirements.
This will be used to enforce this restriction within the asserts from a
single place.
A large amount of size handling in this category is just decrementing
the size by 1, so we can tidy up a bunch of conditionals by just doing
that instead.
This is known to cause issues in some cases. We hit the first game that
checks for this bit and early exits if it is found.
Lets the MMORPG Tibia run in non-VM situations.
Looks like they have more checks for VMs other than hypervisor bit, so
running under Parallels still won't work. Running on bare Linux is fine.
Centralizes all the shifting behavior and whatnot into a single
function, making everything much more localized.
Also gets rid of a lot of magic constants related to the encoding limits
of immediates.
When we were taking a signal that had a misaligned stack, we would store
the host stack at a weird offset.
After that point when we were trying to sigreturn we wouldn't know the
alignment of the stack coming back and we would try loading the host
stack from the wrong offset. Easy fix is to just align the host stack
location.
Fixes Ender Lilies, which was consistently crashing from a SIGCHLD due
to having a misaligned stack.
Side-change: Move the cookie check to the start of the restore. Doesn't
make sense to check the cookie after restoring state since it could be
quite wrong.
Now that we've moved MOVHPS over to sharing the implementation of
MOVHPD, the FLAGS_SF_HIGH_XMM_REG is now unused.
Since we're supporting AVX, this flag is kind of weird in terms of
behavior, since what determines the high part of a register is now
situationally different.
Also it's much more explicit to perform the insert directly in the
implementation of instructions, than relying on a flag to do it for us.
So, instead of keeping it around, we can reclaim it as unused for use
with any necessary behavior that we would require in the future.
These instructions essentially have the same behavior. This also allows
us to remove the only used instance of FLAGS_SF_HIGH_XMM_REG, which,
given that we now support AVX, has ambiguous use.
While we're at it, we can expand the tests to make use of the store to
memory variant.
Also removes an erroneous copy-pasted comment about ZEXTing. This is
from the MOVQ implementation function. MOVHPS/MOVHPD don't do any
ZEXTing, they either store to memory or insert into a register.