From: Julian Seward Date: Sun, 9 Dec 2007 02:08:42 +0000 (+0000) Subject: Don't do comparisons of (signed) Words by merely subtracting them, as X-Git-Tag: svn/VALGRIND_3_3_0~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=37742f8d111800e6cee8f7e2ec468bd6ae5b28e2;p=thirdparty%2Fvalgrind.git Don't do comparisons of (signed) Words by merely subtracting them, as this does not always produce correct results. Instead use a slower but correct method. Fixes #147545. (Nick Nethercote, Tom Hughes et al) git-svn-id: svn://svn.valgrind.org/valgrind/trunk@7283 --- diff --git a/coregrind/m_oset.c b/coregrind/m_oset.c index b61820f984..ad3b66dde2 100644 --- a/coregrind/m_oset.c +++ b/coregrind/m_oset.c @@ -175,7 +175,17 @@ void* fast_key_of_node(AvlNode* n) // Compare the first word of each element. Inlining is *crucial*. static inline Word fast_cmp(void* k, AvlNode* n) { - return ( *(Word*)k - *(Word*)elem_of_node(n) ); + Word w1 = *(Word*)k; + Word w2 = *(Word*)elem_of_node(n); + // In previous versions, we tried to do this faster by doing + // "return w1 - w2". But it didn't work reliably, because the + // complete result of subtracting two N-bit numbers is an N+1-bit + // number, and what the caller is interested in is the sign of + // the complete N+1-bit result. The branching version is slightly + // slower, but safer and easier to understand. + if (w1 > w2) return 1; + if (w1 < w2) return -1; + return 0; } // Compare a key and an element. Inlining is *crucial*. @@ -490,22 +500,23 @@ static AvlNode* avl_lookup(AvlTree* t, void* k) while (True) { if (curr == NULL) return NULL; cmpres = slow_cmp(t, k, curr); - if (cmpres < 0) curr = curr->left; else - if (cmpres > 0) curr = curr->right; else - return curr; + if (cmpres < 0) curr = curr->left; + else if (cmpres > 0) curr = curr->right; + else return curr; } } else { // Fast-track special case. We use the no-check version of // elem_of_node because it saves about 10% on lookup time. This // shouldn't be very dangerous because each node will have been // checked on insertion. - Word kk = *(Word*)k; + Word w1 = *(Word*)k; + Word w2; while (True) { if (curr == NULL) return NULL; - cmpres = kk - *(Word*)elem_of_node_no_check(curr); - if (cmpres < 0) curr = curr->left; else - if (cmpres > 0) curr = curr->right; else - return curr; + w2 = *(Word*)elem_of_node_no_check(curr); + if (w1 < w2) curr = curr->left; + else if (w1 > w2) curr = curr->right; + else return curr; } } } diff --git a/include/pub_tool_oset.h b/include/pub_tool_oset.h index 860a277183..eafdc9a564 100644 --- a/include/pub_tool_oset.h +++ b/include/pub_tool_oset.h @@ -72,7 +72,7 @@ typedef struct _OSet OSet; -// - Cmp: returns -1, 0 or 1 if key is <=, == or >= elem. +// - Cmp: returns -1, 0 or 1 if key is <, == or > elem. // - Alloc: allocates a chunk of memory. // - Free: frees a chunk of memory allocated with Alloc.