Skip to content

Commit 1237c8d

Browse files
author
Jianchun Xu
committed
swb: annotate BaseDictionary (partial)
Expand `Pointer/PointerNoBarrier/WriteBarrierPtr/NoWriteBarrierPtr` to template on `Allocator`. By default they use Recycler and behave the same as before. When used with Arena or other custom allocators, `Pointer/WriteBarrierPtr` will not trigger write barrier (and also allow `operator&` for minimum impact on existing code). Added `WriteBarrier` functions to trigger or ignore write barrier appropriately. Added `SetArray` function to assign array data to a field. When used with Recycler and the element type needs write barrier, it triggers write barrier on the array content. This change does not handle individual `Item` write barrier yet.
1 parent bc973a5 commit 1237c8d

File tree

9 files changed

+180
-45
lines changed

9 files changed

+180
-45
lines changed

lib/Common/DataStructures/BaseDictionary.h

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,9 @@ namespace JsUtil
9090
friend class Js::RemoteDictionary<BaseDictionary>;
9191
template <typename ValueOrKey> struct ComparerType { typedef Comparer<ValueOrKey> Type; }; // Used by diagnostics to access Comparer type
9292

93-
int* buckets;
94-
EntryType* entries;
95-
AllocatorType* alloc;
93+
Pointer(int, TAllocator) buckets;
94+
Pointer(EntryType, TAllocator) entries;
95+
PointerNoBarrier(AllocatorType) alloc;
9696
int size;
9797
uint bucketCount;
9898
int count;
@@ -860,9 +860,14 @@ namespace JsUtil
860860
int initSize = max(capacity, 4);
861861
uint initBucketCount = SizePolicy::GetBucketSize(initSize);
862862
AssertMsg(initBucketCount > 0, "Size returned by policy should be greater than 0");
863-
Allocate(&buckets, &entries, initBucketCount, initSize);
864863

865-
// Allocation can throw - assign the size only after allocation has succeeded.
864+
int* newBuckets = nullptr;
865+
EntryType* newEntries = nullptr;
866+
Allocate(&newBuckets, &newEntries, initBucketCount, initSize);
867+
868+
// Allocation can throw - assign only after allocation has succeeded.
869+
SetArray<TAllocator>(this->buckets, newBuckets, initBucketCount);
870+
SetArray<TAllocator, EntryType, ValueType>(this->entries, newEntries, initSize);
866871
this->bucketCount = initBucketCount;
867872
this->size = initSize;
868873
Assert(this->freeCount == 0);
@@ -1037,10 +1042,10 @@ namespace JsUtil
10371042
if (stats)
10381043
stats->Resize(newSize, /*emptyBuckets=*/ newSize - size);
10391044
#endif
1040-
buckets = newBuckets;
1045+
SetArray<TAllocator>(this->buckets, newBuckets, newBucketCount);
1046+
SetArray<TAllocator, EntryType, ValueType>(this->entries, newEntries, newSize);
10411047
bucketCount = newBucketCount;
10421048
size = newSize;
1043-
entries = newEntries;
10441049
}
10451050

10461051
__ecount(bucketCount) int *AllocateBuckets(DECLSPEC_GUARD_OVERFLOW const uint bucketCount)

lib/Common/Exceptions/Throw.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ namespace Memory {
3737
using namespace Memory;
3838
#include "Memory/Allocator.h"
3939
#include "Memory/HeapAllocator.h"
40+
#include "Memory/RecyclerPointers.h"
4041

4142
// Data structure
4243
#include "DataStructures/Comparer.h"

lib/Common/Memory/Allocator.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@ enum PageHeapMode
2727

2828
namespace Memory
2929
{
30-
31-
template<typename> class WriteBarrierPtr;
32-
template<typename> class NoWriteBarrierPtr;
30+
class Recycler;
31+
class RecyclerNonLeafAllocator;
32+
template<typename, class Allocator=Recycler> class WriteBarrierPtr;
33+
template<typename, class Allocator=Recycler> class NoWriteBarrierPtr;
3334

3435
#ifdef TRACK_ALLOC
3536
struct TrackAllocData

lib/Common/Memory/RecyclerPointers.h

Lines changed: 145 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,142 @@
66

77
namespace Memory
88
{
9+
// Dummy tag classes to mark yes/no write barrier policy
10+
struct _write_barrier_policy {};
11+
struct _no_write_barrier_policy {};
12+
13+
// Type write barrier policy
14+
//
15+
// By default following potentially contains GC pointers and use write barrier policy:
16+
// pointer, WriteBarrierPtr, _write_barrier_policy
17+
//
18+
template <class T>
19+
struct TypeWriteBarrierPolicy { typedef _no_write_barrier_policy Policy; };
20+
template <class T>
21+
struct TypeWriteBarrierPolicy<T*> { typedef _write_barrier_policy Policy; };
22+
template <class T>
23+
struct TypeWriteBarrierPolicy<WriteBarrierPtr<T>> { typedef _write_barrier_policy Policy; };
24+
template <>
25+
struct TypeWriteBarrierPolicy<_write_barrier_policy> { typedef _write_barrier_policy Policy; };
26+
27+
// Combine AllocatorType and data type write barrier policy
28+
// Recycler allocator type + _write_barrier_policy data type => _write_barrier_policy
29+
//
30+
template <class AllocatorType, class TPolicy>
31+
struct AllocatorTypeWriteBarrierPolicy { typedef _no_write_barrier_policy Policy; };
32+
template <>
33+
struct AllocatorTypeWriteBarrierPolicy<Recycler, _write_barrier_policy>
34+
{
35+
typedef _write_barrier_policy Policy;
36+
};
37+
38+
// Combine Allocator + Data write barrier policy
39+
// RecyclerNonLeafAllocator, * => _write_barrier_policy
40+
//
41+
template <class Allocator, class T>
42+
struct AllocatorWriteBarrierPolicy
43+
{
44+
typedef typename AllocatorInfo<Allocator, void>::AllocatorType AllocatorType;
45+
typedef typename TypeWriteBarrierPolicy<T>::Policy TPolicy;
46+
typedef typename AllocatorTypeWriteBarrierPolicy<AllocatorType, TPolicy>::Policy Policy;
47+
};
48+
template <class T>
49+
struct AllocatorWriteBarrierPolicy<RecyclerNonLeafAllocator, T>
50+
{
51+
typedef _write_barrier_policy Policy;
52+
};
53+
54+
// Actual WriteBarrierPolicy behavior
55+
//
56+
template <class Policy>
57+
struct WriteBarrierPolicy
58+
{
59+
template <class Allocator, class T>
60+
static void WriteBarrier(WriteBarrierPtr<T, Allocator> * address) {}
61+
62+
template <class T>
63+
static void WriteBarrier(T * address, size_t count) {}
64+
};
65+
66+
#ifdef RECYCLER_WRITE_BARRIER
67+
template <>
68+
struct WriteBarrierPolicy<_write_barrier_policy>
69+
{
70+
template <class Allocator, class T>
71+
static void WriteBarrier(WriteBarrierPtr<T, Allocator> * address)
72+
{
73+
RecyclerWriteBarrierManager::WriteBarrier(address);
74+
}
75+
76+
template <class T>
77+
static void WriteBarrier(T * address, size_t count)
78+
{
79+
RecyclerWriteBarrierManager::WriteBarrier(address, sizeof(T) * count);
80+
}
81+
};
82+
#endif
83+
84+
// Check if "operator&" is allowed on given WriteBarrierPtr type at compile time.
85+
// Not allowed on WriteBarrierPtr<T, RecyclerFamilyAllocator>.
86+
//
87+
template <class Policy>
88+
void CheckAddressOperator()
89+
{
90+
static_assert(false, "Might need to set barrier for this operation, and use AddressOf instead.");
91+
}
92+
template <>
93+
inline void CheckAddressOperator<_no_write_barrier_policy>()
94+
{
95+
}
96+
template <class Allocator, class T, class PolicyType = WriteBarrierPtr<T, Allocator>>
97+
void CheckAddressOperator(WriteBarrierPtr<T, Allocator> * p)
98+
{
99+
typedef typename AllocatorWriteBarrierPolicy<Allocator, PolicyType>::Policy Policy;
100+
CheckAddressOperator<Policy>();
101+
}
102+
103+
// Trigger write barrier on changing a WriteBarrierPtr if Allocator type is one of
104+
// recycler allocators. Ignore otherwise.
105+
//
106+
template <class Allocator, class T, class PolicyType = WriteBarrierPtr<T, Allocator>>
107+
void WriteBarrier(WriteBarrierPtr<T, Allocator> * address)
108+
{
109+
typedef typename AllocatorWriteBarrierPolicy<Allocator, PolicyType>::Policy Policy;
110+
return WriteBarrierPolicy<Policy>::WriteBarrier(address);
111+
}
112+
113+
// Trigger write barrier on changing array content if Allocator and element type
114+
// determines write barrier is needed. Ignore otherwise.
115+
//
116+
template <class Allocator, class T, class PolicyType = T>
117+
void WriteBarrier(T * address, size_t count)
118+
{
119+
typedef typename AllocatorWriteBarrierPolicy<Allocator, PolicyType>::Policy Policy;
120+
return WriteBarrierPolicy<Policy>::WriteBarrier(address, count);
121+
}
122+
123+
// Set an array field (naked pointer or NoWriteBarrierPtr) to an allocated array.
124+
// Used when the field does not use write barrier. Will not trigger write barrier
125+
// on the array content.
126+
//
127+
template <class Allocator, class T, class PolicyType = T>
128+
void SetArray(T*& dst, T* src, size_t srcCount)
129+
{
130+
dst = src;
131+
}
132+
133+
// Set an array field (WriteBarrierPtr) to an allocated array.
134+
// Used when the field uses write barrier. Will trigger write barrier on the array
135+
// content if Allocator and element type determines write barrier is needed.
136+
//
137+
template <class Allocator, class T, class PolicyType = T>
138+
void SetArray(WriteBarrierPtr<T, Allocator>& dst, T* src, size_t srcCount)
139+
{
140+
dst = src;
141+
WriteBarrier<Allocator, T, PolicyType>(dst, srcCount);
142+
}
143+
144+
9145
template <typename T>
10146
class NoWriteBarrierField
11147
{
@@ -31,7 +167,7 @@ class NoWriteBarrierField
31167
T value;
32168
};
33169

34-
template <typename T>
170+
template <typename T, class Allocator>
35171
class NoWriteBarrierPtr
36172
{
37173
public:
@@ -80,7 +216,7 @@ class WriteBarrierObjectConstructorTrigger
80216
Recycler* recycler;
81217
};
82218

83-
template <typename T>
219+
template <typename T, class Allocator>
84220
class WriteBarrierPtr
85221
{
86222
public:
@@ -100,12 +236,12 @@ class WriteBarrierPtr
100236

101237
T const** operator&() const
102238
{
103-
static_assert(false, "Might need to set barrier for this operation, and use AddressOf instead.");
239+
CheckAddressOperator(this);
104240
return &ptr;
105241
}
106242
T** operator&()
107243
{
108-
static_assert(false, "Might need to set barrier for this operation, and use AddressOf instead.");
244+
CheckAddressOperator(this);
109245
return &ptr;
110246
}
111247

@@ -122,9 +258,7 @@ class WriteBarrierPtr
122258
void WriteBarrierSet(T * ptr)
123259
{
124260
NoWriteBarrierSet(ptr);
125-
#ifdef RECYCLER_WRITE_BARRIER
126-
RecyclerWriteBarrierManager::WriteBarrier(this);
127-
#endif
261+
WriteBarrier(this);
128262
}
129263

130264
WriteBarrierPtr& operator=(WriteBarrierPtr const& other)
@@ -136,23 +270,17 @@ class WriteBarrierPtr
136270
static void MoveArray(WriteBarrierPtr * dst, WriteBarrierPtr * src, size_t count)
137271
{
138272
memmove((void *)dst, src, sizeof(WriteBarrierPtr) * count);
139-
#ifdef RECYCLER_WRITE_BARRIER
140-
RecyclerWriteBarrierManager::WriteBarrier(dst, count);
141-
#endif
273+
WriteBarrier<Allocator>(dst, count);
142274
}
143275
static void CopyArray(WriteBarrierPtr * dst, size_t dstCount, T const* src, size_t srcCount)
144276
{
145277
js_memcpy_s((void *)dst, sizeof(WriteBarrierPtr) * dstCount, src, sizeof(T *) * srcCount);
146-
#ifdef RECYCLER_WRITE_BARRIER
147-
RecyclerWriteBarrierManager::WriteBarrier(dst, dstCount);
148-
#endif
278+
WriteBarrier<Allocator>(dst, dstCount);
149279
}
150280
static void CopyArray(WriteBarrierPtr * dst, size_t dstCount, WriteBarrierPtr const* src, size_t srcCount)
151281
{
152282
js_memcpy_s((void *)dst, sizeof(WriteBarrierPtr) * dstCount, src, sizeof(WriteBarrierPtr) * srcCount);
153-
#ifdef RECYCLER_WRITE_BARRIER
154-
RecyclerWriteBarrierManager::WriteBarrier(dst, dstCount);
155-
#endif
283+
WriteBarrier<Allocator>(dst, dstCount);
156284
}
157285
static void ClearArray(WriteBarrierPtr * dst, size_t count)
158286
{
@@ -162,7 +290,7 @@ class WriteBarrierPtr
162290
private:
163291
T * ptr;
164292
};
165-
}
293+
} // namespace Memory
166294

167295

168296
template<class T> inline

lib/Common/Memory/RecyclerWriteBarrierManager.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ X64WriteBarrierCardTableManager::OnThreadInit()
5757
ULONG_PTR stackEnd = 0;
5858
::GetCurrentThreadStackLimits(&stackEnd, &stackBase);
5959
#endif
60-
60+
6161
size_t numPages = (stackBase - stackEnd) / AutoSystemInfo::PageSize;
6262
// stackEnd is the lower boundary
6363
return OnSegmentAlloc((char*) stackEnd, numPages);
@@ -283,11 +283,11 @@ RecyclerWriteBarrierManager::WriteBarrier(void * address)
283283

284284

285285
void
286-
RecyclerWriteBarrierManager::WriteBarrier(void * address, size_t ptrCount)
286+
RecyclerWriteBarrierManager::WriteBarrier(void * address, size_t bytes)
287287
{
288288
#ifdef RECYCLER_WRITE_BARRIER_BYTE
289289
uintptr_t startIndex = GetCardTableIndex(address);
290-
char * endAddress = (char *)Math::Align<INT_PTR>((INT_PTR)((char *)address + sizeof(void *) * ptrCount), s_WriteBarrierPageSize);
290+
char * endAddress = (char *)Math::Align<INT_PTR>((INT_PTR)((char *)address + bytes), s_WriteBarrierPageSize);
291291
uintptr_t endIndex = GetCardTableIndex(endAddress);
292292
Assert(startIndex <= endIndex);
293293
memset(cardTable + startIndex, 1, endIndex - startIndex);
@@ -297,7 +297,7 @@ RecyclerWriteBarrierManager::WriteBarrier(void * address, size_t ptrCount)
297297
uint bitMask = 0xFFFFFFFF << bitShift;
298298
uint cardIndex = ((uint)address) / s_BytesPerCard);
299299

300-
char * endAddress = (char *)Math::Align((INT_PTR)((char *)address + sizeof(void *) * ptrCount), s_BytesPerCardBit);
300+
char * endAddress = (char *)Math::Align((INT_PTR)((char *)address + bytes), s_BytesPerCardBit);
301301
char * alignedAddress = (char *)Math::Align((INT_PTR)address, s_WriteBarrierPageSize);
302302
if (alignedAddress > endAddress)
303303
{

lib/Common/Memory/RecyclerWriteBarrierManager.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ class RecyclerWriteBarrierManager
123123
{
124124
public:
125125
static void WriteBarrier(void * address);
126-
static void WriteBarrier(void * address, size_t ptrCount);
126+
static void WriteBarrier(void * address, size_t bytes);
127127

128128
// For JIT
129129
static uintptr_t GetCardTableIndex(void * address);

lib/Common/Memory/WriteBarrierMacros.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
// Usage:
1313
// In general, don't need to use anything, just use the macros to annotate your fields
1414
//
15-
// If you want to force your fields to always generate the right write-barrier types,
15+
// If you want to force your fields to always generate the right write-barrier types,
1616
// at the start of the file, add the following:
1717
// #define FORCE_USE_WRITE_BARRIER
1818
// #include <Memory/WriteBarrierMacros.h>
@@ -27,34 +27,34 @@
2727
#define PushMacro(x) __pragma(push_macro( #x ))
2828

2929
#define SAVE_WRITE_BARRIER_MACROS() \
30-
PushMacro("PointerNoBarrier") \
3130
PushMacro("Field") \
31+
PushMacro("PointerNoBarrier") \
3232
PushMacro("Pointer")
3333

3434
#define RESTORE_WRITE_BARRIER_MACROS() \
35-
PopMacro("PointerNoBarrier") \
3635
PopMacro("Field") \
36+
PopMacro("PointerNoBarrier") \
3737
PopMacro("Pointer")
3838

3939
#endif
4040

4141
#ifdef FORCE_USE_WRITE_BARRIER
4242
SAVE_WRITE_BARRIER_MACROS()
43-
#undef PointerNoBarrier
4443
#undef Field
44+
#undef PointerNoBarrier
4545
#undef Pointer
4646
#endif
4747

4848
// TODO: Turn off these annotations on Win32
4949
#if defined(__clang__) || defined(FORCE_USE_WRITE_BARRIER)
5050
// Various macros for defining field attributes
51-
#define PointerNoBarrier(type) NoWriteBarrierPtr<type>
5251
#define Field(type) NoWriteBarrierField<type>
53-
#define Pointer(type) WriteBarrierPtr<type>
52+
#define PointerNoBarrier(type, ...) NoWriteBarrierPtr<type, ##__VA_ARGS__>
53+
#define Pointer(type, ...) WriteBarrierPtr<type, ##__VA_ARGS__>
5454
#else
55-
#define PointerNoBarrier(type) type*
5655
#define Field(type) type
57-
#define Pointer(type) type*
56+
#define PointerNoBarrier(type) type*
57+
#define Pointer(type, ...) type*
5858
#endif
5959

6060
#undef FORCE_USE_WRITE_BARRIER

lib/Runtime/Base/ScriptContext.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4721,8 +4721,9 @@ void ScriptContext::RegisterPrototypeChainEnsuredToHaveOnlyWritableDataPropertie
47214721
}
47224722

47234723
#if ENABLE_PROFILE_INFO
4724-
template<template<typename> class BarrierT>
4725-
void ScriptContext::AddDynamicProfileInfo(FunctionBody * functionBody, BarrierT<DynamicProfileInfo>& dynamicProfileInfo)
4724+
template<template<typename, class> class BarrierT>
4725+
void ScriptContext::AddDynamicProfileInfo(
4726+
FunctionBody * functionBody, BarrierT<DynamicProfileInfo, Recycler>& dynamicProfileInfo)
47264727
{
47274728
Assert(functionBody->GetScriptContext() == this);
47284729
Assert(functionBody->HasValidSourceInfo());
@@ -4777,7 +4778,6 @@ void ScriptContext::RegisterPrototypeChainEnsuredToHaveOnlyWritableDataPropertie
47774778
Assert(dynamicProfileInfo != nullptr);
47784779
}
47794780
template void ScriptContext::AddDynamicProfileInfo<WriteBarrierPtr>(FunctionBody *, WriteBarrierPtr<DynamicProfileInfo>&);
4780-
template void ScriptContext::AddDynamicProfileInfo<NoWriteBarrierPtr>(FunctionBody *, NoWriteBarrierPtr<DynamicProfileInfo>&);
47814781
#endif
47824782

47834783
CharClassifier const * ScriptContext::GetCharClassifier(void) const

0 commit comments

Comments
 (0)