diff options
| author | Daniel Gavin <danielgavin5@hotmail.com> | 2021-04-23 10:24:05 +0200 |
|---|---|---|
| committer | Daniel Gavin <danielgavin5@hotmail.com> | 2021-04-23 10:24:05 +0200 |
| commit | f10f7ebbf1c9833c74d09db68c0a0f5a149bde8d (patch) | |
| tree | d25d97bafc0f762e537428f99607680aa5e434b3 /src | |
| parent | 40ed7e48d0e4a1f000efbd03d19a4eebe9b8e2f6 (diff) | |
| parent | 17bbb48d8a04aaf6cc53777fe4da6ba1b7fff61b (diff) | |
Merge remote-tracking branch 'upstream/master' into prototype-fmt
Diffstat (limited to 'src')
| -rw-r--r-- | src/build_settings.cpp | 5 | ||||
| -rw-r--r-- | src/check_decl.cpp | 54 | ||||
| -rw-r--r-- | src/check_expr.cpp | 490 | ||||
| -rw-r--r-- | src/check_stmt.cpp | 20 | ||||
| -rw-r--r-- | src/check_type.cpp | 42 | ||||
| -rw-r--r-- | src/checker.cpp | 60 | ||||
| -rw-r--r-- | src/checker.hpp | 6 | ||||
| -rw-r--r-- | src/checker_builtin_procs.hpp | 29 | ||||
| -rw-r--r-- | src/docs_format.cpp | 4 | ||||
| -rw-r--r-- | src/docs_writer.cpp | 10 | ||||
| -rw-r--r-- | src/entity.cpp | 66 | ||||
| -rw-r--r-- | src/gb/gb.h | 4 | ||||
| -rw-r--r-- | src/ir.cpp | 13 | ||||
| -rw-r--r-- | src/ir_print.cpp | 10 | ||||
| -rw-r--r-- | src/llvm_backend.cpp | 466 | ||||
| -rw-r--r-- | src/llvm_backend.hpp | 3 | ||||
| -rw-r--r-- | src/llvm_backend_opt.cpp | 69 | ||||
| -rw-r--r-- | src/main.cpp | 51 | ||||
| -rw-r--r-- | src/parser.cpp | 1 | ||||
| -rw-r--r-- | src/parser.hpp | 2 | ||||
| -rw-r--r-- | src/types.cpp | 45 |
21 files changed, 1089 insertions, 361 deletions
diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 8df045a82..92ac5d284 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -104,6 +104,7 @@ enum BuildModeKind { BuildMode_DynamicLibrary, BuildMode_Object, BuildMode_Assembly, + BuildMode_LLVM_IR, }; enum CommandKind : u32 { @@ -113,7 +114,7 @@ enum CommandKind : u32 { Command_query = 1<<4, Command_doc = 1<<5, Command_version = 1<<6, - Command_test = 1<<7, + Command_test = 1<<7, Command__does_check = Command_run|Command_build|Command_check|Command_query|Command_doc|Command_test, Command__does_build = Command_run|Command_build|Command_test, @@ -838,7 +839,7 @@ void init_build_context(TargetMetrics *cross_target) { bc->link_flags = str_lit("-arch arm64 "); break; } - if (!bc->use_llvm_api) { + if ((bc->command_kind & Command__does_build) != 0 && !bc->use_llvm_api) { gb_printf_err("The arm64 architecture is only supported with -llvm-api\n");; gb_exit(1); } diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 51c0b6ee5..85c58fdf9 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -343,17 +343,31 @@ void override_entity_in_scope(Entity *original_entity, Entity *new_entity) { return; } - // IMPORTANT TODO(bill) - // Date: 2018-09-29 - // This assert fails on `using import` if the name of the alias is the same. What should be the expected behaviour? - // Namespace collision or override? Overridding is the current behaviour + // IMPORTANT NOTE(bill, 2021-04-10): Overriding behaviour was flawed in that the + // original entity was still used check checked, but the checking was only + // relying on "constant" data such as the Entity.type and Entity.Constant.value // - // using import "foo" - // bar :: foo.bar; - - // GB_ASSERT_MSG(found_entity == original_entity, "%.*s == %.*s", LIT(found_entity->token.string), LIT(new_entity->token.string)); + // Therefore two things can be done: the type can be assigned to state that it + // has been "evaluated" and the variant data can be copied across string_map_set(&found_scope->elements, original_name, new_entity); + + original_entity->type = new_entity->type; + + 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 = 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 + isize offset = cast(u8 *)&original_entity->Dummy.start - cast(u8 *)original_entity; + isize size = gb_size_of(*original_entity) - offset; + gb_memmove(cast(u8 *)original_entity, cast(u8 *)new_entity, size); } @@ -374,6 +388,7 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init, Operand operand = {}; + Entity *other_entity = nullptr; if (init != nullptr) { Entity *entity = nullptr; if (init->kind == Ast_Ident) { @@ -412,7 +427,6 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init, GB_ASSERT(operand.proc_group->kind == Entity_ProcGroup); // NOTE(bill, 2020-06-10): It is better to just clone the contents than overriding the entity in the scope // Thank goodness I made entities a tagged union to allow for this implace patching - // override_entity_in_scope(e, operand.proc_group); e->kind = Entity_ProcGroup; e->ProcGroup.entities = array_clone(heap_allocator(), operand.proc_group->ProcGroup.entities); return; @@ -454,7 +468,6 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init, error(decl->attributes[0], "Constant alias declarations cannot have attributes"); } } - return; } } @@ -694,6 +707,18 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { e->flags |= EntityFlag_Cold; } + e->Procedure.optimization_mode = cast(ProcedureOptimizationMode)ac.optimization_mode; + + + switch (e->Procedure.optimization_mode) { + case ProcedureOptimizationMode_None: + case ProcedureOptimizationMode_Minimal: + if (pl->inlining == ProcInlining_inline) { + error(e->token, "#force_inline cannot be used in conjunction with the attribute 'optimization_mode' with neither \"none\" nor \"minimal\""); + } + break; + } + e->Procedure.is_export = ac.is_export; e->deprecated_message = ac.deprecated_message; ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); @@ -718,11 +743,10 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { error(e->token, "Procedure type of 'main' was expected to be 'proc()', got %s", str); gb_string_free(str); } - if (pt->calling_convention != ProcCC_Odin && - pt->calling_convention != ProcCC_Contextless) { + if (pt->calling_convention != ProcCC_Odin) { error(e->token, "Procedure 'main' cannot have a custom calling convention"); } - pt->calling_convention = ProcCC_Contextless; + pt->calling_convention = ProcCC_Odin; if (e->pkg->kind == Package_Init) { if (ctx->info->entry_point != nullptr) { error(e->token, "Redeclaration of the entry pointer procedure 'main'"); @@ -846,7 +870,7 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } } -void check_global_variable_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init_expr) { +void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr, Ast *init_expr) { GB_ASSERT(e->type == nullptr); GB_ASSERT(e->kind == Entity_Variable); @@ -946,7 +970,7 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, check_init_variable(ctx, e, &o, str_lit("variable declaration")); } -void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d) { +void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d) { GB_ASSERT(pg_entity->kind == Entity_ProcGroup); auto *pge = &pg_entity->ProcGroup; String proc_group_name = pg_entity->token.string; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 2114746a3..9818b5015 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -79,15 +79,12 @@ void check_expr_with_type_hint (CheckerContext *c, Operand *o, Ast *e, Type * check_type (CheckerContext *c, Ast *expression); Type * check_type_expr (CheckerContext *c, Ast *expression, Type *named_type); Type * make_optional_ok_type (Type *value, bool typed=true); -void check_type_decl (CheckerContext *c, Entity *e, Ast *type_expr, Type *def); Entity * check_selector (CheckerContext *c, Operand *operand, Ast *node, Type *type_hint); Entity * check_ident (CheckerContext *c, Operand *o, Ast *n, Type *named_type, Type *type_hint, bool allow_import_name); Entity * find_polymorphic_record_entity (CheckerContext *c, Type *original_type, isize param_count, Array<Operand> const &ordered_operands, bool *failure); void check_not_tuple (CheckerContext *c, Operand *operand); void convert_to_typed (CheckerContext *c, Operand *operand, Type *target_type); gbString expr_to_string (Ast *expression); -void check_entity_decl (CheckerContext *c, Entity *e, DeclInfo *decl, Type *named_type); -void check_const_decl (CheckerContext *c, Entity *e, Ast *type_expr, Ast *init_expr, Type *named_type); void check_proc_body (CheckerContext *c, Token token, DeclInfo *decl, Type *type, Ast *body); void update_expr_type (CheckerContext *c, Ast *e, Type *type, bool final); bool check_is_terminating (Ast *node, String const &label); @@ -654,13 +651,27 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type } Ast *expr = unparen_expr(operand->expr); - if (expr != nullptr && expr->kind == Ast_AutoCast) { - Operand x = *operand; - x.expr = expr->AutoCast.expr; - bool ok = check_cast_internal(c, &x, type); - if (ok) { - return MAXIMUM_TYPE_DISTANCE; - } + if (expr != nullptr) { + if (expr->kind == Ast_AutoCast) { + Operand x = *operand; + x.expr = expr->AutoCast.expr; + bool ok = check_cast_internal(c, &x, type); + if (ok) { + return MAXIMUM_TYPE_DISTANCE; + } + } /*else if (expr->kind == Ast_CallExpr) { + // NOTE(bill, 2021-04-19): Allow assignment of procedure calls with #optional_ok + ast_node(ce, CallExpr, expr); + Type *pt = base_type(type_of_expr(ce->proc)); + if (pt->kind == Type_Proc && pt->Proc.optional_ok) { + Operand x = *operand; + x.type = pt->Proc.results->Tuple.variables[0]->type; + i64 res = check_distance_between_types(c, &x, type); + if (res >= 0) { + return res+1; + } + } + }*/ } return -1; @@ -774,6 +785,8 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co LIT(context_name)); operand->mode = Addressing_Invalid; } + + return; } @@ -1714,12 +1727,14 @@ void check_cast_error_suggestion(CheckerContext *c, Operand *o, Type *type) { } -void check_is_expressible(CheckerContext *c, Operand *o, Type *type) { +void check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) { GB_ASSERT(o->mode == Addressing_Constant); - if (!is_type_constant_type(type) || !check_representable_as_constant(c, o->value, type, &o->value)) { + if (!is_type_constant_type(type) || !check_representable_as_constant(ctx, o->value, type, &o->value)) { gbString a = expr_to_string(o->expr); gbString b = type_to_string(type); + gbString c = type_to_string(o->type); defer( + gb_string_free(c); gb_string_free(b); gb_string_free(a); o->mode = Addressing_Invalid; @@ -1729,12 +1744,12 @@ void check_is_expressible(CheckerContext *c, Operand *o, Type *type) { if (!is_type_integer(o->type) && is_type_integer(type)) { error(o->expr, "'%s' truncated to '%s'", a, b); } else { - error(o->expr, "Cannot convert '%s' to '%s'", a, b); - check_assignment_error_suggestion(c, o, type); + error(o->expr, "Cannot convert '%s' to '%s' from '%s", a, b, c); + check_assignment_error_suggestion(ctx, o, type); } } else { - error(o->expr, "Cannot convert '%s' to '%s'", a, b); - check_assignment_error_suggestion(c, o, type); + error(o->expr, "Cannot convert '%s' to '%s' from '%s", a, b, c); + check_assignment_error_suggestion(ctx, o, type); } } } @@ -2224,6 +2239,26 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) { return true; } + // if (is_type_tuple(src)) { + // Ast *expr = unparen_expr(operand->expr); + // if (expr && expr->kind == Ast_CallExpr) { + // // NOTE(bill, 2021-04-19): Allow casting procedure calls with #optional_ok + // ast_node(ce, CallExpr, expr); + // Type *pt = base_type(type_of_expr(ce->proc)); + // if (pt->kind == Type_Proc && pt->Proc.optional_ok) { + // if (pt->Proc.result_count > 0) { + // Operand op = *operand; + // op.type = pt->Proc.results->Tuple.variables[0]->type; + // bool ok = check_is_castable_to(c, &op, y); + // if (ok) { + // ce->optional_ok_one = true; + // } + // return ok; + // } + // } + // } + // } + if (is_constant && is_type_untyped(src) && is_type_string(src)) { if (is_type_u8_array(dst)) { String s = operand->value.value_string; @@ -2339,6 +2374,7 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) { if (is_type_rawptr(src) && is_type_proc(dst)) { return true; } + return false; } @@ -2728,31 +2764,27 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint ExactValue a = x->value; ExactValue b = y->value; - // Type *type = base_type(x->type); - Type *type = x->type; - if (is_type_pointer(type)) { - GB_ASSERT(op.kind == Token_Sub); - i64 bytes = a.value_pointer - b.value_pointer; - i64 diff = bytes/type_size_of(type); - x->value = exact_value_pointer(diff); - return; - } - - if (!is_type_constant_type(type)) { + if (!is_type_constant_type(x->type)) { + #if 0 gbString xt = type_to_string(x->type); gbString err_str = expr_to_string(node); error(op, "Invalid type, '%s', for constant binary expression '%s'", xt, err_str); gb_string_free(err_str); gb_string_free(xt); x->mode = Addressing_Invalid; + #else + // NOTE(bill, 2021-04-21): The above is literally a useless error message. + // Why did I add it in the first place?! + x->mode = Addressing_Value; + #endif return; } - if (op.kind == Token_Quo && is_type_integer(type)) { + if (op.kind == Token_Quo && is_type_integer(x->type)) { op.kind = Token_QuoEq; // NOTE(bill): Hack to get division of integers } - if (is_type_bit_set(type)) { + if (is_type_bit_set(x->type)) { switch (op.kind) { case Token_Add: op.kind = Token_Or; break; case Token_Sub: op.kind = Token_AndNot; break; @@ -2761,11 +2793,11 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint x->value = exact_binary_operator_value(op.kind, a, b); - if (is_type_typed(type)) { + if (is_type_typed(x->type)) { if (node != nullptr) { x->expr = node; } - check_is_expressible(c, x, type); + check_is_expressible(c, x, x->type); } return; } else if (is_type_string(x->type)) { @@ -2797,8 +2829,14 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint void update_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) { + GB_ASSERT(e != nullptr); ExprInfo *found = check_get_expr_info(&c->checker->info, e); if (found == nullptr) { + if (type != nullptr && type != t_invalid) { + if (e->tav.type == nullptr || e->tav.type == t_invalid) { + add_type_and_value(&c->checker->info, e, e->tav.mode, type ? type : e->tav.type, e->tav.value); + } + } return; } ExprInfo old = *found; @@ -2865,6 +2903,7 @@ void update_expr_value(CheckerContext *c, Ast *e, ExactValue value) { void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_type) { gbString expr_str = expr_to_string(operand->expr); gbString type_str = type_to_string(target_type); + gbString from_type_str = type_to_string(operand->type); char const *extra_text = ""; if (operand->mode == Addressing_Constant) { @@ -2875,8 +2914,9 @@ void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_typ } } } - error(operand->expr, "Cannot convert '%s' to '%s'%s", expr_str, type_str, extra_text); + error(operand->expr, "Cannot convert '%s' to '%s' from '%s'%s", expr_str, type_str, from_type_str, extra_text); + gb_string_free(from_type_str); gb_string_free(type_str); gb_string_free(expr_str); operand->mode = Addressing_Invalid; @@ -5717,8 +5757,131 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 operand->mode = Addressing_NoValue; break; + case BuiltinProc_trap: + case BuiltinProc_debug_trap: + if (!build_context.use_llvm_api) { + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); + } + operand->mode = Addressing_NoValue; + break; + + case BuiltinProc_read_cycle_counter: + if (!build_context.use_llvm_api) { + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); + } + operand->mode = Addressing_Value; + operand->type = t_i64; + break; + + case BuiltinProc_count_ones: + case BuiltinProc_trailing_zeros: + case BuiltinProc_reverse_bits: + if (!build_context.use_llvm_api) { + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); + // continue anyway + } + { + Operand x = {}; + check_expr(c, &x, ce->args[0]); + if (x.mode == Addressing_Invalid) { + return false; + } + + if (!is_type_integer_like(x.type)) { + gbString xts = type_to_string(x.type); + error(x.expr, "Values passed to '%.*s' must be an integer-like type (integer, boolean, enum, bit_set), got %s", LIT(builtin_procs[id].name), xts); + gb_string_free(xts); + } else if (x.type == t_llvm_bool) { + gbString xts = type_to_string(x.type); + error(x.expr, "Invalid type passed to '%.*s', got %s", LIT(builtin_procs[id].name), xts); + gb_string_free(xts); + } + + operand->mode = Addressing_Value; + operand->type = default_type(x.type); + } + break; + + case BuiltinProc_byte_swap: + if (!build_context.use_llvm_api) { + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); + // continue anyway + } + { + Operand x = {}; + check_expr(c, &x, ce->args[0]); + if (x.mode == Addressing_Invalid) { + return false; + } + + if (!is_type_integer_like(x.type) && !is_type_float(x.type)) { + gbString xts = type_to_string(x.type); + error(x.expr, "Values passed to '%.*s' must be an integer-like type (integer, boolean, enum, bit_set) or float, got %s", LIT(builtin_procs[id].name), xts); + gb_string_free(xts); + } else if (x.type == t_llvm_bool) { + gbString xts = type_to_string(x.type); + error(x.expr, "Invalid type passed to '%.*s', got %s", LIT(builtin_procs[id].name), xts); + gb_string_free(xts); + } + i64 sz = type_size_of(x.type); + if (sz < 2) { + gbString xts = type_to_string(x.type); + error(x.expr, "Type passed to '%.*s' must be at least 2 bytes, got %s with size of %lld", LIT(builtin_procs[id].name), xts, sz); + gb_string_free(xts); + } + operand->mode = Addressing_Value; + operand->type = default_type(x.type); + } + break; + + case BuiltinProc_overflow_add: + case BuiltinProc_overflow_sub: + case BuiltinProc_overflow_mul: + if (!build_context.use_llvm_api) { + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); + // continue anyway + } + { + Operand x = {}; + Operand y = {}; + check_expr(c, &x, ce->args[0]); + check_expr(c, &y, ce->args[1]); + if (x.mode == Addressing_Invalid) { + return false; + } + if (y.mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, &y, x.type); + convert_to_typed(c, &x, y.type); + if (is_type_untyped(x.type)) { + gbString xts = type_to_string(x.type); + error(x.expr, "Expected a typed integer for '%.*s', got %s", LIT(builtin_procs[id].name), xts); + gb_string_free(xts); + return false; + } + if (!is_type_integer(x.type)) { + gbString xts = type_to_string(x.type); + error(x.expr, "Expected an integer for '%.*s', got %s", LIT(builtin_procs[id].name), xts); + gb_string_free(xts); + return false; + } + Type *ct = core_type(x.type); + if (is_type_different_to_arch_endianness(ct)) { + GB_ASSERT(ct->kind == Type_Basic); + if (ct->Basic.flags & (BasicFlag_EndianLittle|BasicFlag_EndianBig)) { + gbString xts = type_to_string(x.type); + error(x.expr, "Expected an integer which does not specify the explicit endianness for '%.*s', got %s", LIT(builtin_procs[id].name), xts); + gb_string_free(xts); + return false; + } + } + operand->mode = Addressing_Value; + operand->type = make_optional_ok_type(default_type(x.type), false); // Just reusing this procedure, it's not optional + } + break; case BuiltinProc_atomic_fence: case BuiltinProc_atomic_fence_acq: @@ -5859,8 +6022,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_fixed_point_div_sat: { if (!build_context.use_llvm_api) { - error(ce->args[0], "%.*s is not supported on this backend", LIT(builtin_procs[id].name)); - return false; + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); + // continue anyway } Operand x = {}; @@ -5885,7 +6048,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 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, %s vs %s", LIT(builtin_procs[id].name), xts, yts); + error(x.expr, "Mismatched types for '%.*s', %s vs %s", LIT(builtin_procs[id].name), xts, yts); gb_string_free(yts); gb_string_free(xts); return false; @@ -5893,7 +6056,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (!is_type_integer(x.type) || is_type_untyped(x.type)) { gbString xts = type_to_string(x.type); - error(x.expr, "Expected an integer type for %.*s, got %s", LIT(builtin_procs[id].name), xts); + error(x.expr, "Expected an integer type for '%.*s', got %s", LIT(builtin_procs[id].name), xts); gb_string_free(xts); return false; } @@ -5903,17 +6066,17 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 return false; } if (z.mode != Addressing_Constant || !is_type_integer(z.type)) { - error(z.expr, "Expected a constant integer for the scale in %.*s", LIT(builtin_procs[id].name)); + error(z.expr, "Expected a constant integer for the scale in '%.*s'", LIT(builtin_procs[id].name)); return false; } i64 n = exact_value_to_i64(z.value); if (n <= 0) { - error(z.expr, "Scale parameter in %.*s must be positive, got %lld", LIT(builtin_procs[id].name), n); + error(z.expr, "Scale parameter in '%.*s' must be positive, got %lld", LIT(builtin_procs[id].name), n); return false; } i64 sz = 8*type_size_of(x.type); if (n > sz) { - error(z.expr, "Scale parameter in %.*s is larger than the base integer bit width, got %lld, expected a maximum of %lld", LIT(builtin_procs[id].name), n, sz); + error(z.expr, "Scale parameter in '%.*s' is larger than the base integer bit width, got %lld, expected a maximum of %lld", LIT(builtin_procs[id].name), n, sz); return false; } @@ -5923,6 +6086,59 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; + case BuiltinProc_expect: + if (!build_context.use_llvm_api) { + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); + // continue anyway + } + { + Operand x = {}; + Operand y = {}; + check_expr(c, &x, ce->args[0]); + check_expr(c, &y, ce->args[1]); + if (x.mode == Addressing_Invalid) { + return false; + } + if (y.mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, &y, x.type); + convert_to_typed(c, &x, y.type); + 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', %s vs %s", LIT(builtin_procs[id].name), xts, yts); + gb_string_free(yts); + gb_string_free(xts); + *operand = x; // minimize error propagation + return true; + } + + if (!is_type_integer_like(x.type)) { + gbString xts = type_to_string(x.type); + error(x.expr, "Values passed to '%.*s' must be an integer-like type (integer, boolean, enum, bit_set), got %s", LIT(builtin_procs[id].name), xts); + gb_string_free(xts); + *operand = x; + return true; + } + + if (y.mode != Addressing_Constant) { + error(y.expr, "Second argument to '%.*s' must be constant as it is the expected value", LIT(builtin_procs[id].name)); + } + + if (x.mode == Addressing_Constant) { + // NOTE(bill): just completely ignore this intrinsic entirely + *operand = x; + return true; + } + + operand->mode = Addressing_Value; + operand->type = x.type; + } + break; + + + case BuiltinProc_type_base_type: if (operand->mode != Addressing_Type) { @@ -6462,17 +6678,38 @@ bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> const &lhs, if (o.type == nullptr || o.type->kind != Type_Tuple) { if (lhs.count == 2 && rhs.count == 1 && (o.mode == Addressing_MapIndex || o.mode == Addressing_OptionalOk)) { - Type *tuple = make_optional_ok_type(o.type); - add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value); + bool do_normal = true; + Ast *expr = unparen_expr(o.expr); - Operand val = o; - Operand ok = o; - val.mode = Addressing_Value; - ok.mode = Addressing_Value; - ok.type = t_untyped_bool; - array_add(operands, val); - array_add(operands, ok); + Operand val0 = o; + Operand val1 = o; + val0.mode = Addressing_Value; + val1.mode = Addressing_Value; + val1.type = t_untyped_bool; + + if (expr->kind == Ast_CallExpr) { + Type *pt = base_type(type_of_expr(expr->CallExpr.proc)); + if (is_type_proc(pt)) { + do_normal = false; + Type *tuple = pt->Proc.results; + add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value); + + if (pt->Proc.result_count >= 2) { + Type *t1 = tuple->Tuple.variables[1]->type; + val1.type = t1; + } + expr->CallExpr.optional_ok_one = false; + } + } + + if (do_normal) { + Type *tuple = make_optional_ok_type(o.type); + add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value); + } + + array_add(operands, val0); + array_add(operands, val1); optional_ok = true; tuple_index += 2; } else if (o.mode == Addressing_OptionalOk && is_type_tuple(o.type)) { @@ -6493,27 +6730,12 @@ bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> const &lhs, } } else { TypeTuple *tuple = &o.type->Tuple; - if (o.mode == Addressing_OptionalOk && is_type_tuple(o.type) && lhs.count == 1) { - GB_ASSERT(tuple->variables.count == 2); - Ast *expr = unparen_expr(o.expr); - if (expr->kind == Ast_CallExpr) { - expr->CallExpr.optional_ok_one = true; - } - Operand val = o; - val.type = tuple->variables[0]->type; - val.mode = Addressing_Value; - array_add(operands, val); - tuple_index += tuple->variables.count; - - add_type_and_value(c->info, val.expr, val.mode, val.type, val.value); - } else { - for_array(j, tuple->variables) { - o.type = tuple->variables[j]->type; - array_add(operands, o); - } - - tuple_index += tuple->variables.count; + for_array(j, tuple->variables) { + o.type = tuple->variables[j]->type; + array_add(operands, o); } + + tuple_index += tuple->variables.count; } } @@ -6570,18 +6792,38 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, if (o.type == nullptr || o.type->kind != Type_Tuple) { if (allow_ok && lhs_count == 2 && rhs.count == 1 && (o.mode == Addressing_MapIndex || o.mode == Addressing_OptionalOk)) { - Type *tuple = make_optional_ok_type(o.type); - add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value); + bool do_normal = true; + Ast *expr = unparen_expr(o.expr); - Operand val = o; - Operand ok = o; - val.mode = Addressing_Value; - ok.mode = Addressing_Value; - // ok.type = t_bool; - ok.type = t_untyped_bool; - array_add(operands, val); - array_add(operands, ok); + Operand val0 = o; + Operand val1 = o; + val0.mode = Addressing_Value; + val1.mode = Addressing_Value; + val1.type = t_untyped_bool; + + if (expr->kind == Ast_CallExpr) { + Type *pt = base_type(type_of_expr(expr->CallExpr.proc)); + if (is_type_proc(pt)) { + do_normal = false; + Type *tuple = pt->Proc.results; + add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value); + + if (pt->Proc.result_count >= 2) { + Type *t1 = tuple->Tuple.variables[1]->type; + val1.type = t1; + } + expr->CallExpr.optional_ok_one = false; + } + } + + if (do_normal) { + Type *tuple = make_optional_ok_type(o.type); + add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value); + } + + array_add(operands, val0); + array_add(operands, val1); optional_ok = true; tuple_index += add_dependencies_from_unpacking(c, lhs, lhs_count, tuple_index, 2); } else { @@ -6590,30 +6832,13 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, } } else { TypeTuple *tuple = &o.type->Tuple; - if (o.mode == Addressing_OptionalOk && lhs_count == 1) { - GB_ASSERT(tuple->variables.count == 2); - Ast *expr = unparen_expr(o.expr); - if (expr->kind == Ast_CallExpr) { - expr->CallExpr.optional_ok_one = true; - } - Operand val = o; - val.type = tuple->variables[0]->type; - val.mode = Addressing_Value; - array_add(operands, val); - - isize count = tuple->variables.count; - tuple_index += add_dependencies_from_unpacking(c, lhs, lhs_count, tuple_index, count); - - add_type_and_value(c->info, val.expr, val.mode, val.type, val.value); - } else { - for_array(j, tuple->variables) { - o.type = tuple->variables[j]->type; - array_add(operands, o); - } - - isize count = tuple->variables.count; - tuple_index += add_dependencies_from_unpacking(c, lhs, lhs_count, tuple_index, count); + for_array(j, tuple->variables) { + o.type = tuple->variables[j]->type; + array_add(operands, o); } + + isize count = tuple->variables.count; + tuple_index += add_dependencies_from_unpacking(c, lhs, lhs_count, tuple_index, count); } } @@ -6863,6 +7088,7 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { data->score = score; data->result_type = final_proc_type->Proc.results; data->gen_entity = gen_entity; + add_type_and_value(c->info, ce->proc, Addressing_Value, final_proc_type, {}); } return err; @@ -7080,6 +7306,7 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) { data->score = score; data->result_type = pt->results; data->gen_entity = gen_entity; + add_type_and_value(c->info, ce->proc, Addressing_Value, proc_type, {}); } return err; @@ -7333,7 +7560,6 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type lhs = populate_proc_parameter_list(c, e->type, &lhs_count, &is_variadic); check_unpack_arguments(c, lhs, lhs_count, &operands, args, false, is_variadic); - CallArgumentData data = {}; CallArgumentError err = call_checker(c, call, e->type, e, operands, CallArgumentMode_ShowErrors, &data); if (err != CallArgumentError_None) { @@ -7341,7 +7567,9 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type } Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; add_entity_use(c, ident, entity_to_use); - + if (entity_to_use != nullptr) { + update_expr_type(c, operand->expr, entity_to_use->type, true); + } return data; } @@ -7613,6 +7841,9 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data); Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; add_entity_use(c, ident, entity_to_use); + if (entity_to_use != nullptr) { + update_expr_type(c, operand->expr, entity_to_use->type, true); + } if (data.gen_entity != nullptr) { Entity *e = data.gen_entity; @@ -7628,7 +7859,6 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); decl->where_clauses_evaluated = true; } - return data; } } else { @@ -7644,6 +7874,9 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data); Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e; add_entity_use(c, ident, entity_to_use); + if (entity_to_use != nullptr) { + update_expr_type(c, operand->expr, entity_to_use->type, true); + } if (data.gen_entity != nullptr) { Entity *e = data.gen_entity; @@ -7659,10 +7892,10 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); decl->where_clauses_evaluated = true; } - return data; } + CallArgumentData data = {}; data.result_type = t_invalid; return data; @@ -8147,6 +8380,14 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr } Type *pt = base_type(proc_type); + if (pt == t_invalid) { + if (operand->expr != nullptr && operand->expr->kind == Ast_CallExpr) { + pt = type_of_expr(operand->expr->CallExpr.proc); + } + if (pt == t_invalid && data.gen_entity) { + pt = data.gen_entity->type; + } + } if (pt->kind == Type_Proc && pt->Proc.calling_convention == ProcCC_Odin) { if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) { @@ -8175,7 +8416,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr } switch (inlining) { - case ProcInlining_inline: { + case ProcInlining_inline: if (proc != nullptr) { Entity *e = entity_from_expr(proc); if (e != nullptr && e->kind == Entity_Procedure) { @@ -8189,16 +8430,31 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr } } break; - } - case ProcInlining_no_inline: break; } operand->expr = call; - if (pt->kind == Type_Proc && pt->Proc.optional_ok) { - operand->mode = Addressing_OptionalOk; + { + if (proc_type == t_invalid) { + // gb_printf_err("%s\n", expr_to_string(operand->expr)); + } + Type *type = nullptr; + if (operand->expr != nullptr && operand->expr->kind == Ast_CallExpr) { + type = type_of_expr(operand->expr->CallExpr.proc); + } + if (type == nullptr) { + type = pt; + } + type = base_type(type); + if (type->kind == Type_Proc && type->Proc.optional_ok) { + operand->mode = Addressing_OptionalOk; + operand->type = type->Proc.results->Tuple.variables[0]->type; + if (operand->expr != nullptr && operand->expr->kind == Ast_CallExpr) { + operand->expr->CallExpr.optional_ok_one = true; + } + } } // add_type_and_value(c->info, operand->expr, operand->mode, operand->type, operand->value); @@ -9179,9 +9435,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type if (!is_constant) { error(node, "Expected all constant elements for a simd vector"); } - if (t->SimdVector.is_x86_mmx) { - error(node, "Compound literals are not allowed with intrinsics.x86_mmx"); - } } @@ -9822,7 +10075,16 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type return kind; } if (type_hint) { + Type *type = type_of_expr(ac->expr); check_cast(c, o, type_hint); + if (is_type_typed(type) && are_types_identical(type, type_hint)) { + if (build_context.vet) { + error(node, "Redundant 'auto_cast' applied to expression"); + } else { + warning(node, "Redundant 'auto_cast' applied to expression"); + } + } + } o->expr = node; return Expr_Expr; @@ -10563,7 +10825,7 @@ void check_not_tuple(CheckerContext *c, Operand *o) { if (o->type->kind == Type_Tuple) { isize count = o->type->Tuple.variables.count; error(o->expr, - "%td-valued tuple found where single value expected", count); + "%td-valued expression found where single value expected", count); o->mode = Addressing_Invalid; GB_ASSERT(count != 1); } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 9d02d003d..7d9eefe19 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -10,7 +10,19 @@ bool is_diverging_stmt(Ast *stmt) { String name = expr->CallExpr.proc->BasicDirective.name; return name == "panic"; } - Type *t = type_of_expr(expr->CallExpr.proc); + Ast *proc = unparen_expr(expr->CallExpr.proc); + TypeAndValue tv = proc->tav; + if (tv.mode == Addressing_Builtin) { + Entity *e = entity_of_node(proc); + BuiltinProcId id = BuiltinProc_Invalid; + if (e != nullptr) { + id = cast(BuiltinProcId)e->Builtin.id; + } else { + id = BuiltinProc_DIRECTIVE; + } + return builtin_procs[id].diverging; + } + Type *t = tv.type; t = base_type(t); return t != nullptr && t->kind == Type_Proc && t->Proc.diverging; } @@ -1751,7 +1763,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { Type *cond_type = t->Tuple.variables[count-1]->type; if (!is_type_boolean(cond_type)) { gbString s = type_to_string(cond_type); - error(operand.expr, "The final type of %td-valued tuple must be a boolean, got %s", count, s); + error(operand.expr, "The final type of %td-valued expression must be a boolean, got %s", count, s); gb_string_free(s); break; } @@ -1762,14 +1774,14 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { if (rs->vals.count > 1 && rs->vals[1] != nullptr && count < 3) { gbString s = type_to_string(t); - error(operand.expr, "Expected a 3-value tuple on the rhs, got (%s)", s); + error(operand.expr, "Expected a 3-valued expression on the rhs, got (%s)", s); gb_string_free(s); break; } if (rs->vals.count > 0 && rs->vals[0] != nullptr && count < 2) { gbString s = type_to_string(t); - error(operand.expr, "Expected at least a 2-values tuple on the rhs, got (%s)", s); + error(operand.expr, "Expected at least a 2-valued expression on the rhs, got (%s)", s); gb_string_free(s); break; } diff --git a/src/check_type.cpp b/src/check_type.cpp index 39fea75db..e3aac161c 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2101,8 +2101,15 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall } break; } - case Type_Pointer: break; - case Type_Proc: break; // NOTE(bill): Just a pointer + case Type_Pointer: + if (is_type_struct(bt->Pointer.elem)) { + // Force to a raw pointer + new_type = t_rawptr; + } + break; + case Type_Proc: + new_type = t_rawptr; + break; // NOTE(bill): Just a pointer // Odin specific case Type_Slice: @@ -2194,6 +2201,10 @@ Type *type_to_abi_compat_result_type(gbAllocator a, Type *original_type, ProcCal return new_type; } + if (is_type_pointer(single_type)) { + // NOTE(bill): Force a cast to prevent a possible type cycle + return t_rawptr; + } if (build_context.ODIN_OS == "windows") { if (build_context.ODIN_ARCH == "amd64") { @@ -2450,6 +2461,24 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, } } } + if (pt->tags & ProcTag_optional_second) { + if (optional_ok) { + error(proc_type_node, "A procedure type cannot have both an #optional_ok tag and #optional_second"); + } + optional_ok = true; + if (result_count != 2) { + error(proc_type_node, "A procedure type with the #optional_second tag requires 2 return values, got %td", result_count); + } else { + bool ok = false; + if (proc_type_node->file && proc_type_node->file->pkg) { + ok = proc_type_node->file->pkg->scope == ctx->info->runtime_package->scope; + } + + if (!ok) { + error(proc_type_node, "A procedure type with the #optional_second may only be allowed within 'package runtime'"); + } + } + } type->Proc.node = proc_type_node; type->Proc.scope = c->scope; @@ -2590,12 +2619,11 @@ i64 check_array_count(CheckerContext *ctx, Operand *o, Ast *e) { } Type *make_optional_ok_type(Type *value, bool typed) { - // LEAK TODO(bill): probably don't reallocate everything here and reuse the same one for the same type if possible - gbAllocator a = heap_allocator(); + gbAllocator a = permanent_allocator(); Type *t = alloc_type_tuple(); - array_init(&t->Tuple.variables, a, 0, 2); - array_add (&t->Tuple.variables, alloc_entity_field(nullptr, blank_token, value, false, 0)); - array_add (&t->Tuple.variables, alloc_entity_field(nullptr, blank_token, typed ? t_bool : t_untyped_bool, false, 1)); + array_init(&t->Tuple.variables, a, 2); + t->Tuple.variables[0] = alloc_entity_field(nullptr, blank_token, value, false, 0); + t->Tuple.variables[1] = alloc_entity_field(nullptr, blank_token, typed ? t_bool : t_untyped_bool, false, 1); return t; } diff --git a/src/checker.cpp b/src/checker.cpp index e0b303369..f386d6da7 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -783,15 +783,6 @@ void init_universal(void) { } } - // TODO(bill): Set the correct arch for this - if (bc->metrics.arch == TargetArch_amd64 || bc->metrics.arch == TargetArch_386) { - t_vector_x86_mmx = alloc_type(Type_SimdVector); - t_vector_x86_mmx->SimdVector.is_x86_mmx = true; - - Entity *entity = alloc_entity(Entity_TypeName, nullptr, make_token_ident(str_lit("x86_mmx")), t_vector_x86_mmx); - add_global_entity(entity, intrinsics_pkg->scope); - } - bool defined_values_double_declaration = false; for_array(i, bc->defined_values.entries) { char const *name = cast(char const *)cast(uintptr)bc->defined_values.entries[i].key.key; @@ -1782,20 +1773,28 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) { str_lit("memory_equal"), str_lit("memory_compare"), str_lit("memory_compare_zero"), - - str_lit("bswap_16"), - str_lit("bswap_32"), - str_lit("bswap_64"), - str_lit("bswap_128"), - - str_lit("bswap_f16"), - str_lit("bswap_f32"), - str_lit("bswap_f64"), }; for (isize i = 0; i < gb_count_of(required_runtime_entities); i++) { force_add_dependency_entity(c, c->info.runtime_package->scope, required_runtime_entities[i]); } + if (!build_context.use_llvm_api) { + String other_required_runtime_entities[] = { + str_lit("bswap_16"), + str_lit("bswap_32"), + str_lit("bswap_64"), + str_lit("bswap_128"), + + str_lit("bswap_f16"), + str_lit("bswap_f32"), + str_lit("bswap_f64"), + }; + + for (isize i = 0; i < gb_count_of(other_required_runtime_entities); i++) { + force_add_dependency_entity(c, c->info.runtime_package->scope, other_required_runtime_entities[i]); + } + } + if (build_context.no_crt) { String required_no_crt_entities[] = { // NOTE(bill): Only if these exist @@ -2190,8 +2189,6 @@ Type *check_poly_path_pop(CheckerContext *c) { -void check_entity_decl(CheckerContext *c, Entity *e, DeclInfo *d, Type *named_type); - Array<Entity *> proc_group_entities(CheckerContext *c, Operand o) { Array<Entity *> procs = {}; if (o.mode == Addressing_ProcGroup) { @@ -2574,6 +2571,29 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) { } } return true; + } else if (name == "optimization_mode") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_String) { + String mode = ev.value_string; + if (mode == "none") { + ac->optimization_mode = ProcedureOptimizationMode_None; + } else if (mode == "minimal") { + ac->optimization_mode = ProcedureOptimizationMode_Minimal; + } else if (mode == "size") { + ac->optimization_mode = ProcedureOptimizationMode_Size; + } else if (mode == "speed") { + ac->optimization_mode = ProcedureOptimizationMode_Speed; + } else { + error(elem, "Invalid optimization_mode for '%.*s'. Valid modes:", LIT(name)); + error_line("\tnone\n"); + error_line("\tminimal\n"); + error_line("\tsize\n"); + error_line("\tspeed\n"); + } + } else { + error(elem, "Expected a string for '%.*s'", LIT(name)); + } + return true; } return false; } diff --git a/src/checker.hpp b/src/checker.hpp index b3e0b60ec..4ff72717d 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -59,6 +59,7 @@ struct BuiltinProc { bool variadic; ExprKind kind; BuiltinProcPkg pkg; + bool diverging; }; @@ -112,6 +113,7 @@ struct AttributeContext { String thread_local_model; String deprecated_message; DeferredProcedure deferred_procedure; + u32 optimization_mode; // ProcedureOptimizationMode struct TypeAtomOpTable *atom_op_table; }; @@ -396,6 +398,10 @@ void check_add_import_decl(CheckerContext *c, Ast *decl); void check_add_foreign_import_decl(CheckerContext *c, Ast *decl); +void check_entity_decl(CheckerContext *c, Entity *e, DeclInfo *d, Type *named_type); +void check_const_decl(CheckerContext *c, Entity *e, Ast *type_expr, Ast *init_expr, Type *named_type); +void check_type_decl(CheckerContext *c, Entity *e, Ast *type_expr, Type *def); + bool check_arity_match(CheckerContext *c, AstValueDecl *vd, bool is_global = false); void check_collect_entities(CheckerContext *c, Slice<Ast *> const &nodes); void check_collect_entities_from_when_stmt(CheckerContext *c, AstWhenStmt *ws); diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 7c0e0746f..b9794da8a 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -41,6 +41,18 @@ enum BuiltinProcId { BuiltinProc_alloca, BuiltinProc_cpu_relax, + BuiltinProc_trap, + BuiltinProc_debug_trap, + BuiltinProc_read_cycle_counter, + + BuiltinProc_count_ones, + BuiltinProc_trailing_zeros, + BuiltinProc_reverse_bits, + BuiltinProc_byte_swap, + + BuiltinProc_overflow_add, + BuiltinProc_overflow_sub, + BuiltinProc_overflow_mul, BuiltinProc_volatile_store, BuiltinProc_volatile_load, @@ -122,6 +134,7 @@ enum BuiltinProcId { BuiltinProc_fixed_point_mul_sat, BuiltinProc_fixed_point_div_sat, + BuiltinProc_expect, // Constant type tests @@ -246,6 +259,19 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("alloca"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("cpu_relax"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("trap"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics, /*diverging*/true}, + {STR_LIT("debug_trap"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics, /*diverging*/false}, + {STR_LIT("read_cycle_counter"), 0, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("count_ones"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("trailing_zeros"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("reverse_bits"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("byte_swap"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("overflow_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("overflow_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("overflow_mul"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("volatile_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("volatile_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, @@ -327,6 +353,9 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("fixed_point_mul_sat"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("fixed_point_div_sat"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("expect"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("type_base_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_core_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/docs_format.cpp b/src/docs_format.cpp index c3aaebf64..e620da51f 100644 --- a/src/docs_format.cpp +++ b/src/docs_format.cpp @@ -114,10 +114,6 @@ enum OdinDocTypeFlag_BitSet : u32 { OdinDocTypeFlag_BitSet_UnderlyingType = 1<<4, }; -enum OdinDocTypeFlag_SimdVector : u32 { - OdinDocTypeFlag_BitSet_x86_mmx = 1<<1, -}; - enum { // constants OdinDocType_ElemsCap = 4, diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp index 5c9bb9f63..f4fd02376 100644 --- a/src/docs_writer.cpp +++ b/src/docs_writer.cpp @@ -724,13 +724,9 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) { break; case Type_SimdVector: doc_type.kind = OdinDocType_SimdVector; - if (type->SimdVector.is_x86_mmx) { - doc_type.flags |= OdinDocTypeFlag_BitSet_x86_mmx; - } else { - doc_type.elem_count_len = 1; - doc_type.elem_counts[0] = type->SimdVector.count; - doc_type.types = odin_doc_type_as_slice(w, type->SimdVector.elem); - } + doc_type.elem_count_len = 1; + doc_type.elem_counts[0] = type->SimdVector.count; + doc_type.types = odin_doc_type_as_slice(w, type->SimdVector.elem); // TODO(bill): break; case Type_RelativePointer: diff --git a/src/entity.cpp b/src/entity.cpp index 3926678fd..d1f4c78e6 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -33,39 +33,41 @@ String const entity_strings[] = { }; enum EntityFlag : u64 { - EntityFlag_Visited = 1<<0, - EntityFlag_Used = 1<<1, - EntityFlag_Using = 1<<2, - EntityFlag_Field = 1<<3, - EntityFlag_Param = 1<<4, - EntityFlag_Result = 1<<5, - EntityFlag_ArrayElem = 1<<6, - EntityFlag_Ellipsis = 1<<7, - EntityFlag_NoAlias = 1<<8, - EntityFlag_TypeField = 1<<9, - EntityFlag_Value = 1<<10, - EntityFlag_Sret = 1<<11, - EntityFlag_ByVal = 1<<12, - EntityFlag_BitFieldValue = 1<<13, - EntityFlag_PolyConst = 1<<14, - EntityFlag_NotExported = 1<<15, - EntityFlag_ConstInput = 1<<16, + EntityFlag_Visited = 1ull<<0, + EntityFlag_Used = 1ull<<1, + EntityFlag_Using = 1ull<<2, + EntityFlag_Field = 1ull<<3, + EntityFlag_Param = 1ull<<4, + EntityFlag_Result = 1ull<<5, + EntityFlag_ArrayElem = 1ull<<6, + EntityFlag_Ellipsis = 1ull<<7, + EntityFlag_NoAlias = 1ull<<8, + EntityFlag_TypeField = 1ull<<9, + EntityFlag_Value = 1ull<<10, + EntityFlag_Sret = 1ull<<11, + EntityFlag_ByVal = 1ull<<12, + EntityFlag_BitFieldValue = 1ull<<13, + EntityFlag_PolyConst = 1ull<<14, + EntityFlag_NotExported = 1ull<<15, + EntityFlag_ConstInput = 1ull<<16, - EntityFlag_Static = 1<<17, + EntityFlag_Static = 1ull<<17, - EntityFlag_ImplicitReference = 1<<18, // NOTE(bill): equivalent to `const &` in C++ + EntityFlag_ImplicitReference = 1ull<<18, // NOTE(bill): equivalent to `const &` in C++ - EntityFlag_SoaPtrField = 1<<19, // to allow s.x[0] where `s.x` is a pointer rather than a slice + EntityFlag_SoaPtrField = 1ull<<19, // to allow s.x[0] where `s.x` is a pointer rather than a slice - EntityFlag_ProcBodyChecked = 1<<20, + EntityFlag_ProcBodyChecked = 1ull<<20, - EntityFlag_CVarArg = 1<<21, - EntityFlag_AutoCast = 1<<22, + EntityFlag_CVarArg = 1ull<<21, + EntityFlag_AutoCast = 1ull<<22, - EntityFlag_Disabled = 1<<24, - EntityFlag_Cold = 1<<25, // procedure is rarely called + EntityFlag_Disabled = 1ull<<24, + EntityFlag_Cold = 1ull<<25, // procedure is rarely called - EntityFlag_Test = 1<<30, + EntityFlag_Test = 1ull<<30, + + EntityFlag_Overridden = 1ull<<63, }; @@ -97,6 +99,14 @@ enum EntityConstantFlags : u32 { EntityConstantFlag_ImplicitEnumValue = 1<<0, }; +enum ProcedureOptimizationMode : u32 { + ProcedureOptimizationMode_Default, + ProcedureOptimizationMode_None, + ProcedureOptimizationMode_Minimal, + ProcedureOptimizationMode_Size, + ProcedureOptimizationMode_Speed, +}; + // An Entity is a named "thing" in the language struct Entity { EntityKind kind; @@ -126,6 +136,9 @@ struct Entity { // later entity kinds union { struct { + u8 start; + } Dummy; + struct { ExactValue value; ParameterValue param_value; u32 flags; @@ -160,6 +173,7 @@ struct Entity { DeferredProcedure deferred_procedure; bool is_foreign; bool is_export; + ProcedureOptimizationMode optimization_mode; } Procedure; struct { Array<Entity *> entities; diff --git a/src/gb/gb.h b/src/gb/gb.h index b56c20f1e..60e7b8e64 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -491,8 +491,8 @@ typedef i32 b32; // NOTE(bill): Prefer this!!! #define USIZE_MIX U32_MIN #define USIZE_MAX U32_MAX - #define ISIZE_MIX S32_MIN - #define ISIZE_MAX S32_MAX + #define ISIZE_MIX I32_MIN + #define ISIZE_MAX I32_MAX #elif defined(GB_ARCH_64_BIT) #define USIZE_MIX U64_MIN #define USIZE_MAX U64_MAX diff --git a/src/ir.cpp b/src/ir.cpp index 0ad48ca27..1c81f08ed 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -8257,7 +8257,8 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { case_end; case_ast_node(ac, AutoCast, expr); - return ir_build_expr(proc, ac->expr); + irValue *value = ir_build_expr(proc, ac->expr); + return ir_emit_conv(proc, value, tv.type); case_end; case_ast_node(ue, UnaryExpr, expr); @@ -12564,13 +12565,9 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info case Type_SimdVector: ir_emit_comment(proc, str_lit("Type_SimdVector")); tag = ir_emit_conv(proc, variant_ptr, t_type_info_simd_vector_ptr); - if (t->SimdVector.is_x86_mmx) { - ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 3), v_true); - } else { - ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), ir_get_type_info_ptr(proc, t->SimdVector.elem)); - ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), ir_const_int(type_size_of(t->SimdVector.elem))); - ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), ir_const_int(t->SimdVector.count)); - } + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), ir_get_type_info_ptr(proc, t->SimdVector.elem)); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), ir_const_int(type_size_of(t->SimdVector.elem))); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), ir_const_int(t->SimdVector.count)); break; case Type_RelativePointer: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index d0c014a27..54ce5dca1 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -624,13 +624,9 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) { } case Type_SimdVector: - if (t->SimdVector.is_x86_mmx) { - ir_write_str_lit(f, "x86_mmx"); - } else { - ir_fprintf(f, "<%lld x ", t->SimdVector.count);; - ir_print_type(f, m, t->SimdVector.elem); - ir_write_byte(f, '>'); - } + ir_fprintf(f, "<%lld x ", t->SimdVector.count);; + ir_print_type(f, m, t->SimdVector.elem); + ir_write_byte(f, '>'); return; case Type_RelativePointer: diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 62d5a58a6..d3bc3d0f8 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -391,11 +391,37 @@ void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) { lb_insert_dynamic_map_key_and_value(p, addr, addr.map.type, addr.map.key, value, p->curr_stmt); return; } else if (addr.kind == lbAddr_Context) { - lbValue old = lb_addr_load(p, lb_find_or_generate_context_ptr(p)); - lbAddr next_addr = lb_add_local_generated(p, t_context, true); - lb_addr_store(p, next_addr, old); - lb_push_context_onto_stack(p, next_addr); - lbValue next = lb_addr_get_ptr(p, next_addr); + lbAddr old_addr = lb_find_or_generate_context_ptr(p); + + + // IMPORTANT NOTE(bill, 2021-04-22): reuse unused 'context' variables to minimize stack usage + // This has to be done manually since the optimizer cannot determine when this is possible + bool create_new = true; + for_array(i, p->context_stack) { + lbContextData *ctx_data = &p->context_stack[i]; + if (ctx_data->ctx.addr.value == old_addr.addr.value) { + if (ctx_data->uses > 0) { + create_new = true; + } else if (p->scope_index > ctx_data->scope_index) { + create_new = true; + } else { + // gb_printf_err("%.*s (curr:%td) (ctx:%td) (uses:%td)\n", LIT(p->name), p->scope_index, ctx_data->scope_index, ctx_data->uses); + create_new = false; + } + break; + } + } + + lbValue next = {}; + if (create_new) { + lbValue old = lb_addr_load(p, old_addr); + lbAddr next_addr = lb_add_local_generated(p, t_context, true); + lb_addr_store(p, next_addr, old); + lb_push_context_onto_stack(p, next_addr); + next = next_addr.addr; + } else { + next = old_addr.addr; + } if (addr.ctx.sel.index.count > 0) { lbValue lhs = lb_emit_deep_field_gep(p, next, addr.ctx.sel); @@ -623,6 +649,13 @@ lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) { } } else if (addr.kind == lbAddr_Context) { lbValue a = addr.addr; + for_array(i, p->context_stack) { + lbContextData *ctx_data = &p->context_stack[i]; + if (ctx_data->ctx.addr.value == a.value) { + ctx_data->uses += 1; + break; + } + } a.value = LLVMBuildPointerCast(p->builder, a.value, lb_type(p->module, t_context_ptr), ""); if (addr.ctx.sel.index.count > 0) { @@ -1445,9 +1478,6 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { } case Type_SimdVector: - if (type->SimdVector.is_x86_mmx) { - return LLVMX86MMXTypeInContext(ctx); - } return LLVMVectorType(lb_type(m, type->SimdVector.elem), cast(unsigned)type->SimdVector.count); case Type_RelativePointer: @@ -1899,9 +1929,6 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { break; case Type_SimdVector: - if (type->SimdVector.is_x86_mmx) { - return LLVMDIBuilderCreateVectorType(m->debug_builder, 2, 8*cast(unsigned)type_align_of(type), lb_debug_type(m, t_f64), nullptr, 0); - } return LLVMDIBuilderCreateVectorType(m->debug_builder, cast(unsigned)type->SimdVector.count, 8*cast(unsigned)type_align_of(type), lb_debug_type(m, type->SimdVector.elem), nullptr, 0); case Type_RelativePointer: { @@ -2523,7 +2550,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) { p->type = entity->type; p->type_expr = decl->type_expr; p->body = pl->body; - p->inlining = ProcInlining_none; + p->inlining = pl->inlining; p->is_foreign = entity->Procedure.is_foreign; p->is_export = entity->Procedure.is_export; p->is_entry_point = false; @@ -2558,9 +2585,6 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) { LLVMSetFunctionCallConv(p->value, cc_kind); } - if (entity->flags & EntityFlag_Cold) { - lb_add_attribute_to_proc(m, p->value, "cold"); - } if (pt->Proc.diverging) { lb_add_attribute_to_proc(m, p->value, "noreturn"); @@ -2575,6 +2599,28 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) { break; } + if (entity->flags & EntityFlag_Cold) { + lb_add_attribute_to_proc(m, p->value, "cold"); + } + + switch (entity->Procedure.optimization_mode) { + case ProcedureOptimizationMode_None: + lb_add_attribute_to_proc(m, p->value, "optnone"); + break; + case ProcedureOptimizationMode_Minimal: + lb_add_attribute_to_proc(m, p->value, "optnone"); + break; + case ProcedureOptimizationMode_Size: + lb_add_attribute_to_proc(m, p->value, "optsize"); + break; + case ProcedureOptimizationMode_Speed: + // TODO(bill): handle this correctly + lb_add_attribute_to_proc(m, p->value, "optsize"); + break; + } + + + // lbCallingConventionKind cc_kind = lbCallingConvention_C; // // TODO(bill): Clean up this logic // if (build_context.metrics.os != TargetOs_js) { @@ -2624,26 +2670,16 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) { for (isize i = 0; i < pt->Proc.param_count; i++) { Entity *e = params->variables[i]; Type *original_type = e->type; - Type *abi_type = pt->Proc.abi_compat_params[i]; if (e->kind != Entity_Variable) continue; if (i+1 == params->variables.count && pt->Proc.c_vararg) { continue; } - if (is_type_tuple(abi_type)) { - for_array(j, abi_type->Tuple.variables) { - Type *tft = abi_type->Tuple.variables[j]->type; - if (e->flags&EntityFlag_NoAlias) { - lb_add_proc_attribute_at_index(p, offset+parameter_index+j, "noalias"); - } - } - parameter_index += abi_type->Tuple.variables.count; - } else { - if (e->flags&EntityFlag_NoAlias) { - lb_add_proc_attribute_at_index(p, offset+parameter_index, "noalias"); - } - parameter_index += 1; + + if (e->flags&EntityFlag_NoAlias) { + lb_add_proc_attribute_at_index(p, offset+parameter_index, "noalias"); } + parameter_index += 1; } } @@ -7567,6 +7603,7 @@ lbContextData *lb_push_context_onto_stack_from_implicit_parameter(lbProcedure *p lbContextData *cd = array_add_and_get(&p->context_stack); cd->ctx = ctx_addr; cd->scope_index = -1; + cd->uses = +1; // make sure it has been used already return cd; } @@ -9031,6 +9068,145 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, } return {}; + + case BuiltinProc_debug_trap: + case BuiltinProc_trap: + { + char const *name = nullptr; + switch (id) { + case BuiltinProc_debug_trap: name = "llvm.debugtrap"; break; + case BuiltinProc_trap: name = "llvm.trap"; break; + } + + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s", name); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0); + + LLVMBuildCall(p->builder, ip, nullptr, 0, ""); + if (id == BuiltinProc_trap) { + LLVMBuildUnreachable(p->builder); + } + return {}; + } + + case BuiltinProc_read_cycle_counter: + { + char const *name = "llvm.readcyclecounter"; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s", name); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0); + + lbValue res = {}; + res.value = LLVMBuildCall(p->builder, ip, nullptr, 0, ""); + res.type = tv.type; + return res; + } + + case BuiltinProc_trailing_zeros: + { + lbValue x = lb_build_expr(p, ce->args[0]); + x = lb_emit_conv(p, x, tv.type); + + char const *name = "llvm.cttz"; + LLVMTypeRef types[1] = {lb_type(p->module, tv.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[2] = {}; + args[0] = x.value; + args[1] = LLVMConstNull(LLVMInt1TypeInContext(p->module->ctx)); + + lbValue res = {}; + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + res.type = tv.type; + return res; + } + + case BuiltinProc_count_ones: + case BuiltinProc_reverse_bits: + { + lbValue x = lb_build_expr(p, ce->args[0]); + x = lb_emit_conv(p, x, tv.type); + + char const *name = nullptr; + switch (id) { + case BuiltinProc_count_ones: name = "llvm.ctpop"; break; + case BuiltinProc_reverse_bits: name = "llvm.bitreverse"; break; + } + LLVMTypeRef types[1] = {lb_type(p->module, tv.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 = tv.type; + return res; + } + + case BuiltinProc_byte_swap: + { + lbValue x = lb_build_expr(p, ce->args[0]); + x = lb_emit_conv(p, x, tv.type); + return lb_emit_byte_swap(p, x, tv.type); + } + + case BuiltinProc_overflow_add: + 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; + + lbValue x = lb_build_expr(p, ce->args[0]); + lbValue y = lb_build_expr(p, ce->args[1]); + x = lb_emit_conv(p, x, type); + y = lb_emit_conv(p, y, type); + + char const *name = nullptr; + if (is_type_unsigned(type)) { + switch (id) { + case BuiltinProc_overflow_add: name = "llvm.uadd.with.overflow"; break; + case BuiltinProc_overflow_sub: name = "llvm.usub.with.overflow"; break; + case BuiltinProc_overflow_mul: name = "llvm.umul.with.overflow"; break; + } + } else { + switch (id) { + case BuiltinProc_overflow_add: name = "llvm.sadd.with.overflow"; break; + case BuiltinProc_overflow_sub: name = "llvm.ssub.with.overflow"; break; + case BuiltinProc_overflow_mul: name = "llvm.smul.with.overflow"; break; + } + } + 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[2] = {}; + args[0] = x.value; + args[1] = y.value; + + Type *res_type = nullptr; + { + gbAllocator a = permanent_allocator(); + res_type = alloc_type_tuple(); + array_init(&res_type->Tuple.variables, a, 2); + res_type->Tuple.variables[0] = alloc_entity_field(nullptr, blank_token, type, false, 0); + res_type->Tuple.variables[1] = alloc_entity_field(nullptr, blank_token, t_llvm_bool, false, 1); + } + + lbValue res = {}; + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + res.type = res_type; + return res; + } + + case BuiltinProc_atomic_fence: LLVMBuildFence(p->builder, LLVMAtomicOrderingSequentiallyConsistent, false, ""); return {}; @@ -9304,6 +9480,30 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, res.type = platform_type; return lb_emit_conv(p, res, tv.type); } + + case BuiltinProc_expect: + { + Type *t = default_type(tv.type); + lbValue x = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t); + lbValue y = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t); + + char const *name = "llvm.expect"; + + LLVMTypeRef types[1] = {lb_type(p->module, t)}; + 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)); + + lbValue res = {}; + + LLVMValueRef args[2] = {}; + args[0] = x.value; + args[1] = y.value; + + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + res.type = t; + return lb_emit_conv(p, res, t); + } } GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name)); @@ -9692,6 +9892,7 @@ bool lb_is_const_nil(lbValue value) { String lb_get_const_string(lbModule *m, lbValue value) { GB_ASSERT(lb_is_const(value)); + GB_ASSERT(LLVMIsConstant(value.value)); Type *t = base_type(value.type); GB_ASSERT(are_types_identical(t, t_string)); @@ -9743,43 +9944,43 @@ LLVMValueRef lb_lookup_runtime_procedure(lbModule *m, String const &name) { return found->value; } -lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *platform_type) { - Type *vt = core_type(value.type); - GB_ASSERT(type_size_of(vt) == type_size_of(platform_type)); - - // TODO(bill): lb_emit_byte_swap - lbValue res = {}; - res.type = platform_type; - res.value = value.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)); - int sz = cast(int)type_size_of(vt); - if (sz > 1) { - if (is_type_float(platform_type)) { - String name = {}; - switch (sz) { - case 2: name = str_lit("bswap_f16"); break; - case 4: name = str_lit("bswap_f32"); break; - case 8: name = str_lit("bswap_f64"); break; - default: GB_PANIC("unhandled byteswap size"); break; - } - LLVMValueRef fn = lb_lookup_runtime_procedure(p->module, name); - res.value = LLVMBuildCall(p->builder, fn, &value.value, 1, ""); - } else { - GB_ASSERT(is_type_integer(platform_type)); - String name = {}; - switch (sz) { - case 2: name = str_lit("bswap_16"); break; - case 4: name = str_lit("bswap_32"); break; - case 8: name = str_lit("bswap_64"); break; - case 16: name = str_lit("bswap_128"); break; - default: GB_PANIC("unhandled byteswap size"); break; - } - LLVMValueRef fn = lb_lookup_runtime_procedure(p->module, name); + if (type_size_of(value.type) < 2) { + return value; + } - res.value = LLVMBuildCall(p->builder, fn, &value.value, 1, ""); + Type *original_type = value.type; + if (is_type_float(original_type)) { + i64 sz = type_size_of(original_type); + Type *integer_type = nullptr; + switch (sz) { + case 2: integer_type = t_u16; break; + case 4: integer_type = t_u32; break; + case 8: integer_type = t_u64; break; } + GB_ASSERT(integer_type != nullptr); + value = lb_emit_transmute(p, value, integer_type); } + char const *name = "llvm.bswap"; + LLVMTypeRef types[1] = {lb_type(p->module, value.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] = value.value; + + lbValue res = {}; + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + res.type = value.type; + + if (is_type_float(original_type)) { + res = lb_emit_transmute(p, res, original_type); + } + res.type = end_type; return res; } @@ -10963,7 +11164,8 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { case_end; case_ast_node(ac, AutoCast, expr); - return lb_build_expr(p, ac->expr); + lbValue value = lb_build_expr(p, ac->expr); + return lb_emit_conv(p, value, tv.type); case_end; case_ast_node(ue, UnaryExpr, expr); @@ -13355,15 +13557,11 @@ 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_simd_vector_ptr); - LLVMValueRef vals[4] = {}; + LLVMValueRef vals[3] = {}; - if (t->SimdVector.is_x86_mmx) { - vals[3] = lb_const_bool(m, t_bool, true).value; - } else { - vals[0] = lb_get_type_info_ptr(m, t->SimdVector.elem).value; - vals[1] = lb_const_int(m, t_int, type_size_of(t->SimdVector.elem)).value; - vals[2] = lb_const_int(m, t_int, t->SimdVector.count).value; - } + vals[0] = lb_get_type_info_ptr(m, t->SimdVector.elem).value; + vals[1] = lb_const_int(m, t_int, type_size_of(t->SimdVector.elem)).value; + vals[2] = lb_const_int(m, t_int, t->SimdVector.count).value; lbValue res = {}; res.type = type_deref(tag.type); @@ -13844,11 +14042,28 @@ void lb_generate_code(lbGenerator *gen) { LLVMPassRegistryRef pass_registry = LLVMGetGlobalPassRegistry(); LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(mod); + LLVMPassManagerRef function_pass_manager_minimal = LLVMCreateFunctionPassManagerForModule(mod); + LLVMPassManagerRef function_pass_manager_size = LLVMCreateFunctionPassManagerForModule(mod); + LLVMPassManagerRef function_pass_manager_speed = LLVMCreateFunctionPassManagerForModule(mod); 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); @@ -13988,6 +14203,47 @@ void lb_generate_code(lbGenerator *gen) { }*/ } + 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 (!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); + } + LLVMVerifyFunction(p->value, LLVMPrintMessageAction); + gb_exit(1); + } + } + + if (!(build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main)) { TIME_SECTION("LLVM DLL main"); @@ -14075,7 +14331,7 @@ void lb_generate_code(lbGenerator *gen) { } else { lbValue *found = map_get(&m->values, hash_entity(entry_point)); GB_ASSERT(found != nullptr); - LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, found->type)), found->value, nullptr, 0, ""); + lb_emit_call(p, *found, {}); } LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 0, false)); @@ -14093,47 +14349,6 @@ void lb_generate_code(lbGenerator *gen) { } - 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 (!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); - } - LLVMVerifyFunction(p->value, LLVMPrintMessageAction); - gb_exit(1); - } - } - - if (m->debug_builder != nullptr) { TIME_SECTION("LLVM Debug Info Complete Types"); lb_debug_complete_types(m); @@ -14158,7 +14373,25 @@ void lb_generate_code(lbGenerator *gen) { if (p->flags & lbProcedureFlag_WithoutMemcpyPass) { LLVMRunFunctionPassManager(default_function_pass_manager_without_memcpy, p->value); } else { - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); + 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); + } } } } @@ -14220,13 +14453,18 @@ void lb_generate_code(lbGenerator *gen) { return; } llvm_error = nullptr; - if (build_context.keep_temp_files) { + 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"); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 8117271c1..004d6b905 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -157,6 +157,7 @@ struct lbBranchBlocks { struct lbContextData { lbAddr ctx; isize scope_index; + isize uses; }; enum lbParamPasskind { @@ -310,7 +311,7 @@ lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection sel); lbValue lb_emit_deep_field_ev(lbProcedure *p, lbValue e, Selection sel); lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type); -lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *platform_type); +lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *end_type); void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block); lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t); lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right); diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index 5e1154af2..3b268dffa 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -1,11 +1,41 @@ +void lb_populate_function_pass_manager(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_basic_populate_function_pass_manager(LLVMPassManagerRef fpm) { + LLVMAddPromoteMemoryToRegisterPass(fpm); + LLVMAddMergedLoadStoreMotionPass(fpm); + LLVMAddConstantPropagationPass(fpm); + LLVMAddEarlyCSEPass(fpm); + + LLVMAddConstantPropagationPass(fpm); + LLVMAddMergedLoadStoreMotionPass(fpm); + LLVMAddPromoteMemoryToRegisterPass(fpm); + LLVMAddCFGSimplificationPass(fpm); +} + void lb_populate_function_pass_manager(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); - if (!ignore_memcpy_pass) { + if (ignore_memcpy_pass) { + lb_basic_populate_function_pass_manager(fpm); + return; + } else if (optimization_level == 0) { LLVMAddMemCpyOptPass(fpm); + lb_basic_populate_function_pass_manager(fpm); + return; } + +#if 0 + LLVMPassManagerBuilderRef pmb = LLVMPassManagerBuilderCreate(); + LLVMPassManagerBuilderSetOptLevel(pmb, optimization_level); + LLVMPassManagerBuilderSetSizeLevel(pmb, optimization_level); + LLVMPassManagerBuilderPopulateFunctionPassManager(pmb, fpm); +#else + LLVMAddMemCpyOptPass(fpm); LLVMAddPromoteMemoryToRegisterPass(fpm); LLVMAddMergedLoadStoreMotionPass(fpm); LLVMAddConstantPropagationPass(fpm); @@ -16,16 +46,45 @@ void lb_populate_function_pass_manager(LLVMPassManagerRef fpm, bool ignore_memcp LLVMAddPromoteMemoryToRegisterPass(fpm); LLVMAddCFGSimplificationPass(fpm); - // LLVMAddSLPVectorizePass(fpm); - // LLVMAddLoopVectorizePass(fpm); + LLVMAddSCCPPass(fpm); + + LLVMAddPromoteMemoryToRegisterPass(fpm); + LLVMAddUnifyFunctionExitNodesPass(fpm); + + LLVMAddCFGSimplificationPass(fpm); + LLVMAddEarlyCSEPass(fpm); + LLVMAddLowerExpectIntrinsicPass(fpm); +#endif +} + +void lb_populate_function_pass_manager_specific(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); - // LLVMAddScalarizerPass(fpm); - // LLVMAddLoopIdiomPass(fpm); if (optimization_level == 0) { + LLVMAddMemCpyOptPass(fpm); + lb_basic_populate_function_pass_manager(fpm); return; } #if 1 + LLVMPassManagerBuilderRef pmb = LLVMPassManagerBuilderCreate(); + LLVMPassManagerBuilderSetOptLevel(pmb, optimization_level); + LLVMPassManagerBuilderSetSizeLevel(pmb, optimization_level); + LLVMPassManagerBuilderPopulateFunctionPassManager(pmb, fpm); +#else + LLVMAddMemCpyOptPass(fpm); + LLVMAddPromoteMemoryToRegisterPass(fpm); + LLVMAddMergedLoadStoreMotionPass(fpm); + LLVMAddConstantPropagationPass(fpm); + LLVMAddEarlyCSEPass(fpm); + + LLVMAddConstantPropagationPass(fpm); + LLVMAddMergedLoadStoreMotionPass(fpm); + LLVMAddPromoteMemoryToRegisterPass(fpm); + LLVMAddCFGSimplificationPass(fpm); + LLVMAddSCCPPass(fpm); LLVMAddPromoteMemoryToRegisterPass(fpm); diff --git a/src/main.cpp b/src/main.cpp index 20190a187..aa498e84d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -571,6 +571,7 @@ enum BuildFlagKind { BuildFlag_OutFile, BuildFlag_OptimizationLevel, + BuildFlag_OptimizationMode, BuildFlag_ShowTimings, BuildFlag_ShowUnused, BuildFlag_ShowUnusedWithLocation, @@ -687,6 +688,8 @@ bool parse_build_flags(Array<String> args) { add_flag(&build_flags, BuildFlag_Help, str_lit("help"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String, Command__does_build &~ Command_test); add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer, Command__does_build); + add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("o"), BuildFlagParam_String, Command__does_build); + add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("O"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_ShowUnused, str_lit("show-unused"), BuildFlagParam_None, Command_check); @@ -885,8 +888,36 @@ bool parse_build_flags(Array<String> args) { } case BuildFlag_OptimizationLevel: GB_ASSERT(value.kind == ExactValue_Integer); + if (set_flags[BuildFlag_OptimizationMode]) { + gb_printf_err("Mixture of -opt and -o is not allowed\n"); + bad_flags = true; + break; + } build_context.optimization_level = cast(i32)big_int_to_i64(&value.value_integer); break; + case BuildFlag_OptimizationMode: + GB_ASSERT(value.kind == ExactValue_String); + if (set_flags[BuildFlag_OptimizationLevel]) { + gb_printf_err("Mixture of -opt and -o is not allowed\n"); + bad_flags = true; + break; + } + + if (value.value_string == "minimal") { + build_context.optimization_level = 0; + } else if (value.value_string == "size") { + build_context.optimization_level = 1; + } else if (value.value_string == "speed") { + build_context.optimization_level = 2; + } else { + gb_printf_err("Invalid optimization mode for -o:<string>, got %.*s\n", LIT(value.value_string)); + gb_printf_err("Valid optimization modes:\n"); + gb_printf_err("\tminimal\n"); + gb_printf_err("\tsize\n"); + gb_printf_err("\tspeed\n"); + bad_flags = true; + } + break; case BuildFlag_ShowTimings: GB_ASSERT(value.kind == ExactValue_Invalid); build_context.show_timings = true; @@ -1109,8 +1140,16 @@ bool parse_build_flags(Array<String> args) { build_context.build_mode = BuildMode_Executable; } else if (str == "asm" || str == "assembly" || str == "assembler") { build_context.build_mode = BuildMode_Assembly; + } else if (str == "llvm" || str == "llvm-ir") { + build_context.build_mode = BuildMode_LLVM_IR; } else { gb_printf_err("Unknown build mode '%.*s'\n", LIT(str)); + gb_printf_err("Valid build modes:\n"); + gb_printf_err("\tdll, shared\n"); + gb_printf_err("\tobj, object\n"); + gb_printf_err("\texe\n"); + gb_printf_err("\tasm, assembly, assembler\n"); + gb_printf_err("\tllvm, llvm-ir\n"); bad_flags = true; break; } @@ -1612,6 +1651,10 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(1, "-all-packages"); print_usage_line(2, "Generates documentation for all packages used in the current project"); print_usage_line(0, ""); + + print_usage_line(1, "-doc-format"); + print_usage_line(2, "Generates documentation as the .odin-doc format (useful for external tooling)"); + print_usage_line(0, ""); } if (run_or_build) { @@ -1621,10 +1664,16 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(0, ""); print_usage_line(1, "-opt:<integer>"); - print_usage_line(2, "Set the optimization level for complication"); + print_usage_line(2, "Set the optimization level for compilation"); print_usage_line(2, "Accepted values: 0, 1, 2, 3"); print_usage_line(2, "Example: -opt:2"); print_usage_line(0, ""); + + print_usage_line(1, "-o:<string>"); + print_usage_line(2, "Set the optimization mode for compilation"); + print_usage_line(2, "Accepted values: minimal, size, speed"); + print_usage_line(2, "Example: -o:speed"); + print_usage_line(0, ""); } if (check) { diff --git a/src/parser.cpp b/src/parser.cpp index 86419cd8f..18a4ba9d2 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1813,6 +1813,7 @@ void parse_proc_tags(AstFile *f, u64 *tags) { if (false) {} ELSE_IF_ADD_TAG(optional_ok) + ELSE_IF_ADD_TAG(optional_second) ELSE_IF_ADD_TAG(require_results) ELSE_IF_ADD_TAG(bounds_check) ELSE_IF_ADD_TAG(no_bounds_check) diff --git a/src/parser.hpp b/src/parser.hpp index 47e860845..1ff073f91 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -203,6 +203,7 @@ enum ProcTag { ProcTag_require_results = 1<<4, ProcTag_optional_ok = 1<<5, + ProcTag_optional_second = 1<<6, }; enum ProcCallingConvention { @@ -338,6 +339,7 @@ AST_KIND(_ExprBegin, "", bool) \ Token ellipsis; \ ProcInlining inlining; \ bool optional_ok_one; \ + i32 builtin_id; \ }) \ AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \ AST_KIND(TernaryExpr, "ternary expression", struct { Ast *cond, *x, *y; }) \ diff --git a/src/types.cpp b/src/types.cpp index 7d85aa6bb..a5c5c2eb2 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -283,7 +283,6 @@ struct TypeProc { TYPE_KIND(SimdVector, struct { \ i64 count; \ Type *elem; \ - bool is_x86_mmx; \ }) \ TYPE_KIND(RelativePointer, struct { \ Type *pointer_type; \ @@ -460,8 +459,8 @@ gb_global Type basic_types[] = { {Type_Basic, {Basic_i64, BasicFlag_Integer, 8, STR_LIT("i64")}}, {Type_Basic, {Basic_u64, BasicFlag_Integer | BasicFlag_Unsigned, 8, STR_LIT("u64")}}, - {Type_Basic, {Basic_i128, BasicFlag_Integer, 16, STR_LIT("i128")}}, - {Type_Basic, {Basic_u128, BasicFlag_Integer | BasicFlag_Unsigned, 16, STR_LIT("u128")}}, + {Type_Basic, {Basic_i128, BasicFlag_Integer, 16, STR_LIT("i128")}}, + {Type_Basic, {Basic_u128, BasicFlag_Integer | BasicFlag_Unsigned, 16, STR_LIT("u128")}}, {Type_Basic, {Basic_rune, BasicFlag_Integer | BasicFlag_Rune, 4, STR_LIT("rune")}}, @@ -679,8 +678,6 @@ gb_global Type *t_source_code_location_ptr = nullptr; gb_global Type *t_map_hash = nullptr; gb_global Type *t_map_header = nullptr; -gb_global Type *t_vector_x86_mmx = nullptr; - gb_global Type *t_equal_proc = nullptr; gb_global Type *t_hasher_proc = nullptr; @@ -1012,6 +1009,20 @@ bool is_type_integer(Type *t) { } return false; } +bool is_type_integer_like(Type *t) { + t = core_type(t); + if (t->kind == Type_Basic) { + return (t->Basic.flags & (BasicFlag_Integer|BasicFlag_Boolean)) != 0; + } + if (t->kind == Type_BitSet) { + if (t->BitSet.underlying) { + return is_type_integer_like(t->BitSet.underlying); + } + return true; + } + return false; +} + bool is_type_unsigned(Type *t) { t = base_type(t); // t = core_type(t); @@ -1468,7 +1479,7 @@ Type *integer_endian_type_to_platform_type(Type *t) { if (t->kind == Type_BitSet) { t = bit_set_to_int(t); } - GB_ASSERT(t->kind == Type_Basic); + GB_ASSERT_MSG(t->kind == Type_Basic, "%s", type_to_string(t)); switch (t->Basic.kind) { // Endian Specific Types @@ -2148,12 +2159,8 @@ bool are_types_identical(Type *x, Type *y) { case Type_SimdVector: if (y->kind == Type_SimdVector) { - if (x->SimdVector.is_x86_mmx == y->SimdVector.is_x86_mmx) { - if (x->SimdVector.is_x86_mmx) { - return true; - } else if (x->SimdVector.count == y->SimdVector.count) { - return are_types_identical(x->SimdVector.elem, y->SimdVector.elem); - } + if (x->SimdVector.count == y->SimdVector.count) { + return are_types_identical(x->SimdVector.elem, y->SimdVector.elem); } } break; @@ -2953,9 +2960,6 @@ i64 type_align_of_internal(Type *t, TypePath *path) { } case Type_SimdVector: { - if (t->SimdVector.is_x86_mmx) { - return 8; - } // align of i64 count = t->SimdVector.count; Type *elem = t->SimdVector.elem; @@ -3219,9 +3223,6 @@ i64 type_size_of_internal(Type *t, TypePath *path) { } case Type_SimdVector: { - if (t->SimdVector.is_x86_mmx) { - return 8; - } i64 count = t->SimdVector.count; Type *elem = t->SimdVector.elem; return count * type_size_of_internal(elem, path); @@ -3656,12 +3657,8 @@ gbString write_type_to_string(gbString str, Type *type) { break; case Type_SimdVector: - if (type->SimdVector.is_x86_mmx) { - return gb_string_appendc(str, "intrinsics.x86_mmx"); - } else { - str = gb_string_append_fmt(str, "#simd[%d]", cast(int)type->SimdVector.count); - str = write_type_to_string(str, type->SimdVector.elem); - } + str = gb_string_append_fmt(str, "#simd[%d]", cast(int)type->SimdVector.count); + str = write_type_to_string(str, type->SimdVector.elem); break; case Type_RelativePointer: |