1
/* match.S -- x86 assembly version of the zlib longest_match() function.
2
* Optimized for the Intel 686 chips (PPro and later).
4
* Copyright (C) 1998, 2007 Brian Raiter <breadbox@muppetlabs.com>
6
* This software is provided 'as-is', without any express or implied
7
* warranty. In no event will the author be held liable for any damages
8
* arising from the use of this software.
10
* Permission is granted to anyone to use this software for any purpose,
11
* including commercial applications, and to alter it and redistribute it
12
* freely, subject to the following restrictions:
14
* 1. The origin of this software must not be misrepresented; you must not
15
* claim that you wrote the original software. If you use this software
16
* in a product, an acknowledgment in the product documentation would be
17
* appreciated but is not required.
18
* 2. Altered source versions must be plainly marked as such, and must not be
19
* misrepresented as being the original software.
20
* 3. This notice may not be removed or altered from any source distribution.
24
#define match_init _match_init
25
#define longest_match _longest_match
28
#define MAX_MATCH (258)
30
#define MIN_LOOKAHEAD (MAX_MATCH + MIN_MATCH + 1)
31
#define MAX_MATCH_8 ((MAX_MATCH + 7) & ~7)
33
/* stack frame offsets */
35
#define chainlenwmask 0 /* high word: current chain len */
36
/* low word: s->wmask */
37
#define window 4 /* local copy of s->window */
38
#define windowbestlen 8 /* s->window + bestlen */
39
#define scanstart 16 /* first two bytes of string */
40
#define scanend 12 /* last two bytes of string */
41
#define scanalign 20 /* dword-misalignment of string */
42
#define nicematch 24 /* a good enough match size */
43
#define bestlen 28 /* size of best match so far */
44
#define scan 32 /* ptr to string wanting match */
46
#define LocalVarsSize (36)
51
/* return address 52 */
52
#define deflatestate 56 /* the function arguments */
55
/* All the +zlib1222add offsets are due to the addition of fields
56
* in zlib in the deflate_state structure since the asm code was first written
57
* (if you compile with zlib 1.0.4 or older, use "zlib1222add equ (-4)").
58
* (if you compile with zlib between 1.0.5 and 1.2.2.1, use "zlib1222add equ 0").
59
* if you compile with zlib 1.2.2.2 or later , use "zlib1222add equ 8").
62
#define zlib1222add (8)
64
#define dsWSize (36+zlib1222add)
65
#define dsWMask (44+zlib1222add)
66
#define dsWindow (48+zlib1222add)
67
#define dsPrev (56+zlib1222add)
68
#define dsMatchLen (88+zlib1222add)
69
#define dsPrevMatch (92+zlib1222add)
70
#define dsStrStart (100+zlib1222add)
71
#define dsMatchStart (104+zlib1222add)
72
#define dsLookahead (108+zlib1222add)
73
#define dsPrevLen (112+zlib1222add)
74
#define dsMaxChainLen (116+zlib1222add)
75
#define dsGoodMatch (132+zlib1222add)
76
#define dsNiceMatch (136+zlib1222add)
81
.globl match_init, longest_match
85
/* uInt longest_match(deflate_state *deflatestate, IPos curmatch) */
89
/* Save registers that the compiler may be using, and adjust %esp to */
90
/* make room for our stack frame. */
96
subl $LocalVarsSize, %esp
98
/* Retrieve the function arguments. %ecx will hold cur_match */
99
/* throughout the entire function. %edx will hold the pointer to the */
100
/* deflate_state structure during the function's setup (before */
101
/* entering the main loop). */
103
movl deflatestate(%esp), %edx
104
movl curmatch(%esp), %ecx
106
/* uInt wmask = s->w_mask; */
107
/* unsigned chain_length = s->max_chain_length; */
108
/* if (s->prev_length >= s->good_match) { */
109
/* chain_length >>= 2; */
112
movl dsPrevLen(%edx), %eax
113
movl dsGoodMatch(%edx), %ebx
115
movl dsWMask(%edx), %eax
116
movl dsMaxChainLen(%edx), %ebx
121
/* chainlen is decremented once beforehand so that the function can */
122
/* use the sign flag instead of the zero flag for the exit test. */
123
/* It is then shifted into the high word, to make room for the wmask */
124
/* value, which it will always accompany. */
129
movl %ebx, chainlenwmask(%esp)
131
/* if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; */
133
movl dsNiceMatch(%edx), %eax
134
movl dsLookahead(%edx), %ebx
138
LookaheadLess: movl %ebx, nicematch(%esp)
140
/* register Bytef *scan = s->window + s->strstart; */
142
movl dsWindow(%edx), %esi
143
movl %esi, window(%esp)
144
movl dsStrStart(%edx), %ebp
145
lea (%esi,%ebp), %edi
146
movl %edi, scan(%esp)
148
/* Determine how many bytes the scan ptr is off from being */
154
movl %eax, scanalign(%esp)
156
/* IPos limit = s->strstart > (IPos)MAX_DIST(s) ? */
157
/* s->strstart - (IPos)MAX_DIST(s) : NIL; */
159
movl dsWSize(%edx), %eax
160
subl $MIN_LOOKAHEAD, %eax
166
/* int best_len = s->prev_length; */
168
movl dsPrevLen(%edx), %eax
169
movl %eax, bestlen(%esp)
171
/* Store the sum of s->window + best_len in %esi locally, and in %esi. */
174
movl %esi, windowbestlen(%esp)
176
/* register ush scan_start = *(ushf*)scan; */
177
/* register ush scan_end = *(ushf*)(scan+best_len-1); */
178
/* Posf *prev = s->prev; */
181
movl %ebx, scanstart(%esp)
182
movzwl -1(%edi,%eax), %ebx
183
movl %ebx, scanend(%esp)
184
movl dsPrev(%edx), %edi
186
/* Jump into the main loop. */
188
movl chainlenwmask(%esp), %edx
194
* match = s->window + cur_match;
195
* if (*(ushf*)(match+best_len-1) != scan_end ||
196
* *(ushf*)match != scan_start) continue;
198
* } while ((cur_match = prev[cur_match & wmask]) > limit
199
* && --chain_length != 0);
201
* Here is the inner loop of the function. The function will spend the
202
* majority of its time in this loop, and majority of that time will
203
* be spent in the first ten instructions.
208
* %edx = chainlenwmask - i.e., ((chainlen << 16) | wmask)
209
* %esi = windowbestlen - i.e., (window + bestlen)
215
movzwl (%edi,%ecx,2), %ecx
218
subl $0x00010000, %edx
220
LoopEntry: movzwl -1(%esi,%ecx), %eax
223
movl window(%esp), %eax
224
movzwl (%eax,%ecx), %eax
225
cmpl scanstart(%esp), %eax
228
/* Store the current value of chainlen. */
230
movl %edx, chainlenwmask(%esp)
232
/* Point %edi to the string under scrutiny, and %esi to the string we */
233
/* are hoping to match it up with. In actuality, %esi and %edi are */
234
/* both pointed (MAX_MATCH_8 - scanalign) bytes ahead, and %edx is */
235
/* initialized to -(MAX_MATCH_8 - scanalign). */
237
movl window(%esp), %esi
238
movl scan(%esp), %edi
240
movl scanalign(%esp), %eax
241
movl $(-MAX_MATCH_8), %edx
242
lea MAX_MATCH_8(%edi,%eax), %edi
243
lea MAX_MATCH_8(%esi,%eax), %esi
245
/* Test the strings for equality, 8 bytes at a time. At the end,
246
* adjust %edx so that it is offset to the exact byte that mismatched.
248
* We already know at this point that the first three bytes of the
249
* strings match each other, and they can be safely passed over before
250
* starting the compare loop. So what this code does is skip over 0-3
251
* bytes, as much as necessary in order to dword-align the %edi
252
* pointer. (%esi will still be misaligned three times out of four.)
254
* It should be confessed that this loop usually does not represent
255
* much of the total running time. Replacing it with a more
256
* straightforward "rep cmpsb" would not drastically degrade
260
movl (%esi,%edx), %eax
261
xorl (%edi,%edx), %eax
263
movl 4(%esi,%edx), %eax
264
xorl 4(%edi,%edx), %eax
269
LeaveLoopCmps4: addl $4, %edx
270
LeaveLoopCmps: testl $0x0000FFFF, %eax
274
LenLower: subb $1, %al
277
/* Calculate the length of the match. If it is longer than MAX_MATCH, */
278
/* then automatically accept it as the best possible match and leave. */
280
lea (%edi,%edx), %eax
281
movl scan(%esp), %edi
283
cmpl $MAX_MATCH, %eax
286
/* If the length of the match is not longer than the best match we */
287
/* have so far, then forget it and return to the lookup loop. */
289
movl deflatestate(%esp), %edx
290
movl bestlen(%esp), %ebx
293
movl windowbestlen(%esp), %esi
294
movl dsPrev(%edx), %edi
295
movl scanend(%esp), %ebx
296
movl chainlenwmask(%esp), %edx
299
/* s->match_start = cur_match; */
300
/* best_len = len; */
301
/* if (len >= nice_match) break; */
302
/* scan_end = *(ushf*)(scan+best_len-1); */
304
LongerMatch: movl nicematch(%esp), %ebx
305
movl %eax, bestlen(%esp)
306
movl %ecx, dsMatchStart(%edx)
309
movl window(%esp), %esi
311
movl %esi, windowbestlen(%esp)
312
movzwl -1(%edi,%eax), %ebx
313
movl dsPrev(%edx), %edi
314
movl %ebx, scanend(%esp)
315
movl chainlenwmask(%esp), %edx
318
/* Accept the current string, with the maximum possible length. */
320
LenMaximum: movl deflatestate(%esp), %edx
321
movl $MAX_MATCH, bestlen(%esp)
322
movl %ecx, dsMatchStart(%edx)
324
/* if ((uInt)best_len <= s->lookahead) return (uInt)best_len; */
325
/* return s->lookahead; */
328
movl deflatestate(%esp), %edx
329
movl bestlen(%esp), %ebx
330
movl dsLookahead(%edx), %eax
336
/* Restore the stack and return from whence we came. */
338
addl $LocalVarsSize, %esp