builtin: use dlmalloc for `-freestanding` (#13054)
parent
fb66ec7cfb
commit
ec91de3504
|
@ -158,6 +158,12 @@ fn get_all_commands() []Command {
|
|||
okmsg: 'V can compile with -freestanding on Linux with GCC.'
|
||||
rmfile: 'bel'
|
||||
}
|
||||
|
||||
res << Command{
|
||||
line: '$vexe -cc gcc -keepc -freestanding -o str_array vlib/strconv/bare/str_array_example.v'
|
||||
okmsg: 'V can compile & allocate memory with -freestanding on Linux with GCC.'
|
||||
rmfile: 'str_array'
|
||||
}
|
||||
}
|
||||
res << Command{
|
||||
line: '$vexe $vargs -progress test-cleancode'
|
||||
|
|
|
@ -292,13 +292,9 @@ pub fn malloc(n int) &byte {
|
|||
res = C.GC_MALLOC(n)
|
||||
}
|
||||
} $else $if freestanding {
|
||||
mut e := Errno{}
|
||||
res, e = mm_alloc(u64(n))
|
||||
if e != .enoerror {
|
||||
eprint('malloc($n) failed: ')
|
||||
eprintln(e.str())
|
||||
panic('malloc($n) failed')
|
||||
}
|
||||
// todo: is this safe to call malloc there? We export __malloc as malloc and it uses dlmalloc behind the scenes
|
||||
// so theoretically it is safe
|
||||
res = unsafe { __malloc(usize(n)) }
|
||||
} $else {
|
||||
res = unsafe { C.malloc(n) }
|
||||
}
|
||||
|
@ -345,13 +341,7 @@ pub fn malloc_noscan(n int) &byte {
|
|||
}
|
||||
}
|
||||
} $else $if freestanding {
|
||||
mut e := Errno{}
|
||||
res, e = mm_alloc(u64(n))
|
||||
if e != .enoerror {
|
||||
eprint('malloc_noscan($n) failed: ')
|
||||
eprintln(e.str())
|
||||
panic('malloc_noscan($n) failed')
|
||||
}
|
||||
res = unsafe { __malloc(usize(n)) }
|
||||
} $else {
|
||||
res = unsafe { C.malloc(n) }
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
module builtin
|
||||
|
||||
import dlmalloc
|
||||
|
||||
__global global_allocator dlmalloc.Dlmalloc
|
||||
|
||||
[unsafe]
|
||||
pub fn memcpy(dest &C.void, src &C.void, n usize) &C.void {
|
||||
dest_ := unsafe { &byte(dest) }
|
||||
|
@ -15,7 +19,7 @@ pub fn memcpy(dest &C.void, src &C.void, n usize) &C.void {
|
|||
[export: 'malloc']
|
||||
[unsafe]
|
||||
fn __malloc(n usize) &C.void {
|
||||
return unsafe { malloc(int(n)) }
|
||||
return unsafe { global_allocator.malloc(n) }
|
||||
}
|
||||
|
||||
[unsafe]
|
||||
|
@ -107,10 +111,14 @@ fn memcmp(a &C.void, b &C.void, n usize) int {
|
|||
[export: 'free']
|
||||
[unsafe]
|
||||
fn __free(ptr &C.void) {
|
||||
/*
|
||||
err := mm_free(ptr)
|
||||
if err != .enoerror {
|
||||
eprintln('free error:')
|
||||
panic(err)
|
||||
}*/
|
||||
unsafe {
|
||||
global_allocator.free_(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,7 +135,7 @@ fn bare_read(buf &byte, count u64) (i64, Errno) {
|
|||
return sys_read(0, buf, count)
|
||||
}
|
||||
|
||||
fn bare_print(buf &byte, len u64) {
|
||||
pub fn bare_print(buf &byte, len u64) {
|
||||
sys_write(1, buf, len)
|
||||
}
|
||||
|
||||
|
@ -160,3 +168,7 @@ fn __exit(code int) {
|
|||
fn __qsort(base voidptr, nmemb usize, size usize, sort_cb FnSortCB) {
|
||||
panic('qsort() is not yet implemented in `-freestanding`')
|
||||
}
|
||||
|
||||
fn init_global_allocator() {
|
||||
global_allocator = dlmalloc.new(get_linux_allocator())
|
||||
}
|
||||
|
|
|
@ -262,6 +262,13 @@ fn sys_munmap(addr voidptr, len u64) Errno {
|
|||
return Errno(-sys_call2(11, u64(addr), len))
|
||||
}
|
||||
|
||||
// 25 sys_mremap
|
||||
fn sys_mremap(old_addr voidptr, old_len u64, new_len u64, flags u64) (&byte, Errno) {
|
||||
rc := sys_call4(25, u64(old_addr), old_len, new_len, flags)
|
||||
a, e := split_int_errno(rc)
|
||||
return &byte(a), e
|
||||
}
|
||||
|
||||
// 22 sys_pipe
|
||||
fn sys_pipe(filedes &int) Errno {
|
||||
return Errno(sys_call1(22, u64(filedes)))
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
module builtin
|
||||
|
||||
import dlmalloc
|
||||
|
||||
fn mm_alloc(size u64) (&byte, Errno) {
|
||||
// BEGIN CONSTS
|
||||
// the constants need to be here, since the initialization of other constants,
|
||||
|
@ -27,3 +29,64 @@ fn mm_free(addr &byte) Errno {
|
|||
return sys_munmap(addr - sizeof(u64), size + sizeof(u64))
|
||||
}
|
||||
}
|
||||
|
||||
fn system_alloc(_ voidptr, size usize) (voidptr, usize, u32) {
|
||||
// BEGIN CONSTS
|
||||
// the constants need to be here, since the initialization of other constants,
|
||||
// which happen before these ones would, require malloc
|
||||
mem_prot := MemProt(int(MemProt.prot_read) | int(MemProt.prot_write))
|
||||
map_flags := MapFlags(int(MapFlags.map_private) | int(MapFlags.map_anonymous))
|
||||
// END CONSTS
|
||||
|
||||
a, e := sys_mmap(&byte(0), u64(size + sizeof(u64)), mem_prot, map_flags, -1, 0)
|
||||
|
||||
if e == .enoerror {
|
||||
return a, size, 0
|
||||
}
|
||||
return voidptr(0), 0, 0
|
||||
}
|
||||
|
||||
fn system_remap(_ voidptr, ptr voidptr, oldsize usize, newsize usize, can_move bool) voidptr {
|
||||
return voidptr(0)
|
||||
}
|
||||
|
||||
fn system_free_part(_ voidptr, ptr voidptr, oldsize usize, newsize usize) bool {
|
||||
_, e := sys_mremap(ptr, u64(oldsize), u64(newsize), 0)
|
||||
if e == .enoerror {
|
||||
return true
|
||||
}
|
||||
e2 := sys_munmap(voidptr(usize(ptr) + newsize), u64(oldsize - newsize))
|
||||
|
||||
return e2 == .enoerror
|
||||
}
|
||||
|
||||
fn system_free(_ voidptr, ptr voidptr, size usize) bool {
|
||||
unsafe {
|
||||
return sys_munmap(ptr, u64(size)) == .enoerror
|
||||
}
|
||||
}
|
||||
|
||||
fn system_can_release_part(_ voidptr, _ u32) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
fn system_allocates_zeros(_ voidptr) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
fn system_page_size(_ voidptr) usize {
|
||||
return 4096
|
||||
}
|
||||
|
||||
fn get_linux_allocator() dlmalloc.Allocator {
|
||||
return dlmalloc.Allocator{
|
||||
alloc: system_alloc
|
||||
remap: system_remap
|
||||
free_part: system_free_part
|
||||
free_: system_free
|
||||
can_release_part: system_can_release_part
|
||||
allocates_zeros: system_allocates_zeros
|
||||
page_size: system_page_size
|
||||
data: voidptr(0)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,22 +21,8 @@ pub const (
|
|||
n_tree_bins = 32
|
||||
small_bin_shift = 3
|
||||
tree_bin_shift = 8
|
||||
default_granularity = 64 * 1024
|
||||
default_trim_threshold = 2 * 1024 * 1024
|
||||
|
||||
max_release_check_rate = 4095
|
||||
malloc_alignment = sizeof(usize) * 2
|
||||
chunk_overhead = sizeof(usize)
|
||||
mmap_chnk_overhead = 2 * sizeof(usize)
|
||||
min_large_size = 1 << tree_bin_shift
|
||||
max_small_size = min_large_size - 1
|
||||
max_small_request = max_small_size - (malloc_alignment - 1) - chunk_overhead
|
||||
min_chunk_size = align_up(sizeof(Chunk), malloc_alignment)
|
||||
chunk_mem_offset = 2 * sizeof(usize)
|
||||
min_request = min_chunk_size - chunk_overhead - 1
|
||||
top_foot_size = align_offset_usize(chunk_mem_offset) + pad_request(sizeof(Segment)) +
|
||||
min_chunk_size
|
||||
max_request = calc_max_request()
|
||||
mmap_foot_pad = 4 * sizeof(usize)
|
||||
)
|
||||
|
||||
fn usize_leading_zeros(x usize) usize {
|
||||
|
@ -47,14 +33,85 @@ fn usize_leading_zeros(x usize) usize {
|
|||
}
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn default_granularity() usize {
|
||||
return 64 * 1024
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn default_trim_threshold() usize {
|
||||
return 2 * 1024 * 1024
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn malloc_alignment() usize {
|
||||
return sizeof(usize) * 2
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn chunk_overhead() usize {
|
||||
return sizeof(usize)
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn min_large_size() usize {
|
||||
return 1 << dlmalloc.tree_bin_shift
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn mmap_chunk_overhead() usize {
|
||||
return 2 * sizeof(usize)
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn max_small_size() usize {
|
||||
return min_large_size() - 1
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn max_small_request() usize {
|
||||
return max_small_size() - (malloc_alignment() - 1) - chunk_overhead()
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn min_chunk_size() usize {
|
||||
return align_up(sizeof(Chunk), malloc_alignment())
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn chunk_mem_offset() usize {
|
||||
return 2 * sizeof(usize)
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn min_request() usize {
|
||||
return min_chunk_size() - chunk_overhead() - 1
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn top_foot_size() usize {
|
||||
return align_offset_usize(chunk_mem_offset()) + pad_request(sizeof(Segment)) + min_chunk_size()
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn max_request() usize {
|
||||
return calc_max_request()
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn mmap_foot_pad() usize {
|
||||
return 4 * sizeof(usize)
|
||||
}
|
||||
|
||||
fn min_sys_alloc_space() usize {
|
||||
return ((~0 - (dlmalloc.default_granularity + dlmalloc.top_foot_size +
|
||||
dlmalloc.malloc_alignment) + 1) & ~dlmalloc.malloc_alignment) - dlmalloc.chunk_overhead + 1
|
||||
return ((~0 - (default_granularity() + top_foot_size() + malloc_alignment()) +
|
||||
1) & ~malloc_alignment()) - chunk_overhead() + 1
|
||||
}
|
||||
|
||||
fn calc_max_request() usize {
|
||||
x := min_sys_alloc_space()
|
||||
y := (~dlmalloc.min_chunk_size + 1) << 2
|
||||
y := (~min_chunk_size() + 1) << 2
|
||||
|
||||
if x < y {
|
||||
return x
|
||||
} else {
|
||||
|
@ -63,15 +120,15 @@ fn calc_max_request() usize {
|
|||
}
|
||||
|
||||
fn pad_request(amt usize) usize {
|
||||
return align_up(amt + dlmalloc.chunk_overhead, dlmalloc.malloc_alignment)
|
||||
return align_up(amt + chunk_overhead(), malloc_alignment())
|
||||
}
|
||||
|
||||
fn align_offset_usize(addr usize) usize {
|
||||
return align_up(addr, dlmalloc.malloc_alignment) - addr
|
||||
return align_up(addr, malloc_alignment()) - addr
|
||||
}
|
||||
|
||||
fn is_aligned(a usize) bool {
|
||||
return a & (dlmalloc.malloc_alignment - 1) == 0
|
||||
return a & (malloc_alignment() - 1) == 0
|
||||
}
|
||||
|
||||
fn is_small(s usize) bool {
|
||||
|
@ -110,13 +167,13 @@ fn leftshift_for_tree_index(x u32) u32 {
|
|||
[unsafe]
|
||||
fn align_as_chunk(ptr_ voidptr) &Chunk {
|
||||
ptr := usize(ptr_)
|
||||
chunk := ptr + dlmalloc.chunk_mem_offset
|
||||
chunk := ptr + chunk_mem_offset()
|
||||
return &Chunk(ptr + align_offset_usize(chunk))
|
||||
}
|
||||
|
||||
fn request_2_size(req usize) usize {
|
||||
if req < dlmalloc.min_request {
|
||||
return dlmalloc.min_chunk_size
|
||||
if req < min_request() {
|
||||
return min_chunk_size()
|
||||
} else {
|
||||
return pad_request(req)
|
||||
}
|
||||
|
@ -124,9 +181,9 @@ fn request_2_size(req usize) usize {
|
|||
|
||||
fn overhead_for(c &Chunk) usize {
|
||||
if c.mmapped() {
|
||||
return dlmalloc.mmap_chnk_overhead
|
||||
return mmap_chunk_overhead()
|
||||
} else {
|
||||
return dlmalloc.chunk_overhead
|
||||
return chunk_overhead()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,6 +205,7 @@ pub struct Allocator {
|
|||
|
||||
pub struct Dlmalloc {
|
||||
system_allocator Allocator
|
||||
max_request usize = 4294901657
|
||||
mut:
|
||||
// bin maps
|
||||
smallmap u32 // bin map for small bins
|
||||
|
@ -157,8 +215,8 @@ mut:
|
|||
treebins [n_tree_bins]&TreeChunk
|
||||
dvsize usize
|
||||
topsize usize
|
||||
dv &Chunk
|
||||
top &Chunk
|
||||
dv &Chunk = voidptr(0)
|
||||
top &Chunk = voidptr(0)
|
||||
footprint usize
|
||||
max_footprint usize
|
||||
seg Segment
|
||||
|
@ -184,6 +242,7 @@ pub fn new(system_allocator Allocator) Dlmalloc {
|
|||
least_addr: voidptr(0)
|
||||
release_checks: 0
|
||||
system_allocator: system_allocator
|
||||
max_request: 4294901657
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,14 +274,17 @@ mut:
|
|||
}
|
||||
|
||||
const (
|
||||
pinuse = 1 << 0
|
||||
cinuse = 1 << 1
|
||||
flag4 = 1 << 2
|
||||
inuse = pinuse | cinuse
|
||||
flag_bits = pinuse | cinuse | flag4
|
||||
fencepost_head = inuse | sizeof(usize)
|
||||
pinuse = 1 << 0
|
||||
cinuse = 1 << 1
|
||||
flag4 = 1 << 2
|
||||
inuse = pinuse | cinuse
|
||||
flag_bits = pinuse | cinuse | flag4
|
||||
)
|
||||
|
||||
fn fencepost_head() usize {
|
||||
return dlmalloc.inuse | sizeof(usize)
|
||||
}
|
||||
|
||||
fn (c &Chunk) size() usize {
|
||||
return c.head & ~dlmalloc.flag_bits
|
||||
}
|
||||
|
@ -300,12 +362,12 @@ fn (c &Chunk) minus_offset(offset usize) &Chunk {
|
|||
}
|
||||
|
||||
fn (c &Chunk) to_mem() voidptr {
|
||||
return voidptr(usize(c) + dlmalloc.chunk_mem_offset)
|
||||
return voidptr(usize(c) + chunk_mem_offset())
|
||||
}
|
||||
|
||||
fn chunk_from_mem(mem_ voidptr) &Chunk {
|
||||
mem := usize(mem_)
|
||||
return &Chunk((mem - dlmalloc.chunk_mem_offset))
|
||||
return &Chunk((mem - chunk_mem_offset()))
|
||||
}
|
||||
|
||||
fn (tree &TreeChunk) leftmost_child() &TreeChunk {
|
||||
|
@ -485,6 +547,7 @@ fn (mut dl Dlmalloc) unlink_large_chunk(chunk_ &TreeChunk) {
|
|||
fn (mut dl Dlmalloc) unlink_first_small_chunk(head_ &Chunk, next_ &Chunk, idx u32) {
|
||||
mut next := next_
|
||||
mut head := head_
|
||||
|
||||
mut ptr := next.prev
|
||||
if head == ptr {
|
||||
unsafe { dl.clear_smallmap(idx) }
|
||||
|
@ -512,12 +575,15 @@ pub fn (mut dl Dlmalloc) calloc(size usize) voidptr {
|
|||
pub fn (mut dl Dlmalloc) free_(mem voidptr) {
|
||||
unsafe {
|
||||
mut p := chunk_from_mem(mem)
|
||||
|
||||
mut psize := p.size()
|
||||
next := p.plus_offset(psize)
|
||||
|
||||
if !p.pinuse() {
|
||||
prevsize := p.prev_foot
|
||||
|
||||
if p.mmapped() {
|
||||
psize += prevsize + dlmalloc.mmap_foot_pad
|
||||
psize += prevsize + mmap_foot_pad()
|
||||
if dl.system_allocator.free_(dl.system_allocator.data, voidptr(usize(p) - prevsize),
|
||||
psize)
|
||||
{
|
||||
|
@ -531,7 +597,7 @@ pub fn (mut dl Dlmalloc) free_(mem voidptr) {
|
|||
p = prev
|
||||
if voidptr(p) != voidptr(dl.dv) {
|
||||
dl.unlink_chunk(p, prevsize)
|
||||
} else if next.head & dlmalloc.inuse == dlmalloc.inuse {
|
||||
} else if (next.head & dlmalloc.inuse) == dlmalloc.inuse {
|
||||
dl.dvsize = psize
|
||||
p.set_free_with_pinuse(psize, next)
|
||||
return
|
||||
|
@ -596,10 +662,10 @@ fn (mut dl Dlmalloc) sys_trim(pad_ usize) bool {
|
|||
unsafe {
|
||||
mut pad := pad_
|
||||
mut released := usize(0)
|
||||
if pad < dlmalloc.max_request && !isnil(dl.top) {
|
||||
pad += dlmalloc.top_foot_size
|
||||
if pad < dl.max_request && !isnil(dl.top) {
|
||||
pad += top_foot_size()
|
||||
if dl.topsize > pad {
|
||||
unit := usize(dlmalloc.default_granularity)
|
||||
unit := usize(default_granularity)
|
||||
extra := ((dl.topsize - pad + unit - 1) / unit - 1) * unit
|
||||
mut sp := dl.segment_holding(dl.top)
|
||||
|
||||
|
@ -653,7 +719,7 @@ fn (mut dl Dlmalloc) release_unused_segments() usize {
|
|||
mut p := align_as_chunk(base)
|
||||
psize := p.size()
|
||||
chunk_top := voidptr(usize(p) + psize)
|
||||
top := voidptr(usize(base) + (size - dlmalloc.top_foot_size))
|
||||
top := voidptr(usize(base) + (size - top_foot_size()))
|
||||
if !p.inuse() && chunk_top >= top {
|
||||
mut tp := &TreeChunk(p)
|
||||
if voidptr(p) == voidptr(dl.dv) {
|
||||
|
@ -734,6 +800,9 @@ fn (mut dl Dlmalloc) insert_small_chunk(chunk_ &Chunk, size usize) {
|
|||
} else {
|
||||
f = head.prev
|
||||
}
|
||||
|
||||
assert !isnil(f)
|
||||
assert !isnil(head)
|
||||
head.prev = chunk
|
||||
f.next = chunk
|
||||
chunk.prev = f
|
||||
|
@ -757,6 +826,7 @@ fn (mut dl Dlmalloc) insert_large_chunk(chunk_ &TreeChunk, size usize) {
|
|||
dl.mark_treemap(idx)
|
||||
*h = chunk
|
||||
chunk.parent = voidptr(h)
|
||||
assert !isnil(chunkc)
|
||||
chunkc.prev = chunkc
|
||||
chunkc.next = chunkc
|
||||
} else {
|
||||
|
@ -780,6 +850,7 @@ fn (mut dl Dlmalloc) insert_large_chunk(chunk_ &TreeChunk, size usize) {
|
|||
tc := t.chunk()
|
||||
f := tc.prev
|
||||
f.next = chunkc
|
||||
assert !isnil(chunkc)
|
||||
tc.prev = chunkc
|
||||
chunkc.prev = f
|
||||
chunkc.next = tc
|
||||
|
@ -825,8 +896,9 @@ fn (mut dl Dlmalloc) treemap_is_marked(idx u32) bool {
|
|||
[unsafe]
|
||||
pub fn (mut dl Dlmalloc) malloc(size usize) voidptr {
|
||||
mut nb := usize(0)
|
||||
|
||||
unsafe {
|
||||
if size <= dlmalloc.max_small_request {
|
||||
if size <= max_small_request() {
|
||||
nb = request_2_size(size)
|
||||
mut idx := small_index(nb)
|
||||
smallbits := dl.smallmap >> idx
|
||||
|
@ -854,7 +926,7 @@ pub fn (mut dl Dlmalloc) malloc(size usize) voidptr {
|
|||
dl.unlink_first_small_chunk(b, p, i)
|
||||
smallsize := small_index2size(i)
|
||||
rsize := smallsize - nb
|
||||
if sizeof(usize) != 4 && rsize < dlmalloc.min_chunk_size {
|
||||
if sizeof(usize) != 4 && rsize < min_chunk_size() {
|
||||
p.set_inuse_and_pinuse(smallsize)
|
||||
} else {
|
||||
p.set_size_and_pinuse_of_inuse_chunk(nb)
|
||||
|
@ -871,7 +943,7 @@ pub fn (mut dl Dlmalloc) malloc(size usize) voidptr {
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if size >= dlmalloc.max_request {
|
||||
} else if size >= dl.max_request {
|
||||
return voidptr(0)
|
||||
} else {
|
||||
nb = pad_request(size)
|
||||
|
@ -882,12 +954,13 @@ pub fn (mut dl Dlmalloc) malloc(size usize) voidptr {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// use the `dv` node if we can, splitting it if necessary or otherwise
|
||||
// exhausting the entire chunk
|
||||
if nb <= dl.dvsize {
|
||||
rsize := dl.dvsize - nb
|
||||
mut p := dl.dv
|
||||
if rsize >= dlmalloc.min_chunk_size {
|
||||
if rsize >= min_chunk_size() {
|
||||
dl.dv = p.plus_offset(nb)
|
||||
dl.dvsize = rsize
|
||||
mut r := dl.dv
|
||||
|
@ -899,6 +972,7 @@ pub fn (mut dl Dlmalloc) malloc(size usize) voidptr {
|
|||
dl.dv = voidptr(0)
|
||||
p.set_inuse_and_pinuse(dvs)
|
||||
}
|
||||
|
||||
return p.to_mem()
|
||||
}
|
||||
// Split the top node if we can
|
||||
|
@ -910,8 +984,10 @@ pub fn (mut dl Dlmalloc) malloc(size usize) voidptr {
|
|||
mut r := dl.top
|
||||
r.head = rsize | dlmalloc.pinuse
|
||||
p.set_size_and_pinuse_of_inuse_chunk(nb)
|
||||
|
||||
return p.to_mem()
|
||||
}
|
||||
|
||||
return dl.sys_alloc(nb)
|
||||
}
|
||||
}
|
||||
|
@ -937,19 +1013,20 @@ fn (mut dl Dlmalloc) init_top(ptr &Chunk, size_ usize) {
|
|||
dl.topsize = size
|
||||
p.head = size | dlmalloc.pinuse
|
||||
|
||||
p.plus_offset(size).head = dlmalloc.top_foot_size
|
||||
dl.trim_check = dlmalloc.default_trim_threshold
|
||||
p.plus_offset(size).head = top_foot_size()
|
||||
dl.trim_check = u32(default_trim_threshold())
|
||||
}
|
||||
|
||||
[unsafe]
|
||||
fn (mut dl Dlmalloc) sys_alloc(size usize) voidptr {
|
||||
asize := align_up(size + dlmalloc.top_foot_size + dlmalloc.malloc_alignment, dlmalloc.default_granularity)
|
||||
asize := align_up(size + top_foot_size() + malloc_alignment(), default_granularity())
|
||||
unsafe {
|
||||
tbase, mut tsize, flags := dl.system_allocator.alloc(dl.system_allocator.data,
|
||||
asize)
|
||||
if isnil(tbase) {
|
||||
return tbase
|
||||
}
|
||||
|
||||
dl.footprint += tsize
|
||||
dl.max_footprint = if dl.max_footprint > dl.footprint {
|
||||
dl.max_footprint
|
||||
|
@ -965,7 +1042,7 @@ fn (mut dl Dlmalloc) sys_alloc(size usize) voidptr {
|
|||
dl.seg.flags = flags
|
||||
dl.release_checks = dlmalloc.max_release_check_rate
|
||||
dl.init_bins()
|
||||
tsize_ := tsize - dlmalloc.top_foot_size
|
||||
tsize_ := tsize - top_foot_size()
|
||||
dl.init_top(&Chunk(tbase), tsize_)
|
||||
} else {
|
||||
mut sp := &dl.seg
|
||||
|
@ -1037,7 +1114,7 @@ fn (mut dl Dlmalloc) tmalloc_small(size usize) voidptr {
|
|||
|
||||
mut vc := v.chunk()
|
||||
r := &TreeChunk(vc.plus_offset(size))
|
||||
if rsize < dlmalloc.min_chunk_size {
|
||||
if rsize < min_chunk_size() {
|
||||
vc.set_inuse_and_pinuse(rsize + size)
|
||||
} else {
|
||||
mut rc := r.chunk()
|
||||
|
@ -1107,7 +1184,7 @@ fn (mut dl Dlmalloc) tmalloc_large(size usize) voidptr {
|
|||
mut vc := v.chunk()
|
||||
mut r := vc.plus_offset(size)
|
||||
dl.unlink_large_chunk(v)
|
||||
if rsize < dlmalloc.min_chunk_size {
|
||||
if rsize < min_chunk_size() {
|
||||
vc.set_inuse_and_pinuse(rsize + size)
|
||||
} else {
|
||||
vc.set_size_and_pinuse_of_inuse_chunk(size)
|
||||
|
@ -1163,18 +1240,18 @@ fn (mut dl Dlmalloc) add_segment(tbase voidptr, tsize usize, flags u32) {
|
|||
mut oldsp := dl.segment_holding(old_top)
|
||||
old_end := oldsp.top()
|
||||
ssize := pad_request(sizeof(Segment))
|
||||
mut offset := ssize + sizeof(usize) * 4 + dlmalloc.malloc_alignment - 1
|
||||
mut offset := ssize + sizeof(usize) * 4 + malloc_alignment() - 1
|
||||
rawsp := voidptr(usize(old_end) - offset)
|
||||
offset = align_offset_usize((&Chunk(rawsp)).to_mem())
|
||||
asp := voidptr(usize(rawsp) + offset)
|
||||
csp := if asp < voidptr(usize(old_top) + dlmalloc.min_chunk_size) { old_top } else { asp }
|
||||
csp := if asp < voidptr(usize(old_top) + min_chunk_size()) { old_top } else { asp }
|
||||
mut sp := &Chunk(csp)
|
||||
mut ss := &Segment(sp.to_mem())
|
||||
mut tnext := sp.plus_offset(ssize)
|
||||
mut p := tnext
|
||||
mut nfences := 0
|
||||
|
||||
size := tsize - dlmalloc.top_foot_size
|
||||
size := tsize - top_foot_size()
|
||||
dl.init_top(&Chunk(tbase), size)
|
||||
|
||||
sp.set_size_and_pinuse_of_inuse_chunk(ssize)
|
||||
|
@ -1186,7 +1263,7 @@ fn (mut dl Dlmalloc) add_segment(tbase voidptr, tsize usize, flags u32) {
|
|||
|
||||
for {
|
||||
nextp := p.plus_offset(sizeof(usize))
|
||||
p.head = dlmalloc.fencepost_head
|
||||
p.head = fencepost_head()
|
||||
nfences += 1
|
||||
if nextp.head < old_end {
|
||||
p = nextp
|
||||
|
@ -1222,7 +1299,7 @@ fn (mut dl Dlmalloc) segment_holding(ptr voidptr) &Segment {
|
|||
// realloc behaves as libc realloc, but operates within the given space
|
||||
[unsafe]
|
||||
pub fn (mut dl Dlmalloc) realloc(oldmem voidptr, bytes usize) voidptr {
|
||||
if bytes >= dlmalloc.max_request {
|
||||
if bytes >= dl.max_request {
|
||||
return voidptr(0)
|
||||
}
|
||||
unsafe {
|
||||
|
@ -1249,16 +1326,16 @@ pub fn (mut dl Dlmalloc) realloc(oldmem voidptr, bytes usize) voidptr {
|
|||
[unsafe]
|
||||
pub fn (mut dl Dlmalloc) memalign(alignment_ usize, bytes usize) voidptr {
|
||||
mut alignment := alignment_
|
||||
if alignment < dlmalloc.min_chunk_size {
|
||||
alignment = dlmalloc.min_chunk_size
|
||||
if alignment < min_chunk_size() {
|
||||
alignment = min_chunk_size()
|
||||
}
|
||||
|
||||
if bytes >= dlmalloc.max_request - alignment {
|
||||
if bytes >= max_request() - alignment {
|
||||
return voidptr(0)
|
||||
}
|
||||
unsafe {
|
||||
nb := request_2_size(bytes)
|
||||
req := nb + alignment + dlmalloc.min_chunk_size - dlmalloc.chunk_overhead
|
||||
req := nb + alignment + min_chunk_size() - chunk_overhead()
|
||||
mem := dl.malloc(req)
|
||||
if isnil(mem) {
|
||||
return mem
|
||||
|
@ -1274,7 +1351,7 @@ pub fn (mut dl Dlmalloc) memalign(alignment_ usize, bytes usize) voidptr {
|
|||
br_ := (usize(mem) + alignment - 1) & (~alignment + 1)
|
||||
br := chunk_from_mem(voidptr(br_))
|
||||
mut pos := voidptr(0)
|
||||
if usize(br) - usize(p) > dlmalloc.min_chunk_size {
|
||||
if usize(br) - usize(p) > min_chunk_size() {
|
||||
pos = voidptr(br)
|
||||
} else {
|
||||
pos = voidptr(usize(br) + alignment)
|
||||
|
@ -1297,7 +1374,7 @@ pub fn (mut dl Dlmalloc) memalign(alignment_ usize, bytes usize) voidptr {
|
|||
|
||||
if !p.mmapped() {
|
||||
size := p.size()
|
||||
if size > nb + dlmalloc.min_chunk_size {
|
||||
if size > nb + min_chunk_size() {
|
||||
remainder_size := size - nb
|
||||
mut remainder := p.plus_offset(nb)
|
||||
p.set_inuse(nb)
|
||||
|
@ -1319,7 +1396,7 @@ fn (mut dl Dlmalloc) try_realloc_chunk(p_ &Chunk, nb usize, can_move bool) &Chun
|
|||
return dl.mmap_resize(p, nb, can_move)
|
||||
} else if oldsize >= nb {
|
||||
rsize := oldsize - nb
|
||||
if rsize >= dlmalloc.min_chunk_size {
|
||||
if rsize >= min_chunk_size() {
|
||||
mut r := p.plus_offset(nb)
|
||||
p.set_inuse(nb)
|
||||
r.set_inuse(rsize)
|
||||
|
@ -1346,7 +1423,7 @@ fn (mut dl Dlmalloc) try_realloc_chunk(p_ &Chunk, nb usize, can_move bool) &Chun
|
|||
}
|
||||
|
||||
dsize := oldsize + dvs - nb
|
||||
if dsize >= dlmalloc.min_chunk_size {
|
||||
if dsize >= min_chunk_size() {
|
||||
mut r := p.plus_offset(nb)
|
||||
mut n := r.plus_offset(dsize)
|
||||
p.set_inuse(nb)
|
||||
|
@ -1368,7 +1445,7 @@ fn (mut dl Dlmalloc) try_realloc_chunk(p_ &Chunk, nb usize, can_move bool) &Chun
|
|||
}
|
||||
rsize := oldsize + nextsize - nb
|
||||
dl.unlink_chunk(next, nextsize)
|
||||
if rsize < dlmalloc.min_chunk_size {
|
||||
if rsize < min_chunk_size() {
|
||||
newsize := oldsize + nextsize
|
||||
p.set_inuse(newsize)
|
||||
} else {
|
||||
|
@ -1392,13 +1469,13 @@ fn (mut dl Dlmalloc) mmap_resize(oldp_ &Chunk, nb usize, can_move bool) &Chunk {
|
|||
return voidptr(0)
|
||||
}
|
||||
// Keep the old chunk if it's big enough but not too big
|
||||
if oldsize >= nb + sizeof(usize) && (oldsize - nb) <= (dlmalloc.default_granularity << 1) {
|
||||
if oldsize >= nb + sizeof(usize) && (oldsize - nb) <= (default_granularity() << 1) {
|
||||
return oldp
|
||||
}
|
||||
|
||||
offset := oldp.prev_foot
|
||||
oldmmsize := oldsize + offset + dlmalloc.mmap_foot_pad
|
||||
newmmsize := dl.mmap_align(nb + 6 * sizeof(usize) + dlmalloc.malloc_alignment - 1)
|
||||
oldmmsize := oldsize + offset + mmap_foot_pad()
|
||||
newmmsize := dl.mmap_align(nb + 6 * sizeof(usize) + malloc_alignment() - 1)
|
||||
|
||||
ptr := dl.system_allocator.remap(dl.system_allocator.data, voidptr(usize(oldp) - offset),
|
||||
oldmmsize, newmmsize, can_move)
|
||||
|
@ -1407,9 +1484,9 @@ fn (mut dl Dlmalloc) mmap_resize(oldp_ &Chunk, nb usize, can_move bool) &Chunk {
|
|||
}
|
||||
|
||||
mut newp := &Chunk(voidptr(usize(ptr) + offset))
|
||||
psize := newmmsize - offset - dlmalloc.mmap_foot_pad
|
||||
psize := newmmsize - offset - mmap_foot_pad()
|
||||
newp.head = psize
|
||||
newp.plus_offset(psize).head = dlmalloc.fencepost_head
|
||||
newp.plus_offset(psize).head = fencepost_head()
|
||||
newp.plus_offset(psize + sizeof(usize)).head = 0
|
||||
if ptr < dl.least_addr {
|
||||
dl.least_addr = ptr
|
||||
|
@ -1434,7 +1511,7 @@ fn (mut dl Dlmalloc) dispose_chunk(p_ &Chunk, psize_ usize) {
|
|||
if !p.pinuse() {
|
||||
prevsize := p.prev_foot
|
||||
if p.mmapped() {
|
||||
psize += prevsize + dlmalloc.mmap_foot_pad
|
||||
psize += prevsize + mmap_foot_pad()
|
||||
|
||||
if dl.system_allocator.free_(dl.system_allocator.data, voidptr(usize(p) - prevsize),
|
||||
psize)
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
module dlmalloc
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
$if !freestanding {
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
}
|
||||
fn C.munmap(ptr voidptr, size usize) int
|
||||
fn C.mremap(ptr voidptr, old usize, new usize, flags usize) voidptr
|
||||
fn C.mmap(base voidptr, len usize, prot int, flags int, fd int, offset i64) voidptr
|
||||
|
@ -50,17 +51,22 @@ enum MapFlags {
|
|||
}
|
||||
|
||||
fn system_alloc(_ voidptr, size usize) (voidptr, usize, u32) {
|
||||
unsafe {
|
||||
mem_prot := MemProt(int(MemProt.prot_read) | int(MemProt.prot_write))
|
||||
map_flags := MapFlags(int(MapFlags.map_private) | int(MapFlags.map_anonymous))
|
||||
addr := C.mmap(voidptr(0), size, int(mem_prot), int(map_flags), -1, 0)
|
||||
$if !freestanding {
|
||||
unsafe {
|
||||
mem_prot := MemProt(int(MemProt.prot_read) | int(MemProt.prot_write))
|
||||
map_flags := MapFlags(int(MapFlags.map_private) | int(MapFlags.map_anonymous))
|
||||
addr := C.mmap(voidptr(0), size, int(mem_prot), int(map_flags), -1, 0)
|
||||
|
||||
if addr == voidptr(-1) {
|
||||
return voidptr(0), 0, 0
|
||||
} else {
|
||||
return addr, size, 0
|
||||
if addr == voidptr(-1) {
|
||||
return voidptr(0), 0, 0
|
||||
} else {
|
||||
return addr, size, 0
|
||||
}
|
||||
}
|
||||
} $else {
|
||||
return voidptr(0), 0, 0
|
||||
}
|
||||
return voidptr(0), 0, 0
|
||||
}
|
||||
|
||||
fn system_remap(_ voidptr, ptr voidptr, oldsize usize, newsize usize, can_move bool) voidptr {
|
||||
|
@ -68,7 +74,7 @@ fn system_remap(_ voidptr, ptr voidptr, oldsize usize, newsize usize, can_move b
|
|||
}
|
||||
|
||||
fn system_free_part(_ voidptr, ptr voidptr, oldsize usize, newsize usize) bool {
|
||||
$if linux {
|
||||
$if linux && !freestanding {
|
||||
unsafe {
|
||||
rc := C.mremap(ptr, oldsize, newsize, 0)
|
||||
if rc != voidptr(-1) {
|
||||
|
@ -76,7 +82,7 @@ fn system_free_part(_ voidptr, ptr voidptr, oldsize usize, newsize usize) bool {
|
|||
}
|
||||
return C.munmap(voidptr(usize(ptr) + newsize), oldsize - newsize) == 0
|
||||
}
|
||||
} $else $if macos {
|
||||
} $else $if macos && !freestanding {
|
||||
unsafe {
|
||||
return C.munmap(voidptr(usize(ptr) + newsize), oldsize - newsize) == 0
|
||||
}
|
||||
|
@ -85,8 +91,12 @@ fn system_free_part(_ voidptr, ptr voidptr, oldsize usize, newsize usize) bool {
|
|||
}
|
||||
|
||||
fn system_free(_ voidptr, ptr voidptr, size usize) bool {
|
||||
unsafe {
|
||||
return C.munmap(ptr, size) == 0
|
||||
$if !freestanding {
|
||||
unsafe {
|
||||
return C.munmap(ptr, size) == 0
|
||||
}
|
||||
} $else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ pub fn realloc(ptr voidptr, oldsize usize, newsize usize) voidptr {
|
|||
[unsafe]
|
||||
pub fn memalign(size usize, align usize) voidptr {
|
||||
unsafe {
|
||||
if align <= malloc_alignment {
|
||||
if align <= malloc_alignment() {
|
||||
return global.malloc(size)
|
||||
} else {
|
||||
return global.memalign(align, size)
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
fn main() {
|
||||
mut x := []int{cap: 100}
|
||||
x << 42
|
||||
x << 41
|
||||
x << 40
|
||||
println(x)
|
||||
}
|
|
@ -211,7 +211,7 @@ fn (mut b Builder) handle_usecache(vexe string) {
|
|||
// strconv is already imported inside builtin, so skip generating its object file
|
||||
// TODO: incase we have other modules with the same name, make sure they are vlib
|
||||
// is this even doign anything?
|
||||
if imp in ['strconv', 'strings'] {
|
||||
if imp in ['strconv', 'strings', 'dlmalloc'] {
|
||||
continue
|
||||
}
|
||||
if imp in built_modules {
|
||||
|
|
|
@ -1801,7 +1801,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
|||
}
|
||||
ast.Module {
|
||||
// g.is_builtin_mod = node.name == 'builtin'
|
||||
g.is_builtin_mod = node.name in ['builtin', 'strconv', 'strings']
|
||||
g.is_builtin_mod = node.name in ['builtin', 'strconv', 'strings', 'dlmalloc']
|
||||
// g.cur_mod = node.name
|
||||
g.cur_mod = node
|
||||
}
|
||||
|
@ -5229,7 +5229,7 @@ fn (mut g Gen) global_decl(node ast.GlobalDecl) {
|
|||
g.definitions.writeln('$mod$styp $attributes $field.name = {0}; // global')
|
||||
} else {
|
||||
g.definitions.writeln('$mod$styp $attributes $field.name; // global')
|
||||
if field.name !in ['as_cast_type_indexes', 'g_memory_block'] {
|
||||
if field.name !in ['as_cast_type_indexes', 'g_memory_block', 'global_allocator'] {
|
||||
g.global_init.writeln('\t$field.name = *($styp*)&(($styp[]){${g.type_default(field.typ)}}[0]); // global')
|
||||
}
|
||||
}
|
||||
|
@ -5592,6 +5592,10 @@ fn (mut g Gen) write_init_function() {
|
|||
// 11 is SIGSEGV. It is hardcoded here, to avoid FreeBSD compilation errors for trivial examples.
|
||||
g.writeln('#if __STDC_HOSTED__ == 1\n\tsignal(11, v_segmentation_fault_handler);\n#endif')
|
||||
}
|
||||
if g.pref.is_bare {
|
||||
g.writeln('init_global_allocator();')
|
||||
}
|
||||
|
||||
if g.pref.prealloc {
|
||||
g.writeln('prealloc_vinit();')
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import v.util.recompilation
|
|||
|
||||
// math.bits is needed by strconv.ftoa
|
||||
pub const (
|
||||
builtin_module_parts = ['math.bits', 'strconv', 'strconv.ftoa', 'strings', 'builtin']
|
||||
builtin_module_parts = ['math.bits', 'strconv', 'dlmalloc', 'strconv.ftoa', 'strings', 'builtin']
|
||||
bundle_modules = ['clipboard', 'fontstash', 'gg', 'gx', 'sokol', 'szip', 'ui']
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in New Issue