mirror of https://git.ffmpeg.org/ffmpeg.git
checkasm: Explicitly declare function prototypes
Now we no longer have to rely on function pointers intentionally declared without specified argument types. This makes it easier to support functions with floating point parameters or return values as well as functions returning 64-bit values on 32-bit architectures. It also avoids having to explicitly cast strides to ptrdiff_t for example. Signed-off-by: Anton Khirnov <anton@khirnov.net>
This commit is contained in:
parent
e13da244f4
commit
515b69f8f8
|
@ -17,9 +17,6 @@ CHECKASMDIRS := $(sort $(dir $(CHECKASMOBJS)))
|
||||||
$(CHECKASMOBJS): | $(CHECKASMDIRS)
|
$(CHECKASMOBJS): | $(CHECKASMDIRS)
|
||||||
OBJDIRS += $(CHECKASMDIRS)
|
OBJDIRS += $(CHECKASMDIRS)
|
||||||
|
|
||||||
# We rely on function pointers intentionally declared without specified argument types.
|
|
||||||
tests/checkasm/%.o: CFLAGS := $(CFLAGS:-Wstrict-prototypes=-Wno-strict-prototypes)
|
|
||||||
|
|
||||||
CHECKASM := tests/checkasm/checkasm$(EXESUF)
|
CHECKASM := tests/checkasm/checkasm$(EXESUF)
|
||||||
|
|
||||||
$(CHECKASM): $(EXEOBJS) $(CHECKASMOBJS) $(FF_STATIC_DEP_LIBS)
|
$(CHECKASM): $(EXEOBJS) $(CHECKASMOBJS) $(FF_STATIC_DEP_LIBS)
|
||||||
|
|
|
@ -43,6 +43,8 @@
|
||||||
#define check_bswap(type) \
|
#define check_bswap(type) \
|
||||||
do { \
|
do { \
|
||||||
int w; \
|
int w; \
|
||||||
|
declare_func(void, type *dst, const type *src, int w); \
|
||||||
|
\
|
||||||
for (w = 0; w < BUF_SIZE / sizeof(type); w++) { \
|
for (w = 0; w < BUF_SIZE / sizeof(type); w++) { \
|
||||||
int offset = (BUF_SIZE / sizeof(type) - w) & 15; /* Test various alignments */ \
|
int offset = (BUF_SIZE / sizeof(type) - w) & 15; /* Test various alignments */ \
|
||||||
randomize_buffers(); \
|
randomize_buffers(); \
|
||||||
|
|
|
@ -111,7 +111,7 @@ static const struct {
|
||||||
|
|
||||||
typedef struct CheckasmFuncVersion {
|
typedef struct CheckasmFuncVersion {
|
||||||
struct CheckasmFuncVersion *next;
|
struct CheckasmFuncVersion *next;
|
||||||
intptr_t (*func)();
|
void *func;
|
||||||
int ok;
|
int ok;
|
||||||
int cpu;
|
int cpu;
|
||||||
int iterations;
|
int iterations;
|
||||||
|
@ -387,10 +387,10 @@ int main(int argc, char *argv[])
|
||||||
/* Decide whether or not the specified function needs to be tested and
|
/* Decide whether or not the specified function needs to be tested and
|
||||||
* allocate/initialize data structures if needed. Returns a pointer to a
|
* allocate/initialize data structures if needed. Returns a pointer to a
|
||||||
* reference function if the function should be tested, otherwise NULL */
|
* reference function if the function should be tested, otherwise NULL */
|
||||||
intptr_t (*checkasm_check_func(intptr_t (*func)(), const char *name, ...))()
|
void *checkasm_check_func(void *func, const char *name, ...)
|
||||||
{
|
{
|
||||||
char name_buf[256];
|
char name_buf[256];
|
||||||
intptr_t (*ref)() = func;
|
void *ref = func;
|
||||||
CheckasmFuncVersion *v;
|
CheckasmFuncVersion *v;
|
||||||
int name_length;
|
int name_length;
|
||||||
va_list arg;
|
va_list arg;
|
||||||
|
|
|
@ -33,7 +33,7 @@ void checkasm_check_bswapdsp(void);
|
||||||
void checkasm_check_h264pred(void);
|
void checkasm_check_h264pred(void);
|
||||||
void checkasm_check_h264qpel(void);
|
void checkasm_check_h264qpel(void);
|
||||||
|
|
||||||
intptr_t (*checkasm_check_func(intptr_t (*func)(), const char *name, ...))() av_printf_format(2, 3);
|
void *checkasm_check_func(void *func, const char *name, ...) av_printf_format(2, 3);
|
||||||
int checkasm_bench_func(void);
|
int checkasm_bench_func(void);
|
||||||
void checkasm_fail_func(const char *msg, ...) av_printf_format(1, 2);
|
void checkasm_fail_func(const char *msg, ...) av_printf_format(1, 2);
|
||||||
void checkasm_update_bench(int iterations, uint64_t cycles);
|
void checkasm_update_bench(int iterations, uint64_t cycles);
|
||||||
|
@ -42,14 +42,16 @@ void checkasm_report(const char *name, ...) av_printf_format(1, 2);
|
||||||
extern AVLFG checkasm_lfg;
|
extern AVLFG checkasm_lfg;
|
||||||
#define rnd() av_lfg_get(&checkasm_lfg)
|
#define rnd() av_lfg_get(&checkasm_lfg)
|
||||||
|
|
||||||
static av_unused intptr_t (*func_ref)();
|
static av_unused void *func_ref, *func_new;
|
||||||
static av_unused intptr_t (*func_new)();
|
|
||||||
|
|
||||||
#define BENCH_RUNS 1000 /* Trade-off between accuracy and speed */
|
#define BENCH_RUNS 1000 /* Trade-off between accuracy and speed */
|
||||||
|
|
||||||
/* Decide whether or not the specified function needs to be tested */
|
/* Decide whether or not the specified function needs to be tested */
|
||||||
#define check_func(func, ...) ((func_new = (intptr_t (*)())func) &&\
|
#define check_func(func, ...) (func_ref = checkasm_check_func((func_new = func), __VA_ARGS__))
|
||||||
(func_ref = checkasm_check_func(func_new, __VA_ARGS__)))
|
|
||||||
|
/* Declare the function prototype. The first argument is the return value, the remaining
|
||||||
|
* arguments are the function parameters. Naming parameters is optional. */
|
||||||
|
#define declare_func(ret, ...) declare_new(ret, __VA_ARGS__) typedef ret func_type(__VA_ARGS__)
|
||||||
|
|
||||||
/* Indicate that the current test has failed */
|
/* Indicate that the current test has failed */
|
||||||
#define fail() checkasm_fail_func("%s:%d", av_basename(__FILE__), __LINE__)
|
#define fail() checkasm_fail_func("%s:%d", av_basename(__FILE__), __LINE__)
|
||||||
|
@ -58,18 +60,16 @@ static av_unused intptr_t (*func_new)();
|
||||||
#define report checkasm_report
|
#define report checkasm_report
|
||||||
|
|
||||||
/* Call the reference function */
|
/* Call the reference function */
|
||||||
#define call_ref(...) func_ref(__VA_ARGS__)
|
#define call_ref(...) ((func_type *)func_ref)(__VA_ARGS__)
|
||||||
|
|
||||||
#if ARCH_X86 && HAVE_YASM
|
#if ARCH_X86 && HAVE_YASM
|
||||||
/* Verifies that clobbered callee-saved registers are properly saved and restored */
|
/* Verifies that clobbered callee-saved registers are properly saved and restored */
|
||||||
intptr_t checkasm_checked_call(intptr_t (*func)(), ...);
|
void checkasm_checked_call(void *func, ...);
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Call the function */
|
#if ARCH_X86_64
|
||||||
#if ARCH_X86_64 && HAVE_YASM
|
|
||||||
/* Evil hack: detect incorrect assumptions that 32-bit ints are zero-extended to 64-bit.
|
/* Evil hack: detect incorrect assumptions that 32-bit ints are zero-extended to 64-bit.
|
||||||
* This is done by clobbering the stack with junk around the stack pointer and calling the
|
* This is done by clobbering the stack with junk around the stack pointer and calling the
|
||||||
* assembly function through x264_checkasm_call with added dummy arguments which forces all
|
* assembly function through checked_call() with added dummy arguments which forces all
|
||||||
* real arguments to be passed on the stack and not in registers. For 32-bit arguments the
|
* real arguments to be passed on the stack and not in registers. For 32-bit arguments the
|
||||||
* upper half of the 64-bit register locations on the stack will now contain junk which will
|
* upper half of the 64-bit register locations on the stack will now contain junk which will
|
||||||
* cause misbehaving functions to either produce incorrect output or segfault. Note that
|
* cause misbehaving functions to either produce incorrect output or segfault. Note that
|
||||||
|
@ -77,14 +77,20 @@ intptr_t checkasm_checked_call(intptr_t (*func)(), ...);
|
||||||
* and false negatives is theoretically possible, but there can never be any false positives.
|
* and false negatives is theoretically possible, but there can never be any false positives.
|
||||||
*/
|
*/
|
||||||
void checkasm_stack_clobber(uint64_t clobber, ...);
|
void checkasm_stack_clobber(uint64_t clobber, ...);
|
||||||
|
#define declare_new(ret, ...) ret (*checked_call)(void *, int, int, int, int, int, __VA_ARGS__)\
|
||||||
|
= (void *)checkasm_checked_call;
|
||||||
#define CLOB (UINT64_C(0xdeadbeefdeadbeef))
|
#define CLOB (UINT64_C(0xdeadbeefdeadbeef))
|
||||||
#define call_new(...) (checkasm_stack_clobber(CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,\
|
#define call_new(...) (checkasm_stack_clobber(CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,\
|
||||||
CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB),\
|
CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB,CLOB),\
|
||||||
checkasm_checked_call(func_new, 0, 0, 0, 0, 0, __VA_ARGS__))
|
checked_call(func_new, 0, 0, 0, 0, 0, __VA_ARGS__))
|
||||||
#elif ARCH_X86_32 && HAVE_YASM
|
#elif ARCH_X86_32
|
||||||
#define call_new(...) checkasm_checked_call(func_new, __VA_ARGS__)
|
#define declare_new(ret, ...) ret (*checked_call)(void *, __VA_ARGS__) = (void *)checkasm_checked_call;
|
||||||
|
#define call_new(...) checked_call(func_new, __VA_ARGS__)
|
||||||
|
#endif
|
||||||
#else
|
#else
|
||||||
#define call_new(...) func_new(__VA_ARGS__)
|
#define declare_new(ret, ...)
|
||||||
|
/* Call the function */
|
||||||
|
#define call_new(...) ((func_type *)func_new)(__VA_ARGS__)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Benchmark the function */
|
/* Benchmark the function */
|
||||||
|
@ -92,7 +98,7 @@ void checkasm_stack_clobber(uint64_t clobber, ...);
|
||||||
#define bench_new(...)\
|
#define bench_new(...)\
|
||||||
do {\
|
do {\
|
||||||
if (checkasm_bench_func()) {\
|
if (checkasm_bench_func()) {\
|
||||||
intptr_t (*tfunc)() = func_new;\
|
func_type *tfunc = func_new;\
|
||||||
uint64_t tsum = 0;\
|
uint64_t tsum = 0;\
|
||||||
int ti, tcount = 0;\
|
int ti, tcount = 0;\
|
||||||
for (ti = 0; ti < BENCH_RUNS; ti++) {\
|
for (ti = 0; ti < BENCH_RUNS; ti++) {\
|
||||||
|
|
|
@ -144,14 +144,16 @@ static void check_pred4x4(H264PredContext *h, uint8_t *buf0, uint8_t *buf1,
|
||||||
if (chroma_format == 1) {
|
if (chroma_format == 1) {
|
||||||
uint8_t *topright = buf0 + 2*16;
|
uint8_t *topright = buf0 + 2*16;
|
||||||
int pred_mode;
|
int pred_mode;
|
||||||
|
declare_func(void, uint8_t *src, const uint8_t *topright, ptrdiff_t stride);
|
||||||
|
|
||||||
for (pred_mode = 0; pred_mode < 15; pred_mode++) {
|
for (pred_mode = 0; pred_mode < 15; pred_mode++) {
|
||||||
if (check_pred_func(h->pred4x4[pred_mode], "4x4", pred4x4_modes[codec][pred_mode])) {
|
if (check_pred_func(h->pred4x4[pred_mode], "4x4", pred4x4_modes[codec][pred_mode])) {
|
||||||
randomize_buffers();
|
randomize_buffers();
|
||||||
call_ref(src0, topright, (ptrdiff_t)12*SIZEOF_PIXEL);
|
call_ref(src0, topright, 12*SIZEOF_PIXEL);
|
||||||
call_new(src1, topright, (ptrdiff_t)12*SIZEOF_PIXEL);
|
call_new(src1, topright, 12*SIZEOF_PIXEL);
|
||||||
if (memcmp(buf0, buf1, BUF_SIZE))
|
if (memcmp(buf0, buf1, BUF_SIZE))
|
||||||
fail();
|
fail();
|
||||||
bench_new(src1, topright, (ptrdiff_t)12*SIZEOF_PIXEL);
|
bench_new(src1, topright, 12*SIZEOF_PIXEL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,15 +163,17 @@ static void check_pred8x8(H264PredContext *h, uint8_t *buf0, uint8_t *buf1,
|
||||||
int codec, int chroma_format, int bit_depth)
|
int codec, int chroma_format, int bit_depth)
|
||||||
{
|
{
|
||||||
int pred_mode;
|
int pred_mode;
|
||||||
|
declare_func(void, uint8_t *src, ptrdiff_t stride);
|
||||||
|
|
||||||
for (pred_mode = 0; pred_mode < 11; pred_mode++) {
|
for (pred_mode = 0; pred_mode < 11; pred_mode++) {
|
||||||
if (check_pred_func(h->pred8x8[pred_mode], (chroma_format == 2) ? "8x16" : "8x8",
|
if (check_pred_func(h->pred8x8[pred_mode], (chroma_format == 2) ? "8x16" : "8x8",
|
||||||
pred8x8_modes[codec][pred_mode])) {
|
pred8x8_modes[codec][pred_mode])) {
|
||||||
randomize_buffers();
|
randomize_buffers();
|
||||||
call_ref(src0, (ptrdiff_t)24*SIZEOF_PIXEL);
|
call_ref(src0, 24*SIZEOF_PIXEL);
|
||||||
call_new(src1, (ptrdiff_t)24*SIZEOF_PIXEL);
|
call_new(src1, 24*SIZEOF_PIXEL);
|
||||||
if (memcmp(buf0, buf1, BUF_SIZE))
|
if (memcmp(buf0, buf1, BUF_SIZE))
|
||||||
fail();
|
fail();
|
||||||
bench_new(src1, (ptrdiff_t)24*SIZEOF_PIXEL);
|
bench_new(src1, 24*SIZEOF_PIXEL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,14 +183,16 @@ static void check_pred16x16(H264PredContext *h, uint8_t *buf0, uint8_t *buf1,
|
||||||
{
|
{
|
||||||
if (chroma_format == 1) {
|
if (chroma_format == 1) {
|
||||||
int pred_mode;
|
int pred_mode;
|
||||||
|
declare_func(void, uint8_t *src, ptrdiff_t stride);
|
||||||
|
|
||||||
for (pred_mode = 0; pred_mode < 9; pred_mode++) {
|
for (pred_mode = 0; pred_mode < 9; pred_mode++) {
|
||||||
if (check_pred_func(h->pred16x16[pred_mode], "16x16", pred16x16_modes[codec][pred_mode])) {
|
if (check_pred_func(h->pred16x16[pred_mode], "16x16", pred16x16_modes[codec][pred_mode])) {
|
||||||
randomize_buffers();
|
randomize_buffers();
|
||||||
call_ref(src0, (ptrdiff_t)48);
|
call_ref(src0, 48);
|
||||||
call_new(src1, (ptrdiff_t)48);
|
call_new(src1, 48);
|
||||||
if (memcmp(buf0, buf1, BUF_SIZE))
|
if (memcmp(buf0, buf1, BUF_SIZE))
|
||||||
fail();
|
fail();
|
||||||
bench_new(src1, (ptrdiff_t)48);
|
bench_new(src1, 48);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,6 +203,8 @@ static void check_pred8x8l(H264PredContext *h, uint8_t *buf0, uint8_t *buf1,
|
||||||
{
|
{
|
||||||
if (chroma_format == 1 && codec_ids[codec] == AV_CODEC_ID_H264) {
|
if (chroma_format == 1 && codec_ids[codec] == AV_CODEC_ID_H264) {
|
||||||
int pred_mode;
|
int pred_mode;
|
||||||
|
declare_func(void, uint8_t *src, int topleft, int topright, ptrdiff_t stride);
|
||||||
|
|
||||||
for (pred_mode = 0; pred_mode < 12; pred_mode++) {
|
for (pred_mode = 0; pred_mode < 12; pred_mode++) {
|
||||||
if (check_pred_func(h->pred8x8l[pred_mode], "8x8l", pred4x4_modes[codec][pred_mode])) {
|
if (check_pred_func(h->pred8x8l[pred_mode], "8x8l", pred4x4_modes[codec][pred_mode])) {
|
||||||
int neighbors;
|
int neighbors;
|
||||||
|
@ -208,11 +216,11 @@ static void check_pred8x8l(H264PredContext *h, uint8_t *buf0, uint8_t *buf1,
|
||||||
continue; /* Those aren't allowed according to the spec */
|
continue; /* Those aren't allowed according to the spec */
|
||||||
|
|
||||||
randomize_buffers();
|
randomize_buffers();
|
||||||
call_ref(src0, has_topleft, has_topright, (ptrdiff_t)24*SIZEOF_PIXEL);
|
call_ref(src0, has_topleft, has_topright, 24*SIZEOF_PIXEL);
|
||||||
call_new(src1, has_topleft, has_topright, (ptrdiff_t)24*SIZEOF_PIXEL);
|
call_new(src1, has_topleft, has_topright, 24*SIZEOF_PIXEL);
|
||||||
if (memcmp(buf0, buf1, BUF_SIZE))
|
if (memcmp(buf0, buf1, BUF_SIZE))
|
||||||
fail();
|
fail();
|
||||||
bench_new(src1, has_topleft, has_topright, (ptrdiff_t)24*SIZEOF_PIXEL);
|
bench_new(src1, has_topleft, has_topright, 24*SIZEOF_PIXEL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,7 @@ void checkasm_check_h264qpel(void)
|
||||||
LOCAL_ALIGNED_16(uint8_t, dst1, [BUF_SIZE]);
|
LOCAL_ALIGNED_16(uint8_t, dst1, [BUF_SIZE]);
|
||||||
H264QpelContext h;
|
H264QpelContext h;
|
||||||
int op, bit_depth, i, j;
|
int op, bit_depth, i, j;
|
||||||
|
declare_func(void, uint8_t *dst, const uint8_t *src, ptrdiff_t stride);
|
||||||
|
|
||||||
for (op = 0; op < 2; op++) {
|
for (op = 0; op < 2; op++) {
|
||||||
qpel_mc_func (*tab)[16] = op ? h.avg_h264_qpel_pixels_tab : h.put_h264_qpel_pixels_tab;
|
qpel_mc_func (*tab)[16] = op ? h.avg_h264_qpel_pixels_tab : h.put_h264_qpel_pixels_tab;
|
||||||
|
@ -67,11 +68,11 @@ void checkasm_check_h264qpel(void)
|
||||||
for (j = 0; j < 16; j++)
|
for (j = 0; j < 16; j++)
|
||||||
if (check_func(tab[i][j], "%s_h264_qpel_%d_mc%d%d_%d", op_name, size, j & 3, j >> 2, bit_depth)) {
|
if (check_func(tab[i][j], "%s_h264_qpel_%d_mc%d%d_%d", op_name, size, j & 3, j >> 2, bit_depth)) {
|
||||||
randomize_buffers();
|
randomize_buffers();
|
||||||
call_ref(dst0, src0, (ptrdiff_t)size * SIZEOF_PIXEL);
|
call_ref(dst0, src0, size * SIZEOF_PIXEL);
|
||||||
call_new(dst1, src1, (ptrdiff_t)size * SIZEOF_PIXEL);
|
call_new(dst1, src1, size * SIZEOF_PIXEL);
|
||||||
if (memcmp(buf0, buf1, BUF_SIZE) || memcmp(dst0, dst1, BUF_SIZE))
|
if (memcmp(buf0, buf1, BUF_SIZE) || memcmp(dst0, dst1, BUF_SIZE))
|
||||||
fail();
|
fail();
|
||||||
bench_new(dst1, src1, (ptrdiff_t)size * SIZEOF_PIXEL);
|
bench_new(dst1, src1, size * SIZEOF_PIXEL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,7 @@ cglobal stack_clobber, 1,2
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
;-----------------------------------------------------------------------------
|
;-----------------------------------------------------------------------------
|
||||||
; intptr_t checkasm_checked_call(intptr_t (*func)(), ...)
|
; void checkasm_checked_call(void *func, ...)
|
||||||
;-----------------------------------------------------------------------------
|
;-----------------------------------------------------------------------------
|
||||||
INIT_XMM
|
INIT_XMM
|
||||||
cglobal checked_call, 2,15,16,max_args*8+8
|
cglobal checked_call, 2,15,16,max_args*8+8
|
||||||
|
@ -167,7 +167,7 @@ cglobal checked_call, 2,15,16,max_args*8+8
|
||||||
%define n6 dword 0x33627ba7
|
%define n6 dword 0x33627ba7
|
||||||
|
|
||||||
;-----------------------------------------------------------------------------
|
;-----------------------------------------------------------------------------
|
||||||
; intptr_t checkasm_checked_call(intptr_t (*func)(), ...)
|
; void checkasm_checked_call(void *func, ...)
|
||||||
;-----------------------------------------------------------------------------
|
;-----------------------------------------------------------------------------
|
||||||
cglobal checked_call, 1,7
|
cglobal checked_call, 1,7
|
||||||
mov r3, n3
|
mov r3, n3
|
||||||
|
|
Loading…
Reference in New Issue