1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
// SPDX-License-Identifier: GPL-3.0-or-later
#include "rcc/CodeGen/LiveInterval.h"
#include "LiveTestSupport.h"
#include "rcc/CodeGen/LiveIntervals.h"
#include "rcc/CodeGen/LiveVariables.h"
namespace {
using namespace rcc::test::liveness;
void testLiveRange(TestContext &Context) {
const rcc::SlotIndex S16 = rcc::SlotIndex::fromValue(16).registerSlot();
const rcc::SlotIndex S24 = rcc::SlotIndex::fromValue(24).registerSlot();
const rcc::SlotIndex S32 = rcc::SlotIndex::fromValue(32).registerSlot();
const rcc::SlotIndex S40 = rcc::SlotIndex::fromValue(40).registerSlot();
const rcc::SlotIndex S48 = rcc::SlotIndex::fromValue(48).registerSlot();
rcc::LiveRange Range;
auto First = Range.createValue(S16);
Context.expect(First.has_value(), "live value creation must succeed");
if (!First)
return;
Context.expect(Range.addSegment({S16, S32, *First}).has_value() &&
Range.addSegment({S32, S48, *First}).has_value() &&
Range.segments().size() == 1,
"adjacent same-value live segments must merge");
auto Second = Range.createValue(S40, rcc::LiveValueState::Redefined);
Context.expect(Second.has_value() && !Range.addSegment({S24, S40, *Second}),
"overlapping different live values must be rejected");
Context.expect(Range.valueAt(S24) != nullptr && Range.liveAt(S40) &&
Range.remove(S24, S40).has_value() && Range.segments().size() == 2 &&
Range.verify().has_value(),
"live-range lookup and removal must preserve ordered half-open segments");
rcc::LiveRange Shrunk;
auto Value = Shrunk.createValue(S16);
if (!Value) {
Context.expect(false, "shrink test value creation must succeed");
return;
}
const std::array Uses{S32};
Context.expect(Shrunk.addSegment({S16, S48.deadSlot(), *Value}).has_value() &&
Shrunk.shrinkToUses(Uses).has_value() && Shrunk.segments().size() == 1 &&
Shrunk.segments().front().End == S32.deadSlot(),
"shrinkToUses must retain exactly through the final use dead slot");
}
void testLaneSubrangesAndEarlyClobber(TestContext &Context, const rcc::TargetDescription &Target) {
auto Fixture = createFunction("lane-subranges", 3, 1);
if (!Fixture) {
Context.expect(false, "lane-subrange fixture creation must succeed");
return;
}
auto Reg = createVReg(*Fixture->Function);
auto Early = createVReg(*Fixture->Function);
if (!Reg || !Early) {
Context.expect(false, "lane-subrange registers must be created");
return;
}
auto LowDef =
buildDef(*Fixture->Function, Target, *Fixture->Blocks[0], *Reg, rcc::test::TestSubReg::Low32);
auto LowUse =
buildUse(*Fixture->Function, Target, *Fixture->Blocks[0], *Reg, rcc::test::TestSubReg::Low32);
auto HighDef = buildDef(*Fixture->Function, Target, *Fixture->Blocks[0], *Reg,
rcc::test::TestSubReg::High32);
auto HighUse = buildUse(*Fixture->Function, Target, *Fixture->Blocks[0], *Reg,
rcc::test::TestSubReg::High32);
auto EarlyDef = buildDef(*Fixture->Function, Target, *Fixture->Blocks[0], *Early, {}, true);
auto EarlyUse = buildUse(*Fixture->Function, Target, *Fixture->Blocks[0], *Early);
if (!LowDef || !LowUse || !HighDef || !HighUse || !EarlyDef || !EarlyUse) {
Context.expect(false, "lane-subrange instructions must be built");
return;
}
rcc::MachineAnalysisManager Analyses;
auto Registered = rcc::registerStage11MachineAnalyses(Analyses);
auto Intervals = Registered
? Analyses.getResult<rcc::LiveIntervalsAnalysis>(*Fixture->Function, Target)
: rcc::Expected<rcc::LiveIntervalsAnalysis::Result *>{
std::unexpected(Registered.error())};
if (!Intervals) {
Context.expect(false, "lane-subrange intervals must materialize");
return;
}
const rcc::LiveInterval *Interval = (*Intervals)->interval(*Reg);
const rcc::LiveInterval *EarlyInterval = (*Intervals)->interval(*Early);
const bool HasLow = Interval != nullptr &&
std::ranges::any_of(Interval->SubRanges, [](const rcc::LiveSubRange &Range) {
return Range.Lanes == rcc::LaneMask::fromRaw(0x3);
});
const bool HasHigh = Interval != nullptr &&
std::ranges::any_of(Interval->SubRanges, [](const rcc::LiveSubRange &Range) {
return Range.Lanes == rcc::LaneMask::fromRaw(0xC);
});
Context.expect(Interval != nullptr && Interval->SubRanges.size() == 2 && HasLow && HasHigh,
"partial virtual-register accesses must form disjoint lane subranges");
Context.expect(EarlyInterval != nullptr && !EarlyInterval->MainRange.values().empty() &&
EarlyInterval->MainRange.values().front().Definition.kind() ==
rcc::SlotIndexKind::EarlyClobber,
"early-clobber definitions must use the early-clobber slot");
}
} // namespace
int main() {
TestContext Context;
auto Target = createTarget();
Context.expect(Target.has_value(), "liveness TestTarget must be created");
if (!Target)
return Context.result();
testLiveRange(Context);
testLaneSubrangesAndEarlyClobber(Context, *Target);
return Context.result();
}
|