diff options
| author | Daniel Gavin <danielgavin5@hotmail.com> | 2021-05-20 12:15:14 +0200 |
|---|---|---|
| committer | Daniel Gavin <danielgavin5@hotmail.com> | 2021-05-20 12:15:14 +0200 |
| commit | 44ee0f2cdc773fc3ba3261de076b8a8a7fb64f03 (patch) | |
| tree | c408237d1bd2e6bfcb0c5526d9c0d8cdb97bbf69 /src | |
| parent | 50035f257eb33769211ca49a30c51f9a20440a0e (diff) | |
| parent | 92abddddc5ca4be622e93856c7246159b594e9e9 (diff) | |
Merge branch 'master' into prototype-fmt
Diffstat (limited to 'src')
| -rw-r--r-- | src/build_settings.cpp | 70 | ||||
| -rw-r--r-- | src/check_builtin.cpp | 135 | ||||
| -rw-r--r-- | src/check_decl.cpp | 34 | ||||
| -rw-r--r-- | src/check_expr.cpp | 548 | ||||
| -rw-r--r-- | src/check_stmt.cpp | 125 | ||||
| -rw-r--r-- | src/check_type.cpp | 104 | ||||
| -rw-r--r-- | src/checker.cpp | 324 | ||||
| -rw-r--r-- | src/checker.hpp | 2 | ||||
| -rw-r--r-- | src/checker_builtin_procs.hpp | 14 | ||||
| -rw-r--r-- | src/docs_writer.cpp | 3 | ||||
| -rw-r--r-- | src/entity.cpp | 2 | ||||
| -rw-r--r-- | src/llvm_abi.cpp | 203 | ||||
| -rw-r--r-- | src/llvm_backend.cpp | 2487 | ||||
| -rw-r--r-- | src/llvm_backend.hpp | 26 | ||||
| -rw-r--r-- | src/llvm_backend_opt.cpp | 131 | ||||
| -rw-r--r-- | src/main.cpp | 213 | ||||
| -rw-r--r-- | src/parser.cpp | 334 | ||||
| -rw-r--r-- | src/parser.hpp | 49 | ||||
| -rw-r--r-- | src/parser_pos.cpp | 331 | ||||
| -rw-r--r-- | src/thread_pool.cpp | 3 | ||||
| -rw-r--r-- | src/tokenizer.cpp | 221 | ||||
| -rw-r--r-- | src/types.cpp | 66 |
22 files changed, 3271 insertions, 2154 deletions
diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 82ed24f83..0207221bc 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; @@ -202,18 +200,24 @@ 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; bool linker_map_file; + bool use_separate_modules; + u32 cmd_doc_flags; Array<String> extra_packages; QueryDataSetSettings query_data_set_settings; + StringSet test_names; + gbAffinity affinity; isize thread_count; @@ -744,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) { @@ -806,22 +813,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 +833,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 +849,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 +861,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/check_builtin.cpp b/src/check_builtin.cpp index 00963487f..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); @@ -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"); @@ -2017,11 +2021,92 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } } + operand->mode = Addressing_OptionalOk; + operand->type = default_type(x.type); + } + 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 = make_optional_ok_type(default_type(x.type), false); // Just reusing this procedure, it's not optional + operand->type = default_type(x.type); } 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: case BuiltinProc_atomic_fence_rel: @@ -2149,8 +2234,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; @@ -2425,6 +2510,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/check_decl.cpp b/src/check_decl.cpp index f008317ad..0aef40546 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; - } - } } @@ -352,16 +341,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 @@ -375,6 +365,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; @@ -408,6 +399,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; } @@ -896,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/check_expr.cpp b/src/check_expr.cpp index 6c4a78e53..81fe3baa9 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; } } @@ -1113,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; } @@ -1130,7 +1133,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)); } @@ -1141,6 +1144,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 +1201,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)); @@ -2212,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; } @@ -2379,9 +2387,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) { @@ -2391,6 +2405,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; @@ -2503,6 +2520,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); @@ -2520,8 +2541,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 @@ -2628,11 +2655,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; } @@ -2852,13 +2890,33 @@ 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); } 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; @@ -3146,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) { @@ -3896,6 +3954,16 @@ bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> 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; @@ -4010,6 +4078,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; @@ -4034,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)); @@ -4203,8 +4291,13 @@ 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; + } } + } else if (show_error) { + check_assignment(c, &o, t, str_lit("argument")); } score += s; @@ -4220,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) { @@ -4258,6 +4354,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)) { @@ -4266,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); } } } @@ -4480,6 +4580,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; } @@ -5414,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; @@ -5475,6 +5577,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; @@ -5509,9 +5620,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; } } @@ -5661,7 +5778,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: @@ -5849,8 +5966,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); @@ -5861,7 +5979,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); } @@ -5896,6 +6014,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<Operand>(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); @@ -5991,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; @@ -6006,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; @@ -6061,88 +6262,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); @@ -6255,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; @@ -6267,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; @@ -7279,10 +7398,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); - check_expr_base(c, o, ue->expr, type_hint); - c->unary_address_hint = prev_unary_address_hint; + Type *th = type_hint; + if (ue->op.kind == Token_And) { + th = type_deref(th); + } + check_expr_base(c, o, ue->expr, th); node->viral_state_flags |= ue->expr->viral_state_flags; if (o->mode == Addressing_Invalid) { @@ -7444,68 +7564,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); @@ -7542,47 +7601,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<Ast *>(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); @@ -7721,37 +7739,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<Ast *>(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; @@ -7780,10 +7767,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 } @@ -7818,6 +7802,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++) { @@ -8158,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); @@ -8222,20 +8216,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); @@ -8525,8 +8521,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, "..."); @@ -8539,6 +8545,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, "..."); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index a3c9a529c..7772b5c97 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); @@ -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; } @@ -960,9 +961,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)) { @@ -1400,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; } @@ -1454,53 +1512,6 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { auto lhs_to_ignore = array_make<bool>(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<Ast *>(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; @@ -1526,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 @@ -1535,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; } @@ -1632,7 +1643,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/check_type.cpp b/src/check_type.cpp index aef1ddc7a..419904876 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, @@ -944,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)); } @@ -1203,10 +1191,15 @@ 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; + + 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 +1207,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 +1219,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) { @@ -1253,8 +1242,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 { @@ -1267,12 +1259,14 @@ 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; + } else { + *out_type_ = default_type(o.type); + } } - if (out_type_) *out_type_ = default_type(o.type); - return param_value; } @@ -1389,6 +1383,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 +1405,21 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is type = t_invalid; } + 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; + } + } + if (p->flags&FieldFlag_c_vararg) { if (p->type == nullptr || @@ -2109,6 +2121,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); @@ -2511,16 +2529,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; } @@ -2703,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 { @@ -2737,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") { @@ -2762,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 { @@ -2782,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 { @@ -2883,16 +2891,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/checker.cpp b/src/checker.cpp index 878435d67..21ca4c398 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)) { @@ -960,7 +947,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 +964,6 @@ Entity *entity_of_node(Ast *expr) { return nullptr; } - DeclInfo *decl_info_of_entity(Entity *e) { if (e != nullptr) { return e->decl_info; @@ -1870,7 +1860,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; } @@ -2675,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; } @@ -4498,6 +4289,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) @@ -4572,6 +4395,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/checker.hpp b/src/checker.hpp index 4ff72717d..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) { @@ -335,7 +334,6 @@ struct CheckerContext { Scope * polymorphic_scope; Ast *assignment_lhs_hint; - Ast *unary_address_hint; }; struct Checker { diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 98ef5180b..b69bacd30 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -56,6 +56,11 @@ enum BuiltinProcId { BuiltinProc_overflow_sub, BuiltinProc_overflow_mul, + BuiltinProc_sqrt, + + BuiltinProc_mem_copy, + BuiltinProc_mem_copy_non_overlapping, + BuiltinProc_volatile_store, BuiltinProc_volatile_load, @@ -197,6 +202,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, @@ -276,6 +283,11 @@ 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("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}, @@ -415,6 +427,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}, 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/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_abi.cpp b/src/llvm_abi.cpp index 65e3b2c58..aba85ae83 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); @@ -271,110 +286,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); @@ -432,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(t, nullptr); } } else { args[i] = non_struct(c, t, false); @@ -452,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); @@ -523,8 +434,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; } @@ -610,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 { @@ -642,30 +559,48 @@ namespace lbAbiAmd64SysV { return reg_classes; } - void unify(Array<RegClass> *cls, i64 i, RegClass newv) { - RegClass &oldv = (*cls)[i]; + void unify(Array<RegClass> *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) { - 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; + 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; + } } + + (*cls)[i] = to_write; } void fixup(LLVMTypeRef t, Array<RegClass> *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++) { @@ -709,8 +644,8 @@ namespace lbAbiAmd64SysV { unsigned llvec_len(Array<RegClass> 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++; @@ -721,7 +656,7 @@ namespace lbAbiAmd64SysV { LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const ®_classes) { auto types = array_make<LLVMTypeRef>(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: @@ -763,7 +698,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; @@ -779,9 +714,9 @@ namespace lbAbiAmd64SysV { default: GB_PANIC("Unhandled RegClass"); } + i += 1; } - GB_ASSERT(types.count != 0); if (types.count == 1) { return types[0]; } @@ -893,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(c, "byval")); + args[i] = lb_arg_type_indirect_byval(c, t); } } else { args[i] = non_struct(c, t); @@ -913,7 +848,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); @@ -1063,7 +998,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 758f8e5d1..08c9445bd 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1,8 +1,21 @@ +#define MULTITHREAD_OBJECT_GENERATION 1 + +#ifndef USE_SEPARTE_MODULES +#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" -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 = {}; @@ -17,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; @@ -74,6 +93,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))) { @@ -184,7 +212,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 +237,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; } @@ -372,18 +400,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; @@ -501,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); @@ -513,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 @@ -1110,7 +1159,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 +1175,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 +1501,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; } @@ -1823,13 +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(unsigned)type_align_of(type), lb_debug_type(m, type->Array.elem), nullptr, 0); + 8*cast(uint64_t)type_size_of(type), + 8*cast(unsigned)type_align_of(type), + lb_debug_type(m, type->Array.elem), + 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))); } @@ -2187,7 +2269,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, @@ -2463,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); @@ -2514,10 +2617,18 @@ 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); + GB_ASSERT(entity->kind == Entity_Procedure); + + String link_name = {}; - String link_name = lb_get_entity_name(m, entity); + 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); @@ -2588,6 +2699,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"); @@ -2681,6 +2796,11 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) { } } + if (ignore_body) { + p->body = nullptr; + LLVMSetLinkage(p->value, LLVMExternalLinkage); + } + if (m->debug_builder) { // Debug Information Type *bt = base_type(p->type); @@ -2851,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) { @@ -3113,20 +3167,7 @@ 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->builder = LLVMCreateBuilderInContext(p->module->ctx); p->decl_block = lb_create_block(p, "decls", true); p->entry_block = lb_create_block(p, "entry", true); @@ -3143,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); @@ -3273,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); @@ -3287,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); @@ -3295,7 +3342,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); @@ -3362,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); @@ -3393,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); @@ -3419,6 +3485,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); @@ -3544,16 +3625,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 +3654,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); @@ -3948,63 +4031,105 @@ 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_RangeFull: 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_) { @@ -4159,6 +4284,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)); @@ -4195,10 +4325,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); @@ -4347,7 +4474,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); @@ -4358,7 +4485,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); @@ -4525,6 +4652,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; } @@ -4763,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); } @@ -4777,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; @@ -4815,12 +4951,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; @@ -4830,7 +4966,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) { @@ -5412,6 +5548,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); } @@ -5429,6 +5566,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); } @@ -5462,9 +5600,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); @@ -5504,9 +5643,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); @@ -5628,7 +5767,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; } @@ -5661,6 +5800,112 @@ 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) { + 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)); + if (found) { + return *found; + } + + 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; + } + + GB_PANIC("Error in: %s, missing procedure %.*s\n", token_pos_to_string(e->token.pos), LIT(e->token.string)); + return {}; +} + +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); + } + + 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); + + // TODO(bill): correct this logic + bool is_external = other_module != m; + if (!is_external) { + if (e->code_gen_module != nullptr) { + other_module = e->code_gen_module; + } else { + other_module = nullptr; + } + 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); + 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); + + 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); + } + + + 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; @@ -5687,14 +5932,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; @@ -5743,8 +5981,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); @@ -5797,7 +6035,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, @@ -6799,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: @@ -7598,10 +7850,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}; @@ -8084,6 +8339,8 @@ Array<lbValue> lb_value_to_array(lbProcedure *p, lbValue value) { lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr, Array<lbValue> 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; @@ -8158,31 +8415,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<lbValue> 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<lbValue> 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<lbValue> const &args, ProcInlining inlining, bool use_return_ptr_hint) { @@ -8321,9 +8565,7 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> 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; @@ -8645,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; @@ -9134,9 +9376,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]); @@ -9166,21 +9410,85 @@ 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); + + res.type = res_type; + } else { + res.value = LLVMBuildExtractValue(p->builder, res.value, 0, ""); + res.type = type; } + 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 = res_type; + res.type = type; 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, ""); @@ -9390,16 +9698,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; + } } @@ -9490,7 +9804,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 = {}; @@ -9900,24 +10215,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)); @@ -10242,7 +10539,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"); @@ -10268,6 +10565,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 case_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, case_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); @@ -10336,6 +10683,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); @@ -10358,6 +10708,48 @@ 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) { + auto args = array_make<lbValue>(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); + 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); + + 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); @@ -10419,6 +10811,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<lbValue>(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<lbValue>(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) { @@ -10447,42 +10863,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); + } } } @@ -10548,28 +10944,12 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri } - if (is_type_struct(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<lbValue>(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<lbValue>(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; + if ((is_type_struct(a) || is_type_union(a)) && is_type_comparable(a)) { + 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)) { @@ -10783,12 +11163,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); @@ -10797,7 +11174,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); @@ -10811,6 +11188,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 = {}; @@ -10825,11 +11203,59 @@ 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; } -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; @@ -10893,7 +11319,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; @@ -10981,15 +11407,66 @@ 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 (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); + LLVMSetLinkage(g.value, LLVMExternalLinkage); + + 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 {}; +} + +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; - 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; @@ -10999,7 +11476,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); @@ -11009,6 +11486,12 @@ 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 return lb_const_value(p->module, tv.type, tv.value); } @@ -11032,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); @@ -11070,24 +11553,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); @@ -11114,47 +11580,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] = {}; @@ -11213,6 +11638,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); @@ -11358,6 +11787,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; @@ -11436,7 +11872,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; } @@ -11462,11 +11904,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); @@ -11813,27 +12258,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)) { @@ -11969,6 +12393,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 = {}; @@ -11977,36 +12402,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); @@ -12780,8 +13175,16 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { void lb_init_module(lbModule *m, Checker *c) { m->info = &c->info; - m->ctx = LLVMGetGlobalContext(); - m->mod = LLVMModuleCreateWithNameInContext("odin_module", m->ctx); + 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"); + } + + m->ctx = LLVMContextCreate(); + 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}; @@ -12808,10 +13211,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); map_init(&m->llvm_types, a); @@ -12870,6 +13269,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); @@ -12879,8 +13279,34 @@ 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); + 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); + 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; + 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; } @@ -12891,8 +13317,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; @@ -12901,7 +13329,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))); @@ -12915,17 +13343,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) { @@ -12940,32 +13363,37 @@ 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; } 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; @@ -13007,12 +13435,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)); @@ -13044,7 +13472,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)); @@ -13447,7 +13875,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); @@ -13476,10 +13904,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); @@ -13691,18 +14128,445 @@ 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); + 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(); + 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); + } + + lb_run_function_pass_manager(default_function_pass_manager, p); + + return p; +} + +lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *startup_type_info, Array<lbGlobalVariable> &global_variables) { // Startup Runtime + LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(main_module->mod); + 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(); + 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(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(main_module, 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; + } + + 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)); + 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(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(main_module, 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 (!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"); + LLVMVerifyFunction(p->value, LLVMAbortProcessAction); + } + + lb_run_function_pass_manager(default_function_pass_manager, p); + + return p; +} + + +lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) { + LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod); + 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(); + 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, blank_token, 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}; + 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); + } + + 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; + + 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 = lb_find_procedure_value_from_entity(m, testing_proc); + + 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<lbValue>(heap_allocator(), 1); + args[0] = lb_addr_load(p, all_tests_slice); + lb_emit_call(p, runner, args); + } else { + 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)); + + 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); + } + + lb_run_function_pass_manager(default_function_pass_manager, p); + 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); + } else if (USE_SEPARTE_MODULES) { + path = concatenate_strings(permanent_allocator(), path, STR_LIT("-builtin")); + } + 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); +} + + +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; + + 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 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(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); + 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(m, 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) { + 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: + lb_run_function_pass_manager(function_pass_manager_minimal, p); + break; + case ProcedureOptimizationMode_Size: + lb_run_function_pass_manager(function_pass_manager_size, p); + break; + case ProcedureOptimizationMode_Speed: + lb_run_function_pass_manager(function_pass_manager_speed, p); + break; + default: + lb_run_function_pass_manager(default_function_pass_manager, p); + break; + } + } else { + lb_run_function_pass_manager(default_function_pass_manager, p); + } + } + } + } + + for_array(i, m->equal_procs.entries) { + lbProcedure *p = m->equal_procs.entries[i].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; + lb_run_function_pass_manager(default_function_pass_manager, p); + } + + 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) { #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"); - lbModule *m = &gen->module; - LLVMModuleRef mod = gen->module.mod; + 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; auto *min_dep_set = &info->minimum_dependency_set; - LLVMInitializeAllTargetInfos(); LLVMInitializeAllTargets(); LLVMInitializeAllTargetMCs(); @@ -13711,16 +14575,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; @@ -13754,78 +14621,89 @@ 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)); - - - LLVMSetModuleDataLayout(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; + // NOTE(bill, 2021-05-04): Target machines must be unique to each module because they are not thread safe + auto target_machines = array_make<LLVMTargetMachineRef>(permanent_allocator(), gen->modules.entries.count); + + for_array(i, gen->modules.entries) { + 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) { + 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); } - - 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(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); + if (!USE_SEPARTE_MODULES) { + LLVMSetLinkage(g, LLVMInternalLinkage); + } 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, 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 // NOTE(bill): Removes need for heap allocation by making it global memory @@ -13856,7 +14734,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)}); @@ -13865,7 +14743,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)}); @@ -13873,7 +14751,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)}); @@ -13882,7 +14760,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)}); @@ -13891,7 +14769,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)}); @@ -13931,13 +14809,8 @@ void lb_generate_code(lbGenerator *gen) { } } - struct GlobalVariable { - lbValue var; - lbValue init; - DeclInfo *decl; - bool is_initialized; - }; - auto global_variables = array_make<GlobalVariable>(permanent_allocator(), 0, global_variable_max_count); + + auto global_variables = array_make<lbGlobalVariable>(permanent_allocator(), 0, global_variable_max_count); for_array(i, info->variable_init_order) { DeclInfo *d = info->variable_init_order[i]; @@ -13960,8 +14833,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)); @@ -13985,6 +14859,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 { @@ -13993,13 +14868,15 @@ void lb_generate_code(lbGenerator *gen) { if (is_export) { LLVMSetLinkage(g.value, LLVMDLLExportLinkage); LLVMSetDLLStorageClass(g.value, LLVMDLLExportStorageClass); + } else { + if (USE_SEPARTE_MODULES) { + LLVMSetLinkage(g.value, LLVMExternalLinkage); + } else { + LLVMSetLinkage(g.value, LLVMInternalLinkage); + } } - if (e->flags & EntityFlag_Static) { - LLVMSetLinkage(g.value, LLVMInternalLinkage); - } - - GlobalVariable var = {}; + lbGlobalVariable var = {}; var.var = g; var.decl = decl; @@ -14026,13 +14903,14 @@ 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 != "_") { 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; @@ -14096,6 +14974,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); @@ -14113,450 +14995,209 @@ void lb_generate_code(lbGenerator *gen) { } - TIME_SECTION("LLVM Registry Initializtion"); - - 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); - 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(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(default_module); - 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 *p = lb_create_dummy_procedure(m, str_lit(LB_STARTUP_RUNTIME_PROC_NAME), proc_type); - p->is_startup = true; - startup_runtime = p; + lbProcedure *startup_runtime = lb_create_startup_runtime(default_module, startup_type_info, global_variables); - 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) { + TIME_SECTION("LLVM Procedure Generation"); + 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; } - - 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; + 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); } } - - - 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")); - 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; - } - } + if (!(build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main)) { + TIME_SECTION("LLVM main"); + lb_create_main_procedure(default_module, startup_runtime); + } - 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(mod, cast(char const *)filepath_ll.text, &llvm_error)) { - gb_printf_err("LLVM Error: %s\n", llvm_error); + 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); } - LLVMVerifyFunction(p->value, LLVMPrintMessageAction); - gb_exit(1); } } - 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(); - - 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); - } - - 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); + TIME_SECTION("LLVM Function Pass"); + for_array(i, gen->modules.entries) { + lbModule *m = gen->modules.entries[i].value; + lb_llvm_function_pass_worker_proc(m); + } - lbProcedure *p = lb_create_dummy_procedure(m, name, proc_type); - p->is_startup = true; + TIME_SECTION("LLVM Module Pass"); - lb_begin_procedure_body(p); + for_array(i, gen->modules.entries) { + lbModule *m = gen->modules.entries[i].value; - LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, startup_runtime->type)), startup_runtime->value, nullptr, 0, ""); + auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData); + wd->m = m; + wd->target_machine = target_machines[i]; - 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); + lb_llvm_module_pass_worker_proc(wd); + } - LLVMTypeRef lbt_Internal_Test = lb_type(m, t_Internal_Test); - LLVMValueRef indices[2] = {}; - indices[0] = LLVMConstInt(lb_type(m, t_i32), 0, false); + llvm_error = nullptr; + defer (LLVMDisposeMessage(llvm_error)); - 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); + LLVMCodeGenFileType code_gen_file_type = LLVMObjectFile; + if (build_context.build_mode == BuildMode_Assembly) { + code_gen_file_type = LLVMAssemblyFile; + } - String pkg_name = {}; - if (testing_proc->pkg != nullptr) { - pkg_name = testing_proc->pkg->name; + 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; } - 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); } + 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"); - 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)); + for_array(j, gen->modules.entries) { + lbModule *m = gen->modules.entries[j].value; + if (lb_is_module_empty(m)) { + continue; + } - lbValue runner = lb_find_package_value(m, str_lit("testing"), str_lit("runner")); + 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); - auto args = array_make<lbValue>(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); + if (build_context.build_mode == BuildMode_LLVM_IR) { + gb_exit(0); + return; } - - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); } - if (m->debug_builder != nullptr) { - TIME_SECTION("LLVM Debug Info Complete Types"); - lb_debug_complete_types(m); + 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) { + Entity *e = m->info->required_foreign_imports_through_force[i]; + lb_add_foreign_library_path(m, e); + } - TIME_SECTION("LLVM Print Module to File"); - if (LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error)) { - gb_printf_err("LLVM Error: %s\n", llvm_error); - gb_exit(1); - return; + if (lb_is_module_empty(m)) { + continue; } - TIME_SECTION("LLVM Debug Info Builder Finalize"); - LLVMDIBuilderFinalize(m->debug_builder); } + TIME_SECTION("LLVM Object Generation"); - TIME_SECTION("LLVM Function Pass"); - { - 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); - } - } + if (do_threading) { + for_array(j, gen->modules.entries) { + lbModule *m = gen->modules.entries[j].value; + if (lb_is_module_empty(m)) { + continue; } - } - 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); - } - } + 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); - TIME_SECTION("LLVM Module Pass"); + auto *wd = gb_alloc_item(heap_allocator(), lbLLVMEmitWorker); + 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(&lb_thread_pool, lb_llvm_emit_worker_proc, wd); + } - LLVMPassManagerRef module_pass_manager = LLVMCreatePassManager(); - defer (LLVMDisposePassManager(module_pass_manager)); - lb_populate_module_pass_manager(target_machine, module_pass_manager, build_context.optimization_level); + 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; + if (lb_is_module_empty(m)) { + continue; + } - LLVMRunPassManager(module_pass_manager, mod); + String filepath_obj = lb_filepath_obj_for_module(m); + array_add(&gen->output_object_paths, filepath_obj); - llvm_error = nullptr; - defer (LLVMDisposeMessage(llvm_error)); + 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); - String filepath_obj = {}; - LLVMCodeGenFileType code_gen_file_type = LLVMObjectFile; + TIME_SECTION_WITH_LEN(section_name, gb_string_length(section_name)); - 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(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 (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; } } - 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(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); - return; - } } - TIME_SECTION("LLVM Object Generation"); - if (LLVMTargetMachineEmitToFile(target_machine, 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); - - 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 e8811a91e..b35c042ee 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -85,11 +85,10 @@ struct lbModule { LLVMModuleRef mod; LLVMContextRef ctx; - u64 state_flags; + struct lbGenerator *gen; CheckerInfo *info; - - gbMutex mutex; + AstPackage *pkg; // associated Map<LLVMTypeRef> types; // Key: Type * Map<Type *> llvm_types; // Key: LLVMTypeRef @@ -109,8 +108,6 @@ struct lbModule { Map<lbProcedure *> equal_procs; // Key: Type * Map<lbProcedure *> hasher_procs; // Key: Type * - u32 global_array_index; - u32 global_generated_index; u32 nested_type_name_guid; Array<lbProcedure *> procedures_to_generate; @@ -126,12 +123,22 @@ struct lbModule { }; struct lbGenerator { - lbModule module; CheckerInfo *info; + gbMutex mutex; + Array<String> output_object_paths; + Array<String> output_temp_paths; String output_base; String output_name; + Map<lbModule *> modules; // Key: AstPackage * + Map<lbModule *> modules_through_ctx; // Key: LLVMContextRef * + lbModule default_module; + + Map<lbProcedure *> anonymous_proc_lits; // Key: Ast * + + gbAtomic32 global_array_index; + gbAtomic32 global_generated_index; }; @@ -210,6 +217,7 @@ enum lbProcedureFlag : u32 { struct lbProcedure { u32 flags; + u16 state_flags; lbProcedure *parent; Array<lbProcedure *> children; @@ -268,9 +276,10 @@ 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); +lbProcedure *lb_create_procedure(lbModule *module, Entity *entity, bool ignore_body=false); void lb_end_procedure(lbProcedure *p); @@ -381,6 +390,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); @@ -472,6 +483,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/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index 3b268dffa..213005f8f 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); @@ -226,3 +248,104 @@ 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; + isize original_instruction_count = 0; + // 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; + /**/) { + if (pass_count == 0) { + original_instruction_count += 1; + } + + 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 LLVMFNeg: + 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: + case LLVMShuffleVector: + case LLVMExtractValue: + 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); +} diff --git a/src/main.cpp b/src/main.cpp index 251616b56..d16a110e3 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)); @@ -265,22 +276,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 " @@ -327,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, @@ -409,6 +420,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); @@ -580,6 +596,7 @@ enum BuildFlagKind { BuildFlag_NoCRT, BuildFlag_NoEntryPoint, BuildFlag_UseLLD, + BuildFlag_UseSeparateModules, BuildFlag_Vet, BuildFlag_VetExtra, BuildFlag_UseLLVMApi, @@ -587,6 +604,8 @@ enum BuildFlagKind { BuildFlag_ExtraLinkerFlags, BuildFlag_Microarch, + BuildFlag_TestName, + BuildFlag_DisallowDo, BuildFlag_DefaultToNilAllocator, BuildFlag_InsertSemicolon, @@ -602,6 +621,7 @@ enum BuildFlagKind { BuildFlag_IgnoreWarnings, BuildFlag_WarningsAsErrors, + BuildFlag_VerboseErrors, #if defined(GB_SYSTEM_WINDOWS) BuildFlag_IgnoreVsSearch, @@ -698,6 +718,7 @@ bool parse_build_flags(Array<String> 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); @@ -705,6 +726,8 @@ bool parse_build_flags(Array<String> 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); @@ -719,6 +742,7 @@ bool parse_build_flags(Array<String> 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); @@ -1175,6 +1199,10 @@ bool parse_build_flags(Array<String> 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; @@ -1203,6 +1231,21 @@ bool parse_build_flags(Array<String> 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; @@ -1279,6 +1322,10 @@ bool parse_build_flags(Array<String> 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); @@ -1507,96 +1554,22 @@ 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; - auto data = array_make<u8>(heap_allocator(), output_base.len + 30); - defer (array_free(&data)); - - 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_temp_paths) { + String path = gen->output_temp_paths[i]; + gb_file_remove(cast(char const *)path.text); } -#undef EXT_REMOVE -} - - - - -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 + if (build_context.build_mode != BuildMode_Object && !build_context.keep_object_files) { + for_array(i, gen->output_object_paths) { + String path = gen->output_object_paths[i]; + gb_file_remove(cast(char const *)path.text); + } + } } -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)); @@ -1612,7 +1585,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") { @@ -1627,6 +1600,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"; @@ -1720,6 +1694,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, ""); } @@ -1746,9 +1725,16 @@ 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, ""); + + 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, ""); + } if (check) { @@ -1776,6 +1762,12 @@ void print_show_help(String const arg0, String const &command) { } } + if (test_only) { + print_usage_line(1, "-test-name:<string>"); + 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:<string>"); print_usage_line(2, "Adds extra linker specific flags in a string"); @@ -1943,7 +1935,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()); @@ -1967,7 +1959,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<String> args = setup_args(arg_count, arg_ptr); @@ -2190,6 +2182,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; @@ -2199,7 +2194,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) diff --git a/src/parser.cpp b/src/parser.cpp index a7e4c9162..a5180b4dd 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1,110 +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_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); - 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; @@ -116,6 +10,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 *)); @@ -241,11 +177,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); @@ -438,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, fmt, va); + error_va(token.pos, end_pos, fmt, va); va_end(va); if (node != nullptr && node->file != nullptr) { node->file->error_count += 1; @@ -457,7 +391,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; @@ -465,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), 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), 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; @@ -646,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; @@ -698,13 +644,6 @@ Ast *ast_compound_lit(AstFile *f, Ast *type, Array<Ast *> 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; @@ -1357,6 +1296,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; } @@ -1587,6 +1527,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) { @@ -2007,35 +1951,28 @@ 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") { - 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); + } else if (name.string == "file") { + 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) { @@ -2047,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) { @@ -2059,6 +1996,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"); @@ -2066,13 +2007,17 @@ 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"); } 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); @@ -2158,6 +2103,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 ---)"); @@ -2170,6 +2117,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; @@ -2317,7 +2272,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; @@ -2394,6 +2349,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 && @@ -2418,6 +2374,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<Ast *> values = parse_element_list(f); @@ -2509,6 +2467,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); @@ -2673,6 +2632,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: @@ -2684,6 +2644,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 */ @@ -2722,6 +2683,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; @@ -2752,16 +2723,26 @@ Ast *parse_unary_expr(AstFile *f, bool lhs) { return ast_auto_cast(f, token, expr); } + case Token_Add: case Token_Sub: - case Token_Not: case Token_Xor: - case Token_And: { + 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_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); + return ast_unary_expr(f, token, expr); + } + + case Token_Period: { Token token = expect_token(f, Token_Period); Ast *ident = parse_ident(f); @@ -2790,6 +2771,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; @@ -2857,7 +2839,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 @@ -2979,7 +2961,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 && @@ -3151,6 +3133,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 @@ -3220,6 +3209,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; } @@ -3896,12 +3886,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) { @@ -3966,7 +3950,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) { @@ -4022,7 +4006,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<Ast *> stmts = parse_stmt_list(f); return ast_case_clause(f, token, list, stmts); @@ -4332,6 +4316,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; @@ -4438,6 +4432,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"); @@ -4445,27 +4440,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) { @@ -4475,16 +4455,19 @@ 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; } 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") { @@ -4571,6 +4554,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/parser.hpp b/src/parser.hpp index 8c2eadb46..89f714aaa 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -7,22 +7,21 @@ 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 + }; struct TypeAndValue { @@ -215,8 +214,9 @@ enum ProcCallingConvention { ProcCC_FastCall = 5, ProcCC_None = 6, + ProcCC_Naked = 7, - ProcCC_InlineAsm = 7, + ProcCC_InlineAsm = 8, ProcCC_MAX, @@ -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; \ @@ -342,10 +342,15 @@ 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; }) \ + 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 { \ 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/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; } - diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 081ef6443..826fccc04 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, "=="), \ @@ -74,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, ""), \ @@ -185,9 +188,11 @@ void init_keyword_hash_table(void) { GB_ASSERT(max_keyword_size < 16); } -gb_global Array<String> global_file_path_strings; // index is file id +gb_global Array<String> global_file_path_strings; // index is file id +gb_global Array<struct AstFile *> 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; @@ -281,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); } @@ -302,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); @@ -315,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; @@ -374,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; @@ -383,17 +423,85 @@ void error_out(char const *fmt, ...) { } -void error_va(Token token, char const *fmt, va_list va) { +bool show_error_on_line(TokenPos const &pos, TokenPos end) { + 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)); + + // TODO(bill): This assumes ASCII + + 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("^"); + 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 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 - 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(pos, end); } gb_mutex_unlock(&global_error_collector.mutex); if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) { @@ -401,22 +509,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, TokenPos end, char const *fmt, va_list va) { if (global_warnings_as_errors()) { - error_va(token, fmt, va); + error_va(pos, end, 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(pos, end); } } gb_mutex_unlock(&global_error_collector.mutex); @@ -429,16 +538,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); @@ -448,16 +557,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, 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 - 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)); - } else if (token.pos.line == 0) { + show_error_on_line(pos, end); + } else if (pos.line == 0) { error_out("Syntax Error: %s\n", gb_bprintf_va(fmt, va)); } @@ -467,21 +577,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, TokenPos end, char const *fmt, va_list va) { if (global_warnings_as_errors()) { - syntax_error_va(token, fmt, va); + syntax_error_va(pos, end, 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)); - } else if (token.pos.line == 0) { + show_error_on_line(pos, end); + } else if (pos.line == 0) { error_out("Warning: %s\n", gb_bprintf_va(fmt, va)); } } @@ -490,17 +601,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); } @@ -509,7 +620,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); } @@ -521,26 +632,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); } @@ -652,13 +761,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++; @@ -670,11 +780,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++; @@ -1202,6 +1310,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); @@ -1287,6 +1398,10 @@ 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; + insert_semicolon = true; } break; case '-': @@ -1298,6 +1413,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 == '-') { + 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; diff --git a/src/types.cpp b/src/types.cpp index 56081acc8..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<Entity *> fields; Array<String> 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; @@ -1915,6 +1896,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 +1952,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); @@ -2766,7 +2760,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) { @@ -3618,6 +3641,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; |