From 43be91bca3bb58dab35506a3f587d6e5ceb4015b Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Sun, 29 Jan 2017 20:48:08 +0000 Subject: [PATCH] Variadic `append` --- code/demo.odin | 7 +--- core/_preload.odin | 14 ++++---- src/check_expr.c | 39 +++++++++++---------- src/checker.c | 2 +- src/ir.c | 87 +++++++++++++++++++++++++++++++++++++++------- 5 files changed, 105 insertions(+), 44 deletions(-) diff --git a/code/demo.odin b/code/demo.odin index 32ff09d33..c52e70f98 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -14,12 +14,7 @@ main :: proc() { defer free(array); reserve(^array, 10); - append(^array, 2); - append(^array, 3); - append(^array, 5); - append(^array, 7); - append(^array, 11); - append(^array, 13); + append(^array, 2, 3, 5, 7, 11, 13); for val, idx in array { fmt.println(val, idx); } diff --git a/core/_preload.odin b/core/_preload.odin index 6417769af..0c10f9cc2 100644 --- a/core/_preload.odin +++ b/core/_preload.odin @@ -352,20 +352,22 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, capa } -__dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int, item_ptr: rawptr) { +__dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int, + items: rawptr, item_count: int) -> int { array := cast(^Raw_Dynamic_Array)array_; ok := true; - if array.data == nil || array.capacity <= array.count { - capacity := 2 * array.capacity + 8; + if array.data == nil || array.capacity <= array.count+item_count { + capacity := 2 * array.capacity + max(8, item_count); ok := __dynamic_array_reserve(array, elem_size, elem_align, capacity); } if !ok { // TODO(bill): Better error handling for failed reservation - return; + return array.count; } data := cast(^byte)array.data; assert(data != nil); - mem.copy(data + (elem_size*array.count), item_ptr, elem_size); - array.count += 1; + mem.copy(data + (elem_size*array.count), items, elem_size * item_count); + array.count += item_count; + return array.count; } diff --git a/src/check_expr.c b/src/check_expr.c index 3e934c77c..f8d966319 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -19,6 +19,7 @@ void check_stmt (Checker *c, AstNode *node, u32 flags); void check_stmt_list (Checker *c, AstNodeArray stmts, u32 flags); void check_init_constant (Checker *c, Entity *e, Operand *operand); bool check_representable_as_constant(Checker *c, ExactValue in_value, Type *type, ExactValue *out_value); +Type * check_call_arguments (Checker *c, Operand *operand, Type *proc_type, AstNode *call); gb_inline Type *check_type(Checker *c, AstNode *expression) { @@ -2598,6 +2599,8 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id } } + Operand prev_operand = *operand; + switch (id) { case BuiltinProc_new: case BuiltinProc_new_slice: @@ -2715,7 +2718,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id } break; case BuiltinProc_append: { - // append :: proc(^[dynamic]Type, item: Type) { + // append :: proc(^[dynamic]Type, item: ...Type) { Type *type = operand->type; if (!is_type_pointer(type)) { gbString str = type_to_string(type); @@ -2730,27 +2733,25 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id gb_string_free(str); return false; } + + // TODO(bill): Semi-memory leaks Type *elem = type->DynamicArray.elem; + Type *slice_elem = make_type_slice(c->allocator, elem); + Type *proc_type_params = make_type_tuple(c->allocator); + proc_type_params->Tuple.variables = gb_alloc_array(c->allocator, Entity *, 2); + proc_type_params->Tuple.variable_count = 2; + proc_type_params->Tuple.variables[0] = make_entity_param(c->allocator, NULL, blank_token, operand->type, false, false); + proc_type_params->Tuple.variables[1] = make_entity_param(c->allocator, NULL, blank_token, slice_elem, false, false); + Type *proc_type = make_type_proc(c->allocator, NULL, proc_type_params, 2, NULL, false, true, ProcCC_Odin); - AstNode *item = ce->args.e[1]; - Operand op = {0}; - check_expr(c, &op, item); - if (op.mode == Addressing_Invalid) { + check_call_arguments(c, &prev_operand, proc_type, call); + + if (prev_operand.mode == Addressing_Invalid) { return false; } - Type *arg_type = op.type; - if (!check_is_assignable_to(c, &op, elem)) { - gbString elem_str = type_to_string(elem); - gbString str = type_to_string(arg_type); - error_node(operand->expr, "Expected `%s` for `append` item, got `%s`", elem_str, str); - gb_string_free(str); - gb_string_free(elem_str); - return false; - } - - operand->type = NULL; - operand->mode = Addressing_NoValue; + operand->mode = Addressing_Value; + operand->type = t_int; } break; @@ -3284,7 +3285,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id return false; } - if (!are_types_identical(operand->type, b.type)) { + if (!are_types_identical(a.type, b.type)) { gbString type_a = type_to_string(a.type); gbString type_b = type_to_string(b.type); error_node(call, @@ -3352,7 +3353,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id return false; } - if (!are_types_identical(operand->type, b.type)) { + if (!are_types_identical(a.type, b.type)) { gbString type_a = type_to_string(a.type); gbString type_b = type_to_string(b.type); error_node(call, diff --git a/src/checker.c b/src/checker.c index 7740a6711..ef28c6d20 100644 --- a/src/checker.c +++ b/src/checker.c @@ -173,7 +173,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = { {STR_LIT("free"), 1, false, Expr_Stmt}, {STR_LIT("reserve"), 2, false, Expr_Stmt}, - {STR_LIT("append"), 2, false, Expr_Stmt}, + {STR_LIT("append"), 1, true, Expr_Expr}, {STR_LIT("size_of"), 1, false, Expr_Expr}, {STR_LIT("size_of_val"), 1, false, Expr_Expr}, diff --git a/src/ir.c b/src/ir.c index 6c01d2e12..00237d90c 100644 --- a/src/ir.c +++ b/src/ir.c @@ -3025,23 +3025,86 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv GB_ASSERT(is_type_pointer(type)); type = base_type(type_deref(type)); GB_ASSERT(is_type_dynamic_array(type)); - Type *elem = type->DynamicArray.elem; + Type *elem_type = type->DynamicArray.elem; - irValue *elem_size = ir_make_const_int(a, type_size_of(proc->module->sizes, a, elem)); - irValue *elem_align = ir_make_const_int(a, type_align_of(proc->module->sizes, a, elem)); + irValue *elem_size = ir_make_const_int(a, type_size_of(proc->module->sizes, a, elem_type)); + irValue *elem_align = ir_make_const_int(a, type_align_of(proc->module->sizes, a, elem_type)); array_ptr = ir_emit_conv(proc, array_ptr, t_rawptr); - irValue *item_ptr = ir_add_local_generated(proc, elem); - irValue *item = ir_build_expr(proc, ce->args.e[1]); - ir_emit_store(proc, item_ptr, item); + isize arg_index = 0; + isize arg_count = 0; + for_array(i, ce->args) { + if (i == 0) { + continue; + } + AstNode *a = ce->args.e[i]; + Type *at = base_type(type_of_expr(proc->module->info, a)); + if (at->kind == Type_Tuple) { + arg_count += at->Tuple.variable_count; + } else { + arg_count++; + } + } + irValue **args = gb_alloc_array(proc->module->allocator, irValue *, arg_count); + bool vari_expand = ce->ellipsis.pos.line != 0; - irValue **args = gb_alloc_array(a, irValue *, 4); - args[0] = array_ptr; - args[1] = elem_size; - args[2] = elem_align; - args[3] = ir_emit_conv(proc, item_ptr, t_rawptr); - return ir_emit_global_call(proc, "__dynamic_array_append", args, 4); + for_array(i, ce->args) { + irValue *a = ir_build_expr(proc, ce->args.e[i]); + Type *at = ir_type(a); + if (at->kind == Type_Tuple) { + for (isize i = 0; i < at->Tuple.variable_count; i++) { + Entity *e = at->Tuple.variables[i]; + irValue *v = ir_emit_struct_ev(proc, a, i); + args[arg_index++] = v; + } + } else { + args[arg_index++] = a; + } + } + + if (!vari_expand) { + for (isize i = 1; i < arg_count; i++) { + args[i] = ir_emit_conv(proc, args[i], elem_type); + } + } + + if (!vari_expand) { + ir_emit_comment(proc, str_lit("variadic call argument generation")); + Type *slice_type = make_type_slice(a, elem_type); + irValue *slice = ir_add_local_generated(proc, slice_type); + isize slice_len = arg_count-1; + + if (slice_len > 0) { + irValue *base_array = ir_add_local_generated(proc, make_type_array(a, elem_type, slice_len)); + + for (isize i = 1, j = 0; i < arg_count; i++, j++) { + irValue *addr = ir_emit_array_epi(proc, base_array, j); + ir_emit_store(proc, addr, args[i]); + } + + irValue *base_elem = ir_emit_array_epi(proc, base_array, 0); + irValue *slice_elem = ir_emit_struct_ep(proc, slice, 0); + ir_emit_store(proc, slice_elem, base_elem); + irValue *len = ir_make_const_int(a, slice_len); + ir_emit_store(proc, ir_emit_struct_ep(proc, slice, 1), len); + } + + arg_count = 2; + args[arg_count-1] = ir_emit_load(proc, slice); + } + + irValue *item_slice = args[1]; + irValue *items = ir_slice_elem(proc, item_slice); + irValue *item_count = ir_slice_count(proc, item_slice); + + irValue **daa_args = gb_alloc_array(a, irValue *, 5); + daa_args[0] = array_ptr; + daa_args[1] = elem_size; + daa_args[2] = elem_align; + daa_args[3] = ir_emit_conv(proc, items, t_rawptr); + daa_args[4] = ir_emit_conv(proc, item_count, t_int); + return ir_emit_global_call(proc, "__dynamic_array_append", daa_args, 5); } break;