aboutsummaryrefslogtreecommitdiff
path: root/backend/unittests/CodeGen/RegAllocBasicTest.cpp
blob: 015fe37cb78d4c25a660e54afd9a1dee7227467a (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
// SPDX-License-Identifier: GPL-3.0-or-later

#include "CodeGen/RegisterAllocationTestSupport.h"

#include <iostream>

namespace {

using namespace rcc;
using namespace rcc::test;
using namespace rcc::test::ra;

[[nodiscard]] Expected<MachineInstr *> buildEarly(MachineFunction &Function,
                                                  const TargetDescription &Target,
                                                  MachineBasicBlock &Block, Register Destination,
                                                  Register Source) {
  MachineIRBuilder Builder(Function, Target);
  if (auto Result = Builder.setInsertPoint(Block); !Result)
    return std::unexpected(Result.error());
  auto Pending = Builder.buildInstr(testOpcode(EarlyOpcode));
  if (!Pending)
    return std::unexpected(Pending.error());
  RegisterOperandFlags Flags;
  Flags.set(RegisterOperandFlag::EarlyClobber);
  if (auto Result = Pending->addDef(Destination, Flags); !Result)
    return std::unexpected(Result.error());
  if (auto Result = Pending->addUse(Source); !Result)
    return std::unexpected(Result.error());
  return Pending->build();
}

void testHintsAndPressure(TestContext &Context, const TargetDescription &Target) {
  auto HintFixture = createFunction("basic-hint", 30);
  if (!HintFixture)
    return;
  auto HintValue = createVReg(*HintFixture->Function);
  if (!HintValue || !buildDef(*HintFixture->Function, Target, *HintFixture->Block, *HintValue, 1) ||
      !buildUse(*HintFixture->Function, Target, *HintFixture->Block, *HintValue) ||
      !HintFixture->Function->registerInfo().addAllocationHint(*HintValue,
                                                               physical(TestRegister::G1)) ||
      !prepareForAllocation(*HintFixture->Function, Target))
    return;
  TestRegisterAllocationInfo Info;
  auto Allocated = runAllocator(*HintFixture->Function, Target, Info, AllocatorKind::Basic);
  const MachineInstr *Use = HintFixture->Block->lastInstruction();
  Context.expect(Allocated && Use != nullptr &&
                     Use->operandAt(0)->registerValue() ==
                         Register::fromPhysical(physical(TestRegister::G1)),
                 "BasicAllocator must honor the first allocation hint");

  auto Pressure = createFunction("basic-pressure", 31);
  if (!Pressure)
    return;
  std::vector<Register> Values;
  for (std::int64_t Value = 0; Value < 3; ++Value) {
    auto Reg = createVReg(*Pressure->Function);
    if (!Reg || !buildDef(*Pressure->Function, Target, *Pressure->Block, *Reg, Value))
      return;
    Values.push_back(*Reg);
  }
  for (const Register Reg : Values)
    if (!buildUse(*Pressure->Function, Target, *Pressure->Block, Reg))
      return;
  if (!prepareForAllocation(*Pressure->Function, Target))
    return;
  Allocated = runAllocator(*Pressure->Function, Target, Info, AllocatorKind::Basic,
                           {.EnableRematerialization = false});
  Context.expect(Allocated &&
                     Pressure->Function->properties().has(MachineFunctionProperty::NoVRegs) &&
                     Pressure->Function->frameInfo().liveObjectCount() != 0,
                 "BasicAllocator must close spill/reload pressure");
}

void testEarlyClobberAndRewrite(TestContext &Context, const TargetDescription &Target) {
  auto Fixture = createFunction("basic-constraints", 32);
  if (!Fixture)
    return;
  auto Source = createVReg(*Fixture->Function);
  auto Destination = createVReg(*Fixture->Function);
  if (!Source || !Destination ||
      !buildDef(*Fixture->Function, Target, *Fixture->Block, *Source, 1) ||
      !buildEarly(*Fixture->Function, Target, *Fixture->Block, *Destination, *Source) ||
      !buildTied(*Fixture->Function, Target, *Fixture->Block, *Destination) ||
      !buildUse(*Fixture->Function, Target, *Fixture->Block, *Destination) ||
      !prepareForAllocation(*Fixture->Function, Target))
    return;
  TestRegisterAllocationInfo Info;
  auto Allocated = runAllocator(*Fixture->Function, Target, Info, AllocatorKind::Basic);
  const MachineInstr *Early = Fixture->Block->firstInstruction()->next();
  const MachineInstr *Tied = Early == nullptr ? nullptr : Early->next();
  Context.expect(Allocated && Early != nullptr && Tied != nullptr &&
                     Early->operandAt(0)->registerValue() != Early->operandAt(1)->registerValue() &&
                     Tied->operandAt(0)->registerValue() == Tied->operandAt(1)->registerValue(),
                 "BasicAllocator must preserve early-clobber and tied constraints");
}

} // namespace

int main() {
  TestContext Context;
  auto Target = createTarget();
  Context.expect(Target.has_value(), "BasicAllocator test target must be constructible");
  if (!Target)
    return Context.result();
  testHintsAndPressure(Context, *Target);
  testEarlyClobberAndRewrite(Context, *Target);
  return Context.result();
}