From e7e1866e5047a85648e758a2d94c8247c65a6608 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 27 Apr 2021 13:09:37 +0100 Subject: Fix #893 --- core/fmt/fmt.odin | 11 ----------- src/check_decl.cpp | 5 +++-- src/check_expr.cpp | 5 +++-- src/checker.cpp | 7 +++++-- src/entity.cpp | 2 ++ src/llvm_backend.cpp | 38 +++++++++++++++++++++++--------------- 6 files changed, 36 insertions(+), 32 deletions(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 6de6b0245..f9c386ffb 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -1908,17 +1908,6 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { } } - - handle_relative_pointer :: proc(ptr: ^$T) -> rawptr where intrinsics.type_is_integer(T) { - if ptr^ == 0 { - return nil; - } - when intrinsics.type_is_unsigned(T) { - return rawptr(uintptr(ptr) + uintptr(ptr^)); - } else { - return rawptr(uintptr(ptr) + uintptr(i64(ptr^))); - } - } } fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) { diff --git a/src/check_decl.cpp b/src/check_decl.cpp index f008317ad..f43248593 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -352,16 +352,17 @@ void override_entity_in_scope(Entity *original_entity, Entity *new_entity) { string_map_set(&found_scope->elements, original_name, new_entity); + original_entity->flags |= EntityFlag_Overridden; original_entity->type = new_entity->type; + original_entity->aliased_of = new_entity; if (original_entity->identifier == nullptr) { original_entity->identifier = new_entity->identifier; } if (original_entity->identifier != nullptr && original_entity->identifier->kind == Ast_Ident) { - original_entity->identifier->Ident.entity = nullptr; + original_entity->identifier->Ident.entity = new_entity; } - original_entity->flags |= EntityFlag_Overridden; // IMPORTANT NOTE(bill, 2021-04-10): copy only the variants // This is most likely NEVER required, but it does not at all hurt to keep diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 6c4a78e53..91f0e5d84 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1141,6 +1141,9 @@ Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Typ } return nullptr; } + + GB_ASSERT((e->flags & EntityFlag_Overridden) == 0); + if (e->parent_proc_decl != nullptr && e->parent_proc_decl != c->curr_proc_decl) { if (e->kind == Entity_Variable) { @@ -1195,8 +1198,6 @@ Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Typ if (e->state == EntityState_Unresolved) { check_entity_decl(c, e, nullptr, named_type); } - - if (e->type == nullptr) { // TODO(bill): Which is correct? return or compiler_error? // compiler_error("How did this happen? type: %s; identifier: %.*s\n", type_to_string(e->type), LIT(name)); diff --git a/src/checker.cpp b/src/checker.cpp index 878435d67..eaf3b5322 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -960,7 +960,11 @@ Entity *entity_of_node(Ast *expr) { expr = unparen_expr(expr); switch (expr->kind) { case_ast_node(ident, Ident, expr); - return ident->entity; + Entity *e = ident->entity; + if (e && e->flags & EntityFlag_Overridden) { + // GB_PANIC("use of an overriden entity: %.*s", LIT(e->token.string)); + } + return e; case_end; case_ast_node(se, SelectorExpr, expr); Ast *s = unselector_expr(se->selector); @@ -973,7 +977,6 @@ Entity *entity_of_node(Ast *expr) { return nullptr; } - DeclInfo *decl_info_of_entity(Entity *e) { if (e != nullptr) { return e->decl_info; diff --git a/src/entity.cpp b/src/entity.cpp index d1f4c78e6..460f4ec6d 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -126,6 +126,8 @@ struct Entity { Entity * using_parent; Ast * using_expr; + Entity * aliased_of; + lbModule * code_gen_module; lbProcedure *code_gen_procedure; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 758f8e5d1..52c69c7c7 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -3544,16 +3544,14 @@ void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd) { Ast *ident = vd->names[i]; GB_ASSERT(ident->kind == Ast_Ident); Entity *e = entity_of_node(ident); - if (e == nullptr) { - continue; - } + GB_ASSERT(e != nullptr); if (e->kind != Entity_TypeName) { continue; } bool polymorphic_struct = false; if (e->type != nullptr && e->kind == Entity_TypeName) { - Type *bt = base_type(e->type); + Type *bt = base_type(e->type); if (bt->kind == Type_Struct) { polymorphic_struct = bt->Struct.is_polymorphic; } @@ -3575,12 +3573,16 @@ void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd) { Ast *ident = vd->names[i]; GB_ASSERT(ident->kind == Ast_Ident); Entity *e = entity_of_node(ident); - if (e == nullptr) { - continue; - } + GB_ASSERT(e != nullptr); if (e->kind != Entity_Procedure) { continue; } + GB_ASSERT (vd->values[i] != nullptr); + + Ast *value = unparen_expr(vd->values[i]); + if (value->kind != Ast_ProcLit) { + continue; // It's an alias + } CheckerInfo *info = p->module->info; DeclInfo *decl = decl_info_of_entity(e); @@ -11436,7 +11438,13 @@ lbValue lb_get_using_variable(lbProcedure *p, Entity *e) { GB_ASSERT(v.value != nullptr); GB_ASSERT_MSG(parent->type == type_deref(v.type), "%s %s", type_to_string(parent->type), type_to_string(v.type)); lbValue ptr = lb_emit_deep_field_gep(p, v, sel); - lb_add_debug_local_variable(p, ptr.value, e->type, e->token); + if (parent->scope) { + if ((parent->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) == 0) { + lb_add_debug_local_variable(p, ptr.value, e->type, e->token); + } + } else { + lb_add_debug_local_variable(p, ptr.value, e->type, e->token); + } return ptr; } @@ -13778,13 +13786,13 @@ void lb_generate_code(lbGenerator *gen) { } gbString producer = gb_string_make(heap_allocator(), "odin"); - producer = gb_string_append_fmt(producer, " version %.*s", LIT(ODIN_VERSION)); - #ifdef NIGHTLY - producer = gb_string_appendc(producer, "-nightly"); - #endif - #ifdef GIT_SHA - producer = gb_string_append_fmt(producer, "-%s", GIT_SHA); - #endif + // producer = gb_string_append_fmt(producer, " version %.*s", LIT(ODIN_VERSION)); + // #ifdef NIGHTLY + // producer = gb_string_appendc(producer, "-nightly"); + // #endif + // #ifdef GIT_SHA + // producer = gb_string_append_fmt(producer, "-%s", GIT_SHA); + // #endif gbString split_name = gb_string_make(heap_allocator(), ""); -- cgit v1.2.3 From cd1658e56c63caa9c5257a4225660a704d4aa8f8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 27 Apr 2021 13:14:54 +0100 Subject: Update Makefile --- Makefile | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 0425d17e0..8f2b7eaeb 100644 --- a/Makefile +++ b/Makefile @@ -8,13 +8,17 @@ CC=clang OS=$(shell uname) ifeq ($(OS), Darwin) + LLVM_CONFIG=llvm-config --version=11 + LDFLAGS:=$(LDFLAGS) -liconv - CFLAGS:=$(CFLAGS) $(shell llvm-config --cxxflags --ldflags) + CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG)--cxxflags --ldflags) LDFLAGS:=$(LDFLAGS) -lLLVM-C endif ifeq ($(OS), Linux) - CFLAGS:=$(CFLAGS) $(shell llvm-config-11 --cxxflags --ldflags) - LDFLAGS:=$(LDFLAGS) $(shell llvm-config-11 --libs core native --system-libs) + LLVM_CONFIG=llvm-config-11 + + CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags) + LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs) endif all: debug demo -- cgit v1.2.3 From ab5460e2e22185d00f8056fb0582c4905086273e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 27 Apr 2021 13:22:30 +0100 Subject: Update CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f9abf219f..cdfa1b994 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: - name: Download LLVM and setup PATH run: | brew install llvm@11 - echo "/usr/local/opt/llvm/bin" >> $GITHUB_PATH + echo "/usr/local/opt/llvm@11/bin" >> $GITHUB_PATH TMP_PATH=$(xcrun --show-sdk-path)/user/include echo "CPATH=$TMP_PATH" >> $GITHUB_ENV - name: build odin -- cgit v1.2.3 From f6d98d2a16e99744b8025a52ac3e5a7fc53a6e20 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 27 Apr 2021 13:29:14 +0100 Subject: Fix Makefile for macOs --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8f2b7eaeb..88bbd8df5 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ CC=clang OS=$(shell uname) ifeq ($(OS), Darwin) - LLVM_CONFIG=llvm-config --version=11 + LLVM_CONFIG=llvm-config LDFLAGS:=$(LDFLAGS) -liconv CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG)--cxxflags --ldflags) -- cgit v1.2.3 From 24f2d97c0ee48eead850c6a9365748d8ba5c172e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 27 Apr 2021 16:55:39 +0100 Subject: Make atomic compare related intrinsics optional ok --- src/check_builtin.cpp | 8 ++++---- src/llvm_backend.cpp | 48 ++++++++++++++++++++++++++++++------------------ 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 00963487f..898239234 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -2017,8 +2017,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } } - operand->mode = Addressing_Value; - operand->type = make_optional_ok_type(default_type(x.type), false); // Just reusing this procedure, it's not optional + operand->mode = Addressing_OptionalOk; + operand->type = default_type(x.type); } break; @@ -2149,8 +2149,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 check_assignment(c, &x, elem, builtin_name); check_assignment(c, &y, elem, builtin_name); - operand->mode = Addressing_Value; - operand->type = make_optional_ok_type(elem, /*typed*/false); + operand->mode = Addressing_OptionalOk; + operand->type = elem; break; } break; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 52c69c7c7..2c17edcf4 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -9136,9 +9136,11 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, case BuiltinProc_overflow_sub: case BuiltinProc_overflow_mul: { - Type *tuple = tv.type; - GB_ASSERT(is_type_tuple(tuple)); - Type *type = tuple->Tuple.variables[0]->type; + Type *main_type = tv.type; + Type *type = main_type; + if (is_type_tuple(main_type)) { + type = main_type->Tuple.variables[0]->type; + } lbValue x = lb_build_expr(p, ce->args[0]); lbValue y = lb_build_expr(p, ce->args[1]); @@ -9168,18 +9170,22 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, args[0] = x.value; args[1] = y.value; - Type *res_type = nullptr; - { + lbValue res = {}; + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + + if (is_type_tuple(main_type)) { + Type *res_type = nullptr; gbAllocator a = permanent_allocator(); res_type = alloc_type_tuple(); array_init(&res_type->Tuple.variables, a, 2); res_type->Tuple.variables[0] = alloc_entity_field(nullptr, blank_token, type, false, 0); res_type->Tuple.variables[1] = alloc_entity_field(nullptr, blank_token, t_llvm_bool, false, 1); - } - lbValue res = {}; - res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); - res.type = res_type; + res.type = res_type; + } else { + res.value = LLVMBuildExtractValue(p->builder, res.value, 0, ""); + res.type = type; + } return res; } @@ -9392,16 +9398,22 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, single_threaded ); - GB_ASSERT(tv.type->kind == Type_Tuple); - Type *fix_typed = alloc_type_tuple(); - array_init(&fix_typed->Tuple.variables, permanent_allocator(), 2); - fix_typed->Tuple.variables[0] = tv.type->Tuple.variables[0]; - fix_typed->Tuple.variables[1] = alloc_entity_field(nullptr, blank_token, t_llvm_bool, false, 1); + if (tv.type->kind == Type_Tuple) { + Type *fix_typed = alloc_type_tuple(); + array_init(&fix_typed->Tuple.variables, permanent_allocator(), 2); + fix_typed->Tuple.variables[0] = tv.type->Tuple.variables[0]; + fix_typed->Tuple.variables[1] = alloc_entity_field(nullptr, blank_token, t_llvm_bool, false, 1); - lbValue res = {}; - res.value = value; - res.type = fix_typed; - return res; + lbValue res = {}; + res.value = value; + res.type = fix_typed; + return res; + } else { + lbValue res = {}; + res.value = LLVMBuildExtractValue(p->builder, value, 0, ""); + res.type = tv.type; + return res; + } } -- cgit v1.2.3 From 96b60d8779c150842a3e36ffb7b733938dd7a8f6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 27 Apr 2021 16:56:11 +0100 Subject: Reimplement sync2.Sema on windows with WaitOnAddress primitives --- core/sync/sync2/primitives.odin | 25 +++---------------------- core/sync/sync2/primitives_atomic.odin | 30 ++++++++++++++++++++++++++++++ core/sync/sync2/primitives_pthreads.odin | 29 +++++++++++++++++++++++++++++ core/sync/sync2/primitives_windows.odin | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 22 deletions(-) diff --git a/core/sync/sync2/primitives.odin b/core/sync/sync2/primitives.odin index dd6688a50..e7c29afc8 100644 --- a/core/sync/sync2/primitives.odin +++ b/core/sync/sync2/primitives.odin @@ -153,33 +153,14 @@ cond_broadcast :: proc(c: ^Cond) { // // A Sema must not be copied after first use Sema :: struct { - // TODO(bill): Is this implementation too lazy? - // Can this be made to work on all OSes without construction and destruction, i.e. Zero is Initialized - - mutex: Mutex, - cond: Cond, - count: int, + impl: _Sema, } sema_wait :: proc(s: ^Sema) { - mutex_lock(&s.mutex); - defer mutex_unlock(&s.mutex); - - for s.count == 0 { - cond_wait(&s.cond, &s.mutex); - } - - s.count -= 1; - if s.count > 0 { - cond_signal(&s.cond); - } + _sema_wait(s); } sema_post :: proc(s: ^Sema, count := 1) { - mutex_lock(&s.mutex); - defer mutex_unlock(&s.mutex); - - s.count += count; - cond_signal(&s.cond); + _sema_post(s, count); } diff --git a/core/sync/sync2/primitives_atomic.odin b/core/sync/sync2/primitives_atomic.odin index 610ab7ee0..0d6b14987 100644 --- a/core/sync/sync2/primitives_atomic.odin +++ b/core/sync/sync2/primitives_atomic.odin @@ -240,5 +240,35 @@ _cond_broadcast :: proc(c: ^Cond) { } } +_Sema :: struct { + mutex: Mutex, + cond: Cond, + count: int, +} + +_sema_wait :: proc(s: ^Sema) { + mutex_lock(&s.impl.mutex); + defer mutex_unlock(&s.impl.mutex); + + for s.impl.count == 0 { + cond_wait(&s.impl.cond, &s.impl.mutex); + } + + s.impl.count -= 1; + if s.impl.count > 0 { + cond_signal(&s.impl.cond); + } +} + +_sema_post :: proc(s: ^Sema, count := 1) { + mutex_lock(&s.impl.mutex); + defer mutex_unlock(&s.impl.mutex); + + s.impl.count += count; + cond_signal(&s.impl.cond); +} + + + } // !ODIN_SYNC_USE_PTHREADS diff --git a/core/sync/sync2/primitives_pthreads.odin b/core/sync/sync2/primitives_pthreads.odin index e85cff7fc..17940c991 100644 --- a/core/sync/sync2/primitives_pthreads.odin +++ b/core/sync/sync2/primitives_pthreads.odin @@ -150,5 +150,34 @@ _cond_broadcast :: proc(c: ^Cond) { assert(err == 0); } +_Sema :: struct { + mutex: Mutex, + cond: Cond, + count: int, +} + +_sema_wait :: proc(s: ^Sema) { + mutex_lock(&s.impl.mutex); + defer mutex_unlock(&s.impl.mutex); + + for s.impl.count == 0 { + cond_wait(&s.impl.cond, &s.impl.mutex); + } + + s.impl.count -= 1; + if s.impl.count > 0 { + cond_signal(&s.impl.cond); + } +} + +_sema_post :: proc(s: ^Sema, count := 1) { + mutex_lock(&s.impl.mutex); + defer mutex_unlock(&s.impl.mutex); + + s.impl.count += count; + cond_signal(&s.impl.cond); +} + + } // ODIN_SYNC_USE_PTHREADS diff --git a/core/sync/sync2/primitives_windows.odin b/core/sync/sync2/primitives_windows.odin index 02b6cd733..0f8bce5ca 100644 --- a/core/sync/sync2/primitives_windows.odin +++ b/core/sync/sync2/primitives_windows.odin @@ -71,3 +71,35 @@ _cond_signal :: proc(c: ^Cond) { _cond_broadcast :: proc(c: ^Cond) { win32.WakeAllConditionVariable(&c.impl.cond); } + + +_Sema :: struct { + count: int, +} + +_sema_wait :: proc(s: ^Sema) { + for { + original_count := s.impl.count; + for original_count == 0 { + win32.WaitOnAddress( + &s.impl.count, + &original_count, + size_of(original_count), + win32.INFINITE, + ); + original_count = s.impl.count; + } + if original_count == atomic_cxchg(&s.impl.count, original_count-1, original_count) { + return; + } + } +} + +_sema_post :: proc(s: ^Sema, count := 1) { + atomic_add(&s.impl.count, count); + if count == 1 { + win32.WakeByAddressSingle(&s.impl.count); + } else { + win32.WakeByAddressAll(&s.impl.count); + } +} -- cgit v1.2.3 From 7ac80544a1e36b81c0b96bac9e7a94d6207058f9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 27 Apr 2021 16:59:25 +0100 Subject: Max sync2.Sema on windows be `i32` for the counter internally. --- core/sync/sync2/primitives_windows.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/sync/sync2/primitives_windows.odin b/core/sync/sync2/primitives_windows.odin index 0f8bce5ca..0eb6579a7 100644 --- a/core/sync/sync2/primitives_windows.odin +++ b/core/sync/sync2/primitives_windows.odin @@ -74,7 +74,7 @@ _cond_broadcast :: proc(c: ^Cond) { _Sema :: struct { - count: int, + count: i32, } _sema_wait :: proc(s: ^Sema) { @@ -96,7 +96,7 @@ _sema_wait :: proc(s: ^Sema) { } _sema_post :: proc(s: ^Sema, count := 1) { - atomic_add(&s.impl.count, count); + atomic_add(&s.impl.count, i32(count)); if count == 1 { win32.WakeByAddressSingle(&s.impl.count); } else { -- cgit v1.2.3 From 17390cd317192345d7c2a03eefb4af2032705753 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 27 Apr 2021 17:19:12 +0100 Subject: Implement sync2.Recursive_Mutex using `WaitOnAddress` and friends on Windows --- core/sync/sync2/primitives.odin | 41 +++----------------------- core/sync/sync2/primitives_atomic.odin | 49 +++++++++++++++++++++++++++++++ core/sync/sync2/primitives_pthreads.odin | 48 ++++++++++++++++++++++++++++++ core/sync/sync2/primitives_windows.odin | 50 ++++++++++++++++++++++++++++++++ 4 files changed, 151 insertions(+), 37 deletions(-) diff --git a/core/sync/sync2/primitives.odin b/core/sync/sync2/primitives.odin index e7c29afc8..07ac91515 100644 --- a/core/sync/sync2/primitives.odin +++ b/core/sync/sync2/primitives.odin @@ -72,52 +72,19 @@ rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool { // // A Recursive_Mutex must not be copied after first use Recursive_Mutex :: struct { - // TODO(bill): Is this implementation too lazy? - // Can this be made to work on all OSes without construction and destruction, i.e. Zero is Initialized - // CRITICAL_SECTION would be a perfect candidate for this on Windows but that cannot be "dumb" - - owner: int, - recursion: int, - mutex: Mutex, + impl: _Recursive_Mutex, } recursive_mutex_lock :: proc(m: ^Recursive_Mutex) { - tid := runtime.current_thread_id(); - if tid != m.owner { - mutex_lock(&m.mutex); - } - // inside the lock - m.owner = tid; - m.recursion += 1; + _recursive_mutex_lock(m); } recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) { - tid := runtime.current_thread_id(); - assert(tid == m.owner); - m.recursion -= 1; - recursion := m.recursion; - if recursion == 0 { - m.owner = 0; - } - if recursion == 0 { - mutex_unlock(&m.mutex); - } - // outside the lock - + _recursive_mutex_unlock(m); } recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool { - tid := runtime.current_thread_id(); - if m.owner == tid { - return mutex_try_lock(&m.mutex); - } - if !mutex_try_lock(&m.mutex) { - return false; - } - // inside the lock - m.owner = tid; - m.recursion += 1; - return true; + return _recursive_mutex_try_lock(m); } diff --git a/core/sync/sync2/primitives_atomic.odin b/core/sync/sync2/primitives_atomic.odin index 0d6b14987..7043f8c84 100644 --- a/core/sync/sync2/primitives_atomic.odin +++ b/core/sync/sync2/primitives_atomic.odin @@ -5,6 +5,7 @@ package sync2 when !#config(ODIN_SYNC_USE_PTHREADS, true) { import "core:time" +import "core:runtime" _Mutex_State :: enum i32 { Unlocked = 0, @@ -160,6 +161,54 @@ _rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool { } +_Recursive_Mutex :: struct { + owner: int, + recursion: int, + mutex: Mutex, +} + +_recursive_mutex_lock :: proc(m: ^Recursive_Mutex) { + tid := runtime.current_thread_id(); + if tid != m.impl.owner { + mutex_lock(&m.impl.mutex); + } + // inside the lock + m.impl.owner = tid; + m.impl.recursion += 1; +} + +_recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) { + tid := runtime.current_thread_id(); + assert(tid == m.impl.owner); + m.impl.recursion -= 1; + recursion := m.impl.recursion; + if recursion == 0 { + m.impl.owner = 0; + } + if recursion == 0 { + mutex_unlock(&m.impl.mutex); + } + // outside the lock + +} + +_recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool { + tid := runtime.current_thread_id(); + if m.impl.owner == tid { + return mutex_try_lock(&m.impl.mutex); + } + if !mutex_try_lock(&m.impl.mutex) { + return false; + } + // inside the lock + m.impl.owner = tid; + m.impl.recursion += 1; + return true; +} + + + + Queue_Item :: struct { next: ^Queue_Item, diff --git a/core/sync/sync2/primitives_pthreads.odin b/core/sync/sync2/primitives_pthreads.odin index 17940c991..ea0d140d8 100644 --- a/core/sync/sync2/primitives_pthreads.odin +++ b/core/sync/sync2/primitives_pthreads.odin @@ -5,6 +5,7 @@ package sync2 when #config(ODIN_SYNC_USE_PTHREADS, true) { import "core:time" +import "core:runtime" import "core:sys/unix" _Mutex_State :: enum i32 { @@ -120,6 +121,53 @@ _rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool { return false; } + +_Recursive_Mutex :: struct { + owner: int, + recursion: int, + mutex: Mutex, +} + +_recursive_mutex_lock :: proc(m: ^Recursive_Mutex) { + tid := runtime.current_thread_id(); + if tid != m.impl.owner { + mutex_lock(&m.impl.mutex); + } + // inside the lock + m.impl.owner = tid; + m.impl.recursion += 1; +} + +_recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) { + tid := runtime.current_thread_id(); + assert(tid == m.impl.owner); + m.impl.recursion -= 1; + recursion := m.impl.recursion; + if recursion == 0 { + m.impl.owner = 0; + } + if recursion == 0 { + mutex_unlock(&m.impl.mutex); + } + // outside the lock + +} + +_recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool { + tid := runtime.current_thread_id(); + if m.impl.owner == tid { + return mutex_try_lock(&m.impl.mutex); + } + if !mutex_try_lock(&m.impl.mutex) { + return false; + } + // inside the lock + m.impl.owner = tid; + m.impl.recursion += 1; + return true; +} + + _Cond :: struct { pthread_cond: unix.pthread_cond_t, } diff --git a/core/sync/sync2/primitives_windows.odin b/core/sync/sync2/primitives_windows.odin index 0eb6579a7..a66b4a9bd 100644 --- a/core/sync/sync2/primitives_windows.odin +++ b/core/sync/sync2/primitives_windows.odin @@ -50,6 +50,56 @@ _rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool { } +_Recursive_Mutex :: struct { + owner: u32, + claim_count: i32, +} + +_recursive_mutex_lock :: proc(m: ^Recursive_Mutex) { + tid := win32.GetCurrentThreadId(); + for { + prev_owner := atomic_cxchg_acq(&m.impl.owner, tid, 0); + switch prev_owner { + case 0, tid: + m.impl.claim_count += 1; + // inside the lock + return; + } + + win32.WaitOnAddress( + &m.impl.owner, + &prev_owner, + size_of(prev_owner), + win32.INFINITE, + ); + } +} + +_recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) { + m.impl.claim_count -= 1; + if m.impl.claim_count != 0 { + return; + } + atomic_xchg_rel(&m.impl.owner, 0); + win32.WakeByAddressSingle(&m.impl.owner); + // outside the lock + +} + +_recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool { + tid := win32.GetCurrentThreadId(); + prev_owner := atomic_cxchg_acq(&m.impl.owner, tid, 0); + switch prev_owner { + case 0, tid: + m.impl.claim_count += 1; + // inside the lock + return true; + } + return false; +} + + + _Cond :: struct { cond: win32.CONDITION_VARIABLE, -- cgit v1.2.3 From ffffb04d8564f22b0e594c024c4dddf305428d6c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 27 Apr 2021 17:21:52 +0100 Subject: Remove unused import --- core/sync/sync2/primitives.odin | 1 - 1 file changed, 1 deletion(-) diff --git a/core/sync/sync2/primitives.odin b/core/sync/sync2/primitives.odin index 07ac91515..bf16ea735 100644 --- a/core/sync/sync2/primitives.odin +++ b/core/sync/sync2/primitives.odin @@ -1,7 +1,6 @@ package sync2 import "core:time" -import "core:runtime" // A Mutex is a mutual exclusion lock // The zero value for a Mutex is an unlocked mutex -- cgit v1.2.3 From afe185ee227b8d4540e4dc11b382eae0671575d2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 27 Apr 2021 21:08:18 +0100 Subject: Fix procedure entity finding --- src/llvm_backend.cpp | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 2c17edcf4..74d5bbe01 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -5663,6 +5663,25 @@ LLVMValueRef lb_build_constant_array_values(lbModule *m, Type *type, Type *elem_ return llvm_const_array(lb_type(m, elem_type), values, cast(unsigned int)count); } +lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) { + e = strip_entity_wrapping(e); + GB_ASSERT(e != nullptr); + auto *found = map_get(&m->values, hash_entity(e)); + if (found) { + return *found; + } + + // TODO(bill): this is + lbProcedure *missing_proc = lb_create_procedure(m, e); + found = map_get(&m->values, hash_entity(e)); + if (found) { + return *found; + } + + GB_PANIC("Error in: %s, missing procedure %.*s\n", token_pos_to_string(e->token.pos), LIT(e->token.string)); + return {}; +} + lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_local) { LLVMContextRef ctx = m->ctx; @@ -5689,14 +5708,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc return lb_generate_anonymous_proc_lit(m, str_lit("_proclit"), expr); } Entity *e = entity_from_expr(expr); - e = strip_entity_wrapping(e); - GB_ASSERT(e != nullptr); - auto *found = map_get(&m->values, hash_entity(e)); - if (found) { - return *found; - } - - GB_PANIC("Error in: %s, missing procedure %.*s\n", token_pos_to_string(e->token.pos), LIT(e->token.string)); + return lb_find_procedure_value_from_entity(m, e); } bool is_local = allow_local && m->curr_procedure != nullptr; -- cgit v1.2.3 From 24fce21d9055ac4cd2124c0fe5396f430e99f24f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 28 Apr 2021 10:49:30 +0100 Subject: Add "naked" calling convention (removes prologue and epilogue) --- core/runtime/core.odin | 1 + src/docs_writer.cpp | 3 +++ src/llvm_backend.cpp | 4 ++++ src/llvm_backend.hpp | 1 + src/parser.cpp | 1 + src/parser.hpp | 3 ++- src/types.cpp | 3 +++ 7 files changed, 15 insertions(+), 1 deletion(-) diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 0033aad9a..5f016579f 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -32,6 +32,7 @@ Calling_Convention :: enum u8 { Fast_Call = 5, None = 6, + Naked = 7, } Type_Info_Enum_Value :: distinct i64; diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp index f4fd02376..03ea25930 100644 --- a/src/docs_writer.cpp +++ b/src/docs_writer.cpp @@ -697,6 +697,9 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) { case ProcCC_None: calling_convention = str_lit("none"); break; + case ProcCC_Naked: + calling_convention = str_lit("naked"); + break; case ProcCC_InlineAsm: calling_convention = str_lit("inline-assembly"); break; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 74d5bbe01..f81077b47 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2588,6 +2588,10 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) { lb_add_attribute_to_proc(m, p->value, "noreturn"); } + if (pt->Proc.calling_convention == ProcCC_Naked) { + lb_add_attribute_to_proc(m, p->value, "naked"); + } + switch (p->inlining) { case ProcInlining_inline: lb_add_attribute_to_proc(m, p->value, "alwaysinline"); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index e8811a91e..cd6a99c20 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -472,6 +472,7 @@ lbCallingConventionKind const lb_calling_convention_map[ProcCC_MAX] = { lbCallingConvention_X86_FastCall, // ProcCC_FastCall, lbCallingConvention_C, // ProcCC_None, + lbCallingConvention_C, // ProcCC_Naked, lbCallingConvention_C, // ProcCC_InlineAsm, }; diff --git a/src/parser.cpp b/src/parser.cpp index a7e4c9162..a6764de08 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3220,6 +3220,7 @@ ProcCallingConvention string_to_calling_convention(String s) { if (s == "fastcall") return ProcCC_FastCall; if (s == "fast") return ProcCC_FastCall; if (s == "none") return ProcCC_None; + if (s == "naked") return ProcCC_Naked; return ProcCC_Invalid; } diff --git a/src/parser.hpp b/src/parser.hpp index 8c2eadb46..93c0363ab 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -215,8 +215,9 @@ enum ProcCallingConvention { ProcCC_FastCall = 5, ProcCC_None = 6, + ProcCC_Naked = 7, - ProcCC_InlineAsm = 7, + ProcCC_InlineAsm = 8, ProcCC_MAX, diff --git a/src/types.cpp b/src/types.cpp index 56081acc8..7b5b81c43 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -3618,6 +3618,9 @@ gbString write_type_to_string(gbString str, Type *type) { case ProcCC_None: str = gb_string_appendc(str, " \"none\" "); break; + case ProcCC_Naked: + str = gb_string_appendc(str, " \"naked\" "); + break; // case ProcCC_VectorCall: // str = gb_string_appendc(str, " \"vectorcall\" "); // break; -- cgit v1.2.3 From 102c29575b555deff1492fceca0ffdab8eb3812c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 28 Apr 2021 10:49:40 +0100 Subject: Allow @(test) to be ran outside of `*_test.odin` files --- src/checker.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/checker.cpp b/src/checker.cpp index eaf3b5322..523d2c297 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1873,7 +1873,8 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) { continue; } - if (e->file == nullptr || !e->file->is_test) { + // if (e->file == nullptr || !e->file->is_test) { + if (e->file == nullptr) { continue; } -- cgit v1.2.3 From 02da4d61ae3f7544d2845d36bfbd46e40d31c70d Mon Sep 17 00:00:00 2001 From: Ricardo Silva Date: Wed, 28 Apr 2021 11:15:05 +0100 Subject: Fix Makefile for OSX --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 88bbd8df5..37a7f8159 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ ifeq ($(OS), Darwin) LLVM_CONFIG=llvm-config LDFLAGS:=$(LDFLAGS) -liconv - CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG)--cxxflags --ldflags) + CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags) LDFLAGS:=$(LDFLAGS) -lLLVM-C endif ifeq ($(OS), Linux) -- cgit v1.2.3 From 3061dd2497130a8a8c04cd9e675a7672f8baa402 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 28 Apr 2021 11:56:47 +0100 Subject: Update build.bat to use `wmic os get LocalDateTime /value` instead of %DATE% which is not consistent across computers --- build.bat | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/build.bat b/build.bat index c6a634aeb..038f02866 100644 --- a/build.bat +++ b/build.bat @@ -2,8 +2,12 @@ setlocal EnableDelayedExpansion -set curr_year=%DATE:~-4% -set curr_month=%DATE:~3,2% +for /f "usebackq tokens=1,2 delims=,=- " %%i in (`wmic os get LocalDateTime /value`) do @if %%i==LocalDateTime ( + set CURR_DATE_TIME=%%j +) + +set curr_year=%CURR_DATE_TIME:~0,4% +set curr_month=%CURR_DATE_TIME:~4,2% :: Make sure this is a decent name and not generic set exe_name=odin.exe -- cgit v1.2.3 From e50ef33c2a194ff1bd5ff0f485f2e6664d6519b0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 28 Apr 2021 12:34:16 +0100 Subject: Fix #921 --- src/llvm_backend.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index f81077b47..f215f12a4 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -14363,13 +14363,15 @@ void lb_generate_code(lbGenerator *gen) { Type *params = alloc_type_tuple(); Type *results = alloc_type_tuple(); + Type *t_ptr_cstring = alloc_type_pointer(t_cstring); + String name = str_lit("main"); if (build_context.metrics.os == TargetOs_windows && build_context.metrics.arch == TargetArch_386) { name = str_lit("mainCRTStartup"); } else { array_init(¶ms->Tuple.variables, permanent_allocator(), 2); params->Tuple.variables[0] = alloc_entity_param(nullptr, make_token_ident("argc"), t_i32, false, true); - params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("argv"), alloc_type_pointer(t_cstring), false, true); + params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("argv"), t_ptr_cstring, false, true); } array_init(&results->Tuple.variables, permanent_allocator(), 1); @@ -14385,6 +14387,14 @@ void lb_generate_code(lbGenerator *gen) { lb_begin_procedure_body(p); + { // initialize `runtime.args__` + lbValue argc = {LLVMGetParam(p->value, 0), t_i32}; + argc = lb_emit_conv(p, argc, t_int); + lbValue argv = {LLVMGetParam(p->value, 1), t_ptr_cstring}; + lbAddr args = lb_addr(lb_find_runtime_value(p->module, str_lit("args__"))); + lb_fill_slice(p, args, argv, argc); + } + LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, startup_runtime->type)), startup_runtime->value, nullptr, 0, ""); if (build_context.command_kind == Command_test) { -- cgit v1.2.3 From 71ac145f49efa816f49b462df3ec38288a57d776 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 28 Apr 2021 15:28:14 +0100 Subject: Remove dead code related to old backend --- src/build_settings.cpp | 61 -------------------------------- src/main.cpp | 95 +++++++++----------------------------------------- 2 files changed, 16 insertions(+), 140 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 82ed24f83..f48aa4999 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -173,8 +173,6 @@ struct BuildContext { String resource_filepath; String pdb_filepath; bool has_resource; - String opt_flags; - String llc_flags; String link_flags; String extra_linker_flags; String microarch; @@ -806,22 +804,12 @@ void init_build_context(TargetMetrics *cross_target) { bc->word_size = metrics->word_size; bc->max_align = metrics->max_align; bc->link_flags = str_lit(" "); - bc->opt_flags = str_lit(" "); - gbString llc_flags = gb_string_make_reserve(heap_allocator(), 64); - if (bc->ODIN_DEBUG) { - // llc_flags = gb_string_appendc(llc_flags, "-debug-compile "); - } - // NOTE(zangent): The linker flags to set the build architecture are different // across OSs. It doesn't make sense to allocate extra data on the heap // here, so I just #defined the linker flags to keep things concise. if (bc->metrics.arch == TargetArch_amd64) { - if (bc->microarch.len == 0) { - llc_flags = gb_string_appendc(llc_flags, "-march=x86-64 "); - } - switch (bc->metrics.os) { case TargetOs_windows: bc->link_flags = str_lit("/machine:x64 "); @@ -836,10 +824,6 @@ void init_build_context(TargetMetrics *cross_target) { break; } } else if (bc->metrics.arch == TargetArch_386) { - if (bc->microarch.len == 0) { - llc_flags = gb_string_appendc(llc_flags, "-march=x86 "); - } - switch (bc->metrics.os) { case TargetOs_windows: bc->link_flags = str_lit("/machine:x86 "); @@ -856,10 +840,6 @@ void init_build_context(TargetMetrics *cross_target) { break; } } else if (bc->metrics.arch == TargetArch_arm64) { - if (bc->microarch.len == 0) { - llc_flags = gb_string_appendc(llc_flags, "-march=arm64 "); - } - switch (bc->metrics.os) { case TargetOs_darwin: bc->link_flags = str_lit("-arch arm64 "); @@ -872,50 +852,9 @@ void init_build_context(TargetMetrics *cross_target) { gb_printf_err("Compiler Error: Unsupported architecture\n");; gb_exit(1); } - llc_flags = gb_string_appendc(llc_flags, " "); - bc->optimization_level = gb_clamp(bc->optimization_level, 0, 3); - gbString opt_flags = gb_string_make_reserve(heap_allocator(), 64); - - if (bc->microarch.len != 0) { - opt_flags = gb_string_appendc(opt_flags, "-march="); - opt_flags = gb_string_append_length(opt_flags, bc->microarch.text, bc->microarch.len); - opt_flags = gb_string_appendc(opt_flags, " "); - - // llc_flags = gb_string_appendc(opt_flags, "-march="); - // llc_flags = gb_string_append_length(llc_flags, bc->microarch.text, bc->microarch.len); - // llc_flags = gb_string_appendc(llc_flags, " "); - } - - - if (bc->optimization_level != 0) { - opt_flags = gb_string_append_fmt(opt_flags, "-O%d ", bc->optimization_level); - // NOTE(lachsinc): The following options were previously passed during call - // to opt in main.cpp:exec_llvm_opt(). - // -die: Dead instruction elimination - // -memcpyopt: MemCpy optimization - } - if (bc->ODIN_DEBUG == false) { - opt_flags = gb_string_appendc(opt_flags, "-mem2reg -die "); - } - - - - - - // NOTE(lachsinc): This optimization option was previously required to get - // around an issue in fmt.odin. Thank bp for tracking it down! Leaving for now until the issue - // is resolved and confirmed by Bill. Maybe it should be readded in non-debug builds. - // if (bc->ODIN_DEBUG == false) { - // opt_flags = gb_string_appendc(opt_flags, "-mem2reg "); - // } - - bc->opt_flags = make_string_c(opt_flags); - bc->llc_flags = make_string_c(llc_flags); - - #undef LINK_FLAG_X64 #undef LINK_FLAG_386 } diff --git a/src/main.cpp b/src/main.cpp index 251616b56..427653feb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -265,22 +265,22 @@ i32 linker_stage(lbGenerator *gen) { LIT(build_context.resource_filepath) ); - if(result == 0) { - result = system_exec_command_line_app("msvc-link", - "\"%.*slink.exe\" %s \"%.*s.res\" -OUT:\"%.*s.%s\" %s " - "/nologo /incremental:no /opt:ref /subsystem:%s " - " %.*s " - " %.*s " - " %s " - "", - LIT(find_result.vs_exe_path), object_files, LIT(output_base), LIT(output_base), output_ext, - link_settings, - subsystem_str, - LIT(build_context.link_flags), - LIT(build_context.extra_linker_flags), - lib_str - ); - } + if (result == 0) { + result = system_exec_command_line_app("msvc-link", + "\"%.*slink.exe\" %s \"%.*s.res\" -OUT:\"%.*s.%s\" %s " + "/nologo /incremental:no /opt:ref /subsystem:%s " + " %.*s " + " %.*s " + " %s " + "", + LIT(find_result.vs_exe_path), object_files, LIT(output_base), LIT(output_base), output_ext, + link_settings, + subsystem_str, + LIT(build_context.link_flags), + LIT(build_context.extra_linker_flags), + lib_str + ); + } } else { result = system_exec_command_line_app("msvc-link", "\"%.*slink.exe\" %s -OUT:\"%.*s.%s\" %s " @@ -1535,69 +1535,6 @@ void remove_temp_files(String output_base) { } - - -i32 exec_llvm_opt(String output_base) { -#if defined(GB_SYSTEM_WINDOWS) - // For more passes arguments: http://llvm.org/docs/Passes.html - - return system_exec_command_line_app("llvm-opt", - "\"%.*sbin/opt\" \"%.*s.ll\" -o \"%.*s_memcpy_pass.bc\" -memcpyopt" - "", - LIT(build_context.ODIN_ROOT), - LIT(output_base), LIT(output_base)) - - || system_exec_command_line_app("llvm-opt", - "\"%.*sbin/opt\" \"%.*s_memcpy_pass.bc\" -o \"%.*s.bc\" %.*s " - "", - LIT(build_context.ODIN_ROOT), - LIT(output_base), LIT(output_base), - LIT(build_context.opt_flags)); -#else - // NOTE(zangent): This is separate because it seems that LLVM tools are packaged - // with the Windows version, while they will be system-provided on MacOS and GNU/Linux - - return system_exec_command_line_app("llvm-opt", - "opt \"%.*s.ll\" -o \"%.*s_memcpy_pass.bc\" -memcpyopt" - "", - LIT(output_base), LIT(output_base)) - - || system_exec_command_line_app("llvm-opt", - "opt \"%.*s_memcpy_pass.bc\" -o \"%.*s.bc\" %.*s " - "", - LIT(output_base), LIT(output_base), - LIT(build_context.opt_flags)); -#endif -} - -i32 exec_llvm_llc(String output_base) { - // For more arguments: http://llvm.org/docs/CommandGuide/llc.html -#if defined(GB_SYSTEM_WINDOWS) - return system_exec_command_line_app("llvm-llc", - "\"%.*sbin\\llc\" \"%.*s.bc\" -filetype=obj -O%d " - "-o \"%.*s.obj\" " - "%.*s" - "", - LIT(build_context.ODIN_ROOT), - LIT(output_base), - build_context.optimization_level, - LIT(output_base), - LIT(build_context.llc_flags)); -#else - // NOTE(zangent): Linux / Unix is unfinished and not tested very well. - return system_exec_command_line_app("llc", - "llc \"%.*s.bc\" -filetype=obj -relocation-model=pic -O%d " - "%.*s " - "%s%.*s", - LIT(output_base), - build_context.optimization_level, - LIT(build_context.llc_flags), - build_context.cross_compiling ? "-mtriple=" : "", - cast(int)(build_context.cross_compiling ? build_context.metrics.target_triplet.len : 0), - build_context.metrics.target_triplet.text); -#endif -} - void print_show_help(String const arg0, String const &command) { print_usage_line(0, "%.*s is a tool for managing Odin source code", LIT(arg0)); print_usage_line(0, "Usage"); -- cgit v1.2.3 From 375132252122e899b365859961a95fc8c1c06e63 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 28 Apr 2021 15:36:29 +0100 Subject: Add `-no-pie` flag to linker on Linux --- src/main.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index 427653feb..117c4e549 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -409,6 +409,11 @@ i32 linker_stage(lbGenerator *gen) { #endif } + if (build_context.metrics.os == TargetOs_linux) { + link_settings = gb_string_appendc(link_settings, "-no-pie "); + } + + if (build_context.out_filepath.len > 0) { //NOTE(thebirk): We have a custom -out arguments, so we should use the extension from that isize pos = string_extension_position(build_context.out_filepath); -- cgit v1.2.3 From d5844dfd2aaa47de24e695ea3ecfd3ea05c9043a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 28 Apr 2021 16:07:46 +0100 Subject: Update `llvm-config` checking on Linux --- Makefile | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 37a7f8159..1eefb02ea 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,15 @@ ifeq ($(OS), Darwin) LDFLAGS:=$(LDFLAGS) -lLLVM-C endif ifeq ($(OS), Linux) - LLVM_CONFIG=llvm-config-11 + ifneq ($(shell command -v llvm-config-11),) + LLVM_CONFIG=llvm-config-11 + else + ifneq ($(shell llvm-config --version | grep 11),) + LLVM_CONFIG=llvm-config + else + $(error "Requirement: llvm-config must be version 11") + endif + endif CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags) LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs) -- cgit v1.2.3 From bee637aef178e71f8bd67433358ab3b382a4305f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 28 Apr 2021 16:23:42 +0100 Subject: Update Makefile for Linux --- Makefile | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 1eefb02ea..c8199b01e 100644 --- a/Makefile +++ b/Makefile @@ -15,14 +15,15 @@ ifeq ($(OS), Darwin) LDFLAGS:=$(LDFLAGS) -lLLVM-C endif ifeq ($(OS), Linux) - ifneq ($(shell command -v llvm-config-11),) + LLVM_CONFIG=llvm-config-11 + ifneq ($(shell which llvm-config-11 2>/dev/null),) LLVM_CONFIG=llvm-config-11 else - ifneq ($(shell llvm-config --version | grep 11),) - LLVM_CONFIG=llvm-config - else - $(error "Requirement: llvm-config must be version 11") - endif + ifneq ($(shell llvm-config --version | grep '^11\.'),) + LLVM_CONFIG=llvm-config + else + $(error "Requirement: llvm-config must be version 11") + endif endif CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags) -- cgit v1.2.3 From 222bab501ce886692f300b02d15b9e7099457406 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 29 Apr 2021 11:00:14 +0100 Subject: Disallow unnamed polymorphic records --- src/check_expr.cpp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 91f0e5d84..117faa18a 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5476,6 +5476,15 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr if (operand->mode == Addressing_Type) { Type *t = operand->type; if (is_type_polymorphic_record(t)) { + if (!is_type_named(t)) { + gbString s = expr_to_string(operand->expr); + error(call, "Illegal use of an unnamed polymorphic record, %s", s); + gb_string_free(s); + operand->mode = Addressing_Invalid; + operand->type = t_invalid;; + return Expr_Expr; + } + auto err = check_polymorphic_record_type(c, operand, call); if (err == 0) { Ast *ident = operand->expr; @@ -8526,8 +8535,18 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) { case_ast_node(st, StructType, node); str = gb_string_appendc(str, "struct "); + if (st->polymorphic_params) { + str = gb_string_append_rune(str, '('); + str = write_expr_to_string(str, st->polymorphic_params, shorthand); + str = gb_string_appendc(str, ") "); + } if (st->is_packed) str = gb_string_appendc(str, "#packed "); if (st->is_raw_union) str = gb_string_appendc(str, "#raw_union "); + if (st->align) { + str = gb_string_appendc(str, "#align "); + str = write_expr_to_string(str, st->align, shorthand); + str = gb_string_append_rune(str, ' '); + } str = gb_string_append_rune(str, '{'); if (shorthand) { str = gb_string_appendc(str, "..."); @@ -8540,6 +8559,18 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) { case_ast_node(st, UnionType, node); str = gb_string_appendc(str, "union "); + if (st->polymorphic_params) { + str = gb_string_append_rune(str, '('); + str = write_expr_to_string(str, st->polymorphic_params, shorthand); + str = gb_string_appendc(str, ") "); + } + if (st->no_nil) str = gb_string_appendc(str, "#no_nil "); + if (st->maybe) str = gb_string_appendc(str, "#maybe "); + if (st->align) { + str = gb_string_appendc(str, "#align "); + str = write_expr_to_string(str, st->align, shorthand); + str = gb_string_append_rune(str, ' '); + } str = gb_string_append_rune(str, '{'); if (shorthand) { str = gb_string_appendc(str, "..."); -- cgit v1.2.3 From 58e023e0cf581db71b4bf3c341b781a2f09ff50a Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 30 Apr 2021 00:21:52 +0200 Subject: Add `compress` and `image` to core. --- core/compress/common.odin | 203 +++++ core/compress/gzip/example.odin | 70 ++ core/compress/gzip/gzip.odin | 314 ++++++++ core/compress/zlib/example.odin | 42 ++ core/compress/zlib/zlib.odin | 602 +++++++++++++++ core/image/common.odin | 107 +++ core/image/png/example.odin | 327 ++++++++ core/image/png/helpers.odin | 521 +++++++++++++ core/image/png/png.odin | 1590 +++++++++++++++++++++++++++++++++++++++ 9 files changed, 3776 insertions(+) create mode 100644 core/compress/common.odin create mode 100644 core/compress/gzip/example.odin create mode 100644 core/compress/gzip/gzip.odin create mode 100644 core/compress/zlib/example.odin create mode 100644 core/compress/zlib/zlib.odin create mode 100644 core/image/common.odin create mode 100644 core/image/png/example.odin create mode 100644 core/image/png/helpers.odin create mode 100644 core/image/png/png.odin diff --git a/core/compress/common.odin b/core/compress/common.odin new file mode 100644 index 000000000..99b054903 --- /dev/null +++ b/core/compress/common.odin @@ -0,0 +1,203 @@ +package compress + +import "core:io" +import "core:image" + +// Error helper, e.g. is_kind(err, General_Error.OK); +is_kind :: proc(u: $U, x: $V) -> bool { + v, ok := u.(V); + return ok && v == x; +} + +Error :: union { + General_Error, + Deflate_Error, + ZLIB_Error, + GZIP_Error, + ZIP_Error, + /* + This is here because png.load will return a this type of error union, + as it may involve an I/O error, a Deflate error, etc. + */ + image.PNG_Error, +} + +General_Error :: enum { + OK = 0, + File_Not_Found, + Cannot_Open_File, + File_Too_Short, + Stream_Too_Short, + Output_Too_Short, + Unknown_Compression_Method, + Checksum_Failed, + Incompatible_Options, + Unimplemented, +} + +GZIP_Error :: enum { + Invalid_GZIP_Signature, + Reserved_Flag_Set, + Invalid_Extra_Data, + Original_Name_Too_Long, + Comment_Too_Long, + Payload_Length_Invalid, + Payload_CRC_Invalid, +} + +ZIP_Error :: enum { + Invalid_ZIP_File_Signature, + Unexpected_Signature, + Insert_Next_Disk, + Expected_End_of_Central_Directory_Record, +} + +ZLIB_Error :: enum { + Unsupported_Window_Size, + FDICT_Unsupported, + Unsupported_Compression_Level, + Code_Buffer_Malformed, +} + +Deflate_Error :: enum { + Huffman_Bad_Sizes, + Huffman_Bad_Code_Lengths, + Inflate_Error, + Bad_Distance, + Bad_Huffman_Code, + Len_Nlen_Mismatch, + BType_3, +} + +// General context for ZLIB, LZW, etc. +Context :: struct { + code_buffer: u32, + num_bits: i8, + /* + num_bits will be set to -100 if the buffer is malformed + */ + eof: b8, + + input: io.Stream, + output: io.Stream, + bytes_written: i64, + // Used to update hash as we write instead of all at once + rolling_hash: u32, + + // Sliding window buffer. Size must be a power of two. + window_size: i64, + last: ^[dynamic]byte, +} + +// Stream helpers +/* + TODO: These need to be optimized. + + Streams should really only check if a certain method is available once, perhaps even during setup. + + Bit and byte readers may be merged so that reading bytes will grab them from the bit buffer first. + This simplifies end-of-stream handling where bits may be left in the bit buffer. +*/ + +read_data :: #force_inline proc(c: ^Context, $T: typeid) -> (res: T, err: io.Error) { + b := make([]u8, size_of(T), context.temp_allocator); + r, e1 := io.to_reader(c.input); + _, e2 := io.read(r, b); + if !e1 || e2 != .None { + return T{}, e2; + } + + res = (^T)(raw_data(b))^; + return res, .None; +} + +read_u8 :: #force_inline proc(z: ^Context) -> (res: u8, err: io.Error) { + return read_data(z, u8); +} + +peek_data :: #force_inline proc(c: ^Context, $T: typeid) -> (res: T, err: io.Error) { + // Get current position to read from. + curr, e1 := c.input->impl_seek(0, .Current); + if e1 != .None { + return T{}, e1; + } + r, e2 := io.to_reader_at(c.input); + if !e2 { + return T{}, .Empty; + } + b := make([]u8, size_of(T), context.temp_allocator); + _, e3 := io.read_at(r, b, curr); + if e3 != .None { + return T{}, .Empty; + } + + res = (^T)(raw_data(b))^; + return res, .None; +} + +// Sliding window read back +peek_back_byte :: proc(c: ^Context, offset: i64) -> (res: u8, err: io.Error) { + // Look back into the sliding window. + return c.last[offset % c.window_size], .None; +} + +// Generalized bit reader LSB +refill_lsb :: proc(z: ^Context, width := i8(24)) { + for { + if z.num_bits > width { + break; + } + if z.code_buffer == 0 && z.num_bits == -1 { + z.num_bits = 0; + } + if z.code_buffer >= 1 << uint(z.num_bits) { + // Code buffer is malformed. + z.num_bits = -100; + return; + } + c, err := read_u8(z); + if err != .None { + // This is fine at the end of the file. + z.num_bits = -42; + z.eof = true; + return; + } + z.code_buffer |= (u32(c) << u8(z.num_bits)); + z.num_bits += 8; + } +} + +consume_bits_lsb :: #force_inline proc(z: ^Context, width: u8) { + z.code_buffer >>= width; + z.num_bits -= i8(width); +} + +peek_bits_lsb :: #force_inline proc(z: ^Context, width: u8) -> u32 { + if z.num_bits < i8(width) { + refill_lsb(z); + } + // assert(z.num_bits >= i8(width)); + return z.code_buffer & ~(~u32(0) << width); +} + +peek_bits_no_refill_lsb :: #force_inline proc(z: ^Context, width: u8) -> u32 { + assert(z.num_bits >= i8(width)); + return z.code_buffer & ~(~u32(0) << width); +} + +read_bits_lsb :: #force_inline proc(z: ^Context, width: u8) -> u32 { + k := peek_bits_lsb(z, width); + consume_bits_lsb(z, width); + return k; +} + +read_bits_no_refill_lsb :: #force_inline proc(z: ^Context, width: u8) -> u32 { + k := peek_bits_no_refill_lsb(z, width); + consume_bits_lsb(z, width); + return k; +} + +discard_to_next_byte_lsb :: proc(z: ^Context) { + discard := u8(z.num_bits & 7); + consume_bits_lsb(z, discard); +} \ No newline at end of file diff --git a/core/compress/gzip/example.odin b/core/compress/gzip/example.odin new file mode 100644 index 000000000..fa0cba9db --- /dev/null +++ b/core/compress/gzip/example.odin @@ -0,0 +1,70 @@ +//+ignore +package gzip + +import "core:compress/gzip" +import "core:bytes" +import "core:os" + +// Small GZIP file with fextra, fname and fcomment present. +@private +TEST: []u8 = { + 0x1f, 0x8b, 0x08, 0x1c, 0xcb, 0x3b, 0x3a, 0x5a, + 0x02, 0x03, 0x07, 0x00, 0x61, 0x62, 0x03, 0x00, + 0x63, 0x64, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x6e, + 0x61, 0x6d, 0x65, 0x00, 0x54, 0x68, 0x69, 0x73, + 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x63, 0x6f, + 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x2b, 0x48, + 0xac, 0xcc, 0xc9, 0x4f, 0x4c, 0x01, 0x00, 0x15, + 0x6a, 0x2c, 0x42, 0x07, 0x00, 0x00, 0x00, +}; + +main :: proc() { + // Set up output buffer. + buf: bytes.Buffer; + defer bytes.buffer_destroy(&buf); + + stdout :: proc(s: string) { + os.write_string(os.stdout, s); + } + stderr :: proc(s: string) { + os.write_string(os.stderr, s); + } + + args := os.args; + + if len(args) < 2 { + stderr("No input file specified.\n"); + err := gzip.load(&TEST, &buf); + if gzip.is_kind(err, gzip.E_General.OK) { + stdout("Displaying test vector: "); + stdout(bytes.buffer_to_string(&buf)); + stdout("\n"); + } + } + + // The rest are all files. + args = args[1:]; + err: gzip.Error; + + for file in args { + if file == "-" { + // Read from stdin + s := os.stream_from_handle(os.stdin); + err = gzip.load(&s, &buf); + } else { + err = gzip.load(file, &buf); + } + if !gzip.is_kind(err, gzip.E_General.OK) { + if gzip.is_kind(err, gzip.E_General.File_Not_Found) { + stderr("File not found: "); + stderr(file); + stderr("\n"); + os.exit(1); + } + stderr("GZIP returned an error.\n"); + os.exit(2); + } + stdout(bytes.buffer_to_string(&buf)); + } + os.exit(0); +} \ No newline at end of file diff --git a/core/compress/gzip/gzip.odin b/core/compress/gzip/gzip.odin new file mode 100644 index 000000000..3278d7c1d --- /dev/null +++ b/core/compress/gzip/gzip.odin @@ -0,0 +1,314 @@ +package gzip + +import "core:compress/zlib" +import "core:compress" +import "core:os" +import "core:io" +import "core:bytes" +import "core:hash" + +/* + + This package implements support for the GZIP file format v4.3, + as specified in RFC 1952. + + It is implemented in such a way that it lends itself naturally + to be the input to a complementary TAR implementation. + +*/ + +Magic :: enum u16le { + GZIP = 0x8b << 8 | 0x1f, +} + +Header :: struct #packed { + magic: Magic, + compression_method: Compression, + flags: Header_Flags, + modification_time: u32le, + xfl: Compression_Flags, + os: OS, +} +#assert(size_of(Header) == 10); + +Header_Flag :: enum u8 { + // Order is important + text = 0, + header_crc = 1, + extra = 2, + name = 3, + comment = 4, + reserved_1 = 5, + reserved_2 = 6, + reserved_3 = 7, +} +Header_Flags :: distinct bit_set[Header_Flag; u8]; + +OS :: enum u8 { + FAT = 0, + Amiga = 1, + VMS = 2, + Unix = 3, + VM_CMS = 4, + Atari_TOS = 5, + HPFS = 6, + Macintosh = 7, + Z_System = 8, + CP_M = 9, + TOPS_20 = 10, + NTFS = 11, + QDOS = 12, + Acorn_RISCOS = 13, + _Unknown = 14, + Unknown = 255, +} +OS_Name :: #partial [OS]string{ + .FAT = "FAT", + .Amiga = "Amiga", + .VMS = "VMS/OpenVMS", + .Unix = "Unix", + .VM_CMS = "VM/CMS", + .Atari_TOS = "Atari TOS", + .HPFS = "HPFS", + .Macintosh = "Macintosh", + .Z_System = "Z-System", + .CP_M = "CP/M", + .TOPS_20 = "TOPS-20", + .NTFS = "NTFS", + .QDOS = "QDOS", + .Acorn_RISCOS = "Acorn RISCOS", + .Unknown = "Unknown", +}; + +Compression :: enum u8 { + DEFLATE = 8, +} + +Compression_Flags :: enum u8 { + Maximum_Compression = 2, + Fastest_Compression = 4, +} + +Error :: compress.Error; +E_General :: compress.General_Error; +E_GZIP :: compress.GZIP_Error; +E_ZLIB :: compress.ZLIB_Error; +E_Deflate :: compress.Deflate_Error; +is_kind :: compress.is_kind; + +load_from_slice :: proc(slice: ^[]u8, buf: ^bytes.Buffer, allocator := context.allocator) -> (err: Error) { + + r := bytes.Reader{}; + bytes.reader_init(&r, slice^); + stream := bytes.reader_to_stream(&r); + + err = load_from_stream(&stream, buf, allocator); + + return err; +} + +load_from_file :: proc(filename: string, buf: ^bytes.Buffer, allocator := context.allocator) -> (err: Error) { + data, ok := os.read_entire_file(filename, context.temp_allocator); + if ok { + err = load_from_slice(&data, buf, allocator); + return; + } else { + return E_General.File_Not_Found; + } +} + +load_from_stream :: proc(stream: ^io.Stream, buf: ^bytes.Buffer, allocator := context.allocator) -> (err: Error) { + + ctx := compress.Context{ + input = stream^, + }; + buf := buf; + ws := bytes.buffer_to_stream(buf); + ctx.output = ws; + + header, e := compress.read_data(&ctx, Header); + if e != .None { + return E_General.File_Too_Short; + } + + if header.magic != .GZIP { + return E_GZIP.Invalid_GZIP_Signature; + } + if header.compression_method != .DEFLATE { + return E_General.Unknown_Compression_Method; + } + + if header.os >= ._Unknown { + header.os = .Unknown; + } + + if .reserved_1 in header.flags || .reserved_2 in header.flags || .reserved_3 in header.flags { + return E_GZIP.Reserved_Flag_Set; + } + + // printf("signature: %v\n", header.magic); + // printf("compression: %v\n", header.compression_method); + // printf("flags: %v\n", header.flags); + // printf("modification time: %v\n", time.unix(i64(header.modification_time), 0)); + // printf("xfl: %v (%v)\n", header.xfl, int(header.xfl)); + // printf("os: %v\n", OS_Name[header.os]); + + if .extra in header.flags { + xlen, e_extra := compress.read_data(&ctx, u16le); + if e_extra != .None { + return E_General.Stream_Too_Short; + } + // printf("Extra data present (%v bytes)\n", xlen); + if xlen < 4 { + // Minimum length is 2 for ID + 2 for a field length, if set to zero. + return E_GZIP.Invalid_Extra_Data; + } + + field_id: [2]u8; + field_length: u16le; + field_error: io.Error; + + for xlen >= 4 { + // println("Parsing Extra field(s)."); + field_id, field_error = compress.read_data(&ctx, [2]u8); + if field_error != .None { + // printf("Parsing Extra returned: %v\n", field_error); + return E_General.Stream_Too_Short; + } + xlen -= 2; + + field_length, field_error = compress.read_data(&ctx, u16le); + if field_error != .None { + // printf("Parsing Extra returned: %v\n", field_error); + return E_General.Stream_Too_Short; + } + xlen -= 2; + + if xlen <= 0 { + // We're not going to try and recover by scanning for a ZLIB header. + // Who knows what else is wrong with this file. + return E_GZIP.Invalid_Extra_Data; + } + + // printf(" Field \"%v\" of length %v found: ", string(field_id[:]), field_length); + if field_length > 0 { + field_data := make([]u8, field_length, context.temp_allocator); + _, field_error = ctx.input->impl_read(field_data); + if field_error != .None { + // printf("Parsing Extra returned: %v\n", field_error); + return E_General.Stream_Too_Short; + } + xlen -= field_length; + + // printf("%v\n", string(field_data)); + } + + if xlen != 0 { + return E_GZIP.Invalid_Extra_Data; + } + } + } + + if .name in header.flags { + // Should be enough. + name: [1024]u8; + b: [1]u8; + i := 0; + name_error: io.Error; + + for i < len(name) { + _, name_error = ctx.input->impl_read(b[:]); + if name_error != .None { + return E_General.Stream_Too_Short; + } + if b == 0 { + break; + } + name[i] = b[0]; + i += 1; + if i >= len(name) { + return E_GZIP.Original_Name_Too_Long; + } + } + // printf("Original filename: %v\n", string(name[:i])); + } + + if .comment in header.flags { + // Should be enough. + comment: [1024]u8; + b: [1]u8; + i := 0; + comment_error: io.Error; + + for i < len(comment) { + _, comment_error = ctx.input->impl_read(b[:]); + if comment_error != .None { + return E_General.Stream_Too_Short; + } + if b == 0 { + break; + } + comment[i] = b[0]; + i += 1; + if i >= len(comment) { + return E_GZIP.Comment_Too_Long; + } + } + // printf("Comment: %v\n", string(comment[:i])); + } + + if .header_crc in header.flags { + crc16: [2]u8; + crc_error: io.Error; + _, crc_error = ctx.input->impl_read(crc16[:]); + if crc_error != .None { + return E_General.Stream_Too_Short; + } + /* + We don't actually check the CRC16 (lower 2 bytes of CRC32 of header data until the CRC field). + If we find a gzip file in the wild that sets this field, we can add proper support for it. + */ + } + + /* + We should have arrived at the ZLIB payload. + */ + + zlib_error := zlib.inflate_raw(&ctx); + + // fmt.printf("ZLIB returned: %v\n", zlib_error); + + if !is_kind(zlib_error, E_General.OK) || zlib_error == nil { + return zlib_error; + } + + /* + Read CRC32 using the ctx bit reader because zlib may leave bytes in there. + */ + compress.discard_to_next_byte_lsb(&ctx); + + payload_crc_b: [4]u8; + payload_len_b: [4]u8; + for i in 0..3 { + payload_crc_b[i] = u8(compress.read_bits_lsb(&ctx, 8)); + } + payload_crc := transmute(u32le)payload_crc_b; + for i in 0..3 { + payload_len_b[i] = u8(compress.read_bits_lsb(&ctx, 8)); + } + payload_len := int(transmute(u32le)payload_len_b); + + payload := bytes.buffer_to_bytes(buf); + crc32 := u32le(hash.crc32(payload)); + + if crc32 != payload_crc { + return E_GZIP.Payload_CRC_Invalid; + } + + if len(payload) != payload_len { + return E_GZIP.Payload_Length_Invalid; + } + return E_General.OK; +} + +load :: proc{load_from_file, load_from_slice, load_from_stream}; \ No newline at end of file diff --git a/core/compress/zlib/example.odin b/core/compress/zlib/example.odin new file mode 100644 index 000000000..a7fa76d62 --- /dev/null +++ b/core/compress/zlib/example.odin @@ -0,0 +1,42 @@ +//+ignore +package zlib + +import "core:compress/zlib" +import "core:bytes" +import "core:fmt" + +main :: proc() { + + ODIN_DEMO: []u8 = { + 120, 156, 101, 144, 77, 110, 131, 48, 16, 133, 215, 204, 41, 158, 44, + 69, 73, 32, 148, 182, 75, 35, 14, 208, 125, 47, 96, 185, 195, 143, + 130, 13, 50, 38, 81, 84, 101, 213, 75, 116, 215, 43, 246, 8, 53, + 82, 126, 8, 181, 188, 152, 153, 111, 222, 147, 159, 123, 165, 247, 170, + 98, 24, 213, 88, 162, 198, 244, 157, 243, 16, 186, 115, 44, 75, 227, + 5, 77, 115, 72, 137, 222, 117, 122, 179, 197, 39, 69, 161, 170, 156, + 50, 144, 5, 68, 130, 4, 49, 126, 127, 190, 191, 144, 34, 19, 57, + 69, 74, 235, 209, 140, 173, 242, 157, 155, 54, 158, 115, 162, 168, 12, + 181, 239, 246, 108, 17, 188, 174, 242, 224, 20, 13, 199, 198, 235, 250, + 194, 166, 129, 86, 3, 99, 157, 172, 37, 230, 62, 73, 129, 151, 252, + 70, 211, 5, 77, 31, 104, 188, 160, 113, 129, 215, 59, 205, 22, 52, + 123, 160, 83, 142, 255, 242, 89, 123, 93, 149, 200, 50, 188, 85, 54, + 252, 18, 248, 192, 238, 228, 235, 198, 86, 224, 118, 224, 176, 113, 166, + 112, 67, 106, 227, 159, 122, 215, 88, 95, 110, 196, 123, 205, 183, 224, + 98, 53, 8, 104, 213, 234, 201, 147, 7, 248, 192, 14, 170, 29, 25, + 171, 15, 18, 59, 138, 112, 63, 23, 205, 110, 254, 136, 109, 78, 231, + 63, 234, 138, 133, 204, + }; + + buf: bytes.Buffer; + + // We can pass ", true" to inflate a raw DEFLATE stream instead of a ZLIB wrapped one. + err := zlib.inflate(&ODIN_DEMO, &buf); + defer bytes.buffer_destroy(&buf); + + if !zlib.is_kind(err, zlib.E_General.OK) { + fmt.printf("\nError: %v\n", err); + } + s := bytes.buffer_to_string(&buf); + fmt.printf("Input: %v bytes, output (%v bytes):\n%v\n", len(ODIN_DEMO), len(s), s); + assert(len(s) == 438); +} \ No newline at end of file diff --git a/core/compress/zlib/zlib.odin b/core/compress/zlib/zlib.odin new file mode 100644 index 000000000..34a7984a7 --- /dev/null +++ b/core/compress/zlib/zlib.odin @@ -0,0 +1,602 @@ +package zlib + +import "core:compress" + +import "core:mem" +import "core:io" +import "core:bytes" +import "core:hash" +/* + zlib.inflate decompresses a ZLIB stream passed in as a []u8 or io.Stream. + Returns: Error. You can use zlib.is_kind or compress.is_kind to easily test for OK. +*/ + +Context :: compress.Context; + +Compression_Method :: enum u8 { + DEFLATE = 8, + Reserved = 15, +} + +Compression_Level :: enum u8 { + Fastest = 0, + Fast = 1, + Default = 2, + Maximum = 3, +} + +Options :: struct { + window_size: u16, + level: u8, +} + +Error :: compress.Error; +E_General :: compress.General_Error; +E_ZLIB :: compress.ZLIB_Error; +E_Deflate :: compress.Deflate_Error; +is_kind :: compress.is_kind; + +DEFLATE_MAX_CHUNK_SIZE :: 65535; +DEFLATE_MAX_LITERAL_SIZE :: 65535; +DEFLATE_MAX_DISTANCE :: 32768; +DEFLATE_MAX_LENGTH :: 258; + +HUFFMAN_MAX_BITS :: 16; +HUFFMAN_FAST_BITS :: 9; +HUFFMAN_FAST_MASK :: ((1 << HUFFMAN_FAST_BITS) - 1); + +Z_LENGTH_BASE := [31]u16{ + 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0, +}; + +Z_LENGTH_EXTRA := [31]u8{ + 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0, +}; + +Z_DIST_BASE := [32]u16{ + 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, + 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0, +}; + +Z_DIST_EXTRA := [32]u8{ + 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0, +}; + +Z_LENGTH_DEZIGZAG := []u8{ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15, +}; + +Z_FIXED_LENGTH := [288]u8{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8, +}; + +Z_FIXED_DIST := [32]u8{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, +}; + +/* + Accelerate all cases in default tables. +*/ +ZFAST_BITS :: 9; +ZFAST_MASK :: ((1 << ZFAST_BITS) - 1); + +/* + ZLIB-style Huffman encoding. + JPEG packs from left, ZLIB from right. We can't share code. +*/ +Huffman_Table :: struct { + fast: [1 << ZFAST_BITS]u16, + firstcode: [16]u16, + maxcode: [17]int, + firstsymbol: [16]u16, + size: [288]u8, + value: [288]u16, +}; + +// Implementation starts here + +z_bit_reverse :: #force_inline proc(n: u16, bits: u8) -> (r: u16) { + assert(bits <= 16); + // NOTE: Can optimize with llvm.bitreverse.i64 or some bit twiddling + // by reversing all of the bits and masking out the unneeded ones. + r = n; + r = ((r & 0xAAAA) >> 1) | ((r & 0x5555) << 1); + r = ((r & 0xCCCC) >> 2) | ((r & 0x3333) << 2); + r = ((r & 0xF0F0) >> 4) | ((r & 0x0F0F) << 4); + r = ((r & 0xFF00) >> 8) | ((r & 0x00FF) << 8); + + r >>= (16 - bits); + return; +} + +write_byte :: #force_inline proc(z: ^Context, c: u8) -> (err: io.Error) #no_bounds_check { + c := c; + buf := transmute([]u8)mem.Raw_Slice{data=&c, len=1}; + z.rolling_hash = hash.adler32(buf, z.rolling_hash); + + _, e := z.output->impl_write(buf); + if e != .None { + return e; + } + z.last[z.bytes_written % z.window_size] = c; + + z.bytes_written += 1; + return .None; +} + +allocate_huffman_table :: proc(allocator := context.allocator) -> (z: ^Huffman_Table, err: Error) { + + z = new(Huffman_Table, allocator); + return z, E_General.OK; +} + +build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) { + sizes: [HUFFMAN_MAX_BITS+1]int; + next_code: [HUFFMAN_MAX_BITS]int; + + k := int(0); + + mem.zero_slice(sizes[:]); + mem.zero_slice(z.fast[:]); + + for v, _ in code_lengths { + sizes[v] += 1; + } + sizes[0] = 0; + + for i in 1..16 { + if sizes[i] > (1 << uint(i)) { + return E_Deflate.Huffman_Bad_Sizes; + } + } + code := int(0); + + for i in 1..<16 { + next_code[i] = code; + z.firstcode[i] = u16(code); + z.firstsymbol[i] = u16(k); + code = code + sizes[i]; + if sizes[i] != 0 { + if (code - 1 >= (1 << u16(i))) { + return E_Deflate.Huffman_Bad_Code_Lengths; + } + } + z.maxcode[i] = code << (16 - uint(i)); + code <<= 1; + k += int(sizes[i]); + } + + z.maxcode[16] = 0x10000; // Sentinel + c: int; + + for v, ci in code_lengths { + if v != 0 { + c = next_code[v] - int(z.firstcode[v]) + int(z.firstsymbol[v]); + fastv := u16((u16(v) << 9) | u16(ci)); + z.size[c] = u8(v); + z.value[c] = u16(ci); + if (v <= ZFAST_BITS) { + j := z_bit_reverse(u16(next_code[v]), v); + for j < (1 << ZFAST_BITS) { + z.fast[j] = fastv; + j += (1 << v); + } + } + next_code[v] += 1; + } + } + return E_General.OK; +} + +decode_huffman_slowpath :: proc(z: ^Context, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check { + + r = 0; + err = E_General.OK; + + k: int; + s: u8; + + code := u16(compress.peek_bits_lsb(z, 16)); + + k = int(z_bit_reverse(code, 16)); + + #no_bounds_check for s = HUFFMAN_FAST_BITS+1; ; { + if k < t.maxcode[s] { + break; + } + s += 1; + } + if (s >= 16) { + return 0, E_Deflate.Bad_Huffman_Code; + } + // code size is s, so: + b := (k >> (16-s)) - int(t.firstcode[s]) + int(t.firstsymbol[s]); + if b >= size_of(t.size) { + return 0, E_Deflate.Bad_Huffman_Code; + } + if t.size[b] != s { + return 0, E_Deflate.Bad_Huffman_Code; + } + + compress.consume_bits_lsb(z, s); + + r = t.value[b]; + return r, E_General.OK; +} + +decode_huffman :: proc(z: ^Context, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check { + + if z.num_bits < 16 { + if z.num_bits == -100 { + return 0, E_ZLIB.Code_Buffer_Malformed; + } + compress.refill_lsb(z); + if z.eof { + return 0, E_General.Stream_Too_Short; + } + } + #no_bounds_check b := t.fast[z.code_buffer & ZFAST_MASK]; + if b != 0 { + s := u8(b >> ZFAST_BITS); + compress.consume_bits_lsb(z, s); + return b & 511, E_General.OK; + } + return decode_huffman_slowpath(z, t); +} + +parse_huffman_block :: proc(z: ^Context, z_repeat, z_offset: ^Huffman_Table) -> (err: Error) #no_bounds_check { + + #no_bounds_check for { + value, e := decode_huffman(z, z_repeat); + if !is_kind(e, E_General.OK) { + return err; + } + if value < 256 { + e := write_byte(z, u8(value)); + if e != .None { + return E_General.Output_Too_Short; + } + } else { + if value == 256 { + // End of block + return E_General.OK; + } + + value -= 257; + length := Z_LENGTH_BASE[value]; + if Z_LENGTH_EXTRA[value] > 0 { + length += u16(compress.read_bits_lsb(z, Z_LENGTH_EXTRA[value])); + } + + value, e = decode_huffman(z, z_offset); + if !is_kind(e, E_General.OK) { + return E_Deflate.Bad_Huffman_Code; + } + + distance := Z_DIST_BASE[value]; + if Z_DIST_EXTRA[value] > 0 { + distance += u16(compress.read_bits_lsb(z, Z_DIST_EXTRA[value])); + } + + if z.bytes_written < i64(distance) { + // Distance is longer than we've decoded so far. + return E_Deflate.Bad_Distance; + } + + offset := i64(z.bytes_written - i64(distance)); + /* + These might be sped up with a repl_byte call that copies + from the already written output more directly, and that + update the Adler checksum once after. + + That way we'd suffer less Stream vtable overhead. + */ + if distance == 1 { + /* + Replicate the last outputted byte, length times. + */ + if length > 0 { + b, e := compress.peek_back_byte(z, offset); + if e != .None { + return E_General.Output_Too_Short; + } + #no_bounds_check for _ in 0.. 0 { + #no_bounds_check for _ in 0.. (err: Error) #no_bounds_check { + /* + ctx.input must be an io.Stream backed by an implementation that supports: + - read + - size + + ctx.output must be an io.Stream backed by an implementation that supports: + - write + + raw determines whether the ZLIB header is processed, or we're inflating a raw + DEFLATE stream. + */ + + if !raw { + data_size := io.size(ctx.input); + if data_size < 6 { + return E_General.Stream_Too_Short; + } + + cmf, _ := compress.read_u8(ctx); + + method := Compression_Method(cmf & 0xf); + if method != .DEFLATE { + return E_General.Unknown_Compression_Method; + } + + cinfo := (cmf >> 4) & 0xf; + if cinfo > 7 { + return E_ZLIB.Unsupported_Window_Size; + } + ctx.window_size = 1 << (cinfo + 8); + + flg, _ := compress.read_u8(ctx); + + fcheck := flg & 0x1f; + fcheck_computed := (cmf << 8 | flg) & 0x1f; + if fcheck != fcheck_computed { + return E_General.Checksum_Failed; + } + + fdict := (flg >> 5) & 1; + /* + We don't handle built-in dictionaries for now. + They're application specific and PNG doesn't use them. + */ + if fdict != 0 { + return E_ZLIB.FDICT_Unsupported; + } + + // flevel := Compression_Level((flg >> 6) & 3); + /* + Inflate can consume bits belonging to the Adler checksum. + We pass the entire stream to Inflate and will unget bytes if we need to + at the end to compare checksums. + */ + + // Seed the Adler32 rolling checksum. + ctx.rolling_hash = 1; + } + + // Parse ZLIB stream without header. + err = inflate_raw(ctx); + if !is_kind(err, E_General.OK) { + return err; + } + + if !raw { + compress.discard_to_next_byte_lsb(ctx); + + adler32 := compress.read_bits_lsb(ctx, 8) << 24 | compress.read_bits_lsb(ctx, 8) << 16 | compress.read_bits_lsb(ctx, 8) << 8 | compress.read_bits_lsb(ctx, 8); + if ctx.rolling_hash != u32(adler32) { + return E_General.Checksum_Failed; + } + } + return E_General.OK; +} + +// @(optimization_mode="speed") +inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) -> (err: Error) #no_bounds_check { + final := u32(0); + type := u32(0); + + z.num_bits = 0; + z.code_buffer = 0; + + z_repeat: ^Huffman_Table; + z_offset: ^Huffman_Table; + codelength_ht: ^Huffman_Table; + + z_repeat, err = allocate_huffman_table(allocator=context.allocator); + if !is_kind(err, E_General.OK) { + return err; + } + z_offset, err = allocate_huffman_table(allocator=context.allocator); + if !is_kind(err, E_General.OK) { + return err; + } + codelength_ht, err = allocate_huffman_table(allocator=context.allocator); + if !is_kind(err, E_General.OK) { + return err; + } + defer free(z_repeat); + defer free(z_offset); + defer free(codelength_ht); + + if z.window_size == 0 { + z.window_size = DEFLATE_MAX_DISTANCE; + } + + // Allocate rolling window buffer. + last_b := mem.make_dynamic_array_len_cap([dynamic]u8, z.window_size, z.window_size, allocator); + z.last = &last_b; + defer delete(last_b); + + for { + final = compress.read_bits_lsb(z, 1); + type = compress.read_bits_lsb(z, 2); + + // log.debugf("Final: %v | Type: %v\n", final, type); + + if type == 0 { + // Uncompressed block + + // Discard bits until next byte boundary + compress.discard_to_next_byte_lsb(z); + + uncompressed_len := int(compress.read_bits_lsb(z, 16)); + length_check := int(compress.read_bits_lsb(z, 16)); + if uncompressed_len != ~length_check { + return E_Deflate.Len_Nlen_Mismatch; + } + + /* + TODO: Maybe speed this up with a stream-to-stream copy (read_from) + and a single Adler32 update after. + */ + #no_bounds_check for uncompressed_len > 0 { + compress.refill_lsb(z); + lit := compress.read_bits_lsb(z, 8); + write_byte(z, u8(lit)); + uncompressed_len -= 1; + } + } else if type == 3 { + return E_Deflate.BType_3; + } else { + // log.debugf("Err: %v | Final: %v | Type: %v\n", err, final, type); + if type == 1 { + // Use fixed code lengths. + err = build_huffman(z_repeat, Z_FIXED_LENGTH[:]); + if !is_kind(err, E_General.OK) { + return err; + } + err = build_huffman(z_offset, Z_FIXED_DIST[:]); + if !is_kind(err, E_General.OK) { + return err; + } + } else { + lencodes: [286+32+137]u8; + codelength_sizes: [19]u8; + + //i: u32; + n: u32; + + compress.refill_lsb(z, 14); + hlit := compress.read_bits_no_refill_lsb(z, 5) + 257; + hdist := compress.read_bits_no_refill_lsb(z, 5) + 1; + hclen := compress.read_bits_no_refill_lsb(z, 4) + 4; + ntot := hlit + hdist; + + #no_bounds_check for i in 0..= 19 { + return E_Deflate.Huffman_Bad_Code_Lengths; + } + if c < 16 { + lencodes[n] = u8(c); + n += 1; + } else { + fill := u8(0); + compress.refill_lsb(z, 7); + if c == 16 { + c = u16(compress.read_bits_no_refill_lsb(z, 2) + 3); + if n == 0 { + return E_Deflate.Huffman_Bad_Code_Lengths; + } + fill = lencodes[n - 1]; + } else if c == 17 { + c = u16(compress.read_bits_no_refill_lsb(z, 3) + 3); + } else if c == 18 { + c = u16(compress.read_bits_no_refill_lsb(z, 7) + 11); + } else { + return E_Deflate.Huffman_Bad_Code_Lengths; + } + + if ntot - n < u32(c) { + return E_Deflate.Huffman_Bad_Code_Lengths; + } + + nc := n + u32(c); + #no_bounds_check for ; n < nc; n += 1 { + lencodes[n] = fill; + } + } + } + + if n != ntot { + return E_Deflate.Huffman_Bad_Code_Lengths; + } + + err = build_huffman(z_repeat, lencodes[:hlit]); + if !is_kind(err, E_General.OK) { + return err; + } + + err = build_huffman(z_offset, lencodes[hlit:ntot]); + if !is_kind(err, E_General.OK) { + return err; + } + } + err = parse_huffman_block(z, z_repeat, z_offset); + // log.debugf("Err: %v | Final: %v | Type: %v\n", err, final, type); + if !is_kind(err, E_General.OK) { + return err; + } + } + if final == 1 { + break; + } + } + return E_General.OK; +} + +inflate_from_byte_array :: proc(input: ^[]u8, buf: ^bytes.Buffer, raw := false) -> (err: Error) { + ctx := Context{}; + + r := bytes.Reader{}; + bytes.reader_init(&r, input^); + rs := bytes.reader_to_stream(&r); + ctx.input = rs; + + buf := buf; + ws := bytes.buffer_to_stream(buf); + ctx.output = ws; + + err = inflate_from_stream(&ctx, raw); + + return err; +} + +inflate_from_byte_array_raw :: proc(input: ^[]u8, buf: ^bytes.Buffer, raw := false) -> (err: Error) { + return inflate_from_byte_array(input, buf, true); +} + +inflate :: proc{inflate_from_stream, inflate_from_byte_array}; +inflate_raw :: proc{inflate_from_stream_raw, inflate_from_byte_array_raw}; \ No newline at end of file diff --git a/core/image/common.odin b/core/image/common.odin new file mode 100644 index 000000000..d05feabaa --- /dev/null +++ b/core/image/common.odin @@ -0,0 +1,107 @@ +package image + +import "core:bytes" + +Image :: struct { + width: int, + height: int, + channels: int, + depth: u8, + pixels: bytes.Buffer, + /* + Some image loaders/writers can return/take an optional background color. + For convenience, we return them as u16 so we don't need to switch on the type + in our viewer, and can just test against nil. + */ + background: Maybe([3]u16), + sidecar: any, +} + +/* +Image_Option: + `.info` + This option behaves as `return_ihdr` and `do_not_decompress_image` and can be used + to gather an image's dimensions and color information. + + `.return_header` + Fill out img.sidecar.header with the image's format-specific header struct. + If we only care about the image specs, we can set `return_header` + + `do_not_decompress_image`, or `.info`, which works as if both of these were set. + + `.return_metadata` + Returns all chunks not needed to decode the data. + It also returns the header as if `.return_header` is set. + + `do_not_decompress_image` + Skip decompressing IDAT chunk, defiltering and the rest. + + `alpha_add_if_missing` + If the image has no alpha channel, it'll add one set to max(type). + Turns RGB into RGBA and Gray into Gray+Alpha + + `alpha_drop_if_present` + If the image has an alpha channel, drop it. + You may want to use `alpha_premultiply` in this case. + + NOTE: For PNG, this also skips handling of the tRNS chunk, if present, + unless you select `alpha_premultiply`. + In this case it'll premultiply the specified pixels in question only, + as the others are implicitly fully opaque. + + `alpha_premultiply` + If the image has an alpha channel, returns image data as follows: + RGB *= A, Gray = Gray *= A + + `blend_background` + If a bKGD chunk is present in a PNG, we normally just set `img.background` + with its value and leave it up to the application to decide how to display the image, + as per the PNG specification. + + With `blend_background` selected, we blend the image against the background + color. As this negates the use for an alpha channel, we'll drop it _unless_ + you also specify `alpha_add_if_missing`. + + Options that don't apply to an image format will be ignored by their loader. +*/ + +Option :: enum { + info = 0, + do_not_decompress_image, + return_header, + return_metadata, + alpha_add_if_missing, + alpha_drop_if_present, + alpha_premultiply, + blend_background, +} +Options :: distinct bit_set[Option]; + +PNG_Error :: enum { + Invalid_PNG_Signature, + IHDR_Not_First_Chunk, + IHDR_Corrupt, + IDAT_Missing, + IDAT_Must_Be_Contiguous, + IDAT_Corrupt, + PNG_Does_Not_Adhere_to_Spec, + PLTE_Encountered_Unexpectedly, + PLTE_Invalid_Length, + TRNS_Encountered_Unexpectedly, + BKGD_Invalid_Length, + Invalid_Image_Dimensions, + Unknown_Color_Type, + Invalid_Color_Bit_Depth_Combo, + Unknown_Filter_Method, + Unknown_Interlace_Method, +} + + +/* + Functions to help with image buffer calculations +*/ + +compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes := int(0)) -> (size: int) { + + size = ((((channels * width * depth) + 7) >> 3) + extra_row_bytes) * height; + return; +} \ No newline at end of file diff --git a/core/image/png/example.odin b/core/image/png/example.odin new file mode 100644 index 000000000..8cac5a505 --- /dev/null +++ b/core/image/png/example.odin @@ -0,0 +1,327 @@ +//+ignore +package png + +import "core:compress" +import "core:image" +import "core:image/png" +import "core:bytes" +import "core:fmt" + +// For PPM writer +import "core:mem" +import "core:os" + +main :: proc() { + file: string; + + options := image.Options{}; + err: compress.Error; + img: ^image.Image; + + file = "../../../misc/logo-slim.png"; + + img, err = png.load(file, options); + defer png.destroy(img); + + if !png.is_kind(err, png.E_General.OK) { + fmt.printf("Trying to read PNG file %v returned %v\n", file, err); + } else { + v: png.Info; + ok: bool; + + fmt.printf("Image: %vx%vx%v, %v-bit.\n", img.width, img.height, img.channels, img.depth); + + if v, ok = img.sidecar.(png.Info); ok { + // Handle ancillary chunks as you wish. + // We provide helper functions for a few types. + for c in v.chunks { + #partial switch (c.header.type) { + case .tIME: + t, _ := png.core_time(c); + fmt.printf("[tIME]: %v\n", t); + case .gAMA: + fmt.printf("[gAMA]: %v\n", png.gamma(c)); + case .pHYs: + phys := png.phys(c); + if phys.unit == .Meter { + xm := f32(img.width) / f32(phys.ppu_x); + ym := f32(img.height) / f32(phys.ppu_y); + dpi_x, dpi_y := png.phys_to_dpi(phys); + fmt.printf("[pHYs] Image resolution is %v x %v pixels per meter.\n", phys.ppu_x, phys.ppu_y); + fmt.printf("[pHYs] Image resolution is %v x %v DPI.\n", dpi_x, dpi_y); + fmt.printf("[pHYs] Image dimensions are %v x %v meters.\n", xm, ym); + } else { + fmt.printf("[pHYs] x: %v, y: %v pixels per unknown unit.\n", phys.ppu_x, phys.ppu_y); + } + case .iTXt, .zTXt, .tEXt: + res, ok_text := png.text(c); + if ok_text { + if c.header.type == .iTXt { + fmt.printf("[iTXt] %v (%v:%v): %v\n", res.keyword, res.language, res.keyword_localized, res.text); + } else { + fmt.printf("[tEXt/zTXt] %v: %v\n", res.keyword, res.text); + } + } + defer png.text_destroy(res); + case .bKGD: + fmt.printf("[bKGD] %v\n", img.background); + case .eXIf: + res, ok_exif := png.exif(c); + if ok_exif { + /* + Other than checking the signature and byte order, we don't handle Exif data. + If you wish to interpret it, pass it to an Exif parser. + */ + fmt.printf("[eXIf] %v\n", res); + } + case .PLTE: + plte, plte_ok := png.plte(c); + if plte_ok { + fmt.printf("[PLTE] %v\n", plte); + } else { + fmt.printf("[PLTE] Error\n"); + } + case .hIST: + res, ok_hist := png.hist(c); + if ok_hist { + fmt.printf("[hIST] %v\n", res); + } + case .cHRM: + res, ok_chrm := png.chrm(c); + if ok_chrm { + fmt.printf("[cHRM] %v\n", res); + } + case .sPLT: + res, ok_splt := png.splt(c); + if ok_splt { + fmt.printf("[sPLT] %v\n", res); + } + png.splt_destroy(res); + case .sBIT: + if res, ok_sbit := png.sbit(c); ok_sbit { + fmt.printf("[sBIT] %v\n", res); + } + case .iCCP: + res, ok_iccp := png.iccp(c); + if ok_iccp { + fmt.printf("[iCCP] %v\n", res); + } + png.iccp_destroy(res); + case .sRGB: + if res, ok_srgb := png.srgb(c); ok_srgb { + fmt.printf("[sRGB] Rendering intent: %v\n", res); + } + case: + type := c.header.type; + name := png.chunk_type_to_name(&type); + fmt.printf("[%v]: %v\n", name, c.data); + } + } + } + } + + if is_kind(err, E_General.OK) && .do_not_decompress_image not_in options && .info not_in options { + if ok := write_image_as_ppm("out.ppm", img); ok { + fmt.println("Saved decoded image."); + } else { + fmt.println("Error saving out.ppm."); + fmt.println(img); + } + } +} + +// Crappy PPM writer used during testing. Don't use in production. +write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: bool) { + + _bg :: proc(bg: Maybe([3]u16), x, y: int, high := true) -> (res: [3]u16) { + if v, ok := bg.?; ok { + res = v; + } else { + if high { + l := u16(30 * 256 + 30); + + if (x & 4 == 0) ~ (y & 4 == 0) { + res = [3]u16{l, 0, l}; + } else { + res = [3]u16{l >> 1, 0, l >> 1}; + } + } else { + if (x & 4 == 0) ~ (y & 4 == 0) { + res = [3]u16{30, 30, 30}; + } else { + res = [3]u16{15, 15, 15}; + } + } + } + return; + } + + // profiler.timed_proc(); + using image; + using os; + + flags: int = O_WRONLY|O_CREATE|O_TRUNC; + + img := image; + + // PBM 16-bit images are big endian + when ODIN_ENDIAN == "little" { + if img.depth == 16 { + // The pixel components are in Big Endian. Let's byteswap back. + input := mem.slice_data_cast([]u16, img.pixels.buf[:]); + output := mem.slice_data_cast([]u16be, img.pixels.buf[:]); + #no_bounds_check for v, i in input { + output[i] = u16be(v); + } + } + } + + pix := bytes.buffer_to_bytes(&img.pixels); + + if len(pix) == 0 || len(pix) < image.width * image.height * int(image.channels) { + return false; + } + + mode: int = 0; + when ODIN_OS == "linux" || ODIN_OS == "darwin" { + // NOTE(justasd): 644 (owner read, write; group read; others read) + mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + } + + fd, err := open(filename, flags, mode); + if err != 0 { + return false; + } + defer close(fd); + + write_string(fd, + fmt.tprintf("P6\n%v %v\n%v\n", width, height, (1 << depth -1)), + ); + + if channels == 3 { + // We don't handle transparency here... + write_ptr(fd, raw_data(pix), len(pix)); + } else { + bpp := depth == 16 ? 2 : 1; + bytes_needed := width * height * 3 * bpp; + + op := bytes.Buffer{}; + bytes.buffer_init_allocator(&op, bytes_needed, bytes_needed); + defer bytes.buffer_destroy(&op); + + if channels == 1 { + if depth == 16 { + assert(len(pix) == width * height * 2); + p16 := mem.slice_data_cast([]u16, pix); + o16 := mem.slice_data_cast([]u16, op.buf[:]); + #no_bounds_check for len(p16) != 0 { + r := u16(p16[0]); + o16[0] = r; + o16[1] = r; + o16[2] = r; + p16 = p16[1:]; + o16 = o16[3:]; + } + } else { + o := 0; + for i := 0; i < len(pix); i += 1 { + r := pix[i]; + op.buf[o ] = r; + op.buf[o+1] = r; + op.buf[o+2] = r; + o += 3; + } + } + write_ptr(fd, raw_data(op.buf), len(op.buf)); + } else if channels == 2 { + if depth == 16 { + p16 := mem.slice_data_cast([]u16, pix); + o16 := mem.slice_data_cast([]u16, op.buf[:]); + + bgcol := img.background; + + #no_bounds_check for len(p16) != 0 { + r := f64(u16(p16[0])); + bg: f64; + if bgcol != nil { + v := bgcol.([3]u16)[0]; + bg = f64(v); + } + a := f64(u16(p16[1])) / 65535.0; + l := (a * r) + (1 - a) * bg; + + o16[0] = u16(l); + o16[1] = u16(l); + o16[2] = u16(l); + + p16 = p16[2:]; + o16 = o16[3:]; + } + } else { + o := 0; + for i := 0; i < len(pix); i += 2 { + r := pix[i]; a := pix[i+1]; a1 := f32(a) / 255.0; + c := u8(f32(r) * a1); + op.buf[o ] = c; + op.buf[o+1] = c; + op.buf[o+2] = c; + o += 3; + } + } + write_ptr(fd, raw_data(op.buf), len(op.buf)); + } else if channels == 4 { + if depth == 16 { + p16 := mem.slice_data_cast([]u16be, pix); + o16 := mem.slice_data_cast([]u16be, op.buf[:]); + + #no_bounds_check for len(p16) != 0 { + + bg := _bg(img.background, 0, 0); + r := f32(p16[0]); + g := f32(p16[1]); + b := f32(p16[2]); + a := f32(p16[3]) / 65535.0; + + lr := (a * r) + (1 - a) * f32(bg[0]); + lg := (a * g) + (1 - a) * f32(bg[1]); + lb := (a * b) + (1 - a) * f32(bg[2]); + + o16[0] = u16be(lr); + o16[1] = u16be(lg); + o16[2] = u16be(lb); + + p16 = p16[4:]; + o16 = o16[3:]; + } + } else { + o := 0; + + for i := 0; i < len(pix); i += 4 { + + x := (i / 4) % width; + y := i / width / 4; + + _b := _bg(img.background, x, y, false); + bgcol := [3]u8{u8(_b[0]), u8(_b[1]), u8(_b[2])}; + + r := f32(pix[i]); + g := f32(pix[i+1]); + b := f32(pix[i+2]); + a := f32(pix[i+3]) / 255.0; + + lr := u8(f32(r) * a + (1 - a) * f32(bgcol[0])); + lg := u8(f32(g) * a + (1 - a) * f32(bgcol[1])); + lb := u8(f32(b) * a + (1 - a) * f32(bgcol[2])); + op.buf[o ] = lr; + op.buf[o+1] = lg; + op.buf[o+2] = lb; + o += 3; + } + } + write_ptr(fd, raw_data(op.buf), len(op.buf)); + } else { + return false; + } + } + return true; +} \ No newline at end of file diff --git a/core/image/png/helpers.odin b/core/image/png/helpers.odin new file mode 100644 index 000000000..a179cd23b --- /dev/null +++ b/core/image/png/helpers.odin @@ -0,0 +1,521 @@ +package png + +import "core:image" +import "core:compress/zlib" +import coretime "core:time" +import "core:strings" +import "core:bytes" +import "core:mem" + +/* + These are a few useful utility functions to work with PNG images. +*/ + +/* + Cleanup of image-specific data. + There are other helpers for cleanup of PNG-specific data. + Those are named *_destroy, where * is the name of the helper. +*/ + +destroy :: proc(img: ^Image) { + if img == nil { + /* + Nothing to do. + Load must've returned with an error. + */ + return; + } + + bytes.buffer_destroy(&img.pixels); + + /* + We don't need to do anything for the individual chunks. + They're allocated on the temp allocator, as is info.chunks + + See read_chunk. + */ + free(img); +} + +/* + Chunk helpers +*/ + +gamma :: proc(c: Chunk) -> f32 { + assert(c.header.type == .gAMA); + res := (^gAMA)(raw_data(c.data))^; + when true { + // Returns the wrong result on old backend + // Fixed for -llvm-api + return f32(res.gamma_100k) / 100_000.0; + } else { + return f32(u32(res.gamma_100k)) / 100_000.0; + } +} + +INCHES_PER_METER :: 1000.0 / 25.4; + +phys :: proc(c: Chunk) -> pHYs { + assert(c.header.type == .pHYs); + res := (^pHYs)(raw_data(c.data))^; + return res; +} + +phys_to_dpi :: proc(p: pHYs) -> (x_dpi, y_dpi: f32) { + return f32(p.ppu_x) / INCHES_PER_METER, f32(p.ppu_y) / INCHES_PER_METER; +} + +time :: proc(c: Chunk) -> tIME { + assert(c.header.type == .tIME); + res := (^tIME)(raw_data(c.data))^; + return res; +} + +core_time :: proc(c: Chunk) -> (t: coretime.Time, ok: bool) { + png_time := time(c); + using png_time; + return coretime.datetime_to_time( + int(year), int(month), int(day), + int(hour), int(minute), int(second)); +} + +text :: proc(c: Chunk) -> (res: Text, ok: bool) { + #partial switch c.header.type { + case .tEXt: + ok = true; + + fields := bytes.split(s=c.data, sep=[]u8{0}, allocator=context.temp_allocator); + if len(fields) == 2 { + res.keyword = strings.clone(string(fields[0])); + res.text = strings.clone(string(fields[1])); + } else { + ok = false; + } + return; + case .zTXt: + ok = true; + + fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator); + if len(fields) != 3 || len(fields[1]) != 0 { + // Compression method must be 0=Deflate, which thanks to the split above turns + // into an empty slice + ok = false; return; + } + + // Set up ZLIB context and decompress text payload. + buf: bytes.Buffer; + zlib_error := zlib.inflate_from_byte_array(&fields[2], &buf); + defer bytes.buffer_destroy(&buf); + if !is_kind(zlib_error, E_General.OK) { + ok = false; return; + } + + res.keyword = strings.clone(string(fields[0])); + res.text = strings.clone(bytes.buffer_to_string(&buf)); + return; + case .iTXt: + ok = true; + + s := string(c.data); + null := strings.index_byte(s, 0); + if null == -1 { + ok = false; return; + } + if len(c.data) < null + 4 { + // At a minimum, including the \0 following the keyword, we require 5 more bytes. + ok = false; return; + } + res.keyword = strings.clone(string(c.data[:null])); + rest := c.data[null+1:]; + + compression_flag := rest[:1][0]; + if compression_flag > 1 { + ok = false; return; + } + compression_method := rest[1:2][0]; + if compression_flag == 1 && compression_method > 0 { + // Only Deflate is supported + ok = false; return; + } + rest = rest[2:]; + + // We now expect an optional language keyword and translated keyword, both followed by a \0 + null = strings.index_byte(string(rest), 0); + if null == -1 { + ok = false; return; + } + res.language = strings.clone(string(rest[:null])); + rest = rest[null+1:]; + + null = strings.index_byte(string(rest), 0); + if null == -1 { + ok = false; return; + } + res.keyword_localized = strings.clone(string(rest[:null])); + rest = rest[null+1:]; + if compression_flag == 0 { + res.text = strings.clone(string(rest)); + } else { + // Set up ZLIB context and decompress text payload. + buf: bytes.Buffer; + zlib_error := zlib.inflate_from_byte_array(&rest, &buf); + defer bytes.buffer_destroy(&buf); + if !is_kind(zlib_error, E_General.OK) { + + ok = false; return; + } + + res.text = strings.clone(bytes.buffer_to_string(&buf)); + } + return; + case: + // PNG text helper called with an unrecognized chunk type. + ok = false; return; + + } +} + +text_destroy :: proc(text: Text) { + delete(text.keyword); + delete(text.keyword_localized); + delete(text.language); + delete(text.text); +} + +iccp :: proc(c: Chunk) -> (res: iCCP, ok: bool) { + ok = true; + + fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator); + + if len(fields[0]) < 1 || len(fields[0]) > 79 { + // Invalid profile name + ok = false; return; + } + + if len(fields[1]) != 0 { + // Compression method should be a zero, which the split turned into an empty slice. + ok = false; return; + } + + // Set up ZLIB context and decompress iCCP payload + buf: bytes.Buffer; + zlib_error := zlib.inflate_from_byte_array(&fields[2], &buf); + if !is_kind(zlib_error, E_General.OK) { + bytes.buffer_destroy(&buf); + ok = false; return; + } + + res.name = strings.clone(string(fields[0])); + res.profile = bytes.buffer_to_bytes(&buf); + + return; +} + +iccp_destroy :: proc(i: iCCP) { + delete(i.name); + + delete(i.profile); + +} + +srgb :: proc(c: Chunk) -> (res: sRGB, ok: bool) { + ok = true; + + if c.header.type != .sRGB || len(c.data) != 1 { + return {}, false; + } + + res.intent = sRGB_Rendering_Intent(c.data[0]); + if res.intent > max(sRGB_Rendering_Intent) { + ok = false; return; + } + return; +} + +plte :: proc(c: Chunk) -> (res: PLTE, ok: bool) { + if c.header.type != .PLTE { + return {}, false; + } + + i := 0; j := 0; ok = true; + for j < int(c.header.length) { + res.entries[i] = {c.data[j], c.data[j+1], c.data[j+2]}; + i += 1; j += 3; + } + res.used = u16(i); + return; +} + +splt :: proc(c: Chunk) -> (res: sPLT, ok: bool) { + if c.header.type != .sPLT { + return {}, false; + } + ok = true; + + fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=2, allocator=context.temp_allocator); + if len(fields) != 2 { + return {}, false; + } + + res.depth = fields[1][0]; + if res.depth != 8 && res.depth != 16 { + return {}, false; + } + + data := fields[1][1:]; + count: int; + + if res.depth == 8 { + if len(data) % 6 != 0 { + return {}, false; + } + count = len(data) / 6; + if count > 256 { + return {}, false; + } + + res.entries = mem.slice_data_cast([][4]u8, data); + } else { // res.depth == 16 + if len(data) % 10 != 0 { + return {}, false; + } + count = len(data) / 10; + if count > 256 { + return {}, false; + } + + res.entries = mem.slice_data_cast([][4]u16, data); + } + + res.name = strings.clone(string(fields[0])); + res.used = u16(count); + + return; +} + +splt_destroy :: proc(s: sPLT) { + delete(s.name); +} + +sbit :: proc(c: Chunk) -> (res: [4]u8, ok: bool) { + /* + Returns [4]u8 with the significant bits in each channel. + A channel will contain zero if not applicable to the PNG color type. + */ + + if len(c.data) < 1 || len(c.data) > 4 { + ok = false; return; + } + ok = true; + + for i := 0; i < len(c.data); i += 1 { + res[i] = c.data[i]; + } + return; + +} + +hist :: proc(c: Chunk) -> (res: hIST, ok: bool) { + if c.header.type != .hIST { + return {}, false; + } + if c.header.length & 1 == 1 || c.header.length > 512 { + // The entries are u16be, so the length must be even. + // At most 256 entries must be present + return {}, false; + } + + ok = true; + data := mem.slice_data_cast([]u16be, c.data); + i := 0; + for len(data) > 0 { + // HIST entries are u16be, we unpack them to machine format + res.entries[i] = u16(data[0]); + i += 1; data = data[1:]; + } + res.used = u16(i); + return; +} + +chrm :: proc(c: Chunk) -> (res: cHRM, ok: bool) { + ok = true; + if c.header.length != size_of(cHRM_Raw) { + return {}, false; + } + chrm := (^cHRM_Raw)(raw_data(c.data))^; + + res.w.x = f32(chrm.w.x) / 100_000.0; + res.w.y = f32(chrm.w.y) / 100_000.0; + res.r.x = f32(chrm.r.x) / 100_000.0; + res.r.y = f32(chrm.r.y) / 100_000.0; + res.g.x = f32(chrm.g.x) / 100_000.0; + res.g.y = f32(chrm.g.y) / 100_000.0; + res.b.x = f32(chrm.b.x) / 100_000.0; + res.b.y = f32(chrm.b.y) / 100_000.0; + return; +} + +exif :: proc(c: Chunk) -> (res: Exif, ok: bool) { + + ok = true; + + if len(c.data) < 4 { + ok = false; return; + } + + if c.data[0] == 'M' && c.data[1] == 'M' { + res.byte_order = .big_endian; + if c.data[2] != 0 || c.data[3] != 42 { + ok = false; return; + } + } else if c.data[0] == 'I' && c.data[1] == 'I' { + res.byte_order = .little_endian; + if c.data[2] != 42 || c.data[3] != 0 { + ok = false; return; + } + } else { + ok = false; return; + } + + res.data = c.data; + return; +} + +/* + General helper functions +*/ + +compute_buffer_size :: image.compute_buffer_size; + +/* + PNG save helpers +*/ + +when false { + + make_chunk :: proc(c: any, t: Chunk_Type) -> (res: Chunk) { + + data: []u8; + if v, ok := c.([]u8); ok { + data = v; + } else { + data = mem.any_to_bytes(c); + } + + res.header.length = u32be(len(data)); + res.header.type = t; + res.data = data; + + // CRC the type + crc := hash.crc32(mem.any_to_bytes(res.header.type)); + // Extend the CRC with the data + res.crc = u32be(hash.crc32(data, crc)); + return; + } + + write_chunk :: proc(fd: os.Handle, chunk: Chunk) { + c := chunk; + // Write length + type + os.write_ptr(fd, &c.header, 8); + // Write data + os.write_ptr(fd, mem.raw_data(c.data), int(c.header.length)); + // Write CRC32 + os.write_ptr(fd, &c.crc, 4); + } + + write_image_as_png :: proc(filename: string, image: Image) -> (err: Error) { + profiler.timed_proc(); + using image; + using os; + flags: int = O_WRONLY|O_CREATE|O_TRUNC; + + if len(image.pixels) == 0 || len(image.pixels) < image.width * image.height * int(image.channels) { + return E_PNG.Invalid_Image_Dimensions; + } + + mode: int = 0; + when ODIN_OS == "linux" || ODIN_OS == "darwin" { + // NOTE(justasd): 644 (owner read, write; group read; others read) + mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + } + + fd, fderr := open(filename, flags, mode); + if fderr != 0 { + return E_General.Cannot_Open_File; + } + defer close(fd); + + magic := Signature; + + write_ptr(fd, &magic, 8); + + ihdr := IHDR{ + width = u32be(width), + height = u32be(height), + bit_depth = depth, + compression_method = 0, + filter_method = 0, + interlace_method = .None, + }; + + if channels == 1 { + ihdr.color_type = Color_Type{}; + } else if channels == 2 { + ihdr.color_type = Color_Type{.Alpha}; + } else if channels == 3 { + ihdr.color_type = Color_Type{.Color}; + } else if channels == 4 { + ihdr.color_type = Color_Type{.Color, .Alpha}; + } else { + // Unhandled + return E_PNG.Unknown_Color_Type; + } + + h := make_chunk(ihdr, .IHDR); + write_chunk(fd, h); + + bytes_needed := width * height * int(channels) + height; + filter_bytes := mem.make_dynamic_array_len_cap([dynamic]u8, bytes_needed, bytes_needed, context.allocator); + defer delete(filter_bytes); + + i := 0; j := 0; + // Add a filter byte 0 per pixel row + for y := 0; y < height; y += 1 { + filter_bytes[j] = 0; j += 1; + for x := 0; x < width; x += 1 { + for z := 0; z < channels; z += 1 { + filter_bytes[j+z] = image.pixels[i+z]; + } + i += channels; j += channels; + } + } + assert(j == bytes_needed); + + a: []u8 = filter_bytes[:]; + + out_buf: ^[dynamic]u8; + defer free(out_buf); + + ctx := zlib.ZLIB_Context{ + in_buf = &a, + out_buf = out_buf, + }; + err = zlib.write_zlib_stream_from_memory(&ctx); + + b: []u8; + if is_kind(err, E_General, E_General.OK) { + b = ctx.out_buf[:]; + } else { + return err; + } + + idat := make_chunk(b, .IDAT); + + write_chunk(fd, idat); + + iend := make_chunk([]u8{}, .IEND); + write_chunk(fd, iend); + + return E_General.OK; + } +} \ No newline at end of file diff --git a/core/image/png/png.odin b/core/image/png/png.odin new file mode 100644 index 000000000..a25fed1ec --- /dev/null +++ b/core/image/png/png.odin @@ -0,0 +1,1590 @@ +package png + +import "core:compress" +import "core:compress/zlib" +import "core:image" + +import "core:os" +import "core:strings" +import "core:hash" +import "core:bytes" +import "core:io" +import "core:mem" +import "core:intrinsics" + +Error :: compress.Error; +E_General :: compress.General_Error; +E_PNG :: image.PNG_Error; +E_Deflate :: compress.Deflate_Error; +is_kind :: compress.is_kind; + +Image :: image.Image; +Options :: image.Options; + +Signature :: enum u64be { + // 0x89504e470d0a1a0a + PNG = 0x89 << 56 | 'P' << 48 | 'N' << 40 | 'G' << 32 | '\r' << 24 | '\n' << 16 | 0x1a << 8 | '\n', +} + +Info :: struct { + header: IHDR, + chunks: [dynamic]Chunk, +} + +Chunk_Header :: struct #packed { + length: u32be, + type: Chunk_Type, +} + +Chunk :: struct #packed { + header: Chunk_Header, + data: []byte, + crc: u32be, +} + +Chunk_Type :: enum u32be { + // IHDR must come first in a file + IHDR = 'I' << 24 | 'H' << 16 | 'D' << 8 | 'R', + // PLTE must precede the first IDAT chunk + PLTE = 'P' << 24 | 'L' << 16 | 'T' << 8 | 'E', + bKGD = 'b' << 24 | 'K' << 16 | 'G' << 8 | 'D', + tRNS = 't' << 24 | 'R' << 16 | 'N' << 8 | 'S', + IDAT = 'I' << 24 | 'D' << 16 | 'A' << 8 | 'T', + + iTXt = 'i' << 24 | 'T' << 16 | 'X' << 8 | 't', + tEXt = 't' << 24 | 'E' << 16 | 'X' << 8 | 't', + zTXt = 'z' << 24 | 'T' << 16 | 'X' << 8 | 't', + + iCCP = 'i' << 24 | 'C' << 16 | 'C' << 8 | 'P', + pHYs = 'p' << 24 | 'H' << 16 | 'Y' << 8 | 's', + gAMA = 'g' << 24 | 'A' << 16 | 'M' << 8 | 'A', + tIME = 't' << 24 | 'I' << 16 | 'M' << 8 | 'E', + + sPLT = 's' << 24 | 'P' << 16 | 'L' << 8 | 'T', + sRGB = 's' << 24 | 'R' << 16 | 'G' << 8 | 'B', + hIST = 'h' << 24 | 'I' << 16 | 'S' << 8 | 'T', + cHRM = 'c' << 24 | 'H' << 16 | 'R' << 8 | 'M', + sBIT = 's' << 24 | 'B' << 16 | 'I' << 8 | 'T', + + /* + eXIf tags are not part of the core spec, but have been ratified + in v1.5.0 of the PNG Ext register. + + We will provide unprocessed chunks to the caller if `.return_metadata` is set. + Applications are free to implement an Exif decoder. + */ + eXIf = 'e' << 24 | 'X' << 16 | 'I' << 8 | 'f', + + // PNG files must end with IEND + IEND = 'I' << 24 | 'E' << 16 | 'N' << 8 | 'D', + + /* + XCode sometimes produces "PNG" files that don't adhere to the PNG spec. + We recognize them only in order to avoid doing further work on them. + + Some tools like PNG Defry may be able to repair them, but we're not + going to reward Apple for producing proprietary broken files purporting + to be PNGs by supporting them. + + */ + iDOT = 'i' << 24 | 'D' << 16 | 'O' << 8 | 'T', + CbGI = 'C' << 24 | 'b' << 16 | 'H' << 8 | 'I', +} + +IHDR :: struct #packed { + width: u32be, + height: u32be, + bit_depth: u8, + color_type: Color_Type, + compression_method: u8, + filter_method: u8, + interlace_method: Interlace_Method, +} +IHDR_SIZE :: size_of(IHDR); +#assert (IHDR_SIZE == 13); + +Color_Value :: enum u8 { + Paletted = 0, // 1 << 0 = 1 + Color = 1, // 1 << 1 = 2 + Alpha = 2, // 1 << 2 = 4 +} +Color_Type :: distinct bit_set[Color_Value; u8]; + +Interlace_Method :: enum u8 { + None = 0, + Adam7 = 1, +} + +Row_Filter :: enum u8 { + None = 0, + Sub = 1, + Up = 2, + Average = 3, + Paeth = 4, +}; + +PLTE_Entry :: [3]u8; + +PLTE :: struct #packed { + entries: [256]PLTE_Entry, + used: u16, +} + +hIST :: struct #packed { + entries: [256]u16, + used: u16, +} + +sPLT :: struct #packed { + name: string, + depth: u8, + entries: union { + [][4]u8, + [][4]u16, + }, + used: u16, +} + +// Other chunks +tIME :: struct #packed { + year: u16be, + month: u8, + day: u8, + hour: u8, + minute: u8, + second: u8, +}; +#assert(size_of(tIME) == 7); + +CIE_1931_Raw :: struct #packed { + x: u32be, + y: u32be, +} + +CIE_1931 :: struct #packed { + x: f32, + y: f32, +} + +cHRM_Raw :: struct #packed { + w: CIE_1931_Raw, + r: CIE_1931_Raw, + g: CIE_1931_Raw, + b: CIE_1931_Raw, +} +#assert(size_of(cHRM_Raw) == 32); + +cHRM :: struct #packed { + w: CIE_1931, + r: CIE_1931, + g: CIE_1931, + b: CIE_1931, +} +#assert(size_of(cHRM) == 32); + +gAMA :: struct { + gamma_100k: u32be, // Gamma * 100k +}; +#assert(size_of(gAMA) == 4); + +pHYs :: struct #packed { + ppu_x: u32be, + ppu_y: u32be, + unit: pHYs_Unit, +}; +#assert(size_of(pHYs) == 9); + +pHYs_Unit :: enum u8 { + Unknown = 0, + Meter = 1, +}; + +Text :: struct { + keyword: string, + keyword_localized: string, + language: string, + text: string, +}; + +Exif :: struct { + byte_order: enum { + little_endian, + big_endian, + }, + data: []u8, +} + +iCCP :: struct { + name: string, + profile: []u8, +} + +sRGB_Rendering_Intent :: enum u8 { + Perceptual = 0, + Relative_colorimetric = 1, + Saturation = 2, + Absolute_colorimetric = 3, +} + +sRGB :: struct #packed { + intent: sRGB_Rendering_Intent, +} + +ADAM7_X_ORIG := []int{ 0,4,0,2,0,1,0 }; +ADAM7_Y_ORIG := []int{ 0,0,4,0,2,0,1 }; +ADAM7_X_SPACING := []int{ 8,8,4,4,2,2,1 }; +ADAM7_Y_SPACING := []int{ 8,8,8,4,4,2,2 }; + +// Implementation starts here + +read_chunk :: proc(ctx: ^compress.Context) -> (Chunk, Error) { + + chunk := Chunk{}; + + ch, e := compress.read_data(ctx, Chunk_Header); + if e != .None { + return {}, E_General.Stream_Too_Short; + } + chunk.header = ch; + + data := make([]u8, ch.length, context.temp_allocator); + _, e2 := ctx.input->impl_read(data); + if e2 != .None { + return {}, E_General.Stream_Too_Short; + } + chunk.data = data; + + // Compute CRC over chunk type + data + type := (^[4]byte)(&ch.type)^; + computed_crc := hash.crc32(type[:]); + computed_crc = hash.crc32(data, computed_crc); + + crc, e3 := compress.read_data(ctx, u32be); + if e3 != .None { + return {}, E_General.Stream_Too_Short; + } + chunk.crc = crc; + + if chunk.crc != u32be(computed_crc) { + return {}, E_General.Checksum_Failed; + } + return chunk, E_General.OK; +} + +read_header :: proc(ctx: ^compress.Context) -> (IHDR, Error) { + + c, e := read_chunk(ctx); + if !is_kind(e, E_General.OK) { + return {}, e; + } + + header := (^IHDR)(raw_data(c.data))^; + // Validate IHDR + using header; + if width == 0 || height == 0 { + return {}, E_PNG.Invalid_Image_Dimensions; + } + + if compression_method != 0 { + return {}, E_General.Unknown_Compression_Method; + } + + if filter_method != 0 { + return {}, E_PNG.Unknown_Filter_Method; + } + + if interlace_method != .None && interlace_method != .Adam7 { + return {}, E_PNG.Unknown_Interlace_Method; + + } + + switch (transmute(u8)color_type) { + case 0: + /* + Grayscale. + Allowed bit depths: 1, 2, 4, 8 and 16. + */ + allowed := false; + for i in ([]u8{1, 2, 4, 8, 16}) { + if bit_depth == i { + allowed = true; + break; + } + } + if !allowed { + return {}, E_PNG.Invalid_Color_Bit_Depth_Combo; + } + case 2, 4, 6: + /* + RGB, Grayscale+Alpha, RGBA. + Allowed bit depths: 8 and 16 + */ + if bit_depth != 8 && bit_depth != 16 { + return {}, E_PNG.Invalid_Color_Bit_Depth_Combo; + } + case 3: + /* + Paletted. PLTE chunk must appear. + Allowed bit depths: 1, 2, 4 and 8. + */ + allowed := false; + for i in ([]u8{1, 2, 4, 8}) { + if bit_depth == i { + allowed = true; + break; + } + } + if !allowed { + return {}, E_PNG.Invalid_Color_Bit_Depth_Combo; + } + + case: + return {}, E_PNG.Unknown_Color_Type; + } + + return header, E_General.OK; +} + +chunk_type_to_name :: proc(type: ^Chunk_Type) -> string { + t := transmute(^u8)type; + return strings.string_from_ptr(t, 4); +} + +load_from_slice :: proc(slice: ^[]u8, options: Options = {}, allocator := context.allocator) -> (img: ^Image, err: Error) { + r := bytes.Reader{}; + bytes.reader_init(&r, slice^); + stream := bytes.reader_to_stream(&r); + + /* + TODO: Add a flag to tell the PNG loader that the stream is backed by a slice. + This way the stream reader could avoid the copy into the temp memory returned by it, + and instead return a slice into the original memory that's already owned by the caller. + */ + img, err = load_from_stream(&stream, options, allocator); + + return img, err; +} + +load_from_file :: proc(filename: string, options: Options = {}, allocator := context.allocator) -> (img: ^Image, err: Error) { + load_file :: proc(filename: string) -> (res: []u8, ok: bool) { + return os.read_entire_file(filename, context.temp_allocator); + } + + data, ok := load_file(filename); + if ok { + img, err = load_from_slice(&data, options, allocator); + return; + } else { + img = new(Image); + return img, E_General.File_Not_Found; + } +} + +load_from_stream :: proc(stream: ^io.Stream, options: Options = {}, allocator := context.allocator) -> (img: ^Image, err: Error) { + options := options; + if .info in options { + options |= {.return_metadata, .do_not_decompress_image}; + options ~= {.info}; + } + + if .alpha_drop_if_present in options && .alpha_add_if_missing in options { + return {}, E_General.Incompatible_Options; + } + + if img == nil { + img = new(Image); + } + + img.sidecar = nil; + + ctx := compress.Context{ + input = stream^, + }; + + signature, io_error := compress.read_data(&ctx, Signature); + if io_error != .None || signature != .PNG { + return img, E_PNG.Invalid_PNG_Signature; + } + + idat: []u8; + idat_b: bytes.Buffer; + idat_length := u32be(0); + defer bytes.buffer_destroy(&idat_b); + + c: Chunk; + ch: Chunk_Header; + e: io.Error; + + header: IHDR; + info: Info; + info.chunks.allocator = context.temp_allocator; + + // State to ensure correct chunk ordering. + seen_ihdr := false; first := true; + seen_plte := false; + seen_bkgd := false; + seen_trns := false; + seen_idat := false; + seen_iend := false; + + _plte := PLTE{}; + trns := Chunk{}; + + final_image_channels := 0; + + read_error: io.Error; + // 12 bytes is the size of a chunk with a zero-length payload. + for (read_error == .None && !seen_iend) { + // Peek at next chunk's length and type. + // TODO: Some streams may not provide seek/read_at + + ch, e = compress.peek_data(&ctx, Chunk_Header); + if e != .None { + return img, E_General.Stream_Too_Short; + } + // name := chunk_type_to_name(&ch.type); // Only used for debug prints during development. + + #partial switch(ch.type) { + case .IHDR: + if seen_ihdr || !first { + return {}, E_PNG.IHDR_Not_First_Chunk; + } + seen_ihdr = true; + + header, err = read_header(&ctx); + if !is_kind(err, E_General.OK) { + return img, err; + } + + if .Paletted in header.color_type { + // Color type 3 + img.channels = 1; + final_image_channels = 3; + img.depth = 8; + } else if .Color in header.color_type { + // Color image without a palette + img.channels = 3; + final_image_channels = 3; + img.depth = header.bit_depth; + } else { + // Grayscale + img.channels = 1; + final_image_channels = 1; + img.depth = header.bit_depth; + } + + if .Alpha in header.color_type { + img.channels += 1; + final_image_channels += 1; + } + + if img.channels == 0 || img.depth == 0 { + return {}, E_PNG.IHDR_Corrupt; + } + + img.width = int(header.width); + img.height = int(header.height); + + using header; + h := IHDR{ + width = width, + height = height, + bit_depth = bit_depth, + color_type = color_type, + compression_method = compression_method, + filter_method = filter_method, + interlace_method = interlace_method, + }; + info.header = h; + case .PLTE: + seen_plte = true; + // PLTE must appear before IDAT and can't appear for color types 0, 4. + ct := transmute(u8)info.header.color_type; + if seen_idat || ct == 0 || ct == 4 { + return img, E_PNG.PLTE_Encountered_Unexpectedly; + } + + c, err = read_chunk(&ctx); + if !is_kind(err, E_General.OK) { + return img, err; + } + + if c.header.length % 3 != 0 || c.header.length > 768 { + return img, E_PNG.PLTE_Invalid_Length; + } + plte_ok: bool; + _plte, plte_ok = plte(c); + if !plte_ok { + return img, E_PNG.PLTE_Invalid_Length; + } + + if .return_metadata in options { + append(&info.chunks, c); + } + case .IDAT: + // If we only want image metadata and don't want the pixel data, we can early out. + if .return_metadata not_in options && .do_not_decompress_image in options { + img.channels = final_image_channels; + img.sidecar = info; + return img, E_General.OK; + } + // There must be at least 1 IDAT, contiguous if more. + if seen_idat { + return img, E_PNG.IDAT_Must_Be_Contiguous; + } + + if idat_length > 0 { + return img, E_PNG.IDAT_Must_Be_Contiguous; + } + + next := ch.type; + for next == .IDAT { + c, err = read_chunk(&ctx); + if !is_kind(err, E_General.OK) { + return img, err; + } + + bytes.buffer_write(&idat_b, c.data); + idat_length += c.header.length; + + ch, e = compress.peek_data(&ctx, Chunk_Header); + if e != .None { + return img, E_General.Stream_Too_Short; + } + next = ch.type; + } + idat = bytes.buffer_to_bytes(&idat_b); + if int(idat_length) != len(idat) { + return {}, E_PNG.IDAT_Corrupt; + } + seen_idat = true; + case .IEND: + c, err = read_chunk(&ctx); + if !is_kind(err, E_General.OK) { + return img, err; + } + seen_iend = true; + case .bKGD: + + // TODO: Make sure that 16-bit bKGD + tRNS chunks return u16 instead of u16be + + c, err = read_chunk(&ctx); + if !is_kind(err, E_General.OK) { + return img, err; + } + seen_bkgd = true; + if .return_metadata in options { + append(&info.chunks, c); + } + + ct := transmute(u8)info.header.color_type; + switch(ct) { + case 3: // Indexed color + if c.header.length != 1 { + return {}, E_PNG.BKGD_Invalid_Length; + } + col := _plte.entries[c.data[0]]; + img.background = [3]u16{ + u16(col[0]) << 8 | u16(col[0]), + u16(col[1]) << 8 | u16(col[1]), + u16(col[2]) << 8 | u16(col[2]), + }; + case 0, 4: // Grayscale, with and without Alpha + if c.header.length != 2 { + return {}, E_PNG.BKGD_Invalid_Length; + } + col := u16(mem.slice_data_cast([]u16be, c.data[:])[0]); + img.background = [3]u16{col, col, col}; + case 2, 6: // Color, with and without Alpha + if c.header.length != 6 { + return {}, E_PNG.BKGD_Invalid_Length; + } + col := mem.slice_data_cast([]u16be, c.data[:]); + img.background = [3]u16{u16(col[0]), u16(col[1]), u16(col[2])}; + } + case .tRNS: + c, err = read_chunk(&ctx); + if !is_kind(err, E_General.OK) { + return img, err; + } + + if .Alpha in info.header.color_type { + return img, E_PNG.TRNS_Encountered_Unexpectedly; + } + + if .return_metadata in options { + append(&info.chunks, c); + } + + /* + This makes the image one with transparency, so set it to +1 here, + even if we need we leave img.channels alone for the defilterer's + sake. If we early because the user just cares about metadata, + we'll set it to 'final_image_channels'. + */ + + final_image_channels += 1; + + seen_trns = true; + if info.header.bit_depth < 8 && .Paletted not_in info.header.color_type { + // Rescale tRNS data so key matches intensity + dsc := depth_scale_table; + scale := dsc[info.header.bit_depth]; + if scale != 1 { + key := mem.slice_data_cast([]u16be, c.data)[0] * u16be(scale); + c.data = []u8{0, u8(key & 255)}; + } + } + trns = c; + case .iDOT, .CbGI: + /* + iPhone PNG bastardization that doesn't adhere to spec with broken IDAT chunk. + We're not going to add support for it. If you have the misfortunte of coming + across one of these files, use a utility to defry it.s + */ + return img, E_PNG.PNG_Does_Not_Adhere_to_Spec; + case: + // Unhandled type + c, err = read_chunk(&ctx); + if !is_kind(err, E_General.OK) { + return img, err; + } + if .return_metadata in options { + // NOTE: Chunk cata is currently allocated on the temp allocator. + append(&info.chunks, c); + } + + first = false; + } + } + + if .return_header in options || .return_metadata in options { + img.sidecar = info; + } + if .do_not_decompress_image in options { + img.channels = final_image_channels; + return img, E_General.OK; + } + + if !seen_idat { + return img, E_PNG.IDAT_Missing; + } + + buf: bytes.Buffer; + zlib_error := zlib.inflate(&idat, &buf); + defer bytes.buffer_destroy(&buf); + + if !is_kind(zlib_error, E_General.OK) { + return {}, zlib_error; + } else { + /* + Let's calcalate the expected size of the IDAT based on its dimensions, + and whether or not it's interlaced + */ + expected_size: int; + buf_len := len(buf.buf); + + if header.interlace_method != .Adam7 { + expected_size = compute_buffer_size(int(header.width), int(header.height), int(img.channels), int(header.bit_depth), 1); + } else { + /* + Because Adam7 divides the image up into sub-images, and each scanline must start + with a filter byte, Adam7 interlaced images can have a larger raw size. + */ + for p := 0; p < 7; p += 1 { + x := (int(header.width) - ADAM7_X_ORIG[p] + ADAM7_X_SPACING[p] - 1) / ADAM7_X_SPACING[p]; + y := (int(header.height) - ADAM7_Y_ORIG[p] + ADAM7_Y_SPACING[p] - 1) / ADAM7_Y_SPACING[p]; + if (x > 0 && y > 0) { + expected_size += compute_buffer_size(int(x), int(y), int(img.channels), int(header.bit_depth), 1); + } + } + } + + if expected_size != buf_len { + return {}, E_PNG.IDAT_Corrupt; + } + } + + /* + Defilter just cares about the raw number of image channels present. + So, we'll save the old value of img.channels we return to the user + as metadata, and set it instead to the raw number of channels. + */ + defilter_error := defilter(img, &buf, &header, options); + if !is_kind(defilter_error, E_General.OK) { + bytes.buffer_destroy(&img.pixels); + return {}, defilter_error; + } + + /* + Now we'll handle the relocoring of paletted images, handling of tRNS chunks, + and we'll expand grayscale images to RGB(A). + + For the sake of convenience we return only RGB(A) images. In the future we + may supply an option to return Gray/Gray+Alpha as-is, in which case RGB(A) + will become the default. + */ + + raw_image_channels := img.channels; + out_image_channels := 3; + + /* + To give ourselves less options to test, we'll knock out + `.blend_background` and `seen_bkgd` if we haven't seen both. + */ + if !(seen_bkgd && .blend_background in options) { + options ~= {.blend_background}; + seen_bkgd = false; + } + + if seen_trns || .Alpha in info.header.color_type || .alpha_add_if_missing in options { + out_image_channels = 4; + } + + if .alpha_drop_if_present in options { + out_image_channels = 3; + } + + if seen_bkgd && .blend_background in options && .alpha_add_if_missing not_in options { + out_image_channels = 3; + } + + add_alpha := (seen_trns && .alpha_drop_if_present not_in options) || (.alpha_add_if_missing in options); + premultiply := .alpha_premultiply in options || .blend_background in options; + + img.channels = out_image_channels; + + if .Paletted in header.color_type { + temp := img.pixels; + defer bytes.buffer_destroy(&temp); + + // We need to create a new image buffer + dest_raw_size := compute_buffer_size(int(header.width), int(header.height), out_image_channels, 8); + t := bytes.Buffer{}; + resize(&t.buf, dest_raw_size); + + i := 0; j := 0; + + // If we don't have transparency or drop it without applying it, we can do this: + if (!seen_trns || (seen_trns && .alpha_drop_if_present in options && .alpha_premultiply not_in options)) && .alpha_add_if_missing not_in options { + for h := 0; h < int(img.height); h += 1 { + for w := 0; w < int(img.width); w += 1 { + c := _plte.entries[temp.buf[i]]; + t.buf[j ] = c.r; + t.buf[j+1] = c.g; + t.buf[j+2] = c.b; + i += 1; j += 3; + } + } + } else if add_alpha || .alpha_drop_if_present in options { + bg := [3]f32{0, 0, 0}; + if premultiply && seen_bkgd { + c16 := img.background.([3]u16); + bg = [3]f32{f32(c16.r), f32(c16.g), f32(c16.b)}; + } + + no_alpha := (.alpha_drop_if_present in options || premultiply) && .alpha_add_if_missing not_in options; + blend_background := seen_bkgd && .blend_background in options; + + for h := 0; h < int(img.height); h += 1 { + for w := 0; w < int(img.width); w += 1 { + index := temp.buf[i]; + + c := _plte.entries[index]; + a := int(index) < len(trns.data) ? trns.data[index] : 255; + alpha := f32(a) / 255.0; + + if blend_background { + c.r = u8((1.0 - alpha) * bg[0] + f32(c.r) * alpha); + c.g = u8((1.0 - alpha) * bg[1] + f32(c.g) * alpha); + c.b = u8((1.0 - alpha) * bg[2] + f32(c.b) * alpha); + a = 255; + } else if premultiply { + c.r = u8(f32(c.r) * alpha); + c.g = u8(f32(c.g) * alpha); + c.b = u8(f32(c.b) * alpha); + } + + t.buf[j ] = c.r; + t.buf[j+1] = c.g; + t.buf[j+2] = c.b; + i += 1; + + if no_alpha { + j += 3; + } else { + t.buf[j+3] = u8(a); + j += 4; + } + } + } + } else { + // This should be impossible. + assert(false); + } + + img.pixels = t; + + } else if img.depth == 16 { + // Check if we need to do something. + if raw_image_channels == out_image_channels { + // If we have 3 in and 3 out, or 4 in and 4 out without premultiplication... + if raw_image_channels == 4 && .alpha_premultiply not_in options && !seen_bkgd { + // Then we're done. + return img, E_General.OK; + } + } + + temp := img.pixels; + defer bytes.buffer_destroy(&temp); + + // We need to create a new image buffer + dest_raw_size := compute_buffer_size(int(header.width), int(header.height), out_image_channels, 16); + t := bytes.Buffer{}; + resize(&t.buf, dest_raw_size); + + p16 := mem.slice_data_cast([]u16, temp.buf[:]); + o16 := mem.slice_data_cast([]u16, t.buf[:]); + + switch (raw_image_channels) { + case 1: + // Gray without Alpha. Might have tRNS alpha. + key := u16(0); + if seen_trns { + key = mem.slice_data_cast([]u16, trns.data)[0]; + } + + for len(p16) > 0 { + r := p16[0]; + + alpha := u16(1); // Default to full opaque + + if seen_trns { + if r == key { + if seen_bkgd { + c := img.background.([3]u16); + r = c[0]; + } else { + alpha = 0; // Keyed transparency + } + } + } + + if premultiply { + o16[0] = r * alpha; + o16[1] = r * alpha; + o16[2] = r * alpha; + } else { + o16[0] = r; + o16[1] = r; + o16[2] = r; + } + + if out_image_channels == 4 { + o16[3] = alpha * 65535; + } + + p16 = p16[1:]; + o16 = o16[out_image_channels:]; + } + case 2: + // Gray with alpha, we shouldn't have a tRNS chunk. + for len(p16) > 0 { + r := p16[0]; + if premultiply { + alpha := p16[1]; + c := u16(f32(r) * f32(alpha) / f32(65535)); + o16[0] = c; + o16[1] = c; + o16[2] = c; + } else { + o16[0] = r; + o16[1] = r; + o16[2] = r; + } + + if .alpha_drop_if_present not_in options { + o16[3] = p16[1]; + } + + p16 = p16[2:]; + o16 = o16[out_image_channels:]; + } + case 3: + /* + Color without Alpha. + We may still have a tRNS chunk or `.alpha_add_if_missing`. + */ + + key: []u16; + if seen_trns { + key = mem.slice_data_cast([]u16, trns.data); + } + + for len(p16) > 0 { + r := p16[0]; + g := p16[1]; + b := p16[2]; + + alpha := u16(1); // Default to full opaque + + if seen_trns { + if r == key[0] && g == key[1] && b == key[2] { + if seen_bkgd { + c := img.background.([3]u16); + r = c[0]; + g = c[1]; + b = c[2]; + } else { + alpha = 0; // Keyed transparency + } + } + } + + if premultiply { + o16[0] = r * alpha; + o16[1] = g * alpha; + o16[2] = b * alpha; + } else { + o16[0] = r; + o16[1] = g; + o16[2] = b; + } + + if out_image_channels == 4 { + o16[3] = alpha * 65535; + } + + p16 = p16[3:]; + o16 = o16[out_image_channels:]; + } + case 4: + // Color with Alpha, can't have tRNS. + for len(p16) > 0 { + r := p16[0]; + g := p16[1]; + b := p16[2]; + a := p16[3]; + + if premultiply { + alpha := f32(a) / 65535.0; + o16[0] = u16(f32(r) * alpha); + o16[1] = u16(f32(g) * alpha); + o16[2] = u16(f32(b) * alpha); + } else { + o16[0] = r; + o16[1] = g; + o16[2] = b; + } + + if .alpha_drop_if_present not_in options { + o16[3] = a; + } + + p16 = p16[4:]; + o16 = o16[out_image_channels:]; + } + case: + unreachable("We should never seen # channels other than 1-4 inclusive."); + } + + img.pixels = t; + img.channels = out_image_channels; + + } else if img.depth == 8 { + // Check if we need to do something. + if raw_image_channels == out_image_channels { + // If we have 3 in and 3 out, or 4 in and 4 out without premultiplication... + if raw_image_channels == 4 && .alpha_premultiply not_in options { + // Then we're done. + return img, E_General.OK; + } + } + + temp := img.pixels; + defer bytes.buffer_destroy(&temp); + + // We need to create a new image buffer + dest_raw_size := compute_buffer_size(int(header.width), int(header.height), out_image_channels, 8); + t := bytes.Buffer{}; + resize(&t.buf, dest_raw_size); + + p := mem.slice_data_cast([]u8, temp.buf[:]); + o := mem.slice_data_cast([]u8, t.buf[:]); + + switch (raw_image_channels) { + case 1: + // Gray without Alpha. Might have tRNS alpha. + key := u8(0); + if seen_trns { + key = u8(mem.slice_data_cast([]u16be, trns.data)[0]); + } + + for len(p) > 0 { + r := p[0]; + alpha := u8(1); + + if seen_trns { + if r == key { + if seen_bkgd { + c := img.background.([3]u16); + r = u8(c[0]); + } else { + alpha = 0; // Keyed transparency + } + } + if premultiply { + o[0] = r * alpha; + o[1] = r * alpha; + o[2] = r * alpha; + } + } else { + o[0] = r; + o[1] = r; + o[2] = r; + } + + if out_image_channels == 4 { + o[3] = alpha * 255; + } + + p = p[1:]; + o = o[out_image_channels:]; + } + case 2: + // Gray with alpha, we shouldn't have a tRNS chunk. + for len(p) > 0 { + r := p[0]; + if .alpha_premultiply in options { + alpha := p[1]; + c := u8(f32(r) * f32(alpha) / f32(255)); + o[0] = c; + o[1] = c; + o[2] = c; + } else { + o[0] = r; + o[1] = r; + o[2] = r; + } + + if .alpha_drop_if_present not_in options { + o[3] = p[1]; + } + + p = p[2:]; + o = o[out_image_channels:]; + } + case 3: + // Color without Alpha. We may still have a tRNS chunk + key: []u8; + if seen_trns { + /* + For 8-bit images, the tRNS chunk still contains a triple in u16be. + We use only the low byte in this case. + */ + key = []u8{trns.data[1], trns.data[3], trns.data[5]}; + } + for len(p) > 0 { + r := p[0]; + g := p[1]; + b := p[2]; + + alpha := u8(1); // Default to full opaque + + // TODO: Combine the seen_trns cases. + if seen_trns { + if r == key[0] && g == key[1] && b == key[2] { + if seen_bkgd { + c := img.background.([3]u16); + r = u8(c[0]); + g = u8(c[1]); + b = u8(c[2]); + } else { + alpha = 0; // Keyed transparency + } + } + + if .alpha_premultiply in options || .blend_background in options { + o[0] = r * alpha; + o[1] = g * alpha; + o[2] = b * alpha; + } + } else { + o[0] = r; + o[1] = g; + o[2] = b; + } + + if out_image_channels == 4 { + o[3] = alpha * 255; + } + + p = p[3:]; + o = o[out_image_channels:]; + } + case 4: + // Color with Alpha, can't have tRNS. + for len(p) > 0 { + r := p[0]; + g := p[1]; + b := p[2]; + a := p[3]; + + if .alpha_premultiply in options { + alpha := f32(a) / 255.0; + o[0] = u8(f32(r) * alpha); + o[1] = u8(f32(g) * alpha); + o[2] = u8(f32(b) * alpha); + } else { + o[0] = r; + o[1] = g; + o[2] = b; + } + + if .alpha_drop_if_present not_in options { + o[3] = a; + } + + p = p[4:]; + o = o[out_image_channels:]; + } + case: + unreachable("We should never seen # channels other than 1-4 inclusive."); + } + + img.pixels = t; + img.channels = out_image_channels; + + } else { + /* + This may change if we ever don't expand 1, 2 and 4 bit images. But, those raw + returns will likely bypass this processing pipeline. + */ + unreachable("We should never see bit depths other than 8, 16 and 'Paletted' here."); + } + + return img, E_General.OK; +} + + +filter_paeth :: #force_inline proc(left, up, up_left: u8) -> u8 { + aa, bb, cc := i16(left), i16(up), i16(up_left); + p := aa + bb - cc; + pa := abs(p - aa); + pb := abs(p - bb); + pc := abs(p - cc); + if pa <= pb && pa <= pc { + return left; + } + if pb <= pc { + return up; + } + return up_left; +} + +Filter_Params :: struct #packed { + src : []u8, + dest : []u8, + width : int, + height : int, + depth : int, + channels: int, + rescale : bool, +} + +depth_scale_table :: []u8{0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01}; + +// @(optimization_mode="speed") +defilter_8 :: proc(params: ^Filter_Params) -> (ok: bool) { + + using params; + row_stride := channels * width; + + // TODO: See about doing a Duff's #unroll where practicable + + // Apron so we don't need to special case first rows. + up := make([]u8, row_stride, context.temp_allocator); + ok = true; + + for _ in 0..> 1; + dest[i] = (src[i] + avg) & 255; + } + for k := 0; k < nk; k += 1 { + avg := u8((u16(up[channels+k]) + u16(dest[k])) >> 1); + dest[channels+k] = (src[channels+k] + avg) & 255; + } + case .Paeth: + for i := 0; i < channels; i += 1 { + paeth := filter_paeth(0, up[i], 0); + dest[i] = (src[i] + paeth) & 255; + } + for k := 0; k < nk; k += 1 { + paeth := filter_paeth(dest[k], up[channels+k], up[k]); + dest[channels+k] = (src[channels+k] + paeth) & 255; + } + case: + return false; + } + + src = src[row_stride:]; + up = dest; + dest = dest[row_stride:]; + } + return; +} + +// @(optimization_mode="speed") +defilter_less_than_8 :: proc(params: ^Filter_Params) -> (ok: bool) #no_bounds_check { + + using params; + ok = true; + + row_stride_in := ((channels * width * depth) + 7) >> 3; + row_stride_out := channels * width; + + // Store defiltered bytes rightmost so we can widen in-place. + row_offset := row_stride_out - row_stride_in; + // Save original dest because we'll need it for the bit widening. + orig_dest := dest; + + // TODO: See about doing a Duff's #unroll where practicable + + // Apron so we don't need to special case first rows. + up := make([]u8, row_stride_out, context.temp_allocator); + + #no_bounds_check for _ in 0..> 1; + dest[i] = (src[i] + avg) & 255; + } + for k in 0..nk { + avg := u8((u16(up[channels+k]) + u16(dest[k])) >> 1); + dest[channels+k] = (src[channels+k] + avg) & 255; + } + case .Paeth: + for i in 0..channels { + paeth := filter_paeth(0, up[i], 0); + dest[i] = (src[i] + paeth) & 255; + } + for k in 0..nk { + paeth := filter_paeth(dest[k], up[channels], up[k]); + dest[channels+k] = (src[channels+k] + paeth) & 255; + } + case: + return false; + } + + src = src [row_stride_in:]; + up = dest; + dest = dest[row_stride_in:]; + } + + // Let's expand the bits + dest = orig_dest; + + // Don't rescale the bits if we're a paletted image. + dsc := depth_scale_table; + scale := rescale ? dsc[depth] : 1; + + /* + For sBIT support we should probably set scale to 1 and mask the significant bits. + Seperately, do we want to support packed pixels? i.e defiltering only, no expansion? + If so, all we have to do is call defilter_8 for that case and not set img.depth to 8. + */ + + for j := 0; j < height; j += 1 { + src = dest[row_offset:]; + + if depth == 4 { + k := row_stride_out; + for ; k >= 2; k -= 2 { + c := src[0]; + dest[0] = scale * (c >> 4); + dest[1] = scale * (c & 15); + dest = dest[2:]; src = src[1:]; + } + if k > 0 { + c := src[0]; + dest[0] = scale * (c >> 4); + dest = dest[1:]; + } + } else if depth == 2 { + k := row_stride_out; + for ; k >= 4; k -= 4 { + c := src[0]; + dest[0] = scale * ((c >> 6) ); + dest[1] = scale * ((c >> 4) & 3); + dest[2] = scale * ((c >> 2) & 3); + dest[3] = scale * ((c ) & 3); + dest = dest[4:]; src = src[1:]; + } + if k > 0 { + c := src[0]; + dest[0] = scale * ((c >> 6) ); + if k > 1 { + dest[1] = scale * ((c >> 4) & 3); + } + if k > 2 { + dest[2] = scale * ((c >> 2) & 3); + } + dest = dest[k:]; + } + } else if depth == 1 { + k := row_stride_out; + for ; k >= 8; k -= 8 { + c := src[0]; + dest[0] = scale * ((c >> 7) ); + dest[1] = scale * ((c >> 6) & 1); + dest[2] = scale * ((c >> 5) & 1); + dest[3] = scale * ((c >> 4) & 1); + dest[4] = scale * ((c >> 3) & 1); + dest[5] = scale * ((c >> 2) & 1); + dest[6] = scale * ((c >> 1) & 1); + dest[7] = scale * ((c ) & 1); + dest = dest[8:]; src = src[1:]; + } + if k > 0 { + c := src[0]; + dest[0] = scale * ((c >> 7) ); + if k > 1 { + dest[1] = scale * ((c >> 6) & 1); + } + if k > 2 { + dest[2] = scale * ((c >> 5) & 1); + } + if k > 3 { + dest[3] = scale * ((c >> 4) & 1); + } + if k > 4 { + dest[4] = scale * ((c >> 3) & 1); + } + if k > 5 { + dest[5] = scale * ((c >> 2) & 1); + } + if k > 6 { + dest[6] = scale * ((c >> 1) & 1); + } + dest = dest[k:]; + + } + } + } + + return; +} + +// @(optimization_mode="speed") +defilter_16 :: proc(params: ^Filter_Params) -> (ok: bool) { + + using params; + ok = true; + + stride := channels * 2; + row_stride := width * stride; + + // TODO: See about doing a Duff's #unroll where practicable + // Apron so we don't need to special case first rows. + up := make([]u8, row_stride, context.temp_allocator); + + for y := 0; y < height; y += 1 { + nk := row_stride - stride; + + filter := Row_Filter(src[0]); src = src[1:]; + switch(filter) { + case .None: + copy(dest, src[:row_stride]); + case .Sub: + for i := 0; i < stride; i += 1 { + dest[i] = src[i]; + } + for k := 0; k < nk; k += 1 { + dest[stride+k] = (src[stride+k] + dest[k]) & 255; + } + case .Up: + for k := 0; k < row_stride; k += 1 { + dest[k] = (src[k] + up[k]) & 255; + } + case .Average: + for i := 0; i < stride; i += 1 { + avg := up[i] >> 1; + dest[i] = (src[i] + avg) & 255; + } + for k := 0; k < nk; k += 1 { + avg := u8((u16(up[stride+k]) + u16(dest[k])) >> 1); + dest[stride+k] = (src[stride+k] + avg) & 255; + } + case .Paeth: + for i := 0; i < stride; i += 1 { + paeth := filter_paeth(0, up[i], 0); + dest[i] = (src[i] + paeth) & 255; + } + for k := 0; k < nk; k += 1 { + paeth := filter_paeth(dest[k], up[stride+k], up[k]); + dest[stride+k] = (src[stride+k] + paeth) & 255; + } + case: + return false; + } + + src = src[row_stride:]; + up = dest; + dest = dest[row_stride:]; + } + + return; +} + +defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^IHDR, options: Options) -> (err: compress.Error) { + input := bytes.buffer_to_bytes(filter_bytes); + width := int(header.width); + height := int(header.height); + channels := int(img.channels); + depth := int(header.bit_depth); + rescale := .Color not_in header.color_type; + + bytes_per_channel := depth == 16 ? 2 : 1; + + num_bytes := compute_buffer_size(width, height, channels, depth == 16 ? 16 : 8); + resize(&img.pixels.buf, num_bytes); + + filter_ok: bool; + + if header.interlace_method != .Adam7 { + params := Filter_Params{ + src = input, + width = width, + height = height, + channels = channels, + depth = depth, + rescale = rescale, + dest = img.pixels.buf[:], + }; + + if depth == 8 { + filter_ok = defilter_8(¶ms); + } else if depth < 8 { + filter_ok = defilter_less_than_8(¶ms); + img.depth = 8; + } else { + filter_ok = defilter_16(¶ms); + } + if !filter_ok { + // Caller will destroy buffer for us. + return E_PNG.Unknown_Filter_Method; + } + } else { + /* + For deinterlacing we need to make a temporary buffer, defiilter part of the image, + and copy that back into the actual output buffer. + */ + + for p := 0; p < 7; p += 1 { + i,j,x,y: int; + x = (width - ADAM7_X_ORIG[p] + ADAM7_X_SPACING[p] - 1) / ADAM7_X_SPACING[p]; + y = (height - ADAM7_Y_ORIG[p] + ADAM7_Y_SPACING[p] - 1) / ADAM7_Y_SPACING[p]; + if (x > 0 && y > 0) { + temp: bytes.Buffer; + temp_len := compute_buffer_size(x, y, channels, depth == 16 ? 16 : 8); + resize(&temp.buf, temp_len); + + params := Filter_Params{ + src = input, + width = x, + height = y, + channels = channels, + depth = depth, + rescale = rescale, + dest = temp.buf[:], + }; + + if depth == 8 { + filter_ok = defilter_8(¶ms); + } else if depth < 8 { + filter_ok = defilter_less_than_8(¶ms); + img.depth = 8; + } else { + filter_ok = defilter_16(¶ms); + } + + if !filter_ok { + // Caller will destroy buffer for us. + return E_PNG.Unknown_Filter_Method; + } + + t := temp.buf[:]; + for j = 0; j < y; j += 1 { + for i = 0; i < x; i += 1 { + out_y := j * ADAM7_Y_SPACING[p] + ADAM7_Y_ORIG[p]; + out_x := i * ADAM7_X_SPACING[p] + ADAM7_X_ORIG[p]; + + out_off := out_y * width * channels * bytes_per_channel; + out_off += out_x * channels * bytes_per_channel; + + for z := 0; z < channels * bytes_per_channel; z += 1 { + img.pixels.buf[out_off + z] = t[z]; + } + t = t[channels * bytes_per_channel:]; + } + } + bytes.buffer_destroy(&temp); + input_stride := compute_buffer_size(x, y, channels, depth, 1); + input = input[input_stride:]; + } + } + } + when ODIN_ENDIAN == "little" { + if img.depth == 16 { + // The pixel components are in Big Endian. Let's byteswap. + input := mem.slice_data_cast([]u16be, img.pixels.buf[:]); + output := mem.slice_data_cast([]u16 , img.pixels.buf[:]); + #no_bounds_check for v, i in input { + output[i] = u16(v); + } + } + } + + return E_General.OK; +} + +load :: proc{load_from_file, load_from_slice, load_from_stream}; \ No newline at end of file -- cgit v1.2.3 From 06f1eaa1538279a82b6a24ed7ee17c1dea1e1293 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 30 Apr 2021 09:35:43 +0200 Subject: Use regular allocator from png+gzip's `load_from_file`. I would've used `os.stream_from_handle`, but: - Parts of it seem to be implemented for Windows only at the moment. - PNG's `peek_data` using that stream didn't manage to rewind and thus tried to parse the data after the header as the header. Two things must happen: - The `os.stream_from_handle` implementation needs to be fixed. - PNG and GZIP's parsers need to be able to handle streams that can't rewind or seek (backward). Those fixes are on my TODO list but are exceed the scope of this patch. --- core/compress/gzip/gzip.odin | 4 +++- core/image/png/png.odin | 6 ++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/compress/gzip/gzip.odin b/core/compress/gzip/gzip.odin index 3278d7c1d..457b32bd3 100644 --- a/core/compress/gzip/gzip.odin +++ b/core/compress/gzip/gzip.odin @@ -108,7 +108,9 @@ load_from_slice :: proc(slice: ^[]u8, buf: ^bytes.Buffer, allocator := context.a } load_from_file :: proc(filename: string, buf: ^bytes.Buffer, allocator := context.allocator) -> (err: Error) { - data, ok := os.read_entire_file(filename, context.temp_allocator); + data, ok := os.read_entire_file(filename, allocator); + defer delete(data); + if ok { err = load_from_slice(&data, buf, allocator); return; diff --git a/core/image/png/png.odin b/core/image/png/png.odin index a25fed1ec..5ea9979a8 100644 --- a/core/image/png/png.odin +++ b/core/image/png/png.odin @@ -366,11 +366,9 @@ load_from_slice :: proc(slice: ^[]u8, options: Options = {}, allocator := contex } load_from_file :: proc(filename: string, options: Options = {}, allocator := context.allocator) -> (img: ^Image, err: Error) { - load_file :: proc(filename: string) -> (res: []u8, ok: bool) { - return os.read_entire_file(filename, context.temp_allocator); - } + data, ok := os.read_entire_file(filename, allocator); + defer delete(data); - data, ok := load_file(filename); if ok { img, err = load_from_slice(&data, options, allocator); return; -- cgit v1.2.3 From 5f617c56e17ce33ca9be5cfd051deb640fb8200b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 30 Apr 2021 10:58:29 +0100 Subject: Minor stylistic code changes to compress and image packages --- core/compress/common.odin | 2 +- core/compress/gzip/example.odin | 6 +- core/compress/gzip/gzip.odin | 78 +++-- core/compress/zlib/example.odin | 22 +- core/compress/zlib/zlib.odin | 133 ++++---- core/image/png/example.odin | 4 +- core/image/png/helpers.odin | 187 +++++------ core/image/png/png.odin | 723 ++++++++++++++++++++-------------------- 8 files changed, 575 insertions(+), 580 deletions(-) diff --git a/core/compress/common.odin b/core/compress/common.odin index 99b054903..11b5b74b3 100644 --- a/core/compress/common.odin +++ b/core/compress/common.odin @@ -200,4 +200,4 @@ read_bits_no_refill_lsb :: #force_inline proc(z: ^Context, width: u8) -> u32 { discard_to_next_byte_lsb :: proc(z: ^Context) { discard := u8(z.num_bits & 7); consume_bits_lsb(z, discard); -} \ No newline at end of file +} diff --git a/core/compress/gzip/example.odin b/core/compress/gzip/example.odin index fa0cba9db..8e0f53cf8 100644 --- a/core/compress/gzip/example.odin +++ b/core/compress/gzip/example.odin @@ -34,7 +34,7 @@ main :: proc() { if len(args) < 2 { stderr("No input file specified.\n"); - err := gzip.load(&TEST, &buf); + err := gzip.load(TEST, &buf); if gzip.is_kind(err, gzip.E_General.OK) { stdout("Displaying test vector: "); stdout(bytes.buffer_to_string(&buf)); @@ -50,7 +50,7 @@ main :: proc() { if file == "-" { // Read from stdin s := os.stream_from_handle(os.stdin); - err = gzip.load(&s, &buf); + err = gzip.load(s, &buf); } else { err = gzip.load(file, &buf); } @@ -67,4 +67,4 @@ main :: proc() { stdout(bytes.buffer_to_string(&buf)); } os.exit(0); -} \ No newline at end of file +} diff --git a/core/compress/gzip/gzip.odin b/core/compress/gzip/gzip.odin index 457b32bd3..f86d3ddce 100644 --- a/core/compress/gzip/gzip.odin +++ b/core/compress/gzip/gzip.odin @@ -45,39 +45,39 @@ Header_Flag :: enum u8 { Header_Flags :: distinct bit_set[Header_Flag; u8]; OS :: enum u8 { - FAT = 0, - Amiga = 1, - VMS = 2, - Unix = 3, - VM_CMS = 4, - Atari_TOS = 5, - HPFS = 6, - Macintosh = 7, - Z_System = 8, - CP_M = 9, - TOPS_20 = 10, - NTFS = 11, - QDOS = 12, + FAT = 0, + Amiga = 1, + VMS = 2, + Unix = 3, + VM_CMS = 4, + Atari_TOS = 5, + HPFS = 6, + Macintosh = 7, + Z_System = 8, + CP_M = 9, + TOPS_20 = 10, + NTFS = 11, + QDOS = 12, Acorn_RISCOS = 13, - _Unknown = 14, - Unknown = 255, + _Unknown = 14, + Unknown = 255, } OS_Name :: #partial [OS]string{ - .FAT = "FAT", - .Amiga = "Amiga", - .VMS = "VMS/OpenVMS", - .Unix = "Unix", - .VM_CMS = "VM/CMS", - .Atari_TOS = "Atari TOS", - .HPFS = "HPFS", - .Macintosh = "Macintosh", - .Z_System = "Z-System", - .CP_M = "CP/M", - .TOPS_20 = "TOPS-20", - .NTFS = "NTFS", - .QDOS = "QDOS", + .FAT = "FAT", + .Amiga = "Amiga", + .VMS = "VMS/OpenVMS", + .Unix = "Unix", + .VM_CMS = "VM/CMS", + .Atari_TOS = "Atari TOS", + .HPFS = "HPFS", + .Macintosh = "Macintosh", + .Z_System = "Z-System", + .CP_M = "CP/M", + .TOPS_20 = "TOPS-20", + .NTFS = "NTFS", + .QDOS = "QDOS", .Acorn_RISCOS = "Acorn RISCOS", - .Unknown = "Unknown", + .Unknown = "Unknown", }; Compression :: enum u8 { @@ -96,13 +96,13 @@ E_ZLIB :: compress.ZLIB_Error; E_Deflate :: compress.Deflate_Error; is_kind :: compress.is_kind; -load_from_slice :: proc(slice: ^[]u8, buf: ^bytes.Buffer, allocator := context.allocator) -> (err: Error) { +load_from_slice :: proc(slice: []u8, buf: ^bytes.Buffer, allocator := context.allocator) -> (err: Error) { r := bytes.Reader{}; - bytes.reader_init(&r, slice^); + bytes.reader_init(&r, slice); stream := bytes.reader_to_stream(&r); - err = load_from_stream(&stream, buf, allocator); + err = load_from_stream(stream, buf, allocator); return err; } @@ -111,18 +111,16 @@ load_from_file :: proc(filename: string, buf: ^bytes.Buffer, allocator := contex data, ok := os.read_entire_file(filename, allocator); defer delete(data); + err = E_General.File_Not_Found; if ok { - err = load_from_slice(&data, buf, allocator); - return; - } else { - return E_General.File_Not_Found; + err = load_from_slice(data, buf, allocator); } + return; } -load_from_stream :: proc(stream: ^io.Stream, buf: ^bytes.Buffer, allocator := context.allocator) -> (err: Error) { - +load_from_stream :: proc(stream: io.Stream, buf: ^bytes.Buffer, allocator := context.allocator) -> (err: Error) { ctx := compress.Context{ - input = stream^, + input = stream, }; buf := buf; ws := bytes.buffer_to_stream(buf); @@ -313,4 +311,4 @@ load_from_stream :: proc(stream: ^io.Stream, buf: ^bytes.Buffer, allocator := co return E_General.OK; } -load :: proc{load_from_file, load_from_slice, load_from_stream}; \ No newline at end of file +load :: proc{load_from_file, load_from_slice, load_from_stream}; diff --git a/core/compress/zlib/example.odin b/core/compress/zlib/example.odin index a7fa76d62..0906d7eef 100644 --- a/core/compress/zlib/example.odin +++ b/core/compress/zlib/example.odin @@ -7,30 +7,30 @@ import "core:fmt" main :: proc() { - ODIN_DEMO: []u8 = { + ODIN_DEMO := []u8{ 120, 156, 101, 144, 77, 110, 131, 48, 16, 133, 215, 204, 41, 158, 44, - 69, 73, 32, 148, 182, 75, 35, 14, 208, 125, 47, 96, 185, 195, 143, + 69, 73, 32, 148, 182, 75, 35, 14, 208, 125, 47, 96, 185, 195, 143, 130, 13, 50, 38, 81, 84, 101, 213, 75, 116, 215, 43, 246, 8, 53, - 82, 126, 8, 181, 188, 152, 153, 111, 222, 147, 159, 123, 165, 247, 170, - 98, 24, 213, 88, 162, 198, 244, 157, 243, 16, 186, 115, 44, 75, 227, - 5, 77, 115, 72, 137, 222, 117, 122, 179, 197, 39, 69, 161, 170, 156, - 50, 144, 5, 68, 130, 4, 49, 126, 127, 190, 191, 144, 34, 19, 57, - 69, 74, 235, 209, 140, 173, 242, 157, 155, 54, 158, 115, 162, 168, 12, + 82, 126, 8, 181, 188, 152, 153, 111, 222, 147, 159, 123, 165, 247, 170, + 98, 24, 213, 88, 162, 198, 244, 157, 243, 16, 186, 115, 44, 75, 227, + 5, 77, 115, 72, 137, 222, 117, 122, 179, 197, 39, 69, 161, 170, 156, + 50, 144, 5, 68, 130, 4, 49, 126, 127, 190, 191, 144, 34, 19, 57, + 69, 74, 235, 209, 140, 173, 242, 157, 155, 54, 158, 115, 162, 168, 12, 181, 239, 246, 108, 17, 188, 174, 242, 224, 20, 13, 199, 198, 235, 250, 194, 166, 129, 86, 3, 99, 157, 172, 37, 230, 62, 73, 129, 151, 252, 70, 211, 5, 77, 31, 104, 188, 160, 113, 129, 215, 59, 205, 22, 52, 123, 160, 83, 142, 255, 242, 89, 123, 93, 149, 200, 50, 188, 85, 54, 252, 18, 248, 192, 238, 228, 235, 198, 86, 224, 118, 224, 176, 113, 166, 112, 67, 106, 227, 159, 122, 215, 88, 95, 110, 196, 123, 205, 183, 224, - 98, 53, 8, 104, 213, 234, 201, 147, 7, 248, 192, 14, 170, 29, 25, + 98, 53, 8, 104, 213, 234, 201, 147, 7, 248, 192, 14, 170, 29, 25, 171, 15, 18, 59, 138, 112, 63, 23, 205, 110, 254, 136, 109, 78, 231, - 63, 234, 138, 133, 204, + 63, 234, 138, 133, 204, }; buf: bytes.Buffer; // We can pass ", true" to inflate a raw DEFLATE stream instead of a ZLIB wrapped one. - err := zlib.inflate(&ODIN_DEMO, &buf); + err := zlib.inflate(ODIN_DEMO, &buf); defer bytes.buffer_destroy(&buf); if !zlib.is_kind(err, zlib.E_General.OK) { @@ -39,4 +39,4 @@ main :: proc() { s := bytes.buffer_to_string(&buf); fmt.printf("Input: %v bytes, output (%v bytes):\n%v\n", len(ODIN_DEMO), len(s), s); assert(len(s) == 438); -} \ No newline at end of file +} diff --git a/core/compress/zlib/zlib.odin b/core/compress/zlib/zlib.odin index 34a7984a7..5ce1f26ad 100644 --- a/core/compress/zlib/zlib.odin +++ b/core/compress/zlib/zlib.odin @@ -21,8 +21,8 @@ Compression_Method :: enum u8 { Compression_Level :: enum u8 { Fastest = 0, Fast = 1, - Default = 2, - Maximum = 3, + Default = 2, + Maximum = 3, } Options :: struct { @@ -68,19 +68,19 @@ Z_LENGTH_DEZIGZAG := []u8{ }; Z_FIXED_LENGTH := [288]u8{ - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8, }; Z_FIXED_DIST := [32]u8{ - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, }; /* @@ -94,12 +94,12 @@ ZFAST_MASK :: ((1 << ZFAST_BITS) - 1); JPEG packs from left, ZLIB from right. We can't share code. */ Huffman_Table :: struct { - fast: [1 << ZFAST_BITS]u16, - firstcode: [16]u16, - maxcode: [17]int, - firstsymbol: [16]u16, - size: [288]u8, - value: [288]u16, + fast: [1 << ZFAST_BITS]u16, + firstcode: [16]u16, + maxcode: [17]int, + firstsymbol: [16]u16, + size: [288]u8, + value: [288]u16, }; // Implementation starts here @@ -218,19 +218,19 @@ decode_huffman_slowpath :: proc(z: ^Context, t: ^Huffman_Table) -> (r: u16, err: if (s >= 16) { return 0, E_Deflate.Bad_Huffman_Code; } - // code size is s, so: - b := (k >> (16-s)) - int(t.firstcode[s]) + int(t.firstsymbol[s]); - if b >= size_of(t.size) { - return 0, E_Deflate.Bad_Huffman_Code; - } - if t.size[b] != s { - return 0, E_Deflate.Bad_Huffman_Code; - } - - compress.consume_bits_lsb(z, s); - - r = t.value[b]; - return r, E_General.OK; + // code size is s, so: + b := (k >> (16-s)) - int(t.firstcode[s]) + int(t.firstsymbol[s]); + if b >= size_of(t.size) { + return 0, E_Deflate.Bad_Huffman_Code; + } + if t.size[b] != s { + return 0, E_Deflate.Bad_Huffman_Code; + } + + compress.consume_bits_lsb(z, s); + + r = t.value[b]; + return r, E_General.OK; } decode_huffman :: proc(z: ^Context, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check { @@ -254,7 +254,6 @@ decode_huffman :: proc(z: ^Context, t: ^Huffman_Table) -> (r: u16, err: Error) # } parse_huffman_block :: proc(z: ^Context, z_repeat, z_offset: ^Huffman_Table) -> (err: Error) #no_bounds_check { - #no_bounds_check for { value, e := decode_huffman(z, z_repeat); if !is_kind(e, E_General.OK) { @@ -267,8 +266,8 @@ parse_huffman_block :: proc(z: ^Context, z_repeat, z_offset: ^Huffman_Table) -> } } else { if value == 256 { - // End of block - return E_General.OK; + // End of block + return E_General.OK; } value -= 257; @@ -370,7 +369,7 @@ inflate_from_stream :: proc(using ctx: ^Context, raw := false, allocator := cont } fdict := (flg >> 5) & 1; - /* + /* We don't handle built-in dictionaries for now. They're application specific and PNG doesn't use them. */ @@ -449,7 +448,8 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) -> // log.debugf("Final: %v | Type: %v\n", final, type); - if type == 0 { + switch type { + case 0: // Uncompressed block // Discard bits until next byte boundary @@ -471,9 +471,9 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) -> write_byte(z, u8(lit)); uncompressed_len -= 1; } - } else if type == 3 { + case 3: return E_Deflate.BType_3; - } else { + case: // log.debugf("Err: %v | Final: %v | Type: %v\n", err, final, type); if type == 1 { // Use fixed code lengths. @@ -487,12 +487,12 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) -> } } else { lencodes: [286+32+137]u8; - codelength_sizes: [19]u8; + codelength_sizes: [19]u8; - //i: u32; - n: u32; + //i: u32; + n: u32; - compress.refill_lsb(z, 14); + compress.refill_lsb(z, 14); hlit := compress.read_bits_no_refill_lsb(z, 5) + 257; hdist := compress.read_bits_no_refill_lsb(z, 5) + 1; hclen := compress.read_bits_no_refill_lsb(z, 4) + 4; @@ -525,34 +525,35 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) -> } else { fill := u8(0); compress.refill_lsb(z, 7); - if c == 16 { + switch c { + case 16: c = u16(compress.read_bits_no_refill_lsb(z, 2) + 3); - if n == 0 { - return E_Deflate.Huffman_Bad_Code_Lengths; - } - fill = lencodes[n - 1]; - } else if c == 17 { - c = u16(compress.read_bits_no_refill_lsb(z, 3) + 3); - } else if c == 18 { - c = u16(compress.read_bits_no_refill_lsb(z, 7) + 11); - } else { - return E_Deflate.Huffman_Bad_Code_Lengths; - } + if n == 0 { + return E_Deflate.Huffman_Bad_Code_Lengths; + } + fill = lencodes[n - 1]; + case 17: + c = u16(compress.read_bits_no_refill_lsb(z, 3) + 3); + case 18: + c = u16(compress.read_bits_no_refill_lsb(z, 7) + 11); + case: + return E_Deflate.Huffman_Bad_Code_Lengths; + } if ntot - n < u32(c) { - return E_Deflate.Huffman_Bad_Code_Lengths; - } + return E_Deflate.Huffman_Bad_Code_Lengths; + } - nc := n + u32(c); - #no_bounds_check for ; n < nc; n += 1 { - lencodes[n] = fill; - } + nc := n + u32(c); + #no_bounds_check for ; n < nc; n += 1 { + lencodes[n] = fill; + } } } if n != ntot { - return E_Deflate.Huffman_Bad_Code_Lengths; - } + return E_Deflate.Huffman_Bad_Code_Lengths; + } err = build_huffman(z_repeat, lencodes[:hlit]); if !is_kind(err, E_General.OK) { @@ -577,11 +578,11 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) -> return E_General.OK; } -inflate_from_byte_array :: proc(input: ^[]u8, buf: ^bytes.Buffer, raw := false) -> (err: Error) { +inflate_from_byte_array :: proc(input: []u8, buf: ^bytes.Buffer, raw := false) -> (err: Error) { ctx := Context{}; r := bytes.Reader{}; - bytes.reader_init(&r, input^); + bytes.reader_init(&r, input); rs := bytes.reader_to_stream(&r); ctx.input = rs; @@ -594,9 +595,9 @@ inflate_from_byte_array :: proc(input: ^[]u8, buf: ^bytes.Buffer, raw := false) return err; } -inflate_from_byte_array_raw :: proc(input: ^[]u8, buf: ^bytes.Buffer, raw := false) -> (err: Error) { +inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, raw := false) -> (err: Error) { return inflate_from_byte_array(input, buf, true); } inflate :: proc{inflate_from_stream, inflate_from_byte_array}; -inflate_raw :: proc{inflate_from_stream_raw, inflate_from_byte_array_raw}; \ No newline at end of file +inflate_raw :: proc{inflate_from_stream_raw, inflate_from_byte_array_raw}; diff --git a/core/image/png/example.odin b/core/image/png/example.odin index 8cac5a505..be6cdd7d7 100644 --- a/core/image/png/example.odin +++ b/core/image/png/example.odin @@ -194,7 +194,7 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b } defer close(fd); - write_string(fd, + write_string(fd, fmt.tprintf("P6\n%v %v\n%v\n", width, height, (1 << depth -1)), ); @@ -324,4 +324,4 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b } } return true; -} \ No newline at end of file +} diff --git a/core/image/png/helpers.odin b/core/image/png/helpers.odin index a179cd23b..9bd5f55b2 100644 --- a/core/image/png/helpers.odin +++ b/core/image/png/helpers.odin @@ -76,102 +76,102 @@ core_time :: proc(c: Chunk) -> (t: coretime.Time, ok: bool) { using png_time; return coretime.datetime_to_time( int(year), int(month), int(day), - int(hour), int(minute), int(second)); + int(hour), int(minute), int(second), + ); } text :: proc(c: Chunk) -> (res: Text, ok: bool) { #partial switch c.header.type { - case .tEXt: - ok = true; - - fields := bytes.split(s=c.data, sep=[]u8{0}, allocator=context.temp_allocator); - if len(fields) == 2 { - res.keyword = strings.clone(string(fields[0])); - res.text = strings.clone(string(fields[1])); - } else { - ok = false; - } - return; - case .zTXt: - ok = true; - - fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator); - if len(fields) != 3 || len(fields[1]) != 0 { - // Compression method must be 0=Deflate, which thanks to the split above turns - // into an empty slice - ok = false; return; - } - - // Set up ZLIB context and decompress text payload. - buf: bytes.Buffer; - zlib_error := zlib.inflate_from_byte_array(&fields[2], &buf); - defer bytes.buffer_destroy(&buf); - if !is_kind(zlib_error, E_General.OK) { - ok = false; return; - } + case .tEXt: + ok = true; + fields := bytes.split(s=c.data, sep=[]u8{0}, allocator=context.temp_allocator); + if len(fields) == 2 { res.keyword = strings.clone(string(fields[0])); - res.text = strings.clone(bytes.buffer_to_string(&buf)); - return; - case .iTXt: - ok = true; + res.text = strings.clone(string(fields[1])); + } else { + ok = false; + } + return; + case .zTXt: + ok = true; - s := string(c.data); - null := strings.index_byte(s, 0); - if null == -1 { - ok = false; return; - } - if len(c.data) < null + 4 { - // At a minimum, including the \0 following the keyword, we require 5 more bytes. - ok = false; return; - } - res.keyword = strings.clone(string(c.data[:null])); - rest := c.data[null+1:]; + fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator); + if len(fields) != 3 || len(fields[1]) != 0 { + // Compression method must be 0=Deflate, which thanks to the split above turns + // into an empty slice + ok = false; return; + } - compression_flag := rest[:1][0]; - if compression_flag > 1 { - ok = false; return; - } - compression_method := rest[1:2][0]; - if compression_flag == 1 && compression_method > 0 { - // Only Deflate is supported - ok = false; return; - } - rest = rest[2:]; + // Set up ZLIB context and decompress text payload. + buf: bytes.Buffer; + zlib_error := zlib.inflate_from_byte_array(fields[2], &buf); + defer bytes.buffer_destroy(&buf); + if !is_kind(zlib_error, E_General.OK) { + ok = false; return; + } - // We now expect an optional language keyword and translated keyword, both followed by a \0 - null = strings.index_byte(string(rest), 0); - if null == -1 { - ok = false; return; - } - res.language = strings.clone(string(rest[:null])); - rest = rest[null+1:]; + res.keyword = strings.clone(string(fields[0])); + res.text = strings.clone(bytes.buffer_to_string(&buf)); + return; + case .iTXt: + ok = true; - null = strings.index_byte(string(rest), 0); - if null == -1 { - ok = false; return; - } - res.keyword_localized = strings.clone(string(rest[:null])); - rest = rest[null+1:]; - if compression_flag == 0 { - res.text = strings.clone(string(rest)); - } else { - // Set up ZLIB context and decompress text payload. - buf: bytes.Buffer; - zlib_error := zlib.inflate_from_byte_array(&rest, &buf); - defer bytes.buffer_destroy(&buf); - if !is_kind(zlib_error, E_General.OK) { - - ok = false; return; - } + s := string(c.data); + null := strings.index_byte(s, 0); + if null == -1 { + ok = false; return; + } + if len(c.data) < null + 4 { + // At a minimum, including the \0 following the keyword, we require 5 more bytes. + ok = false; return; + } + res.keyword = strings.clone(string(c.data[:null])); + rest := c.data[null+1:]; - res.text = strings.clone(bytes.buffer_to_string(&buf)); - } - return; - case: - // PNG text helper called with an unrecognized chunk type. + compression_flag := rest[:1][0]; + if compression_flag > 1 { + ok = false; return; + } + compression_method := rest[1:2][0]; + if compression_flag == 1 && compression_method > 0 { + // Only Deflate is supported ok = false; return; + } + rest = rest[2:]; + // We now expect an optional language keyword and translated keyword, both followed by a \0 + null = strings.index_byte(string(rest), 0); + if null == -1 { + ok = false; return; + } + res.language = strings.clone(string(rest[:null])); + rest = rest[null+1:]; + + null = strings.index_byte(string(rest), 0); + if null == -1 { + ok = false; return; + } + res.keyword_localized = strings.clone(string(rest[:null])); + rest = rest[null+1:]; + if compression_flag == 0 { + res.text = strings.clone(string(rest)); + } else { + // Set up ZLIB context and decompress text payload. + buf: bytes.Buffer; + zlib_error := zlib.inflate_from_byte_array(rest, &buf); + defer bytes.buffer_destroy(&buf); + if !is_kind(zlib_error, E_General.OK) { + + ok = false; return; + } + + res.text = strings.clone(bytes.buffer_to_string(&buf)); + } + return; + case: + // PNG text helper called with an unrecognized chunk type. + ok = false; return; } } @@ -199,7 +199,7 @@ iccp :: proc(c: Chunk) -> (res: iCCP, ok: bool) { // Set up ZLIB context and decompress iCCP payload buf: bytes.Buffer; - zlib_error := zlib.inflate_from_byte_array(&fields[2], &buf); + zlib_error := zlib.inflate_from_byte_array(fields[2], &buf); if !is_kind(zlib_error, E_General.OK) { bytes.buffer_destroy(&buf); ok = false; return; @@ -458,19 +458,14 @@ when false { interlace_method = .None, }; - if channels == 1 { - ihdr.color_type = Color_Type{}; - } else if channels == 2 { - ihdr.color_type = Color_Type{.Alpha}; - } else if channels == 3 { - ihdr.color_type = Color_Type{.Color}; - } else if channels == 4 { - ihdr.color_type = Color_Type{.Color, .Alpha}; - } else { - // Unhandled + switch channels { + case 1: ihdr.color_type = Color_Type{}; + case 2: ihdr.color_type = Color_Type{.Alpha}; + case 3: ihdr.color_type = Color_Type{.Color}; + case 4: ihdr.color_type = Color_Type{.Color, .Alpha}; + case:// Unhandled return E_PNG.Unknown_Color_Type; } - h := make_chunk(ihdr, .IHDR); write_chunk(fd, h); @@ -518,4 +513,4 @@ when false { return E_General.OK; } -} \ No newline at end of file +} diff --git a/core/image/png/png.odin b/core/image/png/png.odin index 5ea9979a8..c8dcd24a3 100644 --- a/core/image/png/png.odin +++ b/core/image/png/png.odin @@ -350,9 +350,9 @@ chunk_type_to_name :: proc(type: ^Chunk_Type) -> string { return strings.string_from_ptr(t, 4); } -load_from_slice :: proc(slice: ^[]u8, options: Options = {}, allocator := context.allocator) -> (img: ^Image, err: Error) { +load_from_slice :: proc(slice: []u8, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { r := bytes.Reader{}; - bytes.reader_init(&r, slice^); + bytes.reader_init(&r, slice); stream := bytes.reader_to_stream(&r); /* @@ -360,17 +360,17 @@ load_from_slice :: proc(slice: ^[]u8, options: Options = {}, allocator := contex This way the stream reader could avoid the copy into the temp memory returned by it, and instead return a slice into the original memory that's already owned by the caller. */ - img, err = load_from_stream(&stream, options, allocator); + img, err = load_from_stream(stream, options, allocator); return img, err; } -load_from_file :: proc(filename: string, options: Options = {}, allocator := context.allocator) -> (img: ^Image, err: Error) { +load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { data, ok := os.read_entire_file(filename, allocator); defer delete(data); if ok { - img, err = load_from_slice(&data, options, allocator); + img, err = load_from_slice(data, options, allocator); return; } else { img = new(Image); @@ -378,7 +378,7 @@ load_from_file :: proc(filename: string, options: Options = {}, allocator := con } } -load_from_stream :: proc(stream: ^io.Stream, options: Options = {}, allocator := context.allocator) -> (img: ^Image, err: Error) { +load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { options := options; if .info in options { options |= {.return_metadata, .do_not_decompress_image}; @@ -396,7 +396,7 @@ load_from_stream :: proc(stream: ^io.Stream, options: Options = {}, allocator := img.sidecar = nil; ctx := compress.Context{ - input = stream^, + input = stream, }; signature, io_error := compress.read_data(&ctx, Signature); @@ -669,7 +669,7 @@ load_from_stream :: proc(stream: ^io.Stream, options: Options = {}, allocator := } buf: bytes.Buffer; - zlib_error := zlib.inflate(&idat, &buf); + zlib_error := zlib.inflate(idat, &buf); defer bytes.buffer_destroy(&buf); if !is_kind(zlib_error, E_General.OK) { @@ -817,8 +817,7 @@ load_from_stream :: proc(stream: ^io.Stream, options: Options = {}, allocator := } } } else { - // This should be impossible. - assert(false); + unreachable(); } img.pixels = t; @@ -845,145 +844,145 @@ load_from_stream :: proc(stream: ^io.Stream, options: Options = {}, allocator := o16 := mem.slice_data_cast([]u16, t.buf[:]); switch (raw_image_channels) { - case 1: - // Gray without Alpha. Might have tRNS alpha. - key := u16(0); - if seen_trns { - key = mem.slice_data_cast([]u16, trns.data)[0]; - } + case 1: + // Gray without Alpha. Might have tRNS alpha. + key := u16(0); + if seen_trns { + key = mem.slice_data_cast([]u16, trns.data)[0]; + } - for len(p16) > 0 { - r := p16[0]; + for len(p16) > 0 { + r := p16[0]; - alpha := u16(1); // Default to full opaque + alpha := u16(1); // Default to full opaque - if seen_trns { - if r == key { - if seen_bkgd { - c := img.background.([3]u16); - r = c[0]; - } else { - alpha = 0; // Keyed transparency - } + if seen_trns { + if r == key { + if seen_bkgd { + c := img.background.([3]u16); + r = c[0]; + } else { + alpha = 0; // Keyed transparency } } + } - if premultiply { - o16[0] = r * alpha; - o16[1] = r * alpha; - o16[2] = r * alpha; - } else { - o16[0] = r; - o16[1] = r; - o16[2] = r; - } - - if out_image_channels == 4 { - o16[3] = alpha * 65535; - } - - p16 = p16[1:]; - o16 = o16[out_image_channels:]; + if premultiply { + o16[0] = r * alpha; + o16[1] = r * alpha; + o16[2] = r * alpha; + } else { + o16[0] = r; + o16[1] = r; + o16[2] = r; } - case 2: - // Gray with alpha, we shouldn't have a tRNS chunk. - for len(p16) > 0 { - r := p16[0]; - if premultiply { - alpha := p16[1]; - c := u16(f32(r) * f32(alpha) / f32(65535)); - o16[0] = c; - o16[1] = c; - o16[2] = c; - } else { - o16[0] = r; - o16[1] = r; - o16[2] = r; - } - if .alpha_drop_if_present not_in options { - o16[3] = p16[1]; - } + if out_image_channels == 4 { + o16[3] = alpha * 65535; + } - p16 = p16[2:]; - o16 = o16[out_image_channels:]; + p16 = p16[1:]; + o16 = o16[out_image_channels:]; + } + case 2: + // Gray with alpha, we shouldn't have a tRNS chunk. + for len(p16) > 0 { + r := p16[0]; + if premultiply { + alpha := p16[1]; + c := u16(f32(r) * f32(alpha) / f32(65535)); + o16[0] = c; + o16[1] = c; + o16[2] = c; + } else { + o16[0] = r; + o16[1] = r; + o16[2] = r; } - case 3: - /* - Color without Alpha. - We may still have a tRNS chunk or `.alpha_add_if_missing`. - */ - key: []u16; - if seen_trns { - key = mem.slice_data_cast([]u16, trns.data); + if .alpha_drop_if_present not_in options { + o16[3] = p16[1]; } - for len(p16) > 0 { - r := p16[0]; - g := p16[1]; - b := p16[2]; - - alpha := u16(1); // Default to full opaque - - if seen_trns { - if r == key[0] && g == key[1] && b == key[2] { - if seen_bkgd { - c := img.background.([3]u16); - r = c[0]; - g = c[1]; - b = c[2]; - } else { - alpha = 0; // Keyed transparency - } - } - } + p16 = p16[2:]; + o16 = o16[out_image_channels:]; + } + case 3: + /* + Color without Alpha. + We may still have a tRNS chunk or `.alpha_add_if_missing`. + */ - if premultiply { - o16[0] = r * alpha; - o16[1] = g * alpha; - o16[2] = b * alpha; - } else { - o16[0] = r; - o16[1] = g; - o16[2] = b; - } + key: []u16; + if seen_trns { + key = mem.slice_data_cast([]u16, trns.data); + } + + for len(p16) > 0 { + r := p16[0]; + g := p16[1]; + b := p16[2]; - if out_image_channels == 4 { - o16[3] = alpha * 65535; + alpha := u16(1); // Default to full opaque + + if seen_trns { + if r == key[0] && g == key[1] && b == key[2] { + if seen_bkgd { + c := img.background.([3]u16); + r = c[0]; + g = c[1]; + b = c[2]; + } else { + alpha = 0; // Keyed transparency + } } + } - p16 = p16[3:]; - o16 = o16[out_image_channels:]; + if premultiply { + o16[0] = r * alpha; + o16[1] = g * alpha; + o16[2] = b * alpha; + } else { + o16[0] = r; + o16[1] = g; + o16[2] = b; } - case 4: - // Color with Alpha, can't have tRNS. - for len(p16) > 0 { - r := p16[0]; - g := p16[1]; - b := p16[2]; - a := p16[3]; - if premultiply { - alpha := f32(a) / 65535.0; - o16[0] = u16(f32(r) * alpha); - o16[1] = u16(f32(g) * alpha); - o16[2] = u16(f32(b) * alpha); - } else { - o16[0] = r; - o16[1] = g; - o16[2] = b; - } + if out_image_channels == 4 { + o16[3] = alpha * 65535; + } - if .alpha_drop_if_present not_in options { - o16[3] = a; - } + p16 = p16[3:]; + o16 = o16[out_image_channels:]; + } + case 4: + // Color with Alpha, can't have tRNS. + for len(p16) > 0 { + r := p16[0]; + g := p16[1]; + b := p16[2]; + a := p16[3]; + + if premultiply { + alpha := f32(a) / 65535.0; + o16[0] = u16(f32(r) * alpha); + o16[1] = u16(f32(g) * alpha); + o16[2] = u16(f32(b) * alpha); + } else { + o16[0] = r; + o16[1] = g; + o16[2] = b; + } - p16 = p16[4:]; - o16 = o16[out_image_channels:]; + if .alpha_drop_if_present not_in options { + o16[3] = a; } - case: - unreachable("We should never seen # channels other than 1-4 inclusive."); + + p16 = p16[4:]; + o16 = o16[out_image_channels:]; + } + case: + unreachable("We should never seen # channels other than 1-4 inclusive."); } img.pixels = t; @@ -1011,143 +1010,143 @@ load_from_stream :: proc(stream: ^io.Stream, options: Options = {}, allocator := o := mem.slice_data_cast([]u8, t.buf[:]); switch (raw_image_channels) { - case 1: - // Gray without Alpha. Might have tRNS alpha. - key := u8(0); - if seen_trns { - key = u8(mem.slice_data_cast([]u16be, trns.data)[0]); - } + case 1: + // Gray without Alpha. Might have tRNS alpha. + key := u8(0); + if seen_trns { + key = u8(mem.slice_data_cast([]u16be, trns.data)[0]); + } - for len(p) > 0 { - r := p[0]; - alpha := u8(1); - - if seen_trns { - if r == key { - if seen_bkgd { - c := img.background.([3]u16); - r = u8(c[0]); - } else { - alpha = 0; // Keyed transparency - } - } - if premultiply { - o[0] = r * alpha; - o[1] = r * alpha; - o[2] = r * alpha; + for len(p) > 0 { + r := p[0]; + alpha := u8(1); + + if seen_trns { + if r == key { + if seen_bkgd { + c := img.background.([3]u16); + r = u8(c[0]); + } else { + alpha = 0; // Keyed transparency } - } else { - o[0] = r; - o[1] = r; - o[2] = r; } - - if out_image_channels == 4 { - o[3] = alpha * 255; + if premultiply { + o[0] = r * alpha; + o[1] = r * alpha; + o[2] = r * alpha; } - - p = p[1:]; - o = o[out_image_channels:]; + } else { + o[0] = r; + o[1] = r; + o[2] = r; } - case 2: - // Gray with alpha, we shouldn't have a tRNS chunk. - for len(p) > 0 { - r := p[0]; - if .alpha_premultiply in options { - alpha := p[1]; - c := u8(f32(r) * f32(alpha) / f32(255)); - o[0] = c; - o[1] = c; - o[2] = c; - } else { - o[0] = r; - o[1] = r; - o[2] = r; - } - if .alpha_drop_if_present not_in options { - o[3] = p[1]; - } + if out_image_channels == 4 { + o[3] = alpha * 255; + } - p = p[2:]; - o = o[out_image_channels:]; + p = p[1:]; + o = o[out_image_channels:]; + } + case 2: + // Gray with alpha, we shouldn't have a tRNS chunk. + for len(p) > 0 { + r := p[0]; + if .alpha_premultiply in options { + alpha := p[1]; + c := u8(f32(r) * f32(alpha) / f32(255)); + o[0] = c; + o[1] = c; + o[2] = c; + } else { + o[0] = r; + o[1] = r; + o[2] = r; } - case 3: - // Color without Alpha. We may still have a tRNS chunk - key: []u8; - if seen_trns { - /* - For 8-bit images, the tRNS chunk still contains a triple in u16be. - We use only the low byte in this case. - */ - key = []u8{trns.data[1], trns.data[3], trns.data[5]}; + + if .alpha_drop_if_present not_in options { + o[3] = p[1]; } - for len(p) > 0 { - r := p[0]; - g := p[1]; - b := p[2]; - - alpha := u8(1); // Default to full opaque - - // TODO: Combine the seen_trns cases. - if seen_trns { - if r == key[0] && g == key[1] && b == key[2] { - if seen_bkgd { - c := img.background.([3]u16); - r = u8(c[0]); - g = u8(c[1]); - b = u8(c[2]); - } else { - alpha = 0; // Keyed transparency - } - } - if .alpha_premultiply in options || .blend_background in options { - o[0] = r * alpha; - o[1] = g * alpha; - o[2] = b * alpha; + p = p[2:]; + o = o[out_image_channels:]; + } + case 3: + // Color without Alpha. We may still have a tRNS chunk + key: []u8; + if seen_trns { + /* + For 8-bit images, the tRNS chunk still contains a triple in u16be. + We use only the low byte in this case. + */ + key = []u8{trns.data[1], trns.data[3], trns.data[5]}; + } + for len(p) > 0 { + r := p[0]; + g := p[1]; + b := p[2]; + + alpha := u8(1); // Default to full opaque + + // TODO: Combine the seen_trns cases. + if seen_trns { + if r == key[0] && g == key[1] && b == key[2] { + if seen_bkgd { + c := img.background.([3]u16); + r = u8(c[0]); + g = u8(c[1]); + b = u8(c[2]); + } else { + alpha = 0; // Keyed transparency } - } else { - o[0] = r; - o[1] = g; - o[2] = b; } - if out_image_channels == 4 { - o[3] = alpha * 255; + if .alpha_premultiply in options || .blend_background in options { + o[0] = r * alpha; + o[1] = g * alpha; + o[2] = b * alpha; } + } else { + o[0] = r; + o[1] = g; + o[2] = b; + } - p = p[3:]; - o = o[out_image_channels:]; + if out_image_channels == 4 { + o[3] = alpha * 255; } - case 4: - // Color with Alpha, can't have tRNS. - for len(p) > 0 { - r := p[0]; - g := p[1]; - b := p[2]; - a := p[3]; - - if .alpha_premultiply in options { - alpha := f32(a) / 255.0; - o[0] = u8(f32(r) * alpha); - o[1] = u8(f32(g) * alpha); - o[2] = u8(f32(b) * alpha); - } else { - o[0] = r; - o[1] = g; - o[2] = b; - } - if .alpha_drop_if_present not_in options { - o[3] = a; - } + p = p[3:]; + o = o[out_image_channels:]; + } + case 4: + // Color with Alpha, can't have tRNS. + for len(p) > 0 { + r := p[0]; + g := p[1]; + b := p[2]; + a := p[3]; + + if .alpha_premultiply in options { + alpha := f32(a) / 255.0; + o[0] = u8(f32(r) * alpha); + o[1] = u8(f32(g) * alpha); + o[2] = u8(f32(b) * alpha); + } else { + o[0] = r; + o[1] = g; + o[2] = b; + } - p = p[4:]; - o = o[out_image_channels:]; + if .alpha_drop_if_present not_in options { + o[3] = a; } - case: - unreachable("We should never seen # channels other than 1-4 inclusive."); + + p = p[4:]; + o = o[out_image_channels:]; + } + case: + unreachable("We should never seen # channels other than 1-4 inclusive."); } img.pixels = t; @@ -1181,13 +1180,13 @@ filter_paeth :: #force_inline proc(left, up, up_left: u8) -> u8 { } Filter_Params :: struct #packed { - src : []u8, - dest : []u8, - width : int, - height : int, - depth : int, + src: []u8, + dest: []u8, + width: int, + height: int, + depth: int, channels: int, - rescale : bool, + rescale: bool, } depth_scale_table :: []u8{0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01}; @@ -1210,39 +1209,39 @@ defilter_8 :: proc(params: ^Filter_Params) -> (ok: bool) { filter := Row_Filter(src[0]); src = src[1:]; // fmt.printf("Row: %v | Filter: %v\n", y, filter); switch(filter) { - case .None: - copy(dest, src[:row_stride]); - case .Sub: - for i := 0; i < channels; i += 1 { - dest[i] = src[i]; - } - for k := 0; k < nk; k += 1 { - dest[channels+k] = (src[channels+k] + dest[k]) & 255; - } - case .Up: - for k := 0; k < row_stride; k += 1 { - dest[k] = (src[k] + up[k]) & 255; - } - case .Average: - for i := 0; i < channels; i += 1 { - avg := up[i] >> 1; - dest[i] = (src[i] + avg) & 255; - } - for k := 0; k < nk; k += 1 { - avg := u8((u16(up[channels+k]) + u16(dest[k])) >> 1); - dest[channels+k] = (src[channels+k] + avg) & 255; - } - case .Paeth: - for i := 0; i < channels; i += 1 { - paeth := filter_paeth(0, up[i], 0); - dest[i] = (src[i] + paeth) & 255; - } - for k := 0; k < nk; k += 1 { - paeth := filter_paeth(dest[k], up[channels+k], up[k]); - dest[channels+k] = (src[channels+k] + paeth) & 255; - } - case: - return false; + case .None: + copy(dest, src[:row_stride]); + case .Sub: + for i := 0; i < channels; i += 1 { + dest[i] = src[i]; + } + for k := 0; k < nk; k += 1 { + dest[channels+k] = (src[channels+k] + dest[k]) & 255; + } + case .Up: + for k := 0; k < row_stride; k += 1 { + dest[k] = (src[k] + up[k]) & 255; + } + case .Average: + for i := 0; i < channels; i += 1 { + avg := up[i] >> 1; + dest[i] = (src[i] + avg) & 255; + } + for k := 0; k < nk; k += 1 { + avg := u8((u16(up[channels+k]) + u16(dest[k])) >> 1); + dest[channels+k] = (src[channels+k] + avg) & 255; + } + case .Paeth: + for i := 0; i < channels; i += 1 { + paeth := filter_paeth(0, up[i], 0); + dest[i] = (src[i] + paeth) & 255; + } + for k := 0; k < nk; k += 1 { + paeth := filter_paeth(dest[k], up[channels+k], up[k]); + dest[channels+k] = (src[channels+k] + paeth) & 255; + } + case: + return false; } src = src[row_stride:]; @@ -1277,45 +1276,45 @@ defilter_less_than_8 :: proc(params: ^Filter_Params) -> (ok: bool) #no_bounds_ch dest = dest[row_offset:]; filter := Row_Filter(src[0]); src = src[1:]; - switch(filter) { - case .None: - copy(dest, src[:row_stride_in]); - case .Sub: - for i in 0..channels { - dest[i] = src[i]; - } - for k in 0..nk { - dest[channels+k] = (src[channels+k] + dest[k]) & 255; - } - case .Up: - for k in 0..row_stride_in { - dest[k] = (src[k] + up[k]) & 255; - } - case .Average: - for i in 0..channels { - avg := up[i] >> 1; - dest[i] = (src[i] + avg) & 255; - } - for k in 0..nk { - avg := u8((u16(up[channels+k]) + u16(dest[k])) >> 1); - dest[channels+k] = (src[channels+k] + avg) & 255; - } - case .Paeth: - for i in 0..channels { - paeth := filter_paeth(0, up[i], 0); - dest[i] = (src[i] + paeth) & 255; - } - for k in 0..nk { - paeth := filter_paeth(dest[k], up[channels], up[k]); - dest[channels+k] = (src[channels+k] + paeth) & 255; - } - case: - return false; + switch filter { + case .None: + copy(dest, src[:row_stride_in]); + case .Sub: + for i in 0..channels { + dest[i] = src[i]; + } + for k in 0..nk { + dest[channels+k] = (src[channels+k] + dest[k]) & 255; + } + case .Up: + for k in 0..row_stride_in { + dest[k] = (src[k] + up[k]) & 255; + } + case .Average: + for i in 0..channels { + avg := up[i] >> 1; + dest[i] = (src[i] + avg) & 255; + } + for k in 0..nk { + avg := u8((u16(up[channels+k]) + u16(dest[k])) >> 1); + dest[channels+k] = (src[channels+k] + avg) & 255; + } + case .Paeth: + for i in 0..channels { + paeth := filter_paeth(0, up[i], 0); + dest[i] = (src[i] + paeth) & 255; + } + for k in 0..nk { + paeth := filter_paeth(dest[k], up[channels], up[k]); + dest[channels+k] = (src[channels+k] + paeth) & 255; + } + case: + return false; } - src = src [row_stride_in:]; - up = dest; - dest = dest[row_stride_in:]; + src = src [row_stride_in:]; + up = dest; + dest = dest[row_stride_in:]; } // Let's expand the bits @@ -1334,7 +1333,8 @@ defilter_less_than_8 :: proc(params: ^Filter_Params) -> (ok: bool) #no_bounds_ch for j := 0; j < height; j += 1 { src = dest[row_offset:]; - if depth == 4 { + switch depth { + case 4: k := row_stride_out; for ; k >= 2; k -= 2 { c := src[0]; @@ -1347,7 +1347,7 @@ defilter_less_than_8 :: proc(params: ^Filter_Params) -> (ok: bool) #no_bounds_ch dest[0] = scale * (c >> 4); dest = dest[1:]; } - } else if depth == 2 { + case 2: k := row_stride_out; for ; k >= 4; k -= 4 { c := src[0]; @@ -1368,7 +1368,7 @@ defilter_less_than_8 :: proc(params: ^Filter_Params) -> (ok: bool) #no_bounds_ch } dest = dest[k:]; } - } else if depth == 1 { + case 1: k := row_stride_out; for ; k >= 8; k -= 8 { c := src[0]; @@ -1406,6 +1406,7 @@ defilter_less_than_8 :: proc(params: ^Filter_Params) -> (ok: bool) #no_bounds_ch dest = dest[k:]; } + } } @@ -1429,40 +1430,40 @@ defilter_16 :: proc(params: ^Filter_Params) -> (ok: bool) { nk := row_stride - stride; filter := Row_Filter(src[0]); src = src[1:]; - switch(filter) { - case .None: - copy(dest, src[:row_stride]); - case .Sub: - for i := 0; i < stride; i += 1 { - dest[i] = src[i]; - } - for k := 0; k < nk; k += 1 { - dest[stride+k] = (src[stride+k] + dest[k]) & 255; - } - case .Up: - for k := 0; k < row_stride; k += 1 { - dest[k] = (src[k] + up[k]) & 255; - } - case .Average: - for i := 0; i < stride; i += 1 { - avg := up[i] >> 1; - dest[i] = (src[i] + avg) & 255; - } - for k := 0; k < nk; k += 1 { - avg := u8((u16(up[stride+k]) + u16(dest[k])) >> 1); - dest[stride+k] = (src[stride+k] + avg) & 255; - } - case .Paeth: - for i := 0; i < stride; i += 1 { - paeth := filter_paeth(0, up[i], 0); - dest[i] = (src[i] + paeth) & 255; - } - for k := 0; k < nk; k += 1 { - paeth := filter_paeth(dest[k], up[stride+k], up[k]); - dest[stride+k] = (src[stride+k] + paeth) & 255; - } - case: - return false; + switch filter { + case .None: + copy(dest, src[:row_stride]); + case .Sub: + for i := 0; i < stride; i += 1 { + dest[i] = src[i]; + } + for k := 0; k < nk; k += 1 { + dest[stride+k] = (src[stride+k] + dest[k]) & 255; + } + case .Up: + for k := 0; k < row_stride; k += 1 { + dest[k] = (src[k] + up[k]) & 255; + } + case .Average: + for i := 0; i < stride; i += 1 { + avg := up[i] >> 1; + dest[i] = (src[i] + avg) & 255; + } + for k := 0; k < nk; k += 1 { + avg := u8((u16(up[stride+k]) + u16(dest[k])) >> 1); + dest[stride+k] = (src[stride+k] + avg) & 255; + } + case .Paeth: + for i := 0; i < stride; i += 1 { + paeth := filter_paeth(0, up[i], 0); + dest[i] = (src[i] + paeth) & 255; + } + for k := 0; k < nk; k += 1 { + paeth := filter_paeth(dest[k], up[stride+k], up[k]); + dest[stride+k] = (src[stride+k] + paeth) & 255; + } + case: + return false; } src = src[row_stride:]; @@ -1510,7 +1511,7 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^IHDR, option if !filter_ok { // Caller will destroy buffer for us. return E_PNG.Unknown_Filter_Method; - } + } } else { /* For deinterlacing we need to make a temporary buffer, defiilter part of the image, @@ -1582,7 +1583,7 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^IHDR, option } } - return E_General.OK; + return E_General.OK; } -load :: proc{load_from_file, load_from_slice, load_from_stream}; \ No newline at end of file +load :: proc{load_from_file, load_from_slice, load_from_stream}; -- cgit v1.2.3 From a1558b339885b17bc03b5808ed48b560696b5f6c Mon Sep 17 00:00:00 2001 From: Mikkel Hjortshøj Date: Fri, 30 Apr 2021 22:19:10 +0200 Subject: Update nightly.yml --- .github/workflows/nightly.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 7c4b42b1e..82a5ecd7e 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -72,8 +72,8 @@ jobs: - uses: actions/checkout@v1 - name: Download LLVM and setup PATH run: | - brew install llvm - echo "/usr/local/opt/llvm/bin" >> $GITHUB_PATH + brew install llvm@11 + echo "/usr/local/opt/llvm@11/bin" >> $GITHUB_PATH TMP_PATH=$(xcrun --show-sdk-path)/user/include echo "CPATH=$TMP_PATH" >> $GITHUB_ENV - name: build odin -- cgit v1.2.3 From 47ff50a92d8273f01c08f8e8ff6fd37b6feaf677 Mon Sep 17 00:00:00 2001 From: Mikkel Hjortshøj Date: Fri, 30 Apr 2021 22:26:15 +0200 Subject: Remove downloading the LLVM binaries in ci.yml --- .github/workflows/ci.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cdfa1b994..faf5ecd29 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,13 +38,6 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v1 - - name: Download and unpack LLVM bins - shell: powershell - run: | - cd bin - $ProgressPreference = "SilentlyContinue"; - Invoke-WebRequest -Uri https://github.com/odin-lang/Odin/releases/download/llvm-windows/llvm-binaries.zip -OutFile llvm-binaries.zip - 7z x llvm-binaries.zip > $null - name: build Odin shell: cmd run: | -- cgit v1.2.3 From 5ec82623ab7e30ed595e30e97e07e241225ceef8 Mon Sep 17 00:00:00 2001 From: Mikkel Hjortshøj Date: Fri, 30 Apr 2021 22:27:09 +0200 Subject: Remove downloading the LLVM binaries in nightly.yml --- .github/workflows/nightly.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 82a5ecd7e..3d58a8fd8 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -10,15 +10,6 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v1 - - name: Install cURL - run: choco install curl - - name: Download and unpack LLVM bins - shell: cmd - run: | - cd bin - curl -sL https://github.com/odin-lang/Odin/releases/download/llvm-windows/llvm-binaries.zip --output llvm-binaries.zip - 7z x llvm-binaries.zip > nul - rm -f llvm-binaries.zip - name: build Odin shell: cmd run: | -- cgit v1.2.3 From 0e972296a48791f87ef656e621816c571e0cd822 Mon Sep 17 00:00:00 2001 From: Mikkel Hjortshøj Date: Fri, 30 Apr 2021 22:50:14 +0200 Subject: Change non-target indentation in Makefile to spaces to fix make error --- Makefile | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index c8199b01e..6633438bc 100644 --- a/Makefile +++ b/Makefile @@ -8,26 +8,26 @@ CC=clang OS=$(shell uname) ifeq ($(OS), Darwin) - LLVM_CONFIG=llvm-config + LLVM_CONFIG=llvm-config - LDFLAGS:=$(LDFLAGS) -liconv - CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags) - LDFLAGS:=$(LDFLAGS) -lLLVM-C + LDFLAGS:=$(LDFLAGS) -liconv + CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags) + LDFLAGS:=$(LDFLAGS) -lLLVM-C endif ifeq ($(OS), Linux) - LLVM_CONFIG=llvm-config-11 - ifneq ($(shell which llvm-config-11 2>/dev/null),) - LLVM_CONFIG=llvm-config-11 - else - ifneq ($(shell llvm-config --version | grep '^11\.'),) - LLVM_CONFIG=llvm-config - else - $(error "Requirement: llvm-config must be version 11") - endif - endif - - CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags) - LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs) + LLVM_CONFIG=llvm-config-11 + ifneq ($(shell which llvm-config-11 2>/dev/null),) + LLVM_CONFIG=llvm-config-11 + else + ifneq ($(shell llvm-config --version | grep '^11\.'),) + LLVM_CONFIG=llvm-config + else + $(error "Requirement: llvm-config must be version 11") + endif + endif + + CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags) + LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs) endif all: debug demo -- cgit v1.2.3 From a02bcd3bfdb0a12a4aa19bf45764f6b011e551de Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 1 May 2021 13:16:47 +0200 Subject: PNG: Fix test for when premultiplication is needed. --- core/image/png/png.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/image/png/png.odin b/core/image/png/png.odin index c8dcd24a3..8a6729484 100644 --- a/core/image/png/png.odin +++ b/core/image/png/png.odin @@ -731,7 +731,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c `.blend_background` and `seen_bkgd` if we haven't seen both. */ if !(seen_bkgd && .blend_background in options) { - options ~= {.blend_background}; + options -= {.blend_background}; seen_bkgd = false; } @@ -748,7 +748,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c } add_alpha := (seen_trns && .alpha_drop_if_present not_in options) || (.alpha_add_if_missing in options); - premultiply := .alpha_premultiply in options || .blend_background in options; + premultiply := .alpha_premultiply in options || seen_bkgd; img.channels = out_image_channels; -- cgit v1.2.3 From db1ef078ff1d7cb00d090c87a8d8b6981fadd530 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 1 May 2021 16:05:13 +0200 Subject: Fix a few more cases in which bKGD wasn't properly applied. --- core/image/png/png.odin | 83 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 71 insertions(+), 12 deletions(-) diff --git a/core/image/png/png.odin b/core/image/png/png.odin index 8a6729484..5b18e6619 100644 --- a/core/image/png/png.odin +++ b/core/image/png/png.odin @@ -886,9 +886,25 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c } case 2: // Gray with alpha, we shouldn't have a tRNS chunk. + bg := f32(0.0); + if seen_bkgd { + bg = f32(img.background.([3]u16)[0]); + } + for len(p16) > 0 { r := p16[0]; - if premultiply { + if seen_bkgd { + alpha := f32(p16[1]) / f32(65535); + c := u16(f32(r) * alpha + (1.0 - alpha) * bg); + o16[0] = c; + o16[1] = c; + o16[2] = c; + /* + After BG blending, the pixel is now fully opaque. + Update the value we'll write to the output alpha. + */ + p16[1] = 65535; + } else if premultiply { alpha := p16[1]; c := u16(f32(r) * f32(alpha) / f32(65535)); o16[0] = c; @@ -900,7 +916,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c o16[2] = r; } - if .alpha_drop_if_present not_in options { + if out_image_channels == 4 { o16[3] = p16[1]; } @@ -963,7 +979,22 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c b := p16[2]; a := p16[3]; - if premultiply { + if seen_bkgd { + alpha := f32(a) / 65535.0; + c := img.background.([3]u16); + rb := f32(c[0]) * (1.0 - alpha); + gb := f32(c[1]) * (1.0 - alpha); + bb := f32(c[2]) * (1.0 - alpha); + + o16[0] = u16(f32(r) * alpha + rb); + o16[1] = u16(f32(g) * alpha + gb); + o16[2] = u16(f32(b) * alpha + bb); + /* + After BG blending, the pixel is now fully opaque. + Update the value we'll write to the output alpha. + */ + a = 65535; + } else if premultiply { alpha := f32(a) / 65535.0; o16[0] = u16(f32(r) * alpha); o16[1] = u16(f32(g) * alpha); @@ -974,7 +1005,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c o16[2] = b; } - if .alpha_drop_if_present not_in options { + if out_image_channels == 4 { o16[3] = a; } @@ -992,7 +1023,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c // Check if we need to do something. if raw_image_channels == out_image_channels { // If we have 3 in and 3 out, or 4 in and 4 out without premultiplication... - if raw_image_channels == 4 && .alpha_premultiply not_in options { + if !premultiply { // Then we're done. return img, E_General.OK; } @@ -1050,9 +1081,25 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c } case 2: // Gray with alpha, we shouldn't have a tRNS chunk. + bg := f32(0.0); + if seen_bkgd { + bg = f32(img.background.([3]u16)[0]); + } + for len(p) > 0 { r := p[0]; - if .alpha_premultiply in options { + if seen_bkgd { + alpha := f32(p[1]) / f32(255); + c := u8(f32(r) * alpha + (1.0 - alpha) * bg); + o[0] = c; + o[1] = c; + o[2] = c; + /* + After BG blending, the pixel is now fully opaque. + Update the value we'll write to the output alpha. + */ + p[1] = 255; + } else if .alpha_premultiply in options { alpha := p[1]; c := u8(f32(r) * f32(alpha) / f32(255)); o[0] = c; @@ -1064,7 +1111,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c o[2] = r; } - if .alpha_drop_if_present not_in options { + if out_image_channels == 4 { o[3] = p[1]; } @@ -1088,7 +1135,6 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c alpha := u8(1); // Default to full opaque - // TODO: Combine the seen_trns cases. if seen_trns { if r == key[0] && g == key[1] && b == key[2] { if seen_bkgd { @@ -1126,8 +1172,22 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c g := p[1]; b := p[2]; a := p[3]; - - if .alpha_premultiply in options { + if seen_bkgd { + alpha := f32(a) / 255.0; + c := img.background.([3]u16); + rb := f32(c[0]) * (1.0 - alpha); + gb := f32(c[1]) * (1.0 - alpha); + bb := f32(c[2]) * (1.0 - alpha); + + o[0] = u8(f32(r) * alpha + rb); + o[1] = u8(f32(g) * alpha + gb); + o[2] = u8(f32(b) * alpha + bb); + /* + After BG blending, the pixel is now fully opaque. + Update the value we'll write to the output alpha. + */ + a = 255; + } else if premultiply { alpha := f32(a) / 255.0; o[0] = u8(f32(r) * alpha); o[1] = u8(f32(g) * alpha); @@ -1138,7 +1198,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c o[2] = b; } - if .alpha_drop_if_present not_in options { + if out_image_channels == 4 { o[3] = a; } @@ -1207,7 +1267,6 @@ defilter_8 :: proc(params: ^Filter_Params) -> (ok: bool) { nk := row_stride - channels; filter := Row_Filter(src[0]); src = src[1:]; - // fmt.printf("Row: %v | Filter: %v\n", y, filter); switch(filter) { case .None: copy(dest, src[:row_stride]); -- cgit v1.2.3 From 97b537f80029724e2a9b9f9240532aeb95e1143d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 1 May 2021 16:23:50 +0100 Subject: Update intrinsics.odin for documentation --- core/intrinsics/intrinsics.odin | 76 +++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 33 deletions(-) diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index ac916a693..84d5b06b5 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -12,7 +12,28 @@ volatile_store :: proc(dst: ^$T, val: T) -> T --- // Trapping debug_trap :: proc() --- -trap :: proc() -> ! --- +trap :: proc() -> ! --- + +// Instructions + +alloca :: proc(size, align: int) -> ^u8 --- +cpu_relax :: proc() --- +read_cycle_counter :: proc() -> i64 --- + +count_ones :: proc(x: $T) -> T where type_is_integer(T) --- +count_zeros :: proc(x: $T) -> T where type_is_integer(T) --- +count_trailing_zeros :: proc(x: $T) -> T where type_is_integer(T) --- +count_leading_zeros :: proc(x: $T) -> T where type_is_integer(T) --- +reverse_bits :: proc(x: $T) -> T where type_is_integer(T) --- +byte_swap :: proc(x: $T) -> T where type_is_integer(T) || type_is_float(T) --- + +overflow_add :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok --- +overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok --- +overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok --- + +// Compiler Hints +expect :: proc(val, expected_val: T) -> T --- + // Atomics atomic_fence :: proc() --- @@ -67,36 +88,25 @@ atomic_xchg_rel :: proc(dst; ^$T, val: T) -> T --- atomic_xchg_acqrel :: proc(dst; ^$T, val: T) -> T --- atomic_xchg_relaxed :: proc(dst; ^$T, val: T) -> T --- -atomic_cxchg :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- -atomic_cxchg_acq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- -atomic_cxchg_rel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- -atomic_cxchg_acqrel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- -atomic_cxchg_relaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- -atomic_cxchg_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- -atomic_cxchg_failacq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- -atomic_cxchg_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- -atomic_cxchg_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- - -atomic_cxchgweak :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- -atomic_cxchgweak_acq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- -atomic_cxchgweak_rel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- -atomic_cxchgweak_acqrel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- -atomic_cxchgweak_relaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- -atomic_cxchgweak_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- -atomic_cxchgweak_failacq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- -atomic_cxchgweak_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- -atomic_cxchgweak_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- - -// Instructions - -alloca :: proc(size, align: int) -> ^u8 --- -cpu_relax :: proc() --- -read_cycle_counter :: proc() -> i64 --- - - -// Compiler Hints -expect :: proc(val, expected_val: T) -> T --- - +atomic_cxchg :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- +atomic_cxchg_acq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- +atomic_cxchg_rel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- +atomic_cxchg_acqrel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- +atomic_cxchg_relaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- +atomic_cxchg_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- +atomic_cxchg_failacq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- +atomic_cxchg_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- +atomic_cxchg_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- + +atomic_cxchgweak :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- +atomic_cxchgweak_acq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- +atomic_cxchgweak_rel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- +atomic_cxchgweak_acqrel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- +atomic_cxchgweak_relaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- +atomic_cxchgweak_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- +atomic_cxchgweak_failacq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- +atomic_cxchgweak_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- +atomic_cxchgweak_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok --- // Constant type tests @@ -159,5 +169,5 @@ type_polymorphic_record_parameter_value :: proc($T: typeid, index: int) -> $V -- type_field_index_of :: proc($T: typeid, $name: string) -> uintptr --- -type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) --- -type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) --- +type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) where type_is_comparable(T) --- +type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) where type_is_comparable(T) --- -- cgit v1.2.3 From 956b59d48c909228188d13278a9ce1fa0f9c2166 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 1 May 2021 16:27:11 +0100 Subject: Update old bin/README.md --- bin/README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bin/README.md b/bin/README.md index 24a19a3c4..f58f0a482 100644 --- a/bin/README.md +++ b/bin/README.md @@ -2,13 +2,12 @@ ## Setup -Odin only supports x86-64 at the moment (64-bit), relies on LLVM for code generation and an external linker. +Odin currently supports x86-64 and ARM64 at the moment (64-bit), relies on LLVM for code generation and an external linker. In addition, the following platform-specific steps are necessary: - Windows * Have Visual Studio installed (MSVC 2010 or later, for the linker) - * Have a copy of `opt.exe` and `llc.exe` in `Odin/bin`. Pre-built Windows binaries can be found [here](https://github.com/odin-lang/Odin/releases/tag/llvm-windows) and *must* be explicitly copied * Open a valid command prompt: * **Basic:** run the `x64 Native Tools Command Prompt for VS2017` shortcut bundled with VS 2017, or * **Advanced:** run `vcvarsall.bat x64` from a blank `cmd` session @@ -19,7 +18,6 @@ In addition, the following platform-specific steps are necessary: * Make sure the LLVM binaries and the linker are added to your `$PATH` environmental variable - GNU/Linux - * Have LLVM installed (opt/llc) * Have Clang installed (version X.X or later, for linking) * Make sure the LLVM binaries and the linker are added to your `$PATH` environmental variable -- cgit v1.2.3 From 0659a11a1a42679af56e988bb63166b483a99c60 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 1 May 2021 18:24:31 +0200 Subject: PNG: Fix tRNS handling. --- core/image/png/png.odin | 34 ++++++++++++++++------------------ core/testing/runner.odin | 1 - 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/core/image/png/png.odin b/core/image/png/png.odin index 5b18e6619..cfbeb43f5 100644 --- a/core/image/png/png.odin +++ b/core/image/png/png.odin @@ -1049,28 +1049,25 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c } for len(p) > 0 { - r := p[0]; + r := p[0]; alpha := u8(1); if seen_trns { if r == key { if seen_bkgd { - c := img.background.([3]u16); - r = u8(c[0]); + bc := img.background.([3]u16); + r = u8(bc[0]); } else { alpha = 0; // Keyed transparency } } if premultiply { - o[0] = r * alpha; - o[1] = r * alpha; - o[2] = r * alpha; + r *= alpha; } - } else { - o[0] = r; - o[1] = r; - o[2] = r; } + o[0] = r; + o[1] = r; + o[2] = r; if out_image_channels == 4 { o[3] = alpha * 255; @@ -1128,6 +1125,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c */ key = []u8{trns.data[1], trns.data[3], trns.data[5]}; } + for len(p) > 0 { r := p[0]; g := p[1]; @@ -1147,17 +1145,17 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c } } - if .alpha_premultiply in options || .blend_background in options { - o[0] = r * alpha; - o[1] = g * alpha; - o[2] = b * alpha; + if premultiply { + r *= alpha; + g *= alpha; + b *= alpha; } - } else { - o[0] = r; - o[1] = g; - o[2] = b; } + o[0] = r; + o[1] = g; + o[2] = b; + if out_image_channels == 4 { o[3] = alpha * 255; } diff --git a/core/testing/runner.odin b/core/testing/runner.odin index efeaa04f6..7691ef11e 100644 --- a/core/testing/runner.odin +++ b/core/testing/runner.odin @@ -3,7 +3,6 @@ package testing import "core:io" import "core:os" -import "core:strings" import "core:slice" reset_t :: proc(t: ^T) { -- cgit v1.2.3 From fd56b4882531fc89f4cd4cc29016314773fd395d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 1 May 2021 17:30:22 +0100 Subject: Add `ODIN_ROOT` information --- bin/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/README.md b/bin/README.md index f58f0a482..9e35adc23 100644 --- a/bin/README.md +++ b/bin/README.md @@ -23,6 +23,7 @@ In addition, the following platform-specific steps are necessary: Then build the compiler by calling `build.bat` (Windows) or `make` (Linux/MacOS). This will automatically run the demo program if successful. -**Notes for Linux:**: The compiler currently relies on the `core` and `shared` library collection being relative to the compiler executable. Installing the compiler in the usual sense (to `/usr/local/bin` or similar) is therefore not as straight forward as you need to make sure the mentioned libraries are available. As a result, it is recommended to simply explicitly invoke the compiler with `/path/to/odin` in your preferred build system, or add `/path/to/odin` to `$PATH`. +**Notes for \*Nix Systems:**: The compiler currently relies on the `core` and `shared` library collection being relative to the compiler executable, by default. Installing the compiler in the usual sense (to `/usr/local/bin` or similar) is therefore not as straight forward as you need to make sure the mentioned libraries are available. As a result, it is recommended to either simply explicitly invoke the compiler with `/path/to/odin` in your preferred build system, or `set ODIN_ROOT=/path/to/odin_root`. + Please read the [Getting Started Guide](https://github.com/odin-lang/Odin/wiki#getting-started-with-odin) for more information. -- cgit v1.2.3 From 406d2ab6ba8a5906df634fc5f00c40b6983a0c39 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 1 May 2021 17:42:59 +0100 Subject: Simplify/Fix the state_flag behaviour for code generation --- src/llvm_backend.cpp | 42 +++++++++++++----------------------------- src/llvm_backend.hpp | 3 +-- src/parser.cpp | 8 ++++++++ 3 files changed, 22 insertions(+), 31 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index f215f12a4..de202658a 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -184,7 +184,7 @@ void lb_emit_bounds_check(lbProcedure *p, Token token, lbValue index, lbValue le if (build_context.no_bounds_check) { return; } - if ((p->module->state_flags & StateFlag_no_bounds_check) != 0) { + if ((p->state_flags & StateFlag_no_bounds_check) != 0) { return; } @@ -209,7 +209,7 @@ void lb_emit_slice_bounds_check(lbProcedure *p, Token token, lbValue low, lbValu if (build_context.no_bounds_check) { return; } - if ((p->module->state_flags & StateFlag_no_bounds_check) != 0) { + if ((p->state_flags & StateFlag_no_bounds_check) != 0) { return; } @@ -3117,19 +3117,6 @@ void lb_begin_procedure_body(lbProcedure *p) { } } - if (p->tags != 0) { - u64 in = p->tags; - u64 out = p->module->state_flags; - if (in & ProcTag_bounds_check) { - out |= StateFlag_bounds_check; - out &= ~StateFlag_no_bounds_check; - } else if (in & ProcTag_no_bounds_check) { - out |= StateFlag_no_bounds_check; - out &= ~StateFlag_bounds_check; - } - p->module->state_flags = out; - } - p->builder = LLVMCreateBuilder(); p->decl_block = lb_create_block(p, "decls", true); @@ -3299,7 +3286,7 @@ void lb_end_procedure_body(lbProcedure *p) { } p->curr_block = nullptr; - p->module->state_flags = 0; + p->state_flags = 0; } void lb_end_procedure(lbProcedure *p) { LLVMDisposeBuilder(p->builder); @@ -4821,12 +4808,12 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { LLVMSetCurrentDebugLocation2(p->builder, prev_debug_location); }); - u64 prev_state_flags = p->module->state_flags; - defer (p->module->state_flags = prev_state_flags); + u16 prev_state_flags = p->state_flags; + defer (p->state_flags = prev_state_flags); if (node->state_flags != 0) { - u64 in = node->state_flags; - u64 out = p->module->state_flags; + u16 in = node->state_flags; + u16 out = p->state_flags; if (in & StateFlag_bounds_check) { out |= StateFlag_bounds_check; @@ -4836,7 +4823,7 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { out &= ~StateFlag_bounds_check; } - p->module->state_flags = out; + p->state_flags = out; } switch (node->kind) { @@ -11014,12 +11001,12 @@ lbValue lb_emit_any_cast(lbProcedure *p, lbValue value, Type *type, TokenPos pos lbValue lb_build_expr(lbProcedure *p, Ast *expr) { lbModule *m = p->module; - u64 prev_state_flags = p->module->state_flags; - defer (p->module->state_flags = prev_state_flags); + u16 prev_state_flags = p->state_flags; + defer (p->state_flags = prev_state_flags); if (expr->state_flags != 0) { - u64 in = expr->state_flags; - u64 out = p->module->state_flags; + u16 in = expr->state_flags; + u16 out = p->state_flags; if (in & StateFlag_bounds_check) { out |= StateFlag_bounds_check; @@ -11029,7 +11016,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { out &= ~StateFlag_bounds_check; } - p->module->state_flags = out; + p->state_flags = out; } expr = unparen_expr(expr); @@ -12844,9 +12831,6 @@ void lb_init_module(lbModule *m, Checker *c) { m->debug_builder = LLVMCreateDIBuilder(m->mod); } - m->state_flags = 0; - m->state_flags |= StateFlag_bounds_check; - gb_mutex_init(&m->mutex); gbAllocator a = heap_allocator(); map_init(&m->types, a); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index cd6a99c20..aed555aaf 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -85,8 +85,6 @@ struct lbModule { LLVMModuleRef mod; LLVMContextRef ctx; - u64 state_flags; - CheckerInfo *info; gbMutex mutex; @@ -210,6 +208,7 @@ enum lbProcedureFlag : u32 { struct lbProcedure { u32 flags; + u16 state_flags; lbProcedure *parent; Array children; diff --git a/src/parser.cpp b/src/parser.cpp index a6764de08..c9275c7f2 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2170,6 +2170,14 @@ Ast *parse_operand(AstFile *f, bool lhs) { body = parse_body(f); f->curr_proc = curr_proc; + // Apply the tags directly to the body rather than the type + if (tags & ProcTag_no_bounds_check) { + body->state_flags |= StateFlag_no_bounds_check; + } + if (tags & ProcTag_bounds_check) { + body->state_flags |= StateFlag_bounds_check; + } + return ast_proc_lit(f, type, body, tags, where_token, where_clauses); } else if (allow_token(f, Token_do)) { Ast *curr_proc = f->curr_proc; -- cgit v1.2.3 From ced7700cdb45ac4a5850e8eaf6fa2dd4cba4b1cf Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 1 May 2021 17:59:30 +0100 Subject: Add extra check for `#no_bounds_check` etc being followed by a newline or empty statement --- src/parser.cpp | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index c9275c7f2..12f0fb9be 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -4341,6 +4341,16 @@ Ast *parse_unrolled_for_loop(AstFile *f, Token unroll_token) { return ast_unroll_range_stmt(f, unroll_token, for_token, val0, val1, in_token, expr, body); } +void parse_check_directive_for_empty_statement(Ast *s, Token const &name) { + if (s != nullptr && s->kind == Ast_EmptyStmt) { + if (s->EmptyStmt.token.string == "\n") { + syntax_error(name, "#%.*s cannot be followed by a newline", LIT(name.string)); + } else { + syntax_error(name, "#%.*s cannot be applied to an empty statement ';'", LIT(name.string)); + } + } +} + Ast *parse_stmt(AstFile *f) { Ast *s = nullptr; Token token = f->curr_token; @@ -4447,6 +4457,7 @@ Ast *parse_stmt(AstFile *f) { if (tag == "bounds_check") { s = parse_stmt(f); + parse_check_directive_for_empty_statement(s, name); s->state_flags |= StateFlag_bounds_check; if ((s->state_flags & StateFlag_no_bounds_check) != 0) { syntax_error(token, "#bounds_check and #no_bounds_check cannot be applied together"); @@ -4454,27 +4465,12 @@ Ast *parse_stmt(AstFile *f) { return s; } else if (tag == "no_bounds_check") { s = parse_stmt(f); + parse_check_directive_for_empty_statement(s, name); s->state_flags |= StateFlag_no_bounds_check; if ((s->state_flags & StateFlag_bounds_check) != 0) { syntax_error(token, "#bounds_check and #no_bounds_check cannot be applied together"); } return s; - } else if (tag == "complete") { - s = parse_stmt(f); - switch (s->kind) { - case Ast_SwitchStmt: - s->SwitchStmt.partial = false; - syntax_warning(token, "#complete is now the default and has been replaced with its opposite: #partial"); - break; - case Ast_TypeSwitchStmt: - s->TypeSwitchStmt.partial = false; - syntax_warning(token, "#complete is now the default and has been replaced with its opposite: #partial"); - break; - default: - syntax_error(token, "#complete can only be applied to a switch statement"); - break; - } - return s; } else if (tag == "partial") { s = parse_stmt(f); switch (s->kind) { @@ -4484,6 +4480,9 @@ Ast *parse_stmt(AstFile *f) { case Ast_TypeSwitchStmt: s->TypeSwitchStmt.partial = true; break; + case Ast_EmptyStmt: + parse_check_directive_for_empty_statement(s, name); + break; default: syntax_error(token, "#partial can only be applied to a switch statement"); break; -- cgit v1.2.3 From b845db16187dc8d609932a0e473e11f53ae31ef7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 1 May 2021 18:26:51 +0100 Subject: Add prototypes for `intrinsics.fixed_point_*` --- core/intrinsics/intrinsics.odin | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index 84d5b06b5..d767f5e47 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -31,6 +31,11 @@ overflow_add :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok --- overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok --- overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok --- +fixed_point_mul :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) --- +fixed_point_div :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) --- +fixed_point_mul_sat :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) --- +fixed_point_div_sat :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) --- + // Compiler Hints expect :: proc(val, expected_val: T) -> T --- -- cgit v1.2.3 From 433d742183eb4e4a9290c0b889bad6dc747dc06c Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 1 May 2021 20:39:00 +0200 Subject: Fix Paeth for bit depth < 8. --- core/image/png/png.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/image/png/png.odin b/core/image/png/png.odin index cfbeb43f5..41ea8e625 100644 --- a/core/image/png/png.odin +++ b/core/image/png/png.odin @@ -1362,7 +1362,7 @@ defilter_less_than_8 :: proc(params: ^Filter_Params) -> (ok: bool) #no_bounds_ch dest[i] = (src[i] + paeth) & 255; } for k in 0..nk { - paeth := filter_paeth(dest[k], up[channels], up[k]); + paeth := filter_paeth(dest[k], up[channels+k], up[k]); dest[channels+k] = (src[channels+k] + paeth) & 255; } case: -- cgit v1.2.3 From 2ad8f99790dda7d021202b9d7830d24d80d4e331 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 1 May 2021 21:56:45 +0200 Subject: ZLIB level 0: LEN/NLEN = i16. --- core/compress/zlib/zlib.odin | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/core/compress/zlib/zlib.odin b/core/compress/zlib/zlib.odin index 5ce1f26ad..252470f7a 100644 --- a/core/compress/zlib/zlib.odin +++ b/core/compress/zlib/zlib.odin @@ -446,7 +446,7 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) -> final = compress.read_bits_lsb(z, 1); type = compress.read_bits_lsb(z, 2); - // log.debugf("Final: %v | Type: %v\n", final, type); + // fmt.printf("Final: %v | Type: %v\n", final, type); switch type { case 0: @@ -455,9 +455,13 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) -> // Discard bits until next byte boundary compress.discard_to_next_byte_lsb(z); - uncompressed_len := int(compress.read_bits_lsb(z, 16)); - length_check := int(compress.read_bits_lsb(z, 16)); - if uncompressed_len != ~length_check { + uncompressed_len := i16(compress.read_bits_lsb(z, 16)); + length_check := i16(compress.read_bits_lsb(z, 16)); + + // fmt.printf("LEN: %v, ~LEN: %v, NLEN: %v, ~NLEN: %v\n", uncompressed_len, ~uncompressed_len, length_check, ~length_check); + + + if ~uncompressed_len != length_check { return E_Deflate.Len_Nlen_Mismatch; } -- cgit v1.2.3 From 52d38ae42b15311102719d25b6f285c43af701bb Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 1 May 2021 22:54:27 +0100 Subject: Make the core:testing runner on windows run in a separate thread to handle crashes in more safe manner --- core/testing/runner.odin | 6 +- core/testing/runner_other.odin | 7 ++ core/testing/runner_windows.odin | 188 +++++++++++++++++++++++++++++++++++++++ core/testing/testing.odin | 7 +- src/main.cpp | 7 +- 5 files changed, 208 insertions(+), 7 deletions(-) create mode 100644 core/testing/runner_other.odin create mode 100644 core/testing/runner_windows.odin diff --git a/core/testing/runner.odin b/core/testing/runner.odin index 7691ef11e..1552cbc12 100644 --- a/core/testing/runner.odin +++ b/core/testing/runner.odin @@ -55,11 +55,9 @@ runner :: proc(internal_tests: []Internal_Test) -> bool { logf(t, "[Test: %s]", it.name); // TODO(bill): Catch panics - { - it.p(t); - } + run_internal_test(t, it); - if t.error_count != 0 { + if failed(t) { logf(t, "[%s : FAILURE]", it.name); } else { logf(t, "[%s : SUCCESS]", it.name); diff --git a/core/testing/runner_other.odin b/core/testing/runner_other.odin new file mode 100644 index 000000000..84d830512 --- /dev/null +++ b/core/testing/runner_other.odin @@ -0,0 +1,7 @@ +//+private +//+build !windows +package testing + +run_internal_test :: proc(t: ^T, it: Internal_Test) { + it.p(t); +} diff --git a/core/testing/runner_windows.odin b/core/testing/runner_windows.odin new file mode 100644 index 000000000..4ab24f39c --- /dev/null +++ b/core/testing/runner_windows.odin @@ -0,0 +1,188 @@ +//+private +//+build windows +package testing + +import win32 "core:sys/windows" +import "core:runtime" +import "core:fmt" +import "intrinsics" + + +Sema :: struct { + count: i32, +} + +sema_reset :: proc "contextless" (s: ^Sema) { + intrinsics.atomic_store(&s.count, 0); +} +sema_wait :: proc "contextless" (s: ^Sema) { + for { + original_count := s.count; + for original_count == 0 { + win32.WaitOnAddress( + &s.count, + &original_count, + size_of(original_count), + win32.INFINITE, + ); + original_count = s.count; + } + if original_count == intrinsics.atomic_cxchg(&s.count, original_count-1, original_count) { + return; + } + } +} + +sema_post :: proc "contextless" (s: ^Sema, count := 1) { + intrinsics.atomic_add(&s.count, i32(count)); + if count == 1 { + win32.WakeByAddressSingle(&s.count); + } else { + win32.WakeByAddressAll(&s.count); + } +} + + +Thread_Proc :: #type proc(^Thread); + +MAX_USER_ARGUMENTS :: 8; + +Thread :: struct { + using specific: Thread_Os_Specific, + procedure: Thread_Proc, + + t: ^T, + it: Internal_Test, + success: bool, + + init_context: Maybe(runtime.Context), + + creation_allocator: runtime.Allocator, +} + +Thread_Os_Specific :: struct { + win32_thread: win32.HANDLE, + win32_thread_id: win32.DWORD, + done: bool, // see note in `is_done` +} + +thread_create :: proc(procedure: Thread_Proc) -> ^Thread { + __windows_thread_entry_proc :: proc "stdcall" (t_: rawptr) -> win32.DWORD { + t := (^Thread)(t_); + context = runtime.default_context(); + c := context; + if ic, ok := t.init_context.?; ok { + c = ic; + } + context = c; + + t.procedure(t); + + if t.init_context == nil { + if context.temp_allocator.data == &runtime.global_default_temp_allocator_data { + runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data); + } + } + + intrinsics.atomic_store(&t.done, true); + return 0; + } + + + thread := new(Thread); + if thread == nil { + return nil; + } + thread.creation_allocator = context.allocator; + + win32_thread_id: win32.DWORD; + win32_thread := win32.CreateThread(nil, 0, __windows_thread_entry_proc, thread, win32.CREATE_SUSPENDED, &win32_thread_id); + if win32_thread == nil { + free(thread, thread.creation_allocator); + return nil; + } + thread.procedure = procedure; + thread.win32_thread = win32_thread; + thread.win32_thread_id = win32_thread_id; + thread.init_context = context; + + return thread; +} + +thread_start :: proc "contextless" (thread: ^Thread) { + win32.ResumeThread(thread.win32_thread); +} + +thread_join_and_destroy :: proc(thread: ^Thread) { + if thread.win32_thread != win32.INVALID_HANDLE { + win32.WaitForSingleObject(thread.win32_thread, win32.INFINITE); + win32.CloseHandle(thread.win32_thread); + thread.win32_thread = win32.INVALID_HANDLE; + } + free(thread, thread.creation_allocator); +} + +thread_terminate :: proc "contextless" (thread: ^Thread, exit_code: int) { + win32.TerminateThread(thread.win32_thread, u32(exit_code)); +} + + + + +global_threaded_runner_semaphore: Sema; +global_exception_handler: rawptr; +global_current_thread: ^Thread; +global_current_t: ^T; + +run_internal_test :: proc(t: ^T, it: Internal_Test) { + thread := thread_create(proc(thread: ^Thread) { + exception_handler_proc :: proc "stdcall" (ExceptionInfo: ^win32.EXCEPTION_POINTERS) -> win32.LONG { + switch ExceptionInfo.ExceptionRecord.ExceptionCode { + case + win32.EXCEPTION_DATATYPE_MISALIGNMENT, + win32.EXCEPTION_BREAKPOINT, + win32.EXCEPTION_ACCESS_VIOLATION, + win32.EXCEPTION_ILLEGAL_INSTRUCTION, + win32.EXCEPTION_ARRAY_BOUNDS_EXCEEDED, + win32.EXCEPTION_STACK_OVERFLOW: + + sema_post(&global_threaded_runner_semaphore); + return win32.EXCEPTION_EXECUTE_HANDLER; + } + + return win32.EXCEPTION_CONTINUE_SEARCH; + } + global_exception_handler = win32.AddVectoredExceptionHandler(0, exception_handler_proc); + + context.assertion_failure_proc = proc(prefix, message: string, loc: runtime.Source_Code_Location) { + errorf(t=global_current_t, format="%s %s", args={prefix, message}, loc=loc); + intrinsics.debug_trap(); + }; + + thread.it.p(thread.t); + + thread.success = true; + sema_post(&global_threaded_runner_semaphore); + }); + + sema_reset(&global_threaded_runner_semaphore); + global_current_t = t; + + thread.t = t; + thread.it = it; + thread.success = false; + + thread_start(thread); + + sema_wait(&global_threaded_runner_semaphore); + thread_terminate(thread, int(!thread.success)); + thread_join_and_destroy(thread); + + win32.RemoveVectoredExceptionHandler(global_exception_handler); + + if !thread.success && t.error_count == 0 { + t.error_count += 1; + } + + return; +} diff --git a/core/testing/testing.odin b/core/testing/testing.odin index a431d8575..8a32ce7c8 100644 --- a/core/testing/testing.odin +++ b/core/testing/testing.odin @@ -29,12 +29,15 @@ T :: struct { error :: proc(t: ^T, args: ..any, loc := #caller_location) { - log(t=t, args=args, loc=loc); + fmt.wprintf(t.w, "%v: ", loc); + fmt.wprintln(t.w, ..args); t.error_count += 1; } errorf :: proc(t: ^T, format: string, args: ..any, loc := #caller_location) { - logf(t=t, format=format, args=args, loc=loc); + fmt.wprintf(t.w, "%v: ", loc); + fmt.wprintf(t.w, format, ..args); + fmt.wprintln(t.w); t.error_count += 1; } diff --git a/src/main.cpp b/src/main.cpp index 117c4e549..abc90d627 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1554,7 +1554,7 @@ void print_show_help(String const arg0, String const &command) { } else if (command == "check") { print_usage_line(1, "check parse and type check .odin file"); } else if (command == "test") { - print_usage_line(1, "test build ands runs 'test_*' procedures in the initial package"); + print_usage_line(1, "test build ands runs procedures with the attribute @(test) in the initial package"); } else if (command == "query") { print_usage_line(1, "query [experimental] parse, type check, and output a .json file containing information about the program"); } else if (command == "doc") { @@ -1662,6 +1662,11 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(3, "-build-mode:shared Build as a dynamically linked library"); print_usage_line(3, "-build-mode:obj Build as an object file"); print_usage_line(3, "-build-mode:object Build as an object file"); + print_usage_line(3, "-build-mode:assembly Build as an object file"); + print_usage_line(3, "-build-mode:assembler Build as an assembly file"); + print_usage_line(3, "-build-mode:asm Build as an assembly file"); + print_usage_line(3, "-build-mode:llvm-ir Build as an LLVM IR file"); + print_usage_line(3, "-build-mode:llvm Build as an LLVM IR file"); print_usage_line(0, ""); } -- cgit v1.2.3 From 364e6c9573f35cb32be50cabb898ceb2afa57b88 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 1 May 2021 22:58:13 +0100 Subject: Move comment --- core/testing/runner.odin | 1 - core/testing/runner_other.odin | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/core/testing/runner.odin b/core/testing/runner.odin index 1552cbc12..e3286988c 100644 --- a/core/testing/runner.odin +++ b/core/testing/runner.odin @@ -54,7 +54,6 @@ runner :: proc(internal_tests: []Internal_Test) -> bool { logf(t, "[Test: %s]", it.name); - // TODO(bill): Catch panics run_internal_test(t, it); if failed(t) { diff --git a/core/testing/runner_other.odin b/core/testing/runner_other.odin index 84d830512..0bd95e10a 100644 --- a/core/testing/runner_other.odin +++ b/core/testing/runner_other.odin @@ -3,5 +3,6 @@ package testing run_internal_test :: proc(t: ^T, it: Internal_Test) { + // TODO(bill): Catch panics on other platforms it.p(t); } -- cgit v1.2.3 From cf0bf1a7cb395cb334cc456d30af3224fa607ca0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 1 May 2021 23:06:14 +0100 Subject: Add `testing.fail_now` --- core/testing/runner_windows.odin | 6 +++++- core/testing/testing.odin | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/core/testing/runner_windows.odin b/core/testing/runner_windows.odin index 4ab24f39c..4b600218a 100644 --- a/core/testing/runner_windows.odin +++ b/core/testing/runner_windows.odin @@ -156,7 +156,7 @@ run_internal_test :: proc(t: ^T, it: Internal_Test) { context.assertion_failure_proc = proc(prefix, message: string, loc: runtime.Source_Code_Location) { errorf(t=global_current_t, format="%s %s", args={prefix, message}, loc=loc); - intrinsics.debug_trap(); + intrinsics.trap(); }; thread.it.p(thread.t); @@ -168,6 +168,10 @@ run_internal_test :: proc(t: ^T, it: Internal_Test) { sema_reset(&global_threaded_runner_semaphore); global_current_t = t; + t._fail_now = proc() -> ! { + intrinsics.trap(); + }; + thread.t = t; thread.it = it; thread.success = false; diff --git a/core/testing/testing.odin b/core/testing/testing.odin index 8a32ce7c8..ec47ca4d4 100644 --- a/core/testing/testing.odin +++ b/core/testing/testing.odin @@ -25,6 +25,8 @@ T :: struct { w: io.Writer, cleanups: [dynamic]Internal_Cleanup, + + _fail_now: proc() -> !, } @@ -46,6 +48,13 @@ fail :: proc(t: ^T) { t.error_count += 1; } +fail_now :: proc(t: ^T) { + fail(t); + if t._fail_now != nil { + t._fail_now(); + } +} + failed :: proc(t: ^T) -> bool { return t.error_count != 0; } -- cgit v1.2.3 From 9854dbe8897b3722e571a23b699c234cfc0baa83 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 1 May 2021 23:14:14 +0100 Subject: Remove unused import --- core/testing/runner_windows.odin | 1 - 1 file changed, 1 deletion(-) diff --git a/core/testing/runner_windows.odin b/core/testing/runner_windows.odin index 4b600218a..d8633f703 100644 --- a/core/testing/runner_windows.odin +++ b/core/testing/runner_windows.odin @@ -4,7 +4,6 @@ package testing import win32 "core:sys/windows" import "core:runtime" -import "core:fmt" import "intrinsics" -- cgit v1.2.3 From 348d25c43aa8b0ffe1070bec70b56d06191a1a3d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 2 May 2021 00:11:36 +0100 Subject: Fix parsing error for `for` --- src/parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.cpp b/src/parser.cpp index 12f0fb9be..c6690990f 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3975,7 +3975,7 @@ Ast *parse_for_stmt(AstFile *f) { } } - if (!is_range && allow_token(f, Token_Semicolon)) { + if (!is_range && parse_control_statement_semicolon_separator(f)) { init = cond; cond = nullptr; if (f->curr_token.kind != Token_Semicolon) { -- cgit v1.2.3 From 2451014b6e1758355449b78970bf5f84ec9b96d4 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 2 May 2021 13:23:57 +0200 Subject: datetime_to_time's ok should default to true. --- core/time/time.odin | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/core/time/time.odin b/core/time/time.odin index 00d7e529a..c75549b17 100644 --- a/core/time/time.odin +++ b/core/time/time.odin @@ -262,19 +262,18 @@ datetime_to_time :: proc(year, month, day, hour, minute, second: int, nsec := in return; } + ok = true; + _y := year - 1970; _m := month - 1; _d := day - 1; - if _m < 0 || _m > 11 { + if month < 1 || month > 12 { _m %= 12; ok = false; } - if _d < 0 || _m > 30 { + if day < 1 || day > 31 { _d %= 31; ok = false; } - if _m < 0 || _m > 11 { - _m %= 12; ok = false; - } s := i64(0); div, mod := divmod(_y, 400); -- cgit v1.2.3 From 7d534769d6437cb63eeb4718bc8ed36ffef937f9 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 2 May 2021 20:38:30 +0200 Subject: Add new PNG post processing options. --- core/compress/common.odin | 2 +- core/image/common.odin | 129 +++++++++++++++++++++++++++++++++++++++++----- core/image/png/png.odin | 29 ++++++++++- 3 files changed, 143 insertions(+), 17 deletions(-) diff --git a/core/compress/common.odin b/core/compress/common.odin index 11b5b74b3..ffdc2d208 100644 --- a/core/compress/common.odin +++ b/core/compress/common.odin @@ -19,7 +19,7 @@ Error :: union { This is here because png.load will return a this type of error union, as it may involve an I/O error, a Deflate error, etc. */ - image.PNG_Error, + image.Error, } General_Error :: enum { diff --git a/core/image/common.odin b/core/image/common.odin index d05feabaa..6630dfd5b 100644 --- a/core/image/common.odin +++ b/core/image/common.odin @@ -1,6 +1,9 @@ package image import "core:bytes" +import "core:mem" + +import "core:fmt" Image :: struct { width: int, @@ -17,49 +20,69 @@ Image :: struct { sidecar: any, } +/* + IMPORTANT: `.do_not_expand_*` options currently skip handling of the `alpha_*` options, + therefore Gray+Alpha will be returned as such even if you add `.alpha_drop_if_present`, + and `.alpha_add_if_missing` and keyed transparency will likewise be ignored. + + The same goes for indexed images. This will be remedied in a near future update. +*/ + /* Image_Option: `.info` - This option behaves as `return_ihdr` and `do_not_decompress_image` and can be used + This option behaves as `.return_ihdr` and `.do_not_decompress_image` and can be used to gather an image's dimensions and color information. `.return_header` Fill out img.sidecar.header with the image's format-specific header struct. - If we only care about the image specs, we can set `return_header` + - `do_not_decompress_image`, or `.info`, which works as if both of these were set. + If we only care about the image specs, we can set `.return_header` + + `.do_not_decompress_image`, or `.info`, which works as if both of these were set. `.return_metadata` Returns all chunks not needed to decode the data. - It also returns the header as if `.return_header` is set. + It also returns the header as if `.return_header` was set. - `do_not_decompress_image` + `.do_not_decompress_image` Skip decompressing IDAT chunk, defiltering and the rest. - `alpha_add_if_missing` + `.do_not_expand_grayscale` + Do not turn grayscale (+ Alpha) images into RGB(A). + Returns just the 1 or 2 channels present, although 1, 2 and 4 bit are still scaled to 8-bit. + + `.do_not_expand_indexed` + Do not turn indexed (+ Alpha) images into RGB(A). + Returns just the 1 or 2 (with `tRNS`) channels present. + Make sure to use `return_metadata` to also return the palette chunk so you can recolor it yourself. + + `.do_not_expand_channels` + Applies both `.do_not_expand_grayscale` and `.do_not_expand_indexed`. + + `.alpha_add_if_missing` If the image has no alpha channel, it'll add one set to max(type). Turns RGB into RGBA and Gray into Gray+Alpha - `alpha_drop_if_present` + `.alpha_drop_if_present` If the image has an alpha channel, drop it. - You may want to use `alpha_premultiply` in this case. + You may want to use `.alpha_premultiply` in this case. NOTE: For PNG, this also skips handling of the tRNS chunk, if present, unless you select `alpha_premultiply`. In this case it'll premultiply the specified pixels in question only, as the others are implicitly fully opaque. - `alpha_premultiply` + `.alpha_premultiply` If the image has an alpha channel, returns image data as follows: RGB *= A, Gray = Gray *= A - `blend_background` + `.blend_background` If a bKGD chunk is present in a PNG, we normally just set `img.background` with its value and leave it up to the application to decide how to display the image, as per the PNG specification. - With `blend_background` selected, we blend the image against the background + With `.blend_background` selected, we blend the image against the background color. As this negates the use for an alpha channel, we'll drop it _unless_ - you also specify `alpha_add_if_missing`. + you also specify `.alpha_add_if_missing`. Options that don't apply to an image format will be ignored by their loader. */ @@ -73,10 +96,14 @@ Option :: enum { alpha_drop_if_present, alpha_premultiply, blend_background, + // Unimplemented + do_not_expand_grayscale, + do_not_expand_indexed, + do_not_expand_channels, } Options :: distinct bit_set[Option]; -PNG_Error :: enum { +Error :: enum { Invalid_PNG_Signature, IHDR_Not_First_Chunk, IHDR_Corrupt, @@ -93,9 +120,10 @@ PNG_Error :: enum { Invalid_Color_Bit_Depth_Combo, Unknown_Filter_Method, Unknown_Interlace_Method, + Requested_Channel_Not_Present, + Post_Processing_Error, } - /* Functions to help with image buffer calculations */ @@ -104,4 +132,77 @@ compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes size = ((((channels * width * depth) + 7) >> 3) + extra_row_bytes) * height; return; +} + +/* + For when you have an RGB(A) image, but want a particular channel. +*/ + +Channel :: enum u8 { + R = 1, + G = 2, + B = 3, + A = 4, +} + +return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok: bool) { + + ok = false; + t: bytes.Buffer; + + idx := int(channel); + + if idx > img.channels { + return {}, false; + } + + if img.channels == 2 && idx == 4 { + // Alpha requested, which in a two channel image is index 2: G. + idx = 2; + } + + switch(img.depth) { + case 8: + buffer_size := compute_buffer_size(img.width, img.height, 1, 8); + t = bytes.Buffer{}; + resize(&t.buf, buffer_size); + + i := bytes.buffer_to_bytes(&img.pixels); + o := bytes.buffer_to_bytes(&t); + + for len(i) > 0 { + o[0] = i[idx]; + i = i[img.channels:]; + o = o[1:]; + } + case 16: + buffer_size := compute_buffer_size(img.width, img.height, 2, 8); + t = bytes.Buffer{}; + resize(&t.buf, buffer_size); + + i := mem.slice_data_cast([]u16, img.pixels.buf[:]); + o := mem.slice_data_cast([]u16, t.buf[:]); + + for len(i) > 0 { + o[0] = i[idx]; + i = i[img.channels:]; + o = o[1:]; + } + case 1, 2, 4: + // We shouldn't see this case, as the loader already turns these into 8-bit. + return {}, false; + } + + res = new(Image); + res.width = img.width; + res.height = img.height; + res.channels = 1; + res.depth = img.depth; + res.pixels = t; + res.background = img.background; + res.sidecar = img.sidecar; + + fmt.println(t); + + return res, true; } \ No newline at end of file diff --git a/core/image/png/png.odin b/core/image/png/png.odin index 41ea8e625..912b31300 100644 --- a/core/image/png/png.odin +++ b/core/image/png/png.odin @@ -14,7 +14,7 @@ import "core:intrinsics" Error :: compress.Error; E_General :: compress.General_Error; -E_PNG :: image.PNG_Error; +E_PNG :: image.Error; E_Deflate :: compress.Deflate_Error; is_kind :: compress.is_kind; @@ -382,13 +382,17 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c options := options; if .info in options { options |= {.return_metadata, .do_not_decompress_image}; - options ~= {.info}; + options -= {.info}; } if .alpha_drop_if_present in options && .alpha_add_if_missing in options { return {}, E_General.Incompatible_Options; } + if .do_not_expand_channels in options { + options |= {.do_not_expand_grayscale, .do_not_expand_indexed}; + } + if img == nil { img = new(Image); } @@ -723,6 +727,14 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c will become the default. */ + if .Paletted in header.color_type && .do_not_expand_indexed in options { + return img, E_General.OK; + } + if .Color not_in header.color_type && .do_not_expand_grayscale in options { + return img, E_General.OK; + } + + raw_image_channels := img.channels; out_image_channels := 3; @@ -1218,6 +1230,19 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c unreachable("We should never see bit depths other than 8, 16 and 'Paletted' here."); } + // TODO: Rather than first expanding to RGB(A) and then dropping channels, give these their own path. + if .do_not_expand_grayscale in options && .Color not_in info.header.color_type { + + single, single_ok := image.return_single_channel(img, .R); + if single_ok { + destroy(img); + img = single; + } else { + destroy(single); + return img, E_PNG.Post_Processing_Error; + } + } + return img, E_General.OK; } -- cgit v1.2.3 From 3160a6a12c5437413afcae70a893ec7debb91893 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 2 May 2021 21:11:06 +0200 Subject: Don't need other path for grayscale output. --- core/image/png/png.odin | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/core/image/png/png.odin b/core/image/png/png.odin index 912b31300..716185c5f 100644 --- a/core/image/png/png.odin +++ b/core/image/png/png.odin @@ -1230,19 +1230,6 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c unreachable("We should never see bit depths other than 8, 16 and 'Paletted' here."); } - // TODO: Rather than first expanding to RGB(A) and then dropping channels, give these their own path. - if .do_not_expand_grayscale in options && .Color not_in info.header.color_type { - - single, single_ok := image.return_single_channel(img, .R); - if single_ok { - destroy(img); - img = single; - } else { - destroy(single); - return img, E_PNG.Post_Processing_Error; - } - } - return img, E_General.OK; } -- cgit v1.2.3 From 448f834b28afa4bb73e82f76c4cfec9c0b45d42e Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 3 May 2021 01:23:03 +0200 Subject: Remove debug print in image helper. --- core/image/common.odin | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/core/image/common.odin b/core/image/common.odin index 6630dfd5b..e92060ed2 100644 --- a/core/image/common.odin +++ b/core/image/common.odin @@ -3,8 +3,6 @@ package image import "core:bytes" import "core:mem" -import "core:fmt" - Image :: struct { width: int, height: int, @@ -202,7 +200,5 @@ return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok res.background = img.background; res.sidecar = img.sidecar; - fmt.println(t); - return res, true; -} \ No newline at end of file +} -- cgit v1.2.3 From 77e2e1e1d02bf1755004f3a92457b2850a91f80c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 3 May 2021 12:02:08 +0100 Subject: Add missing `skip_possible_newline_for_literal` calls --- src/parser.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/parser.cpp b/src/parser.cpp index c6690990f..d048e78e7 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2402,6 +2402,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { } + skip_possible_newline_for_literal(f); Token open = expect_token_after(f, Token_OpenBrace, "union"); while (f->curr_token.kind != Token_CloseBrace && @@ -2426,6 +2427,8 @@ Ast *parse_operand(AstFile *f, bool lhs) { if (f->curr_token.kind != Token_OpenBrace) { base_type = parse_type(f); } + + skip_possible_newline_for_literal(f); Token open = expect_token(f, Token_OpenBrace); Array values = parse_element_list(f); @@ -2517,6 +2520,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { } } + skip_possible_newline_for_literal(f); Token open = expect_token(f, Token_OpenBrace); Ast *asm_string = parse_expr(f, false); expect_token(f, Token_Comma); @@ -2987,7 +2991,7 @@ Ast *parse_foreign_block(AstFile *f, Token token) { defer (f->in_foreign_block = prev_in_foreign_block); f->in_foreign_block = true; - + skip_possible_newline_for_literal(f); open = expect_token(f, Token_OpenBrace); while (f->curr_token.kind != Token_CloseBrace && -- cgit v1.2.3 From 518ecaf9c909bf2fd372fd758a5075f603b07d75 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 3 May 2021 13:17:16 +0100 Subject: Allow `union`s to be comparable if all their variants are comparable --- core/runtime/core.odin | 3 +++ src/llvm_backend.cpp | 71 +++++++++++++++++++++++++++++++++++++++++++++----- src/types.cpp | 18 ++++++++++++- 3 files changed, 85 insertions(+), 7 deletions(-) diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 5f016579f..cb526ed2d 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -121,6 +121,9 @@ Type_Info_Union :: struct { variants: []^Type_Info, tag_offset: uintptr, tag_type: ^Type_Info, + + equal: Equal_Proc, // set only when the struct has .Comparable set but does not have .Simple_Compare set + custom_align: bool, no_nil: bool, maybe: bool, diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index de202658a..29a181f94 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -10259,7 +10259,7 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) { lb_start_block(p, block_diff_ptr); - if (type->kind == Type_Struct) { + if (type->kind == Type_Struct) { type_set_offsets(type); lbBlock *block_false = lb_create_block(p, "bfalse"); @@ -10285,6 +10285,56 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) { lb_start_block(p, block_false); LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_bool), 0, false)); + } else if (type->kind == Type_Union) { + if (is_type_union_maybe_pointer(type)) { + Type *v = type->Union.variants[0]; + Type *pv = alloc_type_pointer(v); + + lbValue left = lb_emit_load(p, lb_emit_conv(p, lhs, pv)); + lbValue right = lb_emit_load(p, lb_emit_conv(p, rhs, pv)); + + lbValue ok = lb_emit_comp(p, Token_CmpEq, left, right); + ok = lb_emit_conv(p, ok, t_bool); + LLVMBuildRet(p->builder, ok.value); + } else { + lbBlock *block_false = lb_create_block(p, "bfalse"); + lbBlock *block_switch = lb_create_block(p, "bswitch"); + + lbValue left_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, lhs)); + lbValue right_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, rhs)); + + lbValue tag_eq = lb_emit_comp(p, Token_CmpEq, left_tag, right_tag); + lb_emit_if(p, tag_eq, block_switch, block_false); + + lb_start_block(p, block_switch); + LLVMValueRef v_switch = LLVMBuildSwitch(p->builder, left_tag.value, block_false->block, cast(unsigned)type->Union.variants.count); + + + for_array(i, type->Union.variants) { + lbBlock *case_block = lb_create_block(p, "bcase"); + lb_start_block(p, case_block); + + Type *v = type->Union.variants[i]; + lbValue tag = lb_const_union_tag(p->module, type, v); + + Type *vp = alloc_type_pointer(v); + + lbValue left = lb_emit_load(p, lb_emit_conv(p, lhs, vp)); + lbValue right = lb_emit_load(p, lb_emit_conv(p, rhs, vp)); + lbValue ok = lb_emit_comp(p, Token_CmpEq, left, right); + ok = lb_emit_conv(p, ok, t_bool); + + LLVMBuildRet(p->builder, ok.value); + + + LLVMAddCase(v_switch, tag.value, case_block->block); + } + + lb_start_block(p, block_false); + + LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_bool), 0, false)); + } + } else { lbValue left = lb_emit_load(p, lhs); lbValue right = lb_emit_load(p, rhs); @@ -10565,7 +10615,7 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri } - if (is_type_struct(a) && is_type_comparable(a)) { + if ((is_type_struct(a) || is_type_union(b)) && is_type_comparable(a)) { lbValue left_ptr = lb_address_from_load_or_generate_local(p, left); lbValue right_ptr = lb_address_from_load_or_generate_local(p, right); lbValue res = {}; @@ -13467,7 +13517,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_union_ptr); { - LLVMValueRef vals[6] = {}; + LLVMValueRef vals[7] = {}; isize variant_count = gb_max(0, t->Union.variants.count); lbValue memory_types = lb_type_info_member_types_offset(p, variant_count); @@ -13496,10 +13546,19 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da vals[2] = LLVMConstNull(lb_type(m, t_type_info_ptr)); } - vals[3] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value; - vals[4] = lb_const_bool(m, t_bool, t->Union.no_nil).value; - vals[5] = lb_const_bool(m, t_bool, t->Union.maybe).value; + if (is_type_comparable(t) && !is_type_simple_compare(t)) { + vals[3] = lb_get_equal_proc_for_type(m, t).value; + } + + vals[4] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value; + vals[5] = lb_const_bool(m, t_bool, t->Union.no_nil).value; + vals[6] = lb_const_bool(m, t_bool, t->Union.maybe).value; + for (isize i = 0; i < gb_count_of(vals); i++) { + if (vals[i] == nullptr) { + vals[i] = LLVMConstNull(lb_type(m, get_struct_field_type(tag.type, i))); + } + } lbValue res = {}; res.type = type_deref(tag.type); diff --git a/src/types.cpp b/src/types.cpp index 7b5b81c43..5e4370f8a 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1544,6 +1544,9 @@ bool is_type_valid_for_keys(Type *t) { if (is_type_untyped(t)) { return false; } + if (t->kind == Type_Union) { + return false; + } return is_type_comparable(t); } @@ -1915,6 +1918,18 @@ bool is_type_comparable(Type *t) { } } return true; + + case Type_Union: + if (type_size_of(t) == 0) { + return false; + } + for_array(i, t->Union.variants) { + Type *v = t->Union.variants[i]; + if (!is_type_comparable(v)) { + return false; + } + } + return true; } return false; } @@ -1959,7 +1974,8 @@ bool is_type_simple_compare(Type *t) { return false; } } - return true; + // make it dumb on purpose + return t->Union.variants.count == 1; case Type_SimdVector: return is_type_simple_compare(t->SimdVector.elem); -- cgit v1.2.3 From 1a3784c4df312df72f15b179e083d83818e08def Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 3 May 2021 13:38:15 +0100 Subject: Allow unions which are comparable to also be valid map keys (i.e. hashable) --- src/check_type.cpp | 6 ++++++ src/llvm_backend.cpp | 39 +++++++++++++++++++++++++++++++++++++-- src/types.cpp | 3 --- 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/check_type.cpp b/src/check_type.cpp index aef1ddc7a..f70230682 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2109,6 +2109,12 @@ void add_map_key_type_dependencies(CheckerContext *ctx, Type *key) { Entity *field = key->Struct.fields[i]; add_map_key_type_dependencies(ctx, field->type); } + } else if (key->kind == Type_Union) { + add_package_dependency(ctx, "runtime", "default_hasher_n"); + for_array(i, key->Union.variants) { + Type *v = key->Union.variants[i]; + add_map_key_type_dependencies(ctx, v); + } } else if (key->kind == Type_EnumeratedArray) { add_package_dependency(ctx, "runtime", "default_hasher_n"); add_map_key_type_dependencies(ctx, key->EnumeratedArray.elem); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 29a181f94..e931d1ce9 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -10315,7 +10315,7 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) { lb_start_block(p, case_block); Type *v = type->Union.variants[i]; - lbValue tag = lb_const_union_tag(p->module, type, v); + lbValue case_tag = lb_const_union_tag(p->module, type, v); Type *vp = alloc_type_pointer(v); @@ -10327,7 +10327,7 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) { LLVMBuildRet(p->builder, ok.value); - LLVMAddCase(v_switch, tag.value, case_block->block); + LLVMAddCase(v_switch, case_tag.value, case_block->block); } lb_start_block(p, block_false); @@ -10403,6 +10403,9 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) { lbValue data = {x, t_rawptr}; lbValue seed = {y, t_uintptr}; + LLVMAttributeRef nonnull_attr = lb_create_enum_attribute(m->ctx, "nonnull"); + LLVMAddAttributeAtIndex(p->value, 1+0, nonnull_attr); + if (is_type_simple_compare(type)) { lbValue res = lb_simple_compare_hash(p, type, data, seed); LLVMBuildRet(p->builder, res.value); @@ -10425,6 +10428,38 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) { seed = lb_emit_call(p, field_hasher, args); } LLVMBuildRet(p->builder, seed.value); + } else if (type->kind == Type_Union) { + lbBlock *end_block = lb_create_block(p, "bend"); + + data = lb_emit_conv(p, data, pt); + + lbValue tag_ptr = lb_emit_union_tag_ptr(p, data); + lbValue tag = lb_emit_load(p, tag_ptr); + + LLVMValueRef v_switch = LLVMBuildSwitch(p->builder, tag.value, end_block->block, cast(unsigned)type->Union.variants.count); + + auto args = array_make(permanent_allocator(), 2); + for_array(i, type->Union.variants) { + lbBlock *case_block = lb_create_block(p, "bcase"); + lb_start_block(p, case_block); + + Type *v = type->Union.variants[i]; + Type *vp = alloc_type_pointer(v); + lbValue case_tag = lb_const_union_tag(p->module, type, v); + + lbValue variant_hasher = lb_get_hasher_proc_for_type(m, v); + + args[0] = data; + args[1] = seed; + lbValue res = lb_emit_call(p, variant_hasher, args); + LLVMBuildRet(p->builder, res.value); + + LLVMAddCase(v_switch, case_tag.value, case_block->block); + } + + lb_start_block(p, end_block); + LLVMBuildRet(p->builder, seed.value); + } else if (type->kind == Type_Array) { lbAddr pres = lb_add_local_generated(p, t_uintptr, false); lb_addr_store(p, pres, seed); diff --git a/src/types.cpp b/src/types.cpp index 5e4370f8a..3fafa00d5 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1544,9 +1544,6 @@ bool is_type_valid_for_keys(Type *t) { if (is_type_untyped(t)) { return false; } - if (t->kind == Type_Union) { - return false; - } return is_type_comparable(t); } -- cgit v1.2.3 From 050f128554d49d69d6411e4e898541ecfe5f8a33 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 3 May 2021 13:40:36 +0100 Subject: Correct hashing for union #maybe --- src/llvm_backend.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index e931d1ce9..92cc40094 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -10429,8 +10429,19 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) { } LLVMBuildRet(p->builder, seed.value); } else if (type->kind == Type_Union) { - lbBlock *end_block = lb_create_block(p, "bend"); + auto args = array_make(permanent_allocator(), 2); + + if (is_type_union_maybe_pointer(type)) { + Type *v = type->Union.variants[0]; + lbValue variant_hasher = lb_get_hasher_proc_for_type(m, v); + + args[0] = data; + args[1] = seed; + lbValue res = lb_emit_call(p, variant_hasher, args); + LLVMBuildRet(p->builder, res.value); + } + lbBlock *end_block = lb_create_block(p, "bend"); data = lb_emit_conv(p, data, pt); lbValue tag_ptr = lb_emit_union_tag_ptr(p, data); @@ -10438,7 +10449,6 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) { LLVMValueRef v_switch = LLVMBuildSwitch(p->builder, tag.value, end_block->block, cast(unsigned)type->Union.variants.count); - auto args = array_make(permanent_allocator(), 2); for_array(i, type->Union.variants) { lbBlock *case_block = lb_create_block(p, "bcase"); lb_start_block(p, case_block); -- cgit v1.2.3 From 357f66fceeb6cbc3b72281300dbe7af4e2b68715 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 3 May 2021 13:45:17 +0100 Subject: Fix typo --- src/llvm_backend.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 92cc40094..55ffb09c3 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -10660,7 +10660,7 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri } - if ((is_type_struct(a) || is_type_union(b)) && is_type_comparable(a)) { + if ((is_type_struct(a) || is_type_union(a)) && is_type_comparable(a)) { lbValue left_ptr = lb_address_from_load_or_generate_local(p, left); lbValue right_ptr = lb_address_from_load_or_generate_local(p, right); lbValue res = {}; -- cgit v1.2.3 From 59b3c472ca1ea4e56728eb81c3c8b595a4b1bcdd Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 3 May 2021 15:08:34 +0200 Subject: Convert `core:compress` and `core:image` error checks to new union comparison. No more need for `is_kind(err, Error_Value)`, just test err == Error_Value. --- core/compress/common.odin | 6 ------ core/compress/gzip/example.odin | 6 +++--- core/compress/gzip/gzip.odin | 3 +-- core/compress/zlib/example.odin | 2 +- core/compress/zlib/zlib.odin | 29 ++++++++++++++--------------- core/image/png/example.odin | 4 ++-- core/image/png/helpers.odin | 8 ++++---- core/image/png/png.odin | 21 ++++++++++----------- 8 files changed, 35 insertions(+), 44 deletions(-) diff --git a/core/compress/common.odin b/core/compress/common.odin index ffdc2d208..de1894f61 100644 --- a/core/compress/common.odin +++ b/core/compress/common.odin @@ -3,12 +3,6 @@ package compress import "core:io" import "core:image" -// Error helper, e.g. is_kind(err, General_Error.OK); -is_kind :: proc(u: $U, x: $V) -> bool { - v, ok := u.(V); - return ok && v == x; -} - Error :: union { General_Error, Deflate_Error, diff --git a/core/compress/gzip/example.odin b/core/compress/gzip/example.odin index 8e0f53cf8..344521489 100644 --- a/core/compress/gzip/example.odin +++ b/core/compress/gzip/example.odin @@ -35,7 +35,7 @@ main :: proc() { if len(args) < 2 { stderr("No input file specified.\n"); err := gzip.load(TEST, &buf); - if gzip.is_kind(err, gzip.E_General.OK) { + if err != E_General.OK { stdout("Displaying test vector: "); stdout(bytes.buffer_to_string(&buf)); stdout("\n"); @@ -54,8 +54,8 @@ main :: proc() { } else { err = gzip.load(file, &buf); } - if !gzip.is_kind(err, gzip.E_General.OK) { - if gzip.is_kind(err, gzip.E_General.File_Not_Found) { + if err != gzip.E_General.OK { + if err != E_General.File_Not_Found { stderr("File not found: "); stderr(file); stderr("\n"); diff --git a/core/compress/gzip/gzip.odin b/core/compress/gzip/gzip.odin index f86d3ddce..2284953d1 100644 --- a/core/compress/gzip/gzip.odin +++ b/core/compress/gzip/gzip.odin @@ -94,7 +94,6 @@ E_General :: compress.General_Error; E_GZIP :: compress.GZIP_Error; E_ZLIB :: compress.ZLIB_Error; E_Deflate :: compress.Deflate_Error; -is_kind :: compress.is_kind; load_from_slice :: proc(slice: []u8, buf: ^bytes.Buffer, allocator := context.allocator) -> (err: Error) { @@ -278,7 +277,7 @@ load_from_stream :: proc(stream: io.Stream, buf: ^bytes.Buffer, allocator := con // fmt.printf("ZLIB returned: %v\n", zlib_error); - if !is_kind(zlib_error, E_General.OK) || zlib_error == nil { + if zlib_error != E_General.OK || zlib_error == nil { return zlib_error; } diff --git a/core/compress/zlib/example.odin b/core/compress/zlib/example.odin index 0906d7eef..65b78c70c 100644 --- a/core/compress/zlib/example.odin +++ b/core/compress/zlib/example.odin @@ -33,7 +33,7 @@ main :: proc() { err := zlib.inflate(ODIN_DEMO, &buf); defer bytes.buffer_destroy(&buf); - if !zlib.is_kind(err, zlib.E_General.OK) { + if err != zlib.E_General.OK { fmt.printf("\nError: %v\n", err); } s := bytes.buffer_to_string(&buf); diff --git a/core/compress/zlib/zlib.odin b/core/compress/zlib/zlib.odin index 252470f7a..c5097a320 100644 --- a/core/compress/zlib/zlib.odin +++ b/core/compress/zlib/zlib.odin @@ -8,7 +8,7 @@ import "core:bytes" import "core:hash" /* zlib.inflate decompresses a ZLIB stream passed in as a []u8 or io.Stream. - Returns: Error. You can use zlib.is_kind or compress.is_kind to easily test for OK. + Returns: Error. */ Context :: compress.Context; @@ -34,7 +34,6 @@ Error :: compress.Error; E_General :: compress.General_Error; E_ZLIB :: compress.ZLIB_Error; E_Deflate :: compress.Deflate_Error; -is_kind :: compress.is_kind; DEFLATE_MAX_CHUNK_SIZE :: 65535; DEFLATE_MAX_LITERAL_SIZE :: 65535; @@ -256,7 +255,7 @@ decode_huffman :: proc(z: ^Context, t: ^Huffman_Table) -> (r: u16, err: Error) # parse_huffman_block :: proc(z: ^Context, z_repeat, z_offset: ^Huffman_Table) -> (err: Error) #no_bounds_check { #no_bounds_check for { value, e := decode_huffman(z, z_repeat); - if !is_kind(e, E_General.OK) { + if e != E_General.OK { return err; } if value < 256 { @@ -277,7 +276,7 @@ parse_huffman_block :: proc(z: ^Context, z_repeat, z_offset: ^Huffman_Table) -> } value, e = decode_huffman(z, z_offset); - if !is_kind(e, E_General.OK) { + if e != E_General.OK { return E_Deflate.Bad_Huffman_Code; } @@ -390,7 +389,7 @@ inflate_from_stream :: proc(using ctx: ^Context, raw := false, allocator := cont // Parse ZLIB stream without header. err = inflate_raw(ctx); - if !is_kind(err, E_General.OK) { + if err != E_General.OK { return err; } @@ -418,15 +417,15 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) -> codelength_ht: ^Huffman_Table; z_repeat, err = allocate_huffman_table(allocator=context.allocator); - if !is_kind(err, E_General.OK) { + if err != E_General.OK { return err; } z_offset, err = allocate_huffman_table(allocator=context.allocator); - if !is_kind(err, E_General.OK) { + if err != E_General.OK { return err; } codelength_ht, err = allocate_huffman_table(allocator=context.allocator); - if !is_kind(err, E_General.OK) { + if err != E_General.OK { return err; } defer free(z_repeat); @@ -482,11 +481,11 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) -> if type == 1 { // Use fixed code lengths. err = build_huffman(z_repeat, Z_FIXED_LENGTH[:]); - if !is_kind(err, E_General.OK) { + if err != E_General.OK { return err; } err = build_huffman(z_offset, Z_FIXED_DIST[:]); - if !is_kind(err, E_General.OK) { + if err != E_General.OK { return err; } } else { @@ -507,7 +506,7 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) -> codelength_sizes[Z_LENGTH_DEZIGZAG[i]] = u8(s); } err = build_huffman(codelength_ht, codelength_sizes[:]); - if !is_kind(err, E_General.OK) { + if err != E_General.OK { return err; } @@ -516,7 +515,7 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) -> for n < ntot { c, err = decode_huffman(z, codelength_ht); - if !is_kind(err, E_General.OK) { + if err != E_General.OK { return err; } @@ -560,18 +559,18 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) -> } err = build_huffman(z_repeat, lencodes[:hlit]); - if !is_kind(err, E_General.OK) { + if err != E_General.OK { return err; } err = build_huffman(z_offset, lencodes[hlit:ntot]); - if !is_kind(err, E_General.OK) { + if err != E_General.OK { return err; } } err = parse_huffman_block(z, z_repeat, z_offset); // log.debugf("Err: %v | Final: %v | Type: %v\n", err, final, type); - if !is_kind(err, E_General.OK) { + if err != E_General.OK { return err; } } diff --git a/core/image/png/example.odin b/core/image/png/example.odin index be6cdd7d7..072f88d6b 100644 --- a/core/image/png/example.odin +++ b/core/image/png/example.odin @@ -23,7 +23,7 @@ main :: proc() { img, err = png.load(file, options); defer png.destroy(img); - if !png.is_kind(err, png.E_General.OK) { + if err != png.E_General.OK { fmt.printf("Trying to read PNG file %v returned %v\n", file, err); } else { v: png.Info; @@ -120,7 +120,7 @@ main :: proc() { } } - if is_kind(err, E_General.OK) && .do_not_decompress_image not_in options && .info not_in options { + if err == E_General.OK && .do_not_decompress_image not_in options && .info not_in options { if ok := write_image_as_ppm("out.ppm", img); ok { fmt.println("Saved decoded image."); } else { diff --git a/core/image/png/helpers.odin b/core/image/png/helpers.odin index 9bd5f55b2..eb0d2f827 100644 --- a/core/image/png/helpers.odin +++ b/core/image/png/helpers.odin @@ -107,7 +107,7 @@ text :: proc(c: Chunk) -> (res: Text, ok: bool) { buf: bytes.Buffer; zlib_error := zlib.inflate_from_byte_array(fields[2], &buf); defer bytes.buffer_destroy(&buf); - if !is_kind(zlib_error, E_General.OK) { + if zlib_error != E_General.OK { ok = false; return; } @@ -161,7 +161,7 @@ text :: proc(c: Chunk) -> (res: Text, ok: bool) { buf: bytes.Buffer; zlib_error := zlib.inflate_from_byte_array(rest, &buf); defer bytes.buffer_destroy(&buf); - if !is_kind(zlib_error, E_General.OK) { + if zlib_error != E_General.OK { ok = false; return; } @@ -200,7 +200,7 @@ iccp :: proc(c: Chunk) -> (res: iCCP, ok: bool) { // Set up ZLIB context and decompress iCCP payload buf: bytes.Buffer; zlib_error := zlib.inflate_from_byte_array(fields[2], &buf); - if !is_kind(zlib_error, E_General.OK) { + if zlib_error != E_General.OK { bytes.buffer_destroy(&buf); ok = false; return; } @@ -498,7 +498,7 @@ when false { err = zlib.write_zlib_stream_from_memory(&ctx); b: []u8; - if is_kind(err, E_General, E_General.OK) { + if err == E_General.OK { b = ctx.out_buf[:]; } else { return err; diff --git a/core/image/png/png.odin b/core/image/png/png.odin index 716185c5f..74f287df8 100644 --- a/core/image/png/png.odin +++ b/core/image/png/png.odin @@ -16,7 +16,6 @@ Error :: compress.Error; E_General :: compress.General_Error; E_PNG :: image.Error; E_Deflate :: compress.Deflate_Error; -is_kind :: compress.is_kind; Image :: image.Image; Options :: image.Options; @@ -274,7 +273,7 @@ read_chunk :: proc(ctx: ^compress.Context) -> (Chunk, Error) { read_header :: proc(ctx: ^compress.Context) -> (IHDR, Error) { c, e := read_chunk(ctx); - if !is_kind(e, E_General.OK) { + if e != E_General.OK { return {}, e; } @@ -454,7 +453,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c seen_ihdr = true; header, err = read_header(&ctx); - if !is_kind(err, E_General.OK) { + if err != E_General.OK { return img, err; } @@ -507,7 +506,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c } c, err = read_chunk(&ctx); - if !is_kind(err, E_General.OK) { + if err != E_General.OK { return img, err; } @@ -542,7 +541,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c next := ch.type; for next == .IDAT { c, err = read_chunk(&ctx); - if !is_kind(err, E_General.OK) { + if err != E_General.OK { return img, err; } @@ -562,7 +561,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c seen_idat = true; case .IEND: c, err = read_chunk(&ctx); - if !is_kind(err, E_General.OK) { + if err != E_General.OK { return img, err; } seen_iend = true; @@ -571,7 +570,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c // TODO: Make sure that 16-bit bKGD + tRNS chunks return u16 instead of u16be c, err = read_chunk(&ctx); - if !is_kind(err, E_General.OK) { + if err != E_General.OK { return img, err; } seen_bkgd = true; @@ -606,7 +605,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c } case .tRNS: c, err = read_chunk(&ctx); - if !is_kind(err, E_General.OK) { + if err != E_General.OK { return img, err; } @@ -648,7 +647,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c case: // Unhandled type c, err = read_chunk(&ctx); - if !is_kind(err, E_General.OK) { + if err != E_General.OK { return img, err; } if .return_metadata in options { @@ -676,7 +675,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c zlib_error := zlib.inflate(idat, &buf); defer bytes.buffer_destroy(&buf); - if !is_kind(zlib_error, E_General.OK) { + if zlib_error != E_General.OK { return {}, zlib_error; } else { /* @@ -713,7 +712,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c as metadata, and set it instead to the raw number of channels. */ defilter_error := defilter(img, &buf, &header, options); - if !is_kind(defilter_error, E_General.OK) { + if defilter_error != E_General.OK { bytes.buffer_destroy(&img.pixels); return {}, defilter_error; } -- cgit v1.2.3 From 3a5245dcce79d3930d16b4968faa41c56e608b9a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 3 May 2021 14:36:27 +0100 Subject: Make sure builder uses the `InContext` version --- src/llvm_backend.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 55ffb09c3..ff7aca3f2 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -3117,7 +3117,7 @@ void lb_begin_procedure_body(lbProcedure *p) { } } - p->builder = LLVMCreateBuilder(); + p->builder = LLVMCreateBuilderInContext(p->module->ctx); p->decl_block = lb_create_block(p, "decls", true); p->entry_block = lb_create_block(p, "entry", true); @@ -12898,7 +12898,11 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { void lb_init_module(lbModule *m, Checker *c) { m->info = &c->info; +#if 1 m->ctx = LLVMGetGlobalContext(); +#else + m->ctx = LLVMContextCreate(); +#endif m->mod = LLVMModuleCreateWithNameInContext("odin_module", m->ctx); // m->debug_builder = nullptr; if (build_context.ODIN_DEBUG) { -- cgit v1.2.3 From 9a39ce6b75c4a459a1ee0df664cf48eb7b1d0567 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 3 May 2021 15:38:43 +0200 Subject: Change General_Error.OK to nil --- core/compress/common.odin | 1 - core/compress/gzip/example.odin | 4 ++-- core/compress/gzip/gzip.odin | 4 ++-- core/compress/zlib/example.odin | 2 +- core/compress/zlib/zlib.odin | 42 ++++++++++++++++++++--------------------- core/image/png/example.odin | 4 ++-- core/image/png/helpers.odin | 10 +++++----- core/image/png/png.odin | 40 +++++++++++++++++++-------------------- 8 files changed, 53 insertions(+), 54 deletions(-) diff --git a/core/compress/common.odin b/core/compress/common.odin index de1894f61..a0e092643 100644 --- a/core/compress/common.odin +++ b/core/compress/common.odin @@ -17,7 +17,6 @@ Error :: union { } General_Error :: enum { - OK = 0, File_Not_Found, Cannot_Open_File, File_Too_Short, diff --git a/core/compress/gzip/example.odin b/core/compress/gzip/example.odin index 344521489..54576c380 100644 --- a/core/compress/gzip/example.odin +++ b/core/compress/gzip/example.odin @@ -35,7 +35,7 @@ main :: proc() { if len(args) < 2 { stderr("No input file specified.\n"); err := gzip.load(TEST, &buf); - if err != E_General.OK { + if err != nil { stdout("Displaying test vector: "); stdout(bytes.buffer_to_string(&buf)); stdout("\n"); @@ -54,7 +54,7 @@ main :: proc() { } else { err = gzip.load(file, &buf); } - if err != gzip.E_General.OK { + if err != nil { if err != E_General.File_Not_Found { stderr("File not found: "); stderr(file); diff --git a/core/compress/gzip/gzip.odin b/core/compress/gzip/gzip.odin index 2284953d1..2b5e513c7 100644 --- a/core/compress/gzip/gzip.odin +++ b/core/compress/gzip/gzip.odin @@ -277,7 +277,7 @@ load_from_stream :: proc(stream: io.Stream, buf: ^bytes.Buffer, allocator := con // fmt.printf("ZLIB returned: %v\n", zlib_error); - if zlib_error != E_General.OK || zlib_error == nil { + if zlib_error != nil { return zlib_error; } @@ -307,7 +307,7 @@ load_from_stream :: proc(stream: io.Stream, buf: ^bytes.Buffer, allocator := con if len(payload) != payload_len { return E_GZIP.Payload_Length_Invalid; } - return E_General.OK; + return nil; } load :: proc{load_from_file, load_from_slice, load_from_stream}; diff --git a/core/compress/zlib/example.odin b/core/compress/zlib/example.odin index 65b78c70c..9af61e4b3 100644 --- a/core/compress/zlib/example.odin +++ b/core/compress/zlib/example.odin @@ -33,7 +33,7 @@ main :: proc() { err := zlib.inflate(ODIN_DEMO, &buf); defer bytes.buffer_destroy(&buf); - if err != zlib.E_General.OK { + if err != nil { fmt.printf("\nError: %v\n", err); } s := bytes.buffer_to_string(&buf); diff --git a/core/compress/zlib/zlib.odin b/core/compress/zlib/zlib.odin index c5097a320..bc19c37ef 100644 --- a/core/compress/zlib/zlib.odin +++ b/core/compress/zlib/zlib.odin @@ -135,7 +135,7 @@ write_byte :: #force_inline proc(z: ^Context, c: u8) -> (err: io.Error) #no_boun allocate_huffman_table :: proc(allocator := context.allocator) -> (z: ^Huffman_Table, err: Error) { z = new(Huffman_Table, allocator); - return z, E_General.OK; + return z, nil; } build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) { @@ -193,13 +193,13 @@ build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) { next_code[v] += 1; } } - return E_General.OK; + return nil; } decode_huffman_slowpath :: proc(z: ^Context, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check { r = 0; - err = E_General.OK; + err = nil; k: int; s: u8; @@ -229,7 +229,7 @@ decode_huffman_slowpath :: proc(z: ^Context, t: ^Huffman_Table) -> (r: u16, err: compress.consume_bits_lsb(z, s); r = t.value[b]; - return r, E_General.OK; + return r, nil; } decode_huffman :: proc(z: ^Context, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check { @@ -247,7 +247,7 @@ decode_huffman :: proc(z: ^Context, t: ^Huffman_Table) -> (r: u16, err: Error) # if b != 0 { s := u8(b >> ZFAST_BITS); compress.consume_bits_lsb(z, s); - return b & 511, E_General.OK; + return b & 511, nil; } return decode_huffman_slowpath(z, t); } @@ -255,7 +255,7 @@ decode_huffman :: proc(z: ^Context, t: ^Huffman_Table) -> (r: u16, err: Error) # parse_huffman_block :: proc(z: ^Context, z_repeat, z_offset: ^Huffman_Table) -> (err: Error) #no_bounds_check { #no_bounds_check for { value, e := decode_huffman(z, z_repeat); - if e != E_General.OK { + if e != nil { return err; } if value < 256 { @@ -266,7 +266,7 @@ parse_huffman_block :: proc(z: ^Context, z_repeat, z_offset: ^Huffman_Table) -> } else { if value == 256 { // End of block - return E_General.OK; + return nil; } value -= 257; @@ -276,7 +276,7 @@ parse_huffman_block :: proc(z: ^Context, z_repeat, z_offset: ^Huffman_Table) -> } value, e = decode_huffman(z, z_offset); - if e != E_General.OK { + if e != nil { return E_Deflate.Bad_Huffman_Code; } @@ -389,7 +389,7 @@ inflate_from_stream :: proc(using ctx: ^Context, raw := false, allocator := cont // Parse ZLIB stream without header. err = inflate_raw(ctx); - if err != E_General.OK { + if err != nil { return err; } @@ -401,7 +401,7 @@ inflate_from_stream :: proc(using ctx: ^Context, raw := false, allocator := cont return E_General.Checksum_Failed; } } - return E_General.OK; + return nil; } // @(optimization_mode="speed") @@ -417,15 +417,15 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) -> codelength_ht: ^Huffman_Table; z_repeat, err = allocate_huffman_table(allocator=context.allocator); - if err != E_General.OK { + if err != nil { return err; } z_offset, err = allocate_huffman_table(allocator=context.allocator); - if err != E_General.OK { + if err != nil { return err; } codelength_ht, err = allocate_huffman_table(allocator=context.allocator); - if err != E_General.OK { + if err != nil { return err; } defer free(z_repeat); @@ -481,11 +481,11 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) -> if type == 1 { // Use fixed code lengths. err = build_huffman(z_repeat, Z_FIXED_LENGTH[:]); - if err != E_General.OK { + if err != nil { return err; } err = build_huffman(z_offset, Z_FIXED_DIST[:]); - if err != E_General.OK { + if err != nil { return err; } } else { @@ -506,7 +506,7 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) -> codelength_sizes[Z_LENGTH_DEZIGZAG[i]] = u8(s); } err = build_huffman(codelength_ht, codelength_sizes[:]); - if err != E_General.OK { + if err != nil { return err; } @@ -515,7 +515,7 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) -> for n < ntot { c, err = decode_huffman(z, codelength_ht); - if err != E_General.OK { + if err != nil { return err; } @@ -559,18 +559,18 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) -> } err = build_huffman(z_repeat, lencodes[:hlit]); - if err != E_General.OK { + if err != nil { return err; } err = build_huffman(z_offset, lencodes[hlit:ntot]); - if err != E_General.OK { + if err != nil { return err; } } err = parse_huffman_block(z, z_repeat, z_offset); // log.debugf("Err: %v | Final: %v | Type: %v\n", err, final, type); - if err != E_General.OK { + if err != nil { return err; } } @@ -578,7 +578,7 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) -> break; } } - return E_General.OK; + return nil; } inflate_from_byte_array :: proc(input: []u8, buf: ^bytes.Buffer, raw := false) -> (err: Error) { diff --git a/core/image/png/example.odin b/core/image/png/example.odin index 072f88d6b..59a4cfd42 100644 --- a/core/image/png/example.odin +++ b/core/image/png/example.odin @@ -23,7 +23,7 @@ main :: proc() { img, err = png.load(file, options); defer png.destroy(img); - if err != png.E_General.OK { + if err != nil { fmt.printf("Trying to read PNG file %v returned %v\n", file, err); } else { v: png.Info; @@ -120,7 +120,7 @@ main :: proc() { } } - if err == E_General.OK && .do_not_decompress_image not_in options && .info not_in options { + if err == nil && .do_not_decompress_image not_in options && .info not_in options { if ok := write_image_as_ppm("out.ppm", img); ok { fmt.println("Saved decoded image."); } else { diff --git a/core/image/png/helpers.odin b/core/image/png/helpers.odin index eb0d2f827..0975d1d87 100644 --- a/core/image/png/helpers.odin +++ b/core/image/png/helpers.odin @@ -107,7 +107,7 @@ text :: proc(c: Chunk) -> (res: Text, ok: bool) { buf: bytes.Buffer; zlib_error := zlib.inflate_from_byte_array(fields[2], &buf); defer bytes.buffer_destroy(&buf); - if zlib_error != E_General.OK { + if zlib_error != nil { ok = false; return; } @@ -161,7 +161,7 @@ text :: proc(c: Chunk) -> (res: Text, ok: bool) { buf: bytes.Buffer; zlib_error := zlib.inflate_from_byte_array(rest, &buf); defer bytes.buffer_destroy(&buf); - if zlib_error != E_General.OK { + if zlib_error != nil { ok = false; return; } @@ -200,7 +200,7 @@ iccp :: proc(c: Chunk) -> (res: iCCP, ok: bool) { // Set up ZLIB context and decompress iCCP payload buf: bytes.Buffer; zlib_error := zlib.inflate_from_byte_array(fields[2], &buf); - if zlib_error != E_General.OK { + if zlib_error != nil { bytes.buffer_destroy(&buf); ok = false; return; } @@ -498,7 +498,7 @@ when false { err = zlib.write_zlib_stream_from_memory(&ctx); b: []u8; - if err == E_General.OK { + if err == nil { b = ctx.out_buf[:]; } else { return err; @@ -511,6 +511,6 @@ when false { iend := make_chunk([]u8{}, .IEND); write_chunk(fd, iend); - return E_General.OK; + return nil; } } diff --git a/core/image/png/png.odin b/core/image/png/png.odin index 74f287df8..7762f0106 100644 --- a/core/image/png/png.odin +++ b/core/image/png/png.odin @@ -267,13 +267,13 @@ read_chunk :: proc(ctx: ^compress.Context) -> (Chunk, Error) { if chunk.crc != u32be(computed_crc) { return {}, E_General.Checksum_Failed; } - return chunk, E_General.OK; + return chunk, nil; } read_header :: proc(ctx: ^compress.Context) -> (IHDR, Error) { c, e := read_chunk(ctx); - if e != E_General.OK { + if e != nil { return {}, e; } @@ -341,7 +341,7 @@ read_header :: proc(ctx: ^compress.Context) -> (IHDR, Error) { return {}, E_PNG.Unknown_Color_Type; } - return header, E_General.OK; + return header, nil; } chunk_type_to_name :: proc(type: ^Chunk_Type) -> string { @@ -453,7 +453,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c seen_ihdr = true; header, err = read_header(&ctx); - if err != E_General.OK { + if err != nil { return img, err; } @@ -506,7 +506,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c } c, err = read_chunk(&ctx); - if err != E_General.OK { + if err != nil { return img, err; } @@ -527,7 +527,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c if .return_metadata not_in options && .do_not_decompress_image in options { img.channels = final_image_channels; img.sidecar = info; - return img, E_General.OK; + return img, nil; } // There must be at least 1 IDAT, contiguous if more. if seen_idat { @@ -541,7 +541,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c next := ch.type; for next == .IDAT { c, err = read_chunk(&ctx); - if err != E_General.OK { + if err != nil { return img, err; } @@ -561,7 +561,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c seen_idat = true; case .IEND: c, err = read_chunk(&ctx); - if err != E_General.OK { + if err != nil { return img, err; } seen_iend = true; @@ -570,7 +570,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c // TODO: Make sure that 16-bit bKGD + tRNS chunks return u16 instead of u16be c, err = read_chunk(&ctx); - if err != E_General.OK { + if err != nil { return img, err; } seen_bkgd = true; @@ -605,7 +605,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c } case .tRNS: c, err = read_chunk(&ctx); - if err != E_General.OK { + if err != nil { return img, err; } @@ -647,7 +647,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c case: // Unhandled type c, err = read_chunk(&ctx); - if err != E_General.OK { + if err != nil { return img, err; } if .return_metadata in options { @@ -664,7 +664,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c } if .do_not_decompress_image in options { img.channels = final_image_channels; - return img, E_General.OK; + return img, nil; } if !seen_idat { @@ -675,7 +675,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c zlib_error := zlib.inflate(idat, &buf); defer bytes.buffer_destroy(&buf); - if zlib_error != E_General.OK { + if zlib_error != nil { return {}, zlib_error; } else { /* @@ -712,7 +712,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c as metadata, and set it instead to the raw number of channels. */ defilter_error := defilter(img, &buf, &header, options); - if defilter_error != E_General.OK { + if defilter_error != nil { bytes.buffer_destroy(&img.pixels); return {}, defilter_error; } @@ -727,10 +727,10 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c */ if .Paletted in header.color_type && .do_not_expand_indexed in options { - return img, E_General.OK; + return img, nil; } if .Color not_in header.color_type && .do_not_expand_grayscale in options { - return img, E_General.OK; + return img, nil; } @@ -839,7 +839,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c // If we have 3 in and 3 out, or 4 in and 4 out without premultiplication... if raw_image_channels == 4 && .alpha_premultiply not_in options && !seen_bkgd { // Then we're done. - return img, E_General.OK; + return img, nil; } } @@ -1036,7 +1036,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c // If we have 3 in and 3 out, or 4 in and 4 out without premultiplication... if !premultiply { // Then we're done. - return img, E_General.OK; + return img, nil; } } @@ -1229,7 +1229,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c unreachable("We should never see bit depths other than 8, 16 and 'Paletted' here."); } - return img, E_General.OK; + return img, nil; } @@ -1651,7 +1651,7 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^IHDR, option } } - return E_General.OK; + return nil; } load :: proc{load_from_file, load_from_slice, load_from_stream}; -- cgit v1.2.3 From 0d044eabacde5910ef10dd18dca6d471fea0a9f2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 3 May 2021 15:00:50 +0100 Subject: Remove non-InContext type creations --- src/llvm_abi.cpp | 104 --------------------------------------------------- src/llvm_backend.cpp | 34 ++++++++++++----- 2 files changed, 24 insertions(+), 114 deletions(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 65e3b2c58..739d43d60 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -271,110 +271,6 @@ i64 lb_alignof(LLVMTypeRef type) { return 1; } -#if 0 -Type *lb_abi_to_odin_type(lbModule *m, LLVMTypeRef type, bool is_return, u32 level = 0) { - Type **found = map_get(&m->llvm_types, hash_pointer(type)); - if (found) { - return *found; - } - GB_ASSERT_MSG(level < 64, "%s %d", LLVMPrintTypeToString(type), is_return); - - LLVMTypeKind kind = LLVMGetTypeKind(type); - switch (kind) { - case LLVMVoidTypeKind: - return nullptr; - case LLVMIntegerTypeKind: - { - unsigned w = LLVMGetIntTypeWidth(type); - if (w == 1) { - return t_llvm_bool; - } - unsigned bytes = (w + 7)/8; - switch (bytes) { - case 1: return t_u8; - case 2: return t_u16; - case 4: return t_u32; - case 8: return t_u64; - case 16: return t_u128; - } - GB_PANIC("Unhandled integer type"); - } - case LLVMFloatTypeKind: - return t_f32; - case LLVMDoubleTypeKind: - return t_f64; - case LLVMPointerTypeKind: - { - LLVMTypeRef elem = LLVMGetElementType(type); - if (lb_is_type_kind(elem, LLVMFunctionTypeKind)) { - unsigned param_count = LLVMCountParamTypes(elem); - LLVMTypeRef *params = gb_alloc_array(heap_allocator(), LLVMTypeRef, param_count); - defer (gb_free(heap_allocator(), params)); - LLVMGetParamTypes(elem, params); - - Type **param_types = gb_alloc_array(heap_allocator(), Type *, param_count); - defer (gb_free(heap_allocator(), param_types)); - - for (unsigned i = 0; i < param_count; i++) { - param_types[i] = lb_abi_to_odin_type(m, params[i], false, level+1); - } - - LLVMTypeRef ret = LLVMGetReturnType(elem); - Type *ret_type = lb_abi_to_odin_type(m, ret, true, level+1); - - bool is_c_vararg = !!LLVMIsFunctionVarArg(elem); - return alloc_type_proc_from_types(param_types, param_count, ret_type, is_c_vararg); - } - return alloc_type_pointer(lb_abi_to_odin_type(m, elem, false, level+1)); - } - case LLVMFunctionTypeKind: - GB_PANIC("LLVMFunctionTypeKind should not be seen on its own"); - break; - - case LLVMStructTypeKind: - { - unsigned field_count = LLVMCountStructElementTypes(type); - Type **fields = gb_alloc_array(heap_allocator(), Type *, field_count); - for (unsigned i = 0; i < field_count; i++) { - LLVMTypeRef field_type = LLVMStructGetTypeAtIndex(type, i); - if (lb_is_type_kind(field_type, LLVMPointerTypeKind) && level > 0) { - fields[i] = t_rawptr; - } else { - fields[i] = lb_abi_to_odin_type(m, field_type, false, level+1); - } - } - if (is_return) { - return alloc_type_tuple_from_field_types(fields, field_count, !!LLVMIsPackedStruct(type), false); - } else { - return alloc_type_struct_from_field_types(fields, field_count, !!LLVMIsPackedStruct(type)); - } - } - break; - case LLVMArrayTypeKind: - { - - i64 count = LLVMGetArrayLength(type); - Type *elem = lb_abi_to_odin_type(m, LLVMGetElementType(type), false, level+1); - return alloc_type_array(elem, count); - } - break; - - case LLVMX86_MMXTypeKind: - return t_vector_x86_mmx; - case LLVMVectorTypeKind: - { - i64 count = LLVMGetVectorSize(type); - Type *elem = lb_abi_to_odin_type(m, LLVMGetElementType(type), false, level+1); - return alloc_type_simd_vector(count, elem); - } - - } - GB_PANIC("Unhandled type for lb_abi_to_odin_type -> %s", LLVMPrintTypeToString(type)); - - return 0; -} -#endif - #define LB_ABI_INFO(name) lbFunctionType *name(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, LLVMTypeRef return_type, bool return_is_defined, ProcCallingConvention calling_convention) typedef LB_ABI_INFO(lbAbiInfoType); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index ff7aca3f2..0f14b3244 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1110,7 +1110,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { case Basic_uintptr: return LLVMIntTypeInContext(ctx, 8*cast(unsigned)build_context.word_size); - case Basic_rawptr: return LLVMPointerType(LLVMInt8Type(), 0); + case Basic_rawptr: return LLVMPointerType(LLVMInt8TypeInContext(ctx), 0); case Basic_string: { char const *name = "..string"; @@ -1126,7 +1126,7 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { LLVMStructSetBody(type, fields, 2, false); return type; } - case Basic_cstring: return LLVMPointerType(LLVMInt8Type(), 0); + case Basic_cstring: return LLVMPointerType(LLVMInt8TypeInContext(ctx), 0); case Basic_any: { char const *name = "..any"; @@ -1452,21 +1452,35 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { } if (param_index < param_count) { params[param_index++] = lb_type(m, t_rawptr); - // params[param_index++] = lb_type(m, t_context_ptr); } GB_ASSERT(param_index == param_count); - lbFunctionType *ft = lb_get_abi_info(m->ctx, params, param_count, ret, ret != nullptr, type->Proc.calling_convention); + { + for_array(j, ft->args) { + auto arg = ft->args[j]; + GB_ASSERT_MSG(LLVMGetTypeContext(arg.type) == ft->ctx, + "\n\t%s %td/%td" + "\n\tArgTypeCtx: %p\n\tCurrentCtx: %p\n\tGlobalCtx: %p", + LLVMPrintTypeToString(arg.type), + j, ft->args.count, + LLVMGetTypeContext(arg.type), ft->ctx, LLVMGetGlobalContext()); + } + GB_ASSERT_MSG(LLVMGetTypeContext(ft->ret.type) == ft->ctx, + "\n\t%s" + "\n\tRetTypeCtx: %p\n\tCurrentCtx: %p\n\tGlobalCtx: %p", + LLVMPrintTypeToString(ft->ret.type), + LLVMGetTypeContext(ft->ret.type), ft->ctx, LLVMGetGlobalContext()); + } + map_set(&m->function_type_map, hash_type(type), ft); LLVMTypeRef new_abi_fn_ptr_type = lb_function_type_to_llvm_ptr(ft, type->Proc.c_vararg); LLVMTypeRef new_abi_fn_type = LLVMGetElementType(new_abi_fn_ptr_type); - // LLVMTypeRef new_ret = LLVMGetReturnType(new_abi_fn_type); - // LLVMTypeRef old_ret = LLVMGetReturnType(old_abi_fn_type); - // unsigned new_count = LLVMCountParamTypes(new_abi_fn_type); - // unsigned old_count = LLVMCountParamTypes(old_abi_fn_type); - // GB_ASSERT_MSG(new_count == old_count, "%u %u, %s %s", new_count, old_count, LLVMPrintTypeToString(new_abi_fn_type), LLVMPrintTypeToString(old_abi_fn_type)); + GB_ASSERT_MSG(LLVMGetTypeContext(new_abi_fn_type) == m->ctx, + "\n\tFuncTypeCtx: %p\n\tCurrentCtx: %p\n\tGlobalCtx: %p", + LLVMGetTypeContext(new_abi_fn_type), m->ctx, LLVMGetGlobalContext()); + return new_abi_fn_ptr_type; } @@ -12898,7 +12912,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { void lb_init_module(lbModule *m, Checker *c) { m->info = &c->info; -#if 1 +#if 0 m->ctx = LLVMGetGlobalContext(); #else m->ctx = LLVMContextCreate(); -- cgit v1.2.3 From b44a56118effd721c164f5c1cb783d3d39958115 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 3 May 2021 15:26:40 +0100 Subject: Begin cleanup for allowing for multiple LLVM modules --- src/llvm_backend.cpp | 580 +++++++++++++++++++++++++++------------------------ src/llvm_backend.hpp | 4 +- src/main.cpp | 15 +- 3 files changed, 320 insertions(+), 279 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 0f14b3244..72694e562 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -12912,12 +12912,15 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { void lb_init_module(lbModule *m, Checker *c) { m->info = &c->info; -#if 0 - m->ctx = LLVMGetGlobalContext(); -#else + gbString module_name = nullptr; + if (m->pkg) { + module_name = gb_string_make(heap_allocator(), "odin_package-"); + module_name = gb_string_append_length(module_name, m->pkg->name.text, m->pkg->name.len); + } + + m->ctx = LLVMContextCreate(); -#endif - m->mod = LLVMModuleCreateWithNameInContext("odin_module", m->ctx); + m->mod = LLVMModuleCreateWithNameInContext(module_name ? module_name : "odin_package", m->ctx); // m->debug_builder = nullptr; if (build_context.ODIN_DEBUG) { enum {DEBUG_METADATA_VERSION = 3}; @@ -13012,8 +13015,18 @@ bool lb_init_generator(lbGenerator *gen, Checker *c) { gen->info = &c->info; - lb_init_module(&gen->module, c); + map_init(&gen->modules, permanent_allocator(), gen->info->packages.entries.count*2); + + for_array(i, gen->info->packages.entries) { + AstPackage *pkg = gen->info->packages.entries[i].value; + auto m = gb_alloc_item(permanent_allocator(), lbModule); + m->pkg = pkg; + map_set(&gen->modules, hash_pointer(pkg), m); + lb_init_module(m, c); + } + map_set(&gen->modules, hash_pointer(nullptr), &gen->default_module); + lb_init_module(&gen->default_module, c); return true; } @@ -13833,18 +13846,261 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da } } +struct lbGlobalVariable { + lbValue var; + lbValue init; + DeclInfo *decl; + bool is_initialized; +}; + +lbProcedure *lb_create_startup_type_info(lbModule *m) { + LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod); + defer (LLVMDisposePassManager(default_function_pass_manager)); + lb_populate_function_pass_manager(default_function_pass_manager, false, build_context.optimization_level); + LLVMFinalizeFunctionPassManager(default_function_pass_manager); + + Type *params = alloc_type_tuple(); + Type *results = alloc_type_tuple(); + + Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl); + + lbProcedure *p = lb_create_dummy_procedure(m, str_lit(LB_STARTUP_TYPE_INFO_PROC_NAME), proc_type); + p->is_startup = true; + + lb_begin_procedure_body(p); + + lb_setup_type_info_data(p); + + lb_end_procedure_body(p); + + if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { + gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main"); + LLVMDumpValue(p->value); + gb_printf_err("\n\n\n\n"); + LLVMVerifyFunction(p->value, LLVMAbortProcessAction); + } + + LLVMRunFunctionPassManager(default_function_pass_manager, p->value); + + return p; +} + +lbProcedure *lb_create_startup_runtime(lbModule *m, lbProcedure *startup_type_info, Array &global_variables) { // Startup Runtime + LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod); + defer (LLVMDisposePassManager(default_function_pass_manager)); + lb_populate_function_pass_manager(default_function_pass_manager, false, build_context.optimization_level); + LLVMFinalizeFunctionPassManager(default_function_pass_manager); + + Type *params = alloc_type_tuple(); + Type *results = alloc_type_tuple(); + + Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl); + + lbProcedure *p = lb_create_dummy_procedure(m, str_lit(LB_STARTUP_RUNTIME_PROC_NAME), proc_type); + p->is_startup = true; + + lb_begin_procedure_body(p); + + LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, startup_type_info->type)), startup_type_info->value, nullptr, 0, ""); + + for_array(i, global_variables) { + auto *var = &global_variables[i]; + if (var->is_initialized) { + continue; + } + + Entity *e = var->decl->entity; + GB_ASSERT(e->kind == Entity_Variable); + + if (var->decl->init_expr != nullptr) { + // gb_printf_err("%s\n", expr_to_string(var->decl->init_expr)); + lbValue init = lb_build_expr(p, var->decl->init_expr); + LLVMValueKind value_kind = LLVMGetValueKind(init.value); + // gb_printf_err("%s %d\n", LLVMPrintValueToString(init.value)); + + if (lb_is_const_or_global(init)) { + if (!var->is_initialized) { + LLVMSetInitializer(var->var.value, init.value); + var->is_initialized = true; + continue; + } + } else { + var->init = init; + } + } + + if (var->init.value != nullptr) { + GB_ASSERT(!var->is_initialized); + Type *t = type_deref(var->var.type); + + if (is_type_any(t)) { + // NOTE(bill): Edge case for 'any' type + Type *var_type = default_type(var->init.type); + lbAddr g = lb_add_global_generated(m, var_type, var->init); + lb_addr_store(p, g, var->init); + lbValue gp = lb_addr_get_ptr(p, g); + + lbValue data = lb_emit_struct_ep(p, var->var, 0); + lbValue ti = lb_emit_struct_ep(p, var->var, 1); + lb_emit_store(p, data, lb_emit_conv(p, gp, t_rawptr)); + lb_emit_store(p, ti, lb_type_info(m, var_type)); + } else { + LLVMTypeRef pvt = LLVMTypeOf(var->var.value); + LLVMTypeRef vt = LLVMGetElementType(pvt); + lbValue src0 = lb_emit_conv(p, var->init, t); + LLVMValueRef src = OdinLLVMBuildTransmute(p, src0.value, vt); + LLVMValueRef dst = var->var.value; + LLVMBuildStore(p->builder, src, dst); + } + + var->is_initialized = true; + } + } + + + lb_end_procedure_body(p); + + if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { + gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main"); + LLVMDumpValue(p->value); + gb_printf_err("\n\n\n\n"); + LLVMVerifyFunction(p->value, LLVMAbortProcessAction); + } + + LLVMRunFunctionPassManager(default_function_pass_manager, p->value); + + return p; +} + + +lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) { + LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod); + defer (LLVMDisposePassManager(default_function_pass_manager)); + lb_populate_function_pass_manager(default_function_pass_manager, false, build_context.optimization_level); + LLVMFinalizeFunctionPassManager(default_function_pass_manager); + + Type *params = alloc_type_tuple(); + Type *results = alloc_type_tuple(); + + Type *t_ptr_cstring = alloc_type_pointer(t_cstring); + + String name = str_lit("main"); + if (build_context.metrics.os == TargetOs_windows && build_context.metrics.arch == TargetArch_386) { + name = str_lit("mainCRTStartup"); + } else { + array_init(¶ms->Tuple.variables, permanent_allocator(), 2); + params->Tuple.variables[0] = alloc_entity_param(nullptr, make_token_ident("argc"), t_i32, false, true); + params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("argv"), t_ptr_cstring, false, true); + } + + array_init(&results->Tuple.variables, permanent_allocator(), 1); + results->Tuple.variables[0] = alloc_entity_param(nullptr, make_token_ident("_"), t_i32, false, true); + + Type *proc_type = alloc_type_proc(nullptr, + params, params->Tuple.variables.count, + results, results->Tuple.variables.count, false, ProcCC_CDecl); + + + lbProcedure *p = lb_create_dummy_procedure(m, name, proc_type); + p->is_startup = true; + + lb_begin_procedure_body(p); + + { // initialize `runtime.args__` + lbValue argc = {LLVMGetParam(p->value, 0), t_i32}; + argc = lb_emit_conv(p, argc, t_int); + lbValue argv = {LLVMGetParam(p->value, 1), t_ptr_cstring}; + lbAddr args = lb_addr(lb_find_runtime_value(p->module, str_lit("args__"))); + lb_fill_slice(p, args, argv, argc); + } + + LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, startup_runtime->type)), startup_runtime->value, nullptr, 0, ""); + + if (build_context.command_kind == Command_test) { + Type *t_Internal_Test = find_type_in_pkg(m->info, str_lit("testing"), str_lit("Internal_Test")); + Type *array_type = alloc_type_array(t_Internal_Test, m->info->testing_procedures.count); + Type *slice_type = alloc_type_slice(t_Internal_Test); + lbAddr all_tests_array_addr = lb_add_global_generated(p->module, array_type, {}); + lbValue all_tests_array = lb_addr_get_ptr(p, all_tests_array_addr); + + LLVMTypeRef lbt_Internal_Test = lb_type(m, t_Internal_Test); + + LLVMValueRef indices[2] = {}; + indices[0] = LLVMConstInt(lb_type(m, t_i32), 0, false); + + for_array(i, m->info->testing_procedures) { + Entity *testing_proc = m->info->testing_procedures[i]; + String name = testing_proc->token.string; + lbValue *found = map_get(&m->values, hash_entity(testing_proc)); + GB_ASSERT(found != nullptr); + + String pkg_name = {}; + if (testing_proc->pkg != nullptr) { + pkg_name = testing_proc->pkg->name; + } + lbValue v_pkg = lb_find_or_add_entity_string(m, pkg_name); + lbValue v_name = lb_find_or_add_entity_string(m, name); + lbValue v_proc = *found; + + indices[1] = LLVMConstInt(lb_type(m, t_int), i, false); + + LLVMValueRef vals[3] = {}; + vals[0] = v_pkg.value; + vals[1] = v_name.value; + vals[2] = v_proc.value; + GB_ASSERT(LLVMIsConstant(vals[0])); + GB_ASSERT(LLVMIsConstant(vals[1])); + GB_ASSERT(LLVMIsConstant(vals[2])); + + LLVMValueRef dst = LLVMConstInBoundsGEP(all_tests_array.value, indices, gb_count_of(indices)); + LLVMValueRef src = llvm_const_named_struct(lbt_Internal_Test, vals, gb_count_of(vals)); + + LLVMBuildStore(p->builder, src, dst); + } + + lbAddr all_tests_slice = lb_add_local_generated(p, slice_type, true); + lb_fill_slice(p, all_tests_slice, + lb_array_elem(p, all_tests_array), + lb_const_int(m, t_int, m->info->testing_procedures.count)); + + + lbValue runner = lb_find_package_value(m, str_lit("testing"), str_lit("runner")); + + auto args = array_make(heap_allocator(), 1); + args[0] = lb_addr_load(p, all_tests_slice); + lb_emit_call(p, runner, args); + } else { + lbValue *found = map_get(&m->values, hash_entity(m->info->entry_point)); + GB_ASSERT(found != nullptr); + lb_emit_call(p, *found, {}); + } + + LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 0, false)); + + lb_end_procedure_body(p); + + if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { + gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main"); + LLVMDumpValue(p->value); + gb_printf_err("\n\n\n\n"); + LLVMVerifyFunction(p->value, LLVMAbortProcessAction); + } + + LLVMRunFunctionPassManager(default_function_pass_manager, p->value); + return p; +} + + void lb_generate_code(lbGenerator *gen) { #define TIME_SECTION(str) do { if (build_context.show_more_timings) timings_start_section(&global_timings, str_lit(str)); } while (0) TIME_SECTION("LLVM Initializtion"); - lbModule *m = &gen->module; - LLVMModuleRef mod = gen->module.mod; + lbModule *m = &gen->default_module; CheckerInfo *info = gen->info; auto *min_dep_set = &info->minimum_dependency_set; - LLVMInitializeAllTargetInfos(); LLVMInitializeAllTargets(); LLVMInitializeAllTargetMCs(); @@ -13853,16 +14109,19 @@ void lb_generate_code(lbGenerator *gen) { LLVMInitializeAllDisassemblers(); LLVMInitializeNativeTarget(); - char const *target_triple = alloc_cstring(permanent_allocator(), build_context.metrics.target_triplet); char const *target_data_layout = alloc_cstring(permanent_allocator(), build_context.metrics.target_data_layout); - LLVMSetTarget(mod, target_triple); + for_array(i, gen->modules.entries) { + LLVMSetTarget(gen->modules.entries[i].value->mod, target_triple); + } LLVMTargetRef target = {}; char *llvm_error = nullptr; LLVMGetTargetFromTriple(target_triple, &target, &llvm_error); GB_ASSERT(target != nullptr); + + TIME_SECTION("LLVM Create Target Machine"); LLVMCodeModel code_mode = LLVMCodeModelDefault; @@ -13905,7 +14164,9 @@ void lb_generate_code(lbGenerator *gen) { defer (LLVMDisposeTargetMachine(target_machine)); - LLVMSetModuleDataLayout(mod, LLVMCreateTargetDataLayout(target_machine)); + for_array(i, gen->modules.entries) { + LLVMSetModuleDataLayout(gen->modules.entries[i].value->mod, LLVMCreateTargetDataLayout(target_machine)); + } if (m->debug_builder) { // Debug Info for_array(i, info->files.entries) { @@ -13960,7 +14221,7 @@ void lb_generate_code(lbGenerator *gen) { isize max_type_info_count = info->minimum_dependency_type_info_set.entries.count+1; // gb_printf_err("max_type_info_count: %td\n", max_type_info_count); Type *t = alloc_type_array(t_type_info, max_type_info_count); - LLVMValueRef g = LLVMAddGlobal(mod, lb_type(m, t), LB_TYPE_INFO_DATA_NAME); + LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), LB_TYPE_INFO_DATA_NAME); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); LLVMSetLinkage(g, LLVMInternalLinkage); @@ -13998,7 +14259,7 @@ void lb_generate_code(lbGenerator *gen) { { char const *name = LB_TYPE_INFO_TYPES_NAME; Type *t = alloc_type_array(t_type_info_ptr, count); - LLVMValueRef g = LLVMAddGlobal(mod, lb_type(m, t), name); + LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); LLVMSetLinkage(g, LLVMInternalLinkage); lb_global_type_info_member_types = lb_addr({g, alloc_type_pointer(t)}); @@ -14007,7 +14268,7 @@ void lb_generate_code(lbGenerator *gen) { { char const *name = LB_TYPE_INFO_NAMES_NAME; Type *t = alloc_type_array(t_string, count); - LLVMValueRef g = LLVMAddGlobal(mod, lb_type(m, t), name); + LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); LLVMSetLinkage(g, LLVMInternalLinkage); lb_global_type_info_member_names = lb_addr({g, alloc_type_pointer(t)}); @@ -14015,7 +14276,7 @@ void lb_generate_code(lbGenerator *gen) { { char const *name = LB_TYPE_INFO_OFFSETS_NAME; Type *t = alloc_type_array(t_uintptr, count); - LLVMValueRef g = LLVMAddGlobal(mod, lb_type(m, t), name); + LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); LLVMSetLinkage(g, LLVMInternalLinkage); lb_global_type_info_member_offsets = lb_addr({g, alloc_type_pointer(t)}); @@ -14024,7 +14285,7 @@ void lb_generate_code(lbGenerator *gen) { { char const *name = LB_TYPE_INFO_USINGS_NAME; Type *t = alloc_type_array(t_bool, count); - LLVMValueRef g = LLVMAddGlobal(mod, lb_type(m, t), name); + LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); LLVMSetLinkage(g, LLVMInternalLinkage); lb_global_type_info_member_usings = lb_addr({g, alloc_type_pointer(t)}); @@ -14033,7 +14294,7 @@ void lb_generate_code(lbGenerator *gen) { { char const *name = LB_TYPE_INFO_TAGS_NAME; Type *t = alloc_type_array(t_string, count); - LLVMValueRef g = LLVMAddGlobal(mod, lb_type(m, t), name); + LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); LLVMSetLinkage(g, LLVMInternalLinkage); lb_global_type_info_member_tags = lb_addr({g, alloc_type_pointer(t)}); @@ -14073,13 +14334,8 @@ void lb_generate_code(lbGenerator *gen) { } } - struct GlobalVariable { - lbValue var; - lbValue init; - DeclInfo *decl; - bool is_initialized; - }; - auto global_variables = array_make(permanent_allocator(), 0, global_variable_max_count); + + auto global_variables = array_make(permanent_allocator(), 0, global_variable_max_count); for_array(i, info->variable_init_order) { DeclInfo *d = info->variable_init_order[i]; @@ -14141,7 +14397,7 @@ void lb_generate_code(lbGenerator *gen) { LLVMSetLinkage(g.value, LLVMInternalLinkage); } - GlobalVariable var = {}; + lbGlobalVariable var = {}; var.var = g; var.decl = decl; @@ -14259,10 +14515,10 @@ void lb_generate_code(lbGenerator *gen) { LLVMPassRegistryRef pass_registry = LLVMGetGlobalPassRegistry(); - LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(mod); - LLVMPassManagerRef function_pass_manager_minimal = LLVMCreateFunctionPassManagerForModule(mod); - LLVMPassManagerRef function_pass_manager_size = LLVMCreateFunctionPassManagerForModule(mod); - LLVMPassManagerRef function_pass_manager_speed = LLVMCreateFunctionPassManagerForModule(mod); + LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod); + LLVMPassManagerRef function_pass_manager_minimal = LLVMCreateFunctionPassManagerForModule(m->mod); + LLVMPassManagerRef function_pass_manager_size = LLVMCreateFunctionPassManagerForModule(m->mod); + LLVMPassManagerRef function_pass_manager_speed = LLVMCreateFunctionPassManagerForModule(m->mod); defer (LLVMDisposePassManager(default_function_pass_manager)); defer (LLVMDisposePassManager(function_pass_manager_minimal)); defer (LLVMDisposePassManager(function_pass_manager_size)); @@ -14284,142 +14540,18 @@ void lb_generate_code(lbGenerator *gen) { LLVMFinalizeFunctionPassManager(function_pass_manager_speed); - LLVMPassManagerRef default_function_pass_manager_without_memcpy = LLVMCreateFunctionPassManagerForModule(mod); + LLVMPassManagerRef default_function_pass_manager_without_memcpy = LLVMCreateFunctionPassManagerForModule(m->mod); defer (LLVMDisposePassManager(default_function_pass_manager_without_memcpy)); LLVMInitializeFunctionPassManager(default_function_pass_manager_without_memcpy); lb_populate_function_pass_manager(default_function_pass_manager_without_memcpy, true, build_context.optimization_level); LLVMFinalizeFunctionPassManager(default_function_pass_manager_without_memcpy); TIME_SECTION("LLVM Runtime Type Information Creation"); + lbProcedure *startup_type_info = lb_create_startup_type_info(m); - lbProcedure *startup_type_info = nullptr; - lbProcedure *startup_runtime = nullptr; - { // Startup Type Info - Type *params = alloc_type_tuple(); - Type *results = alloc_type_tuple(); - - Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl); - - lbProcedure *p = lb_create_dummy_procedure(m, str_lit(LB_STARTUP_TYPE_INFO_PROC_NAME), proc_type); - p->is_startup = true; - startup_type_info = p; - - lb_begin_procedure_body(p); - - lb_setup_type_info_data(p); - - lb_end_procedure_body(p); - - if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { - gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main"); - LLVMDumpValue(p->value); - gb_printf_err("\n\n\n\n"); - LLVMVerifyFunction(p->value, LLVMAbortProcessAction); - } - - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); - } TIME_SECTION("LLVM Runtime Startup Creation (Global Variables)"); - { // Startup Runtime - Type *params = alloc_type_tuple(); - Type *results = alloc_type_tuple(); - - Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl); + lbProcedure *startup_runtime = lb_create_startup_runtime(m, startup_type_info, global_variables); - lbProcedure *p = lb_create_dummy_procedure(m, str_lit(LB_STARTUP_RUNTIME_PROC_NAME), proc_type); - p->is_startup = true; - startup_runtime = p; - - lb_begin_procedure_body(p); - - - LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, startup_type_info->type)), startup_type_info->value, nullptr, 0, ""); - - for_array(i, global_variables) { - auto *var = &global_variables[i]; - if (var->is_initialized) { - continue; - } - - Entity *e = var->decl->entity; - GB_ASSERT(e->kind == Entity_Variable); - - if (var->decl->init_expr != nullptr) { - // gb_printf_err("%s\n", expr_to_string(var->decl->init_expr)); - lbValue init = lb_build_expr(p, var->decl->init_expr); - LLVMValueKind value_kind = LLVMGetValueKind(init.value); - // gb_printf_err("%s %d\n", LLVMPrintValueToString(init.value)); - - if (lb_is_const_or_global(init)) { - if (!var->is_initialized) { - LLVMSetInitializer(var->var.value, init.value); - var->is_initialized = true; - continue; - } - } else { - var->init = init; - } - } - - if (var->init.value != nullptr) { - GB_ASSERT(!var->is_initialized); - Type *t = type_deref(var->var.type); - - if (is_type_any(t)) { - // NOTE(bill): Edge case for 'any' type - Type *var_type = default_type(var->init.type); - lbAddr g = lb_add_global_generated(m, var_type, var->init); - lb_addr_store(p, g, var->init); - lbValue gp = lb_addr_get_ptr(p, g); - - lbValue data = lb_emit_struct_ep(p, var->var, 0); - lbValue ti = lb_emit_struct_ep(p, var->var, 1); - lb_emit_store(p, data, lb_emit_conv(p, gp, t_rawptr)); - lb_emit_store(p, ti, lb_type_info(m, var_type)); - } else { - LLVMTypeRef pvt = LLVMTypeOf(var->var.value); - LLVMTypeRef vt = LLVMGetElementType(pvt); - lbValue src0 = lb_emit_conv(p, var->init, t); - LLVMValueRef src = OdinLLVMBuildTransmute(p, src0.value, vt); - LLVMValueRef dst = var->var.value; - LLVMBuildStore(p->builder, src, dst); - } - - var->is_initialized = true; - } - } - - - lb_end_procedure_body(p); - - if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { - gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main"); - LLVMDumpValue(p->value); - gb_printf_err("\n\n\n\n"); - LLVMVerifyFunction(p->value, LLVMAbortProcessAction); - } - - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); - - /*{ - LLVMValueRef last_instr = LLVMGetLastInstruction(p->decl_block->block); - for (LLVMValueRef instr = LLVMGetFirstInstruction(p->decl_block->block); - instr != last_instr; - instr = LLVMGetNextInstruction(instr)) { - if (LLVMIsAAllocaInst(instr)) { - LLVMTypeRef type = LLVMGetAllocatedType(instr); - LLVMValueRef sz_val = LLVMSizeOf(type); - GB_ASSERT(LLVMIsConstant(sz_val)); - gb_printf_err(">> 0x%p\n", sz_val); - LLVMTypeRef sz_type = LLVMTypeOf(sz_val); - gb_printf_err(">> %s\n", LLVMPrintTypeToString(sz_type)); - unsigned long long sz = LLVMConstIntGetZExtValue(sz_val); - // long long sz = LLVMConstIntGetSExtValue(sz_val); - gb_printf_err(">> %ll\n", sz); - } - } - }*/ - } String filepath_ll = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".ll")); @@ -14453,7 +14585,7 @@ void lb_generate_code(lbGenerator *gen) { gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %.*s\n", LIT(p->name)); LLVMDumpValue(p->value); gb_printf_err("\n\n\n\n"); - if (LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error)) { + if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) { gb_printf_err("LLVM Error: %s\n", llvm_error); } LLVMVerifyFunction(p->value, LLVMPrintMessageAction); @@ -14463,117 +14595,8 @@ void lb_generate_code(lbGenerator *gen) { if (!(build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main)) { - TIME_SECTION("LLVM DLL main"); - - - Type *params = alloc_type_tuple(); - Type *results = alloc_type_tuple(); - - Type *t_ptr_cstring = alloc_type_pointer(t_cstring); - - String name = str_lit("main"); - if (build_context.metrics.os == TargetOs_windows && build_context.metrics.arch == TargetArch_386) { - name = str_lit("mainCRTStartup"); - } else { - array_init(¶ms->Tuple.variables, permanent_allocator(), 2); - params->Tuple.variables[0] = alloc_entity_param(nullptr, make_token_ident("argc"), t_i32, false, true); - params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("argv"), t_ptr_cstring, false, true); - } - - array_init(&results->Tuple.variables, permanent_allocator(), 1); - results->Tuple.variables[0] = alloc_entity_param(nullptr, make_token_ident("_"), t_i32, false, true); - - Type *proc_type = alloc_type_proc(nullptr, - params, params->Tuple.variables.count, - results, results->Tuple.variables.count, false, ProcCC_CDecl); - - - lbProcedure *p = lb_create_dummy_procedure(m, name, proc_type); - p->is_startup = true; - - lb_begin_procedure_body(p); - - { // initialize `runtime.args__` - lbValue argc = {LLVMGetParam(p->value, 0), t_i32}; - argc = lb_emit_conv(p, argc, t_int); - lbValue argv = {LLVMGetParam(p->value, 1), t_ptr_cstring}; - lbAddr args = lb_addr(lb_find_runtime_value(p->module, str_lit("args__"))); - lb_fill_slice(p, args, argv, argc); - } - - LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, startup_runtime->type)), startup_runtime->value, nullptr, 0, ""); - - if (build_context.command_kind == Command_test) { - Type *t_Internal_Test = find_type_in_pkg(m->info, str_lit("testing"), str_lit("Internal_Test")); - Type *array_type = alloc_type_array(t_Internal_Test, m->info->testing_procedures.count); - Type *slice_type = alloc_type_slice(t_Internal_Test); - lbAddr all_tests_array_addr = lb_add_global_generated(p->module, array_type, {}); - lbValue all_tests_array = lb_addr_get_ptr(p, all_tests_array_addr); - - LLVMTypeRef lbt_Internal_Test = lb_type(m, t_Internal_Test); - - LLVMValueRef indices[2] = {}; - indices[0] = LLVMConstInt(lb_type(m, t_i32), 0, false); - - for_array(i, m->info->testing_procedures) { - Entity *testing_proc = m->info->testing_procedures[i]; - String name = testing_proc->token.string; - lbValue *found = map_get(&m->values, hash_entity(testing_proc)); - GB_ASSERT(found != nullptr); - - String pkg_name = {}; - if (testing_proc->pkg != nullptr) { - pkg_name = testing_proc->pkg->name; - } - lbValue v_pkg = lb_find_or_add_entity_string(m, pkg_name); - lbValue v_name = lb_find_or_add_entity_string(m, name); - lbValue v_proc = *found; - - indices[1] = LLVMConstInt(lb_type(m, t_int), i, false); - - LLVMValueRef vals[3] = {}; - vals[0] = v_pkg.value; - vals[1] = v_name.value; - vals[2] = v_proc.value; - GB_ASSERT(LLVMIsConstant(vals[0])); - GB_ASSERT(LLVMIsConstant(vals[1])); - GB_ASSERT(LLVMIsConstant(vals[2])); - - LLVMValueRef dst = LLVMConstInBoundsGEP(all_tests_array.value, indices, gb_count_of(indices)); - LLVMValueRef src = llvm_const_named_struct(lbt_Internal_Test, vals, gb_count_of(vals)); - - LLVMBuildStore(p->builder, src, dst); - } - - lbAddr all_tests_slice = lb_add_local_generated(p, slice_type, true); - lb_fill_slice(p, all_tests_slice, - lb_array_elem(p, all_tests_array), - lb_const_int(m, t_int, m->info->testing_procedures.count)); - - - lbValue runner = lb_find_package_value(m, str_lit("testing"), str_lit("runner")); - - auto args = array_make(heap_allocator(), 1); - args[0] = lb_addr_load(p, all_tests_slice); - lb_emit_call(p, runner, args); - } else { - lbValue *found = map_get(&m->values, hash_entity(entry_point)); - GB_ASSERT(found != nullptr); - lb_emit_call(p, *found, {}); - } - - LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 0, false)); - - lb_end_procedure_body(p); - - if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { - gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main"); - LLVMDumpValue(p->value); - gb_printf_err("\n\n\n\n"); - LLVMVerifyFunction(p->value, LLVMAbortProcessAction); - } - - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); + TIME_SECTION("LLVM main"); + lb_create_main_procedure(m, startup_runtime); } @@ -14583,7 +14606,7 @@ void lb_generate_code(lbGenerator *gen) { TIME_SECTION("LLVM Print Module to File"); - if (LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error)) { + if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) { gb_printf_err("LLVM Error: %s\n", llvm_error); gb_exit(1); return; @@ -14594,7 +14617,9 @@ void lb_generate_code(lbGenerator *gen) { TIME_SECTION("LLVM Function Pass"); - { + for_array(i, gen->modules.entries) { + lbModule *m = gen->modules.entries[i].value; + for_array(i, m->procedures_to_generate) { lbProcedure *p = m->procedures_to_generate[i]; if (p->body != nullptr) { // Build Procedure @@ -14640,7 +14665,10 @@ void lb_generate_code(lbGenerator *gen) { defer (LLVMDisposePassManager(module_pass_manager)); lb_populate_module_pass_manager(target_machine, module_pass_manager, build_context.optimization_level); - LLVMRunPassManager(module_pass_manager, mod); + for_array(i, gen->modules.entries) { + lbModule *m = gen->modules.entries[i].value; + LLVMRunPassManager(module_pass_manager, m->mod); + } llvm_error = nullptr; defer (LLVMDisposeMessage(llvm_error)); @@ -14667,11 +14695,11 @@ void lb_generate_code(lbGenerator *gen) { } } - if (LLVMVerifyModule(mod, LLVMReturnStatusAction, &llvm_error)) { + if (LLVMVerifyModule(m->mod, LLVMReturnStatusAction, &llvm_error)) { gb_printf_err("LLVM Error:\n%s\n", llvm_error); if (build_context.keep_temp_files) { TIME_SECTION("LLVM Print Module to File"); - if (LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error)) { + if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) { gb_printf_err("LLVM Error: %s\n", llvm_error); gb_exit(1); return; @@ -14684,7 +14712,7 @@ void lb_generate_code(lbGenerator *gen) { if (build_context.keep_temp_files || build_context.build_mode == BuildMode_LLVM_IR) { TIME_SECTION("LLVM Print Module to File"); - if (LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error)) { + if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) { gb_printf_err("LLVM Error: %s\n", llvm_error); gb_exit(1); return; @@ -14697,7 +14725,7 @@ void lb_generate_code(lbGenerator *gen) { TIME_SECTION("LLVM Object Generation"); - if (LLVMTargetMachineEmitToFile(target_machine, mod, cast(char *)filepath_obj.text, code_gen_file_type, &llvm_error)) { + if (LLVMTargetMachineEmitToFile(target_machine, m->mod, cast(char *)filepath_obj.text, code_gen_file_type, &llvm_error)) { gb_printf_err("LLVM Error: %s\n", llvm_error); gb_exit(1); return; diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index aed555aaf..392c15ea4 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -86,6 +86,7 @@ struct lbModule { LLVMContextRef ctx; CheckerInfo *info; + AstPackage *pkg; // associated gbMutex mutex; @@ -124,12 +125,13 @@ struct lbModule { }; struct lbGenerator { - lbModule module; CheckerInfo *info; Array output_object_paths; String output_base; String output_name; + Map modules; // Key: AstPackage * + lbModule default_module; }; diff --git a/src/main.cpp b/src/main.cpp index abc90d627..0a38d3496 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -218,8 +218,19 @@ i32 linker_stage(lbGenerator *gen) { add_path(find_result.vs_library_path); } - for_array(i, gen->module.foreign_library_paths) { - String lib = gen->module.foreign_library_paths[i]; + for_array(j, gen->modules.entries) { + lbModule *m = gen->modules.entries[j].value; + for_array(i, m->foreign_library_paths) { + String lib = m->foreign_library_paths[i]; + GB_ASSERT(lib.len < gb_count_of(lib_str_buf)-1); + isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf), + " \"%.*s\"", LIT(lib)); + lib_str = gb_string_appendc(lib_str, lib_str_buf); + } + } + + for_array(i, gen->default_module.foreign_library_paths) { + String lib = gen->default_module.foreign_library_paths[i]; GB_ASSERT(lib.len < gb_count_of(lib_str_buf)-1); isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf), " \"%.*s\"", LIT(lib)); -- cgit v1.2.3 From 3a556eb3046f3f7ff1183981b261ac38e38c0c87 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 3 May 2021 15:44:57 +0100 Subject: Add `-test-name:` flag to allow specific tests to be ran --- src/build_settings.cpp | 2 ++ src/checker.cpp | 35 +++++++++++++++++++++++++++++++++++ src/main.cpp | 32 +++++++++++++++++++++++++++++--- 3 files changed, 66 insertions(+), 3 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index f48aa4999..77d9cc506 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -212,6 +212,8 @@ struct BuildContext { QueryDataSetSettings query_data_set_settings; + StringSet test_names; + gbAffinity affinity; isize thread_count; diff --git a/src/checker.cpp b/src/checker.cpp index 523d2c297..3b7a58c5e 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -4502,6 +4502,38 @@ void check_unchecked_bodies(Checker *c) { } } +void check_test_names(Checker *c) { + if (build_context.test_names.entries.count == 0) { + return; + } + + AstPackage *pkg = c->info.init_package; + Scope *s = pkg->scope; + + for_array(i, build_context.test_names.entries) { + String name = build_context.test_names.entries[i].value; + Entity *e = scope_lookup(s, name); + if (e == nullptr) { + Token tok = {}; + if (pkg->files.count != 0) { + tok = pkg->files[0]->tokens[0]; + } + error(tok, "Unable to find the test '%.*s' in 'package %.*s' ", LIT(name), LIT(pkg->name)); + } + } + + for (isize i = 0; i < c->info.testing_procedures.count; /**/) { + Entity *e = c->info.testing_procedures[i]; + String name = e->token.string; + if (!string_set_exists(&build_context.test_names, name)) { + array_ordered_remove(&c->info.testing_procedures, i); + } else { + i += 1; + } + } + +} + void check_parsed_files(Checker *c) { #define TIME_SECTION(str) do { if (build_context.show_more_timings) timings_start_section(&global_timings, str_lit(str)); } while (0) @@ -4576,6 +4608,9 @@ void check_parsed_files(Checker *c) { TIME_SECTION("generate minimum dependency set"); generate_minimum_dependency_set(c, c->info.entry_point); + TIME_SECTION("check test names"); + check_test_names(c); + TIME_SECTION("calculate global init order"); // Calculate initialization order of global variables calculate_global_init_order(c); diff --git a/src/main.cpp b/src/main.cpp index 0a38d3496..48fb4dcc3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -338,8 +338,8 @@ i32 linker_stage(lbGenerator *gen) { gbString lib_str = gb_string_make(heap_allocator(), "-L/"); defer (gb_string_free(lib_str)); - for_array(i, gen->module.foreign_library_paths) { - String lib = gen->module.foreign_library_paths[i]; + for_array(i, gen->default_module.foreign_library_paths) { + String lib = gen->default_module.foreign_library_paths[i]; // NOTE(zangent): Sometimes, you have to use -framework on MacOS. // This allows you to specify '-f' in a #foreign_system_library, @@ -603,6 +603,8 @@ enum BuildFlagKind { BuildFlag_ExtraLinkerFlags, BuildFlag_Microarch, + BuildFlag_TestName, + BuildFlag_DisallowDo, BuildFlag_DefaultToNilAllocator, BuildFlag_InsertSemicolon, @@ -721,6 +723,8 @@ bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_ExtraLinkerFlags, str_lit("extra-linker-flags"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_Microarch, str_lit("microarch"), BuildFlagParam_String, Command__does_build); + add_flag(&build_flags, BuildFlag_TestName, str_lit("test-name"), BuildFlagParam_String, Command_test); + add_flag(&build_flags, BuildFlag_DisallowDo, str_lit("disallow-do"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_DefaultToNilAllocator, str_lit("default-to-nil-allocator"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_InsertSemicolon, str_lit("insert-semicolon"), BuildFlagParam_None, Command__does_check); @@ -1219,6 +1223,21 @@ bool parse_build_flags(Array args) { string_to_lower(&build_context.microarch); break; + case BuildFlag_TestName: + GB_ASSERT(value.kind == ExactValue_String); + { + String name = value.value_string; + if (!string_is_valid_identifier(name)) { + gb_printf_err("Test name '%.*s' must be a valid identifier\n", LIT(name)); + bad_flags = true; + break; + } + string_set_add(&build_context.test_names, name); + + // NOTE(bill): Allow for multiple -test-name + continue; + } + case BuildFlag_DisallowDo: build_context.disallow_do = true; break; @@ -1580,6 +1599,7 @@ void print_show_help(String const arg0, String const &command) { bool doc = command == "doc"; bool build = command == "build"; bool run_or_build = command == "run" || command == "build" || command == "test"; + bool test_only = command == "test"; bool check_only = command == "check"; bool check = run_or_build || command == "check"; @@ -1734,6 +1754,12 @@ void print_show_help(String const arg0, String const &command) { } } + if (test_only) { + print_usage_line(1, "-test-name:"); + print_usage_line(2, "Run specific test only by name"); + print_usage_line(0, ""); + } + if (run_or_build) { print_usage_line(1, "-extra-linker-flags:"); print_usage_line(2, "Adds extra linker specific flags in a string"); @@ -1925,7 +1951,7 @@ int main(int arg_count, char const **arg_ptr) { map_init(&build_context.defined_values, heap_allocator()); build_context.extra_packages.allocator = heap_allocator(); - + string_set_init(&build_context.test_names, heap_allocator()); Array args = setup_args(arg_count, arg_ptr); -- cgit v1.2.3 From e4286d0ff9d383d03b220ac8fc52934853b35c34 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 3 May 2021 15:57:35 +0100 Subject: Force `zero_init` in `lb_add_local` in certain cases --- src/llvm_backend.cpp | 15 +++++++++++++++ src/types.cpp | 29 +++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 72694e562..2c61132dd 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -3424,6 +3424,21 @@ lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e, bool zero_init, i32 p LLVMSetAlignment(ptr, alignment); LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block); + + + if (!zero_init) { + // If there is any padding of any kind, just zero init regardless of zero_init parameter + LLVMTypeKind kind = LLVMGetTypeKind(llvm_type); + if (kind == LLVMStructTypeKind) { + i64 sz = type_size_of(type); + if (type_size_of_struct_pretend_is_packed(type) != sz) { + zero_init = true; + } + } else if (kind == LLVMArrayTypeKind) { + zero_init = true; + } + } + if (zero_init) { LLVMTypeKind kind = LLVMGetTypeKind(llvm_type); diff --git a/src/types.cpp b/src/types.cpp index 3fafa00d5..ffdb90c03 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2779,7 +2779,36 @@ void type_path_pop(TypePath *tp) { i64 type_size_of_internal (Type *t, TypePath *path); i64 type_align_of_internal(Type *t, TypePath *path); +i64 type_size_of(Type *t); +i64 type_align_of(Type *t); +i64 type_size_of_struct_pretend_is_packed(Type *ot) { + if (ot == nullptr) { + return 0; + } + Type *t = core_type(ot); + if (t->kind != Type_Struct) { + return type_size_of(ot); + } + + if (t->Struct.is_packed) { + return type_size_of(ot); + } + + i64 count = 0, size = 0, align = 1; + + auto const &fields = t->Struct.fields; + count = fields.count; + if (count == 0) { + return 0; + } + + for_array(i, fields) { + size += type_size_of(fields[i]->type); + } + + return align_formula(size, align); +} i64 type_size_of(Type *t) { -- cgit v1.2.3 From 746e880eb521e6cf5b6a452004da9bc5d2095076 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 3 May 2021 17:43:14 +0100 Subject: Begin work on making LLVM backend work with multiple modules for possible faster compilation --- src/build_settings.cpp | 6 + src/llvm_backend.cpp | 668 +++++++++++++++++++++++++++++-------------------- src/llvm_backend.hpp | 11 +- src/main.cpp | 11 +- 4 files changed, 417 insertions(+), 279 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 77d9cc506..77046cf6d 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -207,6 +207,8 @@ struct BuildContext { bool ignore_microsoft_magic; bool linker_map_file; + bool use_separate_modules; + u32 cmd_doc_flags; Array extra_packages; @@ -807,6 +809,10 @@ void init_build_context(TargetMetrics *cross_target) { bc->max_align = metrics->max_align; bc->link_flags = str_lit(" "); + if (bc->metrics.os == TargetOs_windows) { + // bc->use_separate_modules = bc->optimization_level == 0; + } + // NOTE(zangent): The linker flags to set the build architecture are different // across OSs. It doesn't make sense to allocate extra data on the heap diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 2c61132dd..a7c149f06 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1,3 +1,7 @@ +#ifndef USE_SEPARTE_MODULES +#define USE_SEPARTE_MODULES build_context.use_separate_modules +#endif + #include "llvm_backend.hpp" #include "llvm_abi.cpp" #include "llvm_backend_opt.cpp" @@ -74,6 +78,15 @@ bool lb_is_instr_terminating(LLVMValueRef instr) { +lbModule *lb_pkg_module(lbGenerator *gen, AstPackage *pkg) { + auto *found = map_get(&gen->modules, hash_pointer(pkg)); + if (found) { + return *found; + } + return &gen->default_module; +} + + lbAddr lb_addr(lbValue addr) { lbAddr v = {lbAddr_Default, addr}; if (addr.type != nullptr && is_type_relative_pointer(type_deref(addr.type))) { @@ -2528,10 +2541,17 @@ void lb_ensure_abi_function_type(lbModule *m, lbProcedure *p) { GB_ASSERT(p->abi_function_type != nullptr); } -lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) { +lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) { GB_ASSERT(entity != nullptr); - String link_name = lb_get_entity_name(m, entity); + String link_name = {}; + + if (ignore_body) { + lbModule *other_module = lb_pkg_module(m->gen, entity->pkg); + link_name = lb_get_entity_name(other_module, entity); + } else { + link_name = lb_get_entity_name(m, entity); + } { StringHashKey key = string_hash_string(link_name); @@ -2699,6 +2719,10 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) { } } + if (ignore_body) { + p->body = nullptr; + } + if (m->debug_builder) { // Debug Information Type *bt = base_type(p->type); @@ -5484,9 +5508,10 @@ LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String const &str) { isize max_len = 7+8+1; char *name = gb_alloc_array(permanent_allocator(), char, max_len); - isize len = gb_snprintf(name, max_len, "csbs$%x", m->global_array_index); + + u32 id = cast(u32)gb_atomic32_fetch_add(&m->gen->global_array_index, 1); + isize len = gb_snprintf(name, max_len, "csbs$%x", id); len -= 1; - m->global_array_index++; LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name); LLVMSetInitializer(global_data, data); @@ -5526,9 +5551,9 @@ lbValue lb_find_or_add_entity_string_byte_slice(lbModule *m, String const &str) { isize max_len = 7+8+1; name = gb_alloc_array(permanent_allocator(), char, max_len); - isize len = gb_snprintf(name, max_len, "csbs$%x", m->global_array_index); + u32 id = cast(u32)gb_atomic32_fetch_add(&m->gen->global_array_index, 1); + isize len = gb_snprintf(name, max_len, "csbs$%x", id); len -= 1; - m->global_array_index++; } LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name); LLVMSetInitializer(global_data, data); @@ -5684,6 +5709,7 @@ LLVMValueRef lb_build_constant_array_values(lbModule *m, Type *type, Type *elem_ } lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) { + GB_ASSERT(is_type_proc(e->type)); e = strip_entity_wrapping(e); GB_ASSERT(e != nullptr); auto *found = map_get(&m->values, hash_entity(e)); @@ -5691,8 +5717,14 @@ lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) { return *found; } - // TODO(bill): this is - lbProcedure *missing_proc = lb_create_procedure(m, e); + bool ignore_body = false; + + if (USE_SEPARTE_MODULES) { + lbModule *other_module = lb_pkg_module(m->gen, e->pkg); + ignore_body = other_module != m; + } + + lbProcedure *missing_proc = lb_create_procedure(m, e, ignore_body); found = map_get(&m->values, hash_entity(e)); if (found) { return *found; @@ -5702,6 +5734,47 @@ lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) { return {}; } +lbValue lb_find_value_from_entity(lbModule *m, Entity *e) { + e = strip_entity_wrapping(e); + GB_ASSERT(e != nullptr); + if (is_type_proc(e->type)) { + return lb_find_procedure_value_from_entity(m, e); + } + + auto *found = map_get(&m->values, hash_entity(e)); + if (found) { + return *found; + } + + if (USE_SEPARTE_MODULES) { + lbModule *other_module = lb_pkg_module(m->gen, e->pkg); + bool is_external = other_module != m; + if (!is_external) { + other_module = e->code_gen_module; + is_external = other_module != m; + } + + if (is_external) { + String name = lb_get_entity_name(other_module, e); + + lbValue g = {}; + g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(permanent_allocator(), name)); + g.type = alloc_type_pointer(e->type); + LLVMSetExternallyInitialized(g.value, true); + + lb_add_entity(m, e, g); + lb_add_member(m, name, g); + return g; + } + } + + GB_PANIC("\n\tError in: %s, missing value %.*s\n", token_pos_to_string(e->token.pos), LIT(e->token.string)); + return {}; +} + + + + lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_local) { LLVMContextRef ctx = m->ctx; @@ -5777,8 +5850,8 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc } else { isize max_len = 7+8+1; char *str = gb_alloc_array(permanent_allocator(), char, max_len); - isize len = gb_snprintf(str, max_len, "csba$%x", m->global_array_index); - m->global_array_index++; + u32 id = cast(u32)gb_atomic32_fetch_add(&m->gen->global_array_index, 1); + isize len = gb_snprintf(str, max_len, "csba$%x", id); String name = make_string(cast(u8 *)str, len-1); @@ -8192,31 +8265,18 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr, } } -lbValue lb_emit_runtime_call(lbProcedure *p, char const *c_name, Array const &args) { - // LLVMMetadataRef curr_loc = LLVMGetCurrentDebugLocation2(p->builder); - // LLVMSetCurrentDebugLocation2(p->builder, nullptr); - // defer (if (curr_loc) { - // LLVMSetCurrentDebugLocation2(p->builder, curr_loc); - // }); - String name = make_string_c(c_name); - - - AstPackage *pkg = p->module->info->runtime_package; +lbValue lb_lookup_runtime_procedure(lbModule *m, String const &name) { + AstPackage *pkg = m->info->runtime_package; Entity *e = scope_lookup_current(pkg->scope, name); + return lb_find_procedure_value_from_entity(m, e); +} - lbValue *found = nullptr; - if (p->module != e->code_gen_module) { - gb_mutex_lock(&p->module->mutex); - } - GB_ASSERT(e->code_gen_module != nullptr); - found = map_get(&e->code_gen_module->values, hash_entity(e)); - if (p->module != e->code_gen_module) { - gb_mutex_unlock(&p->module->mutex); - } - GB_ASSERT_MSG(found != nullptr, "%s", c_name); - return lb_emit_call(p, *found, args); +lbValue lb_emit_runtime_call(lbProcedure *p, char const *c_name, Array const &args) { + String name = make_string_c(c_name); + lbValue proc = lb_lookup_runtime_procedure(p->module, name); + return lb_emit_call(p, proc, args); } lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, ProcInlining inlining, bool use_return_ptr_hint) { @@ -9946,24 +10006,6 @@ void lb_emit_increment(lbProcedure *p, lbValue addr) { } -LLVMValueRef lb_lookup_runtime_procedure(lbModule *m, String const &name) { - AstPackage *pkg = m->info->runtime_package; - Entity *e = scope_lookup_current(pkg->scope, name); - - lbValue *found = nullptr; - if (m != e->code_gen_module) { - gb_mutex_lock(&m->mutex); - } - GB_ASSERT(e->code_gen_module != nullptr); - found = map_get(&e->code_gen_module->values, hash_entity(e)); - if (m != e->code_gen_module) { - gb_mutex_unlock(&m->mutex); - } - GB_ASSERT(found != nullptr); - - return found->value; -} - lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *end_type) { GB_ASSERT(type_size_of(value.type) == type_size_of(end_type)); @@ -11122,6 +11164,44 @@ lbValue lb_emit_any_cast(lbProcedure *p, lbValue value, Type *type, TokenPos pos } +lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *expr) { + auto *found = map_get(&m->values, hash_entity(e)); + if (found) { + auto v = *found; + // NOTE(bill): This is because pointers are already pointers in LLVM + if (is_type_proc(v.type)) { + return v; + } + return lb_emit_load(p, v); + } else if (e != nullptr && e->kind == Entity_Variable) { + return lb_addr_load(p, lb_build_addr(p, expr)); + } + + if (USE_SEPARTE_MODULES) { + lbModule *other_module = lb_pkg_module(m->gen, e->pkg); + if (other_module != m) { + String name = lb_get_entity_name(other_module, e); + + lbValue g = {}; + g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(permanent_allocator(), name)); + g.type = alloc_type_pointer(e->type); + LLVMSetExternallyInitialized(g.value, true); + + lb_add_entity(m, e, g); + lb_add_member(m, name, g); + return lb_emit_load(p, g); + } + } + + String pkg = {}; + if (e->pkg) { + pkg = e->pkg->name; + } + gb_printf_err("Error in: %s\n", token_pos_to_string(ast_token(expr).pos)); + GB_PANIC("nullptr value for expression from identifier: %.*s.%.*s (%p) : %s @ %p", LIT(pkg), LIT(e->token.string), e, type_to_string(e->type), expr); + return {}; +} + lbValue lb_build_expr(lbProcedure *p, Ast *expr) { lbModule *m = p->module; @@ -11211,24 +11291,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { } GB_ASSERT(e->kind != Entity_ProcGroup); - auto *found = map_get(&p->module->values, hash_entity(e)); - if (found) { - auto v = *found; - // NOTE(bill): This is because pointers are already pointers in LLVM - if (is_type_proc(v.type)) { - return v; - } - return lb_emit_load(p, v); - } else if (e != nullptr && e->kind == Entity_Variable) { - return lb_addr_load(p, lb_build_addr(p, expr)); - } - gb_printf_err("Error in: %s\n", token_pos_to_string(i->token.pos)); - String pkg = {}; - if (e->pkg) { - pkg = e->pkg->name; - } - GB_PANIC("nullptr value for expression from identifier: %.*s.%.*s (%p) : %s @ %p", LIT(pkg), LIT(e->token.string), e, type_to_string(e->type), expr); - return {}; + return lb_find_ident(p, m, e, expr); case_end; case_ast_node(de, DerefExpr, expr); @@ -11609,11 +11672,14 @@ lbAddr lb_build_addr_from_entity(lbProcedure *p, Entity *e, Ast *expr) { return lb_get_soa_variable_addr(p, e); } + if (v.value == nullptr) { - error(expr, "%.*s Unknown value: %.*s, entity: %p %.*s", - LIT(p->name), - LIT(e->token.string), e, LIT(entity_strings[e->kind])); - GB_PANIC("Unknown value"); + return lb_addr(lb_find_value_from_entity(p->module, e)); + + // error(expr, "%.*s Unknown value: %.*s, entity: %p %.*s", + // LIT(p->name), + // LIT(e->token.string), e, LIT(entity_strings[e->kind])); + // GB_PANIC("Unknown value"); } return lb_addr(v); @@ -13031,18 +13097,31 @@ bool lb_init_generator(lbGenerator *gen, Checker *c) { gen->info = &c->info; map_init(&gen->modules, permanent_allocator(), gen->info->packages.entries.count*2); + map_init(&gen->modules_through_ctx, permanent_allocator(), gen->info->packages.entries.count*2); - for_array(i, gen->info->packages.entries) { - AstPackage *pkg = gen->info->packages.entries[i].value; + if (USE_SEPARTE_MODULES) { + for_array(i, gen->info->packages.entries) { + AstPackage *pkg = gen->info->packages.entries[i].value; - auto m = gb_alloc_item(permanent_allocator(), lbModule); - m->pkg = pkg; - map_set(&gen->modules, hash_pointer(pkg), m); - lb_init_module(m, c); + auto m = gb_alloc_item(permanent_allocator(), lbModule); + m->pkg = pkg; + m->gen = gen; + map_set(&gen->modules, hash_pointer(pkg), m); + lb_init_module(m, c); + } } + + gen->default_module.gen = gen; map_set(&gen->modules, hash_pointer(nullptr), &gen->default_module); lb_init_module(&gen->default_module, c); + + for_array(i, gen->modules.entries) { + lbModule *m = gen->modules.entries[i].value; + LLVMContextRef ctx = LLVMGetModuleContext(m->mod); + map_set(&gen->modules_through_ctx, hash_pointer(ctx), m); + } + return true; } @@ -13052,8 +13131,10 @@ lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value) { isize max_len = 7+8+1; u8 *str = cast(u8 *)gb_alloc_array(permanent_allocator(), u8, max_len); - isize len = gb_snprintf(cast(char *)str, max_len, "ggv$%x", m->global_generated_index); - m->global_generated_index++; + + u32 id = cast(u32)gb_atomic32_fetch_add(&m->gen->global_generated_index, 1); + + isize len = gb_snprintf(cast(char *)str, max_len, "ggv$%x", id); String name = make_string(str, len-1); Scope *scope = nullptr; @@ -13076,17 +13157,12 @@ lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value) { lbValue lb_find_runtime_value(lbModule *m, String const &name) { AstPackage *p = m->info->runtime_package; Entity *e = scope_lookup_current(p->scope, name); - lbValue *found = map_get(&m->values, hash_entity(e)); - GB_ASSERT_MSG(found != nullptr, "Unable to find runtime value '%.*s'", LIT(name)); - lbValue value = *found; - return value; + return lb_find_value_from_entity(m, e); } lbValue lb_find_package_value(lbModule *m, String const &pkg, String const &name) { Entity *e = find_entity_in_pkg(m->info, pkg, name); lbValue *found = map_get(&m->values, hash_entity(e)); - GB_ASSERT_MSG(found != nullptr, "Unable to find value '%.*s.%.*s'", LIT(pkg), LIT(name)); - lbValue value = *found; - return value; + return lb_find_value_from_entity(m, e); } lbValue lb_get_type_info_ptr(lbModule *m, Type *type) { @@ -13900,8 +13976,8 @@ lbProcedure *lb_create_startup_type_info(lbModule *m) { return p; } -lbProcedure *lb_create_startup_runtime(lbModule *m, lbProcedure *startup_type_info, Array &global_variables) { // Startup Runtime - LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod); +lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *startup_type_info, Array &global_variables) { // Startup Runtime + LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(main_module->mod); defer (LLVMDisposePassManager(default_function_pass_manager)); lb_populate_function_pass_manager(default_function_pass_manager, false, build_context.optimization_level); LLVMFinalizeFunctionPassManager(default_function_pass_manager); @@ -13911,12 +13987,12 @@ lbProcedure *lb_create_startup_runtime(lbModule *m, lbProcedure *startup_type_in Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl); - lbProcedure *p = lb_create_dummy_procedure(m, str_lit(LB_STARTUP_RUNTIME_PROC_NAME), proc_type); + lbProcedure *p = lb_create_dummy_procedure(main_module, str_lit(LB_STARTUP_RUNTIME_PROC_NAME), proc_type); p->is_startup = true; lb_begin_procedure_body(p); - LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, startup_type_info->type)), startup_type_info->value, nullptr, 0, ""); + LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, startup_type_info->type)), startup_type_info->value, nullptr, 0, ""); for_array(i, global_variables) { auto *var = &global_variables[i]; @@ -13924,8 +14000,11 @@ lbProcedure *lb_create_startup_runtime(lbModule *m, lbProcedure *startup_type_in continue; } + lbModule *entity_module = main_module; + Entity *e = var->decl->entity; GB_ASSERT(e->kind == Entity_Variable); + e->code_gen_module = entity_module; if (var->decl->init_expr != nullptr) { // gb_printf_err("%s\n", expr_to_string(var->decl->init_expr)); @@ -13951,14 +14030,14 @@ lbProcedure *lb_create_startup_runtime(lbModule *m, lbProcedure *startup_type_in if (is_type_any(t)) { // NOTE(bill): Edge case for 'any' type Type *var_type = default_type(var->init.type); - lbAddr g = lb_add_global_generated(m, var_type, var->init); + lbAddr g = lb_add_global_generated(main_module, var_type, var->init); lb_addr_store(p, g, var->init); lbValue gp = lb_addr_get_ptr(p, g); lbValue data = lb_emit_struct_ep(p, var->var, 0); lbValue ti = lb_emit_struct_ep(p, var->var, 1); lb_emit_store(p, data, lb_emit_conv(p, gp, t_rawptr)); - lb_emit_store(p, ti, lb_type_info(m, var_type)); + lb_emit_store(p, ti, lb_type_info(main_module, var_type)); } else { LLVMTypeRef pvt = LLVMTypeOf(var->var.value); LLVMTypeRef vt = LLVMGetElementType(pvt); @@ -13975,7 +14054,7 @@ lbProcedure *lb_create_startup_runtime(lbModule *m, lbProcedure *startup_type_in lb_end_procedure_body(p); - if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { + if (!main_module->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main"); LLVMDumpValue(p->value); gb_printf_err("\n\n\n\n"); @@ -14085,9 +14164,8 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) args[0] = lb_addr_load(p, all_tests_slice); lb_emit_call(p, runner, args); } else { - lbValue *found = map_get(&m->values, hash_entity(m->info->entry_point)); - GB_ASSERT(found != nullptr); - lb_emit_call(p, *found, {}); + lbValue entry_point = lb_find_procedure_value_from_entity(m, m->info->entry_point); + lb_emit_call(p, entry_point, {}); } LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 0, false)); @@ -14105,13 +14183,51 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) return p; } +String lb_filepath_ll_for_module(lbModule *m) { + String path = m->gen->output_base; + if (m->pkg) { + path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name); + } + path = concatenate_strings(permanent_allocator(), path, STR_LIT(".ll")); + + return path; +} +String lb_filepath_obj_for_module(lbModule *m) { + String path = m->gen->output_base; + if (m->pkg) { + path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name); + } + + String ext = {}; + + if (build_context.build_mode == BuildMode_Assembly) { + ext = STR_LIT(".S"); + } else { + switch (build_context.metrics.os) { + case TargetOs_windows: + ext = STR_LIT(".obj"); + break; + case TargetOs_darwin: + case TargetOs_linux: + case TargetOs_essence: + ext = STR_LIT(".o"); + break; + case TargetOs_js: + ext = STR_LIT(".wasm-obj"); + break; + } + } + + return concatenate_strings(permanent_allocator(), path, ext); +} + void lb_generate_code(lbGenerator *gen) { #define TIME_SECTION(str) do { if (build_context.show_more_timings) timings_start_section(&global_timings, str_lit(str)); } while (0) TIME_SECTION("LLVM Initializtion"); - lbModule *m = &gen->default_module; + lbModule *default_module = &gen->default_module; CheckerInfo *info = gen->info; auto *min_dep_set = &info->minimum_dependency_set; @@ -14183,62 +14299,67 @@ void lb_generate_code(lbGenerator *gen) { LLVMSetModuleDataLayout(gen->modules.entries[i].value->mod, LLVMCreateTargetDataLayout(target_machine)); } - if (m->debug_builder) { // Debug Info - for_array(i, info->files.entries) { - AstFile *f = info->files.entries[i].value; - String fullpath = f->fullpath; - String filename = remove_directory_from_path(fullpath); - String directory = directory_from_path(fullpath); - LLVMMetadataRef res = LLVMDIBuilderCreateFile(m->debug_builder, - cast(char const *)filename.text, filename.len, - cast(char const *)directory.text, directory.len); - lb_set_llvm_metadata(m, f, res); - } - - gbString producer = gb_string_make(heap_allocator(), "odin"); - // producer = gb_string_append_fmt(producer, " version %.*s", LIT(ODIN_VERSION)); - // #ifdef NIGHTLY - // producer = gb_string_appendc(producer, "-nightly"); - // #endif - // #ifdef GIT_SHA - // producer = gb_string_append_fmt(producer, "-%s", GIT_SHA); - // #endif - - gbString split_name = gb_string_make(heap_allocator(), ""); - - LLVMBool is_optimized = build_context.optimization_level > 0; - AstFile *init_file = m->info->init_package->files[0]; - if (m->info->entry_point && m->info->entry_point->identifier && m->info->entry_point->identifier->file) { - init_file = m->info->entry_point->identifier->file; - } - - LLVMBool split_debug_inlining = false; - LLVMBool debug_info_for_profiling = false; - - m->debug_compile_unit = LLVMDIBuilderCreateCompileUnit(m->debug_builder, LLVMDWARFSourceLanguageC99, - lb_get_llvm_metadata(m, init_file), - producer, gb_string_length(producer), - is_optimized, "", 0, - 1, split_name, gb_string_length(split_name), - LLVMDWARFEmissionFull, - 0, split_debug_inlining, - debug_info_for_profiling, - "", 0, // sys_root - "", 0 // SDK - ); - GB_ASSERT(m->debug_compile_unit != nullptr); + for_array(i, gen->modules.entries) { + lbModule *m = gen->modules.entries[i].value; + if (m->debug_builder) { // Debug Info + for_array(i, info->files.entries) { + AstFile *f = info->files.entries[i].value; + String fullpath = f->fullpath; + String filename = remove_directory_from_path(fullpath); + String directory = directory_from_path(fullpath); + LLVMMetadataRef res = LLVMDIBuilderCreateFile(m->debug_builder, + cast(char const *)filename.text, filename.len, + cast(char const *)directory.text, directory.len); + lb_set_llvm_metadata(m, f, res); + } + + gbString producer = gb_string_make(heap_allocator(), "odin"); + // producer = gb_string_append_fmt(producer, " version %.*s", LIT(ODIN_VERSION)); + // #ifdef NIGHTLY + // producer = gb_string_appendc(producer, "-nightly"); + // #endif + // #ifdef GIT_SHA + // producer = gb_string_append_fmt(producer, "-%s", GIT_SHA); + // #endif + + gbString split_name = gb_string_make(heap_allocator(), ""); + + LLVMBool is_optimized = build_context.optimization_level > 0; + AstFile *init_file = m->info->init_package->files[0]; + if (m->info->entry_point && m->info->entry_point->identifier && m->info->entry_point->identifier->file) { + init_file = m->info->entry_point->identifier->file; + } + + LLVMBool split_debug_inlining = false; + LLVMBool debug_info_for_profiling = false; + + m->debug_compile_unit = LLVMDIBuilderCreateCompileUnit(m->debug_builder, LLVMDWARFSourceLanguageC99, + lb_get_llvm_metadata(m, init_file), + producer, gb_string_length(producer), + is_optimized, "", 0, + 1, split_name, gb_string_length(split_name), + LLVMDWARFEmissionFull, + 0, split_debug_inlining, + debug_info_for_profiling, + "", 0, // sys_root + "", 0 // SDK + ); + GB_ASSERT(m->debug_compile_unit != nullptr); + } } TIME_SECTION("LLVM Global Variables"); { + lbModule *m = default_module; + { // Add type info data isize max_type_info_count = info->minimum_dependency_type_info_set.entries.count+1; // gb_printf_err("max_type_info_count: %td\n", max_type_info_count); Type *t = alloc_type_array(t_type_info, max_type_info_count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), LB_TYPE_INFO_DATA_NAME); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - LLVMSetLinkage(g, LLVMInternalLinkage); + // LLVMSetLinkage(g, LLVMInternalLinkage); lbValue value = {}; value.value = g; @@ -14276,7 +14397,7 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_type_info_ptr, count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - LLVMSetLinkage(g, LLVMInternalLinkage); + // LLVMSetLinkage(g, LLVMInternalLinkage); lb_global_type_info_member_types = lb_addr({g, alloc_type_pointer(t)}); } @@ -14285,7 +14406,7 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_string, count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - LLVMSetLinkage(g, LLVMInternalLinkage); + // LLVMSetLinkage(g, LLVMInternalLinkage); lb_global_type_info_member_names = lb_addr({g, alloc_type_pointer(t)}); } { @@ -14293,7 +14414,7 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_uintptr, count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - LLVMSetLinkage(g, LLVMInternalLinkage); + // LLVMSetLinkage(g, LLVMInternalLinkage); lb_global_type_info_member_offsets = lb_addr({g, alloc_type_pointer(t)}); } @@ -14302,7 +14423,7 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_bool, count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - LLVMSetLinkage(g, LLVMInternalLinkage); + // LLVMSetLinkage(g, LLVMInternalLinkage); lb_global_type_info_member_usings = lb_addr({g, alloc_type_pointer(t)}); } @@ -14311,7 +14432,7 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_string, count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - LLVMSetLinkage(g, LLVMInternalLinkage); + // LLVMSetLinkage(g, LLVMInternalLinkage); lb_global_type_info_member_tags = lb_addr({g, alloc_type_pointer(t)}); } } @@ -14373,8 +14494,9 @@ void lb_generate_code(lbGenerator *gen) { bool is_foreign = e->Variable.is_foreign; bool is_export = e->Variable.is_export; - String name = lb_get_entity_name(m, e); + lbModule *m = &gen->default_module; + String name = lb_get_entity_name(m, e); lbValue g = {}; g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(permanent_allocator(), name)); @@ -14409,7 +14531,7 @@ void lb_generate_code(lbGenerator *gen) { } if (e->flags & EntityFlag_Static) { - LLVMSetLinkage(g.value, LLVMInternalLinkage); + // LLVMSetLinkage(g.value, LLVMInternalLinkage); } lbGlobalVariable var = {}; @@ -14439,6 +14561,7 @@ void lb_generate_code(lbGenerator *gen) { lb_add_entity(m, e, g); lb_add_member(m, name, g); + if (m->debug_builder) { String global_name = e->token.string; if (global_name.len != 0 && global_name != "_") { @@ -14509,6 +14632,10 @@ void lb_generate_code(lbGenerator *gen) { continue; } + lbModule *m = &gen->default_module; + if (USE_SEPARTE_MODULES) { + m = lb_pkg_module(gen, e->pkg); + } String mangled_name = lb_get_entity_name(m, e); @@ -14526,108 +14653,70 @@ void lb_generate_code(lbGenerator *gen) { } - TIME_SECTION("LLVM Registry Initializtion"); - - LLVMPassRegistryRef pass_registry = LLVMGetGlobalPassRegistry(); - - LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod); - LLVMPassManagerRef function_pass_manager_minimal = LLVMCreateFunctionPassManagerForModule(m->mod); - LLVMPassManagerRef function_pass_manager_size = LLVMCreateFunctionPassManagerForModule(m->mod); - LLVMPassManagerRef function_pass_manager_speed = LLVMCreateFunctionPassManagerForModule(m->mod); - defer (LLVMDisposePassManager(default_function_pass_manager)); - defer (LLVMDisposePassManager(function_pass_manager_minimal)); - defer (LLVMDisposePassManager(function_pass_manager_size)); - defer (LLVMDisposePassManager(function_pass_manager_speed)); - - LLVMInitializeFunctionPassManager(default_function_pass_manager); - LLVMInitializeFunctionPassManager(function_pass_manager_minimal); - LLVMInitializeFunctionPassManager(function_pass_manager_size); - LLVMInitializeFunctionPassManager(function_pass_manager_speed); - - lb_populate_function_pass_manager(default_function_pass_manager, false, build_context.optimization_level); - lb_populate_function_pass_manager_specific(function_pass_manager_minimal, 0); - lb_populate_function_pass_manager_specific(function_pass_manager_size, 1); - lb_populate_function_pass_manager_specific(function_pass_manager_speed, 2); - - LLVMFinalizeFunctionPassManager(default_function_pass_manager); - LLVMFinalizeFunctionPassManager(function_pass_manager_minimal); - LLVMFinalizeFunctionPassManager(function_pass_manager_size); - LLVMFinalizeFunctionPassManager(function_pass_manager_speed); - - - LLVMPassManagerRef default_function_pass_manager_without_memcpy = LLVMCreateFunctionPassManagerForModule(m->mod); - defer (LLVMDisposePassManager(default_function_pass_manager_without_memcpy)); - LLVMInitializeFunctionPassManager(default_function_pass_manager_without_memcpy); - lb_populate_function_pass_manager(default_function_pass_manager_without_memcpy, true, build_context.optimization_level); - LLVMFinalizeFunctionPassManager(default_function_pass_manager_without_memcpy); - TIME_SECTION("LLVM Runtime Type Information Creation"); - lbProcedure *startup_type_info = lb_create_startup_type_info(m); + lbProcedure *startup_type_info = lb_create_startup_type_info(default_module); TIME_SECTION("LLVM Runtime Startup Creation (Global Variables)"); - lbProcedure *startup_runtime = lb_create_startup_runtime(m, startup_type_info, global_variables); - + lbProcedure *startup_runtime = lb_create_startup_runtime(default_module, startup_type_info, global_variables); - String filepath_ll = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".ll")); TIME_SECTION("LLVM Procedure Generation"); - for_array(i, m->procedures_to_generate) { - lbProcedure *p = m->procedures_to_generate[i]; - if (p->is_done) { - continue; - } - if (p->body != nullptr) { // Build Procedure - m->curr_procedure = p; - lb_begin_procedure_body(p); - lb_build_stmt(p, p->body); - lb_end_procedure_body(p); - p->is_done = true; - m->curr_procedure = nullptr; - } - lb_end_procedure(p); - - // Add Flags - if (p->body != nullptr) { - if (p->name == "memcpy" || p->name == "memmove" || - p->name == "runtime.mem_copy" || p->name == "mem_copy_non_overlapping" || - string_starts_with(p->name, str_lit("llvm.memcpy")) || - string_starts_with(p->name, str_lit("llvm.memmove"))) { - p->flags |= lbProcedureFlag_WithoutMemcpyPass; + for_array(j, gen->modules.entries) { + lbModule *m = gen->modules.entries[j].value; + for_array(i, m->procedures_to_generate) { + lbProcedure *p = m->procedures_to_generate[i]; + if (p->is_done) { + continue; } - } - - if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { - gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %.*s\n", LIT(p->name)); - LLVMDumpValue(p->value); - gb_printf_err("\n\n\n\n"); - if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) { - gb_printf_err("LLVM Error: %s\n", llvm_error); + if (p->body != nullptr) { // Build Procedure + m->curr_procedure = p; + lb_begin_procedure_body(p); + lb_build_stmt(p, p->body); + lb_end_procedure_body(p); + p->is_done = true; + m->curr_procedure = nullptr; + } + lb_end_procedure(p); + + // Add Flags + if (p->body != nullptr) { + if (p->name == "memcpy" || p->name == "memmove" || + p->name == "runtime.mem_copy" || p->name == "mem_copy_non_overlapping" || + string_starts_with(p->name, str_lit("llvm.memcpy")) || + string_starts_with(p->name, str_lit("llvm.memmove"))) { + p->flags |= lbProcedureFlag_WithoutMemcpyPass; + } + } + + if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { + gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %.*s\n", LIT(p->name)); + LLVMDumpValue(p->value); + gb_printf_err("\n\n\n\n"); + String filepath_ll = lb_filepath_ll_for_module(m); + if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) { + gb_printf_err("LLVM Error: %s\n", llvm_error); + } + LLVMVerifyFunction(p->value, LLVMPrintMessageAction); + gb_exit(1); } - LLVMVerifyFunction(p->value, LLVMPrintMessageAction); - gb_exit(1); } } if (!(build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main)) { TIME_SECTION("LLVM main"); - lb_create_main_procedure(m, startup_runtime); + lb_create_main_procedure(default_module, startup_runtime); } - - if (m->debug_builder != nullptr) { - TIME_SECTION("LLVM Debug Info Complete Types"); - lb_debug_complete_types(m); - - - TIME_SECTION("LLVM Print Module to File"); - if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) { - gb_printf_err("LLVM Error: %s\n", llvm_error); - gb_exit(1); - return; + if (build_context.ODIN_DEBUG) { + TIME_SECTION("LLVM Debug Info Complete Types and Finalize"); + for_array(j, gen->modules.entries) { + lbModule *m = gen->modules.entries[j].value; + if (m->debug_builder != nullptr) { + lb_debug_complete_types(m); + LLVMDIBuilderFinalize(m->debug_builder); + } } - TIME_SECTION("LLVM Debug Info Builder Finalize"); - LLVMDIBuilderFinalize(m->debug_builder); } @@ -14635,6 +14724,38 @@ void lb_generate_code(lbGenerator *gen) { for_array(i, gen->modules.entries) { lbModule *m = gen->modules.entries[i].value; + LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod); + LLVMPassManagerRef function_pass_manager_minimal = LLVMCreateFunctionPassManagerForModule(m->mod); + LLVMPassManagerRef function_pass_manager_size = LLVMCreateFunctionPassManagerForModule(m->mod); + LLVMPassManagerRef function_pass_manager_speed = LLVMCreateFunctionPassManagerForModule(m->mod); + defer (LLVMDisposePassManager(default_function_pass_manager)); + defer (LLVMDisposePassManager(function_pass_manager_minimal)); + defer (LLVMDisposePassManager(function_pass_manager_size)); + defer (LLVMDisposePassManager(function_pass_manager_speed)); + + LLVMInitializeFunctionPassManager(default_function_pass_manager); + LLVMInitializeFunctionPassManager(function_pass_manager_minimal); + LLVMInitializeFunctionPassManager(function_pass_manager_size); + LLVMInitializeFunctionPassManager(function_pass_manager_speed); + + lb_populate_function_pass_manager(default_function_pass_manager, false, build_context.optimization_level); + lb_populate_function_pass_manager_specific(function_pass_manager_minimal, 0); + lb_populate_function_pass_manager_specific(function_pass_manager_size, 1); + lb_populate_function_pass_manager_specific(function_pass_manager_speed, 2); + + LLVMFinalizeFunctionPassManager(default_function_pass_manager); + LLVMFinalizeFunctionPassManager(function_pass_manager_minimal); + LLVMFinalizeFunctionPassManager(function_pass_manager_size); + LLVMFinalizeFunctionPassManager(function_pass_manager_speed); + + + LLVMPassManagerRef default_function_pass_manager_without_memcpy = LLVMCreateFunctionPassManagerForModule(m->mod); + defer (LLVMDisposePassManager(default_function_pass_manager_without_memcpy)); + LLVMInitializeFunctionPassManager(default_function_pass_manager_without_memcpy); + lb_populate_function_pass_manager(default_function_pass_manager_without_memcpy, true, build_context.optimization_level); + LLVMFinalizeFunctionPassManager(default_function_pass_manager_without_memcpy); + + for_array(i, m->procedures_to_generate) { lbProcedure *p = m->procedures_to_generate[i]; if (p->body != nullptr) { // Build Procedure @@ -14688,49 +14809,41 @@ void lb_generate_code(lbGenerator *gen) { llvm_error = nullptr; defer (LLVMDisposeMessage(llvm_error)); - String filepath_obj = {}; LLVMCodeGenFileType code_gen_file_type = LLVMObjectFile; - if (build_context.build_mode == BuildMode_Assembly) { - filepath_obj = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".S")); code_gen_file_type = LLVMAssemblyFile; - } else { - switch (build_context.metrics.os) { - case TargetOs_windows: - filepath_obj = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".obj")); - break; - case TargetOs_darwin: - case TargetOs_linux: - case TargetOs_essence: - filepath_obj = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".o")); - break; - case TargetOs_js: - filepath_obj = concatenate_strings(permanent_allocator(), gen->output_base, STR_LIT(".wasm-obj")); - break; - } } - if (LLVMVerifyModule(m->mod, LLVMReturnStatusAction, &llvm_error)) { - gb_printf_err("LLVM Error:\n%s\n", llvm_error); - if (build_context.keep_temp_files) { - TIME_SECTION("LLVM Print Module to File"); - if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) { - gb_printf_err("LLVM Error: %s\n", llvm_error); - gb_exit(1); - return; + for_array(j, gen->modules.entries) { + lbModule *m = gen->modules.entries[j].value; + if (LLVMVerifyModule(m->mod, LLVMReturnStatusAction, &llvm_error)) { + gb_printf_err("LLVM Error:\n%s\n", llvm_error); + if (build_context.keep_temp_files) { + TIME_SECTION("LLVM Print Module to File"); + String filepath_ll = lb_filepath_ll_for_module(m); + if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) { + gb_printf_err("LLVM Error: %s\n", llvm_error); + gb_exit(1); + return; + } } + gb_exit(1); + return; } - gb_exit(1); - return; } llvm_error = nullptr; if (build_context.keep_temp_files || build_context.build_mode == BuildMode_LLVM_IR) { TIME_SECTION("LLVM Print Module to File"); - if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) { - gb_printf_err("LLVM Error: %s\n", llvm_error); - gb_exit(1); - return; + + for_array(j, gen->modules.entries) { + lbModule *m = gen->modules.entries[j].value; + String filepath_ll = lb_filepath_ll_for_module(m); + if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) { + gb_printf_err("LLVM Error: %s\n", llvm_error); + gb_exit(1); + return; + } } if (build_context.build_mode == BuildMode_LLVM_IR) { gb_exit(0); @@ -14740,18 +14853,25 @@ void lb_generate_code(lbGenerator *gen) { TIME_SECTION("LLVM Object Generation"); - if (LLVMTargetMachineEmitToFile(target_machine, m->mod, cast(char *)filepath_obj.text, code_gen_file_type, &llvm_error)) { - gb_printf_err("LLVM Error: %s\n", llvm_error); - gb_exit(1); - return; - } + for_array(j, gen->modules.entries) { + lbModule *m = gen->modules.entries[j].value; + String filepath_obj = lb_filepath_obj_for_module(m); - array_add(&gen->output_object_paths, filepath_obj); + if (LLVMTargetMachineEmitToFile(target_machine, m->mod, cast(char *)filepath_obj.text, code_gen_file_type, &llvm_error)) { + gb_printf_err("LLVM Error: %s\n", llvm_error); + gb_exit(1); + return; + } - for_array(i, m->info->required_foreign_imports_through_force) { - Entity *e = m->info->required_foreign_imports_through_force[i]; - lb_add_foreign_library_path(m, e); + array_add(&gen->output_object_paths, filepath_obj); + for_array(i, m->info->required_foreign_imports_through_force) { + Entity *e = m->info->required_foreign_imports_through_force[i]; + lb_add_foreign_library_path(m, e); + } } + + + #undef TIME_SECTION } diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 392c15ea4..8a4f06117 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -85,6 +85,8 @@ struct lbModule { LLVMModuleRef mod; LLVMContextRef ctx; + struct lbGenerator *gen; + CheckerInfo *info; AstPackage *pkg; // associated @@ -108,8 +110,6 @@ struct lbModule { Map equal_procs; // Key: Type * Map hasher_procs; // Key: Type * - u32 global_array_index; - u32 global_generated_index; u32 nested_type_name_guid; Array procedures_to_generate; @@ -131,7 +131,11 @@ struct lbGenerator { String output_base; String output_name; Map modules; // Key: AstPackage * + Map modules_through_ctx; // Key: LLVMContextRef * lbModule default_module; + + gbAtomic32 global_array_index; + gbAtomic32 global_generated_index; }; @@ -271,7 +275,7 @@ String lb_get_entity_name(lbModule *m, Entity *e, String name = {}); LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char const *name, u64 value=0); void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name, u64 value); void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name); -lbProcedure *lb_create_procedure(lbModule *module, Entity *entity); +lbProcedure *lb_create_procedure(lbModule *module, Entity *entity, bool ignore_body=false); void lb_end_procedure(lbProcedure *p); @@ -383,6 +387,7 @@ lbValue lb_gen_map_hash(lbProcedure *p, lbValue key, Type *key_type); void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbAddr addr, Type *map_type, lbValue map_key, lbValue map_value, Ast *node); + void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value); lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValue value); lbValue lb_emit_source_code_location(lbProcedure *p, String const &procedure, TokenPos const &pos); diff --git a/src/main.cpp b/src/main.cpp index 48fb4dcc3..530492cbb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1542,12 +1542,19 @@ void show_timings(Checker *c, Timings *t) { } } -void remove_temp_files(String output_base) { +void remove_temp_files(lbGenerator *gen) { if (build_context.keep_temp_files) return; + String output_base = gen->output_base; + auto data = array_make(heap_allocator(), output_base.len + 30); defer (array_free(&data)); + for_array(i, gen->output_object_paths) { + String path = gen->output_object_paths[i]; + gb_file_remove(cast(char const *)path.text); + } + isize n = output_base.len; gb_memmove(data.data, output_base.text, n); #define EXT_REMOVE(s) do { \ @@ -2183,7 +2190,7 @@ int main(int arg_count, char const **arg_ptr) { show_timings(&checker, timings); } - remove_temp_files(gen.output_base); + remove_temp_files(&gen); if (run_output) { #if defined(GB_SYSTEM_WINDOWS) -- cgit v1.2.3 From d027a5f1a470233a0307bcf3c7c4fe0b6494cd87 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 3 May 2021 18:48:48 +0100 Subject: Add min_f16 and max_f16 dependencies --- src/check_builtin.cpp | 4 ++++ src/llvm_backend.cpp | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 898239234..6b299a68a 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1509,6 +1509,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 { Type *bt = base_type(x.type); + if (are_types_identical(bt, t_f16)) { + add_package_dependency(c, "runtime", "min_f16"); + add_package_dependency(c, "runtime", "max_f16"); + } if (are_types_identical(bt, t_f32)) { add_package_dependency(c, "runtime", "min_f32"); add_package_dependency(c, "runtime", "max_f32"); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index a7c149f06..9649af6c9 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -5458,6 +5458,7 @@ lbValue lb_emit_min(lbProcedure *p, Type *t, lbValue x, lbValue y) { args[0] = x; args[1] = y; switch (sz) { + case 16: return lb_emit_runtime_call(p, "min_f16", args); case 32: return lb_emit_runtime_call(p, "min_f32", args); case 64: return lb_emit_runtime_call(p, "min_f64", args); } @@ -5475,6 +5476,7 @@ lbValue lb_emit_max(lbProcedure *p, Type *t, lbValue x, lbValue y) { args[0] = x; args[1] = y; switch (sz) { + case 16: return lb_emit_runtime_call(p, "max_f16", args); case 32: return lb_emit_runtime_call(p, "max_f32", args); case 64: return lb_emit_runtime_call(p, "max_f64", args); } -- cgit v1.2.3 From dfe1dedeb1f948bebd6121e3cc31fcd4562ab0cd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 3 May 2021 19:39:36 +0100 Subject: Experimental support for `-use-separate-modules` --- src/build_settings.cpp | 4 ---- src/llvm_backend.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++-------- src/llvm_backend.hpp | 1 + src/main.cpp | 42 ++++++++++++++++--------------------- 4 files changed, 66 insertions(+), 37 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 77046cf6d..09d860cfc 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -809,10 +809,6 @@ void init_build_context(TargetMetrics *cross_target) { bc->max_align = metrics->max_align; bc->link_flags = str_lit(" "); - if (bc->metrics.os == TargetOs_windows) { - // bc->use_separate_modules = bc->optimization_level == 0; - } - // NOTE(zangent): The linker flags to set the build architecture are different // across OSs. It doesn't make sense to allocate extra data on the heap diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 9649af6c9..2acbbcff8 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2721,6 +2721,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) if (ignore_body) { p->body = nullptr; + LLVMSetLinkage(p->value, LLVMExternalLinkage); } @@ -5762,7 +5763,7 @@ lbValue lb_find_value_from_entity(lbModule *m, Entity *e) { lbValue g = {}; g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(permanent_allocator(), name)); g.type = alloc_type_pointer(e->type); - LLVMSetExternallyInitialized(g.value, true); + LLVMSetLinkage(g.value, LLVMExternalLinkage); lb_add_entity(m, e, g); lb_add_member(m, name, g); @@ -11179,15 +11180,19 @@ lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *expr) { return lb_addr_load(p, lb_build_addr(p, expr)); } + if (e->kind == Entity_Procedure) { + return lb_find_procedure_value_from_entity(m, e); + } if (USE_SEPARTE_MODULES) { lbModule *other_module = lb_pkg_module(m->gen, e->pkg); if (other_module != m) { + String name = lb_get_entity_name(other_module, e); lbValue g = {}; g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(permanent_allocator(), name)); g.type = alloc_type_pointer(e->type); - LLVMSetExternallyInitialized(g.value, true); + LLVMSetLinkage(g.value, LLVMExternalLinkage); lb_add_entity(m, e, g); lb_add_member(m, name, g); @@ -12995,13 +13000,15 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { void lb_init_module(lbModule *m, Checker *c) { m->info = &c->info; - gbString module_name = nullptr; + gbString module_name = gb_string_make(heap_allocator(), "odin_package-"); if (m->pkg) { - module_name = gb_string_make(heap_allocator(), "odin_package-"); module_name = gb_string_append_length(module_name, m->pkg->name.text, m->pkg->name.len); + } else if (USE_SEPARTE_MODULES) { + module_name = gb_string_appendc(module_name, "builtin"); + } else { + module_name = "odin_package"; } - m->ctx = LLVMContextCreate(); m->mod = LLVMModuleCreateWithNameInContext(module_name ? module_name : "odin_package", m->ctx); // m->debug_builder = nullptr; @@ -13089,6 +13096,7 @@ bool lb_init_generator(lbGenerator *gen, Checker *c) { } gbAllocator ha = heap_allocator(); array_init(&gen->output_object_paths, ha); + array_init(&gen->output_temp_paths, ha); gen->output_base = path_to_full_path(ha, gen->output_base); @@ -14110,8 +14118,20 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) lb_fill_slice(p, args, argv, argc); } + { + auto args = array_make(permanent_allocator(), 1); + args[0] = lb_const_string(p->module, str_lit("Here0\n")); + lb_emit_runtime_call(p, "print_string", args); + } + LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, startup_runtime->type)), startup_runtime->value, nullptr, 0, ""); + { + auto args = array_make(permanent_allocator(), 1); + args[0] = lb_const_string(p->module, str_lit("Here1\n")); + lb_emit_runtime_call(p, "print_string", args); + } + if (build_context.command_kind == Command_test) { Type *t_Internal_Test = find_type_in_pkg(m->info, str_lit("testing"), str_lit("Internal_Test")); Type *array_type = alloc_type_array(t_Internal_Test, m->info->testing_procedures.count); @@ -14189,6 +14209,8 @@ String lb_filepath_ll_for_module(lbModule *m) { String path = m->gen->output_base; if (m->pkg) { path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name); + } else if (USE_SEPARTE_MODULES) { + path = concatenate_strings(permanent_allocator(), path, STR_LIT("-builtin")); } path = concatenate_strings(permanent_allocator(), path, STR_LIT(".ll")); @@ -14522,6 +14544,7 @@ void lb_generate_code(lbGenerator *gen) { LLVMSetThreadLocalMode(g.value, mode); } if (is_foreign) { + LLVMSetLinkage(g.value, LLVMExternalLinkage); LLVMSetExternallyInitialized(g.value, true); lb_add_foreign_library_path(m, e->Variable.foreign_library); } else { @@ -14840,12 +14863,20 @@ void lb_generate_code(lbGenerator *gen) { for_array(j, gen->modules.entries) { lbModule *m = gen->modules.entries[j].value; + + if (LLVMGetFirstFunction(m->mod) == nullptr && + LLVMGetFirstGlobal(m->mod) == nullptr) { + continue; + } + String filepath_ll = lb_filepath_ll_for_module(m); if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) { gb_printf_err("LLVM Error: %s\n", llvm_error); gb_exit(1); return; } + array_add(&gen->output_temp_paths, filepath_ll); + } if (build_context.build_mode == BuildMode_LLVM_IR) { gb_exit(0); @@ -14857,6 +14888,16 @@ void lb_generate_code(lbGenerator *gen) { for_array(j, gen->modules.entries) { lbModule *m = gen->modules.entries[j].value; + for_array(i, m->info->required_foreign_imports_through_force) { + Entity *e = m->info->required_foreign_imports_through_force[i]; + lb_add_foreign_library_path(m, e); + } + + if (LLVMGetFirstFunction(m->mod) == nullptr && + LLVMGetFirstGlobal(m->mod) == nullptr) { + continue; + } + String filepath_obj = lb_filepath_obj_for_module(m); if (LLVMTargetMachineEmitToFile(target_machine, m->mod, cast(char *)filepath_obj.text, code_gen_file_type, &llvm_error)) { @@ -14866,10 +14907,7 @@ void lb_generate_code(lbGenerator *gen) { } array_add(&gen->output_object_paths, filepath_obj); - for_array(i, m->info->required_foreign_imports_through_force) { - Entity *e = m->info->required_foreign_imports_through_force[i]; - lb_add_foreign_library_path(m, e); - } + } diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 8a4f06117..4338edbc7 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -128,6 +128,7 @@ struct lbGenerator { CheckerInfo *info; Array output_object_paths; + Array output_temp_paths; String output_base; String output_name; Map modules; // Key: AstPackage * diff --git a/src/main.cpp b/src/main.cpp index 530492cbb..19b22ef4f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -596,6 +596,7 @@ enum BuildFlagKind { BuildFlag_NoCRT, BuildFlag_NoEntryPoint, BuildFlag_UseLLD, + BuildFlag_UseSeparateModules, BuildFlag_Vet, BuildFlag_VetExtra, BuildFlag_UseLLVMApi, @@ -716,6 +717,7 @@ bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None, Command__does_check &~ Command_test); add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None, Command__does_build); + add_flag(&build_flags, BuildFlag_UseSeparateModules,str_lit("use-separate-modules"),BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetExtra, str_lit("vet-extra"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_UseLLVMApi, str_lit("llvm-api"), BuildFlagParam_None, Command__does_build); @@ -1195,6 +1197,10 @@ bool parse_build_flags(Array args) { build_context.use_lld = true; break; + case BuildFlag_UseSeparateModules: + build_context.use_separate_modules = true; + break; + case BuildFlag_Vet: build_context.vet = true; break; @@ -1545,35 +1551,17 @@ void show_timings(Checker *c, Timings *t) { void remove_temp_files(lbGenerator *gen) { if (build_context.keep_temp_files) return; - String output_base = gen->output_base; - - auto data = array_make(heap_allocator(), output_base.len + 30); - defer (array_free(&data)); - - for_array(i, gen->output_object_paths) { - String path = gen->output_object_paths[i]; + for_array(i, gen->output_temp_paths) { + String path = gen->output_temp_paths[i]; gb_file_remove(cast(char const *)path.text); } - isize n = output_base.len; - gb_memmove(data.data, output_base.text, n); -#define EXT_REMOVE(s) do { \ - gb_memmove(data.data+n, s, gb_size_of(s)); \ - gb_file_remove(cast(char const *)data.data); \ - } while (0) - EXT_REMOVE(".ll"); - EXT_REMOVE(".bc"); - EXT_REMOVE("_memcpy_pass.bc"); if (build_context.build_mode != BuildMode_Object && !build_context.keep_object_files) { - #if defined(GB_SYSTEM_WINDOWS) - EXT_REMOVE(".obj"); - EXT_REMOVE(".res"); - #else - EXT_REMOVE(".o"); - #endif + for_array(i, gen->output_object_paths) { + String path = gen->output_object_paths[i]; + gb_file_remove(cast(char const *)path.text); + } } - -#undef EXT_REMOVE } @@ -1734,6 +1722,12 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(1, "-use-lld"); print_usage_line(2, "Use the LLD linker rather than the default"); print_usage_line(0, ""); + + print_usage_line(1, "-use-separate-modules"); + print_usage_line(2, "The backend generates multiple build units which are then linked together"); + print_usage_line(2, "Normally, a single build unit is generated for a standard project"); + print_usage_line(0, ""); + } if (check) { -- cgit v1.2.3 From d76ba7895b98edd8ad5898937850e20e27706684 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 3 May 2021 19:59:17 +0100 Subject: Minor fixes to -use-separate-modules --- src/llvm_backend.cpp | 23 +++++------------------ src/llvm_backend.hpp | 3 ++- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 2acbbcff8..b98ef2ae5 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -385,18 +385,7 @@ void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) { GB_ASSERT(is_type_polymorphic(e->type)); { - lbValue *found = nullptr; - if (p->module != e->code_gen_module) { - gb_mutex_lock(&p->module->mutex); - } - GB_ASSERT(e->code_gen_module != nullptr); - found = map_get(&e->code_gen_module->values, hash_entity(e)); - if (p->module != e->code_gen_module) { - gb_mutex_unlock(&p->module->mutex); - } - GB_ASSERT_MSG(found != nullptr, "%.*s", LIT(e->token.string)); - - lb_emit_call(p, *found, args); + lb_emit_call(p, lb_find_procedure_value_from_entity(p->module, e), args); } return; @@ -8194,6 +8183,8 @@ Array lb_value_to_array(lbProcedure *p, lbValue value) { lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr, Array const &processed_args, Type *abi_rt, lbAddr context_ptr, ProcInlining inlining) { + GB_ASSERT(p->module->ctx == LLVMGetTypeContext(LLVMTypeOf(value.value))); + unsigned arg_count = cast(unsigned)processed_args.count; if (return_ptr.value != nullptr) { arg_count += 1; @@ -8418,9 +8409,7 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, if (e != nullptr && entity_has_deferred_procedure(e)) { DeferredProcedureKind kind = e->Procedure.deferred_procedure.kind; Entity *deferred_entity = e->Procedure.deferred_procedure.entity; - lbValue *deferred_found = map_get(&p->module->values, hash_entity(deferred_entity)); - GB_ASSERT(deferred_found != nullptr); - lbValue deferred = *deferred_found; + lbValue deferred = lb_find_procedure_value_from_entity(p->module, deferred_entity); auto in_args = args; @@ -14147,8 +14136,6 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) for_array(i, m->info->testing_procedures) { Entity *testing_proc = m->info->testing_procedures[i]; String name = testing_proc->token.string; - lbValue *found = map_get(&m->values, hash_entity(testing_proc)); - GB_ASSERT(found != nullptr); String pkg_name = {}; if (testing_proc->pkg != nullptr) { @@ -14156,7 +14143,7 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) } lbValue v_pkg = lb_find_or_add_entity_string(m, pkg_name); lbValue v_name = lb_find_or_add_entity_string(m, name); - lbValue v_proc = *found; + lbValue v_proc = lb_find_procedure_value_from_entity(m, testing_proc); indices[1] = LLVMConstInt(lb_type(m, t_int), i, false); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 4338edbc7..940a7d69d 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -387,7 +387,8 @@ lbValue lb_gen_map_header(lbProcedure *p, lbValue map_val_ptr, Type *map_type); lbValue lb_gen_map_hash(lbProcedure *p, lbValue key, Type *key_type); void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbAddr addr, Type *map_type, lbValue map_key, lbValue map_value, Ast *node); - +lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e); +lbValue lb_find_value_from_entity(lbModule *m, Entity *e); void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value); lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValue value); -- cgit v1.2.3 From 5d70289b69ec96c4159105206897519407f02189 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 3 May 2021 20:03:37 +0100 Subject: Remove test code --- src/llvm_backend.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index b98ef2ae5..e7026c4ef 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -14107,20 +14107,8 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) lb_fill_slice(p, args, argv, argc); } - { - auto args = array_make(permanent_allocator(), 1); - args[0] = lb_const_string(p->module, str_lit("Here0\n")); - lb_emit_runtime_call(p, "print_string", args); - } - LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, startup_runtime->type)), startup_runtime->value, nullptr, 0, ""); - { - auto args = array_make(permanent_allocator(), 1); - args[0] = lb_const_string(p->module, str_lit("Here1\n")); - lb_emit_runtime_call(p, "print_string", args); - } - if (build_context.command_kind == Command_test) { Type *t_Internal_Test = find_type_in_pkg(m->info, str_lit("testing"), str_lit("Internal_Test")); Type *array_type = alloc_type_array(t_Internal_Test, m->info->testing_procedures.count); -- cgit v1.2.3 From 866d5302fee24b74e8097d042ac3a1e6cb9e8f74 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 3 May 2021 20:05:45 +0100 Subject: Add [EXPERIMENTAL] warning to -user-separate-modules help message --- src/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.cpp b/src/main.cpp index 19b22ef4f..3f92632f2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1724,6 +1724,7 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(0, ""); print_usage_line(1, "-use-separate-modules"); + print_usage_line(1, "[EXPERIMENTAL]"); print_usage_line(2, "The backend generates multiple build units which are then linked together"); print_usage_line(2, "Normally, a single build unit is generated for a standard project"); print_usage_line(0, ""); -- cgit v1.2.3 From a5eea97edbffc531405fea08122fc3d0d6b408dc Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 3 May 2021 20:45:27 +0100 Subject: Prepare to multithread object generation --- src/llvm_backend.cpp | 138 +++++++++++++++++++++++++++++++++++++++++++-------- src/llvm_backend.hpp | 4 +- src/main.cpp | 2 +- 3 files changed, 119 insertions(+), 25 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index e7026c4ef..6d2e49670 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2,6 +2,11 @@ #define USE_SEPARTE_MODULES build_context.use_separate_modules #endif +#ifndef MULTITHREAD_OBJECT_GENERATION +#define MULTITHREAD_OBJECT_GENERATION 0 +#endif + + #include "llvm_backend.hpp" #include "llvm_abi.cpp" #include "llvm_backend_opt.cpp" @@ -13026,7 +13031,6 @@ void lb_init_module(lbModule *m, Checker *c) { m->debug_builder = LLVMCreateDIBuilder(m->mod); } - gb_mutex_init(&m->mutex); gbAllocator a = heap_allocator(); map_init(&m->types, a); map_init(&m->llvm_types, a); @@ -13098,6 +13102,8 @@ bool lb_init_generator(lbGenerator *gen, Checker *c) { map_init(&gen->modules, permanent_allocator(), gen->info->packages.entries.count*2); map_init(&gen->modules_through_ctx, permanent_allocator(), gen->info->packages.entries.count*2); + gb_mutex_init(&gen->mutex); + if (USE_SEPARTE_MODULES) { for_array(i, gen->info->packages.entries) { AstPackage *pkg = gen->info->packages.entries[i].value; @@ -13945,7 +13951,6 @@ struct lbGlobalVariable { lbProcedure *lb_create_startup_type_info(lbModule *m) { LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod); - defer (LLVMDisposePassManager(default_function_pass_manager)); lb_populate_function_pass_manager(default_function_pass_manager, false, build_context.optimization_level); LLVMFinalizeFunctionPassManager(default_function_pass_manager); @@ -13977,7 +13982,6 @@ lbProcedure *lb_create_startup_type_info(lbModule *m) { lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *startup_type_info, Array &global_variables) { // Startup Runtime LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(main_module->mod); - defer (LLVMDisposePassManager(default_function_pass_manager)); lb_populate_function_pass_manager(default_function_pass_manager, false, build_context.optimization_level); LLVMFinalizeFunctionPassManager(default_function_pass_manager); @@ -14068,7 +14072,6 @@ lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *start lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) { LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod); - defer (LLVMDisposePassManager(default_function_pass_manager)); lb_populate_function_pass_manager(default_function_pass_manager, false, build_context.optimization_level); LLVMFinalizeFunctionPassManager(default_function_pass_manager); @@ -14221,6 +14224,67 @@ String lb_filepath_obj_for_module(lbModule *m) { } +bool lb_is_module_empty(lbModule *m) { + if (LLVMGetFirstFunction(m->mod) == nullptr && + LLVMGetFirstGlobal(m->mod) == nullptr) { + return true; + } + for (auto fn = LLVMGetFirstFunction(m->mod); fn != nullptr; fn = LLVMGetNextFunction(fn)) { + if (LLVMGetFirstBasicBlock(fn) != nullptr) { + return false; + } + } + + for (auto g = LLVMGetFirstGlobal(m->mod); g != nullptr; g = LLVMGetNextGlobal(g)) { + if (LLVMGetLinkage(g) == LLVMExternalLinkage) { + continue; + } + if (!LLVMIsExternallyInitialized(g)) { + return false; + } + } + return true; +} + +struct lbLLVMEmitWorker { + LLVMTargetMachineRef target_machine; + LLVMCodeGenFileType code_gen_file_type; + String filepath_obj; + lbModule *m; +}; + +WORKER_TASK_PROC(lb_llvm_emit_worker_proc) { + GB_ASSERT(MULTITHREAD_OBJECT_GENERATION); + + char *llvm_error = nullptr; + + auto wd = cast(lbLLVMEmitWorker *)data; + gbMutex *mutex = &wd->m->gen->mutex; + + +#if 1 + gb_mutex_lock(mutex); + defer (gb_mutex_unlock(mutex)); + if (LLVMTargetMachineEmitToFile(wd->target_machine, wd->m->mod, cast(char *)wd->filepath_obj.text, wd->code_gen_file_type, &llvm_error)) { + gb_printf_err("LLVM Error: %s\n", llvm_error); + gb_exit(1); + return 1; + } +#else + LLVMMemoryBufferRef mem_buf = nullptr; + + if (LLVMTargetMachineEmitToMemoryBuffer(wd->target_machine, wd->m->mod, wd->code_gen_file_type, &llvm_error, &mem_buf)) { + gb_printf_err("LLVM Error: %s\n", llvm_error); + gb_exit(1); + return 1; + } +#endif + + return 0; +} + + + void lb_generate_code(lbGenerator *gen) { #define TIME_SECTION(str) do { if (build_context.show_more_timings) timings_start_section(&global_timings, str_lit(str)); } while (0) @@ -14728,10 +14792,6 @@ void lb_generate_code(lbGenerator *gen) { LLVMPassManagerRef function_pass_manager_minimal = LLVMCreateFunctionPassManagerForModule(m->mod); LLVMPassManagerRef function_pass_manager_size = LLVMCreateFunctionPassManagerForModule(m->mod); LLVMPassManagerRef function_pass_manager_speed = LLVMCreateFunctionPassManagerForModule(m->mod); - defer (LLVMDisposePassManager(default_function_pass_manager)); - defer (LLVMDisposePassManager(function_pass_manager_minimal)); - defer (LLVMDisposePassManager(function_pass_manager_size)); - defer (LLVMDisposePassManager(function_pass_manager_speed)); LLVMInitializeFunctionPassManager(default_function_pass_manager); LLVMInitializeFunctionPassManager(function_pass_manager_minimal); @@ -14750,7 +14810,6 @@ void lb_generate_code(lbGenerator *gen) { LLVMPassManagerRef default_function_pass_manager_without_memcpy = LLVMCreateFunctionPassManagerForModule(m->mod); - defer (LLVMDisposePassManager(default_function_pass_manager_without_memcpy)); LLVMInitializeFunctionPassManager(default_function_pass_manager_without_memcpy); lb_populate_function_pass_manager(default_function_pass_manager_without_memcpy, true, build_context.optimization_level); LLVMFinalizeFunctionPassManager(default_function_pass_manager_without_memcpy); @@ -14798,7 +14857,6 @@ void lb_generate_code(lbGenerator *gen) { TIME_SECTION("LLVM Module Pass"); LLVMPassManagerRef module_pass_manager = LLVMCreatePassManager(); - defer (LLVMDisposePassManager(module_pass_manager)); lb_populate_module_pass_manager(target_machine, module_pass_manager, build_context.optimization_level); for_array(i, gen->modules.entries) { @@ -14839,8 +14897,7 @@ void lb_generate_code(lbGenerator *gen) { for_array(j, gen->modules.entries) { lbModule *m = gen->modules.entries[j].value; - if (LLVMGetFirstFunction(m->mod) == nullptr && - LLVMGetFirstGlobal(m->mod) == nullptr) { + if (lb_is_module_empty(m)) { continue; } @@ -14859,8 +14916,8 @@ void lb_generate_code(lbGenerator *gen) { } } - TIME_SECTION("LLVM Object Generation"); + TIME_SECTION("LLVM Add Foreign Library Paths"); for_array(j, gen->modules.entries) { lbModule *m = gen->modules.entries[j].value; for_array(i, m->info->required_foreign_imports_through_force) { @@ -14868,23 +14925,60 @@ void lb_generate_code(lbGenerator *gen) { lb_add_foreign_library_path(m, e); } - if (LLVMGetFirstFunction(m->mod) == nullptr && - LLVMGetFirstGlobal(m->mod) == nullptr) { + if (lb_is_module_empty(m)) { continue; } + } - String filepath_obj = lb_filepath_obj_for_module(m); + TIME_SECTION("LLVM Object Generation"); - if (LLVMTargetMachineEmitToFile(target_machine, m->mod, cast(char *)filepath_obj.text, code_gen_file_type, &llvm_error)) { - gb_printf_err("LLVM Error: %s\n", llvm_error); - gb_exit(1); - return; + isize thread_count = gb_max(build_context.thread_count, 1); + isize worker_count = thread_count-1; + if (USE_SEPARTE_MODULES && MULTITHREAD_OBJECT_GENERATION && worker_count > 0) { + ThreadPool pool = {}; + thread_pool_init(&pool, heap_allocator(), worker_count, "LLVMEmitWork"); + defer (thread_pool_destroy(&pool)); + + for_array(j, gen->modules.entries) { + lbModule *m = gen->modules.entries[j].value; + if (lb_is_module_empty(m)) { + continue; + } + String filepath_ll = lb_filepath_ll_for_module(m); + String filepath_obj = lb_filepath_obj_for_module(m); + array_add(&gen->output_object_paths, filepath_obj); + array_add(&gen->output_temp_paths, filepath_ll); + + auto *wd = gb_alloc_item(heap_allocator(), lbLLVMEmitWorker); + wd->target_machine = target_machine; + wd->code_gen_file_type = code_gen_file_type; + wd->filepath_obj = filepath_obj; + wd->m = m; + + thread_pool_add_task(&pool, lb_llvm_emit_worker_proc, wd); } - array_add(&gen->output_object_paths, filepath_obj); + thread_pool_start(&pool); + thread_pool_wait_to_process(&pool); + } else { + for_array(j, gen->modules.entries) { + lbModule *m = gen->modules.entries[j].value; + if (lb_is_module_empty(m)) { + continue; + } + // TIME_SECTION("LLVM Generate Object"); - } + String filepath_obj = lb_filepath_obj_for_module(m); + if (LLVMTargetMachineEmitToFile(target_machine, m->mod, cast(char *)filepath_obj.text, code_gen_file_type, &llvm_error)) { + gb_printf_err("LLVM Error: %s\n", llvm_error); + gb_exit(1); + return; + } + + array_add(&gen->output_object_paths, filepath_obj); + } + } diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 940a7d69d..8c4e9b256 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -90,8 +90,6 @@ struct lbModule { CheckerInfo *info; AstPackage *pkg; // associated - gbMutex mutex; - Map types; // Key: Type * Map llvm_types; // Key: LLVMTypeRef i32 internal_type_level; @@ -127,6 +125,8 @@ struct lbModule { struct lbGenerator { CheckerInfo *info; + gbMutex mutex; + Array output_object_paths; Array output_temp_paths; String output_base; diff --git a/src/main.cpp b/src/main.cpp index 3f92632f2..756fcc3ac 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1929,7 +1929,7 @@ int main(int arg_count, char const **arg_ptr) { Timings *timings = &global_timings; - timings_init(timings, str_lit("Total Time"), 128); + timings_init(timings, str_lit("Total Time"), 2048); defer (timings_destroy(timings)); arena_init(&permanent_arena, heap_allocator()); -- cgit v1.2.3 From b83e67f45f3bde2abab70154d76307a9e3d7f4e3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 4 May 2021 00:06:20 +0100 Subject: Get LLVM backend multithread for object generation with `-use-separate-modules` --- src/llvm_backend.cpp | 63 ++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 6d2e49670..ceb470d75 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1,3 +1,5 @@ +#define MULTITHREAD_OBJECT_GENERATION 1 + #ifndef USE_SEPARTE_MODULES #define USE_SEPARTE_MODULES build_context.use_separate_modules #endif @@ -14259,26 +14261,11 @@ WORKER_TASK_PROC(lb_llvm_emit_worker_proc) { char *llvm_error = nullptr; auto wd = cast(lbLLVMEmitWorker *)data; - gbMutex *mutex = &wd->m->gen->mutex; - -#if 1 - gb_mutex_lock(mutex); - defer (gb_mutex_unlock(mutex)); if (LLVMTargetMachineEmitToFile(wd->target_machine, wd->m->mod, cast(char *)wd->filepath_obj.text, wd->code_gen_file_type, &llvm_error)) { gb_printf_err("LLVM Error: %s\n", llvm_error); gb_exit(1); - return 1; - } -#else - LLVMMemoryBufferRef mem_buf = nullptr; - - if (LLVMTargetMachineEmitToMemoryBuffer(wd->target_machine, wd->m->mod, wd->code_gen_file_type, &llvm_error, &mem_buf)) { - gb_printf_err("LLVM Error: %s\n", llvm_error); - gb_exit(1); - return 1; } -#endif return 0; } @@ -14287,6 +14274,7 @@ WORKER_TASK_PROC(lb_llvm_emit_worker_proc) { void lb_generate_code(lbGenerator *gen) { #define TIME_SECTION(str) do { if (build_context.show_more_timings) timings_start_section(&global_timings, str_lit(str)); } while (0) + #define TIME_SECTION_WITH_LEN(str, len) do { if (build_context.show_more_timings) timings_start_section(&global_timings, make_string((u8 *)str, len)); } while (0) TIME_SECTION("LLVM Initializtion"); @@ -14349,17 +14337,17 @@ void lb_generate_code(lbGenerator *gen) { } // NOTE(bill): Target Machine Creation - LLVMTargetMachineRef target_machine = LLVMCreateTargetMachine( - target, target_triple, llvm_cpu, - llvm_features, - code_gen_level, - LLVMRelocDefault, - code_mode); - defer (LLVMDisposeTargetMachine(target_machine)); - + // NOTE(bill, 2021-05-04): Target machines must be unique to each module because they are not thread safe + auto target_machines = array_make(permanent_allocator(), gen->modules.entries.count); for_array(i, gen->modules.entries) { - LLVMSetModuleDataLayout(gen->modules.entries[i].value->mod, LLVMCreateTargetDataLayout(target_machine)); + target_machines[i] = LLVMCreateTargetMachine( + target, target_triple, llvm_cpu, + llvm_features, + code_gen_level, + LLVMRelocDefault, + code_mode); + LLVMSetModuleDataLayout(gen->modules.entries[i].value->mod, LLVMCreateTargetDataLayout(target_machines[i])); } for_array(i, gen->modules.entries) { @@ -14856,10 +14844,10 @@ void lb_generate_code(lbGenerator *gen) { TIME_SECTION("LLVM Module Pass"); - LLVMPassManagerRef module_pass_manager = LLVMCreatePassManager(); - lb_populate_module_pass_manager(target_machine, module_pass_manager, build_context.optimization_level); - for_array(i, gen->modules.entries) { + LLVMPassManagerRef module_pass_manager = LLVMCreatePassManager(); + auto target_machine = target_machines[i]; + lb_populate_module_pass_manager(target_machine, module_pass_manager, build_context.optimization_level); lbModule *m = gen->modules.entries[i].value; LLVMRunPassManager(module_pass_manager, m->mod); } @@ -14918,6 +14906,7 @@ void lb_generate_code(lbGenerator *gen) { TIME_SECTION("LLVM Add Foreign Library Paths"); + for_array(j, gen->modules.entries) { lbModule *m = gen->modules.entries[j].value; for_array(i, m->info->required_foreign_imports_through_force) { @@ -14934,7 +14923,9 @@ void lb_generate_code(lbGenerator *gen) { isize thread_count = gb_max(build_context.thread_count, 1); isize worker_count = thread_count-1; - if (USE_SEPARTE_MODULES && MULTITHREAD_OBJECT_GENERATION && worker_count > 0) { + + LLVMBool do_threading = LLVMIsMultithreaded(); + if (do_threading && USE_SEPARTE_MODULES && MULTITHREAD_OBJECT_GENERATION && worker_count > 0) { ThreadPool pool = {}; thread_pool_init(&pool, heap_allocator(), worker_count, "LLVMEmitWork"); defer (thread_pool_destroy(&pool)); @@ -14944,17 +14935,17 @@ void lb_generate_code(lbGenerator *gen) { if (lb_is_module_empty(m)) { continue; } + String filepath_ll = lb_filepath_ll_for_module(m); String filepath_obj = lb_filepath_obj_for_module(m); array_add(&gen->output_object_paths, filepath_obj); array_add(&gen->output_temp_paths, filepath_ll); auto *wd = gb_alloc_item(heap_allocator(), lbLLVMEmitWorker); - wd->target_machine = target_machine; + wd->target_machine = target_machines[j]; wd->code_gen_file_type = code_gen_file_type; wd->filepath_obj = filepath_obj; wd->m = m; - thread_pool_add_task(&pool, lb_llvm_emit_worker_proc, wd); } @@ -14966,17 +14957,21 @@ void lb_generate_code(lbGenerator *gen) { if (lb_is_module_empty(m)) { continue; } - // TIME_SECTION("LLVM Generate Object"); String filepath_obj = lb_filepath_obj_for_module(m); + array_add(&gen->output_object_paths, filepath_obj); + + String short_name = remove_directory_from_path(filepath_obj); + gbString section_name = gb_string_make(heap_allocator(), "LLVM Generate Object: "); + section_name = gb_string_append_length(section_name, short_name.text, short_name.len); + + TIME_SECTION_WITH_LEN(section_name, gb_string_length(section_name)); - if (LLVMTargetMachineEmitToFile(target_machine, m->mod, cast(char *)filepath_obj.text, code_gen_file_type, &llvm_error)) { + if (LLVMTargetMachineEmitToFile(target_machines[j], m->mod, cast(char *)filepath_obj.text, code_gen_file_type, &llvm_error)) { gb_printf_err("LLVM Error: %s\n", llvm_error); gb_exit(1); return; } - - array_add(&gen->output_object_paths, filepath_obj); } } -- cgit v1.2.3 From 17001bf38c85825c4f7191f5a784ab8a65baeb40 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 4 May 2021 00:45:09 +0100 Subject: Nearly approach full functionality for -use-separate-modules coupled with multithreading --- src/llvm_backend.cpp | 217 ++++++++++++++++++++++++++++++--------------------- src/thread_pool.cpp | 3 - 2 files changed, 129 insertions(+), 91 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index ceb470d75..a84c26c9b 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -13,7 +13,9 @@ #include "llvm_abi.cpp" #include "llvm_backend_opt.cpp" -gb_global lbAddr lb_global_type_info_data = {}; +gb_global ThreadPool lb_thread_pool = {}; + +gb_global Entity *lb_global_type_info_data_entity = {}; gb_global lbAddr lb_global_type_info_member_types = {}; gb_global lbAddr lb_global_type_info_member_names = {}; gb_global lbAddr lb_global_type_info_member_offsets = {}; @@ -28,6 +30,12 @@ gb_global isize lb_global_type_info_member_usings_index = 0; gb_global isize lb_global_type_info_member_tags_index = 0; +lbValue lb_global_type_info_data_ptr(lbModule *m) { + lbValue v = lb_find_value_from_entity(m, lb_global_type_info_data_entity); + return v; +} + + struct lbLoopData { lbAddr idx_addr; lbValue idx; @@ -2539,6 +2547,7 @@ void lb_ensure_abi_function_type(lbModule *m, lbProcedure *p) { lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) { GB_ASSERT(entity != nullptr); + GB_ASSERT(entity->kind == Entity_Procedure); String link_name = {}; @@ -5674,7 +5683,7 @@ lbValue lb_type_info(lbModule *m, Type *type) { }; lbValue value = {}; - value.value = LLVMConstGEP(lb_global_type_info_data.addr.value, indices, gb_count_of(indices)); + value.value = LLVMConstGEP(lb_global_type_info_data_ptr(m).value, indices, gb_count_of(indices)); value.type = t_type_info_ptr; return value; } @@ -5736,7 +5745,7 @@ lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) { lbValue lb_find_value_from_entity(lbModule *m, Entity *e) { e = strip_entity_wrapping(e); GB_ASSERT(e != nullptr); - if (is_type_proc(e->type)) { + if (e->kind == Entity_Procedure) { return lb_find_procedure_value_from_entity(m, e); } @@ -13184,7 +13193,7 @@ lbValue lb_get_type_info_ptr(lbModule *m, Type *type) { lbValue res = {}; res.type = t_type_info_ptr; - res.value = LLVMConstGEP(lb_global_type_info_data.addr.value, indices, cast(unsigned)gb_count_of(indices)); + res.value = LLVMConstGEP(lb_global_type_info_data_ptr(m).value, indices, cast(unsigned)gb_count_of(indices)); return res; } @@ -13251,12 +13260,12 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da { // NOTE(bill): Set the type_table slice with the global backing array lbValue global_type_table = lb_find_runtime_value(m, str_lit("type_table")); - Type *type = base_type(lb_addr_type(lb_global_type_info_data)); + Type *type = base_type(lb_global_type_info_data_entity->type); GB_ASSERT(is_type_array(type)); LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)}; LLVMValueRef values[2] = { - LLVMConstInBoundsGEP(lb_global_type_info_data.addr.value, indices, gb_count_of(indices)), + LLVMConstInBoundsGEP(lb_global_type_info_data_ptr(m).value, indices, gb_count_of(indices)), LLVMConstInt(lb_type(m, t_int), type->Array.count, true), }; LLVMValueRef slice = llvm_const_named_struct(llvm_addr_type(global_type_table), values, gb_count_of(values)); @@ -13288,7 +13297,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da } lbValue tag = {}; - lbValue ti_ptr = lb_emit_array_epi(p, lb_global_type_info_data.addr, cast(i32)entry_index); + lbValue ti_ptr = lb_emit_array_epi(p, lb_global_type_info_data_ptr(m), cast(i32)entry_index); lbValue variant_ptr = lb_emit_struct_ep(p, ti_ptr, 4); lbValue type_info_flags = lb_const_int(p->module, t_type_info_flags, type_info_flags_of_type(t)); @@ -14270,6 +14279,96 @@ WORKER_TASK_PROC(lb_llvm_emit_worker_proc) { return 0; } +WORKER_TASK_PROC(lb_llvm_function_pass_worker_proc) { + GB_ASSERT(MULTITHREAD_OBJECT_GENERATION); + + auto m = cast(lbModule *)data; + + LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod); + LLVMPassManagerRef function_pass_manager_minimal = LLVMCreateFunctionPassManagerForModule(m->mod); + LLVMPassManagerRef function_pass_manager_size = LLVMCreateFunctionPassManagerForModule(m->mod); + LLVMPassManagerRef function_pass_manager_speed = LLVMCreateFunctionPassManagerForModule(m->mod); + + LLVMInitializeFunctionPassManager(default_function_pass_manager); + LLVMInitializeFunctionPassManager(function_pass_manager_minimal); + LLVMInitializeFunctionPassManager(function_pass_manager_size); + LLVMInitializeFunctionPassManager(function_pass_manager_speed); + + lb_populate_function_pass_manager(default_function_pass_manager, false, build_context.optimization_level); + lb_populate_function_pass_manager_specific(function_pass_manager_minimal, 0); + lb_populate_function_pass_manager_specific(function_pass_manager_size, 1); + lb_populate_function_pass_manager_specific(function_pass_manager_speed, 2); + + LLVMFinalizeFunctionPassManager(default_function_pass_manager); + LLVMFinalizeFunctionPassManager(function_pass_manager_minimal); + LLVMFinalizeFunctionPassManager(function_pass_manager_size); + LLVMFinalizeFunctionPassManager(function_pass_manager_speed); + + + LLVMPassManagerRef default_function_pass_manager_without_memcpy = LLVMCreateFunctionPassManagerForModule(m->mod); + LLVMInitializeFunctionPassManager(default_function_pass_manager_without_memcpy); + lb_populate_function_pass_manager(default_function_pass_manager_without_memcpy, true, build_context.optimization_level); + LLVMFinalizeFunctionPassManager(default_function_pass_manager_without_memcpy); + + + for_array(i, m->procedures_to_generate) { + lbProcedure *p = m->procedures_to_generate[i]; + if (p->body != nullptr) { // Build Procedure + if (p->flags & lbProcedureFlag_WithoutMemcpyPass) { + LLVMRunFunctionPassManager(default_function_pass_manager_without_memcpy, p->value); + } else { + if (p->entity && p->entity->kind == Entity_Procedure) { + switch (p->entity->Procedure.optimization_mode) { + case ProcedureOptimizationMode_None: + case ProcedureOptimizationMode_Minimal: + LLVMRunFunctionPassManager(function_pass_manager_minimal, p->value); + break; + case ProcedureOptimizationMode_Size: + LLVMRunFunctionPassManager(function_pass_manager_size, p->value); + break; + case ProcedureOptimizationMode_Speed: + LLVMRunFunctionPassManager(function_pass_manager_speed, p->value); + break; + default: + LLVMRunFunctionPassManager(default_function_pass_manager, p->value); + break; + } + } else { + LLVMRunFunctionPassManager(default_function_pass_manager, p->value); + } + } + } + } + + for_array(i, m->equal_procs.entries) { + lbProcedure *p = m->equal_procs.entries[i].value; + LLVMRunFunctionPassManager(default_function_pass_manager, p->value); + } + for_array(i, m->hasher_procs.entries) { + lbProcedure *p = m->hasher_procs.entries[i].value; + LLVMRunFunctionPassManager(default_function_pass_manager, p->value); + } + + return 0; +} + + +struct lbLLVMModulePassWorkerData { + lbModule *m; + LLVMTargetMachineRef target_machine; +}; + +WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) { + GB_ASSERT(MULTITHREAD_OBJECT_GENERATION); + + auto wd = cast(lbLLVMModulePassWorkerData *)data; + + LLVMPassManagerRef module_pass_manager = LLVMCreatePassManager(); + lb_populate_module_pass_manager(wd->target_machine, module_pass_manager, build_context.optimization_level); + LLVMRunPassManager(module_pass_manager, wd->m->mod); + + return 0; +} void lb_generate_code(lbGenerator *gen) { @@ -14278,6 +14377,14 @@ void lb_generate_code(lbGenerator *gen) { TIME_SECTION("LLVM Initializtion"); + isize thread_count = gb_max(build_context.thread_count, 1); + isize worker_count = thread_count-1; + + LLVMBool do_threading = (LLVMIsMultithreaded() && USE_SEPARTE_MODULES && MULTITHREAD_OBJECT_GENERATION && worker_count > 0); + + thread_pool_init(&lb_thread_pool, heap_allocator(), worker_count, "LLVMBackend"); + defer (thread_pool_destroy(&lb_thread_pool)); + lbModule *default_module = &gen->default_module; CheckerInfo *info = gen->info; @@ -14415,7 +14522,9 @@ void lb_generate_code(lbGenerator *gen) { lbValue value = {}; value.value = g; value.type = alloc_type_pointer(t); - lb_global_type_info_data = lb_addr(value); + + lb_global_type_info_data_entity = alloc_entity_variable(nullptr, blank_token, t, EntityState_Resolved); + lb_add_entity(m, lb_global_type_info_data_entity, value); } { // Type info member buffer // NOTE(bill): Removes need for heap allocation by making it global memory @@ -14776,82 +14885,22 @@ void lb_generate_code(lbGenerator *gen) { for_array(i, gen->modules.entries) { lbModule *m = gen->modules.entries[i].value; - LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod); - LLVMPassManagerRef function_pass_manager_minimal = LLVMCreateFunctionPassManagerForModule(m->mod); - LLVMPassManagerRef function_pass_manager_size = LLVMCreateFunctionPassManagerForModule(m->mod); - LLVMPassManagerRef function_pass_manager_speed = LLVMCreateFunctionPassManagerForModule(m->mod); - - LLVMInitializeFunctionPassManager(default_function_pass_manager); - LLVMInitializeFunctionPassManager(function_pass_manager_minimal); - LLVMInitializeFunctionPassManager(function_pass_manager_size); - LLVMInitializeFunctionPassManager(function_pass_manager_speed); - - lb_populate_function_pass_manager(default_function_pass_manager, false, build_context.optimization_level); - lb_populate_function_pass_manager_specific(function_pass_manager_minimal, 0); - lb_populate_function_pass_manager_specific(function_pass_manager_size, 1); - lb_populate_function_pass_manager_specific(function_pass_manager_speed, 2); - - LLVMFinalizeFunctionPassManager(default_function_pass_manager); - LLVMFinalizeFunctionPassManager(function_pass_manager_minimal); - LLVMFinalizeFunctionPassManager(function_pass_manager_size); - LLVMFinalizeFunctionPassManager(function_pass_manager_speed); - - - LLVMPassManagerRef default_function_pass_manager_without_memcpy = LLVMCreateFunctionPassManagerForModule(m->mod); - LLVMInitializeFunctionPassManager(default_function_pass_manager_without_memcpy); - lb_populate_function_pass_manager(default_function_pass_manager_without_memcpy, true, build_context.optimization_level); - LLVMFinalizeFunctionPassManager(default_function_pass_manager_without_memcpy); - - - for_array(i, m->procedures_to_generate) { - lbProcedure *p = m->procedures_to_generate[i]; - if (p->body != nullptr) { // Build Procedure - if (p->flags & lbProcedureFlag_WithoutMemcpyPass) { - LLVMRunFunctionPassManager(default_function_pass_manager_without_memcpy, p->value); - } else { - if (p->entity && p->entity->kind == Entity_Procedure) { - switch (p->entity->Procedure.optimization_mode) { - case ProcedureOptimizationMode_None: - case ProcedureOptimizationMode_Minimal: - LLVMRunFunctionPassManager(function_pass_manager_minimal, p->value); - break; - case ProcedureOptimizationMode_Size: - LLVMRunFunctionPassManager(function_pass_manager_size, p->value); - break; - case ProcedureOptimizationMode_Speed: - LLVMRunFunctionPassManager(function_pass_manager_speed, p->value); - break; - default: - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); - break; - } - } else { - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); - } - } - } - } - - for_array(i, m->equal_procs.entries) { - lbProcedure *p = m->equal_procs.entries[i].value; - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); - } - for_array(i, m->hasher_procs.entries) { - lbProcedure *p = m->hasher_procs.entries[i].value; - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); - } + lb_llvm_function_pass_worker_proc(m); } TIME_SECTION("LLVM Module Pass"); for_array(i, gen->modules.entries) { - LLVMPassManagerRef module_pass_manager = LLVMCreatePassManager(); - auto target_machine = target_machines[i]; - lb_populate_module_pass_manager(target_machine, module_pass_manager, build_context.optimization_level); lbModule *m = gen->modules.entries[i].value; - LLVMRunPassManager(module_pass_manager, m->mod); + + auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData); + wd->m = m; + wd->target_machine = target_machines[i]; + + lb_llvm_module_pass_worker_proc(wd); } + llvm_error = nullptr; defer (LLVMDisposeMessage(llvm_error)); @@ -14921,15 +14970,7 @@ void lb_generate_code(lbGenerator *gen) { TIME_SECTION("LLVM Object Generation"); - isize thread_count = gb_max(build_context.thread_count, 1); - isize worker_count = thread_count-1; - - LLVMBool do_threading = LLVMIsMultithreaded(); - if (do_threading && USE_SEPARTE_MODULES && MULTITHREAD_OBJECT_GENERATION && worker_count > 0) { - ThreadPool pool = {}; - thread_pool_init(&pool, heap_allocator(), worker_count, "LLVMEmitWork"); - defer (thread_pool_destroy(&pool)); - + if (do_threading) { for_array(j, gen->modules.entries) { lbModule *m = gen->modules.entries[j].value; if (lb_is_module_empty(m)) { @@ -14946,11 +14987,11 @@ void lb_generate_code(lbGenerator *gen) { wd->code_gen_file_type = code_gen_file_type; wd->filepath_obj = filepath_obj; wd->m = m; - thread_pool_add_task(&pool, lb_llvm_emit_worker_proc, wd); + thread_pool_add_task(&lb_thread_pool, lb_llvm_emit_worker_proc, wd); } - thread_pool_start(&pool); - thread_pool_wait_to_process(&pool); + thread_pool_start(&lb_thread_pool); + thread_pool_wait_to_process(&lb_thread_pool); } else { for_array(j, gen->modules.entries) { lbModule *m = gen->modules.entries[j].value; diff --git a/src/thread_pool.cpp b/src/thread_pool.cpp index 2467ba609..73118321b 100644 --- a/src/thread_pool.cpp +++ b/src/thread_pool.cpp @@ -35,8 +35,6 @@ void thread_pool_destroy(ThreadPool *pool); void thread_pool_start(ThreadPool *pool); void thread_pool_join(ThreadPool *pool); void thread_pool_add_task(ThreadPool *pool, WorkerTaskProc *proc, void *data); -void thread_pool_kick(ThreadPool *pool); -void thread_pool_kick_and_wait(ThreadPool *pool); GB_THREAD_PROC(worker_thread_internal); void thread_pool_init(ThreadPool *pool, gbAllocator const &a, isize thread_count, char const *worker_prefix) { @@ -181,4 +179,3 @@ GB_THREAD_PROC(worker_thread_internal) { return 0; } - -- cgit v1.2.3 From 94570a24c1f7efcad439fe49421861758e273a9a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 4 May 2021 00:56:29 +0100 Subject: Move anonymous proc generation into `lbGenerator` --- src/llvm_backend.cpp | 14 +++++++------- src/llvm_backend.hpp | 2 ++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index a84c26c9b..1185bb38b 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -10974,12 +10974,9 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent) { - auto *found = map_get(&m->anonymous_proc_lits, hash_pointer(expr)); - if (found != nullptr) { - lbValue value = {}; - value.value = (*found)->value; - value.type = (*found)->type; - return value; + lbProcedure **found = map_get(&m->gen->anonymous_proc_lits, hash_pointer(expr)); + if (found) { + return lb_find_procedure_value_from_entity(m, (*found)->entity); } ast_node(pl, ProcLit, expr); @@ -10988,7 +10985,7 @@ lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, A // parent$count isize name_len = prefix_name.len + 1 + 8 + 1; char *name_text = gb_alloc_array(permanent_allocator(), char, name_len); - i32 name_id = cast(i32)m->anonymous_proc_lits.entries.count; + i32 name_id = cast(i32)m->gen->anonymous_proc_lits.entries.count; name_len = gb_snprintf(name_text, name_len, "%.*s$anon-%d", LIT(prefix_name), name_id); String name = make_string((u8 *)name_text, name_len-1); @@ -11002,6 +10999,7 @@ lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, A Entity *e = alloc_entity_procedure(nullptr, token, type, pl->tags); e->file = expr->file; e->decl_info = pl->decl; + e->code_gen_module = m; lbProcedure *p = lb_create_procedure(m, e); lbValue value = {}; @@ -11016,6 +11014,7 @@ lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, A } map_set(&m->anonymous_proc_lits, hash_pointer(expr), p); + map_set(&m->gen->anonymous_proc_lits, hash_pointer(expr), p); return value; } @@ -13112,6 +13111,7 @@ bool lb_init_generator(lbGenerator *gen, Checker *c) { map_init(&gen->modules, permanent_allocator(), gen->info->packages.entries.count*2); map_init(&gen->modules_through_ctx, permanent_allocator(), gen->info->packages.entries.count*2); + map_init(&gen->anonymous_proc_lits, heap_allocator(), 1024); gb_mutex_init(&gen->mutex); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 8c4e9b256..f212d9fc0 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -135,6 +135,8 @@ struct lbGenerator { Map modules_through_ctx; // Key: LLVMContextRef * lbModule default_module; + Map anonymous_proc_lits; // Key: Ast * + gbAtomic32 global_array_index; gbAtomic32 global_generated_index; }; -- cgit v1.2.3 From 6fa5eb9e1f195d538212adc24eb5cbdaf98edb01 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 4 May 2021 15:27:41 +0100 Subject: Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 23a969271..a555f1dc8 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,6 @@ In addition, the following platform-specific steps are necessary: - Windows * Have Visual Studio installed (MSVC 2010 or later, for the linker) - * Have a copy of `opt.exe` and `llc.exe` in `Odin/bin`. Pre-built Windows binaries can be found [here](https://github.com/odin-lang/Odin/releases/tag/llvm-windows) and *must* be explicitly copied * Open a valid command prompt: * **Basic:** run the `x64 Native Tools Command Prompt for VS2017` shortcut bundled with VS 2017, or * **Advanced:** run `vcvarsall.bat x64` from a blank `cmd` session @@ -128,7 +127,6 @@ Please read the [Getting Started Guide](https://github.com/odin-lang/Odin/wiki#g - Windows * x86-64/amd64 * MSVC 2010 installed (C++11 support) - * [LLVM binaries](https://github.com/odin-lang/Odin/releases/tag/llvm-windows) for `opt.exe`, `llc.exe`, and `lld-link.exe` * Requires MSVC's link.exe as the linker * run `vcvarsall.bat` to setup the path -- cgit v1.2.3 From afb6ebd21e8296f7f49a49161e38cf2414ffbf3e Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 4 May 2021 17:48:43 +0200 Subject: Fix gray+alpha alpha extract. --- core/image/common.odin | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/image/common.odin b/core/image/common.odin index e92060ed2..9024ec769 100644 --- a/core/image/common.odin +++ b/core/image/common.odin @@ -150,15 +150,15 @@ return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok idx := int(channel); - if idx > img.channels { - return {}, false; - } - if img.channels == 2 && idx == 4 { // Alpha requested, which in a two channel image is index 2: G. idx = 2; } + if idx > img.channels { + return {}, false; + } + switch(img.depth) { case 8: buffer_size := compute_buffer_size(img.width, img.height, 1, 8); -- cgit v1.2.3 From bb9c1d04db025f5497d8e2cc4f3c6346bfd298cc Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 4 May 2021 23:41:35 +0100 Subject: Fix missing newline check --- src/parser.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/parser.cpp b/src/parser.cpp index d048e78e7..d0a2c933c 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2158,6 +2158,8 @@ Ast *parse_operand(AstFile *f, bool lhs) { return type; } + skip_possible_newline_for_literal(f); + if (allow_token(f, Token_Undef)) { if (where_token.kind != Token_Invalid) { syntax_error(where_token, "'where' clauses are not allowed on procedure literals without a defined body (replaced with ---)"); -- cgit v1.2.3 From 08360e2337ee2b4b581b2e52bcef7566e642c489 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 5 May 2021 10:56:19 +0100 Subject: Minor cleanup --- src/llvm_backend.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 1185bb38b..78a75cf5b 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -5745,6 +5745,9 @@ lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) { lbValue lb_find_value_from_entity(lbModule *m, Entity *e) { e = strip_entity_wrapping(e); GB_ASSERT(e != nullptr); + + GB_ASSERT(e->token.string != "_"); + if (e->kind == Entity_Procedure) { return lb_find_procedure_value_from_entity(m, e); } @@ -14101,7 +14104,7 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) } array_init(&results->Tuple.variables, permanent_allocator(), 1); - results->Tuple.variables[0] = alloc_entity_param(nullptr, make_token_ident("_"), t_i32, false, true); + results->Tuple.variables[0] = alloc_entity_param(nullptr, blank_token, t_i32, false, true); Type *proc_type = alloc_type_proc(nullptr, params, params->Tuple.variables.count, @@ -14523,7 +14526,7 @@ void lb_generate_code(lbGenerator *gen) { value.value = g; value.type = alloc_type_pointer(t); - lb_global_type_info_data_entity = alloc_entity_variable(nullptr, blank_token, t, EntityState_Resolved); + lb_global_type_info_data_entity = alloc_entity_variable(nullptr, make_token_ident(LB_TYPE_INFO_DATA_NAME), t, EntityState_Resolved); lb_add_entity(m, lb_global_type_info_data_entity, value); } { // Type info member buffer -- cgit v1.2.3 From 579b317be8d136df7af134b2ad441fe16684fdbb Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 5 May 2021 11:47:47 +0100 Subject: Mark external thread local globals --- src/llvm_backend.cpp | 19 +++++++++++++++++++ src/main.cpp | 3 +++ 2 files changed, 22 insertions(+) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 78a75cf5b..c4d704cc6 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -5773,6 +5773,25 @@ lbValue lb_find_value_from_entity(lbModule *m, Entity *e) { g.type = alloc_type_pointer(e->type); LLVMSetLinkage(g.value, LLVMExternalLinkage); + if (e->Variable.thread_local_model != "") { + LLVMSetThreadLocal(g.value, true); + + String m = e->Variable.thread_local_model; + LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel; + if (m == "default") { + mode = LLVMGeneralDynamicTLSModel; + } else if (m == "localdynamic") { + mode = LLVMLocalDynamicTLSModel; + } else if (m == "initialexec") { + mode = LLVMInitialExecTLSModel; + } else if (m == "localexec") { + mode = LLVMLocalExecTLSModel; + } else { + GB_PANIC("Unhandled thread local mode %.*s", LIT(m)); + } + LLVMSetThreadLocalMode(g.value, mode); + } + lb_add_entity(m, e, g); lb_add_member(m, name, g); return g; diff --git a/src/main.cpp b/src/main.cpp index 756fcc3ac..8f13b28f6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2176,6 +2176,9 @@ int main(int arg_count, char const **arg_ptr) { case BuildMode_DynamicLibrary: i32 result = linker_stage(&gen); if (result != 0) { + if (build_context.show_timings) { + show_timings(&checker, timings); + } return 1; } break; -- cgit v1.2.3 From abdf54800ec282934d7b0730d13a77b46c5c663b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 5 May 2021 15:14:39 +0100 Subject: Improve ternary expression logic within parametric polymorphic parameter assignments --- src/check_expr.cpp | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 117faa18a..158325af1 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2860,6 +2860,36 @@ void update_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) { } case_end; + case_ast_node(te, TernaryExpr, e); + if (old.value.kind != ExactValue_Invalid) { + // See above note in UnaryExpr case + break; + } + + update_expr_type(c, te->x, type, final); + update_expr_type(c, te->y, type, final); + case_end; + + case_ast_node(te, TernaryIfExpr, e); + if (old.value.kind != ExactValue_Invalid) { + // See above note in UnaryExpr case + break; + } + + update_expr_type(c, te->x, type, final); + update_expr_type(c, te->y, type, final); + case_end; + + case_ast_node(te, TernaryWhenExpr, e); + if (old.value.kind != ExactValue_Invalid) { + // See above note in UnaryExpr case + break; + } + + update_expr_type(c, te->x, type, final); + update_expr_type(c, te->y, type, final); + case_end; + case_ast_node(pe, ParenExpr, e); update_expr_type(c, pe->expr, type, final); case_end; @@ -4204,7 +4234,10 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { if (show_error) { check_assignment(c, &o, t, str_lit("argument")); } - err = CallArgumentError_WrongTypes; + // TODO(bill, 2021-05-05): Is this incorrect logic to only fail if there is ambiguity for definite? + if (o.mode == Addressing_Invalid) { + err = CallArgumentError_WrongTypes; + } } } score += s; -- cgit v1.2.3 From 278de3a92f80e1c3f1abc3ece53db6d0bba1e393 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 5 May 2021 15:22:54 +0100 Subject: Unify `AstTernaryExpr` with `AstTernaryIfExpr` Allow for both syntaxes `x if cond else y` and `cond ? x : y` Removes the confusing semantics behind `?:` which could be `if` or `when` depending on the context. --- core/runtime/udivmod128.odin | 2 +- src/check_expr.cpp | 120 ++++++------------------------------------- src/check_type.cpp | 10 ---- src/llvm_backend.cpp | 41 --------------- src/parser.cpp | 15 +----- src/parser.hpp | 1 - 6 files changed, 17 insertions(+), 172 deletions(-) diff --git a/core/runtime/udivmod128.odin b/core/runtime/udivmod128.odin index e4b7380d3..fff856ab6 100644 --- a/core/runtime/udivmod128.odin +++ b/core/runtime/udivmod128.odin @@ -11,7 +11,7 @@ udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 { q, r: [2]u64 = ---, ---; sr: u32 = 0; - low :: ODIN_ENDIAN == "big" ? 1 : 0; + low :: 1 when ODIN_ENDIAN == "big" else 0; high :: 1 - low; U64_BITS :: 8*size_of(u64); U128_BITS :: 8*size_of(u128); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 158325af1..338679755 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2860,16 +2860,6 @@ void update_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) { } case_end; - case_ast_node(te, TernaryExpr, e); - if (old.value.kind != ExactValue_Invalid) { - // See above note in UnaryExpr case - break; - } - - update_expr_type(c, te->x, type, final); - update_expr_type(c, te->y, type, final); - case_end; - case_ast_node(te, TernaryIfExpr, e); if (old.value.kind != ExactValue_Invalid) { // See above note in UnaryExpr case @@ -6104,88 +6094,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type o->type = type; case_end; - case_ast_node(te, TernaryExpr, node); - Operand cond = {Addressing_Invalid}; - check_expr(c, &cond, te->cond); - node->viral_state_flags |= te->cond->viral_state_flags; - - if (cond.mode != Addressing_Invalid && !is_type_boolean(cond.type)) { - error(te->cond, "Non-boolean condition in if expression"); - } - - Operand x = {Addressing_Invalid}; - Operand y = {Addressing_Invalid}; - check_expr_or_type(c, &x, te->x, type_hint); - node->viral_state_flags |= te->x->viral_state_flags; - - if (te->y != nullptr) { - check_expr_or_type(c, &y, te->y, type_hint); - node->viral_state_flags |= te->y->viral_state_flags; - } else { - error(node, "A ternary expression must have an else clause"); - return kind; - } - - if (x.type == nullptr || x.type == t_invalid || - y.type == nullptr || y.type == t_invalid) { - return kind; - } - - if (x.mode == Addressing_Type && y.mode == Addressing_Type && - cond.mode == Addressing_Constant && is_type_boolean(cond.type)) { - o->mode = Addressing_Type; - if (cond.value.value_bool) { - o->type = x.type; - o->expr = x.expr; - } else { - o->type = y.type; - o->expr = y.expr; - } - return Expr_Expr; - } - - convert_to_typed(c, &x, y.type); - if (x.mode == Addressing_Invalid) { - return kind; - } - convert_to_typed(c, &y, x.type); - if (y.mode == Addressing_Invalid) { - x.mode = Addressing_Invalid; - return kind; - } - - if (!ternary_compare_types(x.type, y.type)) { - gbString its = type_to_string(x.type); - gbString ets = type_to_string(y.type); - error(node, "Mismatched types in ternary expression, %s vs %s", its, ets); - gb_string_free(ets); - gb_string_free(its); - return kind; - } - - Type *type = x.type; - if (is_type_untyped_nil(type) || is_type_untyped_undef(type)) { - type = y.type; - } - - o->type = type; - o->mode = Addressing_Value; - - if (cond.mode == Addressing_Constant && is_type_boolean(cond.type) && - x.mode == Addressing_Constant && - y.mode == Addressing_Constant) { - - o->mode = Addressing_Constant; - - if (cond.value.value_bool) { - o->value = x.value; - } else { - o->value = y.value; - } - } - - case_end; - case_ast_node(te, TernaryIfExpr, node); Operand cond = {Addressing_Invalid}; check_expr(c, &cond, te->cond); @@ -8265,20 +8173,22 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) { str = write_expr_to_string(str, be->right, shorthand); case_end; - case_ast_node(te, TernaryExpr, node); - str = write_expr_to_string(str, te->cond, shorthand); - str = gb_string_appendc(str, " ? "); - str = write_expr_to_string(str, te->x, shorthand); - str = gb_string_appendc(str, " : "); - str = write_expr_to_string(str, te->y, shorthand); - case_end; - case_ast_node(te, TernaryIfExpr, node); - str = write_expr_to_string(str, te->x, shorthand); - str = gb_string_appendc(str, " if "); - str = write_expr_to_string(str, te->cond, shorthand); - str = gb_string_appendc(str, " else "); - str = write_expr_to_string(str, te->y, shorthand); + TokenPos x = ast_token(te->x).pos; + TokenPos cond = ast_token(te->cond).pos; + if (x < cond) { + str = write_expr_to_string(str, te->x, shorthand); + str = gb_string_appendc(str, " if "); + str = write_expr_to_string(str, te->cond, shorthand); + str = gb_string_appendc(str, " else "); + str = write_expr_to_string(str, te->y, shorthand); + } else { + str = write_expr_to_string(str, te->cond, shorthand); + str = gb_string_appendc(str, " ? "); + str = write_expr_to_string(str, te->x, shorthand); + str = gb_string_appendc(str, " : "); + str = write_expr_to_string(str, te->y, shorthand); + } case_end; case_ast_node(te, TernaryWhenExpr, node); diff --git a/src/check_type.cpp b/src/check_type.cpp index f70230682..b90732e00 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2889,16 +2889,6 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t } case_end; - case_ast_node(te, TernaryExpr, e); - Operand o = {}; - check_expr_or_type(ctx, &o, e); - if (o.mode == Addressing_Type) { - *type = o.type; - set_base_type(named_type, *type); - return true; - } - case_end; - case_ast_node(te, TernaryIfExpr, e); Operand o = {}; check_expr_or_type(ctx, &o, e); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index c4d704cc6..baf768880 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -11351,47 +11351,6 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { return lb_build_expr(p, se->call); case_end; - case_ast_node(te, TernaryExpr, expr); - LLVMValueRef incoming_values[2] = {}; - LLVMBasicBlockRef incoming_blocks[2] = {}; - - GB_ASSERT(te->y != nullptr); - lbBlock *then = lb_create_block(p, "if.then"); - lbBlock *done = lb_create_block(p, "if.done"); // NOTE(bill): Append later - lbBlock *else_ = lb_create_block(p, "if.else"); - - lbValue cond = lb_build_cond(p, te->cond, then, else_); - lb_start_block(p, then); - - Type *type = default_type(type_of_expr(expr)); - - lb_open_scope(p, nullptr); - incoming_values[0] = lb_emit_conv(p, lb_build_expr(p, te->x), type).value; - lb_close_scope(p, lbDeferExit_Default, nullptr); - - lb_emit_jump(p, done); - lb_start_block(p, else_); - - lb_open_scope(p, nullptr); - incoming_values[1] = lb_emit_conv(p, lb_build_expr(p, te->y), type).value; - lb_close_scope(p, lbDeferExit_Default, nullptr); - - lb_emit_jump(p, done); - lb_start_block(p, done); - - lbValue res = {}; - res.value = LLVMBuildPhi(p->builder, lb_type(p->module, type), ""); - res.type = type; - - GB_ASSERT(p->curr_block->preds.count >= 2); - incoming_blocks[0] = p->curr_block->preds[0]->block; - incoming_blocks[1] = p->curr_block->preds[1]->block; - - LLVMAddIncoming(res.value, incoming_values, incoming_blocks, 2); - - return res; - case_end; - case_ast_node(te, TernaryIfExpr, expr); LLVMValueRef incoming_values[2] = {}; LLVMBasicBlockRef incoming_blocks[2] = {}; diff --git a/src/parser.cpp b/src/parser.cpp index d0a2c933c..376ac58dc 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -39,7 +39,6 @@ Token ast_token(Ast *node) { case Ast_Ellipsis: return node->Ellipsis.token; case Ast_FieldValue: return node->FieldValue.eq; case Ast_DerefExpr: return node->DerefExpr.op; - case Ast_TernaryExpr: return ast_token(node->TernaryExpr.cond); case Ast_TernaryIfExpr: return ast_token(node->TernaryIfExpr.x); case Ast_TernaryWhenExpr: return ast_token(node->TernaryWhenExpr.x); case Ast_TypeAssertion: return ast_token(node->TypeAssertion.expr); @@ -241,11 +240,6 @@ Ast *clone_ast(Ast *node) { n->FieldValue.value = clone_ast(n->FieldValue.value); break; - case Ast_TernaryExpr: - n->TernaryExpr.cond = clone_ast(n->TernaryExpr.cond); - n->TernaryExpr.x = clone_ast(n->TernaryExpr.x); - n->TernaryExpr.y = clone_ast(n->TernaryExpr.y); - break; case Ast_TernaryIfExpr: n->TernaryIfExpr.x = clone_ast(n->TernaryIfExpr.x); n->TernaryIfExpr.cond = clone_ast(n->TernaryIfExpr.cond); @@ -698,13 +692,6 @@ Ast *ast_compound_lit(AstFile *f, Ast *type, Array const &elems, Token op } -Ast *ast_ternary_expr(AstFile *f, Ast *cond, Ast *x, Ast *y) { - Ast *result = alloc_ast_node(f, Ast_TernaryExpr); - result->TernaryExpr.cond = cond; - result->TernaryExpr.x = x; - result->TernaryExpr.y = y; - return result; -} Ast *ast_ternary_if_expr(AstFile *f, Ast *x, Ast *cond, Ast *y) { Ast *result = alloc_ast_node(f, Ast_TernaryIfExpr); result->TernaryIfExpr.x = x; @@ -2871,7 +2858,7 @@ Ast *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) { Ast *x = parse_expr(f, lhs); Token token_c = expect_token(f, Token_Colon); Ast *y = parse_expr(f, lhs); - expr = ast_ternary_expr(f, cond, x, y); + expr = ast_ternary_if_expr(f, x, cond, y); } else if (op.kind == Token_if) { Ast *x = expr; // Token_if diff --git a/src/parser.hpp b/src/parser.hpp index 93c0363ab..2a0b0fa27 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -343,7 +343,6 @@ AST_KIND(_ExprBegin, "", bool) \ i32 builtin_id; \ }) \ AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \ - AST_KIND(TernaryExpr, "ternary expression", struct { Ast *cond, *x, *y; }) \ AST_KIND(TernaryIfExpr, "ternary if expression", struct { Ast *x, *cond, *y; }) \ AST_KIND(TernaryWhenExpr, "ternary when expression", struct { Ast *x, *cond, *y; }) \ AST_KIND(TypeAssertion, "type assertion", struct { Ast *expr; Token dot; Ast *type; Type *type_hint; }) \ -- cgit v1.2.3 From 0f2a9e6143e584d79af72858dceffa1b9e4b11d4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 5 May 2021 23:00:12 +0100 Subject: Fix LLVM store point for nested types containing procedures (e.g. `^^proc()`) --- src/llvm_backend.cpp | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index baf768880..c0d0a66ad 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -518,6 +518,38 @@ void lb_const_store(lbValue ptr, lbValue value) { } +bool lb_is_type_proc_recursive(Type *t) { + for (;;) { + if (t == nullptr) { + return false; + } + switch (t->kind) { + case Type_Named: + t = t->Named.base; + break; + case Type_Pointer: + t = t->Pointer.elem; + break; + case Type_Array: + t = t->Array.elem; + break; + case Type_EnumeratedArray: + t = t->EnumeratedArray.elem; + break; + case Type_Slice: + t = t->Slice.elem; + break; + case Type_DynamicArray: + t = t->DynamicArray.elem; + break; + case Type_Proc: + return true; + default: + return false; + } + } +} + void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) { GB_ASSERT(value.value != nullptr); Type *a = type_deref(ptr.type); @@ -530,7 +562,7 @@ void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) { GB_ASSERT_MSG(are_types_identical(ca, core_type(value.type)), "%s != %s", type_to_string(a), type_to_string(value.type)); } - if (is_type_proc(a)) { + if (lb_is_type_proc_recursive(a)) { // NOTE(bill, 2020-11-11): Because of certain LLVM rules, a procedure value may be // stored as regular pointer with no procedure information -- cgit v1.2.3 From 60685369b97a6053b98e2689ce4e7910347a2797 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 5 May 2021 23:34:39 +0100 Subject: Fix typo --- src/llvm_backend.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index c0d0a66ad..cb273dc00 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1888,7 +1888,10 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { case Type_Array: return LLVMDIBuilderCreateArrayType(m->debug_builder, - type->Array.count, 8*cast(unsigned)type_align_of(type), lb_debug_type(m, type->Array.elem), nullptr, 0); + type->Array.count, + 8*cast(unsigned)type_align_of(type), + lb_debug_type(m, type->Array.elem), + nullptr, 0); case Type_EnumeratedArray: { LLVMMetadataRef array_type = LLVMDIBuilderCreateArrayType(m->debug_builder, @@ -2250,7 +2253,7 @@ void lb_debug_complete_types(lbModule *m) { 8*type_size_of(bt)-word_bits + 1*word_bits, LLVMDIFlagZero, lb_debug_type(m, t_int) ); - elements[3] = LLVMDIBuilderCreateMemberType( + elements[2] = LLVMDIBuilderCreateMemberType( m->debug_builder, record_scope, ".allocator", 12, file, 0, @@ -3527,6 +3530,9 @@ lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e, bool zero_init, i32 p if (e != nullptr) { lb_add_entity(p->module, e, val); + if (e->token.string == "v123") { + gb_printf_err("%.*s\n", LIT(e->token.string)); + } lb_add_debug_local_variable(p, ptr, type, e->token); } -- cgit v1.2.3 From b6a1ec022923239c27f911ce1be107a9e201ad22 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 5 May 2021 23:34:53 +0100 Subject: Remove debug code --- src/llvm_backend.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index cb273dc00..2065f7d70 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -3530,9 +3530,6 @@ lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e, bool zero_init, i32 p if (e != nullptr) { lb_add_entity(p->module, e, val); - if (e->token.string == "v123") { - gb_printf_err("%.*s\n", LIT(e->token.string)); - } lb_add_debug_local_variable(p, ptr, type, e->token); } -- cgit v1.2.3 From 4f51d74fc29a3cfa09f00dd598d3751901261902 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 6 May 2021 11:25:41 +0200 Subject: Fix typo in core:mem alloc() comment. --- core/mem/alloc.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/mem/alloc.odin b/core/mem/alloc.odin index 0df68255f..0da7a9708 100644 --- a/core/mem/alloc.odin +++ b/core/mem/alloc.odin @@ -22,7 +22,7 @@ Allocator_Mode_Set :: distinct bit_set[Allocator_Mode]; Allocator_Query_Info :: runtime.Allocator_Query_Info; /* Allocator_Query_Info :: struct { - pointer: Maybe(rawptr), + pointer: rawptr, size: Maybe(int), alignment: Maybe(int), } -- cgit v1.2.3 From 03862d1f48a62d1ab9445050a6f3a3b8d0323f88 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 6 May 2021 13:23:17 +0200 Subject: Mark mem.slice_ptr_to_bytes as deprecated. Use byte_slice instead. We can't make it an alias *and* mark it as deprecated, regrettably: ```odin byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte { return transmute([]u8)Raw_Slice{data=data, len=max(len, 0)}; } @(deprecated="use byte_slice") slice_ptr_to_bytes :: byte_slice; "mem.odin(145:1) Constant alias declarations cannot have attributes" ``` --- core/mem/mem.odin | 1 + 1 file changed, 1 insertion(+) diff --git a/core/mem/mem.odin b/core/mem/mem.odin index ddf9e9637..ecf232557 100644 --- a/core/mem/mem.odin +++ b/core/mem/mem.odin @@ -142,6 +142,7 @@ slice_ptr :: proc(ptr: ^$T, len: int) -> []T { byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte { return transmute([]u8)Raw_Slice{data=data, len=max(len, 0)}; } +@(deprecated="use byte_slice") slice_ptr_to_bytes :: proc(data: rawptr, len: int) -> []byte { return transmute([]u8)Raw_Slice{data=data, len=max(len, 0)}; } -- cgit v1.2.3 From da7a9a3584de18531f1e516f19a90b3b27ed91bc Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 6 May 2021 13:59:05 +0100 Subject: Improve type inference rules for implicit selector expressions New improvements: `(.A == x)` `a_union_containing_many_enums = .A;` --- src/check_expr.cpp | 178 +++++++++++++++++++++++++++++++-------------------- src/llvm_backend.cpp | 50 +++++++++------ 2 files changed, 139 insertions(+), 89 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 338679755..d0e0959d0 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2504,6 +2504,10 @@ bool check_binary_array_expr(CheckerContext *c, Token op, Operand *x, Operand *y return false; } +bool is_ise_expr(Ast *node) { + node = unparen_expr(node); + return node->kind == Ast_ImplicitSelectorExpr; +} void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint, bool use_lhs_as_type_hint=false) { GB_ASSERT(node->kind == Ast_BinaryExpr); @@ -2521,8 +2525,14 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint case Token_CmpEq: case Token_NotEq: { // NOTE(bill): Allow comparisons between types - check_expr_or_type(c, x, be->left, type_hint); - check_expr_or_type(c, y, be->right, x->type); + if (is_ise_expr(be->left)) { + // Evalute the right before the left for an '.X' expression + check_expr_or_type(c, y, be->right, type_hint); + check_expr_or_type(c, x, be->left, y->type); + } else { + check_expr_or_type(c, x, be->left, type_hint); + check_expr_or_type(c, y, be->right, x->type); + } bool xt = x->mode == Addressing_Type; bool yt = y->mode == Addressing_Type; // If only one is a type, this is an error @@ -2629,11 +2639,22 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint return; default: - check_expr_with_type_hint(c, x, be->left, type_hint); - if (use_lhs_as_type_hint) { - check_expr_with_type_hint(c, y, be->right, x->type); + if (is_ise_expr(be->left)) { + // Evalute the right before the left for an '.X' expression + check_expr_or_type(c, y, be->right, type_hint); + + if (use_lhs_as_type_hint) { // RHS in this case + check_expr_or_type(c, x, be->left, y->type); + } else { + check_expr_with_type_hint(c, x, be->left, type_hint); + } } else { - check_expr_with_type_hint(c, y, be->right, type_hint); + check_expr_with_type_hint(c, x, be->left, type_hint); + if (use_lhs_as_type_hint) { + check_expr_with_type_hint(c, y, be->right, x->type); + } else { + check_expr_with_type_hint(c, y, be->right, type_hint); + } } break; } @@ -5929,6 +5950,88 @@ bool check_is_operand_compound_lit_constant(CheckerContext *c, Operand *o) { } +bool attempt_implicit_selector_expr(CheckerContext *c, Operand *o, AstImplicitSelectorExpr *ise, Type *th) { + if (is_type_enum(th)) { + Type *enum_type = base_type(th); + GB_ASSERT(enum_type->kind == Type_Enum); + + String name = ise->selector->Ident.token.string; + + Entity *e = scope_lookup_current(enum_type->Enum.scope, name); + if (e == nullptr) { + return false; + } + GB_ASSERT(are_types_identical(base_type(e->type), enum_type)); + GB_ASSERT(e->kind == Entity_Constant); + o->value = e->Constant.value; + o->mode = Addressing_Constant; + o->type = e->type; + return true; + } + bool show_error = true; + if (is_type_union(th)) { + Type *union_type = base_type(th); + isize enum_count = 0; + Type *et = nullptr; + + auto operands = array_make(temporary_allocator(), 0, union_type->Union.variants.count); + + for_array(i, union_type->Union.variants) { + Type *vt = union_type->Union.variants[i]; + + Operand x = {}; + if (attempt_implicit_selector_expr(c, &x, ise, vt)) { + array_add(&operands, x); + } + } + + if (operands.count == 1) { + *o = operands[0]; + return true; + } + } + return false; +} + +ExprKind check_implicit_selector_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { + ast_node(ise, ImplicitSelectorExpr, node); + + o->type = t_invalid; + o->expr = node; + o->mode = Addressing_Invalid; + + Type *th = type_hint; + + if (th == nullptr) { + gbString str = expr_to_string(node); + error(node, "Cannot determine type for implicit selector expression '%s'", str); + gb_string_free(str); + return Expr_Expr; + } + o->type = th; + Type *enum_type = th; + + bool ok = attempt_implicit_selector_expr(c, o, ise, th); + if (!ok) { + String name = ise->selector->Ident.token.string; + + if (is_type_enum(th)) { + gbString typ = type_to_string(th); + error(node, "Undeclared name %.*s for type '%s'", LIT(name), typ); + gb_string_free(typ); + } else { + gbString typ = type_to_string(th); + gbString str = expr_to_string(node); + error(node, "Invalid type '%s' for implicit selector expression '%s'", typ, str); + gb_string_free(str); + gb_string_free(typ); + } + } + + o->expr = node; + return Expr_Expr; +} + ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { u32 prev_state_flags = c->state_flags; defer (c->state_flags = prev_state_flags); @@ -7395,68 +7498,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_ast_node(ise, ImplicitSelectorExpr, node); - o->type = t_invalid; - o->expr = node; - o->mode = Addressing_Invalid; - - Type *th = type_hint; - - if (th == nullptr) { - gbString str = expr_to_string(node); - error(node, "Cannot determine type for implicit selector expression '%s'", str); - gb_string_free(str); - return Expr_Expr; - } - o->type = th; - Type *enum_type = th; - - if (!is_type_enum(th)) { - bool show_error = true; - if (is_type_union(th)) { - Type *union_type = base_type(th); - isize enum_count = 0; - Type *et = nullptr; - for_array(i, union_type->Union.variants) { - Type *vt = union_type->Union.variants[i]; - if (is_type_enum(vt)) { - enum_count += 1; - et = vt; - } - } - if (enum_count == 1) { - show_error = false; - enum_type = et; - } - } - - if (show_error) { - gbString typ = type_to_string(th); - gbString str = expr_to_string(node); - error(node, "Invalid type '%s' for implicit selector expression '%s'", typ, str); - gb_string_free(str); - gb_string_free(typ); - return Expr_Expr; - } - } - GB_ASSERT(ise->selector->kind == Ast_Ident); - String name = ise->selector->Ident.token.string; - - enum_type = base_type(enum_type); - GB_ASSERT(enum_type->kind == Type_Enum); - Entity *e = scope_lookup_current(enum_type->Enum.scope, name); - if (e == nullptr) { - gbString typ = type_to_string(th); - error(node, "Undeclared name %.*s for type '%s'", LIT(name), typ); - gb_string_free(typ); - return Expr_Expr; - } - GB_ASSERT(are_types_identical(base_type(e->type), enum_type)); - GB_ASSERT(e->kind == Entity_Constant); - o->value = e->Constant.value; - o->mode = Addressing_Constant; - o->type = e->type; - - return Expr_Expr; + return check_implicit_selector_expr(c, o, node, type_hint); case_end; case_ast_node(ie, IndexExpr, node); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 2065f7d70..9a6595b24 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -10667,6 +10667,30 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) { return {p->value, p->type}; } +lbValue lb_compare_records(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right, Type *type) { + GB_ASSERT((is_type_struct(type) || is_type_union(type)) && is_type_comparable(type)); + lbValue left_ptr = lb_address_from_load_or_generate_local(p, left); + lbValue right_ptr = lb_address_from_load_or_generate_local(p, right); + lbValue res = {}; + if (is_type_simple_compare(type)) { + // TODO(bill): Test to see if this is actually faster!!!! + auto args = array_make(permanent_allocator(), 3); + args[0] = lb_emit_conv(p, left_ptr, t_rawptr); + args[1] = lb_emit_conv(p, right_ptr, t_rawptr); + args[2] = lb_const_int(p->module, t_int, type_size_of(type)); + res = lb_emit_runtime_call(p, "memory_equal", args); + } else { + lbValue value = lb_get_equal_proc_for_type(p->module, type); + auto args = array_make(permanent_allocator(), 2); + args[0] = lb_emit_conv(p, left_ptr, t_rawptr); + args[1] = lb_emit_conv(p, right_ptr, t_rawptr); + res = lb_emit_call(p, value, args); + } + if (op_kind == Token_NotEq) { + res = lb_emit_unary_arith(p, Token_Not, res, res.type); + } + return res; +} lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right) { @@ -10797,27 +10821,11 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri if ((is_type_struct(a) || is_type_union(a)) && is_type_comparable(a)) { - lbValue left_ptr = lb_address_from_load_or_generate_local(p, left); - lbValue right_ptr = lb_address_from_load_or_generate_local(p, right); - lbValue res = {}; - if (is_type_simple_compare(a)) { - // TODO(bill): Test to see if this is actually faster!!!! - auto args = array_make(permanent_allocator(), 3); - args[0] = lb_emit_conv(p, left_ptr, t_rawptr); - args[1] = lb_emit_conv(p, right_ptr, t_rawptr); - args[2] = lb_const_int(p->module, t_int, type_size_of(a)); - res = lb_emit_runtime_call(p, "memory_equal", args); - } else { - lbValue value = lb_get_equal_proc_for_type(p->module, a); - auto args = array_make(permanent_allocator(), 2); - args[0] = lb_emit_conv(p, left_ptr, t_rawptr); - args[1] = lb_emit_conv(p, right_ptr, t_rawptr); - res = lb_emit_call(p, value, args); - } - if (op_kind == Token_NotEq) { - res = lb_emit_unary_arith(p, Token_Not, res, res.type); - } - return res; + return lb_compare_records(p, op_kind, left, right, a); + } + + if ((is_type_struct(b) || is_type_union(b)) && is_type_comparable(b)) { + return lb_compare_records(p, op_kind, left, right, b); } if (is_type_string(a)) { -- cgit v1.2.3 From 502ad0c10baa98f0dfeb55795b9555e15d4e4d13 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 6 May 2021 14:00:01 +0100 Subject: `sync2.Auto_Reset_Event`; Make atomic operations names clearer --- core/sync/sync2/atomic.odin | 84 ++++++++++++++++---------------- core/sync/sync2/extended.odin | 54 +++++++++++++++----- core/sync/sync2/primitives.odin | 49 +++++++++++++++++++ core/sync/sync2/primitives_pthreads.odin | 4 +- core/sync/sync2/primitives_windows.odin | 8 +-- 5 files changed, 138 insertions(+), 61 deletions(-) diff --git a/core/sync/sync2/atomic.odin b/core/sync/sync2/atomic.odin index 1f8e2f3a8..fa86ec352 100644 --- a/core/sync/sync2/atomic.odin +++ b/core/sync/sync2/atomic.odin @@ -2,78 +2,76 @@ package sync2 import "intrinsics" -// TODO(bill): Is this even a good design? The intrinsics seem to be more than good enough and just as clean - cpu_relax :: intrinsics.cpu_relax; -atomic_fence :: intrinsics.atomic_fence; -atomic_fence_acq :: intrinsics.atomic_fence_acq; -atomic_fence_rel :: intrinsics.atomic_fence_rel; -atomic_fence_acqrel :: intrinsics.atomic_fence_acqrel; +atomic_fence :: intrinsics.atomic_fence; +atomic_fence_acquire :: intrinsics.atomic_fence_acq; +atomic_fence_release :: intrinsics.atomic_fence_rel; +atomic_fence_acqrel :: intrinsics.atomic_fence_acqrel; atomic_store :: intrinsics.atomic_store; -atomic_store_rel :: intrinsics.atomic_store_rel; +atomic_store_release :: intrinsics.atomic_store_rel; atomic_store_relaxed :: intrinsics.atomic_store_relaxed; atomic_store_unordered :: intrinsics.atomic_store_unordered; atomic_load :: intrinsics.atomic_load; -atomic_load_acq :: intrinsics.atomic_load_acq; +atomic_load_acquire :: intrinsics.atomic_load_acq; atomic_load_relaxed :: intrinsics.atomic_load_relaxed; atomic_load_unordered :: intrinsics.atomic_load_unordered; atomic_add :: intrinsics.atomic_add; -atomic_add_acq :: intrinsics.atomic_add_acq; -atomic_add_rel :: intrinsics.atomic_add_rel; +atomic_add_acquire :: intrinsics.atomic_add_acq; +atomic_add_release :: intrinsics.atomic_add_rel; atomic_add_acqrel :: intrinsics.atomic_add_acqrel; atomic_add_relaxed :: intrinsics.atomic_add_relaxed; atomic_sub :: intrinsics.atomic_sub; -atomic_sub_acq :: intrinsics.atomic_sub_acq; -atomic_sub_rel :: intrinsics.atomic_sub_rel; +atomic_sub_acquire :: intrinsics.atomic_sub_acq; +atomic_sub_release :: intrinsics.atomic_sub_rel; atomic_sub_acqrel :: intrinsics.atomic_sub_acqrel; atomic_sub_relaxed :: intrinsics.atomic_sub_relaxed; atomic_and :: intrinsics.atomic_and; -atomic_and_acq :: intrinsics.atomic_and_acq; -atomic_and_rel :: intrinsics.atomic_and_rel; +atomic_and_acquire :: intrinsics.atomic_and_acq; +atomic_and_release :: intrinsics.atomic_and_rel; atomic_and_acqrel :: intrinsics.atomic_and_acqrel; atomic_and_relaxed :: intrinsics.atomic_and_relaxed; atomic_nand :: intrinsics.atomic_nand; -atomic_nand_acq :: intrinsics.atomic_nand_acq; -atomic_nand_rel :: intrinsics.atomic_nand_rel; +atomic_nand_acquire :: intrinsics.atomic_nand_acq; +atomic_nand_release :: intrinsics.atomic_nand_rel; atomic_nand_acqrel :: intrinsics.atomic_nand_acqrel; atomic_nand_relaxed :: intrinsics.atomic_nand_relaxed; atomic_or :: intrinsics.atomic_or; -atomic_or_acq :: intrinsics.atomic_or_acq; -atomic_or_rel :: intrinsics.atomic_or_rel; +atomic_or_acquire :: intrinsics.atomic_or_acq; +atomic_or_release :: intrinsics.atomic_or_rel; atomic_or_acqrel :: intrinsics.atomic_or_acqrel; atomic_or_relaxed :: intrinsics.atomic_or_relaxed; atomic_xor :: intrinsics.atomic_xor; -atomic_xor_acq :: intrinsics.atomic_xor_acq; -atomic_xor_rel :: intrinsics.atomic_xor_rel; +atomic_xor_acquire :: intrinsics.atomic_xor_acq; +atomic_xor_release :: intrinsics.atomic_xor_rel; atomic_xor_acqrel :: intrinsics.atomic_xor_acqrel; atomic_xor_relaxed :: intrinsics.atomic_xor_relaxed; -atomic_xchg :: intrinsics.atomic_xchg; -atomic_xchg_acq :: intrinsics.atomic_xchg_acq; -atomic_xchg_rel :: intrinsics.atomic_xchg_rel; -atomic_xchg_acqrel :: intrinsics.atomic_xchg_acqrel; -atomic_xchg_relaxed :: intrinsics.atomic_xchg_relaxed; +atomic_exchange :: intrinsics.atomic_xchg; +atomic_exchange_acquire :: intrinsics.atomic_xchg_acq; +atomic_exchange_release :: intrinsics.atomic_xchg_rel; +atomic_exchange_acqrel :: intrinsics.atomic_xchg_acqrel; +atomic_exchange_relaxed :: intrinsics.atomic_xchg_relaxed; -atomic_cxchg :: intrinsics.atomic_cxchg; -atomic_cxchg_acq :: intrinsics.atomic_cxchg_acq; -atomic_cxchg_rel :: intrinsics.atomic_cxchg_rel; -atomic_cxchg_acqrel :: intrinsics.atomic_cxchg_acqrel; -atomic_cxchg_relaxed :: intrinsics.atomic_cxchg_relaxed; -atomic_cxchg_failrelaxed :: intrinsics.atomic_cxchg_failrelaxed; -atomic_cxchg_failacq :: intrinsics.atomic_cxchg_failacq; -atomic_cxchg_acq_failrelaxed :: intrinsics.atomic_cxchg_acq_failrelaxed; -atomic_cxchg_acqrel_failrelaxed :: intrinsics.atomic_cxchg_acqrel_failrelaxed; +atomic_compare_exchange_strong :: intrinsics.atomic_cxchg; +atomic_compare_exchange_strong_acquire :: intrinsics.atomic_cxchg_acq; +atomic_compare_exchange_strong_release :: intrinsics.atomic_cxchg_rel; +atomic_compare_exchange_strong_acqrel :: intrinsics.atomic_cxchg_acqrel; +atomic_compare_exchange_strong_relaxed :: intrinsics.atomic_cxchg_relaxed; +atomic_compare_exchange_strong_failrelaxed :: intrinsics.atomic_cxchg_failrelaxed; +atomic_compare_exchange_strong_failacquire :: intrinsics.atomic_cxchg_failacq; +atomic_compare_exchange_strong_acquire_failrelaxed :: intrinsics.atomic_cxchg_acq_failrelaxed; +atomic_compare_exchange_strong_acqrel_failrelaxed :: intrinsics.atomic_cxchg_acqrel_failrelaxed; -atomic_cxchgweak :: intrinsics.atomic_cxchgweak; -atomic_cxchgweak_acq :: intrinsics.atomic_cxchgweak_acq; -atomic_cxchgweak_rel :: intrinsics.atomic_cxchgweak_rel; -atomic_cxchgweak_acqrel :: intrinsics.atomic_cxchgweak_acqrel; -atomic_cxchgweak_relaxed :: intrinsics.atomic_cxchgweak_relaxed; -atomic_cxchgweak_failrelaxed :: intrinsics.atomic_cxchgweak_failrelaxed; -atomic_cxchgweak_failacq :: intrinsics.atomic_cxchgweak_failacq; -atomic_cxchgweak_acq_failrelaxed :: intrinsics.atomic_cxchgweak_acq_failrelaxed; -atomic_cxchgweak_acqrel_failrelaxed :: intrinsics.atomic_cxchgweak_acqrel_failrelaxed; +atomic_compare_exchange_weak :: intrinsics.atomic_cxchgweak; +atomic_compare_exchange_weak_acquire :: intrinsics.atomic_cxchgweak_acq; +atomic_compare_exchange_weak_release :: intrinsics.atomic_cxchgweak_rel; +atomic_compare_exchange_weak_acqrel :: intrinsics.atomic_cxchgweak_acqrel; +atomic_compare_exchange_weak_relaxed :: intrinsics.atomic_cxchgweak_relaxed; +atomic_compare_exchange_weak_failrelaxed :: intrinsics.atomic_cxchgweak_failrelaxed; +atomic_compare_exchange_weak_failacquire :: intrinsics.atomic_cxchgweak_failacq; +atomic_compare_exchange_weak_acquire_failrelaxed :: intrinsics.atomic_cxchgweak_acq_failrelaxed; +atomic_compare_exchange_weak_acqrel_failrelaxed :: intrinsics.atomic_cxchgweak_acqrel_failrelaxed; diff --git a/core/sync/sync2/extended.odin b/core/sync/sync2/extended.odin index 3f44a172a..06051c822 100644 --- a/core/sync/sync2/extended.odin +++ b/core/sync/sync2/extended.odin @@ -122,6 +122,36 @@ barrier_wait :: proc(b: ^Barrier) -> (is_leader: bool) { } +Auto_Reset_Event :: struct { + // status == 0: Event is reset and no threads are waiting + // status == 1: Event is signaled + // status == -N: Event is reset and N threads are waiting + status: i32, + sema: Sema, +} + +auto_reset_event_signal :: proc(e: ^Auto_Reset_Event) { + old_status := atomic_load_relaxed(&e.status); + for { + new_status := old_status + 1 if old_status < 1 else 1; + if _, ok := atomic_compare_exchange_weak_release(&e.status, old_status, new_status); ok { + break; + } + + if old_status < 0 { + sema_post(&e.sema); + } + } +} + +auto_reset_event_wait :: proc(e: ^Auto_Reset_Event) { + old_status := atomic_sub_acquire(&e.status, 1); + if old_status < 1 { + sema_wait(&e.sema); + } +} + + Ticket_Mutex :: struct { ticket: uint, @@ -130,7 +160,7 @@ Ticket_Mutex :: struct { ticket_mutex_lock :: #force_inline proc(m: ^Ticket_Mutex) { ticket := atomic_add_relaxed(&m.ticket, 1); - for ticket != atomic_load_acq(&m.serving) { + for ticket != atomic_load_acquire(&m.serving) { cpu_relax(); } } @@ -142,23 +172,23 @@ ticket_mutex_unlock :: #force_inline proc(m: ^Ticket_Mutex) { Benaphore :: struct { - counter: int, + counter: i32, sema: Sema, } benaphore_lock :: proc(b: ^Benaphore) { - if atomic_add_acq(&b.counter, 1) > 1 { + if atomic_add_acquire(&b.counter, 1) > 1 { sema_wait(&b.sema); } } benaphore_try_lock :: proc(b: ^Benaphore) -> bool { - v, _ := atomic_cxchg_acq(&b.counter, 1, 0); + v, _ := atomic_compare_exchange_strong_acquire(&b.counter, 1, 0); return v == 0; } benaphore_unlock :: proc(b: ^Benaphore) { - if atomic_sub_rel(&b.counter, 1) > 0 { + if atomic_sub_release(&b.counter, 1) > 0 { sema_post(&b.sema); } } @@ -166,13 +196,13 @@ benaphore_unlock :: proc(b: ^Benaphore) { Recursive_Benaphore :: struct { counter: int, owner: int, - recursion: int, + recursion: i32, sema: Sema, } recursive_benaphore_lock :: proc(b: ^Recursive_Benaphore) { tid := runtime.current_thread_id(); - if atomic_add_acq(&b.counter, 1) > 1 { + if atomic_add_acquire(&b.counter, 1) > 1 { if tid != b.owner { sema_wait(&b.sema); } @@ -185,10 +215,10 @@ recursive_benaphore_lock :: proc(b: ^Recursive_Benaphore) { recursive_benaphore_try_lock :: proc(b: ^Recursive_Benaphore) -> bool { tid := runtime.current_thread_id(); if b.owner == tid { - atomic_add_acq(&b.counter, 1); + atomic_add_acquire(&b.counter, 1); } - if v, _ := atomic_cxchg_acq(&b.counter, 1, 0); v != 0 { + if v, _ := atomic_compare_exchange_strong_acquire(&b.counter, 1, 0); v != 0 { return false; } // inside the lock @@ -205,7 +235,7 @@ recursive_benaphore_unlock :: proc(b: ^Recursive_Benaphore) { if recursion == 0 { b.owner = 0; } - if atomic_sub_rel(&b.counter, 1) > 0 { + if atomic_sub_release(&b.counter, 1) > 0 { if recursion == 0 { sema_post(&b.sema); } @@ -223,7 +253,7 @@ Once :: struct { } once_do :: proc(o: ^Once, fn: proc()) { - if atomic_load_acq(&o.done) == false { + if atomic_load_acquire(&o.done) == false { _once_do_slow(o, fn); } } @@ -234,6 +264,6 @@ _once_do_slow :: proc(o: ^Once, fn: proc()) { defer mutex_unlock(&o.m); if !o.done { fn(); - atomic_store_rel(&o.done, true); + atomic_store_release(&o.done, true); } } diff --git a/core/sync/sync2/primitives.odin b/core/sync/sync2/primitives.odin index bf16ea735..1ed83f706 100644 --- a/core/sync/sync2/primitives.odin +++ b/core/sync/sync2/primitives.odin @@ -25,6 +25,18 @@ mutex_try_lock :: proc(m: ^Mutex) -> bool { return _mutex_try_lock(m); } +// Example: +// +// if mutex_guard(&m) { +// ... +// } +// +@(deferred_in=mutex_unlock) +mutex_guard :: proc(m: ^Mutex) -> bool { + mutex_lock(m); + return true; +} + // A RW_Mutex is a reader/writer mutual exclusion lock // The lock can be held by any arbitrary number of readers or a single writer // The zero value for a RW_Mutex is an unlocked mutex @@ -65,6 +77,31 @@ rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool { return _rw_mutex_try_shared_lock(rw); } +// Example: +// +// if rw_mutex_guard(&m) { +// ... +// } +// +@(deferred_in=rw_mutex_unlock) +rw_mutex_guard :: proc(m: ^RW_Mutex) -> bool { + rw_mutex_lock(m); + return true; +} + +// Example: +// +// if rw_mutex_shared_guard(&m) { +// ... +// } +// +@(deferred_in=rw_mutex_shared_unlock) +rw_mutex_shared_guard :: proc(m: ^RW_Mutex) -> bool { + rw_mutex_shared_lock(m); + return true; +} + + // A Recusrive_Mutex is a recursive mutual exclusion lock // The zero value for a Recursive_Mutex is an unlocked mutex @@ -87,6 +124,18 @@ recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool { } +// Example: +// +// if recursive_mutex_guard(&m) { +// ... +// } +// +@(deferred_in=recursive_mutex_unlock) +recursive_mutex_guard :: proc(m: ^Recursive_Mutex) -> bool { + recursive_mutex_lock(m); + return true; +} + // Cond implements a condition variable, a rendezvous point for threads // waiting for signalling the occurence of an event diff --git a/core/sync/sync2/primitives_pthreads.odin b/core/sync/sync2/primitives_pthreads.odin index ea0d140d8..5fd43d871 100644 --- a/core/sync/sync2/primitives_pthreads.odin +++ b/core/sync/sync2/primitives_pthreads.odin @@ -84,7 +84,7 @@ _rw_mutex_shared_lock :: proc(rw: ^RW_Mutex) { state := atomic_load(&rw.impl.state); for state & (RW_Mutex_State_Is_Writing|RW_Mutex_State_Writer_Mask) == 0 { ok: bool; - state, ok = atomic_cxchgweak(&rw.impl.state, state, state + RW_Mutex_State_Reader); + state, ok = atomic_compare_exchange_weak(&rw.impl.state, state, state + RW_Mutex_State_Reader); if ok { return; } @@ -107,7 +107,7 @@ _rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) { _rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool { state := atomic_load(&rw.impl.state); if state & (RW_Mutex_State_Is_Writing|RW_Mutex_State_Writer_Mask) == 0 { - _, ok := atomic_cxchg(&rw.impl.state, state, state + RW_Mutex_State_Reader); + _, ok := atomic_compare_exchange_strong(&rw.impl.state, state, state + RW_Mutex_State_Reader); if ok { return true; } diff --git a/core/sync/sync2/primitives_windows.odin b/core/sync/sync2/primitives_windows.odin index a66b4a9bd..219af0162 100644 --- a/core/sync/sync2/primitives_windows.odin +++ b/core/sync/sync2/primitives_windows.odin @@ -58,7 +58,7 @@ _Recursive_Mutex :: struct { _recursive_mutex_lock :: proc(m: ^Recursive_Mutex) { tid := win32.GetCurrentThreadId(); for { - prev_owner := atomic_cxchg_acq(&m.impl.owner, tid, 0); + prev_owner := atomic_compare_exchange_strong_acquire(&m.impl.owner, tid, 0); switch prev_owner { case 0, tid: m.impl.claim_count += 1; @@ -80,7 +80,7 @@ _recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) { if m.impl.claim_count != 0 { return; } - atomic_xchg_rel(&m.impl.owner, 0); + atomic_exchange_release(&m.impl.owner, 0); win32.WakeByAddressSingle(&m.impl.owner); // outside the lock @@ -88,7 +88,7 @@ _recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) { _recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool { tid := win32.GetCurrentThreadId(); - prev_owner := atomic_cxchg_acq(&m.impl.owner, tid, 0); + prev_owner := atomic_compare_exchange_strong_acquire(&m.impl.owner, tid, 0); switch prev_owner { case 0, tid: m.impl.claim_count += 1; @@ -139,7 +139,7 @@ _sema_wait :: proc(s: ^Sema) { ); original_count = s.impl.count; } - if original_count == atomic_cxchg(&s.impl.count, original_count-1, original_count) { + if original_count == atomic_compare_exchange_strong(&s.impl.count, original_count-1, original_count) { return; } } -- cgit v1.2.3 From 902be0d09c4bf40735a7b15bfc3afe73198e2d5a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 6 May 2021 14:06:22 +0100 Subject: Improve type inference system to allow `&{}` alongside `&T{}` in some cases --- src/check_expr.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index d0e0959d0..75e2bcf5f 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7335,7 +7335,12 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_ast_node(ue, UnaryExpr, node); Ast *prev_unary_address_hint = c->unary_address_hint; c->unary_address_hint = unparen_expr(node); - check_expr_base(c, o, ue->expr, type_hint); + + Type *th = type_hint; + if (ue->op.kind == Token_And) { + th = type_deref(th); + } + check_expr_base(c, o, ue->expr, th); c->unary_address_hint = prev_unary_address_hint; node->viral_state_flags |= ue->expr->viral_state_flags; -- cgit v1.2.3 From 47f97b8f89e1d22967b7117546fa7552d895bd1f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 6 May 2021 14:09:50 +0100 Subject: Remove unused hint --- src/check_expr.cpp | 4 ---- src/checker.hpp | 1 - 2 files changed, 5 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 75e2bcf5f..60f07a068 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7333,15 +7333,11 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_end; case_ast_node(ue, UnaryExpr, node); - Ast *prev_unary_address_hint = c->unary_address_hint; - c->unary_address_hint = unparen_expr(node); - Type *th = type_hint; if (ue->op.kind == Token_And) { th = type_deref(th); } check_expr_base(c, o, ue->expr, th); - c->unary_address_hint = prev_unary_address_hint; node->viral_state_flags |= ue->expr->viral_state_flags; if (o->mode == Addressing_Invalid) { diff --git a/src/checker.hpp b/src/checker.hpp index 4ff72717d..41b6e8c42 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -335,7 +335,6 @@ struct CheckerContext { Scope * polymorphic_scope; Ast *assignment_lhs_hint; - Ast *unary_address_hint; }; struct Checker { -- cgit v1.2.3 From d70b5475eb5d8672395fae08c10d037f28247fdf Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 7 May 2021 13:18:01 +0100 Subject: Minor code clean up --- src/checker.cpp | 79 ++++++++++++++++++++++++--------------------------------- 1 file changed, 33 insertions(+), 46 deletions(-) diff --git a/src/checker.cpp b/src/checker.cpp index 3b7a58c5e..8016f1020 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -675,55 +675,44 @@ Entity *add_global_entity(Entity *entity, Scope *scope=builtin_pkg->scope) { return entity; } -void add_global_constant(String name, Type *type, ExactValue value) { +void add_global_constant(char const *name, Type *type, ExactValue value) { Entity *entity = alloc_entity(Entity_Constant, nullptr, make_token_ident(name), type); entity->Constant.value = value; add_global_entity(entity); } -void add_global_string_constant(String name, String value) { +void add_global_string_constant(char const *name, String const &value) { add_global_constant(name, t_untyped_string, exact_value_string(value)); } +void add_global_bool_constant(char const *name, bool value) { + add_global_constant(name, t_untyped_bool, exact_value_bool(value)); +} void add_global_type_entity(String name, Type *type) { add_global_entity(alloc_entity_type_name(nullptr, make_token_ident(name), type)); } - -void init_universal(void) { - BuildContext *bc = &build_context; - - // NOTE(bill): No need to free these - // gbAllocator a = heap_allocator(); +AstPackage *create_builtin_package(char const *name) { gbAllocator a = permanent_allocator(); + AstPackage *pkg = gb_alloc_item(a, AstPackage); + pkg->name = make_string_c(name); + pkg->kind = Package_Normal; - builtin_pkg = gb_alloc_item(a, AstPackage); - builtin_pkg->name = str_lit("builtin"); - builtin_pkg->kind = Package_Normal; - - builtin_pkg->scope = create_scope(nullptr); - builtin_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global | ScopeFlag_Builtin; - builtin_pkg->scope->pkg = builtin_pkg; - - intrinsics_pkg = gb_alloc_item(a, AstPackage); - intrinsics_pkg->name = str_lit("intrinsics"); - intrinsics_pkg->kind = Package_Normal; - - intrinsics_pkg->scope = create_scope(nullptr); - intrinsics_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global | ScopeFlag_Builtin; - intrinsics_pkg->scope->pkg = intrinsics_pkg; - - config_pkg = gb_alloc_item(a, AstPackage); - config_pkg->name = str_lit("config"); - config_pkg->kind = Package_Normal; + pkg->scope = create_scope(nullptr); + pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global | ScopeFlag_Builtin; + pkg->scope->pkg = pkg; + return pkg; +} - config_pkg->scope = create_scope(nullptr); - config_pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global | ScopeFlag_Builtin; - config_pkg->scope->pkg = config_pkg; +void init_universal(void) { + BuildContext *bc = &build_context; + builtin_pkg = create_builtin_package("builtin"); + intrinsics_pkg = create_builtin_package("intrinsics"); + config_pkg = create_builtin_package("config"); // Types for (isize i = 0; i < gb_count_of(basic_types); i++) { @@ -740,23 +729,23 @@ void init_universal(void) { } // Constants - add_global_constant(str_lit("true"), t_untyped_bool, exact_value_bool(true)); - add_global_constant(str_lit("false"), t_untyped_bool, exact_value_bool(false)); - add_global_entity(alloc_entity_nil(str_lit("nil"), t_untyped_nil)); + add_global_bool_constant("true", true); + add_global_bool_constant("false", false); + // TODO(bill): Set through flags in the compiler - add_global_string_constant(str_lit("ODIN_OS"), bc->ODIN_OS); - add_global_string_constant(str_lit("ODIN_ARCH"), bc->ODIN_ARCH); - add_global_string_constant(str_lit("ODIN_ENDIAN"), bc->ODIN_ENDIAN); - add_global_string_constant(str_lit("ODIN_VENDOR"), bc->ODIN_VENDOR); - add_global_string_constant(str_lit("ODIN_VERSION"), bc->ODIN_VERSION); - add_global_string_constant(str_lit("ODIN_ROOT"), bc->ODIN_ROOT); - add_global_constant(str_lit("ODIN_DEBUG"), t_untyped_bool, exact_value_bool(bc->ODIN_DEBUG)); - add_global_constant(str_lit("ODIN_DISABLE_ASSERT"), t_untyped_bool, exact_value_bool(bc->ODIN_DISABLE_ASSERT)); - add_global_constant(str_lit("ODIN_DEFAULT_TO_NIL_ALLOCATOR"), t_untyped_bool, exact_value_bool(bc->ODIN_DEFAULT_TO_NIL_ALLOCATOR)); - add_global_constant(str_lit("ODIN_NO_DYNAMIC_LITERALS"), t_untyped_bool, exact_value_bool(bc->no_dynamic_literals)); - add_global_constant(str_lit("ODIN_TEST"), t_untyped_bool, exact_value_bool(bc->command_kind == Command_test)); + add_global_string_constant("ODIN_OS", bc->ODIN_OS); + add_global_string_constant("ODIN_ARCH", bc->ODIN_ARCH); + add_global_string_constant("ODIN_ENDIAN", bc->ODIN_ENDIAN); + add_global_string_constant("ODIN_VENDOR", bc->ODIN_VENDOR); + add_global_string_constant("ODIN_VERSION", bc->ODIN_VERSION); + add_global_string_constant("ODIN_ROOT", bc->ODIN_ROOT); + add_global_bool_constant("ODIN_DEBUG", bc->ODIN_DEBUG); + add_global_bool_constant("ODIN_DISABLE_ASSERT", bc->ODIN_DISABLE_ASSERT); + add_global_bool_constant("ODIN_DEFAULT_TO_NIL_ALLOCATOR", bc->ODIN_DEFAULT_TO_NIL_ALLOCATOR); + add_global_bool_constant("ODIN_NO_DYNAMIC_LITERALS", bc->no_dynamic_literals); + add_global_bool_constant("ODIN_TEST", bc->command_kind == Command_test); // Builtin Procedures @@ -801,8 +790,6 @@ void init_universal(void) { } GB_ASSERT(type != nullptr); - - Entity *entity = alloc_entity_constant(nullptr, make_token_ident(name), type, value); entity->state = EntityState_Resolved; if (scope_insert(config_pkg->scope, entity)) { -- cgit v1.2.3 From ff6fdc7812bce3edfbf102dddc97c5821da6e840 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 10 May 2021 21:16:26 +0100 Subject: Correct SysV ABI for `-> (f32, bool)` --- src/llvm_abi.cpp | 5 +++++ src/llvm_backend.cpp | 27 +++++++++++++++++++++------ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 739d43d60..6635423e7 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -549,6 +549,11 @@ namespace lbAbiAmd64SysV { } else if (oldv == RegClass_Memory || newv == RegClass_Memory) { return; } else if (oldv == RegClass_Int || newv == RegClass_Int) { + if (oldv == RegClass_SSEFv || oldv == RegClass_SSEFs) { + oldv = RegClass_Int; + } else if (newv == RegClass_SSEFv || newv == RegClass_SSEFs) { + oldv = RegClass_Int; + } return; } else if (oldv == RegClass_X87 || oldv == RegClass_X87Up || oldv == RegClass_ComplexX87 || newv == RegClass_X87 || newv == RegClass_X87Up || newv == RegClass_ComplexX87) { diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 9a6595b24..78ffb36e5 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -5794,9 +5794,15 @@ lbValue lb_find_value_from_entity(lbModule *m, Entity *e) { if (USE_SEPARTE_MODULES) { lbModule *other_module = lb_pkg_module(m->gen, e->pkg); + + // TODO(bill): correct this logic bool is_external = other_module != m; if (!is_external) { - other_module = e->code_gen_module; + if (e->code_gen_module != nullptr) { + other_module = e->code_gen_module; + } else { + other_module = nullptr; + } is_external = other_module != m; } @@ -5806,8 +5812,20 @@ lbValue lb_find_value_from_entity(lbModule *m, Entity *e) { lbValue g = {}; g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(permanent_allocator(), name)); g.type = alloc_type_pointer(e->type); + lb_add_entity(m, e, g); + lb_add_member(m, name, g); + LLVMSetLinkage(g.value, LLVMExternalLinkage); + // if (other_module != nullptr) { + // lbValue *other_found = string_map_get(&other_module->members, name); + // if (other_found) { + // lbValue other_g = *other_found; + // } + // } + + // LLVMSetLinkage(other_g.value, LLVMExternalLinkage); + if (e->Variable.thread_local_model != "") { LLVMSetThreadLocal(g.value, true); @@ -5827,8 +5845,7 @@ lbValue lb_find_value_from_entity(lbModule *m, Entity *e) { LLVMSetThreadLocalMode(g.value, mode); } - lb_add_entity(m, e, g); - lb_add_member(m, name, g); + return g; } } @@ -14713,9 +14730,7 @@ void lb_generate_code(lbGenerator *gen) { if (is_export) { LLVMSetLinkage(g.value, LLVMDLLExportLinkage); LLVMSetDLLStorageClass(g.value, LLVMDLLExportStorageClass); - } - - if (e->flags & EntityFlag_Static) { + } else { // LLVMSetLinkage(g.value, LLVMInternalLinkage); } -- cgit v1.2.3 From 9a4d942b0bad10c4543a3818ced1475bd14bed00 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 10 May 2021 21:29:25 +0100 Subject: Fix debug information for array types by setting the `DISubrange` --- src/llvm_backend.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 78ffb36e5..c581ea46d 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1886,16 +1886,32 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { case Type_Pointer: return LLVMDIBuilderCreatePointerType(m->debug_builder, lb_debug_type(m, type->Pointer.elem), word_bits, word_bits, 0, nullptr, 0); - case Type_Array: + case Type_Array: { + LLVMMetadataRef subscripts[1] = {}; + subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder, + 0ll, + type->Array.count + ); + return LLVMDIBuilderCreateArrayType(m->debug_builder, - type->Array.count, + 8*cast(uint64_t)type_size_of(type), 8*cast(unsigned)type_align_of(type), lb_debug_type(m, type->Array.elem), - nullptr, 0); + subscripts, gb_count_of(subscripts)); + } case Type_EnumeratedArray: { + LLVMMetadataRef subscripts[1] = {}; + subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder, + 0ll, + type->EnumeratedArray.count + ); + LLVMMetadataRef array_type = LLVMDIBuilderCreateArrayType(m->debug_builder, - type->EnumeratedArray.count, 8*cast(unsigned)type_align_of(type), lb_debug_type(m, type->EnumeratedArray.elem), nullptr, 0); + 8*cast(uint64_t)type_size_of(type), + 8*cast(unsigned)type_align_of(type), + lb_debug_type(m, type->EnumeratedArray.elem), + subscripts, gb_count_of(subscripts)); gbString name = type_to_string(type, temporary_allocator()); return LLVMDIBuilderCreateTypedef(m->debug_builder, array_type, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type))); } -- cgit v1.2.3 From 073bd3f6c9a07298e7381ba2c504b26546a65454 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 May 2021 10:55:55 +0100 Subject: Update LLVM-C.lib (since it was missing many required definitions missing from the official one that LLVM supplied) --- bin/llvm/windows/LLVM-C.lib | Bin 274018 -> 287196 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/bin/llvm/windows/LLVM-C.lib b/bin/llvm/windows/LLVM-C.lib index 2353c6a39..a55bb863c 100644 Binary files a/bin/llvm/windows/LLVM-C.lib and b/bin/llvm/windows/LLVM-C.lib differ -- cgit v1.2.3 From f31b09212ac33159f12ee42e01c04b5037c282f8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 May 2021 12:00:27 +0100 Subject: Improve SysV ABI --- src/llvm_abi.cpp | 62 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 6635423e7..c2298bcd0 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -419,8 +419,14 @@ namespace lbAbiAmd64SysV { switch (reg_class) { case RegClass_SSEFs: case RegClass_SSEFv: + case RegClass_SSEDs: case RegClass_SSEDv: return true; + case RegClass_SSEInt8: + case RegClass_SSEInt16: + case RegClass_SSEInt32: + case RegClass_SSEInt64: + return true; } return false; } @@ -538,35 +544,48 @@ namespace lbAbiAmd64SysV { return reg_classes; } - void unify(Array *cls, i64 i, RegClass newv) { - RegClass &oldv = (*cls)[i]; + void unify(Array *cls, i64 i, RegClass const newv) { + RegClass const oldv = (*cls)[i]; if (oldv == newv) { return; - } else if (oldv == RegClass_NoClass) { - oldv = newv; + } + + RegClass to_write = newv; + if (oldv == RegClass_NoClass) { + to_write = newv; } else if (newv == RegClass_NoClass) { return; } else if (oldv == RegClass_Memory || newv == RegClass_Memory) { - return; - } else if (oldv == RegClass_Int || newv == RegClass_Int) { - if (oldv == RegClass_SSEFv || oldv == RegClass_SSEFs) { - oldv = RegClass_Int; - } else if (newv == RegClass_SSEFv || newv == RegClass_SSEFs) { - oldv = RegClass_Int; + to_write = RegClass_Memory; + } else if (oldv == RegClass_Int || newv == RegClass_Int) { + to_write = RegClass_Int; + } else if (oldv == RegClass_X87 || oldv == RegClass_X87Up || oldv == RegClass_ComplexX87) { + to_write = RegClass_Memory; + } else if (newv == RegClass_X87 || newv == RegClass_X87Up || newv == RegClass_ComplexX87) { + to_write = RegClass_Memory; + } else if (newv == RegClass_SSEUp) { + switch (oldv) { + case RegClass_SSEFv: + case RegClass_SSEFs: + case RegClass_SSEDv: + case RegClass_SSEDs: + case RegClass_SSEInt8: + case RegClass_SSEInt16: + case RegClass_SSEInt32: + case RegClass_SSEInt64: + return; } - return; - } else if (oldv == RegClass_X87 || oldv == RegClass_X87Up || oldv == RegClass_ComplexX87 || - newv == RegClass_X87 || newv == RegClass_X87Up || newv == RegClass_ComplexX87) { - oldv = RegClass_Memory; - } else { - oldv = newv; } + + (*cls)[i] = to_write; } void fixup(LLVMTypeRef t, Array *cls) { i64 i = 0; i64 e = cls->count; - if (e > 2 && (lb_is_type_kind(t, LLVMStructTypeKind) || lb_is_type_kind(t, LLVMArrayTypeKind))) { + if (e > 2 && (lb_is_type_kind(t, LLVMStructTypeKind) || + lb_is_type_kind(t, LLVMArrayTypeKind) || + lb_is_type_kind(t, LLVMVectorTypeKind))) { RegClass &oldv = (*cls)[i]; if (is_sse(oldv)) { for (i++; i < e; i++) { @@ -610,8 +629,8 @@ namespace lbAbiAmd64SysV { unsigned llvec_len(Array const ®_classes, isize offset) { unsigned len = 1; - for (isize i = offset+1; i < reg_classes.count; i++) { - if (reg_classes[offset] != RegClass_SSEFv && reg_classes[i] != RegClass_SSEUp) { + for (isize i = offset; i < reg_classes.count; i++) { + if (reg_classes[i] != RegClass_SSEUp) { break; } len++; @@ -622,7 +641,7 @@ namespace lbAbiAmd64SysV { LLVMTypeRef llreg(LLVMContextRef c, Array const ®_classes) { auto types = array_make(heap_allocator(), 0, reg_classes.count); - for_array(i, reg_classes) { + for (isize i = 0; i < reg_classes.count; /**/) { RegClass reg_class = reg_classes[i]; switch (reg_class) { case RegClass_Int: @@ -664,7 +683,7 @@ namespace lbAbiAmd64SysV { break; } - unsigned vec_len = llvec_len(reg_classes, i); + unsigned vec_len = llvec_len(reg_classes, i+1); LLVMTypeRef vec_type = LLVMVectorType(elem_type, vec_len * elems_per_word); array_add(&types, vec_type); i += vec_len; @@ -680,6 +699,7 @@ namespace lbAbiAmd64SysV { default: GB_PANIC("Unhandled RegClass"); } + i += 1; } GB_ASSERT(types.count != 0); -- cgit v1.2.3 From 8ff80dec5824a710eac384ed83608f5a88a57cb7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 May 2021 12:00:55 +0100 Subject: Minor change (in preparation for something else) to opt passes --- src/llvm_backend.cpp | 16 ++++++++-------- src/llvm_backend_opt.cpp | 32 +++++++++++++++++++++++++++----- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index c581ea46d..d784e36b6 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -14019,7 +14019,7 @@ struct lbGlobalVariable { lbProcedure *lb_create_startup_type_info(lbModule *m) { LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod); - lb_populate_function_pass_manager(default_function_pass_manager, false, build_context.optimization_level); + lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level); LLVMFinalizeFunctionPassManager(default_function_pass_manager); Type *params = alloc_type_tuple(); @@ -14050,7 +14050,7 @@ lbProcedure *lb_create_startup_type_info(lbModule *m) { lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *startup_type_info, Array &global_variables) { // Startup Runtime LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(main_module->mod); - lb_populate_function_pass_manager(default_function_pass_manager, false, build_context.optimization_level); + lb_populate_function_pass_manager(main_module, default_function_pass_manager, false, build_context.optimization_level); LLVMFinalizeFunctionPassManager(default_function_pass_manager); Type *params = alloc_type_tuple(); @@ -14140,7 +14140,7 @@ lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *start lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) { LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod); - lb_populate_function_pass_manager(default_function_pass_manager, false, build_context.optimization_level); + lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level); LLVMFinalizeFunctionPassManager(default_function_pass_manager); Type *params = alloc_type_tuple(); @@ -14351,10 +14351,10 @@ WORKER_TASK_PROC(lb_llvm_function_pass_worker_proc) { LLVMInitializeFunctionPassManager(function_pass_manager_size); LLVMInitializeFunctionPassManager(function_pass_manager_speed); - lb_populate_function_pass_manager(default_function_pass_manager, false, build_context.optimization_level); - lb_populate_function_pass_manager_specific(function_pass_manager_minimal, 0); - lb_populate_function_pass_manager_specific(function_pass_manager_size, 1); - lb_populate_function_pass_manager_specific(function_pass_manager_speed, 2); + lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level); + lb_populate_function_pass_manager_specific(m, function_pass_manager_minimal, 0); + lb_populate_function_pass_manager_specific(m, function_pass_manager_size, 1); + lb_populate_function_pass_manager_specific(m, function_pass_manager_speed, 2); LLVMFinalizeFunctionPassManager(default_function_pass_manager); LLVMFinalizeFunctionPassManager(function_pass_manager_minimal); @@ -14364,7 +14364,7 @@ WORKER_TASK_PROC(lb_llvm_function_pass_worker_proc) { LLVMPassManagerRef default_function_pass_manager_without_memcpy = LLVMCreateFunctionPassManagerForModule(m->mod); LLVMInitializeFunctionPassManager(default_function_pass_manager_without_memcpy); - lb_populate_function_pass_manager(default_function_pass_manager_without_memcpy, true, build_context.optimization_level); + lb_populate_function_pass_manager(m, default_function_pass_manager_without_memcpy, true, build_context.optimization_level); LLVMFinalizeFunctionPassManager(default_function_pass_manager_without_memcpy); diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index 3b268dffa..fa1c87bf5 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -1,7 +1,25 @@ -void lb_populate_function_pass_manager(LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level); +void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level); void lb_add_function_simplifcation_passes(LLVMPassManagerRef mpm, i32 optimization_level); void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPassManagerRef mpm, i32 optimization_level); -void lb_populate_function_pass_manager_specific(LLVMPassManagerRef fpm, i32 optimization_level); +void lb_populate_function_pass_manager_specific(lbModule *m, LLVMPassManagerRef fpm, i32 optimization_level); + +LLVMBool lb_must_preserve_predicate_callback(LLVMValueRef value, void *user_data) { + lbModule *m = cast(lbModule *)user_data; + if (m == nullptr) { + return false; + } + if (value == nullptr) { + return false; + } + return LLVMIsAAllocaInst(value) != nullptr; +} + +void lb_add_must_preserve_predicate_pass(lbModule *m, LLVMPassManagerRef fpm, i32 optimization_level) { + if (false && optimization_level == 0 && m->debug_builder) { + LLVMAddInternalizePassWithMustPreservePredicate(fpm, m, lb_must_preserve_predicate_callback); + } +} + void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm) { LLVMAddPromoteMemoryToRegisterPass(fpm); @@ -15,11 +33,13 @@ void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm) { LLVMAddCFGSimplificationPass(fpm); } -void lb_populate_function_pass_manager(LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level) { +void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level) { // NOTE(bill): Treat -opt:3 as if it was -opt:2 // TODO(bill): Determine which opt definitions should exist in the first place optimization_level = gb_clamp(optimization_level, 0, 2); + lb_add_must_preserve_predicate_pass(m, fpm, optimization_level); + if (ignore_memcpy_pass) { lb_basic_populate_function_pass_manager(fpm); return; @@ -57,11 +77,13 @@ void lb_populate_function_pass_manager(LLVMPassManagerRef fpm, bool ignore_memcp #endif } -void lb_populate_function_pass_manager_specific(LLVMPassManagerRef fpm, i32 optimization_level) { +void lb_populate_function_pass_manager_specific(lbModule *m, LLVMPassManagerRef fpm, i32 optimization_level) { // NOTE(bill): Treat -opt:3 as if it was -opt:2 // TODO(bill): Determine which opt definitions should exist in the first place optimization_level = gb_clamp(optimization_level, 0, 2); + lb_add_must_preserve_predicate_pass(m, fpm, optimization_level); + if (optimization_level == 0) { LLVMAddMemCpyOptPass(fpm); lb_basic_populate_function_pass_manager(fpm); @@ -194,7 +216,7 @@ void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPa LLVMAddGlobalDCEPass(mpm); LLVMAddGlobalOptimizerPass(mpm); - // LLVMAddLowerConstantIntrinsicsPass(mpm); + LLVMAddLowerConstantIntrinsicsPass(mpm); LLVMAddLoopRotatePass(mpm); -- cgit v1.2.3 From 8bb6651dda3e239be9ae85eeadf2d189bd478f73 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 May 2021 12:08:09 +0100 Subject: Remove test call for LLVM --- src/llvm_backend_opt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index fa1c87bf5..b40998c5f 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -16,7 +16,7 @@ LLVMBool lb_must_preserve_predicate_callback(LLVMValueRef value, void *user_data void lb_add_must_preserve_predicate_pass(lbModule *m, LLVMPassManagerRef fpm, i32 optimization_level) { if (false && optimization_level == 0 && m->debug_builder) { - LLVMAddInternalizePassWithMustPreservePredicate(fpm, m, lb_must_preserve_predicate_callback); + // LLVMAddInternalizePassWithMustPreservePredicate(fpm, m, lb_must_preserve_predicate_callback); } } -- cgit v1.2.3 From 20f7e61363db27bc55d652d9d26fb704b8a71237 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 May 2021 12:11:59 +0100 Subject: Revert LLVM-C.lib --- bin/llvm/windows/LLVM-C.lib | Bin 287196 -> 274018 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/bin/llvm/windows/LLVM-C.lib b/bin/llvm/windows/LLVM-C.lib index a55bb863c..2353c6a39 100644 Binary files a/bin/llvm/windows/LLVM-C.lib and b/bin/llvm/windows/LLVM-C.lib differ -- cgit v1.2.3 From eac61fb5361a595363684df4737c3d918e9e7b88 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 May 2021 12:17:48 +0100 Subject: Remove `LLVMAddLowerConstantIntrinsicsPass` --- src/llvm_backend_opt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index b40998c5f..6b7ca507a 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -216,7 +216,7 @@ void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPa LLVMAddGlobalDCEPass(mpm); LLVMAddGlobalOptimizerPass(mpm); - LLVMAddLowerConstantIntrinsicsPass(mpm); + // LLVMAddLowerConstantIntrinsicsPass(mpm); LLVMAddLoopRotatePass(mpm); -- cgit v1.2.3 From e1c2528d87b182b19311ffad71a060acf218a7a4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 May 2021 12:29:27 +0100 Subject: Remove warning on \*nix --- src/llvm_backend.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index d784e36b6..d92df5ae2 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -13061,13 +13061,12 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { void lb_init_module(lbModule *m, Checker *c) { m->info = &c->info; - gbString module_name = gb_string_make(heap_allocator(), "odin_package-"); + gbString module_name = gb_string_make(heap_allocator(), "odin_package"); if (m->pkg) { + module_name = gb_string_appendc(module_name, "-"); module_name = gb_string_append_length(module_name, m->pkg->name.text, m->pkg->name.len); } else if (USE_SEPARTE_MODULES) { - module_name = gb_string_appendc(module_name, "builtin"); - } else { - module_name = "odin_package"; + module_name = gb_string_appendc(module_name, "-builtin"); } m->ctx = LLVMContextCreate(); -- cgit v1.2.3 From d0f7cf74e94741f2820a61ecba60ed5be460a921 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 May 2021 12:31:56 +0100 Subject: Add llvm-config version 11 check for Darwin in Makefile --- Makefile | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/Makefile b/Makefile index 6633438bc..5cdf1e467 100644 --- a/Makefile +++ b/Makefile @@ -8,26 +8,31 @@ CC=clang OS=$(shell uname) ifeq ($(OS), Darwin) - LLVM_CONFIG=llvm-config - - LDFLAGS:=$(LDFLAGS) -liconv - CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags) - LDFLAGS:=$(LDFLAGS) -lLLVM-C + LLVM_CONFIG=llvm-config + ifneq ($(shell llvm-config --version | grep '^11\.'),) + LLVM_CONFIG=llvm-config + else + $(error "Requirement: llvm-config must be version 11") + endif + + LDFLAGS:=$(LDFLAGS) -liconv + CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags) + LDFLAGS:=$(LDFLAGS) -lLLVM-C endif ifeq ($(OS), Linux) - LLVM_CONFIG=llvm-config-11 - ifneq ($(shell which llvm-config-11 2>/dev/null),) - LLVM_CONFIG=llvm-config-11 - else - ifneq ($(shell llvm-config --version | grep '^11\.'),) - LLVM_CONFIG=llvm-config - else - $(error "Requirement: llvm-config must be version 11") - endif - endif - - CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags) - LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs) + LLVM_CONFIG=llvm-config-11 + ifneq ($(shell which llvm-config-11 2>/dev/null),) + LLVM_CONFIG=llvm-config-11 + else + ifneq ($(shell llvm-config --version | grep '^11\.'),) + LLVM_CONFIG=llvm-config + else + $(error "Requirement: llvm-config must be version 11") + endif + endif + + CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags) + LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs) endif all: debug demo -- cgit v1.2.3 From 8144e82c6c1ffa3406ed8bcd87c15096d6ab4538 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 May 2021 13:13:34 +0100 Subject: Fix docs for `-lld` --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 8f13b28f6..0dd1e0c27 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1719,7 +1719,7 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(2, "Disables automatic linking with the C Run Time"); print_usage_line(0, ""); - print_usage_line(1, "-use-lld"); + print_usage_line(1, "-lld"); print_usage_line(2, "Use the LLD linker rather than the default"); print_usage_line(0, ""); -- cgit v1.2.3 From d353f97f91c18fd910c4c16604dcaf3e3afbc5f2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 May 2021 13:30:27 +0100 Subject: Add `byval` with `align`, `sret` attributes for SysV --- src/llvm_abi.cpp | 39 +++++++++++++++++++++++++++------------ src/llvm_backend.cpp | 27 ++++++++++++++++++++++++--- src/llvm_backend.hpp | 1 + 3 files changed, 52 insertions(+), 15 deletions(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index c2298bcd0..d8f16b451 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -10,21 +10,35 @@ struct lbArgType { LLVMTypeRef cast_type; // Optional LLVMTypeRef pad_type; // Optional LLVMAttributeRef attribute; // Optional + LLVMAttributeRef align_attribute; // Optional }; + +i64 lb_sizeof(LLVMTypeRef type); +i64 lb_alignof(LLVMTypeRef type); + lbArgType lb_arg_type_direct(LLVMTypeRef type, LLVMTypeRef cast_type, LLVMTypeRef pad_type, LLVMAttributeRef attr) { - return lbArgType{lbArg_Direct, type, cast_type, pad_type, attr}; + return lbArgType{lbArg_Direct, type, cast_type, pad_type, attr, nullptr}; } lbArgType lb_arg_type_direct(LLVMTypeRef type) { return lb_arg_type_direct(type, nullptr, nullptr, nullptr); } lbArgType lb_arg_type_indirect(LLVMTypeRef type, LLVMAttributeRef attr) { - return lbArgType{lbArg_Indirect, type, nullptr, nullptr, attr}; + return lbArgType{lbArg_Indirect, type, nullptr, nullptr, attr, nullptr}; +} + +lbArgType lb_arg_type_indirect_byval(LLVMContextRef c, LLVMTypeRef type) { + i64 alignment = lb_alignof(type); + alignment = gb_max(alignment, 8); + + LLVMAttributeRef byval_attr = lb_create_enum_attribute_with_type(c, "byval", type); + LLVMAttributeRef align_attr = lb_create_enum_attribute(c, "align", alignment); + return lbArgType{lbArg_Indirect, type, nullptr, nullptr, byval_attr, align_attr}; } lbArgType lb_arg_type_ignore(LLVMTypeRef type) { - return lbArgType{lbArg_Ignore, type, nullptr, nullptr, nullptr}; + return lbArgType{lbArg_Ignore, type, nullptr, nullptr, nullptr, nullptr}; } struct lbFunctionType { @@ -121,6 +135,9 @@ void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType *ft, ProcCa if (arg->attribute) { LLVMAddAttributeAtIndex(fn, arg_index+1, arg->attribute); } + if (arg->align_attribute) { + LLVMAddAttributeAtIndex(fn, arg_index+1, arg->align_attribute); + } arg_index++; } @@ -145,8 +162,6 @@ void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType *ft, ProcCa } -i64 lb_sizeof(LLVMTypeRef type); -i64 lb_alignof(LLVMTypeRef type); i64 lb_sizeof(LLVMTypeRef type) { LLVMTypeKind kind = LLVMGetTypeKind(type); @@ -328,7 +343,7 @@ namespace lbAbi386 { if (sz == 0) { args[i] = lb_arg_type_ignore(t); } else { - args[i] = lb_arg_type_indirect(t, lb_create_enum_attribute(c, "byval")); + args[i] = lb_arg_type_indirect_byval(c, t); } } else { args[i] = non_struct(c, t, false); @@ -348,7 +363,7 @@ namespace lbAbi386 { case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr); case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr); } - LLVMAttributeRef attr = lb_create_enum_attribute(c, "sret"); + LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type); return lb_arg_type_indirect(return_type, attr); } return non_struct(c, return_type, true); @@ -512,9 +527,9 @@ namespace lbAbiAmd64SysV { if (is_mem_cls(cls, attribute_kind)) { LLVMAttributeRef attribute = nullptr; if (attribute_kind == Amd64TypeAttribute_ByVal) { - attribute = lb_create_enum_attribute(c, "byval"); + return lb_arg_type_indirect_byval(c, type); } else if (attribute_kind == Amd64TypeAttribute_StructRect) { - attribute = lb_create_enum_attribute(c, "sret"); + attribute = lb_create_enum_attribute_with_type(c, "sret", type); } return lb_arg_type_indirect(type, attribute); } else { @@ -814,7 +829,7 @@ namespace lbAbiAmd64SysV { if (sz == 0) { args[i] = lb_arg_type_ignore(t); } else { - args[i] = lb_arg_type_indirect(t, lb_create_enum_attribute(c, "byval")); + args[i] = lb_arg_type_indirect(t, lb_create_enum_attribute_with_type(c, "byval", t)); } } else { args[i] = non_struct(c, t); @@ -834,7 +849,7 @@ namespace lbAbiAmd64SysV { case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr); case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr); } - LLVMAttributeRef attr = lb_create_enum_attribute(c, "sret"); + LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type); return lb_arg_type_indirect(return_type, attr); } else if (build_context.metrics.os == TargetOs_windows && lb_is_type_kind(return_type, LLVMIntegerTypeKind) && lb_sizeof(return_type) == 16) { return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 128), nullptr, nullptr); @@ -984,7 +999,7 @@ namespace lbAbiArm64 { } return lb_arg_type_direct(type, cast_type, nullptr, nullptr); } else { - LLVMAttributeRef attr = lb_create_enum_attribute(c, "sret"); + LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", type); return lb_arg_type_indirect(type, attr); } } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index d92df5ae2..28c37be7d 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2545,20 +2545,41 @@ lbValue lb_emit_string(lbProcedure *p, lbValue str_elem, lbValue str_len) { } } -LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char const *name, u64 value) { +LLVMAttributeRef lb_create_enum_attribute_with_type(LLVMContextRef ctx, char const *name, LLVMTypeRef type) { String s = make_string_c(name); // NOTE(2021-02-25, bill); All this attributes require a type associated with them // and the current LLVM C API does not expose this functionality yet. // It is better to ignore the attributes for the time being if (s == "byval") { - return nullptr; + // return nullptr; } else if (s == "byref") { return nullptr; } else if (s == "preallocated") { return nullptr; } else if (s == "sret") { - return nullptr; + // return nullptr; + } + + unsigned kind = LLVMGetEnumAttributeKindForName(name, s.len); + GB_ASSERT_MSG(kind != 0, "unknown attribute: %s", name); + return LLVMCreateEnumAttribute(ctx, kind, 0); +} + +LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char const *name, u64 value) { + String s = make_string_c(name); + + // NOTE(2021-02-25, bill); All this attributes require a type associated with them + // and the current LLVM C API does not expose this functionality yet. + // It is better to ignore the attributes for the time being + if (s == "byval") { + GB_PANIC("lb_create_enum_attribute_with_type should be used for %s", name); + } else if (s == "byref") { + GB_PANIC("lb_create_enum_attribute_with_type should be used for %s", name); + } else if (s == "preallocated") { + GB_PANIC("lb_create_enum_attribute_with_type should be used for %s", name); + } else if (s == "sret") { + GB_PANIC("lb_create_enum_attribute_with_type should be used for %s", name); } unsigned kind = LLVMGetEnumAttributeKindForName(name, s.len); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index f212d9fc0..b35c042ee 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -276,6 +276,7 @@ String lb_mangle_name(lbModule *m, Entity *e); String lb_get_entity_name(lbModule *m, Entity *e, String name = {}); LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char const *name, u64 value=0); +LLVMAttributeRef lb_create_enum_attribute_with_type(LLVMContextRef ctx, char const *name, LLVMTypeRef type); void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name, u64 value); void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name); lbProcedure *lb_create_procedure(lbModule *module, Entity *entity, bool ignore_body=false); -- cgit v1.2.3 From e08f39ec28eaf462ddee6c418721ce4c7a03450e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 May 2021 16:20:50 +0100 Subject: Remove unneeded assert --- src/llvm_abi.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index d8f16b451..dcbd07118 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -717,7 +717,6 @@ namespace lbAbiAmd64SysV { i += 1; } - GB_ASSERT(types.count != 0); if (types.count == 1) { return types[0]; } -- cgit v1.2.3 From d2fcbf0e1d5488ee3d64f94e488122d7800214eb Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 12 May 2021 15:00:19 +0100 Subject: Fix #948 --- src/llvm_backend.cpp | 34 +++++++--------------------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 28c37be7d..81ccf1659 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -10773,42 +10773,22 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri Type *lt = left.type; Type *rt = right.type; - // if (is_type_bit_set(lt) && is_type_bit_set(rt)) { - // Type *blt = base_type(lt); - // Type *brt = base_type(rt); - // i64 bits = gb_max(blt->BitSet.bits, brt->BitSet.bits); - // i64 bytes = bits / 8; - // switch (bytes) { - // case 1: - // left = lb_emit_conv(p, left, t_u8); - // right = lb_emit_conv(p, right, t_u8); - // break; - // case 2: - // left = lb_emit_conv(p, left, t_u16); - // right = lb_emit_conv(p, right, t_u16); - // break; - // case 4: - // left = lb_emit_conv(p, left, t_u32); - // right = lb_emit_conv(p, right, t_u32); - // break; - // case 8: - // left = lb_emit_conv(p, left, t_u64); - // right = lb_emit_conv(p, right, t_u64); - // break; - // default: GB_PANIC("Unknown integer size"); break; - // } - // } - lt = left.type; rt = right.type; i64 ls = type_size_of(lt); i64 rs = type_size_of(rt); + + // NOTE(bill): Quick heuristic, larger types are usually the target type if (ls < rs) { left = lb_emit_conv(p, left, rt); } else if (ls > rs) { right = lb_emit_conv(p, right, lt); } else { - right = lb_emit_conv(p, right, lt); + if (is_type_union(rt)) { + left = lb_emit_conv(p, left, rt); + } else { + right = lb_emit_conv(p, right, lt); + } } } -- cgit v1.2.3 From dfb81431496f68bce56b9a92681a841b072300be Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 12 May 2021 22:23:43 +0100 Subject: Fix #651 --- src/check_expr.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 60f07a068..a64acf744 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2380,9 +2380,15 @@ bool check_cast_internal(CheckerContext *c, Operand *x, Type *type) { if (core_type(bt)->kind == Type_Basic) { if (check_representable_as_constant(c, x->value, bt, &x->value)) { return true; - } else if (is_type_pointer(type) && check_is_castable_to(c, x, type)) { - return true; + } else if (check_is_castable_to(c, x, type)) { + if (is_type_pointer(type)) { + return true; + } } + } else if (check_is_castable_to(c, x, type)) { + x->value = {}; + x->mode = Addressing_Value; + return true; } } else if (check_is_castable_to(c, x, type)) { if (x->mode != Addressing_Constant) { @@ -2392,6 +2398,9 @@ bool check_cast_internal(CheckerContext *c, Operand *x, Type *type) { } else if (is_type_union(type)) { x->mode = Addressing_Value; } + if (x->mode == Addressing_Value) { + x->value = {}; + } return true; } return false; -- cgit v1.2.3 From d962cfdc6bd1865c7df302d08af237027533437f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 12 May 2021 22:38:37 +0100 Subject: Fix #713 --- src/check_expr.cpp | 10 ++++++++-- src/check_type.cpp | 17 ++++++++++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index a64acf744..26c7983de 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5572,9 +5572,15 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr } check_expr(c, operand, arg); if (operand->mode != Addressing_Invalid) { - check_cast(c, operand, t); + if (is_type_polymorphic(t)) { + error(call, "A polymorphic type cannot be used in a type conversion"); + } else { + // NOTE(bill): Otherwise the compiler can override the polymorphic type + // as it assumes it is determining the type + check_cast(c, operand, t); + } } - + operand->type = t; break; } } diff --git a/src/check_type.cpp b/src/check_type.cpp index b90732e00..89f5ca34c 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1271,7 +1271,13 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type * check_assignment(ctx, &o, in_type, str_lit("parameter value")); } - if (out_type_) *out_type_ = default_type(o.type); + if (out_type_) { + if (in_type != nullptr) { + *out_type_ = in_type; + } else { + *out_type_ = default_type(o.type); + } + } return param_value; } @@ -1389,6 +1395,9 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is } } } + + + if (type == nullptr) { error(param, "Invalid parameter type"); type = t_invalid; @@ -1408,6 +1417,12 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is type = t_invalid; } + if (param_value.kind != ParameterValue_Invalid && is_type_polymorphic(type)) { + gbString str = type_to_string(type); + error(params[i], "A default value for a parameter must not be a polymorphic constant type, got %s", str); + gb_string_free(str); + } + if (p->flags&FieldFlag_c_vararg) { if (p->type == nullptr || -- cgit v1.2.3 From da9cabc3344900059d0837729e54e09e58089ae9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 12 May 2021 23:02:57 +0100 Subject: Fix #682 --- src/check_decl.cpp | 13 +++++++++++++ src/check_type.cpp | 10 ---------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/check_decl.cpp b/src/check_decl.cpp index f43248593..baabe4184 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -376,6 +376,7 @@ void override_entity_in_scope(Entity *original_entity, Entity *new_entity) { void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init, Type *named_type) { GB_ASSERT(e->type == nullptr); GB_ASSERT(e->kind == Entity_Constant); + init = unparen_expr(init); if (e->flags & EntityFlag_Visited) { e->type = t_invalid; @@ -409,6 +410,18 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init, e->kind = Entity_TypeName; e->type = nullptr; + if (entity != nullptr && entity->type != nullptr && + is_type_polymorphic_record_unspecialized(entity->type)) { + DeclInfo *decl = decl_info_of_entity(e); + if (decl != nullptr) { + if (decl->attributes.count > 0) { + error(decl->attributes[0], "Constant alias declarations cannot have attributes"); + } + } + + override_entity_in_scope(e, entity); + return; + } check_type_decl(ctx, e, ctx->decl->init_expr, named_type); return; } diff --git a/src/check_type.cpp b/src/check_type.cpp index 89f5ca34c..e433faf7f 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2532,16 +2532,6 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t return true; } } - - // if (ctx->type_level == 0 && entity->state == EntityState_InProgress) { - // error(entity->token, "Illegal declaration cycle of `%.*s`", LIT(entity->token.string)); - // for_array(j, *ctx->type_path) { - // Entity *k = (*ctx->type_path)[j]; - // error(k->token, "\t%.*s refers to", LIT(k->token.string)); - // } - // error(entity->token, "\t%.*s", LIT(entity->token.string)); - // *type = t_invalid; - // } return true; } -- cgit v1.2.3 From fdd0c726bc1b0ff3978ead267001e81bda736673 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 12 May 2021 23:12:20 +0100 Subject: Improve error message to "fix" #640 --- src/check_expr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 26c7983de..ddf2a3aed 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5730,7 +5730,7 @@ void check_expr_with_type_hint(CheckerContext *c, Operand *o, Ast *e, Type *t) { break; case Addressing_Type: if (t == nullptr || !is_type_typeid(t)) { - err_str = "is not an expression but a type"; + err_str = "is not an expression but a type, in this context it is ambiguous"; } break; case Addressing_Builtin: -- cgit v1.2.3 From c81f7b31c6253d70ff08f3c4ca346676f4bfbda0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 12 May 2021 23:22:43 +0100 Subject: Add explicit numbers to `enum AddressingMode` --- src/parser.hpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/parser.hpp b/src/parser.hpp index 2a0b0fa27..982588c0f 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -7,22 +7,22 @@ struct AstFile; struct AstPackage; enum AddressingMode { - Addressing_Invalid, // invalid addressing mode - Addressing_NoValue, // no value (void in C) - Addressing_Value, // computed value (rvalue) - Addressing_Context, // context value - Addressing_Variable, // addressable variable (lvalue) - Addressing_Constant, // constant - Addressing_Type, // type - Addressing_Builtin, // built-in procedure - Addressing_ProcGroup, // procedure group (overloaded procedure) - Addressing_MapIndex, // map index expression - - // lhs: acts like a Variable - // rhs: acts like OptionalOk - Addressing_OptionalOk, // rhs: acts like a value with an optional boolean part (for existence check) - Addressing_SoaVariable, // Struct-Of-Arrays indexed variable - - Addressing_AtomOpAssign, // Specialized for custom atom operations for assignments + Addressing_Invalid = 0, // invalid addressing mode + Addressing_NoValue = 1, // no value (void in C) + Addressing_Value = 2, // computed value (rvalue) + Addressing_Context = 3, // context value + Addressing_Variable = 4, // addressable variable (lvalue) + Addressing_Constant = 5, // constant + Addressing_Type = 6, // type + Addressing_Builtin = 7, // built-in procedure + Addressing_ProcGroup = 8, // procedure group (overloaded procedure) + Addressing_MapIndex = 9, // map index expression - + // lhs: acts like a Variable + // rhs: acts like OptionalOk + Addressing_OptionalOk = 10, // rhs: acts like a value with an optional boolean part (for existence check) + Addressing_SoaVariable = 11, // Struct-Of-Arrays indexed variable + + Addressing_AtomOpAssign = 12, // Specialized for custom atom operations for assignments }; struct TypeAndValue { -- cgit v1.2.3 From 5420cc083d08a4c0faffbcf3ec1deecf05b6265d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 12 May 2021 23:26:21 +0100 Subject: Implement #807 --- core/fmt/fmt.odin | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index f9c386ffb..3b3716a15 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -641,9 +641,9 @@ fmt_write_padding :: proc(fi: ^Info, width: int) { return; } - pad_byte: byte = '0'; - if fi.space { - pad_byte = ' '; + pad_byte: byte = ' '; + if !fi.space { + pad_byte = '0'; } for i := 0; i < width; i += 1 { -- cgit v1.2.3 From c6c5af527b907036361e9de96d7e1dcdd72ee945 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 12 May 2021 23:43:16 +0100 Subject: Fix #746 --- src/parser.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index 376ac58dc..b24bb8aed 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1994,14 +1994,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { Token name = expect_token(f, Token_Ident); if (name.string == "type") { return ast_helper_type(f, token, parse_type(f)); - } /* else if (name.string == "no_deferred") { - operand = parse_expr(f, false); - if (unparen_expr(operand)->kind != Ast_CallExpr) { - syntax_error(operand, "#no_deferred can only be applied to procedure calls"); - operand = ast_bad_expr(f, token, f->curr_token); - } - operand->state_flags |= StateFlag_no_deferred; - } */ else if (name.string == "file") { + } else if (name.string == "file") { return ast_basic_directive(f, token, name.string); } else if (name.string == "line") { return ast_basic_directive(f, token, name.string); } else if (name.string == "procedure") { return ast_basic_directive(f, token, name.string); @@ -2046,6 +2039,10 @@ Ast *parse_operand(AstFile *f, bool lhs) { return original_type; } else if (name.string == "bounds_check") { Ast *operand = parse_expr(f, lhs); + if (operand == nullptr) { + syntax_error(token, "Invalid expresssion for #%.*s", LIT(name.string)); + return nullptr; + } operand->state_flags |= StateFlag_bounds_check; if ((operand->state_flags & StateFlag_no_bounds_check) != 0) { syntax_error(token, "#bounds_check and #no_bounds_check cannot be applied together"); @@ -2053,6 +2050,10 @@ Ast *parse_operand(AstFile *f, bool lhs) { return operand; } else if (name.string == "no_bounds_check") { Ast *operand = parse_expr(f, lhs); + if (operand == nullptr) { + syntax_error(token, "Invalid expresssion for #%.*s", LIT(name.string)); + return nullptr; + } operand->state_flags |= StateFlag_no_bounds_check; if ((operand->state_flags & StateFlag_bounds_check) != 0) { syntax_error(token, "#bounds_check and #no_bounds_check cannot be applied together"); -- cgit v1.2.3 From 083e9e20536fa5438886d8a9756b2a87448a21d7 Mon Sep 17 00:00:00 2001 From: Mikkel Hjortshøj Date: Thu, 13 May 2021 01:25:37 +0200 Subject: Update feature_request.md --- .github/ISSUE_TEMPLATE/feature_request.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index df81be2be..263161b46 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -7,6 +7,8 @@ assignees: '' --- +# PLEASE POST THIS IN THE DISCUSSION TAB UNDER "PROPOSALS" + **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -- cgit v1.2.3 From d5c3f996550b75f0da383bfcf7b30dba482cf081 Mon Sep 17 00:00:00 2001 From: Mikkel Hjortshøj Date: Thu, 13 May 2021 01:26:15 +0200 Subject: Update feature_request.md --- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 263161b46..33d7e7a71 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -7,7 +7,7 @@ assignees: '' --- -# PLEASE POST THIS IN THE DISCUSSION TAB UNDER "PROPOSALS" +# PLEASE POST THIS IN THE DISCUSSION TAB UNDER "PROPOSALS" OR "IDEAS/REQUESTS" **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -- cgit v1.2.3 From 2e5f57d8a18352c02da72f2f2385116a099d7942 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 13 May 2021 00:44:33 +0100 Subject: Fix #741 --- src/check_expr.cpp | 4 +++- src/check_type.cpp | 25 +++++++++++-------------- src/llvm_backend.cpp | 5 +++-- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index ddf2a3aed..5784744d9 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -621,7 +621,9 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type } PolyProcData poly_proc_data = {}; if (check_polymorphic_procedure_assignment(c, operand, type, operand->expr, &poly_proc_data)) { - add_entity_use(c, operand->expr, poly_proc_data.gen_entity); + Entity *e = poly_proc_data.gen_entity; + add_type_and_value(c->info, operand->expr, Addressing_Value, e->type, {}); + add_entity_use(c, operand->expr, e); return 4; } } diff --git a/src/check_type.cpp b/src/check_type.cpp index e433faf7f..ef0e20948 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1207,6 +1207,11 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type * init_core_source_code_location(ctx->checker); param_value.kind = ParameterValue_Location; o.type = t_source_code_location; + + if (in_type) { + check_assignment(ctx, &o, in_type, str_lit("parameter value")); + } + } else { if (in_type) { check_expr_with_type_hint(ctx, &o, expr, in_type); @@ -1214,6 +1219,11 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type * check_expr(ctx, &o, expr); } + if (in_type) { + check_assignment(ctx, &o, in_type, str_lit("parameter value")); + } + + if (is_operand_nil(o)) { param_value.kind = ParameterValue_Nil; } else if (o.mode != Addressing_Constant) { @@ -1221,16 +1231,7 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type * param_value.kind = ParameterValue_Constant; param_value.value = exact_value_procedure(expr); } else { - Entity *e = nullptr; - // if (o.mode == Addressing_Value && is_type_proc(o.type)) { - if (o.mode == Addressing_Value || o.mode == Addressing_Variable) { - Operand x = {}; - if (expr->kind == Ast_Ident) { - e = check_ident(ctx, &x, expr, nullptr, nullptr, false); - } else if (expr->kind == Ast_SelectorExpr) { - e = check_selector(ctx, &x, expr, nullptr); - } - } + Entity *e = entity_from_expr(o.expr); if (e != nullptr) { if (e->kind == Entity_Procedure) { @@ -1267,10 +1268,6 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type * } } - if (in_type) { - check_assignment(ctx, &o, in_type, str_lit("parameter value")); - } - if (out_type_) { if (in_type != nullptr) { *out_type_ = in_type; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 81ccf1659..8a9e5d7e2 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -6023,7 +6023,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc res.value = llvm_const_array(et, elems, cast(unsigned)count); return res; } - GB_PANIC("HERE!\n"); + GB_PANIC("This should not have happened!\n"); LLVMValueRef data = LLVMConstStringInContext(ctx, cast(char const *)value.value_string.text, @@ -9715,7 +9715,8 @@ lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterVal switch (param_value.kind) { case ParameterValue_Constant: if (is_type_constant_type(parameter_type)) { - return lb_const_value(p->module, parameter_type, param_value.value); + auto res = lb_const_value(p->module, parameter_type, param_value.value); + return res; } else { ExactValue ev = param_value.value; lbValue arg = {}; -- cgit v1.2.3 From be12f12c3c2872fd89021af16c2ab838277c4c4a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 13 May 2021 00:57:10 +0100 Subject: Fix #814 --- src/check_stmt.cpp | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index a3c9a529c..9ca53c4fc 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -960,9 +960,44 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { Operand b1 = rhs; check_comparison(ctx, &a1, &b1, Token_LtEq); - add_constant_switch_case(ctx, &seen, lhs); - if (upper_op == Token_GtEq) { - add_constant_switch_case(ctx, &seen, rhs); + if (is_type_enum(x.type)) { + // TODO(bill): Fix this logic so it's fast!!! + + i64 v0 = exact_value_to_i64(lhs.value); + i64 v1 = exact_value_to_i64(rhs.value); + Operand v = {}; + v.mode = Addressing_Constant; + v.type = x.type; + v.expr = x.expr; + + Type *bt = base_type(x.type); + GB_ASSERT(bt->kind == Type_Enum); + for (i64 vi = v0; vi <= v1; vi++) { + if (upper_op != Token_GtEq && vi == v1) { + break; + } + + bool found = false; + for_array(j, bt->Enum.fields) { + Entity *f = bt->Enum.fields[j]; + GB_ASSERT(f->kind == Entity_Constant); + + i64 fv = exact_value_to_i64(f->Constant.value); + if (fv == vi) { + found = true; + break; + } + } + if (found) { + v.value = exact_value_i64(vi); + add_constant_switch_case(ctx, &seen, v); + } + } + } else { + add_constant_switch_case(ctx, &seen, lhs); + if (upper_op == Token_GtEq) { + add_constant_switch_case(ctx, &seen, rhs); + } } if (is_type_string(x.type)) { -- cgit v1.2.3 From d4ee1a9e1924778e69214dd24164ef11f3953ac9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 13 May 2021 10:07:28 +0100 Subject: Correct default procedure parameter logic --- src/check_type.cpp | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/check_type.cpp b/src/check_type.cpp index ef0e20948..0e77792b7 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1254,8 +1254,11 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type * } else if (allow_caller_location && o.mode == Addressing_Context) { param_value.kind = ParameterValue_Value; param_value.ast_value = expr; + } else if (o.value.kind != ExactValue_Invalid) { + param_value.kind = ParameterValue_Constant; + param_value.value = o.value; } else { - error(expr, "Default parameter must be a constant"); + error(expr, "Default parameter must be a constant, %d", o.mode); } } } else { @@ -1414,10 +1417,19 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is type = t_invalid; } - if (param_value.kind != ParameterValue_Invalid && is_type_polymorphic(type)) { - gbString str = type_to_string(type); - error(params[i], "A default value for a parameter must not be a polymorphic constant type, got %s", str); - gb_string_free(str); + if (is_type_polymorphic(type)) { + switch (param_value.kind) { + case ParameterValue_Invalid: + case ParameterValue_Constant: + case ParameterValue_Nil: + break; + case ParameterValue_Location: + case ParameterValue_Value: + gbString str = type_to_string(type); + error(params[i], "A default value for a parameter must not be a polymorphic constant type, got %s", str); + gb_string_free(str); + break; + } } -- cgit v1.2.3 From b37d344eb214336036c597250206f36043c75975 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 13 May 2021 12:04:51 +0100 Subject: Add intrinsics.type_is_variant_of --- core/intrinsics/intrinsics.odin | 1 + src/check_builtin.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ src/checker_builtin_procs.hpp | 4 ++++ 3 files changed, 45 insertions(+) diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index d767f5e47..60b595aab 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -159,6 +159,7 @@ type_is_simd_vector :: proc($T: typeid) -> bool --- type_has_nil :: proc($T: typeid) -> bool --- type_is_specialization_of :: proc($T, $S: typeid) -> bool --- +type_is_variant_of :: proc($U, $V: typeid) -> bool where type_is_union(U) --- type_has_field :: proc($T: typeid, $name: string) -> bool --- diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 6b299a68a..8932ac914 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -2429,6 +2429,46 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } break; + case BuiltinProc_type_is_variant_of: + { + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + operand->mode = Addressing_Invalid; + operand->type = t_invalid; + return false; + } + + + Type *u = operand->type; + + if (!is_type_union(u)) { + error(operand->expr, "Expected a union type for '%.*s'", LIT(builtin_name)); + operand->mode = Addressing_Invalid; + operand->type = t_invalid; + return false; + } + + Type *v = check_type(c, ce->args[1]); + + u = base_type(u); + GB_ASSERT(u->kind == Type_Union); + + bool is_variant = false; + + for_array(i, u->Union.variants) { + Type *vt = u->Union.variants[i]; + if (are_types_identical(v, vt)) { + is_variant = true; + break; + } + } + + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + operand->value = exact_value_bool(is_variant); + } + break; + case BuiltinProc_type_struct_field_count: operand->value = exact_value_i64(0); if (operand->mode != Addressing_Type) { diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 98ef5180b..5b1aecd68 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -197,6 +197,8 @@ BuiltinProc__type_simple_boolean_end, BuiltinProc_type_is_specialization_of, + BuiltinProc_type_is_variant_of, + BuiltinProc_type_struct_field_count, BuiltinProc_type_proc_parameter_count, @@ -415,6 +417,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_is_specialization_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_variant_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_struct_field_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_proc_parameter_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, -- cgit v1.2.3 From 465b6139d55441fbe6da8d2699865c414cd87d3a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 13 May 2021 12:05:23 +0100 Subject: Temporarily fix syscall in Linux and Freebsd (eventually to be replaced with a proper implementation) --- core/os/os_freebsd.odin | 2 +- core/os/os_linux.odin | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/os/os_freebsd.odin b/core/os/os_freebsd.odin index 137c6f864..2afa8bd14 100644 --- a/core/os/os_freebsd.odin +++ b/core/os/os_freebsd.odin @@ -10,7 +10,7 @@ import "core:c" Handle :: distinct i32; File_Time :: distinct u64; Errno :: distinct i32; -Syscall :: distinct int; +Syscall :: distinct i32; INVALID_HANDLE :: ~Handle(0); diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index dd0914f40..7569909d7 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -11,7 +11,7 @@ import "core:strconv" Handle :: distinct i32; File_Time :: distinct u64; Errno :: distinct i32; -Syscall :: distinct int; +Syscall :: distinct i32; INVALID_HANDLE :: ~Handle(0); @@ -269,7 +269,7 @@ SYS_GETTID: Syscall : 186; foreign libc { @(link_name="__errno_location") __errno_location :: proc() -> ^int ---; - @(link_name="syscall") syscall :: proc(number: Syscall, #c_vararg args: ..any) -> int ---; + @(link_name="syscall") syscall :: proc(number: Syscall, #c_vararg args: ..any) -> i32 ---; @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---; @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---; @@ -595,7 +595,7 @@ exit :: proc "contextless" (code: int) -> ! { } current_thread_id :: proc "contextless" () -> int { - return syscall(SYS_GETTID); + return cast(int)syscall(SYS_GETTID); } dlopen :: proc(filename: string, flags: int) -> rawptr { -- cgit v1.2.3 From b8a35c658cf30a3f4f46ec40b38cdf478224a371 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 13 May 2021 12:06:35 +0100 Subject: Remove incl/excl usage from demo --- examples/demo/demo.odin | 10 +++++----- examples/demo_insert_semicolon/demo.odin | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 6817b15a4..76e8aa9fc 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -1352,8 +1352,8 @@ bit_set_type :: proc() { d: Days; d = {Sunday, Monday}; - e := d | WEEKEND; - e |= {Monday}; + e := d + WEEKEND; + e += {Monday}; fmt.println(d, e); ok := Saturday in e; // `in` is only allowed for `map` and `bit_set` types @@ -1372,12 +1372,12 @@ bit_set_type :: proc() { fmt.println(typeid_of(type_of(x))); // bit_set[A..Z] fmt.println(typeid_of(type_of(y))); // bit_set[0..8; u16] - incl(&x, 'F'); + x += {'F'}; assert('F' in x); - excl(&x, 'F'); + x -= {'F'}; assert('F' not_in x); - y |= {1, 4, 2}; + y += {1, 4, 2}; assert(2 in y); } { diff --git a/examples/demo_insert_semicolon/demo.odin b/examples/demo_insert_semicolon/demo.odin index ae677e5a9..3fbae274e 100644 --- a/examples/demo_insert_semicolon/demo.odin +++ b/examples/demo_insert_semicolon/demo.odin @@ -1347,8 +1347,8 @@ bit_set_type :: proc() { d: Days d = {Sunday, Monday} - e := d | WEEKEND - e |= {Monday} + e := d + WEEKEND + e += {Monday} fmt.println(d, e) ok := Saturday in e // `in` is only allowed for `map` and `bit_set` types @@ -1367,12 +1367,12 @@ bit_set_type :: proc() { fmt.println(typeid_of(type_of(x))) // bit_set[A..Z] fmt.println(typeid_of(type_of(y))) // bit_set[0..8; u16] - incl(&x, 'F') + x += {'F'}; assert('F' in x) - excl(&x, 'F') + x -= {'F'}; assert('F' not_in x) - y |= {1, 4, 2} + y += {1, 4, 2} assert(2 in y) } { -- cgit v1.2.3 From 63b54ce7c695fb1e204fe77db029cdebf7206b28 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 13 May 2021 12:48:12 +0100 Subject: Add minor ignoring hint on type assertions to get better code generation with no optimizations enabled --- src/check_expr.cpp | 20 +++++++++++++++++++ src/llvm_backend.cpp | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/parser.hpp | 8 +++++++- 3 files changed, 80 insertions(+), 3 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 5784744d9..51dad8f79 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3949,6 +3949,16 @@ bool check_assignment_arguments(CheckerContext *ctx, Array const &lhs, add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value); } + if (o.mode == Addressing_OptionalOk && expr->kind == Ast_TypeAssertion) { + // NOTE(bill): Used only for optimizations in the backend + if (is_blank_ident(lhs[0].expr)) { + expr->TypeAssertion.ignores[0] = true; + } + if (is_blank_ident(lhs[1].expr)) { + expr->TypeAssertion.ignores[1] = true; + } + } + array_add(operands, val0); array_add(operands, val1); optional_ok = true; @@ -4063,6 +4073,16 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value); } + if (o.mode == Addressing_OptionalOk && expr->kind == Ast_TypeAssertion) { + // NOTE(bill): Used only for optimizations in the backend + if (is_blank_ident(lhs[0]->token)) { + expr->TypeAssertion.ignores[0] = true; + } + if (is_blank_ident(lhs[1]->token)) { + expr->TypeAssertion.ignores[1] = true; + } + } + array_add(operands, val0); array_add(operands, val1); optional_ok = true; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 8a9e5d7e2..dc36100a4 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -11119,7 +11119,54 @@ lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, A return value; } -lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos pos, bool do_conversion_check=true) { +lbValue lb_emit_union_cast_only_ok_check(lbProcedure *p, lbValue value, Type *type, TokenPos pos) { + GB_ASSERT(is_type_tuple(type)); + lbModule *m = p->module; + + Type *src_type = value.type; + bool is_ptr = is_type_pointer(src_type); + + + // IMPORTANT NOTE(bill): This assumes that the value is completely ignored + // so when it does an assignment, it complete ignores the value. + // Just make it two booleans and ignore the first one + // + // _, ok := x.(T); + // + Type *ok_type = type->Tuple.variables[1]->type; + Type *gen_tuple_types[2] = {}; + gen_tuple_types[0] = ok_type; + gen_tuple_types[1] = ok_type; + + Type *gen_tuple = alloc_type_tuple_from_field_types(gen_tuple_types, gb_count_of(gen_tuple_types), false, true); + + lbAddr v = lb_add_local_generated(p, gen_tuple, false); + + if (is_ptr) { + value = lb_emit_load(p, value); + } + Type *src = base_type(type_deref(src_type)); + GB_ASSERT_MSG(is_type_union(src), "%s", type_to_string(src_type)); + Type *dst = type->Tuple.variables[0]->type; + + lbValue cond = {}; + + if (is_type_union_maybe_pointer(src)) { + lbValue data = lb_emit_transmute(p, value, dst); + cond = lb_emit_comp_against_nil(p, Token_NotEq, data); + } else { + lbValue tag = lb_emit_union_tag_value(p, value); + lbValue dst_tag = lb_const_union_tag(m, src, dst); + cond = lb_emit_comp(p, Token_CmpEq, tag, dst_tag); + } + + lbValue gep1 = lb_emit_struct_ep(p, v.addr, 1); + lb_emit_store(p, gep1, cond); + + return lb_addr_load(p, v); +} + +lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos pos) { lbModule *m = p->module; Type *src_type = value.type; @@ -11183,7 +11230,7 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p lb_start_block(p, end_block); if (!is_tuple) { - if (do_conversion_check) { + { // NOTE(bill): Panic on invalid conversion Type *dst_type = tuple->Tuple.variables[0]->type; @@ -11487,6 +11534,10 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { lbValue e = lb_build_expr(p, ta->expr); Type *t = type_deref(e.type); if (is_type_union(t)) { + if (ta->ignores[0]) { + // NOTE(bill): This is not needed for optimization levels other than 0 + return lb_emit_union_cast_only_ok_check(p, e, type, pos); + } return lb_emit_union_cast(p, e, type, pos); } else if (is_type_any(t)) { return lb_emit_any_cast(p, e, type, pos); diff --git a/src/parser.hpp b/src/parser.hpp index 982588c0f..b6bf42f63 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -345,7 +345,13 @@ AST_KIND(_ExprBegin, "", bool) \ AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \ AST_KIND(TernaryIfExpr, "ternary if expression", struct { Ast *x, *cond, *y; }) \ AST_KIND(TernaryWhenExpr, "ternary when expression", struct { Ast *x, *cond, *y; }) \ - AST_KIND(TypeAssertion, "type assertion", struct { Ast *expr; Token dot; Ast *type; Type *type_hint; }) \ + AST_KIND(TypeAssertion, "type assertion", struct { \ + Ast *expr; \ + Token dot; \ + Ast *type; \ + Type *type_hint; \ + bool ignores[2]; \ + }) \ AST_KIND(TypeCast, "type cast", struct { Token token; Ast *type, *expr; }) \ AST_KIND(AutoCast, "auto_cast", struct { Token token; Ast *expr; }) \ AST_KIND(InlineAsmExpr, "inline asm expression", struct { \ -- cgit v1.2.3 From b01c2e101712cf58e60bfc41d85fc33b60fcf349 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 May 2021 16:40:40 +0100 Subject: Disallow slicing of constant values --- src/check_expr.cpp | 13 ++++++++++--- src/llvm_backend.cpp | 19 ++++++++++++++++++- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 51dad8f79..18f210b63 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7811,10 +7811,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type return kind; } - o->mode = Addressing_Value; - if (se->low == nullptr && se->high != nullptr) { - // error(se->interval0, "1st index is required if a 2nd index is specified"); // It is okay to continue as it will assume the 1st index is zero } @@ -7849,6 +7846,16 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type } } + if (max_count < 0) { + if (o->mode == Addressing_Constant) { + gbString s = expr_to_string(se->expr); + error(se->expr, "Cannot slice constant value '%s'", s); + gb_string_free(s); + } + } + + o->mode = Addressing_Value; + if (is_type_string(t) && max_count >= 0) { bool all_constant = true; for (isize i = 0; i < gb_count_of(nodes); i++) { diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index dc36100a4..66c45793b 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -11360,6 +11360,15 @@ lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *expr) { return {}; } +bool lb_is_expr_constant_zero(Ast *expr) { + GB_ASSERT(expr != nullptr); + auto v = exact_value_to_integer(expr->tav.value); + if (v.kind == ExactValue_Integer) { + return big_int_cmp_zero(&v.value_integer) == 0; + } + return false; +} + lbValue lb_build_expr(lbProcedure *p, Ast *expr) { lbModule *m = p->module; @@ -11683,6 +11692,13 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { case_end; case_ast_node(se, SliceExpr, expr); + if (is_type_slice(type_of_expr(se->expr))) { + // NOTE(bill): Quick optimization + if (se->high == nullptr && + (se->low == nullptr || lb_is_expr_constant_zero(se->low))) { + return lb_build_expr(p, se->expr); + } + } return lb_addr_load(p, lb_build_addr(p, expr)); case_end; @@ -12303,6 +12319,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { case_end; case_ast_node(se, SliceExpr, expr); + lbValue low = lb_const_int(p->module, t_int, 0); lbValue high = {}; @@ -13268,7 +13285,7 @@ lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value) { g.type = alloc_type_pointer(type); g.value = LLVMAddGlobal(m->mod, lb_type(m, type), cast(char const *)str); if (value.value != nullptr) { - GB_ASSERT(LLVMIsConstant(value.value)); + GB_ASSERT_MSG(LLVMIsConstant(value.value), LLVMPrintValueToString(value.value)); LLVMSetInitializer(g.value, value.value); } else { LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, type))); -- cgit v1.2.3 From f7b1290fe930099a1250dbde64cc1c081fbd4828 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 May 2021 18:02:06 +0100 Subject: Change `for i in x..y {}` behaviour Adds an extra check before incrementation to prevent the possibility of overflowing of `y` is at the limit maximum size of the integer e.g. `for i in u8(0)..255 {}` (assuming `255` is not known at compile time) --- src/llvm_backend.cpp | 196 +++++++++++++++++++++++---------------------------- 1 file changed, 89 insertions(+), 107 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 66c45793b..8d19f6ba7 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2971,72 +2971,6 @@ Type *struct_type_from_systemv_distribute_struct_fields(Type *abi_type) { } -lbValue lb_add_param(lbProcedure *p, Entity *e, Ast *expr, Type *abi_type, i32 index) { - lbParamPasskind kind = lbParamPass_Value; - lbValue v = lb_value_param(p, e, abi_type, index, &kind); - array_add(&p->params, v); - - lbValue res = {}; - - switch (kind) { - case lbParamPass_Value: { - lbAddr l = lb_add_local(p, e->type, e, false, index); - lbValue x = v; - if (abi_type == t_llvm_bool) { - x = lb_emit_conv(p, x, t_bool); - } - lb_addr_store(p, l, x); - return x; - } - case lbParamPass_Pointer: - lb_add_entity(p->module, e, v); - return lb_emit_load(p, v); - - case lbParamPass_Integer: { - lbAddr l = lb_add_local(p, e->type, e, false, index); - lbValue iptr = lb_emit_conv(p, l.addr, alloc_type_pointer(abi_type)); - lb_emit_store(p, iptr, v); - return lb_addr_load(p, l); - } - - case lbParamPass_ConstRef: - lb_add_entity(p->module, e, v); - return lb_emit_load(p, v); - - case lbParamPass_BitCast: { - lbAddr l = lb_add_local(p, e->type, e, false, index); - lbValue x = lb_emit_transmute(p, v, e->type); - lb_addr_store(p, l, x); - return x; - } - case lbParamPass_Tuple: { - lbAddr l = lb_add_local(p, e->type, e, true, index); - Type *st = struct_type_from_systemv_distribute_struct_fields(abi_type); - lbValue ptr = lb_emit_transmute(p, l.addr, alloc_type_pointer(st)); - if (abi_type->Tuple.variables.count > 0) { - array_pop(&p->params); - } - for_array(i, abi_type->Tuple.variables) { - Type *t = abi_type->Tuple.variables[i]->type; - GB_ASSERT(!is_type_tuple(t)); - - lbParamPasskind elem_kind = lbParamPass_Value; - lbValue elem = lb_value_param(p, nullptr, t, index+cast(i32)i, &elem_kind); - array_add(&p->params, elem); - - lbValue dst = lb_emit_struct_ep(p, ptr, cast(i32)i); - lb_emit_store(p, dst, elem); - } - return lb_addr_load(p, l); - } - - } - - - GB_PANIC("Unreachable"); - return {}; -} - void lb_start_block(lbProcedure *p, lbBlock *b) { GB_ASSERT(b != nullptr); if (!b->appended) { @@ -4072,63 +4006,104 @@ void lb_build_range_string(lbProcedure *p, lbValue expr, Type *val_type, } -void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node, Type *val_type, - lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_) { +void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node, + AstRangeStmt *rs, Scope *scope) { + bool ADD_EXTRA_WRAPPING_CHECK = true; + lbModule *m = p->module; - // TODO(bill): How should the behaviour work for lower and upper bounds checking for iteration? - // If 'lower' is changed, should 'val' do so or is that not typical behaviour? + lb_open_scope(p, scope); - lbValue lower = lb_build_expr(p, node->left); - lbValue upper = {}; + Type *val0_type = nullptr; + Type *val1_type = nullptr; + if (rs->vals.count > 0 && rs->vals[0] != nullptr && !is_blank_ident(rs->vals[0])) { + val0_type = type_of_expr(rs->vals[0]); + } + if (rs->vals.count > 1 && rs->vals[1] != nullptr && !is_blank_ident(rs->vals[1])) { + val1_type = type_of_expr(rs->vals[1]); + } - lbValue val = {}; - lbValue idx = {}; - lbBlock *loop = nullptr; - lbBlock *done = nullptr; - lbBlock *body = nullptr; + if (val0_type != nullptr) { + Entity *e = entity_of_node(rs->vals[0]); + lb_add_local(p, e->type, e, true); + } + if (val1_type != nullptr) { + Entity *e = entity_of_node(rs->vals[1]); + lb_add_local(p, e->type, e, true); + } - if (val_type == nullptr) { - val_type = lower.type; + TokenKind op = Token_Lt; + switch (node->op.kind) { + case Token_Ellipsis: op = Token_LtEq; break; + case Token_RangeHalf: op = Token_Lt; break; + default: GB_PANIC("Invalid interval operator"); break; } - lbAddr value = lb_add_local_generated(p, val_type, false); + + lbValue lower = lb_build_expr(p, node->left); + lbValue upper = {}; // initialized each time in the loop + + lbAddr value = lb_add_local_generated(p, val0_type ? val0_type : lower.type, false); lb_addr_store(p, value, lower); lbAddr index = lb_add_local_generated(p, t_int, false); lb_addr_store(p, index, lb_const_int(m, t_int, 0)); - loop = lb_create_block(p, "for.interval.loop"); + lbBlock *loop = lb_create_block(p, "for.interval.loop"); + lbBlock *body = lb_create_block(p, "for.interval.body"); + lbBlock *done = lb_create_block(p, "for.interval.done"); + lb_emit_jump(p, loop); lb_start_block(p, loop); - body = lb_create_block(p, "for.interval.body"); - done = lb_create_block(p, "for.interval.done"); - - - TokenKind op = Token_Lt; - switch (node->op.kind) { - case Token_Ellipsis: op = Token_LtEq; break; - case Token_RangeHalf: op = Token_Lt; break; - default: GB_PANIC("Invalid interval operator"); break; - } - upper = lb_build_expr(p, node->right); - lbValue curr_value = lb_addr_load(p, value); lbValue cond = lb_emit_comp(p, op, curr_value, upper); lb_emit_if(p, cond, body, done); lb_start_block(p, body); - val = lb_addr_load(p, value); - idx = lb_addr_load(p, index); + lbValue val = lb_addr_load(p, value); + lbValue idx = lb_addr_load(p, index); + if (val0_type) lb_store_range_stmt_val(p, rs->vals[0], val); + if (val1_type) lb_store_range_stmt_val(p, rs->vals[1], idx); - lb_emit_increment(p, value.addr); - lb_emit_increment(p, index.addr); + { + // NOTE: this check block will most likely be optimized out, and is here + // to make this code easier to read + lbBlock *check = nullptr; + lbBlock *post = lb_create_block(p, "for.interval.post"); - if (val_) *val_ = val; - if (idx_) *idx_ = idx; - if (loop_) *loop_ = loop; - if (done_) *done_ = done; + lbBlock *continue_block = post; + + if (ADD_EXTRA_WRAPPING_CHECK && + op == Token_LtEq) { + check = lb_create_block(p, "for.interval.check"); + continue_block = check; + } + + lb_push_target_list(p, rs->label, done, continue_block, nullptr); + + lb_build_stmt(p, rs->body); + + lb_close_scope(p, lbDeferExit_Default, nullptr); + lb_pop_target_list(p); + + if (check != nullptr) { + lb_emit_jump(p, check); + lb_start_block(p, check); + + lbValue check_cond = lb_emit_comp(p, Token_NotEq, curr_value, upper); + lb_emit_if(p, check_cond, post, done); + } else { + lb_emit_jump(p, post); + } + + lb_start_block(p, post); + lb_emit_increment(p, value.addr); + lb_emit_increment(p, index.addr); + lb_emit_jump(p, loop); + } + + lb_start_block(p, done); } void lb_build_range_enum(lbProcedure *p, Type *enum_type, Type *val_type, lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_) { @@ -4283,6 +4258,11 @@ void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs, Scope *sco void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *scope) { Ast *expr = unparen_expr(rs->expr); + if (is_ast_range(expr)) { + lb_build_range_interval(p, &expr->BinaryExpr, rs, scope); + return; + } + Type *expr_type = type_of_expr(expr); if (expr_type != nullptr) { Type *et = base_type(type_deref(expr_type)); @@ -4319,10 +4299,7 @@ void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *scope) { bool is_map = false; TypeAndValue tav = type_and_value_of_expr(expr); - - if (is_ast_range(expr)) { - lb_build_range_interval(p, &expr->BinaryExpr, val0_type, &val, &key, &loop, &done); - } else if (tav.mode == Addressing_Type) { + if (tav.mode == Addressing_Type) { lb_build_range_enum(p, type_deref(tav.type), val0_type, &val, &key, &loop, &done); } else { Type *expr_type = type_of_expr(expr); @@ -7824,10 +7801,13 @@ lbContextData *lb_push_context_onto_stack_from_implicit_parameter(lbProcedure *p GB_ASSERT(pt->kind == Type_Proc); GB_ASSERT(pt->Proc.calling_convention == ProcCC_Odin); - Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("__.context_ptr")), t_context_ptr, false, false); + String name = str_lit("__.context_ptr"); + + Entity *e = alloc_entity_param(nullptr, make_token_ident(name), t_context_ptr, false, false); e->flags |= EntityFlag_NoAlias; LLVMValueRef context_ptr = LLVMGetParam(p->value, LLVMCountParams(p->value)-1); + LLVMSetValueName2(context_ptr, cast(char const *)name.text, name.len); context_ptr = LLVMBuildPointerCast(p->builder, context_ptr, lb_type(p->module, e->type), ""); lbValue param = {context_ptr, e->type}; @@ -14241,8 +14221,10 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) { // initialize `runtime.args__` lbValue argc = {LLVMGetParam(p->value, 0), t_i32}; - argc = lb_emit_conv(p, argc, t_int); lbValue argv = {LLVMGetParam(p->value, 1), t_ptr_cstring}; + LLVMSetValueName2(argc.value, "argc", 4); + LLVMSetValueName2(argv.value, "argv", 4); + argc = lb_emit_conv(p, argc, t_int); lbAddr args = lb_addr(lb_find_runtime_value(p->module, str_lit("args__"))); lb_fill_slice(p, args, argv, argc); } -- cgit v1.2.3 From 0507b9ebb78e1239501a3153315bd4fd195a88a2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 May 2021 18:40:06 +0100 Subject: Fix #987 --- src/check_expr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 18f210b63..8cc5aa894 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1132,7 +1132,7 @@ Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Typ Entity *e = scope_lookup(c->scope, name); if (e == nullptr) { if (is_blank_ident(name)) { - error(n, "'_' cannot be used as a value type"); + error(n, "'_' cannot be used as a value"); } else { error(n, "Undeclared name: %.*s", LIT(name)); } -- cgit v1.2.3 From 5ae564cc8c6934675c25cb251e753103e4549c52 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 May 2021 18:53:52 +0100 Subject: Add name to aggregate result pointer to procedures --- src/llvm_backend.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 8d19f6ba7..ce633b127 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -3184,11 +3184,15 @@ void lb_begin_procedure_body(lbProcedure *p) { lbValue return_ptr_value = {}; if (ft->ret.kind == lbArg_Indirect) { // NOTE(bill): this must be parameter 0 + + String name = str_lit("agg.result"); + Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(p->type->Proc.results)); - Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("agg.result")), ptr_type, false, false); + Entity *e = alloc_entity_param(nullptr, make_token_ident(name), ptr_type, false, false); e->flags |= EntityFlag_Sret | EntityFlag_NoAlias; return_ptr_value.value = LLVMGetParam(p->value, 0); + LLVMSetValueName2(return_ptr_value.value, cast(char const *)name.text, name.len); return_ptr_value.type = ptr_type; p->return_ptr = lb_addr(return_ptr_value); -- cgit v1.2.3 From 7b7081d60733caa996a89be2651482a2aeed8bbd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 May 2021 18:59:54 +0100 Subject: Remove old dead code --- src/check_decl.cpp | 11 --- src/check_expr.cpp | 72 ------------------- src/check_stmt.cpp | 47 ------------ src/check_type.cpp | 13 ---- src/checker.cpp | 200 --------------------------------------------------- src/checker.hpp | 1 - src/llvm_backend.cpp | 51 ------------- src/parser.hpp | 1 - src/types.cpp | 19 ----- 9 files changed, 415 deletions(-) diff --git a/src/check_decl.cpp b/src/check_decl.cpp index baabe4184..5e8e79791 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -289,17 +289,6 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def) if (decl != nullptr) { AttributeContext ac = {}; check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac); - if (ac.atom_op_table != nullptr) { - Type *bt = base_type(e->type); - switch (bt->kind) { - case Type_Struct: - bt->Struct.atom_op_table = ac.atom_op_table; - break; - default: - error(e->token, "Only struct types can have custom atom operations"); - break; - } - } } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 8cc5aa894..fb3a51415 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7573,47 +7573,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type return Expr_Expr; } - if (t->kind == Type_Struct) { - TypeAtomOpTable *atom_op_table = t->Struct.atom_op_table; - if (atom_op_table != nullptr) { - if (atom_op_table->op[TypeAtomOp_index_set]) { - if (c->assignment_lhs_hint == node) { - o->mode = Addressing_AtomOpAssign; - o->type = o->type; - o->expr = node; - return kind; - } - } - if (atom_op_table->op[TypeAtomOp_index_get]) { - Entity *e = atom_op_table->op[TypeAtomOp_index_get]; - if (ie->index == nullptr) { - gbString str = expr_to_string(o->expr); - error(o->expr, "Missing index for '%s'", str); - gb_string_free(str); - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } - - GB_ASSERT(e->identifier != nullptr); - Ast *proc_ident = clone_ast(e->identifier); - - auto args = array_make(heap_allocator(), 2); - args[0] = ie->expr; - args[1] = ie->index; - - GB_ASSERT(c->file != nullptr); - Ast *fake_call = ast_call_expr(c->file, proc_ident, args, ie->open, ie->close, {}); - check_expr_base(c, o, fake_call, type_hint); - AtomOpMapEntry entry = {TypeAtomOp_index_get, fake_call}; - map_set(&c->info->atom_op_map, hash_pointer(node), entry); - o->expr = node; - return kind; - } - } - } - - i64 max_count = -1; bool valid = check_set_index_data(o, t, is_ptr, &max_count, o->type); @@ -7752,37 +7711,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type if (is_type_soa_struct(t)) { valid = true; o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem); - } else { - TypeAtomOpTable *atom_op_table = t->Struct.atom_op_table; - if (atom_op_table != nullptr && atom_op_table->op[TypeAtomOp_slice]) { - Entity *e = atom_op_table->op[TypeAtomOp_slice]; - GB_ASSERT(e->identifier != nullptr); - Ast *proc_ident = clone_ast(e->identifier); - - Ast *expr = se->expr; - if (o->mode == Addressing_Variable) { - expr = ast_unary_expr(c->file, {Token_And, STR_LIT("&")}, expr); - } else if (is_type_pointer(o->type)) { - // Okay - } else { - gbString str = expr_to_string(node); - error(node, "Cannot slice '%s', value is not addressable", str); - gb_string_free(str); - o->mode = Addressing_Invalid; - o->expr = node; - return kind; - } - auto args = array_make(heap_allocator(), 1); - args[0] = expr; - - - GB_ASSERT(c->file != nullptr); - Ast *fake_call = ast_call_expr(c->file, proc_ident, args, se->open, se->close, {}); - check_expr_base(c, o, fake_call, type_hint); - AtomOpMapEntry entry = {TypeAtomOp_slice, fake_call}; - map_set(&c->info->atom_op_map, hash_pointer(node), entry); - valid = true; - } } break; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 9ca53c4fc..a954b44b6 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1489,53 +1489,6 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { auto lhs_to_ignore = array_make(temporary_allocator(), lhs_count); isize max = gb_min(lhs_count, rhs_count); - // NOTE(bill, 2020-05-02): This is an utter hack to get these custom atom operations working - // correctly for assignments - for (isize i = 0; i < max; i++) { - if (lhs_operands[i].mode == Addressing_AtomOpAssign) { - Operand lhs = lhs_operands[i]; - - Type *t = base_type(lhs.type); - GB_ASSERT(t->kind == Type_Struct); - ast_node(ie, IndexExpr, unparen_expr(lhs.expr)); - - TypeAtomOpTable *atom_op_table = t->Struct.atom_op_table; - GB_ASSERT(atom_op_table->op[TypeAtomOp_index_set] != nullptr); - Entity *e = atom_op_table->op[TypeAtomOp_index_set]; - - GB_ASSERT(e->identifier != nullptr); - Ast *proc_ident = clone_ast(e->identifier); - GB_ASSERT(ctx->file != nullptr); - - - TypeAndValue tv = type_and_value_of_expr(ie->expr); - Ast *expr = ie->expr; - if (is_type_pointer(tv.type)) { - // Okay - } else if (tv.mode == Addressing_Variable) { - // NOTE(bill): Hack it to take the address instead - expr = ast_unary_expr(ctx->file, {Token_And, STR_LIT("&")}, ie->expr); - } else { - continue; - } - - auto args = array_make(heap_allocator(), 3); - args[0] = expr; - args[1] = ie->index; - args[2] = rhs_operands[i].expr; - - Ast *fake_call = ast_call_expr(ctx->file, proc_ident, args, ie->open, ie->close, {}); - Operand fake_operand = {}; - fake_operand.expr = lhs.expr; - check_expr_base(ctx, &fake_operand, fake_call, nullptr); - AtomOpMapEntry entry = {TypeAtomOp_index_set, fake_call}; - map_set(&ctx->info->atom_op_map, hash_pointer(lhs.expr), entry); - - lhs_to_ignore[i] = true; - - } - } - for (isize i = 0; i < max; i++) { if (lhs_to_ignore[i]) { continue; diff --git a/src/check_type.cpp b/src/check_type.cpp index 0e77792b7..cdce6dae8 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -322,19 +322,6 @@ void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, Type *named_t array_add(&array, e); map_set(&ctx->checker->info.gen_types, hash_pointer(original_type), array); } - - { - Type *dst_bt = base_type(named_type); - Type *src_bt = base_type(original_type); - if ((dst_bt != nullptr && src_bt != nullptr) && - (dst_bt->kind == src_bt->kind)){ - if (dst_bt->kind == Type_Struct) { - if (dst_bt->Struct.atom_op_table == nullptr) { - dst_bt->Struct.atom_op_table = src_bt->Struct.atom_op_table; - } - } - } - } } Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *polymorphic_params, diff --git a/src/checker.cpp b/src/checker.cpp index 8016f1020..21ca4c398 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2666,206 +2666,6 @@ DECL_ATTRIBUTE_PROC(type_decl_attribute) { if (name == "private") { // NOTE(bill): Handled elsewhere `check_collect_value_decl` return true; - } else if (name == "index_get") { - if (value != nullptr) { - Operand o = {}; - check_expr_or_type(c, &o, value); - Entity *e = entity_of_node(value); - if (e != nullptr && e->kind == Entity_Procedure) { - if (ac->deferred_procedure.entity != nullptr) { - error(elem, "Previous usage of the '%.*s' attribute", LIT(name)); - } - - bool valid = true; - - { - Type *pt = base_type(e->type); - GB_ASSERT(pt->kind == Type_Proc); - - if (pt->Proc.result_count == 0) { - error(value, "'%s' attribute must return something", LIT(name)); - valid = false; - } - - if (pt->Proc.param_count < 2) { - error(value, "'%s' attribute must allow for 2 parameters", LIT(name)); - valid = false; - } else { - isize minimum_param_count = 0; - for_array(i, pt->Proc.params->Tuple.variables) { - Entity *param = pt->Proc.params->Tuple.variables[i]; - if (param->kind == Entity_Variable) { - if (param->Variable.param_value.kind == ParameterValue_Invalid) { - minimum_param_count += 1; - } else { - break; - } - } else if (param->kind == Entity_Constant) { - minimum_param_count += 1; - } else { - break; - } - } - - if (minimum_param_count > 2) { - error(value, "'%s' attribute must allow for at a minimum 2 parameters", LIT(name)); - valid = false; - } - } - } - - if (valid) { - if (ac->atom_op_table == nullptr) { - ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable); - } - ac->atom_op_table->op[TypeAtomOp_index_get] = e; - } - return true; - } - } - error(elem, "Expected a procedure entity for '%.*s'", LIT(name)); - return false; - } else if (name == "index_set") { - if (value != nullptr) { - Operand o = {}; - check_expr_or_type(c, &o, value); - Entity *e = entity_of_node(value); - if (e != nullptr && e->kind == Entity_Procedure) { - if (ac->deferred_procedure.entity != nullptr) { - error(elem, "Previous usage of the '%.*s' attribute", LIT(name)); - } - - bool valid = true; - - { - Type *pt = base_type(e->type); - GB_ASSERT(pt->kind == Type_Proc); - - if (pt->Proc.param_count < 3) { - error(value, "'%s' attribute must allow for 3 parameters", LIT(name)); - valid = false; - } else { - isize minimum_param_count = 0; - for_array(i, pt->Proc.params->Tuple.variables) { - Entity *param = pt->Proc.params->Tuple.variables[i]; - if (param->kind == Entity_Variable) { - if (param->Variable.param_value.kind == ParameterValue_Invalid) { - minimum_param_count += 1; - } else { - break; - } - } else if (param->kind == Entity_Constant) { - minimum_param_count += 1; - } else { - break; - } - } - - if (minimum_param_count > 3) { - error(value, "'%s' attribute must allow for at a minimum 3 parameters", LIT(name)); - valid = false; - } - } - - if (pt->Proc.variadic || pt->Proc.c_vararg) { - error(value, "'%s' attribute does not allow variadic procedures", LIT(name)); - valid = false; - } - } - - if (valid) { - if (ac->atom_op_table == nullptr) { - ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable); - } - ac->atom_op_table->op[TypeAtomOp_index_set] = e; - } - return true; - } - } - error(elem, "Expected a procedure entity for '%.*s'", LIT(name)); - return false; - } else if (name == "slice") { - if (value != nullptr) { - Operand o = {}; - check_expr_or_type(c, &o, value); - Entity *e = entity_of_node(value); - if (e != nullptr && e->kind == Entity_Procedure) { - if (ac->deferred_procedure.entity != nullptr) { - error(elem, "Previous usage of the '%.*s' attribute", LIT(name)); - } - - bool valid = true; - - { - Type *pt = base_type(e->type); - GB_ASSERT(pt->kind == Type_Proc); - - if (pt->Proc.param_count < 1) { - error(value, "'%s' attribute must allow for 1 parameter", LIT(name)); - valid = false; - } else { - isize minimum_param_count = 0; - for_array(i, pt->Proc.params->Tuple.variables) { - Entity *param = pt->Proc.params->Tuple.variables[i]; - if (param->kind == Entity_Variable) { - if (param->Variable.param_value.kind == ParameterValue_Invalid) { - minimum_param_count += 1; - } else { - break; - } - } else if (param->kind == Entity_Constant) { - minimum_param_count += 1; - } else { - break; - } - } - - if (minimum_param_count > 1) { - error(value, "'%s' attribute must allow for at a minimum 1 parameter", LIT(name)); - valid = false; - } - { - Entity *param = pt->Proc.params->Tuple.variables[0]; - Type *param_type = base_type(param->type); - if (is_type_pointer(param_type) && !is_type_rawptr(param_type)) { - // okay - } else { - error(value, "'%s' attribute's first parameter must be a pointer", LIT(name)); - valid = false; - } - - } - } - - if (pt->Proc.variadic || pt->Proc.c_vararg) { - error(value, "'%s' attribute does not allow variadic procedures", LIT(name)); - valid = false; - } - - if (pt->Proc.result_count != 1) { - error(value, "'%s' attribute must return 1 result", LIT(name)); - valid = false; - } else { - Type *rt = pt->Proc.results->Tuple.variables[0]->type; - rt = base_type(rt); - if (!is_type_slice(rt)) { - error(value, "'%s' attribute must return a slice", LIT(name)); - valid = false; - } - } - } - - if (valid) { - if (ac->atom_op_table == nullptr) { - ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable); - } - ac->atom_op_table->op[TypeAtomOp_slice] = e; - } - return true; - } - } - error(elem, "Expected a procedure entity for '%.*s'", LIT(name)); - return false; } return false; } diff --git a/src/checker.hpp b/src/checker.hpp index 41b6e8c42..38628ed51 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -114,7 +114,6 @@ struct AttributeContext { String deprecated_message; DeferredProcedure deferred_procedure; u32 optimization_mode; // ProcedureOptimizationMode - struct TypeAtomOpTable *atom_op_table; }; AttributeContext make_attribute_context(String link_prefix) { diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index ce633b127..240e33c80 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -12147,27 +12147,6 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { return lb_addr(val); } - if (!is_type_indexable(t)) { - AtomOpMapEntry *found = map_get(&p->module->info->atom_op_map, hash_pointer(expr)); - if (found != nullptr) { - if (found->kind == TypeAtomOp_index_get) { - return lb_build_addr(p, found->node); - } else if (found->kind == TypeAtomOp_index_get_ptr) { - return lb_addr(lb_build_expr(p, found->node)); - } else if (found->kind == TypeAtomOp_index_set) { - lbValue ptr = lb_build_addr_ptr(p, ie->expr); - if (deref) { - ptr = lb_emit_load(p, ptr); - } - - lbAddr addr = {lbAddr_AtomOp_index_set}; - addr.addr = ptr; - addr.index_set.index = lb_build_expr(p, ie->index); - addr.index_set.node = found->node; - return addr; - } - } - } GB_ASSERT_MSG(is_type_indexable(t), "%s %s", type_to_string(t), expr_to_string(expr)); if (is_type_map(t)) { @@ -12312,36 +12291,6 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { bool no_indices = se->low == nullptr && se->high == nullptr; - { - Type *type = base_type(type_of_expr(se->expr)); - if (type->kind == Type_Struct && !is_type_soa_struct(type)) { - TypeAtomOpTable *atom_op_table = type->Struct.atom_op_table; - if (atom_op_table != nullptr && atom_op_table->op[TypeAtomOp_slice]) { - AtomOpMapEntry *found = map_get(&p->module->info->atom_op_map, hash_pointer(expr)); - if (found) { - lbValue base = lb_build_expr(p, found->node); - - Type *slice_type = base.type; - lbValue len = lb_slice_len(p, base); - if (high.value == nullptr) high = len; - - if (!no_indices) { - lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr); - } - - - lbValue elem = lb_emit_ptr_offset(p, lb_slice_elem(p, base), low); - lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int); - - lbAddr slice = lb_add_local_generated(p, slice_type, false); - lb_fill_slice(p, slice, elem, new_len); - return slice; - } - } - } - } - - lbAddr addr = lb_build_addr(p, se->expr); lbValue base = lb_addr_load(p, addr); Type *type = base_type(base.type); diff --git a/src/parser.hpp b/src/parser.hpp index b6bf42f63..77d5f1b02 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -22,7 +22,6 @@ enum AddressingMode { Addressing_OptionalOk = 10, // rhs: acts like a value with an optional boolean part (for existence check) Addressing_SoaVariable = 11, // Struct-Of-Arrays indexed variable - Addressing_AtomOpAssign = 12, // Specialized for custom atom operations for assignments }; struct TypeAndValue { diff --git a/src/types.cpp b/src/types.cpp index ffdb90c03..8a78e08d1 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -128,21 +128,6 @@ enum StructSoaKind { StructSoa_Dynamic = 3, }; -enum TypeAtomOpKind { - TypeAtomOp_Invalid, - - TypeAtomOp_index_get, - TypeAtomOp_index_set, - TypeAtomOp_slice, - TypeAtomOp_index_get_ptr, - - TypeAtomOp_COUNT, -}; - -struct TypeAtomOpTable { - Entity *op[TypeAtomOp_COUNT]; -}; - struct TypeStruct { Array fields; Array tags; @@ -156,8 +141,6 @@ struct TypeStruct { i64 custom_align; Entity * names; - TypeAtomOpTable *atom_op_table; - Type * soa_elem; i64 soa_count; StructSoaKind soa_kind; @@ -180,8 +163,6 @@ struct TypeUnion { Type * polymorphic_params; // Type_Tuple Type * polymorphic_parent; - TypeAtomOpTable *atom_op_table; - bool no_nil; bool maybe; bool is_polymorphic; -- cgit v1.2.3 From 5e31c04a01e5fa1d11c5a72b684263005451980a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 May 2021 19:13:34 +0100 Subject: Disallow duplicate unary operators for `+`, `-`, and `~` --- src/parser.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index b24bb8aed..f11735af6 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2754,13 +2754,23 @@ Ast *parse_unary_expr(AstFile *f, bool lhs) { return ast_auto_cast(f, token, expr); } + case Token_And: + case Token_Not: { + Token token = advance_token(f); + Ast *expr = parse_unary_expr(f, lhs); + return ast_unary_expr(f, token, expr); + } + case Token_Add: case Token_Sub: - case Token_Not: - case Token_Xor: - case Token_And: { + case Token_Xor: { Token token = advance_token(f); Ast *expr = parse_unary_expr(f, lhs); + if (expr != nullptr && expr->kind == Ast_UnaryExpr) { + if (expr->UnaryExpr.op.kind == token.kind) { + syntax_error(expr, "Duplicate unary operator '%.*s' will produce a redundant no-op", LIT(token.string)); + } + } return ast_unary_expr(f, token, expr); } -- cgit v1.2.3 From 5d03bc61b870d525536290627044e9638c3495b9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 May 2021 19:34:46 +0100 Subject: Tokenize `++` and `--` as tokens but disallow them in the parser, and give better error messages for they are used as operators/statements --- src/parser.cpp | 33 +++++++++++++++++++++++++-------- src/tokenizer.cpp | 14 +++++++++++--- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index f11735af6..0dae732e5 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2724,6 +2724,16 @@ Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) { } break; + case Token_Increment: + case Token_Decrement: + if (!lhs) { + Token token = advance_token(f); + syntax_error(token, "Postfix '%.*s' operator is not supported", LIT(token.string)); + } else { + loop = false; + } + break; + default: loop = false; break; @@ -2754,6 +2764,10 @@ Ast *parse_unary_expr(AstFile *f, bool lhs) { return ast_auto_cast(f, token, expr); } + + case Token_Add: + case Token_Sub: + case Token_Xor: case Token_And: case Token_Not: { Token token = advance_token(f); @@ -2761,19 +2775,15 @@ Ast *parse_unary_expr(AstFile *f, bool lhs) { return ast_unary_expr(f, token, expr); } - case Token_Add: - case Token_Sub: - case Token_Xor: { + case Token_Increment: + case Token_Decrement: { Token token = advance_token(f); + syntax_error(token, "Unary '%.*s' operator is not supported", LIT(token.string)); Ast *expr = parse_unary_expr(f, lhs); - if (expr != nullptr && expr->kind == Ast_UnaryExpr) { - if (expr->UnaryExpr.op.kind == token.kind) { - syntax_error(expr, "Duplicate unary operator '%.*s' will produce a redundant no-op", LIT(token.string)); - } - } return ast_unary_expr(f, token, expr); } + case Token_Period: { Token token = expect_token(f, Token_Period); Ast *ident = parse_ident(f); @@ -3163,6 +3173,13 @@ Ast *parse_simple_stmt(AstFile *f, u32 flags) { return ast_bad_stmt(f, token, f->curr_token); } + switch (token.kind) { + case Token_Increment: + case Token_Decrement: + advance_token(f); + syntax_error(token, "Postfix '%.*s' statement is not supported", LIT(token.string)); + break; + } #if 0 diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 081ef6443..3410517bc 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -51,8 +51,10 @@ TOKEN_KIND(Token__AssignOpBegin, ""), \ TOKEN_KIND(Token_CmpAndEq, "&&="), \ TOKEN_KIND(Token_CmpOrEq, "||="), \ TOKEN_KIND(Token__AssignOpEnd, ""), \ - TOKEN_KIND(Token_ArrowRight, "->"), \ - TOKEN_KIND(Token_Undef, "---"), \ + TOKEN_KIND(Token_Increment, "++"), \ + TOKEN_KIND(Token_Decrement, "--"), \ + TOKEN_KIND(Token_ArrowRight,"->"), \ + TOKEN_KIND(Token_Undef, "---"), \ \ TOKEN_KIND(Token__ComparisonBegin, ""), \ TOKEN_KIND(Token_CmpEq, "=="), \ @@ -1287,6 +1289,9 @@ void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) { if (t->curr_rune == '=') { advance_to_next_rune(t); token->kind = Token_AddEq; + } else if (t->curr_rune == '+') { + advance_to_next_rune(t); + token->kind = Token_Increment; } break; case '-': @@ -1298,7 +1303,10 @@ void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) { advance_to_next_rune(t); advance_to_next_rune(t); token->kind = Token_Undef; - } else if (t->curr_rune == '>') { + } else if (t->curr_rune == '-') { + advance_to_next_rune(t); + token->kind = Token_Decrement; + }else if (t->curr_rune == '>') { advance_to_next_rune(t); token->kind = Token_ArrowRight; } -- cgit v1.2.3 From 0ad599675e4be36a4548b61f5458edef65aeeef3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 May 2021 19:47:00 +0100 Subject: Improve semicolon insertion rule for dummy tokens `++` and `--` --- src/tokenizer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 3410517bc..b3d3f660e 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -1292,6 +1292,7 @@ void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) { } else if (t->curr_rune == '+') { advance_to_next_rune(t); token->kind = Token_Increment; + insert_semicolon = true; } break; case '-': @@ -1306,6 +1307,7 @@ void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) { } else if (t->curr_rune == '-') { advance_to_next_rune(t); token->kind = Token_Decrement; + insert_semicolon = true; }else if (t->curr_rune == '>') { advance_to_next_rune(t); token->kind = Token_ArrowRight; -- cgit v1.2.3 From 7886798156ff67a4ab20349a7c789efcc981c4ef Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 May 2021 19:49:48 +0100 Subject: Add space --- src/tokenizer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index b3d3f660e..5936ac3fe 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -1308,7 +1308,7 @@ void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) { advance_to_next_rune(t); token->kind = Token_Decrement; insert_semicolon = true; - }else if (t->curr_rune == '>') { + } else if (t->curr_rune == '>') { advance_to_next_rune(t); token->kind = Token_ArrowRight; } -- cgit v1.2.3 From 1cf6b6679dd7dfd4e00cb55891970ce68f59c67d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 May 2021 21:10:06 +0100 Subject: Add custom basic dead instruction elimination pass --- src/llvm_backend.cpp | 28 ++++++++------- src/llvm_backend_opt.cpp | 93 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 13 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 240e33c80..7bdc8b04a 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -3318,9 +3318,11 @@ void lb_end_procedure_body(lbProcedure *p) { LLVMBuildBr(p->builder, p->entry_block->block); LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block); + LLVMValueRef instr = nullptr; + // Make sure there is a "ret void" at the end of a procedure with no return type if (p->type->Proc.result_count == 0) { - LLVMValueRef instr = LLVMGetLastInstruction(p->curr_block->block); + instr = LLVMGetLastInstruction(p->curr_block->block); if (!lb_is_instr_terminating(instr)) { lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr); LLVMBuildRetVoid(p->builder); @@ -3332,7 +3334,7 @@ void lb_end_procedure_body(lbProcedure *p) { // Make sure every block terminates, and if not, make it unreachable for (block = first_block; block != nullptr; block = LLVMGetNextBasicBlock(block)) { - LLVMValueRef instr = LLVMGetLastInstruction(block); + instr = LLVMGetLastInstruction(block); if (instr == nullptr || !lb_is_instr_terminating(instr)) { LLVMPositionBuilderAtEnd(p->builder, block); LLVMBuildUnreachable(p->builder); @@ -14045,7 +14047,7 @@ lbProcedure *lb_create_startup_type_info(lbModule *m) { LLVMVerifyFunction(p->value, LLVMAbortProcessAction); } - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); + lb_run_function_pass_manager(default_function_pass_manager, p); return p; } @@ -14134,7 +14136,7 @@ lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *start LLVMVerifyFunction(p->value, LLVMAbortProcessAction); } - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); + lb_run_function_pass_manager(default_function_pass_manager, p); return p; } @@ -14251,7 +14253,7 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) LLVMVerifyFunction(p->value, LLVMAbortProcessAction); } - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); + lb_run_function_pass_manager(default_function_pass_manager, p); return p; } @@ -14376,26 +14378,26 @@ WORKER_TASK_PROC(lb_llvm_function_pass_worker_proc) { lbProcedure *p = m->procedures_to_generate[i]; if (p->body != nullptr) { // Build Procedure if (p->flags & lbProcedureFlag_WithoutMemcpyPass) { - LLVMRunFunctionPassManager(default_function_pass_manager_without_memcpy, p->value); + lb_run_function_pass_manager(default_function_pass_manager_without_memcpy, p); } else { if (p->entity && p->entity->kind == Entity_Procedure) { switch (p->entity->Procedure.optimization_mode) { case ProcedureOptimizationMode_None: case ProcedureOptimizationMode_Minimal: - LLVMRunFunctionPassManager(function_pass_manager_minimal, p->value); + lb_run_function_pass_manager(function_pass_manager_minimal, p); break; case ProcedureOptimizationMode_Size: - LLVMRunFunctionPassManager(function_pass_manager_size, p->value); + lb_run_function_pass_manager(function_pass_manager_size, p); break; case ProcedureOptimizationMode_Speed: - LLVMRunFunctionPassManager(function_pass_manager_speed, p->value); + lb_run_function_pass_manager(function_pass_manager_speed, p); break; default: - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); + lb_run_function_pass_manager(default_function_pass_manager, p); break; } } else { - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); + lb_run_function_pass_manager(default_function_pass_manager, p); } } } @@ -14403,11 +14405,11 @@ WORKER_TASK_PROC(lb_llvm_function_pass_worker_proc) { for_array(i, m->equal_procs.entries) { lbProcedure *p = m->equal_procs.entries[i].value; - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); + lb_run_function_pass_manager(default_function_pass_manager, p); } for_array(i, m->hasher_procs.entries) { lbProcedure *p = m->hasher_procs.entries[i].value; - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); + lb_run_function_pass_manager(default_function_pass_manager, p); } return 0; diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index 6b7ca507a..fa9d3b456 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -248,3 +248,96 @@ void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPa LLVMAddCFGSimplificationPass(mpm); } + +void lb_run_remove_dead_instruction_pass(lbProcedure *p) { + isize removal_count = 0; + isize pass_count = 0; + isize const max_pass_count = 10; + // Custom remove dead instruction pass + for (; pass_count < max_pass_count; pass_count++) { + bool was_dead_instructions = false; + + // NOTE(bill): Iterate backwards + // reduces the number of passes as things later on will depend on things previously + for (LLVMBasicBlockRef block = LLVMGetLastBasicBlock(p->value); + block != nullptr; + block = LLVMGetPreviousBasicBlock(block)) { + // NOTE(bill): Iterate backwards + // reduces the number of passes as things later on will depend on things previously + for (LLVMValueRef instr = LLVMGetLastInstruction(block); + instr != nullptr; + /**/) { + LLVMValueRef curr_instr = instr; + instr = LLVMGetPreviousInstruction(instr); + + LLVMUseRef first_use = LLVMGetFirstUse(curr_instr); + if (first_use != nullptr) { + continue; + } + if (LLVMTypeOf(curr_instr) == nullptr) { + continue; + } + + // NOTE(bill): Explicit instructions are set here because some instructions could have side effects + switch (LLVMGetInstructionOpcode(curr_instr)) { + case LLVMAdd: + case LLVMFAdd: + case LLVMSub: + case LLVMFSub: + case LLVMMul: + case LLVMFMul: + case LLVMUDiv: + case LLVMSDiv: + case LLVMFDiv: + case LLVMURem: + case LLVMSRem: + case LLVMFRem: + case LLVMShl: + case LLVMLShr: + case LLVMAShr: + case LLVMAnd: + case LLVMOr: + case LLVMXor: + case LLVMAlloca: + case LLVMLoad: + case LLVMGetElementPtr: + case LLVMTrunc: + case LLVMZExt: + case LLVMSExt: + case LLVMFPToUI: + case LLVMFPToSI: + case LLVMUIToFP: + case LLVMSIToFP: + case LLVMFPTrunc: + case LLVMFPExt: + case LLVMPtrToInt: + case LLVMIntToPtr: + case LLVMBitCast: + case LLVMAddrSpaceCast: + case LLVMICmp: + case LLVMFCmp: + case LLVMSelect: + case LLVMExtractElement: + removal_count += 1; + LLVMInstructionEraseFromParent(curr_instr); + was_dead_instructions = true; + break; + } + } + } + + if (!was_dead_instructions) { + break; + } + } +} + + +void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedure *p) { + LLVMRunFunctionPassManager(fpm, p->value); + // NOTE(bill): LLVMAddDCEPass doesn't seem to be exported in the official DLL's for LLVM + // which means we cannot rely upon it + // This is also useful for read the .ll for debug purposes because a lot of instructions + // are not removed + lb_run_remove_dead_instruction_pass(p); +} -- cgit v1.2.3 From fffb83282b4e725152476ceb54a19d770e93a83e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 May 2021 21:24:17 +0100 Subject: Add missing instructions to pass --- src/llvm_backend_opt.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index fa9d3b456..c4eecdb5d 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -318,6 +318,8 @@ void lb_run_remove_dead_instruction_pass(lbProcedure *p) { case LLVMFCmp: case LLVMSelect: case LLVMExtractElement: + case LLVMShuffleVector: + case LLVMExtractValue: removal_count += 1; LLVMInstructionEraseFromParent(curr_instr); was_dead_instructions = true; -- cgit v1.2.3 From b1cfeb6c95066e0f50d43bcd44f846e1f28f61ce Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 May 2021 22:14:20 +0100 Subject: Add missing instruction to pass --- src/llvm_backend_opt.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index c4eecdb5d..213005f8f 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -253,6 +253,7 @@ void lb_run_remove_dead_instruction_pass(lbProcedure *p) { isize removal_count = 0; isize pass_count = 0; isize const max_pass_count = 10; + isize original_instruction_count = 0; // Custom remove dead instruction pass for (; pass_count < max_pass_count; pass_count++) { bool was_dead_instructions = false; @@ -267,6 +268,10 @@ void lb_run_remove_dead_instruction_pass(lbProcedure *p) { for (LLVMValueRef instr = LLVMGetLastInstruction(block); instr != nullptr; /**/) { + if (pass_count == 0) { + original_instruction_count += 1; + } + LLVMValueRef curr_instr = instr; instr = LLVMGetPreviousInstruction(instr); @@ -280,6 +285,7 @@ void lb_run_remove_dead_instruction_pass(lbProcedure *p) { // NOTE(bill): Explicit instructions are set here because some instructions could have side effects switch (LLVMGetInstructionOpcode(curr_instr)) { + case LLVMFNeg: case LLVMAdd: case LLVMFAdd: case LLVMSub: -- cgit v1.2.3 From 85e5be03d1d8660bbd4025c278bb59897cb153c9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 16 May 2021 00:25:47 +0100 Subject: Redesign `os2.Error` to work with the new extended `union` behaviour --- core/os/os2/errors.odin | 61 ++++++++++---------------------------- core/os/os2/file_stream.odin | 20 +++---------- core/os/os2/file_util.odin | 1 + core/os/os2/file_windows.odin | 20 ++++++------- core/os/os2/pipe_windows.odin | 2 +- core/os/os2/stat_windows.odin | 6 ++-- core/os/os2/temp_file_windows.odin | 4 +-- 7 files changed, 37 insertions(+), 77 deletions(-) diff --git a/core/os/os2/errors.odin b/core/os/os2/errors.odin index 00cd600a8..2fc49deed 100644 --- a/core/os/os2/errors.odin +++ b/core/os/os2/errors.odin @@ -1,11 +1,8 @@ package os2 -Platform_Error_Min_Bits :: 32; +import "core:io" -Error :: enum u64 { - None = 0, - - // General Errors +General_Error :: enum u32 { Invalid_Argument, Permission_Denied, @@ -13,42 +10,19 @@ Error :: enum u64 { Not_Exist, Closed, - // Timeout Errors Timeout, +} - // I/O Errors - // EOF is the error returned by `read` when no more input is available - EOF, - - // Unexpected_EOF means that EOF was encountered in the middle of reading a fixed-sized block of data - Unexpected_EOF, - - // Short_Write means that a write accepted fewer bytes than requested but failed to return an explicit error - Short_Write, - - // Invalid_Write means that a write returned an impossible count - Invalid_Write, - - // Short_Buffer means that a read required a longer buffer than was provided - Short_Buffer, - - // No_Progress is returned by some implementations of `io.Reader` when many calls - // to `read` have failed to return any data or error. - // This is usually a signed of a broken `io.Reader` implementation - No_Progress, - - Invalid_Whence, - Invalid_Offset, - Invalid_Unread, - - Negative_Read, - Negative_Write, - Negative_Count, - Buffer_Full, +Platform_Error :: struct { + err: i32, +} - // Platform Specific Errors - Platform_Minimum = 1< (err: i32, ok: bool) { - if ferr >= .Platform_Minimum { - err = i32(u64(ferr)>>Platform_Error_Min_Bits); - ok = true; + v: Platform_Error; + if v, ok = ferr.(Platform_Error); ok { + err = v.err; } return; } -error_from_platform_error :: proc(errno: i32) -> Error { - return Error(u64(errno) << Platform_Error_Min_Bits); -} error_string :: proc(ferr: Error) -> string { - #partial switch ferr { - case .None: return ""; + switch ferr { + case nil: return ""; case .Invalid_Argument: return "invalid argument"; case .Permission_Denied: return "permission denied"; case .Exist: return "file already exists"; diff --git a/core/os/os2/file_stream.odin b/core/os/os2/file_stream.odin index 6877faea4..208b2323b 100644 --- a/core/os/os2/file_stream.odin +++ b/core/os/os2/file_stream.odin @@ -10,23 +10,11 @@ file_to_stream :: proc(fd: Handle) -> (s: io.Stream) { @(private) error_to_io_error :: proc(ferr: Error) -> io.Error { - #partial switch ferr { - case .None: return .None; - case .EOF: return .EOF; - case .Unexpected_EOF: return .Unexpected_EOF; - case .Short_Write: return .Short_Write; - case .Invalid_Write: return .Invalid_Write; - case .Short_Buffer: return .Short_Buffer; - case .No_Progress: return .No_Progress; - case .Invalid_Whence: return .Invalid_Whence; - case .Invalid_Offset: return .Invalid_Offset; - case .Invalid_Unread: return .Invalid_Unread; - case .Negative_Read: return .Negative_Read; - case .Negative_Write: return .Negative_Write; - case .Negative_Count: return .Negative_Count; - case .Buffer_Full: return .Buffer_Full; + err, ok := ferr.(io.Error); + if !ok { + err = .Unknown; } - return .Unknown; + return err; } diff --git a/core/os/os2/file_util.odin b/core/os/os2/file_util.odin index 435eba3ab..db6842cf8 100644 --- a/core/os/os2/file_util.odin +++ b/core/os/os2/file_util.odin @@ -1,6 +1,7 @@ package os2 import "core:mem" +import "core:io" import "core:strconv" import "core:unicode/utf8" diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index 97fe6b3d9..5e87d80a4 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -5,19 +5,19 @@ import "core:io" import "core:time" _create :: proc(name: string) -> (Handle, Error) { - return 0, .None; + return 0, nil; } _open :: proc(name: string) -> (Handle, Error) { - return 0, .None; + return 0, nil; } _open_file :: proc(name: string, flag: int, perm: File_Mode) -> (Handle, Error) { - return 0, .None; + return 0, nil; } _close :: proc(fd: Handle) -> Error { - return .None; + return nil; } _name :: proc(fd: Handle, allocator := context.allocator) -> string { @@ -58,11 +58,11 @@ _file_size :: proc(fd: Handle) -> (n: i64, err: Error) { _sync :: proc(fd: Handle) -> Error { - return .None; + return nil; } _flush :: proc(fd: Handle) -> Error { - return .None; + return nil; } _truncate :: proc(fd: Handle, size: i64) -> Maybe(Path_Error) { @@ -92,20 +92,20 @@ _read_link :: proc(name: string) -> (string, Maybe(Path_Error)) { _chdir :: proc(fd: Handle) -> Error { - return .None; + return nil; } _chmod :: proc(fd: Handle, mode: File_Mode) -> Error { - return .None; + return nil; } _chown :: proc(fd: Handle, uid, gid: int) -> Error { - return .None; + return nil; } _lchown :: proc(name: string, uid, gid: int) -> Error { - return .None; + return nil; } diff --git a/core/os/os2/pipe_windows.odin b/core/os/os2/pipe_windows.odin index 68adb6c3b..04750bf88 100644 --- a/core/os/os2/pipe_windows.odin +++ b/core/os/os2/pipe_windows.odin @@ -6,7 +6,7 @@ import win32 "core:sys/windows" _pipe :: proc() -> (r, w: Handle, err: Error) { p: [2]win32.HANDLE; if !win32.CreatePipe(&p[0], &p[1], nil, 0) { - return 0, 0, error_from_platform_error(i32(win32.GetLastError())); + return 0, 0, Platform_Error{i32(win32.GetLastError())}; } return Handle(p[0]), Handle(p[1]), nil; } diff --git a/core/os/os2/stat_windows.odin b/core/os/os2/stat_windows.odin index ed739b894..48811340a 100644 --- a/core/os/os2/stat_windows.odin +++ b/core/os/os2/stat_windows.odin @@ -40,7 +40,7 @@ _same_file :: proc(fi1, fi2: File_Info) -> bool { _stat_errno :: proc(errno: win32.DWORD) -> Path_Error { - return Path_Error{err = error_from_platform_error(i32(errno))}; + return Path_Error{err = Platform_Error{i32(errno)}}; } @@ -89,7 +89,7 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator := co fd: win32.WIN32_FIND_DATAW; sh := win32.FindFirstFileW(wname, &fd); if sh == win32.INVALID_HANDLE_VALUE { - e = Path_Error{err = error_from_platform_error(i32(win32.GetLastError()))}; + e = Path_Error{err = Platform_Error{i32(win32.GetLastError())}}; return; } win32.FindClose(sh); @@ -99,7 +99,7 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator := co h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil); if h == win32.INVALID_HANDLE_VALUE { - e = Path_Error{err = error_from_platform_error(i32(win32.GetLastError()))}; + e = Path_Error{err = Platform_Error{i32(win32.GetLastError())}}; return; } defer win32.CloseHandle(h); diff --git a/core/os/os2/temp_file_windows.odin b/core/os/os2/temp_file_windows.odin index 19dca1b04..dd050ab48 100644 --- a/core/os/os2/temp_file_windows.odin +++ b/core/os/os2/temp_file_windows.odin @@ -4,11 +4,11 @@ package os2 import win32 "core:sys/windows" _create_temp :: proc(dir, pattern: string) -> (Handle, Error) { - return 0, .None; + return 0, nil; } _mkdir_temp :: proc(dir, pattern: string, allocator := context.allocator) -> (string, Error) { - return "", .None; + return "", nil; } _temp_dir :: proc(allocator := context.allocator) -> string { -- cgit v1.2.3 From 24c89b3eeed9d36fd6adfdc2f4a412b39fc59c6b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 16 May 2021 00:29:22 +0100 Subject: Minor change --- core/os/os2/file_stream.odin | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/os/os2/file_stream.odin b/core/os/os2/file_stream.odin index 208b2323b..52f5b30e9 100644 --- a/core/os/os2/file_stream.odin +++ b/core/os/os2/file_stream.odin @@ -10,6 +10,9 @@ file_to_stream :: proc(fd: Handle) -> (s: io.Stream) { @(private) error_to_io_error :: proc(ferr: Error) -> io.Error { + if ferr == nil { + return .None; + } err, ok := ferr.(io.Error); if !ok { err = .Unknown; -- cgit v1.2.3 From ce08e832f7dcdeeae37cf0e432648efa2f27f2a7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 16 May 2021 12:34:35 +0100 Subject: Allow `..=` alongside `..` as a "full range" operator; Update `core:odin/parser` etc --- core/odin/parser/parser.odin | 66 ++++++++++++++++++++++++++++++++++---- core/odin/tokenizer/token.odin | 2 ++ core/odin/tokenizer/tokenizer.odin | 3 ++ src/check_expr.cpp | 7 ++-- src/check_stmt.cpp | 1 + src/check_type.cpp | 1 + src/llvm_backend.cpp | 6 ++-- src/parser.cpp | 18 ++++++----- src/tokenizer.cpp | 4 +++ 9 files changed, 89 insertions(+), 19 deletions(-) diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 160eb3a5c..661c46751 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -12,6 +12,10 @@ Parser :: struct { file: ^ast.File, tok: tokenizer.Tokenizer, + // If optional_semicolons is true, semicolons are completely as statement terminators + // different to .Insert_Semicolon in tok.flags + optional_semicolons: bool, + warn: Warning_Handler, err: Error_Handler, @@ -128,6 +132,10 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool { p.line_comment = nil; } + if p.optional_semicolons { + p.tok.flags += {.Insert_Semicolon}; + } + p.file = file; tokenizer.init(&p.tok, file.src, file.fullpath, p.err); if p.tok.ch <= 0 { @@ -400,6 +408,11 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool { if node == nil { return false; } + + if p.optional_semicolons { + return true; + } + switch n in node.derived { case ast.Empty_Stmt, ast.Block_Stmt: return true; @@ -439,14 +452,34 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool { return false; } +expect_semicolon_newline_error :: proc(p: ^Parser, token: tokenizer.Token, s: ^ast.Node) { + if !p.optional_semicolons && .Insert_Semicolon in p.tok.flags && token.text == "\n" { + #partial switch token.kind { + case .Close_Brace: + case .Close_Paren: + case .Else: + return; + } + if is_semicolon_optional_for_node(p, s) { + return; + } + + tok := token; + tok.pos.column -= 1; + error(p, tok.pos, "expected ';', got newline"); + } +} + expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool { if allow_token(p, .Semicolon) { + expect_semicolon_newline_error(p, p.prev_tok, node); return true; } prev := p.prev_tok; if prev.kind == .Semicolon { + expect_semicolon_newline_error(p, p.prev_tok, node); return true; } @@ -615,7 +648,7 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt { cond = parse_expr(p, false); } else { init = parse_simple_stmt(p, nil); - if allow_token(p, .Semicolon) { + if parse_control_statement_semicolon_separator(p) { cond = parse_expr(p, false); } else { cond = convert_stmt_to_expr(p, init, "boolean expression"); @@ -668,6 +701,18 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt { return if_stmt; } +parse_control_statement_semicolon_separator :: proc(p: ^Parser) -> bool { + tok := peek_token(p); + if tok.kind != .Open_Brace { + return allow_token(p, .Semicolon); + } + if tok.text == ";" { + return allow_token(p, .Semicolon); + } + return false; + +} + parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt { if p.curr_proc == nil { error(p, p.curr_tok.pos, "you cannot use a for statement in the file scope"); @@ -716,7 +761,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt { } } - if !is_range && allow_token(p, .Semicolon) { + if !is_range && parse_control_statement_semicolon_separator(p) { init = cond; cond = nil; if p.curr_tok.kind != .Semicolon { @@ -820,7 +865,7 @@ parse_switch_stmt :: proc(p: ^Parser) -> ^ast.Stmt { tag = parse_simple_stmt(p, {Stmt_Allow_Flag.In}); if as, ok := tag.derived.(ast.Assign_Stmt); ok && as.op.kind == .In { is_type_switch = true; - } else if allow_token(p, .Semicolon) { + } else if parse_control_statement_semicolon_separator(p) { init = tag; tag = nil; if p.curr_tok.kind != .Open_Brace { @@ -831,6 +876,7 @@ parse_switch_stmt :: proc(p: ^Parser) -> ^ast.Stmt { } + skip_possible_newline(p); open := expect_token(p, .Open_Brace); for p.curr_tok.kind == .Case { @@ -958,6 +1004,7 @@ parse_foreign_block :: proc(p: ^Parser, tok: tokenizer.Token) -> ^ast.Foreign_Bl defer p.in_foreign_block = prev_in_foreign_block; p.in_foreign_block = true; + skip_possible_newline_for_literal(p); open := expect_token(p, .Open_Brace); for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF { decl := parse_foreign_block_decl(p); @@ -1287,7 +1334,7 @@ token_precedence :: proc(p: ^Parser, kind: tokenizer.Token_Kind) -> int { #partial switch kind { case .Question, .If, .When: return 1; - case .Ellipsis, .Range_Half: + case .Ellipsis, .Range_Half, .Range_Full: if !p.allow_range { return 0; } @@ -2233,6 +2280,8 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { } body: ^ast.Stmt; + skip_possible_newline_for_literal(p); + if allow_token(p, .Undef) { body = nil; if where_token.kind != .Invalid { @@ -2405,6 +2454,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { p.expr_level = where_prev_level; } + skip_possible_newline_for_literal(p); expect_token(p, .Open_Brace); fields, name_count = parse_field_list(p, .Close_Brace, ast.Field_Flags_Struct); close := expect_token(p, .Close_Brace); @@ -2473,6 +2523,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { variants: [dynamic]^ast.Expr; + skip_possible_newline_for_literal(p); expect_token_after(p, .Open_Brace, "union"); for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF { @@ -2503,6 +2554,8 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { if p.curr_tok.kind != .Open_Brace { base_type = parse_type(p); } + + skip_possible_newline_for_literal(p); open := expect_token(p, .Open_Brace); fields := parse_elem_list(p); close := expect_token(p, .Close_Brace); @@ -2601,6 +2654,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { } } + skip_possible_newline_for_literal(p); open := expect_token(p, .Open_Brace); asm_string := parse_expr(p, false); expect_token(p, .Comma); @@ -2811,7 +2865,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a open := expect_token(p, .Open_Bracket); #partial switch p.curr_tok.kind { - case .Colon, .Ellipsis, .Range_Half: + case .Colon, .Ellipsis, .Range_Half, .Range_Full: // NOTE(bill): Do not err yet break; case: @@ -2819,7 +2873,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a } #partial switch p.curr_tok.kind { - case .Ellipsis, .Range_Half: + case .Ellipsis, .Range_Half, .Range_Full: error(p, p.curr_tok.pos, "expected a colon, not a range"); fallthrough; case .Colon: diff --git a/core/odin/tokenizer/token.odin b/core/odin/tokenizer/token.odin index 1b37bae23..88908d7f8 100644 --- a/core/odin/tokenizer/token.odin +++ b/core/odin/tokenizer/token.odin @@ -107,6 +107,7 @@ Token_Kind :: enum u32 { Comma, // , Ellipsis, // .. Range_Half, // ..< + Range_Full, // ..= Back_Slash, // \ B_Operator_End, @@ -233,6 +234,7 @@ tokens := [Token_Kind.COUNT]string { ",", "..", "..<", + "..=", "\\", "", diff --git a/core/odin/tokenizer/tokenizer.odin b/core/odin/tokenizer/tokenizer.odin index 58f546191..297f42e3d 100644 --- a/core/odin/tokenizer/tokenizer.odin +++ b/core/odin/tokenizer/tokenizer.odin @@ -623,6 +623,9 @@ scan :: proc(t: ^Tokenizer) -> Token { if t.ch == '<' { advance_rune(t); kind = .Range_Half; + } else if t.ch == '=' { + advance_rune(t); + kind = .Range_Full; } } } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index fb3a51415..61cfd7d6e 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5940,8 +5940,9 @@ bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *y, ExactValu TokenKind op = Token_Lt; switch (ie->op.kind) { - case Token_Ellipsis: op = Token_LtEq; break; - case Token_RangeHalf: op = Token_Lt; break; + case Token_Ellipsis: op = Token_LtEq; break; // .. + case Token_RangeFull: op = Token_LtEq; break; // ..= + case Token_RangeHalf: op = Token_Lt; break; // ..< default: error(ie->op, "Invalid range operator"); break; } bool ok = compare_exact_values(op, a, b); @@ -5952,7 +5953,7 @@ bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *y, ExactValu } ExactValue inline_for_depth = exact_value_sub(b, a); - if (ie->op.kind == Token_Ellipsis) { + if (ie->op.kind != Token_RangeHalf) { inline_for_depth = exact_value_increment_one(inline_for_depth); } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index a954b44b6..2c9d8e00e 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -939,6 +939,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { TokenKind upper_op = Token_Invalid; switch (be->op.kind) { case Token_Ellipsis: upper_op = Token_GtEq; break; + case Token_RangeFull: upper_op = Token_GtEq; break; case Token_RangeHalf: upper_op = Token_Gt; break; default: GB_PANIC("Invalid range operator"); break; } diff --git a/src/check_type.cpp b/src/check_type.cpp index cdce6dae8..24a3e0a59 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -931,6 +931,7 @@ void check_bit_set_type(CheckerContext *c, Type *type, Type *named_type, Ast *no switch (be->op.kind) { case Token_Ellipsis: + case Token_RangeFull: if (upper - lower >= bits) { error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, (upper-lower+1)); } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 7bdc8b04a..41b1e8fc9 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -4041,6 +4041,7 @@ void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node, TokenKind op = Token_Lt; switch (node->op.kind) { case Token_Ellipsis: op = Token_LtEq; break; + case Token_RangeFull: op = Token_LtEq; break; case Token_RangeHalf: op = Token_Lt; break; default: GB_PANIC("Invalid interval operator"); break; } @@ -4454,7 +4455,7 @@ void lb_build_inline_range_stmt(lbProcedure *p, AstUnrollRangeStmt *rs, Scope *s ExactValue start = start_expr->tav.value; ExactValue end = end_expr->tav.value; - if (op == Token_Ellipsis) { // .. [start, end] + if (op != Token_RangeHalf) { // .. [start, end] (or ..=) ExactValue index = exact_value_i64(0); for (ExactValue val = start; compare_exact_values(Token_LtEq, val, end); @@ -4465,7 +4466,7 @@ void lb_build_inline_range_stmt(lbProcedure *p, AstUnrollRangeStmt *rs, Scope *s lb_build_stmt(p, rs->body); } - } else if (op == Token_RangeHalf) { // ..< [start, end) + } else { // ..< [start, end) ExactValue index = exact_value_i64(0); for (ExactValue val = start; compare_exact_values(Token_Lt, val, end); @@ -4632,6 +4633,7 @@ void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *scope) { TokenKind op = Token_Invalid; switch (ie->op.kind) { case Token_Ellipsis: op = Token_LtEq; break; + case Token_RangeFull: op = Token_LtEq; break; case Token_RangeHalf: op = Token_Lt; break; default: GB_PANIC("Invalid interval operator"); break; } diff --git a/src/parser.cpp b/src/parser.cpp index 0dae732e5..2e27b8698 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1344,6 +1344,7 @@ Token expect_token_after(AstFile *f, TokenKind kind, char const *msg) { bool is_token_range(TokenKind kind) { switch (kind) { case Token_Ellipsis: + case Token_RangeFull: case Token_RangeHalf: return true; } @@ -1574,6 +1575,10 @@ void expect_semicolon(AstFile *f, Ast *s) { return; } + if (f->curr_token.kind == Token_EOF) { + return; + } + if (s != nullptr) { bool insert_semi = (f->tokenizer.flags & TokenizerFlag_InsertSemicolon) != 0; if (insert_semi) { @@ -2315,7 +2320,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { f->expr_level = prev_level; } - + skip_possible_newline_for_literal(f); Token open = expect_token_after(f, Token_OpenBrace, "struct"); isize name_count = 0; @@ -2675,6 +2680,7 @@ Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) { switch (f->curr_token.kind) { case Token_Ellipsis: + case Token_RangeFull: case Token_RangeHalf: // NOTE(bill): Do not err yet case Token_Colon: @@ -2686,6 +2692,7 @@ Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) { switch (f->curr_token.kind) { case Token_Ellipsis: + case Token_RangeFull: case Token_RangeHalf: syntax_error(f->curr_token, "Expected a colon, not a range"); /* fallthrough */ @@ -2812,6 +2819,7 @@ i32 token_precedence(AstFile *f, TokenKind t) { case Token_when: return 1; case Token_Ellipsis: + case Token_RangeFull: case Token_RangeHalf: if (!f->allow_range) { return 0; @@ -3926,12 +3934,6 @@ Ast *parse_return_stmt(AstFile *f) { while (f->curr_token.kind != Token_Semicolon) { Ast *arg = parse_expr(f, false); - // if (f->curr_token.kind == Token_Eq) { - // Token eq = expect_token(f, Token_Eq); - // Ast *value = parse_value(f); - // arg = ast_field_value(f, arg, value, eq); - // } - array_add(&results, arg); if (f->curr_token.kind != Token_Comma || f->curr_token.kind == Token_EOF) { @@ -4052,7 +4054,7 @@ Ast *parse_case_clause(AstFile *f, bool is_type) { } f->allow_range = prev_allow_range; f->allow_in_expr = prev_allow_in_expr; - expect_token(f, Token_Colon); // TODO(bill): Is this the best syntax? + expect_token(f, Token_Colon); Array stmts = parse_stmt_list(f); return ast_case_clause(f, token, list, stmts); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 5936ac3fe..d1310b56e 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -76,6 +76,7 @@ TOKEN_KIND(Token__ComparisonEnd, ""), \ TOKEN_KIND(Token_Period, "."), \ TOKEN_KIND(Token_Comma, ","), \ TOKEN_KIND(Token_Ellipsis, ".."), \ + TOKEN_KIND(Token_RangeFull, "..="), \ TOKEN_KIND(Token_RangeHalf, "..<"), \ TOKEN_KIND(Token_BackSlash, "\\"), \ TOKEN_KIND(Token__OperatorEnd, ""), \ @@ -1204,6 +1205,9 @@ void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) { if (t->curr_rune == '<') { advance_to_next_rune(t); token->kind = Token_RangeHalf; + } else if (t->curr_rune == '=') { + advance_to_next_rune(t); + token->kind = Token_RangeFull; } } else if ('0' <= t->curr_rune && t->curr_rune <= '9') { scan_number_to_token(t, token, true); -- cgit v1.2.3 From df3512b1120dc17a01f8cfebd1f2f9042acdb9b5 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 16 May 2021 12:38:27 +0100 Subject: Make `core:odin` use a `string` for the source rather than `[]byte` --- core/odin/ast/ast.odin | 2 +- core/odin/parser/parse_files.odin | 2 +- core/odin/tokenizer/tokenizer.odin | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index 0d015f9bb..cf2cdeacc 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -69,7 +69,7 @@ File :: struct { pkg: ^Package, fullpath: string, - src: []byte, + src: string, docs: ^Comment_Group, diff --git a/core/odin/parser/parse_files.odin b/core/odin/parser/parse_files.odin index 99275777c..f622c9781 100644 --- a/core/odin/parser/parse_files.odin +++ b/core/odin/parser/parse_files.odin @@ -39,7 +39,7 @@ collect_package :: proc(path: string) -> (pkg: ^ast.Package, success: bool) { } file := ast.new(ast.File, NO_POS, NO_POS); file.pkg = pkg; - file.src = src; + file.src = string(src); file.fullpath = fullpath; pkg.files[fullpath] = file; } diff --git a/core/odin/tokenizer/tokenizer.odin b/core/odin/tokenizer/tokenizer.odin index 297f42e3d..f13dd5b7a 100644 --- a/core/odin/tokenizer/tokenizer.odin +++ b/core/odin/tokenizer/tokenizer.odin @@ -14,7 +14,7 @@ Flags :: distinct bit_set[Flag; u32]; Tokenizer :: struct { // Immutable data path: string, - src: []byte, + src: string, err: Error_Handler, flags: Flags, @@ -31,7 +31,7 @@ Tokenizer :: struct { error_count: int, } -init :: proc(t: ^Tokenizer, src: []byte, path: string, err: Error_Handler = default_error_handler) { +init :: proc(t: ^Tokenizer, src: string, path: string, err: Error_Handler = default_error_handler) { t.src = src; t.err = err; t.ch = ' '; @@ -87,7 +87,7 @@ advance_rune :: proc(using t: ^Tokenizer) { case r == 0: error(t, t.offset, "illegal character NUL"); case r >= utf8.RUNE_SELF: - r, w = utf8.decode_rune(src[read_offset:]); + r, w = utf8.decode_rune_in_string(src[read_offset:]); if r == utf8.RUNE_ERROR && w == 1 { error(t, t.offset, "illegal UTF-8 encoding"); } else if r == utf8.RUNE_BOM && offset > 0 { -- cgit v1.2.3 From 9ccdc40f65542702d5a4603f34a468435d97c352 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 16 May 2021 12:43:35 +0100 Subject: Make `.Optional_Semicolons` a flag for the parser --- core/odin/parser/parser.odin | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 661c46751..f8373eabe 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -8,13 +8,20 @@ import "core:fmt" Warning_Handler :: #type proc(pos: tokenizer.Pos, fmt: string, args: ..any); Error_Handler :: #type proc(pos: tokenizer.Pos, fmt: string, args: ..any); +Flag :: enum u32 { + Optional_Semicolons, +} + +Flags :: distinct bit_set[Flag; u32]; + + Parser :: struct { file: ^ast.File, tok: tokenizer.Tokenizer, - // If optional_semicolons is true, semicolons are completely as statement terminators + // If .Optional_Semicolons is true, semicolons are completely as statement terminators // different to .Insert_Semicolon in tok.flags - optional_semicolons: bool, + flags: Flags, warn: Warning_Handler, err: Error_Handler, @@ -104,8 +111,9 @@ end_pos :: proc(tok: tokenizer.Token) -> tokenizer.Pos { return pos; } -default_parser :: proc() -> Parser { +default_parser :: proc(flags := Flags{}) -> Parser { return Parser { + flags = flags, err = default_error_handler, warn = default_warning_handler, }; @@ -132,7 +140,7 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool { p.line_comment = nil; } - if p.optional_semicolons { + if .Optional_Semicolons in p.flags { p.tok.flags += {.Insert_Semicolon}; } @@ -409,7 +417,7 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool { return false; } - if p.optional_semicolons { + if .Optional_Semicolons in p.flags { return true; } @@ -453,7 +461,7 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool { } expect_semicolon_newline_error :: proc(p: ^Parser, token: tokenizer.Token, s: ^ast.Node) { - if !p.optional_semicolons && .Insert_Semicolon in p.tok.flags && token.text == "\n" { + if .Optional_Semicolons not_in p.flags && .Insert_Semicolon in p.tok.flags && token.text == "\n" { #partial switch token.kind { case .Close_Brace: case .Close_Paren: -- cgit v1.2.3 From 385385364b3d19c6bac2ee6184da1f0db38e9099 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 16 May 2021 12:46:25 +0100 Subject: Fix #988 --- src/check_expr.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 61cfd7d6e..ecffe7510 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1115,6 +1115,7 @@ bool check_cycle(CheckerContext *c, Entity *curr, bool report) { error(curr->token, "\t%.*s refers to", LIT(curr->token.string)); } error(curr->token, "\t%.*s", LIT(curr->token.string)); + curr->type = t_invalid; } return true; } -- cgit v1.2.3 From 50369cf19ca6aceb342e4688aee1d1903d7a1bb8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 16 May 2021 12:48:36 +0100 Subject: Update LICENSE year --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index e9e75e569..8ee9b17d6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2016-2020 Ginger Bill. All rights reserved. +Copyright (c) 2016-2021 Ginger Bill. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: -- cgit v1.2.3 From 2e633f57a065b09c9b0c7ad16f393762475a308a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 16 May 2021 13:00:16 +0100 Subject: Add concrete type information for untyped values as procedure arguments --- src/check_expr.cpp | 6 ++++++ src/llvm_backend.cpp | 1 + 2 files changed, 7 insertions(+) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index ecffe7510..4a37b2903 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4282,6 +4282,8 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { err = CallArgumentError_WrongTypes; } } + } else if (show_error) { + check_assignment(c, &o, t, str_lit("argument")); } score += s; @@ -4335,6 +4337,8 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { check_assignment(c, &o, t, str_lit("argument")); } err = CallArgumentError_WrongTypes; + } else if (show_error) { + check_assignment(c, &o, t, str_lit("argument")); } score += s; if (is_type_any(elem)) { @@ -4557,6 +4561,8 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) { err = CallArgumentError_NoneConstantParameter; } } + } else if (show_error) { + check_assignment(c, o, e->type, str_lit("procedure argument")); } score += s; } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 41b1e8fc9..c46c4fd85 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -11386,6 +11386,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { if (tv.value.kind != ExactValue_Invalid) { // NOTE(bill): Short on constant values + // GB_ASSERT_MSG(!is_type_untyped(tv.type), "%s @ %s", type_to_string(tv.type), token_pos_to_string(expr_pos)); return lb_const_value(p->module, tv.type, tv.value); } -- cgit v1.2.3 From 6ef96d33003d2fbdedd283ea432e70afc2f1d7ec Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 16 May 2021 14:44:02 +0100 Subject: Improve untyped to typed logic for aiding the backend --- src/check_expr.cpp | 23 ++++++++++++++++++-- src/check_stmt.cpp | 6 +++++- src/llvm_backend.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 79 insertions(+), 10 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 4a37b2903..68f05084e 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2216,6 +2216,10 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ return; } + if (is_type_untyped(y->type)) { + convert_to_typed(c, y, t_uint); + } + x->mode = Addressing_Value; } @@ -2886,7 +2890,7 @@ void update_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) { if (token_is_comparison(be->op.kind)) { // NOTE(bill): Do nothing as the types are fine } else if (token_is_shift(be->op.kind)) { - update_expr_type(c, be->left, type, final); + update_expr_type(c, be->left, type, final); } else { update_expr_type(c, be->left, type, final); update_expr_type(c, be->right, type, final); @@ -3200,8 +3204,8 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) { break; } - operand->type = target_type; update_expr_type(c, operand->expr, target_type, true); + operand->type = target_type; } bool check_index_value(CheckerContext *c, bool open_range, Ast *index_value, i64 max_count, i64 *value, Type *type_hint=nullptr) { @@ -4108,6 +4112,16 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, } +bool is_expr_constant_zero(Ast *expr) { + GB_ASSERT(expr != nullptr); + auto v = exact_value_to_integer(expr->tav.value); + if (v.kind == ExactValue_Integer) { + return big_int_cmp_zero(&v.value_integer) == 0; + } + return false; +} + + CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { ast_node(ce, CallExpr, call); GB_ASSERT(is_type_proc(proc_type)); @@ -4299,7 +4313,10 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { if (o.mode == Addressing_Type && is_type_typeid(e->type)) { add_type_info_type(c, o.type); add_type_and_value(c->info, o.expr, Addressing_Value, e->type, exact_value_typeid(o.type)); + } else if (show_error && is_type_untyped(o.type)) { + update_expr_type(c, o.expr, t, true); } + } if (variadic) { @@ -4347,6 +4364,8 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { if (o.mode == Addressing_Type && is_type_typeid(t)) { add_type_info_type(c, o.type); add_type_and_value(c->info, o.expr, Addressing_Value, t, exact_value_typeid(o.type)); + } else if (show_error && is_type_untyped(o.type)) { + update_expr_type(c, o.expr, t, true); } } } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 2c9d8e00e..ee60a4acd 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1621,7 +1621,11 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { } else { for (isize i = 0; i < result_count; i++) { Entity *e = pt->results->Tuple.variables[i]; - check_assignment(ctx, &operands[i], e->type, str_lit("return statement")); + Operand *o = &operands[i]; + check_assignment(ctx, o, e->type, str_lit("return statement")); + if (is_type_untyped(o->type)) { + update_expr_type(ctx, o->expr, e->type, true); + } } } case_end; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index c46c4fd85..a15b6a172 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -3409,6 +3409,20 @@ void lb_emit_if(lbProcedure *p, lbValue cond, lbBlock *true_block, lbBlock *fals LLVMBuildCondBr(p->builder, cv, true_block->block, false_block->block); } +bool lb_is_expr_untyped_const(Ast *expr) { + auto const &tv = type_and_value_of_expr(expr); + if (is_type_untyped(tv.type)) { + return tv.value.kind != ExactValue_Invalid; + } + return false; +} + +lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type *t) { + GB_ASSERT(is_type_typed(t)); + auto const &tv = type_and_value_of_expr(expr); + return lb_const_value(m, t, tv.value); +} + lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *false_block) { GB_ASSERT(cond != nullptr); GB_ASSERT(true_block != nullptr); @@ -3440,8 +3454,13 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f case_end; } - lbValue v = lb_build_expr(p, cond); - // v = lb_emit_conv(p, v, t_bool); + lbValue v = {}; + if (lb_is_expr_untyped_const(cond)) { + v = lb_expr_untyped_const_to_typed(p->module, cond, t_llvm_bool); + } else { + v = lb_build_expr(p, cond); + } + v = lb_emit_conv(p, v, t_llvm_bool); lb_emit_if(p, v, true_block, false_block); @@ -4872,6 +4891,9 @@ lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, Ast *left, Ast if (done->preds.count == 0) { lb_start_block(p, rhs); + if (lb_is_expr_untyped_const(right)) { + return lb_expr_untyped_const_to_typed(m, right, type); + } return lb_build_expr(p, right); } @@ -4886,7 +4908,12 @@ lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, Ast *left, Ast } lb_start_block(p, rhs); - lbValue edge = lb_build_expr(p, right); + lbValue edge = {}; + if (lb_is_expr_untyped_const(right)) { + edge = lb_expr_untyped_const_to_typed(m, right, type); + } else { + edge = lb_build_expr(p, right); + } incoming_values[done->preds.count] = edge.value; incoming_blocks[done->preds.count] = p->curr_block->block; @@ -7010,15 +7037,29 @@ lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) { case Token_And: case Token_Or: case Token_Xor: - case Token_AndNot: - case Token_Shl: - case Token_Shr: { + case Token_AndNot: { Type *type = default_type(tv.type); lbValue left = lb_build_expr(p, be->left); lbValue right = lb_build_expr(p, be->right); return lb_emit_arith(p, be->op.kind, left, right, type); } + case Token_Shl: + case Token_Shr: { + lbValue left, right; + Type *type = default_type(tv.type); + left = lb_build_expr(p, be->left); + + if (lb_is_expr_untyped_const(be->right)) { + // NOTE(bill): RHS shift operands can still be untyped + // Just bypass the standard lb_build_expr + right = lb_expr_untyped_const_to_typed(p->module, be->right, type); + } else { + right = lb_build_expr(p, be->right); + } + return lb_emit_arith(p, be->op.kind, left, right, type); + } + case Token_CmpEq: case Token_NotEq: case Token_Lt: @@ -11385,8 +11426,13 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { GB_ASSERT_MSG(tv.mode != Addressing_Invalid, "invalid expression '%s' (tv.mode = %d, tv.type = %s) @ %s\n Current Proc: %.*s : %s", expr_to_string(expr), tv.mode, type_to_string(tv.type), token_pos_to_string(expr_pos), LIT(p->name), type_to_string(p->type)); if (tv.value.kind != ExactValue_Invalid) { + // NOTE(bill): The commented out code below is just for debug purposes only + // GB_ASSERT_MSG(!is_type_untyped(tv.type), "%s @ %s\n%s", type_to_string(tv.type), token_pos_to_string(expr_pos), expr_to_string(expr)); + // if (is_type_untyped(tv.type)) { + // gb_printf_err("%s %s\n", token_pos_to_string(expr_pos), expr_to_string(expr)); + // } + // NOTE(bill): Short on constant values - // GB_ASSERT_MSG(!is_type_untyped(tv.type), "%s @ %s", type_to_string(tv.type), token_pos_to_string(expr_pos)); return lb_const_value(p->module, tv.type, tv.value); } -- cgit v1.2.3 From 8d044fd442f2f8fef678c2aef4b4de733b817f1c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 17 May 2021 17:08:25 +0100 Subject: Minor ABI change and cleanup --- src/llvm_abi.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index dcbd07118..aba85ae83 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -343,7 +343,7 @@ namespace lbAbi386 { if (sz == 0) { args[i] = lb_arg_type_ignore(t); } else { - args[i] = lb_arg_type_indirect_byval(c, t); + args[i] = lb_arg_type_indirect(t, nullptr); } } else { args[i] = non_struct(c, t, false); @@ -828,7 +828,7 @@ namespace lbAbiAmd64SysV { if (sz == 0) { args[i] = lb_arg_type_ignore(t); } else { - args[i] = lb_arg_type_indirect(t, lb_create_enum_attribute_with_type(c, "byval", t)); + args[i] = lb_arg_type_indirect_byval(c, t); } } else { args[i] = non_struct(c, t); -- cgit v1.2.3 From e0225c3579147557ad4fe2241d8fbe61851a6389 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 10:32:41 +0100 Subject: Add `intrinsics.sqrt` for floating-point values --- core/runtime/internal.odin | 17 ++++++----------- src/check_builtin.cpp | 28 ++++++++++++++++++++++++++++ src/checker_builtin_procs.hpp | 4 ++++ src/llvm_backend.cpp | 22 ++++++++++++++++++++++ 4 files changed, 60 insertions(+), 11 deletions(-) diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index 0e128567a..b95f3e64d 100644 --- a/core/runtime/internal.odin +++ b/core/runtime/internal.odin @@ -409,11 +409,6 @@ string_decode_rune :: #force_inline proc "contextless" (s: string) -> (rune, int return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4; } -@(default_calling_convention = "none") -foreign { - @(link_name="llvm.sqrt.f32") _sqrt_f32 :: proc(x: f32) -> f32 --- - @(link_name="llvm.sqrt.f64") _sqrt_f64 :: proc(x: f64) -> f64 --- -} abs_f16 :: #force_inline proc "contextless" (x: f16) -> f16 { return -x if x < 0 else x; } @@ -445,27 +440,27 @@ max_f64 :: proc(a, b: f64) -> f64 { abs_complex32 :: #force_inline proc "contextless" (x: complex32) -> f16 { r, i := real(x), imag(x); - return f16(_sqrt_f32(f32(r*r + i*i))); + return f16(intrinsics.sqrt(f32(r*r + i*i))); } abs_complex64 :: #force_inline proc "contextless" (x: complex64) -> f32 { r, i := real(x), imag(x); - return _sqrt_f32(r*r + i*i); + return intrinsics.sqrt(r*r + i*i); } abs_complex128 :: #force_inline proc "contextless" (x: complex128) -> f64 { r, i := real(x), imag(x); - return _sqrt_f64(r*r + i*i); + return intrinsics.sqrt(r*r + i*i); } abs_quaternion64 :: #force_inline proc "contextless" (x: quaternion64) -> f16 { r, i, j, k := real(x), imag(x), jmag(x), kmag(x); - return f16(_sqrt_f32(f32(r*r + i*i + j*j + k*k))); + return f16(intrinsics.sqrt(f32(r*r + i*i + j*j + k*k))); } abs_quaternion128 :: #force_inline proc "contextless" (x: quaternion128) -> f32 { r, i, j, k := real(x), imag(x), jmag(x), kmag(x); - return _sqrt_f32(r*r + i*i + j*j + k*k); + return intrinsics.sqrt(r*r + i*i + j*j + k*k); } abs_quaternion256 :: #force_inline proc "contextless" (x: quaternion256) -> f64 { r, i, j, k := real(x), imag(x), jmag(x), kmag(x); - return _sqrt_f64(r*r + i*i + j*j + k*k); + return intrinsics.sqrt(r*r + i*i + j*j + k*k); } diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 8932ac914..54cd21582 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -2026,6 +2026,34 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } break; + case BuiltinProc_sqrt: + { + Operand x = {}; + check_expr(c, &x, ce->args[0]); + if (x.mode == Addressing_Invalid) { + return false; + } + if (!is_type_float(x.type)) { + gbString xts = type_to_string(x.type); + error(x.expr, "Expected a floating point value for '%.*s', got %s", LIT(builtin_procs[id].name), xts); + gb_string_free(xts); + return false; + } + + if (x.mode == Addressing_Constant) { + f64 v = exact_value_to_f64(x.value); + + operand->mode = Addressing_Constant; + operand->type = x.type; + operand->value = exact_value_float(gb_sqrt(v)); + break; + } + operand->mode = Addressing_Value; + operand->type = default_type(x.type); + } + break; + + case BuiltinProc_atomic_fence: case BuiltinProc_atomic_fence_acq: case BuiltinProc_atomic_fence_rel: diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 5b1aecd68..cb864f37d 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -56,6 +56,8 @@ enum BuiltinProcId { BuiltinProc_overflow_sub, BuiltinProc_overflow_mul, + BuiltinProc_sqrt, + BuiltinProc_volatile_store, BuiltinProc_volatile_load, @@ -278,6 +280,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("overflow_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("overflow_mul"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("sqrt"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("volatile_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("volatile_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index a15b6a172..9dfa123a2 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -9429,6 +9429,28 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, return res; } + case BuiltinProc_sqrt: + { + Type *type = tv.type; + + lbValue x = lb_build_expr(p, ce->args[0]); + x = lb_emit_conv(p, x, type); + + char const *name = "llvm.sqrt"; + LLVMTypeRef types[1] = {lb_type(p->module, type)}; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0])); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); + + LLVMValueRef args[1] = {}; + args[0] = x.value; + + lbValue res = {}; + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + res.type = type; + return res; + } + case BuiltinProc_atomic_fence: LLVMBuildFence(p->builder, LLVMAtomicOrderingSequentiallyConsistent, false, ""); -- cgit v1.2.3 From e82e4398b65de36bf68e9f61d634165642b03af1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 10:50:02 +0100 Subject: Add `intrinsics.mem_copy` and `intrinsics.mem_copy_non_overlapping` --- core/runtime/internal.odin | 24 ++++---------------- src/check_builtin.cpp | 53 +++++++++++++++++++++++++++++++++++++++++++ src/checker_builtin_procs.hpp | 6 +++++ src/llvm_backend.cpp | 38 +++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 20 deletions(-) diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index b95f3e64d..8a7b22ca4 100644 --- a/core/runtime/internal.odin +++ b/core/runtime/internal.odin @@ -105,17 +105,9 @@ mem_copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr { if src == nil { return dst; } + // NOTE(bill): This _must_ be implemented like C's memmove - foreign _ { - when size_of(rawptr) == 8 { - @(link_name="llvm.memmove.p0i8.p0i8.i64") - llvm_memmove :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---; - } else { - @(link_name="llvm.memmove.p0i8.p0i8.i32") - llvm_memmove :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---; - } - } - llvm_memmove(dst, src, len); + intrinsics.mem_copy(dst, src, len); return dst; } @@ -123,17 +115,9 @@ mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> r if src == nil { return dst; } + // NOTE(bill): This _must_ be implemented like C's memcpy - foreign _ { - when size_of(rawptr) == 8 { - @(link_name="llvm.memcpy.p0i8.p0i8.i64") - llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---; - } else { - @(link_name="llvm.memcpy.p0i8.p0i8.i32") - llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---; - } - } - llvm_memcpy(dst, src, len); + intrinsics.mem_copy_non_overlapping(dst, src, len); return dst; } diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 54cd21582..c9b911f92 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -2053,6 +2053,59 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } break; + case BuiltinProc_mem_copy: + case BuiltinProc_mem_copy_non_overlapping: + { + operand->mode = Addressing_NoValue; + operand->type = t_invalid; + + Operand dst = {}; + Operand src = {}; + Operand len = {}; + check_expr(c, &dst, ce->args[0]); + check_expr(c, &src, ce->args[1]); + check_expr(c, &len, ce->args[2]); + if (dst.mode == Addressing_Invalid) { + return false; + } + if (src.mode == Addressing_Invalid) { + return false; + } + if (len.mode == Addressing_Invalid) { + return false; + } + + + if (!is_type_pointer(dst.type)) { + gbString str = type_to_string(dst.type); + error(dst.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_procs[id].name), str); + gb_string_free(str); + return false; + } + if (!is_type_pointer(src.type)) { + gbString str = type_to_string(src.type); + error(src.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_procs[id].name), str); + gb_string_free(str); + return false; + } + if (!is_type_integer(len.type)) { + gbString str = type_to_string(len.type); + error(len.expr, "Expected an integer value for the number of bytes for '%.*s', got %s", LIT(builtin_procs[id].name), str); + gb_string_free(str); + return false; + } + + if (len.mode == Addressing_Constant) { + i64 n = exact_value_to_i64(len.value); + if (n < 0) { + gbString str = expr_to_string(len.expr); + error(len.expr, "Expected a non-negative integer value for the number of bytes for '%.*s', got %s", LIT(builtin_procs[id].name), str); + gb_string_free(str); + } + } + } + break; + case BuiltinProc_atomic_fence: case BuiltinProc_atomic_fence_acq: diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index cb864f37d..b69bacd30 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -58,6 +58,9 @@ enum BuiltinProcId { BuiltinProc_sqrt, + BuiltinProc_mem_copy, + BuiltinProc_mem_copy_non_overlapping, + BuiltinProc_volatile_store, BuiltinProc_volatile_load, @@ -282,6 +285,9 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("sqrt"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("mem_copy"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("mem_copy_non_overlapping"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("volatile_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("volatile_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 9dfa123a2..b6d1990c3 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -9451,6 +9451,44 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, return res; } + case BuiltinProc_mem_copy: + case BuiltinProc_mem_copy_non_overlapping: + { + + + lbValue dst = lb_build_expr(p, ce->args[0]); + lbValue src = lb_build_expr(p, ce->args[1]); + lbValue len = lb_build_expr(p, ce->args[2]); + dst = lb_emit_conv(p, dst, t_rawptr); + src = lb_emit_conv(p, src, t_rawptr); + len = lb_emit_conv(p, len, t_int); + + char const *name = nullptr; + switch (id) { + case BuiltinProc_mem_copy: name = "llvm.memmove"; break; + case BuiltinProc_mem_copy_non_overlapping: name = "llvm.memcpy"; break; + } + + LLVMTypeRef types[3] = { + lb_type(p->module, t_rawptr), + lb_type(p->module, t_rawptr), + lb_type(p->module, t_int) + }; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s.%s.%s.%s", name, LLVMPrintTypeToString(types[0]), LLVMPrintTypeToString(types[1]), LLVMPrintTypeToString(types[2])); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); + + LLVMValueRef args[4] = {}; + args[0] = dst.value; + args[1] = src.value; + args[2] = len.value; + args[3] = LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), 0, false); // is_volatile parameter + + LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + + return {}; + } + case BuiltinProc_atomic_fence: LLVMBuildFence(p->builder, LLVMAtomicOrderingSequentiallyConsistent, false, ""); -- cgit v1.2.3 From a580cdbe7b61e9b2d9d7cb7e31dcb83cce88ae1e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 10:50:27 +0100 Subject: Delete core:sync/sync2/channel* stuff (for the time being) --- core/sync/sync2/channel.odin | 886 ----------------------------------- core/sync/sync2/channel_unix.odin | 17 - core/sync/sync2/channel_windows.odin | 34 -- 3 files changed, 937 deletions(-) delete mode 100644 core/sync/sync2/channel.odin delete mode 100644 core/sync/sync2/channel_unix.odin delete mode 100644 core/sync/sync2/channel_windows.odin diff --git a/core/sync/sync2/channel.odin b/core/sync/sync2/channel.odin deleted file mode 100644 index fc30d8280..000000000 --- a/core/sync/sync2/channel.odin +++ /dev/null @@ -1,886 +0,0 @@ -package sync2 - -// TODO(bill): The Channel implementation needs a complete rewrite for this new package sync design -// Especially how the `select` things work - -import "core:mem" -import "core:time" -import "core:math/rand" - -_, _ :: time, rand; - -Channel_Direction :: enum i8 { - Both = 0, - Send = +1, - Recv = -1, -} - -Channel :: struct(T: typeid, Direction := Channel_Direction.Both) { - using _internal: ^Raw_Channel, -} - -channel_init :: proc(ch: ^$C/Channel($T, $D), cap := 0, allocator := context.allocator) { - context.allocator = allocator; - ch._internal = raw_channel_create(size_of(T), align_of(T), cap); - return; -} - -channel_make :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Both)) { - context.allocator = allocator; - ch._internal = raw_channel_create(size_of(T), align_of(T), cap); - return; -} - -channel_make_send :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Send)) { - context.allocator = allocator; - ch._internal = raw_channel_create(size_of(T), align_of(T), cap); - return; -} -channel_make_recv :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Recv)) { - context.allocator = allocator; - ch._internal = raw_channel_create(size_of(T), align_of(T), cap); - return; -} - -channel_destroy :: proc(ch: $C/Channel($T, $D)) { - raw_channel_destroy(ch._internal); -} - -channel_as_send :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Send)) { - res._internal = ch._internal; - return; -} - -channel_as_recv :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Recv)) { - res._internal = ch._internal; - return; -} - - -channel_len :: proc(ch: $C/Channel($T, $D)) -> int { - return ch._internal.len if ch._internal != nil else 0; -} -channel_cap :: proc(ch: $C/Channel($T, $D)) -> int { - return ch._internal.cap if ch._internal != nil else 0; -} - - -channel_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) where D >= .Both { - msg := msg; - _ = raw_channel_send_impl(ch._internal, &msg, /*block*/true, loc); -} -channel_try_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) -> bool where D >= .Both { - msg := msg; - return raw_channel_send_impl(ch._internal, &msg, /*block*/false, loc); -} - -channel_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T) where D <= .Both { - c := ch._internal; - if c == nil { - panic(message="cannot recv message; channel is nil", loc=loc); - } - mutex_lock(&c.mutex); - raw_channel_recv_impl(c, &msg, loc); - mutex_unlock(&c.mutex); - return; -} -channel_try_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T, ok: bool) where D <= .Both { - c := ch._internal; - if c != nil && mutex_try_lock(&c.mutex) { - if c.len > 0 { - raw_channel_recv_impl(c, &msg, loc); - ok = true; - } - mutex_unlock(&c.mutex); - } - return; -} -channel_try_recv_ptr :: proc(ch: $C/Channel($T, $D), msg: ^T, loc := #caller_location) -> (ok: bool) where D <= .Both { - res: T; - res, ok = channel_try_recv(ch, loc); - if ok && msg != nil { - msg^ = res; - } - return; -} - - -channel_is_nil :: proc(ch: $C/Channel($T, $D)) -> bool { - return ch._internal == nil; -} -channel_is_open :: proc(ch: $C/Channel($T, $D)) -> bool { - c := ch._internal; - return c != nil && !c.closed; -} - - -channel_eq :: proc(a, b: $C/Channel($T, $D)) -> bool { - return a._internal == b._internal; -} -channel_ne :: proc(a, b: $C/Channel($T, $D)) -> bool { - return a._internal != b._internal; -} - - -channel_can_send :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D >= .Both { - return raw_channel_can_send(ch._internal); -} -channel_can_recv :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D <= .Both { - return raw_channel_can_recv(ch._internal); -} - - -channel_peek :: proc(ch: $C/Channel($T, $D)) -> int { - c := ch._internal; - if c == nil { - return -1; - } - if atomic_load(&c.closed) { - return -1; - } - return atomic_load(&c.len); -} - - -channel_close :: proc(ch: $C/Channel($T, $D), loc := #caller_location) { - raw_channel_close(ch._internal, loc); -} - - -channel_iterator :: proc(ch: $C/Channel($T, $D)) -> (msg: T, ok: bool) where D <= .Both { - c := ch._internal; - if c == nil { - return; - } - - if !c.closed || c.len > 0 { - msg, ok = channel_recv(ch), true; - } - return; -} -channel_drain :: proc(ch: $C/Channel($T, $D)) where D >= .Both { - raw_channel_drain(ch._internal); -} - - -channel_move :: proc(dst: $C1/Channel($T, $D1) src: $C2/Channel(T, $D2)) where D1 <= .Both, D2 >= .Both { - for msg in channel_iterator(src) { - channel_send(dst, msg); - } -} - - -Raw_Channel_Wait_Queue :: struct { - next: ^Raw_Channel_Wait_Queue, - state: ^uintptr, -} - - -Raw_Channel :: struct { - closed: bool, - ready: bool, // ready to recv - data_offset: u16, // data is stored at the end of this data structure - elem_size: u32, - len, cap: int, - read, write: int, - mutex: Mutex, - cond: Cond, - allocator: mem.Allocator, - - sendq: ^Raw_Channel_Wait_Queue, - recvq: ^Raw_Channel_Wait_Queue, -} - -raw_channel_wait_queue_insert :: proc(head: ^^Raw_Channel_Wait_Queue, val: ^Raw_Channel_Wait_Queue) { - val.next = head^; - head^ = val; -} -raw_channel_wait_queue_remove :: proc(head: ^^Raw_Channel_Wait_Queue, val: ^Raw_Channel_Wait_Queue) { - p := head; - for p^ != nil && p^ != val { - p = &p^.next; - } - if p != nil { - p^ = p^.next; - } -} - - -raw_channel_create :: proc(elem_size, elem_align: int, cap := 0) -> ^Raw_Channel { - assert(int(u32(elem_size)) == elem_size); - - s := size_of(Raw_Channel); - s = mem.align_forward_int(s, elem_align); - data_offset := uintptr(s); - s += elem_size * max(cap, 1); - - a := max(elem_align, align_of(Raw_Channel)); - - c := (^Raw_Channel)(mem.alloc(s, a)); - if c == nil { - return nil; - } - - c.data_offset = u16(data_offset); - c.elem_size = u32(elem_size); - c.len, c.cap = 0, max(cap, 0); - c.read, c.write = 0, 0; - c.allocator = context.allocator; - c.closed = false; - - return c; -} - - -raw_channel_destroy :: proc(c: ^Raw_Channel) { - if c == nil { - return; - } - context.allocator = c.allocator; - atomic_store(&c.closed, true); - free(c); -} - -raw_channel_close :: proc(c: ^Raw_Channel, loc := #caller_location) { - if c == nil { - panic(message="cannot close nil channel", loc=loc); - } - mutex_lock(&c.mutex); - defer mutex_unlock(&c.mutex); - atomic_store(&c.closed, true); - - // Release readers and writers - raw_channel_wait_queue_broadcast(c.recvq); - raw_channel_wait_queue_broadcast(c.sendq); - cond_broadcast(&c.cond); -} - - - -raw_channel_send_impl :: proc(c: ^Raw_Channel, msg: rawptr, block: bool, loc := #caller_location) -> bool { - send :: proc(c: ^Raw_Channel, src: rawptr) { - data := uintptr(c) + uintptr(c.data_offset); - dst := data + uintptr(c.write * int(c.elem_size)); - mem.copy(rawptr(dst), src, int(c.elem_size)); - c.len += 1; - c.write = (c.write + 1) % max(c.cap, 1); - } - - switch { - case c == nil: - panic(message="cannot send message; channel is nil", loc=loc); - case c.closed: - panic(message="cannot send message; channel is closed", loc=loc); - } - - mutex_lock(&c.mutex); - defer mutex_unlock(&c.mutex); - - if c.cap > 0 { - if !block && c.len >= c.cap { - return false; - } - - for c.len >= c.cap { - cond_wait(&c.cond, &c.mutex); - } - } else if c.len > 0 { // TODO(bill): determine correct behaviour - if !block { - return false; - } - cond_wait(&c.cond, &c.mutex); - } else if c.len == 0 && !block { - return false; - } - - send(c, msg); - cond_signal(&c.cond); - raw_channel_wait_queue_signal(c.recvq); - - return true; -} - -raw_channel_recv_impl :: proc(c: ^Raw_Channel, res: rawptr, loc := #caller_location) { - recv :: proc(c: ^Raw_Channel, dst: rawptr, loc := #caller_location) { - if c.len < 1 { - panic(message="cannot recv message; channel is empty", loc=loc); - } - c.len -= 1; - - data := uintptr(c) + uintptr(c.data_offset); - src := data + uintptr(c.read * int(c.elem_size)); - mem.copy(dst, rawptr(src), int(c.elem_size)); - c.read = (c.read + 1) % max(c.cap, 1); - } - - if c == nil { - panic(message="cannot recv message; channel is nil", loc=loc); - } - atomic_store(&c.ready, true); - for c.len < 1 { - raw_channel_wait_queue_signal(c.sendq); - cond_wait(&c.cond, &c.mutex); - } - atomic_store(&c.ready, false); - recv(c, res, loc); - if c.cap > 0 { - if c.len == c.cap - 1 { - // NOTE(bill): Only signal on the last one - cond_signal(&c.cond); - } - } else { - cond_signal(&c.cond); - } -} - - -raw_channel_can_send :: proc(c: ^Raw_Channel) -> (ok: bool) { - if c == nil { - return false; - } - mutex_lock(&c.mutex); - switch { - case c.closed: - ok = false; - case c.cap > 0: - ok = c.ready && c.len < c.cap; - case: - ok = c.ready && c.len == 0; - } - mutex_unlock(&c.mutex); - return; -} -raw_channel_can_recv :: proc(c: ^Raw_Channel) -> (ok: bool) { - if c == nil { - return false; - } - mutex_lock(&c.mutex); - ok = c.len > 0; - mutex_unlock(&c.mutex); - return; -} - - -raw_channel_drain :: proc(c: ^Raw_Channel) { - if c == nil { - return; - } - mutex_lock(&c.mutex); - c.len = 0; - c.read = 0; - c.write = 0; - mutex_unlock(&c.mutex); -} - - - -MAX_SELECT_CHANNELS :: 64; -SELECT_MAX_TIMEOUT :: max(time.Duration); - -Select_Command :: enum { - Recv, - Send, -} - -Select_Channel :: struct { - channel: ^Raw_Channel, - command: Select_Command, -} - - - -select :: proc(channels: ..Select_Channel) -> (index: int) { - return select_timeout(SELECT_MAX_TIMEOUT, ..channels); -} -select_timeout :: proc(timeout: time.Duration, channels: ..Select_Channel) -> (index: int) { - switch len(channels) { - case 0: - panic("sync: select with no channels"); - } - - assert(len(channels) <= MAX_SELECT_CHANNELS); - - backing: [MAX_SELECT_CHANNELS]int; - queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue; - candidates := backing[:]; - cap := len(channels); - candidates = candidates[:cap]; - - count := u32(0); - for c, i in channels { - if c.channel == nil { - continue; - } - switch c.command { - case .Recv: - if raw_channel_can_recv(c.channel) { - candidates[count] = i; - count += 1; - } - case .Send: - if raw_channel_can_send(c.channel) { - candidates[count] = i; - count += 1; - } - } - } - - if count == 0 { - wait_state: uintptr = 0; - for _, i in channels { - q := &queues[i]; - q.state = &wait_state; - } - - for c, i in channels { - if c.channel == nil { - continue; - } - q := &queues[i]; - switch c.command { - case .Recv: raw_channel_wait_queue_insert(&c.channel.recvq, q); - case .Send: raw_channel_wait_queue_insert(&c.channel.sendq, q); - } - } - raw_channel_wait_queue_wait_on(&wait_state, timeout); - for c, i in channels { - if c.channel == nil { - continue; - } - q := &queues[i]; - switch c.command { - case .Recv: raw_channel_wait_queue_remove(&c.channel.recvq, q); - case .Send: raw_channel_wait_queue_remove(&c.channel.sendq, q); - } - } - - for c, i in channels { - switch c.command { - case .Recv: - if raw_channel_can_recv(c.channel) { - candidates[count] = i; - count += 1; - } - case .Send: - if raw_channel_can_send(c.channel) { - candidates[count] = i; - count += 1; - } - } - } - if count == 0 && timeout == SELECT_MAX_TIMEOUT { - index = -1; - return; - } - - assert(count != 0); - } - - t := time.now(); - r := rand.create(transmute(u64)t); - i := rand.uint32(&r); - - index = candidates[i % count]; - return; -} - -select_recv :: proc(channels: ..^Raw_Channel) -> (index: int) { - switch len(channels) { - case 0: - panic("sync: select with no channels"); - } - - assert(len(channels) <= MAX_SELECT_CHANNELS); - - backing: [MAX_SELECT_CHANNELS]int; - queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue; - candidates := backing[:]; - cap := len(channels); - candidates = candidates[:cap]; - - count := u32(0); - for c, i in channels { - if raw_channel_can_recv(c) { - candidates[count] = i; - count += 1; - } - } - - if count == 0 { - state: uintptr; - for c, i in channels { - q := &queues[i]; - q.state = &state; - raw_channel_wait_queue_insert(&c.recvq, q); - } - raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT); - for c, i in channels { - q := &queues[i]; - raw_channel_wait_queue_remove(&c.recvq, q); - } - - for c, i in channels { - if raw_channel_can_recv(c) { - candidates[count] = i; - count += 1; - } - } - assert(count != 0); - } - - t := time.now(); - r := rand.create(transmute(u64)t); - i := rand.uint32(&r); - - index = candidates[i % count]; - return; -} - -select_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) { - switch len(channels) { - case 0: - panic("sync: select with no channels"); - } - - assert(len(channels) <= MAX_SELECT_CHANNELS); - - queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue; - candidates: [MAX_SELECT_CHANNELS]int; - - count := u32(0); - for c, i in channels { - if raw_channel_can_recv(c) { - candidates[count] = i; - count += 1; - } - } - - if count == 0 { - state: uintptr; - for c, i in channels { - q := &queues[i]; - q.state = &state; - raw_channel_wait_queue_insert(&c.recvq, q); - } - raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT); - for c, i in channels { - q := &queues[i]; - raw_channel_wait_queue_remove(&c.recvq, q); - } - - for c, i in channels { - if raw_channel_can_recv(c) { - candidates[count] = i; - count += 1; - } - } - assert(count != 0); - } - - t := time.now(); - r := rand.create(transmute(u64)t); - i := rand.uint32(&r); - - index = candidates[i % count]; - msg = channel_recv(channels[index]); - - return; -} - -select_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) { - switch len(channels) { - case 0: - panic("sync: select with no channels"); - } - - assert(len(channels) <= MAX_SELECT_CHANNELS); - - backing: [MAX_SELECT_CHANNELS]int; - queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue; - candidates := backing[:]; - cap := len(channels); - candidates = candidates[:cap]; - - count := u32(0); - for c, i in channels { - if raw_channel_can_recv(c) { - candidates[count] = i; - count += 1; - } - } - - if count == 0 { - state: uintptr; - for c, i in channels { - q := &queues[i]; - q.state = &state; - raw_channel_wait_queue_insert(&c.recvq, q); - } - raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT); - for c, i in channels { - q := &queues[i]; - raw_channel_wait_queue_remove(&c.recvq, q); - } - - for c, i in channels { - if raw_channel_can_recv(c) { - candidates[count] = i; - count += 1; - } - } - assert(count != 0); - } - - t := time.now(); - r := rand.create(transmute(u64)t); - i := rand.uint32(&r); - - index = candidates[i % count]; - - if msg != nil { - channel_send(channels[index], msg); - } - - return; -} - -select_send :: proc(channels: ..^Raw_Channel) -> (index: int) { - switch len(channels) { - case 0: - panic("sync: select with no channels"); - } - - assert(len(channels) <= MAX_SELECT_CHANNELS); - candidates: [MAX_SELECT_CHANNELS]int; - queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue; - - count := u32(0); - for c, i in channels { - if raw_channel_can_send(c) { - candidates[count] = i; - count += 1; - } - } - - if count == 0 { - state: uintptr; - for c, i in channels { - q := &queues[i]; - q.state = &state; - raw_channel_wait_queue_insert(&c.sendq, q); - } - raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT); - for c, i in channels { - q := &queues[i]; - raw_channel_wait_queue_remove(&c.sendq, q); - } - - for c, i in channels { - if raw_channel_can_send(c) { - candidates[count] = i; - count += 1; - } - } - assert(count != 0); - } - - t := time.now(); - r := rand.create(transmute(u64)t); - i := rand.uint32(&r); - - index = candidates[i % count]; - return; -} - -select_try :: proc(channels: ..Select_Channel) -> (index: int) { - switch len(channels) { - case 0: - panic("sync: select with no channels"); - } - - assert(len(channels) <= MAX_SELECT_CHANNELS); - - backing: [MAX_SELECT_CHANNELS]int; - candidates := backing[:]; - cap := len(channels); - candidates = candidates[:cap]; - - count := u32(0); - for c, i in channels { - switch c.command { - case .Recv: - if raw_channel_can_recv(c.channel) { - candidates[count] = i; - count += 1; - } - case .Send: - if raw_channel_can_send(c.channel) { - candidates[count] = i; - count += 1; - } - } - } - - if count == 0 { - index = -1; - return; - } - - t := time.now(); - r := rand.create(transmute(u64)t); - i := rand.uint32(&r); - - index = candidates[i % count]; - return; -} - - -select_try_recv :: proc(channels: ..^Raw_Channel) -> (index: int) { - switch len(channels) { - case 0: - index = -1; - return; - case 1: - index = -1; - if raw_channel_can_recv(channels[0]) { - index = 0; - } - return; - } - - assert(len(channels) <= MAX_SELECT_CHANNELS); - candidates: [MAX_SELECT_CHANNELS]int; - - count := u32(0); - for c, i in channels { - if raw_channel_can_recv(c) { - candidates[count] = i; - count += 1; - } - } - - if count == 0 { - index = -1; - return; - } - - t := time.now(); - r := rand.create(transmute(u64)t); - i := rand.uint32(&r); - - index = candidates[i % count]; - return; -} - - -select_try_send :: proc(channels: ..^Raw_Channel) -> (index: int) #no_bounds_check { - switch len(channels) { - case 0: - return -1; - case 1: - if raw_channel_can_send(channels[0]) { - return 0; - } - return -1; - } - - assert(len(channels) <= MAX_SELECT_CHANNELS); - candidates: [MAX_SELECT_CHANNELS]int; - - count := u32(0); - for c, i in channels { - if raw_channel_can_send(c) { - candidates[count] = i; - count += 1; - } - } - - if count == 0 { - index = -1; - return; - } - - t := time.now(); - r := rand.create(transmute(u64)t); - i := rand.uint32(&r); - - index = candidates[i % count]; - return; -} - -select_try_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) { - switch len(channels) { - case 0: - index = -1; - return; - case 1: - ok: bool; - if msg, ok = channel_try_recv(channels[0]); ok { - index = 0; - } - return; - } - - assert(len(channels) <= MAX_SELECT_CHANNELS); - candidates: [MAX_SELECT_CHANNELS]int; - - count := u32(0); - for c, i in channels { - if channel_can_recv(c) { - candidates[count] = i; - count += 1; - } - } - - if count == 0 { - index = -1; - return; - } - - t := time.now(); - r := rand.create(transmute(u64)t); - i := rand.uint32(&r); - - index = candidates[i % count]; - msg = channel_recv(channels[index]); - return; -} - -select_try_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) { - index = -1; - switch len(channels) { - case 0: - return; - case 1: - if channel_try_send(channels[0], msg) { - index = 0; - } - return; - } - - - assert(len(channels) <= MAX_SELECT_CHANNELS); - candidates: [MAX_SELECT_CHANNELS]int; - - count := u32(0); - for c, i in channels { - if raw_channel_can_send(c) { - candidates[count] = i; - count += 1; - } - } - - if count == 0 { - index = -1; - return; - } - - t := time.now(); - r := rand.create(transmute(u64)t); - i := rand.uint32(&r); - - index = candidates[i % count]; - channel_send(channels[index], msg); - return; -} - diff --git a/core/sync/sync2/channel_unix.odin b/core/sync/sync2/channel_unix.odin deleted file mode 100644 index 7429b67db..000000000 --- a/core/sync/sync2/channel_unix.odin +++ /dev/null @@ -1,17 +0,0 @@ -//+build linux, darwin, freebsd -//+private -package sync2 - -import "core:time" - -raw_channel_wait_queue_wait_on :: proc(state: ^uintptr, timeout: time.Duration) { - // stub -} - -raw_channel_wait_queue_signal :: proc(q: ^Raw_Channel_Wait_Queue) { - // stub -} - -raw_channel_wait_queue_broadcast :: proc(q: ^Raw_Channel_Wait_Queue) { - // stub -} diff --git a/core/sync/sync2/channel_windows.odin b/core/sync/sync2/channel_windows.odin deleted file mode 100644 index e365506c8..000000000 --- a/core/sync/sync2/channel_windows.odin +++ /dev/null @@ -1,34 +0,0 @@ -//+build windows -//+private -package sync2 - -import win32 "core:sys/windows" -import "core:time" - -raw_channel_wait_queue_wait_on :: proc(state: ^uintptr, timeout: time.Duration) { - ms: win32.DWORD = win32.INFINITE; - if max(time.Duration) != SELECT_MAX_TIMEOUT { - ms = win32.DWORD((max(time.duration_nanoseconds(timeout), 0) + 999999)/1000000); - } - - v := atomic_load(state); - for v == 0 { - win32.WaitOnAddress(state, &v, size_of(state^), ms); - v = atomic_load(state); - } - atomic_store(state, 0); -} - -raw_channel_wait_queue_signal :: proc(q: ^Raw_Channel_Wait_Queue) { - for x := q; x != nil; x = x.next { - atomic_add(x.state, 1); - win32.WakeByAddressSingle(x.state); - } -} - -raw_channel_wait_queue_broadcast :: proc(q: ^Raw_Channel_Wait_Queue) { - for x := q; x != nil; x = x.next { - atomic_add(x.state, 1); - win32.WakeByAddressAll(x.state); - } -} -- cgit v1.2.3 From 10b798456cd5bf7c13b4c9dd49a4d07533f78ac6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 10:55:56 +0100 Subject: Add `soa_zip` and `soa_unzip` to demo.odin --- examples/demo/demo.odin | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 76e8aa9fc..8cf3cfd9a 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -1760,8 +1760,6 @@ range_statements_with_multiple_return_values :: proc() { soa_struct_layout :: proc() { - // IMPORTANT NOTE(bill, 2019-11-03): This feature is subject to be changed/removed - // NOTE(bill): Most likely #soa [N]T fmt.println("\n#SOA Struct Layout"); { @@ -1858,6 +1856,30 @@ soa_struct_layout :: proc() { fmt.println(cap(d)); fmt.println(d[:]); } + { // soa_zip and soa_unzip + fmt.println("\nsoa_zip and soa_unzip"); + + x := []i32{1, 3, 9}; + y := []f32{2, 4, 16}; + z := []b32{true, false, true}; + + // produce an #soa slice the normal slices passed + s := soa_zip(a=x, b=y, c=z); + + // iterate over the #soa slice + for v, i in s { + fmt.println(v); // exactly the same as s[i] + // NOTE: 'v' is NOT a temporary value but has a specialized addressing mode + // which means that when accessing v.a etc, it does the correct transformation + // internally: + // s[i].a === s.a[i] + fmt.println(v.a, v.b, v.c); + } + + // Recover the slices from the #soa slice + a, b, c := soa_unzip(s); + fmt.println(a, b, c); + } } constant_literal_expressions :: proc() { -- cgit v1.2.3 From 28561ef5f5fba8bd4fd79ac3dcd7bd891248ab13 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 11:26:05 +0100 Subject: Minor change to internal linkage stuff --- src/llvm_backend.cpp | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index b6d1990c3..5c43b1f1a 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -5607,7 +5607,9 @@ LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String const &str) { LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name); LLVMSetInitializer(global_data, data); - LLVMSetLinkage(global_data, LLVMInternalLinkage); + if (!USE_SEPARTE_MODULES) { + LLVMSetLinkage(global_data, LLVMInternalLinkage); + } LLVMValueRef ptr = LLVMConstInBoundsGEP(global_data, indices, 2); string_map_set(&m->const_strings, key, ptr); @@ -5649,7 +5651,9 @@ lbValue lb_find_or_add_entity_string_byte_slice(lbModule *m, String const &str) } LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name); LLVMSetInitializer(global_data, data); - LLVMSetLinkage(global_data, LLVMInternalLinkage); + if (!USE_SEPARTE_MODULES) { + LLVMSetLinkage(global_data, LLVMInternalLinkage); + } LLVMValueRef ptr = nullptr; if (str.len != 0) { @@ -14689,7 +14693,9 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_type_info, max_type_info_count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), LB_TYPE_INFO_DATA_NAME); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - // LLVMSetLinkage(g, LLVMInternalLinkage); + if (!USE_SEPARTE_MODULES) { + LLVMSetLinkage(g, LLVMInternalLinkage); + } lbValue value = {}; value.value = g; @@ -14729,7 +14735,9 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_type_info_ptr, count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - // LLVMSetLinkage(g, LLVMInternalLinkage); + if (!USE_SEPARTE_MODULES) { + LLVMSetLinkage(g, LLVMInternalLinkage); + } lb_global_type_info_member_types = lb_addr({g, alloc_type_pointer(t)}); } @@ -14738,7 +14746,9 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_string, count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - // LLVMSetLinkage(g, LLVMInternalLinkage); + if (!USE_SEPARTE_MODULES) { + LLVMSetLinkage(g, LLVMInternalLinkage); + } lb_global_type_info_member_names = lb_addr({g, alloc_type_pointer(t)}); } { @@ -14746,7 +14756,9 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_uintptr, count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - // LLVMSetLinkage(g, LLVMInternalLinkage); + if (!USE_SEPARTE_MODULES) { + LLVMSetLinkage(g, LLVMInternalLinkage); + } lb_global_type_info_member_offsets = lb_addr({g, alloc_type_pointer(t)}); } @@ -14755,7 +14767,9 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_bool, count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - // LLVMSetLinkage(g, LLVMInternalLinkage); + if (!USE_SEPARTE_MODULES) { + LLVMSetLinkage(g, LLVMInternalLinkage); + } lb_global_type_info_member_usings = lb_addr({g, alloc_type_pointer(t)}); } @@ -14764,7 +14778,9 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_string, count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - // LLVMSetLinkage(g, LLVMInternalLinkage); + if (!USE_SEPARTE_MODULES) { + LLVMSetLinkage(g, LLVMInternalLinkage); + } lb_global_type_info_member_tags = lb_addr({g, alloc_type_pointer(t)}); } } @@ -14862,7 +14878,9 @@ void lb_generate_code(lbGenerator *gen) { LLVMSetLinkage(g.value, LLVMDLLExportLinkage); LLVMSetDLLStorageClass(g.value, LLVMDLLExportStorageClass); } else { - // LLVMSetLinkage(g.value, LLVMInternalLinkage); + if (!USE_SEPARTE_MODULES) { + LLVMSetLinkage(g.value, LLVMInternalLinkage); + } } lbGlobalVariable var = {}; -- cgit v1.2.3 From b34e4a9fd1fad2548c90624504ff8ce420cfeccc Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 11:46:27 +0100 Subject: More minor linkage changes --- src/llvm_backend.cpp | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 5c43b1f1a..a0f22be44 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -5607,9 +5607,7 @@ LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String const &str) { LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name); LLVMSetInitializer(global_data, data); - if (!USE_SEPARTE_MODULES) { - LLVMSetLinkage(global_data, LLVMInternalLinkage); - } + LLVMSetLinkage(global_data, LLVMInternalLinkage); LLVMValueRef ptr = LLVMConstInBoundsGEP(global_data, indices, 2); string_map_set(&m->const_strings, key, ptr); @@ -5651,9 +5649,7 @@ lbValue lb_find_or_add_entity_string_byte_slice(lbModule *m, String const &str) } LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name); LLVMSetInitializer(global_data, data); - if (!USE_SEPARTE_MODULES) { - LLVMSetLinkage(global_data, LLVMInternalLinkage); - } + LLVMSetLinkage(global_data, LLVMInternalLinkage); LLVMValueRef ptr = nullptr; if (str.len != 0) { @@ -13373,26 +13369,31 @@ lbValue lb_get_type_info_ptr(lbModule *m, Type *type) { lbValue lb_type_info_member_types_offset(lbProcedure *p, isize count) { + GB_ASSERT(p->module == &p->module->gen->default_module); lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_types.addr, lb_global_type_info_member_types_index); lb_global_type_info_member_types_index += cast(i32)count; return offset; } lbValue lb_type_info_member_names_offset(lbProcedure *p, isize count) { + GB_ASSERT(p->module == &p->module->gen->default_module); lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_names.addr, lb_global_type_info_member_names_index); lb_global_type_info_member_names_index += cast(i32)count; return offset; } lbValue lb_type_info_member_offsets_offset(lbProcedure *p, isize count) { + GB_ASSERT(p->module == &p->module->gen->default_module); lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_offsets.addr, lb_global_type_info_member_offsets_index); lb_global_type_info_member_offsets_index += cast(i32)count; return offset; } lbValue lb_type_info_member_usings_offset(lbProcedure *p, isize count) { + GB_ASSERT(p->module == &p->module->gen->default_module); lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_usings.addr, lb_global_type_info_member_usings_index); lb_global_type_info_member_usings_index += cast(i32)count; return offset; } lbValue lb_type_info_member_tags_offset(lbProcedure *p, isize count) { + GB_ASSERT(p->module == &p->module->gen->default_module); lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_tags.addr, lb_global_type_info_member_tags_index); lb_global_type_info_member_tags_index += cast(i32)count; return offset; @@ -14735,9 +14736,7 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_type_info_ptr, count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - if (!USE_SEPARTE_MODULES) { - LLVMSetLinkage(g, LLVMInternalLinkage); - } + LLVMSetLinkage(g, LLVMInternalLinkage); lb_global_type_info_member_types = lb_addr({g, alloc_type_pointer(t)}); } @@ -14746,9 +14745,7 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_string, count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - if (!USE_SEPARTE_MODULES) { - LLVMSetLinkage(g, LLVMInternalLinkage); - } + LLVMSetLinkage(g, LLVMInternalLinkage); lb_global_type_info_member_names = lb_addr({g, alloc_type_pointer(t)}); } { @@ -14756,9 +14753,7 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_uintptr, count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - if (!USE_SEPARTE_MODULES) { - LLVMSetLinkage(g, LLVMInternalLinkage); - } + LLVMSetLinkage(g, LLVMInternalLinkage); lb_global_type_info_member_offsets = lb_addr({g, alloc_type_pointer(t)}); } @@ -14767,9 +14762,7 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_bool, count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - if (!USE_SEPARTE_MODULES) { - LLVMSetLinkage(g, LLVMInternalLinkage); - } + LLVMSetLinkage(g, LLVMInternalLinkage); lb_global_type_info_member_usings = lb_addr({g, alloc_type_pointer(t)}); } @@ -14778,9 +14771,7 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_string, count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - if (!USE_SEPARTE_MODULES) { - LLVMSetLinkage(g, LLVMInternalLinkage); - } + LLVMSetLinkage(g, LLVMInternalLinkage); lb_global_type_info_member_tags = lb_addr({g, alloc_type_pointer(t)}); } } @@ -14878,7 +14869,9 @@ void lb_generate_code(lbGenerator *gen) { LLVMSetLinkage(g.value, LLVMDLLExportLinkage); LLVMSetDLLStorageClass(g.value, LLVMDLLExportStorageClass); } else { - if (!USE_SEPARTE_MODULES) { + if (USE_SEPARTE_MODULES) { + LLVMSetLinkage(g.value, LLVMExternalLinkage); + } else { LLVMSetLinkage(g.value, LLVMInternalLinkage); } } -- cgit v1.2.3 From 26ce40c188c31539cbf7f97cf2ad1bb81000bc55 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 11:51:48 +0100 Subject: Remove @(static) for global variables --- core/math/rand/rand.odin | 4 ++-- core/strings/builder.odin | 2 +- core/unicode/tables.odin | 10 ---------- src/check_decl.cpp | 5 ++--- src/llvm_backend.cpp | 2 +- 5 files changed, 6 insertions(+), 17 deletions(-) diff --git a/core/math/rand/rand.odin b/core/math/rand/rand.odin index 4f6e7474f..f5558bb8c 100644 --- a/core/math/rand/rand.odin +++ b/core/math/rand/rand.odin @@ -6,9 +6,9 @@ Rand :: struct { } -@(private, static) +@(private) _GLOBAL_SEED_DATA := 1234567890; -@(private, static) +@(private) global_rand := create(u64(uintptr(&_GLOBAL_SEED_DATA))); set_global_seed :: proc(seed: u64) { diff --git a/core/strings/builder.odin b/core/strings/builder.odin index dd7fd4f1e..843f79381 100644 --- a/core/strings/builder.odin +++ b/core/strings/builder.odin @@ -221,7 +221,7 @@ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) { } -@(private, static) +@(private) DIGITS_LOWER := "0123456789abcdefx"; write_quoted_string :: proc{ diff --git a/core/unicode/tables.odin b/core/unicode/tables.odin index bb858fd04..ff4793402 100644 --- a/core/unicode/tables.odin +++ b/core/unicode/tables.odin @@ -12,7 +12,6 @@ package unicode @(private) pLo :: pLl | pLu; // a letter that is neither upper nor lower case. @(private) pLmask :: pLo; -@(static) char_properties := [MAX_LATIN1+1]u8{ 0x00 = pC, // '\x00' 0x01 = pC, // '\x01' @@ -273,7 +272,6 @@ char_properties := [MAX_LATIN1+1]u8{ }; -@(static) alpha_ranges := [?]i32{ 0x00d8, 0x00f6, 0x00f8, 0x01f5, @@ -429,7 +427,6 @@ alpha_ranges := [?]i32{ 0xffda, 0xffdc, }; -@(static) alpha_singlets := [?]i32{ 0x00aa, 0x00b5, @@ -465,7 +462,6 @@ alpha_singlets := [?]i32{ 0xfe74, }; -@(static) space_ranges := [?]i32{ 0x0009, 0x000d, // tab and newline 0x0020, 0x0020, // space @@ -481,7 +477,6 @@ space_ranges := [?]i32{ 0xfeff, 0xfeff, }; -@(static) unicode_spaces := [?]i32{ 0x0009, // tab 0x000a, // LF @@ -499,7 +494,6 @@ unicode_spaces := [?]i32{ 0xfeff, // unknown }; -@(static) to_upper_ranges := [?]i32{ 0x0061, 0x007a, 468, // a-z A-Z 0x00e0, 0x00f6, 468, @@ -538,7 +532,6 @@ to_upper_ranges := [?]i32{ 0xff41, 0xff5a, 468, }; -@(static) to_upper_singlets := [?]i32{ 0x00ff, 621, 0x0101, 499, @@ -882,7 +875,6 @@ to_upper_singlets := [?]i32{ 0x1ff3, 509, }; -@(static) to_lower_ranges := [?]i32{ 0x0041, 0x005a, 532, // A-Z a-z 0x00c0, 0x00d6, 532, // - - @@ -922,7 +914,6 @@ to_lower_ranges := [?]i32{ 0xff21, 0xff3a, 532, // - - }; -@(static) to_lower_singlets := [?]i32{ 0x0100, 501, 0x0102, 501, @@ -1259,7 +1250,6 @@ to_lower_singlets := [?]i32{ 0x1ffc, 491, }; -@(static) to_title_singlets := [?]i32{ 0x01c4, 501, 0x01c6, 499, diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 5e8e79791..0aef40546 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -899,10 +899,9 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr, e->Variable.thread_local_model = ac.thread_local_model; e->Variable.is_export = ac.is_export; + e->flags &= ~EntityFlag_Static; if (ac.is_static) { - e->flags |= EntityFlag_Static; - } else { - e->flags &= ~EntityFlag_Static; + error(e->token, "@(static) is not supported for global variables, nor required"); } ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index a0f22be44..8bd5aaa37 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -14910,7 +14910,7 @@ void lb_generate_code(lbGenerator *gen) { LLVMMetadataRef llvm_file = lb_get_llvm_metadata(m, e->file); LLVMMetadataRef llvm_scope = llvm_file; - LLVMBool local_to_unit = e->flags & EntityFlag_Static; + LLVMBool local_to_unit = LLVMGetLinkage(g.value) == LLVMInternalLinkage; LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0); LLVMMetadataRef llvm_decl = nullptr; -- cgit v1.2.3 From 3ac934dd15b59be670627213643e80c6d902fe4d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 11:58:02 +0100 Subject: Add suggestion for unused expression on `x == y` Expression is not used: 'x == 123' Suggestion: Did you mean to do an assignment? 'x = 123;' --- src/check_stmt.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index ee60a4acd..a62b55ab2 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1436,6 +1436,28 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { gbString expr_str = expr_to_string(operand.expr); error(node, "Expression is not used: '%s'", expr_str); gb_string_free(expr_str); + if (operand.expr->kind == Ast_BinaryExpr) { + ast_node(be, BinaryExpr, operand.expr); + if (be->op.kind != Token_CmpEq) { + break; + } + + switch (be->left->tav.mode) { + case Addressing_Context: + case Addressing_Variable: + case Addressing_MapIndex: + case Addressing_SoaVariable: + { + gbString lhs = expr_to_string(be->left); + gbString rhs = expr_to_string(be->right); + error_line("\tSuggestion: Did you mean to do an assignment?\n", lhs, rhs); + error_line("\t '%s = %s;'\n", lhs, rhs); + gb_string_free(rhs); + gb_string_free(lhs); + } + break; + } + } break; } -- cgit v1.2.3 From 86dbcb1b20e7a5575013f2acbf0f48d194595d27 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 12:57:30 +0100 Subject: Add `-verbose-errors` which shows the error in the line of code --- src/build_settings.cpp | 5 +++ src/main.cpp | 6 ++++ src/parser.cpp | 43 +++++++++++++++++++++++ src/tokenizer.cpp | 92 ++++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 144 insertions(+), 2 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 09d860cfc..0207221bc 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -200,8 +200,10 @@ struct BuildContext { bool disallow_do; bool insert_semicolon; + bool ignore_warnings; bool warnings_as_errors; + bool show_error_line; bool use_subsystem_windows; bool ignore_microsoft_magic; @@ -746,6 +748,9 @@ String get_fullpath_core(gbAllocator a, String path) { return path_to_fullpath(a, res); } +bool show_error_line(void) { + return build_context.show_error_line; +} void init_build_context(TargetMetrics *cross_target) { diff --git a/src/main.cpp b/src/main.cpp index 0dd1e0c27..d16a110e3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -621,6 +621,7 @@ enum BuildFlagKind { BuildFlag_IgnoreWarnings, BuildFlag_WarningsAsErrors, + BuildFlag_VerboseErrors, #if defined(GB_SYSTEM_WINDOWS) BuildFlag_IgnoreVsSearch, @@ -741,6 +742,7 @@ bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_IgnoreWarnings, str_lit("ignore-warnings"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_WarningsAsErrors, str_lit("warnings-as-errors"), BuildFlagParam_None, Command_all); + add_flag(&build_flags, BuildFlag_VerboseErrors, str_lit("verbose-errors"), BuildFlagParam_None, Command_all); #if defined(GB_SYSTEM_WINDOWS) add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"), BuildFlagParam_None, Command__does_build); @@ -1320,6 +1322,10 @@ bool parse_build_flags(Array args) { } break; + case BuildFlag_VerboseErrors: + build_context.show_error_line = true; + break; + #if defined(GB_SYSTEM_WINDOWS) case BuildFlag_IgnoreVsSearch: GB_ASSERT(value.kind == ExactValue_Invalid); diff --git a/src/parser.cpp b/src/parser.cpp index 2e27b8698..77ce7ea70 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -115,6 +115,48 @@ Token token_end_of_line(AstFile *f, Token tok) { return tok; } +gbString get_file_line_as_string(TokenPos const &pos, i32 *offset_) { + AstFile *file = get_ast_file_from_id(pos.file_id); + if (file == nullptr) { + return nullptr; + } + isize offset = pos.offset; + + u8 *start = file->tokenizer.start; + u8 *end = file->tokenizer.end; + isize len = end-start; + if (len < offset) { + return nullptr; + } + + u8 *pos_offset = start+offset; + + u8 *line_start = pos_offset; + u8 *line_end = pos_offset; + while (line_start >= start) { + if (*line_start == '\n') { + line_start += 1; + break; + } + line_start -= 1; + } + + while (line_end < end) { + if (*line_end == '\n') { + line_end -= 1; + break; + } + line_end += 1; + } + String the_line = make_string(line_start, line_end-line_start); + the_line = string_trim_whitespace(the_line); + + if (offset_) *offset_ = cast(i32)(pos_offset - the_line.text); + + return gb_string_make_length(heap_allocator(), the_line.text, the_line.len); +} + + isize ast_node_size(AstKind kind) { return align_formula_isize(gb_size_of(AstCommonStuff) + ast_variant_sizes[kind], gb_align_of(void *)); @@ -4602,6 +4644,7 @@ ParseFileError init_ast_file(AstFile *f, String fullpath, TokenPos *err_pos) { GB_ASSERT(f != nullptr); f->fullpath = string_trim_whitespace(fullpath); // Just in case set_file_path_string(f->id, fullpath); + set_ast_file_from_id(f->id, f); if (!string_ends_with(f->fullpath, str_lit(".odin"))) { return ParseFile_WrongExtension; } diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index d1310b56e..84d6ac9e2 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -188,9 +188,11 @@ void init_keyword_hash_table(void) { GB_ASSERT(max_keyword_size < 16); } -gb_global Array global_file_path_strings; // index is file id +gb_global Array global_file_path_strings; // index is file id +gb_global Array global_files; // index is file id -String get_file_path_string(i32 index); +String get_file_path_string(i32 index); +struct AstFile *get_ast_file_from_id(i32 index); struct TokenPos { i32 file_id; @@ -284,6 +286,7 @@ void init_global_error_collector(void) { array_init(&global_error_collector.errors, heap_allocator()); array_init(&global_error_collector.error_buffer, heap_allocator()); array_init(&global_file_path_strings, heap_allocator(), 4096); + array_init(&global_files, heap_allocator(), 4096); } @@ -305,6 +308,24 @@ bool set_file_path_string(i32 index, String const &path) { return ok; } +bool set_ast_file_from_id(i32 index, AstFile *file) { + bool ok = false; + GB_ASSERT(index >= 0); + gb_mutex_lock(&global_error_collector.string_mutex); + + if (index >= global_files.count) { + array_resize(&global_files, index); + } + AstFile *prev = global_files[index]; + if (prev == nullptr) { + global_files[index] = file; + ok = true; + } + + gb_mutex_unlock(&global_error_collector.string_mutex); + return ok; +} + String get_file_path_string(i32 index) { GB_ASSERT(index >= 0); gb_mutex_lock(&global_error_collector.string_mutex); @@ -318,6 +339,20 @@ String get_file_path_string(i32 index) { return path; } +AstFile *get_ast_file_from_id(i32 index) { + GB_ASSERT(index >= 0); + gb_mutex_lock(&global_error_collector.string_mutex); + + AstFile *file = nullptr; + if (index < global_files.count) { + file = global_files[index]; + } + + gb_mutex_unlock(&global_error_collector.string_mutex); + return file; +} + + void begin_error_block(void) { gb_mutex_lock(&global_error_collector.mutex); global_error_collector.in_block = true; @@ -377,6 +412,8 @@ ErrorOutProc *error_out_va = default_error_out_va; // NOTE: defined in build_settings.cpp bool global_warnings_as_errors(void); bool global_ignore_warnings(void); +bool show_error_line(void); +gbString get_file_line_as_string(TokenPos const &pos, i32 *offset); void error_out(char const *fmt, ...) { va_list va; @@ -386,6 +423,53 @@ void error_out(char const *fmt, ...) { } +bool show_error_on_line(TokenPos const &pos) { + if (!show_error_line()) { + return false; + } + + i32 offset = 0; + gbString the_line = get_file_line_as_string(pos, &offset); + defer (gb_string_free(the_line)); + + if (the_line != nullptr) { + String line = make_string(cast(u8 const *)the_line, gb_string_length(the_line)); + + enum { + MAX_LINE_LENGTH = 76, + MAX_TAB_WIDTH = 8, + ELLIPSIS_PADDING = 8 + }; + + error_out("\n\t"); + if (line.len+MAX_TAB_WIDTH+ELLIPSIS_PADDING > MAX_LINE_LENGTH) { + i32 const half_width = MAX_LINE_LENGTH/2; + i32 left = cast(i32)(offset); + i32 right = cast(i32)(line.len - offset); + left = gb_min(left, half_width); + right = gb_min(right, half_width); + + line.text += offset-left; + line.len -= offset+right-left; + + line = string_trim_whitespace(line); + + offset = left + ELLIPSIS_PADDING/2; + + error_out("... %.*s ...", LIT(line)); + } else { + error_out("%.*s", LIT(line)); + } + error_out("\n\t"); + + for (i32 i = 0; i < offset; i++) error_out(" "); + error_out("^\n"); + error_out("\n"); + return true; + } + return false; +} + void error_va(Token token, char const *fmt, va_list va) { gb_mutex_lock(&global_error_collector.mutex); global_error_collector.count++; @@ -397,6 +481,7 @@ void error_va(Token token, char const *fmt, va_list va) { error_out("%s %s\n", token_pos_to_string(token.pos), gb_bprintf_va(fmt, va)); + show_error_on_line(token.pos); } gb_mutex_unlock(&global_error_collector.mutex); if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) { @@ -420,6 +505,7 @@ void warning_va(Token token, char const *fmt, va_list va) { error_out("%s Warning: %s\n", token_pos_to_string(token.pos), gb_bprintf_va(fmt, va)); + show_error_on_line(token.pos); } } gb_mutex_unlock(&global_error_collector.mutex); @@ -460,6 +546,7 @@ void syntax_error_va(Token token, char const *fmt, va_list va) { error_out("%s Syntax Error: %s\n", token_pos_to_string(token.pos), gb_bprintf_va(fmt, va)); + show_error_on_line(token.pos); } else if (token.pos.line == 0) { error_out("Syntax Error: %s\n", gb_bprintf_va(fmt, va)); } @@ -484,6 +571,7 @@ void syntax_warning_va(Token token, char const *fmt, va_list va) { error_out("%s Syntax Warning: %s\n", token_pos_to_string(token.pos), gb_bprintf_va(fmt, va)); + show_error_on_line(token.pos); } else if (token.pos.line == 0) { error_out("Warning: %s\n", gb_bprintf_va(fmt, va)); } -- cgit v1.2.3 From 5108ebf015e460e9c226a2067f6109785a20a143 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 13:02:44 +0100 Subject: Replace `error` calls with `Token` to use `TokenPos` --- src/parser.cpp | 8 ++--- src/tokenizer.cpp | 99 +++++++++++++++++++++++++++---------------------------- 2 files changed, 52 insertions(+), 55 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index 77ce7ea70..b8d53e724 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -479,7 +479,7 @@ void error(Ast *node, char const *fmt, ...) { } va_list va; va_start(va, fmt); - error_va(token, fmt, va); + error_va(token.pos, fmt, va); va_end(va); if (node != nullptr && node->file != nullptr) { node->file->error_count += 1; @@ -493,7 +493,7 @@ void error_no_newline(Ast *node, char const *fmt, ...) { } va_list va; va_start(va, fmt); - error_no_newline_va(token, fmt, va); + error_no_newline_va(token.pos, fmt, va); va_end(va); if (node != nullptr && node->file != nullptr) { node->file->error_count += 1; @@ -503,14 +503,14 @@ void error_no_newline(Ast *node, char const *fmt, ...) { void warning(Ast *node, char const *fmt, ...) { va_list va; va_start(va, fmt); - warning_va(ast_token(node), fmt, va); + warning_va(ast_token(node).pos, fmt, va); va_end(va); } void syntax_error(Ast *node, char const *fmt, ...) { va_list va; va_start(va, fmt); - syntax_error_va(ast_token(node), fmt, va); + syntax_error_va(ast_token(node).pos, fmt, va); va_end(va); if (node != nullptr && node->file != nullptr) { node->file->error_count += 1; diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 84d6ac9e2..3c44aaad4 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -470,18 +470,18 @@ bool show_error_on_line(TokenPos const &pos) { return false; } -void error_va(Token token, char const *fmt, va_list va) { +void error_va(TokenPos pos, char const *fmt, va_list va) { gb_mutex_lock(&global_error_collector.mutex); global_error_collector.count++; // NOTE(bill): Duplicate error, skip it - if (token.pos.line == 0) { + if (pos.line == 0) { error_out("Error: %s\n", gb_bprintf_va(fmt, va)); - } else if (global_error_collector.prev != token.pos) { - global_error_collector.prev = token.pos; + } else if (global_error_collector.prev != pos) { + global_error_collector.prev = pos; error_out("%s %s\n", - token_pos_to_string(token.pos), + token_pos_to_string(pos), gb_bprintf_va(fmt, va)); - show_error_on_line(token.pos); + show_error_on_line(pos); } gb_mutex_unlock(&global_error_collector.mutex); if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) { @@ -489,23 +489,23 @@ void error_va(Token token, char const *fmt, va_list va) { } } -void warning_va(Token token, char const *fmt, va_list va) { +void warning_va(TokenPos const &pos, char const *fmt, va_list va) { if (global_warnings_as_errors()) { - error_va(token, fmt, va); + error_va(pos, fmt, va); return; } gb_mutex_lock(&global_error_collector.mutex); global_error_collector.warning_count++; if (!global_ignore_warnings()) { // NOTE(bill): Duplicate error, skip it - if (token.pos.line == 0) { + if (pos.line == 0) { error_out("Warning: %s\n", gb_bprintf_va(fmt, va)); - } else if (global_error_collector.prev != token.pos) { - global_error_collector.prev = token.pos; + } else if (global_error_collector.prev != pos) { + global_error_collector.prev = pos; error_out("%s Warning: %s\n", - token_pos_to_string(token.pos), + token_pos_to_string(pos), gb_bprintf_va(fmt, va)); - show_error_on_line(token.pos); + show_error_on_line(pos); } } gb_mutex_unlock(&global_error_collector.mutex); @@ -518,16 +518,16 @@ void error_line_va(char const *fmt, va_list va) { gb_mutex_unlock(&global_error_collector.mutex); } -void error_no_newline_va(Token token, char const *fmt, va_list va) { +void error_no_newline_va(TokenPos const &pos, char const *fmt, va_list va) { gb_mutex_lock(&global_error_collector.mutex); global_error_collector.count++; // NOTE(bill): Duplicate error, skip it - if (token.pos.line == 0) { + if (pos.line == 0) { error_out("Error: %s", gb_bprintf_va(fmt, va)); - } else if (global_error_collector.prev != token.pos) { - global_error_collector.prev = token.pos; + } else if (global_error_collector.prev != pos) { + global_error_collector.prev = pos; error_out("%s %s", - token_pos_to_string(token.pos), + token_pos_to_string(pos), gb_bprintf_va(fmt, va)); } gb_mutex_unlock(&global_error_collector.mutex); @@ -537,17 +537,17 @@ void error_no_newline_va(Token token, char const *fmt, va_list va) { } -void syntax_error_va(Token token, char const *fmt, va_list va) { +void syntax_error_va(TokenPos const &pos, char const *fmt, va_list va) { gb_mutex_lock(&global_error_collector.mutex); global_error_collector.count++; // NOTE(bill): Duplicate error, skip it - if (global_error_collector.prev != token.pos) { - global_error_collector.prev = token.pos; + if (global_error_collector.prev != pos) { + global_error_collector.prev = pos; error_out("%s Syntax Error: %s\n", - token_pos_to_string(token.pos), + token_pos_to_string(pos), gb_bprintf_va(fmt, va)); - show_error_on_line(token.pos); - } else if (token.pos.line == 0) { + show_error_on_line(pos); + } else if (pos.line == 0) { error_out("Syntax Error: %s\n", gb_bprintf_va(fmt, va)); } @@ -557,22 +557,22 @@ void syntax_error_va(Token token, char const *fmt, va_list va) { } } -void syntax_warning_va(Token token, char const *fmt, va_list va) { +void syntax_warning_va(TokenPos const &pos, char const *fmt, va_list va) { if (global_warnings_as_errors()) { - syntax_error_va(token, fmt, va); + syntax_error_va(pos, fmt, va); return; } gb_mutex_lock(&global_error_collector.mutex); global_error_collector.warning_count++; if (!global_ignore_warnings()) { // NOTE(bill): Duplicate error, skip it - if (global_error_collector.prev != token.pos) { - global_error_collector.prev = token.pos; + if (global_error_collector.prev != pos) { + global_error_collector.prev = pos; error_out("%s Syntax Warning: %s\n", - token_pos_to_string(token.pos), + token_pos_to_string(pos), gb_bprintf_va(fmt, va)); - show_error_on_line(token.pos); - } else if (token.pos.line == 0) { + show_error_on_line(pos); + } else if (pos.line == 0) { error_out("Warning: %s\n", gb_bprintf_va(fmt, va)); } } @@ -581,17 +581,17 @@ void syntax_warning_va(Token token, char const *fmt, va_list va) { -void warning(Token token, char const *fmt, ...) { +void warning(Token const &token, char const *fmt, ...) { va_list va; va_start(va, fmt); - warning_va(token, fmt, va); + warning_va(token.pos, fmt, va); va_end(va); } -void error(Token token, char const *fmt, ...) { +void error(Token const &token, char const *fmt, ...) { va_list va; va_start(va, fmt); - error_va(token, fmt, va); + error_va(token.pos, fmt, va); va_end(va); } @@ -600,7 +600,7 @@ void error(TokenPos pos, char const *fmt, ...) { va_start(va, fmt); Token token = {}; token.pos = pos; - error_va(token, fmt, va); + error_va(pos, fmt, va); va_end(va); } @@ -612,26 +612,24 @@ void error_line(char const *fmt, ...) { } -void syntax_error(Token token, char const *fmt, ...) { +void syntax_error(Token const &token, char const *fmt, ...) { va_list va; va_start(va, fmt); - syntax_error_va(token, fmt, va); + syntax_error_va(token.pos, fmt, va); va_end(va); } void syntax_error(TokenPos pos, char const *fmt, ...) { va_list va; va_start(va, fmt); - Token token = {}; - token.pos = pos; - syntax_error_va(token, fmt, va); + syntax_error_va(pos, fmt, va); va_end(va); } -void syntax_warning(Token token, char const *fmt, ...) { +void syntax_warning(Token const &token, char const *fmt, ...) { va_list va; va_start(va, fmt); - syntax_warning_va(token, fmt, va); + syntax_warning_va(token.pos, fmt, va); va_end(va); } @@ -743,13 +741,14 @@ void tokenizer_err(Tokenizer *t, char const *msg, ...) { if (column < 1) { column = 1; } - Token token = {}; - token.pos.file_id = t->curr_file_id; - token.pos.line = t->line_count; - token.pos.column = cast(i32)column; + TokenPos pos = {}; + pos.file_id = t->curr_file_id; + pos.line = t->line_count; + pos.column = cast(i32)column; + pos.offset = cast(i32)(t->read_curr - t->start); va_start(va, msg); - syntax_error_va(token, msg, va); + syntax_error_va(pos, msg, va); va_end(va); t->error_count++; @@ -761,11 +760,9 @@ void tokenizer_err(Tokenizer *t, TokenPos const &pos, char const *msg, ...) { if (column < 1) { column = 1; } - Token token = {}; - token.pos = pos; va_start(va, msg); - syntax_error_va(token, msg, va); + syntax_error_va(pos, msg, va); va_end(va); t->error_count++; -- cgit v1.2.3 From 9c54ed57924bf8ff241b0bffebaeabfa541db24c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 14:15:57 +0100 Subject: Add range-based error messages to `-verbose-errors` Example: Cannot convert '(1 + 2)' to 'untyped bool' from 'untyped integer' x := (1 + 2) * true; ^~~~~~^ --- src/check_builtin.cpp | 4 +- src/check_expr.cpp | 17 +-- src/check_stmt.cpp | 2 +- src/check_type.cpp | 10 +- src/llvm_backend.cpp | 4 +- src/parser.cpp | 158 ++++++------------------ src/parser.hpp | 6 +- src/parser_pos.cpp | 331 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/tokenizer.cpp | 64 ++++++---- 9 files changed, 429 insertions(+), 167 deletions(-) create mode 100644 src/parser_pos.cpp diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index c9b911f92..1acb9732f 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -87,7 +87,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_DIRECTIVE: { ast_node(bd, BasicDirective, ce->proc); - String name = bd->name; + String name = bd->name.string; if (name == "defined") { break; } @@ -124,7 +124,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_DIRECTIVE: { ast_node(bd, BasicDirective, ce->proc); - String name = bd->name; + String name = bd->name.string; if (name == "location") { if (ce->args.count > 1) { error(ce->args[0], "'#location' expects either 0 or 1 arguments, got %td", ce->args.count); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 68f05084e..81fe3baa9 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5516,7 +5516,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr if (proc != nullptr && proc->kind == Ast_BasicDirective) { ast_node(bd, BasicDirective, proc); - String name = bd->name; + String name = bd->name.string; if (name == "location" || name == "assert" || name == "panic" || name == "defined" || name == "config" || name == "load") { operand->mode = Addressing_Builtin; operand->builtin_id = BuiltinProc_DIRECTIVE; @@ -6191,13 +6191,14 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_ast_node(bd, BasicDirective, node); o->mode = Addressing_Constant; - if (bd->name == "file") { + String name = bd->name.string; + if (name == "file") { o->type = t_untyped_string; o->value = exact_value_string(get_file_path_string(bd->token.pos.file_id)); - } else if (bd->name == "line") { + } else if (name == "line") { o->type = t_untyped_integer; o->value = exact_value_i64(bd->token.pos.line); - } else if (bd->name == "procedure") { + } else if (name == "procedure") { if (c->curr_proc_decl == nullptr) { error(node, "#procedure may only be used within procedures"); o->type = t_untyped_string; @@ -6206,7 +6207,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type o->type = t_untyped_string; o->value = exact_value_string(c->proc_name); } - } else if (bd->name == "caller_location") { + } else if (name == "caller_location") { init_core_source_code_location(c->checker); error(node, "#caller_location may only be used as a default argument parameter"); o->type = t_source_code_location; @@ -6373,7 +6374,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type if (cl->type->ArrayType.tag != nullptr) { Ast *tag = cl->type->ArrayType.tag; GB_ASSERT(tag->kind == Ast_BasicDirective); - String name = tag->BasicDirective.name; + String name = tag->BasicDirective.name.string; if (name == "soa") { error(node, "#soa arrays are not supported for compound literals"); return kind; @@ -6385,7 +6386,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type if (cl->elems.count > 0) { Ast *tag = cl->type->DynamicArrayType.tag; GB_ASSERT(tag->kind == Ast_BasicDirective); - String name = tag->BasicDirective.name; + String name = tag->BasicDirective.name.string; if (name == "soa") { error(node, "#soa arrays are not supported for compound literals"); return kind; @@ -8151,7 +8152,7 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) { case_ast_node(bd, BasicDirective, node); str = gb_string_append_rune(str, '#'); - str = string_append_string(str, bd->name); + str = string_append_string(str, bd->name.string); case_end; case_ast_node(ud, Undef, node); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index a62b55ab2..64d17a8c8 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -7,7 +7,7 @@ bool is_diverging_stmt(Ast *stmt) { return false; } if (expr->CallExpr.proc->kind == Ast_BasicDirective) { - String name = expr->CallExpr.proc->BasicDirective.name; + String name = expr->CallExpr.proc->BasicDirective.name.string; return name == "panic"; } Ast *proc = unparen_expr(expr->CallExpr.proc); diff --git a/src/check_type.cpp b/src/check_type.cpp index 24a3e0a59..419904876 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1191,7 +1191,7 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type * if (allow_caller_location && expr->kind == Ast_BasicDirective && - expr->BasicDirective.name == "caller_location") { + expr->BasicDirective.name.string == "caller_location") { init_core_source_code_location(ctx->checker); param_value.kind = ParameterValue_Location; o.type = t_source_code_location; @@ -2711,7 +2711,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t bool is_partial = false; if (at->tag != nullptr) { GB_ASSERT(at->tag->kind == Ast_BasicDirective); - String name = at->tag->BasicDirective.name; + String name = at->tag->BasicDirective.name.string; if (name == "partial") { is_partial = true; } else { @@ -2745,7 +2745,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t if (at->tag != nullptr) { GB_ASSERT(at->tag->kind == Ast_BasicDirective); - String name = at->tag->BasicDirective.name; + String name = at->tag->BasicDirective.name.string; if (name == "soa") { *type = make_soa_struct_fixed(ctx, e, at->elem, elem, count, generic_type); } else if (name == "simd") { @@ -2770,7 +2770,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t if (at->tag != nullptr) { GB_ASSERT(at->tag->kind == Ast_BasicDirective); - String name = at->tag->BasicDirective.name; + String name = at->tag->BasicDirective.name.string; if (name == "soa") { *type = make_soa_struct_slice(ctx, e, at->elem, elem); } else { @@ -2790,7 +2790,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t Type *elem = check_type(ctx, dat->elem); if (dat->tag != nullptr) { GB_ASSERT(dat->tag->kind == Ast_BasicDirective); - String name = dat->tag->BasicDirective.name; + String name = dat->tag->BasicDirective.name.string; if (name == "soa") { *type = make_soa_struct_dynamic_array(ctx, e, dat->elem, elem); } else { diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 8bd5aaa37..08c9445bd 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -8887,7 +8887,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, switch (id) { case BuiltinProc_DIRECTIVE: { ast_node(bd, BasicDirective, ce->proc); - String name = bd->name; + String name = bd->name.string; GB_ASSERT(name == "location"); String procedure = p->entity->token.string; TokenPos pos = ast_token(ce->proc).pos; @@ -11515,7 +11515,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { case_ast_node(bd, BasicDirective, expr); TokenPos pos = bd->token.pos; - GB_PANIC("Non-constant basic literal %s - %.*s", token_pos_to_string(pos), LIT(bd->name)); + GB_PANIC("Non-constant basic literal %s - %.*s", token_pos_to_string(pos), LIT(bd->name.string)); case_end; case_ast_node(i, Implicit, expr); diff --git a/src/parser.cpp b/src/parser.cpp index b8d53e724..a5180b4dd 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1,109 +1,4 @@ -Token ast_token(Ast *node) { - switch (node->kind) { - case Ast_Ident: return node->Ident.token; - case Ast_Implicit: return node->Implicit; - case Ast_Undef: return node->Undef; - case Ast_BasicLit: return node->BasicLit.token; - case Ast_BasicDirective: return node->BasicDirective.token; - case Ast_ProcGroup: return node->ProcGroup.token; - case Ast_ProcLit: return ast_token(node->ProcLit.type); - case Ast_CompoundLit: - if (node->CompoundLit.type != nullptr) { - return ast_token(node->CompoundLit.type); - } - return node->CompoundLit.open; - - case Ast_TagExpr: return node->TagExpr.token; - case Ast_BadExpr: return node->BadExpr.begin; - case Ast_UnaryExpr: return node->UnaryExpr.op; - case Ast_BinaryExpr: return ast_token(node->BinaryExpr.left); - case Ast_ParenExpr: return node->ParenExpr.open; - case Ast_CallExpr: return ast_token(node->CallExpr.proc); - case Ast_SelectorExpr: - if (node->SelectorExpr.selector != nullptr) { - return ast_token(node->SelectorExpr.selector); - } - return node->SelectorExpr.token; - case Ast_SelectorCallExpr: - if (node->SelectorCallExpr.expr != nullptr) { - return ast_token(node->SelectorCallExpr.expr); - } - return node->SelectorCallExpr.token; - case Ast_ImplicitSelectorExpr: - if (node->ImplicitSelectorExpr.selector != nullptr) { - return ast_token(node->ImplicitSelectorExpr.selector); - } - return node->ImplicitSelectorExpr.token; - case Ast_IndexExpr: return node->IndexExpr.open; - case Ast_SliceExpr: return node->SliceExpr.open; - case Ast_Ellipsis: return node->Ellipsis.token; - case Ast_FieldValue: return node->FieldValue.eq; - case Ast_DerefExpr: return node->DerefExpr.op; - case Ast_TernaryIfExpr: return ast_token(node->TernaryIfExpr.x); - case Ast_TernaryWhenExpr: return ast_token(node->TernaryWhenExpr.x); - case Ast_TypeAssertion: return ast_token(node->TypeAssertion.expr); - case Ast_TypeCast: return node->TypeCast.token; - case Ast_AutoCast: return node->AutoCast.token; - case Ast_InlineAsmExpr: return node->InlineAsmExpr.token; - - case Ast_BadStmt: return node->BadStmt.begin; - case Ast_EmptyStmt: return node->EmptyStmt.token; - case Ast_ExprStmt: return ast_token(node->ExprStmt.expr); - case Ast_TagStmt: return node->TagStmt.token; - case Ast_AssignStmt: return node->AssignStmt.op; - case Ast_BlockStmt: return node->BlockStmt.open; - case Ast_IfStmt: return node->IfStmt.token; - case Ast_WhenStmt: return node->WhenStmt.token; - case Ast_ReturnStmt: return node->ReturnStmt.token; - case Ast_ForStmt: return node->ForStmt.token; - case Ast_RangeStmt: return node->RangeStmt.token; - case Ast_UnrollRangeStmt: return node->UnrollRangeStmt.unroll_token; - case Ast_CaseClause: return node->CaseClause.token; - case Ast_SwitchStmt: return node->SwitchStmt.token; - case Ast_TypeSwitchStmt: return node->TypeSwitchStmt.token; - case Ast_DeferStmt: return node->DeferStmt.token; - case Ast_BranchStmt: return node->BranchStmt.token; - case Ast_UsingStmt: return node->UsingStmt.token; - - case Ast_BadDecl: return node->BadDecl.begin; - case Ast_Label: return node->Label.token; - - case Ast_ValueDecl: return ast_token(node->ValueDecl.names[0]); - case Ast_PackageDecl: return node->PackageDecl.token; - case Ast_ImportDecl: return node->ImportDecl.token; - case Ast_ForeignImportDecl: return node->ForeignImportDecl.token; - - case Ast_ForeignBlockDecl: return node->ForeignBlockDecl.token; - - case Ast_Attribute: - return node->Attribute.token; - - case Ast_Field: - if (node->Field.names.count > 0) { - return ast_token(node->Field.names[0]); - } - return ast_token(node->Field.type); - case Ast_FieldList: - return node->FieldList.token; - - case Ast_TypeidType: return node->TypeidType.token; - case Ast_HelperType: return node->HelperType.token; - case Ast_DistinctType: return node->DistinctType.token; - case Ast_PolyType: return node->PolyType.token; - case Ast_ProcType: return node->ProcType.token; - case Ast_RelativeType: return ast_token(node->RelativeType.tag); - case Ast_PointerType: return node->PointerType.token; - case Ast_ArrayType: return node->ArrayType.token; - case Ast_DynamicArrayType: return node->DynamicArrayType.token; - case Ast_StructType: return node->StructType.token; - case Ast_UnionType: return node->UnionType.token; - case Ast_EnumType: return node->EnumType.token; - case Ast_BitSetType: return node->BitSetType.token; - case Ast_MapType: return node->MapType.token; - } - - return empty_token; -} +#include "parser_pos.cpp" Token token_end_of_line(AstFile *f, Token tok) { u8 const *start = f->tokenizer.start + tok.pos.offset; @@ -474,12 +369,15 @@ Ast *clone_ast(Ast *node) { void error(Ast *node, char const *fmt, ...) { Token token = {}; + TokenPos end_pos = {}; if (node != nullptr) { token = ast_token(node); + end_pos = ast_end_pos(node); } + va_list va; va_start(va, fmt); - error_va(token.pos, fmt, va); + error_va(token.pos, end_pos, fmt, va); va_end(va); if (node != nullptr && node->file != nullptr) { node->file->error_count += 1; @@ -501,16 +399,28 @@ void error_no_newline(Ast *node, char const *fmt, ...) { } void warning(Ast *node, char const *fmt, ...) { + Token token = {}; + TokenPos end_pos = {}; + if (node != nullptr) { + token = ast_token(node); + end_pos = ast_end_pos(node); + } va_list va; va_start(va, fmt); - warning_va(ast_token(node).pos, fmt, va); + warning_va(token.pos, end_pos, fmt, va); va_end(va); } void syntax_error(Ast *node, char const *fmt, ...) { + Token token = {}; + TokenPos end_pos = {}; + if (node != nullptr) { + token = ast_token(node); + end_pos = ast_end_pos(node); + } va_list va; va_start(va, fmt); - syntax_error_va(ast_token(node).pos, fmt, va); + syntax_error_va(token.pos, end_pos, fmt, va); va_end(va); if (node != nullptr && node->file != nullptr) { node->file->error_count += 1; @@ -682,7 +592,7 @@ Ast *ast_basic_lit(AstFile *f, Token basic_lit) { return result; } -Ast *ast_basic_directive(AstFile *f, Token token, String name) { +Ast *ast_basic_directive(AstFile *f, Token token, Token name) { Ast *result = alloc_ast_node(f, Ast_BasicDirective); result->BasicDirective.token = token; result->BasicDirective.name = name; @@ -2042,27 +1952,27 @@ Ast *parse_operand(AstFile *f, bool lhs) { if (name.string == "type") { return ast_helper_type(f, token, parse_type(f)); } else if (name.string == "file") { - return ast_basic_directive(f, token, name.string); - } else if (name.string == "line") { return ast_basic_directive(f, token, name.string); - } else if (name.string == "procedure") { return ast_basic_directive(f, token, name.string); - } else if (name.string == "caller_location") { return ast_basic_directive(f, token, name.string); + return ast_basic_directive(f, token, name); + } else if (name.string == "line") { return ast_basic_directive(f, token, name); + } else if (name.string == "procedure") { return ast_basic_directive(f, token, name); + } else if (name.string == "caller_location") { return ast_basic_directive(f, token, name); } else if (name.string == "location") { - Ast *tag = ast_basic_directive(f, token, name.string); + Ast *tag = ast_basic_directive(f, token, name); return parse_call_expr(f, tag); } else if (name.string == "load") { - Ast *tag = ast_basic_directive(f, token, name.string); + Ast *tag = ast_basic_directive(f, token, name); return parse_call_expr(f, tag); } else if (name.string == "assert") { - Ast *tag = ast_basic_directive(f, token, name.string); + Ast *tag = ast_basic_directive(f, token, name); return parse_call_expr(f, tag); } else if (name.string == "defined") { - Ast *tag = ast_basic_directive(f, token, name.string); + Ast *tag = ast_basic_directive(f, token, name); return parse_call_expr(f, tag); } else if (name.string == "config") { - Ast *tag = ast_basic_directive(f, token, name.string); + Ast *tag = ast_basic_directive(f, token, name); return parse_call_expr(f, tag); } else if (name.string == "soa" || name.string == "simd") { - Ast *tag = ast_basic_directive(f, token, name.string); + Ast *tag = ast_basic_directive(f, token, name); Ast *original_type = parse_type(f); Ast *type = unparen_expr(original_type); switch (type->kind) { @@ -2074,7 +1984,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { } return original_type; } else if (name.string == "partial") { - Ast *tag = ast_basic_directive(f, token, name.string); + Ast *tag = ast_basic_directive(f, token, name); Ast *original_type = parse_type(f); Ast *type = unparen_expr(original_type); switch (type->kind) { @@ -2107,7 +2017,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { } return operand; } else if (name.string == "relative") { - Ast *tag = ast_basic_directive(f, token, name.string); + Ast *tag = ast_basic_directive(f, token, name); tag = parse_call_expr(f, tag); Ast *type = parse_type(f); return ast_relative_type(f, tag, type); @@ -4554,10 +4464,10 @@ Ast *parse_stmt(AstFile *f) { } return s; } else if (tag == "assert") { - Ast *t = ast_basic_directive(f, hash_token, tag); + Ast *t = ast_basic_directive(f, hash_token, name); return ast_expr_stmt(f, parse_call_expr(f, t)); } else if (tag == "panic") { - Ast *t = ast_basic_directive(f, hash_token, tag); + Ast *t = ast_basic_directive(f, hash_token, name); return ast_expr_stmt(f, parse_call_expr(f, t)); } else if (name.string == "force_inline" || name.string == "force_no_inline") { diff --git a/src/parser.hpp b/src/parser.hpp index 77d5f1b02..89f714aaa 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -286,8 +286,8 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = { Token token; \ }) \ AST_KIND(BasicDirective, "basic directive", struct { \ - Token token; \ - String name; \ + Token token; \ + Token name; \ }) \ AST_KIND(Ellipsis, "ellipsis", struct { \ Token token; \ @@ -324,7 +324,7 @@ AST_KIND(_ExprBegin, "", bool) \ AST_KIND(ImplicitSelectorExpr, "implicit selector expression", struct { Token token; Ast *selector; }) \ AST_KIND(SelectorCallExpr, "selector call expression", struct { Token token; Ast *expr, *call; bool modified_call; }) \ AST_KIND(IndexExpr, "index expression", struct { Ast *expr, *index; Token open, close; }) \ - AST_KIND(DerefExpr, "dereference expression", struct { Token op; Ast *expr; }) \ + AST_KIND(DerefExpr, "dereference expression", struct { Ast *expr; Token op; }) \ AST_KIND(SliceExpr, "slice expression", struct { \ Ast *expr; \ Token open, close; \ diff --git a/src/parser_pos.cpp b/src/parser_pos.cpp new file mode 100644 index 000000000..c5ad89604 --- /dev/null +++ b/src/parser_pos.cpp @@ -0,0 +1,331 @@ +Token ast_token(Ast *node) { + switch (node->kind) { + case Ast_Ident: return node->Ident.token; + case Ast_Implicit: return node->Implicit; + case Ast_Undef: return node->Undef; + case Ast_BasicLit: return node->BasicLit.token; + case Ast_BasicDirective: return node->BasicDirective.token; + case Ast_ProcGroup: return node->ProcGroup.token; + case Ast_ProcLit: return ast_token(node->ProcLit.type); + case Ast_CompoundLit: + if (node->CompoundLit.type != nullptr) { + return ast_token(node->CompoundLit.type); + } + return node->CompoundLit.open; + + case Ast_TagExpr: return node->TagExpr.token; + case Ast_BadExpr: return node->BadExpr.begin; + case Ast_UnaryExpr: return node->UnaryExpr.op; + case Ast_BinaryExpr: return ast_token(node->BinaryExpr.left); + case Ast_ParenExpr: return node->ParenExpr.open; + case Ast_CallExpr: return ast_token(node->CallExpr.proc); + case Ast_SelectorExpr: + if (node->SelectorExpr.selector != nullptr) { + return ast_token(node->SelectorExpr.selector); + } + return node->SelectorExpr.token; + case Ast_SelectorCallExpr: + if (node->SelectorCallExpr.expr != nullptr) { + return ast_token(node->SelectorCallExpr.expr); + } + return node->SelectorCallExpr.token; + case Ast_ImplicitSelectorExpr: + if (node->ImplicitSelectorExpr.selector != nullptr) { + return ast_token(node->ImplicitSelectorExpr.selector); + } + return node->ImplicitSelectorExpr.token; + case Ast_IndexExpr: return node->IndexExpr.open; + case Ast_SliceExpr: return node->SliceExpr.open; + case Ast_Ellipsis: return node->Ellipsis.token; + case Ast_FieldValue: return node->FieldValue.eq; + case Ast_DerefExpr: return node->DerefExpr.op; + case Ast_TernaryIfExpr: return ast_token(node->TernaryIfExpr.x); + case Ast_TernaryWhenExpr: return ast_token(node->TernaryWhenExpr.x); + case Ast_TypeAssertion: return ast_token(node->TypeAssertion.expr); + case Ast_TypeCast: return node->TypeCast.token; + case Ast_AutoCast: return node->AutoCast.token; + case Ast_InlineAsmExpr: return node->InlineAsmExpr.token; + + case Ast_BadStmt: return node->BadStmt.begin; + case Ast_EmptyStmt: return node->EmptyStmt.token; + case Ast_ExprStmt: return ast_token(node->ExprStmt.expr); + case Ast_TagStmt: return node->TagStmt.token; + case Ast_AssignStmt: return node->AssignStmt.op; + case Ast_BlockStmt: return node->BlockStmt.open; + case Ast_IfStmt: return node->IfStmt.token; + case Ast_WhenStmt: return node->WhenStmt.token; + case Ast_ReturnStmt: return node->ReturnStmt.token; + case Ast_ForStmt: return node->ForStmt.token; + case Ast_RangeStmt: return node->RangeStmt.token; + case Ast_UnrollRangeStmt: return node->UnrollRangeStmt.unroll_token; + case Ast_CaseClause: return node->CaseClause.token; + case Ast_SwitchStmt: return node->SwitchStmt.token; + case Ast_TypeSwitchStmt: return node->TypeSwitchStmt.token; + case Ast_DeferStmt: return node->DeferStmt.token; + case Ast_BranchStmt: return node->BranchStmt.token; + case Ast_UsingStmt: return node->UsingStmt.token; + + case Ast_BadDecl: return node->BadDecl.begin; + case Ast_Label: return node->Label.token; + + case Ast_ValueDecl: return ast_token(node->ValueDecl.names[0]); + case Ast_PackageDecl: return node->PackageDecl.token; + case Ast_ImportDecl: return node->ImportDecl.token; + case Ast_ForeignImportDecl: return node->ForeignImportDecl.token; + + case Ast_ForeignBlockDecl: return node->ForeignBlockDecl.token; + + case Ast_Attribute: + return node->Attribute.token; + + case Ast_Field: + if (node->Field.names.count > 0) { + return ast_token(node->Field.names[0]); + } + return ast_token(node->Field.type); + case Ast_FieldList: + return node->FieldList.token; + + case Ast_TypeidType: return node->TypeidType.token; + case Ast_HelperType: return node->HelperType.token; + case Ast_DistinctType: return node->DistinctType.token; + case Ast_PolyType: return node->PolyType.token; + case Ast_ProcType: return node->ProcType.token; + case Ast_RelativeType: return ast_token(node->RelativeType.tag); + case Ast_PointerType: return node->PointerType.token; + case Ast_ArrayType: return node->ArrayType.token; + case Ast_DynamicArrayType: return node->DynamicArrayType.token; + case Ast_StructType: return node->StructType.token; + case Ast_UnionType: return node->UnionType.token; + case Ast_EnumType: return node->EnumType.token; + case Ast_BitSetType: return node->BitSetType.token; + case Ast_MapType: return node->MapType.token; + } + + return empty_token; +} + +TokenPos token_pos_end(Token const &token) { + TokenPos pos = token.pos; + pos.offset += cast(i32)token.string.len; + for (isize i = 0; i < token.string.len; i++) { + // TODO(bill): This assumes ASCII + char c = token.string[i]; + if (c == '\n') { + pos.line += 1; + pos.column = 1; + } else { + pos.column += 1; + } + } + return pos; +} + +Token ast_end_token(Ast *node) { + GB_ASSERT(node != nullptr); + + switch (node->kind) { + case Ast_Ident: return node->Ident.token; + case Ast_Implicit: return node->Implicit; + case Ast_Undef: return node->Undef; + case Ast_BasicLit: return node->BasicLit.token; + case Ast_BasicDirective: return node->BasicDirective.token; + case Ast_ProcGroup: return node->ProcGroup.close; + case Ast_ProcLit: + if (node->ProcLit.body) { + return ast_end_token(node->ProcLit.body); + } + return ast_end_token(node->ProcLit.type); + case Ast_CompoundLit: + return node->CompoundLit.close; + + case Ast_BadExpr: return node->BadExpr.end; + case Ast_TagExpr: return ast_end_token(node->TagExpr.expr); + case Ast_UnaryExpr: return ast_end_token(node->UnaryExpr.expr); + case Ast_BinaryExpr: return ast_end_token(node->BinaryExpr.right); + case Ast_ParenExpr: return node->ParenExpr.close; + case Ast_CallExpr: return node->CallExpr.close; + case Ast_SelectorExpr: + return ast_end_token(node->SelectorExpr.selector); + case Ast_SelectorCallExpr: + return ast_end_token(node->SelectorCallExpr.call); + case Ast_ImplicitSelectorExpr: + return ast_end_token(node->SelectorExpr.selector); + case Ast_IndexExpr: return node->IndexExpr.close; + case Ast_SliceExpr: return node->SliceExpr.close; + case Ast_Ellipsis: + if (node->Ellipsis.expr) { + return ast_end_token(node->Ellipsis.expr); + } + return node->Ellipsis.token; + case Ast_FieldValue: return ast_end_token(node->FieldValue.value); + case Ast_DerefExpr: return node->DerefExpr.op; + case Ast_TernaryIfExpr: return ast_end_token(node->TernaryIfExpr.y); + case Ast_TernaryWhenExpr: return ast_end_token(node->TernaryWhenExpr.y); + case Ast_TypeAssertion: return ast_end_token(node->TypeAssertion.type); + case Ast_TypeCast: return ast_end_token(node->TypeCast.expr); + case Ast_AutoCast: return ast_end_token(node->AutoCast.expr); + case Ast_InlineAsmExpr: return node->InlineAsmExpr.close; + + case Ast_BadStmt: return node->BadStmt.end; + case Ast_EmptyStmt: return node->EmptyStmt.token; + case Ast_ExprStmt: return ast_end_token(node->ExprStmt.expr); + case Ast_TagStmt: return ast_end_token(node->TagStmt.stmt); + case Ast_AssignStmt: + if (node->AssignStmt.rhs.count > 0) { + return ast_end_token(node->AssignStmt.rhs[node->AssignStmt.rhs.count-1]); + } + return node->AssignStmt.op; + case Ast_BlockStmt: return node->BlockStmt.close; + case Ast_IfStmt: + if (node->IfStmt.else_stmt) { + return ast_end_token(node->IfStmt.else_stmt); + } + return ast_end_token(node->IfStmt.body); + case Ast_WhenStmt: + if (node->WhenStmt.else_stmt) { + return ast_end_token(node->WhenStmt.else_stmt); + } + return ast_end_token(node->WhenStmt.body); + case Ast_ReturnStmt: + if (node->ReturnStmt.results.count > 0) { + return ast_end_token(node->ReturnStmt.results[node->ReturnStmt.results.count-1]); + } + return node->ReturnStmt.token; + case Ast_ForStmt: return ast_end_token(node->ForStmt.body); + case Ast_RangeStmt: return ast_end_token(node->RangeStmt.body); + case Ast_UnrollRangeStmt: return ast_end_token(node->UnrollRangeStmt.body); + case Ast_CaseClause: + if (node->CaseClause.stmts.count) { + return ast_end_token(node->CaseClause.stmts[node->CaseClause.stmts.count-1]); + } else if (node->CaseClause.list.count) { + return ast_end_token(node->CaseClause.list[node->CaseClause.list.count-1]); + } + return node->CaseClause.token; + case Ast_SwitchStmt: return ast_end_token(node->SwitchStmt.body); + case Ast_TypeSwitchStmt: return ast_end_token(node->TypeSwitchStmt.body); + case Ast_DeferStmt: return ast_end_token(node->DeferStmt.stmt); + case Ast_BranchStmt: + if (node->BranchStmt.label) { + return ast_end_token(node->BranchStmt.label); + } + return node->BranchStmt.token; + case Ast_UsingStmt: + if (node->UsingStmt.list.count > 0) { + return ast_end_token(node->UsingStmt.list[node->UsingStmt.list.count-1]); + } + return node->UsingStmt.token; + + case Ast_BadDecl: return node->BadDecl.end; + case Ast_Label: + if (node->Label.name) { + return ast_end_token(node->Label.name); + } + return node->Label.token; + + case Ast_ValueDecl: + if (node->ValueDecl.values.count > 0) { + return ast_end_token(node->ValueDecl.values[node->ValueDecl.values.count-1]); + } + if (node->ValueDecl.type) { + return ast_end_token(node->ValueDecl.type); + } + if (node->ValueDecl.names.count > 0) { + return ast_end_token(node->ValueDecl.names[node->ValueDecl.names.count-1]); + } + return {}; + + case Ast_PackageDecl: return node->PackageDecl.name; + case Ast_ImportDecl: return node->ImportDecl.relpath; + case Ast_ForeignImportDecl: + if (node->ForeignImportDecl.filepaths.count > 0) { + return node->ForeignImportDecl.filepaths[node->ForeignImportDecl.filepaths.count-1]; + } + if (node->ForeignImportDecl.library_name.kind != Token_Invalid) { + return node->ForeignImportDecl.library_name; + } + return node->ForeignImportDecl.token; + + case Ast_ForeignBlockDecl: + return ast_end_token(node->ForeignBlockDecl.body); + + case Ast_Attribute: + if (node->Attribute.close.kind != Token_Invalid) { + return node->Attribute.close; + } + return ast_end_token(node->Attribute.elems[node->Attribute.elems.count-1]); + + case Ast_Field: + if (node->Field.tag.kind != Token_Invalid) { + return node->Field.tag; + } + if (node->Field.default_value) { + return ast_end_token(node->Field.default_value); + } + if (node->Field.type) { + return ast_end_token(node->Field.type); + } + return ast_end_token(node->Field.names[node->Field.names.count-1]); + case Ast_FieldList: + if (node->FieldList.list.count > 0) { + return ast_end_token(node->FieldList.list[node->FieldList.list.count-1]); + } + return node->FieldList.token; + + case Ast_TypeidType: + if (node->TypeidType.specialization) { + return ast_end_token(node->TypeidType.specialization); + } + return node->TypeidType.token; + case Ast_HelperType: return ast_end_token(node->HelperType.type); + case Ast_DistinctType: return ast_end_token(node->DistinctType.type); + case Ast_PolyType: + if (node->PolyType.specialization) { + return ast_end_token(node->PolyType.specialization); + } + return ast_end_token(node->PolyType.type); + case Ast_ProcType: + if (node->ProcType.results) { + return ast_end_token(node->ProcType.results); + } + if (node->ProcType.params) { + return ast_end_token(node->ProcType.params); + } + return node->ProcType.token; + case Ast_RelativeType: + return ast_end_token(node->RelativeType.type); + case Ast_PointerType: return ast_end_token(node->PointerType.type); + case Ast_ArrayType: return ast_end_token(node->ArrayType.elem); + case Ast_DynamicArrayType: return ast_end_token(node->DynamicArrayType.elem); + case Ast_StructType: + if (node->StructType.fields.count > 0) { + return ast_end_token(node->StructType.fields[node->StructType.fields.count-1]); + } + return node->StructType.token; + case Ast_UnionType: + if (node->UnionType.variants.count > 0) { + return ast_end_token(node->UnionType.variants[node->UnionType.variants.count-1]); + } + return node->UnionType.token; + case Ast_EnumType: + if (node->EnumType.fields.count > 0) { + return ast_end_token(node->EnumType.fields[node->EnumType.fields.count-1]); + } + if (node->EnumType.base_type) { + return ast_end_token(node->EnumType.base_type); + } + return node->EnumType.token; + case Ast_BitSetType: + if (node->BitSetType.underlying) { + return ast_end_token(node->BitSetType.underlying); + } + return ast_end_token(node->BitSetType.elem); + case Ast_MapType: return ast_end_token(node->MapType.value); + } + + return empty_token; +} + +TokenPos ast_end_pos(Ast *node) { + return token_pos_end(ast_end_token(node)); +} diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 3c44aaad4..826fccc04 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -423,7 +423,7 @@ void error_out(char const *fmt, ...) { } -bool show_error_on_line(TokenPos const &pos) { +bool show_error_on_line(TokenPos const &pos, TokenPos end) { if (!show_error_line()) { return false; } @@ -435,6 +435,8 @@ bool show_error_on_line(TokenPos const &pos) { if (the_line != nullptr) { String line = make_string(cast(u8 const *)the_line, gb_string_length(the_line)); + // TODO(bill): This assumes ASCII + enum { MAX_LINE_LENGTH = 76, MAX_TAB_WIDTH = 8, @@ -462,15 +464,33 @@ bool show_error_on_line(TokenPos const &pos) { } error_out("\n\t"); - for (i32 i = 0; i < offset; i++) error_out(" "); - error_out("^\n"); - error_out("\n"); + for (i32 i = 0; i < offset; i++) { + error_out(" "); + } + error_out("^"); + if (end.file_id == pos.file_id) { + if (end.line > pos.line) { + for (i32 i = offset; i < line.len; i++) { + error_out("~"); + } + } else if (end.line == pos.line && end.column > pos.column) { + i32 length = gb_min(end.offset - pos.offset, cast(i32)(line.len-offset)); + for (i32 i = 1; i < length-1; i++) { + error_out("~"); + } + if (length > 1) { + error_out("^"); + } + } + } + + error_out("\n\n"); return true; } return false; } -void error_va(TokenPos pos, char const *fmt, va_list va) { +void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) { gb_mutex_lock(&global_error_collector.mutex); global_error_collector.count++; // NOTE(bill): Duplicate error, skip it @@ -481,7 +501,7 @@ void error_va(TokenPos pos, char const *fmt, va_list va) { error_out("%s %s\n", token_pos_to_string(pos), gb_bprintf_va(fmt, va)); - show_error_on_line(pos); + show_error_on_line(pos, end); } gb_mutex_unlock(&global_error_collector.mutex); if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) { @@ -489,9 +509,9 @@ void error_va(TokenPos pos, char const *fmt, va_list va) { } } -void warning_va(TokenPos const &pos, char const *fmt, va_list va) { +void warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) { if (global_warnings_as_errors()) { - error_va(pos, fmt, va); + error_va(pos, end, fmt, va); return; } gb_mutex_lock(&global_error_collector.mutex); @@ -505,7 +525,7 @@ void warning_va(TokenPos const &pos, char const *fmt, va_list va) { error_out("%s Warning: %s\n", token_pos_to_string(pos), gb_bprintf_va(fmt, va)); - show_error_on_line(pos); + show_error_on_line(pos, end); } } gb_mutex_unlock(&global_error_collector.mutex); @@ -537,7 +557,7 @@ void error_no_newline_va(TokenPos const &pos, char const *fmt, va_list va) { } -void syntax_error_va(TokenPos const &pos, char const *fmt, va_list va) { +void syntax_error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) { gb_mutex_lock(&global_error_collector.mutex); global_error_collector.count++; // NOTE(bill): Duplicate error, skip it @@ -546,7 +566,7 @@ void syntax_error_va(TokenPos const &pos, char const *fmt, va_list va) { error_out("%s Syntax Error: %s\n", token_pos_to_string(pos), gb_bprintf_va(fmt, va)); - show_error_on_line(pos); + show_error_on_line(pos, end); } else if (pos.line == 0) { error_out("Syntax Error: %s\n", gb_bprintf_va(fmt, va)); } @@ -557,9 +577,9 @@ void syntax_error_va(TokenPos const &pos, char const *fmt, va_list va) { } } -void syntax_warning_va(TokenPos const &pos, char const *fmt, va_list va) { +void syntax_warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) { if (global_warnings_as_errors()) { - syntax_error_va(pos, fmt, va); + syntax_error_va(pos, end, fmt, va); return; } gb_mutex_lock(&global_error_collector.mutex); @@ -571,7 +591,7 @@ void syntax_warning_va(TokenPos const &pos, char const *fmt, va_list va) { error_out("%s Syntax Warning: %s\n", token_pos_to_string(pos), gb_bprintf_va(fmt, va)); - show_error_on_line(pos); + show_error_on_line(pos, end); } else if (pos.line == 0) { error_out("Warning: %s\n", gb_bprintf_va(fmt, va)); } @@ -584,14 +604,14 @@ void syntax_warning_va(TokenPos const &pos, char const *fmt, va_list va) { void warning(Token const &token, char const *fmt, ...) { va_list va; va_start(va, fmt); - warning_va(token.pos, fmt, va); + warning_va(token.pos, {}, fmt, va); va_end(va); } void error(Token const &token, char const *fmt, ...) { va_list va; va_start(va, fmt); - error_va(token.pos, fmt, va); + error_va(token.pos, {}, fmt, va); va_end(va); } @@ -600,7 +620,7 @@ void error(TokenPos pos, char const *fmt, ...) { va_start(va, fmt); Token token = {}; token.pos = pos; - error_va(pos, fmt, va); + error_va(pos, {}, fmt, va); va_end(va); } @@ -615,21 +635,21 @@ void error_line(char const *fmt, ...) { void syntax_error(Token const &token, char const *fmt, ...) { va_list va; va_start(va, fmt); - syntax_error_va(token.pos, fmt, va); + syntax_error_va(token.pos, {}, fmt, va); va_end(va); } void syntax_error(TokenPos pos, char const *fmt, ...) { va_list va; va_start(va, fmt); - syntax_error_va(pos, fmt, va); + syntax_error_va(pos, {}, fmt, va); va_end(va); } void syntax_warning(Token const &token, char const *fmt, ...) { va_list va; va_start(va, fmt); - syntax_warning_va(token.pos, fmt, va); + syntax_warning_va(token.pos, {}, fmt, va); va_end(va); } @@ -748,7 +768,7 @@ void tokenizer_err(Tokenizer *t, char const *msg, ...) { pos.offset = cast(i32)(t->read_curr - t->start); va_start(va, msg); - syntax_error_va(pos, msg, va); + syntax_error_va(pos, {}, msg, va); va_end(va); t->error_count++; @@ -762,7 +782,7 @@ void tokenizer_err(Tokenizer *t, TokenPos const &pos, char const *msg, ...) { } va_start(va, msg); - syntax_error_va(pos, msg, va); + syntax_error_va(pos, {}, msg, va); va_end(va); t->error_count++; -- cgit v1.2.3 From 4d580ed693edab796498acf5eba5a1c4d88c9b2b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 14:26:20 +0100 Subject: Keep -vet happy --- examples/demo/demo.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 8cf3cfd9a..af67df3e9 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -1868,7 +1868,7 @@ soa_struct_layout :: proc() { // iterate over the #soa slice for v, i in s { - fmt.println(v); // exactly the same as s[i] + fmt.println(v, i); // exactly the same as s[i] // NOTE: 'v' is NOT a temporary value but has a specialized addressing mode // which means that when accessing v.a etc, it does the correct transformation // internally: -- cgit v1.2.3 From 92abddddc5ca4be622e93856c7246159b594e9e9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 15:02:36 +0100 Subject: Be a little more correct with the temporary Ast node --- src/check_stmt.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 64d17a8c8..7772b5c97 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1537,8 +1537,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { } Operand lhs = {Addressing_Invalid}; Operand rhs = {Addressing_Invalid}; - Ast binary_expr = {Ast_BinaryExpr}; - ast_node(be, BinaryExpr, &binary_expr); + Ast *binary_expr = alloc_ast_node(node->file, Ast_BinaryExpr); + ast_node(be, BinaryExpr, binary_expr); be->op = op; be->op.kind = cast(TokenKind)(cast(i32)be->op.kind - (Token_AddEq - Token_Add)); // NOTE(bill): Only use the first one will be used @@ -1546,7 +1546,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { be->right = as->rhs[0]; check_expr(ctx, &lhs, as->lhs[0]); - check_binary_expr(ctx, &rhs, &binary_expr, nullptr, true); + check_binary_expr(ctx, &rhs, binary_expr, nullptr, true); if (rhs.mode == Addressing_Invalid) { return; } -- cgit v1.2.3