소스 검색

Add `compress_values`

gingerBill 4 달 전
부모
커밋
d2d4ac8120
4개의 변경된 파일246개의 추가작업 그리고 1개의 파일을 삭제
  1. 2 1
      base/builtin/builtin.odin
  2. 180 0
      src/check_builtin.cpp
  3. 2 0
      src/checker_builtin_procs.hpp
  4. 62 0
      src/llvm_backend_proc.cpp

+ 2 - 1
base/builtin/builtin.odin

@@ -119,7 +119,8 @@ jmag       :: proc(value: Quaternion) -> Float ---
 kmag       :: proc(value: Quaternion) -> Float ---
 conj       :: proc(value: Complex_Or_Quaternion) -> Complex_Or_Quaternion ---
 
-expand_values :: proc(value: Struct_Or_Array) -> (A, B, C, ...) ---
+expand_values   :: proc(value: Struct_Or_Array) -> (A, B, C, ...) ---
+compress_values :: proc(values: ...) -> Struct_Or_Array_Like_Type ---
 
 min   :: proc(values: ..T) -> T ---
 max   :: proc(values: ..T) -> T ---

+ 180 - 0
src/check_builtin.cpp

@@ -3243,6 +3243,186 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
 		break;
 	}
 
+	case BuiltinProc_compress_values: {
+		Operand *ops = gb_alloc_array(temporary_allocator(), Operand, ce->args.count);
+
+		isize value_count = 0;
+
+		for_array(i, ce->args) {
+			Ast *arg = ce->args[i];
+			Operand *op = &ops[i];
+			check_multi_expr(c, op, arg);
+			if (op->mode == Addressing_Invalid) {
+				return false;
+			}
+
+			if (op->type == nullptr || op->type == t_invalid) {
+				gbString s = expr_to_string(op->expr);
+				error(op->expr, "Invalid expression to '%.*s', got %s", LIT(builtin_name), s);
+				gb_string_free(s);
+			}
+			if (is_type_tuple(op->type)) {
+				value_count += op->type->Tuple.variables.count;
+			} else {
+				value_count += 1;
+			}
+		}
+
+		GB_ASSERT(value_count >= 1);
+
+		if (value_count == 1) {
+			*operand = ops[0];
+			break;
+		}
+
+		if (type_hint != nullptr) {
+			Type *th = base_type(type_hint);
+			if (th->kind == Type_Struct) {
+				if (value_count == th->Struct.fields.count) {
+					isize index = 0;
+					for_array(i, ce->args) {
+						Operand *op = &ops[i];
+						if (is_type_tuple(op->type)) {
+							for (Entity *v : op->type->Tuple.variables) {
+								Operand x = {};
+								x.mode = Addressing_Value;
+								x.type = v->type;
+								check_assignment(c, &x, th->Struct.fields[index++]->type, builtin_name);
+								if (x.mode == Addressing_Invalid) {
+									return false;
+								}
+							}
+						} else {
+							check_assignment(c, op, th->Struct.fields[index++]->type, builtin_name);
+							if (op->mode == Addressing_Invalid) {
+								return false;
+							}
+						}
+					}
+
+					operand->type = type_hint;
+					operand->mode = Addressing_Value;
+					break;
+				}
+			} else if (is_type_array_like(th)) {
+				if (cast(i64)value_count == get_array_type_count(th)) {
+					Type *elem = base_array_type(th);
+					for_array(i, ce->args) {
+						Operand *op = &ops[i];
+						if (is_type_tuple(op->type)) {
+							for (Entity *v : op->type->Tuple.variables) {
+								Operand x = {};
+								x.mode = Addressing_Value;
+								x.type = v->type;
+								check_assignment(c, &x, elem, builtin_name);
+								if (x.mode == Addressing_Invalid) {
+									return false;
+								}
+							}
+						} else {
+							check_assignment(c, op, elem, builtin_name);
+							if (op->mode == Addressing_Invalid) {
+								return false;
+							}
+						}
+					}
+
+					operand->type = type_hint;
+					operand->mode = Addressing_Value;
+					break;
+				}
+			}
+		}
+
+		bool all_types_the_same = true;
+		Type *last_type = nullptr;
+		for_array(i, ce->args) {
+			Operand *op = &ops[i];
+			if (is_type_tuple(op->type)) {
+				if (last_type == nullptr) {
+					op->type->Tuple.variables[0]->type;
+				}
+				for (Entity *v : op->type->Tuple.variables) {
+					if (!are_types_identical(last_type, v->type)) {
+						all_types_the_same = false;
+						break;
+					}
+					last_type = v->type;
+				}
+			} else {
+				if (last_type == nullptr) {
+					last_type = op->type;
+				} else {
+					if (!are_types_identical(last_type, op->type)) {
+						all_types_the_same = false;
+						break;
+					}
+					last_type = op->type;
+				}
+			}
+		}
+
+		if (all_types_the_same) {
+			operand->type = alloc_type_array(last_type, value_count);
+			operand->mode = Addressing_Value;
+		} else {
+			Type *st = alloc_type_struct_complete();
+			st->Struct.fields = slice_make<Entity *>(permanent_allocator(), value_count);
+			st->Struct.tags = gb_alloc_array(permanent_allocator(), String, value_count);
+			st->Struct.offsets = gb_alloc_array(permanent_allocator(), i64, value_count);
+
+			Scope *scope = create_scope(c->info, nullptr);
+
+			Token token = {};
+			token.kind = Token_Ident;
+			token.pos = ast_token(call).pos;
+
+			isize index = 0;
+			for_array(i, ce->args) {
+				Operand *op = &ops[i];
+				if (is_type_tuple(op->type)) {
+					for (Entity *v : op->type->Tuple.variables) {
+						Type *t = default_type(v->type);
+						if (is_type_untyped(t)) {
+							gbString s = expr_to_string(op->expr);
+							error(op->expr, "Invalid use of '%s' in '%.*s'", s, LIT(builtin_name));
+							gb_string_free(s);
+							return false;
+						}
+
+						gbString s = gb_string_make_reserve(permanent_allocator(), 32);
+						s = gb_string_append_fmt(s, "v%lld", cast(long long)index);
+						token.string = make_string_c(s);
+						Entity *e = alloc_entity_field(scope, token, t, false, cast(i32)index, EntityState_Resolved);
+						st->Struct.fields[index++] = e;
+					}
+				} else {
+					Type *t = default_type(op->type);
+					if (is_type_untyped(t)) {
+						gbString s = expr_to_string(op->expr);
+						error(op->expr, "Invalid use of '%s' in '%.*s'", s, LIT(builtin_name));
+						gb_string_free(s);
+						return false;
+					}
+
+					gbString s = gb_string_make_reserve(permanent_allocator(), 32);
+					s = gb_string_append_fmt(s, "v%lld", cast(long long)index);
+					token.string = make_string_c(s);
+					Entity *e = alloc_entity_field(scope, token, t, false, cast(i32)index, EntityState_Resolved);
+					st->Struct.fields[index++] = e;
+				}
+			}
+
+
+			gb_unused(type_size_of(st));
+
+			operand->type = st;
+			operand->mode = Addressing_Value;
+		}
+		break;
+	}
+
+
 	case BuiltinProc_min: {
 		// min :: proc($T: typeid) -> ordered
 		// min :: proc(a: ..ordered) -> ordered

+ 2 - 0
src/checker_builtin_procs.hpp

@@ -26,6 +26,7 @@ enum BuiltinProcId {
 	BuiltinProc_conj,
 
 	BuiltinProc_expand_values,
+	BuiltinProc_compress_values,
 
 	BuiltinProc_min,
 	BuiltinProc_max,
@@ -376,6 +377,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 	{STR_LIT("conj"),             1, false, Expr_Expr, BuiltinProcPkg_builtin},
 
 	{STR_LIT("expand_values"),    1, false, Expr_Expr, BuiltinProcPkg_builtin},
+	{STR_LIT("compress_values"),  1, true,  Expr_Expr, BuiltinProcPkg_builtin},
 
 	{STR_LIT("min"),              1, true,  Expr_Expr, BuiltinProcPkg_builtin},
 	{STR_LIT("max"),              1, true,  Expr_Expr, BuiltinProcPkg_builtin},

+ 62 - 0
src/llvm_backend_proc.cpp

@@ -2246,6 +2246,68 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
 		return lb_emit_load(p, tuple);
 	}
 
+	case BuiltinProc_compress_values: {
+		isize value_count = 0;
+		for (Ast *arg : ce->args) {
+			Type *t = arg->tav.type;
+			if (is_type_tuple(t)) {
+				value_count += t->Tuple.variables.count;
+			} else {
+				value_count += 1;
+			}
+		}
+
+		if (value_count == 1) {
+			lbValue x = lb_build_expr(p, ce->args[0]);
+			x = lb_emit_conv(p, x, tv.type);
+			return x;
+		}
+
+		Type *dt = base_type(tv.type);
+		lbAddr addr = lb_add_local_generated(p, tv.type, true);
+		if (is_type_struct(dt) || is_type_tuple(dt)) {
+			i32 index = 0;
+			for (Ast *arg : ce->args) {
+				lbValue x = lb_build_expr(p, arg);
+				if (is_type_tuple(x.type)) {
+					for (isize i = 0; i < x.type->Tuple.variables.count; i++) {
+						lbValue y = lb_emit_tuple_ev(p, x, cast(i32)i);
+						lbValue ptr = lb_emit_struct_ep(p, addr.addr, index++);
+						y = lb_emit_conv(p, y, type_deref(ptr.type));
+						lb_emit_store(p, ptr, y);
+					}
+				} else {
+					lbValue ptr = lb_emit_struct_ep(p, addr.addr, index++);
+					x = lb_emit_conv(p, x, type_deref(ptr.type));
+					lb_emit_store(p, ptr, x);
+				}
+			}
+			GB_ASSERT(index == value_count);
+		} else if (is_type_array_like(dt)) {
+			i32 index = 0;
+			for (Ast *arg : ce->args) {
+				lbValue x = lb_build_expr(p, arg);
+				if (is_type_tuple(x.type)) {
+					for (isize i = 0; i < x.type->Tuple.variables.count; i++) {
+						lbValue y = lb_emit_tuple_ev(p, x, cast(i32)i);
+						lbValue ptr = lb_emit_array_epi(p, addr.addr, index++);
+						y = lb_emit_conv(p, y, type_deref(ptr.type));
+						lb_emit_store(p, ptr, y);
+					}
+				} else {
+					lbValue ptr = lb_emit_array_epi(p, addr.addr, index++);
+					x = lb_emit_conv(p, x, type_deref(ptr.type));
+					lb_emit_store(p, ptr, x);
+				}
+			}
+			GB_ASSERT(index == value_count);
+		} else {
+			GB_PANIC("TODO(bill): compress_values -> %s", type_to_string(tv.type));
+		}
+
+		return lb_addr_load(p, addr);
+	}
+
 	case BuiltinProc_min: {
 		Type *t = type_of_expr(expr);
 		if (ce->args.count == 2) {