improvements targeted at improving async functions

* Reuse bytes of async function frames when non-async functions
   make `noasync` calls. This prevents explosive stack growth.
 * Zig now passes a stack size argument to the linker when linking ELF
   binaries. Linux ignores this value, but it is available as a program
   header called GNU_STACK. I prototyped some code that memory maps
   extra space to the stack using this program header, but there was
   still a problem when accessing stack memory very far down. Stack
   probing is needed or not working or something. I also prototyped
   using `@newStackCall` to call main and that does work around the
   issue but it also brings its own issues. That code is commented out
   for now in std/special/start.zig. I'm on a plane with no Internet,
   but I plan to consult with the musl community for advice when I get a
   chance.
 * Added `noasync` to a bunch of function calls in std.debug. It's very
   messy but it's a workaround that makes stack traces functional with
   evented I/O enabled. Eventually these will be cleaned up as the root
   bugs are found and fixed. Programs built in blocking mode are
   unaffected.
 * Lowered the default stack size of std.io.InStream (for the async
   version) to 1 MiB instead of 4. Until we figure out how to get
   choosing a stack size working (see 2nd bullet point above), 4 MiB
   tends to cause segfaults due to stack size running out, or usage of
   stack memory too far apart, or something like that.
 * Default thread stack size is bumped from 8 MiB to 16 to match the
   size we give for the main thread. It's planned to eventually remove
   this hard coded value and have Zig able to determine this value
   during semantic analysis, with call graph analysis and function
   pointer annotations and extern function annotations.
This commit is contained in:
Andrew Kelley
2019-09-11 20:22:49 -04:00
parent 68b49f74c4
commit cf4bccf765
7 changed files with 90 additions and 32 deletions

View File

@@ -1478,10 +1478,11 @@ const LineNumberProgram = struct {
}
};
// TODO the noasyncs here are workarounds
fn readStringRaw(allocator: *mem.Allocator, in_stream: var) ![]u8 {
var buf = ArrayList(u8).init(allocator);
while (true) {
const byte = try in_stream.readByte();
const byte = try noasync in_stream.readByte();
if (byte == 0) break;
try buf.append(byte);
}
@@ -1494,10 +1495,11 @@ fn getString(di: *DwarfInfo, offset: u64) ![]u8 {
return di.readString();
}
// TODO the noasyncs here are workarounds
fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 {
const buf = try allocator.alloc(u8, size);
errdefer allocator.free(buf);
if ((try in_stream.read(buf)) < size) return error.EndOfFile;
if ((try noasync in_stream.read(buf)) < size) return error.EndOfFile;
return buf;
}
@@ -1506,8 +1508,9 @@ fn parseFormValueBlockLen(allocator: *mem.Allocator, in_stream: var, size: usize
return FormValue{ .Block = buf };
}
// TODO the noasyncs here are workarounds
fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue {
const block_len = try in_stream.readVarInt(usize, builtin.Endian.Little, size);
const block_len = try noasync in_stream.readVarInt(usize, builtin.Endian.Little, size);
return parseFormValueBlockLen(allocator, in_stream, block_len);
}
@@ -1537,27 +1540,37 @@ fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: boo
};
}
// TODO the noasyncs here are workarounds
fn parseFormValueDwarfOffsetSize(in_stream: var, is_64: bool) !u64 {
return if (is_64) try in_stream.readIntLittle(u64) else u64(try in_stream.readIntLittle(u32));
return if (is_64) try noasync in_stream.readIntLittle(u64) else u64(try noasync in_stream.readIntLittle(u32));
}
// TODO the noasyncs here are workarounds
fn parseFormValueTargetAddrSize(in_stream: var) !u64 {
return if (@sizeOf(usize) == 4) u64(try in_stream.readIntLittle(u32)) else if (@sizeOf(usize) == 8) try in_stream.readIntLittle(u64) else unreachable;
if (@sizeOf(usize) == 4) {
return u64(try noasync in_stream.readIntLittle(u32));
} else if (@sizeOf(usize) == 8) {
return noasync in_stream.readIntLittle(u64);
} else {
unreachable;
}
}
// TODO the noasyncs here are workarounds
fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, size: i32) !FormValue {
return FormValue{
.Ref = switch (size) {
1 => try in_stream.readIntLittle(u8),
2 => try in_stream.readIntLittle(u16),
4 => try in_stream.readIntLittle(u32),
8 => try in_stream.readIntLittle(u64),
-1 => try leb.readULEB128(u64, in_stream),
1 => try noasync in_stream.readIntLittle(u8),
2 => try noasync in_stream.readIntLittle(u16),
4 => try noasync in_stream.readIntLittle(u32),
8 => try noasync in_stream.readIntLittle(u64),
-1 => try noasync leb.readULEB128(u64, in_stream),
else => unreachable,
},
};
}
// TODO the noasyncs here are workarounds
fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) anyerror!FormValue {
return switch (form_id) {
DW.FORM_addr => FormValue{ .Address = try parseFormValueTargetAddrSize(in_stream) },
@@ -1565,7 +1578,7 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64
DW.FORM_block2 => parseFormValueBlock(allocator, in_stream, 2),
DW.FORM_block4 => parseFormValueBlock(allocator, in_stream, 4),
DW.FORM_block => x: {
const block_len = try leb.readULEB128(usize, in_stream);
const block_len = try noasync leb.readULEB128(usize, in_stream);
return parseFormValueBlockLen(allocator, in_stream, block_len);
},
DW.FORM_data1 => parseFormValueConstant(allocator, in_stream, false, 1),
@@ -1577,11 +1590,11 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64
return parseFormValueConstant(allocator, in_stream, signed, -1);
},
DW.FORM_exprloc => {
const size = try leb.readULEB128(usize, in_stream);
const size = try noasync leb.readULEB128(usize, in_stream);
const buf = try readAllocBytes(allocator, in_stream, size);
return FormValue{ .ExprLoc = buf };
},
DW.FORM_flag => FormValue{ .Flag = (try in_stream.readByte()) != 0 },
DW.FORM_flag => FormValue{ .Flag = (try noasync in_stream.readByte()) != 0 },
DW.FORM_flag_present => FormValue{ .Flag = true },
DW.FORM_sec_offset => FormValue{ .SecOffset = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
@@ -1592,12 +1605,12 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64
DW.FORM_ref_udata => parseFormValueRef(allocator, in_stream, -1),
DW.FORM_ref_addr => FormValue{ .RefAddr = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
DW.FORM_ref_sig8 => FormValue{ .Ref = try in_stream.readIntLittle(u64) },
DW.FORM_ref_sig8 => FormValue{ .Ref = try noasync in_stream.readIntLittle(u64) },
DW.FORM_string => FormValue{ .String = try readStringRaw(allocator, in_stream) },
DW.FORM_strp => FormValue{ .StrPtr = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
DW.FORM_indirect => {
const child_form_id = try leb.readULEB128(u64, in_stream);
const child_form_id = try noasync leb.readULEB128(u64, in_stream);
const F = @typeOf(async parseFormValue(allocator, in_stream, child_form_id, is_64));
var frame = try allocator.create(F);
defer allocator.destroy(frame);
@@ -2400,3 +2413,9 @@ stdcallcc fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) c_long {
else => return windows.EXCEPTION_CONTINUE_SEARCH,
}
}
pub fn dumpStackPointerAddr(prefix: []const u8) void {
const sp = asm ("" : [argc] "={rsp}" (-> usize));
std.debug.warn("{} sp = 0x{x}\n", prefix, sp);
}