Skip to content

Commit 36beea5

Browse files
authored
Fix code generation for stack-allocated closures (#1703)
These changes fix an issue with stack-allocated closures constructed within loops causing unlimited stack growth.
2 parents 4ceaf1e + 4f56f15 commit 36beea5

File tree

3 files changed

+141
-108
lines changed

3 files changed

+141
-108
lines changed

documentation/source/release-notes/2025.1.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ Compiler
5656
* Fixed `issue 1395 <https://github.com/dylan-lang/opendylan/issues/1395>`_, which
5757
prevented some valid variable names with a leading numeric from being parsed correctly.
5858

59+
* A bug in the LLVM back-end that could cause stack overflows in
60+
long-running functions that capture closures (such as for
61+
exception handlers) has been fixed.
62+
5963
Tools
6064
=====
6165

sources/dfmc/llvm-back-end/llvm-emit-computation.dylan

Lines changed: 123 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,23 @@ define method emit-local-tmp-definition
139139
end if;
140140
end if;
141141
temporary-value(tmp) := alloca;
142+
else
143+
let c = tmp.generator;
144+
if (instance?(c, <make-closure>))
145+
let o = function(c.computation-closure-method);
146+
if (closure?(o) & c.closure-has-dynamic-extent?)
147+
let class
148+
= if (instance?(o, <&keyword-method>))
149+
dylan-value(#"<keyword-closure-method>")
150+
else
151+
dylan-value(#"<simple-closure-method>");
152+
end if;
153+
let closure-size = closure-size(o.environment);
154+
let closure = op--stack-allocate-closure(back-end, class, closure-size);
155+
let result = ins--bitcast(back-end, closure, $llvm-object-pointer-type);
156+
temporary-value(tmp) := result;
157+
end if;
158+
end if;
142159
end if;
143160
end method;
144161

@@ -481,142 +498,148 @@ end method;
481498
define method emit-computation
482499
(back-end :: <llvm-back-end>, m :: <llvm-module>, c :: <make-closure>)
483500
=> ();
501+
let temp = c.temporary;
484502
let o = function(c.computation-closure-method);
485503
let sigtmp = c.computation-signature-value;
486504
let key? = instance?(o, <&keyword-method>);
487-
if (closure?(o))
488-
let init? = computation-init-closure?(c);
489-
let top-level? = computation-top-level-closure?(c);
490-
let env = o.environment;
491-
let closure-inits
492-
= if (init? & ~top-level?) env.closure else #() end if;
493-
if (c.closure-has-dynamic-extent?)
494-
// Stack-allocated closure
495-
let class
496-
= if (key?)
497-
dylan-value(#"<keyword-closure-method>")
498-
else
499-
dylan-value(#"<simple-closure-method>");
500-
end if;
501-
502-
let template = emit-reference(back-end, m, o);
503-
let closure-size = closure-size(env);
504-
let closure
505-
= op--stack-allocate-closure(back-end, class, template, closure-size);
506-
507-
for (index from 0, init in closure-inits)
508-
let value = emit-reference(back-end, m, init);
509-
let ptr
510-
= op--getslotptr(back-end, closure, class, #"environment-element",
511-
index);
512-
ins--store(back-end, value, ptr);
513-
end for;
505+
if (temp.used?)
506+
if (closure?(o))
507+
let init? = computation-init-closure?(c);
508+
let top-level? = computation-top-level-closure?(c);
509+
let env = o.environment;
510+
let closure-inits
511+
= if (init? & ~top-level?) env.closure else #() end if;
512+
if (c.closure-has-dynamic-extent?)
513+
// Stack-allocated closure
514+
let class
515+
= if (key?)
516+
dylan-value(#"<keyword-closure-method>")
517+
else
518+
dylan-value(#"<simple-closure-method>");
519+
end if;
514520

515-
if (sigtmp)
516-
// Dynamic signature
517-
let signature = emit-reference(back-end, m, sigtmp);
518-
let signature-ptr
519-
= op--getslotptr(back-end, closure, class, #"function-signature");
520-
ins--store(back-end, signature, signature-ptr);
521-
end if;
521+
let template = emit-reference(back-end, m, o);
522+
let closure-size = closure-size(env);
523+
let class-type
524+
= llvm-class-type(back-end, class, repeated-size: closure-size);
525+
let closure
526+
= ins--bitcast(back-end, temporary-value(temp),
527+
llvm-pointer-to(back-end, class-type));
528+
op--initialize-stack-allocated-closure
529+
(back-end, class, template, closure, closure-size);
530+
531+
for (index from 0, init in closure-inits)
532+
let value = emit-reference(back-end, m, init);
533+
let ptr
534+
= op--getslotptr(back-end, closure, class, #"environment-element",
535+
index);
536+
ins--store(back-end, value, ptr);
537+
end for;
522538

523-
let result = ins--bitcast(back-end, closure, $llvm-object-pointer-type);
524-
computation-result(back-end, c, result);
525-
else
526-
if (sigtmp)
527-
if (top-level?)
528-
// Top-level method with a dynamic signature
539+
if (sigtmp)
540+
// Dynamic signature
529541
let signature = emit-reference(back-end, m, sigtmp);
530-
let name = emit-name(back-end, m, o);
531-
let global = llvm-builder-global(back-end, name);
532-
let class = o.^object-class;
533-
llvm-constrain-type
534-
(global.llvm-value-type,
535-
llvm-pointer-to(back-end, llvm-class-type(back-end, class)));
536542
let signature-ptr
537-
= op--getslotptr(back-end, global, class, #"function-signature");
543+
= op--getslotptr(back-end, closure, class, #"function-signature");
538544
ins--store(back-end, signature, signature-ptr);
539-
540-
let result = ins--bitcast(back-end, global, $llvm-object-pointer-type);
541-
computation-result(back-end, c, result);
545+
end if;
546+
else
547+
if (sigtmp)
548+
if (top-level?)
549+
// Top-level method with a dynamic signature
550+
let signature = emit-reference(back-end, m, sigtmp);
551+
let name = emit-name(back-end, m, o);
552+
let global = llvm-builder-global(back-end, name);
553+
let class = o.^object-class;
554+
llvm-constrain-type
555+
(global.llvm-value-type,
556+
llvm-pointer-to(back-end, llvm-class-type(back-end, class)));
557+
let signature-ptr
558+
= op--getslotptr(back-end, global, class, #"function-signature");
559+
ins--store(back-end, signature, signature-ptr);
560+
561+
let result
562+
= ins--bitcast(back-end, global, $llvm-object-pointer-type);
563+
computation-result(back-end, c, result);
564+
else
565+
let result
566+
= if (init?)
567+
// Initialized closure
568+
let primitive
569+
= if (key?)
570+
primitive-make-keyword-closure-with-environment-signature-descriptor
571+
else
572+
primitive-make-closure-with-environment-signature-descriptor
573+
end if;
574+
apply(call-primitive, back-end, primitive,
575+
emit-reference(back-end, m, o),
576+
emit-reference(back-end, m, sigtmp),
577+
closure-size(env),
578+
map(curry(emit-reference, back-end, m), closure-inits))
579+
else
580+
// Uninitialized closure
581+
let primitive
582+
= if (key?)
583+
primitive-make-keyword-closure-signature-descriptor
584+
else
585+
primitive-make-closure-signature-descriptor
586+
end if;
587+
call-primitive(back-end, primitive,
588+
emit-reference(back-end, m, o),
589+
emit-reference(back-end, m, sigtmp),
590+
closure-size(env));
591+
end if;
592+
computation-result(back-end, c, result);
593+
end if;
542594
else
543595
let result
544596
= if (init?)
545597
// Initialized closure
546598
let primitive
547599
= if (key?)
548-
primitive-make-keyword-closure-with-environment-signature-descriptor
600+
primitive-make-keyword-closure-with-environment-descriptor
549601
else
550-
primitive-make-closure-with-environment-signature-descriptor
602+
primitive-make-closure-with-environment-descriptor
551603
end if;
552604
apply(call-primitive, back-end, primitive,
553605
emit-reference(back-end, m, o),
554-
emit-reference(back-end, m, sigtmp),
555606
closure-size(env),
556607
map(curry(emit-reference, back-end, m), closure-inits))
557608
else
558609
// Uninitialized closure
559610
let primitive
560611
= if (key?)
561-
primitive-make-keyword-closure-signature-descriptor
612+
primitive-make-keyword-closure-descriptor
562613
else
563-
primitive-make-closure-signature-descriptor
614+
primitive-make-closure-descriptor
564615
end if;
565616
call-primitive(back-end, primitive,
566617
emit-reference(back-end, m, o),
567-
emit-reference(back-end, m, sigtmp),
568618
closure-size(env));
569619
end if;
570620
computation-result(back-end, c, result);
571621
end if;
572-
else
573-
let result
574-
= if (init?)
575-
// Initialized closure
576-
let primitive
577-
= if (key?)
578-
primitive-make-keyword-closure-with-environment-descriptor
579-
else
580-
primitive-make-closure-with-environment-descriptor
581-
end if;
582-
apply(call-primitive, back-end, primitive,
583-
emit-reference(back-end, m, o),
584-
closure-size(env),
585-
map(curry(emit-reference, back-end, m), closure-inits))
622+
end if;
623+
else
624+
// Not a closure
625+
if (sigtmp)
626+
// Dynamic method signature
627+
let primitive
628+
= if (key?)
629+
primitive-make-keyword-method-with-signature-descriptor
586630
else
587-
// Uninitialized closure
588-
let primitive
589-
= if (key?)
590-
primitive-make-keyword-closure-descriptor
591-
else
592-
primitive-make-closure-descriptor
593-
end if;
594-
call-primitive(back-end, primitive,
595-
emit-reference(back-end, m, o),
596-
closure-size(env));
631+
primitive-make-method-with-signature-descriptor
597632
end if;
633+
let result
634+
= call-primitive(back-end, primitive,
635+
emit-reference(back-end, m, o),
636+
emit-reference(back-end, m, sigtmp));
598637
computation-result(back-end, c, result);
599-
end if;
638+
else
639+
// Ordinary compile-time method signature
640+
computation-result(back-end, c, emit-reference(back-end, m, o));
641+
end if
600642
end if;
601-
else
602-
// Not a closure
603-
if (sigtmp)
604-
// Dynamic method signature
605-
let primitive
606-
= if (key?)
607-
primitive-make-keyword-method-with-signature-descriptor
608-
else
609-
primitive-make-method-with-signature-descriptor
610-
end if;
611-
let result
612-
= call-primitive(back-end, primitive,
613-
emit-reference(back-end, m, o),
614-
emit-reference(back-end, m, sigtmp));
615-
computation-result(back-end, c, result);
616-
else
617-
// Ordinary compile-time method signature
618-
computation-result(back-end, c, emit-reference(back-end, m, o));
619-
end if
620643
end if;
621644
end method;
622645

sources/dfmc/llvm-back-end/llvm-primitives-lambda.dylan

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -978,16 +978,24 @@ end method;
978978
// Stack allocate a closure
979979
define method op--stack-allocate-closure
980980
(back-end :: <llvm-back-end>, class :: <&class>,
981-
template :: <llvm-value>, closure-size :: <integer>)
982-
=> (new-vector :: <llvm-value>);
983-
let module = back-end.llvm-builder-module;
981+
closure-size :: <integer>)
982+
=> (closure :: <llvm-value>);
984983
let word-size = back-end-word-size(back-end);
985-
let header-words = dylan-value(#"$number-header-words");
986984

987985
let class-type
988986
= llvm-class-type(back-end, class, repeated-size: closure-size);
989-
let closure
990-
= ins--alloca(back-end, class-type, 1, alignment: word-size);
987+
ins--alloca(back-end, class-type, 1, alignment: word-size)
988+
end method;
989+
990+
// Initialize a stack-allocated closure
991+
define method op--initialize-stack-allocated-closure
992+
(back-end :: <llvm-back-end>, class :: <&class>,
993+
template :: <llvm-value>, closure :: <llvm-value>,
994+
closure-size :: <integer>)
995+
=> ();
996+
let module = back-end.llvm-builder-module;
997+
let word-size = back-end-word-size(back-end);
998+
let header-words = dylan-value(#"$number-header-words");
991999

9921000
// Copy from the template
9931001
let fixed-size = header-words + ^instance-storage-size(class);
@@ -1003,7 +1011,5 @@ define method op--stack-allocate-closure
10031011
= op--getslotptr(back-end, closure, class, #"environment-size");
10041012
let size-ref = emit-reference(back-end, module, closure-size);
10051013
ins--store(back-end, size-ref, size-slot-ptr);
1006-
1007-
closure
10081014
end method;
10091015

0 commit comments

Comments
 (0)