From: Judah Caruso Date: Tue, 5 Dec 2023 06:17:38 +0000 (-0700) Subject: add 'memory_equal' intrinsic X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=22ac63eca677e510c434cd17c9e88c557c1d0abb;p=onyx.git add 'memory_equal' intrinsic --- diff --git a/compiler/include/astnodes.h b/compiler/include/astnodes.h index 0c294da8..14cb5df7 100644 --- a/compiler/include/astnodes.h +++ b/compiler/include/astnodes.h @@ -385,6 +385,7 @@ typedef enum OnyxIntrinsic { ONYX_INTRINSIC_MEMORY_SIZE, ONYX_INTRINSIC_MEMORY_GROW, ONYX_INTRINSIC_MEMORY_COPY, ONYX_INTRINSIC_MEMORY_FILL, + ONYX_INTRINSIC_MEMORY_EQUAL, ONYX_INTRINSIC_INITIALIZE, @@ -1293,7 +1294,7 @@ struct AstImport { // be imported as. If NULL, the last token of // the package path is used. The following // imports the core package as C. - // + // // use core { C :: package } // OnyxToken *qualified_package_name; @@ -1798,7 +1799,7 @@ typedef struct CheckerData { typedef struct ContextCaches { bh_imap implicit_cast_to_bool_cache; -} ContextCaches; +} ContextCaches; typedef struct DefinedVariable { char *key; diff --git a/compiler/include/wasm_emit.h b/compiler/include/wasm_emit.h index d3cfe966..81c4ada8 100644 --- a/compiler/include/wasm_emit.h +++ b/compiler/include/wasm_emit.h @@ -422,8 +422,8 @@ typedef enum WasmInstructionType { WI_I32X4_TRUNC_SAT_F32X4_U = SIMD_INSTR_MASK | 249, WI_F32X4_CONVERT_I32X4_S = SIMD_INSTR_MASK | 250, WI_F32X4_CONVERT_I32X4_U = SIMD_INSTR_MASK | 251, - - + + WI_MEMORY_INIT = EXT_INSTR_MASK | 0x08, WI_MEMORY_COPY = EXT_INSTR_MASK | 0x0a, WI_MEMORY_FILL = EXT_INSTR_MASK | 0x0b, @@ -699,7 +699,7 @@ typedef struct OnyxWasmModule { bh_imap elem_map; bh_arr(DeferredStmt) deferred_stmts; - bh_arr(AllocatedSpace) local_allocations; + bh_arr(AllocatedSpace) local_allocations; bh_arr(PatchInfo) stack_leave_patches; bh_arr(DatumPatchInfo) data_patches; @@ -792,7 +792,7 @@ void onyx_wasm_module_free(OnyxWasmModule* module); void onyx_wasm_module_write_to_buffer(OnyxWasmModule* module, bh_buffer* buffer); void onyx_wasm_module_write_to_file(OnyxWasmModule* module, bh_file file); -#ifdef ONYX_RUNTIME_LIBRARY +#ifdef ONYX_RUNTIME_LIBRARY void onyx_run_initialize(b32 debug_enabled); b32 onyx_run_wasm(bh_buffer code_buffer, int argc, char *argv[]); #endif diff --git a/compiler/src/builtins.c b/compiler/src/builtins.c index c78a7f58..e9d3b5af 100644 --- a/compiler/src/builtins.c +++ b/compiler/src/builtins.c @@ -115,10 +115,11 @@ IntrinsicTable intrinsic_table; static IntrinsicMap builtin_intrinsics[] = { { "unreachable", ONYX_INTRINSIC_UNREACHABLE }, - { "memory_size", ONYX_INTRINSIC_MEMORY_SIZE }, - { "memory_grow", ONYX_INTRINSIC_MEMORY_GROW }, - { "memory_copy", ONYX_INTRINSIC_MEMORY_COPY }, - { "memory_fill", ONYX_INTRINSIC_MEMORY_FILL }, + { "memory_size", ONYX_INTRINSIC_MEMORY_SIZE }, + { "memory_grow", ONYX_INTRINSIC_MEMORY_GROW }, + { "memory_copy", ONYX_INTRINSIC_MEMORY_COPY }, + { "memory_fill", ONYX_INTRINSIC_MEMORY_FILL }, + { "memory_equal", ONYX_INTRINSIC_MEMORY_EQUAL }, { "__initialize", ONYX_INTRINSIC_INITIALIZE }, @@ -551,12 +552,12 @@ void initialize_builtins(bh_allocator a) { fori (i, 0, Binary_Op_Count) { operator_overloads[i] = NULL; - bh_arr_new(global_heap_allocator, operator_overloads[i], 4); + bh_arr_new(global_heap_allocator, operator_overloads[i], 4); } fori (i, 0, Unary_Op_Count) { unary_operator_overloads[i] = NULL; - bh_arr_new(global_heap_allocator, unary_operator_overloads[i], 4); + bh_arr_new(global_heap_allocator, unary_operator_overloads[i], 4); } IntrinsicMap* intrinsic = &builtin_intrinsics[0]; @@ -584,7 +585,7 @@ void initalize_special_globals() { void introduce_build_options(bh_allocator a) { Package* p = package_lookup_or_create("runtime", context.global_scope, a, context.global_scope->created_at); - + // HACK creating this for later package_lookup_or_create("runtime.vars", p->scope, a, context.global_scope->created_at); diff --git a/compiler/src/parser.c b/compiler/src/parser.c index a9ef24a8..ded751ff 100644 --- a/compiler/src/parser.c +++ b/compiler/src/parser.c @@ -713,7 +713,7 @@ static AstTyped* parse_factor(OnyxParser* parser) { if (parser->current_function_stack && bh_arr_length(parser->current_function_stack) > 0) { bh_arr_push(bh_arr_last(parser->current_function_stack)->nodes_that_need_entities_after_clone, (AstNode *) fc); - + } else { ENTITY_SUBMIT(fc); } @@ -883,7 +883,7 @@ static AstTyped* parse_factor(OnyxParser* parser) { export_name->token = parser->curr - 1; export_name->func = (AstFunction *) parse_factor(parser); export_name->type_node = builtin_string_type; - + retval = (AstTyped *) export_name; break; } @@ -993,7 +993,7 @@ static AstTyped* parse_factor(OnyxParser* parser) { } method_call->right = (AstTyped *) parse_function_call(parser, (AstTyped *) method); - + retval = (AstTyped *) method_call; break; } @@ -1050,7 +1050,7 @@ static inline i32 get_precedence(BinaryOp kind) { case Binary_Op_Divide: return 8; case Binary_Op_Modulus: return 9; - + case Binary_Op_Coalesce: return 11; default: return -1; @@ -1348,10 +1348,10 @@ static AstSwitchCase* parse_case_stmt(OnyxParser* parser) { b32 is_pointer = 0; if (consume_token_if_next(parser, '&') || consume_token_if_next(parser, '^')) is_pointer = 1; - + OnyxToken *capture_symbol = expect_token(parser, Token_Type_Symbol); AstLocal *capture = make_local(parser->allocator, capture_symbol, NULL); - + sc_node->capture = capture; sc_node->capture_is_by_pointer = is_pointer; @@ -2248,7 +2248,7 @@ static AstStructType* parse_struct(OnyxParser* parser) { } expect_token(parser, '{'); - + parser->current_scope = scope_symbols_in_structures_should_be_bound_to; b32 member_is_used = 0; @@ -3668,7 +3668,7 @@ static void parse_top_level_statement(OnyxParser* parser) { // #match // something :: (....) { // } - // + // // This will make converting something to a overloaded // function easier and require less copying by the programmer. if (next_tokens_are(parser, 2, ':', ':')) { @@ -3715,7 +3715,7 @@ static void parse_top_level_statement(OnyxParser* parser) { inject->documentation = parser->last_documentation_token; parser->last_documentation_token = NULL; } - + ENTITY_SUBMIT(inject); return; } diff --git a/compiler/src/wasm_emit.c b/compiler/src/wasm_emit.c index 93d1f395..4f0e0d4d 100644 --- a/compiler/src/wasm_emit.c +++ b/compiler/src/wasm_emit.c @@ -317,7 +317,7 @@ static void debug_emit_instruction(OnyxWasmModule *mod, OnyxToken *token) { return; } - DebugContext *ctx = mod->debug_context; + DebugContext *ctx = mod->debug_context; assert(ctx && ctx->last_token); // Sanity check @@ -337,7 +337,7 @@ static void debug_emit_instruction(OnyxWasmModule *mod, OnyxToken *token) { repeat_previous = 1; } } - + if (repeat_previous) { // Output / increment REP instruction if (ctx->last_op_was_rep) { @@ -551,6 +551,7 @@ EMIT_FUNC(values_into_contiguous_memory, u64 base_ptr_local, Type *type, u32 o EMIT_FUNC(struct_literal_into_contiguous_memory, AstStructLiteral* sl, u64 base_ptr_local, u32 offset); EMIT_FUNC(wasm_copy, OnyxToken *token); EMIT_FUNC(wasm_fill, OnyxToken *token); +EMIT_FUNC(wasm_memory_equal, OnyxToken *token); EMIT_FUNC(enter_structured_block, StructuredBlockType sbt, OnyxToken* block_token); EMIT_FUNC_NO_ARGS(leave_structured_block); @@ -1316,7 +1317,7 @@ EMIT_FUNC(for_range, AstFor* for_node, u64 iter_local) { WIL(for_node->token, WI_LOCAL_GET, step_local); WI(for_node->token, WI_I32_ADD); WIL(for_node->token, WI_LOCAL_SET, iter_local); - + if (for_node->has_first) { WIL(for_node->token, WI_I32_CONST, 0); WIL(for_node->token, WI_LOCAL_SET, for_node->first_local); @@ -1743,7 +1744,7 @@ EMIT_FUNC(switch, AstSwitch* switch_node) { WIL(NULL, WI_LOCAL_GET, union_capture_idx); WIL(NULL, WI_PTR_CONST, union_expr_type->Union.alignment); WI(NULL, WI_PTR_ADD); - + WIL(NULL, WI_I32_CONST, type_size_of(sc->capture->type)); emit_wasm_copy(mod, &code, NULL); } @@ -2145,7 +2146,7 @@ EMIT_FUNC(call, AstCall* call) { emit_store_instruction(mod, &code, &basic_types[Basic_Kind_Type_Index], reserve_size + 4); local_raw_free(mod->local_alloc, WASM_TYPE_PTR); - + WIL(call_token, WI_LOCAL_GET, stack_top_store_local); WIL(call_token, WI_I32_CONST, reserve_size); WI(call_token, WI_I32_ADD); @@ -2239,7 +2240,7 @@ EMIT_FUNC(call, AstCall* call) { } else { emit_expression(mod, &code, call->callee); - + u64 global_closure_base_idx = bh_imap_get(&mod->index_map, (u64) &builtin_closure_base); WI(NULL, WI_DROP); WIL(NULL, WI_GLOBAL_SET, global_closure_base_idx); @@ -2326,7 +2327,7 @@ EMIT_FUNC(method_call, AstBinaryOp *mcall) { } else { first_arg->value = (AstTyped *) tmp_local; } - + // // Actually emit the function call. emit_call(mod, &code, call_node); @@ -2407,7 +2408,7 @@ EMIT_FUNC(intrinsic_call, AstCall* call) { switch (call->intrinsic) { case ONYX_INTRINSIC_UNREACHABLE: WI(call->token, WI_UNREACHABLE); break; - + case ONYX_INTRINSIC_MEMORY_SIZE: WID(call->token, WI_MEMORY_SIZE, 0x00); break; case ONYX_INTRINSIC_MEMORY_GROW: WID(call->token, WI_MEMORY_GROW, 0x00); break; case ONYX_INTRINSIC_MEMORY_COPY: @@ -2416,6 +2417,9 @@ EMIT_FUNC(intrinsic_call, AstCall* call) { case ONYX_INTRINSIC_MEMORY_FILL: emit_wasm_fill(mod, &code, call->token); break; + case ONYX_INTRINSIC_MEMORY_EQUAL: + emit_wasm_memory_equal(mod, &code, call->token); + break; case ONYX_INTRINSIC_INITIALIZE: { Type* type_to_initialize = ((AstArgument *) call->args.values[0])->value->type->Pointer.elem; @@ -3007,6 +3011,12 @@ EMIT_FUNC(wasm_fill, OnyxToken *token) { *pcode = code; } +EMIT_FUNC(wasm_memory_equal, OnyxToken *token) { + bh_arr(WasmInstruction) code = *pcode; + emit_intrinsic_memory_equal(mod, &code); + *pcode = code; +} + EMIT_FUNC(values_into_contiguous_memory, u64 base_ptr_local, Type *type, u32 offset, i32 value_count, AstTyped **values) { bh_arr(WasmInstruction) code = *pcode; @@ -3525,7 +3535,7 @@ EMIT_FUNC(expression, AstTyped* expr) { u64 capture_block_ptr = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR); WIL(NULL, WI_LOCAL_TEE, capture_block_ptr); - + // Populate the block bh_arr_each(AstCaptureLocal *, capture, func->captures->captures) { WIL(NULL, WI_LOCAL_GET, capture_block_ptr); @@ -3538,7 +3548,7 @@ EMIT_FUNC(expression, AstTyped* expr) { emit_store_instruction(mod, &code, (*capture)->type, (*capture)->offset); } - + local_raw_free(mod->local_alloc, WASM_TYPE_PTR); WIL(NULL, WI_I32_CONST, func->captures->total_size_in_bytes); @@ -3624,7 +3634,7 @@ EMIT_FUNC(expression, AstTyped* expr) { } else { WIL(NULL, WI_I32_CONST, type_alignment_of(field->expr->type)); } - + WI(NULL, WI_I32_ADD); WIL(NULL, WI_I32_CONST, type_size_of(field->type->Union.variants_ordered[1]->type)); @@ -4326,7 +4336,7 @@ static void emit_function(OnyxWasmModule* mod, AstFunction* fd) { if (fd == builtin_initialize_data_segments && !mod->doing_linking) { // This is a large hack, but is necessary. // This particular function (__initialize_data_segments) should not be generated - // until the module is in its linking phase. This is because we have to wait + // until the module is in its linking phase. This is because we have to wait // until we have reached the linking phase to know every data element that is // and will be present. Without this, the generating data segments in the bodies // of the functions is not possible. @@ -4379,7 +4389,7 @@ static void emit_function(OnyxWasmModule* mod, AstFunction* fd) { u64 localidx = 0; bh_arr_each(AstParam, param, fd->params) { switch (type_get_param_pass(param->local->type)) { - case Param_Pass_By_Multiple_Values: + case Param_Pass_By_Multiple_Values: debug_introduce_symbol(mod, param->local->token, DSL_REGISTER, localidx | LOCAL_IS_WASM, param->local->type); bh_imap_put(&mod->local_map, (u64) param->local, localidx | LOCAL_IS_WASM); localidx += type_structlike_mem_count(param->local->type); @@ -4618,7 +4628,7 @@ static void emit_raw_string(OnyxWasmModule* mod, char *data, i32 len, u64 *out_d bh_free(global_heap_allocator, strdata); return; } - + // :ProperLinking // The length used here is one greater than the string length, because // we DO want to include the null-terminator in the outputted string. @@ -4642,7 +4652,7 @@ static void emit_string_literal(OnyxWasmModule* mod, AstStrLit* strlit) { static u32 emit_data_entry(OnyxWasmModule *mod, WasmDatum *datum) { datum->offset_ = 0; - datum->id = NEXT_DATA_ID(mod); + datum->id = NEXT_DATA_ID(mod); bh_arr_push(mod->data, *datum); return datum->id; } @@ -4933,7 +4943,7 @@ static void emit_file_contents(OnyxWasmModule* mod, AstFileContents* fc) { if (parent_file == NULL) parent_file = "."; char* parent_folder = bh_path_get_parent(parent_file, global_scratch_allocator); - + OnyxToken *filename_token = fc->filename_expr->token; token_toggle_end(filename_token); @@ -5325,7 +5335,7 @@ b32 onyx_wasm_build_link_options_from_node(OnyxWasmLinkOptions *opts, AstTyped * assert(builtin_link_options_type); Type *link_options_type = type_build_from_ast(context.ast_alloc, builtin_link_options_type); - + AstStructLiteral *input = (AstStructLiteral *) node; StructMember smem; diff --git a/compiler/src/wasm_intrinsics.h b/compiler/src/wasm_intrinsics.h index f3f72e6e..8e107f16 100644 --- a/compiler/src/wasm_intrinsics.h +++ b/compiler/src/wasm_intrinsics.h @@ -9,110 +9,190 @@ // :32BitPointers EMIT_FUNC_NO_ARGS(intrinsic_memory_copy) { bh_arr(WasmInstruction) code = *pcode; - + // The stack should look like this: // // // - + u64 count_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32); u64 source_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR); u64 dest_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR); - + WIL(NULL, WI_LOCAL_SET, count_local); WIL(NULL, WI_LOCAL_SET, source_local); WIL(NULL, WI_LOCAL_SET, dest_local); - + // count is greater than 0 WIL(NULL, WI_LOCAL_GET, count_local); WID(NULL, WI_I32_CONST, 0); WI(NULL, WI_I32_GT_S); - + WID(NULL, WI_IF_START, 0x40); WID(NULL, WI_LOOP_START, 0x40); - + WIL(NULL, WI_LOCAL_GET, count_local); WID(NULL, WI_I32_CONST, 1); WI(NULL, WI_I32_SUB); WIL(NULL, WI_LOCAL_SET, count_local); - + WIL(NULL, WI_LOCAL_GET, dest_local); WIL(NULL, WI_LOCAL_GET, count_local); WI(NULL, WI_PTR_ADD); - + WIL(NULL, WI_LOCAL_GET, source_local); WIL(NULL, WI_LOCAL_GET, count_local); WI(NULL, WI_PTR_ADD); - + WID(NULL, WI_I32_LOAD_8_U, ((WasmInstructionData) { 0, 0 })); WID(NULL, WI_I32_STORE_8, ((WasmInstructionData) { 0, 0 })); - + WIL(NULL, WI_LOCAL_GET, count_local); WID(NULL, WI_I32_CONST, 0); WI(NULL, WI_I32_GT_S); WID(NULL, WI_COND_JUMP, 0x00); - + WI(NULL, WI_LOOP_END); WI(NULL, WI_IF_END); - + local_raw_free(mod->local_alloc, WASM_TYPE_INT32); local_raw_free(mod->local_alloc, WASM_TYPE_PTR); local_raw_free(mod->local_alloc, WASM_TYPE_PTR); - + *pcode = code; } EMIT_FUNC_NO_ARGS(intrinsic_memory_fill) { bh_arr(WasmInstruction) code = *pcode; - + // The stack should look like this: // // // - + u64 count_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32); u64 byte_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32); u64 dest_local = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR); - + WIL(NULL, WI_LOCAL_SET, count_local); WIL(NULL, WI_LOCAL_SET, byte_local); WIL(NULL, WI_LOCAL_SET, dest_local); - + // count is greater than 0 WIL(NULL, WI_LOCAL_GET, count_local); WID(NULL, WI_I32_CONST, 0); WI(NULL, WI_I32_GT_S); - + WID(NULL, WI_IF_START, 0x40); WID(NULL, WI_LOOP_START, 0x40); - + WIL(NULL, WI_LOCAL_GET, count_local); WID(NULL, WI_I32_CONST, 1); WI(NULL, WI_I32_SUB); WIL(NULL, WI_LOCAL_SET, count_local); - + WIL(NULL, WI_LOCAL_GET, dest_local); WIL(NULL, WI_LOCAL_GET, count_local); WI(NULL, WI_PTR_ADD); - + WIL(NULL, WI_LOCAL_GET, byte_local); WID(NULL, WI_I32_STORE_8, ((WasmInstructionData) { 0, 0 })); - + WIL(NULL, WI_LOCAL_GET, count_local); WID(NULL, WI_I32_CONST, 0); WI(NULL, WI_I32_GT_S); WID(NULL, WI_COND_JUMP, 0x00); - + WI(NULL, WI_LOOP_END); WI(NULL, WI_IF_END); - + local_raw_free(mod->local_alloc, WASM_TYPE_INT32); local_raw_free(mod->local_alloc, WASM_TYPE_INT32); local_raw_free(mod->local_alloc, WASM_TYPE_PTR); - + *pcode = code; } +// IMPROVE: This is a stripped down/unoptimized version of memcmp that only checks equality +// between two blocks of memory (and doesn't return -1/1). Returns 1 if the blocks of memory are equal, +// and 0 otherwise. +// NOTE: This implementation is unsafe and assumes both blocks of memory are at least size_in_bytes long. +EMIT_FUNC_NO_ARGS(intrinsic_memory_equal) { + bh_arr(WasmInstruction) code = *pcode; + + // The stack should look like this: + // + // + // + + u64 size_in_bytes = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32); + u64 b_addr = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR); + u64 a_addr = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR); + + WIL(NULL, WI_LOCAL_SET, size_in_bytes); + WIL(NULL, WI_LOCAL_SET, b_addr); + WIL(NULL, WI_LOCAL_SET, a_addr); + + u64 memory_is_equal = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32); + WID(NULL, WI_I32_CONST, 1); // set to 1 so size_in_bytes == 0 will return true + WIL(NULL, WI_LOCAL_SET, memory_is_equal); + + // if size_in_bytes > 0 + WIL(NULL, WI_LOCAL_GET, size_in_bytes); + WID(NULL, WI_I32_CONST, 0); + WI (NULL, WI_I32_GT_S); + WID(NULL, WI_IF_START, 0x40); + u64 loop_idx = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32); + WIL(NULL, WI_I32_CONST, 0); + WIL(NULL, WI_LOCAL_SET, loop_idx); + + WID(NULL, WI_LOOP_START, 0x40); + // if loop_idx >= size_in_bytes, break + WIL(NULL, WI_LOCAL_GET, loop_idx); + WIL(NULL, WI_LOCAL_GET, size_in_bytes); + WI (NULL, WI_I32_GE_S); + WID(NULL, WI_COND_JUMP, 0x01); + + // a = *(a_addr + loop_idx) + WIL(NULL, WI_LOCAL_GET, a_addr); + WIL(NULL, WI_LOCAL_GET, loop_idx); + WI (NULL, WI_PTR_ADD); + WID(NULL, WI_I32_LOAD_8_U, ((WasmInstructionData) { 0, 0 })); + + // b = *(b_addr + loop_idx) + WIL(NULL, WI_LOCAL_GET, b_addr); + WIL(NULL, WI_LOCAL_GET, loop_idx); + WI (NULL, WI_PTR_ADD); + WID(NULL, WI_I32_LOAD_8_U, ((WasmInstructionData) { 0, 0 })); + + WI (NULL, WI_I32_EQ); + WIL(NULL, WI_LOCAL_SET, memory_is_equal); + + // if bytes are not equal, break + WIL(NULL, WI_LOCAL_GET, memory_is_equal); + WI (NULL, WI_I32_EQZ); + WID(NULL, WI_COND_JUMP, 0x01); + + // loop_idx += 1 + WIL(NULL, WI_LOCAL_GET, loop_idx); + WIL(NULL, WI_I32_CONST, 1); + WI (NULL, WI_I32_ADD); + WIL(NULL, WI_LOCAL_SET, loop_idx); + + WID(NULL, WI_JUMP, 0x00); + WI(NULL, WI_LOOP_END); + WI(NULL, WI_IF_END); + + WIL(NULL, WI_LOCAL_GET, memory_is_equal); + + local_raw_free(mod->local_alloc, WASM_TYPE_PTR); // a_addr + local_raw_free(mod->local_alloc, WASM_TYPE_PTR); // b_addr + local_raw_free(mod->local_alloc, WASM_TYPE_INT32); // size_in_bytes + local_raw_free(mod->local_alloc, WASM_TYPE_INT32); // memory_is_equal + local_raw_free(mod->local_alloc, WASM_TYPE_INT32); // loop_idx + + *pcode = code; +} EMIT_FUNC(initialize_type, Type* type, OnyxToken* where) { bh_arr(WasmInstruction) code = *pcode; @@ -137,7 +217,7 @@ EMIT_FUNC(initialize_type, Type* type, OnyxToken* where) { emit_expression(mod, &code, *smem->initial_value); emit_store_instruction(mod, &code, smem->type, smem->offset); } - + local_raw_free(mod->local_alloc, WASM_TYPE_PTR); break; } diff --git a/core/alloc/heap.onyx b/core/alloc/heap.onyx index 1c12f67b..f967098b 100644 --- a/core/alloc/heap.onyx +++ b/core/alloc/heap.onyx @@ -64,6 +64,7 @@ get_freed_size :: () => { use core.intrinsics.wasm { memory_size, memory_grow, memory_copy, memory_fill, + memory_equal, } use core {memory, math} @@ -190,7 +191,7 @@ get_freed_size :: () => { heap_free :: (ptr: rawptr) { #if Enable_Debug do assert(ptr != null, "Trying to free a null pointer."); - + hb_ptr := cast(&heap_freed_block) (cast(uintptr) ptr - sizeof heap_allocated_block); // @@ -350,7 +351,7 @@ get_freed_size :: () => { hb_ptr.size |= Allocated_Flag; new_ptr := heap_alloc(new_size_, align); #if runtime.Multi_Threading_Enabled do sync.mutex_lock(&heap_mutex); - + memory_copy(new_ptr, ptr, old_size - sizeof heap_block); heap_free(ptr); return new_ptr; diff --git a/core/builtin.onyx b/core/builtin.onyx index cdb3dc8f..eed4ad27 100644 --- a/core/builtin.onyx +++ b/core/builtin.onyx @@ -64,9 +64,9 @@ null :: cast(rawptr) 0 `null_proc` is a special function that breaks the normal rules of type checking. `null_proc`, or any procedure marked with `#null`, is assignable to any function type, regardless of if the types match. For example, - + f: (i32) -> i32 = null_proc; - + Even though `null_proc` is a `() -> void` function, it bypasses that check and gets assigned to `f`. If f is called, there will be a runtime exception. This is by design. @@ -393,22 +393,22 @@ cfree :: (ptr: rawptr) => raw_free(context.allocator, ptr); #doc """ Represents a generic "iterator" or "generator" of a specific type. Can be used in a for-loop natively. - + `data` is used for contextual information and is passed to the `next`, `close`, and `remove` procedures. - + `next` is used to extract the next value out of the iterator. It returns the next value, and a continuation flag. If the flag is false, the value should be ignored and iteration should stop. - + `close` should called when the iterator has ended. This is done automatically in for-loops, and in the `core.iter` library. In for-loops, `close` is called no matter which way the for-loop exits (`break`, `return`, etc). Using this rule, iterator can be used to create "resources" that automatically close when you are done with them. - + `remove` is used to tell the iterator to remove the last value returned from some underlying data store. Invoked automatically using the `#remove` directive in a for-loop. @@ -508,7 +508,7 @@ Link_Options :: struct { // But when stack_first is true, it is: // reserved | stack | static-data | heap stack_first := false; - + // The size, in bytes of the stack. stack_size := 16 * 65536; // 16 pages * 65536 bytes per page = 1 MiB stack @@ -563,9 +563,9 @@ Link_Options :: struct { #doc """ Special type used to represent a package at runtime. For example, - + x: package_id = package main - + Currently, there is not much you can do with this; it is only used by the runtime.info library if you want to filter tags based on which package they are coming from. @@ -581,7 +581,8 @@ any_package :: cast(package_id) 0 #operator == macro (p1, p2: package_id) => cast(u32) p1 == cast(u32) p2; #operator != macro (p1, p2: package_id) => cast(u32) p1 != cast(u32) p2; - +#operator == macro (l, r: [$N]$T) => core.intrinsics.wasm.memory_equal(cast(rawptr)l, cast(rawptr)r, N * sizeof T); +#operator != macro (l, r: [$N]$T) => !(l == r); // // DEPRECATED THINGS diff --git a/core/intrinsics/wasm.onyx b/core/intrinsics/wasm.onyx index 59608309..cde0b2c9 100644 --- a/core/intrinsics/wasm.onyx +++ b/core/intrinsics/wasm.onyx @@ -7,6 +7,7 @@ memory_size :: () -> u32 #intrinsic --- memory_grow :: (val: u32) -> i32 #intrinsic --- memory_copy :: (dst: rawptr, src: rawptr, count: i32) -> void #intrinsic --- memory_fill :: (dst: rawptr, byte: u8, count: i32) -> void #intrinsic --- +memory_equal :: (a: rawptr, b: rawptr, count: i32) -> bool #intrinsic --- // Isn't an actual wasm intrinsic right now, but will be if the proposal is accepted. clz_i32 :: (val: i32) -> i32 #intrinsic --- ctz_i32 :: (val: i32) -> i32 #intrinsic ---