File: Utilities.h

package info (click to toggle)
llvm-toolchain-16 1%3A16.0.6-15~deb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,634,792 kB
  • sloc: cpp: 6,179,261; ansic: 1,216,205; asm: 741,319; python: 196,614; objc: 75,325; f90: 49,640; lisp: 32,396; pascal: 12,286; sh: 9,394; perl: 7,442; ml: 5,494; awk: 3,523; makefile: 2,723; javascript: 1,206; xml: 886; fortran: 581; cs: 573
file content (255 lines) | stat: -rw-r--r-- 8,599 bytes parent folder | download | duplicates (4)
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
//===------- Utilities.h - Target independent OpenMP target RTL -- C++ ----===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Routines and classes used to provide useful functionalities like string
// parsing and environment variables.
//
//===----------------------------------------------------------------------===//

#ifndef OPENMP_LIBOMPTARGET_INCLUDE_UTILITIES_H
#define OPENMP_LIBOMPTARGET_INCLUDE_UTILITIES_H

#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"

#include "Debug.h"

#include <algorithm>
#include <atomic>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <functional>
#include <limits>
#include <memory>
#include <sstream>
#include <string>

namespace llvm {
namespace omp {
namespace target {

/// Utility class for parsing strings to other types.
struct StringParser {
  /// Parse a string to another type.
  template <typename Ty> static bool parse(const char *Value, Ty &Result);
};

/// Class for reading and checking environment variables. Currently working with
/// integer, floats, std::string and bool types.
template <typename Ty> class Envar {
  Ty Data;
  bool IsPresent;
  bool Initialized;

public:
  /// Auxiliary function to safely create envars. This static function safely
  /// creates envars using fallible constructors. See the constructors to know
  /// more details about the creation parameters.
  template <typename... ArgsTy>
  static Expected<Envar> create(ArgsTy &&...Args) {
    Error Err = Error::success();
    Envar Envar(std::forward<ArgsTy>(Args)..., Err);
    if (Err)
      return std::move(Err);
    return std::move(Envar);
  }

  /// Create an empty envar. Cannot be consulted. This constructor is merely
  /// for convenience. This constructor is not fallible.
  Envar() : Data(Ty()), IsPresent(false), Initialized(false) {}

  /// Create an envar with a name and an optional default. The Envar object will
  /// take the value read from the environment variable, or the default if it
  /// was not set or not correct. This constructor is not fallible.
  Envar(StringRef Name, Ty Default = Ty())
      : Data(Default), IsPresent(false), Initialized(true) {

    if (const char *EnvStr = getenv(Name.data())) {
      // Check whether the envar is defined and valid.
      IsPresent = StringParser::parse<Ty>(EnvStr, Data);

      if (!IsPresent) {
        DP("Ignoring invalid value %s for envar %s\n", EnvStr, Name.data());
        Data = Default;
      }
    }
  }

  /// Get the definitive value.
  const Ty &get() const {
    // Throw a runtime error in case this envar is not initialized.
    if (!Initialized)
      FATAL_MESSAGE0(1, "Consulting envar before initialization");

    return Data;
  }

  /// Get the definitive value.
  operator Ty() const { return get(); }

  /// Indicate whether the environment variable was defined and valid.
  bool isPresent() const { return IsPresent; }

private:
  /// This constructor should never fail but we provide it for convenience. This
  /// way, the constructor can be used by the Envar::create() static function
  /// to safely create this kind of envars.
  Envar(StringRef Name, Ty Default, Error &Err) : Envar(Name, Default) {
    ErrorAsOutParameter EAO(&Err);
    Err = Error::success();
  }

  /// Create an envar with a name, getter function and a setter function. The
  /// Envar object will take the value read from the environment variable if
  /// this value is accepted by the setter function. Otherwise, the getter
  /// function will be executed to get the default value. The getter should be
  /// of the form Error GetterFunctionTy(Ty &Value) and the setter should
  /// be of the form Error SetterFunctionTy(Ty Value). This constructor has a
  /// private visibility because is a fallible constructor. Please use the
  /// Envar::create() static function to safely create this object instead.
  template <typename GetterFunctor, typename SetterFunctor>
  Envar(StringRef Name, GetterFunctor Getter, SetterFunctor Setter, Error &Err)
      : Data(Ty()), IsPresent(false), Initialized(true) {
    ErrorAsOutParameter EAO(&Err);
    Err = init(Name, Getter, Setter);
  }

  template <typename GetterFunctor, typename SetterFunctor>
  Error init(StringRef Name, GetterFunctor Getter, SetterFunctor Setter);
};

/// Define some common envar types.
using IntEnvar = Envar<int>;
using Int32Envar = Envar<int32_t>;
using Int64Envar = Envar<int64_t>;
using UInt32Envar = Envar<uint32_t>;
using UInt64Envar = Envar<uint64_t>;
using StringEnvar = Envar<std::string>;
using BoolEnvar = Envar<bool>;

/// Utility class for thread-safe reference counting. Any class that needs
/// objects' reference counting can inherit from this entity or have it as a
/// class data member.
template <typename Ty = uint32_t,
          std::memory_order MemoryOrder = std::memory_order_relaxed>
struct RefCountTy {
  /// Create a refcount object initialized to zero.
  RefCountTy() : Refs(0) {}

  ~RefCountTy() { assert(Refs == 0 && "Destroying with non-zero refcount"); }

  /// Increase the reference count atomically.
  void increase() { Refs.fetch_add(1, MemoryOrder); }

  /// Decrease the reference count and return whether it became zero. Decreasing
  /// the counter in more units than it was previously increased results in
  /// undefined behavior.
  bool decrease() {
    Ty Prev = Refs.fetch_sub(1, MemoryOrder);
    assert(Prev > 0 && "Invalid refcount");
    return (Prev == 1);
  }

  Ty get() const { return Refs.load(MemoryOrder); }

private:
  /// The atomic reference counter.
  std::atomic<Ty> Refs;
};

template <>
inline bool StringParser::parse(const char *ValueStr, bool &Result) {
  std::string Value(ValueStr);

  // Convert the string to lowercase.
  std::transform(Value.begin(), Value.end(), Value.begin(),
                 [](unsigned char c) { return std::tolower(c); });

  // May be implemented with fancier C++ features, but let's keep it simple.
  if (Value == "true" || Value == "yes" || Value == "on" || Value == "1")
    Result = true;
  else if (Value == "false" || Value == "no" || Value == "off" || Value == "0")
    Result = false;
  else
    return false;

  // Parsed correctly.
  return true;
}

template <typename Ty>
inline bool StringParser::parse(const char *Value, Ty &Result) {
  assert(Value && "Parsed value cannot be null");

  std::istringstream Stream(Value);
  Stream >> Result;

  return !Stream.fail();
}

template <typename Ty>
template <typename GetterFunctor, typename SetterFunctor>
inline Error Envar<Ty>::init(StringRef Name, GetterFunctor Getter,
                             SetterFunctor Setter) {
  // Get the default value.
  Ty Default;
  if (Error Err = Getter(Default))
    return Err;

  if (const char *EnvStr = getenv(Name.data())) {
    IsPresent = StringParser::parse<Ty>(EnvStr, Data);
    if (IsPresent) {
      // Check whether the envar value is actually valid.
      Error Err = Setter(Data);
      if (Err) {
        // The setter reported an invalid value. Mark the user-defined value as
        // not present and reset to the getter value (default).
        IsPresent = false;
        Data = Default;
        DP("Setter of envar %s failed, resetting to %s\n", Name.data(),
           std::to_string(Data).data());
        consumeError(std::move(Err));
      }
    } else {
      DP("Ignoring invalid value %s for envar %s\n", EnvStr, Name.data());
      Data = Default;
    }
  } else {
    Data = Default;
  }

  return Error::success();
}

/// Return the difference (in bytes) between \p Begin and \p End.
template <typename Ty = char>
ptrdiff_t getPtrDiff(const void *End, const void *Begin) {
  return reinterpret_cast<const Ty *>(End) -
         reinterpret_cast<const Ty *>(Begin);
}

/// Return \p Ptr advanced by \p Offset bytes.
template <typename Ty> Ty *advanceVoidPtr(Ty *Ptr, int64_t Offset) {
  static_assert(std::is_void<Ty>::value);
  return const_cast<char *>(reinterpret_cast<const char *>(Ptr) + Offset);
}

/// Return \p Ptr aligned to \p Alignment bytes.
template <typename Ty> Ty *alignPtr(Ty *Ptr, int64_t Alignment) {
  size_t Space = std::numeric_limits<size_t>::max();
  return std::align(Alignment, sizeof(char), Ptr, Space);
}

} // namespace target
} // namespace omp
} // namespace llvm

#endif // OPENMP_LIBOMPTARGET_INCLUDE_UTILITIES_H