From eb49b5f84a3ac222530a20261d20e5f0ded094ef Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 19 Apr 2021 11:01:00 +0100 Subject: Fix `override_entity_in_scope `behaviour to correctly to report the changes upstream better --- src/check_expr.cpp | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 2114746a3..8819ad6db 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 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); -- cgit v1.2.3 From c83d13d0cbbf204a045a379c74bffc4d5bcfb53c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 19 Apr 2021 15:12:10 +0100 Subject: Fix `update_expr_type` behaviour, along with fixing procedure groups updating the proc expr type --- src/check_expr.cpp | 58 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 10 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 8819ad6db..953b32667 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -694,6 +694,13 @@ bool check_is_assignable_to(CheckerContext *c, Operand *operand, Type *type) { return check_is_assignable_to_with_score(c, operand, type, &score); } +void add_optional_ok_for_procedure(Type *type, Operand *operand) { + type = base_type(type); + if (type->kind == Type_Proc && type->Proc.optional_ok) { + operand->mode = Addressing_OptionalOk; + } +} + // NOTE(bill): 'content_name' is for debugging and error messages void check_assignment(CheckerContext *c, Operand *operand, Type *type, String context_name) { @@ -750,6 +757,7 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co Entity *e = procs[i]; add_entity_use(c, operand->expr, e); good = true; + add_optional_ok_for_procedure(e->type, operand); break; } } @@ -771,6 +779,8 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co LIT(context_name)); operand->mode = Addressing_Invalid; } + + return; } @@ -2794,8 +2804,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; @@ -6490,7 +6506,7 @@ bool check_assignment_arguments(CheckerContext *ctx, Array const &lhs, } } else { TypeTuple *tuple = &o.type->Tuple; - if (o.mode == Addressing_OptionalOk && is_type_tuple(o.type) && lhs.count == 1) { + 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) { @@ -7330,7 +7346,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) { @@ -7338,7 +7353,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; } @@ -7610,6 +7627,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; @@ -7625,7 +7645,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 { @@ -7641,6 +7660,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; @@ -7656,10 +7678,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; @@ -8144,6 +8166,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) { @@ -8172,7 +8202,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) { @@ -8186,16 +8216,24 @@ 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; + } + add_optional_ok_for_procedure(type, operand); } // add_type_and_value(c->info, operand->expr, operand->mode, operand->type, operand->value); -- cgit v1.2.3 From 36125696240469d0adcb78caa8093f581debdcb2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 19 Apr 2021 15:46:12 +0100 Subject: Allow casting of `#optional_ok` call expressions --- src/check_expr.cpp | 23 ++++++++++++++++++++++- src/check_stmt.cpp | 6 +++--- 2 files changed, 25 insertions(+), 4 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 953b32667..bf097580b 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2231,6 +2231,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; @@ -2346,6 +2366,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; } @@ -10598,7 +10619,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..0ce0ff6a0 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1751,7 +1751,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 +1762,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; } -- cgit v1.2.3 From 0eb75886d16307091d5379d53fac9a3cca8d5a7d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 19 Apr 2021 16:02:36 +0100 Subject: Allow assignment of procedure calls with `#optional_ok` to single values --- src/check_expr.cpp | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index bf097580b..02a37c522 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -651,12 +651,26 @@ 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; + } + } } } @@ -694,7 +708,7 @@ bool check_is_assignable_to(CheckerContext *c, Operand *operand, Type *type) { return check_is_assignable_to_with_score(c, operand, type, &score); } -void add_optional_ok_for_procedure(Type *type, Operand *operand) { +void add_optional_ok_for_procedure(Type *type, Operand *operand, Type *type_hint) { type = base_type(type); if (type->kind == Type_Proc && type->Proc.optional_ok) { operand->mode = Addressing_OptionalOk; @@ -757,7 +771,7 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co Entity *e = procs[i]; add_entity_use(c, operand->expr, e); good = true; - add_optional_ok_for_procedure(e->type, operand); + add_optional_ok_for_procedure(e->type, operand, type); break; } } @@ -8254,7 +8268,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr if (type == nullptr) { type = pt; } - add_optional_ok_for_procedure(type, operand); + add_optional_ok_for_procedure(type, operand, type_hint); } // add_type_and_value(c->info, operand->expr, operand->mode, operand->type, operand->value); -- cgit v1.2.3 From f1bdd2e60f76fd02f10728b2fe11f5d58350785d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 19 Apr 2021 22:31:14 +0100 Subject: Improve `#optional_ok` logic for procedures; Add `#optional_second` for `package runtime` usage --- src/check_expr.cpp | 210 ++++++++++++++++++++++++++++------------------------- src/check_type.cpp | 18 +++++ src/parser.cpp | 1 + src/parser.hpp | 1 + 4 files changed, 131 insertions(+), 99 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 02a37c522..e18f69642 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -659,7 +659,7 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type if (ok) { return MAXIMUM_TYPE_DISTANCE; } - } else if (expr->kind == Ast_CallExpr) { + } /*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)); @@ -671,7 +671,7 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type return res+1; } } - } + }*/ } return -1; @@ -708,13 +708,6 @@ bool check_is_assignable_to(CheckerContext *c, Operand *operand, Type *type) { return check_is_assignable_to_with_score(c, operand, type, &score); } -void add_optional_ok_for_procedure(Type *type, Operand *operand, Type *type_hint) { - type = base_type(type); - if (type->kind == Type_Proc && type->Proc.optional_ok) { - operand->mode = Addressing_OptionalOk; - } -} - // NOTE(bill): 'content_name' is for debugging and error messages void check_assignment(CheckerContext *c, Operand *operand, Type *type, String context_name) { @@ -771,7 +764,6 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co Entity *e = procs[i]; add_entity_use(c, operand->expr, e); good = true; - add_optional_ok_for_procedure(e->type, operand, type); break; } } @@ -1735,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; @@ -1750,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' form '%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); } } } @@ -2245,25 +2239,25 @@ 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_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)) { @@ -2913,6 +2907,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) { @@ -2923,8 +2918,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; @@ -6510,16 +6506,35 @@ bool check_assignment_arguments(CheckerContext *ctx, Array 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); + } optional_ok = true; tuple_index += 2; @@ -6541,27 +6556,12 @@ bool check_assignment_arguments(CheckerContext *ctx, Array 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; } } @@ -6618,18 +6618,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 { @@ -6638,30 +6658,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); } } @@ -6911,6 +6914,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; @@ -7128,6 +7132,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; @@ -8268,7 +8273,14 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr if (type == nullptr) { type = pt; } - add_optional_ok_for_procedure(type, operand, type_hint); + 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); diff --git a/src/check_type.cpp b/src/check_type.cpp index 39fea75db..35b0c7644 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2450,6 +2450,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; 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..35a1c1997 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 { -- cgit v1.2.3 From d21e522208d9edcb6813d82398f64b71f01b521d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 19 Apr 2021 22:35:59 +0100 Subject: Add code that was accidentally removed --- src/check_expr.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index e18f69642..9600445d6 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6536,6 +6536,8 @@ bool check_assignment_arguments(CheckerContext *ctx, Array const &lhs, 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)) { -- cgit v1.2.3 From 201cad51a98579627a7b0ac495045a7323474e27 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 19 Apr 2021 22:41:52 +0100 Subject: Fix typo --- src/check_expr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 9600445d6..93cff8468 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1744,7 +1744,7 @@ void check_is_expressible(CheckerContext *ctx, 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' form '%s", a, b, c); + error(o->expr, "Cannot convert '%s' to '%s' from '%s", a, b, c); check_assignment_error_suggestion(ctx, o, type); } } else { -- cgit v1.2.3 From 6b45856e810835bb6a6ea408909531bce5cf40e9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 21 Apr 2021 10:52:26 +0100 Subject: Remove useless error message for constant binary expressions with non-constant operations --- src/check_expr.cpp | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 93cff8468..ffd19edd5 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2764,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; @@ -2797,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)) { -- cgit v1.2.3 From 47c7dc6a9bfc679f027984bd68523743b4d7734f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Apr 2021 10:35:17 +0100 Subject: Add new intrinsics: debug_trap, trap, read_cycle_counter, expect --- core/intrinsics/intrinsics.odin | 15 +++++++-- core/runtime/core.odin | 3 -- src/check_expr.cpp | 67 ++++++++++++++++++++++++++++++++++++++++- src/check_stmt.cpp | 14 ++++++++- src/checker.hpp | 1 + src/checker_builtin_procs.hpp | 12 ++++++++ src/llvm_backend.cpp | 58 +++++++++++++++++++++++++++++++++++ src/parser.hpp | 1 + src/types.cpp | 18 +++++++++-- 9 files changed, 179 insertions(+), 10 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index 0979241af..8b2fd31fd 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -6,6 +6,7 @@ package intrinsics x86_mmx :: x86_mmx; // Specialized SIMD Vector type +// Types simd_vector :: proc($N: int, $T: typeid) -> type/#simd[N]T soa_struct :: proc($N: int, $T: typeid) -> type/#soa[N]T @@ -13,8 +14,11 @@ soa_struct :: proc($N: int, $T: typeid) -> type/#soa[N]T volatile_load :: proc(dst: ^$T) -> T --- volatile_store :: proc(dst: ^$T, val: T) -> T --- -// Atomics +// Trapping +debug_trap :: proc() --- +trap :: proc() -> ! --- +// Atomics atomic_fence :: proc() --- atomic_fence_acq :: proc() --- atomic_fence_rel :: proc() --- @@ -89,9 +93,14 @@ atomic_cxchgweak_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*opti // Instructions -alloca :: proc(size, align: int) -> ^u8 --- +alloca :: proc(size, align: int) -> ^u8 --- +cpu_relax :: proc() --- +read_cycle_counter :: proc() -> i64 --- + + +// Compiler Hints +expect :: proc(val, expected_val: T) -> T --- -cpu_relax :: proc() --- // Constant type tests diff --git a/core/runtime/core.odin b/core/runtime/core.odin index ac5e90460..1052b74fb 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -433,9 +433,6 @@ typeid_base_without_enum :: typeid_core; @(default_calling_convention = "none") foreign { - @(link_name="llvm.assume") - assume :: proc(cond: bool) ---; - @(link_name="llvm.debugtrap") debug_trap :: proc() ---; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index ffd19edd5..31057598d 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5757,8 +5757,21 @@ 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_atomic_fence: case BuiltinProc_atomic_fence_acq: @@ -5963,6 +5976,58 @@ 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)); + return false; + } else { + 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) { diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 0ce0ff6a0..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; } diff --git a/src/checker.hpp b/src/checker.hpp index 76c528dfb..4ff72717d 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -59,6 +59,7 @@ struct BuiltinProc { bool variadic; ExprKind kind; BuiltinProcPkg pkg; + bool diverging; }; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 7c0e0746f..19a9d841e 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -41,6 +41,9 @@ enum BuiltinProcId { BuiltinProc_alloca, BuiltinProc_cpu_relax, + BuiltinProc_trap, + BuiltinProc_debug_trap, + BuiltinProc_read_cycle_counter, BuiltinProc_volatile_store, BuiltinProc_volatile_load, @@ -122,6 +125,7 @@ enum BuiltinProcId { BuiltinProc_fixed_point_mul_sat, BuiltinProc_fixed_point_div_sat, + BuiltinProc_expect, // Constant type tests @@ -246,6 +250,11 @@ 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("volatile_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("volatile_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, @@ -327,6 +336,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/llvm_backend.cpp b/src/llvm_backend.cpp index fc29be59c..48c276771 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -9049,6 +9049,40 @@ 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_atomic_fence: LLVMBuildFence(p->builder, LLVMAtomicOrderingSequentiallyConsistent, false, ""); return {}; @@ -9322,6 +9356,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)); diff --git a/src/parser.hpp b/src/parser.hpp index 35a1c1997..1ff073f91 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -339,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..9871e469c 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -460,8 +460,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")}}, @@ -1012,6 +1012,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); -- cgit v1.2.3 From 0a66f8c9a35c57714952182143984eb988f2ef0f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Apr 2021 10:39:20 +0100 Subject: Remove `intrinsics.x86_mmx` type --- core/fmt/fmt.odin | 3 --- core/intrinsics/intrinsics.odin | 4 ---- core/odin/doc-format/doc_format.odin | 5 ----- core/reflect/types.odin | 12 ++++-------- core/runtime/core.odin | 1 - core/runtime/print.odin | 12 ++++-------- src/check_expr.cpp | 3 --- src/checker.cpp | 9 --------- src/docs_format.cpp | 4 ---- src/docs_writer.cpp | 10 +++------- src/ir.cpp | 10 +++------- src/ir_print.cpp | 10 +++------- src/llvm_backend.cpp | 18 ++++-------------- src/types.cpp | 25 ++++--------------------- 14 files changed, 25 insertions(+), 101 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 4716f7954..6de6b0245 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -1638,9 +1638,6 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { } case runtime.Type_Info_Simd_Vector: - if info.is_x86_mmx { - io.write_string(fi.writer, "intrinsics.x86_mmx<>"); - } io.write_byte(fi.writer, '<'); defer io.write_byte(fi.writer, '>'); for i in 0.. type/#simd[N]T soa_struct :: proc($N: int, $T: typeid) -> type/#soa[N]T diff --git a/core/odin/doc-format/doc_format.odin b/core/odin/doc-format/doc_format.odin index 61bfc913d..8d9d0a027 100644 --- a/core/odin/doc-format/doc_format.odin +++ b/core/odin/doc-format/doc_format.odin @@ -212,11 +212,6 @@ Type_Flag_Bit_Set :: enum u32le { Underlying_Type = 4, } -Type_Flags_SimdVector :: distinct bit_set[Type_Flag_SimdVector; u32le]; -Type_Flag_SimdVector :: enum u32le { - x86_mmx = 1, -} - from_array :: proc(base: ^Header_Base, a: $A/Array($T)) -> []T { s: mem.Raw_Slice; s.data = rawptr(uintptr(base) + uintptr(a.offset)); diff --git a/core/reflect/types.odin b/core/reflect/types.odin index 34a53b10a..8c7dc1735 100644 --- a/core/reflect/types.odin +++ b/core/reflect/types.odin @@ -565,14 +565,10 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info) -> (n: int) { n += _n(io.write_byte(w, ']')); case Type_Info_Simd_Vector: - if info.is_x86_mmx { - n += write_string(w, "intrinsics.x86_mmx"); - } else { - n += write_string(w, "#simd["); - n += _n(io.write_i64(w, i64(info.count))); - n += _n(io.write_byte(w, ']')); - n += write_type(w, info.elem); - } + n += write_string(w, "#simd["); + n += _n(io.write_i64(w, i64(info.count))); + n += _n(io.write_byte(w, ']')); + n += write_type(w, info.elem); case Type_Info_Relative_Pointer: n += write_string(w, "#relative("); diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 1052b74fb..78d43b65a 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -144,7 +144,6 @@ Type_Info_Simd_Vector :: struct { elem: ^Type_Info, elem_size: int, count: int, - is_x86_mmx: bool, }; Type_Info_Relative_Pointer :: struct { pointer: ^Type_Info, diff --git a/core/runtime/print.odin b/core/runtime/print.odin index c05e6039c..e60d97103 100644 --- a/core/runtime/print.odin +++ b/core/runtime/print.odin @@ -347,14 +347,10 @@ print_type :: proc "contextless" (ti: ^Type_Info) { case Type_Info_Simd_Vector: - if info.is_x86_mmx { - print_string("intrinsics.x86_mmx"); - } else { - print_string("#simd["); - print_u64(u64(info.count)); - print_byte(']'); - print_type(info.elem); - } + print_string("#simd["); + print_u64(u64(info.count)); + print_byte(']'); + print_type(info.elem); case Type_Info_Relative_Pointer: print_string("#relative("); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 31057598d..b9ff3bf1f 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -9324,9 +9324,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"); - } } diff --git a/src/checker.cpp b/src/checker.cpp index 10376b149..eab704d9c 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; 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/ir.cpp b/src/ir.cpp index 0ad48ca27..9f08450d6 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -12564,13 +12564,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 48c276771..a67078180 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1445,9 +1445,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 +1896,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: { @@ -13432,15 +13426,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); diff --git a/src/types.cpp b/src/types.cpp index 9871e469c..f706032a5 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; \ @@ -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; @@ -2162,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; @@ -2967,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; @@ -3233,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); @@ -3670,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: -- cgit v1.2.3 From 542098dc6fbc871d44a5061bb871f93968d48e3e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Apr 2021 11:06:16 +0100 Subject: Add intrinsics: byte_swap (integers and floats); count_ones; trailing_zeros; reverse_bits --- src/check_expr.cpp | 40 +++++++++++++++++++-- src/checker_builtin_procs.hpp | 9 +++++ src/llvm_backend.cpp | 82 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 3 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b9ff3bf1f..ae4f1ec30 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5773,6 +5773,39 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 operand->type = t_i64; break; + case BuiltinProc_count_ones: + case BuiltinProc_trailing_zeros: + case BuiltinProc_reverse_bits: + 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 (id == BuiltinProc_byte_swap && (!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 (id != BuiltinProc_byte_swap && !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_atomic_fence: case BuiltinProc_atomic_fence_acq: case BuiltinProc_atomic_fence_rel: @@ -5913,7 +5946,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 { if (!build_context.use_llvm_api) { error(ce->args[0], "%.*s is not supported on this backend", LIT(builtin_procs[id].name)); - return false; + // continue anyway } Operand x = {}; @@ -5979,8 +6012,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 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)); - return false; - } else { + // continue anyway + } + { Operand x = {}; Operand y = {}; check_expr(c, &x, ce->args[0]); diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 19a9d841e..7f8fd5323 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -45,6 +45,11 @@ enum BuiltinProcId { BuiltinProc_debug_trap, BuiltinProc_read_cycle_counter, + BuiltinProc_count_ones, + BuiltinProc_trailing_zeros, + BuiltinProc_reverse_bits, + BuiltinProc_byte_swap, + BuiltinProc_volatile_store, BuiltinProc_volatile_load, @@ -254,6 +259,10 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {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("volatile_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("volatile_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index a67078180..2e77d66f2 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -9077,6 +9077,88 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, 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); + if (is_type_float(tv.type)) { + i64 sz = type_size_of(tv.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); + x = lb_emit_transmute(p, x, integer_type); + } + + char const *name = "llvm.bswap"; + LLVMTypeRef types[1] = {lb_type(p->module, x.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 = x.type; + + if (is_type_float(tv.type)) { + res = lb_emit_transmute(p, res, tv.type); + } + return res; + } + + case BuiltinProc_atomic_fence: LLVMBuildFence(p->builder, LLVMAtomicOrderingSequentiallyConsistent, false, ""); return {}; -- cgit v1.2.3 From b68b090f136d43c3501c4c9772bd4dd68399eb37 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Apr 2021 11:33:46 +0100 Subject: Add intrinsics: overflow_add, overflow_sub, overflow_mul; Change byte swap behaviour in -llvm-api to be the same as the intrinsic --- src/check_expr.cpp | 115 ++++++++++++++++++++++++++++++++------- src/check_type.cpp | 9 ++-- src/checker.cpp | 26 +++++---- src/checker_builtin_procs.hpp | 8 +++ src/llvm_backend.cpp | 123 +++++++++++++++++++++++++----------------- src/llvm_backend.hpp | 2 +- src/types.cpp | 2 +- 7 files changed, 200 insertions(+), 85 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index ae4f1ec30..a915aa92f 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5760,14 +5760,14 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 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)); + 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)); + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); } operand->mode = Addressing_Value; operand->type = t_i64; @@ -5776,9 +5776,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_count_ones: case BuiltinProc_trailing_zeros: case BuiltinProc_reverse_bits: - 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)); + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); // continue anyway } { @@ -5788,24 +5787,102 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 return false; } - if (id == BuiltinProc_byte_swap && (!is_type_integer_like(x.type) && !is_type_float(x.type))) { + 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, "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); + error(x.expr, "Invalid type passed to '%.*s', got %s", LIT(builtin_procs[id].name), xts); gb_string_free(xts); - } else if (id != BuiltinProc_byte_swap && !is_type_integer_like(x.type)) { + } + + 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), got %s", LIT(builtin_procs[id].name), xts); + 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); + 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: case BuiltinProc_atomic_fence_rel: @@ -5945,7 +6022,7 @@ 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)); + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); // continue anyway } @@ -5971,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; @@ -5979,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; } @@ -5989,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; } @@ -6011,7 +6088,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 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)); + error(ce->args[0], "'%.*s' is not supported on this backend", LIT(builtin_procs[id].name)); // continue anyway } { @@ -6030,7 +6107,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); *operand = x; // minimize error propagation @@ -6039,14 +6116,14 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 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); + 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)); + 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) { diff --git a/src/check_type.cpp b/src/check_type.cpp index 612a11593..e3aac161c 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2619,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 eab704d9c..f386d6da7 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1773,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 diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 7f8fd5323..b9794da8a 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -50,6 +50,10 @@ enum BuiltinProcId { BuiltinProc_reverse_bits, BuiltinProc_byte_swap, + BuiltinProc_overflow_add, + BuiltinProc_overflow_sub, + BuiltinProc_overflow_mul, + BuiltinProc_volatile_store, BuiltinProc_volatile_load, @@ -264,6 +268,10 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {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}, diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 2e77d66f2..a74d5bd3a 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -9127,34 +9127,57 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, { lbValue x = lb_build_expr(p, ce->args[0]); x = lb_emit_conv(p, x, tv.type); - if (is_type_float(tv.type)) { - i64 sz = type_size_of(tv.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; + 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; } - GB_ASSERT(integer_type != nullptr); - x = lb_emit_transmute(p, x, integer_type); } - - char const *name = "llvm.bswap"; - LLVMTypeRef types[1] = {lb_type(p->module, x.type)}; + LLVMTypeRef types[1] = {lb_type(p->module, type)}; unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0])); LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); - LLVMValueRef args[1] = {}; + 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 = x.type; - - if (is_type_float(tv.type)) { - res = lb_emit_transmute(p, res, tv.type); - } + res.type = res_type; return res; } @@ -9896,43 +9919,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)); +lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *end_type) { + GB_ASSERT(type_size_of(value.type) == type_size_of(end_type)); - // TODO(bill): lb_emit_byte_swap - lbValue res = {}; - res.type = platform_type; - res.value = value.value; - - 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; } diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 8117271c1..47c58fde3 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -310,7 +310,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/types.cpp b/src/types.cpp index f706032a5..a5c5c2eb2 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1479,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 -- cgit v1.2.3 From 17bbb48d8a04aaf6cc53777fe4da6ba1b7fff61b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Apr 2021 17:36:28 +0100 Subject: Warn on redundant `auto_cast`, and make an error on `-vet` --- src/check_expr.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index a915aa92f..9818b5015 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -10075,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; -- cgit v1.2.3