From 2820bbc269bb989052163544f2764bc4287254e4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 5 Jan 2024 13:38:30 +0000 Subject: Add `@(entry_point_only)` for procedures --- src/check_decl.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 71b897a84..85ba4230a 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -908,6 +908,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { break; } + e->Procedure.entry_point_only = ac.entry_point_only; e->Procedure.is_export = ac.is_export; e->deprecated_message = ac.deprecated_message; e->warning_message = ac.warning_message; -- cgit v1.2.3 From d7d23e65eae616d44cc070edff8171eb3160b5b0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 5 Jan 2024 13:47:00 +0000 Subject: Clean up error block usage --- src/build_settings.cpp | 1 + src/check_decl.cpp | 7 +++--- src/check_expr.cpp | 68 +++++++++++++++++++++++++++++--------------------- src/check_stmt.cpp | 6 ++--- src/check_type.cpp | 3 +-- src/checker.cpp | 6 ++--- src/main.cpp | 2 ++ 7 files changed, 50 insertions(+), 43 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/src/build_settings.cpp b/src/build_settings.cpp index d91a31ff2..18ad8ac0d 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -361,6 +361,7 @@ struct BuildContext { bool ignore_warnings; bool warnings_as_errors; bool hide_error_line; + bool terse_errors; bool has_ansi_terminal_colours; bool ignore_lazy; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 85ba4230a..8a3f89877 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -138,11 +138,10 @@ gb_internal void check_init_variables(CheckerContext *ctx, Entity **lhs, isize l } if (o->type && is_type_no_copy(o->type)) { - begin_error_block(); + ERROR_BLOCK(); if (check_no_copy_assignment(*o, str_lit("initialization"))) { error_line("\tInitialization of a #no_copy type must be either implicitly zero, a constant literal, or a return value from a call expression"); } - end_error_block(); } } if (rhs_count > 0 && lhs_count != rhs_count) { @@ -1301,8 +1300,8 @@ gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, D continue; } - begin_error_block(); - defer (end_error_block()); + + ERROR_BLOCK(); ProcTypeOverloadKind kind = are_proc_types_overload_safe(p->type, q->type); bool both_have_where_clauses = false; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 14e3bc0de..8d347bec7 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -184,6 +184,8 @@ gb_internal void populate_check_did_you_mean_objc_entity(StringSet *set, Entity gb_internal void check_did_you_mean_objc_entity(String const &name, Entity *e, bool is_type, char const *prefix = "") { + if (build_context.terse_errors) { return; } + ERROR_BLOCK(); GB_ASSERT(e->kind == Entity_TypeName); GB_ASSERT(e->TypeName.objc_metadata != nullptr); @@ -204,6 +206,8 @@ gb_internal void check_did_you_mean_objc_entity(String const &name, Entity *e, b } gb_internal void check_did_you_mean_type(String const &name, Array const &fields, char const *prefix = "") { + if (build_context.terse_errors) { return; } + ERROR_BLOCK(); DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), fields.count, name); @@ -217,6 +221,8 @@ gb_internal void check_did_you_mean_type(String const &name, Array con gb_internal void check_did_you_mean_type(String const &name, Slice const &fields, char const *prefix = "") { + if (build_context.terse_errors) { return; } + ERROR_BLOCK(); DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), fields.count, name); @@ -229,6 +235,8 @@ gb_internal void check_did_you_mean_type(String const &name, Slice con } gb_internal void check_did_you_mean_scope(String const &name, Scope *scope, char const *prefix = "") { + if (build_context.terse_errors) { return; } + ERROR_BLOCK(); DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), scope->elements.count, name); @@ -2203,7 +2211,6 @@ gb_internal bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *typ ERROR_BLOCK(); - if (is_type_numeric(o->type) && is_type_numeric(type)) { if (!is_type_integer(o->type) && is_type_integer(type)) { error(o->expr, "'%s' truncated to '%s', got %s", a, b, s); @@ -2264,8 +2271,7 @@ gb_internal void check_old_for_or_switch_value_usage(Ast *expr) { if (e != nullptr && (e->flags & EntityFlag_OldForOrSwitchValue) != 0) { GB_ASSERT(e->kind == Entity_Variable); - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); if ((e->flags & EntityFlag_ForValue) != 0) { Type *parent_type = type_deref(e->Variable.for_loop_parent_type); @@ -2309,8 +2315,7 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * break; default: { - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); error(op, "Cannot take the pointer address of '%s'", str); if (e != nullptr && (e->flags & EntityFlag_ForValue) != 0) { Type *parent_type = type_deref(e->Variable.for_loop_parent_type); @@ -3071,7 +3076,7 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) { x->mode = Addressing_Invalid; - begin_error_block(); + ERROR_BLOCK(); error(x->expr, "Cannot cast '%s' as '%s' from '%s'", expr_str, to_type, from_type); if (is_const_expr) { gbString val_str = exact_value_to_string(x->value); @@ -3094,8 +3099,6 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) { } check_cast_error_suggestion(c, x, type); - end_error_block(); - return; } @@ -4047,8 +4050,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar if (check_is_assignable_to(c, operand, elem)) { if (t->Matrix.row_count != t->Matrix.column_count) { operand->mode = Addressing_Invalid; - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); convert_untyped_error(c, operand, target_type); error_line("\tNote: Only a square matrix types can be initialized with a scalar value\n"); @@ -4109,8 +4111,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar target_type = t->Union.variants[first_success_index]; break; } else if (valid_count > 1) { - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); GB_ASSERT(first_success_index >= 0); operand->mode = Addressing_Invalid; @@ -4136,8 +4137,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar } else if (is_type_untyped_uninit(operand->type)) { target_type = t_untyped_uninit; } else if (!is_type_untyped_nil(operand->type) || !type_has_nil(target_type)) { - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); operand->mode = Addressing_Invalid; convert_untyped_error(c, operand, target_type); @@ -4714,6 +4714,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod entity = scope_lookup_current(import_scope, entity_name); bool allow_builtin = false; if (!is_entity_declared_for_selector(entity, import_scope, &allow_builtin)) { + ERROR_BLOCK(); error(node, "'%.*s' is not declared by '%.*s'", LIT(entity_name), LIT(import_name)); operand->mode = Addressing_Invalid; operand->expr = node; @@ -4914,6 +4915,8 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod error(op_expr, "Type '%s' has no field '%s'", op_str, sel_str); } } else { + ERROR_BLOCK(); + error(op_expr, "'%s' of type '%s' has no field '%s'", op_str, type_str, sel_str); if (operand->type != nullptr && selector->kind == Ast_Ident) { @@ -6338,8 +6341,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, }; if (valids.count == 0) { - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); error(operand->expr, "No procedures or ambiguous call for procedure group '%s' that match with the given arguments", expr_name); if (positional_operands.count == 0 && named_operands.count == 0) { @@ -6429,8 +6431,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, data.result_type = t_invalid; } else if (valids.count > 1) { - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name); print_argument_types(); @@ -7649,6 +7650,8 @@ gb_internal ExprKind check_implicit_selector_expr(CheckerContext *c, Operand *o, String name = ise->selector->Ident.token.string; if (is_type_enum(th)) { + ERROR_BLOCK(); + Type *bt = base_type(th); GB_ASSERT(bt->kind == Type_Enum); @@ -9050,8 +9053,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * } if (unhandled.count > 0) { - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); if (unhandled.count == 1) { error_no_newline(node, "Unhandled enumerated array case: %.*s", LIT(unhandled[0]->token.string)); @@ -9062,9 +9064,11 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * error_line("\t%.*s\n", LIT(f->token.string)); } } - error_line("\n"); - error_line("\tSuggestion: Was '#partial %s{...}' wanted?\n", type_to_string(type)); + if (!build_context.terse_errors) { + error_line("\n"); + error_line("\tSuggestion: Was '#partial %s{...}' wanted?\n", type_to_string(type)); + } } } @@ -9688,7 +9692,9 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, if (index < 0) { gbString str = expr_to_string(o->expr); error(o->expr, "Cannot index a constant '%s'", str); - error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); + if (!build_context.terse_errors) { + error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); + } gb_string_free(str); o->mode = Addressing_Invalid; o->expr = node; @@ -9702,7 +9708,9 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, if (!success) { gbString str = expr_to_string(o->expr); error(o->expr, "Cannot index a constant '%s' with index %lld", str, cast(long long)index); - error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); + if (!build_context.terse_errors) { + error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); + } gb_string_free(str); o->mode = Addressing_Invalid; o->expr = node; @@ -9890,7 +9898,9 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node, if (!all_constant) { gbString str = expr_to_string(o->expr); error(o->expr, "Cannot slice '%s' with non-constant indices", str); - error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); + if (!build_context.terse_errors) { + error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n"); + } gb_string_free(str); o->mode = Addressing_Value; // NOTE(bill): Keep subsequent values going without erring o->expr = node; @@ -10246,15 +10256,15 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast } else { gbString str = expr_to_string(o->expr); gbString typ = type_to_string(o->type); - begin_error_block(); + ERROR_BLOCK(); error(o->expr, "Cannot dereference '%s' of type '%s'", str, typ); if (o->type && is_type_multi_pointer(o->type)) { - error_line("\tDid you mean '%s[0]'?\n", str); + if (!build_context.terse_errors) { + error_line("\tDid you mean '%s[0]'?\n", str); + } } - end_error_block(); - gb_string_free(typ); gb_string_free(str); o->mode = Addressing_Invalid; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index b93be734e..d56e5e212 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1085,8 +1085,7 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags } if (unhandled.count > 0) { - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); if (unhandled.count == 1) { error_no_newline(node, "Unhandled switch case: %.*s", LIT(unhandled[0]->token.string)); @@ -1813,7 +1812,7 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f } if (new_name_count == 0) { - begin_error_block(); + ERROR_BLOCK(); error(node, "No new declarations on the left hand side"); bool all_underscore = true; for (Ast *name : vd->names) { @@ -1831,7 +1830,6 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f error_line("\tSuggestion: Try changing the declaration (:=) to an assignment (=)\n"); } - end_error_block(); } Type *init_type = nullptr; diff --git a/src/check_type.cpp b/src/check_type.cpp index f11418dc0..a95026711 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2702,14 +2702,13 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T check_expr_or_type(&c, &o, pt->type); if (o.mode != Addressing_Invalid && o.mode != Addressing_Type) { // NOTE(bill): call check_type_expr again to get a consistent error message - begin_error_block(); + ERROR_BLOCK(); elem = check_type_expr(&c, pt->type, nullptr); if (o.mode == Addressing_Variable) { gbString s = expr_to_string(pt->type); error_line("\tSuggestion: ^ is used for pointer types, did you mean '&%s'?\n", s); gb_string_free(s); } - end_error_block(); } else { elem = o.type; } diff --git a/src/checker.cpp b/src/checker.cpp index ac885222d..d25acb15e 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -4025,12 +4025,11 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { if (c->foreign_context.default_cc > 0) { cc = c->foreign_context.default_cc; } else if (is_arch_wasm()) { - begin_error_block(); + ERROR_BLOCK(); error(init, "For wasm related targets, it is required that you either define the" " @(default_calling_convention=) on the foreign block or" " explicitly assign it on the procedure signature"); error_line("\tSuggestion: when dealing with normal Odin code (e.g. js_wasm32), use \"contextless\"; when dealing with Emscripten like code, use \"c\"\n"); - end_error_block(); } } e->Procedure.link_prefix = c->foreign_context.link_prefix; @@ -4077,8 +4076,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { if (e->kind != Entity_Procedure) { if (fl != nullptr) { - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); AstKind kind = init->kind; error(name, "Only procedures and variables are allowed to be in a foreign block, got %.*s", LIT(ast_strings[kind])); diff --git a/src/main.cpp b/src/main.cpp index 4d4e01ada..fd754248b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1160,10 +1160,12 @@ gb_internal bool parse_build_flags(Array args) { case BuildFlag_TerseErrors: build_context.hide_error_line = true; + build_context.terse_errors = true; break; case BuildFlag_VerboseErrors: gb_printf_err("-verbose-errors is not the default, -terse-errors can now disable it\n"); build_context.hide_error_line = false; + build_context.terse_errors = false; break; case BuildFlag_ErrorPosStyle: -- cgit v1.2.3 From aff8f06e3cb1bdb9c3ffee98a68675c843df78fd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 7 Jan 2024 19:56:00 +0000 Subject: Add frontend stuff instrumentation tooling //+no-instrumentation @(no_instrumentation) @(instrumentation_enter) @(instrumentation_exit) --- src/check_decl.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/checker.cpp | 44 ++++++++++++++++++++++++++++++++++++ src/checker.hpp | 35 ++++++++++++++++++++--------- src/entity.cpp | 1 + src/parser.cpp | 4 +++- src/parser.hpp | 2 ++ 6 files changed, 140 insertions(+), 12 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 8a3f89877..c69d8185e 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -909,6 +909,72 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { e->Procedure.entry_point_only = ac.entry_point_only; e->Procedure.is_export = ac.is_export; + + bool no_instrumentation = false; + if (pl->body == nullptr) { + no_instrumentation = true; + if (ac.no_instrumentation != Instrumentation_Default) { + error(e->token, "@(no_instrumentation) is not allowed on foreign procedures"); + } + } else { + if (e->file) { + no_instrumentation = (e->file->flags & AstFile_NoInstrumentation) != 0; + } + + switch (ac.no_instrumentation) { + case Instrumentation_Enabled: no_instrumentation = false; break; + case Instrumentation_Default: break; + case Instrumentation_Disabled: no_instrumentation = true; break; + } + } + e->Procedure.no_instrumentation = no_instrumentation; + + auto const is_valid_instrumentation_call = [](Type *type) -> bool { + if (type == nullptr || type->kind != Type_Proc) { + return false; + } + if (type->Proc.calling_convention != ProcCC_CDecl) { + return false; + } + if (type->Proc.result_count != 0) { + return false; + } + if (type->Proc.param_count != 2) { + return false; + } + Type *p0 = type->Proc.params->Tuple.variables[0]->type; + Type *p1 = type->Proc.params->Tuple.variables[1]->type; + return is_type_rawptr(p0) && is_type_rawptr(p1); + }; + + if (ac.instrumentation_enter && ac.instrumentation_exit) { + error(e->token, "A procedure cannot be marked with both @(instrumentation_enter) and @(instrumentation_exit)"); + } else if (ac.instrumentation_enter) { + if (!is_valid_instrumentation_call(e->type)) { + gbString s = type_to_string(e->type); + error(e->token, "@(instrumentation_enter) procedures must have the type 'proc \"c\" (rawptr, rawptr)', got %s", s); + gb_string_free(s); + } + MUTEX_GUARD(&ctx->info->instrumentation_mutex); + if (ctx->info->instrumentation_enter_entity != nullptr) { + error(e->token, "@(instrumentation_enter) has already been set"); + } else { + ctx->info->instrumentation_enter_entity = e; + } + } else if (ac.instrumentation_exit) { + if (!is_valid_instrumentation_call(e->type)) { + gbString s = type_to_string(e->type); + error(e->token, "@(instrumentation_exit) procedures must have the type 'proc \"c\" (rawptr, rawptr)', got %s", s); + gb_string_free(s); + } + MUTEX_GUARD(&ctx->info->instrumentation_mutex); + if (ctx->info->instrumentation_exit_entity != nullptr) { + error(e->token, "@(instrumentation_exit) has already been set"); + } else { + ctx->info->instrumentation_exit_entity = e; + } + } + e->deprecated_message = ac.deprecated_message; e->warning_message = ac.warning_message; ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); diff --git a/src/checker.cpp b/src/checker.cpp index d25acb15e..5e46e87fe 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2581,6 +2581,9 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) { str_lit("multi_pointer_slice_expr_error"), ); + add_dependency_to_set(c, c->info.instrumentation_enter_entity); + add_dependency_to_set(c, c->info.instrumentation_exit_entity); + generate_minimum_dependency_set_internal(c, start); @@ -3414,8 +3417,38 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { } return true; } else if (name == "entry_point_only") { + if (value != nullptr) { + error(value, "'%.*s' expects no parameter", LIT(name)); + } ac->entry_point_only = true; return true; + } else if (name == "no_instrumentation") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_Invalid) { + ac->no_instrumentation = Instrumentation_Disabled; + } else if (ev.kind == ExactValue_Bool) { + if (ev.value_bool) { + ac->no_instrumentation = Instrumentation_Disabled; + } else { + ac->no_instrumentation = Instrumentation_Enabled; + } + } else { + error(value, "Expected either a boolean or no parameter for '%.*s'", LIT(name)); + return false; + } + return true; + } else if (name == "instrumentation_enter") { + if (value != nullptr) { + error(value, "'%.*s' expects no parameter", LIT(name)); + } + ac->instrumentation_enter = true; + return true; + } else if (name == "instrumentation_exit") { + if (value != nullptr) { + error(value, "'%.*s' expects no parameter", LIT(name)); + } + ac->instrumentation_exit = true; + return true; } return false; } @@ -6216,6 +6249,17 @@ gb_internal void check_parsed_files(Checker *c) { GB_ASSERT(c->info.entity_queue.count.load(std::memory_order_relaxed) == 0); GB_ASSERT(c->info.definition_queue.count.load(std::memory_order_relaxed) == 0); + TIME_SECTION("check instrumentation calls"); + { + if ((c->info.instrumentation_enter_entity != nullptr) ^ + (c->info.instrumentation_exit_entity != nullptr)) { + Entity *e = c->info.instrumentation_enter_entity; + if (!e) e = c->info.instrumentation_exit_entity; + error(e->token, "Both @(instrumentation_enter) and @(instrumentation_exit) must be defined"); + } + } + + TIME_SECTION("add untyped expression values"); // Add untyped expression values for (UntypedExprInfo u = {}; mpsc_dequeue(&c->global_untyped_queue, &u); /**/) { diff --git a/src/checker.hpp b/src/checker.hpp index 7c399e50f..9da0f2950 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -103,6 +103,12 @@ struct DeferredProcedure { }; +enum InstrumentationFlag : i32 { + Instrumentation_Enabled = -1, + Instrumentation_Default = 0, + Instrumentation_Disabled = +1, +}; + struct AttributeContext { String link_name; String link_prefix; @@ -113,20 +119,23 @@ struct AttributeContext { String deprecated_message; String warning_message; DeferredProcedure deferred_procedure; - bool is_export : 1; - bool is_static : 1; - bool require_results : 1; - bool require_declaration : 1; - bool has_disabled_proc : 1; - bool disabled_proc : 1; - bool test : 1; - bool init : 1; - bool fini : 1; - bool set_cold : 1; - bool entry_point_only : 1; + bool is_export : 1; + bool is_static : 1; + bool require_results : 1; + bool require_declaration : 1; + bool has_disabled_proc : 1; + bool disabled_proc : 1; + bool test : 1; + bool init : 1; + bool fini : 1; + bool set_cold : 1; + bool entry_point_only : 1; + bool instrumentation_enter : 1; + bool instrumentation_exit : 1; u32 optimization_mode; // ProcedureOptimizationMode i64 foreign_import_priority_index; String extra_linker_flags; + InstrumentationFlag no_instrumentation; String objc_class; String objc_name; @@ -403,6 +412,10 @@ struct CheckerInfo { BlockingMutex all_procedures_mutex; Array all_procedures; + + BlockingMutex instrumentation_mutex; + Entity *instrumentation_enter_entity; + Entity *instrumentation_exit_entity; }; struct CheckerContext { diff --git a/src/entity.cpp b/src/entity.cpp index d0b3cf139..0539386d0 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -251,6 +251,7 @@ struct Entity { bool generated_from_polymorphic : 1; bool target_feature_disabled : 1; bool entry_point_only : 1; + bool no_instrumentation : 1; String target_feature; } Procedure; struct { diff --git a/src/parser.cpp b/src/parser.cpp index c0498b425..2671054df 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -5919,7 +5919,7 @@ gb_internal bool parse_file(Parser *p, AstFile *f) { f->vet_flags = parse_vet_tag(tok, lc); f->vet_flags_set = true; } else if (string_starts_with(lc, str_lit("+ignore"))) { - return false; + return false; } else if (string_starts_with(lc, str_lit("+private"))) { f->flags |= AstFile_IsPrivatePkg; String command = string_trim_starts_with(lc, str_lit("+private ")); @@ -5941,6 +5941,8 @@ gb_internal bool parse_file(Parser *p, AstFile *f) { } else { f->flags |= AstFile_IsLazy; } + } else if (lc == "+no-instrumentation") { + f->flags |= AstFile_NoInstrumentation; } else { warning(tok, "Ignoring unknown tag '%.*s'", LIT(lc)); } diff --git a/src/parser.hpp b/src/parser.hpp index bce818652..cc1836ef3 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -76,6 +76,8 @@ enum AstFileFlag : u32 { AstFile_IsTest = 1<<3, AstFile_IsLazy = 1<<4, + + AstFile_NoInstrumentation = 1<<5, }; enum AstDelayQueueKind { -- cgit v1.2.3 From f4782157d3b586a88983d3b770c2c0eeb17946d1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 7 Jan 2024 21:34:44 +0000 Subject: Implement instrumentation pass --- core/runtime/core.odin | 1 + core/runtime/entry_unix.odin | 1 + core/runtime/entry_wasm.odin | 1 + core/runtime/entry_windows.odin | 1 + core/runtime/error_checks.odin | 24 ++++++----- core/runtime/procs_windows_amd64.odin | 1 + core/runtime/procs_windows_i386.odin | 1 + src/check_decl.cpp | 26 ++++++++---- src/entity.cpp | 2 +- src/llvm_backend.cpp | 2 - src/llvm_backend.hpp | 2 +- src/llvm_backend_general.cpp | 13 ++++++ src/llvm_backend_opt.cpp | 77 +++++++++++++++++++++++++++++++++++ src/llvm_backend_proc.cpp | 12 ++++++ 14 files changed, 143 insertions(+), 21 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 2d176f909..e12d9a43d 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -18,6 +18,7 @@ // This could change at a later date if the all these data structures are // implemented within the compiler rather than in this "preload" file // +//+no-instrumentation package runtime import "core:intrinsics" diff --git a/core/runtime/entry_unix.odin b/core/runtime/entry_unix.odin index 78e545c22..f494a509e 100644 --- a/core/runtime/entry_unix.odin +++ b/core/runtime/entry_unix.odin @@ -1,5 +1,6 @@ //+private //+build linux, darwin, freebsd, openbsd +//+no-instrumentation package runtime import "core:intrinsics" diff --git a/core/runtime/entry_wasm.odin b/core/runtime/entry_wasm.odin index 235d5611b..e7f3f156f 100644 --- a/core/runtime/entry_wasm.odin +++ b/core/runtime/entry_wasm.odin @@ -1,5 +1,6 @@ //+private //+build wasm32, wasm64p32 +//+no-instrumentation package runtime import "core:intrinsics" diff --git a/core/runtime/entry_windows.odin b/core/runtime/entry_windows.odin index a315c1209..277daecca 100644 --- a/core/runtime/entry_windows.odin +++ b/core/runtime/entry_windows.odin @@ -1,5 +1,6 @@ //+private //+build windows +//+no-instrumentation package runtime import "core:intrinsics" diff --git a/core/runtime/error_checks.odin b/core/runtime/error_checks.odin index 9d484979a..ea6333c29 100644 --- a/core/runtime/error_checks.odin +++ b/core/runtime/error_checks.odin @@ -1,5 +1,6 @@ package runtime +@(no_instrumentation) bounds_trap :: proc "contextless" () -> ! { when ODIN_OS == .Windows { windows_trap_array_bounds() @@ -8,6 +9,7 @@ bounds_trap :: proc "contextless" () -> ! { } } +@(no_instrumentation) type_assertion_trap :: proc "contextless" () -> ! { when ODIN_OS == .Windows { windows_trap_type_assertion() @@ -21,7 +23,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index if uint(index) < uint(count) { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (file: string, line, column: i32, index, count: int) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Index ") @@ -34,6 +36,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index handle_error(file, line, column, index, count) } +@(no_instrumentation) slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int, len: int) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid slice indices ") @@ -46,6 +49,7 @@ slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, h bounds_trap() } +@(no_instrumentation) multi_pointer_slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid slice indices ") @@ -82,7 +86,7 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32, if 0 <= low && low <= high && high <= max { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid dynamic array indices ") @@ -103,7 +107,7 @@ matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32 uint(column_index) < uint(column_count) { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Matrix indices [") @@ -127,7 +131,7 @@ when ODIN_NO_RTTI { if ok { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (file: string, line, column: i32) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid type assertion\n") @@ -140,7 +144,7 @@ when ODIN_NO_RTTI { if ok { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (file: string, line, column: i32) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid type assertion\n") @@ -153,7 +157,7 @@ when ODIN_NO_RTTI { if ok { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid) -> ! { print_caller_location(Source_Code_Location{file, line, column, ""}) print_string(" Invalid type assertion from ") @@ -198,7 +202,7 @@ when ODIN_NO_RTTI { return id } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! { actual := variant_type(from, from_data) @@ -224,7 +228,7 @@ make_slice_error_loc :: #force_inline proc "contextless" (loc := #caller_locatio if 0 <= len { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (loc: Source_Code_Location, len: int) -> ! { print_caller_location(loc) print_string(" Invalid slice length for make: ") @@ -239,7 +243,7 @@ make_dynamic_array_error_loc :: #force_inline proc "contextless" (loc := #caller if 0 <= len && len <= cap { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (loc: Source_Code_Location, len, cap: int) -> ! { print_caller_location(loc) print_string(" Invalid dynamic array parameters for make: ") @@ -256,7 +260,7 @@ make_map_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_loca if 0 <= cap { return } - @(cold) + @(cold, no_instrumentation) handle_error :: proc "contextless" (loc: Source_Code_Location, cap: int) -> ! { print_caller_location(loc) print_string(" Invalid map capacity for make: ") diff --git a/core/runtime/procs_windows_amd64.odin b/core/runtime/procs_windows_amd64.odin index e430357be..a30985d5c 100644 --- a/core/runtime/procs_windows_amd64.odin +++ b/core/runtime/procs_windows_amd64.odin @@ -1,4 +1,5 @@ //+private +//+no-instrumentation package runtime foreign import kernel32 "system:Kernel32.lib" diff --git a/core/runtime/procs_windows_i386.odin b/core/runtime/procs_windows_i386.odin index f810197f1..4f606da8f 100644 --- a/core/runtime/procs_windows_i386.odin +++ b/core/runtime/procs_windows_i386.odin @@ -1,4 +1,5 @@ //+private +//+no-instrumentation package runtime @require foreign import "system:int64.lib" diff --git a/src/check_decl.cpp b/src/check_decl.cpp index c69d8185e..a530945f9 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -910,24 +910,24 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { e->Procedure.entry_point_only = ac.entry_point_only; e->Procedure.is_export = ac.is_export; - bool no_instrumentation = false; + bool has_instrumentation = false; if (pl->body == nullptr) { - no_instrumentation = true; + has_instrumentation = false; if (ac.no_instrumentation != Instrumentation_Default) { error(e->token, "@(no_instrumentation) is not allowed on foreign procedures"); } } else { - if (e->file) { - no_instrumentation = (e->file->flags & AstFile_NoInstrumentation) != 0; + AstFile *file = e->token.pos.file_id ? global_files[e->token.pos.file_id] : nullptr; + if (file) { + has_instrumentation = (file->flags & AstFile_NoInstrumentation) == 0; } switch (ac.no_instrumentation) { - case Instrumentation_Enabled: no_instrumentation = false; break; + case Instrumentation_Enabled: has_instrumentation = true; break; case Instrumentation_Default: break; - case Instrumentation_Disabled: no_instrumentation = true; break; + case Instrumentation_Disabled: has_instrumentation = false; break; } } - e->Procedure.no_instrumentation = no_instrumentation; auto const is_valid_instrumentation_call = [](Type *type) -> bool { if (type == nullptr || type->kind != Type_Proc) { @@ -949,6 +949,9 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { if (ac.instrumentation_enter && ac.instrumentation_exit) { error(e->token, "A procedure cannot be marked with both @(instrumentation_enter) and @(instrumentation_exit)"); + + has_instrumentation = false; + e->flags |= EntityFlag_Require; } else if (ac.instrumentation_enter) { if (!is_valid_instrumentation_call(e->type)) { gbString s = type_to_string(e->type); @@ -961,6 +964,9 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } else { ctx->info->instrumentation_enter_entity = e; } + + has_instrumentation = false; + e->flags |= EntityFlag_Require; } else if (ac.instrumentation_exit) { if (!is_valid_instrumentation_call(e->type)) { gbString s = type_to_string(e->type); @@ -973,8 +979,14 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } else { ctx->info->instrumentation_exit_entity = e; } + + has_instrumentation = false; + e->flags |= EntityFlag_Require; } + e->Procedure.has_instrumentation = has_instrumentation; + + e->deprecated_message = ac.deprecated_message; e->warning_message = ac.warning_message; ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); diff --git a/src/entity.cpp b/src/entity.cpp index 0539386d0..e6c46d37e 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -251,7 +251,7 @@ struct Entity { bool generated_from_polymorphic : 1; bool target_feature_disabled : 1; bool entry_point_only : 1; - bool no_instrumentation : 1; + bool has_instrumentation : 1; String target_feature; } Procedure; struct { diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index b90fd8495..0175d039e 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1497,8 +1497,6 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) { auto passes = array_make(heap_allocator(), 0, 64); defer (array_free(&passes)); - - LLVMPassBuilderOptionsRef pb_options = LLVMCreatePassBuilderOptions(); defer (LLVMDisposePassBuilderOptions(pb_options)); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 4e193bcea..b645d66d6 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -563,7 +563,7 @@ gb_internal LLVMTypeRef OdinLLVMGetVectorElementType(LLVMTypeRef type); gb_internal String lb_filepath_ll_for_module(lbModule *m); - +gb_internal LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *type); gb_internal LLVMTypeRef llvm_array_type(LLVMTypeRef ElementType, uint64_t ElementCount) { #if LB_USE_NEW_PASS_SYSTEM diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 54327cc54..f0f5327c6 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2348,6 +2348,15 @@ gb_internal LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char c return LLVMCreateEnumAttribute(ctx, kind, value); } +gb_internal LLVMAttributeRef lb_create_string_attribute(LLVMContextRef ctx, String const &key, String const &value) { + LLVMAttributeRef attr = LLVMCreateStringAttribute( + ctx, + cast(char const *)key.text, cast(unsigned)key.len, + cast(char const *)value.text, cast(unsigned)value.len); + return attr; +} + + gb_internal void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name, u64 value) { LLVMAttributeRef attr = lb_create_enum_attribute(p->module->ctx, name, value); GB_ASSERT(attr != nullptr); @@ -2361,6 +2370,10 @@ gb_internal void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, cha gb_internal void lb_add_attribute_to_proc(lbModule *m, LLVMValueRef proc_value, char const *name, u64 value=0) { LLVMAddAttributeAtIndex(proc_value, LLVMAttributeIndex_FunctionIndex, lb_create_enum_attribute(m->ctx, name, value)); } +gb_internal void lb_add_attribute_to_proc_with_string(lbModule *m, LLVMValueRef proc_value, String const &name, String const &value) { + LLVMAttributeRef attr = lb_create_string_attribute(m->ctx, name, value); + LLVMAddAttributeAtIndex(proc_value, LLVMAttributeIndex_FunctionIndex, attr); +} diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index 2e03b7974..d5487d259 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -380,6 +380,80 @@ gb_internal void lb_run_remove_dead_instruction_pass(lbProcedure *p) { } } +gb_internal LLVMValueRef lb_run_instrumentation_pass_insert_call(lbProcedure *p, Entity *entity, LLVMBuilderRef dummy_builder) { + lbModule *m = p->module; + + lbValue cc = lb_find_procedure_value_from_entity(m, entity); + + LLVMValueRef args[2] = {}; + args[0] = p->value; + + LLVMValueRef returnaddress_args[1] = {}; + + returnaddress_args[0] = LLVMConstInt(LLVMInt32TypeInContext(m->ctx), 0, false); + + char const *instrinsic_name = "llvm.returnaddress"; + unsigned id = LLVMLookupIntrinsicID(instrinsic_name, gb_strlen(instrinsic_name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s", instrinsic_name); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(m->mod, id, nullptr, 0); + LLVMTypeRef call_type = LLVMIntrinsicGetType(m->ctx, id, nullptr, 0); + args[1] = LLVMBuildCall2(dummy_builder, call_type, ip, returnaddress_args, gb_count_of(returnaddress_args), ""); + + LLVMTypeRef fnp = lb_type_internal_for_procedures_raw(p->module, entity->type); + return LLVMBuildCall2(dummy_builder, fnp, cc.value, args, 2, ""); +} + + +gb_internal void lb_run_instrumentation_pass(lbProcedure *p) { + lbModule *m = p->module; + Entity *enter = m->info->instrumentation_enter_entity; + Entity *exit = m->info->instrumentation_exit_entity; + if (enter == nullptr || exit == nullptr) { + return; + } + if (!(p->entity && + p->entity->kind == Entity_Procedure && + p->entity->Procedure.has_instrumentation)) { + return; + } + +#define LLVM_V_NAME(x) x, cast(unsigned)(gb_count_of(x)-1) + + LLVMBuilderRef dummy_builder = LLVMCreateBuilderInContext(m->ctx); + defer (LLVMDisposeBuilder(dummy_builder)); + + LLVMBasicBlockRef entry_bb = p->entry_block->block; + LLVMPositionBuilder(dummy_builder, entry_bb, LLVMGetFirstInstruction(entry_bb)); + lb_run_instrumentation_pass_insert_call(p, enter, dummy_builder); + LLVMRemoveStringAttributeAtIndex(p->value, LLVMAttributeIndex_FunctionIndex, LLVM_V_NAME("instrument-function-entry")); + + unsigned bb_count = LLVMCountBasicBlocks(p->value); + LLVMBasicBlockRef *bbs = gb_alloc_array(temporary_allocator(), LLVMBasicBlockRef, bb_count); + LLVMGetBasicBlocks(p->value, bbs); + for (unsigned i = 0; i < bb_count; i++) { + LLVMBasicBlockRef bb = bbs[i]; + LLVMValueRef terminator = LLVMGetBasicBlockTerminator(bb); + if (terminator == nullptr || + !LLVMIsAReturnInst(terminator)) { + continue; + } + + // TODO(bill): getTerminatingMustTailCall() + // If T is preceded by a musttail call, that's the real terminator. + // if (CallInst *CI = BB.getTerminatingMustTailCall()) + // T = CI; + + + LLVMPositionBuilderBefore(dummy_builder, terminator); + lb_run_instrumentation_pass_insert_call(p, exit, dummy_builder); + } + + LLVMRemoveStringAttributeAtIndex(p->value, LLVMAttributeIndex_FunctionIndex, LLVM_V_NAME("instrument-function-exit")); + +#undef LLVM_V_NAME +} + + gb_internal void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedure *p, lbFunctionPassManagerKind pass_manager_kind) { if (p == nullptr) { @@ -401,6 +475,7 @@ gb_internal void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedur } break; } + lb_run_instrumentation_pass(p); LLVMRunFunctionPassManager(fpm, p->value); } @@ -552,3 +627,5 @@ gb_internal void lb_run_remove_unused_globals_pass(lbModule *m) { } } } + + diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index d4eae84bc..09bebd0cf 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -329,6 +329,18 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i } } + if (p->body && entity->Procedure.has_instrumentation) { + Entity *instrumentation_enter = m->info->instrumentation_enter_entity; + Entity *instrumentation_exit = m->info->instrumentation_exit_entity; + if (instrumentation_enter && instrumentation_exit) { + String enter = lb_get_entity_name(m, instrumentation_enter); + String exit = lb_get_entity_name(m, instrumentation_exit); + + lb_add_attribute_to_proc_with_string(m, p->value, make_string_c("instrument-function-entry"), enter); + lb_add_attribute_to_proc_with_string(m, p->value, make_string_c("instrument-function-exit"), exit); + } + } + 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 67dcd916e8f7b1badef5c6beee621557b069db3c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 9 Jan 2024 11:01:18 +0000 Subject: Update instrumentation signature to support `runtime.Source_Code_Location` as last parameter. --- src/check_decl.cpp | 15 ++++++++++----- src/llvm_backend.hpp | 2 ++ src/llvm_backend_opt.cpp | 10 ++++++++-- 3 files changed, 20 insertions(+), 7 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index a530945f9..ed3a109c2 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -933,20 +933,23 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { if (type == nullptr || type->kind != Type_Proc) { return false; } - if (type->Proc.calling_convention != ProcCC_CDecl) { + if (type->Proc.calling_convention != ProcCC_Contextless) { return false; } if (type->Proc.result_count != 0) { return false; } - if (type->Proc.param_count != 2) { + if (type->Proc.param_count != 3) { return false; } Type *p0 = type->Proc.params->Tuple.variables[0]->type; Type *p1 = type->Proc.params->Tuple.variables[1]->type; - return is_type_rawptr(p0) && is_type_rawptr(p1); + Type *p3 = type->Proc.params->Tuple.variables[2]->type; + return is_type_rawptr(p0) && is_type_rawptr(p1) && are_types_identical(p3, t_source_code_location); }; + static char const *instrumentation_proc_type_str = "proc \"contextless\" (proc_address: rawptr, call_site_return_address: rawptr, loc: runtime.Source_Code_Location)"; + if (ac.instrumentation_enter && ac.instrumentation_exit) { error(e->token, "A procedure cannot be marked with both @(instrumentation_enter) and @(instrumentation_exit)"); @@ -954,8 +957,9 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { e->flags |= EntityFlag_Require; } else if (ac.instrumentation_enter) { if (!is_valid_instrumentation_call(e->type)) { + init_core_source_code_location(ctx->checker); gbString s = type_to_string(e->type); - error(e->token, "@(instrumentation_enter) procedures must have the type 'proc \"c\" (rawptr, rawptr)', got %s", s); + error(e->token, "@(instrumentation_enter) procedures must have the type '%s', got %s", instrumentation_proc_type_str, s); gb_string_free(s); } MUTEX_GUARD(&ctx->info->instrumentation_mutex); @@ -968,9 +972,10 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { has_instrumentation = false; e->flags |= EntityFlag_Require; } else if (ac.instrumentation_exit) { + init_core_source_code_location(ctx->checker); if (!is_valid_instrumentation_call(e->type)) { gbString s = type_to_string(e->type); - error(e->token, "@(instrumentation_exit) procedures must have the type 'proc \"c\" (rawptr, rawptr)', got %s", s); + error(e->token, "@(instrumentation_exit) procedures must have the type '%s', got %s", instrumentation_proc_type_str, s); gb_string_free(s); } MUTEX_GUARD(&ctx->info->instrumentation_mutex); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index b645d66d6..fe2c2deba 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -565,6 +565,8 @@ gb_internal String lb_filepath_ll_for_module(lbModule *m); gb_internal LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *type); +gb_internal lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, String const &procedure, TokenPos const &pos); + gb_internal LLVMTypeRef llvm_array_type(LLVMTypeRef ElementType, uint64_t ElementCount) { #if LB_USE_NEW_PASS_SYSTEM return LLVMArrayType2(ElementType, ElementCount); diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index d5487d259..b57e74799 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -385,7 +385,7 @@ gb_internal LLVMValueRef lb_run_instrumentation_pass_insert_call(lbProcedure *p, lbValue cc = lb_find_procedure_value_from_entity(m, entity); - LLVMValueRef args[2] = {}; + LLVMValueRef args[3] = {}; args[0] = p->value; LLVMValueRef returnaddress_args[1] = {}; @@ -399,8 +399,14 @@ gb_internal LLVMValueRef lb_run_instrumentation_pass_insert_call(lbProcedure *p, LLVMTypeRef call_type = LLVMIntrinsicGetType(m->ctx, id, nullptr, 0); args[1] = LLVMBuildCall2(dummy_builder, call_type, ip, returnaddress_args, gb_count_of(returnaddress_args), ""); + Token name = {}; + if (p->entity) { + name = p->entity->token; + } + args[2] = lb_emit_source_code_location_as_global_ptr(p, name.string, name.pos).value; + LLVMTypeRef fnp = lb_type_internal_for_procedures_raw(p->module, entity->type); - return LLVMBuildCall2(dummy_builder, fnp, cc.value, args, 2, ""); + return LLVMBuildCall2(dummy_builder, fnp, cc.value, args, gb_count_of(args), ""); } -- cgit v1.2.3 From 3c47503780bd99b547777b727baf502504244bbb Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 28 Jan 2024 23:35:01 +0000 Subject: Improve foreign variable fuzzy type checking --- base/runtime/os_specific_bsd.odin | 3 ++- base/runtime/os_specific_darwin.odin | 1 + src/check_decl.cpp | 4 ++-- src/check_stmt.cpp | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/base/runtime/os_specific_bsd.odin b/base/runtime/os_specific_bsd.odin index 93ed9b4e6..7f23b625e 100644 --- a/base/runtime/os_specific_bsd.odin +++ b/base/runtime/os_specific_bsd.odin @@ -4,9 +4,10 @@ package runtime foreign import libc "system:c" +@(default_calling_convention="c") foreign libc { @(link_name="write") - _unix_write :: proc(fd: uintptr, buf: rawptr, size: int) -> int --- + _unix_write :: proc(fd: i32, buf: rawptr, size: int) -> int --- __error :: proc() -> ^i32 --- } diff --git a/base/runtime/os_specific_darwin.odin b/base/runtime/os_specific_darwin.odin index 5630c733c..0cb46024c 100644 --- a/base/runtime/os_specific_darwin.odin +++ b/base/runtime/os_specific_darwin.odin @@ -4,6 +4,7 @@ package runtime foreign import libc "system:System.framework" +@(default_calling_convention="c") foreign libc { @(link_name="__stderrp") _stderr: rawptr diff --git a/src/check_decl.cpp b/src/check_decl.cpp index ed3a109c2..3ccf1b97a 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1143,7 +1143,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { "\tat %s", LIT(name), token_pos_to_string(pos)); } - } else if (!are_types_identical(this_type, other_type)) { + } else if (!signature_parameter_similar_enough(this_type, other_type)) { error(d->proc_lit, "Foreign entity '%.*s' previously declared elsewhere with a different type\n" "\tat %s", @@ -1284,7 +1284,7 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast TokenPos pos = f->token.pos; Type *this_type = base_type(e->type); Type *other_type = base_type(f->type); - if (!are_types_identical(this_type, other_type)) { + if (!signature_parameter_similar_enough(this_type, other_type)) { error(e->token, "Foreign entity '%.*s' previously declared elsewhere with a different type\n" "\tat %s", diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index d56e5e212..6897701d6 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1934,7 +1934,7 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f TokenPos pos = f->token.pos; Type *this_type = base_type(e->type); Type *other_type = base_type(f->type); - if (!are_types_identical(this_type, other_type)) { + if (!signature_parameter_similar_enough(this_type, other_type)) { error(e->token, "Foreign entity '%.*s' previously declared elsewhere with a different type\n" "\tat %s", -- cgit v1.2.3 From 5f001f6d5138d61fbb4900c951e2ccb12894d5ed Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 22 Feb 2024 18:15:13 +0000 Subject: Allow casting between a `bit_field` and its backing type --- src/check_decl.cpp | 1 + src/check_expr.cpp | 7 +++++++ src/llvm_backend_expr.cpp | 18 ++++++++++++++++++ 3 files changed, 26 insertions(+) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 3ccf1b97a..2c0f7a7b8 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -210,6 +210,7 @@ gb_internal bool is_type_distinct(Ast *node) { case Ast_UnionType: case Ast_EnumType: case Ast_ProcType: + case Ast_BitFieldType: return true; case Ast_PointerType: diff --git a/src/check_expr.cpp b/src/check_expr.cpp index aba2f5831..792e5b43c 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2908,6 +2908,13 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type } } + if (is_type_bit_field(src)) { + return are_types_identical(core_type(src->BitField.backing_type), dst); + } + if (is_type_bit_field(dst)) { + return are_types_identical(src, core_type(dst->BitField.backing_type)); + } + if (is_type_integer(src) && is_type_rune(dst)) { return true; } diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 6bef21822..7b31ca989 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1946,6 +1946,24 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { } } + // bit_field <-> backing type + if (is_type_bit_field(src)) { + if (are_types_identical(src->BitField.backing_type, dst)) { + lbValue res = {}; + res.type = t; + res.value = value.value; + return res; + } + } + if (is_type_bit_field(dst)) { + if (are_types_identical(src, dst->BitField.backing_type)) { + lbValue res = {}; + res.type = t; + res.value = value.value; + return res; + } + } + // Pointer <-> uintptr if (is_type_pointer(src) && is_type_uintptr(dst)) { -- cgit v1.2.3 From 517d7ae0b0fd400ceb6a213e7d644c19b8088bfd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 23 Mar 2024 17:51:56 +0000 Subject: Add error block around `error_line` calls --- src/check_builtin.cpp | 3 +++ src/check_decl.cpp | 1 + src/check_expr.cpp | 5 +++++ src/check_stmt.cpp | 5 +++++ src/checker.cpp | 12 +++++++++++- src/parser.cpp | 6 +++--- 6 files changed, 28 insertions(+), 4 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index d3158961e..53e4acbd1 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -89,6 +89,7 @@ gb_internal void check_or_else_split_types(CheckerContext *c, Operand *x, String gb_internal void check_or_else_expr_no_value_error(CheckerContext *c, String const &name, Operand const &x, Type *type_hint) { + ERROR_BLOCK(); gbString t = type_to_string(x.type); error(x.expr, "'%.*s' does not return a value, value is of type %s", LIT(name), t); if (is_type_union(type_deref(x.type))) { @@ -1565,6 +1566,7 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o } if (!operand->value.value_bool) { + ERROR_BLOCK(); gbString arg1 = expr_to_string(ce->args[0]); gbString arg2 = {}; @@ -1590,6 +1592,7 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o operand->type = t_untyped_bool; operand->mode = Addressing_Constant; } else if (name == "panic") { + ERROR_BLOCK(); if (ce->args.count != 1) { error(call, "'#panic' expects 1 argument, got %td", ce->args.count); return false; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 2c0f7a7b8..952a877a4 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1630,6 +1630,7 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de Entity *uvar = entry.uvar; Entity *prev = scope_insert_no_mutex(ctx->scope, uvar); if (prev != nullptr) { + ERROR_BLOCK(); error(e->token, "Namespace collision while 'using' procedure argument '%.*s' of: %.*s", LIT(e->token.string), LIT(prev->token.string)); error_line("%.*s != %.*s\n", LIT(uvar->token.string), LIT(prev->token.string)); break; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 80008d73a..ecc8a804c 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5905,6 +5905,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A s = assign_score_function(MAXIMUM_TYPE_DISTANCE); } else { if (show_error) { + ERROR_BLOCK(); check_assignment(c, o, param_type, str_lit("procedure argument")); Type *src = base_type(o->type); @@ -8459,6 +8460,7 @@ gb_internal ExprKind check_or_return_expr(CheckerContext *c, Operand *o, Ast *no // NOTE(bill): allow implicit conversion between boolean types // within 'or_return' to improve the experience using third-party code } else if (!check_is_assignable_to(c, &rhs, end_type)) { + ERROR_BLOCK(); // TODO(bill): better error message gbString a = type_to_string(right_type); gbString b = type_to_string(end_type); @@ -10030,6 +10032,7 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, bool ok = check_index_value(c, t, false, ie->index, max_count, &index, index_type_hint); if (is_const) { if (index < 0) { + ERROR_BLOCK(); gbString str = expr_to_string(o->expr); error(o->expr, "Cannot index a constant '%s'", str); if (!build_context.terse_errors) { @@ -10046,6 +10049,7 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, bool finish = false; o->value = get_constant_field_single(c, value, cast(i32)index, &success, &finish); if (!success) { + ERROR_BLOCK(); gbString str = expr_to_string(o->expr); error(o->expr, "Cannot index a constant '%s' with index %lld", str, cast(long long)index); if (!build_context.terse_errors) { @@ -10236,6 +10240,7 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node, } } if (!all_constant) { + ERROR_BLOCK(); gbString str = expr_to_string(o->expr); error(o->expr, "Cannot slice '%s' with non-constant indices", str); if (!build_context.terse_errors) { diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index d34695a3a..1d7e7d4e9 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -883,6 +883,7 @@ gb_internal void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod } if (ctx->inline_for_depth >= MAX_INLINE_FOR_DEPTH && prev_inline_for_depth < MAX_INLINE_FOR_DEPTH) { + ERROR_BLOCK(); if (prev_inline_for_depth > 0) { error(node, "Nested '#unroll for' loop cannot be inlined as it exceeds the maximum '#unroll for' depth (%lld levels >= %lld maximum levels)", v, MAX_INLINE_FOR_DEPTH); } else { @@ -1592,6 +1593,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { isize count = t->Tuple.variables.count; if (count < 1 || count > 3) { + ERROR_BLOCK(); check_not_tuple(ctx, &operand); error_line("\tMultiple return valued parameters in a range statement are limited to a maximum of 2 usable values with a trailing boolean for the conditional\n"); break; @@ -2085,6 +2087,9 @@ gb_internal void check_expr_stmt(CheckerContext *ctx, Ast *node) { } return; } + + ERROR_BLOCK(); + gbString expr_str = expr_to_string(operand.expr); error(node, "Expression is not used: '%s'", expr_str); gb_string_free(expr_str); diff --git a/src/checker.cpp b/src/checker.cpp index bf6a84588..6456cab0c 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3180,6 +3180,7 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { linkage == "link_once") { ac->linkage = linkage; } else { + ERROR_BLOCK(); error(elem, "Invalid linkage '%.*s'. Valid kinds:", LIT(linkage)); error_line("\tinternal\n"); error_line("\tstrong\n"); @@ -3428,6 +3429,7 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { } else if (mode == "speed") { ac->optimization_mode = ProcedureOptimizationMode_Speed; } else { + ERROR_BLOCK(); error(elem, "Invalid optimization_mode for '%.*s'. Valid modes:", LIT(name)); error_line("\tnone\n"); error_line("\tminimal\n"); @@ -3558,6 +3560,7 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) { model == "localexec") { ac->thread_local_model = model; } else { + ERROR_BLOCK(); error(elem, "Invalid thread local model '%.*s'. Valid models:", LIT(model)); error_line("\tdefault\n"); error_line("\tlocaldynamic\n"); @@ -3608,6 +3611,7 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) { linkage == "link_once") { ac->linkage = linkage; } else { + ERROR_BLOCK(); error(elem, "Invalid linkage '%.*s'. Valid kinds:", LIT(linkage)); error_line("\tinternal\n"); error_line("\tstrong\n"); @@ -3762,6 +3766,7 @@ gb_internal void check_decl_attributes(CheckerContext *c, Array const &at if (!proc(c, elem, name, value, ac)) { if (!build_context.ignore_unknown_attributes) { + ERROR_BLOCK(); error(elem, "Unknown attribute element name '%.*s'", LIT(name)); error_line("\tDid you forget to use build flag '-ignore-unknown-attributes'?\n"); } @@ -3831,6 +3836,8 @@ gb_internal bool check_arity_match(CheckerContext *c, AstValueDecl *vd, bool is_ gb_string_free(str); return false; } else if (is_global) { + ERROR_BLOCK(); + Ast *n = vd->values[rhs-1]; error(n, "Expected %td expressions on the right hand side, got %td", lhs, rhs); error_line("Note: Global declarations do not allow for multi-valued expressions"); @@ -6052,11 +6059,14 @@ gb_internal void check_unique_package_names(Checker *c) { continue; } + + begin_error_block(); error(curr, "Duplicate declaration of 'package %.*s'", LIT(name)); error_line("\tA package name must be unique\n" "\tThere is no relation between a package name and the directory that contains it, so they can be completely different\n" "\tA package name is required for link name prefixing to have a consistent ABI\n"); - error(prev, "found at previous location"); + error_line("%s found at previous location\n", token_pos_to_string(ast_token(prev).pos)); + end_error_block(); } } diff --git a/src/parser.cpp b/src/parser.cpp index bb9a526fe..b4a2e060c 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -6295,7 +6295,7 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) { if (!path_is_directory(init_fullpath)) { String const ext = str_lit(".odin"); if (!string_ends_with(init_fullpath, ext)) { - error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename)); + error({}, "Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename)); return ParseFile_WrongExtension; } } else if (init_fullpath.len != 0) { @@ -6308,7 +6308,7 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) { String short_path = filename_from_path(path); char *cpath = alloc_cstring(temporary_allocator(), short_path); if (gb_file_exists(cpath)) { - error_line("Please specify the executable name with -out: as a directory exists with the same name in the current working directory"); + error({}, "Please specify the executable name with -out: as a directory exists with the same name in the current working directory"); return ParseFile_DirectoryAlreadyExists; } } @@ -6344,7 +6344,7 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) { if (!path_is_directory(fullpath)) { String const ext = str_lit(".odin"); if (!string_ends_with(fullpath, ext)) { - error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(fullpath)); + error({}, "Expected either a directory or a .odin file, got '%.*s'\n", LIT(fullpath)); return ParseFile_WrongExtension; } } -- cgit v1.2.3 From 25f1d0906d2b5a8276c3832783970a798c12cc6c Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Wed, 1 May 2024 22:12:37 +0200 Subject: compiler: improve target features support --- .gitignore | 4 +- base/intrinsics/intrinsics.odin | 10 +- core/simd/x86/sse3.odin | 4 +- core/simd/x86/sse41.odin | 4 +- core/sync/futex_wasm.odin | 44 ++- misc/featuregen/README.md | 28 ++ misc/featuregen/featuregen.cpp | 37 +++ misc/featuregen/featuregen.py | 116 ++++++++ src/build_settings.cpp | 617 ++++++++++++++++++++++++++++++++++------ src/check_builtin.cpp | 46 ++- src/check_decl.cpp | 49 +++- src/check_expr.cpp | 68 ++++- src/checker_builtin_procs.hpp | 4 + src/entity.cpp | 4 +- src/llvm_backend.cpp | 104 +++---- src/llvm_backend_proc.cpp | 27 +- src/llvm_backend_utility.cpp | 3 +- src/main.cpp | 79 ++++- src/types.cpp | 22 ++ 19 files changed, 1066 insertions(+), 204 deletions(-) create mode 100644 misc/featuregen/README.md create mode 100644 misc/featuregen/featuregen.cpp create mode 100644 misc/featuregen/featuregen.py (limited to 'src/check_decl.cpp') diff --git a/.gitignore b/.gitignore index f6c3927a2..2b6b5281a 100644 --- a/.gitignore +++ b/.gitignore @@ -322,4 +322,6 @@ build.sh !core/debug/ # RAD debugger project file -*.raddbg \ No newline at end of file +*.raddbg + +misc/featuregen/featuregen diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index dca33bfd9..d887f8dcc 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -282,6 +282,12 @@ 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 --- +// Checks if the current target supports the given target features. +// +// Takes a constant comma-seperated string (eg: "sha512,sse4.1"), or a procedure type which has either +// `@(require_target_feature)` or `@(enable_target_feature)` as its input and returns a boolean indicating +// if all listed features are supported. +has_target_feature :: proc($test: $T) -> bool where type_is_string(T) || type_is_proc(T) --- // WASM targets only wasm_memory_grow :: proc(index, delta: uintptr) -> int --- @@ -293,9 +299,9 @@ wasm_memory_size :: proc(index: uintptr) -> int --- // 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 -@(enable_target_feature="atomics") +@(require_target_feature="atomics") wasm_memory_atomic_wait32 :: proc(ptr: ^u32, expected: u32, timeout_ns: i64) -> u32 --- -@(enable_target_feature="atomics") +@(require_target_feature="atomics") wasm_memory_atomic_notify32 :: proc(ptr: ^u32, waiters: u32) -> (waiters_woken_up: u32) --- // x86 Targets (i386, amd64) diff --git a/core/simd/x86/sse3.odin b/core/simd/x86/sse3.odin index cf5f3b2fa..ca19c3954 100644 --- a/core/simd/x86/sse3.odin +++ b/core/simd/x86/sse3.odin @@ -36,7 +36,7 @@ _mm_lddqu_si128 :: #force_inline proc "c" (mem_addr: ^__m128i) -> __m128i { _mm_movedup_pd :: #force_inline proc "c" (a: __m128d) -> __m128d { return simd.shuffle(a, a, 0, 0) } -@(require_results, enable_target_feature="sse3") +@(require_results, enable_target_feature="sse2,sse3") _mm_loaddup_pd :: #force_inline proc "c" (mem_addr: [^]f64) -> __m128d { return _mm_load1_pd(mem_addr) } @@ -65,4 +65,4 @@ foreign _ { hsubps :: proc(a, b: __m128) -> __m128 --- @(link_name = "llvm.x86.sse3.ldu.dq") lddqu :: proc(mem_addr: rawptr) -> i8x16 --- -} \ No newline at end of file +} diff --git a/core/simd/x86/sse41.odin b/core/simd/x86/sse41.odin index 8c306ba4c..0b9c5986f 100644 --- a/core/simd/x86/sse41.odin +++ b/core/simd/x86/sse41.odin @@ -268,7 +268,7 @@ _mm_testnzc_si128 :: #force_inline proc "c" (a: __m128i, mask: __m128i) -> i32 { _mm_test_all_zeros :: #force_inline proc "c" (a: __m128i, mask: __m128i) -> i32 { return _mm_testz_si128(a, mask) } -@(require_results, enable_target_feature="sse4.1") +@(require_results, enable_target_feature="sse2,sse4.1") _mm_test_all_ones :: #force_inline proc "c" (a: __m128i) -> i32 { return _mm_testc_si128(a, _mm_cmpeq_epi32(a, a)) } @@ -349,4 +349,4 @@ foreign _ { ptestc :: proc(a, mask: i64x2) -> i32 --- @(link_name = "llvm.x86.sse41.ptestnzc") ptestnzc :: proc(a, mask: i64x2) -> i32 --- -} \ No newline at end of file +} diff --git a/core/sync/futex_wasm.odin b/core/sync/futex_wasm.odin index de1013364..de88e8198 100644 --- a/core/sync/futex_wasm.odin +++ b/core/sync/futex_wasm.odin @@ -5,31 +5,49 @@ package sync import "base:intrinsics" import "core:time" +// NOTE: because `core:sync` is in the dependency chain of a lot of the core packages (mostly through `core:mem`) +// without actually calling into it much, I opted for a runtime panic instead of a compile error here. + _futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool { - s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, -1) - return s != 0 + when !intrinsics.has_target_feature("atomics") { + _panic("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it") + } else { + s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, -1) + return s != 0 + } } _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool { - s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, i64(duration)) - return s != 0 - + when !intrinsics.has_target_feature("atomics") { + _panic("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it") + } else { + s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, i64(duration)) + return s != 0 + } } _futex_signal :: proc "contextless" (f: ^Futex) { - loop: for { - s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), 1) - if s >= 1 { - return + when !intrinsics.has_target_feature("atomics") { + _panic("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it") + } else { + loop: for { + s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), 1) + if s >= 1 { + return + } } } } _futex_broadcast :: proc "contextless" (f: ^Futex) { - loop: for { - s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), ~u32(0)) - if s >= 0 { - return + when !intrinsics.has_target_feature("atomics") { + _panic("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it") + } else { + loop: for { + s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), ~u32(0)) + if s >= 0 { + return + } } } } diff --git a/misc/featuregen/README.md b/misc/featuregen/README.md new file mode 100644 index 000000000..22a798cca --- /dev/null +++ b/misc/featuregen/README.md @@ -0,0 +1,28 @@ +# Featuregen + +This directory contains a python and CPP script that generates the needed information +for features regarding microarchitecture and target features of the compiler. + +It is not pretty! But LLVM has no way to query this information with their C API. + +It generates these globals (intended for `src/build_settings.cpp`: + +- `target_microarch_list`: an array of strings indexed by the architecture, each string is a comma-seperated list of microarchitectures available on that architecture +- `target_features_list`: an array of strings indexed by the architecture, each string is a comma-seperated list of target features available on that architecture +- `target_microarch_counts`: an array of ints indexed by the architecture, each int represents the amount of microarchitectures available on that target, intended for easier iteration of the next global +- `microarch_features_list`: an array of a tuple like struct where the first string is a microarchitecture and the second is a comma-seperated list of all features that are enabled by default for it + +In order to get the default features for a microarchitecture there is a small CPP program that takes +a target triple and microarchitecture and spits out the default features, this is then parsed by the python script. + +This should be ran each time we update LLVM to stay in sync. + +If there are minor differences (like the Odin user using LLVM 14 and this table being generated on LLVM 17) it +does not impact much at all, the only thing it will do is make LLVM print a message that the feature is ignored (if it was added between 14 and 17 in this case). + +## Usage + +1. Make sure the table of architectures at the top of the python script is up-to-date (the triple can be any valid triple for the architecture) +1. `./build.sh` +1. `python3 featuregen.py` +1. Copy the output into `src/build_settings.cpp` diff --git a/misc/featuregen/featuregen.cpp b/misc/featuregen/featuregen.cpp new file mode 100644 index 000000000..a1d00ab31 --- /dev/null +++ b/misc/featuregen/featuregen.cpp @@ -0,0 +1,37 @@ +#include +#include +#include +#include +#include +#include + +// Dumps the default set of supported features for the given microarch. +int main(int argc, char **argv) { + if (argc < 3) { + llvm::errs() << "Error: first arg should be triple, second should be microarch\n"; + return 1; + } + + llvm::InitializeAllTargets(); + llvm::InitializeAllTargetMCs(); + + std::string error; + const llvm::Target* target = llvm::TargetRegistry::lookupTarget(argv[1], error); + + if (!target) { + llvm::errs() << "Error: " << error << "\n"; + return 1; + } + + auto STI = target->createMCSubtargetInfo(argv[1], argv[2], ""); + + std::string plus = "+"; + llvm::ArrayRef features = STI->getAllProcessorFeatures(); + for (const auto& feature : features) { + if (STI->checkFeatures(plus + feature.Key)) { + llvm::outs() << feature.Key << "\n"; + } + } + + return 0; +} diff --git a/misc/featuregen/featuregen.py b/misc/featuregen/featuregen.py new file mode 100644 index 000000000..da4cc68f5 --- /dev/null +++ b/misc/featuregen/featuregen.py @@ -0,0 +1,116 @@ +import subprocess +import tempfile +import os +import sys + +archs = [ + ("amd64", "linux_amd64", "x86_64-pc-linux-gnu", [], []), + ("i386", "linux_i386", "i386-pc-linux-gnu", [], []), + ("arm32", "linux_arm32", "arm-linux-gnu", [], []), + ("arm64", "linux_arm64", "aarch64-linux-elf", [], []), + ("wasm32", "js_wasm32", "wasm32-js-js", [], []), + ("wasm64p32", "js_wasm64p32","wasm32-js-js", [], []), +]; + +SEEKING_CPUS = 0 +PARSING_CPUS = 1 +PARSING_FEATURES = 2 + +with tempfile.NamedTemporaryFile(suffix=".odin", delete=True) as temp_file: + temp_file.write(b"package main\n") + + for arch, target, triple, cpus, features in archs: + cmd = ["odin", "build", temp_file.name, "-file", "-build-mode:llvm", "-out:temp", "-target-features:\"help\"", f"-target:\"{target}\""] + process = subprocess.Popen(cmd, stderr=subprocess.PIPE, text=True) + + state = SEEKING_CPUS + for line in process.stderr: + + if state == SEEKING_CPUS: + if line == "Available CPUs for this target:\n": + state = PARSING_CPUS + + elif state == PARSING_CPUS: + if line == "Available features for this target:\n": + state = PARSING_FEATURES + continue + + parts = line.split(" -", maxsplit=1) + if len(parts) < 2: + continue + + cpu = parts[0].strip() + cpus.append(cpu) + + elif state == PARSING_FEATURES: + if line == "\n" and len(features) > 0: + break + + parts = line.split(" -", maxsplit=1) + if len(parts) < 2: + continue + + feature = parts[0].strip() + features.append(feature) + + process.wait() + if process.returncode != 0: + print(f"odin build returned with non-zero exit code {process.returncode}") + sys.exit(1) + + os.remove("temp.ll") + +def print_default_features(triple, microarch): + cmd = ["./featuregen", triple, microarch] + process = subprocess.Popen(cmd, stdout=subprocess.PIPE, text=True) + first = True + for line in process.stdout: + print("" if first else ",", line.strip(), sep="", end="") + first = False + process.wait() + if process.returncode != 0: + print(f"featuregen returned with non-zero exit code {process.returncode}") + sys.exit(1) + +print("// Generated with the featuregen script in `misc/featuregen`") +print("gb_global String target_microarch_list[TargetArch_COUNT] = {") +print("\t// TargetArch_Invalid:") +print('\tstr_lit(""),') +for arch, target, triple, cpus, features in archs: + print(f"\t// TargetArch_{arch}:") + print(f'\tstr_lit("{','.join(cpus)}"),') +print("};") + +print("") + +print("// Generated with the featuregen script in `misc/featuregen`") +print("gb_global String target_features_list[TargetArch_COUNT] = {") +print("\t// TargetArch_Invalid:") +print('\tstr_lit(""),') +for arch, target, triple, cpus, features in archs: + print(f"\t// TargetArch_{arch}:") + print(f'\tstr_lit("{','.join(features)}"),') +print("};") + +print("") + +print("// Generated with the featuregen script in `misc/featuregen`") +print("gb_global int target_microarch_counts[TargetArch_COUNT] = {") +print("\t// TargetArch_Invalid:") +print("\t0,") +for arch, target, triple, cpus, feature in archs: + print(f"\t// TargetArch_{arch}:") + print(f"\t{len(cpus)},") +print("};") + +print("") + +print("// Generated with the featuregen script in `misc/featuregen`") +print("gb_global MicroarchFeatureList microarch_features_list[] = {") +for arch, target, triple, cpus, features in archs: + print(f"\t// TargetArch_{arch}:") + for cpu in cpus: + print(f'\t{{ str_lit("{cpu}"), str_lit("', end="") + print_default_features(triple, cpu) + print('") },') +print("};") diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 8509394ff..8ad03b1b9 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -71,6 +71,11 @@ enum Windows_Subsystem : u8 { Windows_Subsystem_COUNT, }; +struct MicroarchFeatureList { + String microarch; + String features; +}; + gb_global String target_os_names[TargetOs_COUNT] = { str_lit(""), str_lit("windows"), @@ -97,21 +102,467 @@ gb_global String target_arch_names[TargetArch_COUNT] = { str_lit("wasm64p32"), }; +// Generated with the featuregen script in `misc/featuregen` gb_global String target_microarch_list[TargetArch_COUNT] = { - // TargetArch_Invalid, - str_lit("Invalid!"), - // TargetArch_amd64, - str_lit("alderlake,amdfam10,athlon-fx,athlon64,athlon64-sse3,atom_sse4_2,atom_sse4_2_movbe,barcelona,bdver1,bdver2,bdver3,bdver4,broadwell,btver1,btver2,cannonlake,cascadelake,cooperlake,core-avx-i,core-avx2,core2,core_2_duo_sse4_1,core_2_duo_ssse3,core_2nd_gen_avx,core_3rd_gen_avx,core_4th_gen_avx,core_4th_gen_avx_tsx,core_5th_gen_avx,core_5th_gen_avx_tsx,core_aes_pclmulqdq,core_i7_sse4_2,corei7,corei7-avx,generic,goldmont,goldmont-plus,goldmont_plus,grandridge,graniterapids,graniterapids-d,graniterapids_d,haswell,icelake-client,icelake-server,icelake_client,icelake_server,ivybridge,k8,k8-sse3,knl,knm,meteorlake,mic_avx512,native,nehalem,nocona,opteron,opteron-sse3,penryn,raptorlake,rocketlake,sandybridge,sapphirerapids,sierraforest,silvermont,skx,skylake,skylake-avx512,skylake_avx512,slm,tigerlake,tremont,westmere,x86-64,x86-64-v2,x86-64-v3,x86-64-v4,znver1,znver2,znver3,znver4"), - // TargetArch_i386, - str_lit("athlon,athlon-4,athlon-mp,athlon-tbird,athlon-xp,atom,bonnell,c3,c3-2,generic,geode,i386,i486,i586,i686,k6,k6-2,k6-3,lakemont,native,pentium,pentium-m,pentium-mmx,pentium2,pentium3,pentium3m,pentium4,pentium4m,pentium_4,pentium_4_sse3,pentium_ii,pentium_iii,pentium_iii_no_xmm_regs,pentium_m,pentium_mmx,pentium_pro,pentiumpro,prescott,winchip-c6,winchip2,yonah"), - // TargetArch_arm32, - str_lit("arm1020e,arm1020t,arm1022e,arm10e,arm10tdmi,arm1136j-s,arm1136jf-s,arm1156t2-s,arm1156t2f-s,arm1176jz-s,arm1176jzf-s,arm710t,arm720t,arm7tdmi,arm7tdmi-s,arm8,arm810,arm9,arm920,arm920t,arm922t,arm926ej-s,arm940t,arm946e-s,arm966e-s,arm968e-s,arm9e,arm9tdmi,cortex-a12,cortex-a15,cortex-a17,cortex-a32,cortex-a35,cortex-a5,cortex-a53,cortex-a55,cortex-a57,cortex-a7,cortex-a710,cortex-a72,cortex-a73,cortex-a75,cortex-a76,cortex-a76ae,cortex-a77,cortex-a78,cortex-a78c,cortex-a8,cortex-a9,cortex-m0,cortex-m0plus,cortex-m1,cortex-m23,cortex-m3,cortex-m33,cortex-m35p,cortex-m4,cortex-m55,cortex-m7,cortex-m85,cortex-r4,cortex-r4f,cortex-r5,cortex-r52,cortex-r7,cortex-r8,cortex-x1,cortex-x1c,cyclone,ep9312,exynos-m3,exynos-m4,exynos-m5,generic,iwmmxt,krait,kryo,mpcore,mpcorenovfp,native,neoverse-n1,neoverse-n2,neoverse-v1,sc000,sc300,strongarm,strongarm110,strongarm1100,strongarm1110,swift,xscale"), - // TargetArch_arm64, - str_lit("a64fx,ampere1,ampere1a,apple-a10,apple-a11,apple-a12,apple-a13,apple-a14,apple-a15,apple-a16,apple-a7,apple-a8,apple-a9,apple-latest,apple-m1,apple-m2,apple-s4,apple-s5,carmel,cortex-a34,cortex-a35,cortex-a510,cortex-a53,cortex-a55,cortex-a57,cortex-a65,cortex-a65ae,cortex-a710,cortex-a715,cortex-a72,cortex-a73,cortex-a75,cortex-a76,cortex-a76ae,cortex-a77,cortex-a78,cortex-a78c,cortex-r82,cortex-x1,cortex-x1c,cortex-x2,cortex-x3,cyclone,exynos-m3,exynos-m4,exynos-m5,falkor,generic,kryo,native,neoverse-512tvb,neoverse-e1,neoverse-n1,neoverse-n2,neoverse-v1,neoverse-v2,saphira,thunderx,thunderx2t99,thunderx3t110,thunderxt81,thunderxt83,thunderxt88,tsv110"), - // TargetArch_wasm32, - str_lit("generic"), - // TargetArch_wasm64p32, - str_lit("generic"), + // TargetArch_Invalid: + str_lit(""), + // TargetArch_amd64: + str_lit("alderlake,amdfam10,athlon,athlon-4,athlon-fx,athlon-mp,athlon-tbird,athlon-xp,athlon64,athlon64-sse3,atom,atom_sse4_2,atom_sse4_2_movbe,barcelona,bdver1,bdver2,bdver3,bdver4,bonnell,broadwell,btver1,btver2,c3,c3-2,cannonlake,cascadelake,cooperlake,core-avx-i,core-avx2,core2,core_2_duo_sse4_1,core_2_duo_ssse3,core_2nd_gen_avx,core_3rd_gen_avx,core_4th_gen_avx,core_4th_gen_avx_tsx,core_5th_gen_avx,core_5th_gen_avx_tsx,core_aes_pclmulqdq,core_i7_sse4_2,corei7,corei7-avx,emeraldrapids,generic,geode,goldmont,goldmont-plus,goldmont_plus,grandridge,graniterapids,graniterapids-d,graniterapids_d,haswell,i386,i486,i586,i686,icelake-client,icelake-server,icelake_client,icelake_server,ivybridge,k6,k6-2,k6-3,k8,k8-sse3,knl,knm,lakemont,meteorlake,mic_avx512,nehalem,nocona,opteron,opteron-sse3,penryn,pentium,pentium-m,pentium-mmx,pentium2,pentium3,pentium3m,pentium4,pentium4m,pentium_4,pentium_4_sse3,pentium_ii,pentium_iii,pentium_iii_no_xmm_regs,pentium_m,pentium_mmx,pentium_pro,pentiumpro,prescott,raptorlake,rocketlake,sandybridge,sapphirerapids,sierraforest,silvermont,skx,skylake,skylake-avx512,skylake_avx512,slm,tigerlake,tremont,westmere,winchip-c6,winchip2,x86-64,x86-64-v2,x86-64-v3,x86-64-v4,yonah,znver1,znver2,znver3,znver4"), + // TargetArch_i386: + str_lit("alderlake,amdfam10,athlon,athlon-4,athlon-fx,athlon-mp,athlon-tbird,athlon-xp,athlon64,athlon64-sse3,atom,atom_sse4_2,atom_sse4_2_movbe,barcelona,bdver1,bdver2,bdver3,bdver4,bonnell,broadwell,btver1,btver2,c3,c3-2,cannonlake,cascadelake,cooperlake,core-avx-i,core-avx2,core2,core_2_duo_sse4_1,core_2_duo_ssse3,core_2nd_gen_avx,core_3rd_gen_avx,core_4th_gen_avx,core_4th_gen_avx_tsx,core_5th_gen_avx,core_5th_gen_avx_tsx,core_aes_pclmulqdq,core_i7_sse4_2,corei7,corei7-avx,emeraldrapids,generic,geode,goldmont,goldmont-plus,goldmont_plus,grandridge,graniterapids,graniterapids-d,graniterapids_d,haswell,i386,i486,i586,i686,icelake-client,icelake-server,icelake_client,icelake_server,ivybridge,k6,k6-2,k6-3,k8,k8-sse3,knl,knm,lakemont,meteorlake,mic_avx512,nehalem,nocona,opteron,opteron-sse3,penryn,pentium,pentium-m,pentium-mmx,pentium2,pentium3,pentium3m,pentium4,pentium4m,pentium_4,pentium_4_sse3,pentium_ii,pentium_iii,pentium_iii_no_xmm_regs,pentium_m,pentium_mmx,pentium_pro,pentiumpro,prescott,raptorlake,rocketlake,sandybridge,sapphirerapids,sierraforest,silvermont,skx,skylake,skylake-avx512,skylake_avx512,slm,tigerlake,tremont,westmere,winchip-c6,winchip2,x86-64,x86-64-v2,x86-64-v3,x86-64-v4,yonah,znver1,znver2,znver3,znver4"), + // TargetArch_arm32: + str_lit("arm1020e,arm1020t,arm1022e,arm10e,arm10tdmi,arm1136j-s,arm1136jf-s,arm1156t2-s,arm1156t2f-s,arm1176jz-s,arm1176jzf-s,arm710t,arm720t,arm7tdmi,arm7tdmi-s,arm8,arm810,arm9,arm920,arm920t,arm922t,arm926ej-s,arm940t,arm946e-s,arm966e-s,arm968e-s,arm9e,arm9tdmi,cortex-a12,cortex-a15,cortex-a17,cortex-a32,cortex-a35,cortex-a5,cortex-a53,cortex-a55,cortex-a57,cortex-a7,cortex-a710,cortex-a72,cortex-a73,cortex-a75,cortex-a76,cortex-a76ae,cortex-a77,cortex-a78,cortex-a78c,cortex-a8,cortex-a9,cortex-m0,cortex-m0plus,cortex-m1,cortex-m23,cortex-m3,cortex-m33,cortex-m35p,cortex-m4,cortex-m55,cortex-m7,cortex-m85,cortex-r4,cortex-r4f,cortex-r5,cortex-r52,cortex-r7,cortex-r8,cortex-x1,cortex-x1c,cyclone,ep9312,exynos-m3,exynos-m4,exynos-m5,generic,iwmmxt,krait,kryo,mpcore,mpcorenovfp,neoverse-n1,neoverse-n2,neoverse-v1,sc000,sc300,strongarm,strongarm110,strongarm1100,strongarm1110,swift,xscale"), + // TargetArch_arm64: + str_lit("a64fx,ampere1,ampere1a,apple-a10,apple-a11,apple-a12,apple-a13,apple-a14,apple-a15,apple-a16,apple-a7,apple-a8,apple-a9,apple-latest,apple-m1,apple-m2,apple-s4,apple-s5,carmel,cortex-a34,cortex-a35,cortex-a510,cortex-a53,cortex-a55,cortex-a57,cortex-a65,cortex-a65ae,cortex-a710,cortex-a715,cortex-a72,cortex-a73,cortex-a75,cortex-a76,cortex-a76ae,cortex-a77,cortex-a78,cortex-a78c,cortex-r82,cortex-x1,cortex-x1c,cortex-x2,cortex-x3,cyclone,exynos-m3,exynos-m4,exynos-m5,falkor,generic,kryo,neoverse-512tvb,neoverse-e1,neoverse-n1,neoverse-n2,neoverse-v1,neoverse-v2,saphira,thunderx,thunderx2t99,thunderx3t110,thunderxt81,thunderxt83,thunderxt88,tsv110"), + // TargetArch_wasm32: + str_lit("bleeding-edge,generic,mvp"), + // TargetArch_wasm64p32: + str_lit("bleeding-edge,generic,mvp"), +}; + +// Generated with the featuregen script in `misc/featuregen` +gb_global String target_features_list[TargetArch_COUNT] = { + // TargetArch_Invalid: + str_lit(""), + // TargetArch_amd64: + str_lit("16bit-mode,32bit-mode,3dnow,3dnowa,64bit,64bit-mode,adx,aes,allow-light-256-bit,amx-bf16,amx-complex,amx-fp16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512er,avx512f,avx512fp16,avx512ifma,avx512pf,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vp2intersect,avx512vpopcntdq,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,branchfusion,cldemote,clflushopt,clwb,clzero,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,ermsb,f16c,false-deps-getmant,false-deps-lzcnt-tzcnt,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-popcnt,false-deps-range,fast-11bytenop,fast-15bytenop,fast-7bytenop,fast-bextr,fast-gather,fast-hops,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fast-vector-shift-masks,faster-shift-than-shuffle,fma,fma4,fsgsbase,fsrm,fxsr,gfni,harden-sls-ijmp,harden-sls-ret,hreset,idivl-to-divb,idivq-to-divl,invpcid,kl,lea-sp,lea-uses-ag,lvi-cfi,lvi-load-hardening,lwp,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,mwaitx,no-bypass-delay,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pad-short-functions,pclmul,pconfig,pku,popcnt,prefer-128-bit,prefer-256-bit,prefer-mask-registers,prefer-movmsk-over-vtest,prefer-no-gather,prefer-no-scatter,prefetchi,prefetchwt1,prfchw,ptwrite,raoint,rdpid,rdpru,rdrnd,rdseed,retpoline,retpoline-external-thunk,retpoline-indirect-branches,retpoline-indirect-calls,rtm,sahf,sbb-dep-breaking,serialize,seses,sgx,sha,sha512,shstk,slow-3ops-lea,slow-incdec,slow-lea,slow-pmaddwd,slow-pmulld,slow-shld,slow-two-mem-ops,slow-unaligned-mem-16,slow-unaligned-mem-32,sm3,sm4,soft-float,sse,sse-unaligned-mem,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,tagged-globals,tbm,tsxldtrk,tuning-fast-imm-vector-shift,uintr,use-glm-div-sqrt-costs,use-slm-arith-costs,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,widekl,x87,xop,xsave,xsavec,xsaveopt,xsaves"), + // TargetArch_i386: + str_lit("16bit-mode,32bit-mode,3dnow,3dnowa,64bit,64bit-mode,adx,aes,allow-light-256-bit,amx-bf16,amx-complex,amx-fp16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512er,avx512f,avx512fp16,avx512ifma,avx512pf,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vp2intersect,avx512vpopcntdq,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,branchfusion,cldemote,clflushopt,clwb,clzero,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,ermsb,f16c,false-deps-getmant,false-deps-lzcnt-tzcnt,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-popcnt,false-deps-range,fast-11bytenop,fast-15bytenop,fast-7bytenop,fast-bextr,fast-gather,fast-hops,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fast-vector-shift-masks,faster-shift-than-shuffle,fma,fma4,fsgsbase,fsrm,fxsr,gfni,harden-sls-ijmp,harden-sls-ret,hreset,idivl-to-divb,idivq-to-divl,invpcid,kl,lea-sp,lea-uses-ag,lvi-cfi,lvi-load-hardening,lwp,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,mwaitx,no-bypass-delay,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pad-short-functions,pclmul,pconfig,pku,popcnt,prefer-128-bit,prefer-256-bit,prefer-mask-registers,prefer-movmsk-over-vtest,prefer-no-gather,prefer-no-scatter,prefetchi,prefetchwt1,prfchw,ptwrite,raoint,rdpid,rdpru,rdrnd,rdseed,retpoline,retpoline-external-thunk,retpoline-indirect-branches,retpoline-indirect-calls,rtm,sahf,sbb-dep-breaking,serialize,seses,sgx,sha,sha512,shstk,slow-3ops-lea,slow-incdec,slow-lea,slow-pmaddwd,slow-pmulld,slow-shld,slow-two-mem-ops,slow-unaligned-mem-16,slow-unaligned-mem-32,sm3,sm4,soft-float,sse,sse-unaligned-mem,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,tagged-globals,tbm,tsxldtrk,tuning-fast-imm-vector-shift,uintr,use-glm-div-sqrt-costs,use-slm-arith-costs,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,widekl,x87,xop,xsave,xsavec,xsaveopt,xsaves"), + // TargetArch_arm32: + str_lit("32bit,8msecext,a12,a15,a17,a32,a35,a5,a53,a55,a57,a7,a72,a73,a75,a76,a77,a78c,a8,a9,aapcs-frame-chain,aapcs-frame-chain-leaf,aclass,acquire-release,aes,armv4,armv4t,armv5t,armv5te,armv5tej,armv6,armv6-m,armv6j,armv6k,armv6kz,armv6s-m,armv6t2,armv7-a,armv7-m,armv7-r,armv7e-m,armv7k,armv7s,armv7ve,armv8-a,armv8-m.base,armv8-m.main,armv8-r,armv8.1-a,armv8.1-m.main,armv8.2-a,armv8.3-a,armv8.4-a,armv8.5-a,armv8.6-a,armv8.7-a,armv8.8-a,armv8.9-a,armv9-a,armv9.1-a,armv9.2-a,armv9.3-a,armv9.4-a,atomics-32,avoid-movs-shop,avoid-partial-cpsr,bf16,big-endian-instructions,cde,cdecp0,cdecp1,cdecp2,cdecp3,cdecp4,cdecp5,cdecp6,cdecp7,cheap-predicable-cpsr,clrbhb,cortex-a710,cortex-a78,cortex-x1,cortex-x1c,crc,crypto,d32,db,dfb,disable-postra-scheduler,dont-widen-vmovs,dotprod,dsp,execute-only,expand-fp-mlx,exynos,fix-cmse-cve-2021-35465,fix-cortex-a57-aes-1742098,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp16fml,fp64,fpao,fpregs,fpregs16,fpregs64,fullfp16,fuse-aes,fuse-literals,harden-sls-blr,harden-sls-nocomdat,harden-sls-retbr,hwdiv,hwdiv-arm,i8mm,iwmmxt,iwmmxt2,krait,kryo,lob,long-calls,loop-align,m3,m7,mclass,mp,muxed-units,mve,mve.fp,mve1beat,mve2beat,mve4beat,nacl-trap,neon,neon-fpmovs,neonfp,neoverse-v1,no-branch-predictor,no-bti-at-return-twice,no-movt,no-neg-immediates,noarm,nonpipelined-vfp,pacbti,perfmon,prefer-ishst,prefer-vmovsr,prof-unpr,r4,r5,r52,r7,ras,rclass,read-tp-tpidrprw,read-tp-tpidruro,read-tp-tpidrurw,reserve-r9,ret-addr-stack,sb,sha2,slow-fp-brcc,slow-load-D-subreg,slow-odd-reg,slow-vdup32,slow-vgetlni32,slowfpvfmx,slowfpvmlx,soft-float,splat-vfp-neon,strict-align,swift,thumb-mode,thumb2,trustzone,use-mipipeliner,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.1m.main,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8.8a,v8.9a,v8m,v8m.main,v9.1a,v9.2a,v9.3a,v9.4a,v9a,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,vldn-align,vmlx-forwarding,vmlx-hazards,wide-stride-vfp,xscale,zcz"), + // TargetArch_arm64: + str_lit("CONTEXTIDREL2,a35,a510,a53,a55,a57,a64fx,a65,a710,a715,a72,a73,a75,a76,a77,a78,a78c,aes,aggressive-fma,all,alternate-sextload-cvt-f32-pattern,altnzcv,am,ampere1,ampere1a,amvs,apple-a10,apple-a11,apple-a12,apple-a13,apple-a14,apple-a15,apple-a16,apple-a7,apple-a7-sysreg,arith-bcc-fusion,arith-cbz-fusion,ascend-store-address,b16b16,balance-fp-ops,bf16,brbe,bti,call-saved-x10,call-saved-x11,call-saved-x12,call-saved-x13,call-saved-x14,call-saved-x15,call-saved-x18,call-saved-x8,call-saved-x9,carmel,ccdp,ccidx,ccpp,chk,clrbhb,cmp-bcc-fusion,complxnum,cortex-r82,cortex-x1,cortex-x2,cortex-x3,crc,crypto,cssc,custom-cheap-as-move,d128,disable-latency-sched-heuristic,dit,dotprod,ecv,el2vmsa,el3,enable-select-opt,ete,exynos-cheap-as-move,exynosm3,exynosm4,f32mm,f64mm,falkor,fgt,fix-cortex-a53-835769,flagm,fmv,force-32bit-jump-tables,fp-armv8,fp16fml,fptoint,fullfp16,fuse-address,fuse-addsub-2reg-const1,fuse-adrp-add,fuse-aes,fuse-arith-logic,fuse-crypto-eor,fuse-csel,fuse-literals,gcs,harden-sls-blr,harden-sls-nocomdat,harden-sls-retbr,hbc,hcx,i8mm,ite,jsconv,kryo,lor,ls64,lse,lse128,lse2,lsl-fast,mec,mops,mpam,mte,neon,neoverse512tvb,neoversee1,neoversen1,neoversen2,neoversev1,neoversev2,nmi,no-bti-at-return-twice,no-neg-immediates,no-sve-fp-ld1r,no-zcz-fp,nv,outline-atomics,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,prfm-slc-target,rand,ras,rasv2,rcpc,rcpc-immo,rcpc3,rdm,reserve-x1,reserve-x10,reserve-x11,reserve-x12,reserve-x13,reserve-x14,reserve-x15,reserve-x18,reserve-x2,reserve-x20,reserve-x21,reserve-x22,reserve-x23,reserve-x24,reserve-x25,reserve-x26,reserve-x27,reserve-x28,reserve-x3,reserve-x30,reserve-x4,reserve-x5,reserve-x6,reserve-x7,reserve-x9,rme,saphira,sb,sel2,sha2,sha3,slow-misaligned-128store,slow-paired-128,slow-strqro-store,sm4,sme,sme-f16f16,sme-f64f64,sme-i16i64,sme2,sme2p1,spe,spe-eef,specres2,specrestrict,ssbs,strict-align,sve,sve2,sve2-aes,sve2-bitperm,sve2-sha3,sve2-sm4,sve2p1,tagged-globals,the,thunderx,thunderx2t99,thunderx3t110,thunderxt81,thunderxt83,thunderxt88,tlb-rmi,tme,tpidr-el1,tpidr-el2,tpidr-el3,tpidrro-el0,tracev8.4,trbe,tsv110,uaops,use-experimental-zeroing-pseudos,use-postra-scheduler,use-reciprocal-square-root,use-scalar-inc-vl,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8.8a,v8.9a,v8a,v8r,v9.1a,v9.2a,v9.3a,v9.4a,v9a,vh,wfxt,xs,zcm,zcz,zcz-fp-workaround,zcz-gp"), + // TargetArch_wasm32: + str_lit("atomics,bulk-memory,exception-handling,extended-const,multivalue,mutable-globals,nontrapping-fptoint,reference-types,relaxed-simd,sign-ext,simd128,tail-call"), + // TargetArch_wasm64p32: + str_lit("atomics,bulk-memory,exception-handling,extended-const,multivalue,mutable-globals,nontrapping-fptoint,reference-types,relaxed-simd,sign-ext,simd128,tail-call"), +}; + +// Generated with the featuregen script in `misc/featuregen` +gb_global int target_microarch_counts[TargetArch_COUNT] = { + // TargetArch_Invalid: + 0, + // TargetArch_amd64: + 120, + // TargetArch_i386: + 120, + // TargetArch_arm32: + 90, + // TargetArch_arm64: + 63, + // TargetArch_wasm32: + 3, + // TargetArch_wasm64p32: + 3, +}; + +// Generated with the featuregen script in `misc/featuregen` +gb_global MicroarchFeatureList microarch_features_list[] = { + // TargetArch_amd64: + { str_lit("alderlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("amdfam10"), str_lit("3dnow,3dnowa,64bit,64bit-mode,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,lzcnt,mmx,nopl,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4a,vzeroupper,x87") }, + { str_lit("athlon"), str_lit("3dnow,3dnowa,64bit-mode,cmov,cx8,mmx,nopl,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("athlon-4"), str_lit("3dnow,3dnowa,64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("athlon-fx"), str_lit("3dnow,3dnowa,64bit,64bit-mode,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("athlon-mp"), str_lit("3dnow,3dnowa,64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("athlon-tbird"), str_lit("3dnow,3dnowa,64bit-mode,cmov,cx8,mmx,nopl,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("athlon-xp"), str_lit("3dnow,3dnowa,64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("athlon64"), str_lit("3dnow,3dnowa,64bit,64bit-mode,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("athlon64-sse3"), str_lit("3dnow,3dnowa,64bit,64bit-mode,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") }, + { str_lit("atom"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fxsr,idivl-to-divb,idivq-to-divl,lea-sp,lea-uses-ag,mmx,movbe,no-bypass-delay,nopl,pad-short-functions,sahf,slow-two-mem-ops,slow-unaligned-mem-16,sse,sse2,sse3,ssse3,vzeroupper,x87") }, + { str_lit("atom_sse4_2"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-7bytenop,fast-movbe,fxsr,idivq-to-divl,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,sahf,slow-incdec,slow-lea,slow-pmulld,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-slm-arith-costs,vzeroupper,x87") }, + { str_lit("atom_sse4_2_movbe"), str_lit("64bit,64bit-mode,aes,clflushopt,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-7bytenop,fast-movbe,fsgsbase,fxsr,idivq-to-divl,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-pmulld,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-slm-arith-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("barcelona"), str_lit("3dnow,3dnowa,64bit,64bit-mode,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,lzcnt,mmx,nopl,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4a,vzeroupper,x87") }, + { str_lit("bdver1"), str_lit("64bit,64bit-mode,aes,avx,branchfusion,cmov,crc32,cx16,cx8,fast-11bytenop,fast-scalar-shift-masks,fma4,fxsr,lwp,lzcnt,mmx,nopl,pclmul,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vzeroupper,x87,xop,xsave") }, + { str_lit("bdver2"), str_lit("64bit,64bit-mode,aes,avx,bmi,branchfusion,cmov,crc32,cx16,cx8,f16c,fast-11bytenop,fast-bextr,fast-movbe,fast-scalar-shift-masks,fma,fma4,fxsr,lwp,lzcnt,mmx,nopl,pclmul,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,tbm,vzeroupper,x87,xop,xsave") }, + { str_lit("bdver3"), str_lit("64bit,64bit-mode,aes,avx,bmi,branchfusion,cmov,crc32,cx16,cx8,f16c,fast-11bytenop,fast-bextr,fast-movbe,fast-scalar-shift-masks,fma,fma4,fsgsbase,fxsr,lwp,lzcnt,mmx,nopl,pclmul,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,tbm,vzeroupper,x87,xop,xsave,xsaveopt") }, + { str_lit("bdver4"), str_lit("64bit,64bit-mode,aes,avx,avx2,bmi,bmi2,branchfusion,cmov,crc32,cx16,cx8,f16c,fast-11bytenop,fast-bextr,fast-movbe,fast-scalar-shift-masks,fma,fma4,fsgsbase,fxsr,lwp,lzcnt,mmx,movbe,mwaitx,nopl,pclmul,popcnt,prfchw,rdrnd,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,tbm,vzeroupper,x87,xop,xsave,xsaveopt") }, + { str_lit("bonnell"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fxsr,idivl-to-divb,idivq-to-divl,lea-sp,lea-uses-ag,mmx,movbe,no-bypass-delay,nopl,pad-short-functions,sahf,slow-two-mem-ops,slow-unaligned-mem-16,sse,sse2,sse3,ssse3,vzeroupper,x87") }, + { str_lit("broadwell"), str_lit("64bit,64bit-mode,adx,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") }, + { str_lit("btver1"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fast-15bytenop,fast-scalar-shift-masks,fast-vector-shift-masks,fxsr,lzcnt,mmx,nopl,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4a,ssse3,vzeroupper,x87") }, + { str_lit("btver2"), str_lit("64bit,64bit-mode,aes,avx,bmi,cmov,crc32,cx16,cx8,f16c,fast-15bytenop,fast-bextr,fast-hops,fast-lzcnt,fast-movbe,fast-scalar-shift-masks,fast-vector-shift-masks,fxsr,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,x87,xsave,xsaveopt") }, + { str_lit("c3"), str_lit("3dnow,64bit-mode,mmx,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("c3-2"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("cannonlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vl,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,sha,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("cascadelake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,avx512vnni,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("cooperlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bf16,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,avx512vnni,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("core-avx-i"), str_lit("64bit,64bit-mode,avx,cmov,crc32,cx16,cx8,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fsgsbase,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") }, + { str_lit("core-avx2"), str_lit("64bit,64bit-mode,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") }, + { str_lit("core2"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fxsr,macrofusion,mmx,nopl,sahf,slow-unaligned-mem-16,sse,sse2,sse3,ssse3,vzeroupper,x87") }, + { str_lit("core_2_duo_sse4_1"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fxsr,macrofusion,mmx,nopl,sahf,slow-unaligned-mem-16,sse,sse2,sse3,sse4.1,ssse3,vzeroupper,x87") }, + { str_lit("core_2_duo_ssse3"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fxsr,macrofusion,mmx,nopl,sahf,slow-unaligned-mem-16,sse,sse2,sse3,ssse3,vzeroupper,x87") }, + { str_lit("core_2nd_gen_avx"), str_lit("64bit,64bit-mode,avx,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") }, + { str_lit("core_3rd_gen_avx"), str_lit("64bit,64bit-mode,avx,cmov,crc32,cx16,cx8,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fsgsbase,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") }, + { str_lit("core_4th_gen_avx"), str_lit("64bit,64bit-mode,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") }, + { str_lit("core_4th_gen_avx_tsx"), str_lit("64bit,64bit-mode,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") }, + { str_lit("core_5th_gen_avx"), str_lit("64bit,64bit-mode,adx,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") }, + { str_lit("core_5th_gen_avx_tsx"), str_lit("64bit,64bit-mode,adx,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") }, + { str_lit("core_aes_pclmulqdq"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,fxsr,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") }, + { str_lit("core_i7_sse4_2"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,fxsr,macrofusion,mmx,no-bypass-delay-mov,nopl,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") }, + { str_lit("corei7"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,fxsr,macrofusion,mmx,no-bypass-delay-mov,nopl,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") }, + { str_lit("corei7-avx"), str_lit("64bit,64bit-mode,avx,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") }, + { str_lit("emeraldrapids"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,amx-bf16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("generic"), str_lit("64bit,64bit-mode,cx8,fast-15bytenop,fast-scalar-fsqrt,idivq-to-divl,macrofusion,slow-3ops-lea,sse,sse2,vzeroupper,x87") }, + { str_lit("geode"), str_lit("3dnow,3dnowa,64bit-mode,cx8,mmx,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("goldmont"), str_lit("64bit,64bit-mode,aes,clflushopt,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-movbe,fsgsbase,fxsr,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-glm-div-sqrt-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("goldmont-plus"), str_lit("64bit,64bit-mode,aes,clflushopt,cmov,crc32,cx16,cx8,fast-movbe,fsgsbase,fxsr,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-glm-div-sqrt-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("goldmont_plus"), str_lit("64bit,64bit-mode,aes,clflushopt,cmov,crc32,cx16,cx8,fast-movbe,fsgsbase,fxsr,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-glm-div-sqrt-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("grandridge"), str_lit("64bit,64bit-mode,adx,aes,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,fast-movbe,fma,fsgsbase,fxsr,gfni,hreset,invpcid,kl,lzcnt,mmx,movbe,movdir64b,movdiri,no-bypass-delay,nopl,pclmul,pconfig,pku,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,uintr,use-glm-div-sqrt-costs,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("graniterapids"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,amx-bf16,amx-fp16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prefetchi,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("graniterapids-d"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,amx-bf16,amx-complex,amx-fp16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prefetchi,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("graniterapids_d"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,amx-bf16,amx-complex,amx-fp16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prefetchi,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("haswell"), str_lit("64bit,64bit-mode,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") }, + { str_lit("i386"), str_lit("64bit-mode,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("i486"), str_lit("64bit-mode,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("i586"), str_lit("64bit-mode,cx8,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("i686"), str_lit("64bit-mode,cmov,cx8,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("icelake-client"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("icelake-server"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("icelake_client"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("icelake_server"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("ivybridge"), str_lit("64bit,64bit-mode,avx,cmov,crc32,cx16,cx8,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fsgsbase,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") }, + { str_lit("k6"), str_lit("64bit-mode,cx8,mmx,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("k6-2"), str_lit("3dnow,64bit-mode,cx8,mmx,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("k6-3"), str_lit("3dnow,64bit-mode,cx8,mmx,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("k8"), str_lit("3dnow,3dnowa,64bit,64bit-mode,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("k8-sse3"), str_lit("3dnow,3dnowa,64bit,64bit-mode,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") }, + { str_lit("knl"), str_lit("64bit,64bit-mode,adx,aes,avx,avx2,avx512cd,avx512er,avx512f,avx512pf,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,fast-gather,fast-movbe,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prefer-mask-registers,prefetchwt1,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,slow-incdec,slow-pmaddwd,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,x87,xsave,xsaveopt") }, + { str_lit("knm"), str_lit("64bit,64bit-mode,adx,aes,avx,avx2,avx512cd,avx512er,avx512f,avx512pf,avx512vpopcntdq,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,fast-gather,fast-movbe,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prefer-mask-registers,prefetchwt1,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,slow-incdec,slow-pmaddwd,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,x87,xsave,xsaveopt") }, + { str_lit("lakemont"), str_lit("64bit-mode,cx8,slow-unaligned-mem-16,sse,sse2,vzeroupper") }, + { str_lit("meteorlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("mic_avx512"), str_lit("64bit,64bit-mode,adx,aes,avx,avx2,avx512cd,avx512er,avx512f,avx512pf,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,fast-gather,fast-movbe,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prefer-mask-registers,prefetchwt1,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,slow-incdec,slow-pmaddwd,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,x87,xsave,xsaveopt") }, + { str_lit("nehalem"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,fxsr,macrofusion,mmx,no-bypass-delay-mov,nopl,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") }, + { str_lit("nocona"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") }, + { str_lit("opteron"), str_lit("3dnow,3dnowa,64bit,64bit-mode,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("opteron-sse3"), str_lit("3dnow,3dnowa,64bit,64bit-mode,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") }, + { str_lit("penryn"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fxsr,macrofusion,mmx,nopl,sahf,slow-unaligned-mem-16,sse,sse2,sse3,sse4.1,ssse3,vzeroupper,x87") }, + { str_lit("pentium"), str_lit("64bit-mode,cx8,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("pentium-m"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("pentium-mmx"), str_lit("64bit-mode,cx8,mmx,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("pentium2"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("pentium3"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("pentium3m"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("pentium4"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("pentium4m"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("pentium_4"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("pentium_4_sse3"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") }, + { str_lit("pentium_ii"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("pentium_iii"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("pentium_iii_no_xmm_regs"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("pentium_m"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("pentium_mmx"), str_lit("64bit-mode,cx8,mmx,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("pentium_pro"), str_lit("64bit-mode,cmov,cx8,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("pentiumpro"), str_lit("64bit-mode,cmov,cx8,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("prescott"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") }, + { str_lit("raptorlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("rocketlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("sandybridge"), str_lit("64bit,64bit-mode,avx,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") }, + { str_lit("sapphirerapids"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,amx-bf16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("sierraforest"), str_lit("64bit,64bit-mode,adx,aes,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,fast-movbe,fma,fsgsbase,fxsr,gfni,hreset,invpcid,kl,lzcnt,mmx,movbe,movdir64b,movdiri,no-bypass-delay,nopl,pclmul,pconfig,pku,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,uintr,use-glm-div-sqrt-costs,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("silvermont"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-7bytenop,fast-movbe,fxsr,idivq-to-divl,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,sahf,slow-incdec,slow-lea,slow-pmulld,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-slm-arith-costs,vzeroupper,x87") }, + { str_lit("skx"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("skylake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("skylake-avx512"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("skylake_avx512"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("slm"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-7bytenop,fast-movbe,fxsr,idivq-to-divl,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,sahf,slow-incdec,slow-lea,slow-pmulld,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-slm-arith-costs,vzeroupper,x87") }, + { str_lit("tigerlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vp2intersect,avx512vpopcntdq,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("tremont"), str_lit("64bit,64bit-mode,aes,clflushopt,clwb,cmov,crc32,cx16,cx8,fast-movbe,fsgsbase,fxsr,gfni,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-glm-div-sqrt-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("westmere"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,fxsr,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") }, + { str_lit("winchip-c6"), str_lit("64bit-mode,mmx,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("winchip2"), str_lit("3dnow,64bit-mode,mmx,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("x86-64"), str_lit("64bit,64bit-mode,cmov,cx8,fxsr,idivq-to-divl,macrofusion,mmx,nopl,slow-3ops-lea,slow-incdec,sse,sse2,vzeroupper,x87") }, + { str_lit("x86-64-v2"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fxsr,idivq-to-divl,macrofusion,mmx,nopl,popcnt,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") }, + { str_lit("x86-64-v3"), str_lit("64bit,64bit-mode,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fxsr,idivq-to-divl,lzcnt,macrofusion,mmx,movbe,nopl,popcnt,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave") }, + { str_lit("x86-64-v4"), str_lit("64bit,64bit-mode,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fxsr,idivq-to-divl,lzcnt,macrofusion,mmx,movbe,nopl,popcnt,prefer-256-bit,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave") }, + { str_lit("yonah"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") }, + { str_lit("znver1"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,bmi,bmi2,branchfusion,clflushopt,clzero,cmov,crc32,cx16,cx8,f16c,fast-15bytenop,fast-bextr,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,lzcnt,mmx,movbe,mwaitx,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("znver2"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,bmi,bmi2,branchfusion,clflushopt,clwb,clzero,cmov,crc32,cx16,cx8,f16c,fast-15bytenop,fast-bextr,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,lzcnt,mmx,movbe,mwaitx,nopl,pclmul,popcnt,prfchw,rdpid,rdpru,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("znver3"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,bmi,bmi2,branchfusion,clflushopt,clwb,clzero,cmov,crc32,cx16,cx8,f16c,fast-15bytenop,fast-bextr,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,invpcid,lzcnt,macrofusion,mmx,movbe,mwaitx,nopl,pclmul,pku,popcnt,prfchw,rdpid,rdpru,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("znver4"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,branchfusion,clflushopt,clwb,clzero,cmov,crc32,cx16,cx8,evex512,f16c,fast-15bytenop,fast-bextr,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,invpcid,lzcnt,macrofusion,mmx,movbe,mwaitx,nopl,pclmul,pku,popcnt,prfchw,rdpid,rdpru,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,shstk,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") }, + // TargetArch_i386: + { str_lit("alderlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("amdfam10"), str_lit("32bit-mode,3dnow,3dnowa,64bit,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,lzcnt,mmx,nopl,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4a,vzeroupper,x87") }, + { str_lit("athlon"), str_lit("32bit-mode,3dnow,3dnowa,cmov,cx8,mmx,nopl,slow-shld,slow-unaligned-mem-16,vzeroupper,x87") }, + { str_lit("athlon-4"), str_lit("32bit-mode,3dnow,3dnowa,cmov,cx8,fxsr,mmx,nopl,slow-shld,slow-unaligned-mem-16,sse,vzeroupper,x87") }, + { str_lit("athlon-fx"), str_lit("32bit-mode,3dnow,3dnowa,64bit,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("athlon-mp"), str_lit("32bit-mode,3dnow,3dnowa,cmov,cx8,fxsr,mmx,nopl,slow-shld,slow-unaligned-mem-16,sse,vzeroupper,x87") }, + { str_lit("athlon-tbird"), str_lit("32bit-mode,3dnow,3dnowa,cmov,cx8,mmx,nopl,slow-shld,slow-unaligned-mem-16,vzeroupper,x87") }, + { str_lit("athlon-xp"), str_lit("32bit-mode,3dnow,3dnowa,cmov,cx8,fxsr,mmx,nopl,slow-shld,slow-unaligned-mem-16,sse,vzeroupper,x87") }, + { str_lit("athlon64"), str_lit("32bit-mode,3dnow,3dnowa,64bit,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("athlon64-sse3"), str_lit("32bit-mode,3dnow,3dnowa,64bit,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") }, + { str_lit("atom"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fxsr,idivl-to-divb,idivq-to-divl,lea-sp,lea-uses-ag,mmx,movbe,no-bypass-delay,nopl,pad-short-functions,sahf,slow-two-mem-ops,slow-unaligned-mem-16,sse,sse2,sse3,ssse3,vzeroupper,x87") }, + { str_lit("atom_sse4_2"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-7bytenop,fast-movbe,fxsr,idivq-to-divl,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,sahf,slow-incdec,slow-lea,slow-pmulld,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-slm-arith-costs,vzeroupper,x87") }, + { str_lit("atom_sse4_2_movbe"), str_lit("32bit-mode,64bit,aes,clflushopt,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-7bytenop,fast-movbe,fsgsbase,fxsr,idivq-to-divl,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-pmulld,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-slm-arith-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("barcelona"), str_lit("32bit-mode,3dnow,3dnowa,64bit,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,lzcnt,mmx,nopl,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4a,vzeroupper,x87") }, + { str_lit("bdver1"), str_lit("32bit-mode,64bit,aes,avx,branchfusion,cmov,crc32,cx16,cx8,fast-11bytenop,fast-scalar-shift-masks,fma4,fxsr,lwp,lzcnt,mmx,nopl,pclmul,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vzeroupper,x87,xop,xsave") }, + { str_lit("bdver2"), str_lit("32bit-mode,64bit,aes,avx,bmi,branchfusion,cmov,crc32,cx16,cx8,f16c,fast-11bytenop,fast-bextr,fast-movbe,fast-scalar-shift-masks,fma,fma4,fxsr,lwp,lzcnt,mmx,nopl,pclmul,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,tbm,vzeroupper,x87,xop,xsave") }, + { str_lit("bdver3"), str_lit("32bit-mode,64bit,aes,avx,bmi,branchfusion,cmov,crc32,cx16,cx8,f16c,fast-11bytenop,fast-bextr,fast-movbe,fast-scalar-shift-masks,fma,fma4,fsgsbase,fxsr,lwp,lzcnt,mmx,nopl,pclmul,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,tbm,vzeroupper,x87,xop,xsave,xsaveopt") }, + { str_lit("bdver4"), str_lit("32bit-mode,64bit,aes,avx,avx2,bmi,bmi2,branchfusion,cmov,crc32,cx16,cx8,f16c,fast-11bytenop,fast-bextr,fast-movbe,fast-scalar-shift-masks,fma,fma4,fsgsbase,fxsr,lwp,lzcnt,mmx,movbe,mwaitx,nopl,pclmul,popcnt,prfchw,rdrnd,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,tbm,vzeroupper,x87,xop,xsave,xsaveopt") }, + { str_lit("bonnell"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fxsr,idivl-to-divb,idivq-to-divl,lea-sp,lea-uses-ag,mmx,movbe,no-bypass-delay,nopl,pad-short-functions,sahf,slow-two-mem-ops,slow-unaligned-mem-16,sse,sse2,sse3,ssse3,vzeroupper,x87") }, + { str_lit("broadwell"), str_lit("32bit-mode,64bit,adx,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") }, + { str_lit("btver1"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fast-15bytenop,fast-scalar-shift-masks,fast-vector-shift-masks,fxsr,lzcnt,mmx,nopl,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4a,ssse3,vzeroupper,x87") }, + { str_lit("btver2"), str_lit("32bit-mode,64bit,aes,avx,bmi,cmov,crc32,cx16,cx8,f16c,fast-15bytenop,fast-bextr,fast-hops,fast-lzcnt,fast-movbe,fast-scalar-shift-masks,fast-vector-shift-masks,fxsr,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,x87,xsave,xsaveopt") }, + { str_lit("c3"), str_lit("32bit-mode,3dnow,mmx,slow-unaligned-mem-16,vzeroupper,x87") }, + { str_lit("c3-2"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,slow-unaligned-mem-16,sse,vzeroupper,x87") }, + { str_lit("cannonlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vl,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,sha,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("cascadelake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,avx512vnni,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("cooperlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bf16,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,avx512vnni,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("core-avx-i"), str_lit("32bit-mode,64bit,avx,cmov,crc32,cx16,cx8,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fsgsbase,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") }, + { str_lit("core-avx2"), str_lit("32bit-mode,64bit,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") }, + { str_lit("core2"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fxsr,macrofusion,mmx,nopl,sahf,slow-unaligned-mem-16,sse,sse2,sse3,ssse3,vzeroupper,x87") }, + { str_lit("core_2_duo_sse4_1"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fxsr,macrofusion,mmx,nopl,sahf,slow-unaligned-mem-16,sse,sse2,sse3,sse4.1,ssse3,vzeroupper,x87") }, + { str_lit("core_2_duo_ssse3"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fxsr,macrofusion,mmx,nopl,sahf,slow-unaligned-mem-16,sse,sse2,sse3,ssse3,vzeroupper,x87") }, + { str_lit("core_2nd_gen_avx"), str_lit("32bit-mode,64bit,avx,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") }, + { str_lit("core_3rd_gen_avx"), str_lit("32bit-mode,64bit,avx,cmov,crc32,cx16,cx8,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fsgsbase,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") }, + { str_lit("core_4th_gen_avx"), str_lit("32bit-mode,64bit,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") }, + { str_lit("core_4th_gen_avx_tsx"), str_lit("32bit-mode,64bit,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") }, + { str_lit("core_5th_gen_avx"), str_lit("32bit-mode,64bit,adx,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") }, + { str_lit("core_5th_gen_avx_tsx"), str_lit("32bit-mode,64bit,adx,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") }, + { str_lit("core_aes_pclmulqdq"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,fxsr,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") }, + { str_lit("core_i7_sse4_2"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,fxsr,macrofusion,mmx,no-bypass-delay-mov,nopl,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") }, + { str_lit("corei7"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,fxsr,macrofusion,mmx,no-bypass-delay-mov,nopl,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") }, + { str_lit("corei7-avx"), str_lit("32bit-mode,64bit,avx,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") }, + { str_lit("emeraldrapids"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,amx-bf16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("generic"), str_lit("32bit-mode,64bit,cx8,fast-15bytenop,fast-scalar-fsqrt,idivq-to-divl,macrofusion,slow-3ops-lea,vzeroupper,x87") }, + { str_lit("geode"), str_lit("32bit-mode,3dnow,3dnowa,cx8,mmx,slow-unaligned-mem-16,vzeroupper,x87") }, + { str_lit("goldmont"), str_lit("32bit-mode,64bit,aes,clflushopt,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-movbe,fsgsbase,fxsr,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-glm-div-sqrt-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("goldmont-plus"), str_lit("32bit-mode,64bit,aes,clflushopt,cmov,crc32,cx16,cx8,fast-movbe,fsgsbase,fxsr,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-glm-div-sqrt-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("goldmont_plus"), str_lit("32bit-mode,64bit,aes,clflushopt,cmov,crc32,cx16,cx8,fast-movbe,fsgsbase,fxsr,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-glm-div-sqrt-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("grandridge"), str_lit("32bit-mode,64bit,adx,aes,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,fast-movbe,fma,fsgsbase,fxsr,gfni,hreset,invpcid,kl,lzcnt,mmx,movbe,movdir64b,movdiri,no-bypass-delay,nopl,pclmul,pconfig,pku,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,uintr,use-glm-div-sqrt-costs,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("graniterapids"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,amx-bf16,amx-fp16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prefetchi,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("graniterapids-d"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,amx-bf16,amx-complex,amx-fp16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prefetchi,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("graniterapids_d"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,amx-bf16,amx-complex,amx-fp16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prefetchi,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("haswell"), str_lit("32bit-mode,64bit,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") }, + { str_lit("i386"), str_lit("32bit-mode,slow-unaligned-mem-16,vzeroupper,x87") }, + { str_lit("i486"), str_lit("32bit-mode,slow-unaligned-mem-16,vzeroupper,x87") }, + { str_lit("i586"), str_lit("32bit-mode,cx8,slow-unaligned-mem-16,vzeroupper,x87") }, + { str_lit("i686"), str_lit("32bit-mode,cmov,cx8,slow-unaligned-mem-16,vzeroupper,x87") }, + { str_lit("icelake-client"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("icelake-server"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("icelake_client"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("icelake_server"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("ivybridge"), str_lit("32bit-mode,64bit,avx,cmov,crc32,cx16,cx8,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fsgsbase,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") }, + { str_lit("k6"), str_lit("32bit-mode,cx8,mmx,slow-unaligned-mem-16,vzeroupper,x87") }, + { str_lit("k6-2"), str_lit("32bit-mode,3dnow,cx8,mmx,slow-unaligned-mem-16,vzeroupper,x87") }, + { str_lit("k6-3"), str_lit("32bit-mode,3dnow,cx8,mmx,slow-unaligned-mem-16,vzeroupper,x87") }, + { str_lit("k8"), str_lit("32bit-mode,3dnow,3dnowa,64bit,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("k8-sse3"), str_lit("32bit-mode,3dnow,3dnowa,64bit,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") }, + { str_lit("knl"), str_lit("32bit-mode,64bit,adx,aes,avx,avx2,avx512cd,avx512er,avx512f,avx512pf,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,fast-gather,fast-movbe,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prefer-mask-registers,prefetchwt1,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,slow-incdec,slow-pmaddwd,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,x87,xsave,xsaveopt") }, + { str_lit("knm"), str_lit("32bit-mode,64bit,adx,aes,avx,avx2,avx512cd,avx512er,avx512f,avx512pf,avx512vpopcntdq,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,fast-gather,fast-movbe,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prefer-mask-registers,prefetchwt1,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,slow-incdec,slow-pmaddwd,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,x87,xsave,xsaveopt") }, + { str_lit("lakemont"), str_lit("32bit-mode,cx8,slow-unaligned-mem-16,vzeroupper") }, + { str_lit("meteorlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("mic_avx512"), str_lit("32bit-mode,64bit,adx,aes,avx,avx2,avx512cd,avx512er,avx512f,avx512pf,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,fast-gather,fast-movbe,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prefer-mask-registers,prefetchwt1,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,slow-incdec,slow-pmaddwd,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,x87,xsave,xsaveopt") }, + { str_lit("nehalem"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,fxsr,macrofusion,mmx,no-bypass-delay-mov,nopl,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") }, + { str_lit("nocona"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") }, + { str_lit("opteron"), str_lit("32bit-mode,3dnow,3dnowa,64bit,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("opteron-sse3"), str_lit("32bit-mode,3dnow,3dnowa,64bit,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") }, + { str_lit("penryn"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fxsr,macrofusion,mmx,nopl,sahf,slow-unaligned-mem-16,sse,sse2,sse3,sse4.1,ssse3,vzeroupper,x87") }, + { str_lit("pentium"), str_lit("32bit-mode,cx8,slow-unaligned-mem-16,vzeroupper,x87") }, + { str_lit("pentium-m"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("pentium-mmx"), str_lit("32bit-mode,cx8,mmx,slow-unaligned-mem-16,vzeroupper,x87") }, + { str_lit("pentium2"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,vzeroupper,x87") }, + { str_lit("pentium3"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,vzeroupper,x87") }, + { str_lit("pentium3m"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,vzeroupper,x87") }, + { str_lit("pentium4"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("pentium4m"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("pentium_4"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("pentium_4_sse3"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") }, + { str_lit("pentium_ii"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,vzeroupper,x87") }, + { str_lit("pentium_iii"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,vzeroupper,x87") }, + { str_lit("pentium_iii_no_xmm_regs"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,vzeroupper,x87") }, + { str_lit("pentium_m"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") }, + { str_lit("pentium_mmx"), str_lit("32bit-mode,cx8,mmx,slow-unaligned-mem-16,vzeroupper,x87") }, + { str_lit("pentium_pro"), str_lit("32bit-mode,cmov,cx8,nopl,slow-unaligned-mem-16,vzeroupper,x87") }, + { str_lit("pentiumpro"), str_lit("32bit-mode,cmov,cx8,nopl,slow-unaligned-mem-16,vzeroupper,x87") }, + { str_lit("prescott"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") }, + { str_lit("raptorlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("rocketlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("sandybridge"), str_lit("32bit-mode,64bit,avx,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") }, + { str_lit("sapphirerapids"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,amx-bf16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("sierraforest"), str_lit("32bit-mode,64bit,adx,aes,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,fast-movbe,fma,fsgsbase,fxsr,gfni,hreset,invpcid,kl,lzcnt,mmx,movbe,movdir64b,movdiri,no-bypass-delay,nopl,pclmul,pconfig,pku,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,uintr,use-glm-div-sqrt-costs,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("silvermont"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-7bytenop,fast-movbe,fxsr,idivq-to-divl,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,sahf,slow-incdec,slow-lea,slow-pmulld,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-slm-arith-costs,vzeroupper,x87") }, + { str_lit("skx"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("skylake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("skylake-avx512"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("skylake_avx512"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("slm"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-7bytenop,fast-movbe,fxsr,idivq-to-divl,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,sahf,slow-incdec,slow-lea,slow-pmulld,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-slm-arith-costs,vzeroupper,x87") }, + { str_lit("tigerlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vp2intersect,avx512vpopcntdq,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("tremont"), str_lit("32bit-mode,64bit,aes,clflushopt,clwb,cmov,crc32,cx16,cx8,fast-movbe,fsgsbase,fxsr,gfni,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-glm-div-sqrt-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("westmere"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,fxsr,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") }, + { str_lit("winchip-c6"), str_lit("32bit-mode,mmx,slow-unaligned-mem-16,vzeroupper,x87") }, + { str_lit("winchip2"), str_lit("32bit-mode,3dnow,mmx,slow-unaligned-mem-16,vzeroupper,x87") }, + { str_lit("x86-64"), str_lit("32bit-mode,64bit,cmov,cx8,fxsr,idivq-to-divl,macrofusion,mmx,nopl,slow-3ops-lea,slow-incdec,sse,sse2,vzeroupper,x87") }, + { str_lit("x86-64-v2"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fxsr,idivq-to-divl,macrofusion,mmx,nopl,popcnt,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") }, + { str_lit("x86-64-v3"), str_lit("32bit-mode,64bit,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fxsr,idivq-to-divl,lzcnt,macrofusion,mmx,movbe,nopl,popcnt,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave") }, + { str_lit("x86-64-v4"), str_lit("32bit-mode,64bit,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fxsr,idivq-to-divl,lzcnt,macrofusion,mmx,movbe,nopl,popcnt,prefer-256-bit,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave") }, + { str_lit("yonah"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") }, + { str_lit("znver1"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,bmi,bmi2,branchfusion,clflushopt,clzero,cmov,crc32,cx16,cx8,f16c,fast-15bytenop,fast-bextr,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,lzcnt,mmx,movbe,mwaitx,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("znver2"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,bmi,bmi2,branchfusion,clflushopt,clwb,clzero,cmov,crc32,cx16,cx8,f16c,fast-15bytenop,fast-bextr,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,lzcnt,mmx,movbe,mwaitx,nopl,pclmul,popcnt,prfchw,rdpid,rdpru,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("znver3"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,bmi,bmi2,branchfusion,clflushopt,clwb,clzero,cmov,crc32,cx16,cx8,f16c,fast-15bytenop,fast-bextr,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,invpcid,lzcnt,macrofusion,mmx,movbe,mwaitx,nopl,pclmul,pku,popcnt,prfchw,rdpid,rdpru,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") }, + { str_lit("znver4"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,branchfusion,clflushopt,clwb,clzero,cmov,crc32,cx16,cx8,evex512,f16c,fast-15bytenop,fast-bextr,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,invpcid,lzcnt,macrofusion,mmx,movbe,mwaitx,nopl,pclmul,pku,popcnt,prfchw,rdpid,rdpru,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,shstk,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") }, + // TargetArch_arm32: + { str_lit("arm1020e"), str_lit("armv5te,v4t,v5t,v5te") }, + { str_lit("arm1020t"), str_lit("armv5t,v4t,v5t") }, + { str_lit("arm1022e"), str_lit("armv5te,v4t,v5t,v5te") }, + { str_lit("arm10e"), str_lit("armv5te,v4t,v5t,v5te") }, + { str_lit("arm10tdmi"), str_lit("armv5t,v4t,v5t") }, + { str_lit("arm1136j-s"), str_lit("armv6,dsp,v4t,v5t,v5te,v6") }, + { str_lit("arm1136jf-s"), str_lit("armv6,dsp,fp64,fpregs,fpregs64,slowfpvmlx,v4t,v5t,v5te,v6,vfp2,vfp2sp") }, + { str_lit("arm1156t2-s"), str_lit("armv6t2,dsp,thumb2,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v8m") }, + { str_lit("arm1156t2f-s"), str_lit("armv6t2,dsp,fp64,fpregs,fpregs64,slowfpvmlx,thumb2,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v8m,vfp2,vfp2sp") }, + { str_lit("arm1176jz-s"), str_lit("armv6kz,trustzone,v4t,v5t,v5te,v6,v6k") }, + { str_lit("arm1176jzf-s"), str_lit("armv6kz,fp64,fpregs,fpregs64,slowfpvmlx,trustzone,v4t,v5t,v5te,v6,v6k,vfp2,vfp2sp") }, + { str_lit("arm710t"), str_lit("armv4t,v4t") }, + { str_lit("arm720t"), str_lit("armv4t,v4t") }, + { str_lit("arm7tdmi"), str_lit("armv4t,v4t") }, + { str_lit("arm7tdmi-s"), str_lit("armv4t,v4t") }, + { str_lit("arm8"), str_lit("armv4") }, + { str_lit("arm810"), str_lit("armv4") }, + { str_lit("arm9"), str_lit("armv4t,v4t") }, + { str_lit("arm920"), str_lit("armv4t,v4t") }, + { str_lit("arm920t"), str_lit("armv4t,v4t") }, + { str_lit("arm922t"), str_lit("armv4t,v4t") }, + { str_lit("arm926ej-s"), str_lit("armv5te,v4t,v5t,v5te") }, + { str_lit("arm940t"), str_lit("armv4t,v4t") }, + { str_lit("arm946e-s"), str_lit("armv5te,v4t,v5t,v5te") }, + { str_lit("arm966e-s"), str_lit("armv5te,v4t,v5t,v5te") }, + { str_lit("arm968e-s"), str_lit("armv5te,v4t,v5t,v5te") }, + { str_lit("arm9e"), str_lit("armv5te,v4t,v5t,v5te") }, + { str_lit("arm9tdmi"), str_lit("armv4t,v4t") }, + { str_lit("cortex-a12"), str_lit("a12,aclass,armv7-a,avoid-partial-cpsr,d32,db,dsp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,ret-addr-stack,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,vmlx-forwarding") }, + { str_lit("cortex-a15"), str_lit("a15,aclass,armv7-a,avoid-partial-cpsr,d32,db,dont-widen-vmovs,dsp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,muxed-units,neon,perfmon,ret-addr-stack,splat-vfp-neon,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,vldn-align") }, + { str_lit("cortex-a17"), str_lit("a17,aclass,armv7-a,avoid-partial-cpsr,d32,db,dsp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,ret-addr-stack,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,vmlx-forwarding") }, + { str_lit("cortex-a32"), str_lit("aclass,acquire-release,aes,armv8-a,crc,crypto,d32,db,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") }, + { str_lit("cortex-a35"), str_lit("a35,aclass,acquire-release,aes,armv8-a,crc,crypto,d32,db,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") }, + { str_lit("cortex-a5"), str_lit("a5,aclass,armv7-a,d32,db,dsp,fp16,fp64,fpregs,fpregs64,mp,neon,perfmon,ret-addr-stack,slow-fp-brcc,slowfpvfmx,slowfpvmlx,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,vmlx-forwarding") }, + { str_lit("cortex-a53"), str_lit("a53,aclass,acquire-release,aes,armv8-a,crc,crypto,d32,db,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpao,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") }, + { str_lit("cortex-a55"), str_lit("a55,aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") }, + { str_lit("cortex-a57"), str_lit("a57,aclass,acquire-release,aes,armv8-a,avoid-partial-cpsr,cheap-predicable-cpsr,crc,crypto,d32,db,dsp,fix-cortex-a57-aes-1742098,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpao,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") }, + { str_lit("cortex-a7"), str_lit("a7,aclass,armv7-a,d32,db,dsp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,ret-addr-stack,slow-fp-brcc,slowfpvfmx,slowfpvmlx,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,vmlx-forwarding,vmlx-hazards") }, + { str_lit("cortex-a710"), str_lit("aclass,acquire-release,armv9-a,bf16,cortex-a710,crc,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp16fml,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,i8mm,mp,neon,perfmon,ras,sb,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8m,v9a,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") }, + { str_lit("cortex-a72"), str_lit("a72,aclass,acquire-release,aes,armv8-a,crc,crypto,d32,db,dsp,fix-cortex-a57-aes-1742098,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") }, + { str_lit("cortex-a73"), str_lit("a73,aclass,acquire-release,aes,armv8-a,crc,crypto,d32,db,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") }, + { str_lit("cortex-a75"), str_lit("a75,aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") }, + { str_lit("cortex-a76"), str_lit("a76,aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") }, + { str_lit("cortex-a76ae"), str_lit("a76,aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") }, + { str_lit("cortex-a77"), str_lit("a77,aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") }, + { str_lit("cortex-a78"), str_lit("aclass,acquire-release,aes,armv8.2-a,cortex-a78,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") }, + { str_lit("cortex-a78c"), str_lit("a78c,aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") }, + { str_lit("cortex-a8"), str_lit("a8,aclass,armv7-a,d32,db,dsp,fp64,fpregs,fpregs64,neon,nonpipelined-vfp,perfmon,ret-addr-stack,slow-fp-brcc,slowfpvfmx,slowfpvmlx,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vmlx-forwarding,vmlx-hazards") }, + { str_lit("cortex-a9"), str_lit("a9,aclass,armv7-a,avoid-partial-cpsr,d32,db,dsp,expand-fp-mlx,fp16,fp64,fpregs,fpregs64,mp,muxed-units,neon,neon-fpmovs,perfmon,prefer-vmovsr,ret-addr-stack,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vldn-align,vmlx-forwarding,vmlx-hazards") }, + { str_lit("cortex-m0"), str_lit("armv6-m,db,mclass,no-branch-predictor,noarm,strict-align,thumb-mode,v4t,v5t,v5te,v6,v6m") }, + { str_lit("cortex-m0plus"), str_lit("armv6-m,db,mclass,no-branch-predictor,noarm,strict-align,thumb-mode,v4t,v5t,v5te,v6,v6m") }, + { str_lit("cortex-m1"), str_lit("armv6-m,db,mclass,no-branch-predictor,noarm,strict-align,thumb-mode,v4t,v5t,v5te,v6,v6m") }, + { str_lit("cortex-m23"), str_lit("8msecext,acquire-release,armv8-m.base,db,hwdiv,mclass,no-branch-predictor,no-movt,noarm,strict-align,thumb-mode,v4t,v5t,v5te,v6,v6m,v7clrex,v8m") }, + { str_lit("cortex-m3"), str_lit("armv7-m,db,hwdiv,loop-align,m3,mclass,no-branch-predictor,noarm,thumb-mode,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m") }, + { str_lit("cortex-m33"), str_lit("8msecext,acquire-release,armv8-m.main,db,dsp,fix-cmse-cve-2021-35465,fp-armv8d16sp,fp16,fpregs,hwdiv,loop-align,mclass,no-branch-predictor,noarm,slowfpvfmx,slowfpvmlx,thumb-mode,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,v8m.main,vfp2sp,vfp3d16sp,vfp4d16sp") }, + { str_lit("cortex-m35p"), str_lit("8msecext,acquire-release,armv8-m.main,db,dsp,fix-cmse-cve-2021-35465,fp-armv8d16sp,fp16,fpregs,hwdiv,loop-align,mclass,no-branch-predictor,noarm,slowfpvfmx,slowfpvmlx,thumb-mode,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,v8m.main,vfp2sp,vfp3d16sp,vfp4d16sp") }, + { str_lit("cortex-m4"), str_lit("armv7e-m,db,dsp,fp16,fpregs,hwdiv,loop-align,mclass,no-branch-predictor,noarm,slowfpvfmx,slowfpvmlx,thumb-mode,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2sp,vfp3d16sp,vfp4d16sp") }, + { str_lit("cortex-m55"), str_lit("8msecext,acquire-release,armv8.1-m.main,db,dsp,fix-cmse-cve-2021-35465,fp-armv8d16,fp-armv8d16sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,lob,loop-align,mclass,mve,mve.fp,no-branch-predictor,noarm,ras,slowfpvmlx,thumb-mode,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8.1m.main,v8m,v8m.main,vfp2,vfp2sp,vfp3d16,vfp3d16sp,vfp4d16,vfp4d16sp") }, + { str_lit("cortex-m7"), str_lit("armv7e-m,db,dsp,fp-armv8d16,fp-armv8d16sp,fp16,fp64,fpregs,fpregs64,hwdiv,m7,mclass,noarm,thumb-mode,thumb2,use-mipipeliner,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3d16,vfp3d16sp,vfp4d16,vfp4d16sp") }, + { str_lit("cortex-m85"), str_lit("8msecext,acquire-release,armv8.1-m.main,db,dsp,fp-armv8d16,fp-armv8d16sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,lob,mclass,mve,mve.fp,noarm,pacbti,ras,thumb-mode,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8.1m.main,v8m,v8m.main,vfp2,vfp2sp,vfp3d16,vfp3d16sp,vfp4d16,vfp4d16sp") }, + { str_lit("cortex-r4"), str_lit("armv7-r,avoid-partial-cpsr,db,dsp,hwdiv,perfmon,r4,rclass,ret-addr-stack,thumb2,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m") }, + { str_lit("cortex-r4f"), str_lit("armv7-r,avoid-partial-cpsr,db,dsp,fp64,fpregs,fpregs64,hwdiv,perfmon,r4,rclass,ret-addr-stack,slow-fp-brcc,slowfpvfmx,slowfpvmlx,thumb2,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3d16,vfp3d16sp") }, + { str_lit("cortex-r5"), str_lit("armv7-r,avoid-partial-cpsr,db,dsp,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,perfmon,r5,rclass,ret-addr-stack,slow-fp-brcc,slowfpvfmx,slowfpvmlx,thumb2,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3d16,vfp3d16sp") }, + { str_lit("cortex-r52"), str_lit("acquire-release,armv8-r,crc,d32,db,dfb,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpao,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,r52,rclass,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") }, + { str_lit("cortex-r7"), str_lit("armv7-r,avoid-partial-cpsr,db,dsp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,perfmon,r7,rclass,ret-addr-stack,slow-fp-brcc,slowfpvfmx,slowfpvmlx,thumb2,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3d16,vfp3d16sp") }, + { str_lit("cortex-r8"), str_lit("armv7-r,avoid-partial-cpsr,db,dsp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,perfmon,rclass,ret-addr-stack,slow-fp-brcc,slowfpvfmx,slowfpvmlx,thumb2,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3d16,vfp3d16sp") }, + { str_lit("cortex-x1"), str_lit("aclass,acquire-release,aes,armv8.2-a,cortex-x1,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") }, + { str_lit("cortex-x1c"), str_lit("aclass,acquire-release,aes,armv8.2-a,cortex-x1c,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") }, + { str_lit("cyclone"), str_lit("aclass,acquire-release,aes,armv8-a,avoid-movs-shop,avoid-partial-cpsr,crc,crypto,d32,db,disable-postra-scheduler,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,neonfp,perfmon,ret-addr-stack,sha2,slowfpvfmx,slowfpvmlx,swift,thumb2,trustzone,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,zcz") }, + { str_lit("ep9312"), str_lit("armv4t,v4t") }, + { str_lit("exynos-m3"), str_lit("aclass,acquire-release,aes,armv8-a,crc,crypto,d32,db,dont-widen-vmovs,dsp,expand-fp-mlx,exynos,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,fuse-aes,fuse-literals,hwdiv,hwdiv-arm,mp,neon,perfmon,prof-unpr,ret-addr-stack,sha2,slow-fp-brcc,slow-vdup32,slow-vgetlni32,slowfpvfmx,slowfpvmlx,splat-vfp-neon,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,wide-stride-vfp,zcz") }, + { str_lit("exynos-m4"), str_lit("aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dont-widen-vmovs,dotprod,dsp,expand-fp-mlx,exynos,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,fuse-aes,fuse-literals,hwdiv,hwdiv-arm,mp,neon,perfmon,prof-unpr,ras,ret-addr-stack,sha2,slow-fp-brcc,slow-vdup32,slow-vgetlni32,slowfpvfmx,slowfpvmlx,splat-vfp-neon,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,wide-stride-vfp,zcz") }, + { str_lit("exynos-m5"), str_lit("aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dont-widen-vmovs,dotprod,dsp,expand-fp-mlx,exynos,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,fuse-aes,fuse-literals,hwdiv,hwdiv-arm,mp,neon,perfmon,prof-unpr,ras,ret-addr-stack,sha2,slow-fp-brcc,slow-vdup32,slow-vgetlni32,slowfpvfmx,slowfpvmlx,splat-vfp-neon,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,wide-stride-vfp,zcz") }, + { str_lit("generic"), str_lit("") }, + { str_lit("iwmmxt"), str_lit("armv5te,v4t,v5t,v5te") }, + { str_lit("krait"), str_lit("aclass,armv7-a,avoid-partial-cpsr,d32,db,dsp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,krait,muxed-units,neon,perfmon,ret-addr-stack,thumb2,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,vldn-align,vmlx-forwarding") }, + { str_lit("kryo"), str_lit("aclass,acquire-release,aes,armv8-a,crc,crypto,d32,db,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,kryo,mp,neon,perfmon,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") }, + { str_lit("mpcore"), str_lit("armv6k,fp64,fpregs,fpregs64,slowfpvmlx,v4t,v5t,v5te,v6,v6k,vfp2,vfp2sp") }, + { str_lit("mpcorenovfp"), str_lit("armv6k,v4t,v5t,v5te,v6,v6k") }, + { str_lit("neoverse-n1"), str_lit("aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") }, + { str_lit("neoverse-n2"), str_lit("aclass,acquire-release,armv9-a,bf16,crc,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,i8mm,mp,neon,perfmon,ras,sb,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8m,v9a,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") }, + { str_lit("neoverse-v1"), str_lit("aclass,acquire-release,aes,armv8.4-a,bf16,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,i8mm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8.3a,v8.4a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") }, + { str_lit("sc000"), str_lit("armv6-m,db,mclass,no-branch-predictor,noarm,strict-align,thumb-mode,v4t,v5t,v5te,v6,v6m") }, + { str_lit("sc300"), str_lit("armv7-m,db,hwdiv,m3,mclass,no-branch-predictor,noarm,thumb-mode,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m") }, + { str_lit("strongarm"), str_lit("armv4") }, + { str_lit("strongarm110"), str_lit("armv4") }, + { str_lit("strongarm1100"), str_lit("armv4") }, + { str_lit("strongarm1110"), str_lit("armv4") }, + { str_lit("swift"), str_lit("aclass,armv7-a,avoid-movs-shop,avoid-partial-cpsr,d32,db,disable-postra-scheduler,dsp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,neonfp,perfmon,prefer-ishst,prof-unpr,ret-addr-stack,slow-load-D-subreg,slow-odd-reg,slow-vdup32,slow-vgetlni32,slowfpvfmx,slowfpvmlx,swift,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,vmlx-hazards,wide-stride-vfp") }, + { str_lit("xscale"), str_lit("armv5te,v4t,v5t,v5te") }, + // TargetArch_arm64: + { str_lit("a64fx"), str_lit("CONTEXTIDREL2,a64fx,aggressive-fma,arith-bcc-fusion,ccpp,complxnum,crc,el2vmsa,el3,fp-armv8,fullfp16,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rdm,sha2,store-pair-suppress,sve,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") }, + { str_lit("ampere1"), str_lit("CONTEXTIDREL2,addr-lsl-fast,aes,aggressive-fma,altnzcv,alu-lsl-fast,am,ampere1,amvs,arith-bcc-fusion,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,crc,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fptoint,fuse-address,fuse-aes,fuse-literals,i8mm,jsconv,ldp-aligned-only,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predres,rand,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,specrestrict,ssbs,store-pair-suppress,stp-aligned-only,tlb-rmi,tracev8.4,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8a,vh") }, + { str_lit("ampere1a"), str_lit("CONTEXTIDREL2,addr-lsl-fast,aes,aggressive-fma,altnzcv,alu-lsl-fast,am,ampere1a,amvs,arith-bcc-fusion,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,crc,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fptoint,fuse-address,fuse-aes,fuse-literals,i8mm,jsconv,ldp-aligned-only,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predres,rand,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,sm4,specrestrict,ssbs,store-pair-suppress,stp-aligned-only,tlb-rmi,tracev8.4,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8a,vh") }, + { str_lit("apple-a10"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,apple-a10,arith-bcc-fusion,arith-cbz-fusion,crc,crypto,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fuse-aes,fuse-crypto-eor,lor,neon,pan,perfmon,rdm,sha2,store-pair-suppress,v8a,vh,zcm,zcz,zcz-gp") }, + { str_lit("apple-a11"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,apple-a11,arith-bcc-fusion,arith-cbz-fusion,ccpp,crc,crypto,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fullfp16,fuse-aes,fuse-crypto-eor,lor,lse,neon,pan,pan-rwv,perfmon,ras,rdm,sha2,store-pair-suppress,uaops,v8.1a,v8.2a,v8a,vh,zcm,zcz,zcz-gp") }, + { str_lit("apple-a12"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,apple-a12,arith-bcc-fusion,arith-cbz-fusion,ccidx,ccpp,complxnum,crc,crypto,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fullfp16,fuse-aes,fuse-crypto-eor,jsconv,lor,lse,neon,pan,pan-rwv,pauth,perfmon,ras,rcpc,rdm,sha2,store-pair-suppress,uaops,v8.1a,v8.2a,v8.3a,v8a,vh,zcm,zcz,zcz-gp") }, + { str_lit("apple-a13"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,am,apple-a13,arith-bcc-fusion,arith-cbz-fusion,ccidx,ccpp,complxnum,crc,crypto,disable-latency-sched-heuristic,dit,dotprod,el2vmsa,el3,flagm,fp-armv8,fp16fml,fullfp16,fuse-aes,fuse-crypto-eor,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,ras,rcpc,rcpc-immo,rdm,sel2,sha2,sha3,store-pair-suppress,tlb-rmi,tracev8.4,uaops,v8.1a,v8.2a,v8.3a,v8.4a,v8a,vh,zcm,zcz,zcz-gp") }, + { str_lit("apple-a14"), str_lit("CONTEXTIDREL2,aes,aggressive-fma,alternate-sextload-cvt-f32-pattern,altnzcv,am,apple-a14,arith-bcc-fusion,arith-cbz-fusion,ccdp,ccidx,ccpp,complxnum,crc,crypto,disable-latency-sched-heuristic,dit,dotprod,el2vmsa,el3,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-arith-logic,fuse-crypto-eor,fuse-csel,fuse-literals,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,specrestrict,ssbs,store-pair-suppress,tlb-rmi,tracev8.4,uaops,v8.1a,v8.2a,v8.3a,v8.4a,v8a,vh,zcm,zcz,zcz-gp") }, + { str_lit("apple-a15"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,altnzcv,am,amvs,apple-a15,arith-bcc-fusion,arith-cbz-fusion,bf16,bti,ccdp,ccidx,ccpp,complxnum,crc,crypto,disable-latency-sched-heuristic,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-address,fuse-aes,fuse-arith-logic,fuse-crypto-eor,fuse-csel,fuse-literals,i8mm,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,specrestrict,ssbs,store-pair-suppress,tlb-rmi,tracev8.4,uaops,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8a,vh,zcm,zcz,zcz-gp") }, + { str_lit("apple-a16"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,altnzcv,am,amvs,apple-a16,arith-bcc-fusion,arith-cbz-fusion,bf16,bti,ccdp,ccidx,ccpp,complxnum,crc,crypto,disable-latency-sched-heuristic,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-address,fuse-aes,fuse-arith-logic,fuse-crypto-eor,fuse-csel,fuse-literals,hcx,i8mm,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,specrestrict,ssbs,store-pair-suppress,tlb-rmi,tracev8.4,uaops,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8a,vh,zcm,zcz,zcz-gp") }, + { str_lit("apple-a7"), str_lit("aes,alternate-sextload-cvt-f32-pattern,apple-a7,apple-a7-sysreg,arith-bcc-fusion,arith-cbz-fusion,crypto,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fuse-aes,fuse-crypto-eor,neon,perfmon,sha2,store-pair-suppress,v8a,zcm,zcz,zcz-fp-workaround,zcz-gp") }, + { str_lit("apple-a8"), str_lit("aes,alternate-sextload-cvt-f32-pattern,apple-a7,apple-a7-sysreg,arith-bcc-fusion,arith-cbz-fusion,crypto,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fuse-aes,fuse-crypto-eor,neon,perfmon,sha2,store-pair-suppress,v8a,zcm,zcz,zcz-fp-workaround,zcz-gp") }, + { str_lit("apple-a9"), str_lit("aes,alternate-sextload-cvt-f32-pattern,apple-a7,apple-a7-sysreg,arith-bcc-fusion,arith-cbz-fusion,crypto,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fuse-aes,fuse-crypto-eor,neon,perfmon,sha2,store-pair-suppress,v8a,zcm,zcz,zcz-fp-workaround,zcz-gp") }, + { str_lit("apple-latest"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,altnzcv,am,amvs,apple-a16,arith-bcc-fusion,arith-cbz-fusion,bf16,bti,ccdp,ccidx,ccpp,complxnum,crc,crypto,disable-latency-sched-heuristic,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-address,fuse-aes,fuse-arith-logic,fuse-crypto-eor,fuse-csel,fuse-literals,hcx,i8mm,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,specrestrict,ssbs,store-pair-suppress,tlb-rmi,tracev8.4,uaops,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8a,vh,zcm,zcz,zcz-gp") }, + { str_lit("apple-m1"), str_lit("CONTEXTIDREL2,aes,aggressive-fma,alternate-sextload-cvt-f32-pattern,altnzcv,am,apple-a14,arith-bcc-fusion,arith-cbz-fusion,ccdp,ccidx,ccpp,complxnum,crc,crypto,disable-latency-sched-heuristic,dit,dotprod,el2vmsa,el3,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-arith-logic,fuse-crypto-eor,fuse-csel,fuse-literals,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,specrestrict,ssbs,store-pair-suppress,tlb-rmi,tracev8.4,uaops,v8.1a,v8.2a,v8.3a,v8.4a,v8a,vh,zcm,zcz,zcz-gp") }, + { str_lit("apple-m2"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,altnzcv,am,amvs,apple-a15,arith-bcc-fusion,arith-cbz-fusion,bf16,bti,ccdp,ccidx,ccpp,complxnum,crc,crypto,disable-latency-sched-heuristic,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-address,fuse-aes,fuse-arith-logic,fuse-crypto-eor,fuse-csel,fuse-literals,i8mm,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,specrestrict,ssbs,store-pair-suppress,tlb-rmi,tracev8.4,uaops,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8a,vh,zcm,zcz,zcz-gp") }, + { str_lit("apple-s4"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,apple-a12,arith-bcc-fusion,arith-cbz-fusion,ccidx,ccpp,complxnum,crc,crypto,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fullfp16,fuse-aes,fuse-crypto-eor,jsconv,lor,lse,neon,pan,pan-rwv,pauth,perfmon,ras,rcpc,rdm,sha2,store-pair-suppress,uaops,v8.1a,v8.2a,v8.3a,v8a,vh,zcm,zcz,zcz-gp") }, + { str_lit("apple-s5"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,apple-a12,arith-bcc-fusion,arith-cbz-fusion,ccidx,ccpp,complxnum,crc,crypto,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fullfp16,fuse-aes,fuse-crypto-eor,jsconv,lor,lse,neon,pan,pan-rwv,pauth,perfmon,ras,rcpc,rdm,sha2,store-pair-suppress,uaops,v8.1a,v8.2a,v8.3a,v8a,vh,zcm,zcz,zcz-gp") }, + { str_lit("carmel"), str_lit("CONTEXTIDREL2,aes,carmel,ccpp,crc,crypto,el2vmsa,el3,fp-armv8,fullfp16,lor,lse,neon,pan,pan-rwv,ras,rdm,sha2,uaops,v8.1a,v8.2a,v8a,vh") }, + { str_lit("cortex-a34"), str_lit("a35,aes,crc,crypto,el2vmsa,el3,fp-armv8,neon,perfmon,sha2,v8a") }, + { str_lit("cortex-a35"), str_lit("a35,aes,crc,crypto,el2vmsa,el3,fp-armv8,neon,perfmon,sha2,v8a") }, + { str_lit("cortex-a510"), str_lit("CONTEXTIDREL2,a510,altnzcv,am,bf16,bti,ccdp,ccidx,ccpp,complxnum,crc,dit,dotprod,el2vmsa,el3,ete,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mec,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,rme,sb,sel2,specrestrict,ssbs,sve,sve2,sve2-bitperm,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,use-scalar-inc-vl,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") }, + { str_lit("cortex-a53"), str_lit("a53,aes,balance-fp-ops,crc,crypto,el2vmsa,el3,fp-armv8,fuse-adrp-add,fuse-aes,neon,perfmon,sha2,use-postra-scheduler,v8a") }, + { str_lit("cortex-a55"), str_lit("CONTEXTIDREL2,a55,aes,ccpp,crc,crypto,dotprod,el2vmsa,el3,fp-armv8,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,lor,lse,neon,pan,pan-rwv,perfmon,ras,rcpc,rdm,sha2,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") }, + { str_lit("cortex-a57"), str_lit("a57,aes,balance-fp-ops,crc,crypto,el2vmsa,el3,enable-select-opt,fp-armv8,fuse-adrp-add,fuse-aes,fuse-literals,neon,perfmon,predictable-select-expensive,sha2,use-postra-scheduler,v8a") }, + { str_lit("cortex-a65"), str_lit("CONTEXTIDREL2,a65,aes,ccpp,crc,crypto,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-literals,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,ssbs,uaops,v8.1a,v8.2a,v8a,vh") }, + { str_lit("cortex-a65ae"), str_lit("CONTEXTIDREL2,a65,aes,ccpp,crc,crypto,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-literals,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,ssbs,uaops,v8.1a,v8.2a,v8a,vh") }, + { str_lit("cortex-a710"), str_lit("CONTEXTIDREL2,a710,addr-lsl-fast,altnzcv,alu-lsl-fast,am,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,ete,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mec,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,rme,sb,sel2,specrestrict,ssbs,sve,sve2,sve2-bitperm,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,use-scalar-inc-vl,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") }, + { str_lit("cortex-a715"), str_lit("CONTEXTIDREL2,a715,addr-lsl-fast,altnzcv,alu-lsl-fast,am,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,ete,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mec,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,rme,sb,sel2,spe,specrestrict,ssbs,sve,sve2,sve2-bitperm,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,use-scalar-inc-vl,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") }, + { str_lit("cortex-a72"), str_lit("a72,aes,crc,crypto,el2vmsa,el3,enable-select-opt,fp-armv8,fuse-adrp-add,fuse-aes,fuse-literals,neon,perfmon,predictable-select-expensive,sha2,v8a") }, + { str_lit("cortex-a73"), str_lit("a73,aes,crc,crypto,el2vmsa,el3,enable-select-opt,fp-armv8,fuse-adrp-add,fuse-aes,neon,perfmon,predictable-select-expensive,sha2,v8a") }, + { str_lit("cortex-a75"), str_lit("CONTEXTIDREL2,a75,aes,ccpp,crc,crypto,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,uaops,v8.1a,v8.2a,v8a,vh") }, + { str_lit("cortex-a76"), str_lit("CONTEXTIDREL2,a76,addr-lsl-fast,aes,alu-lsl-fast,ccpp,crc,crypto,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,ssbs,uaops,v8.1a,v8.2a,v8a,vh") }, + { str_lit("cortex-a76ae"), str_lit("CONTEXTIDREL2,a76,addr-lsl-fast,aes,alu-lsl-fast,ccpp,crc,crypto,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,ssbs,uaops,v8.1a,v8.2a,v8a,vh") }, + { str_lit("cortex-a77"), str_lit("CONTEXTIDREL2,a77,addr-lsl-fast,aes,alu-lsl-fast,ccpp,cmp-bcc-fusion,crc,crypto,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,ssbs,uaops,v8.1a,v8.2a,v8a,vh") }, + { str_lit("cortex-a78"), str_lit("CONTEXTIDREL2,a78,addr-lsl-fast,aes,alu-lsl-fast,ccpp,cmp-bcc-fusion,crc,crypto,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,spe,ssbs,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") }, + { str_lit("cortex-a78c"), str_lit("CONTEXTIDREL2,a78c,addr-lsl-fast,aes,alu-lsl-fast,ccpp,cmp-bcc-fusion,crc,crypto,dotprod,el2vmsa,el3,enable-select-opt,flagm,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,spe,ssbs,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") }, + { str_lit("cortex-r82"), str_lit("CONTEXTIDREL2,ccidx,ccpp,complxnum,cortex-r82,crc,dit,dotprod,flagm,fp-armv8,fp16fml,fullfp16,jsconv,lse,neon,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,specrestrict,ssbs,tlb-rmi,tracev8.4,uaops,use-postra-scheduler,v8r") }, + { str_lit("cortex-x1"), str_lit("CONTEXTIDREL2,addr-lsl-fast,aes,alu-lsl-fast,ccpp,cmp-bcc-fusion,cortex-x1,crc,crypto,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,spe,ssbs,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") }, + { str_lit("cortex-x1c"), str_lit("CONTEXTIDREL2,addr-lsl-fast,aes,alu-lsl-fast,ccpp,cmp-bcc-fusion,cortex-x1,crc,crypto,dotprod,el2vmsa,el3,enable-select-opt,flagm,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,lse2,neon,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,ras,rcpc,rcpc-immo,rdm,sha2,spe,ssbs,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") }, + { str_lit("cortex-x2"), str_lit("CONTEXTIDREL2,addr-lsl-fast,altnzcv,alu-lsl-fast,am,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,cortex-x2,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,ete,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mec,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,rme,sb,sel2,specrestrict,ssbs,sve,sve2,sve2-bitperm,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,use-scalar-inc-vl,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") }, + { str_lit("cortex-x3"), str_lit("CONTEXTIDREL2,addr-lsl-fast,altnzcv,alu-lsl-fast,am,bf16,bti,ccdp,ccidx,ccpp,complxnum,cortex-x3,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,ete,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mec,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,rme,sb,sel2,spe,specrestrict,ssbs,sve,sve2,sve2-bitperm,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,use-scalar-inc-vl,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") }, + { str_lit("cyclone"), str_lit("aes,alternate-sextload-cvt-f32-pattern,apple-a7,apple-a7-sysreg,arith-bcc-fusion,arith-cbz-fusion,crypto,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fuse-aes,fuse-crypto-eor,neon,perfmon,sha2,store-pair-suppress,v8a,zcm,zcz,zcz-fp-workaround,zcz-gp") }, + { str_lit("exynos-m3"), str_lit("addr-lsl-fast,aes,alu-lsl-fast,crc,crypto,el2vmsa,el3,exynos-cheap-as-move,exynosm3,force-32bit-jump-tables,fp-armv8,fuse-address,fuse-adrp-add,fuse-aes,fuse-csel,fuse-literals,neon,perfmon,predictable-select-expensive,sha2,store-pair-suppress,use-postra-scheduler,v8a") }, + { str_lit("exynos-m4"), str_lit("CONTEXTIDREL2,addr-lsl-fast,aes,alu-lsl-fast,arith-bcc-fusion,arith-cbz-fusion,ccpp,crc,crypto,dotprod,el2vmsa,el3,exynos-cheap-as-move,exynosm4,force-32bit-jump-tables,fp-armv8,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-arith-logic,fuse-csel,fuse-literals,lor,lse,neon,pan,pan-rwv,perfmon,ras,rdm,sha2,store-pair-suppress,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh,zcz,zcz-gp") }, + { str_lit("exynos-m5"), str_lit("CONTEXTIDREL2,addr-lsl-fast,aes,alu-lsl-fast,arith-bcc-fusion,arith-cbz-fusion,ccpp,crc,crypto,dotprod,el2vmsa,el3,exynos-cheap-as-move,exynosm4,force-32bit-jump-tables,fp-armv8,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-arith-logic,fuse-csel,fuse-literals,lor,lse,neon,pan,pan-rwv,perfmon,ras,rdm,sha2,store-pair-suppress,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh,zcz,zcz-gp") }, + { str_lit("falkor"), str_lit("addr-lsl-fast,aes,alu-lsl-fast,crc,crypto,el2vmsa,el3,falkor,fp-armv8,neon,perfmon,predictable-select-expensive,rdm,sha2,slow-strqro-store,store-pair-suppress,use-postra-scheduler,v8a,zcz,zcz-gp") }, + { str_lit("generic"), str_lit("enable-select-opt,ete,fp-armv8,fuse-adrp-add,fuse-aes,neon,trbe,use-postra-scheduler") }, + { str_lit("kryo"), str_lit("addr-lsl-fast,aes,alu-lsl-fast,crc,crypto,el2vmsa,el3,fp-armv8,kryo,neon,perfmon,predictable-select-expensive,sha2,store-pair-suppress,use-postra-scheduler,v8a,zcz,zcz-gp") }, + { str_lit("neoverse-512tvb"), str_lit("CONTEXTIDREL2,addr-lsl-fast,aes,alu-lsl-fast,am,bf16,ccdp,ccidx,ccpp,complxnum,crc,crypto,dit,dotprod,el2vmsa,el3,enable-select-opt,flagm,fp-armv8,fp16fml,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mpam,neon,neoverse512tvb,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,rand,ras,rcpc,rcpc-immo,rdm,sel2,sha2,spe,ssbs,sve,tlb-rmi,tracev8.4,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8a,vh") }, + { str_lit("neoverse-e1"), str_lit("CONTEXTIDREL2,aes,ccpp,crc,crypto,dotprod,el2vmsa,el3,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,neoversee1,pan,pan-rwv,perfmon,ras,rcpc,rdm,sha2,ssbs,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") }, + { str_lit("neoverse-n1"), str_lit("CONTEXTIDREL2,addr-lsl-fast,aes,alu-lsl-fast,ccpp,crc,crypto,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,neoversen1,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,spe,ssbs,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") }, + { str_lit("neoverse-n2"), str_lit("CONTEXTIDREL2,addr-lsl-fast,altnzcv,alu-lsl-fast,am,bf16,bti,ccdp,ccidx,ccpp,complxnum,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,ete,flagm,fp-armv8,fptoint,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mec,mpam,mte,neon,neoversen2,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,rme,sb,sel2,specrestrict,ssbs,sve,sve2,sve2-bitperm,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,use-scalar-inc-vl,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") }, + { str_lit("neoverse-v1"), str_lit("CONTEXTIDREL2,addr-lsl-fast,aes,alu-lsl-fast,am,bf16,ccdp,ccidx,ccpp,complxnum,crc,crypto,dit,dotprod,el2vmsa,el3,enable-select-opt,flagm,fp-armv8,fp16fml,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mpam,neon,neoversev1,no-sve-fp-ld1r,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,rand,ras,rcpc,rcpc-immo,rdm,sel2,sha2,spe,ssbs,sve,tlb-rmi,tracev8.4,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8a,vh") }, + { str_lit("neoverse-v2"), str_lit("CONTEXTIDREL2,addr-lsl-fast,altnzcv,alu-lsl-fast,am,bf16,bti,ccdp,ccidx,ccpp,complxnum,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,ete,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mec,mpam,mte,neon,neoversev2,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,rand,ras,rcpc,rcpc-immo,rdm,rme,sb,sel2,spe,specrestrict,ssbs,sve,sve2,sve2-bitperm,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,use-scalar-inc-vl,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") }, + { str_lit("saphira"), str_lit("CONTEXTIDREL2,addr-lsl-fast,aes,alu-lsl-fast,am,ccidx,ccpp,complxnum,crc,crypto,dit,dotprod,el2vmsa,el3,flagm,fp-armv8,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,ras,rcpc,rcpc-immo,rdm,saphira,sel2,sha2,spe,store-pair-suppress,tlb-rmi,tracev8.4,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8a,vh,zcz,zcz-gp") }, + { str_lit("thunderx"), str_lit("aes,crc,crypto,el2vmsa,el3,fp-armv8,neon,perfmon,predictable-select-expensive,sha2,store-pair-suppress,thunderx,use-postra-scheduler,v8a") }, + { str_lit("thunderx2t99"), str_lit("CONTEXTIDREL2,aes,aggressive-fma,arith-bcc-fusion,crc,crypto,el2vmsa,el3,fp-armv8,lor,lse,neon,pan,predictable-select-expensive,rdm,sha2,store-pair-suppress,thunderx2t99,use-postra-scheduler,v8.1a,v8a,vh") }, + { str_lit("thunderx3t110"), str_lit("CONTEXTIDREL2,aes,aggressive-fma,arith-bcc-fusion,balance-fp-ops,ccidx,ccpp,complxnum,crc,crypto,el2vmsa,el3,fp-armv8,jsconv,lor,lse,neon,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,store-pair-suppress,strict-align,thunderx3t110,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8a,vh") }, + { str_lit("thunderxt81"), str_lit("aes,crc,crypto,el2vmsa,el3,fp-armv8,neon,perfmon,predictable-select-expensive,sha2,store-pair-suppress,thunderxt81,use-postra-scheduler,v8a") }, + { str_lit("thunderxt83"), str_lit("aes,crc,crypto,el2vmsa,el3,fp-armv8,neon,perfmon,predictable-select-expensive,sha2,store-pair-suppress,thunderxt83,use-postra-scheduler,v8a") }, + { str_lit("thunderxt88"), str_lit("aes,crc,crypto,el2vmsa,el3,fp-armv8,neon,perfmon,predictable-select-expensive,sha2,store-pair-suppress,thunderxt88,use-postra-scheduler,v8a") }, + { str_lit("tsv110"), str_lit("CONTEXTIDREL2,aes,ccpp,complxnum,crc,crypto,dotprod,el2vmsa,el3,fp-armv8,fp16fml,fullfp16,fuse-aes,jsconv,lor,lse,neon,pan,pan-rwv,perfmon,ras,rdm,sha2,spe,store-pair-suppress,tsv110,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") }, + // TargetArch_wasm32: + { str_lit("bleeding-edge"), str_lit("atomics,bulk-memory,mutable-globals,nontrapping-fptoint,sign-ext,simd128,tail-call") }, + { str_lit("generic"), str_lit("mutable-globals,sign-ext") }, + { str_lit("mvp"), str_lit("") }, + // TargetArch_wasm64p32: + { str_lit("bleeding-edge"), str_lit("atomics,bulk-memory,mutable-globals,nontrapping-fptoint,sign-ext,simd128,tail-call") }, + { str_lit("generic"), str_lit("mutable-globals,sign-ext") }, + { str_lit("mvp"), str_lit("") }, }; gb_global String target_endian_names[TargetEndian_COUNT] = { @@ -443,9 +894,9 @@ struct BuildContext { PtrMap defined_values; - BlockingMutex target_features_mutex; StringSet target_features_set; String target_features_string; + bool strict_target_features; String minimum_os_version_string; bool minimum_os_version_string_given; @@ -1596,48 +2047,53 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta #include "microsoft_craziness.h" #endif +// NOTE: the target feature and microarch lists are all sorted, so if it turns out to be slow (I don't think it will) +// a binary search is possible. -gb_internal Array split_by_comma(String const &list) { - isize n = 1; - for (isize i = 0; i < list.len; i++) { - if (list.text[i] == ',') { - n++; +gb_internal bool check_single_target_feature_is_valid(String const &feature_list, String const &feature) { + String_Iterator it = {feature_list, 0}; + for (;;) { + String str = string_split_iterator(&it, ','); + if (str == "") break; + if (str == feature) { + return true; } } - 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; + return false; +} + +gb_internal bool check_target_feature_is_valid(String const &feature, TargetArchKind arch, String *invalid) { + String feature_list = target_features_list[arch]; + String_Iterator it = {feature, 0}; + for (;;) { + String str = string_split_iterator(&it, ','); + if (str == "") break; + if (!check_single_target_feature_is_valid(feature_list, str)) { + if (invalid) *invalid = str; + return false; } - res[i] = substring(s, 0, m); - s = substring(s, m+1, s.len); } - return res; -} -gb_internal bool check_target_feature_is_valid(TokenPos pos, String const &feature) { - // TODO(bill): check_target_feature_is_valid return true; } -gb_internal 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 (String const &item : items) { - if (!check_target_feature_is_valid(pos, item)) { - error(pos, "Target feature '%.*s' is not valid", LIT(item)); - return false; +gb_internal bool check_target_feature_is_valid_globally(String const &feature, String *invalid) { + String_Iterator it = {feature, 0}; + for (;;) { + String str = string_split_iterator(&it, ','); + if (str == "") break; + + bool valid = false; + for (int arch = TargetArch_Invalid; arch < TargetArch_COUNT; arch += 1) { + if (check_target_feature_is_valid(str, cast(TargetArchKind)arch, invalid)) { + valid = true; + break; + } } - if (!string_set_exists(&bc->target_features_set, item)) { - error(pos, "Target feature '%.*s' is not enabled", LIT(item)); + + if (!valid) { + if (invalid) *invalid = str; return false; } } @@ -1645,54 +2101,35 @@ gb_internal bool check_target_feature_is_enabled(TokenPos pos, String const &tar return true; } -gb_internal 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); - for (String const &item : items) { - if (!check_target_feature_is_valid(pos, item)) { - error(pos, "Target feature '%.*s' is not valid", LIT(item)); - continue; - } - - string_set_add(&bc->target_features_set, item); - } - array_free(&items); +gb_internal bool check_target_feature_is_valid_for_target_arch(String const &feature, String *invalid) { + return check_target_feature_is_valid(feature, build_context.metrics.arch, invalid); } - -gb_internal char const *target_features_set_to_cstring(gbAllocator allocator, bool with_quotes, bool with_plus) { - isize len = 0; - isize i = 0; - for (String const &feature : build_context.target_features_set) { - if (i != 0) { - len += 1; +gb_internal bool check_target_feature_is_enabled(String const &feature, String *not_enabled) { + String_Iterator it = {feature, 0}; + for (;;) { + String str = string_split_iterator(&it, ','); + if (str == "") break; + if (!string_set_exists(&build_context.target_features_set, str)) { + if (not_enabled) *not_enabled = str; + return false; } - len += feature.len; - if (with_quotes) len += 2; - if (with_plus) len += 1; - i += 1; } - char *features = gb_alloc_array(allocator, char, len+1); - len = 0; - i = 0; - for (String const &feature : build_context.target_features_set) { - if (i != 0) { - features[len++] = ','; - } - if (with_quotes) features[len++] = '"'; - if (with_plus) features[len++] = '+'; - gb_memmove(features + len, feature.text, feature.len); - len += feature.len; - if (with_quotes) features[len++] = '"'; - i += 1; - } - features[len++] = 0; + return true; +} - return features; +gb_internal bool check_target_feature_is_superset_of(String const &superset, String const &of, String *missing) { + String_Iterator it = {of, 0}; + for (;;) { + String str = string_split_iterator(&it, ','); + if (str == "") break; + if (!check_single_target_feature_is_valid(superset, str)) { + if (missing) *missing = str; + return false; + } + } + return true; } // NOTE(Jeroen): Set/create the output and other paths and report an error as appropriate. @@ -1983,10 +2420,6 @@ gb_internal bool init_build_paths(String init_filename) { } } - if (bc->target_features_string.len != 0) { - enable_target_feature({}, bc->target_features_string); - } - return true; } diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index c3c217ec7..825fc6448 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1719,6 +1719,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As case BuiltinProc_objc_register_selector: case BuiltinProc_objc_register_class: case BuiltinProc_atomic_type_is_lock_free: + case BuiltinProc_has_target_feature: // NOTE(bill): The first arg may be a Type, this will be checked case by case break; @@ -3663,6 +3664,41 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As break; } + case BuiltinProc_has_target_feature: { + String features = str_lit(""); + + check_expr_or_type(c, operand, ce->args[0]); + + if (is_type_string(operand->type) && operand->mode == Addressing_Constant) { + GB_ASSERT(operand->value.kind == ExactValue_String); + features = operand->value.value_string; + } else { + Type *pt = base_type(operand->type); + if (pt->kind == Type_Proc) { + if (pt->Proc.require_target_feature.len != 0) { + GB_ASSERT(pt->Proc.enable_target_feature.len == 0); + features = pt->Proc.require_target_feature; + } else if (pt->Proc.enable_target_feature.len != 0) { + features = pt->Proc.enable_target_feature; + } else { + error(ce->args[0], "Expected the procedure type given to '%.*s' to have @(require_target_feature=\"...\") or @(enable_target_feature=\"...\")", LIT(builtin_name)); + } + } else { + error(ce->args[0], "Expected a constant string or procedure type for '%.*s'", LIT(builtin_name)); + } + } + + String invalid; + if (!check_target_feature_is_valid_globally(features, &invalid)) { + error(ce->args[0], "Target feature '%.*s' is not a valid target feature", LIT(invalid)); + } + + operand->value = exact_value_bool(check_target_feature_is_enabled(features, nullptr)); + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + } + case BuiltinProc_soa_struct: { Operand x = {}; Operand y = {}; @@ -6014,7 +6050,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As return false; } - enable_target_feature({}, str_lit("atomics")); + if (!check_target_feature_is_enabled(str_lit("atomics"), nullptr)) { + error(call, "'%.*s' requires target feature 'atomics' to be enabled, enable it with -target-features:\"atomics\" or choose a different -microarch", LIT(builtin_name)); + return false; + } Operand ptr = {}; Operand expected = {}; @@ -6068,7 +6107,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As return false; } - enable_target_feature({}, str_lit("atomics")); + if (!check_target_feature_is_enabled(str_lit("atomics"), nullptr)) { + error(call, "'%.*s' requires target feature 'atomics' to be enabled, enable it with -target-features:\"atomics\" or choose a different -microarch", LIT(builtin_name)); + return false; + } Operand ptr = {}; Operand waiters = {}; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 952a877a4..5b9486873 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -886,17 +886,37 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { check_objc_methods(ctx, e, ac); - 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; + { + if (ac.require_target_feature.len != 0 && ac.enable_target_feature.len != 0) { + error(e->token, "A procedure cannot have both @(require_target_feature=\"...\") and @(enable_target_feature=\"...\")"); + } + + if (build_context.strict_target_features && ac.enable_target_feature.len != 0) { + ac.require_target_feature = ac.enable_target_feature; + ac.enable_target_feature.len = 0; + } + + if (ac.require_target_feature.len != 0) { + pt->require_target_feature = ac.require_target_feature; + String invalid; + if (!check_target_feature_is_valid_globally(ac.require_target_feature, &invalid)) { + error(e->token, "Required target feature '%.*s' is not a valid target feature", LIT(invalid)); + } else if (!check_target_feature_is_enabled(ac.require_target_feature, nullptr)) { + e->flags |= EntityFlag_Disabled; + } + } else if (ac.enable_target_feature.len != 0) { + + // NOTE: disallow wasm, features on that arch are always global to the module. + if (is_arch_wasm()) { + error(e->token, "@(enable_target_feature=\"...\") is not allowed on wasm, features for wasm must be declared globally"); + } + + pt->enable_target_feature = ac.enable_target_feature; + String invalid; + if (!check_target_feature_is_valid_globally(ac.enable_target_feature, &invalid)) { + error(e->token, "Procedure enabled target feature '%.*s' is not a valid target feature", LIT(invalid)); + } } - } 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) { @@ -1370,6 +1390,10 @@ gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, D continue; } + if (p->flags & EntityFlag_Disabled) { + continue; + } + String name = p->token.string; for (isize k = j+1; k < pge->entities.count; k++) { @@ -1387,6 +1411,10 @@ gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, D ERROR_BLOCK(); + if (q->flags & EntityFlag_Disabled) { + continue; + } + ProcTypeOverloadKind kind = are_proc_types_overload_safe(p->type, q->type); bool both_have_where_clauses = false; if (p->decl_info->proc_lit != nullptr && q->decl_info->proc_lit != nullptr) { @@ -1423,6 +1451,7 @@ gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, D break; case ProcOverload_ParamCount: case ProcOverload_ParamTypes: + case ProcOverload_TargetFeatures: // This is okay :) break; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 06d0a8b12..490c9aae7 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6526,12 +6526,17 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, array_add(&proc_entities, proc); } + int max_matched_features = 0; gbString expr_name = expr_to_string(operand->expr); defer (gb_string_free(expr_name)); for_array(i, procs) { Entity *p = procs[i]; + if (p->flags & EntityFlag_Disabled) { + continue; + } + Type *pt = base_type(p->type); if (pt != nullptr && is_type_proc(pt)) { CallArgumentData data = {}; @@ -6562,11 +6567,24 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, item.score += assign_score_function(1); } + max_matched_features = gb_max(max_matched_features, matched_target_features(&pt->Proc)); + item.index = index; array_add(&valids, item); } } + if (max_matched_features > 0) { + for_array(i, valids) { + Entity *p = procs[valids[i].index]; + Type *t = base_type(p->type); + GB_ASSERT(t->kind == Type_Proc); + + int matched = matched_target_features(&t->Proc); + valids[i].score += assign_score_function(max_matched_features-matched); + } + } + if (valids.count > 1) { array_sort(valids, valid_index_and_score_cmp); i64 best_score = valids[0].score; @@ -6708,7 +6726,11 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, ERROR_BLOCK(); error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name); - print_argument_types(); + if (positional_operands.count == 0 && named_operands.count == 0) { + error_line("\tNo given arguments\n"); + } else { + print_argument_types(); + } for (auto const &valid : valids) { Entity *proc = proc_entities[valid.index]; @@ -7553,8 +7575,11 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } } + bool is_call_inlined = false; + switch (inlining) { case ProcInlining_inline: + is_call_inlined = true; if (proc != nullptr) { Entity *e = entity_from_expr(proc); if (e != nullptr && e->kind == Entity_Procedure) { @@ -7570,6 +7595,47 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c break; case ProcInlining_no_inline: break; + case ProcInlining_none: + if (proc != nullptr) { + Entity *e = entity_from_expr(proc); + if (e != nullptr && e->kind == Entity_Procedure) { + DeclInfo *decl = e->decl_info; + if (decl->proc_lit) { + ast_node(pl, ProcLit, decl->proc_lit); + if (pl->inlining == ProcInlining_inline) { + is_call_inlined = true; + } + } + } + } + } + + { + String invalid; + if (pt->kind == Type_Proc && pt->Proc.require_target_feature.len != 0) { + if (!check_target_feature_is_valid_for_target_arch(pt->Proc.require_target_feature, &invalid)) { + error(call, "Called procedure requires target feature '%.*s' which is invalid for the build target", LIT(invalid)); + } else if (!check_target_feature_is_enabled(pt->Proc.require_target_feature, &invalid)) { + error(call, "Calling this procedure requires target feature '%.*s' to be enabled", LIT(invalid)); + } + } + + if (pt->kind == Type_Proc && pt->Proc.enable_target_feature.len != 0) { + if (!check_target_feature_is_valid_for_target_arch(pt->Proc.enable_target_feature, &invalid)) { + error(call, "Called procedure enables target feature '%.*s' which is invalid for the build target", LIT(invalid)); + } + + // NOTE: Due to restrictions in LLVM you can not inline calls with a superset of features. + if (is_call_inlined) { + GB_ASSERT(c->curr_proc_decl); + GB_ASSERT(c->curr_proc_decl->entity); + GB_ASSERT(c->curr_proc_decl->entity->type->kind == Type_Proc); + String scope_features = c->curr_proc_decl->entity->type->Proc.enable_target_feature; + if (!check_target_feature_is_superset_of(scope_features, pt->Proc.enable_target_feature, &invalid)) { + error(call, "Inlined procedure enables target feature '%.*s', this requires the calling procedure to at least enable the same feature", LIT(invalid)); + } + } + } } operand->expr = call; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index c15ec7137..8419c6568 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -44,6 +44,8 @@ enum BuiltinProcId { // "Intrinsics" BuiltinProc_is_package_imported, + BuiltinProc_has_target_feature, + BuiltinProc_transpose, BuiltinProc_outer_product, BuiltinProc_hadamard_product, @@ -354,6 +356,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { // "Intrinsics" {STR_LIT("is_package_imported"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("has_target_feature"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("transpose"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("outer_product"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("hadamard_product"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/entity.cpp b/src/entity.cpp index a12e1d0a6..d76d5f441 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -252,10 +252,8 @@ struct Entity { bool is_foreign : 1; bool is_export : 1; bool generated_from_polymorphic : 1; - bool target_feature_disabled : 1; bool entry_point_only : 1; bool has_instrumentation : 1; - String target_feature; } Procedure; struct { Array entities; @@ -502,4 +500,4 @@ gb_internal bool is_entity_local_variable(Entity *e) { return ((e->scope->flags &~ ScopeFlag_ContextDefined) == 0) || (e->scope->flags & ScopeFlag_Proc) != 0; -} \ No newline at end of file +} diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 4b94cf020..fad130b99 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -41,6 +41,37 @@ String get_default_microarchitecture() { return default_march; } +String get_final_microarchitecture() { + BuildContext *bc = &build_context; + + String microarch = bc->microarch; + if (microarch.len == 0) { + microarch = get_default_microarchitecture(); + } else if (microarch == str_lit("native")) { + microarch = make_string_c(LLVMGetHostCPUName()); + } + return microarch; +} + +gb_internal String get_default_features() { + BuildContext *bc = &build_context; + + int off = 0; + for (int i = 0; i < bc->metrics.arch; i += 1) { + off += target_microarch_counts[i]; + } + + String microarch = get_final_microarchitecture(); + for (int i = off; i < off+target_microarch_counts[bc->metrics.arch]; i += 1) { + if (microarch_features_list[i].microarch == microarch) { + return microarch_features_list[i].features; + } + } + + GB_PANIC("unknown microarch"); + return {}; +} + gb_internal void lb_add_foreign_library_path(lbModule *m, Entity *e) { if (e == nullptr) { return; @@ -2468,69 +2499,24 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { code_mode = LLVMCodeModelKernel; } - String host_cpu_name = copy_string(permanent_allocator(), make_string_c(LLVMGetHostCPUName())); - String llvm_cpu = get_default_microarchitecture(); - char const *llvm_features = ""; - if (build_context.microarch.len != 0) { - if (build_context.microarch == "native") { - llvm_cpu = host_cpu_name; - } else { - llvm_cpu = copy_string(permanent_allocator(), build_context.microarch); - } - if (llvm_cpu == host_cpu_name) { - llvm_features = LLVMGetHostCPUFeatures(); + String llvm_cpu = get_final_microarchitecture(); + + gbString llvm_features = gb_string_make(temporary_allocator(), ""); + String_Iterator it = {build_context.target_features_string, 0}; + bool first = true; + for (;;) { + String str = string_split_iterator(&it, ','); + if (str == "") break; + if (!first) { + llvm_features = gb_string_appendc(llvm_features, ","); } - } + first = false; - // NOTE(Jeroen): Uncomment to get the list of supported microarchitectures. - /* - if (build_context.microarch == "?") { - string_set_add(&build_context.target_features_set, str_lit("+cpuhelp")); + llvm_features = gb_string_appendc(llvm_features, "+"); + llvm_features = gb_string_append_length(llvm_features, str.text, str.len); } - */ - if (build_context.target_features_set.entries.count != 0) { - // Prefix all of the features with a `+`, because we are - // enabling additional features. - char const *additional_features = target_features_set_to_cstring(permanent_allocator(), false, true); - - String f_string = make_string_c(llvm_features); - String a_string = make_string_c(additional_features); - isize f_len = f_string.len; - - if (f_len == 0) { - // The common case is that llvm_features is empty, so - // the target_features_set additions can be used as is. - llvm_features = additional_features; - } else { - // The user probably specified `-microarch:native`, so - // llvm_features is populated by LLVM's idea of what - // the host CPU supports. - // - // As far as I can tell, (which is barely better than - // wild guessing), a bitset is formed by parsing the - // string left to right. - // - // So, llvm_features + ',' + additonal_features, will - // makes the target_features_set override llvm_features. - - char *tmp = gb_alloc_array(permanent_allocator(), char, f_len + 1 + a_string.len + 1); - isize len = 0; - - // tmp = f_string - gb_memmove(tmp, f_string.text, f_string.len); - len += f_string.len; - // tmp += ',' - tmp[len++] = ','; - // tmp += a_string - gb_memmove(tmp + len, a_string.text, a_string.len); - len += a_string.len; - // tmp += NUL - tmp[len++] = 0; - - llvm_features = tmp; - } - } + debugf("CPU: %.*s, Features: %s\n", LIT(llvm_cpu), llvm_features); // GB_ASSERT_MSG(LLVMTargetHasAsmBackend(target)); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index f73698d34..898c9ac31 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -177,17 +177,24 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i break; } - 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 (pt->Proc.enable_target_feature.len != 0) { + gbString feature_str = gb_string_make(temporary_allocator(), ""); + + String_Iterator it = {pt->Proc.enable_target_feature, 0}; + bool first = true; + for (;;) { + String str = string_split_iterator(&it, ','); + if (str == "") break; + if (!first) { + feature_str = gb_string_appendc(feature_str, ","); + } + first = false; + + feature_str = gb_string_appendc(feature_str, "+"); + feature_str = gb_string_append_length(feature_str, str.text, str.len); } + + lb_add_attribute_to_proc_with_string(m, p->value, make_string_c("target-features"), make_string_c(feature_str)); } if (entity->flags & EntityFlag_Cold) { diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index c01ab0692..db99ebc99 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -1708,7 +1708,8 @@ gb_internal lbValue lb_emit_mul_add(lbProcedure *p, lbValue a, lbValue b, lbValu if (is_possible) { switch (build_context.metrics.arch) { case TargetArch_amd64: - if (type_size_of(t) == 2) { + // NOTE: using the intrinsic when not supported causes slow codegen (See #2928). + if (type_size_of(t) == 2 || !check_target_feature_is_enabled(str_lit("fma"), nullptr)) { is_possible = false; } break; diff --git a/src/main.cpp b/src/main.cpp index ee7de7f81..93685acb9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -272,6 +272,7 @@ enum BuildFlagKind { BuildFlag_ExtraAssemblerFlags, BuildFlag_Microarch, BuildFlag_TargetFeatures, + BuildFlag_StrictTargetFeatures, BuildFlag_MinimumOSVersion, BuildFlag_NoThreadLocal, @@ -467,6 +468,7 @@ gb_internal bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_ExtraAssemblerFlags, str_lit("extra-assembler-flags"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_Microarch, str_lit("microarch"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_TargetFeatures, str_lit("target-features"), BuildFlagParam_String, Command__does_build); + add_flag(&build_flags, BuildFlag_StrictTargetFeatures, str_lit("strict-target-features"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_MinimumOSVersion, str_lit("minimum-os-version"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_RelocMode, str_lit("reloc-mode"), BuildFlagParam_String, Command__does_build); @@ -1083,6 +1085,9 @@ gb_internal bool parse_build_flags(Array args) { string_to_lower(&build_context.target_features_string); break; } + case BuildFlag_StrictTargetFeatures: + build_context.strict_target_features = true; + break; case BuildFlag_MinimumOSVersion: { GB_ASSERT(value.kind == ExactValue_String); build_context.minimum_os_version_string = value.value_string; @@ -1981,7 +1986,20 @@ gb_internal void print_show_help(String const arg0, String const &command) { print_usage_line(2, "Examples:"); print_usage_line(3, "-microarch:sandybridge"); print_usage_line(3, "-microarch:native"); - print_usage_line(3, "-microarch:? for a list"); + print_usage_line(3, "-microarch:\"?\" for a list"); + print_usage_line(0, ""); + + print_usage_line(1, "-target-features:"); + print_usage_line(2, "Specifies CPU features to enable on top of the enabled features implied by -microarch."); + print_usage_line(2, "Examples:"); + print_usage_line(3, "-target-features:atomics"); + print_usage_line(3, "-target-features:\"sse2,aes\""); + print_usage_line(3, "-target-features:\"?\" for a list"); + print_usage_line(0, ""); + + print_usage_line(1, "-strict-target-features"); + print_usage_line(2, "Makes @(enable_target_features=\"...\") behave the same way as @(require_target_features=\"...\")."); + print_usage_line(2, "This enforces that all generated code uses features supported by the combination of -target, -microarch, and -target-features."); print_usage_line(0, ""); print_usage_line(1, "-reloc-mode:"); @@ -2663,7 +2681,7 @@ int main(int arg_count, char const **arg_ptr) { // Check chosen microarchitecture. If not found or ?, print list. bool print_microarch_list = true; - if (build_context.microarch.len == 0) { + if (build_context.microarch.len == 0 || build_context.microarch == str_lit("native")) { // Autodetect, no need to print list. print_microarch_list = false; } else { @@ -2680,6 +2698,11 @@ int main(int arg_count, char const **arg_ptr) { } } + // Set and check build paths... + if (!init_build_paths(init_filename)) { + return 1; + } + String default_march = get_default_microarchitecture(); if (print_microarch_list) { if (build_context.microarch != "?") { @@ -2703,13 +2726,57 @@ int main(int arg_count, char const **arg_ptr) { return 0; } - // Set and check build paths... - if (!init_build_paths(init_filename)) { - return 1; + String march = get_final_microarchitecture(); + String default_features = get_default_features(); + { + String_Iterator it = {default_features, 0}; + for (;;) { + String str = string_split_iterator(&it, ','); + if (str == "") break; + string_set_add(&build_context.target_features_set, str); + } + } + + if (build_context.target_features_string.len != 0) { + String_Iterator target_it = {build_context.target_features_string, 0}; + for (;;) { + String item = string_split_iterator(&target_it, ','); + if (item == "") break; + + String invalid; + if (!check_target_feature_is_valid_for_target_arch(item, &invalid) && item != str_lit("help")) { + if (item != str_lit("?")) { + gb_printf_err("Unkown target feature '%.*s'.\n", LIT(invalid)); + } + gb_printf("Possible -target-features for target %.*s are:\n", LIT(target_arch_names[build_context.metrics.arch])); + gb_printf("\n"); + + String feature_list = target_features_list[build_context.metrics.arch]; + String_Iterator it = {feature_list, 0}; + for (;;) { + String str = string_split_iterator(&it, ','); + if (str == "") break; + if (check_single_target_feature_is_valid(default_features, str)) { + if (has_ansi_terminal_colours()) { + gb_printf("\t%.*s\x1b[38;5;244m (implied by target microarch %.*s)\x1b[0m\n", LIT(str), LIT(march)); + } else { + gb_printf("\t%.*s (implied by current microarch %.*s)\n", LIT(str), LIT(march)); + } + } else { + gb_printf("\t%.*s\n", LIT(str)); + } + } + + return 1; + } + + string_set_add(&build_context.target_features_set, item); + } } if (build_context.show_debug_messages) { - debugf("Selected microarch: %.*s\n", LIT(default_march)); + debugf("Selected microarch: %.*s\n", LIT(march)); + debugf("Default microarch features: %.*s\n", LIT(default_features)); for_array(i, build_context.build_paths) { String build_path = path_to_string(heap_allocator(), build_context.build_paths[i]); debugf("build_paths[%ld]: %.*s\n", i, LIT(build_path)); diff --git a/src/types.cpp b/src/types.cpp index 18cb12ea1..3ec05059f 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -184,6 +184,8 @@ struct TypeProc { isize specialization_count; ProcCallingConvention calling_convention; i32 variadic_index; + String require_target_feature; + String enable_target_feature; // TODO(bill): Make this a flag set rather than bools bool variadic; bool require_results; @@ -2991,7 +2993,22 @@ gb_internal Type *union_tag_type(Type *u) { return t_uint; } +gb_internal int matched_target_features(TypeProc *t) { + if (t->require_target_feature.len == 0) { + return 0; + } + int matches = 0; + String_Iterator it = {t->require_target_feature, 0}; + for (;;) { + String str = string_split_iterator(&it, ','); + if (str == "") break; + if (check_target_feature_is_valid_for_target_arch(str, nullptr)) { + matches += 1; + } + } + return matches; +} enum ProcTypeOverloadKind { ProcOverload_Identical, // The types are identical @@ -3003,6 +3020,7 @@ enum ProcTypeOverloadKind { ProcOverload_ResultCount, ProcOverload_ResultTypes, ProcOverload_Polymorphic, + ProcOverload_TargetFeatures, ProcOverload_NotProcedure, @@ -3060,6 +3078,10 @@ gb_internal ProcTypeOverloadKind are_proc_types_overload_safe(Type *x, Type *y) } } + if (matched_target_features(&px) != matched_target_features(&py)) { + return ProcOverload_TargetFeatures; + } + if (px.params != nullptr && py.params != nullptr) { Entity *ex = px.params->Tuple.variables[0]; Entity *ey = py.params->Tuple.variables[0]; -- cgit v1.2.3 From d85c8f0b2c5989f7d14b02c9023060990d241111 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 9 May 2024 10:58:57 +0100 Subject: Fix #3555 --- src/check_decl.cpp | 11 +++++++++++ src/check_type.cpp | 1 + src/entity.cpp | 1 + src/types.cpp | 4 ++++ 4 files changed, 17 insertions(+) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 5b9486873..441c8000d 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1619,6 +1619,17 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de if (e->kind != Entity_Variable) { continue; } + if (is_type_polymorphic(e->type)) { + gbString s = type_to_string(e->type); + char const *msg = "Unspecialized polymorphic types are not allowed in procedure parameters, got %s"; + if (e->Variable.type_expr) { + error(e->Variable.type_expr, msg, s); + } else { + error(e->token, msg, s); + } + gb_string_free(s); + } + if (!(e->flags & EntityFlag_Using)) { continue; } diff --git a/src/check_type.cpp b/src/check_type.cpp index c119ce6b5..3d11b5012 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2076,6 +2076,7 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para param = alloc_entity_param(scope, name->Ident.token, type, is_using, true); param->Variable.param_value = param_value; param->Variable.field_group_index = field_group_index; + param->Variable.type_expr = type_expr; } } if (p->flags&FieldFlag_no_alias) { diff --git a/src/entity.cpp b/src/entity.cpp index d76d5f441..60ca208ec 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -210,6 +210,7 @@ struct Entity { CommentGroup *comment; } Constant; struct { + Ast *type_expr; // only used for some variables within procedure bodies Ast *init_expr; // only used for some variables within procedure bodies i32 field_index; i32 field_group_index; diff --git a/src/types.cpp b/src/types.cpp index 3ec05059f..30e009086 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -3267,6 +3267,10 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name } } + if (is_type_polymorphic(type)) { + // NOTE(bill): A polymorphic struct has no fields, this only hits in the case of an error + return sel; + } wait_signal_until_available(&type->Struct.fields_wait_signal); isize field_count = type->Struct.fields.count; if (field_count != 0) for_array(i, type->Struct.fields) { -- cgit v1.2.3 From 8e263de4aa2a48cb26bf235bdad162de5c1547c8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 11 May 2024 13:59:06 +0100 Subject: Fix error message handling --- src/check_decl.cpp | 2 +- src/types.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 441c8000d..1ec366ae7 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1619,7 +1619,7 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de if (e->kind != Entity_Variable) { continue; } - if (is_type_polymorphic(e->type)) { + if (is_type_polymorphic(e->type) && is_type_polymorphic_record_unspecialized(e->type)) { gbString s = type_to_string(e->type); char const *msg = "Unspecialized polymorphic types are not allowed in procedure parameters, got %s"; if (e->Variable.type_expr) { diff --git a/src/types.cpp b/src/types.cpp index 30e009086..47ed86f7a 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2100,8 +2100,8 @@ gb_internal bool is_type_polymorphic_record_unspecialized(Type *t) { t = base_type(t); if (t->kind == Type_Struct) { return t->Struct.is_polymorphic && !t->Struct.is_poly_specialized; - } else if (t->kind == Type_Struct) { - return t->Struct.is_polymorphic && !t->Struct.is_poly_specialized; + } else if (t->kind == Type_Union) { + return t->Union.is_polymorphic && !t->Union.is_poly_specialized; } return false; } -- cgit v1.2.3 From 66acbb7fed88ed9132dfc8107865e0ac27ed3ac8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 30 May 2024 21:48:23 +0100 Subject: Add `@(link_suffix=)` --- src/check_decl.cpp | 13 +++++++------ src/check_stmt.cpp | 4 ++-- src/checker.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/checker.hpp | 5 ++++- src/entity.cpp | 2 ++ 5 files changed, 61 insertions(+), 9 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 1ec366ae7..44b06d712 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -724,15 +724,16 @@ gb_internal Entity *init_entity_foreign_library(CheckerContext *ctx, Entity *e) return nullptr; } -gb_internal String handle_link_name(CheckerContext *ctx, Token token, String link_name, String link_prefix) { +gb_internal String handle_link_name(CheckerContext *ctx, Token token, String link_name, String link_prefix, String link_suffix) { if (link_prefix.len > 0) { if (link_name.len > 0) { error(token, "'link_name' and 'link_prefix' cannot be used together"); } else { - isize len = link_prefix.len + token.string.len; + isize len = link_prefix.len + token.string.len + link_suffix.len; u8 *name = gb_alloc_array(permanent_allocator(), u8, len+1); gb_memmove(name, &link_prefix[0], link_prefix.len); gb_memmove(name+link_prefix.len, &token.string[0], token.string.len); + gb_memmove(name+link_prefix.len+token.string.len, link_suffix.text, link_suffix.len); name[len] = 0; link_name = make_string(name, len); @@ -862,7 +863,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } TypeProc *pt = &proc_type->Proc; - AttributeContext ac = make_attribute_context(e->Procedure.link_prefix); + AttributeContext ac = make_attribute_context(e->Procedure.link_prefix, e->Procedure.link_suffix); if (d != nullptr) { check_decl_attributes(ctx, d->attributes, proc_decl_attribute, &ac); @@ -1015,7 +1016,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { e->deprecated_message = ac.deprecated_message; e->warning_message = ac.warning_message; - ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); + ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix,ac.link_suffix); if (ac.has_disabled_proc) { if (ac.disabled_proc) { e->flags |= EntityFlag_Disabled; @@ -1223,7 +1224,7 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast } e->flags |= EntityFlag_Visited; - AttributeContext ac = make_attribute_context(e->Variable.link_prefix); + AttributeContext ac = make_attribute_context(e->Variable.link_prefix, e->Variable.link_suffix); ac.init_expr_list_count = init_expr != nullptr ? 1 : 0; DeclInfo *decl = decl_info_of_entity(e); @@ -1244,7 +1245,7 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast if (ac.is_static) { error(e->token, "@(static) is not supported for global variables, nor required"); } - ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); + ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix, ac.link_suffix); if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) { e->Variable.thread_local_model.len = 0; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 866cdb5a1..2c37bced0 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2020,7 +2020,7 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f // TODO NOTE(bill): This technically checks things multple times - AttributeContext ac = make_attribute_context(ctx->foreign_context.link_prefix); + AttributeContext ac = make_attribute_context(ctx->foreign_context.link_prefix, ctx->foreign_context.link_suffix); check_decl_attributes(ctx, vd->attributes, var_decl_attribute, &ac); for (isize i = 0; i < entity_count; i++) { @@ -2037,7 +2037,7 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f e->type = init_type; e->state = EntityState_Resolved; } - ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); + ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix, ac.link_suffix); if (ac.link_name.len > 0) { e->Variable.link_name = ac.link_name; diff --git a/src/checker.cpp b/src/checker.cpp index 1ded6ea6e..ec58b9d8e 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3127,6 +3127,18 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) { error(elem, "Expected a string value for '%.*s'", LIT(name)); } return true; + } else if (name == "link_suffix") { + if (ev.kind == ExactValue_String) { + String link_suffix = ev.value_string; + if (!is_foreign_name_valid(link_suffix)) { + error(elem, "Invalid link suffix: '%.*s'", LIT(link_suffix)); + } else { + c->foreign_context.link_suffix = link_suffix; + } + } else { + error(elem, "Expected a string value for '%.*s'", LIT(name)); + } + return true; } else if (name == "private") { EntityVisiblityKind kind = EntityVisiblity_PrivateToPackage; if (ev.kind == ExactValue_Invalid) { @@ -3421,6 +3433,18 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { error(elem, "Expected a string value for '%.*s'", LIT(name)); } return true; + } else if (name == "link_suffix") { + ExactValue ev = check_decl_attribute_value(c, value); + + if (ev.kind == ExactValue_String) { + ac->link_suffix = ev.value_string; + if (!is_foreign_name_valid(ac->link_suffix)) { + error(elem, "Invalid link suffix: %.*s", LIT(ac->link_suffix)); + } + } else { + error(elem, "Expected a string value for '%.*s'", LIT(name)); + } + return true; } else if (name == "deprecated") { ExactValue ev = check_decl_attribute_value(c, value); @@ -3702,6 +3726,17 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) { error(elem, "Expected a string value for '%.*s'", LIT(name)); } return true; + } else if (name == "link_suffix") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_String) { + ac->link_suffix = ev.value_string; + if (!is_foreign_name_valid(ac->link_suffix)) { + error(elem, "Invalid link suffix: %.*s", LIT(ac->link_suffix)); + } + } else { + error(elem, "Expected a string value for '%.*s'", LIT(name)); + } + return true; } else if (name == "link_section") { ExactValue ev = check_decl_attribute_value(c, value); if (ev.kind == ExactValue_String) { @@ -3733,6 +3768,7 @@ gb_internal DECL_ATTRIBUTE_PROC(const_decl_attribute) { name == "linkage" || name == "link_name" || name == "link_prefix" || + name == "link_suffix" || false) { error(elem, "@(%.*s) is not supported for compile time constant value declarations", LIT(name)); return true; @@ -3775,8 +3811,10 @@ gb_internal void check_decl_attributes(CheckerContext *c, Array const &at if (attributes.count == 0) return; String original_link_prefix = {}; + String original_link_suffix = {}; if (ac) { original_link_prefix = ac->link_prefix; + original_link_suffix = ac->link_suffix; } StringSet set = {}; @@ -3851,6 +3889,12 @@ gb_internal void check_decl_attributes(CheckerContext *c, Array const &at ac->link_prefix.len = 0; } } + if (ac->link_suffix.text == original_link_suffix.text) { + if (ac->link_name.len > 0) { + ac->link_suffix.text = nullptr; + ac->link_suffix.len = 0; + } + } } } @@ -4145,6 +4189,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { e->Variable.foreign_library_ident = fl; e->Variable.link_prefix = c->foreign_context.link_prefix; + e->Variable.link_suffix = c->foreign_context.link_suffix; } Ast *init_expr = value; @@ -4219,6 +4264,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { } } e->Procedure.link_prefix = c->foreign_context.link_prefix; + e->Procedure.link_suffix = c->foreign_context.link_suffix; GB_ASSERT(cc != ProcCC_Invalid); pl->type->ProcType.calling_convention = cc; diff --git a/src/checker.hpp b/src/checker.hpp index 6ae7b90e2..539b72b2d 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -112,6 +112,7 @@ enum InstrumentationFlag : i32 { struct AttributeContext { String link_name; String link_prefix; + String link_suffix; String link_section; String linkage; isize init_expr_list_count; @@ -146,9 +147,10 @@ struct AttributeContext { String enable_target_feature; // will be enabled for the procedure only }; -gb_internal gb_inline AttributeContext make_attribute_context(String link_prefix) { +gb_internal gb_inline AttributeContext make_attribute_context(String link_prefix, String link_suffix) { AttributeContext ac = {}; ac.link_prefix = link_prefix; + ac.link_suffix = link_suffix; return ac; } @@ -302,6 +304,7 @@ struct ForeignContext { Ast * curr_library; ProcCallingConvention default_cc; String link_prefix; + String link_suffix; EntityVisiblityKind visibility_kind; }; diff --git a/src/entity.cpp b/src/entity.cpp index 1461b96d7..e4fc66dac 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -223,6 +223,7 @@ struct Entity { Ast * foreign_library_ident; String link_name; String link_prefix; + String link_suffix; String link_section; CommentGroup *docs; CommentGroup *comment; @@ -243,6 +244,7 @@ struct Entity { Ast * foreign_library_ident; String link_name; String link_prefix; + String link_suffix; DeferredProcedure deferred_procedure; struct GenProcsData *gen_procs; -- cgit v1.2.3 From 8db87170a9f491145cb10a69e52c6c2674efdf71 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 30 May 2024 21:53:23 +0100 Subject: Clean up `handle_link_name` handling of `link_suffix` --- src/check_decl.cpp | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 44b06d712..f2afce59c 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -725,23 +725,42 @@ gb_internal Entity *init_entity_foreign_library(CheckerContext *ctx, Entity *e) } gb_internal String handle_link_name(CheckerContext *ctx, Token token, String link_name, String link_prefix, String link_suffix) { + String original_link_name = link_name; if (link_prefix.len > 0) { - if (link_name.len > 0) { + if (original_link_name.len > 0) { error(token, "'link_name' and 'link_prefix' cannot be used together"); } else { - isize len = link_prefix.len + token.string.len + link_suffix.len; + isize len = link_prefix.len + token.string.len; u8 *name = gb_alloc_array(permanent_allocator(), u8, len+1); gb_memmove(name, &link_prefix[0], link_prefix.len); gb_memmove(name+link_prefix.len, &token.string[0], token.string.len); - gb_memmove(name+link_prefix.len+token.string.len, link_suffix.text, link_suffix.len); name[len] = 0; link_name = make_string(name, len); } } + + if (link_suffix.len > 0) { + if (original_link_name.len > 0) { + error(token, "'link_name' and 'link_suffix' cannot be used together"); + } else { + String new_name = token.string; + if (link_name != original_link_name) { + new_name = link_name; + } + + isize len = new_name.len + link_suffix.len; + u8 *name = gb_alloc_array(permanent_allocator(), u8, len+1); + gb_memmove(name, &new_name[0], new_name.len); + gb_memmove(name+new_name.len, &link_suffix[0], link_suffix.len); + name[len] = 0; + link_name = make_string(name, len); + } + } return link_name; } + gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeContext const &ac) { if (!(ac.objc_name.len || ac.objc_is_class_method || ac.objc_type)) { return; -- cgit v1.2.3 From 9ef43fc782159893b7af139f9d9be3aec3108ecd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 6 Jun 2024 15:16:34 +0100 Subject: Add `@(rodata)` --- src/check_decl.cpp | 6 ++++++ src/check_stmt.cpp | 6 ++++++ src/checker.cpp | 6 ++++++ src/checker.hpp | 1 + src/entity.cpp | 1 + src/llvm_backend.cpp | 20 ++++++++++++++++++-- 6 files changed, 38 insertions(+), 2 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index f2afce59c..43947836b 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1264,6 +1264,9 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast if (ac.is_static) { error(e->token, "@(static) is not supported for global variables, nor required"); } + if (ac.rodata) { + e->Variable.is_rodata = true; + } ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix, ac.link_suffix); if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) { @@ -1350,6 +1353,9 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast Operand o = {}; check_expr_with_type_hint(ctx, &o, init_expr, e->type); check_init_variable(ctx, e, &o, str_lit("variable declaration")); + if (e->Variable.is_rodata && o.mode != Addressing_Constant) { + error(o.expr, "Variables declared with @(rodata) must have constant initialization"); + } check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed"); } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 2c37bced0..fc443a7b5 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -501,6 +501,9 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O return nullptr; case Addressing_Variable: + if (e && e->kind == Entity_Variable && e->Variable.is_rodata) { + error(lhs->expr, "Assignment to variable '%.*s' marked as @(rodata) is not allowed", LIT(e->token.string)); + } break; case Addressing_MapIndex: { @@ -2055,6 +2058,9 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f } } } + if (ac.rodata) { + error(e->token, "Only global variables can have @(rodata) applied"); + } if (ac.thread_local_model != "") { String name = e->token.string; if (name == "_") { diff --git a/src/checker.cpp b/src/checker.cpp index 97e685d33..8a58bb425 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3628,6 +3628,12 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) { } ac->is_static = true; return true; + } else if (name == "rodata") { + if (value != nullptr) { + error(elem, "'rodata' does not have any parameters"); + } + ac->rodata = true; + return true; } else if (name == "thread_local") { ExactValue ev = check_decl_attribute_value(c, value); if (ac->init_expr_list_count > 0) { diff --git a/src/checker.hpp b/src/checker.hpp index e793540e3..2ac4c8e7a 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -133,6 +133,7 @@ struct AttributeContext { bool entry_point_only : 1; bool instrumentation_enter : 1; bool instrumentation_exit : 1; + bool rodata : 1; u32 optimization_mode; // ProcedureOptimizationMode i64 foreign_import_priority_index; String extra_linker_flags; diff --git a/src/entity.cpp b/src/entity.cpp index e4fc66dac..7f484e308 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -230,6 +230,7 @@ struct Entity { bool is_foreign; bool is_export; bool is_global; + bool is_rodata; } Variable; struct { Type * type_parameter_specialization; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 03c17a8bb..5dc6d94d5 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1160,6 +1160,10 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc if (is_type_untyped_nil(init.type)) { LLVMSetInitializer(var.var.value, LLVMConstNull(global_type)); var.is_initialized = true; + + if (e->Variable.is_rodata) { + LLVMSetGlobalConstant(var.var.value, true); + } continue; } GB_PANIC("Invalid init value, got %s", expr_to_string(init_expr)); @@ -1174,6 +1178,10 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc } LLVMSetInitializer(var.var.value, init.value); var.is_initialized = true; + + if (e->Variable.is_rodata) { + LLVMSetGlobalConstant(var.var.value, true); + } continue; } } else { @@ -1206,8 +1214,9 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc var.is_initialized = true; } + + } - CheckerInfo *info = main_module->gen->info; for (Entity *e : info->init_procedures) { @@ -3210,14 +3219,21 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { lbValue init = lb_const_value(m, tav.type, v); LLVMSetInitializer(g.value, init.value); var.is_initialized = true; + if (e->kind == Entity_Variable && e->Variable.is_rodata) { + LLVMSetGlobalConstant(g.value, true); + } } } } if (!var.is_initialized && is_type_untyped_nil(tav.type)) { var.is_initialized = true; + if (e->kind == Entity_Variable && e->Variable.is_rodata) { + LLVMSetGlobalConstant(g.value, true); + } } + } else if (e->kind == Entity_Variable && e->Variable.is_rodata) { + LLVMSetGlobalConstant(g.value, true); } - array_add(&global_variables, var); lb_add_entity(m, e, g); -- cgit v1.2.3 From fa3cae2bb04db76f52f1b2288a9c858f20332b8a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 10 Jun 2024 15:02:34 +0100 Subject: Add `intrinsics.procedure_of` ```odin foo :: proc(x: $T) { fmt.println(x) } bar :: intrinsics.procedure_of(foo(int(123))) // parameters are never ran at compile time, similar to `size_of` bar(333) // prints 333 ``` --- base/intrinsics/intrinsics.odin | 4 ++++ src/check_builtin.cpp | 46 +++++++++++++++++++++++++++++++++++++++++ src/check_decl.cpp | 17 ++++++++++----- src/check_expr.cpp | 1 + src/check_stmt.cpp | 10 ++++++++- src/checker.cpp | 4 ++++ src/checker.hpp | 6 ++++++ src/checker_builtin_procs.hpp | 4 ++++ src/parser.hpp | 1 + 9 files changed, 87 insertions(+), 6 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index 8873f3bbc..4f6fa2713 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -295,6 +295,10 @@ simd_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T --- // if all listed features are supported. has_target_feature :: proc($test: $T) -> bool where type_is_string(T) || type_is_proc(T) --- + +// Returns the value of the procedure where `x` must be a call expression +procedure_of :: proc(x: $T) -> T where type_is_proc(T) --- + // WASM targets only wasm_memory_grow :: proc(index, delta: uintptr) -> int --- wasm_memory_size :: proc(index: uintptr) -> int --- diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 98c695a2c..3aee804df 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1843,6 +1843,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As case BuiltinProc_objc_register_class: case BuiltinProc_atomic_type_is_lock_free: case BuiltinProc_has_target_feature: + case BuiltinProc_procedure_of: // NOTE(bill): The first arg may be a Type, this will be checked case by case break; @@ -6157,6 +6158,51 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As break; } + case BuiltinProc_procedure_of: + { + Ast *call_expr = unparen_expr(ce->args[0]); + Operand op = {}; + check_expr_base(c, &op, ce->args[0], nullptr); + if (op.mode != Addressing_Value && !(call_expr && call_expr->kind == Ast_CallExpr)) { + error(ce->args[0], "Expected a call expression for '%.*s'", LIT(builtin_name)); + return false; + } + + Ast *proc = call_expr->CallExpr.proc; + Entity *e = entity_of_node(proc); + + if (e == nullptr) { + error(ce->args[0], "Invalid procedure value, expected a regular/specialized procedure"); + return false; + } + + TypeAndValue tav = proc->tav; + + + operand->type = e->type; + operand->mode = Addressing_Value; + operand->value = tav.value; + operand->builtin_id = BuiltinProc_Invalid; + operand->proc_group = nullptr; + + if (tav.mode == Addressing_Builtin) { + operand->mode = tav.mode; + operand->builtin_id = cast(BuiltinProcId)e->Builtin.id; + break; + } + + if (!is_type_proc(e->type)) { + gbString s = type_to_string(e->type); + error(ce->args[0], "Expected a procedure value, got '%s'", s); + gb_string_free(s); + return false; + } + + + ce->entity_procedure_of = e; + break; + } + case BuiltinProc_constant_utf16_cstring: { String value = {}; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 43947836b..13b14149a 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -88,11 +88,14 @@ gb_internal Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *o e->type = t_invalid; return nullptr; } else if (is_type_polymorphic(t)) { - gbString str = type_to_string(t); - defer (gb_string_free(str)); - error(e->token, "Invalid use of a polymorphic type '%s' in %.*s", str, LIT(context_name)); - e->type = t_invalid; - return nullptr; + Entity *e = entity_of_node(operand->expr); + if (e->state.load() != EntityState_Resolved) { + gbString str = type_to_string(t); + defer (gb_string_free(str)); + error(e->token, "Invalid use of a polymorphic type '%s' in %.*s", str, LIT(context_name)); + e->type = t_invalid; + return nullptr; + } } else if (is_type_empty_union(t)) { gbString str = type_to_string(t); defer (gb_string_free(str)); @@ -479,6 +482,9 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr entity = check_selector(ctx, &operand, init, e->type); } else { check_expr_or_type(ctx, &operand, init, e->type); + if (init->kind == Ast_CallExpr) { + entity = init->CallExpr.entity_procedure_of; + } } switch (operand.mode) { @@ -526,6 +532,7 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr return; } + if (entity != nullptr) { if (e->type != nullptr) { Operand x = {}; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 641f70566..01cba881e 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -578,6 +578,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E d->defer_use_checked = false; Entity *entity = alloc_entity_procedure(nullptr, token, final_proc_type, tags); + entity->state.store(EntityState_Resolved); entity->identifier = ident; add_entity_and_decl_info(&nctx, ident, entity, d); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index c37c58cd6..f2e3b0242 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2224,8 +2224,16 @@ gb_internal void check_expr_stmt(CheckerContext *ctx, Ast *node) { } if (do_require) { gbString expr_str = expr_to_string(ce->proc); + defer (gb_string_free(expr_str)); + if (builtin_id) { + String real_name = builtin_procs[builtin_id].name; + if (real_name != make_string(cast(u8 const *)expr_str, gb_string_length(expr_str))) { + error(node, "'%s' ('%.*s.%.*s') requires that its results must be handled", expr_str, + LIT(builtin_proc_pkg_name[builtin_procs[builtin_id].pkg]), LIT(real_name)); + return; + } + } error(node, "'%s' requires that its results must be handled", expr_str); - gb_string_free(expr_str); } return; } else if (expr && expr->kind == Ast_SelectorCallExpr) { diff --git a/src/checker.cpp b/src/checker.cpp index 8f0cc1cd1..852fb89bb 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1479,6 +1479,10 @@ gb_internal Entity *entity_of_node(Ast *expr) { case_ast_node(cc, CaseClause, expr); return cc->implicit_entity; case_end; + + case_ast_node(ce, CallExpr, expr); + return ce->entity_procedure_of; + case_end; } return nullptr; } diff --git a/src/checker.hpp b/src/checker.hpp index 2ac4c8e7a..492a64fb6 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -51,6 +51,12 @@ enum StmtFlag { enum BuiltinProcPkg { BuiltinProcPkg_builtin, BuiltinProcPkg_intrinsics, + BuiltinProcPkg_COUNT +}; + +String builtin_proc_pkg_name[BuiltinProcPkg_COUNT] = { + str_lit("builtin"), + str_lit("intrinsics"), }; struct BuiltinProc { diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 5f98bb7b3..35acad42f 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -299,6 +299,8 @@ BuiltinProc__type_simple_boolean_end, BuiltinProc__type_end, + BuiltinProc_procedure_of, + BuiltinProc___entry_point, BuiltinProc_objc_send, @@ -614,6 +616,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("procedure_of"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("__entry_point"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("objc_send"), 3, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, diff --git a/src/parser.hpp b/src/parser.hpp index 0e411d9ac..02f2af28d 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -458,6 +458,7 @@ AST_KIND(_ExprBegin, "", bool) \ bool optional_ok_one; \ bool was_selector; \ AstSplitArgs *split_args; \ + Entity *entity_procedure_of; \ }) \ AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \ AST_KIND(EnumFieldValue, "enum field value", struct { \ -- cgit v1.2.3 From 61c630bbf8367ab473897b066175990d0996bd14 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 11 Jun 2024 05:23:43 -0400 Subject: Fix #3730 --- src/check_decl.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 13b14149a..8f4d9f922 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -89,6 +89,9 @@ gb_internal Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *o return nullptr; } else if (is_type_polymorphic(t)) { Entity *e = entity_of_node(operand->expr); + if (e == nullptr) { + return nullptr; + } if (e->state.load() != EntityState_Resolved) { gbString str = type_to_string(t); defer (gb_string_free(str)); -- cgit v1.2.3 From 0b02c67cdf316d55232f0433d51f6ea781d74a22 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 Jun 2024 12:19:52 +0100 Subject: Minor clean up for backend --- src/check_decl.cpp | 9 ++- src/entity.cpp | 1 + src/llvm_backend.cpp | 130 +++++++++++++++++++++++++------------------ src/llvm_backend.hpp | 3 +- src/llvm_backend_general.cpp | 3 +- 5 files changed, 89 insertions(+), 57 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 13b14149a..a5c9119ea 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1142,7 +1142,14 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } if (ac.link_name.len > 0) { - e->Procedure.link_name = ac.link_name; + String ln = ac.link_name; + e->Procedure.link_name = ln; + if (ln == "memcpy" || + ln == "memmove" || + ln == "mem_copy" || + ln == "mem_copy_non_overlapping") { + e->Procedure.is_memcpy_like = true; + } } if (ac.deferred_procedure.entity != nullptr) { diff --git a/src/entity.cpp b/src/entity.cpp index 7f484e308..8f55c1faf 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -256,6 +256,7 @@ struct Entity { bool generated_from_polymorphic : 1; bool entry_point_only : 1; bool has_instrumentation : 1; + bool is_memcpy_like : 1; } Procedure; struct { Array entities; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 01680ffa9..81de2c224 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1300,18 +1300,14 @@ gb_internal lbProcedure *lb_create_cleanup_runtime(lbModule *main_module) { // C gb_internal WORKER_TASK_PROC(lb_generate_procedures_and_types_per_module) { lbModule *m = cast(lbModule *)data; - for (Entity *e : m->global_procedures_and_types_to_create) { - if (e->kind == Entity_TypeName) { - (void)lb_get_entity_name(m, e); - lb_type(m, e->type); - } + for (Entity *e : m->global_types_to_create) { + (void)lb_get_entity_name(m, e); + (void)lb_type(m, e->type); } - for (Entity *e : m->global_procedures_and_types_to_create) { - if (e->kind == Entity_Procedure) { - (void)lb_get_entity_name(m, e); - array_add(&m->procedures_to_generate, lb_create_procedure(m, e)); - } + for (Entity *e : m->global_procedures_to_create) { + (void)lb_get_entity_name(m, e); + array_add(&m->procedures_to_generate, lb_create_procedure(m, e)); } return 0; } @@ -1365,16 +1361,24 @@ gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, Checker m = lb_module_of_entity(gen, e); } - array_add(&m->global_procedures_and_types_to_create, e); + if (e->kind == Entity_Procedure) { + array_add(&m->global_procedures_to_create, e); + } else if (e->kind == Entity_TypeName) { + array_add(&m->global_types_to_create, e); + } } - for (auto const &entry : gen->modules) { - lbModule *m = entry.value; - if (do_threading) { + if (do_threading) { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; thread_pool_add_task(lb_generate_procedures_and_types_per_module, m); - } else { + } + } else { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; lb_generate_procedures_and_types_per_module(m); } + } thread_pool_wait(); @@ -2405,16 +2409,19 @@ gb_internal WORKER_TASK_PROC(lb_generate_procedures_worker_proc) { } gb_internal void lb_generate_procedures(lbGenerator *gen, bool do_threading) { - for (auto const &entry : gen->modules) { - lbModule *m = entry.value; - if (do_threading) { + if (do_threading) { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; thread_pool_add_task(lb_generate_procedures_worker_proc, m); - } else { + } + + thread_pool_wait(); + } else { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; lb_generate_procedures_worker_proc(m); } } - - thread_pool_wait(); } gb_internal WORKER_TASK_PROC(lb_generate_missing_procedures_to_check_worker_proc) { @@ -2428,17 +2435,20 @@ gb_internal WORKER_TASK_PROC(lb_generate_missing_procedures_to_check_worker_proc } gb_internal void lb_generate_missing_procedures(lbGenerator *gen, bool do_threading) { - for (auto const &entry : gen->modules) { - lbModule *m = entry.value; - // NOTE(bill): procedures may be added during generation - if (do_threading) { + if (do_threading) { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; + // NOTE(bill): procedures may be added during generation thread_pool_add_task(lb_generate_missing_procedures_to_check_worker_proc, m); - } else { + } + thread_pool_wait(); + } else { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; + // NOTE(bill): procedures may be added during generation lb_generate_missing_procedures_to_check_worker_proc(m); } } - - thread_pool_wait(); } gb_internal void lb_debug_info_complete_types_and_finalize(lbGenerator *gen) { @@ -2451,32 +2461,45 @@ gb_internal void lb_debug_info_complete_types_and_finalize(lbGenerator *gen) { } gb_internal void lb_llvm_function_passes(lbGenerator *gen, bool do_threading) { - for (auto const &entry : gen->modules) { - lbModule *m = entry.value; - if (do_threading) { + if (do_threading) { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; thread_pool_add_task(lb_llvm_function_pass_per_module, m); - } else { + } + thread_pool_wait(); + } else { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; lb_llvm_function_pass_per_module(m); } } - thread_pool_wait(); } gb_internal void lb_llvm_module_passes(lbGenerator *gen, bool do_threading) { - for (auto const &entry : gen->modules) { - lbModule *m = entry.value; - auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData); - wd->m = m; - wd->target_machine = m->target_machine; + if (do_threading) { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; + auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData); + wd->m = m; + wd->target_machine = m->target_machine; - if (do_threading) { - thread_pool_add_task(lb_llvm_module_pass_worker_proc, wd); - } else { + if (do_threading) { + thread_pool_add_task(lb_llvm_module_pass_worker_proc, wd); + } else { + lb_llvm_module_pass_worker_proc(wd); + } + } + thread_pool_wait(); + } else { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; + auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData); + wd->m = m; + wd->target_machine = m->target_machine; lb_llvm_module_pass_worker_proc(wd); } } - thread_pool_wait(); } gb_internal String lb_filepath_ll_for_module(lbModule *m) { @@ -2556,17 +2579,21 @@ gb_internal String lb_filepath_obj_for_module(lbModule *m) { gb_internal bool lb_llvm_module_verification(lbGenerator *gen, bool do_threading) { - for (auto const &entry : gen->modules) { - lbModule *m = entry.value; - if (do_threading) { + if (do_threading) { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; thread_pool_add_task(lb_llvm_module_verification_worker_proc, m); - } else { + } + thread_pool_wait(); + + } else { + for (auto const &entry : gen->modules) { + lbModule *m = entry.value; if (lb_llvm_module_verification_worker_proc(m)) { return false; } } } - thread_pool_wait(); return true; } @@ -2808,13 +2835,8 @@ gb_internal void lb_generate_procedure(lbModule *m, lbProcedure *p) { lb_end_procedure(p); // Add Flags - if (p->body != nullptr) { - if (p->name == "memcpy" || p->name == "memmove" || - p->name == "runtime.mem_copy" || p->name == "mem_copy_non_overlapping" || - string_starts_with(p->name, str_lit("llvm.memcpy")) || - string_starts_with(p->name, str_lit("llvm.memmove"))) { - p->flags |= lbProcedureFlag_WithoutMemcpyPass; - } + if (p->entity && p->entity->kind == Entity_Procedure && p->entity->Procedure.is_memcpy_like) { + p->flags |= lbProcedureFlag_WithoutMemcpyPass; } lb_verify_function(m, p, true); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 9f7bc8843..447e93d42 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -181,7 +181,8 @@ struct lbModule { std::atomic nested_type_name_guid; Array procedures_to_generate; - Array global_procedures_and_types_to_create; + Array global_procedures_to_create; + Array global_types_to_create; lbProcedure *curr_procedure; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index ea98fc60a..03d0f8b32 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -78,7 +78,8 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) { array_init(&m->procedures_to_generate, a, 0, c->info.all_procedures.count); map_init(&m->procedure_values, c->info.all_procedures.count*2); } - array_init(&m->global_procedures_and_types_to_create, a, 0, 1024); + array_init(&m->global_procedures_to_create, a, 0, 1024); + array_init(&m->global_types_to_create, a, 0, 1024); array_init(&m->missing_procedures_to_check, a, 0, 16); map_init(&m->debug_values); -- cgit v1.2.3 From 0196cc46e35eb0074ffd5f8dd24cdf8c42fde9e0 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 18 Jun 2024 01:47:51 -0400 Subject: Allow custom `main` when `-no-entry-point` is set --- src/check_decl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 02445cbc6..818556951 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1077,7 +1077,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } - if (e->pkg != nullptr && e->token.string == "main") { + if (e->pkg != nullptr && e->token.string == "main" && !build_context.no_entry_point) { if (e->pkg->kind != Package_Runtime) { if (pt->param_count != 0 || pt->result_count != 0) { -- cgit v1.2.3 From 140ee036cef1857feca1444178a3700108e61698 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 27 Jun 2024 23:29:36 +0200 Subject: wasm error is no longer relevant --- src/check_decl.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 818556951..b5307838c 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1320,9 +1320,9 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast error(e->token, "A foreign variable declaration cannot have a default value"); } init_entity_foreign_library(ctx, e); - if (is_arch_wasm()) { - error(e->token, "A foreign variable declaration are not allowed for the '%.*s' architecture", LIT(target_arch_names[build_context.metrics.arch])); - } + // if (is_arch_wasm()) { + // error(e->token, "A foreign variable declaration are not allowed for the '%.*s' architecture", LIT(target_arch_names[build_context.metrics.arch])); + // } } if (ac.link_name.len > 0) { e->Variable.link_name = ac.link_name; -- cgit v1.2.3 From b79d7e69177ccbc25b75f1aa7a90a62f64c6cee1 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Fri, 28 Jun 2024 01:16:26 +0200 Subject: disallow non-global foreign import of variables on wasm --- src/build_settings.cpp | 4 +++- src/check_decl.cpp | 8 ++++---- src/llvm_backend.cpp | 2 -- src/llvm_backend_proc.cpp | 2 +- src/llvm_backend_utility.cpp | 2 +- src/tilde.cpp | 1 - 6 files changed, 9 insertions(+), 10 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 0495cf3b5..1988e9cac 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -2053,10 +2053,12 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta gbString link_flags = gb_string_make(heap_allocator(), " "); // link_flags = gb_string_appendc(link_flags, "--export-all "); // link_flags = gb_string_appendc(link_flags, "--export-table "); - link_flags = gb_string_appendc(link_flags, "--allow-undefined "); // if (bc->metrics.arch == TargetArch_wasm64) { // link_flags = gb_string_appendc(link_flags, "-mwasm64 "); // } + if (bc->metrics.os != TargetOs_orca) { + link_flags = gb_string_appendc(link_flags, "--allow-undefined "); + } if (bc->no_entry_point || bc->metrics.os == TargetOs_orca) { link_flags = gb_string_appendc(link_flags, "--no-entry "); } diff --git a/src/check_decl.cpp b/src/check_decl.cpp index b5307838c..883cfcba9 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -709,7 +709,7 @@ gb_internal Entity *init_entity_foreign_library(CheckerContext *ctx, Entity *e) } if (ident == nullptr) { - error(e->token, "foreign entiies must declare which library they are from"); + error(e->token, "foreign entities must declare which library they are from"); } else if (ident->kind != Ast_Ident) { error(ident, "foreign library names must be an identifier"); } else { @@ -1320,9 +1320,9 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast error(e->token, "A foreign variable declaration cannot have a default value"); } init_entity_foreign_library(ctx, e); - // if (is_arch_wasm()) { - // error(e->token, "A foreign variable declaration are not allowed for the '%.*s' architecture", LIT(target_arch_names[build_context.metrics.arch])); - // } + if (is_arch_wasm() && e->Variable.foreign_library != nullptr) { + error(e->token, "A foreign variable declaration can not be scoped to a module and must be declared in a 'foreign {' (without a library) block"); + } } if (ac.link_name.len > 0) { e->Variable.link_name = ac.link_name; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index d119633ec..92c8138c5 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -3217,8 +3217,6 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { LLVMSetDLLStorageClass(g.value, LLVMDLLImportStorageClass); LLVMSetExternallyInitialized(g.value, true); lb_add_foreign_library_path(m, e->Variable.foreign_library); - - // lb_set_wasm_import_attributes(g.value, e, name); } else { LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, e->type))); } diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 958b4fd38..68ba4f74e 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -233,7 +233,7 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i if (p->is_foreign) { - lb_set_wasm_import_attributes(p->value, entity, p->name); + lb_set_wasm_procedure_import_attributes(p->value, entity, p->name); } diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 94153e233..98ed0c57e 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -2018,7 +2018,7 @@ gb_internal LLVMValueRef llvm_get_inline_asm(LLVMTypeRef func_type, String const } -gb_internal void lb_set_wasm_import_attributes(LLVMValueRef value, Entity *entity, String import_name) { +gb_internal void lb_set_wasm_procedure_import_attributes(LLVMValueRef value, Entity *entity, String import_name) { if (!is_arch_wasm()) { return; } diff --git a/src/tilde.cpp b/src/tilde.cpp index 4fc7d1c9b..f6fed0f9a 100644 --- a/src/tilde.cpp +++ b/src/tilde.cpp @@ -363,7 +363,6 @@ gb_internal bool cg_global_variables_create(cgModule *m, Array if (is_foreign) { linkage = TB_LINKAGE_PUBLIC; // lb_add_foreign_library_path(m, e->Variable.foreign_library); - // lb_set_wasm_import_attributes(g.value, e, name); } else if (is_export) { linkage = TB_LINKAGE_PUBLIC; } -- cgit v1.2.3 From 6f1cc8071c3ff49c5431cc8ad078d12883f91545 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Tue, 2 Jul 2024 15:28:08 +0200 Subject: wasm: add foreign import and linking of wasm object files --- src/build_settings.cpp | 9 --------- src/check_decl.cpp | 7 +++++-- src/checker.cpp | 3 +-- src/linker.cpp | 14 ++++++++++++++ src/llvm_backend_utility.cpp | 6 +++++- src/parser.cpp | 3 +-- 6 files changed, 26 insertions(+), 16 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/src/build_settings.cpp b/src/build_settings.cpp index c3b4f2506..9c93a5b69 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -860,15 +860,6 @@ gb_internal bool is_arch_x86(void) { return false; } -gb_internal bool allow_check_foreign_filepath(void) { - switch (build_context.metrics.arch) { - case TargetArch_wasm32: - case TargetArch_wasm64p32: - return false; - } - return true; -} - // TODO(bill): OS dependent versions for the BuildContext // join_path // is_dir diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 883cfcba9..3c4a4b3de 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1178,9 +1178,12 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { if (foreign_library->LibraryName.paths.count >= 1) { module_name = foreign_library->LibraryName.paths[0]; } - name = concatenate3_strings(permanent_allocator(), module_name, WASM_MODULE_NAME_SEPARATOR, name); + + if (!string_ends_with(module_name, str_lit(".o"))) { + name = concatenate3_strings(permanent_allocator(), module_name, WASM_MODULE_NAME_SEPARATOR, name); + } } - + e->Procedure.is_foreign = true; e->Procedure.link_name = name; diff --git a/src/checker.cpp b/src/checker.cpp index 734659510..c3d2ae5eb 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -5000,9 +5000,8 @@ gb_internal void check_foreign_import_fullpaths(Checker *c) { String file_str = op.value.value_string; file_str = string_trim_whitespace(file_str); - String fullpath = file_str; - if (allow_check_foreign_filepath()) { + if (!is_arch_wasm() || string_ends_with(file_str, str_lit(".o"))) { String foreign_path = {}; bool ok = determine_path_from_string(nullptr, decl, base_dir, file_str, &foreign_path, /*use error not syntax_error*/true); if (ok) { diff --git a/src/linker.cpp b/src/linker.cpp index 371736743..34c0af7e5 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -85,6 +85,20 @@ gb_internal i32 linker_stage(LinkerData *gen) { if (extra_linker_flags.len != 0) { lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(extra_linker_flags)); } + + for_array(i, e->LibraryName.paths) { + String lib = e->LibraryName.paths[i]; + + if (lib.len == 0) { + continue; + } + + if (!string_ends_with(lib, str_lit(".o"))) { + continue; + } + + inputs = gb_string_append_fmt(inputs, " \"%.*s\"", LIT(lib)); + } } if (build_context.metrics.os == TargetOs_orca) { diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 98ed0c57e..1165476be 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -2029,7 +2029,11 @@ gb_internal void lb_set_wasm_procedure_import_attributes(LLVMValueRef value, Ent GB_ASSERT(foreign_library->LibraryName.paths.count == 1); module_name = foreign_library->LibraryName.paths[0]; - + + if (string_ends_with(module_name, str_lit(".o"))) { + return; + } + if (string_starts_with(import_name, module_name)) { import_name = substring(import_name, module_name.len+WASM_MODULE_NAME_SEPARATOR.len, import_name.len); } diff --git a/src/parser.cpp b/src/parser.cpp index 583f4a57d..997d32fa5 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -5813,7 +5813,6 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node return false; } - if (collection_name.len > 0) { // NOTE(bill): `base:runtime` == `core:runtime` if (collection_name == "core") { @@ -5968,7 +5967,7 @@ gb_internal void parse_setup_file_decls(Parser *p, AstFile *f, String const &bas Token fp_token = fp->BasicLit.token; String file_str = string_trim_whitespace(string_value_from_token(f, fp_token)); String fullpath = file_str; - if (allow_check_foreign_filepath()) { + if (!is_arch_wasm() || string_ends_with(fullpath, str_lit(".o"))) { String foreign_path = {}; bool ok = determine_path_from_string(&p->file_decl_mutex, node, base_dir, file_str, &foreign_path); if (!ok) { -- cgit v1.2.3 From 657bc88535eb3b160d86fed5f5e5d0d6ea67c78c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 4 Jul 2024 13:48:52 +0100 Subject: Allow `x :: y when cond else proc(...){...}` --- src/check_decl.cpp | 234 +++++++++++++++++++++++++++++++++++------------------ src/check_expr.cpp | 22 ++++- src/checker.cpp | 12 +++ 3 files changed, 189 insertions(+), 79 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 3c4a4b3de..0f48c28e3 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -155,6 +155,154 @@ gb_internal void check_init_variables(CheckerContext *ctx, Entity **lhs, isize l } } + +gb_internal void override_entity_in_scope(Entity *original_entity, Entity *new_entity) { + // NOTE(bill): The original_entity's scope may not be same scope that it was inserted into + // e.g. file entity inserted into its package scope + String original_name = original_entity->token.string; + Scope *found_scope = nullptr; + Entity *found_entity = nullptr; + scope_lookup_parent(original_entity->scope, original_name, &found_scope, &found_entity); + if (found_scope == nullptr) { + return; + } + rw_mutex_lock(&found_scope->mutex); + defer (rw_mutex_unlock(&found_scope->mutex)); + + // IMPORTANT NOTE(bill, 2021-04-10): Overriding behaviour was flawed in that the + // original entity was still used check checked, but the checking was only + // relying on "constant" data such as the Entity.type and Entity.Constant.value + // + // Therefore two things can be done: the type can be assigned to state that it + // has been "evaluated" and the variant data can be copied across + + string_map_set(&found_scope->elements, original_name, new_entity); + + original_entity->flags |= EntityFlag_Overridden; + original_entity->type = new_entity->type; + original_entity->aliased_of = new_entity; + + Ast *empty_ident = nullptr; + original_entity->identifier.compare_exchange_strong(empty_ident, new_entity->identifier); + + if (original_entity->identifier.load() != nullptr && + original_entity->identifier.load()->kind == Ast_Ident) { + original_entity->identifier.load()->Ident.entity = new_entity; + } + + // IMPORTANT NOTE(bill, 2021-04-10): copy only the variants + // This is most likely NEVER required, but it does not at all hurt to keep + isize offset = cast(u8 *)&original_entity->Dummy.start - cast(u8 *)original_entity; + isize size = gb_size_of(*original_entity) - offset; + gb_memmove(cast(u8 *)original_entity, cast(u8 *)new_entity, size); +} + + +gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d); + +gb_internal bool check_try_override_const_decl(CheckerContext *ctx, Entity *e, Entity *entity, Ast *init, Type *named_type) { + if (entity == nullptr) { + retry_proc_lit:; + init = unparen_expr(init); + if (init == nullptr) { + return false; + } + if (init->kind == Ast_TernaryWhenExpr) { + ast_node(we, TernaryWhenExpr, init); + if (we->cond == nullptr) { + return false; + } + if (we->cond->tav.value.kind != ExactValue_Bool) { + return false; + } + init = we->cond->tav.value.value_bool ? we->x : we->y; + goto retry_proc_lit; + } if (init->kind == Ast_ProcLit) { + // NOTE(bill, 2024-07-04): Override as a procedure entity because this could be within a `when` statement + e->kind = Entity_Procedure; + e->type = nullptr; + DeclInfo *d = decl_info_of_entity(e); + d->proc_lit = init; + check_proc_decl(ctx, e, d); + return true; + } + + return false; + } + switch (entity->kind) { + case Entity_TypeName: + // @TypeAliasingProblem + // NOTE(bill, 2022-02-03): This is used to solve the problem caused by type aliases + // being "confused" as constants + // + // A :: B + // C :: proc "c" (^A) + // B :: struct {x: C} + // + // A gets evaluated first, and then checks B. + // B then checks C. + // C then tries to check A which is unresolved but thought to be a constant. + // Therefore within C's check, A errs as "not a type". + // + // This is because a const declaration may or may not be a type and this cannot + // be determined from a syntactical standpoint. + // This check allows the compiler to override the entity to be checked as a type. + // + // There is no problem if B is prefixed with the `#type` helper enforcing at + // both a syntax and semantic level that B must be a type. + // + // A :: #type B + // + // This approach is not fool proof and can fail in case such as: + // + // X :: type_of(x) + // X :: Foo(int).Type + // + // Since even these kind of declarations may cause weird checking cycles. + // For the time being, these are going to be treated as an unfortunate error + // until there is a proper delaying system to try declaration again if they + // have failed. + + e->kind = Entity_TypeName; + check_type_decl(ctx, e, init, named_type); + return true; + case Entity_Builtin: + if (e->type != nullptr) { + return false; + } + e->kind = Entity_Builtin; + e->Builtin.id = entity->Builtin.id; + e->type = t_invalid; + return true; + case Entity_ProcGroup: + // NOTE(bill, 2020-06-10): It is better to just clone the contents than overriding the entity in the scope + // Thank goodness I made entities a tagged union to allow for this implace patching + e->kind = Entity_ProcGroup; + e->ProcGroup.entities = array_clone(heap_allocator(), entity->ProcGroup.entities); + return true; + } + + if (e->type != nullptr && entity->type != nullptr) { + Operand x = {}; + x.type = entity->type; + x.mode = Addressing_Variable; + if (!check_is_assignable_to(ctx, &x, e->type)) { + return false; + } + } + + // NOTE(bill): Override aliased entity + switch (entity->kind) { + case Entity_ProcGroup: + case Entity_Procedure: + case Entity_LibraryName: + case Entity_ImportName: + override_entity_in_scope(e, entity); + return true; + } + return false; +} + gb_internal void check_init_constant(CheckerContext *ctx, Entity *e, Operand *operand) { if (operand->mode == Addressing_Invalid || operand->type == t_invalid || @@ -165,6 +313,13 @@ gb_internal void check_init_constant(CheckerContext *ctx, Entity *e, Operand *op return; } + if (operand->mode != Addressing_Constant) { + Entity *entity = entity_of_node(operand->expr); + if (check_try_override_const_decl(ctx, e, entity, operand->expr, nullptr)) { + return; + } + } + if (operand->mode != Addressing_Constant) { gbString str = expr_to_string(operand->expr); error(operand->expr, "'%s' is not a compile-time known constant", str); @@ -373,49 +528,6 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, } -gb_internal void override_entity_in_scope(Entity *original_entity, Entity *new_entity) { - // NOTE(bill): The original_entity's scope may not be same scope that it was inserted into - // e.g. file entity inserted into its package scope - String original_name = original_entity->token.string; - Scope *found_scope = nullptr; - Entity *found_entity = nullptr; - scope_lookup_parent(original_entity->scope, original_name, &found_scope, &found_entity); - if (found_scope == nullptr) { - return; - } - rw_mutex_lock(&found_scope->mutex); - defer (rw_mutex_unlock(&found_scope->mutex)); - - // IMPORTANT NOTE(bill, 2021-04-10): Overriding behaviour was flawed in that the - // original entity was still used check checked, but the checking was only - // relying on "constant" data such as the Entity.type and Entity.Constant.value - // - // Therefore two things can be done: the type can be assigned to state that it - // has been "evaluated" and the variant data can be copied across - - string_map_set(&found_scope->elements, original_name, new_entity); - - original_entity->flags |= EntityFlag_Overridden; - original_entity->type = new_entity->type; - original_entity->aliased_of = new_entity; - - Ast *empty_ident = nullptr; - original_entity->identifier.compare_exchange_strong(empty_ident, new_entity->identifier); - - if (original_entity->identifier.load() != nullptr && - original_entity->identifier.load()->kind == Ast_Ident) { - original_entity->identifier.load()->Ident.entity = new_entity; - } - - // IMPORTANT NOTE(bill, 2021-04-10): copy only the variants - // This is most likely NEVER required, but it does not at all hurt to keep - isize offset = cast(u8 *)&original_entity->Dummy.start - cast(u8 *)original_entity; - isize size = gb_size_of(*original_entity) - offset; - gb_memmove(cast(u8 *)original_entity, cast(u8 *)new_entity, size); -} - - - gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init, Type *named_type) { GB_ASSERT(e->type == nullptr); GB_ASSERT(e->kind == Entity_Constant); @@ -441,41 +553,7 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr if (init != nullptr) { Entity *entity = check_entity_from_ident_or_selector(ctx, init, false); - if (entity != nullptr && entity->kind == Entity_TypeName) { - // @TypeAliasingProblem - // NOTE(bill, 2022-02-03): This is used to solve the problem caused by type aliases - // being "confused" as constants - // - // A :: B - // C :: proc "c" (^A) - // B :: struct {x: C} - // - // A gets evaluated first, and then checks B. - // B then checks C. - // C then tries to check A which is unresolved but thought to be a constant. - // Therefore within C's check, A errs as "not a type". - // - // This is because a const declaration may or may not be a type and this cannot - // be determined from a syntactical standpoint. - // This check allows the compiler to override the entity to be checked as a type. - // - // There is no problem if B is prefixed with the `#type` helper enforcing at - // both a syntax and semantic level that B must be a type. - // - // A :: #type B - // - // This approach is not fool proof and can fail in case such as: - // - // X :: type_of(x) - // X :: Foo(int).Type - // - // Since even these kind of declarations may cause weird checking cycles. - // For the time being, these are going to be treated as an unfortunate error - // until there is a proper delaying system to try declaration again if they - // have failed. - - e->kind = Entity_TypeName; - check_type_decl(ctx, e, init, named_type); + if (check_try_override_const_decl(ctx, e, entity, init, named_type)) { return; } entity = nullptr; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 2d0b9dfa9..af5a9c521 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4968,7 +4968,27 @@ gb_internal bool is_entity_declared_for_selector(Entity *entity, Scope *import_s // NOTE(bill, 2022-02-03): see `check_const_decl` for why it exists reasoning gb_internal Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast *node, bool ident_only) { - if (node->kind == Ast_Ident) { + if (node == nullptr) { + return nullptr; + } + if (node->kind == Ast_TernaryWhenExpr) { + ast_node(we, TernaryWhenExpr, node); + if (we->cond == nullptr) { + return nullptr; + } + if (we->cond->tav.mode != Addressing_Constant) { + return nullptr; + } + if (we->cond->tav.value.kind != ExactValue_Bool) { + return nullptr; + } + if (we->cond->tav.value.value_bool) { + return check_entity_from_ident_or_selector(c, we->x, ident_only); + } else { + Entity *e = check_entity_from_ident_or_selector(c, we->y, ident_only); + return e; + } + } else if (node->kind == Ast_Ident) { String name = node->Ident.token.string; return scope_lookup(c->scope, name); } else if (!ident_only) if (node->kind == Ast_SelectorExpr) { diff --git a/src/checker.cpp b/src/checker.cpp index deee93dcc..503494c76 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1470,6 +1470,7 @@ gb_internal Entity *implicit_entity_of_node(Ast *clause) { } gb_internal Entity *entity_of_node(Ast *expr) { +retry:; expr = unparen_expr(expr); switch (expr->kind) { case_ast_node(ident, Ident, expr); @@ -1490,6 +1491,17 @@ gb_internal Entity *entity_of_node(Ast *expr) { case_ast_node(ce, CallExpr, expr); return ce->entity_procedure_of; case_end; + + case_ast_node(we, TernaryWhenExpr, expr); + if (we->cond == nullptr) { + break; + } + if (we->cond->tav.value.kind != ExactValue_Bool) { + break; + } + expr = we->cond->tav.value.value_bool ? we->x : we->y; + goto retry; + case_end; } return nullptr; } -- cgit v1.2.3 From 87bccc8f3f1130814791ce824c60e2e8e3c08192 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 4 Jul 2024 13:53:52 +0100 Subject: Remove import name cases --- src/check_decl.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 0f48c28e3..2116b5261 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -295,8 +295,6 @@ gb_internal bool check_try_override_const_decl(CheckerContext *ctx, Entity *e, E switch (entity->kind) { case Entity_ProcGroup: case Entity_Procedure: - case Entity_LibraryName: - case Entity_ImportName: override_entity_in_scope(e, entity); return true; } -- cgit v1.2.3 From 8b7c6a23ba7aec812e334b8bbb447708587f4cce Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 4 Jul 2024 14:06:02 +0100 Subject: Restrict a bit more --- src/check_decl.cpp | 85 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 44 insertions(+), 41 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 2116b5261..84c3d9ecc 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -197,40 +197,8 @@ gb_internal void override_entity_in_scope(Entity *original_entity, Entity *new_e gb_memmove(cast(u8 *)original_entity, cast(u8 *)new_entity, size); } - -gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d); - -gb_internal bool check_try_override_const_decl(CheckerContext *ctx, Entity *e, Entity *entity, Ast *init, Type *named_type) { - if (entity == nullptr) { - retry_proc_lit:; - init = unparen_expr(init); - if (init == nullptr) { - return false; - } - if (init->kind == Ast_TernaryWhenExpr) { - ast_node(we, TernaryWhenExpr, init); - if (we->cond == nullptr) { - return false; - } - if (we->cond->tav.value.kind != ExactValue_Bool) { - return false; - } - init = we->cond->tav.value.value_bool ? we->x : we->y; - goto retry_proc_lit; - } if (init->kind == Ast_ProcLit) { - // NOTE(bill, 2024-07-04): Override as a procedure entity because this could be within a `when` statement - e->kind = Entity_Procedure; - e->type = nullptr; - DeclInfo *d = decl_info_of_entity(e); - d->proc_lit = init; - check_proc_decl(ctx, e, d); - return true; - } - - return false; - } - switch (entity->kind) { - case Entity_TypeName: +gb_internal bool check_override_as_type_due_to_aliasing(CheckerContext *ctx, Entity *e, Entity *entity, Ast *init, Type *named_type) { + if (entity != nullptr && entity->kind == Entity_TypeName) { // @TypeAliasingProblem // NOTE(bill, 2022-02-03): This is used to solve the problem caused by type aliases // being "confused" as constants @@ -266,6 +234,47 @@ gb_internal bool check_try_override_const_decl(CheckerContext *ctx, Entity *e, E e->kind = Entity_TypeName; check_type_decl(ctx, e, init, named_type); return true; + } + return false; +} + +gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d); + +gb_internal bool check_try_override_const_decl(CheckerContext *ctx, Entity *e, Entity *entity, Ast *init, Type *named_type) { + if (entity == nullptr) { + retry_proc_lit:; + init = unparen_expr(init); + if (init == nullptr) { + return false; + } + if (init->kind == Ast_TernaryWhenExpr) { + ast_node(we, TernaryWhenExpr, init); + if (we->cond == nullptr) { + return false; + } + if (we->cond->tav.value.kind != ExactValue_Bool) { + return false; + } + init = we->cond->tav.value.value_bool ? we->x : we->y; + goto retry_proc_lit; + } if (init->kind == Ast_ProcLit) { + // NOTE(bill, 2024-07-04): Override as a procedure entity because this could be within a `when` statement + e->kind = Entity_Procedure; + e->type = nullptr; + DeclInfo *d = decl_info_of_entity(e); + d->proc_lit = init; + check_proc_decl(ctx, e, d); + return true; + } + + return false; + } + switch (entity->kind) { + case Entity_TypeName: + if (check_override_as_type_due_to_aliasing(ctx, e, entity, init, named_type)) { + return true; + } + break; case Entity_Builtin: if (e->type != nullptr) { return false; @@ -274,12 +283,6 @@ gb_internal bool check_try_override_const_decl(CheckerContext *ctx, Entity *e, E e->Builtin.id = entity->Builtin.id; e->type = t_invalid; return true; - case Entity_ProcGroup: - // NOTE(bill, 2020-06-10): It is better to just clone the contents than overriding the entity in the scope - // Thank goodness I made entities a tagged union to allow for this implace patching - e->kind = Entity_ProcGroup; - e->ProcGroup.entities = array_clone(heap_allocator(), entity->ProcGroup.entities); - return true; } if (e->type != nullptr && entity->type != nullptr) { @@ -551,7 +554,7 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr if (init != nullptr) { Entity *entity = check_entity_from_ident_or_selector(ctx, init, false); - if (check_try_override_const_decl(ctx, e, entity, init, named_type)) { + if (check_override_as_type_due_to_aliasing(ctx, e, entity, init, named_type)) { return; } entity = nullptr; -- cgit v1.2.3 From 1a20b78633038614635da99b5e634015d4ce7d6e Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Mon, 8 Jul 2024 21:06:57 +0200 Subject: remove misleading `@(optimization_mode)` values and make "none" inhibit optimizations --- src/check_decl.cpp | 1 - src/checker.cpp | 12 ++++++------ src/entity.cpp | 4 +--- src/llvm_backend.cpp | 17 +++++------------ src/llvm_backend.hpp | 5 ----- src/llvm_backend_general.cpp | 6 ++++++ src/llvm_backend_proc.cpp | 8 +------- 7 files changed, 19 insertions(+), 34 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 84c3d9ecc..7d81d102d 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1030,7 +1030,6 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { switch (e->Procedure.optimization_mode) { case ProcedureOptimizationMode_None: - case ProcedureOptimizationMode_Minimal: if (pl->inlining == ProcInlining_inline) { error(e->token, "#force_inline cannot be used in conjunction with the attribute 'optimization_mode' with neither \"none\" nor \"minimal\""); } diff --git a/src/checker.cpp b/src/checker.cpp index 18b9db6ef..8756cce1a 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3544,19 +3544,19 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { String mode = ev.value_string; if (mode == "none") { ac->optimization_mode = ProcedureOptimizationMode_None; + } else if (mode == "favor_size") { + ac->optimization_mode = ProcedureOptimizationMode_FavorSize; } else if (mode == "minimal") { - ac->optimization_mode = ProcedureOptimizationMode_Minimal; + error(elem, "Invalid optimization_mode 'minimal' for '%.*s', mode has been removed due to confusion, but 'none' has the same behaviour", LIT(name)); } else if (mode == "size") { - ac->optimization_mode = ProcedureOptimizationMode_Size; + error(elem, "Invalid optimization_mode 'size' for '%.*s', mode has been removed due to confusion, but 'favor_size' has the same behaviour", LIT(name)); } else if (mode == "speed") { - ac->optimization_mode = ProcedureOptimizationMode_Speed; + error(elem, "Invalid optimization_mode 'speed' for '%.*s', mode has been removed due to confusion, but 'favor_size' has the same behaviour", LIT(name)); } else { ERROR_BLOCK(); error(elem, "Invalid optimization_mode for '%.*s'. Valid modes:", LIT(name)); error_line("\tnone\n"); - error_line("\tminimal\n"); - error_line("\tsize\n"); - error_line("\tspeed\n"); + error_line("\tfavor_size\n"); } } else { error(elem, "Expected a string for '%.*s'", LIT(name)); diff --git a/src/entity.cpp b/src/entity.cpp index 8f55c1faf..62a190437 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -133,9 +133,7 @@ enum EntityConstantFlags : u32 { enum ProcedureOptimizationMode : u8 { ProcedureOptimizationMode_Default, ProcedureOptimizationMode_None, - ProcedureOptimizationMode_Minimal, - ProcedureOptimizationMode_Size, - ProcedureOptimizationMode_Speed, + ProcedureOptimizationMode_FavorSize, }; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 8c82d7117..cc9254269 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1486,10 +1486,6 @@ gb_internal WORKER_TASK_PROC(lb_llvm_function_pass_per_module) { lb_populate_function_pass_manager(m, m->function_pass_managers[lbFunctionPassManager_default], false, build_context.optimization_level); lb_populate_function_pass_manager(m, m->function_pass_managers[lbFunctionPassManager_default_without_memcpy], true, build_context.optimization_level); lb_populate_function_pass_manager_specific(m, m->function_pass_managers[lbFunctionPassManager_none], -1); - lb_populate_function_pass_manager_specific(m, m->function_pass_managers[lbFunctionPassManager_minimal], 0); - lb_populate_function_pass_manager_specific(m, m->function_pass_managers[lbFunctionPassManager_size], 1); - lb_populate_function_pass_manager_specific(m, m->function_pass_managers[lbFunctionPassManager_speed], 2); - lb_populate_function_pass_manager_specific(m, m->function_pass_managers[lbFunctionPassManager_aggressive], 3); for (i32 i = 0; i < lbFunctionPassManager_COUNT; i++) { LLVMFinalizeFunctionPassManager(m->function_pass_managers[i]); @@ -1513,15 +1509,12 @@ gb_internal WORKER_TASK_PROC(lb_llvm_function_pass_per_module) { if (p->entity && p->entity->kind == Entity_Procedure) { switch (p->entity->Procedure.optimization_mode) { case ProcedureOptimizationMode_None: - case ProcedureOptimizationMode_Minimal: - pass_manager_kind = lbFunctionPassManager_minimal; + pass_manager_kind = lbFunctionPassManager_none; + GB_ASSERT(lb_proc_has_attribute(p->module, p->value, "optnone")); + GB_ASSERT(lb_proc_has_attribute(p->module, p->value, "noinline")); break; - case ProcedureOptimizationMode_Size: - pass_manager_kind = lbFunctionPassManager_size; - lb_add_attribute_to_proc(p->module, p->value, "optsize"); - break; - case ProcedureOptimizationMode_Speed: - pass_manager_kind = lbFunctionPassManager_speed; + case ProcedureOptimizationMode_FavorSize: + GB_ASSERT(lb_proc_has_attribute(p->module, p->value, "optsize")); break; } } diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 447e93d42..022f47857 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -134,11 +134,6 @@ enum lbFunctionPassManagerKind { lbFunctionPassManager_default, lbFunctionPassManager_default_without_memcpy, lbFunctionPassManager_none, - lbFunctionPassManager_minimal, - lbFunctionPassManager_size, - lbFunctionPassManager_speed, - lbFunctionPassManager_aggressive, - lbFunctionPassManager_COUNT }; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index f65bc9ac7..f8a5e1ebf 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2522,6 +2522,12 @@ gb_internal void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, cha gb_internal void lb_add_attribute_to_proc(lbModule *m, LLVMValueRef proc_value, char const *name, u64 value=0) { LLVMAddAttributeAtIndex(proc_value, LLVMAttributeIndex_FunctionIndex, lb_create_enum_attribute(m->ctx, name, value)); } + +gb_internal bool lb_proc_has_attribute(lbModule *m, LLVMValueRef proc_value, char const *name) { + LLVMAttributeRef ref = LLVMGetEnumAttributeAtIndex(proc_value, LLVMAttributeIndex_FunctionIndex, LLVMGetEnumAttributeKindForName(name, gb_strlen(name))); + return ref != nullptr; +} + gb_internal void lb_add_attribute_to_proc_with_string(lbModule *m, LLVMValueRef proc_value, String const &name, String const &value) { LLVMAttributeRef attr = lb_create_string_attribute(m->ctx, name, value); LLVMAddAttributeAtIndex(proc_value, LLVMAttributeIndex_FunctionIndex, attr); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 68ba4f74e..610c34de2 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -163,16 +163,10 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i switch (entity->Procedure.optimization_mode) { case ProcedureOptimizationMode_None: - break; - case ProcedureOptimizationMode_Minimal: lb_add_attribute_to_proc(m, p->value, "optnone"); lb_add_attribute_to_proc(m, p->value, "noinline"); break; - case ProcedureOptimizationMode_Size: - lb_add_attribute_to_proc(m, p->value, "optsize"); - break; - case ProcedureOptimizationMode_Speed: - // TODO(bill): handle this correctly + case ProcedureOptimizationMode_FavorSize: lb_add_attribute_to_proc(m, p->value, "optsize"); break; } -- cgit v1.2.3 From 6959554040d85597300ab2ce6c25852d18e61923 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 13:44:47 +0100 Subject: Calculate size and alignment, and reuse memory for all variadic calls within a procedure body --- src/check_decl.cpp | 9 +++++++++ src/checker.cpp | 2 ++ src/checker.hpp | 4 +++- src/llvm_backend.hpp | 6 +++--- src/llvm_backend_proc.cpp | 25 +++++++++++++++++-------- 5 files changed, 34 insertions(+), 12 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 7d81d102d..6828774e4 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1869,5 +1869,14 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de add_deps_from_child_to_parent(decl); + for (VariadicReuseData const &vr : decl->variadic_reuses) { + GB_ASSERT(vr.slice_type->kind == Type_Slice); + Type *elem = vr.slice_type->Slice.elem; + i64 size = type_size_of(elem); + i64 align = type_align_of(elem); + decl->variadic_reuse_max_bytes = gb_max(decl->variadic_reuse_max_bytes, size*vr.max_count); + decl->variadic_reuse_max_align = gb_max(decl->variadic_reuse_max_align, align); + } + return true; } diff --git a/src/checker.cpp b/src/checker.cpp index 9adf4ef3c..336440d32 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -185,6 +185,8 @@ gb_internal void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) { ptr_set_init(&d->type_info_deps, 0); d->labels.allocator = heap_allocator(); d->variadic_reuses.allocator = heap_allocator(); + d->variadic_reuse_max_bytes = 0; + d->variadic_reuse_max_align = 1; } gb_internal DeclInfo *make_decl_info(Scope *scope, DeclInfo *parent) { diff --git a/src/checker.hpp b/src/checker.hpp index 2fadbe56a..d76e4c7d0 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -183,7 +183,7 @@ char const *ProcCheckedState_strings[ProcCheckedState_COUNT] { struct VariadicReuseData { Type *slice_type; // ..elem_type - isize max_count; + i64 max_count; }; // DeclInfo is used to store information of certain declarations to allow for "any order" usage @@ -225,6 +225,8 @@ struct DeclInfo { Array labels; Array variadic_reuses; + i64 variadic_reuse_max_bytes; + i64 variadic_reuse_max_align; // NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time struct lbModule *code_gen_module; diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 100748038..deb05528f 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -296,9 +296,8 @@ enum lbProcedureFlag : u32 { lbProcedureFlag_DebugAllocaCopy = 1<<1, }; -struct lbVariadicReuseData { +struct lbVariadicReuseSlices { Type *slice_type; - lbAddr base_array; lbAddr slice_addr; }; @@ -342,7 +341,8 @@ struct lbProcedure { bool in_multi_assignment; Array raw_input_parameters; - Array variadic_reuses; + Array variadic_reuses; + lbAddr variadic_reuse_base_array_ptr; LLVMValueRef temp_callee_return_struct_memory; Ast *curr_stmt; diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index bc85b14c2..7a895fbdd 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -3456,39 +3456,48 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { } isize slice_len = var_args.count; if (slice_len > 0) { - lbAddr base_array = {}; lbAddr slice = {}; for (auto const &vr : p->variadic_reuses) { if (are_types_identical(vr.slice_type, slice_type)) { - base_array = vr.base_array; slice = vr.slice_addr; break; } } + DeclInfo *d = decl_info_of_entity(p->entity); - if (d != nullptr && base_array.addr.value == nullptr) { + if (d != nullptr && slice.addr.value == nullptr) { for (auto const &vr : d->variadic_reuses) { if (are_types_identical(vr.slice_type, slice_type)) { - base_array = lb_add_local_generated(p, alloc_type_array(elem_type, vr.max_count), true); slice = lb_add_local_generated(p, slice_type, true); - array_add(&p->variadic_reuses, lbVariadicReuseData{slice_type, base_array, slice}); + array_add(&p->variadic_reuses, lbVariadicReuseSlices{slice_type, slice}); break; } } } - GB_ASSERT(base_array.addr.value != nullptr); + + lbValue base_array_ptr = p->variadic_reuse_base_array_ptr.addr; + if (d != nullptr && base_array_ptr.value == nullptr) { + i64 max_bytes = d->variadic_reuse_max_bytes; + i64 max_align = gb_max(d->variadic_reuse_max_align, 16); + p->variadic_reuse_base_array_ptr = lb_add_local_generated(p, alloc_type_array(t_u8, max_bytes), true); + lb_try_update_alignment(p->variadic_reuse_base_array_ptr.addr, cast(unsigned)max_align); + base_array_ptr = p->variadic_reuse_base_array_ptr.addr; + } + + GB_ASSERT(base_array_ptr.value != nullptr); GB_ASSERT(slice.addr.value != nullptr); + base_array_ptr = lb_emit_conv(p, base_array_ptr, alloc_type_pointer(alloc_type_array(elem_type, slice_len))); for (isize i = 0; i < var_args.count; i++) { - lbValue addr = lb_emit_array_epi(p, base_array.addr, cast(i32)i); + lbValue addr = lb_emit_array_epi(p, base_array_ptr, cast(i32)i); lbValue var_arg = var_args[i]; var_arg = lb_emit_conv(p, var_arg, elem_type); lb_emit_store(p, addr, var_arg); } - lbValue base_elem = lb_emit_array_epi(p, base_array.addr, 0); + lbValue base_elem = lb_emit_array_epi(p, base_array_ptr, 0); lbValue len = lb_const_int(p->module, t_int, slice_len); lb_fill_slice(p, slice, base_elem, len); -- cgit v1.2.3 From e4ba786948e8f3abe89ff2ec6b7618f4af2b21cb Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 15 Jul 2024 01:29:57 +0100 Subject: Remove use of mutex in single threaded code --- src/check_decl.cpp | 3 +-- src/checker.cpp | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 7 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 6828774e4..a1436fe03 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -182,8 +182,7 @@ gb_internal void override_entity_in_scope(Entity *original_entity, Entity *new_e original_entity->type = new_entity->type; original_entity->aliased_of = new_entity; - Ast *empty_ident = nullptr; - original_entity->identifier.compare_exchange_strong(empty_ident, new_entity->identifier); + original_entity->identifier.store(new_entity->identifier); if (original_entity->identifier.load() != nullptr && original_entity->identifier.load()->kind == Ast_Ident) { diff --git a/src/checker.cpp b/src/checker.cpp index 0e65af211..cc39e9a44 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -384,6 +384,8 @@ gb_internal Entity *scope_lookup_current(Scope *s, String const &name) { return nullptr; } +gb_global bool in_single_threaded_mode_scopes = false; + gb_internal void scope_lookup_parent(Scope *scope, String const &name, Scope **scope_, Entity **entity_) { if (scope != nullptr) { bool gone_thru_proc = false; @@ -391,9 +393,9 @@ gb_internal void scope_lookup_parent(Scope *scope, String const &name, Scope **s StringHashKey key = string_hash_string(name); for (Scope *s = scope; s != nullptr; s = s->parent) { Entity **found = nullptr; - rw_mutex_shared_lock(&s->mutex); + if (!in_single_threaded_mode_scopes) rw_mutex_shared_lock(&s->mutex); found = string_map_get(&s->elements, key); - rw_mutex_shared_unlock(&s->mutex); + if (!in_single_threaded_mode_scopes) rw_mutex_shared_unlock(&s->mutex); if (found) { Entity *e = *found; if (gone_thru_proc) { @@ -513,7 +515,11 @@ end:; gb_internal Entity *scope_insert(Scope *s, Entity *entity) { String name = entity->token.string; - return scope_insert_with_name(s, name, entity); + if (in_single_threaded_mode_scopes) { + return scope_insert_with_name_no_mutex(s, name, entity); + } else { + return scope_insert_with_name(s, name, entity); + } } gb_internal Entity *scope_insert_no_mutex(Scope *s, Entity *entity) { @@ -1795,8 +1801,7 @@ gb_internal void add_entity_use(CheckerContext *c, Ast *identifier, Entity *enti if (identifier == nullptr || identifier->kind != Ast_Ident) { return; } - Ast *empty_ident = nullptr; - entity->identifier.compare_exchange_strong(empty_ident, identifier); + entity->identifier.store(identifier); identifier->Ident.entity = entity; @@ -4591,6 +4596,8 @@ gb_internal void check_single_global_entity(Checker *c, Entity *e, DeclInfo *d) } gb_internal void check_all_global_entities(Checker *c) { + in_single_threaded_mode_scopes = true; + // NOTE(bill): This must be single threaded // Don't bother trying for_array(i, c->info.entities) { @@ -4610,6 +4617,8 @@ gb_internal void check_all_global_entities(Checker *c) { (void)type_align_of(e->type); } } + + in_single_threaded_mode_scopes = false; } -- cgit v1.2.3 From 9eb7186cda2081c7fadbc0d196346b4e57f5e4c1 Mon Sep 17 00:00:00 2001 From: Roland Kovacs Date: Sun, 11 Aug 2024 01:09:22 +0200 Subject: Fix alias handling of procedures An incorrect memmove when overriding entities caused multiple ones to point to the same procedure with incomplete variant data, resulting in later hiting a compiler assertion. Introduced delayed type checking for procedure aliases, as it was masked by the previous error in the override logic. --- src/check_decl.cpp | 4 +++- src/checker.cpp | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index a1436fe03..1425aafa8 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -180,6 +180,8 @@ gb_internal void override_entity_in_scope(Entity *original_entity, Entity *new_e original_entity->flags |= EntityFlag_Overridden; original_entity->type = new_entity->type; + original_entity->kind = new_entity->kind; + original_entity->decl_info = new_entity->decl_info; original_entity->aliased_of = new_entity; original_entity->identifier.store(new_entity->identifier); @@ -193,7 +195,7 @@ gb_internal void override_entity_in_scope(Entity *original_entity, Entity *new_e // This is most likely NEVER required, but it does not at all hurt to keep isize offset = cast(u8 *)&original_entity->Dummy.start - cast(u8 *)original_entity; isize size = gb_size_of(*original_entity) - offset; - gb_memmove(cast(u8 *)original_entity, cast(u8 *)new_entity, size); + gb_memmove(cast(u8 *)original_entity + offset, cast(u8 *)new_entity + offset, size); } gb_internal bool check_override_as_type_due_to_aliasing(CheckerContext *ctx, Entity *e, Entity *entity, Ast *init, Type *named_type) { diff --git a/src/checker.cpp b/src/checker.cpp index 60000ec29..91e7a08fe 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -5676,6 +5676,18 @@ gb_internal void check_procedure_later_from_entity(Checker *c, Entity *e, char c if ((e->flags & EntityFlag_ProcBodyChecked) != 0) { return; } + if ((e->flags & EntityFlag_Overridden) != 0) { + // NOTE (zen3ger) Delay checking of a proc alias until the underlying proc is checked. + GB_ASSERT(e->aliased_of != nullptr); + GB_ASSERT(e->aliased_of->kind == Entity_Procedure); + if ((e->aliased_of->flags & EntityFlag_ProcBodyChecked) != 0) { + e->flags |= EntityFlag_ProcBodyChecked; + return; + } + // NOTE (zen3ger) A proc alias *does not* have a body and tags! + check_procedure_later(c, e->file, e->token, e->decl_info, e->type, nullptr, 0); + return; + } Type *type = base_type(e->type); if (type == t_invalid) { return; -- cgit v1.2.3 From efe68c2e24e0a38e591f146822ed93904e4193d7 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Sat, 27 Jul 2024 04:20:03 +0200 Subject: posix: add package --- core/c/libc/complex.odin | 4 +- core/c/libc/errno.odin | 2 +- core/c/libc/setjmp.odin | 19 +- core/c/libc/stdio.odin | 43 +- core/c/libc/stdlib.odin | 21 +- core/c/libc/string.odin | 2 +- core/c/libc/time.odin | 32 +- core/mem/virtual/virtual_darwin.odin | 153 +-- core/os/dir_unix.odin | 1 + core/os/os_darwin.odin | 72 +- core/os/os_freebsd.odin | 10 +- core/os/os_linux.odin | 6 +- core/os/os_netbsd.odin | 8 +- core/os/os_openbsd.odin | 6 +- core/path/filepath/path_unix.odin | 7 +- core/prof/spall/spall_unix.odin | 33 +- core/sys/posix/README.md | 57 + core/sys/posix/arpa_inet.odin | 58 + core/sys/posix/dirent.odin | 205 +++ core/sys/posix/dlfcn.odin | 115 ++ core/sys/posix/errno.odin | 373 ++++++ core/sys/posix/fcntl.odin | 415 +++++++ core/sys/posix/fnmatch.odin | 58 + core/sys/posix/glob.odin | 179 +++ core/sys/posix/grp.odin | 130 ++ core/sys/posix/iconv.odin | 50 + core/sys/posix/langinfo.odin | 285 +++++ core/sys/posix/libgen.odin | 74 ++ core/sys/posix/limits.odin | 459 +++++++ core/sys/posix/locale.odin | 93 ++ core/sys/posix/monetary.odin | 42 + core/sys/posix/net_if.odin | 60 + core/sys/posix/netdb.odin | 443 +++++++ core/sys/posix/netinet_in.odin | 199 +++ core/sys/posix/netinet_tcp.odin | 11 + core/sys/posix/poll.odin | 78 ++ core/sys/posix/posix.odin | 26 + core/sys/posix/pthread.odin | 518 ++++++++ core/sys/posix/pwd.odin | 168 +++ core/sys/posix/sched.odin | 105 ++ core/sys/posix/setjmp.odin | 58 + core/sys/posix/signal.odin | 1131 +++++++++++++++++ core/sys/posix/stdio.odin | 332 +++++ core/sys/posix/stdlib.odin | 450 +++++++ core/sys/posix/string.odin | 47 + core/sys/posix/sys_ipc.odin | 89 ++ core/sys/posix/sys_mman.odin | 229 ++++ core/sys/posix/sys_msg.odin | 161 +++ core/sys/posix/sys_resource.odin | 152 +++ core/sys/posix/sys_select.odin | 120 ++ core/sys/posix/sys_sem.odin | 132 ++ core/sys/posix/sys_shm.odin | 146 +++ core/sys/posix/sys_socket.odin | 491 ++++++++ core/sys/posix/sys_stat.odin | 540 ++++++++ core/sys/posix/sys_statvfs.odin | 135 ++ core/sys/posix/sys_time.odin | 82 ++ core/sys/posix/sys_times.odin | 38 + core/sys/posix/sys_uio.odin | 42 + core/sys/posix/sys_un.odin | 17 + core/sys/posix/sys_utsname.odin | 55 + core/sys/posix/sys_wait.odin | 380 ++++++ core/sys/posix/termios.odin | 475 +++++++ core/sys/posix/time.odin | 234 ++++ core/sys/posix/ulimit.odin | 43 + core/sys/posix/unistd.odin | 1914 +++++++++++++++++++++++++++++ core/sys/posix/utime.odin | 36 + core/sys/posix/wordexp.odin | 107 ++ core/sys/unix/pthread_netbsd.odin | 2 +- core/sys/unix/pthread_unix.odin | 5 + core/sys/unix/time_unix.odin | 83 -- core/time/time_linux.odin | 38 + core/time/time_unix.odin | 42 +- examples/all/all_posix.odin | 6 + src/big_int.cpp | 4 + src/check_builtin.cpp | 17 + src/check_decl.cpp | 20 +- src/checker_builtin_procs.hpp | 4 + tests/core/normal.odin | 1 + tests/core/sys/posix/posix.odin | 211 ++++ tests/core/sys/posix/structs.odin | 127 ++ tests/core/sys/posix/structs/.gitignore | 2 + tests/core/sys/posix/structs/structs.c | 103 ++ tests/core/sys/posix/structs/structs.odin | 74 ++ 83 files changed, 12666 insertions(+), 329 deletions(-) create mode 100644 core/sys/posix/README.md create mode 100644 core/sys/posix/arpa_inet.odin create mode 100644 core/sys/posix/dirent.odin create mode 100644 core/sys/posix/dlfcn.odin create mode 100644 core/sys/posix/errno.odin create mode 100644 core/sys/posix/fcntl.odin create mode 100644 core/sys/posix/fnmatch.odin create mode 100644 core/sys/posix/glob.odin create mode 100644 core/sys/posix/grp.odin create mode 100644 core/sys/posix/iconv.odin create mode 100644 core/sys/posix/langinfo.odin create mode 100644 core/sys/posix/libgen.odin create mode 100644 core/sys/posix/limits.odin create mode 100644 core/sys/posix/locale.odin create mode 100644 core/sys/posix/monetary.odin create mode 100644 core/sys/posix/net_if.odin create mode 100644 core/sys/posix/netdb.odin create mode 100644 core/sys/posix/netinet_in.odin create mode 100644 core/sys/posix/netinet_tcp.odin create mode 100644 core/sys/posix/poll.odin create mode 100644 core/sys/posix/posix.odin create mode 100644 core/sys/posix/pthread.odin create mode 100644 core/sys/posix/pwd.odin create mode 100644 core/sys/posix/sched.odin create mode 100644 core/sys/posix/setjmp.odin create mode 100644 core/sys/posix/signal.odin create mode 100644 core/sys/posix/stdio.odin create mode 100644 core/sys/posix/stdlib.odin create mode 100644 core/sys/posix/string.odin create mode 100644 core/sys/posix/sys_ipc.odin create mode 100644 core/sys/posix/sys_mman.odin create mode 100644 core/sys/posix/sys_msg.odin create mode 100644 core/sys/posix/sys_resource.odin create mode 100644 core/sys/posix/sys_select.odin create mode 100644 core/sys/posix/sys_sem.odin create mode 100644 core/sys/posix/sys_shm.odin create mode 100644 core/sys/posix/sys_socket.odin create mode 100644 core/sys/posix/sys_stat.odin create mode 100644 core/sys/posix/sys_statvfs.odin create mode 100644 core/sys/posix/sys_time.odin create mode 100644 core/sys/posix/sys_times.odin create mode 100644 core/sys/posix/sys_uio.odin create mode 100644 core/sys/posix/sys_un.odin create mode 100644 core/sys/posix/sys_utsname.odin create mode 100644 core/sys/posix/sys_wait.odin create mode 100644 core/sys/posix/termios.odin create mode 100644 core/sys/posix/time.odin create mode 100644 core/sys/posix/ulimit.odin create mode 100644 core/sys/posix/unistd.odin create mode 100644 core/sys/posix/utime.odin create mode 100644 core/sys/posix/wordexp.odin delete mode 100644 core/sys/unix/time_unix.odin create mode 100644 core/time/time_linux.odin create mode 100644 examples/all/all_posix.odin create mode 100644 tests/core/sys/posix/posix.odin create mode 100644 tests/core/sys/posix/structs.odin create mode 100644 tests/core/sys/posix/structs/.gitignore create mode 100644 tests/core/sys/posix/structs/structs.c create mode 100644 tests/core/sys/posix/structs/structs.odin (limited to 'src/check_decl.cpp') diff --git a/core/c/libc/complex.odin b/core/c/libc/complex.odin index 81d2b75be..98fd7b1bb 100644 --- a/core/c/libc/complex.odin +++ b/core/c/libc/complex.odin @@ -47,8 +47,8 @@ foreign libc { clogf :: proc(z: complex_float) -> complex_float --- // 7.3.8 Power and absolute-value functions - cabs :: proc(z: complex_double) -> complex_double --- - cabsf :: proc(z: complex_float) -> complex_float --- + cabs :: proc(z: complex_double) -> double --- + cabsf :: proc(z: complex_float) -> float --- cpow :: proc(x, y: complex_double) -> complex_double --- cpowf :: proc(x, y: complex_float) -> complex_float --- csqrt :: proc(z: complex_double) -> complex_double --- diff --git a/core/c/libc/errno.odin b/core/c/libc/errno.odin index d28a24f56..843b2f1b6 100644 --- a/core/c/libc/errno.odin +++ b/core/c/libc/errno.odin @@ -102,6 +102,6 @@ when ODIN_OS == .Haiku { // read the value, or to produce an lvalue such that you can assign a different // error value to errno. To work around this, just expose it as a function like // it actually is. -errno :: #force_inline proc() -> ^int { +errno :: #force_inline proc "contextless" () -> ^int { return _get_errno() } diff --git a/core/c/libc/setjmp.odin b/core/c/libc/setjmp.odin index 68f5ac010..101b614b3 100644 --- a/core/c/libc/setjmp.odin +++ b/core/c/libc/setjmp.odin @@ -32,24 +32,21 @@ when ODIN_OS == .Windows { // the RDX register will contain zero and correctly set the flag to disable // stack unwinding. @(link_name="_setjmp") - setjmp :: proc(env: ^jmp_buf, hack: rawptr = nil) -> int --- + setjmp :: proc(env: ^jmp_buf, hack: rawptr = nil) -> int --- } } else { @(default_calling_convention="c") foreign libc { // 7.13.1 Save calling environment - // - // NOTE(dweiler): C11 requires setjmp be a macro, which means it won't - // necessarily export a symbol named setjmp but rather _setjmp in the case - // of musl, glibc, BSD libc, and msvcrt. - @(link_name="_setjmp") - setjmp :: proc(env: ^jmp_buf) -> int --- + @(link_name=LSETJMP) + setjmp :: proc(env: ^jmp_buf) -> int --- } } @(default_calling_convention="c") foreign libc { // 7.13.2 Restore calling environment + @(link_name=LLONGJMP) longjmp :: proc(env: ^jmp_buf, val: int) -> ! --- } @@ -64,3 +61,11 @@ foreign libc { // The choice of 4096 bytes for storage of this type is more than enough on all // relevant platforms. jmp_buf :: struct #align(16) { _: [4096]char, } + +when ODIN_OS == .NetBSD { + @(private) LSETJMP :: "__setjmp14" + @(private) LLONGJMP :: "__longjmp14" +} else { + @(private) LSETJMP :: "setjmp" + @(private) LLONGJMP :: "longjmp" +} diff --git a/core/c/libc/stdio.odin b/core/c/libc/stdio.odin index 3e1d0f5a2..4be00ff0b 100644 --- a/core/c/libc/stdio.odin +++ b/core/c/libc/stdio.odin @@ -17,6 +17,12 @@ when ODIN_OS == .Windows { FILE :: struct {} +Whence :: enum int { + SET = SEEK_SET, + CUR = SEEK_CUR, + END = SEEK_END, +} + // MSVCRT compatible. when ODIN_OS == .Windows { _IOFBF :: 0x0000 @@ -101,6 +107,8 @@ when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD { SEEK_CUR :: 1 SEEK_END :: 2 + TMP_MAX :: 308915776 + foreign libc { __sF: [3]FILE } @@ -128,6 +136,8 @@ when ODIN_OS == .FreeBSD { SEEK_CUR :: 1 SEEK_END :: 2 + TMP_MAX :: 308915776 + foreign libc { @(link_name="__stderrp") stderr: ^FILE @(link_name="__stdinp") stdin: ^FILE @@ -195,10 +205,21 @@ when ODIN_OS == .Haiku { } } +when ODIN_OS == .NetBSD { + @(private) LRENAME :: "__posix_rename" + @(private) LFGETPOS :: "__fgetpos50" + @(private) LFSETPOS :: "__fsetpos50" +} else { + @(private) LRENAME :: "rename" + @(private) LFGETPOS :: "fgetpos" + @(private) LFSETPOS :: "fsetpos" +} + @(default_calling_convention="c") foreign libc { // 7.21.4 Operations on files remove :: proc(filename: cstring) -> int --- + @(link_name=LRENAME) rename :: proc(old, new: cstring) -> int --- tmpfile :: proc() -> ^FILE --- tmpnam :: proc(s: [^]char) -> [^]char --- @@ -240,8 +261,10 @@ foreign libc { fwrite :: proc(ptr: rawptr, size: size_t, nmemb: size_t, stream: ^FILE) -> size_t --- // 7.21.9 File positioning functions + @(link_name=LFGETPOS) fgetpos :: proc(stream: ^FILE, pos: ^fpos_t) -> int --- - fseek :: proc(stream: ^FILE, offset: long, whence: int) -> int --- + fseek :: proc(stream: ^FILE, offset: long, whence: Whence) -> int --- + @(link_name=LFSETPOS) fsetpos :: proc(stream: ^FILE, pos: ^fpos_t) -> int --- ftell :: proc(stream: ^FILE) -> long --- rewind :: proc(stream: ^FILE) --- @@ -288,11 +311,11 @@ to_stream :: proc(file: ^FILE) -> io.Stream { return 0, unknown_or_eof(file) } - if fseek(file, long(offset), SEEK_SET) != 0 { + if fseek(file, long(offset), .SET) != 0 { return 0, unknown_or_eof(file) } - defer fseek(file, long(curr), SEEK_SET) + defer fseek(file, long(curr), .SET) n = i64(fread(raw_data(p), size_of(byte), len(p), file)) if n == 0 { err = unknown_or_eof(file) } @@ -307,17 +330,21 @@ to_stream :: proc(file: ^FILE) -> io.Stream { return 0, unknown_or_eof(file) } - if fseek(file, long(offset), SEEK_SET) != 0 { + if fseek(file, long(offset), .SET) != 0 { return 0, unknown_or_eof(file) } - defer fseek(file, long(curr), SEEK_SET) + defer fseek(file, long(curr), .SET) n = i64(fwrite(raw_data(p), size_of(byte), len(p), file)) if n == 0 { err = unknown_or_eof(file) } case .Seek: - if fseek(file, long(offset), int(whence)) != 0 { + #assert(int(Whence.SET) == int(io.Seek_From.Start)) + #assert(int(Whence.CUR) == int(io.Seek_From.Current)) + #assert(int(Whence.END) == int(io.Seek_From.End)) + + if fseek(file, long(offset), Whence(whence)) != 0 { return 0, unknown_or_eof(file) } @@ -326,9 +353,9 @@ to_stream :: proc(file: ^FILE) -> io.Stream { if curr == -1 { return 0, unknown_or_eof(file) } - defer fseek(file, curr, SEEK_SET) + defer fseek(file, curr, .SET) - if fseek(file, 0, SEEK_END) != 0 { + if fseek(file, 0, .END) != 0 { return 0, unknown_or_eof(file) } diff --git a/core/c/libc/stdlib.odin b/core/c/libc/stdlib.odin index d797b8746..08c6fa6f0 100644 --- a/core/c/libc/stdlib.odin +++ b/core/c/libc/stdlib.odin @@ -40,10 +40,9 @@ when ODIN_OS == .Linux { } -when ODIN_OS == .Darwin { +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD { RAND_MAX :: 0x7fffffff - // GLIBC and MUSL only @(private="file") @(default_calling_convention="c") foreign libc { @@ -55,6 +54,20 @@ when ODIN_OS == .Darwin { } } +when ODIN_OS == .NetBSD { + RAND_MAX :: 0x7fffffff + + @(private="file") + @(default_calling_convention="c") + foreign libc { + __mb_cur_max: size_t + } + + MB_CUR_MAX :: #force_inline proc() -> size_t { + return __mb_cur_max + } +} + // C does not declare what these values should be, as an implementation is free // to use any two distinct values it wants to indicate success or failure. // However, nobody actually does and everyone appears to have agreed upon these @@ -99,7 +112,7 @@ foreign libc { at_quick_exit :: proc(func: proc "c" ()) -> int --- exit :: proc(status: int) -> ! --- _Exit :: proc(status: int) -> ! --- - getenv :: proc(name: cstring) -> [^]char --- + getenv :: proc(name: cstring) -> cstring --- quick_exit :: proc(status: int) -> ! --- system :: proc(cmd: cstring) -> int --- @@ -150,4 +163,4 @@ aligned_free :: #force_inline proc "c" (ptr: rawptr) { } else { free(ptr) } -} \ No newline at end of file +} diff --git a/core/c/libc/string.odin b/core/c/libc/string.odin index e6a959f7b..cde9c7e6b 100644 --- a/core/c/libc/string.odin +++ b/core/c/libc/string.odin @@ -40,7 +40,7 @@ foreign libc { strtok :: proc(s1: [^]char, s2: cstring) -> [^]char --- // 7.24.6 Miscellaneous functions - strerror :: proc(errnum: int) -> [^]char --- + strerror :: proc(errnum: int) -> cstring --- strlen :: proc(s: cstring) -> size_t --- } memset :: proc "c" (s: rawptr, c: int, n: size_t) -> rawptr { diff --git a/core/c/libc/time.odin b/core/c/libc/time.odin index 924cf4aec..21859c602 100644 --- a/core/c/libc/time.odin +++ b/core/c/libc/time.odin @@ -50,30 +50,56 @@ when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS = foreign libc { // 7.27.2 Time manipulation functions clock :: proc() -> clock_t --- + @(link_name=LDIFFTIME) difftime :: proc(time1, time2: time_t) -> double --- + @(link_name=LMKTIME) mktime :: proc(timeptr: ^tm) -> time_t --- + @(link_name=LTIME) time :: proc(timer: ^time_t) -> time_t --- timespec_get :: proc(ts: ^timespec, base: int) -> int --- // 7.27.3 Time conversion functions asctime :: proc(timeptr: ^tm) -> [^]char --- + @(link_name=LCTIME) ctime :: proc(timer: ^time_t) -> [^]char --- + @(link_name=LGMTIME) gmtime :: proc(timer: ^time_t) -> ^tm --- + @(link_name=LLOCALTIME) localtime :: proc(timer: ^time_t) -> ^tm --- strftime :: proc(s: [^]char, maxsize: size_t, format: cstring, timeptr: ^tm) -> size_t --- } + when ODIN_OS == .NetBSD { + @(private) LDIFFTIME :: "__difftime50" + @(private) LMKTIME :: "__mktime50" + @(private) LTIME :: "__time50" + @(private) LCTIME :: "__ctime50" + @(private) LGMTIME :: "__gmtime50" + @(private) LLOCALTIME :: "__localtime50" + } else { + @(private) LDIFFTIME :: "difftime" + @(private) LMKTIME :: "mktime" + @(private) LTIME :: "ltime" + @(private) LCTIME :: "ctime" + @(private) LGMTIME :: "gmtime" + @(private) LLOCALTIME :: "localtime" + } + when ODIN_OS == .OpenBSD { CLOCKS_PER_SEC :: 100 } else { CLOCKS_PER_SEC :: 1000000 } - TIME_UTC :: 1 + TIME_UTC :: 1 - time_t :: distinct i64 + time_t :: distinct i64 - clock_t :: long + when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD { + clock_t :: distinct int32_t + } else { + clock_t :: distinct long + } timespec :: struct { tv_sec: time_t, diff --git a/core/mem/virtual/virtual_darwin.odin b/core/mem/virtual/virtual_darwin.odin index d2e3c8b51..9098866d0 100644 --- a/core/mem/virtual/virtual_darwin.odin +++ b/core/mem/virtual/virtual_darwin.odin @@ -2,141 +2,45 @@ //+private package mem_virtual -foreign import libc "system:System.framework" -import "core:c" - -PROT_NONE :: 0x0 /* [MC2] no permissions */ -PROT_READ :: 0x1 /* [MC2] pages can be read */ -PROT_WRITE :: 0x2 /* [MC2] pages can be written */ -PROT_EXEC :: 0x4 /* [MC2] pages can be executed */ - -// Sharing options -MAP_SHARED :: 0x1 /* [MF|SHM] share changes */ -MAP_PRIVATE :: 0x2 /* [MF|SHM] changes are private */ - -// Other flags -MAP_FIXED :: 0x0010 /* [MF|SHM] interpret addr exactly */ -MAP_RENAME :: 0x0020 /* Sun: rename private pages to file */ -MAP_NORESERVE :: 0x0040 /* Sun: don't reserve needed swap area */ -MAP_RESERVED0080 :: 0x0080 /* previously unimplemented MAP_INHERIT */ -MAP_NOEXTEND :: 0x0100 /* for MAP_FILE, don't change file size */ -MAP_HASSEMAPHORE :: 0x0200 /* region may contain semaphores */ -MAP_NOCACHE :: 0x0400 /* don't cache pages for this mapping */ -MAP_JIT :: 0x0800 /* Allocate a region that will be used for JIT purposes */ - -// Mapping type -MAP_FILE :: 0x0000 /* map from file (default) */ -MAP_ANONYMOUS :: 0x1000 /* allocated from memory, swap space */ - - -/* - * The MAP_RESILIENT_* flags can be used when the caller wants to map some - * possibly unreliable memory and be able to access it safely, possibly - * getting the wrong contents rather than raising any exception. - * For safety reasons, such mappings have to be read-only (PROT_READ access - * only). - * - * MAP_RESILIENT_CODESIGN: - * accessing this mapping will not generate code-signing violations, - * even if the contents are tainted. - * MAP_RESILIENT_MEDIA: - * accessing this mapping will not generate an exception if the contents - * are not available (unreachable removable or remote media, access beyond - * end-of-file, ...). Missing contents will be replaced with zeroes. - */ -MAP_RESILIENT_CODESIGN :: 0x2000 /* no code-signing failures */ -MAP_RESILIENT_MEDIA :: 0x4000 /* no backing-store failures */ - -MAP_32BIT :: 0x8000 /* Return virtual addresses <4G only */ - -// Flags used to support translated processes. -MAP_TRANSLATED_ALLOW_EXECUTE :: 0x20000 /* allow execute in translated processes */ -MAP_UNIX03 :: 0x40000 /* UNIX03 compliance */ - -// Process memory locking -MCL_CURRENT :: 0x0001 /* [ML] Lock only current memory */ -MCL_FUTURE :: 0x0002 /* [ML] Lock all future memory as well */ - -MADV_NORMAL :: 0 /* [MC1] no further special treatment */ -MADV_RANDOM :: 1 /* [MC1] expect random page refs */ -MADV_SEQUENTIAL :: 2 /* [MC1] expect sequential page refs */ -MADV_WILLNEED :: 3 /* [MC1] will need these pages */ -MADV_DONTNEED :: 4 /* [MC1] dont need these pages */ -MADV_FREE :: 5 /* pages unneeded, discard contents */ -MADV_ZERO_WIRED_PAGES :: 6 /* zero the wired pages that have not been unwired before the entry is deleted */ -MADV_FREE_REUSABLE :: 7 /* pages can be reused (by anyone) */ -MADV_FREE_REUSE :: 8 /* caller wants to reuse those pages */ -MADV_CAN_REUSE :: 9 -MADV_PAGEOUT :: 10 /* page out now (internal only) */ - -// msync() flags -MS_ASYNC :: 0x0001 /* [MF|SIO] return immediately */ -MS_INVALIDATE :: 0x0002 /* [MF|SIO] invalidate all cached data */ -MS_SYNC :: 0x0010 /* [MF|SIO] msync synchronously */ -MS_KILLPAGES :: 0x0004 /* invalidate pages, leave mapped */ -MS_DEACTIVATE :: 0x0008 /* deactivate pages, leave mapped */ - -// Return bits from mincore -MINCORE_INCORE :: 0x1 /* Page is incore */ -MINCORE_REFERENCED :: 0x2 /* Page has been referenced by us */ -MINCORE_MODIFIED :: 0x4 /* Page has been modified by us */ -MINCORE_REFERENCED_OTHER :: 0x8 /* Page has been referenced */ -MINCORE_MODIFIED_OTHER :: 0x10 /* Page has been modified */ -MINCORE_PAGED_OUT :: 0x20 /* Page has been paged out */ -MINCORE_COPIED :: 0x40 /* Page has been copied */ -MINCORE_ANONYMOUS :: 0x80 /* Page belongs to an anonymous object */ - -// Allocation failure result -MAP_FAILED : rawptr = rawptr(~uintptr(0)) - -foreign libc { - @(link_name="mlockall") _mlockall :: proc(flags: c.int) -> c.int --- - @(link_name="munlockall") _munlockall :: proc() -> c.int --- - @(link_name="mlock") _mlock :: proc(addr: rawptr, len: c.size_t) -> c.int --- - @(link_name="mmap") _mmap :: proc(addr: rawptr, len: c.size_t, prot: c.int, flags: c.int, fd: c.int, offset: int) -> rawptr --- - @(link_name="mprotect") _mprotect :: proc(addr: rawptr, len: c.size_t, prot: c.int) -> c.int --- - @(link_name="msync") _msync :: proc(addr: rawptr, len: c.size_t) -> c.int --- - @(link_name="munlock") _munlock :: proc(addr: rawptr, len: c.size_t) -> c.int --- - @(link_name="munmap") _munmap :: proc(addr: rawptr, len: c.size_t) -> c.int --- - @(link_name="shm_open") _shm_open :: proc(name: cstring, oflag: c.int, #c_vararg args: ..any) -> c.int --- - @(link_name="shm_unlink") _shm_unlink :: proc(name: cstring) -> c.int --- - @(link_name="posix_madvise") _posix_madvise :: proc(addr: rawptr, len: c.size_t, advice: c.int) -> c.int --- - @(link_name="madvise") _madvise :: proc(addr: rawptr, len: c.size_t, advice: c.int) -> c.int --- - @(link_name="mincore") _mincore :: proc(addr: rawptr, len: c.size_t, vec: cstring) -> c.int --- - @(link_name="minherit") _minherit :: proc(addr: rawptr, len: c.size_t, inherit: c.int) -> c.int --- -} +import "core:sys/posix" + +MAP_ANONYMOUS :: 0x1000 /* allocated from memory, swap space */ +MADV_FREE :: 5 /* pages unneeded, discard contents */ _reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) { - result := _mmap(nil, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) - if result == MAP_FAILED { + flags := posix.Map_Flags{ .PRIVATE } + transmute(posix.Map_Flags)i32(MAP_ANONYMOUS) + result := posix.mmap(nil, size, {}, flags) + if result == posix.MAP_FAILED { return nil, .Out_Of_Memory } + return ([^]byte)(uintptr(result))[:size], nil } _commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error { - result := _mprotect(data, size, PROT_READ|PROT_WRITE) - if result != 0 { + if posix.mprotect(data, size, { .READ, .WRITE }) != .OK { return .Out_Of_Memory } + return nil } + _decommit :: proc "contextless" (data: rawptr, size: uint) { - _mprotect(data, size, PROT_NONE) - _madvise(data, size, MADV_FREE) + posix.mprotect(data, size, {}) + posix.posix_madvise(data, size, transmute(posix.MAdvice)i32(MADV_FREE)) } + _release :: proc "contextless" (data: rawptr, size: uint) { - _munmap(data, size) + posix.munmap(data, size) } + _protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool { - pflags: c.int - pflags = PROT_NONE - if .Read in flags { pflags |= PROT_READ } - if .Write in flags { pflags |= PROT_WRITE } - if .Execute in flags { pflags |= PROT_EXEC } - err := _mprotect(data, size, pflags) - return err == 0 + #assert(i32(posix.Prot_Flag_Bits.READ) == i32(Protect_Flag.Read)) + #assert(i32(posix.Prot_Flag_Bits.WRITE) == i32(Protect_Flag.Write)) + #assert(i32(posix.Prot_Flag_Bits.EXEC) == i32(Protect_Flag.Execute)) + + return posix.mprotect(data, size, transmute(posix.Prot_Flags)flags) == .OK } @@ -149,16 +53,11 @@ _platform_memory_init :: proc() { _map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) { - prot, mflags: c.int - if .Read in flags { - prot |= PROT_READ - } - if .Write in flags { - prot |= PROT_WRITE - } - mflags |= MAP_SHARED - addr := _mmap(nil, c.size_t(size), prot, mflags, i32(fd), 0) - if addr == nil { + #assert(i32(posix.Prot_Flag_Bits.READ) == i32(Map_File_Flag.Read)) + #assert(i32(posix.Prot_Flag_Bits.WRITE) == i32(Map_File_Flag.Write)) + + addr := posix.mmap(nil, uint(size), transmute(posix.Prot_Flags)flags, { .SHARED }, posix.FD(fd)) + if addr == posix.MAP_FAILED || addr == nil { return nil, .Map_Failure } return ([^]byte)(addr)[:size], nil diff --git a/core/os/dir_unix.odin b/core/os/dir_unix.odin index b472e89b7..b0e8e0732 100644 --- a/core/os/dir_unix.odin +++ b/core/os/dir_unix.odin @@ -1,6 +1,7 @@ //+build darwin, linux, netbsd, freebsd, openbsd package os +import "base:runtime" import "core:strings" @(require_results) diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin index 0644ca645..940c0294e 100644 --- a/core/os/os_darwin.odin +++ b/core/os/os_darwin.odin @@ -584,7 +584,7 @@ F_GETPATH :: 50 // return the full path of the fd foreign libc { @(link_name="__error") __error :: proc() -> ^c.int --- - @(link_name="open") _unix_open :: proc(path: cstring, flags: i32, #c_vararg args: ..any) -> Handle --- + @(link_name="open") _unix_open :: proc(path: cstring, flags: i32, mode: u16) -> Handle --- @(link_name="close") _unix_close :: proc(handle: Handle) -> c.int --- @(link_name="read") _unix_read :: proc(handle: Handle, buffer: rawptr, count: c.size_t) -> int --- @(link_name="write") _unix_write :: proc(handle: Handle, buffer: rawptr, count: c.size_t) -> int --- @@ -628,23 +628,23 @@ foreign libc { @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring --- @(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int --- @(link_name="mkdir") _unix_mkdir :: proc(buf: cstring, mode: u16) -> c.int --- - @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr --- + @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: [^]byte = nil) -> cstring --- @(link_name="strerror") _darwin_string_error :: proc(num : c.int) -> cstring --- @(link_name="sysctlbyname") _sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int --- - @(link_name="socket") _unix_socket :: proc(domain: int, type: int, protocol: int) -> int --- - @(link_name="listen") _unix_listen :: proc(socket: int, backlog: int) -> int --- - @(link_name="accept") _unix_accept :: proc(socket: int, addr: rawptr, addr_len: rawptr) -> int --- - @(link_name="connect") _unix_connect :: proc(socket: int, addr: rawptr, addr_len: socklen_t) -> int --- - @(link_name="bind") _unix_bind :: proc(socket: int, addr: rawptr, addr_len: socklen_t) -> int --- - @(link_name="setsockopt") _unix_setsockopt :: proc(socket: int, level: int, opt_name: int, opt_val: rawptr, opt_len: socklen_t) -> int --- - @(link_name="getsockopt") _unix_getsockopt :: proc(socket: int, level: int, opt_name: int, opt_val: rawptr, opt_len: socklen_t) -> int --- - @(link_name="recvfrom") _unix_recvfrom :: proc(socket: int, buffer: rawptr, buffer_len: c.size_t, flags: int, addr: rawptr, addr_len: ^socklen_t) -> c.ssize_t --- - @(link_name="recv") _unix_recv :: proc(socket: int, buffer: rawptr, buffer_len: c.size_t, flags: int) -> c.ssize_t --- - @(link_name="sendto") _unix_sendto :: proc(socket: int, buffer: rawptr, buffer_len: c.size_t, flags: int, addr: rawptr, addr_len: socklen_t) -> c.ssize_t --- - @(link_name="send") _unix_send :: proc(socket: int, buffer: rawptr, buffer_len: c.size_t, flags: int) -> c.ssize_t --- - @(link_name="shutdown") _unix_shutdown :: proc(socket: int, how: int) -> int --- + @(link_name="socket") _unix_socket :: proc(domain: c.int, type: c.int, protocol: c.int) -> c.int --- + @(link_name="listen") _unix_listen :: proc(socket: c.int, backlog: c.int) -> c.int --- + @(link_name="accept") _unix_accept :: proc(socket: c.int, addr: rawptr, addr_len: rawptr) -> c.int --- + @(link_name="connect") _unix_connect :: proc(socket: c.int, addr: rawptr, addr_len: socklen_t) -> c.int --- + @(link_name="bind") _unix_bind :: proc(socket: c.int, addr: rawptr, addr_len: socklen_t) -> c.int --- + @(link_name="setsockopt") _unix_setsockopt :: proc(socket: c.int, level: c.int, opt_name: c.int, opt_val: rawptr, opt_len: socklen_t) -> c.int --- + @(link_name="getsockopt") _unix_getsockopt :: proc(socket: c.int, level: c.int, opt_name: c.int, opt_val: rawptr, opt_len: ^socklen_t) -> c.int --- + @(link_name="recvfrom") _unix_recvfrom :: proc(socket: c.int, buffer: rawptr, buffer_len: c.size_t, flags: c.int, addr: rawptr, addr_len: ^socklen_t) -> c.ssize_t --- + @(link_name="recv") _unix_recv :: proc(socket: c.int, buffer: rawptr, buffer_len: c.size_t, flags: c.int) -> c.ssize_t --- + @(link_name="sendto") _unix_sendto :: proc(socket: c.int, buffer: rawptr, buffer_len: c.size_t, flags: c.int, addr: rawptr, addr_len: socklen_t) -> c.ssize_t --- + @(link_name="send") _unix_send :: proc(socket: c.int, buffer: rawptr, buffer_len: c.size_t, flags: c.int) -> c.ssize_t --- + @(link_name="shutdown") _unix_shutdown :: proc(socket: c.int, how: c.int) -> c.int --- @(link_name="getifaddrs") _getifaddrs :: proc(ifap: ^^ifaddrs) -> (c.int) --- @(link_name="freeifaddrs") _freeifaddrs :: proc(ifa: ^ifaddrs) --- @@ -661,9 +661,9 @@ when ODIN_ARCH != .arm64 { } foreign dl { - @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: int) -> rawptr --- + @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr --- @(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr --- - @(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> int --- + @(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int --- @(link_name="dlerror") _unix_dlerror :: proc() -> cstring --- } @@ -1040,10 +1040,9 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) { if path_ptr == nil { return "", get_last_error() } - defer _unix_free(path_ptr) + defer _unix_free(rawptr(path_ptr)) - path_cstr := cast(cstring)path_ptr - path = strings.clone(string(path_cstr)) + path = strings.clone(string(path_ptr)) return path, nil } @@ -1154,7 +1153,7 @@ current_thread_id :: proc "contextless" () -> int { dlopen :: proc(filename: string, flags: int) -> rawptr { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() cstr := strings.clone_to_cstring(filename, context.temp_allocator) - handle := _unix_dlopen(cstr, flags) + handle := _unix_dlopen(cstr, c.int(flags)) return handle } @(require_results) @@ -1208,26 +1207,24 @@ _alloc_command_line_arguments :: proc() -> []string { return res } -@(require_results) socket :: proc(domain: int, type: int, protocol: int) -> (Socket, Error) { - result := _unix_socket(domain, type, protocol) + result := _unix_socket(c.int(domain), c.int(type), c.int(protocol)) if result < 0 { return 0, get_last_error() } return Socket(result), nil } -@(require_results) connect :: proc(sd: Socket, addr: ^SOCKADDR, len: socklen_t) -> Error { - result := _unix_connect(int(sd), addr, len) + result := _unix_connect(c.int(sd), addr, len) if result < 0 { return get_last_error() } return nil } -bind :: proc(sd: Socket, addr: ^SOCKADDR, len: socklen_t) -> Error { - result := _unix_bind(int(sd), addr, len) +bind :: proc(sd: Socket, addr: ^SOCKADDR, len: socklen_t) -> (Error) { + result := _unix_bind(c.int(sd), addr, len) if result < 0 { return get_last_error() } @@ -1235,15 +1232,15 @@ bind :: proc(sd: Socket, addr: ^SOCKADDR, len: socklen_t) -> Error { } accept :: proc(sd: Socket, addr: ^SOCKADDR, len: rawptr) -> (Socket, Error) { - result := _unix_accept(int(sd), rawptr(addr), len) + result := _unix_accept(c.int(sd), rawptr(addr), len) if result < 0 { return 0, get_last_error() } return Socket(result), nil } -listen :: proc(sd: Socket, backlog: int) -> Error { - result := _unix_listen(int(sd), backlog) +listen :: proc(sd: Socket, backlog: int) -> (Error) { + result := _unix_listen(c.int(sd), c.int(backlog)) if result < 0 { return get_last_error() } @@ -1251,7 +1248,7 @@ listen :: proc(sd: Socket, backlog: int) -> Error { } setsockopt :: proc(sd: Socket, level: int, optname: int, optval: rawptr, optlen: socklen_t) -> Error { - result := _unix_setsockopt(int(sd), level, optname, optval, optlen) + result := _unix_setsockopt(c.int(sd), c.int(level), c.int(optname), optval, optlen) if result < 0 { return get_last_error() } @@ -1259,7 +1256,8 @@ setsockopt :: proc(sd: Socket, level: int, optname: int, optval: rawptr, optlen: } getsockopt :: proc(sd: Socket, level: int, optname: int, optval: rawptr, optlen: socklen_t) -> Error { - result := _unix_getsockopt(int(sd), level, optname, optval, optlen) + optlen := optlen + result := _unix_getsockopt(c.int(sd), c.int(level), c.int(optname), optval, &optlen) if result < 0 { return get_last_error() } @@ -1267,7 +1265,7 @@ getsockopt :: proc(sd: Socket, level: int, optname: int, optval: rawptr, optlen: } recvfrom :: proc(sd: Socket, data: []byte, flags: int, addr: ^SOCKADDR, addr_size: ^socklen_t) -> (u32, Error) { - result := _unix_recvfrom(int(sd), raw_data(data), len(data), flags, addr, addr_size) + result := _unix_recvfrom(c.int(sd), raw_data(data), len(data), c.int(flags), addr, addr_size) if result < 0 { return 0, get_last_error() } @@ -1275,7 +1273,7 @@ recvfrom :: proc(sd: Socket, data: []byte, flags: int, addr: ^SOCKADDR, addr_siz } recv :: proc(sd: Socket, data: []byte, flags: int) -> (u32, Error) { - result := _unix_recv(int(sd), raw_data(data), len(data), flags) + result := _unix_recv(c.int(sd), raw_data(data), len(data), c.int(flags)) if result < 0 { return 0, get_last_error() } @@ -1283,7 +1281,7 @@ recv :: proc(sd: Socket, data: []byte, flags: int) -> (u32, Error) { } sendto :: proc(sd: Socket, data: []u8, flags: int, addr: ^SOCKADDR, addrlen: socklen_t) -> (u32, Error) { - result := _unix_sendto(int(sd), raw_data(data), len(data), flags, addr, addrlen) + result := _unix_sendto(c.int(sd), raw_data(data), len(data), c.int(flags), addr, addrlen) if result < 0 { return 0, get_last_error() } @@ -1291,15 +1289,15 @@ sendto :: proc(sd: Socket, data: []u8, flags: int, addr: ^SOCKADDR, addrlen: soc } send :: proc(sd: Socket, data: []byte, flags: int) -> (u32, Error) { - result := _unix_send(int(sd), raw_data(data), len(data), 0) + result := _unix_send(c.int(sd), raw_data(data), len(data), 0) if result < 0 { return 0, get_last_error() } return u32(result), nil } -shutdown :: proc(sd: Socket, how: int) -> Error { - result := _unix_shutdown(int(sd), how) +shutdown :: proc(sd: Socket, how: int) -> (Error) { + result := _unix_shutdown(c.int(sd), c.int(how)) if result < 0 { return get_last_error() } diff --git a/core/os/os_freebsd.odin b/core/os/os_freebsd.odin index e37f9767b..ba7e7ccf3 100644 --- a/core/os/os_freebsd.odin +++ b/core/os/os_freebsd.odin @@ -371,7 +371,7 @@ F_KINFO :: 22 foreign libc { @(link_name="__error") __Error_location :: proc() -> ^c.int --- - @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle --- + @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.uint16_t) -> Handle --- @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int --- @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- @@ -402,7 +402,7 @@ foreign libc { @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr --- @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring --- - @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr --- + @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: [^]byte = nil) -> cstring --- @(link_name="sysctlbyname") _sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int --- @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! --- @@ -430,7 +430,7 @@ get_last_error :: proc "contextless" () -> Error { open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Error) { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() cstr := strings.clone_to_cstring(path, context.temp_allocator) - handle := _unix_open(cstr, c.int(flags), c.int(mode)) + handle := _unix_open(cstr, c.int(flags), u16(mode)) if handle == -1 { return INVALID_HANDLE, get_last_error() } @@ -786,10 +786,10 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) { if path_ptr == nil { return "", get_last_error() } - defer _unix_free(path_ptr) + defer _unix_free(rawptr(path_ptr)) - path = strings.clone(string(cstring(path_ptr))) + path = strings.clone(string(path_ptr)) return path, nil } diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index 78da32a57..0fcd1a21a 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -491,7 +491,7 @@ foreign libc { @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring --- @(link_name="putenv") _unix_putenv :: proc(cstring) -> c.int --- @(link_name="setenv") _unix_setenv :: proc(key: cstring, value: cstring, overwrite: c.int) -> c.int --- - @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr --- + @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: [^]byte = nil) -> cstring --- @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! --- } @@ -917,9 +917,9 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) { if path_ptr == nil { return "", get_last_error() } - defer _unix_free(path_ptr) + defer _unix_free(rawptr(path_ptr)) - path = strings.clone(string(cstring(path_ptr))) + path = strings.clone(string(path_ptr)) return path, nil } diff --git a/core/os/os_netbsd.odin b/core/os/os_netbsd.odin index ffaf4a5f8..fac1bc311 100644 --- a/core/os/os_netbsd.odin +++ b/core/os/os_netbsd.odin @@ -330,7 +330,7 @@ dev_t :: u64 ino_t :: u64 nlink_t :: u32 off_t :: i64 -mode_t :: u16 +mode_t :: u32 pid_t :: u32 uid_t :: u32 gid_t :: u32 @@ -454,7 +454,7 @@ foreign libc { @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr --- @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring --- - @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr --- + @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: [^]byte = nil) -> cstring --- @(link_name="sysctlbyname") _sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int --- @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! --- @@ -832,9 +832,9 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) { if path_ptr == nil { return "", get_last_error() } - defer _unix_free(path_ptr) + defer _unix_free(rawptr(path_ptr)) - path = strings.clone(string(cstring(path_ptr))) + path = strings.clone(string(path_ptr)) return path, nil } diff --git a/core/os/os_openbsd.odin b/core/os/os_openbsd.odin index a41637ea3..caae19e6e 100644 --- a/core/os/os_openbsd.odin +++ b/core/os/os_openbsd.odin @@ -379,7 +379,7 @@ foreign libc { @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr --- @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring --- - @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr --- + @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: [^]byte = nil) -> cstring --- @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! --- @@ -746,9 +746,9 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) { if path_ptr == nil { return "", get_last_error() } - defer _unix_free(path_ptr) + defer _unix_free(rawptr(path_ptr)) - path = strings.clone(string(cstring(path_ptr))) + path = strings.clone(string(path_ptr)) return path, nil } diff --git a/core/path/filepath/path_unix.odin b/core/path/filepath/path_unix.odin index b44a6a344..361bba49a 100644 --- a/core/path/filepath/path_unix.odin +++ b/core/path/filepath/path_unix.odin @@ -32,10 +32,9 @@ abs :: proc(path: string, allocator := context.allocator) -> (string, bool) { if path_ptr == nil { return "", __error()^ == 0 } - defer _unix_free(path_ptr) + defer _unix_free(rawptr(path_ptr)) - path_cstr := cstring(path_ptr) - path_str := strings.clone(string(path_cstr), allocator) + path_str := strings.clone(string(path_ptr), allocator) return path_str, true } @@ -52,7 +51,7 @@ join :: proc(elems: []string, allocator := context.allocator) -> string { @(private) foreign libc { - realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr --- + realpath :: proc(path: cstring, resolved_path: [^]byte = nil) -> cstring --- @(link_name="free") _unix_free :: proc(ptr: rawptr) --- } diff --git a/core/prof/spall/spall_unix.odin b/core/prof/spall/spall_unix.odin index 174b3a11b..fc05b8525 100644 --- a/core/prof/spall/spall_unix.odin +++ b/core/prof/spall/spall_unix.odin @@ -5,22 +5,7 @@ package spall // Only for types. import "core:os" -when ODIN_OS == .Darwin { - foreign import libc "system:System.framework" -} else { - foreign import libc "system:c" -} - -timespec :: struct { - tv_sec: i64, // seconds - tv_nsec: i64, // nanoseconds -} - -foreign libc { - __error :: proc() -> ^i32 --- - @(link_name="write") _unix_write :: proc(handle: os.Handle, buffer: rawptr, count: uint) -> int --- - @(link_name="clock_gettime") _unix_clock_gettime :: proc(clock_id: u64, timespec: ^timespec) -> i32 --- -} +import "core:sys/posix" MAX_RW :: 0x7fffffff @@ -32,7 +17,7 @@ _write :: proc "contextless" (fd: os.Handle, data: []byte) -> (n: int, err: os.E for n < len(data) { chunk := data[:min(len(data), MAX_RW)] - written := _unix_write(fd, raw_data(chunk), len(chunk)) + written := posix.write(posix.FD(fd), raw_data(chunk), len(chunk)) if written < 0 { return n, os.get_last_error() } @@ -42,11 +27,17 @@ _write :: proc "contextless" (fd: os.Handle, data: []byte) -> (n: int, err: os.E return n, nil } -CLOCK_MONOTONIC_RAW :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP. +// NOTE(tetra): "RAW" means: Not adjusted by NTP. +when ODIN_OS == .Darwin { + CLOCK :: posix.Clock(4) // CLOCK_MONOTONIC_RAW +} else { + // It looks like the BSDs don't have a CLOCK_MONOTONIC_RAW equivalent. + CLOCK :: posix.Clock.MONOTONIC +} @(no_instrumentation) _tick_now :: proc "contextless" () -> (ns: i64) { - t: timespec - _unix_clock_gettime(CLOCK_MONOTONIC_RAW, &t) - return t.tv_sec*1e9 + t.tv_nsec + t: posix.timespec + posix.clock_gettime(CLOCK, &t) + return i64(t.tv_sec)*1e9 + i64(t.tv_nsec) } diff --git a/core/sys/posix/README.md b/core/sys/posix/README.md new file mode 100644 index 000000000..4afe50d6a --- /dev/null +++ b/core/sys/posix/README.md @@ -0,0 +1,57 @@ +# POSIX + +defines bindings for most posix APIs. + +If a header is added, all of it must be implemented. + +Each platform must define the exact same symbols, different values are allowed, even structs with different non-standard fields. + +APIs part of extensions may be left out completely if one target doesn't implement it. + +APIs with a direct replacement in `core` might not be implemented. + +Macros are emulated with force inlined functions. + +Struct fields defined by the posix standard (and thus portable) are documented with `[PSX]`. + + +ADD A TEST FOR SIGINFO, one thread signalling and retrieving the signal out of siginfo or something. +ADD A TEST FOR wait.h +ADD A TEST FOR pthread. +ADDD A test for stat.h. +ADD A TEST FOR setjmp.h. +HAIKU. + +Unimplemented POSIX headers: + +- aio.h +- complex.h | See `core:c/libc` and our own complex types +- cpio.h +- ctype.h | See `core:c/libc` for most of it +- ndbm.h | Never seen or heard of it +- fenv.h +- float.h +- fmtmsg.h +- ftw.h +- semaphore.h | See `core:sync` +- inttypes.h | See `core:c` +- iso646.h | Impossible +- math.h | See `core:c/libc` +- mqueue.h | Targets don't seem to have implemented it +- regex.h | See `core:regex` +- search.h | Not useful in Odin +- spawn.h | Use `fork`, `execve`, etc. +- stdarg.h | See `core:c/libc` +- stdint.h | See `core:c` +- stropts.h +- syslog.h +- pthread.h | Only the actual threads API is bound, see `core:sync` for synchronization primitives +- string.h | Most of this is not useful in Odin, only a select few symbols are bound +- tar.h +- tgmath.h +- trace.h +- wchar.h +- wctype.h + +TODO: +- time.h | Docs diff --git a/core/sys/posix/arpa_inet.odin b/core/sys/posix/arpa_inet.odin new file mode 100644 index 000000000..7e950c4be --- /dev/null +++ b/core/sys/posix/arpa_inet.odin @@ -0,0 +1,58 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// arpa/inet.h - definitions for internet operations + +foreign lib { + // Use Odin's native big endian types `u32be` and `u16be` instead. + // htonl :: proc(c.uint32_t) -> c.uint32_t --- + // htons :: proc(c.uint16_t) -> c.uint16_t --- + // ntohl :: proc(c.uint32_t) -> c.uint32_t --- + // ntohs :: proc(c.uint16_t) -> c.uint16_t --- + + // Use of this function is problematic because -1 is a valid address (255.255.255.255). + // Avoid its use in favor of inet_aton(), inet_pton(3), or getaddrinfo(3) which provide a cleaner way to indicate error return. + // inet_addr :: proc(cstring) -> in_addr_t --- + + // Convert the Internet host address specified by in to a string in the Internet standard dot notation. + // + // NOTE: returns a static string overwritten by further calls. + // + // [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/inet_ntoa.html ]] + inet_ntoa :: proc(in_addr) -> cstring --- + + // Convert a numeric address into a text string suitable for presentation. + // + // Returns `nil` and sets `errno` on failure. + // + // [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/inet_ntop.html ]] + inet_ntop :: proc( + af: AF, // INET or INET6 + src: rawptr, // either ^in_addr or ^in_addr6 + dst: [^]byte, // use `INET_ADDRSTRLEN` or `INET6_ADDRSTRLEN` for minimum lengths + size: socklen_t, + ) -> cstring --- + + // Convert an address in its standard text presentation form into its numeric binary form. + // + // [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/inet_ntop.html ]] + inet_pton :: proc( + af: AF, // INET or INET6 + src: cstring, + dst: rawptr, // either ^in_addr or ^in_addr6 + size: socklen_t, // size_of(dst^) + ) -> pton_result --- +} + +pton_result :: enum c.int { + AFNOSUPPORT = -1, + INVALID = 0, + SUCCESS = 1, +} diff --git a/core/sys/posix/dirent.odin b/core/sys/posix/dirent.odin new file mode 100644 index 000000000..bbb5416c5 --- /dev/null +++ b/core/sys/posix/dirent.odin @@ -0,0 +1,205 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// dirent.h - format of directory entries + +foreign lib { + /* + can be used as the comparison function for the scandir() function to sort the directory entries, d1 and d2, into alphabetical order. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/scandir.html ]] + */ + @(link_name=LALPHASORT) + alphasort :: proc([^]^dirent, [^]^dirent) -> c.int --- + + /* + Scan the directory dir, calling the function referenced by sel on each directory entry. + + Example: + list: [^]^posix.dirent + ret := posix.scandir(#directory, &list, nil, posix.alphasort) + if ret < 0 { + panic(string(posix.strerror(posix.errno()))) + } + defer posix.free(list) + + entries := list[:ret] + for entry in entries { + log.info(entry) + posix.free(entry) + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/scandir.html ]] + */ + @(link_name=LSCANDIR) + scandir :: proc( + dir: cstring, + sel: ^[^]^dirent, + filter: proc "c" (^dirent) -> b32 = nil, + compar: proc "c" ([^]^dirent, [^]^dirent) -> c.int = alphasort, + ) -> c.int --- + + /* + Close the directory stream referred to by the argument dirp. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/closedir.html ]] + */ + closedir :: proc(dirp: DIR) -> result --- + + /* + Return a file descriptor referring to the same directory as the dirp argument. + + // TODO: this is a macro on NetBSD? + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dirfd.html ]] + */ + dirfd :: proc(dirp: DIR) -> FD --- + + /* + Equivalent to the opendir() function except that the directory is specified by a file descriptor + rather than by a name. + The file offset associated with the file descriptor at the time of the call determines + which entries are returned. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdopendir.html ]] + */ + @(link_name="fdopendir" + INODE_SUFFIX) + fdopendir :: proc(dirp: FD) -> DIR --- + + /* + Open a directory stream corresponding to the directory named by the dirname argument. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdopendir.html ]] + */ + @(link_name=LOPENDIR) + opendir :: proc(path: cstring) -> DIR --- + + /* + Returns a pointer to a structure representing the directory entry at the current position + in the directory stream specified by the argument dirp, and position the directory stream at + the next entry. + + Returns nil when the end is reached or an error occurred (which sets errno). + + Example: + posix.set_errno(.NONE) + entry := posix.readdir(dirp) + if entry == nil { + if errno := posix.errno(); errno != .NONE { + panic(string(posix.strerror(errno))) + } else { + fmt.println("end of directory stream") + } + } else { + fmt.println(entry) + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/readdir.html ]] + */ + @(link_name=LREADDIR) + readdir :: proc(dirp: DIR) -> ^dirent --- + + /* + Reset the position of the directory stream to which dirp refers to the beginning of the directory. + It shall also cause the directory stream to refer to the current state of the corresponding directory, + as a call to opendir() would have done. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/rewinddir.html ]] + */ + @(link_name="rewinddir" + INODE_SUFFIX) + rewinddir :: proc(dirp: DIR) --- + + /* + The seekdir() function shall set the position of the next readdir() operation on the directory + stream specified by dirp to the position specified by loc. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/seekdir.html ]] + */ + @(link_name="seekdir" + INODE_SUFFIX) + seekdir :: proc(dirp: DIR, loc: dir_loc) --- + + /* + The telldir() function shall obtain the current location associated with the directory stream + specified by dirp. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/telldir.html ]] + */ + @(link_name="telldir" + INODE_SUFFIX) + telldir :: proc(dirp: DIR) -> dir_loc --- + + // deprecated. + // readdir_r :: proc(DIR, ^dirent, ^^dirent) -> c.int --- +} + +DIR :: distinct rawptr + +dir_loc :: c.long + +// NOTE: `d_type` is not a POSIX standard field, but all targets we support add it. +D_Type :: enum c.uint8_t { + UNKNOWN = 0, + FIFO = 1, + CHR = 2, + DIR = 4, + BLK = 6, + REG = 8, + LNK = 10, + SOCK = 12, + WHT = 14, +} + +when ODIN_OS == .NetBSD { + @(private) LALPHASORT :: "__alphasort30" + @(private) LSCANDIR :: "__scandir30" + @(private) LOPENDIR :: "__opendir30" + @(private) LREADDIR :: "__readdir30" +} else { + @(private) LALPHASORT :: "alphasort" + INODE_SUFFIX + @(private) LSCANDIR :: "scandir" + INODE_SUFFIX + @(private) LOPENDIR :: "opendir" + INODE_SUFFIX + @(private) LREADDIR :: "readdir" + INODE_SUFFIX +} + +when ODIN_OS == .Darwin { + + dirent :: struct { + d_ino: ino_t, /* [PSX] file number of entry */ + d_seekoff: c.uint64_t, /* seek offset */ + d_reclen: c.uint16_t, /* length of this record */ + d_namelen: c.uint16_t, /* length of string in d_name */ + d_type: D_Type, /* file type */ + d_name: [1024]c.char `fmt:"s,0"`, /* [PSX] entry name */ + } + +} else when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD { + + dirent :: struct { + d_ino: ino_t, /* [PSX] file number of entry */ + d_off: off_t, /* directory offset of the next entry */ + d_reclen: c.uint16_t, /* length of this record */ + d_type: D_Type, /* file type */ + d_namelen: c.uint8_t, /* length of string in d_name */ + d_pad0: c.uint32_t, + d_name: [256]c.char `fmt:"s,0"`, /* [PSX] entry name */ + } + +} else when ODIN_OS == .NetBSD { + + dirent :: struct { + d_ino: ino_t, /* [PSX] file number of entry */ + d_reclen: c.uint16_t, /* length of this record */ + d_namelen: c.uint16_t, /* length of string in d_name */ + d_type: D_Type, /* file type */ + d_name: [512]c.char `fmt:"s,0"`, /* [PSX] entry name */ + } + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/dlfcn.odin b/core/sys/posix/dlfcn.odin new file mode 100644 index 000000000..0ee666e24 --- /dev/null +++ b/core/sys/posix/dlfcn.odin @@ -0,0 +1,115 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// dlfcn.h - dynamic linking + +foreign lib { + /* + inform the system that the object referenced by a handle returned from a previous dlopen() + invocation is no longer needed by the application. + + Returns: 0 on success, non-zero on failure (use dlerror() for more information) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dlclose.html ]] + */ + dlclose :: proc(handle: Symbol_Table) -> c.int --- + + /* + return a null-terminated character string (with no trailing ) that describes + the last error that occurred during dynamic linking processing. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dlerror.html ]] + */ + dlerror :: proc() -> cstring --- + + /* + Make the symbols (function identifiers and data object identifiers) in the executable object + file specified by file available to the calling program. + + Returns: a reference to the symbol table on success, nil on failure (use dlerror() for more information) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dlopen.html ]] + */ + dlopen :: proc(file: cstring, mode: RTLD_Flags) -> Symbol_Table --- + + /* + Obtain the address of a symbol (a function identifier or a data object identifier) + defined in the symbol table identified by the handle argument. + + Returns: the address of the matched symbol on success, nil on failure (use dlerror() for more information) + + Example: + handle := posix.dlopen("/usr/home/me/libfoo.so", posix.RTLD_LOCAL + { .RTLD_LAZY }) + defer posix.dlclose(handle) + + if handle == nil { + panic(string(posix.dlerror())) + } + + foo: proc(a, b: int) -> int + foo = auto_cast posix.dlsym(handle, "foo") + + if foo == nil { + panic(string(posix.dlerror())) + } + + fmt.printfln("foo(%v, %v) == %v", 1, 2, foo(1, 2)) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dlsym.html ]] + */ + dlsym :: proc(handle: Symbol_Table, name: cstring) -> rawptr --- +} + +RTLD_Flag_Bits :: enum c.int { + LAZY = log2(RTLD_LAZY), + NOW = log2(RTLD_NOW), + GLOBAL = log2(RTLD_GLOBAL), + + // NOTE: use with `posix.RTLD_LOCAL + { .OTHER_FLAG, .OTHER_FLAG }`, unfortunately can't be in + // this bit set enum because it is 0 on some platforms and a value on others. + // LOCAL = RTLD_LOCAL + + _MAX = 31, +} +RTLD_Flags :: bit_set[RTLD_Flag_Bits; c.int] + +Symbol_Table :: distinct rawptr + +when ODIN_OS == .Darwin { + + RTLD_LAZY :: 0x1 + RTLD_NOW :: 0x2 + _RTLD_LOCAL :: 0x4 + RTLD_GLOBAL :: 0x8 + + RTLD_LOCAL :: RTLD_Flags{RTLD_Flag_Bits(log2(_RTLD_LOCAL))} + +} else when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD { + + RTLD_LAZY :: 1 + RTLD_NOW :: 2 + _RTLD_LOCAL :: 0 + RTLD_GLOBAL :: 0x100 + + RTLD_LOCAL :: RTLD_Flags{} + +} else when ODIN_OS == .NetBSD { + + RTLD_LAZY :: 0x1 + RTLD_NOW :: 0x2 + _RTLD_LOCAL :: 0x200 + RTLD_GLOBAL :: 0x100 + + RTLD_LOCAL :: RTLD_Flags{RTLD_Flag_Bits(log2(_RTLD_LOCAL))} + +} else { + #panic("posix is unimplemented for the current target") +} + diff --git a/core/sys/posix/errno.odin b/core/sys/posix/errno.odin new file mode 100644 index 000000000..4ef10aadf --- /dev/null +++ b/core/sys/posix/errno.odin @@ -0,0 +1,373 @@ +package posix + +import "core:c" +import "core:c/libc" + +// errno.h - system error numbers + +EDOM :: libc.EDOM +EILSEQ :: libc.EILSEQ +ERANGE :: libc.ERANGE + +@(no_instrumentation) +get_errno :: #force_inline proc "contextless" () -> Errno { + return (^Errno)(libc.errno())^ +} + +set_errno :: #force_inline proc "contextless" (err: Errno) { + libc.errno()^ = i32(err) +} + +errno :: proc { + get_errno, + set_errno, +} + +Errno :: enum c.int { + NONE = 0, + EDOM = EDOM, + EILSEQ = EILSEQ, + ERANGE = ERANGE, + E2BIG = E2BIG, + EACCES = EACCES, + EADDRINUSE = EADDRINUSE, + EADDRNOTAVAIL = EADDRNOTAVAIL, + EAFNOSUPPORT = EAFNOSUPPORT, + EAGAIN = EAGAIN, + EALREADY = EALREADY, + EBADF = EBADF, + EBADMSG = EBADMSG, + EBUSY = EBUSY, + ECANCELED = ECANCELED, + ECHILD = ECHILD, + ECONNABORTED = ECONNABORTED, + ECONNREFUSED = ECONNREFUSED, + ECONNRESET = ECONNRESET, + EDEADLK = EDEADLK, + EDESTADDRREQ = EDESTADDRREQ, + EDQUOT = EDQUOT, + EEXIST = EEXIST, + EFAULT = EFAULT, + EFBIG = EFBIG, + EHOSTUNREACH = EHOSTUNREACH, + EIDRM = EIDRM, + EINPROGRESS = EINPROGRESS, + EINTR = EINTR, + EINVAL = EINVAL, + EIO = EIO, + EISCONN = EISCONN, + EISDIR = EISDIR, + ELOOP = ELOOP, + EMFILE = EMFILE, + EMLINK = EMLINK, + EMSGSIZE = EMSGSIZE, + EMULTIHOP = EMULTIHOP, + ENAMETOOLONG = ENAMETOOLONG, + ENETDOWN = ENETDOWN, + ENETRESET = ENETRESET, + ENETUNREACH = ENETUNREACH, + ENFILE = ENFILE, + ENOBUFS = ENOBUFS, + ENODATA = ENODATA, + ENODEV = ENODEV, + ENOENT = ENOENT, + ENOEXEC = ENOEXEC, + ENOLCK = ENOLCK, + ENOLINK = ENOLINK, + ENOMEM = ENOMEM, + ENOMSG = ENOMSG, + ENOPROTOOPT = ENOPROTOOPT, + ENOSPC = ENOSPC, + ENOSR = ENOSR, + ENOSTR = ENOSTR, + ENOSYS = ENOSYS, + ENOTCONN = ENOTCONN, + ENOTDIR = ENOTDIR, + ENOTEMPTY = ENOTEMPTY, + ENOTRECOVERABLE = ENOTRECOVERABLE, + ENOTSOCK = ENOTSOCK, + ENOTSUP = ENOTSUP, + ENOTTY = ENOTTY, + ENXIO = ENXIO, + EOPNOTSUPP = EOPNOTSUPP, + EOVERFLOW = EOVERFLOW, + EOWNERDEAD = EOWNERDEAD, + EPERM = EPERM, + EPIPE = EPIPE, + EPROTO = EPROTO, + EPROTONOSUPPORT = EPROTONOSUPPORT, + EPROTOTYPE = EPROTOTYPE, + EROFS = EROFS, + ESPIPE = ESPIPE, + ESRCH = ESRCH, + ESTALE = ESTALE, + ETIME = ETIME, + ETIMEDOUT = ETIMEDOUT, + ETXTBSY = ETXTBSY, + EWOULDBLOCK = EWOULDBLOCK, + EXDEV = EXDEV, +} + +when ODIN_OS == .Darwin { + EPERM :: 1 + ENOENT :: 2 + ESRCH :: 3 + EINTR :: 4 + EIO :: 5 + ENXIO :: 6 + E2BIG :: 7 + ENOEXEC :: 8 + EBADF :: 9 + ECHILD :: 10 + EDEADLK :: 11 + ENOMEM :: 12 + EACCES :: 13 + EFAULT :: 14 + EBUSY :: 16 + EEXIST :: 17 + EXDEV :: 18 + ENODEV :: 19 + ENOTDIR :: 20 + EISDIR :: 21 + EINVAL :: 22 + ENFILE :: 23 + EMFILE :: 24 + ENOTTY :: 25 + ETXTBSY :: 26 + EFBIG :: 27 + ENOSPC :: 28 + ESPIPE :: 29 + EROFS :: 30 + EMLINK :: 31 + EPIPE :: 32 + EAGAIN :: 35 + EWOULDBLOCK :: 35 + EINPROGRESS :: 36 + EALREADY :: 37 + ENOTSOCK :: 38 + EDESTADDRREQ :: 39 + EMSGSIZE :: 40 + EPROTOTYPE :: 41 + ENOPROTOOPT :: 42 + EPROTONOSUPPORT :: 43 + ENOTSUP :: 45 + EOPNOTSUPP :: 45 + EAFNOSUPPORT :: 47 + EADDRINUSE :: 48 + EADDRNOTAVAIL :: 49 + ENETDOWN :: 50 + ENETUNREACH :: 51 + ENETRESET :: 52 + ECONNABORTED :: 53 + ECONNRESET :: 54 + ENOBUFS :: 55 + EISCONN :: 56 + ENOTCONN :: 57 + ETIMEDOUT :: 60 + ECONNREFUSED :: 61 + ELOOP :: 62 + ENAMETOOLONG :: 63 + EHOSTUNREACH :: 65 + ENOTEMPTY :: 66 + EDQUOT :: 69 + ESTALE :: 70 + ENOLCK :: 77 + ENOSYS :: 78 + EOVERFLOW :: 84 + ECANCELED :: 89 + EIDRM :: 90 + ENOMSG :: 91 + EBADMSG :: 94 + EMULTIHOP :: 95 + ENODATA :: 96 + ENOLINK :: 97 + ENOSR :: 98 + ENOSTR :: 99 + EPROTO :: 100 + ETIME :: 101 + ENOTRECOVERABLE :: 104 + EOWNERDEAD :: 105 +} else when ODIN_OS == .FreeBSD { + EPERM :: 1 + ENOENT :: 2 + ESRCH :: 3 + EINTR :: 4 + EIO :: 5 + ENXIO :: 6 + E2BIG :: 7 + ENOEXEC :: 8 + EBADF :: 9 + ECHILD :: 10 + EDEADLK :: 11 + ENOMEM :: 12 + EACCES :: 13 + EFAULT :: 14 + EBUSY :: 16 + EEXIST :: 17 + EXDEV :: 18 + ENODEV :: 19 + ENOTDIR :: 20 + EISDIR :: 21 + EINVAL :: 22 + ENFILE :: 23 + EMFILE :: 24 + ENOTTY :: 25 + ETXTBSY :: 26 + EFBIG :: 27 + ENOSPC :: 28 + ESPIPE :: 29 + EROFS :: 30 + EMLINK :: 31 + EPIPE :: 32 + EAGAIN :: 35 + EWOULDBLOCK :: 35 + EINPROGRESS :: 36 + EALREADY :: 37 + ENOTSOCK :: 38 + EDESTADDRREQ :: 39 + EMSGSIZE :: 40 + EPROTOTYPE :: 41 + ENOPROTOOPT :: 42 + EPROTONOSUPPORT :: 43 + ENOTSUP :: 45 + EOPNOTSUPP :: 45 + EAFNOSUPPORT :: 47 + EADDRINUSE :: 48 + EADDRNOTAVAIL :: 49 + ENETDOWN :: 50 + ENETUNREACH :: 51 + ENETRESET :: 52 + ECONNABORTED :: 53 + ECONNRESET :: 54 + ENOBUFS :: 55 + EISCONN :: 56 + ENOTCONN :: 57 + ETIMEDOUT :: 60 + ECONNREFUSED :: 61 + ELOOP :: 62 + ENAMETOOLONG :: 63 + EHOSTUNREACH :: 65 + ENOTEMPTY :: 66 + EDQUOT :: 69 + ESTALE :: 70 + ENOLCK :: 77 + ENOSYS :: 78 + EOVERFLOW :: 84 + EIDRM :: 82 + ENOMSG :: 83 + ECANCELED :: 85 + EBADMSG :: 89 + EMULTIHOP :: 90 + ENOLINK :: 91 + EPROTO :: 92 + ENOTRECOVERABLE :: 95 + EOWNERDEAD :: 96 + + // NOTE: not defined for freebsd + ENODATA :: -1 + ENOSR :: -1 + ENOSTR :: -1 + ETIME :: -1 +} else when ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + EPERM :: 1 + ENOENT :: 2 + ESRCH :: 3 + EINTR :: 4 + EIO :: 5 + ENXIO :: 6 + E2BIG :: 7 + ENOEXEC :: 8 + EBADF :: 9 + ECHILD :: 10 + EDEADLK :: 11 + ENOMEM :: 12 + EACCES :: 13 + EFAULT :: 14 + EBUSY :: 16 + EEXIST :: 17 + EXDEV :: 18 + ENODEV :: 19 + ENOTDIR :: 20 + EISDIR :: 21 + EINVAL :: 22 + ENFILE :: 23 + EMFILE :: 24 + ENOTTY :: 25 + ETXTBSY :: 26 + EFBIG :: 27 + ENOSPC :: 28 + ESPIPE :: 29 + EROFS :: 30 + EMLINK :: 31 + EPIPE :: 32 + EAGAIN :: 35 + EWOULDBLOCK :: 35 + EINPROGRESS :: 36 + EALREADY :: 37 + ENOTSOCK :: 38 + EDESTADDRREQ :: 39 + EMSGSIZE :: 40 + EPROTOTYPE :: 41 + ENOPROTOOPT :: 42 + EPROTONOSUPPORT :: 43 + ENOTSUP :: 45 + EOPNOTSUPP :: 45 + EAFNOSUPPORT :: 47 + EADDRINUSE :: 48 + EADDRNOTAVAIL :: 49 + ENETDOWN :: 50 + ENETUNREACH :: 51 + ENETRESET :: 52 + ECONNABORTED :: 53 + ECONNRESET :: 54 + ENOBUFS :: 55 + EISCONN :: 56 + ENOTCONN :: 57 + ETIMEDOUT :: 60 + ECONNREFUSED :: 61 + ELOOP :: 62 + ENAMETOOLONG :: 63 + EHOSTUNREACH :: 65 + ENOTEMPTY :: 66 + EDQUOT :: 69 + ESTALE :: 70 + ENOLCK :: 77 + ENOSYS :: 78 + + when ODIN_OS == .NetBSD { + EOVERFLOW :: 84 + EIDRM :: 82 + ENOMSG :: 83 + ECANCELED :: 87 + EBADMSG :: 88 + ENODATA :: 89 + EMULTIHOP :: 94 + ENOLINK :: 95 + EPROTO :: 96 + ENOTRECOVERABLE :: 98 + EOWNERDEAD :: 97 + ENOSR :: 90 + ENOSTR :: 91 + ETIME :: 92 + } else { + EOVERFLOW :: 87 + EIDRM :: 89 + ENOMSG :: 90 + ECANCELED :: 88 + EBADMSG :: 92 + EPROTO :: 95 + ENOTRECOVERABLE :: 93 + EOWNERDEAD :: 94 + // NOTE: not defined for openbsd + ENODATA :: -1 + EMULTIHOP :: -1 + ENOLINK :: -1 + ENOSR :: -1 + ENOSTR :: -1 + ETIME :: -1 + } + +} else { + #panic("posix is unimplemented for the current target") +} + diff --git a/core/sys/posix/fcntl.odin b/core/sys/posix/fcntl.odin new file mode 100644 index 000000000..436104613 --- /dev/null +++ b/core/sys/posix/fcntl.odin @@ -0,0 +1,415 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// fcntl.h - file control options + +foreign lib { + /* + Implemented as `return open(path, O_WRONLY|O_CREAT|O_TRUNC, mode);` + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/creat.html ]] + */ + creat :: proc(path: cstring, mode: mode_t) -> FD --- + + /* + Perform the operations on open files. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fcntl.html ]] + */ + fcntl :: proc(fd: FD, cmd: FCNTL_Cmd, arg: rawptr = nil) -> c.int --- + + /* + Establish the connection between a file and a file descriptor. + It shall create an open file description that refers to a file and a file descriptor that + refers to that open file description. The file descriptor is used by other I/O functions to + refer to that file. + The path argument points to a pathname naming the file + + Returns: -1 on failure (setting errno), a file descriptor on success. + + Example: + // The following example opens the file /tmp/file, either by creating it (if it does not already exist), + // or by truncating its length to 0 (if it does exist). In the former case, if the call creates a new file, + // the access permission bits in the file mode of the file are set to permit reading and writing by the owner, + // and to permit reading only by group members and others. + fd := posix.open("/tmp/file", { .WRONLY, .CREAT, .TRUNC }, { .IRUSR, .IWUSR, .IRGRP, .IROTH }) + + // The following example uses the open() function to try to create the LOCKFILE file and open it for writing. + // Since the open() function specifies the O_EXCL flag, the call fails if the file already exists. + // In that case, the program assumes that someone else is updating the password file and exits. + fd := posix.open("/etc/ptmp", { .WRONLY, .CREAT, .EXCL }, { .IRUSR, .IWUSR, .IRGRP, .IROTH }) + if fd == -1 { + fmt.println("cannot open /etc/ptmp") + } + + // The following example opens a file for writing, creating the file if it does not already exist. + // If the file does exist, the system truncates the file to zero bytes. + fd := posix.open("/etc/ptmp", { .WRONLY, .CREAT, .TRUNC }, { .IRUSR, .IWUSR, .IRGRP, .IROTH }) + if fd == -1 { + fmt.println("cannot open output file") + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html ]] + */ + open :: proc(path: cstring, flags: O_Flags, mode: mode_t = {}) -> FD --- + + /* + Equivalent to the open() function except in the case where path specifies a relative path. + In this case the file to be opened is determined relative to the directory associated with the + file descriptor fd instead of the current working directory. + + Returns: -1 on failure (setting errno), a file descriptor on success. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html ]] + */ + openat :: proc(fd: FD, path: cstring, flags: O_Flags, mode: mode_t = {}) -> FD --- +} + +FCNTL_Cmd :: enum c.int { + DUPFD = F_DUPFD, + DUPFD_CLOEXEC = F_DUPFD_CLOEXEC, + GETFD = F_GETFD, + SETFD = F_SETFD, + GETFL = F_GETFL, + SETFL = F_SETFL, + GETLK = F_GETLK, + SETLK = F_SETLK, + SETLKW = F_SETLKW, + GETOWN = F_GETOWN, + SETOWN = F_SETOWN, +} + +Lock_Type :: enum c.short { + RDLCK = F_RDLCK, + UNLCK = F_UNLCK, + WRLCK = F_WRLCK, +} + +// Assertions made to unify this bit set. +#assert(O_RDONLY == 0) + +O_Flag_Bits :: enum c.int { + // Sets FD_CLOEXEC on the file descriptor. + CLOEXEC = log2(O_CLOEXEC), + // If not exists, combined with DIRECTORY will cause creation of a directory, otherwise a regular file. + CREAT = log2(O_CREAT), + // Fails if the opened descriptor would not be a directory. + DIRECTORY = log2(O_DIRECTORY), + // If combined with CREAT, causes a failure if the file already exists. + EXCL = log2(O_EXCL), + // If terminal device, do not make it the controlling terminal for the process. + NOCTTY = log2(O_NOCTTY), + // Don't follow symbolic links, fail with errno ELOOP. + NOFOLLOW = log2(O_NOFOLOW), + // If exists and regular, truncate the length to 0. + TRUNC = log2(O_TRUNC), + + // NOTE: use with `posix.O_TTY_INIT + { .OTHER_FLAG, .OTHER_FLAG }`, unfortunately can't be in + // this bit set enum because it is 0 on some platforms and a value on others. + // TTY_INIT = O_TTY_INIT, + + // Set file offset to end of file prior to each write. + APPEND = log2(O_APPEND), + // Write I/O shall complete as defined by synchronized I/O data integrity completion. + DSYNC = log2(O_DSYNC), + // Causes nonblocking behaviour in various situations. + NONBLOCK = log2(O_NONBLOCK), + // Write I/O shall complete as defined by synchronized I/O file integrity completion. + SYNC = log2(O_SYNC), + // NOTE: use with `posix.O_RSYNC + { .OTHER_FLAG, .OTHER_FLAG }`, unfortunately can't be in + // this bit set enum because it is 0 on some platforms and a value on others. + // RSYNC = O_RSYNC, + + // Execute only. + EXEC = log2(O_EXEC), + // Reading and writing. + RDWR = log2(O_RDWR), + // Writing only. + WRONLY = log2(O_WRONLY), + // Reading only. + // RDONLY = 0, // Default + +} +O_Flags :: bit_set[O_Flag_Bits; c.int] + +// A mask of all the access mode bits. +O_ACCMODE :: O_Flags{ .EXEC, .RDWR, .WRONLY } + +AT_Flag_Bits :: enum c.int { + EACCESS = log2(AT_EACCESS), + SYMLINK_NOFOLLOW = log2(AT_SYMLINK_NOFOLLOW), + SYMLINK_FOLLOW = log2(AT_SYMLINK_FOLLOW), + REMOVEDIR = log2(AT_REMOVEDIR), +} +AT_Flags :: bit_set[AT_Flag_Bits; c.int] + +when ODIN_OS == .Darwin { + + off_t :: distinct c.int64_t + pid_t :: distinct c.int32_t + + F_DUPFD :: 0 + F_DUPFD_CLOEXEC :: 67 + F_GETFD :: 1 + F_SETFD :: 2 + F_GETFL :: 3 + F_SETFL :: 4 + F_GETLK :: 7 + F_SETLK :: 8 + F_SETLKW :: 9 + F_GETOWN :: 5 + F_SETOWN :: 6 + + FD_CLOEXEC :: 1 + + F_RDLCK :: 1 + F_UNLCK :: 2 + F_WRLCK :: 3 + + O_CLOEXEC :: 0x01000000 + O_CREAT :: 0x00000200 + O_DIRECTORY :: 0x00100000 + O_EXCL :: 0x00000800 + O_NOCTTY :: 0x00020000 + O_NOFOLOW :: 0x00000100 + O_TRUNC :: 0x00000400 + + _O_TTY_INIT :: 0 + O_TTY_INIT :: O_Flags{} + + O_APPEND :: 0x00000008 + O_DSYNC :: 0x00400000 + O_NONBLOCK :: 0x00000004 + O_SYNC :: 0x0080 + + _O_RSYNC :: 0 + O_RSYNC :: O_Flags{} + + O_EXEC :: 0x40000000 + O_RDONLY :: 0 + O_RDWR :: 0x0002 + O_WRONLY :: 0x0001 + + _O_SEARCH :: O_EXEC | O_DIRECTORY + O_SEARCH :: O_Flags{ .EXEC, .DIRECTORY } + + AT_FDCWD: FD: -2 + + AT_EACCESS :: 0x0010 + AT_SYMLINK_NOFOLLOW :: 0x0020 + AT_SYMLINK_FOLLOW :: 0x0040 + AT_REMOVEDIR :: 0x0080 + + flock :: struct { + l_start: off_t, /* [PSX] relative offset in bytes */ + l_len: off_t, /* [PSX] size; if 0 then until EOF */ + l_pid: pid_t, /* [PSX] process ID of the process holding the lock */ + l_type: Lock_Type, /* [PSX] type of lock */ + l_whence: c.short, /* [PSX] flag (Whence) of starting offset */ + } + +} else when ODIN_OS == .FreeBSD { + + off_t :: distinct c.int64_t + pid_t :: distinct c.int32_t + + F_DUPFD :: 0 + F_DUPFD_CLOEXEC :: 17 + F_GETFD :: 1 + F_SETFD :: 2 + F_GETFL :: 3 + F_SETFL :: 4 + F_GETLK :: 7 + F_SETLK :: 8 + F_SETLKW :: 9 + F_GETOWN :: 5 + F_SETOWN :: 6 + + FD_CLOEXEC :: 1 + + F_RDLCK :: 1 + F_UNLCK :: 2 + F_WRLCK :: 3 + + O_CLOEXEC :: 0x00100000 + O_CREAT :: 0x0200 + O_DIRECTORY :: 0x00020000 + O_EXCL :: 0x0800 + O_NOCTTY :: 0x8000 + O_NOFOLOW :: 0x0100 + O_TRUNC :: 0x0400 + + _O_TTY_INIT :: 0x00080000 + O_TTY_INIT :: O_Flags{O_Flag_Bits(log2(_O_TTY_INIT))} + + O_APPEND :: 0x0008 + O_DSYNC :: 0x01000000 + O_NONBLOCK :: 0x0004 + O_SYNC :: 0x0080 + _O_RSYNC :: 0 + O_RSYNC :: O_Flags{} + + O_EXEC :: 0x00040000 + O_RDONLY :: 0 + O_RDWR :: 0x0002 + O_WRONLY :: 0x0001 + + _O_SEARCH :: O_EXEC|O_DIRECTORY + O_SEARCH :: O_Flags{ .EXEC, .DIRECTORY } + + AT_FDCWD: FD: -100 + + AT_EACCESS :: 0x0100 + AT_SYMLINK_NOFOLLOW :: 0x0200 + AT_SYMLINK_FOLLOW :: 0x0400 + AT_REMOVEDIR :: 0x0800 + + flock :: struct { + l_start: off_t, /* [PSX] relative offset in bytes */ + l_len: off_t, /* [PSX] size; if 0 then until EOF */ + l_pid: pid_t, /* [PSX] process ID of the process holding the lock */ + l_type: Lock_Type, /* [PSX] type of lock */ + l_whence: c.short, /* [PSX] flag (Whence) of starting offset */ + l_sysid: c.int, + } + +} else when ODIN_OS == .NetBSD { + + off_t :: distinct c.int64_t + pid_t :: distinct c.int32_t + + F_DUPFD :: 0 + F_DUPFD_CLOEXEC :: 12 + F_GETFD :: 1 + F_SETFD :: 2 + F_GETFL :: 3 + F_SETFL :: 4 + F_GETLK :: 7 + F_SETLK :: 8 + F_SETLKW :: 9 + F_GETOWN :: 5 + F_SETOWN :: 6 + + FD_CLOEXEC :: 1 + + F_RDLCK :: 1 + F_UNLCK :: 2 + F_WRLCK :: 3 + + O_CLOEXEC :: 0x00400000 + O_CREAT :: 0x0200 + O_DIRECTORY :: 0x0020000 + O_EXCL :: 0x0800 + O_NOCTTY :: 0x8000 + O_NOFOLOW :: 0x0100 + O_TRUNC :: 0x0400 + + _O_TTY_INIT :: 0 + O_TTY_INIT :: O_Flags{} // NOTE: not defined in the headers + + O_APPEND :: 0x0008 + O_DSYNC :: 0x010000 + O_NONBLOCK :: 0x0004 + O_SYNC :: 0x0080 + + _O_RSYNC :: 0x0002 + O_RSYNC :: O_Flags{O_Flag_Bits(log2(_O_RSYNC))} + + + O_EXEC :: 0x04000000 + O_RDONLY :: 0 + O_RDWR :: 0x0002 + O_WRONLY :: 0x0001 + + _O_SEARCH :: 0x00800000 + O_SEARCH :: O_Flags{O_Flag_Bits(log2(_O_SEARCH))} + + AT_FDCWD: FD: -100 + + AT_EACCESS :: 0x100 + AT_SYMLINK_NOFOLLOW :: 0x200 + AT_SYMLINK_FOLLOW :: 0x400 + AT_REMOVEDIR :: 0x800 + + flock :: struct { + l_start: off_t, /* [PSX] relative offset in bytes */ + l_len: off_t, /* [PSX] size; if 0 then until EOF */ + l_pid: pid_t, /* [PSX] process ID of the process holding the lock */ + l_type: Lock_Type, /* [PSX] type of lock */ + l_whence: c.short, /* [PSX] flag (Whence) of starting offset */ + } +} else when ODIN_OS == .OpenBSD { + + off_t :: distinct c.int64_t + pid_t :: distinct c.int32_t + + F_DUPFD :: 0 + F_DUPFD_CLOEXEC :: 10 + F_GETFD :: 1 + F_SETFD :: 2 + F_GETFL :: 3 + F_SETFL :: 4 + F_GETLK :: 7 + F_SETLK :: 8 + F_SETLKW :: 9 + F_GETOWN :: 5 + F_SETOWN :: 6 + + FD_CLOEXEC :: 1 + + F_RDLCK :: 1 + F_UNLCK :: 2 + F_WRLCK :: 3 + + O_CLOEXEC :: 0x10000 + O_CREAT :: 0x0200 + O_DIRECTORY :: 0x20000 + O_EXCL :: 0x0800 + O_NOCTTY :: 0x8000 + O_NOFOLOW :: 0x0100 + O_TRUNC :: 0x0400 + + _O_TTY_INIT :: 0 + O_TTY_INIT :: O_Flags{} // NOTE: not defined in the headers + + O_APPEND :: 0x0008 + O_DSYNC :: 0x010000 + O_NONBLOCK :: 0x0004 + O_SYNC :: 0x0080 + + _O_RSYNC :: O_SYNC + O_RSYNC :: O_Flags{ .SYNC } + + O_EXEC :: 0x04000000 // NOTE: not defined in the headers + O_RDONLY :: 0 + O_RDWR :: 0x0002 + O_WRONLY :: 0x0001 + + _O_SEARCH :: 0 + O_SEARCH :: O_Flags{} // NOTE: not defined in the headers + + AT_FDCWD: FD: -100 + + AT_EACCESS :: 0x01 + AT_SYMLINK_NOFOLLOW :: 0x02 + AT_SYMLINK_FOLLOW :: 0x04 + AT_REMOVEDIR :: 0x08 + + flock :: struct { + l_start: off_t, /* [PSX] relative offset in bytes */ + l_len: off_t, /* [PSX] size; if 0 then until EOF */ + l_pid: pid_t, /* [PSX] process ID of the process holding the lock */ + l_type: Lock_Type, /* [PSX] type of lock */ + l_whence: c.short, /* [PSX] flag (Whence) of starting offset */ + } + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/fnmatch.odin b/core/sys/posix/fnmatch.odin new file mode 100644 index 000000000..9e54972e7 --- /dev/null +++ b/core/sys/posix/fnmatch.odin @@ -0,0 +1,58 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// fnmatch.h - filename-matching types + +foreign lib { + /* + Match patterns as described in XCU [[ Patterns Matching a Single Character; https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13_01 ]] + // and [[ Patterns Matching Multiple Characters; https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13_02 ]]. + It checks the string specified by the string argument to see if it matches the pattern specified by the pattern argument. + + Returns: 0 when matched. if there is no match, fnmatch() shall return FNM_NOMATCH. Non-zero on other errors. + + Example: + assert(posix.fnmatch("*.odin", "foo.odin", {}) == 0) + assert(posix.fnmatch("*.txt", "foo.odin", {}) == posix.FNM_NOMATCH) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fnmatch.html ]] + */ + fnmatch :: proc(pattern: cstring, string: cstring, flags: FNM_Flags) -> c.int --- +} + +FNM_Flag_Bits :: enum c.int { + // A character ( '/' ) in string shall be explicitly matched by a in pattern; + // it shall not be matched by either the or special characters, + // nor by a bracket expression. + PATHNAME = log2(FNM_PATHNAME), + + // A leading ( '.' ) in string shall match a in pattern; + // as described by rule 2 in XCU [[ Patterns Used for Filename Expansion; https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13_03 ]] + // where the location of "leading" is indicated by the value of PATHNAME: + // 1. If PATHNAME is set, a is "leading" if it is the first character in string or if it immediately follows a . + // 2. If PATHNAME is not set, a is "leading" only if it is the first character of string. + PERIOD = log2(FNM_PERIOD), + + // A character shall be treated as an ordinary character. + NOESCAPE = log2(FNM_NOESCAPE), +} +FNM_Flags :: bit_set[FNM_Flag_Bits; c.int] + +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + + FNM_NOMATCH :: 1 + + FNM_PATHNAME :: 0x02 + FNM_PERIOD :: 0x04 + FNM_NOESCAPE :: 0x01 + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/glob.odin b/core/sys/posix/glob.odin new file mode 100644 index 000000000..4f41d83db --- /dev/null +++ b/core/sys/posix/glob.odin @@ -0,0 +1,179 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// glob.h - pathname pattern-matching types + +foreign lib { + /* + The glob() function is a pathname generator that shall implement the rules defined in + [[ XCU Pattern Matching Notation; https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13 ]], + with optional support for rule 3 in XCU [[ Patterns Used for Filename Expansion; https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13_03 ]]. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/glob.html ]] + */ + @(link_name=LGLOB) + glob :: proc( + pattern: cstring, + flags: Glob_Flags, + errfunc: proc "c" (epath: cstring, eerrno: Errno) -> b32 = nil, // Return `true` to abort the glob(). + pglob: ^glob_t, + ) -> Glob_Result --- + + /* + Free the glob results. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/glob.html ]] + */ + @(link_name=LGLOBFREE) + globfree :: proc(^glob_t) --- +} + +Glob_Flag_Bits :: enum c.int { + // Append pathnames generated to the ones from a previous call to glob(). + APPEND = log2(GLOB_APPEND), + // Make use of pglob->gl_offs. If this flag is set, pglob->gl_offs is used to specify how many null pointers to add to the beginning of pglob->gl_pathv. + // In other words, pglob->gl_pathv shall point to pglob->gl_offs null pointers, followed by pglob->gl_pathc pathname pointers, followed by a null pointer. + DOOFFS = log2(GLOB_DOOFFS), + // Cause glob() to return when it encounters a directory that it cannot open or read. Ordinarily, + // glob() continues to find matches. + ERR = log2(GLOB_ERR), + // Each pathname that is a directory that matches pattern shall have a appended. + MARK = log2(GLOB_MARK), + // Supports rule 3 in [[ XCU Patterns Used for Filename Expansion; https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13_03 ]]. + // If pattern does not match any pathname, then glob() shall return a list consisting of only pattern, + // and the number of matched pathnames is 1. + NOCHECK = log2(GLOB_NOCHECK), + // Disable backslash escaping. + NOESCAPE = log2(GLOB_NOESCAPE), + // Ordinarily, glob() sorts the matching pathnames according to the current setting of the + // LC_COLLATE category; see XBD LC_COLLATE. When this flag is used, + // the order of pathnames returned is unspecified. + NOSORT = log2(GLOB_NOSORT), +} +Glob_Flags :: bit_set[Glob_Flag_Bits; c.int] + +Glob_Result :: enum c.int { + SUCCESS = 0, + ABORTED = GLOB_ABORTED, + NOMATCH = GLOB_NOMATCH, + NOSPACE = GLOB_NOSPACE, +} + +when ODIN_OS == .NetBSD { + @(private) LGLOB :: "__glob30" + @(private) LGLOBFREE :: "__globfree30" +} else { + @(private) LGLOB :: "glob" + INODE_SUFFIX + @(private) LGLOBFREE :: "globfree" +} + +when ODIN_OS == .Darwin { + + glob_t :: struct { + gl_pathc: c.size_t, /* [PSX] count of paths matched by pattern */ + gl_matchc: c.int, /* count of paths matching pattern */ + gl_offs: c.size_t, /* [PSX] slots to reserve at the beginning of gl_pathv */ + gl_flags: Glob_Flags, /* copy of flags parameter to glob */ + gl_pathv: [^]cstring `fmt:"v,gl_pathc"`, /* [PSX] pointer to list of matched pathnames */ + + // Non-standard alternate file system access functions: + + using _: struct #raw_union { + gl_errfunc: proc "c" (cstring, c.int) -> c.int, + gl_errblk: proc "c" (cstring, c.int) -> c.int, + }, + gl_closedir: proc "c" (dirp: DIR), + gl_readdir: proc "c" (dirp: DIR) -> ^dirent, + gl_opendir: proc "c" (path: cstring) -> DIR, + gl_lstat: proc "c" (path: cstring, buf: ^stat_t) -> result, + gl_stat: proc "c" (path: cstring, buf: ^stat_t) -> result, + } + + GLOB_APPEND :: 0x0001 + GLOB_DOOFFS :: 0x0002 + GLOB_ERR :: 0x0004 + GLOB_MARK :: 0x0008 + GLOB_NOCHECK :: 0x0010 + GLOB_NOESCAPE :: 0x2000 + GLOB_NOSORT :: 0x0020 + + GLOB_ABORTED :: -2 + GLOB_NOMATCH :: -3 + GLOB_NOSPACE :: -1 + +} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD { + + glob_t :: struct { + gl_pathc: c.size_t, /* [PSX] count of paths matched by pattern */ + gl_matchc: c.size_t, /* count of paths matching pattern */ + gl_offs: c.size_t, /* [PSX] slots to reserve at the beginning of gl_pathv */ + gl_flags: Glob_Flags, /* copy of flags parameter to glob */ + gl_pathv: [^]cstring `fmt:"v,gl_pathc"`, /* [PSX] pointer to list of matched pathnames */ + + // Non-standard alternate file system access functions: + + gl_errfunc: proc "c" (cstring, c.int) -> c.int, + + gl_closedir: proc "c" (dirp: DIR), + gl_readdir: proc "c" (dirp: DIR) -> ^dirent, + gl_opendir: proc "c" (path: cstring) -> DIR, + gl_lstat: proc "c" (path: cstring, buf: ^stat_t) -> result, + gl_stat: proc "c" (path: cstring, buf: ^stat_t) -> result, + } + + GLOB_APPEND :: 0x0001 + GLOB_DOOFFS :: 0x0002 + GLOB_ERR :: 0x0004 + GLOB_MARK :: 0x0008 + GLOB_NOCHECK :: 0x0010 + GLOB_NOESCAPE :: 0x2000 when ODIN_OS == .FreeBSD else 0x0100 + GLOB_NOSORT :: 0x0020 + + GLOB_ABORTED :: -2 + GLOB_NOMATCH :: -3 + GLOB_NOSPACE :: -1 + +} else when ODIN_OS == .OpenBSD { + + glob_t :: struct { + gl_pathc: c.size_t, /* [PSX] count of paths matched by pattern */ + gl_matchc: c.size_t, /* count of paths matching pattern */ + gl_offs: c.size_t, /* [PSX] slots to reserve at the beginning of gl_pathv */ + gl_flags: Glob_Flags, /* copy of flags parameter to glob */ + gl_pathv: [^]cstring `fmt:"v,gl_pathc"`, /* [PSX] pointer to list of matched pathnames */ + + gl_statv: [^]stat_t, + + // Non-standard alternate file system access functions: + + gl_errfunc: proc "c" (cstring, c.int) -> c.int, + + gl_closedir: proc "c" (dirp: DIR), + gl_readdir: proc "c" (dirp: DIR) -> ^dirent, + gl_opendir: proc "c" (path: cstring) -> DIR, + gl_lstat: proc "c" (path: cstring, buf: ^stat_t) -> result, + gl_stat: proc "c" (path: cstring, buf: ^stat_t) -> result, + } + + GLOB_APPEND :: 0x0001 + GLOB_DOOFFS :: 0x0002 + GLOB_ERR :: 0x0004 + GLOB_MARK :: 0x0008 + GLOB_NOCHECK :: 0x0010 + GLOB_NOESCAPE :: 0x1000 + GLOB_NOSORT :: 0x0020 + + GLOB_ABORTED :: -2 + GLOB_NOMATCH :: -3 + GLOB_NOSPACE :: -1 + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/grp.odin b/core/sys/posix/grp.odin new file mode 100644 index 000000000..c8a39de6a --- /dev/null +++ b/core/sys/posix/grp.odin @@ -0,0 +1,130 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// grp.h - group structure + +foreign lib { + /* + Closes the group database. + + Checking status would be done by setting errno to 0, calling this, and checking errno. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/endgrent.html ]] + */ + endgrent :: proc() --- + + /* + Rewinds the group database so getgrent() returns the first entry again. + + Checking status would be done by setting errno to 0, calling this, and checking errno. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/endgrent.html ]] + */ + setgrent :: proc() --- + + /* + Returns a pointer to an entry of the group database. + + Opens the group database if it isn't. + + Returns: nil on failure (setting errno) or EOF (not setting errno), the entry otherwise + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/endgrent.html ]] + */ + getgrent :: proc() -> ^group --- + + /* + Searches for an entry with a matching gid in the group database. + + Returns: nil (setting errno) on failure, a pointer to the entry on success + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getgrgid.html ]] + */ + getgrgid :: proc(gid: gid_t) -> ^group --- + + /* + Searches for an entry with a matching gid in the group database. + + Updates grp with the matching entry and stores it (or a nil pointer (setting errno)) into result. + + Strings are allocated into the given buffer, you can call `sysconf(._GETGR_R_SIZE_MAX)` for an appropriate size. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getgrgid.html ]] + */ + getgrgid_r :: proc(gid: gid_t, grp: ^group, buffer: [^]byte, bufsize: c.size_t, result: ^^group) -> Errno --- + + /* + Searches for an entry with a matching gid in the group database. + + Returns: nil (setting errno) on failure, a pointer to the entry on success + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getgrnam.html ]] + */ + getgrnam :: proc(name: cstring) -> ^group --- + + /* + Searches for an entry with a matching gid in the group database. + + Updates grp with the matching entry and stores it (or a nil pointer (setting errno)) into result. + + Strings are allocated into the given buffer, you can call `sysconf(._GETGR_R_SIZE_MAX)` for an appropriate size. + + Example: + length := posix.sysconf(._GETGR_R_SIZE_MAX) + if length == -1 { + length = 1024 + } + + result: posix.group + resultp: ^posix.group + + e: posix.Errno + + buffer: [dynamic]byte + defer delete(buffer) + + for { + mem_err := resize(&buffer, length) + assert(mem_err == nil) + + e = posix.getgrnam_r("nobody", &result, raw_data(buffer), len(buffer), &resultp) + if e != .ERANGE { + break + } + + length *= 2 + assert(length > 0) + } + + if e != .NONE { + panic(string(posix.strerror(e))) + } + + fmt.println(result) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getgrnam.html ]] + */ + getgrnam_r :: proc(name: cstring, grp: ^group, buffer: [^]byte, bufsize: c.size_t, result: ^^group) -> Errno --- +} + +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + + gid_t :: distinct c.uint32_t + + group :: struct { + gr_name: cstring, /* [PSX] group name */ + gr_passwd: cstring, /* group password */ + gr_gid: gid_t, /* [PSX] group id */ + gr_mem: [^]cstring, /* [PSX] group members */ + } + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/iconv.odin b/core/sys/posix/iconv.odin new file mode 100644 index 000000000..59248890f --- /dev/null +++ b/core/sys/posix/iconv.odin @@ -0,0 +1,50 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + // NOTE: iconv is in a different library + foreign import lib "system:iconv" +} else { + foreign import lib "system:c" +} + +// iconv.h - codeset conversion facility + +iconv_t :: distinct rawptr + +foreign lib { + /* + Convert the sequence of characters from one codeset, in the array specified by inbuf, + into a sequence of corresponding characters in another codeset, in the array specified by outbuf. + + Returns: -1 (setting errno) on failure, the number of non-identical conversions performed on success + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/iconv.html ]] + */ + iconv :: proc( + cd: iconv_t, + inbuf: ^[^]byte, + inbytesleft: ^c.size_t, + outbuf: ^[^]byte, + outbyteslen: ^c.size_t, + ) -> c.size_t --- + + /* + Deallocates the conversion descriptor cd and all other associated resources allocated by iconv_open(). + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/iconv_close.html ]] + */ + iconv_close :: proc(cd: iconv_t) -> result --- + + /* + Returns a conversion descriptor that describes a conversion from the codeset specified by the + string pointed to by the fromcode argument to the codeset specified by the string pointed to by + the tocode argument. + + Returns: -1 (setting errno) on failure, a conversion descriptor on success + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/iconv_open.html ]] + */ + iconv_open :: proc(tocode: cstring, fromcode: cstring) -> iconv_t --- +} diff --git a/core/sys/posix/langinfo.odin b/core/sys/posix/langinfo.odin new file mode 100644 index 000000000..24ecc917a --- /dev/null +++ b/core/sys/posix/langinfo.odin @@ -0,0 +1,285 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// langinfo.h - language information constants + +foreign lib { + /* + Return a pointer to a string containing information relevant to the particular language or + cultural area defined in the current locale. + + Returns: a string that should not be freed or modified, and that can be invalidated at any time later + + Example: + for item in posix.nl_item { + fmt.printfln("%v: %q", item, posix.nl_langinfo(item)) + } + + Possible Output: + CODESET: "US-ASCII" + D_T_FMT: "%a %b %e %H:%M:%S %Y" + D_FMT: "%m/%d/%y" + T_FMT: "%H:%M:%S" + T_FMT_AMPM: "%I:%M:%S %p" + AM_STR: "AM" + PM_STR: "PM" + DAY_1: "Sunday" + DAY_2: "Monday" + DAY_3: "Tuesday" + DAY_4: "Wednesday" + DAY_5: "Thursday" + DAY_6: "Friday" + DAY_7: "Saturday" + ABDAY_1: "Sun" + ABDAY_2: "Mon" + ABDAY_3: "Tue" + ABDAY_4: "Wed" + ABDAY_5: "Thu" + ABDAY_6: "Fri" + ABDAY_7: "Sat" + MON_1: "January" + MON_2: "February" + MON_3: "March" + MON_4: "April" + MON_5: "May" + MON_6: "June" + MON_7: "July" + MON_8: "August" + MON_9: "September" + MON_10: "October" + MON_11: "November" + MON_12: "December" + ABMON_1: "Jan" + ABMON_2: "Feb" + ABMON_3: "Mar" + ABMON_4: "Apr" + ABMON_5: "May" + ABMON_6: "Jun" + ABMON_7: "Jul" + ABMON_8: "Aug" + ABMON_9: "Sep" + ABMON_10: "Oct" + ABMON_11: "Nov" + ABMON_12: "Dec" + ERA: "" + ERA_D_FMT: "" + ERA_D_T_FMT: "" + ERA_T_FMT: "" + ALT_DIGITS: "" + RADIXCHAR: "." + THOUSEP: "" + YESEXPR: "^[yY]" + NOEXPR: "^[nN]" + CRNCYSTR: "" + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/nl_langinfo.html ]] + */ + nl_langinfo :: proc(nl_item) -> cstring --- +} + +nl_item :: enum nl_item_t { + CODESET = CODESET, + D_T_FMT = D_T_FMT, + D_FMT = D_FMT, + T_FMT = T_FMT, + T_FMT_AMPM = T_FMT_AMPM, + AM_STR = AM_STR, + PM_STR = PM_STR, + DAY_1 = DAY_1, + DAY_2 = DAY_2, + DAY_3 = DAY_3, + DAY_4 = DAY_4, + DAY_5 = DAY_5, + DAY_6 = DAY_6, + DAY_7 = DAY_7, + ABDAY_1 = ABDAY_1, + ABDAY_2 = ABDAY_2, + ABDAY_3 = ABDAY_3, + ABDAY_4 = ABDAY_4, + ABDAY_5 = ABDAY_5, + ABDAY_6 = ABDAY_6, + ABDAY_7 = ABDAY_7, + MON_1 = MON_1, + MON_2 = MON_2, + MON_3 = MON_3, + MON_4 = MON_4, + MON_5 = MON_5, + MON_6 = MON_6, + MON_7 = MON_7, + MON_8 = MON_8, + MON_9 = MON_9, + MON_10 = MON_10, + MON_11 = MON_11, + MON_12 = MON_12, + ABMON_1 = ABMON_1, + ABMON_2 = ABMON_2, + ABMON_3 = ABMON_3, + ABMON_4 = ABMON_4, + ABMON_5 = ABMON_5, + ABMON_6 = ABMON_6, + ABMON_7 = ABMON_7, + ABMON_8 = ABMON_8, + ABMON_9 = ABMON_9, + ABMON_10 = ABMON_10, + ABMON_11 = ABMON_11, + ABMON_12 = ABMON_12, + ERA = ERA, + ERA_D_FMT = ERA_D_FMT, + ERA_D_T_FMT = ERA_D_T_FMT, + ERA_T_FMT = ERA_T_FMT, + ALT_DIGITS = ALT_DIGITS, + RADIXCHAR = RADIXCHAR, + THOUSEP = THOUSEP, + YESEXPR = YESEXPR, + NOEXPR = NOEXPR, + CRNCYSTR = CRNCYSTR, +} + +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD { + + // NOTE: declared with `_t` so we can enumerate the real `nl_info`. + nl_item_t :: distinct c.int + + CODESET :: 0 + D_T_FMT :: 1 + D_FMT :: 2 + T_FMT :: 3 + T_FMT_AMPM :: 4 + AM_STR :: 5 + PM_STR :: 6 + + DAY_1 :: 7 + DAY_2 :: 8 + DAY_3 :: 9 + DAY_4 :: 10 + DAY_5 :: 11 + DAY_6 :: 12 + DAY_7 :: 13 + + ABDAY_1 :: 14 + ABDAY_2 :: 15 + ABDAY_3 :: 16 + ABDAY_4 :: 17 + ABDAY_5 :: 18 + ABDAY_6 :: 19 + ABDAY_7 :: 20 + + MON_1 :: 21 + MON_2 :: 22 + MON_3 :: 23 + MON_4 :: 24 + MON_5 :: 25 + MON_6 :: 26 + MON_7 :: 27 + MON_8 :: 28 + MON_9 :: 29 + MON_10 :: 30 + MON_11 :: 31 + MON_12 :: 32 + + ABMON_1 :: 33 + ABMON_2 :: 34 + ABMON_3 :: 35 + ABMON_4 :: 36 + ABMON_5 :: 37 + ABMON_6 :: 38 + ABMON_7 :: 39 + ABMON_8 :: 40 + ABMON_9 :: 41 + ABMON_10 :: 42 + ABMON_11 :: 43 + ABMON_12 :: 44 + + ERA :: 45 + ERA_D_FMT :: 46 + ERA_D_T_FMT :: 47 + ERA_T_FMT :: 48 + ALT_DIGITS :: 49 + + RADIXCHAR :: 50 + THOUSEP :: 51 + + YESEXPR :: 52 + NOEXPR :: 53 + + CRNCYSTR :: 56 + +} else when ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + + // NOTE: declared with `_t` so we can enumerate the real `nl_info`. + nl_item_t :: distinct c.int + + CODESET :: 51 + D_T_FMT :: 0 + D_FMT :: 1 + T_FMT :: 2 + T_FMT_AMPM :: 3 + AM_STR :: 4 + PM_STR :: 5 + + DAY_1 :: 6 + DAY_2 :: 7 + DAY_3 :: 8 + DAY_4 :: 9 + DAY_5 :: 10 + DAY_6 :: 11 + DAY_7 :: 12 + + ABDAY_1 :: 13 + ABDAY_2 :: 14 + ABDAY_3 :: 15 + ABDAY_4 :: 16 + ABDAY_5 :: 17 + ABDAY_6 :: 18 + ABDAY_7 :: 19 + + MON_1 :: 20 + MON_2 :: 21 + MON_3 :: 22 + MON_4 :: 23 + MON_5 :: 24 + MON_6 :: 25 + MON_7 :: 26 + MON_8 :: 27 + MON_9 :: 28 + MON_10 :: 29 + MON_11 :: 30 + MON_12 :: 31 + + ABMON_1 :: 32 + ABMON_2 :: 33 + ABMON_3 :: 34 + ABMON_4 :: 35 + ABMON_5 :: 36 + ABMON_6 :: 37 + ABMON_7 :: 38 + ABMON_8 :: 39 + ABMON_9 :: 40 + ABMON_10 :: 41 + ABMON_11 :: 42 + ABMON_12 :: 43 + + ERA :: 52 + ERA_D_FMT :: 53 + ERA_D_T_FMT :: 54 + ERA_T_FMT :: 55 + ALT_DIGITS :: 56 + + RADIXCHAR :: 44 + THOUSEP :: 45 + + YESEXPR :: 47 + NOEXPR :: 49 + + CRNCYSTR :: 50 + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/libgen.odin b/core/sys/posix/libgen.odin new file mode 100644 index 000000000..99506797e --- /dev/null +++ b/core/sys/posix/libgen.odin @@ -0,0 +1,74 @@ +package posix + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// libgen.h - definitions for pattern matching functions + +foreign lib { + /* + Takes the pathname pointed to by path and return a pointer to the final component of the + pathname, deleting any trailing '/' characters. + + NOTE: may modify input, so don't give it string literals. + + Returns: a string that might be a modification of the input string or a static string overwritten by subsequent calls + + Example: + tests := []string{ + "usr", "usr/", "", "/", "//", "///", "/usr/", "/usr/lib", + "//usr//lib//", "/home//dwc//test", + } + + tbl: table.Table + table.init(&tbl) + table.header(&tbl, "input", "dirname", "basename") + + for test in tests { + din := strings.clone_to_cstring(test); defer delete(din) + dir := strings.clone_from_cstring(posix.dirname(din)) + + bin := strings.clone_to_cstring(test); defer delete(bin) + base := strings.clone_from_cstring(posix.basename(bin)) + table.row(&tbl, test, dir, base) + } + + table.write_plain_table(os.stream_from_handle(os.stdout), &tbl) + + Output: + +----------------+----------+--------+ + |input |dirname |basename| + +----------------+----------+--------+ + |usr |. |usr | + |usr/ |. |usr | + | |. |. | + |/ |/ |/ | + |// |/ |/ | + |/// |/ |/ | + |/usr/ |/ |usr | + |/usr/lib |/usr |lib | + |//usr//lib// |//usr |lib | + |/home//dwc//test|/home//dwc|test | + +----------------+----------+--------+ + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/basename.html ]] + */ + basename :: proc(path: cstring) -> cstring --- + + /* + Takes a string that contains a pathname, and returns a string that is a pathname of the parent + directory of that file. + + NOTE: may modify input, so don't give it string literals. + + Returns: a string that might be a modification of the input string or a static string overwritten by subsequent calls + + See example for basename(). + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dirname.html ]] + */ + dirname :: proc(path: cstring) -> cstring --- +} diff --git a/core/sys/posix/limits.odin b/core/sys/posix/limits.odin new file mode 100644 index 000000000..7bb561215 --- /dev/null +++ b/core/sys/posix/limits.odin @@ -0,0 +1,459 @@ +package posix + +// limits.h - implementation-defined constants + +// NOTE: numerical limits are left out because Odin provides `min(T)` and `max(T)`. + +// The header shall define the following symbolic constants with the values shown. +// These are the most restrictive values for certain features on an implementation. +// A conforming implementation shall provide values no larger than these values. +// A conforming application must not require a smaller value for correct operation. + +_POSIX_CLOCKRES_MIN :: 20000000 + +// The header shall define the following symbolic constants with the values shown. +// These are the most restrictive values for certain features on an implementation conforming to +// this volume of POSIX.1-2017. +// Related symbolic constants are defined elsewhere in this volume of POSIX.1-2017 which reflect +// the actual implementation and which need not be as restrictive. For each of these limits, +// a conforming implementation shall provide a value at least this large or shall have no limit. +// A strictly conforming application must not require a larger value for correct operation. + +_POSIX_AIO_LISTIO_MAX :: 2 +_POSIX_AIO_MAX :: 1 +_POSIX_ARG_MAX :: 4096 +_POSIX_CHILD_MAX :: 25 +_POSIX_DELAYTIMER_MAX :: 32 +_POSIX_HOST_NAME_MAX :: 255 +_POSIX_LINK_MAX :: 8 +_POSIX_MAX_CANON :: 255 +_POSIX_MAX_INPUT :: 255 +_POSIX_MQ_OPEN_MAX :: 8 +_POSIX_MQ_PRIO_MAX :: 32 +_POSIX_NAME_MAX :: 14 +_POSIX_NGROUPS_MAX :: 8 +_POSIX_OPEN_MAX :: 20 +_POSIX_PATH_MAX :: 256 +_POSIX_PIPE_BUF :: 512 +_POSIX_RE_DUP_MAX :: 255 +_POSIX_RTSIG_MAX :: 8 +_POSIX_SEM_NSEMS_MAX :: 256 +_POSIX_SEM_VALUE_MAX :: 32767 +_POSIX_SS_REPL_MAX :: 4 +_POSIX_STREAM_MAX :: 8 +_POSIX_SYMLINK_MAX :: 255 +_POSIX_SYMLOOP_MAX :: 8 +_POSIX_THREAD_DESTRUCTION_ITERATIONS :: 4 +_POSIX_THREAD_KEYS_MAX :: 128 +_POSIX_THREADS_THREADS_MAX :: 64 +_POSIX_TIMER_MAX :: 32 +_POSIX_TRAXE_EVENT_NAME_MAX :: 30 +_POSIX_TRACE_NAME_MAX :: 8 +_POSIX_TRACE_SYS_MAX :: 8 +_POSIX_TRACE_USER_EVENT_MAX :: 32 +_POSIX_TTY_NAME_MAX :: 9 +_POSIX_TZNAME_MAX :: 6 +_POSIX2_BC_BASE_MAX :: 99 +_POSIX2_BC_DIM_MAX :: 2048 +_POSIX2_BC_SCALE_MAX :: 99 +_POSIX2_CHARCLASS_NAME_MAX :: 14 +_POSIX2_COLL_WEIGHTS_MAX :: 2 +_POSIX2_EXPR_NEST_MAX :: 32 +_POSIX2_LINE_MAX :: 2048 +_POSIX2_RE_DUP_MAX :: 255 +_XOPEN_IOV_MAX :: 16 +_XOPEN_NAME_MAX :: 255 +_XOPEN_PATH_MAX :: 1024 + +/* +NOTE: for full portability, usage should look something like: + + page_size: uint + when #defined(posix.PAGESIZE) { + page_size = posix.PAGESIZE + } else { + page_size = posix.sysconf(._PAGESIZE) + } +*/ + +when ODIN_OS == .Darwin { + // A definition of one of the symbolic constants in the following list shall be omitted from + // on specific implementations where the corresponding value is equal to or greater + // than the stated minimum, but is unspecified. + // + // This indetermination might depend on the amount of available memory space on a specific + // instance of a specific implementation. The actual value supported by a specific instance shall + // be provided by the sysconf() function. + + // AIO_LISTIO_MAX :: sysconf(._AIO_LISTIO_MAX) + // AIO_MAX :: sysconf(._AIO_MAX) + // AIO_PRIO_DELTA_MAX :: sysconf(._AIO_PRIO_DELTA_MAX) + ARG_MAX :: 1024 * 1024 + // ATEXIT_MAX :: sysconf(._ATEXIT_MAX) + CHILD_MAX :: 266 + // DELAYTIMER_MAX :: sysconf(._DELAYTIMER_MAX) + // HOST_NAME_MAX :: sysconf(._HOST_NAME_MAX) + IOV_MAX :: 1024 + // LOGIN_NAME_MAX :: sysconf(._LOGIN_NAME_MAX) + // MQ_OPEN_MAX :: sysconf(._MQ_OPEN_MAX) + // MQ_PRIO_MAX :: sysconf(._MQ_PRIO_MAX) + PAGESIZE :: PAGE_SIZE + PAGE_SIZE :: 1 << 12 + PTHREAD_DESTRUCTOR_ITERATIONS :: 4 + PTHREAD_KEYS_MAX :: 512 + PTHREAD_STACK_MIN :: 16384 when ODIN_ARCH == .arm64 else 8192 + // RTSIG_MAX :: sysconf(._RTSIG_MAX) + // SEM_NSEMS_MAX :: sysconf(._SEM_NSEMS_MAX) + // SEM_VALUE_MAX :: sysconf(._SEM_VALUE_MAX) + // SIGQUEUE_MAX :: sysconf(._SIGQUEUE_MAX) + // SS_REPL_MAX :: sysconf(._SS_REPL_MAX) + // STREAM_MAX :: sysconf(._STREAM_MAX) + // SYMLOOP_MAX :: sysconf(._SYMLOOP_MAX) + // TIMER_MAX :: sysconf(._TIMER_MAX) + // TRACE_EVENT_NAME_MAX :: sysconf(._TRACE_EVENT_NAME_MAX) + // TRACE_NAME_MAX :: sysconf(._TRACE_NAME_MAX) + // TRACE_SYS_MAX :: sysconf(._TRACE_SYS_MAX) + // TRACE_USER_EVENT_MAX :: sysconf(._TRACE_USER_EVENT_MAX) + // TTY_NAME_MAX :: sysconf(._TTY_NAME_MAX) + // TZNAME_MAX :: sysconf(._TZNAME_MAX) + + // The values in the following list may be constants within an implementation or may vary from + // one pathname to another. + // For example, file systems or directories may have different characteristics. + // + // A definition of one of the symbolic constants in the following list shall be omitted from the + // header on specific implementations where the corresponding value is equal to or + // greater than the stated minimum, but where the value can vary depending on the file to which + // it is applied. + // The actual value supported for a specific pathname shall be provided by the pathconf() function. + + // FILESIZEBITS :: pathconf(".", ._FILESIZEBITS) + LINK_MAX :: 32767 + MAX_CANON :: 1024 + MAX_INPUT :: 1024 + NAME_MAX :: 255 + PATH_MAX :: 1024 + PIPE_BUF :: 512 + // POSIX_ALLOC_SIZE_MIN :: pathconf("foo.txt", ._POSIX_ALLOC_SIZE_MIN) + // POSIX_REC_INCR_XFER_SIZE :: pathconf("foo.txt", ._POSIX_REC_INCR_XFER_SIZE) + // POSIX_REC_MAX_XFER_SIZE :: pathconf("foo.txt", ._POSIX_REC_MAX_XFER_SIZE) + // POSIX_REC_MIN_XFER_SIZE :: pathconf("foo.txt", ._POSIX_REC_MIN_XFER_SIZE) + // POSIX_REC_XFER_ALIGN :: pathconf("foo.txt", ._POSIX_REC_XFER_ALIGN) + // SYMLINK_MAX :: pathconf(".", ._SYMLINK_MAX) + + + // The magnitude limitations in the following list shall be fixed by specific implementations. + // An application should assume that the value of the symbolic constant defined by + // in a specific implementation is the minimum that pertains whenever the application is run + // under that implementation. + // A specific instance of a specific implementation may increase the value relative to that + // supplied by for that implementation. + // The actual value supported by a specific instance shall be provided by the sysconf() function. + + BC_BASE_MAX :: 99 + BC_DIM_MAX :: 2048 + BC_SCALE_MAX :: 99 + BC_STRING_MAX :: 1000 + CHARCLASS_NAME_MAX :: 14 + COLL_WEIGHTS_MAX :: 2 + EXPR_NEST_MAX :: 2 + LINE_MAX :: 2048 + NGROUPS_MAX :: 16 + RE_DUP_MAX :: 255 + + // Other limits. + + NL_ARGMAX :: 9 + NL_LANGMAX :: 14 + NL_MSGMAX :: 32767 + NL_SETMAX :: 255 + NL_TEXTMAX :: 2048 + NZERO :: 20 + +} else when ODIN_OS == .FreeBSD { + // A definition of one of the symbolic constants in the following list shall be omitted from + // on specific implementations where the corresponding value is equal to or greater + // than the stated minimum, but is unspecified. + // + // This indetermination might depend on the amount of available memory space on a specific + // instance of a specific implementation. The actual value supported by a specific instance shall + // be provided by the sysconf() function. + + // AIO_LISTIO_MAX :: sysconf(._AIO_LISTIO_MAX) + // AIO_MAX :: sysconf(._AIO_MAX) + // AIO_PRIO_DELTA_MAX :: sysconf(._AIO_PRIO_DELTA_MAX) + ARG_MAX :: 2 * 256 * 1024 + // ATEXIT_MAX :: sysconf(._ATEXIT_MAX) + CHILD_MAX :: 40 + // DELAYTIMER_MAX :: sysconf(._DELAYTIMER_MAX) + // HOST_NAME_MAX :: sysconf(._HOST_NAME_MAX) + IOV_MAX :: 1024 + // LOGIN_NAME_MAX :: sysconf(._LOGIN_NAME_MAX) + // MQ_OPEN_MAX :: sysconf(._MQ_OPEN_MAX) + MQ_PRIO_MAX :: 64 + PAGESIZE :: PAGE_SIZE + PAGE_SIZE :: 1 << 12 + PTHREAD_DESTRUCTOR_ITERATIONS :: 4 + PTHREAD_KEYS_MAX :: 256 + PTHREAD_STACK_MIN :: MINSIGSTKSZ + // RTSIG_MAX :: sysconf(._RTSIG_MAX) + // SEM_NSEMS_MAX :: sysconf(._SEM_NSEMS_MAX) + // SEM_VALUE_MAX :: sysconf(._SEM_VALUE_MAX) + // SIGQUEUE_MAX :: sysconf(._SIGQUEUE_MAX) + // SS_REPL_MAX :: sysconf(._SS_REPL_MAX) + // STREAM_MAX :: sysconf(._STREAM_MAX) + // SYMLOOP_MAX :: sysconf(._SYMLOOP_MAX) + // TIMER_MAX :: sysconf(._TIMER_MAX) + // TRACE_EVENT_NAME_MAX :: sysconf(._TRACE_EVENT_NAME_MAX) + // TRACE_NAME_MAX :: sysconf(._TRACE_NAME_MAX) + // TRACE_SYS_MAX :: sysconf(._TRACE_SYS_MAX) + // TRACE_USER_EVENT_MAX :: sysconf(._TRACE_USER_EVENT_MAX) + // TTY_NAME_MAX :: sysconf(._TTY_NAME_MAX) + // TZNAME_MAX :: sysconf(._TZNAME_MAX) + + // The values in the following list may be constants within an implementation or may vary from + // one pathname to another. + // For example, file systems or directories may have different characteristics. + // + // A definition of one of the symbolic constants in the following list shall be omitted from the + // header on specific implementations where the corresponding value is equal to or + // greater than the stated minimum, but where the value can vary depending on the file to which + // it is applied. + // The actual value supported for a specific pathname shall be provided by the pathconf() function. + + // FILESIZEBITS :: pathconf(".", ._FILESIZEBITS) + // LINK_MAX :: pathconf(foo.txt", ._LINK_MAX) + MAX_CANON :: 255 + MAX_INPUT :: 255 + NAME_MAX :: 255 + PATH_MAX :: 1024 + PIPE_BUF :: 512 + // POSIX_ALLOC_SIZE_MIN :: pathconf("foo.txt", ._POSIX_ALLOC_SIZE_MIN) + // POSIX_REC_INCR_XFER_SIZE :: pathconf("foo.txt", ._POSIX_REC_INCR_XFER_SIZE) + // POSIX_REC_MAX_XFER_SIZE :: pathconf("foo.txt", ._POSIX_REC_MAX_XFER_SIZE) + // POSIX_REC_MIN_XFER_SIZE :: pathconf("foo.txt", ._POSIX_REC_MIN_XFER_SIZE) + // POSIX_REC_XFER_ALIGN :: pathconf("foo.txt", ._POSIX_REC_XFER_ALIGN) + // SYMLINK_MAX :: pathconf(".", ._SYMLINK_MAX) + + + // The magnitude limitations in the following list shall be fixed by specific implementations. + // An application should assume that the value of the symbolic constant defined by + // in a specific implementation is the minimum that pertains whenever the application is run + // under that implementation. + // A specific instance of a specific implementation may increase the value relative to that + // supplied by for that implementation. + // The actual value supported by a specific instance shall be provided by the sysconf() function. + + BC_BASE_MAX :: 99 + BC_DIM_MAX :: 2048 + BC_SCALE_MAX :: 99 + BC_STRING_MAX :: 1000 + CHARCLASS_NAME_MAX :: 14 + COLL_WEIGHTS_MAX :: 10 + EXPR_NEST_MAX :: 32 + LINE_MAX :: 2048 + NGROUPS_MAX :: 1023 + RE_DUP_MAX :: 255 + + // Other limits. + + NL_ARGMAX :: 4096 + NL_LANGMAX :: 31 + NL_MSGMAX :: 32767 + NL_SETMAX :: 255 + NL_TEXTMAX :: 2048 + NZERO :: 0 + +} else when ODIN_OS == .NetBSD { + + // A definition of one of the symbolic constants in the following list shall be omitted from + // on specific implementations where the corresponding value is equal to or greater + // than the stated minimum, but is unspecified. + // + // This indetermination might depend on the amount of available memory space on a specific + // instance of a specific implementation. The actual value supported by a specific instance shall + // be provided by the sysconf() function. + + // AIO_LISTIO_MAX :: sysconf(._AIO_LISTIO_MAX) + // AIO_MAX :: sysconf(._AIO_MAX) + // AIO_PRIO_DELTA_MAX :: sysconf(._AIO_PRIO_DELTA_MAX) + ARG_MAX :: 256 * 1024 + // ATEXIT_MAX :: sysconf(._ATEXIT_MAX) + CHILD_MAX :: 160 + // DELAYTIMER_MAX :: sysconf(._DELAYTIMER_MAX) + // HOST_NAME_MAX :: sysconf(._HOST_NAME_MAX) + IOV_MAX :: 1024 + LOGIN_NAME_MAX :: 17 + MQ_OPEN_MAX :: 512 + MQ_PRIO_MAX :: 32 + PAGESIZE :: PAGE_SIZE + PAGE_SIZE :: 1 << 12 + PTHREAD_DESTRUCTOR_ITERATIONS :: 4 + PTHREAD_KEYS_MAX :: 256 + // PTHREAD_STACK_MIN :: sysconf(._THREAD_STACK_MIN) + // RTSIG_MAX :: sysconf(._RTSIG_MAX) + // SEM_NSEMS_MAX :: sysconf(._SEM_NSEMS_MAX) + // SEM_VALUE_MAX :: sysconf(._SEM_VALUE_MAX) + // SIGQUEUE_MAX :: sysconf(._SIGQUEUE_MAX) + // SS_REPL_MAX :: sysconf(._SS_REPL_MAX) + // STREAM_MAX :: sysconf(._STREAM_MAX) + // SYMLOOP_MAX :: sysconf(._SYMLOOP_MAX) + // TIMER_MAX :: sysconf(._TIMER_MAX) + // TRACE_EVENT_NAME_MAX :: sysconf(._TRACE_EVENT_NAME_MAX) + // TRACE_NAME_MAX :: sysconf(._TRACE_NAME_MAX) + // TRACE_SYS_MAX :: sysconf(._TRACE_SYS_MAX) + // TRACE_USER_EVENT_MAX :: sysconf(._TRACE_USER_EVENT_MAX) + // TTY_NAME_MAX :: sysconf(._TTY_NAME_MAX) + // TZNAME_MAX :: sysconf(._TZNAME_MAX) + + // The values in the following list may be constants within an implementation or may vary from + // one pathname to another. + // For example, file systems or directories may have different characteristics. + // + // A definition of one of the symbolic constants in the following list shall be omitted from the + // header on specific implementations where the corresponding value is equal to or + // greater than the stated minimum, but where the value can vary depending on the file to which + // it is applied. + // The actual value supported for a specific pathname shall be provided by the pathconf() function. + + // FILESIZEBITS :: pathconf(".", ._FILESIZEBITS) + LINK_MAX :: 32767 + MAX_CANON :: 255 + MAX_INPUT :: 255 + NAME_MAX :: 511 + PATH_MAX :: 1024 + PIPE_BUF :: 512 + // POSIX_ALLOC_SIZE_MIN :: pathconf("foo.txt", ._POSIX_ALLOC_SIZE_MIN) + // POSIX_REC_INCR_XFER_SIZE :: pathconf("foo.txt", ._POSIX_REC_INCR_XFER_SIZE) + // POSIX_REC_MAX_XFER_SIZE :: pathconf("foo.txt", ._POSIX_REC_MAX_XFER_SIZE) + // POSIX_REC_MIN_XFER_SIZE :: pathconf("foo.txt", ._POSIX_REC_MIN_XFER_SIZE) + // POSIX_REC_XFER_ALIGN :: pathconf("foo.txt", ._POSIX_REC_XFER_ALIGN) + // SYMLINK_MAX :: pathconf(".", ._SYMLINK_MAX) + + + // The magnitude limitations in the following list shall be fixed by specific implementations. + // An application should assume that the value of the symbolic constant defined by + // in a specific implementation is the minimum that pertains whenever the application is run + // under that implementation. + // A specific instance of a specific implementation may increase the value relative to that + // supplied by for that implementation. + // The actual value supported by a specific instance shall be provided by the sysconf() function. + + BC_BASE_MAX :: max(i32) + BC_DIM_MAX :: 65535 + BC_SCALE_MAX :: max(i32) + BC_STRING_MAX :: max(i32) + CHARCLASS_NAME_MAX :: 14 + COLL_WEIGHTS_MAX :: 2 + EXPR_NEST_MAX :: 32 + LINE_MAX :: 2048 + NGROUPS_MAX :: 16 + RE_DUP_MAX :: 255 + + // Other limits. + + NL_ARGMAX :: 9 + NL_LANGMAX :: 14 + NL_MSGMAX :: 32767 + NL_SETMAX :: 255 + NL_TEXTMAX :: 2048 + NZERO :: 20 + +} else when ODIN_OS == .OpenBSD { + + // A definition of one of the symbolic constants in the following list shall be omitted from + // on specific implementations where the corresponding value is equal to or greater + // than the stated minimum, but is unspecified. + // + // This indetermination might depend on the amount of available memory space on a specific + // instance of a specific implementation. The actual value supported by a specific instance shall + // be provided by the sysconf() function. + + // AIO_LISTIO_MAX :: sysconf(._AIO_LISTIO_MAX) + // AIO_MAX :: sysconf(._AIO_MAX) + // AIO_PRIO_DELTA_MAX :: sysconf(._AIO_PRIO_DELTA_MAX) + ARG_MAX :: 512 * 1024 + // ATEXIT_MAX :: sysconf(._ATEXIT_MAX) + CHILD_MAX :: 80 + // DELAYTIMER_MAX :: sysconf(._DELAYTIMER_MAX) + // HOST_NAME_MAX :: sysconf(._HOST_NAME_MAX) + IOV_MAX :: 1024 + LOGIN_NAME_MAX :: 32 + MQ_OPEN_MAX :: 512 + MQ_PRIO_MAX :: 32 + PAGESIZE :: PAGE_SIZE + PAGE_SIZE :: 1 << 12 + PTHREAD_DESTRUCTOR_ITERATIONS :: 4 + PTHREAD_KEYS_MAX :: 256 + PTHREAD_STACK_MIN :: 1 << 12 + // RTSIG_MAX :: sysconf(._RTSIG_MAX) + // SEM_NSEMS_MAX :: sysconf(._SEM_NSEMS_MAX) + SEM_VALUE_MAX :: max(u32) + // SIGQUEUE_MAX :: sysconf(._SIGQUEUE_MAX) + // SS_REPL_MAX :: sysconf(._SS_REPL_MAX) + // STREAM_MAX :: sysconf(._STREAM_MAX) + SYMLOOP_MAX :: 32 + // TIMER_MAX :: sysconf(._TIMER_MAX) + // TRACE_EVENT_NAME_MAX :: sysconf(._TRACE_EVENT_NAME_MAX) + // TRACE_NAME_MAX :: sysconf(._TRACE_NAME_MAX) + // TRACE_SYS_MAX :: sysconf(._TRACE_SYS_MAX) + // TRACE_USER_EVENT_MAX :: sysconf(._TRACE_USER_EVENT_MAX) + // TTY_NAME_MAX :: sysconf(._TTY_NAME_MAX) + // TZNAME_MAX :: sysconf(._TZNAME_MAX) + + // The values in the following list may be constants within an implementation or may vary from + // one pathname to another. + // For example, file systems or directories may have different characteristics. + // + // A definition of one of the symbolic constants in the following list shall be omitted from the + // header on specific implementations where the corresponding value is equal to or + // greater than the stated minimum, but where the value can vary depending on the file to which + // it is applied. + // The actual value supported for a specific pathname shall be provided by the pathconf() function. + + // FILESIZEBITS :: pathconf(".", ._FILESIZEBITS) + LINK_MAX :: 32767 + MAX_CANON :: 255 + MAX_INPUT :: 255 + NAME_MAX :: 255 + PATH_MAX :: 1024 + PIPE_BUF :: 512 + // POSIX_ALLOC_SIZE_MIN :: pathconf("foo.txt", ._POSIX_ALLOC_SIZE_MIN) + // POSIX_REC_INCR_XFER_SIZE :: pathconf("foo.txt", ._POSIX_REC_INCR_XFER_SIZE) + // POSIX_REC_MAX_XFER_SIZE :: pathconf("foo.txt", ._POSIX_REC_MAX_XFER_SIZE) + // POSIX_REC_MIN_XFER_SIZE :: pathconf("foo.txt", ._POSIX_REC_MIN_XFER_SIZE) + // POSIX_REC_XFER_ALIGN :: pathconf("foo.txt", ._POSIX_REC_XFER_ALIGN) + SYMLINK_MAX :: PATH_MAX + + + // The magnitude limitations in the following list shall be fixed by specific implementations. + // An application should assume that the value of the symbolic constant defined by + // in a specific implementation is the minimum that pertains whenever the application is run + // under that implementation. + // A specific instance of a specific implementation may increase the value relative to that + // supplied by for that implementation. + // The actual value supported by a specific instance shall be provided by the sysconf() function. + + BC_BASE_MAX :: max(i32) + BC_DIM_MAX :: 65535 + BC_SCALE_MAX :: max(i32) + BC_STRING_MAX :: max(i32) + CHARCLASS_NAME_MAX :: 14 + COLL_WEIGHTS_MAX :: 2 + EXPR_NEST_MAX :: 32 + LINE_MAX :: 2048 + NGROUPS_MAX :: 16 + RE_DUP_MAX :: 255 + + // Other limits. + + NL_ARGMAX :: 9 + NL_LANGMAX :: 14 + NL_MSGMAX :: 32767 + NL_SETMAX :: 255 + NL_TEXTMAX :: 255 + NZERO :: 20 + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/locale.odin b/core/sys/posix/locale.odin new file mode 100644 index 000000000..1f2a336b5 --- /dev/null +++ b/core/sys/posix/locale.odin @@ -0,0 +1,93 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// locale.h - category macros + +foreign lib { + /* + Sets the components of an object with the type lconv with the values appropriate for the + formatting of numeric quantities (monetary and otherwise) according to the rules of the current + locale. + + Returns: a pointer to the lconv structure, might be invalidated by subsequent calls to localeconv() and setlocale() + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/localeconv.html ]] + */ + localeconv :: proc() -> ^lconv --- + + /* + Selects the appropriate piece of the global locale, as specified by the category and locale arguments, + and can be used to change or query the entire global locale or portions thereof. + + Returns: the current locale if `locale` is `nil`, the set locale otherwise + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setlocale.html ]] + */ + @(link_name=LSETLOCALE) + setlocale :: proc(category: Locale_Category, locale: cstring) -> cstring --- +} + +Locale_Category :: enum c.int { + ALL = LC_ALL, + COLLATE = LC_COLLATE, + CTYPE = LC_CTYPE, + MESSAGES = LC_MESSAGES, + MONETARY = LC_MONETARY, + NUMERIC = LC_NUMERIC, + TIME = LC_TIME, +} + +when ODIN_OS == .NetBSD { + @(private) LSETLOCALE :: "__setlocale50" +} else { + @(private) LSETLOCALE :: "setlocale" +} + +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + + // NOTE: All of these fields are standard ([PSX]). + lconv :: struct { + decimal_point: cstring, + thousand_sep: cstring, + grouping: cstring, + int_curr_symbol: cstring, + currency_symbol: cstring, + mon_decimal_points: cstring, + mon_thousands_sep: cstring, + mon_grouping: cstring, + positive_sign: cstring, + negative_sign: cstring, + int_frac_digits: c.char, + frac_digits: c.char, + p_cs_precedes: c.char, + p_sep_by_space: c.char, + n_cs_precedes: c.char, + n_sep_by_space: c.char, + p_sign_posn: c.char, + n_sign_posn: c.char, + int_p_cs_precedes: c.char, + int_n_cs_precedes: c.char, + int_p_sep_by_space: c.char, + int_n_sep_by_space: c.char, + int_p_sign_posn: c.char, + int_n_sign_posn: c.char, + } + + LC_ALL :: 0 + LC_COLLATE :: 1 + LC_CTYPE :: 2 + LC_MESSAGES :: 6 + LC_MONETARY :: 3 + LC_NUMERIC :: 4 + LC_TIME :: 5 + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/monetary.odin b/core/sys/posix/monetary.odin new file mode 100644 index 000000000..b4f0c31ee --- /dev/null +++ b/core/sys/posix/monetary.odin @@ -0,0 +1,42 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// monetary.h - monetary types + +foreign lib { + + /* + Places characters into the array pointed to by s as controlled by the string format. + No more than maxsize bytes are placed into the array. + + Returns: -1 (setting errno) on failure, the number of bytes added to s otherwise + + Example: + posix.setlocale(.ALL, "en_US.UTF-8") + value := 123456.789 + buffer: [100]byte + size := posix.strfmon(raw_data(buffer[:]), len(buffer), "%n", value) + if int(size) == -1 { + fmt.panicf("strfmon failure: %s", posix.strerror(posix.errno())) + } + fmt.println(string(buffer[:size])) + + Output: + $123,456.79 + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/strfmon.html ]] + */ + strfmon :: proc( + s: [^]byte, + maxsize: c.size_t, + format: cstring, + #c_vararg args: ..any, + ) -> c.size_t --- +} diff --git a/core/sys/posix/net_if.odin b/core/sys/posix/net_if.odin new file mode 100644 index 000000000..aaeb5088a --- /dev/null +++ b/core/sys/posix/net_if.odin @@ -0,0 +1,60 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// net/if.h - sockets local interfaces + +foreign lib { + /* + Retrieve an array of name indexes. Where the last one has an index of 0 and name of nil. + + Returns: nil (setting errno) on failure, an array that should be freed with if_freenameindex otherwise + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/if_nameindex.html ]] + */ + if_nameindex :: proc() -> [^]if_nameindex_t --- + + /* + Returns the interface index matching the name or zero. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/if_nametoindex.html ]] + */ + if_nametoindex :: proc(name: cstring) -> c.uint --- + + /* + Returns the name corresponding to the index. + + ifname should be at least IF_NAMESIZE bytes in size. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/if_indextoname.html ]] + */ + if_indextoname :: proc(ifindex: c.uint, ifname: [^]byte) -> cstring --- + + /* + Frees memory allocated by if_nameindex. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/if_freenameindex.html ]] + */ + if_freenameindex :: proc(ptr: ^if_nameindex_t) --- +} + +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + + // NOTE: `_t` suffix added due to name conflict. + + if_nameindex_t :: struct { + if_index: c.uint, /* [PSX] 1, 2, ... */ + if_name: cstring, /* [PSX] null terminated name: "le0", ... */ + } + + IF_NAMESIZE :: 16 + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/netdb.odin b/core/sys/posix/netdb.odin new file mode 100644 index 000000000..7570f9a22 --- /dev/null +++ b/core/sys/posix/netdb.odin @@ -0,0 +1,443 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// netdb.h - definitions for network database operations + +foreign lib { + /* + Translate node/serv name and return a set of socket addresses and associated information to be + used in creating a socket with which to address the specified service. + + Example: + // The following (incomplete) program demonstrates the use of getaddrinfo() to obtain the + // socket address structure(s) for the service named in the program's command-line argument. + // The program then loops through each of the address structures attempting to create and bind + // a socket to the address, until it performs a successful bind(). + + args := runtime.args__ + if len(args) != 2 { + fmt.eprintfln("Usage: %s port", args[0]) + posix.exit(1) + } + + hints: posix.addrinfo + hints.ai_socktype = .DGRAM + hints.ai_flags = { .PASSIVE } + + result: ^posix.addrinfo + s := posix.getaddrinfo(nil, args[1], &hints, &result) + if s != .NONE { + fmt.eprintfln("getaddrinfo: %s", posix.gai_strerror(s)) + posix.exit(1) + } + defer posix.freeaddrinfo(result) + + // Try each address until a successful bind(). + rp: ^posix.addrinfo + for rp = result; rp != nil; rp = rp.ai_next { + sfd := posix.socket(rp.ai_family, rp.ai_socktype, rp.ai_protocol) + if sfd == -1 { + continue + } + + if posix.bind(sfd, rp.ai_addr, rp.ai_addrlen) == 0 { + // Success. + break + } + + posix.close(sfd) + } + + if rp == nil { + fmt.eprintln("Could not bind") + posix.exit(1) + } + + // Use the socket... + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html ]] + */ + getaddrinfo :: proc( + nodename: cstring, + servname: cstring, + hints: ^addrinfo, + res: ^^addrinfo, + ) -> Info_Errno --- + + /* + Frees the given address info linked list. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html ]] + */ + freeaddrinfo :: proc(ai: ^addrinfo) --- + + /* + Translate a socket address to a node name and service location. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getnameinfo.html ]] + */ + getnameinfo :: proc( + sa: ^sockaddr, salen: socklen_t, + node: [^]byte, nodelen: socklen_t, + service: [^]byte, servicelen: socklen_t, + flags: Nameinfo_Flags, + ) -> Info_Errno --- + + /* + Get a textual description for the address info errors. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/gai_strerror.html ]] + */ + gai_strerror :: proc(ecode: Info_Errno) -> cstring --- + + /* + Opens a connection to the database and set the next entry to the first entry in the database. + + This reads /etc/hosts on most systems. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sethostent.html ]] + */ + sethostent :: proc(stayopen: b32) --- + + /* + Reads the next entry in the database, opening and closing a connection as necessary. + + This reads /etc/hosts on most systems. + + Example: + posix.sethostent(true) + defer posix.endhostent() + for ent := posix.gethostent(); ent != nil; ent = posix.gethostent() { + fmt.println(ent) + fmt.println(ent.h_addr_list[0][:ent.h_length]) + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sethostent.html ]] + */ + gethostent :: proc() -> ^hostent --- + + /* + Closes the connection to the database. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sethostent.html ]] + */ + endhostent :: proc() --- + + /* + Opens and rewinds the database. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setnetent.html ]] + */ + setnetent :: proc(stayopen: b32) --- + + /* + Reads the next entry of the database. + + Example: + posix.setnetent(true) + defer posix.endnetent() + for ent := posix.getnetent(); ent != nil; ent = posix.getnetent() { + fmt.println(ent) + fmt.println(transmute([4]byte)ent.n_net) + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setnetent.html ]] + */ + getnetent :: proc() -> ^netent --- + + /* + Search the database from the beginning, and find the first entry that matches. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setnetent.html ]] + */ + getnetbyaddr :: proc(net: c.uint32_t, type: AF) -> ^netent --- + + /* + Search the database from the beginning, and find the first entry that matches. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setnetent.html ]] + */ + getnetbyname :: proc(name: cstring) -> ^netent --- + + /* + Closes the database. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setnetent.html ]] + */ + endnetent :: proc() --- + + /* + Opens and rewinds the database. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setprotoent.html ]] + */ + setprotoent :: proc(stayopen: b32) --- + + /* + Reads the next entry of the database. + + Example: + posix.setprotoent(true) + defer posix.endprotoent() + for ent := posix.getprotoent(); ent != nil; ent = posix.getprotoent() { + fmt.println(ent) + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setprotoent.html ]] + */ + getprotoent :: proc() -> ^protoent --- + + /* + Search the database from the beginning, and find the first entry that matches. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setprotoent.html ]] + */ + getprotobyname :: proc(name: cstring) -> ^protoent --- + + /* + Search the database from the beginning, and find the first entry that matches. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setprotoent.html ]] + */ + getprotobynumber :: proc(proto: c.int) -> ^protoent --- + + /* + Closes the database. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setprotoent.html ]] + */ + endprotoent :: proc() --- + + /* + Opens and rewinds the database. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setservent.html ]] + */ + setservent :: proc(stayopen: b32) --- + + /* + Reads the next entry of the database. + + Example: + posix.setservent(true) + defer posix.endservent() + for ent := posix.getservent(); ent != nil; ent = posix.getservent() { + fmt.println(ent) + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setservent.html ]] + */ + getservent :: proc() -> ^servent --- + + /* + Search the database from the beginning, and find the first entry that matches. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setservent.html ]] + */ + getservbyname :: proc(name: cstring, proto: cstring) -> ^servent --- + + /* + Search the database from the beginning, and find the first entry that matches. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setservent.html ]] + */ + getservbyport :: proc(port: c.int, proto: cstring) -> ^servent --- + + /* + Closes the database. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setservent.html ]] + */ + endservent :: proc() --- +} + +Addrinfo_Flag_Bits :: enum c.int { + // Socket address is intended for bind(). + PASSIVE = log2(AI_PASSIVE), + // Request for canonical name. + CANONNAME = log2(AI_CANONNAME), + // Return numeric host address as name. + NUMERICHOST = log2(AI_NUMERICHOST), + // Inhibit service name resolution. + NUMERICSERV = log2(AI_NUMERICSERV), + // If no IPv6 addresses are found, query for IPv4 addresses and return them to the + // caller as IPv4-mapped IPv6 addresses. + V4MAPPED = log2(AI_V4MAPPED), + // Query for both IPv4 and IPv6 addresses. + ALL = log2(AI_ALL), + // Query for IPv4 addresses only when an IPv4 address is configured; query for IPv6 addresses + // only when an IPv6 address is configured. + ADDRCONFIG = log2(AI_ADDRCONFIG), +} +Addrinfo_Flags :: bit_set[Addrinfo_Flag_Bits; c.int] + +Nameinfo_Flag_Bits :: enum c.int { + // Only the nodename portion of the FQDN is returned for local hosts. + NOFQDN = log2(NI_NOFQDN), + // The numeric form of the node's address is returned instead of its name. + NUMERICHOST = log2(NI_NUMERICHOST), + // Return an error if the node's name cannot be located in the database. + NAMEREQD = log2(NI_NAMEREQD), + // The numeric form of the service address is returned instead of its name. + NUMERICSERV = log2(NI_NUMERICSERV), + // For IPv6 addresses, the numeric form of the scope identifier is returned instead of its name. + NUMERICSCOPE = log2(NI_NUMERICSCOPE), + // Indicates that the service is a datagram service (SOCK_DGRAM). + DGRAM = log2(NI_DGRAM), +} +Nameinfo_Flags :: bit_set[Nameinfo_Flag_Bits; c.int] + +Info_Errno :: enum c.int { + NONE = 0, + // The name could not be resolved at this time. Future attempts may succeed. + AGAIN = EAI_AGAIN, + // The flags had an invalid value. + BADFLAGS = EAI_BADFLAGS, + // A non-recoverable error ocurred. + FAIL = EAI_FAIL, + // The address family was not recognized or the address length was invald for the specified family. + FAMILY = EAI_FAMILY, + // There was a memory allocation failure. + MEMORY = EAI_MEMORY, + // The name does not resolve for the supplied parameters. + NONAME = EAI_NONAME, + // The service passed was not recognized for the specified socket. + SERVICE = EAI_SERVICE, + // The intended socket type was not recognized. + SOCKTYPE = EAI_SOCKTYPE, + // A system error occurred. The error code can be found in errno. + SYSTEM = EAI_SYSTEM, + // An argument buffer overflowed. + OVERFLOW = EAI_OVERFLOW, +} + +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + + hostent :: struct { + h_name: cstring, /* [PSX] official name of host */ + h_aliases: [^]cstring `fmt:"v,0"`, /* [PSX] alias list */ + h_addrtype: AF, /* [PSX] host address type */ + h_length: c.int, /* [PSX] length of address */ + h_addr_list: [^][^]byte `fmt:"v,0"`, /* [PSX] list of addresses from name server */ + } + + netent :: struct { + n_name: cstring, /* [PSX] official name of net */ + n_aliases: [^]cstring `fmt:"v,0"`, /* [PSX] alias list */ + n_addrtype: AF, /* [PSX] net address type */ + n_net: c.uint32_t, /* [PSX] network # */ + } + + protoent :: struct { + p_name: cstring, /* [PSX] official protocol name */ + p_aliases: [^]cstring `fmt:"v,0"`, /* [PSX] alias list */ + p_proto: c.int, /* [PSX] protocol # */ + } + + servent :: struct { + s_name: cstring, /* [PSX] official service name */ + s_aliases: [^]cstring `fmt:"v,0"`, /* [PSX] alias list */ + s_port: c.int, /* [PSX] port # */ + s_proto: cstring, /* [PSX] protocol # */ + } + + // The highest reserved port number. + IPPORT_RESERVED :: 1024 + + addrinfo :: struct { + ai_flags: Addrinfo_Flags, /* [PSX] input flags */ + ai_family: AF, /* [PSX] address family of socket */ + ai_socktype: Sock, /* [PSX] socket type */ + ai_protocol: Protocol, /* [PSX] protocol of socket */ + ai_addrlen: socklen_t, /* [PSX] length of socket address */ + ai_canonname: cstring, /* [PSX] canonical name of service location */ + ai_addr: ^sockaddr, /* [PSX] binary address */ + ai_next: ^addrinfo, /* [PSX] pointer to next in list */ + } + + when ODIN_OS == .Darwin { + + AI_PASSIVE :: 0x00000001 + AI_CANONNAME :: 0x00000002 + AI_NUMERICHOST :: 0x00000004 + AI_NUMERICSERV :: 0x00001000 + AI_V4MAPPED :: 0x00000800 + AI_ALL :: 0x00000100 + AI_ADDRCONFIG :: 0x00000400 + + NI_NOFQDN :: 0x00000001 + NI_NUMERICHOST :: 0x00000002 + NI_NAMEREQD :: 0x00000004 + NI_NUMERICSERV :: 0x00000008 + NI_NUMERICSCOPE :: 0x00000100 + NI_DGRAM :: 0x00000010 + + } else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD { + + AI_PASSIVE :: 0x00000001 + AI_CANONNAME :: 0x00000002 + AI_NUMERICHOST :: 0x00000004 + AI_NUMERICSERV :: 0x00000008 + AI_V4MAPPED :: 0x00000800 // NOTE: not implemented on netbsd + AI_ALL :: 0x00000100 // NOTE: not implemented on netbsd + AI_ADDRCONFIG :: 0x00000400 + + NI_NOFQDN :: 0x00000001 + NI_NUMERICHOST :: 0x00000002 + NI_NAMEREQD :: 0x00000004 + NI_NUMERICSERV :: 0x00000008 + NI_NUMERICSCOPE :: 0x00000010 + NI_DGRAM :: 0x00000020 + + } else when ODIN_OS == .OpenBSD { + + AI_PASSIVE :: 1 + AI_CANONNAME :: 2 + AI_NUMERICHOST :: 4 + AI_NUMERICSERV :: 16 + AI_V4MAPPED :: 0x00000800 // NOTE: not implemented + AI_ALL :: 0x00000100 // NOTE: not implemented + AI_ADDRCONFIG :: 64 + + NI_NOFQDN :: 4 + NI_NUMERICHOST :: 1 + NI_NAMEREQD :: 8 + NI_NUMERICSERV :: 2 + NI_NUMERICSCOPE :: 32 + NI_DGRAM :: 16 + } + + when ODIN_OS == .OpenBSD { + EAI_AGAIN :: -3 + EAI_BADFLAGS :: -1 + EAI_FAIL :: -4 + EAI_FAMILY :: -6 + EAI_MEMORY :: -10 + EAI_NONAME :: -2 + EAI_SERVICE :: -8 + EAI_SOCKTYPE :: -7 + EAI_SYSTEM :: -11 + EAI_OVERFLOW :: -14 + } else { + EAI_AGAIN :: 2 + EAI_BADFLAGS :: 3 + EAI_FAIL :: 4 + EAI_FAMILY :: 5 + EAI_MEMORY :: 6 + EAI_NONAME :: 8 + EAI_SERVICE :: 9 + EAI_SOCKTYPE :: 10 + EAI_SYSTEM :: 11 + EAI_OVERFLOW :: 14 + } + +}else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/netinet_in.odin b/core/sys/posix/netinet_in.odin new file mode 100644 index 000000000..3926c5288 --- /dev/null +++ b/core/sys/posix/netinet_in.odin @@ -0,0 +1,199 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// netinet/in.h - Internet address family + +foreign lib { + in6addr_any: in6_addr + in6addr_loopback: in6_addr +} + +in_port_t :: u16be +in_addr_t :: u32be + +INET_ADDRSTRLEN :: 16 +INET6_ADDRSTRLEN :: 46 + +Protocol :: enum c.int { + IP = IPPROTO_IP, + ICMP = IPPROTO_ICMP, + IPV6 = IPPROTO_IPV6, + RAW = IPPROTO_RAW, + TCP = IPPROTO_TCP, + UDP = IPPROTO_UDP, +} + +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + + in_addr :: struct { + s_addr: in_addr_t, /* [PSX] big endian address */ + } + + in6_addr :: struct { + using _: struct #raw_union { + s6_addr: [16]c.uint8_t, /* [PSX] big endian address */ + __u6_addr16: [8]c.uint16_t, + __u6_addr32: [4]c.uint32_t, + }, + } + + sockaddr_in :: struct { + sin_len: c.uint8_t, + sin_family: sa_family_t, /* [PSX] AF_INET (but a smaller size) */ + sin_port: in_port_t, /* [PSX] port number */ + sin_addr: in_addr, /* [PSX] IP address */ + sin_zero: [8]c.char, + } + + sockaddr_in6 :: struct { + sin6_len: c.uint8_t, + sin6_family: sa_family_t, /* [PSX] AF_INET6 (but a smaller size) */ + sin6_port: in_port_t, /* [PSX] port number */ + sin6_flowinfo: c.uint32_t, /* [PSX] IPv6 traffic class and flow information */ + sin6_addr: in6_addr, /* [PSX] IPv6 address */ + sin6_scope_id: c.uint32_t, /* [PSX] set of interfaces for a scope */ + } + + ipv6_mreq :: struct { + ipv6mr_multiaddr: in6_addr, /* [PSX] IPv6 multicast address */ + ipv6mr_interface: c.uint, /* [PSX] interface index */ + } + + IPPROTO_IP :: 0 + IPPROTO_ICMP :: 1 + IPPROTO_IPV6 :: 41 + IPPROTO_RAW :: 255 + IPPROTO_TCP :: 6 + IPPROTO_UDP :: 17 + + INADDR_ANY :: 0x00000000 + INADDR_BROADCAST :: 0xFFFFFFFF + + IPV6_JOIN_GROUP :: 12 + IPV6_LEAVE_GROUP :: 13 + IPV6_MULTICAST_HOPS :: 10 + IPV6_MULTICAST_IF :: 9 + IPV6_MULTICAST_LOOP :: 11 + IPV6_UNICAST_HOPS :: 4 + IPV6_V6ONLY :: 27 + + IN6_IS_ADDR_UNSPECIFIED :: #force_inline proc "contextless" (a: in6_addr) -> b32 { + return a.s6_addr == 0 + } + + IN6_IS_ADDR_LOOPBACK :: #force_inline proc "contextless" (a: in6_addr) -> b32 { + a := a + return ( + (^c.uint32_t)(&a.s6_addr[0])^ == 0 && + (^c.uint32_t)(&a.s6_addr[4])^ == 0 && + (^c.uint32_t)(&a.s6_addr[8])^ == 0 && + (^u32be)(&a.s6_addr[12])^ == 1 \ + ) + } + + IN6_IS_ADDR_MULTICAST :: #force_inline proc "contextless" (a: in6_addr) -> b32 { + return a.s6_addr[0] == 0xff + } + + IN6_IS_ADDR_LINKLOCAL :: #force_inline proc "contextless" (a: in6_addr) -> b32 { + return a.s6_addr[0] == 0xfe && a.s6_addr[1] & 0xc0 == 0x80 + } + + IN6_IS_ADDR_SITELOCAL :: #force_inline proc "contextless" (a: in6_addr) -> b32 { + return a.s6_addr[0] == 0xfe && a.s6_addr[1] & 0xc0 == 0xc0 + } + + IN6_IS_ADDR_V4MAPPED :: #force_inline proc "contextless" (a: in6_addr) -> b32 { + a := a + return ( + (^c.uint32_t)(&a.s6_addr[0])^ == 0 && + (^c.uint32_t)(&a.s6_addr[4])^ == 0 && + (^u32be)(&a.s6_addr[8])^ == 0x0000ffff \ + ) + } + + IN6_IS_ADDR_V4COMPAT :: #force_inline proc "contextless" (a: in6_addr) -> b32 { + a := a + return ( + (^c.uint32_t)(&a.s6_addr[0])^ == 0 && + (^c.uint32_t)(&a.s6_addr[4])^ == 0 && + (^c.uint32_t)(&a.s6_addr[8])^ == 0 && + (^c.uint32_t)(&a.s6_addr[12])^ != 0 && + (^u32be)(&a.s6_addr[12])^ != 1 \ + ) + } + + @(private) + __IPV6_ADDR_SCOPE_NODELOCAL :: 0x01 + @(private) + __IPV6_ADDR_SCOPE_LINKLOCAL :: 0x02 + @(private) + __IPV6_ADDR_SCOPE_SITELOCAL :: 0x05 + @(private) + __IPV6_ADDR_SCOPE_ORGLOCAL :: 0x08 + @(private) + __IPV6_ADDR_SCOPE_GLOBAL :: 0x0e + + @(private) + IPV6_ADDR_MC_FLAGS :: #force_inline proc "contextless" (a: in6_addr) -> c.uint8_t { + return a.s6_addr[1] & 0xf0 + } + + @(private) + IPV6_ADDR_MC_FLAGS_TRANSIENT :: 0x10 + @(private) + IPV6_ADDR_MC_FLAGS_PREFIX :: 0x20 + @(private) + IPV6_ADDR_MC_FLAGS_UNICAST_BASED :: IPV6_ADDR_MC_FLAGS_TRANSIENT | IPV6_ADDR_MC_FLAGS_PREFIX + + @(private) + __IPV6_ADDR_MC_SCOPE :: #force_inline proc "contextless" (a: in6_addr) -> c.uint8_t { + return a.s6_addr[1] & 0x0f + } + + IN6_IS_ADDR_MC_NODELOCAL :: #force_inline proc "contextless" (a: in6_addr) -> b32 { + return ( + IN6_IS_ADDR_MULTICAST(a) && + (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_NODELOCAL) \ + ) + } + + IN6_IS_ADDR_MC_LINKLOCAL :: #force_inline proc "contextless" (a: in6_addr) -> b32 { + return ( + IN6_IS_ADDR_MULTICAST(a) && + (IPV6_ADDR_MC_FLAGS(a) != IPV6_ADDR_MC_FLAGS_UNICAST_BASED) && + (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_LINKLOCAL) \ + ) + } + + IN6_IS_ADDR_MC_SITELOCAL :: #force_inline proc "contextless" (a: in6_addr) -> b32 { + return ( + IN6_IS_ADDR_MULTICAST(a) && + (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_SITELOCAL) \ + ) + } + + IN6_IS_ADDR_MC_ORGLOCAL :: #force_inline proc "contextless" (a: in6_addr) -> b32 { + return ( + IN6_IS_ADDR_MULTICAST(a) && + (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_ORGLOCAL) \ + ) + } + + IN6_IS_ADDR_MC_GLOBAL :: #force_inline proc "contextless" (a: in6_addr) -> b32 { + return ( + IN6_IS_ADDR_MULTICAST(a) && + (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_GLOBAL) \ + ) + } + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/netinet_tcp.odin b/core/sys/posix/netinet_tcp.odin new file mode 100644 index 000000000..ecd084b38 --- /dev/null +++ b/core/sys/posix/netinet_tcp.odin @@ -0,0 +1,11 @@ +package posix + +// netinet/tcp.h - definitions for the Internet Transmission Control Protocol (TCP) + +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + + TCP_NODELAY :: 0x01 + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/poll.odin b/core/sys/posix/poll.odin new file mode 100644 index 000000000..3e825e009 --- /dev/null +++ b/core/sys/posix/poll.odin @@ -0,0 +1,78 @@ +package posix + +import "base:intrinsics" + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// poll.h - definitions for the poll() function + +foreign lib { + /* + For each pointer in fds, poll() shall examine the given descriptor for the events. + poll will identify on which descriptors writes or reads can be done. + + Returns: -1 (setting errno) on failure, 0 on timeout, the amount of fds that have been changed on success. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html ]] + */ + poll :: proc(fds: [^]pollfd, nfds: nfds_t, timeout: c.int) -> c.int --- +} + +nfds_t :: c.uint + +Poll_Event_Bits :: enum c.short { + // Data other than high-priority data may be read without blocking. + IN = log2(POLLIN), + // Normal data may be read without blocking. + RDNORM = log2(POLLRDNORM), + // Priority data may be read without blocking. + RDBAND = log2(POLLRDBAND), + // High priority data may be read without blocking. + PRI = log2(POLLPRI), + + // Normal data may be written without blocking. + OUT = log2(POLLOUT), + // Equivalent to POLLOUT. + WRNORM = log2(POLLWRNORM), + // Priority data may be written. + WRBAND = log2(POLLWRBAND), + + // An error has occurred (revents only). + ERR = log2(POLLERR), + // Device hsa been disconnected (revents only). + HUP = log2(POLLHUP), + // Invalid fd member (revents only). + NVAL = log2(POLLNVAL), +} +Poll_Event :: bit_set[Poll_Event_Bits; c.short] + +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + + pollfd :: struct { + fd: FD, /* [PSX] the following descriptor being polled */ + events: Poll_Event, /* [PSX] the input event flags */ + revents: Poll_Event, /* [PSX] the output event flags */ + } + + POLLIN :: 0x0001 + POLLRDNORM :: 0x0040 + POLLRDBAND :: 0x0080 + POLLPRI :: 0x0002 + POLLOUT :: 0x0004 + POLLWRNORM :: POLLOUT + POLLWRBAND :: 0x0100 + + POLLERR :: 0x0008 + POLLHUP :: 0x0010 + POLLNVAL :: 0x0020 + +} else { + #panic("posix is unimplemented for the current target") +} + diff --git a/core/sys/posix/posix.odin b/core/sys/posix/posix.odin new file mode 100644 index 000000000..eb0109a3e --- /dev/null +++ b/core/sys/posix/posix.odin @@ -0,0 +1,26 @@ +package posix + +import "base:intrinsics" + +import "core:c" + +result :: enum c.int { + // Use `errno` and `strerror` for more information. + FAIL = -1, + // Operation succeeded. + OK = 0, +} + +FD :: distinct c.int + +@(private) +log2 :: intrinsics.constant_log2 + +when ODIN_OS == .Darwin && ODIN_ARCH == .amd64 { + @(private) + INODE_SUFFIX :: "$INODE64" +} else { + @(private) + INODE_SUFFIX :: "" +} + diff --git a/core/sys/posix/pthread.odin b/core/sys/posix/pthread.odin new file mode 100644 index 000000000..e264f6f6c --- /dev/null +++ b/core/sys/posix/pthread.odin @@ -0,0 +1,518 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD { + foreign import lib "system:pthread" +} else { + foreign import lib "system:c" +} + +// pthread.h - threads + +// NOTE: mutexes, rwlock, condition variables, once and barriers are left out in favour of `core:sync`. + +foreign lib { + /* + Initializes a thread attributes object. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_init.html ]] + */ + pthread_attr_init :: proc(attr: ^pthread_attr_t) -> Errno --- + + /* + Destroys a thread attributes object. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_init.html ]] + */ + pthread_attr_destroy :: proc(attr: ^pthread_attr_t) -> Errno --- + + /* + The detachstate attribute controls whether the thread is created in a detached state. + If the thread is created detached, then use of the ID of the newly created thread is an error. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_getdetachstate.html ]] + */ + pthread_attr_getdetachstate :: proc(attr: ^pthread_attr_t, detachstate: ^Detach_State) -> Errno --- + + /* + The detachstate attribute controls whether the thread is created in a detached state. + If the thread is created detached, then use of the ID of the newly created thread is an error. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_getdetachstate.html ]] + */ + pthread_attr_setdetachstate :: proc(attr: ^pthread_attr_t, detachstate: Detach_State) -> Errno --- + + /* + The guardsize attribute controls the size of the guard area for the created thread's stack. + The guardsize attribute provides protection against overflow of the stack pointer. + If a thread's stack is created with guard protection, the implementation allocates extra memory + at the overflow end of the stack as a buffer against stack overflow of the stack pointer. + If an application overflows into this buffer an error shall result (possibly in a SIGSEGV signal being delivered to the thread). + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_setguardsize.html ]] + */ + pthread_attr_getguardsize :: proc(attr: ^pthread_attr_t, guardsize: ^c.size_t) -> Errno --- + + /* + The guardsize attribute controls the size of the guard area for the created thread's stack. + The guardsize attribute provides protection against overflow of the stack pointer. + If a thread's stack is created with guard protection, the implementation allocates extra memory + at the overflow end of the stack as a buffer against stack overflow of the stack pointer. + If an application overflows into this buffer an error shall result (possibly in a SIGSEGV signal being delivered to the thread). + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_setguardsize.html ]] + */ + pthread_attr_setguardsize :: proc(attr: ^pthread_attr_t, guardsize: c.size_t) -> Errno --- + + /* + When the attributes objects are used by pthread_create(), the inheritsched attribute determines + how the other scheduling attributes of the created thread shall be set. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_setinheritsched.html ]] + */ + pthread_attr_getinheritsched :: proc(attr: ^pthread_attr_t, inheritsched: ^Inherit_Sched) -> Errno --- + + /* + When the attributes objects are used by pthread_create(), the inheritsched attribute determines + how the other scheduling attributes of the created thread shall be set. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_setinheritsched.html ]] + */ + pthread_attr_setinheritsched :: proc(attr: ^pthread_attr_t, inheritsched: Inherit_Sched) -> Errno --- + + /* + Gets the scheduling param. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_setschedparam.html ]] + */ + pthread_attr_getschedparam :: proc(attr: ^pthread_attr_t, param: ^sched_param) -> Errno --- + + /* + Sets the scheduling param. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_setschedparam.html ]] + */ + pthread_attr_setschedparam :: proc(attr: ^pthread_attr_t, param: ^sched_param) -> Errno --- + + /* + Gets the scheduling poicy. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_getschedpolicy.html ]] + */ + pthread_attr_getschedpolicy :: proc(attr: ^pthread_attr_t, policy: ^Sched_Policy) -> Errno --- + + /* + Sets the scheduling poicy. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_getschedpolicy.html ]] + */ + pthread_attr_setschedpolicy :: proc(attr: ^pthread_attr_t, policy: Sched_Policy) -> Errno --- + + /* + Gets the contention scope. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_getscope.html ]] + */ + pthread_attr_getscope :: proc(attr: ^pthread_attr_t, contentionscope: ^Thread_Scope) -> Errno --- + + /* + Sets the contention scope. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_getscope.html ]] + */ + pthread_attr_setscope :: proc(attr: ^pthread_attr_t, contentionscope: ^Thread_Scope) -> Errno --- + + /* + Get the area of storage to be used for the created thread's stack. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_getstack.html ]] + */ + pthread_attr_getstack :: proc(attr: ^pthread_attr_t, stackaddr: ^[^]byte, stacksize: ^c.size_t) -> Errno --- + + /* + Specify the area of storage to be used for the created thread's stack. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_getstack.html ]] + */ + pthread_attr_setstack :: proc(attr: ^pthread_attr_t, stackaddr: [^]byte, stacksize: c.size_t) -> Errno --- + + /* + Gets the stack size. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_getstacksize.html ]] + */ + pthread_attr_getstacksize :: proc(attr: ^pthread_attr_t, stacksize: ^c.size_t) -> Errno --- + + /* + Sets the stack size. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_getstacksize.html ]] + */ + pthread_attr_setstacksize :: proc(attr: ^pthread_attr_t, stacksize: c.size_t) -> Errno --- + + /* + Register fork handlers to be called before and after fork(). + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_atfork.html ]] + */ + pthread_atfork :: proc(prepare: proc "c" (), parent: proc "c" (), child: proc "c" ()) -> Errno --- + + + /* + Cancel the execution of a thread. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cancel.html ]] + */ + pthread_cancel :: proc(thread: pthread_t) -> Errno --- + + /* + Creates a new thread with the given attributes. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_create.html ]] + */ + pthread_create :: proc( + thread: ^pthread_t, + attr: ^pthread_attr_t, + start_routine: proc "c" (arg: rawptr) -> rawptr, + arg: rawptr, + ) -> Errno --- + + + /* + Indicate that storage for the thread can be reclaimed when the thread terminates. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_detach.html ]] + */ + pthread_detach :: proc(thread: pthread_t) -> Errno --- + + /* + Compare thread IDs. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_equal.html ]] + */ + pthread_equal :: proc(t1: pthread_t, t2: pthread_t) -> b32 --- + + /* + Terminates the calling thread and make the given value available to any successfull join calls. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_exit.html ]] + */ + pthread_exit :: proc(value_ptr: rawptr) -> ! --- + + /* + Gets the current concurrency hint. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_getconcurrency.html ]] + */ + pthread_getconcurrency :: proc() -> c.int --- + + /* + Sets the current desired concurrency hint. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_getconcurrency.html ]] + */ + pthread_setconcurrency :: proc(new_level: c.int) -> Errno --- + + /* + Access a thread CPU-time clock. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_getcpuclockid.html ]] + */ + pthread_getcpuclockid :: proc(thread_id: pthread_t, clock_id: ^clockid_t) -> Errno --- + + /* + Gets the scheduling policy and parameters. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_getschedparam.html ]] + */ + pthread_getschedparam :: proc(thread: pthread_t, policy: ^Sched_Policy, param: ^sched_param) -> Errno --- + + /* + Sets the scheduling policy and parameters. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_getschedparam.html ]] + */ + pthread_setschedparam :: proc(thread: pthread_t, policy: Sched_Policy, param: ^sched_param) -> Errno --- + + /* + Creates a thread-specific data key visible to all threads in the process. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_key_create.html ]] + */ + pthread_key_create :: proc(key: ^pthread_key_t, destructor: proc "c" (value: rawptr) = nil) -> Errno --- + + /* + Deletes a thread-specific data key visible to all threads in the process. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_key_delete.html ]] + */ + pthread_key_delete :: proc(key: pthread_key_t) -> Errno --- + + /* + Returns the value currently bound to the specified key on behalf of the calling thread. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_getspecific.html ]] + */ + pthread_getspecific :: proc(key: pthread_key_t) -> rawptr --- + + /* + Sets the value currently bound to the specified key on behalf of the calling thread. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_getspecific.html ]] + */ + pthread_setspecific :: proc(key: pthread_key_t, value: rawptr) -> Errno --- + + /* + Suspends execution of the calling thread until the target thread terminates. + + Example: + ar: [10_000]i32 + + sb1 := ar[:5_000] + sb2 := ar[5_000:] + + th1, th2: posix.pthread_t + + posix.pthread_create(&th1, nil, incer, &sb1) + posix.pthread_create(&th2, nil, incer, &sb2) + + posix.pthread_join(th1) + posix.pthread_join(th2) + + incer :: proc "c" (arg: rawptr) -> rawptr { + sb := (^[]i32)(arg) + for &val in sb { + val += 1 + } + + return nil + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_join.html ]] + */ + pthread_join :: proc(thread: pthread_t, value_ptr: ^rawptr = nil) -> Errno --- + + /* + Get the calling thread ID. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_self.html ]] + */ + pthread_self :: proc() -> pthread_t --- + + /* + Atomically set the calling thread's cancelability and return the previous value. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_setcancelstate.html ]] + */ + pthread_setcancelstate :: proc(state: Cancel_State, oldstate: ^Cancel_State) -> Errno --- + + /* + Atomically set the calling thread's cancel type and return the previous value. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_setcancelstate.html ]] + */ + pthread_setcanceltype :: proc(type: Cancel_Type, oldtype: ^Cancel_Type) -> Errno --- + + + /* + Creates a cancellation point in the calling thread. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_testcancel.html ]] + */ + pthread_testcancel :: proc() --- + + /* + Sets the scheduling priority for the thread given. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_setschedprio.html ]] + */ + pthread_setschedprio :: proc(thread: pthread_t, prio: c.int) -> Errno --- +} + +Detach_State :: enum c.int { + // Causes all threads to be in the joinable state. + CREATE_JOINABLE = PTHREAD_CREATE_JOINABLE, + // Causes all threads to be in the detached state. + CREATE_DETACHED = PTHREAD_CREATE_DETACHED, +} + +Inherit_Sched :: enum c.int { + // Threads inherit from the creating thread. + INHERIT_SCHED = PTHREAD_INHERIT_SCHED, + // Threads scheduling shall be set to the corresponding values from the attributes object. + EXPLICIT_SCHED = PTHREAD_EXPLICIT_SCHED, +} + +Thread_Scope :: enum c.int { + // System scheduling contention scope. + SYSTEM = PTHREAD_SCOPE_SYSTEM, + // Process scheduling contention scope. + PROCESS = PTHREAD_SCOPE_PROCESS, +} + +Cancel_State :: enum c.int { + ENABLE = PTHREAD_CANCEL_ENABLE, + DISABLE = PTHREAD_CANCEL_DISABLE, +} + +Cancel_Type :: enum c.int { + DEFERRED = PTHREAD_CANCEL_DEFERRED, + ASYNCHRONOUS = PTHREAD_CANCEL_ASYNCHRONOUS, +} + +when ODIN_OS == .Darwin { + + PTHREAD_CANCEL_ASYNCHRONOUS :: 0x00 + PTHREAD_CANCEL_DEFERRED :: 0x02 + + PTHREAD_CANCEL_DISABLE :: 0x00 + PTHREAD_CANCEL_ENABLE :: 0x01 + + PTHREAD_CANCELED :: rawptr(uintptr(1)) + + PTHREAD_CREATE_DETACHED :: 2 + PTHREAD_CREATE_JOINABLE :: 1 + + PTHREAD_EXPLICIT_SCHED :: 2 + PTHREAD_INHERIT_SCHED :: 1 + + PTHREAD_PRIO_INHERIT :: 1 + PTHREAD_PRIO_NONE :: 0 + PTHREAD_PRIO_PROTECT :: 2 + + PTHREAD_PROCESS_SHARED :: 1 + PTHREAD_PROCESS_PRIVATE :: 2 + + PTHREAD_SCOPE_PROCESS :: 2 + PTHREAD_SCOPE_SYSTEM :: 1 + + pthread_t :: distinct u64 + + pthread_attr_t :: struct { + __sig: c.long, + __opaque: [56]c.char, + } + + pthread_key_t :: distinct c.ulong + + sched_param :: struct { + sched_priority: c.int, /* [PSX] process or thread execution scheduling priority */ + _: [4]c.char, + } + +} else when ODIN_OS == .FreeBSD { + + PTHREAD_CANCEL_ASYNCHRONOUS :: 0x02 + PTHREAD_CANCEL_DEFERRED :: 0x00 + + PTHREAD_CANCEL_DISABLE :: 0x01 + PTHREAD_CANCEL_ENABLE :: 0x00 + + PTHREAD_CANCELED :: rawptr(uintptr(1)) + + PTHREAD_CREATE_DETACHED :: 1 + PTHREAD_CREATE_JOINABLE :: 0 + + PTHREAD_EXPLICIT_SCHED :: 0 + PTHREAD_INHERIT_SCHED :: 4 + + PTHREAD_PRIO_INHERIT :: 1 + PTHREAD_PRIO_NONE :: 0 + PTHREAD_PRIO_PROTECT :: 2 + + PTHREAD_PROCESS_SHARED :: 0 + PTHREAD_PROCESS_PRIVATE :: 1 + + PTHREAD_SCOPE_PROCESS :: 0 + PTHREAD_SCOPE_SYSTEM :: 2 + + pthread_t :: distinct u64 + + pthread_attr_t :: distinct rawptr + + pthread_key_t :: distinct c.int + + sched_param :: struct { + sched_priority: c.int, /* [PSX] process or thread execution scheduling priority */ + } + +} else when ODIN_OS == .NetBSD { + + PTHREAD_CANCEL_ASYNCHRONOUS :: 1 + PTHREAD_CANCEL_DEFERRED :: 0 + + PTHREAD_CANCEL_DISABLE :: 1 + PTHREAD_CANCEL_ENABLE :: 0 + + PTHREAD_CANCELED :: rawptr(uintptr(1)) + + PTHREAD_CREATE_DETACHED :: 1 + PTHREAD_CREATE_JOINABLE :: 0 + + PTHREAD_EXPLICIT_SCHED :: 1 + PTHREAD_INHERIT_SCHED :: 0 + + PTHREAD_PRIO_INHERIT :: 1 + PTHREAD_PRIO_NONE :: 0 + PTHREAD_PRIO_PROTECT :: 2 + + PTHREAD_PROCESS_SHARED :: 1 + PTHREAD_PROCESS_PRIVATE :: 0 + + PTHREAD_SCOPE_PROCESS :: 0 + PTHREAD_SCOPE_SYSTEM :: 1 + + pthread_t :: distinct rawptr + + pthread_attr_t :: struct { + pta_magic: c.uint, + pta_flags: c.int, + pta_private: rawptr, + } + + pthread_key_t :: distinct c.int + + sched_param :: struct { + sched_priority: c.int, /* [PSX] process or thread execution scheduling priority */ + } + +} else when ODIN_OS == .OpenBSD { + + PTHREAD_CANCEL_ASYNCHRONOUS :: 2 + PTHREAD_CANCEL_DEFERRED :: 0 + + PTHREAD_CANCEL_DISABLE :: 1 + PTHREAD_CANCEL_ENABLE :: 0 + + PTHREAD_CANCELED :: rawptr(uintptr(1)) + + PTHREAD_CREATE_DETACHED :: 0x1 + PTHREAD_CREATE_JOINABLE :: 0 + + PTHREAD_EXPLICIT_SCHED :: 0 + PTHREAD_INHERIT_SCHED :: 0x4 + + PTHREAD_PRIO_INHERIT :: 1 + PTHREAD_PRIO_NONE :: 0 + PTHREAD_PRIO_PROTECT :: 2 + + PTHREAD_PROCESS_SHARED :: 0 + PTHREAD_PROCESS_PRIVATE :: 1 + + PTHREAD_SCOPE_PROCESS :: 0 + PTHREAD_SCOPE_SYSTEM :: 0x2 + + pthread_t :: distinct rawptr + pthread_attr_t :: distinct rawptr + pthread_key_t :: distinct c.int + + sched_param :: struct { + sched_priority: c.int, /* [PSX] process or thread execution scheduling priority */ + } + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/pwd.odin b/core/sys/posix/pwd.odin new file mode 100644 index 000000000..546d58309 --- /dev/null +++ b/core/sys/posix/pwd.odin @@ -0,0 +1,168 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// pwd.h - password structure + +foreign lib { + /* + Rewinds the user database so that the next getpwent() returns the first entry. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setpwent.html ]] + */ + setpwent :: proc() --- + + /* + Returns the current entry in the user database. + + Returns: nil (setting errno) on error, nil (not setting errno) on success. + + Example: + posix.setpwent() + defer posix.endpwent() + for e := posix.getpwent(); e != nil; e = posix.getpwent() { + fmt.println(e) + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setpwent.html ]] + */ + @(link_name=LGETPWENT) + getpwent :: proc() -> ^passwd --- + + /* + Closes the user database. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setpwent.html ]] + */ + endpwent :: proc() --- + + /* + Searches the database for an entry with a matching name. + + Returns: nil (setting errno) on error, nil (not setting errno) on success. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwnam.html ]] + */ + @(link_name=LGETPWNAM) + getpwnam :: proc(name: cstring) -> ^passwd --- + + /* + Searches the database for an entry with a matching name. + Populating the pwd fields and using the buffer to allocate strings into. + Setting result to nil on failure and to the address of pwd otherwise. + + ERANGE will be returned if there is not enough space in buffer. + sysconf(_SC_GETPW_R_SIZE_MAX) can be called for the suggested size of this buffer, note that it could return -1. + + Example: + length := posix.sysconf(._GETPW_R_SIZE_MAX) + length = length == -1 ? 1024 : length + + buffer: [dynamic]byte + defer delete(buffer) + + result: posix.passwd + resultp: ^posix.passwd + errno: posix.Errno + for { + if err := resize(&buffer, length); err != nil { + fmt.panicf("allocation failure: %v", err) + } + + errno = posix.getpwnam_r("root", &result, raw_data(buffer), len(buffer), &resultp) + if errno != .ERANGE { + break + } + } + + if errno != .NONE { + panic(string(posix.strerror(errno))) + } + + fmt.println(result) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwnam.html ]] + */ + @(link_name=LGETPWNAMR) + getpwnam_r :: proc(name: cstring, pwd: ^passwd, buffer: [^]byte, bufsize: c.size_t, result: ^^passwd) -> Errno --- + + /* + Searches the database for an entry with a matching uid. + + Returns: nil (setting errno) on error, nil (not setting errno) on success. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid.html ]] + */ + @(link_name=LGETPWUID) + getpwuid :: proc(uid: uid_t) -> ^passwd --- + + /* + Searches the database for an entry with a matching uid. + Populating the pwd fields and using the buffer to allocate strings into. + Setting result to nil on failure and to the address of pwd otherwise. + + ERANGE will be returned if there is not enough space in buffer. + sysconf(_SC_GETPW_R_SIZE_MAX) can be called for the suggested size of this buffer, note that it could return -1. + + See the example for getpwnam_r. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html ]] + */ + @(link_name=LGETPWUIDR) + getpwuid_r :: proc(uid: uid_t, pwd: ^passwd, buffer: [^]byte, bufsize: c.size_t, result: ^^passwd) -> Errno --- +} + +when ODIN_OS == .NetBSD { + @(private) LGETPWENT :: "__getpwent50" + @(private) LGETPWNAM :: "__getpwnam50" + @(private) LGETPWNAMR :: "__getpwnam_r50" + @(private) LGETPWUID :: "__getpwuid50" + @(private) LGETPWUIDR :: "__getpwuid_r50" +} else { + @(private) LGETPWENT :: "getpwent" + @(private) LGETPWNAM :: "getpwnam" + @(private) LGETPWNAMR :: "getpwnam_r" + @(private) LGETPWUID :: "getpwuid" + @(private) LGETPWUIDR :: "getpwuid_r" +} + +when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + + passwd :: struct { + pw_name: cstring, /* [PSX] user name */ + pw_passwd: cstring, /* encrypted password */ + pw_uid: uid_t, /* [PSX] user uid */ + pw_gid: gid_t, /* [PSX] user gid */ + pw_change: time_t, /* password change time */ + pw_class: cstring, /* user access class */ + pw_gecos: cstring, /* Honeywell login info */ + pw_dir: cstring, /* [PSX] home directory */ + pw_shell: cstring, /* [PSX] default shell */ + pw_expire: time_t, /* account expiration */ + } + +} else when ODIN_OS == .FreeBSD { + + passwd :: struct { + pw_name: cstring, /* [PSX] user name */ + pw_passwd: cstring, /* encrypted password */ + pw_uid: uid_t, /* [PSX] user uid */ + pw_gid: gid_t, /* [PSX] user gid */ + pw_change: time_t, /* password change time */ + pw_class: cstring, /* user access class */ + pw_gecos: cstring, /* Honeywell login info */ + pw_dir: cstring, /* [PSX] home directory */ + pw_shell: cstring, /* [PSX] default shell */ + pw_expire: time_t, /* account expiration */ + pw_fields: c.int, + } + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/sched.odin b/core/sys/posix/sched.odin new file mode 100644 index 000000000..6623ba6e6 --- /dev/null +++ b/core/sys/posix/sched.odin @@ -0,0 +1,105 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// sched.h - execution scheduling + +foreign lib { + /* + Returns the minimum for the given scheduling policy. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_get_priority_max.html ]] + */ + sched_get_priority_max :: proc(policy: Sched_Policy) -> c.int --- + + /* + Returns the maximum for the given scheduling policy. + + Returns: -1 (setting errno) on failure, the maximum on success + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_get_priority_max.html ]] + */ + sched_get_priority_min :: proc(policy: Sched_Policy) -> c.int --- + + /* + Forces the running thread to relinquish the processor until it again becomes the head of its thread list. + */ + sched_yield :: proc() -> result --- + + /* NOTE: unimplemented on darwin (I think?). + /* + Get the scheduling params of a process, pid of 0 will return that of the current process. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_getparam.html ]] + */ + sched_getparam :: proc(pid: pid_t, param: ^sched_param) -> result --- + /* + Sets the scheduling parameters of the given process, pid of 0 will set that of the current process. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_setparam.html ]] + */ + sched_setparam :: proc(pid: pid_t, param: ^sched_param) -> result --- + + /* + Returns the scheduling policy of a process, pid of 0 will return that of the current process. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_getscheduler.html ]] + */ + sched_getscheduler :: proc(pid: pid_t) -> Sched_Policy --- + + /* + Sets the scheduling policy and parameters of the process, pid 0 will be the current process. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_setscheduler.html ]] + */ + sched_setscheduler :: proc(pid: pid_t, policy: Sched_Policy, param: ^sched_param) -> result --- + + /* + Updates the timespec structure to contain the current execution time limit for the process. + pid of 0 will return that of the current process. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_rr_get_interval.html ]] + */ + sched_rr_get_interval :: proc(pid: pid_t, interval: ^timespec) -> result --- + */ +} + +Sched_Policy :: enum c.int { + // Error condition of sched_getscheduler. + ERROR = -1, + // First in-first out (FIFO) scheduling policy. + FIFO = SCHED_FIFO, + // Round robin scheduling policy. + RR = SCHED_RR, + // Another scheduling policy. + OTHER = SCHED_OTHER, +} + +when ODIN_OS == .Darwin { + + SCHED_FIFO :: 4 + SCHED_RR :: 2 + // SCHED_SPORADIC :: 3 NOTE: not a thing on freebsd, netbsd and probably others, leaving it out + SCHED_OTHER :: 1 + +} else when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD { + + SCHED_FIFO :: 1 + SCHED_RR :: 3 + SCHED_OTHER :: 2 + +} else when ODIN_OS == .NetBSD { + + SCHED_OTHER :: 0 + SCHED_FIFO :: 1 + SCHED_RR :: 2 + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/setjmp.odin b/core/sys/posix/setjmp.odin new file mode 100644 index 000000000..cb1dad184 --- /dev/null +++ b/core/sys/posix/setjmp.odin @@ -0,0 +1,58 @@ +package posix + +import "core:c" +import "core:c/libc" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// setjmp.h - stack environment declarations + +foreign lib { + /* + Equivalent to longjmp() but must not touch signals. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/_longjmp.html ]] + */ + _longjmp :: proc(env: ^jmp_buf, val: c.int) -> ! --- + + /* + Equivalent to setjmp() but must not touch signals. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/_longjmp.html ]] + */ + _setjmp :: proc(env: ^jmp_buf) -> c.int --- + + /* + Equivalent to longjmp() but restores saved signal masks. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/siglongjump.html ]] + */ + @(link_name=LSIGLONGJMP) + siglongjmp :: proc(env: ^sigjmp_buf, val: c.int) -> ! --- + + /* + Equivalent to setjmp() but restores saved signal masks. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/siglongjump.html ]] + */ + @(link_name=LSIGSETJMP) + sigsetjmp :: proc(env: ^sigjmp_buf, savemask: b32) -> c.int --- +} + +jmp_buf :: libc.jmp_buf +sigjmp_buf :: distinct jmp_buf + +longjmp :: libc.longjmp +setjmp :: libc.setjmp + +when ODIN_OS == .NetBSD { + @(private) LSIGSETJMP :: "__sigsetjmp14" + @(private) LSIGLONGJMP :: "__siglongjmp14" +} else { + @(private) LSIGSETJMP :: "sigsetjmp" + @(private) LSIGLONGJMP :: "siglongjmp" +} diff --git a/core/sys/posix/signal.odin b/core/sys/posix/signal.odin new file mode 100644 index 000000000..3bfd662f0 --- /dev/null +++ b/core/sys/posix/signal.odin @@ -0,0 +1,1131 @@ +package posix + +import "base:intrinsics" + +import "core:c" +import "core:c/libc" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// signal.h - signals + +foreign lib { + // LIBC: + + /* + Set a signal handler. + + func can either be: + - `auto_cast posix.SIG_DFL` setting the default handler for that specific signal + - `auto_cast posix.SIG_IGN` causing the specific signal to be ignored + - a custom signal handler + + Returns: SIG_ERR (setting errno), the last value of func on success + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/signal.html ]] + */ + signal :: proc(sig: Signal, func: proc "c" (Signal)) -> proc "c" (Signal) --- + + /* + Raises a signal, calling its handler and then returning. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/raise.html ]] + */ + raise :: proc(sig: Signal) -> result --- + + // POSIX: + + /* + Raise a signal to the process/group specified by pid. + + If sig is 0, this function can be used to check if the pid is just checked for validity. + + If pid is -1, the signal is sent to all processes that the current process has permission to send. + + If pid is negative (not -1), the signal is sent to all processes in the group identifier by the + absolute value. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/kill.html ]] + */ + kill :: proc(pid: pid_t, sig: Signal) -> result --- + + /* + Shorthand for `kill(-pgrp, sig)` which will kill all processes in the given process group. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/killpg.html ]] + */ + killpg :: proc(pgrp: pid_t, sig: Signal) -> result --- + + /* + Writes a language-dependent message to stderror. + + Example: + posix.psignal(.SIGSEGV, "that didn't go well") + + Possible Output: + that didn't go well: Segmentation fault + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/psignal.html ]] + */ + psignal :: proc(signum: Signal, message: cstring) --- + + /* + Send a signal to a thread. + + As with kill, if sig is 0, only validation (of the pthread_t given) is done and no signal is sent. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_kill.html ]] + */ + pthread_kill :: proc(thread: pthread_t, sig: Signal) -> Errno --- + + /* + Examine and change blocked signals. + + Equivalent to sigprocmask(), without the restriction that the call be made in a single-threaded process. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_sigmask.html ]] + */ + pthread_sigmask :: proc(how: Sig, set: ^sigset_t, oset: ^sigset_t) -> Errno --- + + /* + Examine and change blocked signals in a single-threaded process. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_sigmask.html ]] + */ + @(link_name=LSIGPROCMASK) + sigprocmask :: proc(how: Sig, set: ^sigset_t, oldset: ^sigset_t) -> result --- + + /* + Examine and change a signal action. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaction.html ]] + */ + @(link_name=LSIGACTION) + sigaction :: proc(sig: Signal, act: ^sigaction_t, oact: ^sigaction_t) -> result --- + + @(link_name=LSIGADDSET) + sigaddset :: proc(set: ^sigset_t, signo: Signal) -> result --- + @(link_name=LSIGDELSET) + sigdelset :: proc(^sigset_t, Signal) -> c.int --- + @(link_name=LSIGEMPTYSET) + sigemptyset :: proc(^sigset_t) -> c.int --- + @(link_name=LSIGFILLSET) + sigfillset :: proc(^sigset_t) -> c.int --- + + /* + Set and get the signal alternate stack context. + + Example: + sigstk := posix.stack_t { + ss_sp = make([^]byte, posix.SIGSTKSZ) or_else panic("allocation failure"), + ss_size = posix.SIGSTKSZ, + ss_flags = {}, + } + if posix.sigaltstack(&sigstk, nil) != .OK { + fmt.panicf("sigaltstack failure: %v", posix.strerror(posix.errno())) + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaltstack.html ]] + */ + @(link_name=LSIGALTSTACK) + sigaltstack :: proc(ss: ^stack_t, oss: ^stack_t) -> result --- + + /* + Adds sig to the signal mask of the calling process. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sighold.html ]] + */ + sighold :: proc(sig: Signal) -> result --- + + /* + Sets the disposition of sig to SIG_IGN. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sighold.html ]] + */ + sigignore :: proc(sig: Signal) -> result --- + + /* + Removes sig from the signal mask of the calling process and suspend the calling process until + a signal is received. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sighold.html ]] + */ + sigpause :: proc(sig: Signal) -> result --- + + /* + Removes sig from the signal mask of the calling process. + + Returns: always -1. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sighold.html ]] + */ + sigrelse :: proc(sig: Signal) -> result --- + + /* + Changes the restart behavior when a function is interrupted by the specified signal. + + If flag is true, SA_RESTART is removed, added otherwise. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/siginterrupt.html ]] + */ + siginterrupt :: proc(sig: Signal, flag: b32) -> result --- + + /* + Test for a signal in a signal set. + + Returns: 1 if it is a member, 0 if not, -1 (setting errno) on failure + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigismember.html ]] + */ + @(link_name=LSIGISMEMBER) + sigismember :: proc(set: ^sigset_t, signo: Signal) -> c.int --- + + /* + Stores the set of signals that are blocked from delivery to the calling thread and that are pending + on the process or the calling thread. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigpending.html ]] + */ + @(link_name=LSIGPENDING) + sigpending :: proc(set: ^sigset_t) -> result --- + + /* + Wait for one of the given signals. + + Returns: always -1 + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigsuspend.html ]] + */ + @(link_name=LSIGSUSPEND) + sigsuspend :: proc(sigmask: ^sigset_t) -> result --- + + /* + Wait for queued signals. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigwait.html ]] + */ + sigwait :: proc(set: ^sigset_t, sig: ^Signal) -> Errno --- + + /* NOTE: unimplemented on darwin. + + void psiginfo(const siginfo_t *, const char *); + int sigqueue(pid_t, int, union sigval); + void (*sigset(int, void (*)(int)))(int); + int sigsuspend(const sigset_t *); + int sigtimedwait(const sigset_t *restrict, siginfo_t *restrict, + const struct timespec *restrict); + int sigwaitinfo(const sigset_t *restrict, siginfo_t *restrict); + */ +} + +sigval :: struct #raw_union { + sigval_int: c.int, /* [PSX] integer signal value */ + sigval_ptr: rawptr, /* [PSX] pointer signal value */ +} + +Signal :: enum c.int { + NONE, + + // LIBC: + + // Process abort signal. + SIGABRT = SIGABRT, + // Erronous arithemtic operation. + SIGFPE = SIGFPE, + // Illegal instruction. + SIGILL = SIGILL, + // Terminal interrupt signal. + SIGINT = SIGINT, + // Invalid memory reference. + SIGSEGV = SIGSEGV, + // Termination signal. + SIGTERM = SIGTERM, + + // POSIX: + + // Process abort signal. + SIGALRM = SIGALRM, + // Access to an undefined portion of a memory object. + SIGBUS = SIGBUS, + // Child process terminated, stopped, or continued. + SIGCHLD = SIGCHLD, + // Continue execution, if stopped. + SIGCONT = SIGCONT, + // Hangup. + SIGHUP = SIGHUP, + // Kill (cannot be caught or ignored). + SIGKILL = SIGKILL, + // Write on a pipe with no one to read it. + SIGPIPE = SIGPIPE, + // Terminal quit signal. + SIGQUIT = SIGQUIT, + // Stop executing (cannot be caught or ignored). + SIGSTOP = SIGSTOP, + // Terminal stop process. + SIGTSTP = SIGTSTP, + // Background process attempting read. + SIGTTIN = SIGTTIN, + // Background process attempting write. + SIGTTOU = SIGTTOU, + // User-defined signal 1. + SIGUSR1 = SIGUSR1, + // User-defined signal 2. + SIGUSR2 = SIGUSR2, + // Pollable event. + SIGPOLL = SIGPOLL, + // Profiling timer expired. + SIGPROF = SIGPROF, + // Bad system call. + SIGSYS = SIGSYS, + // Trace/breakpoint trap. + SIGTRAP = SIGTRAP, + // High bandwidth data is available at a socket. + SIGURG = SIGURG, + // Virtual timer expired. + SIGVTALRM = SIGVTALRM, + // CPU time limit exceeded. + SIGXCPU = SIGXCPU, + // File size limit exceeded. + SIGXFSZ = SIGXFSZ, +} + +ILL_Code :: enum c.int { + // Illegal opcode. + ILLOPC = ILL_ILLOPC, + // Illegal operand. + ILLOPN = ILL_ILLOPN, + // Illegal addressing mode. + ILLADR = ILL_ILLADR, + // Illegal trap. + ILLTRP = ILL_ILLTRP, + // Priviledged opcode. + PRVOPC = ILL_PRVOPC, + // Priviledged register. + PRVREG = ILL_PRVREG, + // Coprocessor error. + COPROC = ILL_COPROC, + // Internal stack error. + BADSTK = ILL_BADSTK, +} + +FPE_Code :: enum c.int { + // Integer divide by zero. + INTDIV = FPE_INTDIV, + // Integer overflow. + INTOVF = FPE_INTOVF, + // Floating-point divide by zero. + FLTDIV = FPE_FLTDIV, + // Floating-point overflow. + FLTOVF = FPE_FLTOVF, + // Floating-point underflow. + FLTUND = FPE_FLTUND, + // Floating-point inexact result. + FLTRES = FPE_FLTRES, + // Invalid floating-point operation. + FLTINV = FPE_FLTINV, + // Subscript out of range. + FLTSUB = FPE_FLTSUB, +} + +SEGV_Code :: enum c.int { + // Address not mapped to object. + MAPERR = SEGV_MAPERR, + // Invalid permissions for mapped object. + ACCERR = SEGV_ACCERR, +} + +BUS_Code :: enum c.int { + // Invalid address alignment. + ADRALN = BUS_ADRALN, + // Nonexistent physical address. + ADRERR = BUS_ADRERR, + // Object-specific hardware error. + OBJERR = BUS_OBJERR, +} + +TRAP_Code :: enum c.int { + // Process breakpoint. + BRKPT = TRAP_BRKPT, + // Process trace trap. + TRACE = TRAP_TRACE, +} + +CLD_Code :: enum c.int { + // Child has exited.. + EXITED = CLD_EXITED, + // Child has terminated abnormally and did not create a core file. + KILLED = CLD_KILLED, + // Child has terminated abnormally and created a core file. + DUMPED = CLD_DUMPED, + // Traced child trapped. + TRAPPED = CLD_TRAPPED, + // Child has stopped. + STOPPED = CLD_STOPPED, + // Stopped child has continued. + CONTINUED = CLD_CONTINUED, +} + +POLL_Code :: enum c.int { + // Data input is available. + IN = POLL_IN, + // Output buffers available. + OUT = POLL_OUT, + // Input message available. + MSG = POLL_MSG, + // I/O error. + ERR = POLL_ERR, + // High priority input available. + PRI = POLL_PRI, + // Device disconnected. + HUP = POLL_HUP, +} + +Any_Code :: enum c.int { + // Signal sent by kill(). + USER = SI_USER, + // Signal sent by sigqueue(). + QUEUE = SI_QUEUE, + // Signal generated by expiration of a timer set by timer_settime(). + TIMER = SI_TIMER, + // Signal generated by completion of an asynchronous I/O request. + ASYNCIO = SI_ASYNCIO, + // Signal generated by arrival of a message on an empty message queue. + MESGQ = SI_MESGQ, +} + +SA_Flags_Bits :: enum c.int { + // Do not generate SIGCHLD when children stop or stopped children continue. + NOCLDSTOP = log2(SA_NOCLDSTOP), + // Cause signal delivery to occur on an alternate stack. + ONSTACK = log2(SA_ONSTACK), + // Cause signal disposition to be set to SIG_DFL on entry to signal handlers. + RESETHAND = log2(SA_RESETHAND), + // Cause certain functions to become restartable. + RESTART = log2(SA_RESTART), + // Cause extra information to be passed to signal handlers at the time of receipt of a signal. + SIGINFO = log2(SA_SIGINFO), + // Cause implemention not to create zombie processes or status information on child termination. + NOCLDWAIT = log2(SA_NOCLDWAIT), + // Cause signal not to be automatically blocked on entry to signal handler. + SA_NODEFER = log2(SA_NODEFER), +} +SA_Flags :: bit_set[SA_Flags_Bits; c.int] + +SS_Flag_Bits :: enum c.int { + // Process is executing on an alternate signal stack. + ONSTACK = log2(SS_ONSTACK), + // Alternate signal stack is disabled. + DISABLE = log2(SS_DISABLE), +} +SS_Flags :: bit_set[SS_Flag_Bits; c.int] + +Sig :: enum c.int { + // Resulting set is the union of the current set and the signal set and the complement of + // the signal set pointed to by the argument. + BLOCK = SIG_BLOCK, + // Resulting set is the intersection of the current set and the complement of the signal set + // pointed to by the argument. + UNBLOCK = SIG_UNBLOCK, + // Resulting set is the signal set pointed to by the argument. + SETMASK = SIG_SETMASK, +} + +// Request for default signal handling. +SIG_DFL :: libc.SIG_DFL +// Return value from signal() in case of error. +SIG_ERR :: libc.SIG_ERR +// Request that signal be ignored. +SIG_IGN :: libc.SIG_IGN + +SIGABRT :: libc.SIGABRT +SIGFPE :: libc.SIGFPE +SIGILL :: libc.SIGILL +SIGINT :: libc.SIGINT +SIGSEGV :: libc.SIGSEGV +SIGTERM :: libc.SIGTERM + +when ODIN_OS == .NetBSD { + @(private) LSIGPROCMASK :: "__sigprocmask14" + @(private) LSIGACTION :: "__sigaction_siginfo" + @(private) LSIGADDSET :: "__sigaddset14" + @(private) LSIGDELSET :: "__sigdelset14" + @(private) LSIGEMPTYSET :: "__sigemptyset14" + @(private) LSIGFILLSET :: "__sigfillset14" + @(private) LSIGALTSTACK :: "__sigaltstack14" + @(private) LSIGISMEMBER :: "__sigismember14" + @(private) LSIGPENDING :: "__sigpending14" + @(private) LSIGSUSPEND :: "__sigsuspend14" +} else { + @(private) LSIGPROCMASK :: "sigprocmask" + @(private) LSIGACTION :: "sigaction" + @(private) LSIGADDSET :: "sigaddset" + @(private) LSIGDELSET :: "sigdelset" + @(private) LSIGEMPTYSET :: "sigemptyset" + @(private) LSIGFILLSET :: "sigfillset" + @(private) LSIGALTSTACK :: "sigaltstack" + @(private) LSIGISMEMBER :: "sigismember" + @(private) LSIGPENDING :: "sigpending" + @(private) LSIGSUSPEND :: "sigsuspend" +} + +when ODIN_OS == .Darwin { + + // Request that signal be held + SIG_HOLD :: rawptr(uintptr(5)) + + uid_t :: distinct c.uint32_t + sigset_t :: distinct c.uint32_t + + // MOTE: unimplemented on darwin. + // + // SIGRTMIN :: + // SIGRTMAX :: + + SIGHUP :: 1 + SIGQUIT :: 3 + SIGTRAP :: 5 + SIGPOLL :: 7 + SIGKILL :: 9 + SIGBUS :: 10 + SIGSYS :: 12 + SIGPIPE :: 13 + SIGALRM :: 14 + SIGURG :: 16 + SIGCONT :: 19 + SIGSTOP :: 17 + SIGTSTP :: 18 + SIGCHLD :: 20 + SIGTTIN :: 21 + SIGTTOU :: 22 + SIGXCPU :: 24 + SIGXFSZ :: 25 + SIGVTALRM :: 26 + SIGPROF :: 27 + SIGUSR1 :: 30 + SIGUSR2 :: 31 + + // NOTE: this is actually defined as `sigaction`, but due to the function with the same name + // `_t` has been added. + + sigaction_t :: struct { + using _: struct #raw_union { + sa_handler: proc "c" (Signal), /* [PSX] signal-catching function or one of the SIG_IGN or SIG_DFL */ + sa_sigaction: proc "c" (Signal, ^siginfo_t, rawptr), /* [PSX] signal-catching function */ + }, + sa_mask: sigset_t, /* [PSX] set of signals to be blocked during execution of the signal handling function */ + sa_flags: SA_Flags, /* [PSX] special flags */ + } + + SIG_BLOCK :: 1 + SIG_UNBLOCK :: 2 + SIG_SETMASK :: 3 + + SA_NOCLDSTOP :: 0x0008 + SA_ONSTACK :: 0x0001 + SA_RESETHAND :: 0x0004 + SA_RESTART :: 0x0002 + SA_SIGINFO :: 0x0040 + SA_NOCLDWAIT :: 0x0020 + SA_NODEFER :: 0x0010 + + SS_ONSTACK :: 0x0001 + SS_DISABLE :: 0x0004 + + MINSIGSTKSZ :: 32768 + SIGSTKSZ :: 131072 + + stack_t :: struct { + ss_sp: rawptr, /* [PSX] stack base or pointer */ + ss_size: c.size_t, /* [PSX] stack size */ + ss_flags: SS_Flags, /* [PSX] flags */ + } + + siginfo_t :: struct { + si_signo: Signal, /* [PSX] signal number */ + si_errno: Errno, /* [PSX] errno value associated with this signal */ + si_code: struct #raw_union { /* [PSX] specific more detailed codes per signal */ + ill: ILL_Code, + fpe: FPE_Code, + segv: SEGV_Code, + bus: BUS_Code, + trap: TRAP_Code, + chld: CLD_Code, + poll: POLL_Code, + any: Any_Code, + }, + si_pid: pid_t, /* [PSX] sending process ID */ + si_uid: uid_t, /* [PSX] real user ID of sending process */ + si_status: c.int, /* [PSX] exit value of signal */ + si_addr: rawptr, /* [PSX] address of faulting instruction */ + si_value: sigval, /* [PSX] signal value */ + si_band: c.long, /* [PSX] band event for SIGPOLL */ + __pad: [7]c.ulong, + } + + ILL_ILLOPC :: 1 + ILL_ILLOPN :: 4 + ILL_ILLADR :: 5 + ILL_ILLTRP :: 2 + ILL_PRVOPC :: 3 + ILL_PRVREG :: 6 + ILL_COPROC :: 7 + ILL_BADSTK :: 8 + + FPE_INTDIV :: 7 + FPE_INTOVF :: 8 + FPE_FLTDIV :: 1 + FPE_FLTOVF :: 2 + FPE_FLTUND :: 3 + FPE_FLTRES :: 4 + FPE_FLTINV :: 5 + FPE_FLTSUB :: 6 + + SEGV_MAPERR :: 1 + SEGV_ACCERR :: 2 + + BUS_ADRALN :: 1 + BUS_ADRERR :: 2 + BUS_OBJERR :: 3 + + TRAP_BRKPT :: 1 + TRAP_TRACE :: 2 + + CLD_EXITED :: 1 + CLD_KILLED :: 2 + CLD_DUMPED :: 3 + CLD_TRAPPED :: 4 + CLD_STOPPED :: 5 + CLD_CONTINUED :: 6 + + POLL_IN :: 1 + POLL_OUT :: 2 + POLL_MSG :: 3 + POLL_ERR :: 4 + POLL_PRI :: 5 + POLL_HUP :: 6 + + SI_USER :: 0x10001 + SI_QUEUE :: 0x10002 + SI_TIMER :: 0x10003 + SI_ASYNCIO :: 0x10004 + SI_MESGQ :: 0x10005 + +} else when ODIN_OS == .FreeBSD { + + // Request that signal be held + SIG_HOLD :: rawptr(uintptr(3)) + + uid_t :: distinct c.uint32_t + + sigset_t :: struct { + __bits: [4]c.uint32_t, + } + + // MOTE: unimplemented on darwin. + // + // SIGRTMIN :: 65 + // SIGRTMAX :: 126 + + SIGHUP :: 1 + SIGQUIT :: 3 + SIGTRAP :: 5 + SIGPOLL :: 7 + SIGKILL :: 9 + SIGBUS :: 10 + SIGSYS :: 12 + SIGPIPE :: 13 + SIGALRM :: 14 + SIGURG :: 16 + SIGCONT :: 19 + SIGSTOP :: 17 + SIGTSTP :: 18 + SIGCHLD :: 20 + SIGTTIN :: 21 + SIGTTOU :: 22 + SIGXCPU :: 24 + SIGXFSZ :: 25 + SIGVTALRM :: 26 + SIGPROF :: 27 + SIGUSR1 :: 30 + SIGUSR2 :: 31 + + // NOTE: this is actually defined as `sigaction`, but due to the function with the same name + // `_t` has been added. + + sigaction_t :: struct { + using _: struct #raw_union { + sa_handler: proc "c" (Signal), /* [PSX] signal-catching function or one of the SIG_IGN or SIG_DFL */ + sa_sigaction: proc "c" (Signal, ^siginfo_t, rawptr), /* [PSX] signal-catching function */ + }, + sa_flags: SA_Flags, /* [PSX] special flags */ + sa_mask: sigset_t, /* [PSX] set of signals to be blocked during execution of the signal handling function */ + } + + SIG_BLOCK :: 1 + SIG_UNBLOCK :: 2 + SIG_SETMASK :: 3 + + SA_NOCLDSTOP :: 0x0008 + SA_ONSTACK :: 0x0001 + SA_RESETHAND :: 0x0004 + SA_RESTART :: 0x0002 + SA_SIGINFO :: 0x0040 + SA_NOCLDWAIT :: 0x0020 + SA_NODEFER :: 0x0010 + + SS_ONSTACK :: 0x0001 + SS_DISABLE :: 0x0004 + + when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm32 { + MINSIGSTKSZ :: 1024 * 4 + } else when ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 { + MINSIGSTKSZ :: 512 * 4 + } + + SIGSTKSZ :: MINSIGSTKSZ + 32768 + + stack_t :: struct { + ss_sp: rawptr, /* [PSX] stack base or pointer */ + ss_size: c.size_t, /* [PSX] stack size */ + ss_flags: SS_Flags, /* [PSX] flags */ + } + + siginfo_t :: struct { + si_signo: Signal, /* [PSX] signal number */ + si_errno: Errno, /* [PSX] errno value associated with this signal */ + si_code: struct #raw_union { /* [PSX] specific more detailed codes per signal */ + ill: ILL_Code, + fpe: FPE_Code, + segv: SEGV_Code, + bus: BUS_Code, + trap: TRAP_Code, + chld: CLD_Code, + poll: POLL_Code, + any: Any_Code, + }, + si_pid: pid_t, /* [PSX] sending process ID */ + si_uid: uid_t, /* [PSX] real user ID of sending process */ + si_status: c.int, /* [PSX] exit value of signal */ + si_addr: rawptr, /* [PSX] address of faulting instruction */ + si_value: sigval, /* [PSX] signal value */ + using _reason: struct #raw_union { + _fault: struct { + _trapno: c.int, /* machine specific trap code */ + }, + _timer: struct { + _timerid: c.int, + _overrun: c.int, + }, + _mesgq: struct { + _mqd: c.int, + }, + using _poll: struct { + si_band: c.long, /* [PSX] band event for SIGPOLL */ + }, + _capsicum: struct { + _syscall: c.int, /* syscall number for signals delivered as a result of system calls denied by capsicum */ + }, + __spare__: struct { + __spare1__: c.long, + __spare2__: [7]c.int, + }, + }, + } + + ILL_ILLOPC :: 1 + ILL_ILLOPN :: 2 + ILL_ILLADR :: 3 + ILL_ILLTRP :: 4 + ILL_PRVOPC :: 5 + ILL_PRVREG :: 6 + ILL_COPROC :: 7 + ILL_BADSTK :: 8 + + FPE_INTDIV :: 2 + FPE_INTOVF :: 1 + FPE_FLTDIV :: 3 + FPE_FLTOVF :: 4 + FPE_FLTUND :: 5 + FPE_FLTRES :: 6 + FPE_FLTINV :: 7 + FPE_FLTSUB :: 8 + + SEGV_MAPERR :: 1 + SEGV_ACCERR :: 2 + + BUS_ADRALN :: 1 + BUS_ADRERR :: 2 + BUS_OBJERR :: 3 + + TRAP_BRKPT :: 1 + TRAP_TRACE :: 2 + + CLD_EXITED :: 1 + CLD_KILLED :: 2 + CLD_DUMPED :: 3 + CLD_TRAPPED :: 4 + CLD_STOPPED :: 5 + CLD_CONTINUED :: 6 + + POLL_IN :: 1 + POLL_OUT :: 2 + POLL_MSG :: 3 + POLL_ERR :: 4 + POLL_PRI :: 5 + POLL_HUP :: 6 + + SI_USER :: 0x10001 + SI_QUEUE :: 0x10002 + SI_TIMER :: 0x10003 + SI_ASYNCIO :: 0x10004 + SI_MESGQ :: 0x10005 + +} else when ODIN_OS == .NetBSD { + + // Request that signal be held + SIG_HOLD :: rawptr(uintptr(3)) + + uid_t :: distinct c.uint32_t + sigset_t :: struct { + __bits: [4]c.uint32_t, + } + + // MOTE: unimplemented on darwin. + // + // SIGRTMIN :: 33 + // SIGRTMAX :: 63 + + SIGHUP :: 1 + SIGQUIT :: 3 + SIGTRAP :: 5 + SIGPOLL :: 7 + SIGKILL :: 9 + SIGBUS :: 10 + SIGSYS :: 12 + SIGPIPE :: 13 + SIGALRM :: 14 + SIGURG :: 16 + SIGCONT :: 19 + SIGSTOP :: 17 + SIGTSTP :: 18 + SIGCHLD :: 20 + SIGTTIN :: 21 + SIGTTOU :: 22 + SIGXCPU :: 24 + SIGXFSZ :: 25 + SIGVTALRM :: 26 + SIGPROF :: 27 + SIGUSR1 :: 30 + SIGUSR2 :: 31 + + // NOTE: this is actually defined as `sigaction`, but due to the function with the same name + // `_t` has been added. + + sigaction_t :: struct { + using _: struct #raw_union { + sa_handler: proc "c" (Signal), /* [PSX] signal-catching function or one of the SIG_IGN or SIG_DFL */ + sa_sigaction: proc "c" (Signal, ^siginfo_t, rawptr), /* [PSX] signal-catching function */ + }, + sa_mask: sigset_t, /* [PSX] set of signals to be blocked during execution of the signal handling function */ + sa_flags: SA_Flags, /* [PSX] special flags */ + } + + SIG_BLOCK :: 1 + SIG_UNBLOCK :: 2 + SIG_SETMASK :: 3 + + SA_NOCLDSTOP :: 0x0008 + SA_ONSTACK :: 0x0001 + SA_RESETHAND :: 0x0004 + SA_RESTART :: 0x0002 + SA_SIGINFO :: 0x0040 + SA_NOCLDWAIT :: 0x0020 + SA_NODEFER :: 0x0010 + + SS_ONSTACK :: 0x0001 + SS_DISABLE :: 0x0004 + + MINSIGSTKSZ :: 8192 + SIGSTKSZ :: MINSIGSTKSZ + 32768 + + stack_t :: struct { + ss_sp: rawptr, /* [PSX] stack base or pointer */ + ss_size: c.size_t, /* [PSX] stack size */ + ss_flags: SS_Flags, /* [PSX] flags */ + } + + @(private) + lwpid_t :: c.int32_t + + siginfo_t :: struct #raw_union { + si_pad: [128]byte, + using _info: struct { + si_signo: Signal, /* [PSX] signal number */ + si_code: struct #raw_union { /* [PSX] specific more detailed codes per signal */ + ill: ILL_Code, + fpe: FPE_Code, + segv: SEGV_Code, + bus: BUS_Code, + trap: TRAP_Code, + chld: CLD_Code, + poll: POLL_Code, + any: Any_Code, + }, + si_errno: Errno, /* [PSX] errno value associated with this signal */ + // #ifdef _LP64 + /* In _LP64 the union starts on an 8-byte boundary. */ + _pad: c.int, + // #endif + using _reason: struct #raw_union { + using _rt: struct { + _pid: pid_t, + _uid: uid_t, + si_value: sigval, /* [PSX] signal value */ + }, + using _child: struct { + si_pid: pid_t, /* [PSX] sending process ID */ + si_uid: uid_t, /* [PSX] real user ID of sending process */ + si_status: c.int, /* [PSX] exit value of signal */ + _utime: clock_t, + _stime: clock_t, + }, + using _fault: struct { + si_addr: rawptr, /* [PSX] address of faulting instruction */ + _trap: c.int, + _trap2: c.int, + _trap3: c.int, + }, + using _poll: struct { + si_band: c.long, /* [PSX] band event for SIGPOLL */ + _fd: FD, + }, + _syscall: struct { + _sysnum: c.int, + _retval: [2]c.int, + _error: c.int, + _args: [8]c.uint64_t, + }, + _ptrace_state: struct { + _pe_report_event: c.int, + _option: struct #raw_union { + _pe_other_pid: pid_t, + _pe_lwp: lwpid_t, + }, + }, + }, + }, + } + + ILL_ILLOPC :: 1 + ILL_ILLOPN :: 2 + ILL_ILLADR :: 3 + ILL_ILLTRP :: 4 + ILL_PRVOPC :: 5 + ILL_PRVREG :: 6 + ILL_COPROC :: 7 + ILL_BADSTK :: 8 + + FPE_INTDIV :: 1 + FPE_INTOVF :: 2 + FPE_FLTDIV :: 3 + FPE_FLTOVF :: 4 + FPE_FLTUND :: 5 + FPE_FLTRES :: 6 + FPE_FLTINV :: 7 + FPE_FLTSUB :: 8 + + SEGV_MAPERR :: 1 + SEGV_ACCERR :: 2 + + BUS_ADRALN :: 1 + BUS_ADRERR :: 2 + BUS_OBJERR :: 3 + + TRAP_BRKPT :: 1 + TRAP_TRACE :: 2 + + CLD_EXITED :: 1 + CLD_KILLED :: 2 + CLD_DUMPED :: 3 + CLD_TRAPPED :: 4 + CLD_STOPPED :: 5 + CLD_CONTINUED :: 6 + + POLL_IN :: 1 + POLL_OUT :: 2 + POLL_MSG :: 3 + POLL_ERR :: 4 + POLL_PRI :: 5 + POLL_HUP :: 6 + + SI_USER :: 0 + SI_QUEUE :: -1 + SI_TIMER :: -2 + SI_ASYNCIO :: -3 + SI_MESGQ :: -4 + +} else when ODIN_OS == .OpenBSD { + + // Request that signal be held + SIG_HOLD :: rawptr(uintptr(3)) + + uid_t :: distinct c.uint32_t + sigset_t :: distinct c.uint32_t + + SIGHUP :: 1 + SIGQUIT :: 3 + SIGTRAP :: 5 + SIGPOLL :: 7 + SIGKILL :: 9 + SIGBUS :: 10 + SIGSYS :: 12 + SIGPIPE :: 13 + SIGALRM :: 14 + SIGURG :: 16 + SIGCONT :: 19 + SIGSTOP :: 17 + SIGTSTP :: 18 + SIGCHLD :: 20 + SIGTTIN :: 21 + SIGTTOU :: 22 + SIGXCPU :: 24 + SIGXFSZ :: 25 + SIGVTALRM :: 26 + SIGPROF :: 27 + SIGUSR1 :: 30 + SIGUSR2 :: 31 + + // NOTE: this is actually defined as `sigaction`, but due to the function with the same name + // `_t` has been added. + + sigaction_t :: struct { + using _: struct #raw_union { + sa_handler: proc "c" (Signal), /* [PSX] signal-catching function or one of the SIG_IGN or SIG_DFL */ + sa_sigaction: proc "c" (Signal, ^siginfo_t, rawptr), /* [PSX] signal-catching function */ + }, + sa_mask: sigset_t, /* [PSX] set of signals to be blocked during execution of the signal handling function */ + sa_flags: SA_Flags, /* [PSX] special flags */ + } + + SIG_BLOCK :: 1 + SIG_UNBLOCK :: 2 + SIG_SETMASK :: 3 + + SA_NOCLDSTOP :: 0x0008 + SA_ONSTACK :: 0x0001 + SA_RESETHAND :: 0x0004 + SA_RESTART :: 0x0002 + SA_SIGINFO :: 0x0040 + SA_NOCLDWAIT :: 0x0020 + SA_NODEFER :: 0x0010 + + SS_ONSTACK :: 0x0001 + SS_DISABLE :: 0x0004 + + MINSIGSTKSZ :: 3 << 12 + SIGSTKSZ :: MINSIGSTKSZ + (1 << 12) * 4 + + stack_t :: struct { + ss_sp: rawptr, /* [PSX] stack base or pointer */ + ss_size: c.size_t, /* [PSX] stack size */ + ss_flags: SS_Flags, /* [PSX] flags */ + } + + SI_MAXSZ :: 128 + SI_PAD :: (SI_MAXSZ / size_of(c.int)) - 3 + + siginfo_t :: struct { + si_signo: Signal, /* [PSX] signal number */ + si_code: struct #raw_union { /* [PSX] specific more detailed codes per signal */ + ill: ILL_Code, + fpe: FPE_Code, + segv: SEGV_Code, + bus: BUS_Code, + trap: TRAP_Code, + chld: CLD_Code, + poll: POLL_Code, + any: Any_Code, + }, + si_errno: Errno, /* [PSX] errno value associated with this signal */ + using _data: struct #raw_union { + _pad: [SI_PAD]c.int, + using _proc: struct { + si_pid: pid_t, /* [PSX] sending process ID */ + si_uid: uid_t, /* [PSX] real user ID of sending process */ + using _pdata: struct #raw_union { + using _kill: struct { + si_value: sigval, + }, + using _cld: struct { + _utime: clock_t, + _stime: clock_t, + si_status: c.int, + }, + }, + }, + using _fault: struct { + si_addr: rawptr, + _trapno: c.int, + }, + using _file: struct { + _fd: FD, + si_band: c.long, /* [PSX] band event for SIGPOLL */ + }, + }, + } + + ILL_ILLOPC :: 1 + ILL_ILLOPN :: 2 + ILL_ILLADR :: 3 + ILL_ILLTRP :: 4 + ILL_PRVOPC :: 5 + ILL_PRVREG :: 6 + ILL_COPROC :: 7 + ILL_BADSTK :: 8 + + FPE_INTDIV :: 1 + FPE_INTOVF :: 2 + FPE_FLTDIV :: 3 + FPE_FLTOVF :: 4 + FPE_FLTUND :: 5 + FPE_FLTRES :: 6 + FPE_FLTINV :: 7 + FPE_FLTSUB :: 8 + + SEGV_MAPERR :: 1 + SEGV_ACCERR :: 2 + + BUS_ADRALN :: 1 + BUS_ADRERR :: 2 + BUS_OBJERR :: 3 + + TRAP_BRKPT :: 1 + TRAP_TRACE :: 2 + + CLD_EXITED :: 1 + CLD_KILLED :: 2 + CLD_DUMPED :: 3 + CLD_TRAPPED :: 4 + CLD_STOPPED :: 5 + CLD_CONTINUED :: 6 + + POLL_IN :: 1 + POLL_OUT :: 2 + POLL_MSG :: 3 + POLL_ERR :: 4 + POLL_PRI :: 5 + POLL_HUP :: 6 + + SI_USER :: 0 + SI_QUEUE :: -2 + SI_TIMER :: -3 + SI_ASYNCIO :: -4 // NOTE: not implemented + SI_MESGQ :: -5 // NOTE: not implemented + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/stdio.odin b/core/sys/posix/stdio.odin new file mode 100644 index 000000000..de716f8d7 --- /dev/null +++ b/core/sys/posix/stdio.odin @@ -0,0 +1,332 @@ +package posix + +import "core:c" +import "core:c/libc" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// stdio.h - standard buffered input/output + +foreign lib { + /* + Generates a string that, when used as a pathname, + refers to the current controlling terminal for the current process. + + If s is nil, the returned string might be static and overwritten by subsequent calls or other factors. + If s is not nil, s is assumed len(s) >= L_ctermid. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/ctermid.html ]] + */ + ctermid :: proc(s: [^]byte) -> cstring --- + + /* + Equivalent to fprintf but output is written to the file descriptor. + + Return: number of bytes written, negative (setting errno) on failure + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dprintf.html ]] + */ + dprintf :: proc(fildse: FD, format: cstring, #c_vararg args: ..any) -> c.int --- + + /* + Equivalent to fprintf but output is written to s, it is the user's responsibility to + ensure there is enough space. + + Return: number of bytes written, negative (setting errno) on failure + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dprintf.html ]] + */ + sprintf :: proc(s: [^]byte, format: cstring, #c_vararg args: ..any) -> c.int --- + + /* + Associate a stream with a file descriptor. + + Returns: nil (setting errno) on failure, the stream on success + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdopen.html ]] + */ + fdopen :: proc(fildes: FD, mode: cstring) -> ^FILE --- + + /* + Map a stream pointer to a file descriptor. + + Returns: the file descriptor or -1 (setting errno) on failure + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fileno.html ]] + */ + fileno :: proc(stream: ^FILE) -> FD --- + + /* + Locks a file. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/flockfile.html ]] + */ + flockfile :: proc(file: ^FILE) --- + + /* + Tries to lock a file. + + Returns: 0 if it could be locked, non-zero if it couldn't + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/flockfile.html ]] + */ + ftrylockfile :: proc(file: ^FILE) -> c.int --- + + /* + Unlocks a file. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/flockfile.html ]] + */ + funlockfile :: proc(file: ^FILE) --- + + /* + Open a memory buffer stream. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fmemopen.html ]] + */ + fmemopen :: proc(buf: [^]byte, size: c.size_t, mode: cstring) -> ^FILE --- + + /* + Reposition a file-position indicator in a stream. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fseeko.html ]] + */ + fseeko :: proc(stream: ^FILE, offset: off_t, whence: Whence) -> result --- + + /* + Return the file offset in a stream. + + Returns: the current file offset, -1 (setting errno) on error + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/ftello.html ]] + */ + ftello :: proc(^FILE) -> off_t --- + + /* + Open a dynamic memory buffer stream. + + Returns: nil (setting errno) on failure, the stream on success + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/open_memstream.html ]] + */ + open_memstream :: proc(bufp: ^[^]byte, sizep: ^c.size_t) -> ^FILE --- + + /* + Equivalent to getc but unaffected by locks. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getc_unlocked.html ]] + */ + getc_unlocked :: proc(stream: ^FILE) -> c.int --- + + /* + Equivalent to getchar but unaffected by locks. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getc_unlocked.html ]] + */ + getchar_unlocked :: proc() -> c.int --- + + /* + Equivalent to putc but unaffected by locks. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getc_unlocked.html ]] + */ + putc_unlocked :: proc(ch: c.int, stream: ^FILE) -> c.int --- + + /* + Equivalent to putchar but unaffected by locks. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getc_unlocked.html ]] + */ + putchar_unlocked :: proc(ch: c.int) -> c.int --- + + /* + Read a delimited record from the stream. + + Returns: the number of bytes written or -1 on failure/EOF + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getdelim.html ]] + */ + getdelim :: proc(lineptr: ^cstring, n: ^c.size_t, delimiter: c.int, stream: ^FILE) -> c.ssize_t --- + + /* + Read a line delimited record from the stream. + + Returns: the number of bytes written or -1 on failure/EOF + + Example: + fp := posix.fopen(#file, "r") + if fp == nil { + posix.exit(1) + } + + line: cstring + length: uint + for { + read := posix.getline(&line, &length, fp) + if read == -1 do break + posix.printf("Retrieved line of length %zu :\n", read) + posix.printf("%s", line) + } + if posix.ferror(fp) != 0 { + /* handle error */ + } + posix.free(rawptr(line)) + posix.fclose(fp) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getdelim.html ]] + */ + getline :: proc(lineptr: ^cstring, n: ^c.size_t, stream: ^FILE) -> c.ssize_t --- + + /* + Get a string from the stdin stream. + + It is up to the user to make sure s is big enough. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/gets.html ]] + */ + gets :: proc(s: [^]byte) -> cstring --- + + /* + Create a name for a temporary file. + + Returns: an allocated cstring that needs to be freed, nil on failure + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/tempnam.html ]] + */ + tempnam :: proc(dir: cstring, pfx: cstring) -> cstring --- + + /* + Executes the command specified, creating a pipe and returning a pointer to a stream that can + read or write from/to the pipe. + + Returns: nil (setting errno) on failure or a pointer to the stream + + Example: + fp := posix.popen("ls *", "r") + if fp == nil { + /* Handle error */ + } + + path: [1024]byte + for posix.fgets(raw_data(path[:]), len(path), fp) != nil { + posix.printf("%s", &path) + } + + status := posix.pclose(fp) + if status == -1 { + /* Error reported by pclose() */ + } else { + /* Use functions described under wait() to inspect `status` in order + to determine success/failure of the command executed by popen() */ + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/popen.html ]] + */ + popen :: proc(command: cstring, mode: cstring) -> ^FILE --- + + /* + Closes a pipe stream to or from a process. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pclose.html ]] + */ + pclose :: proc(stream: ^FILE) -> c.int --- + + /* + Equivalent to rename but relative directories are resolved from their respective fds. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/renameat.html ]] + */ + renameat :: proc(oldfd: FD, old: cstring, newfd: FD, new: cstring) -> result --- +} + +clearerr :: libc.clearerr +fclose :: libc.fclose +feof :: libc.feof +ferror :: libc.ferror +fflush :: libc.fflush +fgetc :: libc.fgetc +fgetpos :: libc.fgetpos +fgets :: libc.fgets +fopen :: libc.fopen +fprintf :: libc.fprintf +fputc :: libc.fputc +fread :: libc.fread +freopen :: libc.freopen +fscanf :: libc.fscanf +fseek :: libc.fseek +fsetpos :: libc.fsetpos +ftell :: libc.ftell +fwrite :: libc.fwrite +getc :: libc.getc +getchar :: libc.getchar +perror :: libc.perror +printf :: libc.printf +putc :: libc.puts +putchar :: libc.putchar +puts :: libc.puts +remove :: libc.remove +rename :: libc.rename +rewind :: libc.rewind +scanf :: libc.scanf +setbuf :: libc.setbuf +setvbuf :: libc.setvbuf +snprintf :: libc.snprintf +sscanf :: libc.sscanf +tmpfile :: libc.tmpfile +tmpnam :: libc.tmpnam +vfprintf :: libc.vfprintf +vfscanf :: libc.vfscanf +vprintf :: libc.vprintf +vscanf :: libc.vscanf +vsnprintf :: libc.vsnprintf +vsprintf :: libc.vsprintf +vsscanf :: libc.vsscanf +ungetc :: libc.ungetc + +to_stream :: libc.to_stream + +Whence :: libc.Whence +FILE :: libc.FILE +fpos_t :: libc.fpos_t + +BUFSIZ :: libc.BUFSIZ + +_IOFBF :: libc._IOFBF +_IOLBF :: libc._IOLBF +_IONBF :: libc._IONBF + +SEEK_CUR :: libc.SEEK_CUR +SEEK_END :: libc.SEEK_END +SEEK_SET :: libc.SEEK_SET + +FILENAME_MAX :: libc.FILENAME_MAX +FOPEN_MAX :: libc.FOPEN_MAX +TMP_MAX :: libc.TMP_MAX + +EOF :: libc.EOF + +stderr := libc.stderr +stdin := libc.stdin +stdout := libc.stdout + +when ODIN_OS == .Darwin { + + L_ctermid :: 1024 + L_tmpnam :: 1024 + + P_tmpdir :: "/var/tmp/" + +} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + + L_ctermid :: 1024 + L_tmpnam :: 1024 + + P_tmpdir :: "/tmp/" + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/stdlib.odin b/core/sys/posix/stdlib.odin new file mode 100644 index 000000000..a1e2eab50 --- /dev/null +++ b/core/sys/posix/stdlib.odin @@ -0,0 +1,450 @@ +package posix + +import "base:intrinsics" + +import "core:c" +import "core:c/libc" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// stdlib.h - standard library definitions + +atof :: libc.atof +atoi :: libc.atoi +atol :: libc.atol +atoll :: libc.atoll +strtod :: libc.strtod +strtof :: libc.strtof +strtol :: libc.strtol +strtoll :: libc.strtoll +strtoul :: libc.strtoul +strtoull :: libc.strtoull + +rand :: libc.rand +srand :: libc.srand + +calloc :: libc.calloc +malloc :: libc.malloc +realloc :: libc.realloc + +abort :: libc.abort +atexit :: libc.atexit +at_quick_exit :: libc.at_quick_exit +exit :: libc.exit +_Exit :: libc._Exit +getenv :: libc.getenv +quick_exit :: libc.quick_exit +system :: libc.system + +bsearch :: libc.bsearch +qsort :: libc.qsort + +abs :: libc.abs +labs :: libc.labs +llabs :: libc.llabs +div :: libc.div +ldiv :: libc.ldiv +lldiv :: libc.lldiv + +mblen :: libc.mblen +mbtowc :: libc.mbtowc +wctomb :: libc.wctomb + +mbstowcs :: libc.mbstowcs +wcstombs :: libc.wcstombs + +free :: #force_inline proc(ptr: $T) where intrinsics.type_is_pointer(T) || intrinsics.type_is_multi_pointer(T) || T == cstring { + libc.free(rawptr(ptr)) +} + +foreign lib { + /* + Takes a pointer to a radix-64 representation, in which the first digit is the least significant, + and return the corresponding long value. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/a64l.html ]] + */ + a64l :: proc(s: cstring) -> c.long --- + + /* + The l64a() function shall take a long argument and return a pointer to the corresponding + radix-64 representation. + + Returns: a string that may be invalidated by subsequent calls + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/a64l.html ]] + */ + l64a :: proc(value: c.long) -> cstring --- + + /* + This family of functions shall generate pseudo-random numbers using a linear congruential algorithm and 48-bit integer arithmetic. + + Returns: non-negative, double-precision, floating-point values, uniformly distributed over the interval [0.0,1.0) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/drand48.html ]] + */ + drand48 :: proc() -> c.double --- + + /* + This family of functions shall generate pseudo-random numbers using a linear congruential algorithm and 48-bit integer arithmetic. + + Returns: non-negative, double-precision, floating-point values, uniformly distributed over the interval [0.0,1.0) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/drand48.html ]] + */ + erand48 :: proc(xsubi: ^[3]c.ushort) -> c.double --- + + /* + This family of functions shall generate pseudo-random numbers using a linear congruential algorithm and 48-bit integer arithmetic. + + Returns: return signed long integers uniformly distributed over the interval [-231,231) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/drand48.html ]] + */ + mrand48 :: proc() -> c.long --- + + /* + This family of functions shall generate pseudo-random numbers using a linear congruential algorithm and 48-bit integer arithmetic. + + Returns: return signed long integers uniformly distributed over the interval [-231,231) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/drand48.html ]] + */ + jrand48 :: proc(xsubi: ^[3]c.ushort) -> c.long --- + + /* + This family of functions shall generate pseudo-random numbers using a linear congruential algorithm and 48-bit integer arithmetic. + + Returns: non-negative, long integers, uniformly distributed over the interval [0,231) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/drand48.html ]] + */ + lrand48 :: proc() -> c.long --- + + /* + This family of functions shall generate pseudo-random numbers using a linear congruential algorithm and 48-bit integer arithmetic. + + Returns: non-negative, long integers, uniformly distributed over the interval [0,231) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/drand48.html ]] + */ + nrand48 :: proc(xsubi: ^[3]c.ushort) -> c.long --- + + /* + This family of functions shall generate pseudo-random numbers using a linear congruential algorithm and 48-bit integer arithmetic. + + The srand48(), seed48(), and lcong48() functions are initialization entry points, one of which should be invoked before either drand48(), lrand48(), or mrand48() is called. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/drand48.html ]] + */ + srand48 :: proc(seedval: c.long) --- + + /* + This family of functions shall generate pseudo-random numbers using a linear congruential algorithm and 48-bit integer arithmetic. + + The srand48(), seed48(), and lcong48() functions are initialization entry points, one of which should be invoked before either drand48(), lrand48(), or mrand48() is called. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/drand48.html ]] + */ + lcong48 :: proc(param: ^[7]c.ushort) --- + + /* + This family of functions shall generate pseudo-random numbers using a linear congruential algorithm and 48-bit integer arithmetic. + + The srand48(), seed48(), and lcong48() functions are initialization entry points, one of which should be invoked before either drand48(), lrand48(), or mrand48() is called. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/drand48.html ]] + */ + seed48 :: proc(seed16v: ^[3]c.ushort) -> ^[3]c.ushort --- + + /* + Parses suboption arguments in a flag argument. + + Returns: the index of the matched token string, or -1 if no token strings were matched + + Example: + args := runtime.args__ + + Opt :: enum { + RO, + RW, + NAME, + NIL, + } + token := [Opt]cstring{ + .RO = "ro", + .RW = "rw", + .NAME = "name", + .NIL = nil, + } + + Options :: struct { + readonly, readwrite: bool, + name: cstring, + + } + opts: Options + + errfnd: bool + for { + opt := posix.getopt(i32(len(args)), raw_data(args), "o:") + if opt == -1 { + break + } + + switch opt { + case 'o': + subopt := posix.optarg + value: cstring + for subopt != "" && !errfnd { + o := posix.getsubopt(&subopt, &token[.RO], &value) + switch Opt(o) { + case .RO: opts.readonly = true + case .RW: opts.readwrite = true + case .NAME: + if value == nil { + fmt.eprintfln("missing value for suboption %s", token[.NAME]) + errfnd = true + continue + } + + opts.name = value + case .NIL: + fallthrough + case: + fmt.eprintfln("no match found for token: %s", value) + errfnd = true + } + } + if opts.readwrite && opts.readonly { + fmt.eprintfln("Only one of %s and %s can be specified", token[.RO], token[.RW]) + errfnd = true + } + case: + errfnd = true + } + } + + if errfnd || len(args) == 1 { + fmt.eprintfln("\nUsage: %s -o ", args[0]) + fmt.eprintfln("suboptions are 'ro', 'rw', and 'name='") + posix.exit(1) + } + + fmt.println(opts) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsubopt.html ]] + */ + getsubopt :: proc(optionp: ^cstring, keylistp: [^]cstring, valuep: ^cstring) -> c.int --- + + /* + Changes the mode and ownership of the slave pseudo-terminal device associated with its master pseudo-terminal counterpart. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/grantpt.html ]] + */ + grantpt :: proc(fildes: FD) -> result --- + + /* + Allows a state array, pointed to by the state argument, to be initialized for future use. + + Returns: the previous state array or nil on failure + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/initstate.html ]] + */ + @(link_name=LINITSTATE) + initstate :: proc(seed: c.uint, state: [^]byte, size: c.size_t) -> [^]byte --- + + /* + Sets the state array of the random number generator. + + Returns: the previous state array or nil on failure + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/initstate.html ]] + */ + setstate :: proc(state: [^]byte) -> [^]byte --- + + /* + Use a non-linear additive feedback random-number generator employing a default state array + size of 31 long integers to return successive pseudo-random numbers in the range from 0 to 231-1. + The period of this random-number generator is approximately 16 x (231-1). + The size of the state array determines the period of the random-number generator. + Increasing the state array size shall increase the period. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/initstate.html ]] + */ + random :: proc() -> c.long --- + + /* + Initializes the current state array using the value of seed. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/initstate.html ]] + */ + @(link_name=LSRANDOM) + srandom :: proc(seed: c.uint) --- + + /* + Creates a directory with a unique name derived from template. + The application shall ensure that the string provided in template is a pathname ending + with at least six trailing 'X' characters. + + Returns: nil (setting errno) on failure, template on success + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdtemp.html ]] + */ + mkdtemp :: proc(template: [^]byte) -> cstring --- + + /* + Creates a regular file with a unique name derived from template and return a file descriptor + for the file open for reading and writing. + The application shall ensure that the string provided in template is a pathname ending with + at least six trailing 'X' characters. + + Returns: -1 (setting errno) on failure, an open file descriptor on success + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdtemp.html ]] + */ + mkstemp :: proc(template: cstring) -> FD --- + + /* + Allocates size bytes aligned on a boundary specified by alignment, and shall return a pointer + to the allocated memory in memptr. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_memalign.html ]] + */ + posix_memalign :: proc(memptr: ^[^]byte, alignment: c.size_t, size: c.size_t) -> Errno --- + + /* + Establishes a connection between a master device for a pseudo-terminal and a file descriptor. + + Returns: -1 (setting errno) on failure, an open file descriptor otherwise + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_openpt.html ]] + */ + posix_openpt :: proc(oflag: O_Flags) -> FD --- + + /* + Returns the name of the slave pseudo-terminal device associated with a master pseudo-terminal device. + + Returns: nil (setting errno) on failure, the name on success, which may be invalidated on subsequent calls + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/ptsname.html ]] + */ + ptsname :: proc(fildes: FD) -> cstring --- + + /* + Unlocks the slave pseudo-terminal device associated with the master to which fildes refers. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlockpt.html ]] + */ + unlockpt :: proc(fildes: FD) -> result --- + + /* + Uses the string argument to set environment variable values. + + Returns: 0 on success, non-zero (setting errno) on failure + + Example: + if posix.putenv("HOME=/usr/home") != 0 { + fmt.panicf("putenv failure: %v", posix.strerror(posix.errno())) + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/putenv.html ]] + */ + @(link_name=LPUTENV) + putenv :: proc(string: cstring) -> c.int --- + + /* + Updates or add a variable in the environment of the calling process. + + Example: + if posix.setenv("HOME", "/usr/home") != .OK { + fmt.panicf("putenv failure: %v", posix.strerror(posix.errno())) + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setenv.html ]] + */ + setenv :: proc(envname: cstring, envval: cstring, overwrite: b32) -> result --- + + /* + Removes an environment variable from the environment of the calling process. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/unsetenv.html ]] + */ + @(link_name=LUNSETENV) + unsetenv :: proc(name: cstring) -> result --- + + /* + Computes a sequence of pseudo-random integers in the range [0, {RAND_MAX}]. + (The value of the {RAND_MAX} macro shall be at least 32767.) + + If rand_r() is called with the same initial value for the object pointed to by seed and that object is not modified between successive returns and calls to rand_r(), the same sequence shall be generated. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/rand_r.html ]] + */ + rand_r :: proc(seed: ^c.uint) -> c.int --- + + /* + Derive, from the pathname file_name, an absolute pathname that resolves to the same directory entry, + whose resolution does not involve '.', '..', or symbolic links. + + If resolved_name is not `nil` it should be larger than `PATH_MAX` and the result will use it as a backing buffer. + If resolved_name is `nil` the returned string is allocated by `malloc`. + + Returns: `nil` (setting errno) on failure, the "real path" otherwise + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html ]] + */ + realpath :: proc(file_name: cstring, resolved_name: [^]byte = nil) -> cstring --- + + /* + Provides access to an implementation-defined encoding algorithm. + The argument of setkey() is an array of length 64 bytes containing only the bytes with numerical + value of 0 and 1. + + If this string is divided into groups of 8, the low-order bit in each group is ignored; this gives a 56-bit key which is used by the algorithm. + This is the key that shall be used with the algorithm to encode a string block passed to encrypt(). + + The setkey() function shall not change the setting of errno if successful. + An application wishing to check for error situations should set errno to 0 before calling setkey(). + If errno is non-zero on return, an error has occurred. + + Example: + key: [64]byte + // set key bytes... + + posix.set_errno(.NONE) + posix.setkey(raw_data(key)) + if errno := posix.errno(); errno != .NONE { + fmt.panicf("setkey failure: %s", posix.strerror(errno)) + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setkey.html ]] + */ + setkey :: proc(key: [^]byte) --- +} + +EXIT_FAILURE :: libc.EXIT_FAILURE +EXIT_SUCCESS :: libc.EXIT_SUCCESS + +RAND_MAX :: libc.RAND_MAX +MB_CUR_MAX :: libc.MB_CUR_MAX + +div_t :: libc.div_t +ldiv_t :: libc.ldiv_t +lldiv_t :: libc.lldiv_t + +when ODIN_OS == .NetBSD { + @(private) LPUTENV :: "__putenv50" + @(private) LINITSTATE :: "__initstate60" + @(private) LSRANDOM :: "__srandom60" + @(private) LUNSETENV :: "__unsetenv13" +} else { + @(private) LPUTENV :: "putenv" + @(private) LINITSTATE :: "initstate" + @(private) LSRANDOM :: "srandom" + @(private) LUNSETENV :: "unsetenv" +} diff --git a/core/sys/posix/string.odin b/core/sys/posix/string.odin new file mode 100644 index 000000000..d22f49a96 --- /dev/null +++ b/core/sys/posix/string.odin @@ -0,0 +1,47 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// string.h - string operations + +// NOTE: most of the symbols in this header are not useful in Odin and have been left out. + +foreign lib { + /* + Map the error number to a locale-dependent error message string. + + Returns: a string that may be invalidated by subsequent calls + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/strerror.html ]] + */ + @(link_name="strerror") + _strerror :: proc(errnum: Errno) -> cstring --- + + /* + Map the error number to a locale-dependent error message string and put it in the buffer. + + Returns: ERANGE if the buffer is not big enough + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/strerror_r.html ]] + */ + strerror_r :: proc(errnum: Errno, strerrbuf: [^]byte, buflen: c.size_t) -> Errno --- + + /* + Map the signal number to an implementation-defined string. + + Returns: a string that may be invalidated by subsequent calls + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/strsignal.html ]] + */ + strsignal :: proc(sig: Signal) -> cstring --- +} + +strerror :: #force_inline proc "contextless" (errnum: Maybe(Errno) = nil) -> cstring { + return _strerror(errnum.? or_else errno()) +} diff --git a/core/sys/posix/sys_ipc.odin b/core/sys/posix/sys_ipc.odin new file mode 100644 index 000000000..f8778ee15 --- /dev/null +++ b/core/sys/posix/sys_ipc.odin @@ -0,0 +1,89 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// sys/ipc.h = XSI interprocess communication access structure + +foreign lib { + /* + Generate an IPC key. + + Returns: -1 (setting errno) on failure, the key otherwise + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/ftok.html ]] + */ + ftok :: proc(path: cstring, id: c.int) -> key_t --- +} + +IPC_Cmd :: enum c.int { + RMID = IPC_RMID, + SET = IPC_SET, + STAT = IPC_STAT, +} + +IPC_Flag_Bits :: enum c.int { + CREAT = log2(IPC_CREAT), + EXCL = log2(IPC_EXCL), + NOWAIT = log2(IPC_NOWAIT), + + MSG_NOERROR = log2(MSG_NOERROR), +} +IPC_Flags :: bit_set[IPC_Flag_Bits; c.int] + +when ODIN_OS == .Darwin { + + key_t :: distinct c.int32_t + + ipc_perm :: struct { + uid: uid_t, /* [PSX] owner's user ID */ + gid: gid_t, /* [PSX] owner's group ID */ + cuid: uid_t, /* [PSX] creator's user ID */ + cgid: gid_t, /* [PSX] creator's group ID */ + mode: mode_t, /* [PSX] read/write perms */ + _seq: c.ushort, + _key: key_t, + } + + IPC_CREAT :: 0o01000 + IPC_EXCL :: 0o02000 + IPC_NOWAIT :: 0o04000 + + IPC_PRIVATE :: key_t(0) + + IPC_RMID :: 0 + IPC_SET :: 1 + IPC_STAT :: 2 + +} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + + key_t :: distinct c.long + + ipc_perm :: struct { + cuid: uid_t, /* [PSX] creator's user ID */ + cgid: gid_t, /* [PSX] creator's group ID */ + uid: uid_t, /* [PSX] owner's user ID */ + gid: gid_t, /* [PSX] owner's group ID */ + mode: mode_t, /* [PSX] read/write perms */ + _seq: c.ushort, + _key: key_t, + } + + IPC_CREAT :: 0o01000 + IPC_EXCL :: 0o02000 + IPC_NOWAIT :: 0o04000 + + IPC_PRIVATE :: key_t(0) + + IPC_RMID :: 0 + IPC_SET :: 1 + IPC_STAT :: 2 + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/sys_mman.odin b/core/sys/posix/sys_mman.odin new file mode 100644 index 000000000..217d321ac --- /dev/null +++ b/core/sys/posix/sys_mman.odin @@ -0,0 +1,229 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// mman.h - memory management declarations + +foreign lib { + /* + Establish a mapping between an address space of a process and a memory object. + + Returns: MAP_FAILED (setting errno) on failure, the address in memory otherwise + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mmap.html ]] + */ + mmap :: proc( + addr: rawptr, + len: c.size_t, + prot: Prot_Flags, + flags: Map_Flags, + fd: FD = -1, + off: off_t = 0, + ) -> rawptr --- + + /* + Unmaps pages of memory. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/munmap.html ]] + */ + munmap :: proc(addr: rawptr, len: c.size_t) -> result --- + + /* + Locks a range of the process address space. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mlock.html ]] + */ + mlock :: proc(addr: rawptr, len: c.size_t) -> result --- + + /* + Unlocks a range of the process address space. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mlock.html ]] + */ + munlock :: proc(addr: rawptr, len: c.size_t) -> result --- + + /* + Locks all pages of the process address space. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mlockall.html ]] + */ + mlockall :: proc(flags: Lock_Flags) -> result --- + + /* + Unlocks all pages of the process address space. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mlockall.html ]] + */ + munlockall :: proc() -> result --- + + /* + Set protection of a memory mapping. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mprotect.html ]] + */ + mprotect :: proc(addr: rawptr, len: c.size_t, prot: Prot_Flags) -> result --- + + /* + Write all modified data to permanent storage locations. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/msync.html ]] + */ + @(link_name=LMSYNC) + msync :: proc(addr: rawptr, len: c.size_t, flags: Sync_Flags) -> result --- + + /* + Advise the implementation of expected behavior of the application. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_madvise.html ]] + */ + posix_madvise :: proc(addr: rawptr, len: c.size_t, advice: MAdvice) -> Errno --- + + /* + Open a shared memory object. + + Returns: -1 (setting errno) on failure, an open file descriptor otherwise + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/shm_open.html ]] + */ + shm_open :: proc(name: cstring, oflag: O_Flags, mode: mode_t) -> FD --- + + /* + Removes a shared memory object. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/shm_unlink.html ]] + */ + shm_unlink :: proc(name: cstring) -> result --- +} + +#assert(_PROT_NONE == 0) +PROT_NONE :: Prot_Flags{} + +Prot_Flag_Bits :: enum c.int { + // Data can be executed. + EXEC = log2(PROT_EXEC), + // Data can be read. + READ = log2(PROT_READ), + // Data can be written. + WRITE = log2(PROT_WRITE), +} +Prot_Flags :: bit_set[Prot_Flag_Bits; c.int] + +Map_Flag_Bits :: enum c.int { + // Interpret addr exactly. + FIXED = log2(MAP_FIXED), + // Changes are private. + PRIVATE = log2(MAP_PRIVATE), + // Changes are shared. + SHARED = log2(MAP_SHARED), +} +Map_Flags :: bit_set[Map_Flag_Bits; c.int] + +Lock_Flag_Bits :: enum c.int { + // Lock all pages currently mapped into the address space of the process. + CURRENT = log2(MCL_CURRENT), + // Lock all pages that become mapped into the address space of the process in the future, + // when those mappings are established. + FUTURE = log2(MCL_FUTURE), +} +Lock_Flags :: bit_set[Lock_Flag_Bits; c.int] + +Sync_Flags_Bits :: enum c.int { + // Perform asynchronous writes. + ASYNC = log2(MS_ASYNC), + // Invalidate cached data. + INVALIDATE = log2(MS_INVALIDATE), + + // Perform synchronous writes. + // NOTE: use with `posix.MS_SYNC + { .OTHER_FLAG, .OTHER_FLAG }`, unfortunately can't be in + // this bit set enum because it is 0 on some platforms and a value on others. + // LOCAL = RTLD_LOCAL + // SYNC = MS_SYNC, + + _MAX = 31, +} +Sync_Flags :: bit_set[Sync_Flags_Bits; c.int] + +MAdvice :: enum c.int { + DONTNEED = POSIX_MADV_DONTNEED, + NORMAL = POSIX_MADV_NORMAL, + RANDOM = POSIX_MADV_RANDOM, + SEQUENTIAL = POSIX_MADV_SEQUENTIAL, + WILLNEED = POSIX_MADV_WILLNEED, +} + +when ODIN_OS == .NetBSD { + @(private) LMSYNC :: "__msync13" +} else { + @(private) LMSYNC :: "msync" +} + +when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + + PROT_EXEC :: 0x04 + _PROT_NONE :: 0x00 + PROT_READ :: 0x01 + PROT_WRITE :: 0x02 + + MAP_FIXED :: 0x0010 + MAP_PRIVATE :: 0x0002 + MAP_SHARED :: 0x0001 + + when ODIN_OS == .Darwin { + MS_INVALIDATE :: 0x0002 + _MS_SYNC :: 0x0010 + } else when ODIN_OS == .NetBSD { + MS_INVALIDATE :: 0x0002 + _MS_SYNC :: 0x0004 + } else when ODIN_OS == .OpenBSD { + MS_INVALIDATE :: 0x0004 + _MS_SYNC :: 0x0002 + } + MS_ASYNC :: 0x0001 + MS_SYNC :: Sync_Flags{Sync_Flags_Bits(log2(_MS_SYNC))} + + MCL_CURRENT :: 0x0001 + MCL_FUTURE :: 0x0002 + + MAP_FAILED :: rawptr(~uintptr(0)) + + POSIX_MADV_DONTNEED :: 4 + POSIX_MADV_NORMAL :: 0 + POSIX_MADV_RANDOM :: 1 + POSIX_MADV_SEQUENTIAL :: 2 + POSIX_MADV_WILLNEED :: 3 + +} else when ODIN_OS == .FreeBSD { + + PROT_EXEC :: 0x04 + _PROT_NONE :: 0x00 + PROT_READ :: 0x01 + PROT_WRITE :: 0x02 + + MAP_FIXED :: 0x0010 + MAP_PRIVATE :: 0x0002 + MAP_SHARED :: 0x0001 + + MS_ASYNC :: 0x0001 + MS_INVALIDATE :: 0x0002 + MS_SYNC :: Sync_Flags{} + + MCL_CURRENT :: 0x0001 + MCL_FUTURE :: 0x0002 + + MAP_FAILED :: rawptr(~uintptr(0)) + + POSIX_MADV_DONTNEED :: 4 + POSIX_MADV_NORMAL :: 0 + POSIX_MADV_RANDOM :: 1 + POSIX_MADV_SEQUENTIAL :: 2 + POSIX_MADV_WILLNEED :: 3 + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/sys_msg.odin b/core/sys/posix/sys_msg.odin new file mode 100644 index 000000000..a8b86e501 --- /dev/null +++ b/core/sys/posix/sys_msg.odin @@ -0,0 +1,161 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// sys/msg.h = XSI message queue structures + +foreign lib { + /* + Provides various operation as specified by the given cmd. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/msgctl.html ]] + */ + @(link_name=LMSGCTL) + msgctl :: proc(msqid: FD, cmd: IPC_Cmd, buf: ^msqid_ds) -> result --- + + /* + Returns the message queue identifier associated with the argument key. + + Returns: -1 (setting errno) on failure, the identifier otherwise + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/msgget.html ]] + */ + msgget :: proc(key: key_t, msgflg: IPC_Flags) -> FD --- + + /* + Read a message from the queue. + + Returns: -1 (setting errno) on failure, the bytes received otherwise + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/msgrcv.html ]] + */ + msgrcv :: proc( + msgid: FD, + msgp: rawptr, + msgsz: c.size_t, + msgtyp: c.long, + msgflg: IPC_Flags, + ) -> c.ssize_t --- + + /* + Send a message on the queue. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/msgsnd.html ]] + */ + msgsnd :: proc(msgid: FD, msgp: rawptr, msgsz: c.size_t, msgflg: IPC_Flags) -> result --- +} + +when ODIN_OS == .NetBSD { + @(private) LMSGCTL :: "__msgctl50" +} else { + @(private) LMSGCTL :: "msgctl" +} + +when ODIN_OS == .Darwin { + + msgqnum_t :: distinct c.ulong + msglen_t :: distinct c.ulong + + MSG_NOERROR :: 0o10000 + + // NOTE: this is #pragma pack(4) + + msqid_ds :: struct #align(4) { + msg_perm: ipc_perm, /* [PSX] operation permission structure */ + msg_first: c.int32_t, + msg_last: c.int32_t, + msg_cbytes: msglen_t, + msg_qnum: msgqnum_t, /* [PSX] number of messages currently on queue */ + msg_qbytes: msglen_t, /* [PSX] maximum number of bytes allowed on queue */ + msg_lspid: pid_t, /* [PSX] process ID of last msgsnd() */ + msg_lrpid: pid_t, /* [PSX] process ID of last msgrcv() */ + msg_stime: time_t, /* [PSX] time of last msgsnd() */ + msg_pad1: c.int32_t, + using _: struct #align(4) { + msg_rtime: time_t, /* [PSX] time of last msgrcv() */ + msg_pad2: c.int32_t, + using _: struct #align(4) { + msg_ctime: time_t, /* [PSX] time of last change */ + msg_pad3: c.int32_t, + msg_pad4: [4]c.int32_t, + }, + }, + } + +} else when ODIN_OS == .FreeBSD { + + msgqnum_t :: distinct c.ulong + msglen_t :: distinct c.ulong + + MSG_NOERROR :: 0o10000 + + msqid_ds :: struct { + msg_perm: ipc_perm, /* [PSX] operation permission structure */ + __msg_first: rawptr, + __msg_last: rawptr, + msg_cbytes: msglen_t, + msg_qnum: msgqnum_t, /* [PSX] number of messages currently on queue */ + msg_qbytes: msglen_t, /* [PSX] maximum number of bytes allowed on queue */ + msg_lspid: pid_t, /* [PSX] process ID of last msgsnd() */ + msg_lrpid: pid_t, /* [PSX] process ID of last msgrcv() */ + msg_stime: time_t, /* [PSX] time of last msgsnd() */ + msg_rtime: time_t, /* [PSX] time of last msgrcv() */ + msg_ctime: time_t, /* [PSX] time of last change */ + } + +} else when ODIN_OS == .NetBSD { + + msgqnum_t :: distinct c.ulong + msglen_t :: distinct c.size_t + + MSG_NOERROR :: 0o10000 + + msqid_ds :: struct { + msg_perm: ipc_perm, /* [PSX] operation permission structure */ + msg_qnum: msgqnum_t, /* [PSX] number of messages currently on queue */ + msg_qbytes: msglen_t, /* [PSX] maximum number of bytes allowed on queue */ + msg_lspid: pid_t, /* [PSX] process ID of last msgsnd() */ + msg_lrpid: pid_t, /* [PSX] process ID of last msgrcv() */ + msg_stime: time_t, /* [PSX] time of last msgsnd() */ + msg_rtime: time_t, /* [PSX] time of last msgrcv() */ + msg_ctime: time_t, /* [PSX] time of last change */ + + _msg_first: rawptr, + _msg_last: rawptr, + _msg_cbytes: msglen_t, + } + +} else when ODIN_OS == .OpenBSD { + + msgqnum_t :: distinct c.ulong + msglen_t :: distinct c.ulong + + MSG_NOERROR :: 0o10000 + + msqid_ds :: struct { + msg_perm: ipc_perm, /* [PSX] operation permission structure */ + __msg_first: rawptr, + __msg_last: rawptr, + msg_cbytes: msglen_t, + msg_qnum: msgqnum_t, /* [PSX] number of messages currently on queue */ + msg_qbytes: msglen_t, /* [PSX] maximum number of bytes allowed on queue */ + msg_lspid: pid_t, /* [PSX] process ID of last msgsnd() */ + msg_lrpid: pid_t, /* [PSX] process ID of last msgrcv() */ + msg_stime: time_t, /* [PSX] time of last msgsnd() */ + msg_pad1: c.long, + msg_rtime: time_t, /* [PSX] time of last msgrcv() */ + msg_pad2: c.long, + msg_ctime: time_t, /* [PSX] time of last change */ + msg_pad3: c.long, + msg_pad4: [4]c.long, + } + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/sys_resource.odin b/core/sys/posix/sys_resource.odin new file mode 100644 index 000000000..6716d60c3 --- /dev/null +++ b/core/sys/posix/sys_resource.odin @@ -0,0 +1,152 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// sys/resource.h - definitions XSI resource operations + +foreign lib { + /* + Gets the nice value of the process, process group or user given. + + Note that a nice value can be -1, so checking for an error would mean clearing errno, doing the + call and then checking that this returns -1 and it has an errno. + + Returns: -1 (setting errno) on failure, the value otherwise + + Example: + pid := posix.getpid() + posix.set_errno(.NONE) + prio := posix.getpriority(.PROCESS, pid) + if err := posix.errno(); prio == -1 && err != .NONE { + // Handle error... + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpriority.html ]] + */ + getpriority :: proc(which: Which_Prio, who: id_t) -> c.int --- + + /* + Sets the nice value of the process, process group or user given. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpriority.html ]] + */ + setpriority :: proc(which: Which_Prio, who: id_t, value: c.int) -> result --- + + /* + Get a resource limit. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html ]] + */ + getrlimit :: proc(resource: Resource, rlp: ^rlimit) -> result --- + + /* + Set a resource limit. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html ]] + */ + setrlimit :: proc(resource: Resource, rlp: ^rlimit) -> result --- + + /* + Get resource usage. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrusage.html ]] + */ + @(link_name=LGETRUSAGE) + getrusage :: proc(who: Which_Usage, rusage: ^rusage) -> result --- +} + +Which_Prio :: enum c.int { + PROCESS = PRIO_PROCESS, + PGRP = PRIO_PGRP, + USER = PRIO_USER, +} + +Which_Usage :: enum c.int { + SELF = RUSAGE_SELF, + CHILDREN = RUSAGE_CHILDREN, +} + +Resource :: enum c.int { + // Maximum byte size of a core file that may be created by a process. + CORE = RLIMIT_CORE, + // Maximum amount of CPU time, in seconds, used by a process. + CPU = RLIMIT_CPU, + // Maximum size of data segment of the process, in bytes. + DATA = RLIMIT_DATA, + // Maximum size of a file, in bytes, that may be created by a process. + FSIZE = RLIMIT_FSIZE, + // A number one greater than the maximum value that the system may assign to a newly-created descriptor. + NOFILE = RLIMIT_NOFILE, + // The maximum size of the initial thread's stack, in bytes. + STACK = RLIMIT_STACK, + // Maximum size of total available memory of the process, in bytes. + AS = RLIMIT_AS, +} + +when ODIN_OS == .NetBSD { + @(private) LGETRUSAGE :: "__getrusage50" +} else { + @(private) LGETRUSAGE :: "getrusage" +} + +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + + PRIO_PROCESS :: 0 + PRIO_PGRP :: 1 + PRIO_USER :: 2 + + rlim_t :: distinct c.uint64_t + + RLIM_INFINITY :: (rlim_t(1) << 63) - 1 + RLIM_SAVED_MAX :: RLIM_INFINITY + RLIM_SAVED_CUR :: RLIM_INFINITY + + RUSAGE_SELF :: 0 + RUSAGE_CHILDREN :: -1 + + rlimit :: struct { + rlim_cur: rlim_t, /* [PSX] the current (soft) limit */ + rlim_max: rlim_t, /* [PSX] the hard limit */ + } + + rusage :: struct { + ru_utime: timeval, /* [PSX] user time used */ + ru_stime: timeval, /* [PSX] system time used */ + + // Informational aliases for source compatibility with programs + // that need more information than that provided by standards, + // and which do not mind being OS-dependent. + + ru_maxrss: c.long, /* max resident set size (PL) */ + ru_ixrss: c.long, /* integral shared memory size (NU) */ + ru_idrss: c.long, /* integral unshared data (NU) */ + ru_isrss: c.long, /* integral unshared stack (NU) */ + ru_minflt: c.long, /* page reclaims (NU) */ + ru_majflt: c.long, /* page faults (NU) */ + ru_nswap: c.long, /* swaps (NU) */ + ru_inblock: c.long, /* block input operations (atomic) */ + ru_outblock: c.long, /* block output operations (atomic) */ + ru_msgsnd: c.long, /* messages sent (atomic) */ + ru_msgrcv: c.long, /* messages received (atomic) */ + ru_nsignals: c.long, /* signals received (atomic) */ + ru_nvcsw: c.long, /* voluntary context switches (atomic) */ + ru_nivcsw: c.long, /* involuntary " */ + } + + RLIMIT_CORE :: 4 + RLIMIT_CPU :: 0 + RLIMIT_DATA :: 2 + RLIMIT_FSIZE :: 1 + RLIMIT_NOFILE :: 8 + RLIMIT_STACK :: 3 + RLIMIT_AS :: 5 when ODIN_OS == .Darwin || ODIN_OS == .OpenBSD else 10 + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/sys_select.odin b/core/sys/posix/sys_select.odin new file mode 100644 index 000000000..3392e02bc --- /dev/null +++ b/core/sys/posix/sys_select.odin @@ -0,0 +1,120 @@ +package posix + +import "base:intrinsics" + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// sys/select.h - select types + +foreign lib { + /* + Examines the file descriptor sets to see whether some of their descriptors are ready for writing, + or have an exceptional condition pending, respectively. + + Returns: -1 (setting errno) on failure, total amount of bits set in the bit masks otherwise + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html ]] + */ + @(link_name=LPSELECT) + pselect :: proc( + nfds: c.int, + readfds: ^fd_set, + writefds: ^fd_set, + errorfds: ^fd_set, + timeout: ^timespec, + sigmask: ^sigset_t, + ) -> c.int --- + + /* + Equivalent to pselect() except a more specific timeout resolution (nanoseconds), + does not have a signal mask, and may modify the timeout. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html ]] + */ + @(link_name=LSELECT) + select :: proc( + nfds: c.int, + readfds: ^fd_set, + writefds: ^fd_set, + errorfds: ^fd_set, + timeout: ^timeval, + ) -> c.int --- +} + +when ODIN_OS == .NetBSD { + LPSELECT :: "__pselect50" + LSELECT :: "__select50" +} else { + LPSELECT :: "pselect" + LSELECT :: "select" +} + +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + + suseconds_t :: distinct (c.int32_t when ODIN_OS == .Darwin || ODIN_OS == .NetBSD else c.long) + + timeval :: struct { + tv_sec: time_t, /* [PSX] seconds */ + tv_usec: suseconds_t, /* [PSX] microseconds */ + } + + // Maximum number of file descriptors in the fd_set structure. + FD_SETSIZE :: #config(POSIX_FD_SETSIZE, 256 when ODIN_OS == .NetBSD else 1024) + + @(private) + __NFDBITS :: size_of(c.int32_t) * 8 + + // NOTE: this seems correct for FreeBSD but they do use a set backed by the long type themselves (thus the align change). + @(private) + ALIGN :: align_of(c.long) when ODIN_OS == .FreeBSD else align_of(c.int32_t) + + fd_set :: struct #align(ALIGN) { + fds_bits: [(FD_SETSIZE / __NFDBITS) when (FD_SETSIZE % __NFDBITS) == 0 else (FD_SETSIZE / __NFDBITS) + 1]c.int32_t, + } + + @(private) + __check_fd_set :: #force_inline proc "contextless" (_a: FD, _b: rawptr) -> bool { + if _a < 0 { + set_errno(.EINVAL) + } + + if _a >= FD_SETSIZE { + set_errno(.EINVAL) + } + + return true + } + + FD_CLR :: #force_inline proc "contextless" (_fd: FD, _p: ^fd_set) { + if __check_fd_set(_fd, _p) { + _p.fds_bits[cast(c.ulong)_fd / __NFDBITS] &= ~cast(c.int32_t)((cast(c.ulong)1) << (cast(c.ulong)_fd % __NFDBITS)) + } + } + + FD_ISSET :: #force_inline proc "contextless" (_fd: FD, _p: ^fd_set) -> bool { + if __check_fd_set(_fd, _p) { + return bool(_p.fds_bits[cast(c.ulong)_fd / __NFDBITS] & cast(c.int32_t)((cast(c.ulong)1) << (cast(c.ulong)_fd % __NFDBITS))) + } + + return false + } + + FD_SET :: #force_inline proc "contextless" (_fd: FD, _p: ^fd_set) { + if __check_fd_set(_fd, _p) { + _p.fds_bits[cast(c.ulong)_fd / __NFDBITS] |= cast(c.int32_t)((cast(c.ulong)1) << (cast(c.ulong)_fd % __NFDBITS)) + } + } + + FD_ZERO :: #force_inline proc "contextless" (_p: ^fd_set) { + intrinsics.mem_zero(_p, size_of(fd_set)) + } + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/sys_sem.odin b/core/sys/posix/sys_sem.odin new file mode 100644 index 000000000..3fcde325b --- /dev/null +++ b/core/sys/posix/sys_sem.odin @@ -0,0 +1,132 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// sys/sem.h - XSI semaphore facility + +foreign lib { + /* + Provides various semaphore control operation as specified by cmd. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/semctl.html ]] + */ + @(link_name=LSEMCTL) + semctl :: proc(semid: FD, semnum: c.int, cmd: Sem_Cmd, arg: ^semun = nil) -> c.int --- + + /* + Returns the semaphore identifier associated with key. + + Returns: -1 (setting errno) on failure, a semaphore file descriptor otherwise + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/semget.html ]] + */ + semget :: proc(key: key_t, nsems: c.int, semflg: IPC_Flags) -> FD --- + + /* + Perform atomically a user-defined array of semaphore operations in array order on the set of + semaphores associated with the semaphore identifier specified by the argument semid. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/semop.html ]] + */ + semop :: proc(semid: FD, sops: [^]sembuf, nsops: c.size_t) -> result --- +} + +Sem_Cmd :: enum c.int { + // Returns the value of semncnt. + GETNCNT = GETNCNT, + // Returns the value of sempid. + GETPID = GETPID, + // Return the value of semval. + GETVAL = GETVAL, + // Returns the value of semval for each semaphore in the semaphore set. + GETALL = GETALL, + // Returns the value of semzcnt. + GETZCNT = GETZCNT, + // Sets the value of semval to arg.val. + SETVAL = SETVAL, + // Sets the value of semval for each semaphore in the set. + SETALL = SETALL, +} + +semun :: struct #raw_union { + val: c.int, + buf: ^semid_ds, + array: [^]c.ushort, +} + +when ODIN_OS == .NetBSD { + @(private) LSEMCTL :: "__semctl50" +} else { + @(private) LSEMCTL :: "semctl" +} + +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + + SEM_UNDO :: 0o10000 + + GETNCNT :: 3 + GETPID :: 4 + GETVAL :: 5 + GETALL :: 6 + GETZCNT :: 7 + SETVAL :: 8 + SETALL :: 9 + + when ODIN_OS == .Darwin { + // NOTE: this is #pragma pack(4) + + semid_ds :: struct #align(4) { + sem_perm: ipc_perm, /* [PSX] operation permission structure */ + sem_base: c.int32_t, /* 32 bit base ptr for semaphore set */ + sem_nsems: c.ushort, /* [PSX] number of semaphores in set */ + sem_otime: time_t, /* [PSX] last semop() */ + sem_pad1: c.int32_t, + using _: struct #align(4) { + sem_ctime: time_t, /* [PSX] last time changed by semctl() */ + sem_pad2: c.int32_t, + sem_pad3: [4]c.int32_t, + }, + } + } else when ODIN_OS == .FreeBSD { + semid_ds :: struct { + sem_perm: ipc_perm, /* [PSX] operation permission structure */ + sem_base: rawptr, /* 32 bit base ptr for semaphore set */ + sem_nsems: c.ushort, /* [PSX] number of semaphores in set */ + sem_otime: time_t, /* [PSX] last semop() */ + sem_ctime: time_t, /* [PSX] last time changed by semctl() */ + } + } else when ODIN_OS == .NetBSD { + semid_ds :: struct { + sem_perm: ipc_perm, /* [PSX] operation permission structure */ + sem_nsems: c.ushort, /* [PSX] number of semaphores in set */ + sem_otime: time_t, /* [PSX] last semop() */ + sem_ctime: time_t, /* [PSX] last time changed by semctl() */ + _sem_base: rawptr, /* 32 bit base ptr for semaphore set */ + } + } else when ODIN_OS == .OpenBSD { + semid_ds :: struct { + sem_perm: ipc_perm, /* [PSX] operation permission structure */ + sem_nsems: c.ushort, /* [PSX] number of semaphores in set */ + sem_otime: time_t, /* [PSX] last semop() */ + sem_pad1: c.long, + sem_ctime: time_t, /* [PSX] last time changed by semctl() */ + sem_pad2: c.long, + sem_pad3: [4]c.long, + } + } + + sembuf :: struct { + sem_num: c.ushort, /* [PSX] semaphore number */ + sem_op: c.short, /* [PSX] semaphore operation */ + sem_flg: c.short, /* [PSX] operation flags */ + } + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/sys_shm.odin b/core/sys/posix/sys_shm.odin new file mode 100644 index 000000000..3bc883ce4 --- /dev/null +++ b/core/sys/posix/sys_shm.odin @@ -0,0 +1,146 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// sys/shm.h = XSI shared memory facility + +foreign lib { + /* + Attaches the shared memory segment associated with the identifier + into the address space of the calling process. + + Returns: nil (setting errno) on failure, the address otherwise + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/shmat.html ]] + */ + shmat :: proc(shmid: FD, shmaddr: rawptr, shmflag: SHM_Flags) -> rawptr --- + + /* + Provides various shared memory operation as specified by the given cmd. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/shmctl.html ]] + */ + @(link_name=LSHMCTL) + shmctl :: proc(shmid: FD, cmd: IPC_Cmd, buf: ^shmid_ds) -> result --- + + /* + Detaches the shared memory segment located at the address specified. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/shmdt.html ]] + */ + shmdt :: proc(shmaddr: rawptr) -> result --- + + /* + Returns the shared memory identifier associated with key. + + Returns: -1 (setting errno) on failure, the shared memory ID otherwise + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/shmget.html ]] + */ + shmget :: proc(key: key_t, size: c.size_t, shmflag: SHM_Flags) -> FD --- +} + +SHM_Flag_Bits :: enum c.int { + RDONLY = log2(SHM_RDONLY), + RND = log2(SHM_RND), +} +SHM_Flags :: bit_set[SHM_Flag_Bits; c.int] + +when ODIN_OS == .NetBSD { + @(private) LSHMCTL :: "__shmctl50" +} else { + @(private) LSHMCTL :: "shmctl" +} + +when ODIN_OS == .Darwin { + + SHM_RDONLY :: 0o10000 + SHM_RND :: 0o20000 + + SHMLBA :: 16 * 1024 when ODIN_ARCH == .arm64 else 4096 + + shmatt_t :: distinct c.ushort + + // NOTE: this is #pragma pack(4) + + shmid_ds :: struct #align(4) { + shm_perm: ipc_perm, /* [PSX] operation permission structure */ + shm_segsz: c.size_t, /* [PSX] size of segment in bytes */ + shm_lpid: pid_t, /* [PSX] process ID of last shared memory operation */ + shm_cpid: pid_t, /* [PSX] process ID of creator */ + shm_nattch: shmatt_t, /* [PSX] number of current attaches */ + using _: struct #align(4) { + shm_atime: time_t, /* [PSX] time of last shmat() */ + shm_dtime: time_t, /* [PSX] time of last shmdt() */ + shm_ctime: time_t, /* [PSX] time of last change by shmctl() */ + shm_internal: rawptr, + }, + } + +} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD { + + SHM_RDONLY :: 0o10000 + SHM_RND :: 0o20000 + + SHMLBA :: PAGESIZE + + shmatt_t :: distinct c.uint + + when ODIN_OS == .FreeBSD { + shmid_ds :: struct { + shm_perm: ipc_perm, /* [PSX] operation permission structure */ + shm_segsz: c.size_t, /* [PSX] size of segment in bytes */ + shm_lpid: pid_t, /* [PSX] process ID of last shared memory operation */ + shm_cpid: pid_t, /* [PSX] process ID of creator */ + shm_nattch: shmatt_t, /* [PSX] number of current attaches */ + shm_atime: time_t, /* [PSX] time of last shmat() */ + shm_dtime: time_t, /* [PSX] time of last shmdt() */ + shm_ctime: time_t, /* [PSX] time of last change by shmctl() */ + } + } else { + shmid_ds :: struct { + shm_perm: ipc_perm, /* [PSX] operation permission structure */ + shm_segsz: c.size_t, /* [PSX] size of segment in bytes */ + shm_lpid: pid_t, /* [PSX] process ID of last shared memory operation */ + shm_cpid: pid_t, /* [PSX] process ID of creator */ + shm_nattch: shmatt_t, /* [PSX] number of current attaches */ + shm_atime: time_t, /* [PSX] time of last shmat() */ + shm_dtime: time_t, /* [PSX] time of last shmdt() */ + shm_ctime: time_t, /* [PSX] time of last change by shmctl() */ + _shm_internal: rawptr, + } + } + +} else when ODIN_OS == .OpenBSD { + + SHM_RDONLY :: 0o10000 + SHM_RND :: 0o20000 + + SHMLBA :: 1 << 12 + + shmatt_t :: distinct c.short + + shmid_ds :: struct { + shm_perm: ipc_perm, /* [PSX] operation permission structure */ + shm_segsz: c.int, /* [PSX] size of segment in bytes */ + shm_lpid: pid_t, /* [PSX] process ID of last shared memory operation */ + shm_cpid: pid_t, /* [PSX] process ID of creator */ + shm_nattch: shmatt_t, /* [PSX] number of current attaches */ + shm_atime: time_t, /* [PSX] time of last shmat() */ + __shm_atimensec: c.long, + shm_dtime: time_t, /* [PSX] time of last shmdt() */ + __shm_dtimensec: c.long, + shm_ctime: time_t, /* [PSX] time of last change by shmctl() */ + __shm_ctimensec: c.long, + _shm_internal: rawptr, + } + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/sys_socket.odin b/core/sys/posix/sys_socket.odin new file mode 100644 index 000000000..e82101367 --- /dev/null +++ b/core/sys/posix/sys_socket.odin @@ -0,0 +1,491 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import libc "system:System.framework" +} else { + foreign import libc "system:c" +} + +// sys/socket.h - main sockets header + +#assert(Protocol.IP == Protocol(0), "socket() assumes this") + +foreign libc { + /* + Creates a socket. + + Returns: -1 (setting errno) on failure, file descriptor of socket otherwise + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html ]] + */ + @(link_name=LSOCKET) + socket :: proc(domain: AF, type: Sock, protocol: Protocol = .IP) -> FD --- + + /* + Extracts the first connection on the queue of pending connections. + + Blocks (if not O_NONBLOCK) if there is no pending connection. + + Returns: -1 (setting errno) on failure, file descriptor of accepted socket otherwise + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.html ]] + */ + accept :: proc(socket: FD, address: ^sockaddr, address_len: ^socklen_t) -> FD --- + + /* + Assigns a local socket address to the socket. + + Example: + sfd := posix.socket(.UNIX, .STREAM) + if sfd == -1 { + /* Handle error */ + } + + addr: posix.sockaddr_un + addr.sun_family = .UNIX + copy(addr.sun_path[:], "/somepath\x00") + + if posix.bind(sfd, (^posix.sockaddr)(&addr), size_of(addr)) != .OK { + /* Handle error */ + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html ]] + */ + bind :: proc(socket: FD, address: ^sockaddr, address_len: socklen_t) -> result --- + + /* + Attempt to make a connection. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html ]] + */ + connect :: proc(socket: FD, address: ^sockaddr, address_len: socklen_t) -> result --- + + /* + Get the peer address of the specified socket. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html ]] + */ + getpeername :: proc(socket: FD, address: ^sockaddr, address_len: ^socklen_t) -> result --- + + /* + Get the socket name. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html ]] + */ + getsockname :: proc(socket: FD, address: ^sockaddr, address_len: ^socklen_t) -> result --- + + /* + Retrieves the value for the option specified by option_name. + + level: either `c.int(posix.Protocol(...))` to specify a protocol level or `posix.SOL_SOCKET` + to specify the socket local level. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockopt.html ]] + */ + getsockopt :: proc( + socket: FD, + level: c.int, + option_name: Sock_Option, + option_value: rawptr, + option_len: ^socklen_t, + ) -> result --- + + /* + Sets the specified option. + + level: either `c.int(posix.Protocol(...))` to specify a protocol level or `posix.SOL_SOCKET` + to specify the socket local level. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setsockopt.html ]] + */ + setsockopt :: proc( + socket: FD, + level: c.int, + option_name: Sock_Option, + option_value: rawptr, + option_len: socklen_t, + ) -> result --- + + /* + Mark the socket as a socket accepting connections. + + backlog provides a hint to limit the number of connections on the listen queue. + Implementation may silently reduce the backlog, additionally `SOMAXCONN` specifies the maximum + an implementation has to support. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html ]] + */ + listen :: proc(socket: FD, backlog: c.int) -> result --- + + /* + Receives a message from a socket. + + Blocks (besides with O_NONBLOCK) if there is nothing to receive. + + Returns: 0 when the peer shutdown with no more messages, -1 (setting errno) on failure, the amount of bytes received on success + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/recv.html ]] + */ + recv :: proc(socket: FD, buffer: rawptr, length: c.size_t, flags: Msg_Flags) -> c.ssize_t --- + + /* + Receives a message from a socket. + + Equivalent to recv() but retrieves the source address too. + + Returns: 0 when the peer shutdown with no more messages, -1 (setting errno) on failure, the amount of bytes received on success + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html ]] + */ + recvfrom :: proc( + socket: FD, + buffer: rawptr, + length: c.size_t, + flags: Msg_Flags, + address: ^sockaddr, + address_len: ^socklen_t, + ) -> c.ssize_t --- + + /* + Receives a message from a socket. + + Returns: 0 when the peer shutdown with no more messages, -1 (setting errno) on failure, the amount of bytes received on success + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvmsg.html ]] + */ + recvmsg :: proc(socket: FD, message: ^msghdr, flags: Msg_Flags) -> c.ssize_t --- + + /* + Sends a message on a socket. + + Returns: -1 (setting errno) on failure, the amount of bytes received on success + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/send.html ]] + */ + send :: proc(socket: FD, buffer: rawptr, length: c.size_t, flags: Msg_Flags) -> c.ssize_t --- + + /* + Sends a message on a socket. + + Returns: -1 (setting errno) on failure, the amount of bytes received on success + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendmsg.html ]] + */ + sendmsg :: proc(socket: FD, message: ^msghdr, flags: Msg_Flags) -> c.ssize_t --- + + /* + Sends a message on a socket. + + If the socket is connectionless, the dest_addr is used to send to. + + Returns: -1 (setting errno) on failure, the amount of bytes received on success + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendto.html ]] + */ + sendto :: proc( + socket: FD, + message: rawptr, + length: c.size_t, + flags: Msg_Flags, + dest_addr: ^sockaddr, + dest_len: socklen_t, + ) -> c.ssize_t --- + + /* + Shuts down a socket end or both. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/shutdown.html ]] + */ + shutdown :: proc(socket: FD, how: Shut) -> result --- + + /* + Determine wheter a socket is at the out-of-band mark. + + Returns: -1 (setting errno) on failure, 0 if not at the mark, 1 if it is + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sockatmark.html ]] + */ + sockatmark :: proc(socket: FD) -> c.int --- + + /* + Create a pair of connected sockets. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/socketpair.html ]] + */ + socketpair :: proc(domain: AF, type: Sock, protocol: Protocol, socket_vector: ^[2]FD) -> result --- +} + +AF_UNSPEC :: 0 + +AF :: enum c.int { + // Unspecified. + UNSPEC = AF_UNSPEC, + // Internet domain sockets for use with IPv4 addresses. + INET = AF_INET, + // Internet domain sockets for use with IPv6 addresses. + INET6 = AF_INET6, + // UNIX domain sockets. + UNIX = AF_UNIX, +} + +sa_family_t :: enum _sa_family_t { + // Unspecified. + UNSPEC = AF_UNSPEC, + // Internet domain sockets for use with IPv4 addresses. + INET = AF_INET, + // Internet domain sockets for use with IPv6 addresses. + INET6 = AF_INET6, + // UNIX domain sockets. + UNIX = AF_UNIX, +} + +Sock :: enum c.int { + // Datagram socket. + DGRAM = SOCK_DGRAM, + // Raw Protocol Interface. + RAW = SOCK_RAW, + // Sequenced-packet socket. + SEQPACKET = SOCK_SEQPACKET, + // Byte-stream socket. + STREAM = SOCK_STREAM, +} + +Shut :: enum c.int { + // Disables further receive operations. + RD = SHUT_RD, + // Disables further send and receive operations. + RDWR = SHUT_RDWR, + // Disables further send operations. + WR = SHUT_WR, +} + +Msg_Flag_Bits :: enum c.int { + // Control data truncated. + CTRUNC = log2(MSG_CTRUNC), + // Send without using routing table. + DONTROUTE = log2(MSG_DONTROUTE), + // Terminates a record (if supported by protocol). + EOR = log2(MSG_EOR), + // Out-of-band data. + OOB = log2(MSG_OOB), + // No SIGPIPE is generated when an attempt to send is made on a stream-oriented socket that is + // no longer connected. + NOSIGNAL = log2(MSG_NOSIGNAL), + // Leave received data in queue. + PEEK = log2(MSG_PEEK), + // Normal data truncated. + TRUNC = log2(MSG_TRUNC), + // Attempt to fill the read buffer. + WAITALL = log2(MSG_WAITALL), +} +Msg_Flags :: bit_set[Msg_Flag_Bits; c.int] + +Sock_Option :: enum c.int { + // Transmission of broadcast message is supported. + BROADCAST = SO_BROADCAST, + // Debugging information is being recorded. + DEBUG = SO_DEBUG, + // Bypass normal routing. + DONTROUTE = SO_DONTROUTE, + // Socket error status. + ERROR = SO_ERROR, + // Connections are kept alive with periodic messages. + KEEPALIVE = SO_KEEPALIVE, + // Socket lingers on close. + LINGER = SO_LINGER, + // Out-of-band data is transmitted in line. + OOBINLINE = SO_OOBINLINE, + // Receive buffer size. + RCVBUF = SO_RCVBUF, + // Receive low water mark. + RCVLOWAT = SO_RCVLOWAT, + // Receive timeout. + RCVTIMEO = SO_RCVTIMEO, + // Reuse of local addresses is supported. + REUSEADDR = SO_REUSEADDR, + // Send buffer size. + SNDBUF = SO_SNDBUF, + // Send low water mark. + SNDLOWAT = SO_SNDLOWAT, + // Send timeout. + SNDTIMEO = SO_SNDTIMEO, + // Socket type. + TYPE = SO_TYPE, +} + +when ODIN_OS == .NetBSD { + @(private) LSOCKET :: "__socket30" +} else { + @(private) LSOCKET :: "socket" +} + +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + + socklen_t :: distinct c.uint + + _sa_family_t :: distinct c.uint8_t + + sockaddr :: struct { + sa_len: c.uint8_t, /* total length */ + sa_family: sa_family_t, /* [PSX] address family */ + sa_data: [14]c.char, /* [PSX] socket address */ + } + + + when ODIN_OS == .OpenBSD { + @(private) + _SS_PAD1SIZE :: 6 + @(private) + _SS_PAD2SIZE :: 240 + } else { + @(private) + _SS_MAXSIZE :: 128 + @(private) + _SS_ALIGNSIZE :: size_of(c.int64_t) + @(private) + _SS_PAD1SIZE :: _SS_ALIGNSIZE - size_of(c.uint8_t) - size_of(sa_family_t) + @(private) + _SS_PAD2SIZE :: _SS_MAXSIZE - size_of(c.uint8_t) - size_of(sa_family_t) - _SS_PAD1SIZE - _SS_ALIGNSIZE + } + + sockaddr_storage :: struct { + ss_len: c.uint8_t, /* address length */ + ss_family: sa_family_t, /* [PSX] address family */ + __ss_pad1: [_SS_PAD1SIZE]c.char, + __ss_align: c.int64_t, /* force structure storage alignment */ + __ss_pad2: [_SS_PAD2SIZE]c.char, + } + + msghdr :: struct { + msg_name: rawptr, /* [PSX] optional address */ + msg_namelen: socklen_t, /* [PSX] size of address */ + msg_iov: [^]iovec, /* [PSX] scatter/gather array */ + msg_iovlen: c.int, /* [PSX] members in msg_iov */ + msg_control: rawptr, /* [PSX] ancillary data */ + msg_controllen: socklen_t, /* [PSX] ancillary data buffer length */ + msg_flags: Msg_Flags, /* [PSX] flags on received message */ + } + + cmsghdr :: struct { + cmsg_len: socklen_t, /* [PSX] data byte count, including cmsghdr */ + cmsg_level: c.int, /* [PSX] originating protocol */ + cmsg_type: c.int, /* [PSX] protocol-specific type */ + } + + SCM_RIGHTS :: 0x01 + + @(private) + __ALIGN32 :: #force_inline proc "contextless" (p: uintptr) -> uintptr { + __ALIGNBYTES32 :: size_of(c.uint32_t) - 1 + return (p + __ALIGNBYTES32) &~ __ALIGNBYTES32 + } + + // Returns a pointer to the data array. + CMSG_DATA :: #force_inline proc "contextless" (cmsg: ^cmsghdr) -> [^]c.uchar { + return ([^]c.uchar)(uintptr(cmsg) + __ALIGN32(size_of(cmsghdr))) + } + + // Returns a pointer to the next cmsghdr or nil. + CMSG_NXTHDR :: #force_inline proc "contextless" (mhdr: ^msghdr, cmsg: ^cmsghdr) -> ^cmsghdr { + if cmsg == nil { + return CMSG_FIRSTHDR(mhdr) + } + + ptr := uintptr(cmsg) + __ALIGN32(uintptr(cmsg.cmsg_len)) + if ptr + __ALIGN32(size_of(cmsghdr)) > uintptr(mhdr.msg_control) + uintptr(mhdr.msg_controllen) { + return nil + } + + return (^cmsghdr)(ptr) + } + + // Returns a pointer to the first cmsghdr or nil. + CMSG_FIRSTHDR :: #force_inline proc "contextless" (mhdr: ^msghdr) -> ^cmsghdr { + if mhdr.msg_controllen >= size_of(cmsghdr) { + return (^cmsghdr)(mhdr.msg_control) + } + + return nil + } + + linger :: struct { + l_onoff: c.int, /* [PSX] indicates whether linger option is enabled */ + l_linger: c.int, /* [PSX] linger time in seconds */ + } + + SOCK_DGRAM :: 2 + SOCK_RAW :: 3 + SOCK_SEQPACKET :: 5 + SOCK_STREAM :: 1 + + // Options to be accessed at socket level, not protocol level. + SOL_SOCKET :: 0xffff + + SO_ACCEPTCONN :: 0x0002 + SO_BROADCAST :: 0x0020 + SO_DEBUG :: 0x0001 + SO_DONTROUTE :: 0x0010 + SO_ERROR :: 0x1007 + SO_KEEPALIVE :: 0x0008 + SO_OOBINLINE :: 0x0100 + SO_RCVBUF :: 0x1002 + SO_RCVLOWAT :: 0x1004 + SO_REUSEADDR :: 0x0004 + SO_SNDBUF :: 0x1001 + SO_SNDLOWAT :: 0x1003 + SO_TYPE :: 0x1008 + + when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD { + SO_LINGER :: 0x1080 + SO_RCVTIMEO :: 0x1006 + SO_SNDTIMEO :: 0x1005 + } else when ODIN_OS == .NetBSD { + SO_LINGER :: 0x0080 + SO_RCVTIMEO :: 0x100c + SO_SNDTIMEO :: 0x100b + } else when ODIN_OS == .OpenBSD { + SO_LINGER :: 0x0080 + SO_RCVTIMEO :: 0x1006 + SO_SNDTIMEO :: 0x1005 + } + + // The maximum backlog queue length for listen(). + SOMAXCONN :: 128 + + MSG_CTRUNC :: 0x20 + MSG_DONTROUTE :: 0x4 + MSG_EOR :: 0x8 + MSG_OOB :: 0x1 + MSG_PEEK :: 0x2 + MSG_TRUNC :: 0x10 + MSG_WAITALL :: 0x40 + + when ODIN_OS == .Darwin { + MSG_NOSIGNAL :: 0x80000 + } else when ODIN_OS == .FreeBSD { + MSG_NOSIGNAL :: 0x00020000 + } else when ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + MSG_NOSIGNAL :: 0x0400 + } + + AF_INET :: 2 + AF_UNIX :: 1 + + when ODIN_OS == .Darwin { + AF_INET6 :: 30 + } else when ODIN_OS == .FreeBSD { + AF_INET6 :: 28 + } else when ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + AF_INET6 :: 24 + } + + SHUT_RD :: 0 + SHUT_RDWR :: 2 + SHUT_WR :: 1 + +} else { + #panic("posix is unimplemented for the current target") +} + diff --git a/core/sys/posix/sys_stat.odin b/core/sys/posix/sys_stat.odin new file mode 100644 index 000000000..016f1b1e6 --- /dev/null +++ b/core/sys/posix/sys_stat.odin @@ -0,0 +1,540 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// sys/stat.h - data returned by the stat() function + +foreign lib { + + /* + Equivalent to either stat or lstat (based on the SYMLINK_NOFOLLOW bit in flags) + but resolves relative paths based on the given fd. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstatat.html ]] + */ + @(link_name="fstatat" + INODE_SUFFIX) + fstatat :: proc(fd: FD, path: cstring, buf: ^stat_t, flag: AT_Flags) -> result --- + + /* + Obtain information about a "file" at the given path. + + Follows symbolic links. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstatat.html ]] + */ + @(link_name=LSTAT) + stat :: proc(path: cstring, buf: ^stat_t) -> result --- + + /* + Obtain information about an open file. + + Follows symbol links. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstat.html ]] + */ + @(link_name=LFSTAT) + fstat :: proc(fildes: FD, buf: ^stat_t) -> result --- + + /* + Obtain information about a "file" at the given path. + + Does not follow symlinks (will stat the symlink itself). + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstatat.html ]] + */ + @(link_name=LLSTAT) + lstat :: proc(path: cstring, buf: ^stat_t) -> result --- + + /* + Change the mode of a file. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/chmod.html ]] + */ + chmod :: proc(path: cstring, mode: mode_t) -> result --- + + /* + Equivalent to chmod but takes an open file descriptor. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html ]] + */ + fchmod :: proc(fd: FD, mode: mode_t) -> result --- + + /* + Equivalent to chmod but follows (or doesn't) symlinks based on the flag and resolves + relative paths from the given fd. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/chmod.html ]] + */ + fchmodat :: proc(fd: FD, path: cstring, mode: mode_t, flag: AT_Flags) -> result --- + + /* + Make a directory. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdir.html ]] + */ + mkdir :: proc(path: cstring, mode: mode_t) -> result --- + + /* + Equivalent to mkdir but relative paths are relative to fd. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdir.html ]] + */ + mkdirat :: proc(fd: FD, path: cstring, mode: mode_t) -> result --- + + /* + Make a FIFO special file. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkfifo.html ]] + */ + mkfifo :: proc(path: cstring, mode: mode_t) -> result --- + + /* + Equivalent to mkfifo but relative paths are relative to fd. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkfifo.html ]] + */ + mkfifoat :: proc(fd: FD, path: cstring, mode: mode_t) -> result --- + + /* + Make directory, special file, or regular file. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mknodat.html ]] + */ + @(link_name=LMKNOD) + mknod :: proc(path: cstring, mode: mode_t, dev: dev_t) -> result --- + + /* + Equivalent to mknod but relative paths are relative to fd. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mknodat.html ]] + */ + mknodat :: proc(fd: FD, path: cstring, mode: mode_t, dev: dev_t) -> result --- + + /* + Sets the file access and modification time of the given file descriptor. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html ]] + */ + futimens :: proc(fd: FD, times: ^[2]timespec) -> result --- + + /* + Equivalent to futimens. + Relative directories are based on fd. + Symlinks may or may not be followed based on the flags. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html ]] + */ + utimensat :: proc(fd: FD, path: cstring, times: ^[2]timespec, flag: AT_Flags) -> result --- + + /* + Set and get the file mode creation flags. + + Makes the file mode permissions bits in cmask the new default for the process. + + Returns: the previous value + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/umask.html ]] + */ + umask :: proc(cmask: mode_t) -> mode_t --- +} + +// Read, write, execute user. +S_IRWXU :: mode_t{ .IRUSR, .IWUSR, .IXUSR } +// Read, write, execute group. +S_IRWXG :: mode_t{ .IRGRP, .IWGRP, .IXGRP } +// Read, write, execute other. +S_IRWXO :: mode_t{ .IROTH, .IWOTH, .IXOTH } + +Mode_Bits :: enum c.int { + // File type: + + IFBLK = log2(S_IFBLK), /* Block special */ + IFCHR = log2(S_IFCHR), /* Character special */ + IFIFO = log2(S_IFIFO), /* FIFO special */ + IFREG = log2(S_IFREG), /* Regular */ + IFDIR = log2(S_IFDIR), /* Directory */ + IFLNK = log2(S_IFLNK), /* Symbolic link */ + IFSOCK = log2(S_IFSOCK), /* Socket */ + + // Permissions: + + IRUSR = log2(_S_IRUSR), /* R for owner */ + IWUSR = log2(_S_IWUSR), /* W for owner */ + IXUSR = log2(_S_IXUSR), /* X for owner */ + + IRGRP = log2(_S_IRGRP), /* R for group */ + IWGRP = log2(_S_IWGRP), /* W for group */ + IXGRP = log2(_S_IXGRP), /* X for group */ + + IROTH = log2(_S_IROTH), /* R for other */ + IWOTH = log2(_S_IWOTH), /* W for other */ + IXOTH = log2(_S_IXOTH), /* X for other */ + + ISUID = log2(_S_ISUID), /* Set user ID on execution */ + ISGID = log2(_S_ISGID), /* Set group ID on execution */ + ISVXT = log2(_S_ISVTX), /* On directories, restricted deletion flag */ +} +mode_t :: bit_set[Mode_Bits; _mode_t] +#assert(size_of(mode_t) == size_of(_mode_t)) + +// NOTE: making these `.IFREG in m` would probably be fine too, +// but implementations make this an exclusive check so lets stick to it. + +_S_IFMT :: mode_t{ .IFBLK, .IFCHR, .IFIFO, .IFREG, .IFDIR, .IFLNK, .IFSOCK } + +// Test for a block special file. +S_ISBLK :: #force_inline proc "contextless" (m: mode_t) -> bool { + return (m & _S_IFMT) == { .IFBLK } +} + +// Test for a character special file. +S_ISCHR :: #force_inline proc "contextless" (m: mode_t) -> bool { + return (m & _S_IFMT) == { .IFCHR } +} + +// Test for a pipe or FIFO special file. +S_ISFIFO :: #force_inline proc "contextless" (m: mode_t) -> bool { + return (m & _S_IFMT) == { .IFIFO } +} + +// Test for a regular file. +S_ISREG :: #force_inline proc "contextless" (m: mode_t) -> bool { + return (m & _S_IFMT) == { .IFREG } +} + +// Test for a directory. +S_ISDIR :: #force_inline proc "contextless" (m: mode_t) -> bool { + return (m & _S_IFMT) == { .IFDIR } +} + +// Test for a symbolic link. +S_ISLNK :: #force_inline proc "contextless" (m: mode_t) -> bool { + return (m & _S_IFMT) == { .IFLNK } +} + +// Test for a socket. +S_ISSOCK :: #force_inline proc "contextless" (m: mode_t) -> bool { + return (m & _S_IFMT) == { .IFSOCK } +} + +// Test for a message queue. +S_TYPEISMQ :: #force_inline proc "contextless" (m: mode_t) -> bool { + return _S_TYPEISMQ(m) +} + +// Test for a semaphore. +S_TYPEISSEM :: #force_inline proc "contextless" (m: mode_t) -> bool { + return _S_TYPEISSEM(m) +} + +// Test for a shared memory object. +S_TYPEISSHM :: #force_inline proc "contextless" (m: mode_t) -> bool { + return _S_TYPEISSHM(m) +} + +// Test macro for a typed memory object. +S_TYPEISTMO :: #force_inline proc "contextless" (m: mode_t) -> bool { + return _S_TYPEISTMO(m) +} + +_S_IRWXU :: 0o000700 +_S_IRUSR :: 0o000400 +_S_IWUSR :: 0o000200 +_S_IXUSR :: 0o000100 + +_S_IRWXG :: 0o000070 +_S_IRGRP :: 0o000040 +_S_IWGRP :: 0o000020 +_S_IXGRP :: 0o000010 + +_S_IRWXO :: 0o000007 +_S_IROTH :: 0o000004 +_S_IWOTH :: 0o000002 +_S_IXOTH :: 0o000001 + +_S_ISUID :: 0o004000 +_S_ISGID :: 0o002000 +_S_ISVTX :: 0o001000 + +when ODIN_OS == .NetBSD { + @(private) LSTAT :: "__stat50" + @(private) LFSTAT :: "__fstat50" + @(private) LLSTAT :: "__lstat50" + @(private) LMKNOD :: "__mknod50" +} else { + @(private) LSTAT :: "stat" + INODE_SUFFIX + @(private) LFSTAT :: "fstat" + INODE_SUFFIX + @(private) LLSTAT :: "lstat" + INODE_SUFFIX + @(private) LMKNOD :: "mknod" +} + +when ODIN_OS == .Darwin { + + dev_t :: distinct c.int32_t + nlink_t :: distinct c.uint16_t + _mode_t :: distinct c.uint16_t + blkcnt_t :: distinct c.int64_t + blksize_t :: distinct c.int32_t + ino_t :: distinct c.uint64_t + + stat_t :: struct { + st_dev: dev_t, /* [XSI] ID of device containing file */ + st_mode: mode_t, /* [XSI] mode of file */ + st_nlink: nlink_t, /* [XSI] number of hard links */ + st_ino: ino_t, /* [XSI] file serial number */ + st_uid: uid_t, /* [XSI] user ID of the file */ + st_gid: gid_t, /* [XSI] group ID of the file */ + st_rdev: dev_t, /* [XSI] device ID */ + st_atim: timespec, /* [XSI] time of last access */ + st_mtim: timespec, /* [XSI] time of last data modification */ + st_ctim: timespec, /* [XSI] time of last status change */ + st_birthtimespec: timespec, /* time of file creation(birth) */ + st_size: off_t, /* [XSI] file size, in bytes */ + st_blocks: blkcnt_t, /* [XSI] blocks allocated for file */ + st_blksize: blksize_t, /* [XSI] optimal blocksize for I/O */ + st_flags: c.uint32_t, /* user defined flags for file */ + st_gen: c.uint32_t, /* file generation number */ + st_lspare: c.int32_t, /* RESERVED */ + st_qspare: [2]c.int64_t, /* RESERVED */ + } + + S_IFBLK :: 0o060000 + S_IFCHR :: 0o020000 + S_IFIFO :: 0o010000 + S_IFREG :: 0o100000 + S_IFDIR :: 0o040000 + S_IFLNK :: 0o120000 + S_IFSOCK :: 0o140000 + + __S_IFMT :: 0o170000 + + _S_TYPEISMQ :: #force_inline proc "contextless" (m: mode_t) -> bool { + return false + } + + _S_TYPEISSEM :: #force_inline proc "contextless" (m: mode_t) -> bool { + return false + } + + _S_TYPEISSHM :: #force_inline proc "contextless" (m: mode_t) -> bool { + return false + } + + _S_TYPEISTMO :: #force_inline proc "contextless" (m: mode_t) -> bool { + return false + } + + UTIME_NOW :: -1 + UTIME_OMIT :: -2 + +} else when ODIN_OS == .FreeBSD { + + dev_t :: distinct c.uint64_t + nlink_t :: distinct c.uint64_t + _mode_t :: distinct c.uint16_t + blkcnt_t :: distinct c.int64_t + blksize_t :: distinct c.int32_t + ino_t :: distinct c.uint64_t + + when ODIN_ARCH == .i386 { + stat_t :: struct { + st_dev: dev_t, /* [XSI] ID of device containing file */ + st_ino: ino_t, /* [XSI] file serial number */ + st_nlink: nlink_t, /* [XSI] number of hard links */ + st_mode: mode_t, /* [XSI] mode of file */ + st_padding0: c.int16_t, + st_uid: uid_t, /* [XSI] user ID of the file */ + st_gid: gid_t, /* [XSI] group ID of the file */ + st_padding1: c.int32_t, + st_rdev: dev_t, /* [XSI] device ID */ + st_atim_ext: c.int32_t, + st_atim: timespec, /* [XSI] time of last access */ + st_mtim_ext: c.int32_t, + st_mtim: timespec, /* [XSI] time of last data modification */ + st_ctim_ext: c.int32_t, + st_ctim: timespec, /* [XSI] time of last status change */ + st_birthtimespec: timespec, /* time of file creation(birth) */ + st_size: off_t, /* [XSI] file size, in bytes */ + st_blocks: blkcnt_t, /* [XSI] blocks allocated for file */ + st_blksize: blksize_t, /* [XSI] optimal blocksize for I/O */ + st_flags: c.uint32_t, /* user defined flags for file */ + st_gen: c.uint64_t, + st_spare: [10]c.uint64_t, + } + } else { + stat_t :: struct { + st_dev: dev_t, /* [XSI] ID of device containing file */ + st_ino: ino_t, /* [XSI] file serial number */ + st_nlink: nlink_t, /* [XSI] number of hard links */ + st_mode: mode_t, /* [XSI] mode of file */ + st_padding0: c.int16_t, + st_uid: uid_t, /* [XSI] user ID of the file */ + st_gid: gid_t, /* [XSI] group ID of the file */ + st_padding1: c.int32_t, + st_rdev: dev_t, /* [XSI] device ID */ + st_atim: timespec, /* [XSI] time of last access */ + st_mtim: timespec, /* [XSI] time of last data modification */ + st_ctim: timespec, /* [XSI] time of last status change */ + st_birthtimespec: timespec, /* time of file creation(birth) */ + st_size: off_t, /* [XSI] file size, in bytes */ + st_blocks: blkcnt_t, /* [XSI] blocks allocated for file */ + st_blksize: blksize_t, /* [XSI] optimal blocksize for I/O */ + st_flags: c.uint32_t, /* user defined flags for file */ + st_gen: c.uint64_t, + st_spare: [10]c.uint64_t, + } + } + + S_IFBLK :: 0o060000 + S_IFCHR :: 0o020000 + S_IFIFO :: 0o010000 + S_IFREG :: 0o100000 + S_IFDIR :: 0o040000 + S_IFLNK :: 0o120000 + S_IFSOCK :: 0o140000 + + __S_IFMT :: 0o170000 + + _S_TYPEISMQ :: #force_inline proc "contextless" (m: mode_t) -> bool { + return false + } + + _S_TYPEISSEM :: #force_inline proc "contextless" (m: mode_t) -> bool { + return false + } + + _S_TYPEISSHM :: #force_inline proc "contextless" (m: mode_t) -> bool { + return false + } + + _S_TYPEISTMO :: #force_inline proc "contextless" (m: mode_t) -> bool { + return false + } + + UTIME_NOW :: -1 + UTIME_OMIT :: -2 + +} else when ODIN_OS == .NetBSD { + + dev_t :: distinct c.uint64_t + nlink_t :: distinct c.uint32_t + _mode_t :: distinct c.uint32_t + blkcnt_t :: distinct c.int64_t + blksize_t :: distinct c.int32_t + ino_t :: distinct c.uint64_t + + stat_t :: struct { + st_dev: dev_t, /* [XSI] ID of device containing file */ + st_mode: mode_t, /* [XSI] mode of file */ + st_ino: ino_t, /* [XSI] file serial number */ + st_nlink: nlink_t, /* [XSI] number of hard links */ + st_uid: uid_t, /* [XSI] user ID of the file */ + st_gid: gid_t, /* [XSI] group ID of the file */ + st_rdev: dev_t, /* [XSI] device ID */ + st_atim: timespec, /* [XSI] time of last access */ + st_mtim: timespec, /* [XSI] time of last data modification */ + st_ctim: timespec, /* [XSI] time of last status change */ + st_birthtimespec: timespec, /* time of file creation(birth) */ + st_size: off_t, /* [XSI] file size, in bytes */ + st_blocks: blkcnt_t, /* [XSI] blocks allocated for file */ + st_blksize: blksize_t, /* [XSI] optimal blocksize for I/O */ + st_flags: c.uint32_t, /* user defined flags for file */ + st_gen: c.uint64_t, + st_spare: [2]c.uint32_t, + } + + S_IFBLK :: 0o060000 + S_IFCHR :: 0o020000 + S_IFIFO :: 0o010000 + S_IFREG :: 0o100000 + S_IFDIR :: 0o040000 + S_IFLNK :: 0o120000 + S_IFSOCK :: 0o140000 + + __S_IFMT :: 0o170000 + + _S_TYPEISMQ :: #force_inline proc "contextless" (m: mode_t) -> bool { + return false + } + + _S_TYPEISSEM :: #force_inline proc "contextless" (m: mode_t) -> bool { + return false + } + + _S_TYPEISSHM :: #force_inline proc "contextless" (m: mode_t) -> bool { + return false + } + + _S_TYPEISTMO :: #force_inline proc "contextless" (m: mode_t) -> bool { + return false + } + + UTIME_NOW :: (1 << 30) - 1 + UTIME_OMIT :: (1 << 30) - 2 + +} else when ODIN_OS == .OpenBSD { + + dev_t :: distinct c.int32_t + nlink_t :: distinct c.uint32_t + _mode_t :: distinct c.uint32_t + blkcnt_t :: distinct c.int64_t + blksize_t :: distinct c.int32_t + ino_t :: distinct c.uint64_t + + stat_t :: struct { + st_mode: mode_t, /* [XSI] mode of file */ + st_dev: dev_t, /* [XSI] ID of device containing file */ + st_ino: ino_t, /* [XSI] file serial number */ + st_nlink: nlink_t, /* [XSI] number of hard links */ + st_uid: uid_t, /* [XSI] user ID of the file */ + st_gid: gid_t, /* [XSI] group ID of the file */ + st_rdev: dev_t, /* [XSI] device ID */ + st_atim: timespec, /* [XSI] time of last access */ + st_mtim: timespec, /* [XSI] time of last data modification */ + st_ctim: timespec, /* [XSI] time of last status change */ + st_size: off_t, /* [XSI] file size, in bytes */ + st_blocks: blkcnt_t, /* [XSI] blocks allocated for file */ + st_blksize: blksize_t, /* [XSI] optimal blocksize for I/O */ + st_flags: c.uint32_t, /* user defined flags for file */ + st_gen: c.int32_t, + st_birthtimespec: timespec, + } + + S_IFBLK :: 0o060000 + S_IFCHR :: 0o020000 + S_IFIFO :: 0o010000 + S_IFREG :: 0o100000 + S_IFDIR :: 0o040000 + S_IFLNK :: 0o120000 + S_IFSOCK :: 0o140000 + + __S_IFMT :: 0o170000 + + _S_TYPEISMQ :: #force_inline proc "contextless" (m: mode_t) -> bool { + return false + } + + _S_TYPEISSEM :: #force_inline proc "contextless" (m: mode_t) -> bool { + return false + } + + _S_TYPEISSHM :: #force_inline proc "contextless" (m: mode_t) -> bool { + return false + } + + _S_TYPEISTMO :: #force_inline proc "contextless" (m: mode_t) -> bool { + return false + } + + UTIME_NOW :: -2 + UTIME_OMIT :: -1 + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/sys_statvfs.odin b/core/sys/posix/sys_statvfs.odin new file mode 100644 index 000000000..eb6c16806 --- /dev/null +++ b/core/sys/posix/sys_statvfs.odin @@ -0,0 +1,135 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// sys/statvfs.h - VFS File System information structure + +foreign lib { + + /* + Obtains information about the file system containing the fildes. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/statvfs.html ]] + */ + @(link_name=LFSTATVFS) + fstatvfs :: proc(fildes: FD, buf: ^statvfs_t) -> result --- + + /* + Obtains information about the file system containing the file named by path. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/statvfs.html ]] + */ + @(link_name=LSTATVFS) + statvfs :: proc(path: cstring, buf: ^statvfs_t) -> result --- +} + +VFS_Flag_Bits :: enum c.ulong { + // Read-only file system. + RDONLY = log2(ST_RDONLY), + // Does not support the semantics of the ST_ISUID and ST_ISGID file mode bits. + NOSUID = log2(ST_NOSUID), +} +VFS_Flags :: bit_set[VFS_Flag_Bits; c.ulong] + +when ODIN_OS == .NetBSD { + @(private) LFSTATVFS :: "__fstatvfs90" + @(private) LSTATVFS :: "__statvfs90" +} else { + @(private) LFSTATVFS :: "fstatvfs" + @(private) LSTATVFS :: "statvfs" +} + +when ODIN_OS == .Darwin || ODIN_OS == .OpenBSD { + + fsblkcnt_t :: distinct c.uint + + statvfs_t :: struct { + f_bsize: c.ulong, /* [PSX] file system block size */ + f_frsize: c.ulong, /* [PSX] fundamental file system block size */ + f_blocks: fsblkcnt_t, /* [PSX] total number of blocks on file system in units of f_frsize */ + f_bfree: fsblkcnt_t, /* [PSX] total number of free blocks */ + f_bavail: fsblkcnt_t, /* [PSX] number of free blocks available to non-privileged process */ + f_files: fsblkcnt_t, /* [PSX] total number of file serial numbers */ + f_ffree: fsblkcnt_t, /* [PSX] total number of free file serial numbers */ + f_favail: fsblkcnt_t, /* [PSX] number of file serial numbers available to non-privileged process */ + f_fsid: c.ulong, /* [PSX] file system ID */ + f_flag: VFS_Flags, /* [PSX] bit mask of f_flag values */ + f_namemax: c.ulong, /* [PSX] maximum filename length */ + } + + ST_RDONLY :: 0x00000001 + ST_NOSUID :: 0x00000002 + +} else when ODIN_OS == .FreeBSD { + + fsblkcnt_t :: distinct c.uint64_t + + statvfs_t :: struct { + f_bavail: fsblkcnt_t, /* [PSX] number of free blocks available to non-privileged process */ + f_bfree: fsblkcnt_t, /* [PSX] total number of free blocks */ + f_blocks: fsblkcnt_t, /* [PSX] total number of blocks on file system in units of f_frsize */ + f_favail: fsblkcnt_t, /* [PSX] number of file serial numbers available to non-privileged process */ + f_ffree: fsblkcnt_t, /* [PSX] total number of free file serial numbers */ + f_files: fsblkcnt_t, /* [PSX] total number of file serial numbers */ + f_bsize: c.ulong, /* [PSX] file system block size */ + f_flag: VFS_Flags, /* [PSX] bit mask of f_flag values */ + f_frsize: c.ulong, /* [PSX] fundamental file system block size */ + f_fsid: c.ulong, /* [PSX] file system ID */ + f_namemax: c.ulong, /* [PSX] maximum filename length */ + } + + ST_RDONLY :: 0x00000001 + ST_NOSUID :: 0x00000002 + +} else when ODIN_OS == .NetBSD { + + fsblkcnt_t :: distinct c.uint64_t + + @(private) + _VFS_NAMELEN :: 1024 + + @(private) + fsid_t :: struct { + __fsid_val: [2]c.int, + } + + statvfs_t :: struct { + f_flag: VFS_Flags, /* [PSX] bit mask of f_flag values */ + f_bsize: c.ulong, /* [PSX] file system block size */ + f_frsize: c.ulong, /* [PSX] fundamental file system block size */ + f_iosize: c.ulong, + f_blocks: fsblkcnt_t, /* [PSX] total number of blocks on file system in units of f_frsize */ + f_bfree: fsblkcnt_t, /* [PSX] total number of free blocks */ + f_bavail: fsblkcnt_t, /* [PSX] number of free blocks available to non-privileged process */ + f_bresvd: fsblkcnt_t, + f_files: fsblkcnt_t, /* [PSX] total number of file serial numbers */ + f_ffree: fsblkcnt_t, /* [PSX] total number of free file serial numbers */ + f_favail: fsblkcnt_t, /* [PSX] number of file serial numbers available to non-privileged process */ + f_fresvd: fsblkcnt_t, + f_syncreads: c.uint64_t, + f_syncwrites: c.uint64_t, + f_asyncreads: c.uint64_t, + f_asyncwrites: c.uint64_t, + f_fsidx: fsid_t, + f_fsid: c.ulong, /* [PSX] file system ID */ + f_namemax: c.ulong, /* [PSX] maximum filename length */ + f_owner: uid_t, + f_spare: [4]c.uint64_t, + f_fstypename: [_VFS_NAMELEN]c.char `fmt:"s,0"`, + f_mntonname: [_VFS_NAMELEN]c.char `fmt:"s,0"`, + f_mntfromname: [_VFS_NAMELEN]c.char `fmt:"s,0"`, + f_mntfromlabel: [_VFS_NAMELEN]c.char `fmt:"s,0"`, + } + + ST_RDONLY :: 0x00000001 + ST_NOSUID :: 0x00000008 + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/sys_time.odin b/core/sys/posix/sys_time.odin new file mode 100644 index 000000000..093fdd688 --- /dev/null +++ b/core/sys/posix/sys_time.odin @@ -0,0 +1,82 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// sys/time.h - time types + +foreign lib { + /* + Store the current value of timer into value. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getitimer.html ]] + */ + @(link_name=LGETITIMER) + getitimer :: proc(which: ITimer, value: ^itimerval) -> result --- + + /* + Set the timer to the value given, and store the previous value in ovalue if it is not nil. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getitimer.html ]] + */ + @(link_name=LSETITIMER) + setitimer :: proc(which: ITimer, value: ^itimerval, ovalue: ^itimerval) -> result --- + + /* + Obtains the current time. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/gettimeofday.html ]] + */ + @(link_name=LGETTIMEOFDAY) + gettimeofday :: proc(tp: ^timeval, tzp: rawptr = nil) -> result --- + + /* + Sets the access and modification times of the file at the given path. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimes.html ]] + */ + @(link_name=LUTIMES) + utimes :: proc(path: cstring, times: ^[2]timeval) -> result --- +} + +ITimer :: enum c.int { + // Decrements in real time. + REAL = ITIMER_REAL, + // Decrements in process virtual time, only when the process is executing. + VIRTUAL = ITIMER_VIRTUAL, + // Decrements both in process virtual time and when the system is running on + // behalf of the process. + PROF = ITIMER_PROF, +} + +when ODIN_OS == .NetBSD { + @(private) LGETITIMER :: "__getitimer50" + @(private) LSETITIMER :: "__setitimer50" + @(private) LGETTIMEOFDAY :: "__gettimeofday50" + @(private) LUTIMES :: "__utimes50" +} else { + @(private) LGETITIMER :: "getitimer" + @(private) LSETITIMER :: "setitimer" + @(private) LGETTIMEOFDAY :: "gettimeofday" + @(private) LUTIMES :: "utimes" +} + +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + + itimerval :: struct { + it_interval: timeval, /* [PSX] timer interval */ + it_value: timeval, /* [PSX] current value */ + } + + ITIMER_REAL :: 0 + ITIMER_VIRTUAL :: 1 + ITIMER_PROF :: 2 + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/sys_times.odin b/core/sys/posix/sys_times.odin new file mode 100644 index 000000000..685ced515 --- /dev/null +++ b/core/sys/posix/sys_times.odin @@ -0,0 +1,38 @@ +package posix + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// sys/times.h - file access and modification times structure + +foreign lib { + /* + Get time accounting information. + + Returns: -1 (setting errno) on failure, the elapsed real time, since an arbitrary point in the past + */ + @(link_name=LTIMES) + times :: proc(buffer: ^tms) -> clock_t --- +} + +when ODIN_OS == .NetBSD { + @(private) LTIMES :: "__times13" +} else { + @(private) LTIMES :: "times" +} + +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + + tms :: struct { + tms_utime: clock_t, /* [PSX] user CPU time */ + tms_stime: clock_t, /* [PSX] system CPU time */ + tms_cutime: clock_t, /* [PSX] terminated children user CPU time */ + tms_cstime: clock_t, /* [PSX] terminated children system CPU time */ + } + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/sys_uio.odin b/core/sys/posix/sys_uio.odin new file mode 100644 index 000000000..01664e576 --- /dev/null +++ b/core/sys/posix/sys_uio.odin @@ -0,0 +1,42 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import libc "system:System.framework" +} else { + foreign import libc "system:c" +} + +// sys/uio.h - definitions for vector I/O operations + +foreign libc { + /* + Equivalent to read() but takes a vector of inputs. + + iovcnt can be 0..=IOV_MAX in length. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/readv.html ]] + */ + readv :: proc(fildes: FD, iov: [^]iovec, iovcnt: c.int) -> c.ssize_t --- + + /* + Equivalent to write() but takes a vector of inputs. + + iovcnt can be 0..=IOV_MAX in length. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/readv.html ]] + */ + writev :: proc(fildes: FD, iov: [^]iovec, iovcnt: c.int) -> c.ssize_t --- +} + +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + + iovec :: struct { + iov_base: rawptr, /* [PSX] base address of I/O memory region */ + iov_len: c.size_t, /* [PSX] size of the region iov_base points to */ + } + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/sys_un.odin b/core/sys/posix/sys_un.odin new file mode 100644 index 000000000..146882051 --- /dev/null +++ b/core/sys/posix/sys_un.odin @@ -0,0 +1,17 @@ +package posix + +import "core:c" + +// sys/un.h = definitions for UNIX domain sockets + +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + + sockaddr_un :: struct { + sun_len: c.uchar, /* sockaddr len including nil */ + sun_family: sa_family_t, /* [PSX] address family */ + sun_path: [104]c.char, /* [PSX] socket pathname */ + } + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/sys_utsname.odin b/core/sys/posix/sys_utsname.odin new file mode 100644 index 000000000..803f40ffd --- /dev/null +++ b/core/sys/posix/sys_utsname.odin @@ -0,0 +1,55 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// sys/utsname.h = system name structure + +foreign lib { + /* + Stores information identifying the current system in the given structure. + + Returns: non-negative on success, -1 (setting errno) on failure + + NOTE: have a look at `core:sys/info` for similar/better system information. + + Example: + uname: posix.utsname + posix.uname(&uname) + fmt.printfln("%#v", uname) + + Possible Output: + utsname{ + sysname = Darwin, + nodename = Laytans-MacBook-Pro.local, + release = 23.5.0, + version = Darwin Kernel Version 23.5.0: Wed May 1 20:16:51 PDT 2024; root:xnu-11331.111.3~1/RELEASE_ARM64_T8103, + machine = arm64, + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/uname.html ]] + */ + uname :: proc(uname: ^utsname) -> c.int --- +} + +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + + @(private) + _SYS_NAMELEN :: 256 + + utsname :: struct { + sysname: [_SYS_NAMELEN]c.char `fmt:"s,0"`, /* [PSX] name of OS */ + nodename: [_SYS_NAMELEN]c.char `fmt:"s,0"`, /* [PSX] name of this network node */ + release: [_SYS_NAMELEN]c.char `fmt:"s,0"`, /* [PSX] release level */ + version: [_SYS_NAMELEN]c.char `fmt:"s,0"`, /* [PSX] version level */ + machine: [_SYS_NAMELEN]c.char `fmt:"s,0"`, /* [PSX] hardware type */ + } + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/sys_wait.odin b/core/sys/posix/sys_wait.odin new file mode 100644 index 000000000..6d0336e80 --- /dev/null +++ b/core/sys/posix/sys_wait.odin @@ -0,0 +1,380 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// sys/wait.h - declarations for waiting + +foreign lib { + /* + Obtains status information pertaining to one of the caller's child processes. + + Returns: -1 (setting errno) on failure or signal on calling process, the pid of the process that caused the return otherwise + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html ]] + */ + wait :: proc(stat_loc: ^c.int) -> pid_t --- + + /* + Obtains status information pertaining to the given pid specifier. + + If pid is -1, status is requested for any child process. + If pid is greater than 0, it specifies the process ID of a single child process. + If pid is 0, it specifies any child process whose process group ID is equal to that of the call. + If pid is < -1, status is requested for any child whose process group ID is the absolute value of pid. + + Returns: -1 (setting errno) on failure or signal on calling process, 0 if NOHANG and status is not available, the pid of the process that caused the return otherwise + + Example: + // The following example demonstrates the use of waitpid(), fork(), and the macros used to + // interpret the status value returned by waitpid() (and wait()). The code segment creates a + // child process which does some unspecified work. Meanwhile the parent loops performing calls + // to waitpid() to monitor the status of the child. The loop terminates when child termination + // is detected. + + child_pid := posix.fork(); switch child_pid { + case -1: // `fork` failed. + panic("fork failed") + + case 0: // This is the child. + + // Do some work... + + case: + for { + status: i32 + wpid := posix.waitpid(child_pid, &status, { .UNTRACED, .CONTINUED }) + if wpid == -1 { + panic("waitpid failure") + } + + switch { + case posix.WIFEXITED(status): + fmt.printfln("child exited, status=%v", posix.WEXITSTATUS(status)) + case posix.WIFSIGNALED(status): + fmt.printfln("child killed (signal %v)", posix.WTERMSIG(status)) + case posix.WIFSTOPPED(status): + fmt.printfln("child stopped (signal %v", posix.WSTOPSIG(status)) + case posix.WIFCONTINUED(status): + fmt.println("child continued") + case: + // Should never happen. + fmt.println("unexpected status (%x)", status) + } + + if posix.WIFEXITED(status) || posix.WIFSIGNALED(status) { + break + } + } + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html ]] + */ + waitpid :: proc(pid: pid_t, stat_loc: ^c.int, options: Wait_Flags) -> pid_t --- + + /* + Obtains status information pertaining to the given idtype_t and id specifier. + + Returns: 0 if WNOHANG and no status available, 0 if child changed state, -1 (setting errno) on failure + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/waitid.html ]] + */ + waitid :: proc(idtype: idtype_t, id: id_t, infop: ^siginfo_t, options: Wait_Flags) -> c.int --- +} + +// If terminated normally. +WIFEXITED :: #force_inline proc "contextless" (x: c.int) -> bool { + return _WIFEXITED(x) +} + +// If WIFEXITED is true, returns the exit status. +WEXITSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int { + return _WEXITSTATUS(x) +} + +// If terminated due to an uncaught signal. +WIFSIGNALED :: #force_inline proc "contextless" (x: c.int) -> bool { + return _WIFSIGNALED(x) +} + +// If WIFSIGNALED is true, returns the signal. +WTERMSIG :: #force_inline proc "contextless" (x: c.int) -> Signal { + return _WTERMSIG(x) +} + +// If status was returned for a child process that is currently stopped. +WIFSTOPPED :: #force_inline proc "contextless" (x: c.int) -> bool { + return _WIFSTOPPED(x) +} + +// If WIFSTOPPED, the signal that caused the child process to stop. +WSTOPSIG :: #force_inline proc "contextless" (x: c.int) -> Signal { + return _WSTOPSIG(x) +} + +// If status was returned for a child process that has continued from a job control stop. +WIFCONTINUED :: #force_inline proc "contextless" (x: c.int) -> bool { + return _WIFCONTINUED(x) +} + +idtype_t :: enum c.int { + // Wait for any children and `id` is ignored. + P_ALL, + // Wait for any child wiith a process group ID equal to `id`. + P_PID, + // Wait for any child with a process group ID equal to `id`. + P_PGID, +} + +Wait_Flag_Bits :: enum c.int { + // Report the status of any continued child process specified by pid whose status has not been + // reported since it continued from a job control stop. + CONTINUED = log2(WCONTINUED), + // Don't suspend execution of the calling thread if status is not immediately available for one + // of the child processes specified by pid. + NOHANG = log2(WNOHANG), + // The status of any child process specified by pid that are stopped, and whose status has not + // yet been reported since they stopped, shall also be reported to the requesting process. + UNTRACED = log2(WUNTRACED), + + // Following are only available on `waitid`, not `waitpid`. + + // Wait for processes that have exited. + EXITED = log2(WEXITED), + // Keep the process whose status is returned in a waitable state, so it may be waited on again. + NOWAIT = log2(WNOWAIT), + // Children that have stopped upon receipt of a signal, and whose status either hasn't been reported + // or has been reported but that report was called with NOWAIT. + STOPPED = log2(WSTOPPED), +} +Wait_Flags :: bit_set[Wait_Flag_Bits; c.int] + +when ODIN_OS == .Darwin { + + id_t :: distinct c.uint + + WCONTINUED :: 0x00000010 + WNOHANG :: 0x00000001 + WUNTRACED :: 0x00000002 + + WEXITED :: 0x00000004 + WNOWAIT :: 0x00000020 + WSTOPPED :: 0x00000008 + + @(private) + _WSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int { + return x & 0o177 + } + + @(private) + _WSTOPPED :: 0o177 + + @(private) + _WIFEXITED :: #force_inline proc "contextless" (x: c.int) -> bool { + return _WSTATUS(x) == 0 + } + + @(private) + _WEXITSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int { + return x >> 8 + } + + @(private) + _WIFSIGNALED :: #force_inline proc "contextless" (x: c.int) -> bool { + return _WSTATUS(x) != _WSTOPPED && _WSTATUS(x) != 0 + } + + @(private) + _WTERMSIG :: #force_inline proc "contextless" (x: c.int) -> Signal { + return Signal(_WSTATUS(x)) + } + + @(private) + _WIFSTOPPED :: #force_inline proc "contextless" (x: c.int) -> bool { + return _WSTATUS(x) == _WSTOPPED && WSTOPSIG(x) != .SIGCONT + } + + @(private) + _WSTOPSIG :: #force_inline proc "contextless" (x: c.int) -> Signal { + return Signal(x >> 8) + } + + @(private) + _WIFCONTINUED :: #force_inline proc "contextless" (x: c.int) -> bool { + return _WSTATUS(x) == _WSTOPPED && WSTOPSIG(x) == .SIGCONT + } + +} else when ODIN_OS == .FreeBSD { + + id_t :: distinct c.int64_t + + WCONTINUED :: 4 + WNOHANG :: 1 + WUNTRACED :: 2 + + WEXITED :: 16 + WNOWAIT :: 8 + WSTOPPED :: 2 + + @(private) + _WSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int { + return x & 0o177 + } + + @(private) + _WSTOPPED :: 0o177 + + @(private) + _WIFEXITED :: #force_inline proc "contextless" (x: c.int) -> bool { + return _WSTATUS(x) == 0 + } + + @(private) + _WEXITSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int { + return x >> 8 + } + + @(private) + _WIFSIGNALED :: #force_inline proc "contextless" (x: c.int) -> bool { + return _WSTATUS(x) != _WSTOPPED && _WSTATUS(x) != 0 && x != c.int(Signal.SIGCONT) + } + + @(private) + _WTERMSIG :: #force_inline proc "contextless" (x: c.int) -> Signal { + return Signal(_WSTATUS(x)) + } + + @(private) + _WIFSTOPPED :: #force_inline proc "contextless" (x: c.int) -> bool { + return _WSTATUS(x) == _WSTOPPED + } + + @(private) + _WSTOPSIG :: #force_inline proc "contextless" (x: c.int) -> Signal { + return Signal(x >> 8) + } + + @(private) + _WIFCONTINUED :: #force_inline proc "contextless" (x: c.int) -> bool { + return x == c.int(Signal.SIGCONT) + } +} else when ODIN_OS == .NetBSD { + + id_t :: distinct c.uint32_t + + WCONTINUED :: 0x00000010 + WNOHANG :: 0x00000001 + WUNTRACED :: 0x00000002 + + WEXITED :: 0x00000020 + WNOWAIT :: 0x00010000 + WSTOPPED :: 0x00000002 + + @(private) + _WSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int { + return x & 0o177 + } + + @(private) + _WSTOPPED :: 0o177 + + @(private) + _WIFEXITED :: #force_inline proc "contextless" (x: c.int) -> bool { + return _WSTATUS(x) == 0 + } + + @(private) + _WEXITSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int { + return (x >> 8) & 0x000000ff + } + + @(private) + _WIFSIGNALED :: #force_inline proc "contextless" (x: c.int) -> bool { + return !WIFSTOPPED(x) && !WIFCONTINUED(x) && !WIFEXITED(x) + } + + @(private) + _WTERMSIG :: #force_inline proc "contextless" (x: c.int) -> Signal { + return Signal(_WSTATUS(x)) + } + + @(private) + _WIFSTOPPED :: #force_inline proc "contextless" (x: c.int) -> bool { + return _WSTATUS(x) == _WSTOPPED && !WIFCONTINUED(x) + } + + @(private) + _WSTOPSIG :: #force_inline proc "contextless" (x: c.int) -> Signal { + return Signal((x >> 8) & 0xff) + } + + @(private) + _WIFCONTINUED :: #force_inline proc "contextless" (x: c.int) -> bool { + return x == 0xffff + } + +} else when ODIN_OS == .OpenBSD { + + id_t :: distinct c.uint32_t + + WCONTINUED :: 0x00000010 + WNOHANG :: 0x00000001 + WUNTRACED :: 0x00000002 + + WEXITED :: 0x00000020 + WNOWAIT :: 0x00010000 + WSTOPPED :: 0x00000002 + + @(private) + _WSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int { + return x & 0o177 + } + + @(private) + _WSTOPPED :: 0o177 + @(private) + _WCONTINUED :: 0o177777 + + @(private) + _WIFEXITED :: #force_inline proc "contextless" (x: c.int) -> bool { + return _WSTATUS(x) == 0 + } + + @(private) + _WEXITSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int { + return (x >> 8) & 0x000000ff + } + + @(private) + _WIFSIGNALED :: #force_inline proc "contextless" (x: c.int) -> bool { + return _WSTATUS(x) != _WSTOPPED && _WSTATUS(x) != 0 + } + + @(private) + _WTERMSIG :: #force_inline proc "contextless" (x: c.int) -> Signal { + return Signal(_WSTATUS(x)) + } + + @(private) + _WIFSTOPPED :: #force_inline proc "contextless" (x: c.int) -> bool { + return (x & 0xff) == _WSTOPPED + } + + @(private) + _WSTOPSIG :: #force_inline proc "contextless" (x: c.int) -> Signal { + return Signal((x >> 8) & 0xff) + } + + @(private) + _WIFCONTINUED :: #force_inline proc "contextless" (x: c.int) -> bool { + return (x & _WCONTINUED) == _WCONTINUED + } + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/termios.odin b/core/sys/posix/termios.odin new file mode 100644 index 000000000..372e67e06 --- /dev/null +++ b/core/sys/posix/termios.odin @@ -0,0 +1,475 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// termios.h - define values for termios + +foreign lib { + /* + Get the input baud rate. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html ]] + */ + cfgetispeed :: proc(termios_p: ^termios) -> speed_t --- + + /* + Set the input baud rate. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html ]] + */ + cfsetispeed :: proc(termios_p: ^termios, rate: speed_t) -> result --- + + /* + Get the output baud rate. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html ]] + */ + cfgetospeed :: proc(termios_p: ^termios) -> speed_t --- + + /* + Set the output baud rate. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html ]] + */ + cfsetospeed :: proc(termios_p: ^termios, rate: speed_t) -> result --- + + /* + Wait for transmission of output. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcdrain.html ]] + */ + tcdrain :: proc(fildes: FD) -> result --- + + /* + Suspend or restart the transmission or reception of data. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflow.html ]] + */ + tcflow :: proc(fildes: FD, action: TC_Action) -> result --- + + /* + Flush non-transmitted output data, non-read input data, or both. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflush.html ]] + */ + tcflush :: proc(fildes: FD, queue_selector: TC_Queue) -> result --- + + /* + Get the parameters associated with the terminal. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetattr.html ]] + */ + tcgetattr :: proc(fildes: FD, termios_p: ^termios) -> result --- + + /* + Get the process group ID for the session leader for the controlling terminal. + + Returns: -1 (setting errno) on failure, the pid otherwise + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetsid.html ]] + */ + tcgetsid :: proc(fildes: FD) -> pid_t --- + + /* + Send a break for a specific duration. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsendbreak.html ]] + */ + tcsendbreak :: proc(fildes: FD, duration: c.int) -> result --- + + /* + Set the parameters associated with the terminal. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetattr.html ]] + */ + tcsetattr :: proc(fildes: FD, optional_actions: c.int, termios_p: ^termios) -> result --- +} + +Control_Char :: enum c.int { + VEOF = VEOF, + VEOL = VEOL, + VERASE = VERASE, + VINTR = VINTR, + VKILL = VKILL, + VMIN = VMIN, + VQUIT = VQUIT, + VSTART = VSTART, + VSTOP = VSTOP, + VSUSP = VSUSP, + VTIME = VTIME, + + NCCS = NCCS-1, +} +#assert(len(#sparse [Control_Char]cc_t) == NCCS) + +CInput_Flag_Bits :: enum tcflag_t { + IGNBRK = log2(IGNBRK), /* ignore BREAK condition */ + BRKINT = log2(BRKINT), /* map BREAK to SIGINTR */ + IGNPAR = log2(IGNPAR), /* ignore (discard) parity errors */ + PARMRK = log2(PARMRK), /* mark parity and framing errors */ + INPCK = log2(INPCK), /* enable checking of parity errors */ + ISTRIP = log2(ISTRIP), /* strip 8th bit off chars */ + INLCR = log2(INLCR), /* map NL into CR */ + IGNCR = log2(IGNCR), /* ignore CR */ + ICRNL = log2(ICRNL), /* map CR to NL (ala CRMOD) */ + IXON = log2(IXON), /* enable output flow control */ + IXOFF = log2(IXOFF), /* enable input flow control */ + IXANY = log2(IXANY), /* any char will restart after stop */ +} +CInput_Flags :: bit_set[CInput_Flag_Bits; tcflag_t] + +CLocal_Flag_Bits :: enum tcflag_t { + ECHO = log2(ECHO), /* visual erase for line kill */ + ECHOE = log2(ECHOE), /* visually erase chars */ + ECHOK = log2(ECHOK), /* echo NL after line kill */ + ECHONL = log2(ECHONL), /* echo NL even if ECHO is off */ + ICANON = log2(ICANON), /* canonicalize input lines */ + IEXTEN = log2(IEXTEN), /* enable DISCARD and LNEXT */ + ISIG = log2(ISIG), /* enable signals INTR, QUIT, [D]SUSP */ + NOFLSH = log2(NOFLSH), /* don't flush after interrupt */ + TOSTOP = log2(TOSTOP), /* stop background jobs from output */ +} +CLocal_Flags :: bit_set[CLocal_Flag_Bits; tcflag_t] + +CControl_Flag_Bits :: enum tcflag_t { + // CS5 = log2(CS5), /* 5 bits (pseudo) (default) */ + CS6 = log2(CS6), /* 6 bits */ + CS7 = log2(CS7), /* 7 bits */ + CS8 = log2(CS8), /* 8 bits */ + CSTOPB = log2(CSTOPB), /* send 2 stop bits */ + CREAD = log2(CREAD), /* enable receiver */ + PARENB = log2(PARENB), /* parity enable */ + PARODD = log2(PARODD), /* odd parity, else even */ + HUPCL = log2(HUPCL), /* hang up on last close */ + CLOCAL = log2(CLOCAL), /* ignore modem status lines */ +} +CControl_Flags :: bit_set[CControl_Flag_Bits; tcflag_t] + +// character size mask +CSIZE :: CControl_Flags{ .CS6, .CS7, .CS8 } + +COutput_Flag_Bits :: enum tcflag_t { + OPOST = log2(OPOST), /* enable following output processing */ + ONLCR = log2(ONLCR), /* map NL to CR-NL (ala CRMOD) */ + OCRNL = log2(OCRNL), /* map CR to NL on output */ + ONOCR = log2(ONOCR), /* no CR output at column 0 */ + ONLRET = log2(ONLRET), /* NL performs CR function */ + OFDEL = log2(OFDEL), /* fill is DEL, else NUL */ + OFILL = log2(OFILL), /* use fill characters for delay */ + // NL0 = log2(NL0), /* \n delay 0 (default) */ + NL1 = log2(NL1), /* \n delay 1 */ + // CR0 = log2(CR0), /* \r delay 0 (default) */ + CR1 = log2(CR1), /* \r delay 1 */ + CR2 = log2(CR2), /* \r delay 2 */ + CR3 = log2(CR3), /* \r delay 3 */ + // TAB0 = log2(TAB0),/* horizontal tab delay 0 (default) */ + TAB1 = log2(TAB1), /* horizontal tab delay 1 */ + TAB3 = log2(TAB3), /* horizontal tab delay 3 */ + // BS0 = log2(BS0), /* \b delay 0 (default) */ + BS1 = log2(BS1), /* \b delay 1 */ + // VT0 = log2(VT0), /* vertical tab delay 0 (default) */ + VT1 = log2(VT1), /* vertical tab delay 1 */ + // FF0 = log2(FF0), /* form feed delay 0 (default) */ + FF1 = log2(FF1), /* form feed delay 1 */ +} +COutput_Flags :: bit_set[COutput_Flag_Bits; tcflag_t] + +// \n delay mask +NLDLY :: COutput_Flags{ .NL1, COutput_Flag_Bits(9) } +// \r delay mask +CRDLY :: COutput_Flags{ .CR1, .CR2, .CR3 } +// horizontal tab delay mask +TABDLY :: COutput_Flags{ .TAB1, .TAB3, COutput_Flag_Bits(2) } +// \b delay mask +BSDLY :: COutput_Flags{ .BS1 } +// vertical tab delay mask +VTDLY :: COutput_Flags{ .VT1 } +// form feed delay mask +FFDLY :: COutput_Flags{ .FF1 } + +speed_t :: enum _speed_t { + B0 = B0, + B50 = B50, + B75 = B75, + B110 = B110, + B134 = B134, + B150 = B150, + B200 = B200, + B300 = B300, + B600 = B600, + B1200 = B1200, + B1800 = B1800, + B2400 = B2400, + B4800 = B4800, + B9600 = B9600, + B19200 = B19200, + B38400 = B38400, +} + +TC_Action :: enum c.int { + TCIOFF = TCIOFF, + TCION = TCION, + TCOOFF = TCOOFF, + TCOON = TCOON, +} + +TC_Queue :: enum c.int { + TCIFLUSH = TCIFLUSH, + TCOFLUSH = TCOFLUSH, + TCIOFLUSH = TCIOFLUSH, +} + +when ODIN_OS == .Darwin { + + cc_t :: distinct c.uchar + _speed_t :: distinct c.ulong + tcflag_t :: distinct c.ulong + + termios :: struct { + c_iflag: CInput_Flags, /* [XBD] input flags */ + c_oflag: COutput_Flags, /* [XBD] output flags */ + c_cflag: CControl_Flags, /* [XBD] control flags */ + c_lflag: CLocal_Flags, /* [XBD] local flag */ + c_cc: #sparse [Control_Char]cc_t, /* [XBD] control chars */ + c_ispeed: speed_t, /* input speed */ + c_ospeed: speed_t, /* output speed */ + } + + NCCS :: 20 + + VEOF :: 0 + VEOL :: 1 + VERASE :: 3 + VINTR :: 8 + VKILL :: 5 + VMIN :: 16 + VQUIT :: 9 + VSTART :: 12 + VSTOP :: 13 + VSUSP :: 10 + VTIME :: 17 + + IGNBRK :: 0x00000001 + BRKINT :: 0x00000002 + IGNPAR :: 0x00000004 + PARMRK :: 0x00000008 + INPCK :: 0x00000010 + ISTRIP :: 0x00000020 + INLCR :: 0x00000040 + IGNCR :: 0x00000080 + ICRNL :: 0x00000100 + IXON :: 0x00000200 + IXOFF :: 0x00000400 + IXANY :: 0x00000800 + + OPOST :: 0x00000001 + ONLCR :: 0x00000002 + OCRNL :: 0x00000010 + ONOCR :: 0x00000020 + ONLRET :: 0x00000040 + OFDEL :: 0x00020000 + OFILL :: 0x00000080 + _NLDLY :: 0x00000300 + NL0 :: 0x00000000 + NL1 :: 0x00000100 + _CRDLY :: 0x00003000 + CR0 :: 0x00000000 + CR1 :: 0x00001000 + CR2 :: 0x00002000 + CR3 :: 0x00003000 + _TABDLY :: 0x00000c04 + TAB0 :: 0x00000000 + TAB1 :: 0x00000400 + TAB3 :: 0x00000800 + _BSDLY :: 0x00008000 + BS0 :: 0x00000000 + BS1 :: 0x00008000 + _VTDLY :: 0x00010000 + VT0 :: 0x00000000 + VT1 :: 0x00010000 + _FFDLY :: 0x00004000 + FF0 :: 0x00000000 + FF1 :: 0x00004000 + + B0 :: 0 + B50 :: 50 + B75 :: 75 + B110 :: 110 + B134 :: 134 + B150 :: 150 + B200 :: 200 + B300 :: 300 + B600 :: 600 + B1200 :: 1200 + B1800 :: 1800 + B2400 :: 2400 + B4800 :: 4800 + B9600 :: 9600 + B19200 :: 19200 + B38400 :: 38400 + + _CSIZE :: 0x00000300 + CS5 :: 0x00000000 + CS6 :: 0x00000100 + CS7 :: 0x00000200 + CS8 :: 0x00000300 + CSTOPB :: 0x00000400 + CREAD :: 0x00000800 + PARENB :: 0x00001000 + PARODD :: 0x00002000 + HUPCL :: 0x00004000 + CLOCAL :: 0x00008000 + + ECHO :: 0x00000008 + ECHOE :: 0x00000002 + ECHOK :: 0x00000004 + ECHONL :: 0x00000010 + ICANON :: 0x00000100 + IEXTEN :: 0x00000400 + ISIG :: 0x00000080 + NOFLSH :: 0x80000000 + TOSTOP :: 0x00400000 + + TCIFLUSH :: 1 + TCOFLUSH :: 2 + TCIOFLUSH :: 3 + + TCIOFF :: 3 + TCION :: 4 + TCOOFF :: 1 + TCOON :: 2 + +} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + + cc_t :: distinct c.uchar + _speed_t :: distinct c.uint + tcflag_t :: distinct c.uint + + termios :: struct { + c_iflag: CInput_Flags, /* [XBD] input flags */ + c_oflag: COutput_Flags, /* [XBD] output flags */ + c_cflag: CControl_Flags, /* [XBD] control flags */ + c_lflag: CLocal_Flags, /* [XBD] local flag */ + c_cc: #sparse [Control_Char]cc_t, /* [XBD] control chars */ + c_ispeed: speed_t, /* input speed */ + c_ospeed: speed_t, /* output speed */ + } + + NCCS :: 20 + + VEOF :: 0 + VEOL :: 1 + VERASE :: 3 + VINTR :: 8 + VKILL :: 5 + VMIN :: 16 + VQUIT :: 9 + VSTART :: 12 + VSTOP :: 13 + VSUSP :: 10 + VTIME :: 17 + + IGNBRK :: 0x00000001 + BRKINT :: 0x00000002 + IGNPAR :: 0x00000004 + PARMRK :: 0x00000008 + INPCK :: 0x00000010 + ISTRIP :: 0x00000020 + INLCR :: 0x00000040 + IGNCR :: 0x00000080 + ICRNL :: 0x00000100 + IXON :: 0x00000200 + IXOFF :: 0x00000400 + IXANY :: 0x00000800 + + OPOST :: 0x00000001 + ONLCR :: 0x00000002 + OCRNL :: 0x00000010 + when ODIN_OS == .OpenBSD { + ONOCR :: 0x00000040 + ONLRET :: 0x00000080 + } else { + ONOCR :: 0x00000020 + ONLRET :: 0x00000040 + } + OFDEL :: 0x00020000 // NOTE: not in headers + OFILL :: 0x00000080 // NOTE: not in headers + _NLDLY :: 0x00000300 // NOTE: not in headers + NL0 :: 0x00000000 // NOTE: not in headers + NL1 :: 0x00000100 // NOTE: not in headers + _CRDLY :: 0x00003000 // NOTE: not in headers + CR0 :: 0x00000000 // NOTE: not in headers + CR1 :: 0x00001000 // NOTE: not in headers + CR2 :: 0x00002000 // NOTE: not in headers + CR3 :: 0x00003000 // NOTE: not in headers + _TABDLY :: 0x00000004 // NOTE: not in headers (netbsd) + TAB0 :: 0x00000000 // NOTE: not in headers (netbsd) + TAB1 :: 0x00000004 // NOTE: not in headers + TAB3 :: 0x00000004 // NOTE: not in headers (netbsd) + _BSDLY :: 0x00008000 // NOTE: not in headers + BS0 :: 0x00000000 // NOTE: not in headers + BS1 :: 0x00008000 // NOTE: not in headers + _VTDLY :: 0x00010000 // NOTE: not in headers + VT0 :: 0x00000000 // NOTE: not in headers + VT1 :: 0x00010000 // NOTE: not in headers + _FFDLY :: 0x00004000 // NOTE: not in headers + FF0 :: 0x00000000 // NOTE: not in headers + FF1 :: 0x00004000 // NOTE: not in headers + + B0 :: 0 + B50 :: 50 + B75 :: 75 + B110 :: 110 + B134 :: 134 + B150 :: 150 + B200 :: 200 + B300 :: 300 + B600 :: 600 + B1200 :: 1200 + B1800 :: 1800 + B2400 :: 2400 + B4800 :: 4800 + B9600 :: 9600 + B19200 :: 19200 + B38400 :: 38400 + + _CSIZE :: 0x00000300 + CS5 :: 0x00000000 + CS6 :: 0x00000100 + CS7 :: 0x00000200 + CS8 :: 0x00000300 + CSTOPB :: 0x00000400 + CREAD :: 0x00000800 + PARENB :: 0x00001000 + PARODD :: 0x00002000 + HUPCL :: 0x00004000 + CLOCAL :: 0x00008000 + + ECHO :: 0x00000008 + ECHOE :: 0x00000002 + ECHOK :: 0x00000004 + ECHONL :: 0x00000010 + ICANON :: 0x00000100 + IEXTEN :: 0x00000400 + ISIG :: 0x00000080 + NOFLSH :: 0x80000000 + TOSTOP :: 0x00400000 + + TCIFLUSH :: 1 + TCOFLUSH :: 2 + TCIOFLUSH :: 3 + + TCIOFF :: 3 + TCION :: 4 + TCOOFF :: 1 + TCOON :: 2 + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/time.odin b/core/sys/posix/time.odin new file mode 100644 index 000000000..9b91c9558 --- /dev/null +++ b/core/sys/posix/time.odin @@ -0,0 +1,234 @@ +package posix + +import "core:c" +import "core:c/libc" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// time.h - time types + +foreign lib { + /* + Convert the broken down time in the structure to a string form: Sun Sep 16 01:03:52 1973\n\0 + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/asctime_r.html ]] + */ + asctime_r :: proc(tm: ^tm, buf: [^]c.char) -> cstring --- + + /* + Convert a time value to a date and time string in the same format as asctime(). + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/ctime_r.html ]] + */ + @(link_name=LCTIMER) + ctime_r :: proc(clock: ^time_t, buf: [^]c.char) -> cstring --- + + /* + Converts the time in seconds since epoch to a broken-down tm struct. + + Returns: nil (setting errno) on failure, the result pointer on success. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/gmtime_r.html ]] + */ + @(link_name=LGMTIMER) + gmtime_r :: proc(timer: ^time_t, result: ^tm) -> ^tm --- + + /* + Convert the time in seconds since epoch to a broken-down tm struct in local time. + + Returns: nil (setting errno) on failure, the result pointer on success. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/localtime_r.html ]] + */ + @(link_name=LLOCALTIMER) + localtime_r :: proc(timer: ^time_t, result: ^tm) -> ^tm --- + + /* + Returns the resolution of any clock. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.html ]] + */ + @(link_name=LCLOCKGETRES) + clock_getres :: proc(clock_id: Clock, res: ^timespec) -> result --- + + /* + Returns the current value tp for the specified clock, clock_id. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.html ]] + */ + @(link_name=LCLOCKGETTIME) + clock_gettime :: proc(clock_id: Clock, tp: ^timespec) -> result --- + + /* + Sets the specified clock's time. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.html ]] + */ + @(link_name=LCLOCKSETTIME) + clock_settime :: proc(clock_id: Clock, tp: ^timespec) -> result --- + + /* + Converts a string representation of a date or time into a broken-down time. + + Returns: nil (setting getdate_err) on failure, the broken-down time otherwise + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getdate.html ]] + */ + getdate :: proc(string: cstring) -> ^tm --- + + /* + Causes the current thread to be suspended from execution until either the time interval + specified by rqtp has elapsed or a signal is delivered. + + Returns: -1 on failure (setting errno), if it was due to a signal, rmtp will be filled with the + remaining time, 0 if all time has been slept + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/nanosleep.html ]] + */ + @(link_name=LNANOSLEEP) + nanosleep :: proc(rqtp: ^timespec, rmtp: ^timespec) -> result --- + + /* + Converts the character string to values which are stored in tm, using the specified format. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/strptime.html ]] + */ + strptime :: proc(buf: [^]c.char, format: cstring, tm: ^tm) -> cstring --- + + /* + Uses the value of the environment variable TZ (or default) to set time conversion info. + + `daylight` is set to whether daylight saving time conversion should be done. + `timezone` is set to the difference, in seconds, between UTC and local standard time. + `tzname` is set by `tzname[0] = "std"` and `tzname[1] = "dst"` + + Example: + posix.tzset() + fmt.println(posix.tzname) + fmt.println(posix.daylight) + fmt.println(posix.timezone) + + Possible Output: + ["CET", "CEST"] + true + -3600 + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/tzset.html ]] + */ + tzset :: proc() --- + + // Whether daylight saving conversion should be done. + daylight: b32 + // The time in seconds between UTC and local standard time. + @(link_name=LTIMEZONE) + timezone: c.long + tzname: [2]cstring +} + +time_t :: libc.time_t +clock_t :: libc.clock_t + +tm :: libc.tm +timespec :: libc.timespec + +CLOCKS_PER_SEC :: libc.CLOCKS_PER_SEC + +asctime :: libc.asctime +clock :: libc.clock +ctime :: libc.ctime +difftime :: libc.difftime +gmtime :: libc.gmtime +localtime :: libc.localtime +mktime :: libc.mktime +strftime :: libc.strftime +time :: libc.time + +Clock :: enum clockid_t { + // system-wide monotonic clock, defined as clock measuring real time, + // can be set with clock_settime() and cannot have negative clock jumps. + MONOTONIC = CLOCK_MONOTONIC, + // CPU-time clock associated with the process making a clock() function call. + PROCESS_CPUTIME_ID = CLOCK_PROCESS_CPUTIME_ID, + // system-wide clock measuring real time. + REALTIME = CLOCK_REALTIME, + // CPU-time clock associated with the thread making a clock() function call. + THREAD_CPUTIME_ID = CLOCK_THREAD_CPUTIME_ID, +} + +when ODIN_OS == .NetBSD { + @(private) LCTIMER :: "__ctime_r50" + @(private) LGMTIMER :: "__gmtime_r50" + @(private) LLOCALTIMER :: "__localtime_r50" + @(private) LCLOCKGETRES :: "__clock_getres50" + @(private) LCLOCKGETTIME :: "__clock_gettime50" + @(private) LCLOCKSETTIME :: "__clock_settime50" + @(private) LNANOSLEEP :: "__nanosleep50" + @(private) LTIMEZONE :: "__timezone13" +} else { + @(private) LCTIMER :: "ctime_r" + @(private) LGMTIMER :: "gmtime_r" + @(private) LLOCALTIMER :: "localtime_r" + @(private) LCLOCKGETRES :: "clock_getres" + @(private) LCLOCKGETTIME :: "clock_gettime" + @(private) LCLOCKSETTIME :: "clock_settime" + @(private) LNANOSLEEP :: "nanosleep" + @(private) LTIMEZONE :: "timezone" +} + +when ODIN_OS == .Darwin { + + clockid_t :: distinct c.int + + CLOCK_MONOTONIC :: 6 + CLOCK_PROCESS_CPUTIME_ID :: 12 + CLOCK_REALTIME :: 0 + CLOCK_THREAD_CPUTIME_ID :: 16 + + foreign lib { + getdate_err: Errno + } + +} else when ODIN_OS == .FreeBSD { + + clockid_t :: distinct c.int + + CLOCK_MONOTONIC :: 4 + CLOCK_PROCESS_CPUTIME_ID :: 15 + CLOCK_REALTIME :: 0 + CLOCK_THREAD_CPUTIME_ID :: 14 + + foreign lib { + getdate_err: Errno + } + +} else when ODIN_OS == .NetBSD { + + clockid_t :: distinct c.uint + + CLOCK_MONOTONIC :: 3 + CLOCK_PROCESS_CPUTIME_ID :: 0x40000000 + CLOCK_REALTIME :: 0 + CLOCK_THREAD_CPUTIME_ID :: 0x20000000 + + foreign lib { + getdate_err: Errno + } + +} else when ODIN_OS == .OpenBSD { + + clockid_t :: distinct c.uint + + CLOCK_MONOTONIC :: 3 + CLOCK_PROCESS_CPUTIME_ID :: 2 + CLOCK_REALTIME :: 0 + CLOCK_THREAD_CPUTIME_ID :: 4 + + getdate_err: Errno = .ENOSYS // NOTE: looks like it's not a thing on OpenBSD. + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/ulimit.odin b/core/sys/posix/ulimit.odin new file mode 100644 index 000000000..067b83271 --- /dev/null +++ b/core/sys/posix/ulimit.odin @@ -0,0 +1,43 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// ulimit.h - ulimit commands + +foreign lib { + /* + Control process limits. + + Note that -1 is a valid return value, applications should clear errno, do this call and then + check both -1 and the errno to determine status. + + Returns: -1 (setting errno) on failure. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/ulimit.html ]] + */ + ulimit :: proc(i: c.int, #c_vararg arg: ..c.long) -> c.long --- +} + +Ulimit_Cmd :: enum c.int { + // Returns the file size limit of the process in units of 512-byte blocks inherited by children. + GETFSIZE = UL_GETFSIZE, + // Set the file size limit for output operations, taken as a long, multiplied by 512. + SETFSIZE = UL_SETFSIZE, +} + +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + + UL_GETFSIZE :: 1 + UL_SETFSIZE :: 2 + + // NOTE: I don't think OpenBSD implements this API. + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/unistd.odin b/core/sys/posix/unistd.odin new file mode 100644 index 000000000..8e08860b4 --- /dev/null +++ b/core/sys/posix/unistd.odin @@ -0,0 +1,1914 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// unistd.h - standard symbolic constants and types + +foreign lib { + /* + Checks the file named by the pathname pointed to by the path argument for + accessibility according to the bit pattern contained in amode. + + Example: + if (posix.access("/tmp/myfile", posix.F_OK) != .OK) { + fmt.printfln("/tmp/myfile access check failed: %v", posix.strerror(posix.errno())) + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html ]] + */ + access :: proc(path: cstring, amode: Mode_Flags = F_OK) -> result --- + + /* + Equivalent to `access` but relative paths are resolved based on `fd`. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html ]] + */ + faccessat :: proc(fd: FD, path: cstring, amode: Mode_Flags, flag: AT_Flags) -> result --- + + /* + The alarm() function shall cause the system to generate a SIGALRM signal for the process after the number of realtime seconds specified by seconds have elapsed. Processor scheduling delays may prevent the process from handling the signal as soon as it is generated. + + If seconds is 0, a pending alarm request, if any, is canceled. + + Returns: the time left on the previous alarm() or 0 + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/alarm.html ]] + */ + alarm :: proc(seconds: c.uint) -> c.uint --- + + /* + Causes the directory named by path to become the current working directory. + + Example: + if (posix.chdir("/tmp") == .OK) { + fmt.println("changed current directory to /tmp") + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/chdir.html ]] + */ + chdir :: proc(path: cstring) -> result --- + + /* + Equivalent to chdir but instead of a path the fildes is resolved to a directory. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchdir.html ]] + */ + fchdir :: proc(fildes: FD) -> result --- + + /* + Changes the user and group ownership of a file. + + If owner or group is specified as (uid_t)-1 or (gid_t)-1, respectively, the corresponding ID of the file shall not be changed. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/chown.html ]] + */ + @(link_name=LCHOWN) + chown :: proc(path: cstring, owner: uid_t, group: gid_t) -> result --- + + /* + Equivalent to chown expect that it takes a file descriptor. + + Example: + fildes := posix.open("/home/cnd/mod1", {.RDWR}) + pwd := posix.getpwnam("jones") + grp := posix.getgrnam("cnd") + posix.fchown(fildes, pwd.pw_uid, grp.gr_gid) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchown.html ]] + */ + @(link_name=LFCHOWN) + fchown :: proc(fildes: FD, owner: uid_t, mode: gid_t) -> result --- + + /* + Equivalent to fchown except that relative paths are based on the given fildes. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/chown.html ]] + */ + fchownat :: proc(fildes: FD, path: cstring, owner: uid_t, group: gid_t, flag: AT_Flags) -> result --- + + /* + If path points to a symbolic link, the owner and group of the link itself is changed. + Equivalent to chown on normal files. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/lchown.html ]] + */ + @(link_name=LLCHOWN) + lchown :: proc(path: cstring, owner: uid_t, group: gid_t) -> result --- + + /* + Deallocates the file descriptor indicated by fildes. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html ]] + */ + close :: proc(fildes: FD) -> result --- + + /* + Return configuration-defined string values. + Its use and purpose are similar to sysconf(), but it is used where string values rather than numeric values are returned. + + Returns: 0 (setting errno) if `name` is invalid, need `buf` `len` if buf is `nil`, amount of bytes added to buf otherwise + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/confstr.html ]] + */ + confstr :: proc(name: CS, buf: [^]c.char, len: c.size_t) -> c.size_t --- + + /* + Determines the current value of a configurable limit or option that is associated with a file or directory. + + Returns: value on success, -1 (setting errno) on failure, -1 (no errno) if the variable should be taken from limits + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fpathconf.html ]] + */ + pathconf :: proc(path: cstring, name: PC) -> c.long --- + + /* + Equivalent to pathconf but takes a file descriptor instead of a path. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fpathconf.html ]] + */ + fpathconf :: proc(fildes: FD, name: PC) -> c.long --- + + /* + Determines the current value of configurable system limit or options. + + Returns: value on success, -1 (setting errno) on failure, -1 (no errno) if the variable should be taken from limits + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sysconf.html ]] + */ + sysconf :: proc(name: SC) -> c.long --- + + /* + A string encoding function. The algorithm is implementation-defined. + + The use of crypt() for anything other than password hashing is not recommended. + + Returns: a static string overwritten by subsequent calls, `nil` (setting errno) on failure + */ + crypt :: proc(key: cstring, salt: cstring) -> cstring --- + + /* + An implementation-defined encoding algorithm. + The key generated by setkey() is used to encrypt the string block with encrypt(). + + block must be 64 bytes. + + decode controls if the block is encoded or decoded. + + May set errno to ENOSYS if the functionality is not supported. + + Example: + block: [64]byte + copy(block[:], "Hello, World!") + + posix.set_errno(.NONE) + posix.encrypt(raw_data(block[:]), decode=false) + assert(posix.errno() == .NONE, "encrypt not supported") + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/encrypt.html ]] + */ + encrypt :: proc(block: [^]c.char, decode: b32) --- + + /* + Returns a new file descriptor referring to the one given, sharing locks, clearing CLOEXEC. + + Returns: -1 (setting errno) on failure, the new file descriptor on success + + Example: + // Redirecting stdout to a file: + file := posix.open("/tmp/out", { .RDWR }) + posix.close(1) + posix.dup(file) + posix.close(file) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup.html ]] + */ + dup :: proc(fildes: FD) -> FD --- + + /* + Causes the file descriptor fildes2 to refer to the same open file description as + the file descriptor fildes and to share any locks, and shall return fildes2. + + Returns: -1 (setting errno) on failure, fildes2 on success + + Example: + // Redirecting stderr to stdout: + posix.dup2(1, 2) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup.html ]] + */ + dup2 :: proc(fildes, fildes2: FD) -> FD --- + + /* + Exits but, shall not call functions registered with atexit() nor any registered signal handlers. + Open streams shall not be flushed. + Whether open streams are closed (without flushing) is implementation-defined. Finally, the calling process shall be terminated with the consequences described below. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/_exit.html ]] + */ + _exit :: proc(status: c.int) --- + + /* + The exec family of functions shall replace the current process image with a new process image. + The new image shall be constructed from a regular, executable file called the new process image file. + There shall be no return from a successful exec, + because the calling process image is overlaid by the new process image. + + Takes arguments as varargs and the last of them must be nil. + + Example: + ret := posix.execl("/bin/ls", "ls", "-l", nil) + fmt.panicf("could not execute: %v %v", ret, posix.strerror(posix.errno())) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/execl.html ]] + */ + execl :: proc(path: cstring, arg0: cstring, #c_vararg args: ..cstring) -> c.int --- + + /* + The exec family of functions shall replace the current process image with a new process image. + The new image shall be constructed from a regular, executable file called the new process image file. + There shall be no return from a successful exec, + because the calling process image is overlaid by the new process image. + + Takes arguments as varargs and the last of them must be nil. + After the arguments an array of environment strings (also nil terminated) is expected. + + Example: + env := []cstring{ + "HOME=/usr/home", + "LOGNAME=home", + nil, + } + ret := posix.execle("/bin/ls", "ls", cstring("-l"), cstring(nil), raw_data(env)) + fmt.panicf("could not execute: %v", posix.strerror(posix.errno())) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/execl.html ]] + */ + execle :: proc(path: cstring, arg0: cstring, #c_vararg args: ..any) -> c.int --- + + /* + The exec family of functions shall replace the current process image with a new process image. + The new image shall be constructed from a regular, executable file called the new process image file. + There shall be no return from a successful exec, + because the calling process image is overlaid by the new process image. + + If file does not contain a slash the PATH environment variable is searched for a matching file. + Takes arguments as varargs and the last of them must be nil. + + Example: + ret := posix.execlp("ls", "-l", cstring(nil)) + fmt.panicf("could not execute: %v, %v", ret, posix.strerror(posix.errno())) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/execl.html ]] + */ + execlp :: proc(file: cstring, arg0: cstring, #c_vararg args: ..cstring) -> c.int --- + + /* + The exec family of functions shall replace the current process image with a new process image. + The new image shall be constructed from a regular, executable file called the new process image file. + There shall be no return from a successful exec, + because the calling process image is overlaid by the new process image. + + Takes arguments as an array which should be nil terminated. + + Example: + args := []cstring{ "ls", "-l", nil } + ret := posix.execv("/bin/ls", raw_data(args)) + fmt.panicf("could not execute: %v, %v", ret, posix.strerror(posix.errno())) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/execl.html ]] + */ + execv :: proc(path: cstring, argv: [^]cstring) -> c.int --- + + /* + The exec family of functions shall replace the current process image with a new process image. + The new image shall be constructed from a regular, executable file called the new process image file. + There shall be no return from a successful exec, + because the calling process image is overlaid by the new process image. + + If file does not contain a slash the PATH environment variable is searched for a matching file. + Takes arguments as an array which should be nil terminated. + + Example: + cmd := []cstring{ "ls", "-l", nil } + ret := posix.execvp("ls", raw_data(cmd)) + fmt.panicf("could not execute: %v, %v", ret, posix.strerror(posix.errno())) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/execl.html ]] + */ + execvp :: proc(file: cstring, argv: [^]cstring) -> c.int --- + + /* + The exec family of functions shall replace the current process image with a new process image. + The new image shall be constructed from a regular, executable file called the new process image file. + There shall be no return from a successful exec, + because the calling process image is overlaid by the new process image. + + Takes arguments as an array which should be nil terminated. + Takes environment variables as an array which should be nil terminated. + + Example: + cmd := []cstring{ "ls", "-l", nil } + env := []cstring{ "HOME=/usr/home", "LOGNAME=home", nil } + ret := posix.execve("/bin/ls", raw_data(cmd), raw_data(env)) + fmt.panicf("could not execute: %v, %v", ret, posix.strerror(posix.errno())) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/execl.html ]] + */ + execve :: proc(path: cstring, argv: [^]cstring, envp: [^]cstring) -> c.int --- + + /* + The exec family of functions shall replace the current process image with a new process image. + The new image shall be constructed from a regular, executable file called the new process image file. + There shall be no return from a successful exec, + because the calling process image is overlaid by the new process image. + + Equivalent to execve but takes a file descriptor instead of a path. + + Example: + ls := posix.open("/bin/ls", { .EXEC }) + cmd := []cstring{ "ls", "-l", nil } + env := []cstring{ "HOME=/usr/home", "LOGNAME=home", nil } + ret := posix.fexecve(ls, raw_data(cmd), raw_data(env)) + fmt.panicf("could not execute: %v, %v", ret, posix.strerror(posix.errno())) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/execl.html ]] + */ + fexecve :: proc(fd: FD, argv: [^]cstring, envp: [^]cstring) -> c.int --- + + /* + Example: + for i, entry := 0, posix.environ[0]; entry != nil; i, entry = i+1, posix.environ[i] { + fmt.println(entry) + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/execl.html ]] + */ + environ: [^]cstring + + /* + Forcec all currently queued I/O operations associated with the file indicated by file descriptor + fildes to the synchronized I/O completion state. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdatasync.html ]] + */ + fdatasync :: proc(fd: FD) -> result --- + + /* + The fork() function shall create a new process. + The new process (child process) shall be an exact copy of the calling process (parent process). + With some exceptions outlined below. + + Result: -1 (setting errno) on failure, otherwise 0 to the child process and the child process id to the parent process. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html ]] + */ + fork :: proc() -> pid_t --- + + /* + Requests that all data for the open file descriptor named by fildes is to be transferred + to the storage device associated with the file described by fildes. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fsync.html ]] + */ + fsync :: proc(fildes: FD) -> result --- + + /* + Truncates a file to the specified length. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/truncate.html ]] + */ + truncate :: proc(path: cstring, length: off_t) -> result --- + + /* + Truncates a file to the specified length. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html ]] + */ + ftruncate :: proc(fildes: FD, length: off_t) -> result --- + + /* + Places an absolute pathname of the current working directory into buf. + + Returns: buf as a cstring on success, nil (setting errno) on failure + + Example: + size: int + path_max := posix.pathconf(".", ._PATH_MAX) + if path_max == -1 { + size = 1024 + } else if path_max > 10240 { + size = 10240 + } else { + size = int(path_max) + } + + buf: [dynamic]byte + cwd: cstring + for ; cwd == nil; size *= 2 { + if err := resize(&buf, size); err != nil { + fmt.panicf("allocation failure: %v", err) + } + + cwd = posix.getcwd(raw_data(buf), len(buf)) + if errno := posix.errno(); cwd == nil && errno != .ERANGE { + fmt.panicf("getcwd failure: %v", posix.strerror(errno)) + } + } + + fmt.println(path_max, cwd) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getcwd.html ]] + */ + getcwd :: proc(buf: [^]c.char, size: c.size_t) -> cstring --- + + /* + Returns the effective group ID of the calling process. + + Returns: the ID, no failure is defined + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getegid.html ]] + */ + getegid :: proc() -> gid_t --- + + /* + Returns the effective user ID of the calling process. + + Returns: the ID, no failure is defined + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/geteuid.html ]] + */ + geteuid :: proc() -> uid_t --- + + /* + Returns the real group ID of the calling process. + + Returns: the ID, no failure is defined + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getgid.html ]] + */ + getgid :: proc() -> gid_t --- + + /* + Fills the grouplist array with the current supplementary group IDs of the calling process. + + Returns: -1 (setting errno) on failure, desired grouplist length if gidsetsize is 0, amount of IDs added otherwise + + Example: + length := posix.getgroups(0, nil) + if length == -1 { + fmt.panicf("getgroups failure: %v", posix.strerror(posix.errno())) + } + + groups := make([]posix.gid_t, length) or_else panic("allocation failure") + if posix.getgroups(length, raw_data(groups)) != length { + fmt.panicf("getgroups failure: %v", posix.strerror(posix.errno())) + } + + fmt.println(groups) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getgroups.html ]] + */ + getgroups :: proc(gidsetsize: c.int, grouplist: [^]gid_t) -> c.int --- + + /* + Retrieves a 32-bit identifier for the current host. + + Returns: the ID, no failure is defined + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/gethostid.html ]] + */ + gethostid :: proc() -> c.long --- + + /* + Returns the standard host name for the current machine. + + Host names are limited to HOST_NAME_MAX bytes. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/gethostname.html ]] + */ + gethostname :: proc(name: [^]c.char, namelen: c.size_t) -> result --- + + /* + Returns a string containing the user name associated by the login activity. + + Returns: nil (setting errno) on failure, the login name otherwise in a potentially static buffer overwritten by subsequent calls + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getlogin.html ]] + */ + getlogin :: proc() -> cstring --- + + /* + Equivalent to getlogin but puts the name in the name buffer given. + + The name is limited to LOGIN_NAME_MAX bytes. + + Example: + max := posix.sysconf(posix._SC_LOGIN_NAME_MAX)+1 + buf := make([]byte, max) + posix.getlogin_r(raw_data(buf), uint(len(max))) + fmt.printfln("login: %v", cstring(buf)) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getlogin.html ]] + */ + getlogin_r :: proc(name: [^]c.char, namelen: c.size_t) -> Errno --- + + /* + A command-line parser, see linked docs. + + Example: + // The following code fragment shows how you might process the arguments for a utility that + // can take the mutually-exclusive options a and b and the options f and o, both of which + // require arguments. + + bflg, aflg, errflg: bool + ifile: string + ofile: string + + for { + c := posix.getopt(i32(len(runtime.args__)), raw_data(runtime.args__), ":abf:o:") + (c != -1) or_break + + switch c { + case 'a': + if bflg { + errflg = true + } else { + aflg = true + } + case 'b': + if aflg { + errflg = true + } else { + bflg = true + } + case 'f': + ifile = string(posix.optarg) + case 'o': + ofile = string(posix.optarg) + case ':': /* -f or -o without operand */ + fmt.eprintfln("Option -%c requires an operand", posix.optopt) + errflg = true + case '?': + fmt.eprintfln("Unrecognized option: '-%c'", posix.optopt) + errflg = true + } + } + + if errflg { + fmt.eprintfln("usage: . . . ") + posix.exit(2) + } + + // Loop through remaining arguments: + for ; posix.optind < i32(len(runtime.args__)); posix.optind += 1 { + fmt.println(runtime.args__[posix.optind]) + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getopt.html ]] + */ + getopt :: proc(argc: c.int, argv: [^]cstring, optstring: cstring) -> c.int --- + + optarg: cstring + opterr: c.int + optind: c.int + optopt: c.int + + /* + Returns the process group ID of the process whose process ID is equal to pid. + If pid is 0, it returns the process group ID of the calling process. + + Returns: -1 on failure, the ID otherwise + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpgid.html ]] + */ + getpgid :: proc(pid: pid_t) -> pid_t --- + + /* + Returns the process group ID of the calling process. + + Returns: no failure + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpgrp.html ]] + */ + getpgrp :: proc() -> pid_t --- + + /* + Returns the ID of the calling process. + + Returns: no failure + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpid.html ]] + */ + getpid :: proc() -> pid_t --- + + /* + Returns the parent process ID. + + Returns: no failure + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getppid.html ]] + */ + getppid :: proc() -> pid_t --- + + + /* + Get the process group ID of the session leader. + If pid is 0, it is the current process. + + Returns: -1 (setting errno) on failure, the pid otherwise + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsid.html ]] + */ + getsid :: proc(pid: pid_t) -> pid_t --- + + /* + Returns the real user ID of the calling process. + + Returns: no failure + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getuid.html ]] + */ + getuid :: proc() -> uid_t --- + + /* + Tests whether fildes is associated with a terminal device. + + Returns: false (setting errno) if fildes is invalid or not a terminal, true otherwise + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/isatty.html ]] + */ + isatty :: proc(fildes: FD) -> b32 --- + + /* + Creates a new link for the existing file path1 to path2. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/link.html ]] + */ + link :: proc(path1: cstring, path2: cstring) -> result --- + + /* + If path1 is relative it is relative to directory fd1. + If path2 is relative it is relative to directory fd2. + If flag is { .SYMLINK_FOLLOW } path1 is resolved to its link if it is a link. + Equivalent to link otherwise. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/link.html ]] + */ + linkat :: proc(fd1: FD, path1: cstring, fd2: FD, path2: cstring, flag: AT_Flags) -> result --- + + /* + Creates a symbolic link called path2 that contains a link to path1. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/symlink.html ]] + */ + symlink :: proc(path1: cstring, path2: cstring) -> result --- + + /* + Equivalent to symlink but relative paths are resolved to dir fd. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/symlink.html ]] + */ + symlinkat :: proc(path1: cstring, fd: FD, path2: cstring) -> result --- + + /* + Locks sections of a file with advisory-mode locks. + + Example: + fildes := posix.open("/home/cnd/mod1", { .RDWR }) + if posix.lockf(fildes, .TLOCK, 10000) != .OK { + errno := posix.errno(); #partial switch errno { + case .EACCES, .EAGAIN: + // File is already locked. + case: + // Other error. + fmt.panicf("lockf failure: %v", posix.strerror(errno)) + } + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/lockf.html ]] + */ + lockf :: proc(fildes: FD, function: Lock_Function, size: off_t) -> result --- + + /* + Sets the file offset of the given file descriptor. + + If whence is .SET, the offset is set + If whence is .CUR, the offset is the current offset + given offset + If whence is .END, the offset is set to the size of the file + given offset + + Returns: the resulting offset or -1 (setting errno) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/lseek.html ]] + */ + lseek :: proc(fildes: FD, offset: off_t, whence: Whence) -> off_t --- + + /* + Changes the nice value of a process. + + Higher values result in less favorable scheduling. + + Because -1 is a valid nice value, checking failure would be done by first setting errno to .NONE + and then calling nice. + + Returns: the new nice value, or -1 (setting) errno on failure + + Example: + posix.set_errno(.NONE) + niceness := posix.nice(-20) + if errno := posix.errno(); niceness == -1 && errno != .NONE { + fmt.panicf("nice failure: %v", posix.strerror(errno)) + } + fmt.printfln("Niceness is now: %v", niceness) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/nice.html ]] + */ + nice :: proc(incr: c.int) -> c.int --- + + /* + Suspend the thread until a signal is received. + + Returns: -1 (setting errno to EINTR) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pause.html ]] + */ + pause :: proc() -> c.int --- + + /* + Create an interprocess channel. + + Example: + fildes: [2]posix.FD + if posix.pipe(&fildes) != .OK { + // Handle error ... + } + + switch posix.fork() { + case -1: + // Handle error ... + + case 0: /* Child - reads from pipe */ + BSIZE :: 100 + buf: [BSIZE]byte + nbytes: int + + posix.close(fildes[1]) /* Write end is unused */ + nbytes = posix.read(fildes[0], raw_data(buf[:]), BSIZE) /* Get data from pipe */ + /* At this point, a further read would see end-of-file ... */ + posix.close(fildes[0]) /* Finished with pipe */ + + fmt.println(string(buf[:nbytes])) + + posix.exit(0) + + case: /* Parent - write to pipe */ + msg := raw_data(transmute([]byte)string("Hello world\n")) + posix.close(fildes[0]) /* Read end is unused */ + posix.write(fildes[1], msg, 12); /* Write data on pipe */ + posix.close(fildes[1]) + posix.exit(0) + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pipe.html ]] + */ + pipe :: proc(fildes: ^[2]FD) -> result --- + + /* + Read from a file. + + Returns: the amount of bytes read or -1 (setting errno) on failure + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pread.html ]] + */ + read :: proc(fd: FD, buf: [^]byte, nbyte: c.size_t) -> c.ssize_t --- + + /* + Equivalent to read on a specified offset instead of the internal offset. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pread.html ]] + */ + pread :: proc(fd: FD, buf: [^]byte, nbyte: c.size_t, offset: off_t) -> c.ssize_t --- + + /* + Write on a file. + + Returns: the amount of bytes written or -1 (setting errno) on failure. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html ]] + */ + write :: proc(fd: FD, buf: [^]byte, buflen: c.size_t) -> c.ssize_t --- + + /* + Equivalent to write on a specified offset instead of the internal offset. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html ]] + */ + pwrite :: proc(fd: FD, buf: [^]byte, buflen: c.size_t, offset: off_t) -> c.ssize_t --- + + /* + Read the contents of a symbolic link. + + Returns: the amount of bytes read or -1 (setting errno) on failure. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html ]] + */ + readlink :: proc(path: cstring, buf: [^]byte, bufsize: c.size_t) -> c.ssize_t --- + + /* + Equivalent to readlink but relative paths are resolved based on the dir fd. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html ]] + */ + readlinkat :: proc(fd: FD, path: cstring, buf: [^]byte, bufsize: c.size_t) -> c.ssize_t --- + + /* + Remove an (empty) directory. + + ]] More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/rmdir.html ]] + */ + rmdir :: proc(path: cstring) -> result --- + + /* + Set the effective group ID. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setegid.html ]] + */ + setegid :: proc(gid: gid_t) -> result --- + + /* + Sets the effective user ID. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/seteuid.html ]] + */ + seteuid :: proc(uid: uid_t) -> result --- + + /* + Sets the group ID. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setgid.html ]] + */ + setgid :: proc(gid: gid_t) -> result --- + + /* + Set process group ID. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setpgid.html ]] + */ + setpgid :: proc(pid: pid_t, pgid: pid_t) -> result --- + + /* + Set the process group ID to that of the process. + + Returns: the process group id, no failures are defined + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setpgrp.html ]] + */ + setpgrp :: proc() -> pid_t --- + + /* + Set the real and effective group IDs. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setregid.html ]] + */ + setregid :: proc(rgid: gid_t, egid: gid_t) -> result --- + + /* + Set real and effective user IDs. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setreuid.html ]] + */ + setreuid :: proc(ruid: uid_t, euid: uid_t) -> result --- + + /* + Create session and set process group ID. + + Returns: the new process group ID or -1 (setting errno) on failure + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setsid.html ]] + */ + setsid :: proc() -> pid_t --- + + /* + Set user ID. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setuid.html ]] + */ + setuid :: proc(uid: uid_t) -> result --- + + /* + Suspend execution for an interval of time. + + Returns: the time left to sleep (may be > 0 in case of signals) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sleep.html ]] + */ + sleep :: proc(seconds: c.uint) -> c.uint --- + + /* + Copy nbyte bytes, from src, to dest, exchanging adjecent bytes. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/swab.html ]] + */ + swab :: proc(src: [^]byte, dest: [^]byte, nbytes: c.ssize_t) --- + + /* + Schedule file system updates. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sync.html ]] + */ + sync :: proc() --- + + /* + Get the foreground process group ID. + + Returns: -1 (setting errno) on failure, the id otherwise + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetpgrp.html ]] + */ + tcgetpgrp :: proc(fildes: FD) -> pid_t --- + + /* + Set the foreground process group ID. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetpgrp.html ]] + */ + tcsetpgrp :: proc(fildes: FD, pgid_id: pid_t) -> result --- + + /* + Find the path name of a terminal. + + Returns: nil (setting errno) on failure, the name, which may be invalidated by subsequent calls on success + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/ttyname.html ]] + */ + ttyname :: proc(fildes: FD) -> cstring --- + + /* + Equivalent to ttyname but name is placed into the buf. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/ttyname.html ]] + */ + ttyname_r :: proc(fildes: FD, name: [^]byte, namesize: c.size_t) -> Errno --- + + /* + Remove a directory entry. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html ]] + */ + unlink :: proc(path: cstring) -> result --- + + /* + Equivalent to unlink or rmdir (if flag is .REMOVEDIR) but relative paths are relative to the dir fd. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html ]] + */ + unlinkat :: proc(fd: FD, path: cstring, flag: AT_Flags) -> result --- +} + +STDERR_FILENO :: 2 +STDIN_FILENO :: 0 +STDOUT_FILENO :: 1 + +Mode_Flag_Bits :: enum c.int { + X_OK = log2(X_OK), + W_OK = log2(W_OK), + R_OK = log2(R_OK), +} +Mode_Flags :: bit_set[Mode_Flag_Bits; c.int] + +#assert(_F_OK == 0) +F_OK :: Mode_Flags{} + +CS :: enum c.int { + _PATH = _CS_PATH, + _POSIX_V6_ILP32_OFF32_CFLAGS = _CS_POSIX_V6_ILP32_OFF32_CFLAGS, + _POSIX_V6_ILP32_OFF32_LDFLAGS = _CS_POSIX_V6_ILP32_OFF32_LDFLAGS, + _POSIX_V6_ILP32_OFF32_LIBS = _CS_POSIX_V6_ILP32_OFF32_LIBS, + _POSIX_V6_ILP32_OFFBIG_CFLAGS = _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS, + _POSIX_V6_ILP32_OFFBIG_LDFLAGS = _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS, + _POSIX_V6_ILP32_OFFBIG_LIBS = _CS_POSIX_V6_ILP32_OFFBIG_LIBS, + _POSIX_V6_LP64_OFF64_CFLAGS = _CS_POSIX_V6_LP64_OFF64_CFLAGS, + _POSIX_V6_LP64_OFF64_LDFLAGS = _CS_POSIX_V6_LP64_OFF64_LDFLAGS, + _POSIX_V6_LP64_OFF64_LIBS = _CS_POSIX_V6_LP64_OFF64_LIBS, + _POSIX_V6_LPBIG_OFFBIG_CFLAGS = _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS, + _POSIX_V6_LPBIG_OFFBIG_LDFLAGS = _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS, + _POSIX_V6_LPBIG_OFFBIG_LIBS = _CS_POSIX_V6_LPBIG_OFFBIG_LIBS, + _POSIX_V6_WIDTH_RESTRICTED_ENVS = _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS, +} + +PC :: enum c.int { + _2_SYMLINK = _PC_2_SYMLINK, + _ALLOC_SIZE_MIN = _PC_ALLOC_SIZE_MIN, + _ASYNC_IO = _PC_ASYNC_IO, + _CHOWN_RESTRICTED = _PC_CHOWN_RESTRICTED, + _FILESIZEBITS = _PC_FILESIZEBITS, + _LINK_MAX = _PC_LINK_MAX, + _MAX_CANON = _PC_MAX_CANON, + _MAX_INPUT = _PC_MAX_INPUT, + _NAME_MAX = _PC_NAME_MAX, + _NO_TRUNC = _PC_NO_TRUNC, + _PATH_MAX = _PC_PATH_MAX, + _PIPE_BUF = _PC_PIPE_BUF, + _PRIO_IO = _PC_PRIO_IO, + _REC_INCR_XFER_SIZE = _PC_REC_INCR_XFER_SIZE, + _REC_MAX_XFER_SIZE = _PC_REC_MAX_XFER_SIZE, + _REC_MIN_XFER_SIZE = _PC_REC_MIN_XFER_SIZE, + _REC_XFER_ALIGN = _PC_REC_XFER_ALIGN, + _SYMLINK_MAX = _PC_SYMLINK_MAX, + _SYNC_IO = _PC_SYNC_IO, + _VDISABLE = _PC_VDISABLE, +} + +SC :: enum c.int { + _2_C_BIND = _SC_2_C_BIND, + _2_C_DEV = _SC_2_C_DEV, + _2_CHAR_TERM = _SC_2_CHAR_TERM, + _2_FORT_DEV = _SC_2_FORT_DEV, + _2_FORT_RUN = _SC_2_FORT_RUN, + _2_LOCALEDEF = _SC_2_LOCALEDEF, + _2_PBS = _SC_2_PBS, + _2_PBS_ACCOUNTING = _SC_2_PBS_ACCOUNTING, + _2_PBS_CHECKPOINT = _SC_2_PBS_CHECKPOINT, + _2_PBS_LOCATE = _SC_2_PBS_LOCATE, + _2_PBS_MESSAGE = _SC_2_PBS_MESSAGE, + _2_PBS_TRACK = _SC_2_PBS_TRACK, + _2_SW_DEV = _SC_2_SW_DEV, + _2_UPE = _SC_2_UPE, + _2_VERSION = _SC_2_VERSION, + _ADVISORY_INFO = _SC_ADVISORY_INFO, + _AIO_LISTIO_MAX = _SC_AIO_LISTIO_MAX, + _AIO_MAX = _SC_AIO_MAX, + _AIO_PRIO_DELTA_MAX = _SC_AIO_PRIO_DELTA_MAX, + _ARG_MAX = _SC_ARG_MAX, + _ASYNCHRONOUS_IO = _SC_ASYNCHRONOUS_IO, + _ATEXIT_MAX = _SC_ATEXIT_MAX, + _BARRIERS = _SC_BARRIERS, + _BC_BASE_MAX = _SC_BC_BASE_MAX, + _BC_DIM_MAX = _SC_BC_DIM_MAX, + _BC_SCALE_MAX = _SC_BC_SCALE_MAX, + _BC_STRING_MAX = _SC_BC_STRING_MAX, + _CHILD_MAX = _SC_CHILD_MAX, + _CLK_TCK = _SC_CLK_TCK, + _CLOCK_SELECTION = _SC_CLOCK_SELECTION, + _COLL_WEIGHTS_MAX = _SC_COLL_WEIGHTS_MAX, + _CPUTIME = _SC_CPUTIME, + _DELAYTIMER_MAX = _SC_DELAYTIMER_MAX, + _EXPR_NEST_MAX = _SC_EXPR_NEST_MAX, + _FSYNC = _SC_FSYNC, + _GETGR_R_SIZE_MAX = _SC_GETGR_R_SIZE_MAX, + _GETPW_R_SIZE_MAX = _SC_GETPW_R_SIZE_MAX, + _HOST_NAME_MAX = _SC_HOST_NAME_MAX, + _IOV_MAX = _SC_IOV_MAX, + _IPV6 = _SC_IPV6, + _JOB_CONTROL = _SC_JOB_CONTROL, + _LINE_MAX = _SC_LINE_MAX, + _LOGIN_NAME_MAX = _SC_LOGIN_NAME_MAX, + _MAPPED_FILES = _SC_MAPPED_FILES, + _MEMLOCK = _SC_MEMLOCK, + _MEMLOCK_RANGE = _SC_MEMLOCK_RANGE, + _MEMORY_PROTECTION = _SC_MEMORY_PROTECTION, + _MESSAGE_PASSING = _SC_MESSAGE_PASSING, + _MONOTONIC_CLOCK = _SC_MONOTONIC_CLOCK, + _MQ_OPEN_MAX = _SC_MQ_OPEN_MAX, + _MQ_PRIO_MAX = _SC_MQ_PRIO_MAX, + _NGROUPS_MAX = _SC_NGROUPS_MAX, + _OPEN_MAX = _SC_OPEN_MAX, + _PAGE_SIZE = _SC_PAGE_SIZE, + _PAGESIZE = _SC_PAGESIZE, + _PRIORITIZED_IO = _SC_PRIORITIZED_IO, + _PRIORITY_SCHEDULING = _SC_PRIORITY_SCHEDULING, + _RAW_SOCKETS = _SC_RAW_SOCKETS, + _RE_DUP_MAX = _SC_RE_DUP_MAX, + _READER_WRITER_LOCKS = _SC_READER_WRITER_LOCKS, + _REALTIME_SIGNALS = _SC_REALTIME_SIGNALS, + _REGEXP = _SC_REGEXP, + _RTSIG_MAX = _SC_RTSIG_MAX, + _SAVED_IDS = _SC_SAVED_IDS, + _SEM_NSEMS_MAX = _SC_SEM_NSEMS_MAX, + _SEM_VALUE_MAX = _SC_SEM_VALUE_MAX, + _SEMAPHORES = _SC_SEMAPHORES, + _SHARED_MEMORY_OBJECTS = _SC_SHARED_MEMORY_OBJECTS, + _SHELL = _SC_SHELL, + _SIGQUEUE_MAX = _SC_SIGQUEUE_MAX, + _SPAWN = _SC_SPAWN, + _SPIN_LOCKS = _SC_SPIN_LOCKS, + _SPORADIC_SERVER = _SC_SPORADIC_SERVER, + _SS_REPL_MAX = _SC_SS_REPL_MAX, + _STREAM_MAX = _SC_STREAM_MAX, + _SYMLOOP_MAX = _SC_SYMLOOP_MAX, + _SYNCHRONIZED_IO = _SC_SYNCHRONIZED_IO, + _THREAD_ATTR_STACKADDR = _SC_THREAD_ATTR_STACKADDR, + _THREAD_ATTR_STACKSIZE = _SC_THREAD_ATTR_STACKSIZE, + _THREAD_CPUTIME = _SC_THREAD_CPUTIME, + _THREAD_DESTRUCTOR_ITERATIONS = _SC_THREAD_DESTRUCTOR_ITERATIONS, + _THREAD_KEYS_MAX = _SC_THREAD_KEYS_MAX, + _THREAD_PRIO_INHERIT = _SC_THREAD_PRIO_INHERIT, + _THREAD_PRIO_PROTECT = _SC_THREAD_PRIO_PROTECT, + _THREAD_PRIORITY_SCHEDULING = _SC_THREAD_PRIORITY_SCHEDULING, + _THREAD_PROCESS_SHARED = _SC_THREAD_PROCESS_SHARED, + _THREAD_SAFE_FUNCTIONS = _SC_THREAD_SAFE_FUNCTIONS, + _THREAD_SPORADIC_SERVER = _SC_THREAD_SPORADIC_SERVER, + _THREAD_STACK_MIN = _SC_THREAD_STACK_MIN, + _THREAD_THREADS_MAX = _SC_THREAD_THREADS_MAX, + _THREADS = _SC_THREADS, + _TIMEOUTS = _SC_TIMEOUTS, + _TIMER_MAX = _SC_TIMER_MAX, + _TIMERS = _SC_TIMERS, + _TRACE = _SC_TRACE, + _TRACE_EVENT_FILTER = _SC_TRACE_EVENT_FILTER, + _TRACE_EVENT_NAME_MAX = _SC_TRACE_EVENT_NAME_MAX, + _TRACE_INHERIT = _SC_TRACE_INHERIT, + _TRACE_LOG = _SC_TRACE_LOG, + _TRACE_NAME_MAX = _SC_TRACE_NAME_MAX, + _TRACE_SYS_MAX = _SC_TRACE_SYS_MAX, + _TRACE_USER_EVENT_MAX = _SC_TRACE_USER_EVENT_MAX, + _TTY_NAME_MAX = _SC_TTY_NAME_MAX, + _TYPED_MEMORY_OBJECTS = _SC_TYPED_MEMORY_OBJECTS, + _TZNAME_MAX = _SC_TZNAME_MAX, + _V6_ILP32_OFF32 = _SC_V6_ILP32_OFF32, + _V6_ILP32_OFFBIG = _SC_V6_ILP32_OFFBIG, + _V6_LP64_OFF64 = _SC_V6_LP64_OFF64, + _V6_LPBIG_OFFBIG = _SC_V6_LPBIG_OFFBIG, + _VERSION = _SC_VERSION, + _XOPEN_CRYPT = _SC_XOPEN_CRYPT, + _XOPEN_ENH_I18N = _SC_XOPEN_ENH_I18N, + _XOPEN_REALTIME = _SC_XOPEN_REALTIME, + _XOPEN_REALTIME_THREADS = _SC_XOPEN_REALTIME_THREADS, + _XOPEN_SHM = _SC_XOPEN_SHM, + _XOPEN_STREAMS = _SC_XOPEN_STREAMS, + _XOPEN_UNIX = _SC_XOPEN_UNIX, + _XOPEN_VERSION = _SC_XOPEN_VERSION, +} + +Lock_Function :: enum c.int { + // Lock a section for exclusive use. + LOCK = F_LOCK, + // Test a section for locks by other processes. + TEST = F_TEST, + // Test and lock a section for exclusive use. + TLOCK = F_TLOCK, + // Unlock locked sections. + ULOCK = F_ULOCK, +} + +when ODIN_OS == .NetBSD { + @(private) LCHOWN :: "__posix_chown" + @(private) LFCHOWN :: "__posix_fchown" + @(private) LLCHOWN :: "__posix_lchown" +} else { + @(private) LCHOWN :: "chown" + @(private) LFCHOWN :: "fchown" + @(private) LLCHOWN :: "lchown" +} + +when ODIN_OS == .Darwin { + + _F_OK :: 0 + X_OK :: (1<<0) + W_OK :: (1<<1) + R_OK :: (1<<2) + + F_LOCK :: 1 + F_TEST :: 3 + F_TLOCK :: 2 + F_ULOCK :: 0 + + _CS_PATH :: 1 + _CS_POSIX_V6_ILP32_OFF32_CFLAGS :: 2 + _CS_POSIX_V6_ILP32_OFF32_LDFLAGS :: 3 + _CS_POSIX_V6_ILP32_OFF32_LIBS :: 4 + _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS :: 5 + _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS :: 6 + _CS_POSIX_V6_ILP32_OFFBIG_LIBS :: 7 + _CS_POSIX_V6_LP64_OFF64_CFLAGS :: 8 + _CS_POSIX_V6_LP64_OFF64_LDFLAGS :: 9 + _CS_POSIX_V6_LP64_OFF64_LIBS :: 10 + _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS :: 11 + _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS :: 12 + _CS_POSIX_V6_LPBIG_OFFBIG_LIBS :: 13 + _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS :: 14 + + _PC_LINK_MAX :: 1 + _PC_MAX_CANON :: 2 + _PC_MAX_INPUT :: 3 + _PC_NAME_MAX :: 4 + _PC_PATH_MAX :: 5 + _PC_PIPE_BUF :: 6 + _PC_CHOWN_RESTRICTED :: 7 + _PC_NO_TRUNC :: 8 + _PC_VDISABLE :: 9 + _PC_2_SYMLINK :: 15 + _PC_ALLOC_SIZE_MIN :: 16 + _PC_ASYNC_IO :: 17 + _PC_FILESIZEBITS :: 18 + _PC_PRIO_IO :: 19 + _PC_REC_INCR_XFER_SIZE :: 20 + _PC_REC_MAX_XFER_SIZE :: 21 + _PC_REC_MIN_XFER_SIZE :: 22 + _PC_REC_XFER_ALIGN :: 23 + _PC_SYMLINK_MAX :: 24 + _PC_SYNC_IO :: 25 + + _SC_ARG_MAX :: 1 + _SC_CHILD_MAX :: 2 + _SC_CLK_TCK :: 3 + _SC_NGROUPS_MAX :: 4 + _SC_OPEN_MAX :: 5 + _SC_JOB_CONTROL :: 6 + _SC_SAVED_IDS :: 7 + _SC_VERSION :: 8 + _SC_BC_BASE_MAX :: 9 + + _SC_BC_DIM_MAX :: 10 + _SC_BC_SCALE_MAX :: 11 + _SC_BC_STRING_MAX :: 12 + _SC_COLL_WEIGHTS_MAX :: 13 + _SC_EXPR_NEST_MAX :: 14 + _SC_LINE_MAX :: 15 + _SC_RE_DUP_MAX :: 16 + _SC_2_VERSION :: 17 + _SC_2_C_BIND :: 18 + _SC_2_C_DEV :: 19 + + _SC_2_CHAR_TERM :: 20 + _SC_2_FORT_DEV :: 21 + _SC_2_FORT_RUN :: 22 + _SC_2_LOCALEDEF :: 23 + _SC_2_SW_DEV :: 24 + _SC_2_UPE :: 25 + _SC_STREAM_MAX :: 26 + _SC_TZNAME_MAX :: 27 + _SC_ASYNCHRONOUS_IO :: 28 + _SC_PAGE_SIZE :: 29 + _SC_PAGESIZE :: _SC_PAGE_SIZE + + _SC_MEMLOCK :: 30 + _SC_MEMLOCK_RANGE :: 31 + _SC_MEMORY_PROTECTION :: 32 + _SC_MESSAGE_PASSING :: 33 + _SC_PRIORITIZED_IO :: 34 + _SC_PRIORITY_SCHEDULING :: 35 + _SC_REALTIME_SIGNALS :: 36 + _SC_SEMAPHORES :: 37 + _SC_FSYNC :: 38 + _SC_SHARED_MEMORY_OBJECTS :: 39 + + _SC_SYNCHRONIZED_IO :: 40 + _SC_TIMERS :: 41 + _SC_AIO_LISTIO_MAX :: 42 + _SC_AIO_MAX :: 43 + _SC_AIO_PRIO_DELTA_MAX :: 44 + _SC_DELAYTIMER_MAX :: 45 + _SC_MQ_OPEN_MAX :: 46 + _SC_MAPPED_FILES :: 47 + _SC_RTSIG_MAX :: 48 + _SC_SEM_NSEMS_MAX :: 49 + + _SC_SEM_VALUE_MAX :: 50 + _SC_SIGQUEUE_MAX :: 51 + _SC_TIMER_MAX :: 52 + _SC_IOV_MAX :: 56 + _SC_2_PBS :: 59 + + _SC_2_PBS_ACCOUNTING :: 60 + _SC_2_PBS_CHECKPOINT :: 61 + _SC_2_PBS_LOCATE :: 62 + _SC_2_PBS_MESSAGE :: 63 + _SC_2_PBS_TRACK :: 64 + _SC_ADVISORY_INFO :: 65 + _SC_BARRIERS :: 66 + _SC_CLOCK_SELECTION :: 67 + _SC_CPUTIME :: 68 + + _SC_GETGR_R_SIZE_MAX :: 70 + _SC_GETPW_R_SIZE_MAX :: 71 + _SC_HOST_NAME_MAX :: 72 + _SC_LOGIN_NAME_MAX :: 73 + _SC_MONOTONIC_CLOCK :: 74 + _SC_MQ_PRIO_MAX :: 75 + _SC_READER_WRITER_LOCKS :: 76 + _SC_REGEXP :: 77 + _SC_SHELL :: 78 + _SC_SPAWN :: 79 + + _SC_SPIN_LOCKS :: 80 + _SC_SPORADIC_SERVER :: 81 + _SC_THREAD_ATTR_STACKADDR :: 82 + _SC_THREAD_ATTR_STACKSIZE :: 83 + _SC_THREAD_CPUTIME :: 84 + _SC_THREAD_DESTRUCTOR_ITERATIONS :: 85 + _SC_THREAD_KEYS_MAX :: 86 + _SC_THREAD_PRIO_INHERIT :: 87 + _SC_THREAD_PRIO_PROTECT :: 88 + _SC_THREAD_PRIORITY_SCHEDULING :: 89 + + _SC_THREAD_PROCESS_SHARED :: 90 + _SC_THREAD_SAFE_FUNCTIONS :: 91 + _SC_THREAD_SPORADIC_SERVER :: 92 + _SC_THREAD_STACK_MIN :: 93 + _SC_THREAD_THREADS_MAX :: 94 + _SC_TIMEOUTS :: 95 + _SC_THREADS :: 96 + _SC_TRACE :: 97 + _SC_TRACE_EVENT_FILTER :: 98 + _SC_TRACE_INHERIT :: 99 + + _SC_TRACE_LOG :: 100 + _SC_TTY_NAME_MAX :: 101 + _SC_TYPED_MEMORY_OBJECTS :: 102 + _SC_V6_ILP32_OFF32 :: 103 + _SC_V6_ILP32_OFFBIG :: 104 + _SC_V6_LP64_OFF64 :: 105 + _SC_V6_LPBIG_OFFBIG :: 106 + _SC_ATEXIT_MAX :: 107 + _SC_XOPEN_CRYPT :: 108 + _SC_XOPEN_ENH_I18N :: 109 + + _SC_XOPEN_REALTIME :: 111 + _SC_XOPEN_REALTIME_THREADS :: 112 + _SC_XOPEN_SHM :: 113 + _SC_XOPEN_STREAMS :: 114 + _SC_XOPEN_UNIX :: 115 + _SC_XOPEN_VERSION :: 116 + _SC_IPV6 :: 118 + _SC_RAW_SOCKETS :: 119 + + _SC_SYMLOOP_MAX :: 120 + _SC_SS_REPL_MAX :: 126 + _SC_TRACE_EVENT_NAME_MAX :: 127 + _SC_TRACE_NAME_MAX :: 128 + _SC_TRACE_SYS_MAX :: 129 + _SC_TRACE_USER_EVENT_MAX :: 130 + + _POSIX_VDISABLE :: '\377' + +} else when ODIN_OS == .FreeBSD { + + _F_OK :: 0 + X_OK :: 0x01 + W_OK :: 0x02 + R_OK :: 0x04 + + F_LOCK :: 1 + F_TEST :: 3 + F_TLOCK :: 2 + F_ULOCK :: 0 + + _CS_PATH :: 1 + _CS_POSIX_V6_ILP32_OFF32_CFLAGS :: 2 + _CS_POSIX_V6_ILP32_OFF32_LDFLAGS :: 3 + _CS_POSIX_V6_ILP32_OFF32_LIBS :: 4 + _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS :: 5 + _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS :: 6 + _CS_POSIX_V6_ILP32_OFFBIG_LIBS :: 7 + _CS_POSIX_V6_LP64_OFF64_CFLAGS :: 8 + _CS_POSIX_V6_LP64_OFF64_LDFLAGS :: 9 + _CS_POSIX_V6_LP64_OFF64_LIBS :: 10 + _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS :: 11 + _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS :: 12 + _CS_POSIX_V6_LPBIG_OFFBIG_LIBS :: 13 + _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS :: 14 + + _PC_LINK_MAX :: 1 + _PC_MAX_CANON :: 2 + _PC_MAX_INPUT :: 3 + _PC_NAME_MAX :: 4 + _PC_PATH_MAX :: 5 + _PC_PIPE_BUF :: 6 + _PC_CHOWN_RESTRICTED :: 7 + _PC_NO_TRUNC :: 8 + _PC_VDISABLE :: 9 + _PC_2_SYMLINK :: 13 // NOTE: not in headers (freebsd) + _PC_ALLOC_SIZE_MIN :: 10 + _PC_ASYNC_IO :: 53 + _PC_FILESIZEBITS :: 12 + _PC_PRIO_IO :: 54 + _PC_REC_INCR_XFER_SIZE :: 14 + _PC_REC_MAX_XFER_SIZE :: 15 + _PC_REC_MIN_XFER_SIZE :: 16 + _PC_REC_XFER_ALIGN :: 17 + _PC_SYMLINK_MAX :: 18 + _PC_SYNC_IO :: 55 + + _SC_ARG_MAX :: 1 + _SC_CHILD_MAX :: 2 + _SC_CLK_TCK :: 3 + _SC_NGROUPS_MAX :: 4 + _SC_OPEN_MAX :: 5 + _SC_JOB_CONTROL :: 6 + _SC_SAVED_IDS :: 7 + _SC_VERSION :: 8 + _SC_BC_BASE_MAX :: 9 + + _SC_BC_DIM_MAX :: 10 + _SC_BC_SCALE_MAX :: 11 + _SC_BC_STRING_MAX :: 12 + _SC_COLL_WEIGHTS_MAX :: 13 + _SC_EXPR_NEST_MAX :: 14 + _SC_LINE_MAX :: 15 + _SC_RE_DUP_MAX :: 16 + _SC_2_VERSION :: 17 + _SC_2_C_BIND :: 18 + _SC_2_C_DEV :: 19 + + _SC_2_CHAR_TERM :: 20 + _SC_2_FORT_DEV :: 21 + _SC_2_FORT_RUN :: 22 + _SC_2_LOCALEDEF :: 23 + _SC_2_SW_DEV :: 24 + _SC_2_UPE :: 25 + _SC_STREAM_MAX :: 26 + _SC_TZNAME_MAX :: 27 + _SC_ASYNCHRONOUS_IO :: 28 + _SC_MAPPED_FILES :: 29 + + _SC_MEMLOCK :: 30 + _SC_MEMLOCK_RANGE :: 31 + _SC_MEMORY_PROTECTION :: 32 + _SC_MESSAGE_PASSING :: 33 + _SC_PRIORITIZED_IO :: 34 + _SC_PRIORITY_SCHEDULING :: 35 + _SC_REALTIME_SIGNALS :: 36 + _SC_SEMAPHORES :: 37 + _SC_FSYNC :: 38 + _SC_SHARED_MEMORY_OBJECTS :: 39 + + _SC_SYNCHRONIZED_IO :: 40 + _SC_TIMERS :: 41 + _SC_AIO_LISTIO_MAX :: 42 + _SC_AIO_MAX :: 43 + _SC_AIO_PRIO_DELTA_MAX :: 44 + _SC_DELAYTIMER_MAX :: 45 + _SC_MQ_OPEN_MAX :: 46 + _SC_PAGE_SIZE :: 47 + _SC_PAGESIZE :: _SC_PAGE_SIZE + _SC_RTSIG_MAX :: 48 + _SC_SEM_NSEMS_MAX :: 49 + + _SC_SEM_VALUE_MAX :: 50 + _SC_SIGQUEUE_MAX :: 51 + _SC_TIMER_MAX :: 52 + _SC_IOV_MAX :: 56 + _SC_2_PBS :: 59 + + _SC_2_PBS_ACCOUNTING :: 60 + _SC_2_PBS_CHECKPOINT :: 61 + _SC_2_PBS_LOCATE :: 62 + _SC_2_PBS_MESSAGE :: 63 + _SC_2_PBS_TRACK :: 64 + _SC_ADVISORY_INFO :: 65 + _SC_BARRIERS :: 66 + _SC_CLOCK_SELECTION :: 67 + _SC_CPUTIME :: 68 + + _SC_GETGR_R_SIZE_MAX :: 70 + _SC_GETPW_R_SIZE_MAX :: 71 + _SC_HOST_NAME_MAX :: 72 + _SC_LOGIN_NAME_MAX :: 73 + _SC_MONOTONIC_CLOCK :: 74 + _SC_MQ_PRIO_MAX :: 75 + _SC_READER_WRITER_LOCKS :: 76 + _SC_REGEXP :: 77 + _SC_SHELL :: 78 + _SC_SPAWN :: 79 + + _SC_SPIN_LOCKS :: 80 + _SC_SPORADIC_SERVER :: 81 + _SC_THREAD_ATTR_STACKADDR :: 82 + _SC_THREAD_ATTR_STACKSIZE :: 83 + _SC_THREAD_CPUTIME :: 84 + _SC_THREAD_DESTRUCTOR_ITERATIONS :: 85 + _SC_THREAD_KEYS_MAX :: 86 + _SC_THREAD_PRIO_INHERIT :: 87 + _SC_THREAD_PRIO_PROTECT :: 88 + _SC_THREAD_PRIORITY_SCHEDULING :: 89 + + _SC_THREAD_PROCESS_SHARED :: 90 + _SC_THREAD_SAFE_FUNCTIONS :: 91 + _SC_THREAD_SPORADIC_SERVER :: 92 + _SC_THREAD_STACK_MIN :: 93 + _SC_THREAD_THREADS_MAX :: 94 + _SC_TIMEOUTS :: 95 + _SC_THREADS :: 96 + _SC_TRACE :: 97 + _SC_TRACE_EVENT_FILTER :: 98 + _SC_TRACE_INHERIT :: 99 + + _SC_TRACE_LOG :: 100 + _SC_TTY_NAME_MAX :: 101 + _SC_TYPED_MEMORY_OBJECTS :: 102 + _SC_V6_ILP32_OFF32 :: 103 + _SC_V6_ILP32_OFFBIG :: 104 + _SC_V6_LP64_OFF64 :: 105 + _SC_V6_LPBIG_OFFBIG :: 106 + _SC_ATEXIT_MAX :: 107 + _SC_XOPEN_CRYPT :: 108 + _SC_XOPEN_ENH_I18N :: 109 + + _SC_XOPEN_REALTIME :: 111 + _SC_XOPEN_REALTIME_THREADS :: 112 + _SC_XOPEN_SHM :: 113 + _SC_XOPEN_STREAMS :: 114 + _SC_XOPEN_UNIX :: 115 + _SC_XOPEN_VERSION :: 116 + _SC_IPV6 :: 118 + _SC_RAW_SOCKETS :: 119 + + _SC_SYMLOOP_MAX :: 120 + _SC_SS_REPL_MAX :: 126 // NOTE: not in headers + _SC_TRACE_EVENT_NAME_MAX :: 127 // NOTE: not in headers + _SC_TRACE_NAME_MAX :: 128 // NOTE: not in headers + _SC_TRACE_SYS_MAX :: 129 // NOTE: not in headers + _SC_TRACE_USER_EVENT_MAX :: 130 // NOTE: not in headers + + _POSIX_VDISABLE :: 0xff + +} else when ODIN_OS == .NetBSD { + + _F_OK :: 0 + X_OK :: 0x01 + W_OK :: 0x02 + R_OK :: 0x04 + + F_LOCK :: 1 + F_TEST :: 3 + F_TLOCK :: 2 + F_ULOCK :: 0 + + _CS_PATH :: 1 + _CS_POSIX_V6_ILP32_OFF32_CFLAGS :: 2 + _CS_POSIX_V6_ILP32_OFF32_LDFLAGS :: 3 + _CS_POSIX_V6_ILP32_OFF32_LIBS :: 4 + _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS :: 5 + _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS :: 6 + _CS_POSIX_V6_ILP32_OFFBIG_LIBS :: 7 + _CS_POSIX_V6_LP64_OFF64_CFLAGS :: 8 + _CS_POSIX_V6_LP64_OFF64_LDFLAGS :: 9 + _CS_POSIX_V6_LP64_OFF64_LIBS :: 10 + _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS :: 11 + _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS :: 12 + _CS_POSIX_V6_LPBIG_OFFBIG_LIBS :: 13 + _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS :: 14 + + _PC_LINK_MAX :: 1 + _PC_MAX_CANON :: 2 + _PC_MAX_INPUT :: 3 + _PC_NAME_MAX :: 4 + _PC_PATH_MAX :: 5 + _PC_PIPE_BUF :: 6 + _PC_CHOWN_RESTRICTED :: 7 + _PC_NO_TRUNC :: 8 + _PC_VDISABLE :: 9 + _PC_2_SYMLINK :: 13 // NOTE: not in headers + _PC_ALLOC_SIZE_MIN :: 10 // NOTE: not in headers + _PC_ASYNC_IO :: 53 // NOTE: not in headers + _PC_FILESIZEBITS :: 11 + _PC_PRIO_IO :: 54 // NOTE: not in headers + _PC_REC_INCR_XFER_SIZE :: 14 // NOTE: not in headers + _PC_REC_MAX_XFER_SIZE :: 15 // NOTE: not in headers + _PC_REC_MIN_XFER_SIZE :: 16 // NOTE: not in headers + _PC_REC_XFER_ALIGN :: 17 // NOTE: not in headers + _PC_SYMLINK_MAX :: 12 + _PC_SYNC_IO :: 10 + + _SC_ARG_MAX :: 1 + _SC_CHILD_MAX :: 2 + _SC_NGROUPS_MAX :: 4 + _SC_OPEN_MAX :: 5 + _SC_JOB_CONTROL :: 6 + _SC_SAVED_IDS :: 7 + _SC_VERSION :: 8 + _SC_BC_BASE_MAX :: 9 + + _SC_BC_DIM_MAX :: 10 + _SC_BC_SCALE_MAX :: 11 + _SC_BC_STRING_MAX :: 12 + _SC_COLL_WEIGHTS_MAX :: 13 + _SC_EXPR_NEST_MAX :: 14 + _SC_LINE_MAX :: 15 + _SC_RE_DUP_MAX :: 16 + _SC_2_VERSION :: 17 + _SC_2_C_BIND :: 18 + _SC_2_C_DEV :: 19 + + _SC_2_CHAR_TERM :: 20 + _SC_2_FORT_DEV :: 21 + _SC_2_FORT_RUN :: 22 + _SC_2_LOCALEDEF :: 23 + _SC_2_SW_DEV :: 24 + _SC_2_UPE :: 25 + _SC_STREAM_MAX :: 26 + _SC_TZNAME_MAX :: 27 + _SC_PAGE_SIZE :: 28 + _SC_PAGESIZE :: _SC_PAGE_SIZE + _SC_FSYNC :: 29 + + _SC_XOPEN_SHM :: 30 + _SC_SYNCHRONIZED_IO :: 31 + _SC_IOV_MAX :: 32 + _SC_MAPPED_FILES :: 33 + _SC_MEMLOCK :: 34 + _SC_MEMLOCK_RANGE :: 35 + _SC_MEMORY_PROTECTION :: 36 + _SC_LOGIN_NAME_MAX :: 37 + _SC_MONOTONIC_CLOCK :: 38 + _SC_CLK_TCK :: 39 + + _SC_ATEXIT_MAX :: 40 + _SC_THREADS :: 41 + _SC_SEMAPHORES :: 42 + _SC_BARRIERS :: 43 + _SC_TIMERS :: 44 + _SC_SPIN_LOCKS :: 45 + _SC_READER_WRITER_LOCKS :: 46 + _SC_GETGR_R_SIZE_MAX :: 47 + _SC_GETPW_R_SIZE_MAX :: 48 + _SC_CLOCK_SELECTION :: 49 + + _SC_ASYNCHRONOUS_IO :: 50 + _SC_AIO_LISTIO_MAX :: 51 + _SC_AIO_MAX :: 52 + _SC_MESSAGE_PASSING :: 53 + _SC_MQ_OPEN_MAX :: 54 + _SC_MQ_PRIO_MAX :: 55 + _SC_PRIORITY_SCHEDULING :: 56 + _SC_THREAD_DESTRUCTOR_ITERATIONS :: 57 + _SC_THREAD_KEYS_MAX :: 58 + _SC_THREAD_STACK_MIN :: 59 + + _SC_THREAD_THREADS_MAX :: 60 + _SC_THREAD_ATTR_STACKADDR :: 61 + _SC_THREAD_ATTR_STACKSIZE :: 62 + _SC_THREAD_PRIORITY_SCHEDULING :: 63 + _SC_THREAD_PRIO_INHERIT :: 64 + _SC_THREAD_PRIO_PROTECT :: 65 + _SC_THREAD_PROCESS_SHARED :: 66 + _SC_THREAD_SAFE_FUNCTIONS :: 67 + _SC_TTY_NAME_MAX :: 68 + _SC_HOST_NAME_MAX :: 69 + + _SC_PASS_MAX :: 70 + _SC_REGEXP :: 71 + _SC_SHELL :: 72 + _SC_SYMLOOP_MAX :: 73 + _SC_V6_ILP32_OFF32 :: 74 + _SC_V6_ILP32_OFFBIG :: 75 + _SC_V6_LP64_OFF64 :: 76 + _SC_V6_LPBIG_OFFBIG :: 77 + + _SC_2_PBS :: 80 + _SC_2_PBS_ACCOUNTING :: 81 + _SC_2_PBS_CHECKPOINT :: 82 + _SC_2_PBS_LOCATE :: 83 + _SC_2_PBS_MESSAGE :: 84 + _SC_2_PBS_TRACK :: 85 + _SC_SPAWN :: 86 + _SC_SHARED_MEMORY_OBJECTS :: 87 + _SC_TIMER_MAX :: 88 + _SC_SEM_NSEMS_MAX :: 89 + + _SC_CPUTIME :: 90 + _SC_THREAD_CPUTIME :: 91 + _SC_DELAYTIMER_MAX :: 92 + _SC_SIGQUEUE_MAX :: 93 + _SC_REALTIME_SIGNALS :: 94 + _SC_RTSIG_MAX :: 95 + + _POSIX_VDISABLE :: '\377' + + // NOTE: following are not defined in netbsd headers. + + _SC_SPORADIC_SERVER :: 81 + _SC_SEM_VALUE_MAX :: 50 + + _SC_TRACE :: 97 + _SC_TRACE_EVENT_FILTER :: 98 + _SC_TRACE_INHERIT :: 99 + _SC_TRACE_LOG :: 100 + _SC_TYPED_MEMORY_OBJECTS :: 102 + + _SC_THREAD_SPORADIC_SERVER :: 92 + _SC_TIMEOUTS :: 95 + + _SC_XOPEN_CRYPT :: 108 + _SC_XOPEN_ENH_I18N :: 109 + _SC_XOPEN_REALTIME :: 111 + _SC_XOPEN_REALTIME_THREADS :: 112 + _SC_XOPEN_STREAMS :: 114 + _SC_XOPEN_UNIX :: 115 + _SC_XOPEN_VERSION :: 116 + _SC_IPV6 :: 118 + _SC_RAW_SOCKETS :: 119 + + _SC_PRIORITIZED_IO :: 34 + _SC_AIO_PRIO_DELTA_MAX :: 44 + _SC_ADVISORY_INFO :: 65 + _SC_SS_REPL_MAX :: 126 + _SC_TRACE_EVENT_NAME_MAX :: 127 + _SC_TRACE_NAME_MAX :: 128 + _SC_TRACE_SYS_MAX :: 129 + _SC_TRACE_USER_EVENT_MAX :: 130 + +} else when ODIN_OS == .OpenBSD { + + _F_OK :: 0 + X_OK :: 0x01 + W_OK :: 0x02 + R_OK :: 0x04 + + F_LOCK :: 1 + F_TEST :: 3 + F_TLOCK :: 2 + F_ULOCK :: 0 + + _CS_PATH :: 1 + _CS_POSIX_V6_ILP32_OFF32_CFLAGS :: 2 + _CS_POSIX_V6_ILP32_OFF32_LDFLAGS :: 3 + _CS_POSIX_V6_ILP32_OFF32_LIBS :: 4 + _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS :: 5 + _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS :: 6 + _CS_POSIX_V6_ILP32_OFFBIG_LIBS :: 7 + _CS_POSIX_V6_LP64_OFF64_CFLAGS :: 8 + _CS_POSIX_V6_LP64_OFF64_LDFLAGS :: 9 + _CS_POSIX_V6_LP64_OFF64_LIBS :: 10 + _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS :: 11 + _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS :: 12 + _CS_POSIX_V6_LPBIG_OFFBIG_LIBS :: 13 + _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS :: 14 + + _PC_LINK_MAX :: 1 + _PC_MAX_CANON :: 2 + _PC_MAX_INPUT :: 3 + _PC_NAME_MAX :: 4 + _PC_PATH_MAX :: 5 + _PC_PIPE_BUF :: 6 + _PC_CHOWN_RESTRICTED :: 7 + _PC_NO_TRUNC :: 8 + _PC_VDISABLE :: 9 + _PC_2_SYMLINK :: 10 + _PC_ALLOC_SIZE_MIN :: 11 + _PC_ASYNC_IO :: 12 + _PC_FILESIZEBITS :: 13 + _PC_PRIO_IO :: 14 + _PC_REC_INCR_XFER_SIZE :: 15 + _PC_REC_MAX_XFER_SIZE :: 16 + _PC_REC_MIN_XFER_SIZE :: 17 + _PC_REC_XFER_ALIGN :: 18 + _PC_SYMLINK_MAX :: 19 + _PC_SYNC_IO :: 20 + + _SC_ARG_MAX :: 1 + _SC_CHILD_MAX :: 2 + _SC_CLK_TCK :: 3 + _SC_NGROUPS_MAX :: 4 + _SC_OPEN_MAX :: 5 + _SC_JOB_CONTROL :: 6 + _SC_SAVED_IDS :: 7 + _SC_VERSION :: 8 + _SC_BC_BASE_MAX :: 9 + + _SC_BC_DIM_MAX :: 10 + _SC_BC_SCALE_MAX :: 11 + _SC_BC_STRING_MAX :: 12 + _SC_COLL_WEIGHTS_MAX :: 13 + _SC_EXPR_NEST_MAX :: 14 + _SC_LINE_MAX :: 15 + _SC_RE_DUP_MAX :: 16 + _SC_2_VERSION :: 17 + _SC_2_C_BIND :: 18 + _SC_2_C_DEV :: 19 + + _SC_2_CHAR_TERM :: 20 + _SC_2_FORT_DEV :: 21 + _SC_2_FORT_RUN :: 22 + _SC_2_LOCALEDEF :: 23 + _SC_2_SW_DEV :: 24 + _SC_2_UPE :: 25 + _SC_STREAM_MAX :: 26 + _SC_TZNAME_MAX :: 27 + _SC_PAGESIZE :: 28 + _SC_PAGE_SIZE :: _SC_PAGESIZE + _SC_FSYNC :: 29 + + _SC_XOPEN_SHM :: 30 + _SC_SEM_NSEMS_MAX :: 31 + _SC_SEM_VALUE_MAX :: 32 + _SC_HOST_NAME_MAX :: 33 + _SC_MONOTONIC_CLOCK :: 34 + _SC_2_PBS :: 35 + _SC_2_PBS_ACCOUNTING :: 36 + _SC_2_PBS_CHECKPOINT :: 37 + _SC_2_PBS_LOCATE :: 38 + _SC_2_PBS_MESSAGE :: 39 + + _SC_2_PBS_TRACK :: 40 + _SC_ADVISORY_INFO :: 41 + _SC_AIO_LISTIO_MAX :: 42 + _SC_AIO_MAX :: 43 + _SC_AIO_PRIO_DELTA_MAX :: 44 + _SC_ASYNCHRONOUS_IO :: 45 + _SC_ATEXIT_MAX :: 46 + _SC_BARRIERS :: 47 + _SC_CLOCK_SELECTION :: 48 + _SC_CPUTIME :: 49 + + _SC_DELAYTIMER_MAX :: 50 + _SC_IOV_MAX :: 51 + _SC_IPV6 :: 52 + _SC_MAPPED_FILES :: 53 + _SC_MEMLOCK :: 54 + _SC_MEMLOCK_RANGE :: 55 + _SC_MEMORY_PROTECTION :: 56 + _SC_MESSAGE_PASSING :: 57 + _SC_MQ_OPEN_MAX :: 58 + _SC_MQ_PRIO_MAX :: 59 + + _SC_PRIORITIZED_IO :: 60 + _SC_PRIORITY_SCHEDULING :: 61 + _SC_RAW_SOCKETS :: 62 + _SC_READER_WRITER_LOCKS :: 63 + _SC_REALTIME_SIGNALS :: 64 + _SC_REGEXP :: 65 + _SC_RTSIG_MAX :: 66 + _SC_SEMAPHORES :: 67 + _SC_SHARED_MEMORY_OBJECTS :: 68 + _SC_SHELL :: 69 + + _SC_SIGQUEUE_MAX :: 70 + _SC_SPAWN :: 71 + _SC_SPIN_LOCKS :: 72 + _SC_SPORADIC_SERVER :: 73 + _SC_SS_REPL_MAX :: 74 + _SC_SYNCHRONIZED_IO :: 75 + _SC_SYMLOOP_MAX :: 76 + _SC_THREAD_ATTR_STACKADDR :: 77 + _SC_THREAD_ATTR_STACKSIZE :: 78 + _SC_THREAD_CPUTIME :: 79 + + _SC_THREAD_DESTRUCTOR_ITERATIONS :: 80 + _SC_THREAD_KEYS_MAX :: 81 + _SC_THREAD_PRIO_INHERIT :: 82 + _SC_THREAD_PRIO_PROTECT :: 83 + _SC_THREAD_PRIORITY_SCHEDULING :: 84 + _SC_THREAD_PROCESS_SHARED :: 85 + _SC_THREAD_ROBUST_PRIO_INHERIT :: 86 + _SC_THREAD_ROBUST_PRIO_PROTECT :: 87 + _SC_THREAD_SPORADIC_SERVER :: 88 + _SC_THREAD_STACK_MIN :: 89 + + _SC_THREAD_THREADS_MAX :: 90 + _SC_THREADS :: 91 + _SC_TIMEOUTS :: 92 + _SC_TIMER_MAX :: 93 + _SC_TIMERS :: 94 + _SC_TRACE :: 95 + _SC_TRACE_EVENT_FILTER :: 96 + _SC_TRACE_EVENT_NAME_MAX :: 97 + _SC_TRACE_INHERIT :: 98 + _SC_TRACE_LOG :: 99 + + _SC_GETGR_R_SIZE_MAX :: 100 + _SC_GETPW_R_SIZE_MAX :: 101 + _SC_LOGIN_NAME_MAX :: 102 + _SC_THREAD_SAFE_FUNCTIONS :: 103 + _SC_TRACE_NAME_MAX :: 104 + _SC_TRACE_SYS_MAX :: 105 + _SC_TRACE_USER_EVENT_MAX :: 106 + _SC_TTY_NAME_MAX :: 107 + _SC_TYPED_MEMORY_OBJECTS :: 108 + _SC_V6_ILP32_OFF32 :: 109 + + _SC_V6_ILP32_OFFBIG :: 110 + _SC_V6_LP64_OFF64 :: 111 + _SC_V6_LPBIG_OFFBIG :: 112 + _SC_V7_ILP32_OFF32 :: 113 + _SC_V7_ILP32_OFFBIG :: 114 + _SC_V7_LP64_OFF64 :: 115 + _SC_V7_LPBIG_OFFBIG :: 116 + _SC_XOPEN_CRYPT :: 117 + _SC_XOPEN_ENH_I18N :: 118 + _SC_XOPEN_LEGACY :: 119 + + _SC_XOPEN_REALTIME :: 120 + _SC_XOPEN_REALTIME_THREADS :: 121 + _SC_XOPEN_STREAMS :: 122 + _SC_XOPEN_UNIX :: 123 + _SC_XOPEN_UUCP :: 124 + _SC_XOPEN_VERSION :: 125 + + _SC_PHYS_PAGES :: 500 + _SC_AVPHYS_PAGES :: 501 + _SC_NPROCESSORS_CONF :: 502 + _SC_NPROCESSORS_ONLN :: 503 + + _POSIX_VDISABLE :: '\377' + +} else { + #panic("posix is unimplemented for the current target") +} + diff --git a/core/sys/posix/utime.odin b/core/sys/posix/utime.odin new file mode 100644 index 000000000..591a6db06 --- /dev/null +++ b/core/sys/posix/utime.odin @@ -0,0 +1,36 @@ +package posix + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// utime.h - access and modification time structure + +foreign lib { + /* + Set file access and modification times. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/utime.html ]] + */ + @(link_name=LUTIME) + utime :: proc(path: cstring, times: ^utimbuf) -> result --- +} + +when ODIN_OS == .NetBSD { + @(private) LUTIME :: "__utime50" +} else { + @(private) LUTIME :: "utime" +} + +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + + utimbuf :: struct { + actime: time_t, /* [PSX] access time (seconds since epoch) */ + modtime: time_t, /* [PSX] modification time (seconds since epoch) */ + } + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/posix/wordexp.odin b/core/sys/posix/wordexp.odin new file mode 100644 index 000000000..d730db0f7 --- /dev/null +++ b/core/sys/posix/wordexp.odin @@ -0,0 +1,107 @@ +package posix + +import "core:c" + +when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// wordexp.h - word-expansion type + +foreign lib { + /* + Perform word expansion. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/wordexp.html ]] + */ + wordexp :: proc(words: cstring, pwordexp: ^wordexp_t, flags: WRDE_Flags) -> WRDE_Errno --- + + /* + Free the space allocated during word expansion. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/wordexp.html ]] + */ + wordfree :: proc(pwordexp: ^wordexp_t) --- +} + +WRDE_Flag_Bits :: enum c.int { + // Appends words to those previously generated. + APPEND = log2(WRDE_APPEND), + // Number of null pointers to prepend to we_wordv. + DOOFFS = log2(WRDE_DOOFFS), + // Fail if command substitution is requested. + NOCMD = log2(WRDE_NOCMD), + // The pwordexp argument was passed to a previous successful call to wordexp(), + // and has not been passed to wordfree(). + REUSE = log2(WRDE_REUSE), + // Do not redirect stderr to /dev/null. + SHOWERR = log2(WRDE_SHOWERR), + // Report error on attempt to expand an undefined shell variable. + UNDEF = log2(WRDE_UNDEF), +} +WRDE_Flags :: bit_set[WRDE_Flag_Bits; c.int] + +WRDE_Errno :: enum c.int { + OK = 0, + // One of the unquoted characters- , '|', '&', ';', '<', '>', '(', ')', '{', '}' - + // appears in words in an inappropriate context. + BADCHAR = WRDE_BADCHAR, + // Reference to undefined shell variable when WRDE_UNDEF is set in flags. + BADVAL = WRDE_BADVAL, + // Command substitution requested when WRDE_NOCMD was set in flags. + CMDSUB = WRDE_CMDSUB, + // Attempt to allocate memory failed. + NOSPACE = WRDE_NOSPACE, + // Shell syntax error, such as unbalanced parentheses or an unterminated string. + SYNTAX = WRDE_SYNTAX, +} + +when ODIN_OS == .Darwin { + + wordexp_t :: struct { + we_wordc: c.size_t, /* [PSX] count of words matched by words */ + we_wordv: [^]cstring, /* [PSX] pointer to list of expanded words */ + we_offs: c.size_t, /* [PSX] slots to reserve at the beginning of we_wordv */ + } + + WRDE_APPEND :: 0x01 + WRDE_DOOFFS :: 0x02 + WRDE_NOCMD :: 0x04 + WRDE_REUSE :: 0x08 + WRDE_SHOWERR :: 0x10 + WRDE_UNDEF :: 0x20 + + WRDE_BADCHAR :: 1 + WRDE_BADVAL :: 2 + WRDE_CMDSUB :: 3 + WRDE_NOSPACE :: 4 + WRDE_SYNTAX :: 6 + +} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + + wordexp_t :: struct { + we_wordc: c.size_t, /* [PSX] count of words matched by words */ + we_wordv: [^]cstring, /* [PSX] pointer to list of expanded words */ + we_offs: c.size_t, /* [PSX] slots to reserve at the beginning of we_wordv */ + we_strings: [^]byte, /* storage for wordv strings */ + we_nbytes: c.size_t, /* size of we_strings */ + } + + WRDE_APPEND :: 0x01 + WRDE_DOOFFS :: 0x02 + WRDE_NOCMD :: 0x04 + WRDE_REUSE :: 0x08 + WRDE_SHOWERR :: 0x10 + WRDE_UNDEF :: 0x20 + + WRDE_BADCHAR :: 1 + WRDE_BADVAL :: 2 + WRDE_CMDSUB :: 3 + WRDE_NOSPACE :: 4 + WRDE_SYNTAX :: 6 + +} else { + #panic("posix is unimplemented for the current target") +} diff --git a/core/sys/unix/pthread_netbsd.odin b/core/sys/unix/pthread_netbsd.odin index afbbc321c..9107f1139 100644 --- a/core/sys/unix/pthread_netbsd.odin +++ b/core/sys/unix/pthread_netbsd.odin @@ -2,7 +2,7 @@ package unix import "core:c" -pthread_t :: distinct u64 +pthread_t :: distinct rawptr SEM_T_SIZE :: 8 diff --git a/core/sys/unix/pthread_unix.odin b/core/sys/unix/pthread_unix.odin index c876a214a..68a0859b4 100644 --- a/core/sys/unix/pthread_unix.odin +++ b/core/sys/unix/pthread_unix.odin @@ -5,6 +5,11 @@ foreign import "system:pthread" import "core:c" +timespec :: struct { + tv_sec: i64, + tv_nsec: i64, +} + // // On success, these functions return 0. // diff --git a/core/sys/unix/time_unix.odin b/core/sys/unix/time_unix.odin deleted file mode 100644 index 442202d36..000000000 --- a/core/sys/unix/time_unix.odin +++ /dev/null @@ -1,83 +0,0 @@ -//+build linux, darwin, freebsd, openbsd, netbsd, haiku -package unix - -when ODIN_OS == .Darwin { - foreign import libc "system:System.framework" -} else { - foreign import libc "system:c" -} - -import "core:c" - -when ODIN_OS == .NetBSD { - @(default_calling_convention="c") - foreign libc { - @(link_name="__clock_gettime50") clock_gettime :: proc(clock_id: u64, timespec: ^timespec) -> c.int --- - @(link_name="__nanosleep50") nanosleep :: proc(requested, remaining: ^timespec) -> c.int --- - @(link_name="sleep") sleep :: proc(seconds: c.uint) -> c.int --- - } -} else { - @(default_calling_convention="c") - foreign libc { - clock_gettime :: proc(clock_id: u64, timespec: ^timespec) -> c.int --- - sleep :: proc(seconds: c.uint) -> c.int --- - nanosleep :: proc(requested, remaining: ^timespec) -> c.int --- - } -} - -timespec :: struct { - tv_sec: i64, // seconds - tv_nsec: i64, // nanoseconds -} - -when ODIN_OS == .OpenBSD { - CLOCK_REALTIME :: 0 - CLOCK_PROCESS_CPUTIME_ID :: 2 - CLOCK_MONOTONIC :: 3 - CLOCK_THREAD_CPUTIME_ID :: 4 - CLOCK_UPTIME :: 5 - CLOCK_BOOTTIME :: 6 - - // CLOCK_MONOTONIC_RAW doesn't exist, use CLOCK_MONOTONIC - CLOCK_MONOTONIC_RAW :: CLOCK_MONOTONIC -} else { - CLOCK_REALTIME :: 0 // NOTE(tetra): May jump in time, when user changes the system time. - CLOCK_MONOTONIC :: 1 // NOTE(tetra): May stand still while system is asleep. - CLOCK_PROCESS_CPUTIME_ID :: 2 - CLOCK_THREAD_CPUTIME_ID :: 3 - CLOCK_MONOTONIC_RAW :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP. - CLOCK_REALTIME_COARSE :: 5 // NOTE(tetra): "COARSE" clocks are apparently much faster, but not "fine-grained." - CLOCK_MONOTONIC_COARSE :: 6 - CLOCK_BOOTTIME :: 7 // NOTE(tetra): Same as MONOTONIC, except also including time system was asleep. - CLOCK_REALTIME_ALARM :: 8 - CLOCK_BOOTTIME_ALARM :: 9 -} - -// TODO(tetra, 2019-11-05): The original implementation of this package for Darwin used this constants. -// I do not know if Darwin programmers are used to the existance of these constants or not, so -// I'm leaving aliases to them for now. -CLOCK_SYSTEM :: CLOCK_REALTIME -CLOCK_CALENDAR :: CLOCK_MONOTONIC - -boot_time_in_nanoseconds :: proc "c" () -> i64 { - ts_now, ts_boottime: timespec - clock_gettime(CLOCK_REALTIME, &ts_now) - clock_gettime(CLOCK_BOOTTIME, &ts_boottime) - - ns := (ts_now.tv_sec - ts_boottime.tv_sec) * 1e9 + ts_now.tv_nsec - ts_boottime.tv_nsec - return i64(ns) -} - -seconds_since_boot :: proc "c" () -> f64 { - ts_boottime: timespec - clock_gettime(CLOCK_BOOTTIME, &ts_boottime) - return f64(ts_boottime.tv_sec) + f64(ts_boottime.tv_nsec) / 1e9 -} - -inline_nanosleep :: proc "c" (nanoseconds: i64) -> (remaining: timespec, res: i32) { - s, ns := nanoseconds / 1e9, nanoseconds % 1e9 - requested := timespec{tv_sec=s, tv_nsec=ns} - res = nanosleep(&requested, &remaining) - return -} - diff --git a/core/time/time_linux.odin b/core/time/time_linux.odin new file mode 100644 index 000000000..649f601dc --- /dev/null +++ b/core/time/time_linux.odin @@ -0,0 +1,38 @@ +package time + +import "core:sys/linux" + +_IS_SUPPORTED :: true + +_now :: proc "contextless" () -> Time { + time_spec_now, _ := linux.clock_gettime(.REALTIME) + ns := time_spec_now.time_sec * 1e9 + time_spec_now.time_nsec + return Time{_nsec=i64(ns)} +} + +_sleep :: proc "contextless" (d: Duration) { + ds := duration_seconds(d) + seconds := uint(ds) + nanoseconds := uint((ds - f64(seconds)) * 1e9) + + ts := linux.Time_Spec{ + time_sec = seconds, + time_nsec = nanoseconds, + } + + for { + if linux.nanosleep(&ts, &ts) != .EINTR { + break + } + } +} + +_tick_now :: proc "contextless" () -> Tick { + t, _ := linux.clock_gettime(.MONOTONIC_RAW) + return Tick{_nsec = i64(t.time_sec*1e9 + t.time_nsec)} +} + +_yield :: proc "contextless" () { + linux.sched_yield() +} + diff --git a/core/time/time_unix.odin b/core/time/time_unix.odin index f3e0d6640..61543b99c 100644 --- a/core/time/time_unix.odin +++ b/core/time/time_unix.odin @@ -1,34 +1,50 @@ //+private -//+build linux, darwin, freebsd, openbsd, netbsd, haiku +//+build darwin, freebsd, openbsd, netbsd, haiku package time -import "core:sys/unix" +import "core:sys/posix" -_IS_SUPPORTED :: true // NOTE: Times on Darwin are UTC. +_IS_SUPPORTED :: true _now :: proc "contextless" () -> Time { - time_spec_now: unix.timespec - unix.clock_gettime(unix.CLOCK_REALTIME, &time_spec_now) - ns := time_spec_now.tv_sec * 1e9 + time_spec_now.tv_nsec + time_spec_now: posix.timespec + posix.clock_gettime(.REALTIME, &time_spec_now) + ns := i64(time_spec_now.tv_sec) * 1e9 + time_spec_now.tv_nsec return Time{_nsec=ns} } _sleep :: proc "contextless" (d: Duration) { ds := duration_seconds(d) - seconds := u32(ds) + seconds := posix.time_t(ds) nanoseconds := i64((ds - f64(seconds)) * 1e9) - if seconds > 0 { unix.sleep(seconds) } - if nanoseconds > 0 { unix.inline_nanosleep(nanoseconds) } + ts := posix.timespec{ + tv_sec = seconds, + tv_nsec = nanoseconds, + } + + for { + res := posix.nanosleep(&ts, &ts) + if res == .OK || posix.errno() != .EINTR { + break + } + } +} + +when ODIN_OS == .Darwin { + TICK_CLOCK :: posix.Clock(4) // CLOCK_MONOTONIC_RAW +} else { + // It looks like the BSDs don't have a CLOCK_MONOTONIC_RAW equivalent. + TICK_CLOCK :: posix.Clock.MONOTONIC } _tick_now :: proc "contextless" () -> Tick { - t: unix.timespec - unix.clock_gettime(unix.CLOCK_MONOTONIC_RAW, &t) - return Tick{_nsec = t.tv_sec*1e9 + t.tv_nsec} + t: posix.timespec + posix.clock_gettime(TICK_CLOCK, &t) + return Tick{_nsec = i64(t.tv_sec)*1e9 + t.tv_nsec} } _yield :: proc "contextless" () { - unix.sched_yield() + posix.sched_yield() } diff --git a/examples/all/all_posix.odin b/examples/all/all_posix.odin new file mode 100644 index 000000000..819dd6dd3 --- /dev/null +++ b/examples/all/all_posix.odin @@ -0,0 +1,6 @@ +//+build darwin, openbsd, freebsd, netbsd +package all + +import posix "core:sys/posix" + +_ :: posix diff --git a/src/big_int.cpp b/src/big_int.cpp index e350687b4..83235483c 100644 --- a/src/big_int.cpp +++ b/src/big_int.cpp @@ -621,3 +621,7 @@ gb_internal String big_int_to_string(gbAllocator allocator, BigInt const *x, u64 } return make_string(cast(u8 *)buf.data, buf.count); } + +gb_internal int big_int_log2(BigInt const *x) { + return mp_count_bits(x) - 1; +} diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index bde102a8d..e5282f63e 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -3979,6 +3979,23 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As break; } + case BuiltinProc_constant_log2: { + Operand o = {}; + check_expr(c, &o, ce->args[0]); + + if (!is_type_integer(o.type) && (o.mode != Addressing_Constant)) { + error(ce->args[0], "Expected a constant integer for '%.*s'", LIT(builtin_name)); + return false; + } + + int log2 = big_int_log2(&o.value.value_integer); + + operand->mode = Addressing_Constant; + operand->value = exact_value_i64(cast(i64)log2); + operand->type = t_untyped_integer; + break; + } + case BuiltinProc_soa_struct: { Operand x = {}; Operand y = {}; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 1425aafa8..0d4b47037 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -756,13 +756,29 @@ gb_internal bool are_signatures_similar_enough(Type *a_, Type *b_) { for (isize i = 0; i < a->param_count; i++) { Type *x = core_type(a->params->Tuple.variables[i]->type); Type *y = core_type(b->params->Tuple.variables[i]->type); + + if (x->kind == Type_BitSet && x->BitSet.underlying) { + x = core_type(x->BitSet.underlying); + } + if (y->kind == Type_BitSet && y->BitSet.underlying) { + y = core_type(y->BitSet.underlying); + } + if (!signature_parameter_similar_enough(x, y)) { return false; } } for (isize i = 0; i < a->result_count; i++) { - Type *x = base_type(a->results->Tuple.variables[i]->type); - Type *y = base_type(b->results->Tuple.variables[i]->type); + Type *x = core_type(a->results->Tuple.variables[i]->type); + Type *y = core_type(b->results->Tuple.variables[i]->type); + + if (x->kind == Type_BitSet && x->BitSet.underlying) { + x = core_type(x->BitSet.underlying); + } + if (y->kind == Type_BitSet && y->BitSet.underlying) { + y = core_type(y->BitSet.underlying); + } + if (!signature_parameter_similar_enough(x, y)) { return false; } diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 6245dadaf..ef07938c7 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -46,6 +46,8 @@ enum BuiltinProcId { BuiltinProc_has_target_feature, + BuiltinProc_constant_log2, + BuiltinProc_transpose, BuiltinProc_outer_product, BuiltinProc_hadamard_product, @@ -380,6 +382,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("has_target_feature"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("constant_log2"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("transpose"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("outer_product"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("hadamard_product"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/tests/core/normal.odin b/tests/core/normal.odin index e35d86598..45c66ed8d 100644 --- a/tests/core/normal.odin +++ b/tests/core/normal.odin @@ -38,6 +38,7 @@ download_assets :: proc() { @(require) import "slice" @(require) import "strconv" @(require) import "strings" +@(require) import "sys/posix" @(require) import "sys/windows" @(require) import "text/i18n" @(require) import "text/match" diff --git a/tests/core/sys/posix/posix.odin b/tests/core/sys/posix/posix.odin new file mode 100644 index 000000000..1942bfbda --- /dev/null +++ b/tests/core/sys/posix/posix.odin @@ -0,0 +1,211 @@ +//+build darwin, freebsd, openbsd, netbsd +package tests_core_posix + +import "core:sys/posix" +import "core:testing" +import "core:log" +import "core:strings" +import "core:path/filepath" + +@(test) +test_arpa_inet :: proc(t: ^testing.T) { + + check :: proc(t: ^testing.T, $af: posix.AF, src: cstring, expect: posix.pton_result, loc := #caller_location) { + when af == .INET { + addr: posix.in_addr + dst: [posix.INET_ADDRSTRLEN]byte + } else { + addr: posix.in6_addr + dst: [posix.INET6_ADDRSTRLEN]byte + } + + res := posix.inet_pton(af, src, &addr, size_of(addr)) + testing.expect_value(t, res, expect, loc) + + if expect == .SUCCESS { + back := posix.inet_ntop(af, &addr, raw_data(dst[:]), len(dst)) + testing.expect_value(t, back, src, loc) + + when af == .INET { + back = posix.inet_ntoa(addr) + testing.expect_value(t, back, src, loc) + } + } + } + + check(t, .INET, "127.0.0.1", .SUCCESS) + check(t, .INET, "blah", .INVALID) + check(t, .INET6, "::1", .SUCCESS) + check(t, .INET6, "L", .INVALID) + check(t, .UNIX, "127.0.0.1", .AFNOSUPPORT) +} + +@(test) +test_dirent :: proc(t: ^testing.T) { + test := #load_directory(#directory) + test_map: map[string]struct{} + defer delete(test_map) + + test_map[".."] = {} + test_map["."] = {} + + for file in test { + test_map[filepath.base(file.name)] = {} + } + + { + list: [^]^posix.dirent + ret := posix.scandir(#directory, &list) + testing.expectf(t, ret >= 0, "%v >= 0: %v", ret, posix.strerror(posix.errno())) + defer posix.free(list) + + entries := list[:ret] + for entry in entries { + defer posix.free(entry) + + if entry.d_type != .REG { + continue + } + + name := string(cstring(raw_data(entry.d_name[:]))) + testing.expectf(t, name in test_map, "%v in %v", name, test_map) + } + } + + { + dir := posix.opendir(#directory) + defer posix.closedir(dir) + + for { + posix.set_errno(.NONE) + entry := posix.readdir(dir) + if entry == nil { + testing.expect_value(t, posix.errno(), posix.Errno.NONE) + break + } + + if entry.d_type != .REG { + continue + } + + name := string(cstring(raw_data(entry.d_name[:]))) + testing.expectf(t, name in test_map, "%v in %v", name, test_map) + } + } +} + +@(test) +test_errno :: proc(t: ^testing.T) { + posix.errno(posix.Errno.ENOMEM) + testing.expect_value(t, posix.errno(), posix.Errno.ENOMEM) + + res := posix.open("", {}) + testing.expect_value(t, res, -1) + testing.expect_value(t, posix.errno(), posix.Errno.ENOENT) +} + +@(test) +test_fcntl :: proc(t: ^testing.T) { + res := posix.open(#file, { .WRONLY, .CREAT, .EXCL }) + testing.expect_value(t, res, -1) + testing.expect_value(t, posix.errno(), posix.Errno.EEXIST) +} + +@(test) +test_fnmatch :: proc(t: ^testing.T) { + testing.expect_value(t, posix.fnmatch("*.odin", #file, {}), 0) + testing.expect_value(t, posix.fnmatch("*.txt", #file, {}), posix.FNM_NOMATCH) + testing.expect_value(t, posix.fnmatch("**/*.odin", #file, {}), 0) +} + +@(test) +test_glob :: proc(t: ^testing.T) { + glob: posix.glob_t + res := posix.glob(#directory + ":)))))))", {}, nil, &glob) + testing.expect_value(t, res, posix.Glob_Result.NOMATCH) + posix.globfree(&glob) +} + +@(test) +test_langinfo :: proc(t: ^testing.T) { + locale := posix.setlocale(.TIME, nil) + testing.expectf(t, locale == "POSIX" || locale == "C", "invalid locale for test: %v", locale) + + day1 := posix.nl_langinfo(.DAY_1) + testing.expect_value(t, day1, "Sunday") +} + +@(test) +test_libgen :: proc(t: ^testing.T) { + tests := [][3]cstring{ + { "usr", ".", "usr" }, + { "usr/", ".", "usr" }, + { "", ".", "." }, + { "/", "/", "/" }, + { "//", "/", "/" }, + { "///", "/", "/" }, + { "/usr/", "/", "usr" }, + { "/usr/lib", "/usr", "lib" }, + { "//usr//lib//", "//usr", "lib" }, + { "/home//dwc//test", "/home//dwc", "test" }, + } + + for test in tests { + // NOTE: dir/basename can change their input so they can't be literals. + + dinput := strings.clone_to_cstring(string(test[0])) + defer delete(dinput) + + dir := posix.dirname(dinput) + testing.expectf(t, dir == test[1], "dirname(%q) == %q, expected %q", test[0], dir, test[1]) + + binput := strings.clone_to_cstring(string(test[0])) + defer delete(binput) + + base := posix.basename(binput) + testing.expectf(t, base == test[2], "basename(%q) == %q, expected %q", test[0], base, test[2]) + } +} + +@(test) +test_locale :: proc(t: ^testing.T) { + lconv := posix.localeconv() + testing.expect(t, lconv != nil) + + locale := posix.setlocale(.ALL, nil) + testing.expectf(t, locale == "POSIX" || locale == "C", "%q is not POSIX or C", locale) +} + +@(test) +test_monetary :: proc(t: ^testing.T) { + when ODIN_OS == .Darwin && .Address in ODIN_SANITIZER_FLAGS { + log.warn("skipping on darwin with -sanitize:address, this fails inside macOS (also from C/clang)") + return + } + + value := 123456.789 + buf: [128]byte + size := posix.strfmon(raw_data(buf[:]), len(buf), "%n", value) + testing.expectf(t, int(size) != -1, "strfmon failure: %v", posix.strerror(posix.errno())) + log.debug(string(buf[:size])) +} + +@(test) +test_stat :: proc(t: ^testing.T) { + testing.expect_value(t, posix.S_IRWXU, transmute(posix.mode_t)posix._mode_t(posix._S_IRWXU)) + testing.expect_value(t, posix.S_IRWXG, transmute(posix.mode_t)posix._mode_t(posix._S_IRWXG)) + testing.expect_value(t, posix.S_IRWXO, transmute(posix.mode_t)posix._mode_t(posix._S_IRWXO)) + testing.expect_value(t, posix._S_IFMT, transmute(posix.mode_t)posix._mode_t(posix.__S_IFMT)) +} + +@(test) +test_termios :: proc(t: ^testing.T) { + testing.expect_value(t, transmute(posix.CControl_Flags)posix.tcflag_t(posix._CSIZE), posix.CSIZE) + + testing.expect_value(t, transmute(posix.COutput_Flags)posix.tcflag_t(posix._NLDLY), posix.NLDLY) + testing.expect_value(t, transmute(posix.COutput_Flags)posix.tcflag_t(posix._CRDLY), posix.CRDLY) + testing.expect_value(t, transmute(posix.COutput_Flags)posix.tcflag_t(posix._TABDLY), posix.TABDLY) + testing.expect_value(t, transmute(posix.COutput_Flags)posix.tcflag_t(posix._BSDLY), posix.BSDLY) + testing.expect_value(t, transmute(posix.COutput_Flags)posix.tcflag_t(posix._VTDLY), posix.VTDLY) + testing.expect_value(t, transmute(posix.COutput_Flags)posix.tcflag_t(posix._FFDLY), posix.FFDLY) +} diff --git a/tests/core/sys/posix/structs.odin b/tests/core/sys/posix/structs.odin new file mode 100644 index 000000000..bdb1c24e3 --- /dev/null +++ b/tests/core/sys/posix/structs.odin @@ -0,0 +1,127 @@ +//+build darwin, freebsd, openbsd, netbsd +package tests_core_posix + +import "core:log" +import "core:testing" +import "core:sys/posix" + +// This test tests some of the process APIs of posix while also double checking size and alignment +// of the structs we bound. + +@(test) +execute_struct_checks :: proc(t: ^testing.T) { + log.debug("compiling C project") + { + switch pid := posix.fork(); pid { + case -1: + log.errorf("fork() failed: %s", posix.strerror()) + case 0: + c_compiler := posix.getenv("CC") + if c_compiler == nil { + c_compiler = "clang" + } + + posix.execlp(c_compiler, + c_compiler, #directory + "/structs/structs.c", "-o", #directory + "/structs/c_structs", nil) + posix.exit(69) + case: + if !wait_for(t, pid) { return } + log.debug("C code has been compiled!") + } + } + + log.debug("compiling Odin project") + { + switch pid := posix.fork(); pid { + case -1: + log.errorf("fork() failed: %s", posix.strerror()) + case 0: + posix.execlp(ODIN_ROOT + "/odin", + ODIN_ROOT + "/odin", "build", #directory + "/structs/structs.odin", "-out:" + #directory + "/structs/odin_structs", "-file", nil) + posix.exit(69) + case: + if !wait_for(t, pid) { return } + log.debug("Odin code has been compiled!") + } + } + + c_buf: [dynamic]byte + defer delete(c_buf) + c_out := get_output(t, &c_buf, #directory + "/structs/c_structs", nil) + + odin_buf: [dynamic]byte + defer delete(odin_buf) + odin_out := get_output(t, &odin_buf, #directory + "/structs/odin_structs", nil) + + testing.expectf(t, c_out == odin_out, "The C output and Odin output differ!\nC output:\n%s\n\n\n\nOdin Output:\n%s", c_out, odin_out) + + /* ----------- HELPERS ----------- */ + + wait_for :: proc(t: ^testing.T, pid: posix.pid_t) -> (ok: bool) { + log.debugf("waiting on pid %v", pid) + + waiting: for { + status: i32 + wpid := posix.waitpid(pid, &status, {}) + if !testing.expectf(t, wpid != -1, "waitpid() failure: %v", posix.strerror()) { + return false + } + + switch { + case posix.WIFEXITED(status): + ok = testing.expect_value(t, posix.WEXITSTATUS(status), 0) + break waiting + case posix.WIFSIGNALED(status): + log.errorf("child process raised: %v", posix.strsignal(posix.WTERMSIG(status))) + ok = false + break waiting + case: + log.errorf("unexpected status (this should never happen): %v", status) + ok = false + break waiting + } + } + + return + } + + get_output :: proc(t: ^testing.T, output: ^[dynamic]byte, cmd: ..cstring) -> (out_str: string) { + log.debugf("capturing output of: %v", cmd) + + pipe: [2]posix.FD + if !testing.expect_value(t, posix.pipe(&pipe), posix.result.OK) { + return + } + + switch pid := posix.fork(); pid { + case -1: + log.errorf("fork() failed: %s", posix.strerror()) + return + case 0: + posix.close(pipe[0]) + posix.dup2(pipe[1], 1) + posix.execv(cmd[0], raw_data(cmd[:])) + panic(string(posix.strerror())) + case: + posix.close(pipe[1]) + log.debugf("waiting on pid %v", pid) + + reader: for { + buf: [256]byte + switch read := posix.read(pipe[0], &buf[0], 256); { + case read < 0: + log.errorf("read output failed: %v", posix.strerror()) + return + case read == 0: + break reader + case: + append(output, ..buf[:read]) + } + } + + wait_for(t, pid) + + return string(output[:]) + } + } +} diff --git a/tests/core/sys/posix/structs/.gitignore b/tests/core/sys/posix/structs/.gitignore new file mode 100644 index 000000000..646eae0a2 --- /dev/null +++ b/tests/core/sys/posix/structs/.gitignore @@ -0,0 +1,2 @@ +c_structs +odin_structs diff --git a/tests/core/sys/posix/structs/structs.c b/tests/core/sys/posix/structs/structs.c new file mode 100644 index 000000000..7d8038fbb --- /dev/null +++ b/tests/core/sys/posix/structs/structs.c @@ -0,0 +1,103 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + printf("dirent %zu %zu\n", sizeof(struct dirent), _Alignof(struct dirent)); + printf("flock %zu %zu\n", sizeof(struct flock), _Alignof(struct flock)); + printf("glob_t %zu %zu\n", sizeof(glob_t), _Alignof(glob_t)); + printf("group %zu %zu\n", sizeof(struct group), _Alignof(struct group)); + printf("lconv %zu %zu\n", sizeof(struct lconv), _Alignof(struct lconv)); + + printf("pthread_t %zu %zu\n", sizeof(pthread_t), _Alignof(pthread_t)); + printf("pthread_attr_t %zu %zu\n", sizeof(pthread_attr_t), _Alignof(pthread_attr_t)); + printf("pthread_key_t %zu %zu\n", sizeof(pthread_key_t), _Alignof(pthread_key_t)); + + printf("sched_param %zu %zu\n", sizeof(struct sched_param), _Alignof(struct sched_param)); + + printf("termios %zu %zu\n", sizeof(struct termios), _Alignof(struct termios)); + + printf("in_addr %zu %zu\n", sizeof(struct in_addr), _Alignof(struct in_addr)); + printf("in6_addr %zu %zu\n", sizeof(struct in6_addr), _Alignof(struct in6_addr)); + printf("sockaddr_in %zu %zu\n", sizeof(struct sockaddr_in), _Alignof(struct sockaddr_in)); + printf("sockaddr_in6 %zu %zu\n", sizeof(struct sockaddr_in6), _Alignof(struct sockaddr_in6)); + printf("ipv6_mreq %zu %zu\n", sizeof(struct ipv6_mreq), _Alignof(struct ipv6_mreq)); + + printf("sockaddr_storage %zu %zu\n", sizeof(struct sockaddr_storage), _Alignof(struct sockaddr_storage)); + printf("msghdr %zu %zu\n", sizeof(struct msghdr), _Alignof(struct msghdr)); + printf("cmsghdr %zu %zu\n", sizeof(struct cmsghdr), _Alignof(struct cmsghdr)); + printf("linger %zu %zu\n", sizeof(struct linger), _Alignof(struct linger)); + + printf("hostent %zu %zu\n", sizeof(struct hostent), _Alignof(struct hostent)); + printf("netent %zu %zu\n", sizeof(struct netent), _Alignof(struct netent)); + printf("protoent %zu %zu\n", sizeof(struct protoent), _Alignof(struct protoent)); + printf("servent %zu %zu\n", sizeof(struct servent), _Alignof(struct servent)); + printf("addrinfo %zu %zu\n", sizeof(struct addrinfo), _Alignof(struct addrinfo)); + + printf("pollfd %zu %zu\n", sizeof(struct pollfd), _Alignof(struct pollfd)); + + printf("passwd %zu %zu\n", sizeof(struct passwd), _Alignof(struct passwd)); + + printf("shmid_ds %zu %zu\n", sizeof(struct shmid_ds), _Alignof(struct shmid_ds)); + printf("ipc_perm %zu %zu\n", sizeof(struct ipc_perm), _Alignof(struct ipc_perm)); + printf("msqid_ds %zu %zu\n", sizeof(struct msqid_ds), _Alignof(struct msqid_ds)); + + printf("rlimit %zu %zu\n", sizeof(struct rlimit), _Alignof(struct rlimit)); + printf("rusage %zu %zu\n", sizeof(struct rusage), _Alignof(struct rusage)); + + printf("sockaddr_un %zu %zu\n", sizeof(struct sockaddr_un), _Alignof(struct sockaddr_un)); + + printf("utsname %zu %zu\n", sizeof(struct utsname), _Alignof(struct utsname)); + + printf("tms %zu %zu\n", sizeof(struct tms), _Alignof(struct tms)); + + printf("sigaction %zu %zu\n", sizeof(struct sigaction), _Alignof(struct sigaction)); + printf("stack_t %zu %zu\n", sizeof(stack_t), _Alignof(stack_t)); + printf("siginfo_t %zu %zu\n", sizeof(siginfo_t), _Alignof(siginfo_t)); + + printf("fd_set %zu %zu\n", sizeof(fd_set), _Alignof(fd_set)); + + printf("iovec %zu %zu\n", sizeof(struct iovec), _Alignof(struct iovec)); + + printf("semid_ds %zu %zu\n", sizeof(struct semid_ds), _Alignof(struct semid_ds)); + printf("sembuf %zu %zu\n", sizeof(struct sembuf), _Alignof(struct sembuf)); + + printf("itimerval %zu %zu\n", sizeof(struct itimerval), _Alignof(struct itimerval)); + + printf("utimbuf %zu %zu\n", sizeof(struct utimbuf), _Alignof(struct utimbuf)); + + printf("wordexp_t %zu %zu\n", sizeof(wordexp_t), _Alignof(wordexp_t)); + + printf("time_t %zu %zu\n", sizeof(time_t), _Alignof(time_t)); + printf("timespec %zu %zu\n", sizeof(struct timespec), _Alignof(struct timespec)); + printf("clock_t %zu %zu\n", sizeof(clock_t), _Alignof(clock_t)); + + return 0; +} diff --git a/tests/core/sys/posix/structs/structs.odin b/tests/core/sys/posix/structs/structs.odin new file mode 100644 index 000000000..2663d1e30 --- /dev/null +++ b/tests/core/sys/posix/structs/structs.odin @@ -0,0 +1,74 @@ +package main + +import "core:fmt" +import "core:sys/posix" + +main :: proc() { + fmt.println("dirent", size_of(posix.dirent), align_of(posix.dirent)) + fmt.println("flock", size_of(posix.flock), align_of(posix.flock)) + fmt.println("glob_t", size_of(posix.glob_t), align_of(posix.glob_t)) + fmt.println("group", size_of(posix.group), align_of(posix.group)) + fmt.println("lconv", size_of(posix.lconv), align_of(posix.lconv)) + + fmt.println("pthread_t", size_of(posix.pthread_t), align_of(posix.pthread_t)) + fmt.println("pthread_attr_t", size_of(posix.pthread_attr_t), align_of(posix.pthread_attr_t)) + fmt.println("pthread_key_t", size_of(posix.pthread_key_t), align_of(posix.pthread_key_t)) + + fmt.println("sched_param", size_of(posix.sched_param), align_of(posix.sched_param)) + + fmt.println("termios", size_of(posix.termios), align_of(posix.termios)) + + fmt.println("in_addr", size_of(posix.in_addr), align_of(posix.in_addr)) + fmt.println("in6_addr", size_of(posix.in6_addr), align_of(posix.in6_addr)) + fmt.println("sockaddr_in", size_of(posix.sockaddr_in), align_of(posix.sockaddr_in)) + fmt.println("sockaddr_in6", size_of(posix.sockaddr_in6), align_of(posix.sockaddr_in6)) + fmt.println("ipv6_mreq", size_of(posix.ipv6_mreq), align_of(posix.ipv6_mreq)) + + fmt.println("sockaddr_storage", size_of(posix.sockaddr_storage), align_of(posix.sockaddr_storage)) + fmt.println("msghdr", size_of(posix.msghdr), align_of(posix.msghdr)) + fmt.println("cmsghdr", size_of(posix.cmsghdr), align_of(posix.cmsghdr)) + fmt.println("linger", size_of(posix.linger), align_of(posix.linger)) + + fmt.println("hostent", size_of(posix.hostent), align_of(posix.hostent)) + fmt.println("netent", size_of(posix.netent), align_of(posix.netent)) + fmt.println("protoent", size_of(posix.protoent), align_of(posix.protoent)) + fmt.println("servent", size_of(posix.servent), align_of(posix.servent)) + fmt.println("addrinfo", size_of(posix.addrinfo), align_of(posix.addrinfo)) + + fmt.println("pollfd", size_of(posix.pollfd), align_of(posix.pollfd)) + fmt.println("passwd", size_of(posix.passwd), align_of(posix.passwd)) + + fmt.println("shmid_ds", size_of(posix.shmid_ds), align_of(posix.shmid_ds)) + fmt.println("ipc_perm", size_of(posix.ipc_perm), align_of(posix.ipc_perm)) + fmt.println("msqid_ds", size_of(posix.msqid_ds), align_of(posix.msqid_ds)) + + fmt.println("rlimit", size_of(posix.rlimit), align_of(posix.rlimit)) + fmt.println("rusage", size_of(posix.rusage), align_of(posix.rusage)) + + fmt.println("sockaddr_un", size_of(posix.sockaddr_un), align_of(posix.sockaddr_un)) + + fmt.println("utsname", size_of(posix.utsname), align_of(posix.utsname)) + + fmt.println("tms", size_of(posix.tms), align_of(posix.tms)) + + fmt.println("sigaction", size_of(posix.sigaction_t), align_of(posix.sigaction_t)) + fmt.println("stack_t", size_of(posix.stack_t), align_of(posix.stack_t)) + fmt.println("siginfo_t", size_of(posix.siginfo_t), align_of(posix.siginfo_t)) + + fmt.println("fd_set", size_of(posix.fd_set), align_of(posix.fd_set)) + + fmt.println("iovec", size_of(posix.iovec), align_of(posix.iovec)) + + fmt.println("semid_ds", size_of(posix.semid_ds), align_of(posix.semid_ds)) + fmt.println("sembuf", size_of(posix.sembuf), align_of(posix.sembuf)) + + fmt.println("itimerval", size_of(posix.itimerval), align_of(posix.itimerval)) + + fmt.println("utimbuf", size_of(posix.utimbuf), align_of(posix.utimbuf)) + + fmt.println("wordexp_t", size_of(posix.wordexp_t), align_of(posix.wordexp_t)) + + fmt.println("time_t", size_of(posix.time_t), align_of(posix.time_t)) + fmt.println("timespec", size_of(posix.timespec), align_of(posix.timespec)) + fmt.println("clock_t", size_of(posix.clock_t), align_of(posix.clock_t)) +} -- cgit v1.2.3 From fd5376ba88ef6ad8dcb4657d3159c7eee737a2af Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 14 Aug 2024 11:28:49 +0100 Subject: Allow `@(require_results)` on `foreign` blocks --- src/check_decl.cpp | 4 +++- src/checker.cpp | 7 +++++++ src/checker.hpp | 4 ++++ 3 files changed, 14 insertions(+), 1 deletion(-) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 1425aafa8..27c1554e6 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1124,7 +1124,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { e->deprecated_message = ac.deprecated_message; e->warning_message = ac.warning_message; - ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix,ac.link_suffix); + ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix, ac.link_suffix); if (ac.has_disabled_proc) { if (ac.disabled_proc) { e->flags |= EntityFlag_Disabled; @@ -1221,6 +1221,8 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } else { pt->require_results = true; } + } else if (d->foreign_require_results && pt->result_count != 0) { + pt->require_results = true; } if (ac.link_name.len > 0) { diff --git a/src/checker.cpp b/src/checker.cpp index 3c4b5ddc0..6571f696a 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3212,6 +3212,12 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) { } c->foreign_context.visibility_kind = kind; return true; + } else if (name == "require_results") { + if (value != nullptr) { + error(elem, "Expected no value for '%.*s'", LIT(name)); + } + c->foreign_context.require_results = true; + return true; } return false; @@ -4300,6 +4306,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { } ast_node(pl, ProcLit, init); e = alloc_entity_procedure(d->scope, token, nullptr, pl->tags); + d->foreign_require_results = c->foreign_context.require_results; if (fl != nullptr) { GB_ASSERT(fl->kind == Ast_Ident); e->Procedure.foreign_library_ident = fl; diff --git a/src/checker.hpp b/src/checker.hpp index d76e4c7d0..438156f18 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -204,9 +204,12 @@ struct DeclInfo { Array attributes; Ast * proc_lit; // Ast_ProcLit Type * gen_proc_type; // Precalculated + bool is_using; bool where_clauses_evaluated; + bool foreign_require_results; std::atomic proc_checked_state; + BlockingMutex proc_checked_mutex; isize defer_used; bool defer_use_checked; @@ -322,6 +325,7 @@ struct ForeignContext { String link_prefix; String link_suffix; EntityVisiblityKind visibility_kind; + bool require_results; }; typedef Array CheckerTypePath; -- cgit v1.2.3 From f7d7d65bc0f7da6993e117e67e771c7e1741ea06 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Fri, 16 Aug 2024 22:19:28 +0200 Subject: fix `open` bindings `open` specifies the `mode` argument as vararg (presumably to make it optional). varargs actually have rules about casting, in this case the rule that any integer arg of size <= 4 has to be casted to `i32` before passing it. Not doing that implicit cast makes the permissions wrong or not apply at all. --- core/os/os_darwin.odin | 14 +------------- core/os/os_freebsd.odin | 2 +- core/os/os_haiku.odin | 4 ++-- core/os/os_netbsd.odin | 4 ++-- core/os/os_openbsd.odin | 4 ++-- core/sys/posix/fcntl.odin | 2 +- src/check_decl.cpp | 15 +++++++++++++++ tests/core/sys/posix/posix.odin | 21 +++++++++++++++++++++ 8 files changed, 45 insertions(+), 21 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin index 602257314..1253e9ae6 100644 --- a/core/os/os_darwin.odin +++ b/core/os/os_darwin.odin @@ -584,7 +584,7 @@ F_GETPATH :: 50 // return the full path of the fd foreign libc { @(link_name="__error") __error :: proc() -> ^c.int --- - @(link_name="open") _unix_open :: proc(path: cstring, flags: i32, mode: u16) -> Handle --- + @(link_name="open") _unix_open :: proc(path: cstring, flags: i32, #c_vararg mode: ..u16) -> Handle --- @(link_name="close") _unix_close :: proc(handle: Handle) -> c.int --- @(link_name="read") _unix_read :: proc(handle: Handle, buffer: rawptr, count: c.size_t) -> int --- @(link_name="write") _unix_write :: proc(handle: Handle, buffer: rawptr, count: c.size_t) -> int --- @@ -698,18 +698,6 @@ open :: proc(path: string, flags: int = O_RDWR, mode: int = 0) -> (handle: Handl return } - /* - @INFO(Platin): this is only done because O_CREATE for some reason fails to apply mode - should not happen if the handle is a directory - */ - if flags & O_CREATE != 0 && mode != 0 && !isDir { - err = fchmod(handle, cast(u16)mode) - if err != nil { - _unix_close(handle) - handle = INVALID_HANDLE - } - } - return } diff --git a/core/os/os_freebsd.odin b/core/os/os_freebsd.odin index c2d747a60..41c487b2b 100644 --- a/core/os/os_freebsd.odin +++ b/core/os/os_freebsd.odin @@ -371,7 +371,7 @@ F_KINFO :: 22 foreign libc { @(link_name="__error") __Error_location :: proc() -> ^c.int --- - @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.uint16_t) -> Handle --- + @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, #c_vararg mode: ..u16) -> Handle --- @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int --- @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- diff --git a/core/os/os_haiku.odin b/core/os/os_haiku.odin index 7f1ec7089..2d87c07a6 100644 --- a/core/os/os_haiku.odin +++ b/core/os/os_haiku.odin @@ -124,7 +124,7 @@ foreign libc { @(link_name="fork") _unix_fork :: proc() -> pid_t --- @(link_name="getthrid") _unix_getthrid :: proc() -> int --- - @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle --- + @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, #c_vararg mode: ..u16) -> Handle --- @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int --- @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- @@ -200,7 +200,7 @@ fork :: proc() -> (Pid, Error) { open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Error) { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() cstr := strings.clone_to_cstring(path, context.temp_allocator) - handle := _unix_open(cstr, c.int(flags), c.int(mode)) + handle := _unix_open(cstr, c.int(flags), u16(mode)) if handle == -1 { return INVALID_HANDLE, get_last_error() } diff --git a/core/os/os_netbsd.odin b/core/os/os_netbsd.odin index 931b38f12..82a8dc1eb 100644 --- a/core/os/os_netbsd.odin +++ b/core/os/os_netbsd.odin @@ -423,7 +423,7 @@ R_OK :: 4 // Test for read permission foreign libc { @(link_name="__errno") __errno_location :: proc() -> ^c.int --- - @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle --- + @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, #c_vararg mode: ..u32) -> Handle --- @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int --- @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- @@ -488,7 +488,7 @@ get_last_error :: proc "contextless" () -> Error { open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Error) { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() cstr := strings.clone_to_cstring(path, context.temp_allocator) - handle := _unix_open(cstr, c.int(flags), c.int(mode)) + handle := _unix_open(cstr, c.int(flags), c.uint(mode)) if handle == -1 { return INVALID_HANDLE, get_last_error() } diff --git a/core/os/os_openbsd.odin b/core/os/os_openbsd.odin index caae19e6e..44eac8564 100644 --- a/core/os/os_openbsd.odin +++ b/core/os/os_openbsd.odin @@ -348,7 +348,7 @@ foreign libc { @(link_name="fork") _unix_fork :: proc() -> pid_t --- @(link_name="getthrid") _unix_getthrid :: proc() -> int --- - @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle --- + @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, #c_vararg mode: ..u32) -> Handle --- @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int --- @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- @@ -412,7 +412,7 @@ fork :: proc() -> (Pid, Error) { open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Error) { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() cstr := strings.clone_to_cstring(path, context.temp_allocator) - handle := _unix_open(cstr, c.int(flags), c.int(mode)) + handle := _unix_open(cstr, c.int(flags), c.uint(mode)) if handle == -1 { return INVALID_HANDLE, get_last_error() } diff --git a/core/sys/posix/fcntl.odin b/core/sys/posix/fcntl.odin index 8f1b1a511..ca030a9a5 100644 --- a/core/sys/posix/fcntl.odin +++ b/core/sys/posix/fcntl.odin @@ -58,7 +58,7 @@ foreign lib { [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html ]] */ - open :: proc(path: cstring, flags: O_Flags, mode: mode_t = {}) -> FD --- + open :: proc(path: cstring, flags: O_Flags, #c_vararg mode: ..mode_t) -> FD --- /* Equivalent to the open() function except in the case where path specifies a relative path. diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 090b7f615..c60084ec3 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -691,6 +691,13 @@ gb_internal bool sig_compare(TypeCheckSig *a, TypeCheckSig *b, Type *x, Type *y) } gb_internal bool signature_parameter_similar_enough(Type *x, Type *y) { + if (is_type_bit_set(x)) { + x = bit_set_to_int(x); + } + if (is_type_bit_set(y)) { + y = bit_set_to_int(y); + } + if (sig_compare(is_type_pointer, x, y)) { return true; } @@ -737,6 +744,14 @@ gb_internal bool signature_parameter_similar_enough(Type *x, Type *y) { return true; } + if (sig_compare(is_type_slice, x, y)) { + Type *s1 = core_type(x); + Type *s2 = core_type(y); + if (signature_parameter_similar_enough(s1->Slice.elem, s2->Slice.elem)) { + return true; + } + } + return are_types_identical(x, y); } diff --git a/tests/core/sys/posix/posix.odin b/tests/core/sys/posix/posix.odin index 380149c42..fa30d1601 100644 --- a/tests/core/sys/posix/posix.odin +++ b/tests/core/sys/posix/posix.odin @@ -287,3 +287,24 @@ test_pthreads :: proc(t: ^testing.T) { return nil } } + +@(test) +open_permissions :: proc(t: ^testing.T) { + in_mode := posix.mode_t{.IRUSR, .IWUSR, .IROTH, .IRGRP} + fd := posix.open("test_posix_permissions.txt", {.CREAT, .RDWR}, in_mode) + testing.expectf(t, fd != -1, "failed to open: %v", posix.strerror()) + + defer { + ret := posix.close(fd) + testing.expectf(t, ret == .OK, "failed to close: %v", posix.strerror()) + ret2 := posix.remove("test_posix_permissions.txt") + testing.expectf(t, ret2 == 0, "failed to remove: %v", posix.strerror()) + } + + stat: posix.stat_t + res := posix.fstat(fd, &stat) + testing.expectf(t, res == .OK, "failed to stat: %v", posix.strerror()) + + stat.st_mode -= posix.S_IFMT + testing.expect_value(t, stat.st_mode, in_mode) +} -- cgit v1.2.3 From dcf339517e5139a07cc45a2835567538b716d45c Mon Sep 17 00:00:00 2001 From: Laytan Date: Wed, 4 Sep 2024 18:47:08 +0200 Subject: make c vararg with any vs concrete type similar enough --- src/check_decl.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index c60084ec3..3b532a727 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -768,6 +768,19 @@ gb_internal bool are_signatures_similar_enough(Type *a_, Type *b_) { if (a->result_count != b->result_count) { return false; } + + if (a->c_vararg != b->c_vararg) { + return false; + } + + if (a->variadic != b->variadic) { + return false; + } + + if (a->variadic && a->variadic_index != b->variadic_index) { + return false; + } + for (isize i = 0; i < a->param_count; i++) { Type *x = core_type(a->params->Tuple.variables[i]->type); Type *y = core_type(b->params->Tuple.variables[i]->type); @@ -779,6 +792,17 @@ gb_internal bool are_signatures_similar_enough(Type *a_, Type *b_) { y = core_type(y->BitSet.underlying); } + // Allow a `#c_vararg args: ..any` with `#c_vararg args: ..foo`. + if (a->variadic && i == a->variadic_index) { + GB_ASSERT(x->kind == Type_Slice); + GB_ASSERT(y->kind == Type_Slice); + Type *x_elem = core_type(x->Slice.elem); + Type *y_elem = core_type(y->Slice.elem); + if (is_type_any(x_elem) || is_type_any(y_elem)) { + continue; + } + } + if (!signature_parameter_similar_enough(x, y)) { return false; } -- cgit v1.2.3 From 3498d4341d27d3ddb7f5dc6013d58a7db3bece25 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 2 Oct 2024 15:07:22 +0100 Subject: Fix #4330 --- src/check_decl.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 3b532a727..0cc89435d 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -232,6 +232,10 @@ gb_internal bool check_override_as_type_due_to_aliasing(CheckerContext *ctx, Ent // until there is a proper delaying system to try declaration again if they // have failed. + if (e->type != nullptr && is_type_typed(e->type)) { + return false; + } + e->kind = Entity_TypeName; check_type_decl(ctx, e, init, named_type); return true; -- cgit v1.2.3 From 01c7380df8b2c42da103ce93e0bd8631ace8ad12 Mon Sep 17 00:00:00 2001 From: Misomosi Date: Sun, 20 Oct 2024 22:45:30 -0400 Subject: Fix #4390 by assigning invalid entity type --- src/check_decl.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 0cc89435d..60eb030ff 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -88,11 +88,12 @@ gb_internal Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *o e->type = t_invalid; return nullptr; } else if (is_type_polymorphic(t)) { - Entity *e = entity_of_node(operand->expr); - if (e == nullptr) { + Entity *e2 = entity_of_node(operand->expr); + if (e2 == nullptr) { + e->type = t_invalid; return nullptr; } - if (e->state.load() != EntityState_Resolved) { + if (e2->state.load() != EntityState_Resolved) { gbString str = type_to_string(t); defer (gb_string_free(str)); error(e->token, "Invalid use of a polymorphic type '%s' in %.*s", str, LIT(context_name)); -- cgit v1.2.3 From 9f77f7c4175a6202b01a984a692c0288ac6077ec Mon Sep 17 00:00:00 2001 From: Roland Kovacs Date: Sun, 22 Dec 2024 12:38:37 +0100 Subject: Fix crash on assignment of parapoly proc to variable --- src/check_decl.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 60eb030ff..d541426d3 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -94,12 +94,14 @@ gb_internal Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *o return nullptr; } if (e2->state.load() != EntityState_Resolved) { - gbString str = type_to_string(t); - defer (gb_string_free(str)); - error(e->token, "Invalid use of a polymorphic type '%s' in %.*s", str, LIT(context_name)); - e->type = t_invalid; + e->type = t; return nullptr; } + gbString str = type_to_string(t); + defer (gb_string_free(str)); + error(operand->expr, "Invalid use of a non-specialized polymorphic type '%s' in %.*s", str, LIT(context_name)); + e->type = t_invalid; + return nullptr; } else if (is_type_empty_union(t)) { gbString str = type_to_string(t); defer (gb_string_free(str)); -- cgit v1.2.3 From c8ad2a4245fb9f0f5a0975e394693333299eb2a7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 1 Jan 2025 15:12:54 +0000 Subject: Support multiple paths for wasm in `foreign import` --- src/check_decl.cpp | 88 ++++++++++++++++++++++++++---------------------------- src/checker.cpp | 34 +++++++++++++++++++++ src/checker.hpp | 1 + 3 files changed, 77 insertions(+), 46 deletions(-) (limited to 'src/check_decl.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 60eb030ff..ced886bc9 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -971,6 +971,43 @@ gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeCon } } +gb_internal void check_foreign_procedure(CheckerContext *ctx, Entity *e, DeclInfo *d) { + GB_ASSERT(e != nullptr); + GB_ASSERT(e->kind == Entity_Procedure); + String name = e->Procedure.link_name; + + mutex_lock(&ctx->info->foreign_mutex); + + auto *fp = &ctx->info->foreigns; + StringHashKey key = string_hash_string(name); + Entity **found = string_map_get(fp, key); + if (found && e != *found) { + Entity *f = *found; + TokenPos pos = f->token.pos; + Type *this_type = base_type(e->type); + Type *other_type = base_type(f->type); + if (is_type_proc(this_type) && is_type_proc(other_type)) { + if (!are_signatures_similar_enough(this_type, other_type)) { + error(d->proc_lit, + "Redeclaration of foreign procedure '%.*s' with different type signatures\n" + "\tat %s", + LIT(name), token_pos_to_string(pos)); + } + } else if (!signature_parameter_similar_enough(this_type, other_type)) { + error(d->proc_lit, + "Foreign entity '%.*s' previously declared elsewhere with a different type\n" + "\tat %s", + LIT(name), token_pos_to_string(pos)); + } + } else if (name == "main") { + error(d->proc_lit, "The link name 'main' is reserved for internal use"); + } else { + string_map_set(fp, key, e); + } + + mutex_unlock(&ctx->info->foreign_mutex); +} + gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { GB_ASSERT(e->type == nullptr); if (d->proc_lit->kind != Ast_ProcLit) { @@ -1307,57 +1344,16 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { name = e->Procedure.link_name; } Entity *foreign_library = init_entity_foreign_library(ctx, e); - - if (is_arch_wasm() && foreign_library != nullptr) { - String module_name = str_lit("env"); - GB_ASSERT (foreign_library->kind == Entity_LibraryName); - if (foreign_library->LibraryName.paths.count != 1) { - error(foreign_library->token, "'foreign import' for '%.*s' architecture may only have one path, got %td", - LIT(target_arch_names[build_context.metrics.arch]), foreign_library->LibraryName.paths.count); - } - - if (foreign_library->LibraryName.paths.count >= 1) { - module_name = foreign_library->LibraryName.paths[0]; - } - - if (!string_ends_with(module_name, str_lit(".o"))) { - name = concatenate3_strings(permanent_allocator(), module_name, WASM_MODULE_NAME_SEPARATOR, name); - } - } - e->Procedure.is_foreign = true; e->Procedure.link_name = name; + e->Procedure.foreign_library = foreign_library; - mutex_lock(&ctx->info->foreign_mutex); - - auto *fp = &ctx->info->foreigns; - StringHashKey key = string_hash_string(name); - Entity **found = string_map_get(fp, key); - if (found && e != *found) { - Entity *f = *found; - TokenPos pos = f->token.pos; - Type *this_type = base_type(e->type); - Type *other_type = base_type(f->type); - if (is_type_proc(this_type) && is_type_proc(other_type)) { - if (!are_signatures_similar_enough(this_type, other_type)) { - error(d->proc_lit, - "Redeclaration of foreign procedure '%.*s' with different type signatures\n" - "\tat %s", - LIT(name), token_pos_to_string(pos)); - } - } else if (!signature_parameter_similar_enough(this_type, other_type)) { - error(d->proc_lit, - "Foreign entity '%.*s' previously declared elsewhere with a different type\n" - "\tat %s", - LIT(name), token_pos_to_string(pos)); - } - } else if (name == "main") { - error(d->proc_lit, "The link name 'main' is reserved for internal use"); + if (is_arch_wasm() && foreign_library != nullptr) { + // NOTE(bill): this must be delayed because the foreign import paths might not be evaluated yet until much later + mpsc_enqueue(&ctx->info->foreign_decls_to_check, e); } else { - string_map_set(fp, key, e); + check_foreign_procedure(ctx, e, d); } - - mutex_unlock(&ctx->info->foreign_mutex); } else { String name = e->token.string; if (e->Procedure.link_name.len > 0) { diff --git a/src/checker.cpp b/src/checker.cpp index b7cf343f8..7e0a64d75 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1356,6 +1356,7 @@ gb_internal void init_checker_info(CheckerInfo *i) { mpsc_init(&i->required_global_variable_queue, a); // 1<<10); mpsc_init(&i->required_foreign_imports_through_force_queue, a); // 1<<10); mpsc_init(&i->foreign_imports_to_check_fullpaths, a); // 1<<10); + mpsc_init(&i->foreign_decls_to_check, a); // 1<<10); mpsc_init(&i->intrinsics_entry_point_usage, a); // 1<<10); // just waste some memory here, even if it probably never used string_map_init(&i->load_directory_cache); @@ -1382,6 +1383,7 @@ gb_internal void destroy_checker_info(CheckerInfo *i) { mpsc_destroy(&i->required_global_variable_queue); mpsc_destroy(&i->required_foreign_imports_through_force_queue); mpsc_destroy(&i->foreign_imports_to_check_fullpaths); + mpsc_destroy(&i->foreign_decls_to_check); map_destroy(&i->objc_msgSend_types); string_map_destroy(&i->load_file_cache); @@ -5094,6 +5096,38 @@ gb_internal void check_foreign_import_fullpaths(Checker *c) { e->LibraryName.paths = fl->fullpaths; } + + for (Entity *e = nullptr; mpsc_dequeue(&c->info.foreign_decls_to_check, &e); /**/) { + GB_ASSERT(e != nullptr); + if (e->kind != Entity_Procedure) { + continue; + } + if (!is_arch_wasm()) { + continue; + } + Entity *foreign_library = e->Procedure.foreign_library; + GB_ASSERT(foreign_library != nullptr); + + String name = e->Procedure.link_name; + + String module_name = str_lit("env"); + GB_ASSERT (foreign_library->kind == Entity_LibraryName); + if (foreign_library->LibraryName.paths.count != 1) { + error(foreign_library->token, "'foreign import' for '%.*s' architecture may only have one path, got %td", + LIT(target_arch_names[build_context.metrics.arch]), foreign_library->LibraryName.paths.count); + } + + if (foreign_library->LibraryName.paths.count >= 1) { + module_name = foreign_library->LibraryName.paths[0]; + } + + if (!string_ends_with(module_name, str_lit(".o"))) { + name = concatenate3_strings(permanent_allocator(), module_name, WASM_MODULE_NAME_SEPARATOR, name); + } + e->Procedure.link_name = name; + + check_foreign_procedure(&ctx, e, e->decl_info); + } } gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) { diff --git a/src/checker.hpp b/src/checker.hpp index 438156f18..036990f29 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -461,6 +461,7 @@ struct CheckerInfo { MPSCQueue required_global_variable_queue; MPSCQueue required_foreign_imports_through_force_queue; MPSCQueue foreign_imports_to_check_fullpaths; + MPSCQueue foreign_decls_to_check; MPSCQueue intrinsics_entry_point_usage; -- cgit v1.2.3