Commit Graph
88 Commits
Author SHA1 Message Date
Sintendo da9546cb2f Jit64: Merge subfx and subfcx
Again, logic and optimizations are mostly the same so it makes sense.
2021-09-22 22:29:20 +02:00
Sintendo a725eb80ff Jit64: Merge addx and addcx
They are extremely similar and can share nearly all optimizations.
2021-09-22 22:28:54 +02:00
Sintendo a9e0f21373 Jit64: Split arithcx into addcx and subfcx
JitArm64 also opts to separate the two. The shared logic makes less
sense once we start adding more optimizations.
2021-09-21 22:11:11 +02:00
Sintendo 7de4623d48 Jit64: mtfsfx - Optimized masking
The masking logic can be eliminated when the mask is known to be all
ones. This case is very common.

Before:
66 48 0F 7E F0       movq        rax,xmm6
8B 55 5C             mov         edx,dword ptr [rbp+5Ch]
83 E0 FF             and         eax,0FFFFFFFFh
83 E2 00             and         edx,0
0B C2                or          eax,edx
89 45 5C             mov         dword ptr [rbp+5Ch],eax
48 8D 15 C3 3C FE 01 lea         rdx,[19520020h]
83 E0 07             and         eax,7
0F AE 14 82          ldmxcsr     dword ptr [rdx+rax*4]

After:
66 48 0F 7E F0       movq        rax,xmm6
89 45 5C             mov         dword ptr [rbp+5Ch],eax
48 8D 15 2A D1 FD 01 lea         rdx,[19440000h]
83 E0 07             and         eax,7
0F AE 14 82          ldmxcsr     dword ptr [rdx+rax*4]
2021-06-26 17:31:31 +02:00
Sintendo 18aaf488b0 Jit64: subfic - Optimize constants for d != a
These optimizations were already present, but only when d == a. They
also make sense when this condition does not hold.

- imm == 0
Before:
41 BB 00 00 00 00    mov         r11d,0
45 2B DF             sub         r11d,r15d

After:
45 8B DF             mov         r11d,r15d
41 F7 DB             neg         r11d

- imm == -1
Before:
41 BD FF FF FF FF    mov         r13d,0FFFFFFFFh
44 2B EE             sub         r13d,esi
0F 93 45 68          setae       byte ptr [rbp+68h]

After:
44 8B EE             mov         r13d,esi
41 F7 D5             not         r13d
C6 45 68 01          mov         byte ptr [rbp+68h],1
2021-06-16 20:33:02 +02:00
Sintendo 2cafa0a960 Jit64: divwux - Prefer three-operand IMUL
By taking advantage of three-operand IMUL, we can eliminate a MOV
instruction. This is a small code size win. However, due to IMUL sign
extending the immediate value to 64 bits, we can only apply this when
the magic number's most significant bit is zero.

To ensure this can actually happen, we also minimize the magic number by
checking for trailing zeroes.

Example (Unsigned division by 18)
Before:
41 BE E4 38 8E E3    mov         r14d,0E38E38E4h
4D 0F AF F5          imul        r14,r13
49 C1 EE 24          shr         r14,24h

After:
4D 69 F5 39 8E E3 38 imul        r14,r13,38E38E39h
49 C1 EE 22          shr         r14,22h
2021-05-06 19:54:33 +02:00
Sintendo 47e16133e5 Jit64: divwx - Eliminate XOR for constant dividend
We normally check for division by zero to know if we should set the
destination register to zero with a XOR. However, when the divisor and
destination registers are the same the explicit zeroing can be omitted.
In addition, some of the surrounding branching can be simplified as
well.

Before:
45 85 FF             test        r15d,r15d
75 05                jne         normal_path
45 33 FF             xor         r15d,r15d
EB 0C                jmp         done
normal_path:
B8 5A 00 00 00       mov         eax,5Ah
99                   cdq
41 F7 FF             idiv        eax,r15d
44 8B F8             mov         r15d,eax
done:

After:
45 85 FF             test        r15d,r15d
74 0C                je          done
B8 5A 00 00 00       mov         eax,5Ah
99                   cdq
41 F7 FF             idiv        eax,r15d
44 8B F8             mov         r15d,eax
done:
2021-04-24 21:32:21 +02:00
Sintendo abc4c8f601 Jit64: divwx - Eliminate MOV for division by power of 2
Division by a power of two can be slightly improved when the
destination and dividend registers are the same.

Before:
8B C6                mov         eax,esi
85 C0                test        eax,eax
8D 70 03             lea         esi,[rax+3]
0F 49 F0             cmovns      esi,eax
C1 FE 02             sar         esi,2

After:
85 F6                test        esi,esi
8D 46 03             lea         eax,[rsi+3]
0F 48 F0             cmovs       esi,eax
C1 FE 02             sar         esi,2
2021-04-24 19:28:23 +02:00
Sintendo 246adf0d6d Jit64: divwx - Eliminate MOV for division by 2
When destination and input registers match, a redundant MOV instruction
can be eliminated.

Before:
8B C7                mov         eax,edi
8B F8                mov         edi,eax
C1 EF 1F             shr         edi,1Fh
03 F8                add         edi,eax
D1 FF                sar         edi,1

After:
8B C7                mov         eax,edi
C1 EF 1F             shr         edi,1Fh
03 F8                add         edi,eax
D1 FF                sar         edi,1
2021-04-24 18:53:21 +02:00
Sintendoandmerry defe7162f5 Jit64: divwx - Simplify divisor == -1 case
Suggested by @MerryMage. Thanks!

Co-authored-by: merry <[email protected]>
2021-03-07 18:29:12 +01:00
Sintendo 83f38388a1 Jit64: divwx - Micro-optimize default case
Both the normal path and the overflow path end with the same
instruction, so their tails can be merged.

Before:
41 8B C7             mov         eax,r15d
45 85 C0             test        r8d,r8d
74 0D                je          overflow
3D 00 00 00 80       cmp         eax,80000000h
75 0E                jne         normal_path
41 83 F8 FF          cmp         r8d,0FFFFFFFFh
75 08                jne         normal_path
overflow:
C1 F8 1F             sar         eax,1Fh
44 8B F0             mov         r14d,eax
EB 07                jmp         done
normal_path:
99                   cdq
41 F7 F8             idiv        eax,r8d
44 8B F0             mov         r14d,eax
done:

After:
41 8B C7             mov         eax,r15d
45 85 C0             test        r8d,r8d
74 0D                je          overflow
3D 00 00 00 80       cmp         eax,80000000h
75 0B                jne         normal_path
41 83 F8 FF          cmp         r8d,0FFFFFFFFh
75 05                jne         normal_path
overflow:
C1 F8 1F             sar         eax,1Fh
EB 04                jmp         done
normal_path:
99                   cdq
41 F7 F8             idiv        eax,r8d
done:
44 8B F0             mov         r14d,eax
2021-03-07 18:29:12 +01:00
Sintendo 1865035798 Jit64: divwx - Optimize division by 2
...and let's optimize a divisor of 2 ever so slightly for good measure.
I wouldn't have bothered, but most GameCube games seem to hit this on
launch.

- Division by 2
Before:
41 BE 02 00 00 00    mov         r14d,2
41 8B C2             mov         eax,r10d
45 85 F6             test        r14d,r14d
74 0D                je          overflow
3D 00 00 00 80       cmp         eax,80000000h
75 0E                jne         normal_path
41 83 FE FF          cmp         r14d,0FFFFFFFFh
75 08                jne         normal_path
overflow:
C1 F8 1F             sar         eax,1Fh
44 8B F0             mov         r14d,eax
EB 07                jmp         done
normal_path:
99                   cdq
41 F7 FE             idiv        eax,r14d
44 8B F0             mov         r14d,eax
done:

After:
45 8B F2             mov         r14d,r10d
41 C1 EE 1F          shr         r14d,1Fh
45 03 F2             add         r14d,r10d
41 D1 FE             sar         r14d,1
2021-03-07 18:29:12 +01:00
Sintendo 0637a7ec59 Jit64: divwx - Optimize power-of-two divisors
Power-of-two divisors can be done more elegantly, so handle them
separately.

- Division by 4
Before:
41 BD 04 00 00 00    mov         r13d,4
41 8B C0             mov         eax,r8d
45 85 ED             test        r13d,r13d
74 0D                je          overflow
3D 00 00 00 80       cmp         eax,80000000h
75 0E                jne         normal_path
41 83 FD FF          cmp         r13d,0FFFFFFFFh
75 08                jne         normal_path
overflow:
C1 F8 1F             sar         eax,1Fh
44 8B E8             mov         r13d,eax
EB 07                jmp         done
normal_path:
99                   cdq
41 F7 FD             idiv        eax,r13d
44 8B E8             mov         r13d,eax
done:

After:
45 85 C0             test        r8d,r8d
45 8D 68 03          lea         r13d,[r8+3]
45 0F 49 E8          cmovns      r13d,r8d
41 C1 FD 02          sar         r13d,2
2021-03-07 18:29:12 +01:00
Sintendo 530475dce8 Jit64: divwx - Micro-optimize certain divisors
When the multiplier is positive (which is the most common case), we can
generate slightly better code.

- Division by 30307
Before:
49 63 C5             movsxd      rax,r13d
48 69 C0 65 6B 32 45 imul        rax,rax,45326B65h
4C 8B C0             mov         r8,rax
48 C1 E8 3F          shr         rax,3Fh
49 C1 F8 2D          sar         r8,2Dh
44 03 C0             add         r8d,eax

After:
49 63 C5             movsxd      rax,r13d
4C 69 C0 65 6B 32 45 imul        r8,rax,45326B65h
C1 E8 1F             shr         eax,1Fh
49 C1 F8 2D          sar         r8,2Dh
44 03 C0             add         r8d,eax
2021-03-07 18:29:12 +01:00
Sintendo 95698c5ae1 Jit64: divwx - Optimize constant divisor
Optimize division by a constant into multiplication. This method is also
used by GCC and LLVM.

We also add optimized paths for divisors 0, 1, and -1, because they
don't work using this method. They don't occur very often, but are
necessary for correctness.

- Division by 1
Before:
41 BF 01 00 00 00    mov         r15d,1
41 8B C5             mov         eax,r13d
45 85 FF             test        r15d,r15d
74 0D                je          overflow
3D 00 00 00 80       cmp         eax,80000000h
75 0E                jne         normal_path
41 83 FF FF          cmp         r15d,0FFFFFFFFh
75 08                jne         normal_path
overflow:
C1 F8 1F             sar         eax,1Fh
44 8B F8             mov         r15d,eax
EB 07                jmp         done
normal_path:
99                   cdq
41 F7 FF             idiv        eax,r15d
44 8B F8             mov         r15d,eax
done:

After:
45 8B FD             mov         r15d,r13d

- Division by 30307
Before:
41 BA 63 76 00 00    mov         r10d,7663h
41 8B C5             mov         eax,r13d
45 85 D2             test        r10d,r10d
74 0D                je          overflow
3D 00 00 00 80       cmp         eax,80000000h
75 0E                jne         normal_path
41 83 FA FF          cmp         r10d,0FFFFFFFFh
75 08                jne         normal_path
overflow:
C1 F8 1F             sar         eax,1Fh
44 8B C0             mov         r8d,eax
EB 07                jmp         done
normal_path:
99                   cdq
41 F7 FA             idiv        eax,r10d
44 8B C0             mov         r8d,eax
done:

After:
49 63 C5             movsxd      rax,r13d
48 69 C0 65 6B 32 45 imul        rax,rax,45326B65h
4C 8B C0             mov         r8,rax
48 C1 E8 3F          shr         rax,3Fh
49 C1 F8 2D          sar         r8,2Dh
44 03 C0             add         r8d,eax

- Division by 30323
Before:
41 BA 73 76 00 00    mov         r10d,7673h
41 8B C5             mov         eax,r13d
45 85 D2             test        r10d,r10d
74 0D                je          overflow
3D 00 00 00 80       cmp         eax,80000000h
75 0E                jne         normal_path
41 83 FA FF          cmp         r10d,0FFFFFFFFh
75 08                jne         normal_path
overflow:
C1 F8 1F             sar         eax,1Fh
44 8B C0             mov         r8d,eax
EB 07                jmp         00000000161737E7
normal_path:
99                   cdq
41 F7 FA             idiv        eax,r10d
44 8B C0             mov         r8d,eax
done:

After:
49 63 C5             movsxd      rax,r13d
4C 69 C0 19 25 52 8A imul        r8,rax,0FFFFFFFF8A522519h
49 C1 E8 20          shr         r8,20h
44 03 C0             add         r8d,eax
C1 E8 1F             shr         eax,1Fh
41 C1 F8 0E          sar         r8d,0Eh
44 03 C0             add         r8d,eax
2021-03-07 18:29:01 +01:00
Sintendo 5bb8798df6 JitCommon: Signed 32-bit division magic constants
Add a function to calculate the magic constants required to optimize
signed 32-bit division.

Since this optimization is not exclusive to any particular architecture,
JitCommon seemed like a good place to put this.
2021-03-07 18:27:36 +01:00
Sintendo c9adc60d73 Jit64: divwx - Special case dividend == 0
Zero divided by any number is still zero. For whatever reason, this case
shows up frequently too.

Before:
B8 00 00 00 00       mov         eax,0
85 F6                test        esi,esi
74 0C                je          overflow
3D 00 00 00 80       cmp         eax,80000000h
75 0C                jne         normal_path
83 FE FF             cmp         esi,0FFFFFFFFh
75 07                jne         normal_path
overflow:
C1 F8 1F             sar         eax,1Fh
8B F8                mov         edi,eax
EB 05                jmp         done
normal_path:
99                   cdq
F7 FE                idiv        eax,esi
8B F8                mov         edi,eax
done:

After:
Nothing!
2021-03-07 18:27:30 +01:00
Sintendo c081e3f2b3 Jit64: divwx - Optimize constant dividend
When the dividend is known at compile time, we can eliminate some of the
branching and precompute the result for the overflow case.

Before:
B8 54 D3 E6 02       mov         eax,2E6D354h
85 FF                test        edi,edi
74 0C                je          overflow
3D 00 00 00 80       cmp         eax,80000000h
75 0C                jne         normal_path
83 FF FF             cmp         edi,0FFFFFFFFh
75 07                jne         normal_path
overflow:
C1 F8 1F             sar         eax,1Fh
8B F8                mov         edi,eax
EB 05                jmp         done
normal_path:
99                   cdq
F7 FF                idiv        eax,edi
8B F8                mov         edi,eax
done:

After:
85 FF                test        edi,edi
75 04                jne         normal_path
33 FF                xor         edi,edi
EB 0A                jmp         done
normal_path:
B8 54 D3 E6 02       mov         eax,2E6D354h
99                   cdq
F7 FF                idiv        eax,edi
8B F8                mov         edi,eax
done:

Fairly common with constant dividend of zero. Non-zero values occur
frequently in Ocarina of Time Master Quest.
2021-03-07 18:25:08 +01:00
Sintendo 2454bd5ba6 Jit64: Add optional argument to GenerateOverflow
This allows setting the overflow flag based on any condition code.
Defaults to NO (no overflow).
2021-03-05 17:14:45 +01:00
Sintendo 6a51c1564f Jit64: addx revisited
This doesn't really add any new optimizations, but fixes an issue that
prevented the optimizations introduced in #8551 and #8755 from being
applied in specific cases. A similar issue was solved for subfx as part
of #9425.

Consider the case where the destination register is also an input
register and happens to hold an immediate value. This results in a set
of constraints that forces the RegCache to allocate a register and move
the immediate value into it for us. By the time we check for immediate
values in the JIT, we're too late.

We solve this by refactoring the code in such a way that we can check
for immediates before involving the RegCache.

- Example 1
Before:
41 BF 00 68 00 CC    mov         r15d,0CC006800h
44 03 FF             add         r15d,edi

After:
44 8D BF 00 68 00 CC lea         r15d,[rdi-33FF9800h]

- Example 2
Before:
41 BE 00 00 00 00    mov         r14d,0
44 03 F7             add         r14d,edi

After:
44 8B F7             mov         r14d,edi

- Example 3
Before:
41 BD 03 00 00 00    mov         r13d,3
44 03 6D 8C          add         r13d,dword ptr [rbp-74h]

After:
44 8B 6D 8C          mov         r13d,dword ptr [rbp-74h]
41 83 C5 03          add         r13d,3
2021-01-26 21:25:00 +01:00
Sintendo 8964612577 Jit64: boolx - Eliminate read dependency
For certain occurrences of nandx/norx, we declare a ReadWrite constraint
on the destination register, even though the value of the destination
register is irrelevant. This false dependency would force the RegCache
to generate a redundant MOV when the destination register wasn't already
assigned to a host register.

Example 1:
BF 00 00 00 00       mov         edi,0
8B FE                mov         edi,esi
F7 D7                not         edi

Example 2:
8B 7D 80             mov         edi,dword ptr [rbp-80h]
8B FE                mov         edi,esi
F7 D7                not         edi
2021-01-17 21:32:28 +01:00
Sintendo 305cd31bd9 Jit64: Fix FinalizeCarryOverflow XER[OV/SO]
FinalizeCarryOverflow didn't maintain XER[OV/SO] properly due to an
oversight. Here's the code it would generate:

0:  9c                      pushf
1:  80 65 3b fe             and    BYTE PTR [rbp+0x3b],0xfe
5:  71 04                   jno    b <jno>
7:  c6 45 3b 03             mov    BYTE PTR [rbp+0x3b],0x3
000000000000000b <jno>:
b:  9d                      popf

At first glance it seems reasonable. The host flags are carefully
preserved with PUSHF. The AND instruction clears XER[OV]. Next, an
conditional branch checks the host's overflow flag and, if needed, skips
over a MOV that sets XER[OV/SO]. Finally, host flags are restored with
POPF.

However, the AND instruction also clears the host's overflow flag. As a
result, the branch that follows it is always taken and the MOV is always
skipped. The end result is that XER[OV] is always cleared while XER[SO]
is left unchanged.

Putting POPF immediately after the AND would fix this, but we already
have GenerateOverflow doing it correctly (and without the PUSHF/POPF
shenanigans too). So let's just use that instead.
2021-01-09 22:52:18 +01:00
Sintendo df70077e6b JitArm64: subfx - Special case a == b 2021-01-05 18:52:24 +01:00
Sintendo c0be34aa81 Jit64: subfx - Special case a == b
Soul Calibur II does this.

Before:
2B F6                sub         esi,esi

After:
Nothing!
2021-01-05 00:26:26 +01:00
Sintendo b0be20560f Jit64: subfx - Special case b == 0
Happens in Super Mario Sunshine. You could probably do something similar
for b == -1 (like we do for subfic), but I couldn't find any titles that
do this.

- Case 1: d == a

Before:
41 8B C7             mov         eax,r15d
41 BF 00 00 00 00    mov         r15d,0
44 2B F8             sub         r15d,eax

After:
41 F7 DF             neg         r15d

- Case 2: d != a

Before:
BF 00 00 00 00       mov         edi,0
41 2B FD             sub         edi,r13d

After:
41 8B FD             mov         edi,r13d
F7 DF                neg         edi
2021-01-05 00:11:16 +01:00
Sintendo 57548b456b Jit64: subfx - Special case a == 0
Occurs a bunch of times in Super Mario Sunshine.

Before:
41 83 EE 00          sub         r14d,0

After:
Nothing!
2021-01-04 23:54:15 +01:00
Sintendo b805223108 Jit64: subfx - Optimize more constant a cases
Consider the case where d and a refer to the same PowerPC register,
which is known to hold an immediate value by the RegCache. We place a
ReadWrite constraint on this register and bind it to an x86 register.
The RegCache then allocates a new register, initializes it with the
immediate, and returns a RCX64Reg for both d and a.

At this point information about the immediate value becomes unreachable.
In the case of subfx, this generates suboptimal code:

Before 1:
BF 1E 00 00 00       mov         edi,1Eh       <- done by RegCache
8B C7                mov         eax,edi
8B FE                mov         edi,esi
2B F8                sub         edi,eax

Before 2:
BE 00 AC 3F 80       mov         esi,803FAC00h <- done by RegCache
8B C6                mov         eax,esi
8B 75 EC             mov         esi,dword ptr [rbp-14h]
2B F0                sub         esi,eax

The solution is to explicitly handle the constant a case before having
the RegCache allocate registers for us.

After 1:
8D 7E E2             lea         edi,[rsi-1Eh]

After 2:
8B 75 EC             mov         esi,dword ptr [rbp-14h]
81 EE 00 AC 3F 80    sub         esi,803FAC00h
2021-01-04 23:02:22 +01:00
Sintendo 67d2fa11f1 Jit64: srawx - Handle constant zero input
Shifting zero by any amount always gives zero.

Before:
41 B9 00 00 00 00    mov         r9d,0
41 8B CF             mov         ecx,r15d
49 C1 E1 20          shl         r9,20h
49 D3 F9             sar         r9,cl
49 C1 E9 20          shr         r9,20h

After:
Nothing, register is set to constant zero.

Before:
41 B8 00 00 00 00    mov         r8d,0
41 8B CF             mov         ecx,r15d
49 C1 E0 20          shl         r8,20h
49 D3 F8             sar         r8,cl
41 8B C0             mov         eax,r8d
49 C1 E8 20          shr         r8,20h
44 85 C0             test        eax,r8d
0F 95 45 58          setne       byte ptr [rbp+58h]

After:
C6 45 58 00          mov         byte ptr [rbp+58h],0

Occurs a bunch of times in Super Mario Sunshine. Since this is an
arithmetic shift a similar optimization can be done for constant -1
(0xFFFFFFFF), but I couldn't find any game where this happens.
2020-12-25 19:30:51 +01:00
Sintendo 10d65519f9 Jit64: slwx - Handle constant zero input
Shifting zero by any amount always gives zero.

Before:
41 BF 00 00 00 00    mov         r15d,0
8B CF                mov         ecx,edi
49 D3 E7             shl         r15,cl
45 8B FF             mov         r15d,r15d

After:
Nothing, register is set to constant zero.

All games I've tried hit this optimization on launch. In Soul Calibur II
it occurs very frequently during gameplay.
2020-12-25 19:30:51 +01:00
Sintendo 1a52fdf7e3 Jit64: rlwnmx - Optimize rotate by constant
Only removes the scratch register and a MOV, but hey.

Before:
B9 02 00 00 00       mov         ecx,2
41 8B F5             mov         esi,r13d
D3 C6                rol         esi,cl
83 E6 01             and         esi,1

After:
41 8B F5             mov         esi,r13d
C1 C6 02             rol         esi,2
83 E6 01             and         esi,1
2020-12-25 19:30:51 +01:00
Sintendo cb70d5ee4f Jit64: srawix - Handle constant input register
Much like we did for srawx. This was already implemented on JitArm64.

Before:
B8 00 00 00 00       mov         eax,0
8B F0                mov         esi,eax
C1 E8 1F             shr         eax,1Fh
23 C6                and         eax,esi
D1 FE                sar         esi,1
88 45 58             mov         byte ptr [rbp+58h],al

After:
C6 45 58 00          mov         byte ptr [rbp+58h],0
2020-12-25 19:30:51 +01:00
Sintendo 8ac40162da Jit64: srawx - Handle constant input registers
If both input registers hold known values at compile time, we can just
calculate the result on the spot.

Code has mostly been copied from JitArm64 where it had already been implemented.

Before:
BF FF FF FF FF       mov         edi,0FFFFFFFFh
8B C7                mov         eax,edi
C1 FF 10             sar         edi,10h
C1 E0 10             shl         eax,10h
85 F8                test        eax,edi
0F 95 45 58          setne       byte ptr [rbp+58h]

After:
C6 45 58 01          mov         byte ptr [rbp+58h],1
2020-12-25 19:30:51 +01:00
Sintendo b968120f8a Jit64: srawx - Optimize shift by constant
More efficient code can be generated if the shift amount is known at
compile time. We can once again take advantage of shifts with the shift
amount in an 8-bit immediate to eliminate ECX as a scratch register,
reducing register pressure and removing the occasional spill. We can
also do 32-bit shifts instead of 64-bit operations.

We recognize four distinct cases:

- The special case where we're dealing with the PowerPC's quirky shift
  amount masking. If the shift amount is a number from 32 to 63, all
  bits are shifted out and the result it either all zeroes or all ones.

Before:
B9 F0 FF FF FF       mov         ecx,0FFFFFFF0h
8B F7                mov         esi,edi
48 C1 E6 20          shl         rsi,20h
48 D3 FE             sar         rsi,cl
8B C6                mov         eax,esi
48 C1 EE 20          shr         rsi,20h
85 F0                test        eax,esi
0F 95 45 58          setne       byte ptr [rbp+58h]

After:
8B F7                mov         esi,edi
C1 FE 1F             sar         esi,1Fh
0F 95 45 58          setne       byte ptr [rbp+58h]

- The shift amount is zero. Not calculation needs to be done, just clear
  the carry flag.

Before:
B9 00 00 00 00       mov         ecx,0
49 C1 E5 20          shl         r13,20h
49 D3 FD             sar         r13,cl
41 8B C5             mov         eax,r13d
49 C1 ED 20          shr         r13,20h
44 85 E8             test        eax,r13d
0F 95 45 58          setne       byte ptr [rbp+58h]

After:
C6 45 58 00          mov         byte ptr [rbp+58h],0

- The carry flag doesn't need to be computed. Just do the arithmetic
  shift.

Before:
B9 02 00 00 00       mov         ecx,2
48 C1 E7 20          shl         rdi,20h
48 D3 FF             sar         rdi,cl
48 C1 EF 20          shr         rdi,20h

After:
C1 FF 02             sar         edi,2

- The carry flag must be computed. In addition to the arithmetic shift,
  we do a shift to the left and and them together to know if any ones
  were shifted out. It's still better than before, because we can do
  32-bit shifts.

Before:
B9 02 00 00 00       mov         ecx,2
49 C1 E5 20          shl         r13,20h
49 D3 FD             sar         r13,cl
41 8B C5             mov         eax,r13d
49 C1 ED 20          shr         r13,20h
44 85 E8             test        eax,r13d
0F 95 45 58          setne       byte ptr [rbp+58h]

After:
41 8B C5             mov         eax,r13d
41 C1 FD 02          sar         r13d,2
C1 E0 1E             shl         eax,1Eh
44 85 E8             test        eax,r13d
0F 95 45 58          setne       byte ptr [rbp+58h]
2020-12-25 19:30:51 +01:00
Sintendo 17dc870847 Jit64: slwx - Optimize shift by constant
More efficient code can be generated if the shift amount is known at
compile time. Similar optimizations were present in JitArm64 already,
but were missing in Jit64.

- By using an 8-bit immediate we can eliminate the need for ECX as a
  scratch register, thereby reducing register pressure and occasionally
  eliminating a spill.

Before:
B9 18 00 00 00       mov         ecx,18h
41 8B F7             mov         esi,r15d
48 D3 E6             shl         rsi,cl
8B F6                mov         esi,esi

After:
41 8B CF             mov         ecx,r15d
C1 E1 18             shl         ecx,18h

- PowerPC has strange shift amount masking behavior which is emulated
  using 64-bit shifts, even though we only care about a 32-bit result.
  If the shift amount is known, we can handle this special case
  separately, and use 32-bit shift instructions otherwise. We also no
  longer need to clear the upper 32 bits of the register.

Before:
BE F8 FF FF FF       mov         esi,0FFFFFFF8h
8B CE                mov         ecx,esi
41 8B F4             mov         esi,r12d
48 D3 E6             shl         rsi,cl
8B F6                mov         esi,esi

After:
Nothing, register is set to constant zero.

- A shift by zero becomes a simple MOV.

Before:
BE 00 00 00 00       mov         esi,0
8B CE                mov         ecx,esi
41 8B F3             mov         esi,r11d
48 D3 E6             shl         rsi,cl
8B F6                mov         esi,esi

After:
41 8B FB             mov         edi,r11d
2020-12-25 19:30:51 +01:00
Sintendo 17db359979 Jit64: srwx - Optimize shift by constant
More efficient code can be generated if the shift amount is known at
compile time. Similar optimizations were present in JitArm64 already,
but were missing in Jit64.

- By using an 8-bit immediate we can eliminate the need for ECX as a
  scratch register, thereby reducing register pressure and occasionally
  eliminating a spill.

Before:
B9 18 00 00 00       mov         ecx,18h
45 8B C1             mov         r8d,r9d
49 D3 E8             shr         r8,cl

After:
45 8B C1             mov         r8d,r9d
41 C1 E8 18          shr         r8d,18h

- PowerPC has strange shift amount masking behavior which is emulated
  using 64-bit shifts, even though we only care about a 32-bit result.
  If the shift amount is known, we can handle this special case
  separately, and use 32-bit shift instructions otherwise.

Before:
B9 F8 FF FF FF       mov         ecx,0FFFFFFF8h
45 8B C1             mov         r8d,r9d
49 D3 E8             shr         r8,cl

After:
Nothing, register is set to constant zero.

- A shift by zero becomes a simple MOV.

Before:
B9 00 00 00 00       mov         ecx,0
45 8B C1             mov         r8d,r9d
49 D3 E8             shr         r8,cl

After:
45 8B C1             mov         r8d,r9d
2020-12-25 19:30:51 +01:00
Sintendo 2e4e2ad1ff Jit64: subfic - Handle constants
Occurs surprisingly often. Prevents generating silly code like this:

BE 03 00 00 00       mov         esi,3
83 EE 08             sub         esi,8
0F 93 45 58          setae       byte ptr [rbp+58h]
2020-12-25 19:30:51 +01:00
Sintendo 567357e12d JitArm64: srawix - Fix undefined behavior
Signed bitwise left shift invokes UB when shifting a negative value.
2020-12-21 11:05:22 +01:00
Sintendo 97eb616719 JitArm64: srawx - Fix undefined behavior
Signed bitwise left shift invokes UB when shifting a negative value.
2020-12-21 11:01:42 +01:00
Sintendo 3499cedde4 Jit64: fselx - Skip MOVAPS + MOVSD (SSE4.1)
For the non-packed variant of this instruction, a MOVSD instruction was
generated to copy only the lower 64 bits of XMM1 to the destination
register. This was done in order to keep the destination register's
upper half intact.

However, when register c and the destination register are the same,
there is no need for this copy. Because the registers match and due to
the way the mask is generated, BLENDVPD will end up taking the upper
half from the destination register, as intended.

Additionally, the MOVAPS to copy Rc into XMM1 can also be skipped.

Before:
66 0F 57 C0          xorpd       xmm0,xmm0
F2 41 0F C2 C6 06    cmpnlesd    xmm0,xmm14
41 0F 28 CE          movaps      xmm1,xmm14
66 41 0F 38 15 CA    blendvpd    xmm1,xmm10,xmm0
F2 44 0F 10 F1       movsd       xmm14,xmm1

After:
66 0F 57 C0          xorpd       xmm0,xmm0
F2 41 0F C2 C6 06    cmpnlesd    xmm0,xmm14
66 45 0F 38 15 F2    blendvpd    xmm14,xmm10,xmm0
2020-10-03 18:28:41 +02:00
Sintendo 9ac324aed3 Jit64: fselx - Skip MOVSD (AVX)
For the non-packed variant of this instruction, a MOVSD instruction was
generated to copy only the lower 64 bits of XMM1 to the destination
register. This was done in order to keep the destination register's
upper half intact.

However, when register c and the destination register are the same,
there is no need for this copy. Because the registers match and due to
the way the mask is generated, VBLENDVPD will end up taking the upper
half from the destination register, as intended.

Before:
66 0F 57 C0          xorpd       xmm0,xmm0
F2 41 0F C2 C6 06    cmpnlesd    xmm0,xmm14
C4 C3 09 4B CA 00    vblendvpd   xmm1,xmm14,xmm10,xmm0
F2 44 0F 10 F1       movsd       xmm14,xmm1

After:
66 0F 57 C0          xorpd       xmm0,xmm0
F2 41 0F C2 C6 06    cmpnlesd    xmm0,xmm14
C4 43 09 4B F2 00    vblendvpd   xmm14,xmm14,xmm10,xmm0
2020-10-03 18:28:27 +02:00
Sintendo 363f3f82bb DSPJitRegCache: Simplify WriteReg
The intent here is to generate a more compact instruction if a 32-bit
immediate can be zero-extended to the desired 64-bit immediate.

Nowadays the emitter is smart enough to do this for us, so this logic is
redundant.
2020-08-05 10:23:28 +02:00
Sintendo 618d261b91 DSPJitMultiplier: addpaxz - AND constant directly
There's no need to load the 64-bit immediate into a temporary register.
x64 will sign-extend 32-bit immediates to 64 bits, giving us the exact
value we need in this case.

48 C7 C0 00 00 FF FF    mov         rax,0FFFFFFFFFFFF0000h
48 21 C2                and         rdx,rax

48 81 E2 00 00 FF FF    and         rdx,0FFFFFFFFFFFF0000h
2020-08-05 10:22:51 +02:00
Sintendo 49a4e09719 DSPJit: Replace/optimize LEAs
- LEA is a bit silly when the source and the destination are the same. A
simple ADD or SHL will do in those cases.

66 8D 04 45 00 00 00 00 lea         ax,[rax*2]
66 03 C0                add         ax,ax

48 8D 04 00             lea         rax,[rax+rax]
48 03 C0                add         rax,rax

66 8D 14 D5 00 00 00 00 lea         dx,[rdx*8]
66 C1 E2 03             shl         dx,3

- When scaling by 2, consider summing the register with itself instead.
The former always needs a 32-bit displacement, so the sum is more
compact.

66 8D 14 45 00 00 00 00 lea dx,[rax*2]
66 8D 14 00             lea dx,[rax+rax]
2020-08-05 10:22:50 +02:00
Sintendo 08bdeefe05 Jit64AsmCommon: Use AVX in ConvertDoubleToSingle
Using AVX we can eliminate another MOVAPS instruction here.

Before:
0F 28 C8                movaps      xmm1,xmm0
66 0F DB 0D CF 2C 00 00 pand        xmm1,xmmword ptr [1F8D283B220h]

After:
C5 F9 DB 0D D2 2C 00 00 vpand       xmm1,xmm0,xmmword ptr [271835FB220h]
2020-08-02 18:07:47 +02:00
Sintendo 31755bc13a Jit64: fselx - Optimize SSE4.1 packed
Pretty much the same optimization we did for AVX, although slightly more
constrained because we're stuck with the two-operand instruction where
destination and source have to match.

We could also specialize the case where registers b, c, and d are all
distinct, but I decided against it since I couldn't find any game that
does this.

Before:
66 0F 57 C0          xorpd       xmm0,xmm0
66 41 0F C2 C1 06    cmpnlepd    xmm0,xmm9
41 0F 28 CE          movaps      xmm1,xmm14
66 41 0F 38 15 CC    blendvpd    xmm1,xmm12,xmm0
44 0F 28 F1          movaps      xmm14,xmm1

After:
66 0F 57 C0          xorpd       xmm0,xmm0
66 41 0F C2 C1 06    cmpnlepd    xmm0,xmm9
66 45 0F 38 15 F4    blendvpd    xmm14,xmm12,xmm0
2020-07-29 17:28:48 +02:00
Sintendo afb86a12ab Jit64: fselx - Optimize AVX packed
For the packed variant, we can skip the final MOVAPS and write the
result directly into the destination register.

Before:
66 0F 57 C0          xorpd       xmm0,xmm0
66 41 0F C2 C1 06    cmpnlepd    xmm0,xmm9
C4 C3 09 4B CC 00    vblendvpd   xmm1,xmm14,xmm12,xmm0
44 0F 28 F1          movaps      xmm14,xmm1

After:
66 0F 57 C0          xorpd       xmm0,xmm0
66 41 0F C2 C1 06    cmpnlepd    xmm0,xmm9
C4 43 09 4B F4 00    vblendvpd   xmm14,xmm14,xmm12,xmm0
2020-07-29 17:06:52 +02:00
Sintendo a52774ca63 Jit64: fselx - Add AVX path
AVX has a four-operand VBLENDVPD instruction, which allows for the first
input and the destination to be different. By taking advantage of this,
we no longer need to copy one of the inputs around and we can just
reference it directly, provided it's already in a register (I have yet
to see this not be the case).

Before:
66 0F 57 C0          xorpd       xmm0,xmm0
F2 41 0F C2 C6 06    cmpnlesd    xmm0,xmm14
41 0F 28 CE          movaps      xmm1,xmm14
66 41 0F 38 15 CA    blendvpd    xmm1,xmm10,xmm0
F2 44 0F 10 F1       movsd       xmm14,xmm1

After:
66 0F 57 C0          xorpd       xmm0,xmm0
F2 41 0F C2 C6 06    cmpnlesd    xmm0,xmm14
C4 C3 09 4B CA 00    vblendvpd   xmm1,xmm14,xmm10,xmm0
F2 44 0F 10 F1       movsd       xmm14,xmm1
2020-07-28 23:17:18 +02:00
Sintendo 523954e03a Analytics: Report correct quirk for mismatched xf/bp colors
Looks like a copy-paste error. The quirk for mismatched xf/bp texgens
was used twice.
2020-04-24 02:22:51 +02:00
Sintendo 19dda51a0d Jit64: subfx - Use LEA when possible
Similar to what we do for addx. Since we're calculating b - a and
because subtraction is not communitative, we can only apply this when
source register a holds the constant.

Before:
45 8B EE             mov         r13d,r14d
41 83 ED 08          sub         r13d,8

After:
45 8D 6E F8          lea         r13d,[r14-8]
2020-04-21 22:45:47 +02:00
Sintendo 89646c898f Jit64: addx - Skip ADD after MOV when possible
We can get away with skipping the addition when we know we're dealing
with a constant zero. Just a MOV will suffice in this case.

Once again, we don't bother to add separate handling for when overflow
is needed, because no titles would ever hit that path during my testing.

Before:
8B 7D F8             mov         edi,dword ptr [rbp-8]
83 C7 00             add         edi,0

After:
8B 7D F8             mov         edi,dword ptr [rbp-8]
2020-04-21 22:45:47 +02:00
Sintendo 50f7a7d248 Jit64: addx - Prefer smaller MOV+ADD sequence
ADD has a smaller encoding for immediates that can be expressed as an
8-bit signed integer (in other words, between -128 and 127). MOV lacks
this compact representation.

Since addition allows us to swap the source registers, we can always get
the shortest sequence here by carefully checking if we're dealing with a
small immediate first. If we are, move the other source into the
destination and add the small immediate onto that. For large immediates
the reverse is preferrable.

Before:
41 BE 40 00 00 00    mov         r14d,40h
44 03 75 A8          add         r14d,dword ptr [rbp-58h]

After:
44 8B 75 A8          mov         r14d,dword ptr [rbp-58h]
41 83 C6 40          add         r14d,40h

Before:
44 8B 7D F8          mov         r15d,dword ptr [rbp-8]
41 81 C7 00 68 00 CC add         r15d,0CC006800h

After:
41 BF 00 68 00 CC    mov         r15d,0CC006800h
44 03 7D F8          add         r15d,dword ptr [rbp-8]
2020-04-21 22:42:02 +02:00
Sintendo 2481660519 Jit64: addx - Emit MOV when possible
When the source registers are a simple register and a constant zero and
overflow isn't needed, emitting LEA is kinda silly.

This will occasionally save a single byte for certain registers due to
how x86 encoding works. More importantly, LEA takes up execution
resources while MOV does not.

Before:
41 8D 7D 00          lea         edi,[r13]

After:
41 8B FD             mov         edi,r13d
2020-04-21 22:36:20 +02:00
Sintendo 1c25e6352a Jit64: addx - Emit nothing when possible
When the destination register matches a source register, the other
source register contains zero, and overflow isn't needed, the
instruction becomes a nop and we don't need to emit anything.

We could add specialized handling for the case where overflow is needed,
but none of the titles I tried would hit this path.

Before:
83 C7 00             add         edi,0

After:
2020-04-21 22:35:17 +02:00
Sintendo f1c3ab359d Jit64: addx - Deduplicate branches part 2
No functional change, just simplify some repeated logic in the case
where we're dealing with exactly one immediate and one simple register
when overflow isn't needed.
2020-04-21 22:06:46 +02:00
Sintendo 72fbdf1a6b Jit64: addx - Deduplicate branches part 1
No functional change, just simplify some repeated logic for the cases
where the destination register matches one of the sources.
2020-04-21 22:06:39 +02:00
Sintendo bdcdd763fe x64Emitter: Remove unused macros
No users, and one them seems to do the same as stddef.h's offsetof()
already used elsewhere.
2020-01-13 08:43:42 +01:00
Sintendo f82c38e156 X64Emitter: Remove obsolete TODO
TODO was already taken care of in PR #941.
2020-01-13 08:43:42 +01:00
Sintendo bdfc472751 x64Emitter: Refactor OpArg::WriteRest
Shorter, displacement is now handled in one location.
2020-01-13 08:43:42 +01:00
Sintendo cde3a3b448 x64Emitter: Avoid 8-bit displacement when possible
Due to the way the ModRM encoding works on x86, memory addressing
combinations involving RBP or R13 need an additional byte for an 8-bit
displacement of zero.

However, this was also applied in cases where it is unnecessary,
effectively wasting a byte.

- MatR with RSP or R12

8B 44 24 00          mov         eax,dword ptr [rsp]
8B 04 24             mov         eax,dword ptr [rsp]

- MRegSum with base != RBP or R13

46 8D 7C 37 00       lea         r15d,[rdi+r14]
46 8D 3C 37          lea         r15d,[rdi+r14]

- MComplex without offset

8B 4C CA 00          mov         ecx,dword ptr [rdx+rcx*8]
8B 0C CA             mov         ecx,dword ptr [rdx+rcx*8]
2020-01-13 08:43:42 +01:00
Sintendo d5cb858165 x64Emitter: Unit test memory addressing modes
Test the behavior of OpArg::WriteRest by using MOV with the various
addressing modes (MatR, MRegSum, etc.) in the source operand.

Both the instruction and the instruction length are validated.
2020-01-13 08:43:30 +01:00
Sintendo 12fcbac2a3 Jit64: addx - Emit LEA for register + immediate
Prefer LEA over MOV + ADD when dealing with immediates.

Before:
44 8B EE             mov         r13d,esi
41 83 C5 20          add         r13d,20h

After:
44 8D 6E 20          lea         r13d,[rsi+20h]
2020-01-05 23:39:13 +01:00
Sintendo 8e7b6f4178 Jit64: addx - Prefer ADD over LEA when possible
The old logic would always emit LEA when both sources are in a register
and OE is disabled. However, ADD is still preferable when one of the
sources matches the destination.

Before:
45 8D 6C 35 00       lea         r13d,[r13+rsi]

After:
44 03 EE             add         r13d,esi
2020-01-05 23:23:56 +01:00
Sintendo e9d4869965 Jit64AsmCommon: Micro-optimize GenFrsqrte
Save 2 bytes by testing register against itself and branching on the 
sign flag.

Before:
0:  48 0f ba e0 3f          bt     rax,0x3f

After:
0:  48 85 c0                test   rax,rax
2019-06-05 23:58:44 +02:00
Sintendo 5b5886160e DSPJitArithmetic: Precompute addis immediate
Compute immediate value at compile time.
2019-06-05 23:36:30 +02:00
Sintendo 3951d238d9 DSPJitArithmetic: Precompute addi immediate
Compute immediate value at compile time.
2019-06-05 23:36:14 +02:00
Sintendo a3744c3c26 DSPJitUtil: Use round_long_acc in get_long_prod_round_prodl
Identical, except that this used a temporary register as scratch instead 
of RCX. round_long_acc now no longer needs scratch, so we can 
deduplicate the logic.
2019-06-05 23:33:02 +02:00
Sintendo 832e320f08 DSPJitUtil: Make round_long_acc branchless
Also no longer clobbers RCX.
2019-06-05 23:25:39 +02:00
Sintendo 9b6f2859a4 DSPJitUtil: Micro-optimize increment_addr_reg
2 bytes shorter.

Before:
0:  41 0f b7 c2             movzx  eax,r10w
4:  41 89 c5                mov    r13d,eax
7:  83 c0 01                add    eax,0x1

After:
0:  45 0f b7 ea             movzx  r13d,r10w
4:  41 8d 45 01             lea    eax,[r13+0x1]
2019-06-05 23:23:22 +02:00
Sintendo 9fe3150358 x64EmitterTest: Test 64-bit MOV with 32-bit immediate 2019-05-29 01:04:16 +02:00
Sintendo c84f34bd50 x64Emitter: Short MOVs for 32bit immediates
Prior to this commit, the emitter would emit a 7-byte instruction when
loading a 32-bit immediate to a 64-bit register.

0:  48 c7 c0 ff ff ff 7f    mov    rax,0x7fffffff

With this change, it will check if it can instead emit a load to a
32-bit register, which takes only 5 or 6 bytes.

0:  b8 ff ff ff 7f          mov    eax,0x7fffffff
2019-05-29 01:04:09 +02:00
Sintendo d8953dbe57 x64Emitter: nit, use helper method in CMP_or_TEST 2018-09-29 09:52:54 +02:00
Sintendo ef94fca504 DSPJit: various micro-optimizations 2018-09-29 09:52:30 +02:00
Sintendo e3b424facd EmuCodeBlock: remove redundant instructions
Their result is overwritten by a subsequent MOV.
2018-09-28 23:58:16 +02:00
Sintendo 8a93dd0105 x64EmitterTest: Fix linting issues 2018-09-16 19:52:12 +02:00
Sintendo 58a0d0f117 x64EmitterTest: test MOV with 64-bit immediates 2018-09-16 19:52:12 +02:00
Sintendo 53a947749a x64Emitter: short MOV for 64bit immediates (2)
Prior to this commit, the emitter would unconditionally emit a 10-byte
instruction known as MOVABS when loading a 64-bit immediate to a
register.

0:  48 b8 ef be ad de 00    movabs rax,0xdeadbeef
7:  00 00 00

With this change, it will instead rely on the fact that on x64 writes to
32-bit registers are automatically zero extended to 64-bits, allowing
us to emit a 5 or 6-bytes instruction with the same effect for certain
immediates.

0:  b8 ef be ad de          mov    eax,0xdeadbeef
2018-09-16 19:52:03 +02:00
Sintendo 575f1b309a x64Emitter: short MOV for 64bit immediates (1)
Prior to this commit, the emitter would unconditionally emit a 10-byte
instruction known as MOVABS when loading a 64-bit immediate to a
register.

0:  48 b8 ef be ad de ff    movabs rax,0xffffffffdeadbeef
7:  ff ff ff

With this change, it will instead emit a 7-byte instruction when it is
possible to express the 64-bit immediate using a signed 32-bit value.

0:  48 c7 c0 ef be ad de    mov    rax,0xffffffffdeadbeef
2018-09-14 23:11:26 +02:00
Sintendo 78bc9690e2 Eliminate branch in ConvertDoubleToSingle 2018-06-13 23:02:50 +02:00
Sintendo f163bd1048 Fix various comment typos 2016-10-24 18:27:49 +02:00
Sintendo 00cc59e83a VideoInterface: Fix comment typo 2016-02-21 09:39:01 +01:00
Sintendo c4b56f06f9 Fix a few typos 2015-11-02 21:17:43 +01:00
Sintendo ce10525056 Eliminate branch in GenFres 2015-07-05 11:18:59 +02:00
Sintendo c19482c9a3 Add function to emit CMP, or TEST when possible
Also, a spelling mistake.
2015-03-22 17:22:27 +01:00
Sintendo b35c34186c More minor DSP JIT optimizations 2015-03-22 17:14:38 +01:00
Sintendo 46df8a5358 Correct clobber comments 2015-02-04 22:26:40 +01:00
Sintendo ca0790e9c9 Merge Update_SR_Register64_Carry functions 2015-02-04 22:26:40 +01:00
Sintendo 34afe08de9 Minor DSP JIT optimizations 2015-02-04 22:26:39 +01:00
Sintendo 29cca5c84f Change fres/frsqrte jumps 2014-09-24 21:58:01 +02:00