📄 inffast.s
字号:
/*
* inffast.S is a hand tuned assembler version of:
*
* inffast.c -- fast decoding
* Copyright (C) 1995-2003 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*
* Copyright (C) 2003 Chris Anderson <christop@charm.net>
* Please use the copyright conditions above.
*
* This version (Jan-23-2003) of inflate_fast was coded and tested under
* GNU/Linux on a pentium 3, using the gcc-3.2 compiler distribution. On that
* machine, I found that gzip style archives decompressed about 20% faster than
* the gcc-3.2 -O3 -fomit-frame-pointer compiled version. Your results will
* depend on how large of a buffer is used for z_stream.next_in & next_out
* (8K-32K worked best for my 256K cpu cache) and how much overhead there is in
* stream processing I/O and crc32/addler32. In my case, this routine used
* 70% of the cpu time and crc32 used 20%.
*
* I am confident that this version will work in the general case, but I have
* not tested a wide variety of datasets or a wide variety of platforms.
*
* Jan-24-2003 -- Added -DUSE_MMX define for slightly faster inflating.
* It should be a runtime flag instead of compile time flag...
*
* Jan-26-2003 -- Added runtime check for MMX support with cpuid instruction.
* With -DUSE_MMX, only MMX code is compiled. With -DNO_MMX, only non-MMX code
* is compiled. Without either option, runtime detection is enabled. Runtime
* detection should work on all modern cpus and the recomended algorithm (flip
* ID bit on eflags and then use the cpuid instruction) is used in many
* multimedia applications. Tested under win2k with gcc-2.95 and gas-2.12
* distributed with cygwin3. Compiling with gcc-2.95 -c inffast.S -o
* inffast.obj generates a COFF object which can then be linked with MSVC++
* compiled code. Tested under FreeBSD 4.7 with gcc-2.95.
*
* Jan-28-2003 -- Tested Athlon XP... MMX mode is slower than no MMX (and
* slower than compiler generated code). Adjusted cpuid check to use the MMX
* code only for Pentiums < P4 until I have more data on the P4. Speed
* improvment is only about 15% on the Athlon when compared with code generated
* with MSVC++. Not sure yet, but I think the P4 will also be slower using the
* MMX mode because many of it's x86 ALU instructions execute in .5 cycles and
* have less latency than MMX ops. Added code to buffer the last 11 bytes of
* the input stream since the MMX code grabs bits in chunks of 32, which
* differs from the inffast.c algorithm. I don't think there would have been
* read overruns where a page boundary was crossed (a segfault), but there
* could have been overruns when next_in ends on unaligned memory (unintialized
* memory read).
*
* Mar-13-2003 -- P4 MMX is slightly slower than P4 NO_MMX. I created a C
* version of the non-MMX code so that it doesn't depend on zstrm and zstate
* structure offsets which are hard coded in this file. This was last tested
* with zlib-1.2.0 which is currently in beta testing, newer versions of this
* and inffas86.c can be found at http://www.eetbeetee.com/zlib/ and
* http://www.charm.net/~christop/zlib/
*/
/*
* if you have underscore linking problems (_inflate_fast undefined), try
* using -DGAS_COFF
*/
#if ! defined( GAS_COFF ) && ! defined( GAS_ELF )
#if defined( WIN32 ) || defined( __CYGWIN__ )
#define GAS_COFF /* windows object format */
#else
#define GAS_ELF
#endif
#endif /* ! GAS_COFF && ! GAS_ELF */
#if defined( GAS_COFF )
/* coff externals have underscores */
#define inflate_fast _inflate_fast
#define inflate_fast_use_mmx _inflate_fast_use_mmx
#endif /* GAS_COFF */
.file "inffast.S"
.globl inflate_fast
.text
.align 4,0
.L_invalid_literal_length_code_msg:
.string "invalid literal/length code"
.align 4,0
.L_invalid_distance_code_msg:
.string "invalid distance code"
.align 4,0
.L_invalid_distance_too_far_msg:
.string "invalid distance too far back"
#if ! defined( NO_MMX )
.align 4,0
.L_mask: /* mask[N] = ( 1 << N ) - 1 */
.long 0
.long 1
.long 3
.long 7
.long 15
.long 31
.long 63
.long 127
.long 255
.long 511
.long 1023
.long 2047
.long 4095
.long 8191
.long 16383
.long 32767
.long 65535
.long 131071
.long 262143
.long 524287
.long 1048575
.long 2097151
.long 4194303
.long 8388607
.long 16777215
.long 33554431
.long 67108863
.long 134217727
.long 268435455
.long 536870911
.long 1073741823
.long 2147483647
.long 4294967295
#endif /* NO_MMX */
.text
/*
* struct z_stream offsets, in zlib.h
*/
#define next_in_strm 0 /* strm->next_in */
#define avail_in_strm 4 /* strm->avail_in */
#define next_out_strm 12 /* strm->next_out */
#define avail_out_strm 16 /* strm->avail_out */
#define msg_strm 24 /* strm->msg */
#define state_strm 28 /* strm->state */
/*
* struct inflate_state offsets, in inflate.h
*/
#define mode_state 0 /* state->mode */
#define wsize_state 32 /* state->wsize */
#define write_state 40 /* state->write */
#define window_state 44 /* state->window */
#define hold_state 48 /* state->hold */
#define bits_state 52 /* state->bits */
#define lencode_state 68 /* state->lencode */
#define distcode_state 72 /* state->distcode */
#define lenbits_state 76 /* state->lenbits */
#define distbits_state 80 /* state->distbits */
/*
* inflate_fast's activation record
*/
#define local_var_size 64 /* how much local space for vars */
#define strm_sp 88 /* first arg: z_stream * (local_var_size + 24) */
#define start_sp 92 /* second arg: unsigned int (local_var_size + 28) */
/*
* offsets for local vars on stack
*/
#define out 60 /* unsigned char* */
#define window 56 /* unsigned char* */
#define wsize 52 /* unsigned int */
#define write 48 /* unsigned int */
#define in 44 /* unsigned char* */
#define beg 40 /* unsigned char* */
#define buf 28 /* char[ 12 ] */
#define len 24 /* unsigned int */
#define last 20 /* unsigned char* */
#define end 16 /* unsigned char* */
#define dcode 12 /* code* */
#define lcode 8 /* code* */
#define dmask 4 /* unsigned int */
#define lmask 0 /* unsigned int */
/*
* typedef enum inflate_mode consts, in inflate.h
*/
#define INFLATE_MODE_TYPE 11 /* state->mode flags enum-ed in inflate.h */
#define INFLATE_MODE_BAD 26
#if ! defined( USE_MMX ) && ! defined( NO_MMX )
#define RUN_TIME_MMX
#define CHECK_MMX 1
#define DO_USE_MMX 2
#define DONT_USE_MMX 3
.globl inflate_fast_use_mmx
.data
.align 4,0
inflate_fast_use_mmx: /* integer flag for run time control 1=check,2=mmx,3=no */
.long CHECK_MMX
#if defined( GAS_ELF )
/* elf info */
.type inflate_fast_use_mmx,@object
.size inflate_fast_use_mmx,4
#endif
#endif /* RUN_TIME_MMX */
#if defined( GAS_COFF )
/* coff info: scl 2 = extern, type 32 = function */
.def inflate_fast; .scl 2; .type 32; .endef
#endif
.text
.align 32,0x90
inflate_fast:
pushl %edi
pushl %esi
pushl %ebp
pushl %ebx
pushf /* save eflags (strm_sp, state_sp assumes this is 32 bits) */
subl $local_var_size, %esp
cld
#define strm_r %esi
#define state_r %edi
movl strm_sp(%esp), strm_r
movl state_strm(strm_r), state_r
/* in = strm->next_in;
* out = strm->next_out;
* last = in + strm->avail_in - 11;
* beg = out - (start - strm->avail_out);
* end = out + (strm->avail_out - 257);
*/
movl avail_in_strm(strm_r), %edx
movl next_in_strm(strm_r), %eax
addl %eax, %edx /* avail_in += next_in */
subl $11, %edx /* avail_in -= 11 */
movl %eax, in(%esp)
movl %edx, last(%esp)
movl start_sp(%esp), %ebp
movl avail_out_strm(strm_r), %ecx
movl next_out_strm(strm_r), %ebx
subl %ecx, %ebp /* start -= avail_out */
negl %ebp /* start = -start */
addl %ebx, %ebp /* start += next_out */
subl $257, %ecx /* avail_out -= 257 */
addl %ebx, %ecx /* avail_out += out */
movl %ebx, out(%esp)
movl %ebp, beg(%esp)
movl %ecx, end(%esp)
/* wsize = state->wsize;
* write = state->write;
* window = state->window;
* hold = state->hold;
* bits = state->bits;
* lcode = state->lencode;
* dcode = state->distcode;
* lmask = ( 1 << state->lenbits ) - 1;
* dmask = ( 1 << state->distbits ) - 1;
*/
movl lencode_state(state_r), %eax
movl distcode_state(state_r), %ecx
movl %eax, lcode(%esp)
movl %ecx, dcode(%esp)
movl $1, %eax
movl lenbits_state(state_r), %ecx
shll %cl, %eax
decl %eax
movl %eax, lmask(%esp)
movl $1, %eax
movl distbits_state(state_r), %ecx
shll %cl, %eax
decl %eax
movl %eax, dmask(%esp)
movl wsize_state(state_r), %eax
movl write_state(state_r), %ecx
movl window_state(state_r), %edx
movl %eax, wsize(%esp)
movl %ecx, write(%esp)
movl %edx, window(%esp)
movl hold_state(state_r), %ebp
movl bits_state(state_r), %ebx
#undef strm_r
#undef state_r
#define in_r %esi
#define from_r %esi
#define out_r %edi
movl in(%esp), in_r
movl last(%esp), %ecx
cmpl in_r, %ecx
ja .L_align_long /* if in < last */
addl $11, %ecx /* ecx = &in[ avail_in ] */
subl in_r, %ecx /* ecx = avail_in */
movl $12, %eax
subl %ecx, %eax /* eax = 12 - avail_in */
leal buf(%esp), %edi
rep movsb /* memcpy( buf, in, avail_in ) */
movl %eax, %ecx
xorl %eax, %eax
rep stosb /* memset( &buf[ avail_in ], 0, 12 - avail_in ) */
leal buf(%esp), in_r /* in = buf */
movl in_r, last(%esp) /* last = in, do just one iteration */
jmp .L_is_aligned
/* align in_r on long boundary */
.L_align_long:
testl $3, in_r
jz .L_is_aligned
xorl %eax, %eax
movb (in_r), %al
incl in_r
movl %ebx, %ecx
addl $8, %ebx
shll %cl, %eax
orl %eax, %ebp
jmp .L_align_long
.L_is_aligned:
movl out(%esp), out_r
#if defined( NO_MMX )
jmp .L_do_loop
#endif
#if defined( USE_MMX )
jmp .L_init_mmx
#endif
/*** Runtime MMX check ***/
#if defined( RUN_TIME_MMX )
.L_check_mmx:
cmpl $DO_USE_MMX, inflate_fast_use_mmx
je .L_init_mmx
ja .L_do_loop /* > 2 */
pushl %eax
pushl %ebx
pushl %ecx
pushl %edx
pushf
movl (%esp), %eax /* copy eflags to eax */
xorl $0x200000, (%esp) /* try toggling ID bit of eflags (bit 21)
* to see if cpu supports cpuid...
* ID bit method not supported by NexGen but
* bios may load a cpuid instruction and
* cpuid may be disabled on Cyrix 5-6x86 */
popf
pushf
popl %edx /* copy new eflags to edx */
xorl %eax, %edx /* test if ID bit is flipped */
jz .L_dont_use_mmx /* not flipped if zero */
xorl %eax, %eax
cpuid
cmpl $0x756e6547, %ebx /* check for GenuineIntel in ebx,ecx,edx */
jne .L_dont_use_mmx
cmpl $0x6c65746e, %ecx
jne .L_dont_use_mmx
cmpl $0x49656e69, %edx
jne .L_dont_use_mmx
movl $1, %eax
cpuid /* get cpu features */
shrl $8, %eax
andl $15, %eax
cmpl $6, %eax /* check for Pentium family, is 0xf for P4 */
jne .L_dont_use_mmx
testl $0x800000, %edx /* test if MMX feature is set (bit 23) */
jnz .L_use_mmx
jmp .L_dont_use_mmx
.L_use_mmx:
movl $DO_USE_MMX, inflate_fast_use_mmx
jmp .L_check_mmx_pop
.L_dont_use_mmx:
movl $DONT_USE_MMX, inflate_fast_use_mmx
.L_check_mmx_pop:
popl %edx
popl %ecx
popl %ebx
popl %eax
jmp .L_check_mmx
#endif
/*** Non-MMX code ***/
#if defined ( NO_MMX ) || defined( RUN_TIME_MMX )
#define hold_r %ebp
#define bits_r %bl
#define bitslong_r %ebx
.align 32,0x90
.L_while_test:
/* while (in < last && out < end)
*/
cmpl out_r, end(%esp)
jbe .L_break_loop /* if (out >= end) */
cmpl in_r, last(%esp)
jbe .L_break_loop
.L_do_loop:
/* regs: %esi = in, %ebp = hold, %bl = bits, %edi = out
*
* do {
* if (bits < 15) {
* hold |= *((unsigned short *)in)++ << bits;
* bits += 16
* }
* this = lcode[hold & lmask]
*/
cmpb $15, bits_r
ja .L_get_length_code /* if (15 < bits) */
xorl %eax, %eax
lodsw /* al = *(ushort *)in++ */
movb bits_r, %cl /* cl = bits, needs it for shifting */
addb $16, bits_r /* bits += 16 */
shll %cl, %eax
orl %eax, hold_r /* hold |= *((ushort *)in)++ << bits */
.L_get_length_code:
movl lmask(%esp), %edx /* edx = lmask */
movl lcode(%esp), %ecx /* ecx = lcode */
andl hold_r, %edx /* edx &= hold */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -