From 97be86710306702a672309b23fbe8d38f1e6eeec Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 5 Feb 2022 13:01:15 +0000 Subject: Rename `#partial[Enum]Type` to `#sparse[Enum]Type` for non-contiguous enum fields --- src/types.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/types.cpp') diff --git a/src/types.cpp b/src/types.cpp index 07951196a..e0d35a12c 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -221,6 +221,7 @@ struct TypeProc { ExactValue *max_value; \ i64 count; \ TokenKind op; \ + bool is_sparse; \ }) \ TYPE_KIND(Slice, struct { Type *elem; }) \ TYPE_KIND(DynamicArray, struct { Type *elem; }) \ @@ -3830,6 +3831,9 @@ gbString write_type_to_string(gbString str, Type *type) { break; case Type_EnumeratedArray: + if (type->EnumeratedArray.is_sparse) { + str = gb_string_appendc(str, "#sparse"); + } str = gb_string_append_rune(str, '['); str = write_type_to_string(str, type->EnumeratedArray.index); str = gb_string_append_rune(str, ']'); -- cgit v1.2.3 From 19aec13a1060a521913abc6bd669080171d43594 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 6 Feb 2022 11:42:59 +0000 Subject: Support rank-2 arrays (matrix-like) for `transpose` --- src/check_builtin.cpp | 38 ++++++++++++++++++++++++++++++++++++-- src/llvm_backend_expr.cpp | 21 +++++++++++++++++++++ src/types.cpp | 19 +++++++++++++++++++ 3 files changed, 76 insertions(+), 2 deletions(-) (limited to 'src/types.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index a42741976..d3a3103b1 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -2183,9 +2183,43 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } operand->mode = Addressing_Value; - if (is_type_array(t)) { + if (t->kind == Type_Array) { + i32 rank = type_math_rank(t); // Do nothing - operand->type = x.type; + operand->type = x.type; + if (rank > 2) { + gbString s = type_to_string(x.type); + error(call, "'%.*s' expects a matrix or array with a rank of 2, got %s of rank %d", LIT(builtin_name), s, rank); + gb_string_free(s); + return false; + } else if (rank == 2) { + Type *inner = base_type(t->Array.elem); + GB_ASSERT(inner->kind == Type_Array); + Type *elem = inner->Array.elem; + Type *array_inner = alloc_type_array(elem, t->Array.count); + Type *array_outer = alloc_type_array(array_inner, inner->Array.count); + operand->type = array_outer; + + i64 elements = t->Array.count*inner->Array.count; + i64 size = type_size_of(operand->type); + if (!is_type_valid_for_matrix_elems(elem)) { + gbString s = type_to_string(x.type); + error(call, "'%.*s' expects a matrix or array with a base element type of an integer, float, or complex number, got %s", LIT(builtin_name), s); + gb_string_free(s); + } else if (elements > MATRIX_ELEMENT_COUNT_MAX) { + gbString s = type_to_string(x.type); + error(call, "'%.*s' expects a matrix or array with a maximum of %d elements, got %s with %lld elements", LIT(builtin_name), MATRIX_ELEMENT_COUNT_MAX, s, elements); + gb_string_free(s); + } else if (elements > MATRIX_ELEMENT_COUNT_MAX) { + gbString s = type_to_string(x.type); + error(call, "'%.*s' expects a matrix or array with non-zero elements, got %s", LIT(builtin_name), MATRIX_ELEMENT_COUNT_MAX, s); + gb_string_free(s); + } else if (size > MATRIX_ELEMENT_MAX_SIZE) { + gbString s = type_to_string(x.type); + error(call, "Too large of a type for '%.*s', got %s of size %lld, maximum size %d", LIT(builtin_name), s, cast(long long)size, MATRIX_ELEMENT_MAX_SIZE); + gb_string_free(s); + } + } } else { GB_ASSERT(t->kind == Type_Matrix); operand->type = alloc_type_matrix(t->Matrix.elem, t->Matrix.column_count, t->Matrix.row_count); diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 715b7df78..29a86d116 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -580,6 +580,27 @@ LLVMValueRef lb_matrix_to_trimmed_vector(lbProcedure *p, lbValue m) { lbValue lb_emit_matrix_tranpose(lbProcedure *p, lbValue m, Type *type) { if (is_type_array(m.type)) { + i32 rank = type_math_rank(m.type); + if (rank == 2) { + lbAddr addr = lb_add_local_generated(p, type, false); + lbValue dst = addr.addr; + lbValue src = m; + i32 n = cast(i32)get_array_type_count(m.type); + i32 m = cast(i32)get_array_type_count(type); + // m.type == [n][m]T + // type == [m][n]T + + for (i32 j = 0; j < m; j++) { + lbValue dst_col = lb_emit_struct_ep(p, dst, j); + for (i32 i = 0; i < n; i++) { + lbValue dst_row = lb_emit_struct_ep(p, dst_col, i); + lbValue src_col = lb_emit_struct_ev(p, src, i); + lbValue src_row = lb_emit_struct_ev(p, src_col, j); + lb_emit_store(p, dst_row, src_row); + } + } + return lb_addr_load(p, addr); + } // no-op m.type = type; return m; diff --git a/src/types.cpp b/src/types.cpp index e0d35a12c..9ee6ba359 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -363,6 +363,7 @@ enum TypeInfoFlag : u32 { enum : int { MATRIX_ELEMENT_COUNT_MIN = 1, MATRIX_ELEMENT_COUNT_MAX = 16, + MATRIX_ELEMENT_MAX_SIZE = MATRIX_ELEMENT_COUNT_MAX * (2 * 8), // complex128 }; @@ -1583,6 +1584,24 @@ Type *core_array_type(Type *t) { } } +i32 type_math_rank(Type *t) { + i32 rank = 0; + for (;;) { + t = base_type(t); + switch (t->kind) { + case Type_Array: + rank += 1; + t = t->Array.elem; + break; + case Type_Matrix: + rank += 2; + t = t->Matrix.elem; + break; + default: + return rank; + } + } +} Type *base_complex_elem_type(Type *t) { -- cgit v1.2.3 From 0cc40db565a9c4b99e6fa0844b9ac512558626e4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 8 Feb 2022 17:04:55 +0000 Subject: Begin work on support objc intrinsics --- src/check_builtin.cpp | 223 +++++++++++++++++++++++++++++++++++++++++- src/check_decl.cpp | 3 + src/checker.cpp | 36 +++++++ src/checker.hpp | 15 ++- src/checker_builtin_procs.hpp | 6 ++ src/entity.cpp | 1 + src/llvm_backend.cpp | 59 ++++++++++- src/llvm_backend.hpp | 6 +- src/llvm_backend_general.cpp | 8 +- src/llvm_backend_proc.cpp | 3 + src/llvm_backend_utility.cpp | 96 ++++++++++++++++++ src/types.cpp | 10 ++ 12 files changed, 459 insertions(+), 7 deletions(-) (limited to 'src/types.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 1fb3d6037..8ada77b12 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -143,6 +143,219 @@ void check_or_return_split_types(CheckerContext *c, Operand *x, String const &na } +bool does_require_msgSend_stret(Type *return_type) { + if (return_type == nullptr) { + return false; + } + if (build_context.metrics.arch == TargetArch_i386 || build_context.metrics.arch == TargetArch_amd64) { + i64 struct_limit = type_size_of(t_uintptr) << 1; + return type_size_of(return_type) > struct_limit; + } + if (build_context.metrics.arch == TargetArch_arm64) { + return false; + } + + // if (build_context.metrics.arch == TargetArch_arm32) { + // i64 struct_limit = type_size_of(t_uintptr); + // // NOTE(bill): This is technically wrong + // return is_type_struct(return_type) && !is_type_raw_union(return_type) && type_size_of(return_type) > struct_limit; + // } + GB_PANIC("unsupported architecture"); + return false; +} + +ObjcMsgKind get_objc_proc_kind(Type *return_type) { + if (return_type == nullptr) { + return ObjcMsg_normal; + } + + if (build_context.metrics.arch == TargetArch_i386 || build_context.metrics.arch == TargetArch_amd64) { + if (is_type_float(return_type)) { + return ObjcMsg_fpret; + } + if (build_context.metrics.arch == TargetArch_amd64) { + if (is_type_complex(return_type)) { + // URL: https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/message.h#L143-L159 + return ObjcMsg_fpret; + } + } + } + if (build_context.metrics.arch != TargetArch_arm64) { + if (does_require_msgSend_stret(return_type)) { + return ObjcMsg_stret; + } + } + return ObjcMsg_normal; +} + +void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice param_types) { + ObjcMsgKind kind = get_objc_proc_kind(return_type); + + Scope *scope = create_scope(c->info, nullptr); + + // NOTE(bill, 2022-02-08): the backend's ABI handling should handle this correctly, I hope + Type *params = alloc_type_tuple(); + { + auto variables = array_make(permanent_allocator(), 0, param_types.count); + + for_array(i, param_types) { + Type *type = param_types[i]; + Entity *param = alloc_entity_param(scope, blank_token, type, false, true); + array_add(&variables, param); + } + params->Tuple.variables = slice_from_array(variables); + } + + Type *results = alloc_type_tuple(); + if (return_type) { + auto variables = array_make(permanent_allocator(), 1); + results->Tuple.variables = slice_from_array(variables); + Entity *param = alloc_entity_param(scope, blank_token, return_type, false, true); + results->Tuple.variables[0] = param; + } + + + ObjcMsgData data = {}; + data.kind = kind; + data.proc_type = alloc_type_proc(scope, params, param_types.count, results, results->Tuple.variables.count, false, ProcCC_CDecl); + + mutex_lock(&c->info->objc_types_mutex); + map_set(&c->info->objc_msgSend_types, call, data); + mutex_unlock(&c->info->objc_types_mutex); + + add_package_dependency(c, "runtime", "objc_lookUpClass"); + add_package_dependency(c, "runtime", "sel_registerName"); + + add_package_dependency(c, "runtime", "objc_msgSend"); + add_package_dependency(c, "runtime", "objc_msgSend_fpret"); + add_package_dependency(c, "runtime", "objc_msgSend_fp2ret"); + add_package_dependency(c, "runtime", "objc_msgSend_stret"); +} + +bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) { + auto const is_constant_string = [](CheckerContext *c, String const &builtin_name, Ast *expr, String *name_) -> bool { + Operand op = {}; + check_expr(c, &op, expr); + if (op.mode == Addressing_Constant && op.value.kind == ExactValue_String) { + if (name_) *name_ = op.value.value_string; + return true; + } + gbString e = expr_to_string(op.expr); + gbString t = type_to_string(op.type); + error(op.expr, "'%.*s' expected a constant string value, got %s of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + }; + String builtin_name = builtin_procs[id].name; + + if (build_context.metrics.os != TargetOs_darwin) { + error(call, "'%.*s' only works on darwin", LIT(builtin_name)); + return false; + } + + + ast_node(ce, CallExpr, call); + switch (id) { + default: + GB_PANIC("Implement objective built-in procedure: %.*s", LIT(builtin_name)); + return false; + + case BuiltinProc_objc_send: { + Type *return_type = nullptr; + + Operand rt = {}; + check_expr_or_type(c, &rt, ce->args[0]); + if (rt.mode == Addressing_Type) { + return_type = rt.type; + } else if (is_operand_nil(rt)) { + return_type = nullptr; + } else { + gbString e = expr_to_string(rt.expr); + error(rt.expr, "'%.*s' expected a type or nil to define the return type of the Objective-C call, got %s", LIT(builtin_name), e); + gb_string_free(e); + return false; + } + + operand->type = return_type; + operand->mode = return_type ? Addressing_Value : Addressing_NoValue; + + String class_name = {}; + String sel_name = {}; + + Type *sel_type = t_objc_SEL; + Operand self = {}; + check_expr_or_type(c, &self, ce->args[1]); + if (self.mode == Addressing_Type) { + if (!internal_check_is_assignable_to(self.type, t_objc_object)) { + gbString t = type_to_string(self.type); + error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got type %s", LIT(builtin_name), t); + gb_string_free(t); + return false; + } + if (!(self.type->kind == Type_Named && + self.type->Named.type_name != nullptr && + self.type->Named.type_name->TypeName.objc_class_name != "")) { + gbString t = type_to_string(self.type); + error(self.expr, "'%.*s' expected a named type with the attribute @(obj_class=) , got type %s", LIT(builtin_name), t); + gb_string_free(t); + return false; + } + + sel_type = t_objc_Class; + } else if (!is_operand_value(self) || !check_is_assignable_to(c, &self, t_objc_id)) { + gbString e = expr_to_string(self.expr); + gbString t = type_to_string(self.type); + error(self.expr, "'%.*s'3 expected a type or value derived from intrinsics.objc_object, got '%s' of type %s %d", LIT(builtin_name), e, t, self.type->kind); + gb_string_free(t); + gb_string_free(e); + return false; + } else if (!is_type_pointer(self.type)) { + gbString e = expr_to_string(self.expr); + gbString t = type_to_string(self.type); + error(self.expr, "'%.*s' expected a pointer of a value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } else { + Type *type = type_deref(self.type); + if (!(type->kind == Type_Named && + type->Named.type_name != nullptr && + type->Named.type_name->TypeName.objc_class_name != "")) { + gbString t = type_to_string(type); + error(self.expr, "'%.*s' expected a named type with the attribute @(obj_class=) , got type %s", LIT(builtin_name), t); + gb_string_free(t); + return false; + } + } + + + if (!is_constant_string(c, builtin_name, ce->args[2], &sel_name)) { + return false; + } + + isize const arg_offset = 1; + auto param_types = slice_make(permanent_allocator(), ce->args.count-arg_offset); + param_types[0] = t_objc_id; + param_types[1] = sel_type; + + for (isize i = 2+arg_offset; i < ce->args.count; i++) { + Operand x = {}; + check_expr(c, &x, ce->args[i]); + param_types[i-arg_offset] = x.type; + } + + add_objc_proc_type(c, call, return_type, param_types); + + return true; + } break; + + case BuiltinProc_objc_create: { + GB_PANIC("TODO: BuiltinProc_objc_create"); + return false; + } break; + } +} bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) { ast_node(ce, CallExpr, call); @@ -179,6 +392,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_len: case BuiltinProc_min: case BuiltinProc_max: + case BuiltinProc_objc_send: + case BuiltinProc_objc_create: // NOTE(bill): The first arg may be a Type, this will be checked case by case break; @@ -202,7 +417,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; } - String builtin_name = builtin_procs[id].name;; + String builtin_name = builtin_procs[id].name; if (ce->args.count > 0) { @@ -219,6 +434,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 GB_PANIC("Implement built-in procedure: %.*s", LIT(builtin_name)); break; + case BuiltinProc_objc_send: + case BuiltinProc_objc_create: + return check_builtin_objc_procedure(c, operand, call, id, type_hint); + case BuiltinProc___entry_point: operand->mode = Addressing_NoValue; operand->type = nullptr; @@ -3815,6 +4034,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 operand->type = t_hasher_proc; break; } + + } return true; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index f6dade812..243dbbbc6 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -338,6 +338,9 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def) if (decl != nullptr) { AttributeContext ac = {}; check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac); + if (e->kind == Entity_TypeName && ac.objc_class != "") { + e->TypeName.objc_class_name = ac.objc_class; + } } diff --git a/src/checker.cpp b/src/checker.cpp index 7fb4fdb29..2ab487592 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -841,6 +841,17 @@ void add_global_enum_constant(Slice const &fields, char const *name, i GB_PANIC("Unfound enum value for global constant: %s %lld", name, cast(long long)value); } +Type *add_global_type_name(Scope *scope, String const &type_name, Type *backing_type) { + Entity *e = alloc_entity_type_name(scope, make_token_ident(type_name), nullptr, EntityState_Resolved); + Type *named_type = alloc_type_named(type_name, backing_type, e); + e->type = named_type; + set_base_type(named_type, backing_type); + if (scope_insert(scope, e)) { + compiler_error("double declaration of %.*s", LIT(e->token.string)); + } + return named_type; +} + void init_universal(void) { BuildContext *bc = &build_context; @@ -985,6 +996,17 @@ void init_universal(void) { t_f64_ptr = alloc_type_pointer(t_f64); t_u8_slice = alloc_type_slice(t_u8); t_string_slice = alloc_type_slice(t_string); + + // intrinsics types for objective-c stuff + { + t_objc_object = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_object"), alloc_type_struct()); + t_objc_selector = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_selector"), alloc_type_struct()); + t_objc_class = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_class"), alloc_type_struct()); + + t_objc_id = alloc_type_pointer(t_objc_object); + t_objc_SEL = alloc_type_pointer(t_objc_selector); + t_objc_Class = alloc_type_pointer(t_objc_class); + } } @@ -1041,6 +1063,9 @@ void init_checker_info(CheckerInfo *i) { semaphore_init(&i->collect_semaphore); mpmc_init(&i->intrinsics_entry_point_usage, a, 1<<10); // just waste some memory here, even if it probably never used + + mutex_init(&i->objc_types_mutex); + map_init(&i->objc_msgSend_types, a); } void destroy_checker_info(CheckerInfo *i) { @@ -1073,6 +1098,9 @@ void destroy_checker_info(CheckerInfo *i) { mutex_destroy(&i->type_and_value_mutex); mutex_destroy(&i->identifier_uses_mutex); mutex_destroy(&i->foreign_mutex); + + mutex_destroy(&i->objc_types_mutex); + map_destroy(&i->objc_msgSend_types); } CheckerContext make_checker_context(Checker *c) { @@ -3161,6 +3189,14 @@ DECL_ATTRIBUTE_PROC(type_decl_attribute) { } else if (name == "private") { // NOTE(bill): Handled elsewhere `check_collect_value_decl` return true; + } else if (name == "objc_class") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind != ExactValue_String || ev.value_string == "") { + error(elem, "Expected a non-empty string value for '%.*s'", LIT(name)); + } else { + ac->objc_class = ev.value_string; + } + return true; } return false; } diff --git a/src/checker.hpp b/src/checker.hpp index 9a8753efd..b812e10a4 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -107,6 +107,7 @@ struct AttributeContext { String thread_local_model; String deprecated_message; String warning_message; + String objc_class; DeferredProcedure deferred_procedure; bool is_export : 1; bool is_static : 1; @@ -267,6 +268,17 @@ struct UntypedExprInfo { typedef PtrMap UntypedExprInfoMap; typedef MPMCQueue ProcBodyQueue; +enum ObjcMsgKind : u32 { + ObjcMsg_normal, + ObjcMsg_fpret, + ObjcMsg_fp2ret, + ObjcMsg_stret, +}; +struct ObjcMsgData { + ObjcMsgKind kind; + Type *proc_type; +}; + // CheckerInfo stores all the symbol information for a type-checked program struct CheckerInfo { Checker *checker; @@ -340,7 +352,8 @@ struct CheckerInfo { MPMCQueue intrinsics_entry_point_usage; - + BlockingMutex objc_types_mutex; + PtrMap objc_msgSend_types; }; struct CheckerContext { diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index d833a055f..1a9d7d7a0 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -250,6 +250,9 @@ BuiltinProc__type_end, BuiltinProc___entry_point, + BuiltinProc_objc_send, + BuiltinProc_objc_create, + BuiltinProc_COUNT, }; gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { @@ -500,4 +503,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("__entry_point"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + + {STR_LIT("objc_send"), 3, true, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("objc_create"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, }; diff --git a/src/entity.cpp b/src/entity.cpp index 8327a517e..4d5b3b9e1 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -186,6 +186,7 @@ struct Entity { Type * type_parameter_specialization; String ir_mangled_name; bool is_type_alias; + String objc_class_name; } TypeName; struct { u64 tags; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 304effb7f..7941c65a3 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -652,7 +652,53 @@ lbProcedure *lb_create_startup_type_info(lbModule *m) { return p; } -lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *startup_type_info, Array &global_variables) { // Startup Runtime +lbProcedure *lb_create_objc_names(lbModule *main_module) { + if (build_context.metrics.os != TargetOs_darwin) { + return nullptr; + } + Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl); + lbProcedure *p = lb_create_dummy_procedure(main_module, str_lit("__$init_objc_names"), proc_type); + p->is_startup = true; + return p; +} + +void lb_finalize_objc_names(lbProcedure *p) { + if (p == nullptr) { + return; + } + lbModule *m = p->module; + + LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod); + lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level); + LLVMFinalizeFunctionPassManager(default_function_pass_manager); + + LLVMSetLinkage(p->value, LLVMInternalLinkage); + lb_begin_procedure_body(p); + for_array(i, m->objc_classes.entries) { + auto const &entry = m->objc_classes.entries[i]; + String name = entry.key.string; + auto args = array_make(permanent_allocator(), 1); + args[0] = lb_const_value(m, t_cstring, exact_value_string(name)); + lbValue ptr = lb_emit_runtime_call(p, "objc_lookUpClass", args); + lb_addr_store(p, entry.value, ptr); + } + + for_array(i, m->objc_selectors.entries) { + auto const &entry = m->objc_selectors.entries[i]; + String name = entry.key.string; + auto args = array_make(permanent_allocator(), 1); + args[0] = lb_const_value(m, t_cstring, exact_value_string(name)); + lbValue ptr = lb_emit_runtime_call(p, "sel_registerName", args); + lb_addr_store(p, entry.value, ptr); + } + + lb_end_procedure_body(p); + + lb_run_function_pass_manager(default_function_pass_manager, p); + +} + +lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *startup_type_info, lbProcedure *objc_names, Array &global_variables) { // Startup Runtime LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(main_module->mod); lb_populate_function_pass_manager(main_module, default_function_pass_manager, false, build_context.optimization_level); LLVMFinalizeFunctionPassManager(default_function_pass_manager); @@ -666,6 +712,10 @@ lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *start LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, startup_type_info->type)), startup_type_info->value, nullptr, 0, ""); + if (objc_names) { + LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, objc_names->type)), objc_names->value, nullptr, 0, ""); + } + for_array(i, global_variables) { auto *var = &global_variables[i]; if (var->is_initialized) { @@ -1570,8 +1620,10 @@ void lb_generate_code(lbGenerator *gen) { TIME_SECTION("LLVM Runtime Type Information Creation"); lbProcedure *startup_type_info = lb_create_startup_type_info(default_module); + lbProcedure *objc_names = lb_create_objc_names(default_module); + TIME_SECTION("LLVM Runtime Startup Creation (Global Variables)"); - lbProcedure *startup_runtime = lb_create_startup_runtime(default_module, startup_type_info, global_variables); + lbProcedure *startup_runtime = lb_create_startup_runtime(default_module, startup_type_info, objc_names, global_variables); gb_unused(startup_runtime); TIME_SECTION("LLVM Global Procedures and Types"); @@ -1650,6 +1702,8 @@ void lb_generate_code(lbGenerator *gen) { } } + lb_finalize_objc_names(objc_names); + if (build_context.ODIN_DEBUG) { TIME_SECTION("LLVM Debug Info Complete Types and Finalize"); for_array(j, gen->modules.entries) { @@ -1662,6 +1716,7 @@ void lb_generate_code(lbGenerator *gen) { } + TIME_SECTION("LLVM Function Pass"); for_array(i, gen->modules.entries) { lbModule *m = gen->modules.entries[i].value; diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index d7093bc63..087cda22a 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -144,6 +144,9 @@ struct lbModule { PtrMap debug_values; Array debug_incomplete_types; + + StringMap objc_classes; + StringMap objc_selectors; }; struct lbGenerator { @@ -293,7 +296,6 @@ struct lbProcedure { bool lb_init_generator(lbGenerator *gen, Checker *c); -void lb_generate_module(lbGenerator *gen); String lb_mangle_name(lbModule *m, Entity *e); String lb_get_entity_name(lbModule *m, Entity *e, String name = {}); @@ -366,7 +368,7 @@ lbContextData *lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx); lbContextData *lb_push_context_onto_stack_from_implicit_parameter(lbProcedure *p); -lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value={}); +lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value={}, Entity **entity_=nullptr); lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e=nullptr, bool zero_init=true, i32 param_index=0, bool force_no_init=false); void lb_add_foreign_library_path(lbModule *m, Entity *e); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 2fc21b534..1c98aa77f 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -72,6 +72,9 @@ void lb_init_module(lbModule *m, Checker *c) { map_init(&m->debug_values, a); array_init(&m->debug_incomplete_types, a, 0, 1024); + + string_map_init(&m->objc_classes, a); + string_map_init(&m->objc_selectors, a); } bool lb_init_generator(lbGenerator *gen, Checker *c) { @@ -2450,7 +2453,7 @@ lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) { return {}; } -lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value) { +lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value, Entity **entity_) { GB_ASSERT(type != nullptr); type = default_type(type); @@ -2476,6 +2479,9 @@ lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value) { lb_add_entity(m, e, g); lb_add_member(m, name, g); + + if (entity_) *entity_ = e; + return lb_addr(g); } diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 7a6fac603..6de0aaed7 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -2106,6 +2106,9 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, res.type = t_uintptr; return res; } + + case BuiltinProc_objc_send: + return lb_handle_obj_send(p, expr); } GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name)); diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 7e2bd7daa..d92f711ba 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -1819,3 +1819,99 @@ void lb_set_wasm_export_attributes(LLVMValueRef value, String export_name) { LLVMSetVisibility(value, LLVMDefaultVisibility); LLVMAddTargetDependentFunctionAttr(value, "wasm-export-name", alloc_cstring(permanent_allocator(), export_name)); } + + +lbValue lb_lookup_runtime_procedure(lbModule *m, String const &name); + +lbValue lb_handle_obj_id(lbProcedure *p, Ast *expr) { + TypeAndValue const &tav = type_and_value_of_expr(expr); + if (tav.mode == Addressing_Type) { + Type *type = tav.type; + GB_ASSERT_MSG(type->kind == Type_Named, "%s", type_to_string(type)); + Entity *e = type->Named.type_name; + GB_ASSERT(e->kind == Entity_TypeName); + String name = e->TypeName.objc_class_name; + + lbAddr *found = string_map_get(&p->module->objc_classes, name); + if (found) { + return lb_addr_load(p, *found); + } else { + lbModule *default_module = &p->module->gen->default_module; + Entity *e = nullptr; + lbAddr default_addr = lb_add_global_generated(default_module, t_objc_Class, {}, &e); + + lbValue ptr = lb_find_value_from_entity(p->module, e); + lbAddr local_addr = lb_addr(ptr); + + string_map_set(&default_module->objc_classes, name, default_addr); + if (default_module != p->module) { + string_map_set(&p->module->objc_classes, name, local_addr); + } + return lb_addr_load(p, local_addr); + } + } + + return lb_build_expr(p, expr); +} + + +lbValue lb_handle_obj_selector(lbProcedure *p, String const &name) { + lbAddr *found = string_map_get(&p->module->objc_selectors, name); + if (found) { + return lb_addr_load(p, *found); + } else { + lbModule *default_module = &p->module->gen->default_module; + Entity *e = nullptr; + lbAddr default_addr = lb_add_global_generated(default_module, t_objc_SEL, {}, &e); + + lbValue ptr = lb_find_value_from_entity(p->module, e); + lbAddr local_addr = lb_addr(ptr); + + string_map_set(&default_module->objc_selectors, name, default_addr); + if (default_module != p->module) { + string_map_set(&p->module->objc_selectors, name, local_addr); + } + return lb_addr_load(p, local_addr); + } +} + + +lbValue lb_handle_obj_send(lbProcedure *p, Ast *expr) { + ast_node(ce, CallExpr, expr); + + lbModule *m = p->module; + CheckerInfo *info = m->info; + ObjcMsgData data = map_must_get(&info->objc_msgSend_types, expr); + GB_ASSERT(data.proc_type != nullptr); + + GB_ASSERT(ce->args.count >= 3); + auto args = array_make(permanent_allocator(), 0, ce->args.count-1); + + lbValue id = lb_handle_obj_id(p, ce->args[1]); + Ast *sel_expr = ce->args[2]; + GB_ASSERT(sel_expr->tav.value.kind == ExactValue_String); + lbValue sel = lb_handle_obj_selector(p, sel_expr->tav.value.value_string); + + array_add(&args, id); + array_add(&args, sel); + for (isize i = 3; i < ce->args.count; i++) { + lbValue arg = lb_build_expr(p, ce->args[i]); + array_add(&args, arg); + } + + + lbValue the_proc = {}; + switch (data.kind) { + default: + GB_PANIC("unhandled ObjcMsgKind %u", data.kind); + break; + case ObjcMsg_normal: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend")); break; + case ObjcMsg_fpret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_fpret")); break; + case ObjcMsg_fp2ret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_fp2ret")); break; + case ObjcMsg_stret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_stret")); break; + } + + the_proc = lb_emit_conv(p, the_proc, data.proc_type); + + return lb_emit_call(p, the_proc, args); +} diff --git a/src/types.cpp b/src/types.cpp index 9ee6ba359..cdb31c6e1 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -685,6 +685,16 @@ gb_global Type *t_map_header = nullptr; gb_global Type *t_equal_proc = nullptr; gb_global Type *t_hasher_proc = nullptr; +gb_global Type *t_objc_object = nullptr; +gb_global Type *t_objc_selector = nullptr; +gb_global Type *t_objc_class = nullptr; + +gb_global Type *t_objc_id = nullptr; +gb_global Type *t_objc_SEL = nullptr; +gb_global Type *t_objc_Class = nullptr; + + + gb_global RecursiveMutex g_type_mutex; struct TypePath; -- cgit v1.2.3 From c5d348515dcddbd2c29aca79f1863dd36af5476a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 8 Feb 2022 22:59:37 +0000 Subject: Add `intrinsics.type_is_subtype_of`; `intrinsics.objc_selector_name` --- src/check_builtin.cpp | 42 ++++++++++++++++++++++++++++++++++++----- src/check_expr.cpp | 36 ----------------------------------- src/checker_builtin_procs.hpp | 8 ++++++-- src/llvm_backend_proc.cpp | 3 +++ src/llvm_backend_utility.cpp | 11 +++++++++++ src/types.cpp | 44 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 101 insertions(+), 43 deletions(-) (limited to 'src/types.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 8ada77b12..df22d82e2 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -350,9 +350,15 @@ bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call return true; } break; - case BuiltinProc_objc_create: { - GB_PANIC("TODO: BuiltinProc_objc_create"); - return false; + case BuiltinProc_objc_selector_name: { + String sel_name = {}; + if (!is_constant_string(c, builtin_name, ce->args[0], &sel_name)) { + return false; + } + + operand->type = t_objc_SEL; + operand->mode = Addressing_Value; + return true; } break; } } @@ -392,8 +398,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_len: case BuiltinProc_min: case BuiltinProc_max: + case BuiltinProc_type_is_subtype_of: case BuiltinProc_objc_send: - case BuiltinProc_objc_create: + case BuiltinProc_objc_selector_name: // NOTE(bill): The first arg may be a Type, this will be checked case by case break; @@ -435,7 +442,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; case BuiltinProc_objc_send: - case BuiltinProc_objc_create: + case BuiltinProc_objc_selector_name: return check_builtin_objc_procedure(c, operand, call, id, type_hint); case BuiltinProc___entry_point: @@ -3945,6 +3952,31 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; + case BuiltinProc_type_is_subtype_of: + { + Operand op_src = {}; + Operand op_dst = {}; + + check_expr_or_type(c, &op_src, ce->args[0]); + if (op_src.mode != Addressing_Type) { + gbString e = expr_to_string(op_src.expr); + error(op_src.expr, "'%.*s' expects a type, got %s", LIT(builtin_name), e); + gb_string_free(e); + return false; + } + check_expr_or_type(c, &op_dst, ce->args[1]); + if (op_dst.mode != Addressing_Type) { + gbString e = expr_to_string(op_dst.expr); + error(op_dst.expr, "'%.*s' expects a type, got %s", LIT(builtin_name), e); + gb_string_free(e); + return false; + } + + operand->value = exact_value_bool(is_type_subtype_of(op_src.type, op_dst.type)); + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + } break; + case BuiltinProc_type_field_index_of: { Operand op = {}; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 3f31ac810..6db17a316 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -228,42 +228,6 @@ void check_scope_decls(CheckerContext *c, Slice const &nodes, isize reser } } - -isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0, bool src_is_ptr = false) { - Type *prev_src = src; - src = type_deref(src); - if (!src_is_ptr) { - src_is_ptr = src != prev_src; - } - src = base_type(src); - - if (!is_type_struct(src)) { - return 0; - } - - for_array(i, src->Struct.fields) { - Entity *f = src->Struct.fields[i]; - if (f->kind != Entity_Variable || (f->flags&EntityFlag_Using) == 0) { - continue; - } - - if (are_types_identical(f->type, dst)) { - return level+1; - } - if (src_is_ptr && is_type_pointer(dst)) { - if (are_types_identical(f->type, type_deref(dst))) { - return level+1; - } - } - isize nested_level = check_is_assignable_to_using_subtype(f->type, dst, level+1, src_is_ptr); - if (nested_level > 0) { - return nested_level; - } - } - - return 0; -} - bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_entity, Type *type, Array *param_operands, Ast *poly_def_node, PolyProcData *poly_proc_data) { /////////////////////////////////////////////////////////////////////////////// diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 1a9d7d7a0..c14c18412 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -241,6 +241,8 @@ BuiltinProc__type_simple_boolean_end, BuiltinProc_type_polymorphic_record_parameter_count, BuiltinProc_type_polymorphic_record_parameter_value, + BuiltinProc_type_is_subtype_of, + BuiltinProc_type_field_index_of, BuiltinProc_type_equal_proc, @@ -251,7 +253,7 @@ BuiltinProc__type_end, BuiltinProc___entry_point, BuiltinProc_objc_send, - BuiltinProc_objc_create, + BuiltinProc_objc_selector_name, BuiltinProc_COUNT, }; @@ -494,6 +496,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_polymorphic_record_parameter_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_polymorphic_record_parameter_value"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_subtype_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_field_index_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_equal_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, @@ -505,5 +509,5 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("__entry_point"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("objc_send"), 3, true, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("objc_create"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("objc_selector_name"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, }; diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 6de0aaed7..caa3dfa1a 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -2109,6 +2109,9 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, case BuiltinProc_objc_send: return lb_handle_obj_send(p, expr); + + case BuiltinProc_objc_selector_name: + return lb_handle_obj_selector_name(p, expr); } GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name)); diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index d92f711ba..8ef66df7a 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -1915,3 +1915,14 @@ lbValue lb_handle_obj_send(lbProcedure *p, Ast *expr) { return lb_emit_call(p, the_proc, args); } + + +lbValue lb_handle_obj_selector_name(lbProcedure *p, Ast *expr) { + ast_node(ce, CallExpr, expr); + + auto tav = ce->args[0]->tav; + GB_ASSERT(tav.value.kind == ExactValue_String); + String name = tav.value.value_string; + return lb_handle_obj_selector(p, name); + +} \ No newline at end of file diff --git a/src/types.cpp b/src/types.cpp index cdb31c6e1..024d644a2 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -3748,6 +3748,50 @@ i64 type_offset_of_from_selection(Type *type, Selection sel) { return offset; } +isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0, bool src_is_ptr = false) { + Type *prev_src = src; + src = type_deref(src); + if (!src_is_ptr) { + src_is_ptr = src != prev_src; + } + src = base_type(src); + + if (!is_type_struct(src)) { + return 0; + } + + for_array(i, src->Struct.fields) { + Entity *f = src->Struct.fields[i]; + if (f->kind != Entity_Variable || (f->flags&EntityFlag_Using) == 0) { + continue; + } + + if (are_types_identical(f->type, dst)) { + return level+1; + } + if (src_is_ptr && is_type_pointer(dst)) { + if (are_types_identical(f->type, type_deref(dst))) { + return level+1; + } + } + isize nested_level = check_is_assignable_to_using_subtype(f->type, dst, level+1, src_is_ptr); + if (nested_level > 0) { + return nested_level; + } + } + + return 0; +} + +bool is_type_subtype_of(Type *src, Type *dst) { + if (are_types_identical(src, dst)) { + return true; + } + + return 0 < check_is_assignable_to_using_subtype(src, dst, 0, is_type_pointer(src)); +} + + Type *get_struct_field_type(Type *t, isize index) { t = base_type(type_deref(t)); -- cgit v1.2.3 From f8afda3b221f6c2279a393c2c0fb8ab7ea1d59df Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 11 Feb 2022 22:54:51 +0000 Subject: Add more objc attributes --- core/sys/darwin/Foundation/NSArray.odin | 36 ++++-- core/sys/darwin/Foundation/NSAutoreleasePool.odin | 15 ++- core/sys/darwin/Foundation/NSBundle.odin | 56 ++++++-- core/sys/darwin/Foundation/NSData.odin | 12 ++ core/sys/darwin/Foundation/NSDate.odin | 12 ++ core/sys/darwin/Foundation/NSDictionary.odin | 18 +++ core/sys/darwin/Foundation/NSEnumerator.odin | 15 ++- core/sys/darwin/Foundation/NSError.odin | 22 +++- core/sys/darwin/Foundation/NSLock.odin | 24 ++++ core/sys/darwin/Foundation/NSNotification.odin | 15 +++ core/sys/darwin/Foundation/NSNumber.odin | 149 ++++++++++++---------- core/sys/darwin/Foundation/NSObject.odin | 28 ++-- core/sys/darwin/Foundation/NSString.odin | 24 ++++ core/sys/darwin/Foundation/NSURL.odin | 12 ++ src/check_builtin.cpp | 8 +- src/check_decl.cpp | 63 +++++++++ src/check_type.cpp | 2 + src/checker.cpp | 50 +++++++- src/checker.hpp | 6 +- src/entity.cpp | 23 ++++ src/llvm_backend_expr.cpp | 11 +- src/main.cpp | 32 +---- src/string.cpp | 31 +++++ src/types.cpp | 62 +++++++++ 24 files changed, 586 insertions(+), 140 deletions(-) (limited to 'src/types.cpp') diff --git a/core/sys/darwin/Foundation/NSArray.odin b/core/sys/darwin/Foundation/NSArray.odin index 435e239a2..e17223ff7 100644 --- a/core/sys/darwin/Foundation/NSArray.odin +++ b/core/sys/darwin/Foundation/NSArray.odin @@ -3,26 +3,40 @@ package objc_Foundation import "core:intrinsics" @(objc_class="NSArray") -Array :: struct($T: typeid) where intrinsics.type_is_pointer(T), intrinsics.type_is_subtype_of(T, ^Object) { - using _: Copying(Array(T)), +Array :: struct { + using _: Copying(Array), } -Array_initWithObjects :: proc(self: ^$A/Array($T), objects: [^]^Object, count: UInteger) -> ^A { - return msgSend(^A, "initWithObjects:count:", objects, count) +@(objc_type=Array, objc_class_name="alloc") +Array_alloc :: proc() -> ^Array { + return msgSend(^Array, Array, "alloc") } -Array_initWithCoder :: proc(self: ^$A/Array($T), coder: ^Coder) -> ^A { - return msgSend(^A, "initWithCoder:", coder) +@(objc_type=Array, objc_name="init") +Array_init :: proc(self: ^Array) -> ^Array { + return msgSend(^Array, self, "init") } -Array_objectAtIndex :: proc(self: ^Array($T), index: UInteger) -> ^Object { - return msgSend(^Object, self, "objectAtIndex:", index) +@(objc_type=Array, objc_name="initWithObjects") +Array_initWithObjects :: proc(self: ^Array, objects: [^]^Object, count: UInteger) -> ^Array { + return msgSend(^Array, self, "initWithObjects:count:", objects, count) +} + +@(objc_type=Array, objc_name="initWithCoder") +Array_initWithCoder :: proc(self: ^Array, coder: ^Coder) -> ^Array { + return msgSend(^Array, self, "initWithCoder:", coder) } -Array_object :: proc(self: ^Array($T), index: UInteger) -> T { - return (T)(Array_objectAtIndex(self, index)) +@(objc_type=Array, objc_name="object") +Array_object :: proc(self: ^Array, index: UInteger) -> ^Object { + return msgSend(^Object, self, "objectAtIndex:", index) +} +@(objc_type=Array, objc_name="objectAs") +Array_objectAs :: proc(self: ^Array, index: UInteger, $T: typeid) -> T where intrinsics.type_is_pointer(T), intrinsics.type_is_subtype_of(T, ^Object) { + return (T)(Array_object(self, index)) } -Array_count :: proc(self: ^Array($T)) -> UInteger { +@(objc_type=Array, objc_name="count") +Array_count :: proc(self: ^Array) -> UInteger { return msgSend(UInteger, self, "count") } diff --git a/core/sys/darwin/Foundation/NSAutoreleasePool.odin b/core/sys/darwin/Foundation/NSAutoreleasePool.odin index 47cbc0e9a..17ec88ba1 100644 --- a/core/sys/darwin/Foundation/NSAutoreleasePool.odin +++ b/core/sys/darwin/Foundation/NSAutoreleasePool.odin @@ -3,12 +3,25 @@ package objc_Foundation @(objc_class="NSAutoreleasePool") AutoreleasePool :: struct {using _: Object} +@(objc_type=AutoreleasePool, objc_class_name="alloc") +AutoreleasePool_alloc :: proc() -> ^AutoreleasePool { + return msgSend(^AutoreleasePool, AutoreleasePool, "alloc") +} + +@(objc_type=AutoreleasePool, objc_name="init") +AutoreleasePool_init :: proc(self: ^AutoreleasePool) -> ^AutoreleasePool { + return msgSend(^AutoreleasePool, self, "init") +} + +@(objc_type=AutoreleasePool, objc_name="drain") AutoreleasePool_drain :: proc(self: ^AutoreleasePool) { msgSend(nil, self, "drain") } +@(objc_type=AutoreleasePool, objc_name="addObject") AutoreleasePool_addObject :: proc(self: ^AutoreleasePool, obj: ^Object) { msgSend(nil, self, "addObject:", obj) } +@(objc_type=AutoreleasePool, objc_name="showPools") AutoreleasePool_showPools :: proc(self: ^AutoreleasePool, obj: ^Object) { msgSend(nil, self, "showPools") } @@ -16,5 +29,5 @@ AutoreleasePool_showPools :: proc(self: ^AutoreleasePool, obj: ^Object) { @(deferred_out=AutoreleasePool_drain) scoped_autoreleasepool :: proc() -> ^AutoreleasePool { - return init(alloc(AutoreleasePool)) + return AutoreleasePool.alloc()->init() } \ No newline at end of file diff --git a/core/sys/darwin/Foundation/NSBundle.odin b/core/sys/darwin/Foundation/NSBundle.odin index 375e15b65..a18809b7e 100644 --- a/core/sys/darwin/Foundation/NSBundle.odin +++ b/core/sys/darwin/Foundation/NSBundle.odin @@ -3,98 +3,120 @@ package objc_Foundation @(objc_class="NSBundle") Bundle :: struct { using _: Object } +@(objc_type=Bundle, objc_class_name="mainBundle") Bundle_mainBundle :: proc() -> ^Bundle { return msgSend(^Bundle, Bundle, "mainBundle") } +@(objc_type=Bundle, objc_class_name="bundleWithPath") Bundle_bundleWithPath :: proc(path: ^String) -> ^Bundle { return msgSend(^Bundle, Bundle, "bundleWithPath:", path) } +@(objc_type=Bundle, objc_class_name="bundleWithURL") Bundle_bundleWithURL :: proc(url: ^URL) -> ^Bundle { return msgSend(^Bundle, Bundle, "bundleWithUrl:", url) } -Bundle_bundle :: proc{ - Bundle_bundleWithPath, - Bundle_bundleWithURL, + + +@(objc_type=Bundle, objc_class_name="alloc") +Bundle_alloc :: proc() -> ^Bundle { + return msgSend(^Bundle, Bundle, "alloc") } +@(objc_type=Bundle, objc_name="init") +Bundle_init :: proc(self: ^Bundle) -> ^Bundle { + return msgSend(^Bundle, self, "init") +} +@(objc_type=Bundle, objc_name="initWithPath") Bundle_initWithPath :: proc(self: ^Bundle, path: ^String) -> ^Bundle { return msgSend(^Bundle, self, "initWithPath:", path) } +@(objc_type=Bundle, objc_name="initWithURL") Bundle_initWithURL :: proc(self: ^Bundle, url: ^URL) -> ^Bundle { return msgSend(^Bundle, self, "initWithUrl:", url) } -Bundle_init :: proc{ - Bundle_initWithPath, - Bundle_initWithURL, -} - -Bundle_allBundles :: proc() -> (all: ^Array(^Bundle)) { +@(objc_type=Bundle, objc_name="allBundles") +Bundle_allBundles :: proc() -> (all: ^Array) { return msgSend(type_of(all), Bundle, "allBundles") } -Bundle_allFrameworks :: proc() -> (all: ^Array(^Object)) { +@(objc_type=Bundle, objc_name="allFrameworks") +Bundle_allFrameworks :: proc() -> (all: ^Array) { return msgSend(type_of(all), Bundle, "allFrameworks") } +@(objc_type=Bundle, objc_name="load") Bundle_load :: proc(self: ^Bundle) -> BOOL { return msgSend(BOOL, self, "load") } +@(objc_type=Bundle, objc_name="unload") Bundle_unload :: proc(self: ^Bundle) -> BOOL { return msgSend(BOOL, self, "unload") } +@(objc_type=Bundle, objc_name="isLoaded") Bundle_isLoaded :: proc(self: ^Bundle) -> BOOL { return msgSend(BOOL, self, "isLoaded") } +@(objc_type=Bundle, objc_name="preflightAndReturnError") Bundle_preflightAndReturnError :: proc(self: ^Bundle) -> (ok: BOOL, error: ^Error) { ok = msgSend(BOOL, self, "preflightAndReturnError:", &error) return } +@(objc_type=Bundle, objc_name="loadAndReturnError") Bundle_loadAndReturnError :: proc(self: ^Bundle) -> (ok: BOOL, error: ^Error) { ok = msgSend(BOOL, self, "loadAndReturnError:", &error) return } +@(objc_type=Bundle, objc_name="bundleURL") Bundle_bundleURL :: proc(self: ^Bundle) -> ^URL { return msgSend(^URL, self, "bundleURL") } +@(objc_type=Bundle, objc_name="resourceURL") Bundle_resourceURL :: proc(self: ^Bundle) -> ^URL { return msgSend(^URL, self, "resourceURL") } +@(objc_type=Bundle, objc_name="executableURL") Bundle_executableURL :: proc(self: ^Bundle) -> ^URL { return msgSend(^URL, self, "executableURL") } +@(objc_type=Bundle, objc_name="URLForAuxiliaryExecutable") Bundle_URLForAuxiliaryExecutable :: proc(self: ^Bundle, executableName: ^String) -> ^URL { return msgSend(^URL, self, "URLForAuxiliaryExecutable:", executableName) } +@(objc_type=Bundle, objc_name="privateFrameworksURL") Bundle_privateFrameworksURL :: proc(self: ^Bundle) -> ^URL { return msgSend(^URL, self, "privateFrameworksURL") } +@(objc_type=Bundle, objc_name="sharedFrameworksURL") Bundle_sharedFrameworksURL :: proc(self: ^Bundle) -> ^URL { return msgSend(^URL, self, "sharedFrameworksURL") } +@(objc_type=Bundle, objc_name="sharedSupportURL") Bundle_sharedSupportURL :: proc(self: ^Bundle) -> ^URL { return msgSend(^URL, self, "sharedSupportURL") } +@(objc_type=Bundle, objc_name="builtInPlugInsURL") Bundle_builtInPlugInsURL :: proc(self: ^Bundle) -> ^URL { return msgSend(^URL, self, "builtInPlugInsURL") } +@(objc_type=Bundle, objc_name="appStoreReceiptURL") Bundle_appStoreReceiptURL :: proc(self: ^Bundle) -> ^URL { return msgSend(^URL, self, "appStoreReceiptURL") } @@ -102,60 +124,74 @@ Bundle_appStoreReceiptURL :: proc(self: ^Bundle) -> ^URL { +@(objc_type=Bundle, objc_name="bundlePath") Bundle_bundlePath :: proc(self: ^Bundle) -> ^String { return msgSend(^String, self, "bundlePath") } +@(objc_type=Bundle, objc_name="resourcePath") Bundle_resourcePath :: proc(self: ^Bundle) -> ^String { return msgSend(^String, self, "resourcePath") } +@(objc_type=Bundle, objc_name="executablePath") Bundle_executablePath :: proc(self: ^Bundle) -> ^String { return msgSend(^String, self, "executablePath") } +@(objc_type=Bundle, objc_name="PathForAuxiliaryExecutable") Bundle_PathForAuxiliaryExecutable :: proc(self: ^Bundle, executableName: ^String) -> ^String { return msgSend(^String, self, "PathForAuxiliaryExecutable:", executableName) } +@(objc_type=Bundle, objc_name="privateFrameworksPath") Bundle_privateFrameworksPath :: proc(self: ^Bundle) -> ^String { return msgSend(^String, self, "privateFrameworksPath") } +@(objc_type=Bundle, objc_name="sharedFrameworksPath") Bundle_sharedFrameworksPath :: proc(self: ^Bundle) -> ^String { return msgSend(^String, self, "sharedFrameworksPath") } +@(objc_type=Bundle, objc_name="sharedSupportPath") Bundle_sharedSupportPath :: proc(self: ^Bundle) -> ^String { return msgSend(^String, self, "sharedSupportPath") } +@(objc_type=Bundle, objc_name="builtInPlugInsPath") Bundle_builtInPlugInsPath :: proc(self: ^Bundle) -> ^String { return msgSend(^String, self, "builtInPlugInsPath") } +@(objc_type=Bundle, objc_name="appStoreReceiptPath") Bundle_appStoreReceiptPath :: proc(self: ^Bundle) -> ^String { return msgSend(^String, self, "appStoreReceiptPath") } +@(objc_type=Bundle, objc_name="bundleIdentifier") Bundle_bundleIdentifier :: proc(self: ^Bundle) -> ^String { return msgSend(^String, self, "bundleIdentifier") } +@(objc_type=Bundle, objc_name="infoDictionary") Bundle_infoDictionary :: proc(self: ^Bundle) -> ^Dictionary { return msgSend(^Dictionary, self, "infoDictionary") } +@(objc_type=Bundle, objc_name="localizedInfoDictionary") Bundle_localizedInfoDictionary :: proc(self: ^Bundle) -> ^Dictionary { return msgSend(^Dictionary, self, "localizedInfoDictionary") } +@(objc_type=Bundle, objc_name="objectForInfoDictionaryKey") Bundle_objectForInfoDictionaryKey :: proc(self: ^Bundle, key: ^String) -> ^Object { return msgSend(^Object, self, "objectForInfoDictionaryKey:", key) } +@(objc_type=Bundle, objc_name="localizedStringForKey") Bundle_localizedStringForKey :: proc(self: ^Bundle, key: ^String, value: ^String = nil, tableName: ^String = nil) -> ^String { return msgSend(^String, self, "localizedStringForKey:value:table:", key, value, tableName) } diff --git a/core/sys/darwin/Foundation/NSData.odin b/core/sys/darwin/Foundation/NSData.odin index 93bb3ae0e..e28f6a644 100644 --- a/core/sys/darwin/Foundation/NSData.odin +++ b/core/sys/darwin/Foundation/NSData.odin @@ -3,10 +3,22 @@ package objc_Foundation @(objc_class="NSData") Data :: struct {using _: Copying(Data)} +@(objc_type=Data, objc_class_name="alloc") +Data_alloc :: proc() -> ^Data { + return msgSend(^Data, Data, "alloc") +} + +@(objc_type=Data, objc_name="init") +Data_init :: proc(self: ^Data) -> ^Data { + return msgSend(^Data, self, "init") +} + +@(objc_type=Data, objc_name="mutableBytes") Data_mutableBytes :: proc(self: ^Data) -> rawptr { return msgSend(rawptr, self, "mutableBytes") } +@(objc_type=Data, objc_name="length") Data_length :: proc(self: ^Data) -> UInteger { return msgSend(UInteger, self, "length") } \ No newline at end of file diff --git a/core/sys/darwin/Foundation/NSDate.odin b/core/sys/darwin/Foundation/NSDate.odin index 4b298ee24..85bb14c3e 100644 --- a/core/sys/darwin/Foundation/NSDate.odin +++ b/core/sys/darwin/Foundation/NSDate.odin @@ -3,6 +3,18 @@ package objc_Foundation @(objc_class="NSDate") Date :: struct {using _: Copying(Date)} +@(objc_type=Date, objc_class_name="alloc") +Date_alloc :: proc() -> ^Date { + return msgSend(^Date, Date, "alloc") +} + +@(objc_type=Date, objc_name="init") +Date_init :: proc(self: ^Date) -> ^Date { + return msgSend(^Date, self, "init") +} + + +@(objc_type=Date, objc_name="dateWithTimeIntervalSinceNow") Date_dateWithTimeIntervalSinceNow :: proc(secs: TimeInterval) -> ^Date { return msgSend(^Date, Date, "dateWithTimeIntervalSinceNow:", secs) } \ No newline at end of file diff --git a/core/sys/darwin/Foundation/NSDictionary.odin b/core/sys/darwin/Foundation/NSDictionary.odin index f82b8cffd..3eb378dc7 100644 --- a/core/sys/darwin/Foundation/NSDictionary.odin +++ b/core/sys/darwin/Foundation/NSDictionary.odin @@ -3,31 +3,49 @@ package objc_Foundation @(objc_class="NSDictionary") Dictionary :: struct {using _: Copying(Dictionary)} +@(objc_type=Dictionary, objc_class_name="dictionary") Dictionary_dictionary :: proc() -> ^Dictionary { return msgSend(^Dictionary, Dictionary, "dictionary") } +@(objc_type=Dictionary, objc_class_name="dictionaryWithObject") Dictionary_dictionaryWithObject :: proc(object: ^Object, forKey: ^Object) -> ^Dictionary { return msgSend(^Dictionary, Dictionary, "dictionaryWithObject:forKey:", object, forKey) } +@(objc_type=Dictionary, objc_class_name="dictionaryWithObjects") Dictionary_dictionaryWithObjects :: proc(objects: [^]^Object, forKeys: [^]^Object, count: UInteger) -> ^Dictionary { return msgSend(^Dictionary, Dictionary, "dictionaryWithObjects:forKeys:count", objects, forKeys, count) } +@(objc_type=Dictionary, objc_class_name="alloc") +Dictionary_alloc :: proc() -> ^Dictionary { + return msgSend(^Dictionary, Dictionary, "alloc") +} + +@(objc_type=Dictionary, objc_name="init") +Dictionary_init :: proc(self: ^Dictionary) -> ^Dictionary { + return msgSend(^Dictionary, self, "init") +} + + +@(objc_type=Dictionary, objc_name="initWithObjects") Dictionary_initWithObjects :: proc(self: ^Dictionary, objects: [^]^Object, forKeys: [^]^Object, count: UInteger) -> ^Dictionary { return msgSend(^Dictionary, self, "initWithObjects:forKeys:count", objects, forKeys, count) } +@(objc_type=Dictionary, objc_name="objectForKey") Dictionary_objectForKey :: proc(self: ^Dictionary, key: ^Object) -> ^Object { return msgSend(^Dictionary, self, "objectForKey:", key) } +@(objc_type=Dictionary, objc_name="count") Dictionary_count :: proc(self: ^Dictionary) -> UInteger { return msgSend(UInteger, self, "count") } +@(objc_type=Dictionary, objc_name="keyEnumerator") Dictionary_keyEnumerator :: proc(self: ^Dictionary, $KeyType: typeid) -> (enumerator: ^Enumerator(KeyType)) { return msgSend(type_of(enumerator), self, "keyEnumerator") } diff --git a/core/sys/darwin/Foundation/NSEnumerator.odin b/core/sys/darwin/Foundation/NSEnumerator.odin index b6adf2b98..8a5a32e69 100644 --- a/core/sys/darwin/Foundation/NSEnumerator.odin +++ b/core/sys/darwin/Foundation/NSEnumerator.odin @@ -18,6 +18,19 @@ Enumerator :: struct($T: typeid) where intrinsics.type_is_pointer(T), intrinsics using _: FastEnumeration, } + +@(objc_type=FastEnumeration, objc_class_name="alloc") +FastEnumeration_alloc :: proc() -> ^FastEnumeration { + return msgSend(^FastEnumeration, FastEnumeration, "alloc") +} + +@(objc_type=FastEnumeration, objc_name="init") +FastEnumeration_init :: proc(self: ^FastEnumeration) -> ^FastEnumeration { + return msgSend(^FastEnumeration, self, "init") +} + + +@(objc_type=FastEnumeration, objc_name="countByEnumerating") FastEnumeration_countByEnumerating :: proc(self: ^FastEnumeration, state: ^FastEnumerationState, buffer: [^]^Object, len: UInteger) -> UInteger { return msgSend(UInteger, self, "countByEnumeratingWithState:objects:count:", state, buffer, len) } @@ -26,7 +39,7 @@ Enumerator_nextObject :: proc(self: ^$E/Enumerator($T)) -> T { return msgSend(T, self, "nextObject") } -Enumerator_allObjects :: proc(self: ^$E/Enumerator($T)) -> (all: Array(T)) { +Enumerator_allObjects :: proc(self: ^$E/Enumerator($T)) -> (all: ^Array) { return msgSend(type_of(all), self, "allObjects") } diff --git a/core/sys/darwin/Foundation/NSError.odin b/core/sys/darwin/Foundation/NSError.odin index aebf01035..bff0088e9 100644 --- a/core/sys/darwin/Foundation/NSError.odin +++ b/core/sys/darwin/Foundation/NSError.odin @@ -31,38 +31,58 @@ foreign Foundation { @(objc_class="NSError") Error :: struct { using _: Copying(Error) } + +@(objc_type=Error, objc_class_name="alloc") +Error_alloc :: proc() -> ^Error { + return msgSend(^Error, Error, "alloc") +} + +@(objc_type=Error, objc_name="init") +Error_init :: proc(self: ^Error) -> ^Error { + return msgSend(^Error, self, "init") +} + +@(objc_type=Error, objc_class_name="errorWithDomain") Error_errorWithDomain :: proc(domain: ErrorDomain, code: Integer, userInfo: ^Dictionary) -> ^Error { return msgSend(^Error, Error, "errorWithDomain:code:userInfo:", domain, code, userInfo) } +@(objc_type=Error, objc_name="initWithDomain") Error_initWithDomain :: proc(self: ^Error, domain: ErrorDomain, code: Integer, userInfo: ^Dictionary) -> ^Error { return msgSend(^Error, self, "initWithDomain:code:userInfo:", domain, code, userInfo) } +@(objc_type=Error, objc_name="code") Error_code :: proc(self: ^Error) -> Integer { return msgSend(Integer, self, "code") } +@(objc_type=Error, objc_name="domain") Error_domain :: proc(self: ^Error) -> ErrorDomain { return msgSend(ErrorDomain, self, "domain") } +@(objc_type=Error, objc_name="userInfo") Error_userInfo :: proc(self: ^Error) -> ^Dictionary { return msgSend(^Dictionary, self, "userInfo") } +@(objc_type=Error, objc_name="localizedDescription") Error_localizedDescription :: proc(self: ^Error) -> ^String { return msgSend(^String, self, "localizedDescription") } -Error_localizedRecoveryOptions :: proc(self: ^Error) -> (options: ^Array(^Object)) { +@(objc_type=Error, objc_name="localizedRecoveryOptions") +Error_localizedRecoveryOptions :: proc(self: ^Error) -> (options: ^Array) { return msgSend(type_of(options), self, "localizedRecoveryOptions") } +@(objc_type=Error, objc_name="localizedRecoverySuggestion") Error_localizedRecoverySuggestion :: proc(self: ^Error) -> ^String { return msgSend(^String, self, "localizedRecoverySuggestion") } +@(objc_type=Error, objc_name="localizedFailureReason") Error_localizedFailureReason :: proc(self: ^Error) -> ^String { return msgSend(^String, self, "localizedFailureReason") } \ No newline at end of file diff --git a/core/sys/darwin/Foundation/NSLock.odin b/core/sys/darwin/Foundation/NSLock.odin index 0e76dfc62..3bcc06eab 100644 --- a/core/sys/darwin/Foundation/NSLock.odin +++ b/core/sys/darwin/Foundation/NSLock.odin @@ -12,18 +12,42 @@ Locking_unlock :: proc(self: ^Locking($T)) { @(objc_class="NSCondition") Condition :: struct {using _: Locking(Condition) } + +@(objc_type=Condition, objc_class_name="alloc") +Condition_alloc :: proc() -> ^Condition { + return msgSend(^Condition, Condition, "alloc") +} + +@(objc_type=Condition, objc_name="init") +Condition_init :: proc(self: ^Condition) -> ^Condition { + return msgSend(^Condition, self, "init") +} + +@(objc_type=Condition, objc_name="wait") Condition_wait :: proc(self: ^Condition) { msgSend(nil, self, "wait") } +@(objc_type=Condition, objc_name="waitUntilDate") Condition_waitUntilDate :: proc(self: ^Condition, limit: ^Date) -> BOOL { return msgSend(BOOL, self, "waitUntilDate:", limit) } +@(objc_type=Condition, objc_name="signal") Condition_signal :: proc(self: ^Condition) { msgSend(nil, self, "signal") } +@(objc_type=Condition, objc_name="broadcast") Condition_broadcast :: proc(self: ^Condition) { msgSend(nil, self, "broadcast") +} + +@(objc_type=Condition, objc_name="lock") +Condition_lock :: proc(self: ^Condition) { + msgSend(nil, self, "lock") +} +@(objc_type=Condition, objc_name="unlock") +Condition_unlock :: proc(self: ^Condition) { + msgSend(nil, self, "unlock") } \ No newline at end of file diff --git a/core/sys/darwin/Foundation/NSNotification.odin b/core/sys/darwin/Foundation/NSNotification.odin index fa9160cc8..d8b142e53 100644 --- a/core/sys/darwin/Foundation/NSNotification.odin +++ b/core/sys/darwin/Foundation/NSNotification.odin @@ -3,14 +3,29 @@ package objc_Foundation @(objc_class="NSNotification") Notification :: struct{using _: Object} + +@(objc_type=Notification, objc_class_name="alloc") +Notification_alloc :: proc() -> ^Notification { + return msgSend(^Notification, Notification, "alloc") +} + +@(objc_type=Notification, objc_name="init") +Notification_init :: proc(self: ^Notification) -> ^Notification { + return msgSend(^Notification, self, "init") +} + + +@(objc_type=Notification, objc_name="name") Notification_name :: proc(self: ^Notification) -> ^String { return msgSend(^String, self, "name") } +@(objc_type=Notification, objc_name="object") Notification_object :: proc(self: ^Notification) -> ^Object { return msgSend(^Object, self, "object") } +@(objc_type=Notification, objc_name="userInfo") Notification_userInfo :: proc(self: ^Notification) -> ^Dictionary { return msgSend(^Dictionary, self, "userInfo") } \ No newline at end of file diff --git a/core/sys/darwin/Foundation/NSNumber.odin b/core/sys/darwin/Foundation/NSNumber.odin index 2630e6daa..f77b0866a 100644 --- a/core/sys/darwin/Foundation/NSNumber.odin +++ b/core/sys/darwin/Foundation/NSNumber.odin @@ -8,35 +8,53 @@ import "core:c" @(objc_class="NSValue") Value :: struct{using _: Copying(Value)} +@(objc_type=Value, objc_class_name="alloc") +Value_alloc :: proc() -> ^Value { + return msgSend(^Value, Value, "alloc") +} + +@(objc_type=Value, objc_name="init") +Value_init :: proc(self: ^Value) -> ^Value { + return msgSend(^Value, self, "init") +} + +@(objc_type=Value, objc_class_name="valueWithBytes") Value_valueWithBytes :: proc(value: rawptr, type: cstring) -> ^Value { return msgSend(^Value, Value, "valueWithBytes:objCType:", value, type) } +@(objc_type=Value, objc_class_name="valueWithPointer") Value_valueWithPointer :: proc(pointer: rawptr) -> ^Value { return msgSend(^Value, Value, "valueWithPointer:", pointer) } +@(objc_type=Value, objc_name="initWithBytes") Value_initWithBytes :: proc(self: ^Value, value: rawptr, type: cstring) -> ^Value { return msgSend(^Value, self, "initWithBytes:objCType:", value, type) } -Value_initWithCoder :: proc(coder: ^Coder) -> ^Value { - return msgSend(^Value, Value, "initWithCoder:", coder) +@(objc_type=Value, objc_name="initWithCoder") +Value_initWithCoder :: proc(self: ^Value, coder: ^Coder) -> ^Value { + return msgSend(^Value, self, "initWithCoder:", coder) } +@(objc_type=Value, objc_name="getValue") Value_getValue :: proc(self: ^Value, value: rawptr, size: UInteger) { msgSend(nil, self, "getValue:size:", value, size) } +@(objc_type=Value, objc_name="objCType") Value_objCType :: proc(self: ^Value) -> cstring { return msgSend(cstring, self, "objCType") } +@(objc_type=Value, objc_name="isEqualToValue") Value_isEqualToValue :: proc(self, other: ^Value) -> BOOL { return msgSend(BOOL, self, "isEqualToValue:", other) } +@(objc_type=Value, objc_name="pointerValue") Value_pointerValue :: proc(self: ^Value) -> rawptr { return msgSend(rawptr, self, "pointerValue") } @@ -46,19 +64,29 @@ Value_pointerValue :: proc(self: ^Value) -> rawptr { Number :: struct{using _: Copying(Number), using _: Value} -Number_numberWithI8 :: proc(value: i8) -> ^Number { return msgSend(^Number, Number, "numberWithChar:", value) } -Number_numberWithU8 :: proc(value: u8) -> ^Number { return msgSend(^Number, Number, "numberWithUnsignedChar:", value) } -Number_numberWithI16 :: proc(value: i16) -> ^Number { return msgSend(^Number, Number, "numberWithShort:", value) } -Number_numberWithU16 :: proc(value: u16) -> ^Number { return msgSend(^Number, Number, "numberWithUnsignedShort:", value) } -Number_numberWithI32 :: proc(value: i32) -> ^Number { return msgSend(^Number, Number, "numberWithInt:", value) } -Number_numberWithU32 :: proc(value: u32) -> ^Number { return msgSend(^Number, Number, "numberWithUnsignedInt:", value) } -Number_numberWithInt :: proc(value: int) -> ^Number { return msgSend(^Number, Number, "numberWithLong:", value) } -Number_numberWithUint :: proc(value: uint) -> ^Number { return msgSend(^Number, Number, "numberWithUnsignedLong:", value) } -Number_numberWithU64 :: proc(value: u64) -> ^Number { return msgSend(^Number, Number, "numberWithLongLong:", value) } -Number_numberWithI64 :: proc(value: i64) -> ^Number { return msgSend(^Number, Number, "numberWithUnsignedLongLong:", value) } -Number_numberWithF32 :: proc(value: f32) -> ^Number { return msgSend(^Number, Number, "numberWithFloat:", value) } -Number_numberWithF64 :: proc(value: f64) -> ^Number { return msgSend(^Number, Number, "numberWithDouble:", value) } -Number_numberWithBool :: proc(value: BOOL) -> ^Number { return msgSend(^Number, Number, "numberWithBool:", value) } +@(objc_type=Number, objc_class_name="alloc") +Number_alloc :: proc() -> ^Number { + return msgSend(^Number, Number, "alloc") +} + +@(objc_type=Number, objc_name="init") +Number_init :: proc(self: ^Number) -> ^Number { + return msgSend(^Number, self, "init") +} + +@(objc_type=Number, objc_class_name="numberWithI8") Number_numberWithI8 :: proc(value: i8) -> ^Number { return msgSend(^Number, Number, "numberWithChar:", value) } +@(objc_type=Number, objc_class_name="numberWithU8") Number_numberWithU8 :: proc(value: u8) -> ^Number { return msgSend(^Number, Number, "numberWithUnsignedChar:", value) } +@(objc_type=Number, objc_class_name="numberWithI16") Number_numberWithI16 :: proc(value: i16) -> ^Number { return msgSend(^Number, Number, "numberWithShort:", value) } +@(objc_type=Number, objc_class_name="numberWithU16") Number_numberWithU16 :: proc(value: u16) -> ^Number { return msgSend(^Number, Number, "numberWithUnsignedShort:", value) } +@(objc_type=Number, objc_class_name="numberWithI32") Number_numberWithI32 :: proc(value: i32) -> ^Number { return msgSend(^Number, Number, "numberWithInt:", value) } +@(objc_type=Number, objc_class_name="numberWithU32") Number_numberWithU32 :: proc(value: u32) -> ^Number { return msgSend(^Number, Number, "numberWithUnsignedInt:", value) } +@(objc_type=Number, objc_class_name="numberWithInt") Number_numberWithInt :: proc(value: int) -> ^Number { return msgSend(^Number, Number, "numberWithLong:", value) } +@(objc_type=Number, objc_class_name="numberWithUint") Number_numberWithUint :: proc(value: uint) -> ^Number { return msgSend(^Number, Number, "numberWithUnsignedLong:", value) } +@(objc_type=Number, objc_class_name="numberWithU64") Number_numberWithU64 :: proc(value: u64) -> ^Number { return msgSend(^Number, Number, "numberWithLongLong:", value) } +@(objc_type=Number, objc_class_name="numberWithI64") Number_numberWithI64 :: proc(value: i64) -> ^Number { return msgSend(^Number, Number, "numberWithUnsignedLongLong:", value) } +@(objc_type=Number, objc_class_name="numberWithF32") Number_numberWithF32 :: proc(value: f32) -> ^Number { return msgSend(^Number, Number, "numberWithFloat:", value) } +@(objc_type=Number, objc_class_name="numberWithF64") Number_numberWithF64 :: proc(value: f64) -> ^Number { return msgSend(^Number, Number, "numberWithDouble:", value) } +@(objc_type=Number, objc_class_name="numberWithBool") Number_numberWithBool :: proc(value: BOOL) -> ^Number { return msgSend(^Number, Number, "numberWithBool:", value) } Number_number :: proc{ Number_numberWithI8, @@ -76,62 +104,49 @@ Number_number :: proc{ Number_numberWithBool, } -Number_initWithI8 :: proc(self: ^Number, value: i8) -> ^Number { return msgSend(^Number, self, "initWithChar:", value) } -Number_initWithU8 :: proc(self: ^Number, value: u8) -> ^Number { return msgSend(^Number, self, "initWithUnsignedChar:", value) } -Number_initWithI16 :: proc(self: ^Number, value: i16) -> ^Number { return msgSend(^Number, self, "initWithShort:", value) } -Number_initWithU16 :: proc(self: ^Number, value: u16) -> ^Number { return msgSend(^Number, self, "initWithUnsignedShort:", value) } -Number_initWithI32 :: proc(self: ^Number, value: i32) -> ^Number { return msgSend(^Number, self, "initWithInt:", value) } -Number_initWithU32 :: proc(self: ^Number, value: u32) -> ^Number { return msgSend(^Number, self, "initWithUnsignedInt:", value) } -Number_initWithInt :: proc(self: ^Number, value: int) -> ^Number { return msgSend(^Number, self, "initWithLong:", value) } -Number_initWithUint :: proc(self: ^Number, value: uint) -> ^Number { return msgSend(^Number, self, "initWithUnsignedLong:", value) } -Number_initWithU64 :: proc(self: ^Number, value: u64) -> ^Number { return msgSend(^Number, self, "initWithLongLong:", value) } -Number_initWithI64 :: proc(self: ^Number, value: i64) -> ^Number { return msgSend(^Number, self, "initWithUnsignedLongLong:", value) } -Number_initWithF32 :: proc(self: ^Number, value: f32) -> ^Number { return msgSend(^Number, self, "initWithFloat:", value) } -Number_initWithF64 :: proc(self: ^Number, value: f64) -> ^Number { return msgSend(^Number, self, "initWithDouble:", value) } -Number_initWithBool :: proc(self: ^Number, value: BOOL) -> ^Number { return msgSend(^Number, self, "initWithBool:", value) } - - -Number_init :: proc{ - Number_initWithI8, - Number_initWithU8, - Number_initWithI16, - Number_initWithU16, - Number_initWithI32, - Number_initWithU32, - Number_initWithInt, - Number_initWithUint, - Number_initWithU64, - Number_initWithI64, - Number_initWithF32, - Number_initWithF64, - Number_initWithBool, -} - -Number_i8Value :: proc(self: ^Number) -> i8 { return msgSend(i8, self, "charValue") } -Number_u8Value :: proc(self: ^Number) -> u8 { return msgSend(u8, self, "unsignedCharValue") } -Number_i16Value :: proc(self: ^Number) -> i16 { return msgSend(i16, self, "shortValue") } -Number_u16Value :: proc(self: ^Number) -> u16 { return msgSend(u16, self, "unsignedShortValue") } -Number_i32Value :: proc(self: ^Number) -> i32 { return msgSend(i32, self, "intValue") } -Number_u32Value :: proc(self: ^Number) -> u32 { return msgSend(u32, self, "unsignedIntValue") } -Number_intValue :: proc(self: ^Number) -> int { return msgSend(int, self, "longValue") } -Number_uintValue :: proc(self: ^Number) -> uint { return msgSend(uint, self, "unsignedLongValue") } -Number_u64Value :: proc(self: ^Number) -> u64 { return msgSend(u64, self, "longLongValue") } -Number_i64Value :: proc(self: ^Number) -> i64 { return msgSend(i64, self, "unsignedLongLongValue") } -Number_f32Value :: proc(self: ^Number) -> f32 { return msgSend(f32, self, "floatValue") } -Number_f64Value :: proc(self: ^Number) -> f64 { return msgSend(f64, self, "doubleValue") } -Number_boolValue :: proc(self: ^Number) -> BOOL { return msgSend(BOOL, self, "boolValue") } -Number_integerValue :: proc(self: ^Number) -> Integer { return msgSend(Integer, self, "integerValue") } -Number_uintegerValue :: proc(self: ^Number) -> UInteger { return msgSend(UInteger, self, "unsignedIntegerValue") } -Number_stringValue :: proc(self: ^Number) -> ^String { return msgSend(^String, self, "stringValue") } - -Number_compare :: proc(a, b: ^Number) -> ComparisonResult { - return msgSend(ComparisonResult, a, "compare:", b) +@(objc_type=Number, objc_name="initWithI8") Number_initWithI8 :: proc(self: ^Number, value: i8) -> ^Number { return msgSend(^Number, self, "initWithChar:", value) } +@(objc_type=Number, objc_name="initWithU8") Number_initWithU8 :: proc(self: ^Number, value: u8) -> ^Number { return msgSend(^Number, self, "initWithUnsignedChar:", value) } +@(objc_type=Number, objc_name="initWithI16") Number_initWithI16 :: proc(self: ^Number, value: i16) -> ^Number { return msgSend(^Number, self, "initWithShort:", value) } +@(objc_type=Number, objc_name="initWithU16") Number_initWithU16 :: proc(self: ^Number, value: u16) -> ^Number { return msgSend(^Number, self, "initWithUnsignedShort:", value) } +@(objc_type=Number, objc_name="initWithI32") Number_initWithI32 :: proc(self: ^Number, value: i32) -> ^Number { return msgSend(^Number, self, "initWithInt:", value) } +@(objc_type=Number, objc_name="initWithU32") Number_initWithU32 :: proc(self: ^Number, value: u32) -> ^Number { return msgSend(^Number, self, "initWithUnsignedInt:", value) } +@(objc_type=Number, objc_name="initWithInt") Number_initWithInt :: proc(self: ^Number, value: int) -> ^Number { return msgSend(^Number, self, "initWithLong:", value) } +@(objc_type=Number, objc_name="initWithUint") Number_initWithUint :: proc(self: ^Number, value: uint) -> ^Number { return msgSend(^Number, self, "initWithUnsignedLong:", value) } +@(objc_type=Number, objc_name="initWithU64") Number_initWithU64 :: proc(self: ^Number, value: u64) -> ^Number { return msgSend(^Number, self, "initWithLongLong:", value) } +@(objc_type=Number, objc_name="initWithI64") Number_initWithI64 :: proc(self: ^Number, value: i64) -> ^Number { return msgSend(^Number, self, "initWithUnsignedLongLong:", value) } +@(objc_type=Number, objc_name="initWithF32") Number_initWithF32 :: proc(self: ^Number, value: f32) -> ^Number { return msgSend(^Number, self, "initWithFloat:", value) } +@(objc_type=Number, objc_name="initWithF64") Number_initWithF64 :: proc(self: ^Number, value: f64) -> ^Number { return msgSend(^Number, self, "initWithDouble:", value) } +@(objc_type=Number, objc_name="initWithBool") Number_initWithBool :: proc(self: ^Number, value: BOOL) -> ^Number { return msgSend(^Number, self, "initWithBool:", value) } + + +@(objc_type=Number, objc_name="i8Value") Number_i8Value :: proc(self: ^Number) -> i8 { return msgSend(i8, self, "charValue") } +@(objc_type=Number, objc_name="u8Value") Number_u8Value :: proc(self: ^Number) -> u8 { return msgSend(u8, self, "unsignedCharValue") } +@(objc_type=Number, objc_name="i16Value") Number_i16Value :: proc(self: ^Number) -> i16 { return msgSend(i16, self, "shortValue") } +@(objc_type=Number, objc_name="u16Value") Number_u16Value :: proc(self: ^Number) -> u16 { return msgSend(u16, self, "unsignedShortValue") } +@(objc_type=Number, objc_name="i32Value") Number_i32Value :: proc(self: ^Number) -> i32 { return msgSend(i32, self, "intValue") } +@(objc_type=Number, objc_name="u32Value") Number_u32Value :: proc(self: ^Number) -> u32 { return msgSend(u32, self, "unsignedIntValue") } +@(objc_type=Number, objc_name="intValue") Number_intValue :: proc(self: ^Number) -> int { return msgSend(int, self, "longValue") } +@(objc_type=Number, objc_name="uintValue") Number_uintValue :: proc(self: ^Number) -> uint { return msgSend(uint, self, "unsignedLongValue") } +@(objc_type=Number, objc_name="u64Value") Number_u64Value :: proc(self: ^Number) -> u64 { return msgSend(u64, self, "longLongValue") } +@(objc_type=Number, objc_name="i64Value") Number_i64Value :: proc(self: ^Number) -> i64 { return msgSend(i64, self, "unsignedLongLongValue") } +@(objc_type=Number, objc_name="f32Value") Number_f32Value :: proc(self: ^Number) -> f32 { return msgSend(f32, self, "floatValue") } +@(objc_type=Number, objc_name="f64Value") Number_f64Value :: proc(self: ^Number) -> f64 { return msgSend(f64, self, "doubleValue") } +@(objc_type=Number, objc_name="boolValue") Number_boolValue :: proc(self: ^Number) -> BOOL { return msgSend(BOOL, self, "boolValue") } +@(objc_type=Number, objc_name="integerValue") Number_integerValue :: proc(self: ^Number) -> Integer { return msgSend(Integer, self, "integerValue") } +@(objc_type=Number, objc_name="uintegerValue") Number_uintegerValue :: proc(self: ^Number) -> UInteger { return msgSend(UInteger, self, "unsignedIntegerValue") } +@(objc_type=Number, objc_name="stringValue") Number_stringValue :: proc(self: ^Number) -> ^String { return msgSend(^String, self, "stringValue") } + +@(objc_type=Number, objc_name="compare") +Number_compare :: proc(self, other: ^Number) -> ComparisonResult { + return msgSend(ComparisonResult, self, "compare:", other) } -Number_isEqualToNumber :: proc(a, b: ^Number) -> BOOL { - return msgSend(BOOL, a, "isEqualToNumber:", b) +@(objc_type=Number, objc_name="isEqualToNumber") +Number_isEqualToNumber :: proc(self, other: ^Number) -> BOOL { + return msgSend(BOOL, self, "isEqualToNumber:", other) } +@(objc_type=Number, objc_name="descriptionWithLocale") Number_descriptionWithLocale :: proc(self: ^Number, locale: ^Object) -> ^String { return msgSend(^String, self, "descriptionWithLocale:", locale) } \ No newline at end of file diff --git a/core/sys/darwin/Foundation/NSObject.odin b/core/sys/darwin/Foundation/NSObject.odin index d61f8e947..cd0919edd 100644 --- a/core/sys/darwin/Foundation/NSObject.odin +++ b/core/sys/darwin/Foundation/NSObject.odin @@ -27,37 +27,45 @@ alloc :: proc($T: typeid) -> ^T where intrinsics.type_is_subtype_of(T, Object) { init :: proc(self: ^$T) -> ^T where intrinsics.type_is_subtype_of(T, Object) { return msgSend(^T, self, "init") } -retain :: proc(self: ^$T) -> ^T where intrinsics.type_is_subtype_of(T, Object) { - return msgSend(^T, self, "retain") +copy :: proc(self: ^Copying($T)) -> ^T where intrinsics.type_is_subtype_of(T, Object) { + return msgSend(^T, self, "copy") +} + + +@(objc_type=Object, objc_name="retain") +retain :: proc(self: ^Object) { + _ = msgSend(^Object, self, "retain") } -release :: proc(self: ^$T) where intrinsics.type_is_subtype_of(T, Object) { +@(objc_type=Object, objc_name="release") +release :: proc(self: ^Object) { msgSend(nil, self, "release") } -autorelease :: proc(self: ^$T) where intrinsics.type_is_subtype_of(T, Object) { +@(objc_type=Object, objc_name="autorelease") +autorelease :: proc(self: ^Object) { msgSend(nil, self, "autorelease") } -retainCount :: proc(self: ^$T) -> UInteger where intrinsics.type_is_subtype_of(T, Object) { +@(objc_type=Object, objc_name="retainCount") +retainCount :: proc(self: ^Object) -> UInteger { return msgSend(UInteger, self, "retainCount") } -copy :: proc(self: ^Copying($T)) -> ^T where intrinsics.type_is_subtype_of(T, Object) { - return msgSend(^T, self, "copy") -} - - +@(objc_type=Object, objc_name="hash") hash :: proc(self: ^Object) -> UInteger { return msgSend(UInteger, self, "hash") } +@(objc_type=Object, objc_name="isEqual") isEqual :: proc(self, pObject: ^Object) -> BOOL { return msgSend(BOOL, self, "isEqual:", pObject) } +@(objc_type=Object, objc_name="description") description :: proc(self: ^Object) -> ^String { return msgSend(^String, self, "description") } +@(objc_type=Object, objc_name="debugDescription") debugDescription :: proc(self: ^Object) -> ^String { if msgSendSafeCheck(self, intrinsics.objc_selector_name("debugDescription")) { return msgSend(^String, self, "debugDescription") diff --git a/core/sys/darwin/Foundation/NSString.odin b/core/sys/darwin/Foundation/NSString.odin index 86c8cabea..06dbc27a3 100644 --- a/core/sys/darwin/Foundation/NSString.odin +++ b/core/sys/darwin/Foundation/NSString.odin @@ -59,54 +59,78 @@ MakeConstantString :: proc "c" (#const c: cstring) -> ^String { } +@(objc_type=String, objc_class_name="alloc") +String_alloc :: proc() -> ^String { + return msgSend(^String, String, "alloc") +} + +@(objc_type=String, objc_name="init") +String_init :: proc(self: ^String) -> ^String { + return msgSend(^String, self, "init") +} + + +@(objc_type=String, objc_name="initWithString") String_initWithString :: proc(self: ^String, other: ^String) -> ^String { return msgSend(^String, self, "initWithString:", other) } +@(objc_type=String, objc_name="initWithCString") String_initWithCString :: proc(self: ^String, pString: cstring, encoding: StringEncoding) -> ^String { return msgSend(^String, self, "initWithCstring:encoding:", pString, encoding) } +@(objc_type=String, objc_name="initWithBytesNoCopy") String_initWithBytesNoCopy :: proc(self: ^String, pBytes: rawptr, length: UInteger, encoding: StringEncoding, freeWhenDone: bool) -> ^String { return msgSend(^String, self, "initWithBytesNoCopy:length:encoding:freeWhenDone:", pBytes, length, encoding, freeWhenDone) } +@(objc_type=String, objc_name="initWithOdinString") String_initWithOdinString :: proc(self: ^String, str: string) -> ^String { return String_initWithBytesNoCopy(self, raw_data(str), UInteger(len(str)), .UTF8, false) } +@(objc_type=String, objc_name="characterAtIndex") String_characterAtIndex :: proc(self: ^String, index: UInteger) -> unichar { return msgSend(unichar, self, "characterAtIndex:", index) } +@(objc_type=String, objc_name="length") String_length :: proc(self: ^String) -> UInteger { return msgSend(UInteger, self, "length") } +@(objc_type=String, objc_name="cStringUsingEncoding") String_cStringUsingEncoding :: proc(self: ^String, encoding: StringEncoding) -> cstring { return msgSend(cstring, self, "cStringUsingEncoding:", encoding) } +@(objc_type=String, objc_name="UTF8String") String_UTF8String :: proc(self: ^String) -> cstring { return msgSend(cstring, self, "UTF8String") } +@(objc_type=String, objc_name="OdinString") String_OdinString :: proc(self: ^String) -> string { return string(String_UTF8String(self)) } +@(objc_type=String, objc_name="maximumLengthOfBytesUsingEncoding") String_maximumLengthOfBytesUsingEncoding :: proc(self: ^String, encoding: StringEncoding) -> UInteger { return msgSend(UInteger, self, "maximumLengthOfBytesUsingEncoding:", encoding) } +@(objc_type=String, objc_name="lengthOfBytesUsingEncoding") String_lengthOfBytesUsingEncoding :: proc(self: ^String, encoding: StringEncoding) -> UInteger { return msgSend(UInteger, self, "lengthOfBytesUsingEncoding:", encoding) } +@(objc_type=String, objc_name="isEqualToString") String_isEqualToString :: proc(self, other: ^String) -> BOOL { return msgSend(BOOL, self, "isEqualToString:", other) } +@(objc_type=String, objc_name="rangeOfString") String_rangeOfString :: proc(self, other: ^String, options: StringCompareOptions) -> Range { return msgSend(Range, self, "rangeOfString:options:", other, options) } \ No newline at end of file diff --git a/core/sys/darwin/Foundation/NSURL.odin b/core/sys/darwin/Foundation/NSURL.odin index f4f776130..42edf91c0 100644 --- a/core/sys/darwin/Foundation/NSURL.odin +++ b/core/sys/darwin/Foundation/NSURL.odin @@ -3,6 +3,18 @@ package objc_Foundation @(objc_class="NSURL") URL :: struct{using _: Copying(URL)} + +@(objc_type=URL, objc_class_name="alloc") +URL_alloc :: proc() -> ^URL { + return msgSend(^URL, URL, "alloc") +} + +@(objc_type=URL, objc_name="init") +URL_init :: proc(self: ^URL) -> ^URL { + return msgSend(^URL, self, "init") +} + + URL_initWithString :: proc(self: ^URL, value: ^String) -> ^URL { return msgSend(^URL, self, "initWithString:", value) } diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index df22d82e2..a9ee5d25f 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -287,15 +287,13 @@ bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call Operand self = {}; check_expr_or_type(c, &self, ce->args[1]); if (self.mode == Addressing_Type) { - if (!internal_check_is_assignable_to(self.type, t_objc_object)) { + if (!is_type_objc_object(self.type)) { gbString t = type_to_string(self.type); error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got type %s", LIT(builtin_name), t); gb_string_free(t); return false; } - if (!(self.type->kind == Type_Named && - self.type->Named.type_name != nullptr && - self.type->Named.type_name->TypeName.objc_class_name != "")) { + if (!has_type_got_objc_class_attribute(self.type)) { gbString t = type_to_string(self.type); error(self.expr, "'%.*s' expected a named type with the attribute @(obj_class=) , got type %s", LIT(builtin_name), t); gb_string_free(t); @@ -306,7 +304,7 @@ bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call } else if (!is_operand_value(self) || !check_is_assignable_to(c, &self, t_objc_id)) { gbString e = expr_to_string(self.expr); gbString t = type_to_string(self.type); - error(self.expr, "'%.*s'3 expected a type or value derived from intrinsics.objc_object, got '%s' of type %s %d", LIT(builtin_name), e, t, self.type->kind); + error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got '%s' of type %s %d", LIT(builtin_name), e, t, self.type->kind); gb_string_free(t); gb_string_free(e); return false; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 243dbbbc6..1d30088d6 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -340,6 +340,10 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def) check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac); if (e->kind == Entity_TypeName && ac.objc_class != "") { e->TypeName.objc_class_name = ac.objc_class; + + if (type_size_of(e->type) > 0) { + error(e->token, "@(objc_class) marked type must be of zero size"); + } } } @@ -822,6 +826,65 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } e->Procedure.optimization_mode = cast(ProcedureOptimizationMode)ac.optimization_mode; + if (ac.objc_name.len || ac.objc_class_name.len || ac.objc_type) { + if (ac.objc_class_name.len && ac.objc_name.len) { + error(e->token, "@(objc_class_name) and @(objc_name) may not be allowed at the same time"); + } else if (ac.objc_type == nullptr) { + if (ac.objc_name.len) { + error(e->token, "@(objc_name) requires that @(objc_type) to be set"); + } else { + error(e->token, "@(objc_class_name) requires that @(objc_type) to be set"); + } + } else { + Type *t = ac.objc_type; + if (t->kind == Type_Named) { + Entity *tn = t->Named.type_name; + + GB_ASSERT(tn->kind == Entity_TypeName); + + if (tn->scope != e->scope) { + error(e->token, "@(objc_name) and @(objc_class_name) attributes may only be applied to procedures and types within the same scope"); + } else { + mutex_lock(&global_type_name_objc_metadata_mutex); + defer (mutex_unlock(&global_type_name_objc_metadata_mutex)); + + if (!tn->TypeName.objc_metadata) { + tn->TypeName.objc_metadata = create_type_name_obj_c_metadata(); + } + auto *md = tn->TypeName.objc_metadata; + mutex_lock(md->mutex); + defer (mutex_unlock(md->mutex)); + + if (ac.objc_name.len) { + bool ok = true; + for (TypeNameObjCMetadataEntry const &entry : md->value_entries) { + if (entry.name == ac.objc_name) { + error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name)); + ok = false; + break; + } + } + if (ok) { + array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e}); + } + } else { + bool ok = true; + for (TypeNameObjCMetadataEntry const &entry : md->type_entries) { + if (entry.name == ac.objc_class_name) { + error(e->token, "Previous declaration of @(objc_class_name=\"%.*s\")", LIT(ac.objc_class_name)); + ok = false; + break; + } + } + if (ok) { + array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_class_name, e}); + } + } + } + } + } + } + switch (e->Procedure.optimization_mode) { case ProcedureOptimizationMode_None: diff --git a/src/check_type.cpp b/src/check_type.cpp index e1a0df7e6..32340070e 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -325,6 +325,8 @@ void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, Type *named_t named_type->Named.type_name = e; GB_ASSERT(original_type->kind == Type_Named); e->TypeName.objc_class_name = original_type->Named.type_name->TypeName.objc_class_name; + // TODO(bill): Is this even correct? Or should the metadata be copied? + e->TypeName.objc_metadata = original_type->Named.type_name->TypeName.objc_metadata; mutex_lock(&ctx->info->gen_types_mutex); auto *found_gen_types = map_get(&ctx->info->gen_types, original_type); diff --git a/src/checker.cpp b/src/checker.cpp index 2ab487592..0dd36987e 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -4,7 +4,7 @@ void check_expr(CheckerContext *c, Operand *operand, Ast *expression); void check_expr_or_type(CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint=nullptr); void add_comparison_procedures_for_fields(CheckerContext *c, Type *t); - +Type *check_type(CheckerContext *ctx, Ast *e); bool is_operand_value(Operand o) { switch (o.mode) { @@ -2740,6 +2740,14 @@ ExactValue check_decl_attribute_value(CheckerContext *c, Ast *value) { return ev; } +Type *check_decl_attribute_type(CheckerContext *c, Ast *value) { + if (value != nullptr) { + return check_type(c, value); + } + return nullptr; +} + + #define ATTRIBUTE_USER_TAG_NAME "tag" @@ -3039,6 +3047,46 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) { error(elem, "Expected a string for '%.*s'", LIT(name)); } return true; + } else if (name == "objc_name") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_String) { + if (string_is_valid_identifier(ev.value_string)) { + ac->objc_name = ev.value_string; + } else { + error(elem, "Invalid identifier for '%.*s', got '%.*s'", LIT(name), LIT(ev.value_string)); + } + } else { + error(elem, "Expected a string value for '%.*s'", LIT(name)); + } + return true; + } else if (name == "objc_class_name") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_String) { + if (string_is_valid_identifier(ev.value_string)) { + ac->objc_class_name = ev.value_string; + } else { + error(elem, "Invalid identifier for '%.*s', got '%.*s'", LIT(name), LIT(ev.value_string)); + } + } else { + error(elem, "Expected a string value for '%.*s'", LIT(name)); + } + return true; + } else if (name == "objc_type") { + if (value == nullptr) { + error(elem, "Expected a type for '%.*s'", LIT(name)); + } else { + Type *objc_type = check_type(c, value); + if (objc_type != nullptr) { + if (!has_type_got_objc_class_attribute(objc_type)) { + gbString t = type_to_string(objc_type); + error(value, "'%.*s' expected a named type with the attribute @(obj_class=), got type %s", LIT(name), t); + gb_string_free(t); + } else { + ac->objc_type = objc_type; + } + } + } + return true; } return false; } diff --git a/src/checker.hpp b/src/checker.hpp index b812e10a4..38c8d32f6 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -107,7 +107,6 @@ struct AttributeContext { String thread_local_model; String deprecated_message; String warning_message; - String objc_class; DeferredProcedure deferred_procedure; bool is_export : 1; bool is_static : 1; @@ -119,6 +118,11 @@ struct AttributeContext { bool init : 1; bool set_cold : 1; u32 optimization_mode; // ProcedureOptimizationMode + + String objc_class; + String objc_name; + String objc_class_name; + Type * objc_type; }; AttributeContext make_attribute_context(String link_prefix) { diff --git a/src/entity.cpp b/src/entity.cpp index 4d5b3b9e1..df8ee3faa 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -122,6 +122,28 @@ enum ProcedureOptimizationMode : u32 { ProcedureOptimizationMode_Speed, }; + +BlockingMutex global_type_name_objc_metadata_mutex; + +struct TypeNameObjCMetadataEntry { + String name; + Entity *entity; +}; +struct TypeNameObjCMetadata { + BlockingMutex *mutex; + Array type_entries; + Array value_entries; +}; + +TypeNameObjCMetadata *create_type_name_obj_c_metadata() { + TypeNameObjCMetadata *md = gb_alloc_item(permanent_allocator(), TypeNameObjCMetadata); + md->mutex = gb_alloc_item(permanent_allocator(), BlockingMutex); + mutex_init(md->mutex); + array_init(&md->type_entries, heap_allocator()); + array_init(&md->value_entries, heap_allocator()); + return md; +} + // An Entity is a named "thing" in the language struct Entity { EntityKind kind; @@ -187,6 +209,7 @@ struct Entity { String ir_mangled_name; bool is_type_alias; String objc_class_name; + TypeNameObjCMetadata *objc_metadata; } TypeName; struct { u64 tags; diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 29a86d116..b2f430cd2 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -3320,7 +3320,12 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { Type *type = base_type(tav.type); if (tav.mode == Addressing_Type) { // Addressing_Type - GB_PANIC("Unreachable"); + Selection sel = lookup_field(tav.type, selector, true); + if (sel.pseudo_field) { + GB_ASSERT(sel.entity->kind == Entity_Procedure); + return lb_addr(lb_find_value_from_entity(p->module, sel.entity)); + } + GB_PANIC("Unreachable %.*s", LIT(selector)); } if (se->swizzle_count > 0) { @@ -3347,6 +3352,10 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { Selection sel = lookup_field(type, selector, false); GB_ASSERT(sel.entity != nullptr); + if (sel.pseudo_field) { + GB_ASSERT(sel.entity->kind == Entity_Procedure); + return lb_addr(lb_find_value_from_entity(p->module, sel.entity)); + } { lbAddr addr = lb_build_addr(p, se->expr); diff --git a/src/main.cpp b/src/main.cpp index fe56d451f..bd06193bd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -585,37 +585,6 @@ void usage(String argv0) { print_usage_line(1, "e.g. odin build -help"); } - -bool string_is_valid_identifier(String str) { - if (str.len <= 0) return false; - - isize rune_count = 0; - - isize w = 0; - isize offset = 0; - while (offset < str.len) { - Rune r = 0; - w = utf8_decode(str.text, str.len, &r); - if (r == GB_RUNE_INVALID) { - return false; - } - - if (rune_count == 0) { - if (!rune_is_letter(r)) { - return false; - } - } else { - if (!rune_is_letter(r) && !rune_is_digit(r)) { - return false; - } - } - rune_count += 1; - offset += w; - } - - return true; -} - enum BuildFlagKind { BuildFlag_Invalid, @@ -2447,6 +2416,7 @@ int main(int arg_count, char const **arg_ptr) { virtual_memory_init(); mutex_init(&fullpath_mutex); mutex_init(&hash_exact_value_mutex); + mutex_init(&global_type_name_objc_metadata_mutex); init_string_buffer_memory(); init_string_interner(); diff --git a/src/string.cpp b/src/string.cpp index eb6058f78..bcaf23b9b 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -781,3 +781,34 @@ i32 unquote_string(gbAllocator a, String *s_, u8 quote=0, bool has_carriage_retu return 2; } + + +bool string_is_valid_identifier(String str) { + if (str.len <= 0) return false; + + isize rune_count = 0; + + isize w = 0; + isize offset = 0; + while (offset < str.len) { + Rune r = 0; + w = utf8_decode(str.text, str.len, &r); + if (r == GB_RUNE_INVALID) { + return false; + } + + if (rune_count == 0) { + if (!rune_is_letter(r)) { + return false; + } + } else { + if (!rune_is_letter(r) && !rune_is_digit(r)) { + return false; + } + } + rune_count += 1; + offset += w; + } + + return true; +} diff --git a/src/types.cpp b/src/types.cpp index 024d644a2..78958146b 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -393,6 +393,7 @@ struct Selection { bool indirect; // Set if there was a pointer deref anywhere down the line u8 swizzle_count; // maximum components = 4 u8 swizzle_indices; // 2 bits per component, representing which swizzle index + bool pseudo_field; }; Selection empty_selection = {0}; @@ -2782,6 +2783,7 @@ Selection lookup_field_from_index(Type *type, i64 index) { } Entity *scope_lookup_current(Scope *s, String const &name); +bool has_type_got_objc_class_attribute(Type *t); Selection lookup_field_with_selection(Type *type_, String field_name, bool is_type, Selection sel, bool allow_blank_ident) { GB_ASSERT(type_ != nullptr); @@ -2794,9 +2796,40 @@ Selection lookup_field_with_selection(Type *type_, String field_name, bool is_ty bool is_ptr = type != type_; sel.indirect = sel.indirect || is_ptr; + Type *original_type = type; + type = base_type(type); if (is_type) { + if (has_type_got_objc_class_attribute(original_type) && original_type->kind == Type_Named) { + Entity *e = original_type->Named.type_name; + GB_ASSERT(e->kind == Entity_TypeName); + if (e->TypeName.objc_metadata) { + auto *md = e->TypeName.objc_metadata; + mutex_lock(md->mutex); + defer (mutex_unlock(md->mutex)); + for (TypeNameObjCMetadataEntry const &entry : md->type_entries) { + GB_ASSERT(entry.entity->kind == Entity_Procedure); + if (entry.name == field_name) { + sel.entity = entry.entity; + sel.pseudo_field = true; + return sel; + } + } + } + if (type->kind == Type_Struct) { + for_array(i, type->Struct.fields) { + Entity *f = type->Struct.fields[i]; + if (f->flags&EntityFlag_Using) { + sel = lookup_field_with_selection(f->type, field_name, is_type, sel, allow_blank_ident); + if (sel.entity) { + return sel; + } + } + } + } + } + if (is_type_enum(type)) { // NOTE(bill): These may not have been added yet, so check in case for_array(i, type->Enum.fields) { @@ -2843,6 +2876,24 @@ Selection lookup_field_with_selection(Type *type_, String field_name, bool is_ty } else if (type->kind == Type_Union) { } else if (type->kind == Type_Struct) { + if (has_type_got_objc_class_attribute(original_type) && original_type->kind == Type_Named) { + Entity *e = original_type->Named.type_name; + GB_ASSERT(e->kind == Entity_TypeName); + if (e->TypeName.objc_metadata) { + auto *md = e->TypeName.objc_metadata; + mutex_lock(md->mutex); + defer (mutex_unlock(md->mutex)); + for (TypeNameObjCMetadataEntry const &entry : md->value_entries) { + GB_ASSERT(entry.entity->kind == Entity_Procedure); + if (entry.name == field_name) { + sel.entity = entry.entity; + sel.pseudo_field = true; + return sel; + } + } + } + } + for_array(i, type->Struct.fields) { Entity *f = type->Struct.fields[i]; if (f->kind != Entity_Variable || (f->flags & EntityFlag_Field) == 0) { @@ -3792,6 +3843,17 @@ bool is_type_subtype_of(Type *src, Type *dst) { } +bool has_type_got_objc_class_attribute(Type *t) { + return t->kind == Type_Named && t->Named.type_name != nullptr && t->Named.type_name->TypeName.objc_class_name != ""; +} + + + +bool is_type_objc_object(Type *t) { + bool internal_check_is_assignable_to(Type *src, Type *dst); + + return internal_check_is_assignable_to(t, t_objc_object); +} Type *get_struct_field_type(Type *t, isize index) { t = base_type(type_deref(t)); -- cgit v1.2.3 From 65dedbb1caaa785a444d32a7a15adaf6c396b07f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 16 Feb 2022 11:54:15 +0000 Subject: Add `#subtype` struct field prefix, required to have a COM interface hierarchy --- src/check_expr.cpp | 3 +++ src/check_type.cpp | 18 ++++++++++++++++++ src/entity.cpp | 5 +++++ src/parser.cpp | 4 +++- src/parser.hpp | 3 ++- src/types.cpp | 12 ++++++------ 6 files changed, 37 insertions(+), 8 deletions(-) (limited to 'src/types.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 7fb0e44f2..884f1bb9f 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -9774,6 +9774,9 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) { if (f->flags&FieldFlag_const) { str = gb_string_appendc(str, "#const "); } + if (f->flags&FieldFlag_subtype) { + str = gb_string_appendc(str, "#subtype "); + } for_array(i, f->names) { Ast *name = f->names[i]; diff --git a/src/check_type.cpp b/src/check_type.cpp index 32340070e..7e0ad2bd9 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -144,6 +144,7 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice *fields } bool is_using = (p->flags&FieldFlag_using) != 0; + bool is_subtype = (p->flags&FieldFlag_subtype) != 0; for_array(j, p->names) { Ast *name = p->names[j]; @@ -158,6 +159,9 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice *fields Entity *field = alloc_entity_field(ctx->scope, name_token, type, is_using, field_src_index); add_entity(ctx, ctx->scope, name, field); field->Variable.field_group_index = field_group_index; + if (is_subtype) { + field->flags |= EntityFlag_Subtype; + } if (j == 0) { field->Variable.docs = docs; @@ -194,6 +198,20 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice *fields populate_using_entity_scope(ctx, node, p, type); } + + if (is_subtype && p->names.count > 0) { + Type *first_type = fields_array[fields_array.count-1]->type; + Type *t = base_type(type_deref(first_type)); + + if (!does_field_type_allow_using(t) && + p->names.count >= 1 && + p->names[0]->kind == Ast_Ident) { + Token name_token = p->names[0]->Ident.token; + gbString type_str = type_to_string(first_type); + error(name_token, "'subtype' cannot be applied to the field '%.*s' of type '%s'", LIT(name_token.string), type_str); + gb_string_free(type_str); + } + } } *fields = slice_from_array(fields_array); diff --git a/src/entity.cpp b/src/entity.cpp index df8ee3faa..f5720293f 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -74,6 +74,7 @@ enum EntityFlag : u64 { EntityFlag_Test = 1ull<<30, EntityFlag_Init = 1ull<<31, + EntityFlag_Subtype = 1ull<<32, EntityFlag_CustomLinkName = 1ull<<40, EntityFlag_CustomLinkage_Internal = 1ull<<41, @@ -86,6 +87,10 @@ enum EntityFlag : u64 { EntityFlag_Overridden = 1ull<<63, }; +enum : u64 { + EntityFlags_IsSubtype = EntityFlag_Using|EntityFlag_Subtype, +}; + enum EntityState : u32 { EntityState_Unresolved = 0, EntityState_InProgress = 1, diff --git a/src/parser.cpp b/src/parser.cpp index 0914c77ca..b55d745f1 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3504,12 +3504,13 @@ enum FieldPrefixKind : i32 { FieldPrefix_Unknown = -1, FieldPrefix_Invalid = 0, - FieldPrefix_using, + FieldPrefix_using, // implies #subtype FieldPrefix_const, FieldPrefix_no_alias, FieldPrefix_c_vararg, FieldPrefix_auto_cast, FieldPrefix_any_int, + FieldPrefix_subtype, // does not imply `using` semantics }; struct ParseFieldPrefixMapping { @@ -3526,6 +3527,7 @@ gb_global ParseFieldPrefixMapping parse_field_prefix_mappings[] = { {str_lit("c_vararg"), Token_Hash, FieldPrefix_c_vararg, FieldFlag_c_vararg}, {str_lit("const"), Token_Hash, FieldPrefix_const, FieldFlag_const}, {str_lit("any_int"), Token_Hash, FieldPrefix_any_int, FieldFlag_any_int}, + {str_lit("subtype"), Token_Hash, FieldPrefix_subtype, FieldFlag_subtype}, }; diff --git a/src/parser.hpp b/src/parser.hpp index ff0df0382..fb84210b3 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -282,6 +282,7 @@ enum FieldFlag : u32 { FieldFlag_auto_cast = 1<<4, FieldFlag_const = 1<<5, FieldFlag_any_int = 1<<6, + FieldFlag_subtype = 1<<7, // Internal use by the parser only FieldFlag_Tags = 1<<10, @@ -289,7 +290,7 @@ enum FieldFlag : u32 { // Parameter List Restrictions FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_auto_cast|FieldFlag_const|FieldFlag_any_int, - FieldFlag_Struct = FieldFlag_using|FieldFlag_Tags, + FieldFlag_Struct = FieldFlag_using|FieldFlag_subtype|FieldFlag_Tags, }; enum StmtAllowFlag { diff --git a/src/types.cpp b/src/types.cpp index 78958146b..2c1e6162f 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2334,7 +2334,7 @@ String lookup_subtype_polymorphic_field(Type *dst, Type *src) { GB_ASSERT(is_type_struct(src) || is_type_union(src)); for_array(i, src->Struct.fields) { Entity *f = src->Struct.fields[i]; - if (f->kind == Entity_Variable && f->flags & EntityFlag_Using) { + if (f->kind == Entity_Variable && f->flags & EntityFlags_IsSubtype) { if (are_types_identical(dst, f->type)) { return f->token.string; } @@ -2343,7 +2343,7 @@ String lookup_subtype_polymorphic_field(Type *dst, Type *src) { return f->token.string; } } - if (is_type_struct(f->type)) { + if ((f->flags & EntityFlag_Using) != 0 && is_type_struct(f->type)) { String name = lookup_subtype_polymorphic_field(dst, f->type); if (name.len > 0) { return name; @@ -2489,9 +2489,9 @@ bool are_types_identical_internal(Type *x, Type *y, bool check_tuple_names) { if (xf->token.string != yf->token.string) { return false; } - bool xf_is_using = (xf->flags&EntityFlag_Using) != 0; - bool yf_is_using = (yf->flags&EntityFlag_Using) != 0; - if (xf_is_using ^ yf_is_using) { + u64 xf_flags = (xf->flags&EntityFlags_IsSubtype); + u64 yf_flags = (yf->flags&EntityFlags_IsSubtype); + if (xf_flags != yf_flags) { return false; } } @@ -3813,7 +3813,7 @@ isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0 for_array(i, src->Struct.fields) { Entity *f = src->Struct.fields[i]; - if (f->kind != Entity_Variable || (f->flags&EntityFlag_Using) == 0) { + if (f->kind != Entity_Variable || (f->flags&EntityFlags_IsSubtype) == 0) { continue; } -- cgit v1.2.3 From ad2f1ac24ebdb4937c5a469c3d3f48b8f53f6218 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 22 Feb 2022 23:01:28 +0000 Subject: Improve `union_tag_size` --- src/types.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src/types.cpp') diff --git a/src/types.cpp b/src/types.cpp index 2c1e6162f..74080334a 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2623,6 +2623,17 @@ i64 union_tag_size(Type *u) { // TODO(bill): Is this an okay approach? i64 max_align = 1; + + if (u->Union.variants.count < 1ull<<8) { + max_align = 1; + } else if (u->Union.variants.count < 1ull<<16) { + max_align = 2; + } else if (u->Union.variants.count < 1ull<<32) { + max_align = 4; + } else { + GB_PANIC("how many variants do you have?!"); + } + for_array(i, u->Union.variants) { Type *variant_type = u->Union.variants[i]; i64 align = type_align_of(variant_type); -- cgit v1.2.3 From 49fecbdc5e6579956346e484a418b9501dfddd71 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 1 Mar 2022 14:49:05 +0000 Subject: Improve error message when there is "no field" found for a large anonymous struct --- src/check_builtin.cpp | 8 ++++---- src/check_expr.cpp | 8 ++++---- src/types.cpp | 37 +++++++++++++++++++++++-------------- 3 files changed, 31 insertions(+), 22 deletions(-) (limited to 'src/types.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 69cc40de8..aeeeb9e4d 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1106,7 +1106,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 Selection sel = lookup_field(type, field_name, false); if (sel.entity == nullptr) { - gbString type_str = type_to_string(type); + gbString type_str = type_to_string_shorthand(type); error(ce->args[0], "'%s' has no field named '%.*s'", type_str, LIT(field_name)); gb_string_free(type_str); @@ -1118,7 +1118,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 return false; } if (sel.indirect) { - gbString type_str = type_to_string(type); + gbString type_str = type_to_string_shorthand(type); error(ce->args[0], "Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str); gb_string_free(type_str); @@ -1179,7 +1179,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 Selection sel = lookup_field(type, field_name, false); if (sel.entity == nullptr) { - gbString type_str = type_to_string(type); + gbString type_str = type_to_string_shorthand(type); error(ce->args[0], "'%s' has no field named '%.*s'", type_str, LIT(field_name)); gb_string_free(type_str); @@ -1191,7 +1191,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 return false; } if (sel.indirect) { - gbString type_str = type_to_string(type); + gbString type_str = type_to_string_shorthand(type); error(ce->args[0], "Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str); gb_string_free(type_str); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index e07dc3d60..614da2368 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4470,7 +4470,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ if (entity == nullptr) { gbString op_str = expr_to_string(op_expr); - gbString type_str = type_to_string(operand->type); + gbString type_str = type_to_string_shorthand(operand->type); gbString sel_str = expr_to_string(selector); error(op_expr, "'%s' of type '%s' has no field '%s'", op_str, type_str, sel_str); @@ -4511,7 +4511,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ } gbString op_str = expr_to_string(op_expr); - gbString type_str = type_to_string(operand->type); + gbString type_str = type_to_string_shorthand(operand->type); gbString sel_str = expr_to_string(selector); error(op_expr, "Cannot access non-constant field '%s' from '%s'", sel_str, op_str); gb_string_free(sel_str); @@ -4536,7 +4536,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ } gbString op_str = expr_to_string(op_expr); - gbString type_str = type_to_string(operand->type); + gbString type_str = type_to_string_shorthand(operand->type); gbString sel_str = expr_to_string(selector); error(op_expr, "Cannot access non-constant field '%s' from '%s'", sel_str, op_str); gb_string_free(sel_str); @@ -4549,7 +4549,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ if (expr_entity != nullptr && is_type_polymorphic(expr_entity->type)) { gbString op_str = expr_to_string(op_expr); - gbString type_str = type_to_string(operand->type); + gbString type_str = type_to_string_shorthand(operand->type); gbString sel_str = expr_to_string(selector); error(op_expr, "Cannot access field '%s' from non-specialized polymorphic type '%s'", sel_str, op_str); gb_string_free(sel_str); diff --git a/src/types.cpp b/src/types.cpp index 74080334a..58ccdf5b9 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -703,7 +703,7 @@ struct TypePath; i64 type_size_of (Type *t); i64 type_align_of (Type *t); i64 type_offset_of (Type *t, i32 index); -gbString type_to_string (Type *type); +gbString type_to_string (Type *type, bool shorthand=false); i64 type_size_of_internal(Type *t, TypePath *path); void init_map_internal_types(Type *type); Type * bit_set_to_int(Type *t); @@ -3936,7 +3936,7 @@ Type *alloc_type_proc_from_types(Type **param_types, unsigned param_count, Type -gbString write_type_to_string(gbString str, Type *type) { +gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) { if (type == nullptr) { return gb_string_appendc(str, ""); } @@ -4051,15 +4051,21 @@ gbString write_type_to_string(gbString str, Type *type) { if (type->Struct.is_raw_union) str = gb_string_appendc(str, " #raw_union"); if (type->Struct.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Struct.custom_align); str = gb_string_appendc(str, " {"); - for_array(i, type->Struct.fields) { - Entity *f = type->Struct.fields[i]; - GB_ASSERT(f->kind == Entity_Variable); - if (i > 0) { - str = gb_string_appendc(str, ", "); + + + if (shorthand && type->Struct.fields.count > 16) { + str = gb_string_append_fmt(str, "%lld fields...", cast(long long)type->Struct.fields.count); + } else { + for_array(i, type->Struct.fields) { + Entity *f = type->Struct.fields[i]; + GB_ASSERT(f->kind == Entity_Variable); + if (i > 0) { + str = gb_string_appendc(str, ", "); + } + str = gb_string_append_length(str, f->token.string.text, f->token.string.len); + str = gb_string_appendc(str, ": "); + str = write_type_to_string(str, f->type); } - str = gb_string_append_length(str, f->token.string.text, f->token.string.len); - str = gb_string_appendc(str, ": "); - str = write_type_to_string(str, f->type); } str = gb_string_append_rune(str, '}'); } break; @@ -4234,13 +4240,16 @@ gbString write_type_to_string(gbString str, Type *type) { } -gbString type_to_string(Type *type, gbAllocator allocator) { - return write_type_to_string(gb_string_make(allocator, ""), type); +gbString type_to_string(Type *type, gbAllocator allocator, bool shorthand=false) { + return write_type_to_string(gb_string_make(allocator, ""), type, shorthand); } -gbString type_to_string(Type *type) { - return write_type_to_string(gb_string_make(heap_allocator(), ""), type); +gbString type_to_string(Type *type, bool shorthand) { + return write_type_to_string(gb_string_make(heap_allocator(), ""), type, shorthand); } +gbString type_to_string_shorthand(Type *type) { + return type_to_string(type, true); +} -- cgit v1.2.3 From a68f0b2d72a3883879da124321868afcdac3b9d6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 18 Mar 2022 22:18:12 +0000 Subject: Improve procedure group selection based on the minimum number of arguments --- src/check_expr.cpp | 79 +++++++++++++++++++++++++++++++++++++++++++----------- src/check_type.cpp | 14 ---------- src/checker.cpp | 9 +++++++ src/entity.cpp | 10 +++++++ src/types.cpp | 1 - 5 files changed, 82 insertions(+), 31 deletions(-) (limited to 'src/types.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 577f3b07c..b7039fd9a 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4872,25 +4872,16 @@ bool is_expr_constant_zero(Ast *expr) { return false; } - -CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { - ast_node(ce, CallExpr, call); - GB_ASSERT(is_type_proc(proc_type)); - proc_type = base_type(proc_type); - TypeProc *pt = &proc_type->Proc; - +isize get_procedure_param_count_excluding_defaults(Type *pt, isize *param_count_) { + GB_ASSERT(pt != nullptr); + GB_ASSERT(pt->kind == Type_Proc); isize param_count = 0; isize param_count_excluding_defaults = 0; - bool variadic = pt->variadic; - bool vari_expand = (ce->ellipsis.pos.line != 0); - i64 score = 0; - bool show_error = show_error_mode == CallArgumentMode_ShowErrors; - - + bool variadic = pt->Proc.variadic; TypeTuple *param_tuple = nullptr; - if (pt->params != nullptr) { - param_tuple = &pt->params->Tuple; + if (pt->Proc.params != nullptr) { + param_tuple = &pt->Proc.params->Tuple; param_count = param_tuple->variables.count; if (variadic) { @@ -4930,6 +4921,31 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { } } + if (param_count_) *param_count_ = param_count; + return param_count_excluding_defaults; +} + + +CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { + ast_node(ce, CallExpr, call); + GB_ASSERT(is_type_proc(proc_type)); + proc_type = base_type(proc_type); + TypeProc *pt = &proc_type->Proc; + + isize param_count = 0; + isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(proc_type, ¶m_count); + bool variadic = pt->variadic; + bool vari_expand = (ce->ellipsis.pos.line != 0); + i64 score = 0; + bool show_error = show_error_mode == CallArgumentMode_ShowErrors; + + + TypeTuple *param_tuple = nullptr; + if (pt->params != nullptr) { + param_tuple = &pt->params->Tuple; + } + + CallArgumentError err = CallArgumentError_None; Type *final_proc_type = proc_type; Entity *gen_entity = nullptr; @@ -5602,7 +5618,37 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type if (operand->mode == Addressing_ProcGroup) { check_entity_decl(c, operand->proc_group, nullptr, nullptr); - Array procs = proc_group_entities(c, *operand); + auto procs = proc_group_entities_cloned(c, *operand); + + if (procs.count > 1) { + isize max_arg_count = args.count; + for_array(i, args) { + // NOTE(bill): The only thing that may have multiple values + // will be a call expression (assuming `or_return` and `()` will be stripped) + Ast *arg = strip_or_return_expr(args[i]); + if (arg && arg->kind == Ast_CallExpr) { + max_arg_count = ISIZE_MAX; + break; + } + } + + for (isize proc_index = 0; proc_index < procs.count; /**/) { + Entity *proc = procs[proc_index]; + Type *pt = base_type(proc->type); + if (!(pt != nullptr && is_type_proc(pt))) { + continue; + } + + isize param_count = 0; + isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(pt, ¶m_count); + + if (param_count_excluding_defaults > max_arg_count) { + array_unordered_remove(&procs, proc_index); + } else { + proc_index++; + } + } + } if (procs.count == 1) { Ast *ident = operand->expr; @@ -5632,6 +5678,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type return data; } + Entity **lhs = nullptr; isize lhs_count = -1; diff --git a/src/check_type.cpp b/src/check_type.cpp index ecb2c26ea..3c7f33a46 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1960,20 +1960,6 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, if (params) param_count = params ->Tuple.variables.count; if (results) result_count = results->Tuple.variables.count; - if (param_count > 0) { - for_array(i, params->Tuple.variables) { - Entity *param = params->Tuple.variables[i]; - if (param->kind == Entity_Variable) { - ParameterValue pv = param->Variable.param_value; - if (pv.kind == ParameterValue_Constant && - pv.value.kind == ExactValue_Procedure) { - type->Proc.has_proc_default_values = true; - break; - } - } - } - } - if (result_count > 0) { Entity *first = results->Tuple.variables[0]; type->Proc.has_named_results = first->token.string != ""; diff --git a/src/checker.cpp b/src/checker.cpp index ea300afd9..53bba654d 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2657,6 +2657,15 @@ Array proc_group_entities(CheckerContext *c, Operand o) { return procs; } +Array proc_group_entities_cloned(CheckerContext *c, Operand o) { + auto entities = proc_group_entities(c, o); + if (entities.count == 0) { + return {}; + } + return array_clone(permanent_allocator(), entities); +} + + void init_core_type_info(Checker *c) { diff --git a/src/entity.cpp b/src/entity.cpp index f5720293f..17fa884e1 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -115,6 +115,16 @@ struct ParameterValue { }; }; +bool has_parameter_value(ParameterValue const ¶m_value) { + if (param_value.kind != ParameterValue_Invalid) { + return true; + } + if (param_value.original_ast_expr != nullptr) { + return true; + } + return false; +} + enum EntityConstantFlags : u32 { EntityConstantFlag_ImplicitEnumValue = 1<<0, }; diff --git a/src/types.cpp b/src/types.cpp index 58ccdf5b9..ef770c846 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -186,7 +186,6 @@ struct TypeProc { bool c_vararg; bool is_polymorphic; bool is_poly_specialized; - bool has_proc_default_values; bool has_named_results; bool diverging; // no return bool return_by_pointer; -- cgit v1.2.3 From 3f935bea2505b3ee7e169a29b7aed50c0e5614b7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 24 Mar 2022 11:55:03 +0000 Subject: `union #shared_nil` This adds a feature to `union` which requires all the variants to have a `nil` value and on assign to the union, checks whether that value is `nil` or not. If the value is `nil`, the union will be `nil` (thus sharing the `nil` value) --- core/runtime/core.odin | 1 + src/check_expr.cpp | 7 +++++-- src/check_type.cpp | 19 ++++++++++++++----- src/docs_format.cpp | 1 + src/docs_writer.cpp | 8 +++++--- src/llvm_backend_general.cpp | 31 ++++++++++++++++++++++++++++--- src/llvm_backend_type.cpp | 7 ++++--- src/parser.cpp | 30 ++++++++++++++++++++++++++---- src/parser.hpp | 10 ++++++++-- src/types.cpp | 22 ++++++++++++---------- 10 files changed, 104 insertions(+), 32 deletions(-) (limited to 'src/types.cpp') diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 8c95a234f..a5a190a9c 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -136,6 +136,7 @@ Type_Info_Union :: struct { custom_align: bool, no_nil: bool, maybe: bool, + shared_nil: bool, } Type_Info_Enum :: struct { base: ^Type_Info, diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 66bf8bbd7..dcf17af39 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -10047,8 +10047,11 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) { str = write_expr_to_string(str, st->polymorphic_params, shorthand); str = gb_string_appendc(str, ") "); } - if (st->no_nil) str = gb_string_appendc(str, "#no_nil "); - if (st->maybe) str = gb_string_appendc(str, "#maybe "); + switch (st->kind) { + case UnionType_maybe: str = gb_string_appendc(str, "#maybe "); break; + case UnionType_no_nil: str = gb_string_appendc(str, "#no_nil "); break; + case UnionType_shared_nil: str = gb_string_appendc(str, "#shared_nil "); break; + } if (st->align) { str = gb_string_appendc(str, "#align "); str = write_expr_to_string(str, st->align, shorthand); diff --git a/src/check_type.cpp b/src/check_type.cpp index 3c7f33a46..51f472961 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -675,22 +675,31 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Arraykind == UnionType_shared_nil) { + if (!type_has_nil(t)) { + gbString s = type_to_string(t); + error(node, "Each variant of a union with #shared_nil must have a 'nil' value, got %s", s); + gb_string_free(s); + } + } } } } union_type->Union.variants = slice_from_array(variants); - union_type->Union.no_nil = ut->no_nil; - union_type->Union.maybe = ut->maybe; - if (union_type->Union.no_nil) { + union_type->Union.kind = ut->kind; + switch (ut->kind) { + case UnionType_no_nil: if (variants.count < 2) { error(ut->align, "A union with #no_nil must have at least 2 variants"); } - } - if (union_type->Union.maybe) { + break; + case UnionType_maybe: if (variants.count != 1) { error(ut->align, "A union with #maybe must have at 1 variant, got %lld", cast(long long)variants.count); } + break; } if (ut->align != nullptr) { diff --git a/src/docs_format.cpp b/src/docs_format.cpp index 7ce93d2bf..ee32d0e05 100644 --- a/src/docs_format.cpp +++ b/src/docs_format.cpp @@ -99,6 +99,7 @@ enum OdinDocTypeFlag_Union : u32 { OdinDocTypeFlag_Union_polymorphic = 1<<0, OdinDocTypeFlag_Union_no_nil = 1<<1, OdinDocTypeFlag_Union_maybe = 1<<2, + OdinDocTypeFlag_Union_shared_nil = 1<<3, }; enum OdinDocTypeFlag_Proc : u32 { diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp index 2c5186c39..0ad10ac49 100644 --- a/src/docs_writer.cpp +++ b/src/docs_writer.cpp @@ -619,9 +619,11 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) { case Type_Union: doc_type.kind = OdinDocType_Union; if (type->Union.is_polymorphic) { doc_type.flags |= OdinDocTypeFlag_Union_polymorphic; } - if (type->Union.no_nil) { doc_type.flags |= OdinDocTypeFlag_Union_no_nil; } - if (type->Union.maybe) { doc_type.flags |= OdinDocTypeFlag_Union_maybe; } - + switch (type->Union.kind) { + case UnionType_maybe: doc_type.flags |= OdinDocTypeFlag_Union_maybe; break; + case UnionType_no_nil: doc_type.flags |= OdinDocTypeFlag_Union_no_nil; break; + case UnionType_shared_nil: doc_type.flags |= OdinDocTypeFlag_Union_shared_nil; break; + } { auto variants = array_make(heap_allocator(), type->Union.variants.count); defer (array_free(&variants)); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index dd1555193..f6dd97966 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -1176,10 +1176,35 @@ void lb_emit_store_union_variant_tag(lbProcedure *p, lbValue parent, Type *varia } void lb_emit_store_union_variant(lbProcedure *p, lbValue parent, lbValue variant, Type *variant_type) { - lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type)); + Type *pt = base_type(type_deref(parent.type)); + GB_ASSERT(pt->kind == Type_Union); + if (pt->Union.kind == UnionType_shared_nil) { + lbBlock *if_nil = lb_create_block(p, "shared_nil.if_nil"); + lbBlock *if_not_nil = lb_create_block(p, "shared_nil.if_not_nil"); + lbBlock *done = lb_create_block(p, "shared_nil.done"); + + lbValue cond_is_nil = lb_emit_comp_against_nil(p, Token_CmpEq, variant); + lb_emit_if(p, cond_is_nil, if_nil, if_not_nil); + + lb_start_block(p, if_nil); + lb_emit_store(p, parent, lb_const_nil(p->module, type_deref(parent.type))); + lb_emit_jump(p, done); + + lb_start_block(p, if_not_nil); + lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type)); + lb_emit_store(p, underlying, variant); + lb_emit_store_union_variant_tag(p, parent, variant_type); + lb_emit_jump(p, done); + + lb_start_block(p, done); - lb_emit_store(p, underlying, variant); - lb_emit_store_union_variant_tag(p, parent, variant_type); + + } else { + lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type)); + + lb_emit_store(p, underlying, variant); + lb_emit_store_union_variant_tag(p, parent, variant_type); + } } diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index e245a8b40..7d73956e8 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -641,7 +641,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_union_ptr); { - LLVMValueRef vals[7] = {}; + LLVMValueRef vals[8] = {}; isize variant_count = gb_max(0, t->Union.variants.count); lbValue memory_types = lb_type_info_member_types_offset(p, variant_count); @@ -675,8 +675,9 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da } vals[4] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value; - vals[5] = lb_const_bool(m, t_bool, t->Union.no_nil).value; - vals[6] = lb_const_bool(m, t_bool, t->Union.maybe).value; + vals[5] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_no_nil).value; + vals[6] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_maybe).value; + vals[7] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_shared_nil).value; for (isize i = 0; i < gb_count_of(vals); i++) { if (vals[i] == nullptr) { diff --git a/src/parser.cpp b/src/parser.cpp index a435d1317..767119aa8 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1071,15 +1071,14 @@ Ast *ast_struct_type(AstFile *f, Token token, Slice fields, isize field_c } -Ast *ast_union_type(AstFile *f, Token token, Array const &variants, Ast *polymorphic_params, Ast *align, bool no_nil, bool maybe, +Ast *ast_union_type(AstFile *f, Token token, Array const &variants, Ast *polymorphic_params, Ast *align, UnionTypeKind kind, Token where_token, Array const &where_clauses) { Ast *result = alloc_ast_node(f, Ast_UnionType); result->UnionType.token = token; result->UnionType.variants = slice_from_array(variants); result->UnionType.polymorphic_params = polymorphic_params; result->UnionType.align = align; - result->UnionType.no_nil = no_nil; - result->UnionType.maybe = maybe; + result->UnionType.kind = kind; result->UnionType.where_token = where_token; result->UnionType.where_clauses = slice_from_array(where_clauses); return result; @@ -2475,6 +2474,9 @@ Ast *parse_operand(AstFile *f, bool lhs) { Ast *align = nullptr; bool no_nil = false; bool maybe = false; + bool shared_nil = false; + + UnionTypeKind union_kind = UnionType_Normal; Token start_token = f->curr_token; @@ -2501,6 +2503,11 @@ Ast *parse_operand(AstFile *f, bool lhs) { syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string)); } no_nil = true; + } else if (tag.string == "shared_nil") { + if (shared_nil) { + syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string)); + } + shared_nil = true; } else if (tag.string == "maybe") { if (maybe) { syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string)); @@ -2513,6 +2520,21 @@ Ast *parse_operand(AstFile *f, bool lhs) { if (no_nil && maybe) { syntax_error(f->curr_token, "#maybe and #no_nil cannot be applied together"); } + if (no_nil && shared_nil) { + syntax_error(f->curr_token, "#shared_nil and #no_nil cannot be applied together"); + } + if (shared_nil && maybe) { + syntax_error(f->curr_token, "#maybe and #shared_nil cannot be applied together"); + } + + + if (maybe) { + union_kind = UnionType_maybe; + } else if (no_nil) { + union_kind = UnionType_no_nil; + } else if (shared_nil) { + union_kind = UnionType_shared_nil; + } skip_possible_newline_for_literal(f); @@ -2544,7 +2566,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { Token close = expect_closing_brace_of_field_list(f); - return ast_union_type(f, token, variants, polymorphic_params, align, no_nil, maybe, where_token, where_clauses); + return ast_union_type(f, token, variants, polymorphic_params, align, union_kind, where_token, where_clauses); } break; case Token_enum: { diff --git a/src/parser.hpp b/src/parser.hpp index c33d1520b..c7b4fd0d8 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -330,6 +330,13 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = { "intel", }; +enum UnionTypeKind : u8 { + UnionType_Normal = 0, + UnionType_maybe = 1, + UnionType_no_nil = 2, + UnionType_shared_nil = 3, +}; + #define AST_KINDS \ AST_KIND(Ident, "identifier", struct { \ Token token; \ @@ -678,8 +685,7 @@ AST_KIND(_TypeBegin, "", bool) \ Slice variants; \ Ast *polymorphic_params; \ Ast * align; \ - bool maybe; \ - bool no_nil; \ + UnionTypeKind kind; \ Token where_token; \ Slice where_clauses; \ }) \ diff --git a/src/types.cpp b/src/types.cpp index ef770c846..b231218a2 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -165,9 +165,8 @@ struct TypeUnion { i16 tag_size; bool is_polymorphic; - bool is_poly_specialized : 1; - bool no_nil : 1; - bool maybe : 1; + bool is_poly_specialized; + UnionTypeKind kind; }; struct TypeProc { @@ -1664,7 +1663,7 @@ bool is_type_map(Type *t) { bool is_type_union_maybe_pointer(Type *t) { t = base_type(t); - if (t->kind == Type_Union && t->Union.maybe) { + if (t->kind == Type_Union && t->Union.kind == UnionType_maybe) { if (t->Union.variants.count == 1) { Type *v = t->Union.variants[0]; return is_type_pointer(v) || is_type_multi_pointer(v); @@ -1676,7 +1675,7 @@ bool is_type_union_maybe_pointer(Type *t) { bool is_type_union_maybe_pointer_original_alignment(Type *t) { t = base_type(t); - if (t->kind == Type_Union && t->Union.maybe) { + if (t->kind == Type_Union && t->Union.kind == UnionType_maybe) { if (t->Union.variants.count == 1) { Type *v = t->Union.variants[0]; if (is_type_pointer(v) || is_type_multi_pointer(v)) { @@ -2168,7 +2167,7 @@ bool type_has_nil(Type *t) { case Type_Map: return true; case Type_Union: - return !t->Union.no_nil; + return t->Union.kind != UnionType_no_nil; case Type_Struct: if (is_type_soa_struct(t)) { switch (t->Struct.soa_kind) { @@ -2454,7 +2453,7 @@ bool are_types_identical_internal(Type *x, Type *y, bool check_tuple_names) { if (y->kind == Type_Union) { if (x->Union.variants.count == y->Union.variants.count && x->Union.custom_align == y->Union.custom_align && - x->Union.no_nil == y->Union.no_nil) { + x->Union.kind == y->Union.kind) { // NOTE(bill): zeroth variant is nullptr for_array(i, x->Union.variants) { if (!are_types_identical(x->Union.variants[i], y->Union.variants[i])) { @@ -2598,7 +2597,7 @@ i64 union_variant_index(Type *u, Type *v) { for_array(i, u->Union.variants) { Type *vt = u->Union.variants[i]; if (are_types_identical(v, vt)) { - if (u->Union.no_nil) { + if (u->Union.kind == UnionType_no_nil) { return cast(i64)(i+0); } else { return cast(i64)(i+1); @@ -4021,8 +4020,11 @@ gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) { case Type_Union: str = gb_string_appendc(str, "union"); - if (type->Union.no_nil != 0) str = gb_string_appendc(str, " #no_nil"); - if (type->Union.maybe != 0) str = gb_string_appendc(str, " #maybe"); + switch (type->Union.kind) { + case UnionType_maybe: str = gb_string_appendc(str, " #maybe"); break; + case UnionType_no_nil: str = gb_string_appendc(str, " #no_nil"); break; + case UnionType_shared_nil: str = gb_string_appendc(str, " #shared_nil"); break; + } if (type->Union.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Union.custom_align); str = gb_string_appendc(str, " {"); for_array(i, type->Union.variants) { -- cgit v1.2.3 From 203382461b43db30977c17f51929a565c1f3a229 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 31 Mar 2022 00:14:49 +0100 Subject: Replace the atomic intrinsics Matching C11 in style --- src/check_builtin.cpp | 191 +++++++++++++++++++++++++++++------------- src/checker.cpp | 25 +++++- src/checker_builtin_procs.hpp | 175 +++++++++----------------------------- src/llvm_backend_proc.cpp | 170 ++++++++++--------------------------- src/llvm_backend_utility.cpp | 22 +++++ src/types.cpp | 13 +++ 6 files changed, 272 insertions(+), 324 deletions(-) (limited to 'src/types.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index e480704e3..3fe0f1048 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -379,6 +379,32 @@ bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call } } +bool check_atomic_memory_order_argument(CheckerContext *c, Ast *expr, String const &builtin_name, char const *extra_message = nullptr) { + Operand x = {}; + check_expr_with_type_hint(c, &x, expr, t_atomic_memory_order); + if (x.mode == Addressing_Invalid) { + return false; + } + if (!are_types_identical(x.type, t_atomic_memory_order) || x.mode != Addressing_Constant) { + gbString str = type_to_string(x.type); + if (extra_message) { + error(x.expr, "Expected a constant Atomic_Memory_Order value for the %s of '%.*s', got %s", extra_message, LIT(builtin_name), str); + } else { + error(x.expr, "Expected a constant Atomic_Memory_Order value for '%.*s', got %s", LIT(builtin_name), str); + } + gb_string_free(str); + return false; + } + i64 value = exact_value_to_i64(x.value); + if (value < 0 || value >= OdinAtomicMemoryOrder_COUNT) { + error(x.expr, "Illegal Atomic_Memory_Order value, got %lld", cast(long long)value); + return false; + } + + return true; + +} + bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) { ast_node(ce, CallExpr, call); if (ce->inlining != ProcInlining_none) { @@ -423,6 +449,11 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 // NOTE(bill): The first arg may be a Type, this will be checked case by case break; + case BuiltinProc_atomic_thread_fence: + case BuiltinProc_atomic_signal_fence: + // NOTE(bill): first type will require a type hint + break; + case BuiltinProc_DIRECTIVE: { ast_node(bd, BasicDirective, ce->proc); String name = bd->name.string; @@ -3198,10 +3229,12 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; - case BuiltinProc_atomic_fence: - case BuiltinProc_atomic_fence_acq: - case BuiltinProc_atomic_fence_rel: - case BuiltinProc_atomic_fence_acqrel: + + case BuiltinProc_atomic_thread_fence: + case BuiltinProc_atomic_signal_fence: + if (!check_atomic_memory_order_argument(c, ce->args[0], builtin_name)) { + return false; + } operand->mode = Addressing_NoValue; break; @@ -3210,9 +3243,6 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_unaligned_store: /*fallthrough*/ case BuiltinProc_atomic_store: - case BuiltinProc_atomic_store_rel: - case BuiltinProc_atomic_store_relaxed: - case BuiltinProc_atomic_store_unordered: { Type *elem = nullptr; if (!is_type_normal_pointer(operand->type, &elem)) { @@ -3228,14 +3258,32 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; } + case BuiltinProc_atomic_store_explicit: + { + Type *elem = nullptr; + if (!is_type_normal_pointer(operand->type, &elem)) { + error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name)); + return false; + } + Operand x = {}; + check_expr_with_type_hint(c, &x, ce->args[1], elem); + check_assignment(c, &x, elem, builtin_name); + + if (!check_atomic_memory_order_argument(c, ce->args[2], builtin_name)) { + return false; + } + + operand->type = nullptr; + operand->mode = Addressing_NoValue; + break; + } + + case BuiltinProc_volatile_load: /*fallthrough*/ case BuiltinProc_unaligned_load: /*fallthrough*/ case BuiltinProc_atomic_load: - case BuiltinProc_atomic_load_acq: - case BuiltinProc_atomic_load_relaxed: - case BuiltinProc_atomic_load_unordered: { Type *elem = nullptr; if (!is_type_normal_pointer(operand->type, &elem)) { @@ -3247,41 +3295,52 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; } + case BuiltinProc_atomic_load_explicit: + { + Type *elem = nullptr; + if (!is_type_normal_pointer(operand->type, &elem)) { + error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name)); + return false; + } + + if (!check_atomic_memory_order_argument(c, ce->args[1], builtin_name)) { + return false; + } + + operand->type = elem; + operand->mode = Addressing_Value; + break; + } + case BuiltinProc_atomic_add: - case BuiltinProc_atomic_add_acq: - case BuiltinProc_atomic_add_rel: - case BuiltinProc_atomic_add_acqrel: - case BuiltinProc_atomic_add_relaxed: case BuiltinProc_atomic_sub: - case BuiltinProc_atomic_sub_acq: - case BuiltinProc_atomic_sub_rel: - case BuiltinProc_atomic_sub_acqrel: - case BuiltinProc_atomic_sub_relaxed: case BuiltinProc_atomic_and: - case BuiltinProc_atomic_and_acq: - case BuiltinProc_atomic_and_rel: - case BuiltinProc_atomic_and_acqrel: - case BuiltinProc_atomic_and_relaxed: case BuiltinProc_atomic_nand: - case BuiltinProc_atomic_nand_acq: - case BuiltinProc_atomic_nand_rel: - case BuiltinProc_atomic_nand_acqrel: - case BuiltinProc_atomic_nand_relaxed: case BuiltinProc_atomic_or: - case BuiltinProc_atomic_or_acq: - case BuiltinProc_atomic_or_rel: - case BuiltinProc_atomic_or_acqrel: - case BuiltinProc_atomic_or_relaxed: case BuiltinProc_atomic_xor: - case BuiltinProc_atomic_xor_acq: - case BuiltinProc_atomic_xor_rel: - case BuiltinProc_atomic_xor_acqrel: - case BuiltinProc_atomic_xor_relaxed: - case BuiltinProc_atomic_xchg: - case BuiltinProc_atomic_xchg_acq: - case BuiltinProc_atomic_xchg_rel: - case BuiltinProc_atomic_xchg_acqrel: - case BuiltinProc_atomic_xchg_relaxed: + case BuiltinProc_atomic_exchange: + { + Type *elem = nullptr; + if (!is_type_normal_pointer(operand->type, &elem)) { + error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name)); + return false; + } + Operand x = {}; + check_expr_with_type_hint(c, &x, ce->args[1], elem); + check_assignment(c, &x, elem, builtin_name); + + operand->type = elem; + operand->mode = Addressing_Value; + break; + } + + case BuiltinProc_atomic_add_explicit: + case BuiltinProc_atomic_sub_explicit: + case BuiltinProc_atomic_and_explicit: + case BuiltinProc_atomic_nand_explicit: + case BuiltinProc_atomic_or_explicit: + case BuiltinProc_atomic_xor_explicit: + case BuiltinProc_atomic_exchange_explicit: { Type *elem = nullptr; if (!is_type_normal_pointer(operand->type, &elem)) { @@ -3292,30 +3351,18 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 check_expr_with_type_hint(c, &x, ce->args[1], elem); check_assignment(c, &x, elem, builtin_name); + + if (!check_atomic_memory_order_argument(c, ce->args[2], builtin_name)) { + return false; + } + operand->type = elem; operand->mode = Addressing_Value; break; } - case BuiltinProc_atomic_cxchg: - case BuiltinProc_atomic_cxchg_acq: - case BuiltinProc_atomic_cxchg_rel: - case BuiltinProc_atomic_cxchg_acqrel: - case BuiltinProc_atomic_cxchg_relaxed: - case BuiltinProc_atomic_cxchg_failrelaxed: - case BuiltinProc_atomic_cxchg_failacq: - case BuiltinProc_atomic_cxchg_acq_failrelaxed: - case BuiltinProc_atomic_cxchg_acqrel_failrelaxed: - - case BuiltinProc_atomic_cxchgweak: - case BuiltinProc_atomic_cxchgweak_acq: - case BuiltinProc_atomic_cxchgweak_rel: - case BuiltinProc_atomic_cxchgweak_acqrel: - case BuiltinProc_atomic_cxchgweak_relaxed: - case BuiltinProc_atomic_cxchgweak_failrelaxed: - case BuiltinProc_atomic_cxchgweak_failacq: - case BuiltinProc_atomic_cxchgweak_acq_failrelaxed: - case BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed: + case BuiltinProc_atomic_compare_exchange_strong: + case BuiltinProc_atomic_compare_exchange_weak: { Type *elem = nullptr; if (!is_type_normal_pointer(operand->type, &elem)) { @@ -3333,7 +3380,33 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 operand->type = elem; break; } - break; + + case BuiltinProc_atomic_compare_exchange_strong_explicit: + case BuiltinProc_atomic_compare_exchange_weak_explicit: + { + Type *elem = nullptr; + if (!is_type_normal_pointer(operand->type, &elem)) { + error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name)); + return false; + } + Operand x = {}; + Operand y = {}; + check_expr_with_type_hint(c, &x, ce->args[1], elem); + check_expr_with_type_hint(c, &y, ce->args[2], elem); + check_assignment(c, &x, elem, builtin_name); + check_assignment(c, &y, elem, builtin_name); + + if (!check_atomic_memory_order_argument(c, ce->args[3], builtin_name, "success ordering")) { + return false; + } + if (!check_atomic_memory_order_argument(c, ce->args[4], builtin_name, "failure ordering")) { + return false; + } + + operand->mode = Addressing_OptionalOk; + operand->type = elem; + break; + } case BuiltinProc_fixed_point_mul: case BuiltinProc_fixed_point_div: diff --git a/src/checker.cpp b/src/checker.cpp index 53bba654d..0c72a8e14 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -829,15 +829,16 @@ struct GlobalEnumValue { i64 value; }; -Slice add_global_enum_type(String const &type_name, GlobalEnumValue *values, isize value_count) { +Slice add_global_enum_type(String const &type_name, GlobalEnumValue *values, isize value_count, Type **enum_type_ = nullptr) { Scope *scope = create_scope(nullptr, builtin_pkg->scope); - Entity *e = alloc_entity_type_name(scope, make_token_ident(type_name), nullptr, EntityState_Resolved); + Entity *entity = alloc_entity_type_name(scope, make_token_ident(type_name), nullptr, EntityState_Resolved); Type *enum_type = alloc_type_enum(); - Type *named_type = alloc_type_named(type_name, enum_type, e); + Type *named_type = alloc_type_named(type_name, enum_type, entity); set_base_type(named_type, enum_type); enum_type->Enum.base_type = t_int; enum_type->Enum.scope = scope; + entity->type = named_type; auto fields = array_make(permanent_allocator(), value_count); for (isize i = 0; i < value_count; i++) { @@ -858,6 +859,9 @@ Slice add_global_enum_type(String const &type_name, GlobalEnumValue *v enum_type->Enum.min_value = &enum_type->Enum.fields[enum_type->Enum.min_value_index]->Constant.value; enum_type->Enum.max_value = &enum_type->Enum.fields[enum_type->Enum.max_value_index]->Constant.value; + + if (enum_type_) *enum_type_ = named_type; + return slice_from_array(fields); } void add_global_enum_constant(Slice const &fields, char const *name, i64 value) { @@ -986,6 +990,21 @@ void init_universal(void) { add_global_enum_constant(fields, "ODIN_ERROR_POS_STYLE", build_context.ODIN_ERROR_POS_STYLE); } + { + GlobalEnumValue values[OdinAtomicMemoryOrder_COUNT] = { + {"relaxed", OdinAtomicMemoryOrder_relaxed}, + {"consume", OdinAtomicMemoryOrder_consume}, + {"acquire", OdinAtomicMemoryOrder_acquire}, + {"release", OdinAtomicMemoryOrder_release}, + {"acq_rel", OdinAtomicMemoryOrder_acq_rel}, + {"seq_cst", OdinAtomicMemoryOrder_seq_cst}, + }; + + add_global_enum_type(str_lit("Atomic_Memory_Order"), values, gb_count_of(values), &t_atomic_memory_order); + GB_ASSERT(t_atomic_memory_order->kind == Type_Named); + scope_insert(intrinsics_pkg->scope, t_atomic_memory_order->Named.type_name); + } + add_global_bool_constant("ODIN_DEBUG", bc->ODIN_DEBUG); add_global_bool_constant("ODIN_DISABLE_ASSERT", bc->ODIN_DISABLE_ASSERT); diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index c647d5a01..c6b0afee0 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -86,77 +86,30 @@ enum BuiltinProcId { BuiltinProc_prefetch_write_instruction, BuiltinProc_prefetch_write_data, - BuiltinProc_atomic_fence, - BuiltinProc_atomic_fence_acq, - BuiltinProc_atomic_fence_rel, - BuiltinProc_atomic_fence_acqrel, - + BuiltinProc_atomic_thread_fence, + BuiltinProc_atomic_signal_fence, BuiltinProc_atomic_store, - BuiltinProc_atomic_store_rel, - BuiltinProc_atomic_store_relaxed, - BuiltinProc_atomic_store_unordered, - + BuiltinProc_atomic_store_explicit, BuiltinProc_atomic_load, - BuiltinProc_atomic_load_acq, - BuiltinProc_atomic_load_relaxed, - BuiltinProc_atomic_load_unordered, - + BuiltinProc_atomic_load_explicit, BuiltinProc_atomic_add, - BuiltinProc_atomic_add_acq, - BuiltinProc_atomic_add_rel, - BuiltinProc_atomic_add_acqrel, - BuiltinProc_atomic_add_relaxed, + BuiltinProc_atomic_add_explicit, BuiltinProc_atomic_sub, - BuiltinProc_atomic_sub_acq, - BuiltinProc_atomic_sub_rel, - BuiltinProc_atomic_sub_acqrel, - BuiltinProc_atomic_sub_relaxed, + BuiltinProc_atomic_sub_explicit, BuiltinProc_atomic_and, - BuiltinProc_atomic_and_acq, - BuiltinProc_atomic_and_rel, - BuiltinProc_atomic_and_acqrel, - BuiltinProc_atomic_and_relaxed, + BuiltinProc_atomic_and_explicit, BuiltinProc_atomic_nand, - BuiltinProc_atomic_nand_acq, - BuiltinProc_atomic_nand_rel, - BuiltinProc_atomic_nand_acqrel, - BuiltinProc_atomic_nand_relaxed, + BuiltinProc_atomic_nand_explicit, BuiltinProc_atomic_or, - BuiltinProc_atomic_or_acq, - BuiltinProc_atomic_or_rel, - BuiltinProc_atomic_or_acqrel, - BuiltinProc_atomic_or_relaxed, + BuiltinProc_atomic_or_explicit, BuiltinProc_atomic_xor, - BuiltinProc_atomic_xor_acq, - BuiltinProc_atomic_xor_rel, - BuiltinProc_atomic_xor_acqrel, - BuiltinProc_atomic_xor_relaxed, - - BuiltinProc_atomic_xchg, - BuiltinProc_atomic_xchg_acq, - BuiltinProc_atomic_xchg_rel, - BuiltinProc_atomic_xchg_acqrel, - BuiltinProc_atomic_xchg_relaxed, - - BuiltinProc_atomic_cxchg, - BuiltinProc_atomic_cxchg_acq, - BuiltinProc_atomic_cxchg_rel, - BuiltinProc_atomic_cxchg_acqrel, - BuiltinProc_atomic_cxchg_relaxed, - BuiltinProc_atomic_cxchg_failrelaxed, - BuiltinProc_atomic_cxchg_failacq, - BuiltinProc_atomic_cxchg_acq_failrelaxed, - BuiltinProc_atomic_cxchg_acqrel_failrelaxed, - - BuiltinProc_atomic_cxchgweak, - BuiltinProc_atomic_cxchgweak_acq, - BuiltinProc_atomic_cxchgweak_rel, - BuiltinProc_atomic_cxchgweak_acqrel, - BuiltinProc_atomic_cxchgweak_relaxed, - BuiltinProc_atomic_cxchgweak_failrelaxed, - BuiltinProc_atomic_cxchgweak_failacq, - BuiltinProc_atomic_cxchgweak_acq_failrelaxed, - BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed, + BuiltinProc_atomic_xor_explicit, + BuiltinProc_atomic_exchange, + BuiltinProc_atomic_exchange_explicit, + BuiltinProc_atomic_compare_exchange_strong, + BuiltinProc_atomic_compare_exchange_strong_explicit, + BuiltinProc_atomic_compare_exchange_weak, + BuiltinProc_atomic_compare_exchange_weak_explicit, BuiltinProc_fixed_point_mul, BuiltinProc_fixed_point_div, @@ -352,78 +305,30 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("prefetch_write_instruction"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("prefetch_write_data"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_fence"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_fence_acq"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_fence_rel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_fence_acqrel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_store_rel"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_store_relaxed"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_store_unordered"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_load_acq"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_load_relaxed"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_load_unordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_add_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_add_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_add_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_add_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_sub_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_sub_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_sub_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_sub_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_and_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_and_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_and_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_and_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_nand"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_nand_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_nand_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_nand_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_nand_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_or_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_or_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_or_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_or_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xor_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xor_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xor_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xor_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_xchg"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xchg_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xchg_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xchg_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xchg_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_cxchg"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_cxchgweak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - + {STR_LIT("atomic_thread_fence"), 1, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_signal_fence"), 1, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_store_explicit"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_load_explicit"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_add_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_sub_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_and_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_nand"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_nand_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_or_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xor_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_exchange"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_exchange_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_compare_exchange_strong"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_compare_exchange_strong_explicit"), 5, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_compare_exchange_weak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_compare_exchange_weak_explicit"), 5, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("fixed_point_mul"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("fixed_point_div"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index d7f3d6c45..ad82b79e5 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1606,36 +1606,26 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, } - - case BuiltinProc_atomic_fence: - LLVMBuildFence(p->builder, LLVMAtomicOrderingSequentiallyConsistent, false, ""); - return {}; - case BuiltinProc_atomic_fence_acq: - LLVMBuildFence(p->builder, LLVMAtomicOrderingAcquire, false, ""); - return {}; - case BuiltinProc_atomic_fence_rel: - LLVMBuildFence(p->builder, LLVMAtomicOrderingRelease, false, ""); + // TODO(bill): Which is correct? + case BuiltinProc_atomic_thread_fence: + LLVMBuildFence(p->builder, llvm_atomic_ordering_from_odin(ce->args[0]), false, ""); return {}; - case BuiltinProc_atomic_fence_acqrel: - LLVMBuildFence(p->builder, LLVMAtomicOrderingAcquireRelease, false, ""); + case BuiltinProc_atomic_signal_fence: + LLVMBuildFence(p->builder, llvm_atomic_ordering_from_odin(ce->args[0]), true, ""); return {}; case BuiltinProc_volatile_store: case BuiltinProc_atomic_store: - case BuiltinProc_atomic_store_rel: - case BuiltinProc_atomic_store_relaxed: - case BuiltinProc_atomic_store_unordered: { + case BuiltinProc_atomic_store_explicit: { lbValue dst = lb_build_expr(p, ce->args[0]); lbValue val = lb_build_expr(p, ce->args[1]); val = lb_emit_conv(p, val, type_deref(dst.type)); LLVMValueRef instr = LLVMBuildStore(p->builder, val.value, dst.value); switch (id) { - case BuiltinProc_volatile_store: LLVMSetVolatile(instr, true); break; - case BuiltinProc_atomic_store: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break; - case BuiltinProc_atomic_store_rel: LLVMSetOrdering(instr, LLVMAtomicOrderingRelease); break; - case BuiltinProc_atomic_store_relaxed: LLVMSetOrdering(instr, LLVMAtomicOrderingMonotonic); break; - case BuiltinProc_atomic_store_unordered: LLVMSetOrdering(instr, LLVMAtomicOrderingUnordered); break; + case BuiltinProc_volatile_store: LLVMSetVolatile(instr, true); break; + case BuiltinProc_atomic_store: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break; + case BuiltinProc_atomic_store_explicit: LLVMSetOrdering(instr, llvm_atomic_ordering_from_odin(ce->args[2])); break; } LLVMSetAlignment(instr, cast(unsigned)type_align_of(type_deref(dst.type))); @@ -1645,18 +1635,14 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, case BuiltinProc_volatile_load: case BuiltinProc_atomic_load: - case BuiltinProc_atomic_load_acq: - case BuiltinProc_atomic_load_relaxed: - case BuiltinProc_atomic_load_unordered: { + case BuiltinProc_atomic_load_explicit: { lbValue dst = lb_build_expr(p, ce->args[0]); LLVMValueRef instr = LLVMBuildLoad(p->builder, dst.value, ""); switch (id) { - case BuiltinProc_volatile_load: LLVMSetVolatile(instr, true); break; - case BuiltinProc_atomic_load: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break; - case BuiltinProc_atomic_load_acq: LLVMSetOrdering(instr, LLVMAtomicOrderingAcquire); break; - case BuiltinProc_atomic_load_relaxed: LLVMSetOrdering(instr, LLVMAtomicOrderingMonotonic); break; - case BuiltinProc_atomic_load_unordered: LLVMSetOrdering(instr, LLVMAtomicOrderingUnordered); break; + case BuiltinProc_volatile_load: LLVMSetVolatile(instr, true); break; + case BuiltinProc_atomic_load: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break; + case BuiltinProc_atomic_load_explicit: LLVMSetOrdering(instr, llvm_atomic_ordering_from_odin(ce->args[1])); break; } LLVMSetAlignment(instr, cast(unsigned)type_align_of(type_deref(dst.type))); @@ -1686,40 +1672,19 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, } case BuiltinProc_atomic_add: - case BuiltinProc_atomic_add_acq: - case BuiltinProc_atomic_add_rel: - case BuiltinProc_atomic_add_acqrel: - case BuiltinProc_atomic_add_relaxed: case BuiltinProc_atomic_sub: - case BuiltinProc_atomic_sub_acq: - case BuiltinProc_atomic_sub_rel: - case BuiltinProc_atomic_sub_acqrel: - case BuiltinProc_atomic_sub_relaxed: case BuiltinProc_atomic_and: - case BuiltinProc_atomic_and_acq: - case BuiltinProc_atomic_and_rel: - case BuiltinProc_atomic_and_acqrel: - case BuiltinProc_atomic_and_relaxed: case BuiltinProc_atomic_nand: - case BuiltinProc_atomic_nand_acq: - case BuiltinProc_atomic_nand_rel: - case BuiltinProc_atomic_nand_acqrel: - case BuiltinProc_atomic_nand_relaxed: case BuiltinProc_atomic_or: - case BuiltinProc_atomic_or_acq: - case BuiltinProc_atomic_or_rel: - case BuiltinProc_atomic_or_acqrel: - case BuiltinProc_atomic_or_relaxed: case BuiltinProc_atomic_xor: - case BuiltinProc_atomic_xor_acq: - case BuiltinProc_atomic_xor_rel: - case BuiltinProc_atomic_xor_acqrel: - case BuiltinProc_atomic_xor_relaxed: - case BuiltinProc_atomic_xchg: - case BuiltinProc_atomic_xchg_acq: - case BuiltinProc_atomic_xchg_rel: - case BuiltinProc_atomic_xchg_acqrel: - case BuiltinProc_atomic_xchg_relaxed: { + case BuiltinProc_atomic_exchange: + case BuiltinProc_atomic_add_explicit: + case BuiltinProc_atomic_sub_explicit: + case BuiltinProc_atomic_and_explicit: + case BuiltinProc_atomic_nand_explicit: + case BuiltinProc_atomic_or_explicit: + case BuiltinProc_atomic_xor_explicit: + case BuiltinProc_atomic_exchange_explicit: { lbValue dst = lb_build_expr(p, ce->args[0]); lbValue val = lb_build_expr(p, ce->args[1]); val = lb_emit_conv(p, val, type_deref(dst.type)); @@ -1728,41 +1693,20 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, LLVMAtomicOrdering ordering = {}; switch (id) { - case BuiltinProc_atomic_add: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; - case BuiltinProc_atomic_add_acq: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingAcquire; break; - case BuiltinProc_atomic_add_rel: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingRelease; break; - case BuiltinProc_atomic_add_acqrel: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingAcquireRelease; break; - case BuiltinProc_atomic_add_relaxed: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingMonotonic; break; - case BuiltinProc_atomic_sub: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; - case BuiltinProc_atomic_sub_acq: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingAcquire; break; - case BuiltinProc_atomic_sub_rel: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingRelease; break; - case BuiltinProc_atomic_sub_acqrel: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingAcquireRelease; break; - case BuiltinProc_atomic_sub_relaxed: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingMonotonic; break; - case BuiltinProc_atomic_and: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; - case BuiltinProc_atomic_and_acq: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingAcquire; break; - case BuiltinProc_atomic_and_rel: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingRelease; break; - case BuiltinProc_atomic_and_acqrel: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingAcquireRelease; break; - case BuiltinProc_atomic_and_relaxed: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingMonotonic; break; - case BuiltinProc_atomic_nand: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; - case BuiltinProc_atomic_nand_acq: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingAcquire; break; - case BuiltinProc_atomic_nand_rel: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingRelease; break; - case BuiltinProc_atomic_nand_acqrel: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingAcquireRelease; break; - case BuiltinProc_atomic_nand_relaxed: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingMonotonic; break; - case BuiltinProc_atomic_or: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; - case BuiltinProc_atomic_or_acq: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingAcquire; break; - case BuiltinProc_atomic_or_rel: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingRelease; break; - case BuiltinProc_atomic_or_acqrel: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingAcquireRelease; break; - case BuiltinProc_atomic_or_relaxed: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingMonotonic; break; - case BuiltinProc_atomic_xor: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; - case BuiltinProc_atomic_xor_acq: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingAcquire; break; - case BuiltinProc_atomic_xor_rel: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingRelease; break; - case BuiltinProc_atomic_xor_acqrel: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingAcquireRelease; break; - case BuiltinProc_atomic_xor_relaxed: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingMonotonic; break; - case BuiltinProc_atomic_xchg: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; - case BuiltinProc_atomic_xchg_acq: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingAcquire; break; - case BuiltinProc_atomic_xchg_rel: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingRelease; break; - case BuiltinProc_atomic_xchg_acqrel: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingAcquireRelease; break; - case BuiltinProc_atomic_xchg_relaxed: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingMonotonic; break; + case BuiltinProc_atomic_add: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; + case BuiltinProc_atomic_sub: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; + case BuiltinProc_atomic_and: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; + case BuiltinProc_atomic_nand: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; + case BuiltinProc_atomic_or: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; + case BuiltinProc_atomic_xor: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; + case BuiltinProc_atomic_exchange: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingSequentiallyConsistent; break; + case BuiltinProc_atomic_add_explicit: op = LLVMAtomicRMWBinOpAdd; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break; + case BuiltinProc_atomic_sub_explicit: op = LLVMAtomicRMWBinOpSub; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break; + case BuiltinProc_atomic_and_explicit: op = LLVMAtomicRMWBinOpAnd; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break; + case BuiltinProc_atomic_nand_explicit: op = LLVMAtomicRMWBinOpNand; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break; + case BuiltinProc_atomic_or_explicit: op = LLVMAtomicRMWBinOpOr; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break; + case BuiltinProc_atomic_xor_explicit: op = LLVMAtomicRMWBinOpXor; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break; + case BuiltinProc_atomic_exchange_explicit: op = LLVMAtomicRMWBinOpXchg; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break; } lbValue res = {}; @@ -1771,24 +1715,10 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, return res; } - case BuiltinProc_atomic_cxchg: - case BuiltinProc_atomic_cxchg_acq: - case BuiltinProc_atomic_cxchg_rel: - case BuiltinProc_atomic_cxchg_acqrel: - case BuiltinProc_atomic_cxchg_relaxed: - case BuiltinProc_atomic_cxchg_failrelaxed: - case BuiltinProc_atomic_cxchg_failacq: - case BuiltinProc_atomic_cxchg_acq_failrelaxed: - case BuiltinProc_atomic_cxchg_acqrel_failrelaxed: - case BuiltinProc_atomic_cxchgweak: - case BuiltinProc_atomic_cxchgweak_acq: - case BuiltinProc_atomic_cxchgweak_rel: - case BuiltinProc_atomic_cxchgweak_acqrel: - case BuiltinProc_atomic_cxchgweak_relaxed: - case BuiltinProc_atomic_cxchgweak_failrelaxed: - case BuiltinProc_atomic_cxchgweak_failacq: - case BuiltinProc_atomic_cxchgweak_acq_failrelaxed: - case BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed: { + case BuiltinProc_atomic_compare_exchange_strong: + case BuiltinProc_atomic_compare_exchange_weak: + case BuiltinProc_atomic_compare_exchange_strong_explicit: + case BuiltinProc_atomic_compare_exchange_weak_explicit: { lbValue address = lb_build_expr(p, ce->args[0]); Type *elem = type_deref(address.type); lbValue old_value = lb_build_expr(p, ce->args[1]); @@ -1801,24 +1731,10 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, LLVMBool weak = false; switch (id) { - case BuiltinProc_atomic_cxchg: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break; - case BuiltinProc_atomic_cxchg_acq: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break; - case BuiltinProc_atomic_cxchg_rel: success_ordering = LLVMAtomicOrderingRelease; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break; - case BuiltinProc_atomic_cxchg_acqrel: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break; - case BuiltinProc_atomic_cxchg_relaxed: success_ordering = LLVMAtomicOrderingMonotonic; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break; - case BuiltinProc_atomic_cxchg_failrelaxed: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break; - case BuiltinProc_atomic_cxchg_failacq: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingAcquire; weak = false; break; - case BuiltinProc_atomic_cxchg_acq_failrelaxed: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break; - case BuiltinProc_atomic_cxchg_acqrel_failrelaxed: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break; - case BuiltinProc_atomic_cxchgweak: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break; - case BuiltinProc_atomic_cxchgweak_acq: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break; - case BuiltinProc_atomic_cxchgweak_rel: success_ordering = LLVMAtomicOrderingRelease; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break; - case BuiltinProc_atomic_cxchgweak_acqrel: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break; - case BuiltinProc_atomic_cxchgweak_relaxed: success_ordering = LLVMAtomicOrderingMonotonic; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break; - case BuiltinProc_atomic_cxchgweak_failrelaxed: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break; - case BuiltinProc_atomic_cxchgweak_failacq: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingAcquire; weak = true; break; - case BuiltinProc_atomic_cxchgweak_acq_failrelaxed: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break; - case BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break; + case BuiltinProc_atomic_compare_exchange_strong: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break; + case BuiltinProc_atomic_compare_exchange_weak: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break; + case BuiltinProc_atomic_compare_exchange_strong_explicit: success_ordering = llvm_atomic_ordering_from_odin(ce->args[3]); failure_ordering = llvm_atomic_ordering_from_odin(ce->args[4]); weak = false; break; + case BuiltinProc_atomic_compare_exchange_weak_explicit: success_ordering = llvm_atomic_ordering_from_odin(ce->args[3]); failure_ordering = llvm_atomic_ordering_from_odin(ce->args[4]); weak = true; break; } // TODO(bill): Figure out how to make it weak diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 399d1632d..037171637 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -2005,3 +2005,25 @@ lbValue lb_handle_objc_send(lbProcedure *p, Ast *expr) { } + + +LLVMAtomicOrdering llvm_atomic_ordering_from_odin(ExactValue const &value) { + GB_ASSERT(value.kind == ExactValue_Integer); + i64 v = exact_value_to_i64(value); + switch (v) { + case OdinAtomicMemoryOrder_relaxed: return LLVMAtomicOrderingUnordered; + case OdinAtomicMemoryOrder_consume: return LLVMAtomicOrderingMonotonic; + case OdinAtomicMemoryOrder_acquire: return LLVMAtomicOrderingAcquire; + case OdinAtomicMemoryOrder_release: return LLVMAtomicOrderingRelease; + case OdinAtomicMemoryOrder_acq_rel: return LLVMAtomicOrderingAcquireRelease; + case OdinAtomicMemoryOrder_seq_cst: return LLVMAtomicOrderingSequentiallyConsistent; + } + GB_PANIC("Unknown atomic ordering"); + return LLVMAtomicOrderingSequentiallyConsistent; +} + + +LLVMAtomicOrdering llvm_atomic_ordering_from_odin(Ast *expr) { + ExactValue value = type_and_value_of_expr(expr).value; + return llvm_atomic_ordering_from_odin(value); +} diff --git a/src/types.cpp b/src/types.cpp index b231218a2..25e29820c 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -692,6 +692,19 @@ gb_global Type *t_objc_id = nullptr; gb_global Type *t_objc_SEL = nullptr; gb_global Type *t_objc_Class = nullptr; +enum OdinAtomicMemoryOrder : i32 { + OdinAtomicMemoryOrder_relaxed = 0, + OdinAtomicMemoryOrder_consume = 1, + OdinAtomicMemoryOrder_acquire = 2, + OdinAtomicMemoryOrder_release = 3, + OdinAtomicMemoryOrder_acq_rel = 4, + OdinAtomicMemoryOrder_seq_cst = 5, + OdinAtomicMemoryOrder_COUNT, +}; + +gb_global Type *t_atomic_memory_order = nullptr; + + gb_global RecursiveMutex g_type_mutex; -- cgit v1.2.3 From 6bc0c611abf9426815207d06c8f1d922533b278b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 31 Mar 2022 00:49:53 +0100 Subject: Enforce success failure pairings of `compare_exchange_*_explicit` at compile time --- core/sync/primitives_atomic.odin | 2 +- src/check_builtin.cpp | 92 +++++++++++++++++++++++++++++++++++++--- src/types.cpp | 13 +++++- 3 files changed, 97 insertions(+), 10 deletions(-) (limited to 'src/types.cpp') diff --git a/core/sync/primitives_atomic.odin b/core/sync/primitives_atomic.odin index 8cec36602..dd432745f 100644 --- a/core/sync/primitives_atomic.odin +++ b/core/sync/primitives_atomic.odin @@ -77,7 +77,7 @@ atomic_mutex_unlock :: proc(m: ^Atomic_Mutex) { // atomic_mutex_try_lock tries to lock m, will return true on success, and false on failure atomic_mutex_try_lock :: proc(m: ^Atomic_Mutex) -> bool { - _, ok := atomic_compare_exchange_strong_explicit(&m.state, .Unlocked, .Locked, .acquire, .acquire) + _, ok := atomic_compare_exchange_strong_explicit(&m.state, .Unlocked, .Locked, .acquire, .consume) return ok } diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 3fe0f1048..a82577b31 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -379,7 +379,7 @@ bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call } } -bool check_atomic_memory_order_argument(CheckerContext *c, Ast *expr, String const &builtin_name, char const *extra_message = nullptr) { +bool check_atomic_memory_order_argument(CheckerContext *c, Ast *expr, String const &builtin_name, OdinAtomicMemoryOrder *memory_order_, char const *extra_message = nullptr) { Operand x = {}; check_expr_with_type_hint(c, &x, expr, t_atomic_memory_order); if (x.mode == Addressing_Invalid) { @@ -400,6 +400,9 @@ bool check_atomic_memory_order_argument(CheckerContext *c, Ast *expr, String con error(x.expr, "Illegal Atomic_Memory_Order value, got %lld", cast(long long)value); return false; } + if (memory_order_) { + *memory_order_ = cast(OdinAtomicMemoryOrder)value; + } return true; @@ -3232,7 +3235,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_atomic_thread_fence: case BuiltinProc_atomic_signal_fence: - if (!check_atomic_memory_order_argument(c, ce->args[0], builtin_name)) { + if (!check_atomic_memory_order_argument(c, ce->args[0], builtin_name, nullptr)) { return false; } operand->mode = Addressing_NoValue; @@ -3269,9 +3272,17 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 check_expr_with_type_hint(c, &x, ce->args[1], elem); check_assignment(c, &x, elem, builtin_name); - if (!check_atomic_memory_order_argument(c, ce->args[2], builtin_name)) { + OdinAtomicMemoryOrder memory_order = {}; + if (!check_atomic_memory_order_argument(c, ce->args[2], builtin_name, &memory_order)) { return false; } + switch (memory_order) { + case OdinAtomicMemoryOrder_consume: + case OdinAtomicMemoryOrder_acquire: + case OdinAtomicMemoryOrder_acq_rel: + error(ce->args[2], "Illegal memory order .%s for %.*s", OdinAtomicMemoryOrder_strings[memory_order], LIT(builtin_name)); + break; + } operand->type = nullptr; operand->mode = Addressing_NoValue; @@ -3303,10 +3314,18 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 return false; } - if (!check_atomic_memory_order_argument(c, ce->args[1], builtin_name)) { + OdinAtomicMemoryOrder memory_order = {}; + if (!check_atomic_memory_order_argument(c, ce->args[1], builtin_name, &memory_order)) { return false; } + switch (memory_order) { + case OdinAtomicMemoryOrder_release: + case OdinAtomicMemoryOrder_acq_rel: + error(ce->args[1], "Illegal memory order .%s for %.*s", OdinAtomicMemoryOrder_strings[memory_order], LIT(builtin_name)); + break; + } + operand->type = elem; operand->mode = Addressing_Value; break; @@ -3352,7 +3371,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 check_assignment(c, &x, elem, builtin_name); - if (!check_atomic_memory_order_argument(c, ce->args[2], builtin_name)) { + if (!check_atomic_memory_order_argument(c, ce->args[2], builtin_name, nullptr)) { return false; } @@ -3396,13 +3415,72 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 check_assignment(c, &x, elem, builtin_name); check_assignment(c, &y, elem, builtin_name); - if (!check_atomic_memory_order_argument(c, ce->args[3], builtin_name, "success ordering")) { + OdinAtomicMemoryOrder success_memory_order = {}; + OdinAtomicMemoryOrder failure_memory_order = {}; + if (!check_atomic_memory_order_argument(c, ce->args[3], builtin_name, &success_memory_order, "success ordering")) { return false; } - if (!check_atomic_memory_order_argument(c, ce->args[4], builtin_name, "failure ordering")) { + if (!check_atomic_memory_order_argument(c, ce->args[4], builtin_name, &failure_memory_order, "failure ordering")) { return false; } + bool invalid_combination = false; + + switch (success_memory_order) { + case OdinAtomicMemoryOrder_relaxed: + case OdinAtomicMemoryOrder_release: + if (failure_memory_order != OdinAtomicMemoryOrder_relaxed) { + invalid_combination = true; + } + break; + case OdinAtomicMemoryOrder_consume: + switch (failure_memory_order) { + case OdinAtomicMemoryOrder_relaxed: + case OdinAtomicMemoryOrder_consume: + break; + default: + invalid_combination = true; + break; + } + break; + case OdinAtomicMemoryOrder_acquire: + case OdinAtomicMemoryOrder_acq_rel: + switch (failure_memory_order) { + case OdinAtomicMemoryOrder_relaxed: + case OdinAtomicMemoryOrder_consume: + case OdinAtomicMemoryOrder_acquire: + break; + default: + invalid_combination = true; + break; + } + break; + case OdinAtomicMemoryOrder_seq_cst: + switch (failure_memory_order) { + case OdinAtomicMemoryOrder_relaxed: + case OdinAtomicMemoryOrder_consume: + case OdinAtomicMemoryOrder_acquire: + case OdinAtomicMemoryOrder_seq_cst: + break; + default: + invalid_combination = true; + break; + } + break; + default: + invalid_combination = true; + break; + } + + + if (invalid_combination) { + error(ce->args[3], "Illegal memory order pairing for %.*s, success = .%s, failure = .%s", + LIT(builtin_name), + OdinAtomicMemoryOrder_strings[success_memory_order], + OdinAtomicMemoryOrder_strings[failure_memory_order] + ); + } + operand->mode = Addressing_OptionalOk; operand->type = elem; break; diff --git a/src/types.cpp b/src/types.cpp index 25e29820c..16d07d392 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -693,8 +693,8 @@ gb_global Type *t_objc_SEL = nullptr; gb_global Type *t_objc_Class = nullptr; enum OdinAtomicMemoryOrder : i32 { - OdinAtomicMemoryOrder_relaxed = 0, - OdinAtomicMemoryOrder_consume = 1, + OdinAtomicMemoryOrder_relaxed = 0, // unordered + OdinAtomicMemoryOrder_consume = 1, // monotonic OdinAtomicMemoryOrder_acquire = 2, OdinAtomicMemoryOrder_release = 3, OdinAtomicMemoryOrder_acq_rel = 4, @@ -702,6 +702,15 @@ enum OdinAtomicMemoryOrder : i32 { OdinAtomicMemoryOrder_COUNT, }; +char const *OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_COUNT] = { + "relaxed", + "consume", + "acquire", + "release", + "acq_rel", + "seq_cst", +}; + gb_global Type *t_atomic_memory_order = nullptr; -- cgit v1.2.3 From 9f2d710c357f9d1ca28c169269a9816e1788d1b1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 31 Mar 2022 12:57:24 +0100 Subject: Change `intrinsics.Atomic_Memory_Order` fields to use `Ada_Case` rather than `snake_case` --- core/c/libc/stdatomic.odin | 150 ++++++++++++++++++------------------- core/intrinsics/intrinsics.odin | 12 +-- core/mem/mem.odin | 2 +- core/sync/atomic.odin | 12 +-- core/sync/extended.odin | 30 ++++---- core/sync/primitives_atomic.odin | 16 ++-- core/sync/primitives_internal.odin | 6 +- src/checker.cpp | 12 +-- src/types.cpp | 12 +-- 9 files changed, 126 insertions(+), 126 deletions(-) (limited to 'src/types.cpp') diff --git a/core/c/libc/stdatomic.odin b/core/c/libc/stdatomic.odin index 26ea0db5e..9de3c4678 100644 --- a/core/c/libc/stdatomic.odin +++ b/core/c/libc/stdatomic.odin @@ -50,10 +50,10 @@ atomic_thread_fence :: #force_inline proc(order: memory_order) { assert(order != .relaxed) assert(order != .consume) #partial switch order { - case .acquire: intrinsics.atomic_thread_fence(.acquire) - case .release: intrinsics.atomic_thread_fence(.release) - case .acq_rel: intrinsics.atomic_thread_fence(.acq_rel) - case .seq_cst: intrinsics.atomic_thread_fence(.seq_cst) + case .acquire: intrinsics.atomic_thread_fence(.Acquire) + case .release: intrinsics.atomic_thread_fence(.Release) + case .acq_rel: intrinsics.atomic_thread_fence(.Acq_Rel) + case .seq_cst: intrinsics.atomic_thread_fence(.Seq_Cst) } } @@ -61,10 +61,10 @@ atomic_signal_fence :: #force_inline proc(order: memory_order) { assert(order != .relaxed) assert(order != .consume) #partial switch order { - case .acquire: intrinsics.atomic_signal_fence(.acquire) - case .release: intrinsics.atomic_signal_fence(.release) - case .acq_rel: intrinsics.atomic_signal_fence(.acq_rel) - case .seq_cst: intrinsics.atomic_signal_fence(.seq_cst) + case .acquire: intrinsics.atomic_signal_fence(.Acquire) + case .release: intrinsics.atomic_signal_fence(.Release) + case .acq_rel: intrinsics.atomic_signal_fence(.Acq_Rel) + case .seq_cst: intrinsics.atomic_signal_fence(.Seq_Cst) } } @@ -123,9 +123,9 @@ atomic_store_explicit :: #force_inline proc(object: ^$T, desired: T, order: memo assert(order != .acq_rel) #partial switch order { - case .relaxed: intrinsics.atomic_store_explicit(object, desired, .relaxed) - case .release: intrinsics.atomic_store_explicit(object, desired, .release) - case .seq_cst: intrinsics.atomic_store_explicit(object, desired, .seq_cst) + case .relaxed: intrinsics.atomic_store_explicit(object, desired, .Relaxed) + case .release: intrinsics.atomic_store_explicit(object, desired, .Release) + case .seq_cst: intrinsics.atomic_store_explicit(object, desired, .Seq_Cst) } } @@ -138,10 +138,10 @@ atomic_load_explicit :: #force_inline proc(object: ^$T, order: memory_order) { assert(order != .acq_rel) #partial switch order { - case .relaxed: return intrinsics.atomic_load_explicit(object, .relaxed) - case .consume: return intrinsics.atomic_load_explicit(object, .consume) - case .acquire: return intrinsics.atomic_load_explicit(object, .acquire) - case .seq_cst: return intrinsics.atomic_load_explicit(object, .seq_cst) + case .relaxed: return intrinsics.atomic_load_explicit(object, .Relaxed) + case .consume: return intrinsics.atomic_load_explicit(object, .Consume) + case .acquire: return intrinsics.atomic_load_explicit(object, .Acquire) + case .seq_cst: return intrinsics.atomic_load_explicit(object, .Seq_Cst) } } @@ -151,12 +151,12 @@ atomic_exchange :: #force_inline proc(object: ^$T, desired: T) -> T { atomic_exchange_explicit :: #force_inline proc(object: ^$T, desired: T, order: memory_order) -> T { switch order { - case .relaxed: return intrinsics.atomic_exchange_explicit(object, desired, .relaxed) - case .consume: return intrinsics.atomic_exchange_explicit(object, desired, .consume) - case .acquire: return intrinsics.atomic_exchange_explicit(object, desired, .acquire) - case .release: return intrinsics.atomic_exchange_explicit(object, desired, .release) - case .acq_rel: return intrinsics.atomic_exchange_explicit(object, desired, .acq_rel) - case .seq_cst: return intrinsics.atomic_exchange_explicit(object, desired, .seq_cst) + case .relaxed: return intrinsics.atomic_exchange_explicit(object, desired, .Relaxed) + case .consume: return intrinsics.atomic_exchange_explicit(object, desired, .Consume) + case .acquire: return intrinsics.atomic_exchange_explicit(object, desired, .Acquire) + case .release: return intrinsics.atomic_exchange_explicit(object, desired, .Release) + case .acq_rel: return intrinsics.atomic_exchange_explicit(object, desired, .Acq_Rel) + case .seq_cst: return intrinsics.atomic_exchange_explicit(object, desired, .Seq_Cst) } return false } @@ -193,36 +193,36 @@ atomic_compare_exchange_strong_explicit :: #force_inline proc(object, expected: assert(success != .relaxed) #partial switch success { case .seq_cst: - value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .seq_cst, .seq_cst) + value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Seq_Cst) case .acquire: - value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .acquire, .seq_cst) + value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acquire, .Seq_Cst) case .consume: - value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .consume, .seq_cst) + value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Consume, .Seq_Cst) case .release: - value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .release, .seq_cst) + value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Release, .Seq_Cst) case .acq_rel: - value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .acq_rel, .seq_cst) + value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acq_Rel, .Seq_Cst) } case .relaxed: assert(success != .release) #partial switch success { case .relaxed: - value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .relaxed, .relaxed) + value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Relaxed, .Relaxed) case .seq_cst: - value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .seq_cst, .relaxed) + value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Relaxed) case .acquire: - value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .acquire, .relaxed) + value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acquire, .Relaxed) case .consume: - value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .consume, .relaxed) + value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Consume, .Relaxed) case .acq_rel: - value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .acq_rel, .relaxed) + value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acq_Rel, .Relaxed) } case .consume: assert(success == .seq_cst) - value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .seq_cst, .consume) + value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Consume) case .acquire: assert(success == .seq_cst) - value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .seq_cst, .acquire) + value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Acquire) } if !ok { expected^ = value } @@ -245,36 +245,36 @@ atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$ assert(success != .relaxed) #partial switch success { case .seq_cst: - value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .seq_cst, .seq_cst) + value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Seq_Cst) case .acquire: - value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .acquire, .seq_cst) + value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acquire, .Seq_Cst) case .consume: - value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .consume, .seq_cst) + value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Consume, .Seq_Cst) case .release: - value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .release, .seq_cst) + value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Release, .Seq_Cst) case .acq_rel: - value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .acq_rel, .seq_cst) + value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acq_Rel, .Seq_Cst) } case .relaxed: assert(success != .release) #partial switch success { case .relaxed: - value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .relaxed, .relaxed) + value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Relaxed, .Relaxed) case .seq_cst: - value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .seq_cst, .relaxed) + value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Relaxed) case .acquire: - value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .acquire, .relaxed) + value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acquire, .Relaxed) case .consume: - value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .consume, .relaxed) + value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Consume, .Relaxed) case .acq_rel: - value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .acq_rel, .relaxed) + value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acq_Rel, .Relaxed) } case .consume: assert(success == .seq_cst) - value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .seq_cst, .consume) + value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Consume) case .acquire: assert(success == .seq_cst) - value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .seq_cst, .acquire) + value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Acquire) } if !ok { expected^ = value } @@ -288,13 +288,13 @@ atomic_fetch_add :: #force_inline proc(object: ^$T, operand: T) -> T { atomic_fetch_add_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T { switch order { - case .relaxed: return intrinsics.atomic_add_explicit(object, operand, .relaxed) - case .consume: return intrinsics.atomic_add_explicit(object, operand, .consume) - case .acquire: return intrinsics.atomic_add_explicit(object, operand, .acquire) - case .release: return intrinsics.atomic_add_explicit(object, operand, .release) - case .acq_rel: return intrinsics.atomic_add_explicit(object, operand, .acq_rel) + case .relaxed: return intrinsics.atomic_add_explicit(object, operand, .Relaxed) + case .consume: return intrinsics.atomic_add_explicit(object, operand, .Consume) + case .acquire: return intrinsics.atomic_add_explicit(object, operand, .Acquire) + case .release: return intrinsics.atomic_add_explicit(object, operand, .Release) + case .acq_rel: return intrinsics.atomic_add_explicit(object, operand, .Acq_Rel) case: fallthrough - case .seq_cst: return intrinsics.atomic_add_explicit(object, operand, .seq_cst) + case .seq_cst: return intrinsics.atomic_add_explicit(object, operand, .Seq_Cst) } } @@ -304,13 +304,13 @@ atomic_fetch_sub :: #force_inline proc(object: ^$T, operand: T) -> T { atomic_fetch_sub_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T { switch order { - case .relaxed: return intrinsics.atomic_sub_explicit(object, operand, .relaxed) - case .consume: return intrinsics.atomic_sub_explicit(object, operand, .consume) - case .acquire: return intrinsics.atomic_sub_explicit(object, operand, .acquire) - case .release: return intrinsics.atomic_sub_explicit(object, operand, .release) - case .acq_rel: return intrinsics.atomic_sub_explicit(object, operand, .acq_rel) + case .relaxed: return intrinsics.atomic_sub_explicit(object, operand, .Relaxed) + case .consume: return intrinsics.atomic_sub_explicit(object, operand, .Consume) + case .acquire: return intrinsics.atomic_sub_explicit(object, operand, .Acquire) + case .release: return intrinsics.atomic_sub_explicit(object, operand, .Release) + case .acq_rel: return intrinsics.atomic_sub_explicit(object, operand, .Acq_Rel) case: fallthrough - case .seq_cst: return intrinsics.atomic_sub_explicit(object, operand, .seq_cst) + case .seq_cst: return intrinsics.atomic_sub_explicit(object, operand, .Seq_Cst) } } @@ -320,13 +320,13 @@ atomic_fetch_or :: #force_inline proc(object: ^$T, operand: T) -> T { atomic_fetch_or_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T { switch order { - case .relaxed: return intrinsics.atomic_or_explicit(object, operand, .relaxed) - case .consume: return intrinsics.atomic_or_explicit(object, operand, .consume) - case .acquire: return intrinsics.atomic_or_explicit(object, operand, .acquire) - case .release: return intrinsics.atomic_or_explicit(object, operand, .release) - case .acq_rel: return intrinsics.atomic_or_explicit(object, operand, .acq_rel) + case .relaxed: return intrinsics.atomic_or_explicit(object, operand, .Relaxed) + case .consume: return intrinsics.atomic_or_explicit(object, operand, .Consume) + case .acquire: return intrinsics.atomic_or_explicit(object, operand, .Acquire) + case .release: return intrinsics.atomic_or_explicit(object, operand, .Release) + case .acq_rel: return intrinsics.atomic_or_explicit(object, operand, .Acq_Rel) case: fallthrough - case .seq_cst: return intrinsics.atomic_or_explicit(object, operand, .seq_cst) + case .seq_cst: return intrinsics.atomic_or_explicit(object, operand, .Seq_Cst) } } @@ -336,13 +336,13 @@ atomic_fetch_xor :: #force_inline proc(object: ^$T, operand: T) -> T { atomic_fetch_xor_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T { switch order { - case .relaxed: return intrinsics.atomic_xor_explicit(object, operand, .relaxed) - case .consume: return intrinsics.atomic_xor_explicit(object, operand, .consume) - case .acquire: return intrinsics.atomic_xor_explicit(object, operand, .acquire) - case .release: return intrinsics.atomic_xor_explicit(object, operand, .release) - case .acq_rel: return intrinsics.atomic_xor_explicit(object, operand, .acq_rel) + case .relaxed: return intrinsics.atomic_xor_explicit(object, operand, .Relaxed) + case .consume: return intrinsics.atomic_xor_explicit(object, operand, .Consume) + case .acquire: return intrinsics.atomic_xor_explicit(object, operand, .Acquire) + case .release: return intrinsics.atomic_xor_explicit(object, operand, .Release) + case .acq_rel: return intrinsics.atomic_xor_explicit(object, operand, .Acq_Rel) case: fallthrough - case .seq_cst: return intrinsics.atomic_xor_explicit(object, operand, .seq_cst) + case .seq_cst: return intrinsics.atomic_xor_explicit(object, operand, .Seq_Cst) } } @@ -351,13 +351,13 @@ atomic_fetch_and :: #force_inline proc(object: ^$T, operand: T) -> T { } atomic_fetch_and_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T { switch order { - case .relaxed: return intrinsics.atomic_and_explicit(object, operand, .relaxed) - case .consume: return intrinsics.atomic_and_explicit(object, operand, .consume) - case .acquire: return intrinsics.atomic_and_explicit(object, operand, .acquire) - case .release: return intrinsics.atomic_and_explicit(object, operand, .release) - case .acq_rel: return intrinsics.atomic_and_explicit(object, operand, .acq_rel) + case .relaxed: return intrinsics.atomic_and_explicit(object, operand, .Relaxed) + case .consume: return intrinsics.atomic_and_explicit(object, operand, .Consume) + case .acquire: return intrinsics.atomic_and_explicit(object, operand, .Acquire) + case .release: return intrinsics.atomic_and_explicit(object, operand, .Release) + case .acq_rel: return intrinsics.atomic_and_explicit(object, operand, .Acq_Rel) case: fallthrough - case .seq_cst: return intrinsics.atomic_and_explicit(object, operand, .seq_cst) + case .seq_cst: return intrinsics.atomic_and_explicit(object, operand, .Seq_Cst) } } diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index 4c1c166e6..ce8fd96ea 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -63,12 +63,12 @@ syscall :: proc(id: uintptr, args: ..uintptr) -> uintptr --- // Atomics Atomic_Memory_Order :: enum { - relaxed = 0, // unordered - consume = 1, // monotonic - acquire = 2, - release = 3, - acq_rel = 4, - seq_cst = 5, + Relaxed = 0, // Unordered + Consume = 1, // Monotonic + Acquire = 2, + Release = 3, + Acq_Rel = 4, + Seq_Cst = 5, } atomic_thread_fence :: proc(order: Atomic_Memory_Order) --- diff --git a/core/mem/mem.odin b/core/mem/mem.odin index 1ebe558ef..817c0ea5f 100644 --- a/core/mem/mem.odin +++ b/core/mem/mem.odin @@ -16,7 +16,7 @@ zero_explicit :: proc "contextless" (data: rawptr, len: int) -> rawptr { // equivalent semantics to those provided by the C11 Annex K 3.7.4.1 // memset_s call. intrinsics.mem_zero_volatile(data, len) // Use the volatile mem_zero - intrinsics.atomic_thread_fence(.seq_cst) // Prevent reordering + intrinsics.atomic_thread_fence(.Seq_Cst) // Prevent reordering return data } zero_item :: proc "contextless" (item: $P/^$T) { diff --git a/core/sync/atomic.odin b/core/sync/atomic.odin index d6521daed..f537764c4 100644 --- a/core/sync/atomic.odin +++ b/core/sync/atomic.odin @@ -6,12 +6,12 @@ cpu_relax :: intrinsics.cpu_relax /* Atomic_Memory_Order :: enum { - relaxed = 0, - consume = 1, - acquire = 2, - release = 3, - acq_rel = 4, - seq_cst = 5, + Relaxed = 0, + Consume = 1, + Acquire = 2, + Release = 3, + Acq_Rel = 4, + Seq_Cst = 5, } */ Atomic_Memory_Order :: intrinsics.Atomic_Memory_Order diff --git a/core/sync/extended.odin b/core/sync/extended.odin index c8ae4493f..24f7c096c 100644 --- a/core/sync/extended.odin +++ b/core/sync/extended.odin @@ -146,10 +146,10 @@ Auto_Reset_Event :: struct { } auto_reset_event_signal :: proc(e: ^Auto_Reset_Event) { - old_status := atomic_load_explicit(&e.status, .relaxed) + old_status := atomic_load_explicit(&e.status, .Relaxed) for { new_status := old_status + 1 if old_status < 1 else 1 - if _, ok := atomic_compare_exchange_weak_explicit(&e.status, old_status, new_status, .release, .relaxed); ok { + if _, ok := atomic_compare_exchange_weak_explicit(&e.status, old_status, new_status, .Release, .Relaxed); ok { break } @@ -160,7 +160,7 @@ auto_reset_event_signal :: proc(e: ^Auto_Reset_Event) { } auto_reset_event_wait :: proc(e: ^Auto_Reset_Event) { - old_status := atomic_sub_explicit(&e.status, 1, .acquire) + old_status := atomic_sub_explicit(&e.status, 1, .Acquire) if old_status < 1 { sema_wait(&e.sema) } @@ -174,14 +174,14 @@ Ticket_Mutex :: struct { } ticket_mutex_lock :: #force_inline proc(m: ^Ticket_Mutex) { - ticket := atomic_add_explicit(&m.ticket, 1, .relaxed) - for ticket != atomic_load_explicit(&m.serving, .acquire) { + ticket := atomic_add_explicit(&m.ticket, 1, .Relaxed) + for ticket != atomic_load_explicit(&m.serving, .Acquire) { cpu_relax() } } ticket_mutex_unlock :: #force_inline proc(m: ^Ticket_Mutex) { - atomic_add_explicit(&m.serving, 1, .relaxed) + atomic_add_explicit(&m.serving, 1, .Relaxed) } @(deferred_in=ticket_mutex_unlock) ticket_mutex_guard :: proc(m: ^Ticket_Mutex) -> bool { @@ -196,18 +196,18 @@ Benaphore :: struct { } benaphore_lock :: proc(b: ^Benaphore) { - if atomic_add_explicit(&b.counter, 1, .acquire) > 1 { + if atomic_add_explicit(&b.counter, 1, .Acquire) > 1 { sema_wait(&b.sema) } } benaphore_try_lock :: proc(b: ^Benaphore) -> bool { - v, _ := atomic_compare_exchange_strong_explicit(&b.counter, 1, 0, .acquire, .acquire) + v, _ := atomic_compare_exchange_strong_explicit(&b.counter, 1, 0, .Acquire, .Acquire) return v == 0 } benaphore_unlock :: proc(b: ^Benaphore) { - if atomic_sub_explicit(&b.counter, 1, .release) > 0 { + if atomic_sub_explicit(&b.counter, 1, .Release) > 0 { sema_post(&b.sema) } } @@ -227,7 +227,7 @@ Recursive_Benaphore :: struct { recursive_benaphore_lock :: proc(b: ^Recursive_Benaphore) { tid := current_thread_id() - if atomic_add_explicit(&b.counter, 1, .acquire) > 1 { + if atomic_add_explicit(&b.counter, 1, .Acquire) > 1 { if tid != b.owner { sema_wait(&b.sema) } @@ -240,10 +240,10 @@ recursive_benaphore_lock :: proc(b: ^Recursive_Benaphore) { recursive_benaphore_try_lock :: proc(b: ^Recursive_Benaphore) -> bool { tid := current_thread_id() if b.owner == tid { - atomic_add_explicit(&b.counter, 1, .acquire) + atomic_add_explicit(&b.counter, 1, .Acquire) } - if v, _ := atomic_compare_exchange_strong_explicit(&b.counter, 1, 0, .acquire, .acquire); v != 0 { + if v, _ := atomic_compare_exchange_strong_explicit(&b.counter, 1, 0, .Acquire, .Acquire); v != 0 { return false } // inside the lock @@ -260,7 +260,7 @@ recursive_benaphore_unlock :: proc(b: ^Recursive_Benaphore) { if recursion == 0 { b.owner = 0 } - if atomic_sub_explicit(&b.counter, 1, .release) > 0 { + if atomic_sub_explicit(&b.counter, 1, .Release) > 0 { if recursion == 0 { sema_post(&b.sema) } @@ -293,12 +293,12 @@ once_do :: proc(o: ^Once, fn: proc()) { defer mutex_unlock(&o.m) if !o.done { fn() - atomic_store_explicit(&o.done, true, .release) + atomic_store_explicit(&o.done, true, .Release) } } - if atomic_load_explicit(&o.done, .acquire) == false { + if atomic_load_explicit(&o.done, .Acquire) == false { do_slow(o, fn) } } diff --git a/core/sync/primitives_atomic.odin b/core/sync/primitives_atomic.odin index 401f78b91..11fff4e60 100644 --- a/core/sync/primitives_atomic.odin +++ b/core/sync/primitives_atomic.odin @@ -24,7 +24,7 @@ atomic_mutex_lock :: proc(m: ^Atomic_Mutex) { new_state := curr_state // Make a copy of it spin_lock: for spin in 0.. bool { - _, ok := atomic_compare_exchange_strong_explicit(&m.state, .Unlocked, .Locked, .acquire, .consume) + _, ok := atomic_compare_exchange_strong_explicit(&m.state, .Unlocked, .Locked, .Acquire, .Consume) return ok } @@ -290,7 +290,7 @@ Queue_Item :: struct { @(private="file") queue_item_wait :: proc(item: ^Queue_Item) { - for atomic_load_explicit(&item.futex, .acquire) == 0 { + for atomic_load_explicit(&item.futex, .Acquire) == 0 { futex_wait(&item.futex, 0) cpu_relax() } @@ -298,7 +298,7 @@ queue_item_wait :: proc(item: ^Queue_Item) { @(private="file") queue_item_wait_with_timeout :: proc(item: ^Queue_Item, duration: time.Duration) -> bool { start := time.tick_now() - for atomic_load_explicit(&item.futex, .acquire) == 0 { + for atomic_load_explicit(&item.futex, .Acquire) == 0 { remaining := duration - time.tick_since(start) if remaining < 0 { return false @@ -312,7 +312,7 @@ queue_item_wait_with_timeout :: proc(item: ^Queue_Item, duration: time.Duration) } @(private="file") queue_item_signal :: proc(item: ^Queue_Item) { - atomic_store_explicit(&item.futex, 1, .release) + atomic_store_explicit(&item.futex, 1, .Release) futex_signal(&item.futex) } diff --git a/core/sync/primitives_internal.odin b/core/sync/primitives_internal.odin index c1a38b4ad..4807b41c1 100644 --- a/core/sync/primitives_internal.odin +++ b/core/sync/primitives_internal.odin @@ -10,7 +10,7 @@ when #config(ODIN_SYNC_RECURSIVE_MUTEX_USE_FUTEX, true) { _recursive_mutex_lock :: proc(m: ^Recursive_Mutex) { tid := Futex(current_thread_id()) for { - prev_owner := atomic_compare_exchange_strong_explicit(&m.impl.owner, tid, 0, .acquire, .acquire) + prev_owner := atomic_compare_exchange_strong_explicit(&m.impl.owner, tid, 0, .Acquire, .Acquire) switch prev_owner { case 0, tid: m.impl.recursion += 1 @@ -27,7 +27,7 @@ when #config(ODIN_SYNC_RECURSIVE_MUTEX_USE_FUTEX, true) { if m.impl.recursion != 0 { return } - atomic_exchange_explicit(&m.impl.owner, 0, .release) + atomic_exchange_explicit(&m.impl.owner, 0, .Release) futex_signal(&m.impl.owner) // outside the lock @@ -36,7 +36,7 @@ when #config(ODIN_SYNC_RECURSIVE_MUTEX_USE_FUTEX, true) { _recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool { tid := Futex(current_thread_id()) - prev_owner := atomic_compare_exchange_strong_explicit(&m.impl.owner, tid, 0, .acquire, .acquire) + prev_owner := atomic_compare_exchange_strong_explicit(&m.impl.owner, tid, 0, .Acquire, .Acquire) switch prev_owner { case 0, tid: m.impl.recursion += 1 diff --git a/src/checker.cpp b/src/checker.cpp index 0c72a8e14..1bb786ea1 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -992,12 +992,12 @@ void init_universal(void) { { GlobalEnumValue values[OdinAtomicMemoryOrder_COUNT] = { - {"relaxed", OdinAtomicMemoryOrder_relaxed}, - {"consume", OdinAtomicMemoryOrder_consume}, - {"acquire", OdinAtomicMemoryOrder_acquire}, - {"release", OdinAtomicMemoryOrder_release}, - {"acq_rel", OdinAtomicMemoryOrder_acq_rel}, - {"seq_cst", OdinAtomicMemoryOrder_seq_cst}, + {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_relaxed], OdinAtomicMemoryOrder_relaxed}, + {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_consume], OdinAtomicMemoryOrder_consume}, + {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_acquire], OdinAtomicMemoryOrder_acquire}, + {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_release], OdinAtomicMemoryOrder_release}, + {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_acq_rel], OdinAtomicMemoryOrder_acq_rel}, + {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_seq_cst], OdinAtomicMemoryOrder_seq_cst}, }; add_global_enum_type(str_lit("Atomic_Memory_Order"), values, gb_count_of(values), &t_atomic_memory_order); diff --git a/src/types.cpp b/src/types.cpp index 16d07d392..e10dae1ed 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -703,12 +703,12 @@ enum OdinAtomicMemoryOrder : i32 { }; char const *OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_COUNT] = { - "relaxed", - "consume", - "acquire", - "release", - "acq_rel", - "seq_cst", + "Relaxed", + "Consume", + "Acquire", + "Release", + "Acq_Rel", + "Seq_Cst", }; gb_global Type *t_atomic_memory_order = nullptr; -- cgit v1.2.3 From a232c0888c4b711ceb94563defdeef514eafb28a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 2 Apr 2022 14:38:42 +0100 Subject: `intrinsics.atomic_type_is_lock_free` --- core/c/libc/stdatomic.odin | 2 +- core/intrinsics/intrinsics.odin | 2 ++ src/check_builtin.cpp | 30 ++++++++++++++++++++++++++++++ src/checker_builtin_procs.hpp | 2 ++ src/entity.cpp | 6 +++--- src/llvm_backend_proc.cpp | 2 +- src/types.cpp | 11 +++++++++++ 7 files changed, 50 insertions(+), 5 deletions(-) (limited to 'src/types.cpp') diff --git a/core/c/libc/stdatomic.odin b/core/c/libc/stdatomic.odin index 9de3c4678..6e1581c58 100644 --- a/core/c/libc/stdatomic.odin +++ b/core/c/libc/stdatomic.odin @@ -70,7 +70,7 @@ atomic_signal_fence :: #force_inline proc(order: memory_order) { // 7.17.5 Lock-free property atomic_is_lock_free :: #force_inline proc(obj: ^$T) -> bool { - return size_of(T) <= 8 && (intrinsics.type_is_integer(T) || intrinsics.type_is_pointer(T)) + return intrinsics.atomic_type_is_lock_free(T) } // 7.17.6 Atomic integer types diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index ce8fd96ea..a25e9783d 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -71,6 +71,8 @@ Atomic_Memory_Order :: enum { Seq_Cst = 5, } +atomic_type_is_lock_free :: proc($T: typeid) -> bool --- + atomic_thread_fence :: proc(order: Atomic_Memory_Order) --- atomic_signal_fence :: proc(order: Atomic_Memory_Order) --- diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 8b8814176..abe58855b 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -449,6 +449,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_objc_find_class: case BuiltinProc_objc_register_selector: case BuiltinProc_objc_register_class: + case BuiltinProc_atomic_type_is_lock_free: // NOTE(bill): The first arg may be a Type, this will be checked case by case break; @@ -3232,6 +3233,35 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; + case BuiltinProc_atomic_type_is_lock_free: + { + Ast *expr = ce->args[0]; + Operand o = {}; + check_expr_or_type(c, &o, expr); + + if (o.mode == Addressing_Invalid || o.mode == Addressing_Builtin) { + return false; + } + if (o.type == nullptr || o.type == t_invalid || is_type_asm_proc(o.type)) { + error(o.expr, "Invalid argument to '%.*s'", LIT(builtin_name)); + return false; + } + if (is_type_polymorphic(o.type)) { + error(o.expr, "'%.*s' of polymorphic type cannot be determined", LIT(builtin_name)); + return false; + } + if (is_type_untyped(o.type)) { + error(o.expr, "'%.*s' of untyped type is not allowed", LIT(builtin_name)); + return false; + } + Type *t = o.type; + bool is_lock_free = is_type_lock_free(t); + + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + operand->value = exact_value_bool(is_lock_free); + break; + } case BuiltinProc_atomic_thread_fence: case BuiltinProc_atomic_signal_fence: diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index c6b0afee0..fe14ae372 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -86,6 +86,7 @@ enum BuiltinProcId { BuiltinProc_prefetch_write_instruction, BuiltinProc_prefetch_write_data, + BuiltinProc_atomic_type_is_lock_free, BuiltinProc_atomic_thread_fence, BuiltinProc_atomic_signal_fence, BuiltinProc_atomic_store, @@ -305,6 +306,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("prefetch_write_instruction"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("prefetch_write_data"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_type_is_lock_free"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_thread_fence"), 1, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_signal_fence"), 1, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("atomic_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, diff --git a/src/entity.cpp b/src/entity.cpp index 17fa884e1..1f87f7af6 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -45,9 +45,9 @@ enum EntityFlag : u64 { EntityFlag_NoAlias = 1ull<<9, EntityFlag_TypeField = 1ull<<10, EntityFlag_Value = 1ull<<11, - EntityFlag_Sret = 1ull<<12, - EntityFlag_ByVal = 1ull<<13, - EntityFlag_BitFieldValue = 1ull<<14, + + + EntityFlag_PolyConst = 1ull<<15, EntityFlag_NotExported = 1ull<<16, EntityFlag_ConstInput = 1ull<<17, diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 4de2af9d8..96ff19d10 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -447,7 +447,7 @@ void lb_begin_procedure_body(lbProcedure *p) { Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(p->type->Proc.results)); Entity *e = alloc_entity_param(nullptr, make_token_ident(name), ptr_type, false, false); - e->flags |= EntityFlag_Sret | EntityFlag_NoAlias; + e->flags |= EntityFlag_NoAlias; return_ptr_value.value = LLVMGetParam(p->value, 0); LLVMSetValueName2(return_ptr_value.value, cast(char const *)name.text, name.len); diff --git a/src/types.cpp b/src/types.cpp index e10dae1ed..b4dc17256 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2218,6 +2218,17 @@ bool elem_type_can_be_constant(Type *t) { return true; } +bool is_type_lock_free(Type *t) { + t = core_type(t); + if (t == t_invalid) { + return false; + } + i64 sz = type_size_of(t); + // TODO(bill): Figure this out correctly + return sz <= build_context.max_align; +} + + bool is_type_comparable(Type *t) { t = base_type(t); -- cgit v1.2.3 From 3ec70c5517062f3d35822253b2072df696b0c55f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 23 May 2022 12:04:19 +0100 Subject: Merge functionality of `#maybe` with the standard 'union' functionality --- core/encoding/json/unmarshal.odin | 2 +- core/reflect/reflect.odin | 2 +- core/reflect/types.odin | 16 +++++++++++++--- core/runtime/core.odin | 1 - core/runtime/core_builtin.odin | 2 +- src/check_expr.cpp | 1 - src/docs_writer.cpp | 1 - src/llvm_backend_type.cpp | 5 ++--- src/parser.cpp | 1 + src/parser.hpp | 2 +- src/types.cpp | 19 +++++++------------ 11 files changed, 27 insertions(+), 25 deletions(-) (limited to 'src/types.cpp') diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin index bd48011f1..2ff268a21 100644 --- a/core/encoding/json/unmarshal.odin +++ b/core/encoding/json/unmarshal.odin @@ -209,7 +209,7 @@ unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) { variant := u.variants[0] v.id = variant.id ti = reflect.type_info_base(variant) - if !(u.maybe && reflect.is_pointer(variant)) { + if !reflect.is_pointer_internally(variant) { tag := any{rawptr(uintptr(v.data) + u.tag_offset), u.tag_type.id} assign_int(tag, 1) } diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin index 49d7ef9b5..27a83e680 100644 --- a/core/reflect/reflect.odin +++ b/core/reflect/reflect.odin @@ -654,7 +654,7 @@ union_variant_type_info :: proc(a: any) -> ^Type_Info { } type_info_union_is_pure_maybe :: proc(info: runtime.Type_Info_Union) -> bool { - return info.maybe && len(info.variants) == 1 && is_pointer(info.variants[0]) + return len(info.variants) == 1 && is_pointer(info.variants[0]) } union_variant_typeid :: proc(a: any) -> typeid { diff --git a/core/reflect/types.odin b/core/reflect/types.odin index 2e2149820..b211abb45 100644 --- a/core/reflect/types.odin +++ b/core/reflect/types.odin @@ -256,6 +256,17 @@ is_multi_pointer :: proc(info: ^Type_Info) -> bool { _, ok := type_info_base(info).variant.(Type_Info_Multi_Pointer) return ok } +is_pointer_internally :: proc(info: ^Type_Info) -> bool { + if info == nil { return false } + #partial switch v in info.variant { + case Type_Info_Pointer, Type_Info_Multi_Pointer, + Type_Info_Procedure: + return true + case Type_Info_String: + return v.is_cstring + } + return false +} is_procedure :: proc(info: ^Type_Info) -> bool { if info == nil { return false } _, ok := type_info_base(info).variant.(Type_Info_Procedure) @@ -531,9 +542,8 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) - case Type_Info_Union: io.write_string(w, "union ", &n) or_return - if info.maybe { - io.write_string(w, "#maybe ", &n) or_return - } + if info.no_nil { io.write_string(w, "#no_nil ", &n) or_return } + if info.shared_nil { io.write_string(w, "#shared_nil ", &n) or_return } if info.custom_align { io.write_string(w, "#align ", &n) or_return io.write_i64(w, i64(ti.align), 10, &n) or_return diff --git a/core/runtime/core.odin b/core/runtime/core.odin index e2933d20a..73d1e6371 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -135,7 +135,6 @@ Type_Info_Union :: struct { custom_align: bool, no_nil: bool, - maybe: bool, shared_nil: bool, } Type_Info_Enum :: struct { diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin index cb72e397d..4ddc3928a 100644 --- a/core/runtime/core_builtin.odin +++ b/core/runtime/core_builtin.odin @@ -3,7 +3,7 @@ package runtime import "core:intrinsics" @builtin -Maybe :: union($T: typeid) #maybe {T} +Maybe :: union($T: typeid) {T} @builtin diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 013d22913..b911771b1 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -10063,7 +10063,6 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) { str = gb_string_appendc(str, ") "); } switch (st->kind) { - case UnionType_maybe: str = gb_string_appendc(str, "#maybe "); break; case UnionType_no_nil: str = gb_string_appendc(str, "#no_nil "); break; case UnionType_shared_nil: str = gb_string_appendc(str, "#shared_nil "); break; } diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp index 0ad10ac49..2f531a45c 100644 --- a/src/docs_writer.cpp +++ b/src/docs_writer.cpp @@ -620,7 +620,6 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) { doc_type.kind = OdinDocType_Union; if (type->Union.is_polymorphic) { doc_type.flags |= OdinDocTypeFlag_Union_polymorphic; } switch (type->Union.kind) { - case UnionType_maybe: doc_type.flags |= OdinDocTypeFlag_Union_maybe; break; case UnionType_no_nil: doc_type.flags |= OdinDocTypeFlag_Union_no_nil; break; case UnionType_shared_nil: doc_type.flags |= OdinDocTypeFlag_Union_shared_nil; break; } diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index 7d73956e8..2e7b2788a 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -641,7 +641,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_union_ptr); { - LLVMValueRef vals[8] = {}; + LLVMValueRef vals[7] = {}; isize variant_count = gb_max(0, t->Union.variants.count); lbValue memory_types = lb_type_info_member_types_offset(p, variant_count); @@ -676,8 +676,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da vals[4] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value; vals[5] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_no_nil).value; - vals[6] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_maybe).value; - vals[7] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_shared_nil).value; + vals[6] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_shared_nil).value; for (isize i = 0; i < gb_count_of(vals); i++) { if (vals[i] == nullptr) { diff --git a/src/parser.cpp b/src/parser.cpp index ab947774b..d19e249e5 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2529,6 +2529,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { if (maybe) { union_kind = UnionType_maybe; + syntax_error(f->curr_token, "#maybe functionality has now been merged with standard 'union' functionality"); } else if (no_nil) { union_kind = UnionType_no_nil; } else if (shared_nil) { diff --git a/src/parser.hpp b/src/parser.hpp index 698ed7623..dc294b6ce 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -332,7 +332,7 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = { enum UnionTypeKind : u8 { UnionType_Normal = 0, - UnionType_maybe = 1, + UnionType_maybe = 1, // removed UnionType_no_nil = 2, UnionType_shared_nil = 3, }; diff --git a/src/types.cpp b/src/types.cpp index b4dc17256..c79b8e652 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1685,11 +1685,9 @@ bool is_type_map(Type *t) { bool is_type_union_maybe_pointer(Type *t) { t = base_type(t); - if (t->kind == Type_Union && t->Union.kind == UnionType_maybe) { - if (t->Union.variants.count == 1) { - Type *v = t->Union.variants[0]; - return is_type_pointer(v) || is_type_multi_pointer(v); - } + if (t->kind == Type_Union && t->Union.variants.count == 1) { + Type *v = t->Union.variants[0]; + return is_type_internally_pointer_like(v); } return false; } @@ -1697,12 +1695,10 @@ bool is_type_union_maybe_pointer(Type *t) { bool is_type_union_maybe_pointer_original_alignment(Type *t) { t = base_type(t); - if (t->kind == Type_Union && t->Union.kind == UnionType_maybe) { - if (t->Union.variants.count == 1) { - Type *v = t->Union.variants[0]; - if (is_type_pointer(v) || is_type_multi_pointer(v)) { - return type_align_of(v) == type_align_of(t); - } + if (t->kind == Type_Union && t->Union.variants.count == 1) { + Type *v = t->Union.variants[0]; + if (is_type_internally_pointer_like(v)) { + return type_align_of(v) == type_align_of(t); } } return false; @@ -4054,7 +4050,6 @@ gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) { case Type_Union: str = gb_string_appendc(str, "union"); switch (type->Union.kind) { - case UnionType_maybe: str = gb_string_appendc(str, " #maybe"); break; case UnionType_no_nil: str = gb_string_appendc(str, " #no_nil"); break; case UnionType_shared_nil: str = gb_string_appendc(str, " #shared_nil"); break; } -- cgit v1.2.3 From b032d5af87ebe8d9dee28698cfa570d3628e58e5 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 May 2022 17:26:18 +0100 Subject: Make `#simd` an opaque type --- src/check_decl.cpp | 20 +++++++++++++------- src/check_expr.cpp | 24 ++++++++---------------- src/check_type.cpp | 5 +++++ src/common.cpp | 7 +++++++ src/types.cpp | 2 +- 5 files changed, 34 insertions(+), 24 deletions(-) (limited to 'src/types.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 82ac6c677..d8cad2ce1 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -313,13 +313,19 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def) } named->Named.base = base; - if (is_distinct && is_type_typeid(e->type)) { - error(init_expr, "'distinct' cannot be applied to 'typeid'"); - is_distinct = false; - } - if (is_distinct && is_type_any(e->type)) { - error(init_expr, "'distinct' cannot be applied to 'any'"); - is_distinct = false; + if (is_distinct) { + if (is_type_typeid(e->type)) { + error(init_expr, "'distinct' cannot be applied to 'typeid'"); + is_distinct = false; + } else if (is_type_any(e->type)) { + error(init_expr, "'distinct' cannot be applied to 'any'"); + is_distinct = false; + } else if (is_type_simd_vector(e->type)) { + gbString str = type_to_string(e->type); + error(init_expr, "'distinct' cannot be applied to '%s'", str); + gb_string_free(str); + is_distinct = false; + } } if (!is_distinct) { e->type = bt; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 7b269e048..a4dfade98 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1567,9 +1567,16 @@ bool check_unary_op(CheckerContext *c, Operand *o, Token op) { bool check_binary_op(CheckerContext *c, Operand *o, Token op) { Type *main_type = o->type; + + if (is_type_simd_vector(main_type)) { + error(op, "Operator '%.*s' is not supported on #simd vector types, please use the intrinsics.simd_*", LIT(op.string)); + return false; + } + // TODO(bill): Handle errors correctly Type *type = base_type(core_array_type(main_type)); Type *ct = core_type(type); + switch (op.kind) { case Token_Sub: case Token_SubEq: @@ -1638,14 +1645,6 @@ bool check_binary_op(CheckerContext *c, Operand *o, Token op) { error(op, "Operator '%.*s' is only allowed with integers", LIT(op.string)); return false; } - if (is_type_simd_vector(o->type)) { - switch (op.kind) { - case Token_ModMod: - case Token_ModModEq: - error(op, "Operator '%.*s' is only allowed with integers", LIT(op.string)); - return false; - } - } break; case Token_AndNot: @@ -1654,14 +1653,6 @@ bool check_binary_op(CheckerContext *c, Operand *o, Token op) { error(op, "Operator '%.*s' is only allowed with integers and bit sets", LIT(op.string)); return false; } - if (is_type_simd_vector(o->type)) { - switch (op.kind) { - case Token_AndNot: - case Token_AndNotEq: - error(op, "Operator '%.*s' is only allowed with integers", LIT(op.string)); - return false; - } - } break; case Token_CmpAnd: @@ -7738,6 +7729,7 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type * } if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) { + // TODO(bill): Why was this decision made for simd? if (is_type_simd_vector(t)) { error(cl->elems[0], "'field = value' is not allowed for SIMD vector literals"); } else { diff --git a/src/check_type.cpp b/src/check_type.cpp index 51f472961..193c42cde 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2802,6 +2802,11 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t *type = alloc_type_array(elem, count, generic_type); goto array_end; } + if (count < 1 || !is_power_of_two(count)) { + error(at->elem, "Invalid length for 'intrinsics.simd_vector', expected a power of two length, got '%lld'", cast(long long)count); + *type = alloc_type_array(elem, count, generic_type); + goto array_end; + } *type = alloc_type_simd_vector(count, elem); } else { diff --git a/src/common.cpp b/src/common.cpp index 94248fb62..77caddfe8 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -47,6 +47,13 @@ void debugf(char const *fmt, ...); #include "range_cache.cpp" +bool is_power_of_two(i64 x) { + if (x <= 0) { + return false; + } + return !(x & (x-1)); +} + int isize_cmp(isize x, isize y) { if (x < y) { return -1; diff --git a/src/types.cpp b/src/types.cpp index c79b8e652..d5ba1a531 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -3446,7 +3446,7 @@ i64 type_align_of_internal(Type *t, TypePath *path) { case Type_SimdVector: { // IMPORTANT TODO(bill): Figure out the alignment of vector types - return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_align); + return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_align*2); } case Type_Matrix: -- cgit v1.2.3 From 3b54015e80316af8c13fd83f615b64b611508275 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 May 2022 17:54:05 +0100 Subject: Mock out simd intrinsics --- src/check_builtin.cpp | 209 +++++++++++++++++++++++++++++++++++++++++- src/check_type.cpp | 2 +- src/checker_builtin_procs.hpp | 57 +++++++++++- src/types.cpp | 2 +- 4 files changed, 265 insertions(+), 5 deletions(-) (limited to 'src/types.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 55dd6b016..939892707 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -246,7 +246,7 @@ bool is_constant_string(CheckerContext *c, String const &builtin_name, Ast *expr } bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) { - String builtin_name = builtin_procs[id].name; + String const &builtin_name = builtin_procs[id].name; if (build_context.metrics.os != TargetOs_darwin) { // allow on doc generation (e.g. Metal stuff) @@ -409,6 +409,194 @@ bool check_atomic_memory_order_argument(CheckerContext *c, Ast *expr, String con } + +bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) { + ast_node(ce, CallExpr, call); + + String const &builtin_name = builtin_procs[id].name; + switch (id) { + // Any numeric + case BuiltinProc_simd_add: + case BuiltinProc_simd_sub: + case BuiltinProc_simd_mul: + case BuiltinProc_simd_div: + case BuiltinProc_simd_min: + case BuiltinProc_simd_max: + { + 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; + } + if (!is_type_simd_vector(x.type)) { + error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + if (!is_type_simd_vector(y.type)) { + error(y.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + if (!are_types_identical(x.type, y.type)) { + gbString xs = type_to_string(x.type); + gbString ys = type_to_string(y.type); + error(x.expr, "'%.*s' expected 2 arguments of the same type, got '%s' vs '%s'", LIT(builtin_name), xs, ys); + gb_string_free(ys); + gb_string_free(xs); + return false; + } + Type *elem = base_array_type(x.type); + if (!is_type_integer(elem) && !is_type_float(elem)) { + gbString xs = type_to_string(x.type); + error(x.expr, "'%.*s' expected a #simd type with an integer or floating-point element, got '%s'", LIT(builtin_name), xs); + gb_string_free(xs); + return false; + } + + operand->mode = Addressing_Value; + operand->type = x.type; + return true; + } + + // Integer only + case BuiltinProc_simd_rem: + case BuiltinProc_simd_shl: + case BuiltinProc_simd_shr: + case BuiltinProc_simd_shl_masked: + case BuiltinProc_simd_shr_masked: + case BuiltinProc_simd_and: + case BuiltinProc_simd_or: + case BuiltinProc_simd_xor: + { + 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; + } + if (!is_type_simd_vector(x.type)) { + error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + if (!is_type_simd_vector(y.type)) { + error(y.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + if (!are_types_identical(x.type, y.type)) { + gbString xs = type_to_string(x.type); + gbString ys = type_to_string(y.type); + error(x.expr, "'%.*s' expected 2 arguments of the same type, got '%s' vs '%s'", LIT(builtin_name), xs, ys); + gb_string_free(ys); + gb_string_free(xs); + return false; + } + Type *elem = base_array_type(x.type); + if (!is_type_integer(elem)) { + gbString xs = type_to_string(x.type); + error(x.expr, "'%.*s' expected a #simd type with an integer element, got '%s'", LIT(builtin_name), xs); + gb_string_free(xs); + return false; + } + + operand->mode = Addressing_Value; + operand->type = x.type; + return true; + } + // Unary + case BuiltinProc_simd_neg: + case BuiltinProc_simd_abs: + { + Operand x = {}; + check_expr(c, &x, ce->args[0]); + if (x.mode == Addressing_Invalid) { + return false; + } + if (!is_type_simd_vector(x.type)) { + error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + Type *elem = base_array_type(x.type); + if (!is_type_integer(elem) && !is_type_float(elem)) { + gbString xs = type_to_string(x.type); + error(x.expr, "'%.*s' expected a #simd type with an integer or floating-point element, got '%s'", LIT(builtin_name), xs); + gb_string_free(xs); + return false; + } + operand->mode = Addressing_Value; + operand->type = x.type; + return true; + } + + // Return integer masks + case BuiltinProc_simd_eq: + case BuiltinProc_simd_ne: + case BuiltinProc_simd_lt: + case BuiltinProc_simd_le: + case BuiltinProc_simd_gt: + case BuiltinProc_simd_ge: + { + // op(#simd[N]T, #simd[N]T) -> #simd[N]V + // where `V` is an integer, `size_of(T) == size_of(V)` + // `V` will all 0s if false and all 1s if true (e.g. 0x00 and 0xff for false and true, respectively) + + 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; + } + if (!is_type_simd_vector(x.type)) { + error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + Type *elem = base_array_type(x.type); + if (!is_type_integer(elem) && !is_type_float(elem)) { + gbString xs = type_to_string(x.type); + error(x.expr, "'%.*s' expected a #simd type with an integer or floating-point element, got '%s'", LIT(builtin_name), xs); + gb_string_free(xs); + return false; + } + Type *vt = base_type(x.type); + GB_ASSERT(vt->kind == Type_SimdVector); + i64 count = vt->SimdVector.count; + + i64 sz = type_size_of(elem); + Type *new_elem = nullptr; + + switch (sz) { + case 1: new_elem = t_u8; break; + case 2: new_elem = t_u16; break; + case 4: new_elem = t_u32; break; + case 8: new_elem = t_u64; break; + case 16: + error(x.expr, "'%.*s' not supported 128-bit integer backed simd vector types", LIT(builtin_name)); + return false; + } + + operand->mode = Addressing_Value; + operand->type = alloc_type_simd_vector(count, new_elem); + return true; + } + default: + GB_PANIC("Unhandled simd intrinsic: %.*s", LIT(builtin_name)); + } + + return false; +} + + bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) { ast_node(ce, CallExpr, call); if (ce->inlining != ProcInlining_none) { @@ -479,7 +667,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; } - String builtin_name = builtin_procs[id].name; + String const &builtin_name = builtin_procs[id].name; if (ce->args.count > 0) { @@ -491,6 +679,16 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } } + if (BuiltinProc__simd_begin < id && id < BuiltinProc__simd_end) { + bool ok = check_builtin_simd_operation(c, operand, call, id, type_hint); + if (!ok) { + operand->type = t_invalid; + } + operand->mode = Addressing_Value; + operand->value = {}; + return ok; + } + switch (id) { default: GB_PANIC("Implement built-in procedure: %.*s", LIT(builtin_name)); @@ -2720,6 +2918,13 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 return false; } + if (count < 1 || !is_power_of_two(count)) { + error(call, "Invalid length for 'intrinsics.simd_vector', expected a power of two length, got '%lld'", cast(long long)count); + operand->mode = Addressing_Type; + operand->type = t_invalid; + return false; + } + operand->mode = Addressing_Type; operand->type = alloc_type_simd_vector(count, elem); break; diff --git a/src/check_type.cpp b/src/check_type.cpp index 193c42cde..1df63e599 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2803,7 +2803,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t goto array_end; } if (count < 1 || !is_power_of_two(count)) { - error(at->elem, "Invalid length for 'intrinsics.simd_vector', expected a power of two length, got '%lld'", cast(long long)count); + error(at->count, "Invalid length for 'intrinsics.simd_vector', expected a power of two length, got '%lld'", cast(long long)count); *type = alloc_type_array(elem, count, generic_type); goto array_end; } diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index d407ef7c1..80467ffb1 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -118,6 +118,35 @@ enum BuiltinProcId { BuiltinProc_fixed_point_div_sat, BuiltinProc_expect, + +BuiltinProc__simd_begin, + BuiltinProc_simd_add, + BuiltinProc_simd_sub, + BuiltinProc_simd_mul, + BuiltinProc_simd_div, + BuiltinProc_simd_rem, + BuiltinProc_simd_shl, // Odin logic + BuiltinProc_simd_shr, // Odin logic + BuiltinProc_simd_shl_masked, // C logic + BuiltinProc_simd_shr_masked, // C logic + + BuiltinProc_simd_and, + BuiltinProc_simd_or, + BuiltinProc_simd_xor, + + BuiltinProc_simd_neg, + BuiltinProc_simd_abs, + + BuiltinProc_simd_min, + BuiltinProc_simd_max, + + BuiltinProc_simd_eq, + BuiltinProc_simd_ne, + BuiltinProc_simd_lt, + BuiltinProc_simd_le, + BuiltinProc_simd_gt, + BuiltinProc_simd_ge, +BuiltinProc__simd_end, // Platform specific intrinsics BuiltinProc_syscall, @@ -342,7 +371,33 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {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("simd_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_mul"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_div"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_rem"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_shl"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_shr"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_shl_masked"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_shr_masked"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_neg"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_abs"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_min"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_max"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_eq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_ne"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_lt"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_le"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_gt"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_ge"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + + {STR_LIT("syscall"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/types.cpp b/src/types.cpp index d5ba1a531..755f78f1c 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1932,7 +1932,7 @@ bool is_type_valid_vector_elem(Type *t) { return false; } if (is_type_integer(t)) { - return true; + return !is_type_integer_128bit(t); } if (is_type_float(t)) { return true; -- cgit v1.2.3 From 63cc8a80a045d48960d85640d11f39237c2f8ca4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 May 2022 21:29:45 +0100 Subject: Correct parapoly for #simd --- src/check_expr.cpp | 13 +++++++++++++ src/check_type.cpp | 4 ++-- src/types.cpp | 9 ++++++++- 3 files changed, 23 insertions(+), 3 deletions(-) (limited to 'src/types.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index a30f83e7e..fcd7818bc 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1328,6 +1328,19 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source, } } return false; + + case Type_SimdVector: + if (source->kind == Type_SimdVector) { + if (poly->SimdVector.generic_count != nullptr) { + if (!polymorphic_assign_index(&poly->SimdVector.generic_count, &poly->SimdVector.count, source->SimdVector.count)) { + return false; + } + } + if (poly->SimdVector.count == source->SimdVector.count) { + return is_polymorphic_type_assignable(c, poly->SimdVector.elem, source->SimdVector.elem, true, modify_type); + } + } + return false; } return false; } diff --git a/src/check_type.cpp b/src/check_type.cpp index 088853810..354ab6e94 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2803,14 +2803,14 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t goto array_end; } if (is_type_polymorphic(elem)) { - count = 1; + // Ignore } else if (count < 1 || !is_power_of_two(count)) { error(at->count, "Invalid length for 'intrinsics.simd_vector', expected a power of two length, got '%lld'", cast(long long)count); *type = alloc_type_array(elem, count, generic_type); goto array_end; } - *type = alloc_type_simd_vector(count, elem); + *type = alloc_type_simd_vector(count, elem, generic_type); } else { error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name)); *type = alloc_type_array(elem, count, generic_type); diff --git a/src/types.cpp b/src/types.cpp index 755f78f1c..2d5709b19 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -261,6 +261,7 @@ struct TypeProc { TYPE_KIND(SimdVector, struct { \ i64 count; \ Type *elem; \ + Type *generic_count; \ }) \ TYPE_KIND(RelativePointer, struct { \ Type *pointer_type; \ @@ -1085,10 +1086,11 @@ Type *alloc_type_bit_set() { -Type *alloc_type_simd_vector(i64 count, Type *elem) { +Type *alloc_type_simd_vector(i64 count, Type *elem, Type *generic_count=nullptr) { Type *t = alloc_type(Type_SimdVector); t->SimdVector.count = count; t->SimdVector.elem = elem; + t->SimdVector.generic_count = generic_count; return t; } @@ -2078,6 +2080,11 @@ bool is_type_polymorphic(Type *t, bool or_specialized=false) { return true; } return is_type_polymorphic(t->Array.elem, or_specialized); + case Type_SimdVector: + if (t->SimdVector.generic_count != nullptr) { + return true; + } + return is_type_polymorphic(t->SimdVector.elem, or_specialized); case Type_DynamicArray: return is_type_polymorphic(t->DynamicArray.elem, or_specialized); case Type_Slice: -- cgit v1.2.3 From 808ea30b48b35d1556afbddcd49839ea9014d76e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 May 2022 22:16:44 +0100 Subject: Allow booleans for #simd --- src/check_builtin.cpp | 79 ++++++++++++++++++++++++++++++--------------------- src/check_type.cpp | 2 +- src/types.cpp | 3 ++ 3 files changed, 50 insertions(+), 34 deletions(-) (limited to 'src/types.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index a499937b2..e4fd504b3 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -447,7 +447,7 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call Type *elem = base_array_type(x.type); if (!is_type_integer(elem) && !is_type_float(elem)) { gbString xs = type_to_string(x.type); - error(x.expr, "'%.*s' expected a #simd type with an integer or floating-point element, got '%s'", LIT(builtin_name), xs); + error(x.expr, "'%.*s' expected a #simd type with an integer or floating point element, got '%s'", LIT(builtin_name), xs); gb_string_free(xs); return false; } @@ -485,11 +485,21 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call return false; } Type *elem = base_array_type(x.type); - if (!is_type_integer(elem)) { - gbString xs = type_to_string(x.type); - error(x.expr, "'%.*s' expected a #simd type with an integer element, got '%s'", LIT(builtin_name), xs); - gb_string_free(xs); - return false; + + if (id == BuiltinProc_simd_rem) { + if (!is_type_integer(elem)) { + gbString xs = type_to_string(x.type); + error(x.expr, "'%.*s' expected a #simd type with an integer element, got '%s'", LIT(builtin_name), xs); + gb_string_free(xs); + return false; + } + } else { + if (!is_type_integer(elem) && !is_type_boolean(elem)) { + gbString xs = type_to_string(x.type); + error(x.expr, "'%.*s' expected a #simd type with an integer or boolean element, got '%s'", LIT(builtin_name), xs); + gb_string_free(xs); + return false; + } } operand->mode = Addressing_Value; @@ -497,10 +507,10 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call return true; } - case BuiltinProc_simd_shl: - case BuiltinProc_simd_shr: - case BuiltinProc_simd_shl_masked: - case BuiltinProc_simd_shr_masked: + case BuiltinProc_simd_shl: // Odin-like + case BuiltinProc_simd_shr: // Odin-like + case BuiltinProc_simd_shl_masked: // C-like + case BuiltinProc_simd_shr_masked: // C-like { Operand x = {}; Operand y = {}; @@ -561,7 +571,7 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call Type *elem = base_array_type(x.type); if (!is_type_integer(elem) && !is_type_float(elem)) { gbString xs = type_to_string(x.type); - error(x.expr, "'%.*s' expected a #simd type with an integer or floating-point element, got '%s'", LIT(builtin_name), xs); + error(x.expr, "'%.*s' expected a #simd type with an integer or floating point element, got '%s'", LIT(builtin_name), xs); gb_string_free(xs); return false; } @@ -592,12 +602,27 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call return false; } Type *elem = base_array_type(x.type); - if (!is_type_integer(elem) && !is_type_float(elem)) { - gbString xs = type_to_string(x.type); - error(x.expr, "'%.*s' expected a #simd type with an integer or floating-point element, got '%s'", LIT(builtin_name), xs); - gb_string_free(xs); - return false; + switch (id) { + case BuiltinProc_simd_eq: + case BuiltinProc_simd_ne: + if (!is_type_integer(elem) && !is_type_float(elem) && !is_type_boolean(elem)) { + gbString xs = type_to_string(x.type); + error(x.expr, "'%.*s' expected a #simd type with an integer, floating point, or boolean element, got '%s'", LIT(builtin_name), xs); + gb_string_free(xs); + return false; + } + break; + default: + if (!is_type_integer(elem) && !is_type_float(elem)) { + gbString xs = type_to_string(x.type); + error(x.expr, "'%.*s' expected a #simd type with an integer or floating point element, got '%s'", LIT(builtin_name), xs); + gb_string_free(xs); + return false; + } + break; } + + Type *vt = base_type(x.type); GB_ASSERT(vt->kind == Type_SimdVector); i64 count = vt->SimdVector.count; @@ -630,12 +655,6 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call return false; } Type *elem = base_array_type(x.type); - if (!is_type_integer(elem) && !is_type_float(elem)) { - gbString xs = type_to_string(x.type); - error(x.expr, "'%.*s' expected a #simd type with an integer or floating-point element, got '%s'", LIT(builtin_name), xs); - gb_string_free(xs); - return false; - } i64 max_count = x.type->SimdVector.count; i64 value = -1; if (!check_index_value(c, x.type, false, ce->args[1], max_count, &value)) { @@ -661,12 +680,6 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call return false; } Type *elem = base_array_type(x.type); - if (!is_type_integer(elem) && !is_type_float(elem)) { - gbString xs = type_to_string(x.type); - error(x.expr, "'%.*s' expected a #simd type with an integer or floating-point element, got '%s'", LIT(builtin_name), xs); - gb_string_free(xs); - return false; - } i64 max_count = x.type->SimdVector.count; i64 value = -1; if (!check_index_value(c, x.type, false, ce->args[1], max_count, &value)) { @@ -710,7 +723,7 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call Type *elem = base_array_type(x.type); if (!is_type_integer(elem) && !is_type_float(elem)) { gbString xs = type_to_string(x.type); - error(x.expr, "'%.*s' expected a #simd type with an integer or floating-point element, got '%s'", LIT(builtin_name), xs); + error(x.expr, "'%.*s' expected a #simd type with an integer or floating point element, got '%s'", LIT(builtin_name), xs); gb_string_free(xs); return false; } @@ -732,9 +745,9 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call return false; } Type *elem = base_array_type(x.type); - if (!is_type_integer(elem)) { + if (!is_type_integer(elem) && !is_type_boolean(elem)) { gbString xs = type_to_string(x.type); - error(x.expr, "'%.*s' expected a #simd type with an integer element, got '%s'", LIT(builtin_name), xs); + error(x.expr, "'%.*s' expected a #simd type with an integer or boolean element, got '%s'", LIT(builtin_name), xs); gb_string_free(xs); return false; } @@ -757,7 +770,7 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call // Type *elem = base_array_type(x.type); // if (!is_type_integer(elem) && !is_type_float(elem)) { // gbString xs = type_to_string(x.type); - // error(x.expr, "'%.*s' expected a #simd type with an integer or floating-point element, got '%s'", LIT(builtin_name), xs); + // error(x.expr, "'%.*s' expected a #simd type with an integer or floating point element, got '%s'", LIT(builtin_name), xs); // gb_string_free(xs); // return false; // } @@ -3102,7 +3115,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 Type *elem = y.type; if (!is_type_valid_vector_elem(elem)) { gbString str = type_to_string(elem); - error(call, "Invalid element type for 'intrinsics.simd_vector', expected an integer or float with no specific endianness, got '%s'", str); + error(call, "Invalid element type for 'intrinsics.simd_vector', expected an integer, float, or boolean with no specific endianness, got '%s'", str); gb_string_free(str); operand->mode = Addressing_Type; operand->type = t_invalid; diff --git a/src/check_type.cpp b/src/check_type.cpp index 354ab6e94..540413e32 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2797,7 +2797,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t } else if (name == "simd") { if (!is_type_valid_vector_elem(elem) && !is_type_polymorphic(elem)) { gbString str = type_to_string(elem); - error(at->elem, "Invalid element type for 'intrinsics.simd_vector', expected an integer or float with no specific endianness, got '%s'", str); + error(at->elem, "Invalid element type for 'intrinsics.simd_vector', expected an integer, float, or boolean with no specific endianness, got '%s'", str); gb_string_free(str); *type = alloc_type_array(elem, count, generic_type); goto array_end; diff --git a/src/types.cpp b/src/types.cpp index 2d5709b19..4fca25e52 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1939,6 +1939,9 @@ bool is_type_valid_vector_elem(Type *t) { if (is_type_float(t)) { return true; } + if (is_type_boolean(t)) { + return true; + } } return false; } -- cgit v1.2.3 From 0fd43c1a0b697ea919efdeef42427694f32692bf Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 26 May 2022 11:02:02 +0100 Subject: Add simd.{sqrt, ceil, floor, trunc, nearest} --- core/intrinsics/intrinsics.odin | 6 ++++++ core/simd/simd.odin | 7 +++++++ src/check_builtin.cpp | 26 ++++++++++++++++++++++++++ src/check_type.cpp | 7 +++++-- src/checker_builtin_procs.hpp | 12 ++++++++++++ src/llvm_backend_proc.cpp | 30 +++++++++++++++++++++++++++--- src/types.cpp | 3 +++ 7 files changed, 86 insertions(+), 5 deletions(-) (limited to 'src/types.cpp') diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index 6a1ffe5a0..13a185da0 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -239,6 +239,12 @@ simd_shuffle :: proc(a, b: #simd[N]T, indices: ..int) -> #simd[len(indices)]T -- simd_select :: proc(cond: #simd[N]boolean_or_integer, true, false: #simd[N]T) -> #simd[N]T --- +simd_sqrt :: proc(a: #simd[N]any_float) -> #simd[N]any_float --- +simd_ceil :: proc(a: #simd[N]any_float) -> #simd[N]any_float --- +simd_floor :: proc(a: #simd[N]any_float) -> #simd[N]any_float --- +simd_trunc :: proc(a: #simd[N]any_float) -> #simd[N]any_float --- +simd_nearest :: proc(a: #simd[N]any_float) -> #simd[N]any_float --- + // WASM targets only wasm_memory_grow :: proc(index, delta: uintptr) -> int --- wasm_memory_size :: proc(index: uintptr) -> int --- diff --git a/core/simd/simd.odin b/core/simd/simd.odin index e81f82341..9c37c380c 100644 --- a/core/simd/simd.odin +++ b/core/simd/simd.odin @@ -83,6 +83,13 @@ shuffle :: intrinsics.simd_shuffle // select :: proc(cond: #simd[N]boolean_or_integer, true, false: #simd[N]T) -> #simd[N]T select :: intrinsics.simd_select + +sqrt :: intrinsics.simd_sqrt +ceil :: intrinsics.simd_ceil +floor :: intrinsics.simd_floor +trunc :: intrinsics.simd_trunc +nearest :: intrinsics.simd_nearest + splat :: #force_inline proc "contextless" ($T: typeid/#simd[$LANES]$E, value: E) -> T { return T{0..args[0]); if (x.mode == Addressing_Invalid) { return false; } + + if (!is_type_simd_vector(x.type)) { + error(x.expr, "'%.*s' expected a simd vector boolean type", LIT(builtin_name)); + return false; + } + Type *elem = base_array_type(x.type); + if (!is_type_float(elem)) { + gbString x_str = type_to_string(x.type); + error(x.expr, "'%.*s' expected a simd vector floating point type, got '%s'", LIT(builtin_name), x_str); + gb_string_free(x_str); + return false; + } + + operand->mode = Addressing_Value; + operand->type = x.type; + return true; + } + default: GB_PANIC("Unhandled simd intrinsic: %.*s", LIT(builtin_name)); diff --git a/src/check_type.cpp b/src/check_type.cpp index 74fa235d5..de58db054 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2797,7 +2797,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t } else if (name == "simd") { if (!is_type_valid_vector_elem(elem) && !is_type_polymorphic(elem)) { gbString str = type_to_string(elem); - error(at->elem, "Invalid element type for 'intrinsics.simd_vector', expected an integer, float, or boolean with no specific endianness, got '%s'", str); + error(at->elem, "Invalid element type for #simd, expected an integer, float, or boolean with no specific endianness, got '%s'", str); gb_string_free(str); *type = alloc_type_array(elem, count, generic_type); goto array_end; @@ -2806,7 +2806,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t if (is_type_polymorphic(elem)) { // Ignore } else if (count < 1 || !is_power_of_two(count)) { - error(at->count, "Invalid length for 'intrinsics.simd_vector', expected a power of two length, got '%lld'", cast(long long)count); + error(at->count, "Invalid length for #simd, expected a power of two length, got '%lld'", cast(long long)count); *type = alloc_type_array(elem, count, generic_type); goto array_end; } else @@ -2817,6 +2817,9 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t error(at->count, "wasm based targets are limited to 128-bit types"); } } + if (count > SIMD_ELEMENT_COUNT_MAX) { + error(at->count, "#simd support a maximum element count of %d, got %lld", SIMD_ELEMENT_COUNT_MAX, cast(long long)count); + } } else { error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name)); *type = alloc_type_array(elem, count, generic_type); diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 0fff70f01..adb4e4624 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -159,6 +159,12 @@ BuiltinProc__simd_begin, BuiltinProc_simd_shuffle, BuiltinProc_simd_select, + + BuiltinProc_simd_sqrt, + BuiltinProc_simd_ceil, + BuiltinProc_simd_floor, + BuiltinProc_simd_trunc, + BuiltinProc_simd_nearest, BuiltinProc__simd_end, // Platform specific intrinsics @@ -421,6 +427,12 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("simd_shuffle"), 2, true, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_select"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("simd_sqrt") , 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_ceil") , 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_floor"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_trunc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_nearest"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 05477d84b..88129ba5d 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1231,9 +1231,7 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const 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 = {}; res.value = LLVMBuildCall(p->builder, ip, args, cast(unsigned)args_count, ""); - res.type = tv.type; return res; } case BuiltinProc_simd_reduce_min: @@ -1274,7 +1272,6 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const LLVMValueRef args[1] = {}; args[0] = arg0.value; - lbValue res = {}; res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); return res; } @@ -1314,6 +1311,33 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const return res; } + case BuiltinProc_simd_sqrt: + case BuiltinProc_simd_ceil: + case BuiltinProc_simd_floor: + case BuiltinProc_simd_trunc: + case BuiltinProc_simd_nearest: + { + char const *name = nullptr; + switch (builtin_id) { + case BuiltinProc_simd_sqrt: name = "llvm.sqrt"; break; + case BuiltinProc_simd_ceil: name = "llvm.ceil"; break; + case BuiltinProc_simd_floor: name = "llvm.floor"; break; + case BuiltinProc_simd_trunc: name = "llvm.trunc"; break; + case BuiltinProc_simd_nearest: name = "llvm.nearbyint"; break; + } + + LLVMTypeRef types[1] = {lb_type(p->module, arg0.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] = arg0.value; + + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + return res; + } + } GB_PANIC("Unhandled simd intrinsic: '%.*s'", LIT(builtin_procs[builtin_id].name)); diff --git a/src/types.cpp b/src/types.cpp index 4fca25e52..fccea2937 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -363,6 +363,9 @@ enum : int { MATRIX_ELEMENT_COUNT_MIN = 1, MATRIX_ELEMENT_COUNT_MAX = 16, MATRIX_ELEMENT_MAX_SIZE = MATRIX_ELEMENT_COUNT_MAX * (2 * 8), // complex128 + + SIMD_ELEMENT_COUNT_MIN = 1, + SIMD_ELEMENT_COUNT_MAX = 64, }; -- cgit v1.2.3 From 7ec0236fbf55939ef46662a732b00908730f826b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 26 May 2022 11:14:22 +0100 Subject: Add `simd_reverse` --- core/intrinsics/intrinsics.odin | 2 ++ core/simd/simd.odin | 2 ++ src/check_builtin.cpp | 13 +++++++++++++ src/checker_builtin_procs.hpp | 4 ++++ src/llvm_backend_proc.cpp | 16 ++++++++++++++++ src/types.cpp | 2 ++ 6 files changed, 39 insertions(+) (limited to 'src/types.cpp') diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index 13a185da0..a14488210 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -245,6 +245,8 @@ simd_floor :: proc(a: #simd[N]any_float) -> #simd[N]any_float --- simd_trunc :: proc(a: #simd[N]any_float) -> #simd[N]any_float --- simd_nearest :: proc(a: #simd[N]any_float) -> #simd[N]any_float --- +simd_reverse :: proc(a: #simd[N]T) -> #simd[N]T --- + // WASM targets only wasm_memory_grow :: proc(index, delta: uintptr) -> int --- wasm_memory_size :: proc(index: uintptr) -> int --- diff --git a/core/simd/simd.odin b/core/simd/simd.odin index 9c37c380c..060e32323 100644 --- a/core/simd/simd.odin +++ b/core/simd/simd.odin @@ -90,6 +90,8 @@ floor :: intrinsics.simd_floor trunc :: intrinsics.simd_trunc nearest :: intrinsics.simd_nearest +reverse :: intrinsics.simd_reverse + splat :: #force_inline proc "contextless" ($T: typeid/#simd[$LANES]$E, value: E) -> T { return T{0..args[0]); if (x.mode == Addressing_Invalid) { return false; } + + if (!is_type_simd_vector(x.type)) { + error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + operand->type = x.type; + operand->mode = Addressing_Value; + return true; + } default: GB_PANIC("Unhandled simd intrinsic: %.*s", LIT(builtin_name)); diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index adb4e4624..22ee3d141 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -165,6 +165,8 @@ BuiltinProc__simd_begin, BuiltinProc_simd_floor, BuiltinProc_simd_trunc, BuiltinProc_simd_nearest, + + BuiltinProc_simd_reverse, BuiltinProc__simd_end, // Platform specific intrinsics @@ -433,6 +435,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("simd_floor"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_trunc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_nearest"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("simd_reverse"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 88129ba5d..42f5a60fa 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1338,6 +1338,22 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const return res; } + case BuiltinProc_simd_reverse: + { + i64 count = get_array_type_count(arg0.type); + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, count); + LLVMTypeRef llvm_u32 = lb_type(m, t_u32); + for (i64 i = 0; i < count; i++) { + values[i] = LLVMConstInt(llvm_u32, count-1-i, false); + } + LLVMValueRef mask = LLVMConstVector(values, cast(unsigned)count); + + LLVMValueRef v = arg0.value; + res.value = LLVMBuildShuffleVector(p->builder, v, v, mask, ""); + return res; + } + + } GB_PANIC("Unhandled simd intrinsic: '%.*s'", LIT(builtin_procs[builtin_id].name)); diff --git a/src/types.cpp b/src/types.cpp index fccea2937..6f61015d3 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1598,6 +1598,8 @@ i64 get_array_type_count(Type *t) { return bt->Array.count; } else if (bt->kind == Type_EnumeratedArray) { return bt->EnumeratedArray.count; + } else if (bt->kind == Type_SimdVector) { + return bt->SimdVector.count; } GB_ASSERT(is_type_array_like(t)); return -1; -- cgit v1.2.3 From d0e8a735bae52eaa7d1d852953da721071571020 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 26 May 2022 17:09:46 +0100 Subject: Add arithmetic operator support for simd vectors; Add `intrinsics.simd_and_not` --- core/simd/simd.odin | 16 +++++++++++++--- src/check_builtin.cpp | 1 + src/check_expr.cpp | 7 ++----- src/checker_builtin_procs.hpp | 3 +++ src/llvm_backend_expr.cpp | 40 +++++++++++++++++++++++++++++++++++++++- src/llvm_backend_proc.cpp | 4 ++++ src/types.cpp | 3 +++ 7 files changed, 65 insertions(+), 9 deletions(-) (limited to 'src/types.cpp') diff --git a/core/simd/simd.odin b/core/simd/simd.odin index ef5fdc70b..9673b5cc9 100644 --- a/core/simd/simd.odin +++ b/core/simd/simd.odin @@ -43,9 +43,10 @@ shr_masked :: intrinsics.simd_shr_masked add_sat :: intrinsics.simd_add_sat sub_sat :: intrinsics.simd_sub_sat -and :: intrinsics.simd_and -or :: intrinsics.simd_or -xor :: intrinsics.simd_xor +and :: intrinsics.simd_and +or :: intrinsics.simd_or +xor :: intrinsics.simd_xor +and_not :: intrinsics.simd_and_not neg :: intrinsics.simd_neg @@ -132,3 +133,12 @@ copysign :: #force_inline proc "contextless" (v, sign: $T/#simd[$LANES]$E) -> T magnitude := and(to_bits(v), bit_not(neg_zero)) return transmute(T)or(sign_bit, magnitude) } + +signum :: #force_inline proc "contextless" (v: $T/#simd[$LANES]$E) -> T where intrinsics.type_is_float(E) { + is_nan := ne(v, v) + return select(is_nan, v, copysign(T(1), v)) +} + +recip :: #force_inline proc "contextless" (v: $T/#simd[$LANES]$E) -> T where intrinsics.type_is_float(E) { + return div(T(1), v) +} diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 34b7d14e9..bfaa91cf8 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -464,6 +464,7 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call case BuiltinProc_simd_and: case BuiltinProc_simd_or: case BuiltinProc_simd_xor: + case BuiltinProc_simd_and_not: { Operand x = {}; Operand y = {}; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b7568aa70..7fe9b8acf 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1590,11 +1590,6 @@ bool check_unary_op(CheckerContext *c, Operand *o, Token op) { bool check_binary_op(CheckerContext *c, Operand *o, Token op) { Type *main_type = o->type; - if (is_type_simd_vector(main_type)) { - error(op, "Operator '%.*s' is not supported on #simd vector types, please use the intrinsics.simd_*", LIT(op.string)); - return false; - } - // TODO(bill): Handle errors correctly Type *type = base_type(core_array_type(main_type)); Type *ct = core_type(type); @@ -2500,6 +2495,8 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ gb_string_free(err_str); } + // TODO(bill): Should we support shifts for fixed arrays and #simd vectors? + if (!is_type_integer(x->type)) { gbString err_str = expr_to_string(y->expr); error(node, "Shift operand '%s' must be an integer", err_str); diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 350213de2..eb4bc1498 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -135,6 +135,7 @@ BuiltinProc__simd_begin, BuiltinProc_simd_and, BuiltinProc_simd_or, BuiltinProc_simd_xor, + BuiltinProc_simd_and_not, BuiltinProc_simd_neg, BuiltinProc_simd_abs, @@ -417,6 +418,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("simd_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_and_not"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_neg"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_abs"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 10c337650..55b76b93a 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -258,7 +258,13 @@ lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x, Type *type) LLVMBuildStore(p->builder, v2, LLVMBuildStructGEP(p->builder, addr.addr.value, 2, "")); LLVMBuildStore(p->builder, v3, LLVMBuildStructGEP(p->builder, addr.addr.value, 3, "")); return lb_addr_load(p, addr); - + } else if (is_type_simd_vector(x.type)) { + Type *elem = base_array_type(x.type); + if (is_type_float(elem)) { + res.value = LLVMBuildFNeg(p->builder, x.value, ""); + } else { + res.value = LLVMBuildNeg(p->builder, x.value, ""); + } } else { GB_PANIC("Unhandled type %s", type_to_string(x.type)); } @@ -2559,6 +2565,38 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri case Token_NotEq: pred = LLVMIntNE; break; } res.value = LLVMBuildICmp(p->builder, pred, left.value, right.value, ""); + } else if (is_type_simd_vector(a)) { + LLVMValueRef mask = nullptr; + Type *elem = base_array_type(a); + if (is_type_float(elem)) { + LLVMRealPredicate pred = {}; + switch (op_kind) { + case Token_CmpEq: pred = LLVMRealOEQ; break; + case Token_NotEq: pred = LLVMRealONE; break; + } + mask = LLVMBuildFCmp(p->builder, pred, left.value, right.value, ""); + } else { + LLVMIntPredicate pred = {}; + switch (op_kind) { + case Token_CmpEq: pred = LLVMIntEQ; break; + case Token_NotEq: pred = LLVMIntNE; break; + } + mask = LLVMBuildICmp(p->builder, pred, left.value, right.value, ""); + } + GB_ASSERT_MSG(mask != nullptr, "Unhandled comparison kind %s (%s) %.*s %s (%s)", type_to_string(left.type), type_to_string(base_type(left.type)), LIT(token_strings[op_kind]), type_to_string(right.type), type_to_string(base_type(right.type))); + + // TODO(bill): is this a good approach to dealing with comparisons of vectors? + char const *name = "llvm.vector.reduce.umax"; + LLVMTypeRef types[1] = {LLVMTypeOf(mask)}; + 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] = mask; + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + return res; + } else { GB_PANIC("Unhandled comparison kind %s (%s) %.*s %s (%s)", type_to_string(left.type), type_to_string(base_type(left.type)), LIT(token_strings[op_kind]), type_to_string(right.type), type_to_string(base_type(right.type))); } diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index a56aa862a..99c023311 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1097,11 +1097,15 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const case BuiltinProc_simd_and: case BuiltinProc_simd_or: case BuiltinProc_simd_xor: + case BuiltinProc_simd_and_not: arg1 = lb_build_expr(p, ce->args[1]); switch (builtin_id) { case BuiltinProc_simd_and: op_code = LLVMAnd; break; case BuiltinProc_simd_or: op_code = LLVMOr; break; case BuiltinProc_simd_xor: op_code = LLVMXor; break; + case BuiltinProc_simd_and_not: + res.value = LLVMBuildAnd(p->builder, arg0.value, LLVMBuildNot(p->builder, arg1.value, ""), ""); + return res; } if (op_code) { res.value = LLVMBuildBinOp(p->builder, op_code, arg0.value, arg1.value, ""); diff --git a/src/types.cpp b/src/types.cpp index 6f61015d3..ad83e0568 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2306,6 +2306,9 @@ bool is_type_comparable(Type *t) { } } return true; + + case Type_SimdVector: + return true; } return false; } -- cgit v1.2.3 From ea42613fec8aa5875e1557cbc913651ad979463c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 28 Jun 2022 11:27:50 +0100 Subject: Improve error message with suggestion for #1866 --- src/check_stmt.cpp | 21 ++++++++++++++++++++- src/types.cpp | 11 ++++++----- 2 files changed, 26 insertions(+), 6 deletions(-) (limited to 'src/types.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index f061b4961..a6f6f1a7d 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2142,7 +2142,26 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { } if (new_name_count == 0) { - error(node, "No new declarations on the lhs"); + begin_error_block(); + error(node, "No new declarations on the left hand side"); + bool all_underscore = true; + for_array(i, vd->names) { + Ast *name = vd->names[i]; + if (name->kind == Ast_Ident) { + if (!is_blank_ident(name)) { + all_underscore = false; + break; + } + } else { + all_underscore = false; + break; + } + } + if (all_underscore) { + error_line("\tSuggestion: Try changing the declaration (:=) to an assignment (=)\n"); + } + + end_error_block(); } Type *init_type = nullptr; diff --git a/src/types.cpp b/src/types.cpp index ad83e0568..5f112ce09 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -724,10 +724,11 @@ gb_global RecursiveMutex g_type_mutex; struct TypePath; -i64 type_size_of (Type *t); -i64 type_align_of (Type *t); -i64 type_offset_of (Type *t, i32 index); -gbString type_to_string (Type *type, bool shorthand=false); +i64 type_size_of (Type *t); +i64 type_align_of (Type *t); +i64 type_offset_of (Type *t, i32 index); +gbString type_to_string (Type *type, bool shorthand=true); +gbString type_to_string (Type *type, gbAllocator allocator, bool shorthand=true); i64 type_size_of_internal(Type *t, TypePath *path); void init_map_internal_types(Type *type); Type * bit_set_to_int(Type *t); @@ -4287,7 +4288,7 @@ gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) { } -gbString type_to_string(Type *type, gbAllocator allocator, bool shorthand=false) { +gbString type_to_string(Type *type, gbAllocator allocator, bool shorthand) { return write_type_to_string(gb_string_make(allocator, ""), type, shorthand); } gbString type_to_string(Type *type, bool shorthand) { -- cgit v1.2.3