From 0f3562ef021fc1042d651d5ad1f834af67d2d001 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 11 Aug 2022 16:01:46 +0100 Subject: Improve compound literal generation for array-like types --- src/llvm_backend_expr.cpp | 2210 +++++++++++++++++++++------------------------ 1 file changed, 1039 insertions(+), 1171 deletions(-) (limited to 'src/llvm_backend_expr.cpp') diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 7568f975e..0cf2b3c03 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -3491,1415 +3491,1283 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { return addr; } +void lb_build_addr_compound_lit_populate(lbProcedure *p, Slice const &elems, Array *temp_data, Type *compound_type) { + Type *bt = base_type(compound_type); + Type *et = nullptr; + switch (bt->kind) { + case Type_Array: et = bt->Array.elem; break; + case Type_EnumeratedArray: et = bt->EnumeratedArray.elem; break; + case Type_Slice: et = bt->Slice.elem; break; + case Type_BitSet: et = bt->BitSet.elem; break; + case Type_DynamicArray: et = bt->DynamicArray.elem; break; + case Type_SimdVector: et = bt->SimdVector.elem; break; + case Type_Matrix: et = bt->Matrix.elem; break; + } + GB_ASSERT(et != nullptr); + + + // NOTE(bill): Separate value, gep, store into their own chunks + for_array(i, elems) { + Ast *elem = elems[i]; + if (elem->kind == Ast_FieldValue) { + ast_node(fv, FieldValue, elem); + if (lb_is_elem_const(fv->value, et)) { + continue; + } + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); + + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op != Token_RangeHalf) { + hi += 1; + } -lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) { - switch (expr->kind) { - case_ast_node(i, Implicit, expr); - lbAddr v = {}; - switch (i->kind) { - case Token_context: - v = lb_find_or_generate_context_ptr(p); - break; - } - - GB_ASSERT(v.addr.value != nullptr); - return v; - case_end; - - case_ast_node(i, Ident, expr); - if (is_blank_ident(expr)) { - lbAddr val = {}; - return val; - } - String name = i->token.string; - Entity *e = entity_of_node(expr); - return lb_build_addr_from_entity(p, e, expr); - case_end; + lbValue value = lb_emit_conv(p, lb_build_expr(p, fv->value), et); - case_ast_node(se, SelectorExpr, expr); - Ast *sel_node = unparen_expr(se->selector); - if (sel_node->kind == Ast_Ident) { - String selector = sel_node->Ident.token.string; - TypeAndValue tav = type_and_value_of_expr(se->expr); + GB_ASSERT((hi-lo) > 0); - if (tav.mode == Addressing_Invalid) { - // NOTE(bill): Imports - Entity *imp = entity_of_node(se->expr); - if (imp != nullptr) { - GB_ASSERT(imp->kind == Entity_ImportName); + enum {MAX_ELEMENT_AMOUNT = 32}; + if ((hi-lo) <= MAX_ELEMENT_AMOUNT) { + for (i64 k = lo; k < hi; k++) { + lbCompoundLitElemTempData data = {}; + data.value = value; + data.elem_index = k; + array_add(temp_data, data); + } + } else { + lbCompoundLitElemTempData data = {}; + data.value = value; + data.elem_index = lo; + data.elem_length = hi-lo; + array_add(temp_data, data); } - return lb_build_addr(p, unparen_expr(se->selector)); + } else { + auto tav = fv->field->tav; + GB_ASSERT(tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(tav.value); + + lbValue value = lb_emit_conv(p, lb_build_expr(p, fv->value), et); + GB_ASSERT(!is_type_tuple(value.type)); + + lbCompoundLitElemTempData data = {}; + data.value = value; + data.expr = fv->value; + data.elem_index = cast(i32)index; + array_add(temp_data, data); } + } else { + if (lb_is_elem_const(elem, et)) { + continue; + } - Type *type = base_type(tav.type); - if (tav.mode == Addressing_Type) { // Addressing_Type - Selection sel = lookup_field(tav.type, selector, true); - if (sel.pseudo_field) { - GB_ASSERT(sel.entity->kind == Entity_Procedure); - return lb_addr(lb_find_value_from_entity(p->module, sel.entity)); + lbValue field_expr = lb_build_expr(p, elem); + GB_ASSERT(!is_type_tuple(field_expr.type)); + + lbValue ev = lb_emit_conv(p, field_expr, et); + + lbCompoundLitElemTempData data = {}; + data.value = ev; + data.elem_index = cast(i32)i; + array_add(temp_data, data); + } + } +} +void lb_build_addr_compound_lit_assign_array(lbProcedure *p, Array &temp_data) { + for_array(i, temp_data) { + auto td = temp_data[i]; + if (td.value.value != nullptr) { + if (td.elem_length > 0) { + auto loop_data = lb_loop_start(p, cast(isize)td.elem_length, t_i32); + { + lbValue dst = temp_data[i].gep; + dst = lb_emit_ptr_offset(p, dst, loop_data.idx); + lb_emit_store(p, dst, temp_data[i].value); } - GB_PANIC("Unreachable %.*s", LIT(selector)); + lb_loop_end(p, loop_data); + } else { + lb_emit_store(p, temp_data[i].gep, temp_data[i].value); } + } + } +} - if (se->swizzle_count > 0) { - Type *array_type = base_type(type_deref(tav.type)); - GB_ASSERT(array_type->kind == Type_Array); - u8 swizzle_count = se->swizzle_count; - u8 swizzle_indices_raw = se->swizzle_indices; - u8 swizzle_indices[4] = {}; - for (u8 i = 0; i < swizzle_count; i++) { - u8 index = swizzle_indices_raw>>(i*2) & 3; - swizzle_indices[i] = index; - } - lbValue a = {}; - if (is_type_pointer(tav.type)) { - a = lb_build_expr(p, se->expr); - } else { - lbAddr addr = lb_build_addr(p, se->expr); - a = lb_addr_get_ptr(p, addr); - } - GB_ASSERT(is_type_array(expr->tav.type)); - return lb_addr_swizzle(a, expr->tav.type, swizzle_count, swizzle_indices); - } +lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) { + ast_node(cl, CompoundLit, expr); - Selection sel = lookup_field(type, selector, false); - GB_ASSERT(sel.entity != nullptr); - if (sel.pseudo_field) { - GB_ASSERT(sel.entity->kind == Entity_Procedure); - Entity *e = entity_of_node(sel_node); - return lb_addr(lb_find_value_from_entity(p->module, e)); - } + Type *type = type_of_expr(expr); + Type *bt = base_type(type); - { - lbAddr addr = lb_build_addr(p, se->expr); - if (addr.kind == lbAddr_Map) { - lbValue v = lb_addr_load(p, addr); - lbValue a = lb_address_from_load_or_generate_local(p, v); - a = lb_emit_deep_field_gep(p, a, sel); - return lb_addr(a); - } else if (addr.kind == lbAddr_Context) { - GB_ASSERT(sel.index.count > 0); - if (addr.ctx.sel.index.count >= 0) { - sel = selection_combine(addr.ctx.sel, sel); - } - addr.ctx.sel = sel; - addr.kind = lbAddr_Context; - return addr; - } else if (addr.kind == lbAddr_SoaVariable) { - lbValue index = addr.soa.index; - i32 first_index = sel.index[0]; - Selection sub_sel = sel; - sub_sel.index.data += 1; - sub_sel.index.count -= 1; + lbAddr v = lb_add_local_generated(p, type, true); - lbValue arr = lb_emit_struct_ep(p, addr.addr, first_index); + Type *et = nullptr; + switch (bt->kind) { + case Type_Array: et = bt->Array.elem; break; + case Type_EnumeratedArray: et = bt->EnumeratedArray.elem; break; + case Type_Slice: et = bt->Slice.elem; break; + case Type_BitSet: et = bt->BitSet.elem; break; + case Type_SimdVector: et = bt->SimdVector.elem; break; + case Type_Matrix: et = bt->Matrix.elem; break; + } - Type *t = base_type(type_deref(addr.addr.type)); - GB_ASSERT(is_type_soa_struct(t)); + String proc_name = {}; + if (p->entity) { + proc_name = p->entity->token.string; + } + TokenPos pos = ast_token(expr).pos; - if (addr.soa.index_expr != nullptr && (!lb_is_const(addr.soa.index) || t->Struct.soa_kind != StructSoa_Fixed)) { - lbValue len = lb_soa_struct_len(p, addr.addr); - lb_emit_bounds_check(p, ast_token(addr.soa.index_expr), addr.soa.index, len); - } + switch (bt->kind) { + default: GB_PANIC("Unknown CompoundLit type: %s", type_to_string(type)); break; - lbValue item = {}; + case Type_Struct: { + // TODO(bill): "constant" '#raw_union's are not initialized constantly at the moment. + // NOTE(bill): This is due to the layout of the unions when printed to LLVM-IR + bool is_raw_union = is_type_raw_union(bt); + GB_ASSERT(is_type_struct(bt) || is_raw_union); + TypeStruct *st = &bt->Struct; + if (cl->elems.count > 0) { + lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr))); + lbValue comp_lit_ptr = lb_addr_get_ptr(p, v); - if (t->Struct.soa_kind == StructSoa_Fixed) { - item = lb_emit_array_ep(p, arr, index); - } else { - item = lb_emit_ptr_offset(p, lb_emit_load(p, arr), index); - } - if (sub_sel.index.count > 0) { - item = lb_emit_deep_field_gep(p, item, sub_sel); - } - return lb_addr(item); - } else if (addr.kind == lbAddr_Swizzle) { - GB_ASSERT(sel.index.count > 0); - // NOTE(bill): just patch the index in place - sel.index[0] = addr.swizzle.indices[sel.index[0]]; - } else if (addr.kind == lbAddr_SwizzleLarge) { - GB_ASSERT(sel.index.count > 0); - // NOTE(bill): just patch the index in place - sel.index[0] = addr.swizzle.indices[sel.index[0]]; + for_array(field_index, cl->elems) { + Ast *elem = cl->elems[field_index]; + + lbValue field_expr = {}; + Entity *field = nullptr; + isize index = field_index; + + if (elem->kind == Ast_FieldValue) { + ast_node(fv, FieldValue, elem); + String name = fv->field->Ident.token.string; + Selection sel = lookup_field(bt, name, false); + index = sel.index[0]; + elem = fv->value; + TypeAndValue tav = type_and_value_of_expr(elem); + } else { + TypeAndValue tav = type_and_value_of_expr(elem); + Selection sel = lookup_field_from_index(bt, st->fields[field_index]->Variable.field_index); + index = sel.index[0]; } - lbValue a = lb_addr_get_ptr(p, addr); - a = lb_emit_deep_field_gep(p, a, sel); - return lb_addr(a); - } - } else { - GB_PANIC("Unsupported selector expression"); - } - case_end; + field = st->fields[index]; + Type *ft = field->type; + if (!is_raw_union && !is_type_typeid(ft) && lb_is_elem_const(elem, ft)) { + continue; + } - case_ast_node(se, SelectorCallExpr, expr); - lbValue e = lb_build_expr(p, expr); - return lb_addr(lb_address_from_load_or_generate_local(p, e)); - case_end; + field_expr = lb_build_expr(p, elem); - case_ast_node(ta, TypeAssertion, expr); - TokenPos pos = ast_token(expr).pos; - lbValue e = lb_build_expr(p, ta->expr); - Type *t = type_deref(e.type); - if (is_type_union(t)) { - Type *type = type_of_expr(expr); - lbAddr v = lb_add_local_generated(p, type, false); - lb_addr_store(p, v, lb_emit_union_cast(p, lb_build_expr(p, ta->expr), type, pos)); - return v; - } else if (is_type_any(t)) { - Type *type = type_of_expr(expr); - return lb_emit_any_cast_addr(p, lb_build_expr(p, ta->expr), type, pos); - } else { - GB_PANIC("TODO(bill): type assertion %s", type_to_string(e.type)); + lbValue gep = {}; + if (is_raw_union) { + gep = lb_emit_conv(p, comp_lit_ptr, alloc_type_pointer(ft)); + } else { + gep = lb_emit_struct_ep(p, comp_lit_ptr, cast(i32)index); + } + + Type *fet = field_expr.type; + GB_ASSERT(fet->kind != Type_Tuple); + + // HACK TODO(bill): THIS IS A MASSIVE HACK!!!! + if (is_type_union(ft) && !are_types_identical(fet, ft) && !is_type_untyped(fet)) { + GB_ASSERT_MSG(union_variant_index(ft, fet) > 0, "%s", type_to_string(fet)); + + lb_emit_store_union_variant(p, gep, field_expr, fet); + } else { + lbValue fv = lb_emit_conv(p, field_expr, ft); + lb_emit_store(p, gep, fv); + } + } } - case_end; + break; + } - case_ast_node(ue, UnaryExpr, expr); - switch (ue->op.kind) { - case Token_And: { - lbValue ptr = lb_build_expr(p, expr); - return lb_addr(lb_address_from_load_or_generate_local(p, ptr)); + case Type_Map: { + if (cl->elems.count == 0) { + break; } - default: - GB_PANIC("Invalid unary expression for lb_build_addr"); + GB_ASSERT(!build_context.no_dynamic_literals); + { + auto args = array_make(permanent_allocator(), 3); + args[0] = lb_gen_map_header(p, v.addr, type); + args[1] = lb_const_int(p->module, t_int, 2*cl->elems.count); + args[2] = lb_emit_source_code_location(p, proc_name, pos); + lb_emit_runtime_call(p, "__dynamic_map_reserve", args); } - case_end; - case_ast_node(be, BinaryExpr, expr); - lbValue v = lb_build_expr(p, expr); - Type *t = v.type; - if (is_type_pointer(t)) { - return lb_addr(v); + for_array(field_index, cl->elems) { + Ast *elem = cl->elems[field_index]; + ast_node(fv, FieldValue, elem); + + lbValue key = lb_build_expr(p, fv->field); + lbValue value = lb_build_expr(p, fv->value); + lb_insert_dynamic_map_key_and_value(p, v, type, key, value, elem); } - return lb_addr(lb_address_from_load_or_generate_local(p, v)); - case_end; + break; + } - case_ast_node(ie, IndexExpr, expr); - Type *t = base_type(type_of_expr(ie->expr)); + case Type_Array: { + if (cl->elems.count > 0) { + lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr))); - bool deref = is_type_pointer(t); - t = base_type(type_deref(t)); - if (is_type_soa_struct(t)) { - // SOA STRUCTURES!!!! - lbValue val = lb_build_addr_ptr(p, ie->expr); - if (deref) { - val = lb_emit_load(p, val); + auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); + + lb_build_addr_compound_lit_populate(p, cl->elems, &temp_data, type); + + lbValue dst_ptr = lb_addr_get_ptr(p, v); + for_array(i, temp_data) { + i32 index = cast(i32)(temp_data[i].elem_index); + temp_data[i].gep = lb_emit_array_epi(p, dst_ptr, index); } - lbValue index = lb_build_expr(p, ie->index); - return lb_addr_soa_variable(val, index, ie->index); + lb_build_addr_compound_lit_assign_array(p, temp_data); } + break; + } + case Type_EnumeratedArray: { + if (cl->elems.count > 0) { + lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr))); - if (ie->expr->tav.mode == Addressing_SoaVariable) { - // SOA Structures for slices/dynamic arrays - GB_ASSERT(is_type_pointer(type_of_expr(ie->expr))); - - lbValue field = lb_build_expr(p, ie->expr); - lbValue index = lb_build_expr(p, ie->index); + auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); + lb_build_addr_compound_lit_populate(p, cl->elems, &temp_data, type); - if (!build_context.no_bounds_check) { - // TODO HACK(bill): Clean up this hack to get the length for bounds checking - // GB_ASSERT(LLVMIsALoadInst(field.value)); + lbValue dst_ptr = lb_addr_get_ptr(p, v); + i64 index_offset = exact_value_to_i64(*bt->EnumeratedArray.min_value); + for_array(i, temp_data) { + i32 index = cast(i32)(temp_data[i].elem_index - index_offset); + temp_data[i].gep = lb_emit_array_epi(p, dst_ptr, index); + } - // lbValue a = {}; - // a.value = LLVMGetOperand(field.value, 0); - // a.type = alloc_type_pointer(field.type); + lb_build_addr_compound_lit_assign_array(p, temp_data); + } + break; + } + case Type_Slice: { + if (cl->elems.count > 0) { + lbValue slice = lb_const_value(p->module, type, exact_value_compound(expr)); - // irInstr *b = &a->Instr; - // GB_ASSERT(b->kind == irInstr_StructElementPtr); - // lbValue base_struct = b->StructElementPtr.address; + lbValue data = lb_slice_elem(p, slice); - // GB_ASSERT(is_type_soa_struct(type_deref(ir_type(base_struct)))); - // lbValue len = ir_soa_struct_len(p, base_struct); - // lb_emit_bounds_check(p, ast_token(ie->index), index, len); - } - lbValue val = lb_emit_ptr_offset(p, field, index); - return lb_addr(val); - } + auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); - GB_ASSERT_MSG(is_type_indexable(t), "%s %s", type_to_string(t), expr_to_string(expr)); + lb_build_addr_compound_lit_populate(p, cl->elems, &temp_data, type); - if (is_type_map(t)) { - lbAddr map_addr = lb_build_addr(p, ie->expr); - lbValue map_val = lb_addr_load(p, map_addr); - if (deref) { - map_val = lb_emit_load(p, map_val); + for_array(i, temp_data) { + temp_data[i].gep = lb_emit_ptr_offset(p, data, lb_const_int(p->module, t_int, temp_data[i].elem_index)); } - lbValue key = lb_build_expr(p, ie->index); - key = lb_emit_conv(p, key, t->Map.key); + lb_build_addr_compound_lit_assign_array(p, temp_data); - Type *result_type = type_of_expr(expr); - lbValue map_ptr = lb_address_from_load_or_generate_local(p, map_val); - return lb_addr_map(map_ptr, key, t, result_type); - } + { + lbValue count = {}; + count.type = t_int; - switch (t->kind) { - case Type_Array: { - lbValue array = {}; - array = lb_build_addr_ptr(p, ie->expr); - if (deref) { - array = lb_emit_load(p, array); + if (lb_is_const(slice)) { + unsigned indices[1] = {1}; + count.value = LLVMConstExtractValue(slice.value, indices, gb_count_of(indices)); + } else { + count.value = LLVMBuildExtractValue(p->builder, slice.value, 1, ""); + } + lb_fill_slice(p, v, data, count); } - lbValue index = lb_build_expr(p, ie->index); - index = lb_emit_conv(p, index, t_int); - lbValue elem = lb_emit_array_ep(p, array, index); + } + break; + } - auto index_tv = type_and_value_of_expr(ie->index); - if (index_tv.mode != Addressing_Constant) { - lbValue len = lb_const_int(p->module, t_int, t->Array.count); - lb_emit_bounds_check(p, ast_token(ie->index), index, len); - } - return lb_addr(elem); + case Type_DynamicArray: { + if (cl->elems.count == 0) { + break; } + GB_ASSERT(!build_context.no_dynamic_literals); - case Type_EnumeratedArray: { - lbValue array = {}; - array = lb_build_addr_ptr(p, ie->expr); - if (deref) { - array = lb_emit_load(p, array); - } + Type *et = bt->DynamicArray.elem; + lbValue size = lb_const_int(p->module, t_int, type_size_of(et)); + lbValue align = lb_const_int(p->module, t_int, type_align_of(et)); - Type *index_type = t->EnumeratedArray.index; + i64 item_count = gb_max(cl->max_count, cl->elems.count); + { - auto index_tv = type_and_value_of_expr(ie->index); + auto args = array_make(permanent_allocator(), 5); + args[0] = lb_emit_conv(p, lb_addr_get_ptr(p, v), t_rawptr); + args[1] = size; + args[2] = align; + args[3] = lb_const_int(p->module, t_int, item_count); + args[4] = lb_emit_source_code_location(p, proc_name, pos); + lb_emit_runtime_call(p, "__dynamic_array_reserve", args); + } - lbValue index = {}; - if (compare_exact_values(Token_NotEq, *t->EnumeratedArray.min_value, exact_value_i64(0))) { - if (index_tv.mode == Addressing_Constant) { - ExactValue idx = exact_value_sub(index_tv.value, *t->EnumeratedArray.min_value); - index = lb_const_value(p->module, index_type, idx); - } else { - index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int); - index = lb_emit_arith(p, Token_Sub, index, lb_const_value(p->module, index_type, *t->EnumeratedArray.min_value), index_type); - } - } else { - index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int); - } + lbValue items = lb_generate_local_array(p, et, item_count); - lbValue elem = lb_emit_array_ep(p, array, index); + auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); + lb_build_addr_compound_lit_populate(p, cl->elems, &temp_data, type); - if (index_tv.mode != Addressing_Constant) { - lbValue len = lb_const_int(p->module, t_int, t->EnumeratedArray.count); - lb_emit_bounds_check(p, ast_token(ie->index), index, len); - } - return lb_addr(elem); + for_array(i, temp_data) { + temp_data[i].gep = lb_emit_array_epi(p, items, temp_data[i].elem_index); } + lb_build_addr_compound_lit_assign_array(p, temp_data); - case Type_Slice: { - lbValue slice = {}; - slice = lb_build_expr(p, ie->expr); - if (deref) { - slice = lb_emit_load(p, slice); - } - lbValue elem = lb_slice_elem(p, slice); - lbValue index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int); - lbValue len = lb_slice_len(p, slice); - lb_emit_bounds_check(p, ast_token(ie->index), index, len); - lbValue v = lb_emit_ptr_offset(p, elem, index); - return lb_addr(v); + { + auto args = array_make(permanent_allocator(), 6); + args[0] = lb_emit_conv(p, v.addr, t_rawptr); + args[1] = size; + args[2] = align; + args[3] = lb_emit_conv(p, items, t_rawptr); + args[4] = lb_const_int(p->module, t_int, item_count); + args[5] = lb_emit_source_code_location(p, proc_name, pos); + lb_emit_runtime_call(p, "__dynamic_array_append", args); } + break; + } - case Type_MultiPointer: { - lbValue multi_ptr = {}; - multi_ptr = lb_build_expr(p, ie->expr); - if (deref) { - multi_ptr = lb_emit_load(p, multi_ptr); - } - lbValue index = lb_build_expr(p, ie->index); - lbValue v = {}; + case Type_Basic: { + GB_ASSERT(is_type_any(bt)); + if (cl->elems.count > 0) { + lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr))); + String field_names[2] = { + str_lit("data"), + str_lit("id"), + }; + Type *field_types[2] = { + t_rawptr, + t_typeid, + }; - LLVMValueRef indices[1] = {index.value}; - v.value = LLVMBuildGEP2(p->builder, lb_type(p->module, t->MultiPointer.elem), multi_ptr.value, indices, 1, "foo"); - v.type = alloc_type_pointer(t->MultiPointer.elem); - return lb_addr(v); - } + for_array(field_index, cl->elems) { + Ast *elem = cl->elems[field_index]; - case Type_RelativeSlice: { - lbAddr slice_addr = {}; - if (deref) { - slice_addr = lb_addr(lb_build_expr(p, ie->expr)); - } else { - slice_addr = lb_build_addr(p, ie->expr); - } - lbValue slice = lb_addr_load(p, slice_addr); + lbValue field_expr = {}; + isize index = field_index; - lbValue elem = lb_slice_elem(p, slice); - lbValue index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int); - lbValue len = lb_slice_len(p, slice); - lb_emit_bounds_check(p, ast_token(ie->index), index, len); - lbValue v = lb_emit_ptr_offset(p, elem, index); - return lb_addr(v); - } + if (elem->kind == Ast_FieldValue) { + ast_node(fv, FieldValue, elem); + Selection sel = lookup_field(bt, fv->field->Ident.token.string, false); + index = sel.index[0]; + elem = fv->value; + } else { + TypeAndValue tav = type_and_value_of_expr(elem); + Selection sel = lookup_field(bt, field_names[field_index], false); + index = sel.index[0]; + } - case Type_DynamicArray: { - lbValue dynamic_array = {}; - dynamic_array = lb_build_expr(p, ie->expr); - if (deref) { - dynamic_array = lb_emit_load(p, dynamic_array); - } - lbValue elem = lb_dynamic_array_elem(p, dynamic_array); - lbValue len = lb_dynamic_array_len(p, dynamic_array); - lbValue index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int); - lb_emit_bounds_check(p, ast_token(ie->index), index, len); - lbValue v = lb_emit_ptr_offset(p, elem, index); - return lb_addr(v); - } - - case Type_Matrix: { - lbValue matrix = {}; - matrix = lb_build_addr_ptr(p, ie->expr); - if (deref) { - matrix = lb_emit_load(p, matrix); - } - lbValue index = lb_build_expr(p, ie->index); - index = lb_emit_conv(p, index, t_int); - lbValue elem = lb_emit_matrix_ep(p, matrix, lb_const_int(p->module, t_int, 0), index); - elem = lb_emit_conv(p, elem, alloc_type_pointer(type_of_expr(expr))); + field_expr = lb_build_expr(p, elem); - auto index_tv = type_and_value_of_expr(ie->index); - if (index_tv.mode != Addressing_Constant) { - lbValue len = lb_const_int(p->module, t_int, t->Matrix.column_count); - lb_emit_bounds_check(p, ast_token(ie->index), index, len); + GB_ASSERT(field_expr.type->kind != Type_Tuple); + + Type *ft = field_types[index]; + lbValue fv = lb_emit_conv(p, field_expr, ft); + lbValue gep = lb_emit_struct_ep(p, lb_addr_get_ptr(p, v), cast(i32)index); + lb_emit_store(p, gep, fv); } - return lb_addr(elem); } + break; + } - case Type_Basic: { // Basic_string - lbValue str; - lbValue elem; - lbValue len; - lbValue index; + case Type_BitSet: { + i64 sz = type_size_of(type); + if (cl->elems.count > 0 && sz > 0) { + lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr))); - str = lb_build_expr(p, ie->expr); - if (deref) { - str = lb_emit_load(p, str); - } - elem = lb_string_elem(p, str); - len = lb_string_len(p, str); + lbValue lower = lb_const_value(p->module, t_int, exact_value_i64(bt->BitSet.lower)); + for_array(i, cl->elems) { + Ast *elem = cl->elems[i]; + GB_ASSERT(elem->kind != Ast_FieldValue); - index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int); - lb_emit_bounds_check(p, ast_token(ie->index), index, len); + if (lb_is_elem_const(elem, et)) { + continue; + } - return lb_addr(lb_emit_ptr_offset(p, elem, index)); - } - } - case_end; - - case_ast_node(ie, MatrixIndexExpr, expr); - Type *t = base_type(type_of_expr(ie->expr)); + lbValue expr = lb_build_expr(p, elem); + GB_ASSERT(expr.type->kind != Type_Tuple); - bool deref = is_type_pointer(t); - t = base_type(type_deref(t)); - - lbValue m = {}; - m = lb_build_addr_ptr(p, ie->expr); - if (deref) { - m = lb_emit_load(p, m); - } - lbValue row_index = lb_build_expr(p, ie->row_index); - lbValue column_index = lb_build_expr(p, ie->column_index); - row_index = lb_emit_conv(p, row_index, t_int); - column_index = lb_emit_conv(p, column_index, t_int); - lbValue elem = lb_emit_matrix_ep(p, m, row_index, column_index); + Type *it = bit_set_to_int(bt); + lbValue one = lb_const_value(p->module, it, exact_value_i64(1)); + lbValue e = lb_emit_conv(p, expr, it); + e = lb_emit_arith(p, Token_Sub, e, lower, it); + e = lb_emit_arith(p, Token_Shl, one, e, it); - auto row_index_tv = type_and_value_of_expr(ie->row_index); - auto column_index_tv = type_and_value_of_expr(ie->column_index); - if (row_index_tv.mode != Addressing_Constant || column_index_tv.mode != Addressing_Constant) { - lbValue row_count = lb_const_int(p->module, t_int, t->Matrix.row_count); - lbValue column_count = lb_const_int(p->module, t_int, t->Matrix.column_count); - lb_emit_matrix_bounds_check(p, ast_token(ie->row_index), row_index, column_index, row_count, column_count); + lbValue old_value = lb_emit_transmute(p, lb_addr_load(p, v), it); + lbValue new_value = lb_emit_arith(p, Token_Or, old_value, e, it); + new_value = lb_emit_transmute(p, new_value, type); + lb_addr_store(p, v, new_value); + } } - return lb_addr(elem); - - - case_end; - - case_ast_node(se, SliceExpr, expr); + break; + } - lbValue low = lb_const_int(p->module, t_int, 0); - lbValue high = {}; + case Type_Matrix: { + if (cl->elems.count > 0) { + lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr))); - if (se->low != nullptr) { - low = lb_correct_endianness(p, lb_build_expr(p, se->low)); - } - if (se->high != nullptr) { - high = lb_correct_endianness(p, lb_build_expr(p, se->high)); - } + auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); - bool no_indices = se->low == nullptr && se->high == nullptr; + // NOTE(bill): Separate value, gep, store into their own chunks + for_array(i, cl->elems) { + Ast *elem = cl->elems[i]; - lbAddr addr = lb_build_addr(p, se->expr); - lbValue base = lb_addr_load(p, addr); - Type *type = base_type(base.type); + if (elem->kind == Ast_FieldValue) { + ast_node(fv, FieldValue, elem); + if (lb_is_elem_const(fv->value, et)) { + continue; + } + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); - if (is_type_pointer(type)) { - type = base_type(type_deref(type)); - addr = lb_addr(base); - base = lb_addr_load(p, addr); - } + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op != Token_RangeHalf) { + hi += 1; + } - switch (type->kind) { - case Type_Slice: { - Type *slice_type = type; - lbValue len = lb_slice_len(p, base); - if (high.value == nullptr) high = len; + lbValue value = lb_build_expr(p, fv->value); - if (!no_indices) { - lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr); - } + for (i64 k = lo; k < hi; k++) { + lbCompoundLitElemTempData data = {}; + data.value = value; - lbValue elem = lb_emit_ptr_offset(p, lb_slice_elem(p, base), low); - lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int); + data.elem_index = cast(i32)matrix_row_major_index_to_offset(bt, k); + array_add(&temp_data, data); + } - lbAddr slice = lb_add_local_generated(p, slice_type, false); - lb_fill_slice(p, slice, elem, new_len); - return slice; - } + } else { + auto tav = fv->field->tav; + GB_ASSERT(tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(tav.value); - case Type_RelativeSlice: - GB_PANIC("TODO(bill): Type_RelativeSlice should be handled above already on the lb_addr_load"); - break; + lbValue value = lb_build_expr(p, fv->value); + lbCompoundLitElemTempData data = {}; + data.value = lb_emit_conv(p, value, et); + data.expr = fv->value; - case Type_DynamicArray: { - Type *elem_type = type->DynamicArray.elem; - Type *slice_type = alloc_type_slice(elem_type); + data.elem_index = cast(i32)matrix_row_major_index_to_offset(bt, index); + array_add(&temp_data, data); + } - lbValue len = lb_dynamic_array_len(p, base); - if (high.value == nullptr) high = len; + } else { + if (lb_is_elem_const(elem, et)) { + continue; + } + lbCompoundLitElemTempData data = {}; + data.expr = elem; + data.elem_index = cast(i32)matrix_row_major_index_to_offset(bt, i); + array_add(&temp_data, data); + } + } - if (!no_indices) { - lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr); + for_array(i, temp_data) { + temp_data[i].gep = lb_emit_array_epi(p, lb_addr_get_ptr(p, v), temp_data[i].elem_index); } - lbValue elem = lb_emit_ptr_offset(p, lb_dynamic_array_elem(p, base), low); - lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int); + for_array(i, temp_data) { + lbValue field_expr = temp_data[i].value; + Ast *expr = temp_data[i].expr; - lbAddr slice = lb_add_local_generated(p, slice_type, false); - lb_fill_slice(p, slice, elem, new_len); - return slice; - } + auto prev_hint = lb_set_copy_elision_hint(p, lb_addr(temp_data[i].gep), expr); - case Type_MultiPointer: { - lbAddr res = lb_add_local_generated(p, type_of_expr(expr), false); - if (se->high == nullptr) { - lbValue offset = base; - LLVMValueRef indices[1] = {low.value}; - offset.value = LLVMBuildGEP2(p->builder, lb_type(p->module, offset.type->MultiPointer.elem), offset.value, indices, 1, ""); - lb_addr_store(p, res, offset); - } else { - low = lb_emit_conv(p, low, t_int); - high = lb_emit_conv(p, high, t_int); - - lb_emit_multi_pointer_slice_bounds_check(p, se->open, low, high); + if (field_expr.value == nullptr) { + field_expr = lb_build_expr(p, expr); + } + Type *t = field_expr.type; + GB_ASSERT(t->kind != Type_Tuple); + lbValue ev = lb_emit_conv(p, field_expr, et); - LLVMValueRef indices[1] = {low.value}; - LLVMValueRef ptr = LLVMBuildGEP2(p->builder, lb_type(p->module, base.type->MultiPointer.elem), base.value, indices, 1, ""); - LLVMValueRef len = LLVMBuildSub(p->builder, high.value, low.value, ""); - - LLVMValueRef gep0 = lb_emit_struct_ep(p, res.addr, 0).value; - LLVMValueRef gep1 = lb_emit_struct_ep(p, res.addr, 1).value; - LLVMBuildStore(p->builder, ptr, gep0); - LLVMBuildStore(p->builder, len, gep1); + if (!p->copy_elision_hint.used) { + temp_data[i].value = ev; + } + + lb_reset_copy_elision_hint(p, prev_hint); + } + + for_array(i, temp_data) { + if (temp_data[i].value.value != nullptr) { + lb_emit_store(p, temp_data[i].gep, temp_data[i].value); + } } - return res; } + break; + } - case Type_Array: { - Type *slice_type = alloc_type_slice(type->Array.elem); - lbValue len = lb_const_int(p->module, t_int, type->Array.count); + case Type_SimdVector: { + if (cl->elems.count > 0) { + lbValue vector_value = lb_const_value(p->module, type, exact_value_compound(expr)); + defer (lb_addr_store(p, v, vector_value)); - if (high.value == nullptr) high = len; + auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); - bool low_const = type_and_value_of_expr(se->low).mode == Addressing_Constant; - bool high_const = type_and_value_of_expr(se->high).mode == Addressing_Constant; + // NOTE(bill): Separate value, store into their own chunks + for_array(i, cl->elems) { + Ast *elem = cl->elems[i]; + if (elem->kind == Ast_FieldValue) { + ast_node(fv, FieldValue, elem); + if (lb_is_elem_const(fv->value, et)) { + continue; + } + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); - if (!low_const || !high_const) { - if (!no_indices) { - lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr); + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op != Token_RangeHalf) { + hi += 1; + } + + lbValue value = lb_build_expr(p, fv->value); + + for (i64 k = lo; k < hi; k++) { + lbCompoundLitElemTempData data = {}; + data.value = value; + data.elem_index = cast(i32)k; + array_add(&temp_data, data); + } + + } else { + auto tav = fv->field->tav; + GB_ASSERT(tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(tav.value); + + lbValue value = lb_build_expr(p, fv->value); + lbCompoundLitElemTempData data = {}; + data.value = lb_emit_conv(p, value, et); + data.expr = fv->value; + data.elem_index = cast(i32)index; + array_add(&temp_data, data); + } + + } else { + if (lb_is_elem_const(elem, et)) { + continue; + } + lbCompoundLitElemTempData data = {}; + data.expr = elem; + data.elem_index = cast(i32)i; + array_add(&temp_data, data); } } - lbValue elem = lb_emit_ptr_offset(p, lb_array_elem(p, lb_addr_get_ptr(p, addr)), low); - lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int); - lbAddr slice = lb_add_local_generated(p, slice_type, false); - lb_fill_slice(p, slice, elem, new_len); - return slice; - } - case Type_Basic: { - GB_ASSERT(type == t_string); - lbValue len = lb_string_len(p, base); - if (high.value == nullptr) high = len; + for_array(i, temp_data) { + lbValue field_expr = temp_data[i].value; + Ast *expr = temp_data[i].expr; - if (!no_indices) { - lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr); + auto prev_hint = lb_set_copy_elision_hint(p, lb_addr(temp_data[i].gep), expr); + + if (field_expr.value == nullptr) { + field_expr = lb_build_expr(p, expr); + } + Type *t = field_expr.type; + GB_ASSERT(t->kind != Type_Tuple); + lbValue ev = lb_emit_conv(p, field_expr, et); + + if (!p->copy_elision_hint.used) { + temp_data[i].value = ev; + } + + lb_reset_copy_elision_hint(p, prev_hint); } - lbValue elem = lb_emit_ptr_offset(p, lb_string_elem(p, base), low); - lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int); - lbAddr str = lb_add_local_generated(p, t_string, false); - lb_fill_string(p, str, elem, new_len); - return str; + // TODO(bill): reduce the need for individual `insertelement` if a `shufflevector` + // might be a better option + + for_array(i, temp_data) { + if (temp_data[i].value.value != nullptr) { + LLVMValueRef index = lb_const_int(p->module, t_u32, temp_data[i].elem_index).value; + vector_value.value = LLVMBuildInsertElement(p->builder, vector_value.value, temp_data[i].value.value, index, ""); + } + } } + break; + } + } + return v; +} - case Type_Struct: - if (is_type_soa_struct(type)) { - lbValue len = lb_soa_struct_len(p, lb_addr_get_ptr(p, addr)); - if (high.value == nullptr) high = len; - if (!no_indices) { - lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr); +lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) { + switch (expr->kind) { + case_ast_node(i, Implicit, expr); + lbAddr v = {}; + switch (i->kind) { + case Token_context: + v = lb_find_or_generate_context_ptr(p); + break; + } + + GB_ASSERT(v.addr.value != nullptr); + return v; + case_end; + + case_ast_node(i, Ident, expr); + if (is_blank_ident(expr)) { + lbAddr val = {}; + return val; + } + String name = i->token.string; + Entity *e = entity_of_node(expr); + return lb_build_addr_from_entity(p, e, expr); + case_end; + + case_ast_node(se, SelectorExpr, expr); + Ast *sel_node = unparen_expr(se->selector); + if (sel_node->kind == Ast_Ident) { + String selector = sel_node->Ident.token.string; + TypeAndValue tav = type_and_value_of_expr(se->expr); + + if (tav.mode == Addressing_Invalid) { + // NOTE(bill): Imports + Entity *imp = entity_of_node(se->expr); + if (imp != nullptr) { + GB_ASSERT(imp->kind == Entity_ImportName); } - #if 1 + return lb_build_addr(p, unparen_expr(se->selector)); + } - lbAddr dst = lb_add_local_generated(p, type_of_expr(expr), true); - if (type->Struct.soa_kind == StructSoa_Fixed) { - i32 field_count = cast(i32)type->Struct.fields.count; - for (i32 i = 0; i < field_count; i++) { - lbValue field_dst = lb_emit_struct_ep(p, dst.addr, i); - lbValue field_src = lb_emit_struct_ep(p, lb_addr_get_ptr(p, addr), i); - field_src = lb_emit_array_ep(p, field_src, low); - lb_emit_store(p, field_dst, field_src); - } - lbValue len_dst = lb_emit_struct_ep(p, dst.addr, field_count); - lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int); - lb_emit_store(p, len_dst, new_len); - } else if (type->Struct.soa_kind == StructSoa_Slice) { - if (no_indices) { - lb_addr_store(p, dst, base); - } else { - i32 field_count = cast(i32)type->Struct.fields.count - 1; - for (i32 i = 0; i < field_count; i++) { - lbValue field_dst = lb_emit_struct_ep(p, dst.addr, i); - lbValue field_src = lb_emit_struct_ev(p, base, i); - field_src = lb_emit_ptr_offset(p, field_src, low); - lb_emit_store(p, field_dst, field_src); - } + Type *type = base_type(tav.type); + if (tav.mode == Addressing_Type) { // Addressing_Type + Selection sel = lookup_field(tav.type, selector, true); + if (sel.pseudo_field) { + GB_ASSERT(sel.entity->kind == Entity_Procedure); + return lb_addr(lb_find_value_from_entity(p->module, sel.entity)); + } + GB_PANIC("Unreachable %.*s", LIT(selector)); + } + if (se->swizzle_count > 0) { + Type *array_type = base_type(type_deref(tav.type)); + GB_ASSERT(array_type->kind == Type_Array); + u8 swizzle_count = se->swizzle_count; + u8 swizzle_indices_raw = se->swizzle_indices; + u8 swizzle_indices[4] = {}; + for (u8 i = 0; i < swizzle_count; i++) { + u8 index = swizzle_indices_raw>>(i*2) & 3; + swizzle_indices[i] = index; + } + lbValue a = {}; + if (is_type_pointer(tav.type)) { + a = lb_build_expr(p, se->expr); + } else { + lbAddr addr = lb_build_addr(p, se->expr); + a = lb_addr_get_ptr(p, addr); + } - lbValue len_dst = lb_emit_struct_ep(p, dst.addr, field_count); - lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int); - lb_emit_store(p, len_dst, new_len); + GB_ASSERT(is_type_array(expr->tav.type)); + return lb_addr_swizzle(a, expr->tav.type, swizzle_count, swizzle_indices); + } + + Selection sel = lookup_field(type, selector, false); + GB_ASSERT(sel.entity != nullptr); + if (sel.pseudo_field) { + GB_ASSERT(sel.entity->kind == Entity_Procedure); + Entity *e = entity_of_node(sel_node); + return lb_addr(lb_find_value_from_entity(p->module, e)); + } + + { + lbAddr addr = lb_build_addr(p, se->expr); + if (addr.kind == lbAddr_Map) { + lbValue v = lb_addr_load(p, addr); + lbValue a = lb_address_from_load_or_generate_local(p, v); + a = lb_emit_deep_field_gep(p, a, sel); + return lb_addr(a); + } else if (addr.kind == lbAddr_Context) { + GB_ASSERT(sel.index.count > 0); + if (addr.ctx.sel.index.count >= 0) { + sel = selection_combine(addr.ctx.sel, sel); } - } else if (type->Struct.soa_kind == StructSoa_Dynamic) { - i32 field_count = cast(i32)type->Struct.fields.count - 3; - for (i32 i = 0; i < field_count; i++) { - lbValue field_dst = lb_emit_struct_ep(p, dst.addr, i); - lbValue field_src = lb_emit_struct_ev(p, base, i); - field_src = lb_emit_ptr_offset(p, field_src, low); - lb_emit_store(p, field_dst, field_src); + addr.ctx.sel = sel; + addr.kind = lbAddr_Context; + return addr; + } else if (addr.kind == lbAddr_SoaVariable) { + lbValue index = addr.soa.index; + i32 first_index = sel.index[0]; + Selection sub_sel = sel; + sub_sel.index.data += 1; + sub_sel.index.count -= 1; + + lbValue arr = lb_emit_struct_ep(p, addr.addr, first_index); + + Type *t = base_type(type_deref(addr.addr.type)); + GB_ASSERT(is_type_soa_struct(t)); + + if (addr.soa.index_expr != nullptr && (!lb_is_const(addr.soa.index) || t->Struct.soa_kind != StructSoa_Fixed)) { + lbValue len = lb_soa_struct_len(p, addr.addr); + lb_emit_bounds_check(p, ast_token(addr.soa.index_expr), addr.soa.index, len); } + lbValue item = {}; - lbValue len_dst = lb_emit_struct_ep(p, dst.addr, field_count); - lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int); - lb_emit_store(p, len_dst, new_len); + if (t->Struct.soa_kind == StructSoa_Fixed) { + item = lb_emit_array_ep(p, arr, index); + } else { + item = lb_emit_ptr_offset(p, lb_emit_load(p, arr), index); + } + if (sub_sel.index.count > 0) { + item = lb_emit_deep_field_gep(p, item, sub_sel); + } + return lb_addr(item); + } else if (addr.kind == lbAddr_Swizzle) { + GB_ASSERT(sel.index.count > 0); + // NOTE(bill): just patch the index in place + sel.index[0] = addr.swizzle.indices[sel.index[0]]; + } else if (addr.kind == lbAddr_SwizzleLarge) { + GB_ASSERT(sel.index.count > 0); + // NOTE(bill): just patch the index in place + sel.index[0] = addr.swizzle.indices[sel.index[0]]; } - return dst; - #endif + lbValue a = lb_addr_get_ptr(p, addr); + a = lb_emit_deep_field_gep(p, a, sel); + return lb_addr(a); } - break; - + } else { + GB_PANIC("Unsupported selector expression"); } + case_end; - GB_PANIC("Unknown slicable type"); + case_ast_node(se, SelectorCallExpr, expr); + lbValue e = lb_build_expr(p, expr); + return lb_addr(lb_address_from_load_or_generate_local(p, e)); case_end; - case_ast_node(de, DerefExpr, expr); - Type *t = type_of_expr(de->expr); - if (is_type_relative_pointer(t)) { - lbAddr addr = lb_build_addr(p, de->expr); - addr.relative.deref = true; - return addr; - } else if (is_type_soa_pointer(t)) { - lbValue value = lb_build_expr(p, de->expr); - lbValue ptr = lb_emit_struct_ev(p, value, 0); - lbValue idx = lb_emit_struct_ev(p, value, 1); - return lb_addr_soa_variable(ptr, idx, nullptr); + case_ast_node(ta, TypeAssertion, expr); + TokenPos pos = ast_token(expr).pos; + lbValue e = lb_build_expr(p, ta->expr); + Type *t = type_deref(e.type); + if (is_type_union(t)) { + Type *type = type_of_expr(expr); + lbAddr v = lb_add_local_generated(p, type, false); + lb_addr_store(p, v, lb_emit_union_cast(p, lb_build_expr(p, ta->expr), type, pos)); + return v; + } else if (is_type_any(t)) { + Type *type = type_of_expr(expr); + return lb_emit_any_cast_addr(p, lb_build_expr(p, ta->expr), type, pos); + } else { + GB_PANIC("TODO(bill): type assertion %s", type_to_string(e.type)); } - lbValue addr = lb_build_expr(p, de->expr); - return lb_addr(addr); case_end; - case_ast_node(ce, CallExpr, expr); - BuiltinProcId builtin_id = BuiltinProc_Invalid; - if (ce->proc->tav.mode == Addressing_Builtin) { - Entity *e = entity_of_node(ce->proc); - if (e != nullptr) { - builtin_id = cast(BuiltinProcId)e->Builtin.id; - } else { - builtin_id = BuiltinProc_DIRECTIVE; - } + case_ast_node(ue, UnaryExpr, expr); + switch (ue->op.kind) { + case Token_And: { + lbValue ptr = lb_build_expr(p, expr); + return lb_addr(lb_address_from_load_or_generate_local(p, ptr)); } - auto const &tv = expr->tav; - if (builtin_id == BuiltinProc_swizzle && - is_type_array(tv.type)) { - // NOTE(bill, 2021-08-09): `swizzle` has some bizarre semantics so it needs to be - // specialized here for to be addressable - return lb_build_array_swizzle_addr(p, ce, tv); + default: + GB_PANIC("Invalid unary expression for lb_build_addr"); } - - // NOTE(bill): This is make sure you never need to have an 'array_ev' - lbValue e = lb_build_expr(p, expr); - #if 1 - return lb_addr(lb_address_from_load_or_generate_local(p, e)); - #else - lbAddr v = lb_add_local_generated(p, e.type, false); - lb_addr_store(p, v, e); - return v; - #endif + case_end; + case_ast_node(be, BinaryExpr, expr); + lbValue v = lb_build_expr(p, expr); + Type *t = v.type; + if (is_type_pointer(t)) { + return lb_addr(v); + } + return lb_addr(lb_address_from_load_or_generate_local(p, v)); case_end; - case_ast_node(cl, CompoundLit, expr); - Type *type = type_of_expr(expr); - Type *bt = base_type(type); + case_ast_node(ie, IndexExpr, expr); + Type *t = base_type(type_of_expr(ie->expr)); - lbAddr v = lb_add_local_generated(p, type, true); + bool deref = is_type_pointer(t); + t = base_type(type_deref(t)); + if (is_type_soa_struct(t)) { + // SOA STRUCTURES!!!! + lbValue val = lb_build_addr_ptr(p, ie->expr); + if (deref) { + val = lb_emit_load(p, val); + } - Type *et = nullptr; - switch (bt->kind) { - case Type_Array: et = bt->Array.elem; break; - case Type_EnumeratedArray: et = bt->EnumeratedArray.elem; break; - case Type_Slice: et = bt->Slice.elem; break; - case Type_BitSet: et = bt->BitSet.elem; break; - case Type_SimdVector: et = bt->SimdVector.elem; break; - case Type_Matrix: et = bt->Matrix.elem; break; + lbValue index = lb_build_expr(p, ie->index); + return lb_addr_soa_variable(val, index, ie->index); } - String proc_name = {}; - if (p->entity) { - proc_name = p->entity->token.string; - } - TokenPos pos = ast_token(expr).pos; + if (ie->expr->tav.mode == Addressing_SoaVariable) { + // SOA Structures for slices/dynamic arrays + GB_ASSERT(is_type_pointer(type_of_expr(ie->expr))); - switch (bt->kind) { - default: GB_PANIC("Unknown CompoundLit type: %s", type_to_string(type)); break; - - case Type_Struct: { - // TODO(bill): "constant" '#raw_union's are not initialized constantly at the moment. - // NOTE(bill): This is due to the layout of the unions when printed to LLVM-IR - bool is_raw_union = is_type_raw_union(bt); - GB_ASSERT(is_type_struct(bt) || is_raw_union); - TypeStruct *st = &bt->Struct; - if (cl->elems.count > 0) { - lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr))); - lbValue comp_lit_ptr = lb_addr_get_ptr(p, v); - - for_array(field_index, cl->elems) { - Ast *elem = cl->elems[field_index]; - - lbValue field_expr = {}; - Entity *field = nullptr; - isize index = field_index; - - if (elem->kind == Ast_FieldValue) { - ast_node(fv, FieldValue, elem); - String name = fv->field->Ident.token.string; - Selection sel = lookup_field(bt, name, false); - index = sel.index[0]; - elem = fv->value; - TypeAndValue tav = type_and_value_of_expr(elem); - } else { - TypeAndValue tav = type_and_value_of_expr(elem); - Selection sel = lookup_field_from_index(bt, st->fields[field_index]->Variable.field_index); - index = sel.index[0]; - } + lbValue field = lb_build_expr(p, ie->expr); + lbValue index = lb_build_expr(p, ie->index); - field = st->fields[index]; - Type *ft = field->type; - if (!is_raw_union && !is_type_typeid(ft) && lb_is_elem_const(elem, ft)) { - continue; - } - field_expr = lb_build_expr(p, elem); + if (!build_context.no_bounds_check) { + // TODO HACK(bill): Clean up this hack to get the length for bounds checking + // GB_ASSERT(LLVMIsALoadInst(field.value)); + + // lbValue a = {}; + // a.value = LLVMGetOperand(field.value, 0); + // a.type = alloc_type_pointer(field.type); + + // irInstr *b = &a->Instr; + // GB_ASSERT(b->kind == irInstr_StructElementPtr); + // lbValue base_struct = b->StructElementPtr.address; + + // GB_ASSERT(is_type_soa_struct(type_deref(ir_type(base_struct)))); + // lbValue len = ir_soa_struct_len(p, base_struct); + // lb_emit_bounds_check(p, ast_token(ie->index), index, len); + } + lbValue val = lb_emit_ptr_offset(p, field, index); + return lb_addr(val); + } - lbValue gep = {}; - if (is_raw_union) { - gep = lb_emit_conv(p, comp_lit_ptr, alloc_type_pointer(ft)); - } else { - gep = lb_emit_struct_ep(p, comp_lit_ptr, cast(i32)index); - } + GB_ASSERT_MSG(is_type_indexable(t), "%s %s", type_to_string(t), expr_to_string(expr)); - Type *fet = field_expr.type; - GB_ASSERT(fet->kind != Type_Tuple); + if (is_type_map(t)) { + lbAddr map_addr = lb_build_addr(p, ie->expr); + lbValue map_val = lb_addr_load(p, map_addr); + if (deref) { + map_val = lb_emit_load(p, map_val); + } - // HACK TODO(bill): THIS IS A MASSIVE HACK!!!! - if (is_type_union(ft) && !are_types_identical(fet, ft) && !is_type_untyped(fet)) { - GB_ASSERT_MSG(union_variant_index(ft, fet) > 0, "%s", type_to_string(fet)); + lbValue key = lb_build_expr(p, ie->index); + key = lb_emit_conv(p, key, t->Map.key); - lb_emit_store_union_variant(p, gep, field_expr, fet); - } else { - lbValue fv = lb_emit_conv(p, field_expr, ft); - lb_emit_store(p, gep, fv); - } - } - } - break; + Type *result_type = type_of_expr(expr); + lbValue map_ptr = lb_address_from_load_or_generate_local(p, map_val); + return lb_addr_map(map_ptr, key, t, result_type); } - case Type_Map: { - if (cl->elems.count == 0) { - break; - } - { - auto args = array_make(permanent_allocator(), 3); - args[0] = lb_gen_map_header(p, v.addr, type); - args[1] = lb_const_int(p->module, t_int, 2*cl->elems.count); - args[2] = lb_emit_source_code_location(p, proc_name, pos); - lb_emit_runtime_call(p, "__dynamic_map_reserve", args); + switch (t->kind) { + case Type_Array: { + lbValue array = {}; + array = lb_build_addr_ptr(p, ie->expr); + if (deref) { + array = lb_emit_load(p, array); } - for_array(field_index, cl->elems) { - Ast *elem = cl->elems[field_index]; - ast_node(fv, FieldValue, elem); + lbValue index = lb_build_expr(p, ie->index); + index = lb_emit_conv(p, index, t_int); + lbValue elem = lb_emit_array_ep(p, array, index); - lbValue key = lb_build_expr(p, fv->field); - lbValue value = lb_build_expr(p, fv->value); - lb_insert_dynamic_map_key_and_value(p, v, type, key, value, elem); + auto index_tv = type_and_value_of_expr(ie->index); + if (index_tv.mode != Addressing_Constant) { + lbValue len = lb_const_int(p->module, t_int, t->Array.count); + lb_emit_bounds_check(p, ast_token(ie->index), index, len); } - break; + return lb_addr(elem); } - case Type_Array: { - if (cl->elems.count > 0) { - lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr))); - - auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); - - // NOTE(bill): Separate value, gep, store into their own chunks - for_array(i, cl->elems) { - Ast *elem = cl->elems[i]; - if (elem->kind == Ast_FieldValue) { - ast_node(fv, FieldValue, elem); - if (lb_is_elem_const(fv->value, et)) { - continue; - } - if (is_ast_range(fv->field)) { - ast_node(ie, BinaryExpr, fv->field); - TypeAndValue lo_tav = ie->left->tav; - TypeAndValue hi_tav = ie->right->tav; - GB_ASSERT(lo_tav.mode == Addressing_Constant); - GB_ASSERT(hi_tav.mode == Addressing_Constant); - - TokenKind op = ie->op.kind; - i64 lo = exact_value_to_i64(lo_tav.value); - i64 hi = exact_value_to_i64(hi_tav.value); - if (op != Token_RangeHalf) { - hi += 1; - } - - lbValue value = lb_build_expr(p, fv->value); - - for (i64 k = lo; k < hi; k++) { - lbCompoundLitElemTempData data = {}; - data.value = value; - data.elem_index = cast(i32)k; - array_add(&temp_data, data); - } - - } else { - auto tav = fv->field->tav; - GB_ASSERT(tav.mode == Addressing_Constant); - i64 index = exact_value_to_i64(tav.value); + case Type_EnumeratedArray: { + lbValue array = {}; + array = lb_build_addr_ptr(p, ie->expr); + if (deref) { + array = lb_emit_load(p, array); + } - lbValue value = lb_build_expr(p, fv->value); - lbCompoundLitElemTempData data = {}; - data.value = lb_emit_conv(p, value, et); - data.expr = fv->value; - data.elem_index = cast(i32)index; - array_add(&temp_data, data); - } + Type *index_type = t->EnumeratedArray.index; - } else { - if (lb_is_elem_const(elem, et)) { - continue; - } - lbCompoundLitElemTempData data = {}; - data.expr = elem; - data.elem_index = cast(i32)i; - array_add(&temp_data, data); - } - } + auto index_tv = type_and_value_of_expr(ie->index); - for_array(i, temp_data) { - temp_data[i].gep = lb_emit_array_epi(p, lb_addr_get_ptr(p, v), temp_data[i].elem_index); + lbValue index = {}; + if (compare_exact_values(Token_NotEq, *t->EnumeratedArray.min_value, exact_value_i64(0))) { + if (index_tv.mode == Addressing_Constant) { + ExactValue idx = exact_value_sub(index_tv.value, *t->EnumeratedArray.min_value); + index = lb_const_value(p->module, index_type, idx); + } else { + index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int); + index = lb_emit_arith(p, Token_Sub, index, lb_const_value(p->module, index_type, *t->EnumeratedArray.min_value), index_type); } + } else { + index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int); + } - for_array(i, temp_data) { - lbValue field_expr = temp_data[i].value; - Ast *expr = temp_data[i].expr; + lbValue elem = lb_emit_array_ep(p, array, index); - auto prev_hint = lb_set_copy_elision_hint(p, lb_addr(temp_data[i].gep), expr); + if (index_tv.mode != Addressing_Constant) { + lbValue len = lb_const_int(p->module, t_int, t->EnumeratedArray.count); + lb_emit_bounds_check(p, ast_token(ie->index), index, len); + } + return lb_addr(elem); + } - if (field_expr.value == nullptr) { - field_expr = lb_build_expr(p, expr); - } - Type *t = field_expr.type; - GB_ASSERT(t->kind != Type_Tuple); - lbValue ev = lb_emit_conv(p, field_expr, et); + case Type_Slice: { + lbValue slice = {}; + slice = lb_build_expr(p, ie->expr); + if (deref) { + slice = lb_emit_load(p, slice); + } + lbValue elem = lb_slice_elem(p, slice); + lbValue index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int); + lbValue len = lb_slice_len(p, slice); + lb_emit_bounds_check(p, ast_token(ie->index), index, len); + lbValue v = lb_emit_ptr_offset(p, elem, index); + return lb_addr(v); + } - if (!p->copy_elision_hint.used) { - temp_data[i].value = ev; - } + case Type_MultiPointer: { + lbValue multi_ptr = {}; + multi_ptr = lb_build_expr(p, ie->expr); + if (deref) { + multi_ptr = lb_emit_load(p, multi_ptr); + } + lbValue index = lb_build_expr(p, ie->index); + lbValue v = {}; - lb_reset_copy_elision_hint(p, prev_hint); - } + LLVMValueRef indices[1] = {index.value}; + v.value = LLVMBuildGEP2(p->builder, lb_type(p->module, t->MultiPointer.elem), multi_ptr.value, indices, 1, "foo"); + v.type = alloc_type_pointer(t->MultiPointer.elem); + return lb_addr(v); + } - for_array(i, temp_data) { - if (temp_data[i].value.value != nullptr) { - lb_emit_store(p, temp_data[i].gep, temp_data[i].value); - } - } + case Type_RelativeSlice: { + lbAddr slice_addr = {}; + if (deref) { + slice_addr = lb_addr(lb_build_expr(p, ie->expr)); + } else { + slice_addr = lb_build_addr(p, ie->expr); } - break; - } - case Type_EnumeratedArray: { - if (cl->elems.count > 0) { - lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr))); - - auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); - - // NOTE(bill): Separate value, gep, store into their own chunks - for_array(i, cl->elems) { - Ast *elem = cl->elems[i]; - if (elem->kind == Ast_FieldValue) { - ast_node(fv, FieldValue, elem); - if (lb_is_elem_const(fv->value, et)) { - continue; - } - if (is_ast_range(fv->field)) { - ast_node(ie, BinaryExpr, fv->field); - TypeAndValue lo_tav = ie->left->tav; - TypeAndValue hi_tav = ie->right->tav; - GB_ASSERT(lo_tav.mode == Addressing_Constant); - GB_ASSERT(hi_tav.mode == Addressing_Constant); - - TokenKind op = ie->op.kind; - i64 lo = exact_value_to_i64(lo_tav.value); - i64 hi = exact_value_to_i64(hi_tav.value); - if (op != Token_RangeHalf) { - hi += 1; - } - - lbValue value = lb_build_expr(p, fv->value); - - for (i64 k = lo; k < hi; k++) { - lbCompoundLitElemTempData data = {}; - data.value = value; - data.elem_index = cast(i32)k; - array_add(&temp_data, data); - } + lbValue slice = lb_addr_load(p, slice_addr); - } else { - auto tav = fv->field->tav; - GB_ASSERT(tav.mode == Addressing_Constant); - i64 index = exact_value_to_i64(tav.value); + lbValue elem = lb_slice_elem(p, slice); + lbValue index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int); + lbValue len = lb_slice_len(p, slice); + lb_emit_bounds_check(p, ast_token(ie->index), index, len); + lbValue v = lb_emit_ptr_offset(p, elem, index); + return lb_addr(v); + } - lbValue value = lb_build_expr(p, fv->value); - lbCompoundLitElemTempData data = {}; - data.value = lb_emit_conv(p, value, et); - data.expr = fv->value; - data.elem_index = cast(i32)index; - array_add(&temp_data, data); - } + case Type_DynamicArray: { + lbValue dynamic_array = {}; + dynamic_array = lb_build_expr(p, ie->expr); + if (deref) { + dynamic_array = lb_emit_load(p, dynamic_array); + } + lbValue elem = lb_dynamic_array_elem(p, dynamic_array); + lbValue len = lb_dynamic_array_len(p, dynamic_array); + lbValue index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int); + lb_emit_bounds_check(p, ast_token(ie->index), index, len); + lbValue v = lb_emit_ptr_offset(p, elem, index); + return lb_addr(v); + } - } else { - if (lb_is_elem_const(elem, et)) { - continue; - } - lbCompoundLitElemTempData data = {}; - data.expr = elem; - data.elem_index = cast(i32)i; - array_add(&temp_data, data); - } - } + case Type_Matrix: { + lbValue matrix = {}; + matrix = lb_build_addr_ptr(p, ie->expr); + if (deref) { + matrix = lb_emit_load(p, matrix); + } + lbValue index = lb_build_expr(p, ie->index); + index = lb_emit_conv(p, index, t_int); + lbValue elem = lb_emit_matrix_ep(p, matrix, lb_const_int(p->module, t_int, 0), index); + elem = lb_emit_conv(p, elem, alloc_type_pointer(type_of_expr(expr))); + auto index_tv = type_and_value_of_expr(ie->index); + if (index_tv.mode != Addressing_Constant) { + lbValue len = lb_const_int(p->module, t_int, t->Matrix.column_count); + lb_emit_bounds_check(p, ast_token(ie->index), index, len); + } + return lb_addr(elem); + } - i32 index_offset = cast(i32)exact_value_to_i64(*bt->EnumeratedArray.min_value); - for_array(i, temp_data) { - i32 index = temp_data[i].elem_index - index_offset; - temp_data[i].gep = lb_emit_array_epi(p, lb_addr_get_ptr(p, v), index); - } + case Type_Basic: { // Basic_string + lbValue str; + lbValue elem; + lbValue len; + lbValue index; - for_array(i, temp_data) { - lbValue field_expr = temp_data[i].value; - Ast *expr = temp_data[i].expr; + str = lb_build_expr(p, ie->expr); + if (deref) { + str = lb_emit_load(p, str); + } + elem = lb_string_elem(p, str); + len = lb_string_len(p, str); - auto prev_hint = lb_set_copy_elision_hint(p, lb_addr(temp_data[i].gep), expr); + index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int); + lb_emit_bounds_check(p, ast_token(ie->index), index, len); - if (field_expr.value == nullptr) { - field_expr = lb_build_expr(p, expr); - } - Type *t = field_expr.type; - GB_ASSERT(t->kind != Type_Tuple); - lbValue ev = lb_emit_conv(p, field_expr, et); + return lb_addr(lb_emit_ptr_offset(p, elem, index)); + } + } + case_end; - if (!p->copy_elision_hint.used) { - temp_data[i].value = ev; - } + case_ast_node(ie, MatrixIndexExpr, expr); + Type *t = base_type(type_of_expr(ie->expr)); - lb_reset_copy_elision_hint(p, prev_hint); - } + bool deref = is_type_pointer(t); + t = base_type(type_deref(t)); - for_array(i, temp_data) { - if (temp_data[i].value.value != nullptr) { - lb_emit_store(p, temp_data[i].gep, temp_data[i].value); - } - } - } - break; + lbValue m = {}; + m = lb_build_addr_ptr(p, ie->expr); + if (deref) { + m = lb_emit_load(p, m); } - case Type_Slice: { - if (cl->elems.count > 0) { - lbValue slice = lb_const_value(p->module, type, exact_value_compound(expr)); - - lbValue data = lb_slice_elem(p, slice); + lbValue row_index = lb_build_expr(p, ie->row_index); + lbValue column_index = lb_build_expr(p, ie->column_index); + row_index = lb_emit_conv(p, row_index, t_int); + column_index = lb_emit_conv(p, column_index, t_int); + lbValue elem = lb_emit_matrix_ep(p, m, row_index, column_index); - auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); + auto row_index_tv = type_and_value_of_expr(ie->row_index); + auto column_index_tv = type_and_value_of_expr(ie->column_index); + if (row_index_tv.mode != Addressing_Constant || column_index_tv.mode != Addressing_Constant) { + lbValue row_count = lb_const_int(p->module, t_int, t->Matrix.row_count); + lbValue column_count = lb_const_int(p->module, t_int, t->Matrix.column_count); + lb_emit_matrix_bounds_check(p, ast_token(ie->row_index), row_index, column_index, row_count, column_count); + } + return lb_addr(elem); - for_array(i, cl->elems) { - Ast *elem = cl->elems[i]; - if (elem->kind == Ast_FieldValue) { - ast_node(fv, FieldValue, elem); - if (lb_is_elem_const(fv->value, et)) { - continue; - } + case_end; - if (is_ast_range(fv->field)) { - ast_node(ie, BinaryExpr, fv->field); - TypeAndValue lo_tav = ie->left->tav; - TypeAndValue hi_tav = ie->right->tav; - GB_ASSERT(lo_tav.mode == Addressing_Constant); - GB_ASSERT(hi_tav.mode == Addressing_Constant); - - TokenKind op = ie->op.kind; - i64 lo = exact_value_to_i64(lo_tav.value); - i64 hi = exact_value_to_i64(hi_tav.value); - if (op != Token_RangeHalf) { - hi += 1; - } - - lbValue value = lb_emit_conv(p, lb_build_expr(p, fv->value), et); - - for (i64 k = lo; k < hi; k++) { - lbCompoundLitElemTempData data = {}; - data.value = value; - data.elem_index = cast(i32)k; - array_add(&temp_data, data); - } + case_ast_node(se, SliceExpr, expr); - } else { - GB_ASSERT(fv->field->tav.mode == Addressing_Constant); - i64 index = exact_value_to_i64(fv->field->tav.value); + lbValue low = lb_const_int(p->module, t_int, 0); + lbValue high = {}; - lbValue field_expr = lb_build_expr(p, fv->value); - GB_ASSERT(!is_type_tuple(field_expr.type)); + if (se->low != nullptr) { + low = lb_correct_endianness(p, lb_build_expr(p, se->low)); + } + if (se->high != nullptr) { + high = lb_correct_endianness(p, lb_build_expr(p, se->high)); + } - lbValue ev = lb_emit_conv(p, field_expr, et); + bool no_indices = se->low == nullptr && se->high == nullptr; - lbCompoundLitElemTempData data = {}; - data.value = ev; - data.elem_index = cast(i32)index; - array_add(&temp_data, data); - } - } else { - if (lb_is_elem_const(elem, et)) { - continue; - } - lbValue field_expr = lb_build_expr(p, elem); - GB_ASSERT(!is_type_tuple(field_expr.type)); + lbAddr addr = lb_build_addr(p, se->expr); + lbValue base = lb_addr_load(p, addr); + Type *type = base_type(base.type); - lbValue ev = lb_emit_conv(p, field_expr, et); + if (is_type_pointer(type)) { + type = base_type(type_deref(type)); + addr = lb_addr(base); + base = lb_addr_load(p, addr); + } - lbCompoundLitElemTempData data = {}; - data.value = ev; - data.elem_index = cast(i32)i; - array_add(&temp_data, data); - } - } + switch (type->kind) { + case Type_Slice: { + Type *slice_type = type; + lbValue len = lb_slice_len(p, base); + if (high.value == nullptr) high = len; - for_array(i, temp_data) { - temp_data[i].gep = lb_emit_ptr_offset(p, data, lb_const_int(p->module, t_int, temp_data[i].elem_index)); - } + if (!no_indices) { + lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr); + } - for_array(i, temp_data) { - lb_emit_store(p, temp_data[i].gep, temp_data[i].value); - } + lbValue elem = lb_emit_ptr_offset(p, lb_slice_elem(p, base), low); + lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int); - { - lbValue count = {}; - count.type = t_int; + lbAddr slice = lb_add_local_generated(p, slice_type, false); + lb_fill_slice(p, slice, elem, new_len); + return slice; + } - if (lb_is_const(slice)) { - unsigned indices[1] = {1}; - count.value = LLVMConstExtractValue(slice.value, indices, gb_count_of(indices)); - } else { - count.value = LLVMBuildExtractValue(p->builder, slice.value, 1, ""); - } - lb_fill_slice(p, v, data, count); - } - } + case Type_RelativeSlice: + GB_PANIC("TODO(bill): Type_RelativeSlice should be handled above already on the lb_addr_load"); break; - } case Type_DynamicArray: { - if (cl->elems.count == 0) { - break; - } - Type *et = bt->DynamicArray.elem; - lbValue size = lb_const_int(p->module, t_int, type_size_of(et)); - lbValue align = lb_const_int(p->module, t_int, type_align_of(et)); + Type *elem_type = type->DynamicArray.elem; + Type *slice_type = alloc_type_slice(elem_type); - i64 item_count = gb_max(cl->max_count, cl->elems.count); - { + lbValue len = lb_dynamic_array_len(p, base); + if (high.value == nullptr) high = len; - auto args = array_make(permanent_allocator(), 5); - args[0] = lb_emit_conv(p, lb_addr_get_ptr(p, v), t_rawptr); - args[1] = size; - args[2] = align; - args[3] = lb_const_int(p->module, t_int, 2*item_count); // TODO(bill): Is this too much waste? - args[4] = lb_emit_source_code_location(p, proc_name, pos); - lb_emit_runtime_call(p, "__dynamic_array_reserve", args); + if (!no_indices) { + lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr); } - lbValue items = lb_generate_local_array(p, et, item_count); - // lbValue items = lb_generate_global_array(p->module, et, item_count, str_lit("dacl$"), cast(i64)cast(intptr)expr); - - for_array(i, cl->elems) { - Ast *elem = cl->elems[i]; - if (elem->kind == Ast_FieldValue) { - ast_node(fv, FieldValue, elem); - if (is_ast_range(fv->field)) { - ast_node(ie, BinaryExpr, fv->field); - TypeAndValue lo_tav = ie->left->tav; - TypeAndValue hi_tav = ie->right->tav; - GB_ASSERT(lo_tav.mode == Addressing_Constant); - GB_ASSERT(hi_tav.mode == Addressing_Constant); - - TokenKind op = ie->op.kind; - i64 lo = exact_value_to_i64(lo_tav.value); - i64 hi = exact_value_to_i64(hi_tav.value); - if (op != Token_RangeHalf) { - hi += 1; - } + lbValue elem = lb_emit_ptr_offset(p, lb_dynamic_array_elem(p, base), low); + lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int); - lbValue value = lb_emit_conv(p, lb_build_expr(p, fv->value), et); + lbAddr slice = lb_add_local_generated(p, slice_type, false); + lb_fill_slice(p, slice, elem, new_len); + return slice; + } - for (i64 k = lo; k < hi; k++) { - lbValue ep = lb_emit_array_epi(p, items, cast(i32)k); - lb_emit_store(p, ep, value); - } - } else { - GB_ASSERT(fv->field->tav.mode == Addressing_Constant); + case Type_MultiPointer: { + lbAddr res = lb_add_local_generated(p, type_of_expr(expr), false); + if (se->high == nullptr) { + lbValue offset = base; + LLVMValueRef indices[1] = {low.value}; + offset.value = LLVMBuildGEP2(p->builder, lb_type(p->module, offset.type->MultiPointer.elem), offset.value, indices, 1, ""); + lb_addr_store(p, res, offset); + } else { + low = lb_emit_conv(p, low, t_int); + high = lb_emit_conv(p, high, t_int); - i64 field_index = exact_value_to_i64(fv->field->tav.value); + lb_emit_multi_pointer_slice_bounds_check(p, se->open, low, high); - lbValue ev = lb_build_expr(p, fv->value); - lbValue value = lb_emit_conv(p, ev, et); - lbValue ep = lb_emit_array_epi(p, items, cast(i32)field_index); - lb_emit_store(p, ep, value); - } - } else { - lbValue value = lb_emit_conv(p, lb_build_expr(p, elem), et); - lbValue ep = lb_emit_array_epi(p, items, cast(i32)i); - lb_emit_store(p, ep, value); - } - } + LLVMValueRef indices[1] = {low.value}; + LLVMValueRef ptr = LLVMBuildGEP2(p->builder, lb_type(p->module, base.type->MultiPointer.elem), base.value, indices, 1, ""); + LLVMValueRef len = LLVMBuildSub(p->builder, high.value, low.value, ""); - { - auto args = array_make(permanent_allocator(), 6); - args[0] = lb_emit_conv(p, v.addr, t_rawptr); - args[1] = size; - args[2] = align; - args[3] = lb_emit_conv(p, items, t_rawptr); - args[4] = lb_const_int(p->module, t_int, item_count); - args[5] = lb_emit_source_code_location(p, proc_name, pos); - lb_emit_runtime_call(p, "__dynamic_array_append", args); + LLVMValueRef gep0 = lb_emit_struct_ep(p, res.addr, 0).value; + LLVMValueRef gep1 = lb_emit_struct_ep(p, res.addr, 1).value; + LLVMBuildStore(p->builder, ptr, gep0); + LLVMBuildStore(p->builder, len, gep1); } - break; + return res; } - case Type_Basic: { - GB_ASSERT(is_type_any(bt)); - if (cl->elems.count > 0) { - lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr))); - String field_names[2] = { - str_lit("data"), - str_lit("id"), - }; - Type *field_types[2] = { - t_rawptr, - t_typeid, - }; - - for_array(field_index, cl->elems) { - Ast *elem = cl->elems[field_index]; - - lbValue field_expr = {}; - isize index = field_index; - - if (elem->kind == Ast_FieldValue) { - ast_node(fv, FieldValue, elem); - Selection sel = lookup_field(bt, fv->field->Ident.token.string, false); - index = sel.index[0]; - elem = fv->value; - } else { - TypeAndValue tav = type_and_value_of_expr(elem); - Selection sel = lookup_field(bt, field_names[field_index], false); - index = sel.index[0]; - } + case Type_Array: { + Type *slice_type = alloc_type_slice(type->Array.elem); + lbValue len = lb_const_int(p->module, t_int, type->Array.count); - field_expr = lb_build_expr(p, elem); + if (high.value == nullptr) high = len; - GB_ASSERT(field_expr.type->kind != Type_Tuple); + bool low_const = type_and_value_of_expr(se->low).mode == Addressing_Constant; + bool high_const = type_and_value_of_expr(se->high).mode == Addressing_Constant; - Type *ft = field_types[index]; - lbValue fv = lb_emit_conv(p, field_expr, ft); - lbValue gep = lb_emit_struct_ep(p, lb_addr_get_ptr(p, v), cast(i32)index); - lb_emit_store(p, gep, fv); + if (!low_const || !high_const) { + if (!no_indices) { + lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr); } } + lbValue elem = lb_emit_ptr_offset(p, lb_array_elem(p, lb_addr_get_ptr(p, addr)), low); + lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int); - break; + lbAddr slice = lb_add_local_generated(p, slice_type, false); + lb_fill_slice(p, slice, elem, new_len); + return slice; } - case Type_BitSet: { - i64 sz = type_size_of(type); - if (cl->elems.count > 0 && sz > 0) { - lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr))); - - lbValue lower = lb_const_value(p->module, t_int, exact_value_i64(bt->BitSet.lower)); - for_array(i, cl->elems) { - Ast *elem = cl->elems[i]; - GB_ASSERT(elem->kind != Ast_FieldValue); - - if (lb_is_elem_const(elem, et)) { - continue; - } + case Type_Basic: { + GB_ASSERT(type == t_string); + lbValue len = lb_string_len(p, base); + if (high.value == nullptr) high = len; - lbValue expr = lb_build_expr(p, elem); - GB_ASSERT(expr.type->kind != Type_Tuple); + if (!no_indices) { + lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr); + } - Type *it = bit_set_to_int(bt); - lbValue one = lb_const_value(p->module, it, exact_value_i64(1)); - lbValue e = lb_emit_conv(p, expr, it); - e = lb_emit_arith(p, Token_Sub, e, lower, it); - e = lb_emit_arith(p, Token_Shl, one, e, it); + lbValue elem = lb_emit_ptr_offset(p, lb_string_elem(p, base), low); + lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int); - lbValue old_value = lb_emit_transmute(p, lb_addr_load(p, v), it); - lbValue new_value = lb_emit_arith(p, Token_Or, old_value, e, it); - new_value = lb_emit_transmute(p, new_value, type); - lb_addr_store(p, v, new_value); - } - } - break; + lbAddr str = lb_add_local_generated(p, t_string, false); + lb_fill_string(p, str, elem, new_len); + return str; } - - case Type_Matrix: { - if (cl->elems.count > 0) { - lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr))); - - auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); - - // NOTE(bill): Separate value, gep, store into their own chunks - for_array(i, cl->elems) { - Ast *elem = cl->elems[i]; - - if (elem->kind == Ast_FieldValue) { - ast_node(fv, FieldValue, elem); - if (lb_is_elem_const(fv->value, et)) { - continue; - } - if (is_ast_range(fv->field)) { - ast_node(ie, BinaryExpr, fv->field); - TypeAndValue lo_tav = ie->left->tav; - TypeAndValue hi_tav = ie->right->tav; - GB_ASSERT(lo_tav.mode == Addressing_Constant); - GB_ASSERT(hi_tav.mode == Addressing_Constant); - - TokenKind op = ie->op.kind; - i64 lo = exact_value_to_i64(lo_tav.value); - i64 hi = exact_value_to_i64(hi_tav.value); - if (op != Token_RangeHalf) { - hi += 1; - } - - lbValue value = lb_build_expr(p, fv->value); - - for (i64 k = lo; k < hi; k++) { - lbCompoundLitElemTempData data = {}; - data.value = value; - - data.elem_index = cast(i32)matrix_row_major_index_to_offset(bt, k); - array_add(&temp_data, data); - } - } else { - auto tav = fv->field->tav; - GB_ASSERT(tav.mode == Addressing_Constant); - i64 index = exact_value_to_i64(tav.value); - lbValue value = lb_build_expr(p, fv->value); - lbCompoundLitElemTempData data = {}; - data.value = lb_emit_conv(p, value, et); - data.expr = fv->value; - - data.elem_index = cast(i32)matrix_row_major_index_to_offset(bt, index); - array_add(&temp_data, data); - } + case Type_Struct: + if (is_type_soa_struct(type)) { + lbValue len = lb_soa_struct_len(p, lb_addr_get_ptr(p, addr)); + if (high.value == nullptr) high = len; - } else { - if (lb_is_elem_const(elem, et)) { - continue; - } - lbCompoundLitElemTempData data = {}; - data.expr = elem; - data.elem_index = cast(i32)matrix_row_major_index_to_offset(bt, i); - array_add(&temp_data, data); - } + if (!no_indices) { + lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr); } + #if 1 - for_array(i, temp_data) { - temp_data[i].gep = lb_emit_array_epi(p, lb_addr_get_ptr(p, v), temp_data[i].elem_index); - } + lbAddr dst = lb_add_local_generated(p, type_of_expr(expr), true); + if (type->Struct.soa_kind == StructSoa_Fixed) { + i32 field_count = cast(i32)type->Struct.fields.count; + for (i32 i = 0; i < field_count; i++) { + lbValue field_dst = lb_emit_struct_ep(p, dst.addr, i); + lbValue field_src = lb_emit_struct_ep(p, lb_addr_get_ptr(p, addr), i); + field_src = lb_emit_array_ep(p, field_src, low); + lb_emit_store(p, field_dst, field_src); + } - for_array(i, temp_data) { - lbValue field_expr = temp_data[i].value; - Ast *expr = temp_data[i].expr; + lbValue len_dst = lb_emit_struct_ep(p, dst.addr, field_count); + lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int); + lb_emit_store(p, len_dst, new_len); + } else if (type->Struct.soa_kind == StructSoa_Slice) { + if (no_indices) { + lb_addr_store(p, dst, base); + } else { + i32 field_count = cast(i32)type->Struct.fields.count - 1; + for (i32 i = 0; i < field_count; i++) { + lbValue field_dst = lb_emit_struct_ep(p, dst.addr, i); + lbValue field_src = lb_emit_struct_ev(p, base, i); + field_src = lb_emit_ptr_offset(p, field_src, low); + lb_emit_store(p, field_dst, field_src); + } - auto prev_hint = lb_set_copy_elision_hint(p, lb_addr(temp_data[i].gep), expr); - if (field_expr.value == nullptr) { - field_expr = lb_build_expr(p, expr); + lbValue len_dst = lb_emit_struct_ep(p, dst.addr, field_count); + lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int); + lb_emit_store(p, len_dst, new_len); } - Type *t = field_expr.type; - GB_ASSERT(t->kind != Type_Tuple); - lbValue ev = lb_emit_conv(p, field_expr, et); - - if (!p->copy_elision_hint.used) { - temp_data[i].value = ev; + } else if (type->Struct.soa_kind == StructSoa_Dynamic) { + i32 field_count = cast(i32)type->Struct.fields.count - 3; + for (i32 i = 0; i < field_count; i++) { + lbValue field_dst = lb_emit_struct_ep(p, dst.addr, i); + lbValue field_src = lb_emit_struct_ev(p, base, i); + field_src = lb_emit_ptr_offset(p, field_src, low); + lb_emit_store(p, field_dst, field_src); } - lb_reset_copy_elision_hint(p, prev_hint); - } - for_array(i, temp_data) { - if (temp_data[i].value.value != nullptr) { - lb_emit_store(p, temp_data[i].gep, temp_data[i].value); - } + lbValue len_dst = lb_emit_struct_ep(p, dst.addr, field_count); + lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int); + lb_emit_store(p, len_dst, new_len); } + + return dst; + #endif } break; - } - - case Type_SimdVector: { - if (cl->elems.count > 0) { - lbValue vector_value = lb_const_value(p->module, type, exact_value_compound(expr)); - defer (lb_addr_store(p, v, vector_value)); - - auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); - - // NOTE(bill): Separate value, store into their own chunks - for_array(i, cl->elems) { - Ast *elem = cl->elems[i]; - if (elem->kind == Ast_FieldValue) { - ast_node(fv, FieldValue, elem); - if (lb_is_elem_const(fv->value, et)) { - continue; - } - if (is_ast_range(fv->field)) { - ast_node(ie, BinaryExpr, fv->field); - TypeAndValue lo_tav = ie->left->tav; - TypeAndValue hi_tav = ie->right->tav; - GB_ASSERT(lo_tav.mode == Addressing_Constant); - GB_ASSERT(hi_tav.mode == Addressing_Constant); - - TokenKind op = ie->op.kind; - i64 lo = exact_value_to_i64(lo_tav.value); - i64 hi = exact_value_to_i64(hi_tav.value); - if (op != Token_RangeHalf) { - hi += 1; - } - - lbValue value = lb_build_expr(p, fv->value); - - for (i64 k = lo; k < hi; k++) { - lbCompoundLitElemTempData data = {}; - data.value = value; - data.elem_index = cast(i32)k; - array_add(&temp_data, data); - } - - } else { - auto tav = fv->field->tav; - GB_ASSERT(tav.mode == Addressing_Constant); - i64 index = exact_value_to_i64(tav.value); - - lbValue value = lb_build_expr(p, fv->value); - lbCompoundLitElemTempData data = {}; - data.value = lb_emit_conv(p, value, et); - data.expr = fv->value; - data.elem_index = cast(i32)index; - array_add(&temp_data, data); - } - - } else { - if (lb_is_elem_const(elem, et)) { - continue; - } - lbCompoundLitElemTempData data = {}; - data.expr = elem; - data.elem_index = cast(i32)i; - array_add(&temp_data, data); - } - } - - - for_array(i, temp_data) { - lbValue field_expr = temp_data[i].value; - Ast *expr = temp_data[i].expr; - - auto prev_hint = lb_set_copy_elision_hint(p, lb_addr(temp_data[i].gep), expr); - - if (field_expr.value == nullptr) { - field_expr = lb_build_expr(p, expr); - } - Type *t = field_expr.type; - GB_ASSERT(t->kind != Type_Tuple); - lbValue ev = lb_emit_conv(p, field_expr, et); - - if (!p->copy_elision_hint.used) { - temp_data[i].value = ev; - } - lb_reset_copy_elision_hint(p, prev_hint); - } + } + GB_PANIC("Unknown slicable type"); + case_end; - // TODO(bill): reduce the need for individual `insertelement` if a `shufflevector` - // might be a better option + case_ast_node(de, DerefExpr, expr); + Type *t = type_of_expr(de->expr); + if (is_type_relative_pointer(t)) { + lbAddr addr = lb_build_addr(p, de->expr); + addr.relative.deref = true; + return addr; + } else if (is_type_soa_pointer(t)) { + lbValue value = lb_build_expr(p, de->expr); + lbValue ptr = lb_emit_struct_ev(p, value, 0); + lbValue idx = lb_emit_struct_ev(p, value, 1); + return lb_addr_soa_variable(ptr, idx, nullptr); + } + lbValue addr = lb_build_expr(p, de->expr); + return lb_addr(addr); + case_end; - for_array(i, temp_data) { - if (temp_data[i].value.value != nullptr) { - LLVMValueRef index = lb_const_int(p->module, t_u32, temp_data[i].elem_index).value; - vector_value.value = LLVMBuildInsertElement(p->builder, vector_value.value, temp_data[i].value.value, index, ""); - } - } + case_ast_node(ce, CallExpr, expr); + BuiltinProcId builtin_id = BuiltinProc_Invalid; + if (ce->proc->tav.mode == Addressing_Builtin) { + Entity *e = entity_of_node(ce->proc); + if (e != nullptr) { + builtin_id = cast(BuiltinProcId)e->Builtin.id; + } else { + builtin_id = BuiltinProc_DIRECTIVE; } - break; } + auto const &tv = expr->tav; + if (builtin_id == BuiltinProc_swizzle && + is_type_array(tv.type)) { + // NOTE(bill, 2021-08-09): `swizzle` has some bizarre semantics so it needs to be + // specialized here for to be addressable + return lb_build_array_swizzle_addr(p, ce, tv); } + // NOTE(bill): This is make sure you never need to have an 'array_ev' + lbValue e = lb_build_expr(p, expr); + #if 1 + return lb_addr(lb_address_from_load_or_generate_local(p, e)); + #else + lbAddr v = lb_add_local_generated(p, e.type, false); + lb_addr_store(p, v, e); return v; + #endif + case_end; + + case_ast_node(cl, CompoundLit, expr); + return lb_build_addr_compound_lit(p, expr); case_end; case_ast_node(tc, TypeCast, expr); -- cgit v1.2.3