diff --git a/releasenotes.md b/releasenotes.md index 27a5ae56e..71dedb80e 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -3,6 +3,7 @@ ## 0.5.0 Change List ### Changes / improvements +- vectors may now contain pointers. - `void!` does not convert to `anyfault`. - `$$masked_load` / `$$masked_store` for vector masked load/store. - `$$select` builtin for vector masked select. diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 4522adf62..8062528b0 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -2651,6 +2651,13 @@ INLINE CanonicalType *type_pointer_type(Type *type) return res->pointer; } +INLINE bool type_is_pointer_like(Type *type) +{ + CanonicalType *res = type->canonical; + TypeKind kind = type->type_kind; + return kind == TYPE_POINTER || (kind == TYPE_VECTOR && type->array.base->canonical->type_kind == TYPE_POINTER); +} + INLINE bool type_is_pointer_vector(Type *type) { return type->type_kind == TYPE_VECTOR && type->array.base->canonical->type_kind == TYPE_POINTER; @@ -2925,7 +2932,7 @@ INLINE bool type_is_inferred(Type *type) INLINE bool type_is_numeric(Type *type) { DECL_TYPE_KIND_REAL(kind, type); - return (kind >= TYPE_I8 && kind <= TYPE_FLOAT_LAST) || kind == TYPE_VECTOR; + return (kind >= TYPE_I8 && kind <= TYPE_FLOAT_LAST) || (kind == TYPE_VECTOR && !type_is_pointer_vector(type->canonical)); } INLINE bool type_underlying_is_numeric(Type *type) diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index b913846cc..b2d088db9 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -722,13 +722,17 @@ static inline void llvm_emit_pointer_offset(GenContext *c, BEValue *value, Expr llvm_emit_expr(c, &offset, offset_expr); llvm_value_rvalue(c, &offset); + LLVMTypeRef element_type; + ArraySize vec_len = pointer->type->type_kind == TYPE_VECTOR ? pointer->type->array.len : 0; if (expr->pointer_offset_expr.raw_offset) { - // COERCE UPDATE bitcast removed, check for ways to optimize - value->value = llvm_emit_pointer_gep_raw(c, c->byte_type, value->value, offset.value); - return; + element_type = vec_len ? LLVMVectorType(c->byte_type, vec_len) : c->byte_type; } - value->value = llvm_emit_pointer_gep_raw(c, llvm_get_pointee_type(c, pointer->type), value->value, offset.value); + else + { + element_type = llvm_get_pointee_type(c, vec_len ? pointer->type->array.base : pointer->type); + } + value->value = llvm_emit_pointer_gep_raw(c, element_type, value->value, offset.value); } @@ -4072,7 +4076,35 @@ void llvm_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValue *lhs val = llvm_emit_mult_int(c, lhs_type, lhs_value, rhs_value, expr->span); break; case BINARYOP_SUB: - if (lhs_type->type_kind == TYPE_POINTER) + if (type_is_pointer_vector(lhs_type)) + { + Type *element_type = lhs_type->array.base->pointer; + unsigned len = lhs_type->array.len; + LLVMTypeRef int_vec_type = llvm_get_type(c, type_get_vector(type_isz, len)); + if (lhs_type == rhs_type) + { + val = LLVMBuildSub(c->builder, LLVMBuildPtrToInt(c->builder, lhs_value, int_vec_type, ""), + LLVMBuildPtrToInt(c->builder, rhs_value, int_vec_type, ""), ""); + LLVMValueRef slots[256]; + LLVMValueRef *ptr = slots; + if (len > 256) + { + ptr = MALLOC(len * sizeof(LLVMValueRef)); + } + AlignSize diff = type_abi_alignment(element_type); + for (ArraySize i = 0; i < len; i++) + { + ptr[i] = llvm_const_int(c, type_isz, diff); + } + LLVMValueRef divisor = LLVMConstVector(ptr, len); + val = LLVMBuildExactSDiv(c->builder, val, divisor, ""); + break; + } + rhs_value = LLVMBuildNeg(c->builder, rhs_value, ""); + val = llvm_emit_pointer_gep_raw(c, llvm_get_type(c, element_type), lhs_value, rhs_value); + break; + } + else if (lhs_type->type_kind == TYPE_POINTER) { if (lhs_type == rhs_type) { diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 44fb298a7..22a2f6ffe 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -258,9 +258,8 @@ Type *type_infer_len_from_actual_type(Type *to_infer, Type *actual_type) case TYPE_SUBARRAY: return type_add_optional(type_get_subarray(indexed), is_optional); case TYPE_VECTOR: - // This is unreachable, because unlike arrays, there is no inner type that may be - // the inferred part. - UNREACHABLE + // The case of int[*]*[<2>] x = ... + return type_add_optional(type_get_vector(indexed, to_infer->array.len), is_optional); default: UNREACHABLE } @@ -1557,6 +1556,7 @@ static void cast_vec_to_vec(Expr *expr, Type *to_type) case TYPE_ANYFAULT: case TYPE_FAULTTYPE: insert_runtime_cast(expr, CAST_PTRPTR, to_type); + return; default: UNREACHABLE; } diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index cc585417e..55a1efcf2 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -2699,13 +2699,16 @@ static inline bool sema_expr_analyse_pointer_offset(SemaContext *context, Expr * // 2. Evaluate the offset. Expr *offset = exprptr(expr->pointer_offset_expr.offset); if (!sema_analyse_expr(context, offset)) return false; - if (!cast_implicit(context, offset, type_isz)) return false; + Type *flat = type_flatten(pointer->type); + unsigned vec_len = flat->type_kind == TYPE_VECTOR ? flat->array.len : 0; + + if (!cast_implicit(context, offset, vec_len ? type_get_vector(type_isz, vec_len) : type_isz)) return false; // 3. Store optionality bool is_optional = IS_OPTIONAL(pointer) || IS_OPTIONAL(offset); // 4. Possibly constant fold - if (expr_is_const(pointer) && expr_is_const(offset)) + if (!vec_len && expr_is_const(pointer) && expr_is_const(offset)) { assert(!is_optional); Int mul = { .i.low = type_size(type_flatten(pointer->type)->pointer), .type = offset->const_expr.ixx.type }; @@ -4983,54 +4986,68 @@ static bool sema_expr_analyse_sub(SemaContext *context, Expr *expr, Expr *left, Type *left_type = type_no_optional(left->type)->canonical; Type *right_type = type_no_optional(right->type)->canonical; + bool left_is_pointer_vector = type_is_pointer_vector(left_type); + bool left_is_pointer = left_is_pointer_vector || left_type->type_kind == TYPE_POINTER; // 2. Handle the ptr - x and ptr - other_pointer - if (left_type->type_kind == TYPE_POINTER) + if (left_is_pointer) { + ArraySize vec_len = left_is_pointer_vector ? left_type->array.len : 0; + // We restore the type to ensure distinct types are tested against each other. left_type = type_no_optional(left->type)->canonical; + bool right_is_pointer_vector = type_is_pointer_vector(right_type); + bool right_is_pointer = right_is_pointer_vector || right_type->type_kind == TYPE_POINTER; + + Type *offset_type = vec_len ? type_get_vector(type_isz, vec_len) : type_isz; + // 3. ptr - other pointer - if (right_type->type_kind == TYPE_POINTER) + if (right_is_pointer) { + + // Restore the type right_type = type_no_optional(right->type)->canonical; // 3a. Require that both types are the same. sema_binary_unify_voidptr(left, right, &left_type, &right_type); if (left_type != right_type) { - SEMA_ERROR(expr, "'%s' - '%s' is not allowed. Subtracting pointers of different types from each other is not possible.", type_to_error_string(left_type), type_to_error_string(right_type)); + SEMA_ERROR(expr, "'%s' - '%s' is not allowed. Subtracting pointers of different types is not allowed.", type_to_error_string(left_type), type_to_error_string(right_type)); return false; } - if (expr_both_const(left, right) && sema_constant_fold_ops(left)) + if (!right_is_pointer_vector && !left_is_pointer_vector && expr_both_const(left, right) && sema_constant_fold_ops(left)) { expr_rewrite_const_int(expr, type_isz, (left->const_expr.ptr - right->const_expr.ptr) / type_size(left_type->pointer)); return true; } // 3b. Set the type - expr->type = type_isz; - + expr->type = offset_type; return true; } right_type = right->type->canonical; + bool right_is_vector = right_type->type_kind == TYPE_VECTOR; // 4. Check that the right hand side is an integer. - if (!type_is_integer(right_type)) + if (!type_flat_is_intlike(right_type)) { SEMA_ERROR(expr, "Cannot subtract '%s' from '%s'", type_to_error_string(right_type), type_to_error_string(left_type)); return false; } // 5. Make sure that the integer does not exceed isz in size. - if (type_size(right_type) > type_size(type_isz)) + ArraySize max_size = right_is_vector ? type_size(offset_type) : type_size(type_isz); + if (type_size(right_type) > max_size) { - SEMA_ERROR(expr, "Cannot subtract a '%s' from a pointer, please first cast it to '%s'.", type_to_error_string(right_type), type_to_error_string(type_isz)); - return false; + RETURN_SEMA_ERROR(expr, "Cannot subtract %s from a %s, you need to add an explicit a narrowing cast to %s.", + type_quoted_error_string(right->type), + left_is_pointer_vector ? "pointer vector" : "pointer", + type_quoted_error_string(right_is_vector ? offset_type : type_isz)); } // 6. Convert to isz - if (!cast_implicit(context, right, type_isz)) return true; + if (!cast_implicit(context, right, offset_type)) return true; if (left->expr_kind == EXPR_POINTER_OFFSET) { @@ -5169,34 +5186,43 @@ static bool sema_expr_analyse_add(SemaContext *context, Expr *expr, Expr *left, Type *left_type = type_no_optional(left->type)->canonical; Type *right_type = type_no_optional(right->type)->canonical; + bool right_is_pointer = type_is_pointer_like(right_type); + bool left_is_pointer = type_is_pointer_like(left_type); // 2. To detect pointer additions, reorder if needed - if (right_type->type_kind == TYPE_POINTER && left_type->type_kind != TYPE_POINTER) + if (right_is_pointer && !left_is_pointer) { Expr *temp = right; right = left; left = temp; right_type = left_type; left_type = left->type->canonical; + left_is_pointer = true; + right_is_pointer = false; expr->binary_expr.left = exprid(left); expr->binary_expr.right = exprid(right); } // 3. The "left" will now always be the pointer. // so check if we want to do the normal pointer add special handling. - if (left_type->type_kind == TYPE_POINTER) + if (left_is_pointer) { + bool left_is_vec = left_type->type_kind == TYPE_VECTOR; + bool right_is_vec = right_type->type_kind == TYPE_VECTOR; + ArraySize vec_len = left_is_vec ? left_type->array.len : 0; // 3a. Check that the other side is an integer of some sort. if (!type_is_integer(right_type)) { - SEMA_ERROR(right, "A value of type '%s' cannot be added to '%s', an integer was expected here.", - type_to_error_string(right->type), - type_to_error_string(left->type)); - return false; + if (!left_is_vec || !right_is_vec || !type_is_integer(right_type->array.base)) + { + RETURN_SEMA_ERROR(right, "A value of type '%s' cannot be added to '%s', an integer was expected here.", + type_to_error_string(right->type), + type_to_error_string(left->type)); + } } // 3b. Cast it to usz or isz depending on underlying type. // Either is fine, but it looks a bit nicer if we actually do this and keep the sign. - bool success = cast_explicit(context, right, type_isz); + bool success = cast_explicit(context, right, left_is_vec ? type_get_vector(type_isz, vec_len) : type_isz); // No need to check the cast we just ensured it was an integer. assert(success && "This should always work"); @@ -7083,7 +7109,7 @@ RETRY: if (!type_is_valid_for_vector(type)) { SEMA_ERROR(type_info->array.base, - "%s cannot be vectorized. Only integers, floats and booleans are allowed.", + "%s is not of a vectorizable type.", type_quoted_error_string(type)); return poisoned_type; } diff --git a/src/compiler/types.c b/src/compiler/types.c index f728c3505..89c535f53 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -1093,7 +1093,16 @@ bool type_is_valid_for_vector(Type *type) case ALL_INTS: case ALL_FLOATS: case TYPE_BOOL: + case TYPE_POINTER: + case TYPE_ENUM: + case TYPE_TYPEID: + case TYPE_FAULTTYPE: + case TYPE_ANYFAULT: return true; + case TYPE_DISTINCT: + assert(type->decl->resolve_status == RESOLVE_DONE); + type = type->decl->distinct_decl.base_type; + goto RETRY; case TYPE_TYPEDEF: type = type->canonical; goto RETRY; diff --git a/src/version.h b/src/version.h index d1d0ddbc8..1ab0c17ab 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.642" \ No newline at end of file +#define COMPILER_VERSION "0.4.643" \ No newline at end of file diff --git a/test/test_suite/vector/vector_pointer_errors.c3 b/test/test_suite/vector/vector_pointer_errors.c3 new file mode 100644 index 000000000..26810af30 --- /dev/null +++ b/test/test_suite/vector/vector_pointer_errors.c3 @@ -0,0 +1,14 @@ +module vecpointer; + +fn void pointer_add_sub_diff() +{ + int[5] a; + int*[<2>] y; + double*[<2>] z = y; // #error: 'int*[<2>]' to 'double*[<2>]' + y / y; // #error: Cannot divide + y % y; // #error: not defined + y * y; // #error: multiply + y ^ y; // #error: not defined + iptr[<2>] g = (iptr[<2>])y; + g | g; +} diff --git a/test/unit/regression/vecpointer.c3 b/test/unit/regression/vecpointer.c3 new file mode 100644 index 000000000..c201d56e6 --- /dev/null +++ b/test/unit/regression/vecpointer.c3 @@ -0,0 +1,29 @@ +module vecpointer @test; + +fn void pointer_add_sub_diff() +{ + int[5] a; + void*[<2>] x = { &a[0], &a[4] }; + int*[<2>] y = x; + assert(x[0] == y[0] && x[1] == y[1]); + int*[2] y2; + y2 = y; + assert(y2[0] == x[0] && y2[1] == x[1]); + y = { null, null }; + assert(y[0] == null && y[0] == null); + y = y2; + assert(x[0] == y[0] && x[1] == y[1]); + int[<2>] z = { 1, -1 }; + y = y + z; + assert(y[0] == &a[1] && y[1] == &a[3]); + y = y - z; + assert(y[0] == &a[0] && y[1] == &a[4]); + int*[<2>] yy = { &a[1], &a[2] }; + isz[<2>] w; + w = y - yy; + assert(w == { -1, 2 }); + int*[<2>] zz = y - (y - yy); + assert(zz[0] == &a[1] && zz[1] == &a[2]); + int[*]*[<2>] g = int[2]*[<2>] { null, null }; + int[*]*[<*>] g2 = int[2]*[<2>] { null, null }; +} \ No newline at end of file