blob: ca1562a30971de987b3af78ed5222b22e2d71b4d [file] [log] [blame]
/*
* Copyright (C) 2024 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include "LineColumn.h"
#include <wtf/IterationStatus.h>
#include <wtf/PrintStream.h>
#include <wtf/StdLibExtras.h>
#include <wtf/Vector.h>
namespace JSC {
// See comment at the top of ExpressionInfo.cpp on how ExpressionInfo works.
class ExpressionInfo {
WTF_MAKE_NONCOPYABLE(ExpressionInfo);
WTF_MAKE_FAST_ALLOCATED;
public:
enum class FieldID : uint8_t { InstPC, Divot, Start, End, Line, Column };
class Decoder;
using InstPC = unsigned; // aka bytecode PC.
static constexpr InstPC maxInstPC = std::numeric_limits<unsigned>::max();
struct Chapter {
InstPC startInstPC;
unsigned startEncodedInfoIndex;
};
struct Entry {
void reset()
{
instPC = 0;
lineColumn = { 0, 0 };
divot = 0;
startOffset = 0;
endOffset = 0;
}
InstPC instPC { 0 };
LineColumn lineColumn;
unsigned divot { 0 };
unsigned startOffset { 0 }; // This value is relative to divot.
unsigned endOffset { 0 }; // This value is relative to divot.
};
struct EncodedInfo {
bool isAbsInstPC() const;
bool isExtension() const;
unsigned value;
};
struct Diff;
class Encoder {
public:
void encode(InstPC, unsigned divot, unsigned startOffset, unsigned endOffset, LineColumn);
template<typename RemapFunc>
void remap(Vector<unsigned>&& adjustments, RemapFunc);
Entry entry() const { return m_entry; }
MallocPtr<ExpressionInfo> createExpressionInfo();
void dumpEncodedInfo() // For debugging use only.
{
ExpressionInfo::dumpEncodedInfo(m_expressionInfoEncodedInfo.begin(), m_expressionInfoEncodedInfo.end());
}
private:
struct Wide {
enum class SortOrder : uint8_t { Single, Duo, Multi };
unsigned value;
FieldID fieldID;
SortOrder order { SortOrder::Multi };
};
static EncodedInfo encodeAbsInstPC(InstPC absInstPC);
static EncodedInfo encodeExtension(unsigned offset);
static constexpr EncodedInfo encodeExtensionEnd();
static EncodedInfo encodeSingle(FieldID, unsigned);
static EncodedInfo encodeDuo(FieldID, unsigned value1, FieldID, unsigned value2);
static EncodedInfo encodeMultiHeader(unsigned numWides, Wide*);
static EncodedInfo encodeBasic(const Diff&);
void adjustInstPC(EncodedInfo*, unsigned instPCDelta);
template<unsigned bitCount> bool fits(Wide);
template<typename T, unsigned bitCount> bool fits(T);
Entry m_entry;
unsigned m_currentChapterStartIndex { 0 };
unsigned m_numberOfEncodedInfoExtensions { 0 };
Vector<ExpressionInfo::Chapter> m_expressionInfoChapters;
Vector<ExpressionInfo::EncodedInfo> m_expressionInfoEncodedInfo;
};
class Decoder {
public:
Decoder() = default;
Decoder(const ExpressionInfo&);
Decoder(Vector<ExpressionInfo::EncodedInfo>&);
IterationStatus decode(std::optional<InstPC> targetInstPC = std::nullopt);
void recacheInfo(Vector<ExpressionInfo::EncodedInfo>&);
EncodedInfo* currentInfo() const { return m_currentInfo; }
// This is meant to be used to jump to the start of a chapter, where the encoder
// always start over with no historical Entry values to compute diffs from.
// If you use this to point to any EncodedInfo other than the start of a chapter,
// then you're responsible for setting up the initial conditions of m_entry correctly
// apriori.
void setNextInfo(EncodedInfo* info) { m_nextInfo = info; }
Entry entry() const { return m_entry; }
void setEntry(Entry entry) { m_entry = entry; } // For debugging use only.
InstPC instPC() const { return m_entry.instPC; }
unsigned divot() const { return m_entry.divot; }
unsigned startOffset() const { return m_entry.startOffset; }
unsigned endOffset() const { return m_entry.endOffset; }
LineColumn lineColumn() const { return m_entry.lineColumn; }
private:
struct Wide {
FieldID fieldID;
unsigned value;
};
void appendWide(FieldID id, unsigned value)
{
m_wides[m_numWides++] = { id, value };
}
unsigned m_word { 0 };
Entry m_entry;
EncodedInfo* m_startInfo { nullptr };
EncodedInfo* m_endInfo { nullptr };
EncodedInfo* m_endExtensionInfo { nullptr };
EncodedInfo* m_currentInfo { nullptr };
EncodedInfo* m_nextInfo { nullptr };
bool m_hasDecodedFirstEntry { false };
unsigned m_numWides { 0 };
std::array<Wide, 6> m_wides;
};
~ExpressionInfo() = default;
LineColumn lineColumnForInstPC(InstPC);
Entry entryForInstPC(InstPC);
bool isEmpty() const { return !m_numberOfEncodedInfo; };
size_t byteSize() const;
template<unsigned bitCount>
static void print(PrintStream&, FieldID, unsigned value);
static void dumpEncodedInfo(EncodedInfo* start, EncodedInfo* end); // For debugging use only.
private:
ExpressionInfo(unsigned numberOfChapters, unsigned numberOfEncodedInfo, unsigned numberOfEncodedInfoExtensions);
ExpressionInfo(Vector<Chapter>&&, Vector<EncodedInfo>&&, unsigned numberOfEncodedInfoExtensions);
template<typename T, unsigned bitCount> static T cast(unsigned);
static bool isSpecial(unsigned);
static bool isWideOrSpecial(unsigned);
static size_t payloadSizeInBytes(size_t numChapters, size_t numberOfEncodedInfo, size_t numberOfEncodedInfoExtensions)
{
size_t size = numChapters * sizeof(Chapter) + (numberOfEncodedInfo + numberOfEncodedInfoExtensions) * sizeof(EncodedInfo);
return roundUpToMultipleOf<sizeof(unsigned)>(size);
}
static size_t totalSizeInBytes(size_t numChapters, size_t numberOfEncodedInfo, size_t numberOfEncodedInfoExtensions)
{
return sizeof(ExpressionInfo) + payloadSizeInBytes(numChapters, numberOfEncodedInfo, numberOfEncodedInfoExtensions);
}
EncodedInfo* findChapterEncodedInfoJustBelow(InstPC) const;
Chapter* chapters() const
{
return bitwise_cast<Chapter*>(this + 1);
}
EncodedInfo* encodedInfo() const
{
return bitwise_cast<EncodedInfo*>(&chapters()[m_numberOfChapters]);
}
EncodedInfo* endEncodedInfo() const
{
return &encodedInfo()[m_numberOfEncodedInfo];
}
EncodedInfo* endExtensionEncodedInfo() const
{
return &encodedInfo()[m_numberOfEncodedInfo + m_numberOfEncodedInfoExtensions];
}
size_t payloadSize() const
{
return payloadSizeInBytes(m_numberOfChapters, m_numberOfEncodedInfo, m_numberOfEncodedInfoExtensions) / sizeof(unsigned);
}
unsigned* payload() const
{
return bitwise_cast<unsigned*>(this + 1);
}
static MallocPtr<ExpressionInfo> createUninitialized(unsigned numberOfChapters, unsigned numberOfEncodedInfo, unsigned numberOfEncodedInfoExtensions);
static constexpr unsigned bitsPerWord = sizeof(unsigned) * CHAR_BIT;
// Number of bits of each field in Basic encoding.
static constexpr unsigned instPCBits = 5;
static constexpr unsigned divotBits = 7;
static constexpr unsigned startBits = 6;
static constexpr unsigned endBits = 6;
static constexpr unsigned lineBits = 3;
static constexpr unsigned columnBits = 5;
static_assert(instPCBits + divotBits + startBits + endBits + lineBits + columnBits == bitsPerWord);
// Bias values used for the signed diff values which make it easier to do range checks on these.
static constexpr unsigned divotBias = (1 << divotBits) / 2;
static constexpr unsigned lineBias = (1 << lineBits) / 2;
static constexpr unsigned columnBias = (1 << columnBits) / 2;
static constexpr unsigned instPCShift = bitsPerWord - instPCBits;
static constexpr unsigned divotShift = instPCShift - divotBits;
static constexpr unsigned startShift = divotShift - startBits;
static constexpr unsigned endShift = startShift - endBits;
static constexpr unsigned lineShift = endShift - lineBits;
static constexpr unsigned columnShift = lineShift - columnBits;
static constexpr unsigned specialHeader = (1 << instPCBits) - 1;
static constexpr unsigned wideHeader = specialHeader - 1;
static constexpr unsigned maxInstPCValue = wideHeader - 1;
static constexpr unsigned maxBiasedDivotValue = (1 << divotBits) - 1;
static constexpr unsigned maxStartValue = (1 << startBits) - 1;
static constexpr unsigned maxEndValue = (1 << endBits) - 1;
static constexpr unsigned maxBiasedLineValue = (1 << lineBits) - 1;
static constexpr unsigned sameAsDivotValue = (1 << columnBits) - 1;
static constexpr unsigned maxBiasedColumnValue = sameAsDivotValue - 1;
// Number of bits in Wide / Special encodings.
static constexpr unsigned specialValueBits = 26;
static constexpr unsigned singleValueBits = 23;
static constexpr unsigned duoValueBits = 10;
static constexpr unsigned fullValueBits = 32;
static constexpr unsigned multiSizeBits = 5;
static constexpr unsigned fieldIDBits = 3;
static constexpr unsigned maxSpecialValue = (1 << specialValueBits) - 1;
static constexpr unsigned maxSingleValue = (1 << singleValueBits) - 1;
static constexpr unsigned maxDuoValue = (1 << duoValueBits) - 1;
static constexpr unsigned invalidFieldID = (1 << fieldIDBits) - 1;
static constexpr unsigned multiSizeMask = (1 << multiSizeBits) - 1;
static constexpr unsigned fieldIDMask = (1 << fieldIDBits) - 1;
static constexpr unsigned headerShift = bitsPerWord - instPCBits;
static constexpr unsigned multiBitShift = headerShift - 1;
static_assert(headerShift == 27);
static_assert(multiBitShift == 26);
static constexpr unsigned specialValueShift = multiBitShift - specialValueBits;
static_assert(!specialValueShift);
static constexpr unsigned firstFieldIDShift = multiBitShift - fieldIDBits;
static constexpr unsigned singleValueShift = firstFieldIDShift - singleValueBits;
static_assert(!singleValueShift);
static constexpr unsigned duoFirstValueShift = firstFieldIDShift - duoValueBits;
static constexpr unsigned duoSecondFieldIDShift = duoFirstValueShift - fieldIDBits;
static constexpr unsigned duoSecondValueShift = duoSecondFieldIDShift - duoValueBits;
static_assert(!duoSecondValueShift);
static constexpr unsigned multiSizeShift = firstFieldIDShift - multiSizeBits;
static constexpr unsigned multiFirstFieldShift = multiSizeShift - fieldIDBits;
static constexpr unsigned numberOfWordsBetweenChapters = 10000;
using LineColumnMap = HashMap<InstPC, LineColumn, WTF::IntHash<InstPC>, WTF::UnsignedWithZeroKeyHashTraits<InstPC>>;
mutable LineColumnMap m_cachedLineColumns;
unsigned m_numberOfChapters;
unsigned m_numberOfEncodedInfo;
unsigned m_numberOfEncodedInfoExtensions;
// Followed by the following which are allocated but are dynamically sized.
// Chapter chapters[numberOfChapters];
// EncodedInfo encodedInfo[numberOfEncodedInfo + numberOfEncodedInfoExtensions];
friend class CachedExpressionInfo;
};
static_assert(roundUpToMultipleOf<sizeof(unsigned)>(sizeof(ExpressionInfo)) == sizeof(ExpressionInfo), "CachedExpressionInfo relies on this invariant");
} // namespace JSC
namespace WTF {
JS_EXPORT_PRIVATE void printInternal(PrintStream&, JSC::ExpressionInfo::FieldID);
} // namespace WTF