diff --git a/src/cg/cg.cpp b/src/cg/cg.cpp index 60a881b6b..66c4f3373 100644 --- a/src/cg/cg.cpp +++ b/src/cg/cg.cpp @@ -1,18 +1,103 @@ #include "cg.hpp" +#include "cg_worklist.cpp" +gb_internal void cg_opt(cgProcedure *p, cgWorklist *worklist, bool preserve_types) { + if (worklist == nullptr) { + return; + } +} + +gb_internal gb_inline cgNode *cg_peep(cgGraphBuilder *b, cgNode *n) { + if (n == nullptr) { + return nullptr; + } + return b->peep_callback(b->p, n); +} + +gb_internal bool cg_can_gvn(cgNode *n) { + if (n->kind == cgNode_Local) { + return false; + } + + switch (n->kind) { + case cgNode_Local: + return false; + case cgNode_Region: + case cgNode_If: + case cgNode_DebugBreak: + case cgNode_Trap: + case cgNode_Unreachable: + case cgNode_Call: + case cgNode_Tailcall: + case cgNode_Syscall: + case cgNode_DebugLocation: + return false; + + case cgNode_Blackhole: + return false; + } + + return true; +} + +gb_internal CG_PEEP_PROC(cg_opt_gvn_node) { + if (!cg_can_gvn(n)) { + return n; + } + + return n; +} + template gb_internal T *cg_alloc_node(cgProcedure *p, cgType type, isize input_count, isize input_capacity, Type *odin_type) { GB_ASSERT(input_count >= 0); GB_ASSERT(input_count < UINT16_MAX); + if (input_capacity <= 0) { + input_capacity = input_count; + } GB_ASSERT(input_count <= input_capacity); + GB_ASSERT(input_capacity <= UINT16_MAX); + void *mem = arena_alloc(&p->arena, gb_size_of(T), gb_align_of(T)); new(mem) T{}; T *n = cast(T *)n; - n->input_count = input_count; - n->input_capacity = input_capacity; + n->input_count = cast(u16)input_count; + n->input_capacity = cast(u16)input_capacity; n->type = type; - n->gvn = p->node_count++; + n->gvn = ++p->node_count; + n->odin_type = odin_type; + + if (n->input_capacity > 0) { + n->inputs = arena_alloc_array(&p->arena, n->input_capacity); + } else { + n->inputs = nullptr; + } + + n->user_count = 0; + n->user_capacity = 4; + n->users = arena_alloc_array(&p->arena, n->user_capacity); + + return n; +} + +template +gb_internal T *cg_alloc_node_with_kind(cgProcedure *p, cgNodeKind kind, cgType type, isize input_count, isize input_capacity, Type *odin_type) { + GB_ASSERT(input_count >= 0); + GB_ASSERT(input_count < UINT16_MAX); + if (input_capacity <= 0) { + input_capacity = input_count; + } + GB_ASSERT(input_count <= input_capacity); + GB_ASSERT(input_capacity <= UINT16_MAX); + + void *mem = arena_alloc(&p->arena, gb_size_of(T), gb_align_of(T)); + new(mem) T{kind}; + T *n = cast(T *)n; + n->input_count = cast(u16)input_count; + n->input_capacity = cast(u16)input_capacity; + n->type = type; + n->gvn = ++p->node_count; n->odin_type = odin_type; if (n->input_capacity > 0) { @@ -29,172 +114,671 @@ gb_internal T *cg_alloc_node(cgProcedure *p, cgType type, isize input_count, isi } -gb_internal cgGraphBuilder *cg_builder_enter(cgProcedure *p, Type *odin_signature) { - return nullptr; +gb_internal void cg_add_user(cgProcedure *p, cgNode *n, cgNode *in, u16 slot) { + if (in->user_count >= in->user_capacity) { // resize + isize new_capacity = 2 * cast(isize)in->user_capacity; + if (new_capacity >= UINT16_MAX) { + GB_ASSERT("TOO MANY USERS to one cgNode"); + } + + cgUser *users = arena_alloc_array(&p->arena, new_capacity); + gb_memcopy(users, in->users, in->user_count * gb_size_of(cgUser)); + + in->user_capacity = cast(u16)new_capacity; + in->users = users; + } + + in->users[in->user_count].node = n; + in->users[in->user_count].slot = slot; + + in->user_count += 1; +} +gb_internal void cg_remove_user(cgProcedure *p, cgNode *n, u16 slot) { + if (n->inputs[slot] == nullptr) { + return; + } + + cgNode *old = n->inputs[slot]; + cgUser *old_use = old->users; + + for (u16 i = 0; i < old->user_count; i++) { + if (old_use[i].node == n && old_use[i].slot == slot) { + old->user_count -= 1; + old_use[i] = old_use[old->user_count]; + + if (old->user_count == 0 && p->worklist) { + cg_worklist_push(p->worklist, old); + } + + return; + } + } + + GB_PANIC("failed to remove non-existent user %p from %p (slot %u)", old, n, slot); +} + + +gb_internal void cg_set_input(cgProcedure *p, cgNode *n, cgNode *in, u16 slot) { + GB_ASSERT(slot < n->input_count); + cg_remove_user(p, n, slot); + n->inputs[slot] = in; + if (in != nullptr) { + cg_add_user(p, n, in, slot); + } +} + + +gb_internal u32 cg_type_bit_size(cgModule *m, cgTypeKind kind) { + switch (kind) { + case cgType_void: return 0; + case cgType_bool: return 1; + case cgType_i8: return 8; + case cgType_i16: return 16; + case cgType_i32: return 32; + case cgType_i64: return 64; + case cgType_ptr: + return cast(u32)build_context.metrics.ptr_size; + + case cgType_f16: return 16; + case cgType_f32: return 32; + case cgType_f64: return 64; + + case cgType_v64: return 64; + case cgType_v128: return 128; + case cgType_v256: return 256; + case cgType_v512: return 512; + + case cgType_control: + case cgType_memory: + case cgType_tuple: + GB_ASSERT("Unknown bit size"); + return 0; + } +} + +gb_internal void cg_kill_node(cgProcedure *p, cgNode *n) { + for (u16 i = 0; i < n->input_count; i++) { + cg_remove_user(p, n, i); + n->inputs[i] = nullptr; + } + n->input_count = 0; + n->kind = cgNode_NULL; +} + +gb_internal void cg_kill_violently(cgProcedure *p, cgNode *n) { + // NOTE(bill): kill this node violently. It's not murder, luckily. + + for (u16 i = 0; i < n->input_count; i++) { + cg_remove_user(p, n, i); + n->inputs[i] = nullptr; + } + + GB_ASSERT(n->user_count == 0); + n->user_count = 0; + n->user_capacity = 0; + n->users = nullptr; + + n->input_count = 0; + n->kind = cgNode_NULL; +} + + + +gb_internal cgGraphBuilder *cg_builder_enter(cgProcedure *p, Type *odin_signature, cgWorklist *wl) { + p->worklist = wl; + + auto *b = arena_alloc_item(&p->arena); + b->p = p; + b->arena = &p->temp_arena; + + b->peep_callback = cg_opt_gvn_node; + + return b; } gb_internal void cg_builder_exit(cgGraphBuilder *b) { - return; + if (b->curr) { + cgNode *n = b->curr; + b->curr = nullptr; + cg_builder_label_kill(b, n); + } + + if (b->start_symbol_table != b->curr) { + cg_builder_label_kill(b, b->start_symbol_table); + } + + cgProcedure *p = b->p; + cgNode *ret = p->root_node->inputs[1]; + if (ret->kind == cgNode_Return && + ret->inputs[0]->kind == cgNode_Region && + ret->inputs[0]->input_count == 0) { + cg_kill_node(p, ret->inputs[0]); + + GB_ASSERT(p->root_node->input_count > 0); + u16 last = p->root_node->input_count-1; + GB_ASSERT(last != 1); + cgNode *last_n = p->root_node->inputs[last]; + + cg_set_input(p, p->root_node, nullptr, last); + cg_set_input(p, p->root_node, last_n, 1); + p->root_node->input_count -= 1; + + cg_kill_node(p, ret); + } + + arena_free_all(b->arena); + + if (p->worklist != nullptr) { + cg_worklist_clear(p->worklist); + cg_opt(p, p->worklist, false); + } } + gb_internal cgNode *cg_builder_bool(cgGraphBuilder *b, bool x) { - return nullptr; + auto *n = cg_alloc_node(b->p, CG_TYPE_BOOL, 1); + cg_set_input(b->p, n, b->p->root_node, 0); + n->val = cast(u64)x; + return cg_peep(b, n); } gb_internal cgNode *cg_builder_uint(cgGraphBuilder *b, cgType type, u64 x) { - return nullptr; + GB_ASSERT(type.is_int_or_ptr()); + + u32 bits = cg_type_bit_size(b->p->module, type.kind); + if (bits < 64) { + u64 mask = (~cast(u64)0ull) >> (64 - bits); + x &= mask; + } + + auto *n = cg_alloc_node(b->p, type, 1); + cg_set_input(b->p, n, b->p->root_node, 0); + n->val = cast(u64)x; + return cg_peep(b, n); } -gb_internal cgNode *cg_builder_int (cgGraphBuilder *b, cgType type, i64 x) { - return nullptr; +gb_internal cgNode *cg_builder_int(cgGraphBuilder *b, cgType type, i64 x) { + GB_ASSERT(type.is_int_or_ptr()); + auto *n = cg_alloc_node(b->p, type, 1); + cg_set_input(b->p, n, b->p->root_node, 0); + n->val = cast(u64)x; + return cg_peep(b, n); } gb_internal cgNode *cg_builder_f16(cgGraphBuilder *b, u16 x) { - return nullptr; + auto *n = cg_alloc_node(b->p, CG_TYPE_F16, 1); + cg_set_input(b->p, n, b->p->root_node, 0); + n->val = x; + return cg_peep(b, n); } gb_internal cgNode *cg_builder_f32(cgGraphBuilder *b, f32 x) { - return nullptr; + auto *n = cg_alloc_node(b->p, CG_TYPE_F32, 1); + cg_set_input(b->p, n, b->p->root_node, 0); + n->val = x; + return cg_peep(b, n); } gb_internal cgNode *cg_builder_f64(cgGraphBuilder *b, f64 x) { - return nullptr; + auto *n = cg_alloc_node(b->p, CG_TYPE_F64, 1); + cg_set_input(b->p, n, b->p->root_node, 0); + n->val = x; + return cg_peep(b, n); } gb_internal cgNode *cg_builder_symbol(cgGraphBuilder *b, cgSymbol *s) { - return nullptr; + auto *n = cg_alloc_node(b->p, CG_TYPE_PTR, 1); + cg_set_input(b->p, n, b->p->root_node, 0); + n->symbol = s; + return cg_peep(b, n); } gb_internal cgNode *cg_builder_string_ptr(cgGraphBuilder *b, String str) { + GB_ASSERT(str.len >= 0); + GB_PANIC("TODO(bill): cg_builder_string_ptr"); return nullptr; } +gb_internal u64 cg_const_sign_ext(cgModule *m, cgType type, u64 src) { + u32 src_bits = cg_type_bit_size(m, type); + u32 dst_bits = 64; + if (src_bits == dst_bits) { + return src; + } -gb_internal cgNode *cg_builder_binary_op_int(cgGraphBuilder *b, cgBinaryOpInt op, cgNode *x, cgNode *y) { + u64 mask = ((u64)1ull)<<(src_bits-1); + return (src ^ mask) - mask; +} + + +gb_internal cgNode *cg_builder_binary_op_int(cgGraphBuilder *b, cgNodeKind op, cgNode *x, cgNode *y) { return nullptr; } -gb_internal cgNode *cg_builder_binary_op_float(cgGraphBuilder *b, cgBinaryOpInt op, cgNode *x, cgNode *y) { +gb_internal cgNode *cg_builder_binary_op_float(cgGraphBuilder *b, cgNodeKind op, cgNode *x, cgNode *y) { return nullptr; } gb_internal cgNode *cg_builder_select(cgGraphBuilder *b, cgNode *cond, cgNode *x, cgNode *y) { - return nullptr; + GB_ASSERT(x->type == y->type); + + auto *n = cg_alloc_node(b->p, x->type, 4); + cg_set_input(b->p, n, cond, 1); + cg_set_input(b->p, n, x, 2); + cg_set_input(b->p, n, y, 3); + return cg_peep(b, n); } -gb_internal cgNode *cg_builder_cast(cgGraphBuilder *b, cgType type, cgCastOp op, cgNode *src) { - return nullptr; +gb_internal cgNode *cg_builder_cast(cgGraphBuilder *b, cgType type, cgNodeKind op, cgNode *src) { + GB_ASSERT(cgNode_Bitcast <= op && op <= cgNode_FloatToUint); + + if (src->kind == cgNode_Int) { + auto *iconst = src->downcast(); + if (op == cgNode_ZeroExt) { + u32 bits = cg_type_bit_size(b->p->module, src->type.kind); + u64 val = iconst->val; + return cg_builder_uint(b, src->type, val & (~cast(u64)0) >> (64 - bits)); + } else if (op == cgNode_SignExt) { + u64 val = iconst->val; + val = cg_const_sign_ext(b->p->module, src->type, val); + return cg_builder_uint(b, src->type, val); + } + } + auto *n = cg_alloc_node_with_kind(b->p, op, src->type, 2); + cg_set_input(b->p, n, src, 1); + return cg_peep(b, n); } -gb_internal cgNode *cg_builder_unary(cgGraphBuilder *b, cgUnaryOp op, cgNode *src) { - return nullptr; +gb_internal cgNode *cg_builder_unary(cgGraphBuilder *b, cgNodeKind op, cgNode *src) { + GB_ASSERT(op == cgNode_FNeg); + auto *n = cg_alloc_node_with_kind(b->p, op, src->type, 2); + cg_set_input(b->p, n, src, 1); + return cg_peep(b, n); } gb_internal cgNode *cg_builder_neg(cgGraphBuilder *b, cgNode *src) { - return nullptr; + if (src->type.is_float()) { + return cg_builder_unary(b, cgNode_FNeg, src); + } else { + return cg_builder_binary_op_int(b, cgNode_Sub, cg_builder_int(b, src->type, 0), src); + } } gb_internal cgNode *cg_builder_not(cgGraphBuilder *b, cgNode *src) { - return nullptr; + return cg_builder_binary_op_int(b, cgNode_Xor, src, cg_builder_int(b, src->type, -1)); } -gb_internal cgNode *cg_builder_cmp(cgGraphBuilder *b, cgCompareOp op, cgNode *x, cgNode *y) { +gb_internal cgNode *cg_builder_cmp(cgGraphBuilder *b, cgNodeKind op, cgNode *x, cgNode *y) { + GB_ASSERT(x->type == y->type); + GB_ASSERT(cgNode_Cmp_EQ <= op && op <= cgNode_Cmp_FLE); + + return nullptr; } // base + index*stride -gb_internal cgNode *cg_builder_ptr_array(cgGraphBuilder *b, cgNode *base, cgNode *index, i64 stride) { - return nullptr; +gb_internal cgNode *cg_builder_ptr_array(cgGraphBuilder *b, cgNode *base, cgNode *index, u64 stride) { + GB_ASSERT(base->type.is_ptr()); + GB_ASSERT(index->type.is_int()); + if (stride == 0) { + return base; + } + + cgNode *selection = index; + + if (index->kind == cgNode_Int) { + u64 offset = index->downcast()->val * stride; + if (base->kind == cgNode_PtrOffset && base->inputs[2]->kind == cgNode_Int) { + offset += base->inputs[2]->downcast()->val; + base = base->inputs[1]; + } + + selection = cg_builder_uint(b, index->type, offset); + } else if (stride != 1) { + cgNode *s = cg_builder_int(b, CG_TYPE_I64, stride); + selection = cg_builder_binary_op_int(b, cgNode_Mul, index, s); + } + + auto *n = cg_alloc_node_with_kind(b->p, cgNode_PtrOffset, base->type, 3); + cg_set_input(b->p, n, base, 1); + cg_set_input(b->p, n, selection, 2); + return cg_peep(b, n); } // base + offset gb_internal cgNode *cg_builder_ptr_member(cgGraphBuilder *b, cgNode *base, i64 offset) { - return nullptr; + if (offset = 0) { + return base; + } + if (base->kind == cgNode_PtrOffset && base->inputs[2]->kind == cgNode_Int) { + offset += base->inputs[2]->downcast()->val; + base = base->inputs[1]; + } + + auto *selection = cg_builder_int(b, CG_TYPE_I64, offset); + auto *n = cg_alloc_node_with_kind(b->p, cgNode_PtrOffset, base->type, 3); + cg_set_input(b->p, n, base, 1); + cg_set_input(b->p, n, selection, 1); + return cg_peep(b, n); } +gb_internal cgNode *cg_peek_mem(cgGraphBuilder *b, int mem_var) { + return b->curr->inputs[2 + mem_var]; +} + +gb_internal cgNode *cg_transfer_mem(cgGraphBuilder *b, cgNode *n, int mem_var) { + cgNode *old = b->curr->inputs[2 + mem_var]; + GB_ASSERT(old->type.kind == cgType_memory); + cg_set_input(b->p, b->curr, n, cast(u16)(2 + mem_var)); + return old; +} + +gb_internal cgNode *cg_transfer_ctrl(cgGraphBuilder *b, cgNode *n) { + cgNode *old = b->curr->inputs[0]; + cg_set_input(b->p, b->curr, n, 0); + return old; +} + +gb_internal cgNode *cg_internal_make_proj(cgProcedure *p, cgType type, cgNode *src, i32 index) { + GB_ASSERT(src->type.kind == cgType_tuple); + auto *proj = cg_alloc_node(p, type, 1); + cg_set_input(p, proj, src, 0); + proj->index = index; + return proj; +} + + gb_internal cgNode *cg_builder_load(cgGraphBuilder *b, int mem_var, bool ctrl_dep, cgType type, cgNode *addr, u32 align, bool is_volatile) { - return nullptr; + GB_ASSERT(addr->type.is_ptr()); + + auto *n = cg_alloc_node_with_kind(b->p, cgNode_Load, type, 3); + n->align = gb_max(align, 1); + + if (ctrl_dep) { + cg_set_input(b->p, n, b->curr->inputs[0], 0); + } + cg_set_input(b->p, n, cg_peek_mem(b, mem_var), 1); + cg_set_input(b->p, n, addr, 2); + + n = cg_peep(b, n)->downcast(); + + if (is_volatile) { + auto *barrier = cg_alloc_node_with_kind(b->p, cgNode_VolatileBarrier, CG_TYPE_MEMORY, 3); + cg_set_input(b->p, barrier, b->curr->inputs[0], 0); + cg_set_input(b->p, barrier, cg_transfer_mem(b, barrier, mem_var), 1); + cg_set_input(b->p, barrier, n, 2); + } + + return n; } gb_internal cgNode *cg_builder_store(cgGraphBuilder *b, int mem_var, bool ctrl_dep, cgType type, cgNode *addr, cgNode *val, u32 align, bool is_volatile) { - return nullptr; + GB_ASSERT(addr->type.is_ptr()); + + auto *n = cg_alloc_node_with_kind(b->p, cgNode_Store, CG_TYPE_MEMORY, 3); + n->align = gb_max(align, 1); + + cg_set_input(b->p, n, b->curr->inputs[0], 0); + cg_set_input(b->p, n, cg_transfer_mem(b, n, mem_var), 1); + cg_set_input(b->p, n, addr, 2); + cg_set_input(b->p, n, val, 3); + + if (is_volatile) { + auto *barrier = cg_alloc_node_with_kind(b->p, cgNode_VolatileBarrier, CG_TYPE_MEMORY, 2); + cg_set_input(b->p, barrier, b->curr->inputs[0], 0); + cg_set_input(b->p, barrier, cg_transfer_mem(b, barrier, mem_var), 1); + } + return n; } gb_internal cgNode *cg_builder_memcpy(cgGraphBuilder *b, int mem_var, bool ctrl_dep, cgType type, cgNode *dst, cgNode *src, cgNode *size, u32 align, bool is_volatile) { - return nullptr; + GB_ASSERT(dst->type.is_ptr()); + GB_ASSERT(src->type.is_ptr()); + + auto *n = cg_alloc_node_with_kind(b->p, cgNode_Memcpy, CG_TYPE_MEMORY, 5); + n->align = align; + cg_set_input(b->p, n, b->curr->inputs[0], 0); + cg_set_input(b->p, n, cg_transfer_mem(b, n, mem_var), 1); + cg_set_input(b->p, n, dst, 2); + cg_set_input(b->p, n, src, 3); + cg_set_input(b->p, n, size, 4); + return n; } gb_internal cgNode *cg_builder_memmove(cgGraphBuilder *b, int mem_var, bool ctrl_dep, cgType type, cgNode *dst, cgNode *src, cgNode *size, u32 align, bool is_volatile) { - return nullptr; + GB_ASSERT(dst->type.is_ptr()); + GB_ASSERT(src->type.is_ptr()); + + auto *n = cg_alloc_node_with_kind(b->p, cgNode_Memmove, CG_TYPE_MEMORY, 5); + n->align = align; + cg_set_input(b->p, n, b->curr->inputs[0], 0); + cg_set_input(b->p, n, cg_transfer_mem(b, n, mem_var), 1); + cg_set_input(b->p, n, dst, 2); + cg_set_input(b->p, n, src, 3); + cg_set_input(b->p, n, size, 4); + return n; } gb_internal cgNode *cg_builder_memzero(cgGraphBuilder *b, int mem_var, bool ctrl_dep, cgType type, cgNode *dst, cgNode *size, u32 align, bool is_volatile) { - return nullptr; + GB_ASSERT(dst->type.is_ptr()); + + auto *n = cg_alloc_node_with_kind(b->p, cgNode_Memzero, CG_TYPE_MEMORY, 4); + n->align = align; + cg_set_input(b->p, n, b->curr->inputs[0], 0); + cg_set_input(b->p, n, cg_transfer_mem(b, n, mem_var), 1); + cg_set_input(b->p, n, dst, 2); + cg_set_input(b->p, n, size, 3); + return n; } gb_internal cgNode *cg_builder_local(cgGraphBuilder *b, u32 size, u32 align) { - return nullptr; + GB_ASSERT(align > 0); + + auto *n = cg_alloc_node(b->p, CG_TYPE_PTR, 1); + n->size = size; + n->align = align; + + return n; } -gb_internal cgNode *cg_builder_local_debug(cgGraphBuilder *b, cgNode *n, String name, Type *odin_type) { - return nullptr; +gb_internal cgNode *cg_builder_local_debug(cgGraphBuilder *b, String name, Type *odin_type) { + GB_ASSERT(odin_type != nullptr); + i64 size = type_size_of(odin_type); + i64 align = type_align_of(odin_type); + GB_ASSERT(size <= UINT32_MAX); + GB_ASSERT(align <= UINT32_MAX); + + auto *n = cg_alloc_node(b->p, CG_TYPE_PTR, 1); + n->size = cast(u32)size; + n->align = cast(u32)align; + n->name = string_interner_insert(name); + n->odin_type = alloc_type_pointer(odin_type); + + return n; } gb_internal cgNode *cg_builder_frame_ptr(cgGraphBuilder *b) { - return nullptr; + auto *n = cg_alloc_node_with_kind(b->p, cgNode_FramePtr, CG_TYPE_PTR, 1); + cg_set_input(b->p, n, b->p->root_node, 0); + return cg_peep(b, n); } gb_internal cgNode *cg_builder_label(cgGraphBuilder *b, cgNode *label, bool allow_backward_jumps) { + if (label == nullptr) { + GB_ASSERT(allow_backward_jumps == false); + + auto *r = cg_alloc_node (b->p, CG_TYPE_CONTROL, 0, 2); + auto *st = cg_alloc_node(b->p, CG_TYPE_VOID, b->curr->input_count); + cg_set_input(b->p, st, r, 0); + cg_set_input(b->p, st, r, 1); + return st; + } + + auto *r = cg_alloc_node (b->p, CG_TYPE_CONTROL, 0, 2); + auto *st = cg_alloc_node(b->p, CG_TYPE_VOID, label->input_count); + cg_set_input(b->p, st, r, 0); + cg_set_input(b->p, st, r, 1); + + if (allow_backward_jumps) { + for (u16 i = 2; i < label->input_count; i++) { + auto *n = cg_alloc_node(b->p, label->inputs[i]->type, 1, 3); + cg_set_input(b->p, n, r, 0); + cg_set_input(b->p, st, n, i); + } + st->complete = true; + } + return st; +} + +gb_internal cgNode *cg_phi_identity(cgProcedure *p, cgNode *n) { + GB_PANIC("TODO(bill): cg_phi_identity"); return nullptr; } -gb_internal cgNode *cg_builder_label_complete(cgGraphBuilder *b, cgNode *label) { + +gb_internal cgNode *cg_subsume_node(cgProcedure *p, cgNode *old_n, cgNode *new_n) { + GB_PANIC("TODO(bill): cg_subsume_node"); return nullptr; } +gb_internal void cg_builder_label_complete(cgGraphBuilder *b, cgNode *label) { + cgProcedure *p = b->p; + GB_ASSERT(label->kind == cgNode_SymbolTable); + auto *st = label->downcast(); + if (st->complete) { + return; + } + + st->complete = true; + + cgNode *top_ctrl = label->inputs[1]; + + if (top_ctrl->kind == cgNode_Region) { + for (u16 i = 0; i < top_ctrl->user_count; i++) { + cgUser *u = &top_ctrl->users[i]; + if (u->node->kind == cgNode_Phi) { + GB_ASSERT(u->slot == 0); + cgNode *k = cg_phi_identity(p, u->node); + if (k != u->node) { + cg_subsume_node(p, u->node, k); + } + } + } + } + + if (top_ctrl->input_count != 0) { + for (u16 i = 2; i < label->input_count; i++) { + cg_peep(b, label->inputs[i]); // nullptr will be skipped + } + } +} + + gb_internal void cg_builder_label_kill(cgGraphBuilder *b, cgNode *label) { - return; + if (label->kind != cgNode_NULL) { + GB_ASSERT(label->kind == cgNode_SymbolTable); + GB_ASSERT_MSG(label != b->curr, "Cannot kill the label that is being currently used"); + cg_kill_violently(b->p, label); + } } -gb_internal cgNode *cg_builder_if(cgGraphBuilder *b, cgNode *cond, cgNode *x, cgNode *y) { - return nullptr; +gb_internal cgNode *cg_builder_if(cgGraphBuilder *b, cgNode *cond, cgNode *paths[2]) { + cgProcedure *p = b->p; + auto *n = cg_alloc_node(p, CG_TYPE_TUPLE, 2); + n->prob = 0.5f; + cg_set_input(p, n, cg_transfer_ctrl(b, n), 0); + cg_set_input(p, n, cond, 1); + + cgNode *cproj[2]; + cproj[0] = cg_internal_make_proj(p, CG_TYPE_CONTROL, n, 0); + cproj[1] = cg_internal_make_proj(p, CG_TYPE_CONTROL, n, 1); + + cgNode *curr = b->curr; + b->curr = nullptr; + + for (isize i = 0; i < 2; i++) { + auto *st = cg_alloc_node(p, CG_TYPE_VOID, curr->input_count); + cg_set_input(p, st, cproj[i], 0); + cg_set_input(p, st, cproj[i], 1); + st->complete = true; + + for (u16 j = 2; j < curr->input_count; j++) { + cg_set_input(p, st, curr->inputs[j], j); + } + + paths[i] = st; + } + return n; } gb_internal void cg_builder_jump(cgGraphBuilder *b, cgNode *target) { + auto *st = b->curr->downcast(); + if (st == nullptr) { + return; + } + GB_PANIC("TODO(bill): cg_builder_jump"); + return; } gb_internal cgNode *cg_builder_loop(cgGraphBuilder *b) { + GB_PANIC("TODO(bill): cg_builder_loop"); return nullptr; } gb_internal cgNode *cg_builder_phi(cgGraphBuilder *b, Slice vals) { + GB_PANIC("TODO(bill): cg_builder_phi"); return nullptr; } gb_internal cgNode *cg_builder_switch(cgGraphBuilder *b, cgNode *cond) { + GB_PANIC("TODO(bill): cg_builder_switch"); return nullptr; } gb_internal cgNode *cg_builder_case_default(cgGraphBuilder *b, cgNode *br_syms) { + GB_PANIC("TODO(bill): cg_builder_case_default"); return nullptr; } gb_internal cgNode *cg_builder_case_key(cgGraphBuilder *b, cgNode *br_syms, u64 key) { + GB_PANIC("TODO(bill): cg_builder_case_key"); return nullptr; } +gb_internal void cg_add_input_late(cgProcedure *p, cgNode *n, cgNode *in) { + GB_PANIC("TODO(bill): cg_add_input_late"); +} + gb_internal void cg_builder_ret(cgGraphBuilder *b, int mem_var, Slice args) { + GB_PANIC("TODO(bill): cg_builder_ret"); return; } gb_internal void cg_builder_unreachable(cgGraphBuilder *b, int mem_var) { - return; + auto *n = cg_alloc_node_with_kind(b->p, cgNode_Unreachable, CG_TYPE_CONTROL, 2); + cg_set_input(b->p, n, cg_transfer_ctrl(b, n), 0); + cg_set_input(b->p, n, cg_peek_mem(b, mem_var), 1); + cg_add_input_late(b->p, b->p->root_node, n); + b->curr = nullptr; } gb_internal void cg_builder_trap(cgGraphBuilder *b, int mem_var) { - return; + auto *n = cg_alloc_node_with_kind(b->p, cgNode_Trap, CG_TYPE_CONTROL, 2); + cg_set_input(b->p, n, cg_transfer_ctrl(b, n), 0); + cg_set_input(b->p, n, cg_peek_mem(b, mem_var), 1); + cg_add_input_late(b->p, b->p->root_node, n); + b->curr = nullptr; } -gb_internal void cg_builder_debug_trap(cgGraphBuilder *b, int mem_var) { - return; +gb_internal void cg_builder_debug_break(cgGraphBuilder *b, int mem_var) { + auto *n = cg_alloc_node_with_kind(b->p, cgNode_DebugBreak, CG_TYPE_CONTROL, 2); + cg_set_input(b->p, n, cg_transfer_ctrl(b, n), 0); + cg_set_input(b->p, n, cg_peek_mem(b, mem_var), 1); } + gb_internal void cg_builder_black_hole(cgGraphBuilder *b, Slice args) { + GB_PANIC("TODO(bill): cg_builder_black_hole"); return; } gb_internal cgNode *cg_builder_call(cgGraphBuilder *b, Type *odin_signature, int mem_var, cgNode *target, Slice args) { + GB_PANIC("TODO(bill): cg_builder_call"); return nullptr; } gb_internal cgNode *cg_builder_syscall(cgGraphBuilder *b, cgType dt, int mem_var, cgNode *target, Slice args) { + GB_PANIC("TODO(bill): cg_builder_syscall"); return nullptr; } gb_internal cgNode *cg_builder_atomic_rmw(cgGraphBuilder *b, int mem_var, int op, cgNode *addr, cgNode *val, cgMemoryOrder order) { + GB_PANIC("TODO(bill): cg_builder_atomic_rmw"); return nullptr; } gb_internal cgNode *cg_builder_atomic_load(cgGraphBuilder *b, int mem_var, cgType type, cgNode *addr, cgMemoryOrder order) { + GB_PANIC("TODO(bill): cg_builder_atomic_load"); return nullptr; } gb_internal bool cg_node_is_constant_zero(cgGraphBuilder *b, cgNode *n) { + GB_PANIC("TODO(bill): cg_node_is_constant_zero"); return nullptr; } diff --git a/src/cg/cg.hpp b/src/cg/cg.hpp index c6bb97215..fd0edc37c 100644 --- a/src/cg/cg.hpp +++ b/src/cg/cg.hpp @@ -18,24 +18,63 @@ enum cgTypeKind : u8 { cgType_i16, cgType_i32, cgType_i64, - // cgType_i128, - cgType_ptr, cgType_f16, cgType_f32, cgType_f64, + cgType_v64, + cgType_v128, + cgType_v256, + cgType_v512, cgType_control, - cgType_memory, - cgType_tuple, }; struct cgType { cgTypeKind kind; + + bool is_int() const { + switch (this->kind) { + case cgType_i8: + case cgType_i16: + case cgType_i32: + case cgType_i64: + return true; + } + return false; + } + + bool is_int_or_ptr() const { + switch (this->kind) { + case cgType_bool: + case cgType_i8: + case cgType_i16: + case cgType_i32: + case cgType_i64: + case cgType_ptr: + return true; + } + return false; + } + + bool is_float() const { + switch (this->kind) { + case cgType_f16: + case cgType_f32: + case cgType_f64: + return true; + } + return false; + } + + bool is_ptr() const { + return this->kind == cgType_ptr; + } + }; gb_internal gb_inline bool operator==(cgType x, cgType y) { @@ -48,7 +87,6 @@ gb_global cgType const CG_TYPE_I8 = cgType{cgType_i8}; gb_global cgType const CG_TYPE_I16 = cgType{cgType_i16}; gb_global cgType const CG_TYPE_I32 = cgType{cgType_i32}; gb_global cgType const CG_TYPE_I64 = cgType{cgType_i64}; -// gb_global cgType const CG_TYPE_I128 = cgType{cgType_i128}; gb_global cgType const CG_TYPE_PTR = cgType{cgType_ptr}; gb_global cgType const CG_TYPE_F16 = cgType{cgType_f16}; gb_global cgType const CG_TYPE_F32 = cgType{cgType_f32}; @@ -88,31 +126,6 @@ struct cgSymbol { i64 ordinal; }; -enum cgCompareOp : u8 { - cgCompareOp_Unknown, - cgCompareOp_COUNT, -}; - -enum cgBinaryOpInt : u8 { - cgBinaryOpInt_Unknown, - cgBinaryOpInt_COUNT, -}; - -enum cgUnaryOp : u8 { - cgUnaryOp_Unknown, - cgUnaryOp_COUNT, -}; - -enum cgBinaryOpFloat : u8 { - cgBinaryOpFloat_Unknown, - cgBinaryOpFloat_COUNT, -}; - -enum cgCastOp : u8 { - cgCastOp_Unknown, - cgCastOp_COUNT, -}; - struct cgUser { cgNode *node; i32 slot; @@ -131,38 +144,143 @@ struct cgSafepoint { enum cgNodeKind : u8 { cgNode_NULL, - cgNode_Branch, - cgNode_If, - cgNode_Proj, - cgNode_BranchProj, - - cgNode_SymbolTable, - - cgNode_Local, + // Constants cgNode_Int, cgNode_Int128, cgNode_F16, cgNode_F32, cgNode_F64, - cgNode_Symbol, - cgNode_Compare, - cgNode_BinaryOpInt, + // Projections + // extract a single field of a tuple + cgNode_Proj, + cgNode_BranchProj, - cgNode_MemAccess, - - cgNode_DebugLoc, - cgNode_Atomic, + // Misc + cgNode_SymbolTable, cgNode_Safepoint, + cgNode_FramePtr, + cgNode_Blackhole, + // Control + cgNode_Root, + cgNode_Return, + cgNode_Region, + cgNode_Phi, + cgNode_Branch, + cgNode_If, + cgNode_DebugBreak, + cgNode_Trap, + cgNode_Unreachable, + cgNode_Dead, + cgNode_DeadStore, + + // Control + Memory cgNode_Call, - + cgNode_Syscall, cgNode_Tailcall, + cgNode_DebugLocation, + + // Memory + cgNode_Load, + cgNode_Store, + cgNode_Memcpy, + cgNode_Memmove, + cgNode_Memzero, + cgNode_SplitMem, + cgNode_MergeMem, + + cgNode_AtomicLoad, + cgNode_AtomicStore, + cgNode_AtomicXchg, + cgNode_AtomicAdd, + cgNode_AtomicAnd, + cgNode_AtomicXor, + cgNode_AtomicOr, + cgNode_AtomicPtrOff, + cgNode_AtomicCas, + + cgNode_VolatileBarrier, + + // Pointers + cgNode_Local, + cgNode_Symbol, + cgNode_PtrOffset, + + // Conversions + cgNode_Bitcast, + cgNode_Truncate, + cgNode_FloatTruncate, + cgNode_FloatExt, + cgNode_SignExt, + cgNode_ZeroExt, + cgNode_UintToFloat, + cgNode_IntToFloat, + cgNode_FloatToInt, + cgNode_FloatToUint, + + // Select + cgNode_Select, + + // Bitwise Operations + cgNode_ByteSwap, + cgNode_CountLeadingZeros, + cgNode_CountTrailingZeros, + cgNode_CountOnes, + + // Unary Operation + cgNode_FNeg, + + // Integer Arithmetic + cgNode_And, + cgNode_Or, + cgNode_Xor, + cgNode_Add, + cgNode_Sub, + cgNode_Mul, + + cgNode_Shl, + cgNode_Shr, + cgNode_Ashr, + cgNode_ROL, + cgNode_ROR, + cgNode_UDiv, + cgNode_IDiv, + cgNode_UMod, + cgNode_IMod, + + // Float Arithmetic + cgNode_FAdd, + cgNode_FSub, + cgNode_FMul, + cgNode_FDiv, + cgNode_FMin, + cgNode_FMax, + + // Comparisons + cgNode_Cmp_EQ, + cgNode_Cmp_NE, + cgNode_Cmp_ULT, + cgNode_Cmp_ULE, + cgNode_Cmp_ILT, + cgNode_Cmp_ILE, + cgNode_Cmp_FLT, + cgNode_Cmp_FLE, + + // Float Intrinsics + cgNode_FSqrt, + cgNode_FusedMulAdd, + + // 128-bit operations + cgNode_UMul64Pair, + cgNode_IMul64Pair, + + // Vector Operations + cgNode_VBroadcast, cgNode_VShuffle, - cgNode_Region, cgNode_COUNT, }; @@ -186,26 +304,16 @@ struct cgNode { // ordered use-def edges // after input_count (and up to input_capacity) goes an unordered set of nodes // which act as extra deps, storing things like anti-deps and other scheduling related edges - cgNode **inputs_; + cgNode **inputs; // def-use edges, unordered - cgUser * users_; + cgUser * users; Type *odin_type; // usually `nullptr` - cgNode *inputs(isize index) { - GB_ASSERT(0 <= index && index < this->input_count); - return this->inputs_[index]; - } - cgNode *unordered_inputs(isize index) { - GB_ASSERT(0 <= index && index < (this->input_capacity - this->input_count)); - return this->inputs_[index+this->input_count]; - } + template T * downcast() { return reinterpret_cast(this); } + template T const *downcast() const { return reinterpret_cast(this); } - cgUser &users(isize index) { - GB_ASSERT(0 <= index && index < this->user_count); - return this->users_[index]; - } }; struct cgNodeBranch : cgNode { @@ -231,6 +339,11 @@ struct cgNodeBranchProj : cgNode { u64 taken; i64 key; }; + +struct cgNodeSelect : cgNode { + cgNodeSelect() : cgNode{cgNode_Select} {} +}; + struct cgNodeSymbolTable : cgNode { cgNodeSymbolTable() : cgNode{cgNode_SymbolTable} {} @@ -252,7 +365,7 @@ struct cgNodeLocal : cgNode { struct cgNodeInt : cgNode { cgNodeInt() : cgNode{cgNode_Int} {} - u64 value; + u64 val; }; struct cgNodeInt128 : cgNode { cgNodeInt128() : cgNode{cgNode_Int128} {} @@ -278,30 +391,31 @@ struct cgNodeF64 : cgNode { }; struct cgNodeSymbol : cgNode { cgNodeSymbol() : cgNode{cgNode_Symbol} {} -}; -struct cgNodeCompare : cgNode { - cgNodeCompare() : cgNode{cgNode_Compare} {} - cgCompareOp op; - cgType type; + cgSymbol *symbol; }; -struct cgNodeBinaryOpInt : cgNode { - cgNodeBinaryOpInt() : cgNode{cgNode_BinaryOpInt} {} - cgBinaryOpInt op; +struct cgNodeCast : cgNode { + explicit cgNodeCast(cgNodeKind kind) : cgNode{kind} {} +}; +struct cgNodeUnary : cgNode { + explicit cgNodeUnary(cgNodeKind kind) : cgNode{kind} {} +}; +struct cgNodeBinary : cgNode { + explicit cgNodeBinary(cgNodeKind kind) : cgNode{kind} {} }; struct cgNodeMemAccess : cgNode { - cgNodeMemAccess() : cgNode{cgNode_MemAccess} {} + explicit cgNodeMemAccess(cgNodeKind kind) : cgNode{kind} {} u32 align; }; -struct cgNodeDebugLoc : cgNode { - cgNodeDebugLoc() : cgNode{cgNode_DebugLoc} {} +struct cgNodeDebugLocation : cgNode { + cgNodeDebugLocation() : cgNode{cgNode_DebugLocation} {} TokenPos pos; }; struct cgNodeAtomic : cgNode { - cgNodeAtomic() : cgNode{cgNode_Atomic} {} + explicit cgNodeAtomic(cgNodeKind kind) : cgNode{kind} {} cgMemoryOrder order; cgMemoryOrder order_fail; @@ -329,15 +443,38 @@ struct cgNodeRegion : cgNode { String name; }; +struct cgNodePhi : cgNode { + cgNodePhi() : cgNode{cgNode_Phi} {} +}; + template -gb_internal T *cg_alloc_node(cgProcedure *p, cgType type, isize input_count, isize input_capacity, Type *odin_type=nullptr); +gb_internal T *cg_alloc_node(cgProcedure *p, cgType type, isize input_count, isize input_capacity=0, Type *odin_type=nullptr); +template +gb_internal T *cg_alloc_node_with_kind(cgProcedure *p, cgNodeKind kind, cgType type, isize input_count, isize input_capacity=0, Type *odin_type=nullptr); ///////////////// // Builder API // ///////////////// +#define CG_PEEP_PROC(name) cgNode *name(cgProcedure *p, cgNode *n) +typedef CG_PEEP_PROC(cgPeepProc); + +struct cgGraphBuilder { + cgProcedure *p; + Arena *arena; + + cgNode *curr; + + + cgNodeSymbolTable *start_symbol_table; + + cgPeepProc *peep_callback; + + Slice params; +}; + gb_internal cgGraphBuilder *cg_builder_enter(cgProcedure *p, Type *odin_signature); gb_internal void cg_builder_exit(cgGraphBuilder *b); @@ -352,20 +489,20 @@ gb_internal cgNode *cg_builder_symbol(cgGraphBuilder *b, cgSymbol *s); gb_internal cgNode *cg_builder_string_ptr(cgGraphBuilder *b, String str); -gb_internal cgNode *cg_builder_binary_op_int(cgGraphBuilder *b, cgBinaryOpInt op, cgNode *x, cgNode *y); -gb_internal cgNode *cg_builder_binary_op_float(cgGraphBuilder *b, cgBinaryOpInt op, cgNode *x, cgNode *y); +gb_internal cgNode *cg_builder_binary_op_int(cgGraphBuilder *b, cgNodeKind op, cgNode *x, cgNode *y); +gb_internal cgNode *cg_builder_binary_op_float(cgGraphBuilder *b, cgNodeKind op, cgNode *x, cgNode *y); gb_internal cgNode *cg_builder_select(cgGraphBuilder *b, cgNode *cond, cgNode *x, cgNode *y); -gb_internal cgNode *cg_builder_cast(cgGraphBuilder *b, cgType type, cgCastOp op, cgNode *src); +gb_internal cgNode *cg_builder_cast(cgGraphBuilder *b, cgType type, cgNodeKind op, cgNode *src); -gb_internal cgNode *cg_builder_unary(cgGraphBuilder *b, cgUnaryOp op, cgNode *src); +gb_internal cgNode *cg_builder_unary(cgGraphBuilder *b, cgNodeKind op, cgNode *src); gb_internal cgNode *cg_builder_neg(cgGraphBuilder *b, cgNode *src); gb_internal cgNode *cg_builder_not(cgGraphBuilder *b, cgNode *src); -gb_internal cgNode *cg_builder_cmp(cgGraphBuilder *b, cgCompareOp op, cgNode *x, cgNode *y); +gb_internal cgNode *cg_builder_cmp(cgGraphBuilder *b, cgNodeKind op, cgNode *x, cgNode *y); // base + index*stride -gb_internal cgNode *cg_builder_ptr_array(cgGraphBuilder *b, cgNode *base, cgNode *index, i64 stride); +gb_internal cgNode *cg_builder_ptr_array(cgGraphBuilder *b, cgNode *base, cgNode *index, u64 stride); // base + offset gb_internal cgNode *cg_builder_ptr_member(cgGraphBuilder *b, cgNode *base, i64 offset); @@ -378,7 +515,7 @@ gb_internal cgNode *cg_builder_memzero(cgGraphBuilder *b, int mem_var, bool ctrl gb_internal cgNode *cg_builder_local(cgGraphBuilder *b, u32 size, u32 align); -gb_internal cgNode *cg_builder_local_debug(cgGraphBuilder *b, cgNode *n, String name, Type *odin_type); +gb_internal cgNode *cg_builder_local_debug(cgGraphBuilder *b, String name, Type *odin_type); gb_internal cgNode *cg_builder_frame_ptr(cgGraphBuilder *b); @@ -386,13 +523,12 @@ gb_internal cgNode *cg_builder_frame_ptr(cgGraphBuilder *b); // Control Flow Primitives using Regions gb_internal cgNode *cg_builder_label(cgGraphBuilder *b, cgNode *label=nullptr, bool allow_backward_jumps=false); -// Once a labe is complete, you can no longer insert jumps into it. +// Once a label is complete, you can no longer insert jumps into it. // The phi nodes are placed and you can then insert code into the label's body. -gb_internal cgNode *cg_builder_label_complete(cgGraphBuilder *b, cgNode *label); - +gb_internal void cg_builder_label_complete(cgGraphBuilder *b, cgNode *label); gb_internal void cg_builder_label_kill(cgGraphBuilder *b, cgNode *label); -gb_internal cgNode *cg_builder_if(cgGraphBuilder *b, cgNode *cond, cgNode *x, cgNode *y); +gb_internal cgNode *cg_builder_if(cgGraphBuilder *b, cgNode *cond, cgNode *paths[2]); gb_internal void cg_builder_jump(cgGraphBuilder *b, cgNode *target); gb_internal cgNode *cg_builder_loop(cgGraphBuilder *b); gb_internal cgNode *cg_builder_phi(cgGraphBuilder *b, Slice vals); @@ -405,7 +541,7 @@ gb_internal cgNode *cg_builder_case_key(cgGraphBuilder *b, cgNode *br_syms, u64 gb_internal void cg_builder_ret(cgGraphBuilder *b, int mem_var, Slice args); gb_internal void cg_builder_unreachable(cgGraphBuilder *b, int mem_var); gb_internal void cg_builder_trap(cgGraphBuilder *b, int mem_var); -gb_internal void cg_builder_debug_trap(cgGraphBuilder *b, int mem_var); +gb_internal void cg_builder_debug_break(cgGraphBuilder *b, int mem_var); // All the passed arguments have their lifetimes anchored to this points gb_internal void cg_builder_black_hole(cgGraphBuilder *b, Slice args); @@ -529,12 +665,20 @@ struct cgGlobal : cgSymbol { }; +struct cgWorklist { + Array items; + Slice visited; // bit-array, with gvn as key +}; + struct cgProcedure : cgSymbol { Arena arena; + Arena temp_arena; u32 flags; u16 state_flags; + u32 node_count; + cgProcedure *parent; Array children; @@ -553,6 +697,10 @@ struct cgProcedure : cgSymbol { bool is_entry_point; bool is_startup; + cgWorklist *worklist; + + cgNode *root_node; + cgValue value; }; @@ -562,4 +710,23 @@ struct cgModule { Array procedures; -}; \ No newline at end of file +}; + + +gb_internal void cg_worklist_clear_visited(cgWorklist *wl); +gb_internal void cg_worklist_clear (cgWorklist *wl); +gb_internal void cg_worklist_remove (cgWorklist *wl, cgNode *n); +gb_internal bool cg_worklist_test (cgWorklist *wl, cgNode *n); +gb_internal bool cg_worklist_test_and_set (cgWorklist *wl, cgNode *n); +gb_internal void cg_worklist_push (cgWorklist *wl, cgNode *n); +gb_internal cgNode * cg_worklist_pop (cgWorklist *wl); +gb_internal void cg_worklist_replace (cgWorklist *wl, cgNode *n, cgNode *k); +gb_internal void cg_worklist_init (cgWorklist *wl, isize capacity); +gb_internal void cg_worklist_deinit (cgWorklist *wl); +gb_internal cgWorklist *cg_worklist_create (); +gb_internal void cg_worklist_destroy (cgWorklist *wl); + + + +gb_internal u32 cg_type_bit_size(cgModule *m, cgTypeKind type); +gb_internal u32 cg_type_bit_size(cgModule *m, cgType type) { return cg_type_bit_size(m, type.kind); } diff --git a/src/cg/cg_worklist.cpp b/src/cg/cg_worklist.cpp new file mode 100644 index 000000000..45cdd0781 --- /dev/null +++ b/src/cg/cg_worklist.cpp @@ -0,0 +1,112 @@ +gb_internal void cg_worklist_clear_visited(cgWorklist *wl) { + for_array(i, wl->visited) { + wl->visited[i] = 0; + } +} +gb_internal void cg_worklist_clear(cgWorklist *wl) { + cg_worklist_clear_visited(wl); + array_clear(&wl->items); +} +gb_internal void cg_worklist_remove(cgWorklist *wl, cgNode *n) { + u64 gvn_word = n->gvn / 64; + if (gvn_word >= cast(u64)wl->visited.count) { + return; + } + + u64 gvn_mask = 1ull << (n->gvn % 64); + wl->visited[gvn_word] &= ~gvn_mask; +} +gb_internal bool cg_worklist_test(cgWorklist *wl, cgNode *n) { + u64 gvn_word = n->gvn / 64; + if (gvn_word >= cast(u64)wl->visited.count) { + return false; + } + + u64 gvn_mask = 1ull << (n->gvn % 64); + return (wl->visited[gvn_word] & gvn_mask) != 0; +} + +gb_internal bool cg_worklist_test_and_set(cgWorklist *wl, cgNode *n) { + u64 gvn_word = n->gvn / 64; + if (gvn_word >= cast(u64)wl->visited.count) { + isize new_capacity = gvn_word + 16; + + resize_array_raw(&wl->visited.data, heap_allocator(), wl->visited.count*gb_size_of(u64), new_capacity*gb_size_of(u64)); + + for (isize i = wl->visited.count; i < new_capacity; i++) { + wl->visited.data[i] = 0; + } + wl->visited.count = new_capacity; + } + + u64 gvn_mask = 1ull << (n->gvn % 64); + if (wl->visited[gvn_word] & gvn_mask) { + return true; + } else { + wl->visited[gvn_word] |= gvn_mask; + return false; + } +} + + +gb_internal void cg_worklist_push(cgWorklist *wl, cgNode *n) { + if (!cg_worklist_test_and_set(wl, n)) { + array_add(&wl->items, n); + } +} + +gb_internal cgNode *cg_worklist_pop(cgWorklist *wl) { + if (wl->items.count == 0) { + return nullptr; + } + + cgNode *n = array_pop(&wl->items); + u64 gvn_word = n->gvn / 64; + u64 gvn_mask = 1ull << (n->gvn % 64); + + wl->visited[gvn_word] &= ~gvn_mask; + return n; +} + +gb_internal void cg_worklist_replace(cgWorklist *wl, cgNode *n, cgNode *k) { + if (cg_worklist_test(wl, n)) { + for_array(i, wl->items) { + if (wl->items[i] == n) { + cg_worklist_remove(wl, n); + cg_worklist_test_and_set(wl, k); + wl->items[i] = k; + break; + } + } + } +} + + +gb_internal void cg_worklist_init(cgWorklist *wl, isize capacity) { + isize visited_capacity = (capacity + 63)/64; + + wl->visited = slice_make(heap_allocator(), visited_capacity); + wl->items = array_make(heap_allocator(), visited_capacity * 64); + cg_worklist_clear_visited(wl); +} + +gb_internal void cg_worklist_deinit(cgWorklist *wl) { + array_free(&wl->items); + gb_free(heap_allocator(), wl->visited.data); + *wl = {}; +} + + + + +gb_internal cgWorklist *cg_worklist_create() { + cgWorklist *wl = gb_alloc_item(heap_allocator(), cgWorklist); + cg_worklist_init(wl, 512); + return wl; +} +gb_internal void cg_worklist_destroy(cgWorklist *wl) { + cg_worklist_deinit(wl); + gb_free(heap_allocator(), wl); +} + +