1// Copyright 2023 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5//go:build plan9 || wasm
6
7package runtime
8
9import "unsafe"
10
11const memDebug = false
12
13var bloc uintptr
14var blocMax uintptr
15var memlock mutex
16
17type memHdr struct {
18	next memHdrPtr
19	size uintptr
20}
21
22var memFreelist memHdrPtr // sorted in ascending order
23
24type memHdrPtr uintptr
25
26func (p memHdrPtr) ptr() *memHdr   { return (*memHdr)(unsafe.Pointer(p)) }
27func (p *memHdrPtr) set(x *memHdr) { *p = memHdrPtr(unsafe.Pointer(x)) }
28
29func memAlloc(n uintptr) unsafe.Pointer {
30	n = memRound(n)
31	var prevp *memHdr
32	for p := memFreelist.ptr(); p != nil; p = p.next.ptr() {
33		if p.size >= n {
34			if p.size == n {
35				if prevp != nil {
36					prevp.next = p.next
37				} else {
38					memFreelist = p.next
39				}
40			} else {
41				p.size -= n
42				p = (*memHdr)(add(unsafe.Pointer(p), p.size))
43			}
44			*p = memHdr{}
45			return unsafe.Pointer(p)
46		}
47		prevp = p
48	}
49	return sbrk(n)
50}
51
52func memFree(ap unsafe.Pointer, n uintptr) {
53	n = memRound(n)
54	memclrNoHeapPointers(ap, n)
55	bp := (*memHdr)(ap)
56	bp.size = n
57	bpn := uintptr(ap)
58	if memFreelist == 0 {
59		bp.next = 0
60		memFreelist.set(bp)
61		return
62	}
63	p := memFreelist.ptr()
64	if bpn < uintptr(unsafe.Pointer(p)) {
65		memFreelist.set(bp)
66		if bpn+bp.size == uintptr(unsafe.Pointer(p)) {
67			bp.size += p.size
68			bp.next = p.next
69			*p = memHdr{}
70		} else {
71			bp.next.set(p)
72		}
73		return
74	}
75	for ; p.next != 0; p = p.next.ptr() {
76		if bpn > uintptr(unsafe.Pointer(p)) && bpn < uintptr(unsafe.Pointer(p.next)) {
77			break
78		}
79	}
80	if bpn+bp.size == uintptr(unsafe.Pointer(p.next)) {
81		bp.size += p.next.ptr().size
82		bp.next = p.next.ptr().next
83		*p.next.ptr() = memHdr{}
84	} else {
85		bp.next = p.next
86	}
87	if uintptr(unsafe.Pointer(p))+p.size == bpn {
88		p.size += bp.size
89		p.next = bp.next
90		*bp = memHdr{}
91	} else {
92		p.next.set(bp)
93	}
94}
95
96func memCheck() {
97	if !memDebug {
98		return
99	}
100	for p := memFreelist.ptr(); p != nil && p.next != 0; p = p.next.ptr() {
101		if uintptr(unsafe.Pointer(p)) == uintptr(unsafe.Pointer(p.next)) {
102			print("runtime: ", unsafe.Pointer(p), " == ", unsafe.Pointer(p.next), "\n")
103			throw("mem: infinite loop")
104		}
105		if uintptr(unsafe.Pointer(p)) > uintptr(unsafe.Pointer(p.next)) {
106			print("runtime: ", unsafe.Pointer(p), " > ", unsafe.Pointer(p.next), "\n")
107			throw("mem: unordered list")
108		}
109		if uintptr(unsafe.Pointer(p))+p.size > uintptr(unsafe.Pointer(p.next)) {
110			print("runtime: ", unsafe.Pointer(p), "+", p.size, " > ", unsafe.Pointer(p.next), "\n")
111			throw("mem: overlapping blocks")
112		}
113		for b := add(unsafe.Pointer(p), unsafe.Sizeof(memHdr{})); uintptr(b) < uintptr(unsafe.Pointer(p))+p.size; b = add(b, 1) {
114			if *(*byte)(b) != 0 {
115				print("runtime: value at addr ", b, " with offset ", uintptr(b)-uintptr(unsafe.Pointer(p)), " in block ", p, " of size ", p.size, " is not zero\n")
116				throw("mem: uninitialised memory")
117			}
118		}
119	}
120}
121
122func memRound(p uintptr) uintptr {
123	return alignUp(p, physPageSize)
124}
125
126func initBloc() {
127	bloc = memRound(firstmoduledata.end)
128	blocMax = bloc
129}
130
131func sysAllocOS(n uintptr) unsafe.Pointer {
132	lock(&memlock)
133	p := memAlloc(n)
134	memCheck()
135	unlock(&memlock)
136	return p
137}
138
139func sysFreeOS(v unsafe.Pointer, n uintptr) {
140	lock(&memlock)
141	if uintptr(v)+n == bloc {
142		// Address range being freed is at the end of memory,
143		// so record a new lower value for end of memory.
144		// Can't actually shrink address space because segment is shared.
145		memclrNoHeapPointers(v, n)
146		bloc -= n
147	} else {
148		memFree(v, n)
149		memCheck()
150	}
151	unlock(&memlock)
152}
153
154func sysUnusedOS(v unsafe.Pointer, n uintptr) {
155}
156
157func sysUsedOS(v unsafe.Pointer, n uintptr) {
158}
159
160func sysHugePageOS(v unsafe.Pointer, n uintptr) {
161}
162
163func sysNoHugePageOS(v unsafe.Pointer, n uintptr) {
164}
165
166func sysHugePageCollapseOS(v unsafe.Pointer, n uintptr) {
167}
168
169func sysMapOS(v unsafe.Pointer, n uintptr) {
170}
171
172func sysFaultOS(v unsafe.Pointer, n uintptr) {
173}
174
175func sysReserveOS(v unsafe.Pointer, n uintptr) unsafe.Pointer {
176	lock(&memlock)
177	var p unsafe.Pointer
178	if uintptr(v) == bloc {
179		// Address hint is the current end of memory,
180		// so try to extend the address space.
181		p = sbrk(n)
182	}
183	if p == nil && v == nil {
184		p = memAlloc(n)
185		memCheck()
186	}
187	unlock(&memlock)
188	return p
189}
190