cb424d7448
verify-patch-sanity.py validates every active recipe .patch has internally- consistent hunk line counts — catching the 'malformed patch at line N' failure at commit/CI/preflight time instead of hours into a cook. This cycle hit that class three times (qtwaylandscanner, sddm, xwayland), each only discovered when cookbook tried to apply the patch. Running it across the repo found 29 latent malformed patches (validated against GNU patch: e.g. relibc/P3-sysv-ipc reproduces 'malformed patch at line 22'). They were harmless only because they sit in vendored recipes (baked, not re- applied) — but would fail on any version-bump re-derivation. --fix recounts the hunk headers (body untouched) and repaired all 29. Wired into build-preflight.sh (Phase 1.0D) and redbear-ci.yml, with a unit test (test-patch-sanity.sh). Skips archived/legacy trees and unvalidatable formats (empty placeholders, bare-@@ git hunks).
230 lines
7.2 KiB
C++
230 lines
7.2 KiB
C++
//===- InstrDocsEmitter.cpp - Opcode Documentation Generator --------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// InstrDocsEmitter generates restructured text documentation for the opcodes
|
|
// that can be used by MachineInstr. For each opcode, the documentation lists:
|
|
// * Opcode name
|
|
// * Assembly string
|
|
// * Flags (e.g. mayLoad, isBranch, ...)
|
|
// * Operands, including type and name
|
|
// * Operand constraints
|
|
// * Implicit register uses & defs
|
|
// * Predicates
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "Common/CodeGenDAGPatterns.h"
|
|
#include "Common/CodeGenInstruction.h"
|
|
#include "Common/CodeGenTarget.h"
|
|
#include "llvm/TableGen/Record.h"
|
|
#include "llvm/TableGen/TableGenBackend.h"
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
using namespace llvm;
|
|
|
|
static void writeTitle(StringRef Str, raw_ostream &OS, char Kind = '-') {
|
|
OS << std::string(Str.size(), Kind) << "\n"
|
|
<< Str << "\n"
|
|
<< std::string(Str.size(), Kind) << "\n";
|
|
}
|
|
|
|
static void writeHeader(StringRef Str, raw_ostream &OS, char Kind = '-') {
|
|
OS << Str << "\n" << std::string(Str.size(), Kind) << "\n";
|
|
}
|
|
|
|
static std::string escapeForRST(StringRef Str) {
|
|
std::string Result;
|
|
Result.reserve(Str.size() + 4);
|
|
for (char C : Str) {
|
|
switch (C) {
|
|
// We want special characters to be shown as their C escape codes.
|
|
case '\n':
|
|
Result += "\\n";
|
|
break;
|
|
case '\t':
|
|
Result += "\\t";
|
|
break;
|
|
// Underscore at the end of a line has a special meaning in rst.
|
|
case '_':
|
|
Result += "\\_";
|
|
break;
|
|
default:
|
|
Result += C;
|
|
}
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
static void EmitInstrDocs(const RecordKeeper &RK, raw_ostream &OS) {
|
|
const CodeGenDAGPatterns CDP(RK);
|
|
const CodeGenTarget &Target = CDP.getTargetInfo();
|
|
unsigned VariantCount = Target.getAsmParserVariantCount();
|
|
|
|
// Page title.
|
|
std::string Title = Target.getName().str();
|
|
Title += " Instructions";
|
|
writeTitle(Title, OS);
|
|
OS << "\n";
|
|
|
|
for (const CodeGenInstruction *II : Target.getInstructions()) {
|
|
const Record *Inst = II->TheDef;
|
|
|
|
// Don't print the target-independent instructions.
|
|
if (II->Namespace == "TargetOpcode")
|
|
continue;
|
|
|
|
// Heading (instruction name).
|
|
writeHeader(escapeForRST(Inst->getName()), OS, '=');
|
|
OS << "\n";
|
|
|
|
// Assembly string(s).
|
|
if (!II->AsmString.empty()) {
|
|
for (unsigned VarNum = 0; VarNum < VariantCount; ++VarNum) {
|
|
const Record *AsmVariant = Target.getAsmParserVariant(VarNum);
|
|
OS << "Assembly string";
|
|
if (VariantCount != 1)
|
|
OS << " (" << AsmVariant->getValueAsString("Name") << ")";
|
|
std::string AsmString =
|
|
CodeGenInstruction::FlattenAsmStringVariants(II->AsmString, VarNum);
|
|
// We trim spaces at each end of the asm string because rst needs the
|
|
// formatting backticks to be next to a non-whitespace character.
|
|
OS << ": ``" << escapeForRST(StringRef(AsmString).trim(" "))
|
|
<< "``\n\n";
|
|
}
|
|
}
|
|
|
|
// Boolean flags.
|
|
std::vector<const char *> FlagStrings;
|
|
#define xstr(s) str(s)
|
|
#define str(s) #s
|
|
#define FLAG(f) \
|
|
if (II->f) { \
|
|
FlagStrings.push_back(str(f)); \
|
|
}
|
|
FLAG(isReturn)
|
|
FLAG(isEHScopeReturn)
|
|
FLAG(isBranch)
|
|
FLAG(isIndirectBranch)
|
|
FLAG(isCompare)
|
|
FLAG(isMoveImm)
|
|
FLAG(isBitcast)
|
|
FLAG(isSelect)
|
|
FLAG(isBarrier)
|
|
FLAG(isCall)
|
|
FLAG(isAdd)
|
|
FLAG(isTrap)
|
|
FLAG(canFoldAsLoad)
|
|
FLAG(mayLoad)
|
|
// FLAG(mayLoad_Unset) // Deliberately omitted.
|
|
FLAG(mayStore)
|
|
// FLAG(mayStore_Unset) // Deliberately omitted.
|
|
FLAG(isPredicable)
|
|
FLAG(isConvertibleToThreeAddress)
|
|
FLAG(isCommutable)
|
|
FLAG(isTerminator)
|
|
FLAG(isReMaterializable)
|
|
FLAG(hasDelaySlot)
|
|
FLAG(usesCustomInserter)
|
|
FLAG(hasPostISelHook)
|
|
FLAG(hasCtrlDep)
|
|
FLAG(isNotDuplicable)
|
|
FLAG(hasSideEffects)
|
|
// FLAG(hasSideEffects_Unset) // Deliberately omitted.
|
|
FLAG(isAsCheapAsAMove)
|
|
FLAG(hasExtraSrcRegAllocReq)
|
|
FLAG(hasExtraDefRegAllocReq)
|
|
FLAG(isCodeGenOnly)
|
|
FLAG(isPseudo)
|
|
FLAG(isRegSequence)
|
|
FLAG(isExtractSubreg)
|
|
FLAG(isInsertSubreg)
|
|
FLAG(isConvergent)
|
|
FLAG(hasNoSchedulingInfo)
|
|
FLAG(variadicOpsAreDefs)
|
|
FLAG(isAuthenticated)
|
|
if (!FlagStrings.empty()) {
|
|
OS << "Flags: ";
|
|
ListSeparator LS;
|
|
for (auto FlagString : FlagStrings)
|
|
OS << LS << "``" << FlagString << "``";
|
|
OS << "\n\n";
|
|
}
|
|
|
|
// Operands.
|
|
for (unsigned i = 0; i < II->Operands.size(); ++i) {
|
|
bool IsDef = i < II->Operands.NumDefs;
|
|
const auto &Op = II->Operands[i];
|
|
|
|
if (Op.MINumOperands > 1) {
|
|
// This operand corresponds to multiple operands on the
|
|
// MachineInstruction, so print all of them, showing the types and
|
|
// names of both the compound operand and the basic operands it
|
|
// contains.
|
|
for (unsigned SubOpIdx = 0; SubOpIdx < Op.MINumOperands; ++SubOpIdx) {
|
|
const Record *SubRec =
|
|
cast<DefInit>(Op.MIOperandInfo->getArg(SubOpIdx))->getDef();
|
|
StringRef SubOpName = Op.MIOperandInfo->getArgNameStr(SubOpIdx);
|
|
StringRef SubOpTypeName = SubRec->getName();
|
|
|
|
OS << "* " << (IsDef ? "DEF" : "USE") << " ``" << Op.Rec->getName()
|
|
<< "/" << SubOpTypeName << ":$" << Op.Name << ".";
|
|
// Not all sub-operands are named, make up a name for these.
|
|
if (SubOpName.empty())
|
|
OS << "anon" << SubOpIdx;
|
|
else
|
|
OS << SubOpName;
|
|
OS << "``\n\n";
|
|
}
|
|
} else {
|
|
// The operand corresponds to only one MachineInstruction operand.
|
|
OS << "* " << (IsDef ? "DEF" : "USE") << " ``" << Op.Rec->getName()
|
|
<< ":$" << Op.Name << "``\n\n";
|
|
}
|
|
}
|
|
|
|
// Constraints.
|
|
StringRef Constraints = Inst->getValueAsString("Constraints");
|
|
if (!Constraints.empty()) {
|
|
OS << "Constraints: ``" << Constraints << "``\n\n";
|
|
}
|
|
|
|
// Implicit definitions.
|
|
if (!II->ImplicitDefs.empty()) {
|
|
OS << "Implicit defs: ";
|
|
ListSeparator LS;
|
|
for (const Record *Def : II->ImplicitDefs)
|
|
OS << LS << "``" << Def->getName() << "``";
|
|
OS << "\n\n";
|
|
}
|
|
|
|
// Implicit uses.
|
|
if (!II->ImplicitUses.empty()) {
|
|
OS << "Implicit uses: ";
|
|
ListSeparator LS;
|
|
for (const Record *Use : II->ImplicitUses)
|
|
OS << LS << "``" << Use->getName() << "``";
|
|
OS << "\n\n";
|
|
}
|
|
|
|
// Predicates.
|
|
std::vector<const Record *> Predicates =
|
|
II->TheDef->getValueAsListOfDefs("Predicates");
|
|
if (!Predicates.empty()) {
|
|
OS << "Predicates: ";
|
|
ListSeparator LS;
|
|
for (const Record *P : Predicates)
|
|
OS << LS << "``" << P->getName() << "``";
|
|
OS << "\n\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
static TableGen::Emitter::Opt X("gen-instr-docs", EmitInstrDocs,
|
|
"Generate instruction documentation");
|