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
|
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef TOOLS_CLANG_STACK_MAPS_GC_GC_API_H_
#define TOOLS_CLANG_STACK_MAPS_GC_GC_API_H_
#include <assert.h>
#include <map>
#include <vector>
#include "objects.h"
using ReturnAddress = uint64_t;
using FramePtr = uintptr_t*;
using RBPOffset = uint32_t;
using DWARF = uint16_t;
using HeapAddress = long*;
// The place where HeapObjects live. For simplicity, the underlying data in a
// HeapObject is always a single uintptr_t. The heap layout mocks a simple
// semi-space collector where objects can be moved between two heap fragments.
//
// Note that this is a no-op collector: unreachable objects are not reclaimed
// and allocation will keep filling the heap until its limited memory is
// exhausted.
class Heap {
public:
// Allocates a HeapObject's underlying data field on the heap and returns a
// pointer to it. This allocation will use the heap fragment returned from a
// fromspace() call.
HeapAddress AllocRaw(long value);
// Moves all values from fromspace to tospace. fromspace becomes tospace and
// vice versa (i.e. future allocations take place on the opposite heap
// fragment). Note no objects are dropped in the process.
void MoveObjects();
// For an arbitrary pointer into the heap, this will return a new pointer with
// a corresponding offset into the opposite heap fragment. E.g. a pointer to
// an address at offset +4 into heap fragment A would return an address at
// offset +4 into heap fragment B.
//
// This is used for relocating root pointer values across a collection during
// stack walking.
HeapAddress UpdatePointer(HeapAddress ptr);
private:
static constexpr int kHeapSize = 24;
HeapAddress fromspace() {
if (alloc_on_a_) {
return a_frag_;
} else {
return b_frag_;
}
}
HeapAddress tospace() {
if (alloc_on_a_) {
return b_frag_;
} else {
return a_frag_;
}
}
int heap_ptr = 0;
long a_frag_[kHeapSize];
long b_frag_[kHeapSize];
bool alloc_on_a_ = true;
};
// A FrameRoots object contains all the information needed to precisely identify
// live roots for a given safepoint. It contains a list of registers which are
// known to contain roots, and a list of offsets from the stack pointer to known
// on-stack-roots.
//
// Each stackmap entry in .llvm_stackmaps has two parts: a base pointer (not to
// be confused with EBP), which simply points to an object header; and a derived
// pointer which specifies an offset (if any) into the object's interior. In the
// case where only a base object pointer is desired, the derived pointer will be
// 0.
//
// DWARF Register number mapping can be found here:
// Pg.63
// https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf
class FrameRoots {
public:
FrameRoots(std::vector<DWARF> reg_roots, std::vector<RBPOffset> stack_roots)
: reg_roots_(reg_roots), stack_roots_(stack_roots) {}
const std::vector<DWARF>* reg_roots() { return ®_roots_; }
const std::vector<RBPOffset>* stack_roots() { return &stack_roots_; }
bool empty() { return reg_roots_.empty() && stack_roots_.empty(); }
void Print() const;
private:
std::vector<DWARF> reg_roots_;
std::vector<RBPOffset> stack_roots_;
};
// A SafepointTable provides a runtime mapping of function return addresses to
// on-stack and in-register gc root locations. Return addresses are used as a
// function call site is the only place where safepoints can exist. This map is
// a convenient format for the collector to use while walking a call stack
// looking for the rootset.
class SafepointTable {
public:
SafepointTable(std::map<ReturnAddress, FrameRoots> roots)
: roots_(std::move(roots)) {}
const std::map<ReturnAddress, FrameRoots>* roots() { return &roots_; }
void Print() const;
private:
const std::map<ReturnAddress, FrameRoots> roots_;
};
SafepointTable GenSafepointTable();
extern SafepointTable spt;
extern Heap* heap;
// During stack scanning, the GC must know when it has reached the top of the
// stack so that it can hand execution back over to the mutator. This global
// variable serves that purpose - it is initialised in main to be equal to
// main's RBP value, and checked against each time the gc steps up into the next
// stack frame. For non-main threads this could be pthread top.
extern "C" void InitTopOfStack();
extern uintptr_t TopOfStack;
void PrintSafepointTable();
// Walks the execution stack looking for live gc roots. This function should
// never be called directly. Instead, the void |GC| function should be
// called. |GC| is an assembly shim which jumps to this function after
// placing the value of RBP in RDI (First arg slot mandated by Sys V ABI).
//
// Stack walking starts from the address in `fp` (assumed to be RBP's
// address). The stack is traversed from bottom to top until the frame pointer
// hits a terminal value (usually main's RBP value).
//
// This works by assuming the calling convention for each frame adheres to the
// Sys V ABI, where the frame pointer is known to point to the address of last
// saved frame pointer (and so on), creating a linked list of frames on the
// stack (shown below).
//
// +--------------------+
// | ... |
// +--------------------+
// | Saved RBP |<--+
// +--------------------+ |
// | | |
// | ... | |
// | | |
// +--------------------+ |
// | Return Address | |
// +--------------------+ |
// RBP--> | Saved RBP |---+
// +--------------------+
// | |
// | Args |
// | |
// +--------------------+
//
// This therefore requires that the optimisation -fomit-frame-pointer is
// disabled in order to guarantee that RBP will not be used as a
// general-purpose register.
extern "C" void StackWalkAndMoveObjects(FramePtr fp);
// A very simple allocator for a HeapObject. For the purposes of this
// experiment, a HeapObject's contents is simply a 64 bit integer. The data
// itself is not important, what is, however, is that it can be accessed through
// the rootset after the collector moves it.
Handle<HeapObject> AllocateHeapObject(HeapAddress data);
#endif // TOOLS_CLANG_STACK_MAPS_GC_GC_API_H_
|