fix defer() with the arena allocator ("return values are evaluated before the defer is executed")

This commit is contained in:
Irmen de Jong
2025-09-17 23:59:32 +02:00
parent f6c8e693a5
commit a8bede17b2
11 changed files with 45 additions and 62 deletions
@@ -185,7 +185,7 @@ private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtPro
is PtNumber,
is PtRange,
is PtString -> true
is PtIdentifier -> true // actually PtIdentifier IS "complex" this time (it's a variable that might change) but it's kinda annoying to give a warning message for this very common case
// note that PtIdentifier als is "complex" this time (it's a variable that might change)
else -> false
}
@@ -202,7 +202,6 @@ private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtPro
}
// complex return value, need to store it before calling the defer block
errors.warn("using defer with nontrivial return value(s) incurs stack overhead", ret.children.first { !notComplex(it as PtExpression)}.position)
val pushAndPopCalls = ret.children.map { makePushPopFunctionCalls(it as PtExpression) }
val pushCalls = pushAndPopCalls.map { it.first }.reversed() // push in reverse order
val popCalls = pushAndPopCalls.map { it.second }
+5
View File
@@ -1303,6 +1303,11 @@ It's possible to write a defer for a block of statements, but the advice is to k
If a piece of inlined assembly somehow causes the routine to exit, the compiler cannot detect this,
and defers won't be handled in such cases.
.. attention::
Using defer always has a slight code overhead.
If you are returning non-constant values in a routine that uses defer, the compiler even has to insert some additional
code that uses the cpu stack to save some temporary values.
Library routines and builtin functions
--------------------------------------
+2 -3
View File
@@ -241,9 +241,8 @@ An example of how a super simple dynamic allocator could look like::
uword next = buffer
sub alloc(ubyte size) -> uword {
uword result = next
next += size
return result
defer next += size
return next
}
sub freeall() {
-3
View File
@@ -1,9 +1,6 @@
TODO
====
fix defer() with the arena allocator ("return values are evaluated before the defer is executed")
not all source lines are correctly reported in the IR file,
for example the below subroutine only shows the sub() line::
+2 -3
View File
@@ -141,8 +141,7 @@ arena {
uword next = buffer
sub alloc(ubyte size) -> uword {
uword result = next
next += size
return result
defer next += size
return next
}
}
+2 -3
View File
@@ -227,8 +227,7 @@ arena {
uword next = buffer
sub alloc(ubyte size) -> uword {
uword result = next
next += size
return result
defer next += size
return next
}
}
+2 -3
View File
@@ -106,8 +106,7 @@ arena {
uword next = buffer
sub alloc(ubyte size) -> uword {
uword result = next
next += size
return result
defer next += size
return next
}
}
+2 -3
View File
@@ -88,8 +88,7 @@ arena {
uword next = buffer
sub alloc(ubyte size) -> uword {
uword result = next
next += size
return result
defer next += size
return next
}
}
+2 -3
View File
@@ -225,8 +225,7 @@ arena {
uword next = buffer
sub alloc(ubyte size) -> uword {
uword result = next
next += size
return result
defer next += size
return next
}
}
+2 -3
View File
@@ -86,8 +86,7 @@ arena {
uword next = buffer
sub alloc(ubyte size) -> uword {
uword result = next
next += size
return result
defer next += size
return next
}
}
+25 -36
View File
@@ -3,48 +3,37 @@
main {
sub start() {
txt.print("expected: 0 10 30\n")
counter = 0
txt.print_ub(nodefer(10))
txt.print_uw(allocator.alloc(10))
txt.spc()
txt.print_ub(nodefer(20))
txt.print_uw(allocator.alloc(20))
txt.spc()
txt.print_ub(nodefer(30))
txt.print_uw(allocator.alloc(30))
txt.nl()
counter = 0
txt.print_ub(add(10))
allocator.freeall()
txt.print_uw(allocator.alloc(10))
txt.spc()
txt.print_ub(add(20))
txt.print_uw(allocator.alloc(20))
txt.spc()
txt.print_ub(add(30))
txt.print_uw(allocator.alloc(30))
txt.nl()
counter = 0
txt.print_ub(add2(10))
txt.spc()
txt.print_ub(add2(20))
txt.spc()
txt.print_ub(add2(30))
txt.nl()
}
ubyte counter = 0
sub nodefer(ubyte amount) -> ubyte {
ubyte result = counter
counter += amount
return result
}
sub add(ubyte amount) -> ubyte {
defer counter += amount ; TODO FIX : BORKED!
return counter
}
sub add2(ubyte amount) -> ubyte {
cx16.r0L = 0
defer counter += amount
return counter + cx16.r0L
}
}
allocator {
; extremely trivial allocator allocator
uword buffer = memory("allocator", 2000, 0)
uword next = buffer
sub alloc(ubyte size) -> uword {
defer next += size
return next
}
sub freeall() {
; cannot free individual allocations only the whole allocator at once
next = buffer
}
}