Skip to content
← Back

src/ir/values/memoized_delegate.ghul

1
namespace IR.Values is
2
use TypeTyped = Semantic.Types.Typed;
3
use Semantic.Types.Type;
4
5
// Wraps the creation of a stateless delegate (a zero-capture,
6
// ground function literal) so it is built at most once and reused.
7
// The first evaluation allocates the delegate and stores it into a
8
// static cache field; every later evaluation loads the cached one.
9
// Behaviour matches `inner` exactly because the wrapped literal
10
// captures nothing and carries no type arguments, so a single
11
// instance is interchangeable everywhere it is used.
12
//
13
// ldsfld <cache>
14
// dup
15
// brtrue Lpresent
16
// pop
17
// <inner delegate-creation IL>
18
// dup
19
// stsfld <cache>
20
// Lpresent:
21
class MEMOIZED_DELEGATE: Value, TypeTyped is
22
_inner: Value;
23
_cache_reference: string;
24
_type: Type;
25
26
type: Type => _type;
27
28
init(inner: Value, cache_reference: string, type: Type) is
29
super.init();
30
31
_inner = inner;
32
_cache_reference = cache_reference;
33
_type = type;
34
si
35
36
referenced_function: Semantic.Symbols.Function? => _inner.referenced_function;
37
38
// Stay live rather than freezing to a pre-rendered RAW: gen()
39
// mints a fresh cache-hoist label per emit site, so a delegate
40
// referenced from more than one place (notably a guarded
41
// pipe-fusion's fused path AND its fallback, which both emit the
42
// stage delegate) gets distinct labels instead of one baked into
43
// a RAW and duplicated.
44
freeze() -> Value => self;
45
46
gen(context: IR.CONTEXT) is
47
let present = IR.LABEL();
48
49
context.write_line("ldsfld {_cache_reference}");
50
context.write_line("dup");
51
context.write_line("brtrue {present}");
52
context.write_line("pop");
53
gen(_inner, context);
54
context.write_line("dup");
55
context.write_line("stsfld {_cache_reference}");
56
context.write_line("{present}:");
57
si
58
59
to_string() -> string =>
60
"memoized-delegate:[{_type}]({_inner})";
61
si
62
si