authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-03-15 17:47:47-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-03-15 17:57:21-04:00
log9c13e9b7ed9806d0f9774433d5e24359aff1b238
tree5e595fd2182648e660db56b88b86f162810df8f4
parent4090fe81f600afa290de5bf06a287d5fab2ea9dc
signaturelock-open Commit is signed but in an unrecognized format.

breaking changes to std.mem.Allocator interface API

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 {
13641364
1365 if (str_token[0] == 'c') {1365 if (str_token[0] == 'c') {
1366 // first we add a null1366 // 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;
13691369
1370 // next make an array value1370 // next make an array value
src/all_types.hpp+3-3
...@@ -3356,7 +3356,7 @@ struct IrInstructionCoroPromise {...@@ -3356,7 +3356,7 @@ struct IrInstructionCoroPromise {
3356struct IrInstructionCoroAllocHelper {3356struct IrInstructionCoroAllocHelper {
3357 IrInstruction base;3357 IrInstruction base;
33583358
3359 IrInstruction *alloc_fn;3359 IrInstruction *realloc_fn;
3360 IrInstruction *coro_size;3360 IrInstruction *coro_size;
3361};3361};
33623362
...@@ -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 awaiter3487// 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
37073707
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]), "");
51785178
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;
51825182
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);
51975197
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
5239static LLVMValueRef ir_render_coro_alloc_helper(CodeGen *g, IrExecutable *executable,5246static 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);
52475254
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}
27892789
2790static IrInstruction *ir_build_coro_alloc_helper(IrBuilder *irb, Scope *scope, AstNode *source_node,2790static 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;
27962796
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);
27992799
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);
76457647
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; // self7666 args[0] = implicit_allocator_ptr; // self
7665 args[1] = mem_slice; // old_mem7667 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);
76677678
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
13574static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *call_instruction, ZigFn *fn_entry, ZigType *fn_type,13585static 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);
1358813598
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 }
1359513605
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}
2203422044
22035static IrInstruction *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira, IrInstructionCoroAllocHelper *instruction) {22045static 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;
2203922049
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;
2204322053
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
12861286
1287static void ir_print_coro_alloc_helper(IrPrint *irp, IrInstructionCoroAllocHelper *instruction) {1287static 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 }
148151
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 }
158161
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;
5353
54pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void;54pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void;
55pub extern "c" fn malloc(usize) ?*c_void;55pub extern "c" fn malloc(usize) ?*c_void;
56pub extern "c" fn realloc(*c_void, usize) ?*c_void;56pub extern "c" fn realloc(?*c_void, usize) ?*c_void;
57pub extern "c" fn free(*c_void) void;57pub extern "c" fn free(*c_void) void;
58pub extern "c" fn posix_memalign(memptr: **c_void, alignment: usize, size: usize) c_int;58pub extern "c" fn posix_memalign(memptr: **c_void, alignment: usize, size: usize) c_int;
5959
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 };
10741074
1075 const symbols = allocator.shrink(MachoSymbol, symbols_buf, symbol_index);1075 const symbols = allocator.shrink(symbols_buf, symbol_index);
10761076
1077 // Even though lld emits symbols in ascending order, this debug code1077 // 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 }
3029
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,
4140 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 }
5751
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;
1313
14pub const c_allocator = &c_allocator_state;14pub const c_allocator = &c_allocator_state;
15var c_allocator_state = Allocator{15var c_allocator_state = Allocator{
16 .allocFn = cAlloc,
17 .reallocFn = cRealloc,16 .reallocFn = cRealloc,
18 .freeFn = cFree,17 .shrinkFn = cShrink,
19};18};
2019
21fn cAlloc(self: *Allocator, n: usize, alignment: u29) ![]u8 {20fn 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}
2526
26fn cRealloc(self: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {27fn 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
37fn cFree(self: *Allocator, old_mem: []u8) void {
38 const old_ptr = @ptrCast(*c_void, old_mem.ptr);
39 c.free(old_ptr);
40}31}
4132
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 }
118108
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 }
134133
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};
180171
181/// This allocator takes an existing allocator, wraps it, and provides an interface172/// 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 }
255245
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 }
265256
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};
268261
269pub const FixedBufferAllocator = struct {262pub 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 }
300292
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 }
319314
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};
322319
323pub const ThreadSafeFixedBufferAllocator = blk: {320pub 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 }
359355
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 }
369366
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 }
401399
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) and402 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 }
430431
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) and434 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}
441455
442test "c_allocator" {456test "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}
449463
...@@ -486,10 +500,10 @@ test "FixedBufferAllocator Reuse memory on realloc" {...@@ -486,10 +500,10 @@ test "FixedBufferAllocator Reuse memory on realloc" {
486500
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 block508 // 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 }
525539
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);
528542
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 }
533547
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);
542556
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 // grow564 // 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 // shrink567 // 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 zero570 // 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 zero573 // 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 shrink576 // 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 zero579 // 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}
569583
...@@ -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);
580594
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));
583597
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));
586600
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));
589603
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));
592606
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));
595609
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 }
6262
63 self.limbs = try self.allocator.realloc(Limb, self.limbs, capacity);63 self.limbs = try self.allocator.realloc(self.limbs, capacity);
64 }64 }
6565
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;
11pub const Allocator = struct {11pub const Allocator = struct {
12 pub const Error = error{OutOfMemory};12 pub const Error = error{OutOfMemory};
1313
14 /// Allocate byte_count bytes and return them in a slice, with the14 /// 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 >= 117 /// When the size/alignment is greater than the previous allocation, this function
18 /// `alignment` is guaranteed to be a power of 218 /// 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,
2020 /// 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.ptr23 /// 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.ptr27 /// 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 same31 /// `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 of32 /// 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 >= 134 /// The returned slice must have its pointer aligned at least to `new_alignment` bytes.
34 /// `alignment` is guaranteed to be a power of 235 reallocFn: fn (
35 reallocFn: fn (self: *Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8,36 self: *Allocator,
3637 // 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,
3972
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 {
4780
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 }
5389
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 }
5793
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 mode107 // 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 }
71113
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 }
75131
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 }
84150
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 mode156 // 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 }
97163
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 }
104176
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 }
110193
111 assert(n <= old_mem.len);194 assert(new_n <= old_mem.len);
195 assert(new_alignment <= Slice.alignment);
112196
113 // Here we skip the overflow checking on the multiplication because197 // 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;
116200
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 }
122206
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};
130216
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 }
815815
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 }
820820
...@@ -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 }
567567
568 return allocator.shrink(u8, result, result_index);568 return allocator.shrink(result, result_index);
569}569}
570570
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 }
636636
637 return allocator.shrink(u8, result, result_index);637 return allocator.shrink(result, result_index);
638}638}
639639
640test "os.path.resolve" {640test "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 }
146146
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 }
151151
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 }
200200
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 }
204204
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 }
209210
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 };
754758
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
1241fn printInvocation(args: []const []const u8) void {
1242 for (args) |arg| {
1243 warn("{} ", arg);
1244 }
1245 warn("\n");
1246}