[LLVM] Add HasFakeUses to MachineFunction (#110097)

Following the addition of the llvm.fake.use intrinsic and corresponding
MIR instruction, two further changes are planned: to add an
-fextend-lifetimes flag to Clang that emits these intrinsics, and to
have -Og enable this flag by default. Currently, some logic for handling
fake uses is gated by the optdebug attribute, which is intended to be
switched on by -fextend-lifetimes (and by extension -Og later on).
However, the decision was made that a general optdebug attribute should
be incompatible with other opt_ attributes (e.g. optsize, optnone),
since they all express different intents for how to optimize the
program. We would still like to allow -fextend-lifetimes with optsize
however (i.e. -Os -fextend-lifetimes should be legal), since it may be a
useful configuration and there is no technical reason to not allow it.

This patch resolves this by tracking MachineFunctions that have fake
uses, allowing us to run passes that interact with them and skip passes
that clash with them.
This commit is contained in:
Stephen Tozer
2024-10-04 13:13:30 +01:00
committed by GitHub
parent 6294679faa
commit d826b0c90f
11 changed files with 76 additions and 8 deletions

View File

@@ -734,6 +734,7 @@ struct MachineFunction {
std::optional<bool> NoPHIs;
std::optional<bool> IsSSA;
std::optional<bool> NoVRegs;
std::optional<bool> HasFakeUses;
bool CallsEHReturn = false;
bool CallsUnwindInit = false;
@@ -780,6 +781,7 @@ template <> struct MappingTraits<MachineFunction> {
YamlIO.mapOptional("noPhis", MF.NoPHIs, std::optional<bool>());
YamlIO.mapOptional("isSSA", MF.IsSSA, std::optional<bool>());
YamlIO.mapOptional("noVRegs", MF.NoVRegs, std::optional<bool>());
YamlIO.mapOptional("hasFakeUses", MF.HasFakeUses, std::optional<bool>());
YamlIO.mapOptional("callsEHReturn", MF.CallsEHReturn, false);
YamlIO.mapOptional("callsUnwindInit", MF.CallsUnwindInit, false);

View File

@@ -187,6 +187,7 @@ public:
TiedOpsRewritten,
FailsVerification,
TracksDebugUserValues,
HasFakeUses,
LastProperty = TracksDebugUserValues,
};
@@ -376,6 +377,7 @@ class LLVM_ABI MachineFunction {
bool HasEHCatchret = false;
bool HasEHScopes = false;
bool HasEHFunclets = false;
bool HasFakeUses = false;
bool IsOutlined = false;
/// BBID to assign to the next basic block of this function.
@@ -1195,6 +1197,9 @@ public:
bool hasEHFunclets() const { return HasEHFunclets; }
void setHasEHFunclets(bool V) { HasEHFunclets = V; }
bool hasFakeUses() const { return HasFakeUses; }
void setHasFakeUses(bool V) { HasFakeUses = V; }
bool isOutlined() const { return IsOutlined; }
void setIsOutlined(bool V) { IsOutlined = V; }

View File

@@ -2201,6 +2201,7 @@ bool IRTranslator::translateKnownIntrinsic(const CallInst &CI, Intrinsic::ID ID,
for (auto VReg : getOrCreateVRegs(*Arg))
VRegs.push_back(VReg);
MIRBuilder.buildInstr(TargetOpcode::FAKE_USE, {}, VRegs);
MF->setHasFakeUses(true);
return true;
}
case Intrinsic::dbg_declare: {

View File

@@ -380,6 +380,7 @@ bool MIRParserImpl::computeFunctionProperties(
bool HasPHI = false;
bool HasInlineAsm = false;
bool HasFakeUses = false;
bool AllTiedOpsRewritten = true, HasTiedOps = false;
for (const MachineBasicBlock &MBB : MF) {
for (const MachineInstr &MI : MBB) {
@@ -387,6 +388,8 @@ bool MIRParserImpl::computeFunctionProperties(
HasPHI = true;
if (MI.isInlineAsm())
HasInlineAsm = true;
if (MI.isFakeUse())
HasFakeUses = true;
for (unsigned I = 0; I < MI.getNumOperands(); ++I) {
const MachineOperand &MO = MI.getOperand(I);
if (!MO.isReg() || !MO.getReg())
@@ -441,6 +444,16 @@ bool MIRParserImpl::computeFunctionProperties(
" has explicit property NoVRegs, but contains virtual registers");
}
// For hasFakeUses we follow similar logic to the ComputedPropertyHelper,
// except for caring about the inverse case only, i.e. when the property is
// explicitly set to false and Fake Uses are present; having HasFakeUses=true
// on a function without fake uses is harmless.
if (YamlMF.HasFakeUses && !*YamlMF.HasFakeUses && HasFakeUses)
return error(
MF.getName() +
" has explicit property hasFakeUses=false, but contains fake uses");
MF.setHasFakeUses(YamlMF.HasFakeUses.value_or(HasFakeUses));
return false;
}

View File

@@ -207,6 +207,7 @@ void MIRPrinter::print(const MachineFunction &MF) {
YamlMF.HasEHCatchret = MF.hasEHCatchret();
YamlMF.HasEHScopes = MF.hasEHScopes();
YamlMF.HasEHFunclets = MF.hasEHFunclets();
YamlMF.HasFakeUses = MF.hasFakeUses();
YamlMF.IsOutlined = MF.isOutlined();
YamlMF.UseDebugInstrRef = MF.useDebugInstrRef();

View File

@@ -74,10 +74,8 @@ INITIALIZE_PASS_END(RemoveLoadsIntoFakeUses, DEBUG_TYPE,
"Remove Loads Into Fake Uses", false, false)
bool RemoveLoadsIntoFakeUses::runOnMachineFunction(MachineFunction &MF) {
// Only `optdebug` functions should contain FAKE_USEs, so don't try to run
// this for other functions.
if (!MF.getFunction().hasFnAttribute(Attribute::OptimizeForDebugging) ||
skipFunction(MF.getFunction()))
// Only run this for functions that have fake uses.
if (!MF.hasFakeUses() || skipFunction(MF.getFunction()))
return false;
bool AnyChanges = false;

View File

@@ -31,6 +31,7 @@
#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
@@ -200,12 +201,22 @@ void FunctionLoweringInfo::set(const Function &fn, MachineFunction &mf,
}
}
}
// Look for calls to the @llvm.va_start intrinsic. We can omit some
// prologue boilerplate for variadic functions that don't examine their
// arguments.
if (const auto *II = dyn_cast<IntrinsicInst>(&I)) {
if (II->getIntrinsicID() == Intrinsic::vastart)
switch (II->getIntrinsicID()) {
case Intrinsic::vastart:
// Look for calls to the @llvm.va_start intrinsic. We can omit
// some prologue boilerplate for variadic functions that don't
// examine their arguments.
MF->getFrameInfo().setHasVAStart(true);
break;
case Intrinsic::fake_use:
// Look for llvm.fake.uses, so that we can remove loads into fake
// uses later if necessary.
MF->setHasFakeUses(true);
break;
default:
break;
}
}
// If we have a musttail call in a variadic function, we need to ensure

View File

@@ -0,0 +1,14 @@
# RUN: not llc -run-pass none -o /dev/null %s 2>&1 | FileCheck %s
# Test that computed properties are not conflicting with explicitly set
# properties
---
# CHECK: error: {{.*}}: TestHasFakeUsesOverrideConflict has explicit property hasFakeUses=false, but contains fake uses
name: TestHasFakeUsesOverrideConflict
hasFakeUses: false
body: |
bb.0:
%0:_(s32) = G_IMPLICIT_DEF
FAKE_USE %0
...

View File

@@ -62,3 +62,23 @@ noVRegs: false
name: TestNoVRegsOverrideTrue
noVRegs: true
...
---
# CHECK-LABEL: name: TestHasFakeUses
# CHECK: hasFakeUses: false
# CHECK: ...
name: TestHasFakeUses
...
---
# CHECK-LABEL: name: TestHasFakeUsesOverride
# CHECK: hasFakeUses: false
# CHECK: ...
name: TestHasFakeUsesOverride
hasFakeUses: false
...
---
# CHECK-LABEL: name: TestHasFakeUsesOverrideTrue
# CHECK: hasFakeUses: true
# CHECK: ...
name: TestHasFakeUsesOverrideTrue
hasFakeUses: true
...

View File

@@ -17,6 +17,7 @@
# RESULT-NEXT: noPhis: false
# RESULT-NEXT: isSSA: false
# RESULT-NEXT: noVRegs: false
# RESULT-NEXT: hasFakeUses: true
# RESULT-NEXT: callsEHReturn: true
# RESULT-NEXT: callsUnwindInit: true
# RESULT-NEXT: hasEHCatchret: true
@@ -47,6 +48,7 @@ hasWinCFI: true
noPhis: false
isSSA: false
noVRegs: false
hasFakeUses: true
failsVerification: true
tracksDebugUserValues: true
callsEHReturn: true

View File

@@ -403,6 +403,7 @@ static std::unique_ptr<MachineFunction> cloneMF(MachineFunction *SrcMF,
DstMF->setHasEHCatchret(SrcMF->hasEHCatchret());
DstMF->setHasEHScopes(SrcMF->hasEHScopes());
DstMF->setHasEHFunclets(SrcMF->hasEHFunclets());
DstMF->setHasFakeUses(SrcMF->hasFakeUses());
DstMF->setIsOutlined(SrcMF->isOutlined());
if (!SrcMF->getLandingPads().empty() ||