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).
170 lines
5.6 KiB
C++
170 lines
5.6 KiB
C++
//===- MachineCycleAnalysis.cpp - Compute CycleInfo for Machine IR --------===//
|
|
//
|
|
// 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 "llvm/CodeGen/MachineCycleAnalysis.h"
|
|
#include "llvm/ADT/GenericCycleImpl.h"
|
|
#include "llvm/CodeGen/MachineRegisterInfo.h"
|
|
#include "llvm/CodeGen/MachineSSAContext.h"
|
|
#include "llvm/CodeGen/TargetInstrInfo.h"
|
|
#include "llvm/CodeGen/TargetSubtargetInfo.h"
|
|
#include "llvm/InitializePasses.h"
|
|
|
|
using namespace llvm;
|
|
|
|
template class llvm::GenericCycleInfo<llvm::MachineSSAContext>;
|
|
template class llvm::GenericCycle<llvm::MachineSSAContext>;
|
|
|
|
char MachineCycleInfoWrapperPass::ID = 0;
|
|
|
|
MachineCycleInfoWrapperPass::MachineCycleInfoWrapperPass()
|
|
: MachineFunctionPass(ID) {
|
|
initializeMachineCycleInfoWrapperPassPass(*PassRegistry::getPassRegistry());
|
|
}
|
|
|
|
INITIALIZE_PASS_BEGIN(MachineCycleInfoWrapperPass, "machine-cycles",
|
|
"Machine Cycle Info Analysis", true, true)
|
|
INITIALIZE_PASS_END(MachineCycleInfoWrapperPass, "machine-cycles",
|
|
"Machine Cycle Info Analysis", true, true)
|
|
|
|
void MachineCycleInfoWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
|
|
AU.setPreservesAll();
|
|
MachineFunctionPass::getAnalysisUsage(AU);
|
|
}
|
|
|
|
bool MachineCycleInfoWrapperPass::runOnMachineFunction(MachineFunction &Func) {
|
|
CI.clear();
|
|
|
|
F = &Func;
|
|
CI.compute(Func);
|
|
return false;
|
|
}
|
|
|
|
void MachineCycleInfoWrapperPass::print(raw_ostream &OS, const Module *) const {
|
|
OS << "MachineCycleInfo for function: " << F->getName() << "\n";
|
|
CI.print(OS);
|
|
}
|
|
|
|
void MachineCycleInfoWrapperPass::releaseMemory() {
|
|
CI.clear();
|
|
F = nullptr;
|
|
}
|
|
|
|
AnalysisKey MachineCycleAnalysis::Key;
|
|
|
|
MachineCycleInfo
|
|
MachineCycleAnalysis::run(MachineFunction &MF,
|
|
MachineFunctionAnalysisManager &MFAM) {
|
|
MachineCycleInfo MCI;
|
|
MCI.compute(MF);
|
|
return MCI;
|
|
}
|
|
|
|
namespace {
|
|
class MachineCycleInfoPrinterLegacy : public MachineFunctionPass {
|
|
public:
|
|
static char ID;
|
|
|
|
MachineCycleInfoPrinterLegacy();
|
|
|
|
bool runOnMachineFunction(MachineFunction &F) override;
|
|
void getAnalysisUsage(AnalysisUsage &AU) const override;
|
|
};
|
|
} // namespace
|
|
|
|
char MachineCycleInfoPrinterLegacy::ID = 0;
|
|
|
|
MachineCycleInfoPrinterLegacy::MachineCycleInfoPrinterLegacy()
|
|
: MachineFunctionPass(ID) {
|
|
initializeMachineCycleInfoPrinterLegacyPass(*PassRegistry::getPassRegistry());
|
|
}
|
|
|
|
INITIALIZE_PASS_BEGIN(MachineCycleInfoPrinterLegacy, "print-machine-cycles",
|
|
"Print Machine Cycle Info Analysis", true, true)
|
|
INITIALIZE_PASS_DEPENDENCY(MachineCycleInfoWrapperPass)
|
|
INITIALIZE_PASS_END(MachineCycleInfoPrinterLegacy, "print-machine-cycles",
|
|
"Print Machine Cycle Info Analysis", true, true)
|
|
|
|
void MachineCycleInfoPrinterLegacy::getAnalysisUsage(AnalysisUsage &AU) const {
|
|
AU.setPreservesAll();
|
|
AU.addRequired<MachineCycleInfoWrapperPass>();
|
|
MachineFunctionPass::getAnalysisUsage(AU);
|
|
}
|
|
|
|
bool MachineCycleInfoPrinterLegacy::runOnMachineFunction(MachineFunction &F) {
|
|
auto &CI = getAnalysis<MachineCycleInfoWrapperPass>();
|
|
CI.print(errs());
|
|
return false;
|
|
}
|
|
|
|
PreservedAnalyses
|
|
MachineCycleInfoPrinterPass::run(MachineFunction &MF,
|
|
MachineFunctionAnalysisManager &MFAM) {
|
|
auto &MCI = MFAM.getResult<MachineCycleAnalysis>(MF);
|
|
MCI.print(OS);
|
|
return PreservedAnalyses::all();
|
|
}
|
|
|
|
bool llvm::isCycleInvariant(const MachineCycle *Cycle, MachineInstr &I) {
|
|
MachineFunction *MF = I.getParent()->getParent();
|
|
MachineRegisterInfo *MRI = &MF->getRegInfo();
|
|
const TargetSubtargetInfo &ST = MF->getSubtarget();
|
|
const TargetRegisterInfo *TRI = ST.getRegisterInfo();
|
|
const TargetInstrInfo *TII = ST.getInstrInfo();
|
|
|
|
// The instruction is cycle invariant if all of its operands are.
|
|
for (const MachineOperand &MO : I.operands()) {
|
|
if (!MO.isReg())
|
|
continue;
|
|
|
|
Register Reg = MO.getReg();
|
|
if (Reg == 0)
|
|
continue;
|
|
|
|
// An instruction that uses or defines a physical register can't e.g. be
|
|
// hoisted, so mark this as not invariant.
|
|
if (Reg.isPhysical()) {
|
|
if (MO.isUse()) {
|
|
// If the physreg has no defs anywhere, it's just an ambient register
|
|
// and we can freely move its uses. Alternatively, if it's allocatable,
|
|
// it could get allocated to something with a def during allocation.
|
|
// However, if the physreg is known to always be caller saved/restored
|
|
// then this use is safe to hoist.
|
|
if (!MRI->isConstantPhysReg(Reg) &&
|
|
!(TRI->isCallerPreservedPhysReg(Reg.asMCReg(), *I.getMF())) &&
|
|
!TII->isIgnorableUse(MO))
|
|
return false;
|
|
// Otherwise it's safe to move.
|
|
continue;
|
|
} else if (!MO.isDead()) {
|
|
// A def that isn't dead can't be moved.
|
|
return false;
|
|
} else if (any_of(Cycle->getEntries(),
|
|
[&](const MachineBasicBlock *Block) {
|
|
return Block->isLiveIn(Reg);
|
|
})) {
|
|
// If the reg is live into any header of the cycle we can't hoist an
|
|
// instruction which would clobber it.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!MO.isUse())
|
|
continue;
|
|
|
|
assert(MRI->getVRegDef(Reg) && "Machine instr not mapped for this vreg?!");
|
|
|
|
// If the cycle contains the definition of an operand, then the instruction
|
|
// isn't cycle invariant.
|
|
if (Cycle->contains(MRI->getVRegDef(Reg)->getParent()))
|
|
return false;
|
|
}
|
|
|
|
// If we got this far, the instruction is cycle invariant!
|
|
return true;
|
|
}
|