aboutsummaryrefslogtreecommitdiff
path: root/src/ir.cpp
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2019-03-15 17:47:47 -0400
committerAndrew Kelley <andrew@ziglang.org>2019-03-15 17:57:21 -0400
commit9c13e9b7ed9806d0f9774433d5e24359aff1b238 (patch)
tree5e595fd2182648e660db56b88b86f162810df8f4 /src/ir.cpp
parent4090fe81f600afa290de5bf06a287d5fab2ea9dc (diff)
downloadzig-9c13e9b7ed9806d0f9774433d5e24359aff1b238.tar.gz
zig-9c13e9b7ed9806d0f9774433d5e24359aff1b238.zip
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.
Diffstat (limited to 'src/ir.cpp')
-rw-r--r--src/ir.cpp70
1 files changed, 40 insertions, 30 deletions
diff --git a/src/ir.cpp b/src/ir.cpp
index 61be6670a5..dc6118f453 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -2788,13 +2788,13 @@ static IrInstruction *ir_build_coro_promise(IrBuilder *irb, Scope *scope, AstNod
}
static IrInstruction *ir_build_coro_alloc_helper(IrBuilder *irb, Scope *scope, AstNode *source_node,
- IrInstruction *alloc_fn, IrInstruction *coro_size)
+ IrInstruction *realloc_fn, IrInstruction *coro_size)
{
IrInstructionCoroAllocHelper *instruction = ir_build_instruction<IrInstructionCoroAllocHelper>(irb, scope, source_node);
- instruction->alloc_fn = alloc_fn;
+ instruction->realloc_fn = realloc_fn;
instruction->coro_size = coro_size;
- ir_ref_instruction(alloc_fn, irb->current_basic_block);
+ ir_ref_instruction(realloc_fn, irb->current_basic_block);
ir_ref_instruction(coro_size, irb->current_basic_block);
return &instruction->base;
@@ -3319,9 +3319,11 @@ static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_s
if (!skip_name_check) {
ZigVar *existing_var = find_variable(codegen, parent_scope, name, nullptr);
if (existing_var && !existing_var->shadowable) {
- ErrorMsg *msg = add_node_error(codegen, node,
- buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
- add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here"));
+ if (existing_var->var_type == nullptr || !type_is_invalid(existing_var->var_type)) {
+ ErrorMsg *msg = add_node_error(codegen, node,
+ buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
+ add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here"));
+ }
variable_entry->var_type = codegen->builtin_types.entry_invalid;
} else {
ZigType *type;
@@ -7506,10 +7508,10 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
ImplicitAllocatorIdArg);
irb->exec->coro_allocator_var = ir_create_var(irb, node, coro_scope, nullptr, true, true, true, const_bool_false);
ir_build_var_decl_src(irb, coro_scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, implicit_allocator_ptr);
- Buf *alloc_field_name = buf_create_from_str(ASYNC_ALLOC_FIELD_NAME);
- IrInstruction *alloc_fn_ptr = ir_build_field_ptr(irb, coro_scope, node, implicit_allocator_ptr, alloc_field_name);
- IrInstruction *alloc_fn = ir_build_load_ptr(irb, coro_scope, node, alloc_fn_ptr);
- IrInstruction *maybe_coro_mem_ptr = ir_build_coro_alloc_helper(irb, coro_scope, node, alloc_fn, coro_size);
+ Buf *realloc_field_name = buf_create_from_str(ASYNC_REALLOC_FIELD_NAME);
+ IrInstruction *realloc_fn_ptr = ir_build_field_ptr(irb, coro_scope, node, implicit_allocator_ptr, realloc_field_name);
+ IrInstruction *realloc_fn = ir_build_load_ptr(irb, coro_scope, node, realloc_fn_ptr);
+ IrInstruction *maybe_coro_mem_ptr = ir_build_coro_alloc_helper(irb, coro_scope, node, realloc_fn, coro_size);
IrInstruction *alloc_result_is_ok = ir_build_test_nonnull(irb, coro_scope, node, maybe_coro_mem_ptr);
IrBasicBlock *alloc_err_block = ir_create_basic_block(irb, coro_scope, "AllocError");
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
merge_incoming_values[1] = await_handle_in_block;
IrInstruction *awaiter_handle = ir_build_phi(irb, scope, node, 2, merge_incoming_blocks, merge_incoming_values);
- Buf *free_field_name = buf_create_from_str(ASYNC_FREE_FIELD_NAME);
+ Buf *shrink_field_name = buf_create_from_str(ASYNC_SHRINK_FIELD_NAME);
IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, scope, node,
ImplicitAllocatorIdLocalVar);
- IrInstruction *free_fn_ptr = ir_build_field_ptr(irb, scope, node, implicit_allocator_ptr, free_field_name);
- IrInstruction *free_fn = ir_build_load_ptr(irb, scope, node, free_fn_ptr);
+ IrInstruction *shrink_fn_ptr = ir_build_field_ptr(irb, scope, node, implicit_allocator_ptr, shrink_field_name);
+ IrInstruction *shrink_fn = ir_build_load_ptr(irb, scope, node, shrink_fn_ptr);
IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
IrInstruction *coro_mem_ptr_maybe = ir_build_coro_free(irb, scope, node, coro_id, irb->exec->coro_handle);
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
IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var);
IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr);
IrInstruction *mem_slice = ir_build_slice(irb, scope, node, coro_mem_ptr_ref, zero, coro_size, false);
- size_t arg_count = 2;
+ size_t arg_count = 5;
IrInstruction **args = allocate<IrInstruction *>(arg_count);
args[0] = implicit_allocator_ptr; // self
args[1] = mem_slice; // old_mem
- ir_build_call(irb, scope, node, nullptr, free_fn, arg_count, args, false, FnInlineAuto, false, nullptr, nullptr);
+ args[2] = ir_build_const_usize(irb, scope, node, 8); // old_align
+ // TODO: intentional memory leak here. If this is set to 0 then there is an issue where a coroutine
+ // calls the function and it frees its own stack frame, but then the return value 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.
+ args[3] = ir_build_const_usize(irb, scope, node, 1); // new_size
+ args[4] = ir_build_const_usize(irb, scope, node, 1); // new_align
+ ir_build_call(irb, scope, node, nullptr, shrink_fn, arg_count, args, false, FnInlineAuto, false, nullptr, nullptr);
IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "Resume");
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
static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *call_instruction, ZigFn *fn_entry, ZigType *fn_type,
IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count, IrInstruction *async_allocator_inst)
{
- Buf *alloc_field_name = buf_create_from_str(ASYNC_ALLOC_FIELD_NAME);
- //Buf *free_field_name = buf_create_from_str("freeFn");
+ Buf *realloc_field_name = buf_create_from_str(ASYNC_REALLOC_FIELD_NAME);
assert(async_allocator_inst->value.type->id == ZigTypeIdPointer);
ZigType *container_type = async_allocator_inst->value.type->data.pointer.child_type;
- IrInstruction *field_ptr_inst = ir_analyze_container_field_ptr(ira, alloc_field_name, &call_instruction->base,
+ IrInstruction *field_ptr_inst = ir_analyze_container_field_ptr(ira, realloc_field_name, &call_instruction->base,
async_allocator_inst, container_type);
if (type_is_invalid(field_ptr_inst->value.type)) {
return ira->codegen->invalid_instruction;
}
- ZigType *ptr_to_alloc_fn_type = field_ptr_inst->value.type;
- assert(ptr_to_alloc_fn_type->id == ZigTypeIdPointer);
+ ZigType *ptr_to_realloc_fn_type = field_ptr_inst->value.type;
+ assert(ptr_to_realloc_fn_type->id == ZigTypeIdPointer);
- ZigType *alloc_fn_type = ptr_to_alloc_fn_type->data.pointer.child_type;
- if (alloc_fn_type->id != ZigTypeIdFn) {
+ ZigType *realloc_fn_type = ptr_to_realloc_fn_type->data.pointer.child_type;
+ if (realloc_fn_type->id != ZigTypeIdFn) {
ir_add_error(ira, &call_instruction->base,
- buf_sprintf("expected allocation function, found '%s'", buf_ptr(&alloc_fn_type->name)));
+ buf_sprintf("expected reallocation function, found '%s'", buf_ptr(&realloc_fn_type->name)));
return ira->codegen->invalid_instruction;
}
- ZigType *alloc_fn_return_type = alloc_fn_type->data.fn.fn_type_id.return_type;
- if (alloc_fn_return_type->id != ZigTypeIdErrorUnion) {
+ ZigType *realloc_fn_return_type = realloc_fn_type->data.fn.fn_type_id.return_type;
+ if (realloc_fn_return_type->id != ZigTypeIdErrorUnion) {
ir_add_error(ira, fn_ref,
- buf_sprintf("expected allocation function to return error union, but it returns '%s'", buf_ptr(&alloc_fn_return_type->name)));
+ buf_sprintf("expected allocation function to return error union, but it returns '%s'", buf_ptr(&realloc_fn_return_type->name)));
return ira->codegen->invalid_instruction;
}
- ZigType *alloc_fn_error_set_type = alloc_fn_return_type->data.error_union.err_set_type;
+ ZigType *alloc_fn_error_set_type = realloc_fn_return_type->data.error_union.err_set_type;
ZigType *return_type = fn_type->data.fn.fn_type_id.return_type;
ZigType *promise_type = get_promise_type(ira->codegen, return_type);
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
}
static IrInstruction *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira, IrInstructionCoroAllocHelper *instruction) {
- IrInstruction *alloc_fn = instruction->alloc_fn->child;
- if (type_is_invalid(alloc_fn->value.type))
+ IrInstruction *realloc_fn = instruction->realloc_fn->child;
+ if (type_is_invalid(realloc_fn->value.type))
return ira->codegen->invalid_instruction;
IrInstruction *coro_size = instruction->coro_size->child;
@@ -22042,7 +22052,7 @@ static IrInstruction *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira, I
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_build_coro_alloc_helper(&ira->new_irb, instruction->base.scope,
- instruction->base.source_node, alloc_fn, coro_size);
+ instruction->base.source_node, realloc_fn, coro_size);
ZigType *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, false);
result->value.type = get_optional_type(ira->codegen, u8_ptr_type);
return result;