aboutsummaryrefslogtreecommitdiff
path: root/src/core/stack.h
blob: f699b5242557498d96fa913c2f9779b501d71f47 (plain) (blame)
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
/* growing operand and return stacks share the runtime's buddy allocator.
   allocation here cannot call vm code or collect: an overflowing return stack
   has no room for an allocator call. the runtime registers its allocator and
   stack globals once the heap is ready. addresses are vm word indices. */
#ifndef TINYVM_STACK_H
#define TINYVM_STACK_H
#ifndef VM_TLS
#define VM_TLS
#endif

static VM_TLS Cell stack_control;
enum {
	SC_FRAME_FLOOR, SC_FRAME_TOP, SC_FRAME_GUARD,
	SC_DATA_LOW, SC_DATA_TOP, SC_DATA_NODE,
	SC_RETURN_LOW, SC_RETURN_TOP, SC_RETURN_NODE,
	SC_FREE, SC_STOP, SC_BUMP, SC_MAX, SC_HOLD_LOW, SC_HOLD_HIGH, SC_CUT, SC_GC_ACTIVE, SC_COUNT
};

/* the including core supplies stack_memory_grow and refreshes m if it moves. */
static Cell stack_get(int slot) { Cell p = m[stack_control + slot]; return inmem(p) ? m[p] : 0; }
static void stack_set(int slot, Cell value) { Cell p = m[stack_control + slot]; if (inmem(p)) m[p] = value; }
static int stack_register(Cell control)
{
	int i;
	if (control <= 0 || !inmem(control + SC_COUNT - 1)) return 0;
	for (i = 0; i < SC_COUNT; i++) if (!inmem(m[control + i])) return 0;
	if (!inmem(m[control + SC_FREE] + 31)) return 0;
	stack_control = control;
	return 1;
}
static void stack_push(Cell b, int k)
{
	Cell list = m[stack_control + SC_FREE], head;
	if (k < 3 || k >= 31 || b < 0 || !inmem(b + 6) || list < 0 || !inmem(list + k)) return;
	head = m[list + k];
	if (head && (head < 0 || !inmem(head + 6))) return;
	m[b] = (Cell)1 << k; m[b + 1] = (Cell)k << 16; m[b + 2] = 0;
	m[b + 3] = 0; m[b + 4] = 0; m[b + 5] = head; m[b + 6] = 0;
	if (head) m[head + 6] = b;
	m[list + k] = b;
}
static int stack_heap_grow(int k)
{
	Cell old = stack_get(SC_STOP), h = old - HEAP_LO, block = (Cell)1 << k;
	Cell need = ((h + block - 1) / block + 1) * block, size = h * 2, off, part;
	int c;
	if (h <= 0 || (h & 7) || !HEAP_GROW || !stack_get(SC_MAX) || stack_get(SC_CUT) || stack_get(SC_HOLD_HIGH)) return 0;
	if (size < need) size = need;
	if (HEAP_LO + size > 0x7fffffff || !stack_memory_grow((UCell)(HEAP_LO + size))) {
		size = need;
		if (HEAP_LO + size > 0x7fffffff || !stack_memory_grow((UCell)(HEAP_LO + size))) return 0;
	}
	stack_set(SC_STOP, HEAP_LO + size); stack_set(SC_BUMP, HEAP_LO + size);
	stack_set(SC_MAX, HEAP_LO + size);
	off = h;
	while (off < size) {
		part = 8; c = 3;
		while (part * 2 <= size - off && !(off % (part * 2))) { part *= 2; c++; }
		stack_push(HEAP_LO + off, c); off += part;
	}
	return 1;
}
static Cell stack_allocate(Cell words)
{
	Cell list, b, next, other, p, i;
	int k = 3, j, grown = 0;
	if (!stack_control || words < 0 || words > 0x3ffffffa) return 0;
	while (((Cell)1 << k) < words + 5) k++;
	for (;;) {
		list = m[stack_control + SC_FREE]; j = k;
		if (list < 0 || !inmem(list + 31)) return 0;
		while (j < 31 && !m[list + j]) j++;
		if (j == 31) {
			if (grown || !stack_heap_grow(k)) return 0;
			grown = 1; continue;
		}
		b = m[list + j];
		if (b < 0 || !inmem(b + ((Cell)1 << j) - 1) || m[b] != ((Cell)1 << j) || (m[b + 1] & 65535)) return 0;
		next = m[b + 5]; m[list + j] = next;
		if (next) { if (!inmem(next + 6)) return 0; m[next + 6] = 0; }
		while (j > k) { j--; other = b + ((Cell)1 << j); stack_push(other, j); }
		if (stack_get(SC_HOLD_HIGH) && b < stack_get(SC_HOLD_HIGH) && b + ((Cell)1 << k) > stack_get(SC_HOLD_LOW)) {
			m[b] = (Cell)1 << k; m[b + 1] = ((Cell)k << 16) | 2;
			m[b + 2] = 0; m[b + 5] = 0; m[b + 6] = 0; continue;
		}
		p = b + 5;
		m[b] = (Cell)1 << k; m[b + 1] = ((Cell)k << 16) | 1 | 512 | 1024 | (stack_get(SC_GC_ACTIVE) ? 2 : 0);
		m[b + 2] = p ^ 0x2B7E1516; m[b + 3] = 0; m[b + 4] = -words - 1;
		for (i = p; i < b + ((Cell)1 << k); i++) m[i] = 0;
		return p;
	}
}
static Cell stack_expand(int is_return, Cell minimum, Cell *sp, Cell *rp)
{
	int low_slot = is_return ? SC_RETURN_LOW : SC_DATA_LOW, top_slot = low_slot + 1, node_slot = low_slot + 2;
	Cell low, top, capacity, used, node, oldnode, i, source;
	if (!stack_control) return -1;
	low = stack_get(low_slot); top = stack_get(top_slot); capacity = top - low;
	used = is_return ? *rp - low : top - *sp;
	if (capacity <= 0 || used < 0 || used > capacity + (is_return ? 8 : 0) || minimum < 0 || minimum > 0x3ffffff0) return -1;
	if (minimum <= capacity) return capacity;
	if (minimum < capacity * 2) minimum = capacity * 2;
	node = stack_allocate(minimum + 16);
	if (!node) return -1;
	source = is_return ? low : *sp;
	for (i = 0; i < used; i++) m[node + (is_return ? i : minimum - used + i)] = m[source + i];
	oldnode = stack_get(node_slot);
	stack_set(low_slot, node); stack_set(top_slot, node + minimum); stack_set(node_slot, node);
	if (is_return) *rp = node + used; else *sp = node + minimum - used;
	if (oldnode >= 5 && inmem(oldnode - 4)) stack_push(oldnode - 5, (int)((m[oldnode - 4] >> 16) & 31));
	return minimum;
}
static Cell stack_data_limit(void) { return stack_control ? stack_get(SC_DATA_LOW) + 1100 : DSLIM; }
static Cell stack_return_limit(void) { return stack_control ? stack_get(SC_RETURN_TOP) : RSLIM; }
#endif