'buffer' is both passed into the inline assembly code and modified by it. See https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html, 6.47.2.3. With GCC 4, this commit does not change the generated assembly code at all. With GCC 8, this commit fixes an assembly error: /tmp/{foo}.s: Assembler messages: /tmp/{foo}.s:775: Error: registers may not be the same -- `str r9,[r9],#4' I'm not sure why that error went unnoticed, since I definitely benchmarked the previous commit with GCC 8. Anyhow, this commit changes the generated assembly code slightly but does not alter performance. With Clang 10, this commit changes the generated assembly code slightly but does not alter performance. Refer to #529
132 lines
3.9 KiB
C
132 lines
3.9 KiB
C
/*
|
|
* jchuff.h
|
|
*
|
|
* This file was part of the Independent JPEG Group's software:
|
|
* Copyright (C) 1991-1997, Thomas G. Lane.
|
|
* libjpeg-turbo Modifications:
|
|
* Copyright (C) 2009, 2018, 2021, D. R. Commander.
|
|
* Copyright (C) 2018, Matthias Räncker.
|
|
* Copyright (C) 2020-2021, Arm Limited.
|
|
* For conditions of distribution and use, see the accompanying README.ijg
|
|
* file.
|
|
*/
|
|
|
|
/* Expanded entropy encoder object for Huffman encoding.
|
|
*
|
|
* The savable_state subrecord contains fields that change within an MCU,
|
|
* but must not be updated permanently until we complete the MCU.
|
|
*/
|
|
|
|
#if defined(__aarch64__) || defined(_M_ARM64)
|
|
#define BIT_BUF_SIZE 64
|
|
#else
|
|
#define BIT_BUF_SIZE 32
|
|
#endif
|
|
|
|
typedef struct {
|
|
size_t put_buffer; /* current bit accumulation buffer */
|
|
int free_bits; /* # of bits available in it */
|
|
int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */
|
|
} savable_state;
|
|
|
|
typedef struct {
|
|
JOCTET *next_output_byte; /* => next byte to write in buffer */
|
|
size_t free_in_buffer; /* # of byte spaces remaining in buffer */
|
|
savable_state cur; /* Current bit buffer & DC state */
|
|
j_compress_ptr cinfo; /* dump_buffer needs access to this */
|
|
int simd;
|
|
} working_state;
|
|
|
|
/* Outputting bits to the file */
|
|
|
|
/* Output byte b and, speculatively, an additional 0 byte. 0xFF must be encoded
|
|
* as 0xFF 0x00, so the output buffer pointer is advanced by 2 if the byte is
|
|
* 0xFF. Otherwise, the output buffer pointer is advanced by 1, and the
|
|
* speculative 0 byte will be overwritten by the next byte.
|
|
*/
|
|
#define EMIT_BYTE(b) { \
|
|
buffer[0] = (JOCTET)(b); \
|
|
buffer[1] = 0; \
|
|
buffer -= -2 + ((JOCTET)(b) < 0xFF); \
|
|
}
|
|
|
|
/* Output the entire bit buffer. If there are no 0xFF bytes in it, then write
|
|
* directly to the output buffer. Otherwise, use the EMIT_BYTE() macro to
|
|
* encode 0xFF as 0xFF 0x00.
|
|
*/
|
|
#if defined(__aarch64__) || defined(_M_ARM64)
|
|
|
|
#define FLUSH() { \
|
|
if (put_buffer & 0x8080808080808080 & ~(put_buffer + 0x0101010101010101)) { \
|
|
EMIT_BYTE(put_buffer >> 56) \
|
|
EMIT_BYTE(put_buffer >> 48) \
|
|
EMIT_BYTE(put_buffer >> 40) \
|
|
EMIT_BYTE(put_buffer >> 32) \
|
|
EMIT_BYTE(put_buffer >> 24) \
|
|
EMIT_BYTE(put_buffer >> 16) \
|
|
EMIT_BYTE(put_buffer >> 8) \
|
|
EMIT_BYTE(put_buffer ) \
|
|
} else { \
|
|
*((uint64_t *)buffer) = BUILTIN_BSWAP64(put_buffer); \
|
|
buffer += 8; \
|
|
} \
|
|
}
|
|
|
|
#else
|
|
|
|
#if defined(_MSC_VER) && !defined(__clang__)
|
|
#define SPLAT() { \
|
|
buffer[0] = (JOCTET)(put_buffer >> 24); \
|
|
buffer[1] = (JOCTET)(put_buffer >> 16); \
|
|
buffer[2] = (JOCTET)(put_buffer >> 8); \
|
|
buffer[3] = (JOCTET)(put_buffer ); \
|
|
buffer += 4; \
|
|
}
|
|
#else
|
|
#define SPLAT() { \
|
|
put_buffer = __builtin_bswap32(put_buffer); \
|
|
__asm__("str %1, [%0], #4" : "+r" (buffer) : "r" (put_buffer)); \
|
|
}
|
|
#endif
|
|
|
|
#define FLUSH() { \
|
|
if (put_buffer & 0x80808080 & ~(put_buffer + 0x01010101)) { \
|
|
EMIT_BYTE(put_buffer >> 24) \
|
|
EMIT_BYTE(put_buffer >> 16) \
|
|
EMIT_BYTE(put_buffer >> 8) \
|
|
EMIT_BYTE(put_buffer ) \
|
|
} else { \
|
|
SPLAT(); \
|
|
} \
|
|
}
|
|
|
|
#endif
|
|
|
|
/* Fill the bit buffer to capacity with the leading bits from code, then output
|
|
* the bit buffer and put the remaining bits from code into the bit buffer.
|
|
*/
|
|
#define PUT_AND_FLUSH(code, size) { \
|
|
put_buffer = (put_buffer << (size + free_bits)) | (code >> -free_bits); \
|
|
FLUSH() \
|
|
free_bits += BIT_BUF_SIZE; \
|
|
put_buffer = code; \
|
|
}
|
|
|
|
/* Insert code into the bit buffer and output the bit buffer if needed.
|
|
* NOTE: We can't flush with free_bits == 0, since the left shift in
|
|
* PUT_AND_FLUSH() would have undefined behavior.
|
|
*/
|
|
#define PUT_BITS(code, size) { \
|
|
free_bits -= size; \
|
|
if (free_bits < 0) \
|
|
PUT_AND_FLUSH(code, size) \
|
|
else \
|
|
put_buffer = (put_buffer << size) | code; \
|
|
}
|
|
|
|
#define PUT_CODE(code, size, diff) { \
|
|
diff |= code << nbits; \
|
|
nbits += size; \
|
|
PUT_BITS(diff, nbits) \
|
|
}
|