aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Frontend/HLSL/HLSLBinding.cpp
blob: d581311f22028f351567845295424e4441bd4e06 (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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
//===- HLSLBinding.cpp - Representation for resource bindings in HLSL -----===//
//
// 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/Frontend/HLSL/HLSLBinding.h"
#include "llvm/ADT/STLExtras.h"

using namespace llvm;
using namespace hlsl;

std::optional<uint32_t>
BindingInfo::findAvailableBinding(dxil::ResourceClass RC, uint32_t Space,
                                  int32_t Size) {
  BindingSpaces &BS = getBindingSpaces(RC);
  RegisterSpace &RS = BS.getOrInsertSpace(Space);
  return RS.findAvailableBinding(Size);
}

BindingInfo::RegisterSpace &
BindingInfo::BindingSpaces::getOrInsertSpace(uint32_t Space) {
  for (auto It = Spaces.begin(), End = Spaces.end(); It != End; ++It) {
    if (It->Space == Space)
      return *It;
    if (It->Space < Space)
      continue;
    return *Spaces.insert(It, Space);
  }
  return Spaces.emplace_back(Space);
}

std::optional<uint32_t>
BindingInfo::RegisterSpace::findAvailableBinding(int32_t Size) {
  assert((Size == -1 || Size > 0) && "invalid size");

  if (FreeRanges.empty())
    return std::nullopt;

  // unbounded array
  if (Size == -1) {
    BindingRange &Last = FreeRanges.back();
    if (Last.UpperBound != ~0u)
      // this space is already occupied by an unbounded array
      return std::nullopt;
    uint32_t RegSlot = Last.LowerBound;
    FreeRanges.pop_back();
    return RegSlot;
  }

  // single resource or fixed-size array
  for (BindingRange &R : FreeRanges) {
    // compare the size as uint64_t to prevent overflow for range (0, ~0u)
    if ((uint64_t)R.UpperBound - R.LowerBound + 1 < (uint64_t)Size)
      continue;
    uint32_t RegSlot = R.LowerBound;
    // This might create a range where (LowerBound == UpperBound + 1). When
    // that happens, the next time this function is called the range will
    // skipped over by the check above (at this point Size is always > 0).
    R.LowerBound += Size;
    return RegSlot;
  }

  return std::nullopt;
}

BindingInfo BindingInfoBuilder::calculateBindingInfo(
    llvm::function_ref<void(const BindingInfoBuilder &Builder,
                            const Binding &Overlapping)>
        ReportOverlap) {
  // sort all the collected bindings
  llvm::stable_sort(Bindings);

  // remove duplicates
  Binding *NewEnd = llvm::unique(Bindings);
  if (NewEnd != Bindings.end())
    Bindings.erase(NewEnd);

  BindingInfo Info;

  // Go over the sorted bindings and build up lists of free register ranges
  // for each binding type and used spaces. Bindings are sorted by resource
  // class, space, and lower bound register slot.
  BindingInfo::BindingSpaces *BS =
      &Info.getBindingSpaces(dxil::ResourceClass::SRV);
  for (const Binding &B : Bindings) {
    if (BS->RC != B.RC)
      // move to the next resource class spaces
      BS = &Info.getBindingSpaces(B.RC);

    BindingInfo::RegisterSpace *S = BS->Spaces.empty()
                                        ? &BS->Spaces.emplace_back(B.Space)
                                        : &BS->Spaces.back();
    assert(S->Space <= B.Space && "bindings not sorted correctly?");
    if (B.Space != S->Space)
      // add new space
      S = &BS->Spaces.emplace_back(B.Space);

    // The space is full - there are no free slots left, or the rest of the
    // slots are taken by an unbounded array. Report the overlapping to the
    // caller.
    if (S->FreeRanges.empty() || S->FreeRanges.back().UpperBound < ~0u) {
      ReportOverlap(*this, B);
      continue;
    }
    // adjust the last free range lower bound, split it in two, or remove it
    BindingInfo::BindingRange &LastFreeRange = S->FreeRanges.back();
    if (LastFreeRange.LowerBound == B.LowerBound) {
      if (B.UpperBound < ~0u)
        LastFreeRange.LowerBound = B.UpperBound + 1;
      else
        S->FreeRanges.pop_back();
    } else if (LastFreeRange.LowerBound < B.LowerBound) {
      LastFreeRange.UpperBound = B.LowerBound - 1;
      if (B.UpperBound < ~0u)
        S->FreeRanges.emplace_back(B.UpperBound + 1, ~0u);
    } else {
      // We don't have room here. Report the overlapping binding to the caller
      // and mark any extra space this binding would use as unavailable.
      ReportOverlap(*this, B);
      if (B.UpperBound < ~0u)
        LastFreeRange.LowerBound =
            std::max(LastFreeRange.LowerBound, B.UpperBound + 1);
      else
        S->FreeRanges.pop_back();
    }
  }

  return Info;
}

const BindingInfoBuilder::Binding &BindingInfoBuilder::findOverlapping(
    const BindingInfoBuilder::Binding &ReportedBinding) const {
  for (const BindingInfoBuilder::Binding &Other : Bindings)
    if (ReportedBinding.LowerBound <= Other.UpperBound &&
        Other.LowerBound <= ReportedBinding.UpperBound)
      return Other;

  llvm_unreachable("Searching for overlap for binding that does not overlap");
}