CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
327 lines
11 KiB
C++
327 lines
11 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "DecoderTableEmitter.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/Support/Format.h"
|
|
#include "llvm/Support/LEB128.h"
|
|
|
|
using namespace llvm;
|
|
|
|
unsigned
|
|
DecoderTableEmitter::computeNodeSize(const DecoderTreeNode *Node) const {
|
|
// To make the arithmetic below clearer.
|
|
static constexpr unsigned OpcodeSize = 1;
|
|
static constexpr unsigned FieldWidthSize = 1;
|
|
|
|
switch (Node->getKind()) {
|
|
case DecoderTreeNode::CheckAny: {
|
|
const auto *N = static_cast<const CheckAnyNode *>(Node);
|
|
// Pretend the node was optimized. See the comment in emitCheckAnyNode.
|
|
if (range_size(N->children()) == 1)
|
|
return computeNodeSize(*N->child_begin());
|
|
unsigned Size = 0;
|
|
// All children except the last one are preceded by OPC_Scope opcode and
|
|
// the size of the child.
|
|
for (const DecoderTreeNode *Child : drop_end(N->children())) {
|
|
unsigned ChildSize = computeNodeSize(Child);
|
|
Size += OpcodeSize + getULEB128Size(ChildSize) + ChildSize;
|
|
}
|
|
const DecoderTreeNode *Child = *std::prev(N->child_end());
|
|
return Size + computeNodeSize(Child);
|
|
}
|
|
case DecoderTreeNode::CheckAll: {
|
|
const auto *N = static_cast<const CheckAllNode *>(Node);
|
|
unsigned Size = 0;
|
|
for (const DecoderTreeNode *Child : N->children())
|
|
Size += computeNodeSize(Child);
|
|
return Size;
|
|
}
|
|
case DecoderTreeNode::CheckField: {
|
|
const auto *N = static_cast<const CheckFieldNode *>(Node);
|
|
return OpcodeSize + getULEB128Size(N->getStartBit()) + FieldWidthSize +
|
|
getULEB128Size(N->getValue());
|
|
}
|
|
case DecoderTreeNode::SwitchField: {
|
|
const auto *N = static_cast<const SwitchFieldNode *>(Node);
|
|
unsigned Size =
|
|
OpcodeSize + getULEB128Size(N->getStartBit()) + FieldWidthSize;
|
|
|
|
for (auto [Val, Child] : drop_end(N->cases())) {
|
|
unsigned ChildSize = computeNodeSize(Child);
|
|
Size += getULEB128Size(Val) + getULEB128Size(ChildSize) + ChildSize;
|
|
}
|
|
|
|
// The last child is emitted with sentinel value 0 instead of the size.
|
|
// See the comment in emitSwitchFieldNode.
|
|
auto [Val, Child] = *std::prev(N->case_end());
|
|
unsigned ChildSize = computeNodeSize(Child);
|
|
Size += getULEB128Size(Val) + getULEB128Size(0) + ChildSize;
|
|
return Size;
|
|
}
|
|
case DecoderTreeNode::CheckPredicate: {
|
|
const auto *N = static_cast<const CheckPredicateNode *>(Node);
|
|
unsigned PredicateIndex = N->getPredicateIndex();
|
|
return OpcodeSize + getULEB128Size(PredicateIndex);
|
|
}
|
|
case DecoderTreeNode::SoftFail: {
|
|
const auto *N = static_cast<const SoftFailNode *>(Node);
|
|
return OpcodeSize + getULEB128Size(N->getPositiveMask()) +
|
|
getULEB128Size(N->getNegativeMask());
|
|
}
|
|
case DecoderTreeNode::Decode: {
|
|
const auto *N = static_cast<const DecodeNode *>(Node);
|
|
return OpcodeSize + getULEB128Size(N->getInstOpcode()) +
|
|
getULEB128Size(N->getDecoderIndex());
|
|
}
|
|
}
|
|
llvm_unreachable("Unknown node kind");
|
|
}
|
|
|
|
unsigned DecoderTableEmitter::computeTableSize(const DecoderTreeNode *Root,
|
|
unsigned BitWidth) const {
|
|
unsigned Size = 0;
|
|
if (BitWidth)
|
|
Size += getULEB128Size(BitWidth);
|
|
Size += computeNodeSize(Root);
|
|
return Size;
|
|
}
|
|
|
|
void DecoderTableEmitter::emitStartLine() {
|
|
LineStartIndex = CurrentIndex;
|
|
OS.indent(2);
|
|
}
|
|
|
|
void DecoderTableEmitter::emitOpcode(StringRef Name) {
|
|
emitStartLine();
|
|
OS << Name << ", ";
|
|
++CurrentIndex;
|
|
}
|
|
|
|
void DecoderTableEmitter::emitByte(uint8_t Val) {
|
|
OS << static_cast<unsigned>(Val) << ", ";
|
|
++CurrentIndex;
|
|
}
|
|
|
|
void DecoderTableEmitter::emitUInt8(unsigned Val) {
|
|
assert(isUInt<8>(Val));
|
|
emitByte(Val);
|
|
}
|
|
|
|
void DecoderTableEmitter::emitULEB128(uint64_t Val) {
|
|
while (Val >= 0x80) {
|
|
emitByte((Val & 0x7F) | 0x80);
|
|
Val >>= 7;
|
|
}
|
|
emitByte(Val);
|
|
}
|
|
|
|
raw_ostream &DecoderTableEmitter::emitComment(indent Indent) {
|
|
constexpr unsigned CommentColumn = 45;
|
|
if (OS.getColumn() > CommentColumn)
|
|
OS << '\n';
|
|
OS.PadToColumn(CommentColumn);
|
|
OS << "// " << format_decimal(LineStartIndex, IndexWidth) << ": " << Indent;
|
|
return OS;
|
|
}
|
|
|
|
namespace {
|
|
|
|
/// Helper class for printing bit ranges.
|
|
struct BitRange {
|
|
unsigned MSB, LSB;
|
|
|
|
friend raw_ostream &operator<<(raw_ostream &OS, BitRange R) {
|
|
if (R.MSB == R.LSB)
|
|
OS << '[' << R.LSB << ']';
|
|
else
|
|
OS << '[' << R.MSB << ':' << R.LSB << ']';
|
|
return OS;
|
|
}
|
|
};
|
|
|
|
} // namespace
|
|
|
|
void DecoderTableEmitter::emitCheckAnyNode(const CheckAnyNode *N,
|
|
indent Indent) {
|
|
// TODO: Single-child CheckAny node should be optimized out. For now,
|
|
// pretend this is the case and print the single child unindented.
|
|
if (range_size(N->children()) == 1) {
|
|
emitNode(*N->child_begin(), Indent);
|
|
return;
|
|
}
|
|
|
|
ListSeparator LS("} else ");
|
|
for (const DecoderTreeNode *Child : drop_end(N->children())) {
|
|
emitOpcode("OPC_Scope");
|
|
emitULEB128(computeNodeSize(Child));
|
|
|
|
emitComment(Indent) << LS << "try {\n";
|
|
emitNode(Child, Indent + 1);
|
|
}
|
|
|
|
// Don't emit OPC_Scope for the last child so that we leave the current scope
|
|
// if it fails. Otherwise, we would need some kind of OPC_LeaveScope opcode.
|
|
const DecoderTreeNode *Child = *std::prev(N->child_end());
|
|
|
|
emitComment(Indent) << LS << "try {\n";
|
|
emitNode(Child, Indent + 1);
|
|
emitComment(Indent) << "}\n";
|
|
}
|
|
|
|
void DecoderTableEmitter::emitCheckAllNode(const CheckAllNode *N,
|
|
indent Indent) {
|
|
// TODO: Single-child CheckAll should be optimized out.
|
|
// TODO: Nested CheckAll nodes should be flattened.
|
|
// TODO: Sibling CheckAll and other Check* nodes should be merged together.
|
|
for (const DecoderTreeNode *Child : N->children())
|
|
emitNode(Child, Indent);
|
|
}
|
|
|
|
void DecoderTableEmitter::emitSwitchFieldNode(const SwitchFieldNode *N,
|
|
indent Indent) {
|
|
unsigned LSB = N->getStartBit();
|
|
unsigned Width = N->getNumBits();
|
|
unsigned MSB = LSB + Width - 1;
|
|
|
|
emitOpcode("OPC_SwitchField");
|
|
emitULEB128(LSB);
|
|
emitUInt8(Width);
|
|
|
|
emitComment(Indent) << "switch Inst" << BitRange{MSB, LSB} << " {\n";
|
|
|
|
for (auto [Val, Child] : drop_end(N->cases())) {
|
|
emitStartLine();
|
|
emitULEB128(Val);
|
|
emitULEB128(computeNodeSize(Child));
|
|
|
|
emitComment(Indent) << "case " << format_hex(Val, 0) << ": {\n";
|
|
emitNode(Child, Indent + 1);
|
|
emitComment(Indent) << "}\n";
|
|
}
|
|
|
|
// Don't emit the size of the last child and instead emit a sentinel value,
|
|
// which tells the interpreter that this is the last case. The interpreter
|
|
// doesn't need to know its size because SwitchField node never falls through
|
|
// (we either successfully decode an instruction, or leave the current scope).
|
|
auto [Val, Child] = *std::prev(N->case_end());
|
|
emitStartLine();
|
|
emitULEB128(Val);
|
|
emitULEB128(0);
|
|
|
|
emitComment(Indent) << "case " << format_hex(Val, 0) << ": {\n";
|
|
emitNode(Child, Indent + 1);
|
|
emitComment(Indent) << "}\n";
|
|
|
|
emitComment(Indent) << "} // switch Inst" << BitRange{MSB, LSB} << "\n";
|
|
}
|
|
|
|
void DecoderTableEmitter::emitCheckFieldNode(const CheckFieldNode *N,
|
|
indent Indent) {
|
|
unsigned LSB = N->getStartBit();
|
|
unsigned Width = N->getNumBits();
|
|
unsigned MSB = LSB + Width - 1;
|
|
uint64_t Val = N->getValue();
|
|
|
|
emitOpcode("OPC_CheckField");
|
|
emitULEB128(LSB);
|
|
emitUInt8(Width);
|
|
emitULEB128(Val);
|
|
|
|
emitComment(Indent) << "check Inst" << BitRange{MSB, LSB}
|
|
<< " == " << format_hex(Val, 0) << '\n';
|
|
}
|
|
|
|
void DecoderTableEmitter::emitCheckPredicateNode(const CheckPredicateNode *N,
|
|
indent Indent) {
|
|
unsigned PredicateIndex = N->getPredicateIndex();
|
|
|
|
emitOpcode("OPC_CheckPredicate");
|
|
emitULEB128(PredicateIndex);
|
|
TableInfo.HasCheckPredicate = true;
|
|
|
|
emitComment(Indent) << "check predicate " << PredicateIndex << "\n";
|
|
}
|
|
|
|
void DecoderTableEmitter::emitSoftFailNode(const SoftFailNode *N,
|
|
indent Indent) {
|
|
uint64_t PositiveMask = N->getPositiveMask();
|
|
uint64_t NegativeMask = N->getNegativeMask();
|
|
|
|
emitOpcode("OPC_SoftFail");
|
|
emitULEB128(PositiveMask);
|
|
emitULEB128(NegativeMask);
|
|
TableInfo.HasSoftFail = true;
|
|
|
|
emitComment(Indent) << "softfail";
|
|
OS << " pos=" << format_hex(PositiveMask, 10);
|
|
OS << " neg=" << format_hex(NegativeMask, 10) << '\n';
|
|
}
|
|
|
|
void DecoderTableEmitter::emitDecodeNode(const DecodeNode *N, indent Indent) {
|
|
StringRef EncodingName = N->getEncodingName();
|
|
unsigned InstOpcode = N->getInstOpcode();
|
|
unsigned DecoderIndex = N->getDecoderIndex();
|
|
|
|
emitOpcode("OPC_Decode");
|
|
emitULEB128(InstOpcode);
|
|
emitULEB128(DecoderIndex);
|
|
|
|
emitComment(Indent) << "decode to " << EncodingName << " using decoder "
|
|
<< DecoderIndex << '\n';
|
|
}
|
|
|
|
void DecoderTableEmitter::emitNode(const DecoderTreeNode *N, indent Indent) {
|
|
switch (N->getKind()) {
|
|
case DecoderTreeNode::CheckAny:
|
|
return emitCheckAnyNode(static_cast<const CheckAnyNode *>(N), Indent);
|
|
case DecoderTreeNode::CheckAll:
|
|
return emitCheckAllNode(static_cast<const CheckAllNode *>(N), Indent);
|
|
case DecoderTreeNode::SwitchField:
|
|
return emitSwitchFieldNode(static_cast<const SwitchFieldNode *>(N), Indent);
|
|
case DecoderTreeNode::CheckField:
|
|
return emitCheckFieldNode(static_cast<const CheckFieldNode *>(N), Indent);
|
|
case DecoderTreeNode::CheckPredicate:
|
|
return emitCheckPredicateNode(static_cast<const CheckPredicateNode *>(N),
|
|
Indent);
|
|
case DecoderTreeNode::SoftFail:
|
|
return emitSoftFailNode(static_cast<const SoftFailNode *>(N), Indent);
|
|
case DecoderTreeNode::Decode:
|
|
return emitDecodeNode(static_cast<const DecodeNode *>(N), Indent);
|
|
}
|
|
llvm_unreachable("Unknown node kind");
|
|
}
|
|
|
|
void DecoderTableEmitter::emitTable(StringRef TableName, unsigned BitWidth,
|
|
const DecoderTreeNode *Root) {
|
|
unsigned TableSize = computeTableSize(Root, BitWidth);
|
|
OS << "static const uint8_t " << TableName << "[" << TableSize << "] = {\n";
|
|
|
|
// Calculate the number of decimal places for table indices.
|
|
// This is simply log10 of the table size.
|
|
IndexWidth = 1;
|
|
for (unsigned S = TableSize; S /= 10;)
|
|
++IndexWidth;
|
|
|
|
CurrentIndex = 0;
|
|
|
|
// When specializing decoders per bit width, each decoder table will begin
|
|
// with the bitwidth for that table.
|
|
if (BitWidth) {
|
|
emitStartLine();
|
|
emitULEB128(BitWidth);
|
|
emitComment(indent(0)) << "BitWidth " << BitWidth << '\n';
|
|
}
|
|
|
|
emitNode(Root, indent(0));
|
|
assert(CurrentIndex == TableSize &&
|
|
"The size of the emitted table differs from the calculated one");
|
|
|
|
OS << "};\n";
|
|
}
|