annotate src/zlib-1.2.8/contrib/amd64/amd64-match.S @ 155:54abead6ecce

Opus for Windows (MSVC)
author Chris Cannam <cannam@all-day-breakfast.com>
date Fri, 25 Jan 2019 12:15:58 +0000
parents 5b4145a0d408
children
rev   line source
cannam@128 1 /*
cannam@128 2 * match.S -- optimized version of longest_match()
cannam@128 3 * based on the similar work by Gilles Vollant, and Brian Raiter, written 1998
cannam@128 4 *
cannam@128 5 * This is free software; you can redistribute it and/or modify it
cannam@128 6 * under the terms of the BSD License. Use by owners of Che Guevarra
cannam@128 7 * parafernalia is prohibited, where possible, and highly discouraged
cannam@128 8 * elsewhere.
cannam@128 9 */
cannam@128 10
cannam@128 11 #ifndef NO_UNDERLINE
cannam@128 12 # define match_init _match_init
cannam@128 13 # define longest_match _longest_match
cannam@128 14 #endif
cannam@128 15
cannam@128 16 #define scanend ebx
cannam@128 17 #define scanendw bx
cannam@128 18 #define chainlenwmask edx /* high word: current chain len low word: s->wmask */
cannam@128 19 #define curmatch rsi
cannam@128 20 #define curmatchd esi
cannam@128 21 #define windowbestlen r8
cannam@128 22 #define scanalign r9
cannam@128 23 #define scanalignd r9d
cannam@128 24 #define window r10
cannam@128 25 #define bestlen r11
cannam@128 26 #define bestlend r11d
cannam@128 27 #define scanstart r12d
cannam@128 28 #define scanstartw r12w
cannam@128 29 #define scan r13
cannam@128 30 #define nicematch r14d
cannam@128 31 #define limit r15
cannam@128 32 #define limitd r15d
cannam@128 33 #define prev rcx
cannam@128 34
cannam@128 35 /*
cannam@128 36 * The 258 is a "magic number, not a parameter -- changing it
cannam@128 37 * breaks the hell loose
cannam@128 38 */
cannam@128 39 #define MAX_MATCH (258)
cannam@128 40 #define MIN_MATCH (3)
cannam@128 41 #define MIN_LOOKAHEAD (MAX_MATCH + MIN_MATCH + 1)
cannam@128 42 #define MAX_MATCH_8 ((MAX_MATCH + 7) & ~7)
cannam@128 43
cannam@128 44 /* stack frame offsets */
cannam@128 45 #define LocalVarsSize (112)
cannam@128 46 #define _chainlenwmask ( 8-LocalVarsSize)(%rsp)
cannam@128 47 #define _windowbestlen (16-LocalVarsSize)(%rsp)
cannam@128 48 #define save_r14 (24-LocalVarsSize)(%rsp)
cannam@128 49 #define save_rsi (32-LocalVarsSize)(%rsp)
cannam@128 50 #define save_rbx (40-LocalVarsSize)(%rsp)
cannam@128 51 #define save_r12 (56-LocalVarsSize)(%rsp)
cannam@128 52 #define save_r13 (64-LocalVarsSize)(%rsp)
cannam@128 53 #define save_r15 (80-LocalVarsSize)(%rsp)
cannam@128 54
cannam@128 55
cannam@128 56 .globl match_init, longest_match
cannam@128 57
cannam@128 58 /*
cannam@128 59 * On AMD64 the first argument of a function (in our case -- the pointer to
cannam@128 60 * deflate_state structure) is passed in %rdi, hence our offsets below are
cannam@128 61 * all off of that.
cannam@128 62 */
cannam@128 63
cannam@128 64 /* you can check the structure offset by running
cannam@128 65
cannam@128 66 #include <stdlib.h>
cannam@128 67 #include <stdio.h>
cannam@128 68 #include "deflate.h"
cannam@128 69
cannam@128 70 void print_depl()
cannam@128 71 {
cannam@128 72 deflate_state ds;
cannam@128 73 deflate_state *s=&ds;
cannam@128 74 printf("size pointer=%u\n",(int)sizeof(void*));
cannam@128 75
cannam@128 76 printf("#define dsWSize (%3u)(%%rdi)\n",(int)(((char*)&(s->w_size))-((char*)s)));
cannam@128 77 printf("#define dsWMask (%3u)(%%rdi)\n",(int)(((char*)&(s->w_mask))-((char*)s)));
cannam@128 78 printf("#define dsWindow (%3u)(%%rdi)\n",(int)(((char*)&(s->window))-((char*)s)));
cannam@128 79 printf("#define dsPrev (%3u)(%%rdi)\n",(int)(((char*)&(s->prev))-((char*)s)));
cannam@128 80 printf("#define dsMatchLen (%3u)(%%rdi)\n",(int)(((char*)&(s->match_length))-((char*)s)));
cannam@128 81 printf("#define dsPrevMatch (%3u)(%%rdi)\n",(int)(((char*)&(s->prev_match))-((char*)s)));
cannam@128 82 printf("#define dsStrStart (%3u)(%%rdi)\n",(int)(((char*)&(s->strstart))-((char*)s)));
cannam@128 83 printf("#define dsMatchStart (%3u)(%%rdi)\n",(int)(((char*)&(s->match_start))-((char*)s)));
cannam@128 84 printf("#define dsLookahead (%3u)(%%rdi)\n",(int)(((char*)&(s->lookahead))-((char*)s)));
cannam@128 85 printf("#define dsPrevLen (%3u)(%%rdi)\n",(int)(((char*)&(s->prev_length))-((char*)s)));
cannam@128 86 printf("#define dsMaxChainLen (%3u)(%%rdi)\n",(int)(((char*)&(s->max_chain_length))-((char*)s)));
cannam@128 87 printf("#define dsGoodMatch (%3u)(%%rdi)\n",(int)(((char*)&(s->good_match))-((char*)s)));
cannam@128 88 printf("#define dsNiceMatch (%3u)(%%rdi)\n",(int)(((char*)&(s->nice_match))-((char*)s)));
cannam@128 89 }
cannam@128 90
cannam@128 91 */
cannam@128 92
cannam@128 93
cannam@128 94 /*
cannam@128 95 to compile for XCode 3.2 on MacOSX x86_64
cannam@128 96 - run "gcc -g -c -DXCODE_MAC_X64_STRUCTURE amd64-match.S"
cannam@128 97 */
cannam@128 98
cannam@128 99
cannam@128 100 #ifndef CURRENT_LINX_XCODE_MAC_X64_STRUCTURE
cannam@128 101 #define dsWSize ( 68)(%rdi)
cannam@128 102 #define dsWMask ( 76)(%rdi)
cannam@128 103 #define dsWindow ( 80)(%rdi)
cannam@128 104 #define dsPrev ( 96)(%rdi)
cannam@128 105 #define dsMatchLen (144)(%rdi)
cannam@128 106 #define dsPrevMatch (148)(%rdi)
cannam@128 107 #define dsStrStart (156)(%rdi)
cannam@128 108 #define dsMatchStart (160)(%rdi)
cannam@128 109 #define dsLookahead (164)(%rdi)
cannam@128 110 #define dsPrevLen (168)(%rdi)
cannam@128 111 #define dsMaxChainLen (172)(%rdi)
cannam@128 112 #define dsGoodMatch (188)(%rdi)
cannam@128 113 #define dsNiceMatch (192)(%rdi)
cannam@128 114
cannam@128 115 #else
cannam@128 116
cannam@128 117 #ifndef STRUCT_OFFSET
cannam@128 118 # define STRUCT_OFFSET (0)
cannam@128 119 #endif
cannam@128 120
cannam@128 121
cannam@128 122 #define dsWSize ( 56 + STRUCT_OFFSET)(%rdi)
cannam@128 123 #define dsWMask ( 64 + STRUCT_OFFSET)(%rdi)
cannam@128 124 #define dsWindow ( 72 + STRUCT_OFFSET)(%rdi)
cannam@128 125 #define dsPrev ( 88 + STRUCT_OFFSET)(%rdi)
cannam@128 126 #define dsMatchLen (136 + STRUCT_OFFSET)(%rdi)
cannam@128 127 #define dsPrevMatch (140 + STRUCT_OFFSET)(%rdi)
cannam@128 128 #define dsStrStart (148 + STRUCT_OFFSET)(%rdi)
cannam@128 129 #define dsMatchStart (152 + STRUCT_OFFSET)(%rdi)
cannam@128 130 #define dsLookahead (156 + STRUCT_OFFSET)(%rdi)
cannam@128 131 #define dsPrevLen (160 + STRUCT_OFFSET)(%rdi)
cannam@128 132 #define dsMaxChainLen (164 + STRUCT_OFFSET)(%rdi)
cannam@128 133 #define dsGoodMatch (180 + STRUCT_OFFSET)(%rdi)
cannam@128 134 #define dsNiceMatch (184 + STRUCT_OFFSET)(%rdi)
cannam@128 135
cannam@128 136 #endif
cannam@128 137
cannam@128 138
cannam@128 139
cannam@128 140
cannam@128 141 .text
cannam@128 142
cannam@128 143 /* uInt longest_match(deflate_state *deflatestate, IPos curmatch) */
cannam@128 144
cannam@128 145 longest_match:
cannam@128 146 /*
cannam@128 147 * Retrieve the function arguments. %curmatch will hold cur_match
cannam@128 148 * throughout the entire function (passed via rsi on amd64).
cannam@128 149 * rdi will hold the pointer to the deflate_state (first arg on amd64)
cannam@128 150 */
cannam@128 151 mov %rsi, save_rsi
cannam@128 152 mov %rbx, save_rbx
cannam@128 153 mov %r12, save_r12
cannam@128 154 mov %r13, save_r13
cannam@128 155 mov %r14, save_r14
cannam@128 156 mov %r15, save_r15
cannam@128 157
cannam@128 158 /* uInt wmask = s->w_mask; */
cannam@128 159 /* unsigned chain_length = s->max_chain_length; */
cannam@128 160 /* if (s->prev_length >= s->good_match) { */
cannam@128 161 /* chain_length >>= 2; */
cannam@128 162 /* } */
cannam@128 163
cannam@128 164 movl dsPrevLen, %eax
cannam@128 165 movl dsGoodMatch, %ebx
cannam@128 166 cmpl %ebx, %eax
cannam@128 167 movl dsWMask, %eax
cannam@128 168 movl dsMaxChainLen, %chainlenwmask
cannam@128 169 jl LastMatchGood
cannam@128 170 shrl $2, %chainlenwmask
cannam@128 171 LastMatchGood:
cannam@128 172
cannam@128 173 /* chainlen is decremented once beforehand so that the function can */
cannam@128 174 /* use the sign flag instead of the zero flag for the exit test. */
cannam@128 175 /* It is then shifted into the high word, to make room for the wmask */
cannam@128 176 /* value, which it will always accompany. */
cannam@128 177
cannam@128 178 decl %chainlenwmask
cannam@128 179 shll $16, %chainlenwmask
cannam@128 180 orl %eax, %chainlenwmask
cannam@128 181
cannam@128 182 /* if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; */
cannam@128 183
cannam@128 184 movl dsNiceMatch, %eax
cannam@128 185 movl dsLookahead, %ebx
cannam@128 186 cmpl %eax, %ebx
cannam@128 187 jl LookaheadLess
cannam@128 188 movl %eax, %ebx
cannam@128 189 LookaheadLess: movl %ebx, %nicematch
cannam@128 190
cannam@128 191 /* register Bytef *scan = s->window + s->strstart; */
cannam@128 192
cannam@128 193 mov dsWindow, %window
cannam@128 194 movl dsStrStart, %limitd
cannam@128 195 lea (%limit, %window), %scan
cannam@128 196
cannam@128 197 /* Determine how many bytes the scan ptr is off from being */
cannam@128 198 /* dword-aligned. */
cannam@128 199
cannam@128 200 mov %scan, %scanalign
cannam@128 201 negl %scanalignd
cannam@128 202 andl $3, %scanalignd
cannam@128 203
cannam@128 204 /* IPos limit = s->strstart > (IPos)MAX_DIST(s) ? */
cannam@128 205 /* s->strstart - (IPos)MAX_DIST(s) : NIL; */
cannam@128 206
cannam@128 207 movl dsWSize, %eax
cannam@128 208 subl $MIN_LOOKAHEAD, %eax
cannam@128 209 xorl %ecx, %ecx
cannam@128 210 subl %eax, %limitd
cannam@128 211 cmovng %ecx, %limitd
cannam@128 212
cannam@128 213 /* int best_len = s->prev_length; */
cannam@128 214
cannam@128 215 movl dsPrevLen, %bestlend
cannam@128 216
cannam@128 217 /* Store the sum of s->window + best_len in %windowbestlen locally, and in memory. */
cannam@128 218
cannam@128 219 lea (%window, %bestlen), %windowbestlen
cannam@128 220 mov %windowbestlen, _windowbestlen
cannam@128 221
cannam@128 222 /* register ush scan_start = *(ushf*)scan; */
cannam@128 223 /* register ush scan_end = *(ushf*)(scan+best_len-1); */
cannam@128 224 /* Posf *prev = s->prev; */
cannam@128 225
cannam@128 226 movzwl (%scan), %scanstart
cannam@128 227 movzwl -1(%scan, %bestlen), %scanend
cannam@128 228 mov dsPrev, %prev
cannam@128 229
cannam@128 230 /* Jump into the main loop. */
cannam@128 231
cannam@128 232 movl %chainlenwmask, _chainlenwmask
cannam@128 233 jmp LoopEntry
cannam@128 234
cannam@128 235 .balign 16
cannam@128 236
cannam@128 237 /* do {
cannam@128 238 * match = s->window + cur_match;
cannam@128 239 * if (*(ushf*)(match+best_len-1) != scan_end ||
cannam@128 240 * *(ushf*)match != scan_start) continue;
cannam@128 241 * [...]
cannam@128 242 * } while ((cur_match = prev[cur_match & wmask]) > limit
cannam@128 243 * && --chain_length != 0);
cannam@128 244 *
cannam@128 245 * Here is the inner loop of the function. The function will spend the
cannam@128 246 * majority of its time in this loop, and majority of that time will
cannam@128 247 * be spent in the first ten instructions.
cannam@128 248 */
cannam@128 249 LookupLoop:
cannam@128 250 andl %chainlenwmask, %curmatchd
cannam@128 251 movzwl (%prev, %curmatch, 2), %curmatchd
cannam@128 252 cmpl %limitd, %curmatchd
cannam@128 253 jbe LeaveNow
cannam@128 254 subl $0x00010000, %chainlenwmask
cannam@128 255 js LeaveNow
cannam@128 256 LoopEntry: cmpw -1(%windowbestlen, %curmatch), %scanendw
cannam@128 257 jne LookupLoop
cannam@128 258 cmpw %scanstartw, (%window, %curmatch)
cannam@128 259 jne LookupLoop
cannam@128 260
cannam@128 261 /* Store the current value of chainlen. */
cannam@128 262 movl %chainlenwmask, _chainlenwmask
cannam@128 263
cannam@128 264 /* %scan is the string under scrutiny, and %prev to the string we */
cannam@128 265 /* are hoping to match it up with. In actuality, %esi and %edi are */
cannam@128 266 /* both pointed (MAX_MATCH_8 - scanalign) bytes ahead, and %edx is */
cannam@128 267 /* initialized to -(MAX_MATCH_8 - scanalign). */
cannam@128 268
cannam@128 269 mov $(-MAX_MATCH_8), %rdx
cannam@128 270 lea (%curmatch, %window), %windowbestlen
cannam@128 271 lea MAX_MATCH_8(%windowbestlen, %scanalign), %windowbestlen
cannam@128 272 lea MAX_MATCH_8(%scan, %scanalign), %prev
cannam@128 273
cannam@128 274 /* the prefetching below makes very little difference... */
cannam@128 275 prefetcht1 (%windowbestlen, %rdx)
cannam@128 276 prefetcht1 (%prev, %rdx)
cannam@128 277
cannam@128 278 /*
cannam@128 279 * Test the strings for equality, 8 bytes at a time. At the end,
cannam@128 280 * adjust %rdx so that it is offset to the exact byte that mismatched.
cannam@128 281 *
cannam@128 282 * It should be confessed that this loop usually does not represent
cannam@128 283 * much of the total running time. Replacing it with a more
cannam@128 284 * straightforward "rep cmpsb" would not drastically degrade
cannam@128 285 * performance -- unrolling it, for example, makes no difference.
cannam@128 286 */
cannam@128 287
cannam@128 288 #undef USE_SSE /* works, but is 6-7% slower, than non-SSE... */
cannam@128 289
cannam@128 290 LoopCmps:
cannam@128 291 #ifdef USE_SSE
cannam@128 292 /* Preload the SSE registers */
cannam@128 293 movdqu (%windowbestlen, %rdx), %xmm1
cannam@128 294 movdqu (%prev, %rdx), %xmm2
cannam@128 295 pcmpeqb %xmm2, %xmm1
cannam@128 296 movdqu 16(%windowbestlen, %rdx), %xmm3
cannam@128 297 movdqu 16(%prev, %rdx), %xmm4
cannam@128 298 pcmpeqb %xmm4, %xmm3
cannam@128 299 movdqu 32(%windowbestlen, %rdx), %xmm5
cannam@128 300 movdqu 32(%prev, %rdx), %xmm6
cannam@128 301 pcmpeqb %xmm6, %xmm5
cannam@128 302 movdqu 48(%windowbestlen, %rdx), %xmm7
cannam@128 303 movdqu 48(%prev, %rdx), %xmm8
cannam@128 304 pcmpeqb %xmm8, %xmm7
cannam@128 305
cannam@128 306 /* Check the comparisions' results */
cannam@128 307 pmovmskb %xmm1, %rax
cannam@128 308 notw %ax
cannam@128 309 bsfw %ax, %ax
cannam@128 310 jnz LeaveLoopCmps
cannam@128 311
cannam@128 312 /* this is the only iteration of the loop with a possibility of having
cannam@128 313 incremented rdx by 0x108 (each loop iteration add 16*4 = 0x40
cannam@128 314 and (0x40*4)+8=0x108 */
cannam@128 315 add $8, %rdx
cannam@128 316 jz LenMaximum
cannam@128 317 add $8, %rdx
cannam@128 318
cannam@128 319
cannam@128 320 pmovmskb %xmm3, %rax
cannam@128 321 notw %ax
cannam@128 322 bsfw %ax, %ax
cannam@128 323 jnz LeaveLoopCmps
cannam@128 324
cannam@128 325
cannam@128 326 add $16, %rdx
cannam@128 327
cannam@128 328
cannam@128 329 pmovmskb %xmm5, %rax
cannam@128 330 notw %ax
cannam@128 331 bsfw %ax, %ax
cannam@128 332 jnz LeaveLoopCmps
cannam@128 333
cannam@128 334 add $16, %rdx
cannam@128 335
cannam@128 336
cannam@128 337 pmovmskb %xmm7, %rax
cannam@128 338 notw %ax
cannam@128 339 bsfw %ax, %ax
cannam@128 340 jnz LeaveLoopCmps
cannam@128 341
cannam@128 342 add $16, %rdx
cannam@128 343
cannam@128 344 jmp LoopCmps
cannam@128 345 LeaveLoopCmps: add %rax, %rdx
cannam@128 346 #else
cannam@128 347 mov (%windowbestlen, %rdx), %rax
cannam@128 348 xor (%prev, %rdx), %rax
cannam@128 349 jnz LeaveLoopCmps
cannam@128 350
cannam@128 351 mov 8(%windowbestlen, %rdx), %rax
cannam@128 352 xor 8(%prev, %rdx), %rax
cannam@128 353 jnz LeaveLoopCmps8
cannam@128 354
cannam@128 355 mov 16(%windowbestlen, %rdx), %rax
cannam@128 356 xor 16(%prev, %rdx), %rax
cannam@128 357 jnz LeaveLoopCmps16
cannam@128 358
cannam@128 359 add $24, %rdx
cannam@128 360 jnz LoopCmps
cannam@128 361 jmp LenMaximum
cannam@128 362 # if 0
cannam@128 363 /*
cannam@128 364 * This three-liner is tantalizingly simple, but bsf is a slow instruction,
cannam@128 365 * and the complicated alternative down below is quite a bit faster. Sad...
cannam@128 366 */
cannam@128 367
cannam@128 368 LeaveLoopCmps: bsf %rax, %rax /* find the first non-zero bit */
cannam@128 369 shrl $3, %eax /* divide by 8 to get the byte */
cannam@128 370 add %rax, %rdx
cannam@128 371 # else
cannam@128 372 LeaveLoopCmps16:
cannam@128 373 add $8, %rdx
cannam@128 374 LeaveLoopCmps8:
cannam@128 375 add $8, %rdx
cannam@128 376 LeaveLoopCmps: testl $0xFFFFFFFF, %eax /* Check the first 4 bytes */
cannam@128 377 jnz Check16
cannam@128 378 add $4, %rdx
cannam@128 379 shr $32, %rax
cannam@128 380 Check16: testw $0xFFFF, %ax
cannam@128 381 jnz LenLower
cannam@128 382 add $2, %rdx
cannam@128 383 shrl $16, %eax
cannam@128 384 LenLower: subb $1, %al
cannam@128 385 adc $0, %rdx
cannam@128 386 # endif
cannam@128 387 #endif
cannam@128 388
cannam@128 389 /* Calculate the length of the match. If it is longer than MAX_MATCH, */
cannam@128 390 /* then automatically accept it as the best possible match and leave. */
cannam@128 391
cannam@128 392 lea (%prev, %rdx), %rax
cannam@128 393 sub %scan, %rax
cannam@128 394 cmpl $MAX_MATCH, %eax
cannam@128 395 jge LenMaximum
cannam@128 396
cannam@128 397 /* If the length of the match is not longer than the best match we */
cannam@128 398 /* have so far, then forget it and return to the lookup loop. */
cannam@128 399
cannam@128 400 cmpl %bestlend, %eax
cannam@128 401 jg LongerMatch
cannam@128 402 mov _windowbestlen, %windowbestlen
cannam@128 403 mov dsPrev, %prev
cannam@128 404 movl _chainlenwmask, %edx
cannam@128 405 jmp LookupLoop
cannam@128 406
cannam@128 407 /* s->match_start = cur_match; */
cannam@128 408 /* best_len = len; */
cannam@128 409 /* if (len >= nice_match) break; */
cannam@128 410 /* scan_end = *(ushf*)(scan+best_len-1); */
cannam@128 411
cannam@128 412 LongerMatch:
cannam@128 413 movl %eax, %bestlend
cannam@128 414 movl %curmatchd, dsMatchStart
cannam@128 415 cmpl %nicematch, %eax
cannam@128 416 jge LeaveNow
cannam@128 417
cannam@128 418 lea (%window, %bestlen), %windowbestlen
cannam@128 419 mov %windowbestlen, _windowbestlen
cannam@128 420
cannam@128 421 movzwl -1(%scan, %rax), %scanend
cannam@128 422 mov dsPrev, %prev
cannam@128 423 movl _chainlenwmask, %chainlenwmask
cannam@128 424 jmp LookupLoop
cannam@128 425
cannam@128 426 /* Accept the current string, with the maximum possible length. */
cannam@128 427
cannam@128 428 LenMaximum:
cannam@128 429 movl $MAX_MATCH, %bestlend
cannam@128 430 movl %curmatchd, dsMatchStart
cannam@128 431
cannam@128 432 /* if ((uInt)best_len <= s->lookahead) return (uInt)best_len; */
cannam@128 433 /* return s->lookahead; */
cannam@128 434
cannam@128 435 LeaveNow:
cannam@128 436 movl dsLookahead, %eax
cannam@128 437 cmpl %eax, %bestlend
cannam@128 438 cmovngl %bestlend, %eax
cannam@128 439 LookaheadRet:
cannam@128 440
cannam@128 441 /* Restore the registers and return from whence we came. */
cannam@128 442
cannam@128 443 mov save_rsi, %rsi
cannam@128 444 mov save_rbx, %rbx
cannam@128 445 mov save_r12, %r12
cannam@128 446 mov save_r13, %r13
cannam@128 447 mov save_r14, %r14
cannam@128 448 mov save_r15, %r15
cannam@128 449
cannam@128 450 ret
cannam@128 451
cannam@128 452 match_init: ret