blob: ce4720983673f3b69a9f962aeee0b4e0723a2c3a [file] [edit]
/*
Copyright (c) 2018 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#pragma once
#include <functional>
#include <string>
#include <vector>
#include "runtime/object.h"
#include "util/alloc.h"
namespace lean {
typedef lean_object * object_offset;
class region_reader;
/** Information about a loaded library, used for closure function pointer relocation. */
struct lib_info {
size_t base_addr;
// Loader-reported identity, used as an opaque key to match a saved library against a
// currently-loaded one
std::string id;
};
/** Builds the sorted table of all currently loaded libraries. */
LEAN_EXPORT std::vector<lib_info> get_loaded_libs();
/** A lightweight, non-owning view of a compacted region's address range, used as a cross-region
pointer-relocation dependency. The owning buffer is managed on the Lean side (`CompactedRegion`),
not here. `begin`/`size` give the physical data range; `base_addr` is the logical base its
pointers are relative to. */
struct region_view {
void * begin;
size_t size;
void * base_addr;
};
class LEAN_EXPORT object_compactor {
struct max_sharing_table;
friend struct max_sharing_hash;
friend struct max_sharing_eq;
lean::unordered_map<object*, object_offset, std::hash<object*>, std::equal_to<object*>> m_obj_table;
std::unique_ptr<max_sharing_table> m_max_sharing_table;
// Scratch stack of child offsets, to avoid repeated stack allocs during recursion
std::vector<object_offset> m_tmp;
// Dependency regions sorted by `begin` address for binary search in `to_offset`
std::vector<region_view> m_dep_regions;
// Sorted table of loaded libraries for closure function pointer mapping
std::vector<lib_info> m_libs;
// Buffer-relative byte offsets of every compacted closure's `m_fun` field
std::vector<size_t> m_closure_offsets;
bool m_allow_closures = false;
// On-disk base address used for `mmap`ing compacted regions without relocations
// References within the compacted region are rewritten by subtracting `m_begin` and adding `m_base_addr`
// In the simplest case `base_addr == nullptr`, we get region-relative pointers
void * m_base_addr;
void * m_begin;
void * m_end;
void * m_capacity;
size_t capacity() const { return static_cast<char*>(m_capacity) - static_cast<char*>(m_begin); }
object_offset save(object * o, object * new_o);
object_offset save_max_sharing(object * o, object * new_o, size_t new_o_sz);
object_offset to_offset(object * o);
// Compacts a not-yet-seen heap object (and, recursively, its children),
// returning its offset. Dispatches on the object's tag.
object_offset compact(object * o);
void insert_terminator(object * o);
object * copy_object(object * o, size_t sz = 0);
object_offset insert_constructor(object * o);
object_offset insert_array(object * o);
object_offset insert_sarray(object * o);
object_offset insert_string(object * o);
object_offset insert_thunk(object * o);
object_offset insert_task(object * o);
object_offset insert_promise(object * o);
object_offset insert_ref(object * o);
object_offset insert_mpz(object * o);
object_offset insert_closure(object * o);
public:
object_compactor(void * base_addr = nullptr, std::vector<region_view> dep_regions = {},
bool allow_closures = false);
object_compactor(object_compactor const &) = delete;
object_compactor(object_compactor &&) = delete;
~object_compactor();
object_compactor operator=(object_compactor const &) = delete;
object_compactor operator=(object_compactor &&) = delete;
void operator()(object * o);
size_t size() const { return static_cast<char*>(m_end) - static_cast<char*>(m_begin); }
void const * data() const { return m_begin; }
// Allocate `sz` bytes of zeroed memory.
void * alloc(size_t sz);
void const * base_addr() const { return m_base_addr; }
/** Returns the distinct loaded libraries that contain a compacted closure's `m_fun` pointer
— the subset of loaded libraries needed to relocate this region's closures on load.
Throws if a closure's fn pointer lies in no loaded library. */
std::vector<lib_info> used_libs() const;
/** Buffer-relative offsets of all closure `m_fun` fields compacted so far. */
std::vector<size_t> & closure_offsets() { return m_closure_offsets; }
};
// A transient, non-owning read context: it walks a just-mapped buffer to fix up its pointers
// (`read`), then is discarded. The buffer itself is owned by the Lean `CompactedRegion` and freed
// via `lean_compacted_region_free`, not here, so this class holds no finalizer.
class LEAN_EXPORT region_reader {
size_t m_size;
// see `object_compactor::m_base_addr`
void * m_base_addr;
void * m_begin;
void * m_next;
void * m_end;
// Path of the file this region was loaded from (empty when constructed in-memory rather
// than read from disk). Lets snapshot save re-derive its dep paths from `env.header.regions`.
std::string m_fname;
// Dependency regions for cross-region pointer fixup
std::vector<region_view> m_dep_regions;
// Sorted (old_base, delta) pairs for closure function pointer relocation
std::vector<std::pair<size_t, ptrdiff_t>> m_lib_relocs;
// Data-relative byte offsets of every closure's `m_fun` field. Used to patch fn
// pointers on load without scanning the compacted region. Empty if no closures.
std::vector<size_t> m_closure_offsets;
void sort_and_validate_dep_regions();
void move(size_t d);
void move(object * o);
object * fix_object_ptr(object * o);
void fix_constructor(object * o);
void fix_array(object * o);
void fix_thunk(object * o);
void fix_ref(object * o);
void fix_task(object * o);
void fix_promise(object * o);
void fix_mpz(object * o);
void fix_closure(object * o);
public:
/* Creates a read context over the given just-mapped buffer. Does not take ownership of the
buffer; see the class comment. */
region_reader(size_t sz, void * data, void * base_addr,
std::vector<region_view> dep_regions = {},
std::vector<std::pair<size_t, ptrdiff_t>> lib_relocs = {},
std::vector<size_t> closure_offsets = {});
region_reader(region_reader const &) = delete;
region_reader(region_reader &&) = delete;
region_reader operator=(region_reader const &) = delete;
region_reader operator=(region_reader &&) = delete;
object * read();
};
}