gen: support returning large types from closures on amd64 (#12926)

pull/12929/head
spaceface 2021-12-21 23:18:45 +01:00 committed by GitHub
parent 7b4d83660a
commit cfb814a0e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 112 additions and 11 deletions

View File

@ -457,6 +457,9 @@ fn (mut g Gen) gen_anon_fn(mut node ast.AnonFn) {
for param in node.decl.params {
size_sb.write_string('_REG_WIDTH(${g.typ(param.typ)}) + ')
}
if g.pref.arch == .amd64 && node.decl.return_type != ast.void_type {
size_sb.write_string('(_REG_WIDTH(${g.typ(node.decl.return_type)}) > 2) + ')
}
size_sb.write_string('1')
args_size := size_sb.str()
g.writeln('')

View File

@ -7,6 +7,58 @@ const (
all_param_values = []string{len: max_params, init: '${it + 1}'}
)
struct ReturnType {
name string
init string
assertion string
no_assert_kw bool
}
const return_types = [
ReturnType{
name: ''
init: ''
assertion: ''
no_assert_kw: true
},
ReturnType{
name: 'int'
init: '-123'
assertion: ' == -123'
},
ReturnType{
name: 'u64'
init: '123'
assertion: ' == 123'
},
ReturnType{
name: 'voidptr'
init: 'voidptr(123)'
assertion: ' == voidptr(123)'
},
ReturnType{
name: 'string'
init: "'hello'"
assertion: " == 'hello'"
},
ReturnType{
name: '?'
init: "error('an error')"
assertion: " or { assert err.msg == 'an error' return }\npanic('got no error')"
no_assert_kw: true
},
ReturnType{
name: '?string'
init: "'hello'"
assertion: "? == 'hello'"
},
ReturnType{
name: 'BigStruct'
init: 'BigStruct{ a3: 56, a27: 1234, a61: 5555 }'
assertion: ' == BigStruct{ a3: 56, a27: 1234, a61: 5555 }'
},
]
// test_closures_with_n_args generates a new V file containing closures of `i`
// and parameters of type `typ`, to makes sure that all combinations work correctly
fn test_closures_with_n_args() ? {
@ -14,6 +66,12 @@ fn test_closures_with_n_args() ? {
// NB: the type or value of the captured arg doesn't matter for this test,
// as the entire closure context is always passed as one pointer anyways
v_code.write_string('struct BigStruct {')
for i in 0 .. 64 {
v_code.write_string('\ta$i int ')
}
v_code.writeln('}')
for typ in ['byte', 'u16', 'int', 'i64', 'voidptr', 'string'] {
for i in 0 .. max_params {
param_names := all_param_names[..i]
@ -41,19 +99,59 @@ fn test_closures_with_n_args() ? {
// NB: the captured arg doesn't matter for this test, as closures always receive
// a pointer to the entire closure context as their last argument anyways
v_code.writeln("
fn test_big_closure_${typ}_${i}() {
println('test_big_closure_${typ}_$i')
mut z := $init_val
c := fn [z] (${params.join(', ')}) $return_type {
mut sum := z")
fn test_big_closure_${typ}_${i}() {
println('test_big_closure_${typ}_$i')
mut local := 123
mut local_2 := 234
mut z := $init_val
c := fn [z] (${params.join(', ')}) $return_type {
mut sum := z")
for j in 0 .. i {
v_code.writeln('\t\tsum += ${return_type}(${param_names[j]})')
}
v_code.writeln('
return sum
v_code.writeln("
return sum
}
assert c(${values.join(', ')}) == $expected_val
// ensure stack wasn't overwritten:
assert local == 123
assert local_2 == 234
}")
}
assert c(${values.join(', ')}) == $expected_val
}')
}
for return_type in return_types {
typ := return_type.name
styp := typ.replace('?', 'option_').to_lower()
init := return_type.init
assertion := return_type.assertion
for i in 0 .. 10 {
param_names := all_param_names[..i]
params := param_names.map('$it int')
values := all_param_values[..i]
assert_line := if !return_type.no_assert_kw {
'assert c(${values.join(', ')}) $assertion'
} else {
'c(${values.join(', ')}) $assertion'
}
// NB: the captured arg doesn't matter for this test, as closures always receive
// a pointer to the entire closure context as their last argument anyways
v_code.writeln("
fn test_closure_return_${styp}_${i}() ? {
println('test_closure_return_${styp}_$i')
mut local := 123
mut local_2 := 234
mut z := 1234
c := fn [z] (${params.join(', ')}) $typ {
return $init
}
$assert_line
// ensure stack wasn't overwritten:
assert local == 123
assert local_2 == 234
}")
}
}
@ -62,9 +160,9 @@ fn test_closures_with_n_args() ? {
wrkdir := os.join_path(os.temp_dir(), 'vtests', 'closures')
os.mkdir_all(wrkdir) ?
os.chdir(wrkdir) ?
os.write_file('closure_args_test.v', code) ?
os.write_file('closure_return_test.v', code) ?
vexe := os.getenv('VEXE')
res := os.execute('"$vexe" closure_args_test.v')
res := os.execute('"$vexe" -keepc -cg -showcc closure_return_test.v')
if res.exit_code != 0 {
eprintln(res.output)
assert false