asm-gc Fix 1: precise block typing kills conservative-scan class of bugs
Replaces header format from [size:63 | mark:1] with [size:48 | type:8 | flags:8 (mark in bit 0)]. Every heap_alloc call site in the GC build now sets its type byte via one extra `orq $(HT_X << 8), -8(%rax)` after return. Ten types defined: HT_PAIR, HT_CLOSURE, HT_STRING, HT_SYMBOL, HT_VECTOR, HT_HASHTABLE, HT_HASHSET, HT_ENVNODE, HT_CHAINNODE, HT_PADDING. The mark / sweep / arena-escape walkers now dispatch on the type byte instead of heuristically guessing from block size. Deletes the special-case "negative sentinel at offset 0" branch in gc_mark_drain (hash-table vs hash-set vs vector discrimination was encoded there), the "size == 24 and TAG_SYM at offset 0" check in gc_mark_env, and the "length fits block" sanity check in the vector walker. All that logic collapses into a single compare on the type byte. Also routed the remaining direct-%r15-bump allocators (bi_strref, bi_vector, bi_makevec, bi_listtovec, bi_substr) through heap_alloc so they get proper headers + type bytes. These had been silently broken under the GC build because they bypassed the header-emitting path entirely; any direct-bump'd data appeared to the sweep walker as garbage headers. §6.6.4 cell 4 (asm GC + no snapshot) was crashing at first GC before this change. After: serves 5,000 HTTP requests at ~410 req/s, peak RSS 1,088 KB (one chunk), growth 972 KB — the collector hit its natural steady state. First time we've validated "naive GC as replacement for snapshot discipline" under real traffic. New §6.6.5 "Precise Block Typing" in the whitepaper documents the old heuristic bugs, the new header format, and the cost (one orq per alloc, 16 header bits) vs benefit (class of bugs eliminated). Updated §6.6.4 to reflect cell 4 passing. Remaining known issue: the hash-set bench on the GC build under very heavy sustained allocation still surfaces an occasional unbound-variable error. The precise-type fix addressed the observed HTTP crash; a deeper root-scan edge case remains. Tracked for Fix 2 work. 137 asm no-GC + 137 asm GC + 189 shared functional tests all pass.
This commit is contained in:
parent
3348e9b4bd
commit
5ec9eff5fe
7 changed files with 953 additions and 850 deletions
BIN
asm/uncommonlisp
BIN
asm/uncommonlisp
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -450,8 +450,29 @@ sym_hash_buckets: .skip 8192 # 1024 * 8 bytes
|
|||
.ifdef GC_NAIVE
|
||||
# ───────────────────────────────────────────────────────────
|
||||
# Naive mark-and-sweep GC state (control group for bench).
|
||||
# Every heap block carries an 8-byte header: (size << 1) | mark.
|
||||
# Every heap block carries an 8-byte header:
|
||||
# bit 0 = mark
|
||||
# bits 1–7 = reserved
|
||||
# bits 8–15 = type byte (HT_PAIR, HT_CLOSURE, …)
|
||||
# bits 16–63 = payload size in bytes
|
||||
# The tagged pointer still points at the payload (header at -8).
|
||||
# The type byte lets mark / sweep / arena walkers dispatch
|
||||
# precisely instead of guessing from block size — earlier versions
|
||||
# had to reject 24-byte strings-masquerading-as-env-nodes via
|
||||
# TAG_SYM checks at offset 0, and 40-byte strings masquerading
|
||||
# as vectors via a length-fits-block check. Type byte kills the
|
||||
# entire class of bugs.
|
||||
.equ HT_FREE, 0 # not used during normal ops (0 = unknown / free)
|
||||
.equ HT_PAIR, 1
|
||||
.equ HT_CLOSURE, 2
|
||||
.equ HT_STRING, 3
|
||||
.equ HT_SYMBOL, 4
|
||||
.equ HT_VECTOR, 5
|
||||
.equ HT_HASHTABLE, 6
|
||||
.equ HT_HASHSET, 7
|
||||
.equ HT_ENVNODE, 8
|
||||
.equ HT_CHAINNODE, 9
|
||||
.equ HT_PADDING, 10
|
||||
# Chunks are tracked in a side array so chunk memory stays pure
|
||||
# allocation space. Free blocks are linked via a global list
|
||||
# whose nodes reuse the header word as size and the first 8 bytes
|
||||
|
|
@ -692,7 +713,7 @@ heap_alloc:
|
|||
cmpq %r13, %rcx
|
||||
ja .ha_overflow
|
||||
movq %rbx, %rdx
|
||||
shlq $1, %rdx # header = size << 1, mark=0
|
||||
shlq $16, %rdx # header = (size << 16) | (type<<8) | mark; caller patches type
|
||||
movq %rdx, (%r15)
|
||||
movq %r15, %rax
|
||||
addq $8, %rax # payload ptr
|
||||
|
|
@ -725,7 +746,7 @@ heap_alloc:
|
|||
cmpq %r13, %rcx
|
||||
ja .ha_grow
|
||||
movq %rbx, %rdx
|
||||
shlq $1, %rdx
|
||||
shlq $16, %rdx
|
||||
movq %rdx, (%r15)
|
||||
movq %r15, %rax
|
||||
addq $8, %rax
|
||||
|
|
@ -744,7 +765,8 @@ heap_alloc:
|
|||
movq %rax, %rdx
|
||||
subq $8, %rdx # padding payload size
|
||||
movq %rdx, %rcx
|
||||
shlq $1, %rcx # header, mark=0
|
||||
shlq $16, %rcx # header; type will be patched to HT_PADDING below
|
||||
orq $(HT_PADDING << 8), %rcx
|
||||
movq %rcx, (%r15)
|
||||
# Link onto free list directly so sweep doesn't need to know.
|
||||
movq gc_free_list(%rip), %rcx
|
||||
|
|
@ -785,7 +807,7 @@ gc_freelist_alloc:
|
|||
testq %rax, %rax
|
||||
jz .gfa_empty
|
||||
movq (%rax), %rdx # header
|
||||
shrq $1, %rdx # block payload size
|
||||
shrq $16, %rdx # block payload size (bits 16..63)
|
||||
cmpq %rdi, %rdx
|
||||
jb .gfa_next
|
||||
# Fits. Unlink.
|
||||
|
|
@ -807,14 +829,17 @@ gc_freelist_alloc:
|
|||
leaq 8(%rax,%rdi), %r9 # tail header addr
|
||||
subq $8, %r8
|
||||
movq %r8, %r10
|
||||
shlq $1, %r10 # mark=0
|
||||
shlq $16, %r10 # tail header, type=0 (free), mark=0
|
||||
movq %r10, (%r9)
|
||||
movq gc_free_list(%rip), %r11
|
||||
movq %r11, 8(%r9)
|
||||
movq %r9, gc_free_list(%rip)
|
||||
# Shrink current block's header.
|
||||
# Shrink current block's header — keep its type byte, just resize.
|
||||
movq (%rax), %r10 # old header
|
||||
andq $0xff00, %r10 # keep type byte; drop old size+mark
|
||||
movq %rdi, %rdx
|
||||
shlq $1, %rdx
|
||||
shlq $16, %rdx
|
||||
orq %r10, %rdx # new header: new size + old type
|
||||
movq %rdx, (%rax)
|
||||
.gfa_return_whole:
|
||||
addq $8, %rax # payload ptr
|
||||
|
|
@ -1011,19 +1036,11 @@ gc_mark_env:
|
|||
popq %rdi
|
||||
testq %rcx, %rcx
|
||||
jz .gme_end
|
||||
# Block's header payload-size must be 24 AND offset 0 must be
|
||||
# a tagged symbol. Env nodes are (sym, val, parent); strings
|
||||
# and closures are also 24 bytes when they happen to land at
|
||||
# that size, so a bare size check treats them as env and the
|
||||
# walker corrupts the heap by reading string bytes as env
|
||||
# fields. Demanding TAG_SYM at offset 0 disambiguates.
|
||||
movq -8(%rdi), %rax
|
||||
shrq $1, %rax
|
||||
cmpq $24, %rax
|
||||
jne .gme_end
|
||||
movq (%rdi), %rax
|
||||
andq $7, %rax
|
||||
cmpq $TAG_SYM, %rax
|
||||
# Precise type dispatch: we want HT_ENVNODE exactly. The old
|
||||
# heuristic (size==24 + offset-0 is TAG_SYM) is redundant once
|
||||
# the type byte is populated, but still accepts the same set.
|
||||
movzbq -7(%rdi), %rax # header byte 1 (type byte)
|
||||
cmpq $HT_ENVNODE, %rax
|
||||
jne .gme_end
|
||||
# Already marked? Skip.
|
||||
testq $1, -8(%rdi)
|
||||
|
|
@ -1113,7 +1130,10 @@ gc_push_if_heap:
|
|||
ret
|
||||
|
||||
# gc_mark_drain: pop tagged values from mark stack, set header
|
||||
# mark, push children based on tag. Loop until empty.
|
||||
# mark, push children based on the TYPE BYTE in the header (bits
|
||||
# 8..15). Dispatching by type instead of by tagged-value tag
|
||||
# eliminates the class of bugs where a tag-7 value (stack scan
|
||||
# false positive) had to be disambiguated by guessing from size.
|
||||
gc_mark_drain:
|
||||
.gmd_top:
|
||||
movq gc_mark_depth(%rip), %rcx
|
||||
|
|
@ -1122,23 +1142,24 @@ gc_mark_drain:
|
|||
decq %rcx
|
||||
movq %rcx, gc_mark_depth(%rip)
|
||||
leaq gc_mark_stack(%rip), %rsi
|
||||
movq (%rsi,%rcx,8), %rbx # tagged value (callee-save keeps it safe)
|
||||
movq (%rsi,%rcx,8), %rbx # tagged value
|
||||
movq %rbx, %rsi
|
||||
andq $-8, %rsi # untagged ptr
|
||||
# If already marked, skip.
|
||||
testq $1, -8(%rsi)
|
||||
jnz .gmd_top
|
||||
orq $1, -8(%rsi) # mark header
|
||||
# Dispatch by tag.
|
||||
movq %rbx, %rax
|
||||
andq $TAG_MASK, %rax
|
||||
cmpq $TAG_PAIR, %rax
|
||||
jnz .gmd_top # already marked
|
||||
orq $1, -8(%rsi) # mark
|
||||
movzbq -7(%rsi), %rax # type byte
|
||||
cmpq $HT_PAIR, %rax
|
||||
je .gmd_pair
|
||||
cmpq $TAG_CLOSURE, %rax
|
||||
cmpq $HT_CLOSURE, %rax
|
||||
je .gmd_closure
|
||||
cmpq $7, %rax
|
||||
je .gmd_vec7
|
||||
# STRING / SYM: no children.
|
||||
cmpq $HT_VECTOR, %rax
|
||||
je .gmd_vector
|
||||
cmpq $HT_HASHTABLE, %rax
|
||||
je .gmd_hash
|
||||
cmpq $HT_HASHSET, %rax
|
||||
je .gmd_hash
|
||||
# HT_STRING / HT_SYMBOL / HT_CHAINNODE / HT_PADDING / HT_FREE: no children
|
||||
jmp .gmd_top
|
||||
.gmd_pair:
|
||||
movq (%rsi), %rdi
|
||||
|
|
@ -1161,29 +1182,10 @@ gc_mark_drain:
|
|||
movq 16(%rsi), %rdi
|
||||
call gc_mark_env # env field is an untagged env chain
|
||||
jmp .gmd_top
|
||||
.gmd_vec7:
|
||||
# Block size from header. The conservative stack scan can push
|
||||
# any value whose low 3 bits happen to equal 7; that untagged
|
||||
# "pointer" might land in a chunk but belong to a string or
|
||||
# closure, not an actual vector. Validate size against the
|
||||
# claimed length/layout before walking — otherwise a 40-byte
|
||||
# string misinterpreted as a 25-element vector reads 200 bytes
|
||||
# past itself and corrupts marks on adjacent blocks.
|
||||
movq -8(%rsi), %rax
|
||||
shrq $1, %rax # block payload size
|
||||
movq (%rsi), %rdx # first word: length or -1 or -2
|
||||
cmpq $-2, %rdx
|
||||
je .gmd_ht_check
|
||||
cmpq $-1, %rdx
|
||||
je .gmd_ht_check
|
||||
testq %rdx, %rdx
|
||||
js .gmd_top # other negatives = not ours
|
||||
# Vector: length must fit: 8 + length*8 <= block_size
|
||||
movq %rdx, %rcx
|
||||
shlq $3, %rcx
|
||||
addq $8, %rcx
|
||||
cmpq %rax, %rcx
|
||||
ja .gmd_top # length too large for block
|
||||
.gmd_vector:
|
||||
# Vector: first word = length, elements at offset 8.
|
||||
# Type byte already confirmed it's a real vector, no size guesswork.
|
||||
movq (%rsi), %rdx # length
|
||||
xorq %r8, %r8
|
||||
.gmd_vec_loop:
|
||||
cmpq %rdx, %r8
|
||||
|
|
@ -1199,19 +1201,9 @@ gc_mark_drain:
|
|||
incq %r8
|
||||
jmp .gmd_vec_loop
|
||||
|
||||
.gmd_ht_check:
|
||||
# Hash-table (-1) or hash-set (-2): require block size >= 24
|
||||
# (header) + nbuckets * 8. nbuckets at offset 16.
|
||||
cmpq $24, %rax
|
||||
jb .gmd_top
|
||||
.gmd_hash:
|
||||
# Hash-table or hash-set: nbuckets at offset 16, buckets at 24+.
|
||||
movq 16(%rsi), %rcx # nbuckets
|
||||
testq %rcx, %rcx
|
||||
js .gmd_top # garbage
|
||||
movq %rcx, %rdx
|
||||
shlq $3, %rdx
|
||||
addq $24, %rdx
|
||||
cmpq %rax, %rdx
|
||||
ja .gmd_top # nbuckets too large
|
||||
xorq %r8, %r8
|
||||
.gmd_ht_loop:
|
||||
cmpq %rcx, %r8
|
||||
|
|
@ -1257,7 +1249,7 @@ gc_sweep:
|
|||
jae .gsw_next_chunk
|
||||
movq (%rbx), %rsi # header
|
||||
movq %rsi, %r11
|
||||
shrq $1, %r11 # payload size
|
||||
shrq $16, %r11 # payload size (bits 16..63)
|
||||
testq $1, %rsi
|
||||
jz .gsw_dead
|
||||
# Live: clear mark.
|
||||
|
|
@ -1334,6 +1326,9 @@ make_pair:
|
|||
call heap_alloc
|
||||
popq %rsi
|
||||
popq %rdi
|
||||
.ifdef GC_NAIVE
|
||||
orq $(HT_PAIR << 8), -8(%rax)
|
||||
.endif
|
||||
movq %rdi, (%rax)
|
||||
movq %rsi, 8(%rax)
|
||||
orq $TAG_PAIR, %rax
|
||||
|
|
@ -1349,6 +1344,9 @@ make_closure:
|
|||
popq %rdx
|
||||
popq %rsi
|
||||
popq %rdi
|
||||
.ifdef GC_NAIVE
|
||||
orq $(HT_CLOSURE << 8), -8(%rax)
|
||||
.endif
|
||||
movq %rdi, (%rax) # params
|
||||
movq %rsi, 8(%rax) # body
|
||||
movq %rdx, 16(%rax) # env
|
||||
|
|
@ -1376,6 +1374,9 @@ env_define:
|
|||
popq %rdx
|
||||
popq %rsi
|
||||
popq %rdi
|
||||
.ifdef GC_NAIVE
|
||||
orq $(HT_ENVNODE << 8), -8(%rax)
|
||||
.endif
|
||||
movq %rdi, (%rax)
|
||||
movq %rsi, 8(%rax)
|
||||
movq %rdx, 16(%rax)
|
||||
|
|
@ -1503,6 +1504,9 @@ intern_symbol:
|
|||
# Use the stack to save sym_ptr across the second heap_alloc.
|
||||
leaq 1(%r12), %rdi
|
||||
call heap_alloc
|
||||
.ifdef GC_NAIVE
|
||||
orq $(HT_SYMBOL << 8), -8(%rax)
|
||||
.endif
|
||||
# %rax = sym_ptr; fill symbol: length byte + chars
|
||||
movb %r12b, (%rax)
|
||||
xorq %r8, %r8
|
||||
|
|
@ -1524,6 +1528,9 @@ intern_symbol:
|
|||
# Allocate hash chain node: 16 bytes [sym_ptr, next_ptr]
|
||||
movq $16, %rdi
|
||||
call heap_alloc
|
||||
.ifdef GC_NAIVE
|
||||
orq $(HT_CHAINNODE << 8), -8(%rax)
|
||||
.endif
|
||||
# Fill chain node: %rax = node_ptr, stack top = sym_ptr
|
||||
popq %rcx # rcx = sym_ptr
|
||||
movq %rcx, (%rax) # node->sym = sym_ptr
|
||||
|
|
@ -1879,6 +1886,9 @@ scheme_read:
|
|||
pushq %rbx
|
||||
leaq 8(%rbx), %rdi
|
||||
call heap_alloc
|
||||
.ifdef GC_NAIVE
|
||||
orq $(HT_STRING << 8), -8(%rax)
|
||||
.endif
|
||||
popq %rbx
|
||||
movq %rbx, (%rax) # 8-byte length
|
||||
xorq %rcx, %rcx
|
||||
|
|
@ -4279,12 +4289,18 @@ bi_strref:
|
|||
movq %rax, %rcx # string ptr
|
||||
GETARG %rax # index
|
||||
sarq $3, %rax
|
||||
movzbl 8(%rcx,%rax,1), %edi # byte at offset
|
||||
# Return as 1-char string
|
||||
movq %r15, %rax
|
||||
movq $1, (%r15) # length
|
||||
movb %dil, 8(%r15)
|
||||
addq $16, %r15
|
||||
movzbl 8(%rcx,%rax,1), %r8d # byte at offset (keep across heap_alloc call)
|
||||
# Allocate a 1-char string via heap_alloc so the GC build sees
|
||||
# a proper header / type byte instead of a headerless direct bump.
|
||||
movq $9, %rdi # 8B length + 1B char
|
||||
pushq %r8
|
||||
call heap_alloc
|
||||
popq %r8
|
||||
.ifdef GC_NAIVE
|
||||
orq $(HT_STRING << 8), -8(%rax)
|
||||
.endif
|
||||
movq $1, (%rax) # length
|
||||
movb %r8b, 8(%rax)
|
||||
orq $TAG_STRING, %rax
|
||||
RET_VAL
|
||||
|
||||
|
|
@ -4313,6 +4329,9 @@ bi_strappend:
|
|||
movq %rcx, %rdi
|
||||
addq $8, %rdi
|
||||
call heap_alloc
|
||||
.ifdef GC_NAIVE
|
||||
orq $(HT_STRING << 8), -8(%rax)
|
||||
.endif
|
||||
movq %rax, %rbp # string object base
|
||||
movq %rbx, (%rbp) # store length
|
||||
leaq 8(%rbp), %r9 # dest cursor
|
||||
|
|
@ -4406,6 +4425,9 @@ bi_numtostr:
|
|||
movq %rcx, %rdi
|
||||
addq $8, %rdi
|
||||
call heap_alloc
|
||||
.ifdef GC_NAIVE
|
||||
orq $(HT_STRING << 8), -8(%rax)
|
||||
.endif
|
||||
popq %rcx
|
||||
popq %rdi
|
||||
movq %rcx, (%rax) # length
|
||||
|
|
@ -4601,13 +4623,20 @@ bi_vector:
|
|||
movq 8(%rdx), %rax
|
||||
jmp .bvec_count
|
||||
.bvec_alloc:
|
||||
# Allocate: 8 bytes length + 8*count bytes
|
||||
movq %r15, %rax # vector obj
|
||||
movq %rcx, (%r15) # length
|
||||
leaq 8(%r15,%rcx,8), %r15
|
||||
# Allocate via heap_alloc so GC build gets a header + type byte.
|
||||
pushq %rdi # save arg list ptr
|
||||
pushq %rcx # save count
|
||||
leaq 8(,%rcx,8), %rdi # 8 (length) + count * 8
|
||||
call heap_alloc
|
||||
.ifdef GC_NAIVE
|
||||
orq $(HT_VECTOR << 8), -8(%rax)
|
||||
.endif
|
||||
popq %rcx
|
||||
popq %rdi # restore arg list
|
||||
movq %rcx, (%rax) # length
|
||||
# Fill elements from arg list
|
||||
movq %rdi, %rdx # arg list
|
||||
leaq 8(%rax), %rdi # elements start
|
||||
movq %rdi, %rdx # arg list
|
||||
leaq 8(%rax), %rdi # elements start
|
||||
.bvec_fill:
|
||||
cmpq $VAL_NIL, %rdx
|
||||
je .bvec_done2
|
||||
|
|
@ -4627,15 +4656,21 @@ bi_vector:
|
|||
|
||||
bi_makevec:
|
||||
# (make-vector n fill) — allocate n-slot vector filled with `fill`.
|
||||
# GETARG uses %rax as scratch, so we MUST stash the length in a
|
||||
# callee-safe register before consuming the second arg.
|
||||
GETARG %rax
|
||||
sarq $3, %rax # n (untagged)
|
||||
movq %rax, %rdx # save n in %rdx — survives next GETARG
|
||||
GETARG %rcx # fill value (tagged)
|
||||
movq %r15, %rax # vector obj pointer
|
||||
movq %rdx, (%r15) # store length
|
||||
leaq 8(%r15,%rdx,8), %r15 # advance heap past length + n*8 bytes
|
||||
# Route through heap_alloc so GC build gets header + type.
|
||||
pushq %rdx # n
|
||||
pushq %rcx # fill value
|
||||
leaq 8(,%rdx,8), %rdi # bytes: 8 (length) + n*8
|
||||
call heap_alloc
|
||||
.ifdef GC_NAIVE
|
||||
orq $(HT_VECTOR << 8), -8(%rax)
|
||||
.endif
|
||||
popq %rcx # fill
|
||||
popq %rdx # n
|
||||
movq %rdx, (%rax) # store length
|
||||
leaq 8(%rax), %rdi # elements start
|
||||
movq %rdx, %rsi # count = n
|
||||
.bmv_fill:
|
||||
|
|
@ -4725,11 +4760,18 @@ bi_listtovec:
|
|||
movq 8(%rdx), %rax
|
||||
jmp .bl2v_count
|
||||
.bl2v_alloc:
|
||||
movq %r15, %rax
|
||||
movq %rcx, (%r15)
|
||||
leaq 8(%r15,%rcx,8), %r15
|
||||
leaq 8(%rax), %rdi
|
||||
movq %rsi, %rdx
|
||||
pushq %rsi # save list ptr
|
||||
pushq %rcx # save count
|
||||
leaq 8(,%rcx,8), %rdi # 8 + count*8
|
||||
call heap_alloc
|
||||
.ifdef GC_NAIVE
|
||||
orq $(HT_VECTOR << 8), -8(%rax)
|
||||
.endif
|
||||
popq %rcx
|
||||
popq %rsi
|
||||
movq %rcx, (%rax) # length
|
||||
leaq 8(%rax), %rdi # element cursor
|
||||
movq %rsi, %rdx # list cursor
|
||||
.bl2v_fill:
|
||||
cmpq $VAL_NIL, %rdx
|
||||
je .bl2v_done
|
||||
|
|
@ -4755,11 +4797,24 @@ bi_substr:
|
|||
GETARG %rax # end
|
||||
sarq $3, %rax
|
||||
subq %rcx, %rax # length = end - start
|
||||
movq %r15, %rdx # result
|
||||
movq %rax, (%r15) # length
|
||||
leaq 8(%r15), %rsi # dest
|
||||
leaq 8(%rdi,%rcx,1), %rdi # src
|
||||
movq %rax, %rcx
|
||||
# Allocate via heap_alloc: 8 (length word) + length bytes.
|
||||
pushq %rdi # src base
|
||||
pushq %rcx # start offset
|
||||
pushq %rax # length
|
||||
movq %rax, %rdi
|
||||
addq $8, %rdi # total payload
|
||||
call heap_alloc
|
||||
.ifdef GC_NAIVE
|
||||
orq $(HT_STRING << 8), -8(%rax)
|
||||
.endif
|
||||
popq %rdx # length (restore)
|
||||
popq %rcx # start
|
||||
popq %rdi # src base
|
||||
movq %rdx, (%rax) # write length
|
||||
leaq 8(%rax), %rsi # dest cursor
|
||||
leaq 8(%rdi,%rcx,1), %rdi # src cursor
|
||||
movq %rdx, %rcx # bytes to copy
|
||||
movq %rax, %rdx # save result base
|
||||
.bsub_copy:
|
||||
testq %rcx, %rcx
|
||||
jz .bsub_done
|
||||
|
|
@ -4770,11 +4825,6 @@ bi_substr:
|
|||
decq %rcx
|
||||
jmp .bsub_copy
|
||||
.bsub_done:
|
||||
movq (%rdx), %rax # length
|
||||
addq $8, %rax
|
||||
addq $7, %rax
|
||||
andq $-8, %rax
|
||||
addq %rax, %r15
|
||||
movq %rdx, %rax
|
||||
orq $TAG_STRING, %rax
|
||||
RET_VAL
|
||||
|
|
@ -5296,6 +5346,9 @@ bi_file_to_string:
|
|||
movq %rbp, %rdi
|
||||
addq $8, %rdi
|
||||
call heap_alloc # %rax = ptr
|
||||
.ifdef GC_NAIVE
|
||||
orq $(HT_STRING << 8), -8(%rax)
|
||||
.endif
|
||||
movq %rax, %r12 # save heap pointer
|
||||
movq %rbp, (%r12) # store length
|
||||
|
||||
|
|
@ -5430,6 +5483,9 @@ bi_symbol_to_string:
|
|||
movq %rcx, %rdi
|
||||
addq $8, %rdi # cell size: 8-byte length + bytes
|
||||
call heap_alloc
|
||||
.ifdef GC_NAIVE
|
||||
orq $(HT_STRING << 8), -8(%rax)
|
||||
.endif
|
||||
popq %r8
|
||||
popq %rcx
|
||||
|
||||
|
|
@ -5548,6 +5604,9 @@ ht_chain_find:
|
|||
bi_make_hash_table:
|
||||
movq $HT_TOTAL_BYTES, %rdi
|
||||
call heap_alloc
|
||||
.ifdef GC_NAIVE
|
||||
orq $(HT_HASHTABLE << 8), -8(%rax)
|
||||
.endif
|
||||
movq $HT_SENTINEL, (%rax)
|
||||
movq $0, 8(%rax) # count
|
||||
movq $HT_NBUCKETS, 16(%rax)
|
||||
|
|
@ -5928,6 +5987,9 @@ hs_chain_find:
|
|||
bi_make_hash_set:
|
||||
movq $HT_TOTAL_BYTES, %rdi
|
||||
call heap_alloc
|
||||
.ifdef GC_NAIVE
|
||||
orq $(HT_HASHSET << 8), -8(%rax)
|
||||
.endif
|
||||
movq $HS_SENTINEL, (%rax)
|
||||
movq $0, 8(%rax)
|
||||
movq $HT_NBUCKETS, 16(%rax)
|
||||
|
|
@ -6331,7 +6393,7 @@ arena_verify_and_commit:
|
|||
movq (%rbx), %rax
|
||||
testq $1, %rax
|
||||
jnz .avc_escape
|
||||
shrq $1, %rax
|
||||
shrq $16, %rax
|
||||
leaq 8(%rbx,%rax), %rbx
|
||||
jmp .avc_scan
|
||||
.avc_safe:
|
||||
|
|
@ -6626,6 +6688,9 @@ bi_tcp_recv:
|
|||
movq %rbp, %rdi
|
||||
addq $8, %rdi
|
||||
call heap_alloc
|
||||
.ifdef GC_NAIVE
|
||||
orq $(HT_STRING << 8), -8(%rax)
|
||||
.endif
|
||||
movq %rax, %r12 # string object base
|
||||
|
||||
# read(fd, cell+8, size)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue