Explorar el Código

Add arithmetic operator support for simd vectors; Add `intrinsics.simd_and_not`

gingerBill hace 3 años
padre
commit
d0e8a735ba

+ 13 - 3
core/simd/simd.odin

@@ -43,9 +43,10 @@ shr_masked :: intrinsics.simd_shr_masked
 add_sat :: intrinsics.simd_add_sat
 sub_sat :: intrinsics.simd_sub_sat
 
-and :: intrinsics.simd_and
-or  :: intrinsics.simd_or
-xor :: intrinsics.simd_xor
+and     :: intrinsics.simd_and
+or      :: intrinsics.simd_or
+xor     :: intrinsics.simd_xor
+and_not :: intrinsics.simd_and_not
 
 neg :: intrinsics.simd_neg
 
@@ -132,3 +133,12 @@ copysign :: #force_inline proc "contextless" (v, sign: $T/#simd[$LANES]$E) -> T
 	magnitude := and(to_bits(v), bit_not(neg_zero))
 	return transmute(T)or(sign_bit, magnitude)
 }
+
+signum :: #force_inline proc "contextless" (v: $T/#simd[$LANES]$E) -> T where intrinsics.type_is_float(E) {
+	is_nan := ne(v, v)
+	return select(is_nan, v, copysign(T(1), v))
+}
+
+recip :: #force_inline proc "contextless" (v: $T/#simd[$LANES]$E) -> T where intrinsics.type_is_float(E) {
+	return div(T(1), v)
+}

+ 1 - 0
src/check_builtin.cpp

@@ -464,6 +464,7 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call
 	case BuiltinProc_simd_and:
 	case BuiltinProc_simd_or:
 	case BuiltinProc_simd_xor:
+	case BuiltinProc_simd_and_not:
 		{
 			Operand x = {};
 			Operand y = {};

+ 2 - 5
src/check_expr.cpp

@@ -1590,11 +1590,6 @@ bool check_unary_op(CheckerContext *c, Operand *o, Token op) {
 bool check_binary_op(CheckerContext *c, Operand *o, Token op) {
 	Type *main_type = o->type;
 
-	if (is_type_simd_vector(main_type)) {
-		error(op, "Operator '%.*s' is not supported on #simd vector types, please use the intrinsics.simd_*", LIT(op.string));
-		return false;
-	}
-
 	// TODO(bill): Handle errors correctly
 	Type *type = base_type(core_array_type(main_type));
 	Type *ct = core_type(type);
@@ -2500,6 +2495,8 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ
 		gb_string_free(err_str);
 	}
 
+	// TODO(bill): Should we support shifts for fixed arrays and #simd vectors?
+
 	if (!is_type_integer(x->type)) {
 		gbString err_str = expr_to_string(y->expr);
 		error(node, "Shift operand '%s' must be an integer", err_str);

+ 3 - 0
src/checker_builtin_procs.hpp

@@ -135,6 +135,7 @@ BuiltinProc__simd_begin,
 	BuiltinProc_simd_and,
 	BuiltinProc_simd_or,
 	BuiltinProc_simd_xor,
+	BuiltinProc_simd_and_not,
 
 	BuiltinProc_simd_neg,
 	BuiltinProc_simd_abs,
@@ -417,6 +418,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 	{STR_LIT("simd_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("simd_or"),  2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("simd_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+	{STR_LIT("simd_and_not"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
 	{STR_LIT("simd_neg"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 
 	{STR_LIT("simd_abs"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},

+ 39 - 1
src/llvm_backend_expr.cpp

@@ -258,7 +258,13 @@ lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x, Type *type)
 			LLVMBuildStore(p->builder, v2, LLVMBuildStructGEP(p->builder, addr.addr.value, 2, ""));
 			LLVMBuildStore(p->builder, v3, LLVMBuildStructGEP(p->builder, addr.addr.value, 3, ""));
 			return lb_addr_load(p, addr);
-
+		} else if (is_type_simd_vector(x.type)) {
+			Type *elem = base_array_type(x.type);
+			if (is_type_float(elem)) {
+				res.value = LLVMBuildFNeg(p->builder, x.value, "");
+			} else {
+				res.value = LLVMBuildNeg(p->builder, x.value, "");
+			}
 		} else {
 			GB_PANIC("Unhandled type %s", type_to_string(x.type));
 		}
@@ -2559,6 +2565,38 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri
 		case Token_NotEq: pred = LLVMIntNE;  break;
 		}
 		res.value = LLVMBuildICmp(p->builder, pred, left.value, right.value, "");
+	} else if (is_type_simd_vector(a)) {
+		LLVMValueRef mask = nullptr;
+		Type *elem = base_array_type(a);
+		if (is_type_float(elem)) {
+			LLVMRealPredicate pred = {};
+			switch (op_kind) {
+			case Token_CmpEq: pred = LLVMRealOEQ; break;
+			case Token_NotEq: pred = LLVMRealONE; break;
+			}
+			mask = LLVMBuildFCmp(p->builder, pred, left.value, right.value, "");
+		} else {
+			LLVMIntPredicate pred = {};
+			switch (op_kind) {
+			case Token_CmpEq: pred = LLVMIntEQ; break;
+			case Token_NotEq: pred = LLVMIntNE; break;
+			}
+			mask = LLVMBuildICmp(p->builder, pred, left.value, right.value, "");
+		}
+		GB_ASSERT_MSG(mask != nullptr, "Unhandled comparison kind %s (%s) %.*s %s (%s)", type_to_string(left.type), type_to_string(base_type(left.type)), LIT(token_strings[op_kind]), type_to_string(right.type), type_to_string(base_type(right.type)));
+
+		// TODO(bill): is this a good approach to dealing with comparisons of vectors?
+		char const *name = "llvm.vector.reduce.umax";
+		LLVMTypeRef types[1] = {LLVMTypeOf(mask)};
+		unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+		GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
+		LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+		LLVMValueRef args[1] = {};
+		args[0] = mask;
+		res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+		return res;
+
 	} else {
 		GB_PANIC("Unhandled comparison kind %s (%s) %.*s %s (%s)", type_to_string(left.type), type_to_string(base_type(left.type)), LIT(token_strings[op_kind]), type_to_string(right.type), type_to_string(base_type(right.type)));
 	}

+ 4 - 0
src/llvm_backend_proc.cpp

@@ -1097,11 +1097,15 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const
 	case BuiltinProc_simd_and:
 	case BuiltinProc_simd_or:
 	case BuiltinProc_simd_xor:
+	case BuiltinProc_simd_and_not:
 		arg1 = lb_build_expr(p, ce->args[1]);
 		switch (builtin_id) {
 		case BuiltinProc_simd_and: op_code = LLVMAnd; break;
 		case BuiltinProc_simd_or:  op_code = LLVMOr;  break;
 		case BuiltinProc_simd_xor: op_code = LLVMXor; break;
+		case BuiltinProc_simd_and_not:
+			res.value = LLVMBuildAnd(p->builder, arg0.value, LLVMBuildNot(p->builder, arg1.value, ""), "");
+			return res;
 		}
 		if (op_code) {
 			res.value = LLVMBuildBinOp(p->builder, op_code, arg0.value, arg1.value, "");

+ 3 - 0
src/types.cpp

@@ -2306,6 +2306,9 @@ bool is_type_comparable(Type *t) {
 			}
 		}
 		return true;
+
+	case Type_SimdVector:
+		return true;
 	}
 	return false;
 }