| author | |
| committer | |
| log | 9c13e9b7ed9806d0f9774433d5e24359aff1b238 |
| tree | 5e595fd2182648e660db56b88b86f162810df8f4 |
| parent | 4090fe81f600afa290de5bf06a287d5fab2ea9dc |
| signature |
Before, allocator implementations had to provide `allocFn`,
`reallocFn`, and `freeFn`.
Now, they must provide only `reallocFn` and `shrinkFn`.
Reallocating from a zero length slice is allocation, and
shrinking to a zero length slice is freeing.
When the new memory size is less than or equal to the
previous allocation size, `reallocFn` now has the option
to return `error.OutOfMemory` to indicate that the allocator
would not be able to take advantage of the new size.
For more details see #1306. This commit closes #1306.
This commit paves the way to solving #2009.
This commit also introduces a memory leak to all coroutines.
There is an issue where a coroutine calls the function and it
frees its own stack frame, but then the return value of `shrinkFn`
is a slice, which is implemented as an sret struct. Writing to
the return pointer causes invalid memory write. We could work
around it by having a global helper function which has a void
return type and calling that instead. But instead this hack will
suffice until I rework coroutines to be non-allocating. Basically
coroutines are not supported right now until they are reworked as
in #1194.19 files changed, 374 insertions(+), 253 deletions(-)
src-self-hosted/ir.zig+1-1| ... | @@ -1364,7 +1364,7 @@ pub const Builder = struct { | ... | @@ -1364,7 +1364,7 @@ pub const Builder = struct { |
| 1364 | 1364 | ||
| 1365 | if (str_token[0] == 'c') { | 1365 | if (str_token[0] == 'c') { |
| 1366 | // first we add a null | 1366 | // first we add a null |
| 1367 | buf = try irb.comp.gpa().realloc(u8, buf, buf.len + 1); | 1367 | buf = try irb.comp.gpa().realloc(buf, buf.len + 1); |
| 1368 | buf[buf.len - 1] = 0; | 1368 | buf[buf.len - 1] = 0; |
| 1369 | 1369 | ||
| 1370 | // next make an array value | 1370 | // next make an array value |
src/all_types.hpp+3-3| ... | @@ -3356,7 +3356,7 @@ struct IrInstructionCoroPromise { | ... | @@ -3356,7 +3356,7 @@ struct IrInstructionCoroPromise { |
| 3356 | struct IrInstructionCoroAllocHelper { | 3356 | struct IrInstructionCoroAllocHelper { |
| 3357 | IrInstruction base; | 3357 | IrInstruction base; |
| 3358 | 3358 | ||
| 3359 | IrInstruction *alloc_fn; | 3359 | IrInstruction *realloc_fn; |
| 3360 | IrInstruction *coro_size; | 3360 | IrInstruction *coro_size; |
| 3361 | }; | 3361 | }; |
| 3362 | 3362 | ||
| ... | @@ -3481,8 +3481,8 @@ static const size_t stack_trace_ptr_count = 32; | ... | @@ -3481,8 +3481,8 @@ static const size_t stack_trace_ptr_count = 32; |
| 3481 | #define RETURN_ADDRESSES_FIELD_NAME "return_addresses" | 3481 | #define RETURN_ADDRESSES_FIELD_NAME "return_addresses" |
| 3482 | #define ERR_RET_TRACE_FIELD_NAME "err_ret_trace" | 3482 | #define ERR_RET_TRACE_FIELD_NAME "err_ret_trace" |
| 3483 | #define RESULT_FIELD_NAME "result" | 3483 | #define RESULT_FIELD_NAME "result" |
| 3484 | #define ASYNC_ALLOC_FIELD_NAME "allocFn" | 3484 | #define ASYNC_REALLOC_FIELD_NAME "reallocFn" |
| 3485 | #define ASYNC_FREE_FIELD_NAME "freeFn" | 3485 | #define ASYNC_SHRINK_FIELD_NAME "shrinkFn" |
| 3486 | #define ATOMIC_STATE_FIELD_NAME "atomic_state" | 3486 | #define ATOMIC_STATE_FIELD_NAME "atomic_state" |
| 3487 | // these point to data belonging to the awaiter | 3487 | // these point to data belonging to the awaiter |
| 3488 | #define ERR_RET_TRACE_PTR_FIELD_NAME "err_ret_trace_ptr" | 3488 | #define ERR_RET_TRACE_PTR_FIELD_NAME "err_ret_trace_ptr" |
src/analyze.cpp+5-3| ... | @@ -3707,9 +3707,11 @@ ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf | ... | @@ -3707,9 +3707,11 @@ ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf |
| 3707 | 3707 | ||
| 3708 | ZigVar *existing_var = find_variable(g, parent_scope, name, nullptr); | 3708 | ZigVar *existing_var = find_variable(g, parent_scope, name, nullptr); |
| 3709 | if (existing_var && !existing_var->shadowable) { | 3709 | if (existing_var && !existing_var->shadowable) { |
| 3710 | ErrorMsg *msg = add_node_error(g, source_node, | 3710 | if (existing_var->var_type == nullptr || !type_is_invalid(existing_var->var_type)) { |
| 3711 | buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); | 3711 | ErrorMsg *msg = add_node_error(g, source_node, |
| 3712 | add_error_note(g, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); | 3712 | buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); |
| 3713 | add_error_note(g, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); | ||
| 3714 | } | ||
| 3713 | variable_entry->var_type = g->builtin_types.entry_invalid; | 3715 | variable_entry->var_type = g->builtin_types.entry_invalid; |
| 3714 | } else { | 3716 | } else { |
| 3715 | ZigType *type; | 3717 | ZigType *type; |
src/codegen.cpp+12-5| ... | @@ -5177,7 +5177,7 @@ static LLVMValueRef get_coro_alloc_helper_fn_val(CodeGen *g, LLVMTypeRef alloc_f | ... | @@ -5177,7 +5177,7 @@ static LLVMValueRef get_coro_alloc_helper_fn_val(CodeGen *g, LLVMTypeRef alloc_f |
| 5177 | LLVMValueRef sret_ptr = LLVMBuildAlloca(g->builder, LLVMGetElementType(alloc_fn_arg_types[0]), ""); | 5177 | LLVMValueRef sret_ptr = LLVMBuildAlloca(g->builder, LLVMGetElementType(alloc_fn_arg_types[0]), ""); |
| 5178 | 5178 | ||
| 5179 | size_t next_arg = 0; | 5179 | size_t next_arg = 0; |
| 5180 | LLVMValueRef alloc_fn_val = LLVMGetParam(fn_val, next_arg); | 5180 | LLVMValueRef realloc_fn_val = LLVMGetParam(fn_val, next_arg); |
| 5181 | next_arg += 1; | 5181 | next_arg += 1; |
| 5182 | 5182 | ||
| 5183 | LLVMValueRef stack_trace_val; | 5183 | LLVMValueRef stack_trace_val; |
| ... | @@ -5195,15 +5195,22 @@ static LLVMValueRef get_coro_alloc_helper_fn_val(CodeGen *g, LLVMTypeRef alloc_f | ... | @@ -5195,15 +5195,22 @@ static LLVMValueRef get_coro_alloc_helper_fn_val(CodeGen *g, LLVMTypeRef alloc_f |
| 5195 | LLVMValueRef alignment_val = LLVMConstInt(g->builtin_types.entry_u29->type_ref, | 5195 | LLVMValueRef alignment_val = LLVMConstInt(g->builtin_types.entry_u29->type_ref, |
| 5196 | get_coro_frame_align_bytes(g), false); | 5196 | get_coro_frame_align_bytes(g), false); |
| 5197 | 5197 | ||
| 5198 | ConstExprValue *zero_array = create_const_str_lit(g, buf_create_from_str("")); | ||
| 5199 | ConstExprValue *undef_slice_zero = create_const_slice(g, zero_array, 0, 0, false); | ||
| 5200 | render_const_val(g, undef_slice_zero, ""); | ||
| 5201 | render_const_val_global(g, undef_slice_zero, ""); | ||
| 5202 | |||
| 5198 | ZigList<LLVMValueRef> args = {}; | 5203 | ZigList<LLVMValueRef> args = {}; |
| 5199 | args.append(sret_ptr); | 5204 | args.append(sret_ptr); |
| 5200 | if (g->have_err_ret_tracing) { | 5205 | if (g->have_err_ret_tracing) { |
| 5201 | args.append(stack_trace_val); | 5206 | args.append(stack_trace_val); |
| 5202 | } | 5207 | } |
| 5203 | args.append(allocator_val); | 5208 | args.append(allocator_val); |
| 5209 | args.append(undef_slice_zero->global_refs->llvm_global); | ||
| 5210 | args.append(LLVMGetUndef(g->builtin_types.entry_u29->type_ref)); | ||
| 5204 | args.append(coro_size); | 5211 | args.append(coro_size); |
| 5205 | args.append(alignment_val); | 5212 | args.append(alignment_val); |
| 5206 | LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, alloc_fn_val, args.items, args.length, | 5213 | LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, realloc_fn_val, args.items, args.length, |
| 5207 | get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, ""); | 5214 | get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, ""); |
| 5208 | set_call_instr_sret(g, call_instruction); | 5215 | set_call_instr_sret(g, call_instruction); |
| 5209 | LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, sret_ptr, err_union_err_index, ""); | 5216 | LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, sret_ptr, err_union_err_index, ""); |
| ... | @@ -5239,14 +5246,14 @@ static LLVMValueRef get_coro_alloc_helper_fn_val(CodeGen *g, LLVMTypeRef alloc_f | ... | @@ -5239,14 +5246,14 @@ static LLVMValueRef get_coro_alloc_helper_fn_val(CodeGen *g, LLVMTypeRef alloc_f |
| 5239 | static LLVMValueRef ir_render_coro_alloc_helper(CodeGen *g, IrExecutable *executable, | 5246 | static LLVMValueRef ir_render_coro_alloc_helper(CodeGen *g, IrExecutable *executable, |
| 5240 | IrInstructionCoroAllocHelper *instruction) | 5247 | IrInstructionCoroAllocHelper *instruction) |
| 5241 | { | 5248 | { |
| 5242 | LLVMValueRef alloc_fn = ir_llvm_value(g, instruction->alloc_fn); | 5249 | LLVMValueRef realloc_fn = ir_llvm_value(g, instruction->realloc_fn); |
| 5243 | LLVMValueRef coro_size = ir_llvm_value(g, instruction->coro_size); | 5250 | LLVMValueRef coro_size = ir_llvm_value(g, instruction->coro_size); |
| 5244 | LLVMValueRef fn_val = get_coro_alloc_helper_fn_val(g, LLVMTypeOf(alloc_fn), instruction->alloc_fn->value.type); | 5251 | LLVMValueRef fn_val = get_coro_alloc_helper_fn_val(g, LLVMTypeOf(realloc_fn), instruction->realloc_fn->value.type); |
| 5245 | size_t err_code_ptr_arg_index = get_async_err_code_arg_index(g, &g->cur_fn->type_entry->data.fn.fn_type_id); | 5252 | size_t err_code_ptr_arg_index = get_async_err_code_arg_index(g, &g->cur_fn->type_entry->data.fn.fn_type_id); |
| 5246 | size_t allocator_arg_index = get_async_allocator_arg_index(g, &g->cur_fn->type_entry->data.fn.fn_type_id); | 5253 | size_t allocator_arg_index = get_async_allocator_arg_index(g, &g->cur_fn->type_entry->data.fn.fn_type_id); |
| 5247 | 5254 | ||
| 5248 | ZigList<LLVMValueRef> params = {}; | 5255 | ZigList<LLVMValueRef> params = {}; |
| 5249 | params.append(alloc_fn); | 5256 | params.append(realloc_fn); |
| 5250 | uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, g->cur_fn); | 5257 | uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, g->cur_fn); |
| 5251 | if (err_ret_trace_arg_index != UINT32_MAX) { | 5258 | if (err_ret_trace_arg_index != UINT32_MAX) { |
| 5252 | params.append(LLVMGetParam(g->cur_fn_val, err_ret_trace_arg_index)); | 5259 | params.append(LLVMGetParam(g->cur_fn_val, err_ret_trace_arg_index)); |
src/ir.cpp+40-30| ... | @@ -2788,13 +2788,13 @@ static IrInstruction *ir_build_coro_promise(IrBuilder *irb, Scope *scope, AstNod | ... | @@ -2788,13 +2788,13 @@ static IrInstruction *ir_build_coro_promise(IrBuilder *irb, Scope *scope, AstNod |
| 2788 | } | 2788 | } |
| 2789 | 2789 | ||
| 2790 | static IrInstruction *ir_build_coro_alloc_helper(IrBuilder *irb, Scope *scope, AstNode *source_node, | 2790 | static IrInstruction *ir_build_coro_alloc_helper(IrBuilder *irb, Scope *scope, AstNode *source_node, |
| 2791 | IrInstruction *alloc_fn, IrInstruction *coro_size) | 2791 | IrInstruction *realloc_fn, IrInstruction *coro_size) |
| 2792 | { | 2792 | { |
| 2793 | IrInstructionCoroAllocHelper *instruction = ir_build_instruction<IrInstructionCoroAllocHelper>(irb, scope, source_node); | 2793 | IrInstructionCoroAllocHelper *instruction = ir_build_instruction<IrInstructionCoroAllocHelper>(irb, scope, source_node); |
| 2794 | instruction->alloc_fn = alloc_fn; | 2794 | instruction->realloc_fn = realloc_fn; |
| 2795 | instruction->coro_size = coro_size; | 2795 | instruction->coro_size = coro_size; |
| 2796 | 2796 | ||
| 2797 | ir_ref_instruction(alloc_fn, irb->current_basic_block); | 2797 | ir_ref_instruction(realloc_fn, irb->current_basic_block); |
| 2798 | ir_ref_instruction(coro_size, irb->current_basic_block); | 2798 | ir_ref_instruction(coro_size, irb->current_basic_block); |
| 2799 | 2799 | ||
| 2800 | return &instruction->base; | 2800 | return &instruction->base; |
| ... | @@ -3319,9 +3319,11 @@ static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_s | ... | @@ -3319,9 +3319,11 @@ static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_s |
| 3319 | if (!skip_name_check) { | 3319 | if (!skip_name_check) { |
| 3320 | ZigVar *existing_var = find_variable(codegen, parent_scope, name, nullptr); | 3320 | ZigVar *existing_var = find_variable(codegen, parent_scope, name, nullptr); |
| 3321 | if (existing_var && !existing_var->shadowable) { | 3321 | if (existing_var && !existing_var->shadowable) { |
| 3322 | ErrorMsg *msg = add_node_error(codegen, node, | 3322 | if (existing_var->var_type == nullptr || !type_is_invalid(existing_var->var_type)) { |
| 3323 | buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); | 3323 | ErrorMsg *msg = add_node_error(codegen, node, |
| 3324 | add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); | 3324 | buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); |
| 3325 | add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); | ||
| 3326 | } | ||
| 3325 | variable_entry->var_type = codegen->builtin_types.entry_invalid; | 3327 | variable_entry->var_type = codegen->builtin_types.entry_invalid; |
| 3326 | } else { | 3328 | } else { |
| 3327 | ZigType *type; | 3329 | ZigType *type; |
| ... | @@ -7506,10 +7508,10 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec | ... | @@ -7506,10 +7508,10 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec |
| 7506 | ImplicitAllocatorIdArg); | 7508 | ImplicitAllocatorIdArg); |
| 7507 | irb->exec->coro_allocator_var = ir_create_var(irb, node, coro_scope, nullptr, true, true, true, const_bool_false); | 7509 | irb->exec->coro_allocator_var = ir_create_var(irb, node, coro_scope, nullptr, true, true, true, const_bool_false); |
| 7508 | ir_build_var_decl_src(irb, coro_scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, implicit_allocator_ptr); | 7510 | ir_build_var_decl_src(irb, coro_scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, implicit_allocator_ptr); |
| 7509 | Buf *alloc_field_name = buf_create_from_str(ASYNC_ALLOC_FIELD_NAME); | 7511 | Buf *realloc_field_name = buf_create_from_str(ASYNC_REALLOC_FIELD_NAME); |
| 7510 | IrInstruction *alloc_fn_ptr = ir_build_field_ptr(irb, coro_scope, node, implicit_allocator_ptr, alloc_field_name); | 7512 | IrInstruction *realloc_fn_ptr = ir_build_field_ptr(irb, coro_scope, node, implicit_allocator_ptr, realloc_field_name); |
| 7511 | IrInstruction *alloc_fn = ir_build_load_ptr(irb, coro_scope, node, alloc_fn_ptr); | 7513 | IrInstruction *realloc_fn = ir_build_load_ptr(irb, coro_scope, node, realloc_fn_ptr); |
| 7512 | IrInstruction *maybe_coro_mem_ptr = ir_build_coro_alloc_helper(irb, coro_scope, node, alloc_fn, coro_size); | 7514 | IrInstruction *maybe_coro_mem_ptr = ir_build_coro_alloc_helper(irb, coro_scope, node, realloc_fn, coro_size); |
| 7513 | IrInstruction *alloc_result_is_ok = ir_build_test_nonnull(irb, coro_scope, node, maybe_coro_mem_ptr); | 7515 | IrInstruction *alloc_result_is_ok = ir_build_test_nonnull(irb, coro_scope, node, maybe_coro_mem_ptr); |
| 7514 | IrBasicBlock *alloc_err_block = ir_create_basic_block(irb, coro_scope, "AllocError"); | 7516 | IrBasicBlock *alloc_err_block = ir_create_basic_block(irb, coro_scope, "AllocError"); |
| 7515 | IrBasicBlock *alloc_ok_block = ir_create_basic_block(irb, coro_scope, "AllocOk"); | 7517 | IrBasicBlock *alloc_ok_block = ir_create_basic_block(irb, coro_scope, "AllocOk"); |
| ... | @@ -7643,11 +7645,11 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec | ... | @@ -7643,11 +7645,11 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec |
| 7643 | merge_incoming_values[1] = await_handle_in_block; | 7645 | merge_incoming_values[1] = await_handle_in_block; |
| 7644 | IrInstruction *awaiter_handle = ir_build_phi(irb, scope, node, 2, merge_incoming_blocks, merge_incoming_values); | 7646 | IrInstruction *awaiter_handle = ir_build_phi(irb, scope, node, 2, merge_incoming_blocks, merge_incoming_values); |
| 7645 | 7647 | ||
| 7646 | Buf *free_field_name = buf_create_from_str(ASYNC_FREE_FIELD_NAME); | 7648 | Buf *shrink_field_name = buf_create_from_str(ASYNC_SHRINK_FIELD_NAME); |
| 7647 | IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, scope, node, | 7649 | IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, scope, node, |
| 7648 | ImplicitAllocatorIdLocalVar); | 7650 | ImplicitAllocatorIdLocalVar); |
| 7649 | IrInstruction *free_fn_ptr = ir_build_field_ptr(irb, scope, node, implicit_allocator_ptr, free_field_name); | 7651 | IrInstruction *shrink_fn_ptr = ir_build_field_ptr(irb, scope, node, implicit_allocator_ptr, shrink_field_name); |
| 7650 | IrInstruction *free_fn = ir_build_load_ptr(irb, scope, node, free_fn_ptr); | 7652 | IrInstruction *shrink_fn = ir_build_load_ptr(irb, scope, node, shrink_fn_ptr); |
| 7651 | IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); | 7653 | IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); |
| 7652 | IrInstruction *coro_mem_ptr_maybe = ir_build_coro_free(irb, scope, node, coro_id, irb->exec->coro_handle); | 7654 | IrInstruction *coro_mem_ptr_maybe = ir_build_coro_free(irb, scope, node, coro_id, irb->exec->coro_handle); |
| 7653 | IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node, | 7655 | IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node, |
| ... | @@ -7659,11 +7661,20 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec | ... | @@ -7659,11 +7661,20 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec |
| 7659 | IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var); | 7661 | IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var); |
| 7660 | IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr); | 7662 | IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr); |
| 7661 | IrInstruction *mem_slice = ir_build_slice(irb, scope, node, coro_mem_ptr_ref, zero, coro_size, false); | 7663 | IrInstruction *mem_slice = ir_build_slice(irb, scope, node, coro_mem_ptr_ref, zero, coro_size, false); |
| 7662 | size_t arg_count = 2; | 7664 | size_t arg_count = 5; |
| 7663 | IrInstruction **args = allocate<IrInstruction *>(arg_count); | 7665 | IrInstruction **args = allocate<IrInstruction *>(arg_count); |
| 7664 | args[0] = implicit_allocator_ptr; // self | 7666 | args[0] = implicit_allocator_ptr; // self |
| 7665 | args[1] = mem_slice; // old_mem | 7667 | args[1] = mem_slice; // old_mem |
| 7666 | ir_build_call(irb, scope, node, nullptr, free_fn, arg_count, args, false, FnInlineAuto, false, nullptr, nullptr); | 7668 | args[2] = ir_build_const_usize(irb, scope, node, 8); // old_align |
| 7669 | // TODO: intentional memory leak here. If this is set to 0 then there is an issue where a coroutine | ||
| 7670 | // calls the function and it frees its own stack frame, but then the return value is a slice, which | ||
| 7671 | // is implemented as an sret struct. writing to the return pointer causes invalid memory write. | ||
| 7672 | // We could work around it by having a global helper function which has a void return type | ||
| 7673 | // and calling that instead. But instead this hack will suffice until I rework coroutines to be | ||
| 7674 | // non-allocating. Basically coroutines are not supported right now until they are reworked. | ||
| 7675 | args[3] = ir_build_const_usize(irb, scope, node, 1); // new_size | ||
| 7676 | args[4] = ir_build_const_usize(irb, scope, node, 1); // new_align | ||
| 7677 | ir_build_call(irb, scope, node, nullptr, shrink_fn, arg_count, args, false, FnInlineAuto, false, nullptr, nullptr); | ||
| 7667 | 7678 | ||
| 7668 | IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "Resume"); | 7679 | IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "Resume"); |
| 7669 | ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false); | 7680 | ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false); |
| ... | @@ -13574,32 +13585,31 @@ IrInstruction *ir_get_implicit_allocator(IrAnalyze *ira, IrInstruction *source_i | ... | @@ -13574,32 +13585,31 @@ IrInstruction *ir_get_implicit_allocator(IrAnalyze *ira, IrInstruction *source_i |
| 13574 | static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *call_instruction, ZigFn *fn_entry, ZigType *fn_type, | 13585 | static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *call_instruction, ZigFn *fn_entry, ZigType *fn_type, |
| 13575 | IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count, IrInstruction *async_allocator_inst) | 13586 | IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count, IrInstruction *async_allocator_inst) |
| 13576 | { | 13587 | { |
| 13577 | Buf *alloc_field_name = buf_create_from_str(ASYNC_ALLOC_FIELD_NAME); | 13588 | Buf *realloc_field_name = buf_create_from_str(ASYNC_REALLOC_FIELD_NAME); |
| 13578 | //Buf *free_field_name = buf_create_from_str("freeFn"); | ||
| 13579 | assert(async_allocator_inst->value.type->id == ZigTypeIdPointer); | 13589 | assert(async_allocator_inst->value.type->id == ZigTypeIdPointer); |
| 13580 | ZigType *container_type = async_allocator_inst->value.type->data.pointer.child_type; | 13590 | ZigType *container_type = async_allocator_inst->value.type->data.pointer.child_type; |
| 13581 | IrInstruction *field_ptr_inst = ir_analyze_container_field_ptr(ira, alloc_field_name, &call_instruction->base, | 13591 | IrInstruction *field_ptr_inst = ir_analyze_container_field_ptr(ira, realloc_field_name, &call_instruction->base, |
| 13582 | async_allocator_inst, container_type); | 13592 | async_allocator_inst, container_type); |
| 13583 | if (type_is_invalid(field_ptr_inst->value.type)) { | 13593 | if (type_is_invalid(field_ptr_inst->value.type)) { |
| 13584 | return ira->codegen->invalid_instruction; | 13594 | return ira->codegen->invalid_instruction; |
| 13585 | } | 13595 | } |
| 13586 | ZigType *ptr_to_alloc_fn_type = field_ptr_inst->value.type; | 13596 | ZigType *ptr_to_realloc_fn_type = field_ptr_inst->value.type; |
| 13587 | assert(ptr_to_alloc_fn_type->id == ZigTypeIdPointer); | 13597 | assert(ptr_to_realloc_fn_type->id == ZigTypeIdPointer); |
| 13588 | 13598 | ||
| 13589 | ZigType *alloc_fn_type = ptr_to_alloc_fn_type->data.pointer.child_type; | 13599 | ZigType *realloc_fn_type = ptr_to_realloc_fn_type->data.pointer.child_type; |
| 13590 | if (alloc_fn_type->id != ZigTypeIdFn) { | 13600 | if (realloc_fn_type->id != ZigTypeIdFn) { |
| 13591 | ir_add_error(ira, &call_instruction->base, | 13601 | ir_add_error(ira, &call_instruction->base, |
| 13592 | buf_sprintf("expected allocation function, found '%s'", buf_ptr(&alloc_fn_type->name))); | 13602 | buf_sprintf("expected reallocation function, found '%s'", buf_ptr(&realloc_fn_type->name))); |
| 13593 | return ira->codegen->invalid_instruction; | 13603 | return ira->codegen->invalid_instruction; |
| 13594 | } | 13604 | } |
| 13595 | 13605 | ||
| 13596 | ZigType *alloc_fn_return_type = alloc_fn_type->data.fn.fn_type_id.return_type; | 13606 | ZigType *realloc_fn_return_type = realloc_fn_type->data.fn.fn_type_id.return_type; |
| 13597 | if (alloc_fn_return_type->id != ZigTypeIdErrorUnion) { | 13607 | if (realloc_fn_return_type->id != ZigTypeIdErrorUnion) { |
| 13598 | ir_add_error(ira, fn_ref, | 13608 | ir_add_error(ira, fn_ref, |
| 13599 | buf_sprintf("expected allocation function to return error union, but it returns '%s'", buf_ptr(&alloc_fn_return_type->name))); | 13609 | buf_sprintf("expected allocation function to return error union, but it returns '%s'", buf_ptr(&realloc_fn_return_type->name))); |
| 13600 | return ira->codegen->invalid_instruction; | 13610 | return ira->codegen->invalid_instruction; |
| 13601 | } | 13611 | } |
| 13602 | ZigType *alloc_fn_error_set_type = alloc_fn_return_type->data.error_union.err_set_type; | 13612 | ZigType *alloc_fn_error_set_type = realloc_fn_return_type->data.error_union.err_set_type; |
| 13603 | ZigType *return_type = fn_type->data.fn.fn_type_id.return_type; | 13613 | ZigType *return_type = fn_type->data.fn.fn_type_id.return_type; |
| 13604 | ZigType *promise_type = get_promise_type(ira->codegen, return_type); | 13614 | ZigType *promise_type = get_promise_type(ira->codegen, return_type); |
| 13605 | ZigType *async_return_type = get_error_union_type(ira->codegen, alloc_fn_error_set_type, promise_type); | 13615 | ZigType *async_return_type = get_error_union_type(ira->codegen, alloc_fn_error_set_type, promise_type); |
| ... | @@ -22033,8 +22043,8 @@ static IrInstruction *ir_analyze_instruction_coro_promise(IrAnalyze *ira, IrInst | ... | @@ -22033,8 +22043,8 @@ static IrInstruction *ir_analyze_instruction_coro_promise(IrAnalyze *ira, IrInst |
| 22033 | } | 22043 | } |
| 22034 | 22044 | ||
| 22035 | static IrInstruction *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira, IrInstructionCoroAllocHelper *instruction) { | 22045 | static IrInstruction *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira, IrInstructionCoroAllocHelper *instruction) { |
| 22036 | IrInstruction *alloc_fn = instruction->alloc_fn->child; | 22046 | IrInstruction *realloc_fn = instruction->realloc_fn->child; |
| 22037 | if (type_is_invalid(alloc_fn->value.type)) | 22047 | if (type_is_invalid(realloc_fn->value.type)) |
| 22038 | return ira->codegen->invalid_instruction; | 22048 | return ira->codegen->invalid_instruction; |
| 22039 | 22049 | ||
| 22040 | IrInstruction *coro_size = instruction->coro_size->child; | 22050 | IrInstruction *coro_size = instruction->coro_size->child; |
| ... | @@ -22042,7 +22052,7 @@ static IrInstruction *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira, I | ... | @@ -22042,7 +22052,7 @@ static IrInstruction *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira, I |
| 22042 | return ira->codegen->invalid_instruction; | 22052 | return ira->codegen->invalid_instruction; |
| 22043 | 22053 | ||
| 22044 | IrInstruction *result = ir_build_coro_alloc_helper(&ira->new_irb, instruction->base.scope, | 22054 | IrInstruction *result = ir_build_coro_alloc_helper(&ira->new_irb, instruction->base.scope, |
| 22045 | instruction->base.source_node, alloc_fn, coro_size); | 22055 | instruction->base.source_node, realloc_fn, coro_size); |
| 22046 | ZigType *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, false); | 22056 | ZigType *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, false); |
| 22047 | result->value.type = get_optional_type(ira->codegen, u8_ptr_type); | 22057 | result->value.type = get_optional_type(ira->codegen, u8_ptr_type); |
| 22048 | return result; | 22058 | return result; |
src/ir_print.cpp+1-1| ... | @@ -1286,7 +1286,7 @@ static void ir_print_promise_result_type(IrPrint *irp, IrInstructionPromiseResul | ... | @@ -1286,7 +1286,7 @@ static void ir_print_promise_result_type(IrPrint *irp, IrInstructionPromiseResul |
| 1286 | 1286 | ||
| 1287 | static void ir_print_coro_alloc_helper(IrPrint *irp, IrInstructionCoroAllocHelper *instruction) { | 1287 | static void ir_print_coro_alloc_helper(IrPrint *irp, IrInstructionCoroAllocHelper *instruction) { |
| 1288 | fprintf(irp->f, "@coroAllocHelper("); | 1288 | fprintf(irp->f, "@coroAllocHelper("); |
| 1289 | ir_print_other_instruction(irp, instruction->alloc_fn); | 1289 | ir_print_other_instruction(irp, instruction->realloc_fn); |
| 1290 | fprintf(irp->f, ","); | 1290 | fprintf(irp->f, ","); |
| 1291 | ir_print_other_instruction(irp, instruction->coro_size); | 1291 | ir_print_other_instruction(irp, instruction->coro_size); |
| 1292 | fprintf(irp->f, ")"); | 1292 | fprintf(irp->f, ")"); |
std/array_list.zig+5-2| ... | @@ -80,7 +80,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { | ... | @@ -80,7 +80,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { |
| 80 | /// The caller owns the returned memory. ArrayList becomes empty. | 80 | /// The caller owns the returned memory. ArrayList becomes empty. |
| 81 | pub fn toOwnedSlice(self: *Self) []align(A) T { | 81 | pub fn toOwnedSlice(self: *Self) []align(A) T { |
| 82 | const allocator = self.allocator; | 82 | const allocator = self.allocator; |
| 83 | const result = allocator.alignedShrink(T, A, self.items, self.len); | 83 | const result = allocator.shrink(self.items, self.len); |
| 84 | self.* = init(allocator); | 84 | self.* = init(allocator); |
| 85 | return result; | 85 | return result; |
| 86 | } | 86 | } |
| ... | @@ -144,6 +144,9 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { | ... | @@ -144,6 +144,9 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { |
| 144 | pub fn shrink(self: *Self, new_len: usize) void { | 144 | pub fn shrink(self: *Self, new_len: usize) void { |
| 145 | assert(new_len <= self.len); | 145 | assert(new_len <= self.len); |
| 146 | self.len = new_len; | 146 | self.len = new_len; |
| 147 | self.items = self.allocator.realloc(self.items, new_len) catch |e| switch (e) { | ||
| 148 | error.OutOfMemory => return, // no problem, capacity is still correct then. | ||
| 149 | }; | ||
| 147 | } | 150 | } |
| 148 | 151 | ||
| 149 | pub fn ensureCapacity(self: *Self, new_capacity: usize) !void { | 152 | pub fn ensureCapacity(self: *Self, new_capacity: usize) !void { |
| ... | @@ -153,7 +156,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { | ... | @@ -153,7 +156,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { |
| 153 | better_capacity += better_capacity / 2 + 8; | 156 | better_capacity += better_capacity / 2 + 8; |
| 154 | if (better_capacity >= new_capacity) break; | 157 | if (better_capacity >= new_capacity) break; |
| 155 | } | 158 | } |
| 156 | self.items = try self.allocator.alignedRealloc(T, A, self.items, better_capacity); | 159 | self.items = try self.allocator.realloc(self.items, better_capacity); |
| 157 | } | 160 | } |
| 158 | 161 | ||
| 159 | pub fn addOne(self: *Self) !*T { | 162 | pub fn addOne(self: *Self) !*T { |
std/buffer.zig+1-1| ... | @@ -50,7 +50,7 @@ pub const Buffer = struct { | ... | @@ -50,7 +50,7 @@ pub const Buffer = struct { |
| 50 | /// is safe to `deinit`. | 50 | /// is safe to `deinit`. |
| 51 | pub fn toOwnedSlice(self: *Buffer) []u8 { | 51 | pub fn toOwnedSlice(self: *Buffer) []u8 { |
| 52 | const allocator = self.list.allocator; | 52 | const allocator = self.list.allocator; |
| 53 | const result = allocator.shrink(u8, self.list.items, self.len()); | 53 | const result = allocator.shrink(self.list.items, self.len()); |
| 54 | self.* = initNull(allocator); | 54 | self.* = initNull(allocator); |
| 55 | return result; | 55 | return result; |
| 56 | } | 56 | } |
std/c.zig+1-1| ... | @@ -53,7 +53,7 @@ pub extern "c" fn rmdir(path: [*]const u8) c_int; | ... | @@ -53,7 +53,7 @@ pub extern "c" fn rmdir(path: [*]const u8) c_int; |
| 53 | 53 | ||
| 54 | pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void; | 54 | pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void; |
| 55 | pub extern "c" fn malloc(usize) ?*c_void; | 55 | pub extern "c" fn malloc(usize) ?*c_void; |
| 56 | pub extern "c" fn realloc(*c_void, usize) ?*c_void; | 56 | pub extern "c" fn realloc(?*c_void, usize) ?*c_void; |
| 57 | pub extern "c" fn free(*c_void) void; | 57 | pub extern "c" fn free(*c_void) void; |
| 58 | pub extern "c" fn posix_memalign(memptr: **c_void, alignment: usize, size: usize) c_int; | 58 | pub extern "c" fn posix_memalign(memptr: **c_void, alignment: usize, size: usize) c_int; |
| 59 | 59 |
std/debug.zig+1-1| ... | @@ -1072,7 +1072,7 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { | ... | @@ -1072,7 +1072,7 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { |
| 1072 | .n_value = symbols_buf[symbol_index - 1].nlist.n_value + last_len, | 1072 | .n_value = symbols_buf[symbol_index - 1].nlist.n_value + last_len, |
| 1073 | }; | 1073 | }; |
| 1074 | 1074 | ||
| 1075 | const symbols = allocator.shrink(MachoSymbol, symbols_buf, symbol_index); | 1075 | const symbols = allocator.shrink(symbols_buf, symbol_index); |
| 1076 | 1076 | ||
| 1077 | // Even though lld emits symbols in ascending order, this debug code | 1077 | // Even though lld emits symbols in ascending order, this debug code |
| 1078 | // should work for programs linked in any valid way. | 1078 | // should work for programs linked in any valid way. |
std/debug/failing_allocator.zig+14-21| ... | @@ -21,44 +21,37 @@ pub const FailingAllocator = struct { | ... | @@ -21,44 +21,37 @@ pub const FailingAllocator = struct { |
| 21 | .freed_bytes = 0, | 21 | .freed_bytes = 0, |
| 22 | .deallocations = 0, | 22 | .deallocations = 0, |
| 23 | .allocator = mem.Allocator{ | 23 | .allocator = mem.Allocator{ |
| 24 | .allocFn = alloc, | ||
| 25 | .reallocFn = realloc, | 24 | .reallocFn = realloc, |
| 26 | .freeFn = free, | 25 | .shrinkFn = shrink, |
| 27 | }, | 26 | }, |
| 28 | }; | 27 | }; |
| 29 | } | 28 | } |
| 30 | 29 | ||
| 31 | fn alloc(allocator: *mem.Allocator, n: usize, alignment: u29) ![]u8 { | 30 | fn realloc(allocator: *mem.Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { |
| 32 | const self = @fieldParentPtr(FailingAllocator, "allocator", allocator); | 31 | const self = @fieldParentPtr(FailingAllocator, "allocator", allocator); |
| 33 | if (self.index == self.fail_index) { | 32 | if (self.index == self.fail_index) { |
| 34 | return error.OutOfMemory; | 33 | return error.OutOfMemory; |
| 35 | } | 34 | } |
| 36 | const result = try self.internal_allocator.allocFn(self.internal_allocator, n, alignment); | 35 | const result = try self.internal_allocator.reallocFn( |
| 37 | self.allocated_bytes += result.len; | 36 | self.internal_allocator, |
| 38 | self.index += 1; | 37 | old_mem, |
| 39 | return result; | 38 | old_align, |
| 40 | } | 39 | new_size, |
| 41 | 40 | new_align, | |
| 42 | fn realloc(allocator: *mem.Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { | 41 | ); |
| 43 | const self = @fieldParentPtr(FailingAllocator, "allocator", allocator); | ||
| 44 | if (new_size <= old_mem.len) { | 42 | if (new_size <= old_mem.len) { |
| 45 | self.freed_bytes += old_mem.len - new_size; | 43 | self.freed_bytes += old_mem.len - new_size; |
| 46 | return self.internal_allocator.reallocFn(self.internal_allocator, old_mem, new_size, alignment); | 44 | } else { |
| 45 | self.allocated_bytes += new_size - old_mem.len; | ||
| 47 | } | 46 | } |
| 48 | if (self.index == self.fail_index) { | ||
| 49 | return error.OutOfMemory; | ||
| 50 | } | ||
| 51 | const result = try self.internal_allocator.reallocFn(self.internal_allocator, old_mem, new_size, alignment); | ||
| 52 | self.allocated_bytes += new_size - old_mem.len; | ||
| 53 | self.deallocations += 1; | 47 | self.deallocations += 1; |
| 54 | self.index += 1; | 48 | self.index += 1; |
| 55 | return result; | 49 | return result; |
| 56 | } | 50 | } |
| 57 | 51 | ||
| 58 | fn free(allocator: *mem.Allocator, bytes: []u8) void { | 52 | fn shrink(allocator: *mem.Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { |
| 59 | const self = @fieldParentPtr(FailingAllocator, "allocator", allocator); | 53 | const self = @fieldParentPtr(FailingAllocator, "allocator", allocator); |
| 60 | self.freed_bytes += bytes.len; | 54 | self.freed_bytes += old_mem.len - new_size; |
| 61 | self.deallocations += 1; | 55 | return self.internal_allocator.shrinkFn(self.internal_allocator, old_mem, old_align, new_size, new_align); |
| 62 | return self.internal_allocator.freeFn(self.internal_allocator, bytes); | ||
| 63 | } | 56 | } |
| 64 | }; | 57 | }; |
std/heap.zig+133-119| ... | @@ -13,30 +13,21 @@ const Allocator = mem.Allocator; | ... | @@ -13,30 +13,21 @@ const Allocator = mem.Allocator; |
| 13 | 13 | ||
| 14 | pub const c_allocator = &c_allocator_state; | 14 | pub const c_allocator = &c_allocator_state; |
| 15 | var c_allocator_state = Allocator{ | 15 | var c_allocator_state = Allocator{ |
| 16 | .allocFn = cAlloc, | ||
| 17 | .reallocFn = cRealloc, | 16 | .reallocFn = cRealloc, |
| 18 | .freeFn = cFree, | 17 | .shrinkFn = cShrink, |
| 19 | }; | 18 | }; |
| 20 | 19 | ||
| 21 | fn cAlloc(self: *Allocator, n: usize, alignment: u29) ![]u8 { | 20 | fn cRealloc(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { |
| 22 | assert(alignment <= @alignOf(c_longdouble)); | 21 | assert(new_align <= @alignOf(c_longdouble)); |
| 23 | return if (c.malloc(n)) |buf| @ptrCast([*]u8, buf)[0..n] else error.OutOfMemory; | 22 | const old_ptr = if (old_mem.len == 0) null else @ptrCast(*c_void, old_mem.ptr); |
| 23 | const buf = c.realloc(old_ptr, new_size) orelse return error.OutOfMemory; | ||
| 24 | return @ptrCast([*]u8, buf)[0..new_size]; | ||
| 24 | } | 25 | } |
| 25 | 26 | ||
| 26 | fn cRealloc(self: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { | 27 | fn cShrink(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { |
| 27 | const old_ptr = @ptrCast(*c_void, old_mem.ptr); | 28 | const old_ptr = @ptrCast(*c_void, old_mem.ptr); |
| 28 | if (c.realloc(old_ptr, new_size)) |buf| { | 29 | const buf = c.realloc(old_ptr, new_size) orelse return old_mem[0..new_size]; |
| 29 | return @ptrCast([*]u8, buf)[0..new_size]; | 30 | return @ptrCast([*]u8, buf)[0..new_size]; |
| 30 | } else if (new_size <= old_mem.len) { | ||
| 31 | return old_mem[0..new_size]; | ||
| 32 | } else { | ||
| 33 | return error.OutOfMemory; | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | fn cFree(self: *Allocator, old_mem: []u8) void { | ||
| 38 | const old_ptr = @ptrCast(*c_void, old_mem.ptr); | ||
| 39 | c.free(old_ptr); | ||
| 40 | } | 31 | } |
| 41 | 32 | ||
| 42 | /// This allocator makes a syscall directly for every allocation and free. | 33 | /// This allocator makes a syscall directly for every allocation and free. |
| ... | @@ -50,9 +41,8 @@ pub const DirectAllocator = struct { | ... | @@ -50,9 +41,8 @@ pub const DirectAllocator = struct { |
| 50 | pub fn init() DirectAllocator { | 41 | pub fn init() DirectAllocator { |
| 51 | return DirectAllocator{ | 42 | return DirectAllocator{ |
| 52 | .allocator = Allocator{ | 43 | .allocator = Allocator{ |
| 53 | .allocFn = alloc, | ||
| 54 | .reallocFn = realloc, | 44 | .reallocFn = realloc, |
| 55 | .freeFn = free, | 45 | .shrinkFn = shrink, |
| 56 | }, | 46 | }, |
| 57 | .heap_handle = if (builtin.os == Os.windows) null else {}, | 47 | .heap_handle = if (builtin.os == Os.windows) null else {}, |
| 58 | }; | 48 | }; |
| ... | @@ -116,42 +106,60 @@ pub const DirectAllocator = struct { | ... | @@ -116,42 +106,60 @@ pub const DirectAllocator = struct { |
| 116 | } | 106 | } |
| 117 | } | 107 | } |
| 118 | 108 | ||
| 119 | fn realloc(allocator: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { | 109 | fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { |
| 120 | const self = @fieldParentPtr(DirectAllocator, "allocator", allocator); | ||
| 121 | |||
| 122 | switch (builtin.os) { | 110 | switch (builtin.os) { |
| 123 | Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { | 111 | Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { |
| 124 | if (new_size <= old_mem.len) { | 112 | const base_addr = @ptrToInt(old_mem.ptr); |
| 125 | const base_addr = @ptrToInt(old_mem.ptr); | 113 | const old_addr_end = base_addr + old_mem.len; |
| 126 | const old_addr_end = base_addr + old_mem.len; | 114 | const new_addr_end = base_addr + new_size; |
| 127 | const new_addr_end = base_addr + new_size; | 115 | const new_addr_end_rounded = mem.alignForward(new_addr_end, os.page_size); |
| 128 | const new_addr_end_rounded = mem.alignForward(new_addr_end, os.page_size); | 116 | if (old_addr_end > new_addr_end_rounded) { |
| 129 | if (old_addr_end > new_addr_end_rounded) { | 117 | _ = os.posix.munmap(new_addr_end_rounded, old_addr_end - new_addr_end_rounded); |
| 130 | _ = os.posix.munmap(new_addr_end_rounded, old_addr_end - new_addr_end_rounded); | ||
| 131 | } | ||
| 132 | return old_mem[0..new_size]; | ||
| 133 | } | 118 | } |
| 119 | return old_mem[0..new_size]; | ||
| 120 | }, | ||
| 121 | Os.windows => return realloc(allocator, old_mem, old_align, new_size, new_align) catch { | ||
| 122 | const old_adjusted_addr = @ptrToInt(old_mem.ptr); | ||
| 123 | const old_record_addr = old_adjusted_addr + old_mem.len; | ||
| 124 | const root_addr = @intToPtr(*align(1) usize, old_record_addr).*; | ||
| 125 | const old_ptr = @intToPtr(*c_void, root_addr); | ||
| 126 | const new_record_addr = old_record_addr - new_size + old_mem.len; | ||
| 127 | @intToPtr(*align(1) usize, new_record_addr).* = root_addr; | ||
| 128 | return old_mem[0..new_size]; | ||
| 129 | }, | ||
| 130 | else => @compileError("Unsupported OS"), | ||
| 131 | } | ||
| 132 | } | ||
| 134 | 133 | ||
| 135 | const result = try alloc(allocator, new_size, alignment); | 134 | fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { |
| 135 | switch (builtin.os) { | ||
| 136 | Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { | ||
| 137 | if (new_size <= old_mem.len and new_align <= old_align) { | ||
| 138 | return shrink(allocator, old_mem, old_align, new_size, new_align); | ||
| 139 | } | ||
| 140 | const result = try alloc(allocator, new_size, new_align); | ||
| 136 | mem.copy(u8, result, old_mem); | 141 | mem.copy(u8, result, old_mem); |
| 142 | _ = os.posix.munmap(@ptrToInt(old_mem.ptr), old_mem.len); | ||
| 137 | return result; | 143 | return result; |
| 138 | }, | 144 | }, |
| 139 | Os.windows => { | 145 | Os.windows => { |
| 146 | const self = @fieldParentPtr(DirectAllocator, "allocator", allocator); | ||
| 147 | |||
| 140 | const old_adjusted_addr = @ptrToInt(old_mem.ptr); | 148 | const old_adjusted_addr = @ptrToInt(old_mem.ptr); |
| 141 | const old_record_addr = old_adjusted_addr + old_mem.len; | 149 | const old_record_addr = old_adjusted_addr + old_mem.len; |
| 142 | const root_addr = @intToPtr(*align(1) usize, old_record_addr).*; | 150 | const root_addr = @intToPtr(*align(1) usize, old_record_addr).*; |
| 143 | const old_ptr = @intToPtr(*c_void, root_addr); | 151 | const old_ptr = @intToPtr(*c_void, root_addr); |
| 144 | const amt = new_size + alignment + @sizeOf(usize); | 152 | const amt = new_size + new_align + @sizeOf(usize); |
| 145 | const new_ptr = os.windows.HeapReAlloc(self.heap_handle.?, 0, old_ptr, amt) orelse blk: { | 153 | const new_ptr = os.windows.HeapReAlloc( |
| 146 | if (new_size > old_mem.len) return error.OutOfMemory; | 154 | self.heap_handle.?, |
| 147 | const new_record_addr = old_record_addr - new_size + old_mem.len; | 155 | 0, |
| 148 | @intToPtr(*align(1) usize, new_record_addr).* = root_addr; | 156 | old_ptr, |
| 149 | return old_mem[0..new_size]; | 157 | amt, |
| 150 | }; | 158 | ) orelse return error.OutOfMemory; |
| 151 | const offset = old_adjusted_addr - root_addr; | 159 | const offset = old_adjusted_addr - root_addr; |
| 152 | const new_root_addr = @ptrToInt(new_ptr); | 160 | const new_root_addr = @ptrToInt(new_ptr); |
| 153 | const new_adjusted_addr = new_root_addr + offset; | 161 | const new_adjusted_addr = new_root_addr + offset; |
| 154 | assert(new_adjusted_addr % alignment == 0); | 162 | assert(new_adjusted_addr % new_align == 0); |
| 155 | const new_record_addr = new_adjusted_addr + new_size; | 163 | const new_record_addr = new_adjusted_addr + new_size; |
| 156 | @intToPtr(*align(1) usize, new_record_addr).* = new_root_addr; | 164 | @intToPtr(*align(1) usize, new_record_addr).* = new_root_addr; |
| 157 | return @intToPtr([*]u8, new_adjusted_addr)[0..new_size]; | 165 | return @intToPtr([*]u8, new_adjusted_addr)[0..new_size]; |
| ... | @@ -159,23 +167,6 @@ pub const DirectAllocator = struct { | ... | @@ -159,23 +167,6 @@ pub const DirectAllocator = struct { |
| 159 | else => @compileError("Unsupported OS"), | 167 | else => @compileError("Unsupported OS"), |
| 160 | } | 168 | } |
| 161 | } | 169 | } |
| 162 | |||
| 163 | fn free(allocator: *Allocator, bytes: []u8) void { | ||
| 164 | const self = @fieldParentPtr(DirectAllocator, "allocator", allocator); | ||
| 165 | |||
| 166 | switch (builtin.os) { | ||
| 167 | Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { | ||
| 168 | _ = os.posix.munmap(@ptrToInt(bytes.ptr), bytes.len); | ||
| 169 | }, | ||
| 170 | Os.windows => { | ||
| 171 | const record_addr = @ptrToInt(bytes.ptr) + bytes.len; | ||
| 172 | const root_addr = @intToPtr(*align(1) usize, record_addr).*; | ||
| 173 | const ptr = @intToPtr(*c_void, root_addr); | ||
| 174 | _ = os.windows.HeapFree(self.heap_handle.?, 0, ptr); | ||
| 175 | }, | ||
| 176 | else => @compileError("Unsupported OS"), | ||
| 177 | } | ||
| 178 | } | ||
| 179 | }; | 170 | }; |
| 180 | 171 | ||
| 181 | /// This allocator takes an existing allocator, wraps it, and provides an interface | 172 | /// This allocator takes an existing allocator, wraps it, and provides an interface |
| ... | @@ -192,9 +183,8 @@ pub const ArenaAllocator = struct { | ... | @@ -192,9 +183,8 @@ pub const ArenaAllocator = struct { |
| 192 | pub fn init(child_allocator: *Allocator) ArenaAllocator { | 183 | pub fn init(child_allocator: *Allocator) ArenaAllocator { |
| 193 | return ArenaAllocator{ | 184 | return ArenaAllocator{ |
| 194 | .allocator = Allocator{ | 185 | .allocator = Allocator{ |
| 195 | .allocFn = alloc, | ||
| 196 | .reallocFn = realloc, | 186 | .reallocFn = realloc, |
| 197 | .freeFn = free, | 187 | .shrinkFn = shrink, |
| 198 | }, | 188 | }, |
| 199 | .child_allocator = child_allocator, | 189 | .child_allocator = child_allocator, |
| 200 | .buffer_list = std.LinkedList([]u8).init(), | 190 | .buffer_list = std.LinkedList([]u8).init(), |
| ... | @@ -253,17 +243,20 @@ pub const ArenaAllocator = struct { | ... | @@ -253,17 +243,20 @@ pub const ArenaAllocator = struct { |
| 253 | } | 243 | } |
| 254 | } | 244 | } |
| 255 | 245 | ||
| 256 | fn realloc(allocator: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { | 246 | fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { |
| 257 | if (new_size <= old_mem.len) { | 247 | if (new_size <= old_mem.len and new_align <= new_size) { |
| 258 | return old_mem[0..new_size]; | 248 | // We can't do anything with the memory, so tell the client to keep it. |
| 249 | return error.OutOfMemory; | ||
| 259 | } else { | 250 | } else { |
| 260 | const result = try alloc(allocator, new_size, alignment); | 251 | const result = try alloc(allocator, new_size, new_align); |
| 261 | mem.copy(u8, result, old_mem); | 252 | mem.copy(u8, result, old_mem); |
| 262 | return result; | 253 | return result; |
| 263 | } | 254 | } |
| 264 | } | 255 | } |
| 265 | 256 | ||
| 266 | fn free(allocator: *Allocator, bytes: []u8) void {} | 257 | fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { |
| 258 | return old_mem[0..new_size]; | ||
| 259 | } | ||
| 267 | }; | 260 | }; |
| 268 | 261 | ||
| 269 | pub const FixedBufferAllocator = struct { | 262 | pub const FixedBufferAllocator = struct { |
| ... | @@ -274,9 +267,8 @@ pub const FixedBufferAllocator = struct { | ... | @@ -274,9 +267,8 @@ pub const FixedBufferAllocator = struct { |
| 274 | pub fn init(buffer: []u8) FixedBufferAllocator { | 267 | pub fn init(buffer: []u8) FixedBufferAllocator { |
| 275 | return FixedBufferAllocator{ | 268 | return FixedBufferAllocator{ |
| 276 | .allocator = Allocator{ | 269 | .allocator = Allocator{ |
| 277 | .allocFn = alloc, | ||
| 278 | .reallocFn = realloc, | 270 | .reallocFn = realloc, |
| 279 | .freeFn = free, | 271 | .shrinkFn = shrink, |
| 280 | }, | 272 | }, |
| 281 | .buffer = buffer, | 273 | .buffer = buffer, |
| 282 | .end_index = 0, | 274 | .end_index = 0, |
| ... | @@ -298,26 +290,31 @@ pub const FixedBufferAllocator = struct { | ... | @@ -298,26 +290,31 @@ pub const FixedBufferAllocator = struct { |
| 298 | return result; | 290 | return result; |
| 299 | } | 291 | } |
| 300 | 292 | ||
| 301 | fn realloc(allocator: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { | 293 | fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { |
| 302 | const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator); | 294 | const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator); |
| 303 | assert(old_mem.len <= self.end_index); | 295 | assert(old_mem.len <= self.end_index); |
| 304 | if (new_size <= old_mem.len) { | 296 | if (old_mem.ptr == self.buffer.ptr + self.end_index - old_mem.len and |
| 305 | return old_mem[0..new_size]; | 297 | mem.alignForward(@ptrToInt(old_mem.ptr), new_align) == @ptrToInt(old_mem.ptr)) |
| 306 | } else if (old_mem.ptr == self.buffer.ptr + self.end_index - old_mem.len) { | 298 | { |
| 307 | const start_index = self.end_index - old_mem.len; | 299 | const start_index = self.end_index - old_mem.len; |
| 308 | const new_end_index = start_index + new_size; | 300 | const new_end_index = start_index + new_size; |
| 309 | if (new_end_index > self.buffer.len) return error.OutOfMemory; | 301 | if (new_end_index > self.buffer.len) return error.OutOfMemory; |
| 310 | const result = self.buffer[start_index..new_end_index]; | 302 | const result = self.buffer[start_index..new_end_index]; |
| 311 | self.end_index = new_end_index; | 303 | self.end_index = new_end_index; |
| 312 | return result; | 304 | return result; |
| 305 | } else if (new_size <= old_mem.len and new_align <= old_align) { | ||
| 306 | // We can't do anything with the memory, so tell the client to keep it. | ||
| 307 | return error.OutOfMemory; | ||
| 313 | } else { | 308 | } else { |
| 314 | const result = try alloc(allocator, new_size, alignment); | 309 | const result = try alloc(allocator, new_size, new_align); |
| 315 | mem.copy(u8, result, old_mem); | 310 | mem.copy(u8, result, old_mem); |
| 316 | return result; | 311 | return result; |
| 317 | } | 312 | } |
| 318 | } | 313 | } |
| 319 | 314 | ||
| 320 | fn free(allocator: *Allocator, bytes: []u8) void {} | 315 | fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { |
| 316 | return old_mem[0..new_size]; | ||
| 317 | } | ||
| 321 | }; | 318 | }; |
| 322 | 319 | ||
| 323 | pub const ThreadSafeFixedBufferAllocator = blk: { | 320 | pub const ThreadSafeFixedBufferAllocator = blk: { |
| ... | @@ -333,9 +330,8 @@ pub const ThreadSafeFixedBufferAllocator = blk: { | ... | @@ -333,9 +330,8 @@ pub const ThreadSafeFixedBufferAllocator = blk: { |
| 333 | pub fn init(buffer: []u8) ThreadSafeFixedBufferAllocator { | 330 | pub fn init(buffer: []u8) ThreadSafeFixedBufferAllocator { |
| 334 | return ThreadSafeFixedBufferAllocator{ | 331 | return ThreadSafeFixedBufferAllocator{ |
| 335 | .allocator = Allocator{ | 332 | .allocator = Allocator{ |
| 336 | .allocFn = alloc, | ||
| 337 | .reallocFn = realloc, | 333 | .reallocFn = realloc, |
| 338 | .freeFn = free, | 334 | .shrinkFn = shrink, |
| 339 | }, | 335 | }, |
| 340 | .buffer = buffer, | 336 | .buffer = buffer, |
| 341 | .end_index = 0, | 337 | .end_index = 0, |
| ... | @@ -357,17 +353,20 @@ pub const ThreadSafeFixedBufferAllocator = blk: { | ... | @@ -357,17 +353,20 @@ pub const ThreadSafeFixedBufferAllocator = blk: { |
| 357 | } | 353 | } |
| 358 | } | 354 | } |
| 359 | 355 | ||
| 360 | fn realloc(allocator: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { | 356 | fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { |
| 361 | if (new_size <= old_mem.len) { | 357 | if (new_size <= old_mem.len and new_align <= old_align) { |
| 362 | return old_mem[0..new_size]; | 358 | // We can't do anything useful with the memory, tell the client to keep it. |
| 359 | return error.OutOfMemory; | ||
| 363 | } else { | 360 | } else { |
| 364 | const result = try alloc(allocator, new_size, alignment); | 361 | const result = try alloc(allocator, new_size, new_align); |
| 365 | mem.copy(u8, result, old_mem); | 362 | mem.copy(u8, result, old_mem); |
| 366 | return result; | 363 | return result; |
| 367 | } | 364 | } |
| 368 | } | 365 | } |
| 369 | 366 | ||
| 370 | fn free(allocator: *Allocator, bytes: []u8) void {} | 367 | fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { |
| 368 | return old_mem[0..new_size]; | ||
| 369 | } | ||
| 371 | }; | 370 | }; |
| 372 | } | 371 | } |
| 373 | }; | 372 | }; |
| ... | @@ -378,9 +377,8 @@ pub fn stackFallback(comptime size: usize, fallback_allocator: *Allocator) Stack | ... | @@ -378,9 +377,8 @@ pub fn stackFallback(comptime size: usize, fallback_allocator: *Allocator) Stack |
| 378 | .fallback_allocator = fallback_allocator, | 377 | .fallback_allocator = fallback_allocator, |
| 379 | .fixed_buffer_allocator = undefined, | 378 | .fixed_buffer_allocator = undefined, |
| 380 | .allocator = Allocator{ | 379 | .allocator = Allocator{ |
| 381 | .allocFn = StackFallbackAllocator(size).alloc, | ||
| 382 | .reallocFn = StackFallbackAllocator(size).realloc, | 380 | .reallocFn = StackFallbackAllocator(size).realloc, |
| 383 | .freeFn = StackFallbackAllocator(size).free, | 381 | .shrinkFn = StackFallbackAllocator(size).shrink, |
| 384 | }, | 382 | }, |
| 385 | }; | 383 | }; |
| 386 | } | 384 | } |
| ... | @@ -399,13 +397,7 @@ pub fn StackFallbackAllocator(comptime size: usize) type { | ... | @@ -399,13 +397,7 @@ pub fn StackFallbackAllocator(comptime size: usize) type { |
| 399 | return &self.allocator; | 397 | return &self.allocator; |
| 400 | } | 398 | } |
| 401 | 399 | ||
| 402 | fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 { | 400 | fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { |
| 403 | const self = @fieldParentPtr(Self, "allocator", allocator); | ||
| 404 | return FixedBufferAllocator.alloc(&self.fixed_buffer_allocator.allocator, n, alignment) catch | ||
| 405 | self.fallback_allocator.allocFn(self.fallback_allocator, n, alignment); | ||
| 406 | } | ||
| 407 | |||
| 408 | fn realloc(allocator: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { | ||
| 409 | const self = @fieldParentPtr(Self, "allocator", allocator); | 401 | const self = @fieldParentPtr(Self, "allocator", allocator); |
| 410 | const in_buffer = @ptrToInt(old_mem.ptr) >= @ptrToInt(&self.buffer) and | 402 | const in_buffer = @ptrToInt(old_mem.ptr) >= @ptrToInt(&self.buffer) and |
| 411 | @ptrToInt(old_mem.ptr) < @ptrToInt(&self.buffer) + self.buffer.len; | 403 | @ptrToInt(old_mem.ptr) < @ptrToInt(&self.buffer) + self.buffer.len; |
| ... | @@ -413,37 +405,59 @@ pub fn StackFallbackAllocator(comptime size: usize) type { | ... | @@ -413,37 +405,59 @@ pub fn StackFallbackAllocator(comptime size: usize) type { |
| 413 | return FixedBufferAllocator.realloc( | 405 | return FixedBufferAllocator.realloc( |
| 414 | &self.fixed_buffer_allocator.allocator, | 406 | &self.fixed_buffer_allocator.allocator, |
| 415 | old_mem, | 407 | old_mem, |
| 408 | old_align, | ||
| 416 | new_size, | 409 | new_size, |
| 417 | alignment, | 410 | new_align, |
| 418 | ) catch { | 411 | ) catch { |
| 419 | const result = try self.fallback_allocator.allocFn( | 412 | const result = try self.fallback_allocator.reallocFn( |
| 420 | self.fallback_allocator, | 413 | self.fallback_allocator, |
| 414 | ([*]u8)(undefined)[0..0], | ||
| 415 | undefined, | ||
| 421 | new_size, | 416 | new_size, |
| 422 | alignment, | 417 | new_align, |
| 423 | ); | 418 | ); |
| 424 | mem.copy(u8, result, old_mem); | 419 | mem.copy(u8, result, old_mem); |
| 425 | return result; | 420 | return result; |
| 426 | }; | 421 | }; |
| 427 | } | 422 | } |
| 428 | return self.fallback_allocator.reallocFn(self.fallback_allocator, old_mem, new_size, alignment); | 423 | return self.fallback_allocator.reallocFn( |
| 424 | self.fallback_allocator, | ||
| 425 | old_mem, | ||
| 426 | old_align, | ||
| 427 | new_size, | ||
| 428 | new_align, | ||
| 429 | ); | ||
| 429 | } | 430 | } |
| 430 | 431 | ||
| 431 | fn free(allocator: *Allocator, bytes: []u8) void { | 432 | fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { |
| 432 | const self = @fieldParentPtr(Self, "allocator", allocator); | 433 | const self = @fieldParentPtr(Self, "allocator", allocator); |
| 433 | const in_buffer = @ptrToInt(bytes.ptr) >= @ptrToInt(&self.buffer) and | 434 | const in_buffer = @ptrToInt(old_mem.ptr) >= @ptrToInt(&self.buffer) and |
| 434 | @ptrToInt(bytes.ptr) < @ptrToInt(&self.buffer) + self.buffer.len; | 435 | @ptrToInt(old_mem.ptr) < @ptrToInt(&self.buffer) + self.buffer.len; |
| 435 | if (!in_buffer) { | 436 | if (in_buffer) { |
| 436 | return self.fallback_allocator.freeFn(self.fallback_allocator, bytes); | 437 | return FixedBufferAllocator.shrink( |
| 438 | &self.fixed_buffer_allocator.allocator, | ||
| 439 | old_mem, | ||
| 440 | old_align, | ||
| 441 | new_size, | ||
| 442 | new_align, | ||
| 443 | ); | ||
| 437 | } | 444 | } |
| 445 | return self.fallback_allocator.shrinkFn( | ||
| 446 | self.fallback_allocator, | ||
| 447 | old_mem, | ||
| 448 | old_align, | ||
| 449 | new_size, | ||
| 450 | new_align, | ||
| 451 | ); | ||
| 438 | } | 452 | } |
| 439 | }; | 453 | }; |
| 440 | } | 454 | } |
| 441 | 455 | ||
| 442 | test "c_allocator" { | 456 | test "c_allocator" { |
| 443 | if (builtin.link_libc) { | 457 | if (builtin.link_libc) { |
| 444 | var slice = c_allocator.alloc(u8, 50) catch return; | 458 | var slice = try c_allocator.alloc(u8, 50); |
| 445 | defer c_allocator.free(slice); | 459 | defer c_allocator.free(slice); |
| 446 | slice = c_allocator.realloc(u8, slice, 100) catch return; | 460 | slice = try c_allocator.realloc(slice, 100); |
| 447 | } | 461 | } |
| 448 | } | 462 | } |
| 449 | 463 | ||
| ... | @@ -486,10 +500,10 @@ test "FixedBufferAllocator Reuse memory on realloc" { | ... | @@ -486,10 +500,10 @@ test "FixedBufferAllocator Reuse memory on realloc" { |
| 486 | 500 | ||
| 487 | var slice0 = try fixed_buffer_allocator.allocator.alloc(u8, 5); | 501 | var slice0 = try fixed_buffer_allocator.allocator.alloc(u8, 5); |
| 488 | testing.expect(slice0.len == 5); | 502 | testing.expect(slice0.len == 5); |
| 489 | var slice1 = try fixed_buffer_allocator.allocator.realloc(u8, slice0, 10); | 503 | var slice1 = try fixed_buffer_allocator.allocator.realloc(slice0, 10); |
| 490 | testing.expect(slice1.ptr == slice0.ptr); | 504 | testing.expect(slice1.ptr == slice0.ptr); |
| 491 | testing.expect(slice1.len == 10); | 505 | testing.expect(slice1.len == 10); |
| 492 | testing.expectError(error.OutOfMemory, fixed_buffer_allocator.allocator.realloc(u8, slice1, 11)); | 506 | testing.expectError(error.OutOfMemory, fixed_buffer_allocator.allocator.realloc(slice1, 11)); |
| 493 | } | 507 | } |
| 494 | // check that we don't re-use the memory if it's not the most recent block | 508 | // check that we don't re-use the memory if it's not the most recent block |
| 495 | { | 509 | { |
| ... | @@ -499,7 +513,7 @@ test "FixedBufferAllocator Reuse memory on realloc" { | ... | @@ -499,7 +513,7 @@ test "FixedBufferAllocator Reuse memory on realloc" { |
| 499 | slice0[0] = 1; | 513 | slice0[0] = 1; |
| 500 | slice0[1] = 2; | 514 | slice0[1] = 2; |
| 501 | var slice1 = try fixed_buffer_allocator.allocator.alloc(u8, 2); | 515 | var slice1 = try fixed_buffer_allocator.allocator.alloc(u8, 2); |
| 502 | var slice2 = try fixed_buffer_allocator.allocator.realloc(u8, slice0, 4); | 516 | var slice2 = try fixed_buffer_allocator.allocator.realloc(slice0, 4); |
| 503 | testing.expect(slice0.ptr != slice2.ptr); | 517 | testing.expect(slice0.ptr != slice2.ptr); |
| 504 | testing.expect(slice1.ptr != slice2.ptr); | 518 | testing.expect(slice1.ptr != slice2.ptr); |
| 505 | testing.expect(slice2[0] == 1); | 519 | testing.expect(slice2[0] == 1); |
| ... | @@ -523,7 +537,7 @@ fn testAllocator(allocator: *mem.Allocator) !void { | ... | @@ -523,7 +537,7 @@ fn testAllocator(allocator: *mem.Allocator) !void { |
| 523 | item.*.* = @intCast(i32, i); | 537 | item.*.* = @intCast(i32, i); |
| 524 | } | 538 | } |
| 525 | 539 | ||
| 526 | slice = try allocator.realloc(*i32, slice, 20000); | 540 | slice = try allocator.realloc(slice, 20000); |
| 527 | testing.expect(slice.len == 20000); | 541 | testing.expect(slice.len == 20000); |
| 528 | 542 | ||
| 529 | for (slice[0..100]) |item, i| { | 543 | for (slice[0..100]) |item, i| { |
| ... | @@ -531,13 +545,13 @@ fn testAllocator(allocator: *mem.Allocator) !void { | ... | @@ -531,13 +545,13 @@ fn testAllocator(allocator: *mem.Allocator) !void { |
| 531 | allocator.destroy(item); | 545 | allocator.destroy(item); |
| 532 | } | 546 | } |
| 533 | 547 | ||
| 534 | slice = try allocator.realloc(*i32, slice, 50); | 548 | slice = allocator.shrink(slice, 50); |
| 535 | testing.expect(slice.len == 50); | 549 | testing.expect(slice.len == 50); |
| 536 | slice = try allocator.realloc(*i32, slice, 25); | 550 | slice = allocator.shrink(slice, 25); |
| 537 | testing.expect(slice.len == 25); | 551 | testing.expect(slice.len == 25); |
| 538 | slice = try allocator.realloc(*i32, slice, 0); | 552 | slice = allocator.shrink(slice, 0); |
| 539 | testing.expect(slice.len == 0); | 553 | testing.expect(slice.len == 0); |
| 540 | slice = try allocator.realloc(*i32, slice, 10); | 554 | slice = try allocator.realloc(slice, 10); |
| 541 | testing.expect(slice.len == 10); | 555 | testing.expect(slice.len == 10); |
| 542 | 556 | ||
| 543 | allocator.free(slice); | 557 | allocator.free(slice); |
| ... | @@ -548,22 +562,22 @@ fn testAllocatorAligned(allocator: *mem.Allocator, comptime alignment: u29) !voi | ... | @@ -548,22 +562,22 @@ fn testAllocatorAligned(allocator: *mem.Allocator, comptime alignment: u29) !voi |
| 548 | var slice = try allocator.alignedAlloc(u8, alignment, 10); | 562 | var slice = try allocator.alignedAlloc(u8, alignment, 10); |
| 549 | testing.expect(slice.len == 10); | 563 | testing.expect(slice.len == 10); |
| 550 | // grow | 564 | // grow |
| 551 | slice = try allocator.alignedRealloc(u8, alignment, slice, 100); | 565 | slice = try allocator.realloc(slice, 100); |
| 552 | testing.expect(slice.len == 100); | 566 | testing.expect(slice.len == 100); |
| 553 | // shrink | 567 | // shrink |
| 554 | slice = try allocator.alignedRealloc(u8, alignment, slice, 10); | 568 | slice = allocator.shrink(slice, 10); |
| 555 | testing.expect(slice.len == 10); | 569 | testing.expect(slice.len == 10); |
| 556 | // go to zero | 570 | // go to zero |
| 557 | slice = try allocator.alignedRealloc(u8, alignment, slice, 0); | 571 | slice = allocator.shrink(slice, 0); |
| 558 | testing.expect(slice.len == 0); | 572 | testing.expect(slice.len == 0); |
| 559 | // realloc from zero | 573 | // realloc from zero |
| 560 | slice = try allocator.alignedRealloc(u8, alignment, slice, 100); | 574 | slice = try allocator.realloc(slice, 100); |
| 561 | testing.expect(slice.len == 100); | 575 | testing.expect(slice.len == 100); |
| 562 | // shrink with shrink | 576 | // shrink with shrink |
| 563 | slice = allocator.alignedShrink(u8, alignment, slice, 10); | 577 | slice = allocator.shrink(slice, 10); |
| 564 | testing.expect(slice.len == 10); | 578 | testing.expect(slice.len == 10); |
| 565 | // shrink to zero | 579 | // shrink to zero |
| 566 | slice = allocator.alignedShrink(u8, alignment, slice, 0); | 580 | slice = allocator.shrink(slice, 0); |
| 567 | testing.expect(slice.len == 0); | 581 | testing.expect(slice.len == 0); |
| 568 | } | 582 | } |
| 569 | 583 | ||
| ... | @@ -578,19 +592,19 @@ fn testAllocatorLargeAlignment(allocator: *mem.Allocator) mem.Allocator.Error!vo | ... | @@ -578,19 +592,19 @@ fn testAllocatorLargeAlignment(allocator: *mem.Allocator) mem.Allocator.Error!vo |
| 578 | var align_mask: usize = undefined; | 592 | var align_mask: usize = undefined; |
| 579 | _ = @shlWithOverflow(usize, ~usize(0), USizeShift(@ctz(large_align)), &align_mask); | 593 | _ = @shlWithOverflow(usize, ~usize(0), USizeShift(@ctz(large_align)), &align_mask); |
| 580 | 594 | ||
| 581 | var slice = try allocator.allocFn(allocator, 500, large_align); | 595 | var slice = try allocator.alignedAlloc(u8, large_align, 500); |
| 582 | testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); | 596 | testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); |
| 583 | 597 | ||
| 584 | slice = try allocator.reallocFn(allocator, slice, 100, large_align); | 598 | slice = allocator.shrink(slice, 100); |
| 585 | testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); | 599 | testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); |
| 586 | 600 | ||
| 587 | slice = try allocator.reallocFn(allocator, slice, 5000, large_align); | 601 | slice = try allocator.realloc(slice, 5000); |
| 588 | testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); | 602 | testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); |
| 589 | 603 | ||
| 590 | slice = try allocator.reallocFn(allocator, slice, 10, large_align); | 604 | slice = allocator.shrink(slice, 10); |
| 591 | testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); | 605 | testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); |
| 592 | 606 | ||
| 593 | slice = try allocator.reallocFn(allocator, slice, 20000, large_align); | 607 | slice = try allocator.realloc(slice, 20000); |
| 594 | testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); | 608 | testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); |
| 595 | 609 | ||
| 596 | allocator.free(slice); | 610 | allocator.free(slice); |
std/math/big/int.zig+1-1| ... | @@ -60,7 +60,7 @@ pub const Int = struct { | ... | @@ -60,7 +60,7 @@ pub const Int = struct { |
| 60 | return; | 60 | return; |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | self.limbs = try self.allocator.realloc(Limb, self.limbs, capacity); | 63 | self.limbs = try self.allocator.realloc(self.limbs, capacity); |
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | pub fn deinit(self: *Int) void { | 66 | pub fn deinit(self: *Int) void { |
std/mem.zig+132-46| ... | @@ -11,31 +11,64 @@ const testing = std.testing; | ... | @@ -11,31 +11,64 @@ const testing = std.testing; |
| 11 | pub const Allocator = struct { | 11 | pub const Allocator = struct { |
| 12 | pub const Error = error{OutOfMemory}; | 12 | pub const Error = error{OutOfMemory}; |
| 13 | 13 | ||
| 14 | /// Allocate byte_count bytes and return them in a slice, with the | 14 | /// Realloc is used to modify the size or alignment of an existing allocation, |
| 15 | /// slice's pointer aligned at least to alignment bytes. | 15 | /// as well as to provide the allocator with an opportunity to move an allocation |
| 16 | /// The returned newly allocated memory is undefined. | 16 | /// to a better location. |
| 17 | /// `alignment` is guaranteed to be >= 1 | 17 | /// When the size/alignment is greater than the previous allocation, this function |
| 18 | /// `alignment` is guaranteed to be a power of 2 | 18 | /// returns `error.OutOfMemory` when the requested new allocation could not be granted. |
| 19 | allocFn: fn (self: *Allocator, byte_count: usize, alignment: u29) Error![]u8, | 19 | /// When the size/alignment is less than or equal to the previous allocation, |
| 20 | 20 | /// this function returns `error.OutOfMemory` when the allocator decides the client | |
| 21 | /// If `new_byte_count > old_mem.len`: | 21 | /// would be better off keeping the extra alignment/size. Clients will call |
| 22 | /// * `old_mem.len` is the same as what was returned from allocFn or reallocFn. | 22 | /// `shrinkFn` when they require the allocator to track a new alignment/size, |
| 23 | /// * alignment >= alignment of old_mem.ptr | 23 | /// and so this function should only return success when the allocator considers |
| 24 | /// | 24 | /// the reallocation desirable from the allocator's perspective. |
| 25 | /// If `new_byte_count <= old_mem.len`: | 25 | /// As an example, `std.ArrayList` tracks a "capacity", and therefore can handle |
| 26 | /// * this function must return successfully. | 26 | /// reallocation failure, even when `new_n` <= `old_mem.len`. A `FixedBufferAllocator` |
| 27 | /// * alignment <= alignment of old_mem.ptr | 27 | /// would always return `error.OutOfMemory` for `reallocFn` when the size/alignment |
| 28 | /// | 28 | /// is less than or equal to the old allocation, because it cannot reclaim the memory, |
| 29 | /// and thus the `std.ArrayList` would be better off retaining its capacity. | ||
| 29 | /// When `reallocFn` returns, | 30 | /// When `reallocFn` returns, |
| 30 | /// `return_value[0..min(old_mem.len, new_byte_count)]` must be the same | 31 | /// `return_value[0..min(old_mem.len, new_byte_count)]` must be the same |
| 31 | /// as `old_mem` was when `reallocFn` is called. The bytes of | 32 | /// as `old_mem` was when `reallocFn` is called. The bytes of |
| 32 | /// `return_value[old_mem.len..]` have undefined values. | 33 | /// `return_value[old_mem.len..]` have undefined values. |
| 33 | /// `alignment` is guaranteed to be >= 1 | 34 | /// The returned slice must have its pointer aligned at least to `new_alignment` bytes. |
| 34 | /// `alignment` is guaranteed to be a power of 2 | 35 | reallocFn: fn ( |
| 35 | reallocFn: fn (self: *Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8, | 36 | self: *Allocator, |
| 36 | 37 | // Guaranteed to be the same as what was returned from most recent call to | |
| 37 | /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn` | 38 | // `reallocFn` or `shrinkFn`. |
| 38 | freeFn: fn (self: *Allocator, old_mem: []u8) void, | 39 | // If `old_mem.len == 0` then this is a new allocation and `new_byte_count` |
| 40 | // is guaranteed to be >= 1. | ||
| 41 | old_mem: []u8, | ||
| 42 | // If `old_mem.len == 0` then this is `undefined`, otherwise: | ||
| 43 | // Guaranteed to be the same as what was returned from most recent call to | ||
| 44 | // `reallocFn` or `shrinkFn`. | ||
| 45 | // Guaranteed to be >= 1. | ||
| 46 | // Guaranteed to be a power of 2. | ||
| 47 | old_alignment: u29, | ||
| 48 | // If `new_byte_count` is 0 then this is a free and it is guaranteed that | ||
| 49 | // `old_mem.len != 0`. | ||
| 50 | new_byte_count: usize, | ||
| 51 | // Guaranteed to be >= 1. | ||
| 52 | // Guaranteed to be a power of 2. | ||
| 53 | // Returned slice's pointer must have this alignment. | ||
| 54 | new_alignment: u29, | ||
| 55 | ) Error![]u8, | ||
| 56 | |||
| 57 | /// This function deallocates memory. It must succeed. | ||
| 58 | shrinkFn: fn ( | ||
| 59 | self: *Allocator, | ||
| 60 | // Guaranteed to be the same as what was returned from most recent call to | ||
| 61 | // `reallocFn` or `shrinkFn`. | ||
| 62 | old_mem: []u8, | ||
| 63 | // Guaranteed to be the same as what was returned from most recent call to | ||
| 64 | // `reallocFn` or `shrinkFn`. | ||
| 65 | old_alignment: u29, | ||
| 66 | // Guaranteed to be less than or equal to `old_mem.len`. | ||
| 67 | new_byte_count: usize, | ||
| 68 | // If `new_byte_count == 0` then this is `undefined`, otherwise: | ||
| 69 | // Guaranteed to be less than or equal to `old_alignment`. | ||
| 70 | new_alignment: u29, | ||
| 71 | ) []u8, | ||
| 39 | 72 | ||
| 40 | /// Call `destroy` with the result. | 73 | /// Call `destroy` with the result. |
| 41 | /// Returns undefined memory. | 74 | /// Returns undefined memory. |
| ... | @@ -47,20 +80,29 @@ pub const Allocator = struct { | ... | @@ -47,20 +80,29 @@ pub const Allocator = struct { |
| 47 | 80 | ||
| 48 | /// `ptr` should be the return value of `create` | 81 | /// `ptr` should be the return value of `create` |
| 49 | pub fn destroy(self: *Allocator, ptr: var) void { | 82 | pub fn destroy(self: *Allocator, ptr: var) void { |
| 83 | const T = @typeOf(ptr).Child; | ||
| 84 | if (@sizeOf(T) == 0) return; | ||
| 50 | const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr)); | 85 | const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr)); |
| 51 | self.freeFn(self, non_const_ptr[0..@sizeOf(@typeOf(ptr).Child)]); | 86 | const shrink_result = self.shrinkFn(self, non_const_ptr[0..@sizeOf(T)], @alignOf(T), 0, 1); |
| 87 | assert(shrink_result.len == 0); | ||
| 52 | } | 88 | } |
| 53 | 89 | ||
| 54 | pub fn alloc(self: *Allocator, comptime T: type, n: usize) ![]T { | 90 | pub fn alloc(self: *Allocator, comptime T: type, n: usize) ![]T { |
| 55 | return self.alignedAlloc(T, @alignOf(T), n); | 91 | return self.alignedAlloc(T, @alignOf(T), n); |
| 56 | } | 92 | } |
| 57 | 93 | ||
| 58 | pub fn alignedAlloc(self: *Allocator, comptime T: type, comptime alignment: u29, n: usize) ![]align(alignment) T { | 94 | pub fn alignedAlloc( |
| 95 | self: *Allocator, | ||
| 96 | comptime T: type, | ||
| 97 | comptime alignment: u29, | ||
| 98 | n: usize, | ||
| 99 | ) ![]align(alignment) T { | ||
| 59 | if (n == 0) { | 100 | if (n == 0) { |
| 60 | return ([*]align(alignment) T)(undefined)[0..0]; | 101 | return ([*]align(alignment) T)(undefined)[0..0]; |
| 61 | } | 102 | } |
| 103 | |||
| 62 | const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory; | 104 | const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory; |
| 63 | const byte_slice = try self.allocFn(self, byte_count, alignment); | 105 | const byte_slice = try self.reallocFn(self, ([*]u8)(undefined)[0..0], undefined, byte_count, alignment); |
| 64 | assert(byte_slice.len == byte_count); | 106 | assert(byte_slice.len == byte_count); |
| 65 | // This loop gets optimized out in ReleaseFast mode | 107 | // This loop gets optimized out in ReleaseFast mode |
| 66 | for (byte_slice) |*byte| { | 108 | for (byte_slice) |*byte| { |
| ... | @@ -69,62 +111,106 @@ pub const Allocator = struct { | ... | @@ -69,62 +111,106 @@ pub const Allocator = struct { |
| 69 | return @bytesToSlice(T, @alignCast(alignment, byte_slice)); | 111 | return @bytesToSlice(T, @alignCast(alignment, byte_slice)); |
| 70 | } | 112 | } |
| 71 | 113 | ||
| 72 | pub fn realloc(self: *Allocator, comptime T: type, old_mem: []T, n: usize) ![]T { | 114 | /// This function requests a new byte size for an existing allocation, |
| 73 | return self.alignedRealloc(T, @alignOf(T), @alignCast(@alignOf(T), old_mem), n); | 115 | /// which can be larger, smaller, or the same size as the old memory |
| 116 | /// allocation. | ||
| 117 | /// This function is preferred over `shrink`, because it can fail, even | ||
| 118 | /// when shrinking. This gives the allocator a chance to perform a | ||
| 119 | /// cheap shrink operation if possible, or otherwise return OutOfMemory, | ||
| 120 | /// indicating that the caller should keep their capacity, for example | ||
| 121 | /// in `std.ArrayList.shrink`. | ||
| 122 | /// If you need guaranteed success, call `shrink`. | ||
| 123 | /// If `new_n` is 0, this is the same as `free` and it always succeeds. | ||
| 124 | pub fn realloc(self: *Allocator, old_mem: var, new_n: usize) t: { | ||
| 125 | const Slice = @typeInfo(@typeOf(old_mem)).Pointer; | ||
| 126 | break :t Error![]align(Slice.alignment) Slice.child; | ||
| 127 | } { | ||
| 128 | const old_alignment = @typeInfo(@typeOf(old_mem)).Pointer.alignment; | ||
| 129 | return self.alignedRealloc(old_mem, old_alignment, new_n); | ||
| 74 | } | 130 | } |
| 75 | 131 | ||
| 76 | pub fn alignedRealloc(self: *Allocator, comptime T: type, comptime alignment: u29, old_mem: []align(alignment) T, n: usize) ![]align(alignment) T { | 132 | /// This is the same as `realloc`, except caller may additionally request |
| 133 | /// a new alignment, which can be larger, smaller, or the same as the old | ||
| 134 | /// allocation. | ||
| 135 | pub fn alignedRealloc( | ||
| 136 | self: *Allocator, | ||
| 137 | old_mem: var, | ||
| 138 | comptime new_alignment: u29, | ||
| 139 | new_n: usize, | ||
| 140 | ) Error![]align(new_alignment) @typeInfo(@typeOf(old_mem)).Pointer.child { | ||
| 141 | const Slice = @typeInfo(@typeOf(old_mem)).Pointer; | ||
| 142 | const T = Slice.child; | ||
| 77 | if (old_mem.len == 0) { | 143 | if (old_mem.len == 0) { |
| 78 | return self.alignedAlloc(T, alignment, n); | 144 | return self.alignedAlloc(T, new_alignment, new_n); |
| 79 | } | 145 | } |
| 80 | if (n == 0) { | 146 | if (new_n == 0) { |
| 81 | self.free(old_mem); | 147 | self.free(old_mem); |
| 82 | return ([*]align(alignment) T)(undefined)[0..0]; | 148 | return ([*]align(new_alignment) T)(undefined)[0..0]; |
| 83 | } | 149 | } |
| 84 | 150 | ||
| 85 | const old_byte_slice = @sliceToBytes(old_mem); | 151 | const old_byte_slice = @sliceToBytes(old_mem); |
| 86 | const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory; | 152 | const byte_count = math.mul(usize, @sizeOf(T), new_n) catch return Error.OutOfMemory; |
| 87 | const byte_slice = try self.reallocFn(self, old_byte_slice, byte_count, alignment); | 153 | const byte_slice = try self.reallocFn(self, old_byte_slice, Slice.alignment, byte_count, new_alignment); |
| 88 | assert(byte_slice.len == byte_count); | 154 | assert(byte_slice.len == byte_count); |
| 89 | if (n > old_mem.len) { | 155 | if (new_n > old_mem.len) { |
| 90 | // This loop gets optimized out in ReleaseFast mode | 156 | // This loop gets optimized out in ReleaseFast mode |
| 91 | for (byte_slice[old_byte_slice.len..]) |*byte| { | 157 | for (byte_slice[old_byte_slice.len..]) |*byte| { |
| 92 | byte.* = undefined; | 158 | byte.* = undefined; |
| 93 | } | 159 | } |
| 94 | } | 160 | } |
| 95 | return @bytesToSlice(T, @alignCast(alignment, byte_slice)); | 161 | return @bytesToSlice(T, @alignCast(new_alignment, byte_slice)); |
| 96 | } | 162 | } |
| 97 | 163 | ||
| 98 | /// Reallocate, but `n` must be less than or equal to `old_mem.len`. | 164 | /// Prefer calling realloc to shrink if you can tolerate failure, such as |
| 99 | /// Unlike `realloc`, this function cannot fail. | 165 | /// in an ArrayList data structure with a storage capacity. |
| 166 | /// Shrink always succeeds, and `new_n` must be <= `old_mem.len`. | ||
| 167 | /// Returned slice has same alignment as old_mem. | ||
| 100 | /// Shrinking to 0 is the same as calling `free`. | 168 | /// Shrinking to 0 is the same as calling `free`. |
| 101 | pub fn shrink(self: *Allocator, comptime T: type, old_mem: []T, n: usize) []T { | 169 | pub fn shrink(self: *Allocator, old_mem: var, new_n: usize) t: { |
| 102 | return self.alignedShrink(T, @alignOf(T), @alignCast(@alignOf(T), old_mem), n); | 170 | const Slice = @typeInfo(@typeOf(old_mem)).Pointer; |
| 171 | break :t []align(Slice.alignment) Slice.child; | ||
| 172 | } { | ||
| 173 | const old_alignment = @typeInfo(@typeOf(old_mem)).Pointer.alignment; | ||
| 174 | return self.alignedShrink(old_mem, old_alignment, new_n); | ||
| 103 | } | 175 | } |
| 104 | 176 | ||
| 105 | pub fn alignedShrink(self: *Allocator, comptime T: type, comptime alignment: u29, old_mem: []align(alignment) T, n: usize) []align(alignment) T { | 177 | /// This is the same as `shrink`, except caller may additionally request |
| 106 | if (n == 0) { | 178 | /// a new alignment, which must be smaller or the same as the old |
| 179 | /// allocation. | ||
| 180 | pub fn alignedShrink( | ||
| 181 | self: *Allocator, | ||
| 182 | old_mem: var, | ||
| 183 | comptime new_alignment: u29, | ||
| 184 | new_n: usize, | ||
| 185 | ) []align(new_alignment) @typeInfo(@typeOf(old_mem)).Pointer.child { | ||
| 186 | const Slice = @typeInfo(@typeOf(old_mem)).Pointer; | ||
| 187 | const T = Slice.child; | ||
| 188 | |||
| 189 | if (new_n == 0) { | ||
| 107 | self.free(old_mem); | 190 | self.free(old_mem); |
| 108 | return old_mem[0..0]; | 191 | return old_mem[0..0]; |
| 109 | } | 192 | } |
| 110 | 193 | ||
| 111 | assert(n <= old_mem.len); | 194 | assert(new_n <= old_mem.len); |
| 195 | assert(new_alignment <= Slice.alignment); | ||
| 112 | 196 | ||
| 113 | // Here we skip the overflow checking on the multiplication because | 197 | // Here we skip the overflow checking on the multiplication because |
| 114 | // n <= old_mem.len and the multiplication didn't overflow for that operation. | 198 | // new_n <= old_mem.len and the multiplication didn't overflow for that operation. |
| 115 | const byte_count = @sizeOf(T) * n; | 199 | const byte_count = @sizeOf(T) * new_n; |
| 116 | 200 | ||
| 117 | const old_byte_slice = @sliceToBytes(old_mem); | 201 | const old_byte_slice = @sliceToBytes(old_mem); |
| 118 | const byte_slice = self.reallocFn(self, old_byte_slice, byte_count, alignment) catch unreachable; | 202 | const byte_slice = self.shrinkFn(self, old_byte_slice, Slice.alignment, byte_count, new_alignment); |
| 119 | assert(byte_slice.len == byte_count); | 203 | assert(byte_slice.len == byte_count); |
| 120 | return @bytesToSlice(T, @alignCast(alignment, byte_slice)); | 204 | return @bytesToSlice(T, @alignCast(new_alignment, byte_slice)); |
| 121 | } | 205 | } |
| 122 | 206 | ||
| 123 | pub fn free(self: *Allocator, memory: var) void { | 207 | pub fn free(self: *Allocator, memory: var) void { |
| 208 | const Slice = @typeInfo(@typeOf(memory)).Pointer; | ||
| 124 | const bytes = @sliceToBytes(memory); | 209 | const bytes = @sliceToBytes(memory); |
| 125 | if (bytes.len == 0) return; | 210 | if (bytes.len == 0) return; |
| 126 | const non_const_ptr = @intToPtr([*]u8, @ptrToInt(bytes.ptr)); | 211 | const non_const_ptr = @intToPtr([*]u8, @ptrToInt(bytes.ptr)); |
| 127 | self.freeFn(self, non_const_ptr[0..bytes.len]); | 212 | const shrink_result = self.shrinkFn(self, non_const_ptr[0..bytes.len], Slice.alignment, 0, 1); |
| 213 | assert(shrink_result.len == 0); | ||
| 128 | } | 214 | } |
| 129 | }; | 215 | }; |
| 130 | 216 |
std/os.zig+5-5| ... | @@ -814,7 +814,7 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwned | ... | @@ -814,7 +814,7 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwned |
| 814 | } | 814 | } |
| 815 | 815 | ||
| 816 | if (result > buf.len) { | 816 | if (result > buf.len) { |
| 817 | buf = try allocator.realloc(u16, buf, result); | 817 | buf = try allocator.realloc(buf, result); |
| 818 | continue; | 818 | continue; |
| 819 | } | 819 | } |
| 820 | 820 | ||
| ... | @@ -1648,7 +1648,7 @@ pub const Dir = struct { | ... | @@ -1648,7 +1648,7 @@ pub const Dir = struct { |
| 1648 | switch (err) { | 1648 | switch (err) { |
| 1649 | posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, | 1649 | posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, |
| 1650 | posix.EINVAL => { | 1650 | posix.EINVAL => { |
| 1651 | self.handle.buf = try self.allocator.realloc(u8, self.handle.buf, self.handle.buf.len * 2); | 1651 | self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2); |
| 1652 | continue; | 1652 | continue; |
| 1653 | }, | 1653 | }, |
| 1654 | else => return unexpectedErrorPosix(err), | 1654 | else => return unexpectedErrorPosix(err), |
| ... | @@ -1730,7 +1730,7 @@ pub const Dir = struct { | ... | @@ -1730,7 +1730,7 @@ pub const Dir = struct { |
| 1730 | switch (err) { | 1730 | switch (err) { |
| 1731 | posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, | 1731 | posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, |
| 1732 | posix.EINVAL => { | 1732 | posix.EINVAL => { |
| 1733 | self.handle.buf = try self.allocator.realloc(u8, self.handle.buf, self.handle.buf.len * 2); | 1733 | self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2); |
| 1734 | continue; | 1734 | continue; |
| 1735 | }, | 1735 | }, |
| 1736 | else => return unexpectedErrorPosix(err), | 1736 | else => return unexpectedErrorPosix(err), |
| ... | @@ -1784,7 +1784,7 @@ pub const Dir = struct { | ... | @@ -1784,7 +1784,7 @@ pub const Dir = struct { |
| 1784 | switch (err) { | 1784 | switch (err) { |
| 1785 | posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, | 1785 | posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, |
| 1786 | posix.EINVAL => { | 1786 | posix.EINVAL => { |
| 1787 | self.handle.buf = try self.allocator.realloc(u8, self.handle.buf, self.handle.buf.len * 2); | 1787 | self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2); |
| 1788 | continue; | 1788 | continue; |
| 1789 | }, | 1789 | }, |
| 1790 | else => return unexpectedErrorPosix(err), | 1790 | else => return unexpectedErrorPosix(err), |
| ... | @@ -3279,7 +3279,7 @@ pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { | ... | @@ -3279,7 +3279,7 @@ pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { |
| 3279 | } | 3279 | } |
| 3280 | return sum; | 3280 | return sum; |
| 3281 | } else { | 3281 | } else { |
| 3282 | set = try allocator.realloc(usize, set, set.len * 2); | 3282 | set = try allocator.realloc(set, set.len * 2); |
| 3283 | continue; | 3283 | continue; |
| 3284 | } | 3284 | } |
| 3285 | }, | 3285 | }, |
std/os/path.zig+2-2| ... | @@ -565,7 +565,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { | ... | @@ -565,7 +565,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { |
| 565 | result_index += 1; | 565 | result_index += 1; |
| 566 | } | 566 | } |
| 567 | 567 | ||
| 568 | return allocator.shrink(u8, result, result_index); | 568 | return allocator.shrink(result, result_index); |
| 569 | } | 569 | } |
| 570 | 570 | ||
| 571 | /// This function is like a series of `cd` statements executed one after another. | 571 | /// This function is like a series of `cd` statements executed one after another. |
| ... | @@ -634,7 +634,7 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 { | ... | @@ -634,7 +634,7 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 { |
| 634 | result_index += 1; | 634 | result_index += 1; |
| 635 | } | 635 | } |
| 636 | 636 | ||
| 637 | return allocator.shrink(u8, result, result_index); | 637 | return allocator.shrink(result, result_index); |
| 638 | } | 638 | } |
| 639 | 639 | ||
| 640 | test "os.path.resolve" { | 640 | test "os.path.resolve" { |
std/priority_queue.zig+2-1| ... | @@ -141,7 +141,7 @@ pub fn PriorityQueue(comptime T: type) type { | ... | @@ -141,7 +141,7 @@ pub fn PriorityQueue(comptime T: type) type { |
| 141 | better_capacity += better_capacity / 2 + 8; | 141 | better_capacity += better_capacity / 2 + 8; |
| 142 | if (better_capacity >= new_capacity) break; | 142 | if (better_capacity >= new_capacity) break; |
| 143 | } | 143 | } |
| 144 | self.items = try self.allocator.realloc(T, self.items, better_capacity); | 144 | self.items = try self.allocator.realloc(self.items, better_capacity); |
| 145 | } | 145 | } |
| 146 | 146 | ||
| 147 | pub fn resize(self: *Self, new_len: usize) !void { | 147 | pub fn resize(self: *Self, new_len: usize) !void { |
| ... | @@ -150,6 +150,7 @@ pub fn PriorityQueue(comptime T: type) type { | ... | @@ -150,6 +150,7 @@ pub fn PriorityQueue(comptime T: type) type { |
| 150 | } | 150 | } |
| 151 | 151 | ||
| 152 | pub fn shrink(self: *Self, new_len: usize) void { | 152 | pub fn shrink(self: *Self, new_len: usize) void { |
| 153 | // TODO take advantage of the new realloc semantics | ||
| 153 | assert(new_len <= self.len); | 154 | assert(new_len <= self.len); |
| 154 | self.len = new_len; | 155 | self.len = new_len; |
| 155 | } | 156 | } |
std/segmented_list.zig+4-3| ... | @@ -169,11 +169,11 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type | ... | @@ -169,11 +169,11 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type |
| 169 | const new_cap_shelf_count = shelfCount(new_capacity); | 169 | const new_cap_shelf_count = shelfCount(new_capacity); |
| 170 | const old_shelf_count = @intCast(ShelfIndex, self.dynamic_segments.len); | 170 | const old_shelf_count = @intCast(ShelfIndex, self.dynamic_segments.len); |
| 171 | if (new_cap_shelf_count > old_shelf_count) { | 171 | if (new_cap_shelf_count > old_shelf_count) { |
| 172 | self.dynamic_segments = try self.allocator.realloc([*]T, self.dynamic_segments, new_cap_shelf_count); | 172 | self.dynamic_segments = try self.allocator.realloc(self.dynamic_segments, new_cap_shelf_count); |
| 173 | var i = old_shelf_count; | 173 | var i = old_shelf_count; |
| 174 | errdefer { | 174 | errdefer { |
| 175 | self.freeShelves(i, old_shelf_count); | 175 | self.freeShelves(i, old_shelf_count); |
| 176 | self.dynamic_segments = self.allocator.shrink([*]T, self.dynamic_segments, old_shelf_count); | 176 | self.dynamic_segments = self.allocator.shrink(self.dynamic_segments, old_shelf_count); |
| 177 | } | 177 | } |
| 178 | while (i < new_cap_shelf_count) : (i += 1) { | 178 | while (i < new_cap_shelf_count) : (i += 1) { |
| 179 | self.dynamic_segments[i] = (try self.allocator.alloc(T, shelfSize(i))).ptr; | 179 | self.dynamic_segments[i] = (try self.allocator.alloc(T, shelfSize(i))).ptr; |
| ... | @@ -199,11 +199,12 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type | ... | @@ -199,11 +199,12 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type |
| 199 | } | 199 | } |
| 200 | 200 | ||
| 201 | self.freeShelves(old_shelf_count, new_cap_shelf_count); | 201 | self.freeShelves(old_shelf_count, new_cap_shelf_count); |
| 202 | self.dynamic_segments = self.allocator.shrink([*]T, self.dynamic_segments, new_cap_shelf_count); | 202 | self.dynamic_segments = self.allocator.shrink(self.dynamic_segments, new_cap_shelf_count); |
| 203 | } | 203 | } |
| 204 | 204 | ||
| 205 | pub fn shrink(self: *Self, new_len: usize) void { | 205 | pub fn shrink(self: *Self, new_len: usize) void { |
| 206 | assert(new_len <= self.len); | 206 | assert(new_len <= self.len); |
| 207 | // TODO take advantage of the new realloc semantics | ||
| 207 | self.len = new_len; | 208 | self.len = new_len; |
| 208 | } | 209 | } |
| 209 | 210 |
test/tests.zig+11-7| ... | @@ -316,11 +316,13 @@ pub const CompareOutputContext = struct { | ... | @@ -316,11 +316,13 @@ pub const CompareOutputContext = struct { |
| 316 | Term.Exited => |code| { | 316 | Term.Exited => |code| { |
| 317 | if (code != 0) { | 317 | if (code != 0) { |
| 318 | warn("Process {} exited with error code {}\n", full_exe_path, code); | 318 | warn("Process {} exited with error code {}\n", full_exe_path, code); |
| 319 | printInvocation(args.toSliceConst()); | ||
| 319 | return error.TestFailed; | 320 | return error.TestFailed; |
| 320 | } | 321 | } |
| 321 | }, | 322 | }, |
| 322 | else => { | 323 | else => { |
| 323 | warn("Process {} terminated unexpectedly\n", full_exe_path); | 324 | warn("Process {} terminated unexpectedly\n", full_exe_path); |
| 325 | printInvocation(args.toSliceConst()); | ||
| 324 | return error.TestFailed; | 326 | return error.TestFailed; |
| 325 | }, | 327 | }, |
| 326 | } | 328 | } |
| ... | @@ -681,11 +683,13 @@ pub const CompileErrorContext = struct { | ... | @@ -681,11 +683,13 @@ pub const CompileErrorContext = struct { |
| 681 | switch (term) { | 683 | switch (term) { |
| 682 | Term.Exited => |code| { | 684 | Term.Exited => |code| { |
| 683 | if (code == 0) { | 685 | if (code == 0) { |
| 686 | printInvocation(zig_args.toSliceConst()); | ||
| 684 | return error.CompilationIncorrectlySucceeded; | 687 | return error.CompilationIncorrectlySucceeded; |
| 685 | } | 688 | } |
| 686 | }, | 689 | }, |
| 687 | else => { | 690 | else => { |
| 688 | warn("Process {} terminated unexpectedly\n", b.zig_exe); | 691 | warn("Process {} terminated unexpectedly\n", b.zig_exe); |
| 692 | printInvocation(zig_args.toSliceConst()); | ||
| 689 | return error.TestFailed; | 693 | return error.TestFailed; |
| 690 | }, | 694 | }, |
| 691 | } | 695 | } |
| ... | @@ -752,13 +756,6 @@ pub const CompileErrorContext = struct { | ... | @@ -752,13 +756,6 @@ pub const CompileErrorContext = struct { |
| 752 | } | 756 | } |
| 753 | }; | 757 | }; |
| 754 | 758 | ||
| 755 | fn printInvocation(args: []const []const u8) void { | ||
| 756 | for (args) |arg| { | ||
| 757 | warn("{} ", arg); | ||
| 758 | } | ||
| 759 | warn("\n"); | ||
| 760 | } | ||
| 761 | |||
| 762 | pub fn create(self: *CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) *TestCase { | 759 | pub fn create(self: *CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) *TestCase { |
| 763 | const tc = self.b.allocator.create(TestCase) catch unreachable; | 760 | const tc = self.b.allocator.create(TestCase) catch unreachable; |
| 764 | tc.* = TestCase{ | 761 | tc.* = TestCase{ |
| ... | @@ -1240,3 +1237,10 @@ pub const GenHContext = struct { | ... | @@ -1240,3 +1237,10 @@ pub const GenHContext = struct { |
| 1240 | self.step.dependOn(&cmp_h.step); | 1237 | self.step.dependOn(&cmp_h.step); |
| 1241 | } | 1238 | } |
| 1242 | }; | 1239 | }; |
| 1240 | |||
| 1241 | fn printInvocation(args: []const []const u8) void { | ||
| 1242 | for (args) |arg| { | ||
| 1243 | warn("{} ", arg); | ||
| 1244 | } | ||
| 1245 | warn("\n"); | ||
| 1246 | } |