blob: 841e40809ec2a156179e606bc6ce46ca2fd6f908 [file] [edit]
#ifndef SLINKY_RUNTIME_EVALUATE_H
#define SLINKY_RUNTIME_EVALUATE_H
#include "base/allocator.h"
#include "runtime/expr.h"
#include "runtime/stmt.h"
namespace slinky {
class thread_pool;
struct eval_config {
// These two functions implement allocation. `allocate` is called before
// running the body, and should assign `base` of the buffer to the address
// of the min in each dimension. `free` is called after running the body,
// passing the result of `allocate` in addition to the buffer.
// If these functions are not defined, the default handler will call
// `raw_buffer::allocate` and `::free`.
std::function<void*(var, raw_buffer*)> allocate = [](var, raw_buffer* buf) { return buf->allocate(); };
std::function<void(var, raw_buffer*, void*)> free = [](var, raw_buffer*, void* allocation) { ::free(allocation); };
// Functions called when there is a failure in the pipeline.
// If these functions are not defined, the default handler will write a
// message to cerr and abort.
std::function<void(const expr&)> check_failed;
std::function<void(const call_stmt*)> call_failed;
// A pointer to a thread pool, required for parallel
slinky::thread_pool* thread_pool = nullptr;
// Functions implementing buffer data movement:
// - `copy` should copy from `src` to `dst`, filling `dst` with `pad` when out of bounds of `src`.
// - `pad` should fill the area out of bounds of `src_dims` with `pad` in `dst`.
std::function<void(const raw_buffer& src, const raw_buffer& dst, const raw_buffer* pad)> copy =
static_cast<void (*)(const raw_buffer&, const raw_buffer&, const raw_buffer*)>(slinky::copy);
std::function<void(const dim* in_bounds, const raw_buffer& dst, const raw_buffer& pad)> pad =
static_cast<void (*)(const dim*, const raw_buffer&, const raw_buffer&)>(slinky::pad);
// Functions implementing the `trace_begin` and `trace_end` intrinsics.
std::function<index_t(const char*)> trace_begin;
std::function<void(index_t)> trace_end;
// Alignment to use for `raw_buffer::init_strides` and `raw_buffer::allocate` calls.
std::size_t alignment = sizeof(std::max_align_t);
// Allocations with storage `memory_type::automatic` not bigger than this size (bytes) will be placed on the stack.
std::size_t auto_stack_threshold = 4 * 1024;
};
class eval_context {
// Leave uninitialized to avoid overhead and to detect uninitialized memory access via msan.
std::vector<index_t, uninitialized_allocator<index_t>> values_;
public:
eval_context();
void reserve(std::size_t size) {
if (size > values_.size()) {
values_.resize(std::max(values_.size() * 2, size));
}
}
index_t& operator[](var id) {
reserve(id.id + 1);
return values_[id.id];
}
index_t operator[](var id) const { return values_[id.id]; }
index_t set(var id, index_t value) {
index_t& value_ref = values_[id.id];
index_t old_value = value_ref;
value_ref = value;
return old_value;
}
index_t lookup(var id) const {
assert(id.id < values_.size());
return values_[id.id];
}
const raw_buffer* lookup_buffer(var id) const { return reinterpret_cast<const raw_buffer*>(lookup(id)); }
template <typename T>
const buffer<T>* lookup_buffer(var id) const {
const raw_buffer* buf = lookup_buffer(id);
return buf ? &buf->cast<T>() : nullptr;
}
std::size_t size() const { return values_.size(); }
const eval_config* config;
};
index_t evaluate(const expr& e, eval_context& context);
index_t evaluate(const stmt& s, eval_context& context);
index_t evaluate(const expr& e);
index_t evaluate(const stmt& s);
} // namespace slinky
#endif // SLINKY_RUNTIME_EVALUATE_H