aboutsummaryrefslogtreecommitdiff
path: root/src/tilde_builtin.cpp
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2023-08-07 11:02:01 +0100
committerGitHub <noreply@github.com>2023-08-07 11:02:01 +0100
commit77e5854a16ea9396752d784510169c5856f044ae (patch)
tree61f1a2a43bd1ff52a1d7fa02bb4660e524221edf /src/tilde_builtin.cpp
parentcb5c8219898445a5501a95107c0200ea68b89a39 (diff)
parent2a42dab108ea1c70962815cc714c0b4d3e42a719 (diff)
Merge branch 'master' into stdlib-parser-fixes
Diffstat (limited to 'src/tilde_builtin.cpp')
-rw-r--r--src/tilde_builtin.cpp443
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 {};
+}
+