From f6a087775e0a7baf97bce7b1ebb59b223b10d047 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 25 Jan 2022 12:39:06 +0000 Subject: Disable early return from `check_proc_info` --- src/llvm_backend_proc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index b35c6c304..9f9fe7c7a 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -61,7 +61,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) GB_ASSERT(entity != nullptr); GB_ASSERT(entity->kind == Entity_Procedure); if (!entity->Procedure.is_foreign) { - GB_ASSERT(entity->flags & EntityFlag_ProcBodyChecked); + GB_ASSERT_MSG(entity->flags & EntityFlag_ProcBodyChecked, "%.*s :: %s", LIT(entity->token.string), type_to_string(entity->type)); } String link_name = {}; -- cgit v1.2.3 From eac74631ecb74689dbe6bb1e1d07cd15f1fc677a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 1 Feb 2022 14:11:19 +0000 Subject: Correct debug information logic for procedure parameters --- src/llvm_backend.hpp | 1 + src/llvm_backend_debug.cpp | 71 ++++++++++++++++++++++++++++++++++++++++++++++ src/llvm_backend_proc.cpp | 9 +++--- 3 files changed, 77 insertions(+), 4 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 49f675a49..d7093bc63 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -232,6 +232,7 @@ struct lbTargetList { enum lbProcedureFlag : u32 { lbProcedureFlag_WithoutMemcpyPass = 1<<0, + lbProcedureFlag_DebugAllocaCopy = 1<<1, }; struct lbCopyElisionHint { diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index 7a2b00fe9..f60096aad 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -965,6 +965,77 @@ void lb_add_debug_local_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, T LLVMDIBuilderInsertDeclareBefore(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, instr); } + +void lb_add_debug_param_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, Token const &token, unsigned arg_number) { + if (p->debug_info == nullptr) { + return; + } + if (type == nullptr) { + return; + } + if (type == t_invalid) { + return; + } + if (p->body == nullptr) { + return; + } + + lbModule *m = p->module; + String const &name = token.string; + if (name == "" || name == "_") { + return; + } + + if (lb_get_llvm_metadata(m, ptr) != nullptr) { + // Already been set + return; + } + + + AstFile *file = p->body->file(); + + LLVMMetadataRef llvm_scope = lb_get_current_debug_scope(p); + LLVMMetadataRef llvm_file = lb_get_llvm_metadata(m, file); + GB_ASSERT(llvm_scope != nullptr); + if (llvm_file == nullptr) { + llvm_file = LLVMDIScopeGetFile(llvm_scope); + } + + if (llvm_file == nullptr) { + return; + } + + LLVMDIFlags flags = LLVMDIFlagZero; + LLVMBool always_preserve = build_context.optimization_level == 0; + + LLVMMetadataRef debug_type = lb_debug_type(m, type); + + LLVMMetadataRef var_info = LLVMDIBuilderCreateParameterVariable( + m->debug_builder, llvm_scope, + cast(char const *)name.text, cast(size_t)name.len, + arg_number, + llvm_file, token.pos.line, + debug_type, + always_preserve, flags + ); + + LLVMValueRef storage = ptr; + LLVMValueRef instr = ptr; + LLVMBasicBlockRef block = p->decl_block->block; + LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, token.pos); + LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0); + lb_set_llvm_metadata(m, ptr, llvm_expr); + if (LLVMIsAAllocaInst(instr)) { + LLVMDIBuilderInsertDeclareBefore(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, instr); + } else { + // NOTE(bill, 2022-02-01): For parameter values, you must insert them at the end of the decl block + // The reason is that if the parameter is at index 0 and a pointer, there is not such things as an + // instruction "before" it. + LLVMDIBuilderInsertDeclareAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block); + } +} + + void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx) { if (!p->debug_info || !p->body) { return; diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 9f9fe7c7a..7a6fac603 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -473,6 +473,8 @@ void lb_begin_procedure_body(lbProcedure *p) { } lbArgType *arg_type = &ft->args[param_index]; + defer (param_index += 1); + if (arg_type->kind == lbArg_Ignore) { continue; } else if (arg_type->kind == lbArg_Direct) { @@ -487,20 +489,19 @@ void lb_begin_procedure_body(lbProcedure *p) { param.type = e->type; lbValue ptr = lb_address_from_load_or_generate_local(p, param); + GB_ASSERT(LLVMIsAAllocaInst(ptr.value)); lb_add_entity(p->module, e, ptr); - // lb_add_debug_local_variable(p, ptr.value, e->type, e->token); + lb_add_debug_param_variable(p, ptr.value, e->type, e->token, param_index+1); } } else if (arg_type->kind == lbArg_Indirect) { if (e->token.string.len != 0 && !is_blank_ident(e->token.string)) { lbValue ptr = {}; ptr.value = LLVMGetParam(p->value, param_offset+param_index); ptr.type = alloc_type_pointer(e->type); - lb_add_entity(p->module, e, ptr); - // lb_add_debug_local_variable(p, ptr.value, e->type, e->token); + lb_add_debug_param_variable(p, ptr.value, e->type, e->token, param_index+1); } } - param_index += 1; } } -- 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/llvm_backend_proc.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/llvm_backend_proc.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 7386ca9272c527800720c0dd53a9ad351345aa41 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 14 Feb 2022 11:21:21 +0000 Subject: Add new objc intrinsics: objc_(register|find)_(selector|class) --- core/sys/darwin/Foundation/NSObject.odin | 2 +- src/check_builtin.cpp | 28 ++++++- src/checker_builtin_procs.hpp | 11 ++- src/llvm_backend.cpp | 5 +- src/llvm_backend_proc.cpp | 8 +- src/llvm_backend_utility.cpp | 133 +++++++++++++++++++++++-------- 6 files changed, 140 insertions(+), 47 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/core/sys/darwin/Foundation/NSObject.odin b/core/sys/darwin/Foundation/NSObject.odin index 82d837fb8..55d87a14b 100644 --- a/core/sys/darwin/Foundation/NSObject.odin +++ b/core/sys/darwin/Foundation/NSObject.odin @@ -70,7 +70,7 @@ description :: proc(self: ^Object) -> ^String { @(objc_type=Object, objc_name="debugDescription") debugDescription :: proc(self: ^Object) -> ^String { - if msgSendSafeCheck(self, intrinsics.objc_selector_name("debugDescription")) { + if msgSendSafeCheck(self, intrinsics.objc_find_selector("debugDescription")) { return msgSend(^String, self, "debugDescription") } return nil diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index a9ee5d25f..a6b1759f5 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -348,13 +348,27 @@ bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call return true; } break; - case BuiltinProc_objc_selector_name: { + case BuiltinProc_objc_find_selector: + case BuiltinProc_objc_find_class: + case BuiltinProc_objc_register_selector: + case BuiltinProc_objc_register_class: + { String sel_name = {}; if (!is_constant_string(c, builtin_name, ce->args[0], &sel_name)) { return false; } - operand->type = t_objc_SEL; + switch (id) { + case BuiltinProc_objc_find_selector: + case BuiltinProc_objc_register_selector: + operand->type = t_objc_SEL; + break; + case BuiltinProc_objc_find_class: + case BuiltinProc_objc_register_class: + operand->type = t_objc_Class; + break; + + } operand->mode = Addressing_Value; return true; } break; @@ -398,7 +412,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_max: case BuiltinProc_type_is_subtype_of: case BuiltinProc_objc_send: - case BuiltinProc_objc_selector_name: + case BuiltinProc_objc_find_selector: + case BuiltinProc_objc_find_class: + case BuiltinProc_objc_register_selector: + case BuiltinProc_objc_register_class: // NOTE(bill): The first arg may be a Type, this will be checked case by case break; @@ -440,7 +457,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; case BuiltinProc_objc_send: - case BuiltinProc_objc_selector_name: + case BuiltinProc_objc_find_selector: + case BuiltinProc_objc_find_class: + case BuiltinProc_objc_register_selector: + case BuiltinProc_objc_register_class: return check_builtin_objc_procedure(c, operand, call, id, type_hint); case BuiltinProc___entry_point: diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index c14c18412..19fa94ee6 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -253,7 +253,10 @@ BuiltinProc__type_end, BuiltinProc___entry_point, BuiltinProc_objc_send, - BuiltinProc_objc_selector_name, + BuiltinProc_objc_find_selector, + BuiltinProc_objc_find_class, + BuiltinProc_objc_register_selector, + BuiltinProc_objc_register_class, BuiltinProc_COUNT, }; @@ -509,5 +512,9 @@ 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_selector_name"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("objc_find_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("objc_find_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("objc_register_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("objc_register_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, }; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 7941c65a3..52c46cadc 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -672,12 +672,14 @@ void lb_finalize_objc_names(lbProcedure *p) { lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level); LLVMFinalizeFunctionPassManager(default_function_pass_manager); + + auto args = array_make(permanent_allocator(), 1); + 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); @@ -686,7 +688,6 @@ void lb_finalize_objc_names(lbProcedure *p) { 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); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index caa3dfa1a..2a0380605 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -2108,10 +2108,12 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, } case BuiltinProc_objc_send: - return lb_handle_obj_send(p, expr); + return lb_handle_objc_send(p, expr); - case BuiltinProc_objc_selector_name: - return lb_handle_obj_selector_name(p, expr); + case BuiltinProc_objc_find_selector: return lb_handle_objc_find_selector(p, expr); + case BuiltinProc_objc_find_class: return lb_handle_objc_find_class(p, expr); + case BuiltinProc_objc_register_selector: return lb_handle_objc_register_selector(p, expr); + case BuiltinProc_objc_register_class: return lb_handle_objc_register_class(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 8ef66df7a..75fb89314 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -1823,7 +1823,101 @@ void lb_set_wasm_export_attributes(LLVMValueRef value, String export_name) { lbValue lb_lookup_runtime_procedure(lbModule *m, String const &name); -lbValue lb_handle_obj_id(lbProcedure *p, Ast *expr) { + +lbAddr lb_handle_objc_find_or_register_selector(lbProcedure *p, String const &name) { + lbAddr *found = string_map_get(&p->module->objc_selectors, name); + if (found) { + return *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 local_addr; + } +} + +lbValue lb_handle_objc_find_selector(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_addr_load(p, lb_handle_objc_find_or_register_selector(p, name)); +} + +lbValue lb_handle_objc_register_selector(lbProcedure *p, Ast *expr) { + ast_node(ce, CallExpr, expr); + lbModule *m = p->module; + + auto tav = ce->args[0]->tav; + GB_ASSERT(tav.value.kind == ExactValue_String); + String name = tav.value.value_string; + lbAddr dst = lb_handle_objc_find_or_register_selector(p, name); + + 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, dst, ptr); + + return lb_addr_load(p, dst); +} + +lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String const &name) { + lbAddr *found = string_map_get(&p->module->objc_classes, name); + if (found) { + return *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_classes, name, default_addr); + if (default_module != p->module) { + string_map_set(&p->module->objc_classes, name, local_addr); + } + return local_addr; + } +} + +lbValue lb_handle_objc_find_class(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_addr_load(p, lb_handle_objc_find_or_register_class(p, name)); +} + +lbValue lb_handle_objc_register_class(lbProcedure *p, Ast *expr) { + ast_node(ce, CallExpr, expr); + lbModule *m = p->module; + + auto tav = ce->args[0]->tav; + GB_ASSERT(tav.value.kind == ExactValue_String); + String name = tav.value.value_string; + lbAddr dst = lb_handle_objc_find_or_register_class(p, name); + + 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, dst, ptr); + + return lb_addr_load(p, dst); +} + + +lbValue lb_handle_objc_id(lbProcedure *p, Ast *expr) { TypeAndValue const &tav = type_and_value_of_expr(expr); if (tav.mode == Addressing_Type) { Type *type = tav.type; @@ -1854,29 +1948,7 @@ lbValue lb_handle_obj_id(lbProcedure *p, Ast *expr) { 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) { +lbValue lb_handle_objc_send(lbProcedure *p, Ast *expr) { ast_node(ce, CallExpr, expr); lbModule *m = p->module; @@ -1887,10 +1959,10 @@ lbValue lb_handle_obj_send(lbProcedure *p, Ast *expr) { 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]); + lbValue id = lb_handle_objc_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); + lbValue sel = lb_addr_load(p, lb_handle_objc_find_or_register_selector(p, sel_expr->tav.value.value_string)); array_add(&args, id); array_add(&args, sel); @@ -1917,12 +1989,3 @@ lbValue lb_handle_obj_send(lbProcedure *p, Ast *expr) { } -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 -- cgit v1.2.3 From 0738822dda2a863322b752b91ce7a4e0ac95e6e7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 16 Feb 2022 13:25:31 +0000 Subject: Change how parameter and variables are given debug values --- src/llvm_backend_debug.cpp | 18 +++++++----------- src/llvm_backend_opt.cpp | 2 +- src/llvm_backend_proc.cpp | 3 ++- 3 files changed, 10 insertions(+), 13 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index 1a2705988..88974bd24 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -958,11 +958,11 @@ void lb_add_debug_local_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, T ); LLVMValueRef storage = ptr; - LLVMValueRef instr = ptr; + LLVMBasicBlockRef block = p->decl_block->block; LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, token.pos); LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0); lb_set_llvm_metadata(m, ptr, llvm_expr); - LLVMDIBuilderInsertDbgValueBefore(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, instr); + LLVMDIBuilderInsertDbgValueAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block); } @@ -1020,19 +1020,15 @@ void lb_add_debug_param_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, T ); LLVMValueRef storage = ptr; - LLVMValueRef instr = ptr; LLVMBasicBlockRef block = p->decl_block->block; LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, token.pos); LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0); lb_set_llvm_metadata(m, ptr, llvm_expr); - if (LLVMIsAAllocaInst(instr)) { - LLVMDIBuilderInsertDbgValueBefore(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, instr); - } else { - // NOTE(bill, 2022-02-01): For parameter values, you must insert them at the end of the decl block - // The reason is that if the parameter is at index 0 and a pointer, there is not such things as an - // instruction "before" it. - LLVMDIBuilderInsertDbgValueAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block); - } + + // NOTE(bill, 2022-02-01): For parameter values, you must insert them at the end of the decl block + // The reason is that if the parameter is at index 0 and a pointer, there is not such things as an + // instruction "before" it. + LLVMDIBuilderInsertDbgValueAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block); } diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index 8f1c7ad59..d36bdec0b 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -56,7 +56,7 @@ LLVMBool lb_must_preserve_predicate_callback(LLVMValueRef value, void *user_data #endif void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm, i32 optimization_level) { - if (optimization_level == 0 && build_context.ODIN_DEBUG) { + if (false && optimization_level == 0 && build_context.ODIN_DEBUG) { LLVMAddMergedLoadStoreMotionPass(fpm); } else { LLVMAddPromoteMemoryToRegisterPass(fpm); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 2a0380605..61cba53bd 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -491,7 +491,8 @@ void lb_begin_procedure_body(lbProcedure *p) { lbValue ptr = lb_address_from_load_or_generate_local(p, param); GB_ASSERT(LLVMIsAAllocaInst(ptr.value)); lb_add_entity(p->module, e, ptr); - lb_add_debug_param_variable(p, ptr.value, e->type, e->token, param_index+1); + // lb_add_debug_param_variable(p, ptr.value, e->type, e->token, param_index+1); + lb_add_debug_param_variable(p, value, e->type, e->token, param_index+1); } } else if (arg_type->kind == lbArg_Indirect) { if (e->token.string.len != 0 && !is_blank_ident(e->token.string)) { -- cgit v1.2.3 From e011d812ca5df522f937a4e3866310c6a24fd6c4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 16 Feb 2022 14:39:08 +0000 Subject: Improve debug information for direct procedure parmaters --- src/llvm_backend_debug.cpp | 5 ++--- src/llvm_backend_proc.cpp | 19 +++++++++++++------ 2 files changed, 15 insertions(+), 9 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index 88974bd24..0e86fbb59 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -966,7 +966,7 @@ void lb_add_debug_local_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, T } -void lb_add_debug_param_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, Token const &token, unsigned arg_number) { +void lb_add_debug_param_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, Token const &token, unsigned arg_number, lbBlock *block) { if (p->debug_info == nullptr) { return; } @@ -1020,7 +1020,6 @@ void lb_add_debug_param_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, T ); LLVMValueRef storage = ptr; - LLVMBasicBlockRef block = p->decl_block->block; LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, token.pos); LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0); lb_set_llvm_metadata(m, ptr, llvm_expr); @@ -1028,7 +1027,7 @@ void lb_add_debug_param_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, T // NOTE(bill, 2022-02-01): For parameter values, you must insert them at the end of the decl block // The reason is that if the parameter is at index 0 and a pointer, there is not such things as an // instruction "before" it. - LLVMDIBuilderInsertDbgValueAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block); + LLVMDIBuilderInsertDbgValueAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block->block); } diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 61cba53bd..ccb16ebe0 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -480,9 +480,8 @@ void lb_begin_procedure_body(lbProcedure *p) { } else if (arg_type->kind == lbArg_Direct) { if (e->token.string.len != 0 && !is_blank_ident(e->token.string)) { LLVMTypeRef param_type = lb_type(p->module, e->type); - LLVMValueRef value = LLVMGetParam(p->value, param_offset+param_index); - - value = OdinLLVMBuildTransmute(p, value, param_type); + LLVMValueRef original_value = LLVMGetParam(p->value, param_offset+param_index); + LLVMValueRef value = OdinLLVMBuildTransmute(p, original_value, param_type); lbValue param = {}; param.value = value; @@ -491,8 +490,16 @@ void lb_begin_procedure_body(lbProcedure *p) { lbValue ptr = lb_address_from_load_or_generate_local(p, param); GB_ASSERT(LLVMIsAAllocaInst(ptr.value)); lb_add_entity(p->module, e, ptr); - // lb_add_debug_param_variable(p, ptr.value, e->type, e->token, param_index+1); - lb_add_debug_param_variable(p, value, e->type, e->token, param_index+1); + + lbBlock *block = p->decl_block; + if (original_value != value) { + block = p->curr_block; + } + LLVMValueRef debug_storage_value = value; + if (original_value != value && LLVMIsALoadInst(value)) { + debug_storage_value = ptr.value; + } + lb_add_debug_param_variable(p, debug_storage_value, e->type, e->token, param_index+1, block); } } else if (arg_type->kind == lbArg_Indirect) { if (e->token.string.len != 0 && !is_blank_ident(e->token.string)) { @@ -500,7 +507,7 @@ void lb_begin_procedure_body(lbProcedure *p) { ptr.value = LLVMGetParam(p->value, param_offset+param_index); ptr.type = alloc_type_pointer(e->type); lb_add_entity(p->module, e, ptr); - lb_add_debug_param_variable(p, ptr.value, e->type, e->token, param_index+1); + lb_add_debug_param_variable(p, ptr.value, e->type, e->token, param_index+1, p->decl_block); } } } -- cgit v1.2.3 From db6bd9b358f17c0259ff5fe6411ce93407613338 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 16 Feb 2022 16:03:49 +0000 Subject: Allow sysv and win64 calling conventions to be used on any platform on amd64 --- core/runtime/core.odin | 5 +++++ src/check_type.cpp | 19 +++++++++++++++++++ src/docs_writer.cpp | 35 +---------------------------------- src/llvm_abi.cpp | 6 ++++++ src/llvm_backend.hpp | 4 ++++ src/llvm_backend_proc.cpp | 2 +- src/parser.cpp | 6 ++++++ src/parser.hpp | 18 ++++++++++++++++++ 8 files changed, 60 insertions(+), 35 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/core/runtime/core.odin b/core/runtime/core.odin index fec51f236..229d70417 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -33,6 +33,11 @@ Calling_Convention :: enum u8 { None = 6, Naked = 7, + + _ = 8, // reserved + + Win64 = 9, + SysV = 10, } Type_Info_Enum_Value :: distinct i64 diff --git a/src/check_type.cpp b/src/check_type.cpp index 32340070e..a11f5234b 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1910,6 +1910,25 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, c->scope->flags &= ~ScopeFlag_ContextDefined; } + TargetArchKind arch = build_context.metrics.arch; + switch (cc) { + case ProcCC_StdCall: + case ProcCC_FastCall: + if (arch != TargetArch_i386 || arch != TargetArch_amd64) { + error(proc_type_node, "Invalid procedure calling convention \"%s\" for target architecture, expected either i386 or amd64, got %.*s", + proc_calling_convention_strings[cc], LIT(target_arch_names[arch])); + } + break; + case ProcCC_Win64: + case ProcCC_SysV: + if (arch != TargetArch_amd64) { + error(proc_type_node, "Invalid procedure calling convention \"%s\" for target architecture, expected amd64, got %.*s", + proc_calling_convention_strings[cc], LIT(target_arch_names[arch])); + } + break; + } + + bool variadic = false; isize variadic_index = -1; bool success = true; diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp index 0474ce8ff..2c5186c39 100644 --- a/src/docs_writer.cpp +++ b/src/docs_writer.cpp @@ -683,40 +683,7 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) { types[1] = odin_doc_type(w, type->Proc.results); doc_type.types = odin_write_slice(w, types, gb_count_of(types)); - String calling_convention = {}; - switch (type->Proc.calling_convention) { - case ProcCC_Invalid: - // no need - break; - case ProcCC_Odin: - if (default_calling_convention() != ProcCC_Odin) { - calling_convention = str_lit("odin"); - } - break; - case ProcCC_Contextless: - if (default_calling_convention() != ProcCC_Contextless) { - calling_convention = str_lit("contextless"); - } - break; - case ProcCC_CDecl: - calling_convention = str_lit("cdecl"); - break; - case ProcCC_StdCall: - calling_convention = str_lit("stdcall"); - break; - case ProcCC_FastCall: - calling_convention = str_lit("fastcall"); - break; - case ProcCC_None: - calling_convention = str_lit("none"); - break; - case ProcCC_Naked: - calling_convention = str_lit("naked"); - break; - case ProcCC_InlineAsm: - calling_convention = str_lit("inline-assembly"); - break; - } + String calling_convention = make_string_c(proc_calling_convention_strings[type->Proc.calling_convention]); doc_type.calling_convention = odin_doc_write_string(w, calling_convention); } break; diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 310df6639..0244b73d6 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -1184,6 +1184,12 @@ LB_ABI_INFO(lb_get_abi_info) { ft->calling_convention = calling_convention; return ft; } + case ProcCC_Win64: + GB_ASSERT(build_context.metrics.arch == TargetArch_amd64); + return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); + case ProcCC_SysV: + GB_ASSERT(build_context.metrics.arch == TargetArch_amd64); + return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); } switch (build_context.metrics.arch) { diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 087cda22a..f2bcfaff6 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -548,6 +548,10 @@ lbCallingConventionKind const lb_calling_convention_map[ProcCC_MAX] = { lbCallingConvention_C, // ProcCC_None, lbCallingConvention_C, // ProcCC_Naked, lbCallingConvention_C, // ProcCC_InlineAsm, + + lbCallingConvention_Win64, // ProcCC_Win64, + lbCallingConvention_X86_64_SysV, // ProcCC_SysV, + }; enum : LLVMDWARFTypeEncoding { diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index ccb16ebe0..261e2819c 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -497,7 +497,7 @@ void lb_begin_procedure_body(lbProcedure *p) { } LLVMValueRef debug_storage_value = value; if (original_value != value && LLVMIsALoadInst(value)) { - debug_storage_value = ptr.value; + debug_storage_value = LLVMGetOperand(value, 0); } lb_add_debug_param_variable(p, debug_storage_value, e->type, e->token, param_index+1, block); } diff --git a/src/parser.cpp b/src/parser.cpp index 0914c77ca..bd0e55b7f 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3412,12 +3412,18 @@ ProcCallingConvention string_to_calling_convention(String s) { if (s == "fast") return ProcCC_FastCall; if (s == "none") return ProcCC_None; if (s == "naked") return ProcCC_Naked; + + if (s == "win64") return ProcCC_Win64; + if (s == "sysv") return ProcCC_SysV; + if (s == "system") { if (build_context.metrics.os == TargetOs_windows) { return ProcCC_StdCall; } return ProcCC_CDecl; } + + return ProcCC_Invalid; } diff --git a/src/parser.hpp b/src/parser.hpp index ff0df0382..9e93f4b26 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -249,12 +249,30 @@ enum ProcCallingConvention : i32 { ProcCC_InlineAsm = 8, + ProcCC_Win64 = 9, + ProcCC_SysV = 10, + + ProcCC_MAX, ProcCC_ForeignBlockDefault = -1, }; +char const *proc_calling_convention_strings[ProcCC_MAX] = { + "", + "odin", + "contextless", + "cdecl", + "stdcall", + "fastcall", + "none", + "naked", + "inlineasm", + "win64", + "sysv", +}; + ProcCallingConvention default_calling_convention(void) { return ProcCC_Odin; } -- cgit v1.2.3 From ffc45e8cc239ebf72e38a17a47a57c19e8b4cb39 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 17 Feb 2022 20:48:37 +0000 Subject: Add `intrinsics.constant_utf16_cstring` --- src/check_builtin.cpp | 49 ++++++++++++++++++----------- src/checker_builtin_procs.hpp | 6 ++++ src/llvm_backend_proc.cpp | 71 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 18 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index c7ada8e03..1535f6644 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -223,31 +223,28 @@ void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Sliceinfo->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_allocateClassPair"); - 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 is_constant_string(CheckerContext *c, String const &builtin_name, Ast *expr, String *name_) { + 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; +} + 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) { @@ -371,6 +368,10 @@ bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call } operand->mode = Addressing_Value; + + add_package_dependency(c, "runtime", "objc_lookUpClass"); + add_package_dependency(c, "runtime", "sel_registerName"); + add_package_dependency(c, "runtime", "objc_allocateClassPair"); return true; } break; } @@ -4086,6 +4087,18 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; } + case BuiltinProc_constant_utf16_cstring: + { + String value = {}; + if (!is_constant_string(c, builtin_name, ce->args[0], &value)) { + return false; + } + operand->mode = Addressing_Value; + operand->type = alloc_type_multi_pointer(t_u16); + operand->value = {}; + break; + } + } diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 19fa94ee6..cba952ddf 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -258,6 +258,9 @@ BuiltinProc__type_end, BuiltinProc_objc_register_selector, BuiltinProc_objc_register_class, + BuiltinProc_constant_utf16_cstring, + + BuiltinProc_COUNT, }; gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { @@ -517,4 +520,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("objc_find_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("objc_register_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("objc_register_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + }; diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 261e2819c..7bc7fb61f 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -2122,6 +2122,77 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, case BuiltinProc_objc_find_class: return lb_handle_objc_find_class(p, expr); case BuiltinProc_objc_register_selector: return lb_handle_objc_register_selector(p, expr); case BuiltinProc_objc_register_class: return lb_handle_objc_register_class(p, expr); + + + case BuiltinProc_constant_utf16_cstring: + { + auto const encode_surrogate_pair = [](Rune r, u16 *r1, u16 *r2) { + if (r < 0x10000 || r > 0x10ffff) { + *r1 = 0xfffd; + *r2 = 0xfffd; + } else { + r -= 0x10000; + *r1 = 0xd800 + ((r>>10)&0x3ff); + *r2 = 0xdc00 + (r&0x3ff); + } + }; + + lbModule *m = p->module; + + auto tav = type_and_value_of_expr(ce->args[0]); + GB_ASSERT(tav.value.kind == ExactValue_String); + String value = tav.value.value_string; + + LLVMTypeRef llvm_u16 = lb_type(m, t_u16); + + isize max_len = value.len*2 + 1; + LLVMValueRef *buffer = gb_alloc_array(temporary_allocator(), LLVMValueRef, max_len); + isize n = 0; + while (value.len > 0) { + Rune r = 0; + isize w = gb_utf8_decode(value.text, value.len, &r); + value.text += w; + value.len -= w; + if ((0 <= r && r < 0xd800) || (0xe000 <= r && r < 0x10000)) { + buffer[n++] = LLVMConstInt(llvm_u16, cast(u16)r, false); + } else if (0x10000 <= r && r <= 0x10ffff) { + u16 r1, r2; + encode_surrogate_pair(r, &r1, &r2); + buffer[n++] = LLVMConstInt(llvm_u16, r1, false); + buffer[n++] = LLVMConstInt(llvm_u16, r2, false); + } else { + buffer[n++] = LLVMConstInt(llvm_u16, 0xfffd, false); + } + } + + buffer[n++] = LLVMConstInt(llvm_u16, 0, false); + + LLVMValueRef array = LLVMConstArray(llvm_u16, buffer, cast(unsigned int)n); + + char *name = nullptr; + { + isize max_len = 7+8+1; + name = gb_alloc_array(permanent_allocator(), char, max_len); + u32 id = m->gen->global_array_index.fetch_add(1); + isize len = gb_snprintf(name, max_len, "csbs$%x", id); + len -= 1; + } + LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(array), name); + LLVMSetInitializer(global_data, array); + LLVMSetLinkage(global_data, LLVMInternalLinkage); + + + + LLVMValueRef indices[] = { + LLVMConstInt(lb_type(m, t_u32), 0, false), + LLVMConstInt(lb_type(m, t_u32), 0, false), + }; + lbValue res = {}; + res.type = tv.type; + res.value = LLVMBuildInBoundsGEP(p->builder, global_data, indices, gb_count_of(indices), ""); + return res; + + } } GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name)); -- cgit v1.2.3 From ba61d911da7f08dfac7dd5eaf6ed7f6754e16eca Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 20 Feb 2022 13:26:27 +0000 Subject: Remove dead code --- src/llvm_backend_proc.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 7bc7fb61f..7ead77c2c 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -57,6 +57,7 @@ void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbVal LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); } + lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) { GB_ASSERT(entity != nullptr); GB_ASSERT(entity->kind == Entity_Procedure); @@ -163,14 +164,6 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) break; } - - - // lbCallingConventionKind cc_kind = lbCallingConvention_C; - // // TODO(bill): Clean up this logic - // if (build_context.metrics.os != TargetOs_js) { - // cc_kind = lb_calling_convention_map[pt->Proc.calling_convention]; - // } - // LLVMSetFunctionCallConv(p->value, cc_kind); lbValue proc_value = {p->value, p->type}; lb_add_entity(m, entity, proc_value); lb_add_member(m, p->name, proc_value); -- cgit v1.2.3 From 493bc653b5762514dac8c8941d6564ccf5bb8528 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 23 Feb 2022 11:23:27 +0000 Subject: Add `@(no_red_zone)` for procedures --- src/check_decl.cpp | 8 ++++++++ src/checker.cpp | 7 +++++++ src/checker.hpp | 1 + src/entity.cpp | 1 + src/llvm_backend_proc.cpp | 4 ++++ 5 files changed, 21 insertions(+) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 45d741532..6f8caff98 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -826,6 +826,14 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } e->Procedure.optimization_mode = cast(ProcedureOptimizationMode)ac.optimization_mode; + if (ac.no_red_zone) { + if (!is_arch_wasm()) { + e->Procedure.no_red_zone = true; + } else { + error(e->token, "@(no_red_zone) is not supported on this target architecture"); + } + } + if (ac.objc_name.len || ac.objc_is_class_method || ac.objc_type) { if (ac.objc_name.len == 0 && ac.objc_is_class_method) { error(e->token, "@(objc_name) is required with @(objc_is_class_method)"); diff --git a/src/checker.cpp b/src/checker.cpp index fe1d362fa..89e60c258 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3128,6 +3128,13 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) { } } return true; + } else if (name == "no_red_zone") { + if (value != nullptr) { + error(elem, "Expected no value for '%.*s'", LIT(name)); + } else { + ac->no_red_zone = true; + } + return true; } return false; } diff --git a/src/checker.hpp b/src/checker.hpp index 552e6aca7..548439774 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -117,6 +117,7 @@ struct AttributeContext { bool test : 1; bool init : 1; bool set_cold : 1; + bool no_red_zone : 1; u32 optimization_mode; // ProcedureOptimizationMode String objc_class; diff --git a/src/entity.cpp b/src/entity.cpp index f5720293f..84ddd5c6b 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -226,6 +226,7 @@ struct Entity { bool is_foreign; bool is_export; bool generated_from_polymorphic; + bool no_red_zone; ProcedureOptimizationMode optimization_mode; } Procedure; struct { diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 7ead77c2c..209f2f67b 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -135,6 +135,10 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) lb_add_attribute_to_proc(m, p->value, "naked"); } + if (entity->Procedure.no_red_zone) { + lb_add_attribute_to_proc(m, p->value, "noredzone"); + } + switch (p->inlining) { case ProcInlining_inline: lb_add_attribute_to_proc(m, p->value, "alwaysinline"); -- cgit v1.2.3 From 196bd735d4c30964182fdf1f374f767ccb0912fa Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 23 Feb 2022 11:29:36 +0000 Subject: Replace local `@(no_red_zone)` with global `-disable-red-zone` --- src/build_settings.cpp | 8 ++++++++ src/check_decl.cpp | 8 -------- src/checker.cpp | 7 ------- src/checker.hpp | 1 - src/entity.cpp | 1 - src/llvm_backend_proc.cpp | 2 +- src/main.cpp | 8 ++++++++ 7 files changed, 17 insertions(+), 18 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 790f7f1bc..62f43fad3 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -278,6 +278,7 @@ struct BuildContext { bool copy_file_contents; RelocMode reloc_mode; + bool disable_red_zone; u32 cmd_doc_flags; @@ -1002,6 +1003,13 @@ void init_build_context(TargetMetrics *cross_target) { bc->threaded_checker = true; #endif + if (bc->disable_red_zone) { + if (!(bc->metrics.os == TargetOs_freestanding && !is_arch_wasm())) { + gb_printf_err("-disable-red-zone is not support for this target"); + gb_exit(1); + } + } + // NOTE(zangent): The linker flags to set the build architecture are different // across OSs. It doesn't make sense to allocate extra data on the heap diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 6f8caff98..45d741532 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -826,14 +826,6 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } e->Procedure.optimization_mode = cast(ProcedureOptimizationMode)ac.optimization_mode; - if (ac.no_red_zone) { - if (!is_arch_wasm()) { - e->Procedure.no_red_zone = true; - } else { - error(e->token, "@(no_red_zone) is not supported on this target architecture"); - } - } - if (ac.objc_name.len || ac.objc_is_class_method || ac.objc_type) { if (ac.objc_name.len == 0 && ac.objc_is_class_method) { error(e->token, "@(objc_name) is required with @(objc_is_class_method)"); diff --git a/src/checker.cpp b/src/checker.cpp index 89e60c258..fe1d362fa 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3128,13 +3128,6 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) { } } return true; - } else if (name == "no_red_zone") { - if (value != nullptr) { - error(elem, "Expected no value for '%.*s'", LIT(name)); - } else { - ac->no_red_zone = true; - } - return true; } return false; } diff --git a/src/checker.hpp b/src/checker.hpp index 548439774..552e6aca7 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -117,7 +117,6 @@ struct AttributeContext { bool test : 1; bool init : 1; bool set_cold : 1; - bool no_red_zone : 1; u32 optimization_mode; // ProcedureOptimizationMode String objc_class; diff --git a/src/entity.cpp b/src/entity.cpp index 84ddd5c6b..f5720293f 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -226,7 +226,6 @@ struct Entity { bool is_foreign; bool is_export; bool generated_from_polymorphic; - bool no_red_zone; ProcedureOptimizationMode optimization_mode; } Procedure; struct { diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 209f2f67b..053ee2fb2 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -135,7 +135,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) lb_add_attribute_to_proc(m, p->value, "naked"); } - if (entity->Procedure.no_red_zone) { + if (!entity->Procedure.is_foreign && build_context.disable_red_zone) { lb_add_attribute_to_proc(m, p->value, "noredzone"); } diff --git a/src/main.cpp b/src/main.cpp index cfc7decb1..1e7b78da4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -626,6 +626,7 @@ enum BuildFlagKind { BuildFlag_Microarch, BuildFlag_RelocMode, + BuildFlag_DisableRedZone, BuildFlag_TestName, @@ -782,6 +783,7 @@ bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_Microarch, str_lit("microarch"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_RelocMode, str_lit("reloc-mode"), BuildFlagParam_String, Command__does_build); + add_flag(&build_flags, BuildFlag_DisableRedZone, str_lit("disable-red-zone"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_TestName, str_lit("test-name"), BuildFlagParam_String, Command_test); @@ -1365,6 +1367,9 @@ bool parse_build_flags(Array args) { break; } + case BuildFlag_DisableRedZone: + build_context.disable_red_zone = true; + break; case BuildFlag_TestName: { GB_ASSERT(value.kind == ExactValue_String); { @@ -2096,6 +2101,9 @@ void print_show_help(String const arg0, String const &command) { print_usage_line(3, "pic"); print_usage_line(3, "dynamic-no-pic"); print_usage_line(0, ""); + + print_usage_line(1, "-disable-red-zone"); + print_usage_line(2, "Disable red zone on a supported freestanding target"); } if (check) { -- cgit v1.2.3 From a5dde78f0882cc6db0b329f356dc7345d47798ca Mon Sep 17 00:00:00 2001 From: Joakim Hentula Date: Wed, 2 Mar 2022 16:44:33 +0000 Subject: Add relative slice to type checks for built in len --- src/check_builtin.cpp | 2 +- src/llvm_backend_proc.cpp | 4 ++-- src/llvm_backend_utility.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index aeeeb9e4d..365d3434f 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -952,7 +952,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 mode = Addressing_Constant; value = exact_value_i64(at->EnumeratedArray.count); type = t_untyped_integer; - } else if (is_type_slice(op_type) && id == BuiltinProc_len) { + } else if ((is_type_slice(op_type) || is_type_relative_slice(op_type)) && id == BuiltinProc_len) { mode = Addressing_Value; } else if (is_type_dynamic_array(op_type)) { mode = Addressing_Value; diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 7ead77c2c..eb6c89b85 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1042,7 +1042,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, return lb_string_len(p, v); } else if (is_type_array(t)) { GB_PANIC("Array lengths are constant"); - } else if (is_type_slice(t)) { + } else if (is_type_slice(t) || is_type_relative_slice(t)) { return lb_slice_len(p, v); } else if (is_type_dynamic_array(t)) { return lb_dynamic_array_len(p, v); @@ -1068,7 +1068,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, GB_PANIC("Unreachable"); } else if (is_type_array(t)) { GB_PANIC("Array lengths are constant"); - } else if (is_type_slice(t)) { + } else if (is_type_slice(t) || is_type_relative_slice(t)) { return lb_slice_len(p, v); } else if (is_type_dynamic_array(t)) { return lb_dynamic_array_cap(p, v); diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 98b7e07f0..431cfea9b 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -1373,7 +1373,7 @@ lbValue lb_slice_elem(lbProcedure *p, lbValue slice) { return lb_emit_struct_ev(p, slice, 0); } lbValue lb_slice_len(lbProcedure *p, lbValue slice) { - GB_ASSERT(is_type_slice(slice.type)); + GB_ASSERT(is_type_slice(slice.type) || is_type_relative_slice(slice.type)); return lb_emit_struct_ev(p, slice, 1); } lbValue lb_dynamic_array_elem(lbProcedure *p, lbValue da) { -- cgit v1.2.3 From f907516cbd0078b69996929d02742d0c1a48c226 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 12 Mar 2022 10:48:31 +0000 Subject: #Fix 1615 Replace `llvm.readcyclecounter` with `cntvct_el0` on arm64 --- src/llvm_backend_proc.cpp | 21 +++++++++++++++------ src/llvm_backend_utility.cpp | 2 +- 2 files changed, 16 insertions(+), 7 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index eb6c89b85..1698c211b 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1402,14 +1402,23 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, case BuiltinProc_read_cycle_counter: { - char const *name = "llvm.readcyclecounter"; - unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); - GB_ASSERT_MSG(id != 0, "Unable to find %s", name); - LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0); - lbValue res = {}; - res.value = LLVMBuildCall(p->builder, ip, nullptr, 0, ""); res.type = tv.type; + + if (build_context.metrics.arch == TargetArch_arm64) { + LLVMTypeRef func_type = LLVMFunctionType(LLVMInt64TypeInContext(p->module->ctx), nullptr, 0, false); + bool has_side_effects = false; + LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("mrs x9, cntvct_el0"), str_lit("=r"), has_side_effects); + GB_ASSERT(the_asm != nullptr); + res.value = LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, ""); + } else { + char const *name = "llvm.readcyclecounter"; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s", name); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0); + + res.value = LLVMBuildCall(p->builder, ip, nullptr, 0, ""); + } return res; } diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 431cfea9b..15075380a 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -1779,7 +1779,7 @@ LLVMValueRef llvm_get_inline_asm(LLVMTypeRef func_type, String const &str, Strin return LLVMGetInlineAsm(func_type, cast(char *)str.text, cast(size_t)str.len, cast(char *)clobbers.text, cast(size_t)clobbers.len, - /*HasSideEffects*/true, /*IsAlignStack*/false, + has_side_effects, is_align_stack, dialect #if LLVM_VERSION_MAJOR >= 13 , /*CanThrow*/false -- cgit v1.2.3 From 3a4630e6b417f5aaf4fb2b33ff545193f82310b9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 30 Mar 2022 16:15:23 +0100 Subject: Correct `atomic_cxchg_*` `atomic_cxchgweak_*` intrinsics behaviour to monotonic on failure for acq, rel, and acqrel --- src/llvm_backend_proc.cpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 27a6ec209..2d556b382 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1372,12 +1372,20 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, if (build_context.metrics.arch == TargetArch_i386 || build_context.metrics.arch == TargetArch_amd64) { LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false); - LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("pause"), {}); + LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("pause"), {}, true); GB_ASSERT(the_asm != nullptr); LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, ""); } else if (build_context.metrics.arch == TargetArch_arm64) { LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false); - LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("yield"), {}); + // NOTE(bill, 2022-03-30): `isb` appears to a better option that `yield` + // See: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8258604 + LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("isb"), {}, true); + GB_ASSERT(the_asm != nullptr); + LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, ""); + } else { + // NOTE: default to something to prevent optimization + LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false); + LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit(""), {}, true); GB_ASSERT(the_asm != nullptr); LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, ""); } @@ -1794,18 +1802,18 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, 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 = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break; - case BuiltinProc_atomic_cxchg_rel: success_ordering = LLVMAtomicOrderingRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break; - case BuiltinProc_atomic_cxchg_acqrel: success_ordering = LLVMAtomicOrderingAcquireRelease; 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 = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break; - case BuiltinProc_atomic_cxchgweak_rel: success_ordering = LLVMAtomicOrderingRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break; - case BuiltinProc_atomic_cxchgweak_acqrel: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; 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; -- cgit v1.2.3 From 72ae0617694593aecf4b3d0006f2786d7ad4dea6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 30 Mar 2022 17:29:37 +0100 Subject: Add `intrinsics.wasm_memory_grow` `intrinsics.wasm_memory_size` --- core/intrinsics/intrinsics.odin | 4 +++ src/check_builtin.cpp | 69 ++++++++++++++++++++++++++++++++++++++++- src/check_decl.cpp | 4 ++- src/checker_builtin_procs.hpp | 4 +++ src/llvm_backend_proc.cpp | 39 +++++++++++++++++++++++ 5 files changed, 118 insertions(+), 2 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index c3ef787bc..16dd5cbc5 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -201,6 +201,10 @@ type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, raw type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) where type_is_comparable(T) --- +// WASM targets only +wasm_memory_grow :: proc(index, delta: uintptr) -> int --- +wasm_memory_size :: proc(index: uintptr) -> int --- + // Internal compiler use only __entry_point :: proc() --- \ No newline at end of file diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 4cd09b8e9..e480704e3 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -304,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' 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", LIT(builtin_name), e, t); gb_string_free(t); gb_string_free(e); return false; @@ -4111,6 +4111,73 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } + case BuiltinProc_wasm_memory_grow: + { + if (!is_arch_wasm()) { + error(call, "'%.*s' is only allowed on wasm targets", LIT(builtin_name)); + return false; + } + + Operand index = {}; + Operand delta = {}; + check_expr(c, &index, ce->args[0]); if (index.mode == Addressing_Invalid) return false; + check_expr(c, &delta, ce->args[1]); if (delta.mode == Addressing_Invalid) return false; + + convert_to_typed(c, &index, t_uintptr); if (index.mode == Addressing_Invalid) return false; + convert_to_typed(c, &delta, t_uintptr); if (delta.mode == Addressing_Invalid) return false; + + if (!is_operand_value(index) || !check_is_assignable_to(c, &index, t_uintptr)) { + gbString e = expr_to_string(index.expr); + gbString t = type_to_string(index.type); + error(index.expr, "'%.*s' expected a uintptr for the memory index, got '%s' of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } + + if (!is_operand_value(delta) || !check_is_assignable_to(c, &delta, t_uintptr)) { + gbString e = expr_to_string(delta.expr); + gbString t = type_to_string(delta.type); + error(delta.expr, "'%.*s' expected a uintptr for the memory delta, got '%s' of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } + + operand->mode = Addressing_Value; + operand->type = t_int; + operand->value = {}; + break; + } + break; + case BuiltinProc_wasm_memory_size: + { + if (!is_arch_wasm()) { + error(call, "'%.*s' is only allowed on wasm targets", LIT(builtin_name)); + return false; + } + + Operand index = {}; + check_expr(c, &index, ce->args[0]); if (index.mode == Addressing_Invalid) return false; + + convert_to_typed(c, &index, t_uintptr); if (index.mode == Addressing_Invalid) return false; + + if (!is_operand_value(index) || !check_is_assignable_to(c, &index, t_uintptr)) { + gbString e = expr_to_string(index.expr); + gbString t = type_to_string(index.type); + error(index.expr, "'%.*s' expected a uintptr for the memory index, got '%s' of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } + + operand->mode = Addressing_Value; + operand->type = t_int; + operand->value = {}; + break; + } + break; + } return true; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index d4a320f03..5acd56097 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1137,7 +1137,9 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr, ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) { - error(e->token, "@(thread_local) is not supported for this target platform"); + e->Variable.thread_local_model.len = 0; + // NOTE(bill): ignore this message for the time begin + // error(e->token, "@(thread_local) is not supported for this target platform"); } String context_name = str_lit("variable declaration"); diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index cba952ddf..c647d5a01 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -260,6 +260,8 @@ BuiltinProc__type_end, BuiltinProc_constant_utf16_cstring, + BuiltinProc_wasm_memory_grow, + BuiltinProc_wasm_memory_size, BuiltinProc_COUNT, }; @@ -523,4 +525,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("wasm_memory_grow"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("wasm_memory_size"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, }; diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 2d556b382..d7f3d6c45 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -2207,6 +2207,45 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, return res; } + + case BuiltinProc_wasm_memory_grow: + { + char const *name = "llvm.wasm.memory.grow"; + LLVMTypeRef types[1] = { + lb_type(p->module, t_uintptr), + }; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0])); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); + + LLVMValueRef args[2] = {}; + args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_uintptr).value; + args[1] = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_uintptr).value; + + lbValue res = {}; + res.type = tv.type; + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + return res; + } + case BuiltinProc_wasm_memory_size: + { + char const *name = "llvm.wasm.memory.size"; + LLVMTypeRef types[1] = { + lb_type(p->module, t_uintptr), + }; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0])); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); + + LLVMValueRef args[1] = {}; + args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_uintptr).value; + + lbValue res = {}; + res.type = tv.type; + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + return res; + } + } GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name)); -- 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/llvm_backend_proc.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 6636376a81371506d61d24aa2ea3264b6e47edcb Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 31 Mar 2022 00:58:01 +0100 Subject: Correct weak handling --- src/llvm_backend_proc.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index ad82b79e5..4de2af9d8 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1738,7 +1738,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, } // TODO(bill): Figure out how to make it weak - LLVMBool single_threaded = weak; + LLVMBool single_threaded = false; LLVMValueRef value = LLVMBuildAtomicCmpXchg( p->builder, address.value, @@ -1747,6 +1747,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, failure_ordering, single_threaded ); + LLVMSetWeak(value, weak); if (tv.type->kind == Type_Tuple) { Type *fix_typed = alloc_type_tuple(); -- 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/llvm_backend_proc.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 18ad6c33ef896fc7b7bbf27af8b807b36b59e561 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 1 May 2022 23:32:31 +0100 Subject: Implement syscall for arm32 --- src/llvm_backend_proc.cpp | 171 ++++++++++++++++++++++++++-------------------- 1 file changed, 98 insertions(+), 73 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 96ff19d10..06a74f625 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -189,7 +189,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) GB_ASSERT(entity->kind == Entity_Procedure); String link_name = entity->Procedure.link_name; if (entity->flags & EntityFlag_CustomLinkName && - link_name != "") { + link_name != "") { if (string_starts_with(link_name, str_lit("__"))) { LLVMSetLinkage(p->value, LLVMExternalLinkage); } else { @@ -200,12 +200,12 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) } } lb_set_linkage_from_entity_flags(p->module, p->value, entity->flags); - - + + if (p->is_foreign) { lb_set_wasm_import_attributes(p->value, entity, p->name); } - + // NOTE(bill): offset==0 is the return value isize offset = 1; @@ -280,7 +280,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) if (p->body != nullptr) { // String debug_name = entity->token.string.text; String debug_name = p->name; - + p->debug_info = LLVMDIBuilderCreateFunction(m->debug_builder, scope, cast(char const *)debug_name.text, debug_name.len, cast(char const *)p->name.text, p->name.len, @@ -1315,22 +1315,22 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, case BuiltinProc_clamp: return lb_emit_clamp(p, type_of_expr(expr), - lb_build_expr(p, ce->args[0]), - lb_build_expr(p, ce->args[1]), - lb_build_expr(p, ce->args[2])); + lb_build_expr(p, ce->args[0]), + lb_build_expr(p, ce->args[1]), + lb_build_expr(p, ce->args[2])); case BuiltinProc_soa_zip: return lb_soa_zip(p, ce, tv); case BuiltinProc_soa_unzip: return lb_soa_unzip(p, ce, tv); - + case BuiltinProc_transpose: { lbValue m = lb_build_expr(p, ce->args[0]); return lb_emit_matrix_tranpose(p, m, tv.type); } - + case BuiltinProc_outer_product: { lbValue a = lb_build_expr(p, ce->args[0]); @@ -1347,13 +1347,13 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, GB_ASSERT(is_type_matrix(tv.type)); return lb_emit_arith_matrix(p, Token_Mul, a, b, tv.type, true); } - + case BuiltinProc_matrix_flatten: { lbValue m = lb_build_expr(p, ce->args[0]); return lb_emit_matrix_flatten(p, m, tv.type); } - + // "Intrinsics" case BuiltinProc_alloca: @@ -1370,7 +1370,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, case BuiltinProc_cpu_relax: if (build_context.metrics.arch == TargetArch_i386 || - build_context.metrics.arch == TargetArch_amd64) { + build_context.metrics.arch == TargetArch_amd64) { LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false); LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("pause"), {}, true); GB_ASSERT(the_asm != nullptr); @@ -1538,7 +1538,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, lbValue dst = lb_build_expr(p, ce->args[0]); lbValue src = lb_build_expr(p, ce->args[1]); lbValue len = lb_build_expr(p, ce->args[2]); - + lb_mem_copy_overlapping(p, dst, src, len, false); return {}; } @@ -1547,7 +1547,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, lbValue dst = lb_build_expr(p, ce->args[0]); lbValue src = lb_build_expr(p, ce->args[1]); lbValue len = lb_build_expr(p, ce->args[2]); - + lb_mem_copy_non_overlapping(p, dst, src, len, false); return {}; } @@ -1651,7 +1651,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, res.type = type_deref(dst.type); return res; } - + case BuiltinProc_unaligned_store: { lbValue dst = lb_build_expr(p, ce->args[0]); @@ -1661,7 +1661,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, lb_mem_copy_non_overlapping(p, dst, src, lb_const_int(p->module, t_int, type_size_of(t)), false); return {}; } - + case BuiltinProc_unaligned_load: { lbValue src = lb_build_expr(p, ce->args[0]); @@ -1843,7 +1843,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, res.type = t; return lb_emit_conv(p, res, t); } - + case BuiltinProc_prefetch_read_instruction: case BuiltinProc_prefetch_read_data: case BuiltinProc_prefetch_write_instruction: @@ -1871,27 +1871,27 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, cache = 1; break; } - + char const *name = "llvm.prefetch"; - + LLVMTypeRef types[1] = {lb_type(p->module, t_rawptr)}; 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)); - + LLVMTypeRef llvm_i32 = lb_type(p->module, t_i32); LLVMValueRef args[4] = {}; args[0] = ptr.value; args[1] = LLVMConstInt(llvm_i32, rw, false); args[2] = LLVMConstInt(llvm_i32, locality, false); args[3] = LLVMConstInt(llvm_i32, cache, false); - + lbValue res = {}; res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); res.type = nullptr; return res; } - + case BuiltinProc___entry_point: if (p->module->info->entry_point) { lbValue entry_point = lb_find_procedure_value_from_entity(p->module, p->module->info->entry_point); @@ -1909,22 +1909,22 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, arg = lb_emit_conv(p, arg, t_uintptr); args[i] = arg.value; } - + LLVMTypeRef llvm_uintptr = lb_type(p->module, t_uintptr); LLVMTypeRef *llvm_arg_types = gb_alloc_array(permanent_allocator(), LLVMTypeRef, arg_count); for (unsigned i = 0; i < arg_count; i++) { llvm_arg_types[i] = llvm_uintptr; } - + LLVMTypeRef func_type = LLVMFunctionType(llvm_uintptr, llvm_arg_types, arg_count, false); - + LLVMValueRef inline_asm = nullptr; - + switch (build_context.metrics.arch) { case TargetArch_amd64: { GB_ASSERT(arg_count <= 7); - + char asm_string[] = "syscall"; gbString constraints = gb_string_make(heap_allocator(), "={rax}"); for (unsigned i = 0; i < arg_count; i++) { @@ -1963,11 +1963,11 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, case TargetArch_i386: { GB_ASSERT(arg_count <= 7); - + char asm_string_default[] = "int $0x80"; char *asm_string = asm_string_default; gbString constraints = gb_string_make(heap_allocator(), "={eax}"); - + for (unsigned i = 0; i < gb_min(arg_count, 6); i++) { constraints = gb_string_appendc(constraints, ",{"); static char const *regs[] = { @@ -1984,56 +1984,81 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, if (arg_count == 7) { char asm_string7[] = "push %[arg6]\npush %%ebp\nmov 4(%%esp), %%ebp\nint $0x80\npop %%ebp\nadd $4, %%esp"; asm_string = asm_string7; - + constraints = gb_string_appendc(constraints, ",rm"); } - + inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints)); } break; case TargetArch_arm64: { - GB_ASSERT(arg_count <= 7); - - if(build_context.metrics.os == TargetOs_darwin) { - char asm_string[] = "svc #0x80"; - gbString constraints = gb_string_make(heap_allocator(), "={x0}"); - for (unsigned i = 0; i < arg_count; i++) { - constraints = gb_string_appendc(constraints, ",{"); - static char const *regs[] = { - "x16", - "x0", - "x1", - "x2", - "x3", - "x4", - "x5", - }; - constraints = gb_string_appendc(constraints, regs[i]); - constraints = gb_string_appendc(constraints, "}"); - } - - inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints)); - } else { - char asm_string[] = "svc #0"; - gbString constraints = gb_string_make(heap_allocator(), "={x0}"); - for (unsigned i = 0; i < arg_count; i++) { - constraints = gb_string_appendc(constraints, ",{"); - static char const *regs[] = { - "x8", - "x0", - "x1", - "x2", - "x3", - "x4", - "x5", - }; - constraints = gb_string_appendc(constraints, regs[i]); - constraints = gb_string_appendc(constraints, "}"); - } - - inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints)); - } + GB_ASSERT(arg_count <= 7); + + if(build_context.metrics.os == TargetOs_darwin) { + char asm_string[] = "svc #0x80"; + gbString constraints = gb_string_make(heap_allocator(), "={x0}"); + for (unsigned i = 0; i < arg_count; i++) { + constraints = gb_string_appendc(constraints, ",{"); + static char const *regs[] = { + "x16", + "x0", + "x1", + "x2", + "x3", + "x4", + "x5", + }; + constraints = gb_string_appendc(constraints, regs[i]); + constraints = gb_string_appendc(constraints, "}"); + } + + inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints)); + } else { + char asm_string[] = "svc #0"; + gbString constraints = gb_string_make(heap_allocator(), "={x0}"); + for (unsigned i = 0; i < arg_count; i++) { + constraints = gb_string_appendc(constraints, ",{"); + static char const *regs[] = { + "x8", + "x0", + "x1", + "x2", + "x3", + "x4", + "x5", + }; + constraints = gb_string_appendc(constraints, regs[i]); + constraints = gb_string_appendc(constraints, "}"); + } + + inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints)); + } + } + break; + case TargetArch_arm32: + { + // TODO(bill): Check this is correct + GB_ASSERT(arg_count <= 7); + + char asm_string[] = "svc #0"; + gbString constraints = gb_string_make(heap_allocator(), "={r0}"); + for (unsigned i = 0; i < arg_count; i++) { + constraints = gb_string_appendc(constraints, ",{"); + static char const *regs[] = { + "r8", + "r0", + "r1", + "r2", + "r3", + "r4", + "r5", + }; + constraints = gb_string_appendc(constraints, regs[i]); + constraints = gb_string_appendc(constraints, "}"); + } + + inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints)); } break; default: -- cgit v1.2.3 From 8023c8abc7087c763da5e843252d3899784e57be Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 2 May 2022 14:10:02 +0100 Subject: Fix `@(disable=...)` --- src/llvm_backend_proc.cpp | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 06a74f625..a0e9a5da5 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -823,12 +823,6 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, GB_ASSERT(pt->kind == Type_Proc); Type *results = pt->Proc.results; - if (p->entity != nullptr) { - if (p->entity->flags & EntityFlag_Disabled) { - return {}; - } - } - lbAddr context_ptr = {}; if (pt->Proc.calling_convention == ProcCC_Odin) { context_ptr = lb_find_or_generate_context_ptr(p); @@ -2280,6 +2274,15 @@ lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { // NOTE(bill): Regular call lbValue value = {}; Ast *proc_expr = unparen_expr(ce->proc); + + Entity *proc_entity = entity_of_node(proc_expr); + if (proc_entity != nullptr) { + if (proc_entity->flags & EntityFlag_Disabled) { + GB_ASSERT(tv.type == nullptr); + return {}; + } + } + if (proc_expr->tav.mode == Addressing_Constant) { ExactValue v = proc_expr->tav.value; switch (v.kind) { @@ -2306,13 +2309,6 @@ lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { } } - Entity *proc_entity = entity_of_node(proc_expr); - if (proc_entity != nullptr) { - if (proc_entity->flags & EntityFlag_Disabled) { - return {}; - } - } - if (value.value == nullptr) { value = lb_build_expr(p, proc_expr); } -- cgit v1.2.3 From 846930a07fc83873fbbcdde33e48600a6d9a2a8f Mon Sep 17 00:00:00 2001 From: Tobias Mollstam Date: Wed, 18 May 2022 07:01:19 +0200 Subject: emit optnone and noinline for all procs when opt set to minimal --- src/llvm_backend_proc.cpp | 49 ++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 22 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index a0e9a5da5..a7f9eb013 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -139,35 +139,40 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) lb_add_attribute_to_proc(m, p->value, "noredzone"); } - switch (p->inlining) { - case ProcInlining_inline: - lb_add_attribute_to_proc(m, p->value, "alwaysinline"); - break; - case ProcInlining_no_inline: + if (build_context.optimization_level == 0 && build_context.ODIN_DEBUG) { lb_add_attribute_to_proc(m, p->value, "noinline"); - break; + lb_add_attribute_to_proc(m, p->value, "optnone"); + } else { + switch (p->inlining) { + case ProcInlining_inline: + lb_add_attribute_to_proc(m, p->value, "alwaysinline"); + break; + case ProcInlining_no_inline: + lb_add_attribute_to_proc(m, p->value, "noinline"); + break; + } + + switch (entity->Procedure.optimization_mode) { + case ProcedureOptimizationMode_None: + lb_add_attribute_to_proc(m, p->value, "optnone"); + break; + case ProcedureOptimizationMode_Minimal: + lb_add_attribute_to_proc(m, p->value, "optnone"); + break; + case ProcedureOptimizationMode_Size: + lb_add_attribute_to_proc(m, p->value, "optsize"); + break; + case ProcedureOptimizationMode_Speed: + // TODO(bill): handle this correctly + lb_add_attribute_to_proc(m, p->value, "optsize"); + break; + } } if (entity->flags & EntityFlag_Cold) { lb_add_attribute_to_proc(m, p->value, "cold"); } - switch (entity->Procedure.optimization_mode) { - case ProcedureOptimizationMode_None: - lb_add_attribute_to_proc(m, p->value, "optnone"); - break; - case ProcedureOptimizationMode_Minimal: - lb_add_attribute_to_proc(m, p->value, "optnone"); - break; - case ProcedureOptimizationMode_Size: - lb_add_attribute_to_proc(m, p->value, "optsize"); - break; - case ProcedureOptimizationMode_Speed: - // TODO(bill): handle this correctly - lb_add_attribute_to_proc(m, p->value, "optsize"); - break; - } - lbValue proc_value = {p->value, p->type}; lb_add_entity(m, entity, proc_value); lb_add_member(m, p->name, proc_value); -- cgit v1.2.3 From e48f41165c54913e07d1ab21bebb6a439524cf7c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 21 May 2022 12:58:48 +0100 Subject: Begin work on Atomics for wasm32 (wait and notify intrinsics) --- core/intrinsics/intrinsics.odin | 9 ++++ core/sync/futex_wasm.odin | 36 ++++++++++++++++ core/sync/primitives_wasm.odin | 8 ++++ src/check_builtin.cpp | 93 +++++++++++++++++++++++++++++++++++++++++ src/checker_builtin_procs.hpp | 4 ++ src/llvm_backend_proc.cpp | 45 ++++++++++++++++++++ vendor/wasm/js/runtime.js | 36 ++++++++++++---- 7 files changed, 223 insertions(+), 8 deletions(-) create mode 100644 core/sync/futex_wasm.odin create mode 100644 core/sync/primitives_wasm.odin (limited to 'src/llvm_backend_proc.cpp') diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index 85859e8c3..d71522936 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -190,6 +190,15 @@ constant_utf16_cstring :: proc($literal: string) -> [^]u16 --- wasm_memory_grow :: proc(index, delta: uintptr) -> int --- wasm_memory_size :: proc(index: uintptr) -> int --- +// `timeout_ns` is maximum number of nanoseconds the calling thread will be blocked for +// A negative value will be blocked forever +// Return value: +// 0 - indicates that the thread blocked and then was woken up +// 1 - the loaded value from `ptr` did not match `expected`, the thread did not block +// 2 - the thread blocked, but the timeout +wasm_memory_atomic_wait32 :: proc(ptr: ^u32, expected: u32, timeout_ns: i64) -> u32 --- +wasm_memory_atomic_notify32 :: proc(ptr: ^u32, waiters: u32) -> (waiters_woken_up: u32) --- + // Darwin targets only objc_object :: struct{} diff --git a/core/sync/futex_wasm.odin b/core/sync/futex_wasm.odin new file mode 100644 index 000000000..9e96614d6 --- /dev/null +++ b/core/sync/futex_wasm.odin @@ -0,0 +1,36 @@ +//+private +//+build wasm32 +package sync + +import "core:intrinsics" +import "core:time" + +_futex_wait :: proc(f: ^Futex, expected: u32) -> bool { + s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, -1) + return s != 0 +} + +_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool { + s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, i64(duration)) + return s != 0 + +} + +_futex_signal :: proc(f: ^Futex) { + loop: for { + s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), 1) + if s >= 1 { + return + } + } +} + +_futex_broadcast :: proc(f: ^Futex) { + loop: for { + s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), ~u32(0)) + if s >= 0 { + return + } + } +} + diff --git a/core/sync/primitives_wasm.odin b/core/sync/primitives_wasm.odin new file mode 100644 index 000000000..283971ac5 --- /dev/null +++ b/core/sync/primitives_wasm.odin @@ -0,0 +1,8 @@ +//+private +//+build wasm32 +package sync + +_current_thread_id :: proc "contextless" () -> int { + // TODO(bill): _current_thread_id for wasm32 + return 0 +} \ No newline at end of file diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 9a5d1c554..65983423b 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -4473,6 +4473,99 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } break; + case BuiltinProc_wasm_memory_atomic_wait32: + { + if (!is_arch_wasm()) { + error(call, "'%.*s' is only allowed on wasm targets", LIT(builtin_name)); + return false; + } + + Operand ptr = {}; + Operand expected = {}; + Operand timeout = {}; + check_expr(c, &ptr, ce->args[0]); if (ptr.mode == Addressing_Invalid) return false; + check_expr(c, &expected, ce->args[1]); if (expected.mode == Addressing_Invalid) return false; + check_expr(c, &timeout, ce->args[2]); if (timeout.mode == Addressing_Invalid) return false; + + Type *t_u32_ptr = alloc_type_pointer(t_u32); + convert_to_typed(c, &ptr, t_u32_ptr); if (ptr.mode == Addressing_Invalid) return false; + convert_to_typed(c, &expected, t_u32); if (expected.mode == Addressing_Invalid) return false; + convert_to_typed(c, &timeout, t_i64); if (timeout.mode == Addressing_Invalid) return false; + + if (!is_operand_value(ptr) || !check_is_assignable_to(c, &ptr, t_u32_ptr)) { + gbString e = expr_to_string(ptr.expr); + gbString t = type_to_string(ptr.type); + error(ptr.expr, "'%.*s' expected ^u32 for the memory pointer, got '%s' of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } + + if (!is_operand_value(expected) || !check_is_assignable_to(c, &expected, t_u32)) { + gbString e = expr_to_string(expected.expr); + gbString t = type_to_string(expected.type); + error(expected.expr, "'%.*s' expected u32 for the 'expected' value, got '%s' of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } + + if (!is_operand_value(timeout) || !check_is_assignable_to(c, &timeout, t_i64)) { + gbString e = expr_to_string(timeout.expr); + gbString t = type_to_string(timeout.type); + error(timeout.expr, "'%.*s' expected i64 for the timeout, got '%s' of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } + + operand->mode = Addressing_Value; + operand->type = t_u32; + operand->value = {}; + break; + } + break; + case BuiltinProc_wasm_memory_atomic_notify32: + { + if (!is_arch_wasm()) { + error(call, "'%.*s' is only allowed on wasm targets", LIT(builtin_name)); + return false; + } + + Operand ptr = {}; + Operand waiters = {}; + check_expr(c, &ptr, ce->args[0]); if (ptr.mode == Addressing_Invalid) return false; + check_expr(c, &waiters, ce->args[1]); if (waiters.mode == Addressing_Invalid) return false; + + Type *t_u32_ptr = alloc_type_pointer(t_u32); + convert_to_typed(c, &ptr, t_u32_ptr); if (ptr.mode == Addressing_Invalid) return false; + convert_to_typed(c, &waiters, t_u32); if (waiters.mode == Addressing_Invalid) return false; + + if (!is_operand_value(ptr) || !check_is_assignable_to(c, &ptr, t_u32_ptr)) { + gbString e = expr_to_string(ptr.expr); + gbString t = type_to_string(ptr.type); + error(ptr.expr, "'%.*s' expected ^u32 for the memory pointer, got '%s' of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } + + if (!is_operand_value(waiters) || !check_is_assignable_to(c, &waiters, t_u32)) { + gbString e = expr_to_string(waiters.expr); + gbString t = type_to_string(waiters.type); + error(waiters.expr, "'%.*s' expected u32 for the 'waiters' value, got '%s' of type %s", LIT(builtin_name), e, t); + gb_string_free(t); + gb_string_free(e); + return false; + } + + operand->mode = Addressing_Value; + operand->type = t_u32; + operand->value = {}; + break; + } + break; + } return true; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index d301cae0c..d407ef7c1 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -218,6 +218,8 @@ BuiltinProc__type_end, BuiltinProc_wasm_memory_grow, BuiltinProc_wasm_memory_size, + BuiltinProc_wasm_memory_atomic_wait32, + BuiltinProc_wasm_memory_atomic_notify32, BuiltinProc_COUNT, }; @@ -438,4 +440,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("wasm_memory_grow"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("wasm_memory_size"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("wasm_memory_atomic_wait32"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("wasm_memory_atomic_notify32"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, }; diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index a7f9eb013..2539e6e2e 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -2187,6 +2187,51 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, return res; } + case BuiltinProc_wasm_memory_atomic_wait32: + { + char const *name = "llvm.wasm.memory.atomic.wait32"; + LLVMTypeRef types[1] = { + lb_type(p->module, t_u32), + }; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0])); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); + + Type *t_u32_ptr = alloc_type_pointer(t_u32); + + LLVMValueRef args[3] = {}; + args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_u32_ptr).value; + args[1] = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_u32).value; + args[2] = lb_emit_conv(p, lb_build_expr(p, ce->args[2]), t_i64).value; + + lbValue res = {}; + res.type = tv.type; + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + return res; + } + + case BuiltinProc_wasm_memory_atomic_notify32: + { + char const *name = "llvm.wasm.memory.atomic.notify"; + LLVMTypeRef types[1] = { + lb_type(p->module, t_u32), + }; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0])); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); + + Type *t_u32_ptr = alloc_type_pointer(t_u32); + + LLVMValueRef args[2] = {}; + args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_u32_ptr).value; + args[1] = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_u32).value; + + lbValue res = {}; + res.type = tv.type; + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + return res; + } + } GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name)); diff --git a/vendor/wasm/js/runtime.js b/vendor/wasm/js/runtime.js index 52fe43b6c..20ed7acfa 100644 --- a/vendor/wasm/js/runtime.js +++ b/vendor/wasm/js/runtime.js @@ -1176,10 +1176,31 @@ class WebGLInterface { function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) { const MAX_INFO_CONSOLE_LINES = 512; let infoConsoleLines = new Array(); + let currentLine = ""; + const addConsoleLine = (line) => { if (!line) { return; } + if (!line.includes("\n")) { + currentLine = currentLine.concat(line); + } else { + let printLast = line.endsWith("\n"); + let lines = line.split("\n"); + for (let i = 0; i < lines.length-1; i++) { + let theLine = lines[i].trim("\r"); + currentLine = currentLine.concat(line); + console.log(currentLine); + currentLine = ""; + } + console.log(lines); + if (printLast) { + console.log(lines[lines.length-1]); + } else { + currentLine = currentLine.concat(lines[lines.length-1]); + } + } + if (line.endsWith("\n")) { line = line.substring(0, line.length-1); } else if (infoConsoleLines.length > 0) { @@ -1191,16 +1212,15 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement) { if (infoConsoleLines.length > MAX_INFO_CONSOLE_LINES) { infoConsoleLines.shift(); } - - let data = ""; - for (let i = 0; i < infoConsoleLines.length; i++) { - if (i != 0) { - data = data.concat("\n"); + if (consoleElement) { + let data = ""; + for (let i = 0; i < infoConsoleLines.length; i++) { + if (i != 0) { + data = data.concat("\n"); + } + data = data.concat(infoConsoleLines[i]); } - data = data.concat(infoConsoleLines[i]); - } - if (consoleElement) { let info = consoleElement; info.innerHTML = data; info.scrollTop = info.scrollHeight; -- cgit v1.2.3 From 184d1c57b10260ccb6e8032c34680c5c9c272c43 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 21 May 2022 13:37:00 +0100 Subject: Change atomic.wait32 and atomic.notify selection --- src/llvm_backend_proc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 2539e6e2e..154be2f1f 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -2195,7 +2195,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, }; unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0])); - LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0); // types, gb_count_of(types)); Type *t_u32_ptr = alloc_type_pointer(t_u32); @@ -2218,7 +2218,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, }; unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0])); - LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0); // types, gb_count_of(types)); Type *t_u32_ptr = alloc_type_pointer(t_u32); -- cgit v1.2.3 From 81dd727f750aad45c6468de64a12119a3141de55 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 May 2022 18:49:17 +0100 Subject: Implement backend for simd intrinsics --- src/check_builtin.cpp | 58 ++++++++++++- src/llvm_backend_proc.cpp | 201 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 255 insertions(+), 4 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 939892707..13eb9f47d 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -464,10 +464,6 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call // 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: @@ -510,6 +506,60 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call operand->type = x.type; return true; } + + case BuiltinProc_simd_shl: + case BuiltinProc_simd_shr: + case BuiltinProc_simd_shl_masked: + case BuiltinProc_simd_shr_masked: + { + 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; + } + GB_ASSERT(x.type->kind == Type_SimdVector); + GB_ASSERT(y.type->kind == Type_SimdVector); + Type *xt = x.type; + Type *yt = y.type; + + if (xt->SimdVector.count != yt->SimdVector.count) { + error(x.expr, "'%.*s' mismatched simd vector lengths, got '%lld' vs '%lld'", + LIT(builtin_name), + cast(long long)xt->SimdVector.count, + cast(long long)yt->SimdVector.count); + return false; + } + if (!is_type_integer(base_array_type(x.type))) { + 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 (!is_type_unsigned(base_array_type(y.type))) { + gbString ys = type_to_string(y.type); + error(y.expr, "'%.*s' expected a #simd type with an unsigned integer element as the shifting operand, got '%s'", LIT(builtin_name), ys); + gb_string_free(ys); + return false; + } + + operand->mode = Addressing_Value; + operand->type = x.type; + return true; + } + // Unary case BuiltinProc_simd_neg: case BuiltinProc_simd_abs: diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 154be2f1f..82ad6daef 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -981,10 +981,211 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, return result; } +lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, BuiltinProcId id) { + ast_node(ce, CallExpr, expr); + + lbModule *m = p->module; + + lbValue res = {}; + res.type = tv.type; + + lbValue arg0 = lb_build_expr(p, ce->args[0]); + lbValue arg1 = {}; + + Type *elem = base_array_type(arg0.type); + + bool is_float = is_type_float(elem); + bool is_signed = !is_type_unsigned(elem); + + LLVMOpcode op_code = cast(LLVMOpcode)0; + + switch (id) { + case BuiltinProc_simd_add: + case BuiltinProc_simd_sub: + case BuiltinProc_simd_mul: + case BuiltinProc_simd_div: + case BuiltinProc_simd_rem: + arg1 = lb_build_expr(p, ce->args[1]); + if (is_float) { + switch (id) { + case BuiltinProc_simd_add: op_code = LLVMFAdd; break; + case BuiltinProc_simd_sub: op_code = LLVMFSub; break; + case BuiltinProc_simd_mul: op_code = LLVMFMul; break; + case BuiltinProc_simd_div: op_code = LLVMFDiv; break; + } + } else { + switch (id) { + case BuiltinProc_simd_add: op_code = LLVMAdd; break; + case BuiltinProc_simd_sub: op_code = LLVMSub; break; + case BuiltinProc_simd_mul: op_code = LLVMMul; break; + case BuiltinProc_simd_div: + if (is_signed) { + op_code = LLVMSDiv; + } else { + op_code = LLVMUDiv; + } + break; + case BuiltinProc_simd_rem: + if (is_signed) { + op_code = LLVMSRem; + } else { + op_code = LLVMURem; + } + break; + } + } + if (op_code) { + res.value = LLVMBuildBinOp(p->builder, op_code, arg0.value, arg1.value, ""); + return res; + } + break; + case BuiltinProc_simd_shl: // Odin logic + case BuiltinProc_simd_shr: // Odin logic + case BuiltinProc_simd_shl_masked: // C logic + case BuiltinProc_simd_shr_masked: // C logic + arg1 = lb_build_expr(p, ce->args[1]); + { + i64 sz = type_size_of(elem); + GB_ASSERT(arg0.type->kind == Type_SimdVector); + + i64 count = arg0.type->SimdVector.count; + Type *elem1 = base_array_type(arg1.type); + + bool is_masked = false; + switch (id) { + case BuiltinProc_simd_shl: op_code = LLVMShl; is_masked = false; break; + case BuiltinProc_simd_shr: op_code = is_signed ? LLVMAShr : LLVMLShr; is_masked = false; break; + case BuiltinProc_simd_shl_masked: op_code = LLVMShl; is_masked = true; break; + case BuiltinProc_simd_shr_masked: op_code = is_signed ? LLVMAShr : LLVMLShr; is_masked = true; break; + } + if (op_code) { + LLVMValueRef bit_value = lb_const_int(m, elem1, sz*8 - 1).value; + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, count); + for (i64 i = 0; i < count; i++) { + values[i] = bit_value; + } + LLVMValueRef bits = LLVMConstVector(values, cast(unsigned)count); + if (is_masked) { + // C logic + LLVMValueRef shift = LLVMBuildAnd(p->builder, arg1.value, bits, ""); + res.value = LLVMBuildBinOp(p->builder, op_code, arg0.value, shift, ""); + } else { + // Odin logic + LLVMValueRef zero = lb_const_nil(m, arg1.type).value; + LLVMValueRef mask = LLVMBuildICmp(p->builder, LLVMIntULE, arg1.value, bits, ""); + LLVMValueRef shift = LLVMBuildBinOp(p->builder, op_code, arg0.value, arg1.value, ""); + res.value = LLVMBuildSelect(p->builder, mask, shift, zero, ""); + } + + return res; + } + } + break; + case BuiltinProc_simd_and: + case BuiltinProc_simd_or: + case BuiltinProc_simd_xor: + arg1 = lb_build_expr(p, ce->args[1]); + switch (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; + } + if (op_code) { + res.value = LLVMBuildBinOp(p->builder, op_code, arg0.value, arg1.value, ""); + return res; + } + break; + case BuiltinProc_simd_neg: + if (is_float) { + res.value = LLVMBuildFNeg(p->builder, arg0.value, ""); + } else { + res.value = LLVMBuildNeg(p->builder, arg0.value, ""); + } + return res; + case BuiltinProc_simd_abs: + if (is_float) { + LLVMValueRef pos = arg0.value; + LLVMValueRef neg = LLVMBuildFNeg(p->builder, pos, ""); + LLVMValueRef cond = LLVMBuildFCmp(p->builder, LLVMRealOGT, pos, neg, ""); + res.value = LLVMBuildSelect(p->builder, cond, pos, neg, ""); + } else { + LLVMValueRef pos = arg0.value; + LLVMValueRef neg = LLVMBuildNeg(p->builder, pos, ""); + LLVMValueRef cond = LLVMBuildICmp(p->builder, is_signed ? LLVMIntSGT : LLVMIntUGT, pos, neg, ""); + res.value = LLVMBuildSelect(p->builder, cond, pos, neg, ""); + } + return res; + case BuiltinProc_simd_min: + if (is_float) { + LLVMValueRef cond = LLVMBuildFCmp(p->builder, LLVMRealOLT, arg0.value, arg1.value, ""); + res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, ""); + } else { + LLVMValueRef cond = LLVMBuildICmp(p->builder, is_signed ? LLVMIntSLT : LLVMIntULT, arg0.value, arg1.value, ""); + res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, ""); + } + return res; + case BuiltinProc_simd_max: + arg1 = lb_build_expr(p, ce->args[1]); + if (is_float) { + LLVMValueRef cond = LLVMBuildFCmp(p->builder, LLVMRealOGT, arg0.value, arg1.value, ""); + res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, ""); + } else { + LLVMValueRef cond = LLVMBuildICmp(p->builder, is_signed ? LLVMIntSGT : LLVMIntUGT, arg0.value, arg1.value, ""); + res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, ""); + } + return res; + case BuiltinProc_simd_eq: + case BuiltinProc_simd_ne: + case BuiltinProc_simd_lt: + case BuiltinProc_simd_le: + case BuiltinProc_simd_gt: + case BuiltinProc_simd_ge: + arg1 = lb_build_expr(p, ce->args[1]); + if (is_float) { + LLVMRealPredicate pred = cast(LLVMRealPredicate)0; + switch (id) { + case BuiltinProc_simd_eq: pred = LLVMRealOEQ; break; + case BuiltinProc_simd_ne: pred = LLVMRealONE; break; + case BuiltinProc_simd_lt: pred = LLVMRealOLT; break; + case BuiltinProc_simd_le: pred = LLVMRealOLE; break; + case BuiltinProc_simd_gt: pred = LLVMRealOGT; break; + case BuiltinProc_simd_ge: pred = LLVMRealOGE; break; + } + if (pred) { + res.value = LLVMBuildFCmp(p->builder, pred, arg0.value, arg1.value, ""); + res.value = LLVMBuildSExtOrBitCast(p->builder, res.value, lb_type(m, tv.type), ""); + return res; + } + } else { + LLVMIntPredicate pred = cast(LLVMIntPredicate)0; + switch (id) { + case BuiltinProc_simd_eq: pred = LLVMIntEQ; break; + case BuiltinProc_simd_ne: pred = LLVMIntNE; break; + case BuiltinProc_simd_lt: pred = is_signed ? LLVMIntSLT :LLVMIntULT; break; + case BuiltinProc_simd_le: pred = is_signed ? LLVMIntSLE :LLVMIntULE; break; + case BuiltinProc_simd_gt: pred = is_signed ? LLVMIntSGT :LLVMIntUGT; break; + case BuiltinProc_simd_ge: pred = is_signed ? LLVMIntSGE :LLVMIntUGE; break; + } + if (pred) { + res.value = LLVMBuildICmp(p->builder, pred, arg0.value, arg1.value, ""); + res.value = LLVMBuildSExtOrBitCast(p->builder, res.value, lb_type(m, tv.type), ""); + return res; + } + } + break; + } + GB_PANIC("Unhandled simd intrinsic: '%.*s'", LIT(builtin_procs[id].name)); + return {}; +} + lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, BuiltinProcId id) { ast_node(ce, CallExpr, expr); + if (BuiltinProc__simd_begin < id && id < BuiltinProc__simd_end) { + return lb_build_builtin_simd_proc(p, expr, tv, id); + } + switch (id) { case BuiltinProc_DIRECTIVE: { ast_node(bd, BasicDirective, ce->proc); -- cgit v1.2.3 From 4c4480104de9d6ba520215afb6330c95b0e56b93 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 May 2022 20:27:14 +0100 Subject: Add `simd_extract` and `simd_insert` --- src/check_builtin.cpp | 120 +++++++++++++++++++++++++++++++----------- src/checker_builtin_procs.hpp | 6 +++ src/llvm_backend_proc.cpp | 11 ++++ 3 files changed, 105 insertions(+), 32 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 13eb9f47d..ab4cc210c 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -425,14 +425,9 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call { 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; - } + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) { return false; } + check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) { return false; } + convert_to_typed(c, &y, x.type); if (!is_type_simd_vector(x.type)) { error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); return false; @@ -470,14 +465,9 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call { 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; - } + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) { return false; } + check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) { return false; } + convert_to_typed(c, &y, x.type); if (!is_type_simd_vector(x.type)) { error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); return false; @@ -514,14 +504,9 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call { 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; - } + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) { return false; } + check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) { return false; } + convert_to_typed(c, &y, x.type); if (!is_type_simd_vector(x.type)) { error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); return false; @@ -599,14 +584,9 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call 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; - } + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) { return false; } + check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) { return false; } + convert_to_typed(c, &y, x.type); if (!is_type_simd_vector(x.type)) { error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); return false; @@ -639,6 +619,81 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call operand->type = alloc_type_simd_vector(count, new_elem); return true; } + + case BuiltinProc_simd_extract: + { + 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; + } + i64 max_count = x.type->SimdVector.count; + i64 value = -1; + if (!check_index_value(c, x.type, false, ce->args[1], max_count, &value)) { + return false; + } + if (max_count < 0) { + error(ce->args[1], "'%.*s' expected a constant integer index, got '%lld'", LIT(builtin_name), cast(long long)value); + return false; + } + + operand->mode = Addressing_Value; + operand->type = elem; + return true; + } + break; + case BuiltinProc_simd_insert: + { + 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; + } + i64 max_count = x.type->SimdVector.count; + i64 value = -1; + if (!check_index_value(c, x.type, false, ce->args[1], max_count, &value)) { + return false; + } + if (max_count < 0) { + error(ce->args[1], "'%.*s' expected a constant integer index, got '%lld'", LIT(builtin_name), cast(long long)value); + return false; + } + + Operand y = {}; + check_expr_with_type_hint(c, &y, ce->args[2], elem); if (y.mode == Addressing_Invalid) { return false; } + convert_to_typed(c, &y, elem); + if (!are_types_identical(y.type, elem)) { + gbString et = type_to_string(elem); + gbString yt = type_to_string(y.type); + error(y.expr, "'%.*s' expected a type of '%s' to insert, got '%s'", LIT(builtin_name), et, yt); + gb_string_free(yt); + gb_string_free(et); + return false; + } + + operand->mode = Addressing_Value; + operand->type = x.type; + return true; + } + break; default: GB_PANIC("Unhandled simd intrinsic: %.*s", LIT(builtin_name)); } @@ -736,6 +791,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } operand->mode = Addressing_Value; operand->value = {}; + operand->expr = call; return ok; } diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 80467ffb1..604e9dc8c 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -146,6 +146,9 @@ BuiltinProc__simd_begin, BuiltinProc_simd_le, BuiltinProc_simd_gt, BuiltinProc_simd_ge, + + BuiltinProc_simd_extract, + BuiltinProc_simd_insert, BuiltinProc__simd_end, // Platform specific intrinsics @@ -395,6 +398,9 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {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("simd_extract"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_insert"), 3, 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 82ad6daef..4af5d9440 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -991,6 +991,7 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const lbValue arg0 = lb_build_expr(p, ce->args[0]); lbValue arg1 = {}; + lbValue arg2 = {}; Type *elem = base_array_type(arg0.type); @@ -1173,6 +1174,16 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const } } break; + + case BuiltinProc_simd_extract: + arg1 = lb_build_expr(p, ce->args[1]); + res.value = LLVMBuildExtractElement(p->builder, arg0.value, arg1.value, ""); + return res; + case BuiltinProc_simd_insert: + arg1 = lb_build_expr(p, ce->args[1]); + arg2 = lb_build_expr(p, ce->args[2]); + res.value = LLVMBuildInsertElement(p->builder, arg0.value, arg2.value, arg1.value, ""); + return res; } GB_PANIC("Unhandled simd intrinsic: '%.*s'", LIT(builtin_procs[id].name)); return {}; -- cgit v1.2.3 From b168bf9460491a101f3a7d41c28500a45898ecbf Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 May 2022 21:00:00 +0100 Subject: Rename `simd_insert` to `simd_replace` --- src/check_builtin.cpp | 2 +- src/checker_builtin_procs.hpp | 4 ++-- src/llvm_backend_proc.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index ab4cc210c..64b2ebfce 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -651,7 +651,7 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call return true; } break; - case BuiltinProc_simd_insert: + case BuiltinProc_simd_replace: { Operand x = {}; check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) { return false; } diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 604e9dc8c..f5d4111bc 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -148,7 +148,7 @@ BuiltinProc__simd_begin, BuiltinProc_simd_ge, BuiltinProc_simd_extract, - BuiltinProc_simd_insert, + BuiltinProc_simd_replace, BuiltinProc__simd_end, // Platform specific intrinsics @@ -400,7 +400,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("simd_ge"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_extract"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("simd_insert"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_replace"), 3, 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 4af5d9440..cfb69c654 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1179,7 +1179,7 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const arg1 = lb_build_expr(p, ce->args[1]); res.value = LLVMBuildExtractElement(p->builder, arg0.value, arg1.value, ""); return res; - case BuiltinProc_simd_insert: + case BuiltinProc_simd_replace: arg1 = lb_build_expr(p, ce->args[1]); arg2 = lb_build_expr(p, ce->args[2]); res.value = LLVMBuildInsertElement(p->builder, arg0.value, arg2.value, arg1.value, ""); -- cgit v1.2.3 From 10e4de3c015de149008d0dc573e391af99d82574 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 May 2022 22:04:47 +0100 Subject: Add `intrinsics.simd_reduce_*` --- core/simd/simd.odin | 8 ++++ src/check_builtin.cpp | 50 +++++++++++++++++++++ src/checker_builtin_procs.hpp | 16 +++++++ src/llvm_backend_proc.cpp | 102 ++++++++++++++++++++++++++++++++++++++---- 4 files changed, 167 insertions(+), 9 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/core/simd/simd.odin b/core/simd/simd.odin index 87386f91f..ad14855bd 100644 --- a/core/simd/simd.odin +++ b/core/simd/simd.odin @@ -34,6 +34,14 @@ ge :: intrinsics.simd_ge extract :: intrinsics.simd_extract replace :: intrinsics.simd_replace +reduce_add_ordered :: intrinsics.simd_reduce_add_ordered +reduce_mul_ordered :: intrinsics.simd_reduce_mul_ordered +reduce_min :: intrinsics.simd_reduce_min +reduce_max :: intrinsics.simd_reduce_max +reduce_and :: intrinsics.simd_reduce_and +reduce_or :: intrinsics.simd_reduce_or +reduce_xor :: intrinsics.simd_reduce_xor + 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; + } + 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 = base_array_type(x.type); + return true; + } + + case BuiltinProc_simd_reduce_and: + case BuiltinProc_simd_reduce_or: + case BuiltinProc_simd_reduce_xor: + { + 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)) { + 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 = base_array_type(x.type); + return true; + } + + // case BuiltinProc_simd_rotate_left: // { // Operand x = {}; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index f5d4111bc..98cc9f284 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -149,6 +149,14 @@ BuiltinProc__simd_begin, BuiltinProc_simd_extract, BuiltinProc_simd_replace, + + BuiltinProc_simd_reduce_add_ordered, + BuiltinProc_simd_reduce_mul_ordered, + BuiltinProc_simd_reduce_min, + BuiltinProc_simd_reduce_max, + BuiltinProc_simd_reduce_and, + BuiltinProc_simd_reduce_or, + BuiltinProc_simd_reduce_xor, BuiltinProc__simd_end, // Platform specific intrinsics @@ -401,6 +409,14 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("simd_extract"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_replace"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("simd_reduce_add_ordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_reduce_mul_ordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_reduce_min"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_reduce_max"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_reduce_and"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_reduce_or"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_reduce_xor"), 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 cfb69c654..c09265e7a 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -981,7 +981,7 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, return result; } -lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, BuiltinProcId id) { +lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, BuiltinProcId builtin_id) { ast_node(ce, CallExpr, expr); lbModule *m = p->module; @@ -1000,7 +1000,7 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const LLVMOpcode op_code = cast(LLVMOpcode)0; - switch (id) { + switch (builtin_id) { case BuiltinProc_simd_add: case BuiltinProc_simd_sub: case BuiltinProc_simd_mul: @@ -1008,14 +1008,14 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const case BuiltinProc_simd_rem: arg1 = lb_build_expr(p, ce->args[1]); if (is_float) { - switch (id) { + switch (builtin_id) { case BuiltinProc_simd_add: op_code = LLVMFAdd; break; case BuiltinProc_simd_sub: op_code = LLVMFSub; break; case BuiltinProc_simd_mul: op_code = LLVMFMul; break; case BuiltinProc_simd_div: op_code = LLVMFDiv; break; } } else { - switch (id) { + switch (builtin_id) { case BuiltinProc_simd_add: op_code = LLVMAdd; break; case BuiltinProc_simd_sub: op_code = LLVMSub; break; case BuiltinProc_simd_mul: op_code = LLVMMul; break; @@ -1053,7 +1053,7 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const Type *elem1 = base_array_type(arg1.type); bool is_masked = false; - switch (id) { + switch (builtin_id) { case BuiltinProc_simd_shl: op_code = LLVMShl; is_masked = false; break; case BuiltinProc_simd_shr: op_code = is_signed ? LLVMAShr : LLVMLShr; is_masked = false; break; case BuiltinProc_simd_shl_masked: op_code = LLVMShl; is_masked = true; break; @@ -1086,7 +1086,7 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const case BuiltinProc_simd_or: case BuiltinProc_simd_xor: arg1 = lb_build_expr(p, ce->args[1]); - switch (id) { + 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; @@ -1144,7 +1144,7 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const arg1 = lb_build_expr(p, ce->args[1]); if (is_float) { LLVMRealPredicate pred = cast(LLVMRealPredicate)0; - switch (id) { + switch (builtin_id) { case BuiltinProc_simd_eq: pred = LLVMRealOEQ; break; case BuiltinProc_simd_ne: pred = LLVMRealONE; break; case BuiltinProc_simd_lt: pred = LLVMRealOLT; break; @@ -1159,7 +1159,7 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const } } else { LLVMIntPredicate pred = cast(LLVMIntPredicate)0; - switch (id) { + switch (builtin_id) { case BuiltinProc_simd_eq: pred = LLVMIntEQ; break; case BuiltinProc_simd_ne: pred = LLVMIntNE; break; case BuiltinProc_simd_lt: pred = is_signed ? LLVMIntSLT :LLVMIntULT; break; @@ -1184,8 +1184,92 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const arg2 = lb_build_expr(p, ce->args[2]); res.value = LLVMBuildInsertElement(p->builder, arg0.value, arg2.value, arg1.value, ""); return res; + + case BuiltinProc_simd_reduce_add_ordered: + case BuiltinProc_simd_reduce_mul_ordered: + { + LLVMTypeRef llvm_elem = lb_type(m, elem); + LLVMValueRef args[2] = {}; + isize args_count = 0; + + char const *name = nullptr; + switch (builtin_id) { + case BuiltinProc_simd_reduce_add_ordered: + if (is_float) { + name = "llvm.vector.reduce.fadd"; + args[args_count++] = LLVMConstReal(llvm_elem, 0.0); + } else { + name = "llvm.vector.reduce.add"; + } + break; + case BuiltinProc_simd_reduce_mul_ordered: + if (is_float) { + name = "llvm.vector.reduce.fmul"; + args[args_count++] = LLVMConstReal(llvm_elem, 1.0); + } else { + name = "llvm.vector.reduce.mul"; + } + break; + } + args[args_count++] = arg0.value; + + + 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)); + + lbValue res = {}; + res.value = LLVMBuildCall(p->builder, ip, args, cast(unsigned)args_count, ""); + res.type = tv.type; + return res; + } + case BuiltinProc_simd_reduce_min: + case BuiltinProc_simd_reduce_max: + case BuiltinProc_simd_reduce_and: + case BuiltinProc_simd_reduce_or: + case BuiltinProc_simd_reduce_xor: + { + char const *name = nullptr; + switch (builtin_id) { + case BuiltinProc_simd_reduce_min: + if (is_float) { + name = "llvm.vector.reduce.fmin"; + } else if (is_signed) { + name = "llvm.vector.reduce.smin"; + } else { + name = "llvm.vector.reduce.umin"; + } + break; + case BuiltinProc_simd_reduce_max: + if (is_float) { + name = "llvm.vector.reduce.fmax"; + } else if (is_signed) { + name = "llvm.vector.reduce.smax"; + } else { + name = "llvm.vector.reduce.umax"; + } + break; + case BuiltinProc_simd_reduce_and: name = "llvm.vector.reduce.and"; break; + case BuiltinProc_simd_reduce_or: name = "llvm.vector.reduce.or"; break; + case BuiltinProc_simd_reduce_xor: name = "llvm.vector.reduce.xor"; 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; + + lbValue res = {}; + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + res.type = tv.type; + return res; + } } - GB_PANIC("Unhandled simd intrinsic: '%.*s'", LIT(builtin_procs[id].name)); + GB_PANIC("Unhandled simd intrinsic: '%.*s'", LIT(builtin_procs[builtin_id].name)); + return {}; } -- cgit v1.2.3 From 140c00aa0cdeac6d1149db3845cc9f3433140cf9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 May 2022 23:01:33 +0100 Subject: `intrinsics.simd_shuffle` --- core/simd/simd.odin | 24 ++++++++++++++++ src/check_builtin.cpp | 64 +++++++++++++++++++++++++++++++++++++++++++ src/check_type.cpp | 10 +++++-- src/checker_builtin_procs.hpp | 4 +++ src/llvm_backend_proc.cpp | 45 ++++++++++++++++++++++++------ 5 files changed, 137 insertions(+), 10 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/core/simd/simd.odin b/core/simd/simd.odin index ad14855bd..08839fd23 100644 --- a/core/simd/simd.odin +++ b/core/simd/simd.odin @@ -1,7 +1,28 @@ package simd +import "core:builtin" import "core:intrinsics" +// boolx16 :: #simd[16]bool +// b8x16 :: #simd[16]b8 +// b16x8 :: #simd[8]b16 +// b32x4 :: #simd[4]b32 +// b64x2 :: #simd[2]b64 + +// u8x16 :: #simd[16]u8 +// i8x16 :: #simd[16]i8 +// u16x8 :: #simd[8]u16 +// i16x8 :: #simd[8]i16 +// u32x4 :: #simd[4]u32 +// i32x4 :: #simd[4]i32 +// u64x2 :: #simd[2]u64 +// i64x2 :: #simd[2]i64 + +// f16x8 :: #simd[8]f16 +// f32x4 :: #simd[4]f32 +// f64x2 :: #simd[2]f64 + + add :: intrinsics.simd_add sub :: intrinsics.simd_sub mul :: intrinsics.simd_mul @@ -42,6 +63,9 @@ reduce_and :: intrinsics.simd_reduce_and reduce_or :: intrinsics.simd_reduce_or reduce_xor :: intrinsics.simd_reduce_xor +swizzle :: builtin.swizzle +shuffle :: intrinsics.simd_shuffle + 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; } + check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) { return false; } + convert_to_typed(c, &y, x.type); + 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); + + check_expr(c, &z, ce->args[2]); if (z.mode == Addressing_Invalid) { return false; } + Type *z_elem = base_array_type(z.type); + if (!is_type_simd_vector(z.type) || !are_types_identical(z_elem, t_u32)) { + gbString zstr = type_to_string(z.type); + error(z.expr, "'%.*s' expected a simd vector type with an element of type 'u32', got '%s'", LIT(builtin_name), zstr); + gb_string_free(zstr); + return false; + } + + i64 x_count = x.type->SimdVector.count; + i64 z_count = z.type->SimdVector.count; + + if (!is_power_of_two(z_count)) { + gbString zstr = type_to_string(z.type); + error(z.expr, "'%.*s' expected a simd vector type with a power of two length, got '%s'", LIT(builtin_name), zstr); + gb_string_free(zstr); + return false; + } + if (z_count > x_count) { + gbString zstr = type_to_string(z.type); + error(z.expr, "'%.*s' expected a simd vector type excepts the sum of the two input vectors, got '%s'", LIT(builtin_name), zstr); + gb_string_free(zstr); + return false; + } + + + operand->mode = Addressing_Value; + operand->type = alloc_type_simd_vector(z_count, elem); + return true; + } + + // case BuiltinProc_simd_rotate_left: // { // Operand x = {}; @@ -3131,6 +3189,12 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 operand->mode = Addressing_Type; operand->type = alloc_type_simd_vector(count, elem); + if (is_arch_wasm()) { + if (type_size_of(operand->type) != 16) { + error(x.expr, "wasm based targets are limited to 128-bit types"); + } + } + break; } diff --git a/src/check_type.cpp b/src/check_type.cpp index 540413e32..74fa235d5 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2802,15 +2802,21 @@ 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 (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); *type = alloc_type_array(elem, count, generic_type); goto array_end; - } - + } else *type = alloc_type_simd_vector(count, elem, generic_type); + + if (is_arch_wasm()) { + if (type_size_of(*type) != 16) { + error(at->count, "wasm based targets are limited to 128-bit types"); + } + } } 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 98cc9f284..722bbec84 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -157,6 +157,8 @@ BuiltinProc__simd_begin, BuiltinProc_simd_reduce_and, BuiltinProc_simd_reduce_or, BuiltinProc_simd_reduce_xor, + + BuiltinProc_simd_shuffle, BuiltinProc__simd_end, // Platform specific intrinsics @@ -417,6 +419,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("simd_reduce_and"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_reduce_or"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_reduce_xor"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("simd_shuffle"), 3, 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 c09265e7a..1b5d15d9b 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -981,6 +981,24 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, return result; } +LLVMValueRef llvm_splat_float(i64 count, LLVMTypeRef type, f64 value) { + LLVMValueRef v = LLVMConstReal(type, value); + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, count); + for (i64 i = 0; i < count; i++) { + values[i] = v; + } + return LLVMConstVector(values, cast(unsigned)count); +} +LLVMValueRef llvm_splat_int(i64 count, LLVMTypeRef type, i64 value, bool is_signed=false) { + LLVMValueRef v = LLVMConstInt(type, value, is_signed); + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, count); + for (i64 i = 0; i < count; i++) { + values[i] = v; + } + return LLVMConstVector(values, cast(unsigned)count); +} + + lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, BuiltinProcId builtin_id) { ast_node(ce, CallExpr, expr); @@ -1060,12 +1078,7 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const case BuiltinProc_simd_shr_masked: op_code = is_signed ? LLVMAShr : LLVMLShr; is_masked = true; break; } if (op_code) { - LLVMValueRef bit_value = lb_const_int(m, elem1, sz*8 - 1).value; - LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, count); - for (i64 i = 0; i < count; i++) { - values[i] = bit_value; - } - LLVMValueRef bits = LLVMConstVector(values, cast(unsigned)count); + LLVMValueRef bits = llvm_splat_int(count, lb_type(m, elem1), sz*8 - 1); if (is_masked) { // C logic LLVMValueRef shift = LLVMBuildAnd(p->builder, arg1.value, bits, ""); @@ -1077,7 +1090,6 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const LLVMValueRef shift = LLVMBuildBinOp(p->builder, op_code, arg0.value, arg1.value, ""); res.value = LLVMBuildSelect(p->builder, mask, shift, zero, ""); } - return res; } } @@ -1264,7 +1276,24 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const lbValue res = {}; res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); - res.type = tv.type; + return res; + } + + case BuiltinProc_simd_shuffle: + { + arg1 = lb_build_expr(p, ce->args[1]); + arg2 = lb_build_expr(p, ce->args[2]); + + Type *vt = arg0.type; + GB_ASSERT(vt->kind == Type_SimdVector); + + LLVMValueRef mask = arg2.value; + + i64 max_count = vt->SimdVector.count*2; + LLVMValueRef max_mask = llvm_splat_int(max_count, lb_type(m, arg2.type->SimdVector.elem), max_count-1); + mask = LLVMBuildAnd(p->builder, mask, max_mask, ""); + + res.value = LLVMBuildShuffleVector(p->builder, arg0.value, arg1.value, mask, ""); return res; } } -- cgit v1.2.3 From 7002c94a63c58aa0ac5a5d74b1fffd1511aeb699 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 May 2022 23:34:41 +0100 Subject: Add `intrinsics.simd_select` --- core/simd/simd.odin | 1 + src/check_builtin.cpp | 51 +++++++++++++++++++++++++++++++++++++++++++ src/checker_builtin_procs.hpp | 2 ++ src/llvm_backend_proc.cpp | 12 ++++++++++ 4 files changed, 66 insertions(+) (limited to 'src/llvm_backend_proc.cpp') diff --git a/core/simd/simd.odin b/core/simd/simd.odin index b5207e154..1819f3951 100644 --- a/core/simd/simd.odin +++ b/core/simd/simd.odin @@ -74,6 +74,7 @@ reduce_xor :: intrinsics.simd_reduce_xor swizzle :: builtin.swizzle shuffle :: intrinsics.simd_shuffle +select :: intrinsics.simd_select splat :: #force_inline proc "contextless" ($T: typeid/#simd[$LANES]$E, value: E) -> T { return T{0..args[0]); if (cond.mode == Addressing_Invalid) { return false; } + + if (!is_type_simd_vector(cond.type)) { + error(cond.expr, "'%.*s' expected a simd vector boolean type", LIT(builtin_name)); + return false; + } + if (!is_type_boolean(base_array_type(cond.type))) { + error(cond.expr, "'%.*s' expected a simd vector boolean type", LIT(builtin_name)); + return false; + } + + Operand x = {}; + Operand y = {}; + check_expr(c, &x, ce->args[1]); if (x.mode == Addressing_Invalid) { return false; } + check_expr_with_type_hint(c, &y, ce->args[2], x.type); if (y.mode == Addressing_Invalid) { return false; } + convert_to_typed(c, &y, x.type); + 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 results of the same type, got '%s' vs '%s'", LIT(builtin_name), xs, ys); + gb_string_free(ys); + gb_string_free(xs); + return false; + } + + if (cond.type->SimdVector.count != x.type->SimdVector.count) { + error(x.expr, "'%.*s' expected condition vector to match the length of the result lengths, got '%lld' vs '%lld'", + LIT(builtin_name), + cast(long long)cond.type->SimdVector.count, + cast(long long)x.type->SimdVector.count); + return false; + } + + + operand->mode = Addressing_Value; + operand->type = x.type; + return true; + } + + // case BuiltinProc_simd_rotate_left: // { diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 722bbec84..2fb355e91 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -159,6 +159,7 @@ BuiltinProc__simd_begin, BuiltinProc_simd_reduce_xor, BuiltinProc_simd_shuffle, + BuiltinProc_simd_select, BuiltinProc__simd_end, // Platform specific intrinsics @@ -421,6 +422,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("simd_reduce_xor"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_shuffle"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_select"), 3, 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 1b5d15d9b..7a86427d4 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1296,6 +1296,18 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const res.value = LLVMBuildShuffleVector(p->builder, arg0.value, arg1.value, mask, ""); return res; } + + case BuiltinProc_simd_select: + { + LLVMValueRef cond = arg0.value; + LLVMValueRef x = lb_build_expr(p, ce->args[1]).value; + LLVMValueRef y = lb_build_expr(p, ce->args[2]).value; + + cond = LLVMBuildICmp(p->builder, LLVMIntNE, cond, LLVMConstNull(LLVMTypeOf(cond)), ""); + res.value = LLVMBuildSelect(p->builder, cond, x, y, ""); + return res; + } + } GB_PANIC("Unhandled simd intrinsic: '%.*s'", LIT(builtin_procs[builtin_id].name)); -- cgit v1.2.3 From cde6a2f7a5e5ea0676f9732342f10169baa64c52 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 26 May 2022 00:36:24 +0100 Subject: Make `simd_shuffle` act closer to `swizzle` --- core/intrinsics/intrinsics.odin | 2 +- core/simd/simd.odin | 4 +- src/check_builtin.cpp | 95 +++++++++++++++++++---------------------- src/checker_builtin_procs.hpp | 2 +- src/llvm_backend_proc.cpp | 14 ++++-- 5 files changed, 59 insertions(+), 58 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index 8f2ebce13..097496d47 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -236,7 +236,7 @@ simd_reduce_and :: proc(a: #simd[N]T) -> T --- simd_reduce_or :: proc(a: #simd[N]T) -> T --- simd_reduce_xor :: proc(a: #simd[N]T) -> T --- -simd_shuffle :: proc(a, b: #simd[N]T, indices: #simd[max 2*N]u32) -> #simd[len(indices)]T --- +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 --- diff --git a/core/simd/simd.odin b/core/simd/simd.odin index b26719d56..e81f82341 100644 --- a/core/simd/simd.odin +++ b/core/simd/simd.odin @@ -93,11 +93,11 @@ to_array_ptr :: #force_inline proc "contextless" (v: ^#simd[$LANES]$E) -> ^[LANE to_array :: #force_inline proc "contextless" (v: #simd[$LANES]$E) -> [LANES]E { return transmute([LANES]E)(v) } -from_array :: #force_inline proc "contextless" (v: $A/[$LANES]$E) -> #simd[LANES]E where LANES & (LANES-1) == 0 { +from_array :: #force_inline proc "contextless" (v: $A/[$LANES]$E) -> #simd[LANES]E { return transmute(#simd[LANES]E)v } -from_slice :: proc($T: typeid/#simd[$LANES]$E, slice: []E) -> T where LANES & (LANES-1) == 0 { +from_slice :: proc($T: typeid/#simd[$LANES]$E, slice: []E) -> T { assert(len(slice) >= LANES, "slice length must be a least the number of lanes") array: [LANES]E #no_bounds_check for i in 0..args[0]); if (x.mode == Addressing_Invalid) { return false; } check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) { return false; } convert_to_typed(c, &y, x.type); @@ -784,34 +783,53 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call } Type *elem = base_array_type(x.type); - check_expr(c, &z, ce->args[2]); if (z.mode == Addressing_Invalid) { return false; } - Type *z_elem = base_array_type(z.type); - if (!is_type_simd_vector(z.type) || !are_types_identical(z_elem, t_u32)) { - gbString zstr = type_to_string(z.type); - error(z.expr, "'%.*s' expected a simd vector type with an element of type 'u32', got '%s'", LIT(builtin_name), zstr); - gb_string_free(zstr); - return false; - } + i64 max_count = x.type->SimdVector.count + y.type->SimdVector.count; + + i64 arg_count = 0; + for_array(i, ce->args) { + if (i < 2) { + continue; + } + Ast *arg = ce->args[i]; + Operand op = {}; + check_expr(c, &op, arg); + if (op.mode == Addressing_Invalid) { + return false; + } + Type *arg_type = base_type(op.type); + if (!is_type_integer(arg_type) || op.mode != Addressing_Constant) { + error(op.expr, "Indices to '%.*s' must be constant integers", LIT(builtin_name)); + return false; + } - i64 x_count = x.type->SimdVector.count; - i64 z_count = z.type->SimdVector.count; + if (big_int_is_neg(&op.value.value_integer)) { + error(op.expr, "Negative '%.*s' index", LIT(builtin_name)); + return false; + } - if (!is_power_of_two(z_count)) { - gbString zstr = type_to_string(z.type); - error(z.expr, "'%.*s' expected a simd vector type with a power of two length, got '%s'", LIT(builtin_name), zstr); - gb_string_free(zstr); - return false; + BigInt mc = {}; + big_int_from_i64(&mc, max_count); + if (big_int_cmp(&mc, &op.value.value_integer) <= 0) { + error(op.expr, "'%.*s' index exceeds length", LIT(builtin_name)); + return false; + } + + arg_count++; } - if (z_count > x_count) { - gbString zstr = type_to_string(z.type); - error(z.expr, "'%.*s' expected a simd vector type excepts the sum of the two input vectors, got '%s'", LIT(builtin_name), zstr); - gb_string_free(zstr); + + if (arg_count > max_count) { + error(call, "Too many '%.*s' indices, %td > %td", LIT(builtin_name), arg_count, max_count); return false; } + if (!is_power_of_two(arg_count)) { + error(call, "'%.*s' must have a power of two index arguments, got %lld", LIT(builtin_name), cast(long long)arg_count); + return false; + } + operand->mode = Addressing_Value; - operand->type = alloc_type_simd_vector(z_count, elem); + operand->type = alloc_type_simd_vector(arg_count, elem); return true; } @@ -869,36 +887,6 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call } - - // case BuiltinProc_simd_rotate_left: - // { - // 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 offset = {}; - // check_expr_with_type_hint(c, &offset, ce->args[1]); if (x.mode == Addressing_Invalid) { return false; } - // convert_to_typed(c, &offset, t_int); - // if (offset.mode != Addressing_Constant) { - // error(offset.expr, "'%.*s' expected a constant integer for the offset", LIT(builtin_name)); - // return false; - // } - - // operand->mode = Addressing_Value; - // operand->type = x.type; - // return true - // } default: GB_PANIC("Unhandled simd intrinsic: %.*s", LIT(builtin_name)); } @@ -1540,6 +1528,11 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 bt->Struct.soa_kind == StructSoa_Dynamic) { mode = Addressing_Value; } + } else if (is_type_simd_vector(op_type)) { + Type *bt = base_type(op_type); + mode = Addressing_Constant; + value = exact_value_i64(bt->SimdVector.count); + type = t_untyped_integer; } if (operand->mode == Addressing_Type && mode != Addressing_Constant) { mode = Addressing_Invalid; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 2fb355e91..660208eb0 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -421,7 +421,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("simd_reduce_or"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_reduce_xor"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("simd_shuffle"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_shuffle"), 2, true, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_select"), 3, 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 7a86427d4..2bffa111c 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1282,15 +1282,23 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const case BuiltinProc_simd_shuffle: { arg1 = lb_build_expr(p, ce->args[1]); - arg2 = lb_build_expr(p, ce->args[2]); Type *vt = arg0.type; GB_ASSERT(vt->kind == Type_SimdVector); - LLVMValueRef mask = arg2.value; + i64 mask_count = ce->args.count-2; i64 max_count = vt->SimdVector.count*2; - LLVMValueRef max_mask = llvm_splat_int(max_count, lb_type(m, arg2.type->SimdVector.elem), max_count-1); + + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, mask_count); + for (isize i = 0; i < max_count; i++) { + lbValue idx = lb_build_expr(p, ce->args[i+2]); + GB_ASSERT(LLVMIsConstant(idx.value)); + values[i] = idx.value; + } + LLVMValueRef mask = LLVMConstVector(values, cast(unsigned)mask_count); + + LLVMValueRef max_mask = llvm_splat_int(mask_count, lb_type(m, t_u32), max_count-1); mask = LLVMBuildAnd(p->builder, mask, max_mask, ""); res.value = LLVMBuildShuffleVector(p->builder, arg0.value, arg1.value, mask, ""); -- cgit v1.2.3 From 83d880a94a783e2c78c4ab0c1c2f1740c8ceac72 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 26 May 2022 00:37:48 +0100 Subject: Remove unneeded mask --- src/llvm_backend_proc.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 2bffa111c..1ec71bc21 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1287,21 +1287,18 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const GB_ASSERT(vt->kind == Type_SimdVector); - i64 mask_count = ce->args.count-2; + i64 indices_count = ce->args.count-2; i64 max_count = vt->SimdVector.count*2; - LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, mask_count); + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, indices_count); for (isize i = 0; i < max_count; i++) { lbValue idx = lb_build_expr(p, ce->args[i+2]); GB_ASSERT(LLVMIsConstant(idx.value)); values[i] = idx.value; } - LLVMValueRef mask = LLVMConstVector(values, cast(unsigned)mask_count); + LLVMValueRef indices = LLVMConstVector(values, cast(unsigned)indices_count); - LLVMValueRef max_mask = llvm_splat_int(mask_count, lb_type(m, t_u32), max_count-1); - mask = LLVMBuildAnd(p->builder, mask, max_mask, ""); - - res.value = LLVMBuildShuffleVector(p->builder, arg0.value, arg1.value, mask, ""); + res.value = LLVMBuildShuffleVector(p->builder, arg0.value, arg1.value, indices, ""); return res; } -- cgit v1.2.3 From b95ca80f85efc3f7be292d9c751a742e43e41b2e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 26 May 2022 00:39:34 +0100 Subject: Fix `simd_shuffle` --- src/llvm_backend_proc.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 1ec71bc21..05477d84b 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1289,9 +1289,10 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const i64 indices_count = ce->args.count-2; i64 max_count = vt->SimdVector.count*2; + GB_ASSERT(indices_count <= max_count); LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, indices_count); - for (isize i = 0; i < max_count; i++) { + for (isize i = 0; i < indices_count; i++) { lbValue idx = lb_build_expr(p, ce->args[i+2]); GB_ASSERT(LLVMIsConstant(idx.value)); values[i] = idx.value; -- 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/llvm_backend_proc.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/llvm_backend_proc.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 35502816c7d53b0b5fd422537d32efb5c34b1811 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 26 May 2022 11:24:10 +0100 Subject: Add `simd_add_sat` `simd_sub_sat` --- core/intrinsics/intrinsics.odin | 9 +++++++-- core/simd/simd.odin | 4 ++++ src/check_builtin.cpp | 11 +++++++++-- src/checker_builtin_procs.hpp | 7 +++++++ src/llvm_backend_proc.cpp | 24 ++++++++++++++++++++++++ 5 files changed, 51 insertions(+), 4 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index a14488210..7bbbc1efd 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -202,6 +202,9 @@ simd_shr :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T --- simd_shl_masked :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T --- simd_shr_masked :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T --- +simd_add_sat :: proc(a, b: #simd[N]T) -> #simd[N]T --- +simd_sub_sat :: proc(a, b: #simd[N]T) -> #simd[N]T --- + simd_and :: proc(a, b: #simd[N]T) -> #simd[N]T --- simd_or :: proc(a, b: #simd[N]T) -> #simd[N]T --- simd_xor :: proc(a, b: #simd[N]T) -> #simd[N]T --- @@ -238,13 +241,15 @@ simd_reduce_xor :: proc(a: #simd[N]T) -> T --- 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 --- +// Lane-wise operations +simd_sqrt :: proc(a: #simd[N]any_float) -> #simd[N]any_float --- // IEEE sqrt 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 --- +// rounding to the nearest integral value; if two values are equally near, rounds to the even one simd_nearest :: proc(a: #simd[N]any_float) -> #simd[N]any_float --- +// equivalent a swizzle with descending indices, e.g. reserve(a, 3, 2, 1, 0) simd_reverse :: proc(a: #simd[N]T) -> #simd[N]T --- // WASM targets only diff --git a/core/simd/simd.odin b/core/simd/simd.odin index 060e32323..2ce487d13 100644 --- a/core/simd/simd.odin +++ b/core/simd/simd.odin @@ -39,6 +39,10 @@ shr :: intrinsics.simd_shr shl_masked :: intrinsics.simd_shl_masked shr_masked :: intrinsics.simd_shr_masked +// Saturation Arithmetic +add_sat :: intrinsics.simd_add_sat +sub_sat :: intrinsics.simd_sub_sat + and :: intrinsics.simd_and or :: intrinsics.simd_or xor :: intrinsics.simd_xor diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index edf84b152..682667972 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -458,6 +458,8 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call } // Integer only + case BuiltinProc_simd_add_sat: + case BuiltinProc_simd_sub_sat: case BuiltinProc_simd_rem: case BuiltinProc_simd_and: case BuiltinProc_simd_or: @@ -486,20 +488,25 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call } Type *elem = base_array_type(x.type); - if (id == BuiltinProc_simd_rem) { + switch (id) { + case BuiltinProc_simd_add_sat: + case BuiltinProc_simd_sub_sat: + case 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 { + break; + default: 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; } + break; } operand->mode = Addressing_Value; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 22ee3d141..07c425cfa 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -129,6 +129,9 @@ BuiltinProc__simd_begin, BuiltinProc_simd_shl_masked, // C logic BuiltinProc_simd_shr_masked, // C logic + BuiltinProc_simd_add_sat, // saturation arithmetic + BuiltinProc_simd_sub_sat, // saturation arithmetic + BuiltinProc_simd_and, BuiltinProc_simd_or, BuiltinProc_simd_xor, @@ -402,6 +405,10 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {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_add_sat"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_sub_sat"), 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}, diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 42f5a60fa..dbdc51078 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1353,6 +1353,30 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const return res; } + case BuiltinProc_simd_add_sat: + case BuiltinProc_simd_sub_sat: + { + arg1 = lb_build_expr(p, ce->args[1]); + + char const *name = nullptr; + switch (builtin_id) { + case BuiltinProc_simd_add_sat: name = is_signed ? "llvm.sadd.sat" : "llvm.uadd.sat"; break; + case BuiltinProc_simd_sub_sat: name = is_signed ? "llvm.ssub.sat" : "llvm.usub.sat"; 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[2] = {}; + args[0] = arg0.value; + args[1] = arg1.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)); -- cgit v1.2.3 From e331b0647e99a07b5d0f70cbac948ab30b30b5c7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 26 May 2022 11:48:04 +0100 Subject: Add `simd_rotate_left` simd_rotate_right` --- core/intrinsics/intrinsics.odin | 4 ++++ core/simd/simd.odin | 3 +++ src/check_builtin.cpp | 23 +++++++++++++++++++++++ src/checker_builtin_procs.hpp | 4 ++++ src/llvm_backend_proc.cpp | 35 +++++++++++++++++++++++++++++++++++ 5 files changed, 69 insertions(+) (limited to 'src/llvm_backend_proc.cpp') diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index 7bbbc1efd..f8e138ef6 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -252,6 +252,10 @@ simd_nearest :: proc(a: #simd[N]any_float) -> #simd[N]any_float --- // equivalent a swizzle with descending indices, e.g. reserve(a, 3, 2, 1, 0) simd_reverse :: proc(a: #simd[N]T) -> #simd[N]T --- +simd_rotate_left :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T --- +simd_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #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 2ce487d13..5d2b5edab 100644 --- a/core/simd/simd.odin +++ b/core/simd/simd.odin @@ -96,6 +96,9 @@ nearest :: intrinsics.simd_nearest reverse :: intrinsics.simd_reverse +rotate_left :: intrinsics.simd_rotate_left +rotate_right :: intrinsics.simd_rotate_right + 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 offset = {}; + check_expr(c, &offset, ce->args[1]); if (offset.mode == Addressing_Invalid) { return false; } + convert_to_typed(c, &offset, t_i64); + if (!is_type_integer(offset.type) || offset.mode != Addressing_Constant) { + error(offset.expr, "'%.*s' expected a constant integer offset"); + return false; + } + check_assignment(c, &offset, t_i64, builtin_name); + + 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 07c425cfa..13fe2822c 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -170,6 +170,8 @@ BuiltinProc__simd_begin, BuiltinProc_simd_nearest, BuiltinProc_simd_reverse, + BuiltinProc_simd_rotate_left, + BuiltinProc_simd_rotate_right, BuiltinProc__simd_end, // Platform specific intrinsics @@ -444,6 +446,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("simd_nearest"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_reverse"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_rotate_left"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_rotate_right"), 2, 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 dbdc51078..c433334d1 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1353,6 +1353,41 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const return res; } + case BuiltinProc_simd_rotate_left: + case BuiltinProc_simd_rotate_right: + { + + i64 count = get_array_type_count(arg0.type); + GB_ASSERT(is_power_of_two(count)); + BigInt bi_count = {}; + big_int_from_i64(&bi_count, count); + + TypeAndValue const &tv = ce->args[1]->tav; + ExactValue val = exact_value_to_integer(tv.value); + GB_ASSERT(val.kind == ExactValue_Integer); + BigInt *bi = &val.value_integer; + if (builtin_id == BuiltinProc_simd_rotate_right) { + big_int_neg(bi, bi); + } + big_int_rem(bi, bi, &bi_count); + big_int_dealloc(&bi_count); + + i64 left = big_int_to_i64(bi); + + LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, count); + LLVMTypeRef llvm_u32 = lb_type(m, t_u32); + for (i64 i = 0; i < count; i++) { + u64 idx = cast(u64)(i+left) & cast(u64)(count-1); + values[i] = LLVMConstInt(llvm_u32, idx, false); + } + LLVMValueRef mask = LLVMConstVector(values, cast(unsigned)count); + + LLVMValueRef v = arg0.value; + res.value = LLVMBuildShuffleVector(p->builder, v, v, mask, ""); + return res; + } + + case BuiltinProc_simd_add_sat: case BuiltinProc_simd_sub_sat: { -- cgit v1.2.3 From f3f6c12a7cc6dca745ae40744a5220878ff9261e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 26 May 2022 11:58:55 +0100 Subject: Add `simd_clamp` --- core/intrinsics/intrinsics.odin | 6 +++-- core/simd/simd.odin | 8 ++++--- src/check_builtin.cpp | 51 +++++++++++++++++++++++++++++++++++++++++ src/checker_builtin_procs.hpp | 5 ++++ src/llvm_backend_proc.cpp | 22 ++++++++++++++++++ 5 files changed, 87 insertions(+), 5 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index f8e138ef6..484cd945c 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -212,8 +212,10 @@ simd_xor :: proc(a, b: #simd[N]T) -> #simd[N]T --- simd_neg :: proc(a: #simd[N]T) -> #simd[N]T --- simd_abs :: proc(a: #simd[N]T) -> #simd[N]T --- -simd_min :: proc(a, b: #simd[N]T) -> #simd[N]T --- -simd_max :: proc(a, b: #simd[N]T) -> #simd[N]T --- + +simd_min :: proc(a, b: #simd[N]T) -> #simd[N]T --- +simd_max :: proc(a, b: #simd[N]T) -> #simd[N]T --- +simd_clamp :: proc(v, min, max: #simd[N]T) -> #simd[N]T --- // Return an unsigned integer of the same size as the input type // NOT A BOOLEAN diff --git a/core/simd/simd.odin b/core/simd/simd.odin index 5d2b5edab..1da0bd3e5 100644 --- a/core/simd/simd.odin +++ b/core/simd/simd.odin @@ -49,9 +49,11 @@ xor :: intrinsics.simd_xor neg :: intrinsics.simd_neg -abs :: intrinsics.simd_abs -min :: intrinsics.simd_min -max :: intrinsics.simd_max +abs :: intrinsics.simd_abs + +min :: intrinsics.simd_min +max :: intrinsics.simd_max +clamp :: intrinsics.simd_clamp // Return an unsigned integer of the same size as the input type // NOT A BOOLEAN diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 54bede3a8..45c9c93c5 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -956,6 +956,57 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call return true; } + case BuiltinProc_simd_clamp: + { + Operand x = {}; + Operand y = {}; + Operand z = {}; + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) { return false; } + check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) { return false; } + check_expr_with_type_hint(c, &z, ce->args[2], x.type); if (z.mode == Addressing_Invalid) { return false; } + convert_to_typed(c, &y, x.type); + convert_to_typed(c, &z, x.type); + 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 (!is_type_simd_vector(z.type)) { + error(z.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; + } + if (!are_types_identical(x.type, z.type)) { + gbString xs = type_to_string(x.type); + gbString zs = type_to_string(z.type); + error(x.expr, "'%.*s' expected 2 arguments of the same type, got '%s' vs '%s'", LIT(builtin_name), xs, zs); + gb_string_free(zs); + 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; + } + 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 13fe2822c..eea4d0a8b 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -141,6 +141,7 @@ BuiltinProc__simd_begin, BuiltinProc_simd_min, BuiltinProc_simd_max, + BuiltinProc_simd_clamp, BuiltinProc_simd_eq, BuiltinProc_simd_ne, @@ -415,9 +416,13 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {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_clamp"), 3, 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}, diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index c433334d1..97bb02ba3 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1412,6 +1412,28 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const return res; } + case BuiltinProc_simd_clamp: + { + arg1 = lb_build_expr(p, ce->args[1]); + arg2 = lb_build_expr(p, ce->args[2]); + + LLVMValueRef v = arg0.value; + LLVMValueRef min = arg1.value; + LLVMValueRef max = arg2.value; + + if (is_float) { + v = LLVMBuildSelect(p->builder, LLVMBuildFCmp(p->builder, LLVMRealOLT, v, min, ""), min, v, ""); + res.value = LLVMBuildSelect(p->builder, LLVMBuildFCmp(p->builder, LLVMRealOGT, v, max, ""), max, v, ""); + } else if (is_signed) { + v = LLVMBuildSelect(p->builder, LLVMBuildICmp(p->builder, LLVMIntSLT, v, min, ""), min, v, ""); + res.value = LLVMBuildSelect(p->builder, LLVMBuildICmp(p->builder, LLVMIntSGT, v, max, ""), max, v, ""); + } else { + v = LLVMBuildSelect(p->builder, LLVMBuildICmp(p->builder, LLVMIntULT, v, min, ""), min, v, ""); + res.value = LLVMBuildSelect(p->builder, LLVMBuildICmp(p->builder, LLVMIntUGT, v, max, ""), max, v, ""); + } + return res; + } + } GB_PANIC("Unhandled simd intrinsic: '%.*s'", LIT(builtin_procs[builtin_id].name)); -- cgit v1.2.3 From 66b5a35ec352b74e66ad866640669d920e9d0849 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 26 May 2022 13:45:47 +0100 Subject: Add `simd_to_bits`; correct fix typo causing issue with parapoly --- src/check_builtin.cpp | 27 +++++++++++++++++++++++++++ src/check_decl.cpp | 8 ++++---- src/check_type.cpp | 7 ++++--- src/checker_builtin_procs.hpp | 4 ++++ src/llvm_backend_proc.cpp | 5 +++++ src/parser.cpp | 14 +++++++++++++- 6 files changed, 57 insertions(+), 8 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 45c9c93c5..c432d6080 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1007,6 +1007,33 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call return true; } + case BuiltinProc_simd_to_bits: + { + 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); + i64 count = get_array_type_count(x.type); + i64 sz = type_size_of(elem); + Type *bit_elem = nullptr; + switch (sz) { + case 1: bit_elem = t_u8; break; + case 2: bit_elem = t_u16; break; + case 4: bit_elem = t_u32; break; + case 8: bit_elem = t_u64; break; + } + GB_ASSERT(bit_elem != nullptr); + + operand->type = alloc_type_simd_vector(count, bit_elem); + operand->mode = Addressing_Value; + return true; + } + + default: GB_PANIC("Unhandled simd intrinsic: %.*s", LIT(builtin_name)); } diff --git a/src/check_decl.cpp b/src/check_decl.cpp index d8cad2ce1..62a1e2555 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1315,20 +1315,20 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d) if (!both_have_where_clauses) switch (kind) { case ProcOverload_Identical: - error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); + error(p->token, "Overloaded procedure '%.*s' has the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); is_invalid = true; break; // case ProcOverload_CallingConvention: - // error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); + // error(p->token, "Overloaded procedure '%.*s' has the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); // is_invalid = true; // break; case ProcOverload_ParamVariadic: - error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); + error(p->token, "Overloaded procedure '%.*s' has the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); is_invalid = true; break; case ProcOverload_ResultCount: case ProcOverload_ResultTypes: - error(p->token, "Overloaded procedure '%.*s' as the same parameters but different results in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); + error(p->token, "Overloaded procedure '%.*s' has the same parameters but different results in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); is_invalid = true; break; case ProcOverload_Polymorphic: diff --git a/src/check_type.cpp b/src/check_type.cpp index de58db054..f84c15a19 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1234,7 +1234,7 @@ bool check_type_specialization_to(CheckerContext *ctx, Type *specialization, Typ } -Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Operand operand) { +Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Operand const &operand) { bool modify_type = !ctx->no_polymorphic_errors; bool show_error = modify_type && !ctx->hide_polymorphic_errors; if (!is_operand_value(operand)) { @@ -2803,13 +2803,14 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t goto array_end; } - if (is_type_polymorphic(elem)) { + if (generic_type != nullptr) { // Ignore } else if (count < 1 || !is_power_of_two(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 + } + *type = alloc_type_simd_vector(count, elem, generic_type); if (is_arch_wasm()) { diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index eea4d0a8b..350213de2 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -170,6 +170,8 @@ BuiltinProc__simd_begin, BuiltinProc_simd_trunc, BuiltinProc_simd_nearest, + BuiltinProc_simd_to_bits, + BuiltinProc_simd_reverse, BuiltinProc_simd_rotate_left, BuiltinProc_simd_rotate_right, @@ -450,6 +452,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("simd_trunc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_nearest"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_to_bits"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_reverse"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_rotate_left"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_rotate_right"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 97bb02ba3..a56aa862a 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1434,6 +1434,11 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const return res; } + case BuiltinProc_simd_to_bits: + { + res.value = LLVMBuildBitCast(p->builder, arg0.value, lb_type(m, tv.type), ""); + return res; + } } GB_PANIC("Unhandled simd intrinsic: '%.*s'", LIT(builtin_procs[builtin_id].name)); diff --git a/src/parser.cpp b/src/parser.cpp index d19e249e5..5280fd4b0 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -360,6 +360,7 @@ Ast *clone_ast(Ast *node) { case Ast_ArrayType: n->ArrayType.count = clone_ast(n->ArrayType.count); n->ArrayType.elem = clone_ast(n->ArrayType.elem); + n->ArrayType.tag = clone_ast(n->ArrayType.tag); break; case Ast_DynamicArrayType: n->DynamicArrayType.elem = clone_ast(n->DynamicArrayType.elem); @@ -2127,7 +2128,18 @@ Ast *parse_operand(AstFile *f, bool lhs) { Token name = expect_token(f, Token_Ident); if (name.string == "type") { return ast_helper_type(f, token, parse_type(f)); - } else if (name.string == "soa" || name.string == "simd") { + } else if ( name.string == "simd") { + Ast *tag = ast_basic_directive(f, token, name); + Ast *original_type = parse_type(f); + Ast *type = unparen_expr(original_type); + switch (type->kind) { + case Ast_ArrayType: type->ArrayType.tag = tag; break; + default: + syntax_error(type, "Expected a fixed array type after #%.*s, got %.*s", LIT(name.string), LIT(ast_strings[type->kind])); + break; + } + return original_type; + } else if (name.string == "soa") { Ast *tag = ast_basic_directive(f, token, name); Ast *original_type = parse_type(f); Ast *type = unparen_expr(original_type); -- 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/llvm_backend_proc.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 7092273a8f5a2ae8c60ece297e7114a29e0f3652 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 26 May 2022 17:36:13 +0100 Subject: Rename `simd_eq` etc to `simd_lanes_eq` --- core/intrinsics/intrinsics.odin | 19 ++++++++++--------- core/simd/simd.odin | 12 ++++++------ src/check_builtin.cpp | 16 ++++++++-------- src/checker_builtin_procs.hpp | 24 ++++++++++++------------ src/llvm_backend_proc.cpp | 36 ++++++++++++++++++------------------ 5 files changed, 54 insertions(+), 53 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index a30bb109f..7c211d8fc 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -205,9 +205,10 @@ simd_shr_masked :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T simd_add_sat :: proc(a, b: #simd[N]T) -> #simd[N]T --- simd_sub_sat :: proc(a, b: #simd[N]T) -> #simd[N]T --- -simd_and :: proc(a, b: #simd[N]T) -> #simd[N]T --- -simd_or :: proc(a, b: #simd[N]T) -> #simd[N]T --- -simd_xor :: proc(a, b: #simd[N]T) -> #simd[N]T --- +simd_and :: proc(a, b: #simd[N]T) -> #simd[N]T --- +simd_or :: proc(a, b: #simd[N]T) -> #simd[N]T --- +simd_xor :: proc(a, b: #simd[N]T) -> #simd[N]T --- +simd_and_not :: proc(a, b: #simd[N]T) -> #simd[N]T --- simd_neg :: proc(a: #simd[N]T) -> #simd[N]T --- @@ -222,12 +223,12 @@ simd_clamp :: proc(v, min, max: #simd[N]T) -> #simd[N]T --- // element-wise: // false => 0x00...00 // true => 0xff...ff -simd_eq :: proc(a, b: #simd[N]T) -> #simd[N]Integer --- -simd_ne :: proc(a, b: #simd[N]T) -> #simd[N]Integer --- -simd_lt :: proc(a, b: #simd[N]T) -> #simd[N]Integer --- -simd_le :: proc(a, b: #simd[N]T) -> #simd[N]Integer --- -simd_gt :: proc(a, b: #simd[N]T) -> #simd[N]Integer --- -simd_ge :: proc(a, b: #simd[N]T) -> #simd[N]Integer --- +simd_lanes_eq :: proc(a, b: #simd[N]T) -> #simd[N]Integer --- +simd_lanes_ne :: proc(a, b: #simd[N]T) -> #simd[N]Integer --- +simd_lanes_lt :: proc(a, b: #simd[N]T) -> #simd[N]Integer --- +simd_lanes_le :: proc(a, b: #simd[N]T) -> #simd[N]Integer --- +simd_lanes_gt :: proc(a, b: #simd[N]T) -> #simd[N]Integer --- +simd_lanes_ge :: proc(a, b: #simd[N]T) -> #simd[N]Integer --- simd_extract :: proc(a: #simd[N]T, idx: uint) -> T --- simd_replace :: proc(a: #simd[N]T, idx: uint, elem: T) -> #simd[N]T --- diff --git a/core/simd/simd.odin b/core/simd/simd.odin index 9673b5cc9..c54d2a480 100644 --- a/core/simd/simd.odin +++ b/core/simd/simd.odin @@ -61,12 +61,12 @@ clamp :: intrinsics.simd_clamp // element-wise: // false => 0x00...00 // true => 0xff...ff -eq :: intrinsics.simd_eq -ne :: intrinsics.simd_ne -lt :: intrinsics.simd_lt -le :: intrinsics.simd_le -gt :: intrinsics.simd_gt -ge :: intrinsics.simd_ge +lanes_eq :: intrinsics.simd_lanes_eq +lanes_ne :: intrinsics.simd_lanes_ne +lanes_lt :: intrinsics.simd_lanes_lt +lanes_le :: intrinsics.simd_lanes_le +lanes_gt :: intrinsics.simd_lanes_gt +lanes_ge :: intrinsics.simd_lanes_ge // extract :: proc(a: #simd[N]T, idx: uint) -> T extract :: intrinsics.simd_extract diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index bfaa91cf8..c63c67d90 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -589,12 +589,12 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call } // 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: + case BuiltinProc_simd_lanes_eq: + case BuiltinProc_simd_lanes_ne: + case BuiltinProc_simd_lanes_lt: + case BuiltinProc_simd_lanes_le: + case BuiltinProc_simd_lanes_gt: + case BuiltinProc_simd_lanes_ge: { // op(#simd[N]T, #simd[N]T) -> #simd[N]V // where `V` is an integer, `size_of(T) == size_of(V)` @@ -611,8 +611,8 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call } Type *elem = base_array_type(x.type); switch (id) { - case BuiltinProc_simd_eq: - case BuiltinProc_simd_ne: + case BuiltinProc_simd_lanes_eq: + case BuiltinProc_simd_lanes_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); diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index eb4bc1498..1b2c105f1 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -144,12 +144,12 @@ BuiltinProc__simd_begin, BuiltinProc_simd_max, BuiltinProc_simd_clamp, - BuiltinProc_simd_eq, - BuiltinProc_simd_ne, - BuiltinProc_simd_lt, - BuiltinProc_simd_le, - BuiltinProc_simd_gt, - BuiltinProc_simd_ge, + BuiltinProc_simd_lanes_eq, + BuiltinProc_simd_lanes_ne, + BuiltinProc_simd_lanes_lt, + BuiltinProc_simd_lanes_le, + BuiltinProc_simd_lanes_gt, + BuiltinProc_simd_lanes_ge, BuiltinProc_simd_extract, BuiltinProc_simd_replace, @@ -428,12 +428,12 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("simd_max"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_clamp"), 3, 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("simd_lanes_eq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_lanes_ne"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_lanes_lt"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_lanes_le"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_lanes_gt"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_lanes_ge"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_extract"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_replace"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 99c023311..4c4000fec 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1151,22 +1151,22 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, ""); } return res; - case BuiltinProc_simd_eq: - case BuiltinProc_simd_ne: - case BuiltinProc_simd_lt: - case BuiltinProc_simd_le: - case BuiltinProc_simd_gt: - case BuiltinProc_simd_ge: + case BuiltinProc_simd_lanes_eq: + case BuiltinProc_simd_lanes_ne: + case BuiltinProc_simd_lanes_lt: + case BuiltinProc_simd_lanes_le: + case BuiltinProc_simd_lanes_gt: + case BuiltinProc_simd_lanes_ge: arg1 = lb_build_expr(p, ce->args[1]); if (is_float) { LLVMRealPredicate pred = cast(LLVMRealPredicate)0; switch (builtin_id) { - case BuiltinProc_simd_eq: pred = LLVMRealOEQ; break; - case BuiltinProc_simd_ne: pred = LLVMRealONE; break; - case BuiltinProc_simd_lt: pred = LLVMRealOLT; break; - case BuiltinProc_simd_le: pred = LLVMRealOLE; break; - case BuiltinProc_simd_gt: pred = LLVMRealOGT; break; - case BuiltinProc_simd_ge: pred = LLVMRealOGE; break; + case BuiltinProc_simd_lanes_eq: pred = LLVMRealOEQ; break; + case BuiltinProc_simd_lanes_ne: pred = LLVMRealONE; break; + case BuiltinProc_simd_lanes_lt: pred = LLVMRealOLT; break; + case BuiltinProc_simd_lanes_le: pred = LLVMRealOLE; break; + case BuiltinProc_simd_lanes_gt: pred = LLVMRealOGT; break; + case BuiltinProc_simd_lanes_ge: pred = LLVMRealOGE; break; } if (pred) { res.value = LLVMBuildFCmp(p->builder, pred, arg0.value, arg1.value, ""); @@ -1176,12 +1176,12 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const } else { LLVMIntPredicate pred = cast(LLVMIntPredicate)0; switch (builtin_id) { - case BuiltinProc_simd_eq: pred = LLVMIntEQ; break; - case BuiltinProc_simd_ne: pred = LLVMIntNE; break; - case BuiltinProc_simd_lt: pred = is_signed ? LLVMIntSLT :LLVMIntULT; break; - case BuiltinProc_simd_le: pred = is_signed ? LLVMIntSLE :LLVMIntULE; break; - case BuiltinProc_simd_gt: pred = is_signed ? LLVMIntSGT :LLVMIntUGT; break; - case BuiltinProc_simd_ge: pred = is_signed ? LLVMIntSGE :LLVMIntUGE; break; + case BuiltinProc_simd_lanes_eq: pred = LLVMIntEQ; break; + case BuiltinProc_simd_lanes_ne: pred = LLVMIntNE; break; + case BuiltinProc_simd_lanes_lt: pred = is_signed ? LLVMIntSLT :LLVMIntULT; break; + case BuiltinProc_simd_lanes_le: pred = is_signed ? LLVMIntSLE :LLVMIntULE; break; + case BuiltinProc_simd_lanes_gt: pred = is_signed ? LLVMIntSGT :LLVMIntUGT; break; + case BuiltinProc_simd_lanes_ge: pred = is_signed ? LLVMIntSGE :LLVMIntUGE; break; } if (pred) { res.value = LLVMBuildICmp(p->builder, pred, arg0.value, arg1.value, ""); -- cgit v1.2.3 From 421d45a7a76e53946e7441212af9d08a0d93ff68 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 26 May 2022 18:06:26 +0100 Subject: Add `intrinsics.fused_mul_add` --- core/intrinsics/intrinsics.odin | 2 ++ core/simd/simd.odin | 3 +++ src/check_builtin.cpp | 53 +++++++++++++++++++++++++++++++++++++++++ src/checker_builtin_procs.hpp | 2 ++ src/llvm_backend_proc.cpp | 25 +++++++++++++++++++ 5 files changed, 85 insertions(+) (limited to 'src/llvm_backend_proc.cpp') diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index bf8f56e63..c13e099c5 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -35,6 +35,8 @@ overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok --- sqrt :: proc(x: $T) -> T where type_is_float(T) --- +fused_mul_add :: proc(a, b, c: $T) -> T where type_is_float(T) || (type_is_simd_vector(T) && type_is_float(type_elem_type(T))) --- + mem_copy :: proc(dst, src: rawptr, len: int) --- mem_copy_non_overlapping :: proc(dst, src: rawptr, len: int) --- mem_zero :: proc(ptr: rawptr, len: int) --- diff --git a/core/simd/simd.odin b/core/simd/simd.odin index 17d97f918..ce278bce7 100644 --- a/core/simd/simd.odin +++ b/core/simd/simd.odin @@ -109,6 +109,9 @@ count_zeros :: intrinsics.count_zeros count_trailing_zeros :: intrinsics.count_trailing_zeros count_leading_zeros :: intrinsics.count_leading_zeros +fused_mul_add :: intrinsics.fused_mul_add +fma :: intrinsics.fused_mul_add + to_array_ptr :: #force_inline proc "contextless" (v: ^#simd[$LANES]$E) -> ^[LANES]E { return (^[LANES]E)(v) } diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index ee805702d..19b78b46e 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -3681,6 +3681,59 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } break; + case BuiltinProc_fused_mul_add: + { + Operand x = {}; + Operand y = {}; + Operand z = {}; + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; + check_expr(c, &y, ce->args[1]); if (y.mode == Addressing_Invalid) return false; + check_expr(c, &z, ce->args[2]); if (z.mode == Addressing_Invalid) return false; + + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; + convert_to_typed(c, &x, y.type); if (x.mode == Addressing_Invalid) return false; + convert_to_typed(c, &z, x.type); if (z.mode == Addressing_Invalid) return false; + convert_to_typed(c, &x, z.type); if (x.mode == Addressing_Invalid) return false; + if (is_type_untyped(x.type)) { + gbString xts = type_to_string(x.type); + error(x.expr, "Expected a typed floating point value or #simd vector for '%.*s', got %s", LIT(builtin_name), xts); + gb_string_free(xts); + return false; + } + + Type *elem = core_array_type(x.type); + if (!is_type_float(x.type) && !(is_type_simd_vector(x.type) && is_type_float(elem))) { + gbString xts = type_to_string(x.type); + error(x.expr, "Expected a floating point or #simd vector value for '%.*s', got %s", LIT(builtin_name), xts); + gb_string_free(xts); + return false; + } + if (is_type_different_to_arch_endianness(elem)) { + GB_ASSERT(elem->kind == Type_Basic); + if (elem->Basic.flags & (BasicFlag_EndianLittle|BasicFlag_EndianBig)) { + gbString xts = type_to_string(x.type); + error(x.expr, "Expected a float which does not specify the explicit endianness for '%.*s', got %s", LIT(builtin_name), xts); + gb_string_free(xts); + return false; + } + } + + if (!are_types_identical(x.type, y.type) || !are_types_identical(y.type, z.type)) { + gbString xts = type_to_string(x.type); + gbString yts = type_to_string(y.type); + gbString zts = type_to_string(z.type); + error(x.expr, "Mismatched types for '%.*s', got %s vs %s vs %s", LIT(builtin_name), xts, yts, zts); + gb_string_free(zts); + gb_string_free(yts); + gb_string_free(xts); + return false; + } + + operand->mode = Addressing_Value; + operand->type = default_type(x.type); + } + break; + case BuiltinProc_mem_copy: case BuiltinProc_mem_copy_non_overlapping: { diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 1b2c105f1..5859ce3ab 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -65,6 +65,7 @@ enum BuiltinProcId { BuiltinProc_overflow_mul, BuiltinProc_sqrt, + BuiltinProc_fused_mul_add, BuiltinProc_mem_copy, BuiltinProc_mem_copy_non_overlapping, @@ -348,6 +349,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("overflow_mul"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("sqrt"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("fused_mul_add"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("mem_copy"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("mem_copy_non_overlapping"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 4c4000fec..a5dda7815 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -2005,6 +2005,31 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, return res; } + case BuiltinProc_fused_mul_add: + { + Type *type = tv.type; + lbValue x = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), type); + lbValue y = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), type); + lbValue z = lb_emit_conv(p, lb_build_expr(p, ce->args[2]), type); + + + char const *name = "llvm.fma"; + LLVMTypeRef types[1] = {lb_type(p->module, type)}; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0])); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); + + LLVMValueRef args[3] = {}; + args[0] = x.value; + args[1] = y.value; + args[2] = z.value; + + lbValue res = {}; + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + res.type = type; + return res; + } + case BuiltinProc_mem_copy: { lbValue dst = lb_build_expr(p, ce->args[0]); -- cgit v1.2.3 From 1f438d4e6c0a979b249683cb2da048a0ff36dcce Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 26 May 2022 18:09:59 +0100 Subject: Merge `intrinsics.simd_sqrt` with `intrinsics.sqrt` --- core/intrinsics/intrinsics.odin | 3 +-- core/simd/simd.odin | 2 +- src/check_builtin.cpp | 21 +++++++++++++++++---- src/checker_builtin_procs.hpp | 2 -- src/llvm_backend_proc.cpp | 2 -- 5 files changed, 19 insertions(+), 11 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index c13e099c5..4f10c5a32 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -33,7 +33,7 @@ overflow_add :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok --- overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok --- overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok --- -sqrt :: proc(x: $T) -> T where type_is_float(T) --- +sqrt :: proc(x: $T) -> T where type_is_float(T) || (type_is_simd_vector(T) && type_is_float(type_elem_type(T))) --- fused_mul_add :: proc(a, b, c: $T) -> T where type_is_float(T) || (type_is_simd_vector(T) && type_is_float(type_elem_type(T))) --- @@ -247,7 +247,6 @@ 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 --- // Lane-wise operations -simd_sqrt :: proc(a: #simd[N]any_float) -> #simd[N]any_float --- // IEEE sqrt 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 --- diff --git a/core/simd/simd.odin b/core/simd/simd.odin index ce278bce7..263402c43 100644 --- a/core/simd/simd.odin +++ b/core/simd/simd.odin @@ -91,7 +91,7 @@ shuffle :: intrinsics.simd_shuffle select :: intrinsics.simd_select -sqrt :: intrinsics.simd_sqrt +sqrt :: intrinsics.sqrt ceil :: intrinsics.simd_ceil floor :: intrinsics.simd_floor trunc :: intrinsics.simd_trunc diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 19b78b46e..87f7358f0 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -894,7 +894,6 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call return true; } - case BuiltinProc_simd_sqrt: case BuiltinProc_simd_ceil: case BuiltinProc_simd_floor: case BuiltinProc_simd_trunc: @@ -3661,14 +3660,28 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (x.mode == Addressing_Invalid) { return false; } + + Type *elem = core_array_type(x.type); + if (!is_type_float(x.type) && !(is_type_simd_vector(x.type) && is_type_float(elem))) { + gbString xts = type_to_string(x.type); + error(x.expr, "Expected a floating point or #simd vector value for '%.*s', got %s", LIT(builtin_name), xts); + gb_string_free(xts); + return false; + } else if (is_type_different_to_arch_endianness(elem)) { + GB_ASSERT(elem->kind == Type_Basic); + if (elem->Basic.flags & (BasicFlag_EndianLittle|BasicFlag_EndianBig)) { + gbString xts = type_to_string(x.type); + error(x.expr, "Expected a float which does not specify the explicit endianness for '%.*s', got %s", LIT(builtin_name), xts); + gb_string_free(xts); + return false; + } + } if (!is_type_float(x.type)) { gbString xts = type_to_string(x.type); error(x.expr, "Expected a floating point value for '%.*s', got %s", LIT(builtin_name), xts); gb_string_free(xts); return false; - } - - if (x.mode == Addressing_Constant) { + } else if (x.mode == Addressing_Constant) { f64 v = exact_value_to_f64(x.value); operand->mode = Addressing_Constant; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 5859ce3ab..28e62e5d6 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -166,7 +166,6 @@ BuiltinProc__simd_begin, BuiltinProc_simd_shuffle, BuiltinProc_simd_select, - BuiltinProc_simd_sqrt, BuiltinProc_simd_ceil, BuiltinProc_simd_floor, BuiltinProc_simd_trunc, @@ -451,7 +450,6 @@ 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}, diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index a5dda7815..13643ccfe 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1315,7 +1315,6 @@ 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: @@ -1323,7 +1322,6 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const { 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; -- cgit v1.2.3 From 833f9dd037d2638a9d2a5bd2a1346057b097aed5 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 27 May 2022 11:55:03 +0100 Subject: Minor change --- src/llvm_backend_proc.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 13643ccfe..93481352b 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1104,8 +1104,9 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const 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; + op_code = LLVMAnd; + arg1.value = LLVMBuildNot(p->builder, arg1.value, ""); + break; } if (op_code) { res.value = LLVMBuildBinOp(p->builder, op_code, arg0.value, arg1.value, ""); -- cgit v1.2.3 From 609ddf28b73817f4043aed27fb8056eb1eacffc0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 27 May 2022 14:56:36 +0100 Subject: Add intrinsics `nontemporal_store` and `nontemporal_load` --- core/intrinsics/intrinsics.odin | 3 +++ src/check_builtin.cpp | 6 ++---- src/checker_builtin_procs.hpp | 4 ++++ src/llvm_backend_proc.cpp | 8 ++++++++ 4 files changed, 17 insertions(+), 4 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index fa9b0ecec..8becd998d 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -12,6 +12,9 @@ soa_struct :: proc($N: int, $T: typeid) -> type/#soa[N]T volatile_load :: proc(dst: ^$T) -> T --- volatile_store :: proc(dst: ^$T, val: T) -> T --- +nontemporal_load :: proc(dst: ^$T) -> T --- +nontemporal_store :: proc(dst: ^$T, val: T) -> T --- + // Trapping debug_trap :: proc() --- trap :: proc() -> ! --- diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index e93e63d4d..ba34a177b 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -4025,9 +4025,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; case BuiltinProc_volatile_store: - /*fallthrough*/ case BuiltinProc_unaligned_store: - /*fallthrough*/ + case BuiltinProc_nontemporal_store: case BuiltinProc_atomic_store: { Type *elem = nullptr; @@ -4074,9 +4073,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_volatile_load: - /*fallthrough*/ case BuiltinProc_unaligned_load: - /*fallthrough*/ + case BuiltinProc_nontemporal_load: case BuiltinProc_atomic_load: { Type *elem = nullptr; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 3ef97b361..2dd775193 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -80,6 +80,8 @@ enum BuiltinProcId { BuiltinProc_unaligned_store, BuiltinProc_unaligned_load, + BuiltinProc_nontemporal_store, + BuiltinProc_nontemporal_load, BuiltinProc_prefetch_read_instruction, BuiltinProc_prefetch_read_data, @@ -367,6 +369,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("unaligned_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("unaligned_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("nontemporal_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("nontemporal_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("prefetch_read_instruction"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("prefetch_read_data"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 93481352b..2b7cad5cd 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -2111,6 +2111,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, return {}; case BuiltinProc_volatile_store: + case BuiltinProc_nontemporal_store: case BuiltinProc_atomic_store: case BuiltinProc_atomic_store_explicit: { lbValue dst = lb_build_expr(p, ce->args[0]); @@ -2120,6 +2121,9 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, LLVMValueRef instr = LLVMBuildStore(p->builder, val.value, dst.value); switch (id) { case BuiltinProc_volatile_store: LLVMSetVolatile(instr, true); break; + case BuiltinProc_nontemporal_store: + // TODO(bill): BuiltinProc_nontemporal_store + 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; } @@ -2130,6 +2134,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, } case BuiltinProc_volatile_load: + case BuiltinProc_nontemporal_load: case BuiltinProc_atomic_load: case BuiltinProc_atomic_load_explicit: { lbValue dst = lb_build_expr(p, ce->args[0]); @@ -2137,6 +2142,9 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, LLVMValueRef instr = LLVMBuildLoad(p->builder, dst.value, ""); switch (id) { case BuiltinProc_volatile_load: LLVMSetVolatile(instr, true); break; + case BuiltinProc_nontemporal_load: + // TODO(bill): BuiltinProc_nontemporal_load + 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; } -- cgit v1.2.3 From 8518d3b2327885538993afa2655b83abb71b82e3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 27 May 2022 22:57:16 +0100 Subject: Rename to `non_temporaral_*` --- core/simd/x86/sse.odin | 2 +- core/simd/x86/sse2.odin | 6 +++--- src/check_builtin.cpp | 4 ++-- src/checker_builtin_procs.hpp | 8 ++++---- src/llvm_backend_proc.cpp | 12 ++++++------ 5 files changed, 16 insertions(+), 16 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/core/simd/x86/sse.odin b/core/simd/x86/sse.odin index 50211872e..e697d5f5e 100644 --- a/core/simd/x86/sse.odin +++ b/core/simd/x86/sse.odin @@ -414,7 +414,7 @@ _MM_TRANSPOSE4_PS :: #force_inline proc "c" (row0, row1, row2, row3: ^__m128) { } _mm_stream_ps :: #force_inline proc "c" (addr: [^]f32, a: __m128) { - intrinsics.nontemporal_store((^__m128)(addr), a) + intrinsics.non_temporal_store((^__m128)(addr), a) } diff --git a/core/simd/x86/sse2.odin b/core/simd/x86/sse2.odin index 359f19062..a47d2a09d 100644 --- a/core/simd/x86/sse2.odin +++ b/core/simd/x86/sse2.odin @@ -351,10 +351,10 @@ _mm_storel_epi64 :: #force_inline proc "c" (mem_addr: ^__m128i, a: __m128i) { intrinsics.mem_copy_non_overlapping(mem_addr, &a, 8) } _mm_stream_si128 :: #force_inline proc "c" (mem_addr: ^__m128i, a: __m128i) { - intrinsics.nontemporal_store(mem_addr, a) + intrinsics.non_temporal_store(mem_addr, a) } _mm_stream_si32 :: #force_inline proc "c" (mem_addr: ^i32, a: i32) { - intrinsics.nontemporal_store(mem_addr, a) + intrinsics.non_temporal_store(mem_addr, a) } _mm_move_epi64 :: #force_inline proc "c" (a: __m128i) -> __m128i { zero := _mm_setzero_si128() @@ -694,7 +694,7 @@ _mm_loadl_pd :: #force_inline proc "c" (a: __m128d, mem_addr: ^f64) -> __m128d { return _mm_setr_pd(mem_addr^, simd.extract(a, 1)) } _mm_stream_pd :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) { - intrinsics.nontemporal_store((^__m128d)(mem_addr), a) + intrinsics.non_temporal_store((^__m128d)(mem_addr), a) } _mm_store_sd :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) { mem_addr^ = simd.extract(a, 0) diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index ad227489b..9fa9cc590 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -4021,7 +4021,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_volatile_store: case BuiltinProc_unaligned_store: - case BuiltinProc_nontemporal_store: + case BuiltinProc_non_temporal_store: case BuiltinProc_atomic_store: { Type *elem = nullptr; @@ -4069,7 +4069,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_volatile_load: case BuiltinProc_unaligned_load: - case BuiltinProc_nontemporal_load: + case BuiltinProc_non_temporal_load: case BuiltinProc_atomic_load: { Type *elem = nullptr; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 2dd775193..1d7ee0a34 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -80,8 +80,8 @@ enum BuiltinProcId { BuiltinProc_unaligned_store, BuiltinProc_unaligned_load, - BuiltinProc_nontemporal_store, - BuiltinProc_nontemporal_load, + BuiltinProc_non_temporal_store, + BuiltinProc_non_temporal_load, BuiltinProc_prefetch_read_instruction, BuiltinProc_prefetch_read_data, @@ -369,8 +369,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("unaligned_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("unaligned_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("nontemporal_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("nontemporal_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("non_temporal_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("non_temporal_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("prefetch_read_instruction"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("prefetch_read_data"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 2b7cad5cd..4d0df2861 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -2111,7 +2111,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, return {}; case BuiltinProc_volatile_store: - case BuiltinProc_nontemporal_store: + case BuiltinProc_non_temporal_store: case BuiltinProc_atomic_store: case BuiltinProc_atomic_store_explicit: { lbValue dst = lb_build_expr(p, ce->args[0]); @@ -2121,8 +2121,8 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, LLVMValueRef instr = LLVMBuildStore(p->builder, val.value, dst.value); switch (id) { case BuiltinProc_volatile_store: LLVMSetVolatile(instr, true); break; - case BuiltinProc_nontemporal_store: - // TODO(bill): BuiltinProc_nontemporal_store + case BuiltinProc_non_temporal_store: + // TODO(bill): BuiltinProc_non_temporal_store 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; @@ -2134,7 +2134,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, } case BuiltinProc_volatile_load: - case BuiltinProc_nontemporal_load: + case BuiltinProc_non_temporal_load: case BuiltinProc_atomic_load: case BuiltinProc_atomic_load_explicit: { lbValue dst = lb_build_expr(p, ce->args[0]); @@ -2142,8 +2142,8 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, LLVMValueRef instr = LLVMBuildLoad(p->builder, dst.value, ""); switch (id) { case BuiltinProc_volatile_load: LLVMSetVolatile(instr, true); break; - case BuiltinProc_nontemporal_load: - // TODO(bill): BuiltinProc_nontemporal_load + case BuiltinProc_non_temporal_load: + // TODO(bill): BuiltinProc_non_temporal_load 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; -- cgit v1.2.3 From d7eaf0f87b7677e84bf1f65c34305801748c39ee Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 28 May 2022 15:41:11 +0100 Subject: Add `intrinsics.x86_cpuid` and `intrinsics.x86_xgetbv` --- core/intrinsics/intrinsics.odin | 4 +++ core/simd/x86/ssse3.odin | 3 +- src/build_settings.cpp | 9 ++++++ src/check_builtin.cpp | 60 ++++++++++++++++++++++++++++++++++++++- src/checker_builtin_procs.hpp | 6 ++++ src/llvm_backend_proc.cpp | 63 ++++++++++++++++++++++++++++------------- 6 files changed, 124 insertions(+), 21 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index 8becd998d..89f9a5f20 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -278,6 +278,10 @@ wasm_memory_size :: proc(index: uintptr) -> int --- wasm_memory_atomic_wait32 :: proc(ptr: ^u32, expected: u32, timeout_ns: i64) -> u32 --- wasm_memory_atomic_notify32 :: proc(ptr: ^u32, waiters: u32) -> (waiters_woken_up: u32) --- +// x86 Targets (i386, amd64) +cpuid :: proc(ax, cx: u32) -> (eax, ebc, ecx, edx: u32) --- +xgetbv :: proc(cx: u32) -> (eax, edx: u32) --- + // Darwin targets only objc_object :: struct{} diff --git a/core/simd/x86/ssse3.odin b/core/simd/x86/ssse3.odin index 920dddd85..4abd4c84c 100644 --- a/core/simd/x86/ssse3.odin +++ b/core/simd/x86/ssse3.odin @@ -121,4 +121,5 @@ foreign _ { @(link_name = "llvm.x86.ssse3.psign.w.128") psignw128 :: proc(a, b: i16x8) -> i16x8 --- @(link_name = "llvm.x86.ssse3.psign.d.128") - psignd128 :: proc(a, b: i32x4) -> i32x4 ---} \ No newline at end of file + psignd128 :: proc(a, b: i32x4) -> i32x4 --- +} \ No newline at end of file diff --git a/src/build_settings.cpp b/src/build_settings.cpp index b458d8308..27e09a0db 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -629,6 +629,15 @@ bool is_arch_wasm(void) { return false; } +bool is_arch_x86(void) { + switch (build_context.metrics.arch) { + case TargetArch_i386: + case TargetArch_amd64: + return true; + } + return false; +} + bool allow_check_foreign_filepath(void) { switch (build_context.metrics.arch) { case TargetArch_wasm32: diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 9fa9cc590..f8ac545be 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1060,8 +1060,8 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call operand->type = t_untyped_integer; operand->mode = Addressing_Constant; operand->value = exact_value_i64(result); + return true; } - default: GB_PANIC("Unhandled simd intrinsic: %.*s", LIT(builtin_name)); } @@ -5275,6 +5275,64 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } break; + case BuiltinProc_x86_cpuid: + { + if (!is_arch_x86()) { + error(call, "'%.*s' is only allowed on x86 targets (i386, amd64)", LIT(builtin_name)); + return false; + } + + Operand ax = {}; + Operand cx = {}; + + check_expr_with_type_hint(c, &ax, ce->args[0], t_u32); if (ax.mode == Addressing_Invalid) return false; + check_expr_with_type_hint(c, &cx, ce->args[1], t_u32); if (cx.mode == Addressing_Invalid) return false; + convert_to_typed(c, &ax, t_u32); if (ax.mode == Addressing_Invalid) return false; + convert_to_typed(c, &cx, t_u32); if (cx.mode == Addressing_Invalid) return false; + if (!are_types_identical(ax.type, t_u32)) { + gbString str = type_to_string(ax.type); + error(ax.expr, "'%.*s' expected a u32, got %s", LIT(builtin_name), str); + gb_string_free(str); + return false; + } + if (!are_types_identical(cx.type, t_u32)) { + gbString str = type_to_string(cx.type); + error(cx.expr, "'%.*s' expected a u32, got %s", LIT(builtin_name), str); + gb_string_free(str); + return false; + } + Type *types[4] = {t_u32, t_u32, t_u32, t_u32}; // eax ebc ecx edx + operand->type = alloc_type_tuple_from_field_types(types, gb_count_of(types), false, false); + operand->mode = Addressing_Value; + operand->value = {}; + return true; + } + break; + case BuiltinProc_x86_xgetbv: + { + if (!is_arch_x86()) { + error(call, "'%.*s' is only allowed on x86 targets (i386, amd64)", LIT(builtin_name)); + return false; + } + + Operand cx = {}; + check_expr_with_type_hint(c, &cx, ce->args[0], t_u32); if (cx.mode == Addressing_Invalid) return false; + convert_to_typed(c, &cx, t_u32); if (cx.mode == Addressing_Invalid) return false; + if (!are_types_identical(cx.type, t_u32)) { + gbString str = type_to_string(cx.type); + error(cx.expr, "'%.*s' expected a u32, got %s", LIT(builtin_name), str); + gb_string_free(str); + return false; + } + + Type *types[2] = {t_u32, t_u32}; + operand->type = alloc_type_tuple_from_field_types(types, gb_count_of(types), false, false); + operand->mode = Addressing_Value; + operand->value = {}; + return true; + } + break; + } return true; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 1d7ee0a34..35f14c6a8 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -187,6 +187,9 @@ BuiltinProc__simd_end, // Platform specific intrinsics BuiltinProc_syscall, + BuiltinProc_x86_cpuid, + BuiltinProc_x86_xgetbv, + // Constant type tests BuiltinProc__type_begin, @@ -470,10 +473,13 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("simd_rotate_right"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_x86__MM_SHUFFLE"), 4, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("syscall"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("x86_cpuid"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("x86_xgetbv"), 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 4d0df2861..8cbb533bc 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1007,9 +1007,9 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const lbValue res = {}; res.type = tv.type; - lbValue arg0 = lb_build_expr(p, ce->args[0]); - lbValue arg1 = {}; - lbValue arg2 = {}; + lbValue arg0 = {}; if (ce->args.count > 0) arg0 = lb_build_expr(p, ce->args[0]); + lbValue arg1 = {}; if (ce->args.count > 1) arg0 = lb_build_expr(p, ce->args[1]); + lbValue arg2 = {}; if (ce->args.count > 2) arg0 = lb_build_expr(p, ce->args[2]); Type *elem = base_array_type(arg0.type); @@ -1024,7 +1024,6 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const case BuiltinProc_simd_mul: case BuiltinProc_simd_div: case BuiltinProc_simd_rem: - arg1 = lb_build_expr(p, ce->args[1]); if (is_float) { switch (builtin_id) { case BuiltinProc_simd_add: op_code = LLVMFAdd; break; @@ -1062,7 +1061,6 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const case BuiltinProc_simd_shr: // Odin logic case BuiltinProc_simd_shl_masked: // C logic case BuiltinProc_simd_shr_masked: // C logic - arg1 = lb_build_expr(p, ce->args[1]); { i64 sz = type_size_of(elem); GB_ASSERT(arg0.type->kind == Type_SimdVector); @@ -1098,7 +1096,6 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const 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; @@ -1143,7 +1140,6 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const } return res; case BuiltinProc_simd_max: - arg1 = lb_build_expr(p, ce->args[1]); if (is_float) { LLVMValueRef cond = LLVMBuildFCmp(p->builder, LLVMRealOGT, arg0.value, arg1.value, ""); res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, ""); @@ -1158,7 +1154,6 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const case BuiltinProc_simd_lanes_le: case BuiltinProc_simd_lanes_gt: case BuiltinProc_simd_lanes_ge: - arg1 = lb_build_expr(p, ce->args[1]); if (is_float) { LLVMRealPredicate pred = cast(LLVMRealPredicate)0; switch (builtin_id) { @@ -1193,12 +1188,9 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const break; case BuiltinProc_simd_extract: - arg1 = lb_build_expr(p, ce->args[1]); res.value = LLVMBuildExtractElement(p->builder, arg0.value, arg1.value, ""); return res; case BuiltinProc_simd_replace: - arg1 = lb_build_expr(p, ce->args[1]); - arg2 = lb_build_expr(p, ce->args[2]); res.value = LLVMBuildInsertElement(p->builder, arg0.value, arg2.value, arg1.value, ""); return res; @@ -1283,12 +1275,9 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const case BuiltinProc_simd_shuffle: { - arg1 = lb_build_expr(p, ce->args[1]); - Type *vt = arg0.type; GB_ASSERT(vt->kind == Type_SimdVector); - i64 indices_count = ce->args.count-2; i64 max_count = vt->SimdVector.count*2; GB_ASSERT(indices_count <= max_count); @@ -1394,8 +1383,6 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const case BuiltinProc_simd_add_sat: case BuiltinProc_simd_sub_sat: { - arg1 = lb_build_expr(p, ce->args[1]); - char const *name = nullptr; switch (builtin_id) { case BuiltinProc_simd_add_sat: name = is_signed ? "llvm.sadd.sat" : "llvm.uadd.sat"; break; @@ -1417,9 +1404,6 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const case BuiltinProc_simd_clamp: { - arg1 = lb_build_expr(p, ce->args[1]); - arg2 = lb_build_expr(p, ce->args[2]); - LLVMValueRef v = arg0.value; LLVMValueRef min = arg1.value; LLVMValueRef max = arg2.value; @@ -2737,6 +2721,47 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, return res; } + + case BuiltinProc_x86_cpuid: + { + Type *param_types[2] = {t_u32, t_u32}; + Type *type = alloc_type_proc_from_types(param_types, gb_count_of(param_types), tv.type, false, ProcCC_None); + LLVMTypeRef func_type = LLVMGetElementType(lb_type(p->module, type)); + LLVMValueRef the_asm = llvm_get_inline_asm( + func_type, + str_lit("cpuid"), + str_lit("={ax},={bx},={cx},={dx},{ax},{cx}"), + true + ); + GB_ASSERT(the_asm != nullptr); + + LLVMValueRef args[2] = {}; + args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_u32).value; + args[1] = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_u32).value; + lbValue res = {}; + res.type = tv.type; + res.value = LLVMBuildCall2(p->builder, func_type, the_asm, args, gb_count_of(args), ""); + return res; + } + case BuiltinProc_x86_xgetbv: + { + Type *type = alloc_type_proc_from_types(&t_u32, 1, tv.type, false, ProcCC_None); + LLVMTypeRef func_type = LLVMGetElementType(lb_type(p->module, type)); + LLVMValueRef the_asm = llvm_get_inline_asm( + func_type, + str_lit("xgetbv"), + str_lit("={ax},={dx},{cx}"), + true + ); + GB_ASSERT(the_asm != nullptr); + + LLVMValueRef args[1] = {}; + args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_u32).value; + lbValue res = {}; + res.type = tv.type; + res.value = LLVMBuildCall2(p->builder, func_type, the_asm, args, gb_count_of(args), ""); + return res; + } } GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name)); -- cgit v1.2.3 From cef022539ebd41a4a80707f1a702e09e6748ade0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 29 May 2022 15:13:14 +0100 Subject: Rename to `lanes_rotate_left`, `lanes_rotate_right`, `lanes_reverse` --- core/simd/simd.odin | 6 +++--- src/check_builtin.cpp | 6 +++--- src/checker_builtin_procs.hpp | 12 ++++++------ src/llvm_backend_proc.cpp | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/core/simd/simd.odin b/core/simd/simd.odin index ed7e418f3..390ff377a 100644 --- a/core/simd/simd.odin +++ b/core/simd/simd.odin @@ -134,10 +134,10 @@ nearest :: intrinsics.simd_nearest to_bits :: intrinsics.simd_to_bits -lanes_reverse :: intrinsics.simd_reverse +lanes_reverse :: intrinsics.simd_lanes_reverse -lanes_rotate_left :: intrinsics.simd_rotate_left -lanes_rotate_right :: intrinsics.simd_rotate_right +lanes_rotate_left :: intrinsics.simd_lanes_rotate_left +lanes_rotate_right :: intrinsics.simd_lanes_rotate_right count_ones :: intrinsics.count_ones count_zeros :: intrinsics.count_zeros diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index f8ac545be..92e3987a0 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -919,7 +919,7 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call return true; } - case BuiltinProc_simd_reverse: + case BuiltinProc_simd_lanes_reverse: { Operand x = {}; check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; @@ -933,8 +933,8 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call return true; } - case BuiltinProc_simd_rotate_left: - case BuiltinProc_simd_rotate_right: + case BuiltinProc_simd_lanes_rotate_left: + case BuiltinProc_simd_lanes_rotate_right: { Operand x = {}; check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 35f14c6a8..2e27cc026 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -175,9 +175,9 @@ BuiltinProc__simd_begin, BuiltinProc_simd_to_bits, - BuiltinProc_simd_reverse, - BuiltinProc_simd_rotate_left, - BuiltinProc_simd_rotate_right, + BuiltinProc_simd_lanes_reverse, + BuiltinProc_simd_lanes_rotate_left, + BuiltinProc_simd_lanes_rotate_right, // Platform specific SIMD intrinsics @@ -468,9 +468,9 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("simd_to_bits"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("simd_reverse"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("simd_rotate_left"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("simd_rotate_right"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_lanes_reverse"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_lanes_rotate_left"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_lanes_rotate_right"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_x86__MM_SHUFFLE"), 4, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 8cbb533bc..1e3591bf1 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1330,7 +1330,7 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const return res; } - case BuiltinProc_simd_reverse: + case BuiltinProc_simd_lanes_reverse: { i64 count = get_array_type_count(arg0.type); LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, count); @@ -1345,8 +1345,8 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const return res; } - case BuiltinProc_simd_rotate_left: - case BuiltinProc_simd_rotate_right: + case BuiltinProc_simd_lanes_rotate_left: + case BuiltinProc_simd_lanes_rotate_right: { i64 count = get_array_type_count(arg0.type); @@ -1358,7 +1358,7 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const ExactValue val = exact_value_to_integer(tv.value); GB_ASSERT(val.kind == ExactValue_Integer); BigInt *bi = &val.value_integer; - if (builtin_id == BuiltinProc_simd_rotate_right) { + if (builtin_id == BuiltinProc_simd_lanes_rotate_right) { big_int_neg(bi, bi); } big_int_rem(bi, bi, &bi_count); -- cgit v1.2.3 From f3aefbc4434b92fc3fda74c942c953b08dd18a62 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 30 May 2022 14:53:12 +0100 Subject: `@(require_target_feature=)` `@(enable_target_feature=)` require_target_feature - required by the target micro-architecture enable_target_feature - will be enabled for the specified procedure only --- core/simd/x86/fxsr.odin | 4 + core/simd/x86/pclmulqdq.odin | 1 + core/simd/x86/sha.odin | 7 ++ core/simd/x86/sse.odin | 102 ++++++++++++++++++++ core/simd/x86/sse2.odin | 223 +++++++++++++++++++++++++++++++++++++++++++ core/simd/x86/sse3.odin | 11 +++ core/simd/x86/ssse3.odin | 16 ++++ src/build_settings.cpp | 109 ++++++++++++++++++++- src/check_decl.cpp | 12 +++ src/checker.cpp | 16 ++++ src/checker.hpp | 3 + src/entity.cpp | 8 +- src/llvm_backend.cpp | 4 +- src/llvm_backend_proc.cpp | 13 +++ src/main.cpp | 4 +- src/string.cpp | 9 ++ 16 files changed, 533 insertions(+), 9 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/core/simd/x86/fxsr.odin b/core/simd/x86/fxsr.odin index 847678d29..cd78de7d4 100644 --- a/core/simd/x86/fxsr.odin +++ b/core/simd/x86/fxsr.odin @@ -1,17 +1,21 @@ //+build i386, amd64 package simd_x86 +@(enable_target_feature="fxsr") _fxsave :: #force_inline proc "c" (mem_addr: rawptr) { fxsave(mem_addr) } +@(enable_target_feature="fxsr") _fxrstor :: #force_inline proc "c" (mem_addr: rawptr) { fxrstor(mem_addr) } when ODIN_ARCH == .amd64 { + @(enable_target_feature="fxsr") _fxsave64 :: #force_inline proc "c" (mem_addr: rawptr) { fxsave64(mem_addr) } + @(enable_target_feature="fxsr") _fxrstor64 :: #force_inline proc "c" (mem_addr: rawptr) { fxrstor64(mem_addr) } diff --git a/core/simd/x86/pclmulqdq.odin b/core/simd/x86/pclmulqdq.odin index ba4ecf35f..8a665db03 100644 --- a/core/simd/x86/pclmulqdq.odin +++ b/core/simd/x86/pclmulqdq.odin @@ -1,6 +1,7 @@ //+build i386, amd64 package simd_x86 +@(enable_target_feature="pclmulqdq") _mm_clmulepi64_si128 :: #force_inline proc "c" (a, b: __m128i, $IMM8: u8) -> __m128i { return pclmulqdq(a, b, u8(IMM8)) } diff --git a/core/simd/x86/sha.odin b/core/simd/x86/sha.odin index d907ce6a6..90f1d72ce 100644 --- a/core/simd/x86/sha.odin +++ b/core/simd/x86/sha.odin @@ -1,24 +1,31 @@ //+build i386, amd64 package simd_x86 +@(enable_target_feature="sha") _mm_sha1msg1_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)sha1msg1(transmute(i32x4)a, transmute(i32x4)b) } +@(enable_target_feature="sha") _mm_sha1msg2_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)sha1msg2(transmute(i32x4)a, transmute(i32x4)b) } +@(enable_target_feature="sha") _mm_sha1nexte_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)sha1nexte(transmute(i32x4)a, transmute(i32x4)b) } +@(enable_target_feature="sha") _mm_sha1rnds4_epu32 :: #force_inline proc "c" (a, b: __m128i, $FUNC: u32) -> __m128i where 0 <= FUNC, FUNC <= 3 { return transmute(__m128i)sha1rnds4(transmute(i32x4)a, transmute(i32x4)b, u8(FUNC & 0xff)) } +@(enable_target_feature="sha") _mm_sha256msg1_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)sha256msg1(transmute(i32x4)a, transmute(i32x4)b) } +@(enable_target_feature="sha") _mm_sha256msg2_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)sha256msg2(transmute(i32x4)a, transmute(i32x4)b) } +@(enable_target_feature="sha") _mm_sha256rnds2_epu32 :: #force_inline proc "c" (a, b, k: __m128i) -> __m128i { return transmute(__m128i)sha256rnds2(transmute(i32x4)a, transmute(i32x4)b, transmute(i32x4)k) } diff --git a/core/simd/x86/sse.odin b/core/simd/x86/sse.odin index a564f243a..6d8939b1b 100644 --- a/core/simd/x86/sse.odin +++ b/core/simd/x86/sse.odin @@ -43,232 +43,299 @@ _MM_FLUSH_ZERO_ON :: 0x8000 _MM_FLUSH_ZERO_OFF :: 0x0000 +@(enable_target_feature="sse") _mm_add_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { return addss(a, b) } +@(enable_target_feature="sse") _mm_add_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return simd.add(a, b) } +@(enable_target_feature="sse") _mm_sub_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { return subss(a, b) } +@(enable_target_feature="sse") _mm_sub_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return simd.sub(a, b) } +@(enable_target_feature="sse") _mm_mul_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { return mulss(a, b) } +@(enable_target_feature="sse") _mm_mul_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return simd.mul(a, b) } +@(enable_target_feature="sse") _mm_div_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { return divss(a, b) } +@(enable_target_feature="sse") _mm_div_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return simd.div(a, b) } +@(enable_target_feature="sse") _mm_sqrt_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { return sqrtss(a) } +@(enable_target_feature="sse") _mm_sqrt_ps :: #force_inline proc "c" (a: __m128) -> __m128 { return sqrtps(a) } +@(enable_target_feature="sse") _mm_rcp_ss :: #force_inline proc "c" (a: __m128) -> __m128 { return rcpss(a) } +@(enable_target_feature="sse") _mm_rcp_ps :: #force_inline proc "c" (a: __m128) -> __m128 { return rcpps(a) } +@(enable_target_feature="sse") _mm_rsqrt_ss :: #force_inline proc "c" (a: __m128) -> __m128 { return rsqrtss(a) } +@(enable_target_feature="sse") _mm_rsqrt_ps :: #force_inline proc "c" (a: __m128) -> __m128 { return rsqrtps(a) } +@(enable_target_feature="sse") _mm_min_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { return minss(a, b) } +@(enable_target_feature="sse") _mm_min_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return minps(a, b) } +@(enable_target_feature="sse") _mm_max_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { return maxss(a, b) } +@(enable_target_feature="sse") _mm_max_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return maxps(a, b) } +@(enable_target_feature="sse") _mm_and_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return transmute(__m128)simd.and(transmute(__m128i)a, transmute(__m128i)b) } +@(enable_target_feature="sse") _mm_andnot_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return transmute(__m128)simd.and_not(transmute(__m128i)a, transmute(__m128i)b) } +@(enable_target_feature="sse") _mm_or_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return transmute(__m128)simd.or(transmute(__m128i)a, transmute(__m128i)b) } +@(enable_target_feature="sse") _mm_xor_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return transmute(__m128)simd.xor(transmute(__m128i)a, transmute(__m128i)b) } +@(enable_target_feature="sse") _mm_cmpeq_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { return cmpss(a, b, 0) } +@(enable_target_feature="sse") _mm_cmplt_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { return cmpss(a, b, 1) } +@(enable_target_feature="sse") _mm_cmple_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { return cmpss(a, b, 2) } +@(enable_target_feature="sse") _mm_cmpgt_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { return simd.shuffle(a, cmpss(b, a, 1), 4, 1, 2, 3) } +@(enable_target_feature="sse") _mm_cmpge_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { return simd.shuffle(a, cmpss(b, a, 2), 4, 1, 2, 3) } +@(enable_target_feature="sse") _mm_cmpneq_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { return cmpss(a, b, 4) } +@(enable_target_feature="sse") _mm_cmpnlt_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { return cmpss(a, b, 5) } +@(enable_target_feature="sse") _mm_cmpnle_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { return cmpss(a, b, 6) } +@(enable_target_feature="sse") _mm_cmpngt_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { return simd.shuffle(a, cmpss(b, a, 5), 4, 1, 2, 3) } +@(enable_target_feature="sse") _mm_cmpnge_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { return simd.shuffle(a, cmpss(b, a, 6), 4, 1, 2, 3) } +@(enable_target_feature="sse") _mm_cmpord_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { return cmpss(a, b, 7) } +@(enable_target_feature="sse") _mm_cmpunord_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { return cmpss(a, b, 3) } +@(enable_target_feature="sse") _mm_cmpeq_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return cmpps(a, b, 0) } +@(enable_target_feature="sse") _mm_cmplt_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return cmpps(a, b, 1) } +@(enable_target_feature="sse") _mm_cmple_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return cmpps(a, b, 2) } +@(enable_target_feature="sse") _mm_cmpgt_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return cmpps(b, a, 1) } +@(enable_target_feature="sse") _mm_cmpge_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return cmpps(b, a, 2) } +@(enable_target_feature="sse") _mm_cmpneq_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return cmpps(a, b, 4) } +@(enable_target_feature="sse") _mm_cmpnlt_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return cmpps(a, b, 5) } +@(enable_target_feature="sse") _mm_cmpnle_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return cmpps(a, b, 6) } +@(enable_target_feature="sse") _mm_cmpngt_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return cmpps(b, a, 5) } +@(enable_target_feature="sse") _mm_cmpnge_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return cmpps(b, a, 6) } +@(enable_target_feature="sse") _mm_cmpord_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return cmpps(b, a, 7) } +@(enable_target_feature="sse") _mm_cmpunord_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return cmpps(b, a, 3) } +@(enable_target_feature="sse") _mm_comieq_ss :: #force_inline proc "c" (a, b: __m128) -> b32 { return comieq_ss(a, b) } +@(enable_target_feature="sse") _mm_comilt_ss :: #force_inline proc "c" (a, b: __m128) -> b32 { return comilt_ss(a, b) } +@(enable_target_feature="sse") _mm_comile_ss :: #force_inline proc "c" (a, b: __m128) -> b32 { return comile_ss(a, b) } +@(enable_target_feature="sse") _mm_comigt_ss :: #force_inline proc "c" (a, b: __m128) -> b32 { return comigt_ss(a, b) } +@(enable_target_feature="sse") _mm_comige_ss :: #force_inline proc "c" (a, b: __m128) -> b32 { return comige_ss(a, b) } +@(enable_target_feature="sse") _mm_comineq_ss :: #force_inline proc "c" (a, b: __m128) -> b32 { return comineq_ss(a, b) } +@(enable_target_feature="sse") _mm_ucomieq_ss :: #force_inline proc "c" (a, b: __m128) -> b32 { return ucomieq_ss(a, b) } +@(enable_target_feature="sse") _mm_ucomilt_ss :: #force_inline proc "c" (a, b: __m128) -> b32 { return ucomilt_ss(a, b) } +@(enable_target_feature="sse") _mm_ucomile_ss :: #force_inline proc "c" (a, b: __m128) -> b32 { return ucomile_ss(a, b) } +@(enable_target_feature="sse") _mm_ucomigt_ss :: #force_inline proc "c" (a, b: __m128) -> b32 { return ucomigt_ss(a, b) } +@(enable_target_feature="sse") _mm_ucomige_ss :: #force_inline proc "c" (a, b: __m128) -> b32 { return ucomige_ss(a, b) } +@(enable_target_feature="sse") _mm_ucomineq_ss :: #force_inline proc "c" (a, b: __m128) -> b32 { return ucomineq_ss(a, b) } +@(enable_target_feature="sse") _mm_cvtss_si32 :: #force_inline proc "c" (a: __m128) -> i32 { return cvtss2si(a) } _mm_cvt_ss2si :: _mm_cvtss_si32 _mm_cvttss_si32 :: _mm_cvtss_si32 +@(enable_target_feature="sse") _mm_cvtss_f32 :: #force_inline proc "c" (a: __m128) -> f32 { return simd.extract(a, 0) } +@(enable_target_feature="sse") _mm_cvtsi32_ss :: #force_inline proc "c" (a: __m128, b: i32) -> __m128 { return cvtsi2ss(a, b) } _mm_cvt_si2ss :: _mm_cvtsi32_ss +@(enable_target_feature="sse") _mm_set_ss :: #force_inline proc "c" (a: f32) -> __m128 { return __m128{a, 0, 0, 0} } +@(enable_target_feature="sse") _mm_set1_ps :: #force_inline proc "c" (a: f32) -> __m128 { return __m128(a) } _mm_set_ps1 :: _mm_set1_ps +@(enable_target_feature="sse") _mm_set_ps :: #force_inline proc "c" (a, b, c, d: f32) -> __m128 { return __m128{d, c, b, a} } +@(enable_target_feature="sse") _mm_setr_ps :: #force_inline proc "c" (a, b, c, d: f32) -> __m128 { return __m128{a, b, c, d} } +@(enable_target_feature="sse") _mm_setzero_ps :: #force_inline proc "c" () -> __m128 { return __m128{0, 0, 0, 0} } +@(enable_target_feature="sse") _mm_shuffle_ps :: #force_inline proc "c" (a, b: __m128, $MASK: u32) -> __m128 { return simd.shuffle( a, b, @@ -279,56 +346,69 @@ _mm_shuffle_ps :: #force_inline proc "c" (a, b: __m128, $MASK: u32) -> __m128 { } +@(enable_target_feature="sse") _mm_unpackhi_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return simd.shuffle(a, b, 2, 6, 3, 7) } +@(enable_target_feature="sse") _mm_unpacklo_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return simd.shuffle(a, b, 0, 4, 1, 5) } +@(enable_target_feature="sse") _mm_movehl_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return simd.shuffle(a, b, 6, 7, 2, 3) } +@(enable_target_feature="sse") _mm_movelh_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return simd.shuffle(a, b, 0, 1, 4, 5) } +@(enable_target_feature="sse") _mm_movemask_ps :: #force_inline proc "c" (a: __m128) -> u32 { return movmskps(a) } +@(enable_target_feature="sse") _mm_load_ss :: #force_inline proc "c" (p: ^f32) -> __m128 { return __m128{p^, 0, 0, 0} } +@(enable_target_feature="sse") _mm_load1_ps :: #force_inline proc "c" (p: ^f32) -> __m128 { a := p^ return __m128(a) } _mm_load_ps1 :: _mm_load1_ps +@(enable_target_feature="sse") _mm_load_ps :: #force_inline proc "c" (p: [^]f32) -> __m128 { return (^__m128)(p)^ } +@(enable_target_feature="sse") _mm_loadu_ps :: #force_inline proc "c" (p: [^]f32) -> __m128 { dst := _mm_undefined_ps() intrinsics.mem_copy_non_overlapping(&dst, p, size_of(__m128)) return dst } +@(enable_target_feature="sse") _mm_loadr_ps :: #force_inline proc "c" (p: [^]f32) -> __m128 { return simd.lanes_reverse(_mm_load_ps(p)) } +@(enable_target_feature="sse") _mm_loadu_si64 :: #force_inline proc "c" (mem_addr: rawptr) -> __m128i { a := intrinsics.unaligned_load((^i64)(mem_addr)) return __m128i{a, 0} } +@(enable_target_feature="sse") _mm_store_ss :: #force_inline proc "c" (p: ^f32, a: __m128) { p^ = simd.extract(a, 0) } +@(enable_target_feature="sse") _mm_store1_ps :: #force_inline proc "c" (p: [^]f32, a: __m128) { b := simd.swizzle(a, 0, 0, 0, 0) (^__m128)(p)^ = b @@ -336,71 +416,89 @@ _mm_store1_ps :: #force_inline proc "c" (p: [^]f32, a: __m128) { _mm_store_ps1 :: _mm_store1_ps +@(enable_target_feature="sse") _mm_store_ps :: #force_inline proc "c" (p: [^]f32, a: __m128) { (^__m128)(p)^ = a } +@(enable_target_feature="sse") _mm_storeu_ps :: #force_inline proc "c" (p: [^]f32, a: __m128) { b := a intrinsics.mem_copy_non_overlapping(p, &b, size_of(__m128)) } +@(enable_target_feature="sse") _mm_storer_ps :: #force_inline proc "c" (p: [^]f32, a: __m128) { (^__m128)(p)^ = simd.lanes_reverse(a) } +@(enable_target_feature="sse") _mm_move_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 { return simd.shuffle(a, b, 4, 1, 2, 3) } +@(enable_target_feature="sse") _mm_sfence :: #force_inline proc "c" () { sfence() } +@(enable_target_feature="sse") _mm_getcsr :: #force_inline proc "c" () -> (result: u32) { stmxcsr(&result) return result } +@(enable_target_feature="sse") _mm_setcsr :: #force_inline proc "c" (val: u32) { val := val ldmxcsr(&val) } +@(enable_target_feature="sse") _MM_GET_EXCEPTION_MASK :: #force_inline proc "c" () -> u32 { return _mm_getcsr() & _MM_MASK_MASK } +@(enable_target_feature="sse") _MM_GET_EXCEPTION_STATE :: #force_inline proc "c" () -> u32 { return _mm_getcsr() & _MM_EXCEPT_MASK } +@(enable_target_feature="sse") _MM_GET_FLUSH_ZERO_MODE :: #force_inline proc "c" () -> u32 { return _mm_getcsr() & _MM_FLUSH_ZERO_MASK } +@(enable_target_feature="sse") _MM_GET_ROUNDING_MODE :: #force_inline proc "c" () -> u32 { return _mm_getcsr() & _MM_ROUND_MASK } +@(enable_target_feature="sse") _MM_SET_EXCEPTION_MASK :: #force_inline proc "c" (x: u32) { _mm_setcsr((_mm_getcsr() &~ _MM_MASK_MASK) | x) } +@(enable_target_feature="sse") _MM_SET_EXCEPTION_STATE :: #force_inline proc "c" (x: u32) { _mm_setcsr((_mm_getcsr() &~ _MM_EXCEPT_MASK) | x) } +@(enable_target_feature="sse") _MM_SET_FLUSH_ZERO_MODE :: #force_inline proc "c" (x: u32) { _mm_setcsr((_mm_getcsr() &~ _MM_FLUSH_ZERO_MASK) | x) } +@(enable_target_feature="sse") _MM_SET_ROUNDING_MODE :: #force_inline proc "c" (x: u32) { _mm_setcsr((_mm_getcsr() &~ _MM_ROUND_MASK) | x) } +@(enable_target_feature="sse") _mm_prefetch :: #force_inline proc "c" (p: rawptr, $STRATEGY: u32) { prefetch(p, (STRATEGY>>2)&1, STRATEGY&3, 1) } +@(enable_target_feature="sse") _mm_undefined_ps :: #force_inline proc "c" () -> __m128 { return _mm_set1_ps(0) } +@(enable_target_feature="sse") _MM_TRANSPOSE4_PS :: #force_inline proc "c" (row0, row1, row2, row3: ^__m128) { tmp0 := _mm_unpacklo_ps(row0^, row1^) tmp1 := _mm_unpacklo_ps(row2^, row3^) @@ -413,17 +511,21 @@ _MM_TRANSPOSE4_PS :: #force_inline proc "c" (row0, row1, row2, row3: ^__m128) { row3^ = _mm_movelh_ps(tmp3, tmp1) } +@(enable_target_feature="sse") _mm_stream_ps :: #force_inline proc "c" (addr: [^]f32, a: __m128) { intrinsics.non_temporal_store((^__m128)(addr), a) } when ODIN_ARCH == .amd64 { + @(enable_target_feature="sse") _mm_cvtss_si64 :: #force_inline proc "c"(a: __m128) -> i64 { return cvtss2si64(a) } + @(enable_target_feature="sse") _mm_cvttss_si64 :: #force_inline proc "c"(a: __m128) -> i64 { return cvttss2si64(a) } + @(enable_target_feature="sse") _mm_cvtsi64_ss :: #force_inline proc "c"(a: __m128, b: i64) -> __m128 { return cvtsi642ss(a, b) } diff --git a/core/simd/x86/sse2.odin b/core/simd/x86/sse2.odin index cb2e61f46..d15df8120 100644 --- a/core/simd/x86/sse2.odin +++ b/core/simd/x86/sse2.odin @@ -4,103 +4,135 @@ package simd_x86 import "core:intrinsics" import "core:simd" +@(enable_target_feature="sse2") _mm_pause :: #force_inline proc "c" () { pause() } +@(enable_target_feature="sse2") _mm_clflush :: #force_inline proc "c" (p: rawptr) { clflush(p) } +@(enable_target_feature="sse2") _mm_lfence :: #force_inline proc "c" () { lfence() } +@(enable_target_feature="sse2") _mm_mfence :: #force_inline proc "c" () { mfence() } +@(enable_target_feature="sse2") _mm_add_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.add(transmute(i8x16)a, transmute(i8x16)b) } +@(enable_target_feature="sse2") _mm_add_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.add(transmute(i16x8)a, transmute(i16x8)b) } +@(enable_target_feature="sse2") _mm_add_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.add(transmute(i32x4)a, transmute(i32x4)b) } +@(enable_target_feature="sse2") _mm_add_epi64 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.add(transmute(i64x2)a, transmute(i64x2)b) } +@(enable_target_feature="sse2") _mm_adds_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.add_sat(transmute(i8x16)a, transmute(i8x16)b) } +@(enable_target_feature="sse2") _mm_adds_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.add_sat(transmute(i16x8)a, transmute(i16x8)b) } +@(enable_target_feature="sse2") _mm_adds_epu8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.add_sat(transmute(u8x16)a, transmute(u8x16)b) } +@(enable_target_feature="sse2") _mm_adds_epu16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.add_sat(transmute(u16x8)a, transmute(u16x8)b) } +@(enable_target_feature="sse2") _mm_avg_epu8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)pavgb(transmute(u8x16)a, transmute(u8x16)b) } +@(enable_target_feature="sse2") _mm_avg_epu16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)pavgw(transmute(u16x8)a, transmute(u16x8)b) } +@(enable_target_feature="sse2") _mm_madd_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)pmaddwd(transmute(i16x8)a, transmute(i16x8)b) } +@(enable_target_feature="sse2") _mm_max_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)pmaxsw(transmute(i16x8)a, transmute(i16x8)b) } +@(enable_target_feature="sse2") _mm_max_epu8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)pmaxub(transmute(u8x16)a, transmute(u8x16)b) } +@(enable_target_feature="sse2") _mm_min_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)pminsw(transmute(i16x8)a, transmute(i16x8)b) } +@(enable_target_feature="sse2") _mm_min_epu8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)pminub(transmute(u8x16)a, transmute(u8x16)b) } +@(enable_target_feature="sse2") _mm_mulhi_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)pmulhw(transmute(i16x8)a, transmute(i16x8)b) } +@(enable_target_feature="sse2") _mm_mulhi_epu16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)pmulhuw(transmute(u16x8)a, transmute(u16x8)b) } +@(enable_target_feature="sse2") _mm_mullo_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.mul(transmute(i16x8)a, transmute(i16x8)b) } +@(enable_target_feature="sse2") _mm_mul_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)pmuludq(transmute(u32x4)a, transmute(u32x4)b) } +@(enable_target_feature="sse2") _mm_sad_epu8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)psadbw(transmute(u8x16)a, transmute(u8x16)b) } +@(enable_target_feature="sse2") _mm_sub_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.sub(transmute(i8x16)a, transmute(i8x16)b) } +@(enable_target_feature="sse2") _mm_sub_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.sub(transmute(i16x8)a, transmute(i16x8)b) } +@(enable_target_feature="sse2") _mm_sub_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.sub(transmute(i32x4)a, transmute(i32x4)b) } +@(enable_target_feature="sse2") _mm_sub_epi64 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.sub(transmute(i64x2)a, transmute(i64x2)b) } +@(enable_target_feature="sse2") _mm_subs_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.sub_sat(transmute(i8x16)a, transmute(i8x16)b) } +@(enable_target_feature="sse2") _mm_subs_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.sub_sat(transmute(i16x8)a, transmute(i16x8)b) } +@(enable_target_feature="sse2") _mm_subs_epu8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.sub_sat(transmute(u8x16)a, transmute(u8x16)b) } +@(enable_target_feature="sse2") _mm_subs_epu16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.sub_sat(transmute(u16x8)a, transmute(u16x8)b) } @@ -108,6 +140,7 @@ _mm_subs_epu16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { @(private) +@(enable_target_feature="sse2") _mm_slli_si128_impl :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { shift :: IMM8 & 0xff @@ -134,6 +167,7 @@ _mm_slli_si128_impl :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128 } @(private) +@(enable_target_feature="sse2") _mm_srli_si128_impl :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { shift :: IMM8 return transmute(__m128i)simd.shuffle( @@ -159,203 +193,264 @@ _mm_srli_si128_impl :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128 } +@(enable_target_feature="sse2") _mm_slli_si128 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { return _mm_slli_si128_impl(a, IMM8) } +@(enable_target_feature="sse2") _mm_bslli_si128 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { return _mm_slli_si128_impl(a, IMM8) } +@(enable_target_feature="sse2") _mm_bsrli_si128 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { return _mm_srli_si128_impl(a, IMM8) } +@(enable_target_feature="sse2") _mm_slli_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { return transmute(__m128i)pslliw(transmute(i16x8)a, IMM8) } +@(enable_target_feature="sse2") _mm_sll_epi16 :: #force_inline proc "c" (a, count: __m128i) -> __m128i { return transmute(__m128i)psllw(transmute(i16x8)a, transmute(i16x8)count) } +@(enable_target_feature="sse2") _mm_slli_epi32 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { return transmute(__m128i)psllid(transmute(i32x4)a, IMM8) } +@(enable_target_feature="sse2") _mm_sll_epi32 :: #force_inline proc "c" (a, count: __m128i) -> __m128i { return transmute(__m128i)pslld(transmute(i32x4)a, transmute(i32x4)count) } +@(enable_target_feature="sse2") _mm_slli_epi64 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { return transmute(__m128i)pslliq(transmute(i64x2)a, IMM8) } +@(enable_target_feature="sse2") _mm_sll_epi64 :: #force_inline proc "c" (a, count: __m128i) -> __m128i { return transmute(__m128i)psllq(transmute(i64x2)a, transmute(i64x2)count) } +@(enable_target_feature="sse2") _mm_srai_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { return transmute(__m128i)psraiw(transmute(i16x8)a. IMM8) } +@(enable_target_feature="sse2") _mm_sra_epi16 :: #force_inline proc "c" (a, count: __m128i) -> __m128i { return transmute(__m128i)psraw(transmute(i16x8)a, transmute(i16x8)count) } +@(enable_target_feature="sse2") _mm_srai_epi32 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { return transmute(__m128i)psraid(transmute(i32x4)a, IMM8) } +@(enable_target_feature="sse2") _mm_sra_epi32 :: #force_inline proc "c" (a, count: __m128i) -> __m128i { return transmute(__m128i)psrad(transmute(i32x4)a, transmute(i32x4)count) } +@(enable_target_feature="sse2") _mm_srli_si128 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { return _mm_srli_si128_impl(a, IMM8) } +@(enable_target_feature="sse2") _mm_srli_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { return transmute(__m128i)psrliw(transmute(i16x8)a. IMM8) } +@(enable_target_feature="sse2") _mm_srl_epi16 :: #force_inline proc "c" (a, count: __m128i) -> __m128i { return transmute(__m128i)psrlw(transmute(i16x8)a, transmute(i16x8)count) } +@(enable_target_feature="sse2") _mm_srli_epi32 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { return transmute(__m128i)psrlid(transmute(i32x4)a, IMM8) } +@(enable_target_feature="sse2") _mm_srl_epi32 :: #force_inline proc "c" (a, count: __m128i) -> __m128i { return transmute(__m128i)psrld(transmute(i32x4)a, transmute(i32x4)count) } +@(enable_target_feature="sse2") _mm_srli_epi64 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { return transmute(__m128i)psrliq(transmute(i64x2)a, IMM8) } +@(enable_target_feature="sse2") _mm_srl_epi64 :: #force_inline proc "c" (a, count: __m128i) -> __m128i { return transmute(__m128i)psrlq(transmute(i64x2)a, transmute(i64x2)count) } +@(enable_target_feature="sse2") _mm_and_si128 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return simd.and(a, b) } +@(enable_target_feature="sse2") _mm_andnot_si128 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return simd.and_not(b, a) } +@(enable_target_feature="sse2") _mm_or_si128 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return simd.or(a, b) } +@(enable_target_feature="sse2") _mm_xor_si128 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return simd.xor(a, b) } +@(enable_target_feature="sse2") _mm_cmpeq_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.lanes_eq(transmute(i8x16)a, transmute(i8x16)b) } +@(enable_target_feature="sse2") _mm_cmpeq_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.lanes_eq(transmute(i16x8)a, transmute(i16x8)b) } +@(enable_target_feature="sse2") _mm_cmpeq_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.lanes_eq(transmute(i32x4)a, transmute(i32x4)b) } +@(enable_target_feature="sse2") _mm_cmpgt_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.lanes_gt(transmute(i8x16)a, transmute(i8x16)b) } +@(enable_target_feature="sse2") _mm_cmpgt_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.lanes_gt(transmute(i16x8)a, transmute(i16x8)b) } +@(enable_target_feature="sse2") _mm_cmpgt_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.lanes_gt(transmute(i32x4)a, transmute(i32x4)b) } +@(enable_target_feature="sse2") _mm_cmplt_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.lanes_lt(transmute(i8x16)a, transmute(i8x16)b) } +@(enable_target_feature="sse2") _mm_cmplt_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.lanes_lt(transmute(i16x8)a, transmute(i16x8)b) } +@(enable_target_feature="sse2") _mm_cmplt_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.lanes_lt(transmute(i32x4)a, transmute(i32x4)b) } +@(enable_target_feature="sse2") _mm_cvtepi32_pd :: #force_inline proc "c" (a: __m128i) -> __m128d { v := transmute(i32x4)a return cast(__m128d)simd.shuffle(v, v, 0, 1) } +@(enable_target_feature="sse2") _mm_cvtsi32_sd :: #force_inline proc "c" (a: __m128d, b: i32) -> __m128d { return simd.replace(a, 0, f64(b)) } +@(enable_target_feature="sse2") _mm_cvtepi32_ps :: #force_inline proc "c" (a: __m128i) -> __m128 { return cvtdq2ps(transmute(i32x4)a) } +@(enable_target_feature="sse2") _mm_cvtps_epi32 :: #force_inline proc "c" (a: __m128) -> __m128i { return transmute(__m128i)cvtps2dq(a) } +@(enable_target_feature="sse2") _mm_cvtsi32_si128 :: #force_inline proc "c" (a: i32) -> __m128i { return transmute(__m128i)i32x4{a, 0, 0, 0} } +@(enable_target_feature="sse2") _mm_cvtsi128_si32 :: #force_inline proc "c" (a: __m128i) -> i32 { return simd.extract(transmute(i32x4)a, 0) } +@(enable_target_feature="sse2") _mm_set_epi64x :: #force_inline proc "c" (e1, e0: i64) -> __m128i { return transmute(__m128i)i64x2{e0, e1} } +@(enable_target_feature="sse2") _mm_set_epi32 :: #force_inline proc "c" (e3, e2, e1, e0: i32) -> __m128i { return transmute(__m128i)i32x4{e0, e1, e2, e3} } +@(enable_target_feature="sse2") _mm_set_epi16 :: #force_inline proc "c" (e7, e6, e5, e4, e3, e2, e1, e0: i16) -> __m128i { return transmute(__m128i)i16x8{e0, e1, e2, e3, e4, e5, e6, e7} } +@(enable_target_feature="sse2") _mm_set_epi8 :: #force_inline proc "c" (e15, e14, e13, e12, e11, e10, e9, e8, e7, e6, e5, e4, e3, e2, e1, e0: i8) -> __m128i { return transmute(__m128i)i8x16{e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15} } +@(enable_target_feature="sse2") _mm_set1_epi64x :: #force_inline proc "c" (a: i64) -> __m128i { return _mm_set_epi64x(a, a) } +@(enable_target_feature="sse2") _mm_set1_epi32 :: #force_inline proc "c" (a: i32) -> __m128i { return _mm_set_epi32(a, a, a, a) } +@(enable_target_feature="sse2") _mm_set1_epi16 :: #force_inline proc "c" (a: i16) -> __m128i { return _mm_set_epi16(a, a, a, a, a, a, a, a) } +@(enable_target_feature="sse2") _mm_set1_epi8 :: #force_inline proc "c" (a: i8) -> __m128i { return _mm_set_epi8(a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a) } +@(enable_target_feature="sse2") _mm_setr_epi32 :: #force_inline proc "c" (e3, e2, e1, e0: i32) -> __m128i { return _mm_set_epi32(e0, e1, e2, e3) } +@(enable_target_feature="sse2") _mm_setr_epi16 :: #force_inline proc "c" (e7, e6, e5, e4, e3, e2, e1, e0: i16) -> __m128i { return _mm_set_epi16(e0, e1, e2, e3, e4, e5, e6, e7) } +@(enable_target_feature="sse2") _mm_setr_epi8 :: #force_inline proc "c" (e15, e14, e13, e12, e11, e10, e9, e8, e7, e6, e5, e4, e3, e2, e1, e0: i8) -> __m128i { return _mm_set_epi8(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15) } +@(enable_target_feature="sse2") _mm_setzero_si128 :: #force_inline proc "c" () -> __m128i { return _mm_set1_epi64x(0) } +@(enable_target_feature="sse2") _mm_loadl_epi64 :: #force_inline proc "c" (mem_addr: ^__m128i) -> __m128i { return _mm_set_epi64x(0, intrinsics.unaligned_load((^i64)(mem_addr))) } +@(enable_target_feature="sse2") _mm_load_si128 :: #force_inline proc "c" (mem_addr: ^__m128i) -> __m128i { return mem_addr^ } +@(enable_target_feature="sse2") _mm_loadu_si128 :: #force_inline proc "c" (mem_addr: ^__m128i) -> __m128i { dst := _mm_undefined_si128() intrinsics.mem_copy_non_overlapping(&dst, mem_addr, size_of(__m128i)) return dst } +@(enable_target_feature="sse2") _mm_maskmoveu_si128 :: #force_inline proc "c" (a, mask: __m128i, mem_addr: rawptr) { maskmovdqu(transmute(i8x16)a, transmute(i8x16)mask, mem_addr) } +@(enable_target_feature="sse2") _mm_store_si128 :: #force_inline proc "c" (mem_addr: ^__m128i, a: __m128i) { mem_addr^ = a } +@(enable_target_feature="sse2") _mm_storeu_si128 :: #force_inline proc "c" (mem_addr: ^__m128i, a: __m128i) { storeudq(mem_addr, a) } +@(enable_target_feature="sse2") _mm_storel_epi64 :: #force_inline proc "c" (mem_addr: ^__m128i, a: __m128i) { a := a intrinsics.mem_copy_non_overlapping(mem_addr, &a, 8) } +@(enable_target_feature="sse2") _mm_stream_si128 :: #force_inline proc "c" (mem_addr: ^__m128i, a: __m128i) { intrinsics.non_temporal_store(mem_addr, a) } +@(enable_target_feature="sse2") _mm_stream_si32 :: #force_inline proc "c" (mem_addr: ^i32, a: i32) { intrinsics.non_temporal_store(mem_addr, a) } +@(enable_target_feature="sse2") _mm_move_epi64 :: #force_inline proc "c" (a: __m128i) -> __m128i { zero := _mm_setzero_si128() return transmute(__m128i)simd.shuffle(transmute(i64x2)a, transmute(i64x2)zero, 0, 2) @@ -364,24 +459,31 @@ _mm_move_epi64 :: #force_inline proc "c" (a: __m128i) -> __m128i { +@(enable_target_feature="sse2") _mm_packs_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)packsswb(transmute(i16x8)a, transmute(i16x8)b) } +@(enable_target_feature="sse2") _mm_packs_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)packssdw(transmute(i32x4)a, transmute(i32x4)b) } +@(enable_target_feature="sse2") _mm_packus_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)packuswb(transmute(i16x8)a, transmute(i16x8)b) } +@(enable_target_feature="sse2") _mm_extract_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> i32 { return i32(simd.extract(transmute(u16x8)a, IMM8)) } +@(enable_target_feature="sse2") _mm_insert_epi16 :: #force_inline proc "c" (a: __m128i, i: i32, $IMM8: u32) -> __m128i { return i32(simd.replace(transmute(u16x8)a, IMM8, i16(i))) } +@(enable_target_feature="sse2") _mm_movemask_epi8 :: #force_inline proc "c" (a: __m128i) -> i32 { return pmovmskb(transmute(i8x16)a) } +@(enable_target_feature="sse2") _mm_shuffle_epi32 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { v := transmute(i32x4)a return transmute(__m128i)simd.shuffle( @@ -393,6 +495,7 @@ _mm_shuffle_epi32 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i (IMM8 >> 6) & 0b11, ) } +@(enable_target_feature="sse2") _mm_shufflehi_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { v := transmute(i16x8)a return transmute(__m128i)simd.shuffle( @@ -408,6 +511,7 @@ _mm_shufflehi_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128 ((IMM8 >> 6) & 0b11) + 4, ) } +@(enable_target_feature="sse2") _mm_shufflelo_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { v := transmute(i16x8)a return transmute(__m128i)simd.shuffle( @@ -423,6 +527,7 @@ _mm_shufflelo_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128 7, ) } +@(enable_target_feature="sse2") _mm_unpackhi_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.shuffle( transmute(i8x16)a, @@ -430,15 +535,19 @@ _mm_unpackhi_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { 8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31, ) } +@(enable_target_feature="sse2") _mm_unpackhi_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.shuffle(transmute(i16x8)a, transmute(i16x8)b, 4, 12, 5, 13, 6, 14, 7, 15) } +@(enable_target_feature="sse2") _mm_unpackhi_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.shuffle(transmute(i32x4)a, transmute(i32x4)b, 2, 6, 3, 7) } +@(enable_target_feature="sse2") _mm_unpackhi_epi64 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.shuffle(transmute(i64x2)a, transmute(i64x2)b, 1, 3) } +@(enable_target_feature="sse2") _mm_unpacklo_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.shuffle( transmute(i8x16)a, @@ -446,12 +555,15 @@ _mm_unpacklo_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { 0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23, ) } +@(enable_target_feature="sse2") _mm_unpacklo_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.shuffle(transmute(i16x8)a, transmute(i16x8)b, 0, 8, 1, 9, 2, 10, 3, 11) } +@(enable_target_feature="sse2") _mm_unpacklo_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.shuffle(transmute(i32x4)a, transmute(i32x4)b, 0, 4, 1, 5) } +@(enable_target_feature="sse2") _mm_unpacklo_epi64 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)simd.shuffle(transmute(i64x2)a, transmute(i64x2)b, 0, 2) } @@ -459,57 +571,75 @@ _mm_unpacklo_epi64 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { +@(enable_target_feature="sse2") _mm_add_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return simd.replace(a, 0, _mm_cvtsd_f64(a) + _mm_cvtsd_f64(b)) } +@(enable_target_feature="sse2") _mm_add_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return simd.add(a, b) } +@(enable_target_feature="sse2") _mm_div_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return simd.replace(a, 0, _mm_cvtsd_f64(a) / _mm_cvtsd_f64(b)) } +@(enable_target_feature="sse2") _mm_div_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return simd.div(a, b) } +@(enable_target_feature="sse2") _mm_max_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return maxsd(a, b) } +@(enable_target_feature="sse2") _mm_max_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return maxpd(a, b) } +@(enable_target_feature="sse2") _mm_min_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return minsd(a, b) } +@(enable_target_feature="sse2") _mm_min_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return minpd(a, b) } +@(enable_target_feature="sse2") _mm_mul_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return simd.replace(a, 0, _mm_cvtsd_f64(a) * _mm_cvtsd_f64(b)) } +@(enable_target_feature="sse2") _mm_mul_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return simd.mul(a, b) } +@(enable_target_feature="sse2") _mm_sqrt_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return simd.replace(a, 0, _mm_cvtsd_f64(sqrtsd(b))) } +@(enable_target_feature="sse2") _mm_sqrt_pd :: #force_inline proc "c" (a: __m128d) -> __m128d { return simd.sqrt(a) } +@(enable_target_feature="sse2") _mm_sub_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return simd.replace(a, 0, _mm_cvtsd_f64(a) - _mm_cvtsd_f64(b)) } +@(enable_target_feature="sse2") _mm_sub_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return simd.sub(a, b) } +@(enable_target_feature="sse2") _mm_and_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return transmute(__m128d)_mm_and_si128(transmute(__m128i)a, transmute(__m128i)b) } +@(enable_target_feature="sse2") _mm_andnot_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return transmute(__m128d)_mm_andnot_si128(transmute(__m128i)a, transmute(__m128i)b) } +@(enable_target_feature="sse2") _mm_or_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return transmute(__m128d)_mm_or_si128(transmute(__m128i)a, transmute(__m128i)b) } +@(enable_target_feature="sse2") _mm_xor_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return transmute(__m128d)_mm_xor_si128(transmute(__m128i)a, transmute(__m128i)b) } @@ -517,111 +647,147 @@ _mm_xor_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { +@(enable_target_feature="sse2") _mm_cmpeq_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return cmpsd(a, b, 0) } +@(enable_target_feature="sse2") _mm_cmplt_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return cmpsd(a, b, 1) } +@(enable_target_feature="sse2") _mm_cmple_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return cmpsd(a, b, 2) } +@(enable_target_feature="sse2") _mm_cmpgt_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return simd.replace(_mm_cmplt_sd(b, a), 1, simd.extract(a, 1)) } +@(enable_target_feature="sse2") _mm_cmpge_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return simd.replace(_mm_cmple_sd(b, a), 1, simd.extract(a, 1)) } +@(enable_target_feature="sse2") _mm_cmpord_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return cmpsd(a, b, 7) } +@(enable_target_feature="sse2") _mm_cmpunord_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return cmpsd(a, b, 3) } +@(enable_target_feature="sse2") _mm_cmpneq_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return cmpsd(a, b, 4) } +@(enable_target_feature="sse2") _mm_cmpnlt_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return cmpsd(a, b, 5) } +@(enable_target_feature="sse2") _mm_cmpnle_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return cmpsd(a, b, 6) } +@(enable_target_feature="sse2") _mm_cmpngt_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return simd.replace(_mm_cmpnlt_sd(b, a), 1, simd.extract(a, 1)) } +@(enable_target_feature="sse2") _mm_cmpnge_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return simd.replace(_mm_cmpnle_sd(b, a), 1, simd.extract(a, 1)) } +@(enable_target_feature="sse2") _mm_cmpeq_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return cmppd(a, b, 0) } +@(enable_target_feature="sse2") _mm_cmplt_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return cmppd(a, b, 1) } +@(enable_target_feature="sse2") _mm_cmple_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return cmppd(a, b, 2) } +@(enable_target_feature="sse2") _mm_cmpgt_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return _mm_cmplt_pd(b, a) } +@(enable_target_feature="sse2") _mm_cmpge_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return _mm_cmple_pd(b, a) } +@(enable_target_feature="sse2") _mm_cmpord_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return cmppd(a, b, 7) } +@(enable_target_feature="sse2") _mm_cmpunord_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return cmppd(a, b, 3) } +@(enable_target_feature="sse2") _mm_cmpneq_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return cmppd(a, b, 4) } +@(enable_target_feature="sse2") _mm_cmpnlt_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return cmppd(a, b, 5) } +@(enable_target_feature="sse2") _mm_cmpnle_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return cmppd(a, b, 6) } +@(enable_target_feature="sse2") _mm_cmpngt_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return _mm_cmpnlt_pd(b, a) } +@(enable_target_feature="sse2") _mm_cmpnge_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return _mm_cmpnle_pd(b, a) } +@(enable_target_feature="sse2") _mm_comieq_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 { return comieqsd(a, b) } +@(enable_target_feature="sse2") _mm_comilt_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 { return comiltsd(a, b) } +@(enable_target_feature="sse2") _mm_comile_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 { return comilesd(a, b) } +@(enable_target_feature="sse2") _mm_comigt_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 { return comigtsd(a, b) } +@(enable_target_feature="sse2") _mm_comige_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 { return comigesd(a, b) } +@(enable_target_feature="sse2") _mm_comineq_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 { return comineqsd(a, b) } +@(enable_target_feature="sse2") _mm_ucomieq_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 { return ucomieqsd(a, b) } +@(enable_target_feature="sse2") _mm_ucomilt_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 { return ucomiltsd(a, b) } +@(enable_target_feature="sse2") _mm_ucomile_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 { return ucomilesd(a, b) } +@(enable_target_feature="sse2") _mm_ucomigt_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 { return ucomigtsd(a, b) } +@(enable_target_feature="sse2") _mm_ucomige_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 { return ucomigesd(a, b) } +@(enable_target_feature="sse2") _mm_ucomineq_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 { return ucomineqsd(a, b) } @@ -630,115 +796,151 @@ _mm_ucomineq_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 { +@(enable_target_feature="sse2") _mm_cvtpd_ps :: #force_inline proc "c" (a: __m128d) -> __m128 { return cvtpd2ps(a) } +@(enable_target_feature="sse2") _mm_cvtps_pd :: #force_inline proc "c" (a: __m128) -> __m128d { return cvtps2pd(a) } +@(enable_target_feature="sse2") _mm_cvtpd_epi32 :: #force_inline proc "c" (a: __m128d) -> __m128i { return transmute(__m128i)cvtpd2dq(a) } +@(enable_target_feature="sse2") _mm_cvtsd_si32 :: #force_inline proc "c" (a: __m128d) -> i32 { return cvtsd2si(a) } +@(enable_target_feature="sse2") _mm_cvtsd_ss :: #force_inline proc "c" (a, b: __m128d) -> __m128 { return cvtsd2ss(a, b) } +@(enable_target_feature="sse2") _mm_cvtsd_f64 :: #force_inline proc "c" (a: __m128d) -> f64 { return simd.extract(a, 0) } +@(enable_target_feature="sse2") _mm_cvtss_sd :: #force_inline proc "c" (a, b: __m128) -> __m128d { return cvtss2sd(a, b) } +@(enable_target_feature="sse2") _mm_cvttpd_epi32 :: #force_inline proc "c" (a: __m128d) -> __m128i { return transmute(__m128i)cvttpd2dq(a) } +@(enable_target_feature="sse2") _mm_cvttsd_si32 :: #force_inline proc "c" (a: __m128d) -> i32 { return cvttsd2si(a) } +@(enable_target_feature="sse2") _mm_cvttps_epi32 :: #force_inline proc "c" (a: __m128) -> __m128i { return transmute(__m128i)cvttps2dq(a) } +@(enable_target_feature="sse2") _mm_set_sd :: #force_inline proc "c" (a: f64) -> __m128d { return _mm_set_pd(0.0, a) } +@(enable_target_feature="sse2") _mm_set1_pd :: #force_inline proc "c" (a: f64) -> __m128d { return _mm_set_pd(a, a) } +@(enable_target_feature="sse2") _mm_set_pd1 :: #force_inline proc "c" (a: f64) -> __m128d { return _mm_set_pd(a, a) } +@(enable_target_feature="sse2") _mm_set_pd :: #force_inline proc "c" (a: f64, b: f64) -> __m128d { return __m128d{b, a} } +@(enable_target_feature="sse2") _mm_setr_pd :: #force_inline proc "c" (a: f64, b: f64) -> __m128d { return _mm_set_pd(b, a) } +@(enable_target_feature="sse2") _mm_setzero_pd :: #force_inline proc "c" () -> __m128d { return _mm_set_pd(0.0, 0.0) } +@(enable_target_feature="sse2") _mm_movemask_pd :: #force_inline proc "c" (a: __m128d) -> i32 { return movmskpd(a) } +@(enable_target_feature="sse2") _mm_load_pd :: #force_inline proc "c" (mem_addr: ^f64) -> __m128d { return (^__m128d)(mem_addr)^ } +@(enable_target_feature="sse2") _mm_load_sd :: #force_inline proc "c" (mem_addr: ^f64) -> __m128d { return _mm_setr_pd(mem_addr^, 0.) } +@(enable_target_feature="sse2") _mm_loadh_pd :: #force_inline proc "c" (a: __m128d, mem_addr: ^f64) -> __m128d { return _mm_setr_pd(simd.extract(a, 0), mem_addr^) } +@(enable_target_feature="sse2") _mm_loadl_pd :: #force_inline proc "c" (a: __m128d, mem_addr: ^f64) -> __m128d { return _mm_setr_pd(mem_addr^, simd.extract(a, 1)) } +@(enable_target_feature="sse2") _mm_stream_pd :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) { intrinsics.non_temporal_store((^__m128d)(mem_addr), a) } +@(enable_target_feature="sse2") _mm_store_sd :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) { mem_addr^ = simd.extract(a, 0) } +@(enable_target_feature="sse2") _mm_store_pd :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) { (^__m128d)(mem_addr)^ = a } +@(enable_target_feature="sse2") _mm_storeu_pd :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) { storeupd(mem_addr, a) } +@(enable_target_feature="sse2") _mm_store1_pd :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) { (^__m128d)(mem_addr)^ = simd.shuffle(a, a, 0, 0) } +@(enable_target_feature="sse2") _mm_store_pd1 :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) { (^__m128d)(mem_addr)^ = simd.shuffle(a, a, 0, 0) } +@(enable_target_feature="sse2") _mm_storer_pd :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) { (^__m128d)(mem_addr)^ = simd.shuffle(a, a, 1, 0) } +@(enable_target_feature="sse2") _mm_storeh_pd :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) { mem_addr^ = simd.extract(a, 1) } +@(enable_target_feature="sse2") _mm_storel_pd :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) { mem_addr^ = simd.extract(a, 0) } +@(enable_target_feature="sse2") _mm_load1_pd :: #force_inline proc "c" (mem_addr: ^f64) -> __m128d { d := mem_addr^ return _mm_setr_pd(d, d) } +@(enable_target_feature="sse2") _mm_load_pd1 :: #force_inline proc "c" (mem_addr: ^f64) -> __m128d { return _mm_load1_pd(mem_addr) } +@(enable_target_feature="sse2") _mm_loadr_pd :: #force_inline proc "c" (mem_addr: ^f64) -> __m128d { a := _mm_load_pd(mem_addr) return simd.shuffle(a, a, 1, 0) } +@(enable_target_feature="sse2") _mm_loadu_pd :: #force_inline proc "c" (mem_addr: ^f64) -> __m128d { dst := _mm_undefined_pd() intrinsics.mem_copy_non_overlapping(&dst, mem_addr, size_of(__m128d)) return dst } +@(enable_target_feature="sse2") _mm_shuffle_pd :: #force_inline proc "c" (a, b: __m128d, $MASK: u32) -> __m128d { return simd.shuffle(a, b, MASK&0b1, ((MASK>>1)&0b1) + 2) } +@(enable_target_feature="sse2") _mm_move_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return _mm_setr_pd(simd.extract(b, 0), simd.extract(a, 1)) } @@ -746,71 +948,92 @@ _mm_move_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { +@(enable_target_feature="sse2") _mm_castpd_ps :: #force_inline proc "c" (a: __m128d) -> __m128 { return transmute(__m128)a } +@(enable_target_feature="sse2") _mm_castpd_si128 :: #force_inline proc "c" (a: __m128d) -> __m128i { return transmute(__m128i)a } +@(enable_target_feature="sse2") _mm_castps_pd :: #force_inline proc "c" (a: __m128) -> __m128d { return transmute(__m128d)a } +@(enable_target_feature="sse2") _mm_castps_si128 :: #force_inline proc "c" (a: __m128) -> __m128i { return transmute(__m128i)a } +@(enable_target_feature="sse2") _mm_castsi128_pd :: #force_inline proc "c" (a: __m128i) -> __m128d { return transmute(__m128d)a } +@(enable_target_feature="sse2") _mm_castsi128_ps :: #force_inline proc "c" (a: __m128i) -> __m128 { return transmute(__m128)a } +@(enable_target_feature="sse2") _mm_undefined_pd :: #force_inline proc "c" () -> __m128d { return __m128d{0, 0} } +@(enable_target_feature="sse2") _mm_undefined_si128 :: #force_inline proc "c" () -> __m128i { return __m128i{0, 0} } +@(enable_target_feature="sse2") _mm_unpackhi_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return simd.shuffle(a, b, 1, 3) } +@(enable_target_feature="sse2") _mm_unpacklo_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d { return simd.shuffle(a, b, 0, 2) } when ODIN_ARCH == .amd64 { + @(enable_target_feature="sse2") _mm_cvtsd_si64 :: #force_inline proc "c" (a: __m128d) -> i64 { return cvtsd2si64(a) } + @(enable_target_feature="sse2") _mm_cvtsd_si64x :: #force_inline proc "c" (a: __m128d) -> i64 { return _mm_cvtsd_si64(a) } + @(enable_target_feature="sse2") _mm_cvttsd_si64 :: #force_inline proc "c" (a: __m128d) -> i64 { return cvttsd2si64(a) } + @(enable_target_feature="sse2") _mm_cvttsd_si64x :: #force_inline proc "c" (a: __m128d) -> i64 { return _mm_cvttsd_si64(a) } + @(enable_target_feature="sse2") _mm_stream_si64 :: #force_inline proc "c" (mem_addr: ^i64, a: i64) { intrinsics.non_temporal_store(mem_addr, a) } + @(enable_target_feature="sse2") _mm_cvtsi64_si128 :: #force_inline proc "c" (a: i64) -> __m128i { return _mm_set_epi64x(0, a) } + @(enable_target_feature="sse2") _mm_cvtsi64x_si128 :: #force_inline proc "c" (a: i64) -> __m128i { return _mm_cvtsi64_si128(a) } + @(enable_target_feature="sse2") _mm_cvtsi128_si64 :: #force_inline proc "c" (a: __m128i) -> i64 { return simd.extract(transmute(i64x2)a, 0) } + @(enable_target_feature="sse2") _mm_cvtsi128_si64x :: #force_inline proc "c" (a: __m128i) -> i64 { return _mm_cvtsi128_si64(a) } + @(enable_target_feature="sse2") _mm_cvtsi64_sd :: #force_inline proc "c" (a: __m128d, b: i64) -> __m128d { return simd.replace(a, 0, f64(b)) } + @(enable_target_feature="sse2") _mm_cvtsi64x_sd :: #force_inline proc "c" (a: __m128d, b: i64) -> __m128d { return _mm_cvtsi64_sd(a, b) } diff --git a/core/simd/x86/sse3.odin b/core/simd/x86/sse3.odin index 9766a43e6..370bfa952 100644 --- a/core/simd/x86/sse3.odin +++ b/core/simd/x86/sse3.odin @@ -4,36 +4,47 @@ package simd_x86 import "core:intrinsics" import "core:simd" +@(enable_target_feature="sse3") _mm_addsub_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return addsubps(a, b) } +@(enable_target_feature="sse3") _mm_addsub_pd :: #force_inline proc "c" (a: __m128d, b: __m128d) -> __m128d { return addsubpd(a, b) } +@(enable_target_feature="sse3") _mm_hadd_pd :: #force_inline proc "c" (a: __m128d, b: __m128d) -> __m128d { return haddpd(a, b) } +@(enable_target_feature="sse3") _mm_hadd_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return haddps(a, b) } +@(enable_target_feature="sse3") _mm_hsub_pd :: #force_inline proc "c" (a: __m128d, b: __m128d) -> __m128d { return hsubpd(a, b) } +@(enable_target_feature="sse3") _mm_hsub_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 { return hsubps(a, b) } +@(enable_target_feature="sse3") _mm_lddqu_si128 :: #force_inline proc "c" (mem_addr: ^__m128i) -> __m128i { return transmute(__m128i)lddqu(mem_addr) } +@(enable_target_feature="sse3") _mm_movedup_pd :: #force_inline proc "c" (a: __m128d) -> __m128d { return simd.shuffle(a, a, 0, 0) } +@(enable_target_feature="sse3") _mm_loaddup_pd :: #force_inline proc "c" (mem_addr: [^]f64) -> __m128d { return _mm_load1_pd(mem_addr) } +@(enable_target_feature="sse3") _mm_movehdup_ps :: #force_inline proc "c" (a: __m128) -> __m128 { return simd.shuffle(a, a, 1, 1, 3, 3) } +@(enable_target_feature="sse3") _mm_moveldup_ps :: #force_inline proc "c" (a: __m128) -> __m128 { return simd.shuffle(a, a, 0, 0, 2, 2) } diff --git a/core/simd/x86/ssse3.odin b/core/simd/x86/ssse3.odin index 6c6f28008..8c677aed4 100644 --- a/core/simd/x86/ssse3.odin +++ b/core/simd/x86/ssse3.odin @@ -5,18 +5,23 @@ import "core:intrinsics" import "core:simd" _ :: simd +@(enable_target_feature="ssse3") _mm_abs_epi8 :: #force_inline proc "c" (a: __m128i) -> __m128i { return transmute(__m128i)pabsb128(transmute(i8x16)a) } +@(enable_target_feature="ssse3") _mm_abs_epi16 :: #force_inline proc "c" (a: __m128i) -> __m128i { return transmute(__m128i)pabsw128(transmute(i16x8)a) } +@(enable_target_feature="ssse3") _mm_abs_epi32 :: #force_inline proc "c" (a: __m128i) -> __m128i { return transmute(__m128i)pabsd128(transmute(i32x4)a) } +@(enable_target_feature="ssse3") _mm_shuffle_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)pshufb128(transmute(u8x16)a, transmute(u8x16)b) } +@(enable_target_feature="ssse3") _mm_alignr_epi8 :: #force_inline proc "c" (a, b: __m128i, $IMM8: u32) -> __m128i { shift :: IMM8 @@ -53,36 +58,47 @@ _mm_alignr_epi8 :: #force_inline proc "c" (a, b: __m128i, $IMM8: u32) -> __m128i } +@(enable_target_feature="ssse3") _mm_hadd_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)phaddw128(transmute(i16x8)a, transmute(i16x8)b) } +@(enable_target_feature="ssse3") _mm_hadds_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)phaddsw128(transmute(i16x8)a, transmute(i16x8)b) } +@(enable_target_feature="ssse3") _mm_hadd_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)phaddd128(transmute(i32x4)a, transmute(i32x4)b) } +@(enable_target_feature="ssse3") _mm_hsub_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)phsubw128(transmute(i16x8)a, transmute(i16x8)b) } +@(enable_target_feature="ssse3") _mm_hsubs_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)phsubsw128(transmute(i16x8)a, transmute(i16x8)b) } +@(enable_target_feature="ssse3") _mm_hsub_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)phsubd128(transmute(i32x4)a, transmute(i32x4)b) } +@(enable_target_feature="ssse3") _mm_maddubs_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)pmaddubsw128(transmute(u8x16)a, transmute(i8x16)b) } +@(enable_target_feature="ssse3") _mm_mulhrs_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)pmulhrsw128(transmute(i16x8)a, transmute(i16x8)b) } +@(enable_target_feature="ssse3") _mm_sign_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)psignb128(transmute(i8x16)a, transmute(i8x16)b) } +@(enable_target_feature="ssse3") _mm_sign_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)psignw128(transmute(i16x8)a, transmute(i16x8)b) } +@(enable_target_feature="ssse3") _mm_sign_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i { return transmute(__m128i)psignd128(transmute(i32x4)a, transmute(i32x4)b) } diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 27e09a0db..65470749f 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -256,7 +256,6 @@ struct BuildContext { String extra_linker_flags; String extra_assembler_flags; String microarch; - String target_features; BuildModeKind build_mode; bool generate_docs; i32 optimization_level; @@ -320,6 +319,10 @@ struct BuildContext { PtrMap defined_values; + BlockingMutex target_features_mutex; + StringSet target_features_set; + String target_features_string; + }; gb_global BuildContext build_context = {0}; @@ -1197,6 +1200,100 @@ void init_build_context(TargetMetrics *cross_target) { #include "microsoft_craziness.h" #endif + +Array split_by_comma(String const &list) { + isize n = 1; + for (isize i = 0; i < list.len; i++) { + if (list.text[i] == ',') { + n++; + } + } + auto res = array_make(heap_allocator(), n); + + String s = list; + for (isize i = 0; i < n; i++) { + isize m = string_index_byte(s, ','); + if (m < 0) { + res[i] = s; + break; + } + res[i] = substring(s, 0, m); + s = substring(s, m+1, s.len); + } + return res; +} + +bool check_target_feature_is_valid(TokenPos pos, String const &feature) { + // TODO(bill): check_target_feature_is_valid + return true; +} + +bool check_target_feature_is_enabled(TokenPos pos, String const &target_feature_list) { + BuildContext *bc = &build_context; + mutex_lock(&bc->target_features_mutex); + defer (mutex_unlock(&bc->target_features_mutex)); + + auto items = split_by_comma(target_feature_list); + array_free(&items); + for_array(i, items) { + String const &item = items.data[i]; + if (!check_target_feature_is_valid(pos, item)) { + error(pos, "Target feature '%.*s' is not valid", LIT(item)); + return false; + } + if (!string_set_exists(&bc->target_features_set, item)) { + error(pos, "Target feature '%.*s' is not enabled", LIT(item)); + return false; + } + } + + return true; +} + +void enable_target_feature(TokenPos pos, String const &target_feature_list) { + BuildContext *bc = &build_context; + mutex_lock(&bc->target_features_mutex); + defer (mutex_unlock(&bc->target_features_mutex)); + + auto items = split_by_comma(target_feature_list); + array_free(&items); + for_array(i, items) { + String const &item = items.data[i]; + if (!check_target_feature_is_valid(pos, item)) { + error(pos, "Target feature '%.*s' is not valid", LIT(item)); + } + } +} + + +char const *target_features_set_to_cstring(gbAllocator allocator, bool with_quotes) { + isize len = 0; + for_array(i, build_context.target_features_set.entries) { + if (i != 0) { + len += 1; + } + String feature = build_context.target_features_set.entries[i].value; + len += feature.len; + if (with_quotes) len += 2; + } + char *features = gb_alloc_array(allocator, char, len+1); + len = 0; + for_array(i, build_context.target_features_set.entries) { + if (i != 0) { + features[len++] = ','; + } + + if (with_quotes) features[len++] = '"'; + String feature = build_context.target_features_set.entries[i].value; + gb_memmove(features, feature.text, feature.len); + len += feature.len; + if (with_quotes) features[len++] = '"'; + } + features[len++] = 0; + + return features; +} + // NOTE(Jeroen): Set/create the output and other paths and report an error as appropriate. // We've previously called `parse_build_flags`, so `out_filepath` should be set. bool init_build_paths(String init_filename) { @@ -1206,6 +1303,9 @@ bool init_build_paths(String init_filename) { // NOTE(Jeroen): We're pre-allocating BuildPathCOUNT slots so that certain paths are always at the same enumerated index. array_init(&bc->build_paths, permanent_allocator(), BuildPathCOUNT); + string_set_init(&bc->target_features_set, heap_allocator(), 1024); + mutex_init(&bc->target_features_mutex); + // [BuildPathMainPackage] Turn given init path into a `Path`, which includes normalizing it into a full path. bc->build_paths[BuildPath_Main_Package] = path_from_string(ha, init_filename); @@ -1382,5 +1482,10 @@ bool init_build_paths(String init_filename) { return false; } + if (bc->target_features_string.len != 0) { + enable_target_feature({}, bc->target_features_string); + } + return true; -} \ No newline at end of file +} + diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 62a1e2555..d4818892b 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -899,6 +899,18 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } } + if (ac.require_target_feature.len != 0 && ac.enable_target_feature.len != 0) { + error(e->token, "Attributes @(require_target_feature=...) and @(enable_target_feature=...) cannot be used together"); + } else if (ac.require_target_feature.len != 0) { + if (check_target_feature_is_enabled(e->token.pos, ac.require_target_feature)) { + e->Procedure.target_feature = ac.require_target_feature; + } else { + e->Procedure.target_feature_disabled = true; + } + } else if (ac.enable_target_feature.len != 0) { + enable_target_feature(e->token.pos, ac.enable_target_feature); + e->Procedure.target_feature = ac.enable_target_feature; + } switch (e->Procedure.optimization_mode) { case ProcedureOptimizationMode_None: diff --git a/src/checker.cpp b/src/checker.cpp index 8afc6eb14..874839ece 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3207,6 +3207,22 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) { } } return true; + } else if (name == "require_target_feature") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_String) { + ac->require_target_feature = ev.value_string; + } else { + error(elem, "Expected a string value for '%.*s'", LIT(name)); + } + return true; + } else if (name == "enable_target_feature") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_String) { + ac->enable_target_feature = ev.value_string; + } else { + error(elem, "Expected a string value for '%.*s'", LIT(name)); + } + return true; } return false; } diff --git a/src/checker.hpp b/src/checker.hpp index 1c9ffd8c7..8fc9e54c8 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -124,6 +124,9 @@ struct AttributeContext { String objc_name; bool objc_is_class_method; Type * objc_type; + + String require_target_feature; // required by the target micro-architecture + String enable_target_feature; // will be enabled for the procedure only }; AttributeContext make_attribute_context(String link_prefix) { diff --git a/src/entity.cpp b/src/entity.cpp index 904a630fb..76e6912b9 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -233,10 +233,12 @@ struct Entity { String link_name; String link_prefix; DeferredProcedure deferred_procedure; - bool is_foreign; - bool is_export; - bool generated_from_polymorphic; ProcedureOptimizationMode optimization_mode; + bool is_foreign : 1; + bool is_export : 1; + bool generated_from_polymorphic : 1; + bool target_feature_disabled : 1; + String target_feature; } Procedure; struct { Array entities; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 7cf588853..cf7389ec1 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1332,8 +1332,8 @@ void lb_generate_code(lbGenerator *gen) { } } - if (build_context.target_features.len != 0) { - llvm_features = alloc_cstring(permanent_allocator(), build_context.target_features); + if (build_context.target_features_set.entries.count != 0) { + llvm_features = target_features_set_to_cstring(permanent_allocator(), false); } // GB_ASSERT_MSG(LLVMTargetHasAsmBackend(target)); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 1e3591bf1..296a7fa6b 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -169,6 +169,19 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) } } + if (!entity->Procedure.target_feature_disabled && + entity->Procedure.target_feature.len != 0) { + auto features = split_by_comma(entity->Procedure.target_feature); + for_array(i, features) { + String feature = features[i]; + LLVMAttributeRef ref = LLVMCreateStringAttribute( + m->ctx, + cast(char const *)feature.text, cast(unsigned)feature.len, + "", 0); + LLVMAddAttributeAtIndex(p->value, LLVMAttributeIndex_FunctionIndex, ref); + } + } + if (entity->flags & EntityFlag_Cold) { lb_add_attribute_to_proc(m, p->value, "cold"); } diff --git a/src/main.cpp b/src/main.cpp index 13c8bd74d..ee71b91df 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1376,8 +1376,8 @@ bool parse_build_flags(Array args) { } case BuildFlag_TargetFeatures: { GB_ASSERT(value.kind == ExactValue_String); - build_context.target_features = value.value_string; - string_to_lower(&build_context.target_features); + build_context.target_features_string = value.value_string; + string_to_lower(&build_context.target_features_string); break; } case BuildFlag_RelocMode: { diff --git a/src/string.cpp b/src/string.cpp index 616761265..44eccd2d2 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -157,6 +157,15 @@ int string_compare(String const &x, String const &y) { return 0; } +isize string_index_byte(String const &s, u8 x) { + for (isize i = 0; i < s.len; i++) { + if (s.text[i] == x) { + return i; + } + } + return -1; +} + GB_COMPARE_PROC(string_cmp_proc) { String x = *(String *)a; String y = *(String *)b; -- cgit v1.2.3 From a0babefe55568c400835a5984db676d9f650a8b4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 30 May 2022 15:13:45 +0100 Subject: Fix lb_build_builtin_simd_proc --- src/llvm_backend_proc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 296a7fa6b..e3ffcaef2 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1021,8 +1021,8 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const res.type = tv.type; lbValue arg0 = {}; if (ce->args.count > 0) arg0 = lb_build_expr(p, ce->args[0]); - lbValue arg1 = {}; if (ce->args.count > 1) arg0 = lb_build_expr(p, ce->args[1]); - lbValue arg2 = {}; if (ce->args.count > 2) arg0 = lb_build_expr(p, ce->args[2]); + lbValue arg1 = {}; if (ce->args.count > 1) arg1 = lb_build_expr(p, ce->args[1]); + lbValue arg2 = {}; if (ce->args.count > 2) arg2 = lb_build_expr(p, ce->args[2]); Type *elem = base_array_type(arg0.type); -- cgit v1.2.3 From 516f6647b46c69a67139154c02c74b436cd4b999 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 31 May 2022 11:00:41 +0100 Subject: Fix intrinsics.non_temporal_{load, store} --- src/llvm_backend_proc.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index e3ffcaef2..75ca77641 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -2117,10 +2117,14 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, LLVMValueRef instr = LLVMBuildStore(p->builder, val.value, dst.value); switch (id) { - case BuiltinProc_volatile_store: LLVMSetVolatile(instr, true); break; case BuiltinProc_non_temporal_store: - // TODO(bill): BuiltinProc_non_temporal_store + { + unsigned kind_id = LLVMGetMDKindIDInContext(p->module->ctx, "nontemporal", 11); + LLVMMetadataRef node = LLVMValueAsMetadata(LLVMConstInt(lb_type(p->module, t_u32), 1, false)); + LLVMSetMetadata(instr, kind_id, LLVMMetadataAsValue(p->module->ctx, node)); + } 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; } @@ -2138,10 +2142,15 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, LLVMValueRef instr = LLVMBuildLoad(p->builder, dst.value, ""); switch (id) { - case BuiltinProc_volatile_load: LLVMSetVolatile(instr, true); break; case BuiltinProc_non_temporal_load: - // TODO(bill): BuiltinProc_non_temporal_load + { + unsigned kind_id = LLVMGetMDKindIDInContext(p->module->ctx, "nontemporal", 11); + LLVMMetadataRef node = LLVMValueAsMetadata(LLVMConstInt(lb_type(p->module, t_u32), 1, false)); + LLVMSetMetadata(instr, kind_id, LLVMMetadataAsValue(p->module->ctx, node)); + } break; + 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; } -- cgit v1.2.3 From 041625381cfd6236de5bc5fea3a398123b7328ed Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 16 Jul 2022 17:36:03 +0100 Subject: Fix #1888 --- src/check_expr.cpp | 8 ++++++-- src/llvm_backend.hpp | 2 ++ src/llvm_backend_expr.cpp | 38 ++++++++++++++++++++++++++++---------- src/llvm_backend_proc.cpp | 5 +---- src/parser.cpp | 1 + src/parser.hpp | 4 +++- 6 files changed, 41 insertions(+), 17 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b42301cd6..13d6badbe 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8643,6 +8643,8 @@ ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast *node, Type Ast *first_arg = x.expr->SelectorExpr.expr; GB_ASSERT(first_arg != nullptr); + first_arg->state_flags |= StateFlag_SelectorCallExpr; + Type *pt = base_type(x.type); GB_ASSERT(pt->kind == Type_Proc); Type *first_type = nullptr; @@ -8668,6 +8670,7 @@ ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast *node, Type y.mode = first_arg->tav.mode; y.type = first_arg->tav.type; y.value = first_arg->tav.value; + if (check_is_assignable_to(c, &y, first_type)) { // Do nothing, it's valid } else { @@ -10034,9 +10037,10 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) { str = write_expr_to_string(str, ce->proc, shorthand); str = gb_string_appendc(str, "("); - for_array(i, ce->args) { + isize idx0 = cast(isize)ce->was_selector; + for (isize i = idx0; i < ce->args.count; i++) { Ast *arg = ce->args[i]; - if (i > 0) { + if (i > idx0) { str = gb_string_appendc(str, ", "); } str = write_expr_to_string(str, arg, shorthand); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index f65e1079e..87a354a49 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -292,6 +292,8 @@ struct lbProcedure { LLVMMetadataRef debug_info; lbCopyElisionHint copy_elision_hint; + + PtrMap selector_values; }; diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 1894e85f6..a95d884b0 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -2993,9 +2993,8 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) { return lb_build_addr_ptr(p, ue->expr); } +lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr); lbValue lb_build_expr(lbProcedure *p, Ast *expr) { - lbModule *m = p->module; - u16 prev_state_flags = p->state_flags; defer (p->state_flags = prev_state_flags); @@ -3022,6 +3021,32 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { p->state_flags = out; } + + // IMPORTANT NOTE(bill): + // Selector Call Expressions (foo->bar(...)) + // must only evaluate `foo` once as it gets transformed into + // `foo.bar(foo, ...)` + // And if `foo` is a procedure call or something more complex, storing the value + // once is a very good idea + // If a stored value is found, it must be removed from the cache + if (expr->state_flags & StateFlag_SelectorCallExpr) { + lbValue *pp = map_get(&p->selector_values, expr); + if (pp != nullptr) { + lbValue res = *pp; + map_remove(&p->selector_values, expr); + return res; + } + } + lbValue res = lb_build_expr_internal(p, expr); + if (expr->state_flags & StateFlag_SelectorCallExpr) { + map_set(&p->selector_values, expr, res); + } + return res; +} + +lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) { + lbModule *m = p->module; + expr = unparen_expr(expr); TokenPos expr_pos = ast_token(expr).pos; @@ -3119,14 +3144,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { case_ast_node(se, SelectorCallExpr, expr); GB_ASSERT(se->modified_call); - TypeAndValue tav = type_and_value_of_expr(expr); - GB_ASSERT(tav.mode != Addressing_Invalid); - lbValue res = lb_build_call_expr(p, se->call); - - ast_node(ce, CallExpr, se->call); - ce->sce_temp_data = gb_alloc_copy(permanent_allocator(), &res, gb_size_of(res)); - - return res; + return lb_build_call_expr(p, se->call); case_end; case_ast_node(te, TernaryIfExpr, expr); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 75ca77641..1c0ecabbe 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -113,6 +113,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) p->branch_blocks.allocator = a; p->context_stack.allocator = a; p->scope_stack.allocator = a; + map_init(&p->selector_values, a); if (p->is_foreign) { lb_add_foreign_library_path(p->module, entity->Procedure.foreign_library); @@ -2832,10 +2833,6 @@ lbValue lb_build_call_expr(lbProcedure *p, Ast *expr) { expr = unparen_expr(expr); ast_node(ce, CallExpr, expr); - if (ce->sce_temp_data) { - return *(lbValue *)ce->sce_temp_data; - } - lbValue res = lb_build_call_expr_internal(p, expr); if (ce->optional_ok_one) { // TODO(bill): Minor hack for #optional_ok procedures diff --git a/src/parser.cpp b/src/parser.cpp index a6f30cdfd..b63c530d6 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -164,6 +164,7 @@ Ast *clone_ast(Ast *node) { case Ast_SelectorCallExpr: n->SelectorCallExpr.expr = clone_ast(n->SelectorCallExpr.expr); n->SelectorCallExpr.call = clone_ast(n->SelectorCallExpr.call); + GB_ASSERT(n->SelectorCallExpr.modified_call); break; case Ast_IndexExpr: n->IndexExpr.expr = clone_ast(n->IndexExpr.expr); diff --git a/src/parser.hpp b/src/parser.hpp index c167ef6d5..8719b5e56 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -282,6 +282,8 @@ enum StateFlag : u8 { StateFlag_type_assert = 1<<2, StateFlag_no_type_assert = 1<<3, + StateFlag_SelectorCallExpr = 1<<6, + StateFlag_BeenHandled = 1<<7, }; @@ -411,7 +413,7 @@ AST_KIND(_ExprBegin, "", bool) \ Token ellipsis; \ ProcInlining inlining; \ bool optional_ok_one; \ - void *sce_temp_data; \ + bool was_selector; \ }) \ AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \ AST_KIND(EnumFieldValue, "enum field value", struct { \ -- cgit v1.2.3 From 6c7e5748a8ee80b01b99fe41192d4aac32651849 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 18 Jul 2022 12:49:29 +0100 Subject: Integrate numerous debug fixes from #1877 --- src/llvm_backend_debug.cpp | 2 +- src/llvm_backend_expr.cpp | 11 --------- src/llvm_backend_general.cpp | 8 +++---- src/llvm_backend_proc.cpp | 55 +++++++++++++++++++++++++++++++++----------- src/llvm_backend_stmt.cpp | 8 +++---- 5 files changed, 49 insertions(+), 35 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index 45a868581..207164c63 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -969,7 +969,7 @@ void lb_add_debug_local_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, T ); LLVMValueRef storage = ptr; - LLVMBasicBlockRef block = p->decl_block->block; + LLVMBasicBlockRef block = p->curr_block->block; LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, token.pos); LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0); lb_set_llvm_metadata(m, ptr, llvm_expr); diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 59f75d43f..624d06188 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -3068,17 +3068,6 @@ lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) { return lb_const_value(p->module, type, tv.value); } - #if 0 - LLVMMetadataRef prev_debug_location = nullptr; - if (p->debug_info != nullptr) { - prev_debug_location = LLVMGetCurrentDebugLocation2(p->builder); - LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, expr)); - } - defer (if (prev_debug_location != nullptr) { - LLVMSetCurrentDebugLocation2(p->builder, prev_debug_location); - }); - #endif - switch (expr->kind) { case_ast_node(bl, BasicLit, expr); TokenPos pos = bl->token.pos; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index f6bd306d2..b2a609f85 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2746,10 +2746,6 @@ lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e, bool zero_init, i32 p } } - if (zero_init) { - lb_mem_zero_ptr(p, ptr, type, alignment); - } - lbValue val = {}; val.value = ptr; val.type = alloc_type_pointer(type); @@ -2759,6 +2755,10 @@ lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e, bool zero_init, i32 p lb_add_debug_local_variable(p, ptr, type, e->token); } + if (zero_init) { + lb_mem_zero_ptr(p, ptr, type, alignment); + } + return lb_addr(val); } diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 1c0ecabbe..0ffe1c4c7 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -434,6 +434,40 @@ void lb_start_block(lbProcedure *p, lbBlock *b) { p->curr_block = b; } +void lb_set_debug_position_to_procedure_begin(lbProcedure *p) { + if (p->debug_info == nullptr) { + return; + } + TokenPos pos = {}; + if (p->body != nullptr) { + pos = ast_token(p->body).pos; + } else if (p->type_expr != nullptr) { + pos = ast_token(p->type_expr).pos; + } else if (p->entity != nullptr) { + pos = p->entity->token.pos; + } + if (pos.file_id != 0) { + LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_token_pos(p, pos)); + } +} + +void lb_set_debug_position_to_procedure_end(lbProcedure *p) { + if (p->debug_info == nullptr) { + return; + } + TokenPos pos = {}; + if (p->body != nullptr) { + pos = ast_end_token(p->body).pos; + } else if (p->type_expr != nullptr) { + pos = ast_end_token(p->type_expr).pos; + } else if (p->entity != nullptr) { + pos = p->entity->token.pos; + } + if (pos.file_id != 0) { + LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_token_pos(p, pos)); + } +} + void lb_begin_procedure_body(lbProcedure *p) { DeclInfo *decl = decl_info_of_entity(p->entity); if (decl != nullptr) { @@ -565,29 +599,21 @@ void lb_begin_procedure_body(lbProcedure *p) { lb_push_context_onto_stack_from_implicit_parameter(p); } - lb_start_block(p, p->entry_block); - + lb_set_debug_position_to_procedure_begin(p); if (p->debug_info != nullptr) { - TokenPos pos = {}; - if (p->body != nullptr) { - pos = ast_token(p->body).pos; - } else if (p->type_expr != nullptr) { - pos = ast_token(p->type_expr).pos; - } else if (p->entity != nullptr) { - pos = p->entity->token.pos; - } - if (pos.file_id != 0) { - LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_token_pos(p, pos)); - } - if (p->context_stack.count != 0) { + p->curr_block = p->decl_block; lb_add_debug_context_variable(p, lb_find_or_generate_context_ptr(p)); } } + + lb_start_block(p, p->entry_block); } void lb_end_procedure_body(lbProcedure *p) { + lb_set_debug_position_to_procedure_begin(p); + LLVMPositionBuilderAtEnd(p->builder, p->decl_block->block); LLVMBuildBr(p->builder, p->entry_block->block); LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block); @@ -599,6 +625,7 @@ void lb_end_procedure_body(lbProcedure *p) { instr = LLVMGetLastInstruction(p->curr_block->block); if (!lb_is_instr_terminating(instr)) { lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr); + lb_set_debug_position_to_procedure_end(p); LLVMBuildRetVoid(p->builder); } } diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index 2afb5300b..d007ac747 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -1721,6 +1721,9 @@ void lb_build_for_stmt(lbProcedure *p, Ast *node) { ast_node(fs, ForStmt, node); lb_open_scope(p, fs->scope); // Open Scope here + if (p->debug_info != nullptr) { + LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, node)); + } if (fs->init != nullptr) { #if 1 @@ -1971,14 +1974,9 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { } } - LLVMMetadataRef prev_debug_location = nullptr; if (p->debug_info != nullptr) { - prev_debug_location = LLVMGetCurrentDebugLocation2(p->builder); LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, node)); } - defer (if (prev_debug_location != nullptr) { - LLVMSetCurrentDebugLocation2(p->builder, prev_debug_location); - }); u16 prev_state_flags = p->state_flags; defer (p->state_flags = prev_state_flags); -- cgit v1.2.3 From d492fb3501390ea2fb69addad21855fd1c96e717 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 24 Jul 2022 12:39:59 +0100 Subject: Fix and improve `x->y()` behaviour to minimize duplicate evaluation --- src/llvm_backend.hpp | 1 + src/llvm_backend_expr.cpp | 34 +++++++++++++++++++++++++++++++--- src/llvm_backend_proc.cpp | 3 ++- 3 files changed, 34 insertions(+), 4 deletions(-) (limited to 'src/llvm_backend_proc.cpp') diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 87a354a49..745dbbbc7 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -294,6 +294,7 @@ struct lbProcedure { lbCopyElisionHint copy_elision_hint; PtrMap selector_values; + PtrMap selector_addr; }; diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 624d06188..1365028fc 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -3039,6 +3039,12 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { map_remove(&p->selector_values, expr); return res; } + lbAddr *pa = map_get(&p->selector_addr, expr); + if (pa != nullptr) { + lbAddr res = *pa; + map_remove(&p->selector_addr, expr); + return lb_addr_load(p, res); + } } lbValue res = lb_build_expr_internal(p, expr); if (expr->state_flags & StateFlag_SelectorCallExpr) { @@ -3430,9 +3436,34 @@ lbAddr lb_build_array_swizzle_addr(lbProcedure *p, AstCallExpr *ce, TypeAndValue } +lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr); lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { expr = unparen_expr(expr); + // IMPORTANT NOTE(bill): + // Selector Call Expressions (foo->bar(...)) + // must only evaluate `foo` once as it gets transformed into + // `foo.bar(foo, ...)` + // And if `foo` is a procedure call or something more complex, storing the value + // once is a very good idea + // If a stored value is found, it must be removed from the cache + if (expr->state_flags & StateFlag_SelectorCallExpr) { + lbAddr *pp = map_get(&p->selector_addr, expr); + if (pp != nullptr) { + lbAddr res = *pp; + map_remove(&p->selector_addr, expr); + return res; + } + } + lbAddr addr = lb_build_addr_internal(p, expr); + if (expr->state_flags & StateFlag_SelectorCallExpr) { + map_set(&p->selector_addr, expr, addr); + } + return addr; +} + + +lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) { switch (expr->kind) { case_ast_node(i, Implicit, expr); lbAddr v = {}; @@ -3574,9 +3605,6 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { case_end; case_ast_node(se, SelectorCallExpr, expr); - GB_ASSERT(se->modified_call); - TypeAndValue tav = type_and_value_of_expr(expr); - GB_ASSERT(tav.mode != Addressing_Invalid); lbValue e = lb_build_expr(p, expr); return lb_addr(lb_address_from_load_or_generate_local(p, e)); case_end; diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 0ffe1c4c7..0b0c0794b 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -113,7 +113,8 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) p->branch_blocks.allocator = a; p->context_stack.allocator = a; p->scope_stack.allocator = a; - map_init(&p->selector_values, a); + map_init(&p->selector_values, a, 0); + map_init(&p->selector_addr, a, 0); if (p->is_foreign) { lb_add_foreign_library_path(p->module, entity->Procedure.foreign_library); -- cgit v1.2.3