1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

builtin: use dlmalloc for -freestanding (#13054)

This commit is contained in:
playX 2022-01-06 15:10:37 +03:00 committed by GitHub
parent fb66ec7cfb
commit ec91de3504
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 284 additions and 108 deletions

View File

@ -158,6 +158,12 @@ fn get_all_commands() []Command {
okmsg: 'V can compile with -freestanding on Linux with GCC.' okmsg: 'V can compile with -freestanding on Linux with GCC.'
rmfile: 'bel' 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{ res << Command{
line: '$vexe $vargs -progress test-cleancode' line: '$vexe $vargs -progress test-cleancode'

View File

@ -292,13 +292,9 @@ pub fn malloc(n int) &byte {
res = C.GC_MALLOC(n) res = C.GC_MALLOC(n)
} }
} $else $if freestanding { } $else $if freestanding {
mut e := Errno{} // todo: is this safe to call malloc there? We export __malloc as malloc and it uses dlmalloc behind the scenes
res, e = mm_alloc(u64(n)) // so theoretically it is safe
if e != .enoerror { res = unsafe { __malloc(usize(n)) }
eprint('malloc($n) failed: ')
eprintln(e.str())
panic('malloc($n) failed')
}
} $else { } $else {
res = unsafe { C.malloc(n) } res = unsafe { C.malloc(n) }
} }
@ -345,13 +341,7 @@ pub fn malloc_noscan(n int) &byte {
} }
} }
} $else $if freestanding { } $else $if freestanding {
mut e := Errno{} res = unsafe { __malloc(usize(n)) }
res, e = mm_alloc(u64(n))
if e != .enoerror {
eprint('malloc_noscan($n) failed: ')
eprintln(e.str())
panic('malloc_noscan($n) failed')
}
} $else { } $else {
res = unsafe { C.malloc(n) } res = unsafe { C.malloc(n) }
} }

View File

@ -1,5 +1,9 @@
module builtin module builtin
import dlmalloc
__global global_allocator dlmalloc.Dlmalloc
[unsafe] [unsafe]
pub fn memcpy(dest &C.void, src &C.void, n usize) &C.void { pub fn memcpy(dest &C.void, src &C.void, n usize) &C.void {
dest_ := unsafe { &byte(dest) } dest_ := unsafe { &byte(dest) }
@ -15,7 +19,7 @@ pub fn memcpy(dest &C.void, src &C.void, n usize) &C.void {
[export: 'malloc'] [export: 'malloc']
[unsafe] [unsafe]
fn __malloc(n usize) &C.void { fn __malloc(n usize) &C.void {
return unsafe { malloc(int(n)) } return unsafe { global_allocator.malloc(n) }
} }
[unsafe] [unsafe]
@ -107,10 +111,14 @@ fn memcmp(a &C.void, b &C.void, n usize) int {
[export: 'free'] [export: 'free']
[unsafe] [unsafe]
fn __free(ptr &C.void) { fn __free(ptr &C.void) {
/*
err := mm_free(ptr) err := mm_free(ptr)
if err != .enoerror { if err != .enoerror {
eprintln('free error:') eprintln('free error:')
panic(err) 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) 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) sys_write(1, buf, len)
} }
@ -160,3 +168,7 @@ fn __exit(code int) {
fn __qsort(base voidptr, nmemb usize, size usize, sort_cb FnSortCB) { fn __qsort(base voidptr, nmemb usize, size usize, sort_cb FnSortCB) {
panic('qsort() is not yet implemented in `-freestanding`') panic('qsort() is not yet implemented in `-freestanding`')
} }
fn init_global_allocator() {
global_allocator = dlmalloc.new(get_linux_allocator())
}

View File

@ -262,6 +262,13 @@ fn sys_munmap(addr voidptr, len u64) Errno {
return Errno(-sys_call2(11, u64(addr), len)) 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 // 22 sys_pipe
fn sys_pipe(filedes &int) Errno { fn sys_pipe(filedes &int) Errno {
return Errno(sys_call1(22, u64(filedes))) return Errno(sys_call1(22, u64(filedes)))

View File

@ -1,5 +1,7 @@
module builtin module builtin
import dlmalloc
fn mm_alloc(size u64) (&byte, Errno) { fn mm_alloc(size u64) (&byte, Errno) {
// BEGIN CONSTS // BEGIN CONSTS
// the constants need to be here, since the initialization of other constants, // 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)) 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)
}
}

View File

@ -21,22 +21,8 @@ pub const (
n_tree_bins = 32 n_tree_bins = 32
small_bin_shift = 3 small_bin_shift = 3
tree_bin_shift = 8 tree_bin_shift = 8
default_granularity = 64 * 1024
default_trim_threshold = 2 * 1024 * 1024
max_release_check_rate = 4095 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 { 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 { fn min_sys_alloc_space() usize {
return ((~0 - (dlmalloc.default_granularity + dlmalloc.top_foot_size + return ((~0 - (default_granularity() + top_foot_size() + malloc_alignment()) +
dlmalloc.malloc_alignment) + 1) & ~dlmalloc.malloc_alignment) - dlmalloc.chunk_overhead + 1 1) & ~malloc_alignment()) - chunk_overhead() + 1
} }
fn calc_max_request() usize { fn calc_max_request() usize {
x := min_sys_alloc_space() x := min_sys_alloc_space()
y := (~dlmalloc.min_chunk_size + 1) << 2 y := (~min_chunk_size() + 1) << 2
if x < y { if x < y {
return x return x
} else { } else {
@ -63,15 +120,15 @@ fn calc_max_request() usize {
} }
fn pad_request(amt usize) 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 { 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 { 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 { fn is_small(s usize) bool {
@ -110,13 +167,13 @@ fn leftshift_for_tree_index(x u32) u32 {
[unsafe] [unsafe]
fn align_as_chunk(ptr_ voidptr) &Chunk { fn align_as_chunk(ptr_ voidptr) &Chunk {
ptr := usize(ptr_) ptr := usize(ptr_)
chunk := ptr + dlmalloc.chunk_mem_offset chunk := ptr + chunk_mem_offset()
return &Chunk(ptr + align_offset_usize(chunk)) return &Chunk(ptr + align_offset_usize(chunk))
} }
fn request_2_size(req usize) usize { fn request_2_size(req usize) usize {
if req < dlmalloc.min_request { if req < min_request() {
return dlmalloc.min_chunk_size return min_chunk_size()
} else { } else {
return pad_request(req) return pad_request(req)
} }
@ -124,9 +181,9 @@ fn request_2_size(req usize) usize {
fn overhead_for(c &Chunk) usize { fn overhead_for(c &Chunk) usize {
if c.mmapped() { if c.mmapped() {
return dlmalloc.mmap_chnk_overhead return mmap_chunk_overhead()
} else { } else {
return dlmalloc.chunk_overhead return chunk_overhead()
} }
} }
@ -148,6 +205,7 @@ pub struct Allocator {
pub struct Dlmalloc { pub struct Dlmalloc {
system_allocator Allocator system_allocator Allocator
max_request usize = 4294901657
mut: mut:
// bin maps // bin maps
smallmap u32 // bin map for small bins smallmap u32 // bin map for small bins
@ -157,8 +215,8 @@ mut:
treebins [n_tree_bins]&TreeChunk treebins [n_tree_bins]&TreeChunk
dvsize usize dvsize usize
topsize usize topsize usize
dv &Chunk dv &Chunk = voidptr(0)
top &Chunk top &Chunk = voidptr(0)
footprint usize footprint usize
max_footprint usize max_footprint usize
seg Segment seg Segment
@ -184,6 +242,7 @@ pub fn new(system_allocator Allocator) Dlmalloc {
least_addr: voidptr(0) least_addr: voidptr(0)
release_checks: 0 release_checks: 0
system_allocator: system_allocator system_allocator: system_allocator
max_request: 4294901657
} }
} }
@ -215,14 +274,17 @@ mut:
} }
const ( const (
pinuse = 1 << 0 pinuse = 1 << 0
cinuse = 1 << 1 cinuse = 1 << 1
flag4 = 1 << 2 flag4 = 1 << 2
inuse = pinuse | cinuse inuse = pinuse | cinuse
flag_bits = pinuse | cinuse | flag4 flag_bits = pinuse | cinuse | flag4
fencepost_head = inuse | sizeof(usize)
) )
fn fencepost_head() usize {
return dlmalloc.inuse | sizeof(usize)
}
fn (c &Chunk) size() usize { fn (c &Chunk) size() usize {
return c.head & ~dlmalloc.flag_bits return c.head & ~dlmalloc.flag_bits
} }
@ -300,12 +362,12 @@ fn (c &Chunk) minus_offset(offset usize) &Chunk {
} }
fn (c &Chunk) to_mem() voidptr { 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 { fn chunk_from_mem(mem_ voidptr) &Chunk {
mem := usize(mem_) mem := usize(mem_)
return &Chunk((mem - dlmalloc.chunk_mem_offset)) return &Chunk((mem - chunk_mem_offset()))
} }
fn (tree &TreeChunk) leftmost_child() &TreeChunk { 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) { fn (mut dl Dlmalloc) unlink_first_small_chunk(head_ &Chunk, next_ &Chunk, idx u32) {
mut next := next_ mut next := next_
mut head := head_ mut head := head_
mut ptr := next.prev mut ptr := next.prev
if head == ptr { if head == ptr {
unsafe { dl.clear_smallmap(idx) } 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) { pub fn (mut dl Dlmalloc) free_(mem voidptr) {
unsafe { unsafe {
mut p := chunk_from_mem(mem) mut p := chunk_from_mem(mem)
mut psize := p.size() mut psize := p.size()
next := p.plus_offset(psize) next := p.plus_offset(psize)
if !p.pinuse() { if !p.pinuse() {
prevsize := p.prev_foot prevsize := p.prev_foot
if p.mmapped() { 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), if dl.system_allocator.free_(dl.system_allocator.data, voidptr(usize(p) - prevsize),
psize) psize)
{ {
@ -531,7 +597,7 @@ pub fn (mut dl Dlmalloc) free_(mem voidptr) {
p = prev p = prev
if voidptr(p) != voidptr(dl.dv) { if voidptr(p) != voidptr(dl.dv) {
dl.unlink_chunk(p, prevsize) dl.unlink_chunk(p, prevsize)
} else if next.head & dlmalloc.inuse == dlmalloc.inuse { } else if (next.head & dlmalloc.inuse) == dlmalloc.inuse {
dl.dvsize = psize dl.dvsize = psize
p.set_free_with_pinuse(psize, next) p.set_free_with_pinuse(psize, next)
return return
@ -596,10 +662,10 @@ fn (mut dl Dlmalloc) sys_trim(pad_ usize) bool {
unsafe { unsafe {
mut pad := pad_ mut pad := pad_
mut released := usize(0) mut released := usize(0)
if pad < dlmalloc.max_request && !isnil(dl.top) { if pad < dl.max_request && !isnil(dl.top) {
pad += dlmalloc.top_foot_size pad += top_foot_size()
if dl.topsize > pad { if dl.topsize > pad {
unit := usize(dlmalloc.default_granularity) unit := usize(default_granularity)
extra := ((dl.topsize - pad + unit - 1) / unit - 1) * unit extra := ((dl.topsize - pad + unit - 1) / unit - 1) * unit
mut sp := dl.segment_holding(dl.top) 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) mut p := align_as_chunk(base)
psize := p.size() psize := p.size()
chunk_top := voidptr(usize(p) + psize) 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 { if !p.inuse() && chunk_top >= top {
mut tp := &TreeChunk(p) mut tp := &TreeChunk(p)
if voidptr(p) == voidptr(dl.dv) { if voidptr(p) == voidptr(dl.dv) {
@ -734,6 +800,9 @@ fn (mut dl Dlmalloc) insert_small_chunk(chunk_ &Chunk, size usize) {
} else { } else {
f = head.prev f = head.prev
} }
assert !isnil(f)
assert !isnil(head)
head.prev = chunk head.prev = chunk
f.next = chunk f.next = chunk
chunk.prev = f chunk.prev = f
@ -757,6 +826,7 @@ fn (mut dl Dlmalloc) insert_large_chunk(chunk_ &TreeChunk, size usize) {
dl.mark_treemap(idx) dl.mark_treemap(idx)
*h = chunk *h = chunk
chunk.parent = voidptr(h) chunk.parent = voidptr(h)
assert !isnil(chunkc)
chunkc.prev = chunkc chunkc.prev = chunkc
chunkc.next = chunkc chunkc.next = chunkc
} else { } else {
@ -780,6 +850,7 @@ fn (mut dl Dlmalloc) insert_large_chunk(chunk_ &TreeChunk, size usize) {
tc := t.chunk() tc := t.chunk()
f := tc.prev f := tc.prev
f.next = chunkc f.next = chunkc
assert !isnil(chunkc)
tc.prev = chunkc tc.prev = chunkc
chunkc.prev = f chunkc.prev = f
chunkc.next = tc chunkc.next = tc
@ -825,8 +896,9 @@ fn (mut dl Dlmalloc) treemap_is_marked(idx u32) bool {
[unsafe] [unsafe]
pub fn (mut dl Dlmalloc) malloc(size usize) voidptr { pub fn (mut dl Dlmalloc) malloc(size usize) voidptr {
mut nb := usize(0) mut nb := usize(0)
unsafe { unsafe {
if size <= dlmalloc.max_small_request { if size <= max_small_request() {
nb = request_2_size(size) nb = request_2_size(size)
mut idx := small_index(nb) mut idx := small_index(nb)
smallbits := dl.smallmap >> idx 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) dl.unlink_first_small_chunk(b, p, i)
smallsize := small_index2size(i) smallsize := small_index2size(i)
rsize := smallsize - nb 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) p.set_inuse_and_pinuse(smallsize)
} else { } else {
p.set_size_and_pinuse_of_inuse_chunk(nb) 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) return voidptr(0)
} else { } else {
nb = pad_request(size) 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 // use the `dv` node if we can, splitting it if necessary or otherwise
// exhausting the entire chunk // exhausting the entire chunk
if nb <= dl.dvsize { if nb <= dl.dvsize {
rsize := dl.dvsize - nb rsize := dl.dvsize - nb
mut p := dl.dv mut p := dl.dv
if rsize >= dlmalloc.min_chunk_size { if rsize >= min_chunk_size() {
dl.dv = p.plus_offset(nb) dl.dv = p.plus_offset(nb)
dl.dvsize = rsize dl.dvsize = rsize
mut r := dl.dv mut r := dl.dv
@ -899,6 +972,7 @@ pub fn (mut dl Dlmalloc) malloc(size usize) voidptr {
dl.dv = voidptr(0) dl.dv = voidptr(0)
p.set_inuse_and_pinuse(dvs) p.set_inuse_and_pinuse(dvs)
} }
return p.to_mem() return p.to_mem()
} }
// Split the top node if we can // Split the top node if we can
@ -910,8 +984,10 @@ pub fn (mut dl Dlmalloc) malloc(size usize) voidptr {
mut r := dl.top mut r := dl.top
r.head = rsize | dlmalloc.pinuse r.head = rsize | dlmalloc.pinuse
p.set_size_and_pinuse_of_inuse_chunk(nb) p.set_size_and_pinuse_of_inuse_chunk(nb)
return p.to_mem() return p.to_mem()
} }
return dl.sys_alloc(nb) return dl.sys_alloc(nb)
} }
} }
@ -937,19 +1013,20 @@ fn (mut dl Dlmalloc) init_top(ptr &Chunk, size_ usize) {
dl.topsize = size dl.topsize = size
p.head = size | dlmalloc.pinuse p.head = size | dlmalloc.pinuse
p.plus_offset(size).head = dlmalloc.top_foot_size p.plus_offset(size).head = top_foot_size()
dl.trim_check = dlmalloc.default_trim_threshold dl.trim_check = u32(default_trim_threshold())
} }
[unsafe] [unsafe]
fn (mut dl Dlmalloc) sys_alloc(size usize) voidptr { 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 { unsafe {
tbase, mut tsize, flags := dl.system_allocator.alloc(dl.system_allocator.data, tbase, mut tsize, flags := dl.system_allocator.alloc(dl.system_allocator.data,
asize) asize)
if isnil(tbase) { if isnil(tbase) {
return tbase return tbase
} }
dl.footprint += tsize dl.footprint += tsize
dl.max_footprint = if dl.max_footprint > dl.footprint { dl.max_footprint = if dl.max_footprint > dl.footprint {
dl.max_footprint dl.max_footprint
@ -965,7 +1042,7 @@ fn (mut dl Dlmalloc) sys_alloc(size usize) voidptr {
dl.seg.flags = flags dl.seg.flags = flags
dl.release_checks = dlmalloc.max_release_check_rate dl.release_checks = dlmalloc.max_release_check_rate
dl.init_bins() dl.init_bins()
tsize_ := tsize - dlmalloc.top_foot_size tsize_ := tsize - top_foot_size()
dl.init_top(&Chunk(tbase), tsize_) dl.init_top(&Chunk(tbase), tsize_)
} else { } else {
mut sp := &dl.seg mut sp := &dl.seg
@ -1037,7 +1114,7 @@ fn (mut dl Dlmalloc) tmalloc_small(size usize) voidptr {
mut vc := v.chunk() mut vc := v.chunk()
r := &TreeChunk(vc.plus_offset(size)) r := &TreeChunk(vc.plus_offset(size))
if rsize < dlmalloc.min_chunk_size { if rsize < min_chunk_size() {
vc.set_inuse_and_pinuse(rsize + size) vc.set_inuse_and_pinuse(rsize + size)
} else { } else {
mut rc := r.chunk() mut rc := r.chunk()
@ -1107,7 +1184,7 @@ fn (mut dl Dlmalloc) tmalloc_large(size usize) voidptr {
mut vc := v.chunk() mut vc := v.chunk()
mut r := vc.plus_offset(size) mut r := vc.plus_offset(size)
dl.unlink_large_chunk(v) dl.unlink_large_chunk(v)
if rsize < dlmalloc.min_chunk_size { if rsize < min_chunk_size() {
vc.set_inuse_and_pinuse(rsize + size) vc.set_inuse_and_pinuse(rsize + size)
} else { } else {
vc.set_size_and_pinuse_of_inuse_chunk(size) 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) mut oldsp := dl.segment_holding(old_top)
old_end := oldsp.top() old_end := oldsp.top()
ssize := pad_request(sizeof(Segment)) 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) rawsp := voidptr(usize(old_end) - offset)
offset = align_offset_usize((&Chunk(rawsp)).to_mem()) offset = align_offset_usize((&Chunk(rawsp)).to_mem())
asp := voidptr(usize(rawsp) + offset) 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 sp := &Chunk(csp)
mut ss := &Segment(sp.to_mem()) mut ss := &Segment(sp.to_mem())
mut tnext := sp.plus_offset(ssize) mut tnext := sp.plus_offset(ssize)
mut p := tnext mut p := tnext
mut nfences := 0 mut nfences := 0
size := tsize - dlmalloc.top_foot_size size := tsize - top_foot_size()
dl.init_top(&Chunk(tbase), size) dl.init_top(&Chunk(tbase), size)
sp.set_size_and_pinuse_of_inuse_chunk(ssize) 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 { for {
nextp := p.plus_offset(sizeof(usize)) nextp := p.plus_offset(sizeof(usize))
p.head = dlmalloc.fencepost_head p.head = fencepost_head()
nfences += 1 nfences += 1
if nextp.head < old_end { if nextp.head < old_end {
p = nextp 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 // realloc behaves as libc realloc, but operates within the given space
[unsafe] [unsafe]
pub fn (mut dl Dlmalloc) realloc(oldmem voidptr, bytes usize) voidptr { pub fn (mut dl Dlmalloc) realloc(oldmem voidptr, bytes usize) voidptr {
if bytes >= dlmalloc.max_request { if bytes >= dl.max_request {
return voidptr(0) return voidptr(0)
} }
unsafe { unsafe {
@ -1249,16 +1326,16 @@ pub fn (mut dl Dlmalloc) realloc(oldmem voidptr, bytes usize) voidptr {
[unsafe] [unsafe]
pub fn (mut dl Dlmalloc) memalign(alignment_ usize, bytes usize) voidptr { pub fn (mut dl Dlmalloc) memalign(alignment_ usize, bytes usize) voidptr {
mut alignment := alignment_ mut alignment := alignment_
if alignment < dlmalloc.min_chunk_size { if alignment < min_chunk_size() {
alignment = dlmalloc.min_chunk_size alignment = min_chunk_size()
} }
if bytes >= dlmalloc.max_request - alignment { if bytes >= max_request() - alignment {
return voidptr(0) return voidptr(0)
} }
unsafe { unsafe {
nb := request_2_size(bytes) 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) mem := dl.malloc(req)
if isnil(mem) { if isnil(mem) {
return 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_ := (usize(mem) + alignment - 1) & (~alignment + 1)
br := chunk_from_mem(voidptr(br_)) br := chunk_from_mem(voidptr(br_))
mut pos := voidptr(0) mut pos := voidptr(0)
if usize(br) - usize(p) > dlmalloc.min_chunk_size { if usize(br) - usize(p) > min_chunk_size() {
pos = voidptr(br) pos = voidptr(br)
} else { } else {
pos = voidptr(usize(br) + alignment) pos = voidptr(usize(br) + alignment)
@ -1297,7 +1374,7 @@ pub fn (mut dl Dlmalloc) memalign(alignment_ usize, bytes usize) voidptr {
if !p.mmapped() { if !p.mmapped() {
size := p.size() size := p.size()
if size > nb + dlmalloc.min_chunk_size { if size > nb + min_chunk_size() {
remainder_size := size - nb remainder_size := size - nb
mut remainder := p.plus_offset(nb) mut remainder := p.plus_offset(nb)
p.set_inuse(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) return dl.mmap_resize(p, nb, can_move)
} else if oldsize >= nb { } else if oldsize >= nb {
rsize := oldsize - nb rsize := oldsize - nb
if rsize >= dlmalloc.min_chunk_size { if rsize >= min_chunk_size() {
mut r := p.plus_offset(nb) mut r := p.plus_offset(nb)
p.set_inuse(nb) p.set_inuse(nb)
r.set_inuse(rsize) 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 dsize := oldsize + dvs - nb
if dsize >= dlmalloc.min_chunk_size { if dsize >= min_chunk_size() {
mut r := p.plus_offset(nb) mut r := p.plus_offset(nb)
mut n := r.plus_offset(dsize) mut n := r.plus_offset(dsize)
p.set_inuse(nb) 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 rsize := oldsize + nextsize - nb
dl.unlink_chunk(next, nextsize) dl.unlink_chunk(next, nextsize)
if rsize < dlmalloc.min_chunk_size { if rsize < min_chunk_size() {
newsize := oldsize + nextsize newsize := oldsize + nextsize
p.set_inuse(newsize) p.set_inuse(newsize)
} else { } else {
@ -1392,13 +1469,13 @@ fn (mut dl Dlmalloc) mmap_resize(oldp_ &Chunk, nb usize, can_move bool) &Chunk {
return voidptr(0) return voidptr(0)
} }
// Keep the old chunk if it's big enough but not too big // 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 return oldp
} }
offset := oldp.prev_foot offset := oldp.prev_foot
oldmmsize := oldsize + offset + dlmalloc.mmap_foot_pad oldmmsize := oldsize + offset + mmap_foot_pad()
newmmsize := dl.mmap_align(nb + 6 * sizeof(usize) + dlmalloc.malloc_alignment - 1) newmmsize := dl.mmap_align(nb + 6 * sizeof(usize) + malloc_alignment() - 1)
ptr := dl.system_allocator.remap(dl.system_allocator.data, voidptr(usize(oldp) - offset), ptr := dl.system_allocator.remap(dl.system_allocator.data, voidptr(usize(oldp) - offset),
oldmmsize, newmmsize, can_move) 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)) mut newp := &Chunk(voidptr(usize(ptr) + offset))
psize := newmmsize - offset - dlmalloc.mmap_foot_pad psize := newmmsize - offset - mmap_foot_pad()
newp.head = psize 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 newp.plus_offset(psize + sizeof(usize)).head = 0
if ptr < dl.least_addr { if ptr < dl.least_addr {
dl.least_addr = ptr dl.least_addr = ptr
@ -1434,7 +1511,7 @@ fn (mut dl Dlmalloc) dispose_chunk(p_ &Chunk, psize_ usize) {
if !p.pinuse() { if !p.pinuse() {
prevsize := p.prev_foot prevsize := p.prev_foot
if p.mmapped() { 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), if dl.system_allocator.free_(dl.system_allocator.data, voidptr(usize(p) - prevsize),
psize) psize)

View File

@ -1,8 +1,9 @@
module dlmalloc module dlmalloc
#include <sys/mman.h> $if !freestanding {
#include <unistd.h> #include <sys/mman.h>
#include <unistd.h>
}
fn C.munmap(ptr voidptr, size usize) int fn C.munmap(ptr voidptr, size usize) int
fn C.mremap(ptr voidptr, old usize, new usize, flags usize) voidptr 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 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) { fn system_alloc(_ voidptr, size usize) (voidptr, usize, u32) {
unsafe { $if !freestanding {
mem_prot := MemProt(int(MemProt.prot_read) | int(MemProt.prot_write)) unsafe {
map_flags := MapFlags(int(MapFlags.map_private) | int(MapFlags.map_anonymous)) mem_prot := MemProt(int(MemProt.prot_read) | int(MemProt.prot_write))
addr := C.mmap(voidptr(0), size, int(mem_prot), int(map_flags), -1, 0) 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) { if addr == voidptr(-1) {
return voidptr(0), 0, 0 return voidptr(0), 0, 0
} else { } else {
return addr, size, 0 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 { 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 { fn system_free_part(_ voidptr, ptr voidptr, oldsize usize, newsize usize) bool {
$if linux { $if linux && !freestanding {
unsafe { unsafe {
rc := C.mremap(ptr, oldsize, newsize, 0) rc := C.mremap(ptr, oldsize, newsize, 0)
if rc != voidptr(-1) { 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 return C.munmap(voidptr(usize(ptr) + newsize), oldsize - newsize) == 0
} }
} $else $if macos { } $else $if macos && !freestanding {
unsafe { unsafe {
return C.munmap(voidptr(usize(ptr) + newsize), oldsize - newsize) == 0 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 { fn system_free(_ voidptr, ptr voidptr, size usize) bool {
unsafe { $if !freestanding {
return C.munmap(ptr, size) == 0 unsafe {
return C.munmap(ptr, size) == 0
}
} $else {
return false
} }
} }

View File

@ -54,7 +54,7 @@ pub fn realloc(ptr voidptr, oldsize usize, newsize usize) voidptr {
[unsafe] [unsafe]
pub fn memalign(size usize, align usize) voidptr { pub fn memalign(size usize, align usize) voidptr {
unsafe { unsafe {
if align <= malloc_alignment { if align <= malloc_alignment() {
return global.malloc(size) return global.malloc(size)
} else { } else {
return global.memalign(align, size) return global.memalign(align, size)

View File

@ -0,0 +1,7 @@
fn main() {
mut x := []int{cap: 100}
x << 42
x << 41
x << 40
println(x)
}

View File

@ -211,7 +211,7 @@ fn (mut b Builder) handle_usecache(vexe string) {
// strconv is already imported inside builtin, so skip generating its object file // 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 // TODO: incase we have other modules with the same name, make sure they are vlib
// is this even doign anything? // is this even doign anything?
if imp in ['strconv', 'strings'] { if imp in ['strconv', 'strings', 'dlmalloc'] {
continue continue
} }
if imp in built_modules { if imp in built_modules {

View File

@ -1801,7 +1801,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
} }
ast.Module { ast.Module {
// g.is_builtin_mod = node.name == 'builtin' // 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.name
g.cur_mod = node 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') g.definitions.writeln('$mod$styp $attributes $field.name = {0}; // global')
} else { } else {
g.definitions.writeln('$mod$styp $attributes $field.name; // global') 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') 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. // 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') 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 { if g.pref.prealloc {
g.writeln('prealloc_vinit();') g.writeln('prealloc_vinit();')
} }

View File

@ -11,7 +11,7 @@ import v.util.recompilation
// math.bits is needed by strconv.ftoa // math.bits is needed by strconv.ftoa
pub const ( 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'] bundle_modules = ['clipboard', 'fontstash', 'gg', 'gx', 'sokol', 'szip', 'ui']
) )