From ec91de35046a6962d9d9c6d5bf9d7f4a7630f803 Mon Sep 17 00:00:00 2001 From: playX Date: Thu, 6 Jan 2022 15:10:37 +0300 Subject: [PATCH] builtin: use dlmalloc for `-freestanding` (#13054) --- cmd/tools/vtest-all.v | 6 + vlib/builtin/builtin.c.v | 18 +- vlib/builtin/linux_bare/libc_impl.v | 16 +- vlib/builtin/linux_bare/linux_syscalls.v | 7 + vlib/builtin/linux_bare/memory_managment.v | 63 ++++++ vlib/dlmalloc/dlmalloc.v | 221 ++++++++++++++------- vlib/dlmalloc/dlmalloc_sys_nix.c.v | 40 ++-- vlib/dlmalloc/global.v | 2 +- vlib/strconv/bare/str_array_example.v | 7 + vlib/v/builder/rebuilding.v | 2 +- vlib/v/gen/c/cgen.v | 8 +- vlib/v/util/util.v | 2 +- 12 files changed, 284 insertions(+), 108 deletions(-) create mode 100644 vlib/strconv/bare/str_array_example.v diff --git a/cmd/tools/vtest-all.v b/cmd/tools/vtest-all.v index 403275454..6ea72a6d6 100644 --- a/cmd/tools/vtest-all.v +++ b/cmd/tools/vtest-all.v @@ -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' diff --git a/vlib/builtin/builtin.c.v b/vlib/builtin/builtin.c.v index 3b7216f6f..13a054058 100644 --- a/vlib/builtin/builtin.c.v +++ b/vlib/builtin/builtin.c.v @@ -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) } } diff --git a/vlib/builtin/linux_bare/libc_impl.v b/vlib/builtin/linux_bare/libc_impl.v index 7730d9248..7c7fbb9cd 100644 --- a/vlib/builtin/linux_bare/libc_impl.v +++ b/vlib/builtin/linux_bare/libc_impl.v @@ -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()) +} diff --git a/vlib/builtin/linux_bare/linux_syscalls.v b/vlib/builtin/linux_bare/linux_syscalls.v index b0b2c4724..09c74b2d6 100644 --- a/vlib/builtin/linux_bare/linux_syscalls.v +++ b/vlib/builtin/linux_bare/linux_syscalls.v @@ -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))) diff --git a/vlib/builtin/linux_bare/memory_managment.v b/vlib/builtin/linux_bare/memory_managment.v index 9c23dcfc3..e17520df0 100644 --- a/vlib/builtin/linux_bare/memory_managment.v +++ b/vlib/builtin/linux_bare/memory_managment.v @@ -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) + } +} diff --git a/vlib/dlmalloc/dlmalloc.v b/vlib/dlmalloc/dlmalloc.v index 5d038e6cf..eb8bda8f1 100644 --- a/vlib/dlmalloc/dlmalloc.v +++ b/vlib/dlmalloc/dlmalloc.v @@ -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) diff --git a/vlib/dlmalloc/dlmalloc_sys_nix.c.v b/vlib/dlmalloc/dlmalloc_sys_nix.c.v index 9842405f4..e2e39eb7d 100644 --- a/vlib/dlmalloc/dlmalloc_sys_nix.c.v +++ b/vlib/dlmalloc/dlmalloc_sys_nix.c.v @@ -1,8 +1,9 @@ module dlmalloc -#include -#include - +$if !freestanding { + #include + #include +} 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 } } diff --git a/vlib/dlmalloc/global.v b/vlib/dlmalloc/global.v index ea396ed51..7479bd569 100644 --- a/vlib/dlmalloc/global.v +++ b/vlib/dlmalloc/global.v @@ -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) diff --git a/vlib/strconv/bare/str_array_example.v b/vlib/strconv/bare/str_array_example.v new file mode 100644 index 000000000..b2da62d8a --- /dev/null +++ b/vlib/strconv/bare/str_array_example.v @@ -0,0 +1,7 @@ +fn main() { + mut x := []int{cap: 100} + x << 42 + x << 41 + x << 40 + println(x) +} diff --git a/vlib/v/builder/rebuilding.v b/vlib/v/builder/rebuilding.v index 88b4c6489..3a98d2365 100644 --- a/vlib/v/builder/rebuilding.v +++ b/vlib/v/builder/rebuilding.v @@ -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 { diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index b8d28eaab..b4170c530 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -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();') } diff --git a/vlib/v/util/util.v b/vlib/v/util/util.v index 764806601..23b0fbcf8 100644 --- a/vlib/v/util/util.v +++ b/vlib/v/util/util.v @@ -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'] ) -- 2.30.2