Skip to content

Commit 0bd14a1

Browse files
committed
Merge branch 'bittrick_tests' into 'derivgrind'
Add tests of bit-trick finder instrumentation. See merge request aehle/valgrind!26
2 parents 7319898 + 60529b4 commit 0bd14a1

File tree

8 files changed

+395
-5
lines changed

8 files changed

+395
-5
lines changed

.gitlab-ci.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ test_bar_amd64:
5959
- apt-get install -y build-essential gfortran gdb gcc-multilib g++-multilib gfortran-multilib libc6-dbg clang libomp-dev python3 python3-numpy
6060
- cd derivgrind/diff_tests && python3 run_tests.py bar_amd64* --prefix=$PWD/../../install_lightweight
6161

62+
test_trick_amd64:
63+
stage: test
64+
script:
65+
- export DEBIAN_FRONTEND=noninteractive
66+
- apt-get update
67+
- apt-get install -y build-essential python3 libgcrypt20-dev zlib1g-dev
68+
- cd derivgrind/trick_tests && python3 trick-tests.py --prefix=$PWD/../../install_lightweight
69+
6270
test_perf_x86:
6371
stage: test
6472
script:

derivgrind/bar/dg_bar.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,14 +375,18 @@ void* dg_bar_operation(DiffEnv* diffenv, IROp op,
375375
}
376376
}
377377

378+
void* dg_bar_ccall(DiffEnv* diffenv, IRCallee* cee, IRType retty, IRExpr** args, void** modified_args){
379+
return NULL;
380+
}
381+
378382
const ExpressionHandling dg_bar_expressionhandling = {
379383
&dg_bar_wrtmp,&dg_bar_rdtmp,
380384
&dg_bar_puti,&dg_bar_geti,
381385
&dg_bar_store,&dg_bar_load,
382386
&dg_bar_dirty_storeF80le,&dg_bar_dirty_loadF80le,
383387
&dg_bar_constant,&dg_bar_default_,
384388
&dg_bar_compare,&dg_bar_ite,
385-
&dg_bar_operation
389+
&dg_bar_operation,&dg_bar_ccall
386390
};
387391

388392
void dg_bar_handle_statement(DiffEnv* diffenv, IRStmt* st_orig){

derivgrind/dg_expressionhandling.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,15 @@ void* dg_modify_expression(DiffEnv* diffenv, ExpressionHandling eh, IRExpr* ex){
8383
return eh.geti(diffenv,ex->Iex.GetI.bias,Ity_INVALID,ex->Iex.GetI.descr,ex->Iex.GetI.ix);
8484
} else if (ex->tag==Iex_Load) {
8585
return eh.load(diffenv,ex->Iex.Load.addr,ex->Iex.Load.ty);
86+
} else if (ex->tag==Iex_CCall) {
87+
IRExpr** args = ex->Iex.CCall.args;
88+
int nargs = 0;
89+
while(args[nargs]!=NULL) nargs++;
90+
void** modified_args = LibVEX_Alloc(nargs*sizeof(void*));
91+
for(int i=0; i<nargs; i++){
92+
modified_args[i] = dg_modify_expression(diffenv,eh,args[i]);
93+
}
94+
return eh.ccall(diffenv,ex->Iex.CCall.cee,ex->Iex.CCall.retty,args,modified_args);
8695
} else {
8796
return NULL;
8897
}
@@ -217,7 +226,7 @@ void add_statement_modified(DiffEnv* diffenv, ExpressionHandling eh, IRStmt* st_
217226
IRExpr** args = det->args;
218227
IRExpr* addr = args[0];
219228
IRExpr* expr = args[1];
220-
IRExpr* modified_expr = dg_modify_expression_or_default(diffenv,eh,expr,False,"");
229+
void* modified_expr = dg_modify_expression_or_default(diffenv,eh,expr,False,"");
221230
eh.dirty_storeF80le(diffenv,addr, modified_expr);
222231
}
223232
// The x86g_dirtyhelper_loadF80le dirty call loads 80 bit from

derivgrind/dg_expressionhandling.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,10 @@
5151

5252
/*! Tuple of functions defining how to modify expressions and to instrument statements.
5353
*
54-
* We define two instances:
54+
* We define three instances:
5555
* - dg_dot_expressionhandling in dot/dg_dot.c for forward-mode instrumentation.
5656
* - dg_bar_expressionhandling in bar/dg_bar.c for recording-mode instrumentation.
57+
* - dg_trick_expressionhandling in trick/dg_trick.c for bit-trick-finder instrumentation.
5758
*/
5859
typedef struct {
5960

@@ -158,6 +159,15 @@ typedef struct {
158159
IRExpr* arg1, IRExpr* arg2, IRExpr* arg3, IRExpr* arg4,
159160
void* mod1, void* mod2, void* mod3, void* mod4);
160161

162+
/*! Handling of CCalls.
163+
* \param diffenv - General setup.
164+
* \param cee - Function wrapped by the CCall.
165+
* \param retty - Type of return value of the CCall.
166+
* \param args - NULL-terminated list of argument expressions.
167+
* \param modified_args - NULL-terminated list of the corresponding modified arguments.
168+
*/
169+
void* (*ccall)(DiffEnv* diffenv, IRCallee* cee, IRType retty, IRExpr** args, void** modified_args);
170+
161171
} ExpressionHandling;
162172

163173
/*! Return expression modified for use in the instrumented statement.

derivgrind/dot/dg_dot.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,14 +231,18 @@ void* dg_dot_operation(DiffEnv* diffenv, IROp op,
231231
}
232232
}
233233

234+
void* dg_dot_ccall(DiffEnv* diffenv, IRCallee* cee, IRType retty, IRExpr** args, void** modified_args){
235+
return NULL;
236+
}
237+
234238
const ExpressionHandling dg_dot_expressionhandling = {
235239
&dg_dot_wrtmp,&dg_dot_rdtmp,
236240
&dg_dot_puti,&dg_dot_geti,
237241
&dg_dot_store,&dg_dot_load,
238242
&dg_dot_dirty_storeF80le,&dg_dot_dirty_loadF80le,
239243
&dg_dot_constant,&dg_dot_default_,
240244
&dg_dot_compare,&dg_dot_ite,
241-
&dg_dot_operation
245+
&dg_dot_operation,&dg_dot_ccall
242246
};
243247

244248
void dg_dot_handle_statement(DiffEnv* diffenv, IRStmt* st_orig){

derivgrind/trick/dg_trick.c

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,14 +202,30 @@ void* dg_trick_operation(DiffEnv* diffenv, IROp op,
202202
}
203203
}
204204

205+
void* dg_trick_ccall(DiffEnv* diffenv, IRCallee* cee, IRType retty, IRExpr** args, void** modified_args){
206+
// The result is active if there is an operand with non-zero activity flags.
207+
IRExpr* notActive = IRExpr_Const(IRConst_U1(True));
208+
int i=0;
209+
while(args[i]){
210+
IRType argtype = typeOfIRExpr(diffenv->sb_out->tyenv, args[i]);
211+
notActive = IRExpr_Binop(Iop_And1,notActive,isZero(((IRExpr**)(modified_args[i]))[0],argtype));
212+
i++;
213+
}
214+
IRExpr* flagsLo = IRExpr_ITE(notActive,mkIRConst_zero(retty),mkIRConst_ones(retty));
215+
// The result is always discrete, as we do not consider expressions in the
216+
// AD expression handling right now.
217+
IRExpr* flagsHi = mkIRConst_ones(retty);
218+
return mkIRExprVec_2(flagsLo,flagsHi);
219+
}
220+
205221
const ExpressionHandling dg_trick_expressionhandling = {
206222
&dg_bar_wrtmp,&dg_bar_rdtmp,
207223
&dg_bar_puti,&dg_bar_geti,
208224
&dg_bar_store,&dg_bar_load,
209225
&dg_trick_dirty_storeF80le,&dg_trick_dirty_loadF80le,
210226
&dg_bar_constant,&dg_bar_default_,
211227
&dg_bar_compare,&dg_bar_ite,
212-
&dg_trick_operation
228+
&dg_trick_operation,&dg_trick_ccall
213229
};
214230

215231
void dg_trick_handle_statement(DiffEnv* diffenv, IRStmt* st_orig){
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import sys
2+
import subprocess
3+
import fnmatch
4+
import tempfile
5+
6+
install_dir = "../../install"
7+
temp_dir = None
8+
selected_testcase = "*" # The user might specify a pattern in order to run only a subset of all bit-trick tests.
9+
no_external_dependencies = False # suppress testcases that require external dependencies
10+
for arg in sys.argv[1:]:
11+
if arg.startswith("--prefix="):
12+
install_dir = arg[len("--prefix="):]
13+
elif arg.startswith('--tempdir='):
14+
temp_dir = arg[len('--tempdir='):]
15+
elif arg.startswith('--no-external-dependencies'):
16+
no_external_dependencies = True
17+
elif arg in ['-h', '--help']:
18+
print(
19+
"""Test script to run testcases of the "tricky program" under Derivgrind's
20+
forward-mode and bit-trick-finder instrumentation.
21+
Arguments:
22+
--prefix=path .. Path of Derivgrind installation.
23+
--tempdir=path .. Temporary directory used by this script.
24+
--no-external-dependencies
25+
.. If certain libraries required for some testcases (libgcrypt,libz) are
26+
missing, do not run these.
27+
testcase_name .. Run only a single or few testcases; "*" is a wildcard.
28+
""")
29+
exit(0)
30+
31+
32+
else:
33+
selected_testcase = arg
34+
if temp_dir == None:
35+
t = tempfile.TemporaryDirectory()
36+
temp_dir = t.name
37+
38+
subprocess.run(["g++"] + (["-DTRICKTEST_NO_EXTERNAL_DEPENDENCIES"] if no_external_dependencies else []) +["tricky-program.cpp", "-o", temp_dir+"/tricky-program", "-O0", "-mfpmath=sse", "-g", "-I"+install_dir+"/include"] + ([] if no_external_dependencies else ["-lgcrypt","-lz"]) )
39+
40+
# Map testcase name to a tuple of booleans.
41+
# The first element indicates whether external dependencies are required.
42+
# The secomd element indicates whether a wrong derivative is reported as an error.
43+
# As we mostly test unsupported bit-tricks here, this will usually be False.
44+
# The third element indicates whether a wrong derivative without bit-trick finder
45+
# warning is reported as an error. This is usually True, except for known false negatives.
46+
tests = {
47+
"integer_addition_to_exponent_double": (False,False,True),
48+
"integer_addition_to_exponent_float": (False,False,True),
49+
"incomplete_masking_to_perform_frexp_double": (False,False,True),
50+
"incomplete_masking_to_perform_frexp_float": (False,False,True),
51+
"exploiting_imprecisions_for_rounding_double": (False,False,False),
52+
"exploiting_imprecisions_for_rounding_float": (False,False,False),
53+
"encrypt_decrypt": (True,False,True),
54+
"compress_inflate": (True,False,False),
55+
"multiple_mmap": (True,False,False),
56+
}
57+
fail = False
58+
for test in tests:
59+
if fnmatch.fnmatchcase(test,selected_testcase):
60+
print(test+":")
61+
# If test requires external dependencies but they are disabled, skip.
62+
if tests[test][0] and no_external_dependencies:
63+
print(" ext deps disabled - SKIP")
64+
continue
65+
66+
# Test correctness of forward-mode derivatives.
67+
forwardrun = subprocess.run([install_dir+"/bin/valgrind", "--tool=derivgrind", temp_dir+"/tricky-program", test], capture_output=True)
68+
forward_correct = (forwardrun.stdout.decode("utf-8").find("WRONG FORWARD-MODE DERIVATIVE")==-1)
69+
if forward_correct:
70+
forward_output = "correct derivative - OK "
71+
elif not tests[test][1]:
72+
forward_output = "wrong derivative - OK "
73+
else:
74+
forward_output = "wrong derivative - FAIL"
75+
fail = True
76+
print(" "+forward_output)
77+
78+
# Test whether the bit-trick finding instrumentation produces a warning.
79+
trickrun = subprocess.run([install_dir+"/bin/valgrind", "--tool=derivgrind", "--trick=yes", temp_dir+"/tricky-program", test], capture_output=True)
80+
trick_found = (trickrun.stderr.decode("utf-8").find("Active discrete data used as floating-point operand.")!=-1)
81+
if trick_found:
82+
trick_output = "trick found - OK "
83+
elif not tests[test][2]:
84+
trick_output = "trick not found - OK "
85+
else:
86+
trick_output = "trick not found - FAIL"
87+
fail = True
88+
print(" "+trick_output)
89+
90+
exit(fail)
91+
92+
93+

0 commit comments

Comments
 (0)