From 46eb77dbb200756b96bfae4c5166397fefba66d0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 23 Sep 2016 02:00:23 -0400 Subject: [PATCH] stack trace is able to figure out compilation unit each address is contained within also fix a bug having to do with codegen for enum value initialization expressions --- src/codegen.cpp | 2 +- std/cstr.zig | 36 +-- std/debug.zig | 377 +++++++++++++++++++-------- std/dwarf.zig | 3 + std/io.zig | 27 +- std/list.zig | 47 ++-- std/mem.zig | 30 +++ test/cases/switch_prong_err_enum.zig | 29 +++ test/self_hosted.zig | 1 + 9 files changed, 377 insertions(+), 175 deletions(-) create mode 100644 test/cases/switch_prong_err_enum.zig diff --git a/src/codegen.cpp b/src/codegen.cpp index 6ad0854158..8c2d898ea1 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -729,7 +729,6 @@ static LLVMValueRef gen_enum_value_expr(CodeGen *g, AstNode *node, TypeTableEntr LLVMValueRef new_union_val = gen_expr(g, arg_node); if (arg_node) { arg_node_type = get_expr_type(arg_node); - new_union_val = gen_expr(g, arg_node); } else { arg_node_type = g->builtin_types.entry_void; } @@ -3460,6 +3459,7 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) { zig_unreachable(); } if (make_item_blocks) { + set_debug_source_node(g, var_node); LLVMBuildBr(g->builder, prong_block); } } else { diff --git a/std/cstr.zig b/std/cstr.zig index dbf25811fd..d8f797a388 100644 --- a/std/cstr.zig +++ b/std/cstr.zig @@ -38,33 +38,37 @@ pub struct CBuf { list: List(u8), /// Must deinitialize with deinit. - pub fn init(self: &CBuf, allocator: &Allocator) { - self.list.init(allocator); - // This resize is guaranteed to not have an error because we use a list - // with preallocated memory of at least 1 byte. - %%self.resize(0); + pub fn initEmpty(allocator: &Allocator) -> %CBuf { + const self = CBuf { + .list = List(u8).init(allocator), + }; + %return self.resize(0); + return self; } /// Must deinitialize with deinit. - pub fn initFromMem(self: &CBuf, allocator: &Allocator, m: []const u8) -> %void { - self.init(allocator); + pub fn initFromMem(allocator: &Allocator, m: []const u8) -> %CBuf { + const self = CBuf { + .list = List(u8).init(allocator), + }; %return self.resize(m.len); mem.copy(u8, self.list.items, m); + return self; } /// Must deinitialize with deinit. - pub fn initFromCStr(self: &CBuf, allocator: &Allocator, s: &const u8) -> %void { - self.initFromMem(allocator, s[0...strlen(s)]) + pub fn initFromCStr(allocator: &Allocator, s: &const u8) -> %CBuf { + return CBuf.initFromMem(allocator, s[0...strlen(s)]); } /// Must deinitialize with deinit. - pub fn initFromCBuf(self: &CBuf, cbuf: &const CBuf) -> %void { - self.initFromMem(cbuf.list.allocator, cbuf.list.items[0...cbuf.len()]) + pub fn initFromCBuf(cbuf: &const CBuf) -> %CBuf { + return CBuf.initFromMem(cbuf.list.allocator, cbuf.list.items[0...cbuf.len()]); } /// Must deinitialize with deinit. - pub fn initFromSlice(self: &CBuf, other: &const CBuf, start: usize, end: usize) -> %void { - self.initFromMem(other.list.allocator, other.list.items[start...end]) + pub fn initFromSlice(other: &const CBuf, start: usize, end: usize) -> %CBuf { + return CBuf.initFromMem(other.list.allocator, other.list.items[start...end]); } pub fn deinit(self: &CBuf) { @@ -124,8 +128,7 @@ pub struct CBuf { #attribute("test") fn testSimpleCBuf() { - var buf: CBuf = undefined; - buf.init(&debug.global_allocator); + var buf = %%CBuf.initEmpty(&debug.global_allocator); assert(buf.len() == 0); %%buf.appendCStr(c"hello"); %%buf.appendChar(' '); @@ -133,8 +136,7 @@ fn testSimpleCBuf() { assert(buf.eqlCStr(c"hello world")); assert(buf.eqlMem("hello world")); - var buf2: CBuf = undefined; - %%buf2.initFromCBuf(&buf); + var buf2 = %%CBuf.initFromCBuf(&buf); assert(buf.eqlCBuf(&buf2)); assert(buf.startsWithMem("hell")); diff --git a/std/debug.zig b/std/debug.zig index bdd33298d8..0001bc11ac 100644 --- a/std/debug.zig +++ b/std/debug.zig @@ -1,4 +1,4 @@ -const Allocator = @import("mem.zig").Allocator; +const mem = @import("mem.zig"); const io = @import("io.zig"); const os = @import("os.zig"); const elf = @import("elf.zig"); @@ -21,28 +21,38 @@ pub fn printStackTrace() -> %void { pub fn writeStackTrace(out_stream: &io.OutStream) -> %void { switch (@compileVar("object_format")) { elf => { - var st: ElfStackTrace = undefined; + var stack_trace = ElfStackTrace { + .self_exe_stream = undefined, + .elf = undefined, + .debug_info = undefined, + .debug_abbrev = undefined, + .debug_str = undefined, + .abbrev_table_list = List(AbbrevTableHeader).init(&global_allocator), + .compile_unit_list = List(CompileUnit).init(&global_allocator), + }; + const st = &stack_trace; %return io.openSelfExe(&st.self_exe_stream); - defer %return st.self_exe_stream.close(); + defer st.self_exe_stream.close() %% {}; %return st.elf.openStream(&global_allocator, &st.self_exe_stream); defer %return st.elf.close(); - st.aranges = %return st.elf.findSection(".debug_aranges"); st.debug_info = (%return st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo; st.debug_abbrev = (%return st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo; + st.debug_str = (%return st.elf.findSection(".debug_str")) ?? return error.MissingDebugInfo; + %return scanAllCompileUnits(st); var maybe_fp: ?&const u8 = @frameAddress(); while (true) { const fp = maybe_fp ?? break; const return_address = *(&const usize)(usize(fp) + @sizeOf(usize)); - // read .debug_aranges to find out which compile unit the address is in - const compile_unit_offset = %return findCompileUnitOffset(&st, return_address); + const compile_unit = findCompileUnit(st, return_address) ?? return error.MissingDebugInfo; + const name = %return compile_unit.die.getAttrString(st, DW.AT_name); %return out_stream.printInt(usize, return_address); %return out_stream.printf(" -> "); - %return out_stream.printInt(u64, compile_unit_offset); + %return out_stream.printf(name); %return out_stream.printf("\n"); maybe_fp = *(&const ?&const u8)(fp); } @@ -62,9 +72,38 @@ pub fn writeStackTrace(out_stream: &io.OutStream) -> %void { struct ElfStackTrace { self_exe_stream: io.InStream, elf: elf.Elf, - aranges: ?&elf.SectionHeader, debug_info: &elf.SectionHeader, debug_abbrev: &elf.SectionHeader, + debug_str: &elf.SectionHeader, + abbrev_table_list: List(AbbrevTableHeader), + compile_unit_list: List(CompileUnit), +} + +struct CompileUnit { + is_64: bool, + die: &Die, + pc_start: u64, + pc_end: u64, +} + +const AbbrevTable = List(AbbrevTableEntry); + +struct AbbrevTableHeader { + // offset from .debug_abbrev + offset: u64, + table: AbbrevTable, +} + +struct AbbrevTableEntry { + has_children: bool, + abbrev_code: u64, + tag_id: u64, + attrs: List(AbbrevAttr), +} + +struct AbbrevAttr { + attr_id: u64, + form_id: u64, } enum FormValue { @@ -84,8 +123,76 @@ enum FormValue { struct Constant { payload: []u8, signed: bool, + + fn asUnsignedLe(self: &const Constant) -> %u64 { + if (self.payload.len > @sizeOf(u64)) + return error.InvalidDebugInfo; + if (self.signed) + return error.InvalidDebugInfo; + return mem.sliceAsInt(self.payload, false, u64); + } } +struct Die { + tag_id: u64, + has_children: bool, + attrs: List(Attr), + + struct Attr { + id: u64, + value: FormValue, + } + + fn getAttr(self: &const Die, id: u64) -> ?&const FormValue { + for (self.attrs.toSlice()) |*attr| { + if (attr.id == id) + return &attr.value; + } + return null; + } + + fn getAttrAddr(self: &const Die, id: u64) -> %u64 { + const form_value = self.getAttr(id) ?? return error.InvalidDebugInfo; + return switch (*form_value) { + Address => |value| value, + else => error.InvalidDebugInfo, + }; + } + + fn getAttrUnsignedLe(self: &const Die, id: u64) -> %u64 { + const form_value = self.getAttr(id) ?? return error.InvalidDebugInfo; + return switch (*form_value) { + Const => |value| value.asUnsignedLe(), + else => error.InvalidDebugInfo, + }; + } + + fn getAttrString(self: &const Die, st: &ElfStackTrace, id: u64) -> %[]u8 { + const form_value = self.getAttr(id) ?? return error.InvalidDebugInfo; + return switch (*form_value) { + String => |value| value, + StrPtr => |offset| getString(st, offset), + else => error.InvalidDebugInfo, + } + } +} + +fn readString(in_stream: &io.InStream) -> %[]u8 { + var buf = List(u8).init(&global_allocator); + while (true) { + const byte = %return in_stream.readByte(); + if (byte == 0) + break; + %return buf.append(byte); + } + return buf.items; +} + +fn getString(st: &ElfStackTrace, offset: u64) -> %[]u8 { + const pos = st.debug_str.offset + offset; + %return st.self_exe_stream.seekTo(pos); + return readString(&st.self_exe_stream); +} fn readAllocBytes(in_stream: &io.InStream, size: usize) -> %[]u8 { const buf = %return global_allocator.alloc(u8, size); @@ -99,25 +206,19 @@ fn parseFormValueBlockLen(in_stream: &io.InStream, size: usize) -> %FormValue { return FormValue.Block { buf }; } -fn parseFormValueBlock(in_stream: &io.InStream, inline T: type) -> %FormValue { - const block_len = %return in_stream.readIntLe(T); +fn parseFormValueBlock(in_stream: &io.InStream, size: usize) -> %FormValue { + const block_len = %return in_stream.readVarInt(false, usize, size); return parseFormValueBlockLen(in_stream, block_len); } -fn parseFormValueConstantLen(in_stream: &io.InStream, signed: bool, size: usize) -> %FormValue { - const buf = %return readAllocBytes(in_stream, size); - return FormValue.Const { Constant { +fn parseFormValueConstant(in_stream: &io.InStream, signed: bool, size: usize) -> %FormValue { + FormValue.Const { Constant { .signed = signed, - .payload = buf, - }}; + .payload = %return readAllocBytes(in_stream, size), + }} } -fn parseFormValueConstant(in_stream: &io.InStream, signed: bool, inline T: type) -> %FormValue { - const block_len = %return in_stream.readIntLe(T); - return parseFormValueConstantLen(in_stream, signed, block_len); -} - -fn parseFormValueAddrSize(in_stream: &io.InStream, is_64: bool) -> %u64 { +fn parseFormValueDwarfOffsetSize(in_stream: &io.InStream, is_64: bool) -> %u64 { return if (is_64) { %return in_stream.readIntLe(u64) } else { @@ -125,6 +226,16 @@ fn parseFormValueAddrSize(in_stream: &io.InStream, is_64: bool) -> %u64 { }; } +fn parseFormValueTargetAddrSize(in_stream: &io.InStream) -> %u64 { + return if (@sizeOf(usize) == 4) { + u64(%return in_stream.readIntLe(u32)) + } else if (@sizeOf(usize) == 8) { + %return in_stream.readIntLe(u64) + } else { + @unreachable(); + }; +} + fn parseFormValueRefLen(in_stream: &io.InStream, size: usize) -> %FormValue { const buf = %return readAllocBytes(in_stream, size); return FormValue.Ref { buf }; @@ -137,24 +248,22 @@ fn parseFormValueRef(in_stream: &io.InStream, inline T: type) -> %FormValue { fn parseFormValue(in_stream: &io.InStream, form_id: u64, is_64: bool) -> %FormValue { return switch (form_id) { - DW.FORM_addr => FormValue.Address { - %return parseFormValueAddrSize(in_stream, is_64) - }, - DW.FORM_block1 => parseFormValueBlock(in_stream, u8), - DW.FORM_block2 => parseFormValueBlock(in_stream, u16), - DW.FORM_block4 => parseFormValueBlock(in_stream, u32), + DW.FORM_addr => FormValue.Address { %return parseFormValueTargetAddrSize(in_stream) }, + DW.FORM_block1 => parseFormValueBlock(in_stream, 1), + DW.FORM_block2 => parseFormValueBlock(in_stream, 2), + DW.FORM_block4 => parseFormValueBlock(in_stream, 4), DW.FORM_block => { const block_len = %return readULeb128(in_stream); parseFormValueBlockLen(in_stream, block_len) }, - DW.FORM_data1 => parseFormValueConstant(in_stream, false, u8), - DW.FORM_data2 => parseFormValueConstant(in_stream, false, u16), - DW.FORM_data4 => parseFormValueConstant(in_stream, false, u32), - DW.FORM_data8 => parseFormValueConstant(in_stream, false, u64), + DW.FORM_data1 => parseFormValueConstant(in_stream, false, 1), + DW.FORM_data2 => parseFormValueConstant(in_stream, false, 2), + DW.FORM_data4 => parseFormValueConstant(in_stream, false, 4), + DW.FORM_data8 => parseFormValueConstant(in_stream, false, 8), DW.FORM_udata, DW.FORM_sdata => { const block_len = %return readULeb128(in_stream); const signed = form_id == DW.FORM_sdata; - parseFormValueConstantLen(in_stream, signed, block_len) + parseFormValueConstant(in_stream, signed, block_len) }, DW.FORM_exprloc => { const size = %return readULeb128(in_stream); @@ -164,7 +273,7 @@ fn parseFormValue(in_stream: &io.InStream, form_id: u64, is_64: bool) -> %FormVa DW.FORM_flag => FormValue.Flag { (%return in_stream.readByte()) != 0 }, DW.FORM_flag_present => FormValue.Flag { true }, DW.FORM_sec_offset => FormValue.SecOffset { - %return parseFormValueAddrSize(in_stream, is_64) + %return parseFormValueDwarfOffsetSize(in_stream, is_64) }, DW.FORM_ref1 => parseFormValueRef(in_stream, u8), @@ -176,22 +285,11 @@ fn parseFormValue(in_stream: &io.InStream, form_id: u64, is_64: bool) -> %FormVa parseFormValueRefLen(in_stream, ref_len) }, - DW.FORM_ref_addr => FormValue.RefAddr { %return parseFormValueAddrSize(in_stream, is_64) }, + DW.FORM_ref_addr => FormValue.RefAddr { %return parseFormValueDwarfOffsetSize(in_stream, is_64) }, DW.FORM_ref_sig8 => FormValue.RefSig8 { %return in_stream.readIntLe(u64) }, - DW.FORM_string => { - var buf: List(u8) = undefined; - buf.init(&global_allocator); - while (true) { - const byte = %return in_stream.readByte(); - if (byte == 0) - break; - %return buf.append(byte); - } - - FormValue.String { buf.items } - }, - DW.FORM_strp => FormValue.StrPtr { %return parseFormValueAddrSize(in_stream, is_64) }, + DW.FORM_string => FormValue.String { %return readString(in_stream) }, + DW.FORM_strp => FormValue.StrPtr { %return parseFormValueDwarfOffsetSize(in_stream, is_64) }, DW.FORM_indirect => { const child_form_id = %return readULeb128(in_stream); parseFormValue(in_stream, child_form_id, is_64) @@ -200,16 +298,87 @@ fn parseFormValue(in_stream: &io.InStream, form_id: u64, is_64: bool) -> %FormVa } } -fn findCompileUnitOffset(st: &ElfStackTrace, target_address: usize) -> %u64 { - if (const result ?= %return arangesOffset(st, target_address)) - return result; - - // iterate over compile units looking for a match with the low pc and high pc - %return st.elf.seekToSection(st.debug_info); - +fn parseAbbrevTable(in_stream: &io.InStream) -> %AbbrevTable { + var result = AbbrevTable.init(&global_allocator); while (true) { + const abbrev_code = %return readULeb128(in_stream); + if (abbrev_code == 0) + return result; + %return result.append(AbbrevTableEntry { + .abbrev_code = abbrev_code, + .tag_id = %return readULeb128(in_stream), + .has_children = (%return in_stream.readByte()) == DW.CHILDREN_yes, + .attrs = List(AbbrevAttr).init(&global_allocator), + }); + const attrs = &result.items[result.len - 1].attrs; + + while (true) { + const attr_id = %return readULeb128(in_stream); + const form_id = %return readULeb128(in_stream); + if (attr_id == 0 && form_id == 0) + break; + %return attrs.append(AbbrevAttr { + .attr_id = attr_id, + .form_id = form_id, + }); + } + } +} + +/// Gets an already existing AbbrevTable given the abbrev_offset, or if not found, +/// seeks in the stream and parses it. +fn getAbbrevTable(st: &ElfStackTrace, abbrev_offset: u64) -> %&AbbrevTable { + for (st.abbrev_table_list.toSlice()) |header| { + if (header.offset == abbrev_offset) { + return &header.table; + } + } + %return st.self_exe_stream.seekTo(st.debug_abbrev.offset + abbrev_offset); + %return st.abbrev_table_list.append(AbbrevTableHeader { + .offset = abbrev_offset, + .table = %return parseAbbrevTable(&st.self_exe_stream), + }); + return &st.abbrev_table_list.items[st.abbrev_table_list.len - 1].table; +} + +fn getAbbrevTableEntry(abbrev_table: &const AbbrevTable, abbrev_code: u64) -> ?&const AbbrevTableEntry { + for (abbrev_table.toSlice()) |*table_entry| { + if (table_entry.abbrev_code == abbrev_code) + return table_entry; + } + return null; +} + +fn parseDie(in_stream: &io.InStream, abbrev_table: &const AbbrevTable, is_64: bool) -> %Die { + const abbrev_code = %return readULeb128(in_stream); + const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) ?? return error.InvalidDebugInfo; + + var result = Die { + .tag_id = table_entry.tag_id, + .has_children = table_entry.has_children, + .attrs = List(Die.Attr).init(&global_allocator), + }; + %return result.attrs.resize(table_entry.attrs.len); + for (table_entry.attrs.toSlice()) |attr, i| { + result.attrs.items[i] = Die.Attr { + .id = attr.attr_id, + .value = %return parseFormValue(in_stream, attr.form_id, is_64), + }; + } + return result; +} + +fn scanAllCompileUnits(st: &ElfStackTrace) -> %void { + const debug_info_end = st.debug_info.offset + st.debug_info.size; + var this_unit_offset = st.debug_info.offset; + while (this_unit_offset < debug_info_end) { + %return st.self_exe_stream.seekTo(this_unit_offset); + var is_64: bool = undefined; const unit_length = %return readInitialLength(&st.self_exe_stream, &is_64); + if (unit_length == 0) + return; + const next_offset = unit_length + (if (is_64) usize(12) else usize(4)); const version = %return st.self_exe_stream.readInt(st.elf.is_big_endian, u16); if (version != 4) return error.InvalidDebugInfo; @@ -223,12 +392,47 @@ fn findCompileUnitOffset(st: &ElfStackTrace, target_address: usize) -> %u64 { const address_size = %return st.self_exe_stream.readByte(); if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; - const abbrev_tag_id = %return st.self_exe_stream.readByte(); + const compile_unit_pos = %return st.self_exe_stream.getPos(); + const abbrev_table = %return getAbbrevTable(st, debug_abbrev_offset); + %return st.self_exe_stream.seekTo(compile_unit_pos); + const compile_unit_die = (%return global_allocator.alloc(Die, 1)).ptr; + *compile_unit_die = %return parseDie(&st.self_exe_stream, abbrev_table, is_64); + + if (compile_unit_die.tag_id != DW.TAG_compile_unit) + return error.InvalidDebugInfo; + const low_pc = %return compile_unit_die.getAttrAddr(DW.AT_low_pc); + + const high_pc_value = compile_unit_die.getAttr(DW.AT_high_pc) ?? return error.MissingDebugInfo; + const pc_end = switch (*high_pc_value) { + Address => |value| value, + Const => |value| { + const offset = %return value.asUnsignedLe(); + low_pc + offset + }, + else => return error.InvalidDebugInfo, + }; + + %return st.compile_unit_list.append(CompileUnit { + .is_64 = is_64, + .pc_start = low_pc, + .pc_end = pc_end, + .die = compile_unit_die, + }); + + this_unit_offset += next_offset; } } +fn findCompileUnit(st: &ElfStackTrace, target_address: u64) -> ?&const CompileUnit { + for (st.compile_unit_list.toSlice()) |*compile_unit| { + if (target_address >= compile_unit.pc_start && target_address < compile_unit.pc_end) + return compile_unit; + } + return null; +} + fn readInitialLength(in_stream: &io.InStream, is_64: &bool) -> %u64 { const first_32_bits = %return in_stream.readIntLe(u32); *is_64 = (first_32_bits == 0xffffffff); @@ -283,79 +487,26 @@ fn readILeb128(in_stream: &io.InStream) -> %i64 { } } -fn arangesOffset(st: &ElfStackTrace, target_address: usize) -> %?u64 { - // TODO ability to implicitly cast null to %?T - const aranges = st.aranges ?? return (?u64)(null); - - %return st.elf.seekToSection(aranges); - - const first_32_bits = %return st.self_exe_stream.readIntLe(u32); - var is_64: bool = undefined; - const unit_length = %return readInitialLength(&st.self_exe_stream, &is_64); - var unit_index: u64 = 0; - - while (unit_index < unit_length) { - const version = %return st.self_exe_stream.readIntLe(u16); - if (version != 2) return error.InvalidDebugInfo; - unit_index += 2; - - const debug_info_offset = if (is_64) { - unit_index += 4; - %return st.self_exe_stream.readIntLe(u64) - } else { - unit_index += 2; - %return st.self_exe_stream.readIntLe(u32) - }; - - const address_size = %return st.self_exe_stream.readByte(); - if (address_size > 8) return error.UnsupportedDebugInfo; - unit_index += 1; - - const segment_size = %return st.self_exe_stream.readByte(); - if (segment_size > 0) return error.UnsupportedDebugInfo; - unit_index += 1; - - const align = segment_size + 2 * address_size; - const padding = (%return st.self_exe_stream.getPos()) % align; - %return st.self_exe_stream.seekForward(padding); - unit_index += padding; - - while (true) { - const address = %return st.self_exe_stream.readVarInt(false, u64, address_size); - const length = %return st.self_exe_stream.readVarInt(false, u64, address_size); - unit_index += align; - if (address == 0 && length == 0) break; - - if (target_address >= address && target_address < address + length) { - // TODO ability to implicitly cast T to %?T - return (?u64)(debug_info_offset); - } - } - } - - return error.MissingDebugInfo; -} - -pub var global_allocator = Allocator { +pub var global_allocator = mem.Allocator { .allocFn = globalAlloc, .reallocFn = globalRealloc, .freeFn = globalFree, .context = null, }; -var some_mem: [10 * 1024]u8 = undefined; +var some_mem: [100 * 1024]u8 = undefined; var some_mem_index: usize = 0; -fn globalAlloc(self: &Allocator, n: usize) -> %[]u8 { +fn globalAlloc(self: &mem.Allocator, n: usize) -> %[]u8 { const result = some_mem[some_mem_index ... some_mem_index + n]; some_mem_index += n; return result; } -fn globalRealloc(self: &Allocator, old_mem: []u8, new_size: usize) -> %[]u8 { +fn globalRealloc(self: &mem.Allocator, old_mem: []u8, new_size: usize) -> %[]u8 { const result = %return globalAlloc(self, new_size); @memcpy(result.ptr, old_mem.ptr, old_mem.len); return result; } -fn globalFree(self: &Allocator, old_mem: []u8) { } +fn globalFree(self: &mem.Allocator, old_mem: []u8) { } diff --git a/std/dwarf.zig b/std/dwarf.zig index 319bcef982..ba554a198f 100644 --- a/std/dwarf.zig +++ b/std/dwarf.zig @@ -617,3 +617,6 @@ pub const CFA_MIPS_advance_loc8 = 0x1d; pub const CFA_GNU_window_save = 0x2d; pub const CFA_GNU_args_size = 0x2e; pub const CFA_GNU_negative_offset_extended = 0x2f; + +pub const CHILDREN_no = 0x00; +pub const CHILDREN_yes = 0x01; diff --git a/std/io.zig b/std/io.zig index 9c30233cb4..938368c585 100644 --- a/std/io.zig +++ b/std/io.zig @@ -10,6 +10,7 @@ const endian = @import("endian.zig"); const debug = @import("debug.zig"); const assert = debug.assert; const os = @import("os.zig"); +const mem = @import("mem.zig"); pub const stdin_fileno = 0; pub const stdout_fileno = 1; @@ -261,34 +262,28 @@ pub struct InStream { return result[0]; } - pub inline fn readIntLe(is: &InStream, inline T: type) -> %T { + pub fn readIntLe(is: &InStream, inline T: type) -> %T { is.readInt(false, T) } - pub inline fn readIntBe(is: &InStream, inline T: type) -> %T { + pub fn readIntBe(is: &InStream, inline T: type) -> %T { is.readInt(true, T) } - pub inline fn readInt(is: &InStream, is_be: bool, inline T: type) -> %T { + pub fn readInt(is: &InStream, is_be: bool, inline T: type) -> %T { var result: T = undefined; const result_slice = ([]u8)((&result)[0...1]); %return is.readNoEof(result_slice); return endian.swapIf(!is_be, T, result); } - pub inline fn readVarInt(is: &InStream, is_be: bool, inline T: type, size: usize) -> %T { - var result: T = zeroes; - const result_slice = ([]u8)((&result)[0...1]); - const padding = @sizeOf(T) - size; - {var i: usize = 0; while (i < size; i += 1) { - const index = if (is_be == @compileVar("is_big_endian")) { - padding + i - } else { - result_slice.len - i - 1 - padding - }; - result_slice[index] = %return is.readByte(); - }} - return result; + pub fn readVarInt(is: &InStream, is_be: bool, inline T: type, size: usize) -> %T { + assert(size <= @sizeOf(T)); + assert(size <= 8); + var input_buf: [8]u8 = undefined; + const input_slice = input_buf[0...size]; + %return is.readNoEof(input_slice); + return mem.sliceAsInt(input_slice, is_be, T); } pub fn seekForward(is: &InStream, amount: usize) -> %void { diff --git a/std/list.zig b/std/list.zig index 7d30f83aaf..e4c8e9ac1e 100644 --- a/std/list.zig +++ b/std/list.zig @@ -3,30 +3,27 @@ const assert = debug.assert; const mem = @import("mem.zig"); const Allocator = mem.Allocator; -pub fn List(inline T: type) -> type { - SmallList(T, @sizeOf(usize)) -} - -// TODO: make sure that setting static_size to 0 codegens to the same code -// as if this were programmed without static_size at all. -pub struct SmallList(T: type, static_size: usize) { - const Self = SmallList(T, static_size); +pub struct List(T: type) { + const Self = List(T); items: []T, len: usize, - prealloc_items: [static_size]T, allocator: &Allocator, - pub fn init(l: &Self, allocator: &Allocator) { - l.items = l.prealloc_items[0...]; - l.len = 0; - l.allocator = allocator; + pub fn init(allocator: &Allocator) -> Self { + Self { + .items = zeroes, + .len = 0, + .allocator = allocator, + } } pub fn deinit(l: &Self) { - if (l.items.ptr != &l.prealloc_items[0]) { - l.allocator.free(T, l.items); - } + l.allocator.free(T, l.items); + } + + pub fn toSlice(l: &Self) -> []T { + return l.items[0...l.len]; } pub fn append(l: &Self, item: T) -> %void { @@ -43,24 +40,18 @@ pub struct SmallList(T: type, static_size: usize) { pub fn ensureCapacity(l: &Self, new_capacity: usize) -> %void { var better_capacity = l.items.len; - while (better_capacity < new_capacity) { - better_capacity *= 2; - } - if (better_capacity != l.items.len) { - if (l.items.ptr == &l.prealloc_items[0]) { - l.items = %return l.allocator.alloc(T, better_capacity); - mem.copy(T, l.items, l.prealloc_items[0...l.len]); - } else { - l.items = %return l.allocator.realloc(T, l.items, better_capacity); - } + if (better_capacity >= new_capacity) return; + while (true) { + better_capacity += better_capacity / 2 + 8; + if (better_capacity >= new_capacity) break; } + l.items = %return l.allocator.realloc(T, l.items, better_capacity); } } #attribute("test") fn basicListTest() { - var list: List(i32) = undefined; - list.init(&debug.global_allocator); + var list = List(i32).init(&debug.global_allocator); defer list.deinit(); {var i: usize = 0; while (i < 10; i += 1) { diff --git a/std/mem.zig b/std/mem.zig index 0103c71f8e..e4eb94f8da 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -60,3 +60,33 @@ pub fn cmp(inline T: type, a: []const T, b: []const T) -> Cmp { return if (a.len > b.len) Cmp.Greater else if (a.len < b.len) Cmp.Less else Cmp.Equal; } + +pub fn sliceAsInt(buf: []u8, is_be: bool, inline T: type) -> T { + var result: T = zeroes; + const result_slice = ([]u8)((&result)[0...1]); + const padding = @sizeOf(T) - buf.len; + + if (is_be == @compileVar("is_big_endian")) { + copy(u8, result_slice, buf); + } else { + for (buf) |b, i| { + const index = result_slice.len - i - 1 - padding; + result_slice[index] = b; + } + } + return result; +} + +#attribute("test") +fn testSliceAsInt() { + { + const buf = []u8{0x00, 0x00, 0x12, 0x34}; + const answer = sliceAsInt(buf[0...], true, u64); + assert(answer == 0x00001234); + } + { + const buf = []u8{0x12, 0x34, 0x00, 0x00}; + const answer = sliceAsInt(buf[0...], false, u64); + assert(answer == 0x00003412); + } +} diff --git a/test/cases/switch_prong_err_enum.zig b/test/cases/switch_prong_err_enum.zig new file mode 100644 index 0000000000..cb8c864357 --- /dev/null +++ b/test/cases/switch_prong_err_enum.zig @@ -0,0 +1,29 @@ +const assert = @import("std").debug.assert; + +var read_count: u64 = 0; + +fn readOnce() -> %u64 { + read_count += 1; + return read_count; +} + +error InvalidDebugInfo; + +enum FormValue { + Address: u64, + Other: bool, +} + +#static_eval_enable(false) +fn doThing(form_id: u64) -> %FormValue { + return switch (form_id) { + 17 => FormValue.Address { %return readOnce() }, + else => error.InvalidDebugInfo, + } +} + +#attribute("test") +fn switchProngReturnsErrorEnum() { + %%doThing(17); + assert(read_count == 1); +} diff --git a/test/self_hosted.zig b/test/self_hosted.zig index 2a8814b17a..7b58684b05 100644 --- a/test/self_hosted.zig +++ b/test/self_hosted.zig @@ -12,6 +12,7 @@ const test_max_value_type = @import("cases/max_value_type.zig"); const test_var_params = @import("cases/var_params.zig"); const test_const_slice_child = @import("cases/const_slice_child.zig"); const test_switch_prong_implicit_cast = @import("cases/switch_prong_implicit_cast.zig"); +const test_switch_prong_err_enum = @import("cases/switch_prong_err_enum.zig"); // normal comment /// this is a documentation comment