Files
renderdoc/renderdoc/driver/shaders/dxil/llvm_encoder.cpp
T
2021-09-21 18:04:56 +01:00

972 lines
26 KiB
C++

/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2021 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "llvm_encoder.h"
#include "os/os_specific.h"
namespace LLVMBC
{
static bool isChar6(char c)
{
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= '|') || (c >= '0' && c <= '9') || c == '_' ||
c == '.';
}
static uint32_t GetBlockAbbrevSize(KnownBlock block)
{
uint32_t ret = 0;
// the abbrev sizes seem to be hardcoded in llvm? At least this matches dxc's llvm
switch(block)
{
case KnownBlock::BLOCKINFO: ret = 2; break;
case KnownBlock::MODULE_BLOCK: ret = 3; break;
case KnownBlock::PARAMATTR_BLOCK: ret = 3; break;
case KnownBlock::PARAMATTR_GROUP_BLOCK: ret = 3; break;
case KnownBlock::CONSTANTS_BLOCK: ret = 4; break;
case KnownBlock::FUNCTION_BLOCK: ret = 4; break;
case KnownBlock::VALUE_SYMTAB_BLOCK: ret = 4; break;
case KnownBlock::METADATA_BLOCK: ret = 3; break;
case KnownBlock::METADATA_ATTACHMENT: ret = 3; break;
case KnownBlock::TYPE_BLOCK: ret = 4; break;
case KnownBlock::USELIST_BLOCK: ret = 3; break;
case KnownBlock::Count: break;
}
return ret;
}
#define AbbFixed(n) \
{ \
AbbrevEncoding::Fixed, n \
}
#define AbbVBR(n) \
{ \
AbbrevEncoding::VBR, n \
}
#define AbbArray() \
{ \
AbbrevEncoding::Array, 0 \
}
#define AbbLiteral(lit) \
{ \
AbbrevEncoding::Literal, uint64_t(lit) \
}
#define AbbChar6() \
{ \
AbbrevEncoding::Char6, 0 \
}
#define MagicFixedSizeNumTypes 99
#define MagicFixedSizeNumConstants 999
#define AbbFixedTypes() AbbFixed(MagicFixedSizeNumTypes)
#define AbbFixedConstants() AbbFixed(MagicFixedSizeNumConstants)
using AbbrevDefinition = AbbrevParam[8];
// known abbreviations. Encoded as an array of abbrevs, with each one being an array of params (the
// last param will have AbbrevEncoding::Unknown == 0)
enum class ValueSymtabAbbrev
{
Entry8,
Entry7,
Entry6,
BBEntry6,
};
AbbrevDefinition ValueSymtabAbbrevDefs[] = {
// Entry8
{
AbbFixed(3), AbbVBR(8), AbbArray(), AbbFixed(8),
},
// Entry7
{
AbbLiteral(ValueSymtabRecord::ENTRY), AbbVBR(8), AbbArray(), AbbFixed(7),
},
// Entry6
{
AbbLiteral(ValueSymtabRecord::ENTRY), AbbVBR(8), AbbArray(), AbbChar6(),
},
// BBEntry6
{
AbbLiteral(ValueSymtabRecord::BBENTRY), AbbVBR(8), AbbArray(), AbbChar6(),
},
};
enum class ConstantsAbbrev
{
SetType,
Integer,
EvalCast,
Null,
// the ones below are only used in the global constants block
Aggregate,
String,
CString7,
CString6,
};
AbbrevDefinition ConstantsAbbrevDefs[] = {
// SetType
{
AbbLiteral(ConstantsRecord::SETTYPE), AbbFixedTypes(),
},
// Integer
{
AbbLiteral(ConstantsRecord::INTEGER), AbbVBR(8),
},
// EvalCast
{
AbbLiteral(ConstantsRecord::EVAL_CAST), AbbFixed(4), AbbFixedTypes(), AbbVBR(8),
},
// Null
{
AbbLiteral(ConstantsRecord::CONST_NULL),
},
};
AbbrevDefinition ConstantsGlobalAbbrevDefs[] = {
// Aggregate
{
AbbLiteral(ConstantsRecord::AGGREGATE), AbbArray(), AbbFixedConstants(),
},
// String
{
AbbLiteral(ConstantsRecord::STRING), AbbArray(), AbbFixed(8),
},
// CString7
{
AbbLiteral(ConstantsRecord::CSTRING), AbbArray(), AbbFixed(7),
},
// CString6
{
AbbLiteral(ConstantsRecord::CSTRING), AbbArray(), AbbChar6(),
},
};
enum class FunctionAbbrev
{
Load,
BinOp,
BinOpFlags,
Cast,
RetVoid,
RetValue,
Unreachable,
GEP,
};
AbbrevDefinition FunctionAbbrevDefs[] = {
// Load
{
AbbLiteral(FunctionRecord::INST_LOAD), AbbVBR(6), AbbFixedTypes(), AbbVBR(4), AbbFixed(1),
},
// BinOp
{
AbbLiteral(FunctionRecord::INST_BINOP), AbbVBR(6), AbbVBR(6), AbbFixed(4),
},
// BinOpFlags
{
AbbLiteral(FunctionRecord::INST_BINOP), AbbVBR(6), AbbVBR(6), AbbFixed(4), AbbFixed(7),
},
// Cast
{
AbbLiteral(FunctionRecord::INST_CAST), AbbVBR(6), AbbFixedTypes(), AbbFixed(4),
},
// RetVoid
{
AbbLiteral(FunctionRecord::INST_RET),
},
// RetValue
{
AbbLiteral(FunctionRecord::INST_RET), AbbVBR(6),
},
// Unreachable
{
AbbLiteral(FunctionRecord::INST_UNREACHABLE),
},
// GEP
{
AbbLiteral(FunctionRecord::INST_GEP), AbbFixed(1), AbbFixedTypes(), AbbArray(), AbbVBR(6),
},
};
enum class TypeAbbrev
{
Pointer,
Function,
AnonStruct,
StructName,
NamedStruct,
Array,
};
AbbrevDefinition TypeAbbrevDefs[] = {
// Pointer
{
AbbLiteral(TypeRecord::POINTER), AbbFixedTypes(), AbbLiteral(0),
},
// Function
{
AbbLiteral(TypeRecord::FUNCTION), AbbFixed(1), AbbArray(), AbbFixedTypes(),
},
// AnonStruct
{
AbbLiteral(TypeRecord::STRUCT_ANON), AbbFixed(1), AbbArray(), AbbFixedTypes(),
},
// StructName
{
AbbLiteral(TypeRecord::STRUCT_NAME), AbbArray(), AbbChar6(),
},
// NamedStruct
{
AbbLiteral(TypeRecord::STRUCT_NAMED), AbbFixed(1), AbbArray(), AbbFixedTypes(),
},
// Array
{
AbbLiteral(TypeRecord::ARRAY), AbbVBR(8), AbbFixedTypes(),
},
};
static AbbrevDefinition *GetAbbrevDefs(KnownBlock block)
{
AbbrevDefinition *ret = NULL;
switch(block)
{
case KnownBlock::VALUE_SYMTAB_BLOCK: ret = ValueSymtabAbbrevDefs; break;
case KnownBlock::CONSTANTS_BLOCK: ret = ConstantsAbbrevDefs; break;
case KnownBlock::FUNCTION_BLOCK: ret = FunctionAbbrevDefs; break;
case KnownBlock::TYPE_BLOCK: ret = TypeAbbrevDefs; break;
default: break;
}
return ret;
}
static uint32_t GetNumAbbrevDefs(KnownBlock block)
{
uint32_t ret = 0;
switch(block)
{
case KnownBlock::VALUE_SYMTAB_BLOCK: ret = ARRAY_COUNT(ValueSymtabAbbrevDefs); break;
case KnownBlock::CONSTANTS_BLOCK: ret = ARRAY_COUNT(ConstantsAbbrevDefs); break;
case KnownBlock::FUNCTION_BLOCK: ret = ARRAY_COUNT(FunctionAbbrevDefs); break;
case KnownBlock::TYPE_BLOCK: ret = ARRAY_COUNT(TypeAbbrevDefs); break;
default: break;
}
return ret;
}
BitcodeWriter::BitcodeWriter(bytebuf &buf) : b(buf)
{
b.Write(LLVMBC::BitcodeMagic);
curBlock = KnownBlock::Count;
abbrevSize = 2;
m_GlobalVarAbbrev = ~0U;
}
BitcodeWriter::~BitcodeWriter()
{
}
void BitcodeWriter::BeginBlock(KnownBlock block)
{
uint32_t newAbbrevSize = GetBlockAbbrevSize(block);
if(newAbbrevSize == 0)
{
RDCERR("Encoding error: unrecognised block %u", block);
return;
}
b.fixed(abbrevSize, ENTER_SUBBLOCK);
b.vbr(8, block);
b.vbr(4, newAbbrevSize);
b.align32bits();
size_t offs = b.GetByteOffset();
// write a placeholder length
b.Write<uint32_t>(0U);
curBlock = block;
abbrevSize = newAbbrevSize;
blockStack.push_back({block, offs});
curAbbrevs.swap(blockStack.back().abbrevs);
// emit known abbrevs here that aren't in blockinfo
switch(block)
{
case KnownBlock::CONSTANTS_BLOCK:
case KnownBlock::VALUE_SYMTAB_BLOCK:
case KnownBlock::FUNCTION_BLOCK:
{
// these blocks have abbrevs from the blockinfo. Don't write them, but add them to our abbrev
// dictionary
uint32_t numAbbrevDefs = GetNumAbbrevDefs(block);
AbbrevDefinition *abbrevs = GetAbbrevDefs(block);
for(uint32_t i = 0; i < numAbbrevDefs; i++)
curAbbrevs.push_back(abbrevs[i]);
// the global constants block has some extra abbrevs
// blockStack[0] is always the module block
if(block == KnownBlock::CONSTANTS_BLOCK && blockStack.size() == 2)
{
for(size_t i = 0; i < ARRAY_COUNT(ConstantsGlobalAbbrevDefs); i++)
WriteAbbrevDefinition(ConstantsGlobalAbbrevDefs[i]);
}
break;
}
default:
{
uint32_t numAbbrevDefs = GetNumAbbrevDefs(block);
AbbrevDefinition *abbrevs = GetAbbrevDefs(block);
for(uint32_t i = 0; i < numAbbrevDefs; i++)
WriteAbbrevDefinition(abbrevs[i]);
break;
}
}
}
void BitcodeWriter::EndBlock()
{
b.vbr(abbrevSize, END_BLOCK);
b.align32bits();
size_t offs = blockStack.back().offset;
// -4 because we don't include the word with the length itself
size_t lengthInBytes = b.GetByteOffset() - offs - 4;
b.PatchLengthWord(offs, uint32_t(lengthInBytes / 4));
blockStack.pop_back();
if(blockStack.empty())
{
curBlock = KnownBlock::Count;
abbrevSize = 2;
}
else
{
curBlock = blockStack.back().block;
curAbbrevs = blockStack.back().abbrevs;
abbrevSize = GetBlockAbbrevSize(curBlock);
}
}
void BitcodeWriter::WriteAbbrevDefinition(AbbrevParam *abbrev)
{
curAbbrevs.push_back(abbrev);
b.fixed(abbrevSize, DEFINE_ABBREV);
uint32_t numParams = 0;
while(abbrev[numParams].encoding != AbbrevEncoding::Unknown)
numParams++;
b.vbr(5, numParams);
for(uint32_t p = 0; p < numParams; p++)
{
AbbrevParam param = abbrev[p];
if(param.value == MagicFixedSizeNumTypes)
param.value = m_NumTypeBits;
if(param.value == MagicFixedSizeNumConstants)
param.value = m_NumConstantBits;
const bool lit = param.encoding == AbbrevEncoding::Literal;
b.fixed<bool>(1, lit);
if(lit)
{
b.vbr(8, param.value);
}
else
{
b.fixed(3, param.encoding);
if(param.encoding == AbbrevEncoding::VBR || param.encoding == AbbrevEncoding::Fixed)
b.vbr(5, param.value);
}
}
}
void BitcodeWriter::ConfigureSizes(size_t numTypes, size_t numGlobalConsts, size_t numSections,
uint64_t maxAlign, uint32_t maxGlobalType)
{
m_NumTypeBits = 32 - Bits::CountLeadingZeroes((uint32_t)numTypes);
m_NumConstantBits = 32 - Bits::CountLeadingZeroes((uint32_t)numGlobalConsts);
m_GlobalTypeBits = 32 - Bits::CountLeadingZeroes(maxGlobalType);
if(numSections == 0)
m_NumSectionBits = 0;
else
m_NumSectionBits = 32 - Bits::CountLeadingZeroes((uint32_t)numSections);
if(maxAlign == 0)
{
m_AlignBits = 0;
}
else
{
uint32_t encodedAlign = 32 - Bits::CountLeadingZeroes((uint32_t)maxAlign);
m_AlignBits = 32 - Bits::CountLeadingZeroes(encodedAlign);
}
}
void BitcodeWriter::ModuleBlockInfo()
{
// these abbrevs are hardcoded in llvm, at least at dxc's version
BeginBlock(KnownBlock::BLOCKINFO);
// the module-level blockinfo contains abbrevs for these block types that can be repeated
// subblocks
for(KnownBlock block :
{KnownBlock::VALUE_SYMTAB_BLOCK, KnownBlock::CONSTANTS_BLOCK, KnownBlock::FUNCTION_BLOCK})
{
Unabbrev((uint32_t)BlockInfoRecord::SETBID, true, (uint32_t)block);
AbbrevDefinition *abbrevs = GetAbbrevDefs(block);
uint32_t numAbbrevDefs = GetNumAbbrevDefs(block);
for(uint32_t i = 0; i < numAbbrevDefs; i++)
WriteAbbrevDefinition(abbrevs[i]);
}
EndBlock();
}
void BitcodeWriter::EmitGlobalVarAbbrev()
{
m_GlobalVarAbbrev = (uint32_t)curAbbrevs.size();
AbbrevParam align = AbbFixed(m_AlignBits);
if(m_AlignBits == 0)
align = AbbLiteral(0);
AbbrevParam section = AbbFixed(m_NumSectionBits);
if(m_NumSectionBits == 0)
section = AbbLiteral(0);
m_GlobalVarAbbrevDef[0] = AbbLiteral(ModuleRecord::GLOBALVAR);
m_GlobalVarAbbrevDef[1] = AbbFixed(m_GlobalTypeBits);
m_GlobalVarAbbrevDef[2] = AbbVBR(6);
m_GlobalVarAbbrevDef[3] = AbbVBR(6);
m_GlobalVarAbbrevDef[4] = AbbFixed(5);
m_GlobalVarAbbrevDef[5] = align;
m_GlobalVarAbbrevDef[6] = section;
WriteAbbrevDefinition(m_GlobalVarAbbrevDef);
}
uint32_t BitcodeWriter::GetAbbrevID(uint32_t id)
{
// the id is a block-local index, starting from 0, of the abbrevs defined for that block.
// the ID we need to encode starts at APPLICATION_ABBREV for the first one, so add that
return APPLICATION_ABBREV + id;
}
void BitcodeWriter::AutoRecord(uint32_t record, bool param, uint64_t val)
{
uint32_t idx = ~0U;
// the records with abbrevs are hardcoded, so just determine if this record in this block has an
// abbrev and select it here
switch(curBlock)
{
case KnownBlock::VALUE_SYMTAB_BLOCK:
RDCERR("Symbol table entry needs multiple parameters");
break;
case KnownBlock::MODULE_BLOCK:
switch(ModuleRecord(record))
{
case ModuleRecord::GLOBALVAR: RDCERR("global var needs multiple parameters"); break;
default: break;
}
break;
case KnownBlock::CONSTANTS_BLOCK:
{
// blockStack[0] is always the module block
const bool globalConsts = blockStack.size() == 2;
switch(ConstantsRecord(record))
{
// global only abbrevs
case ConstantsRecord::AGGREGATE:
if(globalConsts)
idx = (uint32_t)ConstantsAbbrev::Aggregate;
break;
case ConstantsRecord::STRING:
if(globalConsts)
idx = (uint32_t)ConstantsAbbrev::String;
break;
// these abbrevs are available in all constants blocks
case ConstantsRecord::SETTYPE: idx = (uint32_t)ConstantsAbbrev::SetType; break;
case ConstantsRecord::INTEGER: idx = (uint32_t)ConstantsAbbrev::Integer; break;
case ConstantsRecord::EVAL_CAST:
idx = (uint32_t)ConstantsAbbrev::EvalCast;
break;
// LLVM doesn't seem to use this abbrev?
// case ConstantsRecord::CONST_NULL: idx = (uint32_t)ConstantsAbbrev::Null; break;
default: break;
}
break;
}
case KnownBlock::TYPE_BLOCK:
switch(TypeRecord(record))
{
case TypeRecord::POINTER: RDCERR("Pointer type needs multiple parameters"); break;
case TypeRecord::FUNCTION: idx = (uint32_t)TypeAbbrev::Function; break;
case TypeRecord::STRUCT_ANON: idx = (uint32_t)TypeAbbrev::AnonStruct; break;
case TypeRecord::STRUCT_NAME: idx = (uint32_t)TypeAbbrev::StructName; break;
case TypeRecord::STRUCT_NAMED: idx = (uint32_t)TypeAbbrev::NamedStruct; break;
case TypeRecord::ARRAY: idx = (uint32_t)TypeAbbrev::Array; break;
default: break;
}
break;
default: break;
}
// if we got a valid abbrev, use it, otherwise emit unabbrev
if(idx < curAbbrevs.size())
{
// write the abbrev ID
b.fixed(abbrevSize, GetAbbrevID(idx));
Abbrev(curAbbrevs[idx], record, val);
}
else
{
Unabbrev(record, param, val);
}
}
void BitcodeWriter::AutoRecord(uint32_t record, const rdcarray<uint64_t> &vals)
{
uint32_t idx = ~0U;
// the records with abbrevs are hardcoded, so just determine if this record in this block has an
// abbrev and select it here
switch(curBlock)
{
case KnownBlock::VALUE_SYMTAB_BLOCK:
// the selection of abbrev here depends on the data
break;
case KnownBlock::MODULE_BLOCK:
switch(ModuleRecord(record))
{
case ModuleRecord::GLOBALVAR:
{
idx = m_GlobalVarAbbrev;
// if any of the later values are non-zero, can't use the global var abbrev
for(size_t i = 6; i < vals.size(); i++)
if(vals[i] != 0)
idx = ~0U;
break;
}
default: break;
}
break;
case KnownBlock::CONSTANTS_BLOCK:
{
// blockStack[0] is always the module block
const bool globalConsts = blockStack.size() == 2;
switch(ConstantsRecord(record))
{
// global only abbrevs
case ConstantsRecord::AGGREGATE:
if(globalConsts)
idx = (uint32_t)ConstantsAbbrev::Aggregate;
break;
case ConstantsRecord::STRING:
if(globalConsts)
idx = (uint32_t)ConstantsAbbrev::String;
break;
case ConstantsRecord::CSTRING:
if(globalConsts)
{
bool c6 = true, c7 = true;
for(size_t i = 0; (c6 || c7) && i < vals.size(); i++)
{
if(!isChar6(char(vals[i])))
c6 = false;
if(vals[i] >= 128)
c7 = false;
}
if(c6)
idx = (uint32_t)ConstantsAbbrev::CString6;
else if(c7)
idx = (uint32_t)ConstantsAbbrev::CString7;
}
break;
// these abbrevs are available in all constants blocks
case ConstantsRecord::SETTYPE: idx = (uint32_t)ConstantsAbbrev::SetType; break;
case ConstantsRecord::INTEGER: idx = (uint32_t)ConstantsAbbrev::Integer; break;
case ConstantsRecord::EVAL_CAST: idx = (uint32_t)ConstantsAbbrev::EvalCast; break;
case ConstantsRecord::CONST_NULL: idx = (uint32_t)ConstantsAbbrev::Null; break;
default: break;
}
break;
}
case KnownBlock::TYPE_BLOCK:
switch(TypeRecord(record))
{
case TypeRecord::POINTER:
// only use pointer abbrev if address space is 0
if(vals.size() == 2 && vals[1] == 0)
idx = (uint32_t)TypeAbbrev::Pointer;
break;
case TypeRecord::FUNCTION: idx = (uint32_t)TypeAbbrev::Function; break;
case TypeRecord::STRUCT_ANON: idx = (uint32_t)TypeAbbrev::AnonStruct; break;
case TypeRecord::STRUCT_NAME:
{
idx = (uint32_t)TypeAbbrev::StructName;
for(size_t i = 0; i < vals.size(); i++)
{
if(!isChar6(char(vals[i])))
{
idx = ~0U;
break;
}
}
break;
}
case TypeRecord::STRUCT_NAMED: idx = (uint32_t)TypeAbbrev::NamedStruct; break;
case TypeRecord::ARRAY: idx = (uint32_t)TypeAbbrev::Array; break;
default: break;
}
break;
default: break;
}
// if we got a valid abbrev, use it, otherwise emit unabbrev
if(idx < curAbbrevs.size())
{
// write the abbrev ID
b.fixed(abbrevSize, GetAbbrevID(idx));
Abbrev(curAbbrevs[idx], record, vals);
}
else
{
Unabbrev(record, false, vals);
}
}
void BitcodeWriter::Abbrev(AbbrevParam *abbr, uint32_t record, uint64_t val)
{
WriteAbbrevParam(abbr[0], record);
// if this abbrev has a parameter, encode it - it may be parameterless in which case we ignore val
if(abbr[1].encoding != AbbrevEncoding::Unknown)
WriteAbbrevParam(abbr[1], val);
}
void BitcodeWriter::Abbrev(AbbrevParam *abbr, uint32_t record, const rdcarray<uint64_t> &vals)
{
WriteAbbrevParam(abbr[0], record);
size_t i = 0;
abbr++;
while(abbr->encoding != AbbrevEncoding::Unknown)
{
RDCASSERT(i < vals.size());
// only one array per abbrev, consume the rest of the vals
if(abbr->encoding == AbbrevEncoding::Array)
{
abbr++;
RDCASSERT(abbr->encoding != AbbrevEncoding::Unknown);
b.vbr(6, vals.size() - i);
for(; i < vals.size(); i++)
WriteAbbrevParam(abbr[0], vals[i]);
// end now
break;
}
else if(abbr->encoding == AbbrevEncoding::Blob)
{
// expect vals to be length then blob pointer packed into uint64_t
size_t length = (size_t)vals[i];
byte *blob = (byte *)vals[i + 1];
b.WriteBlob(blob, length);
}
else
{
WriteAbbrevParam(abbr[0], vals[i++]);
abbr++;
}
}
}
void BitcodeWriter::WriteAbbrevParam(const AbbrevParam &abbrev, uint64_t val)
{
// if the encoding is a literal we don't have to do anything
if(abbrev.encoding == AbbrevEncoding::Literal)
return;
if(abbrev.encoding == AbbrevEncoding::Fixed)
{
uint64_t width = abbrev.value;
if(abbrev.value == MagicFixedSizeNumTypes)
width = m_NumTypeBits;
else if(abbrev.value == MagicFixedSizeNumConstants)
width = m_NumConstantBits;
b.fixed((size_t)width, val);
}
else if(abbrev.encoding == AbbrevEncoding::VBR)
b.vbr((size_t)abbrev.value, val);
else if(abbrev.encoding == AbbrevEncoding::Char6)
b.c6(char(val));
else
RDCERR("Unexpected abbrev param type: %d", abbrev.encoding);
}
void BitcodeWriter::Unabbrev(uint32_t record, bool param, uint64_t val)
{
b.fixed(abbrevSize, UNABBREV_RECORD);
b.vbr(6, record);
b.vbr(6, param ? 1U : 0U);
if(param)
b.vbr(6, val);
}
void BitcodeWriter::Unabbrev(uint32_t record, bool, const rdcarray<uint64_t> &vals)
{
b.fixed(abbrevSize, UNABBREV_RECORD);
b.vbr(6, record);
b.vbr(6, vals.size());
for(uint64_t v : vals)
b.vbr(6, v);
}
};
#if ENABLED(ENABLE_UNIT_TESTS)
#include "catch/catch.hpp"
#include "llvm_decoder.h"
TEST_CASE("Check LLVM bitwriter", "[llvm]")
{
bytebuf bits;
SECTION("Check simple writing of bytes")
{
LLVMBC::BitWriter w(bits);
w.Write<byte>(0x01);
w.Write<byte>(0x02);
w.Write<byte>(0x40);
w.Write<byte>(0x80);
w.Write<byte>(0xff);
w.align32bits();
LLVMBC::BitReader r(bits.data(), bits.size());
CHECK(r.Read<byte>() == 0x01);
CHECK(r.Read<byte>() == 0x02);
CHECK(r.Read<byte>() == 0x40);
CHECK(r.Read<byte>() == 0x80);
CHECK(r.Read<byte>() == 0xff);
r.align32bits();
CHECK(r.AtEndOfStream());
}
SECTION("Check fixed encoding")
{
uint32_t val = 0x3CA5F096;
LLVMBC::BitWriter w(bits);
for(uint32_t i = 0; i < 32; i++)
w.fixed(i + 1, val);
w.align32bits();
LLVMBC::BitReader r(bits.data(), bits.size());
for(uint32_t i = 0; i < 32; i++)
{
CHECK(r.fixed<uint32_t>(i + 1) == (val & ((1ULL << (i + 1)) - 1)));
}
r.align32bits();
CHECK(r.AtEndOfStream());
}
SECTION("Check variable encoding")
{
LLVMBC::BitWriter w(bits);
w.vbr<uint32_t>(8, 0x12);
w.vbr<uint32_t>(6, 0x12);
w.vbr<uint32_t>(5, 0x12);
w.vbr<uint32_t>(4, 0x12);
w.vbr<uint32_t>(3, 0x12);
w.vbr<uint32_t>(8, 0x12345678);
w.vbr<uint32_t>(6, 0x12345678);
w.vbr<uint32_t>(5, 0x12345678);
w.vbr<uint32_t>(4, 0x12345678);
w.vbr<uint32_t>(3, 0x12345678);
w.vbr<uint64_t>(8, 0x123456789ABCDEFULL);
w.vbr<uint64_t>(6, 0x123456789ABCDEFULL);
w.vbr<uint64_t>(5, 0x123456789ABCDEFULL);
w.vbr<uint64_t>(4, 0x123456789ABCDEFULL);
w.vbr<uint64_t>(3, 0x123456789ABCDEFULL);
w.align32bits();
LLVMBC::BitReader r(bits.data(), bits.size());
CHECK(r.vbr<uint32_t>(8) == 0x12);
CHECK(r.vbr<uint32_t>(6) == 0x12);
CHECK(r.vbr<uint32_t>(5) == 0x12);
CHECK(r.vbr<uint32_t>(4) == 0x12);
CHECK(r.vbr<uint32_t>(3) == 0x12);
CHECK(r.vbr<uint32_t>(8) == 0x12345678);
CHECK(r.vbr<uint32_t>(6) == 0x12345678);
CHECK(r.vbr<uint32_t>(5) == 0x12345678);
CHECK(r.vbr<uint32_t>(4) == 0x12345678);
CHECK(r.vbr<uint32_t>(3) == 0x12345678);
CHECK(r.vbr<uint64_t>(8) == 0x123456789ABCDEFULL);
CHECK(r.vbr<uint64_t>(6) == 0x123456789ABCDEFULL);
CHECK(r.vbr<uint64_t>(5) == 0x123456789ABCDEFULL);
CHECK(r.vbr<uint64_t>(4) == 0x123456789ABCDEFULL);
CHECK(r.vbr<uint64_t>(3) == 0x123456789ABCDEFULL);
r.align32bits();
CHECK(r.AtEndOfStream());
}
SECTION("Check signed vbr encoding")
{
LLVMBC::BitWriter w(bits);
w.vbr<uint64_t>(4, LLVMBC::BitWriter::svbr(0x12));
w.vbr<uint64_t>(4, LLVMBC::BitWriter::svbr(-0x12));
w.vbr<uint64_t>(4, LLVMBC::BitWriter::svbr(0x1234));
w.vbr<uint64_t>(4, LLVMBC::BitWriter::svbr(-0x1234));
w.vbr<uint64_t>(4, LLVMBC::BitWriter::svbr(0x12345678));
w.vbr<uint64_t>(4, LLVMBC::BitWriter::svbr(-0x12345678));
w.vbr<uint64_t>(4, LLVMBC::BitWriter::svbr(INT_MAX));
w.vbr<uint64_t>(4, LLVMBC::BitWriter::svbr(-INT_MAX));
w.align32bits();
CHECK(bits.size() == 28);
LLVMBC::BitReader r(bits.data(), bits.size());
CHECK(LLVMBC::BitReader::svbr(r.vbr<uint32_t>(4)) == 0x12);
CHECK(LLVMBC::BitReader::svbr(r.vbr<uint32_t>(4)) == -0x12);
CHECK(LLVMBC::BitReader::svbr(r.vbr<uint32_t>(4)) == 0x1234);
CHECK(LLVMBC::BitReader::svbr(r.vbr<uint32_t>(4)) == -0x1234);
CHECK(LLVMBC::BitReader::svbr(r.vbr<uint32_t>(4)) == 0x12345678);
CHECK(LLVMBC::BitReader::svbr(r.vbr<uint32_t>(4)) == -0x12345678);
CHECK(LLVMBC::BitReader::svbr(r.vbr<uint32_t>(4)) == INT_MAX);
CHECK(LLVMBC::BitReader::svbr(r.vbr<uint32_t>(4)) == -INT_MAX);
r.align32bits();
CHECK(r.AtEndOfStream());
}
SECTION("Check char6 encoding")
{
LLVMBC::BitWriter w(bits);
const char string[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._";
for(size_t i = 0; i < strlen(string); i++)
w.c6(string[i]);
w.align32bits();
LLVMBC::BitReader r(bits.data(), bits.size());
for(size_t i = 0; i < strlen(string); i++)
CHECK(r.c6() == string[i]);
r.align32bits();
CHECK(r.AtEndOfStream());
}
SECTION("Check blobs")
{
bytebuf foo = {0x01, 0x02, 0x40, 0x80, 0xff};
for(byte i = 0; i < 250; i++)
foo.push_back(i);
foo.push_back(0x80);
foo.push_back(0x70);
foo.push_back(0x60);
LLVMBC::BitWriter w(bits);
w.WriteBlob(foo);
w.align32bits();
LLVMBC::BitReader r(bits.data(), bits.size());
const byte *ptr = NULL;
size_t len = 0;
r.ReadBlob(ptr, len);
r.align32bits();
CHECK(r.AtEndOfStream());
REQUIRE(len == foo.size());
CHECK(bytebuf(ptr, len) == foo);
}
}
#endif