File: gc_api.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (181 lines) | stat: -rw-r--r-- 6,514 bytes parent folder | download | duplicates (11)
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 &reg_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_