Skip to content

Commit 8d50d0f

Browse files
committed
fix aliasing bug in util.cpp
We use a template function called "cast" to get raw access to fields in in the VM. In particular, we use this function in util.cpp to treat reference fields as intptr_t fields so we can use the least significant bit as the red/black flag in red/black tree nodes. Unfortunately, this runs afoul of the type aliasing rules in C/C++, and the compiler is permitted to optimize in a way that assumes such aliasing cannot occur. Such optimization caused all the nodes in the tree to be black, leading to extremely unbalanced trees and thus slow performance. The fix in this case is to use the __may_alias__ attribute to tell the compiler we're doing something devious. I've also used this technique to avoid other potential aliasing problems. There may be others lurking, so a complete audit of the VM might be a good idea.
1 parent cb7dc1a commit 8d50d0f

File tree

4 files changed

+29
-17
lines changed

4 files changed

+29
-17
lines changed

src/common.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ typedef uint64_t uintptr_t;
5959
# error "unsupported architecture"
6060
# endif
6161

62+
typedef intptr_t alias_t;
63+
6264
#else // not _MSC_VER
6365

6466
# include "stdint.h"
@@ -86,6 +88,8 @@ typedef uint64_t uintptr_t;
8688
# error "unsupported architecture"
8789
# endif
8890

91+
typedef intptr_t __attribute__((__may_alias__)) alias_t;
92+
8993
#endif // not _MSC_VER
9094

9195
#undef JNIEXPORT

src/machine.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2145,8 +2145,8 @@ class HeapClient: public Heap::Client {
21452145
memcpy(dst, src, n * BytesPerWord);
21462146

21472147
if (hashTaken(t, src)) {
2148-
cast<uintptr_t>(dst, 0) &= PointerMask;
2149-
cast<uintptr_t>(dst, 0) |= ExtendedMark;
2148+
cast<alias_t>(dst, 0) &= PointerMask;
2149+
cast<alias_t>(dst, 0) |= ExtendedMark;
21502150
extendedWord(t, dst, base) = takeHash(t, src);
21512151
}
21522152
}
@@ -2781,7 +2781,7 @@ allocate3(Thread* t, Allocator* allocator, Machine::AllocationType type,
27812781

27822782
memset(o, 0, sizeInBytes);
27832783

2784-
cast<uintptr_t>(o, 0) = FixedMark;
2784+
cast<alias_t>(o, 0) = FixedMark;
27852785

27862786
t->m->fixedFootprint += total;
27872787

@@ -2796,7 +2796,7 @@ allocate3(Thread* t, Allocator* allocator, Machine::AllocationType type,
27962796

27972797
memset(o, 0, sizeInBytes);
27982798

2799-
cast<uintptr_t>(o, 0) = FixedMark;
2799+
cast<alias_t>(o, 0) = FixedMark;
28002800

28012801
return o;
28022802
}

src/machine.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1878,8 +1878,8 @@ setObjectClass(Thread*, object o, object value)
18781878
{
18791879
cast<object>(o, 0)
18801880
= reinterpret_cast<object>
1881-
(reinterpret_cast<uintptr_t>(value)
1882-
| (reinterpret_cast<uintptr_t>(cast<object>(o, 0)) & (~PointerMask)));
1881+
(reinterpret_cast<alias_t>(value)
1882+
| (reinterpret_cast<alias_t>(cast<object>(o, 0)) & (~PointerMask)));
18831883
}
18841884

18851885
inline const char*
@@ -2094,19 +2094,19 @@ setType(Thread* t, Machine::Type type, object value)
20942094
inline bool
20952095
objectFixed(Thread*, object o)
20962096
{
2097-
return (cast<uintptr_t>(o, 0) & (~PointerMask)) == FixedMark;
2097+
return (cast<alias_t>(o, 0) & (~PointerMask)) == FixedMark;
20982098
}
20992099

21002100
inline bool
21012101
objectExtended(Thread*, object o)
21022102
{
2103-
return (cast<uintptr_t>(o, 0) & (~PointerMask)) == ExtendedMark;
2103+
return (cast<alias_t>(o, 0) & (~PointerMask)) == ExtendedMark;
21042104
}
21052105

21062106
inline bool
21072107
hashTaken(Thread*, object o)
21082108
{
2109-
return (cast<uintptr_t>(o, 0) & (~PointerMask)) == HashTakenMark;
2109+
return (cast<alias_t>(o, 0) & (~PointerMask)) == HashTakenMark;
21102110
}
21112111

21122112
inline unsigned
@@ -2237,7 +2237,7 @@ markHashTaken(Thread* t, object o)
22372237

22382238
ACQUIRE_RAW(t, t->m->heapLock);
22392239

2240-
cast<uintptr_t>(o, 0) |= HashTakenMark;
2240+
cast<alias_t>(o, 0) |= HashTakenMark;
22412241
t->m->heap->pad(o);
22422242
}
22432243

src/util.cpp

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,32 +66,32 @@ inline object
6666
getTreeNodeValue(Thread*, object n)
6767
{
6868
return reinterpret_cast<object>
69-
(cast<intptr_t>(n, TreeNodeValue) & PointerMask);
69+
(cast<alias_t>(n, TreeNodeValue) & PointerMask);
7070
}
7171

7272
inline void
7373
setTreeNodeValue(Thread* t, object n, object value)
7474
{
75-
intptr_t red = cast<intptr_t>(n, TreeNodeValue) & (~PointerMask);
75+
alias_t red = cast<alias_t>(n, TreeNodeValue) & (~PointerMask);
7676

7777
set(t, n, TreeNodeValue, value);
7878

79-
cast<intptr_t>(n, TreeNodeValue) |= red;
79+
cast<alias_t>(n, TreeNodeValue) |= red;
8080
}
8181

8282
inline bool
8383
treeNodeRed(Thread*, object n)
8484
{
85-
return (cast<intptr_t>(n, TreeNodeValue) & (~PointerMask)) == 1;
85+
return (cast<alias_t>(n, TreeNodeValue) & (~PointerMask)) == 1;
8686
}
8787

8888
inline void
8989
setTreeNodeRed(Thread*, object n, bool red)
9090
{
9191
if (red) {
92-
cast<intptr_t>(n, TreeNodeValue) |= 1;
92+
cast<alias_t>(n, TreeNodeValue) |= 1;
9393
} else {
94-
cast<intptr_t>(n, TreeNodeValue) &= PointerMask;
94+
cast<alias_t>(n, TreeNodeValue) &= PointerMask;
9595
}
9696
}
9797

@@ -108,7 +108,7 @@ cloneTreeNode(Thread* t, object n)
108108

109109
object
110110
treeFind(Thread* t, object tree, intptr_t key, object sentinal,
111-
intptr_t (*compare)(Thread* t, intptr_t key, object b))
111+
intptr_t (*compare)(Thread* t, intptr_t key, object b))
112112
{
113113
object node = tree;
114114
while (node != sentinal) {
@@ -140,6 +140,7 @@ treeFind(Thread* t, TreeContext* c, object old, intptr_t key, object node,
140140
object new_ = newRoot;
141141
PROTECT(t, new_);
142142

143+
int count = 0;
143144
while (old != sentinal) {
144145
c->ancestors = path(c, new_, c->ancestors);
145146

@@ -162,6 +163,13 @@ treeFind(Thread* t, TreeContext* c, object old, intptr_t key, object node,
162163
c->ancestors = c->ancestors->next;
163164
return;
164165
}
166+
167+
if (++ count > 100) {
168+
// if we've gone this deep, we probably have an unbalanced tree,
169+
// which should only happen if there's a serious bug somewhere
170+
// in our insertion process
171+
abort(t);
172+
}
165173
}
166174

167175
setTreeNodeValue(t, new_, getTreeNodeValue(t, node));

0 commit comments

Comments
 (0)