blob: 2c58574175b1e02732f41c7bce263ffe7488bcf6 [file]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#pragma once
#pragma warning(push)
#pragma warning(disable:6200) // C6200: Index is out of valid index range, compiler complains here we use variable length array
namespace Js
{
template<class T, typename CountT = typename T::CounterFields>
struct CompactCounters
{
friend class FunctionBody;
struct Fields {
union {
uint8 u8Fields[static_cast<size_t>(CountT::Max)];
int8 i8Fields[static_cast<size_t>(CountT::Max)];
uint16 u16Fields[static_cast<size_t>(CountT::Max)];
int16 i16Fields[static_cast<size_t>(CountT::Max)];
uint32 u32Fields[static_cast<size_t>(CountT::Max)];
int32 i32Fields[static_cast<size_t>(CountT::Max)];
};
Fields() {}
};
uint8 GetFieldSize() const { return fieldSize; }
Fields* GetFields() const { return fields; }
private:
FieldWithBarrier(uint8) fieldSize;
#if DBG
mutable FieldWithBarrier(bool) isLockedDown;
#endif
typename FieldWithBarrier(Fields*) fields;
CompactCounters() { }
CompactCounters(T* host)
:fieldSize(0)
#if DBG
, isLockedDown(false)
#endif
{
AllocCounters<uint8>(host);
}
uint32 Get(CountT typeEnum) const
{
uint8 type = static_cast<uint8>(typeEnum);
uint8 localFieldSize = fieldSize;
uint32 value = 0;
if (localFieldSize == 1)
{
value = this->fields->u8Fields[type];
}
else if (localFieldSize == 2)
{
value = this->fields->u16Fields[type];
}
else if (localFieldSize == 4)
{
value = this->fields->u32Fields[type];
}
else
{
Assert(localFieldSize == 0 && this->fields == nullptr); // OOM when initial allocation failed
}
return value;
}
uint32 Set(CountT typeEnum, uint32 val, T* host)
{
uint8 type = static_cast<uint8>(typeEnum);
if (fieldSize == 1)
{
if (val <= UINT8_MAX)
{
return this->fields->u8Fields[type] = static_cast<uint8>(val);
}
else
{
(val <= UINT16_MAX) ? AllocCounters<uint16>(host) : AllocCounters<uint32>(host);
return host->counters.Set(typeEnum, val, host);
}
}
if (fieldSize == 2)
{
if (val <= UINT16_MAX)
{
return this->fields->u16Fields[type] = static_cast<uint16>(val);
}
else
{
AllocCounters<uint32>(host);
return host->counters.Set(typeEnum, val, host);
}
}
if (fieldSize == 4)
{
return this->fields->u32Fields[type] = val;
}
Assert(fieldSize == 0 && this->fields == nullptr && val == 0); // OOM when allocating the counters structure
return val;
}
uint32 Increase(CountT typeEnum, T* host)
{
uint8 type = static_cast<uint8>(typeEnum);
if (fieldSize == 1)
{
if (this->fields->u8Fields[type] < UINT8_MAX)
{
return this->fields->u8Fields[type]++;
}
else
{
AllocCounters<uint16>(host);
return host->counters.Increase(typeEnum, host);
}
}
if (fieldSize == 2)
{
if (this->fields->u16Fields[type] < UINT16_MAX)
{
return this->fields->u16Fields[type]++;
}
else
{
AllocCounters<uint32>(host);
return host->counters.Increase(typeEnum, host);
}
}
Assert(fieldSize == 4);
return this->fields->u32Fields[type]++;
}
template<typename FieldT>
void AllocCounters(T* host)
{
// only allow expanding in foreground thread. while function body cleanup
// we may set counters to 0 but that would not lead to expanding
Assert(ThreadContext::GetContextForCurrentThread());
Assert(host->GetRecycler() != nullptr);
const uint8 max = static_cast<uint8>(CountT::Max);
typedef CompactCounters<T, CountT> CounterT;
CounterT::Fields* fieldsArray = (CounterT::Fields*)RecyclerNewArrayLeafZ(host->GetRecycler(), FieldT, sizeof(FieldT)*max);
CounterT::Fields* oldFieldsArray = host->counters.fields;
uint8 i = 0;
if (this->fieldSize == 1)
{
if (sizeof(FieldT) == 2)
{
for (; i < max; i++)
{
fieldsArray->u16Fields[i] = oldFieldsArray->u8Fields[i];
}
}
else if (sizeof(FieldT) == 4)
{
for (; i < max; i++)
{
fieldsArray->u32Fields[i] = oldFieldsArray->u8Fields[i];
}
}
}
else if (this->fieldSize == 2)
{
for (; i < max; i++)
{
fieldsArray->u32Fields[i] = oldFieldsArray->u16Fields[i];
}
}
else
{
Assert(this->fieldSize==0);
}
if (this->fieldSize == 0)
{
this->fieldSize = sizeof(FieldT);
this->fields = fieldsArray;
}
else
{
AutoCriticalSection autoCS(host->GetScriptContext()->GetThreadContext()->GetFunctionBodyLock());
this->fieldSize = sizeof(FieldT);
this->fields = fieldsArray;
}
}
};
}
#pragma warning(pop)