1 ;uInt longest_match_x64(
\r
3 ; IPos cur_match); /* current match */
\r
5 ; gvmat64.asm -- Asm portion of the optimized longest_match for 32 bits x86_64
\r
6 ; (AMD64 on Athlon 64, Opteron, Phenom
\r
7 ; and Intel EM64T on Pentium 4 with EM64T, Pentium D, Core 2 Duo, Core I5/I7)
\r
8 ; Copyright (C) 1995-2010 Jean-loup Gailly, Brian Raiter and Gilles Vollant.
\r
10 ; File written by Gilles Vollant, by converting to assembly the longest_match
\r
11 ; from Jean-loup Gailly in deflate.c of zLib and infoZip zip.
\r
13 ; and by taking inspiration on asm686 with masm, optimised assembly code
\r
14 ; from Brian Raiter, written 1998
\r
16 ; This software is provided 'as-is', without any express or implied
\r
17 ; warranty. In no event will the authors be held liable for any damages
\r
18 ; arising from the use of this software.
\r
20 ; Permission is granted to anyone to use this software for any purpose,
\r
21 ; including commercial applications, and to alter it and redistribute it
\r
22 ; freely, subject to the following restrictions:
\r
24 ; 1. The origin of this software must not be misrepresented; you must not
\r
25 ; claim that you wrote the original software. If you use this software
\r
26 ; in a product, an acknowledgment in the product documentation would be
\r
27 ; appreciated but is not required.
\r
28 ; 2. Altered source versions must be plainly marked as such, and must not be
\r
29 ; misrepresented as being the original software
\r
30 ; 3. This notice may not be removed or altered from any source distribution.
\r
34 ; http://www.zlib.net
\r
35 ; http://www.winimage.com/zLibDll
\r
36 ; http://www.muppetlabs.com/~breadbox/software/assembly.html
\r
38 ; to compile this file for infozip Zip, I use option:
\r
39 ; ml64.exe /Flgvmat64 /c /Zi /DINFOZIP gvmat64.asm
\r
41 ; to compile this file for zLib, I use option:
\r
42 ; ml64.exe /Flgvmat64 /c /Zi gvmat64.asm
\r
43 ; Be carrefull to adapt zlib1222add below to your version of zLib
\r
44 ; (if you use a version of zLib before 1.0.4 or after 1.2.2.2, change
\r
45 ; value of zlib1222add later)
\r
47 ; This file compile with Microsoft Macro Assembler (x64) for AMD64
\r
49 ; ml64.exe is given with Visual Studio 2005/2008/2010 and Windows WDK
\r
51 ; (you can get Windows WDK with ml64 for AMD64 from
\r
52 ; http://www.microsoft.com/whdc/Devtools/wdk/default.mspx for low price)
\r
56 ;uInt longest_match(s, cur_match)
\r
58 ; IPos cur_match; /* current match */
\r
63 ;LocalVarsSize equ 88
\r
64 LocalVarsSize equ 72
\r
66 ; register used : rax,rbx,rcx,rdx,rsi,rdi,r8,r9,r10,r11,r12
\r
67 ; free register : r14,r15
\r
68 ; register can be saved : rsp
\r
70 chainlenwmask equ rsp + 8 - LocalVarsSize ; high word: current chain len
\r
71 ; low word: s->wmask
\r
72 ;window equ rsp + xx - LocalVarsSize ; local copy of s->window ; stored in r10
\r
73 ;windowbestlen equ rsp + xx - LocalVarsSize ; s->window + bestlen , use r10+r11
\r
74 ;scanstart equ rsp + xx - LocalVarsSize ; first two bytes of string ; stored in r12w
\r
75 ;scanend equ rsp + xx - LocalVarsSize ; last two bytes of string use ebx
\r
76 ;scanalign equ rsp + xx - LocalVarsSize ; dword-misalignment of string r13
\r
77 ;bestlen equ rsp + xx - LocalVarsSize ; size of best match so far -> r11d
\r
78 ;scan equ rsp + xx - LocalVarsSize ; ptr to string wanting match -> r9
\r
81 nicematch equ (rsp + 16 - LocalVarsSize) ; a good enough match size
\r
84 save_rdi equ rsp + 24 - LocalVarsSize
\r
85 save_rsi equ rsp + 32 - LocalVarsSize
\r
86 save_rbx equ rsp + 40 - LocalVarsSize
\r
87 save_rbp equ rsp + 48 - LocalVarsSize
\r
88 save_r12 equ rsp + 56 - LocalVarsSize
\r
89 save_r13 equ rsp + 64 - LocalVarsSize
\r
90 ;save_r14 equ rsp + 72 - LocalVarsSize
\r
91 ;save_r15 equ rsp + 80 - LocalVarsSize
\r
94 ; summary of register usage
\r
114 ; all the +4 offsets are due to the addition of pending_buf_size (in zlib
\r
115 ; in the deflate_state structure since the asm code was first written
\r
116 ; (if you compile with zlib 1.0.4 or older, remove the +4).
\r
117 ; Note : these value are good with a 8 bytes boundary pack structure
\r
122 MIN_LOOKAHEAD equ (MAX_MATCH+MIN_MATCH+1)
\r
125 ;;; Offsets for fields in the deflate_state structure. These numbers
\r
126 ;;; are calculated from the definition of deflate_state, with the
\r
127 ;;; assumption that the compiler will dword-align the fields. (Thus,
\r
128 ;;; changing the definition of deflate_state could easily cause this
\r
129 ;;; program to crash horribly, without so much as a warning at
\r
130 ;;; compile time. Sigh.)
\r
132 ; all the +zlib1222add offsets are due to the addition of fields
\r
133 ; in zlib in the deflate_state structure since the asm code was first written
\r
134 ; (if you compile with zlib 1.0.4 or older, use "zlib1222add equ (-4)").
\r
135 ; (if you compile with zlib between 1.0.5 and 1.2.2.1, use "zlib1222add equ 0").
\r
136 ; if you compile with zlib 1.2.2.2 or later , use "zlib1222add equ 8").
\r
142 COMM window_size:DWORD
\r
144 COMM window:BYTE:010040H
\r
145 COMM prev:WORD:08000H
\r
146 ; MatchLen : unused
\r
147 ; PrevMatch : unused
\r
148 COMM strstart:DWORD
\r
149 COMM match_start:DWORD
\r
150 ; Lookahead : ignore
\r
151 COMM prev_length:DWORD ; PrevLen
\r
152 COMM max_chain_length:DWORD
\r
153 COMM good_match:DWORD
\r
154 COMM nice_match:DWORD
\r
155 prev_ad equ OFFSET prev
\r
156 window_ad equ OFFSET window
\r
157 nicematch equ nice_match
\r
166 dsWSize equ 56+zlib1222add+(zlib1222add/2)
\r
167 dsWMask equ 64+zlib1222add+(zlib1222add/2)
\r
168 dsWindow equ 72+zlib1222add
\r
169 dsPrev equ 88+zlib1222add
\r
170 dsMatchLen equ 128+zlib1222add
\r
171 dsPrevMatch equ 132+zlib1222add
\r
172 dsStrStart equ 140+zlib1222add
\r
173 dsMatchStart equ 144+zlib1222add
\r
174 dsLookahead equ 148+zlib1222add
\r
175 dsPrevLen equ 152+zlib1222add
\r
176 dsMaxChainLen equ 156+zlib1222add
\r
177 dsGoodMatch equ 172+zlib1222add
\r
178 dsNiceMatch equ 176+zlib1222add
\r
180 window_size equ [ rcx + dsWSize]
\r
181 WMask equ [ rcx + dsWMask]
\r
182 window_ad equ [ rcx + dsWindow]
\r
183 prev_ad equ [ rcx + dsPrev]
\r
184 strstart equ [ rcx + dsStrStart]
\r
185 match_start equ [ rcx + dsMatchStart]
\r
186 Lookahead equ [ rcx + dsLookahead] ; 0ffffffffh on infozip
\r
187 prev_length equ [ rcx + dsPrevLen]
\r
188 max_chain_length equ [ rcx + dsMaxChainLen]
\r
189 good_match equ [ rcx + dsGoodMatch]
\r
190 nice_match equ [ rcx + dsNiceMatch]
\r
193 ; parameter 1 in r8(deflate state s), param 2 in rdx (cur match)
\r
195 ; see http://weblogs.asp.net/oldnewthing/archive/2004/01/14/58579.aspx and
\r
196 ; http://msdn.microsoft.com/library/en-us/kmarch/hh/kmarch/64bitAMD_8e951dd2-ee77-4728-8702-55ce4b5dd24a.xml.asp
\r
198 ; All registers must be preserved across the call, except for
\r
199 ; rax, rcx, rdx, r8, r9, r10, and r11, which are scratch.
\r
203 ;;; Save registers that the compiler may be using, and adjust esp to
\r
204 ;;; make room for our stack frame.
\r
207 ;;; Retrieve the function arguments. r8d will hold cur_match
\r
208 ;;; throughout the entire function. edx will hold the pointer to the
\r
209 ;;; deflate_state structure during the function's setup (before
\r
210 ;;; entering the main loop.
\r
212 ; parameter 1 in rcx (deflate_state* s), param 2 in edx -> r8 (cur match)
\r
214 ; this clear high 32 bits of r8, which can be garbage in both r8 and rdx
\r
227 ; mov [save_r14],r14
\r
228 ; mov [save_r15],r15
\r
231 ;;; uInt wmask = s->w_mask;
\r
232 ;;; unsigned chain_length = s->max_chain_length;
\r
233 ;;; if (s->prev_length >= s->good_match) {
\r
234 ;;; chain_length >>= 2;
\r
237 mov edi, prev_length
\r
238 mov esi, good_match
\r
240 mov ebx, max_chain_length
\r
246 ;;; chainlen is decremented once beforehand so that the function can
\r
247 ;;; use the sign flag instead of the zero flag for the exit test.
\r
248 ;;; It is then shifted into the high word, to make room for the wmask
\r
249 ;;; value, which it will always accompany.
\r
256 ;;; if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead;
\r
259 mov [chainlenwmask], ebx
\r
260 ; on infozip nice_match = [nice_match]
\r
262 mov eax, nice_match
\r
263 mov [chainlenwmask], ebx
\r
264 mov r10d, Lookahead
\r
267 mov [nicematch],r10d
\r
270 ;;; register Bytef *scan = s->window + s->strstart;
\r
273 lea r13, [r10 + rbp]
\r
275 ;;; Determine how many bytes the scan ptr is off from being
\r
282 ;;; IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
\r
283 ;;; s->strstart - (IPos)MAX_DIST(s) : NIL;
\r
285 mov eax,07efah ; MAX_DIST = (WSIZE-MIN_LOOKAHEAD) (0x8000-(3+8+1))
\r
287 mov eax, window_size
\r
288 sub eax, MIN_LOOKAHEAD
\r
293 mov r11d, prev_length
\r
297 ;;; int best_len = s->prev_length;
\r
300 ;;; Store the sum of s->window + best_len in esi locally, and in esi.
\r
304 ;;; register ush scan_start = *(ushf*)scan;
\r
305 ;;; register ush scan_end = *(ushf*)(scan+best_len-1);
\r
306 ;;; Posf *prev = s->prev;
\r
308 movzx r12d,word ptr [r9]
\r
309 movzx ebx, word ptr [r9 + r11 - 1]
\r
313 ;;; Jump into the main loop.
\r
315 mov edx, [chainlenwmask]
\r
317 cmp bx,word ptr [rsi + r8 - 1]
\r
318 jz LookupLoopIsZero
\r
323 movzx r8d, word ptr [rdi + r8*2]
\r
330 cmp bx,word ptr [rsi + r8 - 1]
\r
331 jz LookupLoopIsZero
\r
336 movzx r8d, word ptr [rdi + r8*2]
\r
343 cmp bx,word ptr [rsi + r8 - 1]
\r
344 jz LookupLoopIsZero
\r
349 movzx r8d, word ptr [rdi + r8*2]
\r
357 cmp bx,word ptr [rsi + r8 - 1]
\r
359 jmp LookupLoopIsZero
\r
363 ;;; match = s->window + cur_match;
\r
364 ;;; if (*(ushf*)(match+best_len-1) != scan_end ||
\r
365 ;;; *(ushf*)match != scan_start) continue;
\r
367 ;;; } while ((cur_match = prev[cur_match & wmask]) > limit
\r
368 ;;; && --chain_length != 0);
\r
370 ;;; Here is the inner loop of the function. The function will spend the
\r
371 ;;; majority of its time in this loop, and majority of that time will
\r
372 ;;; be spent in the first ten instructions.
\r
374 ;;; Within this loop:
\r
377 ;;; edx = chainlenwmask - i.e., ((chainlen << 16) | wmask)
\r
378 ;;; esi = windowbestlen - i.e., (window + bestlen)
\r
385 movzx r8d, word ptr [rdi + r8*2]
\r
393 cmp bx,word ptr [rsi + r8 - 1]
\r
396 cmp r12w, word ptr [r10 + r8]
\r
400 ;;; Store the current value of chainlen.
\r
401 mov [chainlenwmask], edx
\r
403 ;;; Point edi to the string under scrutiny, and esi to the string we
\r
404 ;;; are hoping to match it up with. In actuality, esi and edi are
\r
405 ;;; both pointed (MAX_MATCH_8 - scanalign) bytes ahead, and edx is
\r
406 ;;; initialized to -(MAX_MATCH_8 - scanalign).
\r
409 mov rdx, 0fffffffffffffef8h; -(MAX_MATCH_8)
\r
410 lea rsi, [rsi + r13 + 0108h] ;MAX_MATCH_8]
\r
411 lea rdi, [r9 + r13 + 0108h] ;MAX_MATCH_8]
\r
413 prefetcht1 [rsi+rdx]
\r
414 prefetcht1 [rdi+rdx]
\r
417 ;;; Test the strings for equality, 8 bytes at a time. At the end,
\r
418 ;;; adjust rdx so that it is offset to the exact byte that mismatched.
\r
420 ;;; We already know at this point that the first three bytes of the
\r
421 ;;; strings match each other, and they can be safely passed over before
\r
422 ;;; starting the compare loop. So what this code does is skip over 0-3
\r
423 ;;; bytes, as much as necessary in order to dword-align the edi
\r
424 ;;; pointer. (rsi will still be misaligned three times out of four.)
\r
426 ;;; It should be confessed that this loop usually does not represent
\r
427 ;;; much of the total running time. Replacing it with a more
\r
428 ;;; straightforward "rep cmpsb" would not drastically degrade
\r
433 mov rax, [rsi + rdx]
\r
434 xor rax, [rdi + rdx]
\r
437 mov rax, [rsi + rdx + 8]
\r
438 xor rax, [rdi + rdx + 8]
\r
442 mov rax, [rsi + rdx + 8+8]
\r
443 xor rax, [rdi + rdx + 8+8]
\r
444 jnz LeaveLoopCmps16
\r
449 jmp short LenMaximum
\r
450 LeaveLoopCmps16: add rdx,8
\r
451 LeaveLoopCmps8: add rdx,8
\r
454 test eax, 0000FFFFh
\r
457 test eax,0ffffffffh
\r
469 LenLower: sub al, 1
\r
471 ;;; Calculate the length of the match. If it is longer than MAX_MATCH,
\r
472 ;;; then automatically accept it as the best possible match and leave.
\r
474 lea rax, [rdi + rdx]
\r
479 ;;; If the length of the match is not longer than the best match we
\r
480 ;;; have so far, then forget it and return to the lookup loop.
\r
481 ;///////////////////////////////////
\r
489 mov edx, [chainlenwmask]
\r
492 ;;; s->match_start = cur_match;
\r
493 ;;; best_len = len;
\r
494 ;;; if (len >= nice_match) break;
\r
495 ;;; scan_end = *(ushf*)(scan+best_len-1);
\r
499 mov match_start, r8d
\r
500 cmp eax, [nicematch]
\r
505 movzx ebx, word ptr [r9 + rax - 1]
\r
507 mov edx, [chainlenwmask]
\r
510 ;;; Accept the current string, with the maximum possible length.
\r
514 mov match_start, r8d
\r
516 ;;; if ((uInt)best_len <= s->lookahead) return (uInt)best_len;
\r
517 ;;; return s->lookahead;
\r
528 ;;; Restore the stack and return from whence we came.
\r
537 ; mov r14,[save_r14]
\r
538 ; mov r15,[save_r15]
\r
542 ; please don't remove this string !
\r
543 ; Your can freely use gvmat64 in any free or commercial app
\r
544 ; but it is far better don't remove the string in the binary!
\r
545 db 0dh,0ah,"asm686 with masm, optimised assembly code from Brian Raiter, written 1998, converted to amd 64 by Gilles Vollant 2005",0dh,0ah,0
\r