From f21e9ee71281ec8665a3009cd1a350ffde3b7046 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 May 2022 18:59:47 +0100 Subject: Allow basic casting of simd vectors --- src/llvm_backend_expr.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'src/llvm_backend_expr.cpp') diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 133df4d41..f4b5702bb 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1820,6 +1820,38 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { return res; } + if (is_type_simd_vector(src) && is_type_simd_vector(dst)) { + Type *src_elem = core_array_type(src); + Type *dst_elem = core_array_type(dst); + + GB_ASSERT(src->SimdVector.count == dst->SimdVector.count); + + lbValue res = {}; + res.type = t; + if (are_types_identical(src_elem, dst_elem)) { + res.value = value.value; + } else if (is_type_float(src_elem) && is_type_integer(dst_elem)) { + if (is_type_unsigned(dst_elem)) { + res.value = LLVMBuildFPToUI(p->builder, value.value, lb_type(m, t), ""); + } else { + res.value = LLVMBuildFPToSI(p->builder, value.value, lb_type(m, t), ""); + } + } else if (is_type_integer(src_elem) && is_type_float(dst_elem)) { + if (is_type_unsigned(src_elem)) { + res.value = LLVMBuildUIToFP(p->builder, value.value, lb_type(m, t), ""); + } else { + res.value = LLVMBuildSIToFP(p->builder, value.value, lb_type(m, t), ""); + } + } else if (is_type_integer(src_elem) && is_type_integer(dst_elem)) { + res.value = LLVMBuildIntCast2(p->builder, value.value, lb_type(m, t), !is_type_unsigned(src_elem), ""); + } else if (is_type_float(src_elem) && is_type_float(dst_elem)) { + res.value = LLVMBuildFPCast(p->builder, value.value, lb_type(m, t), ""); + } else { + GB_PANIC("Unhandled simd vector conversion: %s -> %s", type_to_string(src), type_to_string(dst)); + } + return res; + } + // Pointer <-> uintptr if (is_type_pointer(src) && is_type_uintptr(dst)) { lbValue res = {}; -- cgit v1.2.3 From 0203bb657ed09046dddc958be5322b1cd8c0a589 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 May 2022 20:39:22 +0100 Subject: Allow for non-constant simd vector compound types --- src/check_expr.cpp | 2 +- src/llvm_backend_expr.cpp | 96 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) (limited to 'src/llvm_backend_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 3dbecb1c0..9fd6acefd 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7885,7 +7885,7 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type * if (t->kind == Type_SimdVector) { if (!is_constant) { - error(node, "Expected all constant elements for a simd vector"); + // error(node, "Expected all constant elements for a simd vector"); } } diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index f4b5702bb..1b10cd776 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -4641,6 +4641,102 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { break; } + case Type_SimdVector: { + if (cl->elems.count > 0) { + lbValue vector_value = lb_const_value(p->module, type, exact_value_compound(expr)); + defer (lb_addr_store(p, v, vector_value)); + + auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count); + + // NOTE(bill): Separate value, store into their own chunks + for_array(i, cl->elems) { + Ast *elem = cl->elems[i]; + if (elem->kind == Ast_FieldValue) { + ast_node(fv, FieldValue, elem); + if (lb_is_elem_const(fv->value, et)) { + continue; + } + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); + + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op != Token_RangeHalf) { + hi += 1; + } + + lbValue value = lb_build_expr(p, fv->value); + + for (i64 k = lo; k < hi; k++) { + lbCompoundLitElemTempData data = {}; + data.value = value; + data.elem_index = cast(i32)k; + array_add(&temp_data, data); + } + + } else { + auto tav = fv->field->tav; + GB_ASSERT(tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(tav.value); + + lbValue value = lb_build_expr(p, fv->value); + lbCompoundLitElemTempData data = {}; + data.value = lb_emit_conv(p, value, et); + data.expr = fv->value; + data.elem_index = cast(i32)index; + array_add(&temp_data, data); + } + + } else { + if (lb_is_elem_const(elem, et)) { + continue; + } + lbCompoundLitElemTempData data = {}; + data.expr = elem; + data.elem_index = cast(i32)i; + array_add(&temp_data, data); + } + } + + + for_array(i, temp_data) { + lbValue field_expr = temp_data[i].value; + Ast *expr = temp_data[i].expr; + + auto prev_hint = lb_set_copy_elision_hint(p, lb_addr(temp_data[i].gep), expr); + + if (field_expr.value == nullptr) { + field_expr = lb_build_expr(p, expr); + } + Type *t = field_expr.type; + GB_ASSERT(t->kind != Type_Tuple); + lbValue ev = lb_emit_conv(p, field_expr, et); + + if (!p->copy_elision_hint.used) { + temp_data[i].value = ev; + } + + lb_reset_copy_elision_hint(p, prev_hint); + } + + + // TODO(bill): reduce the need for individual `insertelement` if a `shufflevector` + // might be a better option + + for_array(i, temp_data) { + if (temp_data[i].value.value != nullptr) { + LLVMValueRef index = lb_const_int(p->module, t_u32, temp_data[i].elem_index).value; + vector_value.value = LLVMBuildInsertElement(p->builder, vector_value.value, temp_data[i].value.value, index, ""); + } + } + } + break; + } } return v; -- cgit v1.2.3 From 09f936b04db2be7d30f695fe050ba57ac6d6da3d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 May 2022 23:24:32 +0100 Subject: Correct casting between integer and boolean #simd --- src/llvm_backend_expr.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/llvm_backend_expr.cpp') diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 1b10cd776..426becc1c 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1842,10 +1842,13 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { } else { res.value = LLVMBuildSIToFP(p->builder, value.value, lb_type(m, t), ""); } - } else if (is_type_integer(src_elem) && is_type_integer(dst_elem)) { + } else if ((is_type_integer(src_elem) || is_type_boolean(src_elem)) && is_type_integer(dst_elem)) { res.value = LLVMBuildIntCast2(p->builder, value.value, lb_type(m, t), !is_type_unsigned(src_elem), ""); } else if (is_type_float(src_elem) && is_type_float(dst_elem)) { res.value = LLVMBuildFPCast(p->builder, value.value, lb_type(m, t), ""); + } else if (is_type_integer(src_elem) && is_type_boolean(dst_elem)) { + LLVMValueRef i1vector = LLVMBuildICmp(p->builder, LLVMIntNE, value.value, LLVMConstNull(LLVMTypeOf(value.value)), ""); + res.value = LLVMBuildIntCast2(p->builder, i1vector, lb_type(m, t), !is_type_unsigned(src_elem), ""); } else { GB_PANIC("Unhandled simd vector conversion: %s -> %s", type_to_string(src), type_to_string(dst)); } -- cgit v1.2.3 From f308f37ba112ca361715e470d513749236da026d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 26 May 2022 14:51:50 +0100 Subject: Remove need for `simd.splat` --- core/simd/simd.odin | 9 ++---- src/check_expr.cpp | 8 ++++++ src/llvm_backend_const.cpp | 26 +++++++++++++---- src/llvm_backend_expr.cpp | 72 +++++++++++++++++++++++++++++----------------- 4 files changed, 76 insertions(+), 39 deletions(-) (limited to 'src/llvm_backend_expr.cpp') diff --git a/core/simd/simd.odin b/core/simd/simd.odin index 79d26c845..ef5fdc70b 100644 --- a/core/simd/simd.odin +++ b/core/simd/simd.odin @@ -103,10 +103,6 @@ reverse :: intrinsics.simd_reverse rotate_left :: intrinsics.simd_rotate_left rotate_right :: intrinsics.simd_rotate_right -splat :: #force_inline proc "contextless" ($T: typeid/#simd[$LANES]$E, value: E) -> T { - return T{0.. ^[LANES]E { return (^[LANES]E)(v) } @@ -127,12 +123,11 @@ from_slice :: proc($T: typeid/#simd[$LANES]$E, slice: []E) -> T { } bit_not :: #force_inline proc "contextless" (v: $T/#simd[$LANES]$E) -> T where intrinsics.type_is_integer(E) { - ones := splat(type_of(v), ~E(0)) - return xor(v, ones) + return xor(v, T(~E(0))) } copysign :: #force_inline proc "contextless" (v, sign: $T/#simd[$LANES]$E) -> T where intrinsics.type_is_float(E) { - neg_zero := to_bits(splat(T, E(-0.0))) + neg_zero := to_bits(T(-0.0)) sign_bit := and(to_bits(sign), neg_zero) magnitude := and(to_bits(v), bit_not(neg_zero)) return transmute(T)or(sign_bit, magnitude) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index fcd7818bc..2a3b5bf02 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2713,6 +2713,14 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) { return check_is_castable_to(c, &x, elem_dst); } + if (is_type_simd_vector(dst)) { + Type *elem = base_array_type(dst); + if (check_is_castable_to(c, operand, elem)) { + return true; + } + } + + return false; } diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 3a3067dbc..bd76400de 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -495,9 +495,9 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc res.value = data; return res; } else if (is_type_array(type) && - value.kind != ExactValue_Invalid && - value.kind != ExactValue_String && - value.kind != ExactValue_Compound) { + value.kind != ExactValue_Invalid && + value.kind != ExactValue_String && + value.kind != ExactValue_Compound) { i64 count = type->Array.count; Type *elem = type->Array.elem; @@ -513,8 +513,8 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc res.value = llvm_const_array(lb_type(m, elem), elems, cast(unsigned)count); return res; } else if (is_type_matrix(type) && - value.kind != ExactValue_Invalid && - value.kind != ExactValue_Compound) { + value.kind != ExactValue_Invalid && + value.kind != ExactValue_Compound) { i64 row = type->Matrix.row_count; i64 column = type->Matrix.column_count; GB_ASSERT(row == column); @@ -537,6 +537,22 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc res.value = LLVMConstArray(lb_type(m, elem), elems, cast(unsigned)total_elem_count); return res; + } else if (is_type_simd_vector(type) && + value.kind != ExactValue_Invalid && + value.kind != ExactValue_Compound) { + i64 count = type->SimdVector.count; + Type *elem = type->SimdVector.elem; + + lbValue single_elem = lb_const_value(m, elem, value, allow_local); + single_elem.value = llvm_const_cast(single_elem.value, lb_type(m, elem)); + + LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, count); + for (i64 i = 0; i < count; i++) { + elems[i] = single_elem.value; + } + + res.value = LLVMConstVector(elems, cast(unsigned)count); + return res; } switch (value.kind) { diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 426becc1c..10c337650 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1820,41 +1820,59 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { return res; } - if (is_type_simd_vector(src) && is_type_simd_vector(dst)) { - Type *src_elem = core_array_type(src); - Type *dst_elem = core_array_type(dst); + if (is_type_simd_vector(dst)) { + Type *et = base_array_type(dst); + if (is_type_simd_vector(src)) { + Type *src_elem = core_array_type(src); + Type *dst_elem = core_array_type(dst); - GB_ASSERT(src->SimdVector.count == dst->SimdVector.count); + GB_ASSERT(src->SimdVector.count == dst->SimdVector.count); - lbValue res = {}; - res.type = t; - if (are_types_identical(src_elem, dst_elem)) { - res.value = value.value; - } else if (is_type_float(src_elem) && is_type_integer(dst_elem)) { - if (is_type_unsigned(dst_elem)) { - res.value = LLVMBuildFPToUI(p->builder, value.value, lb_type(m, t), ""); - } else { - res.value = LLVMBuildFPToSI(p->builder, value.value, lb_type(m, t), ""); - } - } else if (is_type_integer(src_elem) && is_type_float(dst_elem)) { - if (is_type_unsigned(src_elem)) { - res.value = LLVMBuildUIToFP(p->builder, value.value, lb_type(m, t), ""); + lbValue res = {}; + res.type = t; + if (are_types_identical(src_elem, dst_elem)) { + res.value = value.value; + } else if (is_type_float(src_elem) && is_type_integer(dst_elem)) { + if (is_type_unsigned(dst_elem)) { + res.value = LLVMBuildFPToUI(p->builder, value.value, lb_type(m, t), ""); + } else { + res.value = LLVMBuildFPToSI(p->builder, value.value, lb_type(m, t), ""); + } + } else if (is_type_integer(src_elem) && is_type_float(dst_elem)) { + if (is_type_unsigned(src_elem)) { + res.value = LLVMBuildUIToFP(p->builder, value.value, lb_type(m, t), ""); + } else { + res.value = LLVMBuildSIToFP(p->builder, value.value, lb_type(m, t), ""); + } + } else if ((is_type_integer(src_elem) || is_type_boolean(src_elem)) && is_type_integer(dst_elem)) { + res.value = LLVMBuildIntCast2(p->builder, value.value, lb_type(m, t), !is_type_unsigned(src_elem), ""); + } else if (is_type_float(src_elem) && is_type_float(dst_elem)) { + res.value = LLVMBuildFPCast(p->builder, value.value, lb_type(m, t), ""); + } else if (is_type_integer(src_elem) && is_type_boolean(dst_elem)) { + LLVMValueRef i1vector = LLVMBuildICmp(p->builder, LLVMIntNE, value.value, LLVMConstNull(LLVMTypeOf(value.value)), ""); + res.value = LLVMBuildIntCast2(p->builder, i1vector, lb_type(m, t), !is_type_unsigned(src_elem), ""); } else { - res.value = LLVMBuildSIToFP(p->builder, value.value, lb_type(m, t), ""); + GB_PANIC("Unhandled simd vector conversion: %s -> %s", type_to_string(src), type_to_string(dst)); } - } else if ((is_type_integer(src_elem) || is_type_boolean(src_elem)) && is_type_integer(dst_elem)) { - res.value = LLVMBuildIntCast2(p->builder, value.value, lb_type(m, t), !is_type_unsigned(src_elem), ""); - } else if (is_type_float(src_elem) && is_type_float(dst_elem)) { - res.value = LLVMBuildFPCast(p->builder, value.value, lb_type(m, t), ""); - } else if (is_type_integer(src_elem) && is_type_boolean(dst_elem)) { - LLVMValueRef i1vector = LLVMBuildICmp(p->builder, LLVMIntNE, value.value, LLVMConstNull(LLVMTypeOf(value.value)), ""); - res.value = LLVMBuildIntCast2(p->builder, i1vector, lb_type(m, t), !is_type_unsigned(src_elem), ""); + return res; } else { - GB_PANIC("Unhandled simd vector conversion: %s -> %s", type_to_string(src), type_to_string(dst)); + i64 count = get_array_type_count(dst); + LLVMTypeRef vt = lb_type(m, t); + LLVMTypeRef llvm_u32 = lb_type(m, t_u32); + LLVMValueRef elem = lb_emit_conv(p, value, et).value; + LLVMValueRef vector = LLVMConstNull(vt); + for (i64 i = 0; i < count; i++) { + LLVMValueRef idx = LLVMConstInt(llvm_u32, i, false); + vector = LLVMBuildInsertElement(p->builder, vector, elem, idx, ""); + } + lbValue res = {}; + res.type = t; + res.value = vector; + return res; } - return res; } + // Pointer <-> uintptr if (is_type_pointer(src) && is_type_uintptr(dst)) { lbValue res = {}; -- cgit v1.2.3 From d0e8a735bae52eaa7d1d852953da721071571020 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 26 May 2022 17:09:46 +0100 Subject: Add arithmetic operator support for simd vectors; Add `intrinsics.simd_and_not` --- core/simd/simd.odin | 16 +++++++++++++--- src/check_builtin.cpp | 1 + src/check_expr.cpp | 7 ++----- src/checker_builtin_procs.hpp | 3 +++ src/llvm_backend_expr.cpp | 40 +++++++++++++++++++++++++++++++++++++++- src/llvm_backend_proc.cpp | 4 ++++ src/types.cpp | 3 +++ 7 files changed, 65 insertions(+), 9 deletions(-) (limited to 'src/llvm_backend_expr.cpp') diff --git a/core/simd/simd.odin b/core/simd/simd.odin index ef5fdc70b..9673b5cc9 100644 --- a/core/simd/simd.odin +++ b/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) +} diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 34b7d14e9..bfaa91cf8 100644 --- a/src/check_builtin.cpp +++ b/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 = {}; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b7568aa70..7fe9b8acf 100644 --- a/src/check_expr.cpp +++ b/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); diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 350213de2..eb4bc1498 100644 --- a/src/checker_builtin_procs.hpp +++ b/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}, diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 10c337650..55b76b93a 100644 --- a/src/llvm_backend_expr.cpp +++ b/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))); } diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index a56aa862a..99c023311 100644 --- a/src/llvm_backend_proc.cpp +++ b/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, ""); diff --git a/src/types.cpp b/src/types.cpp index 6f61015d3..ad83e0568 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2306,6 +2306,9 @@ bool is_type_comparable(Type *t) { } } return true; + + case Type_SimdVector: + return true; } return false; } -- cgit v1.2.3 From 618d3bf62fbcfa6ca7f827ad4090143b8535b4a2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 28 May 2022 13:42:58 +0100 Subject: Improve vector comparison `==` `!=` for horizontal reduction --- src/llvm_backend_expr.cpp | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) (limited to 'src/llvm_backend_expr.cpp') diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 55b76b93a..1894e85f6 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -2585,16 +2585,35 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri } 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), ""); + /* NOTE(bill, 2022-05-28): + Thanks to Per Vognsen, sign extending to + a vector of the same width as the input vector, bit casting to an integer, + and then comparing against zero is the better option + See: https://lists.llvm.org/pipermail/llvm-dev/2012-September/053046.html + + // Example assuming 128-bit vector + + %1 = <4 x float> ... + %2 = <4 x float> ... + %3 = fcmp oeq <4 x float> %1, %2 + %4 = sext <4 x i1> %3 to <4 x i32> + %5 = bitcast <4 x i32> %4 to i128 + %6 = icmp ne i128 %5, 0 + br i1 %6, label %true1, label %false2 + + This will result in 1 cmpps + 1 ptest + 1 br + (even without SSE4.1, contrary to what the mail list states, because of pmovmskb) + + */ + + unsigned count = cast(unsigned)get_array_type_count(a); + unsigned elem_sz = cast(unsigned)(type_size_of(elem)*8); + LLVMTypeRef mask_type = LLVMVectorType(LLVMIntTypeInContext(p->module->ctx, elem_sz), count); + mask = LLVMBuildSExtOrBitCast(p->builder, mask, mask_type, ""); + + LLVMTypeRef mask_int_type = LLVMIntTypeInContext(p->module->ctx, cast(unsigned)(8*type_size_of(a))); + LLVMValueRef mask_int = LLVMBuildBitCast(p->builder, mask, mask_int_type, ""); + res.value = LLVMBuildICmp(p->builder, LLVMIntNE, mask_int, LLVMConstNull(LLVMTypeOf(mask_int)), ""); return res; } else { -- cgit v1.2.3