aboutsummaryrefslogtreecommitdiff
path: root/backend/unittests/CodeGen/LiveIntervalTest.cpp
blob: 34ebd76df273ab5327838fed9f8bfaa9ab6b539a (plain)
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::MachineFunctionAnalysisManager 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();
}