From e27f5796d61aa1e1dbd4b2d52e9f580fd24d3fc2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 2 May 2020 18:45:57 +0100 Subject: [PATCH] Add experimental atom op tables for llvm-backend --- src/check_decl.cpp | 24 ++++- src/check_expr.cpp | 79 +++++++++++++++- src/check_stmt.cpp | 55 +++++++++++ src/check_type.cpp | 15 ++- src/checker.cpp | 211 +++++++++++++++++++++++++++++++++++++++++++ src/checker.hpp | 11 +++ src/entity.cpp | 2 + src/llvm_backend.cpp | 130 +++++++++++++++++++++++++- src/llvm_backend.hpp | 8 +- src/parser.hpp | 2 + src/types.cpp | 29 +++++- 11 files changed, 552 insertions(+), 14 deletions(-) diff --git a/src/check_decl.cpp b/src/check_decl.cpp index dd2432eb9..809ccbc9b 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -256,9 +256,6 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def) GB_ASSERT(e->type == nullptr); DeclInfo *decl = decl_info_of_entity(e); - if (decl != nullptr) { - check_decl_attributes(ctx, decl->attributes, const_decl_attribute, nullptr); - } bool is_distinct = is_type_distinct(init_expr); Ast *te = remove_type_alias_clutter(init_expr); @@ -280,6 +277,10 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def) error(init_expr, "'distinct' cannot be applied to 'typeid'"); is_distinct = false; } + if (is_distinct && is_type_any(e->type)) { + error(init_expr, "'distinct' cannot be applied to 'any'"); + is_distinct = false; + } if (!is_distinct) { e->type = bt; named->Named.base = bt; @@ -298,6 +299,23 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def) } } + if (decl != nullptr) { + AttributeContext ac = {}; + check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac); + if (ac.atom_op_table != nullptr) { + Type *bt = base_type(e->type); + switch (bt->kind) { + case Type_Struct: + bt->Struct.atom_op_table = ac.atom_op_table; + break; + default: + error(e->token, "Only struct types can have custom atom operations"); + gb_free(heap_allocator(), ac.atom_op_table); + break; + } + } + } + // using decl if (decl->is_using) { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index af9750ee4..c998c6dc2 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -53,7 +53,7 @@ typedef CALL_ARGUMENT_CHECKER(CallArgumentCheckerType); void check_expr (CheckerContext *c, Operand *operand, Ast *expression); void check_multi_expr (CheckerContext *c, Operand *operand, Ast *expression); void check_multi_expr_or_type (CheckerContext *c, Operand *operand, Ast *expression); -void check_expr_or_type (CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint = nullptr); +void check_expr_or_type (CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint); ExprKind check_expr_base (CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint); void check_expr_with_type_hint (CheckerContext *c, Operand *o, Ast *e, Type *t); Type * check_type (CheckerContext *c, Ast *expression); @@ -8944,7 +8944,10 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_end; case_ast_node(ue, UnaryExpr, node); + Ast *prev_unary_address_hint = c->unary_address_hint; + c->unary_address_hint = unparen_expr(node); check_expr_base(c, o, ue->expr, type_hint); + c->unary_address_hint = prev_unary_address_hint; node->viral_state_flags |= ue->expr->viral_state_flags; if (o->mode == Addressing_Invalid) { @@ -9070,6 +9073,47 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type return Expr_Expr; } + if (t->kind == Type_Struct) { + TypeAtomOpTable *atom_op_table = t->Struct.atom_op_table; + if (atom_op_table != nullptr) { + if (atom_op_table->op[TypeAtomOp_index_set]) { + if (c->assignment_lhs_hint == node) { + o->mode = Addressing_AtomOpAssign; + o->type = o->type; + o->expr = node; + return kind; + } + } + if (atom_op_table->op[TypeAtomOp_index_get]) { + Entity *e = atom_op_table->op[TypeAtomOp_index_get]; + if (ie->index == nullptr) { + gbString str = expr_to_string(o->expr); + error(o->expr, "Missing index for '%s'", str); + gb_string_free(str); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + + GB_ASSERT(e->identifier != nullptr); + Ast *proc_ident = clone_ast(e->identifier); + + auto args = array_make(heap_allocator(), 2); + args[0] = ie->expr; + args[1] = ie->index; + + GB_ASSERT(c->file != nullptr); + Ast *fake_call = ast_call_expr(c->file, proc_ident, args, ie->open, ie->close, {}); + check_expr_base(c, o, fake_call, type_hint); + AtomOpMapEntry entry = {TypeAtomOp_index_get, fake_call}; + map_set(&c->info->atom_op_map, hash_pointer(node), entry); + o->expr = node; + return kind; + } + } + } + + i64 max_count = -1; bool valid = check_set_index_data(o, t, is_ptr, &max_count, o->type); @@ -9133,8 +9177,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type o->value = get_constant_field_single(c, value, cast(i32)index, nullptr, nullptr); } } - - node->viral_state_flags |= ie->index->viral_state_flags; case_end; @@ -9191,6 +9233,37 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type if (is_type_soa_struct(t)) { valid = true; o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem); + } else { + TypeAtomOpTable *atom_op_table = t->Struct.atom_op_table; + if (atom_op_table != nullptr && atom_op_table->op[TypeAtomOp_slice]) { + Entity *e = atom_op_table->op[TypeAtomOp_slice]; + GB_ASSERT(e->identifier != nullptr); + Ast *proc_ident = clone_ast(e->identifier); + + Ast *expr = se->expr; + if (o->mode == Addressing_Variable) { + expr = ast_unary_expr(c->file, {Token_And, STR_LIT("&")}, expr); + } else if (is_type_pointer(o->type)) { + // Okay + } else { + gbString str = expr_to_string(node); + error(node, "Cannot slice '%s', value is not addressable", str); + gb_string_free(str); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + auto args = array_make(heap_allocator(), 1); + args[0] = expr; + + + GB_ASSERT(c->file != nullptr); + Ast *fake_call = ast_call_expr(c->file, proc_ident, args, se->open, se->close, {}); + check_expr_base(c, o, fake_call, type_hint); + AtomOpMapEntry entry = {TypeAtomOp_slice, fake_call}; + map_set(&c->info->atom_op_map, hash_pointer(node), entry); + valid = true; + } } break; } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 0c90e08f0..3266bd780 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1296,9 +1296,11 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { o->expr = as->lhs[i]; o->mode = Addressing_Value; } else { + ctx->assignment_lhs_hint = unparen_expr(as->lhs[i]); check_expr(ctx, &lhs_operands[i], as->lhs[i]); } } + ctx->assignment_lhs_hint = nullptr; // Reset the assignment_lhs_hint check_assignment_arguments(ctx, lhs_operands, &rhs_operands, as->rhs); @@ -1310,8 +1312,61 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { } } + auto lhs_to_ignore = array_make(ctx->allocator, lhs_count); + defer (array_free(&lhs_to_ignore)); + isize max = gb_min(lhs_count, rhs_count); + // NOTE(bill, 2020-05-02): This is an utter hack to get these custom atom operations working + // correctly for assignments for (isize i = 0; i < max; i++) { + if (lhs_operands[i].mode == Addressing_AtomOpAssign) { + Operand lhs = lhs_operands[i]; + + Type *t = base_type(lhs.type); + GB_ASSERT(t->kind == Type_Struct); + ast_node(ie, IndexExpr, unparen_expr(lhs.expr)); + + TypeAtomOpTable *atom_op_table = t->Struct.atom_op_table; + GB_ASSERT(atom_op_table->op[TypeAtomOp_index_set] != nullptr); + Entity *e = atom_op_table->op[TypeAtomOp_index_set]; + + GB_ASSERT(e->identifier != nullptr); + Ast *proc_ident = clone_ast(e->identifier); + GB_ASSERT(ctx->file != nullptr); + + + TypeAndValue tv = type_and_value_of_expr(ie->expr); + Ast *expr = ie->expr; + if (is_type_pointer(tv.type)) { + // Okay + } else if (tv.mode == Addressing_Variable) { + // NOTE(bill): Hack it to take the address instead + expr = ast_unary_expr(ctx->file, {Token_And, STR_LIT("&")}, ie->expr); + } else { + continue; + } + + auto args = array_make(heap_allocator(), 3); + args[0] = expr; + args[1] = ie->index; + args[2] = rhs_operands[i].expr; + + Ast *fake_call = ast_call_expr(ctx->file, proc_ident, args, ie->open, ie->close, {}); + Operand fake_operand = {}; + fake_operand.expr = lhs.expr; + check_expr_base(ctx, &fake_operand, fake_call, nullptr); + AtomOpMapEntry entry = {TypeAtomOp_index_set, fake_call}; + map_set(&ctx->info->atom_op_map, hash_pointer(lhs.expr), entry); + + lhs_to_ignore[i] = true; + + } + } + + for (isize i = 0; i < max; i++) { + if (lhs_to_ignore[i]) { + continue; + } check_assignment_variable(ctx, &lhs_operands[i], &rhs_operands[i]); } if (lhs_count != rhs_count) { diff --git a/src/check_type.cpp b/src/check_type.cpp index 73f732b45..976c945fd 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -329,12 +329,25 @@ void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, Type *named_t auto *found_gen_types = map_get(&ctx->checker->info.gen_types, hash_pointer(original_type)); if (found_gen_types) { - array_add(found_gen_types, e); + array_add(found_gen_types, e); } else { auto array = array_make(heap_allocator()); array_add(&array, e); map_set(&ctx->checker->info.gen_types, hash_pointer(original_type), array); } + + { + Type *dst_bt = base_type(named_type); + Type *src_bt = base_type(original_type); + if ((dst_bt != nullptr && src_bt != nullptr) && + (dst_bt->kind == src_bt->kind)){ + if (dst_bt->kind == Type_Struct) { + if (dst_bt->Struct.atom_op_table == nullptr) { + dst_bt->Struct.atom_op_table = src_bt->Struct.atom_op_table; + } + } + } + } } void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array *poly_operands, Type *named_type, Type *original_type_for_poly) { diff --git a/src/checker.cpp b/src/checker.cpp index bedb40f0d..5ae8203a3 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2,6 +2,7 @@ #include "types.cpp" void check_expr(CheckerContext *c, Operand *operand, Ast *expression); +void check_expr_or_type(CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint=nullptr); bool is_operand_value(Operand o) { @@ -810,6 +811,9 @@ void init_checker_info(CheckerInfo *i) { if (i->allow_identifier_uses) { array_init(&i->identifier_uses, a); } + + map_init(&i->atom_op_map, a); + } void destroy_checker_info(CheckerInfo *i) { @@ -828,6 +832,7 @@ void destroy_checker_info(CheckerInfo *i) { array_free(&i->required_foreign_imports_through_force); array_free(&i->required_global_variables); + map_destroy(&i->atom_op_map); } CheckerContext make_checker_context(Checker *c) { @@ -2413,7 +2418,213 @@ DECL_ATTRIBUTE_PROC(const_decl_attribute) { return false; } +DECL_ATTRIBUTE_PROC(type_decl_attribute) { + if (name == "private") { + // NOTE(bill): Handled elsewhere `check_collect_value_decl` + return true; + } else if (name == "index_get") { + if (value != nullptr) { + Operand o = {}; + check_expr_or_type(c, &o, value); + Entity *e = entity_of_node(value); + if (e != nullptr && e->kind == Entity_Procedure) { + if (ac->deferred_procedure.entity != nullptr) { + error(elem, "Previous usage of the '%.*s' attribute", LIT(name)); + } + bool valid = true; + + { + Type *pt = base_type(e->type); + GB_ASSERT(pt->kind == Type_Proc); + + if (pt->Proc.result_count == 0) { + error(value, "'%s' attribute must return something", LIT(name)); + valid = false; + } + + if (pt->Proc.param_count < 2) { + error(value, "'%s' attribute must allow for 2 parameters", LIT(name)); + valid = false; + } else { + isize minimum_param_count = 0; + for_array(i, pt->Proc.params->Tuple.variables) { + Entity *param = pt->Proc.params->Tuple.variables[i]; + if (param->kind == Entity_Variable) { + if (param->Variable.param_value.kind == ParameterValue_Invalid) { + minimum_param_count += 1; + } else { + break; + } + } else if (param->kind == Entity_Constant) { + minimum_param_count += 1; + } else { + break; + } + } + + if (minimum_param_count > 2) { + error(value, "'%s' attribute must allow for at a minimum 2 parameters", LIT(name)); + value = false; + } + } + } + + if (valid && build_context.use_llvm_api) { + if (ac->atom_op_table == nullptr) { + ac->atom_op_table = gb_alloc_item(heap_allocator(), TypeAtomOpTable); + } + ac->atom_op_table->op[TypeAtomOp_index_get] = e; + } + return true; + } + } + error(elem, "Expected a procedure entity for '%.*s'", LIT(name)); + return false; + } else if (name == "index_set") { + if (value != nullptr) { + Operand o = {}; + check_expr_or_type(c, &o, value); + Entity *e = entity_of_node(value); + if (e != nullptr && e->kind == Entity_Procedure) { + if (ac->deferred_procedure.entity != nullptr) { + error(elem, "Previous usage of the '%.*s' attribute", LIT(name)); + } + + bool valid = true; + + { + Type *pt = base_type(e->type); + GB_ASSERT(pt->kind == Type_Proc); + + if (pt->Proc.param_count < 3) { + error(value, "'%s' attribute must allow for 3 parameters", LIT(name)); + valid = false; + } else { + isize minimum_param_count = 0; + for_array(i, pt->Proc.params->Tuple.variables) { + Entity *param = pt->Proc.params->Tuple.variables[i]; + if (param->kind == Entity_Variable) { + if (param->Variable.param_value.kind == ParameterValue_Invalid) { + minimum_param_count += 1; + } else { + break; + } + } else if (param->kind == Entity_Constant) { + minimum_param_count += 1; + } else { + break; + } + } + + if (minimum_param_count > 3) { + error(value, "'%s' attribute must allow for at a minimum 3 parameters", LIT(name)); + value = false; + } + } + + if (pt->Proc.variadic || pt->Proc.c_vararg) { + error(value, "'%s' attribute does not allow variadic procedures", LIT(name)); + value = false; + } + } + + if (valid && build_context.use_llvm_api) { + if (ac->atom_op_table == nullptr) { + ac->atom_op_table = gb_alloc_item(heap_allocator(), TypeAtomOpTable); + } + ac->atom_op_table->op[TypeAtomOp_index_set] = e; + } + return true; + } + } + error(elem, "Expected a procedure entity for '%.*s'", LIT(name)); + return false; + } else if (name == "slice") { + if (value != nullptr) { + Operand o = {}; + check_expr_or_type(c, &o, value); + Entity *e = entity_of_node(value); + if (e != nullptr && e->kind == Entity_Procedure) { + if (ac->deferred_procedure.entity != nullptr) { + error(elem, "Previous usage of the '%.*s' attribute", LIT(name)); + } + + bool valid = true; + + { + Type *pt = base_type(e->type); + GB_ASSERT(pt->kind == Type_Proc); + + if (pt->Proc.param_count < 1) { + error(value, "'%s' attribute must allow for 1 parameter", LIT(name)); + valid = false; + } else { + isize minimum_param_count = 0; + for_array(i, pt->Proc.params->Tuple.variables) { + Entity *param = pt->Proc.params->Tuple.variables[i]; + if (param->kind == Entity_Variable) { + if (param->Variable.param_value.kind == ParameterValue_Invalid) { + minimum_param_count += 1; + } else { + break; + } + } else if (param->kind == Entity_Constant) { + minimum_param_count += 1; + } else { + break; + } + } + + if (minimum_param_count > 1) { + error(value, "'%s' attribute must allow for at a minimum 1 parameter", LIT(name)); + value = false; + } + { + Entity *param = pt->Proc.params->Tuple.variables[0]; + Type *param_type = base_type(param->type); + if (is_type_pointer(param_type) && !is_type_rawptr(param_type)) { + // okay + } else { + error(value, "'%s' attribute's first parameter must be a pointer", LIT(name)); + value = false; + } + + } + } + + if (pt->Proc.variadic || pt->Proc.c_vararg) { + error(value, "'%s' attribute does not allow variadic procedures", LIT(name)); + value = false; + } + + if (pt->Proc.result_count != 1) { + error(value, "'%s' attribute must return 1 result", LIT(name)); + value = false; + } else { + Type *rt = pt->Proc.results->Tuple.variables[0]->type; + rt = base_type(rt); + if (!is_type_slice(rt)) { + error(value, "'%s' attribute must return a slice", LIT(name)); + value = false; + } + } + } + + if (valid && build_context.use_llvm_api) { + if (ac->atom_op_table == nullptr) { + ac->atom_op_table = gb_alloc_item(heap_allocator(), TypeAtomOpTable); + } + ac->atom_op_table->op[TypeAtomOp_slice] = e; + } + return true; + } + } + error(elem, "Expected a procedure entity for '%.*s'", LIT(name)); + return false; + } + return false; +} #include "check_expr.cpp" diff --git a/src/checker.hpp b/src/checker.hpp index 4834187da..546dba478 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -107,6 +107,7 @@ struct AttributeContext { String thread_local_model; String deprecated_message; DeferredProcedure deferred_procedure; + struct TypeAtomOpTable *atom_op_table; }; AttributeContext make_attribute_context(String link_prefix) { @@ -232,6 +233,11 @@ struct ForeignContext { typedef Array CheckerTypePath; typedef Array CheckerPolyPath; +struct AtomOpMapEntry { + u32 kind; + Ast *node; +}; + // CheckerInfo stores all the symbol information for a type-checked program struct CheckerInfo { @@ -262,6 +268,8 @@ struct CheckerInfo { Array required_foreign_imports_through_force; Array required_global_variables; + Map atom_op_map; // Key: Ast * + bool allow_identifier_uses; Array identifier_uses; // only used by 'odin query' @@ -301,6 +309,9 @@ struct CheckerContext { bool hide_polymorphic_errors; bool in_polymorphic_specialization; Scope * polymorphic_scope; + + Ast *assignment_lhs_hint; + Ast *unary_address_hint; }; struct Checker { diff --git a/src/entity.cpp b/src/entity.cpp index f4888d072..fdd277331 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -114,6 +114,8 @@ struct Entity { isize order_in_src; String deprecated_message; + // IMPORTANT NOTE(bill): This must be a discriminated union because of patching + // later entity kinds union { struct { ExactValue value; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 06b29dcb0..615e01c15 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -160,8 +160,83 @@ void lb_addr_store(lbProcedure *p, lbAddr const &addr, lbValue value) { value.value = LLVMConstNull(lb_type(p->module, t)); } + if (addr.kind == lbAddr_AtomOp_index_set) { + lbValue ptr = addr.addr; + lbValue index = addr.index_set.index; + Ast *node = addr.index_set.node; - if (addr.kind == lbAddr_Map) { + ast_node(ce, CallExpr, node); + Type *proc_type = type_and_value_of_expr(ce->proc).type; + proc_type = base_type(proc_type); + GB_ASSERT(is_type_proc(proc_type)); + TypeProc *pt = &proc_type->Proc; + + isize arg_count = 3; + isize param_count = 0; + if (pt->params) { + GB_ASSERT(pt->params->kind == Type_Tuple); + param_count = pt->params->Tuple.variables.count; + } + + + auto args = array_make(heap_allocator(), gb_max(arg_count, param_count)); + args[0] = ptr; + args[1] = index; + args[2] = value; + + isize arg_index = arg_count; + if (arg_count < param_count) { + lbModule *m = p->module; + String proc_name = {}; + if (p->entity != nullptr) { + proc_name = p->entity->token.string; + } + TokenPos pos = ast_token(ce->proc).pos; + + TypeTuple *param_tuple = &pt->params->Tuple; + + isize end = cast(isize)param_count; + while (arg_index < end) { + Entity *e = param_tuple->variables[arg_index]; + GB_ASSERT(e->kind == Entity_Variable); + + switch (e->Variable.param_value.kind) { + case ParameterValue_Constant: + args[arg_index++] = lb_const_value(p->module, e->type, e->Variable.param_value.value); + break; + case ParameterValue_Nil: + args[arg_index++] = lb_const_nil(m, e->type); + break; + case ParameterValue_Location: + args[arg_index++] = lb_emit_source_code_location(p, proc_name, pos); + break; + case ParameterValue_Value: + args[arg_index++] = lb_build_expr(p, e->Variable.param_value.ast_value); + break; + } + } + } + + Entity *e = entity_from_expr(ce->proc); + GB_ASSERT(e != nullptr); + GB_ASSERT(is_type_polymorphic(e->type)); + + { + lbValue *found = nullptr; + if (p->module != e->code_gen_module) { + gb_mutex_lock(&p->module->mutex); + } + found = map_get(&e->code_gen_module->values, hash_entity(e)); + if (p->module != e->code_gen_module) { + gb_mutex_unlock(&p->module->mutex); + } + GB_ASSERT_MSG(found != nullptr, "%.*s", LIT(e->token.string)); + + lb_emit_call(p, *found, args); + } + + return; + } else if (addr.kind == lbAddr_Map) { lb_insert_dynamic_map_key_and_value(p, addr, addr.map.type, addr.map.key, value); return; } else if (addr.kind == lbAddr_BitField) { @@ -8587,7 +8662,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { expr = unparen_expr(expr); TypeAndValue tv = type_and_value_of_expr(expr); - GB_ASSERT(tv.mode != Addressing_Invalid); + GB_ASSERT_MSG(tv.mode != Addressing_Invalid, "%s", expr_to_string(expr)); GB_ASSERT(tv.mode != Addressing_Type); if (tv.value.kind != ExactValue_Invalid) { @@ -9301,6 +9376,27 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { return lb_addr(val); } + if (!is_type_indexable(t)) { + AtomOpMapEntry *found = map_get(&p->module->info->atom_op_map, hash_pointer(expr)); + if (found != nullptr) { + if (found->kind == TypeAtomOp_index_get) { + return lb_build_addr(p, found->node); + } else if (found->kind == TypeAtomOp_index_get_ptr) { + return lb_addr(lb_build_expr(p, found->node)); + } else if (found->kind == TypeAtomOp_index_set) { + lbValue ptr = lb_build_addr_ptr(p, ie->expr); + if (deref) { + ptr = lb_emit_load(p, ptr); + } + + lbAddr addr = {lbAddr_AtomOp_index_set}; + addr.addr = ptr; + addr.index_set.index = lb_build_expr(p, ie->index); + addr.index_set.node = found->node; + return addr; + } + } + } GB_ASSERT_MSG(is_type_indexable(t), "%s %s", type_to_string(t), expr_to_string(expr)); if (is_type_map(t)) { @@ -9450,6 +9546,36 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { bool no_indices = se->low == nullptr && se->high == nullptr; + { + Type *type = base_type(type_of_expr(se->expr)); + if (type->kind == Type_Struct && !is_type_soa_struct(type)) { + TypeAtomOpTable *atom_op_table = type->Struct.atom_op_table; + if (atom_op_table != nullptr && atom_op_table->op[TypeAtomOp_slice]) { + AtomOpMapEntry *found = map_get(&p->module->info->atom_op_map, hash_pointer(expr)); + if (found) { + lbValue base = lb_build_expr(p, found->node); + + Type *slice_type = base.type; + lbValue len = lb_slice_len(p, base); + if (high.value == nullptr) high = len; + + 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_slice_elem(p, base), 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; + } + } + } + } + + lbValue addr = lb_build_addr_ptr(p, se->expr); lbValue base = lb_emit_load(p, addr); Type *type = base_type(base.type); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index ee40ea789..1fd81fcab 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -27,6 +27,8 @@ enum lbAddrKind { lbAddr_BitField, lbAddr_Context, lbAddr_SoaVariable, + + lbAddr_AtomOp_index_set, }; struct lbAddr { @@ -48,6 +50,10 @@ struct lbAddr { lbValue index; Ast *index_expr; } soa; + struct { + lbValue index; + Ast *node; + } index_set; }; }; @@ -333,7 +339,7 @@ void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbAddr addr, Type *m void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value); lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValue value); - +lbValue lb_emit_source_code_location(lbProcedure *p, String const &procedure, TokenPos const &pos); #define LB_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime" #define LB_STARTUP_CONTEXT_PROC_NAME "__$startup_context" diff --git a/src/parser.hpp b/src/parser.hpp index 9ef1a8720..ca480f403 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -21,6 +21,8 @@ enum AddressingMode { // rhs: acts like OptionalOk Addressing_OptionalOk, // rhs: acts like a value with an optional boolean part (for existence check) Addressing_SoaVariable, // Struct-Of-Arrays indexed variable + + Addressing_AtomOpAssign, // Specialized for custom atom operations for assignments }; struct TypeAndValue { diff --git a/src/types.cpp b/src/types.cpp index 92634d9d9..990ca8387 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1,5 +1,6 @@ struct Scope; struct Ast; +struct Entity; enum BasicKind { Basic_Invalid, @@ -123,6 +124,21 @@ enum StructSoaKind { StructSoa_Dynamic = 3, }; +enum TypeAtomOpKind { + TypeAtomOp_Invalid, + + TypeAtomOp_index_get, + TypeAtomOp_index_set, + TypeAtomOp_slice, + TypeAtomOp_index_get_ptr, + + TypeAtomOp_COUNT, +}; + +struct TypeAtomOpTable { + Entity *op[TypeAtomOp_COUNT]; +}; + struct TypeStruct { Array fields; Array tags; @@ -135,6 +151,12 @@ struct TypeStruct { i64 custom_align; Entity * names; + + TypeAtomOpTable *atom_op_table; + + Type * soa_elem; + i64 soa_count; + StructSoaKind soa_kind; bool are_offsets_set; bool are_offsets_being_processed; @@ -142,10 +164,6 @@ struct TypeStruct { bool is_raw_union; bool is_polymorphic; bool is_poly_specialized; - - StructSoaKind soa_kind; - Type * soa_elem; - i64 soa_count; }; struct TypeUnion { @@ -157,6 +175,9 @@ struct TypeUnion { i64 tag_size; Type * polymorphic_params; // Type_Tuple Type * polymorphic_parent; + + TypeAtomOpTable *atom_op_table; + bool no_nil; bool maybe; bool is_polymorphic;