diff --git a/releasenotes.md b/releasenotes.md index b194750a6..df076e919 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -16,6 +16,7 @@ - Allow splatting of structs. #2555 - Deprecate `--test-nocapture` in favour of `--test-show-output` #2588. - Xtensa target no longer enabled by default on LLVM 22, Compile with `-DXTENSA_ENABLE` to enable it instead +- Add `float[<3>] x = { .xy = 1.2, .z = 3.3 }` swizzle initialization for vectors. #2599 ### Fixes - `Foo.is_eq` would return false if the type was a `typedef` and had an overload, but the underlying type was not comparable. diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index cdc61c05b..85ac481fe 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -4018,7 +4018,7 @@ void platform_linker(const char *output_file, const char **files, unsigned file_ const char *cc_compiler(const char *cc, const char *file, const char *flags, const char **include_dirs, const char *output_subdir); const char *arch_to_linker_arch(ArchType arch); extern char swizzle[256]; - +#define SWIZZLE_INDEX(c) ((swizzle[(int)(c)] - 1) & 0xF) #define CAT(a,b) CAT2(a,b) // force expand #define CAT2(a,b) a##b // actually concatenate #define TEMP(X) CAT(X, __LINE__) diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 1dbcae5f9..52d33bdaf 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -4497,7 +4497,7 @@ static void llvm_emit_vector_assign_expr(GenContext *c, BEValue *be_value, Expr LLVMValueRef result = be_value->value; for (unsigned i = 0; i < vec_len; i++) { - int index = (swizzle[(int)sw_ptr[i]] - 1) & 0xF; + int index = SWIZZLE_INDEX(sw_ptr[i]); LLVMValueRef val = llvm_emit_extract_value(c, result, i); vector_value = llvm_emit_insert_value(c, vector_value, val, index); } @@ -6860,7 +6860,7 @@ static void llvm_emit_swizzle_from_value(GenContext *c, LLVMValueRef vector_valu const char *sw_ptr = expr->swizzle_expr.swizzle; for (unsigned i = 0; i < vec_len; i++) { - int index = (swizzle[(int)sw_ptr[i]] - 1) & 0xF; + int index = SWIZZLE_INDEX(sw_ptr[i]); mask_val[i] = llvm_const_int(c, type_uint, index); } LLVMValueRef res = LLVMBuildShuffleVector(c->builder, vector_value, LLVMGetUndef(LLVMTypeOf(vector_value)), LLVMConstVector(mask_val, vec_len), sw_ptr); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index d3a537dc2..d3dad8cc0 100755 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -2883,18 +2883,10 @@ static inline bool sema_analyse_method(SemaContext *context, Decl *decl) } break; case ALL_VECTORS: - { - unsigned len = strlen(decl->name); - if (len <= 4) + if (sema_kw_is_swizzle(decl->name, strlen(decl->name))) { - for (unsigned i = 0; i < len; i++) - { - if (!swizzle[(int) decl->name[i]]) goto NEXT; - } RETURN_SEMA_ERROR(decl, "\"%s\" is not a valid method name for a vector, since it matches a swizzle combination.", kw); } - } - NEXT:; FALLTHROUGH; case TYPE_ARRAY: case TYPE_INFERRED_ARRAY: diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 037208aef..7d9d95d0f 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -6061,16 +6061,8 @@ bool sema_expr_rewrite_insert_deref(SemaContext *context, Expr *original) return true; } -static inline bool sema_expr_analyse_swizzle(SemaContext *context, Expr *expr, Expr *parent, Type *flat_type, - const char *kw, unsigned len) +bool sema_check_swizzle_string(SemaContext *context, Expr *expr, const char *kw, unsigned len, unsigned vec_len, bool *is_overlapping_ref, int* index_ref) { - unsigned vec_len = flat_type->array.len; - Type *indexed_type = type_get_indexed_type(parent->type); - assert(indexed_type); - bool is_ref = expr->access_unresolved_expr.is_ref; - bool is_lvalue = expr->access_unresolved_expr.is_lvalue; - if (is_lvalue) is_ref = false; - ASSERT_SPAN(expr, len > 0); int index = 0; bool is_overlapping = false; for (unsigned i = 0; i < len; i++) @@ -6078,6 +6070,7 @@ static inline bool sema_expr_analyse_swizzle(SemaContext *context, Expr *expr, E char val = (char)(swizzle[(int)kw[i]] - 1); if ((val & 0xF) >= vec_len) { + if (!expr) return false; RETURN_SEMA_ERROR(expr, "The '%c' component is not present in a vector of length %d, did you assume a longer vector?", kw[i], vec_len); } if (i == 0) @@ -6086,6 +6079,7 @@ static inline bool sema_expr_analyse_swizzle(SemaContext *context, Expr *expr, E } if ((index ^ val) & 0x10) { + if (!expr) return false; RETURN_SEMA_ERROR(expr, "Mixing [xyzw] and [rgba] is not permitted, you will need to select one of them."); } if (!is_overlapping) @@ -6101,7 +6095,23 @@ static inline bool sema_expr_analyse_swizzle(SemaContext *context, Expr *expr, E } } } - index &= 0xF; + *index_ref = index & 0xF; + *is_overlapping_ref = is_overlapping; + return true; +} +static inline bool sema_expr_analyse_swizzle(SemaContext *context, Expr *expr, Expr *parent, Type *flat_type, + const char *kw, unsigned len) +{ + unsigned vec_len = flat_type->array.len; + Type *indexed_type = type_get_indexed_type(parent->type); + assert(indexed_type); + bool is_ref = expr->access_unresolved_expr.is_ref; + bool is_lvalue = expr->access_unresolved_expr.is_lvalue; + if (is_lvalue) is_ref = false; + ASSERT_SPAN(expr, len > 0); + bool is_overlapping = false; + int index; + if (!sema_check_swizzle_string(context, expr, kw, len, vec_len, &is_overlapping, &index)) return false; if (len == 1) { expr->expr_kind = is_ref ? EXPR_SUBSCRIPT_ADDR : EXPR_SUBSCRIPT; @@ -6128,7 +6138,6 @@ static inline bool sema_expr_analyse_swizzle(SemaContext *context, Expr *expr, E Type *result = type_get_vector(indexed_type, flat_type->type_kind, len); expr->expr_kind = EXPR_SWIZZLE; expr->swizzle_expr = (ExprSwizzle) { .parent = exprid(parent), .swizzle = kw, .is_overlapping = is_overlapping }; - expr->type = result; return true; } @@ -6144,6 +6153,14 @@ static inline bool sema_analyse_maybe_dead_expr(SemaContext *context, Expr *expr context->active_scope.is_dead = false; return success; } +bool sema_kw_is_swizzle(const char *kw, unsigned len) +{ + for (unsigned i = 0; i < len; i++) + { + if (!swizzle[(int)kw[i]]) return false; + } + return true; +} static inline void sema_expr_flatten_const_ident(Expr *expr) { @@ -6330,15 +6347,9 @@ CHECK_DEEPER: if (type_kind_is_real_vector(flat_kind)) { unsigned len = strlen(kw); - if (len <= 4) + if (sema_kw_is_swizzle(kw, len)) { - for (unsigned i = 0; i < len; i++) - { - if (!swizzle[(int)kw[i]]) goto NOT_SWIZZLE; - } - // TODO should we do a missing for this as well? return sema_expr_analyse_swizzle(context, expr, current_parent, flat_type, kw, len); - NOT_SWIZZLE:; } } // Hard coded ptr on slices and any diff --git a/src/compiler/sema_initializers.c b/src/compiler/sema_initializers.c index 6cc4cb1a1..fd5df5d36 100644 --- a/src/compiler/sema_initializers.c +++ b/src/compiler/sema_initializers.c @@ -36,7 +36,7 @@ static inline void sema_update_const_initializer_with_designator_array(ConstInit Expr *value); -bool const_init_local_init_may_be_global_inner(ConstInitializer *init, bool top) +static bool const_init_local_init_may_be_global_inner(ConstInitializer *init, bool top) { ConstInitializer **list = INVALID_PTR; unsigned len = (unsigned)-1; @@ -1282,6 +1282,43 @@ static Type *sema_expr_analyse_designator(SemaContext *context, Type *current, E return current; } +static Type *sema_resolve_vector_element_for_name(SemaContext *context, FlatType *type, DesignatorElement *element, bool *did_report_error) +{ + Expr *field = element->field_expr; + if (field->expr_kind != EXPR_UNRESOLVED_IDENTIFIER) return NULL; + const char *kw = field->unresolved_ident_expr.ident; + unsigned len = strlen(kw); + if (!sema_kw_is_swizzle(kw, len)) return NULL; + bool is_overlapping = false; + int index; + if (!sema_check_swizzle_string(context, field, kw, len, type->array.len, &is_overlapping, &index)) + { + *did_report_error = true; + return NULL; + } + ArrayIndex first = SWIZZLE_INDEX(kw[0]); + ArrayIndex last = SWIZZLE_INDEX(kw[len - 1]); + + if (is_overlapping || (first + len != last + 1)) + { + *did_report_error = true; + SEMA_ERROR(field, "Designated initializers using swizzling must be a contiguous range, like '.xyz = 123'."); + return NULL; + } + ASSERT(last < type->array.len); + element->index = first; + if (len == 1) + { + element->kind = DESIGNATOR_ARRAY; + } + else + { + element->kind = DESIGNATOR_RANGE; + element->index_end = last; + } + return type->array.base; +} + INLINE bool sema_initializer_list_is_empty(Expr *value) { return expr_is_const_initializer(value) && value->const_expr.initializer->kind == CONST_INIT_ZERO; @@ -1352,9 +1389,18 @@ static Type *sema_find_type_of_element(SemaContext *context, Type *type, Designa return base; } ASSERT(element->kind == DESIGNATOR_FIELD); - if (!type_is_union_or_strukt(type_flattened) && type_flattened->type_kind != TYPE_BITSTRUCT) + switch (type_flattened->type_kind) { - return NULL; + case TYPE_UNION: + case TYPE_STRUCT: + case TYPE_BITSTRUCT: + break; + case TYPE_SIMD_VECTOR: + case TYPE_VECTOR: + *member_ptr = NULL; + return sema_resolve_vector_element_for_name(context, type_flattened, element, did_report_error); + default: + return NULL; } Decl *member = sema_resolve_element_for_name(context, type_flattened->decl->strukt.members, @@ -1365,6 +1411,7 @@ static Type *sema_find_type_of_element(SemaContext *context, Type *type, Designa return member->type; } + static ArrayIndex sema_analyse_designator_index(SemaContext *context, Expr *index) { if (!sema_analyse_expr_rvalue(context, index)) @@ -1396,7 +1443,6 @@ static ArrayIndex sema_analyse_designator_index(SemaContext *context, Expr *inde return (ArrayIndex)index_val; } - static Decl *sema_resolve_element_for_name(SemaContext *context, Decl **decls, DesignatorElement ***elements_ref, unsigned *index, bool is_substruct) { diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 36fe9bcd8..816b7c294 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -112,7 +112,8 @@ Expr *sema_ct_eval_expr(SemaContext *context, bool is_type_eval, Expr *inner, bo Expr *sema_resolve_string_ident(SemaContext *context, Expr *inner, bool report_missing); bool sema_analyse_asm(SemaContext *context, AsmInlineBlock *block, Ast *asm_stmt); bool sema_expr_analyse_sprintf(SemaContext *context, Expr *expr, Expr *format_string, Expr **args, unsigned num_args); - +bool sema_check_swizzle_string(SemaContext *context, Expr *expr, const char *kw, unsigned len, unsigned vec_len, bool *is_overlapping_ref, int *index_ref); +bool sema_kw_is_swizzle(const char *kw, unsigned len); bool sema_bit_assignment_check(SemaContext *context, Expr *right, Decl *member, bool *failed_ref); CondResult sema_check_comp_time_bool(SemaContext *context, Expr *expr); diff --git a/test/test_suite/vector/vector_init_swizzle.c3 b/test/test_suite/vector/vector_init_swizzle.c3 new file mode 100644 index 000000000..2366a4dee --- /dev/null +++ b/test/test_suite/vector/vector_init_swizzle.c3 @@ -0,0 +1,8 @@ +fn int test() +{ + int[<2>] v = { .xyz = 2 }; // #error: The 'z' component is not present in a vector of length 2, did you assume a longer vector? + int[<2>] v2 = { .z = 2 }; // #error: The 'z' component is not present in a vector of length 2, did you assume a longer vector? + int[<2>] v3 = { .yx = 2 }; // #error: Designated initializers using swizzling must be a contiguous range, like '.xyz = 123' + int[<3>] v4 = { .xyz = 2 }; + int[<3>] v5 = { .xz = 2 }; // #error: Designated initializers using swizzling must be a contiguous range, like '.xyz = 123' +} diff --git a/test/test_suite/vector/vector_init_swizzle_ok.c3t b/test/test_suite/vector/vector_init_swizzle_ok.c3t new file mode 100644 index 000000000..64bfb369b --- /dev/null +++ b/test/test_suite/vector/vector_init_swizzle_ok.c3t @@ -0,0 +1,22 @@ +// #target: macos-x64 +module test; +import std; +fn int main() +{ + int[<2>] v = { .xy = 2 }; + int[<2>] v2 = { .x = 2, .y = 3 }; + int[<3>] v3 = { .z = 4, .xy = 1 }; + return 0; +} + +/* #expect: test.ll + +entry: + %v = alloca <2 x i32>, align 8 + %v2 = alloca <2 x i32>, align 8 + %v3 = alloca <3 x i32>, align 16 + store <2 x i32> + store <2 x i32> , ptr %v2, align 8 + store <4 x i32> , ptr %v3, align 16 + ret i32 0 +} \ No newline at end of file