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).
219 lines
7.7 KiB
C++
219 lines
7.7 KiB
C++
//===-- User.cpp - Implement the User class -------------------------------===//
|
|
//
|
|
// 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/IR/User.h"
|
|
#include "llvm/IR/Constant.h"
|
|
#include "llvm/IR/GlobalValue.h"
|
|
#include "llvm/IR/IntrinsicInst.h"
|
|
|
|
namespace llvm {
|
|
class BasicBlock;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// User Class
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
bool User::replaceUsesOfWith(Value *From, Value *To) {
|
|
bool Changed = false;
|
|
if (From == To) return Changed; // Duh what?
|
|
|
|
assert((!isa<Constant>(this) || isa<GlobalValue>(this)) &&
|
|
"Cannot call User::replaceUsesOfWith on a constant!");
|
|
|
|
for (unsigned i = 0, E = getNumOperands(); i != E; ++i)
|
|
if (getOperand(i) == From) { // Is This operand is pointing to oldval?
|
|
// The side effects of this setOperand call include linking to
|
|
// "To", adding "this" to the uses list of To, and
|
|
// most importantly, removing "this" from the use list of "From".
|
|
setOperand(i, To);
|
|
Changed = true;
|
|
}
|
|
if (auto DVI = dyn_cast_or_null<DbgVariableIntrinsic>(this)) {
|
|
if (is_contained(DVI->location_ops(), From)) {
|
|
DVI->replaceVariableLocationOp(From, To);
|
|
Changed = true;
|
|
}
|
|
}
|
|
|
|
return Changed;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// User allocHungoffUses Implementation
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
void User::allocHungoffUses(unsigned N, bool IsPhi) {
|
|
assert(HasHungOffUses && "alloc must have hung off uses");
|
|
|
|
static_assert(alignof(Use) >= alignof(BasicBlock *),
|
|
"Alignment is insufficient for 'hung-off-uses' pieces");
|
|
|
|
// Allocate the array of Uses
|
|
size_t size = N * sizeof(Use);
|
|
if (IsPhi)
|
|
size += N * sizeof(BasicBlock *);
|
|
Use *Begin = static_cast<Use*>(::operator new(size));
|
|
Use *End = Begin + N;
|
|
setOperandList(Begin);
|
|
for (; Begin != End; Begin++)
|
|
new (Begin) Use(this);
|
|
}
|
|
|
|
void User::growHungoffUses(unsigned NewNumUses, bool IsPhi) {
|
|
assert(HasHungOffUses && "realloc must have hung off uses");
|
|
|
|
unsigned OldNumUses = getNumOperands();
|
|
|
|
// We don't support shrinking the number of uses. We wouldn't have enough
|
|
// space to copy the old uses in to the new space.
|
|
assert(NewNumUses > OldNumUses && "realloc must grow num uses");
|
|
|
|
Use *OldOps = getOperandList();
|
|
allocHungoffUses(NewNumUses, IsPhi);
|
|
Use *NewOps = getOperandList();
|
|
|
|
// Now copy from the old operands list to the new one.
|
|
std::copy(OldOps, OldOps + OldNumUses, NewOps);
|
|
|
|
// If this is a Phi, then we need to copy the BB pointers too.
|
|
if (IsPhi) {
|
|
auto *OldPtr = reinterpret_cast<char *>(OldOps + OldNumUses);
|
|
auto *NewPtr = reinterpret_cast<char *>(NewOps + NewNumUses);
|
|
std::copy(OldPtr, OldPtr + (OldNumUses * sizeof(BasicBlock *)), NewPtr);
|
|
}
|
|
Use::zap(OldOps, OldOps + OldNumUses, true);
|
|
}
|
|
|
|
|
|
// This is a private struct used by `User` to track the co-allocated descriptor
|
|
// section.
|
|
struct DescriptorInfo {
|
|
intptr_t SizeInBytes;
|
|
};
|
|
|
|
ArrayRef<const uint8_t> User::getDescriptor() const {
|
|
auto MutableARef = const_cast<User *>(this)->getDescriptor();
|
|
return {MutableARef.begin(), MutableARef.end()};
|
|
}
|
|
|
|
MutableArrayRef<uint8_t> User::getDescriptor() {
|
|
assert(HasDescriptor && "Don't call otherwise!");
|
|
assert(!HasHungOffUses && "Invariant!");
|
|
|
|
auto *DI = reinterpret_cast<DescriptorInfo *>(getIntrusiveOperands()) - 1;
|
|
assert(DI->SizeInBytes != 0 && "Should not have had a descriptor otherwise!");
|
|
|
|
return MutableArrayRef<uint8_t>(
|
|
reinterpret_cast<uint8_t *>(DI) - DI->SizeInBytes, DI->SizeInBytes);
|
|
}
|
|
|
|
bool User::isDroppable() const {
|
|
if (auto *II = dyn_cast<IntrinsicInst>(this)) {
|
|
switch (II->getIntrinsicID()) {
|
|
default:
|
|
return false;
|
|
case Intrinsic::assume:
|
|
case Intrinsic::pseudoprobe:
|
|
case Intrinsic::experimental_noalias_scope_decl:
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// User operator new Implementations
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
void *User::allocateFixedOperandUser(size_t Size, unsigned Us,
|
|
unsigned DescBytes) {
|
|
assert(Us < (1u << NumUserOperandsBits) && "Too many operands");
|
|
|
|
static_assert(sizeof(DescriptorInfo) % sizeof(void *) == 0, "Required below");
|
|
|
|
unsigned DescBytesToAllocate =
|
|
DescBytes == 0 ? 0 : (DescBytes + sizeof(DescriptorInfo));
|
|
assert(DescBytesToAllocate % sizeof(void *) == 0 &&
|
|
"We need this to satisfy alignment constraints for Uses");
|
|
|
|
uint8_t *Storage = static_cast<uint8_t *>(
|
|
::operator new(Size + sizeof(Use) * Us + DescBytesToAllocate));
|
|
Use *Start = reinterpret_cast<Use *>(Storage + DescBytesToAllocate);
|
|
Use *End = Start + Us;
|
|
User *Obj = reinterpret_cast<User *>(End);
|
|
Obj->NumUserOperands = Us;
|
|
Obj->HasHungOffUses = false;
|
|
Obj->HasDescriptor = DescBytes != 0;
|
|
for (; Start != End; Start++)
|
|
new (Start) Use(Obj);
|
|
|
|
if (DescBytes != 0) {
|
|
auto *DescInfo = reinterpret_cast<DescriptorInfo *>(Storage + DescBytes);
|
|
DescInfo->SizeInBytes = DescBytes;
|
|
}
|
|
|
|
return Obj;
|
|
}
|
|
|
|
void *User::operator new(size_t Size, IntrusiveOperandsAllocMarker allocTrait) {
|
|
return allocateFixedOperandUser(Size, allocTrait.NumOps, 0);
|
|
}
|
|
|
|
void *User::operator new(size_t Size,
|
|
IntrusiveOperandsAndDescriptorAllocMarker allocTrait) {
|
|
return allocateFixedOperandUser(Size, allocTrait.NumOps,
|
|
allocTrait.DescBytes);
|
|
}
|
|
|
|
void *User::operator new(size_t Size, HungOffOperandsAllocMarker) {
|
|
// Allocate space for a single Use*
|
|
void *Storage = ::operator new(Size + sizeof(Use *));
|
|
Use **HungOffOperandList = static_cast<Use **>(Storage);
|
|
User *Obj = reinterpret_cast<User *>(HungOffOperandList + 1);
|
|
Obj->NumUserOperands = 0;
|
|
Obj->HasHungOffUses = true;
|
|
Obj->HasDescriptor = false;
|
|
*HungOffOperandList = nullptr;
|
|
return Obj;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// User operator delete Implementation
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Repress memory sanitization, due to use-after-destroy by operator
|
|
// delete. Bug report 24578 identifies this issue.
|
|
LLVM_NO_SANITIZE_MEMORY_ATTRIBUTE void User::operator delete(void *Usr) {
|
|
// Hung off uses use a single Use* before the User, while other subclasses
|
|
// use a Use[] allocated prior to the user.
|
|
User *Obj = static_cast<User *>(Usr);
|
|
if (Obj->HasHungOffUses) {
|
|
assert(!Obj->HasDescriptor && "not supported!");
|
|
|
|
Use **HungOffOperandList = static_cast<Use **>(Usr) - 1;
|
|
// drop the hung off uses.
|
|
Use::zap(*HungOffOperandList, *HungOffOperandList + Obj->NumUserOperands,
|
|
/* Delete */ true);
|
|
::operator delete(HungOffOperandList);
|
|
} else if (Obj->HasDescriptor) {
|
|
Use *UseBegin = static_cast<Use *>(Usr) - Obj->NumUserOperands;
|
|
Use::zap(UseBegin, UseBegin + Obj->NumUserOperands, /* Delete */ false);
|
|
|
|
auto *DI = reinterpret_cast<DescriptorInfo *>(UseBegin) - 1;
|
|
uint8_t *Storage = reinterpret_cast<uint8_t *>(DI) - DI->SizeInBytes;
|
|
::operator delete(Storage);
|
|
} else {
|
|
Use *Storage = static_cast<Use *>(Usr) - Obj->NumUserOperands;
|
|
Use::zap(Storage, Storage + Obj->NumUserOperands,
|
|
/* Delete */ false);
|
|
::operator delete(Storage);
|
|
}
|
|
}
|
|
|
|
} // namespace llvm
|