fix(compiler): the built-in ECS stops at its 1024th entity instead of writing past every store
L_alloc handed out a fresh slot without a bound, so the 1025th spawn wrote past the end of every component array. It stops with a located message naming the store's size and where many things belong (ludic.base's Table). Growable stores are plan 24.8: the save, rollback snapshot and mod table write the stores whole at a compile-time size, so that is a file-format change. Reseeded; ludic-dev test 305 passed, selfhost-test 33 passed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
649a65d854
commit
1c7ce84881
4 changed files with 9726 additions and 9579 deletions
7
changes/ecs-entity-overflow.md
Normal file
7
changes/ecs-entity-overflow.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
bump: patch
|
||||
type: fix
|
||||
**The 1,025th entity stops the program instead of corrupting it.** The built-in ECS keeps every
|
||||
component in a fixed array of 1024 slots, and `L_alloc` never checked: one entity more was written
|
||||
past the end of every component array at once. A spawn past the store now stops with `more than
|
||||
1024 entities: the built-in ECS holds 1024; keep many things in a ludic.base Table`. A game that
|
||||
keeps thousands of something keeps them as rows of `ludic.base`'s `Table<T>`, which grows.
|
||||
|
|
@ -123,11 +123,15 @@ function emit_ecs_allocator() -> void {
|
|||
emit(" br label %done\n")
|
||||
emit("fresh:\n")
|
||||
emit(" %ec = load i32, ptr @L_entc\n")
|
||||
emit(` %full = icmp sge i32 %ec, {me}\n`)
|
||||
emit(" br i1 %full, label %over, label %room\n")
|
||||
emit_ecs_full(me)
|
||||
emit("room:\n")
|
||||
emit(" %ec1 = add i32 %ec, 1\n")
|
||||
emit(" store i32 %ec1, ptr @L_entc\n")
|
||||
emit(" br label %done\n")
|
||||
emit("done:\n")
|
||||
emit(" %e = phi i32 [ %re, %reuse ], [ %ec, %fresh ]\n")
|
||||
emit(" %e = phi i32 [ %re, %reuse ], [ %ec, %room ]\n")
|
||||
emit(" call void @L_reset(i32 %e)\n")
|
||||
emit(" %ap = getelementptr inbounds ["); emit(me); emit(" x i32], ptr @L_alive, i32 0, i32 %e\n")
|
||||
emit(" store i32 1, ptr %ap\n")
|
||||
|
|
@ -144,3 +148,15 @@ function emit_ecs_allocator() -> void {
|
|||
emit(" store i32 %fn1, ptr @L_freen\n")
|
||||
emit(" ret void\n}\n\n")
|
||||
}
|
||||
|
||||
# the store is fixed at MAX_ENT: one more entity used to be written past the end of every component
|
||||
# array, silently. It stops the program instead, and says where many things belong.
|
||||
function emit_ecs_full(me: pointer) -> void {
|
||||
g_uses_panic = true
|
||||
emit("over:\n")
|
||||
let prefix = emit_str_const(`{g_src_name}: panic: `)
|
||||
let m = emit_str_const(`more than {me} entities: the built-in ECS holds {me}; keep many things in a ludic.base Table`)
|
||||
let se = emit_bind(stdstream_rhs(2))
|
||||
emit(` call i32 (ptr, ptr, ...) @fprintf(ptr {se}, ptr @.fmt_panic, ptr {prefix}, ptr {m})\n`)
|
||||
emit(" call void @exit(i32 1)\n unreachable\n")
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue