-
Notifications
You must be signed in to change notification settings - Fork 1
/
radixtree.hpp
224 lines (201 loc) · 7.83 KB
/
radixtree.hpp
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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
#ifndef RADIXTREE_HPP
#define RADIXTREE_HPP
/******************************************************************************\
* Radix Tree
* MIT License
* Copyright 2016, Simon Pratt
\******************************************************************************/
#include <vector>
using namespace std;
class RadixTreeIterator {
uint64_t v_, p_;
bool inv_;
public:
RadixTreeIterator(uint64_t vadd, uint64_t padd, bool invalid = false) :
v_(vadd), p_(padd), inv_(invalid) {}
void setInvalid() { inv_ = true; }
bool isValid() { return !inv_; }
friend bool operator!= (const RadixTreeIterator &a,
const RadixTreeIterator &b)
{ return !(a == b); }
friend bool operator==(const RadixTreeIterator &a,
const RadixTreeIterator &b)
{ return a.v_ == b.v_ && a.p_ == b.p_ && a.inv_ == b.inv_; }
uint64_t operator* () { return this->p_; }
};
/******************************************************************************\
* Radix Tree. This implementation is designed as a mock-up of x86-64
* page tables. However, in a real implementation all table base
* addresses would be page-aligned, and therefore require only 40 bits
* to encode (since the low-order bits would just be 0). This is
* simply a mock-up and doesn't page-align anything, and neither does
* it encode meta-information bits. The purpose of this
* implementation is simply to test the speed of virtual->physical
* address mappings in a radix tree similar to the one used in a page table.
*
\******************************************************************************/
class RadixTree {
vector< vector< vector< vector<uint64_t>* >* >* > t_;
size_t s_;
/****************************************************************************\
* A virtual address has the following form:
*
* 6 6 5 4 4 3 2 1 0
* 3210987654321098765432109876543210987654321098765432109876543210
* ----------------------------------------------------------------
* 0000000000000000000000001000000010000000011000000100000000000000 <- example
* \______________/\_______/\_______/\_______/\_______/\__________/
* sign extend(16) p4(9) p3(9) p2(9) p1(9) offset(12)
*
* For the purpose of this mockup, we simply ignore the most
* significant 16 bits. The 9 bits (47:39) of the p4 key give an
* index into the first level of page tables, stored in t_, which
* gives a pointer to the p3 table. The 9 bits (38:30) of the p3 key
* give an index into this table, and so on. The p1 table entry gives
* another 64-bit integer, with the following form:
*
* 6 6 5 4 3 2 1 0
* 3210987654321098765432109876543210987654321098765432109876543210
* ----------------------------------------------------------------
* |\_________/\______________________________________/\_/|||||||||
* N Avl.(11) physical base address(40) A GPDAPPURP
*
* Everything is ignored except for the 40-bit physical base address
* in bits 51:12, which is combined with the 12-bit offset given by
* bits 11:0 of the virtual address, to give a 52-bit physical
* address.
*
\****************************************************************************/
static uint16_t p4_key(uint64_t vadd) {
// \/- 9 bits
uint64_t mask = 0b111111111;
return (vadd & (mask << (9 + 9 + 9 + 12)) >> (9 + 9 + 9 + 12));
}
static uint16_t p3_key(uint64_t vadd) {
// \/- 9 bits
uint64_t mask = 0b111111111;
return (vadd & (mask << (9 + 9 + 12)) >> (9 + 9 + 12));
}
static uint16_t p2_key(uint64_t vadd) {
// \/- 9 bits
uint64_t mask = 0b111111111;
return (vadd & (mask << (9 + 12)) >> (9 + 12));
}
static uint16_t p1_key(uint64_t vadd) {
// \/- 9 bits
uint64_t mask = 0b111111111;
return (vadd & (mask << 12) >> 12);
}
static uint16_t offset(uint64_t vadd) {
// \/- 12 bits
uint64_t mask = 0b111111111111;
return vadd & mask;
}
static uint64_t pb_add(uint64_t padd) {
// \/- 40 bits
uint64_t mask = 0b1111111111111111111111111111111111111111;
return (padd & (mask << 12)) >> 12;
}
static const size_t p4_size = 512;
static const size_t p3_size = 512;
static const size_t p2_size = 512;
static const size_t p1_size = 512;
public:
RadixTree() : t_(p4_size), s_(0) {}
void insert(uint64_t vadd, uint64_t padd) {
uint16_t p4k = p4_key(vadd);
uint16_t p3k = p3_key(vadd);
uint16_t p2k = p2_key(vadd);
uint16_t p1k = p1_key(vadd);
if(t_[p4k] == NULL)
t_[p4k] = new vector< vector< vector<uint64_t>* >* >(p3_size);
if((*t_[p4k])[p3k] == NULL)
(*t_[p4k])[p3k] = new vector< vector<uint64_t>* >(p2_size);
if((*(*t_[p4k])[p3k])[p2k] == NULL)
(*(*t_[p4k])[p3k])[p2k] = new vector<uint64_t>(p1_size);
(*(*(*t_[p4k])[p3k])[p2k])[p1k] = pb_add(padd) << 12;
++s_;
}
RadixTreeIterator find(uint64_t vadd) {
uint16_t p4k = p4_key(vadd);
uint16_t p3k = p3_key(vadd);
uint16_t p2k = p2_key(vadd);
uint16_t p1k = p1_key(vadd);
if(t_[p4k] == NULL ||
(*t_[p4k])[p3k] == NULL ||
(*(*t_[p4k])[p3k])[p2k] == NULL)
return end();
uint64_t base = (pb_add((*(*(*t_[p4k])[p3k])[p2k])[p1k]));
uint64_t padd = offset(vadd) & (base << 12);
return RadixTreeIterator(vadd, padd);
}
RadixTreeIterator end() { return RadixTreeIterator(-1, -1, true); }
void clear() {
for(size_t p4i = 0; p4i < p4_size; ++p4i) {
vector< vector< vector<uint64_t>* >* >* p3 = t_[p4i];
if(p3 == NULL) continue;
for(size_t p3i = 0; p3i < p3_size; ++p3i) {
vector< vector<uint64_t>* >* p2 = (*p3)[p3i];
if(p2 == NULL) continue;
for(size_t p2i = 0; p2i < p2_size; ++p2i) {
vector<uint64_t>* p1 = (*p2)[p2i];
if(p1 == NULL) continue;
p1->clear();
delete p1;
}
p2->clear();
delete p2;
}
p3->clear();
delete p3;
t_[p4i] = NULL;
}
t_.clear();
s_ = 0;
}
size_t size() { return s_; }
};
// Given a 64-bit number vadd, returns the same number but with the
// most-significant 16-bits (63:48) replaced with the value of bit 47,
// effectively sign-extending it to make it a valid virtual address in
// x86_64.
//
// 6 6 5 4 4 3 2 1 0
// 3210987654321098765432109876543210987654321098765432109876543210
// ----------------------------------------------------------------
// \______________/\_______/\_______/\_______/\_______/\__________/
// sign extend(16) p4(9) p3(9) p2(9) p1(9) offset(12)
//
uint64_t correctify_vadd(uint64_t vadd) {
// 48 0'x
uint64_t bit47 = 0b1;
bit47 <<= 47;
if(vadd & bit47) {
uint64_t mask = 0b1111111111111111;
mask <<= 48;
return vadd | mask;
} else {
uint64_t mask = 0b111111111111111111111111111111111111111111111111;
return vadd & mask;
}
}
// Given 64-bit numbers vadd and padd, return a valid physical address
// with the following form:
//
// 6 6 5 4 4 3 2 1 0
// 3210987654321098765432109876543210987654321098765432109876543210
// ----------------------------------------------------------------
// 0000000000000000000000001000000010000000011000000100000000000000 <- example
// \__________/\______________________________________/\__________/
// ignore(12) padd(40) vadd(12)
//
// That is, return the least-significant 12 bits (11:0) from vadd
// bitwise-ORed with bits 47:12 from padd.
//
uint64_t correctify_padd(uint64_t vadd, uint64_t padd) {
uint64_t vmask = 0b111111111111;
uint64_t pmask = 0b1111111111111111111111111111111111111111;
return ((pmask << 12) & padd) | (vmask & vadd);
}
/******************************************************************************/
#endif