From 19343db5936abe5e49ea340db838fe769b499944 Mon Sep 17 00:00:00 2001 From: Andrea Orru Date: Wed, 10 Jan 2018 00:33:07 -0500 Subject: [PATCH 1/4] Intrusive linked lists --- std/linked_list.zig | 95 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 87 insertions(+), 8 deletions(-) diff --git a/std/linked_list.zig b/std/linked_list.zig index 9536e9991a..09a01e22b4 100644 --- a/std/linked_list.zig +++ b/std/linked_list.zig @@ -4,8 +4,18 @@ const assert = debug.assert; const mem = std.mem; const Allocator = mem.Allocator; -/// Generic doubly linked list. +/// Generic non-intrusive doubly linked list. pub fn LinkedList(comptime T: type) -> type { + return BaseLinkedList(T, void, ""); +} + +/// Generic intrusive doubly linked list. +pub fn IntrusiveLinkedList(comptime ParentType: type, comptime field_name: []const u8) -> type { + return BaseLinkedList(void, ParentType, field_name); +} + +/// Generic doubly linked list. +fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_name: []const u8) -> type { return struct { const Self = this; @@ -15,13 +25,23 @@ pub fn LinkedList(comptime T: type) -> type { next: ?&Node, data: T, - pub fn init(data: &const T) -> Node { + pub fn init(value: &const T) -> Node { return Node { .prev = null, .next = null, - .data = *data, + .data = *value, }; } + + pub fn initIntrusive() -> Node { + // TODO: when #678 is solved this can become `init`. + return Node.init({}); + } + + pub fn toData(node: &Node) -> &ParentType { + comptime assert(isIntrusive()); + return @fieldParentPtr(ParentType, field_name, node); + } }; first: ?&Node, @@ -40,6 +60,10 @@ pub fn LinkedList(comptime T: type) -> type { }; } + fn isIntrusive() -> bool { + return ParentType != void or field_name.len != 0; + } + /// Insert a new node after an existing one. /// /// Arguments: @@ -167,6 +191,7 @@ pub fn LinkedList(comptime T: type) -> type { /// Returns: /// A pointer to the new node. pub fn allocateNode(list: &Self, allocator: &Allocator) -> %&Node { + comptime assert(!isIntrusive()); return allocator.create(Node); } @@ -176,6 +201,7 @@ pub fn LinkedList(comptime T: type) -> type { /// node: Pointer to the node to deallocate. /// allocator: Dynamic memory allocator. pub fn destroyNode(list: &Self, node: &Node, allocator: &Allocator) { + comptime assert(!isIntrusive()); allocator.destroy(node); } @@ -188,6 +214,7 @@ pub fn LinkedList(comptime T: type) -> type { /// Returns: /// A pointer to the new node. pub fn createNode(list: &Self, data: &const T, allocator: &Allocator) -> %&Node { + comptime assert(!isIntrusive()); var node = try list.allocateNode(allocator); *node = Node.init(data); return node; @@ -199,11 +226,11 @@ test "basic linked list test" { const allocator = debug.global_allocator; var list = LinkedList(u32).init(); - var one = list.createNode(1, allocator) catch unreachable; - var two = list.createNode(2, allocator) catch unreachable; - var three = list.createNode(3, allocator) catch unreachable; - var four = list.createNode(4, allocator) catch unreachable; - var five = list.createNode(5, allocator) catch unreachable; + var one = try list.createNode(1, allocator); + var two = try list.createNode(2, allocator); + var three = try list.createNode(3, allocator); + var four = try list.createNode(4, allocator); + var five = try list.createNode(5, allocator); defer { list.destroyNode(one, allocator); list.destroyNode(two, allocator); @@ -246,3 +273,55 @@ test "basic linked list test" { assert ((??list.last ).data == 4); assert (list.len == 2); } + +const link = "link"; +const ElementList = IntrusiveLinkedList(Element, link); +const Element = struct { + value: u32, + link: IntrusiveLinkedList(Element, link).Node, +}; + +test "basic intrusive linked list test" { + const allocator = debug.global_allocator; + var list = ElementList.init(); + + var one = Element { .value = 1, .link = ElementList.Node.initIntrusive() }; + var two = Element { .value = 2, .link = ElementList.Node.initIntrusive() }; + var three = Element { .value = 3, .link = ElementList.Node.initIntrusive() }; + var four = Element { .value = 4, .link = ElementList.Node.initIntrusive() }; + var five = Element { .value = 5, .link = ElementList.Node.initIntrusive() }; + + list.append(&two.link); // {2} + list.append(&five.link); // {2, 5} + list.prepend(&one.link); // {1, 2, 5} + list.insertBefore(&five.link, &four.link); // {1, 2, 4, 5} + list.insertAfter(&two.link, &three.link); // {1, 2, 3, 4, 5} + + // Traverse forwards. + { + var it = list.first; + var index: u32 = 1; + while (it) |node| : (it = node.next) { + assert(node.toData().value == index); + index += 1; + } + } + + // Traverse backwards. + { + var it = list.last; + var index: u32 = 1; + while (it) |node| : (it = node.prev) { + assert(node.toData().value == (6 - index)); + index += 1; + } + } + + var first = list.popFirst(); // {2, 3, 4, 5} + var last = list.pop(); // {2, 3, 4} + list.remove(&three.link); // {2, 4} + + assert ((??list.first).toData().value == 2); + assert ((??list.last ).toData().value == 4); + assert (list.len == 2); +} From 24cd99160c2eeea34088ef3e3d631233d8913dc1 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Wed, 10 Jan 2018 19:53:36 +1300 Subject: [PATCH 2/4] Add hw sqrt for x86_64 --- CMakeLists.txt | 1 + std/math/sqrt.zig | 18 ++++++++++++++---- std/math/x86_64/sqrt.zig | 15 +++++++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 std/math/x86_64/sqrt.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index e1bfcf72b6..252b07e6dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -425,6 +425,7 @@ set(ZIG_STD_FILES "math/tan.zig" "math/tanh.zig" "math/trunc.zig" + "math/x86_64/sqrt.zig" "mem.zig" "net.zig" "os/child_process.zig" diff --git a/std/math/sqrt.zig b/std/math/sqrt.zig index 4d84756b4a..18cee6e248 100644 --- a/std/math/sqrt.zig +++ b/std/math/sqrt.zig @@ -18,11 +18,21 @@ pub fn sqrt(x: var) -> (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @ return T(sqrt64(x)); }, TypeId.Float => { - return switch (T) { - f32 => sqrt32(x), - f64 => sqrt64(x), + switch (T) { + f32 => { + switch (builtin.arch) { + builtin.Arch.x86_64 => return @import("x86_64/sqrt.zig").sqrt32(x), + else => return sqrt32(x), + } + }, + f64 => { + switch (builtin.arch) { + builtin.Arch.x86_64 => return @import("x86_64/sqrt.zig").sqrt64(x), + else => return sqrt64(x), + } + }, else => @compileError("sqrt not implemented for " ++ @typeName(T)), - }; + } }, TypeId.IntLiteral => comptime { if (x > @maxValue(u128)) { diff --git a/std/math/x86_64/sqrt.zig b/std/math/x86_64/sqrt.zig new file mode 100644 index 0000000000..fbb898c297 --- /dev/null +++ b/std/math/x86_64/sqrt.zig @@ -0,0 +1,15 @@ +pub fn sqrt32(x: f32) -> f32 { + return asm ( + \\sqrtss %%xmm0, %%xmm0 + : [ret] "={xmm0}" (-> f32) + : [x] "{xmm0}" (x) + ); +} + +pub fn sqrt64(x: f64) -> f64 { + return asm ( + \\sqrtsd %%xmm0, %%xmm0 + : [ret] "={xmm0}" (-> f64) + : [x] "{xmm0}" (x) + ); +} From 899e36489d68fba560122d9222693593cd92aa24 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Thu, 11 Jan 2018 19:42:32 +1300 Subject: [PATCH 3/4] Fix endian swap parameters --- std/endian.zig | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/std/endian.zig b/std/endian.zig index 6f5bfff385..4e67cfc7a5 100644 --- a/std/endian.zig +++ b/std/endian.zig @@ -2,11 +2,11 @@ const mem = @import("mem.zig"); const builtin = @import("builtin"); pub fn swapIfLe(comptime T: type, x: T) -> T { - return swapIf(false, T, x); + return swapIf(builtin.Endian.Little, T, x); } pub fn swapIfBe(comptime T: type, x: T) -> T { - return swapIf(true, T, x); + return swapIf(builtin.Endian.Big, T, x); } pub fn swapIf(endian: builtin.Endian, comptime T: type, x: T) -> T { @@ -15,6 +15,11 @@ pub fn swapIf(endian: builtin.Endian, comptime T: type, x: T) -> T { pub fn swap(comptime T: type, x: T) -> T { var buf: [@sizeOf(T)]u8 = undefined; - mem.writeInt(buf[0..], x, false); + mem.writeInt(buf[0..], x, builtin.Endian.Little); return mem.readInt(buf, T, builtin.Endian.Big); } + +test "swap" { + const debug = @import("debug/index.zig"); + debug.assert(swap(u32, 0xDEADBEEF) == 0xEFBEADDE); +} From 3268276b58d8b65cb295b738d7c14174005bd84e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 11 Jan 2018 20:58:28 -0500 Subject: [PATCH 4/4] the same string literal codegens to the same constant this makes it so that you can send the same string literal as a comptime slice and get the same type --- src/all_types.hpp | 1 + src/analyze.cpp | 8 ++++++++ src/buffer.cpp | 5 ++++- src/codegen.cpp | 1 + src/ir.cpp | 8 ++++---- test/cases/eval.zig | 13 +++++++++++++ 6 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 4388619840..024c78eb73 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1421,6 +1421,7 @@ struct CodeGen { HashMap llvm_fn_table; HashMap exported_symbol_names; HashMap external_prototypes; + HashMap string_literals_table; ZigList import_queue; diff --git a/src/analyze.cpp b/src/analyze.cpp index 2c6f0f209e..7e4a861f0f 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4370,6 +4370,12 @@ bool type_requires_comptime(TypeTableEntry *type_entry) { } void init_const_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) { + auto entry = g->string_literals_table.maybe_get(str); + if (entry != nullptr) { + *const_val = *entry->value; + return; + } + const_val->special = ConstValSpecialStatic; const_val->type = get_array_type(g, g->builtin_types.entry_u8, buf_len(str)); const_val->data.x_array.s_none.elements = create_const_vals(buf_len(str)); @@ -4380,6 +4386,8 @@ void init_const_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) { this_char->type = g->builtin_types.entry_u8; bigint_init_unsigned(&this_char->data.x_bigint, (uint8_t)buf_ptr(str)[i]); } + + g->string_literals_table.put(str, const_val); } ConstExprValue *create_const_str_lit(CodeGen *g, Buf *str) { diff --git a/src/buffer.cpp b/src/buffer.cpp index 1cfe01dfd3..3564481b84 100644 --- a/src/buffer.cpp +++ b/src/buffer.cpp @@ -67,9 +67,12 @@ bool buf_eql_buf(Buf *buf, Buf *other) { uint32_t buf_hash(Buf *buf) { assert(buf->list.length); + size_t interval = buf->list.length / 256; + if (interval == 0) + interval = 1; // FNV 32-bit hash uint32_t h = 2166136261; - for (size_t i = 0; i < buf_len(buf); i += 1) { + for (size_t i = 0; i < buf_len(buf); i += interval) { h = h ^ ((uint8_t)buf->list.at(i)); h = h * 16777619; } diff --git a/src/codegen.cpp b/src/codegen.cpp index 8c27574b6f..9ad71a936e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -87,6 +87,7 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out g->memoized_fn_eval_table.init(16); g->exported_symbol_names.init(8); g->external_prototypes.init(8); + g->string_literals_table.init(16); g->is_test_build = false; g->want_h_file = (out_type == OutTypeObj || out_type == OutTypeLib); buf_resize(&g->global_asm, 0); diff --git a/src/ir.cpp b/src/ir.cpp index 176e5791f8..f236910250 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13224,9 +13224,9 @@ static TypeTableEntry *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstr os_path_resolve(&source_dir_path, rel_file_path, &file_path); // load from file system into const expr - Buf file_contents = BUF_INIT; + Buf *file_contents = buf_alloc(); int err; - if ((err = os_fetch_file_path(&file_path, &file_contents))) { + if ((err = os_fetch_file_path(&file_path, file_contents))) { if (err == ErrorFileNotFound) { ir_add_error(ira, instruction->name, buf_sprintf("unable to find '%s'", buf_ptr(&file_path))); return ira->codegen->builtin_types.entry_invalid; @@ -13240,9 +13240,9 @@ static TypeTableEntry *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstr // we'll have to invalidate the cache ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - init_const_str_lit(ira->codegen, out_val, &file_contents); + init_const_str_lit(ira->codegen, out_val, file_contents); - return get_array_type(ira->codegen, ira->codegen->builtin_types.entry_u8, buf_len(&file_contents)); + return get_array_type(ira->codegen, ira->codegen->builtin_types.entry_u8, buf_len(file_contents)); } static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructionCmpxchg *instruction) { diff --git a/test/cases/eval.zig b/test/cases/eval.zig index a2e015fba7..2349aa75f7 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -375,3 +375,16 @@ test "f128 at compile time is lossy" { // TODO need a better implementation of bigfloat_init_bigint // assert(f128(1 << 113) == 10384593717069655257060992658440192); + +pub fn TypeWithCompTimeSlice(comptime field_name: []const u8) -> type { + return struct { + pub const Node = struct { }; + }; +} + +test "string literal used as comptime slice is memoized" { + const a = "link"; + const b = "link"; + comptime assert(TypeWithCompTimeSlice(a).Node == TypeWithCompTimeSlice(b).Node); + comptime assert(TypeWithCompTimeSlice("link").Node == TypeWithCompTimeSlice("link").Node); +}