Skip to content

Commit 8f2a3d7

Browse files
authored
cgen: fix stack overflow for @[heap] structs with large fixed arrays (fix #22690) (#26183)
1 parent 21a0f11 commit 8f2a3d7

3 files changed

Lines changed: 141 additions & 2 deletions

File tree

‎vlib/v/gen/c/assign.v‎

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -992,7 +992,14 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
992992
} else {
993993
is_option_unwrapped := val is ast.Ident && val.or_expr.kind != .absent
994994
is_option_auto_heap := is_auto_heap && is_option_unwrapped
995-
if is_auto_heap && !is_fn_var {
995+
// For large structs (with large fixed arrays), avoid stack-allocated
996+
// compound literals which can cause stack overflow. Use vcalloc directly.
997+
mut is_large_struct_heap := false
998+
if is_auto_heap && !is_fn_var && val is ast.StructInit
999+
&& g.struct_has_large_fixed_array(val.typ) {
1000+
is_large_struct_heap = true
1001+
}
1002+
if is_auto_heap && !is_fn_var && !is_large_struct_heap {
9961003
if aligned != 0 {
9971004
g.write('HEAP_align(${styp}, (')
9981005
} else {
@@ -1037,14 +1044,53 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
10371044
tmp_var := g.expr_with_var(val, var_type, false)
10381045
g.fixed_array_var_init(tmp_var, false, unaliased_right_sym.info.elem_type,
10391046
unaliased_right_sym.info.size)
1047+
} else if is_large_struct_heap && val is ast.StructInit {
1048+
// For large structs, use vcalloc directly to avoid stack overflow
1049+
// from compound literals on the stack
1050+
tmp_var := g.new_tmp_var()
1051+
stmt_str := g.go_before_last_stmt()
1052+
g.empty_line = true
1053+
g.writeln('${styp}* ${tmp_var} = (${styp}*)builtin__vcalloc(sizeof(${styp}));')
1054+
// Initialize non-zero fields
1055+
val_sym := g.table.final_sym(val.typ)
1056+
if val_sym.info is ast.Struct {
1057+
for init_field in val.init_fields {
1058+
if init_field.typ == 0 {
1059+
continue
1060+
}
1061+
field_name := c_name(init_field.name)
1062+
g.write('${tmp_var}->${field_name} = ')
1063+
g.expr(init_field.expr)
1064+
g.writeln(';')
1065+
}
1066+
// Handle fields with default values
1067+
for field in val_sym.info.fields {
1068+
mut found := false
1069+
for init_field in val.init_fields {
1070+
if init_field.name == field.name {
1071+
found = true
1072+
break
1073+
}
1074+
}
1075+
if !found && field.has_default_expr {
1076+
field_name := c_name(field.name)
1077+
g.write('${tmp_var}->${field_name} = ')
1078+
g.expr(field.default_expr)
1079+
g.writeln(';')
1080+
}
1081+
}
1082+
}
1083+
g.empty_line = false
1084+
g.write2(stmt_str, tmp_var)
10401085
} else {
10411086
old_inside_assign_fn_var := g.inside_assign_fn_var
10421087
g.inside_assign_fn_var = val is ast.PrefixExpr && val.op == .amp
10431088
&& is_fn_var
10441089
g.expr(val)
10451090
g.inside_assign_fn_var = old_inside_assign_fn_var
10461091
}
1047-
if !is_fn_var && is_auto_heap && !is_option_auto_heap {
1092+
if !is_fn_var && is_auto_heap && !is_option_auto_heap
1093+
&& !is_large_struct_heap {
10481094
if aligned != 0 {
10491095
g.write('), ${aligned})')
10501096
} else {

‎vlib/v/gen/c/struct.v‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -821,3 +821,25 @@ fn (mut g Gen) struct_init_field_default(field_unwrap_typ ast.Type, sfield &ast.
821821
g.expr_with_cast(sfield.expr, field_unwrap_typ, sfield.expected_type)
822822
}
823823
}
824+
825+
// struct_has_large_fixed_array returns true if the struct type contains
826+
// fixed arrays whose total size exceeds the threshold (64KB).
827+
// This is used to determine whether to avoid stack-allocated compound literals
828+
// which can cause stack overflow for large structs.
829+
fn (g &Gen) struct_has_large_fixed_array(typ ast.Type) bool {
830+
sym := g.table.final_sym(typ)
831+
if sym.info is ast.Struct {
832+
for field in sym.info.fields {
833+
field_sym := g.table.final_sym(field.typ)
834+
if field_sym.info is ast.ArrayFixed {
835+
// Check if this fixed array is large (> 64KB)
836+
// Conservative estimate: 8 bytes per element
837+
size := i64(field_sym.info.size) * 8
838+
if size > 65536 {
839+
return true
840+
}
841+
}
842+
}
843+
}
844+
return false
845+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Test @[heap] structs with large fixed arrays.
2+
3+
@[heap]
4+
struct LargeData {
5+
array [1024 * 1024]int
6+
}
7+
8+
@[heap]
9+
struct LargeDataWithFields {
10+
array [1024 * 1024]int
11+
value int = 42
12+
name string
13+
}
14+
15+
fn get_large_ref() &LargeData {
16+
d := LargeData{}
17+
return &d
18+
}
19+
20+
fn get_large_with_fields_ref(name string) &LargeDataWithFields {
21+
d := LargeDataWithFields{
22+
name: name
23+
}
24+
return &d
25+
}
26+
27+
fn test_heap_large_fixed_array_basic() {
28+
// Basic test: create a heap struct with large fixed array
29+
d := LargeData{}
30+
assert d.array[0] == 0
31+
assert d.array[1024 * 1024 - 1] == 0
32+
}
33+
34+
fn test_heap_large_fixed_array_reference() {
35+
// Test returning reference to heap struct with large array
36+
ptr := get_large_ref()
37+
assert ptr.array[0] == 0
38+
assert ptr.array[500000] == 0
39+
}
40+
41+
fn test_heap_large_fixed_array_with_fields() {
42+
// Test struct with large array and other fields
43+
d := LargeDataWithFields{
44+
name: 'test'
45+
}
46+
assert d.array[0] == 0
47+
assert d.value == 42 // default value
48+
assert d.name == 'test' // explicit value
49+
}
50+
51+
fn test_heap_large_fixed_array_with_fields_ref() {
52+
// Test reference to struct with large array and fields
53+
ptr := get_large_with_fields_ref('hello')
54+
assert ptr.array[0] == 0
55+
assert ptr.value == 42
56+
assert ptr.name == 'hello'
57+
}
58+
59+
fn test_heap_large_fixed_array_multiple() {
60+
// Test creating multiple large heap structs
61+
a := LargeData{}
62+
b := LargeData{}
63+
c := LargeDataWithFields{
64+
value: 100
65+
name: 'multi'
66+
}
67+
assert a.array[0] == 0
68+
assert b.array[0] == 0
69+
assert c.value == 100
70+
assert c.name == 'multi'
71+
}

0 commit comments

Comments
 (0)