Browse Source

`soa_zip` (-llvm-api only): creates an `#soa[]struct` from passed slices

x := []i32{1, 3, 9};
y := []f32{2, 4, 16};
z := []b32{true, false, true};

s_anonymous := soa_zip(x, y, z);
assert(s_anonymous[0]._1 == 2);

s_named := soa_zip(a=x, b=y, c=z);
assert(s_anonymous[0].b == 2);
gingerBill 4 years ago
parent
commit
989a03dc77
3 changed files with 149 additions and 2 deletions
  1. 114 2
      src/check_expr.cpp
  2. 4 0
      src/checker_builtin_procs.hpp
  3. 31 0
      src/llvm_backend.cpp

+ 114 - 2
src/check_expr.cpp

@@ -3836,8 +3836,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 
 
 	if (ce->args.count > 0) {
 	if (ce->args.count > 0) {
 		if (ce->args[0]->kind == Ast_FieldValue) {
 		if (ce->args[0]->kind == Ast_FieldValue) {
-			error(call, "'field = value' calling is not allowed on built-in procedures");
-			return false;
+			if (id != BuiltinProc_soa_zip) {
+				error(call, "'field = value' calling is not allowed on built-in procedures");
+				return false;
+			}
 		}
 		}
 	}
 	}
 
 
@@ -5230,6 +5232,116 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 		break;
 		break;
 	}
 	}
 
 
+	case BuiltinProc_soa_zip: {
+		if (!build_context.use_llvm_api) {
+			error(call, "'soa_zip' is not supported with this backend");
+			return false;
+		}
+
+		auto types = array_make<Type *>(temporary_allocator(), 0, ce->args.count);
+		auto names = array_make<String>(temporary_allocator(), 0, ce->args.count);
+
+		bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue);
+
+		bool fail = false;
+		for_array(i, ce->args) {
+			Ast *arg = ce->args[i];
+			bool mix = false;
+			if (first_is_field_value) {
+				mix = arg->kind != Ast_FieldValue;
+			} else {
+				mix = arg->kind == Ast_FieldValue;
+			}
+			if (mix) {
+				error(arg, "Mixture of 'field = value' and value elements in the procedure call 'soa_zip' is not allowed");
+				fail = true;
+				break;
+			}
+		}
+		StringSet name_set = {};
+		string_set_init(&name_set, temporary_allocator(), 2*ce->args.count);
+
+		for_array(i, ce->args) {
+			String name = {};
+			Ast *arg = ce->args[i];
+			if (arg->kind == Ast_FieldValue) {
+				Ast *ename = arg->FieldValue.field;
+				if (!fail && ename->kind != Ast_Ident) {
+					error(ename, "Expected an identifier for field argument");
+				} else if (ename->kind == Ast_Ident) {
+					name = ename->Ident.token.string;
+				}
+				arg = arg->FieldValue.value;
+			}
+
+			Operand op = {};
+			check_expr(c, &op, arg);
+			if (op.mode == Addressing_Invalid) {
+				return false;
+			}
+			Type *arg_type = base_type(op.type);
+			if (!is_type_slice(arg_type)) {
+				gbString s = type_to_string(op.type);
+				error(op.expr, "Indices to 'soa_zip' must be slices, got %s", s);
+				gb_string_free(s);
+				return false;
+			}
+			GB_ASSERT(arg_type->kind == Type_Slice);
+			if (name == "_") {
+				error(op.expr, "Field argument name '%.*s' is not allowed", LIT(name));
+				name = {};
+			}
+			if (name.len == 0) {
+				gbString field_name = gb_string_make(permanent_allocator(), "_");
+				field_name = gb_string_append_fmt(field_name, "%td", types.count);
+				name = make_string_c(field_name);
+			}
+
+
+			if (string_set_exists(&name_set, name)) {
+				error(op.expr, "Field argument name '%.*s' already exists", LIT(name));
+			} else {
+				array_add(&types, arg_type->Slice.elem);
+				array_add(&names, name);
+
+				string_set_add(&name_set, name);
+			}
+		}
+
+
+
+
+		Ast *dummy_node_struct = alloc_ast_node(nullptr, Ast_Invalid);
+		Ast *dummy_node_soa = alloc_ast_node(nullptr, Ast_Invalid);
+		Scope *s = create_scope(builtin_pkg->scope);
+
+		auto fields = array_make<Entity *>(permanent_allocator(), 0, types.count);
+		for_array(i, types) {
+			Type *type = types[i];
+			String name = names[i];
+			GB_ASSERT(name != "");
+			Entity *e = alloc_entity_field(s, make_token_ident(name), type, false, cast(i32)i, EntityState_Resolved);
+			array_add(&fields, e);
+			scope_insert(s, e);
+		}
+
+		Type *elem = alloc_type_struct();
+		elem->Struct.scope = s;
+		elem->Struct.fields = fields;
+		elem->Struct.tags = array_make<String>(permanent_allocator(), fields.count);
+		elem->Struct.node = dummy_node_struct;
+		type_set_offsets(elem);
+
+		Type *soa_type = make_soa_struct_slice(c, dummy_node_soa, nullptr, elem);
+		type_set_offsets(soa_type);
+
+		operand->type = soa_type;
+		operand->mode = Addressing_Value;
+
+		break;
+	}
+
+
 	case BuiltinProc_simd_vector: {
 	case BuiltinProc_simd_vector: {
 		Operand x = {};
 		Operand x = {};
 		Operand y = {};
 		Operand y = {};

+ 4 - 0
src/checker_builtin_procs.hpp

@@ -30,6 +30,8 @@ enum BuiltinProcId {
 	BuiltinProc_abs,
 	BuiltinProc_abs,
 	BuiltinProc_clamp,
 	BuiltinProc_clamp,
 
 
+	BuiltinProc_soa_zip,
+
 	BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures
 	BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures
 
 
 	// "Intrinsics"
 	// "Intrinsics"
@@ -225,6 +227,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 	{STR_LIT("abs"),              1, false, Expr_Expr, BuiltinProcPkg_builtin},
 	{STR_LIT("abs"),              1, false, Expr_Expr, BuiltinProcPkg_builtin},
 	{STR_LIT("clamp"),            3, false, Expr_Expr, BuiltinProcPkg_builtin},
 	{STR_LIT("clamp"),            3, false, Expr_Expr, BuiltinProcPkg_builtin},
 
 
+	{STR_LIT("soa_zip"),          1, true, Expr_Expr, BuiltinProcPkg_builtin},
+
 	{STR_LIT(""),                 0, true,  Expr_Expr, BuiltinProcPkg_builtin}, // DIRECTIVE
 	{STR_LIT(""),                 0, true,  Expr_Expr, BuiltinProcPkg_builtin}, // DIRECTIVE
 
 
 
 

+ 31 - 0
src/llvm_backend.cpp

@@ -8250,8 +8250,36 @@ lbValue lb_soa_struct_cap(lbProcedure *p, lbValue value) {
 	return lb_emit_struct_ev(p, value, cast(i32)n);
 	return lb_emit_struct_ev(p, value, cast(i32)n);
 }
 }
 
 
+lbValue lb_soa_zip(lbProcedure *p, AstCallExpr *ce, TypeAndValue const &tv) {
+	GB_ASSERT(ce->args.count > 0);
 
 
+	auto slices = slice_make<lbValue>(temporary_allocator(), ce->args.count);
+	for_array(i, slices) {
+		Ast *arg = ce->args[i];
+		if (arg->kind == Ast_FieldValue) {
+			arg = arg->FieldValue.value;
+		}
+		slices[i] = lb_build_expr(p, arg);
+	}
 
 
+	lbValue len = lb_slice_len(p, slices[0]);
+	for (isize i = 1; i < slices.count; i++) {
+		lbValue other_len = lb_slice_len(p, slices[i]);
+		len = lb_emit_min(p, t_int, len, other_len);
+	}
+
+	GB_ASSERT(is_type_soa_struct(tv.type));
+	lbAddr res = lb_add_local_generated(p, tv.type, true);
+	for_array(i, slices) {
+		lbValue src = lb_slice_elem(p, slices[i]);
+		lbValue dst = lb_emit_struct_ep(p, res.addr, cast(i32)i);
+		lb_emit_store(p, dst, src);
+	}
+	lbValue len_dst = lb_emit_struct_ep(p, res.addr, cast(i32)slices.count);
+	lb_emit_store(p, len_dst, len);
+
+	return lb_addr_load(p, res);
+}
 
 
 lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, BuiltinProcId id) {
 lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, BuiltinProcId id) {
 	ast_node(ce, CallExpr, expr);
 	ast_node(ce, CallExpr, expr);
@@ -8642,6 +8670,9 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
 		                     lb_build_expr(p, ce->args[2]));
 		                     lb_build_expr(p, ce->args[2]));
 
 
 
 
+	case BuiltinProc_soa_zip:
+		return lb_soa_zip(p, ce, tv);
+
 
 
 	// "Intrinsics"
 	// "Intrinsics"