Jit/util.h (146 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
#pragma once
#include "Python.h"
#include <cstdint>
#include <limits>
#include <type_traits>
#ifdef __cplusplus
#include "Jit/log.h"
#include <cstdarg>
#include <cstddef>
#include <memory>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#define ARRAYSIZE(x) (sizeof(x) / sizeof((x)[0]))
#define DISALLOW_COPY_AND_ASSIGN(klass) \
klass(const klass&) = delete; \
klass& operator=(const klass&) = delete
#define UNUSED __attribute__((unused))
extern "C" {
#endif
struct jit_string_t* ss_alloc(void);
void ss_free(struct jit_string_t* ss);
void ss_reset(struct jit_string_t* ss);
int ss_is_empty(const struct jit_string_t* ss);
const char* ss_get_string(const struct jit_string_t* ss);
int ss_vsprintf(struct jit_string_t* ss, const char* format, va_list args);
int ss_sprintf(struct jit_string_t* ss, const char* format, ...);
struct jit_string_t* ss_sprintf_alloc(const char* format, ...);
#ifdef __cplusplus
}
const bool py_debug =
#ifdef Py_DEBUG
true;
#else
false;
#endif
struct jit_string_deleter {
void operator()(jit_string_t* ss) const {
ss_free(ss);
}
};
using auto_jit_string_t = std::unique_ptr<jit_string_t, jit_string_deleter>;
const char* ss_get_string(const auto_jit_string_t& ss);
namespace jit {
const int kPointerSize = sizeof(void*);
const int kCoFlagsAnyGenerator =
CO_ASYNC_GENERATOR | CO_COROUTINE | CO_GENERATOR | CO_ITERABLE_COROUTINE;
// If stable pointers are enabled (with a call to setUseStablePointers(true))
// return 0xdeadbeef. Otherwise, return the original pointer.
const void* getStablePointer(const void* ptr);
// Enable or disable pointer sanitization.
void setUseStablePointers(bool enable);
inline std::size_t combineHash(std::size_t seed, std::size_t hash) {
return seed ^ (hash + 0x9e3779b9 + (seed << 6) + (seed >> 2));
}
std::string codeFullname(PyObject* module, PyCodeObject* code);
std::string funcFullname(PyFunctionObject* func);
// When possible, return the fully qualified name of the given type (including
// its module). Falls back to the type's bare name.
std::string typeFullname(PyTypeObject* type);
// Return the given PyUnicodeObject as a std::string, or "" if an error occurs.
std::string unicodeAsString(PyObject* str);
// Given a code object and an index into f_localsplus, compute which of
// code->co_varnames, code->cellvars, or code->freevars contains the name of
// the variable. Return that tuple and adjust idx as needed.
PyObject* getVarnameTuple(PyCodeObject* code, int* idx);
// Similar to getVarnameTuple, but return the name itself rather than the
// containing tuple.
PyObject* getVarname(PyCodeObject* code, int idx);
inline int popcount(unsigned i) {
return __builtin_popcount(i);
}
inline int popcount(unsigned long i) {
return __builtin_popcountl(i);
}
inline int popcount(unsigned long long i) {
return __builtin_popcountll(i);
}
// Look up an item in the given map. Always abort if key doesn't exist.
template <typename M, typename K>
auto& map_get_strict(M& map, const K& key) {
auto it = map.find(key);
JIT_CHECK(it != map.end(), "Key not found in map");
return it->second;
}
// Look up an item in the given map, aborting if the key doesn't exist. Similar
// to map.at(key) but with a less opaque failure mode.
template <typename M, typename K>
auto& map_get(M& map, const K& key) {
auto it = map.find(key);
JIT_DCHECK(it != map.end(), "Key not found in map");
return it->second;
}
// Look up an item in the given map. If the key doesn't exist, return the
// default value.
template <typename M>
const typename M::mapped_type map_get(
M& map,
const typename M::key_type& key,
const typename M::mapped_type& def) {
auto it = map.find(key);
if (it == map.end()) {
return def;
}
return it->second;
}
// A queue that doesn't enqueue items that are already present. Items must be
// hashable with std::hash.
template <typename T>
class Worklist {
public:
bool empty() const {
return queue_.empty();
}
const T& front() const {
JIT_DCHECK(!empty(), "Worklist is empty");
return queue_.front();
}
void push(const T& item) {
if (set_.insert(item).second) {
queue_.push(item);
}
}
void pop() {
set_.erase(front());
queue_.pop();
}
private:
std::queue<T> queue_;
std::unordered_set<T> set_;
};
template <typename T>
std::enable_if_t<std::is_integral_v<T>, bool> fitsInt32(T val) {
int64_t v = val;
return (
v <= std::numeric_limits<int32_t>::max() &&
v >= std::numeric_limits<int32_t>::min());
}
template <typename T>
std::enable_if_t<std::is_pointer_v<T>, bool> fitsInt32(T val) {
return fitsInt32(reinterpret_cast<intptr_t>(val));
}
// std::unique_ptr for objects created with std::malloc() rather than new.
struct FreeDeleter {
void operator()(void* ptr) const {
std::free(ptr);
}
};
template <typename T>
using unique_c_ptr = std::unique_ptr<T, FreeDeleter>;
} // namespace jit
template <typename D, typename S>
inline constexpr D bit_cast(const S& src) {
static_assert(sizeof(S) == sizeof(D), "src and dst must be the same size");
static_assert(
std::is_scalar_v<D> && std::is_scalar_v<S>,
"both src and dst must be of scalar type.");
D dst;
std::memcpy(&dst, &src, sizeof(dst));
return dst;
}
#endif
// this is for non-test builds. define FRIEND_TEST here so we don't
// have to include the googletest header in our headers to be tested.
#ifndef FRIEND_TEST
#define FRIEND_TEST(test_case_name, test_name)
#endif