diff options
| author | gingerBill <gingerBill@users.noreply.github.com> | 2026-01-30 10:49:55 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-01-30 10:49:55 +0000 |
| commit | 19b545e7cb0b09a7c8b3424ca8276b9e37f8de80 (patch) | |
| tree | 974b844bf082c526f6a12396c4a80adfc73b9a60 /src | |
| parent | 8b745c3909a3482aebe27998d8b870286e448e35 (diff) | |
| parent | 5a21213fa5e2c74d5021adb2a87f0cc441a38eab (diff) | |
Merge branch 'master' into bill/feature-using-stmt
Diffstat (limited to 'src')
| -rw-r--r-- | src/build_settings.cpp | 19 | ||||
| -rw-r--r-- | src/check_builtin.cpp | 26 | ||||
| -rw-r--r-- | src/check_decl.cpp | 1 | ||||
| -rw-r--r-- | src/check_expr.cpp | 152 | ||||
| -rw-r--r-- | src/check_stmt.cpp | 4 | ||||
| -rw-r--r-- | src/check_type.cpp | 29 | ||||
| -rw-r--r-- | src/checker.cpp | 48 | ||||
| -rw-r--r-- | src/checker.hpp | 1 | ||||
| -rw-r--r-- | src/entity.cpp | 1 | ||||
| -rw-r--r-- | src/exact_value.cpp | 13 | ||||
| -rw-r--r-- | src/linker.cpp | 18 | ||||
| -rw-r--r-- | src/llvm_backend.cpp | 23 | ||||
| -rw-r--r-- | src/llvm_backend.hpp | 10 | ||||
| -rw-r--r-- | src/llvm_backend_const.cpp | 23 | ||||
| -rw-r--r-- | src/llvm_backend_expr.cpp | 88 | ||||
| -rw-r--r-- | src/llvm_backend_general.cpp | 2 | ||||
| -rw-r--r-- | src/llvm_backend_opt.cpp | 10 | ||||
| -rw-r--r-- | src/llvm_backend_proc.cpp | 65 | ||||
| -rw-r--r-- | src/llvm_backend_stmt.cpp | 76 | ||||
| -rw-r--r-- | src/llvm_backend_utility.cpp | 15 | ||||
| -rw-r--r-- | src/main.cpp | 32 | ||||
| -rw-r--r-- | src/parser.cpp | 131 | ||||
| -rw-r--r-- | src/parser.hpp | 20 | ||||
| -rw-r--r-- | src/string.cpp | 107 | ||||
| -rw-r--r-- | src/types.cpp | 74 |
25 files changed, 731 insertions, 257 deletions
diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 12631c403..6b8fefa59 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -360,14 +360,13 @@ enum OptInFeatureFlags : u64 { OptInFeatureFlag_IntegerDivisionByZero_Self = 1u<<4, OptInFeatureFlag_IntegerDivisionByZero_AllBits = 1u<<5, - OptInFeatureFlag_UsingStmt = 1u<<6, - - OptInFeatureFlag_IntegerDivisionByZero_ALL = OptInFeatureFlag_IntegerDivisionByZero_Trap| OptInFeatureFlag_IntegerDivisionByZero_Zero| OptInFeatureFlag_IntegerDivisionByZero_Self| OptInFeatureFlag_IntegerDivisionByZero_AllBits, + OptInFeatureFlag_ForceTypeAssert = 1u<<6, + OptInFeatureFlag_UsingStmt = 1u<<7, }; u64 get_feature_flag_from_name(String const &name) { @@ -388,6 +387,9 @@ u64 get_feature_flag_from_name(String const &name) { } if (name == "using-stmt") { return OptInFeatureFlag_UsingStmt; + } + if (name == "force-type-assert") { + return OptInFeatureFlag_ForceTypeAssert; } @@ -587,6 +589,7 @@ struct BuildContext { RelocMode reloc_mode; bool disable_red_zone; + bool disable_unwind; isize max_error_count; @@ -844,6 +847,15 @@ gb_global TargetMetrics target_freestanding_amd64_win64 = { TargetABI_Win64, }; +gb_global TargetMetrics target_freestanding_amd64_mingw = { + TargetOs_freestanding, + TargetArch_amd64, + 8, 8, AMD64_MAX_ALIGNMENT, 32, + str_lit("x86_64-pc-windows-gnu"), + TargetABI_Win64, +}; + + gb_global TargetMetrics target_freestanding_arm64 = { TargetOs_freestanding, TargetArch_arm64, @@ -906,6 +918,7 @@ gb_global NamedTargetMetrics named_targets[] = { { str_lit("freestanding_amd64_sysv"), &target_freestanding_amd64_sysv }, { str_lit("freestanding_amd64_win64"), &target_freestanding_amd64_win64 }, + { str_lit("freestanding_amd64_mingw"), &target_freestanding_amd64_mingw }, { str_lit("freestanding_arm64"), &target_freestanding_arm64 }, { str_lit("freestanding_arm32"), &target_freestanding_arm32 }, diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 1b3e6912c..e732d8ec3 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -2695,6 +2695,16 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As case BuiltinProc_size_of: { // size_of :: proc(Type or expr) -> untyped int + if (ce->args[0]->kind == Ast_UnaryExpr) { + ast_node(arg, UnaryExpr, ce->args[0]); + if (arg->op.kind == Token_And) { + ERROR_BLOCK(); + + warning(ce->args[0], "'size_of(&x)' returns the size of a pointer, not the size of x"); + error_line("\tSuggestion: Use 'size_of(rawptr)' if you want the size of the pointer"); + } + } + Operand o = {}; check_expr_or_type(c, &o, ce->args[0]); if (o.mode == Addressing_Invalid) { @@ -5373,6 +5383,14 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As return false; } } + if (!are_types_identical(x.type, y.type)) { + gbString xts = type_to_string(x.type); + gbString yts = type_to_string(y.type); + error(x.expr, "Mismatched types for '%.*s', got %s vs %s", LIT(builtin_name), xts, yts); + gb_string_free(yts); + gb_string_free(xts); + return false; + } operand->mode = Addressing_Value; operand->type = make_optional_ok_type(default_type(x.type)); @@ -5416,6 +5434,14 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As return false; } } + if (!are_types_identical(x.type, y.type)) { + gbString xts = type_to_string(x.type); + gbString yts = type_to_string(y.type); + error(x.expr, "Mismatched types for '%.*s', got %s vs %s", LIT(builtin_name), xts, yts); + gb_string_free(yts); + gb_string_free(xts); + return false; + } operand->mode = Addressing_Value; operand->type = default_type(x.type); diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 27babd255..8019d00c3 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1473,6 +1473,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { e->Procedure.no_sanitize_address = ac.no_sanitize_address; e->Procedure.no_sanitize_memory = ac.no_sanitize_memory; + e->Procedure.no_sanitize_thread = ac.no_sanitize_thread; e->deprecated_message = ac.deprecated_message; e->warning_message = ac.warning_message; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 2fe6c0251..1c09ad908 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1133,7 +1133,11 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ x.mode = Addressing_Value; x.type = t; if (check_is_assignable_to(c, &x, type)) { - add_entity_use(c, operand->expr, e); + if (operand->expr->kind == Ast_SelectorExpr) { + add_entity_use(c, operand->expr->SelectorExpr.selector, e); + } else { + add_entity_use(c, operand->expr, e); + } good = true; break; } @@ -3526,28 +3530,17 @@ gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type) Type *elem = core_array_type(bt); if (core_type(bt)->kind == Type_Basic) { - if (check_representable_as_constant(c, x->value, type, &x->value)) { - return true; - } - goto check_castable; - } else if (!are_types_identical(elem, bt) && - elem->kind == Type_Basic && - check_representable_as_constant(c, x->value, elem, &x->value)) { - if (check_representable_as_constant(c, x->value, type, &x->value)) { - return true; - } - goto check_castable; + return check_representable_as_constant(c, x->value, type, &x->value) || + (is_type_pointer(type) && check_is_castable_to(c, x, type)); + } else if (!are_types_identical(elem, bt) && elem->kind == Type_Basic && x->type->kind == Type_Basic) { + return check_representable_as_constant(c, x->value, elem, &x->value) || + (is_type_pointer(elem) && check_is_castable_to(c, x, elem)); } else if (check_is_castable_to(c, x, type)) { x->value = {}; x->mode = Addressing_Value; return true; } - - return false; - } - -check_castable: - if (check_is_castable_to(c, x, type)) { + } else if (check_is_castable_to(c, x, type)) { if (x->mode != Addressing_Constant) { x->mode = Addressing_Value; } else if (is_type_slice(type) && is_type_string(x->type)) { @@ -4132,15 +4125,19 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ i64 upper = yt->BitSet.upper; if (lower <= key && key <= upper) { - i64 bit = 1ll<<key; - i64 bits = big_int_to_i64(&v.value_integer); + BigInt idx = big_int_make_i64(key - lower); + BigInt bit = big_int_make_i64(1); + big_int_shl_eq(&bit, &idx); + + BigInt mask = {}; + big_int_and(&mask, &bit, &v.value_integer); x->mode = Addressing_Constant; x->type = t_untyped_bool; if (op.kind == Token_in) { - x->value = exact_value_bool((bit & bits) != 0); + x->value = exact_value_bool(!big_int_is_zero(&mask)); } else { - x->value = exact_value_bool((bit & bits) == 0); + x->value = exact_value_bool(big_int_is_zero(&mask)); } x->expr = node; return; @@ -4707,12 +4704,14 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar switch (t->kind) { case Type_Basic: if (operand->mode == Addressing_Constant) { - check_is_expressible(c, operand, t); + check_is_expressible(c, operand, target_type); if (operand->mode == Addressing_Invalid) { return; } update_untyped_expr_value(c, operand->expr, operand->value); - } else { + } + + { switch (operand->type->Basic.kind) { case Basic_UntypedBool: if (!is_type_boolean(target_type)) { @@ -5195,6 +5194,11 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v } } else { TypeAndValue index_tav = fv->field->tav; + if (index_tav.mode != Addressing_Constant) { + if (success_) *success_ = false; + if (finish_) *finish_ = true; + return empty_exact_value; + } GB_ASSERT(index_tav.mode == Addressing_Constant); ExactValue index_value = index_tav.value; if (is_type_enumerated_array(node->tav.type)) { @@ -8172,13 +8176,7 @@ gb_internal void check_objc_call_expr(CheckerContext *c, Operand *operand, Ast * Type *return_type = proc.result_count == 0 ? nullptr : proc.results->Tuple.variables[0]->type; bool is_return_instancetype = return_type != nullptr && return_type == t_objc_instancetype; - if (params.count == 0 || !is_type_objc_ptr_to_object(params[0]->type)) { - if (!proc_entity->Procedure.is_objc_class_method) { - // Not a class method, invalid call - error(call, "Invalid Objective-C call: The Objective-C method is not a class method but this first parameter is not an Objective-C object pointer."); - return; - } - + if (proc_entity->Procedure.is_objc_class_method) { if (is_return_instancetype) { if (ce->proc->kind == Ast_SelectorExpr) { ast_node(se, SelectorExpr, ce->proc); @@ -8194,7 +8192,8 @@ gb_internal void check_objc_call_expr(CheckerContext *c, Operand *operand, Ast * self_type = t_objc_Class; params_start = 0; - } else if (ce->args.count > 0) { + } else { + GB_ASSERT(ce->args.count > 0); GB_ASSERT(is_type_objc_ptr_to_object(params[0]->type)); if (ce->args[0]->tav.objc_super_target) { @@ -8226,7 +8225,7 @@ gb_internal void check_objc_call_expr(CheckerContext *c, Operand *operand, Ast * add_objc_proc_type(c, call, return_type, param_types); } -gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Slice<Ast *> const &args, ProcInlining inlining, Type *type_hint) { +gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Slice<Ast *> const &args, ProcInlining inlining, ProcTailing tailing, Type *type_hint) { if (proc != nullptr && proc->kind == Ast_BasicDirective) { ast_node(bd, BasicDirective, proc); @@ -8257,7 +8256,10 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c return Expr_Expr; } if (inlining != ProcInlining_none) { - error(call, "Inlining operators are not allowed on built-in procedures"); + error(call, "Inlining directives are not allowed on built-in procedures"); + } + if (tailing != ProcTailing_none) { + error(call, "Tailing directives are not allowed on built-in procedures"); } } else { if (proc != nullptr) { @@ -8399,6 +8401,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } bool is_call_inlined = false; + bool is_call_tailed = true; switch (inlining) { case ProcInlining_inline: @@ -8433,6 +8436,23 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } } + switch (tailing) { + case ProcTailing_none: + break; + case ProcTailing_must_tail: + is_call_tailed = true; + if (c->curr_proc_sig == nullptr || !are_types_identical(c->curr_proc_sig, pt)) { + ERROR_BLOCK(); + gbString a = type_to_string(pt); + gbString b = type_to_string(c->curr_proc_sig); + error(call, "Use of '#must_tail' of a procedure must have the same type as the procedure it was called within"); + error_line("\tCall type: %s, parent type: %s", a, b); + gb_string_free(b); + gb_string_free(a); + } + break; + } + { String invalid; if (pt->kind == Type_Proc && pt->Proc.require_target_feature.len != 0) { @@ -8759,6 +8779,14 @@ gb_internal bool check_is_operand_compound_lit_constant(CheckerContext *c, Opera if (is_operand_nil(*o)) { return true; } + if (is_type_any(field_type)) { + return false; + } + if (field_type != nullptr && is_type_typeid(field_type) && o->mode == Addressing_Type) { + add_type_info_type(c, o->type); + return true; + } + Ast *expr = unparen_expr(o->expr); if (expr != nullptr) { Entity *e = strip_entity_wrapping(entity_from_expr(expr)); @@ -8769,13 +8797,12 @@ gb_internal bool check_is_operand_compound_lit_constant(CheckerContext *c, Opera add_type_and_value(c, expr, Addressing_Constant, type_of_expr(expr), exact_value_procedure(expr)); return true; } - } - if (field_type != nullptr && is_type_typeid(field_type) && o->mode == Addressing_Type) { - add_type_info_type(c, o->type); - return true; - } - if (is_type_any(field_type)) { - return false; + + if (e != nullptr && e->kind == Entity_Variable && e->Variable.is_rodata) { + // DeclInfo *d = e->decl_info; + // TODO(bill): rodata inlining for non-indirect values + // e.g. **NOT** []T{...} + } } return o->mode == Addressing_Constant; } @@ -11044,8 +11071,20 @@ gb_internal ExprKind check_type_assertion(CheckerContext *c, Operand *o, Ast *no end:; if ((c->state_flags & StateFlag_no_type_assert) == 0) { - add_package_dependency(c, "runtime", "type_assertion_check"); - add_package_dependency(c, "runtime", "type_assertion_check2"); + bool has_context = true; + if (c->proc_name.len == 0 && c->curr_proc_sig == nullptr) { + has_context = false; + } else if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) { + has_context = false; + } + + if (has_context) { + add_package_dependency(c, "runtime", "type_assertion_check_with_context"); + add_package_dependency(c, "runtime", "type_assertion_check2_with_context"); + } else { + add_package_dependency(c, "runtime", "type_assertion_check_contextless"); + add_package_dependency(c, "runtime", "type_assertion_check2_contextless"); + } } return kind; } @@ -11570,6 +11609,15 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast return kind; case_end; + case_ast_node(ht, HelperType, node); + Type *type = check_type(c, ht->type); + if (type != nullptr && type != t_invalid) { + o->mode = Addressing_Type; + o->type = type; + } + return kind; + case_end; + case_ast_node(i, Implicit, node); switch (i->kind) { case Token_context: @@ -11832,7 +11880,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast case_end; case_ast_node(ce, CallExpr, node); - return check_call_expr(c, o, node, ce->proc, ce->args, ce->inlining, type_hint); + return check_call_expr(c, o, node, ce->proc, ce->args, ce->inlining, ce->tailing, type_hint); case_end; case_ast_node(de, DerefExpr, node); @@ -12103,12 +12151,12 @@ gb_internal bool is_exact_value_zero(ExactValue const &v) { -gb_internal bool compare_exact_values_compound_lit(TokenKind op, ExactValue x, ExactValue y, bool *do_break_) { +gb_internal bool compare_exact_values_compound_lit(TokenKind op, ExactValue x, ExactValue y) { ast_node(x_cl, CompoundLit, x.value_compound); ast_node(y_cl, CompoundLit, y.value_compound); if (x_cl->elems.count != y_cl->elems.count) { - if (do_break_) *do_break_ = true; + return false; } bool test = op == Token_CmpEq; @@ -12573,6 +12621,12 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan case_end; case_ast_node(ce, CallExpr, node); + switch (ce->tailing) { + case ProcTailing_must_tail: + str = gb_string_appendc(str, "#must_tail "); + break; + } + switch (ce->inlining) { case ProcInlining_inline: str = gb_string_appendc(str, "#force_inline "); @@ -12640,8 +12694,10 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan 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->is_packed) str = gb_string_appendc(str, "#packed "); + if (st->is_raw_union) str = gb_string_appendc(str, "#raw_union "); + if (st->is_all_or_none) str = gb_string_appendc(str, "#all_or_none "); + if (st->is_simple) str = gb_string_appendc(str, "#simple "); if (st->align) { str = gb_string_appendc(str, "#align "); str = write_expr_to_string(str, st->align, shorthand); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 26860db4f..1f433df36 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1027,7 +1027,9 @@ gb_internal void check_unroll_range_stmt(CheckerContext *ctx, Ast *node, u32 mod error(operand.expr, "Cannot iterate over '%s' of type '%s' in an '#unroll for' statement", s, t); gb_string_free(t); gb_string_free(s); - } else if (operand.mode != Addressing_Constant && unroll_count <= 0) { + } else if (operand.mode != Addressing_Constant && ( + unroll_count <= 0 && + compare_exact_values(Token_CmpEq, inline_for_depth, exact_value_i64(0)))) { error(operand.expr, "An '#unroll for' expression must be known at compile time"); } } diff --git a/src/check_type.cpp b/src/check_type.cpp index 2452cc6d0..b1d0045e9 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -679,6 +679,21 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast * gb_unused(where_clause_ok); } check_struct_fields(ctx, node, &struct_type->Struct.fields, &struct_type->Struct.tags, st->fields, min_field_count, struct_type, context); + + if (st->is_simple) { + bool success = true; + for (Entity *f : struct_type->Struct.fields) { + if (!is_type_nearly_simple_compare(f->type)) { + gbString s = type_to_string(f->type); + error(f->token, "'struct #simple' requires all fields to be at least 'nearly simple compare', got %s", s); + gb_string_free(s); + } + } + if (success) { + struct_type->Struct.is_simple = true; + } + } + wait_signal_set(&struct_type->Struct.fields_wait_signal); } @@ -3753,6 +3768,20 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T set_base_type(named_type, *type); return true; case_end; + + default: { + Operand o = {}; + check_expr_base(ctx, &o, e, nullptr); + + if (o.mode == Addressing_Constant && + o.value.kind == ExactValue_Typeid) { + Type *t = o.value.value_typeid; + if (t != nullptr && t != t_invalid) { + *type = t; + return true; + } + } + } } *type = t_invalid; diff --git a/src/checker.cpp b/src/checker.cpp index 4d5482933..453f3e241 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1530,17 +1530,16 @@ gb_internal void destroy_checker_info(CheckerInfo *i) { map_destroy(&i->load_directory_map); } -gb_internal CheckerContext make_checker_context(Checker *c) { - CheckerContext ctx = {}; - ctx.checker = c; - ctx.info = &c->info; - ctx.scope = builtin_pkg->scope; - ctx.pkg = builtin_pkg; +gb_internal void init_checker_context(CheckerContext *ctx, Checker *c) { + ctx->checker = c; + ctx->info = &c->info; + ctx->scope = builtin_pkg->scope; + ctx->pkg = builtin_pkg; - ctx.type_path = new_checker_type_path(heap_allocator()); - ctx.type_level = 0; - return ctx; + ctx->type_path = new_checker_type_path(heap_allocator()); + ctx->type_level = 0; } + gb_internal void destroy_checker_context(CheckerContext *ctx) { destroy_checker_type_path(ctx->type_path, heap_allocator()); } @@ -1605,7 +1604,7 @@ gb_internal void init_checker(Checker *c) { mpsc_init(&c->global_untyped_queue, a); // , 1<<20); mpsc_init(&c->soa_types_to_complete, a); // , 1<<20); - c->builtin_ctx = make_checker_context(c); + init_checker_context(&c->builtin_ctx, c); } gb_internal void destroy_checker(Checker *c) { @@ -3581,7 +3580,7 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { return true; } else if (name == "test") { if (value != nullptr) { - error(value, "'%.*s' expects no parameter, or a string literal containing \"file\" or \"package\"", LIT(name)); + error(value, "Expected no value for '%.*s'", LIT(name)); } ac->test = true; return true; @@ -3629,13 +3628,13 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { return true; } else if (name == "init") { if (value != nullptr) { - error(value, "'%.*s' expects no parameter, or a string literal containing \"file\" or \"package\"", LIT(name)); + error(value, "Expected no value for '%.*s'", LIT(name)); } ac->init = true; return true; } else if (name == "fini") { if (value != nullptr) { - error(value, "'%.*s' expects no parameter, or a string literal containing \"file\" or \"package\"", LIT(name)); + error(value, "Expected no value for '%.*s'", LIT(name)); } ac->fini = true; return true; @@ -3991,6 +3990,12 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { } ac->no_sanitize_memory = true; return true; + } else if (name == "no_sanitize_thread") { + if (value != nullptr) { + error(value, "'%.*s' expects no parameter", LIT(name)); + } + ac->no_sanitize_thread = true; + return true; } return false; } @@ -4027,6 +4032,7 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) { } else if (ev.kind == ExactValue_String) { String model = ev.value_string; if (model == "default" || + model == "globaldynamic" || model == "localdynamic" || model == "initialexec" || model == "localexec") { @@ -4035,6 +4041,7 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) { ERROR_BLOCK(); error(elem, "Invalid thread local model '%.*s'. Valid models:", LIT(model)); error_line("\tdefault\n"); + error_line("\tglobaldynamic\n"); error_line("\tlocaldynamic\n"); error_line("\tinitialexec\n"); error_line("\tlocalexec\n"); @@ -4962,7 +4969,7 @@ gb_internal void check_collect_entities(CheckerContext *c, Slice<Ast *> const &n gb_internal CheckerContext *create_checker_context(Checker *c) { CheckerContext *ctx = gb_alloc_item(permanent_allocator(), CheckerContext); - *ctx = make_checker_context(c); + init_checker_context(ctx, c); return ctx; } @@ -5407,7 +5414,8 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_import_decl_attribute) { } gb_internal void check_foreign_import_fullpaths(Checker *c) { - CheckerContext ctx = make_checker_context(c); + CheckerContext ctx = {}; + init_checker_context(&ctx, c); UntypedExprInfoMap untyped = {}; defer (map_destroy(&untyped)); @@ -5790,7 +5798,7 @@ gb_internal void check_collect_entities_all(Checker *c) { for (isize i = 0; i < thread_count; i++) { auto *wd = &collect_entity_worker_data[i]; wd->c = c; - wd->ctx = make_checker_context(c); + init_checker_context(&wd->ctx, c); map_init(&wd->untyped); } @@ -5831,7 +5839,7 @@ gb_internal void check_export_entities(Checker *c) { for (isize i = 0; i < thread_count; i++) { auto *wd = &collect_entity_worker_data[i]; map_clear(&wd->untyped); - wd->ctx = make_checker_context(c); + init_checker_context(&wd->ctx, c); } for (auto const &entry : c->info.packages) { @@ -5898,7 +5906,8 @@ gb_internal void check_import_entities(Checker *c) { } TIME_SECTION("check_import_entities - collect file decls"); - CheckerContext ctx = make_checker_context(c); + CheckerContext ctx = {}; + init_checker_context(&ctx, c); UntypedExprInfoMap untyped = {}; defer (map_destroy(&untyped)); @@ -6247,7 +6256,8 @@ gb_internal bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *u } } - CheckerContext ctx = make_checker_context(c); + CheckerContext ctx = {}; + init_checker_context(&ctx, c); defer (destroy_checker_context(&ctx)); reset_checker_context(&ctx, pi->file, untyped); ctx.decl = pi->decl; diff --git a/src/checker.hpp b/src/checker.hpp index bda7b2746..f9c279a51 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -141,6 +141,7 @@ struct AttributeContext { bool instrumentation_exit : 1; bool no_sanitize_address : 1; bool no_sanitize_memory : 1; + bool no_sanitize_thread : 1; bool rodata : 1; bool ignore_duplicates : 1; u32 optimization_mode; // ProcedureOptimizationMode diff --git a/src/entity.cpp b/src/entity.cpp index 2b21fdcac..55aca8069 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -268,6 +268,7 @@ struct Entity { bool is_anonymous : 1; bool no_sanitize_address : 1; bool no_sanitize_memory : 1; + bool no_sanitize_thread : 1; bool is_objc_impl_or_import : 1; bool is_objc_class_method : 1; } Procedure; diff --git a/src/exact_value.cpp b/src/exact_value.cpp index f266b8b24..0f425e043 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -947,7 +947,7 @@ gb_internal gb_inline i32 cmp_f64(f64 a, f64 b) { return (a > b) - (a < b); } -gb_internal bool compare_exact_values_compound_lit(TokenKind op, ExactValue x, ExactValue y, bool *do_break_); +gb_internal bool compare_exact_values_compound_lit(TokenKind op, ExactValue x, ExactValue y); gb_internal bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) { match_exact_values(&x, &y); @@ -1060,18 +1060,13 @@ gb_internal bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) case ExactValue_Compound: if (op != Token_CmpEq && op != Token_NotEq) { - break; + return false; } if (x.kind != y.kind) { - break; + return false; } - bool do_break = false; - bool res = compare_exact_values_compound_lit(op, x, y, &do_break); - if (do_break) { - break; - } - return res; + return compare_exact_values_compound_lit(op, x, y); } GB_PANIC("Invalid comparison: %d", x.kind); diff --git a/src/linker.cpp b/src/linker.cpp index c2a3ee928..bc6268049 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -174,7 +174,7 @@ try_cross_linking:; switch (build_context.linker_choice) { case Linker_Default: break; case Linker_lld: section_name = str_lit("lld-link"); break; - #if defined(GB_SYSTEM_LINUX) + #if defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_NETBSD) case Linker_mold: section_name = str_lit("mold-link"); break; #endif #if defined(GB_SYSTEM_WINDOWS) @@ -737,7 +737,21 @@ try_cross_linking:; } if (build_context.build_mode == BuildMode_StaticLibrary) { - compiler_error("TODO(bill): -build-mode:static on non-windows targets"); + TIME_SECTION("Static Library Creation"); + + gbString ar_command = gb_string_make(heap_allocator(), ""); + defer (gb_string_free(ar_command)); + + ar_command = gb_string_appendc(ar_command, "ar rcs "); + ar_command = gb_string_append_fmt(ar_command, "\"%.*s\" ", LIT(output_filename)); + ar_command = gb_string_appendc(ar_command, object_files); + + result = system_exec_command_line_app("ar", ar_command); + if (result) { + return result; + } + + return result; } // NOTE(dweiler): We use clang as a frontend for the linker as there are diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 1cde65640..c15f326f8 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -239,9 +239,9 @@ gb_internal lbContextData *lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx gb_internal String lb_internal_gen_name_from_type(char const *prefix, Type *type) { gbString str = gb_string_make(permanent_allocator(), prefix); - u64 hash = type_hash_canonical_type(type); - str = gb_string_appendc(str, "-"); - str = gb_string_append_fmt(str, "%llu", cast(unsigned long long)hash); + str = gb_string_appendc(str, "$$"); + gbString ct = temp_canonical_string(type); + str = gb_string_append_length(str, ct, gb_string_length(ct)); String proc_name = make_string(cast(u8 const *)str, gb_string_length(str)); return proc_name; } @@ -2045,6 +2045,11 @@ gb_internal bool lb_init_global_var(lbModule *m, lbProcedure *p, Entity *e, Ast lb_emit_store(p, data, lb_emit_conv(p, gp, t_rawptr)); lb_emit_store(p, ti, lb_typeid(p->module, var_type)); } else { + i64 sz = type_size_of(e->type); + if (sz >= 4 * 1024) { + warning(init_expr, "[Possible Code Generation Issue] Non-constant initialization is large (%lld bytes), and might cause problems with LLVM", cast(long long)sz); + } + LLVMTypeRef vt = llvm_addr_type(p->module, var.var); lbValue src0 = lb_emit_conv(p, var.init, t); LLVMValueRef src = OdinLLVMBuildTransmute(p, src0.value, vt); @@ -2111,7 +2116,7 @@ gb_internal void lb_create_startup_runtime_generate_body(lbModule *m, lbProcedur for (Entity *e : info->init_procedures) { lbValue value = lb_find_procedure_value_from_entity(m, e); - lb_emit_call(p, value, {}, ProcInlining_none); + lb_emit_call(p, value, {}, ProcInlining_none, ProcTailing_none); } @@ -2157,7 +2162,7 @@ gb_internal lbProcedure *lb_create_cleanup_runtime(lbModule *main_module) { // C for (Entity *e : info->fini_procedures) { lbValue value = lb_find_procedure_value_from_entity(main_module, e); - lb_emit_call(p, value, {}, ProcInlining_none); + lb_emit_call(p, value, {}, ProcInlining_none, ProcTailing_none); } lb_end_procedure_body(p); @@ -2850,7 +2855,7 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star } lbValue startup_runtime_value = {startup_runtime->value, startup_runtime->type}; - lb_emit_call(p, startup_runtime_value, {}, ProcInlining_none); + lb_emit_call(p, startup_runtime_value, {}, ProcInlining_none, ProcTailing_none); if (build_context.command_kind == Command_test) { Type *t_Internal_Test = find_type_in_pkg(m->info, str_lit("testing"), str_lit("Internal_Test")); @@ -2917,16 +2922,16 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star auto exit_args = array_make<lbValue>(temporary_allocator(), 1); exit_args[0] = lb_emit_select(p, result, lb_const_int(m, t_int, 0), lb_const_int(m, t_int, 1)); - lb_emit_call(p, exit_runner, exit_args, ProcInlining_none); + lb_emit_call(p, exit_runner, exit_args, ProcInlining_none, ProcTailing_none); } else { if (m->info->entry_point != nullptr) { lbValue entry_point = lb_find_procedure_value_from_entity(m, m->info->entry_point); - lb_emit_call(p, entry_point, {}, ProcInlining_no_inline); + lb_emit_call(p, entry_point, {}, ProcInlining_no_inline, ProcTailing_none); } if (call_cleanup) { lbValue cleanup_runtime_value = {cleanup_runtime->value, cleanup_runtime->type}; - lb_emit_call(p, cleanup_runtime_value, {}, ProcInlining_none); + lb_emit_call(p, cleanup_runtime_value, {}, ProcInlining_none, ProcTailing_none); } if (is_dll_main) { diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index da5d91f2e..3491c0d39 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -345,6 +345,7 @@ struct lbProcedure { Ast * body; u64 tags; ProcInlining inlining; + ProcTailing tailing; bool is_foreign; bool is_export; bool is_entry_point; @@ -484,7 +485,7 @@ gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlo gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block, Ast *node); gb_internal lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t); gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right); -gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, ProcInlining inlining = ProcInlining_none); +gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, ProcInlining inlining = ProcInlining_none, ProcTailing tailing = ProcTailing_none); gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t); gb_internal lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x); @@ -618,7 +619,7 @@ gb_internal LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef p gb_internal LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, usize len, unsigned alignment, bool is_volatile); gb_internal gb_inline i64 lb_max_zero_init_size(void) { - return cast(i64)(4*build_context.int_size); + return cast(i64)(8); } gb_internal LLVMTypeRef OdinLLVMGetArrayElementType(LLVMTypeRef type); @@ -670,6 +671,7 @@ enum lbCallingConventionKind : unsigned { lbCallingConvention_PreserveAll = 15, lbCallingConvention_Swift = 16, lbCallingConvention_CXX_FAST_TLS = 17, + lbCallingConvention_PreserveNone = 21, lbCallingConvention_FirstTargetCC = 64, lbCallingConvention_X86_StdCall = 64, lbCallingConvention_X86_FastCall = 65, @@ -723,6 +725,10 @@ lbCallingConventionKind const lb_calling_convention_map[ProcCC_MAX] = { lbCallingConvention_Win64, // ProcCC_Win64, lbCallingConvention_X86_64_SysV, // ProcCC_SysV, + lbCallingConvention_PreserveNone, // ProcCC_PreserveNone, + lbCallingConvention_PreserveMost, // ProcCC_PreserveMost, + lbCallingConvention_PreserveAll, // ProcCC_PreserveAll, + }; enum : LLVMDWARFTypeEncoding { diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 22e124792..8ce2137ab 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -498,6 +498,13 @@ gb_internal LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, Bi if (big_int_is_zero(a)) { return LLVMConstNull(lb_type(m, original_type)); } + + BigInt val = {}; + big_int_init(&val, a); + + if (big_int_is_neg(&val)) { + mp_incr(&val); + } size_t sz = cast(size_t)type_size_of(original_type); u64 rop64[4] = {}; // 2 u64 is the maximum we will ever need, so doubling it will be fine :P @@ -509,7 +516,7 @@ gb_internal LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, Bi size_t nails = 0; mp_endian endian = MP_LITTLE_ENDIAN; - max_count = mp_pack_count(a, nails, size); + max_count = mp_pack_count(&val, nails, size); if (sz < max_count) { debug_print_big_int(a); gb_printf_err("%s -> %tu\n", type_to_string(original_type), sz); @@ -520,7 +527,7 @@ gb_internal LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, Bi mp_err err = mp_pack(rop, sz, &written, MP_LSB_FIRST, size, endian, nails, - a); + &val); GB_ASSERT(err == MP_OKAY); if (!is_type_endian_little(original_type)) { @@ -531,12 +538,18 @@ gb_internal LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, Bi } } + if (big_int_is_neg(a)) { + // sizeof instead of sz for sign extend to work properly + for (size_t i = 0; i < sizeof rop64; i++) { + rop[i] = ~rop[i]; + } + } + + big_int_dealloc(&val); + GB_ASSERT(!is_type_array(original_type)); LLVMValueRef value = LLVMConstIntOfArbitraryPrecision(lb_type(m, original_type), cast(unsigned)((sz+7)/8), cast(u64 *)rop); - if (big_int_is_neg(a)) { - value = LLVMConstNeg(value); - } return value; } diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index dba61df44..aba196af8 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -282,11 +282,16 @@ gb_internal lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x, return res; } - -gb_internal IntegerDivisionByZeroKind lb_check_for_integer_division_by_zero_behaviour(lbProcedure *p) { +gb_internal u64 lb_get_file_feature_flags(lbProcedure *p, Ast *expr = nullptr) { AstFile *file = nullptr; - if (p->body && p->body->file()) { + if (expr != nullptr) { + file = expr->file(); + } + + if (file != nullptr) { + // it is now set + } else if (p->body && p->body->file()) { file = p->body->file(); } else if (p->type_expr && p->type_expr->file()) { file = p->type_expr->file(); @@ -295,20 +300,29 @@ gb_internal IntegerDivisionByZeroKind lb_check_for_integer_division_by_zero_beha } if (file != nullptr && file->feature_flags_set) { - u64 flags = file->feature_flags; - if (flags & OptInFeatureFlag_IntegerDivisionByZero_Trap) { - return IntegerDivisionByZero_Trap; - } - if (flags & OptInFeatureFlag_IntegerDivisionByZero_Zero) { - return IntegerDivisionByZero_Zero; - } - if (flags & OptInFeatureFlag_IntegerDivisionByZero_Self) { - return IntegerDivisionByZero_Self; - } - if (flags & OptInFeatureFlag_IntegerDivisionByZero_AllBits) { - return IntegerDivisionByZero_AllBits; - } + return file->feature_flags; + } + return 0; +} + + + +gb_internal IntegerDivisionByZeroKind lb_check_for_integer_division_by_zero_behaviour(lbProcedure *p) { + u64 flags = lb_get_file_feature_flags(p); + + if (flags & OptInFeatureFlag_IntegerDivisionByZero_Trap) { + return IntegerDivisionByZero_Trap; + } + if (flags & OptInFeatureFlag_IntegerDivisionByZero_Zero) { + return IntegerDivisionByZero_Zero; + } + if (flags & OptInFeatureFlag_IntegerDivisionByZero_Self) { + return IntegerDivisionByZero_Self; + } + if (flags & OptInFeatureFlag_IntegerDivisionByZero_AllBits) { + return IntegerDivisionByZero_AllBits; } + return build_context.integer_division_by_zero_behaviour; } @@ -3802,6 +3816,17 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) { Type *type = type_of_expr(ue_expr); GB_ASSERT(!is_type_tuple(type)); + bool do_type_check = true; + if (build_context.no_type_assert) { + u64 feature_flags = lb_get_file_feature_flags(p, ue_expr); + if ((feature_flags & OptInFeatureFlag_ForceTypeAssert) == 0) { + do_type_check = false; + } + + } else if ((p->state_flags & StateFlag_no_type_assert) != 0) { + do_type_check = false; + } + lbValue e = lb_build_expr(p, ta->expr); Type *t = type_deref(e.type); if (is_type_union(t)) { @@ -3813,7 +3838,7 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) { Type *dst_type = type; - if (!build_context.no_type_assert && (p->state_flags & StateFlag_no_type_assert) == 0) { + if (do_type_check) { lbValue src_tag = {}; lbValue dst_tag = {}; if (is_type_union_maybe_pointer(src_type)) { @@ -3840,7 +3865,12 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) { args[4] = lb_typeid(p->module, src_type); args[5] = lb_typeid(p->module, dst_type); } - lb_emit_runtime_call(p, "type_assertion_check", args); + + char const *name = "type_assertion_check_contextless"; + if (p->context_stack.count > 0) { + name = "type_assertion_check_with_context"; + } + lb_emit_runtime_call(p, name, args); } lbValue data_ptr = v; @@ -3851,21 +3881,33 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) { v = lb_emit_load(p, v); } lbValue data_ptr = lb_emit_struct_ev(p, v, 0); - if (!build_context.no_type_assert && (p->state_flags & StateFlag_no_type_assert) == 0) { + if (do_type_check) { GB_ASSERT(!build_context.no_rtti); lbValue any_id = lb_emit_struct_ev(p, v, 1); + isize arg_count = 6; + if (build_context.no_rtti) { + arg_count = 4; + } + lbValue id = lb_typeid(p->module, type); lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id); - auto args = array_make<lbValue>(permanent_allocator(), 6); + auto args = array_make<lbValue>(permanent_allocator(), arg_count); args[0] = ok; lb_set_file_line_col(p, array_slice(args, 1, args.count), pos); - args[4] = any_id; - args[5] = id; - lb_emit_runtime_call(p, "type_assertion_check", args); + if (!build_context.no_rtti) { + args[4] = any_id; + args[5] = id; + } + + char const *name = "type_assertion_check_contextless"; + if (p->context_stack.count > 0) { + name = "type_assertion_check_with_context"; + } + lb_emit_runtime_call(p, name, args); } return lb_emit_conv(p, data_ptr, tv.type); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 4ebb40d96..c0e57143d 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2628,6 +2628,8 @@ gb_internal bool lb_apply_thread_local_model(LLVMValueRef value, String model) { LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel; if (model == "default") { mode = LLVMGeneralDynamicTLSModel; + } else if (model == "globaldynamic") { + mode = LLVMGeneralDynamicTLSModel; } else if (model == "localdynamic") { mode = LLVMLocalDynamicTLSModel; } else if (model == "initialexec") { diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index 4971b1f10..4131f32bf 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -309,7 +309,15 @@ gb_internal void lb_run_remove_dead_instruction_pass(lbProcedure *p) { // NOTE(bill): Explicit instructions are set here because some instructions could have side effects switch (LLVMGetInstructionOpcode(curr_instr)) { - // case LLVMAlloca: + case LLVMAlloca: + if (map_get(&p->tuple_fix_map, curr_instr) != nullptr) { + // NOTE(bill, 2025-12-27): Remove temporary tuple fix alloca instructions + // if they are never used + removal_count += 1; + LLVMInstructionEraseFromParent(curr_instr); + was_dead_instructions = true; + } + break; case LLVMLoad: if (LLVMGetVolatile(curr_instr)) { break; diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 27167aefd..837d7ce48 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -12,9 +12,9 @@ gb_internal void lb_mem_copy_overlapping(lbProcedure *p, lbValue dst, lbValue sr len = lb_emit_conv(p, len, t_int); char const *name = "llvm.memmove"; - if (LLVMIsConstant(len.value)) { + if (!p->is_startup && LLVMIsConstant(len.value)) { i64 const_len = cast(i64)LLVMConstIntGetSExtValue(len.value); - if (const_len <= 4*build_context.int_size) { + if (const_len <= lb_max_zero_init_size()) { name = "llvm.memmove.inline"; } } @@ -41,9 +41,9 @@ gb_internal void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValu len = lb_emit_conv(p, len, t_int); char const *name = "llvm.memcpy"; - if (LLVMIsConstant(len.value)) { + if (!p->is_startup && LLVMIsConstant(len.value)) { i64 const_len = cast(i64)LLVMConstIntGetSExtValue(len.value); - if (const_len <= 4*build_context.int_size) { + if (const_len <= lb_max_zero_init_size()) { name = "llvm.memcpy.inline"; } } @@ -117,6 +117,7 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i p->type_expr = decl->type_expr; p->body = pl->body; p->inlining = pl->inlining; + p->tailing = pl->tailing; p->is_foreign = entity->Procedure.is_foreign; p->is_export = entity->Procedure.is_export; p->is_entry_point = false; @@ -152,6 +153,10 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i lb_ensure_abi_function_type(m, p); lb_add_function_type_attributes(p->value, p->abi_function_type, p->abi_function_type->calling_convention); + if (build_context.disable_unwind) { + lb_add_attribute_to_proc(m, p->value, "nounwind"); + } + if (pt->Proc.diverging) { lb_add_attribute_to_proc(m, p->value, "noreturn"); } @@ -346,7 +351,7 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i if (build_context.sanitizer_flags & SanitizerFlag_Memory && !entity->Procedure.no_sanitize_memory) { lb_add_attribute_to_proc(m, p->value, "sanitize_memory"); } - if (build_context.sanitizer_flags & SanitizerFlag_Thread) { + if (build_context.sanitizer_flags & SanitizerFlag_Thread && !entity->Procedure.no_sanitize_thread) { lb_add_attribute_to_proc(m, p->value, "sanitize_thread"); } } @@ -387,6 +392,7 @@ gb_internal lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name p->body = nullptr; p->tags = 0; p->inlining = ProcInlining_none; + p->tailing = ProcTailing_none; p->is_foreign = false; p->is_export = false; p->is_entry_point = false; @@ -855,7 +861,7 @@ gb_internal Array<lbValue> lb_value_to_array(lbProcedure *p, gbAllocator const & -gb_internal 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_internal 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, ProcTailing tailing) { GB_ASSERT(p->module->ctx == LLVMGetTypeContext(LLVMTypeOf(value.value))); unsigned arg_count = cast(unsigned)processed_args.count; @@ -972,6 +978,15 @@ gb_internal lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue break; } + switch (tailing) { + case ProcTailing_none: + break; + case ProcTailing_must_tail: + LLVMSetTailCall(ret, true); + LLVMSetTailCallKind(ret, LLVMTailCallKindMustTail); + break; + } + lbValue res = {}; res.value = ret; res.type = abi_rt; @@ -1045,7 +1060,7 @@ gb_internal lbValue lb_emit_conjugate(lbProcedure *p, lbValue val, Type *type) { return lb_emit_load(p, res); } -gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, ProcInlining inlining) { +gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, ProcInlining inlining, ProcTailing tailing) { lbModule *m = p->module; Type *pt = base_type(value.type); @@ -1168,10 +1183,10 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c if (return_by_pointer) { lbValue return_ptr = lb_add_local_generated(p, rt, true).addr; - lb_emit_call_internal(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining); + lb_emit_call_internal(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining, tailing); result = lb_emit_load(p, return_ptr); } else if (rt != nullptr) { - result = lb_emit_call_internal(p, value, {}, processed_args, rt, context_ptr, inlining); + result = lb_emit_call_internal(p, value, {}, processed_args, rt, context_ptr, inlining, tailing); if (ft->ret.cast_type) { result.value = OdinLLVMBuildTransmute(p, result.value, ft->ret.cast_type); } @@ -1184,7 +1199,7 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c result = lb_emit_conv(p, result, rt); } } else { - lb_emit_call_internal(p, value, {}, processed_args, nullptr, context_ptr, inlining); + lb_emit_call_internal(p, value, {}, processed_args, nullptr, context_ptr, inlining, tailing); } if (original_rt != rt) { @@ -1212,15 +1227,6 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c } tuple_fix_values[ret_count-1] = result; - #if 0 - for (isize j = 0; j < ret_count; j++) { - tuple_geps[j] = lb_emit_struct_ep(p, result_ptr, cast(i32)j); - } - for (isize j = 0; j < ret_count; j++) { - lb_emit_store(p, tuple_geps[j], tuple_fix_values[j]); - } - #endif - result = lb_emit_load(p, result_ptr); lbTupleFix tf = {tuple_fix_values}; @@ -4411,6 +4417,25 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { return lb_handle_objc_auto_send(p, expr, slice(call_args, 0, call_args.count)); } - return lb_emit_call(p, value, call_args, ce->inlining); + + ProcInlining inlining = ce->inlining; + ProcTailing tailing = ce->tailing; + + if (tailing == ProcTailing_none && + proc_entity && proc_entity->kind == Entity_Procedure && + proc_entity->decl_info && + proc_entity->decl_info->proc_lit) { + ast_node(pl, ProcLit, proc_entity->decl_info->proc_lit); + + if (pl->inlining != ProcInlining_none) { + inlining = pl->inlining; + } + + if (pl->tailing != ProcTailing_none) { + tailing = pl->tailing; + } + } + + return lb_emit_call(p, value, call_args, inlining, tailing); } diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index 3dbcea4fb..81755af2d 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -1434,12 +1434,13 @@ gb_internal void lb_build_unroll_range_stmt(lbProcedure *p, AstUnrollRangeStmt * if (unroll_count_ev.kind == ExactValue_Invalid) { - GB_ASSERT(expr->tav.mode == Addressing_Constant); Type *t = base_type(expr->tav.type); switch (t->kind) { case Type_Basic: + GB_ASSERT(expr->tav.mode == Addressing_Constant); + GB_ASSERT(is_type_string(t)); { ExactValue value = expr->tav.value; @@ -1720,7 +1721,45 @@ gb_internal void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope * Ast *clause = body->stmts[i]; ast_node(cc, CaseClause, clause); - body_blocks[i] = lb_create_block(p, cc->list.count == 0 ? "switch.default.body" : "switch.case.body"); + char const *block_name = cc->list.count == 0 ? "switch.default.body" : "switch.case.body"; + + if (is_trivial && cc->list.count >= 1) { + gbString bn = gb_string_make(heap_allocator(), "switch.case."); + + Ast *first = cc->list[0]; + if (first->tav.mode == Addressing_Type) { + bn = gb_string_appendc(bn, "type."); + } else if (is_type_rune(first->tav.type)) { + bn = gb_string_appendc(bn, "rune."); + } else { + bn = gb_string_appendc(bn, "value."); + } + + for_array(i, cc->list) { + if (i > 0) { + bn = gb_string_appendc(bn, ".."); + } + + Ast *expr = cc->list[i]; + if (expr->tav.mode == Addressing_Type) { + bn = write_type_to_string(bn, expr->tav.type, false); + } else { + ExactValue value = expr->tav.value; + if (is_type_rune(expr->tav.type) && value.kind == ExactValue_Integer) { + Rune r = cast(Rune)exact_value_to_i64(value); + u8 rune_temp[6] = {}; + isize size = gb_utf8_encode_rune(rune_temp, r); + bn = gb_string_append_length(bn, rune_temp, size); + } else { + bn = write_exact_value_to_string(bn, value, 1024); + } + } + } + + block_name = cast(char const *)bn; + } + + body_blocks[i] = lb_create_block(p, block_name); if (cc->list.count == 0) { default_block = body_blocks[i]; } @@ -1962,7 +2001,7 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss num_cases += cc->list.count; if (cc->list.count == 0) { GB_ASSERT(default_block == nullptr); - default_block = lb_create_block(p, "typeswitch.default.body"); + default_block = lb_create_block(p, "typeswitch.case.default"); else_block = default_block; } } @@ -2042,7 +2081,16 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss continue; } - lbBlock *body = lb_create_block(p, "typeswitch.body"); + char const *body_name = "typeswitch.case"; + + if (!are_types_identical(case_entity->type, parent_base_type)) { + gbString canonical_name = temp_canonical_string(case_entity->type); + gbString bn = gb_string_make(heap_allocator(), "typeswitch.case."); + bn = gb_string_append_length(bn, canonical_name, gb_string_length(canonical_name)); + body_name = cast(char const *)bn; + } + + lbBlock *body = lb_create_block(p, body_name); if (p->debug_info != nullptr) { LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, clause)); } @@ -2122,14 +2170,7 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss gb_internal void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) { for_array(i, vd->names) { lbValue value = {}; - if (vd->values.count > 0) { - GB_ASSERT(vd->names.count == vd->values.count); - Ast *ast_value = vd->values[i]; - GB_ASSERT(ast_value->tav.mode == Addressing_Constant || - ast_value->tav.mode == Addressing_Invalid); - value = lb_const_value(p->module, ast_value->tav.type, ast_value->tav.value, LB_CONST_CONTEXT_DEFAULT_NO_LOCAL); - } Ast *ident = vd->names[i]; GB_ASSERT(!is_blank_ident(ident)); @@ -2137,6 +2178,19 @@ gb_internal void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) { GB_ASSERT(e->flags & EntityFlag_Static); String name = e->token.string; + if (vd->values.count > 0) { + GB_ASSERT(vd->names.count == vd->values.count); + Ast *ast_value = vd->values[i]; + GB_ASSERT(ast_value->tav.mode == Addressing_Constant || + ast_value->tav.mode == Addressing_Invalid); + + auto cc = LB_CONST_CONTEXT_DEFAULT_NO_LOCAL; + if (e->Variable.is_rodata) { + cc.is_rodata = true; + } + value = lb_const_value(p->module, ast_value->tav.type, ast_value->tav.value, cc); + } + String mangled_name = {}; { gbString str = gb_string_make_length(permanent_allocator(), p->name.text, p->name.len); diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 9ddbd1f9c..929239486 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -89,7 +89,7 @@ gb_internal LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef p bool is_inlinable = false; i64 const_len = 0; - if (LLVMIsConstant(len)) { + if (!p->is_startup && LLVMIsConstant(len)) { const_len = cast(i64)LLVMConstIntGetSExtValue(len); // TODO(bill): Determine when it is better to do the `*.inline` versions if (const_len <= lb_max_zero_init_size()) { @@ -803,7 +803,12 @@ gb_internal lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type args[5] = lb_typeid(m, dst_type); args[6] = lb_emit_conv(p, value_, t_rawptr); } - lb_emit_runtime_call(p, "type_assertion_check2", args); + + char const *name = "type_assertion_check2_contextless"; + if (p->context_stack.count > 0) { + name = "type_assertion_check2_with_context"; + } + lb_emit_runtime_call(p, name, args); } return lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 0)); @@ -877,7 +882,11 @@ gb_internal lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *ty args[5] = dst_typeid; args[6] = lb_emit_struct_ev(p, value, 0); } - lb_emit_runtime_call(p, "type_assertion_check2", args); + char const *name = "type_assertion_check2_contextless"; + if (p->context_stack.count > 0) { + name = "type_assertion_check2_with_context"; + } + lb_emit_runtime_call(p, name, args); } return lb_addr(lb_emit_struct_ep(p, v.addr, 0)); diff --git a/src/main.cpp b/src/main.cpp index 9a5df8aea..624869e65 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -324,8 +324,6 @@ enum BuildFlagKind { BuildFlag_NoCRT, BuildFlag_NoRPath, BuildFlag_NoEntryPoint, - BuildFlag_UseLLD, - BuildFlag_UseRADLink, BuildFlag_Linker, BuildFlag_UseSeparateModules, BuildFlag_UseSingleModule, @@ -363,6 +361,8 @@ enum BuildFlagKind { BuildFlag_RelocMode, BuildFlag_DisableRedZone, + BuildFlag_DisableUnwind, + BuildFlag_DisallowDo, BuildFlag_DefaultToNilAllocator, BuildFlag_DefaultToPanicAllocator, @@ -554,8 +554,6 @@ gb_internal 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_NoRPath, str_lit("no-rpath"), 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_UseRADLink, str_lit("radlink"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_Linker, str_lit("linker"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_UseSeparateModules, str_lit("use-separate-modules"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_UseSingleModule, str_lit("use-single-module"), BuildFlagParam_None, Command__does_build); @@ -592,6 +590,8 @@ gb_internal bool parse_build_flags(Array<String> args) { add_flag(&build_flags, BuildFlag_RelocMode, str_lit("reloc-mode"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_DisableRedZone, str_lit("disable-red-zone"), BuildFlagParam_None, Command__does_build); + add_flag(&build_flags, BuildFlag_DisableUnwind, str_lit("disable-unwind"), BuildFlagParam_None, Command__does_build); + 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_DefaultToPanicAllocator, str_lit("default-to-panic-allocator"),BuildFlagParam_None, Command__does_check); @@ -1252,14 +1252,6 @@ gb_internal bool parse_build_flags(Array<String> args) { case BuildFlag_NoThreadLocal: build_context.no_thread_local = true; break; - case BuildFlag_UseLLD: - gb_printf_err("Warning: Use of -lld has been deprecated in favour of -linker:lld\n"); - build_context.linker_choice = Linker_lld; - break; - case BuildFlag_UseRADLink: - gb_printf_err("Warning: Use of -lld has been deprecated in favour of -linker:radlink\n"); - build_context.linker_choice = Linker_radlink; - break; case BuildFlag_Linker: { GB_ASSERT(value.kind == ExactValue_String); @@ -1424,6 +1416,10 @@ gb_internal bool parse_build_flags(Array<String> args) { case BuildFlag_DisableRedZone: build_context.disable_red_zone = true; break; + case BuildFlag_DisableUnwind: + build_context.disable_unwind = true; + break; + case BuildFlag_DisallowDo: build_context.disallow_do = true; break; @@ -2971,6 +2967,10 @@ gb_internal int print_show_help(String const arg0, String command, String option if (check) { if (print_flag("-target:<string>")) { print_usage_line(2, "Sets the target for the executable to be built in."); + print_usage_line(2, "Examples:"); + print_usage_line(3, "-target:linux_amd64"); + print_usage_line(3, "-target:windows_amd64"); + print_usage_line(3, "-target:\"?\" for a list"); } if (print_flag("-terse-errors")) { @@ -4034,6 +4034,14 @@ int main(int arg_count, char const **arg_ptr) { remove_temp_files(gen); } + if (any_errors()) { + print_all_errors(); + return 1; + } + if (any_warnings()) { + print_all_errors(); + } + end_of_code_gen:; if (build_context.export_dependencies_format != DependenciesExportUnspecified) { diff --git a/src/parser.cpp b/src/parser.cpp index e27e184d0..360537ab7 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1230,7 +1230,7 @@ gb_internal Ast *ast_dynamic_array_type(AstFile *f, Token token, Ast *elem) { } gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice<Ast *> fields, isize field_count, - Ast *polymorphic_params, bool is_packed, bool is_raw_union, bool is_all_or_none, + Ast *polymorphic_params, bool is_packed, bool is_raw_union, bool is_all_or_none, bool is_simple, Ast *align, Ast *min_field_align, Ast *max_field_align, Token where_token, Array<Ast *> const &where_clauses) { Ast *result = alloc_ast_node(f, Ast_StructType); @@ -1241,6 +1241,7 @@ gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice<Ast *> fields, i result->StructType.is_packed = is_packed; result->StructType.is_raw_union = is_raw_union; result->StructType.is_all_or_none = is_all_or_none; + result->StructType.is_simple = is_simple; result->StructType.align = align; result->StructType.min_field_align = min_field_align; result->StructType.max_field_align = max_field_align; @@ -2176,38 +2177,50 @@ gb_internal bool ast_on_same_line(Token const &x, Ast *yp) { return x.pos.line == y.pos.line; } -gb_internal Ast *parse_force_inlining_operand(AstFile *f, Token token) { +gb_internal Ast *parse_inlining_or_tailing_operand(AstFile *f, Token token) { Ast *expr = parse_unary_expr(f, false); Ast *e = strip_or_return_expr(expr); if (e == nullptr) { return expr; } if (e->kind != Ast_ProcLit && e->kind != Ast_CallExpr) { - syntax_error(expr, "%.*s must be followed by a procedure literal or call, got %.*s", LIT(token.string), LIT(ast_strings[expr->kind])); + syntax_error(expr, "%.*s must be followed by a procedure literal or call, got %.*s", LIT(token.string), LIT(ast_strings[e->kind])); return ast_bad_expr(f, token, f->curr_token); } ProcInlining pi = ProcInlining_none; + ProcTailing pt = ProcTailing_none; if (token.kind == Token_Ident) { if (token.string == "force_inline") { pi = ProcInlining_inline; } else if (token.string == "force_no_inline") { pi = ProcInlining_no_inline; + } else if (token.string == "must_tail") { + pt = ProcTailing_must_tail; } } if (pi != ProcInlining_none) { if (e->kind == Ast_ProcLit) { - if (expr->ProcLit.inlining != ProcInlining_none && - expr->ProcLit.inlining != pi) { + if (e->ProcLit.inlining != ProcInlining_none && + e->ProcLit.inlining != pi) { syntax_error(expr, "Cannot apply both '#force_inline' and '#force_no_inline' to a procedure literal"); } - expr->ProcLit.inlining = pi; + e->ProcLit.inlining = pi; } else if (e->kind == Ast_CallExpr) { - if (expr->CallExpr.inlining != ProcInlining_none && - expr->CallExpr.inlining != pi) { + if (e->CallExpr.inlining != ProcInlining_none && + e->CallExpr.inlining != pi) { syntax_error(expr, "Cannot apply both '#force_inline' and '#force_no_inline' to a procedure call"); } - expr->CallExpr.inlining = pi; + e->CallExpr.inlining = pi; + } + } + + if (pt != ProcTailing_none) { + if (e->kind == Ast_ProcLit) { + syntax_error(expr, "'#must_call' can only be applied to a procedure call, not the procedure literal"); + e->ProcLit.tailing = pt; + } else if (e->kind == Ast_CallExpr) { + e->CallExpr.tailing = pt; } } @@ -2507,8 +2520,9 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { syntax_error(tag, "#relative types have now been removed in favour of \"core:relative\""); return ast_relative_type(f, tag, type); } else if (name.string == "force_inline" || - name.string == "force_no_inline") { - return parse_force_inlining_operand(f, name); + name.string == "force_no_inline" || + name.string == "must_tail") { + return parse_inlining_or_tailing_operand(f, name); } return ast_basic_directive(f, token, name); } @@ -2775,6 +2789,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { bool is_packed = false; bool is_all_or_none = false; bool is_raw_union = false; + bool is_simple = false; Ast *align = nullptr; Ast *min_field_align = nullptr; Ast *max_field_align = nullptr; @@ -2803,7 +2818,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { } is_packed = true; } else if (tag.string == "all_or_none") { - if (is_packed) { + if (is_all_or_none) { syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string)); } is_all_or_none = true; @@ -2856,11 +2871,16 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { error_line("\tSuggestion: #max_field_align(%s)", s); gb_string_free(s); } - }else if (tag.string == "raw_union") { + } else if (tag.string == "raw_union") { if (is_raw_union) { syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string)); } is_raw_union = true; + } else if (tag.string == "simple") { + if (is_simple) { + syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string)); + } + is_simple = true; } else { syntax_error(tag, "Invalid struct tag '#%.*s'", LIT(tag.string)); } @@ -2906,7 +2926,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { parser_check_polymorphic_record_parameters(f, polymorphic_params); return ast_struct_type(f, token, decls, name_count, - polymorphic_params, is_packed, is_raw_union, is_all_or_none, + polymorphic_params, is_packed, is_raw_union, is_all_or_none, is_simple, align, min_field_align, max_field_align, where_token, where_clauses); } break; @@ -4008,6 +4028,10 @@ gb_internal ProcCallingConvention string_to_calling_convention(String const &s) if (s == "win64") return ProcCC_Win64; if (s == "sysv") return ProcCC_SysV; + if (s == "preserve/none") return ProcCC_PreserveNone; + if (s == "preserve/most") return ProcCC_PreserveMost; + if (s == "preserve/all") return ProcCC_PreserveAll; + if (s == "system") { if (build_context.metrics.os == TargetOs_windows) { return ProcCC_StdCall; @@ -5054,6 +5078,10 @@ gb_internal Ast *parse_import_decl(AstFile *f, ImportDeclKind kind) { syntax_error(import_name, "'using import' is not allowed, please use the import name explicitly"); } + if (file_path.string == "\".\"") { + syntax_error(import_name, "Cannot cyclicly import packages"); + } + expect_semicolon(f); return s; } @@ -5399,8 +5427,9 @@ gb_internal Ast *parse_stmt(AstFile *f) { expect_semicolon(f); return stmt; } else if (name.string == "force_inline" || - name.string == "force_no_inline") { - Ast *expr = parse_force_inlining_operand(f, name); + name.string == "force_no_inline" || + name.string == "must_tail") { + Ast *expr = parse_inlining_or_tailing_operand(f, name); Ast *stmt = ast_expr_stmt(f, expr); expect_semicolon(f); return stmt; @@ -5818,6 +5847,11 @@ gb_internal AstPackage *try_add_import_path(Parser *p, String path, String const return nullptr; } + if (string_ends_with(path, str_lit(".odin"))) { + error(pos, "'import' declarations cannot import directories with a .odin extension/suffix"); + return nullptr; + } + isize files_with_ext = 0; isize files_to_reserve = 1; // always reserve 1 for (FileInfo fi : list) { @@ -5996,6 +6030,12 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node has_windows_drive = true; } } + + for (isize i = 0; i < original_string.len; i++) { + if (original_string.text[i] == '\\') { + original_string.text[i] = '/'; + } + } } #endif @@ -6205,9 +6245,28 @@ gb_internal String build_tag_get_token(String s, String *out) { return s; } +// returns true on failure +gb_internal bool build_require_space_after(String s, String prefix) { + GB_ASSERT(string_starts_with(s, prefix)); + + if (s.len == prefix.len) { + return false; + } + String stripped = string_trim_whitespace(substring(s, prefix.len, s.len)); + + if (s[prefix.len] != ' ' && stripped.len != 0) { + return true; + } + return false; +} + gb_internal bool parse_build_tag(Token token_for_pos, String s) { String const prefix = str_lit("build"); GB_ASSERT(string_starts_with(s, prefix)); + if (build_require_space_after(s, prefix)) { + syntax_error(token_for_pos, "Expected a space after #+%.*s", LIT(prefix)); + return true; + } s = string_trim_whitespace(substring(s, prefix.len, s.len)); if (s.len == 0) { @@ -6331,19 +6390,21 @@ gb_internal String vet_tag_get_token(String s, String *out, bool allow_colon) { } -gb_internal u64 parse_vet_tag(Token token_for_pos, String s) { +gb_internal u64 parse_vet_tag(Token token_for_pos, String s, u64 base_vet_flags) { String const prefix = str_lit("vet"); GB_ASSERT(string_starts_with(s, prefix)); + if (build_require_space_after(s, prefix)) { + syntax_error(token_for_pos, "Expected a space after #+%.*s", LIT(prefix)); + return true; + } s = string_trim_whitespace(substring(s, prefix.len, s.len)); + u64 vet_flags = base_vet_flags; + if (s.len == 0) { - return VetFlag_All; + vet_flags |= VetFlag_All; } - - u64 vet_flags = 0; - u64 vet_not_flags = 0; - while (s.len > 0) { String p = string_trim_whitespace(vet_tag_get_token(s, &s, /*allow_colon*/false)); if (p.len == 0) { @@ -6356,16 +6417,16 @@ gb_internal u64 parse_vet_tag(Token token_for_pos, String s) { p = substring(p, 1, p.len); if (p.len == 0) { syntax_error(token_for_pos, "Expected a vet flag name after '!'"); - return build_context.vet_flags; + return vet_flags; } } u64 flag = get_vet_flag_from_name(p); if (flag != VetFlag_NONE) { if (is_notted) { - vet_not_flags |= flag; + vet_flags = vet_flags &~ flag; } else { - vet_flags |= flag; + vet_flags |= flag; } } else { ERROR_BLOCK(); @@ -6383,26 +6444,20 @@ gb_internal u64 parse_vet_tag(Token token_for_pos, String s) { error_line("\tcast\n"); error_line("\ttabs\n"); error_line("\texplicit-allocators\n"); - return build_context.vet_flags; + return vet_flags; } } - if (vet_flags == 0 && vet_not_flags == 0) { - return build_context.vet_flags; - } - if (vet_flags == 0 && vet_not_flags != 0) { - return build_context.vet_flags &~ vet_not_flags; - } - if (vet_flags != 0 && vet_not_flags == 0) { - return vet_flags; - } - GB_ASSERT(vet_flags != 0 && vet_not_flags != 0); - return vet_flags &~ vet_not_flags; + return vet_flags; } gb_internal u64 parse_feature_tag(Token token_for_pos, String s) { String const prefix = str_lit("feature"); GB_ASSERT(string_starts_with(s, prefix)); + if (build_require_space_after(s, prefix)) { + syntax_error(token_for_pos, "Expected a space after #+%.*s", LIT(prefix)); + return true; + } s = string_trim_whitespace(substring(s, prefix.len, s.len)); if (s.len == 0) { @@ -6578,7 +6633,7 @@ gb_internal bool parse_file_tag(const String &lc, const Token &tok, AstFile *f) return false; } } else if (string_starts_with(lc, str_lit("vet"))) { - f->vet_flags = parse_vet_tag(tok, lc); + f->vet_flags = parse_vet_tag(tok, lc, ast_file_vet_flags(f)); f->vet_flags_set = true; } else if (string_starts_with(lc, str_lit("test"))) { if ((build_context.command_kind & Command_test) == 0) { diff --git a/src/parser.hpp b/src/parser.hpp index 71b61d95f..39f56ffae 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -263,12 +263,17 @@ struct ForeignFileWorkerData { -enum ProcInlining { - ProcInlining_none = 0, - ProcInlining_inline = 1, +enum ProcInlining : u8 { + ProcInlining_none = 0, + ProcInlining_inline = 1, ProcInlining_no_inline = 2, }; +enum ProcTailing : u8 { + ProcTailing_none = 0, + ProcTailing_must_tail = 1, +}; + enum ProcTag { ProcTag_bounds_check = 1<<0, ProcTag_no_bounds_check = 1<<1, @@ -296,6 +301,9 @@ enum ProcCallingConvention : i32 { ProcCC_Win64 = 9, ProcCC_SysV = 10, + ProcCC_PreserveNone = 11, + ProcCC_PreserveMost = 12, + ProcCC_PreserveAll = 13, ProcCC_MAX, @@ -315,6 +323,9 @@ gb_global char const *proc_calling_convention_strings[ProcCC_MAX] = { "inlineasm", "win64", "sysv", + "preserve/none", + "preserve/most", + "preserve/all", }; gb_internal ProcCallingConvention default_calling_convention(void) { @@ -441,6 +452,7 @@ struct AstSplitArgs { Ast *body; \ u64 tags; \ ProcInlining inlining; \ + ProcTailing tailing; \ Token where_token; \ Slice<Ast *> where_clauses; \ DeclInfo *decl; \ @@ -486,6 +498,7 @@ AST_KIND(_ExprBegin, "", bool) \ Token close; \ Token ellipsis; \ ProcInlining inlining; \ + ProcTailing tailing; \ bool optional_ok_one; \ bool was_selector; \ AstSplitArgs *split_args; \ @@ -767,6 +780,7 @@ AST_KIND(_TypeBegin, "", bool) \ bool is_raw_union; \ bool is_no_copy; \ bool is_all_or_none; \ + bool is_simple; \ }) \ AST_KIND(UnionType, "union type", struct { \ Scope *scope; \ diff --git a/src/string.cpp b/src/string.cpp index 9c08114a7..c26457acf 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -635,26 +635,107 @@ gb_internal String normalize_path(gbAllocator a, String const &path, String cons #elif defined(GB_SYSTEM_UNIX) || defined(GB_SYSTEM_OSX) #include <wchar.h> - gb_internal int convert_multibyte_to_widechar(char const *multibyte_input, usize input_length, wchar_t *output, usize output_size) { - String string = copy_string(heap_allocator(), make_string(cast(u8 const*)multibyte_input, input_length)); /* Guarantee NULL terminator */ - u8* input = string.text; + gb_internal void utf16_encode_surrogate_pair(Rune r, u16 *r1, u16 *r2) { + static Rune const _surr_self = 0x10000; + static Rune const _surr1 = 0xd800; + static Rune const _surr2 = 0xdc00; + Rune r_ = r - _surr_self; + *r1 = _surr1 + ((r_ >> 10) & 0x3ff); + *r2 = _surr2 + (r_ & 0x3ff); + } + + gb_internal isize utf16_decode(u16 const *s, isize n, Rune *r) { + static Rune const _surr1 = 0xd800; + static Rune const _surr2 = 0xdc00; + static Rune const _surr3 = 0xe000; + static Rune const _surr_self = 0x10000; + if (n < 1) { + *r = GB_RUNE_INVALID; + return 0; + } + u16 c = s[0]; + if (c < 0xd800 || c > 0xdfff) { + *r = cast(Rune)c; + return 1; + } + if (c >= 0xdc00) { + *r = GB_RUNE_INVALID; + return 1; + } + if (n < 2) { + *r = GB_RUNE_INVALID; + return 1; + } + u16 c2 = s[1]; + if (c2 < 0xdc00 || c2 > 0xdfff) { + *r = GB_RUNE_INVALID; + return 1; + } + *r = (((c-_surr1)<<10) | (c2 - _surr2)) + _surr_self; + return 2; + } - mbstate_t ps = { 0 }; - size_t result = mbsrtowcs(output, cast(const char**)&input, output_size, &ps); + gb_internal int convert_multibyte_to_widechar(char const *multibyte_input, usize input_length, wchar_t *output, usize output_size) { + u16 *out = cast(u16 *)output; + String s = make_string(cast(u8 const *)multibyte_input, input_length); + isize i = 0; + isize output_len = 0; + while (i < s.len) { + Rune r = 0; + isize width = utf8_decode(s.text + i, s.len - i, &r); + if (r == GB_RUNE_INVALID) { + return -1; + } + i += width; + if (r < 0x10000) { + if (out) { + if (cast(usize)output_len+1 > output_size) { + return -1; + } + out[output_len] = cast(u16)r; + } + output_len += 1; + } else { + if (out) { + if (cast(usize)output_len+2 > output_size) { + return -1; + } + u16 r1, r2; + utf16_encode_surrogate_pair(r, &r1, &r2); + out[output_len+0] = r1; + out[output_len+1] = r2; + } + output_len += 2; + } + } - gb_free(heap_allocator(), string.text); - return (result == (size_t)-1) ? -1 : (int)result; + return cast(int)output_len; } gb_internal int convert_widechar_to_multibyte(wchar_t const *widechar_input, usize input_length, char* output, usize output_size) { - String string = copy_string(heap_allocator(), make_string(cast(u8 const*)widechar_input, input_length)); /* Guarantee NULL terminator */ - u8* input = string.text; + u16 const *in = cast(u16 const *)widechar_input; + isize i = 0; + isize output_len = 0; + while (i < input_length) { + Rune r; + isize width = utf16_decode(in + i, input_length - i, &r); + if (r == GB_RUNE_INVALID) { + return -1; + } + i += width; - mbstate_t ps = { 0 }; - size_t result = wcsrtombs(output, cast(const wchar_t**)&input, output_size, &ps); + u8 buf[4]; + isize char_len = gb_utf8_encode_rune(buf, r); - gb_free(heap_allocator(), string.text); - return (result == (size_t)-1) ? -1 : (int)result; + if(output) { + if (cast(usize)output_len+cast(usize)char_len > output_size) { + return -1; + } + gb_memmove(output + output_len, buf, char_len); + } + output_len += char_len; + } + return cast(int)output_len; } #else #error Implement system diff --git a/src/types.cpp b/src/types.cpp index 18e3b56ac..a7f2bfda2 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -163,6 +163,7 @@ struct TypeStruct { bool is_packed : 1; bool is_raw_union : 1; bool is_all_or_none : 1; + bool is_simple : 1; bool is_poly_specialized : 1; std::atomic<bool> are_offsets_being_processed; @@ -2701,12 +2702,16 @@ gb_internal bool is_type_simple_compare(Type *t) { case Type_SoaPointer: case Type_Proc: case Type_BitSet: + case Type_BitField: return true; case Type_Matrix: return is_type_simple_compare(t->Matrix.elem); case Type_Struct: + if (t->Struct.is_simple) { + return true; + } for_array(i, t->Struct.fields) { Entity *f = t->Struct.fields[i]; if (!is_type_simple_compare(f->type)) { @@ -2728,6 +2733,16 @@ gb_internal bool is_type_simple_compare(Type *t) { case Type_SimdVector: return is_type_simple_compare(t->SimdVector.elem); + case Type_Tuple: + if (t->Tuple.variables.count == 1) { + return is_type_simple_compare(t->Tuple.variables[0]->type); + } + break; + + case Type_Slice: + case Type_DynamicArray: + case Type_Map: + return false; } return false; @@ -2757,12 +2772,16 @@ gb_internal bool is_type_nearly_simple_compare(Type *t) { case Type_SoaPointer: case Type_Proc: case Type_BitSet: + case Type_BitField: return true; case Type_Matrix: return is_type_nearly_simple_compare(t->Matrix.elem); case Type_Struct: + if (t->Struct.is_simple) { + return true; + } for_array(i, t->Struct.fields) { Entity *f = t->Struct.fields[i]; if (!is_type_nearly_simple_compare(f->type)) { @@ -2784,6 +2803,17 @@ gb_internal bool is_type_nearly_simple_compare(Type *t) { case Type_SimdVector: return is_type_nearly_simple_compare(t->SimdVector.elem); + case Type_Tuple: + if (t->Tuple.variables.count == 1) { + return is_type_nearly_simple_compare(t->Tuple.variables[0]->type); + } + break; + + case Type_Slice: + case Type_DynamicArray: + case Type_Map: + return false; + } return false; @@ -5099,9 +5129,11 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha str = gb_string_appendc(str, ")"); } - if (type->Struct.is_packed) str = gb_string_appendc(str, " #packed"); - if (type->Struct.is_raw_union) str = gb_string_appendc(str, " #raw_union"); + if (type->Struct.is_packed) str = gb_string_appendc(str, " #packed"); + if (type->Struct.is_raw_union) str = gb_string_appendc(str, " #raw_union"); if (type->Struct.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Struct.custom_align); + if (type->Struct.is_all_or_none) str = gb_string_appendc(str, " #all_or_none"); + if (type->Struct.is_simple) str = gb_string_appendc(str, " #simple"); str = gb_string_appendc(str, " {"); @@ -5210,40 +5242,12 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha case Type_Proc: str = gb_string_appendc(str, "proc"); - switch (type->Proc.calling_convention) { - case ProcCC_Odin: - if (default_calling_convention() != ProcCC_Odin) { - str = gb_string_appendc(str, " \"odin\" "); - } - break; - case ProcCC_Contextless: - if (default_calling_convention() != ProcCC_Contextless) { - str = gb_string_appendc(str, " \"contextless\" "); - } - break; - case ProcCC_CDecl: - str = gb_string_appendc(str, " \"c\" "); - break; - case ProcCC_StdCall: - str = gb_string_appendc(str, " \"std\" "); - break; - case ProcCC_FastCall: - str = gb_string_appendc(str, " \"fastcall\" "); - break; - break; - 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; - // case ProcCC_ClrCall: - // str = gb_string_appendc(str, " \"clrcall\" "); - // break; + if (type->Proc.calling_convention != default_calling_convention()) { + str = gb_string_appendc(str, " \""); + str = gb_string_appendc(str, proc_calling_convention_strings[type->Proc.calling_convention]); + str = gb_string_appendc(str, "\" "); } + str = gb_string_appendc(str, "("); if (type->Proc.params) { str = write_type_to_string(str, type->Proc.params, shorthand, allow_polymorphic); |