builtin: use dlmalloc for `-freestanding` (#13054)

pull/13058/head
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.'
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'

View File

@ -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) }
}

View File

@ -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())
}

View File

@ -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)))

View File

@ -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)
}
}

View File

@ -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)

View File

@ -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
}
}

View File

@ -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)

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
// 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 {

View File

@ -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();')
}

View File

@ -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']
)