authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-17 12:48:48-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-17 13:04:50-04:00
log4d8a6f6fea1b6922e7904b33c5b575249213fe53
treed2ad5906dd41a73827475157619ac36976b4092d
parent456a244d62df62940f4d860cd9f57b40d563ca96
signaturelock-open Commit is signed but in an unrecognized format.

fix compiler not checking alignment of function frames

closes #3086

6 files changed, 57 insertions(+), 11 deletions(-)

doc/langref.html.in+9-4
...@@ -6379,7 +6379,7 @@ comptime {...@@ -6379,7 +6379,7 @@ comptime {
6379 {#header_close#}6379 {#header_close#}
63806380
6381 {#header_open|@asyncCall#}6381 {#header_open|@asyncCall#}
6382 <pre>{#syntax#}@asyncCall(frame_buffer: []u8, result_ptr, function_ptr, args: ...) anyframe->T{#endsyntax#}</pre>6382 <pre>{#syntax#}@asyncCall(frame_buffer: []align(@alignOf(@Frame(anyAsyncFunction))) u8, result_ptr, function_ptr, args: ...) anyframe->T{#endsyntax#}</pre>
6383 <p>6383 <p>
6384 {#syntax#}@asyncCall{#endsyntax#} performs an {#syntax#}async{#endsyntax#} call on a function pointer,6384 {#syntax#}@asyncCall{#endsyntax#} performs an {#syntax#}async{#endsyntax#} call on a function pointer,
6385 which may or may not be an {#link|async function|Async Functions#}.6385 which may or may not be an {#link|async function|Async Functions#}.
...@@ -6405,7 +6405,7 @@ test "async fn pointer in a struct field" {...@@ -6405,7 +6405,7 @@ test "async fn pointer in a struct field" {
6405 bar: async fn (*i32) void,6405 bar: async fn (*i32) void,
6406 };6406 };
6407 var foo = Foo{ .bar = func };6407 var foo = Foo{ .bar = func };
6408 var bytes: [64]u8 = undefined;6408 var bytes: [64]u8 align(@alignOf(@Frame(func))) = undefined;
6409 const f = @asyncCall(&bytes, {}, foo.bar, &data);6409 const f = @asyncCall(&bytes, {}, foo.bar, &data);
6410 assert(data == 2);6410 assert(data == 2);
6411 resume f;6411 resume f;
...@@ -7322,17 +7322,22 @@ mem.set(u8, dest, c);{#endsyntax#}</pre>...@@ -7322,17 +7322,22 @@ mem.set(u8, dest, c);{#endsyntax#}</pre>
7322 {#header_close#}7322 {#header_close#}
73237323
7324 {#header_open|@newStackCall#}7324 {#header_open|@newStackCall#}
7325 <pre>{#syntax#}@newStackCall(new_stack: []u8, function: var, args: ...) var{#endsyntax#}</pre>7325 <pre>{#syntax#}@newStackCall(new_stack: []align(target_stack_align) u8, function: var, args: ...) var{#endsyntax#}</pre>
7326 <p>7326 <p>
7327 This calls a function, in the same way that invoking an expression with parentheses does. However,7327 This calls a function, in the same way that invoking an expression with parentheses does. However,
7328 instead of using the same stack as the caller, the function uses the stack provided in the {#syntax#}new_stack{#endsyntax#}7328 instead of using the same stack as the caller, the function uses the stack provided in the {#syntax#}new_stack{#endsyntax#}
7329 parameter.7329 parameter.
7330 </p>7330 </p>
7331 <p>
7332 The new stack must be aligned to {#syntax#}target_stack_align{#endsyntax#} bytes. This is a target-specific
7333 number. A safe value that will work on all targets is {#syntax#}16{#endsyntax#}. This value can
7334 also be obtained by using {#link|@sizeOf#} on the {#link|@Frame#} type of {#link|Async Functions#}.
7335 </p>
7331 {#code_begin|test#}7336 {#code_begin|test#}
7332const std = @import("std");7337const std = @import("std");
7333const assert = std.debug.assert;7338const assert = std.debug.assert;
73347339
7335var new_stack_bytes: [1024]u8 = undefined;7340var new_stack_bytes: [1024]u8 align(16) = undefined;
73367341
7337test "calling a function with a new stack" {7342test "calling a function with a new stack" {
7338 const arg = 1234;7343 const arg = 1234;
src/ir.cpp+21-2
...@@ -12110,7 +12110,26 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -12110,7 +12110,26 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
12110 array_type->data.array.child_type, source_node,12110 array_type->data.array.child_type, source_node,
12111 !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk)12111 !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk)
12112 {12112 {
12113 return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, wanted_type, result_loc);12113 // If the pointers both have ABI align, it works.
12114 bool ok_align = slice_ptr_type->data.pointer.explicit_alignment == 0 &&
12115 actual_type->data.pointer.explicit_alignment == 0;
12116 if (!ok_align) {
12117 // If either one has non ABI align, we have to resolve them both
12118 if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type,
12119 ResolveStatusAlignmentKnown)))
12120 {
12121 return ira->codegen->invalid_instruction;
12122 }
12123 if ((err = type_resolve(ira->codegen, slice_ptr_type->data.pointer.child_type,
12124 ResolveStatusAlignmentKnown)))
12125 {
12126 return ira->codegen->invalid_instruction;
12127 }
12128 ok_align = get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, slice_ptr_type);
12129 }
12130 if (ok_align) {
12131 return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, wanted_type, result_loc);
12132 }
12114 }12133 }
12115 }12134 }
1211612135
...@@ -15421,7 +15440,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c...@@ -15421,7 +15440,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c
15421 IrInstruction *casted_new_stack = nullptr;15440 IrInstruction *casted_new_stack = nullptr;
15422 if (call_instruction->new_stack != nullptr) {15441 if (call_instruction->new_stack != nullptr) {
15423 ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,15442 ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
15424 false, false, PtrLenUnknown, 0, 0, 0, false);15443 false, false, PtrLenUnknown, target_fn_align(ira->codegen->zig_target), 0, 0, false);
15425 ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr);15444 ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr);
15426 IrInstruction *new_stack = call_instruction->new_stack->child;15445 IrInstruction *new_stack = call_instruction->new_stack->child;
15427 if (type_is_invalid(new_stack->value.type))15446 if (type_is_invalid(new_stack->value.type))
test/compile_errors.zig+23-1
...@@ -2,6 +2,28 @@ const tests = @import("tests.zig");...@@ -2,6 +2,28 @@ const tests = @import("tests.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
33
4pub fn addCases(cases: *tests.CompileErrorContext) void {4pub fn addCases(cases: *tests.CompileErrorContext) void {
5 cases.add(
6 "bad alignment in @asyncCall",
7 \\export fn entry() void {
8 \\ var ptr: async fn () void = func;
9 \\ var bytes: [64]u8 = undefined;
10 \\ _ = @asyncCall(&bytes, {}, ptr);
11 \\}
12 \\async fn func() void {}
13 ,
14 "tmp.zig:4:21: error: expected type '[]align(16) u8', found '*[64]u8'",
15 );
16
17 cases.add(
18 "bad alignment in implicit cast from array pointer to slice",
19 \\export fn a() void {
20 \\ var x: [10]u8 = undefined;
21 \\ var y: []align(16) u8 = &x;
22 \\}
23 ,
24 "tmp.zig:3:30: error: expected type '[]align(16) u8', found '*[10]u8'",
25 );
26
5 cases.add(27 cases.add(
6 "result location incompatibility mismatching handle_is_ptr (generic call)",28 "result location incompatibility mismatching handle_is_ptr (generic call)",
7 \\export fn entry() void {29 \\export fn entry() void {
...@@ -164,7 +186,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -164,7 +186,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
164 "non async function pointer passed to @asyncCall",186 "non async function pointer passed to @asyncCall",
165 \\export fn entry() void {187 \\export fn entry() void {
166 \\ var ptr = afunc;188 \\ var ptr = afunc;
167 \\ var bytes: [100]u8 = undefined;189 \\ var bytes: [100]u8 align(16) = undefined;
168 \\ _ = @asyncCall(&bytes, {}, ptr);190 \\ _ = @asyncCall(&bytes, {}, ptr);
169 \\}191 \\}
170 \\fn afunc() void { }192 \\fn afunc() void { }
test/runtime_safety.zig+1-1
...@@ -30,7 +30,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -30,7 +30,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
30 \\ @import("std").os.exit(126);30 \\ @import("std").os.exit(126);
31 \\}31 \\}
32 \\pub fn main() void {32 \\pub fn main() void {
33 \\ var bytes: [1]u8 = undefined;33 \\ var bytes: [1]u8 align(16) = undefined;
34 \\ var ptr = other;34 \\ var ptr = other;
35 \\ var frame = @asyncCall(&bytes, {}, ptr);35 \\ var frame = @asyncCall(&bytes, {}, ptr);
36 \\}36 \\}
test/stage1/behavior/async_fn.zig+2-2
...@@ -280,7 +280,7 @@ test "async fn pointer in a struct field" {...@@ -280,7 +280,7 @@ test "async fn pointer in a struct field" {
280 bar: async fn (*i32) void,280 bar: async fn (*i32) void,
281 };281 };
282 var foo = Foo{ .bar = simpleAsyncFn2 };282 var foo = Foo{ .bar = simpleAsyncFn2 };
283 var bytes: [64]u8 = undefined;283 var bytes: [64]u8 align(16) = undefined;
284 const f = @asyncCall(&bytes, {}, foo.bar, &data);284 const f = @asyncCall(&bytes, {}, foo.bar, &data);
285 comptime expect(@typeOf(f) == anyframe->void);285 comptime expect(@typeOf(f) == anyframe->void);
286 expect(data == 2);286 expect(data == 2);
...@@ -317,7 +317,7 @@ test "@asyncCall with return type" {...@@ -317,7 +317,7 @@ test "@asyncCall with return type" {
317 }317 }
318 };318 };
319 var foo = Foo{ .bar = Foo.middle };319 var foo = Foo{ .bar = Foo.middle };
320 var bytes: [150]u8 = undefined;320 var bytes: [150]u8 align(16) = undefined;
321 var aresult: i32 = 0;321 var aresult: i32 = 0;
322 _ = @asyncCall(&bytes, &aresult, foo.bar);322 _ = @asyncCall(&bytes, &aresult, foo.bar);
323 expect(aresult == 0);323 expect(aresult == 0);
test/stage1/behavior/new_stack_call.zig+1-1
...@@ -1,7 +1,7 @@...@@ -1,7 +1,7 @@
1const std = @import("std");1const std = @import("std");
2const expect = std.testing.expect;2const expect = std.testing.expect;
33
4var new_stack_bytes: [1024]u8 = undefined;4var new_stack_bytes: [1024]u8 align(16) = undefined;
55
6test "calling a function with a new stack" {6test "calling a function with a new stack" {
7 const arg = 1234;7 const arg = 1234;