diff options
| author | gingerBill <gingerBill@users.noreply.github.com> | 2023-08-07 11:02:01 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-08-07 11:02:01 +0100 |
| commit | 77e5854a16ea9396752d784510169c5856f044ae (patch) | |
| tree | 61f1a2a43bd1ff52a1d7fa02bb4660e524221edf /src/tilde_builtin.cpp | |
| parent | cb5c8219898445a5501a95107c0200ea68b89a39 (diff) | |
| parent | 2a42dab108ea1c70962815cc714c0b4d3e42a719 (diff) | |
Merge branch 'master' into stdlib-parser-fixes
Diffstat (limited to 'src/tilde_builtin.cpp')
| -rw-r--r-- | src/tilde_builtin.cpp | 443 |
1 files changed, 443 insertions, 0 deletions
diff --git a/src/tilde_builtin.cpp b/src/tilde_builtin.cpp new file mode 100644 index 000000000..f036ce583 --- /dev/null +++ b/src/tilde_builtin.cpp @@ -0,0 +1,443 @@ +gb_internal cgValue cg_builtin_len(cgProcedure *p, cgValue value) { + Type *t = base_type(value.type); + + switch (t->kind) { + case Type_Basic: + switch (t->Basic.kind) { + case Basic_string: + { + GB_ASSERT(value.kind == cgValue_Addr); + cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type)); + cgValue len_ptr = cg_emit_struct_ep(p, ptr, 1); + return cg_emit_load(p, len_ptr); + } + case Basic_cstring: + GB_PANIC("TODO(bill): len(cstring)"); + break; + } + break; + case Type_Array: + return cg_const_int(p, t_int, t->Array.count); + case Type_EnumeratedArray: + return cg_const_int(p, t_int, t->EnumeratedArray.count); + case Type_Slice: + { + GB_ASSERT(value.kind == cgValue_Addr); + cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type)); + cgValue len_ptr = cg_emit_struct_ep(p, ptr, 1); + return cg_emit_load(p, len_ptr); + } + case Type_DynamicArray: + { + GB_ASSERT(value.kind == cgValue_Addr); + cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type)); + cgValue len_ptr = cg_emit_struct_ep(p, ptr, 1); + return cg_emit_load(p, len_ptr); + } + case Type_Map: + { + GB_ASSERT(value.kind == cgValue_Addr); + cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type)); + cgValue len_ptr = cg_emit_struct_ep(p, ptr, 1); + return cg_emit_conv(p, cg_emit_load(p, len_ptr), t_int); + } + case Type_Struct: + GB_ASSERT(is_type_soa_struct(t)); + break; + } + + GB_PANIC("TODO(bill): cg_builtin_len %s", type_to_string(t)); + return {}; +} + +gb_internal cgValue cg_builtin_cap(cgProcedure *p, cgValue value) { + Type *t = base_type(value.type); + + switch (t->kind) { + case Type_Basic: + switch (t->Basic.kind) { + case Basic_string: + { + GB_ASSERT(value.kind == cgValue_Addr); + cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type)); + cgValue len_ptr = cg_emit_struct_ep(p, ptr, 1); + return cg_emit_load(p, len_ptr); + } + case Basic_cstring: + GB_PANIC("TODO(bill): cap(cstring)"); + break; + } + break; + case Type_Array: + return cg_const_int(p, t_int, t->Array.count); + case Type_EnumeratedArray: + return cg_const_int(p, t_int, t->EnumeratedArray.count); + case Type_Slice: + { + GB_ASSERT(value.kind == cgValue_Addr); + cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type)); + cgValue len_ptr = cg_emit_struct_ep(p, ptr, 1); + return cg_emit_load(p, len_ptr); + } + case Type_DynamicArray: + { + GB_ASSERT(value.kind == cgValue_Addr); + cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type)); + cgValue len_ptr = cg_emit_struct_ep(p, ptr, 2); + return cg_emit_load(p, len_ptr); + } + case Type_Map: + { + TB_DataType dt_uintptr = cg_data_type(t_uintptr); + TB_Node *zero = tb_inst_uint(p->func, dt_uintptr, 0); + TB_Node *one = tb_inst_uint(p->func, dt_uintptr, 0); + TB_Node *mask = tb_inst_uint(p->func, dt_uintptr, MAP_CACHE_LINE_SIZE-1); + + TB_Node *data = cg_emit_struct_ev(p, value, 0).node; + TB_Node *log2_cap = tb_inst_and(p->func, data, mask); + TB_Node *cap = tb_inst_shl(p->func, one, log2_cap, cast(TB_ArithmeticBehavior)0); + TB_Node *cmp = tb_inst_cmp_eq(p->func, data, zero); + + cgValue res = cg_value(tb_inst_select(p->func, cmp, zero, cap), t_uintptr); + return cg_emit_conv(p, res, t_int); + } + case Type_Struct: + GB_ASSERT(is_type_soa_struct(t)); + break; + } + + GB_PANIC("TODO(bill): cg_builtin_cap %s", type_to_string(t)); + return {}; +} + + +gb_internal cgValue cg_builtin_raw_data(cgProcedure *p, cgValue const &value) { + Type *t = base_type(value.type); + cgValue res = {}; + switch (t->kind) { + case Type_Slice: + { + GB_ASSERT(value.kind == cgValue_Addr); + cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type)); + cgValue data_ptr = cg_emit_struct_ep(p, ptr, 0); + res = cg_emit_load(p, data_ptr); + GB_ASSERT(is_type_multi_pointer(res.type)); + } + break; + case Type_DynamicArray: + { + GB_ASSERT(value.kind == cgValue_Addr); + cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type)); + cgValue data_ptr = cg_emit_struct_ep(p, ptr, 0); + res = cg_emit_load(p, data_ptr); + } + break; + case Type_Basic: + if (t->Basic.kind == Basic_string) { + GB_ASSERT(value.kind == cgValue_Addr); + cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type)); + cgValue data_ptr = cg_emit_struct_ep(p, ptr, 0); + res = cg_emit_load(p, data_ptr); + } else if (t->Basic.kind == Basic_cstring) { + res = cg_emit_conv(p, value, t_u8_multi_ptr); + } + break; + case Type_Pointer: + GB_ASSERT(is_type_array_like(t->Pointer.elem)); + GB_ASSERT(value.kind == cgValue_Value); + res = cg_value(value.node, alloc_type_multi_pointer(base_array_type(t->Pointer.elem))); + break; + case Type_MultiPointer: + + GB_PANIC("TODO(bill) %s", type_to_string(value.type)); + // res = cg_emit_conv(p, value, tv.type); + break; + } + GB_ASSERT(res.node != nullptr); + return res; +} + +gb_internal cgValue cg_builtin_min(cgProcedure *p, Type *t, cgValue x, cgValue y) { + x = cg_emit_conv(p, x, t); + y = cg_emit_conv(p, y, t); + return cg_emit_select(p, cg_emit_comp(p, Token_Lt, x, y), x, y); +} +gb_internal cgValue cg_builtin_max(cgProcedure *p, Type *t, cgValue x, cgValue y) { + x = cg_emit_conv(p, x, t); + y = cg_emit_conv(p, y, t); + return cg_emit_select(p, cg_emit_comp(p, Token_Gt, x, y), x, y); +} + +gb_internal cgValue cg_builtin_abs(cgProcedure *p, cgValue const &x) { + if (is_type_unsigned(x.type)) { + return x; + } + + if (is_type_quaternion(x.type)) { + GB_PANIC("TODO(bill): abs quaternion"); + } else if (is_type_complex(x.type)) { + GB_PANIC("TODO(bill): abs complex"); + } + + TB_DataType dt = cg_data_type(x.type); + GB_ASSERT(!TB_IS_VOID_TYPE(dt)); + TB_Node *zero = nullptr; + if (dt.type == TB_FLOAT) { + if (dt.data == 32) { + zero = tb_inst_float32(p->func, 0); + } else if (dt.data == 64) { + zero = tb_inst_float64(p->func, 0); + } + } else { + zero = tb_inst_uint(p->func, dt, 0); + } + GB_ASSERT(zero != nullptr); + + cgValue cond = cg_emit_comp(p, Token_Lt, x, cg_value(zero, x.type)); + cgValue neg = cg_emit_unary_arith(p, Token_Sub, x, x.type); + return cg_emit_select(p, cond, neg, x); +} + +gb_internal cgValue cg_builtin_clamp(cgProcedure *p, Type *t, cgValue const &x, cgValue const &min, cgValue const &max) { + cgValue z = x; + z = cg_builtin_max(p, t, z, min); + z = cg_builtin_min(p, t, z, max); + return z; +} + + + +gb_internal cgValue cg_builtin_mem_zero(cgProcedure *p, cgValue const &ptr, cgValue const &len) { + GB_ASSERT(ptr.kind == cgValue_Value); + GB_ASSERT(len.kind == cgValue_Value); + tb_inst_memzero(p->func, ptr.node, len.node, 1, false); + return ptr; +} + +gb_internal cgValue cg_builtin_mem_copy(cgProcedure *p, cgValue const &dst, cgValue const &src, cgValue const &len) { + GB_ASSERT(dst.kind == cgValue_Value); + GB_ASSERT(src.kind == cgValue_Value); + GB_ASSERT(len.kind == cgValue_Value); + // TODO(bill): This needs to be memmove + tb_inst_memcpy(p->func, dst.node, src.node, len.node, 1, false); + return dst; +} + +gb_internal cgValue cg_builtin_mem_copy_non_overlapping(cgProcedure *p, cgValue const &dst, cgValue const &src, cgValue const &len) { + GB_ASSERT(dst.kind == cgValue_Value); + GB_ASSERT(src.kind == cgValue_Value); + GB_ASSERT(len.kind == cgValue_Value); + tb_inst_memcpy(p->func, dst.node, src.node, len.node, 1, false); + return dst; +} + + +gb_internal cgValue cg_build_builtin(cgProcedure *p, BuiltinProcId id, Ast *expr) { + ast_node(ce, CallExpr, expr); + + if (BuiltinProc__simd_begin < id && id < BuiltinProc__simd_end) { + GB_PANIC("TODO(bill): cg_build_builtin_simd_proc"); + // return cg_build_builtin_simd_proc(p, expr, tv, id); + } + + String builtin_name = builtin_procs[id].name; + + switch (id) { + case BuiltinProc_DIRECTIVE: { + ast_node(bd, BasicDirective, ce->proc); + String name = bd->name.string; + GB_ASSERT(name == "location"); + String procedure = p->entity->token.string; + TokenPos pos = ast_token(ce->proc).pos; + if (ce->args.count > 0) { + Ast *ident = unselector_expr(ce->args[0]); + GB_ASSERT(ident->kind == Ast_Ident); + Entity *e = entity_of_node(ident); + GB_ASSERT(e != nullptr); + + if (e->parent_proc_decl != nullptr && e->parent_proc_decl->entity != nullptr) { + procedure = e->parent_proc_decl->entity->token.string; + } else { + procedure = str_lit(""); + } + pos = e->token.pos; + + } + return cg_emit_source_code_location_as_global(p, procedure, pos); + } break; + + case BuiltinProc_len: { + cgValue v = cg_build_expr(p, ce->args[0]); + Type *t = base_type(v.type); + if (is_type_pointer(t)) { + // IMPORTANT TODO(bill): Should there be a nil pointer check? + v = cg_emit_load(p, v); + t = type_deref(t); + } + return cg_builtin_len(p, v); + } + + case BuiltinProc_cap: { + cgValue v = cg_build_expr(p, ce->args[0]); + Type *t = base_type(v.type); + if (is_type_pointer(t)) { + // IMPORTANT TODO(bill): Should there be a nil pointer check? + v = cg_emit_load(p, v); + t = type_deref(t); + } + return cg_builtin_cap(p, v); + } + + case BuiltinProc_raw_data: + { + cgValue v = cg_build_expr(p, ce->args[0]); + return cg_builtin_raw_data(p, v); + } + + case BuiltinProc_min: + if (ce->args.count == 2) { + Type *t = type_of_expr(expr); + cgValue x = cg_build_expr(p, ce->args[0]); + cgValue y = cg_build_expr(p, ce->args[1]); + return cg_builtin_min(p, t, x, y); + } else { + Type *t = type_of_expr(expr); + cgValue x = cg_build_expr(p, ce->args[0]); + for (isize i = 1; i < ce->args.count; i++) { + cgValue y = cg_build_expr(p, ce->args[i]); + x = cg_builtin_min(p, t, x, y); + } + return x; + } + break; + case BuiltinProc_max: + if (ce->args.count == 2) { + Type *t = type_of_expr(expr); + cgValue x = cg_build_expr(p, ce->args[0]); + cgValue y = cg_build_expr(p, ce->args[1]); + return cg_builtin_max(p, t, x, y); + } else { + Type *t = type_of_expr(expr); + cgValue x = cg_build_expr(p, ce->args[0]); + for (isize i = 1; i < ce->args.count; i++) { + cgValue y = cg_build_expr(p, ce->args[i]); + x = cg_builtin_max(p, t, x, y); + } + return x; + } + break; + + case BuiltinProc_abs: + { + cgValue x = cg_build_expr(p, ce->args[0]); + return cg_builtin_abs(p, x); + } + + case BuiltinProc_clamp: + { + cgValue x = cg_build_expr(p, ce->args[0]); + cgValue min = cg_build_expr(p, ce->args[1]); + cgValue max = cg_build_expr(p, ce->args[2]); + return cg_builtin_clamp(p, type_of_expr(expr), x, min, max); + } + + case BuiltinProc_debug_trap: + tb_inst_debugbreak(p->func); + return {}; + case BuiltinProc_trap: + tb_inst_trap(p->func); + return {}; + + case BuiltinProc_mem_zero: + { + cgValue ptr = cg_build_expr(p, ce->args[0]); + cgValue len = cg_build_expr(p, ce->args[1]); + return cg_builtin_mem_zero(p, ptr, len); + } + + case BuiltinProc_mem_copy: + { + cgValue dst = cg_build_expr(p, ce->args[0]); + cgValue src = cg_build_expr(p, ce->args[1]); + cgValue len = cg_build_expr(p, ce->args[2]); + return cg_builtin_mem_copy(p, dst, src, len); + } + + case BuiltinProc_mem_copy_non_overlapping: + { + cgValue dst = cg_build_expr(p, ce->args[0]); + cgValue src = cg_build_expr(p, ce->args[1]); + cgValue len = cg_build_expr(p, ce->args[2]); + return cg_builtin_mem_copy_non_overlapping(p, dst, src, len); + } + + + case BuiltinProc_overflow_add: + { + Type *res_type = type_of_expr(expr); + GB_ASSERT(res_type->kind == Type_Tuple); + GB_ASSERT(res_type->Tuple.variables.count == 2); + // TODO(bill): do a proper overflow add + Type *type = res_type->Tuple.variables[0]->type; + Type *ok_type = res_type->Tuple.variables[1]->type; + cgValue x = cg_build_expr(p, ce->args[0]); + cgValue y = cg_build_expr(p, ce->args[1]); + x = cg_emit_conv(p, x, type); + y = cg_emit_conv(p, y, type); + cgValue res = cg_emit_arith(p, Token_Add, x, y, type); + cgValue ok = cg_const_int(p, ok_type, false); + + return cg_value_multi2(res, ok, res_type); + } + + + case BuiltinProc_ptr_offset: + { + cgValue ptr = cg_build_expr(p, ce->args[0]); + cgValue len = cg_build_expr(p, ce->args[1]); + len = cg_emit_conv(p, len, t_int); + return cg_emit_ptr_offset(p, ptr, len); + } + case BuiltinProc_ptr_sub: + { + Type *elem0 = type_deref(type_of_expr(ce->args[0])); + Type *elem1 = type_deref(type_of_expr(ce->args[1])); + GB_ASSERT(are_types_identical(elem0, elem1)); + Type *elem = elem0; + + cgValue ptr0 = cg_emit_conv(p, cg_build_expr(p, ce->args[0]), t_uintptr); + cgValue ptr1 = cg_emit_conv(p, cg_build_expr(p, ce->args[1]), t_uintptr); + + cgValue diff = cg_emit_arith(p, Token_Sub, ptr0, ptr1, t_uintptr); + diff = cg_emit_conv(p, diff, t_int); + return cg_emit_arith(p, Token_Quo, diff, cg_const_int(p, t_int, type_size_of(elem)), t_int); + } + + case BuiltinProc_type_info_of: + { + Ast *arg = ce->args[0]; + TypeAndValue tav = type_and_value_of_expr(arg); + if (tav.mode == Addressing_Type) { + Type *t = default_type(type_of_expr(arg)); + return cg_type_info(p, t); + } + GB_ASSERT(is_type_typeid(tav.type)); + + auto args = slice_make<cgValue>(permanent_allocator(), 1); + args[0] = cg_build_expr(p, arg); + return cg_emit_runtime_call(p, "__type_info_of", args); + } + + + case BuiltinProc_type_equal_proc: + return cg_equal_proc_value_for_type(p, ce->args[0]->tav.type); + + case BuiltinProc_type_hasher_proc: + return cg_hasher_proc_value_for_type(p, ce->args[0]->tav.type); + } + + + GB_PANIC("TODO(bill): builtin procs %d %.*s", id, LIT(builtin_name)); + return {}; +} + |